pax_global_header00006660000000000000000000000064147352521370014523gustar00rootroot0000000000000052 comment=7e9babdc79d7eeba8ca80e5279b01521135d6396 Solaar-1.1.14/000077500000000000000000000000001473525213700130305ustar00rootroot00000000000000Solaar-1.1.14/.coveragerc000066400000000000000000000004271473525213700151540ustar00rootroot00000000000000[run] branch = True source = hid_parser hidapi keysyms logitech_receiver solaar omit = */tests/* */setup.py */__main__.py [report] exclude_lines = pragma: no cover if __name__ == '__main__': if typing.TYPE_CHECKING fail_under = 40 Solaar-1.1.14/.git-blame-ignore-revs000066400000000000000000000004661473525213700171360ustar00rootroot00000000000000# yapf bulk change 72a8d311bce64b1536c08e754d22bb91efb66460 # isort bulk change e6369e0c3c240715a0a2daede6c3b225ed63cf60 # pre-commit bulk change 33521558ed7007c24b24127f1448c2c23ecbfb35 # flake8 bulk change 627185079f65c9cc471194b7927c833682e4a7a3 # yapf style update 27c90fa736d8a7a1ef6acda4345d3599862e185c Solaar-1.1.14/.github/000077500000000000000000000000001473525213700143705ustar00rootroot00000000000000Solaar-1.1.14/.github/ISSUE_TEMPLATE/000077500000000000000000000000001473525213700165535ustar00rootroot00000000000000Solaar-1.1.14/.github/ISSUE_TEMPLATE/bug_report.md000066400000000000000000000032151473525213700212460ustar00rootroot00000000000000--- name: Bug report about: Create a report to help us improve title: '' labels: bug assignees: '' --- **Information** - Solaar version (`solaar --version` or `git describe --tags` if cloned from this repository): - Distribution: - Kernel version (ex. `uname -srmo`): `KERNEL VERSION HERE` - Output of `solaar show`:
``` SOLAAR SHOW OUTPUT HERE ```
- Contents of `~/.config/solaar/config.yaml` (or `~/.config/solaar/config.json` if `~/.config/solaar/config.yaml` not present):
``` CONTENTS HERE ```
- Errors or warrnings from Solaar: **Describe the bug** A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' 4. See error **Screenshots** If applicable, add screenshots to help explain your problem. **Additional context** Add any other context about the problem here. Solaar-1.1.14/.github/ISSUE_TEMPLATE/feature_request.md000066400000000000000000000017451473525213700223070ustar00rootroot00000000000000--- name: Feature request about: Suggest an idea for this project title: '' labels: enhancement assignees: '' --- **Information** - Solaar version (`solaar --version` and `git describe --tags`): - Distribution: - Kernel version (ex. `uname -srmo`): - Output of `solaar show` for the target device (if applicable):
``` OUTPUT HERE ```
**Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] **Describe the solution you'd like** A clear and concise description of what you want to happen. **Describe alternatives you've considered** A clear and concise description of any alternative solutions or features you've considered. **Additional context** Add any other context or screenshots about the feature request here. Solaar-1.1.14/.github/workflows/000077500000000000000000000000001473525213700164255ustar00rootroot00000000000000Solaar-1.1.14/.github/workflows/checks.yml000066400000000000000000000004271473525213700204130ustar00rootroot00000000000000name: checks on: [push, pull_request] jobs: pre-commit: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 - name: Run pre-commit uses: pre-commit/action@v3.0.0 Solaar-1.1.14/.github/workflows/gh-pages.yml000066400000000000000000000017561473525213700206540ustar00rootroot00000000000000name: Deploy to GitHub Pages on: push: branches: - master permissions: contents: read pages: write id-token: write concurrency: group: 'pages' cancel-in-progress: false jobs: build: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 with: python-version: '3.x' - name: Install dependencies run: | pip install mkdocs mkdocs-rtd-dropdown mkdocs-mermaid2-plugin mkdocstrings[python] - name: Build and deploy run: | mkdocs build - name: Upload artifact uses: actions/upload-pages-artifact@v3 with: path: 'site' deploy: environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} runs-on: ubuntu-latest needs: build steps: - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v4 Solaar-1.1.14/.github/workflows/tests.yml000066400000000000000000000036401473525213700203150ustar00rootroot00000000000000name: tests on: [push, pull_request] jobs: ubuntu-tests: runs-on: ubuntu-latest strategy: matrix: python-version: [3.8, 3.12] steps: - name: Checkout uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Install Ubuntu dependencies run: | make install_apt - name: Install Python dependencies run: | make install_pip PIP_ARGS='.["test"]' - name: Run tests on Ubuntu run: | make test - name: Upload coverage to Codecov if: github.ref == 'refs/heads/master' uses: codecov/codecov-action@v4.5.0 with: directory: ./coverage/reports/ env_vars: OS, PYTHON files: ./coverage.xml flags: unittests name: codecov-umbrella token: ${{ secrets.CODECOV_TOKEN }} macos-tests: runs-on: macos-latest strategy: matrix: python-version: [3.8, 3.12] steps: - name: Checkout uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Set up macOS dependencies run: | make install_brew - name: Install Python dependencies run: | make install_pip PIP_ARGS='.["test"]' - name: Run tests on macOS run: | export DYLD_LIBRARY_PATH=$(brew --prefix hidapi)/lib:$DYLD_LIBRARY_PATH && pytest --cov --cov-report=xml - name: Upload coverage to Codecov if: github.ref == 'refs/heads/master' uses: codecov/codecov-action@v4.5.0 with: directory: ./coverage/reports/ env_vars: OS, PYTHON files: ./coverage.xml flags: unittests name: codecov-umbrella token: ${{ secrets.CODECOV_TOKEN }} Solaar-1.1.14/.gitignore000066400000000000000000000003601473525213700150170ustar00rootroot00000000000000*.pyc *.pyo __pycache__/ *.log *.mo /lib/Solaar.egg-info/ /lib/solaar.egg-info/ /lib/solaar/commit /build/ /sdist/ /dist/ /deb_dist/ /MANIFEST .coverage /htmlcov/ /docs/captures/ /share/logitech_icons/ /share/locale/ /po/*.po~ /.idea/ Solaar-1.1.14/.pre-commit-config.yaml000066400000000000000000000007311473525213700173120ustar00rootroot00000000000000repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.3.0 hooks: - id: check-ast - id: check-builtin-literals - id: check-merge-conflict - id: check-yaml - id: check-toml - id: debug-statements - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.2.2 hooks: - id: ruff name: ruff lint args: [--fix, --exit-non-zero-on-fix] - id: ruff-format name: ruff format Solaar-1.1.14/.python-version000066400000000000000000000000051473525213700160300ustar00rootroot000000000000003.11 Solaar-1.1.14/CHANGELOG.md000066400000000000000000001144721473525213700146520ustar00rootroot00000000000000# 1.1.14 * Handle fake feature enums in show * Fix battery entries in config.yaml * Add ratchet setting for smart shift enhanced devices * Refactor Gesture into enum * Replace ERROR NamedInts by IntEnum (#2645) * Refactor hidpp20 to use enum * Update Polish, Swedish, Norwegian Nynorsk (nn), and Norwegian Bokmål (nb) translations * Use IntEnum for firmware and cidgroup constances * Change pairing error values to intenums * Fix initialization bug for PackedRangeControl * Add tests for feature class, process_notification, and key_is_down * Check all bits for extended report rate * Add type hints * Improve about dialog * Reduce dependencies * Refactor code * Improve testing * Allow unknown keys in Key rule conditions * Improve documentation for cli actions * Cycle sw_id to better guard against duplication of messages * Handle error return on root feature * Clean up documentation * Improve github interactions * Add information about Onboard Profiles overriding some settings * Add wording to README.md that Solaar is not a device driver * Clean up imports * Handle unknown device kinds * Fix broken links to Solaar logo * Use mkdocs for public documentation * Clean up setup.py * Remove Dead links in the AppStream file * Update about.py * Remove check on driver * Improve base module * Remove unnecessary receiver info 'hid_driver' * Convert HIDPPNotification to dataclass * Be defensive when converting battery status to string * Automatically detect packages in /lib * Clean up locale code * Improve rules documentation * Refactor creation of devices * Add headings to structure rules.md * Unify imports in logitech package * Don't ping device when getting name or codename * Use dataclasses and enums where useful * Introduce Device protocol and type hints * Add typing_extensions dependency * Move hidpp10 independent functions to module level * Fix macOS compatibility and reenable CI tests * Unify imports in hidapi package * Move screenshots into dedicated folder and add high-level graph of components * Update French and Chinese translations * Drop support for end-of-life Python 3.7 # 1.1.13 * Update Polish and Russian translations. * Fix bug in suspend and resume callback * Add choices universe for backlight setting * Add simplify diversion.py and add unit tests * Get and use current host number for K375sFnSwap because of bug in firmware of MX Keys S * Fix bug with logo in about window * Don't ping device just to get logging information * Optimize write for per-key lighting * Add and initialize per-key lighting to a special no-change value * Remove some Python 2 compatibility code * Update French translation * Refactor rule loading for testability # 1.1.12 * Check for existence of keys file before opening * Perform translation for all translatable strings. * Add included hid_parser to packages installed * Improve label and description for LED zone settings * Add message about Onboard Profiles to LED Zone settings * Initialize device registers to empty list * Use bluez dbus signals to disconnect and connect bluetooth devices * Handle a different signal for onboard profiles directory in ROM * Introduce small delay in getting pairing information to let receiver settle after pairing * Improve testing for settings_templates, settings, hidpp20, and device and fix small bugs found * Add extended adjustable DPI setting * Improve and extend infrastructure for testing setting_templates * Update Greek, Polish, Russian, and Traditional Chinese translations * Implement and test per-key lighting * Refactor and test pair_window in GUI * Handle situation when read of a setting fails in GUI * Permit continuing when a read during pushing fails * Fix bug in LEDZoneSetting when effect is not implemented * Add tests for LEDEffect structures in hidpp20 * Handle BRIGHTNESS_CONTROL notifications * Add settings for BRIGHTNESS_CONTROL and RGB_EFFECTS features * Fix small bugs found from testing in settings * Use f-strings for more exceptions and log message * Tests for setting_templates * Simple change in settings to improve testability * Use feature_request from the device everywhere in hidpp20 * Fix bug in backlight 2 durations * Replace deprecated code constructs * Set up test data and classes to help test HID++ interactions * Use pytest to test code for logitech_receiver modules * Align init methods for all receiver classes * Start refactoring of code base * Allow sub-second delays in Later * Fix bug in setting configuration cookie due to bad documentation * Use ruff for code styling and linting * Upgrade string formating to f-string * Document battery-icons=solaar option * Tell devices to delay device sending first messages until configuration is done * Optimize some functions in FeaturesArray * Fix bug in creating features array * Fix bug in building battery line in show * Refactor diversion_rules * Fix bug in determining tray icon * Fix bug in getting friendly name * Move status information to Device and Receiver objects * Add tests for get_kind_from_index and base product information * Update EX100 documentation * Use object attributes instead of dictionary in status objects * Create subclasses of receiver for different variants * Add requirement for CONFIG_HIDRAW to documentation * Add some low-level tests for some hidpp20 functions, profiles, and lighting and some hidpp10 tests * Fix app name casing in UI * Add missing receiver type for Lightspeed receivers * Add new device types * Refactor device and receiver instantiation * Simplify naming of distribution files * Clean up some logging code * Remove duplicated code to read register * Introduce Hidpp20 and Hidpp10 class * Remove unnecessary calls of del * Fix bug when reading BACKLIGHT setting from device * Replace invalid hidpp10 and hidpp20 usages * Use only timer thread to save config.yaml * Improve README * Copy newer version of hid_parser * Reorder code in settings * Update installation documentation * Add missing license blocks * Clean up listener and notifications code * Add locks to prevent multiple persisters for device * Clean up configuration, device, and receiver code * Move battery constants common to HID++ 1.0 and 2.0 to common * Move mapping of device kind into hidpp20 * Move pairing information gathering to receiver * update contributors * Expand allowable profile numbers * Clean up __init__ in logitech_receiver * Modify pre-commit args to make ruff change files * Fix bug in hidpp20 get host names * Use ruff for formatting and linting * Fix bug in rule Set action * Add notify module to logitech_receiver * Implement setting_changed callback and pass in to new devices and receivers * Add callback to call when changing a setting * Move exceptions, hidpp20 and hidpp10 constants into new modules * Streamline status code * Upgrade debugging in udev * Fix deprecated GitHub actions * Extend makefile and tests * Improve features array * Move ui_async to common.py * Improve module imports * Add tests of common module # 1.1.11 * Rename light icons and install them in correct place * Setup macOS tests using GitHub action (#2284) * Better checking for setting in record_setting * Fix invalid func name set logo name * Simplify installation of udev rules * Add document on implementation * Tidy up scrolling appearance in configuration panel * Correctly handle profile button with no action * Don't unlock setting when changed by external means * Refactor code to record change to setting * Add GitHub action for tests * Introduce tests with pytest * Simplify logger instantiation * Update label and tooltip for divert-gkeys setting * Don't lock setting when an error occurs * Catch assertion errors when reading setting values from devices * Support LED Zone control feature * Dump and load device profiles * Select among profiles. * Support backlight levels and duration * Use "Report Rate" instead of "Polling" for movement report rate * Support extended report rate setting * Add stable branch to release.sh (#2236) * Fix changelog parsing in release.sh * Update installation.md with new udev rules location * Downgrade assertion on missing notification flag to warning * Write empty file if there are no rules to save * Be defensive in device error messages * Add descriptions of M650, PRO X 2, G915, MX Anywhere 2S, G305, and MX Keys S * Report hidraw node in debugging messages * Add names for new Logitech features * Update Spanish, French, and Polish translations * Defend against lightspeed receivers that contact devices for basic information * Remove incorrect feature for M325 mice * Add K845 keyboard * Add partial support for macOS and minimal support for Windows * Correctly enumerate devices on receiver * Add wording in documentation about Logitech reusing model numbers * Better handling and installation of icons * Catch errors when pinging to try to put device online * Be more cautious when creating log messages to avoid exceptions * Correctly handle NoSuchDevice exception when pinging device * Fix test in rules for device equality * Add installation instructions for pipx and add not about other GTK system packages * Fix bug in NamedInt * Add support for MK550 * Install udev rule files to correct placces * Expand expected ping responses * Update codename when device status changes # 1.1.10 * Add information about NixOS flake package * Permit bluetooth devices in hidconsole * Add descriptor for Logitech MX Revolution Mouse M-RCL 124 * Improve determination for short and long messages * Add descriptor for G500s * Fix bug in scan-registers * Add single depress and release options for rule mouse click action * Add rule condition for hostname * Update keysym generation to current list of keysyms * Allow device 0 in hidconsole * Upgrade messages when no supported device found * Documentation update for the gnome extension for better Solaar rule support * Remove udev-acl tag from udev rules * Add support for process condition in Wayland * Update French, Chinese, and German translations * Add G733 Headset * Restore tools/clean.sh * Add Bluetooth Keyboard C714 * Update several device descriptions * Update scan-registers.sh * Remove assertion on last byte of ping responses * Add symbolic version of solaar icon * Fix bug when finding name or codename * Update documentation * Put version in initial INFO logging message # 1.1.9 * Add descriptors for G535 wireless gaming headset and wireless keyboard EX110 * Update Greek translation * Fix minor issues in documentation * Remove some deprecated GTK code * Use zero exit code for kill interrupts * Add rule Test condition for battery charging * Get wpid for 28Mz devices from udev when enumerating * Add Device condition to rules * Don't show wireless link or battery information when unknown or not present * Add desccriptor for G9x and LX7 mice * Fix bug in determining kind of devices for 27Mz receivers * Set initial lock status of smooth scrolling features to ignore * Fix glitch in configuration file update when changing versions * Add more debugging output for rules * Clean up pinging code * Put initial ping of direct-connected devices inside listener thread * Read and check before write of range settings * Improve pairing determination * Cut off determination of receiver devices when all have been found * Remove derived configuration fields when Solaar version changes * Allow device descriptors without name and codename * Filter and escape technical detail fields * Add setting for ADC power managemen * Correctly determine whether to ping with a long HID++ message * Add description for K470 keyboard from the MK470 combo (#1945) * Add setting value for mouse gestures * Update German and French translations * Remove old clean.sh and monitor.py tools * Retry opening device if permissions error encountered * Better handlling of IO errors at device creation * Add KeyIsDown rule condition to check whether a diverted key is down * Clean up device and receiver creation # 1.1.8 * Add parameter to thumb wheel rule conditions * Rename Serbian translation file * Update Polish translation * Improve handling of wheel notifications * Only record changes of scroll wheel ratchet * Don't mark setting as absent if there are device errors * More robust access to keyword exception fields * Add support for config change feature * Clean up GTK code * Update documentation on mouse gestures and known issues * Get commit information from git describe and dpkg-parsechangelog * Update pre-commit tools to current versions * Update installation instructions * Add 8-character commit hash to version information * Update solaar.desktop (#1857) * Get release in setup.py by reading lib/solaar/__init__.py * Remove version number from docs/_config.yml * Update solaar.desktop * Update es.po * Clarify status of C542 receiver * Update devices directory * Add description of several devices * Show name of disconnecting receiver or device in disconnect warning * Reduce standard size of rule editor window * Remove unnecessary dependency on typing_extensions # 1.1.7 * Add dependency on typing_extension to setup.py * Don't defer saves in CLI and don't require Gtk in CLI * Be more permissive in recognizing HID++ report descriptors * Update Polish and Croatian translations * Switch scroll ratcheting in response to scroll ratchet button notification * Add setting to turn scroll ratchet on and off * Eliminate visual glitch when updating range setting * Make hid-parser an optional dependency # 1.1.6 * Only update remaining pairings after successful pairing * Check for presence of status attribute when resuming * Update Polish and Croation translations * Don't add non-existant key in raw xy processing * Add special keys from MX Mechanical Mini * Fix processing of HID++ 1.0 battery reports * Use report descriptors to determine suitable devices * Handle exceptions when processing configuration file * Add Logitech PRO Gaming Keyboard * Fix bad entries in divert-keys when found * Correctly convert old-style diversions to new style and remove old ones * Add optional save argument to write_key_value methods * Use device name in configuration entries if device modelId is zeroes * Don't show normal DJ messages in debug log * Add Later rule action * Correctly record battery feature when ADC produces error * Print feature call errors in solaar show instead of terminating * Use ADC notifications to set device inactive and active * Add one to feature count to count ROOT feature * Don't check modifiers for KeyPress actions that are not clicks * Augment comments on what Solaar cannot do * Fix bug in printing closed handle * Use only product records to determine which receivers unpair * Add conditional delay to get around race with Linux HID++ driver # 1.1.5 * Add G213 Prodigy Gaming Keyboard * Update Turkish, Croation, and zh_CN translations * Move old device descriptions to new locations and clean up * Increase minimum stopped time between mouse movements * Fix bug with non-recognized empty mouse movement * Allow config panel to scroll * Add descriptions for K360 4004, MX Master 4017, Lightspeed C541, and G915 407D * Change security tooltip for unencrypted wireless connections * Add Norwegian Bokmal translation * Update documentation on continuously running GUI, diverted keys, and changing hosts * Solaar config no longer applies settings * Add description of rule components to rule editor * Ignore first movement for mouse gestures when reprog controls version is 5 or more * Add Active rule condition to test whether a device is active * Terminate iteration over receiver devices when all have been found * Downgrade Set prolem messages from error to warning * Log informational messages and above to a temporary file * Don't produce warnings when editing rules in rule editor * Fix bug that didn't save rules file if there were no rules * Allow devices with connection number larger than max_devices * Show more registers in solaar probe * Show feature versions in solaar show * Fix problems with deleting devices on exit * Update German translation * Update device documentation directory * Add descriptors for G502 Proteus Spectrum Optical Mouse and G935 headset * Record feature versions * Add Serbian translation * Fix circular import in rules * Keep track of devices and use in rules * Update zh_CN and zh_TW translations * Convert old-style mouse gestures and sliding dpi settings to new style * Allow multiple keys for mouse gestures and sliding dpi * Defer saving configuration file for a while to cut down on the number if times it is saved * Use correct functions for unified battery and battery voltage features. * Add defensive check for missing keys array in mouse gestures setting * Better presentation of battery information * Add support for ADC MEASUREMENT battery feature * Add information on how to find divertable key names in solaar show output * Better warnings for feature call errors with reprogrammable key and gesture settings * Increase entry size for ChoiceControlBig # 1.1.4 * Update Croatian translation * Handle unanticipated values for persistent remappable action as default * Improve system tray interface documentation * Remove blank lines from system tray popup * Update Polish translation * Add depress and release options to KeyPress rule action * Only close existing devices for receiver * Differentiate debug messages for different setting types * Implement packed range setting in config panel * Only produce warning for unimplemented display of setting * Add sidetone and equalizer settings for headsets * Add packed ranges setting * Use built-ins for conversions between int and byte string * Don't add HI_RES wheel ecodes to evdev device codes * Handle null key in persistent remappable actions when device does not respond * Consult polling rate feature for polling rate * Eliminate extra file handles * Close file handles for receiver-connected devices * Access to more keys in KeyPress rule action when in other keyboard groups * Update Turkish translation * Put battery levels in tray menu labels * Reduce warnings for inaccessible keys in KeyPress rule action * Better determination of keycodes and insert needed shifts in KeyPress actions. * Be careful when processing notifications for partly set up devices. * Clear out fields for empty persistent remappable action. * Fix message generation when device is offline. * Fix bug in feature lookup. * Add support for G533 Headset and G502 Gaming Mouse * Improve device features handling. * Keep trying settings that have worked in the past. * Store None in persisters for non-persisting settings. * Only create configuration entries for off-line devices if they have a serial number. # 1.1.3 * Update documentation files * Lower remove python 3.7 constructs and lower python dependency to 3.6 * Fix bug in xtest mouse scrolling * Allow mouse gesture setting for non-mice, e.g., trackballs * Print message when there is another Solaar process running * Fix determination of whether to force read before writing boolean setting * Add recent diversion features to default list in rules UI * Add setting to divert hires scroll wheel * Don't use device kind to determine how to handle notifications * Update Polish, Russian, and zh_CN translations * Remove pggettext so as to not require Python 3.8 * Remove Python 2 unicode support * Store keys in configuration file as int instead of str * Use yaml for configuration file instead of json. * Remove process-dependent rules from built-in rules. * Handle situation where GTK application cannot be set up in CLI. * Remove obsolete upower signals. * Appinfo/metadata fixes including using correct APP_ID. * Fix bugs in solaar config. * Add full set of mouse buttons in persistent remappable actions setting. * Improve rule behaviour under Wayland and when Xtest or X11 not available. * Fix up and document dependencies (evdev, Python, ...). # 1.1.2 * Update documentation on supported devices and translations * Include evdev as a dependency * Try to use uinput for fake input if XTest extension not available * Add Nano receiver C542 for M190 mice * Broaden range of HID++ Bluetooth devices * Add setting to divert gestures * Rule editor can edit all rule components * Configuration via solaar config takes effect in Solaar UI if it is running * Add setting to disable Onboard Profiles and decouple from Polling Rate setting * Add setting for PERSISTENT_REMAPPABLE_ACTION, common keyboard and mouse cases only * Split Test rule condition into Test and TestBytes and support in rule editor * Fix bug in speed-change setting * Support Backlight3 feature * Ensure that settings are pushed in resume * Update German, Polish, and zh_CN translations * Determine device number for direct-connected devices from protocol * fix bug in add and delete button actions in rule editor * dispose of no-op notifications quickly * add rule condition for checking device settings * use local file for conversion from key names to keysyms * get keyboard group and use to get correct keycodes (X11 only) * improve how rules work under Wayland * add settings for M-Key LEDs and MR-Key LED * fix bug in unpacking M and MR key notification * add G815 keyboard and MX518 mouse * add new special keys for recent keyboards * track M and MR keys for use in rules * make sure that device is online when searching for devices in solaar show * don't check for device kind in dpi sliding setting * fix problem with devices that report 0 DPI * handle missing divert-setting in action RW for settings * add id property (unitId or serial) and don't use ? for unknown serial * fix contains for NamedInts and eliminate use of has_element * check for xtest and disable modifier checking if not available * improve determination of gesture information * add Set rules to rule GUI * add gesture params to Set rules * hide system tray when there are no devices to control * add G733 headset, G9 mouse, G502 Hero mouse * Use greyscale solaar icon in tray when using symbolic icons * Fix bugs in solaar config * Use classes for settings to hep with modularity * Accept '~' and Toggle for toggling boolean settings in cli and rules * handle errors when writing to devices * refactor config_panel.py to use classes for widgets * add rules action to set Solaar settings * decrease amount of logging at each debug level * don't stretch toggles in settings * use key structure for key remapping setting * optimize ReprogrammableKey implementation * keep track of settings that are absent from device * add G512 keyboard and G402 mouse * reformat descriptors.py * use feature numbers for reprogrammable key versions * don't use new_from_icon_set in menu as it is deprecated # 1.1.1 * Keep left pane in Solaar main window the same size * Fix crash when checking a process condition when X11 not running * Add version number to output of solaar show * Fix crash when pinging a device with unknown protocol * Display battery percentage estimates from battery voltage * Add minimal support for Logitech PRO X Wireless Gaming Headset * Push settings when device requests software reconfiguration * Fix read for key/button diversion setting * Add modalias information to Solaar metainfo * Don't do on-screen notifications when devices are powered on * Add setting to switch crown between smooth and ratchet scrolling * Add write_prefix_bytes argument to Boolean validator * Update Russian and Spanish translations * New shell script tools to help determine capabilities of receivers * Add special keys for MX Keys for Business and MX Keys Mini * Improve tray menu ordering * Add --tray-icon-size option to get around bugs in some tray implementations # 1.1.0 * Fix bug when adding receiver to tray menu * Add Catalan translation. * Add toggle command to solaar config to toggle boolean settings * Don't select windows with no PID when looking for focus window * Catch errors when applying settings so that other settings are not affected * Add support for Bolt receivers and devices, including pairing * Revise method for creating system tray menu * Remove obsolete code (mostly Python 2 compatibility code) * Add support for PRO X Wireless Mouse, G914 TKL keyboard * Ignore more notifications that come to a device listener * Handle more device connection protocols * Update usage and rules documentation * Change emojis to text in documentation * Pare down device documentation so as not to require frequent updates * Add information about M500S mouse * Reimplement MOUSE GESTURE and DPI SLIDING settings * Add setting for DPI CHANGE button to switch sensitivity * Use file name instead of icon name for tray to avoid XFCE bug * Update documentation on implemented features and mouse gestures * Update Polish, Japanese, and Spanish translations * Make Quit and About strings more translatable # 1.0.7 * Don't use time_ns so as not to require Python 3.7 * Correctly determine setting box in change_click method * Handle fake Nano connection notifications * Lock on actual handle, not just on handle number * Mark Nano receiver C52F as not unpairing * Upgrade pairing/unpairing documentation * Don't signal status change when battery changes from None to None. * Add Japanese translation * Use first word of name for code name if no other code name available. * Better determination of when to add SW ID. * Check for more HID++ feature request failing. * Fix bug with new_fn_inversion setting. * Use correct device number for directly connected devices * Add debug message when candidate device found * Update Polish, Taiwanese, and Brazilian Portuguese translations * Add MouseProcess a rule condition like Process but for the window under the mouse * Add parameters for binary settings to support prefixes * Add locks to serialize requests to devices * Fix bug when reprog key requests returns None * Fix bug for empty process name and class * Rules can now trigger on both pressing and releasing a diverted key * Upgrade mouse gestures to allow sequences of movements * Fix gkeys diversion faked read * Add support for Logitech g pro x superlight receiver * Convert HID++ 2.0 device kinds to enhanced HID++ 1.0 device kinds * Update about window, bug report templates, and supported kernels. # 1.0.6 * Update sliding DPI to look for suitable keys * Add mouse gestures that can trigger rules * Complain if receivers do not support connection notification * In polling rate setting, only modify onboard profiles when actually writing polling rate * Add ability to ignore settings. * Use symbols for receiver sub-registers * Add support for wired G700 * Do not set attention icon * Replace deprecated GTK stock menu icons * Better handling of icons in tray and tray menus * Receiver c52e does not unpair * Match active WM_CLASS as well as active process name in rules * Correctly set icon theme value when regular battery icons are not available * Handle exception when device is not available when device is being added * Perform initial activation of devices in listener threads * Keep track of serial numbers in the configuration file * Don't update settings for non-active devices * Set the current host name if not stored on the device * Add setting for SMART SHIFT ENHANCED feature * Don't unnecessarily use long messages for HID++ 1.0 commands * Correctly select choices in solaar config and use 1-origin addressing * Add quirk for G915 TKL keyboard because its host mode interferes with its Fn keys * Show command outputs both saved and on-device settings * Update documentation * Fix bug in hidconsole * Update French translation # 1.0.5 * Update documentation on devices forgetting settings. * Improve help messages * Fix bug in finding receiver to pair * Solaar config command can set keyed settings. * Add setting for polling rate * Use long HID++ messages for all 2.0 requests * Update German, Italian, and Polish translations * Solaar config command no longer selects paired but unconnected devices * Show HID++ 1.0 remaining pairings value in solaar show for devices that support it * Add option to not use battery icons in system tray. * Update Polish and Dutch translation. * Add Czech translation. * Remove information on SUSE package as it is very old. * Turn GKEY notifications into Gn key keypresses that can trigger rules. * Push device settings to devices after suspend when device is immediately active. * Reduce unnecessary saving of configuration file. * Better handling of disconnected devices. * Implement GUI to edit rules. * Implement rule-base processing of HID++ feature notifications (depends on X11). * Add settings for diversion of crown and remappable keys. * Access widgets by name instead of by index. * Implement UNIFIED_BATTERY feature and use in battery reports. * Add a clickable lock icon that determines where each setting can be changed. # 1.0.4 * Update pt_BR translation * Support USB and BT connected devices that are not in descriptors.py * Use FRIENDLY NAME for codename if needed and available. * Extract manufacturer and product ID from Udev HID information. * Add Bluetooth and USB product IDs to device descriptors records. * Support Bluetooth-connected devices. * Add model ID and unit ID to device identification. * Support changing DPI by pressing DPI Switch button and sliding horizontally * Add device-specific notification handlers. * Add MX Vertical USB information. * Udev rule adds seat permissions for all Logitech devices. * Support USB-connected devices in GUI. * Make probe and config work for USB-connected devices. * Improve strings and display for settings. * Correctly handle non-unifying connection notifications. * Update GUI strings for several settings. * Better support for EX100 and devices that connect to it. * Partial support for feature GESTURE_2. * Simplify interface for settings. * Use DJ connection notifications to set device active status * Udev rule sets seat write permissions for hidraw nodes for device as well as receivers. * Handle USB devices that use HID++ protocol in CLI. * Use device hidraw nodes where possible. * Handle receivers with serial numbers that don't provide number of pairings. * Ignore exceptions when setting locale. * Correctly discover settings that share a name. * Don't show pop-up notifications at startup. * Keep battery voltage updated in GUI. * Add Portuguese translation. * Update several translations. * Add Lightspeed receivers c545 and c541. * Reimplement REPROG_CONTROLS data structure. # 1.0.3 * Clean up documentation files. * Update documentation on installation. * Update Swedish and French translations. * Add Norwegian Nynorsk and Danish translations. * Fix bug handling DJ pairing notifications. * Add Norwegian Bokmål translation. * Remove deprecated solaar-cli application. * Don't install udev or autostart files from python (or pip). * Solaar needs Python 3.6+ and probably needs kernel 5.2+ * Handle exceptions on dynamic settings when device is not connected. * Fix infinite loop on some low-level write errors * Add support for EX100 keyboard/mouse and receiver (046d:c517) * Add two settings for THUMB_WHEEL feature - inversion and reporting via HID++ * Update German translation * Use REPORT RATE feature when available to determine polling rate. * Improve config command speed when not printing all settings * Improve config command handling and checking of arguments * Add setting for CHANGE_HOST feature * Add argument to settings for values that are not to persist * Add argument to settings to not wait for reply when writing a value to device * Add argument to not wait for reply from request to device * Add settings for MULTIPLATFORM and DUALPLATFORM features * Remove Logitech documents from documentation directory * Change config command to not read all settings when only printing or showing one * Display hosts info in 'solaar show' if device supports it * Remove non-working smooth-scroll from M510 v1 * Add yapf and flake8 code style checks * Fix feature k375s Fn inversion * Update controls (keys and buttons) and tasks (actions) * Improved way to specify feature settings. * Don't abort on device notifications with unexpected device numbers, just warn. * Keep track of non-features so as not to ask device multiple times. * Implement KEYBOARD DISABLE KEYS feature. * Don't create notifications for DJ device activity reports. * Update a few special keys and actions. * Add keyed choice settings in configuration panel. * Support remappable keys from reprogrammable keys v4 feature. * Add setting class for keyed choice. * Only check for features once per device. * Use settings interface to show feature values in `solaar show` if no special code for feature. * Remove maximum window size. * Process battery voltage notifications. * Display battery voltage information in main window if regular battery information not available. * Show next battery level where available. * Update list of implemented features and provide information on how to implement features. * Add c53d as a Lightspeed receiver. # 1.0.2 * Add usage document * Don't produce error dialog for inaccessible receivers with access control lists. * Add option --battery-icons=symbolic to use symbolic icons if available. * Update French translation * Update installation documentation * Remove packaging directory tree as it is not maintained * Pip installs udev rule and solaar autostart when doing install without --user flag * Use Solaar icon instead of a missing battery icon * Use only standard icons for battery levels. Symbolic icons do not change to white in dark themes because of problems external to Solaar. * Better reporting of battery levels when charging for some devices. * Add information on K600 TV, M350 WIPD 4080, and MX Keys * Remove assertion requiring receivers to still be in window when they are updated. * Augment long description of Solaar showing up in repositories. * Update installation directions. * Install udev rule as well as autostart file when doing system install. * Add support for Ayatana AppIndicator. * Use setuptools icon directory on system installs when not using pip. * Add receiver C517 and several older devices. * Improved translations for polish. * Bypass bug in appindicator when solaar is file in current directory. * Don't check that device kind matches feature kind. * Better determination of icons for battery levels. * Use Ayatana AppIndicator if available. * Improve error reporting when required system packages are not install. * Better tooltip description * Add release script to help when creating releases * Look up tray icon filenames to get around a bug in libappindicator. * Make the default behavior be to show the main window at startup. * Support c537 nano receiver * Add logind signals for suspend/resume. * Remove solaar-gnome3 package * Ignore features for devices that don't follow feature specification * Add probe command to command-line interface to dump receiver registers * Don't terminate on malformed or unknown messages * Create fewer internal notifications for messages from devices * Add a button to the main window to terminate (quit) Solaar * Set up nano receivers as receivers with no unpairing and with re-pairing * Set up c534 as receiver with max 2 pairings, no unpairing, re-pairing * Better support receivers that do not unpair or when pairing replace existing pairings * Add information about receiver pairing to receiver data structure * Better support devices that only allow a limited number of total re-pairings * Add --window option to control main window visibility and tray usage * Ignore receiver if USB id is not retrieved * Fix bug with double deleting when devices are disconnected * Determine some receiver information from data structure for USB ids * Treat battery level of 0 as unknown * Fix bug on devices with no serial number * Drop support for python2, and use python3 throughout * Fix bug in remembering features discovered from device reports * Show icons in main window device list * Count offline devices when determining whether pairing is possible * Update French, Dutch, German, and Croatian translations * Better icons for battery levels * Support DPI, Backlight 2, Battery Voltage features * Support M585, M590, M330, MX Master 2s and 3, new M310, new K800, craft keyboard * Documentation improvements * Clean up directory structure and remove unused files # 1.0.1 * Updated the repo url. * Fixed typo which was crashing the application. * Improved the HID write routine which was causing issues on some devices. * Fix non-unifying receivers in Linux 5.2. * Add new Lightspeed receiver (used in the G305) # 1.0.0 * Too many to track... # 0.9.3 * Merged solaar-cli functionality into main solaar. * Scrolling over the systray icon switches between multiple peripherals. * Swedish translation courtesy of Daniel Zippert and Emelie Snecker * French translation courtesy of Papoteur, David Geiger and Damien Lallement. * Fixed some untranslated strings. # 0.9.2 * Added support for hand detection on the K800. * Added support for V550 and V450 Nano. * Fixed side-scrolling with the M705 Marathon. * Fixed identification of the T650 Touchpad. * Added internationalization support and romanian translation. * Polish translation courtesy of Adrian Piotrowicz. # 0.9.1 * When devices report a battery alert, only show the alert once. * Make sure devices in the window tree are sorted by registration index. * Added an autostart .desktop file. * Replaced single-instance code with GtkApplication. * Fixed identification of the M505 mouse. * Fixed an occasional windowing layout bug with the C52F Nano Receiver. # 0.9.0 * New single-window UI. * Performance MX leds show the current battery charge. * Support the VX Nano mouse. * Faster and more accurate detection of devices. * If upower is accessible through DBus, handle suspend/resume. * Replaced Solaar icons with SVGs. * Running solaar-cli in parallel with solaar is now less likely to cause issues. * Bugfixes to saving and applying device settings. * Properly handle ^C when running in console. # 0.8.9 * Improved support for gnome-shell/Unity. * Persist devices settings between runs. * Fixed reading of MK700 keyboard battery status. * Use battery icons from the current theme instead of custom ones. * Debian/Ubuntu packages now depend on an icon theme, to make sure no missing icons appear in the application window. * Fixed missing icons under Kubuntu. * Many more bug-fixes and reliability improvements. # 0.8.8 * Partial support for some Nano receivers. * Improved support for some devices: M510, K800, Performance MX. * Improved battery support for some HID++ 1.0 devices. * Properly handle device loss on computer sleep/wake. * Better handling of receiver adding and removal at runtime. * Removed a few more unhelpful notifications. * Incipient support for multiple connected receivers. * More Python 3 fixes. # 0.8.7 * Don't show the "device disconnected" notification, it can be annoying and not very useful. * More robust detection of systray icon visibility. # 0.8.6 * Ensure the Gtk application is single-instance. * Fix identifying available dpi values. * Fixed locating application icons when installed in a custom prefix. * Fixed some icon names for the oxygen theme. * Python 3 fixes. Solaar-1.1.14/COPYRIGHT000066400000000000000000000012101473525213700143150ustar00rootroot00000000000000Copyright 2012, 2013 Daniel Pavel This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Solaar-1.1.14/LICENSE.txt000066400000000000000000000432541473525213700146630ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. Solaar-1.1.14/MANIFEST.in000066400000000000000000000002271473525213700145670ustar00rootroot00000000000000include COPYRIGHT LICENSE.txt README.md CHANGELOG.md lib/solaar/version lib/solaar/commit recursive-include rules.d * recursive-include share/locale * Solaar-1.1.14/Makefile000066400000000000000000000035231473525213700144730ustar00rootroot00000000000000UDEV_RULE_FILE = 42-logitech-unify-permissions.rules UDEV_RULES_SOURCE := rules.d/$(UDEV_RULE_FILE) UDEV_RULES_SOURCE_UINPUT := rules.d-uinput/$(UDEV_RULE_FILE) UDEV_RULES_DEST := /etc/udev/rules.d/ PIP_ARGS ?= . .PHONY: install_ubuntu install_macos .PHONY: install_apt install_brew install_pip .PHONY: install_udev install_udev_uinput reload_udev uninstall_udev .PHONY: format lint test install_ubuntu: install_apt install_udev_uinput install_pip install_macos: install_brew install_pip install_apt: @echo "Installing Solaar dependencies via apt" sudo apt update sudo apt install libdbus-1-dev libglib2.0-dev libgtk-3-dev libgirepository1.0-dev install_dnf: @echo "Installing Solaar dependencies via dn" sudo dnf install gtk3 python3-gobject python3-dbus python3-pyudev python3-psutil python3-xlib python3-yaml install_brew: @echo "Installing Solaar dependencies via brew" brew update brew install hidapi gtk+3 pygobject3 gobject-introspection install_pip: @echo "Installing Solaar via pip" python -m pip install --upgrade pip pip install $(PIP_ARGS) install_pipx: @echo "Installing Solaar via pipx" pipx install --system-site-packages $(PIP_ARGS) install_udev: @echo "Copying Solaar udev rule to $(UDEV_RULES_DEST)" sudo cp $(UDEV_RULES_SOURCE) $(UDEV_RULES_DEST) make reload_udev install_udev_uinput: @echo "Copying Solaar udev rule (uinput) to $(UDEV_RULES_DEST)" sudo cp $(UDEV_RULES_SOURCE_UINPUT) $(UDEV_RULES_DEST) make reload_udev reload_udev: @echo "Reloading udev rules" sudo udevadm control --reload-rules uninstall_udev: @echo "Removing Solaar udev rules from $(UDEV_RULES_DEST)" sudo rm -f $(UDEV_RULES_DEST)/$(UDEV_RULE_FILE) make reload_udev format: @echo "Formatting Solaar code" ruff format . lint: @echo "Linting Solaar code" ruff check . --fix test: @echo "Running Solaar tests" pytest --cov --cov-report=xml Solaar-1.1.14/README.md000066400000000000000000000062131473525213700143110ustar00rootroot00000000000000# Solaar Solaar is a Linux manager for many Logitech keyboards, mice, and other devices that connect wirelessly to a Unifying, Bolt, Lightspeed or Nano receiver as well as many Logitech devices that connect via a USB cable or Bluetooth. Solaar is not a device driver and responds only to special messages from devices that are otherwise ignored by the Linux input system. More Information - Usage - Capabilities - Rules - Manual Installation [![codecov](https://codecov.io/gh/pwr-Solaar/Solaar/graph/badge.svg?token=D7YWFEWID6)](https://codecov.io/gh/pwr-Solaar/Solaar) [![License: GPL v2](https://img.shields.io/badge/License-GPL%20v2+-blue.svg)](../LICENSE.txt)

 

 

Solaar supports: - pairing/unpairing of devices with receivers - configuring device settings - custom button configuration - running rules in response to special messages from devices For more information see the main Solaar documentation page. - ## Installation Packages Up-to-date prebuilt packages are available for some Linux distros (e.g., Fedora) in their standard repositories. If a recent version of Solaar is not available from the standard repositories for your distribution, you can try one of these packages: - Arch solaar package in the [extra repository][arch] - Ubuntu/Kubuntu package in [Solaar stable ppa][ppa stable] - NixOS Flake package in [Svenum/Solaar-Flake][nix flake] Solaar is available from some other repositories but may be several versions behind the current version: - a [Debian package][debian], courtesy of Stephen Kitt - a Ubuntu package is available from [universe repository][ubuntu universe repository] - a [Gentoo package][gentoo], courtesy of Carlos Silva and Tim Harder - a [Mageia package][mageia], courtesy of David Geiger [ppa stable]: https://launchpad.net/~solaar-unifying/+archive/ubuntu/stable [arch]: https://www.archlinux.org/packages/extra/any/solaar/ [gentoo]: https://packages.gentoo.org/packages/app-misc/solaar [mageia]: http://mageia.madb.org/package/show/release/cauldron/application/0/name/solaar [ubuntu universe repository]: http://packages.ubuntu.com/search?keywords=solaar&searchon=names&suite=all§ion=all [nix flake]: https://github.com/Svenum/Solaar-Flake [debian]: https://packages.debian.org/search?keywords=solaar&searchon=names&suite=all§ion=all Solaar-1.1.14/RELEASE.md000066400000000000000000000012141473525213700144300ustar00rootroot00000000000000# Solaar releases ## Please read before making a release We support two type of releases: normal releases (ex. `1.0.0`) and release candidates (ex. `1.0.0rc1`). Release candidates must have a `rcX` suffix. Release routine: - Update version in `lib/solaar/version` - Add release changes to `CHANGELOG.md` - Add release information to `share/solaar/io.github.pwr_solaar.solaar.metainfo.xml` - Create a commit that starts with `release VERSION` - Push commit to Solaar repository - Invoke `./release.sh` - Git tags are signed so you must have GPG set up - You are required to have a github token with `public_repo` access in `~/.github-token` Solaar-1.1.14/Release_Notes.md000066400000000000000000000243031473525213700161040ustar00rootroot00000000000000# Notes on Major Changes in Releases ## Version 1.1.13 * Solaar will drop support for Python 3.7 immediately after version 1.1.13. * Version 1.1.12 does not push settings to many devices after a resume resulting in the device reverting to its default behaviour. This is fixed in 1.1.13. ## Version 1.1.12 * Solaar now processes DBus disconnection and connection messages from Bluez and re-initializes devices when they reconnect, to handle to a change introduced in Bluez 5.73. The HID++ driver does not re-initialize devices, which causes problems with smooth scrolling. Until the issue is resolved having Scroll Wheel Resolution set to true (and not ignored) may be helpful. * The credits for translations have not been kept up to date. Translators who are not listed can update docs/i18n.ml and lib/solaar/ui/about.py. * Solaar now has settings for features BRIGHTNESS_CONTROL, RGB_EFFECTS, and PER_KEY_LIGHTING features. The names of keys in the Per-key Lighting setting are for the standard US keyboard. Users who wish to modify these names should look at the section Keyboard Key Names and Locations in `https://pwr-solaar.github.io/Solaar/capabilities` * A unit test test suite is being constructed using pytest. * The Solaar code for communicating with receivers and devices has been significantly modified to improve testability and organization. Errors may have been introduced for uncommon hardware. * The Later rule action uses precision timing for delays of less than one second. * Solaar now indentifies supported devices by whether they support the HID protocols that Solaar needs. If a device does not show up at all when running Solaar, it almost certainly cannot be supported by Solaar. ## Version 1.1.11 * Solaar can dump device profiles in YAMLfor devices that support profiles and load profiles back from an edited file. See [the capabilities page](https://pwr-solaar.github.io/Solaar/capabilities) for more information. * Solaar has settings for each LED Zone that a device supports under feature Color LED Effects. * Solaar has settings for extended report rate, backlight levels, durations, and profile selection. * Solaar now partly works in MacOS. Please open new issues for problems. Solaar may work in Windows. Please open new issues for problems. See https://github.com/pwr-Solaar/Solaar/pull/1971 for more information. Fixing problems in MacOS or Windows may take considerable time. * Solaar works better when the Python package hid-parser is available. Distriubtions should try have this package installed. ## Version 1.1.10 * The mouse click rule action can now just simulate depressing or releasing the button. * There is a new rule condition to check the hostname. ## Version 1.1.9 * Solaar now exits with at 0 exit code when killed. * Two Solaar settings can interfere with the implementation of smooth scrolling in modern Linux HID++ drivers. These settings are initially set to ignore so that this interference does not happen. * The Device rule condition checks for the device that produced a notification. * The KeyIsDown rule condition checks whether a *diverted* rule key is down. ## Version 1.1.8 * The thumb wheel rule conditions take an optional parameter that checks for total signed thumb wheel movement. ## Version 1.1.7 * Solaar responds to scroll wheel ratchet notifications by changing its scroll wheel ratcheting. * Solaar processing of report descriptors is optional, depending on whether the package hid-parser is available. ## Version 1.1.6 * Solaar requires Python version 3.7. * Solaar uses report descriptors to recognize unknown devices that use HID++. * The Later rule action takes an integer delay in seconds and one or more rule components. The action immediately completes while also scheduling the rule components for later exection. ## Version 1.1.5 * The Active rule condition takes the serial number or unitID of a device and checks whether the device is active. A device is active if it is connected (via a receiver, USB or Bluetooth), not turned off, and not in a power-saving state. This condition can be used to check whether changing a setting on the device will have any effect, as devices respond to messages only when active. * Solaar logs warnings and errors to a file in the user's temporary file directory. This file is deleted when Solaar exists normally. If Solaar is run with `-dd` or `-ddd` informational messages are also logged in the file. * If the first element of a Mouse Gesture rule condition is a key or button name then that name must be the same as the name of the key or button that initiated the mouse gesture. * The Sliding DPI and Mouse Gestures are now set up using the Key/Button Diversion setting. Changing a key or button to Sliding DPI makes it initiate the sliding DPI changing. Changing a key or button to Mouse Gestures makes it initiate a mouse gesture. There can be multiple keys or buttons for sliding DPI and multiple keys or buttons for mouse gestures. * Solaar waits a few seconds to save settings changes to its configuration file. If you interrupt Solaar soon after changing a setting the change might not be saved. ## Version 1.1.4 * There are settings for sidetone and equalizer gains for headsets. * The KeyPress action can now either deppress, release, or click (default) keys. * The KeyPress action now inserts shift and level 3 keypresses if simulating a key symbol requires one or both of them. So producing a "B" no longer requires adding a shift keysymbol. ## Version 1.1.3 * Solaar uses yaml for configuration files, converting the json configuration file to yaml if necessary. * Solaar rules work better under Wayland but still cannot access the current process nor the current keyboard modifiers. * Solaar uses uinput for simulating input in Wayland. See https://pwr-solaar.github.io/Solaar/rules for instructions on setting up uinput correctly. ## Version 1.1.2 * Solaar now depends on Python evdev. It can be installed if needed via `pip install --user evdev` or, in most distributions, by installing the python3-evdev package. * Solaar rules partly work under Wayland. There is no access to the current process in Wayland. Simulated input uses uinput if XTest extension not available, requiring read and write permissions on /dev/uinput. * There is a setting to divert gestures so that they can trigger rules. * There is a setting to disable Onboard Profiles, which can interfere with the Polling Rate and M-Key LEDs settings. The Polling Rate setting no longer disables onboard profiles. * There is a setting for the Persistent Remappable Keys feature. * There is a new rule condition that tests device settings. * There are new settings to set M-Key LEDs and MR-Key LED. * There is a new kind of Solaar rule action to change settings for devices. ## Version 1.1.1 * There is a new setting to switch keyboard crowns between smooth and ratchet scrolling. ## Version 1.1.0 * Solaar now supports Bolt receivers and devices that connect to them, including authentication of devices when pairing. * A setting has been added for the DPI CHANGE button to switch movement sensitivity. ## Version 1.0.7 * Solaar rules can now trigger on both pressing and releasing a diverted key. * The new rule condition MouseProcess is like the Process condition except for the process of the window under the mouse. * Mouse gestures have been upgraded. A mouse gesture is now a sequence of movements separated by no movement periods while the mouse gesture button is held down. The MouseGesture rule condition matches mouse gesture sequences. The old mouse-up, etc., tests are converted to MouseGesture conditions. ## Version 1.0.6 * The sliding DPI setting now looks for suitable keys to use to trigger its effects. * If a mouse has a suitable button it can generate mouse gestures, which trigger rule processing. Mouse gestures need to be turned on and the button diverted to produce mouse gestures. * Settings can now be ignored by clicking on the icon at the right-hand edge of a setting until the dialog error icon (usually a red icon) appears. Solaar will not try to restore the value for an ignored setting. * Icon handling in the tray and the tray menu has been updated to work better with some system tray implementations. * The process rule condition also matches against the current X11 WM_CLASS. * The SMART SHIFT ENHANCED feature is supported. ## Version 1.0.5 * Solaar has rules that can perform actions such as pressing keys or scrolling when certain HID++ feature notifications happen. Users can change these rules either by editing ~/.config/solaar/rules.yaml or via a GUI. Rules depend on X11 and so are only available under X11. This is an experimental feature for Solaar and may undergo changes in the future. * Each setting has a clickable lock icon that determines whether the setting can be changed. ## Version 1.0.4 * Devices that connect directly via Bluetooth or USB are now supported. These devices show up in the GUI as separate lines, not under a receiver. A device that is directly connected and also paired to a receiver will show up twice, but the entry under the receiver will not be active. With this change identifying devices becomes more difficult so occasionally check your Solaar configuration file (at ~/.config/solaar/config.json) to see that there is only one entry for each of your devices. * There are new settings for gestures, thumb wheels, adjusting the wheel ratchet behavior, and changing DPI using a DPI Switch button. * Solaar's Udev rule now adds seat permissions for all Logitech devices. Users who install Solaar themselves will have to install the new Udev rule and activate the rule. One way to do this is to restart the user's computer. ## Version 1.0.3 * The separate deprecated solaar-cli command has been removed. * Devices can be switched between hosts using the Change Host setting. The device will try to connect to the other host. Some devices will detect that there is no active host on the other connections and reconnect back. ## Version 1.0.2 * The separate unneeded solaar-gnome3 command has been removed. The packaging directories have been removed. * Non-unifying receivers are modelled better. Many of them cannot unpair but instead new pairings replace existing pairings. * Battery icon selection has been simplified. Solaar-1.1.14/bin/000077500000000000000000000000001473525213700136005ustar00rootroot00000000000000Solaar-1.1.14/bin/solaar000077500000000000000000000035201473525213700150070ustar00rootroot00000000000000#!/usr/bin/env python3 # -*- python-mode -*- # -*- coding: UTF-8 -*- ## Copyright (C) 2012-2013 Daniel Pavel ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. def init_paths(): """Make the app work in the source tree.""" import os.path import sys # Python 3 might have problems converting back to UTF-8 in case of Unicode surrogates decoded_path = None try: decoded_path = sys.path[0] sys.path[0].encode(sys.getfilesystemencoding()) except UnicodeError: sys.stderr.write( "ERROR: Solaar cannot recognize encoding of filesystem path, " "this may happen due to non UTF-8 characters in the pathname.\n" ) sys.exit(1) root = os.path.join(os.path.realpath(decoded_path), "..") prefix = os.path.normpath(root) src_lib = os.path.join(prefix, "lib") share_lib = os.path.join(prefix, "share", "solaar", "lib") for location in src_lib, share_lib: init_py = os.path.join(location, "solaar", "__init__.py") if os.path.exists(init_py): sys.path[0] = location break if __name__ == "__main__": init_paths() import solaar.gtk solaar.gtk.main() Solaar-1.1.14/docs/000077500000000000000000000000001473525213700137605ustar00rootroot00000000000000Solaar-1.1.14/docs/.gitignore000066400000000000000000000001241473525213700157450ustar00rootroot00000000000000# ignore documentation-specific files .jekyll-metadata Gemfile Gemfile.lock _site/ Solaar-1.1.14/docs/20121210110342697.pdf000066400000000000000000005320701473525213700162730ustar00rootroot00000000000000%PDF-1.4 % 4 0 obj <> stream JFIF,,C 2!=,.$2I@LKG@FEPZsbPUmVEFdemw{N`}s~|C;!!;|SFS|||||||||||||||||||||||||||||||||||||||||||||||||| " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?𢁠[`sج נ((((((PEPEPEPEPEPc=h-((1EPEPEPEPEPEP`QZ((((( ( ( ( ( ( ( ( ( KE&)hP@(((((((((( ( ( ( ( ( ( ( ( (KEQEQEQEQEQEQEQEQE&(((((((((((((((((((((((((((((((((((((PEPEPEPEPEPEPEPEPEPEPEQEQEQEQEQEQERw(((((((((((((((((()1KEQEbLQZ(0=( Z(6ؿ:Mv`zR@ اGFPmR@)0=)h h{jf((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((+c<~!jWtC5@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@~DԬv N ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((/ß?6Jy?6J(((((((((((((((((((((((((((((((((((((((xQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEᱍ#}R9 XޓCjԠ(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ڵ++_?6Z(((((((((((EѻQ@88HҀEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPW cjլ F=A֠(((((((((((((((((((((((((((((((()H:(((((()I:(((((((((((((((((((((Lh(((((((((((((((((((((((((((( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (2|21C֬ a$f((((((((((((((((((((((((((((( #5Rg GQSAuɠF욊k֙ % ;NW&9e Q9a:Iݖrq* ^9'&iV*2rM?ZAEFҁ~cU^HE(*0rؽE GCU*O"F.NȵEPHQETfdjnoiQERI.'\EV[lE.J1rصEU{WX=hYa=ZM;x8RA2ʹ{ҹ-QA!EPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPH-QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE,ʹ?\Rq@ EPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPG??묟z̓?ѭz(((((((((((((((((((((((((((((<}5j PU}*k22.ߧZ3!ܩniu:c(OSU wJ}Ny'D<߽Uh#L,FAb;c+Ko9]<IJxJ\,"ʁqCTOLQz2F`A܎ΰ]qW)03s@)S6kvd<:ӚBzz @AA~?]I !$[ p+@9(8>d+A39bTN ^,UA4{OGZYHj:w[0(2 #oGPN8QAP+%0\`^[6L W(jm;'cAQ7P5~Ls'Gp*6d`6O|օ U؎8#l%@4PBAEPHQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEო+H?Zc2A<((((((((((((((((((((((((((((((*+y?,Ҭ) qpZ`AA L~${UcgsVh aEPfQEQEQES]8'V7>b5 &'Ty pjQ-QAQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE&G?:\Q@ E&E-QEQEQEQEQEQER1ڤ VtQE ( ( ( ( ( ( ( L□QC)恫75^o1vE-[ӮFh+rQ[&>1Š(ALF)uȠkr;iI ⦪Zs| & 7^qV(%(AEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP/NtGÏֶ˜ϟ_VQEQEQEQEQHsڀ(((((((((((((((((((((*)H.~ց۲%ܛ&:Š(AEP{e]eOCUW4kB IEYQEaEPEPEPEPIKEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEI`*J((x/DkZ@l,m [BV-"+ϵJ4&Sv:n=+u&Ȗ&S0mOT.p%qRve)Kg Ţ((((*e]0]4M&0RvHQԊQF4 2)hECdϖNZvbc>h5˲T4JIiQMFquAEPE򿊁Rl] +f5m1w7zQoAZè($ʚ2שs*+JTlW)sj6bsl :TmnR;|9g s7bőXO(jv^:}E[de*Û{eÊ;3 ;OltJ)R+oZx5:yc]eX'Ys85+Ȉ2`Ӱ*8Pg=Ey`yGF r A4B "HcH^+n*H=A^ƅQ-?v㍫Zbf͸(IecyBgiē. <{ e3ޞ*lj}#֦8Р@5n]P=b(#>s;ɹU5jHQF u(1MQA!T-ܸ/9>v ]ѕ50CT s>c4:{)R*FD;N\]ċ%RE7wr&6}yhm1 agI%rTPϹp$G*95%P QEHIdKEn,H#)}Zb!GsvojE{Y}EQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEso[c +<_PEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEQsiɨ {;Ee,H BOk~*/' ?,Sͮ8!@X-CI3 D\eG5GN?ZHHǞ(^D\tW,`sGh>ytzA}*ʅ@ZμᵚTyrNj-;I$}3 cր7袊@QMwXԳ@ƅ fm6?J~dq5~($ڃ%QQA)QLQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@&-QEQEQEQE(ALR wmGB(54-!bYY 0 U47TZ@_܍!U>%PCmQE ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (9{O7?B (((((((((((((((() ɥ`H=ciWr\aITu\:A]E6ERM} 7pڹ>l1Xus<UTzvq-XT/SNk, }99Z:fms`%b`Oj\6gFoLժ⯬%,VSu:Lq,.A'fC5'+`H=`k"{ U"EJ=!qd"Rp*/dby.%1*P0PZn68$ S袃-Š((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((" #dnSkKco}*P9I݅Q@(V@|I@`(QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQExC?eS!] s[o+((((((((((((((((*kl-g04Lj$<ti.9Cli!eX@5ɪNsT` }xB;FnLh_ص)Rr)$fhYE$AV4mC'r] ޠlf MxE~[4EK7AQ4e%$@k}PP"bz{۬MCP @_3ZCM(QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQES<<>FA₢rQA"dgjW7;Iky@O^ zQ@Q@!8KQ\kwhWv$#Z` TQv (B(((Jy%jEP ( ڟ X YPI!1Ulix9'4]Ϊ2 D_EP ((((((((((((((.È*s.egq!2%~sO{(uZzrRiQLW ( ( ( ( ( ( ( ( ( ( ( ( ( (9{O;|=.[ ((((((((((((((((ӮTw ER?Jt؊\zI+/꫘aAI=ZCL2YfqJ^r3jAdѩ^Cryqlc&]_PGfQ NN z3'ԫ=[O{N؁&@HtZ@r:gR`~ۭyzz{y}]O嚭+L>Vr8vq|k~Ah@tTQE ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ϋ_+BfPkK4( :£") w\}Lfz?,tQ 2;BAj1_`jA5 (0(a#֖`v"~Tr&^RJϽD~$ QE ( _*.:EBE|U%+I 1jeJ6_9?$]çjWPU YkvjY Iok;6fsAqMo0=1-V]ç$[R$PfyıEG XqrP$8ȠJnȵED."#;9eFVcAEPEP &ɷ8SRCr9fA;$ =[5<0tf=qI3PKf84*1asBC#  kd~uG:s0ȁj ns$?)REU. ={PCNq`:dVt{Wp#55| ;P\\EC4:j1{'޴&TUH Τks>"C>!uqkF?F!¸'Ң@A4Hq,v\Ϊ2F1'ӥ(fIUKx%`7ɤSa#шD5bZyʑRFOYqi\*in}.1gҠRkźAq4Ȳ eiB)8T\O&LQh6*6xU` @Ţ($((((((((((#AI]s?tTQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQERE-ۡrnXMt}wĐo$A?κ:l2KtkF}w3QKvi@{9 ~q,grX1Yr4JZUx$H84'gmYիx C?x!Wu%1p:(Y 3a٧YT;}f_,~^Oȧ=4[U]Eىܻ$+Ѫ_ '|gӑRI5NLUzd5Ct)$P(R fVI_̙ڲ_(@oE((((((4PEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPHzqKE ZZ(((((((((((((((((((((((((((((@00:RU5D 5n4r̅[CRU53Vz :56SlmUiJL0^wNQ)+YGU8[IڐLr\xPI_sbX4#Ef7 ejNjWvylG:Pd'm0g+b<jp4Ļsb2I= ,H rVr Ңxr JѬ`ڑ^Bfr*~jUe s`w \,/dhN2i~?#Y5 J51=(\9uow$̪5] KAi(JeV*OqOZ~AӲcԄ9ϹB~e9W ZUWPϡ)[az+zedg6LUI*re[ 9:U:QA|MUNv($W6wlOtQOүA{R(O)Rj?OsV(ROW1z=3M}=b5r'ܨXeFWQA.n[QA!EP%K-pnZт(p,MTZ_RGZ잍 5vqV9K(921j@ҝE)[DXeIS`.1u{X!$4o{(+ϹVy7R.Vh^W!6BZć!s榢s˹Zk$PR-cZE1VMqUM씭]i`3Y$a)F$r))nJeH⢴⍄9Q@U%!5d3E\ݬʍo$wjyQ*(%ɵanRzD]E]Xl,{c2]c`xZP( ((((((((((>]@<(((((((((((((((((MO ;m5qfV?RzV6[>RY:Tk5z@T]tjqGh?@Q@Q@`ڮ2\ӗ`G5YZEshCqš$_?Vqk@#ӎ?((kr@'6)wE"Si,gQEduwr&r.GE((((M;oӂIEPEPEPEPEPEPEPEPEP\ψo3QG󮚹u_*@8ҿk^JLvDa@(((yCp <FAȠvbE( QEqʍ/JZ0H?Ju ()Cp{#v\7!c֬ZB 9#AkEQ@ݖ[w*pqڢi"!H5bP lLUpEW(@>`L<ÜPvl$)Q1E1XX✬C)4E(((((((((((((((((((((((((L-!=h(((((((((((LzZ(3P=x(((((((((((IH0 6+x9EԴP>gk\(QEQEQEQEQEQEQEQERt+i{n ۸zM-}.B֓4hg8QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEx8S(9_#T>/j(((((((((((((((((([Ns+E jW9fb&?[tcx=֑>9Y:d٬?g(>VǶh@Sdvmr2O_\U][v%˞dnyFqZZ6 ̸PutTsM$GsYĻп#PmyAJ w7VrHk0<9\tnDUy AHR"m͑Ҵ4kۛY<8$*{ ڱkzuP3RI"DuU*f-KP{R =hx|2+LIYeYi#`Ҩhڤt \X9=1ڀ: +[{ي}x5H(]K*5 )h?(I$W?zhizDJK``* pv?hאO"ȃQY*2" 9 Wzԓ+-cq\Vwg*ya֓Fӣ&`{()V 2MIEsa~~&#S3iΑѹ_\hwc܏4YےPJF^+cDb. SR 5Rk/Bx^BT#χYaV9c$pM_= ˺-RuzW51ULU9pAk6daER*23KEQEQEQEQEQEQEQEQEQEſXWQ\Ơ;yt袊@QEQE$X,qOn^=ߥԣmK!~,mpYKm#Er[=Q`6x4SwI"ƻUsyk)oxCʜ֩ l6Þ&ȧ8ݽKh$af{9*% R {bjG=ҟ|_85 xn<ի!JC^TNJA`"+whD E,qT"FQm&ndPr}jl.Y`%9;K:bYYk)ƲѸX`OwRvPz%ۏ*1us .%9mJ=Ŏl8 p)47 0)hTY!9<ʽ֡ˬ؜+JHɃ3RYizu4n(EPEPEPEPEPEPEPEPEPEP\֣s/3y}i<WS@tQE ( ( KQd;*!˕ӝH#Ӭs_jF׽p)@=Fih B20ih*c<Ө ( ( ( ( (U>() EPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPW >IoKlmOԒ((((((((((((((((((((((((((((((((((((((((((((<uf ]j<٫(((((((((((((((((=r`q qW&~]r?X)QEQEQEQEQEVhe={bj7Prt҄y[Ne]w:B 2'BXHcXU' cZZ@QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEkeW72ֶMQELZRUw/n`@Ţ(QE|;Őw"շ(afjItԖOGu⑼i,QE((**qwb d ISP Y(QEQEQEQUc@[رUo7_JXnkTҮ}E$%qy)=zQ8Zz]94Nm4Scq$j*/SA TgVu^) dY7€*7,MVhfQE ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ('4Z\IrNBURƭ\( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (9gt_z"ys- ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (Zpc|}of3H?V)QEQEQHN:, O4( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ._5+U50QMQEB2CMiTq!c*Pg)9;($((>ԴQEQEQEQEQEQEQEQEQEQEQEQEQER2aKEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE*$q>p.T{f$&Š((((((((((((((((((((((((((((((((((((((((((((Oo8WG\#_Һ(((((((((((((((((¬:uWR#]MQE (2 F2AYSkwF:/_ d^ Y,đ5xm4gHF7=遏 jANLqU9렮 r`֗t˧Ws[Ճ;4G1!QOm>?IHަwV)QAQvwo' KT25i4QEQ@Q@^Ճf'jX}6ڬA~VRAZ4r ($tQE+*ZgQA[ym2m6#Tkbu AA^T:(>FA8zv1u5(AAm[ EP@QEQEQH4QEQEQEQEQEQH̨bM8C J["`giQFq@ yc4r2*-lO+V@75Jj;qjNHh&Q:((((((((((((((((((((((((((((((((((}D=gAus9?҃ZZ^]01KEQ@Q@Q@Q@Q@Q@b ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (9 5:}*+¿wPoPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP'q:*ҺDJj_-~ ki|߮) L6K/r3U,C4n>Ӊ#q+Ee@C55] );hYz֡{WH$r@p=kS얼ؒ  Ѐm̰N' w$M+íiGÑ/pGJX68>>^&<9ٮ_of JHH&28"zdxM¡&({iXrEu?R{G⡞h2]GIYCG}I.m~b_ s AFt`>Rt>Xu; q (s3N?IKjHFCZk}1}xBLˏ 5"p) 8V[(>PN0lȠ=yU+UJSAߕHl=8J؎H=xu'q*JIԯpcw#hPjQ 8b>^H (V,2!hO'8@]l`jP4k#AN%HxβwxcҶa nDP@lN0 c9?T98uSn}j X^@5;h w)fKweu/M]ӼLet@o[`N؏DSެiWbBvչ UXqo 䌎}*g,Y 岒G\#P]ro(3fXQ]y &TIV-Մ@IM+`p¡Hnx`55k4N 2]G33[kLM?Mn6=ҤKH( Pe/OsWjV8qV4aMvڌރ4s, ƛA1Wv%RVM\VMsVɋnf'QsIQz6f(VOZ{0QqIïkMH%#9F=kTeFW9iE s0@уFky@v.?r8+³3s@r ~WFz"|?k+_$XF2A3ӽVl Ƞ(QYo3wWn|:MuOƷ|߿j7cSf,V41RXipK,3&z@1G +)b(;Z)FT8!\Mt4PbhvbGB:H& xm-vJ8jMOEVX!nF3REQEQEQEQEQE&sZZ(B3KEA+LS 玠(t+X_t9V!PLӕU*v(((+@+~Jh(EP[ϔ#n@ޢ}jH.NxQEmWeۯ~j?UB Uf5<•+,cxn҃y%'w|;+0iQdLŌS[CǂrLI rZ*`.AdAASc *8<2*3`7J`k5 fXAסeH"eA8OA.Obj 6&jњ1`CQ#K47՗?g8j+fBKL͚qKb@01KAQE^76:j #A`y^# ުZ[<?Ae5rKm,SmlYe'${.*x4+(VDG?>Ԑ鶐6R3VQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@GRVzgn)$YCƀEPEPEPEPEPEPE! u }ih((Hyzʜ+zu)utSS_:Q@][¹RE-+A%(3H$ Z(((IJ0뚅laW݌zE)I+& `5#(Wh)THju(((((((((((((?2PH8SLUEh),71+!Вqlrh>N0+XpCojr9oSS*Nl(((*1x?JHJ=h.d6 ^F0EG9\H 4ݛ379jX_4T0-3;Š((((((((((((((((((((((((((,jYxv>(((KB\ӆeɭ |V (((((((((((((((((((((((((((((((((((((((((((((s3:ج 3ֶ(((((((((((((((((('_8LVw.#򼞲)ݏlm80,E(Rp5">3-(.l&OuT@=X杀ޢY$DuƪBsc֢vd⣒(2u9Elu 1\6OjYln,Olҋ4s$áAO#11=BG?kRPLgmM@Sh݊a@ou{Gك# 5-er8 QEQEQEQXkrG5È98 ( 51A5r9$7@HL.{qkV74IlEPEP"ʮt@1k TzO(栻u J1t\F_?ֺl(EPEP?`h| MnH۞> ^C%ISH0:(Q@aGT"+4.O?jh #]TGEˤ1Bm9U@QEWIS. f/҂QESd1j}kQ@Q@Q@ uY]A^~[M(B((((<zSjffAn+( ( Fӎ)YL9 'je4WN5AUfT3\8'>YP:4r:4aD?iFg֖RCZQEQEQEb((((((((((((((((((((((((((((((((((BQ@[ $n(QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQH((((((((((((((((((((((((((((? ?O +#_ ϬU@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ح,uZ.HW\@[dX1_@қQIH}ˌVYfPW>?֥KHV*KwE>U"$v9;W0/*(84ș[a#%@S:@8rvfi $B&,jCe}#4qW5SO9mOȩ.Moω0{c0/hmtrJ*O=0MQhKʙ:VS0 t2'U}=cOX3zFeS+gzWqYN`mK~L7ȇSxv a+Kw<_ΰ,%'L:wɮ]84(ǻTңݪIN%Pd˨Ժ ̠LtaY3ŐTsUmːZ\O KxZY[j(5mY1x}Hyq l?"&w8X2zKY>wk&V*>V 1uBV#1jEs/}jf6 =Gz+ҘQ]H2lؒ3m}LJǯ'\VM\cy=v[@av,,Z<8+sY<>.o,'rIax&5*E9c=$I vUF:Y>U9@yޫ[hR2,ހ |:. rё,^}?2vQgFs@&G^Ti<8 f \ypsBN#,ild*Fsi%=FbU䷕fsԁӄS| $S"kg,\, rUEg>dcsU`+x)A Qt>q KFqVȮuÎHBbVVܩi0Vh߃*IPgpzRk4BF\(7rb$摯8D-R (A?/ᚙcDnDEŰ{QAwaEP ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((('?Oj<^j((((((((((((((((('cUn)ƒ jkb~R?_ ֢8;sTD.«3&dՋh-|\c*8 }@+5m3;B#[It7Ck[qֲ;l! Rr޴nlV)Y@j]+N[y1c2 w\v*XAzL:F hH~BKb\i8=iÌeUt4P]m' )bxҬ]gsPt:-Xݹ9_B x<.riQ@Q@Q@s~#O؉$׮H^R2~d4٣oZUJWWT#"Ť)Y<+گR*Ps3dYqY3Ͻj\<7,G zPkG i1Nӧ]XK;H H#ԑo''6m2kfc9-g2V @Up-?2#TpHY[7sVpحeD;ڨjcDYUAֆ˾xcv]Rnx#XeUfrBwj6֬Q@:}HӊtVBrϩ楢yk\Ua-.űҋR_͈:(RlPa$gMi1Xw L0]cN9fcRKg ye@V(i'ԥ?xOl" mNJr:t($()`R@ KEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE1\pZ}Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@RdzIFr1@ E4Ȁd4hI)t_?IB$D[G} >- gyl q [?B[x@Q5tLup'h\^)RsQ@P}H4"i7|Tk4i |hj*)nÝ6?ʚnGlgT= PJA84϶N홠 4U3 5@uIb"Efzu2k$p4 8ߟLQmr, p#@!h^_ڶpI Sgmb!~}5K 2)<:Rni:IxUO %# $Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@><8 ֵdhĚkPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPWt9*܏ZbxbD_?}bxm?,.lʱsPeE=nҌpKӞi% 8Oʜ]Ӄ.Qss(Qt: ~T)@=M>Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ OG ʰmSmQ@ 5%1Q)Q@Q@Q@Q@Q@Q@Q@axIջ +vH ^BO0gz6?:Ԭ  9'MhPEPEPH@=ihQ@Q@Q@5:=Q@L]DkN<~wҤQEQ@!Pzih((((((((((((((((((( ұ]bg{K=8߃Ԛ[w}?bi'G1Y::O;,g1u-1u ~k3s]:3g>E#ѝbdf26~ahOK:5K3 &䍹S@,F?׏sH ܧG3h:U90ӗKQŴg3Lk~Ѭ@S:Ϗh:ajFog)Xk6'>V#Ņ:}xvmXH*iNb?:jZ[0FGM +.dĀqn8̟Bs#f6(ld 4jǑ)+bAQ@ddy4xnqf1"T $:Y1M>!Z 4G@^>[?ZEנcF>0zƜ(F:"˜oT^"B:uGE=@+}g+ U4,`uVKH a9}OZH; lS} `*iכ?-j*P( @?*Xbœ5ؤ_¶( _ْ=]>3SI:gu8?ʜ5%~¶ 6nKeϡ1uۭ6+o@V?55B5n@V3[|ځꧭ;?[PjZm4Ta'5EaXpܥ7z2G_[tQp1 ư@9i~ӫ->G\ EXW&=)Ln( MoE(uyq kf. ]h>/V\ _t)V)?[4Qp1i̴j#$]а ע-#IUӥ%  (RY^e̛B_N:}ǔfV\ E\rVSOLeSڴPgIǴ鋷Ea$+R:A1#VE+u t贸B~yr9"F) aDC:v#Qi3aօ\ NJN<ǚѢV+R{(L F9f@ec'Ǥb\@;yk_,-ȶ[E| @VK(Uٶ!m#p*+Tp( !4O"xUʓý?jt~T( :@w69R*IE4 _''ߊuxLdǣR@-PEPH@=ihQқh?ȩhż#QlP1cNU҂NJʝE7bt~Tm_ʝE&h# Z(t#PБP14$ŃFA4ė1*M@2%QH(((((((((((( cԬJ(((((((((((((((((Rӣ?ցϸ5 6Q2+\tER(((((((((((((((((((((((((((׉?jV~ Ѐgڴ3M.:((((((((((((((((((((((((((((((K̡gӊM4<#>ֵnPm,`EcUv?8EnW7 m K!¨ZO#ob1Ƕ?ƀ35ˉ4J>OMd-VL|1oʵcyJF~]Sk;`"&C#X涍9XZEQ@R2R #8#EA1O((((((()ʑ(g8=@(((B@@ EPEPE5H.r@iQEQEG (PZ܋wAVPR3R@Q'UU]drJEPE2NQK9 Ojť 1qUpu&ˮ,笧?^@ EU>e""fG= NydMa耎[@)qa6-.ƿyI$H/#UMVϹM&SGݽ)R٦q-,V,SO[H]͎w>e|y ք_NOG֟ )a#]?_z`8 3qZZB2)^nEEr#;KDvOpR] qH1s 0924袊@QEQEQEQEQEQEQEQEQEQEQEQEghCniyִk;B=r@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@^$#Pz1Q֍ż`t?gxgG9r": ((iHuL_$Cjq遯#N<. :r ڮ:Ң5$?kiR)jj1+[;*㬖Nl̀k[[k])ML"k`4xPjMLpdoƗ[͖ ڇ4nϪab"RFch&?]eԣPTs,2yA§Xwj (iH"Bۧ3R@P2IU#Hs Pڕ"bIwXлUQOAYR,=z~o:\G :Ӥ&VnzЪ@DAǠ X=j8<žI 0$u:*HQM+!=MIkm_Ğ@HWwްۮ]wA}..nǢSVJ w7sj;kdB$噹,}ʐ^F =TFpegI@9y3<2OIQ$*I# 80I\!#IEA=ʱ{_POqޠ KTm. ‡J.5 KYDSΑ*.R F@e @HFeV-Zb98ɫy@r{Z}#\ MV+yG_ ֖! {rY1MVᰒ5\@j`]L$ϕzn s !1#%T-Jr1ҟo~od(Ì{Sċy:mIŽMfG LN2), lp3T{M_\u>-\ (9}ϽK}y5K`wL 'R)צj[(;1 +;WEc vuRdmNx=9FO4JDQFIWW\MŸnIzO9M03q H?}5rXP%Tk6;YE} f9ڽ}})=iwg>y!A$8p&4=(.f\_؀jԛMߏ9rs)EZP0_݋9oҴ pUn\ fFqeW5>Y$=1W0YicEDPª^K;o)g}b lUbXSj9$'[@ЬQQ>楪^HT\$C0di.뫑"¨4$==m K+mUj;g[6I GE ow;\rW2D?}>r9 n@p3z]" j2'O3=o +;?]&;x*)"!Eڠ`b+fY!0Y$x} Ȟ[nžR KEVf2f=?m41*R,:V *\(P PM+1FOff|aU(P@cր<)cy @` *8WW2?V<ҥPeԚN*9?.eܻc ތ<с8i6:(;Ff>\WE\ & RΔ(@QEQEQEQEQEQEQEQEQEQEQEQEgB @YKWh((((((((((((((((Α/8QXG C6D5TA`-QH(((CҖ ( ( ( ( ( ( ( )K@Q@ F-QEQE҈I%A'(U_ Z((((('Ḋp' KEPEPEPEPEPL*w;v*I+qҀ3TӔ)=kNH>O+FEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPXvb?*5]cL u{ogpjzkƏ譎 t!=\ *xHTB; mu{;*|}N@|&ASI 8S֞5VdhHu5D[=iW[Ppvƀ5g-.Zd6!mHbI9ʵ1Dp3 hVY#hqJB"Co@XKs!ade@uTz\Zo˱r?:t3j1bv> !ծ[ʮz ad-żi',Bc=nB}Ωol8aZ( =@Yiq"n[N)p֋bMh.A!}k-b@Z9)o99 @ hJٸeqpzU%ZxV!wdq5y FpY F@HQ@Գҿ,Y8݌ \{n5,qkZ6ǩWܦĉ{B0= +Y>XM@j0BI朲j\}c@E.j$*D3iRMBA;z/Vgm$pzT꣍iA{>^;mh9ZVګH.rX԰8ޣWՉ I,%+cZYj턚9Uv1;`sM]L w FG8zzƹi#L~UFF:)|C֡/y1h'bIdY/.LJjגFu4C4뿶Zaj3#ǚ=Ӆ<( bE 28m3M,sjEdˣIK,HێyRgK%̲A?)(8y{d֭ vrrMI" #d9)PMJ|ɦ%Vv]*Z(;Z)T202֨NדoN1[ GsϭZ2-P`VV]qԴPK6(!⣋G,Y#f$~U~@008`acG ԔP0[ےa#'QF"bh((eV`u@ uSYCqu }-Pf$ƄiF=-R`R@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@s1bU~Z諝|L3QH(((((((((((( @_hV~ {/ʴ((((((((((((((((((7\؅Si/5Y,t' (=+>Z)iʖ:q@4R3R@OjDuC#SЃ@llP8րEsCO7d|oc3@oJէ{I>_]%QM2"RhQEQEQEQEQEQTKƱi0 zrhѯդ@`ezР(((-V8@FB542, @(R+k :(y=`fku ~d\ME% :km#<3ޖ+ڱd_~u_Q|d|PV [䲃a' ?t5KbT{ j2nW&=GZ{)Ds(PQk5٣rAygmxq M pv hMv6Zl3A}đ#>$LbT,odBF`RkmơFA$Knl -i;!=sdzͼ1B3AUZU ڀ45Mm. +fn٪ W6q߭Ɠq-?0IyqU$+hO\(c9Urs#1xVqcP%8|Ȇ>o⥴ ,ڳ%{@ڭҗ32z*Z:.+\ {7A׾c'ӣd`՟F ( k bԱd?i5arV5M' ۗCcܬ@zի{ {hL16H<@[bc[F [GUތ? 2#50-ghBK#w% &owuHFvmV9 V *$IZ$E*ZB2H' ?LS02TEm,2:js,AYbk MV;٭Lmz6=*sCDp ֥92FpޮhbvfSט46L_tʚULpmn1;e‘O\ĐD(@ROH 铊筵+إ%gvq?\.vQ< *g%g$T-*6WKw4Edr$PFX8yLF0̠@E`7'׊ܬ1umP]1fh(((((((((((((((((((((((((((((((((((( QEQEQEQEQEjE_c.}y5Fgߢ)QEQEQEQEQEQEQEQEQEQEQEQEGF(+Rg\WU((((((((((((((((( FOBMi1 Eex K j[[3 ׊`I\ ] Ulr;5tTbI)*SHR[M,=F}Mt:n1̬`%Oxu#'?j5i"d?m_p_T+ ,+ PȪ3Ҁ9ɖy<1l@bVL 8R@Cf%q؀v6Ezi$6Jj[gl5X^8^*FpkoJeFFH\qWs0*dgt$$(Bx<@ce'?tߧ p9g^F[}E]0Q1aѳҺԊ8w f_1X4d(@2h%nguI|ujݷgbc<;j3@՝cQT"C2ִh{Qgghw6  .q[PedtR]h\L.zJ) VtVVrKz,Q wR(i6C KYedHPY,(*HHcXP(QKHzZ59xZXHQP2fǶ7'koIs&lǯ8()h ( ( ( ( ( ( ( ( ( IeǞ988hN?媜cM%,wC?4d#~&ݬ&ܪ07V (EPEPEPEPEPEP$q?|EOE)4VS4c ѣ2@ʏX}V{PH1—U .QTS9%К:AEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPYZ_?5YzQ#PԔzJԢ(?Kb^n<Ҳrmq(}2m9Oy!#l~JVҞxFQϦi]DJyu4EᴽB?{Hb;7uȨ@bˏ[3QvTZq+đDIJʪ{Pͧ[$*q{v2`WotIREYB5loc@J-;Ջ_t-z4fH5QsqRlH歮o4 +3LIq򼠏pqkhՈ,#J "U%9SզPUwU$n{$Y'(ݱZlwDWZ}fwf0tō?Ր-UX_fEiny=*އ!Q3nj }6)dx>Б +ض-D"-jEp.yTS2Mk~6n+z(ZVw'Hk7\&K$*> 1$RO^(e' zr\KnZJۃjVD3.zP'xp0L tQE ( ( Yѕ@2/)ZEajf+=1lhj3ixZTEfeP:ӨT~vwuL&:a#qZm-ąvԔ+^L1T.oRkdܦFRxqFOSPNKD=Λr*qئ(Q@Q@Q@Q@Q@Q@ih0Q6c!\T}G<܏Ϛ5F|c5ri.zbQE ( ( ( ( ( ( ( ( ( ( vUYK=1B- [¿0ݞح d4yL hEPEPEPEPEPEPEPEPEPEP U&6犳E)5Aซ v;2?JH@#f$BQzz/kڣ%gځZ/f[è>b*D> j)(((((((((((((((4 j8QVk'MXԇZ`kEʚGg$ʼj.1qvx+hd_Lq1&G PǭO %Nh#&?ȢsgZI#tjOH&~<Ԝd@"tETx^?\ ׮N H?Zwy"LQɋy(?[w tǮM1u80 P‹zz\bkV(((((((((((((((((((((((((()1PEPEPEPEPEPEPEPEPEPEPEPEPEPEPXw89Ϲ԰5|cЍ4QH*&>?{UNnwTmBr)*M7S' .gѢ%?^9l,D$[Ҭz-|g=\((((((( OʭM/ArU(((((((((((((((((!ʤ5sJ92_NՒe:ޟi4 `MER(((((((((((((((((((((((((((ZΕs$խb̀m'8֓Qq>pbCۨ/ H6O|\%?L4D]E ( ( )>wc=)QM+xc: ( ( ( ( ( ( ( ( (zKh9 SQ@kb 1ӃÌZEs!5*`i oT g؏c@[\ӦjDSJ@{7ԵE5\eH4 Š((((((((((((?ⲬF}Z5K}=gW#kmR9G690:PisY?Z:xO#4/+w–-u$?lBkwA"E^0z8 z+ Џa/?Ɨz '"5E5|C h\ȢlQX(B}G|E?ɓ$ ٤qvP<@X +8 n)Gc9Ż @mRV8`{H# z D[)[+`(j Ob}:m'NPyEl5C!6Z7(#9)KRsn3+EbN3lsTM 2v@V4Eݘ54Eam]dnNxGBIQ`7ܜZں_Eܢw/siX + u]@+Rk6JqSV~}@4 6d7llXU5o4U'<4X +ouR9T [=6lXWEAN:ܵ עkX_l"\s shVA^gh"<ۏEڢDg1Cͭ1 9tEcyʌh/cL2?Z٢XDg =Z>Z`P3@~(Xa jQYRG.6M .)-e3ģע-iG)=pH@Tn։8ԂWwfJ٢_?›mdItEcX mR ql-` eukZTV8ո֣7Q[ +4LP&j{#4Ecf]HE;* 2:ע#n~Rt94բH#B2g<i{Nuw}N?06h.j2i~|1 J+;u 4hrMo”`7s~tע_BB`é,N EcC6)Ed 7dGFC1)IY+G(>n'(l~Ic[ؒDi_QoQՇYY 9+6l~_1o -oOγ?,@H#*cd_βLJmc}bMmNzE6j!؎Ɯt{b?)`N>>YSt%4k g)??(Md:hzFsp84Y۳覜4S&˲f m~}QiZe]fB=h:]h}Tr#ia/5piV#]4 0-h:_FT^/u;yY~d]׭ҬmӞK{K.Q;E-4V1VƋQEL!1STlI/)_Hε(d Bv9*Z0mwn0iEˎY?.T]K3U(ܰ%H#EIE "iTSu6ҭQ@E*bc1j950Bu"#wLL !3I"p~N_#WH48G&_3h̜JStlG!#Z4[pLL趍f9L'vE4>ߖpEmm`ccܤ'Ѡ.Ȫ:0p=j8+ynislN:mRe@Q iD9'5 93#i}qyl훭G(ZSy6檝nIn j}U[S#V=v 4\Տ E'eoX'Ո,E(=|Gl"X~; ZMzF`r¤iEOC֢3 /cv[Ө_vix%:).3Xx (Tr_X[800il uW0Ge;;Wi:($j:C1vo5VR?1kQ@; cӧ:()Y--Ǭ}kQE6c >lE6ysGoăeCEѯV7P!*swJ袐Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@FF,(iXFE0}p\]JzR N4hyAM*P?qh{."?!)vWԵEE8oΤZ(Q@Rfh`{Ө((((( [Kx^ @F8dk O0-dXG c46̨Sn8tRi6-ҝ*2;(iM?*AYqn1ޮQ@dfN=EI;s?TPaaj-jV'ѡʊ`F4EaH(knD1<6.>(3MCQH-"$R@ 0NLhOɋy'*J(1#H?"&,L%-UϮ)vs-O(SSG@DQS=&P`zQ)h(((((((((((((((((((((((((((((((((((((((((((((+ \ ?@JZVQH(KBrqIENkI[eG ~+;hԸOsUc'*JFh1>P>X=~]G? cGҀRZxt/4}ܾ彄$}EH0!0(~%.zu&㟥\̐ %( Š(((m9jɵxkPQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@qLEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPE'9(((uN6ZޮwćeOdΚq~H((((CҫZNϔlEPHQEQEQMVHPh+è>6GD?2o/ BzkR*%(2 ( Z@Q*xZ((((((((((((((((((((((((((((((((((((((((((((((((((()y-QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQERSKEWx*\[@[^O\ -}KXJ6ղIK.Pt(&S( ( ( ( ɇ?\ֵclj#PQ@!8ԵWRJv(2/??:Dzag[qWWBIOz}nc"ƤpKlrT)$4eN~aǽC~򨪧B#jÁ#&glǩ[&wJ5E|? ˿sBdo f5R3/_Ω?84piH?Fh sU>:YPgTZfŌ}违S:-N"3Zi0Qma\ˏnj,BHOMVÑ6ʠv5b?/乓 `Ѡ[\\eۓZQT?mmle33KI,-p!3U\€;uSЃໂa]Kov$1 " PZiYK4[K@*z1@#9ՙ}}o$v2d@W[xȤQEQEQE!z;yZ+BHjPԆPkOWViQ@Q@Q@Q@Q@Q@R (((Y( OFF=F)PEP)h ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( $+ ??o?SaS@n?p )@Gj]fêZ\anTOZW٧)2ҝL* &)5j8|,{0V"CN]jCT-ѹ_OwQvoEQm@,]VEJEUKYqS[&\~?Z p|)Irjm3>KU@)_Ɉ)m7KEPsQ@Ǵ?STVTQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEV`Q <ǫN7Lr`4EPT5?%Q T2~ h ЍhV~sE?AhAs[@ZF 5oz1m7R)$idsl_zT=kA(LT2:HU9@,HKXΕ5RF2[稭](p00XdQEy (*jeU.'V~_+KgB~=Ly1}xVWQԚ@Y VcwU=,Ú|qFL#(}Rk0Lۛn>ï@Sa\=j @h%(zm1H쫴Y zzSЬկ$lǖH瓞k]w){ԣ=ih(( b+&8V^6 Ķ )j6Ilcnކ<;+}`H?ʘQE ( ( 789=6AzAQviX6|qVk.8Xw-c]:~߰ŌƔY??S@wmΣ<?XD?T/DQPتkD ut>hP^ڳmp0*R걨?J($AH$V}Ć`tiZʡ+V!\ }h6VXcD;S袃&,7:nD()OK2"ewylg7ZE)v"<CRQ@9EqghEmA%iRq)UlɜF+OO}'qTM&S$֬ik99"SjһܿEUDsD&w/)599Wh6:cGncړQՔ5v85/R5T/3ne}j)7p(#t*b#TQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEVFߛykf>X)_CX|̘ ϯkoă:z?}85Zwv! ŶE_X#/B*fBzv4'E<+qJg'@QEQEQER7?JZlh^ &0U *lH;#ӊ(3mŠ(AEPEPEPEPEPEPEPEPEPEPIZ(0=(h`zR@ 0=)hR@ (>MQzʖMQKE&(Ҍ Z(0=)qEQEQEQEQEQEQEQEQEQEQEQEQEQEV/G6͸q[ULb=ҚeqJH'HU0KEQEQEQEQEUK!BuZ n.JZ̢+Nqizmg !hF4 :AQQEQ@Q@Q@Q@1G)"@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@d1X`9Vd$p`-T-14ږ4X 76OkÄ g?nJٔXzv]L°sSkZ7N]RaX ae*+ FխK-[^DT]@: Z٨ՇEea)OK 7Iq ,*jGZ>~X\ҧ})ƥ:ŻeJsM$EjYWqU kk>C} ,Ecal11|Ee.?GʑtX ZtijVi?O\}x81`E"r{`GlrA=h6ۻ5G‘n|S?[EkY'VayjųgEej˜}G8 {*,9>0jğ`M^ѻl {'ɢkY_o=B_޳ǩEdx8t}P$`5謖@ǟE-Ƥ& ^Ef ?~6zP?ƋEf8XhFԉX *+9Ppcj0ڦx^=Eբj%)u"‹Ee00GMeHq `5Q~ Z+(A|L:f_΀5( \`FRϽiY?b3_ߜf4Ee/3MM>1s+ޟi&t¤ 34Ec.Nc#>ӿo^ր5. ȹ8s/g&Ge*vu${:D8E4i3'G.Ga@rx~u4_\>+ŏ@:(hϵ5 @b}:O6?YsȤ: EO?c<֗@3qk(R0nEiФ702`+8hPO><߀{vB?U|v?3 ?Rpy?U#x.zqKmi?B{UnGސ~"4Ku$hmXL6fCQldOK]Lznl>w:id:;AقNZ;2szA!N}xcKy|8 qNt;C{]6NMC9'|fX^42Z'ċJa_ƀ/X=ղ;+zՊJYg׊c7[} BH  X'LRg@Q@Q@Q@Q@8G^^?ee'1v(*+L03u Ow OYS ʀѤY(fQEQEQE58U))QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEVTEj: Zլ6и J( >=|Iآ3C_}ť>۸:(E)&9hПu(`F袏*<\>o_ʐ }-3Q@ ڣʗh- P( L Z((((((((((((((((((((((((((((((((((>2=x&/$H? ]q%:4jJ]6kSX$PЂqkfIFRHHd_O *7zA(~65(p4R?܉GTTlD|TMhx!(NsXS&n1 ZH3ޘ̒4i ɪk @6lR\d"}f>Z)Fr1GVp _? j)#)>Li0U$ wQI/q`-ϖ#׽V,b:gb b",@=)Z_ ó)4QETn"b[EEk8IW ԴQEQERdZo{ ed tTQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEdR@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ w1I3ĭ5QEQEQEQEQEQEQEe*qA.,iiGVjlUf 'qiجy  OU-CiCRg(({#TN@患kKEGD I@QE(((((*$:<^td`TeEm7O큊AU"JZJZ Š(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((y$ܩ֖VdܼzJrJ(Q@Q@Q@Q@Q@e5;~ZqsPע(MOM>7??ʀ3|; j^1`@cV7#~:9Q\e#iUSs7 L@WŒoκ; yhx#'}gFoD_b<9)QnćeOcNݧƸRj)Sy` 4l( }hd@*wRi)O,*W9>Hl<:)86sA5ꭾRQP@VEƴ#X#2OAހ5~]LD <ӵl~ZEPEPE%h((((((((׮嶷`m#`+)c^l#wG֫34R^# ;Б4gv 2I\- p=O[<C.P=h._JU`ܹi21;tjAi<;lQ E($kӀwYe9fCuڦS]RѮq@h@Rdg [kc }JH2GZZڍ̯F*啪[,JrG$`X)QEQEQE7zg>QEQE5cBUFI=U+]Vs .KT@Q@V+Fqpi,{+d ET sɲHрȩTs4bX|=GZ$"B0UMsA|nP0}*K&Ԑz4y1ᩁ\A"șT椨x#X 1rvD!N@&u ="|銯;d z ~OE5Hu QEQEQEQEQEVV&5m1cbk'Q֛TEPQIQǼTCAxح:'M9õjPMuQ@5ܣ#hfiZ13gwF,<Huw3E+$o߭Mݥ֜Ę$z0od;\uj+ obey%NZP E-44=b-3 ÷FH%N=^cB&=@*Ֆoc;| *2}p? &ȉ >sXIgK3 g^Gl4nWv Y|+cʷ]CJ9^I z aaGfrźNz狇ˁ[Xr`Rh#!tx (`?>jL} V  +t*M#HO1h;Tu4u g$o# =Mro.tT/}gsw`J?aZd &1נ ڬI42Gsj˿DvbThPbgyn:3.G2\!l].to,zM_2Eu 3յ!45QHDZ>̢ y%@y'uEHTVa= c<<Y+ހ9k{ˋIbAJ'e# * MnYCeJ$s@>!bu8P?ԓm_E=U{M{qBZsK) 2sלr(Ru}>սcC}? 8 6Ŏ׶)t%4#o2;q`zP}˦9U1jεk5y<S]CHK\855 P=HC%aP[M#V9Jb(<~4&[}B%W&9_k/c[[#rl9 :IkZuR:3v(EPEPEPYLVgk˻Iw JԖ(¥u?'SߚҬ{?Sm#΀*Ҭ۶S-6R*2֎U1zPh۩l5K _YVLlMBwq"kF ;!';c^Rw%*y*$&2)+t30|۶:ր FUqzuQA+xHz2]*eR8")D :j{e(bSt9ɫ3[$z#HBj}FP6}jXJ0MMEsݕ T. zR@ݐ5 rc"QS9QEC)F* $zP@e Oh(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((ׅ=ˌf_kG]? Ќ" !W2 dYƀy,$a;NN7Lqr:)̅2I%DFq ,Py4fTn[LFG9o6}Gҥ("m=QZ;7&6g&6'vw:6tV$JdSnYGUtP>z{g\Wbk8b͐O\U(vօxᏢdS2+PG:ۻb*P0AKEQEQEQEQEQEZՕhJQE$t*ug{7>xJƘf((Fyu%UUޮAG( ( ("<Hj[P?01)h ( ( ( ( ( ( ( ( ( (-5S((((((((((((( quk, ugh}Ŕ…`f(/-#.vإX'ޭQ@Q@ Z(((('e`];LO2vrگQ@Q@Q@q:06LOv5j _1SMFE-Ci'i @jj@QEQEQEQEQEQEQEQEQEQEQEQEQEQEGZҮ=?^mҧ'PI'^JPٜzf5U5\?iVv v` z~5AeeNOSAZ(((((((((((fbrM>p(QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQER0ܤP5/gkfEmn@ޣ5(+]QEG0Kpt 1Vd`V=z -"Z(0CȠF'4dgjL_07'vv-+b; HCC@%kرEPHQEQEQEQEVNH4?YZt@jESdVCNJq@]hjV_@#VDʆE*Z>=α:b[Z#E7hL +hdL?|u @VoSq룏ͦ^\d 4(Ѥ9{ۃlSr.s[1d+?(dSB#4ioptФ#H_2 yIsL ԙItuf^顥˶$顤̏Z2=j]ݻ_4 *9*! dztrxAZcs@7/F?:tR*M*Ԝo_Ϊed~h]3@tlyǘȪv\y4ٶ}S ̃)D*,pmJSZnPtCZ'(| m4|ҔijE`{ 0c>tx(Ds&=w vEmКQُwOʘX#@u?WɧqxQ6̜?ʏ3O-D|q5wOԡCCp]Rmc5 'x##]QuQH((((((((lsv+3ff 4QEQE&P-2(#ꪭwrTʰ@yd쒿 ٭X${Vh((B3EQEQEQEQEQEQEQEQEQEQEV~.7tb*35>oZ*i:}NOf ׇK\0uY\db'ӿm\U6ۜZ(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( V,J`&k7PM؃QmO)u:[梼uK1V~! cJ]\*GN*M1m2|M)iu:$i1^qTogVy,WaRkpdB/R$Xjt*[(9IFȒ)CޕZzȎV(%2 )dd\8J29KݪAP)>n(c|z;n1ME5(e`[);QAEPE>Kߊ݈55Z[njNײ (u2FnoZՓi珥4QH/t9ITgC+R|81I+Z0,CcF1߬-9H 8QV0 (:u /RdR^\F<|'$S78\`:lԐnb*xU%RJ9hԷ ` ܶ),9 *?zb;((EPEeڛZ%nW`sX&y60:cY:^j {,4K @ֆt66GU l;UQOjuex4݌qA mvJ}6Zw n  I “,8QX 44FCo88E$[ Ҁ6@QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEr'ܚ. _pG\4yti$9vKSDLZ.׭},-Cu/(Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@SՀ:e=6ʩtیpvA5OI$Č,U*ƕ9T5)ðPpƟl?*U4L'CTNGu%(QEQEQEQEQMf 9PEPEPEJ((((((((((((((((((((((((((4QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEɣbMb5Lc#k_KteY$wBeX -=1OlXFx<}1Ɲ}AĖ[r+K?mBYhџЊ׆-l,2Վ+ق#lҀ75MAtUm!seG; [P=ixEgTj7v!W@n`c +u:+!Er(an+uGY (@QEQEQEQEQEQEQEQEQEQEQEQEQEb q5vikA5dl_H~(EPEPEPEPEPEPEPEPEPEPUfm`}2Ww\ƋbI˩V q@˪7Q7֡7_ngg{g Vdw Sr$6  qPaS?Lԟ G 82Tֳ6ݒ{_?Tս7F63L۾\ 8#T{}$85H(((((((k:Uvj:m?4i_ b{ ang׊@R8nr0>Wk?\\ЩDžK6?췊WjJ-As7PkI}Ktqc(FګZVv{a9VM ZrD 4x#W4IE%؟PIdpZ!F0rOSBtt{2LCP׵gXɋ S߆!d Ӵ{Iɪmy)rEWim7c+IfНy:LRm&lsPC H uV\RlNj\!.L`թ&T8c5pk9 88vIXÎ^`3j[h OtIHw0qR!F9秥Z6'ȇUQAQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQER)i!ft((((((((((((((((((((((((((((((((((((((((((((( dP# ^FJb,dPAuӢH`ih1 ( lf8ku*hPGWPr :A7q$O'KP[dYg:0((FjZR/JZDQ@cc/O8ElVFfaN~tQE d_OK4c ?jV_?V02,@:R+b_纂?J٤Y>$ra ygjC=XҀ)TqL3đ+X\+/%jЦJ\NRhՍ*(f@&cɍ1 )1KEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEbs&u ҳE!<ÃZVE0#2\u5!VГSwFpT}k;W[D*W,ǻf65.FԇOC5\M+"`W]c+Me 0΀)f 2MUԭˈ `F8( (+^0Y O58 G n`.alC?JM奶x;l `jQE((((~l.[&?szJ Ryzz{G}# Dʹ"|!uQL 1ӗd n9}k +,?SO=653zǞb *#=̆[g?yrHimE$rk"HumBU(1 {^DYusKg9X3F\Ѽ}8ma?u;ArKdRx{S,+oaΆOv>e Lbyq:9(o<09E5$l|̦ۚK>?QH(( K4{[Ve]뭮ghG=V?PEPEPEPEPEPEPEPTbL$6tr˺_,x6pc?* KC[KӌbkAs`g@ 0]:ORT~.I}(.ćZǺ})f[7*uͲRJ3c@RfEwC>S'.- H=JKcZjN%i,y,w:=٨tVk\|mNUez, >AŬ#v} (N5\trbƀ4VMcEӢ; &g?~ԏLԢĒ^ t٫ ) *+7տ?V *+=au ?΅T hB ԻB>6?~m q|#i~y?~1@hMozz^8Cit9 Ui|:BZ^wh@^[-/1?FzldwUҚ,jԢEP[ P' a+}`JEgeRtGJiչk{3 *+5h?Ү?>S@TVybSGL|fMMhQYG#4cA&IMhYEL }L4[E cY;duogY;4"u5S2 liY3e'ع`[S֗zx~uLhc4d~ι=NM[28ܿly֪c~ʇ9t9@ "gHn`^F?BeXd'I`3@`q} >m?bʰHq,sC| myH4jVG^ٶ@N6V@6: O03όƤO$E9#=䍣#Ա~?}Tt!NV(Q@Nju{pnC 2+}TSE#1"jXu>ӫ X,J1E in :~qh:֞?#V (y14EŚ~Fayğd&yQJP:(0(6O\*l#_(RP1RՆ6b? iSGI?*:4v.vN홭,AFeo4nFW)mpOLR)۝Rz1ˣ,BqE09eTm߂N ?)0=s ,DH?ִ5ȧFNc<7itEAL bWȠ U2`{KYs>MŊЏQKso/iLT$3q}(((((:r*K j9FJ.Z9DS|iZi;cVNzX&qsRT?yy SL)afp!g5NVYt 0\P38p*Y!Nj͜LqLt>D^3?NDd.@.3V-HV8j}TD 򮲹 YK5ܫ6XzS e@Ǡ3\йӦ0̣cjLFPwy$VQXo@Zy+i di#uQ@Q@Q@Q@Q@Q@%hkRГi 8ϖҳXH.cT֐٫f?~I$5FPVI7Ҁ!5$x䴄8V&,6Ir7Fۊ`F~UK_ \O,kl=PQK19HV-=?zk\\j :NR}(XE3;]ZMkMwK%W4)`S23-## uQE ( ($q@ M{8 iOC@߆e_k[åc>]MQEQEQEQEQEQEQESUmNV&~~mR[Mbu#ļ~f wZTQ2hoߌڷvnO]֝14OD_}*fITvvRwfVĭVk#MbeNEk[>0.&1mUOLUe_QQp2O'$znldT(1GJ piTEBhf1G8$ =*KXNOgfw+(=sSDHF3@Uqr)DnPdyrf8歼Ȍ$.⠹ ʹ9iؿY+0[^8ӊePӌU=:$=*2Oڂ%EQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@PuWG(mϷ&,M/+2çjGDT?@FV8iC7p/QPZAyx$ TQE13E@k4\S?5b ( ( ( ( ( ( PEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPֹMVZyCY>EšWKm3n DGBKH((((( -Wc˟0gj# ­I.748qU$IUm02޵(9F9f4ʷ95@XE)4 :EQ@Q@Q@Q@#}KEE `*Z$$GXE/CM(E ׁ8+j5&0MERMOM>D@\iHO+V6Oih2T~+".I?1L G"J3q9*FVZS4Gq "&ҢqF 09ۧefeVj8isH ,}ن?![6 "RfsY%$1AOH@[N* (" #dn0k2c-y2l+}b]H3qH\ kvKmfFii[㔅c&GK$Kps@p\n;A z6=ijDS}*ߢھoBӠ=}o26P?/&(-!-psƗ<sWFI 2F+B֟> =<ֳ!;2]Aut];@E,sPvmum?-X5Yϭ["Ff4Ee&j$YN9}#4X vJ9\[gҫć*1>j inב,r+n>*ǻ!5'.uzUlY|mօ!$@0ZAn"F;nWsʦ]Vl&tL:.qRE0-iJ9䚭k M1Spϻ u48„{cӭ mu9.g )8H WUn0oU8iV[EC! ?EW6¿VBm-;9o% jYndi}SUp =YEVt%g29 AV0rNxɸXdF~VchZ-p)kw#niEs;U%?mM9_$JBH UMA 68ۓX_8ԧ/|&Lo +;Mk>(T59n6b36m8Y@ UF4XhD%ͥ 7ف:/Z.P$$95'Y2 UOk.;oSJ~py(G(X|(Aygdu=RO Kz&rϲPڤ:BYcMkKhi\5ԧfDV]"Ur1mdݷ div`BHlm2*zQ[_tCS!818E\ʱd:MlIGEC?cJ2}XbbF<>.d4M{-Rܩ7b@HS]$zUL`RG5obt~T\SAs$UbG񮶐(((((((*+ FR7je'€iU>ۜy*͔mS7| FoO5qG`2GJ? ;Q@ }Dq?k{E*GzAI%*"g5VW24֪rJ`:(EPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPPqg9mMP.)63MMKYiΝl*@Aze:l?J}?ͿA1m&ܞ?)]*Nx'5~ ( ( ( ( ( ( ( E>lg=h((((((((((((((j pպ:ЀDJf~ rs )6\S Z!O* az-$(fϨƒE$K$v ) b~q qH@ڞTK1Vԇ^OAW[kd 0&nR45ݺ4𬻂[ffajM>N=jH3*}M r*735>* Վ(oSPE5Qrzm᧌0JVHQIUtd3|<ҢF")$ mg - id@:*No/O_C_݋G<TLo M{\k[ vwA{|A-Y&#=tqP*ܷʲʈ[8KMwXѝFId~5].VG o˷b݇ 9xò8=@zMR]^Ohf_;ܥcMբo2Vp:Tm~Y0.T3AnTO*F[gRiaS2@YVGQʹ'<[GN$u܅U$VxcZqoE^eq^˜ZO +k~OV5c@UFoϕ#qi-Esp4@ڥuMPO{#Xf8i{K f7+ù (&2#zUաٲռ߄P8ϽYy Lg9*Wk{p>O.ަPE#2%w5RMV3: } +]!E3zl?vV7wYp8&[,0gEɨNg峇ctA4t,ۮ\M1W  kuqr9f=X55iQTd ljPTo5hC F1XԘlcOzЬU0*+b'fYOzi OjM.5RUE)mg oDR< 9?Ґ.i?(ҳa6 x4y>bv皒 VmӸtURO)4W=zPWћL<#"@U^v6ݤ9V.U(nnb̝qzϟT[f۸;Ҁ%͋kf Ѩ㵅bp??sRX"!D,{ ѲBp+'궜}?1՚@QEQEQEQEQEQEQEQEQEQEQEQEQEQE].b Jo2ԳER +_$\ZcN5r=zT7HXe$;/֜5DxtqwN[Wff)kҶqF`u(1@ȎiZHbo|M\\k$mzq@ZۼN; ?[[?(c֐QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEeKi֌?SY bk?GT(EPEPEPEPEPEPEPE"ʐG4QEQEQEQEQEQEQEQEQEQETr~S޼/a|s`GqV(((((((((M>7;PYΙjO_V\U*9?X+ Gts_S0kB ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( nj՚}*脶zf~Sz (klҤ/1_^ aSh=NJlHc{V[xyS'^B7۪A?!`L v,2+N.L ?THizjj1NF9+8x \{nZS@=H_8S zn)H$|``` wlv avSAAh>!xG}#sO:XWpwcfo:cAGLREo OM)&z nOrq0hlq_4a]br/jrrhN;DRPAZ>b;St6lHgm;hkmbY̊;v5X'Vn=ʵ #PGm\a7HFsJg8zjck7 b4EFu@\ HgΟa_ur#\ij:dZFwtdQt s[4RWl7 P) t֍$YȬGmJ3ӐqVgEk02݁Ѣ{A{*+HFU[@?-Ę V[HI=/lE6 joIL x1֥ }A}2}S*tvYU(a%rX5fk MRlz(%4՚Y)iZÊ( wl ۃ'FSUƅnduER znfcҮ2R Z(,hjN 1?nHsZPV% yڃ=jZAK@s4$nX}oi)0c}*QEZ 8bMES:4ZPv% @A p5b*s!֝PEPq,`Xp5%mi/mYEAwg >TYs0jE5E2(8Fo, ;:a4P+}*U%m-U+_2H$*!Vc6g4+wH{8I_r#5,\&eԟb9p0:*sOLɀ G?Zg^@Vu§q15-ͬ7q0GsVZzyՏ$ HtH%*1e9T}#\!bA"~Ū3/Ӆs͠ K}*a*!:sX#OO{bCQW 7M@Z]Y&@11;(lvwX`zy9tAZV4sm&r>kl+$On~V>iIF]Ԋ*6NI7~+V=A #Q.zIFR?~;<3!آ)cIhPXprF1۵wTҊz?sv^Me9 H&5{y.t% V"BŠՂjj`rmJZ[]HF$L1,hQ4̙?*[g{fudKf*@1nvwOXFcp85Yij$g!GjjvjH!` D:;n/xC!ZOQHۙW)QEU#/Erv3Wk%5t1PSEPEPE\=+LasPCEPEPEPE\Z[o#hQP\%պLt55Rm)h((('Dc)8ԧby랴hd.nsV\Gnvp~e!Aat.?:Zyn!́~yܽ(֡1ʗiWcۑL )5qр4@QEQEWR2-EZnNm?CçY[֭fJ%V ؗFzQܵ͐26RA?ʬ'k*ʚв1+#QH(@A( ϲY0q]8!5-mvRW'qI#}qU8KgM=N!Lv.*KoᷙTyK#RzUo^B`mfoeqdJh,VCoHN'F#kWH%\+:=A "-ܨ+&ѓs}(ǰfR|Zfci:$3s[h4A{ckZr239?[/XY g9D ꞗki$0I=I((((((()M>/t}(K kRѳk*WSӮU]L\p h9G7 1u@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@W]76UbRs(%SBbcF@WMEaV`GkB õ@W)zd4NFec*k 9 Io䑝- @7͞O44)ĉrBǸi;}I RkT/Xė#o?Etk9˟'Bbj 2i0cN`jiX@f ( ( ( ( ( ( ( ( ( @1ܟ-QEQEQEQEU Sǡ:q{qA> 袊 K[gxVbx [g:RP8PM[ :'?cY_Vds``3Ϯk`e CL?µ%OT^ֵIeŶok= qfN٦Ԍ*dWڗTm4oX$c $Q흢t]-?Zѕ5<@38dT./_j]Jl!;##4g-w iЀwoSՔ|oF%}q3m0N6֫Ad>xhhuK# 7h EekMnp2I vUo Wt A٠ I6j&Ȧ3!aUy͂BUhe)H+V4"\3mCW_ȎX玵Mev8[ 8=)61+;@&>ZRX U(FۇS@V=ψ-HFN +&Y#eiQΗ uh5Vu?+uUxqw#סj- # ;_WDV Ρ@OZBK ƪC>~IyqbU}B. &PGYe1l\nsQ7x¥_t>2)A}\yW2T5L!N}]F3 j<?VZ)j^8L65 YQYy(Es#a Zȸv$9G>w9eP28ɭ}9nJ:sȹP0ҟgE8s"zgޢm.c>Z^.gP@nM-PEPX1ByqbN3[ufXPg(EPEPEPEPEPEPLKiN!sɠ N?r8̶1厕r 2?5rjMC8δk7ApiPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPHr{@4𪓅Ӑnc]'8{zkuڙ rie(oeRHs)ݬ; S^.stn â7(CΊ,EPw~t5a2_ድ.\̱" Ci}\%I&-g|1C=k7ryH5C=?,g bxTm7y*K~ud,TPx[r;pր:(Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@%-QEQEQEQEQEQEQEQEQEQEQEQEV~A#Zj[܀N8@4V@AH8o(`8?S4V+xBԟǿ?EۢT?R7@5j!:_HmQX F?܃n ~Aǐ8z,4et*DT(g&w nr<]0B Y &ۥV"0 x~W:-id_(n9ʹoOWe.1tT ((((((((((((((*%Z[Q@J>KsnV/0D_ 9E>S0=: FKH I [KlV7`f O^) xGf@X1:P:줄}2QȮ[N:mI2>*:?jERҠ;)'M@[].`I SnrElwR 3#In? $h3ir^?kne?F'Tݖ*a{C~ܶ;EF̕ݞA/7u=|=d>aPM!+ɸI涴{ rH*H4($$YeXW)OT{'H\rֹ^y6S¼һ(ӬԜɅ[=a[ˋ}1@*Zbhrt=A&7K'!d5Yy2Ż/.0zӖVcP0li!gnF2sҢGYX󸑌ƻk!"=XM>_ii4 7q;MGM[8v, (xzݕe_Xx~I7H$ME K;q eN['5=PT5=1urx5~e(:32MY[h?-a@ 1R@ionCޛ;L̎v_I}Tƀ9#2 7ul*̊#4Aɮ4ƨu@ZV҉C!S.k{L]hH u>15$vW#= Z[X# ;m6PTtp)P`cej\9pU(aʚ5xEI,h zS(((( ió `UG^R\zhۭ!lc1LN$h$$*vJaflsƓŏJ]HGSZQ[b_,W՚) [ 7n|g;kA-EcQTRh0Ӄǵ- oğuzTPNium}us r=y&Ey/!sҶ)6i5pm*s<\j.E4[L#Յ'?*x5`reS]s7c"Jh`gkكԩ4w''ݢQ v晨h7dAJ) (|7iY &%nС"sʺ6u_}M( ECwlv A#əf@5QH 'l4^ 98LWEE;QE(+/^CPA`{֥!d2.U"#`x5qM.cXEt1ARTV3j9̗262=꼚Fv[O oy_njqX Ð}X:t [̀㰐Q--=9٧dc`4VPX݀/zv`?D5!,O\J4(6}d Gq֭kxR?%V=HF0)Ԙ(((((((0=1Nɟ-)˳JZU4ߦ[60|VapxUuXϹv7 vV4:Ѭiδ((((((((((((((((((((((B@Z'~~jِVmoV(r6u7nQɭZBh.M:V(.]Fhס֙'|mE#p0080\ghstޢ15EñbOV`\8"hq8Ϝ+kT 89ħ>sE 1AG?}R=Ed J`h mGV4?ۗßz6٢NdE.{d0ġOrh-E(D}sEF5;"Ey5K#(Agl E/-_S 1z`uKQ&/I|v@4ӫY g@ܜ"PZ)o.֣۱"B@S@m#)E?(^쒑O H@?dȤK+hGo@5|AwoSڟ|S4!ů>֨'~I"ng**t`:y`պ@QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQ`x*۬O!@m/JZd|aOTjZC7[1\GX<[4Ǚsx$:=+"S\bR Z@QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEf#`^b#V@:w98qZJB@@C}+_kWıhs f@Q@Q@Q@fhez?iRxTӢS U,/dڥJ};;y^5ꄪq@QY:yvT==+Z (REQE)UPinR+s,e8h7ΏxO1w<.gX, O\ / HoHet#4Z(Q@VOo0!Nܳc +QLH'#9#Ys_#-&9`;J+}?V=$v˟Ze4 ]*Ǯ=(r(EPEPE8ހ(((((bN8Sdb P-?6pzsWꆆX6庐s@W X\g~bj:}*>:HvviPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPLS;N:Tzn|2z]efb9̍X -k1/QOuleUWnz)9GEg~,/8w٢ )H#el&2kn:A(/(;DФ!W60d #(>`W$0QQcµ"*?/( GHK-Vϖ_ʗbt~T Sn?R1-S?*6A@:ڑy=OV OٿͿ A'L|V5Rnʚuql utVe%kicS@tGyG2-\r[PdF|Gy0չEakjY6SSe(:m Rؒ; ? TgN7x+׭(jq?֐k'5EbƱ (0mƶb8oT5E~?)kG{kn..l"Z>V\ b} Z#+f.9Z? ~jO#Z=ճEk=R\yAV\ oj`])Zi~ԙ(ހ~ ߎF\ QŨ4K9[٢{~Ɛi7'\ aA}֏'o5EhX#4܅j2ۮ?lE:-cuƗ`A]BQQ׭(N~Qק&,rU +^.\]̉)OPTsQ>w.lJc?]Or )S*EE[ԊrhH.MZPwI徺_KG9k鹇jQ@e_N€;dj@сnn{iciƴh ArOo%i(8h{= ƑKSҢ3m0F=%aG%o8VѢ3ERHY94ؖ=cv?ִh 9ȈmؖB8 o<~EgO~,MؚhkB`@N=)`.=2qW KX/KXI=cP1Xk)3nt&X:|66ќV N" hU(XZ7޶7.}bjO.h}gEn"7qZ[PO?ɿ3I똫SJe/tQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEV/m=[UL8rtS893U%QHzP?F"1XǗu>qɭ`eH,pY2rֵ)QU/\$҅b3ʺ_&T ,ˮ!^uvUsHQH((((((}iPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPv?Y# |75v#&#I`WРT77r%Nn{K;(1""(;V xV8vK]&IIJFN98z͔SR +;2=>=:kd2ϧ^+K$upNShRH FzVv-;%`&NO8^YI;>AuyK`f(u[nS#9dž>oIAȞ`zumF"+ʰ4[2[YQH z[nś "X ke #ہsTFknPSjY( i.@+>j__q;NP-!8ϭm3iL/8QEeד.gj'[^EgYR@FrZfS2+gY78>F8EgxzHw jgj.B_8 ]G=bsmmvjq(8<{b+Iax~'"4#4E(((((((()p}Ԅd@4"NNOЬcJ{kF d=66*O2T(tܜdgZӬ0ujPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEp8WO.1=5nivUb̔xUƬ/wcygTRš9\ec_E\:uV- @ cp^ ozbiVQoӽv5^_&gCTN#c@^2&Z: nvʏƱĄFNXchyVQz )i23=w J<8sS)g!T {P}b`-0+B (2FFGZZ(+a1Oь3sq] H@gU''UH@YA4) !x9Zg9FMjR((((((((((((((((((((((((((((((((((((((q\T/Ǭm-#2ߐp i6?QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEV?3kbP2 g@EIVt" $3UfZQ`fyL@Xz֮iuRyR$c; 1T!FqqvHdX9FM9] X`ޝE 2FXZ.c+1@?hQ@}0y)* qLuzkN@(.3ͷ ok+Z@p^mmd"xq#EdIo2B3z٥8)ej}3JG#n =2Ifޱx?.yc$O5w#t9L=giܿ\,r6sZIZk!$2ܞvFge" IQմ,!w`r:5ٹQF'9QE }dcujEq*iwU̦pByo5:+:f86D^X`#v"]0m ]%sRAu^w}] 9Zcwj 戌B?J@`hVp]LB03*OF}c@Vg0ޢA~գ,*ۻ}ܰȦ-Cip)L&ik g qڬi$< 5m6c$du7_c8\WO\Ij  Z,O`X:rd$WRes4T 2x9Kx.YXA3֝ Aى?.2-[`sb3]\2𤱜C8,mm2C#V(Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ A-Pv?ZʩkͷJܨE^R)Y I=%ClB#z3M5Ɋ"^ s\;]*Kր;{aYb9Vk/@GK`%}H(asQQ]v<ffa`AsLlo6 gt4.UTk[=Ĉvh\WSW G~n>~8WkQ5/"!u1%-[PKkV% Til"zHXxqz(EPEPEPPCSS%wr)jfVv (Q@Q@Q@pS!J\(AEPEPEPEPEPEPEPEPHzZ:g:ZgЍin ?;3ZTS&8&LS27?zlVQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEKM~Z59?AVꖞ~{'QWi gY!*z{v\ܜPuLk/"9KEFPdPkw1^4Lxuܿ֯ο$E0&t+ޭ"\U{LOV`&( 9=-&)_N@QEQEQEQE888=-ÅČ}!8EPEPEPEPEPEPEPw((((((((((((((() -!dRqN(((((((((02 g*>FRJ}* `kBhKWh((((((((((((((((<8Y~ ӿbNma?STD1SEP?pYk^xްVe^65ͫĎ2CWY oߊ#Z"x,C޴mn/ʟ2@y"UM<1A>Q sDzdڐkY) =xdZ$[QH^+KCrHZEcN;ն!FIGY20e= K$I\1g1 T]2n`2=zj ie,軙@/4ZIbriwXĜgh[=$m<jGe`LjZ{s&(a52R5 Mަ?$ kv0eɊvD\[Pİđ¨z֖H$&鮬դ ȼ1 S$"B0U$N9iQEQE2HaC铡8hu'=quq-h8cSwOtZ cdh$sޝ(vE$A4V{i1R0v@4Xm HM$~5  ,CythgNZ+[#Aw [3]lƋQ\r:ĆQWiDcǪ`::+k]qٶ>eZaՇEѢ$mS8эƭ8=E EPEPEPEPEPn30t]1T5u'rmJ:Y`K <_]D,s_O58b$ Η-ppH'P-[\*2=OcGmQʩ|znaU>qn$Enp4_풘%P$A smOKXhqϵI<vO_q@ab=čЀ8ՃU"n#kE,!F:QD$({ZQE OB+Q%fx%wAW4ir.F,@JŕݪZϼb!lP]5x=ij3س}x4$(KZNQ$ d#g #x ֦}AXPvK1 õУ~f{,5W#\ku;\EKi^KZKxI%83W+<m {J* =cEdN=03Xv#Vfkq0!d!Aie1O$j=iRČOA@^#Cta}=;I-` <Xc2e=CoamlۢhH`ePByA.DZ$ʺMKEO7h Rg"̞cLT\ }h =[ⵋ˅vsexlO0nsqZ^^ZI 88>eP%4d Um'K@%R.yOjl%T}oL` ѦF.순HHmXn0r95f@ssx~e 2xy),6$cZ4P>|8SuLj+$=kb BDU_$2$O EW~0O~5Ut;E<= VvQ*袀 ( ( ( ( (3KWR5@(ր1@RyEZI;PIT0ОAkw/Gkn]YmoQL @0è=iO].7y]=o]\-V&]FD`TvZWED< e*ZdG2B*(m}޸Q@Q@Q@p@)kW{kxek7k`MER(((IT5"xtM{EPfQEToS - =MMpJ]DIi] (3 oi$JEMљq5Sa 1%טWZTR5Y"3e BA?34S3SSk9h)re>aD1Vbk=8 ڠ*9\P4(fMÏQRP YمQ@(((((((((((((((((((qkPcEO:*U$q?5j ( ( ( ( J(QHzRZJ)h)h ( ( ( ( ( ( ( ( ( ( ( (STDGAiQPH+J ( ( ( ( ( ( ( ),@4V"9IEPEPEPEPEPEPEPH*c9$K1$> o1h[_9H_scY\KEj((((((((((()s 4GAE0MhVn$~*((((((((((((((((+/98֥PB27 O}s_Vj[gy@Q@<Y5XtIMlS+SqZՑjzi$Jנ(EPEPEPEPM(CiPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPviv^Zo* rt*ZkÃy(Q"FYyK;Ui'Ӛ8z?4k{m)ei*o''02L/fq3՜>?)L>S|˷o=Vvv\[R)bp9 cMN[f!?Ϯ!#KO*s@?\%݁Y66WZlƧVo.[R+E&LV5(kyU~Mp.W\έqȏW#ku;A#tTTVwN;oϷjZ@QEQEQlGLt}*2歧J e!h#PdRz u%s IES$}  +{|fX'DVcNyHj0c(+ ybҧʈ/~%QEQES]Pr2(h sҀjH ȥ'&STrBGj}Ղ( c$3t{Ҭti֭<@y?l??l|Ҷ(/65=2au₢FS +F-{cZuiQLw3CD.1j=AAٸ5H27bhPYe-1j(QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQER7?JgӞr jU،`Ɵժ(((kdY\)vڿZq\μYu>mVߟ۬pd9`"Z7m}kPBNHҴmA3u5Gzu%-Z))h(J)hwqYdhONPYJ#/QEQEQEQEQEV%ƴ: gi^ֶV#ݬ!*}N4Vq .?jMpK"*o% >[Q޺xc{{tQH((((((nAGObܓE;_'8?JML M_SQ61g?M6hf>t;lO}RA-QH(((((P4Y6ư`# P0${WHt+B|u=˱T3L t*PlqJueW6qK2mPjRLEQEQEQEQEQEQEQEQEQES_7ҝHt(#ÜZ̹ ج,x@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@g뀶uܿδ+?[Ә &?mʭU]7>9j (1 $edh"85L d0%VdX/V%-R(((((((((((((((((((((((((t ?έǔ0(BQK|vǤE6B$#ЎnH2IykZ,pBl`I)_*ͪF#r,jl'XdC!@yVFAuV)RݦC?X W!vӚݢ^w,05-މouv. 1EjTs n3@ eCXȽh56-ٹ@F0jfo&4ټ[i b@qv5hapK2ڡF8kH siVSd.NI˧MU(:}mj|A9'5KF'2`zEWi< FL0zN ( Cq֖jn7w|tQES>֭F1ҫ^*_0asPg̓S@}bR=j$#®cqzV/VAtFQAQE*yɐGvJ]ݸu5m!hruUQJu KAUynJGR)>_9* (-Yj:L>^5-U!]jM$ (FB Vyp:PqV֫+n=qApkiR 2Hސ۝#hGkh2

hȣ dQW؉amj&f!N1ԵfA0I\Y-r2 1i#oOZxK)7 :(ɿɏ}51H4S`= k#Dh_[2t It%K9/4߁Tr[*@SL6|Ns@5uh(۞{6f'q%et4e&;~cԙ [qҢGk'KةbUԁ ~uzY4%+-f\؇m:¥TRd]LxrzK;„}?7nZȤjO]7:#""$$JZf\ܤ6 5PKm(QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQER#}( ^Yvg3KǗ>@ KIE-Q@Q@>#GXgJ8സaTucԐF]@vQJRQHQ@Q@%-Q@Q@cwj'oZجMBD[T(#S@U6_7E|Vz3¢Fğ-ϡm=hՙ(˜cRP%}+ .E}3Ag42ȤKo.IkXgf s\$q]JCOҚΑI7Oczqڌxl6>[35L1@O-I$6k2 Ki"ꨦ*FA@QEQEU,pY*b3ArPcPf15% 4wk\*ȪRmdN9Z IߗH=q֮"pPe)_dQE id0sV(ڰQEq'%P6QE ( ( ( (+\ cUoc9W,s鞕50uv]!EM4RF)h(NmgzVcs巸O)lzu!h#ë!?Xϑ8*۠6#ZQ\@Q -JJ,02NxWVn;U ։;QAQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQIK@Q@5})ԍʟSvYY&yG= iS)QEQEQEQEQEQER ;EQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQIF-c2H=*/gifg}/}gOCrM_= Am}mw̮GP:¬Pn骪r7*:rB ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( 6Lz*Qq.}W]ِc3WjsM^( m"g#yЍlVF5M}.?席Z?kPER(((((((((((((((((((((,Sz} t(((;^c}0cMAt/[1e2YZUɷ.wł+nEތș巷ԎieXyaO^U{-Vye^;°째E`sfc.>r펀b:[{X-"H5,GRkW"c;{RxVk;vLnrGP=PjY13= ,H#Iob.6RC$ XRON?f/dǰyETИ.qrM V %I;GAL֭g;C|jru>Kw'q#nTd~ \0ʐGXd6IèS÷B?8_Bx fѮQrm'&W]1\~kěc׊۳У\ہVPjN\W+l[VF¥O`G4|E O$|@6Q[$TwHBx\?րJīr5W#k@c2}?UP(@QEWfTZPH3:ʖȎv+ Qh81<:T6WVA#H;FdĮ;y V܎*s\iQA!EPEPEPEPEPEPEPEPEPEPEPE `sRGܐI\z>U%h= -!@.?ζ,j@kn ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (= U]cZT}̯?".#y1(2i-59]֔jOln(Vw8ϦG!EѢN(BpO˧NJ,vĪgPf9Eբ{2^oOM4/EբN#50FwlQYvi15|iEԣj祴{njQY[mƌ=v}2hRƲ@T`Ӽ\EiQYX͸iBO?쟭/ER?P.9lKXH]}X`?֘bF~!Ӭ| p/RSj? T^QU(pRhHcL?lOKMذd[@:C)Tci|aJssOǢW-3RQ#؞”kVg7UAK@Z$&?Hm#dkF]f8XnIlI*(;Xc"*Tv =PZ4Pw(ѹ%Ѣ?.֛g2\{ȹ)o01Js!3ƘkJ`fHaN7z@xF+B7k(i~GpE3퇾iw<_ZTQp2ַ L}M/N?{էE0k fKPO .\kQ5n[jZ/6& EZq}fS7?zAi?zӢ=G_P, iu Ң}}RLF)Fv?'1 Ңr['SǠRd.*(-FW#ukN.chstu4Ѡ[g&[mMj@g@??4?cw jQ@GY3/!Ԣ3b3~5ֽBH΅[~6嘜 EǓ `_#ӚѬ=~}y((((((((((((((((* &?Uꥬg.vZn T4_D= (zyz3θFZZc+'_̦QE ( ( ( ( ( ( ( (8 sϵ-PEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPvf_*m/At~?H;sU䲷_2Hje*$3A6qQAiolX F[Q֡/ơnXUij )+ǥ:;_!@1ثTP-3N]:'Ur,H:}2_6XA\5hpRiċH#!DQgZk=庐 gV-3vfڳ]4Op Zи.axW5OOѠȬI3vENM2K)Udt'jQEKSơ 򌍹O^ج_;J2wutqi/3 (Z((( X=XtTjY_cByVR3|7y#A&eS?:]XaAK4^f8`*E4eŏz KVX阄c&arim|@V=).:TS#Ob3RI0`X"io3v}MxR҃HvrI4@ܸ#JAs8E|O%QsEI򦖥zF0+Fg;yh=[Iʤ!R/dQM+Jp5X$!Qb17ùkpr1@ bǝ)>c/QUЦ?|4[29fX=MV>,k )`VbI^ާ>zғsZzfrfQq R@< Z ޛQ-A*lwadis0*֝ SzK\5)݌qvjmz8D;(_ZVR((){PEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEc=i1KE&)h ( ( ( ( ( ( ( ( ( (3tO|uY((j{\5i((((((((-VHSPOz:LI[|4M-fLAђG'mXa2 JqڠQX\[E(QEQEQEQEQEQEU-\\g֮=WAPZ:bа4B5@Q@:&ÿkZrEv.[5M*OQ*iEegL>+N?k(Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@u?.A`W8?/mܠ 88\h*/XyyvuFK'M:1ݝO,WԓXgqh.2FpOYך{%pUNvh IoQT%`IWAG0>խSPR[ H`4$WLɷ#mexጄvRky6֘n!f;^_hɷy@ݴg㮯gX(=Er[#WZVJ,qs(1FXw4W=oj7[{xa׷[3HҨG PsHEQEQEQEQEQER' vr3Vꥮ *܎w) gK1YY nR=k-32E|sSR/JZ m!QEkTP4@QEQEQEQEQETN&g_ QE((WM q<hGA&RѾMOPB>mMmV&^6Oặ3 ;QEbht:cBkn4vIl(EPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPHzZCҀ)XȋfB8`d{]B»[+J޻ -ͺHHJ915xZU6H-wMHc[t MX\n\(;\sʌR7pkO#sN+"| GJQX:\o.#bZ:5ĉ4 k/4}R[LdG84̖(C+{y7sg;QZk ܮd9<>3c*@\P2Oz\gv_6_ghD +w֨/qr}~O=OM \ʠivH$#6]E̻9!2t{܍~u%QI,1X+XDP8@&mTet" p>sD9*Zkk!(9pO_RVʗ 3W9jQH e2F'$pۢ"?MX ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (2Ln Z@ wJҦ(@QEQEQEQEQE#0U,zYr׌DsB5$㹦Tc5=[QAEPEPEPEPEPEPU5OjV6f3@4JtNNZTQEdݜ?I}}Okh3iFшY ]cz_W=LMER(((((((((((((((((((((((((@~Av ֓Z8%)4BFQE 9^ Vǽ>@pӕAkacyWjp47E& v 'ߔd+J@gh{E*!z I9^N*VAj>e( v[)53yz,x5zx~t6C6~zQ)+un4$_ 5^ъI_kQAR3cKYz8# yb"q/"T4BxvaP1 RlQYZifI<dP_'EfE,ɲI  ֟Z P( aLB>aWYVdq98*Q^@pG7C#и]MTQu4Š(F&gƶC?6ER((((((((((((((((((((((7L-A<bեYz2Ňޅ0~B)cIhPgv9fEPFv((()(=(Z( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (34~Ӭ$6g@!<δk3AN럝iEP&?8IG}>O6vƞ}Xx ?S\pP(Q@Q@Q@ LPEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEi3!L'L\}K.|)Ѝ04k/]$'Io+{S~R}Is3&FޯCЮo 'X8P@ֶ5-fqw\v%i rB* vre8`>][PKTbh0ʐG-Ql^$J5d#sR^FUiP~nȯf6,X!GSBzʋX@eOJvX:i{I/y9MwI$# G'n3iApAwfɼx؎@r ڀ:it  z k]"m"qsת:EޭѓC1 }Ԣ$`P RLw~+@AQol5}fd1=>c V)_)c`$dWE1@aQ}bp U  GhR5tRo X5;Y֭@5yngV=ׇa985Q@:\:L1;%1=OqSRYZ2͏1q+B{jqA5&L+zi[ZH$INjPER{YeM(*)7kp4.G&)pŏAIE=-55PCww (Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@:&v_oVjܦ(@bh&űm&1#[tQH(((((((((((((((((((((( 6{W1S5](@QEQYچq@4T693ڦ((((((*`dEP}^[VLܚˊ,jڀH*;QE((((((U$4U" ^֤[[vAhCNF)IWk|۽HTcؓںl(EPEPEPEPEPEjH#(ą$ p+5x/!;V&ɮT(wϥb֦&JncS)QEQEQEQEQEIXS\ͣ0c̆H]e"RxٰbU ӾZA)>Yyjn,yR:{a") >(EPEPEP~wgY Qs6kF ( ( ( (bRIҁ}<*V6BS#F>aV`thJZ(((((((((PEQETW_7T 6~!ZP0G(DP۬M/=yЫn̿Νts4_FOty3G@/E(((((((((((((((((((((((((tz?WvS >U>sN(EPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEQEQEQEQE%-PEPEPEP?gxzukzVrwll(&Ƶr=wxms ݦ(@QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEP?e}!jY`h`QE ³y&EuR+R?04kdֲpf@ۘxVt9E!wn=vVknGw>( @QEQEQEQEQEQE6爓jSֺJʟEI!,4V]QZF !ϩZ@@;ʳԦ,~Pݍu4Tb[ZS0OH((((((giL.I)T$U]񲃌3\ڛH7tZhkG%¬|b*4K NpyWRYhf{ 6WK$6+ߢ)QEQEQEQEQEU^sO"5zVf@u9\?IV鞌)W_̾a+~O9s>5n=:S~f%:#9jmڱh!Uc]OViQEQEQEQEQEQEV~jX0*OJߪ IOeb3E_ZX'$AߙM~usJ~,^c#`jQE((((()03 @zQ,mo)HCkz((((ӳD1GZ5B'@QEQERVvs٠ŲIg ޔa-M< !9n[֦nIh((((((((((((mRTs3Ä9I(zBzIؠ(  ջXIT?UMGYl9\~bDrӵݦM~FH:j:Ң)QEQEQEQEQEQEQER M-QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEOV뙪i͓eb=ާ ۟ʫxAN(EG-Q@Q@Q@Q@RZ( ( ( ( ( ( ( ( (9 ( ( (-Q@Q@Q@&y-QEQEQEǿ+z0m)?*i (K]9xڶkϟO?`QE ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (3~縇h]df) (s\j0;03 5sM{"OP:ԖĶ!C ^ D_evZЂ_:GXr)ZQ~EPEPEPEPEPEPEPQ h`O )*jl_QhJ?M>|:Ɯ΀4h@QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEVԀ:u= mXw՝DgO*8I~Ԣ)TLJ')&-BI[̗FVboj_l#xTQEQEQEQEQEQEQEQEQEQEQEQEQERR@Q@Q@Q@Q@ʳȱATt[ָK<iEQEQEQEQEQEQEcab'91n|_]6ERC0nV>#`QE ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (33#Qõhu1֍6ER(((((EPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPutUYvl#?\3 Hϭ1K$O>g->89OH1#Tc4(((((((( Z( ( ( ( ( ( ( d18SO(G?_UlPEPR샕-5X3oX1ۦg:|uPD0U@T7Ski&#;zV5.gHfF!3[0RFg-Q@Q@Q@S]U(59 N*(+RZ!!q`jWkQs)7設a|_jPЩVSO~3W+R?`QE dvp;@Au+vc)zQ v@:*J(OFQU%XxčfP?n#tΡ?MOk(@az1XQ`x{kv)QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEhz$Z̶j|v_ V6ER(((((((((((((((((((((((((((((((((((((((((((((((I0y &$$PIt+£X|Z EuC ? DT(f[ ՠ&Š((((((((((((((((= -!hÄfcJ٬o.ϴ/#[4QEirn$$-_%pv]E6]PܓSmN9&o~]JҶd;T`KwE3 sȠ F9#WnۦF- #>s0* ʁڀ5MI"DHq8Ҳ,!  ׃Xkɟh%550+}ERz`EtF'/ HWJHsam$<.zn6I\V\KYʑw`5<;Kum[ila%,']} Yh9 EIнtd T(3m]r`|ѣg*̄x0/:(((((((((((((((() @'u=sbx&k>G5MQE% 9cX`y;_l|WOMWSu?UK&.1UT܀qW |c (Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@%? ]/\ Ѿ*,n fec"(f( ˭+ƹ3H˂*{/+jo:l{^sq yO5|0L : S|}vOG$vn.*Nx kxQ:Zp8!6ElZiqִ.+y1dUKdHc裯h!4+-HD"nQE5\ Ƣ}U.A|p775EI"V;ygս?K_ >v{Ԋ@v\ ?+gwv:^:}(GE yϨ,?TçakÞ)E>>V\ _j}X^`dj.'3vRMd}G[Qp1Xr?Y+ZbX9yﻧJںE°Bj٢mqp֣?Zcux?ƒXϹ#?ʶhb,Ol P>yyxR5Tc?€7h-R3M8>LP4P9-}u Eb?R9=Fu&97k + i&d]ytEaǣ׬f?֗ק3@tV0Sx߈?JtikzB:c`rnicA;#k<e6Ïg3>@b}:i%iPv[2~7票1܅zbHIEsWڛ!C mu 툞>^ @p3]5OOk51r)QEQEQEG4Te})TR_ʭTN( ( ( ( ( ((( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (9_rLџ+n|< cNS`QE 0;|I5X7G*8L~FQE((((((((((((((((((((((@?PO+N!uaX?Jl(EPEPEPEPEPEPEPEPEPEPE((((((((((((+/]V#d8w5\vqj2/hAM]<>4&kYq</Zimwq3*uǯ HܸP9>3M ZCORsV㰸&xW Vcntb@*IQ`7$g#؀uۿp°+ZOkV)QEQHFihFAȥ(((((((d1*laV0`EPEPEPEPEPEPEPEPEC=63H>Y20e=EQEQEu?ay\Xʸ1J3KkL0Yǹ+@i]3[{(nKm[ sSGu ܢŦ3q e +)l/_=1M6x8TEcfߖ|w9t˽ B\x_ROm:蓋1hN˹#I]&|Ұ Z+,/aѦc^ɏbG z+/n-ؖhMMjdzSHt돯 s'CК#VR1]Կ6Y.ޠ MѹGβCN3ni?J8޿l_γ?iF0dlu@^l`gztyEf c UЭ>8 ,c?iTN\ H;u?3N_70G} O>txxU!YӞ3@l?Р^qЪؖGS,h q(7UVKQźPhQn#iuCim9CTh Ju+A`?QhY?SGM4AZ4cߡɴ !9Ɨ.9(OѠ5k6M V~F[ UXgڕlm+FBڽLBL85/-wp8[A0NzQZ ~SMՑ#ǐ* W=Hu qjȳ dRKv0FT:ՠd *̥0 )tUͿ,?ͺY9OCy4hE1$gsN:Ÿ8+ >b**)F>A -drI+CȋQc/@[R}Wb ϷZA@TSD<ҁvfs%"1+E"P0NP C]3ХqMjlOK: el6R**aVLQz@-ʽmu4kr3hG [J]^[&*:_UQfXc<Ed.xbI]GٟOV>qnHSFt_h+fѽ*HǦAF ~ lQ@R,@N(5EdUoj"ɪ}Ң2m$ɦTkEY51BGgW9[sӢ3v|]&h M[w,N\kF.w񋈳Vh󟽴bhd~TRua-}1kEX5Rg OF?lEKmSLm\c?¶c}WqH~ :Zbv<ԿlMFi8Z(wI:]?MkE:]R{j#^1 շE K7yhŸA\v_v_7#'uQp9vrp$b2{S뢢4*}?Ҧ_ q?z 6ienëɫ4Q@Q@z[)nV3Zd3ɭ`QE ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (2#[^EkP6O'MQE(I]cjr`ڇ!z?ZOMa>6`l|KiF% |j! QQ@Q@Q@Q@Q@Q@V71@n%)8 ˻|%>Dg*SZ= HoAY " (Q@Q@Q@Q@Q@Q@ E'UFx $8M{VXAu .""6#~_MڋbI&$i&X]|]Zgmc֢)QERRP\׊aWKX>(O[ELJhiSi=$R3#n6svQՔ;KHq~Ի7 ?mC~oe}V*fmL}홈1!]^;à]y@QřlnJn}xPr/žY@~S\%ĒA'&r }3g1>(ە:P 5]滪c-651QE ( ( )8fT+=i]رEWI|v+%2I( 1tQ`&i|/.@iTXTB۟o9vr7; "Ob/OТeҟ}ƮQ@WB449T(op(QEQEQEQEQEQEQEQEw ]$gt{3ZՍzAj lI`QE ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (0k|nV&규rKdmA99 ER03 EQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE#6$aعg}#^®BEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEbjMZ#9%Gkn5ڦE((((((((((((h IPEPEPEPEPEPEPEPEPEPEPi57EiVS7N!^s>ZQH(sQ,9^e:f6EqPf0'NpE;HkFcc֮iL$}ESAG3Zh* uQH(((((nEԌ;w#dy$*(ms%x̹VV}Usm)gY[I/? ; Z(Q@Q@Q@Q@Q@Q@Q@E#19%{ԖZl\3&0\PEPEPEPX3cɪ,]|_rGTbOn4 aaUVyfP&] oɎO/P>r0gR=z.8-+KH^b0*bPʖ+U$ЯP4[99ɩ-Yل,X4Kk54L@cP2.+㸩bDz}*G`Vd'odz Z3Jd+R+gPNzj{3Dɱ@tQE ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (2OlǪ7J˺>qjS)QEQEQEfPEPEPEPEPEPEPEPEPEPEPEPEP&ރ۬M7jw~cMQE(((((((((((((((((((((((((((((()oʟU.̙b#A]LFcՊEPtQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEbj fͽ@ ׭֛`QE ( ( ( ( ( ( ( ( ( ( (2azZCҖ ( ( ( ( ( ( ( ( ( (2#\#͏Vdjl(EPEPEPEPEPEPEPEPEPEPHI`dԴPEPEPEPEPEPEPEPEPEPEPEPHii6ُ5VB9o]1$kΡkrZZI1z:s:fvq"wp+gWv%ڧΟ)^ۿÚӬ[Vs"w\`VnLn]h81ōӫ H0 (]* #8#5Fͮ^Xs]h,ȌWCʾƀ:!CHds4[[| r`A@ \k`1]?Sn"նH{z晬qn$trcY PybQ@Q@Q@Q@Q@Q@! ( ( ( ( ( ( ( ( ( ( ( (3.OlZu{:{Zt(@QEQEQEQEQEQEQEQEQEQEQEQEQE2I<WdRҀ ( (0H k[tzgMQE(.1;0ֳtjcprC#g"'?uI#Օyp ?9iU$[fv8-AyAr)] vQER(\m1{yoJg9,rN+jrhLPGIDP:7jP@G.^(Q*45QH5XVfD Z5#'~BuDg'"h;h~m%MjZLsK@Q@Q@Q@Q@Q@Q@Q@Q@Q@88:uQEQEQEQEQEQEQEQEQEQEɤ-ߵ>Jyv(ʎv>> պUETRww (B(((((((((*=:(((((((((((((ɥ ( ( ( ( (0u{nsŒoW?C=Cژ ER(((((((((((((((((-ޖh9Sr;((ƛqkf'$x"Mm`QE ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ()jN@fxj,Mq!?ª?Z۸΂HW;attۗFpژ ľLA5}L0=-K};Wl s#Ҁ5d N}Zkwq*=>KWIE!jR2bc?Y5Ke+ǻp('Rlꓓ.j3[ lT0Yrd9#?ZvoDϩ94ۋ|ۍ8h2@~wqYVT|[_Yn8R[g5bXk B"IZK]ZO.9>rx 1*N5]rz-}T~p ;n ^JX$r@EҗRycIa#k. fou#kNn! q"o*0SVp>2?5\b6׭<8Q\`T]gdAU$Vɂ$IYxv5f+Be6}::svX _(Sۙ{VI,%9ɩ]#JuE:@YWOrW*y>c9]5g9?կ!x(xv.6N9UTRd▐ V)."PSإ@QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEG4ZűwBizb۲QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQERn:˟LP~D%>a_*v劉#0d'-Р EdM)>myus9j~lEPmfzkVD hQY#2w Ic?@tVbmdNn2 iYm `CNmr@'?7ԧW;ZjJ.̄Ve$ZG)i6>S04@QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQER0 4}ݩ]PEPEP%Lj!_kn.H!5MQE((((((((((p-޿:o_ΓO@fFL>)2&KEBnɚ?S q@hGThZc>zcEET:BqHڭ\6f?IՖqSNhQYYɦnZ붋Ef6fǠog#mZݶ~~#8FRN鷸!)c^.(:+l>nL;w?VCVR0GelM($ddӼ۸i@&A9>JsܐWN~<unhF#QS#9Jj1Yw祑'} k+6=I$T38P=ik-F7q=aq|-6=[O4FJh ܧ".wj@G2*{GP7&r6 5J[/Dz3ͣyn5@76nőζE0Os5ƴE_H!]Z I 4QprA!͞Z=7eUw7ץ^Ep%_{~[t$8?ukD]3Tc? /ivm |铜S&}-z8U?`o\f[S'Jco`]Ĩ=5Di7Ԯ3ߦ*13bMF]UKvrq;[BLk垪exI_W'4.t:ޜwsVԥ&Ƈ#0/)A?(:P>ρ pi,tK)_i{G} -$&Non? in#V񏳏Ni?,ßrƀ,iY_ФӲ:AqܮMGa1s)>٣^~&˺iX4a,/iۚ~3㦥UmS-!E0 ˬذț4OV?*nq̜i?qgm\[+U9[xtSEg7-kB[xT`D)|"2Lja92dvȤ:fՅj""OSThEnXLލhm`~buH8'+/ + !ؐ)0(z{Nhjk]XnFMe lxֲ@ 9N]c@f*vBM~-si}{} Cyn?g"A3I8qv( yl Vo^E1ʵhe"jɒ,ǵ)V 0~֥\ V'jE\|hsZQp2ͮH{ FnҵhdyY*`^hQEzJO9mN+V.P~Ɛ鷥?5E1ۖ9s܁צ*^he&MkiخO7=?ɭz)\ ѝ:]O?G3]9Ƶp2 ϵ5t۽@z(CC\ Sz'ZR@?备 I%ϮEiE%1ɡtKU˟ƴ m9Z= EQm&՟qVƚ5HqF5Eg FqZPh'c%؃ع(<$4v ֍%ۏW5+Vnr&Q@%`ȩ(#U>bEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEG;B~cF(sG+[cZK{sc/UTx+03֢AAO>OѢ3'd_Pr*y}A@XeQ+Zuf'z֦ER(((((((((((((((( ɬkQԗSObEXXX43|0)GԔQAwՅQ@((((((((((((((((((((((((((py5-V8%ƥŠ(AEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEs@ EPEPEPEPEPEPEPEPEPEPvL~f+S\6 nS`Sb3i*=g鷪K"չYW2eymԎƄ? ܷ$ߒI1R$p !жբ`W~th7x嚁 C]pʞ@ C(jZvC)wnj˿ +VS(!)]Jcթq;6HqTnK'8"(lH=ZS#oր>2NY~SW5!_Z>y |օ ܦ]7@rt{W.FKIylHvSi37VcW(Gݦw fbAlV^D9œ.kAyj𓴞A5gD]6.1)cfd9)9)%ڋRE ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (1@VL" V `-QH((((((((((((((*xnmF79뛍Hi=j-msP\R搶JeV(97'vQE ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (#qۡUu51 V-QEQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@?b,x?mGcη (Q@Q@Q@s ʡ$*5VݠYpB)0<>GhclbjiٱhT#* )qQE`REQEQEQEQEQEQEQEQE%-PE5qyPEPEPERREPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPHN;f@sKEQEQIqK@Q@Q@Q@Q@Q@Q@^ !mbb3@@֚d'+'ă6HFx~CZ)(Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@f%TJҢ>S65wg j>ѽ{ԴP9OQEQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@2W=Sْ`z nf0ƦŠ(AEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPErcu\u?Zi (Q@Q@Q@Q@Q@Q@Q@Q@Q@TsOoL nP+8$TQEQEQEQEQEQEQEQEQEQE%Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@>!\!ԌԁY#o VG1!QLE((((((F`X5VM ދBų8f#fdQQA!2IhY q< ^ 2qAD uCj;htnEP (%21Qe9eTr:tT(@QEQEQEQEQEQHHPI ;AmnR)TowEŲv< ۮSu?j)p|AI{ld6Hvێ1KH(((((((((((JZ(((((((((>+W#׭jVg?[8e}5L)QEQEQEQEQEQE K{UaNh$TR(qr¢9Y@4@~OterxSȕ+d6>bT=jŽțvF("te*JjOjv'!ǡ12a(b)=a$'`j EuZEGт'h[u1' Syy\[;I"&ʖf op(QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEROH*X׫ui]ZBOOHj;R ݅Q@(((((((((((((((((((((((((((((((((((((((((((((((((((((((( MYsZ3Ыnuo ZzeGյL(EPEPuZY Lm:䫃uQcN1nbmeH#b/viY@QEQEQEQEQEsv:}̺Irsf$}ONƵ(p9[Τ"n95^ Q0]$H1"+B3BE @(QE ( ( ( ( ((ImY* B$.k+ 2PrOHZEO_Ӵ XbVP u' JFp20iQEQEQE&~lRm5Ol ( ( .nP$-ZP{[(m'=5F3Z~EawKt=唍nQEQEQEQEQE&Z(((((((((-PEPEPEPE'9(((  F7kJ/I7ִRLE((((((tW BUk’ hGEIW=9"X{y#L$; Be㍋m*եݛ~sʫj+eNjo(ҧme)# 3RQHV8 d`@97 ZyU5P QE(((((((((((((((((((((((((((((yw+-2C1GB;Q5HAw>\Pk槙[;snZIQA!EPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPd%Q*+i9OT-۷AӟARqe(0(LPEQEQEQE%-Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@m=O=[uq} ٦ER((((((((((((((((((@KMQuQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEeAִ Q?PִccQ@QEQEQEQEQEQEQEQEQEQEQQ\iJĀ-giDhT+QAEPEPEPEPEPEPEPEPEPEPEPEPEPHH'ڃqfԏ?uޯ  'ss\9Ns)Tmx(g8QE!8LA"SQEQEQE^ IJ= PӏPi (0(`avqҦPit * F^#23A.-$QEQEQEQEEqn"`HF5!8U{k;ێŚ((((((((()cPH$PaAJo28ӜjXF dgڃofSn*ۗhe8Y{ԑPC-Yc\pjhISr$)*#czS&E֑0S@X0ʐG-T+s. NAv1R6-Yhub@ A#.X EEugEZCSr )[Ϩ#jƧ|@Ca$ӒǗց+UfkȑuuʐGqTSO+l{T1jHH#oւ$-MGY2N((((((((((((((((((((((((((((((((((U\v wa؊خz{BsMt4QH(((((((((((((((((((():u((((((#Ro_΀E4ȃi ю PUq>m>|X(z*?iK87QgER:TV$rJEP}f:?#M݋, hT?sǚ}liMB:# >:O\Vh4U'?vBr9ǩ`/Yj3ơa>G}y6|<NhT>ӨgluT..*s~j |Ƃ?y y8pż/'4$dqbo|À)6pF\Ffב,3>nI>*?>/违,rq}((Q@ B{YP?(FmUaf`:AMrH_ZDa`@.̰@NR7h.$Vɩ+:h S%PK}c$cYWت"Ʒ11|g4T\ H%@`vEPjU<$'4)5Ө2 ( BýR(=L, N2c݃Ls*ziPOM *6j)-2T`.ͺR13`8{X*ȷG?iph)ԋ)VF.N47єg ެPcҢ$lx5fחgYdkBKxP 銍,^ւR 6EK)ŸJFDZhF'>2wJGuҝ-#}h^ӟ\Rxn(kdf̛8e*#'r+VtZ[z.EI,Lw6*z(3DW[#f Zv ֭5XaI9-ܞ[%gV֠E^NmȒydsfIPð5#HQKf/ҕQ3$^W dP$C ?RPM )Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@! /a{vib2}sx#;0⺪RYj[WgVͪ\iii'HL] J+)w|ɃTM^pIX\{ƁNOrhk#֚d@p]GY#Bl{a`#/6[v1D!MD8ϝ? IpR%Q_hz)F?Bco=$ǧF^dOSQSsUFn:I(GTh>h4hږC^cd|?L41@tȶ(7QֲDfy~jO=:b}Q`jˏ6Om%aMY пg䵂5 FT۲784lCgo&7O1R$R}Dcv h`lyp!=sZ>DAyihPap?ui$}XNԢ2ƩrN0Ͻ*wLHl~aZtPXlzoc\+ZmJxlhgj($]j/qN3jh:jԢ2Dv5֭nQ49jڎl6A۰\:ע3>ۨӹA 6!(E3::`SL`>kۖ(P:Z[j94˹tf!HsWD{yo=1@ EPEPEPEPEPEPEPEPI(A((((((((( yymGOK~J/.ܚnXs-ʱK$bK$nz5 Dl1TVR, Q[[-m$ Mrdsv$_H$.m7 $CWY"ΈSZnfsj֤  [Gfr9%v(M]'X r>#Tm[k0O[7sjEud @` `1&uIEwbP)hQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@sZV˶1Ȭk˖T胊|WREԖj]NEʚTOuuJ0AVI V}xYb]r{UhCɸvMA+')L r' {r[F$44mʀRWȭRIDN5,bX7CUO&>NTuܱ)@?vGj4@Iփ)(c ( A(¯Rz d)Y`@2 }j(v)H6wTFsAn*ZBC<\ ''p t#($gn3#iRKkn=&Os#4O31>P?k>dGB~DT@QA.MŠ*3*D}JTS!M:zԊw(#gk<ȝF*8`dVh|ܣ H_yQ(3֣0DzƿIEMIxAϖKE(QEEqP~đ ")P;X(QLg OzEPEP]B?2ձy;ez[_:`JKES9((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((e Z(((((((((((((((((((((((((((((((((((((((((((((((((((܈@PFOZUcj *wyJI䚛RA-Š(AEPEPEPEPEPEPEPEPEPEPEPEPTPĎUγs%G/5 lj)TPe(1)"I=)-`|5R?RAApWv(jlQE4ȃ u6BDlW82B )zVҭjF֕RNU{&êՊlOB(".- ޥyS/N* (led[4sFԤe(QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEV}{"օg dl~Iy_"u8WnBMler޴.YOJBG.^І\@'i@U#AaE.HB\H$օ2HU".WpINPz騋A)}(AL@SN+>t)45ʓۛxU.XWM P\L\KEQ@Q@Q@Q@ eb~QW9=P;Q@((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((()#.C1sz}(QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEۈ [EG> (Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@m譌dN((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((C?(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((endstream endobj 5 0 obj <> stream 0.24000 0 0 0.24000 0 0 cm q 2480 0 0 1748 0 0 cm /Im1 Do Q endstream endobj 6 0 obj 60 endobj 7 0 obj <> >> endobj 8 0 obj <> endobj 3 0 obj <> endobj 1 0 obj <> endobj 2 0 obj <> endobj %RRR010createpdf*001.00.200**********1000 xref 0 9 0000000000 65535 f 0000176802 00000 n 0000176862 00000 n 0000176742 00000 n 0000000015 00000 n 0000176424 00000 n 0000176535 00000 n 0000176553 00000 n 0000176618 00000 n trailer <> startxref 176951 %%EOF Solaar-1.1.14/docs/README.md000066400000000000000000000004651473525213700152440ustar00rootroot00000000000000# Documentation Readme This project's documentation is hosted using GitHub pages, which serves static pages using Jekyll. [Please refer to the official documentation for instructions for how to build the site locally.](https://help.github.com/articles/setting-up-your-github-pages-site-locally-with-jekyll/) Solaar-1.1.14/docs/capabilities.md000066400000000000000000000406521473525213700167420ustar00rootroot00000000000000--- title: Solaar Capabilities layout: page --- # Solaar Capabilities [**Solaar**][solaar] reports on and controls [Logitech][logitech] devices (keyboards, mice, and trackballs) that connect to your computer via a Logitech USB receiver (a very small piece of hardware that plugs into one of your USB ports). Solaar is designed to detect all connected devices, and at the very least display some basic information about them. At this moment, all [Unifying][unifying] receivers are supported (e.g., devices with USB ID `046d:c52b` or `046d:c532`) as are several Lightspeed Receivers and many Nano receivers. Solaar also reports on and controls some Logitech devices that directly connect to your computer using a USB cable or via Bluetooth. Not all such devices supported in Solaar as information needs to be added to Solaar for each device type that directly connects. ## HID++ The devices that Solaar handles use Logitech's HID++ protocol. HID++ is a Logitech-proprietary protocol that extends the standard HID protocol for interfacing with receivers, keyboards, mice, and so on. It allows Logitech receivers to communicate with multiple devices and modify some features of the device. As the HID++ protocol is proprietary, many aspects of it are unknown. Some information about HID++ has been obtained from Logitech but even that is subject to change and extension. There are several versions of HID++ and many Logitech receivers and devices that utilize it. Different receivers and devices implement different portions of HID++ so even if two devices appear to be the same in both physical appearance and behavior they may work differently underneath. (For example, there are versions of the M510 mouse that use different versions of the HID++ protocol.) Contrariwise, two different devices may appear different physically but actually look the same to software. (For example, some M185 mice look the same to software as some M310 mice.) The software identity of a receiver can be determined by its USB product ID (reported by Solaar and also viewable in Linux using `lsusb`). The software identity of a device that connects to a receiver can be determined by its wireless PID as reported by Solaar. The software identity of devices that connect via a USB cable or via bluetooth can be determined by their USB or Bluetooth product ID. ## Pairing and Unpairing Solaar is able to pair and unpair devices with receivers as supported by the device and receiver. For Unifying receivers, pairing adds a new paired device, but only if there is an open slot on the receiver. So these receivers need to be able to unpair devices that they have been paired with or else they will not have any open slots for pairing. Some other receivers, like the Nano receiver with USB ID `046d:c534`, can only pair with particular kinds of devices and pairing a new device replaces whatever device of that kind was previously paired to the receiver. These receivers cannot unpair. Further, some receivers can pair an unlimited number of times but others can only pair a limited number of times. Bolt receivers add an authentication phase to pairing, where the user has type a passcode or click some buttons to authenticate the device. Only some connections between receivers and devices are possible. In should be possible to connect any device with a Unifying logo on it to any receiver with a Unifying logo on it. Receivers without the Unifying logo probably can connect only to the kind of devices they were bought with and devices without the Unifying logo can probably only connect to the kind of receiver that they were bought with. ## Device Settings Solaar can display quite a few changeable settings of receivers and devices. For a list of HID++ features and their support see [the features page](features.md). Solaar does not do much beyond using the HID++ protocol to change the behavior of receivers and devices via changing their settings. In particular, Solaar cannot change how the operating system turns the keycodes that a keyboard produces into characters that are sent to programs. That is the province of HID device drivers and other software (such as X11). Settings can only be changed in the Solaar GUI when they are unlocked. To unlock a setting click on the icon at the right-hand edge of the setting until an unlocked lock appears (with tooltop "Changes allowed"). Solaar keeps track of most of the changeable settings of a device. Devices forget most changed settings when the device is turned off or goes into a power-saving mode. Exceptions include the setting to change the host the device is connected to, the setting to persistently change what a key or button does, and, for some devices, the fn-swap setting. When Solaar starts, it restores on-line devices to their previously-known state except for host connection and persistent key and button changes and while running Solaar restores devices to their previously-known state when the device itself comes on line. Setting information is stored in the file `~/.config/solaar/config.yaml`. Updating of a setting can be turned off in the Solaar GUI by clicking on the icon at the right-hand edge of the setting until a red icon appears (with tooltip "Ignore this setting"). Solaar keeps track of settings independently on each computer. As a result if a device is switched between different computers Solaar may apply different settings for it on the different computers. Querying a device for its current state can require quite a few HID++ interactions. These interactions can temporarily slow down the device, so Solaar tries to internally cache information about devices while it is running. If the device state is changed by some other means, even sometimes by another invocation of Solaar, this cached information may become incorrect. Currently there is no way to force an update of the cached information besides restarting Solaar. Logitech receivers and devices have firmware in them. Some firmware can be updated using Logitech software in Windows. For example, there are security issues with some Logitech receivers and devices and Logitech has firmware updates to alleviate these problems. Some Logitech firmware can also be updated in Linux using `fwupdmgr`. WARNING: Updating firmware can cause a piece of hardware to become permanently non-functional if something goes wrong with the update or the update installs the wrong firmware. ## Other Solaar Capabilities Solaar has a few capabilities that go beyond simply changing device settings. ### Rule-based Processing of HID++ Notifications Solaar can process HID++ Notifications from devices to, for example, change the speed of some thumb wheels. These notifications are only sent for actions that are set in Solaar to their HID++ setting (also known as diverted). For more information on this capability of Solaar see [the rules page](https://pwr-solaar.github.io/Solaar/rules). Users can edit rules using a GUI by clicking on the `Rule Editor` button in the Solaar main window. ### Sliding DPI A few mice (such as the MX Vertical) have a button that is supposed to be used to change the sensitivity (DPI) of the mouse by pressing the button and moving the mouse left and right. Other mice (such as the MX Master 3) don't have a button specific for this purpose but have buttons that can be used for it. The `Key/Button Diversion` setting can assign buttons to adjust sensitivity by setting the value for the button to `Sliding DPI`. This capability is only present if the device supports changing the DPI in this way. Pressing a button when it is set to `Sliding DPI` causes the mouse pointer to stop moving. When the button is released a new Sensitivity (DPI) value is applied to the mouse, depending on how far right or left the mouse is moved. If the mouse is moved only a little bit the previous value that was set is applied to the mouse. Notifications from Solaar are displayed showing the setting that will be applied. ### Mouse Gestures Some mice (such as the MX Master 3) have a button that is supposed to be used to create up/down/left/right mouse gestures. Other mice (such as the MX Vertical) don't have a button specific for this purpose but have buttons that can be used for it. The `Key/Button Diversion` setting can assign buttons to initiate mouse gestures by setting the value for the button to `Mouse Gestures`. This capability is only present if the device can support it. Pressing a button when it is set to `Mouse Gestures` causes the mouse pointer to stop moving. When the button is released a `MOUSE_GESTURE` notification with the mouse movements and diverted key presses is sent to the Solaar rule system so that rules can detect these notifications. For more information on Mouse Gestures rule conditions see [the rules page](https://pwr-solaar.github.io/Solaar/rules). ### Keyboard Key Names and Locations Solaar uses the standard Logitech names for keyboard keys. Some Logitech keyboards have different icons on some of their keys and have different functionality than suggested by these names. Solaar is uses the standard US keyboard layout. This currently only matters for the `Per-key Lighting` setting. Users who want to have the key names for this setting reflect the keyboard layout that they use can create and edit `~/.config/solaar/keys.yaml` which contains a YAML dictionary of key names and locations. For example, switching the `Y` and `Z` keys can be done as: Z: 25 Y: 26 This is an experimental feature and may be modified or even eliminated. ### Device Profiles Some mice store one or more profiles, which control aspects of the behavior of the device. Profiles can control the rate at which the mouse reports movement, the resolution of the the movement reports, what the mouse buttons do, and its LED effects. Solaar can dump the entire set of profiles into a YAML file can load an entire set of profiles from a file. Users can edit the file to effect changes to the profiles. Solaar has a setting that switches between profiles or disables all profiles. When switching between profiles or using a button to change resolution Solaar keeps track of the changes in the settings for these features. When profiles are active changes cannot be made to the Report Rate setting. Changes can be made to the Sensitivity setting and to LED settings. To keep the profile values make these setting ignored. A profile file has some bookkeeping information, including profile version and the name of the device, and a sequence of profiles. Each profile has the following fields: - enabled: Whether the profile is enabled. - sector: Where the profile is stored in device memory. Sectors greater than 0xFF are in ROM and cannot be written (use the low byte as the sector to write to Flash). - name: A memonic name for the profile. - report_rate: A report rate in milliseconds from 1 to 8. - resolutions: A sequence of five sensor resolutions in DPI. - resolution_default_index: The index of the default sensor resolution (0 to 4). - resolution_shift_index: The index of the sensor resolution used when the DPI Shift button is pressed (0 to 4). - buttons: The action for each button on the mouse in normal mode. - gbuttons: The action for each button on the mouse in G-Shift mode. - angle_snap: Enable angle snapping for devices. - red, blue, green: Color indicator for the profile. - lighting: Lighting information for logo and side LEDs in normal mode, then for power saving mode. - ps_timeout: Delay in ms to go into power saving mode. - po_timeout: Delay in ms to go from power saving mode to fully off. - power_mode: Unknown purpose. - write count: Unknown purpose. Missing or unused parts of a profile are often a sequence of 0xFF bytes. Button actions can either perform a function (behavior: 9) or send a button click or key press (behaviour: 8). Functions are: - 0: No Action - do nothing - 1: Tilt Left - 2: Tilt Right - 3: Next DPI - change device resolution to the next DPI - 4: Previous DPI - change device resolution to the previous DPI - 5: Cycle DPI - change device resolution to the next DPI considered as a cycle - 6: Default_DPI - change device resolution to the default resolution - 7: Shift_DPI - change device resolution to the shift resolution - 8: Next Profile - change to the next enabled profile - 9: Previous Profile - change to the previous enabled profile - 10: Cycle Profile - change to the next enabled profile considered as a cycle - 11: G-Shift - change all buttons to their G-Shift state - 12: Battery Status - show battery status on the device LEDs - 13: Profile Select - select the n'th enabled profile - 14: Mode Switch - 15: Host Button - switch between hosts (unverified) - 16: Scroll Down - 17: Scroll Up Some devices might not be able to perform all functions. Buttons can send (type): - 0: Don't send anything. - 1: A button click (value) as a 16-bit bitmap, i.e., 1 is left click, 2 is right, 4 is middle, etc. - 2: An 8-bit USB HID keycode (value) plus an 8-bit modifier bitmap (modifiers), i.e., 0 for no modifiers, 1 for control, 2 for shift, etc. - 3: A 16-bit HID Consumer keycode (value). See USB_HID_KEYCODES and HID_CONSUMERCODES in lib/logitech_receiver/special_keys.py for values to use for keycodes. Buttons can also execute macros but Solaar does not provide any support for macros. Lighting information is a sequence of lighting effects, with the first for the logo LEDs and the second for the side LEDs. The fields possible in an effect are: - ID: The kind of effect: - color: A color parameter for the effect as a 24-bit RGB value. - intensity: How intense to make the color (1%-100%), 0 for the default (usually 100%). - speed: How fast to pulse in ms (one byte). - ramp: How to change to the color (0=default, 1=ramp up/down, 2=no ramping). - period: How fast to perform the effect in ms (two bytes). - form: The form of the breathe effect. - bytes: The raw bytes of other effects. The possible effects and the fields the use are: - 0x0: Disable - No fields. - 0x1: Fixed color - color, whether to ramp. - 0x2: Pulse a color - color, speed. - 0x3 Cycle through the spectrum - period, intensity, form. - 0x8; A boot effect - No fields. - 0x9: A demo effect - NO fields. - 0xa: breathe a color (like pulse) - color, period. - 0xb: Ripple - color, period. - null: An unknown effect. Only effects supported by the device can be used. To set up profiles, first run `solaar profiles `, which will output a YAML dump of the profiles on the device. Then store the YAML dump into a file and edit the file to make changes. Finally run `solaar profiles ` to load the profiles back onto the device. Profiles are stored in flash memory and persist when the device is inactive or turned off. When loading profiles Solaar is careful to only write the flash memory sectors that need to be changed. Solaar also checks for correct profile version and device name before loading a profile into a device. Keep a copy of the initial dump of profiles so that it can be loaded back to the device if problems are encountered with the edited profiles. The safest changes are to take an unused or unenabled profile sector and create a new profile in it, likely mostly copying parts of another profile. ## System Tray Solaar's GUI normally uses an icon in the system tray. This allows users to close Solaar and reopen from the tray. This aspect of Solaar depends on having an active system tray which may require some special setup when using Gnome, particularly under Wayland. If you are running Gnome, you most likely need the `gnome-shell-extension-appindicator` package installed. In Fedora, this can be done by running ``` sudo dnf install gnome-shell-extension-appindicator ``` The likely command in Ubuntu and related distributions is ``` sudo apt install gnome-shell-extension-appindicator ``` You may have to log out and log in again before the system tray shows up. ## Battery Icons For many devices, Solaar shows the approximate battery level via icons that show up in both the main window and the system tray. In previous versions several heuristics determined which icon names to use for this purpose, but as more and more battery icon schemes have been developed this has become impossible to do well. Solaar now uses the eleven standard battery icon names `battery-{full,good,low,critical,empty}[-charging]` and `battery-missing`. Solaar will use the symbolic versions of these icons if started with the option `--battery-icons=symbolic`. Because of external bugs, these symbolic icons may be nearly invisible in dark themes. [solaar]: https://github.com/pwr-Solaar/Solaar [logitech]: https://www.logitech.com [unifying]: https://en.wikipedia.org/wiki/Logitech_Unifying_receiver Solaar-1.1.14/docs/debian.md000066400000000000000000000003731473525213700155270ustar00rootroot00000000000000--- title: Debian Repository layout: page --- # Debian repository Solaar is now part of the [official Debian repository](https://packages.debian.org/solaar). To install it on your Debian machine, use the following command: `sudo apt install solaar` Solaar-1.1.14/docs/devices.md000066400000000000000000000210741473525213700157300ustar00rootroot00000000000000--- title: Supported Devices layout: page --- # Supported receivers and devices Solaar only supports Logitech receivers and devices that use the Logitech proprietary HID++ protocol. Solaar supports most Logitech Nano, Unifying, and Bolt receivers. Solaar supports some Lightspeed receivers. See the receiver table below for the list of currently supported receivers. Solaar supports all Logitech devices (keyboards, mice, trackballs, touchpads, and headsets) that can connect to supported receivers. Solaar supports all Logitech devices that can connect via a USB cable or via Bluetooth, as long as the device uses the HID++ protocol. The best way to determine whether Solaar supports a device is to run Solaar while the device is connected. If the device is supported, it will show up in the Solaar main window. The directory contains edited output of `solaar show` on many devices and can be used to see what Solaar can do with the device. ## Supporting old devices Some old Logitech devices use an old version of HID++. For Solaar to support these devices well, Solaar needs some information about them. If you have an old Logitech device that shows up in Solaar but has no settings and you feel that Solaar should be able to do more with the device you can open an enhancement request for Solaar to better support the device. ## Adding new receivers Adding a new receiver requires knowing whether the receiver is a regular Unifying receiver, a Nano receiver, a Bolt receiver, or a Lightspeed receiver. This can generally be found using `lsusb`. To add a new receiver to the Solaar code add a line to `../lib/logitech_receiver/base_usb.py` defining the receiver as one of these. If the receiver has an unusual number of pairing slots, then this also needs to be specified. Then add the receiver to the tuple of receivers (ALL). ### Supported Receivers | USB ID | Kind | Max Paired Devices | ------------|------------|--------------------| | 046d:c517 | 27MHz | 4 | | 046d:c518 | Nano | 1 | | 046d:c51a | Nano | 1 | | 046d:c51b | Nano | 1 | | 046d:c521 | Nano | 1 | | 046d:c525 | Nano | 1 | | 046d:c526 | Nano | 1 | | 046d:c52b | Unifying | 6 | | 046d:c52e | Nano | 1 | | 046d:c52f | Nano | 1 | | 046d:c531 | Nano | 1 | | 046d:c532 | Unifying | 6 | | 046d:c534 | Nano | 2 | | 046d:c537 | Nano | 2 | | 046d:c539 | Lightspeed | 1 | | 046d:c53a | Lightspeed | 1 | | 046d:c53d | Lightspeed | 1 | | 046d:c53f | Lightspeed | 1 | | 046d:c541 | Lightspeed | 1 | | 046d:c542 | Nano | 1 | | 046d:c545 | Lightspeed | 1 | | 046d:c547 | Lightspeed | 1 | | 046d:c548 | Bolt | 6 | | 17ef:6042 | Nano | 1 | Some Nano receivers are only partly supported as they do not implement the full HID++ 1.0 protocol. Some Nano receivers are not supported as they do not implement the HID++ protocol at all. Receivers with USB ID 046d:c542 fall into this category. The receiver with USB ID 046d:c517 is an old 27 MHz receiver, supporting only a subset of the HID++ 1.0 protocol. Only hardware pairing is supported. ## Supported Devices (Historical Interest Only) The device tables below provide a list of some of the devices that Solaar supports, giving their product name, WPID product number, and HID++ protocol information. The tables concentrate on older devices that have explicit support information in Solaar and are not being updated for new devices that are supported by Solaar. Note that Logitech has the annoying habit of reusing Device names (e.g., M185) so what is important for support is the USB WPID or Bluetooth model ID. ### Keyboards (Unifying) | Device | WPID | HID++ | |------------------|------|-------| | K230 | 400D | 2.0 | | K270 | 4003 | 2.0 | | K340 | 2007 | 1.0 | | K350 | 200A | 1.0 | | K360 | 4004 | 2.0 | | K375s | 4071 | | | K400 Touch | 400E | 2.0 | | K400 Touch | 4024 | 2.0 | | K400 Plus | 404D | 2.0 | | K520 | 2011 | 1.0 | | K600 TV | 4078 | 2.0 | | K750 Solar | 4002 | 2.0 | | K780 | 405B | 2.0 | | K800 Illuminated | 2010 | 1.0 | | K800 (new ver) | 406E | 2.0 | | K830 Illuminated | 4032 | 2.0 | | MX Keys | 408A | 2.0 | | N545 | 2006 | | | TK820 | | 2.0 | | Craft | 4066 | 2.0 | ### Keyboards (Lightspeed) | Device | WPID | HID++ | |------------------|------|-------| | G915 TKL | 408E | 4.2 | ### Mice (Unifying) | Device | WPID | HID++ | |------------------|------|-------| | M150 | 4022 | 2.0 | | M185 | 4055 | 2.0 | | M310 | 4031 | 2.0 | | M310 | 4055 | 2.0 | | M317 | | | | M325 | 400A | 2.0 | | M330 | | 2.0 | | M345 | 4017 | 2.0 | | M350 | 101C | 1.0 | | M350 | 4080 | 2.0 | | M505 | 101D | 1.0 | | M510 | 1025 | 1.0 | | M510 | 4051 | 2.0 | | M515 Couch | 4007 | 2.0 | | M525 | 4013 | 2.0 | | M560 | | 2.0 | | M585 | 406B | 2.0 | | M590 | 406B | 2.0 | | M600 Touch | 401A | 2.0 | | M705 Marathon | 101B | 1.0 | | M705 Marathon | 406D | 2.0 | | M720 Triathlon | 405E | 2.0 | | T400 Zone Touch | | 2.0 | | T620 Touch | | 2.0 | | Performance MX | 101A | 1.0 | | Anywhere MX | 1017 | 1.0 | | Anywhere MX 2 | 404A | 2.0 | | MX Master | 4041 | 2.0 | | MX Master 2S | 4069 | 2.0 | | Cube | | 2.0 | | MX Vertical | 407B | 2.0 | ### Mice (Nano) | Device | WPID | HID++ | |------------------|------|-------| | G7 | 1002 | 1.0 | | G700 | 1023 | 1.0 | | G700s | 102A | 1.0 | | V450 Nano | 1011 | 1.0 | | V550 Nano | 1013 | 1.0 | | VX Nano | 100B | 1.0 | | VX Nano | 100F | 1.0 | | M175 | 4008 | | | M185 (old) | 4038 | 2.0 | | M185 (new) | 4054 | 2.0 | | M187 | 4019 | 2.0 | | M215 | 1020 | 1.0 | | M235 | 4055 | 2.0 | | M305 | 101F | 1.0 | | M310 | 1024 | 1.0 | | M315 | | | | M330 | | ?.? | | MX 1100 | 1014 | 1.0 | * (old): M185 with P/N: 810-003496 * (new): M185 with P/N: 810-005238 or 810-005232 ### Mice (Mini) | Device | WPID | HID++ | |-------------------|------|-------| | MX610 | 1001 | 1.0 | | MX610 left handed | 1004 | 1.0 | | MX620 | 100A | 1.0 | | MX620 | 1016 | 1.0 | | V400 | 1003 | 1.0 | | V450 | 1005 | 1.0 | | VX Revolution | 1006 | 1.0 | | VX Revolution | 100D | 1.0 | | MX Air | 1007 | 1.0 | | MX Air | 100E | 1.0 | | MX Revolution | 1008 | 1.0 | | MX Revolution | 100C | 1.0 | ### Mice (Lightspeed) | Device | WPID | HID++ | |------------------------------|------|-------| | PRO X Superlight Wireless | 4093 | 4.2 | ### Trackballs (Unifying) | Device | WPID | HID++ | |-------------------|------|-------| | M570 Trackball | | 1.0 | | MX Ergo Trackball | | 2.0 | ### Touchpads (Unifying) | Device | WPID | HID++ | |------------------|------|-------| | Wireless Touch | 4011 | 2.0 | | T650 Touchpad | 4101 | 2.0 | ### Mice and Keyboards sold as combos | Device | WPID | HID++ | |------------------|------|-------| | MK220 | | 2.0 | | MK270 | 4023 | 2.0 | | MK320 | 200F | | | MK330 | | | | MK345 | 4023 | 2.0 | | MK520 | | M2/K1 | | MK550 | | | | MK700 | 2008 | 1.0 | | MK710 | | 1.0 | | EX100 keyboard | 0065 | 1.0 | | EX100 mouse | 003f | 1.0 | * The EX100 is an old, pre-Unifying receiver and device set, supporting only some HID++ 1.0 features Solaar-1.1.14/docs/devices/000077500000000000000000000000001473525213700154025ustar00rootroot00000000000000Solaar-1.1.14/docs/devices/00README.txt000066400000000000000000000046141473525213700172450ustar00rootroot00000000000000Files in this directory are edited output from `solaar show` providing information about devices and receivers that Solaar supports. This directory does not contain information about all devices and receivers that Solaar supports. Information is generally only added when provided in a Solaar issue. Directions for constructing the files are given below. The files Unifying Receiver C52B.txt Craft Advanced Keyboard 4066.txt Craft Advanced Keyboard B350.txt MX Master 3 Wireless Mouse 4082.txt MX Master 3 Wireless Mouse B023.txt are good examples of following the directions below. File Naming Logitech device names are often reused so the names of files can't just be the device name. File names start with the name of the device or receiver as given in the first line of of output for the device. The file name continues with a space and the WPID, if the device is connected to a receiver, or the second half of the USB ID, if the device is connected via USB or Bluetooth. The WPID or USB ID are in upper case. As devices can behave differently when connected via a receiver or USB or Bluetooth there should be a file for each possible connection method. Files that do not follow this naming convention are retained for historical purposes. File Contents Each file should contain the output of `solaar show NAME` where NAME is enough of the full name of a device or receiver to identify it. The output of `solaar show` will provide information on all connnected devices and receivers including their names. The output of `solaar show NAME` can be edited to remove serial numbers and variable information such as the current values of settings. Passing the style requirements for Solaar documentation may require removing trailing white space on lines. For older devices probes of the device registers should be included but for newer devices this is not necessary. Unifying receivers can pair with any device that has the Unifying logo. Bolt receivers can pair with any device that has the Bolt logo. Nano and Lightspeed receivers can only pair with certain devices, so the end of their files should state the devices that they have been seen to be paired with or are part of. Updating Files Newer versions of Solaar add support for more settings so it is useful to provide updated versions of these files if there is information from the current version of `solaar show NAME` that is not in the existing file. Solaar-1.1.14/docs/devices/Bluetooth Multi-Device Keyboard K380 B342.txt000066400000000000000000000137141473525213700251100ustar00rootroot00000000000000Solaar version 1.1.4 1: Bluetooth Multi-Device Keyboard K380 Device path : /dev/hidraw5 USB id : 046d:B342 Codename : Keyboard K380 Kind : ? Protocol : HID++ 4.5 Serial number: Model ID: B34200000000 Unit ID: 16000000 Firmware: RBK 42.01.B0017 Hardware: 72 Supports 14 HID++ 2.0 features: 0: ROOT {0000} 1: FEATURE SET {0001} 2: DEVICE FW VERSION {0003} Firmware: Firmware RBK 42.01.B0017 B342C18DBFDD Firmware: Hardware 72 Unit ID: 16000000 Model ID: B34200000000 Transport IDs: {'btid': 'B342'} 3: DEVICE NAME {0005} Name: Bluetooth Multi-Device Keyboard K380 Kind: keyboard 4: DEVICE FRIENDLY NAME {0007} Friendly Name: Keyboard K380 5: RESET {0020} 6: BATTERY STATUS {1000} Battery: 20%, discharging, next level 5%. 7: CHANGE HOST {1814} Change Host : 1:Luay-A 8: HOSTS INFO {1815} Host 0 (paired): Luay-A Host 1 (paired): BCM20703A2 Generic UART UHE App Host 2 (unpaired): 9: REPROG CONTROLS V4 {1B04} Key/Button Diversion (saved): {Volume Up:Regular, Volume Down:Regular, Mute:Regular, Play/Pause:Regular, Next:Regular, Previous:Regular, Search:Regular, Multiplatform App Switch:Regular, Multiplatform Home:Regular, Multiplatform Menu:Diverted, Multiplatform Back:Regular, Multiplatform Insert:Regular, Screen Capture/Print Screen:Regular, Fn Down:Regular, Fn Up:Regular, Multiplatform Lock:Regular} Key/Button Diversion : {Volume Up:Regular, Volume Down:Regular, Mute:Regular, Play/Pause:Regular, Next:Regular, Previous:Regular, Search:Regular, Multiplatform App Switch:Regular, Multiplatform Home:Regular, Multiplatform Menu:Diverted, Multiplatform Back:Regular, Multiplatform Insert:Regular, Screen Capture/Print Screen:Regular, Fn Down:Regular, Fn Up:Regular, Multiplatform Lock:Regular} 10: unknown:1E00 {1E00} hidden 11: NEW FN INVERSION {40A2} Fn-swap: disabled Fn-swap default: enabled Swap Fx function (saved): False Swap Fx function : False 12: LOCK KEY STATE {4220} 13: KEYBOARD DISABLE KEYS {4521} Disable keys (saved): {Caps Lock:False, Scroll Lock:False, Insert:True, Win:False} Disable keys : {Caps Lock:False, Scroll Lock:False, Insert:True, Win:False} Has 16 reprogrammable keys: 0: Multiplatform Home , default: MultiPlatform Home => MultiPlatform Home is FN, FN sensitive, reprogrammable, divertable, pos:4, group:0, group mask:empty reporting: default 1: Multiplatform App Switch , default: MultiPlatform App Switch => MultiPlatform App Switch is FN, FN sensitive, reprogrammable, divertable, pos:5, group:0, group mask:empty reporting: default 2: Multiplatform Menu , default: MultiPlatform Menu => MultiPlatform Menu is FN, FN sensitive, reprogrammable, divertable, pos:6, group:0, group mask:empty reporting: diverted 3: Multiplatform Back , default: MultiPlatform Back => MultiPlatform Back is FN, FN sensitive, reprogrammable, divertable, pos:7, group:0, group mask:empty reporting: default 4: Previous , default: Previous => Previous is FN, FN sensitive, divertable, pos:8, group:0, group mask:empty reporting: default 5: Play/Pause , default: Play/Pause => Play/Pause is FN, FN sensitive, divertable, pos:9, group:0, group mask:empty reporting: default 6: Next , default: Next => Next is FN, FN sensitive, divertable, pos:10, group:0, group mask:empty reporting: default 7: Mute , default: Mute => Mute is FN, FN sensitive, divertable, pos:11, group:0, group mask:empty reporting: default 8: Volume Down , default: Volume Down => Volume Down is FN, FN sensitive, divertable, pos:12, group:0, group mask:empty reporting: default 9: Volume Up , default: Volume Up => Volume Up is FN, FN sensitive, divertable, pos:0, group:0, group mask:empty reporting: default 10: Multiplatform Insert , default: Switch Language => Switch Language FN sensitive, reprogrammable, divertable, pos:0, group:0, group mask:empty reporting: default 11: Fn Up , default: unknown:0070 => unknown:0070 is FN, divertable, pos:0, group:0, group mask:empty reporting: default 12: Fn Down , default: unknown:006F => unknown:006F is FN, divertable, pos:0, group:0, group mask:empty reporting: default 13: Screen Capture/Print Screen, default: Screen Capture => Screen Capture is FN, reprogrammable, divertable, pos:0, group:0, group mask:empty reporting: default 14: Search , default: Search Files => Search Files is FN, reprogrammable, divertable, pos:0, group:0, group mask:empty reporting: default 15: Multiplatform Lock , default: WindowsLock => WindowsLock is FN, reprogrammable, divertable, pos:0, group:0, group mask:empty reporting: default Battery: 20%, discharging, next level 5%. Solaar-1.1.14/docs/devices/Bolt Receiver C548.txt000066400000000000000000000004371473525213700212000ustar00rootroot00000000000000Solaar version 1.1.4 Bolt Receiver Device path : /dev/hidraw8 USB id : 046d:C548 Serial : 38423638313332394241363434313837 Has 2 paired device(s) out of a maximum of 6. Notifications: wireless, software present (0x000900) Device activity counters: 1=252, 2=135 Solaar-1.1.14/docs/devices/Candy companion chip 405F.txt000066400000000000000000000023371473525213700224550ustar00rootroot00000000000000 7: Candy companion chip Device path : /dev/hidraw11 Codename : Candy Kind : touchpad Protocol : HID++ 4.2 Serial number: 5B2B9A98 Model ID: 405F00000000 Unit ID: 32314707 Firmware: CC 07.00.B0010 Bootloader: BOT 32.00.B0010 Supports 11 HID++ 2.0 features: 0: ROOT {0000} 1: FEATURE SET {0001} 2: DEVICE FW VERSION {0003} Firmware: Firmware CC 07.00.B0010 405F Firmware: Bootloader BOT 32.00.B0010 405F Unit ID: 32314707 Model ID: 405F00000000 Transport IDs: {'wpid': '405F'} 3: DEVICE NAME {0005} Name: Candy companion chip Kind: touchpad 4: unknown:18A1 {18A1} internal, hidden 5: unknown:1E00 {1E00} hidden 6: unknown:1EB0 {1EB0} internal, hidden 7: DFUCONTROL SIGNED {00C2} 8: unknown:1801 {1801} internal, hidden 9: DEVICE RESET {1802} internal, hidden 10: unknown:1803 {1803} internal, hidden Battery status unavailable. Part of the G PowerPlay Wireless Mouse Pad. Solaar-1.1.14/docs/devices/Couch Mouse M515 4007.txt000066400000000000000000000027461473525213700212510ustar00rootroot000000000000001: Couch Mouse M515 Codename : M515 Kind : mouse Wireless PID : 4007 Protocol : HID++ 2.0 Polling rate : 8 ms Serial number: BED587E9 Firmware: RQM 24.00.B0023 Bootloader: DFU 00.02.B0010 The power switch is located on the base. Supports 16 HID++ 2.0 features: 0: ROOT {0000} 1: FEATURE SET {0001} 2: DEVICE FW VERSION {0003} 3: DEVICE NAME {0005} 4: DFUCONTROL {00C0} 5: BATTERY STATUS {1000} 6: unknown:1A30 {1A30} hidden 7: REPROG CONTROLS {1B00} 8: WIRELESS DEVICE STATUS {1D4B} 9: unknown:1DF3 {1DF3} hidden 10: VERTICAL SCROLLING {2100} 11: HI RES SCROLLING {2120} 12: MOUSE POINTER {2200} 13: unknown:1F02 {1F02} hidden 14: unknown:1F03 {1F03} hidden 15: unknown:1E80 {1E80} hidden Has 5 reprogrammable keys: 0: LEFT CLICK => LeftClick mse, reprogrammable 1: RIGHT CLICK => RightClick mse, reprogrammable 2: MIDDLE BUTTON => MiddleMouseButton mse, reprogrammable 3: BACK AS BUTTON 4 => BackEx mse, reprogrammable 4: FORWARD AS BUTTON 5 => BrowserForwardEx mse, reprogrammable Battery: 65%, discharging, Solaar-1.1.14/docs/devices/Craft Advanced Keyboard 4066.txt000066400000000000000000000301221473525213700227670ustar00rootroot00000000000000Solaar version 1.1.4 2: Craft Advanced Keyboard Device path : /dev/hidraw4 WPID : 4066 Codename : Craft Kind : keyboard Protocol : HID++ 4.5 Polling rate : 8 ms (125Hz) Serial number: 112C46EA Model ID: B35040660000 Unit ID: 43DAF041 Bootloader: BOT 41.01.B0015 Firmware: MPK 07.01.B0015 Other: Other: The power switch is located on the edge of top right corner. Supports 38 HID++ 2.0 features: 0: ROOT {0000} 1: FEATURE SET {0001} 2: DEVICE FW VERSION {0003} Firmware: Bootloader BOT 41.01.B0015 0000B6A2C54601 Firmware: Firmware MPK 07.01.B0015 4066B6A2C54601 Firmware: Other Firmware: Other Unit ID: 43DAF041 Model ID: B35040660000 Transport IDs: {'btleid': 'B350', 'wpid': '4066'} 3: DEVICE NAME {0005} Name: Craft Advanced Keyboard Kind: keyboard 4: WIRELESS DEVICE STATUS {1D4B} 5: RESET {0020} 6: DEVICE FRIENDLY NAME {0007} Friendly Name: Keyboard Craft 7: BATTERY STATUS {1000} Battery: 50%, discharging, next level 20%. 8: CHANGE HOST {1814} Change Host : 1:idefix 9: HOSTS INFO {1815} Host 0 (paired): idefix Host 1 (paired): idefix Host 2 (paired): Galaxy Tab S6 10: BACKLIGHT2 {1982} Backlight (saved): True Backlight : True 11: REPROG CONTROLS V4 {1B04} Key/Button Diversion (saved): {Calculator:Regular, Show Desktop:Diverted, Lock PC:Regular, Screen Capture/Print Screen:Regular, Brightness Down:Regular, Brightness Up:Regular, Host Switch Channel 1:Regular, Host Switch Channel 2:Diverted, Host Switch Channel 3:Diverted, Mission Control/Task View:Regular, Dashboard Launchpad/Action Center:Regular, Backlight Down:Regular, Backlight Up:Regular, Previous Fn:Regular, Play/Pause Fn:Regular, Next Fn:Regular, Mute Fn:Regular, Volume Down Fn:Regular, Volume Up Fn:Regular, App Contextual Menu/Right Click:Regular, Right Arrow:Regular, Left Arrow:Regular} Key/Button Diversion : {Calculator:Regular, Show Desktop:Diverted, Lock PC:Regular, Screen Capture/Print Screen:Regular, Brightness Down:Regular, Brightness Up:Regular, Host Switch Channel 1:Regular, Host Switch Channel 2:Diverted, Host Switch Channel 3:Diverted, Mission Control/Task View:Regular, Dashboard Launchpad/Action Center:Regular, Backlight Down:Regular, Backlight Up:Regular, Previous Fn:Regular, Play/Pause Fn:Regular, Next Fn:Regular, Mute Fn:Regular, Volume Down Fn:Regular, Volume Up Fn:Regular, App Contextual Menu/Right Click:Regular, Right Arrow:Regular, Left Arrow:Regular} 12: PERSISTENT REMAPPABLE ACTION {1C00} Persistent Key/Button Mapping : {Calculator:AL Calculator, Show Desktop:Meta+D, Lock PC:Meta+L, Screen Capture/Print Screen:SYSRQ, Brightness Down:Brightness Down, Brightness Up:Brightness Up, Mission Control/Task View:Meta+TAB, Dashboard Launchpad/Action Center:Meta+A, Backlight Down:No Output (only as default), Backlight Up:No Output (only as default), Previous Fn:Scan Previous Track, Play/Pause Fn:Play/Pause, Next Fn:Scan Next Track, Mute Fn:Mute, Volume Down Fn:No Output, Volume Up Fn:Volume Up, App Contextual Menu/Right Click:COMPOSE} 13: K375S FN INVERSION {40A3} Swap Fx function (saved): True Swap Fx function : True 14: ENCRYPTION {4100} 15: LOCK KEY STATE {4220} 16: KEYBOARD DISABLE KEYS {4521} Disable keys (saved): {Caps Lock:False, Num Lock:False, Scroll Lock:False, Insert:False, Win:False} Disable keys : {Caps Lock:False, Num Lock:False, Scroll Lock:False, Insert:False, Win:False} 17: MULTIPLATFORM {4531} Set OS (saved): Windows Set OS : Windows 18: CROWN {4600} Crown smooth scroll (saved): False Crown smooth scroll : False Divert crown events (saved): True Divert crown events : True 19: DFUCONTROL SIGNED {00C2} 20: unknown:1803 {1803} internal, hidden 21: CONFIG DEVICE PROPS {1806} internal, hidden 22: unknown:1813 {1813} internal, hidden 23: OOBSTATE {1805} internal, hidden 24: unknown:1830 {1830} internal, hidden 25: unknown:1890 {1890} internal, hidden 26: unknown:1891 {1891} internal, hidden 27: unknown:1801 {1801} internal, hidden 28: unknown:18A1 {18A1} internal, hidden 29: unknown:9280 {9280} internal, hidden 30: unknown:1A20 {1A20} internal, hidden 31: unknown:1DF3 {1DF3} internal, hidden 32: unknown:1E00 {1E00} hidden 33: unknown:1EB0 {1EB0} internal, hidden 34: unknown:1861 {1861} internal, hidden 35: unknown:18B0 {18B0} internal, hidden 36: unknown:92C0 {92C0} internal, hidden 37: unknown:9203 {9203} internal, hidden Has 24 reprogrammable keys: 0: Host Switch Channel 1 , default: HostSwitch Channel 1 => HostSwitch Channel 1 nonstandard, divertable, persistently divertable, pos:0, group:0, group mask:empty reporting: default 1: Host Switch Channel 2 , default: HostSwitch Channel 2 => HostSwitch Channel 2 nonstandard, divertable, persistently divertable, pos:0, group:0, group mask:empty reporting: diverted 2: Host Switch Channel 3 , default: HostSwitch Channel 3 => HostSwitch Channel 3 nonstandard, divertable, persistently divertable, pos:0, group:0, group mask:empty reporting: diverted 3: Brightness Down , default: Brightness Down => Brightness Down is FN, FN sensitive, reprogrammable, divertable, persistently divertable, pos:1, group:0, group mask:empty reporting: default 4: Brightness Up , default: Brightness Up => Brightness Up is FN, FN sensitive, reprogrammable, divertable, persistently divertable, pos:2, group:0, group mask:empty reporting: default 5: Mission Control/Task View , default: Mission Control/Task View => Mission Control/Task View is FN, FN sensitive, reprogrammable, divertable, persistently divertable, pos:3, group:0, group mask:empty reporting: default 6: Dashboard Launchpad/Action Center, default: Dashboard Launchpad/Action Center => Dashboard Launchpad/Action Center is FN, FN sensitive, reprogrammable, divertable, persistently divertable, pos:4, group:0, group mask:empty reporting: default 7: Show Desktop , default: Show Desktop => Show Desktop is FN, FN sensitive, reprogrammable, divertable, persistently divertable, pos:5, group:0, group mask:empty reporting: diverted 8: Backlight Down , default: Backlight Down => Backlight Down is FN, FN sensitive, reprogrammable, divertable, persistently divertable, pos:6, group:0, group mask:empty reporting: default 9: Backlight Up , default: Backlight Up => Backlight Up is FN, FN sensitive, reprogrammable, divertable, persistently divertable, pos:7, group:0, group mask:empty reporting: default 10: Previous Fn , default: Previous => Previous is FN, FN sensitive, reprogrammable, divertable, persistently divertable, pos:8, group:0, group mask:empty reporting: default 11: Play/Pause Fn , default: Play/Pause => Play/Pause is FN, FN sensitive, reprogrammable, divertable, persistently divertable, pos:9, group:0, group mask:empty reporting: default 12: Next Fn , default: Next => Next is FN, FN sensitive, reprogrammable, divertable, persistently divertable, pos:10, group:0, group mask:empty reporting: default 13: Mute Fn , default: Mute => Mute is FN, FN sensitive, reprogrammable, divertable, persistently divertable, pos:11, group:0, group mask:empty reporting: default 14: Volume Down Fn , default: Volume Down => Volume Down is FN, FN sensitive, reprogrammable, divertable, persistently divertable, pos:12, group:0, group mask:empty reporting: default 15: Volume Up Fn , default: Volume Up => Volume Up nonstandard, reprogrammable, divertable, persistently divertable, pos:0, group:0, group mask:empty reporting: default 16: Calculator , default: Calculator => Calculator nonstandard, reprogrammable, divertable, persistently divertable, pos:0, group:0, group mask:empty reporting: default 17: Screen Capture/Print Screen, default: Screen Capture => Screen Capture nonstandard, reprogrammable, divertable, persistently divertable, pos:0, group:0, group mask:empty reporting: default 18: App Contextual Menu/Right Click, default: Right Click/App Contextual Menu => Right Click/App Contextual Menu nonstandard, reprogrammable, divertable, persistently divertable, pos:0, group:0, group mask:empty reporting: default 19: Lock PC , default: WindowsLock => WindowsLock nonstandard, reprogrammable, divertable, persistently divertable, pos:0, group:0, group mask:empty reporting: default 20: Left Arrow , default: Keyboard Left Arrow => Keyboard Left Arrow nonstandard, divertable, persistently divertable, pos:0, group:0, group mask:empty reporting: default 21: Right Arrow , default: Keyboard Right Arrow => Keyboard Right Arrow nonstandard, divertable, persistently divertable, pos:0, group:0, group mask:empty reporting: default 22: F Lock , default: Do Nothing One => Do Nothing One is FN, pos:0, group:0, group mask:empty reporting: default 23: FN Key , default: Do Nothing One => Do Nothing One nonstandard, pos:0, group:0, group mask:empty reporting: default Has 17 persistent remappable keys: 0: Brightness Down => Consumer: Brightness Down (remapped) 1: Brightness Up => Consumer: Brightness Up (remapped) 2: Mission Control/Task View => Key: Meta+TAB 3: Dashboard Launchpad/Action Center => Key: Meta+A 4: Show Desktop => Key: Meta+D 5: Backlight Down => None 6: Backlight Up => None 7: Previous Fn => Consumer: Scan Previous Track 8: Play/Pause Fn => Consumer: Play/Pause 9: Next Fn => Consumer: Scan Next Track 10: Mute Fn => Consumer: Mute 11: Volume Down Fn => Key: No Output (remapped) 12: Volume Up Fn => Consumer: Volume Up 13: Calculator => Consumer: AL Calculator 14: Screen Capture/Print Screen => Key: SYSRQ 15: App Contextual Menu/Right Click => Key: COMPOSE 16: Lock PC => Key: Meta+L Battery: 50%, discharging, next level 20%. Solaar-1.1.14/docs/devices/Craft Advanced Keyboard B350.txt000066400000000000000000000272241473525213700230120ustar00rootroot00000000000000Solaar version 1.1.4 1: Craft Advanced Keyboard Device path : /dev/hidraw5 USB id : 046d:B350 Codename : Craft Kind : keyboard Protocol : HID++ 4.5 Serial number: Model ID: B35040660000 Unit ID: 43DAF041 Bootloader: BOT 41.01.B0015 Firmware: MPK 07.01.B0015 Other: Other: Supports 33 HID++ 2.0 features: 0: ROOT {0000} 1: FEATURE SET {0001} 2: DEVICE FW VERSION {0003} Firmware: Bootloader BOT 41.01.B0015 0000B6A2C54601 Firmware: Firmware MPK 07.01.B0015 B350B6A2C54601 Firmware: Other Firmware: Other Unit ID: 43DAF041 Model ID: B35040660000 Transport IDs: {'btleid': 'B350', 'wpid': '4066'} 3: DEVICE NAME {0005} Name: Craft Advanced Keyboard Kind: keyboard 4: WIRELESS DEVICE STATUS {1D4B} 5: RESET {0020} 6: DEVICE FRIENDLY NAME {0007} Friendly Name: Keyboard Craft 7: BATTERY STATUS {1000} Battery: 50%, discharging, next level 20%. 8: CHANGE HOST {1814} Change Host : 2:idefix 9: HOSTS INFO {1815} Host 0 (paired): idefix Host 1 (paired): idefix Host 2 (paired): Galaxy Tab S6 10: BACKLIGHT2 {1982} Backlight (saved): True Backlight : True 11: REPROG CONTROLS V4 {1B04} Key/Button Diversion (saved): {Calculator:Regular, Show Desktop:Diverted, Lock PC:Regular, Screen Capture/Print Screen:Regular, Brightness Down:Regular, Brightness Up:Regular, Host Switch Channel 1:Regular, Host Switch Channel 2:Diverted, Host Switch Channel 3:Diverted, Mission Control/Task View:Regular, Dashboard Launchpad/Action Center:Regular, Backlight Down:Regular, Backlight Up:Regular, Previous Fn:Regular, Play/Pause Fn:Regular, Next Fn:Regular, Mute Fn:Regular, Volume Down Fn:Regular, Volume Up Fn:Regular, App Contextual Menu/Right Click:Regular, Right Arrow:Regular, Left Arrow:Regular} Key/Button Diversion : {Calculator:Regular, Show Desktop:Diverted, Lock PC:Regular, Screen Capture/Print Screen:Regular, Brightness Down:Regular, Brightness Up:Regular, Host Switch Channel 1:Regular, Host Switch Channel 2:Diverted, Host Switch Channel 3:Diverted, Mission Control/Task View:Regular, Dashboard Launchpad/Action Center:Regular, Backlight Down:Regular, Backlight Up:Regular, Previous Fn:Regular, Play/Pause Fn:Regular, Next Fn:Regular, Mute Fn:Regular, Volume Down Fn:Regular, Volume Up Fn:Regular, App Contextual Menu/Right Click:Regular, Right Arrow:Regular, Left Arrow:Regular} 12: PERSISTENT REMAPPABLE ACTION {1C00} Persistent Key/Button Mapping : {Calculator:AL Calculator, Show Desktop:Meta+D, Lock PC:Meta+L, Screen Capture/Print Screen:SYSRQ, Brightness Down:No Output (only as default), Brightness Up:No Output (only as default), Mission Control/Task View:Meta+TAB, Dashboard Launchpad/Action Center:Meta+A, Backlight Down:No Output (only as default), Backlight Up:No Output (only as default), Previous Fn:Scan Previous Track, Play/Pause Fn:Play/Pause, Next Fn:Scan Next Track, Mute Fn:Mute, Volume Down Fn:Volume Down, Volume Up Fn:Volume Up, App Contextual Menu/Right Click:COMPOSE} 13: K375S FN INVERSION {40A3} Swap Fx function (saved): True Swap Fx function : True 14: ENCRYPTION {4100} 15: LOCK KEY STATE {4220} 16: KEYBOARD DISABLE KEYS {4521} Disable keys (saved): {Caps Lock:False, Num Lock:False, Scroll Lock:False, Insert:False, Win:False} Disable keys : {Caps Lock:False, Num Lock:False, Scroll Lock:False, Insert:False, Win:False} 17: MULTIPLATFORM {4531} Set OS (saved): Windows Set OS : Windows 18: CROWN {4600} Crown smooth scroll (saved): False Crown smooth scroll : False Divert crown events (saved): True Divert crown events : True 19: DFUCONTROL SIGNED {00C2} 20: unknown:1803 {1803} internal, hidden 21: unknown:1813 {1813} internal, hidden 22: unknown:1830 {1830} internal, hidden 23: unknown:1801 {1801} internal, hidden 24: unknown:18A1 {18A1} internal, hidden 25: unknown:1A20 {1A20} internal, hidden 26: unknown:1DF3 {1DF3} internal, hidden 27: unknown:1E00 {1E00} hidden 28: unknown:1EB0 {1EB0} internal, hidden 29: unknown:1861 {1861} internal, hidden 30: unknown:18B0 {18B0} internal, hidden 31: unknown:92C0 {92C0} internal, hidden 32: unknown:9203 {9203} internal, hidden Has 24 reprogrammable keys: 0: Host Switch Channel 1 , default: HostSwitch Channel 1 => HostSwitch Channel 1 nonstandard, divertable, persistently divertable, pos:0, group:0, group mask:empty reporting: default 1: Host Switch Channel 2 , default: HostSwitch Channel 2 => HostSwitch Channel 2 nonstandard, divertable, persistently divertable, pos:0, group:0, group mask:empty reporting: diverted 2: Host Switch Channel 3 , default: HostSwitch Channel 3 => HostSwitch Channel 3 nonstandard, divertable, persistently divertable, pos:0, group:0, group mask:empty reporting: diverted 3: Brightness Down , default: Brightness Down => Brightness Down is FN, FN sensitive, reprogrammable, divertable, persistently divertable, pos:1, group:0, group mask:empty reporting: default 4: Brightness Up , default: Brightness Up => Brightness Up is FN, FN sensitive, reprogrammable, divertable, persistently divertable, pos:2, group:0, group mask:empty reporting: default 5: Mission Control/Task View , default: Mission Control/Task View => Mission Control/Task View is FN, FN sensitive, reprogrammable, divertable, persistently divertable, pos:3, group:0, group mask:empty reporting: default 6: Dashboard Launchpad/Action Center, default: Dashboard Launchpad/Action Center => Dashboard Launchpad/Action Center is FN, FN sensitive, reprogrammable, divertable, persistently divertable, pos:4, group:0, group mask:empty reporting: default 7: Show Desktop , default: Show Desktop => Show Desktop is FN, FN sensitive, reprogrammable, divertable, persistently divertable, pos:5, group:0, group mask:empty reporting: diverted 8: Backlight Down , default: Backlight Down => Backlight Down is FN, FN sensitive, reprogrammable, divertable, persistently divertable, pos:6, group:0, group mask:empty reporting: default 9: Backlight Up , default: Backlight Up => Backlight Up is FN, FN sensitive, reprogrammable, divertable, persistently divertable, pos:7, group:0, group mask:empty reporting: default 10: Previous Fn , default: Previous => Previous is FN, FN sensitive, reprogrammable, divertable, persistently divertable, pos:8, group:0, group mask:empty reporting: default 11: Play/Pause Fn , default: Play/Pause => Play/Pause is FN, FN sensitive, reprogrammable, divertable, persistently divertable, pos:9, group:0, group mask:empty reporting: default 12: Next Fn , default: Next => Next is FN, FN sensitive, reprogrammable, divertable, persistently divertable, pos:10, group:0, group mask:empty reporting: default 13: Mute Fn , default: Mute => Mute is FN, FN sensitive, reprogrammable, divertable, persistently divertable, pos:11, group:0, group mask:empty reporting: default 14: Volume Down Fn , default: Volume Down => Volume Down is FN, FN sensitive, reprogrammable, divertable, persistently divertable, pos:12, group:0, group mask:empty reporting: default 15: Volume Up Fn , default: Volume Up => Volume Up nonstandard, reprogrammable, divertable, persistently divertable, pos:0, group:0, group mask:empty reporting: default 16: Calculator , default: Calculator => Calculator nonstandard, reprogrammable, divertable, persistently divertable, pos:0, group:0, group mask:empty reporting: default 17: Screen Capture/Print Screen, default: Screen Capture => Screen Capture nonstandard, reprogrammable, divertable, persistently divertable, pos:0, group:0, group mask:empty reporting: default 18: App Contextual Menu/Right Click, default: Right Click/App Contextual Menu => Right Click/App Contextual Menu nonstandard, reprogrammable, divertable, persistently divertable, pos:0, group:0, group mask:empty reporting: default 19: Lock PC , default: WindowsLock => WindowsLock nonstandard, reprogrammable, divertable, persistently divertable, pos:0, group:0, group mask:empty reporting: default 20: Left Arrow , default: Keyboard Left Arrow => Keyboard Left Arrow nonstandard, divertable, persistently divertable, pos:0, group:0, group mask:empty reporting: default 21: Right Arrow , default: Keyboard Right Arrow => Keyboard Right Arrow nonstandard, divertable, persistently divertable, pos:0, group:0, group mask:empty reporting: default 22: F Lock , default: Do Nothing One => Do Nothing One is FN, pos:0, group:0, group mask:empty reporting: default 23: FN Key , default: Do Nothing One => Do Nothing One nonstandard, pos:0, group:0, group mask:empty reporting: default Has 17 persistent remappable keys: 0: Brightness Down => None 1: Brightness Up => None 2: Mission Control/Task View => Key: Meta+TAB 3: Dashboard Launchpad/Action Center => Key: Meta+A 4: Show Desktop => Key: Meta+D 5: Backlight Down => None 6: Backlight Up => None 7: Previous Fn => Consumer: Scan Previous Track 8: Play/Pause Fn => Consumer: Play/Pause 9: Next Fn => Consumer: Scan Next Track 10: Mute Fn => Consumer: Mute 11: Volume Down Fn => Consumer: Volume Down 12: Volume Up Fn => Consumer: Volume Up 13: Calculator => Consumer: AL Calculator 14: Screen Capture/Print Screen => Key: SYSRQ 15: App Contextual Menu/Right Click => Key: COMPOSE 16: Lock PC => Key: Meta+L Battery: 50%, discharging, next level 20%. Solaar-1.1.14/docs/devices/ERGO M575 Trackball 4096.txt000066400000000000000000000106431473525213700215640ustar00rootroot00000000000000Solaar version 1.1.4 1: ERGO M575 Trackball Device path : /dev/hidraw5 WPID : 4096 Codename : ERGO M575 Kind : mouse Protocol : HID++ 4.5 Polling rate : 8 ms (125Hz) Serial number: 460DFDC2 Model ID: B02740960000 Unit ID: 460DFDC2 Bootloader: BOT 40.00.B0009 Firmware: MPM 26.00.B0009 Other: The power switch is located on the base. Supports 25 HID++ 2.0 features: 0: ROOT {0000} 1: FEATURE SET {0001} 2: DEVICE FW VERSION {0003} Firmware: Bootloader BOT 40.00.B0009 0000ABF1CB8B01 Firmware: Firmware MPM 26.00.B0009 4096ABF1CB8B01 Firmware: Other Unit ID: 460DFDC2 Model ID: B02740960000 Transport IDs: {'btleid': 'B027', 'wpid': '4096'} 3: DEVICE NAME {0005} Name: ERGO M575 Trackball Kind: trackball 4: WIRELESS DEVICE STATUS {1D4B} 5: RESET {0020} 6: DEVICE FRIENDLY NAME {0007} Friendly Name: ERGO M575 7: UNIFIED BATTERY {1004} 8: REPROG CONTROLS V4 {1B04} DPI Sliding Adjustment (saved): Off DPI Sliding Adjustment : Off Mouse Gestures (saved): Off Mouse Gestures : Off Key/Button Actions (saved): {Left Button:Left Click, Right Button:Right Click, Middle Button:Mouse Middle Button, Back Button:Mouse Back Button, Forward Button:Mouse Forward Button} Key/Button Actions : {Left Button:Left Click, Right Button:Right Click, Middle Button:Mouse Middle Button, Back Button:Mouse Back Button, Forward Button:Mouse Forward Button} Key/Button Diversion (saved): {Middle Button:Regular, Back Button:Regular, Forward Button:Regular} Key/Button Diversion : {Middle Button:Regular, Back Button:Regular, Forward Button:Regular} 9: ADJUSTABLE DPI {2201} Sensitivity (DPI) (saved): 500 Sensitivity (DPI) : 500 10: XY STATS {2250} 11: DFUCONTROL SIGNED {00C2} 12: DEVICE RESET {1802} internal, hidden 13: unknown:1803 {1803} internal, hidden 14: CONFIG DEVICE PROPS {1806} internal, hidden 15: unknown:1812 {1812} internal, hidden 16: OOBSTATE {1805} internal, hidden 17: unknown:1830 {1830} internal, hidden 18: unknown:1890 {1890} internal, hidden 19: unknown:1891 {1891} internal, hidden 20: unknown:18A1 {18A1} internal, hidden 21: unknown:1E00 {1E00} hidden 22: unknown:1EB0 {1EB0} internal, hidden 23: unknown:1861 {1861} internal, hidden 24: unknown:1E22 {1E22} internal, hidden Has 6 reprogrammable keys: 0: Left Button , default: Left Click => Left Click mse, reprogrammable, analytics key events, pos:0, group:1, group mask:g1 reporting: default 1: Right Button , default: Right Click => Right Click mse, reprogrammable, analytics key events, pos:0, group:1, group mask:g1 reporting: default 2: Middle Button , default: Mouse Middle Button => Mouse Middle Button mse, reprogrammable, divertable, persistently divertable, raw XY, analytics key events, pos:0, group:2, group mask:g1,g2 reporting: default 3: Forward Button , default: Mouse Forward Button => Mouse Forward Button mse, reprogrammable, divertable, persistently divertable, raw XY, analytics key events, pos:0, group:2, group mask:g1,g2 reporting: default 4: Back Button , default: Mouse Back Button => Mouse Back Button mse, reprogrammable, divertable, persistently divertable, raw XY, analytics key events, pos:0, group:2, group mask:g1,g2 reporting: default 5: Virtual Gesture Button , default: Virtual Gesture Button => Virtual Gesture Button divertable, virtual, raw XY, force raw XY, pos:0, group:3, group mask:empty reporting: default Battery: 85%, discharging. Solaar-1.1.14/docs/devices/EX100 Receiver 27 Mhz C517.text000066400000000000000000000157641473525213700221370ustar00rootroot00000000000000solaar version 1.1.11-80-gdea496f EX100 Receiver 27 Mhz Device path : /dev/hidraw2 USB id : 046d:C517 Serial : None Has 2 paired device(s) out of a maximum of 4. Notifications: wireless (0x000100) 1: Wireless Mouse EX100 Device path : /dev/hidraw3 WPID : 003F Codename : EX100m Kind : mouse Protocol : HID++ 1.0 Serial number: The power switch is located on the (unknown). Notifications: roller V, mouse extra buttons, battery status, roller H (0x3C0000). Battery: good, discharging. 3: Wireless Keyboard EX100 Device path : /dev/hidraw6 WPID : 0065 Codename : EX100 Kind : keyboard Protocol : HID++ 1.0 Serial number: The power switch is located on the (unknown). Notifications: keyboard multimedia raw, battery status (0x110000). Battery: good, discharging. Register Dump Notifications 0x00: 0x000100 Connection State 0x02: 0x000100 Device Activity 0xb3: None Pairing Register 0xb5 0x00: None Pairing Register 0xb5 0x01: None Pairing Register 0xb5 0x02: None Pairing Register 0xb5 0x03: None Pairing Register 0xb5 0x04: None Pairing Register 0xb5 0x05: None Pairing Register 0xb5 0x06: None Pairing Register 0xb5 0x07: None Pairing Register 0xb5 0x08: None Pairing Register 0xb5 0x09: None Pairing Register 0xb5 0x0a: None Pairing Register 0xb5 0x0b: None Pairing Register 0xb5 0x0c: None Pairing Register 0xb5 0x0d: None Pairing Register 0xb5 0x0e: None Pairing Register 0xb5 0x0f: None Pairing Register 0xb5 0x10: None Pairing Register 0xb5 0x20: None Pairing Register 0xb5 0x30: None Pairing Register 0xb5 0x50: None Pairing Name 0xb5 0x40: None Pairing Name 0xb5 0x60 0x1: 0 None Pairing Name 0xb5 0x60 0x2: 0 None Pairing Name 0xb5 0x60 0x3: 0 None Pairing Register 0xb5 0x11: None Pairing Register 0xb5 0x21: None Pairing Register 0xb5 0x31: None Pairing Register 0xb5 0x51: None Pairing Name 0xb5 0x41: None Pairing Name 0xb5 0x61 0x1: 0 None Pairing Name 0xb5 0x61 0x2: 0 None Pairing Name 0xb5 0x61 0x3: 0 None Pairing Register 0xb5 0x12: None Pairing Register 0xb5 0x22: None Pairing Register 0xb5 0x32: None Pairing Register 0xb5 0x52: None Pairing Name 0xb5 0x42: None Pairing Name 0xb5 0x62 0x1: 0 None Pairing Name 0xb5 0x62 0x2: 0 None Pairing Name 0xb5 0x62 0x3: 0 None Pairing Register 0xb5 0x13: None Pairing Register 0xb5 0x23: None Pairing Register 0xb5 0x33: None Pairing Register 0xb5 0x53: None Pairing Name 0xb5 0x43: None Pairing Name 0xb5 0x63 0x1: 0 None Pairing Name 0xb5 0x63 0x2: 0 None Pairing Name 0xb5 0x63 0x3: 0 None Pairing Register 0xb5 0x14: None Pairing Register 0xb5 0x24: None Pairing Register 0xb5 0x34: None Pairing Register 0xb5 0x54: None Pairing Name 0xb5 0x44: None Pairing Name 0xb5 0x64 0x1: 0 None Pairing Name 0xb5 0x64 0x2: 0 None Pairing Name 0xb5 0x64 0x3: 0 None Pairing Register 0xb5 0x15: None Pairing Register 0xb5 0x25: None Pairing Register 0xb5 0x35: None Pairing Register 0xb5 0x55: None Pairing Name 0xb5 0x45: None Pairing Name 0xb5 0x65 0x1: 0 None Pairing Name 0xb5 0x65 0x2: 0 None Pairing Name 0xb5 0x65 0x3: 0 None Pairing Register 0xb5 0x16: None Pairing Register 0xb5 0x26: None Pairing Register 0xb5 0x36: None Pairing Register 0xb5 0x56: None Pairing Name 0xb5 0x46: None Pairing Name 0xb5 0x66 0x1: 0 None Pairing Name 0xb5 0x66 0x2: 0 None Pairing Name 0xb5 0x66 0x3: 0 None Firmware 0xf1 0x00: None Firmware 0xf1 0x01: None Firmware 0xf1 0x02: None Firmware 0xf1 0x03: None Firmware 0xf1 0x04: None Register Short 0x00 0x00: 0x000100 Register Long 0x00 0x00: invalid SubID/command ... Register Long 0x00 0xfe: invalid SubID/command Register Short 0x01 0x00: 0x000200 Register Long 0x01 0x00: invalid SubID/command Register Long 0x01 0x01: invalid SubID/command Register Long 0x01 0x02: invalid SubID/command ... ./scan-registers.sh ff /dev/hidraw4 # Old notification flags: 000100 >> ( 0.035) [10 FF 8100 000100] '\x10\xff\x81\x00\x00\x01\x00' << ( 0.015) [10 FF 8101 000000] '\x10\xff\x81\x01\x00\x00\x00' >> ( 0.020) [10 FF 8101 000200] '\x10\xff\x81\x01\x00\x02\x00' << ( 0.030) [10 FF 8102 000000] '\x10\xff\x81\x02\x00\x00\x00' >> ( 0.036) [10 FF 8102 000200] '\x10\xff\x81\x02\x00\x02\x00' -- << ( 0.142) [10 FF 8109 000000] '\x10\xff\x81\t\x00\x00\x00' >> ( 0.148) [10 FF 8109 010000] '\x10\xff\x81\t\x01\x00\x00' -- << ( 1.790) [10 FF 8170 000000] '\x10\xff\x81p\x00\x00\x00' >> ( 1.796) [10 FF 8170 012100] '\x10\xff\x81p\x01!\x00' << ( 1.806) [10 FF 8171 000000] '\x10\xff\x81q\x00\x00\x00' >> ( 1.812) [10 FF 8171 011200] '\x10\xff\x81q\x01\x12\x00' -- << ( 1.838) [10 FF 8173 000000] '\x10\xff\x81s\x00\x00\x00' >> ( 1.844) [10 FF 8173 643F00] '\x10\xff\x81sd?\x00' -- << ( 2.046) [10 FF 8180 000000] '\x10\xff\x81\x80\x00\x00\x00' >> ( 2.052) [10 FF 8180 030000] '\x10\xff\x81\x80\x03\x00\x00' -- << ( 3.326) [10 FF 81D0 000000] '\x10\xff\x81\xd0\x00\x00\x00' >> ( 3.332) [10 FF 81D0 000000] '\x10\xff\x81\xd0\x00\x00\x00' devices 01 mouse Red button pressed >> ( 1676.106) [10 01 0810 000000] '\x10\x01\x08\x10\x00\x00\x00' >> ( 1676.114) [10 01 4600 000021] '\x10\x01F\x00\x00\x00!' >> ( 1676.122) [10 FF 4600 211100] '\x10\xffF\x00!\x11\x00' Power lewel? ?? Input: 10 01 81 07 00 00 00 << ( 1739.032) [10 01 8107 000000] '\x10\x01\x81\x07\x00\x00\x00' >> ( 1739.040) [10 01 8107 030000] '\x10\x01\x81\x07\x03\x00\x00' [10 01 8107 070000] '\x10\x01\x81\x07\x07\x00\x00' power change >> ( 2441.563) [10 01 0703 000000] '\x10\x01\x07\x03\x00\x00\x00' >> ( 100.159) [10 01 0707 000000] '\x10\x01\x07\x07\x00\x00\x00' enable power event << ( 59.190) [10 01 8000 100000] '\x10\x01\x80\x00\x10\x00\x00' >> ( 59.193) [10 01 8000 000000] '\x10\x01\x80\x00\x00\x00\x00' 03 keyboard Power level? ?? Input: 10 03 81 07 00 00 00 << ( 1777.961) [10 03 8107 000000] '\x10\x03\x81\x07\x00\x00\x00' >> ( 1777.967) [10 03 8107 070000] '\x10\x03\x81\x07\x07\x00\x00' power on >> ( 1571.805) [10 03 0810 000000] '\x10\x03\x08\x10\x00\x00\x00' >> ( 1574.709) [10 03 0800 000000] '\x10\x03\x08\x00\x00\x00\x00' red button pressed >> ( 1619.043) [10 03 0810 000000] '\x10\x03\x08\x10\x00\x00\x00' >> ( 1619.051) [10 03 4600 000011] '\x10\x03F\x00\x00\x00\x11' >> ( 1619.059) [10 FF 4600 221100] '\x10\xffF\x00"\x11\x00' >> ( 1621.747) [10 03 0800 000000] '\x10\x03\x08\x00\x00\x00\x00' Fn pressed >> ( 1651.715) [10 03 032C 100000] '\x10\x03\x03,\x10\x00\x00' >> ( 1652.170) [10 03 0300 000000] '\x10\x03\x03\x00\x00\x00\x00' Battery status: 1.9V critical 2.3V low 2.5V full Solaar-1.1.14/docs/devices/G Pro Wireless Gaming Mouse 4079.txt000066400000000000000000000051751473525213700234600ustar00rootroot00000000000000Solaar version 1.1.4rc1 1: G Pro Wireless Gaming Mouse Device path : /dev/hidraw10 WPID : 4079 Codename : G Pro Kind : mouse Protocol : HID++ 4.2 Polling rate : 2 ms (500Hz) Serial number: 40B217C0 Model ID: 4079C0880000 Unit ID: 40B217C0 Bootloader: BOT 74.02.B0026 Firmware: MPM 15.02.B0026 Other: The power switch is located on the base. Supports 28 HID++ 2.0 features: 0: ROOT {0000} 1: FEATURE SET {0001} 2: DEVICE FW VERSION {0003} Firmware: Bootloader BOT 74.02.B0026 AADF85EE714A Firmware: Firmware MPM 15.02.B0026 407985EE714A Firmware: Other Unit ID: 40B217C0 Model ID: 4079C0880000 Transport IDs: {'wpid': '4079', 'usbid': 'C088'} 3: DEVICE NAME {0005} Name: G Pro Wireless Gaming Mouse Kind: mouse 4: WIRELESS DEVICE STATUS {1D4B} 5: RESET {0020} 6: BATTERY VOLTAGE {1001} Battery: 3926mV, discharging, 70. 7: COLOR LED EFFECTS {8070} 8: LED CONTROL {1300} 9: ONBOARD PROFILES {8100} Device Mode: On-Board Onboard Profiles (saved): Enable Onboard Profiles : Enable 10: MOUSE BUTTON SPY {8110} 11: REPORT RATE {8060} Polling Rate (ms): 2 Polling Rate (ms) (saved): 2 Polling Rate (ms) : 2 12: ADJUSTABLE DPI {2201} Sensitivity (DPI) (saved): 1600 Sensitivity (DPI) : 1600 13: DEVICE RESET {1802} internal, hidden 14: unknown:1803 {1803} internal, hidden 15: OOBSTATE {1805} internal, hidden 16: CONFIG DEVICE PROPS {1806} internal, hidden 17: unknown:1811 {1811} internal, hidden 18: unknown:1830 {1830} internal, hidden 19: unknown:1890 {1890} internal, hidden 20: unknown:1891 {1891} internal, hidden 21: unknown:18A1 {18A1} internal, hidden 22: unknown:1801 {1801} internal, hidden 23: unknown:18B1 {18B1} internal, hidden 24: unknown:1DF3 {1DF3} internal, hidden 25: unknown:1E00 {1E00} hidden 26: unknown:1EB0 {1EB0} internal, hidden 27: unknown:1863 {1863} internal, hidden Battery: 3926mV, discharging, 70. Solaar-1.1.14/docs/devices/G213 Prodigy Gaming Keyboard C366.txt000066400000000000000000000030351473525213700234640ustar00rootroot00000000000000Solaar version 1.1.5rc1 USB and Bluetooth Devices 1: G213 Prodigy Gaming Keyboard Device path : /dev/hidraw2 USB id : 046d:C336 Codename : G213 Kind : keyboard Protocol : HID++ 4.2 Polling rate : 1 ms (1000Hz) Serial number: Model ID: C33600000000 Unit ID: 31314709 Firmware: U1 09.00.B0006 Bootloader: BOT 31.00.B0002 Supports 15 HID++ 2.0 features: 0: ROOT {0000} V0 1: FEATURE SET {0001} V0 2: DEVICE FW VERSION {0003} V2 Firmware: Firmware U1 09.00.B0006 C336 Firmware: Bootloader BOT 31.00.B0002 AAC8 Unit ID: 31314709 Model ID: C33600000000 Transport IDs: {'usbid': 'C336'} 3: KEYBOARD DISABLE BY USAGE {4522} V0 4: DEVICE NAME {0005} V0 Name: Gaming Keyboard G213 Kind: keyboard 5: unknown:1E00 {1E00} V0 hidden 6: KEYBOARD LAYOUT 2 {4540} V0 7: unknown:1EB0 {1EB0} V0 internal, hidden 8: REPORT RATE {8060} V0 Polling Rate (ms): 1 Polling Rate (ms) : 1 9: DFUCONTROL SIGNED {00C2} V0 10: unknown:1801 {1801} V0 internal, hidden 11: DEVICE RESET {1802} V0 internal, hidden 12: COLOR LED EFFECTS {8070} V5 13: unknown:1821 {1821} V0 14: REPORT HID USAGE {1BC0} V0 Battery: N/A, None. Solaar-1.1.14/docs/devices/G304 Lightspeed Wireless Gaming Mouse 4074.txt000066400000000000000000000051761473525213700252330ustar00rootroot00000000000000solaar version 1.1.8 1: G304 Lightspeed Wireless Gaming Mouse Device path : /dev/hidraw6 WPID : 4074 Codename : G304 Kind : mouse Protocol : HID++ 4.2 Polling rate : 8 ms (125Hz) Serial number: B2D05D23 Model ID: 407400000000 Unit ID: EB490C63 Bootloader: BOT 69.02.B0021 Firmware: RQM 68.02.B0021 The power switch is located on the base. Supports 27 HID++ 2.0 features: 0: ROOT {0000} V0 1: FEATURE SET {0001} V0 2: DEVICE FW VERSION {0003} V2 Firmware: Bootloader BOT 69.02.B0021 4074452F3940 Firmware: Firmware RQM 68.02.B0021 4074452F3940 Unit ID: EB490C63 Model ID: 407400000000 Transport IDs: {'wpid': '4074'} 3: DEVICE NAME {0005} V0 Name: G304 Lightspeed Wireless Gaming Mouse Kind: mouse 4: WIRELESS DEVICE STATUS {1D4B} V0 5: BATTERY STATUS {1000} V0 Battery: 90%, discharging, next level 50%. 6: COLOR LED EFFECTS {8070} V6 7: ONBOARD PROFILES {8100} V0 Device Mode: Host Onboard Profiles (saved): Disable Onboard Profiles : Disable 8: MOUSE BUTTON SPY {8110} V0 9: REPORT RATE {8060} V0 Polling Rate (ms): 8 Polling Rate (ms) (saved): 8 Polling Rate (ms) : 8 10: MODE STATUS {8090} V1 11: DFUCONTROL SIGNED {00C2} V0 12: DEVICE RESET {1802} V0 internal, hidden 13: unknown:1803 {1803} V0 internal, hidden 14: CONFIG DEVICE PROPS {1806} V4 internal, hidden 15: unknown:1811 {1811} V0 internal, hidden 16: OOBSTATE {1805} V0 internal, hidden 17: unknown:1830 {1830} V0 internal, hidden 18: unknown:1890 {1890} V0 internal, hidden 19: unknown:1DF3 {1DF3} V0 internal, hidden 20: unknown:1E00 {1E00} V0 hidden 21: unknown:1EB0 {1EB0} V0 internal, hidden 22: unknown:1861 {1861} V0 internal, hidden 23: unknown:18B1 {18B1} V0 internal, hidden 24: unknown:1E22 {1E22} V0 internal, hidden 25: unknown:1801 {1801} V0 internal, hidden 26: ADJUSTABLE DPI {2201} V1 Sensitivity (DPI) (saved): 2200 Sensitivity (DPI) : 2200 Battery: 90%, discharging, next level 50%. Solaar-1.1.14/docs/devices/G305 Lightspeed Wireless Gaming Mouse 4074.text000066400000000000000000000052011473525213700253660ustar00rootroot00000000000000solaar version 1.1.10 1: G305 Lightspeed Wireless Gaming Mouse Device path : /dev/hidraw7 WPID : 4074 Codename : G305 Kind : mouse Protocol : HID++ 4.2 Polling rate : 1 ms (1000Hz) Serial number: ED5E9515 Model ID: 407400000000 Unit ID: F074D567 Bootloader: BOT 69.02.B0021 Firmware: RQM 68.02.B0021 The power switch is located on the base. Supports 27 HID++ 2.0 features: 0: ROOT {0000} V0 1: FEATURE SET {0001} V0 2: DEVICE FW VERSION {0003} V2 Firmware: Bootloader BOT 69.02.B0021 4074452F3940 Firmware: Firmware RQM 68.02.B0021 4074452F3940 Unit ID: F074D567 Model ID: 407400000000 Transport IDs: {'wpid': '4074'} 3: DEVICE NAME {0005} V0 Name: G305 Lightspeed Wireless Gaming Mouse Kind: mouse 4: WIRELESS DEVICE STATUS {1D4B} V0 5: BATTERY STATUS {1000} V0 Battery: 50%, discharging, next level 30%. 6: COLOR LED EFFECTS {8070} V6 7: ONBOARD PROFILES {8100} V0 Device Mode: On-Board Onboard Profiles (saved): Enable Onboard Profiles : Enable 8: MOUSE BUTTON SPY {8110} V0 9: REPORT RATE {8060} V0 Polling Rate (ms): 1 Polling Rate (ms) (saved): 1 Polling Rate (ms) : 1 10: MODE STATUS {8090} V1 11: DFUCONTROL SIGNED {00C2} V0 12: DEVICE RESET {1802} V0 internal, hidden 13: unknown:1803 {1803} V0 internal, hidden 14: CONFIG DEVICE PROPS {1806} V4 internal, hidden 15: unknown:1811 {1811} V0 internal, hidden 16: OOBSTATE {1805} V0 internal, hidden 17: unknown:1830 {1830} V0 internal, hidden 18: unknown:1890 {1890} V0 internal, hidden 19: unknown:1DF3 {1DF3} V0 internal, hidden 20: unknown:1E00 {1E00} V0 hidden 21: unknown:1EB0 {1EB0} V0 internal, hidden 22: unknown:1861 {1861} V0 internal, hidden 23: unknown:18B1 {18B1} V0 internal, hidden 24: unknown:1E22 {1E22} V0 internal, hidden 25: unknown:1801 {1801} V0 internal, hidden 26: ADJUSTABLE DPI {2201} V1 Sensitivity (DPI) (saved): 1600 Sensitivity (DPI) : 1600 Battery: 50%, discharging, next level 30%. Solaar-1.1.14/docs/devices/G502 Gaming Mouse C07D.text000066400000000000000000000036041473525213700216020ustar00rootroot00000000000000Solaar version 1.1.5 1: G502 Gaming Mouse Device path : /dev/hidraw1 USB id : 046d:C07D Codename : G502 Kind : mouse Protocol : HID++ 4.2 Polling rate : 1 ms (1000Hz) Serial number: Model ID: 000000000000 Unit ID: 00000000 Firmware: U 88.02.B0017 Bootloader: BOT 14.00.B0007 Hardware: 72 Other: Supports 19 HID++ 2.0 features: 0: ROOT {0000} V0 1: FEATURE SET {0001} V0 2: FEATURE INFO {0002} V0 3: DEVICE FW VERSION {0003} V0 Firmware: Firmware U 88.02.B0017 Firmware: Bootloader BOT 14.00.B0007 Firmware: Hardware 72 Firmware: Other Unit ID: 00000000 Model ID: 000000000000 Transport IDs: {} 4: DEVICE NAME {0005} V0 Name: Tunable FPS Gaming Mouse G502 Kind: mouse 5: LED CONTROL {1300} V0 6: unknown:18A1 {18A1} V0 internal, hidden 7: unknown:1E00 {1E00} V0 hidden 8: unknown:1E20 {1E20} V0 9: unknown:1EB0 {1EB0} V0 internal, hidden 10: ADJUSTABLE DPI {2201} V0 Sensitivity (DPI) : 800 11: ANGLE SNAPPING {2230} V0 12: SURFACE TUNING {2240} V0 13: REPORT RATE {8060} V0 Polling Rate (ms): 1 Polling Rate (ms) : 1 14: ONBOARD PROFILES {8100} V0 Device Mode: Host Onboard Profiles : Disable 15: MOUSE BUTTON SPY {8110} V0 16: unknown:1850 {1850} V0 internal, hidden 17: DFUCONTROL UNSIGNED {00C1} V0 18: unknown:1801 {1801} V0 internal, hidden Battery: N/A, None. Solaar-1.1.14/docs/devices/G502 Lightspeed Wireless Gaming Mouse 407F.txt000066400000000000000000000071621473525213700252520ustar00rootroot00000000000000solaar version 1.1.12rc1 1: G502 Gaming Mouse Device path : /dev/hidraw20 WPID : 407F Codename : G502 Kind : mouse Protocol : HID++ 4.2 Report Rate : 1ms Serial number: DDDAADBC Model ID: 407FC08D0000 Unit ID: DDDAADBC 1: BOT 92.00.B0008 0: MPM 17.00.B0008 3: The power switch is located on the base. Supports 30 HID++ 2.0 features: 0: ROOT {0000} V0 1: FEATURE SET {0001} V0 2: DEVICE FW VERSION {0003} V2 Firmware: Bootloader BOT 92.00.B0008 AAEF21F1FA5F Firmware: Firmware MPM 17.00.B0008 407F21F1FA5F Firmware: Other Unit ID: DDDAADBC Model ID: 407FC08D0000 Transport IDs: {'wpid': '407F', 'usbid': 'C08D'} 3: DEVICE NAME {0005} V0 Name: G502 LIGHTSPEED Wireless Gaming Mouse Kind: mouse 4: WIRELESS DEVICE STATUS {1D4B} V0 5: CONFIG CHANGE {0020} V0 Configuration: 11000000000000000000000000000000 6: BATTERY VOLTAGE {1001} V2 Battery: 90% 4166mV , discharging. 7: COLOR LED EFFECTS {8070} V4 LED Control (saved): Device LED Control : Device LEDs Primary (saved): !LEDEffectSetting {ID: 1, color: 16711680, intensity: 0, period: 100, ramp: 0, speed: 0} LEDs Primary : None LEDs Logo : None 8: LED CONTROL {1300} V0 9: ONBOARD PROFILES {8100} V0 Device Mode: On-Board Onboard Profiles (saved): Profile 1 Onboard Profiles : Profile 1 10: MOUSE BUTTON SPY {8110} V0 11: REPORT RATE {8060} V0 Report Rate: 1ms Report Rate (saved): 1ms Report Rate : 1ms 12: ADJUSTABLE DPI {2201} V1 Sensitivity (DPI) (saved): 900 Sensitivity (DPI) : 900 13: DEVICE RESET {1802} V0 internal, hidden 14: unknown:1803 {1803} V0 internal, hidden 15: OOBSTATE {1805} V0 internal, hidden 16: CONFIG DEVICE PROPS {1806} V4 internal, hidden 17: unknown:1811 {1811} V0 internal, hidden 18: unknown:1830 {1830} V0 internal, hidden 19: unknown:1890 {1890} V4 internal, hidden 20: unknown:1891 {1891} V4 internal, hidden 21: unknown:18A1 {18A1} V0 internal, hidden 22: unknown:1801 {1801} V0 internal, hidden 23: unknown:18B1 {18B1} V0 internal, hidden 24: unknown:1DF3 {1DF3} V0 internal, hidden 25: unknown:1E00 {1E00} V0 hidden 26: unknown:1EB0 {1EB0} V0 internal, hidden 27: unknown:1863 {1863} V0 internal, hidden 28: unknown:1E22 {1E22} V0 internal, hidden 29: HIRES WHEEL {2121} V0 Multiplier: 8 Has invert: Normal wheel motion Has ratchet switch: Normal wheel mode High resolution mode HID notification Scroll Wheel Direction (saved): False Scroll Wheel Direction : False Scroll Wheel Resolution (saved): True Scroll Wheel Resolution : True Scroll Wheel Diversion (saved): False Scroll Wheel Diversion : False Battery: 90% 4166mV , discharging. Solaar-1.1.14/docs/devices/G502 Proteus Spectrum Optical Mouse C332.txt000066400000000000000000000032241473525213700250060ustar00rootroot00000000000000solaar version 1.1.9 2: G502 Proteus Spectrum Optical Mouse Device path : /dev/hidraw4 USB id : 046d:C332 Codename : G502 Proteus Spectrum Kind : mouse Protocol : HID++ 4.2 Polling rate : 1 ms (1000Hz) Serial number: Model ID: C33200000000 Unit ID: 31374706 Firmware: U1 03.02.B0012 Bootloader: BOT 14.00.B0007 Supports 20 HID++ 2.0 features: 0: ROOT {0000} V0 1: FEATURE SET {0001} V0 2: COLOR LED EFFECTS {8070} V3 3: DEVICE FW VERSION {0003} V1 Firmware: Firmware U1 03.02.B0012 C332 Firmware: Bootloader BOT 14.00.B0007 AABF Unit ID: 31374706 Model ID: C33200000000 Transport IDs: {'usbid': 'C332'} 4: DEVICE NAME {0005} V0 Name: Tunable RGB Gaming Mouse G502 Kind: mouse 5: LED CONTROL {1300} V0 6: unknown:18A1 {18A1} V0 internal, hidden 7: unknown:1E00 {1E00} V0 hidden 8: unknown:1E20 {1E20} V0 9: unknown:1EB0 {1EB0} V0 internal, hidden 10: ADJUSTABLE DPI {2201} V1 Sensitivity (DPI) (saved): 7000 Sensitivity (DPI) : 7000 11: ANGLE SNAPPING {2230} V0 12: SURFACE TUNING {2240} V0 13: REPORT RATE {8060} V0 Polling Rate (ms): 1 Polling Rate (ms) (saved): 1 Polling Rate (ms) : 1 14: ONBOARD PROFILES {8100} V0 Device Mode: On-Board Onboard Profiles (saved): Enable Onboard Profiles : Enable 15: MOUSE BUTTON SPY {8110} V0 16: unknown:1850 {1850} V0 internal, hidden 17: DFUCONTROL UNSIGNED {00C1} V0 18: unknown:1801 {1801} V0 internal, hidden 19: DEVICE RESET {1802} V0 internal, hidden Battery status unavailable. Solaar-1.1.14/docs/devices/G502 SE Hero Gaming Mouse C08B.txt000066400000000000000000000035141473525213700226020ustar00rootroot000000000000001: G502 SE Hero Gaming Mouse Device path : /dev/hidraw7 USB id : 046d:C08B Codename : G502 Hero Kind : mouse Protocol : HID++ 4.2 Polling rate : 1 ms (1000Hz) Serial number: Model ID: C08B00000000 Unit ID: 30324703 Firmware: U1 27.03.B0010 Bootloader: BOT 81.00.B0002 Supports 19 HID++ 2.0 features: 0: ROOT {0000} 1: FEATURE SET {0001} 2: COLOR LED EFFECTS {8070} 3: DEVICE FW VERSION {0003} Firmware: Firmware U1 27.03.B0010 C08B Firmware: Bootloader BOT 81.00.B0002 AAE6 Unit ID: 30324703 Model ID: C08B00000000 Transport IDs: {'usbid': 'C08B'} 4: DEVICE NAME {0005} Name: G502 HERO Gaming Mouse Kind: mouse 5: LED CONTROL {1300} 6: unknown:18A1 {18A1} internal, hidden 7: unknown:1E00 {1E00} hidden 8: unknown:1E22 {1E22} internal, hidden 9: unknown:1EB0 {1EB0} internal, hidden 10: ADJUSTABLE DPI {2201} Sensitivity (DPI) (saved): 2400 Sensitivity (DPI) : 2400 11: REPORT RATE {8060} Polling Rate (ms): 1 Polling Rate (ms) (saved): 1 Polling Rate (ms) : 1 12: ONBOARD PROFILES {8100} Device Mode: Host 13: MOUSE BUTTON SPY {8110} 14: DFUCONTROL SIGNED {00C2} 15: unknown:1801 {1801} internal, hidden 16: DEVICE RESET {1802} internal, hidden 17: CONFIG DEVICE PROPS {1806} internal, hidden 18: unknown:18B1 {18B1} internal, hidden Battery status unavailable. Solaar-1.1.14/docs/devices/G502 X C099.txt000066400000000000000000000054201473525213700173560ustar00rootroot00000000000000Solaar version 1.1.7 1: G502 X Device path : /dev/hidraw2 USB id : 046d:C099 Codename : G502 Kind : mouse Protocol : HID++ 4.2 Polling rate : 1 ms (1000Hz) Serial number: Model ID: C09900000000 Unit ID: 42004C00 Bootloader: BL1 59.00.B0002 Firmware: U1 60.00.B0009 Supports 22 HID++ 2.0 features: 0: ROOT {0000} V0 1: FEATURE SET {0001} V0 2: DEVICE FW VERSION {0003} V4 Firmware: Bootloader BL1 59.00.B0002 AB17 Firmware: Firmware U1 60.00.B0009 C099 Unit ID: 42004C00 Model ID: C09900000000 Transport IDs: {'usbid': 'C099'} 3: DEVICE NAME {0005} V0 Name: G502 X Kind: mouse 4: RESET {0020} V0 5: DFUCONTROL SIGNED {00C2} V0 6: unknown:1801 {1801} V0 internal, hidden, unknown:000010 7: DEVICE RESET {1802} V0 internal, hidden, unknown:000010 8: OOBSTATE {1805} V0 internal, hidden 9: CONFIG DEVICE PROPS {1806} V8 internal, hidden, unknown:000010 10: unknown:1875 {1875} V0 internal, hidden, unknown:000010 11: unknown:18A1 {18A1} V0 internal, hidden, unknown:000010 12: unknown:18B1 {18B1} V0 internal, hidden, unknown:000010 13: unknown:18C0 {18C0} V0 internal, hidden, unknown:000010 14: unknown:1E00 {1E00} V0 hidden 15: unknown:1E22 {1E22} V0 internal, hidden, unknown:000010 16: unknown:1EB0 {1EB0} V0 internal, hidden, unknown:000010 17: HIRES WHEEL {2121} V1 Multiplier: 8 Has invert: Normal wheel motion Has ratchet switch: Normal wheel mode High resolution mode HID notification Scroll Wheel Direction (saved): False Scroll Wheel Direction : False Scroll Wheel Resolution (saved): True Scroll Wheel Resolution : True Scroll Wheel Diversion (saved): False Scroll Wheel Diversion : False 18: ADJUSTABLE DPI {2201} V2 Sensitivity (DPI) (saved): 1600 Sensitivity (DPI) : 1600 19: REPORT RATE {8060} V0 Polling Rate (ms): 1 Polling Rate (ms) (saved): 1 Polling Rate (ms) : 1 20: ONBOARD PROFILES {8100} V0 Device Mode: On-Board Onboard Profiles (saved): Enable Onboard Profiles : Enable 21: MOUSE BUTTON SPY {8110} V0 Battery status unavailable. Solaar-1.1.14/docs/devices/G535 Wireless Gaming Headset 0AC4.txt000066400000000000000000000020011473525213700234660ustar00rootroot00000000000000solaar version 1.1.8 USB and Bluetooth Devices 1: G535 Wireless Gaming Headset Device path : /dev/hidraw2 USB id : 046d:0AC4 Codename : G535 Kind : ? Protocol : HID++ 4.2 Serial number: Model ID: 000000000AC4 Unit ID: FFFFFFFF Firmware: U1 90.00.B0200 Supports 6 HID++ 2.0 features: 0: ROOT {0000} V0 1: FEATURE SET {0001} V0 2: DEVICE FW VERSION {0003} V2 Firmware: Firmware U1 90.00.B0200 0AC4 Unit ID: FFFFFFFF Model ID: 000000000AC4 Transport IDs: {'btid': '0000', 'btleid': '0000'} 3: DEVICE NAME {0005} V0 Name: G535 Wireless Gaming Headset Kind: None 4: SIDETONE {8300} V0 Sidetone (saved): 0 Sidetone : 0 5: ADC MEASUREMENT {1F20} V0 Battery: 60% 3920mV , discharging. Battery: 60% 3920mV , discharging. Solaar-1.1.14/docs/devices/G600 Gaming Mouse C24A.txt000066400000000000000000000147251473525213700214360ustar00rootroot00000000000000This mouse does not use HID++ [root@gpiro device]# lsusb -vv -d 046d:c24a Bus 003 Device 002: ID 046d:c24a Logitech, Inc. G600 Gaming Mouse Device Descriptor: bLength 18 bDescriptorType 1 bcdUSB 2.00 bDeviceClass 0 bDeviceSubClass 0 bDeviceProtocol 0 bMaxPacketSize0 32 idVendor 0x046d Logitech, Inc. idProduct 0xc24a G600 Gaming Mouse bcdDevice 77.02 iManufacturer 1 Logitech iProduct 2 Gaming Mouse G600 iSerial 3 385226BFEFD20017 bNumConfigurations 1 Configuration Descriptor: bLength 9 bDescriptorType 2 wTotalLength 0x003b bNumInterfaces 2 bConfigurationValue 1 iConfiguration 4 U77.02_B0017 bmAttributes 0xa0 (Bus Powered) Remote Wakeup MaxPower 500mA Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 0 bAlternateSetting 0 bNumEndpoints 1 bInterfaceClass 3 Human Interface Device bInterfaceSubClass 1 Boot Interface Subclass bInterfaceProtocol 2 Mouse iInterface 0 HID Device Descriptor: bLength 9 bDescriptorType 33 bcdHID 1.11 bCountryCode 0 Not supported bNumDescriptors 1 bDescriptorType 34 Report wDescriptorLength 67 Report Descriptors: ** UNAVAILABLE ** Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x81 EP 1 IN bmAttributes 3 Transfer Type Interrupt Synch Type None Usage Type Data wMaxPacketSize 0x0009 1x 9 bytes bInterval 1 Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 1 bAlternateSetting 0 bNumEndpoints 1 bInterfaceClass 3 Human Interface Device bInterfaceSubClass 0 bInterfaceProtocol 1 Keyboard iInterface 0 HID Device Descriptor: bLength 9 bDescriptorType 33 bcdHID 1.11 bCountryCode 0 Not supported bNumDescriptors 1 bDescriptorType 34 Report wDescriptorLength 135 Report Descriptors: ** UNAVAILABLE ** Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x82 EP 2 IN bmAttributes 3 Transfer Type Interrupt Synch Type None Usage Type Data wMaxPacketSize 0x0020 1x 32 bytes bInterval 1 can't get device qualifier: Resource temporarily unavailable can't get debug descriptor: Resource temporarily unavailable Device Status: 0x0000 (Bus Powered) /sys/devices/pci0000:00/0000:00:08.1/0000:05:00.4/usb3/3-2/3-2:1.1/0003:046D:C24A.0007/report_descriptor Usage Page (Desktop), ; Generic desktop controls (01h) Usage (Keyboard), ; Keyboard (06h, application collection) Collection (Application), Report ID (1), Usage Page (Keyboard), ; Keyboard/keypad (07h) Usage Minimum (KB Leftcontrol), ; Keyboard left control (E0h, dynamic value) Usage Maximum (KB Right GUI), ; Keyboard right GUI (E7h, dynamic value) Logical Minimum (0), Logical Maximum (1), Report Size (1), Report Count (8), Input (Variable), Report Size (8), Report Count (5), Logical Minimum (0), Logical Maximum (164), Usage Minimum (None), ; No event (00h, selector) Usage Maximum (KB ExSel), ; Keyboard ExSel (A4h, selector) Input, End Collection, Usage Page (FF80h), ; FF80h, vendor-defined Usage (80h), Collection (Application), Report ID (128), Usage (80h), Report Size (8), Report Count (5), Input (Variable), Report ID (246), Usage (F6h), Report Size (8), Report Count (7), Input (Variable), Report ID (240), Usage (F0h), Report Count (3), Feature (Variable), Report ID (241), Usage (F1h), Report Count (7), Feature (Variable), Report ID (242), Usage (F2h), Report Count (4), Feature (Variable), Report ID (243), Usage (F3h), Report Count (153), Feature (Variable), Report ID (244), Usage (F4h), Report Count (153), Feature (Variable), Report ID (245), Usage (F5h), Report Count (153), Feature (Variable), Report ID (246), Usage (F6h), Report Count (7), Feature (Variable), Report ID (247), Usage (F7h), Report Size (8), Report Count (31), Input (Variable), End Collection /sys/devices/pci0000:00/0000:00:08.1/0000:05:00.4/usb3/3-2/3-2:1.0/0003:046D:C24A.0006/report_descriptor Usage Page (Desktop), ; Generic desktop controls (01h) Usage (Mouse), ; Mouse (02h, application collection) Collection (Application), Usage (Pointer), ; Pointer (01h, physical collection) Collection (Physical), Usage Page (Button), ; Button (09h) Usage Minimum (01h), Usage Maximum (10h), Logical Minimum (0), Logical Maximum (1), Report Size (1), Report Count (16), Input (Variable), Usage Page (Desktop), ; Generic desktop controls (01h) Usage (X), ; X (30h, dynamic value) Usage (Y), ; Y (31h, dynamic value) Logical Minimum (-32767), Logical Maximum (32767), Report Size (16), Report Count (2), Input (Variable, Relative), Usage (Wheel), ; Wheel (38h, dynamic value) Report Size (8), Report Count (1), Logical Minimum (-127), Logical Maximum (127), Input (Variable, Relative), Usage Page (Consumer), ; Consumer (0Ch) Usage (AC Pan), ; AC pan (0238h, linear control) Report Count (1), Input (Variable, Relative), End Collection, End Collection Solaar-1.1.14/docs/devices/G613 Wireless Mechanical Gaming Keyboard 4065.txt000066400000000000000000000067001473525213700256340ustar00rootroot00000000000000Solaar version 1.1.3 1: G613 Wireless Mechanical Gaming Keyboard Device path : None WPID : 4065 Codename : G613 Kind : keyboard Protocol : HID++ 4.2 Polling rate : 1 ms (1000Hz) Serial number: 0DBC5FF6 Model ID: B34F40650000 Unit ID: 9AAB3225 Bootloader: BOT 46.00.B0006 Firmware: MPK 05.02.B0021 Other: Supports 32 HID++ 2.0 features: 0: ROOT {0000} 1: FEATURE SET {0001} 2: DEVICE FW VERSION {0003} Firmware: Bootloader BOT 46.00.B0006 00006E86A7BD Firmware: Firmware MPK 05.02.B0021 40656E86A7BD Firmware: Other Unit ID: 9AAB3225 Model ID: B34F40650000 Transport IDs: {'btleid': 'B34F', 'wpid': '4065'} 3: DEVICE NAME {0005} Name: G613 Wireless Mechanical Gaming Keyboard Kind: keyboard 4: WIRELESS DEVICE STATUS {1D4B} 5: RESET {0020} 6: DEVICE FRIENDLY NAME {0007} Friendly Name: G613 7: BATTERY STATUS {1000} Battery: 50%, discharging, next level 20%. 8: CHANGE HOST {1814} Change Host : 1:video2 9: HOSTS INFO {1815} Host 0 (paired): video2 Host 1 (paired): CB73CG2 10: REPROG CONTROLS V4 {1B04} Key/Button Diversion (saved): {Host Switch Channel 1:Regular, Host Switch Channel 2:Regular} Key/Button Diversion : {Host Switch Channel 1:Regular, Host Switch Channel 2:Regular} 11: REPORT HID USAGE {1BC0} 12: ENCRYPTION {4100} 13: KEYBOARD DISABLE BY USAGE {4522} 14: KEYBOARD LAYOUT 2 {4540} 15: GKEY {8010} Divert G Keys (saved): True Divert G Keys : False 16: REPORT RATE {8060} Polling Rate (ms): 1 Polling Rate (ms) (saved): 1 Polling Rate (ms) : 1 17: DFUCONTROL SIGNED {00C2} 18: DEVICE RESET {1802} internal, hidden 19: unknown:1803 {1803} internal, hidden 20: CONFIG DEVICE PROPS {1806} internal, hidden 21: unknown:1813 {1813} internal, hidden 22: OOBSTATE {1805} internal, hidden 23: unknown:1830 {1830} internal, hidden 24: unknown:1890 {1890} internal, hidden 25: unknown:1891 {1891} internal, hidden 26: unknown:18A1 {18A1} internal, hidden 27: unknown:1DF3 {1DF3} internal, hidden 28: unknown:1E00 {1E00} hidden 29: unknown:1EB0 {1EB0} internal, hidden 30: unknown:1861 {1861} internal, hidden 31: unknown:18B1 {18B1} internal, hidden Has 2 reprogrammable keys: 0: Host Switch Channel 1 , default: HostSwitch Channel 1 => HostSwitch Channel 1 divertable, persistently divertable, pos:1, group:0, group mask:empty reporting: default 1: Host Switch Channel 2 , default: HostSwitch Channel 2 => HostSwitch Channel 2 divertable, persistently divertable, pos:2, group:0, group mask:empty reporting: default Battery: 50%, discharging, next level 20%. Solaar-1.1.14/docs/devices/G703 Wired-Wireless Gaming Mouse 4070.txt000066400000000000000000000035741473525213700242310ustar00rootroot00000000000000 1: G703 Wired/Wireless Gaming Mouse Codename : G703 Kind : mouse Wireless PID : 4070 Protocol : HID++ 4.2 Polling rate : 1 ms (1000Hz) Serial number: AFE674F7 Firmware: MPM 14.02.B0007 Bootloader: BOT 64.02.B0007 Other: The power switch is located on the base. Supports 29 HID++ 2.0 features: 0: ROOT {0000} 1: FEATURE SET {0001} 2: DEVICE FW VERSION {0003} 3: DEVICE NAME {0005} 4: BATTERY VOLTAGE {1001} 5: unknown:1863 {1863} internal, hidden 6: unknown:18A1 {18A1} internal, hidden 7: unknown:1E00 {1E00} hidden 8: unknown:1E20 {1E20} 9: unknown:1EB0 {1EB0} internal, hidden 10: ADJUSTABLE DPI {2201} Sensitivity (DPI): 1600 11: ANGLE SNAPPING {2230} 12: SURFACE TUNING {2240} 13: REPORT RATE {8060} 14: ONBOARD PROFILES {8100} 15: MOUSE BUTTON SPY {8110} 16: unknown:1850 {1850} internal, hidden 17: DFUCONTROL SIGNED {00C2} 18: unknown:1801 {1801} internal, hidden 19: DEVICE RESET {1802} internal, hidden 20: unknown:1803 {1803} internal, hidden 21: unknown:1890 {1890} internal, hidden 22: unknown:1811 {1811} internal, hidden 23: LATENCY MONITORING {8111} 24: COLOR LED EFFECTS {8070} 25: unknown:1809 {1809} 26: unknown:1830 {1830} internal, hidden 27: OOBSTATE {1805} internal, hidden 28: CONFIG DEVICE PROPS {1806} internal, hidden Battery: 3807mV, discharging, average Solaar-1.1.14/docs/devices/G733 Gaming Headset 0AB5.text000066400000000000000000000022341473525213700220650ustar00rootroot00000000000000Solaar version 1.1.5 2: G733 Gaming Headset Device path : /dev/hidraw2 USB id : 046d:0AB5 Codename : G733 Headset Kind : headset Protocol : HID++ 4.2 Serial number: Model ID: 0AB500000000 Unit ID: FFFFFFFF Firmware: U1 37.00.B0131 Supports 9 HID++ 2.0 features: 0: ROOT {0000} V0 1: FEATURE SET {0001} V0 2: DEVICE FW VERSION {0003} V2 Firmware: Firmware U1 37.00.B0131 0AB5 Unit ID: FFFFFFFF Model ID: 0AB500000000 Transport IDs: {'usbid': '0AB5'} 3: DEVICE NAME {0005} V0 Name: G733 Gaming Headset Kind: None 4: COLOR LED EFFECTS {8070} V0 5: GKEY {8010} V0 Divert G Keys (saved): True Divert G Keys : False 6: EQUALIZER {8310} V0 7: SIDETONE {8300} V0 Sidetone (saved): 65 Sidetone : 65 8: ADC MEASUREMENT {1F20} V0 Battery status unavailable. Battery status unavailable. Solaar-1.1.14/docs/devices/G733 Gaming Headset 0AFE.text000066400000000000000000000034421473525213700221130ustar00rootroot00000000000000solaar version 1.1.11 G733 Gaming Headset Device path : /dev/hidraw3 USB id : 046d:0AFE Codename : G733 Headset New Kind : headset Protocol : HID++ 4.2 Serial number: Model ID: 0AFE00000000 Unit ID: FFFFFFFF Firmware: U2 00.06 Supports 9 HID++ 2.0 features: 0: ROOT {0000} V0 1: FEATURE SET {0001} V0 2: DEVICE FW VERSION {0003} V2 Firmware: Firmware U2 00.06 0AFE Unit ID: FFFFFFFF Model ID: 0AFE00000000 Transport IDs: {'usbid': '0AFE'} 3: DEVICE NAME {0005} V0 Name: G733 Gaming Headset Kind: None 4: COLOR LED EFFECTS {8070} V3 LED Control (saved): Device LED Control : Device LEDs Logo (saved): !LEDEffectSetting {ID: 0x0} LEDs Logo : !LEDEffectSetting {ID: 0} LEDs Primary (saved): !LEDEffectSetting {ID: 0x1, color: 0x0, ramp: 0x0} LEDs Primary : !LEDEffectSetting {ID: 1, color: 0x10000, ramp: 0x0} 5: GKEY {8010} V0 Divert G and M Keys (saved): False Divert G and M Keys : False 6: EQUALIZER {8310} V1 Equalizer (saved): {0: 0, 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0, 7: 0, 8: 0, 9: 0} Equalizer : {0: 0, 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0, 7: 0, 8: 0, 9: 0} 7: SIDETONE {8300} V0 Sidetone (saved): 0 Sidetone : 0 8: ADC MEASUREMENT {1F20} V4 Battery: 60% 3867mV , discharging. Power Management (saved): 0 Power Management : 0 Battery: 60% 3867mV , discharging. Solaar-1.1.14/docs/devices/G815 Mechanical Keyboard C33F.txt000066400000000000000000000037611473525213700227430ustar00rootroot00000000000000solaar version 1.1.9 1: G815 Mechanical Keyboard Device path : /dev/hidraw2 USB id : 046d:C33F Codename : G815 Kind : keyboard Protocol : HID++ 4.2 Polling rate : 1 ms (1000Hz) Serial number: Model ID: C33F00000000 Unit ID: 35304716 Bootloader: BOT 84.00.B0003 Firmware: U1 31.02.B0018 Other: Other: Other: Supports 24 HID++ 2.0 features: 0: ROOT {0000} V0 1: FEATURE SET {0001} V0 2: DEVICE FW VERSION {0003} V2 Firmware: Bootloader BOT 84.00.B0003 AAEA Firmware: Firmware U1 31.02.B0018 C33F Firmware: Other Firmware: Other Firmware: Other Unit ID: 35304716 Model ID: C33F00000000 Transport IDs: {'usbid': 'C33F'} 3: DEVICE NAME {0005} V0 Name: G815 RGB MECHANICAL GAMING KEYBOARD Kind: keyboard 4: CONFIG CHANGE {0020} V0 5: DFUCONTROL SIGNED {00C2} V0 6: DFU {00D0} V0 7: REPORT HID USAGE {1BC0} V0 8: KEYBOARD DISABLE BY USAGE {4522} V0 9: KEYBOARD LAYOUT 2 {4540} V0 10: GKEY {8010} V0 Divert G Keys (saved): True Divert G Keys : False 11: MKEYS {8020} V0 M-Key LEDs (saved): {M1:False, M2:False, M3:False} M-Key LEDs : {M1:False, M2:False, M3:False} 12: MR {8030} V0 MR-Key LED (saved): False MR-Key LED : False 13: BRIGHTNESS CONTROL {8040} V0 14: REPORT RATE {8060} V0 Polling Rate (ms): 1 Polling Rate (ms) (saved): 1 Polling Rate (ms) : 1 15: RGB EFFECTS {8071} V0 16: PER KEY LIGHTING V2 {8081} V2 17: ONBOARD PROFILES {8100} V0 Device Mode: Host Onboard Profiles (saved): Disable Onboard Profiles : Disable 18: unknown:1801 {1801} V0 internal, hidden 19: DEVICE RESET {1802} V0 internal, hidden 20: CONFIG DEVICE PROPS {1806} V5 internal, hidden 21: unknown:18B0 {18B0} V0 internal, hidden 22: unknown:1E00 {1E00} V0 hidden 23: unknown:1EB0 {1EB0} V0 internal, hidden Battery status unavailable. Solaar-1.1.14/docs/devices/G903 LIGHTSPEED Wireless Gaming Mouse 4087.txt000066400000000000000000000112631473525213700245760ustar00rootroot00000000000000solaar version 1.1.8rc3+git1940-4e7b6b3 1: G903 LIGHTSPEED Wireless Gaming Mouse w/ HERO Device path : /dev/hidraw13 WPID : 4087 Codename : G903 LS Kind : mouse Protocol : HID++ 4.2 Polling rate : 1 ms (1000Hz) Serial number: 3EF038B9 Model ID: 4087C0910000 Unit ID: 3EF038B9 Bootloader: BL1 06.01.B0013 Firmware: MPM 23.01.B0013 Other: The power switch is located on the base. Supports 31 HID++ 2.0 features: 0: ROOT {0000} V0 1: FEATURE SET {0001} V0 2: DEVICE FW VERSION {0003} V2 Firmware: Bootloader BL1 06.01.B0013 0000047072FE Firmware: Firmware MPM 23.01.B0013 4087047072FE Firmware: Other Unit ID: 3EF038B9 Model ID: 4087C0910000 Transport IDs: {'wpid': '4087', 'usbid': 'C091'} 3: DEVICE NAME {0005} V0 Name: G903 LIGHTSPEED Wireless Gaming Mouse w/ HERO Kind: mouse 4: WIRELESS DEVICE STATUS {1D4B} V0 5: CONFIG CHANGE {0020} V0 6: BATTERY VOLTAGE {1001} V2 Battery: 90% 4079mV , discharging. 7: RGB EFFECTS {8071} V0 8: ONBOARD PROFILES {8100} V0 Device Mode: On-Board Onboard Profiles (saved): Enable Onboard Profiles : Enable 9: MOUSE BUTTON SPY {8110} V0 10: REPORT RATE {8060} V0 Polling Rate (ms): 1 Polling Rate (ms) (saved): 1 Polling Rate (ms) : 1 11: ADJUSTABLE DPI {2201} V1 Sensitivity (DPI) (saved): 6400 Sensitivity (DPI) : 6400 12: DFUCONTROL SIGNED {00C2} V0 13: DEVICE RESET {1802} V0 internal, hidden 14: unknown:1803 {1803} V0 internal, hidden 15: OOBSTATE {1805} V0 internal, hidden 16: CONFIG DEVICE PROPS {1806} V4 internal, hidden 17: unknown:1811 {1811} V0 internal, hidden 18: unknown:1830 {1830} V0 internal, hidden 19: unknown:1890 {1890} V4 internal, hidden 20: unknown:1891 {1891} V4 internal, hidden 21: unknown:18A1 {18A1} V0 internal, hidden 22: unknown:1801 {1801} V0 internal, hidden 23: unknown:18B1 {18B1} V0 internal, hidden 24: unknown:1DF3 {1DF3} V0 internal, hidden 25: unknown:1E00 {1E00} V0 hidden 26: unknown:1EB0 {1EB0} V0 internal, hidden 27: unknown:1863 {1863} V0 internal, hidden 28: unknown:1E22 {1E22} V0 internal, hidden 29: HIRES WHEEL {2121} V0 Multiplier: 8 Has invert: Normal wheel motion Has ratchet switch: Normal wheel mode Low resolution mode HID notification Scroll Wheel Direction (saved): False Scroll Wheel Direction : False Scroll Wheel Resolution (saved): False Scroll Wheel Resolution : False Scroll Wheel Diversion (saved): False Scroll Wheel Diversion : False 30: unknown:18C0 {18C0} V0 internal, hidden Battery: 90% 4079mV , discharging. 7: Candy companion chip Device path : /dev/hidraw14 Codename : Candy Kind : touchpad Protocol : HID++ 4.2 Serial number: 4E4E9946 Model ID: 405F00000000 Unit ID: 34304713 Firmware: CC 07.00.B0010 Bootloader: BOT 32.00.B0010 Supports 12 HID++ 2.0 features: 0: ROOT {0000} V0 1: FEATURE SET {0001} V0 2: DEVICE FW VERSION {0003} V2 Firmware: Firmware CC 07.00.B0010 405F Firmware: Bootloader BOT 32.00.B0010 405F Unit ID: 34304713 Model ID: 405F00000000 Transport IDs: {'wpid': '405F'} 3: DEVICE NAME {0005} V0 Name: Candy companion chip Kind: touchpad 4: unknown:18A1 {18A1} V0 internal, hidden 5: unknown:1E00 {1E00} V0 hidden 6: unknown:1EB0 {1EB0} V0 internal, hidden 7: DFUCONTROL SIGNED {00C2} V0 8: unknown:1801 {1801} V0 internal, hidden 9: DEVICE RESET {1802} V0 internal, hidden 10: unknown:1803 {1803} V0 internal, hidden 11: COLOR LED EFFECTS {8070} V4 Battery status unavailable. Solaar-1.1.14/docs/devices/G915 TKL LIGHTSPEED Wireless RGB Mechanical Gaming Keyboard 408E.txt000066400000000000000000000101671473525213700303040ustar00rootroot00000000000000Solaar version 1.1.4 1: G915 TKL LIGHTSPEED Wireless RGB Mechanical Gaming Keyboard Device path : None WPID : 408E Codename : G915 TKL Kind : keyboard Protocol : HID++ 4.2 Polling rate : 1 ms (1000Hz) Serial number: F085CB9D Model ID: B35F408EC343 Unit ID: F085CB9D Bootloader: BL1 12.00.B0017 Other: Firmware: MPK 14.01.B0021 Other: Other: The power switch is located on the top left corner. Supports 37 HID++ 2.0 features: 0: ROOT {0000} 1: FEATURE SET {0001} 2: DEVICE FW VERSION {0003} Firmware: Bootloader BL1 12.00.B0017 00008B79E978 Firmware: Other Firmware: Firmware MPK 14.01.B0021 408E5E599E54 Firmware: Other Firmware: Other Unit ID: F085CB9D Model ID: B35F408EC343 Transport IDs: {'btleid': 'B35F', 'wpid': '408E', 'usbid': 'C343'} 3: DEVICE NAME {0005} Name: G915 TKL LIGHTSPEED Wireless RGB Mechanical Gaming Keyboard Kind: keyboard 4: WIRELESS DEVICE STATUS {1D4B} 5: RESET {0020} 6: DEVICE FRIENDLY NAME {0007} Friendly Name: G915 TKL 7: BATTERY VOLTAGE {1001} Battery: 3923mV, discharging, 70. 8: CHANGE HOST {1814} Change Host : 1:archlinux 9: HOSTS INFO {1815} Host 0 (paired): archlinux Host 1 (paired): 10: RGB EFFECTS {8071} 11: PER KEY LIGHTING V2 {8081} 12: REPROG CONTROLS V4 {1B04} Key/Button Diversion (saved): {Host Switch Channel 1:Regular, Host Switch Channel 2:Regular} Key/Button Diversion : {Host Switch Channel 1:Regular, Host Switch Channel 2:Regular} 13: REPORT HID USAGE {1BC0} 14: ENCRYPTION {4100} 15: KEYBOARD DISABLE BY USAGE {4522} 16: KEYBOARD LAYOUT 2 {4540} 17: GKEY {8010} Divert G Keys (saved): False Divert G Keys : False 18: MKEYS {8020} M-Key LEDs (saved): {M1:False, M2:False, M3:False} M-Key LEDs : {M1:False, M2:False, M3:False} 19: MR {8030} MR-Key LED (saved): False MR-Key LED : False 20: BRIGHTNESS CONTROL {8040} 21: ONBOARD PROFILES {8100} Device Mode: On-Board Onboard Profiles (saved): Enable Onboard Profiles : Enable 22: REPORT RATE {8060} Polling Rate (ms): 1 Polling Rate (ms) (saved): 1 Polling Rate (ms) : 1 23: DFUCONTROL SIGNED {00C2} 24: DFU {00D0} 25: DEVICE RESET {1802} internal, hidden 26: unknown:1803 {1803} internal, hidden 27: CONFIG DEVICE PROPS {1806} internal, hidden 28: unknown:1813 {1813} internal, hidden 29: OOBSTATE {1805} internal, hidden 30: unknown:1830 {1830} internal, hidden 31: unknown:1890 {1890} internal, hidden 32: unknown:1891 {1891} internal, hidden 33: unknown:18A1 {18A1} internal, hidden 34: unknown:1E00 {1E00} hidden 35: unknown:1EB0 {1EB0} internal, hidden 36: unknown:1861 {1861} internal, hidden Has 2 reprogrammable keys: 0: Host Switch Channel 1 , default: HostSwitch Channel 1 => HostSwitch Channel 1 divertable, persistently divertable, pos:1, group:0, group mask:empty reporting: default 1: Host Switch Channel 2 , default: HostSwitch Channel 2 => HostSwitch Channel 2 divertable, persistently divertable, pos:2, group:0, group mask:empty reporting: default Battery: 3923mV, discharging, 70. Solaar-1.1.14/docs/devices/G915 WIRELESS RGB MECHANICAL GAMING KEYBOARD 407C.text000066400000000000000000000176151473525213700246270ustar00rootroot00000000000000solaar version 1.1.12rc1 1: G915 WIRELESS RGB MECHANICAL GAMING KEYBOARD Device path : None WPID : 407C Codename : G915 KEYBOARD Kind : keyboard Protocol : HID++ 4.2 Report Rate : 1ms Serial number: A502B0E1 Model ID: B354407CC33E Unit ID: A502B0E1 1: BOT 77.02.B0039 3: 0: MPK 09.03.B0041 3: 3: The power switch is located on the top left corner. Supports 38 HID++ 2.0 features: 0: ROOT {0000} V0 1: FEATURE SET {0001} V0 2: DEVICE FW VERSION {0003} V4 Firmware: Bootloader BOT 77.02.B0039 0000EC44D534 Firmware: Other Firmware: Firmware MPK 09.03.B0041 407C3791543D Firmware: Other Firmware: Other Unit ID: A502B0E1 Model ID: B354407CC33E Transport IDs: {'btleid': 'B354', 'wpid': '407C', 'usbid': 'C33E'} 3: DEVICE NAME {0005} V0 Name: G915 WIRELESS RGB MECHANICAL GAMING KEYBOARD Kind: keyboard 4: WIRELESS DEVICE STATUS {1D4B} V0 5: CONFIG CHANGE {0020} V0 Configuration: 11000000000000000000000000000000 6: DEVICE FRIENDLY NAME {0007} V0 Friendly Name: G915 KEYBOARD� 7: BATTERY VOLTAGE {1001} V3 Battery: 80% 3998mV , discharging. 8: CHANGE HOST {1814} V1 Change Host : 1:Yon 9: HOSTS INFO {1815} V1 Host 0 (paired): Yon Host 1 (paired): 10: RGB EFFECTS {8071} V0 RGB Control (saved): Device RGB Control : Device LEDs Logo (saved): !LEDEffectSetting {ID: 1, color: 11546720, intensity: 0, period: 100, ramp: 0, speed: 0} LEDs Logo : HID++ error {'number': 1, 'request': 2799, 'error': 7, 'params': b'\x00'} LEDs Primary (saved): !LEDEffectSetting {ID: 1, color: 16776960, intensity: 0, period: 100, ramp: 0, speed: 0} LEDs Primary : HID++ error {'number': 1, 'request': 2796, 'error': 7, 'params': b'\x01'} 11: PER KEY LIGHTING V2 {8081} V2 Per-key Lighting (saved): {A:white, B:red, C:white, D:white, E:white, F:white, G:white, H:white, I:white, J:white, K:white, L:white, M:white, N:white, O:white, P:white, Q:white, R:white, S:white, T:white, U:white, V:white, W:white, X:white, Y:white, Z:white, 1:white, 2:white, 3:white, 4:white, 5:white, 6:white, 7:white, 8:white, 9:white, 0:white, ENTER:white, ESC:white, BACKSPACE:white, TAB:white, SPACE:white, -:white, =:white, [:white, \:white, KEY 46:white, ~:white, ;:white, ':white, `:white, ,:white, .:white, /:white, CAPS LOCK:white, F1:white, F2:white, F3:white, F4:white, F5:white, F6:white, F7:white, F8:white, F9:white, F10:white, F11:white, F12:white, PRINT:white, SCROLL LOCK:white, PASTE:white, INSERT:white, HOME:white, PAGE UP:white, DELETE:white, END:white, PAGE DOWN:white, RIGHT:white, LEFT:white, DOWN:white, UP:white, NUMLOCK:white, KEYPAD /:white, KEYPAD *:white, KEYPAD -:white, KEYPAD +:white, KEYPAD ENTER:white, KEYPAD 1:white, KEYPAD 2:white, KEYPAD 3:white, KEYPAD 4:white, KEYPAD 5:white, KEYPAD 6:white, KEYPAD 7:white, KEYPAD 8:white, KEYPAD 9:white, KEYPAD 0:white, KEYPAD .:white, KEY 97:white, COMPOSE:white, POWER:white, KEY 100:white, KEY 101:white, KEY 102:white, KEY 103:white, LEFT CTRL:white, LEFT SHIFT:white, LEFT ALT:white, LEFT WINDOWS:white, RIGHT CTRL:white, RIGHT SHIFT:white, RIGHT ALTGR:white, RIGHT WINDOWS:white, BRIGHTNESS:white, PAUSE:white, MUTE:white, NEXT:white, PREVIOUS:white, G1:white, G2:white, G3:white, G4:white, G5:white, LOGO:white} Per-key Lighting : {A:white, B:white, C:white, D:white, E:white, F:white, G:white, H:white, I:white, J:white, K:white, L:white, M:white, N:white, O:white, P:white, Q:white, R:white, S:white, T:white, U:white, V:white, W:white, X:white, Y:white, Z:white, 1:white, 2:white, 3:white, 4:white, 5:white, 6:white, 7:white, 8:white, 9:white, 0:white, ENTER:white, ESC:white, BACKSPACE:white, TAB:white, SPACE:white, -:white, =:white, [:white, \:white, KEY 46:white, ~:white, ;:white, ':white, `:white, ,:white, .:white, /:white, CAPS LOCK:white, F1:white, F2:white, F3:white, F4:white, F5:white, F6:white, F7:white, F8:white, F9:white, F10:white, F11:white, F12:white, PRINT:white, SCROLL LOCK:white, PASTE:white, INSERT:white, HOME:white, PAGE UP:white, DELETE:white, END:white, PAGE DOWN:white, RIGHT:white, LEFT:white, DOWN:white, UP:white, NUMLOCK:white, KEYPAD /:white, KEYPAD *:white, KEYPAD -:white, KEYPAD +:white, KEYPAD ENTER:white, KEYPAD 1:white, KEYPAD 2:white, KEYPAD 3:white, KEYPAD 4:white, KEYPAD 5:white, KEYPAD 6:white, KEYPAD 7:white, KEYPAD 8:white, KEYPAD 9:white, KEYPAD 0:white, KEYPAD .:white, KEY 97:white, COMPOSE:white, POWER:white, KEY 100:white, KEY 101:white, KEY 102:white, KEY 103:white, LEFT CTRL:white, LEFT SHIFT:white, LEFT ALT:white, LEFT WINDOWS:white, RIGHT CTRL:white, RIGHT SHIFT:white, RIGHT ALTGR:white, RIGHT WINDOWS:white, BRIGHTNESS:white, PAUSE:white, MUTE:white, NEXT:white, PREVIOUS:white, G1:white, G2:white, G3:white, G4:white, G5:white, LOGO:white} 12: REPROG CONTROLS V4 {1B04} V4 Key/Button Diversion (saved): {Host Switch Channel 1:Regular, Host Switch Channel 2:Regular} Key/Button Diversion : {Host Switch Channel 1:Regular, Host Switch Channel 2:Regular} 13: REPORT HID USAGE {1BC0} V1 14: ENCRYPTION {4100} V0 15: KEYBOARD DISABLE BY USAGE {4522} V0 16: KEYBOARD LAYOUT 2 {4540} V0 17: GKEY {8010} V0 Divert G and M Keys (saved): False Divert G and M Keys : False 18: MKEYS {8020} V0 M-Key LEDs (saved): {M1:False, M2:False, M3:False} M-Key LEDs : {M1:False, M2:False, M3:False} 19: MR {8030} V0 MR-Key LED (saved): False MR-Key LED : False 20: BRIGHTNESS CONTROL {8040} V0 Brightness Control (saved): 12 Brightness Control : 12 21: ONBOARD PROFILES {8100} V0 Device Mode: Host Onboard Profiles (saved): Disabled Onboard Profiles : Disabled 22: REPORT RATE {8060} V0 Report Rate: 1ms Report Rate (saved): 1ms Report Rate : 1ms 23: DFUCONTROL SIGNED {00C2} V0 24: DFU {00D0} V3 25: DEVICE RESET {1802} V0 internal, hidden 26: unknown:1803 {1803} V0 internal, hidden 27: CONFIG DEVICE PROPS {1806} V8 internal, hidden 28: unknown:1813 {1813} V0 internal, hidden 29: OOBSTATE {1805} V0 internal, hidden 30: unknown:1830 {1830} V0 internal, hidden 31: unknown:1890 {1890} V5 internal, hidden 32: unknown:1891 {1891} V5 internal, hidden 33: unknown:18A1 {18A1} V0 internal, hidden 34: unknown:1E00 {1E00} V0 hidden 35: unknown:1EB0 {1EB0} V0 internal, hidden 36: unknown:1861 {1861} V0 internal, hidden 37: unknown:18B0 {18B0} V0 internal, hidden Has 2 reprogrammable keys: 0: Host Switch Channel 1 , default: HostSwitch Channel 1 => HostSwitch Channel 1 divertable, persistently divertable, pos:1, group:0, group mask:empty reporting: default 1: Host Switch Channel 2 , default: HostSwitch Channel 2 => HostSwitch Channel 2 divertable, persistently divertable, pos:2, group:0, group mask:empty reporting: default Battery: 80% 3998mV , discharging. Solaar-1.1.14/docs/devices/G915 WIRELESS RGB MECHANICAL GAMING KEYBOARD C33E.text000066400000000000000000000104141473525213700246350ustar00rootroot00000000000000solaar version 1.1.10 USB and Bluetooth Devices 1: G915 WIRELESS RGB MECHANICAL GAMING KEYBOARD Device path : /dev/hidraw13 USB id : 046d:C33E Codename : G915 Kind : ? Protocol : HID++ 4.2 Polling rate : 1 ms (1000Hz) Serial number: Model ID: B354407CC33E Unit ID: 8816D0DF Bootloader: BOT 77.03.B0041 Other: Firmware: MPK 09.04.B0042 Other: Other: Supports 37 HID++ 2.0 features: 0: ROOT {0000} V0 1: FEATURE SET {0001} V0 2: DEVICE FW VERSION {0003} V4 Firmware: Bootloader BOT 77.03.B0041 00003791543D Firmware: Other Firmware: Firmware MPK 09.04.B0042 C33E8A23A76B Firmware: Other Firmware: Other Unit ID: 8816D0DF Model ID: B354407CC33E Transport IDs: {'btleid': 'B354', 'wpid': '407C', 'usbid': 'C33E'} 3: DEVICE NAME {0005} V0 Name: G915 WIRELESS RGB MECHANICAL GAMING KEYBOARD Kind: keyboard 4: WIRELESS DEVICE STATUS {1D4B} V0 5: CONFIG CHANGE {0020} V0 6: BATTERY VOLTAGE {1001} V3 Battery: 70% 3965mV , recharging. 7: CHANGE HOST {1814} V1 Changer d'hôte : 1:stagcrown 8: HOSTS INFO {1815} V1 Host 0 (paired): stagcrown Host 1 (paired): 9: RGB EFFECTS {8071} V0 10: PER KEY LIGHTING V2 {8081} V2 11: REPROG CONTROLS V4 {1B04} V4 Interception des boutons/touches (saved): {Host Switch Channel 1:Interception, Host Switch Channel 2:Interception} Interception des boutons/touches : {Host Switch Channel 1:Interception, Host Switch Channel 2:Interception} 12: REPORT HID USAGE {1BC0} V1 13: ENCRYPTION {4100} V0 14: KEYBOARD DISABLE BY USAGE {4522} V0 15: KEYBOARD LAYOUT 2 {4540} V0 16: GKEY {8010} V0 Définir les touches G (saved): True Définir les touches G : False 17: MKEYS {8020} V0 LEDs de touche M (saved): {M1:False, M2:False, M3:False} LEDs de touche M : {M1:False, M2:False, M3:False} 18: MR {8030} V0 LED de touche MR (saved): False LED de touche MR : False 19: BRIGHTNESS CONTROL {8040} V0 20: ONBOARD PROFILES {8100} V0 Device Mode: On-Board Profils embarqués (saved): Enable Profils embarqués : Enable 21: REPORT RATE {8060} V0 Polling Rate (ms): 1 Taux de scrutation (ms) (saved): 1 Taux de scrutation (ms) : 1 22: DFUCONTROL SIGNED {00C2} V0 23: DFU {00D0} V3 24: DEVICE RESET {1802} V0 internal, hidden 25: unknown:1803 {1803} V0 internal, hidden 26: CONFIG DEVICE PROPS {1806} V8 internal, hidden 27: unknown:1813 {1813} V0 internal, hidden 28: OOBSTATE {1805} V0 internal, hidden 29: unknown:1830 {1830} V0 internal, hidden 30: unknown:1890 {1890} V9 internal, hidden 31: unknown:1891 {1891} V9 internal, hidden 32: unknown:18A1 {18A1} V0 internal, hidden 33: unknown:1E00 {1E00} V0 hidden 34: unknown:1EB0 {1EB0} V0 internal, hidden 35: unknown:1861 {1861} V0 internal, hidden 36: unknown:18B0 {18B0} V0 internal, hidden Has 2 reprogrammable keys: 0: Host Switch Channel 1 , default: HostSwitch Channel 1 => HostSwitch Channel 1 divertable, persistently divertable, pos:1, group:0, group mask:empty reporting: diverted 1: Host Switch Channel 2 , default: HostSwitch Channel 2 => HostSwitch Channel 2 divertable, persistently divertable, pos:2, group:0, group mask:empty reporting: diverted Battery: 70% 3965mV , recharging. Solaar-1.1.14/docs/devices/G915 WIRELESS RGB Mechanical Gaming Keyboard 407E.txt000066400000000000000000000075321473525213700257210ustar00rootroot00000000000000Solaar version 1.1.1 1: G915 WIRELESS RGB MECHANICAL GAMING KEYBOARD Device path : None WPID : 407C Codename : G915 KEYBOARD Kind : keyboard Protocol : HID++ 4.2 Polling rate : 8 ms (125Hz) Serial number: 7FDC464D Model ID: B354407CC33E Unit ID: 7FDC464D Bootloader: BOT 77.01.B0035 Other: Firmware: MPK 09.01.B0035 Other: Other: The power switch is located on the top left corner. Supports 39 HID++ 2.0 features: 0: ROOT {0000} 1: FEATURE SET {0001} 2: DEVICE FW VERSION {0003} Firmware: Bootloader BOT 77.01.B0035 0000691BEBFB Firmware: Other Firmware: Firmware MPK 09.01.B0035 407C691BEBFB Firmware: Other Firmware: Other Unit ID: 7FDC464D Model ID: B354407CC33E Transport IDs: {'btleid': 'B354', 'wpid': '407C', 'usbid': 'C33E'} 3: DEVICE NAME {0005} Name: G915 WIRELESS RGB MECHANICAL GAMING KEYBOARD Kind: keyboard 4: WIRELESS DEVICE STATUS {1D4B} 5: RESET {0020} 6: DEVICE FRIENDLY NAME {0007} Friendly Name: G915 KEYBOARD 7: BATTERY VOLTAGE {1001} Battery: 3914mV, discharging, 60. 8: CHANGE HOST {1814} Change Host : 1:evoxs 9: HOSTS INFO {1815} Host 0 (paired): evoxs Host 1 (unpaired): 10: RGB EFFECTS {8071} 11: PER KEY LIGHTING V2 {8081} 12: REPROG CONTROLS V4 {1B04} Key/Button Diversion (saved): {'209': 0, '210': 0} Key/Button Diversion : {'209': 0, '210': 0} 13: REPORT HID USAGE {1BC0} 14: ENCRYPTION {4100} 15: KEYBOARD DISABLE BY USAGE {4522} 16: KEYBOARD LAYOUT 2 {4540} 17: GKEY {8010} Divert G Keys (saved): False Divert G Keys : False 18: MKEYS {8020} 19: MR {8030} 20: BRIGHTNESS CONTROL {8040} 21: ONBOARD PROFILES {8100} Device Mode: Host 22: REPORT RATE {8060} Polling Rate (ms): 1 Polling Rate (ms) (saved): 1 Polling Rate (ms) : 1 23: DFUCONTROL SIGNED {00C2} 24: DFU {00D0} 25: DEVICE RESET {1802} internal, hidden 26: unknown:1803 {1803} internal, hidden 27: CONFIG DEVICE PROPS {1806} internal, hidden 28: unknown:1813 {1813} internal, hidden 29: OOBSTATE {1805} internal, hidden 30: unknown:1830 {1830} internal, hidden 31: unknown:1890 {1890} internal, hidden 32: unknown:1891 {1891} internal, hidden 33: unknown:18A1 {18A1} internal, hidden 34: unknown:1DF3 {1DF3} internal, hidden 35: unknown:1E00 {1E00} hidden 36: unknown:1EB0 {1EB0} internal, hidden 37: unknown:1861 {1861} internal, hidden 38: unknown:18B0 {18B0} internal, hidden Has 2 reprogrammable keys: 0: Host Switch Channel 1 , default: HostSwitch Channel 1 => HostSwitch Channel 1 divertable, persistently divertable, pos:1, group:0, group mask:empty reporting: default 1: Host Switch Channel 2 , default: HostSwitch Channel 2 => HostSwitch Channel 2 divertable, persistently divertable, pos:2, group:0, group mask:empty reporting: default Battery: 3914mV, discharging, 60. Solaar-1.1.14/docs/devices/G935 Gaming Headset 0A87.txt000066400000000000000000000022531473525213700217150ustar00rootroot00000000000000Solaar version 1.1.7 1: G935 Gaming Headset Device path : /dev/hidraw2 USB id : 046d:0A87 Codename : G935 Headset Kind : headset Protocol : HID++ 4.2 Serial number: Model ID: 000000000A87 Unit ID: FFFFFFFF Firmware: U1 29.00.B0012 Supports 9 HID++ 2.0 features: 0: ROOT {0000} V0 1: FEATURE SET {0001} V0 2: DEVICE FW VERSION {0003} V2 Firmware: Firmware U1 29.00.B0012 0A87 Unit ID: FFFFFFFF Model ID: 000000000A87 Transport IDs: {'btid': '0000', 'btleid': '0000'} 3: DEVICE NAME {0005} V0 Name: G935 Gaming Headset Kind: None 4: COLOR LED EFFECTS {8070} V0 5: GKEY {8010} V0 Divert G Keys (saved): False Divert G Keys : False 6: EQUALIZER {8310} V0 7: SIDETONE {8300} V0 Sidetone (saved): 0 Sidetone : 0 8: ADC MEASUREMENT {1F20} V0 Battery status unavailable. Battery status unavailable. Solaar-1.1.14/docs/devices/Illuminated Keyboard C318.txt000066400000000000000000000005351473525213700225350ustar00rootroot00000000000000solaar version 1.1.8-29-g0ae14c7 1: Illuminated Keyboard Device path : /dev/hidraw1 USB id : 046d:C318 Codename : Illuminated Kind : keyboard Protocol : HID++ 1.0 Serial number: Firmware: 55.01.B0025 Notifications: (none). Features: (none) Battery status unavailable. Solaar-1.1.14/docs/devices/Illuminated Living-Room Keyboard K830 4032.txt000066400000000000000000000033561473525213700252440ustar00rootroot00000000000000# Provided by Mikkel Munch Mortensen (solaar) 3: Illuminated Living-Room Keyboard K830 Codename : K830 Kind : keyboard Wireless PID : 4032 Protocol : HID++ 4.1 Polling rate : 8 ms (125Hz) Serial number: 9F7C6FD7 Firmware: RQK 56.00.B0020 The power switch is located on the edge of top right corner. Supports 27 HID++ 2.0 features: 0: ROOT {0000} 1: FEATURE SET {0001} 2: DEVICE FW VERSION {0003} 3: DEVICE NAME {0005} 4: WIRELESS DEVICE STATUS {1D4B} 5: unknown:0020 {0020} 6: BATTERY STATUS {1000} 7: BACKLIGHT {1981} 8: unknown:1B04 {1B04} 9: unknown:2005 {2005} 10: NEW FN INVERSION {40A2} 11: ENCRYPTION {4100} 12: unknown:4521 {4521} 13: TOUCHPAD RAW XY {6100} hidden 14: unknown:6501 {6501} 15: unknown:00C1 {00C1} 16: unknown:1811 {1811} internal, hidden 17: unknown:1830 {1830} internal, hidden 18: unknown:1890 {1890} internal, hidden 19: unknown:18A0 {18A0} internal, hidden 20: unknown:1DF3 {1DF3} internal, hidden 21: unknown:1E00 {1E00} hidden 22: unknown:1EB0 {1EB0} internal, hidden 23: unknown:1861 {1861} internal, hidden 24: unknown:1A20 {1A20} internal, hidden 25: unknown:18B0 {18B0} internal, hidden 26: unknown:1F07 {1F07} internal, hidden Battery: 50%, discharging. Solaar-1.1.14/docs/devices/K850 Performance Wireless Keyboard 4062.txt000066400000000000000000000203251473525213700246710ustar00rootroot00000000000000Solaar version 1.1.1 2: K850 Performance Wireless Keyboard Device path : /dev/hidraw2 WPID : 4062 Codename : K850 Kind : keyboard Protocol : HID++ 4.5 Polling rate : 20 ms (50Hz) Serial number: E2A15F0B Model ID: B34D40620000 Unit ID: 178D05AD Bootloader: BOT 43.01.B0004 Firmware: MPK 04.03.B0015 Other: The power switch is located on the edge of top right corner. Supports 31 HID++ 2.0 features: 0: ROOT {0000} 1: FEATURE SET {0001} 2: DEVICE FW VERSION {0003} Firmware: Bootloader BOT 43.01.B0004 00002FD362F001 Firmware: Firmware MPK 04.03.B0015 40622FD362F001 Firmware: Other Unit ID: 178D05AD Model ID: B34D40620000 Transport IDs: {'btleid': 'B34D', 'wpid': '4062'} 3: DEVICE NAME {0005} Name: K850 Performance Wireless Keyboard Kind: keyboard 4: WIRELESS DEVICE STATUS {1D4B} 5: RESET {0020} 6: DEVICE FRIENDLY NAME {0007} Friendly Name: Keyboard K850 7: BATTERY STATUS {1000} Battery: 90%, discharging, next level 50%. 8: CHANGE HOST {1814} Change Host : 2:desktop 9: HOSTS INFO {1815} Host 0 (paired): rpi Host 1 (paired): desktop Host 2 (paired): laptop 10: REPROG CONTROLS V4 {1B04} Key/Button Diversion (saved): {'1': 0, '10': 0, '189': 0, '191': 0, '192': 0, '193': 0, '194': 0, '2': 0, '209': 0, '210': 0, '211': 0, '212': 0, '213': 0, '214': 0, '221': 0, '3': 0, '4': 0, '5': 0, '6': 0} Key/Button Diversion : {'209': 0, '210': 0, '211': 0, '213': 0, '214': 0, '189': 0, '212': 0, '6': 0, '4': 0, '5': 0, '3': 0, '2': 0, '1': 0, '194': 0, '221': 0, '191': 0, '10': 0, '193': 0, '192': 0} 11: PERSISTENT REMAPPABLE ACTION {1C00} 12: K375S FN INVERSION {40A3} Swap Fx function (saved): False Swap Fx function : False 13: ENCRYPTION {4100} 14: LOCK KEY STATE {4220} 15: KEYBOARD DISABLE KEYS {4521} Disable keys (saved): {'1': False, '16': False, '2': False, '4': False, '8': False} Disable keys : {'1': False, '2': False, '4': False, '8': False, '16': False} 16: MULTIPLATFORM {4531} Set OS (saved): 0 Set OS : Windows 17: DFUCONTROL SIGNED {00C2} 18: unknown:1803 {1803} internal, hidden 19: CONFIG DEVICE PROPS {1806} internal, hidden 20: OOBSTATE {1805} internal, hidden 21: unknown:1813 {1813} internal, hidden 22: unknown:1830 {1830} internal, hidden 23: unknown:1861 {1861} internal, hidden 24: unknown:1890 {1890} internal, hidden 25: unknown:1891 {1891} internal, hidden 26: unknown:18A1 {18A1} internal, hidden 27: unknown:1DF3 {1DF3} internal, hidden 28: unknown:1E00 {1E00} hidden 29: unknown:1EB0 {1EB0} internal, hidden 30: unknown:18B0 {18B0} internal, hidden Has 21 reprogrammable keys: 0: Host Switch Channel 1 , default: HostSwitch Channel 1 => HostSwitch Channel 1 nonstandard, divertable, persistently divertable, pos:0, group:0, group mask:empty reporting: default 1: Host Switch Channel 2 , default: HostSwitch Channel 2 => HostSwitch Channel 2 nonstandard, divertable, persistently divertable, pos:0, group:0, group mask:empty reporting: default 2: Host Switch Channel 3 , default: HostSwitch Channel 3 => HostSwitch Channel 3 nonstandard, divertable, persistently divertable, pos:0, group:0, group mask:empty reporting: default 3: MultiPlatform Home/Mission Control, default: Multiplatform Home/Mission Control => Multiplatform Home/Mission Control is FN, FN sensitive, reprogrammable, divertable, persistently divertable, pos:1, group:0, group mask:empty reporting: default 4: MultiPlatform Menu/Show/Hide Virtual Keyboard/Launchpad, default: Multiplatform Menu/Launchpad => Multiplatform Menu/Launchpad is FN, FN sensitive, reprogrammable, divertable, persistently divertable, pos:2, group:0, group mask:empty reporting: default 5: Multiplatform Back , default: MultiPlatform Back => MultiPlatform Back is FN, FN sensitive, reprogrammable, divertable, persistently divertable, pos:3, group:0, group mask:empty reporting: default 6: MultiPlatform Search , default: Multiplatform Search => Multiplatform Search is FN, FN sensitive, reprogrammable, divertable, persistently divertable, pos:4, group:0, group mask:empty reporting: default 7: Previous , default: Previous => Previous is FN, FN sensitive, divertable, persistently divertable, pos:5, group:0, group mask:empty reporting: default 8: Play/Pause , default: Play/Pause => Play/Pause is FN, FN sensitive, divertable, persistently divertable, pos:6, group:0, group mask:empty reporting: default 9: Next , default: Next => Next is FN, FN sensitive, divertable, persistently divertable, pos:7, group:0, group mask:empty reporting: default 10: Mute , default: Mute => Mute is FN, FN sensitive, divertable, persistently divertable, pos:8, group:0, group mask:empty reporting: default 11: Volume Down , default: Volume Down => Volume Down is FN, FN sensitive, divertable, persistently divertable, pos:9, group:0, group mask:empty reporting: default 12: Volume Up , default: Volume Up => Volume Up is FN, FN sensitive, divertable, persistently divertable, pos:10, group:0, group mask:empty reporting: default 13: Multiplatform Lock , default: WindowsLock => WindowsLock is FN, FN sensitive, reprogrammable, divertable, persistently divertable, pos:11, group:0, group mask:empty reporting: default 14: Multi Platform Language Switch, default: Multiplatform Language Switch => Multiplatform Language Switch is FN, FN sensitive, reprogrammable, divertable, persistently divertable, pos:12, group:0, group mask:empty reporting: default 15: Screen Capture/Print Screen, default: Screen Capture => Screen Capture is FN, reprogrammable, divertable, persistently divertable, pos:0, group:0, group mask:empty reporting: default 16: Calculator , default: Calculator => Calculator is FN, reprogrammable, divertable, persistently divertable, pos:0, group:0, group mask:empty reporting: default 17: Fn Up , default: unknown:0070 => unknown:0070 is FN, divertable, persistently divertable, pos:0, group:0, group mask:empty reporting: default 18: Fn Down , default: unknown:006F => unknown:006F is FN, divertable, persistently divertable, pos:0, group:0, group mask:empty reporting: default 19: F Lock , default: Do Nothing One => Do Nothing One is FN, pos:0, group:0, group mask:empty reporting: default 20: unknown:0034 , default: Do Nothing One => Do Nothing One nonstandard, pos:0, group:0, group mask:empty reporting: default Battery: 90%, discharging, next level 50%. Solaar-1.1.14/docs/devices/K850 Performance Wireless Keyboard B34D.txt000066400000000000000000000227141473525213700247360ustar00rootroot00000000000000Solaar version 1.1.4 2: K850 Performance Wireless Keyboard Device path : /dev/hidraw1 USB id : 046d:B34D Codename : Keyboard K850 Kind : ? Protocol : HID++ 4.5 Serial number: Model ID: B34D40620000 Unit ID: 420F6F9D Bootloader: BOT 43.01.B0004 Firmware: MPK 04.03.B0015 Other: Supports 17 HID++ 2.0 features: 0: ROOT {0000} 1: FEATURE SET {0001} 2: DEVICE FW VERSION {0003} Firmware: Bootloader BOT 43.01.B0004 00002FD362F001 Firmware: Firmware MPK 04.03.B0015 B34D2FD362F001 Firmware: Other Unit ID: 420F6F9D Model ID: B34D40620000 Transport IDs: {'btleid': 'B34D', 'wpid': '4062'} 3: DEVICE NAME {0005} Name: K850 Performance Wireless Keyboard Kind: keyboard 4: WIRELESS DEVICE STATUS {1D4B} 5: RESET {0020} 6: DEVICE FRIENDLY NAME {0007} Friendly Name: Keyboard K850 7: BATTERY STATUS {1000} Battery: 90%, discharging, next level 50%. 8: CHANGE HOST {1814} Change Host : 3:shoaib-MacBookPro 9: HOSTS INFO {1815} Host 0 (paired): Host 1 (paired): Shoaib’s MacBook Pro Host 2 (paired): shoaib-MacBookPro 10: REPROG CONTROLS V4 {1B04} Key/Button Diversion (saved): {Volume Up:Regular, Volume Down:Regular, Mute:Regular, Play/Pause:Regular, Next:Regular, Previous:Regular, Calculator:Regular, Multiplatform Back:Regular, Screen Capture/Print Screen:Regular, Fn Down:Regular, Fn Up:Regular, Multiplatform Lock:Regular, Host Switch Channel 1:Regular, Host Switch Channel 2:Regular, Host Switch Channel 3:Regular, MultiPlatform Search:Regular, MultiPlatform Home/Mission Control:Regular, MultiPlatform Menu/Show/Hide Virtual Keyboard/Launchpad:Regular, Multi Platform Language Switch:Regular} Key/Button Diversion : {Volume Up:Regular, Volume Down:Regular, Mute:Regular, Play/Pause:Regular, Next:Regular, Previous:Regular, Calculator:Regular, Multiplatform Back:Regular, Screen Capture/Print Screen:Regular, Fn Down:Regular, Fn Up:Regular, Multiplatform Lock:Regular, Host Switch Channel 1:Regular, Host Switch Channel 2:Regular, Host Switch Channel 3:Regular, MultiPlatform Search:Regular, MultiPlatform Home/Mission Control:Regular, MultiPlatform Menu/Show/Hide Virtual Keyboard/Launchpad:Regular, Multi Platform Language Switch:Regular} 11: PERSISTENT REMAPPABLE ACTION {1C00} Persistent Key/Button Mapping : {Volume Up:Volume Up, Volume Down:Volume Down, Mute:Mute, Play/Pause:Play/Pause, Next:Scan Next Track, Previous:Scan Previous Track, Calculator:AL Calculator, Multiplatform Back:AC Back, Screen Capture/Print Screen:SYSRQ, Multiplatform Lock:Default, MultiPlatform Search:AC Search, MultiPlatform Home/Mission Control:AC Home, MultiPlatform Menu/Show/Hide Virtual Keyboard/Launchpad:COMPOSE, Multi Platform Language Switch:Alt+Shift+No Output} 12: K375S FN INVERSION {40A3} Swap Fx function (saved): False Swap Fx function : False 13: ENCRYPTION {4100} 14: LOCK KEY STATE {4220} 15: KEYBOARD DISABLE KEYS {4521} Disable keys (saved): {Caps Lock:False, Num Lock:False, Scroll Lock:False, Insert:False, Win:False} Disable keys : {Caps Lock:False, Num Lock:False, Scroll Lock:False, Insert:False, Win:False} 16: MULTIPLATFORM {4531} Set OS (saved): Windows Set OS : Windows Has 21 reprogrammable keys: 0: Host Switch Channel 1 , default: HostSwitch Channel 1 => HostSwitch Channel 1 nonstandard, divertable, persistently divertable, pos:0, group:0, group mask:empty reporting: default 1: Host Switch Channel 2 , default: HostSwitch Channel 2 => HostSwitch Channel 2 nonstandard, divertable, persistently divertable, pos:0, group:0, group mask:empty reporting: default 2: Host Switch Channel 3 , default: HostSwitch Channel 3 => HostSwitch Channel 3 nonstandard, divertable, persistently divertable, pos:0, group:0, group mask:empty reporting: default 3: MultiPlatform Home/Mission Control, default: Multiplatform Home/Mission Control => Multiplatform Home/Mission Control is FN, FN sensitive, reprogrammable, divertable, persistently divertable, pos:1, group:0, group mask:empty reporting: default 4: MultiPlatform Menu/Show/Hide Virtual Keyboard/Launchpad, default: Multiplatform Menu/Launchpad => Multiplatform Menu/Launchpad is FN, FN sensitive, reprogrammable, divertable, persistently divertable, pos:2, group:0, group mask:empty reporting: default 5: Multiplatform Back , default: MultiPlatform Back => MultiPlatform Back is FN, FN sensitive, reprogrammable, divertable, persistently divertable, pos:3, group:0, group mask:empty reporting: default 6: MultiPlatform Search , default: Multiplatform Search => Multiplatform Search is FN, FN sensitive, reprogrammable, divertable, persistently divertable, pos:4, group:0, group mask:empty reporting: default 7: Previous , default: Previous => Previous is FN, FN sensitive, divertable, persistently divertable, pos:5, group:0, group mask:empty reporting: default 8: Play/Pause , default: Play/Pause => Play/Pause is FN, FN sensitive, divertable, persistently divertable, pos:6, group:0, group mask:empty reporting: default 9: Next , default: Next => Next is FN, FN sensitive, divertable, persistently divertable, pos:7, group:0, group mask:empty reporting: default 10: Mute , default: Mute => Mute is FN, FN sensitive, divertable, persistently divertable, pos:8, group:0, group mask:empty reporting: default 11: Volume Down , default: Volume Down => Volume Down is FN, FN sensitive, divertable, persistently divertable, pos:9, group:0, group mask:empty reporting: default 12: Volume Up , default: Volume Up => Volume Up is FN, FN sensitive, divertable, persistently divertable, pos:10, group:0, group mask:empty reporting: default 13: Multiplatform Lock , default: WindowsLock => WindowsLock is FN, FN sensitive, reprogrammable, divertable, persistently divertable, pos:11, group:0, group mask:empty reporting: default 14: Multi Platform Language Switch, default: Multiplatform Language Switch => Multiplatform Language Switch is FN, FN sensitive, reprogrammable, divertable, persistently divertable, pos:12, group:0, group mask:empty reporting: default 15: Screen Capture/Print Screen, default: Screen Capture => Screen Capture is FN, reprogrammable, divertable, persistently divertable, pos:0, group:0, group mask:empty reporting: default 16: Calculator , default: Calculator => Calculator is FN, reprogrammable, divertable, persistently divertable, pos:0, group:0, group mask:empty reporting: default 17: Fn Up , default: unknown:0070 => unknown:0070 is FN, divertable, persistently divertable, pos:0, group:0, group mask:empty reporting: default 18: Fn Down , default: unknown:006F => unknown:006F is FN, divertable, persistently divertable, pos:0, group:0, group mask:empty reporting: default 19: F Lock , default: Do Nothing One => Do Nothing One is FN, pos:0, group:0, group mask:empty reporting: default 20: FN Key , default: Do Nothing One => Do Nothing One nonstandard, pos:0, group:0, group mask:empty reporting: default Has 14 persistent remappable keys: 0: MultiPlatform Home/Mission Control => Consumer: AC Home 1: MultiPlatform Menu/Show/Hide Virtual Keyboard/Launchpad => Key: COMPOSE 2: Multiplatform Back => Consumer: AC Back 3: MultiPlatform Search => Consumer: AC Search 4: Previous => Consumer: Scan Previous Track 5: Play/Pause => Consumer: Play/Pause 6: Next => Consumer: Scan Next Track 7: Mute => Consumer: Mute 8: Volume Down => Consumer: Volume Down 9: Volume Up => Consumer: Volume Up 10: Multiplatform Lock => Unknown 11: Multi Platform Language Switch => Key: Alt+Shift+No Output 12: Screen Capture/Print Screen => Key: SYSRQ 13: Calculator => Consumer: AL Calculator Battery: 90%, discharging, next level 50%. Solaar-1.1.14/docs/devices/Keyboard K600 TV 4078.txt000066400000000000000000000311601473525213700212020ustar00rootroot00000000000000Solaar version 1.1.7 1: Keyboard K600 TV Device path : /dev/hidraw3 WPID : 4078 Codename : K600 TV Kind : keyboard Protocol : HID++ 4.5 Polling rate : 8 ms (125Hz) Serial number: C4E873FC Model ID: B35340780000 Unit ID: CA575869 Bootloader: BOT 73.00.B0015 Firmware: MPK 08.00.B0015 Other: Other: Supports 36 HID++ 2.0 features: 0: ROOT {0000} V0 1: FEATURE SET {0001} V0 2: DEVICE FW VERSION {0003} V2 Firmware: Bootloader BOT 73.00.B0015 00001E30DA07 Firmware: Firmware MPK 08.00.B0015 40781E30DA07 Firmware: Other Firmware: Other Unit ID: CA575869 Model ID: B35340780000 Transport IDs: {'btleid': 'B353', 'wpid': '4078'} 3: DEVICE NAME {0005} V0 Name: Keyboard K600 TV Kind: keyboard 4: WIRELESS DEVICE STATUS {1D4B} V0 5: RESET {0020} V0 6: DEVICE FRIENDLY NAME {0007} V0 Friendly Name: Keyboard K600 T 7: BATTERY STATUS {1000} V1 Battery: 90%, discharging, next level 50%. 8: REPROG CONTROLS V4 {1B04} V4 Desvio de Tecla/Botón (saved): {Fn Left Click:Normal, Multiplatform App Switch:Normal, Multiplatform Back:Normal, Multiplatform Insert:Normal, Brightness Down:Normal, Brightness Up:Normal, Host Switch Channel 1:Normal, Host Switch Channel 2:Normal, Host Switch Channel 3:Normal, MultiPlatform Search:Normal, MultiPlatform Home/Mission Control:Normal, MultiPlatform Menu/Show/Hide Virtual Keyboard/Launchpad:Normal, Previous Fn:Normal, Play/Pause Fn:Normal, Next Fn:Normal, Mute Fn:Normal, Volume Down Fn:Normal, Volume Up Fn:Normal} Desvio de Tecla/Botón : {Fn Left Click:Normal, Multiplatform App Switch:Normal, Multiplatform Back:Normal, Multiplatform Insert:Normal, Brightness Down:Normal, Brightness Up:Normal, Host Switch Channel 1:Normal, Host Switch Channel 2:Normal, Host Switch Channel 3:Normal, MultiPlatform Search:Normal, MultiPlatform Home/Mission Control:Normal, MultiPlatform Menu/Show/Hide Virtual Keyboard/Launchpad:Normal, Previous Fn:Normal, Play/Pause Fn:Normal, Next Fn:Normal, Mute Fn:Normal, Volume Down Fn:Normal, Volume Up Fn:Normal} 9: PERSISTENT REMAPPABLE ACTION {1C00} V0 Persistent Key/Button Mapping : {Multiplatform App Switch:No Output (only as default), Multiplatform Back:No Output (only as default), Multiplatform Insert:No Output (only as default), Brightness Down:No Output (only as default), Brightness Up:No Output (only as default), MultiPlatform Search:No Output (only as default), MultiPlatform Home/Mission Control:No Output (only as default), MultiPlatform Menu/Show/Hide Virtual Keyboard/Launchpad:No Output (only as default), Previous Fn:No Output (only as default), Play/Pause Fn:No Output (only as default), Next Fn:No Output (only as default), Mute Fn:No Output (only as default), Volume Down Fn:No Output (only as default), Volume Up Fn:No Output (only as default)} 10: SWAP BUTTON CANCEL {2005} V0 11: CHANGE HOST {1814} V1 Cambiar Host : 1:romanescu 12: HOSTS INFO {1815} V1 Host 0 (paired): romanescu Host 1 (unpaired): Host 2 (unpaired): 13: NEW FN INVERSION {40A2} V0 Fn-swap: enabled Fn-swap default: enabled Intercambiar función Fx (saved): True Intercambiar función Fx : True 14: ENCRYPTION {4100} V0 15: LOCK KEY STATE {4220} V0 16: KEYBOARD DISABLE KEYS {4521} V0 Desactivar teclas (saved): {Caps Lock:False, Insert:False, Win:False} Desactivar teclas : {Caps Lock:False, Insert:False, Win:False} 17: MULTIPLATFORM {4531} V1 Especificar SO (saved): Windows Especificar SO : Windows 18: TOUCHPAD RAW XY {6100} V0 19: GESTURE 2 {6501} V0 Gestos (saved): {1: True, 2: True, 4: True, 5: True, 10: True, 30: True, 34: False, 42: True, 43: True, 45: False, 84: True} Gestos : {1: True, 2: True, 4: True, 5: True, 30: True, 10: True, 45: False, 42: True, 43: True, 84: True, 34: False} Gestures Diversion (saved): {1: False, 2: False, 4: False, 5: False, 10: False, 44: False, 84: False, 85: False, 100: False} Gestures Diversion : {1: False, 2: False, 4: False, 5: False, 10: False, 44: False, 84: False, 85: False, 100: False} Parámetros de gestos (saved): {4: {'scale': 256}} Parámetros de gestos : {4: {'scale': 256}} 20: DFUCONTROL SIGNED {00C2} V0 21: DEVICE RESET {1802} V0 internal, hidden 22: unknown:1803 {1803} V0 internal, hidden 23: CONFIG DEVICE PROPS {1806} V5 internal, hidden 24: unknown:1813 {1813} V0 internal, hidden 25: OOBSTATE {1805} V0 internal, hidden 26: unknown:1830 {1830} V0 internal, hidden 27: unknown:1890 {1890} V2 internal, hidden 28: unknown:1891 {1891} V2 internal, hidden 29: unknown:18A1 {18A1} V0 internal, hidden 30: unknown:1DF3 {1DF3} V0 internal, hidden 31: unknown:1E00 {1E00} V0 hidden 32: unknown:1EB0 {1EB0} V0 internal, hidden 33: unknown:1861 {1861} V0 internal, hidden 34: unknown:18B0 {18B0} V0 internal, hidden 35: unknown:1F11 {1F11} V0 internal, hidden Has 19 reprogrammable keys: 0: Host Switch Channel 1 , default: HostSwitch Channel 1 => HostSwitch Channel 1 is FN, FN sensitive, divertable, persistently divertable, pos:1, group:0, group mask:empty reporting: default 1: Host Switch Channel 2 , default: HostSwitch Channel 2 => HostSwitch Channel 2 is FN, FN sensitive, divertable, persistently divertable, pos:2, group:0, group mask:empty reporting: default 2: Host Switch Channel 3 , default: HostSwitch Channel 3 => HostSwitch Channel 3 is FN, FN sensitive, divertable, persistently divertable, pos:3, group:0, group mask:empty reporting: default 3: MultiPlatform Menu/Show/Hide Virtual Keyboard/Launchpad, default: Multiplatform Menu/Launchpad => Multiplatform Menu/Launchpad is FN, FN sensitive, reprogrammable, divertable, persistently divertable, pos:4, group:0, group mask:empty reporting: default 4: Brightness Down , default: Brightness Down => Brightness Down is FN, FN sensitive, reprogrammable, divertable, persistently divertable, pos:5, group:0, group mask:empty reporting: default 5: Brightness Up , default: Brightness Up => Brightness Up is FN, FN sensitive, reprogrammable, divertable, persistently divertable, pos:6, group:0, group mask:empty reporting: default 6: Previous Fn , default: Previous => Previous is FN, FN sensitive, reprogrammable, divertable, persistently divertable, pos:7, group:0, group mask:empty reporting: default 7: Play/Pause Fn , default: Play/Pause => Play/Pause is FN, FN sensitive, reprogrammable, divertable, persistently divertable, pos:8, group:0, group mask:empty reporting: default 8: Next Fn , default: Next => Next is FN, FN sensitive, reprogrammable, divertable, persistently divertable, pos:9, group:0, group mask:empty reporting: default 9: Mute Fn , default: Mute => Mute is FN, FN sensitive, reprogrammable, divertable, persistently divertable, pos:10, group:0, group mask:empty reporting: default 10: Volume Down Fn , default: Volume Down => Volume Down is FN, FN sensitive, reprogrammable, divertable, persistently divertable, pos:11, group:0, group mask:empty reporting: default 11: Volume Up Fn , default: Volume Up => Volume Up is FN, FN sensitive, reprogrammable, divertable, persistently divertable, pos:12, group:0, group mask:empty reporting: default 12: Multiplatform Insert , default: Switch Language => Switch Language is FN, FN sensitive, reprogrammable, divertable, persistently divertable, pos:0, group:0, group mask:empty reporting: default 13: MultiPlatform Home/Mission Control, default: Multiplatform Home/Mission Control => Multiplatform Home/Mission Control nonstandard, reprogrammable, divertable, persistently divertable, pos:0, group:0, group mask:empty reporting: default 14: Multiplatform Back , default: MultiPlatform Back => MultiPlatform Back nonstandard, reprogrammable, divertable, persistently divertable, pos:0, group:0, group mask:empty reporting: default 15: Multiplatform App Switch , default: MultiPlatform App Switch => MultiPlatform App Switch nonstandard, reprogrammable, divertable, persistently divertable, pos:0, group:0, group mask:empty reporting: default 16: MultiPlatform Search , default: Multiplatform Search => Multiplatform Search nonstandard, reprogrammable, divertable, persistently divertable, pos:0, group:0, group mask:empty reporting: default 17: Fn Left Click , default: ShowUI => ShowUI divertable, pos:0, group:0, group mask:empty reporting: default 18: FN Key , default: Do Nothing One => Do Nothing One nonstandard, pos:0, group:0, group mask:empty reporting: default Has 14 persistent remappable keys: 0: MultiPlatform Menu/Show/Hide Virtual Keyboard/Launchpad => None 1: Brightness Down => None 2: Brightness Up => None 3: Previous Fn => None 4: Play/Pause Fn => None 5: Next Fn => None 6: Mute Fn => None 7: Volume Down Fn => None 8: Volume Up Fn => None 9: Multiplatform Insert => None 10: MultiPlatform Home/Mission Control => None 11: Multiplatform Back => None 12: Multiplatform App Switch => None 13: MultiPlatform Search => None Has 16 gesture(s), 1 param(s) and 5 spec(s): Track1Finger Enabled(None): None Diverted:(None) None TapGestures Enabled(None): None Diverted:(None) None Tap1Finger Enabled( 0): True Diverted:( 0) False Tap2Finger Enabled( 1): True Diverted:( 1) False Click1Finger Enabled( 2): True Diverted:( 2) False Click2Finger Enabled( 3): True Diverted:( 3) False TapDrag1Finger Enabled( 4): True Diverted:(None) None DoubleTap1Finger Enabled( 5): True Diverted:( 4) False Scroll2FingerStateless Enabled(None): None Diverted:( 5) False NaturalScrolling Enabled( 6): False Diverted:(None) None Scroll2FingerHoriz Enabled( 7): True Diverted:(None) None Scroll2FingerVert Enabled( 8): True Diverted:(None) None Zoom2FingerStateless Enabled( 9): True Diverted:( 6) False FnClickGestureSuppression Enabled( 10): False Diverted:(None) None TwoFingersPresent Enabled(None): None Diverted:( 7) False DeviceSpecificRawData Enabled(None): None Diverted:( 8) False ScaleFactor Value ( 0): 256 [Default: 256] period unit Spec ( 3): 2048 DVI field width Spec ( 1): 8 field widths Spec ( 2): 8 multiplier Spec ( 5): None resolution Spec ( 4): 4 Battery: 90%, discharging, next level 50%. Solaar-1.1.14/docs/devices/LIFT For Business B033.txt000066400000000000000000000124011473525213700215520ustar00rootroot00000000000000solaar version 1.1.8 2: LIFT For Business Device path : None WPID : B033 Codename : LIFT B Kind : mouse Protocol : HID++ 4.5 Serial number: A67F904D Model ID: B03300000000 Unit ID: A67F904D Bootloader: BL1 56.01.B0010 Firmware: RBM 21.01.B0010 Other: The power switch is located on the (unknown). Supports 32 HID++ 2.0 features: 0: ROOT {0000} V0 1: FEATURE SET {0001} V0 2: DEVICE FW VERSION {0003} V4 Firmware: Bootloader BL1 56.01.B0010 B033B0706FCD Firmware: Firmware RBM 21.01.B0010 B033B0706FCD Firmware: Other Unit ID: A67F904D Model ID: B03300000000 Transport IDs: {'btleid': 'B033'} 3: DEVICE NAME {0005} V0 Name: LIFT For Business Kind: mouse 4: WIRELESS DEVICE STATUS {1D4B} V0 5: CONFIG CHANGE {0020} V0 6: CRYPTO ID {0021} V1 7: DEVICE FRIENDLY NAME {0007} V0 Friendly Name: LIFT B 8: UNIFIED BATTERY {1004} V3 Battery: 100%, discharging. 9: REPROG CONTROLS V4 {1B04} V5 Key/Button Actions (saved): {Middle Button:Mouse Middle Button, Back Button:Mouse Back Button, Forward Button:Mouse Forward Button, DPI Switch:DPI Switch} Key/Button Actions : {Middle Button:Mouse Middle Button, Back Button:Mouse Back Button, Forward Button:Mouse Forward Button, DPI Switch:DPI Switch} Key/Button Diversion (saved): {Middle Button:Regular, Back Button:Regular, Forward Button:Regular, DPI Switch:Regular} Key/Button Diversion : {Middle Button:Regular, Back Button:Regular, Forward Button:Regular, DPI Switch:Regular} 10: CHANGE HOST {1814} V1 Change Host : 1:feathora 11: HOSTS INFO {1815} V2 Host 0 (paired): feathora Host 1 (unpaired): Host 2 (unpaired): 12: XY STATS {2250} V1 13: LOWRES WHEEL {2130} V0 Wheel Reports: HID Scroll Wheel Diversion (saved): False Scroll Wheel Diversion : False 14: ADJUSTABLE DPI {2201} V2 Sensitivity (DPI) (saved): 1600 Sensitivity (DPI) : 1600 15: DFUCONTROL {00C3} V0 16: DEVICE RESET {1802} V0 internal, hidden, unknown:000010 17: unknown:1803 {1803} V0 internal, hidden, unknown:000010 18: CONFIG DEVICE PROPS {1806} V8 internal, hidden, unknown:000010 19: unknown:1816 {1816} V0 internal, hidden, unknown:000010 20: OOBSTATE {1805} V0 internal, hidden 21: unknown:1830 {1830} V0 internal, hidden, unknown:000010 22: unknown:1891 {1891} V7 internal, hidden, unknown:000008 23: unknown:18A1 {18A1} V0 internal, hidden, unknown:000010 24: unknown:1E00 {1E00} V0 hidden 25: unknown:1E02 {1E02} V0 internal, hidden 26: unknown:1E22 {1E22} V1 internal, hidden, unknown:000010 27: unknown:1602 {1602} V0 28: unknown:1EB0 {1EB0} V0 internal, hidden, unknown:000010 29: unknown:1861 {1861} V1 internal, hidden, unknown:000010 30: unknown:18B1 {18B1} V0 internal, hidden, unknown:000010 31: unknown:920A {920A} V0 internal, hidden, unknown:000010 Has 7 reprogrammable keys: 0: Left Button , default: Left Click => Left Click mse, analytics key events, pos:0, group:1, group mask:empty reporting: default 1: Right Button , default: Right Click => Right Click mse, analytics key events, pos:0, group:1, group mask:empty reporting: default 2: Middle Button , default: Mouse Middle Button => Mouse Middle Button mse, reprogrammable, divertable, raw XY, analytics key events, pos:0, group:3, group mask:g1,g2,g3 reporting: default 3: Back Button , default: Mouse Back Button => Mouse Back Button mse, reprogrammable, divertable, raw XY, analytics key events, unknown:000800, pos:0, group:2, group mask:g1,g2 reporting: default 4: Forward Button , default: Mouse Forward Button => Mouse Forward Button mse, reprogrammable, divertable, raw XY, analytics key events, unknown:000800, pos:0, group:2, group mask:g1,g2 reporting: default 5: DPI Switch , default: DPI Switch => DPI Switch mse, reprogrammable, divertable, raw XY, analytics key events, pos:0, group:3, group mask:g1,g2,g3 reporting: default 6: Virtual Gesture Button , default: Virtual Gesture Button => Virtual Gesture Button divertable, virtual, raw XY, force raw XY, pos:0, group:4, group mask:empty reporting: default Battery: 100%, discharging. Solaar-1.1.14/docs/devices/LIFT VERTICAL ERGONOMIC MOUSE B031.txt000066400000000000000000000124021473525213700226340ustar00rootroot00000000000000Solaar version 1.1.4 2: LIFT VERTICAL ERGONOMIC MOUSE Device path : None WPID : B031 Codename : LIFT Kind : mouse Protocol : HID++ 4.5 Serial number: E86E35D0 Model ID: B03100000000 Unit ID: E86E35D0 Bootloader: BL1 56.00.B0008 Firmware: RBM 21.00.B0008 Other: The power switch is located on the (unknown). Supports 31 HID++ 2.0 features: 0: ROOT {0000} 1: FEATURE SET {0001} 2: DEVICE FW VERSION {0003} Firmware: Bootloader BL1 56.00.B0008 B0317599BD37 Firmware: Firmware RBM 21.00.B0008 B0317599BD37 Firmware: Other Unit ID: E86E35D0 Model ID: B03100000000 Transport IDs: {'btleid': 'B031'} 3: DEVICE NAME {0005} Name: LIFT VERTICAL ERGONOMIC MOUSE Kind: mouse 4: WIRELESS DEVICE STATUS {1D4B} 5: RESET {0020} 6: CRYPTO ID {0021} 7: DEVICE FRIENDLY NAME {0007} Friendly Name: LIFT 8: UNIFIED BATTERY {1004} 9: REPROG CONTROLS V4 {1B04} DPI Sliding Adjustment (saved): DPI Switch DPI Sliding Adjustment : Off Mouse Gestures (saved): Off Mouse Gestures : Off Key/Button Actions (saved): {Middle Button:Mouse Middle Button, Back Button:Mouse Back Button, Forward Button:Mouse Forward Button, DPI Switch:DPI Switch} Key/Button Actions : {Middle Button:Mouse Middle Button, Back Button:Mouse Back Button, Forward Button:Mouse Forward Button, DPI Switch:DPI Switch} Key/Button Diversion (saved): {Middle Button:Regular, Back Button:Regular, Forward Button:Regular, DPI Switch:Diverted} Key/Button Diversion : {Middle Button:Regular, Back Button:Regular, Forward Button:Regular, DPI Switch:Diverted} 10: CHANGE HOST {1814} Change Host : 1:gtronick 11: HOSTS INFO {1815} Host 0 (paired): gtronick Host 1 (paired): LAP0707 Host 2 (unpaired): 12: XY STATS {2250} 13: LOWRES WHEEL {2130} Wheel Reports: HID Scroll Wheel Diversion (saved): False Scroll Wheel Diversion : False 14: ADJUSTABLE DPI {2201} Sensitivity (DPI) (saved): 2400 Sensitivity (DPI) : 2400 15: DFUCONTROL {00C3} 16: DEVICE RESET {1802} internal, hidden, unknown:000010 17: unknown:1803 {1803} internal, hidden, unknown:000010 18: CONFIG DEVICE PROPS {1806} internal, hidden, unknown:000010 19: unknown:1816 {1816} internal, hidden, unknown:000010 20: OOBSTATE {1805} internal, hidden 21: unknown:1830 {1830} internal, hidden, unknown:000010 22: unknown:1891 {1891} internal, hidden, unknown:000008 23: unknown:18A1 {18A1} internal, hidden, unknown:000010 24: unknown:1E00 {1E00} hidden 25: unknown:1E02 {1E02} internal, hidden 26: unknown:1E22 {1E22} internal, hidden, unknown:000010 27: unknown:1602 {1602} 28: unknown:1EB0 {1EB0} internal, hidden, unknown:000010 29: unknown:1861 {1861} internal, hidden, unknown:000010 30: unknown:18B1 {18B1} internal, hidden, unknown:000010 Has 7 reprogrammable keys: 0: Left Button , default: Left Click => Left Click mse, analytics key events, pos:0, group:1, group mask:empty reporting: default 1: Right Button , default: Right Click => Right Click mse, analytics key events, pos:0, group:1, group mask:empty reporting: default 2: Middle Button , default: Mouse Middle Button => Mouse Middle Button mse, reprogrammable, divertable, raw XY, analytics key events, pos:0, group:3, group mask:g1,g2,g3 reporting: default 3: Back Button , default: Mouse Back Button => Mouse Back Button mse, reprogrammable, divertable, raw XY, analytics key events, unknown:000800, pos:0, group:2, group mask:g1,g2 reporting: default 4: Forward Button , default: Mouse Forward Button => Mouse Forward Button mse, reprogrammable, divertable, raw XY, analytics key events, unknown:000800, pos:0, group:2, group mask:g1,g2 reporting: default 5: DPI Switch , default: DPI Switch => DPI Switch mse, reprogrammable, divertable, raw XY, analytics key events, pos:0, group:3, group mask:g1,g2,g3 reporting: diverted, raw XY diverted 6: Virtual Gesture Button , default: Virtual Gesture Button => Virtual Gesture Button divertable, virtual, raw XY, force raw XY, pos:0, group:4, group mask:empty reporting: default Battery: 100%, discharging. Solaar-1.1.14/docs/devices/Lightspeed Receiver C539.txt000066400000000000000000000006561473525213700223730ustar00rootroot00000000000000Solaar version 1.1.4 Lightspeed Receiver Device path : /dev/hidraw8 USB id : 046d:C539 Serial : C1469DD4 Firmware : 39.06.B0040 Bootloader : 02.09 Other : AA.BE Has 1 paired device(s) out of a maximum of 1. Notifications: wireless, software present (0x000900) Device activity counters: (empty) Seen paired with G502 Gaming Mouse 407F Seen paired with G Pro Wireless Gaming Mouse 4079 Solaar-1.1.14/docs/devices/Lightspeed Receiver C53A.txt000066400000000000000000000007101473525213700223720ustar00rootroot00000000000000Lightspeed Receiver Device path : /dev/hidraw9 USB id : 046d:C53A Serial : 5B2B9A98 Firmware : 40.03.B0014 Bootloader : 02.09 Other : AA.CA Has 2 paired device(s) out of a maximum of 1. Notifications: wireless, software present (0x000900) Device activity counters: (empty) Seen as part of a G PowerPlay Wireless Mouse Pad with a Candy companion chip paired a number 7 Seen paired with a G502 Gaming Mouse 407F. Solaar-1.1.14/docs/devices/Lightspeed Receiver C53D.txt000066400000000000000000000006231473525213700224000ustar00rootroot00000000000000Solaar version 1.1.3 Lightspeed Receiver Device path : /dev/hidraw2 USB id : 046d:C53D Serial : C271A999 Firmware : 43.01.B0009 Bootloader : 02.10 Other : AA.D2 Has 1 paired device(s) out of a maximum of 1. Notifications: wireless, software present (0x000900) Device activity counters: (empty) Seen paired with G613 Wireless Mechanical Gaming Keyboard 4065 Solaar-1.1.14/docs/devices/Lightspeed Receiver C53F.txt000066400000000000000000000004641473525213700224050ustar00rootroot00000000000000Lightspeed Receiver Device path : /dev/hidraw3 USB id : 046d:C53F Serial : Firmware : 44.01.B0005 Bootloader : 00.02 Other : AA.DE Has 0 paired device(s) out of a maximum of 1. Notifications: wireless, software present (0x000900) Device activity counters: (empty) Solaar-1.1.14/docs/devices/Lightspeed Receiver C541.txt000066400000000000000000000006271473525213700223620ustar00rootroot00000000000000Solaar version 1.1.1 Lightspeed Receiver Device path : /dev/hidraw4 USB id : 046d:C541 Serial : 18E8EC31 Firmware : 01.01.B0027 Bootloader : 01.27 Other : B9.BC Has 1 paired device(s) out of a maximum of 1. Notifications: wireless, software present (0x000900) Device activity counters: (empty) Seen paired with G915 WIRELESS RGB Mechanical Gaming Keyboard 407E Solaar-1.1.14/docs/devices/Lightspeed Receiver C545.txt000066400000000000000000000006431473525213700223640ustar00rootroot00000000000000Solaar version 1.1.4 Lightspeed Receiver Device path : /dev/hidraw5 USB id : 046d:C545 Serial : AD1567FD Firmware : 02.01.B0006 Bootloader : 00.01 Other : 3D.88 Has 1 paired device(s) out of a maximum of 2. Notifications: wireless, software present (0x000900) Device activity counters: 1=22 Seen paired with G915 TKL LIGHTSPEED Wireless RGB Mechanical Gaming Keyboard 408E Solaar-1.1.14/docs/devices/Lightspeed Receiver C547.txt000066400000000000000000000005731473525213700223700ustar00rootroot00000000000000Solaar version 1.1.3 Lightspeed Receiver Device path : /dev/hidraw6 USB id : 046d:C547 Serial : 9B341CD1 Firmware : 04.02.B0009 Bootloader : 00.05 Other : F3.24 Has 1 paired device(s) out of a maximum of 2. Notifications: wireless, software present (0x000900) Device activity counters: 1=171 Seen paired with the PRO X Wireless 4093 Solaar-1.1.14/docs/devices/Logi Pop Keys B365.txt000066400000000000000000000213451473525213700210550ustar00rootroot00000000000000solaar version 1.1.8 Bolt Receiver Device path : /dev/hidraw2 USB id : 046d:C548 Serial : 31454343464242444143334635323035 Has 1 paired device(s) out of a maximum of 6. Notifications: wireless, software present (0x000900) Device activity counters: 1=28 1: Logi POP Keys Device path : None WPID : B365 Codename : Logi POP Keys Kind : keyboard Protocol : HID++ 4.5 Serial number: D1F99582 Model ID: B36500000000 Unit ID: D1F99582 Bootloader: BL1 44.01.B0008 Firmware: RBK 69.01.B0008 Other: The power switch is located on the (unknown). Supports 31 HID++ 2.0 features: 0: ROOT {0000} V0 1: FEATURE SET {0001} V0 2: DEVICE FW VERSION {0003} V4 Firmware: Bootloader BL1 44.01.B0008 B3652BE8BAF4 Firmware: Firmware RBK 69.01.B0008 B3652BE8BAF4 Firmware: Other Unit ID: D1F99582 Model ID: B36500000000 Transport IDs: {'btleid': 'B365'} 3: DEVICE NAME {0005} V0 Name: Logi POP Keys Kind: keyboard 4: WIRELESS DEVICE STATUS {1D4B} V0 5: CONFIG CHANGE {0020} V0 6: DEVICE FRIENDLY NAME {0007} V0 Friendly Name: Logi POP Keys 7: UNIFIED BATTERY {1004} V3 Battery: 100%, discharging. 8: REPROG CONTROLS V4 {1B04} V5 Key/Button Diversion (saved): {Show Desktop:Regular, Previous Fn:Regular, Play/Pause Fn:Regular, Next Fn:Regular, Mute Fn:Regular, Volume Down Fn:Regular, Volume Up Fn:Regular, Voice Dictation:Regular, Emoji Smiley Heart Eyes:Regular, Emoji Crying Face:Regular, Emoji Smiley:Regular, Emoji Smilie With Tears:Regular, Open Emoji Panel:Regular, Snipping Tool:Regular, Mute Microphone:Regular} Key/Button Diversion : {Show Desktop:Regular, Previous Fn:Regular, Play/Pause Fn:Regular, Next Fn:Regular, Mute Fn:Regular, Volume Down Fn:Regular, Volume Up Fn:Regular, Voice Dictation:Regular, Emoji Smiley Heart Eyes:Regular, Emoji Crying Face:Regular, Emoji Smiley:Regular, Emoji Smilie With Tears:Regular, Open Emoji Panel:Regular, Snipping Tool:Regular, Mute Microphone:Regular} 9: CHANGE HOST {1814} V1 Change Host : 1:astra 10: HOSTS INFO {1815} V2 Host 0 (paired): astra Host 1 (unpaired): Host 2 (unpaired): 11: K375S FN INVERSION {40A3} V0 Swap Fx function (saved): False Swap Fx function : False 12: LOCK KEY STATE {4220} V0 13: KEYBOARD DISABLE KEYS {4521} V0 Disable keys (saved): {Caps Lock:False, Insert:False, Win:False} Disable keys : {Caps Lock:False, Insert:False, Win:False} 14: MULTIPLATFORM {4531} V1 Set OS (saved): Windows Set OS : Windows 15: KEYBOARD LAYOUT 2 {4540} V0 16: DFUCONTROL {00C3} V0 17: DEVICE RESET {1802} V0 internal, hidden, unknown:000010 18: unknown:1803 {1803} V0 internal, hidden, unknown:000010 19: CONFIG DEVICE PROPS {1806} V8 internal, hidden, unknown:000010 20: unknown:1816 {1816} V0 internal, hidden, unknown:000010 21: OOBSTATE {1805} V0 internal, hidden 22: unknown:1830 {1830} V0 internal, hidden, unknown:000010 23: unknown:1891 {1891} V7 internal, hidden, unknown:000008 24: unknown:18A1 {18A1} V0 internal, hidden, unknown:000010 25: unknown:1E00 {1E00} V0 hidden 26: unknown:1E02 {1E02} V0 internal, hidden 27: unknown:1602 {1602} V0 28: unknown:1EB0 {1EB0} V0 internal, hidden, unknown:000010 29: unknown:1861 {1861} V1 internal, hidden, unknown:000010 30: unknown:18B0 {18B0} V0 internal, hidden, unknown:000010 Has 20 reprogrammable keys: 0: Host Switch Channel 1 , default: HostSwitch Channel 1 => HostSwitch Channel 1 is FN, FN sensitive, analytics key events, pos:1, group:0, group mask:empty reporting: default 1: Host Switch Channel 2 , default: HostSwitch Channel 2 => HostSwitch Channel 2 is FN, FN sensitive, analytics key events, pos:2, group:0, group mask:empty reporting: default 2: Host Switch Channel 3 , default: HostSwitch Channel 3 => HostSwitch Channel 3 is FN, FN sensitive, analytics key events, pos:3, group:0, group mask:empty reporting: default 3: Show Desktop , default: Show Desktop => Show Desktop is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:4, group:0, group mask:empty reporting: default 4: Snipping Tool , default: Snipping Tool => Snipping Tool is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:5, group:0, group mask:empty reporting: default 5: Mute Microphone , default: Mute Microphone => Mute Microphone is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:6, group:0, group mask:empty reporting: default 6: Previous Fn , default: Previous => Previous is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:7, group:0, group mask:empty reporting: default 7: Play/Pause Fn , default: Play/Pause => Play/Pause is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:8, group:0, group mask:empty reporting: default 8: Next Fn , default: Next => Next is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:9, group:0, group mask:empty reporting: default 9: Mute Fn , default: Mute => Mute is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:10, group:0, group mask:empty reporting: default 10: Volume Down Fn , default: Volume Down => Volume Down is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:11, group:0, group mask:empty reporting: default 11: Volume Up Fn , default: Volume Up => Volume Up is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:12, group:0, group mask:empty reporting: default 12: Voice Dictation , default: Voice Dictation => Voice Dictation nonstandard, reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 13: Emoji Smiley Heart Eyes , default: Emoji Smiling Face With Heart Shaped Eyes => Emoji Smiling Face With Heart Shaped Eyes nonstandard, reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 14: Emoji Crying Face , default: Emoji Loudly Crying Face => Emoji Loudly Crying Face nonstandard, reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 15: Emoji Smiley , default: Emoji Smiley => Emoji Smiley nonstandard, reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 16: Emoji Smilie With Tears , default: Emoji Smiley With Tears => Emoji Smiley With Tears nonstandard, reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 17: Open Emoji Panel , default: Open Emoji Panel => Open Emoji Panel nonstandard, reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 18: F Lock , default: Do Nothing One => Do Nothing One is FN, analytics key events, pos:0, group:0, group mask:empty reporting: default 19: FN Key , default: Do Nothing One => Do Nothing One nonstandard, analytics key events, pos:0, group:0, group mask:empty reporting: default Battery: 100%, discharging. Solaar-1.1.14/docs/devices/Logitech PRO X Wireless Gaming Headset 0ABA.txt000066400000000000000000000034551473525213700254440ustar00rootroot00000000000000 1: Logitech PRO X Wireless Gaming Headset Device path : /dev/hidraw2 USB id : 046d:0ABA Codename : PRO Headset Kind : headset Protocol : HID++ 4.2 Serial number: Model ID: 000000000ABA Unit ID: FFFFFFFF Firmware: U1 12.01.B0203 Supports 6 HID++ 2.0 features: 0: ROOT {0000} 1: FEATURE SET {0001} 2: DEVICE FW VERSION {0003} Firmware: Firmware U1 12.01.B0203 0ABA Unit ID: FFFFFFFF Model ID: 000000000ABA Transport IDs: {'btid': '0000', 'btleid': '0000'} 3: DEVICE NAME {0005} Name: PRO X Wireless Gaming Headset Kind: None 4: EQUALIZER {8310} 5: SIDETONE {8300} 6: ADC MEASUREMENT {1F20} Battery status unavailable. 2: Logitech PRO X Wireless Gaming Headset Device path : /dev/hidraw5 USB id : 046d:0ABA Codename : PRO Headset Kind : headset Protocol : HID++ 4.2 Serial number: Model ID: 000000000ABA Unit ID: FFFFFFFF Firmware: U1 12.01.B0203 Supports 6 HID++ 2.0 features: 0: ROOT {0000} 1: FEATURE SET {0001} 2: DEVICE FW VERSION {0003} Firmware: Firmware U1 12.01.B0203 0ABA Unit ID: FFFFFFFF Model ID: 000000000ABA Transport IDs: {'btid': '0000', 'btleid': '0000'} 3: DEVICE NAME {0005} Name: PRO X Wireless Gaming Headset Kind: None 4: EQUALIZER {8310} 5: SIDETONE {8300} Sidetone (saved): 0 Sidetone : 0 Battery status unavailable. Solaar-1.1.14/docs/devices/M720 Triathlon Multi-Device Mouse 405E.txt000066400000000000000000000137641473525213700244470ustar00rootroot00000000000000Solaar version 1.1.1 1: M720 Triathlon Multi-Device Mouse Device path : /dev/hidraw1 WPID : 405E Codename : M720 Triathlon Kind : mouse Protocol : HID++ 4.5 Polling rate : 8 ms (125Hz) Serial number: 205F2717 Model ID: B015405E0000 Unit ID: C738473C Bootloader: BOT 34.01.B0005 Firmware: MPM 04.10.B0013 Other: The power switch is located on the base. Supports 36 HID++ 2.0 features: 0: ROOT {0000} 1: FEATURE SET {0001} 2: DEVICE FW VERSION {0003} Firmware: Bootloader BOT 34.01.B0005 000000A96719 Firmware: Firmware MPM 04.10.B0013 405E00A96719 Firmware: Other Unit ID: C738473C Model ID: B015405E0000 Transport IDs: {'btleid': 'B015', 'wpid': '405E'} 3: DEVICE NAME {0005} Name: M720 Triathlon Multi-Device Mouse Kind: mouse 4: WIRELESS DEVICE STATUS {1D4B} 5: RESET {0020} 6: CRYPTO ID {0021} 7: DEVICE FRIENDLY NAME {0007} Friendly Name: M720 Triathlon 8: BATTERY STATUS {1000} Battery: 90%, discharging, next level 50%. 9: CHANGE HOST {1814} Change Host : 2:desktop 10: HOSTS INFO {1815} Host 0 (paired): rpi Host 1 (paired): desktop Host 2 (paired): laptop 11: REPROG CONTROLS V4 {1B04} Key/Button Actions (saved): {'208': 208, '80': 80, '81': 81, '82': 82, '83': 83, '86': 86, '91': 91, '93': 93} Key/Button Actions : {'80': 80, '81': 81, '82': 82, '83': 83, '86': 86, '91': 91, '93': 93, '208': 208} Key/Button Diversion (saved): {'208': 0, '82': 0, '83': 0, '86': 0, '91': 0, '93': 0} Key/Button Diversion : {'82': 0, '83': 0, '86': 0, '91': 0, '93': 0, '208': 0} 12: PERSISTENT REMAPPABLE ACTION {1C00} 13: POINTER SPEED {2205} Pointer Speed: 1.0 Sensitivity (Pointer Speed) (saved): 256 Sensitivity (Pointer Speed) : 256 14: VERTICAL SCROLLING {2100} Roller type: 3G Ratchet per turn: 24 Scroll lines: 0 15: DFUCONTROL UNSIGNED {00C1} 16: DFUCONTROL SIGNED {00C2} 17: DEVICE RESET {1802} internal, hidden 18: unknown:1803 {1803} internal, hidden 19: CONFIG DEVICE PROPS {1806} internal, hidden 20: OOBSTATE {1805} internal, hidden 21: unknown:1813 {1813} internal, hidden 22: unknown:1830 {1830} internal, hidden 23: unknown:1861 {1861} internal, hidden 24: unknown:1890 {1890} internal, hidden 25: unknown:1891 {1891} internal, hidden 26: unknown:18A1 {18A1} internal, hidden 27: unknown:1DF3 {1DF3} internal, hidden 28: unknown:1E00 {1E00} hidden 29: unknown:1EB0 {1EB0} internal, hidden 30: unknown:18B1 {18B1} internal, hidden 31: unknown:1850 {1850} internal, hidden 32: unknown:1E22 {1E22} 33: unknown:1F03 {1F03} internal, hidden 34: unknown:18C0 {18C0} internal, hidden 35: HIRES WHEEL {2121} Multiplier: 8 Has invert: Normal wheel motion Has ratchet switch: Normal wheel mode High resolution mode HID notification Scroll Wheel Direction (saved): False Scroll Wheel Direction : False Scroll Wheel Resolution (saved): True Scroll Wheel Resolution : True Has 9 reprogrammable keys: 0: Left Button , default: Left Click => Left Click mse, reprogrammable, pos:0, group:1, group mask:g1 reporting: default 1: Right Button , default: Right Click => Right Click mse, reprogrammable, pos:0, group:1, group mask:g1 reporting: default 2: Middle Button , default: Mouse Middle Button => Mouse Middle Button mse, reprogrammable, divertable, persistently divertable, raw XY, pos:0, group:2, group mask:g1,g2 reporting: default 3: Back Button , default: Mouse Back Button => Mouse Back Button mse, reprogrammable, divertable, persistently divertable, raw XY, pos:0, group:2, group mask:g1,g2 reporting: default 4: Forward Button , default: Mouse Forward Button => Mouse Forward Button mse, reprogrammable, divertable, persistently divertable, raw XY, pos:0, group:2, group mask:g1,g2 reporting: default 5: Left Tilt , default: Mouse Scroll Left Button => Mouse Scroll Left Button mse, reprogrammable, divertable, persistently divertable, raw XY, pos:0, group:2, group mask:g1,g2 reporting: default 6: Right Tilt , default: Mouse Scroll Right Button => Mouse Scroll Right Button mse, reprogrammable, divertable, persistently divertable, raw XY, pos:0, group:2, group mask:g1,g2 reporting: default 7: MultiPlatform Gesture Button, default: Multiplatform Gesture Button => Multiplatform Gesture Button mse, reprogrammable, divertable, persistently divertable, raw XY, pos:0, group:2, group mask:g1,g2 reporting: default 8: Virtual Gesture Button , default: Virtual Gesture Button => Virtual Gesture Button divertable, virtual, raw XY, force raw XY, pos:0, group:3, group mask:empty reporting: default Battery: 90%, discharging, next level 50%. Solaar-1.1.14/docs/devices/M720 Triathlon Multi-Device Mouse B015.txt000066400000000000000000000155321473525213700244340ustar00rootroot00000000000000Solaar version 1.1.4 1: M720 Triathlon Multi-Device Mouse Device path : /dev/hidraw0 USB id : 046d:B015 Codename : M720 Triathlon Kind : mouse Protocol : HID++ 4.5 Serial number: Model ID: B015405E0000 Unit ID: E167BBCD Bootloader: BOT 34.01.B0005 Firmware: MPM 04.10.B0013 Other: Supports 30 HID++ 2.0 features: 0: ROOT {0000} 1: FEATURE SET {0001} 2: DEVICE FW VERSION {0003} Firmware: Bootloader BOT 34.01.B0005 000000A96719 Firmware: Firmware MPM 04.10.B0013 B01500A96719 Firmware: Other Unit ID: E167BBCD Model ID: B015405E0000 Transport IDs: {'btleid': 'B015', 'wpid': '405E'} 3: DEVICE NAME {0005} Name: M720 Triathlon Multi-Device Mouse Kind: mouse 4: WIRELESS DEVICE STATUS {1D4B} 5: RESET {0020} 6: CRYPTO ID {0021} 7: DEVICE FRIENDLY NAME {0007} Friendly Name: M720 Triathlon 8: BATTERY STATUS {1000} Battery: 50%, discharging, next level 20%. 9: CHANGE HOST {1814} Change Host : 3:shoaib-MacBookPro 10: HOSTS INFO {1815} Host 0 (paired): Host 1 (paired): Shoaib’s MacBook Pro Host 2 (paired): shoaib-MacBookPro 11: REPROG CONTROLS V4 {1B04} Mouse Gestures (saved): Off Mouse Gestures : Off Key/Button Actions (saved): {Left Button:Left Click, Right Button:Right Click, Middle Button:Mouse Middle Button, Back Button:Mouse Back Button, Forward Button:Mouse Forward Button, Left Tilt:Mouse Scroll Left Button , Right Tilt:Mouse Scroll Right Button, MultiPlatform Gesture Button:Multiplatform Gesture Button} Key/Button Actions : {Left Button:Left Click, Right Button:Right Click, Middle Button:Mouse Middle Button, Back Button:Mouse Back Button, Forward Button:Mouse Forward Button, Left Tilt:Mouse Scroll Left Button , Right Tilt:Mouse Scroll Right Button, MultiPlatform Gesture Button:Multiplatform Gesture Button} Key/Button Diversion (saved): {Middle Button:Regular, Back Button:Regular, Forward Button:Regular, Left Tilt:Regular, Right Tilt:Regular, MultiPlatform Gesture Button:Regular} Key/Button Diversion : {Middle Button:Regular, Back Button:Regular, Forward Button:Regular, Left Tilt:Regular, Right Tilt:Regular, MultiPlatform Gesture Button:Regular} 12: PERSISTENT REMAPPABLE ACTION {1C00} Persistent Key/Button Mapping : {Left Button:Mouse Button Left, Right Button:Mouse Button Right, Middle Button:Mouse Button Middle, Back Button:Mouse Button Back, Forward Button:Mouse Button Forward, Left Tilt:Horizontal Scroll Left, Right Tilt:Horizontal Scroll Right, MultiPlatform Gesture Button:Alt+Cntrl+TAB} 13: POINTER SPEED {2205} Pointer Speed: 1.0 Sensitivity (Pointer Speed) (saved): 256 Sensitivity (Pointer Speed) : 256 14: VERTICAL SCROLLING {2100} Roller type: 3G Ratchet per turn: 24 Scroll lines: 0 15: DFUCONTROL UNSIGNED {00C1} 16: DFUCONTROL SIGNED {00C2} 17: unknown:1803 {1803} internal, hidden 18: unknown:1813 {1813} internal, hidden 19: unknown:1830 {1830} internal, hidden 20: unknown:18A1 {18A1} internal, hidden 21: unknown:1DF3 {1DF3} internal, hidden 22: unknown:1E00 {1E00} hidden 23: unknown:1EB0 {1EB0} internal, hidden 24: unknown:1861 {1861} internal, hidden 25: unknown:18B1 {18B1} internal, hidden 26: unknown:1850 {1850} internal, hidden 27: unknown:1E22 {1E22} 28: unknown:1F03 {1F03} internal, hidden 29: unknown:18C0 {18C0} internal, hidden Has 9 reprogrammable keys: 0: Left Button , default: Left Click => Left Click mse, reprogrammable, pos:0, group:1, group mask:g1 reporting: default 1: Right Button , default: Right Click => Right Click mse, reprogrammable, pos:0, group:1, group mask:g1 reporting: default 2: Middle Button , default: Mouse Middle Button => Mouse Middle Button mse, reprogrammable, divertable, persistently divertable, raw XY, pos:0, group:2, group mask:g1,g2 reporting: default 3: Back Button , default: Mouse Back Button => Mouse Back Button mse, reprogrammable, divertable, persistently divertable, raw XY, pos:0, group:2, group mask:g1,g2 reporting: default 4: Forward Button , default: Mouse Forward Button => Mouse Forward Button mse, reprogrammable, divertable, persistently divertable, raw XY, pos:0, group:2, group mask:g1,g2 reporting: default 5: Left Tilt , default: Mouse Scroll Left Button => Mouse Scroll Left Button mse, reprogrammable, divertable, persistently divertable, raw XY, pos:0, group:2, group mask:g1,g2 reporting: default 6: Right Tilt , default: Mouse Scroll Right Button => Mouse Scroll Right Button mse, reprogrammable, divertable, persistently divertable, raw XY, pos:0, group:2, group mask:g1,g2 reporting: default 7: MultiPlatform Gesture Button, default: Multiplatform Gesture Button => Multiplatform Gesture Button mse, reprogrammable, divertable, persistently divertable, raw XY, pos:0, group:2, group mask:g1,g2 reporting: default 8: Virtual Gesture Button , default: Virtual Gesture Button => Virtual Gesture Button divertable, virtual, raw XY, force raw XY, pos:0, group:3, group mask:empty reporting: default Has 8 persistent remappable keys: 0: Left Button => Mouse Button: Mouse Button Left 1: Right Button => Mouse Button: Mouse Button Right 2: Middle Button => Mouse Button: Mouse Button Middle 3: Back Button => Mouse Button: Mouse Button Back 4: Forward Button => Mouse Button: Mouse Button Forward 5: Left Tilt => Horizontal Scroll: Horizontal Scroll Left 6: Right Tilt => Horizontal Scroll: Horizontal Scroll Right 7: MultiPlatform Gesture Button => Key: Alt+Cntrl+TAB Battery: 50%, discharging, next level 20%. Solaar-1.1.14/docs/devices/MX Anywhere 3 4090.txt000066400000000000000000000130601473525213700207320ustar00rootroot00000000000000Solaar version 1.1.4 1: MX Anywhere 3 Device path : /dev/hidraw3 WPID : 4090 Codename : MX Anywhere 3 Kind : mouse Protocol : HID++ 4.5 Polling rate : 8 ms (125Hz) Serial number: 8F4A05EA Model ID: B02540900000 Unit ID: 8F4A05EA Bootloader: BL1 13.01.B0015 Firmware: MPM 24.01.B0015 Other: The power switch is located on the base. Supports 32 HID++ 2.0 features: 0: ROOT {0000} V0 1: FEATURE SET {0001} V0 2: DEVICE FW VERSION {0003} V4 Firmware: Bootloader BL1 13.01.B0015 0000AACD8D78 Firmware: Firmware MPM 24.01.B0015 4090AACD8D78 Firmware: Other Unit ID: 8F4A05EA Model ID: B02540900000 Transport IDs: {'btleid': 'B025', 'wpid': '4090'} 3: DEVICE NAME {0005} V0 Name: MX Anywhere 3 Kind: mouse 4: WIRELESS DEVICE STATUS {1D4B} V0 5: RESET {0020} V0 6: CRYPTO ID {0021} V1 7: DEVICE FRIENDLY NAME {0007} V0 Friendly Name: MX Anywhere 3 8: UNIFIED BATTERY {1004} V0 Battery: 65%, discharging. 9: REPROG CONTROLS V4 {1B04} V5 Key/Button Actions (saved): {Left Button:Left Click, Right Button:Right Click, Middle Button:Mouse Middle Button, Back Button:Mouse Back Button, Forward Button:Mouse Forward Button, Smart Shift:Smart Shift} Key/Button Actions : {Left Button:Left Click, Right Button:Right Click, Middle Button:Mouse Middle Button, Back Button:Mouse Back Button, Forward Button:Mouse Forward Button, Smart Shift:Smart Shift} Key/Button Diversion (saved): {Middle Button:Regular, Back Button:Regular, Forward Button:Regular, Smart Shift:Regular} Key/Button Diversion : {Middle Button:Regular, Back Button:Regular, Forward Button:Regular, Smart Shift:Regular} 10: CHANGE HOST {1814} V1 Change Host : 1:apollo 11: XY STATS {2250} V1 12: ADJUSTABLE DPI {2201} V1 Sensibilidade (DPI) (saved): 4000 Sensibilidade (DPI) : 4000 13: SMART SHIFT ENHANCED {2111} V0 Scroll Wheel Rachet (saved): 12 Scroll Wheel Rachet : 12 14: HIRES WHEEL {2121} V1 Multiplier: 15 Has invert: Normal wheel motion Has ratchet switch: Normal wheel mode Low resolution mode HID notification Scroll Wheel Direction (saved): False Scroll Wheel Direction : False Scroll Wheel Resolution (saved): False Scroll Wheel Resolution : False Scroll Wheel Diversion (saved): False Scroll Wheel Diversion : False 15: WHEEL STATS {2251} V0 16: DFUCONTROL SIGNED {00C2} V0 17: DEVICE RESET {1802} V0 internal, hidden 18: unknown:1803 {1803} V0 internal, hidden 19: CONFIG DEVICE PROPS {1806} V7 internal, hidden 20: unknown:1812 {1812} V0 internal, hidden 21: OOBSTATE {1805} V0 internal, hidden 22: unknown:1830 {1830} V0 internal, hidden 23: unknown:1890 {1890} V5 internal, hidden 24: unknown:1891 {1891} V5 internal, hidden 25: unknown:18A1 {18A1} V0 internal, hidden 26: unknown:1E00 {1E00} V0 hidden 27: unknown:1EB0 {1EB0} V0 internal, hidden 28: unknown:1861 {1861} V0 internal, hidden 29: unknown:9001 {9001} V0 internal, hidden 30: unknown:1E22 {1E22} V0 internal, hidden 31: unknown:9205 {9205} V0 internal, hidden Has 7 reprogrammable keys: 0: Left Button , default: Left Click => Left Click mse, analytics key events, pos:0, group:1, group mask:g1 reporting: default 1: Right Button , default: Right Click => Right Click mse, analytics key events, pos:0, group:1, group mask:g1 reporting: default 2: Middle Button , default: Mouse Middle Button => Mouse Middle Button mse, reprogrammable, divertable, raw XY, analytics key events, pos:0, group:2, group mask:g1,g2 reporting: default 3: Back Button , default: Mouse Back Button => Mouse Back Button mse, reprogrammable, divertable, raw XY, analytics key events, unknown:000800, pos:0, group:2, group mask:g1,g2 reporting: default 4: Forward Button , default: Mouse Forward Button => Mouse Forward Button mse, reprogrammable, divertable, raw XY, analytics key events, unknown:000800, pos:0, group:2, group mask:g1,g2 reporting: default 5: Smart Shift , default: Smart Shift => Smart Shift mse, reprogrammable, divertable, raw XY, analytics key events, pos:0, group:2, group mask:g1,g2 reporting: default 6: Virtual Gesture Button , default: Virtual Gesture Button => Virtual Gesture Button divertable, virtual, raw XY, force raw XY, pos:0, group:3, group mask:empty reporting: default Battery: 65%, discharging. Solaar-1.1.14/docs/devices/MX Anywhere 3 B025.txt000066400000000000000000000125261473525213700207540ustar00rootroot00000000000000Solaar version 1.1.4 1: MX Anywhere 3 Device path : /dev/hidraw1 USB id : 046d:B025 Codename : MX Anywhere 3 Kind : mouse Protocol : HID++ 4.5 Serial number: Model ID: B02540900000 Unit ID: 179ADDDA Bootloader: BL1 13.00.B0014 Firmware: MPM 24.00.B0014 Other: Supports 29 HID++ 2.0 features: 0: ROOT {0000} 1: FEATURE SET {0001} 2: DEVICE FW VERSION {0003} Firmware: Bootloader BL1 13.00.B0014 000095D7D19A Firmware: Firmware MPM 24.00.B0014 B02595D7D19A Firmware: Other Unit ID: 179ADDDA Model ID: B02540900000 Transport IDs: {'btleid': 'B025', 'wpid': '4090'} 3: DEVICE NAME {0005} Name: MX Anywhere 3 Kind: mouse 4: WIRELESS DEVICE STATUS {1D4B} 5: RESET {0020} 6: CRYPTO ID {0021} 7: DEVICE FRIENDLY NAME {0007} Friendly Name: MX Anywhere 3 8: UNIFIED BATTERY {1004} Battery: N/A, None. 9: REPROG CONTROLS V4 {1B04} DPI Sliding Adjustment (saved): Off DPI Sliding Adjustment : Off Mouse Gestures (saved): Off Mouse Gestures : Off Key/Button Actions (saved): {Left Button:Left Click, Right Button:Right Click, Middle Button:Mouse Middle Button, Back Button:Mouse Back Button, Forward Button:Mouse Forward Button, Smart Shift:Smart Shift} Key/Button Actions : {Left Button:Left Click, Right Button:Right Click, Middle Button:Mouse Middle Button, Back Button:Mouse Back Button, Forward Button:Mouse Forward Button, Smart Shift:Smart Shift} Key/Button Diversion (saved): {Middle Button:Regular, Back Button:Regular, Forward Button:Regular, Smart Shift:Regular} Key/Button Diversion : {Middle Button:Regular, Back Button:Regular, Forward Button:Regular, Smart Shift:Regular} 10: CHANGE HOST {1814} Change Host : 1:zerocool 11: XY STATS {2250} 12: ADJUSTABLE DPI {2201} Sensitivity (DPI) (saved): 1000 Sensitivity (DPI) : 1000 13: SMART SHIFT ENHANCED {2111} Scroll Wheel Rachet (saved): 12 Scroll Wheel Rachet : 12 14: HIRES WHEEL {2121} Multiplier: 15 Has invert: Normal wheel motion Has ratchet switch: Normal wheel mode Low resolution mode HID notification Scroll Wheel Direction (saved): False Scroll Wheel Direction : False Scroll Wheel Resolution (saved): False Scroll Wheel Resolution : False Scroll Wheel Diversion (saved): False Scroll Wheel Diversion : False 15: WHEEL STATS {2251} 16: DFUCONTROL SIGNED {00C2} 17: DEVICE RESET {1802} internal, hidden 18: unknown:1803 {1803} internal, hidden 19: CONFIG DEVICE PROPS {1806} internal, hidden 20: unknown:1812 {1812} internal, hidden 21: OOBSTATE {1805} internal, hidden 22: unknown:1830 {1830} internal, hidden 23: unknown:18A1 {18A1} internal, hidden 24: unknown:1E00 {1E00} hidden 25: unknown:1EB0 {1EB0} internal, hidden 26: unknown:1861 {1861} internal, hidden 27: unknown:9300 {9300} internal, hidden 28: unknown:9001 {9001} internal, hidden Has 7 reprogrammable keys: 0: Left Button , default: Left Click => Left Click mse, analytics key events, pos:0, group:1, group mask:g1 reporting: default 1: Right Button , default: Right Click => Right Click mse, analytics key events, pos:0, group:1, group mask:g1 reporting: default 2: Middle Button , default: Mouse Middle Button => Mouse Middle Button mse, reprogrammable, divertable, raw XY, analytics key events, pos:0, group:2, group mask:g1,g2 reporting: default 3: Back Button , default: Mouse Back Button => Mouse Back Button mse, reprogrammable, divertable, raw XY, analytics key events, unknown:000800, pos:0, group:2, group mask:g1,g2 reporting: default 4: Forward Button , default: Mouse Forward Button => Mouse Forward Button mse, reprogrammable, divertable, raw XY, analytics key events, unknown:000800, pos:0, group:2, group mask:g1,g2 reporting: default 5: Smart Shift , default: Smart Shift => Smart Shift mse, reprogrammable, divertable, raw XY, analytics key events, pos:0, group:2, group mask:g1,g2 reporting: default 6: Virtual Gesture Button , default: Virtual Gesture Button => Virtual Gesture Button divertable, virtual, raw XY, force raw XY, pos:0, group:3, group mask:empty reporting: default Battery: N/A, None. Solaar-1.1.14/docs/devices/MX Ergo Multi-Device Trackball 406F.txt000066400000000000000000000161041473525213700240760ustar00rootroot00000000000000Solaar version 1.1.4 1: MX Ergo Multi-Device Trackball Device path : /dev/hidraw4 WPID : 406F Codename : MX Ergo Kind : trackball Protocol : HID++ 4.5 Polling rate : 8 ms (125Hz) Serial number: 0E0F20E7 Model ID: B01D406F0000 Unit ID: 13AF9325 Bootloader: BOT 49.00.B0001 Firmware: MPM 06.03.B0022 Other: The power switch is located on the base. Supports 35 HID++ 2.0 features: 0: ROOT {0000} 1: FEATURE SET {0001} 2: DEVICE FW VERSION {0003} Firmware: Bootloader BOT 49.00.B0001 0000CDE885F3 Firmware: Firmware MPM 06.03.B0022 406FCDE885F3 Firmware: Other Unit ID: 13AF9325 Model ID: B01D406F0000 Transport IDs: {'btleid': 'B01D', 'wpid': '406F'} 3: DEVICE NAME {0005} Name: MX Ergo Multi-Device Trackball Kind: trackball 4: WIRELESS DEVICE STATUS {1D4B} 5: DEVICE FRIENDLY NAME {0007} Friendly Name: MX Ergo 6: RESET {0020} 7: CRYPTO ID {0021} 8: BATTERY STATUS {1000} Battery: 50%, discharging, next level 20%. 9: LED CONTROL {1300} 10: REPROG CONTROLS V4 {1B04} Key/Button Actions (saved): {Left Button:Left Click, Right Button:Right Click, Middle Button:Mouse Middle Button, Back Button:Mouse Back Button, Forward Button:Mouse Forward Button, Left Tilt:Mouse Scroll Left Button , Right Tilt:Mouse Scroll Right Button, DPI Change:DPI Change} Key/Button Actions : {Left Button:Left Click, Right Button:Right Click, Middle Button:Mouse Middle Button, Back Button:Mouse Back Button, Forward Button:Mouse Forward Button, Left Tilt:Mouse Scroll Left Button , Right Tilt:Mouse Scroll Right Button, DPI Change:DPI Change} Key/Button Diversion (saved): {Middle Button:Regular, Back Button:Regular, Forward Button:Regular, Left Tilt:Regular, Right Tilt:Regular, DPI Change:Regular} Key/Button Diversion : {Middle Button:Regular, Back Button:Regular, Forward Button:Regular, Left Tilt:Regular, Right Tilt:Regular, DPI Change:Regular} 11: PERSISTENT REMAPPABLE ACTION {1C00} Persistent Key/Button Mapping : {Left Button:Mouse Button Left, Right Button:Mouse Button Right, Middle Button:Mouse Button Middle, Back Button:Mouse Button Back, Forward Button:Mouse Button Forward, Left Tilt:Horizontal Scroll Left, Right Tilt:Horizontal Scroll Right, DPI Change:Mouse Button DPI} 12: POINTER AXIS ORIENTATION {2006} 13: POINTER SPEED {2205} Pointer Speed: 1.046875 Sensitivity (Pointer Speed) (saved): 268 Sensitivity (Pointer Speed) : 268 Sensitivity Switching (saved): Off Sensitivity Switching : Off 14: VERTICAL SCROLLING {2100} Roller type: standard Ratchet per turn: 18 Scroll lines: 0 15: DFUCONTROL SIGNED {00C2} 16: DEVICE RESET {1802} internal, hidden 17: unknown:1803 {1803} internal, hidden 18: CONFIG DEVICE PROPS {1806} internal, hidden 19: OOBSTATE {1805} internal, hidden 20: unknown:1813 {1813} internal, hidden 21: CHANGE HOST {1814} Change Host : 1:razorback 22: HOSTS INFO {1815} Host 0 (paired): razorback Host 1 (unpaired): Host 2 (unpaired): 23: unknown:1830 {1830} internal, hidden 24: unknown:1861 {1861} internal, hidden 25: unknown:1890 {1890} internal, hidden 26: unknown:1891 {1891} internal, hidden 27: unknown:18A1 {18A1} internal, hidden 28: unknown:1DF3 {1DF3} internal, hidden 29: unknown:1E00 {1E00} hidden 30: unknown:1EB0 {1EB0} internal, hidden 31: unknown:18B1 {18B1} internal, hidden 32: unknown:1850 {1850} internal, hidden 33: unknown:1F03 {1F03} internal, hidden 34: unknown:18C0 {18C0} internal, hidden Has 9 reprogrammable keys: 0: Left Button , default: Left Click => Left Click mse, reprogrammable, pos:0, group:1, group mask:g1 reporting: default 1: Right Button , default: Right Click => Right Click mse, reprogrammable, pos:0, group:1, group mask:g1 reporting: default 2: Middle Button , default: Mouse Middle Button => Mouse Middle Button mse, reprogrammable, divertable, persistently divertable, raw XY, pos:0, group:2, group mask:g1,g2 reporting: default 3: Back Button , default: Mouse Back Button => Mouse Back Button mse, reprogrammable, divertable, persistently divertable, raw XY, pos:0, group:2, group mask:g1,g2 reporting: default 4: Forward Button , default: Mouse Forward Button => Mouse Forward Button mse, reprogrammable, divertable, persistently divertable, raw XY, pos:0, group:2, group mask:g1,g2 reporting: default 5: DPI Change , default: DPI Change => DPI Change mse, reprogrammable, divertable, persistently divertable, raw XY, pos:0, group:2, group mask:g1,g2 reporting: default 6: Left Tilt , default: Mouse Scroll Left Button => Mouse Scroll Left Button mse, reprogrammable, divertable, persistently divertable, raw XY, pos:0, group:2, group mask:g1,g2 reporting: default 7: Right Tilt , default: Mouse Scroll Right Button => Mouse Scroll Right Button mse, reprogrammable, divertable, persistently divertable, raw XY, pos:0, group:2, group mask:g1,g2 reporting: default 8: Virtual Gesture Button , default: Virtual Gesture Button => Virtual Gesture Button divertable, virtual, raw XY, force raw XY, pos:0, group:3, group mask:empty reporting: default Has 8 persistent remappable keys: 0: Left Button => Mouse Button: Mouse Button Left 1: Right Button => Mouse Button: Mouse Button Right 2: Middle Button => Mouse Button: Mouse Button Middle 3: Back Button => Mouse Button: Mouse Button Back 4: Forward Button => Mouse Button: Mouse Button Forward 5: DPI Change => Mouse Button: Mouse Button DPI 6: Left Tilt => Horizontal Scroll: Horizontal Scroll Left 7: Right Tilt => Horizontal Scroll: Horizontal Scroll Right Battery: 50%, discharging, next level 20%. Solaar-1.1.14/docs/devices/MX Keys Keyboard 408A.txt000066400000000000000000000247471473525213700215170ustar00rootroot00000000000000Solaar version 1.1.4 1: MX Keys Keyboard Device path : /dev/hidraw1 WPID : 408A Codename : MX Keys Kind : keyboard Protocol : HID++ 4.5 Polling rate : 20 ms (50Hz) Serial number: F359F7AC Model ID: B35B408A0000 Unit ID: F359F7AC Bootloader: BL1 08.00.B0011 Firmware: MPK 12.00.B0011 Other: The power switch is located on the edge of top right corner. Supports 32 HID++ 2.0 features: 0: ROOT {0000} V0 1: FEATURE SET {0001} V0 2: DEVICE FW VERSION {0003} V2 Firmware: Bootloader BL1 08.00.B0011 00008169E8BB Firmware: Firmware MPK 12.00.B0011 408A8169E8BB Firmware: Other Unit ID: F359F7AC Model ID: B35B408A0000 Transport IDs: {'btleid': 'B35B', 'wpid': '408A'} 3: DEVICE NAME {0005} V0 Name: MX Keys Wireless Keyboard Kind: keyboard 4: WIRELESS DEVICE STATUS {1D4B} V0 5: RESET {0020} V0 6: DEVICE FRIENDLY NAME {0007} V0 Friendly Name: MX Keys 7: BATTERY STATUS {1000} V1 Battery: 50%, discharging, next level 20%. 8: REPROG CONTROLS V4 {1B04} V4 Key/Button Diversion (saved): {Calculator:Regular, Show Desktop:Regular, Lock PC:Regular, Screen Capture/Print Screen:Regular, Brightness Down:Regular, Brightness Up:Regular, Host Switch Channel 1:Regular, Host Switch Channel 2:Regular, Host Switch Channel 3:Regular, Mission Control/Task View:Regular, Dashboard Launchpad/Action Center:Regular, Backlight Down:Regular, Backlight Up:Regular, Previous Fn:Regular, Play/Pause Fn:Regular, Next Fn:Regular, Mute Fn:Regular, Volume Down Fn:Regular, Volume Up Fn:Regular, App Contextual Menu/Right Click:Regular, Right Arrow:Regular, Left Arrow:Regular} Key/Button Diversion : {Calculator:Regular, Show Desktop:Regular, Lock PC:Regular, Screen Capture/Print Screen:Regular, Brightness Down:Regular, Brightness Up:Regular, Host Switch Channel 1:Regular, Host Switch Channel 2:Regular, Host Switch Channel 3:Regular, Mission Control/Task View:Regular, Dashboard Launchpad/Action Center:Regular, Backlight Down:Regular, Backlight Up:Regular, Previous Fn:Regular, Play/Pause Fn:Regular, Next Fn:Regular, Mute Fn:Regular, Volume Down Fn:Regular, Volume Up Fn:Regular, App Contextual Menu/Right Click:Regular, Right Arrow:Regular, Left Arrow:Regular} 9: CHANGE HOST {1814} V1 10: HOSTS INFO {1815} V1 Host 0 (paired): brandy Host 1 (paired): Pixel 2 Host 2 (paired): devlaptop002 11: BACKLIGHT2 {1982} V1 Backlight (saved): True Backlight : True 12: K375S FN INVERSION {40A3} V0 Swap Fx function (saved): False Swap Fx function : False 13: ENCRYPTION {4100} V0 14: LOCK KEY STATE {4220} V0 15: KEYBOARD DISABLE KEYS {4521} V0 Disable keys (saved): {Caps Lock:False, Num Lock:False, Scroll Lock:False, Insert:False, Win:False} Disable keys : {Caps Lock:False, Num Lock:False, Scroll Lock:False, Insert:False, Win:False} 16: MULTIPLATFORM {4531} V1 Set OS (saved): Windows Set OS : Windows 17: DFUCONTROL SIGNED {00C2} V0 18: DEVICE RESET {1802} V0 internal, hidden 19: unknown:1803 {1803} V0 internal, hidden 20: CONFIG DEVICE PROPS {1806} V5 internal, hidden 21: unknown:1813 {1813} V0 internal, hidden 22: OOBSTATE {1805} V0 internal, hidden 23: unknown:1830 {1830} V0 internal, hidden 24: unknown:1890 {1890} V5 internal, hidden 25: unknown:1891 {1891} V5 internal, hidden 26: unknown:18A1 {18A1} V0 internal, hidden 27: unknown:1DF3 {1DF3} V0 internal, hidden 28: unknown:1E00 {1E00} V0 hidden 29: unknown:1EB0 {1EB0} V0 internal, hidden 30: unknown:1861 {1861} V0 internal, hidden 31: unknown:1A20 {1A20} V0 internal, hidden Has 24 reprogrammable keys: 0: Host Switch Channel 1 , default: HostSwitch Channel 1 => HostSwitch Channel 1 nonstandard, divertable, persistently divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 1: Host Switch Channel 2 , default: HostSwitch Channel 2 => HostSwitch Channel 2 nonstandard, divertable, persistently divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 2: Host Switch Channel 3 , default: HostSwitch Channel 3 => HostSwitch Channel 3 nonstandard, divertable, persistently divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 3: Brightness Down , default: Brightness Down => Brightness Down is FN, FN sensitive, reprogrammable, divertable, persistently divertable, analytics key events, pos:1, group:0, group mask:empty reporting: default 4: Brightness Up , default: Brightness Up => Brightness Up is FN, FN sensitive, reprogrammable, divertable, persistently divertable, analytics key events, pos:2, group:0, group mask:empty reporting: default 5: Mission Control/Task View , default: Mission Control/Task View => Mission Control/Task View is FN, FN sensitive, reprogrammable, divertable, persistently divertable, analytics key events, pos:3, group:0, group mask:empty reporting: default 6: Dashboard Launchpad/Action Center, default: Dashboard Launchpad/Action Center => Dashboard Launchpad/Action Center is FN, FN sensitive, reprogrammable, divertable, persistently divertable, analytics key events, pos:4, group:0, group mask:empty reporting: default 7: Show Desktop , default: Show Desktop => Show Desktop is FN, FN sensitive, reprogrammable, divertable, persistently divertable, analytics key events, pos:5, group:0, group mask:empty reporting: default 8: Backlight Down , default: Backlight Down => Backlight Down is FN, FN sensitive, reprogrammable, divertable, persistently divertable, analytics key events, pos:6, group:0, group mask:empty reporting: default 9: Backlight Up , default: Backlight Up => Backlight Up is FN, FN sensitive, reprogrammable, divertable, persistently divertable, analytics key events, pos:7, group:0, group mask:empty reporting: default 10: Previous Fn , default: Previous => Previous is FN, FN sensitive, reprogrammable, divertable, persistently divertable, analytics key events, pos:8, group:0, group mask:empty reporting: default 11: Play/Pause Fn , default: Play/Pause => Play/Pause is FN, FN sensitive, reprogrammable, divertable, persistently divertable, analytics key events, pos:9, group:0, group mask:empty reporting: default 12: Next Fn , default: Next => Next is FN, FN sensitive, reprogrammable, divertable, persistently divertable, analytics key events, pos:10, group:0, group mask:empty reporting: default 13: Mute Fn , default: Mute => Mute is FN, FN sensitive, reprogrammable, divertable, persistently divertable, analytics key events, pos:11, group:0, group mask:empty reporting: default 14: Volume Down Fn , default: Volume Down => Volume Down is FN, FN sensitive, reprogrammable, divertable, persistently divertable, analytics key events, pos:12, group:0, group mask:empty reporting: default 15: Volume Up Fn , default: Volume Up => Volume Up nonstandard, reprogrammable, divertable, persistently divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 16: Calculator , default: Calculator => Calculator nonstandard, reprogrammable, divertable, persistently divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 17: Screen Capture/Print Screen, default: Screen Capture => Screen Capture nonstandard, reprogrammable, divertable, persistently divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 18: App Contextual Menu/Right Click, default: Right Click/App Contextual Menu => Right Click/App Contextual Menu nonstandard, reprogrammable, divertable, persistently divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 19: Lock PC , default: WindowsLock => WindowsLock nonstandard, reprogrammable, divertable, persistently divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 20: Left Arrow , default: Keyboard Left Arrow => Keyboard Left Arrow nonstandard, divertable, persistently divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 21: Right Arrow , default: Keyboard Right Arrow => Keyboard Right Arrow nonstandard, divertable, persistently divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 22: F Lock , default: Do Nothing One => Do Nothing One is FN, analytics key events, pos:0, group:0, group mask:empty reporting: default 23: FN Key , default: Do Nothing One => Do Nothing One nonstandard, analytics key events, pos:0, group:0, group mask:empty reporting: default Battery: 50%, discharging, next level 20%. Solaar-1.1.14/docs/devices/MX Keys Keyboard B35B.txt000066400000000000000000000241531473525213700215250ustar00rootroot00000000000000Solaar version 1.1.4rc1 1: MX Keys Keyboard Device path : /dev/hidraw8 USB id : 046d:B35B Codename : MX Keys Kind : keyboard Protocol : HID++ 4.5 Serial number: Model ID: B35B408A0000 Unit ID: EC96CD5C Bootloader: BL1 08.00.B0011 Firmware: MPK 12.01.B0013 Other: Supports 29 HID++ 2.0 features: 0: ROOT {0000} 1: FEATURE SET {0001} 2: DEVICE FW VERSION {0003} Firmware: Bootloader BL1 08.00.B0011 00008169E8BB Firmware: Firmware MPK 12.01.B0013 B35BFE037737 Firmware: Other Unit ID: EC96CD5C Model ID: B35B408A0000 Transport IDs: {'btleid': 'B35B', 'wpid': '408A'} 3: DEVICE NAME {0005} Name: MX Keys Wireless Keyboard Kind: keyboard 4: WIRELESS DEVICE STATUS {1D4B} 5: RESET {0020} 6: DEVICE FRIENDLY NAME {0007} Friendly Name: MX Keys 7: BATTERY STATUS {1000} Battery: 100%, discharging, next level 50%. 8: REPROG CONTROLS V4 {1B04} Key/Button Diversion (saved): {Calculator:Regular, Show Desktop:Regular, Lock PC:Regular, Screen Capture/Print Screen:Regular, Brightness Down:Regular, Brightness Up:Regular, Host Switch Channel 1:Regular, Host Switch Channel 2:Regular, Host Switch Channel 3:Regular, Mission Control/Task View:Regular, Dashboard Launchpad/Action Center:Regular, Backlight Down:Regular, Backlight Up:Regular, Previous Fn:Regular, Play/Pause Fn:Regular, Next Fn:Regular, Mute Fn:Regular, Volume Down Fn:Regular, Volume Up Fn:Regular, App Contextual Menu/Right Click:Regular, Right Arrow:Regular, Left Arrow:Regular} Key/Button Diversion : {Calculator:Regular, Show Desktop:Regular, Lock PC:Regular, Screen Capture/Print Screen:Regular, Brightness Down:Regular, Brightness Up:Regular, Host Switch Channel 1:Regular, Host Switch Channel 2:Regular, Host Switch Channel 3:Regular, Mission Control/Task View:Regular, Dashboard Launchpad/Action Center:Regular, Backlight Down:Regular, Backlight Up:Regular, Previous Fn:Regular, Play/Pause Fn:Regular, Next Fn:Regular, Mute Fn:Regular, Volume Down Fn:Regular, Volume Up Fn:Regular, App Contextual Menu/Right Click:Regular, Right Arrow:Regular, Left Arrow:Regular} 9: CHANGE HOST {1814} Change Host : 1:ak 10: HOSTS INFO {1815} Host 0 (paired): ak Host 1 (unpaired): Host 2 (unpaired): 11: BACKLIGHT2 {1982} Backlight (saved): True Backlight : True 12: K375S FN INVERSION {40A3} Swap Fx function (saved): True Swap Fx function : True 13: ENCRYPTION {4100} 14: LOCK KEY STATE {4220} 15: KEYBOARD DISABLE KEYS {4521} Disable keys (saved): {Caps Lock:False, Num Lock:False, Scroll Lock:False, Insert:False, Win:False} Disable keys : {Caps Lock:False, Num Lock:False, Scroll Lock:False, Insert:False, Win:False} 16: MULTIPLATFORM {4531} Set OS (saved): MacOS Set OS : MacOS 17: DFUCONTROL SIGNED {00C2} 18: DEVICE RESET {1802} internal, hidden 19: unknown:1803 {1803} internal, hidden 20: CONFIG DEVICE PROPS {1806} internal, hidden 21: unknown:1813 {1813} internal, hidden 22: OOBSTATE {1805} internal, hidden 23: unknown:1830 {1830} internal, hidden 24: unknown:18A1 {18A1} internal, hidden 25: unknown:1E00 {1E00} hidden 26: unknown:1EB0 {1EB0} internal, hidden 27: unknown:1861 {1861} internal, hidden 28: unknown:1A20 {1A20} internal, hidden Has 24 reprogrammable keys: 0: Host Switch Channel 1 , default: HostSwitch Channel 1 => HostSwitch Channel 1 nonstandard, divertable, persistently divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 1: Host Switch Channel 2 , default: HostSwitch Channel 2 => HostSwitch Channel 2 nonstandard, divertable, persistently divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 2: Host Switch Channel 3 , default: HostSwitch Channel 3 => HostSwitch Channel 3 nonstandard, divertable, persistently divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 3: Brightness Down , default: Brightness Down => Brightness Down is FN, FN sensitive, reprogrammable, divertable, persistently divertable, analytics key events, pos:1, group:0, group mask:empty reporting: default 4: Brightness Up , default: Brightness Up => Brightness Up is FN, FN sensitive, reprogrammable, divertable, persistently divertable, analytics key events, pos:2, group:0, group mask:empty reporting: default 5: Mission Control/Task View , default: Mission Control/Task View => Mission Control/Task View is FN, FN sensitive, reprogrammable, divertable, persistently divertable, analytics key events, pos:3, group:0, group mask:empty reporting: default 6: Dashboard Launchpad/Action Center, default: Dashboard Launchpad/Action Center => Dashboard Launchpad/Action Center is FN, FN sensitive, reprogrammable, divertable, persistently divertable, analytics key events, pos:4, group:0, group mask:empty reporting: default 7: Show Desktop , default: Show Desktop => Show Desktop is FN, FN sensitive, reprogrammable, divertable, persistently divertable, analytics key events, pos:5, group:0, group mask:empty reporting: default 8: Backlight Down , default: Backlight Down => Backlight Down is FN, FN sensitive, reprogrammable, divertable, persistently divertable, analytics key events, pos:6, group:0, group mask:empty reporting: default 9: Backlight Up , default: Backlight Up => Backlight Up is FN, FN sensitive, reprogrammable, divertable, persistently divertable, analytics key events, pos:7, group:0, group mask:empty reporting: default 10: Previous Fn , default: Previous => Previous is FN, FN sensitive, reprogrammable, divertable, persistently divertable, analytics key events, pos:8, group:0, group mask:empty reporting: default 11: Play/Pause Fn , default: Play/Pause => Play/Pause is FN, FN sensitive, reprogrammable, divertable, persistently divertable, analytics key events, pos:9, group:0, group mask:empty reporting: default 12: Next Fn , default: Next => Next is FN, FN sensitive, reprogrammable, divertable, persistently divertable, analytics key events, pos:10, group:0, group mask:empty reporting: default 13: Mute Fn , default: Mute => Mute is FN, FN sensitive, reprogrammable, divertable, persistently divertable, analytics key events, pos:11, group:0, group mask:empty reporting: default 14: Volume Down Fn , default: Volume Down => Volume Down is FN, FN sensitive, reprogrammable, divertable, persistently divertable, analytics key events, pos:12, group:0, group mask:empty reporting: default 15: Volume Up Fn , default: Volume Up => Volume Up nonstandard, reprogrammable, divertable, persistently divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 16: Calculator , default: Calculator => Calculator nonstandard, reprogrammable, divertable, persistently divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 17: Screen Capture/Print Screen, default: Screen Capture => Screen Capture nonstandard, reprogrammable, divertable, persistently divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 18: App Contextual Menu/Right Click, default: Right Click/App Contextual Menu => Right Click/App Contextual Menu nonstandard, reprogrammable, divertable, persistently divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 19: Lock PC , default: WindowsLock => WindowsLock nonstandard, reprogrammable, divertable, persistently divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 20: Left Arrow , default: Keyboard Left Arrow => Keyboard Left Arrow nonstandard, divertable, persistently divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 21: Right Arrow , default: Keyboard Right Arrow => Keyboard Right Arrow nonstandard, divertable, persistently divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 22: F Lock , default: Do Nothing One => Do Nothing One is FN, analytics key events, pos:0, group:0, group mask:empty reporting: default 23: FN Key , default: Do Nothing One => Do Nothing One nonstandard, analytics key events, pos:0, group:0, group mask:empty reporting: default Battery: 100%, discharging, next level 50%. Solaar-1.1.14/docs/devices/MX Keys Mini B369.txt000066400000000000000000000162601473525213700206510ustar00rootroot00000000000000Solaar version 1.1.4 255: MX Keys Mini Device path : /dev/hidraw6 USB id : 046d:B369 Codename : MX Keys Mini Kind : ? Protocol : HID++ 4.5 Serial number: Model ID: B36900000000 Unit ID: 42BABA25 Bootloader: BL1 53.01.B0006 Firmware: RBK 73.01.B0006 Other: Supports 31 HID++ 2.0 features: 0: ROOT {0000} 1: FEATURE SET {0001} 2: DEVICE FW VERSION {0003} Firmware: Bootloader BL1 53.01.B0006 B369FD70F4 Firmware: Firmware RBK 73.01.B0006 B369FD70F4 Firmware: Other Unit ID: 42BABA25 Model ID: B36900000000 Transport IDs: {'btleid': 'B369'} 3: DEVICE NAME {0005} Name: MX Keys Mini Kind: keyboard 4: WIRELESS DEVICE STATUS {1D4B} 5: RESET {0020} 6: DEVICE FRIENDLY NAME {0007} Friendly Name: MX Keys Mini 7: UNIFIED BATTERY {1004} 8: REPROG CONTROLS V4 {1B04} Key/Button Diversion (saved): {Backlight Down:Regular, Backlight Up:Regular, Play/Pause Fn:Regular, Mute Fn:Regular, Volume Down Fn:Regular, Volume Up Fn:Regular, Voice Dictation:Regular, Open Emoji Panel:Regular, Snipping Tool:Regular, Delete:Regular, Mute Microphone:Regular} Key/Button Diversion : {Backlight Down:Regular, Backlight Up:Regular, Play/Pause Fn:Regular, Mute Fn:Regular, Volume Down Fn:Regular, Volume Up Fn:Regular, Voice Dictation:Regular, Open Emoji Panel:Regular, Snipping Tool:Regular, Delete:Regular, Mute Microphone:Regular} 9: CHANGE HOST {1814} Change Host : 3:fedora 10: HOSTS INFO {1815} Host 0 (paired): U308198 Host 1 (paired): DESKTOP-9C4GF7M Host 2 (paired): fedora 11: BACKLIGHT2 {1982} Backlight (saved): True Backlight : True 12: K375S FN INVERSION {40A3} Swap Fx function (saved): False Swap Fx function : False 13: LOCK KEY STATE {4220} 14: KEYBOARD DISABLE KEYS {4521} Disable keys (saved): {Caps Lock:False, Insert:False, Win:False} Disable keys : {Caps Lock:False, Insert:False, Win:False} 15: MULTIPLATFORM {4531} Set OS (saved): Windows Set OS : Windows 16: DFUCONTROL {00C3} 17: DEVICE RESET {1802} internal, hidden, unknown:000010 18: unknown:1803 {1803} internal, hidden, unknown:000010 19: CONFIG DEVICE PROPS {1806} internal, hidden, unknown:000010 20: unknown:1816 {1816} internal, hidden, unknown:000010 21: OOBSTATE {1805} internal, hidden 22: unknown:1830 {1830} internal, hidden, unknown:000010 23: unknown:1891 {1891} internal, hidden, unknown:000008 24: unknown:18A1 {18A1} internal, hidden, unknown:000010 25: unknown:1E00 {1E00} hidden 26: unknown:1E02 {1E02} internal, hidden 27: unknown:1602 {1602} 28: unknown:1EB0 {1EB0} internal, hidden, unknown:000010 29: unknown:1861 {1861} internal, hidden, unknown:000010 30: unknown:1A20 {1A20} internal, hidden, unknown:000010 Has 16 reprogrammable keys: 0: Host Switch Channel 1 , default: HostSwitch Channel 1 => HostSwitch Channel 1 is FN, FN sensitive, analytics key events, pos:1, group:0, group mask:empty reporting: default 1: Host Switch Channel 2 , default: HostSwitch Channel 2 => HostSwitch Channel 2 is FN, FN sensitive, analytics key events, pos:2, group:0, group mask:empty reporting: default 2: Host Switch Channel 3 , default: HostSwitch Channel 3 => HostSwitch Channel 3 is FN, FN sensitive, analytics key events, pos:3, group:0, group mask:empty reporting: default 3: Backlight Down , default: Backlight Down => Backlight Down is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:4, group:0, group mask:empty reporting: default 4: Backlight Up , default: Backlight Up => Backlight Up is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:5, group:0, group mask:empty reporting: default 5: Voice Dictation , default: Voice Dictation => Voice Dictation is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:6, group:0, group mask:empty reporting: default 6: Open Emoji Panel , default: Open Emoji Panel => Open Emoji Panel is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:7, group:0, group mask:empty reporting: default 7: Snipping Tool , default: Snipping Tool => Snipping Tool is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:8, group:0, group mask:empty reporting: default 8: Mute Microphone , default: Mute Microphone => Mute Microphone is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:9, group:0, group mask:empty reporting: default 9: Play/Pause Fn , default: Play/Pause => Play/Pause is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:10, group:0, group mask:empty reporting: default 10: Mute Fn , default: Mute => Mute is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:11, group:0, group mask:empty reporting: default 11: Volume Down Fn , default: Volume Down => Volume Down is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:12, group:0, group mask:empty reporting: default 12: Volume Up Fn , default: Volume Up => Volume Up nonstandard, reprogrammable, divertable, analytics key events, pos:13, group:0, group mask:empty reporting: default 13: Delete , default: Delete => Delete nonstandard, reprogrammable, divertable, analytics key events, pos:14, group:0, group mask:empty reporting: default 14: F Lock , default: Do Nothing One => Do Nothing One is FN, analytics key events, pos:0, group:0, group mask:empty reporting: default 15: FN Key , default: Do Nothing One => Do Nothing One nonstandard, analytics key events, pos:0, group:0, group mask:empty reporting: default Battery: 40%, discharging. Solaar-1.1.14/docs/devices/MX Keys S B378.text000066400000000000000000000220471473525213700203240ustar00rootroot00000000000000solaar version 1.1.10 1: MX Keys S Device path : None WPID : B378 Codename : MX KEYS S Kind : keyboard Protocol : HID++ 4.5 Serial number: 48548420 Model ID: B37800000000 Unit ID: 48548420 Bootloader: BL1 88.00.B0013 Firmware: RBK 81.00.B0013 Other: The power switch is located on the (unknown). Supports 34 HID++ 2.0 features: 0: ROOT {0000} V0 1: FEATURE SET {0001} V0 2: DEVICE FW VERSION {0003} V4 Firmware: Bootloader BL1 88.00.B0013 B37851DB9520 Firmware: Firmware RBK 81.00.B0013 B37851DB9520 Firmware: Other Unit ID: 48548420 Model ID: B37800000000 Transport IDs: {'btleid': 'B378'} 3: DEVICE NAME {0005} V0 Name: MX Keys S Kind: keyboard 4: WIRELESS DEVICE STATUS {1D4B} V0 5: CONFIG CHANGE {0020} V0 6: DEVICE FRIENDLY NAME {0007} V0 Friendly Name: MX KEYS S 7: unknown:0011 {0011} V0 8: UNIFIED BATTERY {1004} V3 Battery: 75%, discharging. 9: REPROG CONTROLS V4 {1B04} V5 Key/Button Diversion (saved): {Calculator:Regular, Lock PC:Regular, Brightness Down:Regular, Brightness Up:Regular, Backlight Down:Regular, Backlight Up:Regular, Previous Fn:Regular, Play/Pause Fn:Regular, Next Fn:Regular, Mute Fn:Regular, Volume Down Fn:Regular, Volume Up Fn:Regular, App Contextual Menu/Right Click:Regular, Voice Dictation:Regular, Open Emoji Panel:Regular, Snipping Tool:Diverted, Mute Microphone:Regular} Key/Button Diversion : {Calculator:Regular, Lock PC:Regular, Brightness Down:Regular, Brightness Up:Regular, Backlight Down:Regular, Backlight Up:Regular, Previous Fn:Regular, Play/Pause Fn:Regular, Next Fn:Regular, Mute Fn:Regular, Volume Down Fn:Regular, Volume Up Fn:Regular, App Contextual Menu/Right Click:Regular, Voice Dictation:Regular, Open Emoji Panel:Regular, Snipping Tool:Diverted, Mute Microphone:Regular} 10: CHANGE HOST {1814} V1 Change Host : 1:vs 11: HOSTS INFO {1815} V2 Host 0 (paired): vs Host 1 (paired): DEV Host 2 (unpaired): 12: BACKLIGHT2 {1982} V3 Backlight (saved): False Backlight : True 13: K375S FN INVERSION {40A3} V0 Swap Fx function (saved): False Swap Fx function : False 14: LOCK KEY STATE {4220} V0 15: KEYBOARD DISABLE KEYS {4521} V0 Disable keys (saved): {Caps Lock:False, Num Lock:False, Scroll Lock:False, Insert:False, Win:False} Disable keys : {Caps Lock:False, Num Lock:False, Scroll Lock:False, Insert:False, Win:False} 16: MULTIPLATFORM {4531} V1 Set OS (saved): Linux Set OS : Linux 17: KEYBOARD LAYOUT 2 {4540} V0 18: DFUCONTROL {00C3} V0 19: DEVICE RESET {1802} V0 internal, hidden, unknown:000010 20: unknown:1803 {1803} V0 internal, hidden, unknown:000010 21: unknown:1807 {1807} V0 internal, hidden, unknown:000010 22: unknown:1816 {1816} V0 internal, hidden, unknown:000010 23: OOBSTATE {1805} V0 internal, hidden 24: unknown:1830 {1830} V0 internal, hidden, unknown:000010 25: unknown:1891 {1891} V7 internal, hidden, unknown:000008 26: unknown:18A1 {18A1} V0 internal, hidden, unknown:000010 27: unknown:1E00 {1E00} V0 hidden 28: unknown:1E02 {1E02} V0 internal, hidden 29: unknown:1602 {1602} V0 30: unknown:1EB0 {1EB0} V0 internal, hidden, unknown:000010 31: unknown:1861 {1861} V1 internal, hidden, unknown:000010 32: unknown:1A20 {1A20} V1 internal, hidden, unknown:000010 33: unknown:18B0 {18B0} V1 internal, hidden, unknown:000010 Has 21 reprogrammable keys: 0: Brightness Down , default: Brightness Down => Brightness Down is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:1, group:0, group mask:empty reporting: default 1: Brightness Up , default: Brightness Up => Brightness Up is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:2, group:0, group mask:empty reporting: default 2: Backlight Down , default: Backlight Down => Backlight Down is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:3, group:0, group mask:empty reporting: default 3: Backlight Up , default: Backlight Up => Backlight Up is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:4, group:0, group mask:empty reporting: default 4: Voice Dictation , default: Voice Dictation => Voice Dictation is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:5, group:0, group mask:empty reporting: default 5: Open Emoji Panel , default: Open Emoji Panel => Open Emoji Panel is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:6, group:0, group mask:empty reporting: default 6: Mute Microphone , default: Mute Microphone => Mute Microphone is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:7, group:0, group mask:empty reporting: default 7: Previous Fn , default: Previous => Previous is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:8, group:0, group mask:empty reporting: default 8: Play/Pause Fn , default: Play/Pause => Play/Pause is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:9, group:0, group mask:empty reporting: default 9: Next Fn , default: Next => Next is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:10, group:0, group mask:empty reporting: default 10: Mute Fn , default: Mute => Mute is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:11, group:0, group mask:empty reporting: default 11: Volume Down Fn , default: Volume Down => Volume Down is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:12, group:0, group mask:empty reporting: default 12: Volume Up Fn , default: Volume Up => Volume Up nonstandard, reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 13: Calculator , default: Calculator => Calculator nonstandard, reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 14: Snipping Tool , default: Snipping Tool => Snipping Tool nonstandard, reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: diverted 15: App Contextual Menu/Right Click, default: Right Click/App Contextual Menu => Right Click/App Contextual Menu nonstandard, reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 16: Lock PC , default: WindowsLock => WindowsLock nonstandard, reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 17: Host Switch Channel 1 , default: HostSwitch Channel 1 => HostSwitch Channel 1 nonstandard, analytics key events, pos:0, group:0, group mask:empty reporting: default 18: Host Switch Channel 2 , default: HostSwitch Channel 2 => HostSwitch Channel 2 nonstandard, analytics key events, pos:0, group:0, group mask:empty reporting: default 19: Host Switch Channel 3 , default: HostSwitch Channel 3 => HostSwitch Channel 3 nonstandard, analytics key events, pos:0, group:0, group mask:empty reporting: default 20: F Lock , default: Do Nothing One => Do Nothing One analytics key events, pos:0, group:0, group mask:empty reporting: default Battery: 75%, discharging. Solaar-1.1.14/docs/devices/MX Keys for Business B363.text000066400000000000000000000237031473525213700224560ustar00rootroot00000000000000Solaar version 1.1.5 1: MX Keys for Business Device path : None WPID : B363 Codename : MX Keys B Kind : keyboard Protocol : HID++ 4.5 Serial number: C8457F05 Model ID: B36300000000 Unit ID: C8457F05 Bootloader: BL1 31.00.B0009 Firmware: RBK 68.00.B0009 Other: The power switch is located on the (unknown). Supports 32 HID++ 2.0 features: 0: ROOT {0000} V0 1: FEATURE SET {0001} V0 2: DEVICE FW VERSION {0003} V4 Firmware: Bootloader BL1 31.00.B0009 B3636927CBB5 Firmware: Firmware RBK 68.00.B0009 B3636927CBB5 Firmware: Other Unit ID: C8457F05 Model ID: B36300000000 Transport IDs: {'btleid': 'B363'} 3: DEVICE NAME {0005} V0 Name: MX Keys for Business Kind: keyboard 4: WIRELESS DEVICE STATUS {1D4B} V0 5: RESET {0020} V0 6: DEVICE FRIENDLY NAME {0007} V0 Friendly Name: MX Keys B 7: UNIFIED BATTERY {1004} V2 Battery: 60%, discharging. 8: REPROG CONTROLS V4 {1B04} V5 Key/Button Diversion (saved): {Calculator:Regular, Lock PC:Regular, Brightness Down:Diverted, Brightness Up:Regular, Host Switch Channel 1:Regular, Host Switch Channel 2:Regular, Host Switch Channel 3:Regular, Backlight Down:Regular, Backlight Up:Regular, Previous Fn:Regular, Play/Pause Fn:Regular, Next Fn:Regular, Mute Fn:Regular, Volume Down Fn:Regular, Volume Up Fn:Regular, App Contextual Menu/Right Click:Regular, Right Arrow:Regular, Left Arrow:Regular, Voice Dictation:Regular, Open Emoji Panel:Regular, Snipping Tool:Regular, Mute Microphone:Regular} Key/Button Diversion : {Calculator:Regular, Lock PC:Regular, Brightness Down:Diverted, Brightness Up:Regular, Host Switch Channel 1:Regular, Host Switch Channel 2:Regular, Host Switch Channel 3:Regular, Backlight Down:Regular, Backlight Up:Regular, Previous Fn:Regular, Play/Pause Fn:Regular, Next Fn:Regular, Mute Fn:Regular, Volume Down Fn:Regular, Volume Up Fn:Regular, App Contextual Menu/Right Click:Regular, Right Arrow:Regular, Left Arrow:Regular, Voice Dictation:Regular, Open Emoji Panel:Regular, Snipping Tool:Regular, Mute Microphone:Regular} 9: CHANGE HOST {1814} V1 Change Host : 1:Ryzen 10: HOSTS INFO {1815} V1 Host 0 (paired): Ryzen Host 1 (paired): Ryzen Host 2 (paired): Mi 10 Lite 5G 11: BACKLIGHT2 {1982} V1 Backlight (saved): True Backlight : True 12: K375S FN INVERSION {40A3} V0 Swap Fx function (saved): False Swap Fx function : False 13: LOCK KEY STATE {4220} V0 14: KEYBOARD DISABLE KEYS {4521} V0 Disable keys (saved): {Caps Lock:False, Num Lock:False, Scroll Lock:False, Insert:False, Win:False} Disable keys : {Caps Lock:False, Num Lock:False, Scroll Lock:False, Insert:False, Win:False} 15: MULTIPLATFORM {4531} V1 Set OS (saved): Windows Set OS : Windows 16: DFUCONTROL {00C3} V0 17: DEVICE RESET {1802} V0 internal, hidden, unknown:000010 18: unknown:1803 {1803} V0 internal, hidden, unknown:000010 19: CONFIG DEVICE PROPS {1806} V8 internal, hidden, unknown:000010 20: unknown:1816 {1816} V0 internal, hidden, unknown:000010 21: OOBSTATE {1805} V0 internal, hidden 22: unknown:1830 {1830} V0 internal, hidden, unknown:000010 23: unknown:1891 {1891} V6 internal, hidden, unknown:000008 24: unknown:18A1 {18A1} V0 internal, hidden, unknown:000010 25: unknown:1E00 {1E00} V0 hidden 26: unknown:1E02 {1E02} V0 internal, hidden 27: unknown:1602 {1602} V0 28: unknown:1EB0 {1EB0} V0 internal, hidden, unknown:000010 29: unknown:1861 {1861} V0 internal, hidden, unknown:000010 30: unknown:1A20 {1A20} V0 internal, hidden, unknown:000010 31: unknown:18B0 {18B0} V0 internal, hidden, unknown:000010 Has 24 reprogrammable keys: 0: Host Switch Channel 1 , default: HostSwitch Channel 1 => HostSwitch Channel 1 nonstandard, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 1: Host Switch Channel 2 , default: HostSwitch Channel 2 => HostSwitch Channel 2 nonstandard, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 2: Host Switch Channel 3 , default: HostSwitch Channel 3 => HostSwitch Channel 3 nonstandard, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 3: Brightness Down , default: Brightness Down => Brightness Down is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:1, group:0, group mask:empty reporting: diverted 4: Brightness Up , default: Brightness Up => Brightness Up is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:2, group:0, group mask:empty reporting: default 5: Backlight Down , default: Backlight Down => Backlight Down is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:3, group:0, group mask:empty reporting: default 6: Backlight Up , default: Backlight Up => Backlight Up is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:4, group:0, group mask:empty reporting: default 7: Voice Dictation , default: Voice Dictation => Voice Dictation is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:5, group:0, group mask:empty reporting: default 8: Open Emoji Panel , default: Open Emoji Panel => Open Emoji Panel is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:6, group:0, group mask:empty reporting: default 9: Mute Microphone , default: Mute Microphone => Mute Microphone is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:7, group:0, group mask:empty reporting: default 10: Previous Fn , default: Previous => Previous is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:8, group:0, group mask:empty reporting: default 11: Play/Pause Fn , default: Play/Pause => Play/Pause is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:9, group:0, group mask:empty reporting: default 12: Next Fn , default: Next => Next is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:10, group:0, group mask:empty reporting: default 13: Mute Fn , default: Mute => Mute is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:11, group:0, group mask:empty reporting: default 14: Volume Down Fn , default: Volume Down => Volume Down is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:12, group:0, group mask:empty reporting: default 15: Volume Up Fn , default: Volume Up => Volume Up nonstandard, reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 16: Calculator , default: Calculator => Calculator nonstandard, reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 17: Snipping Tool , default: Snipping Tool => Snipping Tool nonstandard, reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 18: App Contextual Menu/Right Click, default: Right Click/App Contextual Menu => Right Click/App Contextual Menu nonstandard, reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 19: Lock PC , default: WindowsLock => WindowsLock nonstandard, reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 20: Left Arrow , default: Keyboard Left Arrow => Keyboard Left Arrow nonstandard, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 21: Right Arrow , default: Keyboard Right Arrow => Keyboard Right Arrow nonstandard, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 22: F Lock , default: Do Nothing One => Do Nothing One is FN, analytics key events, pos:0, group:0, group mask:empty reporting: default 23: FN Key , default: Do Nothing One => Do Nothing One nonstandard, analytics key events, pos:0, group:0, group mask:empty reporting: default Battery: 60%, discharging. Solaar-1.1.14/docs/devices/MX Master 3 Wireless Mouse 4082.txt000066400000000000000000000142451473525213700232410ustar00rootroot00000000000000Solaar version 1.1.4 1: MX Master 3 Wireless Mouse Device path : /dev/hidraw2 WPID : 4082 Codename : MX Master 3 Kind : mouse Protocol : HID++ 4.5 Polling rate : 8 ms (125Hz) Serial number: 198E3EB8 Model ID: B02340820000 Unit ID: 198E3EB8 Bootloader: BOT 95.01.B0015 Firmware: MPM 19.01.B0015 Other: The power switch is located on the base. Supports 34 HID++ 2.0 features: 0: ROOT {0000} 1: FEATURE SET {0001} 2: DEVICE FW VERSION {0003} Firmware: Bootloader BOT 95.01.B0015 00006A9CA89D Firmware: Firmware MPM 19.01.B0015 40826A9CA89D Firmware: Other Unit ID: 198E3EB8 Model ID: B02340820000 Transport IDs: {'btleid': 'B023', 'wpid': '4082'} 3: DEVICE NAME {0005} Name: Wireless Mouse MX Master 3 Kind: mouse 4: WIRELESS DEVICE STATUS {1D4B} 5: RESET {0020} 6: CRYPTO ID {0021} 7: DEVICE FRIENDLY NAME {0007} Friendly Name: MX Master 3 8: BATTERY STATUS {1000} Battery: 100%, discharging, next level 50%. 9: REPROG CONTROLS V4 {1B04} Key/Button Actions (saved): {Left Button:Left Click, Right Button:Right Click, Middle Button:Mouse Middle Button, Back Button:Mouse Back Button, Forward Button:Mouse Forward Button, Mouse Gesture Button:Gesture Button Navigation, Smart Shift:Smart Shift} Key/Button Actions : {Left Button:Left Click, Right Button:Right Click, Middle Button:Mouse Middle Button, Back Button:Mouse Back Button, Forward Button:Mouse Forward Button, Mouse Gesture Button:Gesture Button Navigation, Smart Shift:Smart Shift} Key/Button Diversion (saved): {Middle Button:Regular, Back Button:Regular, Forward Button:Regular, Mouse Gesture Button:Mouse Gestures, Smart Shift:Sliding DPI} Key/Button Diversion : {Middle Button:Regular, Back Button:Regular, Forward Button:Regular, Mouse Gesture Button:Regular, Smart Shift:Regular} 10: CHANGE HOST {1814} Change Host : 1:idefix 11: XY STATS {2250} 12: ADJUSTABLE DPI {2201} Sensitivity (DPI) (saved): 1600 Sensitivity (DPI) : 1600 13: SMART SHIFT {2110} Scroll Wheel Rachet (saved): 32 Scroll Wheel Rachet : 32 14: HIRES WHEEL {2121} Multiplier: 15 Has invert: Normal wheel motion Has ratchet switch: Normal wheel mode High resolution mode HID notification Scroll Wheel Direction (saved): False Scroll Wheel Direction : False Scroll Wheel Resolution (saved): True Scroll Wheel Resolution : True Scroll Wheel Diversion (saved): False Scroll Wheel Diversion : False 15: THUMB WHEEL {2150} Thumb Wheel Direction (saved): False Thumb Wheel Direction : False Thumb Wheel Diversion (saved): False Thumb Wheel Diversion : False 16: WHEEL STATS {2251} 17: DFUCONTROL SIGNED {00C2} 18: DEVICE RESET {1802} internal, hidden 19: unknown:1803 {1803} internal, hidden 20: CONFIG DEVICE PROPS {1806} internal, hidden 21: unknown:1813 {1813} internal, hidden 22: OOBSTATE {1805} internal, hidden 23: unknown:1830 {1830} internal, hidden 24: unknown:1890 {1890} internal, hidden 25: unknown:1891 {1891} internal, hidden 26: unknown:18A1 {18A1} internal, hidden 27: unknown:1DF3 {1DF3} internal, hidden 28: unknown:1E00 {1E00} hidden 29: unknown:1EB0 {1EB0} internal, hidden 30: unknown:1861 {1861} internal, hidden 31: unknown:9001 {9001} internal, hidden 32: unknown:9203 {9203} internal, hidden 33: unknown:9205 {9205} internal, hidden Has 8 reprogrammable keys: 0: Left Button , default: Left Click => Left Click mse, analytics key events, pos:0, group:1, group mask:g1 reporting: default 1: Right Button , default: Right Click => Right Click mse, analytics key events, pos:0, group:1, group mask:g1 reporting: default 2: Middle Button , default: Mouse Middle Button => Mouse Middle Button mse, reprogrammable, divertable, raw XY, analytics key events, pos:0, group:3, group mask:g1,g2,g3 reporting: default 3: Back Button , default: Mouse Back Button => Mouse Back Button mse, reprogrammable, divertable, raw XY, analytics key events, pos:0, group:2, group mask:g1,g2 reporting: default 4: Forward Button , default: Mouse Forward Button => Mouse Forward Button mse, reprogrammable, divertable, raw XY, analytics key events, pos:0, group:2, group mask:g1,g2 reporting: default 5: Mouse Gesture Button , default: Gesture Button Navigation => Gesture Button Navigation mse, reprogrammable, divertable, raw XY, analytics key events, pos:0, group:3, group mask:g1,g2,g3 reporting: default 6: Smart Shift , default: Smart Shift => Smart Shift mse, reprogrammable, divertable, raw XY, analytics key events, pos:0, group:3, group mask:g1,g2,g3 reporting: default 7: Virtual Gesture Button , default: Virtual Gesture Button => Virtual Gesture Button divertable, virtual, raw XY, force raw XY, pos:0, group:4, group mask:empty reporting: default Battery: 100%, discharging, next level 50%. Solaar-1.1.14/docs/devices/MX Master 3 Wireless Mouse B023.txt000066400000000000000000000135361473525213700232540ustar00rootroot00000000000000Solaar version 1.1.4 1: MX Master 3 Wireless Mouse Device path : /dev/hidraw5 USB id : 046d:B023 Codename : MX Master 3 Kind : mouse Protocol : HID++ 4.5 Serial number: Model ID: B02340820000 Unit ID: 198E3EB8 Bootloader: BOT 95.01.B0015 Firmware: MPM 19.01.B0015 Other: Supports 30 HID++ 2.0 features: 0: ROOT {0000} 1: FEATURE SET {0001} 2: DEVICE FW VERSION {0003} Firmware: Bootloader BOT 95.01.B0015 00006A9CA89D Firmware: Firmware MPM 19.01.B0015 B0236A9CA89D Firmware: Other Unit ID: 198E3EB8 Model ID: B02340820000 Transport IDs: {'btleid': 'B023', 'wpid': '4082'} 3: DEVICE NAME {0005} Name: Wireless Mouse MX Master 3 Kind: mouse 4: WIRELESS DEVICE STATUS {1D4B} 5: RESET {0020} 6: CRYPTO ID {0021} 7: DEVICE FRIENDLY NAME {0007} Friendly Name: MX Master 3 8: BATTERY STATUS {1000} Battery: 100%, discharging, next level 50%. 9: REPROG CONTROLS V4 {1B04} Key/Button Actions (saved): {Left Button:Left Click, Right Button:Right Click, Middle Button:Mouse Middle Button, Back Button:Mouse Back Button, Forward Button:Mouse Forward Button, Mouse Gesture Button:Gesture Button Navigation, Smart Shift:Smart Shift} Key/Button Actions : {Left Button:Left Click, Right Button:Right Click, Middle Button:Mouse Middle Button, Back Button:Mouse Back Button, Forward Button:Mouse Forward Button, Mouse Gesture Button:Gesture Button Navigation, Smart Shift:Smart Shift} Key/Button Diversion (saved): {Middle Button:Regular, Back Button:Regular, Forward Button:Regular, Mouse Gesture Button:Mouse Gestures, Smart Shift:Sliding DPI} Key/Button Diversion : {Middle Button:Regular, Back Button:Regular, Forward Button:Regular, Mouse Gesture Button:Regular, Smart Shift:Regular} 10: CHANGE HOST {1814} Change Host : 2:idefix 11: XY STATS {2250} 12: ADJUSTABLE DPI {2201} Sensitivity (DPI) (saved): 1600 Sensitivity (DPI) : 1600 13: SMART SHIFT {2110} Scroll Wheel Rachet (saved): 32 Scroll Wheel Rachet : 32 14: HIRES WHEEL {2121} Multiplier: 15 Has invert: Normal wheel motion Has ratchet switch: Normal wheel mode High resolution mode HID notification Scroll Wheel Direction (saved): False Scroll Wheel Direction : False Scroll Wheel Resolution (saved): True Scroll Wheel Resolution : True Scroll Wheel Diversion (saved): False Scroll Wheel Diversion : False 15: THUMB WHEEL {2150} Thumb Wheel Direction (saved): False Thumb Wheel Direction : False Thumb Wheel Diversion (saved): False Thumb Wheel Diversion : False 16: WHEEL STATS {2251} 17: DFUCONTROL SIGNED {00C2} 18: DEVICE RESET {1802} internal, hidden 19: unknown:1803 {1803} internal, hidden 20: CONFIG DEVICE PROPS {1806} internal, hidden 21: unknown:1813 {1813} internal, hidden 22: OOBSTATE {1805} internal, hidden 23: unknown:1830 {1830} internal, hidden 24: unknown:18A1 {18A1} internal, hidden 25: unknown:1E00 {1E00} hidden 26: unknown:1EB0 {1EB0} internal, hidden 27: unknown:1861 {1861} internal, hidden 28: unknown:9300 {9300} internal, hidden 29: unknown:9001 {9001} internal, hidden Has 8 reprogrammable keys: 0: Left Button , default: Left Click => Left Click mse, analytics key events, pos:0, group:1, group mask:g1 reporting: default 1: Right Button , default: Right Click => Right Click mse, analytics key events, pos:0, group:1, group mask:g1 reporting: default 2: Middle Button , default: Mouse Middle Button => Mouse Middle Button mse, reprogrammable, divertable, raw XY, analytics key events, pos:0, group:3, group mask:g1,g2,g3 reporting: default 3: Back Button , default: Mouse Back Button => Mouse Back Button mse, reprogrammable, divertable, raw XY, analytics key events, pos:0, group:2, group mask:g1,g2 reporting: default 4: Forward Button , default: Mouse Forward Button => Mouse Forward Button mse, reprogrammable, divertable, raw XY, analytics key events, pos:0, group:2, group mask:g1,g2 reporting: default 5: Mouse Gesture Button , default: Gesture Button Navigation => Gesture Button Navigation mse, reprogrammable, divertable, raw XY, analytics key events, pos:0, group:3, group mask:g1,g2,g3 reporting: default 6: Smart Shift , default: Smart Shift => Smart Shift mse, reprogrammable, divertable, raw XY, analytics key events, pos:0, group:3, group mask:g1,g2,g3 reporting: default 7: Virtual Gesture Button , default: Virtual Gesture Button => Virtual Gesture Button divertable, virtual, raw XY, force raw XY, pos:0, group:4, group mask:empty reporting: default Battery: 100%, discharging, next level 50%. Solaar-1.1.14/docs/devices/MX Master 3 for Business B028.text000066400000000000000000000150511473525213700231140ustar00rootroot00000000000000solaar version 1.1.8 1: MX Master 3 for Business Device path : None WPID : B028 Codename : MX Master 3 B Kind : mouse Protocol : HID++ 4.5 Serial number: 18F3413B Model ID: B02800000000 Unit ID: 18F3413B Bootloader: BL1 41.00.B0009 Firmware: RBM 14.00.B0009 Other: The power switch is located on the (unknown). Supports 35 HID++ 2.0 features: 0: ROOT {0000} V0 1: FEATURE SET {0001} V0 2: DEVICE FW VERSION {0003} V4 Firmware: Bootloader BL1 41.00.B0009 B0281D13EFC0 Firmware: Firmware RBM 14.00.B0009 B0281D13EFC0 Firmware: Other Unit ID: 18F3413B Model ID: B02800000000 Transport IDs: {'btleid': 'B028'} 3: DEVICE NAME {0005} V0 Name: MX Master 3 for Business Kind: mouse 4: WIRELESS DEVICE STATUS {1D4B} V0 5: CONFIG CHANGE {0020} V0 6: CRYPTO ID {0021} V1 7: DEVICE FRIENDLY NAME {0007} V0 Friendly Name: MX Master 3 B 8: UNIFIED BATTERY {1004} V2 Battery: 95%, discharging. 9: REPROG CONTROLS V4 {1B04} V5 Key/Button Actions (saved): {Left Button:Left Click, Right Button:Right Click, Middle Button:Mouse Middle Button, Back Button:Mouse Back Button, Forward Button:Mouse Forward Button, Mouse Gesture Button:Gesture Button Navigation, Smart Shift:Smart Shift} Key/Button Actions : {Left Button:Left Click, Right Button:Right Click, Middle Button:Mouse Middle Button, Back Button:Mouse Back Button, Forward Button:Mouse Forward Button, Mouse Gesture Button:Gesture Button Navigation, Smart Shift:Smart Shift} Key/Button Diversion (saved): {Middle Button:Regular, Back Button:Regular, Forward Button:Regular, Mouse Gesture Button:Regular, Smart Shift:Regular} Key/Button Diversion : {Middle Button:Regular, Back Button:Regular, Forward Button:Regular, Mouse Gesture Button:Regular, Smart Shift:Regular} 10: CHANGE HOST {1814} V1 Change Host : 1:bork 11: XY STATS {2250} V1 12: ADJUSTABLE DPI {2201} V2 Sensitivity (DPI) (saved): 1000 Sensitivity (DPI) : 1000 13: SMART SHIFT {2110} V0 Scroll Wheel Ratcheted (saved): Freespinning Scroll Wheel Ratcheted : Freespinning Scroll Wheel Ratchet Speed (saved): 1 Scroll Wheel Ratchet Speed : 1 14: HIRES WHEEL {2121} V1 Multiplier: 15 Has invert: Normal wheel motion Has ratchet switch: Free wheel mode Low resolution mode HID notification Scroll Wheel Direction (saved): False Scroll Wheel Direction : False Scroll Wheel Resolution (saved): False Scroll Wheel Resolution : False Scroll Wheel Diversion (saved): False Scroll Wheel Diversion : False 15: THUMB WHEEL {2150} V0 Thumb Wheel Direction (saved): False Thumb Wheel Direction : False Thumb Wheel Diversion (saved): False Thumb Wheel Diversion : False 16: WHEEL STATS {2251} V0 17: DFUCONTROL {00C3} V0 18: DEVICE RESET {1802} V0 internal, hidden, unknown:000010 19: unknown:1803 {1803} V0 internal, hidden, unknown:000010 20: CONFIG DEVICE PROPS {1806} V8 internal, hidden, unknown:000010 21: unknown:1816 {1816} V0 internal, hidden, unknown:000010 22: OOBSTATE {1805} V0 internal, hidden 23: unknown:1830 {1830} V0 internal, hidden, unknown:000010 24: unknown:1891 {1891} V6 internal, hidden, unknown:000008 25: unknown:18A1 {18A1} V0 internal, hidden, unknown:000010 26: unknown:1E00 {1E00} V0 hidden 27: unknown:1E02 {1E02} V0 internal, hidden 28: unknown:1602 {1602} V0 29: unknown:1EB0 {1EB0} V0 internal, hidden, unknown:000010 30: unknown:1861 {1861} V0 internal, hidden, unknown:000010 31: unknown:9300 {9300} V0 internal, hidden, unknown:000010 32: unknown:9001 {9001} V0 internal, hidden, unknown:000010 33: unknown:1E22 {1E22} V0 internal, hidden, unknown:000010 34: unknown:9205 {9205} V0 internal, hidden, unknown:000010 Has 8 reprogrammable keys: 0: Left Button , default: Left Click => Left Click mse, analytics key events, pos:0, group:1, group mask:g1 reporting: default 1: Right Button , default: Right Click => Right Click mse, analytics key events, pos:0, group:1, group mask:g1 reporting: default 2: Middle Button , default: Mouse Middle Button => Mouse Middle Button mse, reprogrammable, divertable, raw XY, analytics key events, pos:0, group:2, group mask:g1,g2 reporting: default 3: Back Button , default: Mouse Back Button => Mouse Back Button mse, reprogrammable, divertable, raw XY, analytics key events, pos:0, group:2, group mask:g1,g2 reporting: default 4: Forward Button , default: Mouse Forward Button => Mouse Forward Button mse, reprogrammable, divertable, raw XY, analytics key events, pos:0, group:2, group mask:g1,g2 reporting: default 5: Mouse Gesture Button , default: Gesture Button Navigation => Gesture Button Navigation mse, reprogrammable, divertable, raw XY, analytics key events, pos:0, group:2, group mask:g1,g2 reporting: default 6: Smart Shift , default: Smart Shift => Smart Shift mse, reprogrammable, divertable, raw XY, analytics key events, pos:0, group:2, group mask:g1,g2 reporting: default 7: Virtual Gesture Button , default: Virtual Gesture Button => Virtual Gesture Button divertable, virtual, raw XY, force raw XY, pos:0, group:3, group mask:empty reporting: default Battery: 95%, discharging. Solaar-1.1.14/docs/devices/MX Master 3S B034.txt000066400000000000000000000154071473525213700205510ustar00rootroot00000000000000Solaar version 1.1.7 2: MX Master 3S Device path : None WPID : B034 Codename : MX Master 3S Kind : mouse Protocol : HID++ 4.5 Serial number: CD2A6BB7 Model ID: B03400000000 Unit ID: CD2A6BB7 Bootloader: BL1 69.00.B0003 Firmware: RBM 22.00.B0003 Other: The power switch is located on the (unknown). Supports 36 HID++ 2.0 features: 0: ROOT {0000} V0 1: FEATURE SET {0001} V0 2: DEVICE FW VERSION {0003} V4 Firmware: Bootloader BL1 69.00.B0003 B03438FCA4E6 Firmware: Firmware RBM 22.00.B0003 B03438FCA4E6 Firmware: Other Unit ID: CD2A6BB7 Model ID: B03400000000 Transport IDs: {'btleid': 'B034'} 3: DEVICE NAME {0005} V0 Name: MX Master 3S Kind: mouse 4: WIRELESS DEVICE STATUS {1D4B} V0 5: RESET {0020} V0 6: CRYPTO ID {0021} V1 7: DEVICE FRIENDLY NAME {0007} V0 Friendly Name: MX Master 3S 8: UNIFIED BATTERY {1004} V3 Battery: 25%, discharging. 9: REPROG CONTROLS V4 {1B04} V5 Acciones de Tecla/Botón (saved): {Left Button:Left Click, Right Button:Right Click, Middle Button:Mouse Middle Button, Back Button:Mouse Back Button, Forward Button:Mouse Forward Button, Mouse Gesture Button:Gesture Button Navigation, Smart Shift:Smart Shift} Acciones de Tecla/Botón : {Left Button:Left Click, Right Button:Right Click, Middle Button:Mouse Middle Button, Back Button:Mouse Back Button, Forward Button:Mouse Forward Button, Mouse Gesture Button:Gesture Button Navigation, Smart Shift:Smart Shift} Desvio de Tecla/Botón (saved): {Middle Button:Normal, Back Button:Normal, Forward Button:Normal, Mouse Gesture Button:Normal, Smart Shift:Normal} Desvio de Tecla/Botón : {Middle Button:Normal, Back Button:Normal, Forward Button:Normal, Mouse Gesture Button:Normal, Smart Shift:Normal} 10: CHANGE HOST {1814} V1 Cambiar Host : 1:romanescu 11: HOSTS INFO {1815} V2 Host 0 (paired): romanescu Host 1 (unpaired): Host 2 (unpaired): 12: XY STATS {2250} V1 13: ADJUSTABLE DPI {2201} V2 Sensibilidad (PPP) (saved): 1000 Sensibilidad (PPP) : 1000 14: SMART SHIFT {2110} V0 Scroll Wheel Ratcheted (saved): Freespinning Scroll Wheel Ratcheted : Freespinning Scroll Wheel Ratchet Speed (saved): 0 Scroll Wheel Ratchet Speed : 1 15: HIRES WHEEL {2121} V1 Multiplier: 15 Has invert: Normal wheel motion Has ratchet switch: Free wheel mode Low resolution mode HID notification Dirección Rueda Desplazamiento (saved): False Dirección Rueda Desplazamiento : False Resolución Rueda Desplazamiento (saved): False Resolución Rueda Desplazamiento : False Desvio Rueda Desplazamiento (saved): False Desvio Rueda Desplazamiento : False 16: THUMB WHEEL {2150} V0 Dirección de la Rueda del Pulgar (saved): False Dirección de la Rueda del Pulgar : False Desvío de la Rueda del Pulgar (saved): False Desvío de la Rueda del Pulgar : False 17: WHEEL STATS {2251} V0 18: DFUCONTROL {00C3} V0 19: DEVICE RESET {1802} V0 internal, hidden, unknown:000010 20: unknown:1803 {1803} V0 internal, hidden, unknown:000010 21: CONFIG DEVICE PROPS {1806} V8 internal, hidden, unknown:000010 22: unknown:1816 {1816} V0 internal, hidden, unknown:000010 23: OOBSTATE {1805} V0 internal, hidden 24: unknown:1830 {1830} V0 internal, hidden, unknown:000010 25: unknown:1891 {1891} V7 internal, hidden, unknown:000008 26: unknown:18A1 {18A1} V0 internal, hidden, unknown:000010 27: unknown:1E00 {1E00} V0 hidden 28: unknown:1E02 {1E02} V0 internal, hidden 29: unknown:1602 {1602} V0 30: unknown:1EB0 {1EB0} V0 internal, hidden, unknown:000010 31: unknown:1861 {1861} V1 internal, hidden, unknown:000010 32: unknown:9300 {9300} V0 internal, hidden, unknown:000010 33: unknown:9001 {9001} V0 internal, hidden, unknown:000010 34: unknown:1E22 {1E22} V0 internal, hidden, unknown:000010 35: unknown:9205 {9205} V0 internal, hidden, unknown:000010 Has 8 reprogrammable keys: 0: Left Button , default: Left Click => Left Click mse, analytics key events, pos:0, group:1, group mask:g1 reporting: default 1: Right Button , default: Right Click => Right Click mse, analytics key events, pos:0, group:1, group mask:g1 reporting: default 2: Middle Button , default: Mouse Middle Button => Mouse Middle Button mse, reprogrammable, divertable, raw XY, analytics key events, pos:0, group:2, group mask:g1,g2 reporting: default 3: Back Button , default: Mouse Back Button => Mouse Back Button mse, reprogrammable, divertable, raw XY, analytics key events, pos:0, group:2, group mask:g1,g2 reporting: default 4: Forward Button , default: Mouse Forward Button => Mouse Forward Button mse, reprogrammable, divertable, raw XY, analytics key events, pos:0, group:2, group mask:g1,g2 reporting: default 5: Mouse Gesture Button , default: Gesture Button Navigation => Gesture Button Navigation mse, reprogrammable, divertable, raw XY, analytics key events, pos:0, group:2, group mask:g1,g2 reporting: default 6: Smart Shift , default: Smart Shift => Smart Shift mse, reprogrammable, divertable, raw XY, analytics key events, pos:0, group:2, group mask:g1,g2 reporting: default 7: Virtual Gesture Button , default: Virtual Gesture Button => Virtual Gesture Button divertable, virtual, raw XY, force raw XY, pos:0, group:3, group mask:empty reporting: default Battery: 25%, discharging. Solaar-1.1.14/docs/devices/MX Mechanical B366.txt000066400000000000000000000340261473525213700211020ustar00rootroot00000000000000Solaar version 1.1.7 1: MX Mechanical Device path : None WPID : B366 Codename : MX MCHNCL Kind : keyboard Protocol : HID++ 4.5 Serial number: 2DF63C09 Model ID: B36600000000 Unit ID: 2DF63C09 Bootloader: BL1 45.00.B0016 Firmware: RBK 71.00.B0016 Other: The power switch is located on the (unknown). Supports 33 HID++ 2.0 features: 0: ROOT {0000} V0 1: FEATURE SET {0001} V0 2: DEVICE FW VERSION {0003} V4 Firmware: Bootloader BL1 45.00.B0016 B3662CB9B8E2 Firmware: Firmware RBK 71.00.B0016 B3662CB9B8E2 Firmware: Other Unit ID: 2DF63C09 Model ID: B36600000000 Transport IDs: {'btleid': 'B366'} 3: DEVICE NAME {0005} V0 Name: MX Mechanical Kind: keyboard 4: WIRELESS DEVICE STATUS {1D4B} V0 5: RESET {0020} V0 6: DEVICE FRIENDLY NAME {0007} V0 Friendly Name: MX MCHNCL 7: UNIFIED BATTERY {1004} V3 Battery: 50%, discharging. 8: REPROG CONTROLS V4 {1B04} V5 Desvio de Tecla/Botón (saved): {Calculator:Normal, Show Desktop:Normal, Lock PC:Normal, Brightness Down:Normal, Brightness Up:Normal, MultiPlatform Search:Normal, Backlight Down:Normal, Backlight Up:Normal, Previous Fn:Normal, Play/Pause Fn:Normal, Next Fn:Normal, Mute Fn:Normal, Volume Down Fn:Normal, Volume Up Fn:Normal, Voice Dictation:Normal, Open Emoji Panel:Normal, Snipping Tool:Normal, Grave Accent:Normal, Tab Key:Normal, Caps Lock:Normal, Left Shift:Normal, Left Control:Normal, Left Option/Start:Normal, Left Command/Alt:Normal, Right Command/Alt:Normal, Right Option/Start:Normal, Right Control:Normal, Right Shift:Normal, Insert:Normal, Delete:Normal, Home:Normal, End:Normal, Page Up:Normal, Page Down:Normal, Mute Microphone:Normal, Backslash:Normal} Desvio de Tecla/Botón : {Calculator:Normal, Show Desktop:Normal, Lock PC:Normal, Brightness Down:Normal, Brightness Up:Normal, MultiPlatform Search:Normal, Backlight Down:Normal, Backlight Up:Normal, Previous Fn:Normal, Play/Pause Fn:Normal, Next Fn:Normal, Mute Fn:Normal, Volume Down Fn:Normal, Volume Up Fn:Normal, Voice Dictation:Normal, Open Emoji Panel:Normal, Snipping Tool:Normal, Grave Accent:Normal, Tab Key:Normal, Caps Lock:Normal, Left Shift:Normal, Left Control:Normal, Left Option/Start:Normal, Left Command/Alt:Normal, Right Command/Alt:Normal, Right Option/Start:Normal, Right Control:Normal, Right Shift:Normal, Insert:Normal, Delete:Normal, Home:Normal, End:Normal, Page Up:Normal, Page Down:Normal, Mute Microphone:Normal, Backslash:Normal} 9: CHANGE HOST {1814} V1 Cambiar Host : 1:romanescu 10: HOSTS INFO {1815} V2 Host 0 (paired): romanescu Host 1 (unpaired): Host 2 (unpaired): 11: BACKLIGHT2 {1982} V2 Retroiluminación (saved): True Retroiluminación : True 12: K375S FN INVERSION {40A3} V0 Intercambiar función Fx (saved): True Intercambiar función Fx : True 13: LOCK KEY STATE {4220} V0 14: KEYBOARD DISABLE KEYS {4521} V0 Desactivar teclas (saved): {Caps Lock:False, Num Lock:False, Scroll Lock:False, Insert:False, Win:False} Desactivar teclas : {Caps Lock:False, Num Lock:False, Scroll Lock:False, Insert:False, Win:False} 15: MULTIPLATFORM {4531} V1 Especificar SO (saved): Windows Especificar SO : Windows 16: KEYBOARD LAYOUT 2 {4540} V0 17: DFUCONTROL {00C3} V0 18: DEVICE RESET {1802} V0 internal, hidden, unknown:000010 19: unknown:1803 {1803} V0 internal, hidden, unknown:000010 20: CONFIG DEVICE PROPS {1806} V8 internal, hidden, unknown:000010 21: unknown:1816 {1816} V0 internal, hidden, unknown:000010 22: OOBSTATE {1805} V0 internal, hidden 23: unknown:1830 {1830} V0 internal, hidden, unknown:000010 24: unknown:1891 {1891} V7 internal, hidden, unknown:000008 25: unknown:18A1 {18A1} V0 internal, hidden, unknown:000010 26: unknown:1E00 {1E00} V0 hidden 27: unknown:1E02 {1E02} V0 internal, hidden 28: unknown:1602 {1602} V0 29: unknown:1EB0 {1EB0} V0 internal, hidden, unknown:000010 30: unknown:1861 {1861} V1 internal, hidden, unknown:000010 31: unknown:1A20 {1A20} V0 internal, hidden, unknown:000010 32: unknown:18B0 {18B0} V0 internal, hidden, unknown:000010 Has 41 reprogrammable keys: 0: Brightness Down , default: Brightness Down => Brightness Down is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:1, group:0, group mask:empty reporting: default 1: Brightness Up , default: Brightness Up => Brightness Up is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:2, group:0, group mask:empty reporting: default 2: Backlight Down , default: Backlight Down => Backlight Down is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:3, group:0, group mask:empty reporting: default 3: Backlight Up , default: Backlight Up => Backlight Up is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:4, group:0, group mask:empty reporting: default 4: Voice Dictation , default: Voice Dictation => Voice Dictation is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:5, group:0, group mask:empty reporting: default 5: Open Emoji Panel , default: Open Emoji Panel => Open Emoji Panel is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:6, group:0, group mask:empty reporting: default 6: Snipping Tool , default: Snipping Tool => Snipping Tool is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:7, group:0, group mask:empty reporting: default 7: Mute Microphone , default: Mute Microphone => Mute Microphone is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:8, group:0, group mask:empty reporting: default 8: Previous Fn , default: Previous => Previous is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:9, group:0, group mask:empty reporting: default 9: Play/Pause Fn , default: Play/Pause => Play/Pause is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:10, group:0, group mask:empty reporting: default 10: Next Fn , default: Next => Next is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:11, group:0, group mask:empty reporting: default 11: Mute Fn , default: Mute => Mute is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:12, group:0, group mask:empty reporting: default 12: Volume Down Fn , default: Volume Down => Volume Down nonstandard, reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 13: Volume Up Fn , default: Volume Up => Volume Up nonstandard, reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 14: Grave Accent , default: Grave Accent => Grave Accent reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 15: Tab Key , default: Standard Tab Key => Standard Tab Key reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 16: Caps Lock , default: Caps Lock => Caps Lock reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 17: Left Shift , default: Left Shift => Left Shift reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 18: Left Control , default: Left Control => Left Control reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 19: Left Option/Start , default: Left Option/Start => Left Option/Start reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 20: Left Command/Alt , default: Left Command/Alt => Left Command/Alt reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 21: Right Command/Alt , default: Right Command/Alt => Right Command/Alt reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 22: Right Option/Start , default: Right Option/Start => Right Option/Start reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 23: Right Control , default: Right Control => Right Control reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 24: Right Shift , default: Right Shift => Right Shift reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 25: Backslash , default: Backslash => Backslash reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 26: Insert , default: Insert => Insert reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 27: Delete , default: Delete => Delete reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 28: Home , default: Home => Home reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 29: End , default: End => End reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 30: Page Up , default: Page Up => Page Up reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 31: Page Down , default: Page Down => Page Down reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 32: Calculator , default: Calculator => Calculator nonstandard, reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 33: Show Desktop , default: Show Desktop => Show Desktop nonstandard, reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 34: MultiPlatform Search , default: Multiplatform Search => Multiplatform Search nonstandard, reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 35: Lock PC , default: WindowsLock => WindowsLock nonstandard, reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 36: Host Switch Channel 1 , default: HostSwitch Channel 1 => HostSwitch Channel 1 nonstandard, analytics key events, pos:0, group:0, group mask:empty reporting: default 37: Host Switch Channel 2 , default: HostSwitch Channel 2 => HostSwitch Channel 2 nonstandard, analytics key events, pos:0, group:0, group mask:empty reporting: default 38: Host Switch Channel 3 , default: HostSwitch Channel 3 => HostSwitch Channel 3 nonstandard, analytics key events, pos:0, group:0, group mask:empty reporting: default 39: F Lock , default: Do Nothing One => Do Nothing One is FN, analytics key events, pos:0, group:0, group mask:empty reporting: default 40: FN Key , default: Do Nothing One => Do Nothing One nonstandard, analytics key events, pos:0, group:0, group mask:empty reporting: default Battery: 50%, discharging. Solaar-1.1.14/docs/devices/MX Mechanical Mini B367.txt000066400000000000000000000271031473525213700217560ustar00rootroot00000000000000Solaar version 1.1.4 1: MX Mechanical Mini Device path : None WPID : B367 Codename : MX MCHNCL M Kind : keyboard Protocol : HID++ 4.5 Serial number: D4E94F5C Model ID: B36700000000 Unit ID: D4E94F5C Bootloader: BL1 50.00.B0010 Firmware: RBK 72.00.B0010 Other: The power switch is located on the (unknown). Supports 32 HID++ 2.0 features: 0: ROOT {0000} V0 1: FEATURE SET {0001} V0 2: DEVICE FW VERSION {0003} V4 Firmware: Bootloader BL1 50.00.B0010 B367A91B7FC8 Firmware: Firmware RBK 72.00.B0010 B367A91B7FC8 Firmware: Other Unit ID: D4E94F5C Model ID: B36700000000 Transport IDs: {'btleid': 'B367'} 3: DEVICE NAME {0005} V0 Name: MX Mechanical Mini Kind: keyboard 4: WIRELESS DEVICE STATUS {1D4B} V0 5: RESET {0020} V0 6: DEVICE FRIENDLY NAME {0007} V0 Friendly Name: MX MCHNCL M 7: UNIFIED BATTERY {1004} V3 Battery: 90%, discharging. 8: REPROG CONTROLS V4 {1B04} V5 Key/Button Diversion (saved): {MultiPlatform Search:Regular, Backlight Down:Regular, Backlight Up:Regular, Mute Fn:Regular, Volume Down Fn:Regular, Volume Up Fn:Regular, Voice Dictation:Regular, Open Emoji Panel:Regular, Snipping Tool:Regular, Grave Accent:Regular, Tab Key:Regular, Caps Lock:Regular, Left Shift:Regular, Left Control:Regular, Left Option/Start:Regular, Left Command/Alt:Regular, Right Command/Alt:Regular, Right Shift:Regular, Delete:Regular, Home:Regular, End:Regular, Page Up:Regular, Page Down:Regular, Mute Microphone:Regular, Backslash:Regular, unknown:013C:Regular, unknown:0141:Regular} Key/Button Diversion : {MultiPlatform Search:Regular, Backlight Down:Regular, Backlight Up:Regular, Mute Fn:Regular, Volume Down Fn:Regular, Volume Up Fn:Regular, Voice Dictation:Regular, Open Emoji Panel:Regular, Snipping Tool:Regular, Grave Accent:Regular, Tab Key:Regular, Caps Lock:Regular, Left Shift:Regular, Left Control:Regular, Left Option/Start:Regular, Left Command/Alt:Regular, Right Command/Alt:Regular, Right Shift:Regular, Delete:Regular, Home:Regular, End:Regular, Page Up:Regular, Page Down:Regular, Mute Microphone:Regular, Backslash:Regular, unknown:013C:Regular, unknown:0141:Regular} 9: CHANGE HOST {1814} V1 Change Host : 1:chimera 10: HOSTS INFO {1815} V2 Host 0 (paired): chimera Host 1 (paired): Logitech Bolt receiver Host 2 (unpaired): 11: BACKLIGHT2 {1982} V2 Backlight (saved): True Backlight : True 12: K375S FN INVERSION {40A3} V0 Swap Fx function (saved): True Swap Fx function : True 13: LOCK KEY STATE {4220} V0 14: KEYBOARD DISABLE KEYS {4521} V0 Disable keys (saved): {Caps Lock:False, Scroll Lock:False, Insert:False, Win:False} Disable keys : {Caps Lock:False, Scroll Lock:False, Insert:False, Win:False} 15: MULTIPLATFORM {4531} V1 Set OS (saved): Windows Set OS : Windows 16: KEYBOARD LAYOUT 2 {4540} V0 17: DFUCONTROL {00C3} V0 18: DEVICE RESET {1802} V0 internal, hidden, unknown:000010 19: unknown:1803 {1803} V0 internal, hidden, unknown:000010 20: CONFIG DEVICE PROPS {1806} V8 internal, hidden, unknown:000010 21: unknown:1816 {1816} V0 internal, hidden, unknown:000010 22: OOBSTATE {1805} V0 internal, hidden 23: unknown:1830 {1830} V0 internal, hidden, unknown:000010 24: unknown:1891 {1891} V7 internal, hidden, unknown:000008 25: unknown:18A1 {18A1} V0 internal, hidden, unknown:000010 26: unknown:1E00 {1E00} V0 hidden 27: unknown:1E02 {1E02} V0 internal, hidden 28: unknown:1602 {1602} V0 29: unknown:1EB0 {1EB0} V0 internal, hidden, unknown:000010 30: unknown:1861 {1861} V1 internal, hidden, unknown:000010 31: unknown:1A20 {1A20} V0 internal, hidden, unknown:000010 Has 32 reprogrammable keys: 0: Host Switch Channel 1 , default: HostSwitch Channel 1 => HostSwitch Channel 1 is FN, FN sensitive, analytics key events, pos:1, group:0, group mask:empty reporting: default 1: Host Switch Channel 2 , default: HostSwitch Channel 2 => HostSwitch Channel 2 is FN, FN sensitive, analytics key events, pos:2, group:0, group mask:empty reporting: default 2: Host Switch Channel 3 , default: HostSwitch Channel 3 => HostSwitch Channel 3 is FN, FN sensitive, analytics key events, pos:3, group:0, group mask:empty reporting: default 3: Backlight Down , default: Backlight Down => Backlight Down is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:4, group:0, group mask:empty reporting: default 4: Backlight Up , default: Backlight Up => Backlight Up is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:5, group:0, group mask:empty reporting: default 5: Voice Dictation , default: Voice Dictation => Voice Dictation is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:6, group:0, group mask:empty reporting: default 6: Open Emoji Panel , default: Open Emoji Panel => Open Emoji Panel is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:7, group:0, group mask:empty reporting: default 7: Snipping Tool , default: Snipping Tool => Snipping Tool is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:8, group:0, group mask:empty reporting: default 8: Mute Microphone , default: Mute Microphone => Mute Microphone is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:9, group:0, group mask:empty reporting: default 9: MultiPlatform Search , default: Multiplatform Search => Multiplatform Search is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:10, group:0, group mask:empty reporting: default 10: unknown:0141 , default: unknown:00FD => unknown:00FD is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:11, group:0, group mask:empty reporting: default 11: Mute Fn , default: Mute => Mute is FN, FN sensitive, reprogrammable, divertable, analytics key events, pos:12, group:0, group mask:empty reporting: default 12: Volume Down Fn , default: Volume Down => Volume Down nonstandard, reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 13: Volume Up Fn , default: Volume Up => Volume Up nonstandard, reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 14: Grave Accent , default: Grave Accent => Grave Accent reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 15: Tab Key , default: Standard Tab Key => Standard Tab Key reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 16: Caps Lock , default: Caps Lock => Caps Lock reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 17: Left Shift , default: Left Shift => Left Shift reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 18: Left Control , default: Left Control => Left Control reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 19: Left Option/Start , default: Left Option/Start => Left Option/Start reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 20: Left Command/Alt , default: Left Command/Alt => Left Command/Alt reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 21: Right Command/Alt , default: Right Command/Alt => Right Command/Alt reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 22: unknown:013C , default: Right Option/Start/2 => Right Option/Start/2 reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 23: Right Shift , default: Right Shift => Right Shift reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 24: Backslash , default: Backslash => Backslash reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 25: Delete , default: Delete => Delete reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 26: Home , default: Home => Home reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 27: End , default: End => End reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 28: Page Up , default: Page Up => Page Up reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 29: Page Down , default: Page Down => Page Down reprogrammable, divertable, analytics key events, pos:0, group:0, group mask:empty reporting: default 30: F Lock , default: Do Nothing One => Do Nothing One is FN, analytics key events, pos:0, group:0, group mask:empty reporting: default 31: FN Key , default: Do Nothing One => Do Nothing One nonstandard, analytics key events, pos:0, group:0, group mask:empty reporting: default Battery: 90%, discharging. Solaar-1.1.14/docs/devices/MX Vertical Wireless Mouse 407B.txt000066400000000000000000000140731473525213700234520ustar00rootroot00000000000000Solaar version 1.1.3 2: MX Vertical Wireless Mouse Device path : /dev/hidraw5 WPID : 407B Codename : MX Vertical Kind : mouse Protocol : HID++ 4.5 Polling rate : 8 ms (125Hz) Serial number: 87A544C3 Model ID: B020407BC08A Unit ID: 7818C6EE Bootloader: BOT 78.00.B0009 Firmware: MPM 16.00.B0009 Other: Supports 34 HID++ 2.0 features: 0: ROOT {0000} 1: FEATURE SET {0001} 2: DEVICE FW VERSION {0003} Firmware: Bootloader BOT 78.00.B0009 AAE28E5B17FF Firmware: Firmware MPM 16.00.B0009 407B8E5B17FF Firmware: Other Unit ID: 7818C6EE Model ID: B020407BC08A Transport IDs: {'btleid': 'B020', 'wpid': '407B', 'usbid': 'C08A'} 3: DEVICE NAME {0005} Name: MX Vertical Advanced Ergonomic Mouse Kind: mouse 4: WIRELESS DEVICE STATUS {1D4B} 5: RESET {0020} 6: CRYPTO ID {0021} 7: DEVICE FRIENDLY NAME {0007} Friendly Name: MX Vertical 8: BATTERY STATUS {1000} Battery: 50%, discharging, next level 20%. 9: unknown:1002 {1002} 10: REPROG CONTROLS V4 {1B04} DPI Sliding Adjustment (saved): Off DPI Sliding Adjustment : Off Mouse Gestures (saved): Off Mouse Gestures : Off Key/Button Actions (saved): {Left Button:Left Click, Right Button:Right Click, Middle Button:Mouse Middle Button, Back Button:Mouse Back Button, Forward Button:Mouse Forward Button, DPI Switch:DPI Switch} Key/Button Actions : {Left Button:Left Click, Right Button:Right Click, Middle Button:Mouse Middle Button, Back Button:Mouse Back Button, Forward Button:Mouse Forward Button, DPI Switch:DPI Switch} Key/Button Diversion (saved): {Middle Button:Regular, Back Button:Diverted, Forward Button:Diverted, ?, DPI Switch:Diverted} Key/Button Diversion : {Middle Button:Regular, Back Button:Diverted, Forward Button:Diverted, DPI Switch:Diverted} 11: PERSISTENT REMAPPABLE ACTION {1C00} 12: CHANGE HOST {1814} Change Host : 1:pudim 13: HOSTS INFO {1815} Host 0 (unpaired): pudim Host 1 (unpaired): Host 2 (unpaired): 14: XY STATS {2250} 15: unknown:18B1 {18B1} internal, hidden 16: VERTICAL SCROLLING {2100} Roller type: standard Ratchet per turn: 24 Scroll lines: 0 17: LOWRES WHEEL {2130} Wheel Reports: HID Scroll Wheel Diversion (saved): False Scroll Wheel Diversion : False 18: ADJUSTABLE DPI {2201} Sensitivity (DPI) (saved): 2800 Sensitivity (DPI) : 2800 19: DEVICE RESET {1802} internal, hidden 20: unknown:1803 {1803} internal, hidden 21: CONFIG DEVICE PROPS {1806} internal, hidden 22: unknown:1813 {1813} internal, hidden 23: OOBSTATE {1805} internal, hidden 24: unknown:1830 {1830} internal, hidden 25: unknown:1890 {1890} internal, hidden 26: unknown:1891 {1891} internal, hidden 27: unknown:18A1 {18A1} internal, hidden 28: unknown:1DF3 {1DF3} internal, hidden 29: unknown:1E00 {1E00} hidden 30: unknown:1EB0 {1EB0} internal, hidden 31: unknown:1E22 {1E22} internal, hidden 32: unknown:1861 {1861} internal, hidden 33: unknown:18C0 {18C0} internal, hidden Has 7 reprogrammable keys: 0: Left Button , default: Left Click => Left Click mse, reprogrammable, analytics key events, pos:0, group:1, group mask:g1 reporting: default 1: Right Button , default: Right Click => Right Click mse, reprogrammable, analytics key events, pos:0, group:1, group mask:g1 reporting: default 2: Middle Button , default: Mouse Middle Button => Mouse Middle Button mse, reprogrammable, divertable, persistently divertable, raw XY, analytics key events, pos:0, group:2, group mask:g1,g2 reporting: default 3: Back Button , default: Mouse Back Button => Mouse Back Button mse, reprogrammable, divertable, persistently divertable, raw XY, analytics key events, pos:0, group:2, group mask:g1,g2 reporting: diverted 4: Forward Button , default: Mouse Forward Button => Mouse Forward Button mse, reprogrammable, divertable, persistently divertable, raw XY, analytics key events, pos:0, group:2, group mask:g1,g2 reporting: diverted 5: DPI Switch , default: DPI Switch => DPI Switch mse, reprogrammable, divertable, persistently divertable, raw XY, analytics key events, pos:0, group:2, group mask:g1,g2 reporting: diverted 6: Virtual Gesture Button , default: Virtual Gesture Button => Virtual Gesture Button divertable, virtual, raw XY, force raw XY, pos:0, group:3, group mask:empty reporting: default Has 6 persistent remappable keys: 0: Left Button => Mouse Button: Mouse Button Left 1: Right Button => Mouse Button: Mouse Button Right 2: Middle Button => Mouse Button: Mouse Button Middle 3: Back Button => Mouse Button: Mouse Button Back 4: Forward Button => Mouse Button: Mouse Button Forward 5: unknown:0000 => None Battery: 50%, discharging, next level 20%. Solaar-1.1.14/docs/devices/MX Vertical Wireless Mouse B020.txt000066400000000000000000000114761473525213700234450ustar00rootroot00000000000000Solaar version 1.1.3 1: MX Vertical Wireless Mouse Device path : /dev/hidraw0 USB id : 046d:B020 Codename : MX Vertical Kind : mouse Protocol : HID++ 4.5 Serial number: Model ID: B020407BC08A Unit ID: 70926049 Bootloader: BOT 78.00.B0009 Firmware: MPM 16.00.B0009 Other: Supports 30 HID++ 2.0 features: 0: ROOT {0000} 1: FEATURE SET {0001} 2: DEVICE FW VERSION {0003} Firmware: Bootloader BOT 78.00.B0009 AAE28E5B17FF Firmware: Firmware MPM 16.00.B0009 B0208E5B17FF Firmware: Other Unit ID: 70926049 Model ID: B020407BC08A Transport IDs: {'btleid': 'B020', 'wpid': '407B', 'usbid': 'C08A'} 3: DEVICE NAME {0005} Name: MX Vertical Advanced Ergonomic Mouse Kind: mouse 4: WIRELESS DEVICE STATUS {1D4B} 5: RESET {0020} 6: CRYPTO ID {0021} 7: DEVICE FRIENDLY NAME {0007} Friendly Name: MX Vertical 8: BATTERY STATUS {1000} Battery: 90%, discharging, next level 50%. 9: unknown:1002 {1002} 10: REPROG CONTROLS V4 {1B04} 11: PERSISTENT REMAPPABLE ACTION {1C00} 12: CHANGE HOST {1814} 13: HOSTS INFO {1815} Host 0 (unpaired): minerva Host 1 (unpaired): theseus Host 2 (unpaired): athena 14: XY STATS {2250} 15: unknown:18B1 {18B1} internal, hidden 16: VERTICAL SCROLLING {2100} Roller type: standard Ratchet per turn: 24 Scroll lines: 0 17: LOWRES WHEEL {2130} Wheel Reports: HID Scroll Wheel Diversion (saved): False Scroll Wheel Diversion : False 18: ADJUSTABLE DPI {2201} 19: unknown:1803 {1803} internal, hidden 20: CONFIG DEVICE PROPS {1806} internal, hidden 21: unknown:1813 {1813} internal, hidden 22: OOBSTATE {1805} internal, hidden 23: unknown:1830 {1830} internal, hidden 24: unknown:18A1 {18A1} internal, hidden 25: unknown:1E00 {1E00} hidden 26: unknown:1EB0 {1EB0} internal, hidden 27: unknown:1E22 {1E22} internal, hidden 28: unknown:1861 {1861} internal, hidden 29: unknown:18C0 {18C0} internal, hidden Has 7 reprogrammable keys: 0: Left Button , default: Left Click => Left Click mse, reprogrammable, analytics key events, pos:0, group:1, group mask:g1 reporting: default 1: Right Button , default: Right Click => Right Click mse, reprogrammable, analytics key events, pos:0, group:1, group mask:g1 reporting: default 2: Middle Button , default: Mouse Middle Button => Mouse Middle Button mse, reprogrammable, divertable, persistently divertable, raw XY, analytics key events, pos:0, group:2, group mask:g1,g2 reporting: default 3: Back Button , default: Mouse Back Button => Mouse Back Button mse, reprogrammable, divertable, persistently divertable, raw XY, analytics key events, pos:0, group:2, group mask:g1,g2 reporting: default 4: Forward Button , default: Mouse Forward Button => Mouse Forward Button mse, reprogrammable, divertable, persistently divertable, raw XY, analytics key events, pos:0, group:2, group mask:g1,g2 reporting: default 5: DPI Switch , default: DPI Switch => DPI Switch mse, reprogrammable, divertable, persistently divertable, raw XY, analytics key events, pos:0, group:2, group mask:g1,g2 reporting: default 6: Virtual Gesture Button , default: Virtual Gesture Button => Virtual Gesture Button divertable, virtual, raw XY, force raw XY, pos:0, group:3, group mask:empty reporting: default Has 6 persistent remappable keys: 0: Left Button => Mouse Button: Mouse Button Left 1: Right Button => Mouse Button: Mouse Button Right 2: Middle Button => Mouse Button: Mouse Button Middle 3: Back Button => Mouse Button: Mouse Button Back 4: Forward Button => Mouse Button: Mouse Button Forward 5: unknown:0000 => None Battery: 90%, discharging, next level 50%. Solaar-1.1.14/docs/devices/Marathon Mouse M705 101B.txt000066400000000000000000000042611473525213700217650ustar00rootroot00000000000000Solaar version 1.1.5 1: Marathon Mouse M705 (M-R0009) Device path : /dev/hidraw1 WPID : 101B Codename : M705 (M-R0009) Kind : mouse Protocol : HID++ 1.0 Polling rate : 8 ms (125Hz) Serial number: 23691D3D Firmware: 17.01.B0017 Bootloader: 02.06 Other: 00.04 The power switch is located on the base. Notifications: battery status (0x100000). Features: special buttons, scroll accel (0x420000) Battery: 65%, discharging. registers: # writing 0x10 in this register will generate an event # 10 02 0Dxx yyzz00 # where 0D happens to be the battery register number # xx is the battery charge # yy, zz ? << ( 0.001) [10 02 8100 000000] '\x10\x02\x81\x00\x00\x00\x00' >> ( 1.132) [10 02 8100 100000] '\x10\x02\x81\x00\x10\x00\x00' # smooth scroll - possible values # - 00 (off) # - 02 ?, apparently off as well, default value at power-on # - 0x40 (on) << ( 2.005) [10 02 8101 000000] '\x10\x02\x81\x01\x00\x00\x00' >> ( 2.052) [10 02 8101 020000] '\x10\x02\x81\x01\x02\x00\x00' # battery status: percentage full, ?, ? << ( 14.835) [10 02 810D 000000] '\x10\x02\x81\r\x00\x00\x00' >> ( 14.847) [10 02 810D 644734] '\x10\x02\x81\rdG4' # accepts mask 0xF1 # setting 0x10 turns off the movement events (but buttons still work) << ( 221.495) [10 02 81D0 000000] '\x10\x02\x81\xd0\x00\x00\x00' >> ( 221.509) [10 02 81D0 000000] '\x10\x02\x81\xd0\x00\x00\x00' # appears to be read-only? << ( 223.527) [10 02 81D2 000000] '\x10\x02\x81\xd2\x00\x00\x00' >> ( 223.540) [10 02 81D2 000003] '\x10\x02\x81\xd2\x00\x00\x03' # appears to be read-only? << ( 225.557) [10 02 81D4 000000] '\x10\x02\x81\xd4\x00\x00\x00' >> ( 225.571) [10 02 81D4 000004] '\x10\x02\x81\xd4\x00\x00\x04' # read-only, 01-04 firmware info << ( 259.270) [10 02 81F1 000000] '\x10\x02\x81\xf1\x00\x00\x00' >> ( 259.283) [10 02 8F81 F10300] '\x10\x02\x8f\x81\xf1\x03\x00' # writing 01 here will trigger an avalanche of events, most likely # raw input from the mouse; disable by writing 00 << ( 261.300) [10 02 81F3 000000] '\x10\x02\x81\xf3\x00\x00\x00' >> ( 261.315) [10 02 81F3 000000] '\x10\x02\x81\xf3\x00\x00\x00' Solaar-1.1.14/docs/devices/Marathon Mouse M705 406D.txt000066400000000000000000000222401473525213700217740ustar00rootroot00000000000000 3: Marathon Mouse M705 (M-R0073) Device path : /dev/hidraw3 WPID : 406D Codename : M705 (M-R0073) Kind : mouse Protocol : HID++ 4.5 Polling rate : 8 ms (125Hz) Serial number: B14D65DA Model ID: 406D00000000 Unit ID: 5E1A15B8 Bootloader: BOT 59.00.B0002 Firmware: RQM 67.01.B0005 The power switch is located on the base. Supports 26 HID++ 2.0 features: 0: ROOT {0000} 1: FEATURE SET {0001} 2: DEVICE FW VERSION {0003} Firmware: Bootloader BOT 59.00.B0002 406D5F5048B901 Firmware: Firmware RQM 67.01.B0005 406D5F5048B901 Unit ID: 5E1A15B8 Model ID: 406D00000000 Transport IDs: {'wpid': '406D'} 3: DEVICE NAME {0005} Name: Marathon Mouse/Performance Plus M705 Kind: mouse 4: WIRELESS DEVICE STATUS {1D4B} 5: RESET {0020} 6: BATTERY STATUS {1000} Battery: 50%, discharging, next level 20%. 7: REPROG CONTROLS V4 {1B04} Key/Button Actions (saved): {Left Button:Left Click, Right Button:Right Click, Middle Button:Mouse Middle Button, Back Button:Mouse Back Button, Forward Button:Mouse Forward Button, Left Tilt:Mouse Scroll Left Button , Right Tilt:Mouse Scroll Right Button} Key/Button Actions : {Left Button:Left Click, Right Button:Right Click, Middle Button:Mouse Middle Button, Back Button:Mouse Back Button, Forward Button:Mouse Forward Button, Left Tilt:Mouse Scroll Left Button , Right Tilt:Mouse Scroll Right Button} Key/Button Diversion (saved): {Middle Button:Regular, Back Button:Regular, Forward Button:Regular, Left Tilt:Regular, Right Tilt:Regular} Key/Button Diversion : {Middle Button:Regular, Back Button:Regular, Forward Button:Regular, Left Tilt:Regular, Right Tilt:Regular} 8: POINTER SPEED {2205} Pointer Speed: 1.0 Sensitivity (Pointer Speed) (saved): 256 Sensitivity (Pointer Speed) : 256 9: VERTICAL SCROLLING {2100} Roller type: 3G Ratchet per turn: 24 Scroll lines: 0 10: DFUCONTROL SIGNED {00C2} 11: DEVICE RESET {1802} internal, hidden 12: unknown:1803 {1803} internal, hidden 13: CONFIG DEVICE PROPS {1806} internal, hidden 14: unknown:1810 {1810} internal, hidden 15: unknown:1830 {1830} internal, hidden 16: unknown:1890 {1890} internal, hidden 17: unknown:18A1 {18A1} internal, hidden 18: unknown:1DF3 {1DF3} internal, hidden 19: unknown:1E00 {1E00} hidden 20: unknown:1EB0 {1EB0} internal, hidden 21: unknown:1861 {1861} internal, hidden 22: unknown:18B1 {18B1} internal, hidden 23: unknown:1850 {1850} internal, hidden 24: unknown:1F03 {1F03} internal, hidden 25: unknown:18C0 {18C0} internal, hidden Has 7 reprogrammable keys: 0: Left Button , default: Left Click => Left Click mse, reprogrammable, pos:0, group:1, group mask:g1 reporting: default 1: Right Button , default: Right Click => Right Click mse, reprogrammable, pos:0, group:1, group mask:g1 reporting: default 2: Middle Button , default: Mouse Middle Button => Mouse Middle Button mse, reprogrammable, divertable, pos:0, group:2, group mask:g1,g2 reporting: default 3: Back Button , default: Mouse Back Button => Mouse Back Button mse, reprogrammable, divertable, pos:0, group:2, group mask:g1,g2 reporting: default 4: Forward Button , default: Mouse Forward Button => Mouse Forward Button mse, reprogrammable, divertable, pos:0, group:2, group mask:g1,g2 reporting: default 5: Left Tilt , default: Mouse Scroll Left Button => Mouse Scroll Left Button mse, reprogrammable, divertable, pos:0, group:2, group mask:g1,g2 reporting: default 6: Right Tilt , default: Mouse Scroll Right Button => Mouse Scroll Right Button mse, reprogrammable, divertable, pos:0, group:2, group mask:g1,g2 reporting: default Battery: 50%, discharging, next level 20%. 3: Marathon Mouse M705 (M-R0073) Device path : /dev/hidraw3 WPID : 406D Codename : M705 (M-R0073) Kind : mouse Protocol : HID++ 4.5 Polling rate : 8 ms (125Hz) Serial number: B14D65DA Model ID: 406D00000000 Unit ID: 5E1A15B8 Bootloader: BOT 59.00.B0002 Firmware: RQM 67.01.B0005 The power switch is located on the base. Supports 27 HID++ 2.0 features: 0: ROOT {0000} 1: FEATURE SET {0001} 2: DEVICE FW VERSION {0003} Firmware: Bootloader BOT 59.00.B0002 406D5F5048B901 Firmware: Firmware RQM 67.01.B0005 406D5F5048B901 Unit ID: 5E1A15B8 Model ID: 406D00000000 Transport IDs: {'wpid': '406D'} 3: DEVICE NAME {0005} Name: Marathon Mouse/Performance Plus M705 Kind: mouse 4: WIRELESS DEVICE STATUS {1D4B} 5: RESET {0020} 6: BATTERY STATUS {1000} Battery: 50%, discharging, next level 20%. 7: REPROG CONTROLS V4 {1B04} Key/Button Actions : {Left Button:Left Click, Right Button:Right Click, Middle Button:Mouse Middle Button, Back Button:Mouse Back Button, Forward Button:Mouse Forward Button, Left Tilt:Mouse Scroll Left Button , Right Tilt:Mouse Scroll Right Button} Key/Button Diversion : {Middle Button:Regular, Back Button:Regular, Forward Button:Regular, Left Tilt:Regular, Right Tilt:Regular} 8: POINTER SPEED {2205} Pointer Speed: 1.0 Sensitivity (Pointer Speed) : 256 9: VERTICAL SCROLLING {2100} Roller type: 3G Ratchet per turn: 24 Scroll lines: 0 10: DFUCONTROL SIGNED {00C2} 11: DEVICE RESET {1802} internal, hidden 12: unknown:1803 {1803} internal, hidden 13: CONFIG DEVICE PROPS {1806} internal, hidden 14: unknown:1810 {1810} internal, hidden 15: unknown:1830 {1830} internal, hidden 16: unknown:1890 {1890} internal, hidden 17: unknown:18A1 {18A1} internal, hidden 18: unknown:1DF3 {1DF3} internal, hidden 19: unknown:1E00 {1E00} hidden 20: unknown:1EB0 {1EB0} internal, hidden 21: unknown:1861 {1861} internal, hidden 22: unknown:18B1 {18B1} internal, hidden 23: unknown:1850 {1850} internal, hidden 24: unknown:1F03 {1F03} internal, hidden 25: unknown:18C0 {18C0} internal, hidden 26: HIRES WHEEL {2121} Multiplier: 8 Has invert: Normal wheel motion Has ratchet switch: Normal wheel mode Low resolution mode HID notification Scroll Wheel Direction : False Scroll Wheel Resolution : False Has 7 reprogrammable keys: 0: Left Button , default: Left Click => Left Click mse, reprogrammable, pos:0, group:1, group mask:g1 reporting: default 1: Right Button , default: Right Click => Right Click mse, reprogrammable, pos:0, group:1, group mask:g1 reporting: default 2: Middle Button , default: Mouse Middle Button => Mouse Middle Button mse, reprogrammable, divertable, pos:0, group:2, group mask:g1,g2 reporting: default 3: Back Button , default: Mouse Back Button => Mouse Back Button mse, reprogrammable, divertable, pos:0, group:2, group mask:g1,g2 reporting: default 4: Forward Button , default: Mouse Forward Button => Mouse Forward Button mse, reprogrammable, divertable, pos:0, group:2, group mask:g1,g2 reporting: default 5: Left Tilt , default: Mouse Scroll Left Button => Mouse Scroll Left Button mse, reprogrammable, divertable, pos:0, group:2, group mask:g1,g2 reporting: default 6: Right Tilt , default: Mouse Scroll Right Button => Mouse Scroll Right Button mse, reprogrammable, divertable, pos:0, group:2, group mask:g1,g2 reporting: default Battery: 50%, discharging, next level 20%. Solaar-1.1.14/docs/devices/Multi Device Silent Mouse M585-M590 406B.txt000066400000000000000000000154271473525213700243610ustar00rootroot00000000000000Solaar version 1.1.7 2: Multi Device Silent Mouse M585/M590 Device path : /dev/hidraw2 WPID : 406B Codename : M585/M590 Kind : mouse Protocol : HID++ 4.5 Polling rate : 8 ms (125Hz) Serial number: 37216581 Model ID: B01B406B0000 Unit ID: F2D653C0 Bootloader: BOT 48.00.B0001 Firmware: MPM 05.02.B0007 Other: The power switch is located on the base. Supports 34 HID++ 2.0 features: 0: ROOT {0000} V0 1: FEATURE SET {0001} V0 2: DEVICE FW VERSION {0003} V2 Firmware: Bootloader BOT 48.00.B0001 00005BEB5EE001 Firmware: Firmware MPM 05.02.B0007 406B5BEB5EE001 Firmware: Other Unit ID: F2D653C0 Model ID: B01B406B0000 Transport IDs: {'btleid': 'B01B', 'wpid': '406B'} 3: DEVICE NAME {0005} V0 Name: M585/M590 Multi-Device Mouse Kind: mouse 4: WIRELESS DEVICE STATUS {1D4B} V0 5: RESET {0020} V0 6: CRYPTO ID {0021} V0 7: DEVICE FRIENDLY NAME {0007} V0 Friendly Name: M585/M590 8: BATTERY STATUS {1000} V0 Battery: 50%, discharging, next level 20%. 9: CHANGE HOST {1814} V1 Cambiar Host : 1:romanescu 10: HOSTS INFO {1815} V1 Host 0 (paired): romanescu Host 1 (paired): espinaca 11: REPROG CONTROLS V4 {1B04} V3 Acciones de Tecla/Botón (saved): {Left Button:Left Click, Right Button:Right Click, Middle Button:Mouse Middle Button, Back Button:Mouse Back Button, Forward Button:Mouse Forward Button, Left Tilt:Mouse Scroll Left Button , Right Tilt:Mouse Scroll Right Button} Acciones de Tecla/Botón : {Left Button:Left Click, Right Button:Right Click, Middle Button:Mouse Middle Button, Back Button:Mouse Back Button, Forward Button:Mouse Forward Button, Left Tilt:Mouse Scroll Left Button , Right Tilt:Mouse Scroll Right Button} Desvio de Tecla/Botón (saved): {Middle Button:Normal, Back Button:Normal, Forward Button:Normal, Left Tilt:Normal, Right Tilt:Normal} Desvio de Tecla/Botón : {Middle Button:Normal, Back Button:Normal, Forward Button:Normal, Left Tilt:Normal, Right Tilt:Normal} 12: PERSISTENT REMAPPABLE ACTION {1C00} V0 Persistent Key/Button Mapping : {Left Button:Mouse Button Left, Right Button:Mouse Button Right, Middle Button:Mouse Button Middle, Back Button:Mouse Button Back, Forward Button:Mouse Button Forward, Left Tilt:Horizontal Scroll Left, Right Tilt:Horizontal Scroll Right} 13: POINTER SPEED {2205} V0 Pointer Speed: 1.0 Sensibilidad (Velocidad del puntero) (saved): 256 Sensibilidad (Velocidad del puntero) : 256 14: VERTICAL SCROLLING {2100} V0 Roller type: standard Ratchet per turn: 18 Scroll lines: 0 15: DFUCONTROL SIGNED {00C2} V0 16: DEVICE RESET {1802} V0 internal, hidden 17: unknown:1803 {1803} V0 internal, hidden 18: CONFIG DEVICE PROPS {1806} V0 internal, hidden 19: OOBSTATE {1805} V0 internal, hidden 20: unknown:1813 {1813} V0 internal, hidden 21: unknown:1830 {1830} V0 internal, hidden 22: unknown:1861 {1861} V0 internal, hidden 23: unknown:1890 {1890} V0 internal, hidden 24: unknown:1891 {1891} V0 internal, hidden 25: unknown:18A1 {18A1} V0 internal, hidden 26: unknown:1DF3 {1DF3} V0 internal, hidden 27: unknown:1E00 {1E00} V0 hidden 28: unknown:1EB0 {1EB0} V0 internal, hidden 29: unknown:18B1 {18B1} V0 internal, hidden 30: unknown:1850 {1850} V0 internal, hidden 31: unknown:1F03 {1F03} V0 internal, hidden 32: unknown:18C0 {18C0} V0 internal, hidden 33: LOWRES WHEEL {2130} V0 Wheel Reports: HID Desvio Rueda Desplazamiento (saved): False Desvio Rueda Desplazamiento : False Has 8 reprogrammable keys: 0: Left Button , default: Left Click => Left Click mse, reprogrammable, pos:0, group:1, group mask:g1 reporting: default 1: Right Button , default: Right Click => Right Click mse, reprogrammable, pos:0, group:1, group mask:g1 reporting: default 2: Middle Button , default: Mouse Middle Button => Mouse Middle Button mse, reprogrammable, divertable, persistently divertable, raw XY, pos:0, group:2, group mask:g1,g2 reporting: default 3: Back Button , default: Mouse Back Button => Mouse Back Button mse, reprogrammable, divertable, persistently divertable, raw XY, pos:0, group:2, group mask:g1,g2 reporting: default 4: Forward Button , default: Mouse Forward Button => Mouse Forward Button mse, reprogrammable, divertable, persistently divertable, raw XY, pos:0, group:2, group mask:g1,g2 reporting: default 5: Left Tilt , default: Mouse Scroll Left Button => Mouse Scroll Left Button mse, reprogrammable, divertable, persistently divertable, raw XY, pos:0, group:2, group mask:g1,g2 reporting: default 6: Right Tilt , default: Mouse Scroll Right Button => Mouse Scroll Right Button mse, reprogrammable, divertable, persistently divertable, raw XY, pos:0, group:2, group mask:g1,g2 reporting: default 7: Virtual Gesture Button , default: Virtual Gesture Button => Virtual Gesture Button divertable, virtual, raw XY, force raw XY, pos:0, group:3, group mask:empty reporting: default Has 7 persistent remappable keys: 0: Left Button => Mouse Button: Mouse Button Left 1: Right Button => Mouse Button: Mouse Button Right 2: Middle Button => Mouse Button: Mouse Button Middle 3: Back Button => Mouse Button: Mouse Button Back 4: Forward Button => Mouse Button: Mouse Button Forward 5: Left Tilt => Horizontal Scroll: Horizontal Scroll Left 6: Right Tilt => Horizontal Scroll: Horizontal Scroll Right Battery: 50%, discharging, next level 20%. Solaar-1.1.14/docs/devices/Nano Receiver C52F.txt000066400000000000000000000003451473525213700212050ustar00rootroot00000000000000Nano Receiver Device path : /dev/hidraw3 USB id : 046d:c52f Serial : 6D0342C5 Firmware : 30.00.B0009 Has 1 paired device(s) out of a maximum of 1. Notifications: (none) Seen with Marathon Mouse M705 Solaar-1.1.14/docs/devices/Nano Receiver C534.txt000066400000000000000000000005571473525213700211710ustar00rootroot00000000000000Solaar version 1.1.4rc2 Nano Receiver Device path : /dev/hidraw1 USB id : 046d:C534 Serial : None Firmware : 29.01.B0016 Has 2 paired device(s) out of a maximum of 2. Has 18 successful pairing(s) remaining. Notifications: wireless, software present (0x000900) Seen paired with Wireless Keyboard 4075 and Wireless Mouse M185 new 4054 Solaar-1.1.14/docs/devices/Nano Receiver C535.txt000066400000000000000000000004451473525213700211660ustar00rootroot00000000000000Solaar version 1.1.5 Nano Receiver Device path : /dev/hidraw5 USB id : 046d:C535 Serial : 1F3F94FC Firmware : 31.00.B0001 Has 1 paired device(s) out of a maximum of 1. Has 39 successful pairing(s) remaining. Notifications: wireless, software present (0x000900) Solaar-1.1.14/docs/devices/Number Pad N545 2006.txt000066400000000000000000000007351473525213700210510ustar00rootroot00000000000000solaar version 1.1.8 3: Number Pad N545 Device path : /dev/hidraw3 WPID : 2006 Codename : N545 Kind : numpad Protocol : HID++ 1.0 Polling rate : 20 ms (50Hz) Serial number: 900A4D98 Firmware: 13.00.B0037 Bootloader: 02.03 Other: 00.01 The power switch is located on the base. Notifications: battery status (0x100000). Features: (none) Battery: full, discharging. Solaar-1.1.14/docs/devices/PRO X 2 40A9.text000066400000000000000000000061441473525213700176650ustar00rootroot00000000000000solaar version 1.1.10 Receiver Device path : /dev/hidraw3 USB id : 046d:C54D Serial : 8FF3BF7B Firmware : 07.00.B0008 Bootloader : 00.08 Other : C1.53 Has 1 paired device(s) out of a maximum of 2. Notifications: (none) Device activity counters: 1=51 1: PRO X 2 Device path : None WPID : 40A9 Codename : PRO X 2 Kind : mouse Protocol : HID++ 4.2 Polling rate : 8 ms (125Hz) Serial number: Model ID: 40A9C09B0000 Unit ID: Bootloader: BL1 71.00.B0012 Firmware: MPM 32.00.B0012 Supports 32 HID++ 2.0 features: 0: ROOT {0000} V0 1: FEATURE SET {0001} V0 2: DEVICE FW VERSION {0003} V4 Firmware: Bootloader BL1 71.00.B0012 AB1CDBC0A7D9 Firmware: Firmware MPM 32.00.B0012 40A9DBC0A7D9 Unit ID: Model ID: 40A9C09B0000 Transport IDs: {'wpid': '40A9', 'usbid': 'C09B'} 3: DEVICE NAME {0005} V2 Name: PRO X 2 Kind: mouse 4: WIRELESS DEVICE STATUS {1D4B} V0 5: CONFIG CHANGE {0020} V0 6: UNIFIED BATTERY {1004} V3 Battery: 96%, discharging. 7: XY STATS {2250} V1 8: WHEEL STATS {2251} V0 9: unknown:2202 {2202} V0 EXTENDED_ADJUSTABLE_DPI 10: MODE STATUS {8090} V2 11: unknown:8061 {8061} V0 EXTENDED_ADJUSTABLE_REPORT_RATE 12: ONBOARD PROFILES {8100} V0 Device Mode: On-Board Onboard Profiles (saved): Enable Onboard Profiles : Enable 13: MOUSE BUTTON SPY {8110} V0 14: unknown:1500 {1500} V0 FORCE_PAIRING 15: unknown:1801 {1801} V0 internal, hidden, unknown:000010 16: DEVICE RESET {1802} V0 internal, hidden, unknown:000010 17: unknown:1803 {1803} V0 internal, hidden, unknown:000010 18: CONFIG DEVICE PROPS {1806} V8 internal, hidden, unknown:000010 19: unknown:1817 {1817} V0 internal, hidden, unknown:000010 20: OOBSTATE {1805} V0 internal, hidden 21: unknown:1830 {1830} V0 internal, hidden, unknown:000010 22: unknown:1875 {1875} V0 internal, hidden, unknown:000010 23: unknown:1861 {1861} V1 internal, hidden, unknown:000010 24: unknown:1890 {1890} V9 internal, hidden, unknown:000008 25: unknown:18A1 {18A1} V0 internal, hidden, unknown:000010 26: unknown:1E00 {1E00} V0 hidden 27: unknown:1E02 {1E02} V0 internal, hidden 28: unknown:1E22 {1E22} V1 internal, hidden, unknown:000010 29: unknown:1602 {1602} V0 30: unknown:1EB0 {1EB0} V0 internal, hidden, unknown:000010 31: unknown:18B1 {18B1} V0 internal, hidden, unknown:000010 Battery: 96%, discharging. Solaar-1.1.14/docs/devices/PRO X Wireless 4093.txt000066400000000000000000000050051473525213700211310ustar00rootroot00000000000000Solaar version 1.1.3 1: PRO X Wireless Device path : None WPID : 4093 Codename : PRO X Kind : mouse Protocol : HID++ 4.2 Polling rate : 8 ms (125Hz) Serial number: 42F42E12 Model ID: 4093C0940000 Unit ID: 42F42E12 Bootloader: BL1 25.00.B0013 Other: Firmware: MPM 25.01.B0018 Supports 28 HID++ 2.0 features: 0: ROOT {0000} 1: FEATURE SET {0001} 2: DEVICE FW VERSION {0003} Firmware: Bootloader BL1 25.00.B0013 AB00BE657A82 Firmware: Other Firmware: Firmware MPM 25.01.B0018 4093FE92436C Unit ID: 42F42E12 Model ID: 4093C0940000 Transport IDs: {'wpid': '4093', 'usbid': 'C094'} 3: DEVICE NAME {0005} Name: PRO X Wireless Kind: mouse 4: WIRELESS DEVICE STATUS {1D4B} 5: RESET {0020} 6: UNIFIED BATTERY {1004} 7: COLOR LED EFFECTS {8070} internal, hidden 8: ONBOARD PROFILES {8100} Device Mode: Host Onboard Profiles (saved): Disable Onboard Profiles : Disable 9: MOUSE BUTTON SPY {8110} 10: REPORT RATE {8060} Polling Rate (ms): 1 Polling Rate (ms) (saved): 1 Polling Rate (ms) : 1 11: ADJUSTABLE DPI {2201} Sensitivity (DPI) (saved): 1000 Sensitivity (DPI) : 1000 12: unknown:1500 {1500} 13: DEVICE RESET {1802} internal, hidden 14: unknown:1803 {1803} internal, hidden 15: CONFIG DEVICE PROPS {1806} internal, hidden 16: unknown:1811 {1811} internal, hidden 17: OOBSTATE {1805} internal, hidden 18: unknown:1830 {1830} internal, hidden 19: unknown:1890 {1890} internal, hidden 20: unknown:1891 {1891} internal, hidden 21: unknown:18A1 {18A1} internal, hidden 22: unknown:1801 {1801} internal, hidden 23: unknown:18B1 {18B1} internal, hidden 24: unknown:1E00 {1E00} hidden 25: unknown:1EB0 {1EB0} internal, hidden 26: unknown:1863 {1863} internal, hidden 27: unknown:1E22 {1E22} internal, hidden Battery: 76%, discharging. Solaar-1.1.14/docs/devices/Rechargeable Trackpad T651 B00C.txt000066400000000000000000000021721473525213700232500ustar00rootroot00000000000000Solaar version 1.1.7 1: Rechargeable Trackpad T651 Device path : /dev/hidraw9 USB id : 046d:B00C Codename : Rechargeable Kind : touchpad Protocol : HID++ 2.0 Serial number: Model ID: 000000000000 Unit ID: 00000000 Firmware: RBM 05.00.B0026 Other: Supports 9 HID++ 2.0 features: 0: ROOT {0000} V0 1: FEATURE SET {0001} V0 2: FEATURE INFO {0002} V0 3: DEVICE FW VERSION {0003} V0 Firmware: Firmware RBM 05.00.B0026 B00C Firmware: Other Unit ID: 00000000 Model ID: 000000000000 Transport IDs: {} 4: DEVICE NAME {0005} V0 Name: Rechargeable Trackpad T651 Kind: touchpad 5: unknown:1E00 {1E00} V0 hidden 6: GENERIC TEST {1800} V0 internal, hidden 7: BATTERY STATUS {1000} V0 Battery: 90%, discharging, next level 70%. 8: TOUCHPAD RAW XY {6100} V0 Battery: 90%, discharging, next level 70%. Solaar-1.1.14/docs/devices/Signature M650 L Mouse B02A.txt000066400000000000000000000113321473525213700223460ustar00rootroot00000000000000Solaar version 1.1.3 2: Signature M650 L Mouse Device path : None WPID : B02A Codename : Logi M650 L Kind : mouse Protocol : HID++ 4.5 Serial number: 46756ABD Model ID: B02A00000000 Unit ID: 46756ABD Bootloader: BL1 38.01.B0011 Firmware: RBM 16.02.B0013 Other: The power switch is located on the (unknown). Supports 29 HID++ 2.0 features: 0: ROOT {0000} 1: FEATURE SET {0001} 2: DEVICE FW VERSION {0003} Firmware: Bootloader BL1 38.01.B0011 B02ADFC9033D Firmware: Firmware RBM 16.02.B0013 B02AB0706FCD Firmware: Other Unit ID: 46756ABD Model ID: B02A00000000 Transport IDs: {'btleid': 'B02A'} 3: DEVICE NAME {0005} Name: Signature M650 L Mouse Kind: mouse 4: WIRELESS DEVICE STATUS {1D4B} 5: RESET {0020} 6: DEVICE FRIENDLY NAME {0007} Friendly Name: Logi M650 L 7: UNIFIED BATTERY {1004} 8: REPROG CONTROLS V4 {1B04} DPI Sliding Adjustment (saved): Off DPI Sliding Adjustment : Off Mouse Gestures (saved): Off Mouse Gestures : Off Key/Button Actions (saved): {Middle Button:Mouse Middle Button, Back Button:Mouse Back Button, Forward Button:Mouse Forward Button} Key/Button Actions : {Middle Button:Mouse Middle Button, Back Button:Mouse Back Button, Forward Button:Mouse Forward Button} Key/Button Diversion (saved): {Middle Button:Regular, Back Button:Regular, Forward Button:Regular} Key/Button Diversion : {Middle Button:Regular, Back Button:Regular, Forward Button:Regular} 9: HOSTS INFO {1815} Host 0 (paired): legion15 10: XY STATS 11: LOWRES WHEEL {2130} Wheel Reports: HID Scroll Wheel Diversion (saved): False Scroll Wheel Diversion : False 12: ADJUSTABLE DPI {2201} Sensitivity (DPI) (saved): 1200 Sensitivity (DPI) : 1200 13: DFUCONTROL {00C3} 14: DEVICE RESET {1802} internal, hidden, unknown:000010 15: unknown:1803 {1803} internal, hidden, unknown:000010 16: CONFIG DEVICE PROPS {1806} internal, hidden, unknown:000010 17: unknown:1816 {1816} internal, hidden, unknown:000010 18: OOBSTATE {1805} internal, hidden 19: unknown:1830 {1830} internal, hidden, unknown:000010 20: unknown:1891 {1891} internal, hidden, unknown:000008 21: unknown:18A1 {18A1} internal, hidden, unknown:000010 22: unknown:1E00 {1E00} hidden 23: unknown:1E02 {1E02} internal, hidden 24: unknown:1E22 {1E22} internal, hidden, unknown:000010 25: unknown:1602 {1602} 26: unknown:1EB0 {1EB0} internal, hidden, unknown:000010 27: unknown:1861 {1861} internal, hidden, unknown:000010 28: unknown:18B1 {18B1} internal, hidden, unknown:000010 Has 6 reprogrammable keys: 0: Left Button , default: Left Click => Left Click mse, analytics key events, pos:0, group:1, group mask:empty reporting: default 1: Right Button , default: Right Click => Right Click mse, analytics key events, pos:0, group:1, group mask:empty reporting: default 2: Middle Button , default: Mouse Middle Button => Mouse Middle Button mse, reprogrammable, divertable, raw XY, analytics key events, pos:0, group:3, group mask:g1,g2,g3 reporting: default 3: Back Button , default: Mouse Back Button => Mouse Back Button mse, reprogrammable, divertable, raw XY, analytics key events, unknown:000800, pos:0, group:2, group mask:g1,g2 reporting: default 4: Forward Button , default: Mouse Forward Button => Mouse Forward Button mse, reprogrammable, divertable, raw XY, analytics key events, unknown:000800, pos:0, group:2, group mask:g1,g2 reporting: default 5: Virtual Gesture Button , default: Virtual Gesture Button => Virtual Gesture Button divertable, virtual, raw XY, force raw XY, pos:0, group:4, group mask:empty reporting: default Battery: 95%, discharging. Solaar-1.1.14/docs/devices/Unifying Receiver C52B.txt000066400000000000000000000005261473525213700220770ustar00rootroot00000000000000Solaar version 1.1.4 Unifying Receiver Device path : /dev/hidraw0 USB id : 046d:C52B Serial : E0662192 Firmware : 24.11.B0036 Bootloader : 02.09 Other : AA.AC Has 2 paired device(s) out of a maximum of 6. Notifications: wireless, software present (0x000900) Device activity counters: 1=225, 2=132 Solaar-1.1.14/docs/devices/Wireless All-in-One Keyboard TK820 4102.txt000066400000000000000000000075321473525213700244040ustar00rootroot00000000000000Solaar version 1.1.1 1: Wireless All-in-One Keyboard TK820 Device path : /dev/hidraw5 WPID : 4102 Codename : TK820 Kind : keyboard Protocol : HID++ 2.0 Polling rate : 8 ms (125Hz) Serial number: 7D7699C9 Model ID: 000000000000 Unit ID: 00000000 Firmware: RQK 52.00.B0025 Bootloader: BL 02.00.B0001 Hardware: 72 Other: The power switch is located on the top right corner. Supports 28 HID++ 2.0 features: 0: ROOT {0000} 1: FEATURE SET {0001} 2: FEATURE INFO {0002} 3: DEVICE FW VERSION {0003} Firmware: Firmware RQK 52.00.B0025 4102 Firmware: Bootloader BL 02.00.B0001 4102 Firmware: Hardware 72 Firmware: Other Unit ID: 00000000 Model ID: 000000000000 Transport IDs: {} 4: DEVICE NAME {0005} Name: Wireless All-in-One Keyboard TK820 Kind: keyboard 5: BATTERY STATUS {1000} Battery: 20%, discharging, next level 5%. 6: WIRELESS DEVICE STATUS {1D4B} 7: unknown:1DF3 {1DF3} internal, hidden 8: REPROG CONTROLS {1B00} 9: unknown:1F03 {1F03} internal, hidden 10: FN INVERSION {40A0} Swap Fx function (saved): True Swap Fx function : True 11: NEW FN INVERSION {40A2} Fn-swap: enabled Fn-swap default: enabled 12: MOUSE POINTER {2200} DPI: 1000 Acceleration: low Override OS ballistics No vertical tuning, standard mice 13: DFUCONTROL LEGACY {00C0} 14: unknown:1E80 {1E80} internal, hidden 15: TOUCHPAD RAW XY {6100} 16: unknown:1860 {1860} internal, hidden 17: unknown:1E00 {1E00} hidden 18: REPROG CONTROLS V3 {1B03} 19: unknown:1890 {1890} internal, hidden 20: unknown:18E6 {18E6} internal, hidden 21: unknown:18A0 {18A0} internal, hidden 22: unknown:1830 {1830} internal, hidden 23: unknown:1F04 {1F04} internal, hidden 24: unknown:1EA1 {1EA1} internal, hidden 25: unknown:18B0 {18B0} internal, hidden 26: ENCRYPTION {4100} 27: KEYBOARD LAYOUT {4520} Has 15 reprogrammable keys: 0: Fn Left Click => ShowUI divertable 1: Metro Appswitch => MetroAppSwitch is FN, reprogrammable 2: Metro Search => MetroSearch is FN, reprogrammable 3: unknown:00B2 => MetroShare2 is FN, reprogrammable 4: unknown:00B3 => MetroDevices2 is FN, reprogrammable 5: unknown:00B4 => MetroSettings2 is FN, reprogrammable 6: Previous => Previous is FN 7: Play/Pause => Play/Pause is FN 8: Next => Next is FN 9: Mute => Mute is FN 10: Volume Down => Volume Down is FN 11: Volume Up => Volume Up is FN 12: Sleep => Sleep is FN, reprogrammable 13: Left Button => Left Click mse 14: Right Button => Right Click mse Battery: 20%, discharging, next level 5 Solaar-1.1.14/docs/devices/Wireless Illuminated Keyboard K800 2010.txt000066400000000000000000000056311473525213700246640ustar00rootroot00000000000000 2: Wireless Illuminated Keyboard K800 Codename : K800 Kind : keyboard Wireless PID : 2010 Protocol : HID++ 1.0 Polling rate : 20 ms (50Hz) Serial number: FFC65665 Firmware: 22.01.B0019 Bootloader: 02.01 Other: 00.07 The power switch is located on the top right corner. Notifications: battery status (0x100000). Battery: full, discharging. # 0x00 - Enabled Notifications. rw (see HID++ 1.0 spec) << ( 0.055) [10 02 8100 000000] '\x10\x02\x81\x00\x00\x00\x00' >> ( 0.084) [10 02 8100 000000] '\x10\x02\x81\x00\x00\x00\x00' # 0x01 - Keyboard hand detection. rw, last param is 00 when hand detection is # enabled, 30 when disabled. (when enabled, keyboard will light up if not # already when hovering over the front) << ( 1.085) [10 02 8101 000000] '\x10\x02\x81\x01\x00\x00\x00' >> ( 1.114) [10 02 8101 000000] '\x10\x02\x81\x01\x00\x00\x00' # 0x07 - Battery status (3 = one bar; 1 = red/critical; 5=two bars; 7=three # bars/full. Second returned param is 25 when keyboard is charging ) << ( 7.327) [10 02 8107 000000] '\x10\x02\x81\x07\x00\x00\x00' >> ( 7.368) [10 02 8107 030000] '\x10\x02\x81\x07\x03\x00\x00' # 0x09 - F key function. rw (read: status, set/get: 00 01 00 means swap # functions, 00 00 00 means do not swap functions) << ( 9.411) [10 02 8109 000000] '\x10\x02\x81\t\x00\x00\x00' >> ( 9.440) [10 02 8109 000000] '\x10\x02\x81\t\x00\x00\x00' # 0x17 - Illumination info r/w. Last param: 02 to disable backlight, 01 to # enable backlight << ( 24.965) [10 02 8117 000000] '\x10\x02\x81\x17\x00\x00\x00' >> ( 24.988) [10 02 8117 3C0001] '\x10\x02\x81\x17<\x00\x01' # 0x51 - ? << ( 99.294) [10 02 8151 000000] '\x10\x02\x81Q\x00\x00\x00' >> ( 99.543) [10 02 8151 000000] '\x10\x02\x81Q\x00\x00\x00' # 0x54 - ? << ( 103.046) [10 02 8154 000000] '\x10\x02\x81T\x00\x00\x00' >> ( 103.295) [10 02 8154 FF0000] '\x10\x02\x81T\xff\x00\x00' # 0xD0 - ? << ( 253.860) [10 02 81D0 000000] '\x10\x02\x81\xd0\x00\x00\x00' >> ( 253.883) [10 02 81D0 000000] '\x10\x02\x81\xd0\x00\x00\x00' # 0xF1 - Version info (params 0n 00 00 where n is 1..4) << ( 289.991) [10 02 81F1 000000] '\x10\x02\x81\xf1\x00\x00\x00' >> ( 290.032) [10 02 8F81 F10300] '\x10\x02\x8f\x81\xf1\x03\x00' # 0xF3 - ? << ( 292.075) [10 02 81F3 000000] '\x10\x02\x81\xf3\x00\x00\x00' >> ( 292.116) [10 02 81F3 000000] '\x10\x02\x81\xf3\x00\x00\x00' # 0x0F - This changes, the last commented line was observed in an earlier run << ( 17.728) [10 02 830F 000000] '\x10\x02\x83\x0f\x00\x00\x00' >> ( 17.976) [11 02 830F FFFB00000240025C000000000FF90080] '\x11\x02\x83\x0f\xff\xfb\x00\x00\x02@\x02\\\x00\x00\x00\x00\x0f\xf9\x00\x80' #>> ( 17.999) [11 02 830F FFFC007F0243025D000000000FF60080] '\x11\x02\x83\x0f\xff\xfc\x00\x7f\x02C\x02]\x00\x00\x00\x00\x0f\xf6\x00\x80' # See also https://git.lekensteyn.nl/ltunify/tree/registers.txt for a verbose # meaning of registers and params. Solaar-1.1.14/docs/devices/Wireless Illuminated Keyboard K800 new 406E.txt000066400000000000000000000100471473525213700254670ustar00rootroot00000000000000 2: Wireless Illuminated Keyboard K800 new Codename : new Kind : keyboard Wireless PID : 406E Protocol : HID++ 4.5 Polling rate : 20 ms (50Hz) Serial number: 636E1413 Bootloader: BOT 60.00.B0002 Firmware: RQK 68.01.B0009 The power switch is located on the top right corner. Supports 25 HID++ 2.0 features: 0: ROOT {0000} 1: FEATURE SET {0001} 2: DEVICE FW VERSION {0003} 3: DEVICE NAME {0005} 4: WIRELESS DEVICE STATUS {1D4B} 5: RESET {0020} 6: BATTERY STATUS {1000} 7: unknown:1983 {1983} 8: REPROG CONTROLS V4 {1B04} 9: FN INVERSION {40A0} 10: ENCRYPTION {4100} 11: KEYBOARD DISABLE {4521} 12: unknown:00C2 {00C2} 13: unknown:1803 {1803} internal, hidden 14: unknown:1806 {1806} internal, hidden 15: unknown:1811 {1811} internal, hidden 17: unknown:1890 {1890} internal, hidden 18: unknown:18A1 {18A1} internal, hidden 19: unknown:1DF3 {1DF3} internal, hidden 20: unknown:1E00 {1E00} hidden 21: unknown:1EB0 {1EB0} internal, hidden 22: unknown:1861 {1861} internal, hidden 23: unknown:1A20 {1A20} internal, hidden 24: unknown:18B0 {18B0} internal, hidden Has 15 reprogrammable keys: 0: MY HOME , default: HomePage => MY HOME is FN, FN sensitive, reprogrammable, divertable, pos:1, group:0, gmask:0 1: Mail , default: Email => Mail is FN, FN sensitive, reprogrammable, divertable, pos:2, group:0, gmask:0 2: SEARCH , default: SearchForFiles => SEARCH is FN, FN sensitive, reprogrammable, divertable, pos:3, group:0, gmask:0 3: Application Switcher , default: Application Switcher => Application Switcher is FN, FN sensitive, reprogrammable, divertable, pos:4, group:0, gmask:0 4: unknown:00E2 , default: unknown:00C1 => unknown:00E2 is FN, FN sensitive, divertable, pos:5, group:0, gmask:0 5: unknown:00E3 , default: unknown:00C2 => unknown:00E3 is FN, FN sensitive, divertable, pos:6, group:0, gmask:0 6: SLEEP , default: Sleep => SLEEP is FN, FN sensitive, reprogrammable, divertable, pos:8, group:0, gmask:0 7: MEDIA PLAYER , default: Music => MEDIA PLAYER is FN, FN sensitive, reprogrammable, divertable, pos:9, group:0, gmask:0 8: Previous , default: Previous => Previous is FN, FN sensitive, divertable, pos:10, group:0, gmask:0 9: Play/Pause , default: Play/Pause => Play/Pause is FN, FN sensitive, divertable, pos:11, group:0, gmask:0 10: Next , default: Next => Next is FN, FN sensitive, divertable, pos:12, group:0, gmask:0 11: Volume Up , default: Volume Up => Volume Up nonstandard, divertable, pos:0, group:0, gmask:0 12: Volume Down , default: Volume Down => Volume Down nonstandard, divertable, pos:0, group:0, gmask:0 13: Mute , default: Mute => Mute nonstandard, divertable, pos:0, group:0, gmask:0 14: Calculator , default: Calculator => Calculator nonstandard, reprogrammable, divertable, pos:0, group:0, gmask:0 Battery: 50%, discharging. Solaar-1.1.14/docs/devices/Wireless Keyboard 4075.txt000066400000000000000000000060221473525213700220410ustar00rootroot00000000000000Solaar version 1.1.4rc2 1: Wireless Keyboard Device path : /dev/hidraw2 WPID : 4075 Codename : Kind : keyboard Protocol : HID++ 4.5 Polling rate : 20 ms (50Hz) Serial number: 00000000 Model ID: 000000000000 Unit ID: 00000000 Firmware: RQK 71.00.B0002 Supports 19 HID++ 2.0 features: 0: ROOT {0000} 1: FEATURE SET {0001} 2: DEVICE FW VERSION {0003} Firmware: Firmware RQK 71.00.B0002 4075 Unit ID: 00000000 Model ID: 000000000000 Transport IDs: {} 3: DEVICE NAME {0005} Name: Wireless Keyboard Kind: keyboard 4: RESET {0020} 5: BATTERY STATUS {1000} Battery: 30%, discharging, next level 5%. 6: REPROG CONTROLS V4 {1B04} Key/Button Diversion (saved): {Calculator:Regular, Mail:Regular, My Home:Regular, Search:Regular} Key/Button Diversion : {Calculator:Regular, Mail:Regular, My Home:Regular, Search:Regular} 7: WIRELESS DEVICE STATUS {1D4B} 8: NEW FN INVERSION {40A2} Fn-swap: disabled Fn-swap default: disabled Swap Fx function (saved): False Swap Fx function : False 9: ENCRYPTION {4100} 10: LOCK KEY STATE {4220} 11: KEYBOARD DISABLE KEYS {4521} Disable keys (saved): {Caps Lock:False, Num Lock:False, Scroll Lock:False, Insert:False, Win:False} Disable keys : {Caps Lock:False, Num Lock:False, Scroll Lock:False, Insert:False, Win:False} 12: unknown:1810 {1810} internal, hidden 13: unknown:1830 {1830} internal, hidden 14: unknown:1890 {1890} internal, hidden 15: unknown:18A0 {18A0} internal, hidden 16: unknown:18B0 {18B0} internal, hidden 17: unknown:1DF3 {1DF3} internal, hidden 18: unknown:1E00 {1E00} hidden Has 4 reprogrammable keys: 0: My Home , default: HomePage => HomePage is FN, FN sensitive, reprogrammable, divertable, pos:1, group:0, group mask:empty reporting: default 1: Mail , default: Email => Email is FN, FN sensitive, reprogrammable, divertable, pos:2, group:0, group mask:empty reporting: default 2: Search , default: Search Files => Search Files is FN, FN sensitive, reprogrammable, divertable, pos:3, group:0, group mask:empty reporting: default 3: Calculator , default: Calculator => Calculator is FN, FN sensitive, reprogrammable, divertable, pos:4, group:0, group mask:empty reporting: default Battery: 30%, discharging, next level 5%. Solaar-1.1.14/docs/devices/Wireless Keyboard Dell KB714 4015.txt000066400000000000000000000033561473525213700234140ustar00rootroot00000000000000Solaar version 1.1.6rc4 4: Wireless Keyboard Dell KB714 Device path : /dev/hidraw6 WPID : 4015 Codename : Dell KB714 Kind : keyboard Protocol : HID++ 3.0 Polling rate : 20 ms (50Hz) Serial number: C0A37A58 Model ID: 000000000000 Unit ID: 00000000 Firmware: RQK 51.00.B0005 Supports 19 HID++ 2.0 features: 0: ROOT {0000} V0 1: FEATURE SET {0001} V0 2: DEVICE FW VERSION {0003} V0 Firmware: Firmware RQK 51.00.B0005 4015 Unit ID: 00000000 Model ID: 000000000000 Transport IDs: {} 3: DEVICE NAME {0005} V0 Name: Wireless Keyboard Dell KB714 Kind: keyboard 4: BATTERY STATUS {1000} V0 Battery: 90%, discharging, next level 5%. 5: WIRELESS DEVICE STATUS {1D4B} V0 6: ENCRYPTION {4100} V0 7: KEYBOARD LAYOUT {4520} V0 8: unknown:1810 {1810} V0 internal, hidden 9: unknown:1830 {1830} V0 internal, hidden 10: unknown:1860 {1860} V0 internal, hidden 11: unknown:1890 {1890} V0 internal, hidden 12: unknown:18A0 {18A0} V0 internal, hidden 13: unknown:18B0 {18B0} V0 internal, hidden 14: REMAINING PAIRING {1DF0} V0 hidden Remaining Pairings: 54 15: unknown:1DF3 {1DF3} V0 internal, hidden 16: unknown:1E00 {1E00} V0 hidden 17: unknown:1E90 {1E90} V0 internal, hidden 18: DEVICE GROUPS {0006} V0 Battery: 90%, discharging, next level 5%. Solaar-1.1.14/docs/devices/Wireless Keyboard K220 4005.txt000066400000000000000000000037601473525213700224370ustar00rootroot00000000000000 2: Wireless Keyboard K220 Codename : K220 Kind : keyboard Wireless PID : 4005 Protocol : HID++ 2.0 Polling rate : 20 ms (50Hz) Serial number: 5BB1D72E Firmware: RQK 37.00.B0011 Supports 14 HID++ 2.0 features: 0: ROOT {0000} 1: FEATURE SET {0001} 2: FEATURE INFO {0002} 3: DEVICE FW VERSION {0003} 4: DEVICE NAME {0005} 5: BATTERY STATUS {1000} 6: unknown:1820 {1820} hidden 7: REPROG CONTROLS {1B00} 8: REPROG CONTROLS V2 {1B01} 9: WIRELESS DEVICE STATUS {1D4B} 10: unknown:1DF0 {1DF0} hidden 11: unknown:1DF3 {1DF3} hidden 12: ENCRYPTION {4100} 13: KEYBOARD LAYOUT {4520} Has 12 reprogrammable keys: 0: FN F1 => Do Nothing One is FN, reprogrammable 1: FN F2 => Do Nothing One is FN, reprogrammable 2: FN F3 => Do Nothing One is FN, reprogrammable 3: FN F4 => Do Nothing One is FN, reprogrammable 4: FN F5 => Do Nothing One is FN, reprogrammable 5: FN F6 => Do Nothing One is FN, reprogrammable 6: FN F7 => Do Nothing One is FN, reprogrammable 7: FN F8 => Do Nothing One is FN, reprogrammable 8: FN F9 => Do Nothing One is FN, reprogrammable 9: Mute => Mute is FN 10: Volume Down => Volume Down is FN 11: Volume Up => Volume Up is FN Battery: 90%, discharging. Solaar-1.1.14/docs/devices/Wireless Keyboard K230 400D.txt000066400000000000000000000046661473525213700224650ustar00rootroot00000000000000 1: Wireless Keyboard K230 Device path : /dev/hidraw5 WPID : 400D Codename : K230 Kind : keyboard Protocol : HID++ 2.0 Polling rate : 20 ms (50Hz) Serial number: 9BB42D7D Model ID: 000000000000 Unit ID: 00000000 Firmware: RQK 40.00.B0011 The power switch is located on the bottom left corner. Supports 14 HID++ 2.0 features: 0: ROOT {0000} 1: FEATURE SET {0001} 2: FEATURE INFO {0002} 3: DEVICE FW VERSION {0003} Firmware: Firmware RQK 40.00.B0011 400D Unit ID: 00000000 Model ID: 000000000000 Transport IDs: {} 4: DEVICE NAME {0005} Name: Wireless Keyboard K230 Kind: keyboard 5: BATTERY STATUS {1000} Battery: 70%, discharging, next level 20%. 6: unknown:1820 {1820} hidden 7: REPROG CONTROLS {1B00} 8: REPROG CONTROLS V2 {1B01} 9: WIRELESS DEVICE STATUS {1D4B} 10: REMAINING PAIRING {1DF0} hidden Remaining Pairings: 54 11: unknown:1DF3 {1DF3} hidden 12: ENCRYPTION {4100} 13: KEYBOARD LAYOUT {4520} Has 12 reprogrammable keys: 0: Fn F1 => Do Nothing One is FN, reprogrammable 1: Fn F2 => Do Nothing One is FN, reprogrammable 2: Fn F3 => Do Nothing One is FN, reprogrammable 3: Fn F4 => Do Nothing One is FN, reprogrammable 4: Fn F5 => Do Nothing One is FN, reprogrammable 5: Fn F6 => Do Nothing One is FN, reprogrammable 6: Fn F7 => Do Nothing One is FN, reprogrammable 7: Fn F8 => Do Nothing One is FN, reprogrammable 8: Fn F9 => Do Nothing One is FN, reprogrammable 9: Mute => Mute is FN 10: Volume Down => Volume Down is FN 11: Volume Up => Volume Up is FN Battery: 70%, discharging, next level 20%. Solaar-1.1.14/docs/devices/Wireless Keyboard K270(unifying) 4003.txt000066400000000000000000000032111473525213700243230ustar00rootroot00000000000000 2: Wireless Keyboard K270(unifying) Codename : K270(unifying) Kind : keyboard Wireless PID : 4003 Protocol : HID++ 2.0 Polling rate : 20 ms (50Hz) Serial number: FE8C8542 Firmware: RQK 35.00.B0017 The power switch is located on the top case. Supports 12 HID++ 2.0 features: 0: ROOT {0000} 1: FEATURE SET {0001} 2: DEVICE FW VERSION {0003} 3: DEVICE NAME {0005} 4: BATTERY STATUS {1000} 5: unknown:1820 {1820} hidden 6: REPROG CONTROLS {1B00} 7: WIRELESS DEVICE STATUS {1D4B} 8: unknown:1DF0 {1DF0} hidden 9: unknown:1DF3 {1DF3} hidden 10: ENCRYPTION {4100} 11: KEYBOARD LAYOUT {4520} Has 8 reprogrammable keys: 0: Play/Pause => Play/Pause nonstandard 1: Mute => Mute nonstandard 2: Volume Down => Volume Down nonstandard 3: Volume Up => Volume Up nonstandard 4: MY HOME => HomePage nonstandard, reprogrammable 5: Mail => Email nonstandard, reprogrammable 6: SLEEP => Sleep nonstandard, reprogrammable 7: Calculator => Calculator nonstandard, reprogrammable Battery: 90%, discharging. Solaar-1.1.14/docs/devices/Wireless Keyboard K360 4004.txt000066400000000000000000000025551473525213700224440ustar00rootroot00000000000000Solaar version 1.1.4 2: Wireless Keyboard K360 Device path : /dev/hidraw2 WPID : 4004 Codename : K360 Kind : keyboard Protocol : HID++ 2.0 Polling rate : 20 ms (50Hz) Serial number: 334AC01A Model ID: 000000000000 Unit ID: 00000000 Firmware: RQK 36.00.B0007 The power switch is located on the top case. Supports 12 HID++ 2.0 features: 0: ROOT {0000} 1: FEATURE SET {0001} 2: DEVICE FW VERSION {0003} Firmware: Firmware RQK 36.00.B0007 4004 Unit ID: 00000000 Model ID: 000000000000 Transport IDs: {} 3: DEVICE NAME {0005} Name: Wireless Keyboard K360 Kind: keyboard 4: BATTERY STATUS {1000} Battery: 70%, discharging, next level 20%. 5: unknown:1820 {1820} hidden 6: REPROG CONTROLS {1B00} 7: WIRELESS DEVICE STATUS {1D4B} 8: REMAINING PAIRING {1DF0} hidden Remaining Pairings: 55 9: unknown:1DF3 {1DF3} hidden 10: FN INVERSION {40A0} Swap Fx function (saved): False Swap Fx function : False 11: ENCRYPTION {4100} Battery: 70%, discharging, next level 20%. Solaar-1.1.14/docs/devices/Wireless Keyboard K470 4075.txt000066400000000000000000000062211473525213700224500ustar00rootroot00000000000000Solaar version 1.1.7 1: Wireless Keyboard Device path : /dev/hidraw6 WPID : 4075 Codename : Kind : keyboard Protocol : HID++ 4.5 Polling rate : 20 ms (50Hz) Serial number: 00000000 Model ID: 000000000000 Unit ID: 00000000 Firmware: RQK 71.00.B0002 Supports 20 HID++ 2.0 features: 0: ROOT {0000} V0 1: FEATURE SET {0001} V0 2: DEVICE FW VERSION {0003} V0 Firmware: Firmware RQK 71.00.B0002 4075 Unit ID: 00000000 Model ID: 000000000000 Transport IDs: {} 3: DEVICE NAME {0005} V0 Name: Wireless Keyboard Kind: keyboard 4: RESET {0020} V0 5: BATTERY STATUS {1000} V0 Battery: 30%, discharging, next level 5%. 6: REPROG CONTROLS V4 {1B04} V2 Key/Button Diversion (saved): {Calculator:Regular, Mail:Regular, My Home:Regular, Search:Regular} Key/Button Diversion : {Calculator:Regular, Mail:Regular, My Home:Regular, Search:Regular} 7: WIRELESS DEVICE STATUS {1D4B} V0 8: NEW FN INVERSION {40A2} V0 Fn-swap: disabled Fn-swap default: disabled Swap Fx function (saved): False Swap Fx function : False 9: ENCRYPTION {4100} V0 10: LOCK KEY STATE {4220} V0 11: KEYBOARD DISABLE KEYS {4521} V0 Disable keys (saved): {Caps Lock:False, Num Lock:False, Scroll Lock:False, Insert:False, Win:False} Disable keys : {Caps Lock:False, Num Lock:False, Scroll Lock:False, Insert:False, Win:False} 12: unknown:1810 {1810} V0 internal, hidden 13: unknown:1830 {1830} V0 internal, hidden 14: unknown:1890 {1890} V0 internal, hidden 15: unknown:18A0 {18A0} V0 internal, hidden 16: unknown:18B0 {18B0} V0 internal, hidden 17: unknown:1DF3 {1DF3} V0 internal, hidden 18: unknown:1E00 {1E00} V0 hidden 19: unknown:1868 {1868} V0 internal, hidden Has 4 reprogrammable keys: 0: My Home , default: HomePage => HomePage is FN, FN sensitive, reprogrammable, divertable, pos:1, group:0, group mask:empty reporting: default 1: Mail , default: Email => Email is FN, FN sensitive, reprogrammable, divertable, pos:2, group:0, group mask:empty reporting: default 2: Search , default: Search Files => Search Files is FN, FN sensitive, reprogrammable, divertable, pos:3, group:0, group mask:empty reporting: default 3: Calculator , default: Calculator => Calculator is FN, FN sensitive, reprogrammable, divertable, pos:4, group:0, group mask:empty reporting: default Battery: 30%, discharging, next level 5%. Solaar-1.1.14/docs/devices/Wireless Keyboard K520 2011.txt000066400000000000000000000006721473525213700224340ustar00rootroot00000000000000 2: Wireless Keyboard K520 Device path : /dev/hidraw5 WPID : 2011 Codename : K520 Kind : keyboard Protocol : HID++ 1.0 Polling rate : 20 ms (50Hz) Serial number: C17F6771 Firmware: 26.00.B0012 Other: 00.07 The power switch is located on the top case. Notifications: battery status (0x100000). Battery: full, discharging. Has Swap Fn register Solaar-1.1.14/docs/devices/Wireless Keyboard MK270 4023.txt000066400000000000000000000033001473525213700225470ustar00rootroot00000000000000Solaar version 1.1.7 1: Wireless Keyboard MK270 Device path : /dev/hidraw6 WPID : 4023 Codename : MK270 Kind : keyboard Protocol : HID++ 2.0 Polling rate : 20 ms (50Hz) Serial number: 90A794A3 Model ID: 000000000000 Unit ID: 00000000 Firmware: RQK 49.00.B0029 Supports 18 HID++ 2.0 features: 0: ROOT {0000} V0 1: FEATURE SET {0001} V0 2: DEVICE FW VERSION {0003} V0 Firmware: Firmware RQK 49.00.B0029 4023 Unit ID: 00000000 Model ID: 000000000000 Transport IDs: {} 3: DEVICE NAME {0005} V0 Name: Wireless Keyboard Kind: keyboard 4: BATTERY STATUS {1000} V0 Battery: 30%, discharging, next level 5%. 5: REPROG CONTROLS {1B00} V0 6: WIRELESS DEVICE STATUS {1D4B} V0 7: FN INVERSION {40A0} V0 Swap Fx function (saved): False Swap Fx function : False 8: ENCRYPTION {4100} V0 9: KEYBOARD LAYOUT {4520} V0 10: unknown:1810 {1810} V0 internal, hidden 11: unknown:1830 {1830} V0 internal, hidden 12: unknown:1890 {1890} V0 internal, hidden 13: unknown:18A0 {18A0} V0 internal, hidden 14: unknown:18B0 {18B0} V0 internal, hidden 15: unknown:1DF3 {1DF3} V0 internal, hidden 16: unknown:1E00 {1E00} V0 hidden 17: unknown:1868 {1868} V0 internal, hidden Battery: 30%, discharging, next level 5%. Solaar-1.1.14/docs/devices/Wireless Mobile Mouse MX Anywhere 2 4072.txt000066400000000000000000000071551473525213700247600ustar00rootroot00000000000000 2: Wireless Mobile Mouse MX Anywhere 2 Codename : MX Anywhere 2 Kind : mouse Wireless PID : 4072 Protocol : HID++ 4.5 Polling rate : 8 ms (125Hz) Serial number: DDAE1933 Bootloader: BOT 57.10.B0003 Firmware: MPM 13.10.B0003 Firmware: MPM 13.10.B0003 Other: The power switch is located on the base. Supports 28 HID++ 2.0 features: 0: ROOT {0000} 1: FEATURE SET {0001} 2: DEVICE FW VERSION {0003} 3: DEVICE NAME {0005} 4: WIRELESS DEVICE STATUS {1D4B} 5: RESET {0020} 6: CRYPTO ID {0021} 7: BATTERY STATUS {1000} 8: CONFIG DEVICE PROPS {1806} internal, hidden 9: CHANGE HOST {1814} 10: REPROG CONTROLS V4 {1B04} 11: ADJUSTABLE DPI {2201} 12: VERTICAL SCROLLING {2100} Roller type: 3G Ratchet per turn: 24 Scroll lines: 0 13: HIRES WHEEL {2121} Multiplier: 8 Has invert Normal wheel motion Has ratchet switch Normal wheel mode Low resolution mode HID notification High Resolution Wheel Invert: False Wheel Resolution: False 14: DFUCONTROL SIGNED {00C2} 15: unknown:1813 {1813} internal, hidden 16: unknown:1830 {1830} internal, hidden 17: unknown:1890 {1890} internal, hidden 18: unknown:1891 {1891} internal, hidden 19: unknown:18A1 {18A1} internal, hidden 20: unknown:18C0 {18C0} internal, hidden 21: unknown:1DF3 {1DF3} internal, hidden 22: unknown:1E00 {1E00} hidden 23: unknown:1EB0 {1EB0} internal, hidden 24: unknown:1803 {1803} internal, hidden 25: unknown:1861 {1861} internal, hidden 26: unknown:9001 {9001} internal, hidden 27: OOBSTATE {1805} internal, hidden Has 8 reprogrammable keys: 0: Left Button , default: Left Click => Left Button mse, pos:0, group:1, gmask:1 1: Right Button , default: Right Click => Right Button mse, pos:0, group:1, gmask:1 2: Middle Button , default: Gesture Button Navigation => Middle Button mse, reprogrammable, divertable, pos:0, group:2, gmask:11 3: Back Button , default: Mouse Back Button => Back Button mse, reprogrammable, divertable, pos:0, group:3, gmask:15 4: Forward Button , default: Mouse Forward Button => Forward Button mse, reprogrammable, divertable, pos:0, group:3, gmask:15 5: Left Tilt , default: Mouse Scroll Left Button => Left Tilt mse, reprogrammable, divertable, pos:0, group:3, gmask:15 6: Right Tilt , default: Mouse Scroll Right Button => Right Tilt mse, reprogrammable, divertable, pos:0, group:3, gmask:15 7: Virtual Gesture Button , default: Virtual Gesture Button => Virtual Gesture Button divertable, virtual, pos:0, group:4, gmask:0 Battery: 90%, discharging, next level 50%. 15:29:59,190 INFO [MainThread] logitech_receiver.receiver: : found new device 3 (4024) Solaar-1.1.14/docs/devices/Wireless Mobile Mouse MX Anywhere 2S 406A.txt000066400000000000000000000114061473525213700251130ustar00rootroot00000000000000Solaar version 1.1.3 2: Wireless Mobile Mouse MX Anywhere 2S Device path : None WPID : 406A Codename : MX Anywhere 2S Kind : mouse Protocol : HID++ 4.5 Polling rate : 8 ms (125Hz) Serial number: C53F2F0B Model ID: B01A406A0000 Unit ID: 3F714CA3 Bootloader: BOT 57.00.B0003 Firmware: MPM 13.00.B0003 Firmware: MPM 13.00.B0003 Other: The power switch is located on the base. Supports 28 HID++ 2.0 features: 0: ROOT {0000} 1: FEATURE SET {0001} 2: DEVICE FW VERSION {0003} Firmware: Bootloader BOT 57.00.B0003 406AD22DCF4D01 Firmware: Firmware MPM 13.00.B0003 B01AD22DCF4D01 Firmware: Firmware MPM 13.00.B0003 406AD22DCF4D01 Firmware: Other Unit ID: 3F714CA3 Model ID: B01A406A0000 Transport IDs: {'btleid': 'B01A', 'wpid': '406A'} 3: DEVICE NAME {0005} Name: Wireless Mobile Mouse MX Anywhere 2S Kind: mouse 4: WIRELESS DEVICE STATUS {1D4B} 5: RESET {0020} 6: BATTERY STATUS {1000} Battery: 90%, discharging, next level 50%. 7: CONFIG DEVICE PROPS {1806} internal, hidden 8: CHANGE HOST {1814} 9: REPROG CONTROLS V4 {1B04} 10: ADJUSTABLE DPI {2201} Sensitivity (DPI) (saved): 4000 Sensitivity (DPI) : 4000 11: VERTICAL SCROLLING {2100} Roller type: 3G Ratchet per turn: 24 Scroll lines: 0 12: HIRES WHEEL {2121} Multiplier: 8 Has invert: Normal wheel motion Has ratchet switch: Free wheel mode Low resolution mode HID notification Scroll Wheel Direction (saved): False Scroll Wheel Direction : False Scroll Wheel Resolution (saved): False Scroll Wheel Resolution : False Scroll Wheel Diversion (saved): False Scroll Wheel Diversion : False 13: DFUCONTROL UNSIGNED {00C1} 14: DFUCONTROL SIGNED {00C2} 15: unknown:1813 {1813} internal, hidden 16: unknown:1830 {1830} internal, hidden 17: unknown:1890 {1890} internal, hidden 18: unknown:1891 {1891} internal, hidden 19: unknown:18A1 {18A1} internal, hidden 20: unknown:18C0 {18C0} internal, hidden 21: unknown:1DF3 {1DF3} internal, hidden 22: unknown:1E00 {1E00} hidden 23: unknown:1EB0 {1EB0} internal, hidden 24: unknown:1803 {1803} internal, hidden 25: unknown:1861 {1861} internal, hidden 26: unknown:9001 {9001} internal, hidden 27: OOBSTATE {1805} internal, hidden Has 8 reprogrammable keys: 0: Left Button , default: Left Click => Left Click mse, pos:0, group:1, group mask:g1 reporting: default 1: Right Button , default: Right Click => Right Click mse, pos:0, group:1, group mask:g1 reporting: default 2: Middle Button , default: Gesture Button Navigation => Gesture Button Navigation mse, reprogrammable, divertable, raw XY, pos:0, group:2, group mask:g1,g2,g4 reporting: default 3: Back Button , default: Mouse Back Button => Mouse Back Button mse, reprogrammable, divertable, raw XY, pos:0, group:3, group mask:g1,g2,g3,g4 reporting: default 4: Forward Button , default: Mouse Forward Button => Mouse Forward Button mse, reprogrammable, divertable, raw XY, pos:0, group:3, group mask:g1,g2,g3,g4 reporting: default 5: Left Tilt , default: Mouse Scroll Left Button => Mouse Scroll Left Button mse, reprogrammable, divertable, raw XY, pos:0, group:3, group mask:g1,g2,g3,g4 reporting: default 6: Right Tilt , default: Mouse Scroll Right Button => Mouse Scroll Right Button mse, reprogrammable, divertable, raw XY, pos:0, group:3, group mask:g1,g2,g3,g4 reporting: default 7: Virtual Gesture Button , default: Virtual Gesture Button => Virtual Gesture Button divertable, virtual, raw XY, force raw XY, pos:0, group:4, group mask:empty reporting: default Battery: 90%, discharging, next level 50%. Solaar-1.1.14/docs/devices/Wireless Mobile Mouse MX Anywhere 2S B01A.txt000066400000000000000000000127221473525213700251260ustar00rootroot00000000000000solaar version 1.1.9 1: Wireless Mobile Mouse MX Anywhere 2S Device path : /dev/hidraw1 USB id : 046d:B01A Codename : Wireless Kind : mouse Protocol : HID++ 4.5 Serial number: Model ID: B01A406A0000 Unit ID: 3F714CA3 Bootloader: BOT 57.00.B0003 Firmware: MPM 13.00.B0003 Firmware: MPM 13.00.B0003 Other: Supports 24 HID++ 2.0 features: 0: ROOT {0000} V0 1: FEATURE SET {0001} V0 2: DEVICE FW VERSION {0003} V2 Firmware: Bootloader BOT 57.00.B0003 406AD22DCF4D01 Firmware: Firmware MPM 13.00.B0003 B01AD22DCF4D01 Firmware: Firmware MPM 13.00.B0003 406AD22DCF4D01 Firmware: Other Unit ID: 3F714CA3 Model ID: B01A406A0000 Transport IDs: {'btleid': 'B01A', 'wpid': '406A'} 3: DEVICE NAME {0005} V0 Name: Wireless Mobile Mouse MX Anywhere 2S Kind: mouse 4: WIRELESS DEVICE STATUS {1D4B} V0 5: CONFIG CHANGE {0020} V0 6: BATTERY STATUS {1000} V0 Battery: 90%, discharging, next level 50%. 7: CONFIG DEVICE PROPS {1806} V0 internal, hidden 8: CHANGE HOST {1814} V1 Change Host : 2:mburcheri2 9: REPROG CONTROLS V4 {1B04} V3 Key/Button Actions (saved): {Left Button:Left Click, Right Button:Right Click, Middle Button:Gesture Button Navigation, Back Button:Mouse Back Button, Forward Button:Mouse Forward Button, Left Tilt:Mouse Scroll Left Button , Right Tilt:Mouse Scroll Right Button} Key/Button Actions : {Left Button:Left Click, Right Button:Right Click, Middle Button:Gesture Button Navigation, Back Button:Mouse Back Button, Forward Button:Mouse Forward Button, Left Tilt:Mouse Scroll Left Button , Right Tilt:Mouse Scroll Right Button} Key/Button Diversion (saved): {Middle Button:Regular, Back Button:Sliding DPI, Forward Button:Regular, Left Tilt:Regular, Right Tilt:Regular} Key/Button Diversion : {Middle Button:Regular, Back Button:Diverted, Forward Button:Regular, Left Tilt:Regular, Right Tilt:Regular} 10: ADJUSTABLE DPI {2201} V1 Sensitivity (DPI) (saved): 3400 Sensitivity (DPI) : 3400 11: VERTICAL SCROLLING {2100} V0 Roller type: 3G Ratchet per turn: 24 Scroll lines: 0 12: HIRES WHEEL {2121} V0 Multiplier: 8 Has invert: Normal wheel motion Has ratchet switch: Free wheel mode Low resolution mode HID notification Scroll Wheel Direction (saved): False Scroll Wheel Direction : False Scroll Wheel Resolution (saved): False Scroll Wheel Resolution : False Scroll Wheel Diversion (saved): False Scroll Wheel Diversion : False 13: unknown:1813 {1813} V0 internal, hidden 14: unknown:1830 {1830} V0 internal, hidden 15: unknown:18A1 {18A1} V0 internal, hidden 16: unknown:18C0 {18C0} V0 internal, hidden 17: unknown:1DF3 {1DF3} V0 internal, hidden 18: unknown:1E00 {1E00} V0 hidden 19: unknown:1EB0 {1EB0} V0 internal, hidden 20: unknown:1803 {1803} V0 internal, hidden 21: unknown:1861 {1861} V0 internal, hidden 22: unknown:9001 {9001} V0 internal, hidden 23: OOBSTATE {1805} V0 internal, hidden Has 8 reprogrammable keys: 0: Left Button , default: Left Click => Left Click mse, pos:0, group:1, group mask:g1 reporting: default 1: Right Button , default: Right Click => Right Click mse, pos:0, group:1, group mask:g1 reporting: default 2: Middle Button , default: Gesture Button Navigation => Gesture Button Navigation mse, reprogrammable, divertable, raw XY, pos:0, group:2, group mask:g1,g2,g4 reporting: default 3: Back Button , default: Mouse Back Button => Mouse Back Button mse, reprogrammable, divertable, raw XY, pos:0, group:3, group mask:g1,g2,g3,g4 reporting: diverted, raw XY diverted 4: Forward Button , default: Mouse Forward Button => Mouse Forward Button mse, reprogrammable, divertable, raw XY, pos:0, group:3, group mask:g1,g2,g3,g4 reporting: default 5: Left Tilt , default: Mouse Scroll Left Button => Mouse Scroll Left Button mse, reprogrammable, divertable, raw XY, pos:0, group:3, group mask:g1,g2,g3,g4 reporting: default 6: Right Tilt , default: Mouse Scroll Right Button => Mouse Scroll Right Button mse, reprogrammable, divertable, raw XY, pos:0, group:3, group mask:g1,g2,g3,g4 reporting: default 7: Virtual Gesture Button , default: Virtual Gesture Button => Virtual Gesture Button divertable, virtual, raw XY, force raw XY, pos:0, group:4, group mask:empty reporting: default Battery: 90%, discharging, next level 50%. Solaar-1.1.14/docs/devices/Wireless Mouse 4022.txt000066400000000000000000000027511473525213700213660ustar00rootroot00000000000000 2: Wireless Mouse Codename : Kind : mouse Wireless PID : 4022 Protocol : HID++ 2.0 Polling rate : 8 ms (125Hz) Serial number: 00000000 Firmware: RQM 38.00.B0044 Supports 18 HID++ 2.0 features: 0: ROOT {0000} 1: FEATURE SET {0001} 2: DEVICE FW VERSION {0003} 3: DEVICE NAME {0005} 4: BATTERY STATUS {1000} 5: REPROG CONTROLS {1B00} 6: WIRELESS DEVICE STATUS {1D4B} 7: VERTICAL SCROLLING {2100} 8: MOUSE POINTER {2200} 9: unknown:1810 {1810} internal, hidden 10: unknown:1830 {1830} internal, hidden 11: unknown:1850 {1850} internal, hidden 12: unknown:1890 {1890} internal, hidden 13: unknown:18B0 {18B0} internal, hidden 14: unknown:1DF3 {1DF3} internal, hidden 15: unknown:1868 {1868} internal, hidden 16: unknown:1869 {1869} internal, hidden 17: unknown:1E00 {1E00} hidden Has 3 reprogrammable keys: 0: LEFT CLICK => LeftClick mse, reprogrammable 1: RIGHT CLICK => RightClick mse, reprogrammable 2: MIDDLE BUTTON => MiddleMouseButton mse, reprogrammable Battery: 30%, discharging. Solaar-1.1.14/docs/devices/Wireless Mouse Dell WM514 4029.txt000066400000000000000000000043211473525213700227670ustar00rootroot00000000000000Solaar version 1.1.6rc4 3: Wireless Mouse Dell WM514 Device path : /dev/hidraw5 WPID : 4029 Codename : Dell WM514 Kind : mouse Protocol : HID++ 3.0 Polling rate : 8 ms (125Hz) Serial number: 17F2CD56 Model ID: 000000000000 Unit ID: 00000000 Firmware: RQM 44.00.B0013 The power switch is located on the base. Supports 23 HID++ 2.0 features: 0: ROOT {0000} V0 1: FEATURE SET {0001} V0 2: DEVICE FW VERSION {0003} V0 Firmware: Firmware RQM 44.00.B0013 4029 Unit ID: 00000000 Model ID: 000000000000 Transport IDs: {} 3: DEVICE NAME {0005} V0 Name: Wireless Mouse Dell WM514 Kind: mouse 4: BATTERY STATUS {1000} V0 Battery: 70%, discharging, next level 5%. 5: unknown:1830 {1830} V0 internal, hidden 6: unknown:1860 {1860} V0 internal, hidden 7: unknown:1890 {1890} V0 internal, hidden 8: unknown:18A0 {18A0} V0 internal, hidden 9: unknown:18B0 {18B0} V0 internal, hidden 10: unknown:18C0 {18C0} V0 internal, hidden 11: WIRELESS DEVICE STATUS {1D4B} V0 12: unknown:1DF3 {1DF3} V0 internal, hidden 13: REPROG CONTROLS {1B00} V0 14: REMAINING PAIRING {1DF0} V0 hidden Remaining Pairings: 124 15: unknown:1E00 {1E00} V0 hidden 16: unknown:1850 {1850} V0 internal, hidden 17: unknown:1E90 {1E90} V0 internal, hidden 18: unknown:1F03 {1F03} V0 internal, hidden 19: VERTICAL SCROLLING {2100} V0 Roller type: standard Ratchet per turn: 24 Scroll lines: 0 20: MOUSE POINTER {2200} V0 DPI: 1000 Acceleration: low Override OS ballistics No vertical tuning, standard mice 21: DEVICE GROUPS {0006} V0 22: unknown:1BB0 {1BB0} V0 Battery: 70%, discharging, next level 5%. Solaar-1.1.14/docs/devices/Wireless Mouse M185 new 4054.txt000066400000000000000000000056531473525213700225640ustar00rootroot00000000000000Solaar version 1.1.4rc2 2: Wireless Mouse M185 new Device path : /dev/hidraw3 WPID : 4054 Codename : M185n Kind : mouse Protocol : HID++ 4.5 Polling rate : 8 ms (125Hz) Serial number: 00000000 Model ID: 000000000000 Unit ID: 00000000 Firmware: RQM 64.00.B0008 The power switch is located on the base. Supports 19 HID++ 2.0 features: 0: ROOT {0000} 1: FEATURE SET {0001} 2: DEVICE FW VERSION {0003} Firmware: Firmware RQM 64.00.B0008 4054 Unit ID: 00000000 Model ID: 000000000000 Transport IDs: {} 3: DEVICE NAME {0005} Name: Wireless Mouse Kind: mouse 4: RESET {0020} 5: REPROG CONTROLS V4 {1B04} Mouse Gestures (saved): Off Mouse Gestures : Off Key/Button Actions (saved): {Left Button:Left Click, Right Button:Right Click, Middle Button:Mouse Middle Button} Key/Button Actions : {Left Button:Left Click, Right Button:Right Click, Middle Button:Mouse Middle Button} Key/Button Diversion (saved): {Middle Button:Regular} Key/Button Diversion : {Middle Button:Regular} 6: WIRELESS DEVICE STATUS {1D4B} 7: LOWRES WHEEL {2130} Wheel Reports: HID Scroll Wheel Diversion (saved): False Scroll Wheel Diversion : False 8: POINTER SPEED {2205} Pointer Speed: 1.0 Sensitivity (Pointer Speed) (saved): 256 Sensitivity (Pointer Speed) : 256 9: DEVICE RESET {1802} internal, hidden 10: unknown:1810 {1810} internal, hidden 11: unknown:1830 {1830} internal, hidden 12: unknown:1850 {1850} internal, hidden 13: unknown:1869 {1869} internal, hidden 14: unknown:1890 {1890} internal, hidden 15: unknown:18B1 {18B1} internal, hidden 16: unknown:1DF3 {1DF3} internal, hidden 17: unknown:1E00 {1E00} hidden 18: unknown:1F03 {1F03} internal, hidden Has 3 reprogrammable keys: 0: Left Button , default: Left Click => Left Click mse, reprogrammable, pos:0, group:1, group mask:g1 reporting: default 1: Right Button , default: Right Click => Right Click mse, reprogrammable, pos:0, group:1, group mask:g1 reporting: default 2: Middle Button , default: Mouse Middle Button => Mouse Middle Button mse, reprogrammable, divertable, raw XY, pos:0, group:2, group mask:g1,g2 reporting: default Battery status unavailable. Solaar-1.1.14/docs/devices/Wireless Mouse M185,M235,M310 4055.txt000066400000000000000000000060611473525213700230450ustar00rootroot00000000000000Solaar version 1.1.4 1: Wireless Mouse M185/M235/M310 Device path : /dev/hidraw2 WPID : 4055 Codename : M185/M235/M310 Kind : mouse Protocol : HID++ 4.5 Polling rate : 8 ms (125Hz) Serial number: 82C9A01F Model ID: 405500000000 Unit ID: 82C9A01F Firmware: RQM 65.00.B0003 The power switch is located on the base. Supports 21 HID++ 2.0 features: 0: ROOT {0000} 1: FEATURE SET {0001} 2: DEVICE FW VERSION {0003} Firmware: Firmware RQM 65.00.B0003 4055 Unit ID: 82C9A01F Model ID: 405500000000 Transport IDs: {'wpid': '4055'} 3: DEVICE NAME {0005} Name: Wireless Mouse Kind: mouse 4: RESET {0020} 5: BATTERY STATUS {1000} Battery: 70%, discharging, next level 5%. 6: unknown:1810 {1810} internal, hidden 7: unknown:1830 {1830} internal, hidden 8: DEVICE RESET {1802} internal, hidden 9: unknown:1862 {1862} internal, hidden 10: unknown:1890 {1890} internal, hidden 11: unknown:18A0 {18A0} internal, hidden 12: unknown:18B1 {18B1} internal, hidden 13: REPROG CONTROLS V4 {1B04} Key/Button Actions (saved): {Left Button:Left Click, Right Button:Right Click, Middle Button:Mouse Middle Button} Key/Button Actions : {Left Button:Left Click, Right Button:Right Click, Middle Button:Mouse Middle Button} Key/Button Diversion (saved): {Left Button:Regular, Right Button:Regular, Middle Button:Regular} Key/Button Diversion : {Left Button:Regular, Right Button:Regular, Middle Button:Regular} 14: WIRELESS DEVICE STATUS {1D4B} 15: REMAINING PAIRING {1DF0} hidden Remaining Pairings: 116 16: unknown:1DF3 {1DF3} internal, hidden 17: unknown:1E00 {1E00} hidden 18: unknown:1EB0 {1EB0} internal, hidden 19: unknown:1F03 {1F03} internal, hidden 20: LOWRES WHEEL {2130} Wheel Reports: HID Scroll Wheel Diversion (saved): False Scroll Wheel Diversion : False Has 3 reprogrammable keys: 0: Left Button , default: Left Click => Left Click mse, divertable, pos:0, group:1, group mask:g1 reporting: default 1: Right Button , default: Right Click => Right Click mse, divertable, pos:0, group:1, group mask:g1 reporting: analytics key events reporting 2: Middle Button , default: Mouse Middle Button => Mouse Middle Button mse, reprogrammable, divertable, raw XY, pos:0, group:2, group mask:g1,g2 reporting: unknown:000200 Battery: 70%, discharging, next level 5%. Solaar-1.1.14/docs/devices/Wireless Mouse M215 2nd Gen 401B.txt000066400000000000000000000034101473525213700231410ustar00rootroot00000000000000Solaar version 1.1.5 1: Wireless Mouse M215 2nd Gen Device path : None WPID : 401B Codename : Wireless Kind : mouse Protocol : HID++ 2.0 Polling rate : 8 ms (125Hz) Serial number: D225825E Model ID: 000000000000 Unit ID: 00000000 Firmware: RQM 27.12.B0029 The power switch is located on the base. Supports 13 HID++ 2.0 features: 0: ROOT {0000} V0 1: FEATURE SET {0001} V0 2: DEVICE FW VERSION {0003} V0 Firmware: Firmware RQM 27.12.B0029 401B Unit ID: 00000000 Model ID: 000000000000 Transport IDs: {} 3: DEVICE NAME {0005} V0 Name: Wireless Mouse M215 2nd Gen Kind: mouse 4: BATTERY STATUS {1000} V0 Battery: 90%, discharging, next level 5%. 5: WIRELESS DEVICE STATUS {1D4B} V0 6: unknown:1DF3 {1DF3} V0 hidden 7: REPROG CONTROLS {1B00} V0 8: REMAINING PAIRING {1DF0} V0 hidden Remaining Pairings: 126 9: unknown:1F03 {1F03} V0 hidden 10: VERTICAL SCROLLING {2100} V0 Roller type: standard Ratchet per turn: 24 Scroll lines: 0 11: HI RES SCROLLING {2120} V0 Hi-res scrolling disabled Hi-res scrolling multiplier: 8 Scroll Wheel High Resolution (saved): False Scroll Wheel High Resolution : False 12: MOUSE POINTER {2200} V0 DPI: 800 Acceleration: low Override OS ballistics No vertical tuning, standard mice Battery: 90%, discharging, next level 5%. Solaar-1.1.14/docs/devices/Wireless Mouse M310 M310t 4031.txt000066400000000000000000000033321473525213700225500ustar00rootroot00000000000000 1: Wireless Mouse M310/M310t Codename : M310/M310t Kind : mouse Wireless PID : 4031 Protocol : HID++ 2.0 Polling rate : 8 ms (125Hz) Serial number: 03E8C0B4 Firmware: RQM 40.01.B0017 The power switch is located on the base. Supports 21 HID++ 2.0 features: 0: ROOT {0000} 1: FEATURE SET {0001} 2: DEVICE FW VERSION {0003} 3: DEVICE NAME {0005} 4: BATTERY STATUS {1000} 5: unknown:1830 {1830} internal, hidden 6: unknown:1850 {1850} internal, hidden 7: unknown:1860 {1860} internal, hidden 8: unknown:1890 {1890} internal, hidden 9: unknown:18A0 {18A0} internal, hidden 10: unknown:18C0 {18C0} internal, hidden 11: WIRELESS DEVICE STATUS {1D4B} 12: unknown:1DF3 {1DF3} internal, hidden 13: REPROG CONTROLS {1B00} 14: unknown:1DF0 {1DF0} hidden 15: unknown:1E00 {1E00} hidden 16: unknown:1E80 {1E80} internal, hidden 17: unknown:1E90 {1E90} internal, hidden 18: unknown:1F03 {1F03} internal, hidden 19: VERTICAL SCROLLING {2100} 20: MOUSE POINTER {2200} Has 3 reprogrammable keys: 0: LEFT CLICK => LeftClick mse, reprogrammable 1: RIGHT CLICK => RightClick mse, reprogrammable 2: MIDDLE BUTTON => MiddleMouseButton mse, reprogrammable Battery: 70%, discharging. Solaar-1.1.14/docs/devices/Wireless Mouse M325 400A.txt000066400000000000000000000065331473525213700220140ustar00rootroot00000000000000 1: Wireless Mouse M325 Device path : /dev/hidraw4 WPID : 400A Codename : M325 Kind : mouse Protocol : HID++ 2.0 Polling rate : 8 ms (125Hz) Serial number: D72D97E9 Model ID: 000000000000 Unit ID: 00000000 Firmware: RQM 40.01.B0018 The power switch is located on the base. Supports 22 HID++ 2.0 features: 0: ROOT {0000} V0 1: FEATURE SET {0001} V0 2: DEVICE FW VERSION {0003} V0 Firmware: Firmware RQM 40.01.B0018 400A Unit ID: 00000000 Model ID: 000000000000 Transport IDs: {} 3: DEVICE NAME {0005} V0 Name: Wireless Mouse M325 Kind: mouse 4: BATTERY STATUS {1000} V0 Battery: 70%, discharging, next level 5%. 5: unknown:1830 {1830} V0 internal, hidden 6: unknown:1850 {1850} V0 internal, hidden 7: unknown:1860 {1860} V0 internal, hidden 8: unknown:1890 {1890} V0 internal, hidden 9: unknown:18A0 {18A0} V0 internal, hidden 10: unknown:18C0 {18C0} V0 internal, hidden 11: WIRELESS DEVICE STATUS {1D4B} V0 12: unknown:1DF3 {1DF3} V0 internal, hidden 13: REPROG CONTROLS {1B00} V0 14: REMAINING PAIRING {1DF0} V0 hidden Remaining Pairings: 117 15: unknown:1E00 {1E00} V0 hidden 16: unknown:1E80 {1E80} V0 internal, hidden 17: unknown:1E90 {1E90} V0 internal, hidden 18: unknown:1F03 {1F03} V0 internal, hidden 19: VERTICAL SCROLLING {2100} V0 Roller type: micro Ratchet per turn: 36 Scroll lines: 0 20: MOUSE POINTER {2200} V0 DPI: 800 Acceleration: low Override OS ballistics No vertical tuning, standard mice 21: unknown:18B0 {18B0} V0 internal, hidden Battery: 70%, discharging, next level 5%. Wireless Mouse M325 Codename : M325 Kind : mouse Wireless PID : 400A Protocol : HID++ 2.0 Polling rate : 8 ms (125Hz) Serial number: ABB05E01 Firmware: RQM 27.02.B0028 The power switch is located on the base. Supports 13 HID++ 2.0 features: 0: ROOT {0000} 1: FEATURE SET {0001} 2: DEVICE FW VERSION {0003} 3: DEVICE NAME {0005} 4: BATTERY STATUS {1000} 5: WIRELESS DEVICE STATUS {1D4B} 6: unknown:1DF3 {1DF3} hidden 7: REPROG CONTROLS {1B00} 8: unknown:1DF0 {1DF0} hidden 9: unknown:1F03 {1F03} hidden 10: VERTICAL SCROLLING {2100} 11: HI RES SCROLLING {2120} 12: MOUSE POINTER {2200} Has 5 reprogrammable keys: 0: LEFT CLICK => LeftClick mse, reprogrammable 1: RIGHT CLICK => RightClick mse, reprogrammable 2: MIDDLE BUTTON => MiddleMouseButton mse, reprogrammable 3: BACK AS BUTTON 4 => Back mse, reprogrammable 4: FORWARD AS BUTTON 5 => BrowserForward mse, reprogrammable Solaar-1.1.14/docs/devices/Wireless Mouse M345 4017.txt000066400000000000000000000015351473525213700220020ustar00rootroot000000000000001: Wireless Mouse M345 Codename : M345 Kind : mouse Wireless PID : 4017 Protocol : HID++ 2.0 Polling rate : 8 ms Serial number: 920DC223 Firmware: RQM 27.02.B0028 The power switch is located on the base. Supports 13 HID++ 2.0 features: 0: ROOT {0000} 1: FEATURE SET {0001} 2: DEVICE FW VERSION {0003} 3: DEVICE NAME {0005} 4: BATTERY STATUS {1000} 5: WIRELESS DEVICE STATUS {1D4B} 6: unknown:1DF3 {1DF3} hidden 7: REPROG CONTROLS {1B00} 8: unknown:1DF0 {1DF0} hidden 9: unknown:1F03 {1F03} hidden 10: VERTICAL SCROLLING {2100} 11: HI RES SCROLLING {2120} 12: MOUSE POINTER {2200} Battery: 90%, discharging, Solaar-1.1.14/docs/devices/Wireless Mouse M510 1025.txt000066400000000000000000000007711473525213700217710ustar00rootroot00000000000000Solaar version 1.1.4 1: Wireless Mouse M510 Device path : /dev/hidraw2 WPID : 1025 Codename : M510 Kind : mouse Protocol : HID++ 1.0 Polling rate : 8 ms (125Hz) Serial number: 93E8527F Firmware: 23.01.B0016 The power switch is located on the base. Notifications: battery status (0x100000). Features: special buttons, buttons control resolution (0x820000) Battery: good, discharging. Has Side Scrolling register Solaar-1.1.14/docs/devices/Wireless Mouse M510 4051.txt000066400000000000000000000051271473525213700217730ustar00rootroot00000000000000 1: Wireless Mouse M510 Codename : M510v2 Kind : mouse Wireless PID : 4051 Protocol : HID++ 4.5 Polling rate : 8 ms (125Hz) Serial number: 8989B04C Firmware: RQM 62.00.B0013 The power switch is located on the base. Supports 22 HID++ 2.0 features: 0: ROOT {0000} 1: FEATURE SET {0001} 2: DEVICE FW VERSION {0003} 3: DEVICE NAME {0005} 4: RESET {0020} 5: BATTERY STATUS {1000} 6: unknown:1802 {1802} internal, hidden 7: unknown:1810 {1810} internal, hidden 8: unknown:1830 {1830} internal, hidden 9: unknown:1862 {1862} internal, hidden 10: unknown:1890 {1890} internal, hidden 11: unknown:18A0 {18A0} internal, hidden 12: unknown:18B1 {18B1} internal, hidden 13: REPROG CONTROLS V4 {1B04} 14: WIRELESS DEVICE STATUS {1D4B} 15: unknown:1DF0 {1DF0} hidden 16: unknown:1DF3 {1DF3} internal, hidden 17: unknown:1E00 {1E00} hidden 18: unknown:1EB0 {1EB0} internal, hidden 19: unknown:1F03 {1F03} internal, hidden 20: LOWRES WHEEL {2130} 21: POINTER SPEED {2205} Has 7 reprogrammable keys: 0: LEFT CLICK , default: LeftClick => LEFT CLICK divertable, mse, pos:0, group:1, gmask:1 1: RIGHT CLICK , default: RightClick => RIGHT CLICK divertable, mse, pos:0, group:1, gmask:1 2: MIDDLE BUTTON , default: MiddleMouseButton => MIDDLE BUTTON divertable, mse, reprogrammable, pos:0, group:2, gmask:3 3: LEFT SCROLL AS AC PAN , default: HorzScrollLeftSet => LEFT SCROLL AS AC PAN divertable, mse, reprogrammable, pos:0, group:2, gmask:3 4: RIGHT SCROLL AS AC PAN , default: HorzScrollRightSet => RIGHT SCROLL AS AC PAN divertable, mse, reprogrammable, pos:0, group:2, gmask:3 5: BACK AS BUTTON 4 , default: BackEx => BACK AS BUTTON 4 divertable, mse, reprogrammable, pos:0, group:2, gmask:3 6: FORWARD AS BUTTON 5 , default: BrowserForwardEx => FORWARD AS BUTTON 5 divertable, mse, reprogrammable, pos:0, group:2, gmask:3 Battery: 70%, discharging. Solaar-1.1.14/docs/devices/Wireless Mouse M525 4013.txt000066400000000000000000000026011473525213700217710ustar00rootroot00000000000000 1: Wireless Mouse M525 Codename : M525 Kind : mouse Wireless PID : 4013 Protocol : HID++ 2.0 Polling rate : 8 ms (125Hz) Serial number: DAFA335E Firmware: RQM 27.02.B0028 The power switch is located on the base. Supports 13 HID++ 2.0 features: 0: ROOT {0000} 1: FEATURE SET {0001} 2: DEVICE FW VERSION {0003} 3: DEVICE NAME {0005} 4: BATTERY STATUS {1000} 5: WIRELESS DEVICE STATUS {1D4B} 6: unknown:1DF3 {1DF3} hidden 7: REPROG CONTROLS {1B00} 8: unknown:1DF0 {1DF0} hidden 9: unknown:1F03 {1F03} hidden 10: VERTICAL SCROLLING {2100} 11: HI RES SCROLLING {2120} 12: MOUSE POINTER {2200} Has 5 reprogrammable keys: 0: LEFT CLICK => LeftClick mse, reprogrammable 1: RIGHT CLICK => RightClick mse, reprogrammable 2: MIDDLE BUTTON => MiddleMouseButton mse, reprogrammable 3: BACK AS BUTTON 4 => Back mse, reprogrammable 4: FORWARD AS BUTTON 5 => BrowserForward mse, reprogrammable Battery: 90%, discharging. Solaar-1.1.14/docs/devices/Wireless Mouse M560 402D.txt000066400000000000000000000041521473525213700220150ustar00rootroot00000000000000 1: Wireless Mouse M560 Codename : M560 Kind : mouse Wireless PID : 402D Protocol : HID++ 2.0 Polling rate : 8 ms (125Hz) Serial number: 16E6E42A Firmware: RQM 48.00.B0015 The power switch is located on the base. Supports 22 HID++ 2.0 features: 0: ROOT {0000} 1: FEATURE SET {0001} 2: DEVICE FW VERSION {0003} 3: DEVICE NAME {0005} 4: BATTERY STATUS {1000} 5: unknown:1830 {1830} internal, hidden 6: unknown:1850 {1850} internal, hidden 7: unknown:1860 {1860} internal, hidden 8: unknown:1890 {1890} internal, hidden 9: unknown:18A0 {18A0} internal, hidden 10: REPROG CONTROLS V3 {1B03} 11: WIRELESS DEVICE STATUS {1D4B} 12: unknown:1DF3 {1DF3} internal, hidden 13: REPROG CONTROLS {1B00} 14: unknown:1DF0 {1DF0} hidden 15: unknown:1E00 {1E00} hidden 16: unknown:18B0 {18B0} internal, hidden 17: unknown:1E90 {1E90} internal, hidden 18: unknown:1F03 {1F03} internal, hidden 19: VERTICAL SCROLLING {2100} 20: HI RES SCROLLING {2120} 21: MOUSE POINTER {2200} Has 7 reprogrammable keys: 0: LEFT CLICK => LeftClick mse, reprogrammable 1: RIGHT CLICK => RightClick mse, reprogrammable 2: BACK AS BUTTON 4 => Win8BackHorzLeft mse, reprogrammable 3: FORWARD AS BUTTON 5 => Win8ForwardHorzRight mse, reprogrammable 4: unknown:00B0 => Win8MetroWin7Forward mse, reprogrammable 5: SHOW DESKTOP HPP => Win8ShowDesktopWin7Back mse, reprogrammable 6: unknown:00AF => Win8Charm Appswitch GifAnimation mse, reprogrammable, unknown:000020 Battery: 70%, discharging. Solaar-1.1.14/docs/devices/Wireless Mouse MX Anywhere 2 404A.txt000066400000000000000000000033651473525213700236030ustar00rootroot00000000000000 1: Wireless Mouse MX Anywhere 2 Codename : MX Anywhere 2 Kind : mouse Wireless PID : 404A Protocol : HID++ 4.5 Polling rate : 8 ms (125Hz) Serial number: F3B81C5B Bootloader: BOT 23.00.B0007 Firmware: MPM 02.00.B0007 Firmware: MPM 02.00.B0007 Other: The power switch is located on the base. Supports 26 HID++ 2.0 features: 0: ROOT {0000} 1: FEATURE SET {0001} 2: DEVICE FW VERSION {0003} 3: DEVICE NAME {0005} 4: WIRELESS DEVICE STATUS {1D4B} 5: RESET {0020} 6: BATTERY STATUS {1000} 7: CHANGE HOST {1814} 8: REPROG CONTROLS V4 {1B04} 9: ADJUSTABLE DPI {2201} 10: VERTICAL SCROLLING {2100} 11: HIRES WHEEL {2121} 12: DFUCONTROL 2 {00C1} 13: unknown:1813 {1813} internal, hidden 14: unknown:1830 {1830} internal, hidden 15: unknown:1890 {1890} internal, hidden 16: unknown:1891 {1891} internal, hidden 17: unknown:18A1 {18A1} internal, hidden 18: unknown:18C0 {18C0} internal, hidden 19: unknown:1DF3 {1DF3} internal, hidden 20: unknown:1E00 {1E00} hidden 21: unknown:1EB0 {1EB0} internal, hidden 22: unknown:1803 {1803} internal, hidden 23: unknown:1861 {1861} internal, hidden 24: unknown:9000 {9000} internal, hidden 25: unknown:1805 {1805} internal, hidden Battery: 0%, recharging. Solaar-1.1.14/docs/devices/Wireless Mouse MX Master 2S 4069.txt000066400000000000000000000152611473525213700233670ustar00rootroot00000000000000Solaar version 1.1.4 1: Wireless Mouse MX Master 2S Device path : /dev/hidraw1 WPID : 4069 Codename : MX Master 2S Kind : mouse Protocol : HID++ 4.5 Polling rate : 8 ms (125Hz) Serial number: 9429B808 Model ID: B01940690000 Unit ID: 65D747F7 Bootloader: BOT 56.01.B0006 Firmware: MPM 12.01.B0006 Firmware: MPM 12.01.B0006 Other: The power switch is located on the base. Supports 31 HID++ 2.0 features: 0: ROOT {0000} 1: FEATURE SET {0001} 2: DEVICE FW VERSION {0003} Firmware: Bootloader BOT 56.01.B0006 406916A9FC5401 Firmware: Firmware MPM 12.01.B0006 B01916A9FC5401 Firmware: Firmware MPM 12.01.B0006 406916A9FC5401 Firmware: Other Unit ID: 65D747F7 Model ID: B01940690000 Transport IDs: {'btleid': 'B019', 'wpid': '4069'} 3: DEVICE NAME {0005} Name: Wireless Mouse MX Master 2S Kind: mouse 4: WIRELESS DEVICE STATUS {1D4B} 5: RESET {0020} 6: CRYPTO ID {0021} 7: BATTERY STATUS {1000} Battery: 50%, discharging, next level 20%. 8: CONFIG DEVICE PROPS {1806} internal, hidden 9: CHANGE HOST {1814} Cambiar Host : 2:lapega 10: REPROG CONTROLS V4 {1B04} Ajustar DPI deslizando (saved): Off Ajustar DPI deslizando : Off Gestos de Ratón (saved): Off Gestos de Ratón : Off Acciones de Tecla/Botón (saved): {Left Button:Left Click, Right Button:Right Click, Middle Button:Mouse Middle Button, Back Button:Mouse Back Button, Forward Button:Mouse Forward Button, Mouse Gesture Button:Gesture Button Navigation, Smart Shift:Smart Shift} Acciones de Tecla/Botón : {Left Button:Left Click, Right Button:Right Click, Middle Button:Mouse Middle Button, Back Button:Mouse Back Button, Forward Button:Mouse Forward Button, Mouse Gesture Button:Gesture Button Navigation, Smart Shift:Smart Shift} Desvio de Tecla/Botón (saved): {Middle Button:Normal, Back Button:Normal, Forward Button:Normal, Mouse Gesture Button:Normal, Smart Shift:Normal} Desvio de Tecla/Botón : {Middle Button:Normal, Back Button:Normal, Forward Button:Normal, Mouse Gesture Button:Normal, Smart Shift:Normal} 11: ADJUSTABLE DPI {2201} Sensibilidad (PPP) (saved): 4000 Sensibilidad (PPP) : 4000 12: VERTICAL SCROLLING {2100} Roller type: 3G Ratchet per turn: 24 Scroll lines: 0 13: SMART SHIFT {2110} Trinquete Rueda Desplazamiento (saved): 50 Trinquete Rueda Desplazamiento : 50 14: HIRES WHEEL {2121} Multiplier: 8 Has invert: Normal wheel motion Has ratchet switch: Normal wheel mode High resolution mode HID notification Dirección Rueda Desplazamiento (saved): False Dirección Rueda Desplazamiento : False Resolución Rueda Desplazamiento (saved): True Resolución Rueda Desplazamiento : True Desvio Rueda Desplazamiento (saved): False Desvio Rueda Desplazamiento : False 15: GESTURE 2 {6501} Gestos (saved): {45: True, 46: True} Gestos : {46: True, 45: True} Gestures Diversion (saved): {46: False} Gestures Diversion : {46: False} 16: DFUCONTROL SIGNED {00C2} 17: unknown:1813 {1813} internal, hidden 18: unknown:1830 {1830} internal, hidden 19: unknown:1890 {1890} internal, hidden 20: unknown:1891 {1891} internal, hidden 21: unknown:18A1 {18A1} internal, hidden 22: unknown:18C0 {18C0} internal, hidden 23: unknown:1DF3 {1DF3} internal, hidden 24: unknown:1E00 {1E00} hidden 25: unknown:1EB0 {1EB0} internal, hidden 26: unknown:1803 {1803} internal, hidden 27: unknown:1861 {1861} internal, hidden 28: unknown:9001 {9001} internal, hidden 29: unknown:9200 {9200} internal, hidden 30: unknown:9202 {9202} internal, hidden Has 8 reprogrammable keys: 0: Left Button , default: Left Click => Left Click mse, pos:0, group:1, group mask:g1 reporting: default 1: Right Button , default: Right Click => Right Click mse, pos:0, group:1, group mask:g1 reporting: default 2: Middle Button , default: Mouse Middle Button => Mouse Middle Button mse, reprogrammable, divertable, raw XY, pos:0, group:3, group mask:g1,g2,g3 reporting: default 3: Back Button , default: Mouse Back Button => Mouse Back Button mse, reprogrammable, divertable, raw XY, pos:0, group:2, group mask:g1,g2 reporting: default 4: Forward Button , default: Mouse Forward Button => Mouse Forward Button mse, reprogrammable, divertable, raw XY, pos:0, group:2, group mask:g1,g2 reporting: default 5: Mouse Gesture Button , default: Gesture Button Navigation => Gesture Button Navigation mse, reprogrammable, divertable, raw XY, pos:0, group:3, group mask:g1,g2,g3 reporting: default 6: Smart Shift , default: Smart Shift => Smart Shift mse, reprogrammable, divertable, raw XY, pos:0, group:3, group mask:g1,g2,g3 reporting: default 7: Virtual Gesture Button , default: Virtual Gesture Button => Virtual Gesture Button divertable, virtual, raw XY, force raw XY, pos:0, group:4, group mask:empty reporting: default Has 2 gesture(s), 0 param(s) and 4 spec(s): Thumbwheel Enabled( 0): True Diverted:( 0) False NaturalScrolling Enabled( 1): True Diverted:(None) None period unit Spec ( 3): 800 DVI field width Spec ( 1): 8 field widths Spec ( 2): 8 resolution Spec ( 4): 2048 Battery: 50%, discharging, next level 20%. Solaar-1.1.14/docs/devices/Wireless Mouse MX Master 2S B019.txt000066400000000000000000000145561473525213700234060ustar00rootroot00000000000000Solaar version 1.1.3 1: Wireless Mouse MX Master 2S Device path : /dev/hidraw3 USB id : 046d:B019 Codename : MX Master 2S Kind : mouse Protocol : HID++ 4.5 Serial number: Model ID: B01940690000 Unit ID: D34FD133 Bootloader: BOT 56.01.B0006 Firmware: MPM 12.01.B0006 Firmware: MPM 12.01.B0006 Other: Supports 29 HID++ 2.0 features: 0: ROOT {0000} 1: FEATURE SET {0001} 2: DEVICE FW VERSION {0003} Firmware: Bootloader BOT 56.01.B0006 406916A9FC5401 Firmware: Firmware MPM 12.01.B0006 B01916A9FC5401 Firmware: Firmware MPM 12.01.B0006 406916A9FC5401 Firmware: Other Unit ID: D34FD133 Model ID: B01940690000 Transport IDs: {'btleid': 'B019', 'wpid': '4069'} 3: DEVICE NAME {0005} Name: Wireless Mouse MX Master 2S Kind: mouse 4: WIRELESS DEVICE STATUS {1D4B} 5: RESET {0020} 6: CRYPTO ID {0021} 7: BATTERY STATUS {1000} Battery: 50%, discharging, next level 20%. 8: CONFIG DEVICE PROPS {1806} internal, hidden 9: CHANGE HOST {1814} 改变主机 : 1:yan-desktop 10: REPROG CONTROLS V4 {1B04} DPI 滑动调节 (saved): 关 DPI 滑动调节 : 关 鼠标手势 (saved): Mouse Gesture Button 鼠标手势 : 关 按键/按钮操作 (saved): {Left Button:Left Click, Right Button:Right Click, Middle Button:Mouse Middle Button, Back Button:Mouse Back Button, Forward Button:Mouse Forward Button, Mouse Gesture Button:Gesture Button Navigation, Smart Shift:Smart Shift} 按键/按钮操作 : {Left Button:Left Click, Right Button:Right Click, Middle Button:Mouse Middle Button, Back Button:Mouse Back Button, Forward Button:Mouse Forward Button, Mouse Gesture Button:Gesture Button Navigation, Smart Shift:Smart Shift} 按键/按钮改道 (saved): {Middle Button:常规, Back Button:改道, Forward Button:改道, Mouse Gesture Button:改道, Smart Shift:常规} 按键/按钮改道 : {Middle Button:常规, Back Button:改道, Forward Button:改道, Mouse Gesture Button:改道, Smart Shift:常规} 11: ADJUSTABLE DPI {2201} 灵敏度 (DPI) (saved): 1000 灵敏度 (DPI) : 1000 12: VERTICAL SCROLLING {2100} Roller type: 3G Ratchet per turn: 24 Scroll lines: 0 13: SMART SHIFT {2110} 滚轮分段模式 (saved): 16 滚轮分段模式 : 16 14: HIRES WHEEL {2121} Multiplier: 8 Has invert: Normal wheel motion Has ratchet switch: Normal wheel mode Low resolution mode HID notification 滚轮方向 (saved): False 滚轮方向 : False 滚轮分辨率 (saved): False 滚轮分辨率 : False 滚轮改道 (saved): False 滚轮改道 : False 15: GESTURE 2 {6501} 手势 (saved): {45: True, 46: True} 手势 : {46: True, 45: True} 手势改道 (saved): {46: False} 手势改道 : {46: False} 16: unknown:1813 {1813} internal, hidden 17: unknown:1830 {1830} internal, hidden 18: unknown:18A1 {18A1} internal, hidden 19: unknown:18C0 {18C0} internal, hidden 20: unknown:1DF3 {1DF3} internal, hidden 21: unknown:1E00 {1E00} hidden 22: unknown:1EB0 {1EB0} internal, hidden 23: unknown:1803 {1803} internal, hidden 24: unknown:1861 {1861} internal, hidden 25: unknown:9001 {9001} internal, hidden 26: unknown:9200 {9200} internal, hidden 27: unknown:9202 {9202} internal, hidden 28: OOBSTATE {1805} internal, hidden Has 8 reprogrammable keys: 0: Left Button , default: Left Click => Left Click mse, pos:0, group:1, group mask:g1 reporting: default 1: Right Button , default: Right Click => Right Click mse, pos:0, group:1, group mask:g1 reporting: default 2: Middle Button , default: Mouse Middle Button => Mouse Middle Button mse, reprogrammable, divertable, raw XY, pos:0, group:3, group mask:g1,g2,g3 reporting: default 3: Back Button , default: Mouse Back Button => Mouse Back Button mse, reprogrammable, divertable, raw XY, pos:0, group:2, group mask:g1,g2 reporting: diverted 4: Forward Button , default: Mouse Forward Button => Mouse Forward Button mse, reprogrammable, divertable, raw XY, pos:0, group:2, group mask:g1,g2 reporting: diverted 5: Mouse Gesture Button , default: Gesture Button Navigation => Gesture Button Navigation mse, reprogrammable, divertable, raw XY, pos:0, group:3, group mask:g1,g2,g3 reporting: diverted, raw XY diverted 6: Smart Shift , default: Smart Shift => Smart Shift mse, reprogrammable, divertable, raw XY, pos:0, group:3, group mask:g1,g2,g3 reporting: default 7: Virtual Gesture Button , default: Virtual Gesture Button => Virtual Gesture Button divertable, virtual, raw XY, force raw XY, pos:0, group:4, group mask:empty reporting: default Has 2 gesture(s), 0 param(s) and 4 spec(s): Thumbwheel Enabled( 0): True Diverted:( 0) False NaturalScrolling Enabled( 1): True Diverted:(None) None period unit Spec ( 3): 800 DVI field width Spec ( 1): 8 field widths Spec ( 2): 8 resolution Spec ( 4): 2048 Battery: 50%, discharging, next level 20%. Solaar-1.1.14/docs/devices/Wireless Mouse MX Master 4041.txt000066400000000000000000000144401473525213700231060ustar00rootroot00000000000000 4: Wireless Mouse MX Master Device path : /dev/hidraw4 WPID : 4041 Codename : MX Master Kind : mouse Protocol : HID++ 4.5 Polling rate : 8 ms (125Hz) Serial number: ---------- Model ID: B01240410000 Unit ID: ---------- Bootloader: BOT 18.01.B0014 Firmware: MPM 11.02.B0014 Firmware: MPM 11.02.B0014 Other: The power switch is located on the base. Supports 28 HID++ 2.0 features: 0: ROOT {0000} 1: FEATURE SET {0001} 2: DEVICE FW VERSION {0003} Firmware: Bootloader BOT 18.01.B0014 40410862AB80 Firmware: Firmware MPM 11.02.B0014 B0120862AB80 Firmware: Firmware MPM 11.02.B0014 40410862AB80 Firmware: Other Unit ID: ------------ Model ID: B01240410000 Transport IDs: {'btleid': 'B012', 'wpid': '4041'} 3: DEVICE NAME {0005} Name: Wireless Mouse MX Master Kind: mouse 4: WIRELESS DEVICE STATUS {1D4B} 5: RESET {0020} 6: BATTERY STATUS {1000} Battery: 50%, discharging, next level 20%. 7: CHANGE HOST {1814} Change Host : 1:jaaxx-elite 8: REPROG CONTROLS V4 {1B04} Key/Button Actions (saved): {Left Button:Left Click, Right Button:Right Click, Middle Button:Mouse Middle Button, Back Button:Mouse Back Button, Forward Button:Mouse Forward Button, Mouse Gesture Button:Gesture Button, Smart Shift:Smart Shift} Key/Button Actions : {Left Button:Left Click, Right Button:Right Click, Middle Button:Mouse Middle Button, Back Button:Mouse Back Button, Forward Button:Mouse Forward Button, Mouse Gesture Button:Gesture Button, Smart Shift:Smart Shift} Key/Button Diversion (saved): {Middle Button:Regular, Back Button:Regular, Forward Button:Regular, Mouse Gesture Button:Regular, Smart Shift:Regular} Key/Button Diversion : {Middle Button:Regular, Back Button:Regular, Forward Button:Regular, Mouse Gesture Button:Regular, Smart Shift:Regular} 9: ADJUSTABLE DPI {2201} Sensitivity (DPI) (saved): 1200 Sensitivity (DPI) : 1200 10: VERTICAL SCROLLING {2100} Roller type: 3G Ratchet per turn: 24 Scroll lines: 0 11: SMART SHIFT {2110} Scroll Wheel Rachet (saved): 8 Scroll Wheel Rachet : 8 12: HIRES WHEEL {2121} Multiplier: 8 Has invert: Normal wheel motion Has ratchet switch: Normal wheel mode High resolution mode HID notification Scroll Wheel Direction (saved): False Scroll Wheel Direction : False Scroll Wheel Resolution (saved): True Scroll Wheel Resolution : True Scroll Wheel Diversion (saved): False Scroll Wheel Diversion : False 13: GESTURE 2 {6501} Gestures (saved): {45: True, 46: True} Gestures : {46: True, 45: True} Gestures Diversion (saved): {46: False, 98: False, 211: False, 212: False} Gestures Diversion : {46: False, 98: False, 211: False, 212: False} 14: DFUCONTROL UNSIGNED {00C1} 15: unknown:1813 {1813} internal, hidden 16: unknown:1830 {1830} internal, hidden 17: unknown:1890 {1890} internal, hidden 18: unknown:18A1 {18A1} internal, hidden 19: unknown:18C0 {18C0} internal, hidden 20: unknown:1DF3 {1DF3} internal, hidden 21: unknown:1E00 {1E00} hidden 22: unknown:1EB0 {1EB0} internal, hidden 23: unknown:1803 {1803} internal, hidden 24: unknown:1861 {1861} internal, hidden 25: unknown:9000 {9000} internal, hidden 26: unknown:9200 {9200} internal, hidden 27: unknown:9240 {9240} internal, hidden Has 7 reprogrammable keys: 0: Left Button , default: Left Click => Left Click mse, reprogrammable, pos:0, group:1, group mask:g1 reporting: default 1: Right Button , default: Right Click => Right Click mse, reprogrammable, pos:0, group:1, group mask:g1 reporting: default 2: Middle Button , default: Mouse Middle Button => Mouse Middle Button mse, reprogrammable, divertable, pos:0, group:2, group mask:g1,g2,g3 reporting: default 3: Back Button , default: Mouse Back Button => Mouse Back Button mse, reprogrammable, divertable, pos:0, group:2, group mask:g1,g2 reporting: default 4: Forward Button , default: Mouse Forward Button => Mouse Forward Button mse, reprogrammable, divertable, pos:0, group:2, group mask:g1,g2 reporting: default 5: Mouse Gesture Button , default: Gesture Button => Gesture Button mse, reprogrammable, divertable, pos:0, group:3, group mask:g1,g2,g3 reporting: default 6: Smart Shift , default: Smart Shift => Smart Shift mse, reprogrammable, divertable, pos:0, group:3, group mask:g1,g2,g3 reporting: default Has 5 gesture(s), 0 param(s) and 4 spec(s): Thumbwheel Enabled( 0): True Diverted:( 0) False NaturalScrolling Enabled( 1): True Diverted:(None) None Finger9 Enabled(None): None Diverted:( 1) False unknown:00D3 Enabled(None): None Diverted:( 2) False unknown:00D4 Enabled(None): None Diverted:( 3) False period unit Spec ( 3): 800 DVI field width Spec ( 1): 8 field widths Spec ( 2): 8 resolution Spec ( 4): 2048 Battery: 50%, discharging, next level 20%. Solaar-1.1.14/docs/devices/Wireless Mouse MX Master 4071.txt000066400000000000000000000151261473525213700231130ustar00rootroot00000000000000Solaar version 1.1.4 3: Wireless Mouse MX Master Device path : /dev/hidraw3 WPID : 4071 Codename : MX Master Kind : mouse Protocol : HID++ 4.5 Polling rate : 8 ms (125Hz) Serial number: D3596818 Model ID: B01E40710000 Unit ID: 9803B76B Bootloader: BOT 56.10.B0005 Firmware: MPM 12.10.B0005 Firmware: MPM 12.10.B0005 Other: The power switch is located on the base. Supports 31 HID++ 2.0 features: 0: ROOT {0000} 1: FEATURE SET {0001} 2: DEVICE FW VERSION {0003} Firmware: Bootloader BOT 56.10.B0005 40717622661101 Firmware: Firmware MPM 12.10.B0005 B01E7622661101 Firmware: Firmware MPM 12.10.B0005 40717622661101 Firmware: Other Unit ID: 9803B76B Model ID: B01E40710000 Transport IDs: {'btleid': 'B01E', 'wpid': '4071'} 3: DEVICE NAME {0005} Name: Wireless Mouse MX Master Kind: mouse 4: WIRELESS DEVICE STATUS {1D4B} 5: RESET {0020} 6: CRYPTO ID {0021} 7: BATTERY STATUS {1000} Battery: N/A, recharging, next level 0%. 8: CONFIG DEVICE PROPS {1806} internal, hidden 9: CHANGE HOST {1814} Change Host : 2:rachel 10: REPROG CONTROLS V4 {1B04} DPI Sliding Adjustment (saved): Off DPI Sliding Adjustment : Off Mouse Gestures (saved): Off Mouse Gestures : Off Key/Button Actions (saved): {Left Button:Left Click, Right Button:Right Click, Middle Button:Mouse Middle Button, Back Button:Mouse Back Button, Forward Button:Mouse Forward Button, Mouse Gesture Button:Gesture Button Navigation, Smart Shift:Smart Shift} Key/Button Actions : {Left Button:Left Click, Right Button:Right Click, Middle Button:Mouse Middle Button, Back Button:Mouse Back Button, Forward Button:Mouse Forward Button, Mouse Gesture Button:Gesture Button Navigation, Smart Shift:Smart Shift} Key/Button Diversion (saved): {Middle Button:Regular, Back Button:Regular, Forward Button:Regular, Mouse Gesture Button:Regular, Smart Shift:Regular} Key/Button Diversion : {Middle Button:Regular, Back Button:Regular, Forward Button:Regular, Mouse Gesture Button:Regular, Smart Shift:Regular} 11: ADJUSTABLE DPI {2201} Sensitivity (DPI) (saved): 1000 Sensitivity (DPI) : 1000 12: VERTICAL SCROLLING {2100} Roller type: 3G Ratchet per turn: 24 Scroll lines: 0 13: SMART SHIFT {2110} Scroll Wheel Rachet (saved): 10 Scroll Wheel Rachet : 10 14: HIRES WHEEL {2121} Multiplier: 8 Has invert: Normal wheel motion Has ratchet switch: Normal wheel mode High resolution mode HID notification Scroll Wheel Direction (saved): False Scroll Wheel Direction : False Scroll Wheel Resolution (saved): True Scroll Wheel Resolution : True Scroll Wheel Diversion (saved): False Scroll Wheel Diversion : False 15: GESTURE 2 {6501} Gestures (saved): {45: True, 46: True} Gestures : {46: True, 45: True} Gestures Diversion (saved): {46: False} Gestures Diversion : {46: False} 16: DFUCONTROL SIGNED {00C2} 17: unknown:1813 {1813} internal, hidden 18: unknown:1830 {1830} internal, hidden 19: unknown:1890 {1890} internal, hidden 20: unknown:1891 {1891} internal, hidden 21: unknown:18A1 {18A1} internal, hidden 22: unknown:18C0 {18C0} internal, hidden 23: unknown:1DF3 {1DF3} internal, hidden 24: unknown:1E00 {1E00} hidden 25: unknown:1EB0 {1EB0} internal, hidden 26: unknown:1803 {1803} internal, hidden 27: unknown:1861 {1861} internal, hidden 28: unknown:9001 {9001} internal, hidden 29: unknown:9200 {9200} internal, hidden 30: unknown:9202 {9202} internal, hidden Has 8 reprogrammable keys: 0: Left Button , default: Left Click => Left Click mse, pos:0, group:1, group mask:g1 reporting: default 1: Right Button , default: Right Click => Right Click mse, pos:0, group:1, group mask:g1 reporting: default 2: Middle Button , default: Mouse Middle Button => Mouse Middle Button mse, reprogrammable, divertable, raw XY, pos:0, group:3, group mask:g1,g2,g3 reporting: default 3: Back Button , default: Mouse Back Button => Mouse Back Button mse, reprogrammable, divertable, raw XY, pos:0, group:2, group mask:g1,g2 reporting: default 4: Forward Button , default: Mouse Forward Button => Mouse Forward Button mse, reprogrammable, divertable, raw XY, pos:0, group:2, group mask:g1,g2 reporting: default 5: Mouse Gesture Button , default: Gesture Button Navigation => Gesture Button Navigation mse, reprogrammable, divertable, raw XY, pos:0, group:3, group mask:g1,g2,g3 reporting: default 6: Smart Shift , default: Smart Shift => Smart Shift mse, reprogrammable, divertable, raw XY, pos:0, group:3, group mask:g1,g2,g3 reporting: default 7: Virtual Gesture Button , default: Virtual Gesture Button => Virtual Gesture Button divertable, virtual, raw XY, force raw XY, pos:0, group:4, group mask:empty reporting: default Has 2 gesture(s), 0 param(s) and 4 spec(s): Thumbwheel Enabled( 0): True Diverted:( 0) False NaturalScrolling Enabled( 1): True Diverted:(None) None period unit Spec ( 3): 800 DVI field width Spec ( 1): 8 field widths Spec ( 2): 8 resolution Spec ( 4): 2048 Battery: N/A, recharging, next level 0%. Solaar-1.1.14/docs/devices/Wireless Mouse MX Master B012.txt000066400000000000000000000143631473525213700231260ustar00rootroot00000000000000Solaar version 1.1.3 1: Wireless Mouse MX Master Device path : /dev/hidraw4 USB id : 046d:B012 Codename : MX Master Kind : mouse Protocol : HID++ 4.5 Serial number: Model ID: B01240410000 Unit ID: 3E9C68EF Bootloader: BOT 18.01.B0014 Firmware: MPM 11.02.B0014 Firmware: MPM 11.02.B0014 Other: Supports 28 HID++ 2.0 features: 0: ROOT {0000} 1: FEATURE SET {0001} 2: DEVICE FW VERSION {0003} Firmware: Bootloader BOT 18.01.B0014 40410862AB80 Firmware: Firmware MPM 11.02.B0014 B0120862AB80 Firmware: Firmware MPM 11.02.B0014 40410862AB80 Firmware: Other Unit ID: 3E9C68EF Model ID: B01240410000 Transport IDs: {'btleid': 'B012', 'wpid': '4041'} 3: DEVICE NAME {0005} Name: Wireless Mouse MX Master Kind: mouse 4: WIRELESS DEVICE STATUS {1D4B} 5: RESET {0020} 6: BATTERY STATUS {1000} Battery: 50%, discharging, next level 20%. 7: CHANGE HOST {1814} Change Host : 1:heimdall 8: REPROG CONTROLS V4 {1B04} Key/Button Actions (saved): {Left Button:Left Click, Right Button:Right Click, Middle Button:Mouse Middle Button, Back Button:Mouse Back Button, Forward Button:Mouse Forward Button, Mouse Gesture Button:Gesture Button, Smart Shift:Smart Shift} Key/Button Actions : {Left Button:Left Click, Right Button:Right Click, Middle Button:Mouse Middle Button, Back Button:Mouse Back Button, Forward Button:Mouse Forward Button, Mouse Gesture Button:Gesture Button, Smart Shift:Smart Shift} Key/Button Diversion (saved): {Middle Button:Regular, Back Button:Regular, Forward Button:Regular, Mouse Gesture Button:Diverted, Smart Shift:Diverted} Key/Button Diversion : {Middle Button:Regular, Back Button:Regular, Forward Button:Regular, Mouse Gesture Button:Diverted, Smart Shift:Diverted} 9: ADJUSTABLE DPI {2201} Sensitivity (DPI) (saved): 1000 Sensitivity (DPI) : 1000 10: VERTICAL SCROLLING {2100} Roller type: 3G Ratchet per turn: 24 Scroll lines: 0 11: SMART SHIFT {2110} Scroll Wheel Rachet (saved): 35 Scroll Wheel Rachet : 35 12: HIRES WHEEL {2121} Multiplier: 8 Has invert: Normal wheel motion Has ratchet switch: Normal wheel mode High resolution mode HID notification Scroll Wheel Direction (saved): False Scroll Wheel Direction : False Scroll Wheel Resolution (saved): True Scroll Wheel Resolution : True Scroll Wheel Diversion (saved): False Scroll Wheel Diversion : False 13: GESTURE 2 {6501} Gestures (saved): {45: True, 46: True} Gestures : {46: True, 45: True} Gestures Diversion (saved): {46: False, 98: False, 211: False, 212: False} Gestures Diversion : {46: False, 98: False, 211: False, 212: False} 14: unknown:1813 {1813} internal, hidden 15: unknown:1830 {1830} internal, hidden 16: unknown:1890 {1890} internal, hidden 17: unknown:18A1 {18A1} internal, hidden 18: unknown:18C0 {18C0} internal, hidden 19: unknown:1DF3 {1DF3} internal, hidden 20: unknown:1E00 {1E00} hidden 21: unknown:1EB0 {1EB0} internal, hidden 22: unknown:1803 {1803} internal, hidden 23: unknown:1861 {1861} internal, hidden 24: unknown:9000 {9000} internal, hidden 25: unknown:9200 {9200} internal, hidden 26: unknown:9240 {9240} internal, hidden 27: OOBSTATE {1805} internal, hidden Has 7 reprogrammable keys: 0: Left Button , default: Left Click => Left Click mse, reprogrammable, pos:0, group:1, group mask:g1 reporting: default 1: Right Button , default: Right Click => Right Click mse, reprogrammable, pos:0, group:1, group mask:g1 reporting: default 2: Middle Button , default: Mouse Middle Button => Mouse Middle Button mse, reprogrammable, divertable, pos:0, group:2, group mask:g1,g2,g3 reporting: default 3: Back Button , default: Mouse Back Button => Mouse Back Button mse, reprogrammable, divertable, pos:0, group:2, group mask:g1,g2 reporting: default 4: Forward Button , default: Mouse Forward Button => Mouse Forward Button mse, reprogrammable, divertable, pos:0, group:2, group mask:g1,g2 reporting: default 5: Mouse Gesture Button , default: Gesture Button => Gesture Button mse, reprogrammable, divertable, pos:0, group:3, group mask:g1,g2,g3 reporting: diverted 6: Smart Shift , default: Smart Shift => Smart Shift mse, reprogrammable, divertable, pos:0, group:3, group mask:g1,g2,g3 reporting: diverted Has 5 gesture(s), 0 param(s) and 4 spec(s): Thumbwheel Enabled( 0): True Diverted:( 0) False NaturalScrolling Enabled( 1): True Diverted:(None) None Finger9 Enabled(None): None Diverted:( 1) False unknown:00D3 Enabled(None): None Diverted:( 2) False unknown:00D4 Enabled(None): None Diverted:( 3) False period unit Spec ( 3): 800 DVI field width Spec ( 1): 8 field widths Spec ( 2): 8 resolution Spec ( 4): 2048 Battery: 50%, discharging, next level 20%. Solaar-1.1.14/docs/devices/Wireless Mouse Pebble M350 4080.txt000066400000000000000000000074271473525213700231560ustar00rootroot00000000000000Solaar version 1.1.4 2: Wireless Mouse Pebble M350 Device path : /dev/hidraw2 WPID : 4080 Codename : Pebble Kind : mouse Protocol : HID++ 4.5 Polling rate : 8 ms (125Hz) Serial number: 2B442F40 Model ID: B02140800000 Unit ID: DE12CCD9 Firmware: MPM 18.00.B0007 Other: The power switch is located on the base. Supports 26 HID++ 2.0 features: 0: ROOT {0000} V0 1: FEATURE SET {0001} V0 2: DEVICE FW VERSION {0003} V2 Firmware: Firmware MPM 18.00.B0007 4080BEA0D686 Firmware: Other Unit ID: DE12CCD9 Model ID: B02140800000 Transport IDs: {'btleid': 'B021', 'wpid': '4080'} 3: DEVICE NAME {0005} V0 Name: M350 Wireless Mouse Kind: mouse 4: WIRELESS DEVICE STATUS {1D4B} V0 5: RESET {0020} V0 6: DEVICE FRIENDLY NAME {0007} V0 Friendly Name: Logitech Pebble 7: BATTERY STATUS {1000} V1 Battery: 50%, discharging, next level 20%. 8: REPROG CONTROLS V4 {1B04} V4 Key/Button Actions (saved): {Left Button:Left Click, Right Button:Right Click, Middle Button:Mouse Middle Button} Key/Button Actions : {Left Button:Left Click, Right Button:Right Click, Middle Button:Mouse Middle Button} Key/Button Diversion (saved): {Middle Button:Regular, ?} Key/Button Diversion : {Middle Button:Regular} 9: CHANGE HOST {1814} V1 Change Host : 1:brandy 10: ADJUSTABLE DPI {2201} V1 Sensitivity (DPI) (saved): 1000 Sensitivity (DPI) : 1000 11: LOWRES WHEEL {2130} V0 Wheel Reports: HID Scroll Wheel Diversion (saved): False Scroll Wheel Diversion : False 12: DEVICE RESET {1802} V0 internal, hidden 13: unknown:1803 {1803} V0 internal, hidden 14: CONFIG DEVICE PROPS {1806} V5 internal, hidden 15: unknown:1812 {1812} V0 internal, hidden 16: OOBSTATE {1805} V0 internal, hidden 17: unknown:1830 {1830} V0 internal, hidden 18: unknown:1890 {1890} V4 internal, hidden 19: unknown:1891 {1891} V4 internal, hidden 20: unknown:18A1 {18A1} V0 internal, hidden 21: unknown:1DF3 {1DF3} V0 internal, hidden 22: unknown:1E00 {1E00} V0 hidden 23: unknown:1EB0 {1EB0} V0 internal, hidden 24: unknown:1861 {1861} V0 internal, hidden 25: unknown:18B1 {18B1} V0 internal, hidden Has 4 reprogrammable keys: 0: Left Button , default: Left Click => Left Click mse, reprogrammable, analytics key events, pos:0, group:1, group mask:g1 reporting: default 1: Right Button , default: Right Click => Right Click mse, reprogrammable, analytics key events, pos:0, group:1, group mask:g1 reporting: default 2: Middle Button , default: Mouse Middle Button => Mouse Middle Button mse, reprogrammable, divertable, raw XY, analytics key events, pos:0, group:2, group mask:g1,g2 reporting: default 3: Virtual Gesture Button , default: Virtual Gesture Button => Virtual Gesture Button divertable, virtual, raw XY, force raw XY, pos:0, group:3, group mask:empty reporting: default Battery: 50%, discharging, next level 20%. Solaar-1.1.14/docs/devices/Wireless Multi-Device Keyboard K780 405B.txt000066400000000000000000000232221473525213700247370ustar00rootroot00000000000000Solaar version 1.1.7 1: Wireless Multi-Device Keyboard K780 Device path : /dev/hidraw4 WPID : 405B Codename : K780 Kind : keyboard Protocol : HID++ 4.5 Polling rate : 20 ms (50Hz) Serial number: 3C60AFA4 Model ID: B33B405B0000 Unit ID: 6CAF50FF Bootloader: BOT 25.01.B0006 Firmware: MPK 01.04.B0028 Other: The power switch is located on the edge of top right corner. Supports 31 HID++ 2.0 features: 0: ROOT {0000} V0 1: FEATURE SET {0001} V0 2: DEVICE FW VERSION {0003} V2 Firmware: Bootloader BOT 25.01.B0006 0000C9839F9301 Firmware: Firmware MPK 01.04.B0028 405BC9839F9301 Firmware: Other Unit ID: 6CAF50FF Model ID: B33B405B0000 Transport IDs: {'btleid': 'B33B', 'wpid': '405B'} 3: DEVICE NAME {0005} V0 Name: K780 Multi-Device Wireless Keyboard Kind: keyboard 4: WIRELESS DEVICE STATUS {1D4B} V0 5: RESET {0020} V0 6: DEVICE FRIENDLY NAME {0007} V0 Friendly Name: Keyboard K780 7: BATTERY STATUS {1000} V0 Battery: 90%, discharging, next level 50%. 8: CHANGE HOST {1814} V1 Cambiar Host : 1:romanescu 9: HOSTS INFO {1815} V1 Host 0 (paired): romanescu Host 1 (paired): espinaca Host 2 (unpaired): 10: REPROG CONTROLS V4 {1B04} V3 Desvio de Tecla/Botón (saved): {Volume Up:Normal, Volume Down:Normal, Mute:Normal, Play/Pause:Normal, Next:Normal, Previous:Normal, Multiplatform Back:Normal, Multiplatform Insert:Normal, Screen Capture/Print Screen:Normal, Fn Down:Normal, Fn Up:Normal, Multiplatform Lock:Normal, Host Switch Channel 1:Normal, Host Switch Channel 2:Normal, Host Switch Channel 3:Normal, MultiPlatform Search:Normal, MultiPlatform Home/Mission Control:Normal, MultiPlatform Menu/Show/Hide Virtual Keyboard/Launchpad:Normal} Desvio de Tecla/Botón : {Volume Up:Normal, Volume Down:Normal, Mute:Normal, Play/Pause:Normal, Next:Normal, Previous:Normal, Multiplatform Back:Normal, Multiplatform Insert:Normal, Screen Capture/Print Screen:Normal, Fn Down:Normal, Fn Up:Normal, Multiplatform Lock:Normal, Host Switch Channel 1:Normal, Host Switch Channel 2:Normal, Host Switch Channel 3:Normal, MultiPlatform Search:Normal, MultiPlatform Home/Mission Control:Normal, MultiPlatform Menu/Show/Hide Virtual Keyboard/Launchpad:Normal} 11: PERSISTENT REMAPPABLE ACTION {1C00} V0 Persistent Key/Button Mapping : {Volume Up:Volume Up, Volume Down:Volume Down, Mute:Mute, Play/Pause:Play/Pause, Next:Scan Next Track, Previous:Scan Previous Track, Multiplatform Back:AC Back, MultiPlatform Search:AC Search, MultiPlatform Home/Mission Control:AC Home, MultiPlatform Menu/Show/Hide Virtual Keyboard/Launchpad:COMPOSE} 12: NEW FN INVERSION {40A2} V0 Fn-swap: enabled Fn-swap default: enabled Intercambiar función Fx (saved): True Intercambiar función Fx : True 13: ENCRYPTION {4100} V0 14: KEYBOARD DISABLE KEYS {4521} V0 Desactivar teclas (saved): {Caps Lock:False, Num Lock:False, Scroll Lock:False, Insert:False, Win:False} Desactivar teclas : {Caps Lock:False, Num Lock:False, Scroll Lock:False, Insert:False, Win:False} 15: MULTIPLATFORM {4531} V0 Especificar SO (saved): Windows Especificar SO : Windows 16: LOCK KEY STATE {4220} V0 17: DFUCONTROL SIGNED {00C2} V0 18: unknown:1803 {1803} V0 internal, hidden 19: CONFIG DEVICE PROPS {1806} V0 internal, hidden 20: OOBSTATE {1805} V0 internal, hidden 21: unknown:1813 {1813} V0 internal, hidden 22: unknown:1830 {1830} V0 internal, hidden 23: unknown:1861 {1861} V0 internal, hidden 24: unknown:1890 {1890} V0 internal, hidden 25: unknown:1891 {1891} V0 internal, hidden 26: unknown:18A1 {18A1} V0 internal, hidden 27: unknown:1DF3 {1DF3} V0 internal, hidden 28: unknown:1E00 {1E00} V0 hidden 29: unknown:1EB0 {1EB0} V0 internal, hidden 30: unknown:18B0 {18B0} V0 internal, hidden Has 19 reprogrammable keys: 0: Host Switch Channel 1 , default: HostSwitch Channel 1 => HostSwitch Channel 1 is FN, FN sensitive, divertable, persistently divertable, pos:1, group:0, group mask:empty reporting: default 1: Host Switch Channel 2 , default: HostSwitch Channel 2 => HostSwitch Channel 2 is FN, FN sensitive, divertable, persistently divertable, pos:2, group:0, group mask:empty reporting: default 2: Host Switch Channel 3 , default: HostSwitch Channel 3 => HostSwitch Channel 3 is FN, FN sensitive, divertable, persistently divertable, pos:3, group:0, group mask:empty reporting: default 3: MultiPlatform Home/Mission Control, default: Multiplatform Home/Mission Control => Multiplatform Home/Mission Control is FN, FN sensitive, reprogrammable, divertable, persistently divertable, pos:4, group:0, group mask:empty reporting: default 4: MultiPlatform Menu/Show/Hide Virtual Keyboard/Launchpad, default: Multiplatform Menu/Launchpad => Multiplatform Menu/Launchpad is FN, FN sensitive, reprogrammable, divertable, persistently divertable, pos:5, group:0, group mask:empty reporting: default 5: Multiplatform Back , default: MultiPlatform Back => MultiPlatform Back is FN, FN sensitive, reprogrammable, divertable, persistently divertable, pos:6, group:0, group mask:empty reporting: default 6: Previous , default: Previous => Previous is FN, FN sensitive, divertable, persistently divertable, pos:7, group:0, group mask:empty reporting: default 7: Play/Pause , default: Play/Pause => Play/Pause is FN, FN sensitive, divertable, persistently divertable, pos:8, group:0, group mask:empty reporting: default 8: Next , default: Next => Next is FN, FN sensitive, divertable, persistently divertable, pos:9, group:0, group mask:empty reporting: default 9: Mute , default: Mute => Mute is FN, FN sensitive, divertable, persistently divertable, pos:10, group:0, group mask:empty reporting: default 10: Volume Down , default: Volume Down => Volume Down is FN, FN sensitive, divertable, persistently divertable, pos:11, group:0, group mask:empty reporting: default 11: Volume Up , default: Volume Up => Volume Up is FN, FN sensitive, divertable, persistently divertable, pos:12, group:0, group mask:empty reporting: default 12: MultiPlatform Search , default: Multiplatform Search => Multiplatform Search nonstandard, reprogrammable, divertable, persistently divertable, pos:0, group:0, group mask:empty reporting: default 13: Multiplatform Insert , default: Switch Language => Switch Language is FN, reprogrammable, divertable, persistently divertable, pos:0, group:0, group mask:empty reporting: default 14: Screen Capture/Print Screen, default: Screen Capture => Screen Capture is FN, reprogrammable, divertable, persistently divertable, pos:0, group:0, group mask:empty reporting: default 15: Multiplatform Lock , default: WindowsLock => WindowsLock is FN, reprogrammable, divertable, persistently divertable, pos:0, group:0, group mask:empty reporting: default 16: Fn Up , default: unknown:0070 => unknown:0070 is FN, divertable, persistently divertable, pos:0, group:0, group mask:empty reporting: default 17: Fn Down , default: unknown:006F => unknown:006F is FN, divertable, persistently divertable, pos:0, group:0, group mask:empty reporting: default 18: FN Key , default: Do Nothing One => Do Nothing One nonstandard, pos:0, group:0, group mask:empty reporting: default Has 10 persistent remappable keys: 0: MultiPlatform Home/Mission Control => Consumer: AC Home 1: MultiPlatform Menu/Show/Hide Virtual Keyboard/Launchpad => Key: COMPOSE 2: Multiplatform Back => Consumer: AC Back 3: Previous => Consumer: Scan Previous Track 4: Play/Pause => Consumer: Play/Pause 5: Next => Consumer: Scan Next Track 6: Mute => Consumer: Mute 7: Volume Down => Consumer: Volume Down 8: Volume Up => Consumer: Volume Up 9: MultiPlatform Search => Consumer: AC Search Battery: 90%, discharging, next level 50%. Solaar-1.1.14/docs/devices/Wireless Rechargeable Touchpad T650 4101.txt000066400000000000000000000033631473525213700250070ustar00rootroot00000000000000 1: Wireless Rechargeable Touchpad T650 Codename : T650 Kind : touchpad Wireless PID : 4101 Protocol : HID++ 2.0 Polling rate : 8 ms (125Hz) Serial number: 22205A4D Firmware: RQM 41.01.B0037 Bootloader: BL 03.00 Hardware: 72 Other: The power switch is located on the base. Supports 23 HID++ 2.0 features: 0: ROOT {0000} 1: FEATURE SET {0001} 2: FEATURE INFO {0002} 3: DEVICE FW VERSION {0003} 4: DEVICE NAME {0005} 5: BATTERY STATUS {1000} 6: WIRELESS DEVICE STATUS {1D4B} 7: unknown:1DF3 {1DF3} internal, hidden 8: REPROG CONTROLS {1B00} 9: unknown:1F03 {1F03} internal, hidden 10: VERTICAL SCROLLING {2100} 11: HI RES SCROLLING {2120} 12: MOUSE POINTER {2200} 13: DFUCONTROL {00C0} 14: unknown:1E80 {1E80} internal, hidden 15: TOUCHPAD RAW XY {6100} 16: unknown:1860 {1860} internal, hidden 17: unknown:1E00 {1E00} hidden 18: REPROG CONTROLS V2 {1B01} 19: unknown:1890 {1890} internal, hidden 20: unknown:18E5 {18E5} internal, hidden 21: unknown:18A0 {18A0} internal, hidden 22: unknown:1830 {1830} internal, hidden Has 2 reprogrammable keys: 0: LEFT CLICK => LeftClick mse, reprogrammable 1: RIGHT CLICK => RightClick mse, reprogrammable Battery: 50%, discharging. Solaar-1.1.14/docs/devices/Wireless Solar Keyboard K750 4002.txt000066400000000000000000000041051473525213700234770ustar00rootroot00000000000000 2: Wireless Solar Keyboard K750 Codename : K750 Kind : keyboard Wireless PID : 4002 Protocol : HID++ 2.0 Polling rate : 20 ms (50Hz) Serial number: 5692B2EC Firmware: RQK 33.00.B0015 Bootloader: DFU 00.02.B0003 The power switch is located on the edge of top right corner. Supports 11 HID++ 2.0 features: 0: ROOT {0000} 1: FEATURE SET {0001} 2: DEVICE FW VERSION {0003} 3: DEVICE NAME {0005} 4: REPROG CONTROLS {1B00} 5: WIRELESS DEVICE STATUS {1D4B} 6: unknown:1DF3 {1DF3} hidden 7: FN INVERSION {40A0} 8: ENCRYPTION {4100} 9: SOLAR DASHBOARD {4301} 10: KEYBOARD LAYOUT {4520} Has 12 reprogrammable keys: 0: MY HOME => HomePage FN sensitive, is FN, reprogrammable 1: Mail => Email FN sensitive, is FN, reprogrammable 2: SEARCH => Search FN sensitive, is FN, reprogrammable 3: Calculator => Calculator FN sensitive, is FN, reprogrammable 4: MEDIA PLAYER => Music FN sensitive, is FN, reprogrammable 5: Previous => Previous FN sensitive, is FN 6: Play/Pause => Play/Pause FN sensitive, is FN 7: Next => Next FN sensitive, is FN 8: Mute => Mute FN sensitive, is FN 9: Volume Down => Volume Down FN sensitive, is FN 10: Volume Up => Volume Up FN sensitive, is FN 11: SLEEP => Sleep FN sensitive, is FN, reprogrammable Battery status unavailable. Solaar-1.1.14/docs/devices/Wireless Touch Keyboard K400 4024.txt000066400000000000000000000055631473525213700235060ustar00rootroot00000000000000 1: Wireless Touch Keyboard K400 Codename : K400 Kind : keyboard Wireless PID : 4024 Protocol : HID++ 2.0 Polling rate : 8 ms (125Hz) Serial number: AC5F5069 Firmware: RQK 50.01.B0013 The power switch is located on the edge of top right corner. Supports 22 HID++ 2.0 features: 0: ROOT {0000} 1: FEATURE SET {0001} 2: FEATURE INFO {0002} 3: DEVICE FW VERSION {0003} 4: DEVICE NAME {0005} 5: BATTERY STATUS {1000} 6: REPROG CONTROLS {1B00} 7: REPROG CONTROLS V3 {1B03} 8: WIRELESS DEVICE STATUS {1D4B} 9: unknown:1DF0 {1DF0} hidden 10: unknown:1DF3 {1DF3} internal, hidden 11: unknown:1E00 {1E00} hidden 12: VERTICAL SCROLLING {2100} 13: HI RES SCROLLING {2120} 14: MOUSE POINTER {2200} 15: FN INVERSION {40A0} 16: NEW FN INVERSION {40A2} 17: ENCRYPTION {4100} 18: KEYBOARD LAYOUT {4520} 19: TOUCHPAD FW ITEMS {6010} 20: TOUCHPAD SW ITEMS {6011} 21: TOUCHPAD WIN8 FW ITEMS {6012} Has 18 reprogrammable keys: 0: SHOW DESKTOP => ShowDesktop is FN, reprogrammable 1: METRO APPSWITCH => MetroAppSwitch is FN, reprogrammable 2: METRO SEARCH => MetroSearch is FN, reprogrammable 3: METRO SHARE => MetroShare is FN, reprogrammable 4: METRO DEVICES => MetroDevices is FN, reprogrammable 5: METRO SETTINGS => MetroSettings is FN, reprogrammable 6: Previous => Previous is FN 7: Play/Pause => Play/Pause is FN 8: Next => Next is FN 9: Mute => Mute is FN 10: Volume Down => Volume Down is FN 11: Volume Up => Volume Up is FN 12: MEDIA PLAYER => Music nonstandard, reprogrammable 13: Lock PC => WindowsLock nonstandard, reprogrammable 14: SLEEP => Sleep nonstandard, reprogrammable 15: LEFT CLICK => LeftClick mse 16: LEFT CLICK => LeftClick mse 17: RIGHT CLICK => RightClick mse Battery: 90%, discharging. Solaar-1.1.14/docs/devices/Wireless Touch Keyboard K400 Plus 404D.txt000066400000000000000000000230441473525213700244260ustar00rootroot00000000000000Solaar version 1.1.4 1: Wireless Touch Keyboard K400 Plus Device path : /dev/hidraw2 WPID : 404D Codename : K400 Plus Kind : keyboard Protocol : HID++ 4.1 Polling rate : 8 ms (125Hz) Serial number: 6DC5C81C Model ID: 404D00000000 Unit ID: 641487CF Bootloader: BOT 22.02.B0002 Firmware: RQK 63.02.B0016 Other: The power switch is located on the top edge. Supports 23 HID++ 2.0 features: 0: ROOT {0000} 1: FEATURE SET {0001} 2: DEVICE FW VERSION {0003} Firmware: Bootloader BOT 22.02.B0002 404D6217AD40 Firmware: Firmware RQK 63.02.B0016 404D6217AD40 Firmware: Other Unit ID: 641487CF Model ID: 404D00000000 Transport IDs: {'wpid': '404D'} 3: DEVICE NAME {0005} Name: Wireless Touch Keyboard K400 Plus Kind: keyboard 4: WIRELESS DEVICE STATUS {1D4B} 5: RESET {0020} 6: BATTERY STATUS {1000} Battery: 90%, discharging, next level 50%. 7: REPROG CONTROLS V4 {1B04} Key/Button Diversion (saved): {Volume Up:Regular, Volume Down:Regular, Mute:Regular, Play/Pause:Regular, Next:Regular, Previous:Regular, Application Switcher:Regular, My Home:Regular, Media Player:Regular, Search:Regular, Sleep:Regular, Contextual Menu:Regular, Back:Regular, Show Desktop:Regular, Maximize Window:Regular, Switch Screen:Regular, Fn Left Click:Regular, Second Left Click:Regular} Key/Button Diversion : {Volume Up:Regular, Volume Down:Regular, Mute:Regular, Play/Pause:Regular, Next:Regular, Previous:Regular, Application Switcher:Regular, My Home:Regular, Media Player:Regular, Search:Regular, Sleep:Regular, Contextual Menu:Regular, Back:Regular, Show Desktop:Regular, Maximize Window:Regular, Switch Screen:Regular, Fn Left Click:Regular, Second Left Click:Regular} 8: SWAP BUTTON CANCEL {2005} 9: NEW FN INVERSION {40A2} Fn-swap: enabled Fn-swap default: enabled Swap Fx function (saved): True Swap Fx function : True 10: ENCRYPTION {4100} 11: KEYBOARD DISABLE KEYS {4521} Disable keys (saved): {Caps Lock:False, Scroll Lock:False, Insert:False, Win:False} Disable keys : {Caps Lock:False, Scroll Lock:False, Insert:False, Win:False} 12: TOUCHPAD RAW XY {6100} 13: GESTURE 2 {6501} Gestures (saved): {1: True, 2: True, 10: True, 30: True, 34: False, 42: True, 43: True, 45: False, 64: False, 65: False, 67: False, 84: True} Gestures : {1: True, 2: True, 30: True, 10: True, 45: False, 42: True, 43: True, 64: False, 65: False, 67: False, 84: True, 34: False} Gestures Diversion (saved): {1: False, 2: False, 10: False, 44: False, 64: False, 65: False, 67: False, 84: False, 85: False, 100: False} Gestures Diversion : {1: False, 2: False, 10: False, 44: False, 64: False, 65: False, 67: False, 84: False, 85: False, 100: False} Gesture params (saved): {4: {'scale': 256}} Gesture params : {4: {'scale': 256}} 14: DFUCONTROL UNSIGNED {00C1} 15: unknown:1811 {1811} internal, hidden 16: unknown:1830 {1830} internal, hidden 17: unknown:1890 {1890} internal, hidden 18: unknown:1DF3 {1DF3} internal, hidden 19: unknown:1E00 {1E00} hidden 20: unknown:1EB0 {1EB0} internal, hidden 21: unknown:1861 {1861} internal, hidden 22: unknown:18B0 {18B0} internal, hidden Has 20 reprogrammable keys: 0: Second Left Click , default: Left Click => Left Click mse, divertable, pos:0, group:0, group mask:empty reporting: default 1: Back , default: Mouse Back Button => Mouse Back Button is FN, FN sensitive, reprogrammable, divertable, pos:1, group:0, group mask:empty reporting: default 2: My Home , default: HomePage => HomePage is FN, FN sensitive, reprogrammable, divertable, pos:2, group:0, group mask:empty reporting: default 3: Application Switcher , default: Application Switcher => Application Switcher is FN, FN sensitive, reprogrammable, divertable, pos:3, group:0, group mask:empty reporting: default 4: Contextual Menu , default: Right Click => Right Click is FN, FN sensitive, reprogrammable, divertable, pos:4, group:0, group mask:empty reporting: default 5: Search , default: Search Files => Search Files is FN, FN sensitive, reprogrammable, divertable, pos:5, group:0, group mask:empty reporting: default 6: Show Desktop , default: Show Desktop => Show Desktop is FN, FN sensitive, reprogrammable, divertable, pos:6, group:0, group mask:empty reporting: default 7: Maximize Window , default: Maximize Window => Maximize Window is FN, FN sensitive, reprogrammable, divertable, pos:7, group:0, group mask:empty reporting: default 8: Switch Screen , default: Switch Presentation/Switch Screen => Switch Presentation/Switch Screen is FN, FN sensitive, reprogrammable, divertable, pos:8, group:0, group mask:empty reporting: default 9: Media Player , default: Music => Music is FN, FN sensitive, reprogrammable, divertable, pos:9, group:0, group mask:empty reporting: default 10: Previous , default: Previous => Previous is FN, FN sensitive, divertable, pos:10, group:0, group mask:empty reporting: default 11: Play/Pause , default: Play/Pause => Play/Pause is FN, FN sensitive, divertable, pos:11, group:0, group mask:empty reporting: default 12: Next , default: Next => Next is FN, FN sensitive, divertable, pos:12, group:0, group mask:empty reporting: default 13: Fn Left Click , default: ShowUI => ShowUI divertable, pos:0, group:0, group mask:empty reporting: default 14: Left Button , default: Left Click => Left Click mse, pos:0, group:0, group mask:empty reporting: default 15: Right Button , default: Right Click => Right Click mse, pos:0, group:0, group mask:empty reporting: default 16: Mute , default: Mute => Mute nonstandard, divertable, pos:0, group:0, group mask:empty reporting: default 17: Volume Up , default: Volume Up => Volume Up nonstandard, divertable, pos:0, group:0, group mask:empty reporting: default 18: Volume Down , default: Volume Down => Volume Down nonstandard, divertable, pos:0, group:0, group mask:empty reporting: default 19: Sleep , default: Sleep => Sleep reprogrammable, divertable, pos:0, group:0, group mask:empty reporting: default Has 17 gesture(s), 1 param(s) and 5 spec(s): Track1Finger Enabled(None): None Diverted:(None) None TapGestures Enabled(None): None Diverted:(None) None Tap1Finger Enabled( 0): True Diverted:( 0) False Tap2Finger Enabled( 1): True Diverted:( 1) False TapDrag1Finger Enabled( 2): True Diverted:(None) None DoubleTap1Finger Enabled( 3): True Diverted:( 2) False Scroll2FingerStateless Enabled(None): None Diverted:( 3) False NaturalScrolling Enabled( 4): False Diverted:(None) None Scroll2FingerHoriz Enabled( 5): True Diverted:(None) None Scroll2FingerVert Enabled( 6): True Diverted:(None) None LeftEdgeSwipe1Finger2 Enabled( 7): False Diverted:( 4) False RightEdgeSwipe1Finger2 Enabled( 8): False Diverted:( 5) False TopEdgeSwipe1Finger2 Enabled( 9): False Diverted:( 6) False Zoom2FingerStateless Enabled( 10): True Diverted:( 7) False FnClickGestureSuppression Enabled( 11): False Diverted:(None) None TwoFingersPresent Enabled(None): None Diverted:( 8) False DeviceSpecificRawData Enabled(None): None Diverted:( 9) False ScaleFactor Value ( 0): 256 [Default: 256] period unit Spec ( 3): 2048 DVI field width Spec ( 1): 8 field widths Spec ( 2): 8 multiplier Spec ( 5): None resolution Spec ( 4): 4 Battery: 90%, discharging, next level 50%. Solaar-1.1.14/docs/devices/Wireless Trackball M570 1028.txt000066400000000000000000000006271473525213700226110ustar00rootroot00000000000000 1: Wireless Trackball M570 Codename : M570 Kind : mouse Wireless PID : 1028 Protocol : HID++ 1.0 Polling rate : 8 ms (125Hz) Serial number: 891DC05A Firmware: 26.00.B0003 Bootloader: 02.06 Other: 00.01 The power switch is located on the base. Notifications: battery status (0x100000). Battery: 100%, discharging. Solaar-1.1.14/docs/devices/Zone Touch Mouse T400 4026.txt000066400000000000000000000047101473525213700221600ustar00rootroot00000000000000 1: Zone Touch Mouse T400 Codename : T400 Kind : mouse Wireless PID : 4026 Protocol : HID++ 2.0 Polling rate : 8 ms (125Hz) Serial number: 131A3093 Firmware: RQM 39.00.B0029 Bootloader: BL 03.00 Hardware: 72 Other: The power switch is located on the base. Supports 28 HID++ 2.0 features: 0: ROOT {0000} 1: FEATURE SET {0001} 2: FEATURE INFO {0002} 3: DEVICE FW VERSION {0003} 4: DEVICE NAME {0005} 5: DFUCONTROL {00C0} 6: BATTERY STATUS {1000} 7: unknown:1802 {1802} internal, hidden 8: unknown:1810 {1810} internal, hidden 9: unknown:1830 {1830} internal, hidden 10: unknown:1850 {1850} internal, hidden 11: unknown:1860 {1860} internal, hidden 12: unknown:1890 {1890} internal, hidden 13: unknown:18A0 {18A0} internal, hidden 14: unknown:18E3 {18E3} internal, hidden 15: REPROG CONTROLS {1B00} 16: WIRELESS DEVICE STATUS {1D4B} 17: unknown:1DF3 {1DF3} internal, hidden 18: unknown:1E00 {1E00} hidden 19: unknown:1E80 {1E80} internal, hidden 20: unknown:1F03 {1F03} internal, hidden 21: unknown:1F04 {1F04} internal, hidden 22: VERTICAL SCROLLING {2100} 23: unknown:2101 {2101} hidden 24: HI RES SCROLLING {2120} 25: MOUSE POINTER {2200} 26: TOUCHMOUSE RAW POINTS {6110} hidden 27: REPROG CONTROLS V3 {1B03} Has 7 reprogrammable keys: 0: LEFT CLICK => LeftClick mse, reprogrammable 1: RIGHT CLICK => RightClick mse, reprogrammable 2: MIDDLE BUTTON => MiddleMouseButton mse, reprogrammable 3: METRO START SCREEN => MetroStartScreen mse, reprogrammable 4: ZOOMIN => Do Nothing mse, reprogrammable 5: ZOOMOUT => Do Nothing mse, reprogrammable 6: BACK HSCROLL => TouchBackForwardHorzScroll Battery: 100%, discharging. Solaar-1.1.14/docs/devices/anywhere-mx.txt000066400000000000000000000054141473525213700204130ustar00rootroot00000000000000Receiver LZ301AR-DJ M/N:C-U0007 (ltunify) Serial number: D1759614 Firmware version: 012.001.00019 Bootloader version: BL.002.014 Supported notification flags: 00 09 00 - 01: Wireless Notifications - 08: Software Present Mouse (ltunify) HID++ version: 1.0 Device index 1 Mouse Name: Anywhere MX Wireless Product ID: 1017 Serial number: 13865F99 Firmware version: 016.001.00040 Bootloader version: BL.002.010 (solaar) Unifying Receiver Device path : /dev/hidraw2 USB id : 046d:c52b Serial : D1759614 Firmware : 12.01.B0019 Bootloader : 02.14 Has 1 paired device(s) out of a maximum of 6. Notifications: (none) Device activity counters: 1=19 1: Anywhere Mouse MX Codename : Anywhere MX Kind : mouse Wireless PID : 1017 Protocol : HID++ 1.0 Polling rate : 8 ms (125Hz) Serial number: 13865F99 Firmware: 16.01.B0040 Bootloader: 02.10 Other: 00.06 The power switch is located on the base. Notifications: (none). Battery: 100%, discharging. (scan-registers) # Old notification flags: 000000 # 00 - Enabled Notifications, supported flags: Battery Status (10) >> ( 0.792) [10 01 8100 100000] b'\x10\x01\x81\x00\x10\x00\x00' # 01 - scrolling settings? # Flags: # 0x40 - Enable Smooth Scrolling # 0x02 - "confuse KDE", see https://github.com/pwr/Solaar/issues/115 << ( 0.011) [10 01 8101 000000] b'\x10\x01\x81\x01\x00\x00\x00' >> ( 1.710) [10 01 8101 020000] b'\x10\x01\x81\x01\x02\x00\x00' # 0D - battery level. first byte is remaining charge in percent; second is # (guessed) maximum?; third is charge status (30=discharging) # "10 ix 0D 64 64 30 00" is a battery notification (when enabled) << ( 9.789) [10 01 810D 000000] b'\x10\x01\x81\r\x00\x00\x00' >> ( 9.816) [10 01 810D 646430] b'\x10\x01\x81\rdd0' # 63 - DPI (range 0x80-0x8e (inclusive)) << ( 75.521) [10 01 8163 000000] b'\x10\x01\x81c\x00\x00\x00' >> ( 75.550) [10 01 8163 890000] b'\x10\x01\x81c\x89\x00\x00' # D0 - ? << ( 163.118) [10 01 81D0 000000] b'\x10\x01\x81\xd0\x00\x00\x00' >> ( 163.148) [10 01 81D0 000000] b'\x10\x01\x81\xd0\x00\x00\x00' # D4 - ? << ( 166.034) [10 01 81D4 000000] b'\x10\x01\x81\xd4\x00\x00\x00' >> ( 166.063) [10 01 81D4 000008] b'\x10\x01\x81\xd4\x00\x00\x08' # F1 - firmware/bootloader version << ( 187.172) [10 01 81F1 000000] b'\x10\x01\x81\xf1\x00\x00\x00' >> ( 187.199) [10 01 8F81 F10300] b'\x10\x01\x8f\x81\xf1\x03\x00' # F3 - ? << ( 188.629) [10 01 81F3 000000] b'\x10\x01\x81\xf3\x00\x00\x00' >> ( 188.661) [10 01 81F3 000000] b'\x10\x01\x81\xf3\x00\x00\x00' # FD - ? << ( 195.715) [10 01 83FD 000000] b'\x10\x01\x83\xfd\x00\x00\x00' >> ( 195.746) [11 01 83FD 00000000000000000000000000000000] b'\x11\x01\x83\xfd\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' Solaar-1.1.14/docs/devices/mk700.txt000066400000000000000000000017471473525213700170120ustar00rootroot00000000000000# Enabled Notifications # 10 - battery status # 02 + 01 - remap FN keys (multimedia + power buttons) >> ( 1.412) [10 02 8100 130000] '\x10\x02\x81\x00\x13\x00\x00' << ( 0.011) [10 02 8101 000000] '\x10\x02\x81\x01\x00\x00\x00' >> ( 0.276) [10 02 8101 000000] '\x10\x02\x81\x01\x00\x00\x00' # Battery status << ( 6.033) [10 02 8107 000000] '\x10\x02\x81\x07\x00\x00\x00' >> ( 6.344) [10 02 8107 070000] '\x10\x02\x81\x07\x07\x00\x00' # FN status << ( 8.055) [10 02 8109 000000] '\x10\x02\x81\t\x00\x00\x00' >> ( 8.144) [10 02 8109 000000] '\x10\x02\x81\t\x00\x00\x00' # ? << ( 208.316) [10 02 81D0 000000] '\x10\x02\x81\xd0\x00\x00\x00' >> ( 208.353) [10 02 81D0 000000] '\x10\x02\x81\xd0\x00\x00\x00' # version info << ( 237.436) [10 02 81F1 000000] '\x10\x02\x81\xf1\x00\x00\x00' >> ( 237.744) [10 02 8F81 F10300] '\x10\x02\x8f\x81\xf1\x03\x00' # ? << ( 239.459) [10 02 81F3 000000] '\x10\x02\x81\xf3\x00\x00\x00' >> ( 239.766) [10 02 81F3 000000] '\x10\x02\x81\xf3\x00\x00\x00' Solaar-1.1.14/docs/devices/performance-mx.txt000066400000000000000000000027451473525213700210760ustar00rootroot00000000000000# Notifications (r1_bit0 = battery status?) << ( 0.113) [10 01 8100 000000] '\x10\x01\x81\x00\x00\x00\x00' >> ( 0.157) [10 01 8100 100000] '\x10\x01\x81\x00\x10\x00\x00' # ? << ( 1.050) [10 01 8101 000000] '\x10\x01\x81\x01\x00\x00\x00' >> ( 1.097) [10 01 8101 020000] '\x10\x01\x81\x01\x02\x00\x00' # battery (07 means full) << ( 7.335) [10 01 8107 000000] '\x10\x01\x81\x07\x00\x00\x00' >> ( 7.382) [10 01 8107 070000] '\x10\x01\x81\x07\x07\x00\x00' # Set LEDS - ab cd 00, where a/b/c/d values are 1=off, 2=on, 3=flash # a = lower led # b = red led # c = upper led # d = middle led # below: all leds are off << ( 86.592) [10 01 8151 000000] '\x10\x01\x81Q\x00\x00\x00' >> ( 86.639) [10 01 8151 111100] '\x10\x01\x81Q\x11\x11\x00' # DPI (values in range 0x81..0x8F; logical value: 100..1500) << ( 108.430) [10 01 8163 000000] '\x10\x01\x81c\x00\x00\x00' >> ( 108.477) [10 01 8163 890000] '\x10\x01\x81c\x89\x00\x00' # ? << ( 240.505) [10 01 81D0 000000] '\x10\x01\x81\xd0\x00\x00\x00' >> ( 240.550) [10 01 81D0 000000] '\x10\x01\x81\xd0\x00\x00\x00' # ? << ( 245.690) [10 01 81D4 000000] '\x10\x01\x81\xd4\x00\x00\x00' >> ( 245.737) [10 01 81D4 000012] '\x10\x01\x81\xd4\x00\x00\x12' # Firmware/bootloader version << ( 281.016) [10 01 81F1 000000] '\x10\x01\x81\xf1\x00\x00\x00' >> ( 282.177) [10 01 8F81 F10300] '\x10\x01\x8f\x81\xf1\x03\x00' # ? << ( 284.106) [10 01 81F3 000000] '\x10\x01\x81\xf3\x00\x00\x00' >> ( 284.153) [10 01 81F3 000000] '\x10\x01\x81\xf3\x00\x00\x00' Solaar-1.1.14/docs/features.md000066400000000000000000000316711473525213700161300ustar00rootroot00000000000000--- title: List of HID++ 2.0 features layout: page --- # List of HID++ 2.0 features ## Feature status See functions in `hidpp20.py` and `settings_templates.py` Feature | ID | Status | Notes ---------------------------------------|----------|:------------------:|------ `ROOT` | `0x0000` | Supported | System `FEATURE_SET` | `0x0001` | Supported | System `FEATURE_INFO` | `0x0002` | Supported | System `DEVICE_FW_VERSION` | `0x0003` | Supported | `get_firmware`, `get_ids`, read only `DEVICE_UNIT_ID` | `0x0004` | Unsupported | `DEVICE_NAME` | `0x0005` | Supported | `get_kind`, `get_name`, read only `DEVICE_GROUPS` | `0x0006` | Unsupported | `DEVICE_FRIENDLY_NAME` | `0x0007` | Supported | `get_friendly_name`, read only `KEEP_ALIVE` | `0x0008` | Unsupported | `RESET` | `0x0020` | Unsupported | aka “Config Change” `CRYPTO_ID` | `0x0021` | Unsupported | `TARGET_SOFTWARE` | `0x0030` | Unsupported | `WIRELESS_SIGNAL_STRENGTH` | `0x0080` | Unsupported | `DFUCONTROL_LEGACY` | `0x00C0` | Unsupported | `DFUCONTROL_UNSIGNED` | `0x00C1` | Unsupported | `DFUCONTROL_SIGNED` | `0x00C2` | Unsupported | `DFU` | `0x00D0` | Unsupported | `BATTERY_STATUS` | `0x1000` | Supported | `get_battery`, read only `BATTERY_VOLTAGE` | `0x1001` | Supported | `get_voltage`, read only `UNIFIED_BATTERY` | `0x1004` | Supported | `get_battery`, read only `CHARGING_CONTROL` | `0x1010` | Unsupported | `LED_CONTROL` | `0x1300` | Unsupported | `GENERIC_TEST` | `0x1800` | Unsupported | `DEVICE_RESET` | `0x1802` | Unsupported | `OOBSTATE` | `0x1805` | Unsupported | `CONFIG_DEVICE_PROPS` | `0x1806` | Unsupported | `CHANGE_HOST` | `0x1814` | Supported | `ChangeHost` `HOSTS_INFO` | `0x1815` | Partial Support | `get_host_names`, partial listing only `BACKLIGHT` | `0x1981` | Unsupported | `BACKLIGHT2` | `0x1982` | Supported | `Backlight2` `BACKLIGHT3` | `0x1983` | Unsupported | `PRESENTER_CONTROL` | `0x1A00` | Unsupported | `SENSOR_3D` | `0x1A01` | Unsupported | `REPROG_CONTROLS` | `0x1B00` | Unsupported | `REPROG_CONTROLS_V2` | `0x1B01` | Listing Only | `get_keys` `REPROG_CONTROLS_V2_2` | `0x1B02` | Unsupported | `REPROG_CONTROLS_V3` | `0x1B03` | Unsupported | `REPROG_CONTROLS_V4` | `0x1B04` | Partial Support | `ReprogrammableKeys`, `DivertKeys`, `MouseGesture`, `get_keys` `REPORT_HID_USAGE` | `0x1BC0` | Unsupported | `PERSISTENT_REMAPPABLE_ACTION` | `0x1C00` | Supported | `PersistentRemappableAction` `WIRELESS_DEVICE_STATUS` | `0x1D4B` | Read only | status reporting from device `REMAINING_PAIRING` | `0x1DF0` | Unsupported | `FIRMWARE_PROPERTIES` | `0x1F1F` | Unsupported | `ADC_MEASUREMENT` | `0x1F20` | Unsupported | `LEFT_RIGHT_SWAP` | `0x2001` | Unsupported | `SWAP_BUTTON_CANCEL` | `0x2005` | Unsupported | `POINTER_AXIS_ORIENTATION` | `0x2006` | Unsupported | `VERTICAL_SCROLLING` | `0x2100` | Supported | `get_vertical_scrolling_info`, read only `SMART_SHIFT` | `0x2110` | Supported | `SmartShift` `SMART_SHIFT_ENHANCED` | `0x2111` | Supported | `SmartShiftEnhanced` `HI_RES_SCROLLING` | `0x2120` | Supported | `HiResScroll`, `get_hi_res_scrolling_info` `HIRES_WHEEL` | `0x2121` | Supported | `HiresSmoothInvert`, `HiresSmoothResolution`, `get_hires_wheel` `LOWRES_WHEEL` | `0x2130` | Supported | `LowresSmoothScroll`, `get_lowres_wheel_status` `THUMB_WHEEL` | `0x2150` | Supported | `ThumbMode`, `ThumbInvert` `MOUSE_POINTER` | `0x2200` | Supported | `get_mouse_pointer_info`, read only `ADJUSTABLE_DPI` | `0x2201` | Supported | `AdjustableDpi`, `DpiSliding` `POINTER_SPEED` | `0x2205` | Supported | `PointerSpeed`, `SpeedChange`, `get_pointer_speed_info` `ANGLE_SNAPPING` | `0x2230` | Unsupported | `SURFACE_TUNING` | `0x2240` | Unsupported | `HYBRID_TRACKING` | `0x2400` | Unsupported | `FN_INVERSION` | `0x40A0` | Supported | `FnSwap` `NEW_FN_INVERSION` | `0x40A2` | Supported | `NewFnSwap`, `get_new_fn_inversion `K375S_FN_INVERSION` | `0x40A3` | Supported | `K375sFnSwap` `ENCRYPTION` | `0x4100` | Unsupported | `LOCK_KEY_STATE` | `0x4220` | Unsupported | `SOLAR_DASHBOARD` | `0x4301` | Unsupported | `KEYBOARD_LAYOUT` | `0x4520` | Unsupported | read only `KEYBOARD_DISABLE_KEYS` | `0x4521` | Supported | `DisableKeyboardKeys` `KEYBOARD_DISABLE_BY_USAGE` | `0x4522` | Unsupported | `DUALPLATFORM` | `0x4530` | Supported | `Dualplatform`, untested `MULTIPLATFORM` | `0x4531` | Supported | `Multiplatform` `KEYBOARD_LAYOUT_2` | `0x4540` | Unsupported | read only `CROWN` | `0x4600` | Supported | `DivertCrown`, `CrownSmooth` `TOUCHPAD_FW_ITEMS` | `0x6010` | Unsupported | `TOUCHPAD_SW_ITEMS` | `0x6011` | Unsupported | `TOUCHPAD_WIN8_FW_ITEMS` | `0x6012` | Unsupported | `TAP_ENABLE` | `0x6020` | Unsupported | `TAP_ENABLE_EXTENDED` | `0x6021` | Unsupported | `CURSOR_BALLISTIC` | `0x6030` | Unsupported | `TOUCHPAD_RESOLUTION` | `0x6040` | Unsupported | `TOUCHPAD_RAW_XY` | `0x6100` | Unsupported | `TOUCHMOUSE_RAW_POINTS` | `0x6110` | Unsupported | `TOUCHMOUSE_6120` | `0x6120` | Unsupported | `GESTURE` | `0x6500` | Unsupported | `GESTURE_2` | `0x6501` | Partial Support | `Gesture2Gestures`, `Gesture2Params` `GKEY` | `0x8010` | Partial Support | `DivertGkeys` `MKEYS` | `0x8020` | Unsupported | `MR` | `0x8030` | Unsupported | `BRIGHTNESS_CONTROL` | `0x8040` | Unsupported | `REPORT_RATE` | `0x8060` | Supported | `ReportRate` `COLOR_LED_EFFECTS` | `0x8070` | Unsupported | `RGB_EFFECTS` | `0X8071` | Unsupported | `PER_KEY_LIGHTING` | `0x8080` | Unsupported | `PER_KEY_LIGHTING_V2` | `0x8081` | Unsupported | `MODE_STATUS` | `0x8090` | Unsupported | `ONBOARD_PROFILES` | `0x8100` | Unsupported | `MOUSE_BUTTON_SPY` | `0x8110` | Unsupported | `LATENCY_MONITORING` | `0x8111` | Unsupported | `GAMING_ATTACHMENTS` | `0x8120` | Unsupported | `FORCE_FEEDBACK` | `0x8123` | Unsupported | `SIDETONE` | `0x8300` | Unsupported | `EQUALIZER` | `0x8310` | Unsupported | `HEADSET_OUT` | `0x8320` | Unsupported | A “read only” note means the feature is a read-only feature. ## Implementing a feature Features are implemented as settable features in `lib/logitech_receiver/settings_templates.py`. Some features also have direct implementation in `lib/logitech_receiver/hidpp20.py`. In most cases it should suffice to only implement the settable feature interface for each setting in the feature. That will add one or more widgets in the Solaar main window to show and change the setting, will permit storing and restoring changed settings, and will output the feature settings in `solaar show`. A setting implementation is a subclass of one of the built-in setting classes illustrated by the pointer speed setting implementation. ```python class PointerSpeed(_Setting): name = 'pointer_speed' label = _('Sensitivity (Pointer Speed)') description = _('Speed multiplier for mouse (256 is normal multiplier).') feature = _F.POINTER_SPEED validator_class = _RangeV min_value = 0x002e max_value = 0x01ff validator_options = {'byte_count': 2} ``` A setting implementation needs a name, a label, and a description. The name is used in the persistent settings structure to store and restore changed settings and should be a valid Python identifier. (Some older settings have dashes.) The label is displayed in the Solaar main window and the description is used as a tooltip there. The label and description should be specified as translatable strings. A setting implementation for a feature (for modern devices that use the HID++ 2.0 protocol) needs a feature identifier. A setting implementation needs a reader/writer and a validator. The reader/writer is responsible for actually writing settings to the device and reading them from the device, writing and reading the byte strings that represent the setting values on the device. For most feature settings the setting implementation can just inherit the standard feature reader/writer, `FeatureRW`. Options for `FeatureRW` are supplied by the `rw_options` class variable, which is used to provide command numbers for reading and writing as well as other information needed to identify the parts of the command and response that hold the setting value and modify the reading and writing procedure. `PointerSpeed` uses the defaults; here is an example of specifying non-default commands for reading and writing: ``` rw_options = {'read_fnid': 0x10, 'write_fnid': 0x20} ``` Some old devices use registers instead and the setting needs to use the register reader/writer. Only implement a register interface for the setting if you are very brave and you have access to a device that has a register interface for the setting. Register interfaces cannot be auto-discovered and need to be stated in descriptors.py for each device with the register interface. The validator instance is responsible for turning raw values read from the device into Python data and Python data into raw values to be written to the device and validating that the Python data is acceptable for the setting. There are several possible kinds of Python data for setting interfaces, ranging from simple toggles, to ranges, to fixed lists, to dynamic choices, to maps of dynamic choices. Pointer speed is a setting whose values are integers in a range so _RangeV validator is used. Arguments to validators are specified as class variables. The _RangeV validator requires the minimum and maximum for the value as separate class variables and the byte size of the value on the device as part of `validator_options`. Splitting the minimum and maximum makes it easier for code that works with settings to determine this information. Settings that are toggles or choices work similarly, but their validators have different arguments. Map settings have more complicated validators and more arguments. Settings where the acceptable values are determined from the device subclass the validator and provide a build class method that queries the device and creates an instance of the validator. This method can also return `None`, indicating that even though the device implements the feature it does not usefully support the setting. Settings need to be added to the `SETTINGS` list so that setting discovery can be done. For more information on implementing feature settings see the comments in `lib/logitech_receiver/settings_templates.py`. Solaar-1.1.14/docs/hidpp-documentation.txt000066400000000000000000000002061473525213700204720ustar00rootroot00000000000000Documentation on HID++ can be found in the Google drive directory https://drive.google.com/drive/folders/0BxbRzx7vEV7eWmgwazJ3NUFfQ28 Solaar-1.1.14/docs/i18n.md000066400000000000000000000064501473525213700150660ustar00rootroot00000000000000--- title: Translating Solaar layout: page --- # Translating Solaar First, make sure you have installed the `gettext` package. Also, you would need to install language pack for Gnome for your language, e.g. `language-pack-gnome-XX-base` for Debian/Ubuntu. Here are the steps to add/update a translation (you should run all scripts from the source root): 1. Get an up-to-date copy of the source files. Preferably, make a fork on GitHub and clone it locally on your machine; this way you can later make a pull request to the main project. 2. Run `./tools/po-update.sh `; it will create/update the file `./po/.po`. 3. Edit `./po/.po` with your favorite editor (just make sure it saves the file with the UTF-8 encoding). For each string in English (msgid), edit the translation (msgstr); if you leave msgstr empty, the string will remain untranslated. Alternatively, you can use the excellent [Poedit](https://poedit.net/) or [Lokalize](https://apps.kde.org/lokalize/). 4. Run `./tools/po-compile.sh`. It will bring up-to-date all the compiled language files, necessary at runtime. 5. Start Solaar (`./bin/solaar`). By default, it will pick up the system language from your environment; to start it in another language, run `LANGUAGE= ./bin/solaar`. To edit the translation iteratively, just repeat from step 3. If the upstream changes, do a `git pull` and then repeat from step 2. Before opening a pull request, please run `./tools/po-update.sh ` again. This will format and sort the translation file, and ensure a minimal diff when updating a translation. ## Supported languages Some of the languages Solaar has been translated to are listed below. A full list of available translations can be obtained by checking the `/po` folder for translation files. - Chinese (Simplified): [Rongrong][Rongronggg9] - Chinese (Taiwan): Peter Dave Hello - Czech: Marián Kyral - Croatian: gogo - Danish: John Erling Blad - Dutch: Heimen Stoffels - Français: [Papoteur][papoteur], [David Geiger][david-geiger], [Damien Lallement][damsweb] - Finnish: Tomi Leppänen - German: Daniel Frost - Greek: Vangelis Skarmoutsos - Indonesia: [Ferdina Kusumah][feku] - Italiano: [Michele Olivo][micheleolivo], Lorenzo - Japanese: Ryunosuke Toda - Norsk (Bokmål): [John Erling Blad][jeblad] - Norsk (Nynorsk): [John Erling Blad][jeblad] - Polski: [Adrian Piotrowicz][nexces], Matthaiks - Portuguese: Américo Monteiro - Portuguese-BR: [Drovetto][drovetto], [Josenivaldo Benito Jr.][jrbenito], Vinícius - Română: Daniel Pavel - Russian: [Dimitriy Ryazantcev][DJm00n], Anton Soroko - Slovak: [Jose Riha][jose1711] - Spanish, Castilian: Jose Luis Tirado - Swedish: John Erling Blad, [Daniel Zippert][zipperten], Emelie Snecker, Jonatan Nyberg - Turkish: Osman Karagöz [Rongronggg9]: https://github.com/Rongronggg9 [papoteur]: https://github.com/papoteur [david-geiger]: https://github.com/david-geiger [damsweb]: https://github.com/damsweb [DJm00n]: https://github.com/DJm00n [jose1711]: https://github.com/jose1711 [nexces]: https://github.com/nexces [zipperten]: https://github.com/zipperten [micheleolivo]: https://github.com/micheleolivo [drovetto]: https://github.com/drovetto [jrbenito]: https://github.com/jrbenito [jeblad]: https://github.com/jeblad [feku]: https://github.com/FerdinaKusumah Solaar-1.1.14/docs/img/000077500000000000000000000000001473525213700145345ustar00rootroot00000000000000Solaar-1.1.14/docs/img/favicon.ico000066400000000000000000000033541473525213700166620ustar00rootroot00000000000000PNG  IHDR szzsBIT|d pHYs A AnktEXtSoftwarewww.inkscape.org<iIDATXW[p[W]>$Yp-[CN'~G5ChLa(4| e 0v#a&Ӧ'Nbc'D,[J$+dY+Ç-UrCÞ3>k9w}6`0S^K7$_O9 l ;͋ HcfGEē럐o"vwMY(*1yH.NA >ysQ'?lmmmuv&0LUec;)"sYvZI_Yw0 >.h6p#SEY$(&nu|"H`:*My!(ѴQXȻܡ7+]w~s:W "*?1'.q[өfuIJEBd%+ C=rw}tH_po|+܏O|Ng`mTg;Ŭg49SzIS q{Q\ַ:>vAymr/V}<+}ꐤ.n-\z4C' &-gNJ[*|W1'bݕsTYq嫦eo :m.2P,k^vj}"/K*e @q{ހB4RHc/cnr2? A]V,* ]XOPN>X%Ypy^edWP_אhnEF^zYrI33Jz}l0H~ Qѿ;ʳH @Ҭ G _8y[%, ͂(cn{, ah02 t SNq41ի+VqfvZ4 imm1XsZݳQ S,4Ua/G+G) DYDCC^;{'%9xrD . gjkgmBٟn|[`:n4=Gei77ۉWJm’҂QbDw?wy4yH֤dNwB1>fv%Z,߈rS1,ДYqӽ6g{Bi45$(dUi06\LZ-ρ@ԂzO~Ų-J%M5"}Wϝ60Wҍ QQJ-[[ G~=6bٖ"ɑDR #nkщۚ5:{{Kacv뼾&lXj$Xxod2+s7`$z;[9q*bRu5 p*&7jb1p#bKy3R\q*xb)\qDM1 4Q\߾wzraNY_M $9o쓽DeWܾs3GũIENDB`Solaar-1.1.14/docs/img/favicon.png000066400000000000000000000033541473525213700166740ustar00rootroot00000000000000PNG  IHDR szzsBIT|d pHYs A AnktEXtSoftwarewww.inkscape.org<iIDATXW[p[W]>$Yp-[CN'~G5ChLa(4| e 0v#a&Ӧ'Nbc'D,[J$+dY+Ç-UrCÞ3>k9w}6`0S^K7$_O9 l ;͋ HcfGEē럐o"vwMY(*1yH.NA >ysQ'?lmmmuv&0LUec;)"sYvZI_Yw0 >.h6p#SEY$(&nu|"H`:*My!(ѴQXȻܡ7+]w~s:W "*?1'.q[өfuIJEBd%+ C=rw}tH_po|+܏O|Ng`mTg;Ŭg49SzIS q{Q\ַ:>vAymr/V}<+}ꐤ.n-\z4C' &-gNJ[*|W1'bݕsTYq嫦eo :m.2P,k^vj}"/K*e @q{ހB4RHc/cnr2? A]V,* ]XOPN>X%Ypy^edWP_אhnEF^zYrI33Jz}l0H~ Qѿ;ʳH @Ҭ G _8y[%, ͂(cn{, ah02 t SNq41ի+VqfvZ4 imm1XsZݳQ S,4Ua/G+G) DYDCC^;{'%9xrD . gjkgmBٟn|[`:n4=Gei77ۉWJm’҂QbDw?wy4yH֤dNwB1>fv%Z,߈rS1,ДYqӽ6g{Bi45$(dUi06\LZ-ρ@ԂzO~Ų-J%M5"}Wϝ60Wҍ QQJ-[[ G~=6bٖ"ɑDR #nkщۚ5:{{Kacv뼾&lXj$Xxod2+s7`$z;[9q*bRu5 p*&7jb1p#bKy3R\q*xb)\qDM1 4Q\߾wzraNY_M $9o쓽DeWܾs3GũIENDB`Solaar-1.1.14/docs/img/solaar.svg000066400000000000000000000072651473525213700165500ustar00rootroot00000000000000 image/svg+xml Solaar-1.1.14/docs/implementation.md000066400000000000000000000403001473525213700173240ustar00rootroot00000000000000--- title: Solaar Implementation layout: page --- # Solaar Implementation Solaar has three main components: code mostly about receivers and devices, code for the command line interface, and code for the graphical user interface. The following graph shows the main components of Solaar and how they interact. ```mermaid graph TD subgraph User interface U[UI] C[CLI] end subgraph Core U --> S{Solaar} C --> S S --> L[Logitech receiver] L --> R[Receiver] L --> D[Device] S --> B[dbus] end subgraph Hardware interface R --> A D --> A A[hidapi]--> P[hid parser] end subgraph Peripherals P <-.-> M[Logitech mouse] P <-.-> K[Logitech keyboard] end ``` ## Receivers and Devices The code in `logitech_receiver` is responsible for creating and maintaining receiver (`receiver/Receiver`) and device (`device/Device`) objects for each device on the computer that uses the Logitech HID++ protocol. These objects are discovered in Linux by interacting with the Linux `udev` system using code in `hidapi`. The code in `logitech_receiver/receiver' is responsible for receiver objects. ... The code in `logitech_receiver/device' is responsible for device objects. ... the complex device setup process A device object stores the currrent value of many aspects of the device. It provides methods for retrieving and setting these aspects. The setters generally store the new value and call an hidpp10 or hidpp20 function to modify the device accordingly. The retrievers generally check whether the value is cached on the device if so just returning the cached value and if not calling an hidpp10 or hidpp20 function to retrieve the value and returning the value after caching it. ... Not all communication with a device is done through the `Device` class. Some is done directly from settings. .... ### HID++ #### HID++ 2.0 The code in `logitech_receiver/hidpp20' interacts with devices using the HID++ 2.0 (and greater) protocol. Many of the functions in this module send messages to devices to modify their internal state, for example setting a host name stored in the device. Many other functions send messages to devices that query their internal state and interpret the response, for example returning how often a mouse sends movement reports. The result of these latter functions are generally cached in device objects. A few of these functions create and return a large structure or a class object. The HID++ 2.0 protocol is built around a number of features, each with its own functionality. One of the features, that is required to be implemented by all devices supporting the protocol, provide information on which features the device provides. The `hidpp20` module provides a class (`FeaturesArray`) to store information on what features are provided by a device and how to access them. Each device that implements the HID++ 2.0 protocol has an instance of this class. The heavily used function `feature_request` creates an HID++ 2.0 message using this information to help determine what data to put into the message. Many devices allow reprogramming some keys or buttons. One the main reasons for reprogramming a key or device is to cause it to produce an HID++ message instead of its normal HID message, this is referred to as diverting the key (to HID++). The `ReprogrammableKey` class stores information about the reprogramming of one key for one version of this capability, with methods to access and update this information. The `PersistentRemappableAction` class does the same for another version. The `KeysArray` class stores information about the reprogramming of a collection of keys, with methods to access this information. Functions in the Device class request `KeysArray` information for a device when appropriate and store it on the device. Many pointing devices provide a facility for recognizing gestures and sending an HID message for the gesture. The `Gesture` class stores inforation for one gesture and the `Gestures` class stores information for all the gestures on a device. Functions in the Device class request `KeysArray` information and store it on devices. Functions in the Device class request `Gestures` information for a device when appropriate and store it on the device. Many gaming devices provide an interface to controlling their LEDs by zone. The `LEDEffectSetting` class stores the current state of one zone of LEDs. This information can come directly from an LED feature but is also part of device profiles so this class provides a byte string interface. Solaar stores this information in YAML so this class provides a YAML interface. The `LEDEffectsInfo` class stores information about what LED zones are on a device and what effects they can perform and provides a method that builds an object by querying a device. Many gaming devices can be controlled by selecting one of their profiles. A profile sets up the rate at which the device reports movement, a set of sensitivites of its movement detector, a set of actions to be performed by mouse buttons or G and M keys, and effects for up to two LED zones. The `Button` class stores information about a button or key action. The `OnboardProfile` class stores a single profile, using the `LEDEffectSetting` and `Button` classes. Because retrieving and changing a profile is complex, this class provides a byte string interface. Because Solaar dumps profiles from devices as YAML documents and loads them into devices from YAML documents, this class provides a YAML interface. The `OnboardProfiles` class class stores the entire profiles information for a device. It provides an interface to construct an `OnboardProfiles` object by querying a device. Because Solaar dumps profiles from devices as YAML documents and loads them into devices from YAML documents, these classes also provide a YAML interface. #### HID++ 1.0 The code in `logitech_receiver/hidpp10' interacts with devices using the HID++ 1.0 protocol. ... ### Low Level Information and Access The module `descriptors` sets up information on device models for which Solaar needs information to support. Solaar can determine all this information for most modern devices so it is only needed for older devices or devices that are unusual in some way. The information may include the name of the device model, short name of the device model, the HID++ protocol used by the device model, HID++ registers supported by the device model, various identifiers for the device model, and the USB interface that the device model uses for HID++ messages. It used to include the HID++-based settings for the device model but this information is now added in `setting_templates`. The information about a device model can be retrieved in several ways. The module `base_usb` sets up information for most of the receiver models that Solaar supports, including USB id, USB interface used for HID++ messages, what kind of receiver model it is, and some capabilities of the receiver model. Solaar can now support other receivers as long as they are not too unusual. The module lso sets up lists of device models by USB ID and Bluetooth ID and provides a function to determine whether a USB ID or Bluetooth ID is an HID++ device model The module `base` provides functions that call discovery to enumerate all current receivers and devices and to set up a callback for when new receivers or devices are discovered. It provides functions to open and close I/O channels to receivers and devices, write HID++ messages to receivers and devices, and read HID++ messages from receivers and devices. It provides a function to turn an HID++ message into a notification. The module provides a function to send an HID++ message to a receiver or device, constructing the message from parameters to the function, and optionally waiting for and returning a response. The function checks messages from the receiver or device, only terminating at timeout or when a message that appears to be the response is seen. Other messages are turned into notifications if appropriate and ignoreed otherwise. A separate function sends a ping message and waits for a reply to the ping. ### Notifications and Status HID++ devices not only respond to commands but can spontaneously emit HID++ messages, such as when their movement sensitivity changes or when a diverted key is pressed. These spontaneous messages are called notifications and if software is well behaved can be distinguished from messages that are responses to commands. (The Linux HIDPP driver was not well behaved at some time and still may not be well behaved, resulting in it causing devices to send responses that cannot be distinguished from notifications.) The `listener` module provides a class to set up a thread that listens to all the HID++ messages that come from a given device or receiver, convert the message that are notifications to a Solaar notification structure, and invoke a callback on the notification. The 'notifications` module provides a function to take a notification from a receiver or device and initiate processing required for that notification. For receivers notifications are used to signal the progress of pairing attempts. For devices some notifications are for pairing, some signal device connection and diconnection from a receiver, some are other parts of the HID++ 1.0 protocol, and some are for the HID++ 2.0 protocol. Devices can provide a callback for special handling of notifications. This facility is used for two special kinds of Solaar settings. The module contains code that determines the meaning of a notification based on fields in the notification and the status and HID++ 2.0 features of the device if appropriate and updates the device and its status accordingly. Updates to device status can trigger updates to the Solaar user interface. The processing of some notifications also directly runs a function to update the Solaar user interface. After this processing HID++ 2.0 notifications are sent to the `diversion` module where they initiate Solaar rule processing. The `status` module provides the `DeviceStatus` class to record the battery status of a device. It also provides an interface to signal changes to the connection status of the device that can invoke a callback. This callback is used to update the Solaar user interface when the status changes. ### Settings The Solaar GUI is based around settings. A setting contains all the information needed to store the value of some aspect of a device, read it from the device, write it to the device, and record its state in a dictionary. A setting also contains information to display and manipulate a setting, namely what kind of user interface element to use, what values are permissable, a label to use for the setting, and a tooltip to provide additional information for the setting. Settings can be either based on HID++ 1.0, using an HID++ 1.0 register that the device provides, or based on HID++ 2.0, using an HID++ 2.0 feature that the device provides. The module `settings` provides classes and methods to create and support a setting. The module `setting_templates` contains all the settings that Solaar supports as well as functions to determine what feature-based settings a device can support. A simple boolean setting can be set up as follows: ``` class HiresSmoothInvert(_Setting): name = 'hires-smooth-invert' label = _('Scroll Wheel Direction') description = _('Invert direction for vertical scroll with wheel.') feature = _F.HIRES_WHEEL rw_options = {'read_fnid': 0x10, 'write_fnid': 0x20} validator_options = {'true_value': 0x04, 'mask': 0x04} ``` The setting is a boolean setting, the default for settings. `name` is the dictionary key for recording the state of the setting. `label` is the label to be shown for the setting in a user interface and `description` is the tooltip. `feature` is the HID++ 2.0 feature that is used to read the current state of the setting from a device and write it back to a device. `rw_options` contains options used when reading or writing the state of the setting, here to use feature command 0x10 to read the value and feature command 0x20 to write the value. `validator_options` contains options to turn setting values into bytes and bytes into setting values. The options here to take a single byte (the default) and mask it with 0x04 to get a value with a result of 0x04 being true and anything else being false. They also say to use 0x04 when writing a true value and 0x00 (the default) when writing a false value. Because this is a boolean setting and the mask masks off part of a byte the value to be written is or'ed with the byte read for the setting before writing to the device. A simple choice setting can be set up as follows: ``` class Backlight(_Setting): name = 'backlight-qualitative' label = _('Backlight') description = _('Set illumination time for keyboard.') feature = _F.BACKLIGHT choices_universe = _NamedInts(Off=0, Varying=2, VeryShort=5, Short=10, Medium=20, Long=60, VeryLong=180) validator_class = _ChoicesV validator_options = {'choices': choices_universe} ``` This is a choice setting because of the value for `validator_class`. `choices_universe` is all the possible stored values for the setting along with how they are to be displayed in a user interface. `validator_options` provides the current permissable choices, here always are the same as all the choices. The Solaar GUI takes these settings and constructs an interface for displaying and changing the setting. This setup allows for very quick implementation of simple settings but it bypasses the data stored in a device object. ### Solaar Rules The `diversion` module (so-named because it initially mostly handled diverted key notifications) implements Solaar rules. ... ### Utility Functions, Structures, and Classes The module `common.py` provides utility functions, structures, and classes. `crc16` is a function to compute checksums used in profiles. `NamedInt`, `NamedInts`, and `UnsortedNamedInts` provide integers and sets of integers with attached names. `FirmwareInfo` provides information about device firmware. `BATTERY_APPROX` provides named integers used for approximate battery levels of devices. `i18n.py` provides a few strings that need translations and might not otherwise be visible to translation software. `special_keys.py` provides named integers for various collections of key codes and colors. ## Discovery of HID++ Receivers and Devices and I/O The code in `hidapi` is responsible for discovery of receivers and devices that use the HID++ protocol. The module used in Linux is `hidapi/udev` which is a modification of some old Python code that provides an interface to the Linux `udev` system. The code originally was only for receivers that used USB and devices paired with them. It identifies HID++ receivers by their USB ids, based on a list of Logitech HID++ receivers with their USB ids. It then added all devices that were paired with them and that were in a list of HID++ devices with their WPID. A WPID is used to identify the device type for devices paired with HID++ receivers. This code now also adds all devices paired with HID++ receivers whether they are in this list or not. The code now also identifies HID ++ devices that are directly connected via either USB or Bluetooth. These devices are recognized by several means: the internal list of HID++ devices for elements of the list that have either a USB IS or a Bluetooth ID, any device with a USB ID or Bluetooth ID that falls in one of several ranges of IDs that are known to support HID++, or any device that has an HID protocol descriptor that claims support for HID++. This last method requires an external Pyshon module to decipher HID protocol descriptors that is not always present. Device and receiver discovery is performed when Solaar starts. While the Solaar GUI is running the `udev` code also listens for connections of new hardware using facilities from `GLib`. This code is also responsible for actual writing data to devices and receivers and reading data from them. ## Solaar ### Startup and Commonalities __init__.py configuration.py gtk.py* i18n.py listener.py tasks.py upower.py The files `version` and `commit` contain data about the current version and git commit of Solaar. ### Solaar Command Line Interface solaar/cli ### Solaar (Graphical) User Interface solaar/ui Solaar-1.1.14/docs/index.md000066400000000000000000000276571473525213700154320ustar00rootroot00000000000000--- title: Solaar layout: default --- **Solaar** is a Linux manager for many Logitech keyboards, mice, and trackpads that connect wirelessly to a USB [Unifying][unifying], Bolt, Lightspeed, or Nano receiver; connect directly via a USB cable; or connect via Bluetooth. Solaar does not work with peripherals from other companies. Documentation here is for the current version of Solaar. Some Linux distributions distribute old versions of Solaar. If you are using an old version and something described here does not work you should upgrade using one of the methods described below. Solaar runs as a regular user process, albeit with direct access to the Linux interface that lets it directly communicate with the Logitech devices it manages using special Logitech-proprietary (HID++) commands. Each Logitech device implements a different subset of these commands. Solaar is thus only able to make the changes to devices that devices implement. Solaar is not a device driver and does not process normal input from devices. It is thus unable to fix problems that arise from incorrect handling of mouse movements or keycodes by Linux drivers or other software. Solaar can be used as a GUI application, the usual case, or via its command-line interface. The Solaar GUI is meant to run continuously in the background, monitoring devices, making changes to them, and responding to some messages they emit. To this end, it is useful to have Solaar start at user login so that changes made to devices by Solaar are applied at login and throughout the user's session. Both Solaar interfaces are able to list the connected devices and show information about each device, often including battery status. Solaar is able to pair and unpair devices with receivers as supported by the device and receiver. Solaar can also control some changeable settings of devices, such as scroll wheel direction and function key behavior. Solaar keeps track of most of these settings on a per-computer basis, because devices forget most settings when powered down, and the GUI application restores them whenever a device connects. For more information on how to use Solaar see [the usage page](https://pwr-solaar.github.io/Solaar/usage), and for more information on its capabilities see [the capabilities page](https://pwr-solaar.github.io/Solaar/capabilities). Solaar's GUI normally uses an icon in the system tray and starts with its main window visible. This aspect of Solaar depends on having an active system tray, which is not the default situation for recent versions of Gnome. For information on to set up a system tray under Gnome see [the capabilities page](https://pwr-solaar.github.io/Solaar/capabilities). Solaar's GUI can be started in several ways - `--window=show` (the default) starts with its main window visible, - `--window=hide` starts with its main window hidden, - `--window=only` does not use the system tray, and starts with main window visible. For more information on Solaar's command-line interface use the help option, as in `solaar --help`. Solaar has progressed past version 1.1. Problems with earlier versions should not be reported as bugs. Instead, upgrade to a recent version or manually install the current version from [GitHub](https://github.com/pwr-Solaar/Solaar). Some capabilities of Solaar have been developed by observing the behavior of Logitech receivers and devices and generalizing from these observations. If your Logitech receiver or device behaves strangely this may be caused by an incorrect behavior generalization. Please report such experiences by creating an issue in [the Solaar repository](https://github.com/pwr-Solaar/Solaar/issues). [unifying]: https://en.wikipedia.org/wiki/Logitech_Unifying_receiver ## Supported Devices Solaar will detect all devices paired with supported Unifying, Bolt, Lightspeed, or Nano receivers, and at the very least display some basic information about them. Solaar will detect many Logitech devices that connect via a USB cable or Bluetooth. Solaar can pair and unpair a Logitech device showing the Unifying logo (Solaar's version of the [logo][logo]) with any Unifying receiver, and pair and unpair a Logitech device showing the Bolt logo with any Bolt receiver, and can pair and unpair Lightspeed devices with Lightspeed receivers for the same model. Solaar can pair some Logitech devices with Logitech Nano receivers, but not all Logitech devices can be paired with Nano receivers. Logitech devices without a Unifying or Bolt logo generally cannot be paired with Unifying or Bolt receivers. Solaar does not handle connecting or disconnecting via Bluetooth, which is done using the usual Bluetooth mechanisms. For a partial list of supported devices and their features, see [the devices page](https://pwr-solaar.github.io/Solaar/devices). [logo]: https://pwr-solaar.github.io/Solaar/img/solaar.svg ## Prebuilt packages Up-to-date prebuilt packages are available for some Linux distros (e.g., Fedora 33+) in their standard repositories. If a recent version of Solaar is not available from the standard repositories for your distribution, you can try one of these packages. - Arch solaar package in the [extra repository][arch] - Ubuntu/Kubuntu package in [Solaar stable ppa][ppa2] - NixOS Flake package in [Svenum/Solaar-Flake][nix flake] Solaar is available from some other repositories but they may be several versions behind the current version. - for Ubuntu/Kubuntu 16.04+: the solaar package from [universe repository][universe repository] - a [Gentoo package][gentoo], courtesy of Carlos Silva and Tim Harder - a [Mageia package][mageia], courtesy of David Geiger Solaar uses a standard system tray implementation; solaar-gnome3 is no longer required for Gnome or Unity integration. [ppa4]: https://launchpad.net/~trebelnik-stefina [ppa2]: https://launchpad.net/~solaar-unifying/+archive/ubuntu/stable [arch]: https://www.archlinux.org/packages/extra/any/solaar/ [gentoo]: https://packages.gentoo.org/packages/app-misc/solaar [mageia]: http://mageia.madb.org/package/show/release/cauldron/application/0/name/solaar [universe repository]: http://packages.ubuntu.com/search?keywords=solaar&searchon=names&suite=all§ion=all [nix flake]: https://github.com/Svenum/Solaar-Flake ## Manual installation See [the installation page](https://pwr-solaar.github.io/Solaar/installation) for the step-by-step procedure for manual installation. ## Known Issues - Onboard Profiles, when active, can prevent changes to other settings, such as Polling Rate, DPI, and various LED settings. Which settings are affected depends on the device. To make changes to affected settings, disable Onboard Profiles. If Onboard Profiles are later enabled the affected settings may change to the value in the profile. - Solaar version 1.1.12 has a bug resulting in devices remaining in their default configuration after a system resume. This is fixed in 1.1.13. - Bluez 5.73 does not remove Bluetooth devices when they disconnect. Solaar 1.1.12 processes the DBus disconnection and connection messages from Bluez and does re-initialize devices when they reconnect. The HID++ driver does not re-initialize devices, which causes problems with smooth scrolling. Until the problem is resolved having Scroll Wheel Resolution set to true (and not ignored) may be helpful. - The Linux HID++ driver modifies the Scroll Wheel Resolution setting to implement smooth scrolling. If Solaar changes this setting, scrolling can be either very fast or very slow. To fix this problem click on the icon at the right edge of the setting to set it to "Ignore this setting", which is the default for new devices. The mouse has to be reset (e.g., by turning it off and on again) before this fix will take effect. - Solaar expects that it has exclusive control over settings that are not ignored. Running other programs that modify these settings, such as logiops, will likely result in unexpected device behavior. - The driver also sets the scrolling direction to its normal setting when implementing smooth scrolling. This can interfere with the Scroll Wheel Direction setting, requiring flipping this setting back and forth to restore reversed scrolling. - The driver sends messages to devices that do not conform with the Logitech HID++ specification resulting in responses being sent back that look like other messages. For some devices this causes Solaar to report incorrect battery levels. - Solaar normally uses icon names for its icons, which in some system tray implementations results in missing or wrong-sized icons. The `--tray-icon-size` option forces Solaar to use icon files of appropriate size for tray icons instead, which produces better results in some system tray implementations. To use icon files close to 32 pixels in size use `--tray-icon-size=32`. - The icon in the system tray can show up as 'black on black' in dark themes or as non-symbolic when the theme uses symbolic icons. This is due to problems in some system tray implementations. Changing to a different theme may help. The `--battery-icons=symbolic` option can be used to force symbolic icons. - Solaar will try to use uinput to simulate input from rules under Wayland or if Xtest is not available but this needs write permission on /dev/uinput. For more information see [the rules page](https://pwr-solaar.github.io/Solaar/rules). - Diverted keys remain diverted and so do not have their normal behavior when Solaar terminates or a device disconnects from a host that is running Solaar. If necessary, their normal behavior can be reestablished by turning the device off and on again. This is most important to restore the host switching behavior of a host switch key that was diverted, for example to switch away from a host that crashed or was turned off. - When a receiver-connected device changes hosts Solaar remembers which diverted keys were down on it. When the device changes back the first time any of these diverted keys is depressed Solaar will not realize that the key was newly depressed. For this reason Solaar rules that can change hosts should trigger on key releasing. ## License This software is distributed under the terms of the [GNU Public License, v2](LICENSE.txt), or later. ## Contributing to Solaar Contributions to Solaar are very welcome. Solaar has complete or partial translations of its GUI strings in several languages. If you want to update a translation or add a new one see [the translation page](https://pwr-solaar.github.io/Solaar/i18n) for more information. If you find a bug, please check first if it has already been reported. If yes, please add additional information you may have to the existing issue. If not, please open a new bug report issue. If you can provide a fix for it, please also open a GitHub pull request. Label your commits using the naming conventions in recent commits to Solaar. If you want to add a new feature to Solaar, feel free to open a feature request issue to discuss your proposal. There are also usually several open issues for enhancements that have already been requested. ## Contributors This project began as a third-hand clone of [Noah K. Tilton](https://github.com/noah)'s logitech-solar-k750 project on GitHub (no longer available). It was developed further thanks to the contributions of many other people, including: - [Daniel Pavel](https://github.com/pwr) - [Filipe Lains](https://github.com/FFY00) - [Peter Wu](https://github.com/Lekensteyn), who also did some [reverse engineering on pairing](https://lekensteyn.nl/logitech-unifying.html) - Julien Danjou - [Lars-Dominik Braun](http://6xq.net/git/lars/lshidpp.git) - [Alexander Hofbauer](http://derhofbauer.at/blog/blog/2012/08/28/logitech-performance-mx) - [Clach04](https://github.com/clach04) - [Peter F. Patel-Schneider](https://github.com/pfps) Thanks go to Nestor Lopez Casado, who provided [public Logitech specifications for the HID++ protocol](http://drive.google.com/folderview?id=0BxbRzx7vEV7eWmgwazJ3NUFfQ28). Also, thanks to Douglas Wagner, Julien Gascard, and others for helping with application testing and supporting new devices. Solaar-1.1.14/docs/installation.md000066400000000000000000000145651473525213700170160ustar00rootroot00000000000000--- title: Manual Installation layout: page --- # Installing from PyPI An easy way to install the most recent release version of Solaar is from the PyPI repository. First install pip, and then run `pip install --user solaar` or `pipx install --system-site-packages solaar` or If you are using pipx add the `` flag. This will not install the Solaar udev rule, which you will need to install manually by copying `~/.local/lib/udev/rules.d/42-logitech-unify-permissions.rules` to `/etc/udev/rules.d` as root. If you want Solaar rules to simulate input you will have to instead install Solaar's uinput udev rule from the GitHub repository. ## Installing in macOS Solaar has limited support for macOS. You can use it to pair devices and configure settings but the rule system and diversion will not work. After installing Solaar via pip use homebrew to install the needed libraries: ``` brew update brew install hidapi gtk+3 pygobject3 ``` # Installating from GitHub ## Downloading Clone Solaar from GitHub by `git clone https://github.com/pwr-Solaar/Solaar.git`. ## Installing using the Makefile Solaar has a makefile that can be used to easily install Solaar after cloning the repository. First, install the needed system packages by `make install_apt` or `make install_dnf` or `make install_brew`. These might not install all needed packages in older versions of your distribution. Next, install the Solaar rule via `make install_udev`. If you are using Wayland instead of X11 you may want to instead `make install_udev_uinput` Finally, install Solaar via `make install_pip` or `make install_pipx`. so that Solaar rules can simulate input in Wayland. Parts of the installation process require sudo privileges so you may be asked for your password. ## Running from the download directory To run Solaar from the download directory, just cd to there and run `bin/solaar` for the GUI or `bin/solaar ` for the CLI. ## Requirements for Solaar This is only relevant if you have problems with the easier methods above. Solaar needs a reasonably new kernel with kernel modules `hid-logitech-dj` and `hid-logitech-hidpp` loaded. The kernel option CONFIG_HIDRAW also needs to be enabled. Most of Solaar should work fine with any kernel more recent than 5.2, but newer kernels might be needed for some devices to be correctly recognized and handled. The `udev` package must be installed and its daemon running. Solaar requires Python 3.7+ and requires several packages to be installed. If you are running the system version of Python you should have the `python3-pyudev`, `python3-psutil`, `python3-xlib`, `python3-evdev`, `python3-typing-extensions`, `dbus-python` or `python3-dbus`, and `python3-yaml` or `python3-pyyaml` packages installed. To run the GUI Solaar also requires Gtk3 and its GObject introspection bindings. If you are running the system version of Python in Debian/Ubuntu you should have the `python3-gi` and `gir1.2-gtk-3.0` packages installed. In Fedora you need `gtk3` and `python3-gobject`. You may have to install `gcc` and the Python development package (`python3-dev` or `python3-devel`, depending on your distribution). Other system packages may be required depending on your distribution, such as `python-gobject-common-devel`. Although the Solaar CLI does not require Gtk3, `solaar config` does use Gtk3 capabilities to determine whether the Solaar GUI is running and thus should tell the Solaar GUI to update its information about settings so it is a good idea to have Gtk3 available even for the Solaar CLI. Solaar needs a library to interact with the system tray. The library that provides this interaction depends on the distribution and window system. If ayatana appindicator is available then it is best to have this library installed, e.g., by installing `libayatana-appindicator` or `gir1.2-ayatanaappindicator3-0.1` or similar, depending on distribution. Otherwise appindicator can sometimes be used, e.g., by installing `libappindicator-gtk3` or `gir1.2-appindicator3-0.1` or similar, depending on distribution. If desktop notifications bindings are also installed (`gir1.2-notify-0.7` for Debian/Ubuntu), you will also see desktop notifications when devices come online and go offline. If the `hid_parser` Python package is available, Solaar parses HID report descriptors and can control more HID++ devices that do not use a receiver. This package may not be available in some distributions but can be installed using pip via `pip install --user hid-parser`. If the `gitinfo` Python package is available, Solaar shows better information about which version of Solaar is running. This package may not be available in some distributions but can be installed using pip via `pip install --user python-git-info`. If you are running a version of Python different from the system version, you may need to use pip to install projects that provide the above Python packages. Solaar runs best under X11 with the Xtest extension enabled so that Solaar rules can fake keyboard input using Xtest. Solaar also uses the X11 library to access the XKB extension, which requires installation of the X11 development package. (In Fedora this is `libX11-devel`. In other distributions it may be `libX11-dev`.) Solaar will run under Wayland but some parts of Solaar rules will not work. For more information see [the rules page](https://pwr-solaar.github.io/Solaar/rules). ## Installing Solaar's udev rule manually You can install Solaar's udev rule manually by copying the file `rules.d/42-logitech-unify-permissions.rules` as root from the Solaar repository to `/etc/udev/rules.d`. In Wayland you may want to instead copy `rules.d-uinput/42-logitech-unify-permissions.rules`. Let udev reload its rules by running `sudo udevadm control --reload-rules`. # Solaar in other languages If you want to have Solaar's user messages in some other language you need to run `tools/po-compile.sh` to create the translation files before running or installing Solaar and set the LANGUAGE environment variable appropriately when running Solaar. # Running Solaar at Startup Distributions can cause Solaar can be run automatically at user login by installing a desktop file at `/etc/xdg/autostart/solaar.desktop`. An example of this file content can be seen in the repository at [`share/autostart/solaar.desktop`](/share/autostart/solaar.desktop). If you install Solaar yourself you may need to create or modify this file or install a startup file under your home directory. Solaar-1.1.14/docs/rules.md000066400000000000000000000445161473525213700154460ustar00rootroot00000000000000--- title: Rule Processing of HID++ Notifications layout: page --- # Rule Processing of HID++ Notifications Creating and editing most rules can be done in the Solaar GUI, by pressing the 'Rule Editor' button in the Solaar main window. Note that rule processing only fully works under X11. When running under Wayland with X11 libraries loaded some features will not be available. When running under Wayland without X11 libraries loaded even more features will not be available. Rule features known not to work under Wayland include process and mouse process conditions, although on GNOME desktop under Wayland, you can use those with the Solaar Gnome extension installed, You can install it from `https://extensions.gnome.org/extension/6162/solaar-extension`. Under Wayland using keyboard groups may result in incorrect symbols being input for simulated input. Under Wayland simulating inputs when modifier keys are pressed may result in incorrect symbols being sent. Simulated input uses Xtest if available under X11 or uinput if the user has write access to /dev/uinput. The easiest way to maintain write access to /dev/uinput is to use Solaar's alternative udev rule by downloading `https://raw.githubusercontent.com/pwr-Solaar/Solaar/master/rules.d-uinput/42-logitech-unify-permissions.rules` and copying it as root into the `/etc/udev/rules.d` directory. You may have to reboot your system for the write permission to be set up. Another way to get write access to /dev/uinput is to run `sudo setfacl -m u:${USER}:rw /dev/uinput` but this needs to be done every time the system is rebooted. ## HID++ notifications and diversion Logitech devices that use HID++ version 2.0 or greater, produce feature-based notifications that Solaar can process using a simple rule language. For example, using rules Solaar can emulate an `XF86_MonBrightnessDown` key tap in response to the pressing of the `Brightness Down` key on Craft keyboards, which normally does not produce any input at all when the keyboard is in Windows mode. Solaar's rules only trigger on HID++ notifications so device actions that normally produce HID output have to be first be set (diverted) to produce HID++ notifications instead of their normal behavior. Currently, Solaar can divert some mouse scroll wheels, some mouse thumb wheels, the crown of Craft keyboards, and some keys and buttons. If the scroll wheel, thumb wheel, crown, key, or button is not diverted by setting the appropriate setting then no HID++ notification is generated and rules will not be triggered by manipulating the wheel, crown, key, or button. Look for `HID++` or `Diversion` settings to see what diversion can be done with your devices. ### Show notifications Running Solaar with the `-ddd` option will show information about notifications, including their feature name, report number, and data. In response to a feature-based HID++ notification Solaar runs a sequence of rules. A `Rule` is a sequence of components, which are either sub-rules, conditions, or actions. Conditions and actions are dictionaries with one entry whose key is the name of the condition or action and whose value is the argument of the action. If the last thing that a rule does is execute an action, no more rules are processed for the notification. Rules are evaluated by evaluating each of their components in order. The evaluation of a rule is terminated early if a condition component evaluates to false or the last evaluated subcomponent of a component is an action. A rule is false if its last evaluated component evaluates to false. ## Conditions ### Not `Not` conditions take a single component and are true if their component evaluates to a false value. ### Or `Or` conditions take a sequence of components and are evaluated by evaluating each of their components in order. An Or condition is terminated early if a component evaluates to true or the last evaluated subcomponent of a component is an action. A Or condition is true if its last evaluated component evaluates to a true value. `And` conditions take a sequence of components which are evaluated the same as rules. ### Feature `Feature` conditions are true if the name of the feature of the current notification is their string argument. `Report` conditions are true if the report number in the current notification is their integer argument. ### Key `Key` conditions are true if the Logitech name of the current **diverted** key or button being pressed is their string argument. Alternatively, if the argument is a list `[name, action]` where `action` is either `'pressed'` or `'released'`, the key down or key up events of `name` argument are matched, respectively. Logitech key and button names are shown in the `Key/Button Diversion` setting. These names are also shown in the output of `solaar show` in the 'Reprogrammable keys' section. Only keys or buttons that have 'Divertable' in their report can be diverted. Some keyboards have 'Gn', 'Mn', or 'MR' keys, which are diverted using the 'Divert G Keys' setting. ### Key is down `KeyIsDown` conditions are true if the **diverted** key or button that is their string argument is currently down. Note that this only works for **diverted** keys or buttons, including diverted Gn, Mn, and MR keys. ### Key and button diversion Solaar can also create special notifications in response to mouse movements on some mice. Setting `Key/Button Diversion` for a key or button to Mouse Gestures causes the key or button to create a `Mouse Gesture` notification for the period that the key or button is depressed. Moving the mouse creates a mouse movement event. Stopping the mouse for a little while and moving it again creates another mouse movement event. Pressing a diverted key creates a key event. When the key is released the sequence of events is sent as a synthetic notification that can be matched with `Mouse Gesture` conditions. ### Mouse gestures `Mouse Gesture` conditions are true if the actions (mouse movements and diverted key presses) taken while a mouse gestures button is held down match the arguments of the condition. Mouse gestures buttons can be set using the 'Key/Button Diversion' setting, by changing the value to `Mouse Gestures`. The arguments of a Mouse Gesture condition can be a direction, i.e., `Mouse Up`, `Mouse Down`, `Mouse Left`, `Mouse Right`, `Mouse Up-Left`, `Mouse Up-Right`, `Mouse Down-Left`, or `Mouse Down-Right`, or the Logitech name of a key. If the first argument is the Logitech name of a key then that argument is matched against the button that was held down to initiate mouse gesture processing. For example, a Mouse Gesture condition of `Mouse Up` -> `Mouse Up` would match pressing any Mouse Gestures button, moving the mouse upwards, pausing momentarily, moving the mouse upwards again, and releasing the button. The condition `Smart Shift` -> `Mouse Down` -> `Back Button` would match pressing the Smart Shift button (provided that it is a Mouse Gestures button!), moving the mouse downwards, clicking the Back button (provided that it is diverted!), and then releasing the Smart Shift button. Directions and buttons can be mixed and chained together however you like. It's possible to create a `No-op` gesture by clicking 'Delete' on the initial Action when you first create the rule. This gesture will trigger when you simply click a Mouse Gestures button. ### Key modifiers `Modifiers` conditions take either a string or a sequence of strings, which can only be `Shift`, `Control`, `Alt`, and `Super`. Modifiers conditions are true if their argument is the current keyboard modifiers. ### Process focused `Process` conditions are true if the process for the focused input window or the window's Window manager class or instance name starts with their string argument. ### Window under cursor `MouseProcess` conditions are true if the process for the window under the mouse or the window's Window manager class or instance name starts with their string argument. ### Device notification and device active `Device` conditions are true if a particular device originated the notification. `Active` conditions are true if a particular device is active. `Device` and `Active` conditions take one argument, which is the serial number or unit ID of a device, as shown in Solaar's detail pane. Some older devices do not have a useful serial number or unit ID and so cannot be tested for by these conditions. ### Host `Host` conditions are true if the computers hostname starts with the condition's argument. ### Solaar device setting `Setting` conditions check the value of a Solaar setting on a device. `Setting` conditions take three or four arguments, depending on the setting: the Serial number or Unit ID of a device, as shown in Solaar's detail pane, or null for the device that initiated rule processing; the internal name of a setting (which can be found from solaar config \); one or two arguments for the setting. For settings that use keys or buttons as an argument the Logtech name can be used as shown in the Solaar main window for these settings, or the numeric value for the key or button. For settings that use gestures as an argument the internal name of the gesture is used, which can be found in the GESTURE2_GESTURES_LABELS structure in lib/logitech_receiver/settings_templates. For settings that need one of a set of names as an argument the name can be used or its internal integer value, as used in the Solaar config file. `Setting` conditions check device settings of devices, provided the device is on-line. The first arguments to the condition are the Serial number or Unit ID of a device, as shown in Solaar's detail pane, or null for the device that initiated rule processing; and the internal name of a setting (which can be found from solaar config \). Most simple settings take one extra argument, the value to check the setting value against. Range setting can also take two arguments, which form an inclusive range to check against. Other settings take two arguments, a key indicating which sub-setting to check and the value to check it against. For settings that use gestures as an argument the internal name of the gesture is used, which can be found in the GESTURE2_GESTURES_LABELS structure in lib/logitech_receiver/settings_templates. For boolean settings '~' can be used to toggle the setting. ### Test and TestBytes `Test` and `TestBytes` conditions are true if their test evaluates to true on the feature, report and data of the current notification. `TestBytes` conditions can return a number instead of a boolean. `TestBytes` conditions consist of a sequence of three or four integers and use the first two to select bytes of the notification data. Writing this kind of test condition is not trivial. Three-element `TestBytes` conditions are true if the selected bytes bit-wise AND with its third element is non-zero. The value of these test conditions is the result of the AND. Four-element `TestBytes` conditions are true if the selected bytes form a signed integer between the third and fourth elements. The value of these conditions is the signed value of the selected bytes if that is non-zero otherwise True. `Test` conditions are mnemonic shorthands for meaningful feature, report, and data combinations in notifications. A `crown_right` test is the rotation amount of a `CROWN` right rotation notification. A `crown_left` test is the rotation amount of a `CROWN` left rotation notification. A `crown_right_ratchet` test is the ratchet amount of a `CROWN` right ratchet rotation notification. A `crown_left_ratchet` test is the ratchet amount of a `CROWN` left ratchet rotation notification. A `crown_tap` test is true for a `CROWN` tap notification. A `crown_start_press` test is true for the start of a `CROWN` press notification. A `crown_stop_press` test is true for the end of a `CROWN` press notification. A `crown_pressed` test is true for a `CROWN` notification with the Crown pressed. A `thumb_wheel_up` test is the rotation amount of a `THUMB WHEEL` upward rotation notification. A `thumb_wheel_down` test is the rotation amount of a `THUMB WHEEL` downward rotation notification. `lowres_wheel_up`, `lowres_wheel_down`, `hires_wheel_up`, `hires_wheel_down` are the same but for `LOWRES WHEEL` and `HIRES WHEEL`. `True` and `False` tests return True and False, respectively. Solaar keeps track of the total signed displacement of the current thumb wheel movement. This displacement is reset when the thumb wheel is inactive. `thumb_wheel_up` and `thumb_wheel_down` tests take an optional integer parameter. With a parameter the test is only true if the current thumb wheel displacement is greater than the parameter. The displacement is then lessened by the amount of the parameter. ## Actions ### Key press A `KeyPress` action takes either the name of an X11 key symbol, such as "a", a list of X11 key symbols, such as "a" or "CTRL + A", or a two-element list with the first element as above and the second element one of `'click'`, `'depress'`, or `'release'` and executes key actions on a simulated keyboard to produce these symbols. Use separate `KeyPress` actions for multiple characters, i.e., don't use a single `KeyPress` like 'a+b'. The `KeyPress` action normally both depresses and releases (clicks) the keys, but can also just depress the keys or just release the keys. Use the depress or release options with extreme care, ensuring that the depressed keys are later released, otherwise it may become difficult to use your system. The keys are depressed in forward order and released in reverse order. If a key symbol can only be produced by a shfited or level 3 keypress, e.g., "A", then Solaar will add keypresses to produce that key symbol, e.g., simulating a left shift keypress to get "A" instead of "a". If a key symbol is not available in the current keymap or needs other shift-like keys, then Solaar cannot simulate it. Under X11 Solaar can determine the current key modifiers (shift, control, etc.). Any key symbols that correspond to these modifier keys are not depressed and released when clicking. So if the shift key is currently down on a keyboard Solaar will not bother to simulate a shift key. Under Wayland this check cannot be done so the net result of a `KeyPress` action that is not a `depress` or a `release` and that contains modifier keys might be to release the modifier keys. Simulating input in Linux is complex. Solaar has to try to determine which keyboard key corresponds to which input character as it cannot directly simulate inputting a key symbol. Unfortunately, this determination can go wrong in several ways and is more likely to go wrong under Wayland than under X11. ### Mouse scroll A `MouseScroll` action takes a sequence of two numbers and simulates a horizontal and vertical mouse scroll of these amounts. If the previous condition in the parent rule returns a number the scroll amounts are multiplied by this number. ### Mouse click A `MouseClick` action takes a mouse button name (`left`, `middle` or `right`) and a positive number or 'click', 'depress', or 'release'. The action simulates that number of clicks of the specified button or just one click, depress, or release of the button. A `MouseClick` action takes a mouse button name (`left`, `middle` or `right`) and a positive number, and simulates that number of clicks of the specified button. An `Execute` action takes a program and arguments and executes it asynchronously. ### Set setting A `Set` action changes a Solaar setting for a device, provided that the device is on-line. `Set` actions take three or four arguments, depending on the setting. The first two are the Serial number or Unit ID of a device, as shown in Solaar's detail pane, or null for the device that initiated rule processing; and the internal name of a setting (which can be found from `solaar config `). Simple settings take one extra argument, the value to set the setting to. For boolean settings `~` can be used to toggle the setting. Other simple settings take two extra arguments, a key indicating which sub-setting to set and the value to set it to. For settings that use gestures as an argument the internal name of the gesture is used, which can be found in the GESTURE2_GESTURES_LABELS structure in `lib/logitech_receiver/settings_templates`. All settings are supported. ### Later A `Later` action executes rule components later. `Later` actions take an integer delay in seconds between 1 and 100 followed by zero or more rule components that will be executed later. Processing of the rest of the rule continues immediately. ## Built-in Rules Solaar has a built-in rule, which is run after user-created rules and so can be overridden by user-created rules. This rule turns `Brightness Down` key press notifications into `XF86_MonBrightnessDown` key taps and `Brightness Up` key press notifications into `XF86_MonBrightnessUp` key taps. ## Example Solaar Rule File Solaar reads rules from a YAML configuration file (normally `~/.config/solaar/rules.yaml`). This file contains zero or more documents, each a rule. Here is a file with six rules: ``` %YAML 1.3 --- - Key: [M2, pressed] - Set: [198E3EB8, dpi, 3000] - Execute: [notify-send, Increased mouse speed] ... --- - Key: [Host Switch Channel 2, pressed] - Set: [43DAF041, change-host, 1] - Set: [198E3EB8, change-host, 1] - Execute: [notify-send, Switched to host 2] ... --- - MouseGesture: [Mouse Up, Mouse Down] - Execute: [notify-send, Locking] - Execute: xflock4 ... - Feature: CROWN - Process: quodlibet - Rule: [ Test: crown_start_press, KeyPress: XF86_AudioMute ] - Rule: [ Test: crown_pressed, Test: crown_right_ratchet, KeyPress: XF86_AudioNext ] - Rule: [ Test: crown_pressed, Test: crown_left_ratchet, KeyPress: XF86_AudioPrev ] - Rule: [ Test: crown_right_ratchet, KeyPress: XF86_AudioRaiseVolume ] - Rule: [ Test: crown_left_ratchet, KeyPress: XF86_AudioLowerVolume ] ... --- - Feature: THUMB WHEEL - Rule: [ Modifiers: Control, Test: thumb_wheel_up, MouseScroll: [-2, 0] ] - Rule: - Modifiers: Control - Test: thumb_wheel_down - MouseScroll: [-2, 0] - Rule: [ Or: [ Test: thumb_wheel_up, Test: thumb_wheel_down ], MouseScroll: [-1, 0] ] ... --- - Feature: LOWRES WHEEL - Rule: [ Or: [ Test: lowres_wheel_up, Test: lowres_wheel_down ], MouseScroll: [0, 2] ] ... ``` ## Button diversion example Here is an example showing how to divert the Back Button on an MX Master 3 so that pressing the button will initiate rule processing and a rule that triggers on this notification and switches the mouse to host 3 after popping up a simple notification. ![Solaar-divert-back](screenshots/Solaar-main-window-back-divert.png) ![Solaar-rule-back-host](screenshots/Solaar-rule-editor.png) Solaar-1.1.14/docs/screenshots/000077500000000000000000000000001473525213700163205ustar00rootroot00000000000000Solaar-1.1.14/docs/screenshots/Solaar-main-window-back-divert.png000066400000000000000000004567301473525213700247060ustar00rootroot00000000000000PNG  IHDR8(*aPsBIT|d IDATxwdGuäݙAM&rd!,0ddlccc`uۯc, +lQBIU]mޝݝᆪo V|g{oݪSUg})E U    ±27{ g    ´~:'sY{茷q    L{Y9$SrlG };<AAAA|>!*Dr<ψlHS9#AAAXv`k8q~TDAAA$ɨ23o1    ǂD_)=#AAAXx߀!/   0}j!*/lQ 2c8AAAA/ǏHAAAA8LQ^n6f3׶M5vV7&I}{   8 qlp]?-ދc0a_lp`.#C8qϣN;sk&[^f<Ҿݘwף5v&#yuf&aBTJOmAAAexJ8Kx|yFv^lT*8?<{F)61yTǜӮmd98li\_/O6V~>O'k{4G(1m%dmL602yy5K1+&J"#N t}mf!qńK6Ua䅟ODAAF9qx~ܚ7W a#//{ABEɂ:ͣfg1ۍ]1QBTea<<2Σ'G&B,U&^OQ80(lFT ֎fnM`twQ~zrogLlJuΪ DAAAx)ǐㅀ~ܚrkbj|2{7ͣfq7ϥvd:q]'5Vv'dS7|laRTP&j]:)&Q#jtrE6m"GV@ISVSf.q'nƀAAAgM;an2 mcZ׏)֌O0QT1QacьYi8 q\>}F`R Syj{Si8 Z>V1!Ӗhq!vIl2ٸ&*5O^i܃#jIdDνvT׽k/tu=Z9M7GSimvFMX1nV2ۏQAWhzfw1l#GDAEH(tA|`V("Ƒ=o-h\/g.(mZ;od2{qjM8V#44?vk6c˹}q<0ɨJNhvx&̬s)u::LD\r[v:IP ie$8»vJ2ƢX5!* +9€~.FTo~5|o[AAxPT";٭M[QjX4_7ۛ2 ƶ%7Zicgf?XL^:usRWĎ0H] vCjYAgъC5;qU- P_@1sQ)Y~zZ{ /mr 񖓗r"N6}.GΝlZGEH2j{  (qxLk**&oPziv6&"QU>ry]Ou <0y8D5iiO|.GQ|'mXe]P."]QDwmQWSLe34Q[#K{Cq7j2jp"^mʉw"}Jh{ {;?}UFcEP`| ,c|wc|f~|XbA+šAŊx4`Sqqe,G8-t'8+Y=f=yUl`Qq`ú>~u4dw8j/ urKWč 084frF{rI›?l+\񑵂  LTeڷ`HՎ[oBbF~dǺuіCijz=s5sf{( pT(˜ #úxfr;|-w\~y6I77i>i#uN 1 sBE\vYPtV}yRl;ܘtQ*S/Kz'J?$(lx֣GXTE,Tb;T dL=NčH%$raJS + E'|}ܸ֣&G[Ħ!*i4IZ8ۉHD@Pы =9Q_ [\f MMN%DEAhEW_7]yj;|wyt!h+nhWa<]. sm|gO3L l<t\2%]9 R.kAz sm<\r6o&{yӫ⊓,0DAx=Zk.nsq=ܸBhj'ک%+V;9rT"KU[Wc#^ c[- #3yDW b>Z*7zbbᬎ<$y'&ءUV"q}echTO:=81CM"jR62V)C8OB;L4w6(pG{[솰 f<6vk1F" "M]KAu#η]_KWTGsfss7ub_-Y837o]Ӷ3caź5sa;w}o?ws_C\sf~wɡ puA[֎%RE^sa~GaV1O-4!ي4`9R5d]bʌGp( p=Sce#('4T" ,@%v.F9tl*!*mUF4nt6ͣN2A V_ BkT_]-clqm$-E̬2K (>7 9^VBTA#|xŇ^E uqsѫg߮ܺew=y?7Vm#eBæjZs͜g 5dRsfO[PL2`\[(cP&A-=co{<.=.=^|q~n~S~9#s~֦j{aĎp=P'oXʼ;%Vor5I Sc1wAb-[Mlzk! LBѴDM"'?q z?>0TTo|DBl &PhM\V`wt3 ;1&&0$H2-&%y!:,*Bh< 51HҿXIG(D6F5Ƙ 6)P@V [d ðey<(Am<*Le=89Fkn9(CLNmنQdWX> 16$"rP&3$ Ӡzp\q1'pjJ9_k/5_ 7ܴ'2Zu7v<0v f98-ZNmD!jhQ9MnGK 0FM4(9MCC^"'?b43;xfj;4t8| 0  &|oROfxu1##TE*zDax8$]zC*=Q'KP[TҦ"Z#B9NԮ5v]GLP!DŽՄ S۱%kzݮcZRTWO[@LQt^T e&1M"ƘY -zr^Zw LRұ aL)l#;IPGBTAɉٿq>q>sU 9gBYrż57\\vf>O ͒0b?4XfSЅN9Uˢy98>u4ָ^Lڲ!;KRZi3= LT1VtLĩF!eVv{,p(Z;tDQHTu&6sʰc8 9jJ;TvQiJ4jp<0'Řg'G*fPe#>,5|.L&1T=/3 6Fő $^߷oPgy+eE|ŅkySuypXt d_89:fVJA$0sjZ)K^P/{jJAvijxb!tPC;±4q;Ν\ڸv;,KA?VkzIR1/eFk"E)?w%1N]JQ&;5$" 7 @GgAgyN]p<:JVJDGbtyaA1&6*Q=񄱭) b:zgdu=F?VlAAʋ0K΢xO)y#џR3T-:qEcX'!* 3R:ro?ȍwOrm(eh!5_msJhUYk:6"*! FNmsk&me" m&J6 ta;ѱ`::;WtYPP i] b(TnW9Mb6QC-lōdIj7 rlc,dD?2W9(^,%cLcĦ@UL4QC,pC0JvĔ*3+$pwX G\#;7F#3&85ԃCGA5;`|pCc. f9@k#x>fB7vۑI3ᣖ郡 QAf?+h㼵IcrʣڈÈUjM2勵Ppxڈ3iKAfGk r+0豠caKmE]Ukvۢv=fswL4~邺F@qq h7Rě.qu'lagO |M9<7:8a8=P +1K rWw77PX`*gVCLAR 'Kg62GsHak0OrؚP%bpC0ņ>Z0S(3fLi_ {um~k* Ĵ%jm::F9 Q eaƖ\Md:\PpBo1ɷY-Gɼ[>ю[{6\TE% G ١1z䂆vĘ:^5<8m҆~̒)F/>&fLMAavom\"^׫q?|;o}q}p?5Ρ*9⤕KWy>Z5%n|H=/N7Vx>׾7ߣ_6ɳ~F>8;؈M?O=Ѳoq9eE#||*~KRruyJk% GoUsrhp..6{/ |a0əgBjXb~F~J%4.UOtjH%6c3Dܚ 2籩7i&á$Daj=ly*)%?nQ{cL@g~r :{{cE"\-tL&A%?yY~=~mMo|F>¦{v)w1pa˪3_(UEwO~m;YΟԢc(yul|eo~=ј|gOl[nɲa>Gyt]9( ٷiF.|gGhkdǃ_]oŶbN|6bWk`ǔfSwvX,9.2˅]5kpn﷌PKt3l &n̺~?=wĄAElKSnXT7׳MbCvFio<EZ0LV aC[S7fmZ?kR&JƚI4Gz$N*x?m,>mlT=PsY!ECF9}̓;hA.< OPO94 =5\mg*#A'tZ;&Ux}8I8 <M8aA%r3g{\3n3<ƽ-ɜŘ֯$ӈ3#u=DTG3:,ɨUB. ףjᲀCٺ؎%.mwY*r5^8 WeݠL(!* Ա?86*;vee߶'Iedϑ-AAMAu¹yF"j$eNM-sf6B0@ &F$bDf| _/1AF׮us>8=pF!"Ag qEca MΔ^Y ݔ˜mmC6 sXs)1]͉Fsy< cs֝@tgV״ Z8"  Ʊv I/j!-VHF̨ &iY&ŁڎMcz\d)Y" ^F:^ݢn9ޯ{ʩvZIY(qۼ~zzr. FQCO F0wQ<`篢>IYݍExhժ6-Y(AAAM5lEY6F+wG<MċiuJXw=eB4Ȋz[[;VɃ{ ZGXm"DjLtlZDi3ukэ U/h!P5oZƼ8Zbcx /X4H !*   W0"v"LO蘬D$,_KTC(UyDx=q4u'|?V&B% =8$8.p}? 1Si:Th7UjC| <טTDBTAAA&i>҄J=׃RjUAeJyc;hfRCe^zsy%ΫZKTevi 3>A_c=AAAxN1lΪUXt)r A0<y]y\g6d    0 яo```-yͽˆ xqlj'H?7t۷o碋^836!   ´Hōw.CCC)?M7g>9pb7Mܵ~čIAAAAE W0 馛Z;7x#qbxxr̯ӟLZZ;#cCAg`C9p ^"} ‹{;dŊ!RVjbƎ;QJ!aFk(b uV8qIA)vdfiO;sZ0 v4G|mOZZZ毶o{abI,;~.I]vXMOP-2{qY"K:jK{jGE^5J?vRlc~J@]ʒ]Wp}\9sqn:v}?ߚ p,1PTصg]z)Q8J)Ȏ;,6{]q5=͈itTyzoGW\$ BU׿nV6=ѻ+Fk]+w˺_h7|P97`„hA$Q.7oRugdtzTHJQ &R}howO}k_+c\!f_&m';c>F?sͿ_afB9|?rˎqk9Y.=SOY@̣C6#{d Ln¦zҟg.y\[A^xXkqcCk fϞ.T* {b| }.tgwXgB5}/co\ =֓OZi-HVݷ1 )^NrZKV,: `I\{Z, ;1Psk&6f_ʇ* |W7~lhr[x|5R*䇻=l_+kW{'┓U{SVn.^t ]4~ eSi.8mFe}-kyx /TE< C(ìY=QV^ pe|ϝ$3s;G`8sT?Ry>W/^0oW ;~"goz@kkzrt~~%^+ <1tl\sK:ȑ۩=^Yީ>s¦Mq<*97' ӫx)dgTchX+~6"@-fqi;Kxzˇ;>ȗ7Norɗ˺v;Ò+61cNTMO(3xo\˒][xxv% GלE^2ah[F='lO<;)s,m =IbtT?g _i&G~~tsMl'GϤs+{%# fE|j&:[w '3̣vƎlmb~Q슱P)6=zhz6jly?O>=@"{2yLyO9ߩj713*=ÏPN,^u+W?l;PN^O͒U ̎!O|!G| A=J)D c Q|Eq B&zd1}z gtl^FoxGTb:i۵(8F;{l-Dˎk|{mM'_ý}Wff=+^n—x0 oVj"nbʮf"={Y;oTtR@=^NAn8Ҷ& wSle*?wQӨ4kNM'xӆmO[|s旼*WRoqҾͬzmWG W).XKQ8yvn(;^ŝc޻tewfg;c wp%xZ_sŖjT:f&lo|s[f!4C 8]v2V@?W\ȲsG?kѫA>}[0񯠗~fE'l_|]ӗOUΧU%>7>-ayeEvb:}>0py/?&n$8VKH23l ~-X ; ]Io5>sgs[n}M_p͡ز|N:m>m&紞Q 0!i @TP(4Q1{,| :KD&or QzfQψ5[:,YUW]řg}wY0a!e}WE ZؘA' Gk<yb?'bڻ䘟S4ϛ5 ||aP %ӺXS/W6YvūX~[Eb'Ot~ǦS_>fr.8>xO\'r:eҾlOq\plޟPڷ6eKAtܗ -O@ϐ[f]j*6_9q#o\ IDAT.8sQco(ūײzA'nn'nom_~TBzXl9+ΥUXSbmW^CEs4LlGo [}2g>_a+{_?~3SR"O\U\[)_̯7~Gaoc[PEXz>N9ei>-u?MH\,Xb.M,Mɱ]}Q]*8c)?|r6wY]L7%d["n(*۝_S_ G| AaʸnYdhhXkR)3:2RHJ֚ىY.6tvv.wx^ÕW^EE0+j}AKl=ODO'p7Q~R{:i\l?`b X},޿Ta|JG 9s"o8^U>'C\Utco8ȫ0[fq-H~Yce#wXu3:t6˩˚>kz>s=>̞x䜲:GCָթF'z'܋rۺY?4.|}7y+us^(>o>c#\{ǝ`@%A><3)k;[?} |}*pOWabГ_G]~]3}\L6zp'$ޔCl~3fN ?M9-t_1n8&:v䧧|=k oMt"n@-n޴W t}PSƔ{Jz׿UKr9~2nɛT+6A)UeVky999ȲLaa!@gV\ , AWʄZXXYr( enY4%jm{ \쨮KZn3}ԼŢSFy)S3zX$UEu)+d1r`n?rMos@͝.4K9|+ͅs1چ PWEEEhʲ)~?~,|}K*p: I4HCu#p:J_~Z<*$klfL}B? m@CΣM-\h,R c1H@3x4q=+ԣO, YHں3T U MoEjU\SϰmGFy } j\"GJvkR{Y?m V6JAAAx<f3ݺuc팿AP>D˅GQHMOs͢;8+xT[vW0^J|# gZ/vV QG Xf]qq2˟rP]'5bZWCH1Y]{!D8!7z*tuj̻¸9DFp82NX/1b8T, IFQ~<ÀIL=ULqs8`>BǾܟ/eh Wó MafڏF"^ꄨeBbf-5ijVr=V!&@cα:. NI(N.tsb>PP%`fJJZa5+t)й_<5Mђ`@2d`H$jpw|Lj.>>  Qf M2MLv6ǃi2Z}չ*Zu?@0С[/ ^w,˧nҰ}k]FIO!ͫ6Bh2~Dp: N'i9IҖ|e)-ߚ@Z: ģv*E$ART/ٞ2TOU<̡LC͜5GCwl6u9yy8Vv07ܑF25Lԧ s1چ Pkp8=v;>>>FnDZl^{C4 l=RPK/$lp8tt:Z(.oBYmlv_s C$̌Psl,Mj_mi2Nf.*P^2Y<17sPIr!bځ$X:,SӜsR0%٩9,4~Nۻ%X! 5$*$Jf}c{ nnʔa̹/pM?2i+\|Y&Xծ_]@ȄHu_^-T$B1F!dkr2>z(1HdBǟ#fܗ^%C0rvlh^6v!oՏQ8Nt-׌P:Nźy6i-Ji3)>Sc#9|8/NytRMÕ+޳9RĎR]LQuC% ~o^Wf[i=k2`/$!<`#L,С}Tɵ]ߜNm%r#6rB@׬3 A^WMP@M]Xzk ^$>' p]N6AZ) RdfeDQNgy6m3oEfo?Ov[>&ݟVnd_^5 t2W'XۇZ7GyJc(ݢyYizPeviIr 2_~MȀ<;p17ӆ21 ۶dC8|c)4 47[>/}m/l\V/jn OfRVcNsph:h BJd z=il,NWiIHHHy2HɓȒDAa!*,"??ݎ(5#GiNgÁmN{dv}' Ͷ <.~ӛFãW\V9adv|Y7P zzw&ڇ 3K&߃8\?2RIMsMlLQ8=Fei"㻇M" >:%Cs+dϿdAb2ևx/7vRES SԔu wj#nb->K6_OZyt.֟ s 8m|{ȟmt\dcUʆ Wߪ?(P rT@ߌ%t%{r$ <>%&&VN. 5{% =l*Y'd wп3#9}@7;0[} lC<ٶ{s]1e8Rh/Frn܂`nx}n^)f+:Ŀc{7dM-?*햡oёvynd:'ZJlI+Us scQbwl6ՀmZ>bh}%\77d=\,J[$K'R<΍iFbFn~ƼY>myaP/SH(W)JVsŌeŤh'PS8WvHzyYXOw\T@ߗ19\$ΟK]t\譔M0ǕR5ә>Yc['v矷VC W*~3_ғqwn ^>knDhmrcuiTzp_ǺJuGsO,tګC1kd?ıaVx^ԊrKVy[kDDmS#H~$ti^zV8bK lז _i:*VD %S;oݦmbx}}#ݧEKOJ\ZI/Oӡ=%@?$ 1n<F%ns EA!*瓜R2+¯:fIIxT#IgoϭEEfz.ws@;8E>4TVﳓ]Mt 6:(o60kI1=ny`&ͤR2K$ b|ooYWDfA\HT y@!^G=peDZx|{n .̦ifN咿p?=ڇ]Biv6'JUzIȁz!Ee 2<{-+tYϥdpTvz@$^.b Q$n fa՚QO|~">#o~u2Q&3!44tC\I~HVД~gNܑ-ܨ1:Pog+'Y3>` ߅P-CRp%a;,fuIzz,p> I(2!^ ^Bg.X֮/a@ ddTrPO,z i䏼3o jEi/^FWP"EM/q[?|՝'yq5OCxdG }OӐ-l=<(@f>5؇!rL]BwNFup*"񧠲iUlHF#7_avPY W%ѶO r1bNUX0CzO/Dp=ruBS!;х^ n J"{?KOӏM/tyHq w>T3h}6{xtXx5Bsq.>ͼ)3 3Wؿ Sה!'`?gdd aMCӄ IDAT࣓tonOQrmGQq>\9Q= KbǼȜeDPL%<MH&Wwy4Tj(ag5Ե7d׊kɍ!_&c;*BM7Ql6U/ϵ'|#;eU*%X2<=%ǐo͝0 ,O\sQL$t:qXVI:ypNBѪn7F"""'q pHaw!~.|hN&lVgj& {+ҿ_?Z8xٟ~m׮T:^x$M.ޟW >z䃔O6!l/8LU]CYYʹF̼#zK2ybR ]fImwDPDll YGϗ&C發_|r/&Өku6讔aޗL)›} lCbǦrrXDӄ`2%07">ѾLd(oG?jcoi'%N$=3-$R3|])qpM6ֻ&#u?LܳHg a\[X# H ?\ԥGYL(R|(I9Ċjי9m;u^ p%jm &6.OEaN.@|<#jB2)t 5ITP ; Yv GT3/i+:BXyˋE)&i2m85aQ5 - U]n^zwIxHOI#j QL4 K܅<]HL|4A%<An7<#8=N'MDxX]FNN99o6f,oū GCQhxOh_âqDC77nX? %Ưi{ os]_\PmpQaP#}scr6s.A5;DTYb7Э.:/wp 0?Mm˴F=ٚPNpd1s,I;xA͞}8]!I+$ )%\FlۂFfexƊИQ~iAAk y1XTָ٦'#[ۀO7-Fken}! /\"""ь'дu3";tӷys^r<2-lC^hG Ά% cO3_d57S^I巷eI2!u⮗_垄ڧ˪w&j׾5GսJAr-A mӋN>i5ģH?oф P8/}9Qr-?ZEؕBN$@{(NivjRI_\|i1jFuoJ0yݸ_6eKOt_aެ;}?:tOEq^1N oUOK' Kˈj?ȂRGKF8Ai^ZtE[9l xpP * ԑ#q ؕӉit Ҁ{PI^-~îטM9ZZff}|b4O`0hxDL9PTV "@&\ +gߧrJr"F}Wutɱof >? *ewo1c[?¡etUGK,1tO@6豞`J-DӮ'{O-uv&G0Sڡ iG6q9ki9x)q \d\?Xv9Pec$Rsmu>5jJ6D[*mjL6i2NPSH"T,psjރ$cWE6cD hM-K$-bgD>MoAHX"\{9AFir ?o:>N3$q4 !VLsԥ./7!IzوTڂ4aYu/yCxG=, KYk2?@%~;{om79Op䤂E+Z ѪA3([Jqt~gkutCxl^s߃\7k9{[$łw$ 99 @Ydt:u_.?CZUB0gx֥C[q4o@"}yČ{/dMcޤ%?*j>[_lM@0'xV({\xaB^}s4Fc:Q%PS}3٫qTZ=:¾ 7E~O|x9&93;}]TUE4̓VzVÓWtD |9{o@b'{Ŧ4ً|7Ȳ5yv[[t^'chV0HyP(:w"ʗ;)yNf çA8vs)0Z\OubƸ09RyOyvZ_?ۯ亥c(VL}l^&LVSD3q<>. ׊y,͸r͌KljKu4Y|pKےшA?ZΉ O,Z-Y; !eyJjq$?z MXfsQUݨB굜P$1n:)Fxf[j^Re^jqd c!-!P|q:zS7+62d &J)o*L'%ӎ؈&a$wa&i9(z?wUZ1HOˢ_G[b4IOSgpG6mnL p{;ч6Q3UGۻe)=[S,?-W3\{|OVaA" ͈ J)%|4u17͈#KU׌[^~ZҽLy-K,(a8cmJNL![H||N}^#]v1nӡ;}Z͝ƾ/^G":ͽNe=Yw錹1M5?VŃ+R_C| c` lcն" Z9ƊէQ$݆"Z~_#UU4 USʓ6l/(df fI",<D,TR\?Z\DJy"M?Яk_G|{2%ۗ-|AqmHG$. }+ur+3|$]??̖+x&)R9z W?"vFt:&?˙m\47V'E u7D]^&4MCT<%zPU џNI%@QP곞)g~xi Їa}{3e!g ϶bi=Bw h֞x+65[?Ӫڝ_$Pm޿c>]Q.'??&]Y6E-5G= ]-FtܶQ%^<~h6M{櫇{|!2gnѓ^t:PSؐt>H򈯓u*#Hj.G2g])ѹs;|Ϳ8l-So޶xkW|o(J0^Mj8:ֳϚ#E ǟtpפY QAljops9D$M66kJMGC %Br";'` &H*$D0ϊ*,`H޹'-syqRvchǕw6Xy't9)<AA?==-T&VsS֕bc+_l^Ewq0)'nmRsJ'x[~~k>, L{W,dl9l[eMBürUka ٽ ǧo=aq37i'+1lw:ȊrqgGBUU4*'-qz z < .w͋)N%EoCG֍Ov~cI.bdGS0vBuCR&vgHW3iYx-3RH~ƅTK/&r$1&VUs3o(cVޖŰap6m▘F%ZqSu'̘6C1}w{:x?Mmɋ!қ2հa,Яo; vppI<ۡwf<4~\u#0=Sfێ( -Н51 ɷ3*שZE.4%L6F7F.8eKYyn}{3gbe'fFm IDAT` u>hJ^oܥu7%h T [M度^Del@_ ('ֲ꤂.zc:] 9>]3msm #P?X"GgD ! P USdXM=8$ST\SENeoɷ?`\$ p[?ƷϿ0dB]F"#Ð4Τ)ԜF%?׊&ER2F#e4P(ْ9%u_`)iVt(9ٲg=Na6Z{V$鉉mT2"״Qc H%@WLr8s@" : y'}WQܚ!z'!$( Sػ]W"wdJM:{mG( $H3gΜ{ΜwNE#64Dtp#8e>ݼ~.~?G*\4xj6+[/qk4}+H1pOw#\Ź??mn5j,*EE$!EBAߙXYN5v6̛6Ԉ<?cm!/ODIA:{u8u/+6ʃKgf$vzpVjtu.ngc2V8?IfP9eٖ.[JJdl攠&9p`@w%WPws[:%{<y16MWzZ EϥxA"5vbkv@1N\o=kNxe8:gy㈛UƜK-QCG/cAdƂNÎ0L_6kA1[0Sa#)kc{V'<0&XNCtCYc|i1[qN鍗zU:,=WWR)^x(*Q?A3c?'1)̪dǭI)N6އr!15Gk>*-X)7q(BB⠺`Е5h3&փNYN6UgcejݛujZ gL?CS#F26KaɚrƎHhΜ?{NvY!zspG=3}6B-[0ХWݯQ6?yOXI.& F p97]': L ʼn(XP;\ks{SՋY9鈠Kd)I05&Ix>S04YNY!*# KYv K4<eL)=jHsE`EZ9evh. ]:܃u{p`DU5YQ~U_t')C-]~ mϿ>߅e ?>޶`UEu8)/BW >^ XE+eexts&w(X&/VB1+Y7^'2G@$+䨡:9*aait$:@E˾c(̤B3vjQ9õ=ٲurΠkr)k1uO3zf6]lOLC~ B+Rx_q:Ԭb[nơX=by)庂4v85/VoR)x]>6!.=-é3?z/ʂUAR\s1 sa((g*N$S538ض^|1nVL7|s0j-__D `DBYIf9c', B\8]u`8˖kɪSz6va01Zm~ǰW?gi;:bڇV6HI+~ t z Y-{oƈp~Xx`ߞe9G>~7Qs)_ ﮗݰ%Nj_Pʡqfl=Iydd^ª[p*ةnYA(u,XVP?}/يɰĨ&=x7x=SwWg&^c]|gxvQ߃m_~*%ġ=j7JGJ绯7RYgs`/́БH%_Y{Yu~ۍDvL?9؛9sSkƆpiqxB))ok*J+q頨xu'|d^A5Q-)dF)@eMni*n:}~ܹf`{ruʹ?$ ݩ,^Jf sM?7+ ?$'5&$=bv0`bBS7ԇ勖"'544O->V[WRRBYi֞T ꏎNYi%%%8zkdh\ ~׬!!\sAu`6wˤc#`(gqn/!_8/};[Jh([رe; CZ0n_wѲh\El_|iL;xZ6?+;s WRFir45Eqc_wF hڤ-b%kzM m?+1uKJ•3l V c.Ǫo?NMyV=fȨ)~t)"͔\gl?֛Mmy$pՃT޾~^2!Ѿ%Y7,n#{>PY({[_=IXJ/揲JoTn?VsC3^Zӈ;+:,)dy=7h*7eoϖ8ө)#Οh+#]4j{ch7|!ĵ F׌D59v ~^lc] !"~}gyJMLP)<t{㹙~zqj}IvMNY;N^Ƴ/c|ѷ+zw>ݻ8' r4Yz.f^/FSL1L{%LoΜsxaAm3l ?q/ n;W<=7my}<߸C ջ8{3(w&*Rﭖ??z˴*ؗS DcUR]Efv)պH:4@T%kN0k7um򌁭$i?֩QRRBqq1+W"??ʊJ4Mׇ&%C8}JQ"ܻ)ݯyR ˜xJd[v.On7ݮRxN? ͣ(+HU8ּz`Feʷq|sίD3;?Q'8f<*&"2 ʂ\rKm(D4T> 9[HDVd5Tc s(1XGH" s(!=8 z+ 5JLl#糃G#0Qid-:񓸰 n9pS:tnnXoXX,h} րtxkGgWrΏ/fĴS3'tb/,ңyy<Կہߦo {y;ݬr6  K3^GQZuHT^ $!H{!Ne<;;j]3XQR^_ ǧ7s!X~k?h׫vv7_dmבݎ !&=8BiZj>v=ٹcfrf^7w^!JȆyb?Ͽ[Tng?EĔodL.*JB!4x?~>=.#WGA >g7 X.:v>D9(3㦧Ϧ^A3ÄBinWּ1gq)B*sE\!*ݒtC4ÐZ,BMM+OS'B!b5B!B!= p!B!ݓB!B!= p!B!ݓB!B!= p!B!ݓB!M ӝ!B3ӝ!t'^'pזvY]z%3q9]x2y((d/p\\.;vdڴ+NgT2!h3BjG t 6W`GV9pݰ)q531f+gO-9YsoI#W'Q}wJ.WX|1˾=maa!IC 8ʪP||jN'mǢiS^Z;ᅪ`0ɾW?š! ,M;B5e՘W."vf87:vG]\{s> PT(F972Gd8FQe_^^NP`+IlpT/@7hsch'Cq9;}0 ($ZU> 5"'E!!mEBR_,}ZR*iiMދ )Eh nS.mNP>Y7ϻ>B:S~鲥xzxqs>Q`p}ׁvҐb3)_!B9 6)5YVzi*9Q|UlY~UKނ]WObe@)c4ע4u=PAVA/FSU-m ;sJwDx?w'=^-[ٕSD]LtӚ-?T-̡Bߡ;q|te8Wi;s* 4&fZtt@L^R;*0̹>z5W`iNjQ [,i's-7zY;7S33}S!w> ^a1D#<È,e lWMxL_ T"p3+9rwo#̀%0zïܺ 27cCFUa1ܯ^!B48-R#:$0%2H[F5fizVmsv\s[2ɥ24!p=Glnn`K9bb<\6WaS<F@+a?ɨ49T(F]^p0IO$y%,wo*M3"ز}NF>#|t!5೧s/0w}U0XK{ 1死O95j%+>;)wn[,|QZ]8sY>%^jy:L1 >z##ԱƯL|9K_=5 )b4aT@?a}87?D3/Xf|Cۗ_^}t(3N$4Nun?3j,CɰGG~ (Rw&6ݬnz μGF 34&Jv?`c;wS{^_sߓz!Kᢠ#Y' ^3kկ 8KS8MJU|Om'7 h$v<Of-b/a*C xFFݽ[K|ˁ9\{݅ -o;˾MB $!m[_\9p WF2k5 59htRR٧"ІZ.k@6Pm~sng+_f^AGV7a^ql~=E.jx #mw'JV"HϫWFיT*Qcr۽|?v]EϞ79Tp^Ȃۙ@bD+X{#S='Ny ;3o)G1Ј2q(_O)_G-˴{ K6NK/n1b#zbA ~iQuxל3qQ?1w:~YK"OedvX<\,?+ naˈ9}ٵ+>[;LJz6` #*Y0{{ 㹨Ns,uʖw3^zٽ'';xJBJ2dX/ }:J8BK%$.0tD\kO2uV>KRY j0I]6R;@+ϫnc<rEU:݌5LVfzZeNtbة;#C5f6N<ݲnE*=:(g̔B!Z8Ͳ2 imRY/!uN1{8Yp3jC :<{5kN|-Mhܿ96HXs$ IDAT DlۼH#!ҧw8yRWo4Q,׽+>x(@ 6{#-}o?Ƌ0WLMq^o8VAw(U?EA&kڬBc˶B4 Dα ts+vn{𭆮9T\@~uCKgSoXF{'6Pzt @Ed>JN8r,vA@TX<ͣG/Hb!@n'Bцy%2c)˫6zXj7@BC +h ik6R5n^H[FHf6rK!GдB jf|8W.h8 ,-AGVORq;=#z< [ o:O|`ޡXeߝ}Nwwh𨎆ۣl$كܟ^ (mFZe@tzp/0hm@{iڬB+$P5+\s4f,u37Pqe=8`F]j$Oa Vj@?T*J)!xv,RKIoe;!_xU%AOzPR 6=_xu*'8wf])baЁz m4c<ѡ;;tt?3Dq5vkxast& %PŁ+~{==g^h3M|2 ?F;xjEs!57e∩D+`GXv18>CoV.tv{8IWjڽ. 2#Ls#1qbu,u;6ǡ.*!-'=8-Sc&zR@ VCe mo3\$RIP}wxy7)V<"',ݖgxdU"vmLa͚o Xȫ/ L;ߝ} ]%O^O"uk<ׯ+L&n)k1T|Z2;k{/<ֶ뭦Ҥr}m<:55hxڛ$q3_VW0l;^H14:fH~0X-XNT0frpfG!8TQB= !E%"1.peldSI6:YGЉT֧呿a{] i`l[ t 5$@{gK ]۟bGNFYщN٧-SC'쿦3,k0wtgR7Hcg`˩P<=!Pmt'לv}=5P";+:;m؜n4=?&UKRz1~LF( hR4MYCFZHIIa1 >6=sf}%*# KYv K4<eqzέllkٜ^Jt. NY/{F/T46St̅/9L p!hBЅ !Z9n`aKxXz|VnbcyD=\_3s¶^|1nVLߙyWxksf]1>Yw?1&k>6l^hHp pi3- EU12uk$͆r@ڦ c67g5֮'bת-o}gً}0wh7/ߒ?Ԩ(RFP,A d6/Sp\ &Nj7wRY\b${c(ɟKV]ÚDS|>~$AHNN jLH9n5r(g)_p~*ŻVF:KS|&7o4o^e5 7eC뼑 R%T%N⼍]vBVHC!KDfAbΐAL73uqDV75If[aYO|r#E#mhҏnx+ ~wM&C)a p=8>4lb~[^ΓoƙY*ӻ7suӉ ( *?̊r 11(55hGVSChh(]cp8PUv}=3.{w?9?f|jX8/0.nR<cl/|>j,O<e^BgoQyp2O̠Gn_6cؗo3,q~YN\?fe3ɟccXx۷."B4N /6r[qzZ*qI#NEJ]^Nw6N,2K֍E1ܧe)ONz*/ʥLPqgN(((:#bJ˨) ǛBhh(Axyy.U֯'̷a,npETGN彏nػ7aфz4އUǾJ "1W* OB :BDo4QN!D xF=04g+$!9;0 xzx` ? hjjj,b6ZXZX,f3&YSDwgxkSxOc&n9Z1u_&c %&x[W&tmZBB!N:EQ0  V+,3:4Lf ~&\!uIC!Vٷ60C !g6 p!BQz&~|>{\ Gï\=oYUU!88B!(C+yk\fB4m\)Ff?@< !8C-O!REF0bcNJBJB!B!D'!B!B{B!B!D'sp!D} B!im%!mPs~ȅB!2DE!B!B!B!D'!B!B{B!B!D'!B!B{B!B!D'!B!B{ӝ!B!Lr(*ȣ~Lf3F/o}X=NO p!B!idgeRQVX=08vl5ՔSQV` a1[֌4AEY) ,"o_$!B!*+)"@ӿꁯ .,vlHN1}lU5`֦KxdedeRKN̖i#sp!B!HQ~YtFDT&0tDtl7UKמ} "cv*Z5SEzp!85ՠ< :rj陸²j4 o̧;?9ehB.QTOl^-fMl^ޱ EQ jaz45Mc ݛAIi .@:w n`ȩ̚{ }{jG t 4^qYh?bjt*w ֐q⚿HDf~qwmU+oT f+>!X1+gO-ќc!=B!ڟ rnbXڝbu?xӕݻܵ&z yOX=<@QU rG\ptСU.s!ht*wl*cVT+r=73!֣_$xzY5{i0>g'Nc/Mj8ny4/:gp972LksEAϸ? C"lR)*FUESU6\鴪|jDS_!_G^V&ZeՃQdM[!wy{с cip;EJveCsi۵ a'V!!hêqRǣ@.}NI;We87K~yqxѫI ߧ|,Zt3\w{#Ԁ!Ey!f`pHZQRXPʋlܴDdd$W"$8!Mf22vZ5@֭O%qHb,!h~lxK=I\3m/ en`ܗ&ǫM?>ަld:[^ʪmN?|*زv{L,u[ I >^9a^}Lg.c22"U,,4{$BB""Z=f8)Ȏdgs+Ȩd $8q;Yh ;vفY_ p!D[UNVٗ^xlp CPuvs~NKf}-Xڵ1ؿ[MM]VS5EEDu"';AKLW,[DLNJ~%!mKx= Ϻ9ex ᡘ?Gns8żylQæyd/gkߤOP |'t6Q'1F0tHICaJ2;\q7zFښ413xxOֳjgȹ-R ]Czf<+ȼ GQ$ug$JH(Qw;iz\SwnAɟQڣ8Rfsst6{M6|p?/(?N>{>C%j|v{FUiq =Յ_{sPz .=B !(.,8)C||(//=)?Jਮj1SV^I`P1QQ^jU*!h,1th`2~_&>Byl>Ll&;XZf͠/b;)kwUgu{S35 gXiGu|;kd3%>^I}wa7Mchbv'̼Y;_+r~'J90v ߗݏʉ돻eԜl$v<Of-b/a*C xFLvV& ',懓b:{}rV֌oHxr:>B!W]]EPHIKǏBCNJ^~d7y>ϪůcPS]{BsU[ttHADJIe֋CjMޏ Clpjdy絋9TtCHXÕǁ2R:ҹnlNL}5td~}e{|_䢐wJ3'_dW}ezzLq޹6. ĈJ^^{x.8q6YUp^ȂۙUΐ@bD+X{:#OsZ,X,VIKlۛK\\xz TU٨@1(y|- pU!,sWY'>s_gRjh~[J <&qq3\z%Ltv?zB+ϫnkE)LfZ?ϣ&T݌,R E]GaRRt. PqI&9{{$;Ե^ ΄/P{ 9{(M=NHʵST&w3׬? "pwSDXx0*:ZQ!EǮT\qO ȝl-ߺ΄k'6PgrtBqFu 7?lՌDsM&\Φ]N&DFxt"Go0ށEho!Ax ь4-$ 3rƢ?ְfvr*+J_MVj~?XB3᚛eB/Pc˶B4 Dα[T:4rhcw{oi9o+ECc4A^~ ޱ2ʀAxMkHy^Bڝ8Uc0&q'.Ϊue\2ڐ&^ ?t(g/| `;4GǦ4 t\j$ng8bqw? cmG48i_F-M [t_&1㡀NBqF5#NIy`u|z%4V#:n975x{Y2ZW p!D;zF2dµ p-h5ƴMᏵ[ׯǺ=]kZ!y`?@ODrtKUH4CARs ,M6b lk  ˢ5Hոxa#mmպw\"(/<pBE=ȡ?iԾq\p8KKѱSA>eulw`iI.7Y+owOW !gp'h8 Y-r043}Gum>J؍ jj*V]I*<:u!hoT+a]}<|3-?&ld|6m?ah401 " IDATL4j[xӉ$82h$فmNa} {&t7TO<|g[BX@\ZEPAk׵mV_+[[7j]j]RQd IHB %@}O>#B\_hȝ9sfIgyN-h3sX..rK\sz,Z={Pvp5:wѾO27B!NubS/ta0:n c(k{@Pt7ҜMsyh;> uBq3'_ͮ-UhP+>V͎G+V s̡y<͎(V+視dL1|J$ڳri 6n'FN%=@|ryH7U'0+sbh01ĝyIլB7(&MM>>5| -{C5p:X};&M^uM2C!$' [wYShNGGGx5Hࡲ2t7 5(ল(# TU֢`ĩ*1I4gV +A3Ēw ΐ@zjjQ0%M'cֱ5D3,҈ʊ꾏vAoeQfqѬF[8C} !eokm& 0poonϿ{.#e!="Ou;޼2(fu-2iR4{Doncys_@ g؃eAf~b"iR2>}9]/̈3#5t*=IJLfgA.Y[w N@ &OIFE&{kn"Pny*4ŇW^Y' \-)1ڷBpZ֖fF~HC!$_*~~o2{_Qeum:j\23)_g Q $ռf&Lo ; VwEE_ee0FcHdfzʖ?!߮?%񽌢LLcl[?tC<33b~*#.4_Ͼ<.:zl1O<nVw~Fiץts-+CgOu }Yuo! /ɌbZ[QkޕKC?|ՔhwKK!,IW/X7mX=&vL2IãQؚ)EAQ%mP&roonO{&|>MaVr8|F[!hϸ{^;jj%wn~ Mr K0D18?tH1PJSuo2L|e:,#6}!ޙmK׳sulIAmq_}CD۹f;1.~#{qLs6N%mt:)/Aww{jiið!ғ8b20a^M>w/6lp36QT|#rΏ.drX?f\(/γ|J敼 D3 د>QW<Ƌ]enZ_9\wM,N;2˒2t|ޢƒ1(IC0Re'nT"g0nFeoemΧ3%E'".}co;:DQ}B5Ii8 pSoՋleӓ_Ro=+)XR*rjB!B#in)M髺* 躎墦@G6c*mVTkFk fG;VӆbvRj0PSSCTT$Eߧ%?(OJ:~@!~hr6%yRhb'$2Pki*%ac6hM[U)m8U+ąXfl8EpK~#oϵm^CIU; TڷBFbFMb=,N❅$ŋB=3SWP픗WGP#(]rhŠ~p@׉O^Ĵׯ;bB6h!BqZk1Λt)UDtl\μe0,a>~}O4N'6 yLf3V/>>>fTo8dB!BqGgg! cjG{;w?(*j+PUՊbQ;-; p!B!'IhxF{vJTL㱻rKe[2TEeĨq'5@$!B!BDSUQF<  bؕrbuD[k3!aMx`x<.'NQ>%$!B!Bd3l88j*/tҹX~(+gjkOjd6c4'>! $*=$!B!B"£bNuUN{ҙB!B!N{B!B!iOB!B!8IC!B!= p!B!'!B!B$!B!BӞTW@!B!!sm57ފtiA;MfC `0 N p!B!@[K3Օdd6'pTUЀP!X>Mf.'FF@`q(;CB!B!Ir:(ݏ%?p@omn|>BB2 ȮC`p(rvFTlZEB!B!Ibkol^"cc ꨪ,eĨ1iHl|a/ޅ"t6$!pRbG7wtJ6[lhAaOu}f(^d^y/B􁭽bG z56}56]E'x['/L܅;5 eLNr90@?+G>GYMN ٗFMbtu/,^-n/v y+$FZ;dBo`Wy+!Qu*hr5b#C͸__}BcEkSwkpc !8=9J?rp$.qbeYꏯWh{Xr/tb,4Ekp-f /{Y520t%`Rԑc4!V)B] -M(BHxĀAC] u5Gy??_o Ҋ^ԦWVWT܃:: ^} M n,!*>ym !06rFmxY'{>,c߿`/"{]!8m:Mɱoecg9Soac% oBۂ9 `Kve I~Q?N6cAV7,}:7{ܶ@o&B!N35UƏ+r#c(sýg^FC_ -zuEu}n2jm߹s1^+!pӋ nt1[_eD4 %LzDM UM8~<;naws EԴ`W,E'yb2#oΌQr ټ癩GCGn[ 'c۬"6}{l(`S ]12#=Tm&gw5mqNM1jj򷳻 f&8:Ss <3vpœ5qio;(޾=U t8U, 38c6TȄ0uZ)]An%bD=ocC]S]\DaKh<##|PFDKԨ?u׻;{hTm!gwmرNC̀+LMqm.*L@`б7xhN@hnAinAQUPΏTzH+75cg7(^$B YnPPc,P?^9Zw5k'G[DؤK9' =wͭ;]TR9džiS1q9ne'g^!wQYF33),F#\=niP^NK̙ă\أ{5坧߱CQP}ur xߠRI0&onzB%~3u{ Z2n>ZQ/;\}D>{q^= grQ?UBqikn&0(dommA457XO}2TUEQ_T]AU`Ht͝JC!*H35~)W)z=?D Ia92v;uޕB11)gK(y$7Xt A/IqJIj*r{{ x;FNO'VfT#eD{rь:!Iu#1I=xR7+ #3{hTg Y|LE(Š7Yg 8xx/ك7oZ̼ )ZeS~}h䀷Z[;:M<Ž~Eu8g_y)MFM[e>^g[ᡇ,$pۜQ䁑jy7,"#Ȁߨ8T)[b4aT@o;WY| EUﱱj]F !FdLBHXDynق(Ҋ`5`،bƶ9KBbHҫGPOnz=9sf:>0{4g-ܲt)׍:x:gx3^2w͑4b2RyF&u[cՑoo5a wM=8c\J*3ƘWQK0 8fߧv?jnD}YdjRI47u*qBʳs(ђy`CYex00<#Vkoŧ/cKav\!;cCOѕlҽ]~1c{o4!3PUe|q픞;g.b#=/u=vA٨/'ᕕh?”:kQ0{ BXyJ|7nL: 6D3ڋziSHlB!"ۅ8x<.'o0r,΂m(f3S:Ch]GQVy^UFp!Đè 9C_z]&y;jop兛DNb57sdPa{Q=Ǎ-* sn9G9q,iiU52lxB{ 1OB-rZ +gLi/$6s}ͻq`)X[Z|n0 БMSG IcVr 8&5Vvr*h5֡a`ĤP㘔ZTKѶݸ.~^]\Bftkb ' b=j()1䕲FwdȲ>tڜe @nmP!Đx0x0Fu#}hOgP]C4p;}vw%%^%R}Ș-Nu6ɺ;cɖ]:;Z=5`$84(Kb #gTSOmWᑽ$@xX(*h 4 Jg7u:S}ְ#MͰ@G9N!+`YI~V:枅7cUS3(oGԃn0v/ĎDZ4f<#v?js6uWi6(bH2 0 =nL4Jqs!i*Lf3zQrpz/f\--X&xUW p!F5:iwٵ,i*dS7[7޺`3h4?a4c@+'|7KŶo[GA%w:mC!R#Di*0x\A px.8~371uҵ:t8,q{lڊgyUW p!iO}7 䣺rTCUXt6;'xIba9{xfbJs ϧTK=+&iSvjT 'wtSY5yR{;/(V|,,[ xb,Ϟs=LyWyW;س5wzvZ_~ՌY6id(!8eL&&58v>U=~pƜ:^ju]^~] n⬫%$}Wu$B1$9i(Ww WΨI\PY^kہݏjqQMeEQFEC3 S)RbhϾ4UWPf%=-!TO5ߣ`JN@)jfX7}e~ouHO%?̶mdXB=m{xK IDAT?py5Vuos]ǃ57<!"Ty eokm]pJH( 4MǃjX(&pX"$!C7'_䩿`[;((b&nXגj(&a@*;G<|6f7ٿ^IC_j8',so=62;IzI`FUDnv{=SHHRdU7; rں n`t"N0yJ4*27Qm#M䖵z`Ry WT|e9woQϞܵ\{o ďX6g`P@oj4d8Oa!" qiomi&4,)1oN[m>Â{L{u z<B!ɗ3~D3jg[:6/MG ;Kft 70 `U]>*{]WW̄im s3.𾭃_ee0FcHdfzʖ?!߮?%,:ad+ض~xff`TF\pi }y]t±cx7z<<2;n>`C–nz5|_=£$p%]1?<6g@,k?<64ÑЙ@]C_x !( ( D QZQ i~mUhAA(ahp_JkY9wߍqSXKrp!eIQƒl26cIELM. *i4{;g꘩ M|kןìp BԟqwK -/܉1ؓ+7ab=q~`'0>קd2ƙv uX:3F l/5B3ۖg?ٜ10:RsD 7T"ƭ_r>+;Ijܸc~6qd514:J_5Kܘry@9uď`VGMg1>܄oW`Ypzunq %p8 !*U#O nzQ!Jdt,U奌Nnu_]UI@PȀ[SUApHxuۍƗ9! tμSՂ%p5hW_n׷#!${Wݷ> ()LyaM߈y Ϻ1W< +y%of(>L:~qy$lfbWVEc&,itӎ,L'%h(dL=JP5i#0lى𻠑eYu|)zIQHo&sx_w]7?ϖygIy[gŏS/`rT(ܸOtۜ:E55++  O 3?Ë$D|.}YOWqxBS?0jj CO'鋆t]Wyuo•AłcD8(ik֎ٌg04Gp(IG|U-ܸ0?-"?9ג<)u ]VRJCCClCpZUFy};nKÆl>޲RJkpVBc JG [C9t#aq N%o좽:<*> AlsT200KPR!DӇ]B!N7.}w8 MFB&wM9,dX(|`:GphvZqc~'$HIAOIA=L| $((??L&SGp iHC!ؠ8B!iFbGƿy-2njzYZN"B#= ˅anaq88NuTՀloחbd2jtp!B!$gXB"{;UCu=; IHhogN"&f3fc`HC!B!8I||1z,i#*nAV~HX8= %*&]MuE9 D#p,B!B!N"B¨4SYZB_` fٌz[`P0~'R]QN| \Rԙ]rbuD[k!١$!B!BAiiiۅD<vz;tGEd6B *C8B!BSlnS];D!B!8B!BqړB!B!N{B!B!iOB!B!8IC!B!= p!B!'!B!B$!B!BӞ8B!BqړB!B!N{B!B!ix+ B!8V+hhu€5 (~u4RQ]KSj7 HIBB!Bi|7[яbs8رi-msK1SdvZ<~2\GB!BxGoǬڄ[!jlcケ+vmg%9XǢa90(Ckb몏XSbS0#Ǐ#!F}iۋ؛98sŬX̧$ p!B!&܊c\%?Oyr 15 c/̆;)3cZP))7VPCє&t!ɀ/!B!4jECdfѹP ? ண<ʰ~S= )O-{AFp!B!ڱ6`5ܞ YhF^;$ɊA%! p!B!uHCbdd=jjj1Pt. p!B!<'rcHSBѸVQL%_o!FB!BѼs 61E2*}v7Fj;XC5}* !DIC!B1ptu}A{>v8h(ϾO|df n!AB!BQs[P2kﱗnT"vOHv{p[-/f{ak7CB8B!'LW*6@ ɏfBPLEt%*fX&%_-_İ !~$*B!8!u}w;ĦrO0/0$6EqQ+!pRbG7wtJ6[lhAaOu}f(^pB@j2?]ŖZ7by)]T I U,V+ v'.d'MۚhjnJlt,S0Mvg!!~q4c}40} a$FGZ69gp깷-ڱST,!d̹믺X?z(ӒG,b͎6NCpPC364e]܅-a:6+-!NC٪OTT$+AhG3jt"QS!l!lo%]'v ;# |j>{Y<{x[#},k3qLv %U9k?6e7pe|x/\tdSg2&[L:7qR[]YR6~ '=ou7b~X!LSVg%Mcs8TTB'ŌOObOdqapRSNy  fp裏B ][ b6cӆDGdf߾lg$4<-9LϘ~ǔB UZ6v?q9=`,^ϓ?=_0)Mgi39ls0̞6Qhoa--C;ؒ`4i?F.'me'U >F=n[-1TD!΃5c[.C4:2B2ܶt)0qә9)!JKe}DG1_Kkb(cJC!*{!9;j g#] 3yW1:.e i?y;(m &>i2SG1Wo`gE+F3:>wUv3N&[o\B~~%5- AÏEF3c6r6oyfБVBy6+ȵl: ؔkCWLH#gOu;U۳]Mf!$nSFv1Ӛj¦ Ndq <3vpœ5qixϋQRЁ`%8j)=v&CjPFTQbȈ0!Kcₛ@FT {[-N<_ A!HGSUYIiG8r4_ 45'BGB!ˍә6^QխR?^9Zw5k'G[DؤK9' =wͭ[axߝ|hm?w'͗V8λaAF B`g0%<:mh2džuk5]߱@<<+ ~ge/F V5_]3ȤnVFpd߃q)c_}D/ n7LlΪQ;}YdjRI47u*qBʳs(ђy`CYex00<#\N;F΋O_.-\C*v~dž2+ {b=4L="iB:gyo,oll-)LlS)涡pCӒg0u=?;<ڍWmx?&ᢘv>_0a<.$B!D_gctsu%?Pg$[RQ}L^7/)=w*\1Ft{._C;f8WV/ S]uO??z\arx|W^JV6B!8. Oq1Y #1GD(Ǘ]MD~Ap{GFp!Đè 9C_z]&y;jop兛DNb57sdPa{Q=Ǎ-* sn9G9q,ijU52lxB{ 1OB-rZ +gLi/$6s}ͻq`)X[Z|ntl :jH{OX1iL/*`8CB #&%WǤPԢZui$oub2돽hzy)p2 "O( BI8l? ۍZ0+`s8p@`<)GB!D?˱;4V#nTSg^kO[?+NǾ>84Ƒ1Z2_ ml/ ''uwPS?-{养uvzj5HphPWP,aDz*3< EFApotamG a<6ms5V&OK:̭t= ?oǦNgQz^]'hH=5ucW~q77㨫Eo1֠(|{902iqۏu[ؾ.𤳇UIx?dlBq\|tj,)8[5cac㰵cwaukux WBqQDN#jt]v-K Y>Rrg@wofm: {QC';rD F2'z(ڰ N!rJ&&b@r k`w_ &şiWS3g^>m]{(cS Olpa`y$đCuZ(N_ؼa7x/T܌~ !~-HC!$_:,F]w&+k\n^^`]G ;3o ui*xĿ?)kS,,ËӲ9 NXc^r  5` g57HDjy3  2. VX);SIL}% Y)q\t Ynp)!GsUGxS}Og_ :R<^O-K}Z TAwhr*9dO~\3FYrfP|86g|F 䦛q& 5>I.gji|J5/Ndzn2aF'u|]>&fǙ]npDr-|WV^ᄗ'Pc8a^ݘG'qLNNY_L?5{__]?u?@6d{ 1DeeM5=~?m (k.ϠuՕؚ_@~[>X>G`RQ6"cCkrQQQ?t'݉~ {n;HHǺ+[/HC!,32ݷ>ePV.(*iiiLk̨ yYj!oPP}|ޥ'3W}f3)<ċ;\- 3apW2;ederEC1;jY0)ƒJDT.h$aƽ g}ȏjΗ1̼r=w{~Zn1q|y)Oz*qgƃ5_/^E>DO:o E{ yl޶Zʪ7`rb q@V5f`]mFb !ᠢ_/E*FPv.˞ +{{NB,&pkFp9v4$!lB!:EORBEi #2{Vqxi墣wyLf3V/>>>fT,t p!B!Dl͖>71[l`MUUV+ I nJC!BoM0Zmb@E_IC!jPY^>MbE$?*ʶckn !e4KH·* p!BBe$&p7Eq]FMf 'd@j)߾̱)N !Bb,B!#8B!Dʷo]vy!ۅ:^/WP]Lfbw38B!!_#rXe%X pJ숤i7C!B!- ([SC55 *B>B!p\}ڂ?0h[&B!E!*6x<^=T(B!đNB!BDPPe[zxc> W+-t/!!~5\pt:s =?Y7?$Fs+[3?pQB]M3 ̠Y+O[}9տ {+\{bL'p.=t=s@ Y85bi+yϽ'yUiWT+~ϕ7^Ō_=ÊbTU4UE9bvJ!z؄Yb!*6bRG@HDL,U%HISWOL **JJмᤤ$2*>?_: #9$!C5f Ӧ:me(p`Xכ2"ۃ@#gX,?Ĺiymgpj@7,UKx" »yӴ&P|&p ) ,s6{m4ՔS\VISϚ]w^Ĕqw76؉qT%EdO[#eEy|2Gn,ǘ~z&qqqXHrs1̔lgUu4BCY6)Sf p!ĐrU<~Re\F9J©8ގ8)gOai,_cΩYكD@[ W~) igꍸtLGH9H7DS G!j ǝ Ӹ)ct!ws)6QЎLB&wN뭕U6=Xz+6tN81.VFAfj[p("G9."CILG;tZ]F֤QthlYφuu?&0)%3.JKuNy!Df4K@?vI`p;Jq: J]ԚFb^w?.&91f[뀴WBq$312ϚFnTÍɹ#wtږ+]z)|1)v;u7_~+S:;{׹ίp7ow=';v! ([(3Uݯby#(s9.S'J`sBAt:&bvVśŘ9)XY@f&O:M |Zorq\l^jTt3W}'5]۳( pz)}-7)}….7y \>xxu@AQP?F^)=%b"l9z8׾µ|{ ̿o~_uԨsH:>#yO{ (p&}=w\y Xȝ7I2FŃڽs4nxOTȓ;lNgXz16!opK֑1G1<55Jpc Z[m GG{V6B{=$8{kfGۀu(BO/zeiGhXGj OggχфQ^Gf}yh"i`2I%YYz Y ixk wYVImn>bKI\\BMJ) gDh;;Uy(u=[XoC3fH'cXZDުmx26}u>sq6\#~lN4 O,~v~=W/9?f\̻>3YAN|knfB CEDZA4565(RW]u/!;@GG/?؝hZtt|@Ӽ?L~g*!8i?S_ysI5;.c*~fY$3¡*x1+NW:Н7)#iV>Wks禬]rRw}IUud>Ak0=q}ȝqcoomz QS>uZ]ǥIQG_xKVNÐEvI3ROI Vށ#rs {4&2'qҞcs8j\z"ϟǗ̈;bӘ<֗ڨ(Bה!0s.:rEK98~jY qm|c9s4e5zխ ^\rf't۪oW`|k䙯k I~_0w9>&\v'%:;kzXW^z͐Ô;o-KYVskF[W\7!.ښ8Jk&!∧yZ@,+ns7,]~W^Wj:bkcςj07(sEB!+]: |FO" :*k4oc̘u ƥYP1YN9T㗬pSݠ>mQC8OCLffSm\QgȂE6 WSwv5*(DdedIdMլMLzs>+z@dnG lQd⾉5QBZ\Rs"Q\ܬ|BN" n~1FB7M%Tw]jC" : *)t~}/{ *'SN_|n7;PLĦaINQr)Qgq>ӹyx6hpC3%f_?P)'qCY2sB\?lA%+̸df7DX>qfuig3#~O>f˅pY14:M3Mx{-ɄsߨnOq1|x1!>DG`60 (%(H'2o^Lt~ASq}mkJ !k:GusC5`UϜ~e$v)DT|НibLgn&W A]_KSi)6}a|zoeI~mVq*'z3QN`͔|kZ8`}]\:jH6GgvcW]eCa=+CcPule+s"܁;Zu`0vouaX<Hv>}:>ofC?٧e)+}%+Y6;+(\o%r<3.gd{P-77a !=g$r [wCp v!5љAڲ=cPF(QK&ɇ1]jKHHe2b^)dߋBq0x]o ^֯Ȭ԰Zhd SMy.hm%n8Xq33p P|neK_?DQwM-J`$n{RjE 5I>S:VlprRױXs+`e1X`zO=?*CWϚ¸:ZEݏؚqѢAD_{i[y~~3I[+:`y)wv!S%?8DHj?e{A#w%θ45ſ`+YjXfp@m  ::FľQou; &Y.BQi@klIgpVU5BSKgC‚m@AbHP׃888<^FezzYݹ\-6:8;l8v66m8vZ$a4M7nOL=i0`j8TŊ@@ÿT+Vtv8qv8j=l0)f,;?l}6ZB^Q \NVy4Ӂ4xkN^e,m!;)(^/ 4[3C[jqtwR_ۀ!,b3%gCs56ԢbɎhH$'+[ڂZ֭ԫ`ʘB@i&>҈ʚerG 寿Zt43CEc]78Hh&>sΩj$qKUEuu'NO/~PQEU=CP0E  OxuÄ{> ZVV_?^:<_C6%b0X0(77Ѵ_oGaMqOZޱ 62bn LMejݽy(E DfdO PPa-oHC@A&LLĀFw{@pkwtBZHwyj4Ň ^ıC7ֽq?w?"sMXOmb/(fw.e2~|4ؾk9S84LC g¸uvw6]11>W٠QHMCKɆ4﷯YBB !ښAVD``7B?L<$!b7S|"qln]^I?=Uy_ #6.{9Ӵ]>A1&rF2-'UkeQPnF_Ze3 ?vtCrcd*xK0--,Gk.g \,7y씯]w^ϭQ7s?XT/*$"|zewbp}oYyvgN 92@kUnRF8j1YX3cesp :VOǼe>ۜl]hS8Ĩ!cr Tuyښ.Uj~z?BV[Ӏ܄dlx6El=pp|(jJo84oQj!b75w>mR'p58Y^}>Ԅc9_ӧҼm)-\Mn$9b"sdB|F͠plnN+~M7Mj|S6Ѥ}7_k,e碼LI]q>~Oyq~=wڥuj\VN13&62'x⾴?edg7q㪷Iƈ(}T6j˷asv/A޿rL)jY| >O_%Kp M[BФ>&ɭ/ϯaɜ24Nmy߲py(NZQr/.`TGv1/=Cb9c!0j zk+:('2:u\TTTv x@wp{LyDPe_B:YwFцv~@AaGg~P:W1)qh:Ac˃ y?ϫҦubg厫u.K1S_Ȣ ŔFadMN°*9SI𻧑2/m!?Ϋy;_RT|"0˹ kNaڶ/jBBJ*㳏fȉf!Lyr/&oDǟp:u=̌A^_J']\~2#{2rCߢ]1q ^y%{ZF01`:69T 0(G_T>':B#ƺZPz7}`UcPTbnjkk1 B@` CŎ$0Z-mX]\.'*TZ"X,(W/{\G|UտqaA>YS!oMuOmuմ6OXsRIiy#FG iit4VP^eC7G|t@K8,t7*[iwj(B"c `.ZwPІBa{WS^gǥZ M .:ѽ8lmr֣M_ʔ^W!ΎbR2X蠸iaÁ[[(߾QrAEE%˿_*>DUQ~w=w: |­1ӐB!H<`0XF9a×Ohi 8,# D'=2L98tGeeT|3`^wmbkUUՊb88vZJWB!Ê1<w.*gWcN ``+(9\{iZB!&4< D-]sCMe6[#qɽ^@Z[lT@UTR1q L$!B!%ny%,?@A1v\vlB)08`+wy:  bsZю{020r ^ۅւք(DFTBѡLB!c'wV_KmLG uÀď GS}-;p\t.ʹSC] u5~_U5`21M8 8$!B!1#~$Tt!8$ Q1G {C~/!B!BB!B!= p!B!bؓB!B!= p!B!bؓB!B!= p!B!b3!B!U^ZZp]i~jd6c4?0gБB!BqFUE9AADc`4]8475aoJTL<crvQ8_8B!BC CBI3۠C`p(^ l\Olb2A}~oU5`vHt\+i!6!ҟ;l$B!Bq4RSUAR(bjDh$6!)(NSC? Ԍ1Qv!84t)>c> W+-t/!!~5\oK\|g4d{`7׊ub:fkW3_w,u%e;2VlhE3_SUfX#H5&ǽOvLA`ADÁ{\ps={B>mmvjlFqQ!>~X};$$R^tL?ihgϱ$TUnoiO#66v@.e!r4geIQ&!?v`κy&N(~.{e+c&.J~6V]@N}m?fP畿ŧ-=s@ Y8ow!EŨhShѠE@_KäB!ڊrbbdՇKIM=ۛ 1T0"%Ouh_-">1H**)+)CjG:ȨH.|逌B 9 ֘1Lg봕0cc7^oʈ8$ŏcU䵝NV. ƊF.L&aNӚ<6{@~K!%sym4ՔS\VISϚ]w^ĔHqwۻu{˶o7a1f0SrB!G!x}˸rS?2TѽqSΞh*Yɩ?S|% 6ZS/Κq S[zo$yTx?=o>;džz h퉕BbXj*%OUyYe%R(N~F`0lٲX++B#ٮzc[kkBɚJ>S*JT[IN ʎV" ߹n|IMj(?;Z3r|6vpobU^vIVbЅ5l(ma'2y,w}g52) ڈ똬]zB3]6 VgCV@WLMоMqJasFV7ӡ ɸ@2%']neV/JbR>;ZIQsS%bI3 rR m% LJ rUomj7Sb@r<2 6SVۂC9q$?vkGwP15 idMEX+@-h~,ړ*wt8<@@@5 $$%P]UŤ>29~\Lrb"Ͷi8Hf4be5`9sG c-7W> -R^{bR/v,'o2WtN?pw~cuy[1>S \]@قGr~Sn|~/NQ\s}u:QTbbô^,NCjxW1'+ I3i/_M.ڻ]ͫYmʴn5杹<ϔk{7.[/0_w/\~5?9y󸪛$p=pj_v5$؏}ӼxuԺ$IUL?["㣍uMZwֽPIou ZUɷ ~@13Hr:uv޺o$_<01GOGT(!8r45Jpc Z[m GG{V6B{=$8{kfGۀUBqSL |,sUO#=D{>UKxjGd=;~>W& "?6.+FIK*Y2ϢkȺvNH [[ǯ˲"VmÛ%˴^Fw+gsM^w~bSyg.L3J,ݪc1Wj~lƤ/}Eq8N>) BTqyMyZξt%@6~Z yoL 0rܑ7^ZL)q|,r ġB+|?&g\6L+e_ChWn=A=6y uUO%=LG:>~=W/9?ZBqut9h@xDԠH]uAoˇXSm`&cwn]uM{M"v0m}n8UO'%yS\;𦏩,e5^Ŭ8>^XBwL[^}?yvoarH=%V1LVek{!w*Ǎ㊿Ͷskꁟ*GMeJוju&E}-Y: CbّF&M"PHE^>eZ&ɻ6jXz^ )z_ӘȬI{[dQOp݋X?_N3#W01y/UQQ=mi&Qmw|wOr㻬ܷ}um|c9s4e2zխ ^\rf'5fƴhbJ a쉧sA\uo5h$f?/n໻}c:e|Ž/_⭳sv爖=W,+/u=gaJ7B,תBCNz,X,Vno6Yp\ү폽3uTnj`I{ kA1(nQ{KrB#D3/gt0u=L:*kz|&S1 K5:̚wrJ1AwSM[NS79Fl\Qϊ3=0gpTV plhTQU" Qn_=қYj8Sv;*w͆f|rfq}w*O٣莵|~(;TLDE56Ш`s3uf_׽1{ʽs3JgɔpW?X4󄳙=wdQFtos.t%朙_B!!]`=1Mx{-ɄswIǍ'__ #1GD($7_/&"q}mkJ !k:J8wJf{%N*"koXVWw-k]vkUY]^Q U:H ^Hdd҃ϗufιgn.9< YKj#ݎYӋtwB!?3&kwk_tZڢ{3o-+z AݙC(Փ7τ]ۮlx_@/a˦}RRza0e _-Mc2Tn.¨>߬xu޵|va:req <Ɩ5Z5 )N4 {qf7* ^i-ECSԺiC>A{IOxڌŧ'zՒI@UG]Bzthh`:o>2+;4,#Sg\Z_ۗ ? N&UBqS|5z|W<m[pg=ckQkzmQ#:%$$|Rt `uv6r0~Tngn'X2t!YwR9e4~8رiUp-kEV??\ɕttir6wS:Y6O5#M9GE`$84kx(8vZ` <,""_]Y]Bzqc2,ǍЀx0xYjc)>pRU݆nQe^UEB Mn8B4 "5\GA]\ )ɜĚͥ1xvof̃rbiBРz7krfƭhGptt /N/[hE8\G11 IDAT*M9GfƖ pulTq c׫]j!hmj`n.F(k{@Pt7Gsk4́qyh-M9NB!NZ9pP|3T,>4t|2lx/[vcZ"Q=da 70Դ5sF%H?_Ā^KVrD_T7(> ܇&v5Y-:*;-|Lp|g>Gڮ GP,_B!:<`c6ˣ38vL[t90w(IFB( {79h(bZqBf(/% 0귕eovҽ !8d^rg4nf+uw*[v|C;®r4T:FĞcDݵm5t0ú 51%@Mڮm3nLjD6W4(7QFa89xnlWy\4J51:~W50y38 ޿q&2/ jA5's芉ā}^Btl!ᔗXeXG F^$!S wn4CέWRS_93;F1&0ftw#Fjlb>; R[K)$Yjw|a؁t<_xt*k|q;y1^lb sB|6nc#źf֕kJ;zLρya69g)[pfL`)!S 2w7gQvV}?iùt\܀ !į`0jh]VrɌu( fKk~졔~)ްhZ K!'].drOط;oGNMpUyZKodHGo*zu5~4c _r̽L4*%2FboϘyQ}F !d CT]KbXo!P; eDTF_woײn8& JAA-Bfiwqs5o8)/-Yb gyD5H(Rew ֎aTp GdCԓA%3 !*r߰ aq:˪-ͭynn|.fe,XJCIC!~mC! (Y&*&Y3 4S/ #iF.F%bz7{i$~_ U[RT~-$N7\SK-=1[Cރ)4mWF͐+oOL۩9 ZkwP'9,;~Su8~L38oSFV!Ĺ,4<"  h: AQnHCbPTEu. *w<aT` Q--e6,N;Ng.@~~>QQ(M(7?T?:gp-$fh" [A&qKDeβleSe'sg"mߦQUEfN)U8:EԹUnuDnsUdB1ǖ\L $j!466WVCtKӲ19UdҭW_|1ӡ>edO15pNVV6Yi_*FPm+VR]' }ȫy1 p!D3;7B!Q\XH7鰓8CÛy7OX}\N4N'UUU핸2X,XVf3ڰG8dB!B Bã؟JB^oZiq(# nRup@UU, >>>@)35roJB!B!D+ h4qh:A!Da0}hq΢^oZR򲏠**]dn|rT48B!BVJ@`0Gv  bx,^UIiI ѫπz!x<]N.'2JK( ѱ J(ڞIC!B!heө38擛uIɬQT㪪ٌhߟn[ Qi[B!BF Q1GŴuS:<ٱL!B!8B!BIC!B!8B!BIC!B!8B!BIC!B! B!B4TEY TUUin9IU XFDi0 p!B! r()."63~A n9`+/%'*£ۺI "!B!^EY)G٧?F e[` 08_@SwaZ;L!B!+.#.7Zh$Sg ۺ) "!B!^UU?0{ B!BiUrn:Gں "!B!BtxB!B!D'!B!BtxB!B!D'!B!BtxB,̎n%$įaScʴF]IYavGx=ͣ` #B!D{iwnСq{ %6:A)ͦf;VBQGA~s#%؜/1tHHK\"WZ+no;35Lk<0w߱c+8B[? *"B,Իq^o@((uUQ c`=bPP(*=Τϫ7B!Z2/t.yq+&|È÷˵CEE,X=X肪WT܏pѤmmBѱe,alڙSE9`2;iݭ9.m*Jk)"ذ2rSM}g+yDk'ߕձb!1y$^ʴaqv^(I{ p )5uoì.&>2+z6э7w1 !8y9y5w`T’/г<$rQQ`?4X'R˵OtJ'**l< {nDFE`\2CBюUӦcG0K4Apۊ9eױo7G֚P Kp<G28͎_c7Mu=9UIq~f÷iz}Iftot ş37o_oȀpBqNrvhXSWĆaz+._*){K% zE6{eof)N,)ttYmkYp>]+6;:@U:-w{~YźntN^^RQP^[iMѳ5Fξe[󏒛Q]P]';%?wj̙Е˯> />sue3\/.,Qjg (RUGR~̨'W3|Ս|s279z$<=un#u<+>U||9fFG~\g3$.w?/,?V~?>F%~||W?e v O,?~_u4~/xh+=9??f=ͬ}/g%O;n; gwG~2A%j̺{7<>/i} )pp( =&cOÇofKɞp%q8E!DQ:ˇu&h@7yYy8QODm9h#d)F@mr[Ue3e6Bx?$8rfUv[SB^tW'3Xg\{X_qvcmW3upgdfS~WPb4aT@ϧO}2(f><fr͜Yמ.*Nye[:s53'}Tewa<oyu 1}]'rriW%6&AEO~˘U6y=:;`?1rң>82t.'WP=n-c&g̈d&fNaL`/q9弞slMzK?2PTwKxCS|KnZ]Ea:5#g|J=q}|R5%1QIcc"rI=d ]¼M,T5KxY0ŇeVA=w_ĔyV~X+cNѲX" 7> <unsH'2^ 96){el̞4j,]:yV@Rάtw1gDBKVbׯ>P[{^iR+\\s䈋imYkv ٕZ.מRc`PԽݽ呜ӓg3`Ua<6zVC7Lv},qIt89`WՖ8B:} VB&.vXC8byJċH~/ᇴqWTs8s(A &=뚆vFej=vJٲe/v%$$o E!¼|\!;U gU^:kZ\-bb 39bҢtqMs)TSu^K͎`+Bq væܱ-[6f f'=CZǶ8\]Z2[)k)T*NvUF\a޿W=!y#FbiJl㘐9 LP=k KV墩\0iMl6KFfӲ#,^[C,[y؋֚L:e64T@%QVZ$WKxYцK+`z;[RM% Oy9:X[.B4+XA9ˆ%ShP չZ\9ԭ G nv;gY)U8J+m6U6 UZ\$tJC!:BTz077pI*_4xlm݅{\\u0}Écm\*~r|h%k {Zv^'xnnSsL31tc̙Ʀ(? *_C͔:c_+mIO*`>D -uVZ3׉3&F\>\((F0t&·i\NYB1(,f\cKGjt8}܂A5 ie)gR@@ntYDh@ ܇3Jhw^ ۅFf n!H?WKSQnC)]좣"II @2X3$B.9)ʲՖ:Y`S TVVV/1D)҈Zfg<'C&fg2^;53PY(!I{5_d؛Szb+X|4bc^BG KF,LNWi:5gy;Ww4mqVO>o>SufPq-#cx),&zB5|lGu3I/$ IDAT΋^!W/(RgE Х] qC sB|6nc#źf֕Ϛx2Ip+fvo6!M`Cp Y֟vQ&#]Z\{iv\黦xni+98It_\ɑuw'Cb(*R3ٵ7 Awq)OxZsmǤa] 3:(²[Ȭ2?.дjdn;nǛ8zzhҨ,dǚ?M?ؙy{o؆DA0n8 Δ^/-Ù|aWQ}Ȩc[9\;jELSſ/I] 'zV߸;'奥T:[SL q?46zfGb~#B!ZI>~n3y':{ud">J;Y: L?%_%>6n T?%]q:deUR\Aw;wBf;ax^BnI3>Evs8uY|DQŘSH kH2;^5|{OFcͷpoupŏ;_wϗ+ٵ+v,9&5An[/#!&Lzx6k eAnt pRR 8"c}q:b S'zd4Cc'kSt%m{4ғ(5=2I34چAu?Q+EB]sRUQAE-kSN_k EӬl\=9ߓSCh$FHs->[\{:N|  0T*0L娖 2 Ӂٍ,` ??H|||P/w 9zWn?Swl!y~g!Uز~5}&7źr˩th(>Dji,2sJҍ):vpa+!J50Nq5"s8!Dcirǯa%pos[-O7FWok\A!wLJ32B!"u$kcJ))VMӰޭ˵[6{kf +kTʱb9`E?Ê~_ y5=op5^4&X !Bh,9*BxA?|KvoaԍOI7Kz̬WRܸZFWۧ0&^˓"DB!he2C!56w"e3Q5w/ŗ7'`}0zcWOUB!h}I!hWT aq kcגGhf&bбR !\#KTB!BIC!B!D<O[7W`P;FcR!B!įꋭc+/bKg%!B! $'q:Bhdd[7A$!B! "(8ԝ-*-qSVR̾@H%.!B!£Xu(uIUGTl\ n8B!Bt AvAh=DE!B!8B!BIC!B!8B!BIC!B!"B!Dz[7A+(J[7kB!;85!!DK9uw`8B!ht]Gu*K9ZX Mu8U5`@ 1B!:(ϡN `[x!Dݔy{9䷣B!̉e%Tҫot9h !u@06,_~B!uEED%v$:nMCQ(/ ]7@B!uMӰWU+ !Du*EB!9uM'Bs(xN n ,QB!=ӵnW=4jB!X[!D%*B!B3鉩ףnB!B!:OeYɪ1#oaueA`sj̾еW"="-tnxO,*no;\u/rŬj^/o,pc]_[+ޗMF=>itn84K|Y¡4h V"bޫ7]|;q^zwSo_8_uss4XqڷY9G+#$))\̪Vr1^-(B^w}]Y**G2qLomw(I{ p )5'޼Y]M|?߿20vs6[uE9`2;iݭ (*FUESUx`箹@ ˓=h|;97ZeG4Bb8`'!_>ZJ팚BԀk"%5h_8BRԛPÁOk{L7B[ӷ/{Ɵ1 4A3a̝ðaX,VS!= :9Wq4/X;V~y^ל3\g'a6|:VdFwfi}U\;m:A\<~DdiYx3ru3ok2_8K=,mlZ^%/%K7g|=./d%͞A}?y?㞹/s-OD)S:}6y 3|PObC2rbϻ\͒+f#O/eCtK¬{p;WNg !ח)."0 ~JҀAtڅ={2(iWuKC!=#?3SϘ.Ϛ>Wq`\_ѡ 8BtQD3D&-JB56Z;`!2 ?/;: e [Rۋ= k͎VE^Nv(B#W`Iѡ w#Eu8lߑ2e?sװ0m \]}ޛhe٤産[?2#)-DtI tg1an1Np:Kb)etl,ͺntN^^R 5 5FbҶl#}60ANlqa4+/)z<}%Tif1`Pobg%zuSywXLaL wwϚD[/􂗸-7#%WFe~c/.Arx=E ${4r !@4< ׏ ǃd:%!_2\J~/rGo% q!eܸ=Տ lw ˻7f̬#;k9zoUp"凢s:5B]vGc>Ս|s2g9z$<ǝ"_)=>Č%ՏܬxCLYɊ`A*ŏ'"ȔCXx|{D!??W~"y]LùqeQ*ss~i Tx]՟S.mﯗܬ oCb ] fw1:SWN3o<9f3϶%'GCP F|:gzђdT T2}p:]x]C~ûOo$\ 7^̰NViC{q^4A10*Wӧ>ds3A3U9f]kwry3ُ?×]c3i d=360a6zwN5|@'%vdXO30!:4Nye[:s53'}Tewa<oy&#ݧ#]7K9Dn ~P(~.W} ?`l65:pN7>^xw͜X7XM'OqvcmW3upgdfS~WqbKOhްLp+^_Dc7k.3O -`wX; ?*rc{het!Dٲ};۶hgJbp=j,\,X'Yrop󨪊`0PYUuWB+̽ߗsldS"=u7inXtbFA] ݸ n{;[˘3" Y{S>po q~sf(CB E6t&;x f<>&P/n||!;fѤg$X{qnAܫ?au2?!="H?p$K%XM AUTN]5^~h**=,u-L~WOeR^i8غv 6]wT&$po׈Y ʹ2Xf;~> x ;wR+iNKcW؈; -9U:3ery/ ݘ2%3~Cglcrƻ/PIY,>̜߼έA8DpK"zQtCtf :=ɮo~0*$Iskpu nT31B\ӈnƢ5g"J(vLnև[e!;:{v5wWˀӓjYXn Îx^-t tMQK;T:NwSIjqhlu$&857~2 2?Y8-xnүo4Ynpj.ŧjE7]]j׿:yϮB4 txzc`PԽݽ呧%YzYix<זѤ6pXrHo . x<m7-[O{:q9i/]Kwn@?uNT4w.n (x<g{ p!9C:QϏ37amb ?:ܛ'I3Ap qmTbժ[V:먣΂)w!{;9?$$rg8ܛs> c #c۠{ TManImlټ\M|ٰYyϐ+WPVQ7mahXpOa?o&H`wM#ޣ ʺtE6\RM$,άyYxc؄HȔ/`Sl+oƸ#37-aK3r|;+7#w*m[ŬO$f7ubאf컕-#IsEE'S5Tʩ>xR}1ͼָrs(A)Zz QkqіGgq K11tD׎tؐlۼM2xh-!8G腫X i{ѰqJxסJf ů=[cV!7l 'q1hpG;m^:\n74i஡JkɣR|2]A5&O3 uZR1uy Ʀ8PRNѱS1\dmYڵX`;,+״`B[^k"|oFoZwi{^XƵ;ILjֈnglak}Wpi=3㖛H띊y‘OZзO?%!?.5B0*dnJnc=Ǐa򠈓,ltTؠuG5P 4 k0UCޙ~xq4:%%68wLZĬt /} ۹la!JEQPCh,VWSь.8_8݆\N!3ej/_XϷsjjKh3J4"$Uˮ G1gVf,Sj; IDAT1b,e芕~c."^iMEBMק7޳rHg8gBv}M\ ?Oo|N6*v5GJBݠshJTus8b)oPwh%ys60uˤM7Lt7Vfgv4uX~0(kNɆdUBYg#T=$F[Fɘ]twacNu#SNTEF _As5IK+n^C|Qcs4֦Q^Yyi^n̍Ws;B8Vsp+ĝy3}+LU埄ң+av,Z 2}` O',xMG-}+8nꦂ5<`1:85]ɡ-GB W[f0v#m4L$_$5~?FuQoE?F_onHӼ53/2^u?|:MG+w~qz`ܔ/x&v.݇3۹i~^xnUd^ȢkU/| #&ri fe.FM7n>}e(8.vFx*s9pZNJ"6:ΚZI]ȃ׼ -[ywZ7epSK$ bP:5E.c &!͚CMQ*TtJ&$1.#.&S*pf" 1 ]4[ңiMAXZZy (r58¬7A Ǒ`e˅碽5/& UU @\.yKdoxGq_~)`:1)3b $%&G=HLCZp!8{ It>;-8{NxAu"9^Z74D% *^Q-NQIhvhR8KH3~9?N'x`{8=N@%,:00tէCF]SAõZ.8XVp38'>hel``;՜Oim-BFiFu5Zo^G`!JzfX"4@" EQjRFtHC!8̩ؼ|m"'bed:QBr.%'}]xQՋx2!Y'?A[*ZKB!ęqɃR;J- FQbSWo:Jpkx^Q!DHC!WKUB!DgdؔG6KMq!U.IЙMYx[|Zt[8!z8gIC!CB!g& 2NGXbH:zB!@z !BcڳB4ׯM҂C!B&4MCU幤}aA#B!Blv5 !yf9폒B!>Taᔕz;Jq>ub}bSB!>FQTU? {`oMw҄MRQV[ ? UU}:2B!>H#"2Jqa.dkZ{'MqSM&蔜BPHXn8B!9G*& pC1 o!hkL&E 8B!A6g͑Ǒ7@B!BORW.$!8S_k:IC!BVq$Q*B#^g$!B!D+Bq4B!B!B!!8[:b1 p!BVB3H`0 dT!B6 0 *(+.pZ{'KqSU6?aQ#!!B!|БFqaՕuJ"$4YB3Kz(/!`6NG q9(B!9r za=np;yBsJ>a޾͟onCB!B 0(+)!6!#n!(BlB2EytpdX!B!|a躎Q70 5" p!BCϚ?1BEA tQB!—z{@qƉ$!B!@u !hWEE!Bӑ !~mB!B!:<"B!O'BR's79< 6DEEҷOaޯ8B!e>0Nk-) !DSXFEu%$$4 عk'~)\rѥگ8W͡kvS6c3GYx?.XtZ;"O%ٻw`n !Q$t'59?) !|D7ؽ̜Bj=`O=I㤗<>f2wm@Xsfܫ̬LpU10??;#,\?- GxB_=J;#'b{`w'+dĥ|G|:7ZÊboa4"Lc"rJtbև8u׼9T7ؓڈ7_M/G5yqjGkHOU}_4G!|֞E]w6Ā0` cnj壏?bal[JB'-#ĝ,XYh((2gPKx]~.j'@J| SCqN7^敧_{Y#Gi| >/Tm?=!J4 Ftρ Н0;}[YMsyc È1*زC%gX [9 !:bzMEU9;d¸1w\v; %3;wb@-ڷ8שHIc?x Ӓ亷-dAN0,&yxꑷY]fR~^JB|6g>\OɼڣˊQɎBVīyi4w?qקat P@QLP7_x7޸9ۑjkuL ɖ !)N,+g Ng-qEXbim[%!58Sx;X|= 9O[oE67"GHtsH# +sٓWa$%9 *r2s2lDuN& w){$+Z_p$I{0PvmNf~9Jhl I-RmV騑KO0!}X~3}XǶ k$њ5// j8XջQi/<(;F+?*ݹUK[YI'/EXk+ Ըq<棌 u8񞸮5+zX=>v~&_\ MQKNy>S[݁8K1B',BQIKa$h]4QiX-VjSBtb/4- VΜe<4~ݹsݿʷ)t_lV |7θģ0Y&3^Xm[Xu̫Z 635Uo U'w;Zx:Ǟp;\q?B3/WW+\pTp(y/nk8Ikע^2*7gwij}|G<۫xVk\KQ3BLB޹|C ewqq Ԯ5n+oV>y{ιl~YT{[(4o<#\, !E0 4ev{4/Bt؋#vXΜ< W-JxCm h,kt<#;WMi RE|d|P4?4z gPexGJmI\r㵌ۉP?/yX9;n'_oLꑆ#F z?~ۿ fLኁIeM_`[P}oְB#Wf 4fi&}- 3]GSszBLtMh^XXp; ܏cRO%L=[w' 85k9u W_`G7N 1,}Dk~fg=#O-bGBwTÈQMJBvi7nnֺg`=xNanO൧2duv&x0L:ZZ.AB Cxg{cztzjh?[F6} MKh넞}qAw+El0y:,ZMn&u3pcRpF_>;󢵬̚Jjj] W{qWiL;δ'w?y:Uu_c_~44Nky^+jbFˮꣃ,aQµm~uvO-w/3 kTmo@~} VPV2C٬XtH%h&MCUTвdCGj2"8#TEe-dltAhc~&@(v||ړ fn0x<4 .Jԥ5泤1ܥBM+ZJ'3 ) +1C,]x+kb`67(Kwor  7pi q1ԣK(# ?;_+ת8C"7Nqc'9hɏ^NqYchLV*DVJqxGqv;q7,#?ӆ?[/0x@fLi-wsx\N?%PR?(j11 \>*6S+ 1y$a Ӊuai i!2Ibf,Gu/^vncLA|v@GSލW8z AUBޞ,*l5>c1m:ȶlt'$B/dbtLtNkb 5>ᨻص-ϵѧpNLS[~YJ_qD֦5yq{08-8O󖎦UՒ,fzy\Bt8֧73O`}Ɔޯ nBZ^GǽhkO.0* >}ҧ/u3ݛUdBQ4M'+k/Z\~Bt x ыfmBf.-ڇfıIM7J(*/2:LDFR^ZBA5!n$nw/=^ $g aĈ6%ו7k<ފr \ETp^*xjk ǿp0}V5gT%o wMDmuZgAԉSC'9%Q P[hQwA(۔߾Z.hWW#wof2 u@F^hM:|\UP{83q9q:qjp:jp9kp:q:kR eń&B/m<'2goօ,ѱ q'{n`h&KݭzfU gv2VnGUڶYIH1̸n~pWÃ``h<&RI?CFc!6>\hгA/ (ώRcN#}-΋3L1[1xs8oRNC8Qrs(A)ZZ] QkqіGgq K11tD׎4lPmނ <ŇBt$LׅbާS`0j%Dذr0hir81fï5Q]oz, S(cةRgU,\{҃XykTPl^a3]"\*iL>n&([eӎpnZdIS: q^iIDYkw:Ԭxnglak}Wpi=3㖛H띊5ډ޵o~ӯK C!:ΗŠx 6WfhU9 O^UcH1^rh%?KL R4F[zE9ͨM+~tr9)~lƨZϷs*X:EQ[pz0x4÷]GukmSɷn8>s~BbYY8V+VFz֧Q^xiF>ק7^M;}54MB4$%_-gM%D06xL_ρj\XHMő]Z~0(kNɆdUBYg#R=$JFɘ]J5l̩n~87S0^^]ރ5'$kM^E>SiBDKk"As5IK+d+JG-㣛4w%/xi^n̍Ws{Bt4J8&\@r9eJhxot#ck)b&1)T: ox=Ԟف=G!C IDATO?_;҂`gw4/xY%;&_n{ -`NmA6\^,ilJ;^˝3WieBE ~\AIw~ Ci%"d||: {MwPQKffnNq`ꠈJ_kB N"9LemA>ij@0i8L9'hV tSWnWO?{sXޟXI)=fǢPx ݇*p 4n~.͂zGBs&//n*X/Su؂O,iB WᇯK=~,] +] 5?cǢo)V"R/gڌL|4j7<0/w[},CAqyrϖ)`qr"/G?^߰ȱPP1 t m2408O9o-3~G6SGru oi{ W?/^1Oǒ,XOΎz鲅u0FK{ vԊ6/,r}W~ALϤY !UCʴ>ҥɷX%-:b" ]/݅" !_n"TMǎAN,F"kP0ثy\n' EaQ .l6Һ9oJ]8ߝt3tŭ:B/2~^AA흌fREոU$Nt] Ux,$$w"֜j8TTIbSB4 8Jq0a&("NAX۪쭥(/ 'FьtNZZM!5F\L(gyk?8;'8a躎idJOco ŊbE*;T@IEǑfE=X *ր@&МU8LZ.8ݽ}1Lz\rK^"{Ǩ(G8ru0:KЉI^ )1-8GbB6qB!|QG6ڳRbb`:! !ĩݽanNg-\Z v{6 ???Tej8yB!]o (aZ R}:$!B!6B-%!B!|D8Y$!B!OB4LE/B!BOB!B.MktQB!G MZTaᔕz;Jq>ub}bSB!>FQTU? {`oMw҄MRQV[ ? UU}:2B!>H#"2Jqa.dkZ{'MqSM&蔜BPHXn8B!9G*& pC1 o!hkL&E 8B!A6g͑Ǒ7@B!BORW.$!8S_k:IC!BVq$Q*B#^g$!B!D+Bq4B!B!$!B!BOB!B!d !BҢBwr8U5aM`pH8[ZB!y  d2w8MӨ PNȘؓ/)q6+$!BZuee%tYFBd2`0{vlf7\SlkӗB!8$!D0wJu:%KsDB!89|QrTO_"!Bt]CBvd2t:%өDB!B!$!B!BOB!B!$!B!BOB!B!d"e!gJ\@ѶUW:1LHal=:<B"_Ni.j]^ łn=my lڲS^Q'>6AcZXR&B_eԒ7rB`\WUzs*ADHB/L=7r\TPUV{0[8,F5T ]C ݽe~R< 7yYpSURSmʙ?V.Z ve⍗# (,6Lg܄gpj椬jdLDd$YxӘˡ.nh٫t^*YM|WRR̜y?`IHKΨJUu5yy|GL769yY$B{ٕf.|+fN7o1_Y1^^s_>DASϑsizuOlp&\7cRhr^[DNNX\ A[?~A< jkyU]UQ׺^@܅r$~ge[Wrw1j#21P[$G+J؂kn9xGՉӿkx:Y[u>B ש{wsz6ɥuK߃3ʯ2S*r9#t]gytJN$&&CdFt"#ڭ 1̙:MZrtB+gANyIi F5( ҙ㟥D^@ẔQOo[Wj_nj~m}=¯fjYbLV̬%Ō:67K`Oĩ8+CC!(2&W)ݲ7YWHkqK! *zUEiq9U{Y_pzhL9N=!D8s)wV/\ǁ0KIeqsKҼdǘሩL~iKOT4Ӗ[Z,$$$fDEF3t0+cuhlؘaOBJ/zsWL3p9[Yō؟a=OěIo5^Of_ o*QɎѲ|Qa؆2}9 ɾ7tnG̛s1А p. *ЉlU"{_Fv{)޺mp8YO| ],,XˁZտG]Ȁ oA6,[AF^{WHPUkDsv^]O}[`,JbjdV7>GNr(wfRnIV~)nH&/1<PV {`81YPwU *j<R jPTv?7BuKMY1EN_W"Hޏ=WRXQᬤʅa $,ް`i,ąP.4Mwr )qł- hBmD\UW9Lx* -…{PqFv)'{Z։WohD~2vlGZl=|m0LXj4G9T8&!qG[|ѝV0z^\oJJKIH~^7xKJ7,_LJr2UmrL p!DcTmGnL'$leO9kUAOdx(^}0O,"`|h)蹳yYOf?#hMHa&mpZW %DUZcUo2ݦ;uϼXp;o3ƦyU9=xLx}U>O ҝˏWL }k$'ͣ3.!^۳#~lδS]s|c*XcGpO6f2g\J]~!f&\Ƃ1wn=&\VWEv-=vb̀$V7wAFf5Uo UM#vt.$w*VlOY< %eE\/F9t]`|X0!2S¦؄Bp+6鶤XmuGs\÷˲1" ?oޓOrg50$ /c.sw7+ZļF߫w>Z%{׭yh'O| th *ZAd c"2ĈG7Ǿ/BNl=L܀i"q%"BR\WUM绦ի9N֨QCA G6+ pSotp:Y!+)$)=r:8N~ ֈ J4I\LT"`Thci!{GsY/=6n3ٸ)ܭK1qI~ US%ث'3tT"&ذlVNWn qCyy5kxbp_NP,vmuѹ :W:9j. vVbcݯ'avg oaw2t:ճ W (&ɊnW|K`Q;BG'7t@5^z8ck%!#ϟ|7g*3~ӻ^\dnx0}2ߙ<|.=Q*fo`|e KƋC_Fb.F `HR *BZ5|OO)ә}se\ ]{\}t̓MtSg<?w= V?8oa-UUPm _CW͔fg ss65it&0pkq-[ì%E\vMça_<~̙Lz$AùTl?W9sy:%ִg5YRLgxƤz=ї#h-+m⭬ZW %+wdӉ|pm㺑[sV)DwW걶:Q.gQ.=Vr.sw槝TVӆGUJji>{jt5OdD챦@$Fz*cKj'EG%+v+߉Nnl*'7Ȅ6lAii:ۂnK+ݕh(MȠ#NtN$4xEdi\ĕ,q/_ U1q3[ C5uQ[9` %!:õo3/GWCrU;e y{*+F5w犉=_;21JJ~p9?p&]z|+MV0F0߸ߩKmU%4n۵g+XNfJ?y4:>vbLnz\\CeW6{}f*"@f8%|ݔ̄}Ab„ |v6W d%YJh$ɐ;7jB `3PePi:#<, 4mD edRR/ѓ)+ *U+0z-bze:Ǻp_ڄ_Wtp x *î|nG3t{?D(ӚʡPTf!}rTT'T H9`"krX2Ck&2yh<0v}cōU +g^ ;wOAe/dh lR!0T6x: L]XLl2fViAr.ۓX"qc9RBQ0LuyiA_K㫞ȤQc[]`K5TW;l|uyF*`Ƥrև3dvmb̆r3+Ϳdw\f F}̈́A Tc F!-1'ǫkD;%rsszX>AW[bbM9׸;PnZ(;;aӆ2xMOHVTj\TV`4da6V`20111 5Dv =Kxh\Gq%PP*H U8^U1(fnBԔ:CǓ <5CL]Kؔ)>;O"c I;PcM%GbV >/ 7ٍ8h0}:1 F(q.6bY_?a/oeaؘ9==Kңk6Qq@V1>z\VE-eO)S$d-K 16V,a3lg"]x-q;k2P<;O߻+4 v-L\JTRR6۪<@޳\\*-.:!$"tJ3m'MlN.VxQslhsf瑨ihEp32i9й=+UdD(BڒX1eOJQq KdvN^fֆ0;֧GukzBi/ 6b.O>~9t*_wvQոфEF~^!VœYЩNkb3=aHK=Uc4ԊTe!!IO?ŭO(z8C4fFAI$t!aUe;rWg^F58^r0N~nN fl;ÊH3՟;`GsE*Wt^יtl[Ueaj7;V+ukv4k0uVWS*?ڱgW.J(9 zX>ӏHW r9l}|W/O Pr>BE,+t||Sg$0;f#ցKsCWJZwO2qy4'md9-glcWITY$zG2뷯emۈLo4ܴfpm [o0"O4Թ@-/I`JvJ/NK@ABAj*ǝr=51gi~t&zsΘI ؎dy)?h{sۙ2m:SZAFj[qf.!sVl>a;ɣP(+)J0_dTr)u(9W{H9)_m:2]4R}vNh72*\ Ir-АnEzOD-$/m٬Te~A*F*\;ڕ k(kr ;tR5k\9P9&BdgvrO/Z =SG㪖YR^}jgcw&r{1n6^cir9pD&OϞփ;iPqM"VxUo+P$=eAMzZ>%0|F"FoYM(46*T3Gwbz4>Cp\v kٖQޠTeQ>\COԸ `/?GlGQpה0L(JǺ Ř۞o'|3D :Fa50t' Nl[>7- E,7r,ƍP'6i3!y5۳)<3+HeI e&; c1DA pl~ږljx4AHgeSj}Xㇴ#%)¬M$0+v:, ][fV"##nT"!r٢G9zB97-k31@D}g}|Z9I-(y7ʨ6b3l3ոucg.vM8FT^TQDAE?IV_1n.:c\C=:?;ʺo -qW?Ǣ|NՂR)շNeۿıpL5)m€+;ҟٶs^fN zds[!ߗoJ7m?IXV5E̸<] oK|xʨ+n_i0$F^|]Ǿ߱SBH{w˥$xv4z ٲc?2Hͫ;&aQ m!CHӟ T`h226g3Uch7I! ~C{/Ne ^_Q@y7~:9*0x?MR]lI ;s'3N#hIߧᏩSA$μGٽ +*.TxF1`p8ɫMCi\\bWNt^DčQoc& c ~s'R4VS\UU,V+EEEh4jd  *ds5z ňbFue>FCQQ!!"so)U$7sk4nBGY!mb⓺;N(Ksίj '" ʍ놇$˂JscRx4gBlNVayAګ8QP ~R#-sa(&ǿO$~^FUA%\o i:]gVO{w xx`OÓƜLjʄUqu˵Ë*j* fйDT,T5xކGl`.kqln,g޴f"j**.xx١7‘TV[Qz}q{gDnn[{C#KZj+Hs&?خ}~>sHAh$#_?cIpQ'%M;+"&}xo@9h G̹ ZB{i=nNř ng~:7|:l~rp%o)̭ߏ 3)I_w'u&H5rqq!<szY뻵D^..[^tp P> O܇Nh   e,quu|h(흖RAP/Lgt . ~n+  -댎 Bj>~K xͽhDQ,AqA_`^@VO I/=AA"u| E7VTA86%ed&08 hhmO/=H  ]r  M@UU,V XNOI)DCAJYI QlV1]I dA>^NGp|Jhkzi6  ]@UUEd]4AX(MP_K'  gYŎß pn$ {jԔ|JYzAA*A@P'wh4&:8AAX/+ %)7STAA@oz&G58%==AAAAz=1EEA'` "&EQ8|ݎo+:8ABeq%fIO'.REq U㎟ǹS u,UWQ5n;ʧD Gʄ?ϙYaJF$T8qe?H(.`R+ҋ BO֐CM3l*YXd _^ז$dY7xNOBF7 ?=VNc2z"kU1jwA +2ߐݛ _Xϕ;e/ʧ$d4:׶U[_O%c\aI?7{l<W@#yTztU.$$-6TDaV*5Gl#H5h<*NgO'*1I%fx >MG5q( CF5Q?#W| &i ;yw{cc{ }y8<5@aTpHѱlJ,nQD5(\׃iSڪ;UUNǏ`wmW񪥔,("HA l2}9yjӾÎK>-ͦSִ̹*~YTk&3ma`,:¾-El!`-?޽.Kߡ$opkp{EnAzІA 6(/yU€G+<=aHLy 4vV)pο F{"%Wu{|ZPuDEDѱlJnA}j*sUhRVqh~2 1*cX`B)NmlJ=B`ĸ`(mo={?. }kU=Gsd"Z=OcqҒǚ͒PdihK #.G\HduG*LwgpW)p -ϝ>Fcȟyty~#>Ómϭ,Z[' M*PJ7>{i nΡFLBv`ХNż]xt  CV6 R=O9.yWl]7<_{_zV߷ql{^d7<.8 sd"|+a^9]2I yh"꧿}𷅿a~;,^ݍ_XcΦO٭vї={)%ȭk˪KuXBF'Dr5S.p^VGn:7x^_)gWoRVE0{q ˪=rkn1CFEykU+\OLhIkzldΓ^> UUbXmHItp Ԛ|B>L7=ؖ&դ2{f,9#⣖co=I^f '͝Hem[Er$.h2 8`] f<])dGZ.vM_F%ᦌ}H1$ >"Jˀ9wH>}w5 gc@_Z;^;ܴhU .*ŇK'OW?I>x)) IDAT$iuh%P |OYc9[ǐ Z7Bt t3n֭bW<7}<>VQVz6~#ɲK GlL[utqA'x]EpM퇏Pdu/sk(k.cڰ|]m3ظ~% [70-zwqȰH$ n+y|Z.Kfͻ 9i_̒.\J`5q;3>,ocĒ2M0.sA~!zkwo'Z-ͿI Ob'1˙vbdYjh1ִ; C0Ejes1U8m?daFqճKۈk7 6ޏg8HF0Y?ቝyW5l (M> WF͜Bd.i!%2o;jvw̛K~"Ou'ꛙ|v Ws?Mŧt ̻? Q4\Kܝ~*%1n 7?cv سVkz fl_Fl)賱F`F sjrR|jGc#1w~(ΚLn0'.2r$kۑE(9k=2^Yz ұoT2ڧyX&OC狓)l̼C[lGN3s> H_<Ճkoaf}_vE34EQQ̰DYn Iq#GWf / Q2vEi$\0ܕ%*iA]x-晧}񵶱ss49.;xgg Q6cvEżKEӏk_xbu(DAʍ~OEN] G\Ūwˈ( I 5pսߓ$mW%7<ɒ̮{I۹LJdd҈}TK9܃/ ΤRԴRUE8Ix;ɩ&]ظ:jԌ з,(fm4kbWPd`ߧj\T7OF:]Vo~an*jn5~ǮRɲhx!ر+@TG'[yFT&$~5V{zp "QtO=N~~>fY w=G.bĄj5X@j*R;H j>wO?k(/.ooyWqCb)%|_ť~CXy#k1g`>{ {Wү^Y^Ϛ6pI GiNeE'qC0Vv~WгQSUIUƻXyFn6m ,Z|Eeĥ\=ܽIy%]μA+x+#uGu$T)7k[l"q)0$P@d (%4>YM!m0irEɬͱ#eĺﻃiq P0v)5TVPj$ ס ng=;T;\[o;U2siJ8k)>R+y('ŠZ vqX1يZ;ʾɱqI sϡO¶W7ĆWי^o^멕;>6 ځ:zj ԾnE׽WE[Zocv]H#/k2R.7~c&;FZT#2لMX}NO䛋/3[N$%&xoHM=#F曈95Կ3t`5pα=OxuHH@H'Bz49?_<= VɄ$I] =Җ2<|qUYS|ʼD}Q?ם)@ݖ^jv"[puf7b4l,d4`2Wc2Vc6Uc20jpu7Y(.+Nj;v!i8>n}"2`--=5͔pjt0kއp-;ɋL$PV(J^Ɏ5YYs̎&| >UQ[ a5x垱x%l|]VwW݊vNm4:GIh +%df qjfi3}@԰=y۩'}kY_ O`z{Iڪ%i]:"#_Z҅7sɘkjK̲_;6Ի{wVCUmB̈́<Zʦ?`mߐZc\Es6 Smͬh45}e3Ww k}[gsTy!:d&F&hX#7PJɪ-#k w.7HAD韍j-TTŊfs=p"|\$}Z:zMzjJFL%5ayݞ^TPUXP3JnB|t̨ѤѣG5rt/-Fp x2|H k 2>xY7^oQ~ӫrI@'r66Mvp:+ZLNL,8f 1/^ϟŰlVuAP:< I=o6QihB$cB)6\td&O!CM]ϖʩ1k&N*"& I0mJiȴkQͥdM%%e3ëx t=%-}_OUl,6L`v3 IͰ2}W9l&O 3C5Bq<8ɷTz+zWM&tLU^|蛤&Eu߆G h=jvŭd2lT^;Z:iJ9n;d nءV>u&:?O96$-bT>`΍TNd6sR9uqr @GxV*,wi%\õc1[Pqk9XQ/]^Y4,n8wb B/˓_JWy]T5.O4DkW zœYЩNkb3=aHK=Ucη$m7+FS]EEBSv;vgmr*:C!3|6u>a9MB5e((fLܰ1zi$W_x=QZԪT_rVyVv> RҊP2ghl6a61[LQ6:LxǦ׳`N+\̳o`d%< -C$ԦWS8FKuzLOgsY{Jܶ'.{v2<.~Fj͙Ul[GY{s{6L .Pu;P{Y E̩_LD>FU^&@ڽqMٗ@?S\\4go=\ݕ^NN .^I x1I&4xX QhPKFF6v4eΘEw$~Z*OM{Ikጎk}@Tʷoe È<ݬֹ@-/I`JvJK19}РPqg$eO ch|L7RK5ɍkcBԒ4-7LnIl.W ~8m5\B |ÒwJwCBM8ᬇÒ=ը\[m9VCŗfwpj͙(F}"/Z"xݵoypZnIۺ=[Z!>!Q)Ξታi^θ€~(;4LvZQ=!NO!CGyh\r8KRA231]~;^05`>yۮXhLE&7witxgyO֧GIW*iJ(b^JǬ "62vLl~|[ rS]K6z es6-H擕q%1η#=jse]:l?76R҉"6I()Xk7h[C;&1`E Kزҟ>B{Ni^ w;AxжRi[b] e 3&/fM&7}稜~ #Xv{5e]{eq *fUUQOi?ɄY A$/$;w!zc:Jś߹.u 3#@kdVk֦m9ds&' L;ܱbEC$ Sy }M & ǠyK┣'1sI/`ٓP6s:X: KndwYV6xx y%O&^|Q!3ۮoK"/[㭝l\| Rg3uxR2Sװb j4!Lhs8? j,!.W›޵^}2kV6TXfN v?3t{ͤL81)ԔfgZ!!N{+uﷲ۹CF*+ kC/Ʌ"3qo' kÔKsFv[?9}{OH%T+"* "WzJ'@BH7e{MlIvM|<|̜g~[_aI`=O>4Y㙻piٗsV߾}W.d4{X]5BbWl18nGփ3y$\ۃDI~~,9h}8+V+&{ nKOgI̻\h25 ߤg},>``rw|z/O,1 +]SE(Z3Eøl3܌]!1 Z^?[nY-Az*t ӉۗBLu6\ٹ''x^7^&bbKNՉgmGySl|FL_/ *h@:jM`P|jcLf+Ȣq$DG̑oqwl}ol} Gk;f^bOңܻ#fR-«ǸYMKƑoG!Z~t&nw%s>9CNC~T7<){~sZ!9bV*V\}S6fA?ǸYlpn'\͟M=џoEB_ާ Ӊ;.c6\."fC(=n rEgE#&\vZ?e/Z2;)\[Kvp‰#wǷ'Yd&\ ~~[/o=jG"TVVb:4m 5{+ ٌijk Q¡Z‘zƉd~ac IDAT-g~[Fwxj7Py5V# '-+r+ayXsʚ!.drm4`eaKjny)A?o"4WR\@KėN~^&~4ӿC۶,X,ƞ9vb/7Һmg,l+F4&uQ5qz|5_N!%Dp0H2pxp yHNti͡b`r Mr G;4>qn ;L16N<~\E`'ޝ[5~2l eŰQ"H*N]psu{0>ܭGZh NnH] J.(ȌÌGx pմ',w`(mDZ k}" QZZ7́͏cba$N `Y#.f?=8 ;4c7q",O<f}CdU/k/l%6͠xtjdaŬ ӁieY=oGOO9EN;'яS0L?.7"<>f>a6p@ @0B$ż*tx^<ϑcOOphtæy_G70s+!2حmu;bX7'8N"ҧl>o؃kDycMRRR cߓŪXϬ| ˖n,fb!T!a'Dd}Jz/'J 9]싵Nw _۟F\LdpRAD:ݧdRCDDs>~1 17Y$y4.E "S2(!""G3qˉ9&YD;MO ߈zpF z<bY1AOI{ $#J!#a,hw])ixD Sy)TaYV?G%"g"˲"1%{Sҕ~/""""aI?_|;nX,ߡ R_[έKH i}JgHsp&iY]n*J8V,ߡg:'$?l I)6VueQCDDD48\1t8$&mGki8O3[O/""""IƃiX6ݧgF ӨI@OFE骗ƱOz{H9W7KQDgtVCDDD YtBĊȠ zJpȠ98DDDDDDO45QSYA b tőI|bRcJpI*/܂B I8INP,Cҳ;QCDDDDDDNJSC=5Ռ0SpJ\|"m CDDDDDDNJMe9yJn 1NBj*+;QCDDDDDDNJ  >a?ދOL"h0zD 9)ԜC fYF(!""""""""""""""2)!""""""""""""""2)!""""""")MT7 Ii;S%%6\|^<яl ˮVPt{p'dY6m`"Ecp]}v.%8DDpog j83=~,ÓN]D6>'IWpߓ_cnߕ!CBCE sFQx쪇TʮbFsI5>f(1H'd22HPX%8MVOdٓMda~ɡꏈȀfӼ)~Y^6<μI X뗳?,ߡic߿ŧKsxIDDV+[눙^2GgtN^;H]7OYףct8RZZK rՂ&SkM].Xb%̞5MQ>]. '5=u2wܓ>""U:w}!W٘췾 :𡲕_Ky?|yZ?u#a7m[1c;Uld:b_Ʌ#B?wg\?cGGl$)9lx,>@^A.EHK.=mXA!ɮ]{˥Oy&~""]Ǜ%UFlpd֟_ڹg&O Aʶar,)y9}4i>jRM[QRB%9k8'/s/ō$vkY&CrXfCzw}4~Ncƨ܄_DEArHr:Wr@9;6newYMrHLG{3DqT44<$e2a'uװwv6$S8f"qe{8X;v;Z 1׈h=9#ؚ$Hv)1%y`C !0 '{j1#9ORQ_VJY] aE|jyY=ݎ4QY^Nmc0|de: 7VSl9`iczjئ<]ޑZb 055 /Sgu>rh\ÆQW'TCDdJ^ /o t87 X0c8IN'<ӻu5D s5,+wkF7Į_ Vqf\m}8 Ǹ;EH^OΞϗ>u%K9!g'7w)|WΙuu_<EGQo|l2Iƣ7R>zlNgU[Υnf3}ݍ\1/>T;Hɦ弽Ney2#f,)Yx[#t͋;|d 5x>]hZU}u {n-Ku+twMNr\)c|C%㦡ԴNShjl69U,]ɶhи1pY7 W5/cVog\u oͿDp/gvƽ+7>6lWͬÝ|{Ǎ[3 IkދK|w; `&nUfRVp%d':6WQeoQCB)ye^XM̝賦2>7 x'6cWi>ȕp`6n>g7q`Z69Yn Gѽk1FC/́wWB{+ܳ8or*]}ť%7'GWb5`C0#? `}$۲mc,+ƯE_e%9s+!"2 ػkQç0c__Èýfa^~,%;aEG69el s}c$7G:3fϡ0r_}7^~40Wyyxܜ&9v-gEgS tx]|,~-wnKkm0 ڷڣ;ɯs~2''f_̦KC,NІʱùK6nLqsӝq蕇y96 4XNvw.\<\Ͳ=72~isEN3/?0!M2*""Z )3k cڗk.g\ȥl%E\w>p>J r{yVFNj36W5zY| җvQ=v ~ьϮ's{ 0b7|+Y FDΎloΙ@1>pIY%Nrٲ]Y,'fۘ}^|n~AKK OXp~p""COf, \Yf{63WK6Ms+))alxd!5 8Ǵ=_N>Xt6SWtqL5r1yeYW\ݳ_} \?I0Etz1K( 3)qbKy Wyv֒:n<(6NgŴg<>"7ʢa2ore0k^gv(eKIP;R8ue{ u|.j-pNeFN~LLRvhaI}aL,}{WjT.6vVjm#3tf̙@$O^N#o-^LjnN0zc$%d7kOG$9 A ȎmK;\.!1]LG5 $n\KeبTE,%2= BNÔ]L g@2q0bh:bR8.]V 6o̱a1uR*JvlM^ SvmG=&'U>a$ՔC=3Iql8Ȗޛ QTrCDDUۖ4I?w3Oj7R]LRӕAulLR3H$;ӇQLUy5<9g/mb犕$ֲ?j>>~XIlΚ5&7Ik%zXV|bZLijy q>hs3!'9J59^gx~:+!DIJ;wYtq> p{9 j*Z_(宣ac&s3οr%]=M+VMeugJL{rI5VM56N& >+Cv ͭ/n~:X D 5pg)%<~70sWIs&7"ءx&n'дP```P``8oa#/:*!"2 9Nà}|Ƕ׿wθ F׍([_AwEn Ht:N7ގY;Bm 6;!qŁ3Nnpvx(JM# OeQe9[_ы 䈈 R ֽw`$ gE2)Bk4MNEuS$')yY7T`SI,y998LYxXJg1- ;m[gŠbA M9L 7i(}natx79HJjf]!̜yGgj~X ^0x>CWчiu6^q@)y|8m4 /޶@6>{d_t#߾FP {6arz_Y둟aM&*""8ĊW`cU_ -A/Kt'ζjT,ͣHݒvY3 ўR}g; Dži>;t\B9Y̚94'7oR=Z7<$ϳO,MG7^d"V.|:&lJi髲L!=,+%o7E/:tb׽3N)3,آVN&qTFu1~ƵÜ؍kxz.+A#\dcU \y>Nn_\.N3Icci*sC|w[lƝ5O% q`24>!+]J@ˍ׌cXT[~c=<@ $L=0HIbN~^xqa.&8fG1Qy+u3חQڱ\7R6},JVboeEնU?tz f:ӦlwuvVol6\:urP5{ֿ͋˻{S4 ,jkl3 囬˛'T10IKqa`Q_^ISAiS_RB}BKvN2&6Re~ >If1,<TΝ*a[$O6~zȀbuwpxL"k:tͰ|zrs:,Kfٙ&emG/`كwdC:&ءzj'ۤ0}MК'v(oh"C ̊3=}i<>8{K/g Y~JIg!v?Xna&faVG'/}\m8)(9RNPu LDDd@eٮ&cyI#Q*waxwO9׀X6la'6Wcw;O-,TEѺ,yU}9Vd>?ca5g3-AtomX=V۱,`}) h3 ,grs3j4"uEQm0I5m,8`p%5>w_ ~ڥKXͫCƚ=M"O_poy~^`?Kٗq{5rɜ9CT]o`E_SNvn1 >7>/1=j;)2$b$呟{@44`eaKjny)\YJqe-1_b:yx"\fK!'+Һ"""k2sN#`#u"8OJ&&h1H6%$m ֮>n6 R\\Oc֧RvkpH )pg]^(8PA&?2u>_#&dtK&VM8&Cr|Q;+ 7aYU%L|M-8L*m.dĨ&""rpxH&N \$}I}PvG0S"}rz|'6X b7~OרLglF*""""zj(Ҟ""2 8_/Yw^>S߹g/dְdM%l\%AICNDDDDHT(%8DDd`0R8Gxֳl w c(7z=TKÑΜ͜4WUPW<$ BEDDDdRCDD '\Fw""""2Xh8bbX!@,aRDDDDDD,/CNz>#JpIIȤhC>F)->Djff#JpIOL")96Q_[*\,;J|BR#ZEEDDDDDDNZzV6^r%fY i󓕛7h$OX QAO AO AO AHgHJp @ zJpȠ98DDDDDDdkj@ e;9#5#Ĥ^ jUT[P?! ! b47SZ|` @zVvWCDDDDDDzjk3a2N 19DvmیUO!""""""VMe9yJn !NBj*+zu""""""2hz?_ lIͽ:F ,+97A̲zu"""""""2)!""""""""""""""2iYXPpL %8DDDDDDDN0&4]?Bm]3Q\Sds,oKU&kd8,ˢr‘0hӅ!3-LsH""""UC~s]s%y^D4G 4X5R !*n 5ۃ Dyy)%L<Vr ?n:d[DD;t&eل!0.DFǾ{;T""CTMm5S'!ZOQpXX&?s;;T5RW"IL"4RRH'4rs;an ;$/ QM|J6~\ݎ4WQVQGc L 2ItwqP[#?G 5PwkVS9Upe0rTDsWIb%V"&DpJ8pv*"""zUV`6fZ:f^qc1SRNy zNfYr ,ygd4Vg_߃3k)Hʈ |x ""C㡲&Bfq\0<1bU`6SPEDDDUl&bヒƸDdnk3w>f@Lvon`5wm(cۺMh:wTrÁ ĂzJhO'g=Ȗ ;(k\s4R'JB̜:df[Y=+^njEýǏi0 c˻qg3jd ^W&0""CTFzMo8kz2k$UCd~&Ӓ;T~~yLkj0|^LsX!ukMǯ;u`VnvN"p|m4U@k8Dil )y t6RS uPUMe!l31;.y$O&nǼas&&`eO.f6Ӈޱ@ J2eBĶM1w"""""aѪJT $\ bff`UYWF:rヌwY60#q3ǿh&ӹhM6$t8>FL~rqeA*\ᴄ$v?"'/ ׶&"?Ǝ@5Jp Y.lj6l8WF.Zt1UvGDDDdΌt9i"ι0LpLjx#^Et\))Q8Nt_wx: Fĺ8ΑA^놟TFY3z#m,¶,F됓H,J7f{zsDPCDdKKMfSXt%,~Uw"""""} 0Fr2ιXj%e1w B6~HvncGQ)u #:N6ghJp aN lE{HODzblؼ\!"""gv0s|0%m444 8g$N>q$`cwqNlm?Ntv7Ͷ7٭ l3B}x\N Mٸw7mM !n˶-2m vAYy)MMl޲cwx"""""Z^+)ALl_ҷP__@bb"Ϛm 0fb`G bCCAB6`0~(I,ֶجӉQ6VG0Fse xtXq3[w>_N*""X]-p8;IODDDDeYضev[vc@}xoL"o1*kbg ?>5r --؀:<ŪflL&dTb11&=20(!"2߿Ӧ1LͰa/DDDD3#+U^m۬~w$$$O}C+WlMc;L`W*+G +q?ݼ;0I>l[w) acJ4‘ֱ*^;ˈ=w4DEDd;{,y{)7l:s¢DDDD𺵘8">4IIIaIF[.n۶]rkn=0_Sf2lRXΛdwāEr'+f_dΔ KDA䜙dl4ppj,0S1,5&O߀fJ"r9rK3DC#_W&Naٽg/G$.ߡ 4X#ޏQYAdjΙ GV*Y0l&{Xeؾ8Svz??5Vn$>-N;Ls]uAb܋fo?X03UO Ps;d$䴶.#F#GgmoFf5PRTDi .c?Rʦ՛!/ Z""g8/ȀHMι\'%X8gjbWTY 3!pm-LK=93nֽ%VTuxӗ9u S̈Ĕ&|9׮x Grޜv 籨-n)|:w \|,HueTXŒ)|hMp7hssm2s9#.Akʷ0uf!"""rFضq-Sf9k *)B(nx"u8SH'ArŽh%Jp'!)mbϯ!8M ` bdXŎ4SWLvKL"{t_+$uND֮>nKpNC=8DDDDDD䌕iY%o1.̄`X @%9YrpKLO 9{{vr'%vv3v '\Z+3 $N83#%0$3""""""""zp b)Igp7%8DDDDDDd2MX TO2HIM(T,a.[svDDDDDDdhn057uKWA+5#CĢEH,:N HJNeM;$9AXvJrJ* I:sp]vo9eҳ|TWS/18Loܼ^'7 Sg"""""""Z|boС!*"""""""2)!""""""""""""""2)!""""""""""""""2zl8D0Nt=Pv'LFNዩE LmGT5t=Pw:'LFثmضMSc=U,+֫ #%=DeLq:'LF9Sl UUE)M yR\DX4J}]5% 4]LדIN$""2(g781la6H¡^d2 7)[Զes 'LF9dW=8jF4x/leayè,+şا=8t=_O"""CrF9zcm˲ZE%g ۶ ,O~Lu*'LF9Sh ZV [O cv,LחדPzNi,ׇ fI0Jh  s"^'8T5;DDDD'To9!"}s-zikADDTVp'"p#U !l|P,8u݊Cx\(LCUALI]_jw5pwd j["˨桯 x-"}˴ m~UpHLaq{NW=SY`z(]Jmce2*?73뷡NDDD@7-<:*_+a daQQǑQA0 0*OEQG [KXB !$dꮪG'dO:fsrNݷVu~v[OZYe$ F Q4 _ܨc@X¼Y'wZA.ihFpk/X<ZwD-4K΄'0/7llGh>Q9|r!)!ŋ&VԐچ;AAz3SYS͂%<~0at W@W57XK2/frw3oWss I.>G6 |Nox츤KVuEk;2܆BA{^xM GBz'OELm~ϒzv,9 ڟ ژ7{=W 5!NA>W`/WJ&mǗL);621aF%}/ \ UQzVKe~dx/jYZu8ٛDCQ(J g( ]fsx?pԺg{ZZwzޚ>e'ЊgSb`x>#}7t2}N"?? 2f`D=:yyyue84lY[c ꨦnښş&cVzR C ɅK (y5w<՜"azac5 l<͆2Tq'L[s=#w>jM9޳_b8hIDATx*G -xwcGƎo 6/bޢ}; c\V>,V,),x !=6ahSR?GX_׸s*VQo~6imX}REper<|(SN&aKٗ#oGObQĝ;AA~ jn.y ,E$J`{FLm 39m9}w*7}W(M{'\X;:gZ"뾝k3NQג#}ͭYWj*`0`4eF pꈦP^N"a@'I:@|dN\#*,-o.u #՜"[r[ 7jwf(Ej]\7??[zѫk"-շ@oc:!͔&4MCG"õhaSOft_&v8F*C3 x&# i_IN\rHF f珒`u⦺SXY;qX5̝A=r.j2-Z'f 2"~!  =5uzMai-h:HqK@';@|n5Gw"AokΕ?@%oRv_y~u'-bdƶ9<Âj账}>\ƃ/>e0ڛ;JzM YxR~h}>9ȖYxux ؙd)\CidM![NXDq' .6ϼ3+xJׯ, ?%^\Z:Gfa {g*@ך-91cGN#T4rR8ZDx+ @K#m\Zig\˾;o6WQ923<' C+*ȕR9*Sqf{rUvϓ"F8SsÂ*<)4V_|y ϔÕAY ݵx%y\"}oL_"ɾ.B{lDe@d.wZoLM!]ԜVն_.VݸMOdzGY˼.S>ۡsyp_h/ &On4p={0o;E7qCl!63F/ge"?+"M^%Ņ/w"Zc6eߊ]9DdQGbrAQi r+ص5@7et 5OAG#sj6?ܗ!-?m6Wv6R ۔U!Ɲ  %;f .8ޣSd&g/6LYe~qttͅ]I. 0k܇7[ 6zʔXqYI*թ}Xz_!2l׬#dc`#J>H6~֮)|5 ]0؂ٷqW׶+}YV1M+qY~Es/wΪk`&s,x.vl,Y^p8Ѩ(H5v->%K%aq'rLÛ -A΋,Z0.Wv0D)_ ؂$ٌ#g-f[n3rBtco" <+H?nY9=+!KPFA%5,ߘΠʯt+:"3PZ. _*H}r k\ٕs{;<7Ld%`yȪ"T'vˋYy1QdRTа+ kH?囍!ϫ4%ސ dlNu$S3 ٯ^ZIP;Bd[+l|CyVڥ ,ڐ&:ZKmI'puUEr.sSL>#ǹbw|a]%*J:v!#в.9z JhHXcW}z0r`/uLKR/{|-'usG~ ,-!o@B弈:<+!b /l V h?tsVXu,U.9_̵JrdZǙ+hNiLe?NU23А0EF)v<AAb O3O/ss{T2?s>ghFls#RԳͷԏNFT i*hRv-z--T|auS"#9]!JHnnafMN.Y1 lÝٞ[T<ٟ%WCʄ1W3,$гv-w7'f%T>a8D iR=z€ƅ]{9SX(;K&肥cDxhZf[3gB~e[jK]LqtV r9"'HQ@ &~6M#aZI!jԱի91J~ σlLfѴa7;Hu.d\{e[ʼn2Xh;Y Dyu0`(p\ yoؙTZ%m;#6QRa}V e|yܥ:9KVi!30\%Q&G"Fw{׺(7Pte.{_XWj^MS=;hw˙ˑNꡅ; lֹk΢JfF &}GV$A\:ŞM纁UF^|w ˝6w].!t&/"tG!]1 ??=gou4$[FE3e۾MH_I躎m2ظ'eo!sr+VnG5 /}Gw"A!+:1[׶cɻ_p˜\y!<6&>{2oF{ٰa/)G?=L5bCoV ";Ft,ϸy{iL 'ĵ-/$y~ ᢆ^~͊nhmbshjb49qc<~* \aGQ륫-]yf"\MR8q`'% !Y-F)~^|m-|/K+U+ ŏCjΜSqw>\bOs-;'>ʟDݩԋQ 2S򅞮7wD Ofת$S]F>ZEHo6TR#3u$r'gޙXir" :vLcL4LfG&a p]~yGTUEq)~k6o*,!,M&$-ͼX!|=KAQ\WFʑMXlKg9JVNNsP&YÕh) 4'<j`rgᴈ:ړ$0Y :S9KSEѬ-nSpўa\Fd6 5:(2Ú*:ņɝ,#6&abRsb@F`IWǚ<8tSD]œh"yp4dckoZ) tWvoV$#@L2\슆d $fAAj:{ 1ert(?XC[w%O И]~X1Jsɢd @+ 4%/\Q&8s2vDpT+Z7 Dv9p*^n9֚Е|%h6(s( fmcvp0:E|Go:+oy7y|.EcjcҔ8t [!{7L&#PC%Vݼ?{dďĤoAču n9I6 KiZ+5n#Ʌ{n[YJO˖ِU=cp:g0v}Q}4ի8-"~ P9~_mS=:U-UT*R+bmU-t^>=K.efiՖ45tܸu~¬isqM7v*93V-tozkoǣkj[+HZɶtHhU!  4L xAj>QWogS[Xzv̠+a-{=D\.-4LbpCAAʫ:?ZwBpC=úb@G,4x  U+Xl@UUdLU݅kWD4M6ISW$ 뼱pQtEjN#zaj vE< MQ]œ  4f"o"_j8.$23jyAh(4M#3#R+OBSxALBSk$!2A؂I:|LTU,h(4N&;+& 0.ה'xALBSSۼ58+2*LFz*gO@SU+/l0Ll\<~x'AAhD(4%ktxGHXA!ab=F%P,#2IzOBSP$ ycOpɲi"F(Wp'xALBSSF-9Rr'"Ƥdۮ˅E< MՊ'AAhD(4k4QvGE%U1ZēTv- #FMv,Bi"AAoQ*mbAAAA31!   B'8AAAAhJk]W]AAAAh$C+8AAAAhIENDB`Solaar-1.1.14/docs/screenshots/Solaar-main-window-button-actions.png000066400000000000000000002732751473525213700254650ustar00rootroot00000000000000PNG  IHDRIgpsBIT|d IDATxwWy=gZz%Ymmlc$@BMBB' ! $! 7Ԅ`)$vEq/^WҪV[os~ݹw]I+ےgyN+}y(z    #AAAg\Cgp$   `\U㷶> Ã8wQ+ڹk,/ q{)d7]~#PhTa_!>8}3ܤuR԰U!y&-[AALc<7&™XP{VI'VVR^rͣ"N*=<5Z?yocNiԶ^yc{D9l0VXiֻ;@AQ'jǁr՞*l7QThb2 )|16ۊ <!ݏ4   ™N!Պa+hdOX+TӉQ<ڞH~ p󘈉3DmUݏzCAR7tJbc"a tum&V & c>_7mJ/a8A   ,r^'[fr^ʐxe</4: AAiR7zE³ Y &&ad6qiNfNdJ;*5 ѰS0zIqS@P.6$T'^Jg/` u< 92@!L{[<И(]D{)+CAAA T2OZD&K@&Nt <5BEv|um)GUq%wb'P?k |㢇wcږ՗ȝݰ]N0UgUW9lh8lކs ӲgC:5Pa:Zѥ-aЙyZg5   ™N3dtPŻ(~c*ǝ5 Auc X<*}~:<ꕏg|m2xSo0?y8$qh\OtG=&P|a$O̾2Uk"##G 8}m=HMw7"dPFD-"IWi sTAg%HjaY{KEݫQcgSHHTyJo5JqFNԮv.*(Q*Σ^{ H'omy>>֝[1m}֙G-&eTqR%Dو鸌E"K.#.\QC-{l ވ R'ٺ%lJ.XS0N| pzfrl9EN+|Lp} =Wͼg`??5 p",EN"$Jl.Y3tJr<槠yYp~!  ILW? ZUve6ڠ1&\QQv.֠գ%΄֎TJӖq%X, BHl,*Q^=p w3m!IJ.]$A 3ȘP7zZrҝkʟ]5T˶pM=A ѱ eo3rԸ'I+2ρW7-ԕ_Qa\c*EiSO*(@*kׯX g)FT"`NGsUG(@c p(%J8UchTL8=*1(*ף3FqvIß1 =vWb+@J#AQ̊< YT!/|ʷS" bphM)U7fty4(FzMr1zJ1֞[/bZ׾ᕴ{KVy(`w>g8L՗õs3d-|1=&yd_7ʥkP##x: Xqyz'"?yԮK yrwɌ7gU@ :zLOCGSc)'E֖idj 0Ap=[%;NtYqQ.qSI_M(YM>ҹ?|`4ήɯ[R1TLe[] ̬ h.H9ÌRpyvA3 { ٳ/qQn'X;v1^łK_'#cwd ܯ/i/7/usu/yͥpYtyP0y*a7ޱ?}k۳3%|E]|7S(ѤKkɴOG_ ™JnX'`18tUxڶqcmqǪ5ALQ񃀤b^ ''UNS49t4eNӚr MSe, F<#BdYEHq2n*]IAѲ`8 ,Fb?':m=K8S{CbګG=;)2_qu8Qu,T [b[0yTq:߫gFeq!MJȃ 9ƗyrjST+Wjz]pߎqmC|xUiS}~v?py>&n^_ΏoJzn7wkZ,Yz|&_TRy\;ӹdA>I} R!ؘ#0) I"3gm5~ekIIR1_cxk4ou*nhPPRE<4鴦)Kp-)-_.1PklHT<\P5+3w Lk 77/zblT 1aPF[_TƐ 08a֣!&|VW#!(c| hZk1?6$^ȨP&YqW« ))r(8K{(Tr2Fn'  W†>ZF!DaPNhPdA#˗,.Y+܎NqYsv}~~CQIm Eʻ{xtmsmf Hzqc|z&HLS&5N㩔'P'e p&)'rjdǔM RN'otwSiF (=r9͌Olᑪʞ~F+6U]cv(ǖ#=@hrH:6E? mE(>Ip\~@YcAhE3`TKȃ sރ=`Xhe{ՋH+hsv;!6D֦t%pX<=_i*+}Ek/ӯY5c*ex MjNAt'CvS}tL0.O)KVӖ?/B:ɗ =jN'p8.1Po4 28蹓.=CZ/Ko㧥5MabG|Q/#CF"@phģgD_'(]b`Li#H#lnPZL-f8c΢R1麗CoМvQx(2'8i`xV0͝ 3]-M1|Q%{tGU*|ceWa_D#&Q gar{?vj9[M.s\Η{{nW95mJ=[ZqJE~Nk5]\¨pdEGu^YaP^ {Tz9ƟҬec*e 18~>b[[(fXQ@.?Y[1<49-.> C5c(~i#1~ "0Q6|Hi_dž8czH}CE ݠ\x].Y(ϗ90챫Hhohͬ&4-,Y0/~ǎl' Q-tJdm+2@4f-{{d5oU@#yi2k~vOQ"xܩo-b!Y8AWLJ=% Dڳ4zLXxK= 9FN([C*RAhǭǪophHSafKc#"JH +KLה(zaxMݨC kvSh7_*Mi3lhG K>O>pBF_*ї/R6zLߍC'+jDȃK@"5*2rU2=d)8v&wpk Va?Hjtl:DC4kQHBA}Oō/fк8yupa~[Ƿ}vg:׿z%fq1dc8] <!6+)`RJUoBһAU{y`r0 uA8|;wsxui^6nzl{D޷ 3:;8w~xf:Ux(uch;?I~"Љ/V-ǰ7Z_Y}xKG}QǮyd| yA8+%9\f5^oj|cƻ.E.˟w7]F-)ڛLGKZ- :4gM#[xkk^]H@%G{JϦ~ɻf!>G5qo#lv~^usA([a9UJ !0ccND4x`Bɾ$0*hWt,K(Q [S%g2"/ׅ:WBy׮Q %b [!hiʅ(c@R6IA\?GMm5cG m{͝(c%z (Bk@GOfw7`\+j `!J8A[P0 .B1 +/;QW,Mw<W 2i<A&:s.)e=c9< ):diG&R!JBT*Yܚ1eCLVd[6 4A3/ _9F;\^Nc')'6RG9#G) w̯S(:r7JzD d4$=*}&]%gyu$΍Rhbn=j׋>'˝doG{(OaZZ׷ۜ0%^-ŗf2ѠR1" @OBA X#BA==B W`ۮ}l&av?MA3esZ֑2F: rB &1NL(0TbflyX@tYJߓ pV:^`P?VPsu,BHT IYVmSU?pN6M!L[8TQ6^uU+b;j z5;L!r6 ]AAAq$Pz!@wA"Q tgH*r,@`cL0+IυZZCB<^]nrqW<( LRnTGnp*bKPV˯7h a>i; -YMIUbGKAjJIz$T\*8y5af\jLVi uAAAHLd  1Q7^چ5&^d uvH mf=7zӄD` *J.a;)U&8 F'+=Z<k@B yAAAؚ)ֵHY͘"F1'߈;֎}SPo,&cS9tMv0jB6Z2%>oSiߨ0nRƤ_\dMx^ʟ   s~Ky*5Ys+o$3YT\(vԫIX۟I75[|&: 0L٘Ġ    r ```}WxG\\.ssJ)n6 @)B׿bvJ$! 3Ky 4?]OgoA8G[P62L}YANw=ҥKdppu)J`Ow7JieH0c fϞ͖[85葟M Bcl;qغe.+济uo+kQGdjI{uPd:08K|?Y6u}m|~:غQFEvBte`vF[䘹d!ӧxO˺ؽ˴K=Tmqo)ws461mB^}[[? 7f=?Ȧ7L[.S1bȾxK_8RT*c-BEkTP(l26m9]i2AAAhW=Fw򍯼e5ׯ]'~I^Z7}1pg= x=U;(UEϼ~?[#7e ۖHd̩]Wñ ?kܶ*wW;7=CT*̢^\gȣa)=yvc4)a,S_~/x_|_Jw~o>p 2&(& fw <0<:>;<o ǧX[Ki [1&r~O~.?Qw7Ǘ3F^ߓ|_$~ k-Z2A`C?k 456m֚JX,b,35gw0hSXEXMٸja곙9Kx'_}?ᒤ5_}0O}W@lmz Jᤛi9m/8- <8,]wmaVW^m5=lxGg}.e]͛v׳9eZtL9Mb q׃\eǰ*RF(aәTo>sEv7$`돾m,,ᗭ0o[^󮛃H£h-o9-O7x~ͅ`7[w>KYhjŰg7GJz7_-~d?c7^_(?EcdqwE,7]?=$wB3w?@ ^ϟyws7϶~~śxWN,ֈ<}c  1T Lxg?=z?M #?]f[k7}]iR٧'\!p4 m938Wj˴<7W8:Zirr_}y p? jf~ f6v\b(ÛxbWh֭m4 2d {7c^Gw=ņm82XL ]z2fƎ|l/oDnc9$`ڵ>/|^?T|7j]'x\w;{dqcqWW]L׿Q~pȀغq;us^~R. &d{@[:.S. wHUV, [9;o c;_95 \&o| O#x.'1a[ F=^{>~=/` /ouVF`0k |v"#C```u2 4o _׿}` <4³\|b~j3vs$t^r [-XmAst+EI V} #}w_~|<4/ ;_}(rUٹ\7_q;OPfq7?ޛ&]>yzJMմ{O<8rW$sw5,g? ~˿};vd{̯0\ōxj2 Pa?Ϸ~vxus*q)[WMǪWﻞkW`w$z7$u[߇NwƮꆑWjnc"g$ޮ%˘_35Xry]O ն%]ed8dX̫?Q.dp(KϏhY`Ebӱh5X>M'=+OEs//\6F #{ -Io+2PU IDAT::ر};-3gFo#Gr S?MρG~_>̜sGۧ3\:FyL\.|NkXdo?z Ԡnc; x[7ٻK{7n?t!`ګ9g-ܽnjl>W5O5wPJwn۩lYy f_曛cct~C;\*+Y4ȎM9Z<=u䔄iS{n5X:ٝd0zzk|@zNd^'k_0ٲ 0w>+f߂ry+1w@R[?ŻoQLR8Bѷ~_yſ{e ϩn$5EZ.Z"[t֬X7˹0=<IR)V~328={(!o888ȴiӪ ZFFF( W b5 GG;?NZB =z檗W>Os+_=C8oxݫۺ<Zvefo~ŝ?hb 07[iV]N [0ܽ:9nBYѺp gla~r JErFN?'7 fj=qq#@\SXP [w7HbŊw!qՙ M{}]\\sٙs~;GBH= ]үߎoۇiZ7ć_ K%K)XjN^h\`L9Ů8ޛKU+jVAqQgHK8ye9,bEjp gYMF}ȸ RxMv:wfxP -ACvmhK I (9g9yBd,N^)HVvE>pd2J+d,Ğ:WQjKċi4VYWr^Lop .+N' /{xo?6@żlk&f7ZwL,ekZ׾=}=C+^Wd&eo'% ~Ea5-S]}~VgtRKdݮ/ #**pJAR\Qe0L%BAZZ,PP\١x uA W[H^xMnXÜy;ߡ_%hk" J؆ DĵWQ&9mofo%#Fm|({plpi9<(yUN `Y=RJ4ʑ=$ڇim.,50Z[∍[LIag1e.[nHvi72K(.W[+*I#F^eE[宇2nQ&n-"ѸVp֖3mImyAEA`5>`Ҡٗ ~;-B]vD8`C UNgy"r][dKخEj&idpLRI̯g4?ltҝoѾRj&t /Yo5k8,c,z fԃdM,u+Y%'\JYl}ywRk};வh)gXFL?~?~DHLVaO1I;`OZGrh +d_̆̊62s|&0xle$;;l%PN3O9ѓSx _p+kEIjL9%6S aӼuN-ZH?}3&tхռV%|=ֹ*Ɖ6h"wڥzg%sD)\eXJ!jDq3ӡC._̈; ХKt:]I\F#fĄۣիqYD^Cp#櫴h΂E+8t0;u?Vwd0][E:AYhU@I&>Bꪍ{BjiWQI%^GIExiLWs Q`(⎭L^tCB"Dil$4 Qr䈞>QQس=`,tׅ6!Zv$fk\QXŧT1U .p*:}kX| ~]%ڥg%%P)*&G悱@ !y 0`ggW0yd233si4aaa<% C~~>&6!IVVK/{dw|un4HXtp_\g&!>0!-@SKM qևvXpr-T]; e;R1&Xm׬l_Uu4&5)iCЕOeV{,y$X7 .bS(鷑)8|W\7)Gk LiC‚WV]h38 |M@$><3LylE%mzs`4j&':,by3s&.CQi%2BL6FDPݫ$TQ ñdZ*6M r*@Ө-nVےN{'fL?TSmd1eK֛2127-Wa+K9[pkk)mY\qգ"mڃ~UT#; s:R+F 7V٧>J]|g'GHAAtiF6~]tBTS<,'`K3L~`([.QCZ\CU$PɌPXƍ'Y:| J3ipNh[pkoY6s2{f9@NDvƥJA T \))hZX, ͛7g̚5z=&Wh4jm۷ٵ5aݹNd2(1]:v#?I'kdׁn+qw L5LwovE>TZ8t1M2 ObW5"QQҢc h:oUy{77X1%7s(QTumd{RW(k\Zu wV諈E}ިJcN, @y6e3K AT%'2Tܰ nh$ Q\8[x$5 qi:%q= æ$P_eb抑qiu/-Ȋ4Rᴩ3oRW!P(dgrmL!%TRw)9w%V˙gi^Gє _(Z(FcccXdI";'o#ZPM;b2ikJVv&[a=u@rtŵ‡M=L*,<N} '(,9IR_VRINS~zAhBH+qʕ":]wd}3Q4~^r/;+ >O}}T554 _^dKk۪*Ա8ƶNnd Ǝ+ʬnâ.a1q0']}wr6æ[ٲzwǙ[H)r;6 e˱L֭#֢~+J=.mߤn3kl.?3 2К>w(?f ̕{e#ݯ8бg6o!r'FQXw&F8QhCl qJ釙ѱ/K`iPϵhhSǽQ)z=Rhp(TK{L2 *k쎧J2br E$M0MU'6v)87`c ;>`J[JEwñ_ym^3Hvg_3q+^K jn.֞^4 sC*_@rc EּyBbR"WLz=\8v F`()'Y[ Flmm5н; '(|lll0͕Vs0.'V9ZHr1 End6WƏ0 'mqLUâ5j@_TAW.n:{쵀U }beWfd.dҮS>F FPQJF4r E:]lam[U g}mCl\)~BYf|B-7S ɉv> 7PT:зe2C5 [I&6Ktj3 ע%ۖoUfd:A>{Hİ 77td@`|"[{Lc<6ĥ+\I`R,߉ ;?䪠,+g%{a+rͤ$ N(/"ӘT0_'6׬bնĒDv#Rᆮxu|go`fwjUP>z7îV"{㧕h)\%?w,A]y)D\ "![ok3zǖ,EGt*찷+*U Ȫ"WSk7 ^ _':H8;InobTRB^n n7~AYVhh6v=MKXv?m Z4;]/}nd|#"ԜEŜ](I oTbӜ6ʹoD!ڦWi5ZZEo9w~॒᩻tl%3>Y Hv{3Z's&TIq/NM l%=闏rсj2n͛*J$myEZB{ YaMV%)`c{ԃ h9fU bK&diQ jf9+¥Y[&Kra0:Rβ֮roԇ]UN%EZ+[-䪹|@+G%ҫ3:;wG 5ɌlٺYяv G2ovNAA!}ǧL~lwt dhD{K\"4 ШHkflaL??WҎg?^xgd">;Bxr$}hJ.N.*i ك&\v{**&ł^J\!2(((͍,2{(MmFsj- !2D8z81ݪp2qb25 ]&=؎/_=ȸbۍƷq<]mᲅ+Xp+מ)(L6U}I+1"?<w!CHf.\Œ_\41bcPNPPM^'ծ _gM,]hU"5w(F=A Є%.]S%]m+кu0ѥʍc]Uo叅G5u ş5h!ۃAO<1фjNfr6h/MZHG_`̙7YxAI;Eg͎F^!>Ȩ\&hѸ1 k,+椓n6U\/VۦPTs׳OrْdFUS:c182dY IDAT.֞ cs_8bH8_?*Ӗ֓[9?#K\rb.^oX,7..`0ˣ7ȲVuEΆd3INI"9%D2jk]B`!Xr\Ō&F440q1+h }du5h!\1sz^R˙,m/R}ܹ/ao"t7=zDVs·B8qm!# -.oS`y.Z@Zn\L⶿Xbq[]W’_?+Z%ޒ2eڵmXO⪎ao1/HIΫC~ !R5ǔpl~"Cq>tMBLQO\m~"ҩ [W^ VĝkU8~~9OFM'yypZxc~̧,Oo(If}ި6:d3w/ӅP%t. y J,\;7n4r-$ޤ'-sQTB̹VF$om sޖ@]\Cq+̙JyM[bVg+AJ+օ}&7/Яj@ $h &++ ggg.Ƣ%`Ʉ x{{ RɟH_?ZƧ}מ߰x:v섋K _UC0h`;\;NfE2u~?=-\J) &kT>r8<^3G kIs|ҽ5/}, _@ 6%zCzgrV֎Z>ѿmSke6M\Y8^BBv&J#~#08?SB %(2YN)O;W8*fsgؐB6e*Q{>ɚPz/b,羠O٩dYx`_c2KI?Vqڶ*E d+l̝tzs/`mnxrxZiqQ0d$r9!<'O\˖IT9$%$c,pr! z[-KҲ (6.pI)'+h\ cO}̄$fW48yݍIJRf&^7q![(aNGYka&a#e2187#$/vF -K xTvAL&RSSy1 6  pppd2UOZZqii$&&rkUBQ!X6o-Ͽ`Ofk 0r'(dsAIAJ{$OMҹ\G̤!ńukw Oo!=t(6Ȋ9 "^}?+2ۘ cO-?do35ln Z'|CjW?a-Fۧ>7Pjzzj#HZtq7E]=? MkP G''o,f}7x{yaoo$I(b)pd2aggG^^ccAUpԍkՂFa@ ?N:R ^Hf8Ypf..Z޷=#?+/>Hgu &sJȘ)]^6%qx%lo;,@ @: BP 4N6Ƹ?--@ [-\J@ @oE @ @ j̿"atVwC 'IHH]@ $BP8rQ@  @ AA @ A@ @ #@ @ 5F @ @ j@ @ ԘEc$;5 . p*R)$-׈'.ۚIˤVݔ IJH"=Z)R 0K:\o@ Ԍ~:wL.nq@ }wAAQd:C( _zwLbx3ۊ7>?+7q.[űa/FjpKU._CMwlWKǕz$Lu։K9H;ϒW|qM; un&A`&79,='Ah Ħ[p jJTjHNIˇVC `0cNΞ?M~ؾm[ۊ@ /sSEQjÖ7s.[!w lGi å2s=vd ,=>M[4 ;UOsDEpzV2W׆+*9g7bK yҠIKy@>)UXwu+t -@kީdFa1#͆4!ISߪOLL IIIhu2]DrAك5ѱjOBL뽴$dY ޞSd 4D</f lBV\ yʍ7EB@?GՂΜ8L $;<. ,Ɲ%R:&{Z4Ǿj1SO"J?a~N A1*(L&FvvLQ/TdphWq] Em%HFbxGj֢%.gTN:J1]cx0^ĩt6tڏ:nt3HJJK.'BHxxo߾'kʮ|2$imޅwO`8,DO!.MgmKzz:69@𶸹x^\$3Vcc H 7 Y-~R<ùٗ÷ϺO_ȗd$UErky"* t6ole'󂟕+ me0ApppDncm[Ǯ8n>+#G५`;#j/**^%K_];] "8gH!1BDh 1*[K{\wlk߉Ƨq6 ;(*uZO4> i\b4qusC+k)0ord Ʉ '9i3_3t)ޣc63d7CP#yy9ۉa!?;b Kvłh$ۜhl1#K8W5Ð8bο ûj4bTA`SW<<kEip`.Ih&JF$Cc(&Z^( J*JxY F #, j%51<ޮDz7ErSI1c{_k`@B"b;zkb42.?Pr/7dk"eհ_O&5ӧPAjd$-5;;;ӑ0 :TBr#ڔ ̙ܽ48+>Z"9x0/O.fE -tϦNwä;m㑑+VJe3èY&Ҽ)qQf/3mn_7,턒q= T+@rCP\Jl=/;M;uO%dpfIRĐ&;gp>Pa Sc?v/w~aS4Q&(NjB~yNH|z>&n> iќvUK(5zbr:[G ޖx*.P/2R㧈N̤@ͷmg_ s.>|i9$;Feא֡X qL֓t2& GPT߂%h"IjurJ*î,{:>8CKbUva[&pJ_@m]^i 1iҔ&%;x7mg8w a%j6T/~ݬVTyh0^u{-x(]a#'Ef+QZkTw`;17@#%og=δ׋ k?Ir&=|%h|1^~ Õ$[MrgdVŵ>T_x#66#7P T N"aN!.5FhM[Mӌ\=r83h}ӬA+W#7_G:&y)PX [(֮:Lwg GY Wnu0ZnPn"*KF.?!QNg+)GC~WnV*a؏ܺxhxq@&hg</VW1U N/]+ee NzǴ.Af::،)I~{LT3-w0K}_c[}KJ^C_S2ŭV,+SQPT2wa,lZ>8~8O4h؎6ߖthIYEvv@vvERF"AA%{<>_wxQb=*d Q~3Gҿ <̪>ΓbUrE߯#zO\TV2a1֡ |>0>?+6$7zNB}Y)l||39zv^)@ l jF$bzUcr>ٙIf&{, OrT: '2Pt>t◮j`T-* ]їqq+dkprvvFΛP}gOrΤ"ٶ טu(]@W7ӈ޳`z^nHhi< ^ s6q?4ήXw`5ó_م0:4b#.W5ku6z ߤdWA'@K8ա%=;ɬڶ}ؚއ?c͠k1AMD~2sU"u2F'-6?%X=*ZBPeOƁh] Erj."IeŁj)AO=Ĭ֒yN8DZ(į6t|z*ҒJ\b6'U*9?l:}=q|>3jm*iiNVU>)8!6<% eKPa=}.Ùp*3ދG/buw7mՂ¥K\#--[[[ Czlmm H^~NNҰF}?w]-BF- 8c/ fcQ1 /f ;$u ºXmʡt6o.ںU7rr#w^$WreAt*u)T eBed@QnC@ 0 U[K.:bd_rv5h1vۮSgMd,=F%D `_m7% ~0]s9n޺a2uu~}e-@ƀQ|5a//aDWV@Ve9qP2+EGǻBmWn  ..ʫ*'3utSjPɄR'2XRHQ'KN9ήN%ہy_2oM+W\H3aFc-{ ߻o/ :uB~QTd"yE3`7Xz < Hh{*>ߢ0H>Yw%&}uao1!}>Q0c,3tOc+YRCpBDGd<(q e>'F~U֦!ΡKvD|g@ ͰZPG``}.i09Z900K.XPvKli M*߈ A?u[q՟0^n#ZYN#-nwj(XE%ݯU%]d &7ILKEBbRŢb  @SQq.:gsK=T39d\B0Iry]䆣dnҳm i|ĞN-_D\VVg'7E3Grn22l#ߟ; U4@0~'0ٔpb&.=nW0vUQQU~Ԭ,F`j>9*`}v6Ea9䩀ma2;\$LQ(U""yGU yzrי~!]M|.ξ-tr{SٱV5CeډPGw7 w10-FJ{C6pc1CٷG&Oe;$I8Bӗ IDAT̫c.|9PS6G0䕩] }W~U4urŭ{Sܼ|T'0N7U+.>DӤmcw{p!L ѕ-u?]ɣ+*꧒hłww2=<Sh|ooO/01́y߲:Y%}Cƽ0Aȸ7B>=XR򀅘53ypFɿ掛CFVD%'# Evj[6;yIw o+*l:ӷowUTǿ3^i@BG E~{UԵ "tHPB 涙 MHBhz>ϳi;gy5 7///ZZZhXVڎNM&1ͶSDn>6ƤSrՙ|ړ_-yQfvC][j[1]̙F[M/ Z47[PqJKS  67,I}G/iOOxoyMl<9?Rob9PFmҽ6(ߔāhl@EZ[CQ"$.*WoM\LRVM)^w0uԡةtf 85ϖasx4ZtZ Nl4VGtIsjK嚟OB15S9t/+YyJ9/:\4ѝ+&_q2;Q=P%Hs]+9ҾF}&/9Y,6puDنxӵB]AK YO Ls-b}H_u;7.&FrplؾsGcTٱ#5bFxAWy~ Kcpp}ԭx8^渮!4*%W#F> pFp@Th4JI=wL>#zH'{.֞jf:VqlH~받fryLƺ{1?@[EՊaA]:z J*DU ! 8fޠCɡVJA*Λ9jwΊF48)).ɑs^ʃ7epӿ 갛xxm0l}<4UGddFim-Z8wp/xY^Hd.j+^'9g1ܞŒFe>CZ6KkgwsUd1npO f!nXOI`NP 3BTT+V~"8pdɲLи0M) |ڬ\ȼ>G^*5d| 5'^Fy;b@  .#,=UvL͢LPt$杻i-Σe j~U+ }; g& P%VsUlT p:Uh偾u;d'OesԲs&Y>yVY< ށg) 1u:"/,z8z.s×N|IM]G%vlD( PSN [nI 89` T[l@!ER'ƐPC56ZҺ~ݚ;|xw6AF7cEfDRh\qnsJF`j{:څN j6tYa5s.of@ 9β36HWcɼk5۶1k<:d"<Tk X0F^b9.(F C9ZĄI(,U}ހv#s BoPHLH"1!i04>9_$s!FvΫ}s#̵delPײNm#K3X !L܇`Bm0#Cڪ\*)6a>" 'oD8ޱ ao,aBaqQTpnBt:P__RwbX;;cB6W%Wfvѭ| 4jJTWopI&p8>|=k2hu(a\0;JA%$Q]O@5ʴLRF"2V{)V#2>V{euoSOՖL^y~iq΀Ѡkg-F]EdlnYJA8"ЕCY}2~ZE&>I ᓱm{K+ɣs;IGHƍcdHe::v{ȫtkFsB6Ʋ4;ɓSaI^Q.>1:fקXhlj.r\:l>wv%:Z|ݐ!C2d6>K 9>~Q0C:[r28ؗC#sG5_~Oi6R߮~4\:ja39l)>3WuX/Sc0W&ٿѲ\8 2fո3jT8݁UE9IyPNg?è\[ϯ&O394Gmg>$&5GGQ_6gF%Xdf7dFE+FGIY (M1uB0}ɒ\ڥ#d3<=E\9x4\?I˟[:O~1/DK*އ|>}WEu X $_fNLpݠV[ ,NᱻtE#m, n2G%V*`P(Q̺f]y\}'i|&imlb,F HfN /i @}A85&|{Te'?7譣ސhjlݩ$cGH69֞L*_Ĭxwl[ <4V,QҒ4m8~DȤWUտulxkgsOl\9৵RSkQb1NKDkɓLoR*+ۑg gPU3S\XJIU#456Ѐ Q ?=0+3E5L`b6o $CԓrM\$?'l5}歏Pq%7ZK`35kᆎ}$\[z)>H}by`♞亣nJkGmQt\Ez6Ӓ/Yϊ9ƱٴfK-;-(5u o'bUT)kX8F85H g_F@aI:]Z7oNA\Rio˟$ YkNL~f`_?>{9H x⏿`W.g;+sǥgpt\yL<Ņ?U!5zL1ˋdoy+9˘3ƛ*0˜xD%xƊ9hgB[[rjxxzֆ`d`J[s * ܎! yW^|$ IA̴sdQ|/yܿ )>&n+>+-+/WѨMD/rfvƑz e<O͎ޛ)7ݗlpRn!q.{}2>oC^ r'Y'8=+7x,yiV$t^1L{$\hhg<~~;ˑyqd(f7$5y²wؐ&0' TJmNwNW__|ʏ+p۶yI#賓Xj(*YI[7//SeW!A}yT,ueT4bQxAOm+|u0^{[>@}}=6o֖VEӃ&N P8*e7. })lr`Ž7o8ydQwR^Hd?8coQ4VcwCiZkj"| qQ  z/̮|.z;) "bb pJɦbz\c+[9Σb 7_;Fm." g+ooo jaZ z |Fp}OQd^VKWs'Y7\7*bq'"wtd+(i11dO ښÇmx>7 AHnA~SiygcCٮ= gq|}8{?8祾̾Ƌ$܂ AA#z ~l\J#I:h2!Yk~uSiz'd~H(KA],c8%dS\xy(]~{Z-80gi$sc8. /Qp  &j^~w6MN=Ax%|5}#'8K 1 Qz D@AAJ6>AA3YjetJ>AAAA8+ ۳N)   YPAAA"    (   o"    Be#AAm{{+Dh xr8ݧ( gPAN?[3M7||'tw&8Kʶ` E)]˱z=hu4Y15|yp<| #4i&ær/AETD;2f/_̺}> RAfvEIn>lfo EI#i>؀ũ_T*֦4h>OboF IWa}~RmMTUTQlA!AxlAehZ+}"hDjK9ye(/"b16* Jip1 _=MGb޾'d"? ):)q\%Cb[==emNhqOM7 ˰;kT,-vF]\r6Q ߿ ܏_8Wc$ȶ}!\͙v 1OSS477p8tx` ҿYFN 舞q5&g#xK S:@ve'K/@v5*UOzIlaލS.JO4n6I1Cidzn/~TDNDTfUAVmo[ЧϞBqqh/qm B)_ʣJӓǦVQd@;N_XҤe\?~Or+0TWZc[hQe|=ȒʨE2wѯYw!}PzP'$[2:ES~{&L)LNA<Īk\/k[[AWjw*4:; r%ښ$dnIG\"Aᠰd4 l۶Ǔ󩯯g0907~^M˾'Ǿ746`;sfŪrg!AדPiQ$f ˾gSqNXؼ S|%$ #vW|]? Y {qJϺCݏt %O~,!'IqչgB{x2־YVN1-qsH^x6'c;- fܶ}laƺC8X,F$NՁͮ%TIס=?JޡOm"' }&?oABEE>>>zTUe$$ű}v,˸՝ހnGi%7 =LS/f دجA}r4UV`ܙ[Y PЦ"{cbQpzBRӈYNn^%(\6}1¡`˨R{F4!)Lu?d|(e')bl ɁiLհ-m{MIjێIϚǸM,{+;,9e]laAt 042}eͨz_0{s9xb IDATʛ-^8*wq~M LD׸b"7{+(z<*"ٟj`^+h<Fb;=f0X?T=ضŀwh7m8 D$0::Ǻ͈7 h|zVSu:9(Ho-JS9yDl7ZG#vf֝J TG)-NVa-mM Ԡ- Ux:lVD{{`xD W5y{)O $UkZނ60RŞl6l*H:=~`ƥLTT`C#2u>}ѡZr-Ԣ{SVa;g0Pfgτ.%(0f!%.ѕƈAxI)A8!Lsp0s+yTw;dgcA0 ݼ{mڽqnekLL;2%ž[]$x;^=޽6TEDOKG ( l9Ώ)$dp?l̕Om%x5ovҭf ^}$W>.>|t QJ'rϓsil&)⟿y%1 {! ChkBB#w5Z GUCq:MMT >4W-tL}Bej+gxlmݟޟ [!B`'oysj?>x"q |x7p9ذ>$` bt8NTUneoNZCUnN 1 ̠% >6rm)Ga;%uGA .;=i7q0Br~-DT|4\ڋ)2Bm~>u h|'Lxx|kSAT?3>wPRɖ]j"G[5h1zD #kč|I]޲;9u hc11#$6K_df͌-P[? [ёxl]ƻo`+*Ttˡt$j6q8f.SO+Q˙52 o)^{dۧ7-'-inIVQDnq^̱aO9{}Ce 6ߒs~ԛ7^Xmyum/o>Oηc:oq&X<c -7p8*(8PXYSQ%/BRYJBڣ6Xm!_ 6ɝpt\{ٰ $ZlcgNX:7/†0"e1ރ T[iiSA2`6p3!jk 붏]%/2舂RK^~ Z"0e۳(u68xH~ q%5EdlÝgVk4Q 6z4a2Q$sJ[Kq!r\q;Z4R›73zOx(.y~3#Ҙ7<.fyOr%*o@ʖ6QhIq~udxG壚t6_Cbfۜb 8 V>q;?3eZj &*ݧF̴F|ejBDsrO3Jʄ8=7JWƇ qGieUÈpQ>|̋U0$qOsC졍sDJ㵥veDǽ[dnķ]T2A5k938 qe[JO_F̡f 3~D0cM4V*M>>Ňq#.ݦ28K{ ܠTC6vvA` 28,g@@@=6(=XKx1)d8{nl6TYɷ+6EYPEM,sV7RD_66Т%>P=!Xw$pt+oP@Nbl_Sh]ʝ( NA/y-A9'2)YUByf&%NT52T灭l!a[;@g} u6W,0+I6wtZ=`JG]IxOqZX{xShjv"պu2~on>6*3]:LF1 ^JYɊ>ȋ[w=80ɓJuF̠i;@[]nZYRQ Rz W6vZG"OYȕn.=R/f~Yu;V[w}vv'-!7̣KX"8<ֵAǣ(:splcXaƕ &:O҂H\=!-K+F*6GEL3PhooGe0j(vȑ#q:;kV RFtOc41& rٛQ^J]^.UNxezh|ج֎ƓشLN̵k,cU-` 9wNFKH;"늪txNCi>E֡P_@*aL tSxɛa306@$c_#)Fb fL7@m`[~cģc(7Zۛd7Ж.3IQj3ܽ1T+ #{ȑ 1r/rn c_󴇁Oe`4Y%~ox3WH!H @i"Hw1:N끇Ik{g- Or#Bk t߾ Ƙ)G]'h52H*n  Ό3_1/ٯ2U*mDA&|0D%y!"/&5qAw}=x]E&jyvn@&b-?ݾ+3PEp:;daKd?T984Ӡ¿%XJ%EhU͔ZO,z$pGhZ∋CU# n恟R[F z"bq߷} (5աhH卽wBnE}a,| vW<9dٺ@˃Cq:; 8|8Xb+qHg% Q%1 +V)q9 dy39臷 ?wnmZˆ,2sڲز4Ғ$FRǰ*͝+XT$S)8#|ZC\u؀&z0iIܴagg MɘԱL81v;lszHFw]d<\,y^6H$7x}jDGxZ(tF#N+aje#z$o8٘_BƮU΃T-= -Aԫ@@~]rwe~Ӹhb2VrnnJLh8 Yٟ秹\-A 'L5JK#* ( ,F;e>  Zvl6ϡU;5S:hCQwVנ K x d-:`Bpz"RUEˉd╌ q|% áӒꏌkJ8+ߢ"!17J2}w;kpz aLAE`W#2rd;p{f͌̚?-_?j֭kY5e s˨ձ̝.L9(D 9~9, b(ʡ @xk&%" `Pu~~~L4޽vf3Jvɘ1c  "7$Daܬ, )憪:\KfQ=|?eG@F :ֆ 9@^hU~UN SL \dbrI RѦj Isq^f00uF2~w2A$!LL @VR%Ga1s ej"8.tM31n/x<ڎh O>nv^ʶ)ux׿>{{-vܬضUұ9ILO\Q5{ɧ_=TS7UUEUAhBaگ_;xi8C s1((WW5R/Dz*c< fisHMG~gJ{0oy3(<2׎UoY1N8p Hm,~ae3&4FV 9.\aӧ|S@F1mjTyei e8J-%Դ`FcͽVp!$c }_R0m+|u0^{[mx^%r^6kXNQiZw"# 4\x/灕zp#02`#/]=P A4W3O7n g<oq:Vz IDATkQl# ш` ͆fQUu  :^ hq!)[h́x}_hq}S([+kT,cȺvhPW(q^}VA8"   lXpJȦ&_;6V &  φ&j^~w6MN=Ax1q.f^{r,'AA~>d#~as3DllI9%A+FAAA~AAAAS=Qv2Σc,<-A8۽:twU_ܽw$%\Ժ+jm]9Zjպj@EQ6` $dܛ;B$Css=y!Bq ,GB!BB(!B!"<!B!G(GHzF B!B!a2:A`B!Bq4t]'SRT@uyP(h`4(꾀BO b00Tlv'8=?$ B!BN($NˈOaN,V>owe%ETXnPAuPM9J@ᄢR:5}8 i!B!D4*KIBR*h4YtpOL! k{E{HLeNPAQ F#d6pB梨ʊRe`{t'H@A!9HeA*@ft+D |AvյӚndD~.Q]dKB_琔`$C䕅乛",)oC4`C64t6QUdKItu d53(ڀ 霄B!]L(ޛOiQ#LuH&AbJ_M|b2FO]LTL^INKDdtրBRvrLnB%7Iq(w}_\6eۍOX^?ՄpiQ~8/.%BmlӢm8Á/i ߯%^Zn`ciCuZŧ[B~".'GV׃!֬E^n Eqk{_楴v QQ ƇKwϯI3 yD4Ϟg#n> Rʠ.hga~'s;\#bBuBr+G[buJ||5(*Y#\6D+|R4%&^ԉڝKg3X~q]|%ICȜڽ*CQnAgr&ijπ\ר-Ia1"Ɏ}h5*WT&3tU{[f.TcĬjn1ͼ2BQ.fNMKY_Ef98unKI✕>>]]g/Oƚfeb&.ci5ƙ5y~^PP6%L7 ) Pb00l)lǶ &45,X&Fs~Z+:+YXq-=xArwmfl:kư( RSD+*m`Qeۛ^V78nbhMXT  w7rM)VZ.YBC䭯gT g[ ORxFF@Evkٶ)fM KP7KίY@:!TaΚm\^˷c97΅}qqyz^崓Y&nR7c{V,m <~U$U,F37Q6L>oQC[LjNOg0D٘[EV0ll(v2Tb&12q.B禪$={ٴ}6-cgq}W3cTCyֱ3&1"=H+ݕٶo`ƹ׮dA _׵l*m.dv1*S#/c6#xԮhb7ϼҒN -xYCW]L&zL d TfÏxX)olݺ5_f|1.Ȧեú%_?㋥ȫlnC7Gή2ZNCRe9~Ȼ})Okd*|9sXʝUMb6{W!섪2B Ia7.!P0@۷p2-N}}V%CAiz|B!4-D0,7#!oDcݤT Fڈe 6‚]& v{Xǰ yi?qDž.gu8b\{Ξ5|Z=Y懄..eelxA5*7K*6yaaBVcM{uX(d(9q6nISy_;ڥU%ѥQ^׆0apS}•-vf#鑠UN)Tdi1&Rvj7[QTt+V :ێmG5BS ؂Z2Bj+XHF`Kc!8v?1l 됇ffڪ*C/ K0tRYY7졦ϐߟnaz]>jPqn_ü{!-'0-FxVGQ+|g<S\#IF ~?劣M;3#8Ů020΀ŵ<{Є^.]8nQT\ctdXvwT?n#xTBTw]5UbYLWu(4N)jr&DI4= ->ʃos.r2Ω+:yZt3iG~s!|mภwer-*`\n<؝’>ֲr⍀^ͲϋТq3 ;Y;ogqۊٌIn>|i>#qF,U@dwRt@.JfL%:lZog{pGx垓(FF[|?Lbp}=/^sO~06/? `Nȕ??}~}{P0OL!G4xIq[Eđs+z7x<Ōk} 5TV9:~m WiZϒԱO㭦imLa漡F KwfgAgr0Ϋقn$=ҀӨCՆp:FHxN/DUiLnq5ϭQ$c󢹦.fWY9o{0;:vMmvEaI1!i\6>ۄBStMd2q LfBnLmwTiX !o-n'n&wF o+\o㼔:QcNsf xs=|qR=9"~[ ,r7U$%'pG׳lQcGt:y{}|IFR$)~uPc- k!vQlf8[U7U|Pr(:Y~+d=>k>=of=aٺFwoXt6n]OaJ W^Ē0/BdsB+ǭhH<%Ty38]1nKF+&{k *}݉2dDZAY% D dĠv4]l ;h15wD_f PF!\/A9e(F4-t>Ù( Zk%op( dyFwL܁6`tg0bq#$* h}=πNN) kwLc&F PvHNE]c7լbW-dPGl'L:f%)qA 7]Lg``+T|5vHϯz""UT N($E0 y@`Ue|?x`|dZfH#)V)lј [{͚O2qA%5Sd_tE$zCÆɿ{tǹu}.Ѩkv8?ߊA.-Du_,LrF5=ɺ]Sp޵L8ZмnMYnKWPZ﷬0/v UPZFb"KR,ķB*(kf\B\# ƠRVYA (X8۽#QMkJc=n5)T"!G\0l|nn[UX>]QK@1^ףccqWGf8v66{<%l+9n,M`}%ŤjP0:nb**M*c͘kvu4}`[=mb7JVs\7;;;ULocKe敵N]բT71.=<.5r54RLX Ki n '8n .*`CΥ̌IPXѪGl_ U}ђ:I*ulxbmmk5)(6b޺-N; \*ؙ[3q*@㮸q,`Y^YrJ.Jq$nj< IDATKl>|QYgܸO=z1](ʹM 2:ަx`5F¢ g5 BY !"TUa2`jN^ P ] mlE!..f8N9w^8v6Zu(. 1e9Vv&uӆ':?Έ kCTjYdolM36V^cV4|~V]`EeY1ܐP;?>H0"vGig`pRSԅ;۽]y~ݦv/:89'jRP;^1`bx -+67P9}Y'1Ϣ-RXe߲FYdXl-V~Zrbf!|zC. YSج:ox>kS 0kՊX(z4>_~7uBpir( j4n}SH bhAg=?o{Q]]MBB& 7koS 'M^0rz?!]\p:jkiY[à( ŵr ΈbV<?mL)VZdž.TŞ]# nֳPjhj~qQ6*uF!}T$7ol[}iԽ]{~=bn4*3לd9v.#FBu V;5bF\9c\X~[2q($'d${i5V؉ m*&:PIuruž- ljPQŒs 4V?u|{l騑S8Jׇ9~*sgẊIk4vR @v}]D& E{; R\T)1c᩿M\ hTߞ];;|5Bv-V?UU̝I례Ϗֵ Ö/N&;TapĢ t_1RµaƅNP/:ΰi瑫#ޢfk>!G{ yi8nOp5 Ln`ui*IfdYH6AiN f7glYV͛\dk9;ˆr~VFD)(Z%TNS13B._w]TN=N-$ndi*G[]=W$2} e!8pncE4[(|QsLu?72zFv\䰹 >M?0rd* y]ˮpbkp{L-u&1k5Q#R0lMڵ #u?U<芉C)x pn%oTZu,HCBqD((DQ^RxZUW㈌%>)Kubs,#oj׎NM:YT;CO# kޝvQ榑&wە2LƱVZ9'u 5~ln+ڥ q!~}Vpi=3BE\C;Q/˘@%)Ҁ5Q\ j'xCQaUSȜڽz7/w{b"e}Hd '.W|E_;rއl˘vx%qӸpsm`Y2ظci<~I_gB>yQtssyefJ;>J4TD.:=[}"Mxm)9,Sc7*㹄BKE!:6*KK+J1L-ÒPۭǒ^b&}J>Z[P˪XWTZ'G 7|@!>HMFyubo( (J::0~G}0 KVtBD]zp~=O'H(SZuhݡ<"9W[\7f(8_y\?ăr ^;ғudGS]O-`ܱ BVD"l*Jyk!P#Gp)-2/Դƕ:V>k]>)C0T`wߓ[cW.W׳[i1"gke{9[8)G䫸q<.w&Mbdܵ,ۯaK14B㛢(f%&6 wpE`6Kve'P8fl|GUhw}4 |u؎&ښmtzUz֔R3F̶h20r: Ƨ؎ 7A3d:=VlaOj [,1s'+慸yYzoPPmItuRuJ3W<‹M\mi>3YgrM71k\GJ%\Xo|şZ8YyEO#B!EcⓨԃyhJ Qþ *4xTq](q7MGiL/;]8{:n^п[){\{C/+OC8NH&%zCPX&hOd=R_O~Y=~JLJc+=!hr4k~w_XMmC!: ([@YQCGfZ<7⌌a (~jI0 >m1PB!4ń3/=҈+)pgRCºCVg k ?V=rڇ~LBq@'gj^Y[6l=;Q_SMrZ!Z;<!B>4WTSq=B!DUU, DDŒi-u]\_[Cu8#PTf5kkHN " ;! B!UYqߗ|x/șr Ea˦KY׋5[~y*RCA!Ҝ9*]O`4'-=.x($?o'Ue8"c@iܖχ]9k73I@A!ʹ=}- EPP ~97̞>r#j N'h[_ˈ%&6ؙ݁xn*J "*͉djvnh!-DDL7 0G~iB0^& ./&'q qz\R!辖Aݎljhsױ;w;@]8vc z}Av;PO]3E`0`0'%hUw$ B^̈#.}B!~-?L&p\444z hv2V֘"+!SD[ \B!uCB!8!5t]GQTUh4t:! ˵\7u;C[ !]y?pQO=B!=2` -e(FUn|8qऍB! %КPB!B!@hM= B!BG B!B!2 (!B!$ B!B.B!B!L B!B!2 (!B!$ B!B.3B!B#MQRTLe(UP_Y;wM &A'K'X_J~q% $.9D %'$Q !B!ĉCsgbMUkF}GLD+h{Y:ߴ=˯Hb/g˒XXz22h陉^H.[!B!ĉASPMP8(AQXuU{e[9{LKs@ȏ/`#>=`C 3k?*g,bp0rx&I.#=lټ- Dii'D/!!B! A+-μ8:!Bג3&QCcg`!l5z^| Ad9xMTa˨TFI;ѻH@A!Bq(ەGQLtP=t8[NQYQ0Z0P16T 2q0K>7#8I@A!BqP{1eiPD7`nuɌ B%փ +F| (!B!Nيl뺷JF?AŌQ_Z0&$М``12Ã$ B!u( 19CQ~:6eOLHR3|PңjPtW` OЍ0tDN!e"!B! NfBPMȔĩ_Q |nrٱ W]۶1쌙6C׏ynȔ@Z쪟 X3\tRD#!B!ĉKPy,ˣ^dg1>e7I'"Ȝ8)R4)z7k.dJ| $ШؖC\u"GR^~'(Zu.cRfB!'&ܥ_rƜuS9"f`dh9*AûKuJ2*8rJSJ+Z2GB!'[y2rBz&HQ̦V[R6 pR !.S;+.D<xv& B!BސO糾,-e,si˂83rьa RLxP-fLL !"_e?QPZۯa0ۉK&cPv<S䡅R~C:Gb[c^ia=Pl//WA5ZbHw5Pϱ>9;$~xE(7pp[;ɺv;GZ35+ _WfW3ulNwPr> +O6== ,";!gqCh,] FFND>g쫜XDQdwRt@.JfL%:lZog{pGx垓:ȥkkWso||r#F0 "YD-9SFTC?-lnv/mf%,)!'" (!88C.zs^HWL? Oءf>]\|~\1f9y\|uc\ߴMUABrxKz<u'o_Ί+HOV7+YAfw|U-B%t)"*EQU}kY F" IDATV"Ke RMnn|?c̙sf̙gΜ!~xiˠa}h2M ǻV>11̟cehſP-fvFncJ 8xq]QȸVX˜b*%l{`Hő'1fXϿ= *] qċ*Fƶ3mc*Ou%+')wX̷EtQNRjI*\: FQx&Ϝ_gp`BX1HT=+6Wcrtd&_==&\c M;׋+l=c (Vth}2cR5 mUvۖ YS0w~U1ݙpyHPcxU@BuxzL~_T(_M@vØFNa~uʯl]|]hFTЙ[ru} -ڌ8|xI>BmCA!|i3!f Md6l?@J~{דw=>ݸ}eGK4v@@n.Pc9 u:w~m$sT|ѵ*`-Z.FN`/NnܝgnU!cf f|ˉYwK3mg$j*A g@M7l=?q&ŗ^5._!JǖLJx4QnƓ,YsLSIٲe?iE:JhO=|mɫ(Pi֥avC3I>@_`%juTWߋpj: EݙƩc9_De~-'-cEkWБp81B2i$Z!8BQ+_,}H+'VDܱ-[6z>Ҷ#o2L2M)nfq::֌tr4hRn6@˫JiḽeҞ}__inٛ~ӯS$a0C[#[X>Co.m{X2M }6L6.=߽y+P#4b)GA "ژz멊)v2exG{L$=SDDF5a "< LLu< (F}>48#؛% Q.pа`TNC:iO!A..jhaJ`P*:\ tʾ!쑀B"m/^7N7^I=oFdJvlvinw Fkwwgo`!iwaΗ3n;;V]O 6-<2`2MC^n XJ H-_s8dQ'IK:6 P>THqOpgyPP&,ĵlN&c/n2ݖTDE_/xUfu`yzekw: t[L/]dFt[1BePBQo !cqp׿Ȧ)hbQ ~@[ׄ!xbtk ;7a-\þ yӘ>zkdxܼ9Cleے?I 44վR9Kl6O?ȗWpL3f0:Ee fs̺݆yU\L (,%1kWuş:T,|:^=<+?ՊM Vr7X|jަ3Srۭ6tPUI9j4 Ptn9_ (6oeo̪IM iiDArRǃ''qgKˆ_9O^Rc2 7lC.zvF3`7v`v<-HKWD3ud+*qπZP2R RP0ETx8'k=g9iځN23P07!BzN %"DW@K>Ir CCA Y:iZ&_B~gZ}:Ll JZ`@#iz]#cz̫ 5mE.&+A;ZjTTpЋn%kj֜1cD+ᅠz*͇7-ge,K1a}IZf*)v6zjݻ6ŀFsmذ]1߭A[r=+P/Y}lN{o.*۵..[om:5vQHܵ*'M9QmIMDG!<:z#KIzjwoe]y FzjN, _Bf' ;[S3:OG DJc<3o|wix)Nwy/V aˮ,tH\/P>3w?ե$첡\VdfZ\=YqW]Ro-!PvUZYAx6o+bQp$U䟕C?ʼT 5/ z\_ܗ=^yV.oi~={+lΩ?0؂J{x_o>:Վ cZuc(8Op]1ӪMsBgz%dgmjSnF~4^.'2:$mXMvVV2BOO+8v&o:< _giNddwgqYOѣx L}m [?z[7fxVe[8Qh"iֵn9v.a7w\J14 Nc2w`˭CPjNP )~9{GAped@V-YɏK薾"V7խkJHHI)B ŕ}hָNtoe}Ilߕ־v 5jimekpQ C#K8(?ᵍBzRqRcqL&L%;a DnIuϨz_oζc| G佬Z0d/,]дHPٛs Q3]q:$;Lzj2]z!4IYde{FNۢ5AW4 (!ӎ_Yv7n$qoY&8h478/1F|2}/;l>ַIg1uS*zMoD|?`גٱl"o]y;w9|ԕ񓇰9~t *l1ڟɲ^6!`5/'c$#b:уFл捫Je6|W#s 06^y{_ {Kb&<~.rYzRmL}g1?77 p#qT_OMz_/Zp^B{ >]yF?7?⟼8 lU̇F?mC\T:܊WC撁C1jEDrغa5;LmPUU ₥DR>μwz^2llBze*:vY 'KUC 42akf%r"4XE6HvӓILϡAH;8~D`915`/O|ٙ-ֻZ\&tnkѐuryT aM w2kޯgx-Nj̶EF=l$̢@TDT׻t$%fS͛_q)cje$Dm搕Α{7p8Fǟnn"ZDHXHPA\0.1 iT|hW[ &i_j#iզik$:~޿UF߱F/ 6݊ ռ_ZNϳ׷Miܴ)ͯwLAMi:PLOuGK>Z`4|޶c0% .HR !=_;(TrmÈޗ^dp.ǠB>t2VzxZ(흐?!9/~BÛ`'+3Mi, B|3{ɩYr7)v3m=̝E%t Zv=MNzh!V^NFσCd0F t]';=qXpGzJMa7,!B󔞽߾#N7Koy'nN@e)]v?pg UowG0O.%7~;{q Ѹ:N^aNݩkp[PUUQ 6#84!!PUvfh:F'rhL=ÏoD3wA+'67ߏGoE_HXtK|, ш٧ί)|,FDTYgHM:)!BZmN5՟rcuPh&\oF0ܴ%gimT3P^/Q\H$ B!D{F2}1_^$ZX%Z Pd |>RUf  (!BQ-wts4 Ɂ=/N 4 :v`0+#GB!BJZI?Y|s!B!^I tt]'+# kQ򪃸`I@A!B!J(1q-ٿ{;vʁ=;i??yA\B!BPPUpB#ٵm#v<޾аʂ CA\$ B!%J ~~DD+9Q ٺ~%~AD狪L,ʃB!B(WU`EbX!04/֢Br3IOIZTHtTLPI@A!B!)h [ai8z0XJXT3z&HqB!BTRTP?,) .ft:0 f,_|}- L: (!B! _PU_W| & (!B!]1 eWPB!BT*HAUI@A!B!jABS!B!#!B!BԚB!BQkPB!B!DI@A!B!&_yB!!w9B4>ˆRU:L B!D#Qڠ/߰FWi#\ϧ9BG&!u]=MvF:Eh\ojGhD# u^I@A!8Ji&YsC12-Dct88}*)*'"2hAsZI!BCeO sNs:6 lsyB( !tʁ=۰X . R!*c&!uLc[pf!'CP$( Bc&B!!]4a/yJu|iZ=Bm&!)?º9M!DEY!n֥BTǛ:L^yB! ];[ @9Br$ B4۽&u!:h !,Wa}g(L>4IC>ݚfAfRiՌO3WPf ›df{8q$aT|i"6Y n5:Ƕm؛OumYc魏ӄn6LgRTpA X}ԛ✸_B!ۮ/84~!Ms 1Qj)*FUESU.Oײy'=o9(^ c??ǤYq`r7_y=MhVֿ?ϭ7%˯EC}:*8#>-!Sw+\Acփ+?:au73aPK*m~j5a} U-7U:r'o0>2wb {{z2yq(-{}9_/FbV(WT Q] W7OS1fZg!c4C,_J#s hDUj=*z-&CQQFz7B+ 1S#$&b[iB B!Rߖ7P92tuzI·7)͗?ŒO?JqXQP(bUv ƉVYA)TVW؟gNkY)I8?s\y#OfT2%ur>ۻu铺Tl+jɹTE.Wi(Q9sdʥ0Nޱml?YԒ][iQQ!mܒ;y36f: zv4 E-!.6lM?sox'!Bg~oJeez`PugXAc-պWLM9II&}=Ƹ ;i$  z*fF\wXt*[17Ӳ>z{&ѾڈXj8s _m-%,:<qKnݺߍt ^xMQPt+gƻ_,x,>. Sp9nNwY7*O]i[@Nd !.;=ڄZN~J'2wv]/"~ U_K@b.9-8};s<-"ŇtڑUoCM۸` z){P y!MӳG;"<mY޳, l*>A4o׉؀(uj8}s.>I\زd%)1֬?~uWn˖6֢7>\z˳\|IGy=6KowgVx\˩.U"믣gecl''c^w{M뫐i<{#` [QVqgZsgbi5f8xvETw41ѷ?aw37ՙ3G0b|\v}=ٗ ͪ]-PyLE7ѢeU:iyܵ2(lم^]_SEڪ IDAT^jΓxVƁ{HH=ECHT+toO!gyX%.oZNR}ij -Ądru"Z -{p$%|%(дWf󩓟z{Xwr{O05#ҙDiqTؾc(4+noքd4 !1袶nBԎŪsٰK 02Pr{t>6U3_?m'Vv-NF;?y40Ϳ7L댶y& hKb/;*P{Ou9q.sϿR|lkxE>ۖ,9Mfo/PbO!ǵ< h4?qITi>.]̎[UC$o VeC0uqfzM[w;1߷/Ԛ@&wU 5-i b4Pې]N\uTy~ܜ0t4ZqT P.Ɖfǝr) ƍxC[1t_ǣw2\h]%s_طbW`o7ޛugIy07!4v4=٫ ]}m:*۾׾Zñ (~͸yo v%lN׷p.z*M@3g+BLz޹UV;[q3Ժ$Lot^d뛙#,u)<׶&Nv~(,(mk䛧T{{O]N{/X|#]]_rS(:/j{/WjoD/<TA B!jh<w\ƕwI9bMHy97C\SCdΌ7~BT~y;~ੇ} 4f=aϞ|;=UZ?s Yk|tU0};{kr#](>BQ)7p j˹_4怈ԙ1cd_{OzT8gm2A^`x{i_^^ccS:N@8 %l1't aMDz?M玞֙Oa \ջ%z ӯçHwƛ۸I=͊7E殛#r8u!}1?̃&>z3׷re\)?f˙pj;<6}Y\1w#An>V߷Ϛy[PzֹW]Y|i{|:Z=1yx)װ6u-cT ג^y|eа>4ߦo`uki}ft"6;Ī8wPŧ?Z&+m@d̈nYk\WЫ?%x0ʹ[?xH4NFuPy) o6#NDܾ ݳuвkcdg!!SGTrut]Gwe+kYLV|AJxvi˖j7EѭjluNurVȏm1cÝ85Ek]ܝ&vNY[t~͟`5n>hN 8tEsIA Z9tFTx&ϜGxgr*|'9UZg&qX vv'y+kT׵)5j.>/B1+C ,2Qˮd0y^ۼ\AP1jٹYAa=lA޳Gwzeǔm뷐+͐+74p7g֎%R6k8ɀvcN㸶BݿEk2]kGUV6/[Y|}u7W}e,Wˌ}\1T:o=Mtt +)l3ru5zӻ|N;Q9m[U3Q.֤D3Q8KFg$[HNsGF5:wBxh[;[`0m߆A 緽^g&:'ʵQŰ)<4&}3Iah4҉0WW95NQq|0JYŧ]ۻxgHa-}OR sh _nxnŎ_FA))^ 8l){ B-=P|Zx9i$`5 1GI[)i$NV=?mH^'ΒaMjd,rU&\tI]>zt҅ɓ&Uكɓ&ѣ{Z./-#NJ1мm UvM<+qMv;ػ? U=t-.Qu;v.׵T(uiOP#R|};tVPtKoVNT9qX)/8sSyf=ŧ]Zzӟ@?b߉ B!_Θqy maRN2I#!a mxg&%뉈p57J&ZV&:-XpyuT^=sy7q£L43c4Ѝ^={ӯ_u:j;v?.]3{D-݋q[hcYNSFT]-ոs5uCmTLfA}LcAM{G޸g)uyYk?mu0i5u.]R/~%])/UH/(@?,cSРX>9Ȝ_3dtZui':;n`M@qC;n[8ݎ$yh u<4$v*a)U>uy۽#?R C%-;SVzRw8<[3';#v]գ'}/%MБ>ʫMY|P|IUqf, BoGCyyvt4Vc> /}ITc+g4.{IVz ˟pP5_S;s7~Yۏ{ 0]0ߥt3>;1 ,9a3M,FC%0#gs9.-~W~5ݫ< A-p3t259a(VKQQbFrdi=pYNbYd 4h3~n\_Û_λ6ck3 r]w^z@un/{UJSMv;%]cw9&jZײr`/9$MFG7GzԎo|eKY0{1w:~mqS6VOUz쩧Q}0 (!ŲV)F(JͬU9:6bSTXF@üsX<Zquc-,BoB:'N@fs36a5;9A%P#F᫙Xx':vd˒U џ*w5uOtZUZ:ub{}$˩8oj{5>j\ßyټΰ 9=UܝV'#/WV|uIk]z`N_ɕh6lE6+JkƶL|*? xCrKӜ:uh]O[xNG .q|ńOWlV:UrB|]{vX0m{L6JQ$u=oEEVt\B5],'9N݁f7ؐ qYO,XCy_1WE_^z+;U&w[IΝڹ}cW?v*WCU'8]l3$IzJn; 46 Qr(ȎPeF4r8]ߢPGӉCwQ&98UZ_mv:X ):qSӯ^c\K#zV,O,.z//?ͺ峺y})PI@A!D vt -re:h;|8{JsP-)7sʝMea ~lY.h]wmZc" }lQ֑ڎ_5mE.&+A; u[emA_dCD^1^&5d|7g[1m͎.)B>-^9F]֗`UǶWݙW~%.䣟Ъm:w]MoټcBlgI.;uY)~]- s8F/]+μn`lڂfNob eHY;F3T,-ko亮!6DW4oٴi*M5ԛ@1ݶ5O~\hd !97fj(=3ăb5A2c V)}]Ós{[X?f v}?p.FW"TeCj _}Q܇)D]ͣ2~E5b #m;_|9BZռQ˱&t\]ҕQQβ}=S5( B@Xuߐ~HEUGtzg_Ò-=ԃR oϠ7p-hg)V}4z՘Vwb_rg ;7P:b?+_Äqi[X*noaw"ݳmk uۿ\«hƬG<6)啩q1'V}ҹ4IyYm>o-6Uw?速YYf׾̈y󣹬]n#s| O?IڋoΣ}U(1Uˋl)+L2v.JE㪻6y |lgu.ԛ^ؙ c 9~t=sJuЫ$4[]Q"xUG)*ry-d.!`˺Ut\o rSN'fc1vq<%B q$A^ NaS-5#6@]'CS7(fWuMp:$EjN#@[^>JwK f(M~YDAͺ/Q{>y;qen@4'ÁnqYZN:|&/–O~'#g״|]5' Y}Q5b2`29HrV>vBPx "Q6V[ =0cp*j2c6QINL%3fB#c 5mmv7y8@߂At;Eyut^m>6]0 rοyFsJY)+8blT%fR?2;(:1cVuXuLg%z;LjTǓOQ%.nj|s3O>{ IDATDz.6՗q4 6Z9\$ _3̓Kn &E,"9c<.ق $i(5WbsSx_^UUzӫ|jdj>nYdCaW苿EQ[bi#>T GA.]v'uX{ߥ4 !5ހWzD*Rچkhn-RZ|](c8|`yP8OE-.Kttx=nRs|*L}t&iKjq{Oҝ*ZFnZuu'uX Bq^()r9Eq!jEМ^mkLoySx[jsU^^Sw7A BF|!8_I#+ (!g9B:BhH^q6I#Z!B!4 (!Pc^QXKѸxZW+}Qlزeٸb1!@ps&BK# 7$!$@hp !؎ ]ظK.dխ3$[%7/lsf̞g=sΈ ql[~{M ;GDZwADDD@0D!" &w5:D9"RWÔP'M -"-3ʊr\w`<\D:u]*I%}H[H?, ۶ %LIdB*+pDWMD:qTUVPt!TB)ض= M#"u;Lk( <>?KYvwDCJJ*E#HMɄ&&SBADD5xH"%-c <͇۶mx<,I}HSBADD5muuQ/ioɄ&.Au/2p5Ձc[#"=s"?[T9"݋{%DDD ]ԋH_wt)"""""""t """""""iZCADD """"}* ""{ԙTDDDDd)"""""""i """"""]f*H7t籱J(H4%'Xػ4%1; """"""a1TQYH8:]-DJjй """"""!MɄ-eVWQP8l<^u-FN"Am_G$\GNnbM*SvP*F?c x bte ,O "%- J(Hcܶ!E$q3bYCغԴPc#EDDDDD]\%' )1 % n?[%DDDDDDd?uL75(eY8͒ I*hʃtw ueĉ """""") )""""""Z3aZ#DDDDDD\SʫOӑ(EYz Kb,-g "Vad=%\p_ȣO]ғ }l\歛c$q|^$rs+5ODDdo}\'>Odqr㡾=n}[}58{~5d0/^z?MP^8!%9F&ɟ!RWWem)吉x<]]iW?wi-qA@ 6Dzi`c[~K>NpD#136P^zj3s9b۷Ey<SH zݗ^%ꊈH{?nZ|YDWbwJ(HAGzdHiSxl\R{0%k"""1e@( gު2jtXs.MedaDk/dJ'rȘ\mάp oYɢek(' [Ą08{z_Çh\2ԔRRVKpJc8ᄱdDw) "".o!)bpJ17 0@xG X~WUDDD:-~q#8}N|\=u&f>Ő-~&ۆ37ŵ ;6M_#vfbs_!o|CƸsWr̠]j1{?S?@ۼ^v/MY2 6I=/O^ƞ|lT]Wrϰ`cǐ=~%DDZ'y_XI`9` GJ0慠Hp_xNAfP6scpKH6O_>2m*W}ċϼƂ/sǝDiʃtsgX!΂EX Cͷ1mӦrϰ`qcp֭[SgkֶwϠKI8 胘d-83)HD'$8xFV+7F\ȽO?Ɖqģ@2ׯczw9`ns]º3f,[˷og"""Wrׯ'|yXK~|R7N9oAނN9g%c.'8Cb͚^4#hqɤ, J4{e{r&Qqپ~#Z gʼnLjF?IL;}>~zSMeeS[^vHXdBXbKeOLf3nm5. ;p3{,[Sʶ(sl,ף7) ""}r*r c4:ADDdd6gvnlmY;K\cذa'N W\NO'$Gv쮼6"xJgoS5bΎFy<|˝2I9| Gz6}ߦ/'jX^_tP>d NU+ش:/YQcz"""A]o]aP{1'7~ź2d|:1'Bm!D.=X`T&0tm5۩6LJhg[䞎Ehi!`H  {WF No|O㢌 ?K3 >e=v}VA ""}&5%OgF]]5b1JV~a٫981.۳[j[`p1H쾳! ˵bZ٢_.\J vp{1z3["u*b!u|:w .^RNȌF#ĢQbK@7( ""}fڵt$''c~aY&"""a:0DU}.u]zAJKK)(( ?/6p. 1~j'?ce㛯~i}-8xzqޑcjk$3wId2F.ŦhT1 `uZ5,Ƹztn]$%DD},[?=xӦMcylX1GUN|ojj1 ᧞ux^[[5jɡ ˓X!5=l_p)eٸuuЩ\1A>4ع_ FbwG`*y,7/ ,p? vy6`ł+jnlg{@=t75DDό7E#uױr՗0r]5.&t޹l>,uP7(y0Dַv>g.SULrGfd~In=[ח*;h ?ݏ9:iֺ{:VO{g]ӿz(Y6U_fl2^y%aH;OēK>qLN< ض5̛50~x>/> Cr1HiQS{ ""ҧB>DWCDDD+ #[)^Fyi~Jx ń|Ăx Yܛo.*j\sy$Nթŷlu&){4'Mߧ0*!洨xk:zӟV!u!O=u|uvdBnbygoؾ􀹟|&w5DDDd/gLBjbFץ}~6s.+ b1uJaԯۀE,o{? ' UePĈGc% c06$꫉4^ν-C./ ԕf6"T#%kƛėdMԸId.0 b “ϩf{44/DJnpmdg=ۭ|Yc'xmglr """"""}"Q7x6Wra$R$WNd[7LD8\睺 q,2c\bu{.SWDka &kFaq׭%i$u$E#(`(;8ZCЛ RטHSZN.%DDDDDDS3>\O"쳩:ߧnHg1Kh%ZʸNՙq:mkLJ(HuwcvnYAa8Nx,mcL߯8@( """"""}u]$i RBADDDDDD}+ߤ+_/ {CbJQް3\g@0Dѿ88H2 #LT%DDDDDDd[EZfn_7%DDDDDD˔H鱑"""""""i92/LIes!"""tBG#$1ˋtIu}O""""ҷ:P0`Da\魺DJj œ/"DDDDu8)RFmuHպq ot:":rr )d$""""CwRS[]c!A,u{EJjc&dRt>@Ɨ R<~IDDDDOF(TnF"y˾eY)b2SztIO""""?:Hc   1`(hu{+d$""""݄BU]莒g,iN'H$'__u{"WvxOQADDDd鄂.EzIDDDDDV^CADz&O"  """"{.<^:L8cPxR=:""""2utoĶPYoA9z Ix;%>B}J&hoXòkٰe;uq/DZNG@džtPlCocُ x2,P.# 3E ŝHb r)o<;=n`*\r :_x6׋mzr6ns(%S˷ˣ2?N"wi\q12#c{cX` 1oG` vډjjۭhoj;Oy0,}_>϶9+@IL9d3ؑj6^̧/t4Z>-{_!n3%|x'q;wXϹ\7\7,, !9ԣ8E+>?3>䋅۟_="1m]gz>6GJOxrNZ-mCq'""""ғ)Ά7֧/2$rM"?p=?j?0/w?.4쪺/6b).&ia2MiԖQRZe1|xPKo['% UyHJqN`ä˙<93jb-7Cʡ\{,!6㐷[!QEJXD8Yu oYɢek(' [Ą08^UʙRJj!$6/9+ ƒ aW\6=!tzJ %OǴL_q>Dg žTrOt-v>w-ta ޻Ekm|> ^oC]!qI'se{8cgppljnF!39{Oy ^~u>h2Ij?y /-óygSw}G4$"b;#>LX8&nwMYMUiZaN[=)?`6IśMb}<"v<-6|jnX v|-ZZsԏ6[na 9񲛸фZt:R.p*+jgXZoecRd=OVu,cN_;HdeD;^N;6mdƌy|gai:˶w>[";̲Iu7q5'yv-ؕxtw,ŝusfꨯ'Űm{nAcEBeOY=8~D>e(u-?ai|2|`5y{ {r rmf̟gBNL\\bF*{=AD[{;܊I9W7MJլ7`Ku>2ڻ]؜:|Gq%gr,rJ>|'_Z[܁w#vL;],g%H +O괃9dƒՓJDFJHєw|t fs3Ã`Obϳ|xX;_fe)\#jGk,(y_jCt#u;;ŝ7oIDATB("8pzm^q*뒒B0jQ(Wn?z"c,ӱz~DTVf6IyVۄ# `l?3rL֐Oᬂ;N++yȫSsI'rW&1i0ڿ.|GS%wp]EC2:K2g>FDb/ͯۿW0L|8EKͼ,dwnx#A1XNCxƜ6E3NsBu5|t:b3N/97+į jcɤz \֤9nB2߽o,y??[4sr[t<*w""""ԔRRR, ۶b|>{%? ,#~, ޵^v+,2[ * +LYY۰ܢy3g8s ׉w_?塟i,X[/P0rtO I,c;SZ;_v'٣_y*.=O`{>* Ěαwn"~]]-g,ED;|-+gȁeRxٲO ;.n}> F1M5+33̟6kϼ-#83(p"Xb39{ܷ>/!>@]nK^}-DDDDg m B! `p/)o6y\m!'֚ $, O/=#?o3)R38Jλ&T5nfEI.6C'&i 0v|6e+WS[ C3~`vʼnLjF?I|xW>ڝɲ7P_!g'YCΥv˻:o`~|cOԢ]_?t @|L/ e({uLFeaR~U^iIqHq'"""" )mc6ǃu_u&e(crZBPao}]S>L7ĺ54{!nǬ|H %{$n{GeLIL9x Z)(vd .es +=_ǔfܚjj\vz-6]kFZ+q'""""=b@-4NaH*ضM9Ƕw=WƢM&y_r2:<>a'v NT; sH zX>oӗrM,o5>*ٴn7YC&1{r?-!{ؙ:_`gs?sϙ2pY\|©apx&~0NNom=Mŝ6mO(۸ 5`(۶UU179b*;ÉMeeU, ֛pq}Ls>::K.&lS<<3͆yB[cg0& 8P&^ٺ 6m%Lj(Dq ҹq g$5?X2]pw>x{ykX/V^A1.rG_Օ#(y)(:,O->"vC-2 !YHi=j6Tt-l/Wlx-xvR; %jZ;"C$0U $5.ģNq'""""=i@lj>:xZPj&Qm;l gt2do)'|P?sI=^z3j5˲,,8N+Wnv6qŨXVD>)0CdH [ hCG;ؔt4e[qA:>/\x)'d<öԵl89K  "V'a<·81;nu_s%ϫmOt#o׸ypy;^X`*+J,R1.plIƒΊ螷7ۗh!y8!xp4g>v; a,GҞōP_gb_u{LkͶI]'ϐe|cmv*RںYwP~)C=cO"Ҧ91Tz O: ~H|ibXƼ%ldɲ\lFf[xTs܉> ~qM?QGmHfl'=V66\t&/c371A;~Fim |?z#ͮ$sϮ([9.4+} lWǃMٱso}~|Kmc?<άs_jSƁėHR6ʪWef;=!\=;<HbRͷMlGᥚA`"tZWz6|]\!\_Kәxg8JDxxd KAeiR[Îśx8(e߲]@9rqLN< ض5̛50~x>/> Cr1\.uwN8DRUlmӎw߼ ;*fdeqJ1IWu+8ReSl͘&eWrPv>umS܉Hp ^@ eժU-?wߦd[# ZqH$?NijɬEX3V5'PN;k'0!|?Ek5|,ȋzx] Lh̴Qv3>{&5I˞nj$\NgƝ Ͻ̴nJW4?y61կsɷNc|(FDs{Oyo73tU\41H"l"zّ3o7[~oҙϱt&Xvw?##]|0Z@'ӑ8ƴK@Dq7~޵ B*&'2֌wJk~iFm!tδrsIDcq\z7gWl1Ʉηj[j<*w""""ң~o<{60.jbFkګQQQcŊ$2pScvJtX<}q{}I}>,->~_5 +#ulH &۶R4L;J$j]u5DoB}F Fpv]_ k!9N$֡tK궖RVQC}JJ%+~L"F<kuѺdSy=dtN4B,bU;oPkbM KTv&j$2QHkIJT=DFx]AC~,#\/K1r{4o[X=4:lPI"C _($/x=xd|681˗Lr Nzo eY7EFVb"t-Q x7Gh! W}AAA!ԛ1aZiǗ̡t4%   '=(UѮSpt{ h}cy   }TBvfc{"   /c4ky,mW{Ո< ~šKeƭ.N S”f8/Ie(0FC Reسy'SO"6rdLL7&viܹH=Įo)kBtDAAPC[u[y=otF\tRG-K5Her؁;qT;R_F:_'v"9GZSic9X.Ke`Y%GQItuԘղ)̪Zgaҍ@yƧCAAA$ozcH|{X5h,-biÝ-Hx蒾8FKIe(/ޣPvqTO(F~Ε_015&b}OO٠jtgC7Gv` Ch-,Ealk]rIHȮ   Zמ38ܵʍ۴, SMij9BW? 0ugeU ߑ{PӍ"dz#߁n_B,Gzf\../`|Px6n>PnI _+yuK#VaSؼִBv5+  sHm-Se r!iٗ0n]fݫI<9뉚2MCvOAx2h>ARKw݇=׿ob t_ A} FRqF1%.TI5Ʀ>DLJ{#a{2#C5XMҸn8FvڣvlqQY:hǂh[1jϳBъ/YNEokfY]e(w:ibY;JkITd87cZ:ս /TR-SyKyI39}j*,/cn1yGc}HA~34}+JV7׏Hk)ec8B>"7DTG5qa5&䱜Ĩ:F3x VL*N4IXX=7Ų~t>MQ*ߤpE5ۋ+C$,^qĩ-/`,f Sb\ZC &E҆񗽵l=Q}Tx+N SY- 9/Jm}# IB%~1×TNsM|ƻy3f( piVHQViuLW$ "1~ʀv|fE>w&3hAah 97(0^2!픮P͸N|g-ٌ' T_n8o%J, OdJh~e"<(JeQdej&ŤTx6 0 a}q0tiD1|M׵n> uiPVL4"ciRR6N0 {PhC:AIK",gTΙ֕! |W!20( 9p;>HX` pܑ3|*b]&] ,枹?"A*о֡3bsaZְD2g++S^_Yr[C'PQX[2!$%aY) i٤#5Ֆ~7sy}t(+YU=E WvimP[=9|i;n^Ӓ ԜB1r{64gh''(X^fɗ5CM:aw 8/X@YVL2^e1uktcZKBvA8hlk5X}mԹ>􋽤t~!)F-&ΙϘڙ7!Mn.ϞD>/l@"AI6&6}yz bv4?A$52//Hvٕ13Dx 栲Y`&'q#<.S+n)ɑA>v$dW㎁}]4sX $>d[/b7sUu Nnwz4LKxv&[y.I+HS|/S,п'?{p'ҫm'rYKlZԄ|}/ ˞*ˀ-|_9x]Q^O10 w^ᅅ[[>گVHǷxhnn;E^Z48`Cg[Z$mÀH ƥ)n0|Xzv1qQ<гf$9,=Ů~ӒCIR zCt{t{ {Fn_y qTaF֋|^21|U`,=ynIdPOdXYrw$l;0]p$*%dW&~s렜V1·][yI1L$Fx­]q?>y1{|Yu5gk-og٧ɡ,<(y,v 7ݵ5|V\>vn`qe`RUeLb޵ABŗ8 <;,[4[YXr[YvؾZWI .\xH^!vUI-i3>A}2CK]d7O{VƄMҜ4A1Д"00jxd=J"4N\1p?۶=',eU`iƸ9f5X`C>p˒tliŽ.9m{.meϭI(ϼqTcIX,T?C'1kb-YT8ז3h'.`]..#! w8&J4r%qgٸvlWjΠI&˯vU1}||my~6<_3:/JlZ=9'ܵǪ۷򏋗2YY;a~W1_v$@·l(F{r.Vq R4'i|fE S&Fs7CϤۏ;2pP-$'H+;Ҡ$RӠFiWBvA8.)][Gx.]HZR Μ3gq׮=e6~Z>g\.ֳj Ym RlNZ>Ic4pMajS&;`42MgVr~ .)i:| oǽ;N']j'&BPH &a<Ac- z;1.jjPb,}~xvlҎʹ:t}z"\3@o[`ȮhtG˲ZcQV^/٨W*PbW-!nKn(/?K{HK9"w1{npY]fhɐQh>PAMEQ:4$YvA(4ߞ;7WLĖv4B)SlT.;4;z/i;[+2 4͚LJ_6٢r i,cʱt1P\wڶH [oD^IY-ok '/I$vlac=4vI &"!IִM( S"$gt2Mk49muPj;A+"fὬ9AUbVĒ 'Wo MY &`-|}S)8sTZ0LĶTI|%'KOnt?[-f5FBְwXqcm2 L@)&h(i3svOldrCc[8gh_-㱫3̓D ^SXmQB( #=)!Q$)$XiJe~$Tyhv(zH ycE,ObTI2z-Rzˈ=s>aOEwD܀Q4'5S36SLkHP,˦.MsC #ifZCh34; iKc6ː-X/¨cu,'zN+ڎ'؄BrR7d̒۩ A|yˡ/UY7p2NHZ4ׅ=pr D2+8x&\,!+J&ֵ(f? (-7$SRLjKbI%: ,kh6I)uNН-kErNC4;ۃo9Qf5ZIiѢŌ'0/D8BE}Aьfu#z`$- 3*Sfsp+P CAXu;{ο 2W+¹R!oa LQb/?)` uYOW'h@S+WpJ\i+F< q0_ ) RPϤ:)E]&5n~pmZmUP!>-^RrDo<6XޮiDi2k;.aZ"jʄ5pe))y`4&ml|x@_t1i3 > (FE}?~yTcD[ΪtoMft\h|pnno:tDw=L ܩS6o ?8K8o$rb2*A?e|.͉d{~: !"Ӗ P1#EvHUW LiH0Ω!C"%< Ev73ڏoaֺXrVed] FŒg7&hH\;_J`TJTp(QCXZqĠ;@*1iQ(2$Γ d݀]Y4i+xƤE1}sNzZ({HB41eռIo T"wv)"VKYyR&&}+ p-&JSmq(!b{6իW>ѼV4tg]{lvtw}۲\gR!&mG ݡ}(?7t CT!OjiR e'cB[F7O%WÙ.kuD/ 4?o^wUlyId)2I()g~F; FɦQ-Bj1{JsBi^ yߺ`:ˆ ?[ B*xBnR/hR!f梩|~y*c xyRQ&O!lUUusd(;RVY/@Rr&{%[WJR:?da h~x9Og֣͇^*Ϣ Q*U_?[1+^<`aB"I{ܙC?\+DMq`i<ZmrGi5rIRtůuw;+Δ͔GYK/:i&"1}vQ]߉륳h,:sd0Ha"log__CY@8uemTòjB. ~ϖhHe賢xwo9CWɊ~!WSV P]R:QBK[C*z)Е͗0vTP lֺ0O!0΅l}v{I,/B ЃBb*{0LcJR8CaȴBGkݷWa' RUHir^{I>jZ+fQ9 W˙Kbn3Kc?/! w]k~>QI,[Ylĺ:ɏ~k7^5ӻL,^s7@gA\Ok2Zxl/\}dS{OO i5p_U@@!CsBφ{wbv[ٌxUo<; p\PEKA%PU>|9F{4?.-_S+B'ko_e܉ %[۷pMOsTɌ)p/~[g[S&@n{ӯ0Gp_OS̘D{Y% xx:(;(蘷((Am.%3X]%㜝TSU&*˙ qVZ[v>Jx>.8uV!j]vXX>nR1A}-굒(WR]ʯf|R6+Ľϣ/Z^E58AO"Mi%'c魇a)L#,tSLEP\ˠ4ڎy*O[Ԅ`6$A/l] Q uw=- O:A^(њqH(yg^ N1{Z+Zن\6Ύ.hcNc[?9SꙒtdyG[ ;^|/vŚeq\LCT)\:DCv`z[fĞje z0yB*jָ{Bmڌ;<86;wus8ڌa[f%9%pS!ʎ+N b#9¦*vBx Q%.AaPb$c+ Hb3VtlUΧ Rۣ ?)1UEY?[ع4, Ѹ-cU)+L8iWtV_qagu"A)t\/W w"Y !+Wv0QxdAA6f,^FBtrf[ ԭ&fcd7.pb6D+T1ьmąeQ0=^J!ZUl>^/yp *?.8ReM++ %oWGp>kgy)`&/ vຢa薦DFedhT4Xvo'0Ӗ;r#+ ^vrktobve]AAAO70^<I=r1CVc±Rhne *NX6-ֽߵDH_lbxQ:lRhtP_HF[n;ϸOAAgF*YnL&RtTZZ6'~ %T+ Uu5VO|~0_Y ٩>CE]qZnv{Z_՞S~e Sjq雞GHȮ    ώgjD~C90GBgF^"QW}S1j    { dAAA8xAR AAA8xAR AAA8xAR AAA8xAR AAA8xAR AAA8xAR AAA8xAR AAA8p,?xQ֣AAA>-P6(hwFAAA8>A*   D   G   QA   pTA*   D   Gg,7~  xX/Յu#as|}?`%Rmo|޹VleWe3|~.v hk*x?Mex9+GwP42irsy7@wKdgЎ"Cg k2<{. ص-k75 ͰVSGκ,evջ|˯anW|ӷ~# hrgo?GHw??wkvkL\#}gnC9e'09̚/> >ªu_⬸2ɮ'Q 4=#^ymˎ[j|*}6=OF1<A8FwIt4ok6{\~F]wfݡukv:/nX`mё5DVN\`J}qG"v-_r (E9z3xAwGbTyEx<h4}OOW龇X$F-V^{pw[ro-\X&5,ˢz ?qvUG}|K/go Nnxx%oo8Z֫U|x3l&-TIQ<{"'"orڻ"3y|qz1fXx ̉۸+g@~.3旄aD@/}Zty?mےB,WBLn?[& N3qlڣo/[sy/UarR)⩵شI4q6N^̆#=2}7f%Ec4.[΢Ia 8zc$ϟc/n$RNyu|-K3{ӽk;;Xژ3!S)йs'_Eh("ϘfcEr SLT.,g͖u!@y,[:I!߹5붲yXŒwrxɫtNa^6l ?of];Zk un{_w_׿yU&5R O^0wZ\c3?o̳_ٳ*? ~0{6>۟v[[!1GrN]pͮ1i~zֲ!KY۸7k6a]}h3G/ UW.% o_j$ĝd r_S0YŐ%b OcTzg_-2Gh3|n';y}zVyqZ5.L?#|=Hib;o^R[^(~Wpҧ|W7=p{Ƚ9,xۍTYXz?w \ŖN xʖpNkG?AٽTXiˌ ߁>$?{f@!{ˇ?o< G0Sӷ_{-yc+/Ck<ߋ9kJi!ۊҟQwẺRMnq`/X('͋bS9/7eg{#&xKu|f1a]t(;أ:m*/ ׳9U"N;~9a*]GZAրyo YL0Jݏ~o=i`ܙb~5t;7psJ?FGw;?ηQa%i7o#_NDsyg:eլՍlbe,]šʋV^Tk Im{ٺ18sfy̙iv4{$|%(GC}DLT:]o]v t|_=tt#|7UlUL6sXwr;8o%d%NX6ze$O~hwGbYNhœj~vi,3w =uM^y4ymXCi?4^)n!fnN;mhAN[kw/0qΉә8zu{fKG/^f O Npw֐md_^0s'ԚlzSt'px+o= Mv?Ag3$Ăp c3p5MpBL \[3Xl1'>ՌZdeӰر~s)5dNͼ[ͯz_I 4+̗~T >|H_F˳W_z#]<#s/P(*3+΍׿E})l5߿5e#7FՏ?2pڻ[~ IDAT;`Vd꿯㳿XOt=\X:?+[=뺫hc~#g^Gw{|H*I#_+XX}[~&t0h4stKCiA(hMPu],K@6Z^ )> -cnY,~?̳5ogWso~3ݵ<ʭ7' "u1-Knr'/7x=xݥqWES?~Go6o<ܧs myχ;CkT9/7}~SmpxgRL:l:cnv`ZOL䞯~l-OIn5k?ϡy;.vcT}o:^=G5y8.eݴR9# Xu^}yRg{O=> .xOqO~0oBYj ɱwk$by/qg('/0ֿPIcOꆌ\GUzSTR1d0*Ե A#QXV,3H I2$!KihHJ^~c%+NN*G攮X<$sޘ\H21H[ܒq2V%sbt˙<~s(]oys_ps;o>wDN%o9o©eciL. L mܟY~>0 15t PssY$*rpF?R'&=afDکXƿ|AxvaR2{LA9Dӣ~߭8v@?̑0A¥C'u%GP4p"{/Q%ײA+*sS5K f7K^磿Ju0o#KA#g&R5en,w) Zxj;;H#wW\0{n3u̟;J[aⱯOau[gSu¹H>>{!OC׈j:l gnjƛ]mX6ɑASou,U_{4$bu*GpZ GHTtuꀫ4}e'98|!==;7 ET-J:)ie7EMFII$uu6x2_πEِڼ7/IL}4l cT~I|ˏ֐hxhT40~x˾Gظ,غ>#[^ُꙇier4IlrY"TÊ2IDZiFR$E Xt[r6#+V EU*y9&="{2P^o ۿ+8{Uo8wuY{/̼/ X@ \=n$AU}]:8 MIxlYe+»рsKM4Vo5k!Vsn#)w/@ZEOaI`W['Ү&]4HxJ:OPi>`c}\+{,@GԨQD/'721\qmJ2xdwj('9`2 4+fӾ!Fs(#z @%.J*\ps7 pCQ  p5\2`jliCgLD ń1|{|V@+9TA-gW.jO %eީ!"aQ\l sqIyzY WsZN?/+/Ammj}48 ݸi= <<'N#6 FxXWK7)ɡ}p25x*AqEōiE5e&_ɪ ^Upt"Ze|xߋ? ԫi-BCO]qcoМ +nph3 bجȊHDAOǁΟ# __t{αo>,㠜adٝc3jhY&r3?*fU>IŽEKSdFʵemSЃܛ) -ICWŜUl8r,91Qeѝ"ѯ;U,' C2 9GzO*9Y9VZ>,U=_+?L<KDDEnn)iDDN^~.NN=vNѝ]qc6tV.a7!ЩS]79:t_odgcI~v+/d0lYV]ĭfjӅ%\/]N>!T_$.T]}!.U)5sIڑ#ۚ[Nh%#S 5sev|}/geձsCL/ZâUL\ѼKϠ~uTE+iAr fWAt6b4cד^L;N$5]G6mfB]2r ]pi̩I)ԉAAtJQ~zu؝mv tjv}YhCS|>tt=l챐R(W5shN!A>l@%Is tALx9uUӱ񔭮"B֡I$&Ng`k2ͩd5KO. 4gHVrSNרF5Y+gc$䐝k&xTVPIMNeOF:aMi0*D3W[ Բ/m<)N"CR ٵg}Ѯ]$))h:χ[Ѿ]vޠ`قdBQd`@]Pшfcou\*K>2tvi\T.Ëq3o$Ppv7)?lk1KGLfTzkfJJ%˰Vc^ ~c'cc<Ɉq*۟n.Y`؇h63%%%5\ h8jf{ `cвu@k^~!-ٲetuh>&d4LyAhi 1"JI5k8TUt}N:&ItnLr =*1а*Jox瓯ꧥ-ڧ%]/ŗeG.^Lس<۩fUM ݮ$~<:XsXR)(j'7k<_W[c}ڣY*xJnkE"9%/Qkߡ |cJy&N]B+U+X睕aEُ**>N,ĮZ"\doƇswź*\' :Gnߞ(M$2≗F0ݷgi:V-ۻFl)wŀ> 8٠AVF&mڄCPOΟeEU󦸸*jUz6qUQvA=hO>L+99$Is *dTBGY?Ng2%K8+aM.XcSogSR䀣o/).Wʜtć~a7zqu}goe=\JP),(D'цU'&MK{sb~ϼ>l\ I2n:!Ck!N+<{DU/tigN jA>mv??hxb~}EZ>|K~9?7WɁnf\sSo毥swŠ&9hsС&pWi`#Ѿ=u횀c|I'Y^wnC_Sg̲Πf Sa @*J{<<~l=6r 8rHir>qSyc/t|_}@LsAm{Yz%~\?؇-+QQ3Bq)>ˮ [8.i,Q:в $ }Ss=$!yY爵ƭulޝ 8͗3oCD)ƙ3"8x H5k1%8X֌в.@[?˟ײdeSn)NLңjTA>ta]ә]'!.ճIntngl:vn}?IWTr3PN:|..I_)a79^d#q'<.ou5mO횄1xsX5#}^RS_=mGlޡ@WyUZC&6&w/B Ǩyp|E?ɭ/0Y_'[Ŗ`n-u>ʭkk*%./kͳx~@,w;C^eEg0FOyfu*QZYg@æ(UB3}~=ςS%hcXxn$=3cj~YjjI \ХGoacrHÜ\a5ɝ>hfϭ|&εl񕃆2]0GB"{u˼{`]K:®ߘN*3$o^ѝ pU)C9}'%ܽ#I29i~̼ a9q=u}LYһ1ho8C|D?b(xУUtړݕ;Ӯ]GF'"-mB# nCpPZ@|}[ @Cn݆6m""*=u"ctJx{ a_>$ojH@wҽ@V^IJJ={E4rrsػwA1N= 7/i>KRm%mW<>Q;\z2?ύBw:j.iw{\FT-7l8x1sE"_d7-8[I'x'!3xV_|LLj-KhJb~<ɂ{mm4޹n%j|2<&RVcI!G ?v-13Z40:ݳƋv"o[F-C*=g>gt_YNJW\|neC[:V9=Ƙ  IDAToF}P>z[&ɒdG}|UgkhY若9_LyxF6k><ײ:\Eݾ]t5.1sv Y*:\ pC}%2$.h hUN2{xyEO^U^K.gϑms+(qLfF "58IHhU,gf9C JxYI)jp/֞Z@3q&94i3 KEE\G<u/'Š :uײn*:99Ҿ]4Cmբ8 [vϿ:EpL/b}qTH<=)$ A#yϘC7s"K_0cFYzw+<3beт5]_1!S^>ZV=ؗnqХE+8=EޞY⼡e]3)c|e Soy 檃 ¥#c $3)]uſ5tK!B%c|wtG nNp}ѻUc)a g-HF\}qOjsiEDVK3jWKV !T +I6u/@kI2[:F0|؈$~5oۻeO8w)}J-yLnNhObXh6rp, s][֙wŘD|: \{i% r:>_ mвPA9dW<7ҟbTA.&{NET\].!֊R9q(25^U$(ڇ[/ߎn깝hz^PA@h3{sFA[r+FWF u<쭄\kл\ NH8x ^  4 #n$w3AiP;n >~Jف.ㅁΗ^ƭt| {O`pGt9q|)⊐|Gk7SGAA.+zG/Z BM@Mo>Y\ŇV\7w?\ʎCZmxբ   " mOT2 %1oė%%Uj$0$OEGAAˈH:g#pkAAAH&      -Cv{0;O/y #99;A+@@@EC,RAV  S!   B   "D@*      -B   B   "e_O" r-z'=2VJ*S'YfSOW(9٤cpwhY9rJBVw :ڇi,+{dLAEl߱wлgo\շk$Rb5l.v:wLZG-SxJM!Éx0C"d/V=z3ơ|fZAYU4f2cJ[tñx{q6ī(qa:sjAZLa>}@-,sQf!yʅjyLQQ"{smןd\;1^7sتYBu(fW1r(&Nh.ZĭS6MA%q[|Y 沧hl,;>`sk)oAh:rRD\Ks$d'zIBbu;&  P,K+oC+Ǩ)%G[7_ 3:ƕ1,(:{#>^#;B6 ~& saŗy %`2qc7&OfyFep*J9ʱ#!N>5fh'_Y,Sk}s¿Ua*S܈NԢtexU Ϗ"9w6iEVVVrz񛼵6ك>wC.9%hƑ,wlj^MdI^Lȍ%BUcd:Z>;Z)@#~_[`Ă\{G3\@,hvc]WoF81>$#E Il%JTȸ\NSƁl`қ,Lg.eeKCj87gH9lϸ7M*)f߽ǿ|N'779h܁b% ݛ-j2=ui p)5[@fd_6J0Ziؓ\֋`䞛/# 8fQE͢RX{[әp@TvJV Dp#@j\ jQ0[ؚ1at-\ xiEI#_E%.^F 3¹j uʹ_|EzGmjxu cM<ǛR8hǽǿ @8z4{Njؗdzxi" T@O`8JWDDz9WhEUBS1Bٛs%7y3=HlY:J2΀f(G9E%%K%:wT2K'GBB#.ZKfCioOQ/A#wJCޚR:4}o鬱T^6#' гd:ҼW,?=IF.\XCZc9kTROב$ea9Ѫ 1 rjlai%hWk$\0aUf8Cj?]x~'q") vǖr-cuhC0'_B= 5 dY%[!_)4UC$iSǞ)L>ٜdҊ4@(-c3d7ZG|4B9U9DoP|Dת f;k@~?a)[ Ln`;D01GNkʍ1toE6lg!3z}C%=aYJFASP1s#CvSAMeT8-'q/Ed j/)I^wYdtP\ )!w<7+k׏I,?>s?">?mQPsط]v+E<1j~n֘|^~iȭ1fZY,ya0n5 Iy82yأ*\*if.Fk_>$}^e;_ۆ9oM#z[Iv޿56+a>o.wF\I747msI8ddMeBִhh52t@d2^Fxɉ_gƱsEk ;{2— 9Svvd\E !(7L-dA**$U6\t 8oj/i*Ԫ!NX5$SbV>gB A}>yC<1Z23]k>z<^ =c鰝|?w-gb9"HKm!hqpF91Bpײ-O"N,F Xϰtj:3lhNKf_I۱ Y}e7o F02R WUPVTZ*Mj'j܃<5ͺ$wg$}D' \bKtS'qsw#/7g.nnetrs?Z4UޝW5gWNW{Y@"s(/gfur-wl~S1Xˁc=bk=9uMmO/ wO|yV%7k8k*(^jA7jȮR+9aLFUQ`T]8S^]QC^Mu&7q߀2b3._ t2剋zl'8P&uھS/EŌgWw_jī-V֣sY4.eٙl! ^7 }} n _&~ l9r"EU4MCՔ6!:]Α%vq^e,'•it6222###Hgر|ׄ}At<7s乍6z^3m5ѓsfGEݑ=Q\(Oeq7׻?,ԔkyBN ?ĝ*XU@'a#VsY BʆgEӱ?R0(ϣ*vSfa8VRn37 +DhDTϞ&AFsV6p7]'a;ىڒ ~R"j12ΕS3-?|~V S TO&֝U䋈G,jV) J_*R/ PK bb!""LddDH4U#22BTT$C(.*hʿ4okJHX<4dwGfMtK5Ur~0Z|_P #54KF= s]WF텑[H.ɥtZRuAZzȮb-ҿ3 h ٺ~ݗ~^EhDu ǫ*(V b̲ ͘{mP8t4mT#ZNKfHmŹZ +<|4vj Jddeԃ-\M4j+mRACZ\\/!b2<ڴ %%%''' MII185qrVb%N`@+]k㮥E$6hFf}' *7xl z.Nbe,߲#3cU^WQ$-~_,SVּW[ntJ&o|]?Ʋ5֝c>>Tgd%r$\TUEjR 5< -b41 'fl.| 0e2=Or!oQg9N¨irNrjd2rM`4xRȩr,EE hBqH2с:d4⒬\D^&A˂&IλIxʗlxfAI.^.dſCYSG㧓%`]~n퍛^Vofb+Vk-ANwןO״J1a9iV;u !kpR5b)%`4U P=xPa6Zi|<~ǐtx_bE~|Og(z{rv:U ; >݇$M{,uF'pHNf8‹AsyxY!l7N2z:D?VHe/'FJhM5GᲤSdKĄp,0+Z0mX G9|*f2aLGM%%K!_dʯG }c-b n%stp+sױ/}!ƉG)ڽ"G--ʢ<$̹dh`_Y$>ӖEF ڟi3r=~zKywޞU%/r^}C_]Mĵ}d׆]|#i0x@.kUAUl48PK%H'sw@!{Udx%jR$ET+63$ʊ5X+륧6lX܈">t!ieH+EEEL&"rf=Iki6.9F$dL~&;F 4 fhˁ3M >(჻|у{gmWw^ƼMbl 2>G.[U霉O#_5BJRCلceW4R ɓV8\ig%r69bMw]16;h7Oe>w.wFo&py!;;m۷NaA!抯/}ӳY{H+%!5x< e1!A+KXZ9z]C'ُZRq WpZ7ĕ?:]éu\.+bXM&LF#&ӥ\WV7:" \&D@* B (<:8HȤiXW]][* \D@* %eIN=̖MI1L A˖HAACVfOyoEA  .j }7<ňW+?Mb ?HAAdBnz -YAQ:عz]rA`  -]cd8UAAA   ?" AAAZHAAAwxUm齒B %t }u {y]]PԵ! DAR j@z}#nB\@y{̹gT   HAAGQnb BbRAA_aƄ?/\Pnڲܼ\6;t"$$aiI vNtd$St@p= 5e5 SwG33\G!'eV$O"u!̭ eWV dhwACʢĢNnx ;dW(Jvg><6l`%/:f=tNgK97;z<J eP?:; Bۙ(9Ją5;p EKT|-~EQPUZ=v;bJ-9yV™grڔ͒(jHz>m%ԳӇH/qb<<^KBE[>v 'ͰfKշ Ÿڲ?kw='2f3F'z#?W0Qyn{ }ӣDJOYK۞t GO窿-H#q]|UwB(fOhzfNiF޹*mID-症35!L)Wt&@-gzFDWz _=/ʕVz3osI:9};sq_!u?bz^ɔ^'/P*H[0.I'Ow߇餖Ou(fx1N6\ Z~Q|!3g$11Ç;HaC*[I(o7Oێ`bKZSLR=LHI Jj|/U9? ꯚkׯ%:=W qd[L h$Xћ( >H7;z[oqcBFsqI@j2Xv-SLA2gz!LBvX/:txW9Nt͞?{yko3M0K!S'ԜR]I2էaЂkƿ:knVR~'P2Hwuvߘx}=nIsh?$G3 ̟}y7CGVqppl5ᒥ"m`XNj?u:3~#e9B/ Y>خ)8PЇjt>yf4`l_*%?<ʝ/f/zeddC.ǐ+.'CwfϞ}R$zn+]lifePٜiCŻ[S7a>Ů l߶*aHO/Q3(\V>I}juXjcEBskgZHe2(r`עְo_> .C. H҈ EQXLv,|2nFChHH0ʇwtZJ [Y\?ee $7|Zhu(»v>]mT~E Nw'Q$=,}r3HNޞ$A[Yw_YE4A2fqݕ}O0y\/i-ކ-XpJU34h?-JM k#(?jɞX^vgB(O]{ؔiCUMggF! `fR}]ٙSFNhl"}z;BQZ璀_sDmm-fݎPUUݝ6Rh"_oAv *}h|M4C61^em~N5r,ɎnC{_|Wvb_)_Wwd/_w5)ϳSɋۘ8t[(b>?S>g _>3F6xXdBǃQ-LJv*iz 4񛖄}y%{lxYӋZz/l ǃsQSS; l6=37г{~޼-\~ʍߖm h:0 D^kr0u\k,Ǟ9t"1C*/ſANl#>㓺~șz-~W?YZ{0E2k"vimżϘOx cK0oG*p+yY'{`y9PU#Iv 59'l.xwp|oa+fÜy㻝't&NdIeknw5x}(Kqd(,[?7`vgĹKNGyڦdSon׾- o>7֡H8Mш7Ap%w纺:<<<0(BMu K_׻bw~"7~[eo1t)Q)]!) jjuS I4t~gسٵ-ݒy-vr$c7cq};_aEuw662=6 iLi¬ !S-=Բ-R@O:*9J>_<,mNǷerM1D^q8 FlZڻ&^^JA?)9>nU'ypsv"`X\VhB-LcKB<%Eە^CH,E1'w9m(ˀ6䉤ףb< ۧï#PI<8W${2,L&SZ7f-˧NdxH 6 3}bo_3[H9ͅ%iuh%P/Xm]OscNExHyԺ|+,SGE9*lM3:)7#EqC F^HtOg8Y^-*(O\ߍk&a@?TbX 歛"dSN\9=l9k߇CJm؟~Aߒ[zJ5&x=g ѫ}䧥sTrNJRmy7  aeDޞ{'''c|Y۞ɯCN<0/Z0_zO|",gҫgMT(ZkSO̩CG%4M#!A6,!|/uI3ԧ/)Hсl*DLkΘ2/BȐG^iSJ\2 ԉe`$$dڹӿe"f//foɭ6pq[G?]Kʔ25gfT;kw})fm6 9mˍǟIw3Idӟ˻yr_qhw,:g4q2gG1\}Rs./Uj6|?õ@>z58MNbRWWGee%& шޘff3UUֺbwH&[տ㻂G*_`M)L:q*5U5(*XIOɚlH5}pwG}ؾRCUm^-.H'ĉx ѨUWN]ƺk;`C"P,Zp7ͳ/O̾b0Akd/(KpRJ{Ji1% h"TS*#jVϸOGylLdB7D~賬.sOXyU?x5 Tddʾ۩>~ iieр&2VzYV жRcJNGg+~ S&sKS+R%L暎ZT~]v2=Z*ZCwY%qҙm.U c-N[.ͽkO$RPJڋ1QD ?m$5j]0hUɥW~jƀҳϽoaeʵ#e5H ^5_hvdN谲U9>`ތ"3ٟaS>;.E J5[2l^پ1zU£FZaZ^CYN歪*dɄf[oe…L>8sj!9t}_;s,|G;4]v.\@EKMSIֵ4BMUuEicÉڬvx}nv3]svEZy: 6[qx$o VNƀcw [lL>]Ceof?m}SEw]AkQ)[SKO0.}C56`TA208H 6~OʰZNnT%h+jY}Tur768^q@?Ry:7< '2ޝ&zʒq`κLGj)wf2cWW{ +J&OiZԼ?]z?z-7Zp1Z(Ÿ^B^xv>wR ql^ʭ|y f*?1.~@鰷Yv[ o$?$ C׿UǦG0܉luZk|HG/lhɖfklcRzԧUda`<15*܏gH@O}V))kl8UWb.+F{:.玽3s?g>ƒ^H> 0]CpkvlsqHn^x9|뒏ruٻY>0}"FImLd1 ilUT֩H4VK{ͩy8Snjz_wQ>M·go:iUwlc u{5d׀8zt@E[Z-ADD#Wp?W'yvsLO=qlo*|Y6soj2ޞ:Tnd=eNq>4Z]g\(e%"C Huע~լU\rIy=Brl3V{k@fИ! %ke[C?۳׳:ǎ&b[q2+Mmzlu6/U fFF6.((4ܼa IDAT|-| iCAjj5ķ@nG );Gs#OQCSbW}ddݢ%۵t<kccn^4_}5y;GRHU*LQQdz/9NE\PNl6 swP1M(xl^y|Ks`:(ZGf3n{HQrkaɜKEnjrjdPM _ $\\rM%&&b60L? d2ѭ[7WE4tz ?,ZZWs]kRRTbxr$h+5uJ>G{^jiԮ_*P"mWLÚZ&LF"'ǚ_^ )]'C9Gz1jF:P~ {lZz icL{hyzcHxxy" 447ʞx{9ws:d{.vWsw )#?5IIF#ҝ;9b%eNфEFaAq뭟8=@>#;5oEMUy-N6fr8^ LgH5~2[s!I'*Km2jIBlڐ$ğٟ!Dh;Eːj3c9|i;3fTGtjl֚uy(.Ѿŗt)+-GABxr9?A~2`$gJabF. Ho2I"((O*gYf}6&Oݹ3[ywoNJrnfĩKʖKd/`|%rZGфk~YȲoBE,L"铸z\\.r$tԱvj}}iL~U-eCSx%ol~AbΖVnhiKɟdٛSx[JFzg&LԹn&~3I¿sj6͈kf(a Y*[H#G՚Ɏd#oܩ9%w>;ضVUC)F'g{4(n!`}[ؑWwVg3shm<'}P,l| vfK-LGxrd8i?[k/~ $I(jc,htq. Wڟznswmuװ$&FA(-ç%h'k= >aZW'Q$Aߺ5 F2|ն]JǞ}(Dws8Ps6/P:z"{]X G+R\8ԩ?o"$$NΝ;/ILLt\G [S"7QzƹݸjhvW͕/pK.Kؐ7gqA2Uŀу[HRYk5;qMc I۞!OJM8V{e͢Gg@e}U,;c-yyC(b F.~0l0X|9~! 8$^t\]+s ׄ~:I71O[j4y񼞾g.ėC.jؾW6ԡj#?s.u=cʐ|P_?MOOܔ #& 3i{Oatzn1Wv'ЖǶ_~akm)3F 1,[]hEôEUuyj`O?&mpgᒠTUPOkoxXϳT$!z(DimFgi3+[gZyྏclSջ]OO'Z=; eAEQH®=qb@X`0rTju5f3H:vj暉v4M+\]^ϸ ܩm]Э)^FAuo&)If{6dSԝUD}9A_X35}$ɸwcwpϵIm\$Ozf}߭cXy,= =co;![r~6SWQQWuX.BDCy9k [>xy]fr{ \UUl6V@@kX7ĸZ4Haa&JAʡC_8DHH;bZe,z)^⃠̞3[E*40\+c8qNƾF12b9窄 I3 tx{?}Ƀ$}'|9]u%[Xќ,Α0$id^js㋯̚gsBΏʽg1`t~:)H(Gge8f>xιCz(8xܲTwu$|mP|4DvJyݟ _[ tIL؇q(+-ƨj 2̻e&OzVK /.DdDl# ͆d2)//#2};VV[]Ѡߟ"o/MHPg*Nuwuy=Eyc3߹aw,.o}:8nlx3Va鍊,ɫ"4:h%-Cp;奵#4AHt4abL̢/bPqv:] C N tv( eȸD) "38Ngg΅F]H~DGGc20Il2Qd6oWhdz=nn a0t{:W3CBSKVx&6s؈o;:k6b[syiG 8"RAAIFAgZ@hN~8'܎7 쒟yKΞHAAQJp =Ytw&qA$RAAA@"%dpMONOK"ETAA.Mǩ`5v=!OW|'}2EOx,  F`D4gJ||y9$AiAAA   pQ\i}   ];/!   .vI      HAAAB   E!RAAA_S}=u fPTD  ŠpaX)Ƥ CӆͬuU5 sT5TTx9kJJ7cGw!%GCmyFzZO<j/BE@N쉯t~EP\XLE+Zܼ z=|3cxxHvg]ΰQRXHi7vAm;'AAA8\PXXHmm-6 NrWgG)$e3aSqT*2hK) ku3kN]ڝ+2A)709%gkǾBOit>D'fhJ{ONonednܟvRG"ݞC@QET ϩh`ult/Tř;ٖ33)1)>x#qȕ $()-&$8;]A$IXX,cZx{{ӡCWX-؋ȪRq*,}C:7;HClaƦ_I=jDՆNuF>ؓ13fC5|$OXZDn Kԩ̈́.{v6.yiu"ڃПM@O 8~wҕ͋Y^?v0TpD5RsʛbW#C< R?bz⬭d wy6 e;~懭F"{/eϖ8Z7Iѝ7cښO}Fׯe%W>=A!lٲE p$ -,,^޳]:}vu,xyzRQQqqR&ḖD玻VUĞ/|q+g6 s Y@AL;-Fr5]NVe|~\ɫNt0x0 LdpRR!voLDpXpu!VJqɥDΦ d*\6(sGl6( VLCC RAl$ X,Hnfa6ٿVEQPTݝcAf1dcDi^%"c8U5aaxIR`"R `ƒO.jSK76ZrP"# At]:w m!x2URѠK xz渥N(Ģ&ϩrP^adZu"P[^z27gP‰J9CbڝzEJEG'(-&#&In04n|t32mWvXIA.6tٵX, l6 b4(TTɗNRGL1u"yѥs$:cK@֓~Y+qHPT!mv}ۼԪ;ή}UcI<| {RwbD.|r$"Pb!Nl KsP[CT,cNt_ʗ)j=u *H<=[ %<<=z8RGSٔc! O*8**d_<]d_|%F2xrUEAxs.6~(wDRax?Z՜֧j?RAY. HM&6 EQxT'H$h)C9Xzb ٯ#4pջ"a4Iݗ=<ЫfVdP:KaPf1U^Ͽ*x TSULNV&݆KyX r)It}BuVDхwpwZ#H)gך-m*)Ǯ @.On^rՂ٢Zt><,msK+ԀQwG׷ wj4b$//e0G⫓?!mZ *سzwmNQ^!VDE|R{%?~:ɏF'#1Qwf ,pT$Um*$Ȁ*b fdM&ԞvvaT g*;k< 䧲nPi`ʔD|bVn gꐨfm Fam[ *. HfscǎtUUl8=P9ϖrl_=ѝMqf)4%yEJB'-xCZuz"zh|~ZIv*Vfl\7Xw k ]7= _,Fk0~3{j ٹ[.RQ5D6T!_ZvbՅ l= 9㮧͆XKYa6{`݁]< C;#_s*vZ"퍁[p*1jdYoJKc$,ruxPM99y;'F7I IDATJ!߃12wF)z7t'H/ Iqi,ΦhpQN\3fkAA.3XV,)r|jTUtmmי2JEK_셙REt㹎sNAFj 1`@?p4(5lu짲NwJuC$]}P+.زjUy*r`$g>'拹e߳|W)v8\3;0p"c:s(&_;nv2ױf_݉l/|9s䆛Auבף|wwUՇ?ܑ2+,A@n{_jUkmֽ{:p֭8Y {C y  {19<5",s9- ukG>Z|Y~a;$gM眳~1D3aJA~}?LI*!ntYq1J*wDDD#:u_8]^0n3f Ç&`լYULQc1pX]Ok;Eq20hV?(5Rc4M6ETX1ĵ&tty =m)',~tcsď`H o(7?QF@[Z,CMņ[.2v㾓'8]SEu UvM!0c~fObc'Meo4F66N$ly}\QDDD:Mԝr[A($993vI 'aSi%bll,FduxX4sv?j4?v(@plÇT壸p+!#Zy;K,"*{gh2U iUaw|y܍#r|^^:x|mfDMxWulRIbJrAƮL"#}/d}l.nS $_ 1H^ڹ*bȲ).܌ۭ~"""=Sibb"~8KYl^H,]'D C `2tX%FY9i8]=m{6a.f&?L`@Ft,ز[°j,葩@b3?g?/rU g31$9LddXl__om@+f30Fyiuh$;5T5O2|-!챌;"߳모 &ޞIL~ZXT mጊKݶUr]7̔! ./,\%nP̲,N:OnjcG^DDDWelXf ϧ9ӈ]u*O0rױ>4aɭ|_ọtg 64>lgW~ǀ#qa;z*mWE|+ D(VȚu D :#A/z[X1~ᘁ:PZ6#>h7–So [΋uKVQm||9Gqq`;)xy %F}TS\@#8b+祽G;gȸ4k|eHcRVPf1Ʀكʈeqb~%sV.ˍդdq$SsSZƢ/Qti#Iss0OCRB8 %.ʽvv*jKtky^RA=S[SI{^y|d{uAMTl| EDeٌȝ9 !vd..)a@JtW3\D%<ħ#9&<yiu3"}nj """"""#HEDDDDDG(HP @*""""""=BTDDDDDDzg{Wl#";u{""Eξͷ'"""݁@]t`dWDDDDDDzR """"""#HEDDDDDG(HP @*""""""=BTDDDDDDzR """"""#HEDDDDDG(HP @*""""""=BTDDDDDDzR """"""#HEDDDDDG(HP @*""""""=BTDDDDDDzR """"""#HEDDDDDG(HptDDDDDDwm{7 K@*"""""rk@w;~^/f؀{0<w9cQa;h0U ,Fxo)A "bS!>% ۃa:ڷal"!B,^U1sʔLd/)X]T;s6k~ERR^j.g q# N 7ѬJ.]Gd+!6ҽǢ1l@?:,:?e!!;+O?*B/.Mz@qrX 7IZ56Li8RT5J5틈H `5յܣOװmVn#ez~v)7=51D'uE`͚[F087o=Zm-?!6,_Jw1Id]O"*}q?0𮙂zf}'~ 7o`Ɬws-8Ǟ(k_W͟KRVn5хa4eђdŴ{s91p1~fqLn2жR*V6u?扇aւ=CazHs<NiZ%ض+o/-3pTTDDxWpیa9?36q$KObEl@5H$qj;_96nË{ UQZ"IO'-kX|+ 8<ĥf1jp2"LVf֔@D* VaI wF=qSiaN^zl3Ac6e)wY۩M]: +K(\ Ìt"j$gͩUC2IjZHcX YZ+~C0~IHM筤`Xef٦tvUe*VT;K@vr84ǹVjF)1fY{wqȮ_TN nT;9n…u& 䦧g@jIHEDDM` s_ -OG2޿2Xy[f<ŷ|F-az sJ.WfJqUNSFEMc 'R:+.p |2NJ-SbĎH`m[ou2'0a`l}xl$3a8i]Wn{OGpS즡$}9]o0#ڕ|}Q~I+7]㮇h'N xOD\xcD:d]w]aG G1QCA'<‡,|6~['pMꢭj3}NaZn?^rc*tGg|os-~K ӏ!ӧL`L=zbw2lx.[(FcF,C,fe`*v$0ŋ72™ph<@hջˑOo >4 jV%y*;fbOu.;\:d FEr/ny3N<3H82;i)kxx6oNr%1ow&qqjnU>çMé;kmj[LhpS8e*]E}'8I7UX<}ϛlv8!H_,5 9Qayˎ`ǟSIqfή+89S94Oݥ/( cOgƘQ1p& qblqȾ|~x IvCcNJ {kP H gЉW3y[Mi]-+Rb.(a)G#Or0>E˼65[ꠛF3*lEX9ɘ@`";IqO sP͋f[A>3  >>H7\?~f2Æc.)rS8Z=Rd?N~d+S+9) Qg2xp^"Wc{ͧF mAMt>/72*aCٳeI汗r,UʊUUX=N)Yj{8Ӧ}BALG'Ggm`(wՐDvq%U fDOOX^_ƒ,\8[IYYw,b Cp[F~޺~^,ёDrepQDb|YN. 5lfλoWXm>֮){ sFt22Lb1)êƦ _WIEݾ2|o"ǞM-7#1puWL%ٓ"'/sG?)+o~&.! i?I)I{0)Ǫ(gͮ-%"d:]Jwl颪Gr\tyRfzH<9_pu ^f6 /s&'X†~Zbe\aa@Xˑd vf^^ㅰ$;q:sxUxVԖ8]v DMIa;R&=*X=*ZNfc۹M-߿y؆#ᚊ=? e]e Di^ e7֚vm|Wl@*""M"8qC`+pˋoqI_\1qaѻ4ȝDǢt,>^0qͪ"̈́ьo]9`y0e\a&bAV篠΃e|VD -Ԅv>*+vY٦ט>d.а%$spx1gWpkOoWL2`U糼]_yaڳ:|܉6Ƀ v9`'<^!܌>l"qM5`[Ekk =dF{ mmk_X IDATчrѩzxB'OHoMyl$ÅE> wi>"""&lUE\}}e Ą(+\U%Ԇ G683)\|?6$`,Yf 82.7ޚJW$&4ۆQǍ (u{O嘡Qg_??1gE&8#טy*N:nLwߛO rNNBuk{n.ÛF;WC{J+~TF&hز/?YHis1Ӹ¹|x!sﻌ_|3#F$ؾ?K8h $3dޖRǟ;$b'seo=r]3xpL:Q -PHԁT]x(}o%&vaaMzzsM輍#G6@*"".<]]cq{l77M䂟@ n{6g'/㦗 7Ï/fĽM #"w?OWS䁝Qh4`C]4a~?_ό9?[7hq)9;6Ih_s 0ej ,""җ,3:vڭ%TP0¢OI_.[jʦeXn d@|.ȇ*r͟Аq?+F9_MExQHJ[,R+$zԔQ\^G0,Ӊۼ;@:'=5/Hmi![k #ޣ *m>!1s QqmY$lZp4C2b:՜{2c'Op7!vdPՐET@\dG$uGx{U*ݱۯ J[D&gDe1;.1x;fhrp?g六ծM/9Gdr츌6׎5uf_/.ryR6QP]^k|vylwQ^dJDTۃaa䁦ipGQX$6-wAn'2!1%6VZ^Ϫ*>XP9*Kx[otoHEDD\.g"DDUdq)Y|WK_lFx^,+Ԏ-MmB 6`[Yn1a0 b#8:7Kjb QQ8v׌?%"""jFd*t1DDZ'M3m;," Ѐ%`Y*mDFATDDDDD䠴#Lڶa$*q]i8yhATDDDDD ιs呧.ft/I"""eQ-[ .aa$iG~=Gʯ{L?'|hxًl4F0{ѷ~'Nr:1?;(PEKAC sp8}ղeK %e7z+"= i@Ӵ_ä4Z|/Gf}+X|^?ecR>bC3핕>\N'."P =+"o=ݾ_yvVA/ .ĞHEDDBU"]9IJl GV>ocVK7}w̯y,>m0NbڨH~X ^4n 1\ ec3&?o}ťBrKr;qVg|q={1ml;D⾲}kbXZj >,\3=+PRs`ɮ~v;"5'#hyĽG7`&%xF #ToaKm-ޖm)<0@0$b eT2JB{nL?=@0@0  H'XU|7sӏĽ7sF@BI7lZ-c0l 7hq?K@*""G\nSҨ,'3361{2 6l8[m%)1."JVa! w߃r5gE /v8qv>HDLӉQ /oa8]=HEDDe+яcƳf*JP[[eK>dxOODUЗGd@5 ۶w綿B222ͥ3mshÑ7?}z^[L?[ᅱM`C`VAc uj˱ cԇTDDf飼öl~?W&&*f+aYmaY`c:5a6֎el;sca L1fFZX SmEfdM#cWoR Qu$M TeX8s OCC>χe[f;UvHi&rǎ!224pdfd㦍=]4ֳ5`U]`YEѻnfiX&_V{ݯU%Ͽ_HMcޖJ.N+j0,/u6`m@{I`F*Gkޯ6R c8V,_Ž{(.̑Ei)kj#ixiPIvv6z 2Ȉp,+DÿĈ PSK#w۞sgZU1ny vMxl_ncoDYsA gӏ쏁 f&dUro+X7k5VRZQΎ]&R!cFEV .93xP=.H23NgwNIX3/e_}-`S#-NL[]Mڌ{Ưpm%T|?]!)2(-0YSX6p&w\Qfj)X53`:W\ GO$;j\`!~']YhxLeyrwwj8G/Ȣ"/p89~}_6,'$8q8SAneؽ*WR ""<<=] kO΁IF/br>4y\t!1c-]JÓO[3= I:̦>$lx B6c˚ ġu'2ڭ-U92xGkp'3᜙t"SOnWY!f8igJԟeSy~ w6Dfa$]@}+2a(HplFNbi8P(bʥ Ѯ]nN;UDd #23&Wv6{0N\0\ %ET0=$?%3'{NdP@he6/N'4d#_Șp+acQ9^g45F8qiS)5V2~eF\@+pX>jߗsoW0hhib0GbGU wߡ(HIƌę+o[^AuבxՕ44{V7H4rGm ] 6PWݰVPga`cY48EWG𐖙C:6壾*ϐ [Զ[@*"""""ݮ#-CꉹWGT< _~I<>='8W9'7έ|ӗlZEel3~ :0:{xqέw.ooA7Ov-זPeۘ) _{CHg:(/- C꘣9g?㬉ɸ:iÉ4NLcsgo".=6]Gc=a2x."N)A61L 7ѿ?Y`ŭ{;okSx3|W6< ϡ/>[M醥̛y/$uB5{`y|?ju _d3wqnZw]<, Ϙitb=mlZ5kgmqvgDl{/KsYw}fET96~pJ>j{CHos@7 -7>ͼ*wyӕ#daۀڵm:Vv{zisU+sjm_eiN~ٮ e7qio7||#Iv4`\܇Z/;v̚s(]Γ|?{7t߉:n$w|yU6fq{]U/p{ιJB@"0t댎Ogtqęall2>TAI H%$GH%$$Y+ksnx 10Az:ph뽏?)/ѐ!]+p[%@X{o~;a$XqSw<:%UngOf8N۳X߁Dﱧ6zSG᢫ 5/2 =M^ɵ fn֭fw^ A%NZLSFvl[Yq'9~ Kl4zFc)!{)'sVj lIDAT,Yb_7N\s/>z6~0F$EʞWqg,fǺl]H@M±AcbQDI !4a i}E?|>sFSUۍ*/mY#j[g25wFh=>/#So髹P5|r)Y7֫ѥf{-tn~en\ޑW]-h+^}6ztNC_T-|ԫ;zXXVЮcXvu,ɃUЍ*Ӆ=o?KבW{*ι.u0*YV;F-pggw{kSPSv<֦NG`wn+e&_N݈=žbix2{<0݆ۗ ̭̚ɐQ=89cΜU:g T>*gc1o)*]/fĻUOؔx$N0wB!h=ZU33lܹ2~?`]QUg4ͅ@ǡ<R~`j.n;ݓ#;0]/[/}ܠTE{V|VmUIW.G.W粓TF:jD6;7l+f0Ɍ{h±BuE=yy8Q\v1Ob9̮UsVM}ܼ@bslң wr8'O=u _+0HT)a^0B{Dڀ>vsv[q8gpϜ¾EsYrk?GBglɽ7[{&{&{^.4.6>%$BZ5x^,¶4Mr.MJkDFF(W[bȢ_tv.vORsv^dz̡msYe'd]4SGT:vF7 c[3{n!Ç1>HBX-)U.OnlA!9 >cܷ2\ȨTyFk.ֺp?m锯#IoiY8}1w$ҡ{淿W޽,XICiV`wm,pq6%,byJ1)-c$c?˘(a$ہrVϽL~*:(J wٟӀq&FǣĝĝB!ZV5e 22HEAUUt]vh!( y謞(e"A!69rVېBUH~UIԚ6W+|se`[G-8!cKTofǠ#I҃޽Ρ J%JZg͂oo+x]5 aeɺ3xy>Y[32y<ݴK5Ыfg)fٽAlB\CW-@0tܯix<ϾGw:y|i)։3Мʒ2jl&_ճf׸Ty5׏% rde@qϜskY"cٸ\&ţ݉;N!Gl*Gxx8>GD g|o] \nqM~b^ߚӟc*xS?•f.RM˙۹ /v, %*9BgBɴk9Xܟ7-$vÏظ8vCjdǎee/MDtQ1ByV|GTw*FE\N"sxp죕Y̕~5k_zsWoo9 }577:|A5[(IPZOvVwme˾btGAō~9g<q ;+`'/fq>=}szoi3MNU뭜Ni`S;|  ot&联{_FcE420!c (\<ŷXqPq~ǫ޾Ys~ݸ۹Gwnb]ljmrUK֏#.W 'H)o%w |n'Nɷ(]^BS~wdsٽsTƍ66m?{=1k:vo^I/O%M7Rv4:zՅvt|o&oʴ{;,Qe!^=q__S~d0G۾Wğob`Lzӿא=MYs/ A&Vo/ Lg=D'>O`5c(UbfpT=ci+(ᗶ 1=i/SUO}u/ޗps.t8TTzq0-P(@ )5f#XNk D;'6YF*-V) ? lOH6Q z@ԈxR#w6 m F!=:"N-6M?|PmZgRpp~щVKLeƉ3 3M}_düd@gU8fe(5Xl@}[6.B;;!B6j i.)(`oLc6W\~e#`Γ|[=N|_rAX(YϛqlQ&rzFRʼnٞDZcT'4ќϦ_b['k ]Lؤ+qwǝB!Z&!mf.PP!:9nbil2'] I\q̃h&Oiw쵣B!B"jn_vnfD;_,ۮgGz&qjΨB!8Wtp1=\?cؔD#Nx$BѨRUӰ, U=}:эJm-ROx!-OB!8T찋cZp'sZ@0PJ<3QKœB!NM vH+T)Bt\WZeTl:H)1 Qr`CE YpCPp, u0D|x|_w19O(_r‡(;Pjb %! 󦂨Б ݃ D QKA66Cw% AaT+>b]%Fh}x;=^-Pha"stpPC)[37<"㈩cدh7~:sQѻ03|G\Q)̟[A`+_\n_FyL e'o^J#%N؊h4FRp$)a4 aZ(ϭR-0!=JH)QRH_8k3<6r´;mP0XH<3YxeFj,5GlqTt{.JQuICjMae8$Y__w_IbSsp8'U0 p;Vɣd{I;*Q<1hUϘ$<{9_|i4!PR EV-1I۸zwG˩Tj8Ba.Vp_j2'tq] (?tcLap;6\ƌA'Rp"`'W!&w)n!]gZgr.0⻐@zvܩ@ ]}X;xdٳm/(%KK8QJe\Kkm!p,;;?GQ/ ߜkl__52y5s"J!FőW& Q"B?W:|Q7`BB=#{> $iHl;[yU0-d,)+L=*íIJ  c돻Fdq 20]JYg(̂p13NFG'žl]ҎgL3e*M) e"ުhFTx.]:7,ikHǼSnvVvuhÇp#w%.j47a )@`s}Afz1b0_RRI r4\8ꎊǡW&I/a%Ca(P(ӥDFt*E!Ia"\guiB$M) ^4-s&WϜ[ˍ{oƾFь49<'8Ue='Hr@El\R6n4Fx!YQk複yO: +P3X_x]/5^r=l TiEE\cHe a(:m0=2EP(!2IP>((_h{?G*W.aL%n 1gܼ{ [FRa]25Xp't»ܥ\P.%QA03wFnxd+˶@'Hg2L;3&2)k<:~/wqh4M)! nE!-]s87D1ڈD˄B_)/v~.Y_DBID`o/">#QfPɘ4L7ؗS԰ ?xzLaS@dy)['2q]vkkE\QhǗ.^}7awF{Bar$f(0ĜaZ̭ *ډn:ϞcBb/ aKpb>'wNA0 j-kٌϤM%X^PNUKwUʾSyS}j /I {.yƳ*AMP.#]PRh4G鼤tv.S2zw,˲wf0~|^=ܩUF9N󜢠v"00t\pp?.܂,<"e8\t "pRE+,rLXO3$(ƖI֓ Vg,X~TkB_؇o\osg%HKpm\N$9=H󒦤ߧƤ Eo51@.y#v X^⛇(AV4Nٶo2u̘?73Iۘ+bfShXW B@IZ!Cs+18⎅iCaȻo sD1X$EM8ڸ <]@Z"ID"Ay)}wW #Ǟ<]}rS;{()Q_(o̝ێ'DO'D̚fy/3ڵJK+UqL^yMMc|섭|jug6rŕ[ k0x}m5E'֓[noz O\P! Qy1og }#xb_ni: H0t; dZSJ +I9nȺ=@Nw\I+]B8Hkqyl9 \p=00<@v.3< 5ؓy~H -P-] I\^E|ny{_=ʷc0`Y@p;ۣR~'w=ߜq!yL&5gwrsۙziu/ ۟~'J>6pýkx5KR [eVKI9-è+yu D44\DѭؘdI0Gy0o<}5 <&/GTJ&p 8m2YĨ;h{fIL+be{dϝXa0c$}$BQ}-9y/_2r@/m%@.tks|1iamadh͌+%bXAuIa?5-IToњUSw% 4IfKDg]ҶK#Xy*ss`9'g3љhsL2$t 8r.te}ݕ1°i>DYi,'OR08BEp|ڷ0[HٞMU}x%B Jb8 !%BڽD9Jv-o_pK?-e2`owf3b=Mw/d z Ƞa ,Pl/QfwңVS1q⾝?W56 ]GTr.1`?[z/x 0HG/h[3x㙳yŢؔ \uWySJz|[T6Ӥahf{xbl]ANg `žCCc|x/\4H 2|${q!h4G@ABڐfz@Z^X$=IB7LbF.V"I^ 6rC&9pueCMDĹEm;."IBu 3\y1(= >\dҷtgX؅=== GyaYs::SDgTqu)ԙEm%AB>k%Iq5MdlW O(fSq.dms 1zVAH&Q3GN:'M9Z(Fdd9n)(0>C߹H'Bٿ R?^O(gyi~tp &^}!X$^XcW`Y/o,c>^ڳ.yOz_*΅ɺ"D#h4!]?hS `یeLŸ&:^Y?v(؆tL*3,~?9z];:X%a|}LDx'Sɱ-ώRA} n\_}><) ]K:H#Qr>b=HuV8LKQkAG}ä'Oz:h(,PpIH!zQmdWmmN7I_lr,nrM`;Ht&2 bWde}0\[h4+fS33j0DsOC@j0JTňrѕR4nZ s;y]{h$PA(d]@LbZF E*Ø*[O({-Ms +6ѭ9H,8p(IR=@A RKu~+O4*Q.<}z ez#{WW]',{:690BOXڽD9;Wsމ) wb|o}v!ZՅ|zﯭ;7~h7lW׭eG79~!=ο9˳WB=tc̙1&E{Z66 4M%>/#YF9.\K J`F"/\FznB5i-PRҐn*w]8US*f i|)p#}hY9[2fwT(\-/<n+tmR2^A4_hO |ڳsL,@:aО\),x?.4b[ I-`8Y$Ì^"Y$|rҒrBzا\LBr˓rrI;rb2O N О[FsQpyU0OM\z7?Gwv@UX8qd^~l^4&!@,i7vD#w=-'_G F/:ߑ fE| 'sF P.?7ak)q |=s#ٳx+Nm3M_) "IG`WmĪ:0z  :ɒnkfgFYͧޞ> ZAhAs_ib"iq_Å*p#]1!ewq};G&7b^w圹 w$:1h4#HXH)H+rē&َw%3" v]m;/~_VGm>_ t(a~J[l3FIGeAL@w9* /\$$"+6 KQr?LHmXvtn)-ZG"z )iEmcطӯ(h4GM|=ϖggO{wn7Å⊓۞濾r;'[rA}C=Gђ\lr3ްmC@Ln oV6 0)ƍɂ[yٍد$y/a4KxJ>d0}4KvӌYm&zba|LG!-IӣZFۆE]^oTeѺ+ yhp(62\,+DLTD#m˔Y#\w(CuةmFH><% [ISx.;Ho'2)Ȇ:#CC!z4,ۂ|:" {&7gTHXem f3giϣ}k4DX61EB(\ǡmٛG3 hJSc*r, 0kR f߁vV,+H2.)iDk4ͱw |,_$j ?[LEx7msPc)o'H3jߊmT23UF oh18KyT4G-a[6#.8\>W>})a!aX|Qa07"^hwN8r DJdo0kT$@~RYW_)Tm$̖[R@X3R9 K4cri;Fہ+m:#('dž-;P='^41LЖ|.s/_hifY&إg#ĉ!%*l~-=50""(# EQ9Ŷ*c*U*#f#<.w(F$-fGFIRJ+/S?p*C` QH;m .d}%B`ϟ;T&#"=v *peK4FREnH }ĹVT'#XoTći7re}Z#ېxEq^n.a zh7}؈yj)Deڨp‡GAE׏&i"k+G/ Ȭ\Y5ҖM 'KX lhkBŊSQk8Fh4aSIQIhΘv\Ŵ ^V>)v<<zxD YܲZ5ʰ;XU"CRL[<N- 1/~xxu#z8 m^h4fxxNЄ7< jO -II<1#${pVxYHt/U$2%#O[8X`BVltv~z=TKv/h4F3\aƹ~aYd(2uX&JRP"v>w8BVʰ\r/}=腐II Ch4F6C܃ CXVRCqmƥ Eė~6 DS)ȟF,ܽDID 7 󱤇?V?ر~7za"%FhG(ʬgZ+ STWeTj@T>&,cPח<%=(_ aHTxJZ+=(T{+wIҰU<t"eT VP ־^Gh4pHVW 8!9%rq+Mh:@2TaxO^,oO&k*K#<^]V.Z, '6>,7CV=qNm$Vl^h4f892Z#$jp#+ 0g/)qqsBtkFh4sBt%Fh4%Fh4%Fh4%Fh4%Fh4%Fh4%Fh4%Fh4 pe:Ǵ3#[u4Fh4Z&XwFh4FyEFh4FsѢ[h4F9hѭh4Fah4Fh0Ztk4Fh4G-5Fh4#!-fGFE'.h48W[9(yOR(*=Ap7.%NyW۽4k6WǼcwr$Ϋ|潿bS>9\sBácm|[?㞭9h=<5_cm}AyT(9Dm؄  =ϝE8>EYWN6&ꒃ?N;c,'-jXn̦eʵQ54/o42owGEaja49z{Uf\6|txCc_WӾ ^GNcBa]ErKM=mx΋nWl[%=gS|Xf9Q: wY#l78357>7s۾a9h; 7TuS7_ &3,;8(A9XGĴÊU))Z ]7d;B.~£15B@rOscݼ%eBJB Gy}}7E#bnQôI䎿vPu^U.꼟~d ZNTƺ[ǯ~_6P.nf^qۘlQ=Nؽg_E)SwoѾSz/tq՝۸53jq&\:L@ab&i J=V}\'p,淜İ D9p?_BM3~< fφyސSwpoNax,iջ[|+Q@m_7=R}j:ՌfVm l Tnw;=m*@fnwVXML1^Z(:-w1')Wl\u;Ȫ ` Lxxuz\ˊ[чM ^9c2,N1rcL̄;y'|enLfGr+ppjVlɁnRuN3Sc;[h`ҬE۲vGQOzH|7F:^PtybO-ګ9ͦA ew`{jԟ?w%I<5W z]y9sh^Ԏby#ڗaYsܾq݊*$Iwt;x@}+ɴ~"2G񧽌n]k-Oo_ɝeqLWgsU j^Z6~\:"ɋY_=QY#8Mo9Czfmo_ϴE~rF S~qj1"冂♻ٱ%2; "C06yAL}p;*?Ƚ[{)h y[aRV%7U89FmY:nGiL^~wռްϑnGJzoDm^c4eP~92?:8g˘5=6l@3裔"/yQw߷XV̑W|ʩCΟ3~8M[d7]9.&1?78_r7ss[ئl?F`obh4`d&p֯bؾj )Y.O[3b5Y|N=Hא2)߬2Nx =Jd3{1/NKRy$=Cf^rmH52atfLM%P2~G>sPQN3N D6rTX*)/NǮU n!Luud,@!sx_Nj=?G~k_o_{mQ^Nxj[q2j~uCRr7o'O6hNbd3ڼHsxH)WB! 1y7s-m|[.'f̟?J06{H<꧱hN+e}lӫaY/R㘷`Bѵ6^HzwwcV-c*¨^v\Ǿai]DxxNdSO^cIgUZ^ĴH3%熯4wle׿QS?ʃr>yi4:7f>k(2Lc*sY̶ΓN`B+&p{|+kK$ s=0?\{})?\ie7a5zX6}%4Ki⻼/X<8m9>Y^{3_m}G3W$۹GŪ5-{׻GssOGS.W! IDAT{dk DAˢ%Lʻv^t5'<׻7w]*'~w=:íS'HĚ&Fwt EьX_&QJa;ӛo6It΀xV;D&QAf|Er>ռ(W3I޶J~FpµeC|LKA*Y>bA2oQ}TsT'J!6-oZ)Ξ^||+ȏ~\aH7/%iC<<:vϓvkFn-RIjHy1&ODkk vQXzUEz/{ 5H#f"MHٙ9)!iSkqәFYFjfHte_EȌXtx?ħbԽ(:˦7凩 ԂcO~s7Y:\Ä=q1O/fSWiD_>!?E2-ƛ}*[;7%7ѢG0'A?y +ɇhƁBaAN8{d@CdxJ w$6P@{) ]/*>vm@$GLu 3{yL7p%̙ nS刉0kTy Ijث#*,7F^%M+`QA0o0{]Htj0Ț%t (餤;ecL 87\yE"b4vXƍ+8~-yPQٴ 1wZtT*Mv6Nԋh$e*iTH=~$i-DQJUU=*p8PΓ Un/WAg0$pĩFOrcew?_)_eFpǍ-<u WSJxy;T~ҶvGW=8Hݷ_)2G_>nvp/\E"8_EZH.9cWE0HރyǸZ\оj4[o@lRrL}\~=@δ~F4RC}Bp_ȴlE[I`~tRriXr9Oqe4Q]i$jP\XṂѦ8?xZPvRbʹ=L~WWkqإW{ʀB֎m)ŨHhg@H-OՆ~-A:D+Xll oy 87jQ󊁘a2iLm*_W9[ ׍X>jq'4h({UQbUCj).J˞ɦ"pşKV 2VKE9%r&pKw j$088\HnMJ狯3fKcĨ),?|Y~$_k  ׸'8oFEK]\xh;/mew6[TEAQ| xXΓlޒ-u]WC>hv$k.l<8ҶG7O WM}YS+|оjz3џ忰4fn)wڷHp]3k~ۦ;CW- sIy 8%t$d{ڠ]j'q- EZq^6b2`S/]kIe!ld#xaܗ|mwso<hr>6UNY}}y =^2KW䣤cJ3;Jaח~وl^T,E.vḠ(]bk4ǹQs"F:Rl rAJt*f!N- #^-8_V(20;X ѹyVNE!;?xCiVQպUκ*+,#2 a5Q.:"{jPXz#=9G黿y8yk¸Qx5׸Lҳ@GPHKk&-QZ ~"7S)(?:{CURIJ)yXhq߹^fdbNN"K0%Ԓg`p`VW-OfRym ~Yäɥ Ir㧍 qA2ꢬ Dߑn ,hAU8}5kmX:RtHfNkؒZOK6M[ MBx KusnG+MN  ޛޞ:8m;IwGaC+&z Ǫ;2gRmd9 '*BJŚ@ҟJppXbyz;IATPjރTg69 P]1Ge4ǹQ# W0KNSѪZL꙳/2/ Y-*Cm{d*gWpbY~O1FjB֠ N YS;M%RgUUeIJ9ĖP'c4@].7+HyeEi>uǾyzЁ6z2w g)[R\CW-"czǎqߑo$'V*CAhXWA\R$q оRu':y UL.iJtيPwmQGܶ=+ް??7 mӖ¶ſ3xRTShOЃ2ïjwX.z\1@Ol@ُAx]8'Vʳ`ڴEN}-2~>8ʺg-j,,^OsyU{_wUTQrÂ#+X:4ckǡU,;]ֺJަel-4&zUY?U%0hXrjYF I9M$ZHNΑHșU>M{~LgJWuO~X؛<3W9uNFW ci5bK1rט'+LW܇ȏ9C KxzH>ͲB&Q"|}jh_Il\9Y259f4WfE&8.p8 K^$b*|[m(RӶV=HvU-|W@@3{dƻ?3Oɍb\s/UҲa;ѾS{sk2>ڕJ r;#HÂg TiÙxrc],mumSAcRp#Ü87ʴ 9Qr|ykM?W_& ` ׿71g+<,ٶf3K+iili" ﺔr{ǫja/hk$oz0A2oQ|3J@1V/ZWbGwio6GJS1xhT`UdiV"Kq?6S9w@S m[pm{ܧ%;o"GMx':4f駱s], Q>6Cr ᔓ)8֟n6gW??_=(qq*eBF͓yu?͇^bbTA֡5=T}7=scZ9]VDpl=No@+h5 jCpPKww:իzHVJNS֝hsUl:>vdr}>fַ^oM[Nm?pc3ضa[#,͏ jt_'R$#ݯOoy"99rbZ$:ʜ;o4bnLmGYiu.Ab-* 9{%i߹ۅyFƩl6;A- hpgp#O͉ > &%%PZfjǎ>w|xStvs"׿$0ٰCnEbt|h;;ePJFsCEpK~!~ɩD3rtS]p7!xt>Ӊc7BO!HsOs۪?2-k?健G~ k[Mg?Y@VNfXB 9+8V'xeQME r%KC׿nd\04YBgyMо΅0)cyɎ17\.j1/2Pb|C/xޓV׳wAz*cyӡaQ9΍f@vïe4~-[#h22[5lN'f3koa2yM:Jo૬5YogsWsh*N,,c )S+Y=9V=ivmǃV`zi6cy7yfydz;H"?zuG>Wg@G]s6O1WI2-rk E?X*O%˗~ŻKdi5C733NR׹aԔ36 ӘTS5@ ueʍٹ{/>/,,bA,[qO? -CCt, v/9(fx&l746~@20YxiN^rmT|lo|/$?04tw޹Ҏr$1"ŀOH41[9y< EvWSr1Ap>2KtxWԺoN|4IM^rޑM#ͅȹp/IcMj>Ñ種U;Hڵ?{Ut opV|Ք\ cD0m=g pTq7jLz 7jM1fiE%oZ4mxiD-HWq_92/y>z{N;e|F\07.@  yi1}?k֡( 72ynӋx5$&nbsF'uipӷl|;_ǐhrظ7$GL@ 1a<<_!%oy:$dh=)tK-:{>->a ! =u1T@ D&@YVJHV/`RWmßO}E~)(_B#{@ !/ܮ:BӢ @ \ \u@  !@ f%^Onqg'_@ hk|oD JHHݫV^>qBt +@p#K@ fFn@ [ @ hf@ !@ fFn@ $R6Z|d PHpQz tjsp9Iw_Ǎp}Try}m1)5V @Pʕ!+"#HBRkSݲѝth8ʸkU1kSwxcz3\C FQ8_9ȴq^~n&(umd ZtE,; ֭ML젣g?>gq+Kp i=̓wsfpSF"$ҝI]/cTURXYi W%vVGp";dzE~^ YjbE5D%dY?^z)(I^R)E.oUz&ĝkت%Cq4)d IDAT˶tk~ sd6vc bڷ+3;JLG1.}Tt+E=e݌3 ]h›ABp &]-,*e8mG6KQ0e1ʗj$771H Wf-vʺ\5CWu=XU>aօyxW-z?2x}wRzqdeN^\FH2U.Z#Ƭr-ѱ_awr(~y,$v}TĽ Ԋ9,ߐxjM hP YüV%GrVK[HX t)+Kcgxpe1Ha;ғ%IU|:s端qW\ bZfջ%MvT2_ 7A$?%+(X^c?Avld[BDO=~W FQXuNodq@+˟C"\3ǯpmĹqg8|.b٘Ϫ.\`b|DO@P-X$դ:dBZh0i Ĵu B>wVqwQIU r)XbNޥhB490=v{*Lƒgq*sPj6yoƍbZ=7Q7V[)vkt|ۃYV!pB2#fo!1bn-4埨K^ǎ$j9 o=M$ѝ-!r V:Vvq0U0M~{ٸfȵ_r8qlO-ѷk]dMF=Cܹ6[Qzbb[m}Jet; ="uhN8< )<-8Lr^`gt3noIT.MXu@uAԄ\>rݭ;md%79`#7g.'p8^|2F΍N20J.r_jѾGW]jn]ag/4% &j>(6]{6Y[vmFO}졜Y՛{&w.Fjf7YyoR$mnjc1\ξ:@Ia{ {‡r?Xo*yJ{{)eKI&{{=r[prT]5 )4O:ǎUo&caRKn2jU)N-δDHH$)R!!݁ -^ظv&vE1D`K2iSHVHi8&Qxr ΁ap/z6>Z^tַZѫnaѩbzǴ˂(ʱ sb|d*X8Pf, dG(˚&+v36j4m$'ڸ7F,ik:Y׊dṛK$Plt'7ƹѭ?N0kF5L O TšwC-Ɇr̚By)2gx{Tv췜R+x] Ifi\xF{qĉMy̫hvki JS9R OGl W %IBFE)=w%72a<(DC!9_%3Pq9snwүMC|%@F#Wcyvil gW<~q{ .S.}Ot,y(o7fl gO͝0TU{ xr"zuCח⦬,{?gҊɽ>UAsPѼdѨnp7ZfeﶽU*Wz]gvl;{{\*-eOo$ܜ|(*[gޒ GC}[eN%IP8dfPM 4{kc %̢$VOd$@;׃PHSTdBӯ?d֡~Dԗ\K,Vh RICN&лxKL##C@ZYtɹc%ZJ-@)Eq #El+e|%P,Nr]G\H&? 8P%=e$Jt|qr@dz a[KGx{HJB^-}%jo\Í~U$C{VxLWE®ɣޖűM}/yyZ<ij[+9ID/o մ-DE/s)LfL/9Y3d8f6CIG'r߾XFW,ÇEm2 IexjN6$_KtG2˶;Dy_ZM#⥣r,-ѭp}FnfqҎ@mZjKZ>\L0޸oP%vZR"ԁlkYni7c-rDG)5OTq8X.;$ZL~n1֗"tL֗22랊e*TGc<ŃDnIs.DHǻ#R,X7d6*OnljzA vWbuDʶ,]f2T^m5[yTWKrbXYPV":>n:E:"i1۟5Gp`ǨK;v1q5n?x8֬hd/NN%& δUn8IT@ 07H#s gV {⡑P )T\T (T%<=_N?S}} rzt;dzbLH UKKFtV$/>OOMlk!;;ږ1\Iac~{[2pX|K>w3nz-bn(`WF[d}9R, âd(]N:Y\ rw&~u h$-czc2sXTƵIT(v13~~vzobqd58jf~df;#ˬlmSM[240P;vϼ_Vx2^9<77*H\rl`rN8ww)gcV,ݐɸJDTRK@)EØkC[O%8XuތXt˶;jݞzr*-)l^8vqD2VJRX`%y q LYUG^.** X%?.dA3mXw+w&j rCYҤ6noo5nüGK-5}V[-N$[/u}#L }& Ř{">yCtN{0 ln(]KwOv3|66F,h2m~KJRV,c3&~3yӹ&1(2h݊i R\CF4Yc,H¡4%\CP}R%dTR1Pr-,=EF,9gxYtAA7ù8#ټ) @̩ɤ+:'88YItpyd[O0o}?ڋtz5v&WtkD/ );͜Z3DtYF6%A)62lrh1= 2X*[312TBuXwxT'Xy3K2\~AczDމ<_zU\httj!se@a~ 9Wws[,Nz&УX@}_n7ESA,L G$Zj ͜8G.@6p7l" dTXeaߖ˛HtKnlw~ćխhF\wIxK!}9f-'-#F'\˯Xؾf LX>'%t+grQr P*0B9n$Zy0GFP\f.FQ%Ln I=CE$-\h@J=":Y[#FF:>[.Pl9pwN|=)o+X4 ar vﮩ|ݱ,4ydC $$ۮroL [10v~rs"x"VE_P(ȞZ´d9ɨ% Hwo#WO7FX}O_%~]:sirXXO-$ǃi$Np\2Zc)[~s&yp'#ªZTR173]L -RԔ38oڇ*ƌ/y6 jmcFMQ QiW!E15r WCޛX C(hjS}{%o?p'PGB!NGu݁xld4ͬ)22Cy:S9w.?Ds[nkbvg*8 jF8Y"9Da?ӼTXߺ?ŝܞ˴E%$g]:P':ȓ52S™gfeUs1K) h䦎|e0~ E$ æTTOXɗ.5  ګbۚmH: f~FtQs-ܥH Yٯ%JVe,a֑u̜vOlH5~"&=FE"m9ʓ%ʮ6䶀{D,؛J˅+Ct;yd^sQe4aUv(ަZ,\•~ZZ%p(d:I+vmR͆1y3ki#㦪g;HJa5՗:5xAP )MRɥtΎOoXlf )v ɸnj'C22o pxukeɲaes>g{ꈿW|eIcۯPԣxim[5mH^3V c{{APLĮp6fMh"l~+v?n>"vfu+v4Cy!T+Z?*n@ύB7w il%put WnnT,;.c"/}%!Jq^:iӸH wߖDCpݰNTZ1 4?ޜKd#]'Sw20A%>,vħ:⯋e N%nd~[+Oj&i ~fYs!ߔI2~_`봱hF-*CFѣW *BT@6]-/}_ڻ i<t+0i܃Z/~k($/e*6Jn@ͷ%m}[6k/7T\ڈpo@ \"g"2%Ag@p ,@ Qҷݤ9uHisݹ.^@pq D@ .jԜN8%dcg!\^%@ La#wkAK!@pq#@ 4zχ58jeml}—ss…@ .2.5]{\!@  @ @ A3#D@ @-@ 43Bt @ ͌}Eᤸh2ùh"B_1LSυ[<`#?3bdܽp+亴GS]\3 ?c-LŚE^?sOӒCZZ:9EV0z"$ov r8պf-deu]w^T[>gRϐ]`7_ZЊScEp ^ɼUm3셤g!0yP$@xL+}# rC-Lhrƛ`kLjBp lMۦ5&Af~"ض|ĤWb9OU#$p- 7_s+\lضWXV䏿z^hWeECpm;9R޻9[2PMH?L͙OլٓL"&&wǞMk;>"gM΋ҳp&y"Ykۍ&]MPUVdhyQ8skv%k"`hH=ڴ.ś]0{4Uvm`ä+hLt4bDuyeg㫨ƏfnMr;4JB ( <`}ֲv,+6:H $@(I{H!i7yz3g;;gNѧG?BǏqcش sM_T)GoyL$,2@lXe~3L> Fiv;+umh4gwRIQwp qYJQN. qBTsE,UYl#YUVtU8loEv'eDM6J7#ouWKzv  G/#0STX]r%4Q6\F/`-vمĆxaTۨ->¡cϭKX>7 t8l*h775SUty Krg[8>PiYhƀ߸ćJ '8t}}/f?9%:c"iw| J9c+*{ɱ\?aoxxd"8$C_յro&YvrǍL}%EGbƬr ,${Tke6$Caݫ{\{zd"Y0t*?"N%#hKcGa+>Y.f_R&*P:G&3*gq9;ӌYqIɓ=.a<;?㳌* ת2^S3^of9Ḵ!{{iZb {ƢJg0$V3;jZjkxE|Z~#xs̜13ǃ6UBޓ-njj>d$͝d'r+1Lõ9'JRfjJё\10n/uuQi(+=sϟˤ%m_dHzܺ>d,,P^e(bnzNuWRlVFܣpB+ NbF[:2 ]s%evܵn㦐}O@hz핔Uҳ\?3h=qk<3't;-9.J`*HHeF&2vrNJ7 -p"wx{׺[Ck_+cŒ)'=M%)nDK%)i ƅyb+;ȶ=Ǩw t}TZ<̱:Z=^Ac49`IOԐw8ܲZ,2?cb3 ^˾βרm XM1$7 {}1ʚPp! *VP oܼ<︃$d{KxEddc|TTT=67^{y2CAEmπp&%z\Ka!vP6rj` #e`/*y0哹?l/Hq`db+vk@$޽0T(?^@U-0p/-JC GK50/zϡZ,ZAQɬF)MU.F%2uz_c@-޲u''1%%wyoٺ-9I=b,~+8MIjc sy-`!:X%$ܣqH|tmYn?$]Ҡ8. T Nπ۝FfU&8t ˆ\d,$# D7Loî39>(u.h%=;9w(-,ł{xv>1Zrr[)*B mG9x-8{8jOaiǟ̴LZ35y1k?ؙmanJ[+SOX3ujn{d֤X{C-*reDK[ (u}9֏hQ8uJȮaw}<|#Τ6 <ύks@Pe:#ײ|;;xѱ|䮧5 S="eG$OwgyI}.'2S0uj{ o&NmsHej)aêTXN(H^v'<0={m8Cw.2aWO?r<Rll]4xt&^ļ.B׆aVVol9l|#+_%SW`Xv?m>a%o>͚G^ໂSy4x?v+s䁗([Fy98tv,ɸ\CHAEs__Ps~=õ!P=q)/#p&=ZOD&饝Wh /IRs""=V4yԎ]Io/:هG|\d ^./mԴ {鲞7]7Txxss8S2>>:|}=shL(!jz*c4] "ʨGю'ybUQh!}w..vɠBm4fJI`i׿'ck<26\,c,NS%oZּJq:J Ù{e\01/}}6+svա@m'ae6+78=Y<)/~~N o<$_.||Ž ߠ"#"ڦeְ{y TS p$E#y izKNJדOx<5E_y|{yR ,[QPOgm<|g^MA{6gN̗bl9K}Un& Y8*%ң8ⳐKeL]N>dG(ggQlV?4Z1Fu_´PG;<A[d5jG H&Bѵ h&3=ض.ykK}>RxOxk*H:ʣVLLHDk1U )PrPw?E67+?nO"518S?4 psQD PhV`GV{O%Gz#a?JMD S$'ɢ8=%rv)ŽiiZ4Ru!H2-QfmW=<$zhʬrr`*).u<ʩ8^3F?û59o4o5U-Wk\'&KȀ={Mןq*ӃXqΎDy|oXw/7@Vv6/Z8rHF,SQ4xYJr=y\MHaF^eӸ-NK`.nUǛs;9w8?yRlny\i:sc\}_]Nꊰsk-Ea_f^XWqlp>Zxna3ˉeO nt{f̻/.seҰ=>ȳza.L36i{/NO-9bw1RI):= 9|{!W qA#pL_1ٚU׿V;kI,vZbA|Nף]qӫՕsc66iRf0 uԠ}F\s6Z] SL~J5֯˯b{?pѝiӂ޳\?Т룦Nea3+Y{/'?t 2))Dj@36o=H S^r8nԫnrD^3bYmmg('SZhh":?2ijrZ6}7uάsRKKed&$WRRGxqO^็vj;g,vJz;8g!m{peja͔*etOcb6m>F`igM5u)=}%m2W^˖^t? SAbawﶃJ63Sw'\ ZPIە2lYٻ=fUubvXGr{ aΊ!BCyUCLEFnm1(rJ>ɞ?J21;d36`:WLnT vkmfj*n trfIAtfM S'WwMH/І.eWQa+Z‚G\T\ێ$y1~B'mTs3՚ 49[esvCOpDkU\Ff$FѺ]ǰL@?]lvmE^H@tZTd)NiLIt;Tp0  ٖCHSIΡcX/ 轉>Lf!<_7?wlMv8"bMw muz<1v :5% r%.6{Kֱ?'iNo{PeSLODΩh. j|IjppN5 b9LĂ[݂^ѵf6r͑_r{|UĦx9^=3(I@F>PA]~udɲToCECXL&.q n"'"^2J>,wz19HySikh &X2 9@N)K"%_A֒uſ8( eEgz(f^9XCeL^TRP܊ !lLG\F~Vb|E~v#0+Hٰ6RU|cy1뜥29I;>. l˽tFה$lbk~v4s4g! SIy24uide0;VUƔ2I $/J5Ui'>zu|PQ1WUҠ>CIOK¯Ïj6={hbL\"SR2s,fx;Ι!Mޡe: ܭ^^8}"$:˯5ޒu k#Tz52WSYZ}jjjUtTj*:9x}qUP 6l>M㼋R"DF~n'RL=Hhnnv]ss3z1qx(5GȮhq9SЅ3v)ΑFClN;pH:og-`z!>tnePG{ T^NSǙQԨ"\eoTbw˼eK9䙋9DlfcVө X.ɟ3$#F0Tfs.L${ tHJ={ҎḃL9Ssb*p؎E2'o0: ٲT-9. sj99D`WYD!a_Z5Gs>ֲj`l!RKQI3*CoCr'$2ZT?}+2'CRi*.F ۜ\Ԗ}*mOeTF@ʸ 6Q| 8*8?$4YillE,M M(cǘpF>L6 *4G!nd|Tɤid ; T(HN5:&wh5=/餥m﷓}{OkLg9K{zo?:I۲QjǛ5<{>|?~~~u5k>iwR2b`cn5z5m'Cjs1}^ (t-o7u G7m&_`r3wLv瀥#ٲT:ꇭf *=+ նZ矮2kLOzFf32ؿ?9M  x4*)m+Vp$!r]za&ضs|nf=ÀjbB7tf +K.zև^.2Ҿ}vLQsi}J=f;Ȯ-}n'/!o$nHTW:t6S^ވo?+驌Հ֕`f S C 5^¾ *'.δ=]&-6JKʇylxӓ >QVgТ6^-8h&\̟rRw‡^3of٥Ï>A;8٠wHHQZRqi ]`pMbϫh;euflsfctj;7z Pt ,^=WmTUV ߁Ϝ-K?/SYRZD~a jW?^b;rFٓ1Pjs2s)h踑-5rm IDATp^+55#mhMJR+zǯ=*P0\ZK)(~ ao;ri]n%l^8~Wg?{r*{)6*+Ec2ed&b.8J{Zw# ~DE:n&r4fhP9KOq;O2v?~OICq>8U0nFj# M4("SBU.5s6=s5vqSFI7 3mt.jd#e'^gwo4S&N~٤Z6W-;YH4 e;:-٤eJ:pyهĐ;#A;G޼,`oR3CG&Ey&b,j&3S)PV# 21c/ggR$& Y#S(ؓNhL&/Zĭ+o;֕7sѢ2nظ4?Vɓظ1# N296-*MGcK>[@2>CX+j,}ݢ(?nb_&o灡W4Ηh7[r%9';V%6- 7c~SZA2u혉Lנ㖣wHX*HߒA]%*N~2LGVWdI&Oa0i IFh=%ej˜9m0^e"^W Oxt[œ%O={nV_[\Sj6vvk|5Y&yqݿQ:l}h@Ș .|''YޤϨY0 0(aW+̱Or}:KC_h;kfw}^q^DgE]{mvL9e`0\.aBjͪSֳadqE I9(gg@9U,?vXᕏrQFC1".n"7C-9|ie'g[k{dSoeJGv<7 03 SM = 9[BM=ѝ4MGrפWw c Q^^]TL*2ΕKrhPk ;>y},{WEOFJ8w2 =SR_TBKc# nn#ƗK{.Q“_J܃IHI!ì{'EsEI{pM#1ĭc FQdOB>r\T;VKGFG/pdr#\9_KiH u_ΆrB>2,saǶ4s ـofN!BeK++Ip#]4ᵜHOrcm97x\ :ӓ]e$uLa:bi ^MXFnOqvէx3Yn%wwHĿxLF7z?k~oٵ]:IofVL7H7+g_Qpb;0Dpïg>_y#iN ALz d|!vJy _ ?]ޏ_ƃ<}>ɲD?]v΋ۣ mN W#ɡ[\8+3s7 :[bpˤR\_OcF<6YGe&,1axnۛ+((kWo0;Ul4Q\݌Mhx0^CXun|une\zZC⍷na,%Ц5NK9;Wֆkipܜ{?~trF}pj7/?ܼZȈwZdԒ1`ٖ} 8I'X37pbg{i1E454nDE;>=(Ao +U\,S;*c3u/gR1qۇ]  0Qiz?ʤUå. HDAAHŻ۶̪g)~vD-  Z{{v %W^$fsʺ])`3xLWo.YwbG{[A8)BuU9Q1ÞvN7pk\MTW[A^ZhνN sx 58 Ɠke`jƇnmYddQe^yIπo&w޺#oRk@CudϦ$}N56p\扭 %'%CAEmπp&%9v#ōz""qNs4je38VFbkL,)1uX'8x&Ս$c:m|vC~"ܵՕ6|`;=}Ōlh?񇳶[7Ь6Ԁi舠[A +yC\m>:zƍۈ ]$${X}NoɔuTXx࡛tVs#&Ś<~ߛjQ/#r^ZR†UO3OO~$/oKXoXWAGJk[%ǟ}5K+]O=•ѧ?}JzN6 ϸ?65opϣ0TU)QFloZ[ #2M`V݂ H; ElޒqwdC5?lL0 sץ(X|13.y>3ϼr7;I`i׿'ck<2ְ{y TS p$E#y iz5:M;lff篧t?!;vg>%_6ÒŪ\+\%iyfС׊ZnwDHD԰rwDn3ikm:"ANԉ|XL45U4c욹|&[μ3oF )#5pr}^![y;3wN<ƕQ7央kLnN)ȺxaM$*$Q4xYJr=َ>nB 3bp}:.m6A{|r;?9Q'kSĊvv/'2XT근ˇyp<ЎǭS7+n [jU$1 Ыӽm]\Qj wmFb n|t zVҞE/sӰ2#0&jjT밃6 mZ.8BiQhBe`(Go}`v%&ug}t&ƌd͜ɬIarp& :k)=6+A? V (0LrYط~3>y+&v*r9Ǭ啜b6m>ʸyJA,.w;`2s%% @{wEp%&nsuUQNtzJȨvA ¨bmmLFpޮWۧ­&|<3=s\2uąe_g:oK#mʚ(Eq.~5 ,v%,I3W*8SXzLXp+[˦LIt;=VʩGA&tx#3>9ң4#6A20)Aq;.0w7W;aFECT8zLDh/C BgmUmh#VӭjQinIP4F݂ #l?>ͥZBC}c ͭ}DKb\ZPOL8mfg-:jJ Ȯ!L[rӖ\Jy8y4Φb?7=85O PV-^>4G2=RWSّ__/04 SRSMF&Qܱc'C چrrP!I€٬VzC SڲyI^\‰^݅ڷZXWjzz'bxF~{Vm6#H`x:h 2~CbiC۱: ܵI €)٘eIObrB1F\+ڋٳ @2bPikmcX*p%#Ǝͭ^^[Ph0:Jz ?I;K3{B~:wH,kikii2ۚ[hms8zt 6gV=gs7Xˆ>fc~tr!Niq4TmHHQZRnX6J+Q?Բ7~^2P]ZG݂ BlMW[^NF|ox~p &.6Aº\uR>>70b_e[;KgwkwEL4J؏K_5vk g)FC%L4J>J8ṟOr}:KLCV1ț{4QeЄGIG#k%Օht:㈼XH݉ `%囸_]nQv X:?Rw:FZ&^Wu32uO1'{LюUܳ=$£)$nӥ_[}7W2\}Ǎ;kKy^=s'7_1jMڝhm\3zXYWs/ZIZƽ ?-L$z=&Oops@ӍH-wsS^O݂ V_AYO$Yśq$NͅK'u(nݝŏy||Y)겈$˼?W]Doۢ[kv4 %W\xcգTO'x#]%k]_7̊) )xh¹g0u-_-ΊWrM z#?];6T_Cyi1A!Ò~yi *9ٴd8֖f4o?(H~7EEjIX-}.Aʌ9N [IHL9ӻ!tZi,d0g|XVDqu367xz3TVHaeوϘ0B+vD܂Mб-?p?ōo#1spj*x;e%ET1>i .]KK3Y1y,t[-8NwLg-jAS@81&=()-(5͑R_@q+Sc֞r$6D[i~,#~b2Fsh^>.^mi#8,jwwaZA3~#'xcOc~B1MC*,,c0˗t_zf`B5A֖ٗf tDM  _no 䡛Ȟ}S"4}%mH^v˹#2mY-2nnnj#OXdt3J6 OR[UHiTm1iinD5DDatAAو7xm9T IM+a 慊BΠX`2h4Z=mM #^Zm47S[]A]M5^>h]Lt:FUUE۱+vf sH~A8iqf 00?xi+l4WUPh ? 7 IDATܩTo5vuuEhtAktH~QlV+jo[jm663 WWWVZ$FFPAA$-n~c;!tmjݝVڰZ(r2F.#1.pA  pFu̪"I,j1L@{.6۝D-  ]j8l\f"AAFul A  0jAvwbrAAAa"AA&nAAAa"AA&nAAAa"AA&nAAAa"AA&&AAF6*j[$;>^.hV_CY@:O|S[AA9mb +*wP-棬h N3jVmu]Dv "AAF*뷓Uee-;k{IH}tUQ`Co;#C#nAAa)lǨW;<ױp M@RiʶmyhorJS6_pDGJAAa4>.%A ҘeWm8ު'llM?ukv"AAG<|&kY4pp.ͮ;g6\IEN~kEœg,A  0d܆eXSwjBD6S~V-5z˳,Fs۟@guosm<6qKnoUc❇r)>ψv2JTx9ctBO'Lo&?oH&_+t).I\;bTU4UE)Sy cёWl{>ފ5ZP"U_r*HPL~z9v9uyZyD%zģLuLZɃ_O;|5U\X V8w#3l K b !pp_rop}Z2ggE][-Sv Єt< sQ@$9še_yg|K.rkU·~1ŏޓu0d?*?t{}:O}Nq+3w>\?%usD5 P-:BXi)[C@ɼҵ6YfzJ^yp?~6uzvGy}۾2Nn\.$\/]&q{6~n$$hZϪç+wLğ-\9IAnnq ȵhu.qS6PL^xz AB4V]lmCWteb~wq b$?I_tkbԚm?8K^:YKDK+ӭ(.C+?5 &\SHdSI߻?gRQ3[HZVśtҁ9귑k]Q{M"1t])Y~>zq+;uϴ$&%ֱAܶGLJoֺmr]KJF<>_v/ t[t5eh=)b)~7p_:t[!-'N((j7<dӬs@+5Zbz#<6AcB]囩ܜqKp]=]sO/uMTx;u0Igܨ`˧{o͔Nhe\l(,oT{B;=M^ 52)E.waPܜ-[8c&UtNX^0ӾݺtG2;tmObWxcVg-*^a>k>?>ů׸T}^Y{<,~dGy-w h̾C&=NX0_<&b U{Q&`kSy| N}q*u]Qo;!gc^ځ4Z/#uDn!hic9,;tG[=>?gniȻn`LYX_E?A ޾ %b4aT@Ϭb"&=ї^&og:e:^OK@yd[0覫9 o'y{X\oO{Pac|"ӜsaQrX+~яe ^yWv!.KpFU1T6+LI[C<\4񿖱 #զŠqbE})ȩ3o|3Y<0 ݯ-9]D.G.˯q <,3OPMmq{¤0?~]>,@KU:gUM;u!mirj~Wg7Һ$p=.g͌g^qC˸0[Z"y?=WՍ")[!+C+e?=?[V2x0Iώq9?s:Wlb:|KRTu`! zN7&goDq#?w%R2ZY*25# 7?njQjz> q!3 *F06:q`:_<˫?xhg^\zMC*b\+3lb^}4rq,Mĉ7j%z6s1tQi鬻vAmF˘ӟll[?R -׸iߩ'}zq?s ܾ6Ry1ڱ%*ޜ1MmQV3Plv7_ǩ,/ˠNLǷ߬j=+׷|w^\]r%®>|/>\BHmiGGd+*!QR |l *9]yhc^5<rWp >s[7)ϕmDd]Ɩ tN'o\$ u`4 ؼ47.+ kܕC"P|,[G^ɝ ŧ' hl˘0`8\iGx,tH:6+-WEPLŻ-]ڹ)菿6+`5N0l9ʎ/^xq7Z"U>z:l1s躥ˆS ;رt5#XUDTbGݬ'gJBTesUӒ9EdOsqh >pBɤlܸ4ܓ)\Uh)ړJL玄; j4; y~ro@,V6R:hѫshעnZ5mQqN =_OVt Ķmq|wq)[+pa"2:dNᠣ5 J!Ҝ / / 4+vmv6n\ˊuIKO6|BevhOs3$::hPn?W t8e͏1a3L߲=OoѬ}Wz~ӯczbf,Z w45A#/=\o:c;Э(sb80]9u rtPsk* {Q[]gh F ANJE{%='?H \%8$4lrut=.MVvQ pD #ç+7SوI-Mj&M/"bշ],CsQӹtvrpTpu@]QN /ǜߗbAv]53] 1 OOd5 NKXF ^ W?L_H[;EpXC. /fl>Fn/W˛K0I&( ф/ؖ-w;/Gq.dLEatut&렽*F/uհJp^hcɱuq9;6GI]LϚ\NI_D5I-M!=nOgQ3 D;+BDyrLoڵ+cJv]kfcET^LǙl۰U9ø$E:noT%\k?ԇKqX3z:Tz ^^E ;JU iݛkE1c.m6tܕױMs͜g7T{ Yl6l|XGٻz.ni8BO[vgуlTs70gѱCj`Fu ,YnŸ+ qѮFsI[Lz#בeL3"X??T\|%HG'I)')h("H8+A?+EI#8!40rj0aA*"=MZ1+]F~N _躎hv^k$BFɖW>wVznsEȢ?J0ݺa@#ij@Į5l>W ǖʃXݼte(hded(%V g`eߛܲ'uL #Cgo_KiKYn Kkň{GDJã*VN [fHٰN}7k+&vp{]];IguJ͆mnZvrPiѶUѱt=-F6wYjYX(FEPG_ ].Iٱ jBV-]ȆUسc3I\SxS"AB4J_wq`ONl)k>V騡_̓5^סy}^ ʖ= /=/ZybCUeg~VjlΨU%߂xۀ Pu[ּ3|~?Ze9ݮ=@Wo݂d:+#kWLDT f+: zDDTsSؼvYMn[rN:ï/ت<[@BH|T\٤=@ €RSsQy侵L~u%>~[6\ 5H?K6rDSKƭԨQL9{N D`(<KX}ĉ)Jn^J@ U֦0ϒA$^=RU>\ϟ-ENtsF Cg*.3eHI>僱*בnޝĖhjwyl*]F3K4~LoXoP`&3jzkg%I{37CT֓ivkUj Mro䶮+y{af=~ Gs3]d=}`ض9qBGw9S;=:y25cyr8~tq! /VdHcuEf<I{K-qc}Z]QT,E"Z ʷv{&a>|kckTWJds0qh?E`zjב0Eitwߣ$Ζn!h*~-h T|Bbi[ +-q|#o]5/:^WpY+ϳ\Ո2j$w*]+FU_ZlRjJR6Y Jz34M@u맓;~Dm6[/] i%N'wѹDžgM&:u͎vA Fc K)BoPm巎 VWUz6C u'|X7 Qlb°IÓRJF32ZiŃp>df5+e[!-m LrV,AË6abO\W|ŀwc<@Smk|{ExhpP@JdXM ZvED5%=%$B!j@μogqХDsM-oBZ33a Qz8k%B!i%xc7TIn!Bq֔b-, +#Ԍ&54 B!Y( _ ѱ-ٳc f#q6Ŷkii%%wB!9dDZUUC `u8t֏`ǖur*n!B!AaXٴf9'3OT}23Mkc ("UUu 2{B!8JdUU DQf {wll!"*P>جdeZHTLK| 8pK-B!D%&0摞C۬mLHd F(7H-B!HI( fo@v\.P///f||gM! B!D*j*(%˔oJ7H-B!XI:Tn6%t !BFtP]6nvit !BFeA#X (ὺu!B:vj;$y9ٴ ]ױ;`5t'? w &qff ~oUxK-BQt]'+#q8nt( QHOI?#2eB!DuMӰ`X$>躎[a>UJ-BQJVi.ZL3''EQp xKzB!D}Ҵc|s!AB!D=^"BQ$Qs-#B!Jʞˉ`F6z!B:v4t#DTqcgbUDƵ TWuӖh6>΍$ڕ!Bz7vr/<;U.< ^&* ҵ FhDUo;5Hb!BsUu+j(V#dTTUTx 7= 462wBqңw߶i]=D1^Uէ ]rZ)t6t%ʓ[!Q k !Oz^2I`ML JʮlڟLf&(-=!Lޔ 5 m!2 pEѩk{Y*n e0)znyLӞ!CgI-牒ZF8*K@?㷊wWھKx]&-Y|%"p V#oL~a ފb <~fgo`ch률Zbzc<~e;,fL GxD:G3w]<^| v訑Wr`{9*[!l !jC1ycR KEt is>)kxﵹ\8g셪3{ >1<4mUd?ećouIt%1ũ|!SLy  vĿ`[X޳AՒ (=xxVa;p0#G"Ȓ@YZDn!8OH SW5/JѴw Y~f} MסkO.cCsH޷5UE7rgK{BA֛A|cW3]},yhsLMiG瞽sSY 7pw7bcKX.: ݉V_HH-BQ2Ղo%W#]lm~F:ʩ%w&e^ˉҮ+lK#9ͅ>5a7!iqZ᲎x}k6ѷ+F@i5>NVf1 ˥Сw'|~Jj:a%1{R*FQ@-ik(jP) B!V %?/ݞέp3/l6lbJg{|9=mCPrqN] S@#y1:hpt ӉSӰ^x+Phcc{zv4Qͧ B!S-?zL&En=.V\@& D%-/j( 7Rꠃc}:?,( Ϝl=ʿ^uBqzxwݜ w!* غ ԈcTҬ:ʮEf 4Lak7alq<&̳0+Bm5t5p:6?m ?g_:GuY][!>5ۉwygf~m--~_QmБrǍ#hkbwY_u/_<Nzbќ/K6s\CTϫ7JMO\Û:ЮlEԆvtG޺҅wmHϾFڸ/:tB4]4 iծCי>JAV1yyc289||hb"|Q]v6;em>ZPt;//&UYY}k\" ハ'9t,џlج2Q1z`rfqp 7 ՊSS0xbrpMPGɂ 4y5s q:c0PU-8[FB!T~^NvV9qX8PU躆=?Bs +[[^6=Y]ל s@L| :Gɶ5|VDk6 N]ly>F'; sY]_t !BԣbȭnP]m:r(6Cpu;w#AB!D}c ~`l$d@!B!Ꙍt !B1w7՝L=&7gu9B!p\y\@td`M..4f[!(B@pY'д1O84M# C8U[!)X|'qV2q ]5!IvV&;bWCqܩqNոB!盒;4,/'R8zx?B A5'&5 AA#X .P$'ݟ)Ԉ\Iriױcwn: q) ~AL͘ߪ] Iw? !DSQ t:$Tt( QHOI?NG?F='!h 5eh"8o躎[a~=W8_GBʠ B qQW6'q$MIҴzWҟyFl!tG-8OIB!BM_$D Bjr"džn!8דxKҨIFc+猚 o1$w,} oFvoiz-l|zc<Į=9v|d! ,m&\1ɾky1'=}d:%1A8چ;!9/r]Y;L}3 rf"\u751vuʙN+8 ktt ,hψzy>uaBW EQTf;hqcCœ\_q+jJmCBrſd] ]1Ѿ}Y5Y~I[~m' *A毿fsz { rWUYw?m:>{rt&Ь;=s3arxc^E4cY3yN0dlX%#MmHBpױyLg}O?ȸ\((/k ~>~:MN4M/e~*^i~<YeM4j8_Y>Lˇo,eE=PSiB4s7CPKc޻3XFwdHgWeb72σdwno(ZUN{SнC Żܶr9?ݛ8ŠILE7вefE'?G3v:щr ]Cb3Wm7w@C^^v!){Qt@dJ3۶%r$=M`x,:'Fa>:DRf8:umO3KeGI$&%618Sw}dgȐY3q||墡Gg-q!\=™my$B?-;qAh*k5i5j[NBԞ9Mcu`>7䣫A <6b ݋C^qp˫'Dפ5˄VE} &O[u?c"v|G->{ *1^˻;T~Bt.W9qU}fT]Nvg<ŞĒOHy>P_>' $ƫ*KL+гko0s2 %w>WRI9֙;u ήSlb/>; t+i9=AMcdO}66ŏA#`62j` ~>΂n)EU>`1=ST^cNfH5nGZ;B4sOƑ5k95|Ar>Xe9f5;=aE-+yZZW\F\ O]/1t} T=9vfsYts?ĤlX ݽŲ?q]72G,ApxBf~M?M|p_ҒT>ܐ)?w< Ze;A?0-,zY#Sx+c?8Ѝ#[dI .8ٶ.`j@.:td=*i=̜6}#~5}p%-xpbO뒿:ŚGw5ʜk'h輪y}pծ3z֡e: JNޥ*0:">\!B."}Lc e=ѱU_l[gޟihxn~MhB4tvO;onhG~¬.vnH}tY OQ+ܳ7q۹n*KZ=֮[¢wGG]8N:Nc߿.#]Sv ݽ.a޲ ^aJĎ1kU\h:(=z3/wL}s S4^q[q~'N!9^RNINd9Q K~Urer"Ӆi(UV/;ZI-uuz ePQGx)}7vga=, ɏֵ;o eKٲd9ɚWNp~ī\}+3];  j}6lbKΥСw'|~JjYa<-+: (e|N]$f$33`Lfͪ-}?C3dv0rtGfsx8Zi@ aͣvY9r@1\qofD}`-Gw+w;!hZq&Mj8cӁ;b?潧igGI1yỸ_]:TKeOb6*&FH}ɩjˏ-ةVrر )нVi;؞MO^z Mf͢ؼ~ _(nsWZ460%\ȸ3ߍz طW(zUcЃh6֩X~'k%[>X| kt )ŌŬY7]|5DsX)ty =܎A;[le,_}1Sz,Ndh`U AZV'5.ce_uvJ"#džj+9PsכZ?))g}yt:֭ܽ➻&2L|>c*^}-܏oLt:gl??,NbCo|ܬF ~QIE!WPW}R]]-\wB퍔yVhI'SYrmIH@1E<k6\ܢCGsSy}I~F] .&i f;8ٛxi| a$@0|0()+jnuTb45{|W| kGv{vl%?/Nma8>%O>H)ذGiVٓʳ|!qݴ/ j*nSWqxVH;Bxtd;5ЦsLžuٗri&cv u0wiLJ}.Z4+j:iNp^q\?IsӏqK vPo| ZmhXEӽ)f3ŸL IDAT֯b>j( 7f݈X .z˕1aP3ك+Wr~SN]ٰf.ѣF1oGbdi  2S)`{UyzYĩܴSkٰc(f6q[M~'N!NHnG }se5lǟ36Qe)^t6Xh]˅F9ruN]lݵCGH L0u ms&42@\i.RP0GFQ`k7alt2'_yZN!33J &##%PFs_pw t;eA,X‘KǗ2'T!̴h(BUέ}v}gg#ɪYIGy}Mw<㞼 ̧36#ZznC/UޙفfPqS  P<-an^8=~~nӗ>fE6XPzX~a8(kc/Y!!*aDG=[vRXzYg+?xr TAdɱ\\:JN `…nyݯöyťxy0f4h`3e,Gv~]#k;D1dZEOANQz;^NC& ^`Ɔc,d*˿ &]k`rzx{gc !]iLlQ{k .ISGޟ& v-g M;3)]S2DemJ2?"y="!n]ɓtV7G+93=ѭӾE8f͚KѽܛJ ԠnL6>>j ݽ)oo7ɔM#)_g&7-cڣzv_HHe*63i 83 Y0,/sqJֹo:jS;oo>/Kֹu6f8ovtWΒ ŧ L]?˻kb!6uqk_;vJZ@Xv& ѭ҇ [;uj+4^uR_7P@bZ**k i1iu[TxI!$}݋$`Hc'LNw,< j-|nr۹wټeA]©\M(DZk;NDd<햁Y=Y{^_~@!v|í 9.:d5 W~!m<^kofcD'yops\VU~ҟy߱ohl,m["S cbYyj%+7b7ؽ,;µdՏ5 &?r>ֳ'Nf_k_9Zke<釖fu5^&8XSv'VG_77a\\75|;c! 9H95\7:$O(ñ nI(&I|Eq];y*PpGZfN8D0s8':MzX*'M.vI%!?h6mzOn\;BC4jxn"-%tL S4M$H@;'0R= \ F|xw_tpӷmn8;O`OβnK}NΜ餡;︃%VCiDOO++ql]TR #X:zxkNIL7e,BnyD++O""n^`.ʢ;V]]yɥ,,gt~Ob9 Gݲؖ1fܶ_s,x? xw'}=Xmmt:&p1ވrX)ݸNE̢(twgUDg70sٲg_ tsn5Ćb.[1̈esMo+\iGq^cKāU3WyJc1ܕ}DDCkKدOijhls+Os7J)%'  Lجei6v젗 j>ǭzSDDDDgDEwqLu3=v_oRW |dT$-eADIV3ݶaXR9 a/IXIDeݑ2Is8ʓ k)wj'QQDDDJڂnS-""""R`u_W3)qV`Tk[DDDDfG3[=ʪ_DDDDru &|IENDB`Solaar-1.1.14/docs/screenshots/Solaar-main-window-multiple.png000066400000000000000000004164521473525213700243430ustar00rootroot00000000000000PNG  IHDR|MsBIT|d IDATxy\u}{Ιɞ M eQ*.պUWQ[mVh\ =̜q=3wޛ&|G3s~N]Mz˧lB!B!B<}q}j}.8TuB!B!,HrLV{2-{^B!B!B<#̼;pMAOo@?=&C!B!B}6T]u&B!B!Bz˧tąs"B!B!8zde§.dߩ=\OcMB!B!sEaP?47iB!B!xHX{ϯ ="B!B!CAaTv(>5B!B!Ϻ]ƚ!B!B {筞lr-NPkE5p wB!B!80GrfyGDgűZ* e׶-Gp𠷣V69avnx1[y_hc`M´y׎u&.7[J`o#lP҂\B!B!}& R͂Ьq_cXӬƲ5 -cZS֌6kSe/%rcoꬴC8W;?GIP =hVl=׫&A)@P)m$CA! ׼o?j LE=4CØj7jƢG1]iP.!-^\cZ2T!B!UƴSk}7U٠.[Yuhu}9"@X5O_[sPѤiۿveSS |uGXEltߙ(ˬ1@R1ty&m?r6 ՆD NhRGc  ;lB!B!АIRG-&;V9YןcKh#i&kG]37AӬ0`yC;lb\$n\7Y&>{1v43y8ªQ&-6o'%_Yzt޼2N%\ -mzN+X'W7_B!%43:lL{ӺB!2|Ȧi ձ͎tFʂ&ێ4!ju';-SA4a;>ʆ\q{z~شmM9I;MxJ/h"ꮶJy݉訂2S??O\Yrg2 >E(,:NxnҩǍP&S v{B!āqE3ݣQ00RbÇWjox>RS1:=C4B[E^Y*”Q9ZXvOՅ1׍MVX!NekƸvdʊJrCپ[6Au;&n][dk{6K] hCZ~ӏKḡ=Tbak-;Ƿ訂ub7xVoz*X]we ^V2_7kёm7^qB!?cq6 M:g-klfa*(b քBQ/b MD!:~AQP;.c[1_Д4Ѭl@4kWC.]ouSiCTC.Piӆ3s^P."ң9G\nK"DؤYnB(:Z!\[Frқ WJoP:glc)њ7/U=æ HzR١wh& !bJt5LT S=yx8qK@gVDHõ)*-?BiX,x ixquFn~̓>P,pl^.8y =h$˘^U#Ow;B\G!qbf(NzUiU[fVLIJi5c%n%G!@ݻ}4M-X03@)F*!r9  G1'diwVV=h@&uA|k!LK } LO7&06Lל=0GI\܆ơ١֪(kYT ! by5TpOpc'Q6ʹ oQ|G팟C!Iç/U{'AYYr~005N1YOc8 cˣM˯]A2熗/ Fb:/T<)iwa0gN>XC[F|u[ctbL7A% Xt&p41b OQf]f5P8TBH9jZG݅V(xNuUfegiS1 l4iҡ ?S~(]Кp]pGkp7IK'X5MPP!b.E=p}\tMP淯d,;y5g|;dM!"IzqX ]05nw\7YXwLy15 =ֲIlMueǘ@jYcP\wPB3 ذ}$ֹ~2HVDNm[5K{F= Q0D&<! NCO.SwN'Ze9]0QD%)"MA?nA5qiq&cCAQBpfӷx$TXA1\1pv?I4m[Т" =>PP!bzu좷tn}~®~ں[jKu8},(BPuWw<I: t[I {XؙX*2[vۿ4A=,UsOs56׿ zyKc粤qGfB!>¶fOtl1Gp 5ʣiոb5'j:y^%\EckgZ.qZ'̔c_%s nhT?$y)3Wpbj=*1Pʑ_IB]+:}I1y܆pM5$jV)C8O@'=УDKuAXO3^Zf%wx5džUGgG9͕X}ڪ##,+zrh`M!8Pqjv]LMTmGa-'(׮1Q;KQ" *Sq*PMl]xj:֎]~ 6.((yaI=4q-x1ۇ+ 3v*GզxAdqx'K=ҏ/(۹( 4/EJ ^Dt`UY(׿.c.eh&UX?9iF#;76qCaٜ}*?,O~>_͏aWVD%\f<޵ e|:P1: Bai3Vϛ?gG^vgyv5Ω7VTL 892)<ǣ`yf K5qH5)t5C0A;J M ̓>QlXS0DhUK)2l1l & ksYVċ4Lҗ.wt!JVQL4PgM[V tS>y[oh͍9 Lc!Wq$CABim~nZ?.sQn/oW׷w{ ^B>R ݏ^Ǖٸn W <\~x+_+%W M:K^poaSw;h[?̻|=WJVsўKV`f.[hBs Pv-!1ZݖzZf|;8Daj+o*=-0 l `2fvjp ȹF9tTTBvT|KeqVlg3 hf/PƠyD+N*5˘}23m2M|ԟgFG>q=**+a+CABfr7o崿9Y/:[s#۸.OśZHlfNçNZgΡۗ֓5S뵦r9r[{忯~J,Y|rYG&g&,G>~3?}BBv((0~QWc8 d[zlc3銣ٕGkm@XTһ,"(-ׯ{L˹69X2(I< L&t(9\J5#+*alv_ Wr ft$aTbBbL{!WhЧ`) q8:d׮p 9HM&hcuny9wxAoD'Ճg'q~yve"aəe 2T!8mOםƻVuRPEy͢ż[]c['C}|M>Ԙ |Au gG5hW9dT"(btLk,o(mڵ=buLHLcLr/[dZvz_zHG/Ae2pvc:k~ͺkdi뎑B!sz>a=ìys8r|N9|B\l^?=_ /Y8Yv MR{j8tE*rآbGdnGvO:Wk\Sq:˲!JB!Zϭ4JӀ pˉC*9>N SV{ B 3Ki=Q5l|?ǚ$mpfj ]af.,>n& 1z@kx9ag`]= [l{frqܪs5|Q CAB6* Wk3Pcż7ʣ)NuWW-aen_,*:-°,K!m[QjӸ]i1åmc--^CwRnh3\-7N{UgsMԎvFmOS "(b"_ӣ#7a?f@Pu׿ IDATAmA>:5-B(0Eɣ6PK{D] _i\E1#6J%6R 3rmp-{NgNoxkGQD{67Nz)t䚸ǚ0Ab$T^ 8$g7VklqǨ(@'+Sc: ZPP!P\EC*FEWW`90JL73qh5EBw'`;F6tr+9|X(=r;vZtOMgYB!8Ī|u~q<ۋyż=+P]&6W3Å8bǐaqT rtuhFM] eՐ$n5:l+}ip4kG12BC;Tu7j%QUDuN :B*JFB6  &A@{Gk9((蘑:owVqj5TCU6n+خy2@fl|ȵL|r j4VЄؐRcP&B!ā\q,+ͥoc_b>Ebέ:1eZO8|JrdeMG'^7Jd^wur`ãnc)z_ N_YB!8 q-kkqkQ]*TKyJ5?&ǕO4R\u~|k-BZ-٤]uAR3R]!(FRcrm\3VMe\;W5e Rwԧoħ2 նV.3& yjom#!JRH߈QT^MދO7oG׮f|ȥTˡZ;v.8Oބqd%^qZAjG'A>+CABS|`;寏XESլ;y񱳒I?:5S[λ c1jmxd+q.i]oo6o?g1o81e !˛dzZ@/օ:iORg`u>m7s"%?dHȖ2jcafh5o^`M4p<@929  9\\P[\qO͏2nk|*arlzO1a:.Vxp@]zD~UCe[{|O?j(GEv {;(뼵cاT!0zܵ[?$1NLa(K2īqf/{M>qu$h]|c֟9U|=jƏ.3;`OXdPn|t%BC: Է ' K'n3qT{ITQDWa>Ic*;}ʣ]5 ew2SǗa]m_m>SBLaX>ǖ lպe%]~cKn*P8ǫLJ/UmKcr.-{V!谄a?W+n6f cQw21f1u' .&.YTwXw^ϫhSKeB1M,^֫qr{=yh=Wܳ۷ yȧY\ٽ=btJ`Nf7y!涫nG'WP^{:W%!n N5k?=x[ΩvexxGq>|cyK+nݩ/4ʝNjVݕW{YB!^45Պ#|co9d0}ݝDuGF}Q5 f0GqLD83t5- eOUBCOz 'a`f,:@:oD2mkT 6d4WTu~&m30 u[v{D*%ߓ5nrHkp77vP[v VT[N`>oxe2[-^&Z&B!4ݟy"q55kJ}|po١'K2ݧpf;+N|>#ãvzsI=[ゟ:-!>~Q|t2ݣ1"ݞBوow߰%3yߢnƫN5^G4eCB1 Ry\3BLT`rN7>Noph4oGkU+ 0Pߨvh$[SWn횙km:uB_n{W9T߷ro7 աQw&M݊ uT}mY y4@6H<{fijf(FԎWƠPxv&jYzխo?B!'nkbKcք߼ojZۑMO\ݸ eJɲ)7/_zMSZ [~3yb3oF=.m o_^Ͽ<2oA9ybV %BC+ M\7<q\۟>w.Dp&9^RN:08reaƘmZPng&[V@M[S yٲ[QR4wu5 X\d5j4^!TQ4hfSU/e =ıcA "oLች0xl<1ڊOmQ%nހYUP>%c]* ԰]'NW2t3juq6X!I̚ò6z.Daޡ6bGe?8ym\*i{?OG2RyE_8 عvμPdi,K!'!Ie'jrᔾnx\8ZsLϱuҰ=vNuۜ=7┇zֵ)B+]hQ֬gI϶f=޲XgZz53+gv{?ԹynBkc"voNG^f7nwFP8:,H-^`Na3?|9ᒀ}bۋ7B0Ryl>_ jR 2BuYA(PP!P}=e>6ogi,3Æeoɏ22CTB!Ъuc`4)# ӒmNCL-nhhn mɆWXfbt-pLV 2K0m, uno,kמʰV,cL" jƆ[6:-핖_23&B[[w^9u i`-Nզ =<۟c'+qZ06Qw!=綻(5SKPmA{#WY?i B!B!DVhbf4蠄ɵZ2^}]|;u?\ʔޏF) 6jwh`Me.$"'Q₪&MP<֔TlZZ 0bNVi*B!B:{;M%T(|`nzHek[jͶM%k<&ZkְٰOuT>OfzMf⡠֠Y${j\T)l[՞Đ)je B!B!Ę4i5YԸ?}=Z6ySoUVRVlVGblTc^!ak8&m{?Y[V!]k.^ =W&{ldeM߷!B!Boz+ B!B!A9]!B!B!$XB!B!b?H&B!B!~`M!B!B B!B!A5!B!B!kB!B!Bw_~V=Ye*!B!8)Xx׵F=YKg?tWA!B!! CAB!B! !B!B$XB!B!b?kB!Dk4̈ȷwО{&|2?H)mM~=} F3T&iG{OyB! 䓏BwqW9n?c[1Xym ɈkUd> Ɵ6~˼}sH!9N5!gx;0qG(E'7퍜d!?p9OC-{43\s׏em2)^3LxWyLJǷ楗X?0U|]o8PX!Bj#4T?`  !B& Ѯqzi-hfm9.޸6e`ݍ)~km:ث8Km,<y{#ЎzcxyU>~fgXKA_bU#z4ndxjK^wCz,)/k5X;SS;}ckv4Js19ڰ߼˧i9.|vCYj艛(xx&o{kLۼUtx:%ƯTy>6 uCdAå?x%|{'f|sn|Qi ]<ey{{'q 3{Coy/;9z*&cI%䜶u71j#:9ů; 'ʭ۫4^>t6z\>?jYgpЍ|]䆒;{^V=]h^]^zq>*ӽ#ź0іyԗ{ꮪY~-ϛ`3}?x|8?p/x7Hi~_@k?u!zk5Tℷ^§߰uORĀ*E$q>G;wZ.y = !gĮ;~}{\qSNcaY }B!aRT?n<ܽ}RXfw2{~?.pww#W s |x q8{Ň`t,[<!8\ 奯^K;1u=|m3yƿ5⛯浇R@NMH'wIq)3s8̙|<8^ g@,xG~!8tox3<AJ;B=O6t-#j)EY~c\?`y*Qz_w[\Ĩ(+Wr^Vn7M{{'?~<\Ṳk?uW?ҢVf;?-i(йz{ Oj䳟OO΍6~mcfH$g sD~KU,fޫa6g/ߖ ՒƃWF>1oOE"ykFpx;fʳ /1VX9l7] g_u?;/䟯R պ9mj./>5@ϵ7=q3wU{)H;gi0-O~#|G0(zY=_q~5lƵPM)BGE7rєr˷w|ؘ6,B1 gmaaEtw52a =Ké(ޅe㔼1o"Wo|2p/~|iP ߸5q{ćlGm̘ok638WX9ڹlk}w9vܢ ;WnLz)͜˿\Gl?ct{l;lNf0}G O8w\vn;l]LL=ȷ1Ya{0m\y.^yh #.} @9;|Y}G S?ӗxзOB\=3.7>'~e>\os:;Ϣ\/_nq Oy -cϽ7gZ{;\Ye__7.~*z%\t] 0wsw%l9,i_r>x[8cqr]?!F/q'Z>)pg!BiA֊86&J?{E`ْ'$"H+bbTD\ pQ`WDKB{$!ge9gfgg).TUEU5~7 ׿ʟwkFAf2 65:pi؝r~fn,#oK6?VT+tղL.6qRTo;MⲹoěüijN9TA2?n]X!v:@u@>j^4vDNcn )6p] ۖ5C*IOF̪٫jIDzzȝI-V7c jڃ(Yeź%%);|ĝ9WA5&854PsE =|+ۘJz dm B^L'97lҳJ}`M v}:e.z*vzo | 6Ի} *761x׭d_уZ˴3z"0 :jCRჽw_ڰU%y>ޒero&{ԚhXw11[@~)m9TLqU-w N U,/XCa?'3tcc1wmߛ {O~G4>@;fA*[7qٖvnKXMy䍑t/OaGMqhsU:΂  5n"ACN'$$I%>HА ^5nl2WX`LHJ6[q=)mA:vt]҄$+{Osi850LΪ#.N24@sǐ/ʴ1i QhF()KEC,ލѱ7=}!`64TEl7n&6âriy4YO} @ZO:4Ѷ]E6>B䴲W6o"^+v'%6dKKY{n-1+4Bq˄H |'7\G&Pd^ @oU aݷu)IycTk+UԌM::!D:W| 7lG;yЉMl?J/qN66z XvmdGQ0u .,*KɍvPT?Cɷ%mr h8rFФ|K@2& QWPY)s 3+L@ڿn=ܤx,JS[kh6&}q'q0u%<;g:~LRla{aI@I+ʾ6] !th \}*BαswrIY6JrCs}>?| *MԹVuᲾ?  ׎$IFY )5Ąs8\NE)^_7Tk+XӀ\Gc`tz'0#9oeI46T; M9/յSjfh:YS3K Ĵ`ѣO哪AY iEO_Hd_ɀȇ3%u3ʡ#,GdpR2E!G=&5E\(.T7*[(D3$Ih-1,VP%@?&JBZa.U-t" ww44K> .7'Ӱa@%!1 KV~i%\Z˅e~,c3pnI#g)E*MyhjV cWI$5(, a ¿\%Sm$*ݝ?Y6m>#aaܶ] r_e)QT6p#y]E-8$)c ƻܹ" (? JRJv??dȟPHtAtyZ%o]9hiEԛ|?M.2n-ܻ.,8ٿa+ 9ؿy'Y*M}UR"  EFs|IF_P`t1\@9*.\GAzc8wfnB+4 32]YAezɹiM%<%dz9iZAO6®-y(7^KlkM.AcZp7PW"vBb6Hz®첋=}H d$d$P Y}\*Y2UH$d$ /~($KԶ/|҂7IO MH?re^rmW9J.R;HsgS1.B2YT*[1jRbmEJ'>$5l[ <-w;l+ ۞yNxz$# c3 1؈83 c4ŸO"RGLa~4v&ؘޟ!ٺ3ҽok*$ VA\o(֜؝hׂ 1WMM PsL{ܨB&Ӿ<;#‹WZXoV7^zЋlGͮv.SU *4pA15e'Oh"m~~ƾC 8>KƄpS2: mB\ފƤh ,~u2d; [ºsK̅ i, +[ɽ]ل(ּ bw8fiUz';;"#CNn6i fu͚W9 'J:M;ҠA}дРA4 PqSuȞ5,fz7mFs*V$mӱĒCZ[DŽPVe\Jh-5B[A?FS}1ڧ9h6 Cuu7-d5e}}O 앍<39Lگ 5Ka|Nfᛣ*LT( zӴKgBJ<[AB};֯`^;FWM(ؕZ4BVwI06c\ؔ܃kL/iV3m[a-XПHW@i e<+ `2F 3dbļLu|>J*1]|$hGS,zHpԭ/ŮzWPwp-»K9S!֕yeݣrsK"j|J7edl/~yG.\՞Xj)p'췗̺y˼Kvˌ5ëYXXFrMnbݙ҉wpr^,?r=z4([a.ٻYJWJ6ɍ?W:jos|f*K ҦUѤ CX&K|6yį[ Z|DV׉Gtᴠ ߼=IWɧ١? 0[ͼ'q zիBfgueXd9<>!tEQoJW~;T65Zn͌{Ntv  Bff&hNzu̢^DE_+x&((EU jQf̌tNUNgZVjQdݝՋ ))LNN?JhwqsJI zU-;rOeEY,/77RѸȂ TLͳov.a2ħE-Ùrd=:peʴI]JC{7ձM?gfаzس@$L՜Z3מƫ@eȋOeٜfydB6~'zk*'x8X%oRńJ@d7-x 6,ׇ x{9UA-# MGPMeYxe#0*pdIuFkljhc<fԜ'YGÍ! g7.dӨD@TZwD&kOnu6 ˎR F_'-Ji2.| '|h`'vB|A`F29x k:6Z){-Zv>0&P#y|<d r>S=MBm䣉_1/+_0O8,8M$ܚWY;4 $a3Sym+a9ce-t4V23<";DƁ[e! hۙê o}|T8 E+n9U.)u8ٗq{##$/ FYY;A(72OBrUPksh{+AB4oٲz^]3Y ՚ʱ`m cHGFN˝U ]~!7砢a;w]Z[w~Gx4Tُca;^Yfvg.+Kxs0v_'лLYV>Q81;]/*$>4ʼn58upl(zo?Q4 ?zEQ r~`Yp)-n=[hRpKzAdKSMQ^\{ c_8%a9]Vd=%ا-**#m#(V8YZ}:̸cʯ85# 75Yt_~Ji~;⩇if*:]JɸHn_?_ʯ(ajx7/Lɻ#ύcw+e;ά[jixlYPw5εk{AA*GcN )1NNu:tY'ut:dYWz_*xO%tr((>}\* EQQTEQVjfv`- >?U)jNO^߹΂]zv5Û\VY&|͌Iٗ4gÙy4bj & >c^ܧLa<-JaI|7Je!+ ѝ~VhdX[#Fߏ*]"/:E>gK"3$,Ի^9Bl5wO:0.TLy{Nk.Vnd l[Hz<ÚӭmwM7W#2շ#ܜ@ZPn:7ioܲ 㬨sRzʁ)vkm2J~kX["ӪGg.WɛNi#y(qk4lɏ/~ $1[e̓-5'8[a/Kt֍.ۣ̕,ˋ 59|w:Y4+>dB4&QCuU֓7ЎIdo5916@l^JΓz7CLG{y#!Ztiٓ?-J^sZgAA/9\BZԪXBHqKRդr]ps{Vf|cIPC*ÊEW>z`Oip;,z ue$MllrpLdO^I[/ gF>~gC}_~恟%џdtMCA<&;{WKSut?=>SYf+-X3{Jzz(_.~´q{)Oq 6PϣomNbo]T[<FL7:I@&&ZlLF===O;L>s̱.ڙYNM$l IDATKg_`aҪl~L,h\sJ_h jNXwTTCg~^K呝jdSVo<獤:뛪T0Dٗ?$!USћkU ]/&~,6U+zbewjmȕJy|> %0xRŭdBiZnVGu򥒶v!?Ēt ҦjA5|,ޓ\ѻ:AA$E˲\q,6-zBC-4$R/ h=vn4oEL:krؐ 線F|S%*Y t@q1o3S* gryyf:Bp>_;'h'H52!>:lŮ/'EV$L 2FBf5Vwdm,?}9}O~Yӿnt]6  kpM>*A&Oh.Lm:%idf82~z,va˘JtPHVd q p;t$^B؍fN'  gtTOq_u(NPIbkŮp&MAAnH;-3DPMAA?*& pxEJ0m|  5AABz?u2AAѯAAAAAAAAAAD`MAAAAAAAAAD`MAAAAʳx=JC_c϶ӗ@JAAA]%[ pUB.   \+     T    B5     T    B5     T    B5u*p ҜHiM%l)84ҼqF,$>y+Ⱦhb;RtAt,qaĮ"(; l۾vоm{nHAA.Mٳgl6Lf3kCz.}GML9/U08KGMpKi;n7N 'sh [7>We{2[95?L:ZԽiԜxNW lP?õICyY0I^v 3/MR  2/Ӽox#s䒖cCәNk v6nıGޣn@^.7݄[eՂ  p}]vsNnFZl`bڵk o߾U۩$!گCPb'^z?.$$I+@scDڛ/܍ا<>v/x%4;"i\ܟ_x5n :wuՄOmY5O-e|wCO=*^ v/gK#dWjWt%RX|gǸwPT+zj8YsϾȜ/f2eZhA^KY8:,W냄,0  (ZoVYqJx֎$_ֱ}Cǭ6Y_>A8ӷo?n7Ĵ ==& r9|/Φ`=CۘP}:sa 6PXS,47ɢFZʟ[JFN# r]|&:a> pdjBnd&-OZFxT4.~9cZy,~i{]m.<y(H\O>:bmhH}Z;-6eK weYh1kcmeUYɁ}B*ݟ{^{-:? 2HE.!A50zh8ȉأtv3M3mڴ+Xsds4GtIg&I~?G >TUj㥿eeO2 Ѐ㧏PqjQsJfӼLq7mՒGݮ<٤s2Q(R]/aM!eo\u>Zy$#io FLxGZVri4q\2.  \i5ؽ{7 4^Ϸ}ǭ`y\.t:AA:u g0pϺsV{JI6882^]Y&ɏ2~S.rP>9z-\j8TvLl.mE4[79s. ~XSYʮE}#+v;д<Ů]9Od)gٹ}:˶s&thسk{g7f)ͯMKWʄ:[y[n憑O$½5@MѪ~sd&O䯓7~_S;&$ïZַi}h{c8>s*K3˞PZR^y[ۧ0 hF^^01BDӮf+S&޵K_.Т>I;O3rAk,Y ð,wbj9}3 Ci[ʬPYshIjm?̫Ƹү'ָu WD%iѯWޛ1f`[R* K&)ԽC5 tyajdn벨=`4O L2rmoG~9Xk`$#r{hAAH;L}|4m6vEAʪU>_i|;W33k_r{g+۟OtY?O_0$Hº}}g;]$;kW7Xi6/Qˍ3݄Y].\fgOo9*L } =4?p}LSF|Ա՞ʟwL݄;ޜ͌:P*zb7Oc s4tu 93OnvpL!yh^<*)\~~:odΫV 3gsa%q?0P*T:&jvvqM'βWeVOhP:Z=7;}s;=Mn{RNt8>\gtZu&ZG&ҨjN-);y6~=jl= &UeЄi_\8``OZͤm{9c =[;mcͤA'2yh}F| f?3N0 ȚUyvcCm 2=xl|廞C7Z[x[}Ͳs1L7f7¿-c 1'_FX<I+6Ӧ]+4;6hb.ٟҲ.ălK&~4jמj&m;DS:7 (Uӯdr:8 5Xl6cQU;n(JAiWq.۸vn/Wʏt!x0zy|+L}K/]Q}i;3}V#M_G.Ӭbư[: cƾ2AYϳ7c#>w+},=0?>Šglnpw:>aڏ6E#2r&c4d/ӫj٬ӛcnw_ex&)^5'/'5cnJrwM{QfY!. 9 sß_oߠM;=7E#>£&/pijjh70o RN}ô}>[hmuzr[|Cg JWOb$걱*z8q.hY:dTRۚCWxkYx!+$N Tu|.낗g.޾ /iY8i27#|Ws8=d/⊢m'Y,F&|$+. Xbï6sR9_╻Sr7ƭ. cT }{K5zSƻIS[x͖o൩|M}`]up&&TՔ?5S=@RӓIU4`jUte1"KBMM&Epd+I%c#:HB Ձqݗ݇y[NfD "Mz]ٯ۱CwjHnѴlT~D~:3<<NƌIyX= >ژUXz"bS= &}Ѕxy{]*]VgK\Z/%Uʃ3XNƥ_e5s$=Q14ofC]6/Cm1I BX1[%~g'QGL6O QOe_n2PSY9m۽*;]%y􎆨ǿg4AkFzl6.ÇxbxqֲbTk d |i}10"pdv8>Z3$x(HSq5]i6r #y9F‘+l1wZ]W]A18p[YPױ:6{En\u/|]]VHM`pmeZFԴϣM/ݙGE)'X>ErMnMr\QUMP5 X5 _OPL@#u4''FzZ W2HISA$/ZȜDIO%U_/sy?z~vmY7+wq*݉[PCnt7CZ)~ v+wɮdzYo}>0N $sX'{c D͟#~#s>Ï;W ztI^x!3)1?yHJ/2|>|wxcAC_0shh%NOCxGɛ %ŋCxlK}>Cd//ǎ_ҕV'_|2`9> %wi}3&0"<8|>[z(nm(|TɌǥ4iTbٺ#ͽ =;TC޽ eˮtJIC3VOjFAM%mLɐGꢕ:"n@e˯kHqbxAzU#?Q99deeaZlߟ^zajU;yvv|CE(n.:F7ii∏9Yy0?T3W*'Y1A^1yxRk|R.7n)3&ƝMl<Ke7rrUĺn(]0oǡ iGc妴R-\ b*7~\ҩ'\`nÀ+:T5ނ>݂|oI^Sz+HZ(M-hh.(l54šKt۶S:2'Z3jrȉ3 IDAT?e[0#[ ds)d>Mf~:@/<ݞٛ}Hm">?κQ72 rh>ؘ-u3[3N&{N^-s&2#%O1sțY_.j:~n"  ~OuعMyGQq{%!tP, RT( ( z$$v$@B iyvw62?#6~Snnd<QXt @#86t9gw ga-uyOo^!ƌ4Hv͜ȢF Ob>Is,-K}Fai~ΎV2e܎2xD) 5)wɇ \À3eސœXpOJRr d H{rKpQ @r8qE2q ?UEE>$IJ#*̘xz^u\Ѵw;W H')9dL&&)3ZKkR a 敲ZL'a/݋Xvތu^t/DQHz; \тSN`qج5h?l:+bq݉ރc;#ƿV\F%Cs}|m)Mr؟̛4S7`=Èt * yH.H"HQ EH݆؃_ohb 3A JBSwT$'Ÿn#lB7 '/wbi/W>yohT^@O{^Z#>Ϲ'lL%%5_!#ޜj&1$ׯ $+g|+QFUlLǐA|> G. {$-NEA_Pqd/8#FΞ^dwh;jZ&>Qm;L2 1z|:Z6F%6²)kFNnE"c_K.TUEULp]r mPIKC΢) 'Y4y. ^7ń6hIE9C\Ѭ?XFC|)9ɾBlR1o\oAMq1ןىɳ?e;լheWs!_Q% )ZWcY$I`H|S>Ujڴk0GsJdȯ|ۖ#>g K٪1Siv>G;ߊjߥW9}˺53ԲRJ#wBFƩl]7kL2h4Dr0}=}zѣrmI@ùLu:zƼ' gzVݟUMc/؟dCáRFUO\SG|bOxYBAcDnt9כT!з,5[Ƨ Gp3B;{yZvͰo7q)r'Xk 'Y!)6@vtQJnH%n<숫]/}%M|9^ժy!stG@ xj( \KzD~~9'%aemMJJ1,jӫg5xe Il[k]^{K8?CVWfvO|e>a8ZNzB3hIl`פ|ƿ ǫٷ,Ox9kjݨ},sEMMOC .(ARV,?a7z^JˊyɎ7;?Oaa$粔u|WLP!wL \Nm`??R>୙4;5Bjr *V<$aie&\($@ZPLkגj$o^Ef^G`rG{ ]᜺IlCKKdE|r=瞪)>Cl d](]pe@viJ8%Nүbp.+l~Jv!EZ[`Q>m]ˇټ/V2e)6FiҺ7,|VPܛ+ dx!uwUjW*.'M^iWV &s21L(*`\!3_KcwdGzΞ43PW]ZP#SBhQӊ ks}W#1I:Ki_NA[t.q x`:ώ=7%dOKU*|{({ûTq}y^{w6SfHBW50d8:hOE [|3Vb!Nqe3ղmrN&U~]\M~Ea`g6emJ\v@ x(a-((۷IK}6ŅwPJb䢡rԛ{Ξ۩,[s zZG=iGܑ|3^Ό d#~&a#h>[)ͦx 34:WܾrhA= Ɉ1ָNobΒZ?}HL\tR\LY칑B'Zܚ'gG$"0%^\P,jи-Ǎ(DIjMf+oZ)X.-KX >*`g[aOhc Tހ:e\ԡjX,% %Vj6b4q]6GpICk=;o5ڶo̗vW :XT|B[esضppb+;n)i[+K?_uʃkH-Sۧ- bcl<|vIo я8JLRr^"BRR" ~ev-MUjw0Zܬ5;b89/~Mwy+2-ECY Jף8P!N<䉛s^ps˱X@|e5%kbz(9sT<\v3l̘~tHG܏鯖OۧT6!6"YW!QF;I%66f@AD\N>h̔}0~ּ(l[LQxPƓ2(# r|( wG0w{K&r@e4s_`]UWa4(kvY|lG2yVЏy57o(VT൞k<6O>NK wn,5@ %"/_w}?7hԨ^^^sQ+C !((xI<߻=_o"29uzW XTμ)䈧oUƱXVe`zׯP"r|h H0^icy'EXbUI(+v8;e<4Kz{Q$ U _6Ik%bɍh3_"ko|[l<9_valRO}mo;FE_{f0~ɴex%s|1Uvu/GA}?όdtPc4u3 ̄Q-q$;̧40-ϙ/x*ﵭžCTޞzZ1bvEŢ+t-?|5oYL.AB/$a43666L&.]t #i ao% zkW%!:pƃ2ZQ7nbƥd^2V}r,,a[ȃ-#P,*>{2|gSTClݟHƧٴ.Ə֡峧)BUU-㊜)aaKY'5A@p0;#pVb9u& =n/3%FCVgM ф]}S17򑬬@.3N~Ի=FOpYUc:zP7[d1nѩt HhKy%ˌԴ\cNiHh-r>|% N^5=mRSIIQKDpһʭ ѩ4^V2GoEmׇ l"@`F辁$δ  II5%LcMX31"aaa; ghт`o̙qswf4>>>Α yJG% ~ 2-lo̻I_?N}g}HM䯑p=C*h&{5}2UZ%IZNEg몕0t`6,iԗ?3PZף틽M]hUgK3^g* feJ.XJækK$vL7߾o';4!xsw>cYlu? )eJC xwK83;W䏐4V[.ne[i|]  *aZ ~7r8?]W o?ḣ 1|νݿB{Q{[sxsy+=Ud:^-$Iş((QTqȞzgYj I)* Sx2]e_IWA,NWEs<7)T=}*T+c[ȉ m,ncǁRlT iC翰@h֪:߆pdOEP?T.Y5X3ǚVo=u^&,,7߽vr''-ilu3–~ða#;e"hoM'4˞#>Nyk2Y,v%sւrg'taCyLzorj@6cv'JëX=;! ˼3bӈקn` uxH8^Cfڗa \8Il!0=}0c’H>UЩmkb٩D --@P*]jՊ#F0r#FUVO1pr㷼n jh#.&;}-v͚̊WeP M"j4^;.r}LƯMGjYKeslbe` F#XewUr ;iQ3&szȦKNX/f VΟċR?3>ArEeB9jfBd*VSβ%Nph}N_ׯr֢wqePR!qoZlȡc TS<Ѩq\L*璶y b2s}3'vDg[:s /3g%lvWa8v{Fc1-8m'QYV{ H␶ Y#6{lzƋu 4ε܅-Ey*QH^ ?l*bȪ> 3Cp"9S~ PraؚO$g9]ҍޔ6T9׬14bFYh-^~ebqHW?vW-BXS)I3?\i2_q)ʲli؁ߢö1Whh?e3WFp5+*R*sOGqۉv4+\]2A?k\#.q)6I& 6^!e%_]95JQvXn 9w%dEg Q CyNVt+Ǧsy.GǑ&YHYǿXnWr{l Ky,6rhvLz IUdluñ?s.{Ҽu0?:Dԟ2$3b OLJSB8x*;SY*J4hZ{|dߌ/&\8a`8ẉi{a8u\b.f\OۼV${oDG!C݇n|'j $bOrKvܼMui,];8PUpHJC7 TTѱXhvyo5~/'7Fi׏Et?7M#?G&lď^K8c9?chul8:*хZʸWtΑޡ^ܽ-Ofw3&u^냃V}zwCf(~;5UiT>ZK^ a^2q~ zơ<̓ X6ޯ&{<ͼѹe,9i3w'cQ%^qޒ`4˔>t @x?~&<[N\,w 9ӼK3~)RSSIIIގ+W3...hbcb|2vv뱲*P4ܑi0l*9d8sg3wHo3Kq2ї/vo169q2ts[2]FzQydΊmw>e"=>?r؀0Nסf14Vٵ9fh5Lb F9RQ?>܍jVfS}d k#c/| !3{Sty'V358D" k1+V>aߓ pօǼJ,і5z>fO%c9·z=?fD9ce(T$!>q &g$ei?CZ_Ћ&Gk||w6?aՌ-iڻ ـl,>s,[3i}$u2~8~S3﫵$.9Α}^`܈k~Vm}i9d}Jj8#{գa~^@ i3p-}vg7g`ɷ NxrR.ر\IxRU-v.^uBr)Gm_͝eTtT'׿C\\weoX111$'%( vvQ^= cQ#I2cKPIɵq6{{a[sJZ<LX9:c[k̼7K?YmKijǜˍIVzx/[w1Z;l+e}^e-/X;Znc@v*_;ost5(w9=[$aW)M$f O\L͍ -,JBcNn\*:'lr!n,ݦ8G% gliicM V/X9⒋S_GW)<Edw/{ú(r˾ˮ_P//cCLS'\D"o>O jW'֨~\lpp-Sڠ 3q?҈rX|p$^PMDށyb Q"3:NŏaoR1?ͤK+ܯW [@ ^u,۷W>p;cB ]Ɵ=Nj2a%˼ժ *|㷘f4͓.$IǴ^ؘI?3in@ Q#ϧN.yfҕyWcVvl` 回l֛)SObTb% ZFFj䌾 Zpƍ-R.3.>iىn+'X "!Ӈgpp? Tjc嗝g.@0i+1Sn7ٞ2 heEmȘ3X$hiJSqqvaρy I^BEM $תCV$Ij=CI!ⶂg)%@/%6\|d3sa@79|l_9~tUeǑSϐ4y؍KŔz3G8z~՟m`_lƟ!F!^U;c8sCɝhrb0_dߡ;v%xC5dL []ql2E+պ߶c>e|iְ!2hTg@/gtjoDgG>`w? !=n\\:>0d ^|'@ EJ\yQ~;|([xC%@|h \'mϿZ~fEo<1Ҹq Ȗ++28fk@  Zulqi ;jm=&$;8u B~骄>mC.R Hh'"-iNEBӧ{k2H2wQ$o&pt$ 埴%@ %.E݈"E|d:tjODdQQ;C^\Ig+j%/;?ʻUGg-&Mפeשz*V)(b. I׏s d Kmv>UDzU5'r a[nS<$sN ^ǷfSBOѧOŵ\~9%>?bTxo,e]t?Ι9*wVU~WcN{G%`r\4 v+ gNBż\9zX0e]/E. w/~.g{]xX:حB P/PI>pF5ywI9r:PgR:v#4~D IE,RLD9սopF"X{PS v@USvS\#U@*^;\:uwHIۻS2l+0@Q)IսFM#a_&IX<5k5 90Gr!: +}p)% HM'nE]^hg[ v$Ii´Ƹ;zS0L@rϜU1$u#׸c)叏%ގhM\>~ÿ*+~U/rI5iܼt YeX+ҳ$qx8GĒZQ?Jc@ O=u=Qmݚ_mb93Vh:G>̓8zNL[uoFقاæ<8p~gmH/2v\~²n8b\n2a[nn㥕-?~?b|cPķ-JJ½^i9+f()1'Ι%˧l_?F43^q3mD|PI:<ax"~h35ݗr.-y\Jm>>C&pT'2ޥ6lONK6ؗB.6T {R7sN_7פJ6HEG~7օ!L:MjfMgr0^MYDH??YC}K Sg$ G $mz=i>@2ZW&;}UGeKPӟYX\GV4˪ ʍ5=_~!|s,xωEfõe48V i.F43Ϲ1XwYvAc}SV}4?/$ؕȘk"@ QۗýkD!#gWv"Y谐@M8ʌ^YH /K.ocާ_}s&vG<,o&?4`YyIr) olH,]/|xko+8`]}Lҋ 37-g1 t2utuI#^d[ƄR6:ӎfDIӧEE[GL[Ŋwi,UcX;/ϻRO۪bzTnmX6{n4Ea$'ʅnXƸyL[& }t) %z ɧn"N87+UqXVCJ/C컮P/K#0gxmejTu'(͞s:psJY\>x[ hPS܍\F8XWIdy.n@7Ӿ].EvĘf0G|JmΨop҇&=Ъj&βkq7-zͿ@+te– ] n:Rz ¢2k| ~j ~35*eڌaк8?DUU~O;7(Dt3i<^ $KHYwx(SZ{Ʊ{HƬBq]S .eulFPf'~*8Df?o#4s'ȝ9mJi"K` ۏsι C{(w)~]DZ#?|@?Uhկu4zeE}0笾hҷtN+ αc/L^#[ 3JZh#qn홵lH*ƯWsaaUhNkYFLK˯ؽu?*mj}mڐiXyIty`gV.7_෕0bE)uFE[%vfO|q5CffySM7UeaaϿq 2 h:vjxӪV6ї~fXsdhʨ.b˧61&Վv=?? {#ܰkՃ=3FVn㥮c̙Y &Ɋf= hhe, Ć#Ѱ.?7Qu%cr` }J[W?FA[ZZjKsCT~whH!YP"ɫ _O̓vh:/W^ ۤ `'̊UChPn*}ƭylh=Ώ:t(%o^(Z*zdw<no gi~v]IŊ"^/#KȀF|3^bu=>OaͮXp>dtܛQM(z(rAPEZ%L慄,袵/|/O-㛿0+i wQ]4X7s׾@Hw63g:\ t|3CdsSM,M3ȃ ~yӺO,]&Ul?t)ϬbɁ.oEP:z&~fD霥.Uv.etŐ½4WF \F\v^@ x)&>>Ua P*6͚`_,ZiO FTOUSoFK ׹|\ ,jлO-tػWN?*V>>/)pvJ*mͮj}{0yX-,&OD_7By˘e'Z7F [SFi.4{HB­û n_WGܻe\0mA ݷLrhƐ3୷ -XyuS^}ƕ{ ɮ^woa٩$BF@jmtiPY5r@8۟CnqY? hjPI| cj50.@vn&eSRVTM`ҍmsox5v5=%D*Zm.sD VK{YQyBzw"d`U:Ucq35G1|s0:>-OR$)[Z[ T]3!euX+kpȉM۸nx! EMu^?י-h߮<9a3W2P-M`]r1d'Zt@Tgt%`{֥=IV%봧bΏ./F@ <YX۾c+ 5" ,Ipԭ]dƿض/jרCu*]ɲk '; RR#8i(ף/ͭxl13sz e\;[G|SPRvulsC*G1uܰj>tP;xx>_oJؾHhܸAjr4FM'<,ŒkICӘWLy剞v%P 6Nٷ7.eӽ{[6XԢy#^vN^mX6H֞vk֥6ve -!(Ne!}0[\ 98)mcc֬KB+6b3PG?<8TT 1$(j"Rwj<ᏯFWVԯ߀ݱf~Qy2HﯞʩE2ˤxW`2EZqe+)$k3FcB1I B vXs'Gr}٥)6n8I [_$Ҽ]HkJvnʵQ89; s %)D\3us7>Ns5׍@ , z 4,DR xNd2gOR%f3fI~z9<3Ds?|s_ IDAT;~眻 6 6([PQPqUֺ~:[GZZQ7*doH$@Yd} InHy={y0%<[?ݮsxwӖu'&s+>M6lF8Vzf_zdL x`h*et=zj=NVps݅ө &E{:c??{x=?KUc'*bH7#N/!2J@F2|}5lr 2ϟRIwj6#uC\lFS, m4c<8ݸ:׍ʁAw}S0ㅷ[~ 7QmV0:{'}=ۜ]/KyB?K"ΌxZ];8kf?ޔz ̯Fc'z\K12PO#2'k^ ݍk4۞XkPyh"CY FwR{1čcJz+sKYVw_.Cr-70rB!٩C^/6ߤyzt]'$tx]*~) ur$QQFi)7vפgrf*.<͟N~^Ǡ?QؚvRK{5'F~x["e-^jQ*xQ#D6;]7$zϝ dD={Ob&D&/[|Nێy}r9i|1a ~SfdrL' ~Tb[ɪ*asV) 3|7$1x`$M[J)ۼ^Sϡ {!㻸GK&[=$#zzZPjls&S31p:¨akOntHiq1Q (~~( jv݃(??_'Fx*.,i=Nv$=`r&URa4S/.:RGKy)/@CyQݗj(!OZ.fP/B!Dhwbd2S^^JMMe(-;;ULe)I[x0zed֪g_;~P=3f_\# +VQ~.cɇ_3>*繲gf %Q{cK޼j1Qf.rRc1* #^-d9&Ӌ*} v=i5 DgHîE57׿elg?;j{ES8` f0OOG >⩷vaS&X}=Z&AY<\Ju+n E/+Gjl[8gһ7^O@yת͛?gXAOޙ/fϤIՈ1\4,a-oB~9]_ѫY8T{<($F7`h\[IYK 6Ň[8\syhƚB!R^re,\O??={a LM5o}ik^~o1ؼ5\e8tS$k\٫S™22bx} 3ႰSVxt~`/_З^ȈD3Yzq-c_zGgy|B%x%}.(y1MePդ\v6K<Wsy)ܲ>Ϯz3i7O#^v^yr[w gә28C&|Y:c?Q". n vzB7ߝ2k B٠8gpz#L 0tNvj`%v"s>ݹ۞[7~uqސNl` 췛Hv~_w=5f7O{>sF2$5 FC~,_Ī}L ӸnBSUlsܬ}\qM?s)sCױηغp&OԸ]18s0[meY NLx$jBzu5N'(aa& xB6^/[zrM ~_1"0OV]D=y1ф/j\841ԸR>[t S9{YGn䛸W%(P5J_ Cs_ק{hYq`٢u4 _c~VB!/Ykc|O3 a>cS꧋عa!6-f Ӯx=C"OmBWo+yNeyŬM֩6b\=_?MoIubH!4Un˥~\0vӧ,?f-xoNQ OMYɸkvy{}PPyk{D1u"!Lb"?y.Щ15:+Ke3sW3d@+pJư1*IQތ/K7|~"0eMviqGt=ş۞gf|ɖC -ooFFmN<\4Lw^ţgqլ~)3RA躎vN}m YQR[!%%&54@+*B۽>~ATTRAqݨj+U[D̤]Eo}ǚsp 4c1 ~w4LfR\>د3vNTWIǧL{ꛊ(\vv8͟W2ϾW LbƵ}iHǟF9wY (zNޞ\8|]dI!)3QR^qc;Tu,=%İW:~Ӟb jh؂"oqFtppr***('~F$Ak[̔RL"$**p1m x=%V㱄KHG;kX߮*Mj_,Y=Jx18D'&ek}̜}%Jltcھ})boA#*13Q!BP.칅PJ *tr"^7>,p;7]7 gv3x"ChEs6A3t*1=ӣۙMCfҩ˗΄,*%LG&=-g? ST!$&&p8Ыa0fVY,XV, fԦiz"Fc4 }e:~S&-k9 i]Nj)ݎKSPRN.!BٓX;+Ty^B!=|pkOL|}W΋yXf^~}{UB&^SB( jYF~<jDb B!g?I/|OsZI |)4n3.'W/`ŗ0w=Ki@_VB!C+]dz6Q|`˗nm&9%B!TĚh?v؍)~h4!:yD3KVOV 5O?%iSB!C?ŷtW ` r&tWsV({S0aLf"gBB!B!Yy"ڡk\;m>KQTTD||<111<IMMUw {75=tN+#;_ǹ3O k'!D7,\⇎#3=|Q;>`f䯦pkﳃ)>=H 䩧 {*[;jE\> B!BѩP' ؞_F݉@Ӽ'ްUz?t@E9wP B7(.I#4rx5Ě!??~a0Xf 6l`С׏@v E\CENr2([CN-$p%PP6e&G*ټh5N%`|ж/j˘O)JЈ:QسXFLBfD}clX]B􏥯yB !B!tx|ޜG(l] `4[Qv.&뀎Kw3a`Iv,VṳӅ8PuP/DdRQķuc-Vg[4 ebf1_-%kr/OWx6`j0}ǞC_{ (fŬ;먩Ί9ԡ/ꨩ.J`.ʝGO45ߏc^~7;M/gوBpKvDLcNSi)@s(alX[/u9;J\#[q4eF23Kxi"Qغha22zNYz5[Ê'jp2$>Ԇi<+|._KfY. P GjL06[Yys\6}0QJDss9H0iC`U0G7. W}xt:!Xt;w~nvzm рA [WfSaBZjVs4sɂJ]:‰ BPM{:n?uTB!`Y tߍRfIJrsw1I js3S#\=S'ז.[Bu] 2:&%)sGiW> \.]/xivq:'hJ0{bس3cuُK G<ǦZ>n)~Pg)~=^Q8 .fl;j8'Mfx$Ѝs.db@]Y;Z-ebEڵ{3Pt,V+Ç+?-iW>p8x*{ h}VK[!BΥpK ۤ4 5toTCz$y͌3Z;^{(uka0` {  -  IDAT-@QÉEzz:^ocRtv(ov0JWk2=Sl.bgn }SJxZwTN0 HFk8ꓘM~FqdM5 8pxGtTjab0v!sM3ZJԣrK &6rq:QŶ>T%1D|T0*H*=k%KɶE}%+<ÿ~"f>a,|tx?qG/rUI!B;&'j)5T7ո4?НW<0)ӛ*vn'(0?fѯ?))ΦF6Ě<"FZZ4"ڑڭj{v$F@NsHL hedV=mk9|*4 af*Ypq &'{G1ٻ#y{t#O%:sCJ粺%Zf!82SHn @KzZ[Qv{vtS_C~t$F9e:<Zʋ}&nesLF;3&skuGxvq 97S^.r^Îht?ݑ!Bq4ޚи^+TЌkCi:O/M`ͨ5Ádh "<@5˖ޯ?II)lߖukшjq`uuucQ3b*h#cogCe;K3-&JÔЃדt1۫uRFq@6}9MeX&#b}Pvcvgyl,wџ.$a11*eSv),gdaEW{;Yٻ+@>ٶ'i 3vix5t/^o+16jU9: zc0I 'w_,j{(&Lt.`{vKKFÁ\nϣJHt2}2zwDj/[6dEK =3-kBvԡa#*%4SXn#)9%[+N'{bE|JB{tEãl_φ],``f1ԗf6Vf{udƀ%,HG!B3܍`Цjng^2w&NW8> ]!Mz H^/fml5?jjjiDh2UAQ=Mk*.dXL 9-tnHj"4ݝ@°1쟽- Y01 b勘_¦r7jPw&^4Gf~i!ڈJd\Z&x(/.`~mڋؗUGa>}zӶYB>jPZ:[yg~=/q7?8v |jL_1'w*Viᕻx7ϋq]ͺ-gj?B_,{w_eUhP7$lsi)XbGҮyM]||J•/}|sB!|Ni[u5\?Џ ?qøDWUuefˍAQ+M}CBGᨨ 22BXĬ_N<^r`N_[+ ^G4:;qrDqtp1kWJl!;Q[d ݉H΀~-[ELGUTZ ?ߔ=-9{5a-`Qĩs!P,r:ݏz[׊xu}=a\d&bvUkW3k+_ycܐ_f˃a0<%vO3s,z]ovȹ]di n۰5~gnW/vy}4R&\v1 4Ok{f޼yDFFr( & 른 &;QR}ص>A.fU3#1c+VfxYQӎ\+_ SJv( (͏[O%IM?v[[`GDوZPN;T &;TT[ ́Y_ jW҆GBZZ4ѳDŽ8#yػ;~z-lJ5vް%,PfE :*穤РMQuдt8yG3ll~*,gGpLB +xQ HTY]Myn; Z7B m4kïcQjFR07kx=<^/NՂY{ƦKٞ][i4V48UC< <4vtI=Nt LmaDCI~k\kZ *p<9B!B4UOa{?gYam&Dt$_ԎA!x&gіBJQ (&2yPz uEEk #25`%+7$w ŹZ5ҥ wd!B5ط(|*`PTBzMr z+{ 8M2\8MY-dhg`, aXz2PEqL <7jXS^;^o%yn/bwfFR^ĭy<*(#!aekgWy~UEZ`Mc\!-N?Uc3(',r)i5!BqS IjhIdH Lur7"ڍJ,^xvupګpp8qq:q8p8]$w74BrYnw~be5S06Z^@ܨksT˛mwds}xF6Bԛ8-|3?çrà:éb;t ƌ ;vtG\xgUWMubDBPIFUL~y-qALNPMUnLH50n.;!cɃ&dB!8[\1*{Ui'V`n.FQl>5śjt"BI7'IkBqel񥟨]]V)T#R9[v|CO)_yj"RAwVsq(ud kU⟳|xه3}gr¨_K)ouBQCRu?ݍ͒],3gl{(CڐJ+O[!BupLpVtB &?V?l(kύk5:N]hM|YcM!xnf.yȋjWE?b}h 2Qc{^oo_O'?L|.akm0?w7ɫ_ ň}O}7W7;~hFpp?-ZŞZp#w]}ohtny:[Tbq 7_ |z Fz=Y~M{P"I*k0ٿP?(puCPB!4>{Nt? 'CQ0q85cVDc6[M\.ut7.t<;m'ita$ք⌧.?) ъh\b4i][KUO~xuW2ϾW LbƵÉB!vE|0}E&QEi@^} Gp>iEqx4~?i-%\n7eee#uU b CU0֣Zj1:k1j\v\.& .Ҳ XO9m1Yq{زiWGoron> !D{lX^OwGe/V3%Py!*`ΤP[R@aE=K(񉱄πQ߀Oi\Ïa,pnw,o})J/B!8 l̫c O-07ꍓ6uM#L`|8:lNa WG&1qGfbcc|8n ˬM&l6V+K;f4oC["22NwB!޼OIҿ}ygͧoHyOgB!B 瑷WBPdXDQT0UDjr7]ۖ۸[sX,TSDEb`6 n62dcW#Ί͛:dC!if9;[ƒ7s(2B0er69PBsozVB!Lphzv 3WRTQDX4A4jMQT fvvKᦙ+24{/GM4[[qqGduV$քBPËo|mfC>rfr5&B!DsRDf~n@_@يa9C5U``*c=2&=%DL% dT3o}K0O4ܕAPl6qU;SɝB!!W?=ԗRZSQ EB!8=%$3os%/϶|j84ێ:h^r=vtkrocQE*[wgD`@FM#$XBq2хB!gaLMP]hnp8phZcNUU1LX#Ԛ'ζHbM!B!Bt(F*.|MVXB!B!vt1֪u6OI5ڙGstif WB!B!84;:v'ҎMyTR^Uݥ"ֆ*`E- 3 PB!B!|-vNO5_L;Jپn5vPjaHj&$C+©yu*7/d2ȤxBؚ:]uT7-(˙B!B!~NIbM4fsgwuB|?w%u%v fFک.+doA9U60^w|CJh)t+bի޻(zbV4A:jH!lyHhݙ3Ιy&)*ă_\8*QgXiK?;+B!Bqt`ںr3me/ Rz`'O]sX^XCΟ3L]9N)-B#k B!B!ڥk//nПWX{0z;4o#Ɲ핬!52pTf?hl)(NR0vu\u).G1a "<2 YSN:WzգVl' %i ^tqO!B!X{#uvq9h˶*f߀@B0,xp9Ըq:=nTE??_ll277c$j(')'&1 ;ca¥|&QLRg AaDslM14NFSXD&# IDATKleJVė+SCz|c'd5(eD í !B!=j)-.? hL&&U=@xՕjhj]NFv wZ|E&wDּq[Wp]8*+N}A>Cmғ{i4`PA֢TX4р^M_X́ϐP ob"6-b*!ɣ8%KɩQ3H3 xXkXVsH B!Br:(*GuLm D0 9 6,V~AxnJ ٰh:5?.chhJ F#~~]VTWjP$CHJQQjpDA+["L1"dw5:L_[KΚ\I݇~QU/#F;7yZ/%؁I B!BulLXT!᝞rJ OۧMFb &?on.[WYAjjjvX&1p@ >xwB\JwALzA5503.IH`MDF`Z[*qۄB#F<lx'xg-5vtB!tulۚGlϤNoPUYAiI!1[ߚۇm)5MX-TUWq{ "*"͝7n}PXJ, c_;݁j˛z;_C\\|[NhV&j6t줆i}!^񦎦i蚆[iJrqz.GGm¶*~!$KRwQo8oUn`$GO"l]1} =;Jcxe>ݙs.4Wxޙv=$&Bqq:lۺ؄ 9ӳWǂj+ I^ꃗwӪ(~jEϤxTUb>CN{:QQQFKL.,FLL,@`` |w^dd:&&v8fpi{7psNi%kXj.<{ObpBEt`Әzm`Z x*~g$w0?hm[^ >!]1毟ٸF/#(ʩ5lg[F`d8- *}ro!B!EDuIP `KX:j#"*z3}hƯ3#&.0 غe+G#$,Ĥo?seWvJ˵֜Nx<'0o렠 t=^t:NB5ZU;48`@m&^鿑Q7aQ{a11*jcVBYɪױEr)#l^왋YOߞOk1!+HݑJPR:}CR)$uUKf-tseK!Sone.ܽOd쉇1oB!PT:]3nYME!0$ 2Bڽ~֚,&,YА0df˖,[cIPHV2|Cskn /7c0q8M7L{lݮCx۩aQneK"BrPZھCъy*wfY'u=ɖ~zsԃy IܻuppPo$j1qF/zɧVzzu `$&.#^h>9B!.LNO7,"m n%:6-icwiS.QvJ~;Xx4 )]cGXύ;D5;X1p1_W|\ovUȁo@^[C !}=+78U?}j;BFr x:;\npS58nF`r쑰cj rKÛȞ1-4J7n3E Vҗh}OZMk+j6gf8>D$ o8\; +k@W}Nwm4ma'eòRPULYh4ac9GLR$^Me׭!$ےMe[N8Bv.]X ^$c픬[ʜ4 QIOEx|2Wo%ؾ)%ԗRPE躝Ҽ1` %!;`_e5 a=HL&;\B!mF|| ވ? h[>5k݊Jbc)).&m}>Ĝ?gGUumÁ8f Oh{+WG718[xfsfʥrωӥ*6V8*ٰHpM&nPPZLyoyٛg'Q{FP|gqron°x[I,>",(k7+yeos G^aV;V ;Qa]]3x6O3k6cv)/0K-\:'I~oU}<ä//>-J{9{q1 = \;\@ƕ!<4lRO~})d\ 我#\W]7)k$G1<<{N0.V7wV39||JWh+r&/p1{ ]siƒOl !K!BK]u5~_jk$ϟvZcZhwJ^;X'};%]O%$DFlE lYɨ!}hLBwP(0޺ _o4îZARMN 2Mi 7'#H q԰$;~%>m<z0lp8喐:-N.֯\Cbbt.c-gk2)TӆhE|üSp=1;+Y|O.SFTUwRArCRo?̵ȤڿH™'Ր%yw3Oz5~.kxUNgیwae`B!D\NctbgN`X4O_8Ex]^{*.a2^63T v tbGaʣR`j~%?uXpR[6Jwh'˿?s OԴh|FJ6.gX*jv)IdHZ61{I5F6Fgdft'(r=r8+7;37dƪj[s,ٜIfV:N`l"E$ZVwe#RڱĽrH*֚:UQ)Se@>PM?3{=̝އf%B!:va0!rb0u] >\n&[_#;kF""" / ˆ%,ě?~Eh ocqGO]'attw#5յ4:=`e7TMsrH;vЍ5f!rUJ#n#6igd3 ni zҸT:䅟 (a#yKX>f-XٔQ~ oc _g$| bx n csFd K@K2^_/ #g(*ژz0 Pb ".1Y5XցWPlY+>Y~Wڛ/6{=6m8)^ SF/6vieX>ٷǰJܘx`!fJ+aj4Tb#F/ucśr"?*B!ǃpnx0.d4i/Cj5inhjЁ/G]o+NǖC!pS6mo(6k!(g& fMfW0+@S5\6PVo_O֭CWŏ!za\չ;Yb-Nci2 ik3xIxaKF&嚁|E,{~\ͅ8piA)7pkk0 TꪩiG`MY_EAUur%gp3ykC(C O<4\[Ze`$  t:;@Hp*h3֞r !B1 ]\3 =nL]*q՘ū?Κjp4Vcach؀E\чף:&G$Jx`“3+j=NyW٩SP dK}RaXL0~V3Fd1܈}y4hkT.vEŤ{T4?dal@WN@/nUhϥlh.\;Lߎ;u[N~B!1MMA(f0\]X8]4 5ݍ;ӜMsyh;к6B \tmlZ YQ֧)(aXćl$zq;2XYC IC#װb%t0{6WNV4/69P#Gpj*ײ tp:*4jκS5Z0xF !-~gMa_WPM̛XYtv4|1jt|Z:F;:XNB!ġ3]:lcyuIUmgPP?<P2B!2/?}ɷ++F: Zʵv ca`Z_F<3'6ׯ"ˡ0x(>yxi)F\޻NMI1 2-|{ PBZIcۓ@#b}L}zkXK|3((:V]EugE0 ’V>ċ 5pসd ))ގ)< B!C]MuGmmM_W[˻kuTh]!ܬ1&6S^ /Wo`6(fc[ ,e,lr:̌,t/҇ dTzu :o_1OFwo`EV+ٴm]_J^=@zћ(vVO bРh hX}=FN=V6RFN~kwn`ifb;B!{zGOmuU_[SMPphطI`M!%/zNVt,aoNG >G=kPY5JL,jl -AHQ˷Ѭ"n3r24_ՄূfG+-}=V֩Z(8N啒cS^A1~u]v'4ѐ9'?Amb#Zr欮#p.˟E3tV?IIcOgU{;}ep_̀ عY`heSIye[} *~E{=tR眓BC!0XmB]*Xm&j xWS`jݾ,aѴu'1քSmTn_9Q#?MXLT\M _*#X%:B| =H?,e~iPHC䤱)nӟVٌ탣<#9G[^`~c_ d9? #9N[ȸCI RّYNn$괫gMVﯼ6fn=~\y̿ }4iC^CB?S|N?v;NEsJ,\ɩ3az ]Bs%Jz3W,H>/?ֲu2 ׷XDQ '_O'%Ӻʐ!VeVև=1/^34|^bD}?O>ó_bo7PTi}n"&ƚiYd&ϭ7F|r.$)|kMOsiȃ׏ĿKc0%3_aʼ ͝7V3ۀ˹e?޺CǨ`S|f3<T[$#&<#|6O݉u!3K'KT[C.;MϖC2SXlݲ(X2X3?;dgcGck08DZ !BF>~Tn/|{)!᝞_@ e%ETo'(}6r{)=]q\a0h]oh0Rx0բZ0Zk0;:8,f75ŨeeeaXPݘ(!W=4\x}JGjhr|w~ºWP,>Edz5n'KZvia߆Q8Y4FEon!GlF2C즶d-$ O}[K+ڞ=7u%léZ %:*5!Bnt%'؞ke{9egO-%ݶ v;E,?fAQhZ5c:r}Vn IDATj7huZcC}GWݥ cbg3״b'ºnBIH#-ԧu?^Q]J;4.`7"m[FBbǶ@r#B!^& 6O-kFdLs٫fKdžSq:l&$,A5LttOn qd6cza0_ucތ !h-eB!B3ټ|I\B#:[h@P0i e.bZX,@@im?[1քB!B!l^>' r£cC0l݄_`9ynJ $<*C3d%5!B!B8B\boj).Jfٌv|f? 2' 0Նrbol*|IE=rGB.cJw7H8B!Bv>~۩b{q1n Ӊy:m;;*QBil? >P 4l]GnsBt;Ο[!BqhV+!8mB!B!8$&B!B!DH`M!B!BB!B! 5!B!B!:@fB!~ BEzRg:zNXB!B7-ofA!?]sD]>XḌWGBkB!]Dut]۱76iÝ-!o6/CB,:SxN$&Boˊ&2r%q`+zB"}%Rg hQ*Bv=uRu NΞBP||?uZRg MKw$5!B.:;**v1B!>[P8j53E[F=B!uMӰ76%7B!jcXiLN uh@Kw$5!BNrF3MwB!AQ<-$]@-uh㥻B!]IwBîM3N97 !Bt#P!8G" *BɎB!m1ք8~H`M!B!BB!]{?eB!3E4M9doڄB!44t%& 8)X|#8k) \tr=‘YKy`YGE;sBHy0r>:$ΟGu] i)i6n`?ǝءt{BJo8oUnDb{]z4 G$֫# |(ejIAU m~g ZT f+Z1$)\0i.3zC{}7PWoDObJTTG!&1 #gUQ0 $W¬]:s\\\Qҿ#WTgv0Slg[6n$g[;ܨf/¢IӛCf[ގ0ΥsɤШ-ΣVԃKggSי[S`BxA9rrs(,YgX,6FIJJ >?ǞkBʝ{w<̏5F}k&bųxY<6^{_}3Xy;b/JG^䅯p wy/DhkLK`CO9+/CAZu[z>:m&_?6fo` RSHMMc)6xV]iw[!m{_`fF[x屳I8}w:V.߈K fnT`PwuMcu 909޸v`'<~W~ͭ%j?G=;J ZDܯɜO=̚=tKAy+j~΋lԮ 8/ϼed.hoɾcul3R"*߸?~My|j^~K:)G㠹p؝Ex^: gĩ,BW,Jgdz/08wPa% P;Yŷ?{ mN} ^}m~mm\g$CJľ!Md૗XrxNѤ~Q][ņu8mbHJBB<6n$5%]iK`M!PZb^i~&`ȍع$v#f9Q|Rg_#}{[i}Gx~n _ˏg%Qpװ~}!Ye'yG{G%Ozk({/}8`5lPFߦPm̓VO~ɚzz+>w &7뀢9+/]sNL{wNzj}h4[ye۟.Y<,*QCsPL ]ѕż3oe s^yWeKv~(Y2"=ox+%ٷBHL#<3IQP7>gY韼J:iOuIWt9vL&3zBCN40֭]#5!8+xf~2? -R6f#FL@DO!BSy+.x*r ]6.'N2b0)蒦$r֍1ckt6Q#ZMkѭ!$Be[N8B,:%TeF7n4o#k XbbIݳz+ZV]փ" k+X u+YSBf! 7i䖅ƀDN %. ~)1{M7FʶPTLRi}6[I]{Kְ`yն>tr2;w$orK*ipXBѫ}}>OZi r8Î\}ܞe[[]lnšXĵR]i9+[J]*V`b;Щ/c[:gFJY_mKBXsq8^>馳0h.3QSLn&4.O JcD`l<{jR 1ۋTTn ".>Ҿʦ9}3h v}?Ǻnt}%iVzHp{zsy?5Sj{YtyYVngxi7Ʋ֬Baen8 CW+烺}W81M-n^d/RSut=rXFv:rKh,'0 7w~gQ9'=LH:B]=N~띀xu\"VcQi9?^NiNk7S{{)=1"?1 DC)"L~k s-<,G<n]V4]xb]e$h MW3a!466x0446;M !Fl'~W1婋u*2Ŗ=QPb9{+n~|?g}|3$ZPy亷wnbnd|Y~ bأNr3ВdڻAb^{w6jw9P?۪'q_g7o E̞/Is!{ ^{<-n\rC3&'bڪJ-X#Gӛ*xIgPק|$nTݻVL庖-vV|ɟ,$V 'N筽v>؋Jԉ18]֔:S8ӏX"6x)@a3:x~p'_㻍:)f"_Ƥ{RX]|R]i&@~[bߛr]n{ruֺuVy8s7O{M|pMOtsIՇdHAu߾3<>%:UpϫKc,n75 &NW`la'w0o^B?iǁ嵮#An YYyņUkiPX.֯^}ܱnثQM%`Hugn\ʦWyb\pY;rէ s73t' _}ŮQ88Lo[7|Œ>_DKYQl9`V&ؓS(]ACțVwOMb1$>쿾oV30v8 Ϙō%9LCQ߽9 KK3Ջwmד1A2w"pg _$wO|\f3ɼȘBt7+Wfժ6-BZʠ.QR =?87ޤ6ׅ9j$lZ]ȗ~̃mZvoOiQzWԝw:zX(rl/c{՗&r ϟ&r}7&zr6 9|F7UUq\ ڝ !đ1ywV2&?odtwuczvwPWN2cӜbM9bf ~S™FYk̙ CN=ؖ=IV4Ǎʐ6x_3oEⴰCVz~:|p85A>OyR4C<z[Sv>UNgیwaܸU xf[M-&:&3M YTz1]1a7CxUq=ٛ{)ќW,c:N>iBa5LĀpFdS2Ma`GۙpǷnZ’ ;d nƭ!a lzێ}`<UuhD.?\@8yf@PWra7< Mܙ|+vgQ;8g)KJ/ܽ/:;^f3cAwS|tn{i. ךnOc5O3w~WՃ-j~ûfE_^{p;9 x$wNzO4՛vB*>&bFT+ny_}Q`4pBuvL5/5!8-|ymEbAYX3w6vxH{{9 90Tc/ƝޛO@֌Y --? ^9=[5^ZøXq-+=7 54TsWi$םɪY(i2a>7| on,dwVaW,~Hc;EtL&#BPCRgcdX10]ʒy˨;=7e6m8?{^E>p;s{zz %AjwuZ@gY{îv+k_P*"@Uj [gGH@;̙̼sʨk+@w+.(*a`6lnlɳsfAzY9~^GޭVúˏ_=|1W7gxv(>\ϑ߸>:1yh6:E*kk_ס+9xl¼Ϸ0sP,AvM"%?eXxkRIH w^W:FT]s 5{ &x{κ+ 3Wk7{ʰ~Vjg8 WS.8*Y9 _NX{ lTyRb93՜Gz{ Ir咝a4Tֻ8tKt4JMr1Ͽ7C:{T4}p|3sqd'G2}lߐe/˭S.cro* 4AsAxm:~^z3-/]]*$ı?5k.30uS>I -e]¯clt5aW\@?s-\XG4|&FBGq_͍x˯^4\Ybߗ) | MN\_k^/^ [p?vdlv!22"Ʀ2ndlYIp$t@xDAr(ք#Y^PL`xC,~>H7W'aAtLtJ5>[%ÆJ¸IaKpLgES 3SênÆU81xPZvWҼNh:`hgeۏ9Rmsش7Wc%m)2غӯ\0P9VU &sy6ZI>c4_Bʅ,+͸Ñ5/ |7ڪ܃ɁՃ]j}z`ZE2(` IDATIƇ} Bbhڨ'90:)lPgOۃ`v: #^P7$-pŔ >stt2 ρ]71| coWz\~^ei[.)aۼ9ݏIW^˔I= <> keIvx:l KE/[?_Lچ|&wBֳ,)I\lCDb.k60sw4/{/u2KQ~}ܸ g75m5b㍊:.ÊMǃbI._  Esٴܯ~ FCoG>sٶis[Ti$k,9Ae~2bfUyvrs}?00ѹg',5 #C#v0:c%0枤vAmzэ:uE@0|Qy@A]ӗrK1L(l߾^YAkB3~}ˤGZ|pŏ``>x"UP#Gq𙬜fpm mħF1f<ҟ!kdam- /%`żxqg?,]Ǧ]+rpz+l*@fp}Lw"zux)+8h>޽zUL&V\T NB I UsNO(^~=W?hxLA/_FgRk]k"uZz.^j͑Os, C.1 $44UVIMڿք3yN؀d x8ڽp"'Fe3Z^oOZQ9N f䙣Z8܅sY55X]9;{|=s rtL]SNBPL_ }g䍷wLkbxV >d뜶d)F ڍ)Zq~r=;a1ua`VV.\Nc Uasuv2q`[rmUPV~VSp0b|=wKd_R2G ~&~Z ~+S`z&wxVHԨ܎a_S9Yvٛ1k2fMB[qAA:׍PèƜwog5M>S{lZǎ ogdR"7;XMQ 2~]&)a7 kM 4e@m&Dߞ]djлwMKQWyp-^0">ٌٌ]67\WQ&1 0Z%%DpKA6RA7 ^6ԛxoG>t k6on`xTk: ^Պhii9--bh?0<݃+? ]>AX 8ɻBy*aQQV*A)?}Ӌd,o>2+v| KE6>`"gߘcl[ݺƑTN5Tb )rqOA`(Bn'glw1Ň߀k\x ;Ko~ӯ BO ]-7(YbogFGZ 59;e VRE{-I9c (axpJ#UyK(7J(,hEĵ7>ԑw>r>vwbIs#9{dh^j(&s5 VcC覎ף^[M"6h|u4cy{sh4k-C̑]Or1DU3|nRʼVNf|0 Ovc/zm~m%9.Q;1hPwk78a5;w ҇='ݥ>x=LwǦ#.hd~3j{CyoK0l)FC{.̘.o ҦaŚ\t*F{(s:q]nQjnsS^nEqi e}\^՜t'O4M_h[ i6k}ohXzjﳲFA #*\t[xwogfr_3yQz8c\[[y B$ SyLvOVJ8);bBgߊ[ܴ%~*'jU Yr t&0fK!W:+Џ}nU7X:& X6L 8T+:ykWO}t(RuN]fV/P,JNjYL+N|AW\1S=qbok_^йFVylOKw$3fP$ :5XkC%w]^ 99/ª$lgǺ}#O%>/^EBt?i0}>6|^~c[/ol&}G=?AF26f6 m;W2I[g~F 076F<^ƮZypL.z}w~Lռr=):y_V=?FVKՐl  kobB=MZ伙Ҟ.Pؙ<Vmi#[QXO[s侶=p[^ ! B9_:a3 Xt^\QPR4l(hOQVc ݳx6y/P5sdK"N%FƻykK S<@OrKQ+;I]t,3^Jյy tS b2s0_ow&zL6K Ԩќ;ރHd .dD&%Aŕogۓ޷q/>6cϢ]AoIR}9 hn&6ORmbPt]LK FY=\2O۞g~K9Ԟo5#nj<R)Z]D1y꡴oC8<_-y> P&n7k6'0Y s58u$Cb)d,Òp>F7S9lꍞMYB$~)7^'7ǘ>ٹvsVd409=XG7@Ĵ-nۧjpumOEyh/L Ԋ)tPYͷ?Nɀ2)G L].;r3\خ=M]Ess sS”Gn`è%<C{YXn9 > UgS;_dI ֗U;eUx6,;"1W^"Pr%O= ?KsEnmbt!!2};CQ MvNڎQNJgۜgrz@yXd #x!̽_,r̯b)IԹE֋˝7/gڿ\v4cu$4.JcB97suϦWoF/f8: {'tLG<,o_Q/_OY>8\yv.xpCзkYŠۮ0zӘlېì' "-8s7d:\ˤACz\~ʣcZ]l @[t]g޽tp4քDgIcM4^}E:|"ULYә k~)*>}͵Z*9LdB>43A5sX6 Ǡ 1pSXl'0<0l̙\pF_j&rw+|a'"TG,.{/E =Yqug_سk)Rv+Ny3fMTJdq\5u* :-j矢:Iڍ# hdA ;?U >eꌊڪE d Bۼ"6ўg]S'Q]\9lmjSj!'/=|)V&2e\ρ[qjX{-^l %}AԀ 8g*7^w6=mU2>\:m{v-绊 0;UNA|߯[:{fߖ*(*z2s  RI- wC7*=z!C.nx/5eɪr0)>eُq4N\Ի+>rg-m٤ڈy*ο{ç9ik=<$O~?}Ɵ@QĤwB\"wױuL]w!/H"kv:OϘ,LV|8qSy= |m9u[#ۊM$X֙ěbWXw68ׯK4|0sc/Z,-ùiLNoTlZEO/U.~fViG k]^Xm&3%눈9 `.Ecvcqcxx<.,f!<^7KpcQA+Qg1XtXUBq[rX@w,qUc;ǥWA|b !{A`,vЌ-(++TŊ+$;k?%[0Q TyxDEE=<ڑg(`:;cħHLHhr{Xք(k&Xog%FIBԫv`-P00th1^QUEE(DWԊ}Ѭ@Rq\1_Zz5Tv]k EAU*D ];S|:o൅ vez f`M Hp'}ؕpUHY6Q<]9R?NnHj`0 <N ݤ3[,8vl6뱱֤+B:nGgǩv'0Cך&<ք} Łwf~Z&@7cy^.S:eP6#;eF)[ B=uV9%8h9xJb={ucxw?U lߙİ#uΗB!8#uLb1|j*fү )yamm@++Cwq7렎|:%̀䁵ҹ ϣsǎ}J`M!Nx =C7epSK(c6i=ˋx\oM߼ēYZTppjL(Lg%URP8}ʽi( ?09ydJHsћ^O_~cW^m 5 1W]%(+-߷<ʬ WnNlf:|d|t7+&p|BO7{zGym K!BFI_N3t]qfˈ6,85jVk8`IԏjJtPq4b,sbY)*.%"2abpJ>[!N *Q/b;K,{/`jT^ɷ'r0oW~|/ݰ g }k?>^y({65J#E] Gsnd\u|`Iτn/!Bqbsرۉ.]fX%ӂ?ٱbayg͚,4SGhlFI)zk[:q K{KBל'p3tÀ3nco !ڃA}<(2z^gW_~'_7GyDtXʘqCV>9%ۯW>K+A7uGG"8!{ }˂S%FoLm_Oc3seK!8O Sn[Up0G}#<" 82\zG?Orn̈́4{'=ȨHTa 4g> иeŔ(./\?R @#9s`o$ GҟG3 TӞ%a%0Іj2QV榬Ws00)&n/LE5Z!NDb__QT@l֋ffᷯ2JFt!O*?wu,A8oN~s Ut6g'A/dǎˆ~f/kN6np1wA%AZs}3V6pDr3aΙz`Ky쮑8=s1 ` g.yB!D0ZQFl8lu-V0JJ(3 kQKHw1ka<.ߧ ":pܕbq".6j&&&C " 䗟 0t$^EZZ !ICd.^_]FЧw ud|8Oۧp)$W ߽(:vc4t4VmXpݸ_1d@o?3hzvGM)!YIeæ\jj}"P`Kx/h״F_' 0{|@ӡ0nof_RnUYZ?|*c&pje[f/!Bq2|01Q@25agƎf,-+߾O'7e  `Pw)=o[~N K.[n7>6S-!ޔ qj~%&' b H@m$]yn|6w :Lf` ۆs~YKֲqgxkDFvQPTrы )2'Gۆ]_<?ߛZ7'?[_cMƻ/%_CŹgr^~R./ډOb̤J ΗB!8ZT`1+Խ=-nb1_L+2.թ$%U ^<2c(n7q>ay)*Y]ƶxXBQ '@؇k 빋Xn'2>zng00HGp53nOE#I݊lFY'?mUNm-1[˫$ë᭯7xcQ9ԌVVT|l^@C3Ŝqْt(Fk ::0<݃u7F\Z˺VB$ܥ,F$ 0z{^g{jVX?-exB,o?y*a݇/67jX?wŻ2'v#hqWl݆:^[w ^:/ ]'8 _GŚ/Xn4:G69ؤ"6h|u4cy{Svl~tj dSV_}[~4档`)vf|dߤh;G1FA+xx7t{rX-XIw+E$q渎 V /!BqR##w񷌞͹Ե1nūD08ct wn=*](XB '[>+R̩ d<;.SB3(Cy5F%eV-MIG}/6ؽ 4;pRwĄξ9i9i{K80L'NEumbeqkq:TO$xd8L`\g9_B!->hUxB~/2j-]/|(n6]xi}\Yd25>-_%}cxv,4T',/y,?گGkBq򕐙#7_[Aa!E /;Ǟ{'k6c(fc+ 5v*cMUKK.K^yϋQprO4(d~Ƭ>sGFPk-Nf`+^t[ݳx6y/~q0Nn.l-V-O5sdK"N%F\YK!BG0= {X1~Ws;l+MSʤ oϻe-i%>6!-fx kǛҷ^]= (c躎U٨> hS\.tye5!h~m [%ڢy(.,S>ebb%Ci߈3)⟟g🛯aũ#-NY~&,`}͵ZR߸)/,gO~\vT0 qs( fYYK!BYP:^DỴ ̟b ˘KH k(g,4S<#˻6n/J+ٵz!*{ALY\6e*c9>Q50xb2(+ 8l,#Q)J'K0ۋK{\xGf%};BmS!d zJ`M!8T;qDv:B!1&4MCUjs~߉J!B!Bx~Ph{0o: uuI K5!B!Bä( # kKhӀvՆMѴ kK ?UUt2ƚB!B!Ġ #Zv6&K 08tXj B!B!a L&A!a`@Wq|L&EkB!B!{*K6Zg6C5`M!B!bt}=o?0aAw !B!B Pg6\i8~F !B!B!1, !B!B1I5!B!BqD[X'B!B!݃&l0 + քB!B!|_URKӍB׵(P@5 3or,=[5``M!B!⿀n_3/xбaQTӡlY#BF͂iqL aT?;ZP+prb($ 0<5 քB!B!qy\JndaSpGHii,:)6I!1=VZϼ~-cI&B!Bq {uy w?FƏʮ:Bq]WC~o+qތq\WT;mDȄkq6l܈0 22ԔtBCB\ քXin GhB!ks+O^j-Tk`M󿧑4bG}q?CUn9k0ڲ_Բ,CB$1ㄓ\J!**}=FdIdQνg)ޘ U~ !B_aM^}a ". J!jƎ ╻hu< IDATIOaNz$0xڦM쨮3b^w?cٷ_3.[BW³7꽿XG=z;(*fUEWUpz[-U:#9vB!p1vpC 4Ur84)6.789ef!NЋHJNFQ`r֬^d"33,̞Ë/HnN.vc@eVK!GJxq<Į׌i٤ŇciI7y+ϳ鱭3W_Hs Ө Q^ɱ#BћGۂӭ;wVKKfM0Ay]2g"vPWWGxx8M͍-yqsٰq=ח@BB<l+Cij̓0ƨoy/qM w3ipz].,+NW+.W2ㄙ,[5i%̌-B cqt:_|#x4KDGfdÎf kq8p{v= qć8 vTWQS9ؠ:ݹkTӈS=It;ٰol1Vnpr=Œuq+Elog !61qal]uɳA_6׳m^\cHJK">ҭvl^ƈ8m=6g:\X@G&WЂ?#ǎ&վ:g2\Ԭ'S -QΚ@D`ߌp 5l?cx\[NEMm[Pc&$p1 B! j`G@;zymh.rb;~6xN2Eϭ%۹|v0xc麎]݈H64MjtL քbS ߾ʛOIypu L%/KZ }oD{)U|;~x~\9HGyoҦctɗ-|k o tu3 Ԩ3&s OQ&^/y R~b3)bI]~6w̌WJu9INz/y)N|ʧ%`cm؂B_krq6}R_|+vX>P9,ӹɸ)~; Ȁ[8vVֽ865k'YgsWpb\ !زxy~!#Hنa[ϙ11 o3cxuf.5fgU0 MkBjx4`M![Dt<׫zY%WM{o%isg4^ʖs!MKQ)XS07[+[W˼.d~=T/˧M1h!Bk:!Gtwff zoN3GxٖY=x2ox=N'ǝÜFUU^/&6gۀ*B+խذk?g3þfc6m#_,݆ș{1oyKJ9cy.扏ْsٸ2DWUaʅAA.|'?߅n ~]mʹ V=>'-If&M"-.bJBg-6\%|TRHD;Ch쩩Zg|}|ԑ-\x{l\w'աx2`d_N L.-ss&]o*.&1Q B!ıIXf y]Gj сޮ s=`a t ky?[q\z]dxf x;9^rltk!}PO;iʪDz,Pٜ:3dǔ 4d !/`3OC ;! I^JXv 9cL'³djo˯ 06f f mKބR姠m.q߇."oApL%G@} ۻ\*#olLoK..s$gbS@êy>Fz{}(ͽG%r3ݿs ՄBqq=,8(o?h>Vhrm_#l\'va Lc˶V,H}?~DE0wf۷S_s&wa!;^˅(T5!8f(?x>>$F Z_R/eKa4m'tD#nJաc">}񱺨1^֗mfu2O$'p&&wv}=:&b'M$BU^ïoz^úM訌NMUՑ$'QHgj :4h.*`Jjf*)Ii((H/,L* MbHC\=8~;:Ƞ[ #%9y|5k/eB!MC5 C]`8B[}}_cgrKaao)HI.䜳瓘dnd2( SQQIbbҀQP!8fmjA+Ỷ{g7 lJTN&l&J'sF"<^GavtS S'6Q }Pߎ12h˳< Ge+x'qUu-Ghd!BfЀdPuzjrк^0Plg%qTNE ([w{gsj/V71ydt/D4XH ?+|#Cq \N:HcȉN7=kߞ[jdr_dӖbJ.$nOurj&cMJ )آ1ed)y}(9L{:6̏Ss.9c(c| 7~_no8f!B ~4 t<`\ؐ+7;&6ijJ)i\E !ıhfoP6Pg{4R9SRcf W_؃nc܉}1E3z;wG f ШQআύ7EL Q6EG5rcfxב_梶Ď,LHơ׺BJ !9SHqmX#=(0OϮg_ř1x˪~ +B1\GpʷX[Vё?$XBptAEpb`? 4'.-V4XrJB^TJ(qٹzt֭C~`j1Щcµ̚3Yw~óvAFڨ2O90 IUJ 6֖&o^Dr%tfd񱡨 ?sXa0~H_z* ъ͸zYV J(#B!)0g#P C7{ UәcP ^I&Õ텟oO0r-s`B smWcXɘ{1!TOY@-]Kσwk.'?b2w5PN#ծm~ ww}_Gа:=~e&&z:v_ӝ=|KG Y;O!ݮSS_Qc36؝JhWR*F1- qcGoo/\<0uo&C1;f\ !rzv8@ ?v Wa>YS3b vc:8B/1քb9n'CsSm}/KS.EO%j SNkРFmׯڇWP𯛸$or7%lwZ=:'ࣼu!h q*wC:9 s撩?gqە|V=K.]1"0ķKVR YQN3yz;i# 5PF)Y7^˼+e2m+Ye@IwO9McLwW>)AG~oCKŧse߯`Ǔ;~$A|XbrɜH քBq̹r~ Qs~de5Muu5+m/v`at}016憧$XBA !B!׾o;"ӰNah>cTlK !:n^ɣ l`x<N'.W^cì-v;6 UحܫB!B!8F]x|0[I"cAEM-AX?o<̱ 6s͓+8ch;-_A(l6V+z~_kB!B!ǰ "ql.{;v$lA{c5EQ1Yl0~\W>Sc4,?X6/~Tqi8oz #VcC%B!B!1.+!'[eW8B dcQT *&k[GeSM 'GsX&@M}뫚$k8!%8Wm`M!B! yA<5ڲm6. ]Dcnt>'oyﻭ ( &U!ƬH^9 06H&B!B_Ԍ0NB z麎i zzlbX]=Ժj-T քB!B!a @QTUl6>KhD$XB!B!d}=HΡH&B!B!IЬg6܃$XB!B!Gı !B!B1I5!B!BhwKػM5!B!BQaZPM6ѩd='@$XB!B!caĞZ\N'j!FUM~FD pM5!B!BZjZ61rC1%|>ٹ}.g+#FC?\#Y!B!FZ61190x=qꉡFQ aRJ(b $XB!B!a{뉎ʸjoOQ8jk ҡz+ B!Bca躎نj_ يC`M!B!,a ^GbhQ[65yT!B!Gad(i=I&B!B#j$B < *B!BA7zkG !B!BqQP!B!BaCב8Mذq#>ÀRS 9r%XB!B!đ5r5O3u{&?B8ao^\o9=,[5M-{"8$ (__{KB\3N8ʕ`M!w֖ojw#Տ$LLd}ȖT=>8aI$øB!b-ۨ5$#}^/-lٌ;. >lr{4pDfų ~rR]EʑNGϠZuBGFxg mO<|\@U~Ŕ:Ns 匠vGB!b03Z3<>w-wb;- ;:o/W= 9}F~U{'%Z8Lf̪n6*?Fw:UPP`Gx6Vl )9EYz &,233{/"9-B au/qӭ/RjM| lېW}Ǧwyƭ4?'.L~5 /=OG?Ask,9Vkȉ䦏cdչkYyk>oy蚩DkB!8*rOJyμ^nۊU:)=l͌L;cVd)_[ΩN#(0?AFZ& _OfFʖ`M!*oJ[!(Z~<{,sYυ+M"ySfiQ\uv3qjg]ϲ3k!2葦O>dqZX : k(oC1lGG]|.h@=k?*˅bjj#"<@f0e˿&F IDAT=-֖J& :j3~rƁRStf>^"z/D\Nwb}Z64ԭD%-s#S(֚ חzbWz5a %! Zx@=ٽG w(V_{'=e #~!Bq (G axyk[_SL=i-.ģ9 rW‡fsBϧkYU؆XHI@G]:g3,aG`9~Qpk]!մvBFN 3s<=+Iou~O%'u<|M()f܊XS vhajV"yn`:\X']kr-0T4Pnkh؃‰ĤQhp:c2jDD$NgaXis:\kB1di?g)p#Ϟz#e~c)~|mq~* nO;k%{v]8X׊3G AL_kީ[o[sfB!0hMicxzm%yl2H/P6lL:=oBV9!k˷!&L8.'ނg O? /sq]ˋ?RQ+X˹c{lkRIޯhkh`E%p|mN=yvGyv{k8g^˝Wh+ɟų ;am5-Soywcܷ~1&;G,%hfS^RDe./儨c%>R0 MZmx<^4͇*$XBc-M,W_gaE ph~-+eU2?k *|KӿO^4fͿV?d+1̹rM9̛:۾[v Ԉ9M$٣ymԭ-~!B1 )Kl헑JԷ5MTHWe%va :NcjTkI<Ŵ5baQٶ7K|'rIamgo?oVԬ⩿''Z<#WŞ0+0z1L9\ քb2KfRW\sg3sZ6q>o㳜xɳU4tÀlr[淩޴U#.Z ~&oVz 7=č%}J7]m]؞x1O}ݔz-躎B„LNqocD3'ԖP%kyP9@hh3G$F+_Lҥ Gkz̹Sq/y.?cҤ U’ ~pdg5c}|#bPݵTWW8QMV..^|nN ل ^/a(/!Đ16c>A*rX7J,Xȹ~O Ӭ>gS.̀=B_ f W!~Swx!{@!8*9sdT4Z8 !B?^֭)|GcgA>49ĪJ\&9a*<괮2"Voɴ=jfPYpi\x|^|ׅ21nwwnM|Z >4řGh2uk9\0Q4YSrD3eT4lms %zWiZm U!i3%U< W1-͊;ini~7+>\O{;5/>ϟo?W% Ь9Y\udeۅ-0-ZYl# (ܙqlZL}ϙHlqLxn\.@H&Cqʳ;.oo ;֭{n?_GSdLM:tLħwփCzJe}&UW_;D[,7;a_!B cҦeP9Da&|j94O$7X]+i+-cNfāmcƳ?~ ؒ[]3>e\"c{^"X ԉF!hTo^05r ׹; >i$m ލ%i JjHLҗ#};>w+mmx4d^õPrS5lGؙ<£c&a&_8RRgp 9$&b2[1L(TTTXGAbPb=RrOt*().%?5|_E-l=ar{׳ E\I'_ScZpfǯZkKۀzyN'NP/B!?h`OJ+n-2'OBk1߯=)rNNJJ֔{<~x(]SL3TS:G?~PP}a^tLhH@mUFܞouED)Rg:dN¼}1㦴 yvlt-_RZXsLZTBnbRn60rB*9YL6irm4ғ0L~ -TKNBGLwI4'ΖM-ܭ]\-\mجǞa u{d͚50m_z !pډÜs.ۋʭ'‚-6t //yz= bbFlNwjPi~\;[ELANMmB!2bAћ[^WHɓ6!f`z-E>Aab'{Z*C~@ԅl}/EtL>ωL>Q1;BDf &%eT逯t2N&3΄u@h;i4reX&p/ҽWsδڨ^_}E7^ƹ=[-(\C#Sm?R i ݍ\kUs'O͛739gʀ7-=քb3$;^ԠOAU . ,¤D½{V7͙a٢Ҽ2Ʀ:?l5B!B0j䩉*}"4&s K OgE[J(#RDnŚqlf5!] n r tˉ<&G LicN6?B;!C,Z[yջ<.Y3oZ 9= J) Sжţ7sݭ]K}+&fkȉNsD+ycэ,zu5;=όM_osW3=`_!B {MD՛)|#] YY$\ԜMvIi ' ͤ0`S..L2ώ[0n|=^yxe>٥McIbKL]CPǤo %8XW2-F3=ް~s? ^<0roz ̘QC*@2 ۍa ['˅^0'c !eKnGg3jBcrh&voHjZ4PӸwqBྒྷ}]k^AnⒼYM 즶2i!}'Y%h q*wC:9 s撩DqpC`/W/1vxbZصe뫚 T.=\3y@l0/B!C >@a\;7`fN2^SLI4o=ut.SGg}{ <[+~E'9ʎovsKZC%+4 }2o rrH} l.Woif1+PGy4&Zh FlEQddM%s2YWG5|װLEEǹY݇e̹\zr!x0 0`x0Nukpr]$XB!Jy>i&.~Wƶu߳c]Eȉ?42{Z7{x&Y![‚+s2oK^=Um[V[vW\ 姆dGe撷xY^ZͦUlꪷ f>d6I!r !Bp"SR.S?&ŒȔ {)d$`[S0hν!<̳K7%O<ƾ2t}<"}[_uJDҩ_pAG`=9Sz|JQTo/U73!7P_<Ɠ#^_Bw)YU8_Yg\uW/`̓6tr5<^/&3-EEF.,F,[Qm͘Xؽn<' هǽzbbvn+WTJόj f O>ఀ7ްفlv2[?U~+&vbzgĘX>\n ?V֌{j)6Jۮlmƣ:˨`ۍ{l?R,~;,jӳՎ κjv6ѦpE0:&>:Z况KLd2~K¶җPQ@Q[xL @ЉJSXsStfi ! ;bB!āZѮҏGQۃ ]̓jBUutKUEQ:mohvRܰ`0 <N @>*łnfA=XgfB!B!k6 kqiGwR͆j%88[ϴ`M!B!GKC3 ;T !B!ȒdM$XB!Bw|U9w$7{B{qjk֪jkjZ[ZZ e$;\F}rϽ$}25 8=#׽158m`׸URb IƎ.PQ.c=j=˲JUaຮ*+QgE8˲d۶SHIՒsU[]%ljuti'*-Y0WIJJNm۝:TXc %µ|}~Uo:NGNx{*5=s$5l E<$)-#K)i2H?S>mۖmx<,Ӈkk`hٶ-u ԰CMZg$5:(!`&. Z#X{UdS'wǟ 5 #vG.b]0`G3´֡l Ԍ1l^#ckU]YP0(u:,c%4IGF@6j%jUanJȖ[i`Osb1lԆk 6*'PRmlVWZ4\E)e)%5CgjɂYJLLRJZz]7\#X UoܨŢM(溲,KEUQZԴNIc亮B&%%%1Fdr]J]@]ב#feYrZj9\c*(;c܎8t m[k~>TP?dw` SAة=b7s]WK-ђKEe!)3##.ҠuAV@)wt=ΐEUY܎7U)$[YJL,d|n}?L u9|32$#-ZHozvﮣ8&sHV.ZeU3zdE<^_U^4d:,VqWN0 %ZN}(8N6j܅ZQZM PqjގG9EN|RW#5moݪҭMZUfaW;GlԴԽ/Nպ7+!Onx wCOW^ۖʶS-ے$K(W+_`bv6Z+6Q#w-]D eI,Ӿш#5bHp zg4vX%&3O:5%w7?Q+A}FU$OZ֗3kʹWuǬɚ;S)K>hnԏ?{WH @1uu[k353unJ7Z5}6a~6]MzRv ^ Q_*QC{QjՊ[]fQ,YjQYYAF,'T{ >tzEk:7tbn~ӣ\idѥ7J.4 NѿUwа)t S UO;_E IDATVa@'7zL2dvN|.zk?zN.|  )Ȁ\I^u-Η , +jT(Ԥ#?Ӱ#Խ{O-?` FQ}VJ_{ӽu w.]+=d':mGI ttXV7AE5jDepWWMeK5wJmjRԓ<| Z^J*PtyLH fhƲR5 OFUΎUt歬PIVA;S~Eq*<(¤?ueZ6mT^Ъe+ϩoFm!knf~~Voׇc~K]{>TDV7Nh|ܼoҏ5=r=w]:E7Լ6__N-Oа=\e#q$I~"'&˶"X_M¨3N*ܥ SvpۨnU_F7W\Ͼi%ohǦ ڭѣ+:TFu˔?Q˾|]O1WE26w,O^K2 C?ׯ`l˚Ru\/POr^]pλ4隨S8Z$V iԌ\_\^:unJ7Zzѣ7"vϾ]{Tӻo?(I3.9O' +RfGz4?S%IF5ݧXUtg!]SCbMz 7ErKEW馞cit¥ilGɽ`y[LcC9SJ+dwֽK֦nͫ[GG?ܣGx]rthqeK]Zmi5tK+IPgCJ˧WOS}-pV_c*45a^\H)o59Ђ' cr5|#z\J}~D1{f͚Kǎ1\#۳ןwMFv|ܲ/HP~9pݲm[hTGMv@KW*f$OC _>=Vv kԆ%_˲sգe{g;ziV\O/]pty-#FnK˷#2ԶdKrV|GTh\aο-./˾r=ډ/)0B?v}kd+F,NbK^=P]~b)a}>w:3wEMCW?Gо~]r[ZYcueFM\\GyAVw=qrFI/Ԁ?Q6'>jejqL6/`idF1m73M⫚i:\|9țK]w3I)yq%uac+`j9glɫՕ {{w=%u\w/=>8:#Vސtu~럚Vcuə+'(VQʸgWoO#Cn:JYoLT8lx-[ՌwzQ#4z_o_v(1Hj9K=j( uU7G^'ףH4"(#_k)UU(W|Y tg ķ!T e=<~Խ4| .SpJq]*iFhp* ^H3&c)yY:g6tw*=QCIE#RYORtV YߤJ~v^;FqE5 t߾5mE4TZR}KQ͛QsAE"[zYS~ųJzfl'"׍mטȖuڬQ2_\yΝy^q=z08DO?VY:̘=`5b>iM1cF.X<sOCJHUj Ki-%KR~~N[P=PtU7OCg^T W(zfn,aմk=]WV:;闺Tj%q4l ]vɏ$ipmoj&I=z}\dd6M6F!j$Rx<,Kj0``kY J زdklTHݜ j%$+̈́޼vݙMy =i\ʺtvc%(W6֩y6icmmTye7Vr.xjb522 WVu;]'֙~t6bccQNvlm[QFʴx toEz)F:D- }iw3h&㏺e & SW (klj6_INjj[P)mig)ڶ]*ZP+_.zEZFFsZPmձ7ݑOO5AnSxn8Ɯ~qa鲋$ǣi_}-U6h $)\?SR 9A)Q(ؠPQ`¡FB GcdLm[TzzkZ65蔼/Ȗ]Վ4dmjk, I 9?0횭D^)ڑ-wM?EK?н-6)`/qI/- +}Ejd hlwiO -+}Do`g #$]>PNXP;F8[)K *wv2?8NLe_$I6h' ݋f !ݞ]褒FmY7_o|0j.;B]q~;ҔierՐo_ JKԴ>^3aKr|cAV.Ӫm_=略J6{4^e(#) ;-ҒM* w8EJRCN{=̝NwLt:v`0qܦzweuVeCxndg('Ӗ䨲RNmulΓ} kWAM;rwVǮg4赵 NIOyy4z'[9iA9ExckY%ӅUj'WttXcu[Z2vgLiRCd)35bλsi^|,[V[Ҍ TmdejՆiSTW jֺ}9>WZmz6.Js54MFS.M-_L=vZ[91yM=׮2Q3tZ>9:]6,E[/]ͫf(۠<_^gM(u+>M؝9v lI1-PMkwْ ת:ԞW$hcfͬoZS辏Akt%gkť[*)mqotCs"wAv[ܾO^d,n~!<y-IN*@G z^N(2ҵ7"'66lCX/O럟ޅv8Fo?s;D_[.w5d3uH%gzvȚwAW] =2owBHfzhJӮ\cGsu~o]//Sx1FpX)/ uwkЙ%w%~Ry߭zT8'Eh/[UVڀ[aI uIԂ*yvԋ*w@v`y.Х#ү/ќCհ|jWL糂uG6>]w4]yd|`q:ilOe{êX1C2Ck>Sp=>v/GgjWJUi5j_\Z O?>m՗hڑklB\5U՜Ihʚ|g'n Һ{iez/P!RX]qMo}RWRL'l)#$IQMmı_-WkGhDDJ鋉3<8•q[ 9A_=UWyf=u.yN8UO?u!q.UΆ|ݨ&TH\mY7׵{ם?W]?a'O.,DD%QL2ᖠ훹u֩Wb@ܵ@'g ҅w>>{MϿ&۠ KfkÒe;\'q.8er|{⪶n+Sw2KFߗ,;ҟnXGTpZΣsȑktwn[|L~8[&Ev]Y;ky[E^;==8=5.V wNR]y-[3~G3zw4p¦p~te:tΖ!VF_yy\35͙Nف|8bgixj/?^S:AkVM,%K.T6hkXS(knUNwz\l5fs?l[a}Ƚn=JMeMd24+ޕJ(5[;wz*%=WM׶3~r~Jƾ)>A&<>(垤k\Ƒ =.հ3$"Ѩ***d{jed!LmP/o^@aE"AE"!1EՊD*بeYuKbd5gkN^8gFzd\Ō)kQ{nF6!*+!E9]TGҴUwMř [4NF5LxG&|JIh>ߺS;=um}~*X^kKj4^fkA"XtV4(b'*K2aM+JNM@Z)y'9ZS(%e0?C,@c亮EԫߠvKJQ®vjnZ0’/)mI VS0}IJ $7`ChiGĀT7IV|L#orQ^m֤ 'x٠5%5 RsT$;V8mf˟"GrB FZF/II,7PcP:Kdv!m<N+,PCxdVW8VII|r{V,ɲ7%x&6,W=OTvXG)Kcg u핡z\= eOMZkV$ W)[YHKcU۞! d_VyRS Ƨn;<ε'qmU|6aG'51EiddF54jG+ͺB1ggVs[*ذ";8_5hwd9z5td5߯B|:FE#avz}>JLLTBM?&Xt Wt^Xed\p^zCRF1Y)@'e\N| ,RBB~[LkN;;I굛E3iؠ9'iƆ1'(SL f&Ӟ ELuzI雚o$ɒT%15;@ocj,Wy}X('/GcpP"Y65@'UrN:`.Φ#lEg]d;`9#f\ UI 55vtA%lTb %kieYJRuU\઀뺪TZfV۰,Km+)9ET-Y0WUrXG'*-Y0WIJJNm۝:TXc 6m ײsUYAkW/8]p=kJM/B5` lzCx$IiYJIː1F6 `i۶lۖeY>\#X 5۶.m 6j=Tء#iZ'`ֿ[Æo 7d"X`o=vG8q X@ā` 5 k@8q X 9 IDAT@ā` 5 k@8q X@ā` 5 k@8q X@ā` 5 k@8q X@ā` 5 k@8q X@ā` 5 k@8q X@ā` 5 k@8q X@ā` 5 k@8q X@ā` 5 k@8q X@ā` 5 k@8q X@ā` 5 k@8q X@ā` 5 k@8q X@ā` 5 k@8q X@ā` 5 k@8q X@ā` 5 k@8x;JJJ:8(k ?r@`*(5 k@Xc  u5(W0:]`ISJZzSeYj6Kq7%ttI8jUu /hW{5vhUuF8D^/(-#KI)iZpv\c5v(SQq7BUTUQޮvk й+llW5vp]G6kp<mW5 k@8}:7'pL)wbk7ڪzEd+7kRuMb)93CI ΚG+ YNu]LhDXT>Oe PI^*}Z4騢ݚYVfB˒m[2 ̹ӕ>) ǣp$|F %[<=%el|=e嚕ѠCFHXhD>GiݳIZfEG %Fz XKJV m<[kgIyõdUcG UY|iJOJ^ayHVœd3:mKTe'vê.۠b_)꒛,gE+UZ^`D| f*/?Gi6ipKJnm%K_/\9Ovf^)?XX8w||f:ƓMO(_5׼9l}O)Γ<߷UڨPmB*T$(c\וq]Ŭ橝Q'X{sԥ =-58eg娺ZMMJǜ>@uuuD"*R%ŲR8"+#Cqj)r]W*ci[s7jke:`iSRcckDdutiAu]c###cZƐk+Iu?7]ɖ$GjWVUIF3"3F5FۼGMM2줤in֮k;mEvi^@իWkJNNe[޽V^ѥonPXrdѴ^JIIQm]Lxl|-nfg{Y2_6_+;i\KjVzٓi[em$kVeaJR2lQEsB>udII61p?\}>Qsf=׫c:BbEfΐY!۶qc7/zW2UtLY>&lGCGZ\GWU`*XD_NA#+U{˄b\ )V*WԳ{FsMVR-UeYhsQLB Xu E%>ۅ` pVuUWiӻ{a%%%vtiA%a@5~9YdYNqcI6y%EZniL IpXiCmw>+9NhJ*W(%;G٩yMD5 ɑd'uոW}LvZ7u|F̀`-^N Vj2DJU>QA ?+/JVRMǞT͝6O*Sמy^ |j,%yLVBfbf]^Q#e7O4Μ%Tvj"J>Hy~>snY?(bN*#X _JIY4I3NEA V*,Kjf&)x` z~U~> `¯u9jki&"""""""re**}ExRR0̴4@He)Nɣ+n7cK' O Y)oz|x}PaMDDDDDDDKyI{Qd m}^I4r&"""""""rH=h7CHNSi54bMDDDDDDDDb8.цm&"""""""taY.WsHkAjzi=4TDDDDDDDD$*A58&""""""""DDDDDDDDDSAs'Qiq 8ST5(, des[t.-J+'k+""""GU+Wݓ(-,%IJF6)ڗ[y9_c9B{ٴf k2 n?ٻH/ftϿr#3EYz[w"l$ӑp|{_t ߿r.mXK')qy_mo(DDDD i Sk5;:Kƕ{Þkd:w!3vⱫ\K>z_nmb5x_-f[}9Aҫs49.7nv1]Yȶ=6ۓӷPc^z5>XJo0h?p<7~zR0tLW~ cD-:w[m0k;ico(DDDDD"?#ʝ20R0L1"l'柼=yZgxQ.iT?0O/چV>ԡlśIc>z)ˏ(edž|7,3>߻|9 A9&?4,ֽkg M3u`U^gY0.<w$g)/dw]bO'T4giv-+? .*2yV!9Hco(DDDDDb2AM$) pp kӷ)Xcg_scpND_;ڮpd>>mξ횣~VꩠWe { e8`a|->ƴiQ =4m}<[0Iu.w Ŏ/⮉۵4g Ο& ҧ?]]_*d 'E|yޱ8o=͟XϽzΝ #BQz(<|{FoNSq9-hQ`\xlܸ *++ B!L4#qWX<+X`:7Q8o.+xי2̚׍5lW/{{㵿Ng#]8W22EJ~?MЗO.>[W¬7ߧjXAU Q̬3v]ɤM3i,za~^ow"p}OS 7XȚ9Kq>ǽe83|$ng!}g}}nS MfǑ,#Ә:sGKomt\"c!"Y5ۮ=>3O\r%W  u_Ȩwӷ&ipF]1h &7n<~}E3о)/Lg# )#rQ; Cȹ ƆqnC; hd12l5-g钥,z.[KyY>uXbb ؀,kI!vq1{mȪ_V?8[7CVn,`Oip_,f¶i/v_?45}m}p";0b #D?g'"ލ()!Τc6y'2knx> LvbRfC;9b մĝD%i/3O anڋF3,B!ǒG4݅E oMi_å88RjC /'2i I9Uޒ#+3s02ɮmncuh sZK~FUOt+IƂv78e;)|˛J ΢‹2z'f*:`-(`Vv`x:(rlp+ `ygv˜᮲ռx6~o{{Ⱦ'z.Tw '!r,{୵Hʡ8K~/ncc* ڮ{Xbv?"P_{Us:ᄾ]a幤挧ÀfӘlV~WYK7C8}69笆k6Lޚ>jTkq'""""G᷾coElٛBL`/ȉ88vpAr.]፹v%hP19}S?]nw&p8MsZK~XLSRB .s1|xe Kq'}<gʗ|s979d!Y*\v bvo Q}}xmc\_fݼ{ H_34bPTU|$լOXS6q<<}7;jCw׻ywZu4yE7?VOFOͥ b^w'bVضCVHUSXH;c::s=bfNbtCeN*94ggs~׺  : C X=%UbSQqX;:J*+留|">L}N};ֻNp^oHx|_yv.YA(fO؎EaCRhP0흪Jwq/紆EV3].ExeIeJ#W<:wmv-?|yK}s/?!;pSשEѮBl ~nߜOA^tdLx|^1NR>&ݩ;]\`|e8u mVEF~9 ߿׻B ~6+^ߘ]`悑5yy DȋNm{u1Gq23y&aTld_<'-%'>j6bU9(X"r"]|I=rR&؂ex8q,wќ7̇aogƴTֻ>S§oUUu_)/&o`{pi]G]K;96ɜ+޷GpͧeXl/.8 W?ou O16=s!;C|H2LВeeghG_D/4|8X;2j zq\Dŏ>J7kUYP`(e[DA-?=?|>>{==cUsZVB1peG>zjt;soSCU̙3᣿^mF ֕&_=Hϫ&2B{6hlO?Ĵ[泪ż{DN˨Ž]AW֔#a‘0QR سMEEEyG.1'r9~f{*gLų_}J̾n82 n~;n[99l2m!;7&%]ٿf}1xT)DDDD$6-69oxx65x&NH~;y}Yylr.i{bfZրk{pbZ&W/DšIٿ07I']5CM}X74Nj*|2n;|ij"yNC?> an|>nuѥkF2}!<;R8鷿@P(pHR$3]^meݲ=%?<hzzz$_G߰'ײ|F>w+\>'T]Mt6o uG6C{/)sy V׽x&0;p"Tw]/zs/?}Un%9pM\>O$\']o.d7X1 Oa_]èyWa+KX3E<ܮO~J@H~ɖE?!=3{J>aHbsn>eI9=~[O i7 fʬ݌=`3<=ˣ T4 6qvdaƣNq'""""i }1TaHniKa)8mcYV-17n6z=^r'AJJjbeY!x _gӓصua_<F8D(WA=!ɟDh6,%ɠsdAAoj*&\QFv'T Uv'R֝吒Ev>@(dqo<|mE'ŸQqwN5e/y}> :OqlFGA[$v_ɣeZk={7^gOQ!g~ml⋾C\r5N{ϋ#|8W&WbfdȕqlQ7qrFFIJ&aX!4RwV"f{;¶rpk꿊6w""""zOмv 1@.:Lhz,c [Oۅ@L8groVXTsjA87*dG"SO2; eP%ɱIE5#'Zk]ƉTa B;|ǥKD,xbADDD=Hhc!k²,LLt00iln>Ip+DDDD$H[uR"O&p$k |W$mQsœ$iW[XfYb7x" ۶)*,$=+XIڲDǓ$iWfi?55+QR\eiti,+BIqkV,#9-TLLk'iK3DDDD$H[|%5j]N^]lٴ۲nHKe\ѥ{>iY /(-ixR"mIS󕘞 rU/ΖMjz&Su|i\. HSAO4wSU'W;Q0ϙ   VלTs4v\~4Ӫ^p䱋֏oԝuX ̼v.@Ac5QJ7V~=xWmFw4XI"# V}ˀ䭋ČRiJ_D"ش Ud}  pT#(>[D '5:dgϳFz~VZ( I{6LG^FڱX[U-L||~,+G5*Pm_Q2x,+D%"JYCW䎗&#҃Z I-{)> Ɉ`{Yw>o`r"  AFJb=6Ϟ,WmTyK]oG2϶/yeDؑ~䕻T[z\~:&[d 5KZZt8oSi/X ~{+QJ&?oVHPTY6uWP~^-R'W AABĮM j| ZZcYVbe.]_.: p9Bt}N~,Oec2oޥ"#=v؎[o,IE1_bK8W:}GxWmբ!9$kzўEi57J VX oV Xo  Gk`Te2-dbcϷ \Iަ8ineEGN /vew~!pˏ<c;X];K (=2V ޞ+5L&KC`;S*( 5W^ך;_йegAA7.^)Z$2l-YJlgYegDN>˘AxZycհ曭6GaH:*{LqNG:яI:oUR燋#/J|=͟ss?=LXN?Y4JK,)Atϛ|#VXCh) 7^A/vكK'~R ?sRys~OA)rFw>lWS,v &*\ʏG'q>^wA;Qje,ȆΣfl]0Ni~\Reϗ`צ|HȔzJM~u?vPvY!4cArZ!-?yzQ,nL?XL,l1X@s__nKa{Swomr&8v~ˏNrlyAf>} J!0p9z!: %ۯbz44yUs~䕝˩fl Q q֕~(@54@X4ǹL>Za: C[G /-^KP!nX!Gֶ e@y߃vFm=)tO4tq4Y]%!|}Om. ,o~Wrei)}N~mN>qKg/,R p~TuhJrJ')xB԰WŲN(+c'(('y;aC-̲ qXaŖ57ƽe5ki0RuQfiˊvGZI(`Ly^ e *+@{B87ÖC,0)믳eϗn[ZD-=βJ{yo=<:Y{+#w=v{c] i /u[ |KT*d)B,qaQl|v2h&Wx8'<"E\fӺ5ܾo7e#\h_%?g/w?AaQPXQXF: _ ,2KգL4O*܌r)8 EAs_ٌF%2b"Eۘ JYtm֖`{gܖUùaXtu_ՑJYǥiyR} XW.˦@)fkg JfM@O6hc)XM"E43 b*liu룂e_TDTntE3讵]we25{갦Ϡ{쾗u]J(6w>A.&~'w$-~af s<7^ 7sgK!z Th^7:(kMJCb[Sܸ)1X/3y>8ELu.cqXBY?Gd[,%mRʲhw`㳾\dm1nM88tn~5]?:?a8_,Kb=PZ%"Hz6Pr@XHeLo(;P(JD[o^,Y$MU[iAF8+F>`GqN>DmKTt^'ªQضwğ5wsT'_Yǃtk[o QQ h[( uPykz>oysc&KTaԸV'Bll&NPC͢:,1fSJ@ɦif͍ W T5r-ju/~)c4RLUjX,5MO!nSwTa]vaH-F`5C}>N䵴 fY\D,޼iO'S*أLj0( އ){v{aӶEC,SMqa/;5‘el=g( 1^m),lܼ`GoNjãrQqpM/(p& ݁i날 H-M1ŝO"tFo KTq~0xb??3won飳c@/7_z8_wܱ85>S>nJ>|O: :es? A~= 亶ð"A0"?O4~Xek,V( z#^z #QaZ(FEhLC\@A5;-#;^K˫՘4#%E%B\e&apqEVa֏15Q rKvX< ;fDZE冝q ]P mEDkw:ioihێ j̝n+[Ww^rAn>oiV͍^ y7 [?AMHXFP|~^:Y_y^)?{b0G pn{y6*rڛ&%U-Ii+Q sµ]JƧXdm޵PGY'ziX~5~U,&)0l)ACh&LDhjNjf&+#"?>o<<_؏,=i{*/@ #oea}u¨vGfP C4WQ(| ɗ/;(_Տo=yNo+*PK|g)~ q˝|;`O>:z~Xē|=ʓ nv{_W>a~"Vv~|#MbY/Um|yaPP9_gQciicsEjYDOM4($kWDYςrU`ݥD!n[|@kӯ4{s]E"s\H5PNYU#Um>m7Dk/AS?Xq` 53HxŻFX*byUtaӾeaqNLGb:]S ea>)F|'ª'_<[[xgB=~gO q3<|xG2'x&`*y_c=n̿վ_Q mLUC2[L_9?rŎ6MV, [7_|sD6 u GG+G.ZG/0l|j&=C0-k8kTgC6u: %'QE<jX m%J~&16EcXBw{nS?D(̎Sc^$}3Uގ8(d-b4)ת 6_f?Nf~o5q]_Mв(/G \=<_<'JDS4ܰuD5B{'Rד bB܅[ָ!бµ mYDɯUe㳱M:nDIuɱ)96 .gLq亸~۲t=Џ(G6Zֺ.rR|^F]Hv}G6[^~䏤˷DJ$4^+W:ݸS lo*JuLʎQ:/H pF}?s|pFq/fl׭󁷾Ό{wM+Rag?6Klb?u\gamZǜ~~l wAYhy%X N"LAHѢEdiBZM[8n+Ki t\'@(8J-#L(Val7( ܠ#,OdP(Ė(,߫Q068O-Hp lۊF$-"q_&@Gl[h캀qHE_ɦѴYA-(%M20"ڬ.G|OSV\uDI=ɛNke˞̳񁌯$}8CS dͿPG! pq8w+\-sܺkkuP\Ç?>t7~fYlLė ᴆll,Cݧa( 7\u] Ƕp, mgrF[ͲLʅA# TȤT@,d( i:F*ppJԿ'vmM5 -Q^ X@j<K$k^ p(V`iE1QGeЧ3ެw~nל )%S[pXNt- /}+$Y%*oET U`: 8jfB9e F8R5LC '% bwmyv~-{6 _'x.l+ׂVD0@[blw#>\W[Ώ,+%`fY1A.ld;qc:n8*CXM2v; (a9nlj;۞.`u7`>Eu9e"104K1lJ[nSwd\"*~Ĺ!0vc9jH0lE.,|p"alڋ;)-ZqILna^ld#Ԋ 2%.D D2nkٟeF3q}-eɦWi>Uiw7LS?rnL{qbTBC;uYzEO2I['| \z }?M{_~*^,U[\.(\3b[?A 03|9^eR4a Aq}5YVl>AUa, ~$Ŏe6\\Q?lp%U]4 &Kt6ï7 q̂)N " h:!U(22S''H%nc:y,5A@: pt܆p4 Ю6viw`Ə`e3S1ks3ɵ΂ufCƼNԜhϴ'LDmcIKR_jS*)ߎ.:}'@eaMNƎ7b%k` py1>C~g 6,UW}$>PCʥ2]UlKDO~!FDKch!|V,AK1( +7_W2k\;\w89-QAdpc&)7JFyh/Qi~Z5\O!Q2xFQL\vtO"NNA$*¨/֏:- qhDX.aRK.6GQ.tJhG0[0lk l΂E1uaH{- ({(^6{; c#”FuXqk3&Luɹw>AVEQ L&WniL1#uشƎ:cٹ广 w; IDAT\so/'$z_-ZmEQsY %%Y5Xobw( P E`ΞHp+؂j"C`L]l9;BNkfڐ/ `n%i^Ra,lʊ qd j~]T&D&*KK*`#{ p"/LvP"(Vcsf6\,&ؖź6%+b6zr\3vID^i5X/.{7bC ͮS8QŊnu]6ܹ??|j֯H({Ax0|zni4% "ώҲ6:؝,`ּ.9ڷk#{q[A. .s0^uz(lMTT~[Pv=lR]4%l g`F1 h=E/_ GJ%M"TlŬQ "_aP/j.-GsjYFs;jLyzVf3Tљ*SAvtʉ gf&+>Y9bcγ}>ߏ&dBAZ,/ ` vηA)Rt\;w>AV_kss?W}Yg@ۙѼxb?vN3 >3~rmfzsi- d"ݺ~ǶY5jj%p)x9J]  R85.X \ R @ ;sMjgnoԛ#~PsnFk01pnF5xqLЫ8ڣi1u4A+r(5?$AqЫRr-kEHzvzj>߫cUP7TˌzLW}f:(MexWjZPwca~e9.vd\L(NۺʰƎS^¿jP9FavmǛBӯmqK2D1; Axk>eϾxi}w~N~xKONx'oMwuc|Cq2[x0_><<3۠b)ƛՏ?c̀ Tp5A$|Qؙ>VGaƷPa4aHWii[r5] 9;1V̀PGPH+剏26[d)髾fhJ˶SC3U?@D Ы6|cK'Ч3|Y1n8*‹ DȎWjuGyPr-+5 ^dl+wܲcN26ba4ZD˳DiM+Mw4S:BϾ ->77ԣ7{כ k?,{);5iJhp Ep-W,FW<ȗ'#ѻk7*{_dw/ph.w>~o"`B~G g_Qo+X7]'l{|zeNlߔE|5PAX5q_^Qvwyue;L43GaڭaכWoٙ9F=EWw;}$ xG?9^}{{F WAEB|r'@iO_;#ˢv;=o>.Z!#,GcQ& Z5ò|ʭי@NЫyTjsKܳ fҷ:_M0Vߦ!Zb%)*mSDkR+^ jv^iB?_k3m \(a~__xr`Sk8{r}ٯ/?Ѫe5GU_V@lc&'?gZYo>Dgc3;[ dR4ΑpU[fצ~u8/0ސ`A3>]e:̉e \LfwOkf-fFsL(Xl;c֓ 3<$1ǰӄ=aWJ˴-Ey'2B'bgʊ)^g ,я#-O\+e9hbYNXdVVFhY/u=7FiURG3V0T` $\$۵`O CP;:i/SP>#~C={1b" QU]t | \\L(#XfqObLP啣xefg9vA4 ͓I|NpMeh Ɛ-/Ol-/YB̋)aŅ=ui_*z,^ h^Yky}P^kmLXt,RToW82̵ڔYM.kbӐ9OCBi>-.wFKa̠:I7V.՛ÛBw/p݄[=M))259aA'  ,-)Y+Qf4Z_zYQ^˸ِDnjcr9dP*rfQxJk͎,/BV<լHJ5Xϯ.E*4#iMg-& 0vQ~OE螮` d,[Bwڢ`C1PYP6AA&Z es-)J.+2.v?758Ŝ2f>]%*<[}k,›.a KT\q/Rs54zXTBH^$˸ |qAA i'`ebj+f ^kxfLľ  IT9JObm<}5WΘR![&)=2ZL"/qAAa5at5Fm1ճJ}!kzRLϣ7͚6Q(<~~4DKQJ5j"IAAA.o%|   \*"JAAAT!D  ppژ    ƺ܍AAAx=!"JAAaAAAX"AAAV(AAA "JAAaAAAX+lO]v }7\& eLO3=3CR=lۦX(R*젯˺86(n}h O=n  \V֌rifppB@ExyqInJ_o/JUmNJE   ¥&"^}0Z{8Jr֮]˞={J‘#Gd*dM   i(ԩx, K/LƑ#Gx衇<9\eϞ=LMOs91Q   fZS<{n(?rJA\cPJ>arUWq10\6; zD>/@ٸKs[^jG)^! Bk'\.ye!ZkRLMO"4Qa6NGGccc]vUMȻA tΠ ]uX!V^NvtlNaysz.F._ݗyoMO[rfi/㾢\zi/jK__\qLNVeQccxt٪Ol . П׮l+C"W?7;^p]tz9 ww]>&:߼Ygg|+(eУ޷yfJ_'~{V7J뙋;ڹC\-wfvwaKsɄ*I) XqS83o-g+͘#3I+> cKgyac#DtQQe_0&HLVg}93U3c#  ?&ܺD[aCz,aGq[yfןbD ?^r;P9q^6v q#3iГyyx ^9eY(q;hMTy8`ʊZFT\VMD?/-}_ԧyVʋF_b:f{l?yGfM-CXgx3|}_K 'Z6 X;(}ϲhl@Q޴/s"=zp7[SATgt6 ج[6̍>-;衧ƮՌ mߞVqxcM3[m%߅Yf8u~Cg+4;5\z\^ÆZ|]ú4 NّQ0f$'5(4S- ̝9#=Ir:ٴMܶ ]( sG~7nؖ Sq{ws0~3su~s1)6+W?MXTJEEE&!Ȯq,s#x}͐KAXqz-ׯ wp=ٺ YҀRUWyxO :+(Fjɀ=z,dxhx^̮dMi0 M.ʲ@Pn}Rq 퍮~ R0Q)ϝdPõ|_ԧ97< ɧWKfjZ;\Irpg?-~q?co[bB,`7 4FvL &.}붳fUNiL0¹  369.uqZ3~I@ zy~L m[bNDo7?jdԢ:hB{G[˫#G85kx3XVS+:\Õk[,'sk^sa8<|S7Qr0p>uZ-=Ǹ]g_ِ2s|珏0YZn{Ml\yW kMwpA[<8ugk{b;3űg_e4TlϮƏa0O[dA4|LUmw\G &^{~~ċq`yҳ+0}%}wG N=rhJcW+oC d>1mK<1N;Z69ުWxT@) W^nN<0o SOС ?Ys\FcVg\q 1s;+3 >Ph3 Ê}%7N!7ĄT۴ͭȎwTdzkύZ=OOg 7 8it%$<7ZuEm3q&¡Vr'mRG#鈱_E|hBԝ ()B78(^<T.vd*JʰCPVfwRKG_fATqe!2gS]]Po Y6r:! j 檚vj|k&F2ko0z_QCc>kYK>u>hBcXɔ:ִpՄ1dˆ&Ώ\ {J%Atzs,p.Ows$sKK;ȉrY%Ώ ֻżۛvtec$jOu} 9Ir  mc"wkfrvoǬsA")j)FV3PAKdJs;e/hK(gijZ>t^g0mu fC***ãqAA%D,nۜH}{{ϼUS:!^Z|3o[_~f^d+Wyg>IhE~y^y1úș+q˕쵃ǰ[Y4}e-yż]lWlںIn2)FC]Q#YП|_:>zr>䓬|x6W=QBRw71}/?.JuĬs e V"WCs"tI O |]7$\uTq0C>2%e8PJiFNDk X?_<,6YOusqL_Fk%gn\8b į1u If'noRGjŷ~ M hMQb jucl8%d,FjlMf(su%eFKk2V["ŸIiiPjsٻ?)zAALW;kGǷg)Cζg A@x4Qaxx]ҡ"#WKu9oBZY7|8bI7Hص?s ?*cxXP wY):_>Cp%U *HGYSM(.|Jo(5T>\eJޫ.)ߣ~"VFeq@@xt,=RH )u-1xfd`Q,"ST&@q[ ,I0__="eu )LU]rwcl8jswݶuP&0&)):ʾS%2ؽٌ&dXP];^)4+u_. y9Hԁ\YAeCFɅ|[~xqJ7+'!= DIZUAiC~:Osd?^95/iK8 -K,%TV$v;,p8ZD2d+ aРAPPwnK[UUը]dF_d옱D$ ѱD$ g)XzN9'_矤.E@6SDЏH/d嶻>!v0<ǟq✢SYyWr]ÎR̾{\hйL+,$1)qr2A?at"@؜`JV>[_od8wv@C`B$ۃ{2Rպg"̏GظjsϾwO;x~ó U0G*P*݂D+եZ!b0HYe ĠH"\#pfjVE;O2}g_Q)gLHŸ>a_P wWH ƱJ$r| @ L4Ys!Dd+;Ǝ˯_:><[x#K<ӳ>CQf3(b6e\,˄2o.}Tl|0tj"JCuݳ$kw>Z&1#Ѥq`l%g_!ZR>עykQr6ɘ[n$qlؽK2%Yl!n\ .*DzdU@@MC'M(4*FBXYIވ)⦄||Zts y#6> .V.03{0TJ~a!ťQn9/mùfXku ] ddYQK˴Sh )uxhe8rg8;ю鎱Ѧp]WQJDEEDD ܺ򲩘 *^^曄$ɪ$HPpn:?BfCA] FOaL s@3zT 5yg)cRh"B+XjkQhްbmZ(j(hD#q9ͣ5p8h4 j^|%*3+iSc#uK/@ٵ}N_-M:Y2/431nQ8ƪUGkb Knrtkz8 '4sz-5QL{ LIm8 +pפUCC q_GGvVSG,b 牢'(@qݗD2TbRiꑛhs!/`Zj, 4KTS~nm҅~uᠹl(̓ulFʨX:OHr@YP]] o/j**_!.~Ly#pO°ȶEr0sm-Vo̘ɐuM+v[] .*`o3 JjF$e [zqsDAN@UUN7Ծ'YUQQ(w܁Jjӓj+cS.<Ȥj#c86-Zx[ d:,JR ܿQhN`.UoqvGlzns}qd;&/ a+u:>2o-$ ӝ's}Wj<>Ʀ머y!!X~Zy,l 25< Bs Cf&U nDˆ V4,{C0U#:RPVH%$Ps#% ^^FоeJUg`TMуiH@>)8SZM\xuYddSb`X) ,2I/͓V 9SݸpׅUo/V5ӷw7JMĚj06~uQu1[d*J%7`!Vrxܔ6ŒD"p(_, @B|;.A_8TV)I3] @ d&X7؊̯io]ەˣ_\nˈFu4 HeY1bPi9{ }5Ǵp~lAe, *ECJN` ((;QPwku ( ῸTGTp;T^ZUZO`7:%,Lyӿa:snЪ՜t XݙLZ 8sP5b >˿rs-AlmT&GdfdᆺEC04vn^ź[XZkȗi1wwȠn|##KAY:"Z5m("hGeDRyjvǞэENkQ %jD mejDɉ4e@X1fpazO4Dc1W&1ZGVvc❸@@`@Ol%ݬl؎xDrrU1#L נE.4ME}fK!$G L-kW`NUc)Ԁ$= ^DG>hrm((! ‹Zʳl,>q=;QO麝ڜ|]Iсx8gRҐV o "Ȇ xA ~fvm/{0~jBq BU2ƑRMInRDyyjpu {; *dpf"qTu:2RTHtr){/:-_'p}.Al|!:x3ѓPٜGL;9a葿]g/azthWUVcE8۩>NE}{sU\&;yN  c֛u Cp2e+;.-AcR[hh} r-\VEjTUVyOGWPa'Yf)xB1f`ˣ_\oL1 v 8qݢ_ oC0h4*++ILH%IV ݎ h6<:˶/΃L&%ŔS\ZRh@¶nWM@D沗i:_pvQWR-a7 _:CB?6nj3|Nd@Dعel+rM f=onpdیyB8&ED8T)سOW6?pߝOSߜ]|\w$TmɺXgwɠn4a“~ᄷʉ"FKNisO@'e4B@pL̙5qAx9Ŝ\3:qWP'ُua1w#{=>QG@T/L˕7/FMa\c4O"~?16ڦ+ ob|X=[ץ $EBь5juMͼ C[$(ssB3@g̡-w_&r.lfWOKIP`KkFC2݀,V+T yCe4 ^bQtxBKAWj3tx6l\OQQÇ߿Ũ(O#*2g,|#5%8>)l0)>/yO .˸>~oSks:q%o|ޛYwuMݰ[χؿh/ϟwLo6tr7r* ~ڹ,1յmƯ2e~ܿnP_ˌ;WQh2`Î{Lf{Drs?,l}&3)Ӥ{yyJPyYfc?w mA\?>/ێz8)}),!|e_ۧ*b㖰Лie ]^R{.b?`'V/YWe8W]?gЋ-FaTUM#ϳ;.)٨*!#募3\b ۨ'z?/8$[FI@SŨ]*;Yo IDAT⠶DŁ,߫U y*2mֳqnP/jj ~{?/e&UV$?&&dY\SúP–_1b>>>t9c۩TSCuu5՛V4M.Mz%[nfM4xӻW?LjqL ?yOܠ J [2ri'K(2?x!]2]gVc2?;aLG7f[_~Ay/v؅Kyܳ4  Kxm:& ;2A`ϛ+rrr7,9kˣaI?e nj&h{sM 9bHԸ 6 @ZZ{fxzzvIr;G! {^lA·D<ibҏLY#f !,0̭pw .GbHdNާ#(.D]RAEEEEEEEa7Jsf߾}>rԔ8ٳXVƏOlLLs|g5=y_yϓ.oMġxpV :v]EEEEEEEEEb\ׯ/Id9ٳg1WAE#!>jnU@U܂|{_XZ;mʡ{_><śW "k7!kd[f B yF<ʣr ҠH~H͂JhDF&ݨJK~^C@Kf#o_'r \(O%WӂWCǞ{kSԯ*********'("+K@ݥ85GU܀vl>r7 z_c=LW {C@EEEEEEEEE&gJ;܇ȋ-G tG'}PQQQQQQQQQJIr*Q************p۽g/WqPQQQ),,"\DEEuB:p萪D| TTTTTTT󩨨tURQQQQQQQQQQQ T%JEEEEEEEEEEEJJ'P(NpY82ç)+ ׇHVˑl ~GӏIĦ[E~1yDea2+d7rHGf`|L ;Ԧ".fbƇތ{:WL&#UUp%RQQ_íJT^^易łQQ 0DwV9?y|P.ǜY{G$B7Ix:?]Ď?԰wgcCJϣrY;iK&kw/N]à'<3G?xj}o~sW'Ƒ^X*r+ho9r0d[EQ0kj `0" B_~6%j߾}ݻQF1h t:555ebbb1cs A@-$Hlϙ~O?=Ap(YM- h":k=<OPw|O-~K.eL6͵B; Vw 'z<5V())!88`,aǑl' K(Qm۶`<<;y2>O MME9} 9J2>]B !q擅̸kJ"}Q[o/vR #k(TC-5eW>on #Cd/|1~ʝrd,?w}϶ε/eK}\巟g7q[ԥPC-$@>|xp:'Mw+ݯDذtk;;zȀ ܹ͘jWjqo**pPSkWGp8.tAh7OwƵT'ʥ[feZZIIIjYbW_7{ÁF!"<3gθ:#:}0:!}{Jr`&Od oc9Y;$q@yb e߼WE2ڔ(>C'>PMc߾,SueKC_Ǩ hw PuUT܁n)DQwv/>q$&&4蠪$;维lnSfVtmm7!I">>}ԾREEEE? oXj[`K`0Q]s-uO6.3㯋 d&׍v% eT/jH,A\ղ󇟨V4DϘPg&̙ C8\1bcc%KG^1W|^e_s!v}t;=#@S[{9YTאμýmE.Y;vss37 ;~4Exse! e!TÐ1h=z$A4k6r|fШuɄ$ITWWc2(//zB$8M7e 3g]͂2/"ʏ^.Ji Ι1dEap8e݆,JEEŽEFV+Ě/ƌ$IH(fwT( r0䳫*'(!beٯcNZLiH6IȱjԷ L_ G$Y *6dSᕫ8ZpS2$ٹx d>4AnAӇ`c ]zGGa6m-FO`Τsl5ΜAO_6H R4Š`(BGZ7Gϵfs;$n`CFXq~ӹº6GY vP.(i.c/RݚycAJ Sˁ, 4)gي)-kRL CcX7Pq/u (4DnrDla̖/ؚĈg3(@d vf]M '~>QB cq =_|]C6%b`֤jv\[E{"$|La]_қ׍$Jl`ח;YG$nXdo9P̌**&nۉbP[[/~~~XVV+UUv~.4"1 0n-M/r >Q&ux( &d{{[3DP~z߾g>V8u CU]+_}[*ZLhWmh+X}ځ7c;0lW)<=z? FkbJ%1$Pg;0?41qĈT6Ll Ek@1q|e!>۟djB'>OZ' sͥ#(/BD67Lj15ksObt$#{3oZ$ރ@H6cF3G!m`8S` 5g.̞ b$k&6hijPN3^N$>%AAvP0fS&&$`p倀иՀ\]DQ+Ɉ)j֕L*?]1jrvî<䈡̘Ћ M˕Mزa''=H10*уS?T)LlԔPdj բis/^x.h4vi'%G> 㶫Xl;}ɝW $ {lZ޲:PdLc߾g>*3$T;no-<mCv E׎F`w8d-7ƿ8J6T J+$S ya/S8ƭ\5iSÕeʧr1P(~Haӏ07UjkU@ٰ<:1v2Sz+: }B%l&:`7}im!۹3GwY8>x|<߼H~XmaZK=m6 ەIptP Nİf FTt 6&d#{I؟oSPn{\~+p:QMCCc dP|h?IS "1L̼z Qٟ4UEj_3r$$ Sq… @:g'~ʄ)T)1lES5!9?!:Wu6q2.٧~5?hq#D;} }sUZbR(N/J!^f{q!e.YER-JdBjE$fϞͬYPT%%귿um߳$z_F296d8 4}vh m)QR~{dyl ^3_yP6td+{3<mX X" zx6->Ky&Vn9$1t˪~:nc?U~#_YѨP* 2<AG\k4&ngnc\u7üwOR:e#i ڷ~  ?/WW2ͅIn{RdD5|͊M eA I n%Зis㩵i :wFKdtSMhsCȔ9A]\Ghd_yg֐[B!R=`@ 3%#J)>yS6Y2bIIw5[p*QhHNŔ| @E80aD,W.#7߂gO1-OCSPLA92hµ9`OM@XK('8NgSTdo@NB\'4fK&Fff3Q6n6'7E/fxO{jqԹX~%|&-Fat}(P~ƒ{A"o.r]-$ֹ*HPi@Цv nV!O:OB7S}۬ZW`IWmEURH?=Vgʵ?b@ bSV8 TV+A fok<8)Kx4Xʮ;I(TfdP& 5a%)bi]I1V;#`ȲٟA]^kU 8Udn}\M`pQAx7>Yv$΍䊙D#]B#"PZ<z2-:+s⒉WB]68;w(VP pnkٜɎi:XcZMTYD} N>~~x2fSu ElmAIA r@D)է@+)*"!t**n ,ټ3BBX,չ }(Obg-unH>LM{eJJvBhXB#{n-gO.ZBdbeTmf X[˂Ü:)_7ҁZb~V(϶-'p3qlyQ(;q *Sc+ZL22gsj/;I^mJΫAaPcA6ɍm%xcDfC!w_YE{7s2 !qݗ2d`$I!z,KN?/UTUAqC&g(2!,߃z% IDAT5 FE_swEFKa$xՐk''H9?N[<[<Ɂh D(|1P\lA6RT%"2p} ٌ(9o?Qo)ӆ ~rnJc̾;Jif;Ț@Cȑt鍢cļYD_ Wq9|ǎ1&G[&ٶv E!YW1r 9J)kX3{ ?-UWKv#c@[ f,Gן_rg75Kn37j-¾fi<z5N^R>D zzVN=y}C][%?wv+<[!e2ɱ" QtxG+~ILr -{S*DfYףCjBB$̙lvdGuڒZf gzZ^gf}-:q:4e!y7nLWl/n3<P5>b)dJlW;#"R~B*lz+ n|Y=Խ8Y/խ֌7Oaʩij!XL ss)-4AX >B[Buxpfehry $lV~nH\׎KN61dHfШ5tJE^ׄP4u4xQǁݵ8d^)ˆ?.`jxa16V^n.*-zqOCu(UX((̇}ȦM\|-8Nz{{yV|\KːHe0> yi$yPnb: ,k­I*KlWp[nWJϥIY[m؜~UmhaKSY+ 'y6!?6p;Ɇz =FoM/ 1~ mpUn />u&78iLib&dmȊa׭@cv2C,dsQV^d'b߷5&(A\*WSK4S,OmEj,Y]鼓2ٷ)XTT>UZB*6 gKt-l5i]~*p8nL\v|^cXl߾˲4 t: A 9zvMcWh^0$I }ܸqcVrnngcAA P\>D*=Lo0)C% UP$[6EJ AeMT{]bAaq"AJrp'|1| ҈ JAx8I*.يS eQѵ8市 ɟ>@A"z>AAA AAA D   ,AAAAQ    (AAAeD=T$ Ų-}:놨A()#TNÒ\Zw.D Q^St$c.HM0Zmw*mzb"g-Zb*S%d,YI<zdpDD/v=n;wPy֋JTʝ2*$L&fVXٜp Yxe7r?g,SpذdL&%]I$*ܳ- d4ÒP#9 g PR0wcwj[F89҅vwoܩW/߱oܴR'nM4@Rlr -K^P{MWI(7i*Q\ *ń$iyKyUryjR5hX6F"w.w/u*n zS1BF'Dsiߟ^Fr`EbT9'P.Ab\'iv";y^ůKLX4ў9 ׋a;]/Yze%7r?g@M6*#s=Q K=LLϽ{=JQfvG[z7MfV7O%̜<=) ](hS{nMɅ+N7]_$_=Huש^$ݼX##[#y:'LHvTԻxEⵛۻl扠IFoD%*mV(46(S2֩=h뛶.>jeYvUvl _Isl>UDĴl*6RD& 9N,27&`pKQկ^%~Z?Nd(cӁC~>"B 4 Kᓭ$AA%1o݉(A,`STZ^s&*7p[]SoZ>{W6i?cDtG=4:*(1}AM En&݁]120H~K6Ԉ j%v`MM2Y([ytG+NEel==eAֱG8rv;,s6omܹs,^d)r)l + ^|$,bŧH RӧY|O)螺{NT+cxCKp 2(~ C %XR&q&>b"IV) Rb q95T3`ܪ~@e׷ϙ;XS]s{stj mlg$yj+):oO`Hp7Ї ЇBU]͝ +dt4N|"=W5d4gt2 aڮs3~}vv:W?BR_y32q7i2/<v%Bu?zA'5Gk2P37}I21+O391D"M~ʶµw {mLmqƓEqYu4=QFӃenHOeYHdfA)WJ"53Q1RmTȠkt'$#ʲHƳ֩3a^[udYƬϽK`̚%K%/HN&6,@gh`H k?jX=` kp?c,?(uaV s}<H-6Ȁ%v8_e|NVƆ="bXQy9zZ*b/ ^y޼u?tQffgo=;̶ҚV -ʡ_"c79u7RJ6/{xl3A@w}?$ukkr^Ed3'dwýOt }dgh#ٓg:@2uGD)IeZa2a`YlQU aܱVk ѕS鵱ydj^'MXTf|j1cY,JrQbա=&1 >3;4m U4bgcr5aehN)e^\3>['ȨelعU>veQgvW=%^ƹ|]f­'~ >NN+;,sݣHXCqN5i=#~lZ?J\ >I%).*qx[%Z6l skU(r<,0z{Yg?ݪs t&.qJ/>D[S}C:se-5~Tmk9w==ϫ F W t]ױ, 21MD隆Y5 dZt oQipj{ $ U**mCm j%d3qC6m61-tH\*{6;,&"` q'`[X+!Nh\*ҹeSHX9bw{X/z;"9lnC%d,O'^c ~PL qI,i s_jz4{۟[sb蔅\!UWGHBܞgMb"SVS˒FJd-~3қW\Ǝ_;MPU-ڑ ]8ͦ\<*'cɄv=;|#lh\;PH^>T6:9g_J1RK/w߾Ajv 8Wqφxy<% D6|'oQ^DIO/qyǪUx\8SJ7l@Wn8(Z: 0 ;(VUy>b $-*$@gY:l4<O8`e%b wgf躎iv2 ]/BKƍSd\$_J܄ȼ 嵮 52apCcbM:h)tɨAwO_/˸q(Xڭ $z>lnf$mzT,F”pG"d#T;/311No1&Z:9+&L`OݟJg;9=:HG[< >‰GxMGϚ7R0's䡡e`W/| T͑ZP&9FO Mln{>*998H_t>ۂ % -G&Dٳ˗/k. pcrJf@߸p_3-$YF{IcX%$s=zg *UN*Q9U&˅tE^2*w+ 3RZ0pj 1Rbw.&tgE*`ǍeTԸxYcp~\DE@~2M bIk>[ m׀2)Y?lT몱&?%mb K,:!٩T)U۸9F`ͅpK`X~=+rHxNPcB&gΓ#\BMHJ˓\Tȃ F.#u;7)47Z>*X;U]G7#Bw3z//>6 ɜ PVr>v)F:"' %UA¼%9F|$dd>ۂ %7',B: KK<Yn6+wZ}C鰝j9, k$L6v{uR-]Iٰ/e~|.CDD;` sFetԾ IDATN_fCe{DjtF6I. M,PEhlLDf\" ך:/Hl2e*v}(mwփz e׷"&@M>kYWO[j[7:kUІA&|R]SPt|s#'$յT-NiMX:q߽YkfX15Ebad+.Or` ;0H Uz^*E$EEF!ٚ$ 9[qz;REnlEAI~M=O^epcRs.V_ F 1K)JIYt&ZIWgá2jv疵p oK71bNWJH.Al _zl 8X.cf5>ZjikŦtr Q18 y*䍘roe,r[=Viz=BCH7e`` sxAp Cr?ñ#F#, ȲJRnbV!|Hdwd*wqޡq#}$FqF?B3|%K_{+Ή a.aSpߧ6ɦ'M/<4sQX\Q5ʷt͆*˟Cz91AV4<H$wAa ;-A Ma *ڒ3,$ HHXϓ 2MPPWpIE*#S-Yn/)W( 5UJT/MN7;KxJeϻh\S[#SZWf ZW2__Hf-pY Iz.<$5u(aCCLZ!ALzVXTS[&349Ic m>R@n2 U{>ͧ7oyP.jzh/}:8vϳyܡ0-ջxz!u{D4=Ʉ";@deuDx$EYMIº~;\.TFCp%3>1l;h#lSi!>^٧G$:c3Iu{.!nM7%"2/2n֋*^,_Z$dk<=FD¬'^Vy}fH ,$> |k?d|k]+g<<^#xP=.rD|!WPF2b왚]f8죾ο~|4y:s>&mO <|JDe- InBP$v;a p%ceu&Z*kI*_wPU|rw!2rVJٻ/> AVh(NN^ ea]$ՋDC~ȍgyzE8]ggO2u t䧃^Vx}+f12Vۨݽ쓰L Xz (CAM$W5sdB0dtx2v.C"2^4걘GL<=`}.8lq!aShb UO58!ΫRsGɵ>G83d۸못Gz><.1F{Nt08arO _s/r,EQK pc=d6pa1gP^yRuᗞfwG5Oȃ/A֑֟XJws$ffwr v_URqnf;X)K왐6f^6τ& k/4'c9NW( nD#6ѐz6T|Ja܉[,Fz\,w˫ ) S;ˤ3ÕFqyyaȸ잍ՋĆ;EnZZLD*ws(mƤߧRPx\V92؞nJ?;adB  +qC+>7OxHTF2l4Ԇ)چP3HB-<K[|o`#͵\xBYAvϷ$&naξцj^2hIz4Qʶiz-dǿ;P*~ZW;l9la?cG?yf$yPnEND _ݽk Sؗˏ~ά/4_"b`\h\ NiNmY?9cXȰRIrH/LSTߌ5l3~MKRL;QT;Q5ѓLT52tN]H3`OX!Pn|zWr\54K6 !Y+z$lw66f\fވB_eӌh'DV9T7/lvL9Rxݲ,9Nt_? 3D͜JuE@rS]@D]SeZsK<{XbO=85⅕gK>= w^cNꥺu/nNUd3,< L]3Jpǣ [q+v{deAX/Th~Wų6݋zHH؝2~;Xds x 0-)}T"Wpa14it@Q   Xbb( *) Ӆ?ubQگ^q#- glOum=dh#C7b;VsybN   362P,ʆ"KYRTF[v1>/9y}~6mN XA62SU'(AAxyҤ3j^AÌܺǻ/ͭ[E-UTLO)1OᡣK3 wH oO2aaE.1F2OVsK2bwq;Oē<=3H|D:fH G"DУHwF89҅g?*ywcy]!N128y5<[l#Iُ9~}n&׈@f Hb cہ> r󃣜x9ag7?d?8Ƶx;T|uy0=Gb֗!Pe$kDLL JAxH7l|WIzzWQ~3)B~]rUN씥ۼۖrUe{ uRnki‘wt^GU K`\;#L[%N Co^G ^A4831ŋĮ3D=%oȅӵq.Djp+!_BǮSOcG>긁cj*~]>;n2o )R&3EEe՚1>6J2&{ ƖAOƕd(y*J/-~s>6X_CM xK‰I /Ȟ[]C46PW'orm5;4钌 X]t ڦ^dg S84͊A޹A 7r T0U 0:9g_ˊRݷo?POٞ\7@O=vyUX_?;cA Ka*b'[6Z>}D%28]"-SCgyz228%lbԬ&Y,(Yy% ~fH7>Χ ;I<J+lںzUL4^s٪ٶg氅˭c¬Q5;w `jvT=qJkE;I{ MouO+w]VJ)=M2mO4c{ZH6q`uę;:QuIJPlk7aQ0,jaYsAYHHtڲ(ћqb,}GčY)4Y>դ}Fxqݝt308FR˓v4'Lh q{Mc+H5HUȤ+ŎC@oEDV  E (#p.|K pY]X**vREXf"Axh)=E>bfG|5 IdHv*6|v uЛ i3Q Gmnj[vahh`>~|P ktd.>3OfenaHL[')EWwdzdrvEB0*aݒбk:80 e J!&)7{06BBSrPjfmh,o5 Fh FhںDz#\?!'L8ɥM>ɭs c:c eJ֦Y[9F,X.dE4Va$ M[ k(egY2]P9q&?AVxaO5@mOb[3@,=\T ]ӗp,DE4 m֝TudSQ14rb( ke׬\.X|k9T4D Y WArZnAڮp-Y<RW4T2U SG/6k>P#LS|WT.=$9q uIOs|ԅ &LY$>OO&p{GQ }3z(GoP޹Q&Cǧ9~:Œrd ' =l>pӕ\~d,R1& LF/7?n'Ӓ܄þBA': "ICE a~%)za]+HNNY$>ٚ?99߿łE% {f~T%kz-zcy MOE9iz Rk(L*̉klLvq}3T4(kiKYH:6otSn!>p3ӗ{Oq+MVa]ȲBrre'&Qm5[*D$/j k vùMv ͢ qxOtBT^ɹf;.{4ǻI^̃&aiY҉1GMU=Ȭ6l3Gcxm!Fʎx{9Բ`G*{Azl)nfu?&{h:tfꠔluWϱWyzCՏ&x"  +++/iÃ#%-shU5o8A5Ҥf.4/ɨ [6R[˖H!4Kf"%bÆMBknWBp"'η1mbtzOm{xto] .v^۟8F2;CMٿ:酚&ٗPΝb #=mH*`-; > 1aNe>NOX(s2ǐU\^?~L)۰ru?|HUξ|}A1O֫tN[p;vm89n)R}]lhٌm'܊DO  SFg{jIeV5g$SY9g-ct1P}yE%%TfM-W<)uH]ÚP$mh^r55A1'[~71$mIk]יAC!:ڮ1N-{tk#_|xNqWKD pȡmlݛ }F\$C} $5$g-oXbBA`(h/Ԣ 34errƍN9db~dIfCfl=A  _Y\e÷R\Tl};gA srN:]˓L191A*9I֭; c'IO&G$kVDD pnjw(bݱ}m}9221M aԞ m[yT:=#{i"KٲLH%B!b1~)=M̞ƛ%qG(XYFaO+[o$9:#&~<˹fۨ @sz677tr˲ HfJBM!B!tnrs;׌ h6&5|h@h6oϵdS` BI%&F#=QB!Bg*ˏ>Á3q\x۶PKFǏG)@xg%4 }eiI%dQ6Is>5o Q]Τ53YX#NtaMbqlwr/DX'{gx+q[א9V2 F1884(L쪟X} {E4A#wP-eD !s@(J0,62X/FzGFj5|(|k~$9V۷c#@o9MCӬX.ϑºTٌ-)NhD[My[אZ*Lϲ3ji U0ݿ{}] r?즾) rr *[a:iL^1}ff?&,\ta?K !%WR>04c,͊'[P~~]h&hx@\^v}fiPm/H]MW5N<<ѩ'AX>W~ N璯gRyu<7;(X2KkZD\{cNi;i"naKlmEt+R:+}Կ]mFw8]wrkWdI -*bxAz ]q-%ypZGiގ^zO춛NZ-~y۸o:EAskfZXw}k{#淋fmM-\q |^!p])|%ڞhͫ.ζ%F0k]Sln/? eSnՇEMa., ]t(kgCrd%9AIY!G#M$4ŗ_RN(!XjT{GeՌkW`}u3@_?E[rqr;I9~C8wFN<᭕5bM"'y=޶ u%_f %"o39| 3zk{$Hf/;$[Jo)zGR ]i|vR^ŞvkhPb6l+$0H\+!Xjݴ$@sPj|n.-94׫`[y`qz":AK06@@)$v$,8S2=#LߙnzaↆƗMV}c) _{jX\{lNՓFk-{J-㻱 `,BI6M7v9X3XY]cc8Vmk]ϝ${5ll3꧳(J+ls3ɔi(I@lCy1]4fQ^s6LEKωnF?吗r!wT gAʊ'=œ& G8KYk@-WsSk|]4k(kgܞɪΪ+c" :NTFudS}&`‚ÓJZC 5 D !Ēcb6uC^rwL~5vl/~dlU\&FyǟȽw]wFGs6o1[r-ϙik@olV+ꍔfs.0xi>ޢ=a4R׼w_Fy^c][:aPKoF_1le& vT}Nl5Ta=7,$!#?` ~.^;A&lTJꨖ3Uٕʊu$=!NgyH/-MkOš"F_#۸mm٧]D#;Soy[!n NNӑ J!=J8fQv%m\wuSHq+<):TōikCMpSU )Lgj4\CZho!tk}gU~) ԻI( O^U)1N9eY1M͞_A4'ۨoQ>62{9JaEٔ)o.cyIwp~)+j V3i6Z &z洌vL=&;Ntf oa9WgXxu8-GCfQF/::N]̄S&2kh3o o^s6P*($#O8VrmmN;t#5VoU#>3\<8U :e ,0!.zܴP! M]~^]ECwsCů{{R`9L7VX͆ gQ5+3tHtP<8GRlx-+l+B S81]k˚wkφ521Zۈn!`-eesTL-ĂAg]=1M5V/Xf^GP5.B :P`]IuÞL{HM$22Wkg@ ](^ј*Vd" _ٞl%hi' lSKVN|Nf0ϼ'J!$ˮjNs^1-[='뷰ey ufNZs*rs\h=!z1WzΝl 'c;5,/zG3wuksztsNBny'ok9VEL( GQ%ӎUbyw=\fPohpUR2 43{FJ%C(М\v7Y鎢Iɞ%Z YNAz{1͂ #)#SEiPWŦƼO}C?l_JMSƙ&ncIfJl^KwI0t<*AT8LX1|"-#SŅg7wVltz{•1ArB"g{N%*NcR ' 4hL B Ѭx2ȧbzk/a=^v;[2u0&`wjݩM0oDllj9^HSg34SsulrVg-\ = uB`tRh,)Y^PCm(HDO-*wQPWfLNkؔ7zvi><myu}e覉tn̹,#{y,̸bN`F Cw 3t}R\?2T,LLˈH(!ҝ٬z+=]Q38@[{ [p+_Fn2DV:UDޮvZ[;Lwo>irvHse *ԲJE+ּe,K=@'[/JoC~S'sҰ 60z䛆3dP+ᖄO}dj3jrNjT^ghz6J"AbH3rm,wX˾aL҇&"6LsT<r*KAڣoO%=Ht<>BgG*8\IVzX;{1Rʲl+ۨoBa2sPL/b45{od—z}xxU<:m#vV4 0MRL,^/@^:l$4a _w |gNs7 O"(PpP>qQ J!/ '{="MW,z4Q 7akK1JG 6Fk"L_G#'&~ȥ",X桬2 ڛ;l9dVP6_ L @ck=&eT$fxpi` .DMAGlo?1nZ;々Eh O=n;'؉ 5Ef{LyLa:Cx\T'.o2(44CO6~Z9]s#9r5Gikj ߏR WP"AB,)6W$F0z%q/M1%,VYE cvشZ^s<9%& zv/|CFwQ|i{j4] oMURI >BXYɭ,'5f&=ģ=,,$ݥkVJ'eFdZ;tulmfw#|RR$ijh#V D$*|Π z&y2o"g:)*)Ǔ2sӅ"#+`pz¾yۥA(!Xr,-;8zu-]w͓EY2lXǷ:nwf4r}t6lU̽l`-ͽ:}ӣrjWW2AsXWNoD?Xظn&*)(Ya/4#[2*SOqȯWR0BIi9.=/,y ݇}#tן̝U͛w/kS^Y^GG=,57"}k|cm7/cE2b*o4zeWҨ{ 1CT1Jdaۙ6VbPb5MONV^yңe}ӄ?MWl;B1^bպM gXhpņ˓eE<<@ !tTRS 4$ 0%nj؜nRSV oꞏ3_G֓/ӡ1c;x b d&=QB!@Z8|`.5W)oZ2uu6--t|} o\Y>30Ї/=sed2'ŗ6#OBqP34k `ۺ\f&߄\[aݙlxQ"87%,T hkn$`!/xx6]6eЋ71lBT 1 IDAT!1=c-YATYpz<8} n!bʂn"/pMleqzI+%n_óѶ&J˗.DY:vpV{6;7C.`S,[U`ǐ J!hV\lx|i,r")6/HIc /^-H%B!.Iť4ן"-vVf%aJ"0 3Olv KD !BKۛCR[S`snATh`_Z%e1hh!B!.8)4}=sޏWV[D !BqbGp @,Ûǜ J!B iXO"H:}"܂"4mWD !Bqi:}2@*HT_/-oJp J!B TV^>n3 9۝BVny$ !BqXfǗjC˘-(&AB!8oJ*U~twPXmv.7ixS}2< J!Bid ̉B!B$H%B!I J!B! AB!B$A(!B!HQB!B B!" D !B!D$B!B$H%B!I J!B!`] !8; B!)??J%KH2_B! B!B$HOB!Z,94mi%B!bFSI)TD !B1J)Ru!cbgK̂[pܤgeMI $B!b#TwW;|iXҜښCd -4@JB!Sx*O0jzR1E;{b64 oJ+֤S{ N7TGRD !B1 }==HeN)irOJ{ds!B!4M"AnP.h8i3D !B1ѫ񙦁ò¤iƨj& B!bLss lz%B!bd 'B!Ĕd1{-=QB!B̘TUl@eq` %B!B\;J\EPYI/.'5Bn{hNrKKȰlΗث= ^p?G@ $B!.&iNNbDՆ '3|t]F !.=C{ /f۔OUV;X,DcQB wze+l> U د^z~0D#1.%Bz2ӳXN_?XՊb4MYV|+kc53‰xD !HJo_V/'5[%ԭX,:0o{(YOmCh*b*NmsYVâ"tuM'iٸaYcP6NvCeQք#8G% +rpM:j$uh$nqSʚu+(pO4Oeˠ, 05Fm{':ʾS]+/;%Bx<ޅ#`GPf*:a,T~TDcyU!wjrTk+Oaѣ^U~o KYٔW5Em 9:_>Y (v>v ÿ=p7kءė^ _TwRmi H%").3qrc\؀ft^(n-vVc7w\Tv&;n0Ix'p)́M`=گx!}jVl;_0[s洣kG0oeYN_.~G8x|4~R9mm_GB؊C-eأԾ[Af/^ƛg>HfaNģ-)~FRɟLSze>Ƚw/WWso-/\~jB)D,s.5B,.4˕|BK?tqZ@?`Ofm񎿸|#B,'a$H#hUX袽?=D'@ml_m{k*?1?;C28ƪ&͉9h 05.rKH4B4w3J3%ABlvsF^sVVXə3defb]dotBb67ַssNw_&Xqt_ןur/$4ocLtDm%Vө4/@ǤᤚK^a&F/Z ^Vͷ- Oטɿ|gdp'!{ZG;L/?g˃{( ];l\mߗC3 hB>3']9FO J|̸;}l>˿3LXHD !HjqlQLc^(!xSX6~kPJBuu5h8]DvƜYiiT&' $OTxs???w?>G+7]UEYXV]D̆f-J@#ABY9z(_u5tt 8r0+\ !Ă衃xJK}R||ijn=(Ы!ROСstD&Li' (@u OucEDDh)1=~s* ܒg^XB3 ecg7indNBYIgB!bԒM>B,!ab&iiBm(̑mB%w($GdMl1JӇ0A)3K@']B3ݘhX2=0r}zp8L4!Enm#t#9 J!Ĭ466nZ<a)--a& G%}~?m4MF~~>y4|4:ۈ8ݟ^ζ1i}avIkvO6c`kY|&46J`4F+ǎ`S!hܘƘ4&~ЂSc&yI J!Ĭ\}?Vÿ~[<w6ΚB,[+ 6D]3L#j>9>ϱ|rdH/|?'? 9UT09\f?Qa'iv4Q2չ1 Zw|͔gOɧӡUz.ݲN_۸aXO^~_.穏}7O&y%W\ ?u9C{4Eަ?k W"=G~z']땫Yɛ -C,4}/lbȗB1mZl!ĒbaPwUfNw]VThh7zj `S rxX">Nu+6%A{ !⺓|r'K}ONS3A< #-2SM Sm@4':L %44$<˞L I^i(eplג bh\p$8Q9mQB!B\F Iķ};>HpNB\o JƘ1(:eb3|'!|q5j'_T/-U֦#C[sp|`~RRI!8fb{y`O(c.t84 oJ+֤S{ N7TGskI}K'!b1$C^a)D\mKR$LM+,LG;y퉒$OB!6%ΕRI$<X2RiKٗ$OB!b6zi(yr(M0F5$OB!bI~i.@6p-h#OXb$hBq)H=Q !(OB!ωBO]$ B!.Vx)|Xl@eqžp\JrAꓔ#V! %|1b4r8X] ZAy{; ?RD{8^HKW?&5++XY7 v}y" r.K؅XTSBwPY6U6 !b̉2/䡧ߠuw$w[x>6gc*cZڹ즵$-?bs#4MwS} w{7V%8Teh?Y.Du%bz%eRt1 wB!p> (IΊlݰtz$@G=J'ݟkyxlx8Aݯ=~f~ȉ_7C W&*֗sj$4ּzM#o+PM򳘛 ZӦlxB؋\)zX.!N!g<Ɨ3狟uX  4S<89?\3NL{D+&+3f' {C-inU~]!ѳȝkP8-w vU+ k?䯾??TmcwO3/χ+؁//xݬ8gW$':Ew}_媩o20.4e&0& [?Ƀt s+]cm;~jL616v9zcJo~w+C;pih&]Ѓ|K/Xq~vws *;)۶|֧x綱ƗAO^}|3*ȟqbxStڻ,{ߙy7y! EKj-jz^Ԫ׭z룵j( *", JH.$1=B.Ι> rpE>nSt{;/>&;rM|GaB~4ɓ𼑌KJTƹrA9pg7ymwl6VǣĝĝBѱt>Xbcc4 ]1MKl}ii.u2|Z[% M2R7ݨSm0[-W{\B9'PeVn…t;カ^Kƀ!~cƎe\^&qF&}!}hu׎/ .Y䃕%'c_c/LKL nuƐ1Cu}WUZ\4@5|Z!‘npv37aSIyU I%nji9nm2dF./ /~3_zd&0 '!ǂ3~?@ߏ'&b 70v&SԢjr[7Lzj2,̦rF9.^ OfKXJ aۻnѷS^7)Car:}rkd287C;wsXzG^DX&H#JLi m-wgr4;K,`,^ER T. ha K>ӹ$4`g6*3QDLFGQ.;] ;!t]Gu 0 ^/q`'aDf7>xSճUV}AТ Dk CuԴʟ(+LDKb%gYI/ظa#~rh#󟸇 ;Fv% 𐚚~5)FqTARK΁ x lS@ɱV=ziet2) ??#E֨ڇ24ͤȟ$d^AʀtEV&3.&~~w3kT% 1%$&sUq )Ng:5wviwB!D;u%y )]IXla)yQi\o2VyIJFDWtR jT)8@ۓiA ±"8F|\wyeY N}[s >Bc4mc73Xr?O>;Q8?3xǃv#x*WKx_"Y-Ie;$I_t/\s3)}g6ݯKMFϡKjP_p)@廹~nt EQA>n:) uj]B;!n'F麎Kvs6eAO7tГ/Z_ń|b!x}c-k/dg_r#7e;JՙXNTԲ湸J _a~A>p'5pE"^Z&&c1ymg=c ū VE%Ƿt ''RթA#aŜ?w1hRzT (Ei>r'_D.Mqp=cG9 ƭ;91oLn:u$zPԼGOWO*8CR#ȡ"=98g~V9bbI]JPU wRvCbz?{)B9ԼFWP oSחNtVU낸z=2wkb۟? Kk.0qG.x_"8𦤒/vuc&6C fg,Uʈ% }u]ȍ=3k˫m'rO+zT~8#( ܲ2e?cW"MSS->?6_f?GN{t8蝙Z2Ç@qxz58ڍU!ÇucQaU.@9M n?̾xvś);AէPuq=Q^+gS ?)+먓ԗl1]K1 BUEE?A2'OgKYlp~\{+o7ls[+Pd &}#Qk;!4׆.m^?(JN9PJK8pwI9\ɍͼk(z)j9y|unT6m}b;O,2 PkYNy2fQ{fx4f\w|`~-4<)pS' 2Ek_-waT6®/Bk51SH]G~yi$rܷ\Q_ۢC(XILt򦭡Qml+R.`d{ikIN(X?jld8jnilT4:% ;?[βW`[犲O`nӏ'd/5%S !6Du|>a`_=~=|Ct-2&S\Gkd ĠiEpd˗Gt@O1s&1wġإ|K>Oɔ8K4==k8!~ɤo`+`ǒByأ3n6kpVNoie[X/~b %%:>_8*s'ﰬDGsũ4E3OQ27D uQw"=;3N9j-î^3o>X-}(q'q'Btn9T?x>6d"&Vmbul{t=1릑01&89<O<ƶkH2~:%M\ϼ-%4OQWŸ0׏n`xt<9~XnPWhcd %? i,)'kp7PE,[)J'a4.k3I)+_E+傞0YsCG/{+͗ƨ}"ik_lk4$TއՎ w<_n?lΊ'P>tO'cMt8M|c&,e w]۱D"\̰4{\U3ȟ{ _`{LzTYhT eW,E*i +HeXeh~{,̐ 1dƦP^UՀ„,z^S$\vc v [gV2pqU`ĤF'R=6*}UBٶEQd ɝ: (,p}ݵNfx2pQPS/jK_#wwB!DV9QW^+?lR+rq F.X~cƺ;\00* 6qf\4r7\TѾt 9EM?yl 6 gtMhߵ"mwg} !Նj}  7yQnm=_.桻W;=?+O!:HƓm $ɰx/NOr%BZtu&v?K0o*lpƓ% tժ'Qn8~LusB4F))}L'!Y$BtpShpWNtxOlY$Btf/j[hF|R2eŨvL%B)Eiq1I'B[I~ֿsAAA8PCvm ^8AAAaw!-ő   SeBiq!l4P\kJz2)ӾAA͡S;"qg}Z JUΖ) DGfv&Y^f{lSil;wH#60xTnEV/o_Ll7i%7A [[8)AAa< *i%Il(rl4JkGU}3[fD_'D KO'ek>'R( +( DH̛l٘q & 0a* i1OE6گ`I=T&j]YO> .6wGrDMo֎ڡ|!5{6gm   l%|5&o1uXŚ'**& QBLMLeS%m|;3f0qZ=XGxp\6>97R}hkM¶/ʄxM $ų1m aLgGu 1 (/[H񏸰6P$dWAAv4_38ѵMMZco4ΎY;Ҷa @E5O6#H1X:vncn !YTb=ӱ4B"a@gD6R> ʚO[٧҂i[C!ׯ ἦ]^Y!AAAarʁ!+W*Fac*ǝ5(֜QT5TDCUL6^J;DzcӖpaCd>;ٸ&*WGDaiL!BeEazo h!GG1G8-zIP,>mdbP^A dBvE1 "4ڙlD$EalɿK8 7xL!դFقz}GU4Ea 7wvsL'GZ J鷱ھzL[1cC̣ jRƗZ~uSND% Av(xH]&JX.V%\ rm'w pF~N+V`M}ɂ ;n6ϼVzZ=Z>#o/0zTͯ4~Fް{µA=O*R %*IdZ=U'b4~n71Nim32n [aQ{e/4$8[7{=)*Gj&sO{"ubiLKg= rcYpw pz( Lnj~13ip4~l֢2Vnto Jk=H^|NB.%ņeVY=$_{8oQaW¢Lځ=AaNi70@ljqr*" +b/xZv-պlLDFHYe雴yN ]v)uGM*̦ yxC4H)V@ +ƒ"KnZ£Z2!neV [YM8r2dO6%A#80M\SY/].g#7Z6CAah#QPBfTiH%/GX.RZ*lL_=H"2&vKF 32V"mh# *N46W?R{>ZkjY5֪&[av^*ϼY38}١\ y:0>ퟹ_y /V0 2S& k"ӄAj2*WZV\ZmSF*5 ƹrZec%YbgkG(:X6-Q*GeՊ>Rk}|< ³vX430+ai)ld"zC# mN ]Vm%Ѩ@|bq:<48 z} g`#cf}4`2? `QQ#Ѓ1ӎ1D7U[KWQ0=eWBvA8 C^wf- 7 nsxn[W?pt*E+^ƯA1-(boXU`&mB֮PͤL'ygm~ٌ?Gn[M_~e݊͡G |^xcTiM \9,3r2W;Ť`1`Ҕ& Ǯ T u39P:!MDȒ׆YiYo,<RUI8-CMa=b@)S~Е3+b42R {0D#pC& zL,HNKÞ6#&"C[@iLk76\s28[_ ^d ӆ?$dWt_ˣxh#- b"gٱrq8y^ڳKXds\ ƹmg_p <ҋOcg0Y}+Мf"5ZcDu$F%|@S7Q&ߪXl,/YMdɺXBKU8V'~k^0iweAh<ak%??09y9]uhsNDŀȡ)*2q}%?W%QQ4<&b'ه4!RVn.pQp4PG; m造f~k @#Lk9B$J'_}y7_yfL#s Fzpdf̧u6ˢSGƻ/\ç[oborXr9i G^J븖8Jʲ+ D>1v]LNԕ5k_ev0 qZ#Z F("ߠ,:BTau6&@F5&#y&WTbPY2<+I/_{``m%Gv*#brTMQdhÖGq^bWi^*hyDhR_T¶T4˰[KrdQQu\ ctS?k"U9X>|j>d]X+^gnQqO[izBrݼWanvHA筳5 ټܷ62{fpީGp,i Ollʥ瘓xϲ)vh4QyA ><*|zlvVog'u/'NeISm.\9=]-92e4TxGtd(b$3ٮ&.Jz,K\/3WD3( #â&4h7C#a,,а~;0(L֪Zfϣ\#< ^Ag8pȬ{ŘY'r9wTa)REݼ㽗[N&_¿b4غwnO' [2WלΫ7 M[gUdyC?㡺PgέOOEw,CpM[F[֎%eP-\rLm|n~U~gA8P=R4~N2umfV6N8V|FIUe/[;Oz%UL3Z=Z39t䘞-09LWc)F;DF}рňB@M&IU?p0qL-ce#(10B̟AR20\ :6|F>emI*ςyI1hw6!U5*.1l6*-m-4mAtkYѡq3ߊ #lflb$dW}gۡP^;]x,67rWOsѡ['?1`X}]\s|>wtݺw,'Te8jn 7ߺuoÎd,9| ~ް^vwR9ssM'[w,ϒ ƯY:QK)m[{죙ϝ g O Y;/'ZLr8 SQ/]~J S3rAw3W {V#O D?Ue{gIL~ULC H뀪)q/^g$xCʸˆN%LdqqLA/&[ޙt:\C$L^&C.4)Ѣ#:\Cgˆ&YcL^6 Ҕ8Ji~Zw`qDܱΖ~Lq}#! Dl^,_,C,?x!J3YyĺzPc>%RJ+ր ʘ05F{uí٩yXZUAmYZ]#Gi8KR垵TʁḬ=pP􏸔GBwUMkbuZZe&G&7 L뺄aM,GwlΣ=iqLȔ( ~,nd&}aHk(zkTd@N[ B:ƨΫX ɌUp2Y*fy,Z Md W3ͅ(dӐaa qb.,@:A-!YVDb(QsVي/諟_UVk5QRy$QHn~U\] rP*1P8jgp"Z8Z3š%C[.GNG˦c/ U1:T\"v̮r2ia;@دt!ѠF kLEL( /=z߫3W`,E>־Cr'%l_,IdN&)mBY83NLaXr#&> \wATP9DžkuN6U鮶iaսM-*~ ! HKOE.Ir.C*Cmi=}hhǮw׉LW{u)9GwU~l-\ {ǰl쯆+]2.5Hm\ mAT.GW]% 떭IQC&.3QX;+ MS^{eJ!l+Gm# diB.,abq'Y~7x~ ~Lp`R%EdX.TUMBI&i/_~/A+oooO~}?>B^7M1cY,o8j2'< ؐ'~7<հ˦X8]E籧_9tŇ̻_q o[pg\Gk;'וӖ ;'B*ba:#d0s ʠ20h Ț%BXMگR rœ[P̘E_?H̭R` Tה69lLYL\}KW2aev{2 5!*)̆<kgBԆ),[:P+J-GN,]z=yq_ğ&]A8gx"6YcqWe8)q hf^_;s:]> n84FG++::[؀w?D!c$p42^j.xUIHܨ?T&7ք(5Z;hW|,ʔ8!QکطnDueQ )r1}ލ@ NܪݥdKovS4~1MGݽ2u&[9A.  ͘P[L#V@lWƠPxk6Wj3ry?Aru[X_jҚM~|}vTY(oM|*PvhczF ], \X3dFG?-\%LZrao=_Okb VG([PNy;[ B=Q@`e%8uf]qR29^Nʘ"tjgarFio< jZ&i+EjkjbnRWV'm7#R*!Xz&pñ> bV6IY֦qph@̫v!t8TT

'"쌡P}7A8YrZE\pĬU?7z%DQ4K8 OeEi'.sF_S"W78X{:!5Jy0<$Dc'5ÚIOhZqٙdGʘ>ĹQ ] M~P:I[,wF:(шF'KjdSXz^B'b^(0mmŶ)sɯtQl6lrҢP&]AҷfTfͺ^֬ۍ6ؠȳ5)OdKဧA}n:(FEVʜR%օ )%)1aj"tMitB@cb|U_јgݸ2 j}O%ر^8&P{m羇)}s*M36.M>!r>C8;~Q  %0NZ8m/`2-Q5U4^XV@Y^4A/My0JVG# ҺL z<(j!Ȉ{Iח*Q'X*8`f lݘ+  g޵щD y"MSi}ME6I~K ?mֶTMLɘ8dTjrxعG'v#+]OM+kGAѸE_/Jm{?ٸxY@9dbbQ:aRhL;W&;O>֛S.'  ®z/ uJHI5Zl&˓'Y~DBLKSiYS0 j!';6hKT Ӵq=d><[^DHȮ   >O{j0!o|w!\yz<8 Hc+  _Rɲ+  _R AAA/   {TBvAAA= A*!    ]AAA}BJȮ   ¾~!H%dWAAaa _x^{{   B|KPzoFAAA80A*   D   {   ^A   WA*   D   {wG*?=5Aa/pIJAA(JS`ASnC=_MA!_>=AAXx?'oO   ;S+K}jַѾCwC*   O~ظq=>~wȆxHAAA`(zc/]WP*}`?XE/; ㏰mHdEk// 3RVd[h{)) Lbtt}[9s&gF)}O;3k_G&١YA%}kXP-^ {oiady,7 ^Y~LT朗N߶~//1O} Avu7;D?{\wu8{Z&&韰:cc1  ݿ[)j*t~:R^7񊣺d} ֚i]]L;DN8DJk׭eu}p܉l׿OWeVux[.۩>EjfQCREiE[VCwO<Agqf1E NP!~'kc12KW-tz;-F's_b}G\}ϭq5|U ٱ|A rhRCE~'W>՟(wu/G~wNZ`K l '9,qZ!;+c2wqkx#\q ''Eb,.O6,#M#]{}y9^ytCTΒe.꓉|,Z~L~[Lqk-p+w}qް?1Ay& lv2 l\>G6N4ŋrg?_Jn̛uq>KcحoMC=cu՚odmӫ&/rzZYlߡav|KNGyseVOhp\"?yH}O KgW8OIYkGy[ϕl~xZmx'X1c!Gw& YcjM8/DϲeKw1Tfۚ5l)5#cIP#wCϺf[_[nt,;X2$Bw\K߳x՟B]mo+Wv<O]z.S#8Xbx渚=ty|{a<a#|~Ϩr>JIɯ~(#][>}f\_nR'|> Ǟ1ZK|Wg}5w-pۮ[٤Og`?j#}]E~9Ϳr>}ptQ In)Xqm/! AjXqss_ WD7b<+m~};ʢ9N|"G ar>ݿeyaCs{B/ \ˣ ?| },^}.'ɽAvWE-COH ',cQr l8s7}o?Uh{*#o| yZmR,-tZw _[ϓO>_}Zݝans3~K^q17ۻ_q.{vwa4C7D(Y.25[&ˆ5[#@[ǼXGq0EցPȚf?/Y3] Y[ʸǺR̚mл# >=.3=8,^zpIEyh5uv:h@ke 2,hӸa^G6Gؑ?.](nl5sf9tiXScۙ%]?<ŋvƼ\\p sU櫟nX0q-[x٥'r;M S{|0[+mp{X[揷K%0$-]Ps4 szwE,Vr#;`o?W ݜ%ذ[x(([T\dA9'.K(>#>Wqpno?:sXEF~̧"F쳯_!m[WylP^{3~lBh9<-?3`箕'3&(p{];3]7_\ұřCk-o VK`[\xzan==|47ߊ\= X/9m|so~Wvݞ춆7/ܺApc>-t&a•-um'=k+>}:{:Xw#4O EOkɱ*— iڸܮ<.^;W'H>7o.sfi7|w3׬f759Wsہ UOcgYfCnc;W3hT;u4>rJu-}kGφma.;Ws_ȓOYlo^{cX/P|M34ª}K%a$K5s;syUC; +n=gTi_!m 8\x6at-x,l[Κn܌޻Xc8:rk{sKwe4{觿5|a!A^Sݥb#KhAekǽ;2h#ϼ0r ˻ X139ֻ;'P9NRM7-uGÕكf6϶DȪXgg]Cl9{w|U-@(HAPAŲZ׶kWQ)M5I~ss̼$ |v]Μsfr<<=+IQw}¢e\\]p1Mnh}5vGymaݾd޾DsYܙaqxcEst MLUU6mJ>I`@ ăis/p],["_}).brڵmOtT 8%ͤLs tsq1̞;vҳGOf4g}qN+] teeTy faKOrS5$IO6(EW'yC.Kf k,qc7tԱ$Qpmg`z4+!g;҃qج|4;w&[YsS|s4˳pEg !VT_vɞ?֢9| Օ ߟ²afc:m|>'o35º#6Jc+u1mꪊ5&Wx81̪CF:Сc gK^ҭsd_xB4jAFl^)s䁧gGQQ5N88NhVL #i&سq*CUϵOzdķR@#GK%6z,w^2(Yg0{U_~'3Γq ijҭ]Jb0"~`feD pf ~Yl\vKDGF"K81Rޱ"kHF'E[{%B JyYʼu&~QLbTֻкaLNWzYBoi[87 e}o2WUOB*aTf`h YPy)$QȗDGaÈ*`ݻ9*8y[nZgЮ6\ 񶤣Еpyp75f6k~~ց]{ ً1͆|` ݐ h`}ѵ%a`˲u ɛ7%XT7ޜ˭hm\q 41UUٶm;u`0FHHHy0!N~α4M#22}vM^^jח5'.G6mYv$[b;h:^ݯyH7F9쫪'YU)z :6V'upexOΞu me?V:ejƩ4;Umr8# ;2#|<čyq>tĄhLbh=5߮`tDfrf_- {iڅ\JXQ~s%;8*A 3 e4K#\2sTh^ufQʸ{7rBC1Fޒ V6" K\' ..P@b*dE)qHYqRK'lǟ`);>n/068 N2T@g؋~ J:]N4@n V:$-l<{;A)rnw_ وmiȲ_Aư;.6(IQIN9E-.=ψXmVf36 ^hD2۶%(׏[|Q48 Rp#v^,ݙI|DGDz%qsCntMT/HOjQ yҳ!1F:k- 0d 5{VPj$RڻkzVwpF%bVȱUX/!#k [ yڅ#l[͘SM7H/ $ R+cpкj(ܑffe;81emI#k Yoj6hvz#7yV5aԡ%]37= owuL]|/KnTZٿeNJwh@H*U+VTtθusn`}u șoGS#J [) vA@Jj I'XH,%t*ytڽ4 -U`͆jjbIBCs1}h4^ŎE&+}#|0gPJfF#l#j.|T^s#t5,#M*HWz@+aIJ[Q3ҭgMS4*РTcǪLCS3@Gsg#Q e,a]gS8c)5- m`]5Q,?hcr<`0p!B T̈m:SLz{`Y ($žƣ}*pj(L+0X'ɍW3?Cd|Sx #d?bIAԳ˦D/Kv,$ܺMyTTV}ʧ:lu D>r QTO|].r=ՑT@95c幻jeߒ糨3w]CAw{jvlIyeu48 &,_ζ \FvnĉDGt:uà,ŵiJꪪZ*lՊ||}9|]th%*[p3hٻM`mr $f%vt_쩸82AZn<#?O??;u]3;9er3N.ubb5 f%mg B8C/g@fRw=TNo]]%ʘ̗aT&{ UP3} x'v 9塞y-t_![54a{{3+' :+$#7Dg. D/բaN\ʲOYy2l wRRN1eVJs/ёѤA< kٚ+Ѿ}$#Gh(*H}+\@j[ oNil6 X/qAJ#b@d<aF<%P b[+Fd QtJuNf Bv)#^T@oN..Y衞,p@CѡC[r%MLޥ\<Y%^J]PKXIl+',;b$Y|ej \/}X4 6GK@2_%u6s`߹ 3=/۳ʐ[wm/PVr?`#FwO9 UM^ήPiKy|hW$♇v{RT {f_Xÿ<3yX3O[Γic*/N(]oh}uf}37) 'bpWhEә1sur |A?׿P.s=璌_ov#@aA 0#0gc ܂xn^@ߠn^_ڑH .mAhd&c NNFTUF$*`۱XJpQT|x'ͫJ$IBSU,ׯ;_pV>{W) 6pZ ohрޭE!݅z-.dUVaL'ĕi#s[mMN-,aqOztpfPTV\q塱W[3O:+ދv(V!Z{q s3ͻzsL=u~=}M/7]Twsquu0gV&mzl}]k<ە)JJ%Uܣ%$&8?̙Iv'|%rAn466v&" @Bq7eWO|wF|~/au5,{wOKR.+qU,}%Rx>.n.u IqaH_,j}/u3y餤RpoB`c.&s|x,J't6Ū͂ "Vo 2%ri BJ:uqQ ,-%ͳ۱6l6>ΐAC\Nǩuҝ(YZz~";hG :T722lS͌t1" ϪjFQF9nyI&3K*LLNJ:ٙ'Z[,-/'ZJl_Qс^ L9D2EtOs0OV[Z`?|3]c+3+d+N D FfVb^'=ğZؐp(H.6hf ˎrald}e/WZ!;yƛЦnHd_K,HxS=pCl}Lҳ5y}G9{w.ˮFM85T'v>!>Dx+V_㻚"p=).6V4TEp( `qwwze=!% 2;="jURC_[ǝ[;:2ȉ ]VM|́˴!JNY~̶Mn8yqgnt4r-8'<7Pr@E ۋ.w? S j"wN\k7j}ԒBKvkǶ0ȓ\ʤwծ=֚9\xNꪍʖ;⫃fVj 9Ry?o(ݾ;q]J[Ywh Xk GƔ/_@Y̌ǐW nwuvTUAHei5UEQÁRNa/w'+uP- tjj<ޝW+ѮI>zB$ FItʕqdeF4YP):Qɕwܴpq%"AAj~]^zB%39-au5 P烒۵Ḽ'RB?>{M0Gyd'J: ەEɩ%>Ok\AkysaH$ %fdY.<}nbA$ ٿooa|ʹYriɫ+.4փa`>Y̢+Wd 3U ;ynA@;H.rJzO(*smoX]jh dg;'4(A?o?ٝ^)KZ(hy0ŨH Udي6Բ\?Z:FUFIس{7Dk˖p^ fү@;L fvR(餅ĦGΐ̦Kv% fAy>ȓq>! Ϧp*=b[.~AuDzF:3g}K\Suv;E&ϲ{nн{m(dOgnD~5ogFʕMV8k}.I-z9QճWl疤K.#--Z" Ippp&֐ڳG W  T&   MB   B   $D@*   4    MB   B&}dZ!#ۮF9ᴢĦFt_$eQ2mZeH.t ŵ#ґ!.4;֯gL%wu#:]S}'ٶa OeQ+ZDңo/"| _   S5 =}4;wٳX,\\] &&&VZ5fUc+Fm 7|}w!x¡750}C랠͹1e^5ϰytN[X=# PF Hoζmݻ7:u`0`6`ժU2tƪa$ ɏЪ ]HHRA@NUkEFo7g- IDATx'vODeT!X@M츩9$2ؽk\ òx [rX6-1ƪ Ǥ{0=ZWWvcjZBC SSo2G0};y_>NY̦Cit޴< O.>Ps1=>]3v: S\7ןcy/r t ?{GdƷ_{KBBB AA% X,Y &`4㏙1cӦMc„ t2N8AxxxcT0&q`Lf~g;VgsED;BaՂ< TЛ'~um‰Yzs*}|/^W<>H|}_@kݮ-_skjRHF|pV`vA]=ɽ/Ҳ(޽{6mC OdhO.RAAZIر6m^gy:N85Ġ!18ìث=ճ~&Ye`\ JZa>HΞxV3:Z5֓j'ևPE3-]En*?|>@? |Z1`ΰPtvwu|-a 4!CokU^+{p`cӫCգ}[BA5%+>ZNÖSh]յ;%%UA6)'W#/Ml^ia_7dd.Gy.᳟%)Y:^LOu=)A -(y罱(,,DQL&\z  Q& YZX, Y((hFqqqcTw h-05f,ų=GM M}ZAz.a$qľ&4DuX<]ט; 40|ÆVgajN9+?/>YnU#o3<:Qom!7f5ZY  [%Ky@ GXݎnl6SXXy%ihpn.Z& [LNl~"9[ &9 1i 8 1}#}oF<ޔ4R* {YͨLp`d4U#a"7˨+eEmqb/qH.4|h}}V[]Lsw?<ÃצJQе$m;N%Gr:dT 94EAAlzӈPdstA32c$<#<%$ p5Z@bwww<<طƘq$^{kKdqK~D-$tUwFV%]88n% ϊ_Syy?@H[`Ĭ{#8zi IXa~l8^g*ndhC⇿A %+=CBhza.MڗҟQt *]Y9f^zFbTA_ o#X,SLaΜ9qH .if\R{y+ b,a-3J'ۖc "Ѧ't5PM0R_ Ԇ1/}ě&MT.捩͵qםt^cUjdT6I`w8D[[3q﹀֟%I~kB;BN#뻟y+hd/?>٣է{gA+1SSU g'ЊJ$>R%KI]Hyp-QGy\>h6>B338z?\)#o,qAwBAJkYEQ1bÇGӴi%%ކ7wbAaOɐɷ|g$~Dq8]w@Guv!A θ9U 6e<"FoFSf|zΈQ/݆4#щs &0{O~^ e{Z73d9V^ؒYLt'E=x||BU6K?=hvFצ0G7h_0j?S~NVqW+uocN:iƐ_$4KdjAA4ʔ‚Lp8p8(NMNIASզ_CZKV|X:Ͳqs)qU=O2V)Ow n!E򰺑}i#N- |T@\1L3 ۙb\Vе;W[7oOvaiy3wD0*(g3+ъ;xqό南|>E4$/% %{Іwt[^yOFw8rk"EzDF9Ѭ}iJt  BSh5$ztLb6ab0'Z̚U~!Eb؝7|[0 Bh]J@ #43)əT]BJR* Ni^1h?~=P/,3k.䌢'|\\eyr0Yy5f4 }i`YA(رc9rH~~~ewwwv;ogQ][cJjr89`ZNt^6ccUSm7 ?-z$S_6?r/9QeN؉GpJܜo&,^E_Ya>QU,#ֲX)~W2=Jk;OŐA(-Ǜ[.Ӗ'$L%q tqF˙S!¬cGu[[\#dzHV:ӆjcb ,MH%_ Ϛt3g%?fcKT4$]tޟփ*'Xor l~SNy859~UCBO;˂ дc$]}  4Z@ ЬY3&MĤI}ǵdt;U`9Óe n* &k"2Ibz[Ysf#.]5<3E]_)OK-O3EuO x#s%&3FTjlIIή.maW黿Au:Veʧ,30*3:p{ׇ{p*X porBB :WAH~w!LƃID.W3.mM1q1Rh.>iGPÜq#Iu^4eO[!kS7CA1a|ʹQGHg i߅WB F\ AAA]$5AAA   $D@*   4    MB   B   $D@*   4k" ҩSS7AAAAhdD@kϞn  4,q4u[QMfv.6jς BmTATsbт_wmOJz6-g?pjhm+1>ʌ#ԠQf}pgAVn  Jfͤh^~WdOfäq8y"]}rl$<̉\6'O?u$2Խ{XH(X-8$=.N/p1ϥP 7>VNڶ}W֣DMeS>zCXίMtxws/O + ;݆T V" gxU;f05C.^X#̚Zī8Vqj>[o䔹N'OƫR8w^|EM7Sh7gI\fAaboWe&SSgrLjsg&oCBC%bԳT鱨!^+Ђy/R.XDP1ՒшA[I2~$5Њſxb lm G?5w=}4Yhb[džm7YxŽsL2]fey$Bֶe$wz+(H\S?W.Tu$)eP}ik'X\3;=J/s kWFcHnS6,^]syy'l2Wk.A+QB(**p7pw'00ooƬ~4!h?Qw)sfLE=($%f͞32~=2GԱrdZ6JX%IXl?hG3lY5Z rӧUeBC|E-&u::ERwEL?{Ƴr޼MU/Zuǀna_!WQ}g9?7!rᨑ5ظAi!~9v(YҮ]DS7 ;l5m"#ʙt3GsArG6s@9;vPumKLmw>ң'ay|Q&[C߈m0YPO1|ͽVZuǻ$aշdk0{y݃>f7kˇ΍2Q'%<75$".=ciC|b[t5uK Olc6/ c&Errs4 S{E~8O}_N/%7cB frU=^ፉanG/SV"NL"2R 4Z@zYΞ=K Bev;&'OA۶mK`a5#Gsm-WmPZBXd0v;B~qvC_,\D1NHdfb&!:ˮQn[9i'G%Hz=BaA>EZ^1v 洏ӅJ;Iu·E]Bp򡹄[9hA5jDFFzL^}عk;7o&006m4Ы+_SQ\"6Pvه 7F˸tq]gzr h^yx˅N=:8ԓJiM@X_p3ɝ+eHx_7Gw|C6# ceצk=G04R#ss/۷SoTCl582d@~`m ģ‰kX}ZArŰ8Zj;\C}Fss a%Όr+*~6SCۭ),r!Wp,Aipp)bbbt$&&sNzELL ?q<|||ʆiUHNaGݧ'?%(G5\ EGhYµ)Ú̖')Ɖ}0o}oY߸q_z6J"S׿sE2D%dڷAޗI~r Zg$\|iK ڢ88QkwS]#vaУfP6*:Z^FF"ȁदs"قwlVҾmkثTbUZfC#Hݶ- .HX3g!僣frH*:Zb[ nٗAr`y^nDRY h4-- F#whwqTgǿMwK%ncc\H-JH$@PĘN=n\qެ3mYdJf]~s8c<ν̽sgҥ 6 FlL UUUMH&'E ֯/gqUQa3uDпW ;Ƣt HXSt[Ԕh-% ݕݗȵiWz W3|e;)X8䒵3+=U/&[Qc=kmICF rJs~m˨Զ:H>%l SRiMzFBjgдm [-lC9~Dr"k8ֱX2ric,\SGUJr{Y%N!eW7擟}*}#@!+Wtg@%]c:qocMX}2߁%-%˲p!W'l)j0?}|Bg;fĥnARr60k ufMEޛ5[ 362fJf%e.q7}^5XXx*ʩ5!ͬaW 8HLowR-)`nP`ǠSuo] :55|>Ӗ g+)l=9kLB5Ǵxc4c'rtmr""GIHIJ~?^6eY`őE\*l:ƥug4BeYl_޾ Hvtl6[a@ag{,E־هIUA! ^DicǃqQGaĿg!"y'^U7JgQ;<%,ƋA\><7 `& vV`dYRYW 1rOdS^C͠ߓ8SF2e`3+7Gߡ1Xe5ekID,.dn}=f>MGQx<8N@Ū[͋=O/ž9.յ$=~w8g8Un?^膈T#]8."e8\8a,@SɎG]guccQk32rکya}S`g fҧp݇eڊSm 㪇3qL,Jyǻ/=ABSpHi""GIH^ ˲=[Îi+"roB׋efEi9Re$0pP-`ճyNF,Ww3G3!?<[vK)5m$ = ,0[osLMޛ$M01bi9o^.]Nڕgݵ_.´8d ⏤z|!op՚AtL4`5еW7<;6/#&1u㫦Db;l I$௮0ueabg҃g'_^DNahEu ȶZ^p}{:XyF/?(7]*@Ey%&4Rm@`b`WrZv<ž1{˞IN?EtV-D6eP̽Pjv6DXX^vapivWݖNvP\Xn߃u3֌d{?wLO\ǁU(v9c&.yoD~sv )Al;ױ̺"? ʴ֞>|,}<];/. tLhic -OL,{qq.e[u)6g)~[Ado~+bR~܅`tFY S] \?gjaY/A/$8;ɐ-=|T3+X` Q9}7MT{mIjj 0bcOCZ&h"s,xzsҔ8K(h41,1~xrBln'̅CȪfΣٵn|AO`u,^m^[6 o`]v>pec=I-\k2p_m(ƈk`@~ ZK9j&;Y]->C-ÞqPTy7pύ|l on/gW.Lzq 9HBv1l= ѣYj#G3}񄜐Կu3J'v[SF18?-Xaw/r`Qq=)nܽ 4հO&6&e'mb`7n 4V_}*V)1zϪJ 77~G`VDrǦuʂlkhsĒջ9 D}KW"jy նy0yߥoںiǐvfu<:bao9v~1$Y,߾1I-tٰb-쯿9xkSQagwf<;ƾ /ƞ3&<:_tC-nɓ2^6"r1 NbkO|;<{s Ǐg"ͪWO1moleɪ*,AnVh_ol1xHϾ(^D#wgt 0˲L!'9hn>dw .zGU@qӱro´g0x`AYlj|zcEczr,.,W,Zξd GO[`sgizV|u #&e5߭5Q؄e&w:bֱe|3cN?IhtƞsC~|MuToa͚eܸN漓o; ђ:{ Q[ l-֭J#ICyfo7 q71M06l`Keld6o {% d̝ɜ;$h)["{pR]0be %Sr x|8ΨU̷>V.76r4ɼ-x&sOslcy|7Xzy5mO['>g,?sw:0O̚Rqq|z_ݴMqCxⰎ?P|gqM oX_ŧp|Rʷ,K$;7{b&+Feﯯg'rLv$̞ ԕoY;R32f$L,93Nh1 &"r뱄pz}VMMMbGĔf^%UXW6ə6XmĀ>׮EK污$"ogML$M_FdT˂-G'j#KDDv5\+8[^^ [Yg%*D.7뷔`spRhŔ`@cM MēP j,rCXӵ33Kccy&N66n5UF.Ea^x4,cR9[n#?7X1(29︒2d^1_Ɇ_]̓S18 ;0-U1mf-gFT0ιjn`Ծ\ܬm,?, g"ϸoZoW˰G,N$ˏو|'ݣq1T <6.آ29pSیé7gꋷXآ2+[DhcϐFP[[Kbb~w{8C`Vl ̲l\_q";>b1wQPԀ9È#;;[q%4ZĻ=B/,4ΦK=T/V| Deg# _GIC ;VSuaC*U/ |0bI3G]] \_[ 8ƎDU[Pi:="H4(ք6V] %$)}wvX@g1?!q{>;|رrßpAڽeYؒFq}qiM p;bݛvvRxylVF"Wor"Z?~/ABBT\N_ݳ3n=@N2'?NBvd9K&'3 ù"h{&3_g\D]uoaw0>W,SSI"B̘ۖK~Ϳ.b ki ؈O%';}c=` ED<=fddظgqWV!x۷Rq[D dH^yA ['~ѺFG-h~D󈗇(rrR8[>eYyiH鬥5l]?C&08H ""]ͫa3|t|A3bIK(/Z aa#%=W>lX Ta2`-^NVn+Ҥ>+`Y4ReHT29kg<ϖ$p C/pMv%b@B.}FQlD%2097O9{?k: =A\f3L(q;ICGD# :ʊ 0 q/ ra1|8\13$ljհM+6 1{Xc e}TU5\mf9xk:ﬨi:YĦg4xQW/r3LYꥉs>cQ܉|$-e.]˗Myysccz |!כ³c#ۙ)oUobC Tޗ)01IpzlmUvZ74_ Wwf2_blLf0~SIDR"1Բ:g]U5X$x_,`Z,:##V$)))L8+Vzjn7111vJJJXr%cǎ%##'׃;Ա~uV3b뛉 ߰b)ޒe,#rp͎ؒƲ,ťm2 sbY tm^!PB##۳WY *) +ĶJÁɮU31>fN?%upFٱ[;k#5`og-4`?g5P#&iˑ8w$䭏fMlX !Kd3a Kji5ǞM['xؼ|%>KVd 4MΛҟ3KOgsCU^dffqF&++;:T3QYÖ:Ai] l\o/l^ S߮ºN' a.s ѯ9)RWۈ7Iv{"|B~eg+) l1O:QIͣ4Z6s~1zqyzj-Woםl{2LA9nǻ-`aKz6RbpZj )l"IL꿻4KZ>Blg-'g)9Ov2V=e;vP7'u|Ǎ uH`,[Ƭmä4N̔*X:s)UrȤ3?u2uc"Hg:[9T``TT#Gdȑ=ۃ˙q]@t1L/uuԵ\~@]:{S'A)E$-[U[(X{hb?"{<]xHZ4% ڹ4e a̎i((%P չ;ܵOrzYm/z8b<Ǎ&u,Ct|!3v s5Eo,&ѻ㺶3!'|ko$6.Wgmʱ^˱6m8g^:o3NX߾}۷7c_\H"W/| #-{;Ӎ׮X˜|q%Ljjk#"~ͮ*$939r]WM[/!mosNh 9&aQϟdwae#".`ߘn8IL%f7IRV/]؈K&򈭗 DҢ;oIDDDDDD$,HX(!PB*""""""aTDDDDDDB R %""""""P?GEDЦ> ҞőE}>t;9RhʮR %""""""JHEDDDDD$,HX(!PB*""""""aTDDDDDDB R %""""""JHEDDDDD$,HX(!PB*""""""aTDDDDDDB R %""""""JHEDDކjv85VS?֑Hp 7P_׶?RɁ>(!Cd3-uXneѰ}1_}5%o1f)]=^y>^:ve(:v IDAT/; U%O\9it=>>u-\p=O>2m 8+ydiW2ң>8H,= <nI񌧸$?+`#0Ƀ'sŠFm`aT=h-G ȁǧS{LӋz?qU]WL"/3=yg,{Z#R9jxJ(,k–ANj4.~οq5<:WWFqeG,ii:]n말n!m-r!W:1]7VR\VOzf 1]YxjJ)ltrb:<ApSU^I &)Xg#]ˤiW %UMX ddvbF+3u5bO m'iNhn)Y'>$> \!}u)(Ìtos`n XԇO y&vR3}zIҐ3Λ8wgn>|~7 @#0'#n|/ ͟/RM 0Eg0+g?@ql5:dt7C9C\f7[>z{ ~,  9'4\T@C9~G\o&^p=x,ۯei8YYݛk=ΰ7.ym}?+qv's7s=C]ո?_EiFɃ&ƥ8Ե-|?6sⷠ\gsE?7?@X<|x?u7}>3yg5U> !]L l)]A|O=Uu)!#0}ş/9l*~;^Nu2һmCN>6ډ,AfC8g7pRX܅ 7Xx+9Xy.&%r||Mg:9]˿e6č\ʬW+/o浌8vjY<|h?^K~s_zӯaHļ,~?sN~Iz6O;_Ȉ ip}'iuמf)OH韉=rw~m1ﻖ&O>'to8( ;V!ݣTDDX ˙ȕ n=z(?3rJGCv2Mfw8 F7 ^~1L̔C!}q^Ƀ_Օr˨NFa qiDo~<v6<"}Ϋ9@ZV53_B+n+Ƿd }3<ϕ7fb^L/u퇣Zy:o_x ?ʵ4T,`lzCcR0k&ksIdh'!g]x;5j. ^}^ rù }e8lb@VGt5 ؈'9[n83edj#zp,|&[/ɣK a@e5cR_ÇSܴE&CrpbSL 4#*?S> ~c,d3ri$rڍ ߲bYZx27bX5~~7z|#J!ݥ%DDggԩ l}]hKf8z֬FH/ c]Xp )9a82-A/kk-<3~Wݿ~g=Kx1bF3yԾC'pR6?̚W 9g:uN&^X qqqXEsrC3 rFQc8/,;+f 'k5M xjs<{h" E ̲,'Ɉvwf.UcWFW_=V˖y%XTx3:tZKeaְ3hik݆d&NӪcm8qwB6 ,r6[`l_VO1>LZ; }<sWB8V!ݦR9h(fegXTW DZ[>ggg[Bia w<21`6֒}4taX0tkͧ¬Kۙ>shՕQhbdѶ0,r창4!/LgMk@q vQD l0aDY37pС8sW|$ә~tVc;Ll9Yd}ʙHn~bvAE㋇O/8n )\qf`y,ٸ>?~_ [j,pI>\ǵ9g']GHnf6lvA맽c:1mF}!X=Ĉ -Bf{8p׾N}H)!CMLe6WZF1Q_>NX+t<Fdv. W.,=@vmDϔImޟjs$|1kWn.ay<\ڷfvo5le'%^4ǦcߛFD9R>9X{PF8lj&[D>}&}!/ 3+|ˆq\pϹ^ۮ _W}.2dHv2.v;e>lرu`D׆]agvX?3Ȉ}l/0B|7q=ezvCO  \Q dɮ]XAL\L>YŃ= FdXMnc֙qb5a|s8sl/I[WnJL8I}}VuR7~M7i~]W"WzYsǔԿeӁTw~'u6Zu}g{J /AzGwATd_JEDߏ\Ieluw Vc{3t` $ %Y622CRg;ɆY5ڹzNv/J=u[SQFi鏶FzU^LIۇbl-meؾ_q11J=k 3rN>m2sb,6W"3JLK$NU—_fոװb{3tR+wʖ7]`6PЅ69];V`l_:ߦ Fg>VS=W[!g*,x^YKCk[o=[&c}Yl[_U[z ~#0!5Z|.}o!F2cŭs #D`5,c}WmƗr\&FbM`r}2d߿8>Es)68>i (X ^ltwݛ+یxD&%mj\v~fsr?XY3cp紳vr[`B'n`=}3; @S=W|lY}.GswOPc;ݸ>F\""r -#+a l5E>;ٵY>3^~5N{bԞM ~7:vm5R#lgkϝW%2^) > #L$o>_/,*D+_1ma i]u)^Fb4|͇_˕58#Ljv,=̴paw#^E|J/݀/!.řrW9cSm~Il+̝f3 3l ƒ*$}3oqfR8 >ds\ p͚Og`70bWq[O3mIG;=dftę{<>ϛ_~ɋ?koe~5WÈh0-0\ Ǟϥ0mxH8~N3L{ΓN旇q˭WL$+s_[c<ڻ<0E2ugs%Q?&sxf? a?7j˖z2w? xjzK0jF>'fEWԅy=l#Ѵ?ia#$dHD1q_|l) 8.ϸftu-\.MlS|,z G<^_o>E|'{u,!^iȁ+K,>-Sn7n~69H-tMWBGvo)AO[=7:l;;{gtzՃFs:x%9nʷdKf1#x&*Kj M!+#v" !|ŏ-xk(,`K$75*V,5eE'޺R0] gzJʩ3#I$!"4uQO,YvsF$_=S]BQeĦ`b 4URTZ;`'*)^ɑA>ԅ,/Eg"#fZFϱԖPTRw \!wWT;;8cI?9FHEDb"%'`v{q22;+.>q!5g,鹱wg8 Jn~jwEYygBn^wF"1;6EĒ컃f64-OP /`쿻7mK_DDDDDDB 슈΀so9y>)r4RoR9BHp'w"hʮR %""""""JHEDDDDD$,HX(!PB*""""""aTDDDDDDB R %""""""JHEDDDDD$,HX(!PB*""""""aTDDDDDDB R %""""""JHEDDDDD$,HX(!PB*""""""aTDDDDDDB R %""""""JHEDDDDD$,HX(!PB*""""""aTDDDDDDB R %""""""JHEDDDDD$,HX(!PB*""""""aTDDDDDDB R %""""""JHEDDDDD$,HX(!PB*""""""aTDDDDDDB R %""""""JHEDDDDD$,HX(!PB*""""""aTDDDDDDB R %""""""JHEDDDDD$,HX(!PB*""""""aw""r*..w"""rJHED$d)""""""!#eYAfFȟUB*""""GIhdTHڝZMNŲ,,ˢ]M@9"v"#IJM#6..)UB*""""GhEY15d&!1C"C罹;p75޼aWRL9"CGaY^p'rd2 b8, kMl|B˟*!#eY쪬$3~Oύl41 >REDDDDea&nw#JFEEe4af ՍPHh *JR)""""r1pG r eVR9hASvEDDD䈠gFE]!#Qz먨`٣HJȼVFLB21pp#L""""r w>j5R@{.bIK#_~^geC]C?zgo"syIDD7iRRZLiy)^߇AzJ:YlzDD.(<{ݼ_J36cN:\Ƥ̥ew0l!P-͋5p.HX\LlL $nPOii1eE>pEKw`#uDFe{m,m;X?f~|up ۋ hSGùTB*""auVRRwz=8v; qFDzuBWDG OgƱ\ j?.᩿Ÿ~@~w4ٷ '=*ΥR ]:k8?݆iZMˀ䤏böp*"+qޣyp/ױPq_lkHM6r6#ILð V}1개r` Ax5Q8$u,}rSvK7SoF5cF'^+װF3Č|FHf(TL"OR"SŶ4$)7̨6Aj(RJdؕlǬ`2Vl&#o8c"+RܔmI :7}:$-E e%'lW+7+#7ڻD #UFDe(2ф=2?%|73ܡa i5x,Ӌsh:>W̎b?VIUWzhއ-:_?Vwtnw&^װ{-fy#]S~5ɛ2ͻ%n3#nॻ*{B2peLg Ij8{yV5\~t ]<0诿`(N39r]5.|z"p}}roP~>=w;.RE_zTa#S&z7bOnkn}"~\?"\6_ƜgCo*6Iʈ7WrBFT%""VQWH3D ,5OQKT7ܡ;10g6O/n3.?cC IDATqy*0M/='{riN >ɚI>OL8$F&c3M[ya 7=!.2dO>>}ޟiw׶Ǚ_ι`Uyrck LF,ϜD⬏f 'srl۝4k[grFsUͼG}T< p.%M|_&Ãv(!JK͠W)l]w|W׽ $^ wmiֶ_mm~k[m묶~ں a6=>I dB|9qN=!8v!{ҩw*HZq9[y-pb/pVl::ry&.zr~ϭY`0l$A};\JJ}ʚV\h79W18nb)ֶY~/҅vFr_$ 2{0h`1ʊW?f7d"fa2(gѵV.؍x1|R+{}~Lo6w=$?Tl-?睽_W0ڮ, { !8RЍID-qFb@]MČ' * !iޘ={;7foidƕF?mo\Nq O/?[0 2,#odg7{ԾvPa"  FQҸlF {Pun:뾜~5t,,G\U]8fE)X;ֲ>RZV9/Y꬛< tcZAw[ꫮbtނr7翦R!s4 ҥK(.+&q80IIx-? A!ôl b"LoX:/lc)޷Up8$.+9CDK j& pl,ĴmŽ!]Go8Շanf"+> A=ǏDP !bTelb|l4%Pcwb@$Mزq;)x5śX1¥59^FK_o[u0+0!܃G2WbcWnge3jٗ9X5?nfP?B~6W,diL~ _/b7f2眰؜[F=-><0 tnA۞ HBrI VDŽq3oNzS]D!DGӊ:CiE Lh\GػSw'\w Z#=bKf|q> J#ujLc)G*D|~?@{8JչU_9U xhz;E88 ) Eb}p-^[l\DXz=C}x]va.(,v%s0+cMV)%6"!>kD\]'PzgRwxy*-+UGl5' #(N}?KUeOA7jַnk2 Bqʹ\npFrR ۶Y.]q$zcޜaZ8C|0A¹w8h.K'7-LAq$5] F)Y>ʻ[MﰱL߃B9 p맸-4v fe60F72PSdi9l_<;6V)GI$fHƲب_Wkr`kZ;fW啕\_hjo]'L؋o3,tx٨p_{uLi'kݶd@*]Xq=]~7n*'+B(lTegƖkYWr5]sX SMqpHc$d-w K\%e#5篸VEGtlǔ  G^_0nfͪ,)Iq:a[#cDU5(^|ʿa,'nK2 B.Dǰt2c;N\2 +6(UUQUؘF#rL,lLV5k7KWŐknd\N(T+S 0i.<}{vRVmCFVo 32?]srkp}M|"%kXVH0>Zc'9AQ7}0}V[R'L2kVb2&eEZMj\Q;>yIt۶3w y۶swn8pU{c7_X[QcݒӘu!Aao2v)ش5yX>1h'Xr óGD?I]b'a#jL}TzfEb.ױٳr%{iq''OP[;MfRz#J<eas`jvuXly'Lz)S~vkmd@*]8w̹lܰg{ӧr*sƞ !:ol1{޹r㣈 PVV~ΖW =N^-(LT0ٜPC|ϿAY q*8bv={<Ƥ=tt9#[k}F?wȚ8>kǧn"5Ҙ{2h$(QZ!g~ٓs[m笞=@w&耚1QsD$!˶ x PVVQS`P^Y=BSVzNV-\HqlfgVcyjH!B}Û斿u$O,i8&$ɥsxj_M^M~M857vȨn,e)$C 2;`hTc=8xϬ{m2ЁM,Z0i|qƱ[NQÙ2&_~͇_81\46ֽ?.n_=-g ?c9oZ6]e?~2%Zr !h7o.tВineKSqݒ_ԩCdì0 Oڰ_0 fݫ9x\3cѻ}6u2Di޲4w.Ͼ>VUU)*\vm}yY'B[Sfj]{ͣv!@mL4OdK!D v!qq-mZGZ-fnwkpElͅ/& Vќmkl/ ]2Ɉ=ƴ4on∗X "w!TȾ<.?Q* QwWDi]xpfY&./x/r$ӵS&?Rc3TJsT2zص Qt֍TA8bኊƣ:R"T(c Ѷee.n/^J<czFRc{FziUS{_z *3B!BǏ=׌@D#9$܌W{k1#t#GͭSmT6|ǭm Ť ;R~R-)w[B!8놑HHmı9-9urWSDxIAIݕiAAB!tt!g69E!B!)!R!BqZo!ĩxWvB!iE4,BUeEɶ&ǝDB!8|S] !8P>TȀT!Btxկ*BlB"EAT3mX+ODB!ⴠ( C5bY.-2))*$w11QU֐\u )B!P=(MJNp{vmö~NTM#::%4i0 M4B!dk@l|"ѱ8Ug:r6櫹*(+B!<5b۶ Dhch͐ !B՜1f|5e#d@*B!N;5;ѦvӒؒB!8@TK}B!BqJhTT'q: !Bԧ͖8TwiƒxOB!x4 uq(/+(0pH%U$$ ~'ZI)NF< !cj􀴺h?%tF\|KE4)).`݄C$w ^'ZII:Bq5{d&zgqtC=eSc? DU}ղNē8a< !ck iQAi陘!q0mEQHKDĶ ē8cq< !jԱ/`6p@ gqp H۶[K<3U[ēB!:Hkj[F[aEynI'ZIZ3B5}nb~iYIad*B~i[B3ēB!85z ubAIJ B!hơҁh4'!!98Ү$mȳB!i֋ּٕC pKB}J>m hޤCp'7"P1ܙpV'_㦿I_wWۤ.gON9qP(ά!q'BњΈ5V:oA:(ԁ)[龎¥.R|wDL~ s?ӕ DKS[UڎU棨(tv稯7>y6y_qaLí# ;!B4eסl;YV`(>:ʨ!G r`:._Ͼ}g ׋ 9Ŷw-5iYx[X[OapVg| fy>6/˙زS]>GCl-ޞeeӿb&:Zl13u!q'Bޜ֯Zyx71{k%Y6((o_mc{AG}fYN)7Ŧoe'>ȴe]čYnv꣠L}wg3t]_܁AΞSO.+0Vk=]gH}`;OsCVU>(&{w~J/)$W]'w_;ݓ)`sM'āl,&v%+w"X)q5S=ﺌMsJ̒ݬ]e/q)]7/q͙ٱ ڍ;W|wʤ>t W޴tN>rC 20gъ0n\_Z{!fW3X<)3=w36_υYŽٰCݙ5Ŧǣ]KI !4c iO?in|>?.WemB7t&Me^Vzk [Q3λ8/&qHcaz/->ؿ>Y5 ËCr㋱Ϻ:zO`M|[zTdUp1|r+]:k^(z[C??d\$o?²*vjhc^?OűL ݨ0c+O'k9y*$\~'<2<5}V'oꬭSP~yeou:Iy9.^~&jt?W,OF(sΣτq336Q9hb}.O2s*~oDo T@Qy2'/|fJlKx;N؜x$Zt;!Bϸⲫعs'A0*O>W4ͅV@ǡ_?:RK5|.>yw?Mo"b6~(ZsfFʏ]u} <>~y6;b3yr}IlM;}k (QirȚm&%#&>ΟŢ[3!8 ~քIs$) )9S̵Wm(q'q'Bݭ! |>,¶+4Mr.Mzֶ)W[P@i\>p.vfhRwؼq\ܩ/b' `tQٹ;W8O׹o9x;{(C=q0^?=nlA!Fr>bϬe#w=MF3`u9֓{n͢rm0l$A};\Jڲt(.4Λ˜ywp S79#lwZ23',\N aDQƢsV<"x*cF=K6?48b{xɫX(CGr~(nl_*YwwB!h+DGG( :nX@Qk菢(hZ\v1E& IxƗZID EV!NV#sks:zEtOα lcb;0b&,՗a&xzJDŦhO%mے];!Bvʮihƶ,>Ac"6z;6H +W?+BM<9AP||ʱ=+5Ы'Jba"J'aSoDStm[ǚ5XK6r>~rvUҗH;?RKg) ~c@uPC9c˸pFFچQ_@e=KD}b57lN$S\|*J)!jX.qׂtU<]N!FzW;2(UU+:og"-܎r@b%W\EtIB={)5<Ⱦ<٠HOiwG962bsAF/}cg/Gvvܕ1Mt>9zwAUE #ۄBGv(>ުEgj)<LIӪ6IX~fVvp;|2ZcjN r?:!CQ=K`xj/i l!!c, oNWq,<:E̛ E`A WSeL[pu:D"DjTmm%(q'q'B]쨪iD_tccN^JTpZEUzGAR9(č:?E=gb؊ UKQ,zzbvi1,km:;6fV; /v, %d;"UgBtI}HX_jǝ$vt!l\ F%jyPwuYs 'fǒwħv'6tal+\#;URr`>;SȞ_O&}2mEG{_ 4pW/_A̹c77*=-C(IROv~GGYƾ]&%SKpY\;zK&%˙Fz\zlzVĽ8Ep)@2{6wca!kKy/AM(U9R1k㷘yF͕53V霪&s6yyo>(K!N'\Lqon6tb`9Q)1`2zy}?1"C[>Y`eD\D(X]wYô{N DNǥ|(~f\MQ!B5?)Z!v(\06m(qמN!R3֐ջz<4Mü:V?ݬC( !l 2)-ڣy,x+=mUppV-AwAG7FwnsQe>{~ 'C$7CX4/Up|>JVJDɓQ>4bsI|K]?Oq_"okbtه>R2†-)@̭h?G_mL]ϭ`?U׿3Qf!;Vg=4&.8G.j*L+뗱Yh*ϊB/ɪ 1ѿ;̂j{c}=_T:0 ;l_J?>_;\ (\vA2'^s)\87`R8؀m\s=d%n_+926]wwB!hݱ/P9Cr|\.mFF F _>'8p8˱0׹plcel!>s8S&^GOO1[\v滿 {w5 3c{xkNDw( G?'Y޹Ow*xe#Y6~x폘h-7`O%Щ/0쀎^s]|>۹]ĭwεFx S>? f,PT?iþßu#¤^7u;es5όǠf(j[I~dϪMI̡20sg,V3bhuF^|3N57>]z TTO þw6mMsbsQNN!펒|go?qm˲ضi=zk~u=ټ9٣F]rt*ò,tC駞橿89+n/.P"?33_!{:n`7Rx^=:X#=35B8ƭeDׯD`Gjemw )׃Ks8e#7N1u^/U Þe誟.aEf]a\E6صe.dĂc 7 UJqK|Qjn: j=Ӷ=w#=8f;l'͎SݨdiIuc ƍ1ި(*X2B"+L0h!{ bHڍdwHؽy]{֊'!BtL}1heWQ<ΈQp]膁Q\t`Z&H9Wj6€Bls΄zg/ƴ)2)imHH&G `c:)1m(~RN1c!PɈNjQ T(l#L5YUiuB mi)/& LGZf:/.[r,E2 W-Kg( !j3MÞeȞ 6F]u <;FEv@%#+.'iмxDNN!MZC3'~- XmN8w|]-O>^PDK$'V=< ̤4%Xqr|;MfT/^9upkI~%B!Dь5mMCv79  78S/'&?1[_̨=W @!ZI+ƓmB!Hze78y7Mëoόr{>A|wXY$:& !BQu a mgSqkX8]IXB!DfHUMò,TyձnT"D}ln6:Ɠ'V$Bj]븧 > CQē8U< !c:ဴ*EQMH0;L QضMQ~> b$ęI!_fHEAUUQcݰB,K6P'2))*$w11QU֐J<3I[ƓB!:F!D'%q{?=hCFfbZ,$$mOB!踚ˮU.>O$:6 E &d*i(ҪJ<3A[ǓB!:&͐Be'ZUUlۖ8Uw;έ=C Oі$BIǾMw΄tfnˍW$ędœB!:& Hv, q::ZI)] !>M$ !ZēB!84!B!ɀT!B!)!R!B!D5~bsp*B!BDZ\fHB!B3-lX!B!D7f?ZxxdEIENDB`Solaar-1.1.14/docs/screenshots/Solaar-main-window-receiver.png000066400000000000000000002222011473525213700242770ustar00rootroot00000000000000PNG  IHDRQf sBIT|dtEXtSoftwaregnome-screenshot> IDATxyU?zknnDaQUpepTqAGqeA88::.(*.(*Ȏa HYvonݵVK   ٟ"~h'   K4G@cs""JAAC_D%t_+_=8'   D"*|A;_v`   ;њz([<   mbwH -=DAAΝ/AAAvƵDg9~ihԵiR@sA|  {/|i< 5mU{xPVJٹu A㨯YckT&^b8&SYvш,CCr?Pl0!(+.L*BF :D+0d B'XNLiwҼcEAA8on4NS/OQ_W }2'hU*ؙh6%gTѨF4z~s:OқkDZP!O9K-TU!NS*NKD]k-DfvC3ؙw4  pR@d~rNG3{xb`<~:'hγBuhĖ( 1MAT*d&s޼YM8 ݃u;Be faZ^`y;!bW;_ im&o]btAh-d\ S}NF Y>Y]P;'O#AS'7kwrcl3㎣Q ꟣h[OO'džckp?9fMghь]4v=TXf!/Ga`ږDO1Lmݾ[2ȵ`,p6܅ݷm3eRJ F׺f l^W#,}\yO}b"p0EN*ƙtXFTMKFL뛨:| =fҤ};j}Z+5؎S3֌=-q,n1$7zn+5F+q{P@iX0<`ve: V{hLKSeA+t!pəyAϨ )lP:@Mao A?7 :y+cg|Ih$o:##UY,cѨLc +XNf}  }]n Q4Y;)#U+,E_A@{0k+Ѐci{ #Ms[Yt?8XJa,dm¶2.Wpz5PTCQ>zvnLc> `0w2ѐAAV`ECR͜ ]%gVãS@uar`[e,`Vw c9d6߇SL,QN(b>AA(:[d')tT$ "Qfj4=▔I1acBr]'(K25Fbh{)1RXo,s63-aG0B$:6.JYGKݮՉK?Gm𙞅Y,Km(pgk6K- Dcj,ҙy(l,UѨPx1PԈQÿ+F :aX#0-]QyegIn-6X e ʪaG,@lWAADhnLluHDQښĶlz_׹qSv([c:=prHƋ6(F2j>?^/> )eY80S13F/*o;ݰWnH}m,W\l~&F K3=0-gi s*ZZZ mGl^-_^ 3%jJ6R\y3D--2 bxq=0g)xlx*("dt'|  ::&_H~j Cy6xM?VD^8\ufb^d3s.c0=oS 5ۆϵ W jZ"Y -ju/Ĉ_G ymPGXx eJy;K^Eәԑ"s6t2 ⇔C E^'0㋨ObM(&޹p2JG!d[09 ,BY5W;'aK9 ;vȺMܻ{"ӿŌŇƓp!2-O^3;{Y~|S<8"+9I;ߖ`*2[枍=Ry8.ea|kx$_3E.9N _ F['} NXV4gOΑs,bV yGQM5O^~U5`"@Oz7R*^He q|C%X 0lYC( 6ŭD(1mcE[0pDYgBPQ#XCO^D&mtL!薙;T I6; U{~.?ܘv;}8z?dMotV_OK8x}\w;wءc|3ssoko)0:w~#e6y~#GU|bbXڙQ ;BԨrtLԤ5+0zNm7F47Q؞F9*A˧B T15uWw};YU8:h\n€+rddo\2"-a 6Ta`ĥzyB&CM&D{(m"P=/9kpj#xss3cnՏl}$d݈1@R&Dۂ|~4b1U OQ- X@~r߇|K;[EdV_|9c.o[[|"px߮6Z;q㻙uvSrS=ζPލܿĠ=^.#7m3vIշ $ߤʡy"Jlzbd$[A)պ%7ʷ'G^|7I^:`#K,tdmHƣ=8ZÐx~_MMIX 3Rd'X.AYܙGROPC)ll:k p]o80~`FѵtavoQV`f\7;K8cZ)T.lڀʴg )YFE/E׮ qabZƻWD }Wv\uI'rJ/8cGE/{2k o =~̵֥{ыxnpU,zImV$zMs>n}w">,վpe]U3f2mj~G>A?t(3ٚIIc}qjYGEe 4kW񄾋U֟1(ͬ -h <ӳQ|YY26ӕiOcħR 4$`H۶ |'FShZ0l5@c2 X`C%t*uli]+>+ .}%|۶k5H nd`oZcL_X*CP k*a]݋A aW3!t,[Yd,h''Ee /p @CO@u?O?Ga7/OLN~U}wqI X-x)F,(8Uۿ[eG,eqȲ,e~ CuaO //{d`A?iw>`pX{)q{5HKDKGL, 1:AL510= .|*Xdm7|/Z\3~vܭ<,6(XcxjC GLGNX!9sB@NMċ \ǔ&c|!2AcפۖErU0ZZoC< /b#ިJ]c3ΰ}p5"ڗ)\o[֨;3jFuc:eYh"'OuمiQ@ V٦}az+Uu+O6jDMnNg0Z.UI/)g%UѿLh(;:%bAtfA* N9EѾpKc+6`;x2#5 hw}Fj"lG}\hV?q A}I,L~"PщClק%CaZ6:;9.e eqR_4)eB_+2~GTS"JH[^&Bs]tU jw ||ڦ }߭ŌN{lWek?FkjZ~G֢+ohVE&%GcSBh;`EִI -]z'Vb"w>VLxJ?X \+h *x'mb9l@ oJ;-kQ]~{ʌScgېK_;JV$\X 6#z~Q8WlZrYfjWcQnD.D6AՆMKfv1$K.VQ,,N8?~hK薮\ttsˢ|ԡQjT D"UDy XGGkӃ(g))`oqܘ@ vEKX ]n;g5-[R3frdzwkTl̯p$4RcgjŒ˯޴` =[iP 5A\Up_֕"Oa?b+?xǂhimafavAQ,L͏lY~jV9uA5M*G8 e6Wp_8[ZH l2{+lH=sl̢QZGh+S1% }t]6Iy)'oT(faX7'غ c+,_aV,g`<7/O ( '| 7܁;$Sqb!O`Gz&?&4 Qo/EOq#6zQNc]  SiL؎OUb٭:(?4dL2&V<}ً(=$8|jғvJ^@ QbEmZ§z !+ `uaG&b t8AtQV r mY(dB)0ZSB+ۋaqW*eڲט-qߓ4',Zf-Q:L 6k |LeAe1.>BL |B9N&Ϩڐ[_\g>ڲĝOg4} pC9ŧ<żNmUDk6wҁŬi-4Z"ɹuu/-ě">~qMZ㨹}Y #^Xo"w0Q 酜bvkW1̈́ߘ*:sZj ;fniA$l -A9e"+KhbR;D$b+ Q_OqVơ NUcE*עbvVCϐ IDAT%: >Cw‡$=v>W ~Νa'w$bxѲfڭl糳y,Ǖo%p`H&qqcҫyàF45P  1uS* Ѯp+@VMQ)!qBJ~:u9z_דӕ1:_aD[MJ#..%ᔀzfBP?;Tf[}KÕ %a#.ƫD cF׮ o`Ʊ$C3+A k\gO_|t #| 3.I 9Ksgv Ҏ"Ivv#y}™Ͼ5[- p1  ?ӓ\C1ש&fγҖ^úA8䴇ft 5Tq2St1.{Qr} l(S >v*MlkݨH5͎ Ō #>;*]Se;Eq+hMqX0R8C`9,'Cg,vVr왁rvqCC)P+32Xj%TͿ}ȥT֘8ܼhQ&t狘rzhMzR>`"l^8pvT2}e]A!KteGf톋úRS/w3߹oׯ>^8Tsrk?7ak_68t?V.xټo.@wT>OgᾬKFaZJ&: j6lT&_C.J!p5m4²zF# AϖLXgB}6ddۖAmC.eG7>[6:\9rKdTZ$i 74b!WTvZv E_BW1vsCr4.˶>l'~6\b%4{A.٧{hP֨ ]t)V$عiq;prVvaEAg=|[ ikYB~jODAQ]33NS'eȽ#?9<^7M1c\wiQnCyXxN&`w nF r8Tam+˒]<-O/>.Pl=2Lveߗu X27t,>h 5NP{A,@CK^WN ߵ޺ˌRq іI0E5O#3B^p/Nexh^WBW6픘w,dR=mj5Gͳ6`& `˘x:njt"ކbFvV0`Oln{[A=Ͼ}/΃ܰDJ/n (od77z;W|y߰^n†eƒL*06<ɿ::6N?qǿͿ~lӊt:0һo|g\xFvo=>k@9n;A8P>~ړ*NEIuɹm5Z Vm7cgz)Ī;5,tﰚЯ$=-t2*Ml?]w3BR"0k,m#9@ղJOʤEQ?FL}uTG(Wݗ^iYo.Ti7ҊC5IR% F2X#%!rZa葧,Ιh}ݳ*(2^4t{K˫(|pOx0.?m׏;`Akijɒ 벣'v(Mαhnt)چrm}<578)p,n`k=}<C|k 2A*a] ÇM+-uE>_*OcQIhv4{d˃kaŊ˴F-E,NNU#KVZ-NI[6Sgqy εRX4~,JRB˲Ft=uX~:z,dAUq))tk+^!OF~38*86FDCA3h}B"dTPSUPQJ'~<.7oc}ѫ̓9 T ³F@O (ͮFC` zkI%-TQ1CScS<%2zlzZ ϊA_m{21b'e gjQhkSnd-E:2\}|;/Bl؁xmtk(ˆbTۢ|1ˀcoFI68<*uOAAH+ZVͮPkEN|XJ0UM$-gƪׯJb(9i U^<%M?'`UKYUȮN?*vD  洓R0515Q) 3ZSO\kbwF*b_2`  =;Q=k' ⤷$7 6<)4JU)8;9: '  :njWԫ$kTC.?T ׫L}<J7!rƼz&l71Y!W LлW6LuOh S e4*T8~L3ˤ7AA'iŘOhMUMᗱijךjfɈAAat^gHLOjj6A n>-1c5cƱ?(m2>Ou![f, gOfo"ј7++&6~as Zj L'?ѽԯ I_  F&&֍shtذ|:z>k{:n>C}{:[ٻ(LEC}?0uj,O I=أ}bqLZ^~4W3h,W"c&x!|  BncRkL e*x= ܯ)X8;uhA!ĝOAAgBD;   w>AAA- %|   <["JAAAx8(D  lqP(qAAQ'  ³A!ĝOAAg  *lһhg o=AA c ec   <%  0DD   LQ   S@D   %  0DD   L)mتW?A*^AA&R~ O> MܔD/Og-yͅRbAAy{xO/y'>_S)WsϞRbA kLh4İȵҒ݋&ݦp@#sappzz{9s&gF)հ̕E^t 9ao/?ږ=A(nF6`TK0-$sϦv0e&?}!K{򶿻 Kx׿;^M_ -?3Bh@Yyq٭fx+O<3nCar-Ә5sw(~A8xQJA[[b O;og``p.\L[[~B^D>L>}mAxNW; syÜ;WYW_=uWx:0}?4¿ͧn5TX.;e\tyNAXŴN{{JM7yfӽ 㷿÷o}%#MQ{GȴuҚs.焽,% `ulFzw)P*|}zCj6tek1Ws+w\v%}"@r9xߏ GָnR)Qq+ 300aY{^a0 Z4s䟠|Ͻg>UC%ag}Įg5"d/߾ߞ1őӏ#1Nna6͌C6_ B<6~bf :hOɜqϏC+X`5I_Wcˏ} Z'pذàgS͡WlV~qLz6l׋eqKt6dڸҗv>;~?%RA[nݜxI8R;^.yz,YrG|bY{ٰE"0rɫ? [<*0ؗrDzdV;NyO>p+70OE;.kE1ujW}q5%֬wM_):<]9_/9cAIZcn&VZE>7u5_Xbg=4P{>q3#Tl7c@q$' <~-Dڏ : 1/6*+xU"6ﺂ;9mWw4izy/CNW޿b}QEGdMH(=Ȫ~ psy%o>_-̺^Ϳ_VF>7pb}=h YO7 S۹Wp[)z.׼s7ORXѿ돖0d9e>+Yկx}>#Wa6&>g.Gx.f{݇&1+e9z5 [5䷮#.#Og/}u׽bG>vzԀ#ş}o`c7|k(-~HbUV|Ǟ|'7</=eŗJ/y#T@^Y?!6eWM89C8k.O~S?f>5Z>?Vy>㕯WEBec8Qr,&~O͇J rad˭\Iъ2~?)ڎ=L~#3=A?4*[[sj/kD u*-}t}ܾ CrEsDdE JL0̺w+ZP(ʕ++eקP(ޖDz,lۉU֛yG.F3gM\fzg[CTy|y z\X|!p\|ć^Y`v}1+x'C_dF]5Û{W<si'sH{ ) G"o~+b]gx1l~d%+wg/c?(Uml ^O]L tc ,ZcwݻwcL4%KQW=?9g٣֘` |#`9ן`ߕ\Nνx8G\I{N>{{8G~Bf[\+е2ȅs'W\3Q؋Q<,N=?ՠ{]k?zT4;q,j/xtkdϟ߂ Lc o8.JRǛ;6<Pdzw7_R?Ou?moy+gqlۧKŭP*- Q-ŋ2o&4{Ъi n"Ǿ=9 \^5{~3x4ȿ80wpջA]<վwN]x3xFR9sZ__{>ݾC|էl;xSQЈ屇3T]w57'-ŬGw*f/?G6=CL_Bu588jX=x ߹+|E[xqԙ=^tܵշ|_~>i~lz`{ca 0K?>ȚK>e牵O0{ܨq.fztF+WϘAp/~mpW]}⎕sy3k2pRa Hp"k92OzN:j$ٶNPR[V/~@jfuם='oG:w? K_˻^K`j.=8կxh^֋[gKA8yb\⺕6n uHkG;= {_𲗾 ο>>iooo1x75s,*n(eQuO 4~w eMK'Iq4ϓS<wYuQLo?9o|5xS?٥};g`&6W?jy݋fp;OW|R+XCX,>7|g-|mWLn Y_൧!^e^+7nsS_3/]\> zת<~Op+?`Vc~|>krsPRp!w?< {z[z5~]q髬{cb/g*c Sn^a'sJWZ)ŝoSbw? Y-~?gMGn5+"7o}JZBhy8`O2$N!ĢP,P{޸B7g{c;9V}X3fEr_E99V hٻ(ߙ-ɦ'BH ;ĮX7'R, R-(]RCB'[g?@ {>σ읹g s{?cx9DHp;ƃ::˯ҪEs/ZHffƌNvv6 -v4l˺uԩFcSJvw[-tx"NxdǦ8t06hGr׾iL~ߧ9M`T4c͘[_2փXzKd֘|x#TSU":v{9BބkQ-4>whIauTFHI`ǚ @ "2H9BqihS[7uɇq6,[}ٲj=inhiI;_xB@gRp*oZb I;޴+4•BRrA*#n^^y\I9h8Tp%$"j񡊥 e2kSNWŤ#.pg ֯H ;͖G.t(Xj׮" ͎]2{ }^'Pź ;~!=@V-tL&)Sϴ?ӦM CGdcǏ(FuPp ZypZ&t"}/|p pꬅ{Ӷ[+<!gJ !JK38ȌvM"ʴ]b|?z`fرf%Ag0kA`((J59UrkY_UJz2rQTEA/ уƱծcۺgzq^V?7ѩm4ʼnqFQ1P/#BT_L6t͆gL;-[Mwdv ^y%ܲw]:EăpqdjESӶesotmɹ䊊EKFO4sC0LL:I׮%P:0f`6p8W umL|r)"""J6W,e5ݻ_23NׇHNI/ahޒ:b݉`^[dy&tj{JFԽ Á$Er+I!u`$JQ\.?m۶[z۠.6˂lܴYiS+GUKxP35q31c"$_ѧ⿤tIdd8deeQ,>DDÄᆵ(T#9_6KWs~VѵiMJ[kؗ@ @T.\9j28&ؙU oxO>=f%? aTZezzUO F0Vzvu/ɴAl EUU|}}ɵG !$$:u~<6lhۆV ._ƽˈ/xF vtmx7*Q^.AKuJ#wb؏P1tj 0lX;3 ZgY6e..1e< bxU )aLF'*M9} VNsUI+&3e ?#ҵU*͛1aQ\k m|w3Nm߃OzvY_q;`X3 O s\02cBk߸аa,X,ֵ<12H9̟sQд~zޗ'J:5j@wqK5ITnn.ADFFt 77722qww'##5"IJ:Ԭs@@KDVUq|n L=3\6V;Q1ԼI /<%zr:/ߨ lyI(8].l6Úk2Ȫ!9q*g3]:Qx`@,z{xic8:wgwoa4-Qi=4W[,}UGpen`]Rj۽'ggM4 A48v>s6ᡒS Q>Du2C-?GCاhFFŦ=k-_-#$HԖⓟ٘Nǚ#17FF6[Gf_4bЊ[1}4s] aVZurc?}33=b~3Z!nBba UUif0!'>DDdԭC:ys F 7J˥\(@Vv&۵c݆o$Qv̠/_>6qLOƸX:}g2Cq/1{ ۙ0&ڎx&_:g&CB q )݊ӹ>De L+ծeZ5k5~gVBFj*x7;@VNZ]VL!&/_'S1RGOO6{F4>OCdz[@=ƪ߰ @$fV7gjp7MB%:2qčx*ecxHqioKM$6yKX ȧsܡsOgT497YViCf?.W޿SHD3X!] 7pqiN32pmfN'ww,?Z)ua]Q(Ϙ^9ء#O=Gg~g02!odkH>0cxl՛2ٗF+gFC/~Cvdw+頔rd~H%Dy{ٵ>1~9TݩqzUUlپ{3?gddyRhԗLj+?Lmp~wQ0z۪+.GAjsDWKzeorqnPu:Zƃ)EHt#o{f_[£So8.΄6tűGMDZ<0u fɮ >z>a7!xyC䛐f:"G;gX:4ooBe (~+@1գ{|I P%"Wٽg7 fFӌxb6( i.Vk.F`@ <4gSKݗ(FmP}=S淢~7Bn)a&oxkQG^\ļ'>~Zbr}G<}…&+SÉ 71V!Μ8ʜgéLiRu]XUb4-xuD d(!Hn]` W׋;5R/-(8:OOON'3?JPv'^pܮ} xZ͋4NOLp[Y%Eӫǚi~ vS)/Tە7ԥ,}c!@08WOi45-} Zh7Kl`$ihz7#Nx,.^UR=R񏽙n$&[sûJ(B/Q]9P&uLt Ӆy{?ip8p8vv;N:؝uV> F#n{~ciij̓չj0=CS-7+ي4{og|J+ʠC4߁w.{=;c=%f֛Is3Q}֭%=noE%NT!&ăv2DB`O~%wB {oc{rh?F`r#+֧.6;Y_/HEa7h((!,3n}^բQ-ʏjWNJq}$ob䦛qos?p2)؟*:la[Au>ՓRjRۯj4y]/WXWۙ3g!;; AGsi8N\.pEzF:ޥ0(1p='Qגl/}~m\ơBoCRT JЉLMQKX;mNE$21Ox~5lD=kZoaҧYrK+.jng?wۡ`\W^r CgYmڽ'Nd `.և 76C8?ץKt)󢿿ӿcW7M#rXIZ~ksg񣤭eA1'>Ջ*YGV2_P;+-T$8N ÁpNvrp8N\ NGnnWG:J\ї.ϻkFnljU,-'CINXM2{W"ij_8ÙLB`7+LGw$ C Cf 5+wby u Ayt L~9gN0p[Er۟LfK!Sݳ99l[Pᰓk( l߶nғP7]#QED]5xӠyx .9թW !Ҩ>IoZ "cϋٜ((Ȥ< + y1ޒ@ !5S' qw:iihZ4Mst:u0L(–͛UT+¿*BoEצt&NKPB}>1Q}ixS12N$r8)lɫ zǢB\Kt*ICb4982HJ:͛ LӦM˼B!D0Zp(Bq]QU]{nY \.g^9y///7jBlm[Q$B!B\7TU~ԮUl233 $y* IB!c2Zr'B! IB!B$QB!BQ &J!C'O!!]q[I:T7r!B\]2O!B!JA(!B!(IB!$B!BR$J!B!JA(!B!(Ĺ8A$A=BݯDMƮAú]կjUpiir3.0!6, SvzU{b3zـmRGB!W_&Qǎcƍ8qՊÃaaRF+?c%oug4P^<zDŮѷ:|ʮOZ"ݞXLGk[7/ƹxc Hh˗q-bc?pL~q/|6 W4c^NU;mJl8zsB!5Ta6l`Μ9T^={rw9>o,XP* Y hiO?mrQb2Pռ)4=_1d"GwW0T)1_mN}ڋq2eJNg8Ic?s9o ȼֱ?mbȇyy-^%FbmҎM*E{, ^[{ѣБ<99g}ŋ~R!BUHeZYt)7t|w4k֌ٳgAN8p (݉ uhP׈u$UmLݫXUKH;5 X7}ă-۾Q’4)2դa}Z霙*,LAiǻ60gԋ<2> }OfZ≍]ce Osrwck&0%=Y{Mz<Iu4nÿ(#8ӃZXJOI9mKۺu+O<:gߡ]t؞#FuֲT!jD%$$Fiӧ|w8N !OP:ws ?NވĆ,<#z:ҋoSdgxaA*`^_Z.7ehd%n`ጉr|?p0ɃnMb۲ߙ:q}Y{ I?`G׳8m6$X6Erea~엌Ùǡ` l{'{d%3&^G%o.+,⬦z<س#t(֍ZP(78sH.b(uUOJc[snO굟z[ ёSu;nsR/|Apr5:YYYD֨AZu @jj*~B!*$JMMEj%33B5HKK+5"z &{Nbfi$҃[ۘ(Z{Lz)_&|w֒c _r{XYY?nk@~<2Ixl0iM d}4ZFqC6l0IU)H|3Z=Ƭ86Y4kւ[Fm=n'6<##xbӧ U)}thތVC[tkϝC~d% =~G,ŴP<P@.pnaOi6-fʲq ?OռF1$'ҵw\GBƭVN`ݎB/( 0)~|V(oFF3,9Oϼ3gΔB!p IDATת$*++ UQlXV23m\rrq\\.t]';I7D;юdҢV̙̟g oxjfKx/zd32H5SGr|s]1u~{ތ7WltF?ގL{RhH'e#[yWLŋ2s[ l?[%`"Qcmz<&;,}+=_!ذj޸nf<׋>LQqwM={_1qאGzgs/S0yYq\4 - *Q8Z 3^_s+BlO>En4PGwj^"o*m)kgR8{p>V g FCuRd\~wW*-'1xkQ!X5͆B\8 #o#''ҝX =&L{.\?gEd IMAÿ-Y{([lXķS?3axn,j|yn=k׸Dfɇ7?B5 ES~fK1^r=Tkٞ/-ФiB˻bp0⋭/߹$Il nF2?y8XZS'n${T<S0Lk8"bVWSDt>!BTHAZZXVz?@.]%PVk(WmG~[KO`ÏqVﳾY?i޸ܱxzxD.4mxC0Jz=҅ [WǬ5G)M pst?5lHË4)h?ƙ"1)aT/Dž/[Ǻ5+kΏ|6>eDF48CR`2cp:/;q8@11+ehowqu{zeTwGǒ._>P׺v<[F#C9,V__'BDeMvv6Nә6*jHb"#K]ˈsO.*Ⱥj2x?w,]юc`I 1|}EmZޘӋx]cz=93֜JӾ |)%^~u^brŷ5,ڟ1fph|:&j]׎vlvѝsEՈ& bw\V7qMh֢ }ɏf~hWa 6M\դ᫂FzqO3,?A!׬YOv"كYG*o0Q9֤Nt4X#QujޔZd:B! TXa;F5.==+ 9915bLaoj3I|e]E}'݌s~*oMxŝ?fxZտ\{˔?f+ƨMjˤEpO- O0 he=7m+ho$l=S0o/|*x{)Vobᰰ?.=f0g/ 9=d0C`)UE#9섘"ì#9dct]oG% V\5#yDt,x[nqG3.PyY4p|B!8l#RJ ы?33mN2Ā>W^ܺcJ$ƌڼ΄go͹q? cvngD\h9m.@ΙÇ/#%̧Yc..f=)kiDɑG.WrcΧo2a{I=CAǕZHLi ?,VtiK4ۡMںҶ/N:{ [WjOUDs9XP'';]۫lo{B!*$;سk( xzz\Upi[N{\QXxFm}'\bu!oPDƃ/O&#?8^eY =CTC/~; l:PxFI-l+vJҞۺ|2ip`v׺hX7R+(fwx ؊kfCPp Npiĉʡk{2/ahwKvJ"V6|l_?Y^ ׍!L?Ȯ l޾-6dwjoWBQ!IT:ux̘141LlٲS0tPב{QIVc=7϶07tZ&+91s&HMeŬnlZ·92p&%;/ђf0m g(ϼxSvuT10*' k sǗ< crw}h_ϭ,~4T)1Ǹ0wf#xkaO[)U9ka6lÆ g;), cW9\!7yg ?@~/0z4oⳝn4cG픶l>я1 ̥Y+_>ŚSZNN{iQʽEpF׉|ZFC !UXx5jļy7n#(87#""zQ5Cf,ᾦEO*i7q߾Mvuk ߼1Vϯ`3f>-TƘ vYKPl6̚r[o&Զ{RF=Zm o<2^_ Yt-tY'w\w8/~.Vb`qA?|;]"!'oܓg8wԞFXr9/fL[l3΋mN;, F]xM|yS 6DpTmXm1R'|tWuNGǶg>Qd4_'5's:d zD}.ʋZOqi6Oûdq+ǯzAU>EQB!?Q#Gd][7rcv}.VxJUx2O#~ ־`m]1WOٵiTpKߖ1o.g٫7rK}FuoK2l>+z]yUp3 cּ 8E$216jXNn~k0E?͒]FMb٨yy,uOIۻwmH(\2Pt !\sdGʗ)9oa^@x⎧-o %xf^x]I[XA+N35w>&oZ)Gաp)rLUe(m+ iA{]M6YAZB!| o>(qW1vJ$MC`#/dR1]0$c'A?CӀg@5kUI3(wx\4J#;i{MoZQt;gPRVB@X4"|/}:!IBS8z_͊23xӠyx +P|NhZơu奘 рWK!B!JR!%B! IB!$B!BR$J!B!JA(!B!(IB!$B!BR(>Q=^q!:!BT&B!BR$J!B!JA(!B!(IB!$B!BR$J!B!J%_0YtR54QT/7]#UyH!Bq$JU6褝qp~&QtS)NΖ5g,f1x\:۵}Ïa[}2y~ a!FTu4NV,r>ԿB!Z&Q']QU튧LuSH ?ț=6SKv\F58FMc~r^:2s01*\-Y|*Ze!BqUXuyC-ߌclش×]nQiaxذUJ?ק'l͸80UM3Ѿ?bydzMAÿB!FO;~('Ow61dPN%'V5 6UtWs촓8:RUq(o^ɛI´lNɠB!$䉓ty?~}{qIN4t-#CJ !BkҒ?~!K>XKǩb6qw?o:s;ʞL3ӯ|ӓu(9ٻ9w jCg?8-:dkn1G{+ߋҮVB}80T%m4H?٬=漦J*x)}HJ@>oilspiv:w $Ȑ£8 .Yv~Y D^{>U?ۭxe{.Y/nU(rXx|1Nf=XIY)Bq](WUEIjB{/7m1ԫ˔鿑͚3Y4yEk*`tsɃ5i*?$,Bk,_Bґ~O4ț~r,6:x2(Ѝ NLٰ-.c~1ƗS0`K9x?]M~_ռyة> p+}(;<8h89rX-nt"kT3]0=z/z^x5B,FL`KNB!^I<Ȣe lOhTShNHTS:vg)xzxQ&|'N+ %"yrTU}J hvjǁBl5JbK6*`UX kgG3ƬWB`;w l>BÆD{0G..N/_t_ ZBlf%_xn1}y$d.SF}agn*-UfcX.xnC~~a?YlJ!S摨ܾQQkpU3M[^5]Vde;lK*fEȰFwS8=vfZ{3,NfZ*fyN%ŭ TP9|Aq4%r:e cubo:.Vj|sh1kdq -@j&\ K4o[ 'lyŪz&%[md_D*nEߒW9lV]Z1s8c/ұQ%[E][MlH/W {en5UaJlCEܽ *[`Ej&e'wai/ vsʢt1.oQ1) B!ʾ&͍,mur;ͣltXoGKܒJFM FB.ԧX%< +`\H,}W|WfYRMʔD$3E2ylP  k)@(`RTsI:vp00OWDK,ig`ّC:`nhVJR1.B!D K\.6l\ Igt]Ye:бꀢv52d@;=UVҨc[;+"-nƼћTʼ ;/̦=^Toˣ{|xH8t-O+Ktk".YA˯'+k_3հ#y9 7w'Hc;sRxY% B!庤;lD׬MZ1ԩCdWj+<$L:UM#(e89TL FV;Ee}7 {9e8lZk 4QS.%H&ouFu{c:: a+]O!*}]˂:Rۀ9XHJA!B\ʜDiӧH#9)8՚KzzRJ TkE% Q},<Ȉ8'x39ltWM ,t #,D ؟P#܌E nlh!EKteߓ;bb'.j;%ꂪsCz>]l=w\hMwjs\vA,YwyP.Gwd\nFwZ{dS6B!uI1-E-h4JHHx{dJiKgLVde;| V'GO2r~בh_CwsgHwz3޳VupŠyE ( ٕdٹ|J71B|жll`&<KwVa FT?m9ls7F#B<(omer! IDATe <#ܹ;ΖB{CVY1!Bkk6:J@ʼ \(8+0-#g͛FUxw'NٗaSbV73}8IEU}|{ĝk`YٚAntTذ0|f"y{|[{t_Zz-vgl/ww 9,~}m&֕h+3F+>SUB!ޔ~ ?Q쓗}s#<޴kQץW$IЫ]fl=?/_^2TLXl<%׷.HSŪ@@=]*;$;݁+n4ɶBFA\;ϭp.C!!q67]הzreɅC5ov*&)6U~=NVhN0cv'@ S{- !B^5ϑηVZj5D5|O㇣4uLڊwStx/LFSfbb2U H7xlhs[0u7Gwp5]]d-Xɪ9]`^T.@q"*+1o X1[!hr?Ɋը@@ T!B|$D|>L&`]zVb4д3ݺiOĵCx&qimRR2.)][Al9˯0$H5,mzlۑFNl=B[@ԜUܔv=\^N= ^Tg Ⲣ=&*wjۦYj;od"k,X:6jNMoyĩ*1Yh%8Y40s,>hrczJ$MRԞ jr߼  9NK9Q8|Q5Y,cuMl,!B!eX躎khZ8~?^;r:$7/- Tہ>@Ui|L+VuQf^BuFWGcBn]tQYk?ɱ&/ͼ`IKK9U\Ų̙8/+kU.=4쯼zj;Heztc#!eڏXŜ&.!-V͇Q{3gUTT@賓9u,%N.>-T"g#or.B!DB!ifyxߙ`xn'}oU85Lfjdy ^Xio9W'.$g8454F\HJMTҏxe(=48<Kr40eeAČ`o{#Ly&NNlO;wA(-yi\MB!K|ǃBԜ9s(..`p^UzJagZ.os3,5Kʄf3Y9^{F,$BQɆ+Ɔku4MC4p9 q=~m%N"򂅟q(@痢uĄ$1>@{KL8we:=; AbZ:)qc=+!BqC Iz#BTNN999N 8u5!<>:6j#LZ^bZ+,\H:K]hxs]LhP][өY6-GHo!!9!K]i15v2xߩ_͢FM$%%Lǘa4 gK+@`p^6W8 ~w?՜HŎmձėWsm?{RxpYU>=ٵ~E~0ډK ib2bPUhylN`=!$U4uZKok-%w7S7dtЃclv4rqb0^cK&£c  \fᵝԕogkB"XB!!{&fGdd%{|>& 0;8s8zF$$ [7 wSU`[EtžYz5]'9HFr=u+=xzQBj%.g>sC`NPQYC[G9;tcxHΈ` a;60u exƚz<3cfB!($ f#BD!*7`] ͞F*k`͠ cS ;yo=_q39gփ2%.d Z/vS}`ftTf-"ybSK֎FLj.soG>r7]*ʞKz.F W =s6iZ/5Gg^ʺjQfFt,%B!5}t:;:@QlÃEl64751sP42JD6)&zN(XmKi]6F$MՔvCDV !CV\9V QZvǎq}/᡽}8&6B!Ͳe8q8%%%x<v;O`LJ1-/0)?V[q41l355KRd1+`ֲq)YqnWVF ٵV7C&}w;GF袡eHWĹpsKCBT R̠ugv.k-6#τbDx;. g~Jwr i)!yM!By 3Q$$$PYYɡCfȽt:CTH1Z!?jLyɕ:;rÂՋH35 xiO3u{rfX _?3NC4ל[ł &cs*x&|)ΒiX XqS=nq_CQ A;j皊"vM/ *r(l.ꨄG~ B!6B!BL(!B! QB!B1B!b$D !B!HB!B %B!QsgϾ%!B!> QEǎ]B!x(!B!QHB!B %B! !J!B!&@BB!BL׮w!B!BwMMW[+=}ь$&.p tRc+* \Z2C)B!BMyҴ.U1:tw+%p^~T3)_8 ʵc[^NP+,uC`p`1NB!X$Dilꦮ~K$CՌtH]C=x[S䦩 3nb4:n#fn{qmbB!btS~?>0T63.@7vPGh 楆_uڲC}694t@銼⡵A!B1.S^( 6E޽'nNMBKt!x wQRt98K{DG7S{)8GE@;ͭ] *&Q'4<z<} nIk =B]jͣ`E`'B!Ք@0/Ћ# * NjvTzokIYcsL_Hw$Q=-i9]ehETV*iȶmGkX=+AΟ*|rO`f*y/Ss}|lYL(B!u0e!nAz!JgtA\&0L# 9NK9Q'{9F4xQ,OˊJanfTb &i#8է9fCQ "(_I7Nr%5L3cB!Bޔ>e4 ᆵNF#Ωlr:]]hHbUJQQJL= NrɋF3;Xs(95 ֗se3q( yLOSzS} \fL.|%H.`L,!B!uuM։#>>u`А08± ~H7oDP]i y/lg")5SI?.ztB!BL:D@gg{_Nj%̂fgphRHOϘP{=dK LA%*sGrҟlblFPG4tM#  !B!u0@JJ*1443CQsߧRWW7gd*%li}2pRWZLiM#] z/`jB!B\7Q>p QQQ r {(t;#|+72*vzz: *S ђ4oF;qI:mXLF u-o^(!B!&?6 ׋8odM&ӈV+?p*qX^4+z̃*ٺk l\|:bY/}I뢬 HB!}k!*?P2TbdIivֽ H]t/EuӃʽV( h7It5m]9q`Uzq!v9FLl IDAT:޾Nz=:F(8~%ښiuPL؜$'ŌB!W%!f M&㉌ eS5s`FJ=:`"cܓoF:]h!&w6NF2#K/Gl;Ƞ!Y}A?eؼ66-kX.݋B_?}t r7YAuAnQ9n,W655DJJ g``UsWOCy}yP.6N^Flz"ch,(eݔvru_olf 1O6QzP!Ơ3R-ٷ6ɏ_?]o?OۼI+:M>'Zv: ɟ@ @mm-f`0p9ŋ5kᜪ&'G$Ggs=3Y;[*ѱFFBo^}^:3w[NozKV.{O_aX}{kBʲgvԹ_^^tG:(}/QBI7~n Ӽ#FUMUQuCJLg!!$͸\.f3R\RB^AEEEh8vBUA뤼}^&4TTяh;fzIwx+{%a$C:s:KgE]~$[HUA.dSp" !yZ+o=_ޖg!υN9B1RHB@ #u殂Nx4bT2zʪ ,D2A11O讜Bx zVfōw]P|9$#nNw\& !X4Z6=˳{K =B!ĨB2a2躎㥬рu~( :gwjN/%%'tݵzVC|@R=Ȕ GgP#vVuمxkE5o?O H{oh}MT6gԜ8FI}7>̙ɏ6-HsU#=AGbB:+tK$uɜ{E2ZX"bH˙N~$B\+! Qf@ 躆!^a*)+Nr@uY-Fey)ïbR9‰j:zqL6'Ә9{&!FId`Hł>ǑnC5B5~9V'Ip.~v|b r}㷟bEz ~ծ GRYy/~'F|[}|ה z(z~i:no'Wy~V3[y[ϰފJx7\Lz: '~w|c 5o.5i/'~M/{:]Ʃlf^UQ@!/5lx[C7XaWiڊrfYW9#d2ә !93ѫ߮F1=f#"~TD?i*=x7pH?BZ?Ã4f_'Ư^?:-WT11)s/ޡ:bkdR}#wUlӸ3ιi8nm/佟?<͹׾2~F|(ŊS59}NKn]s;ƾq\ :`[ӯ/\3;Y邞*v"׿g 'suz?WBWXF~ BL(;"D䐓s_W/Pc6R;;,ŴWYᵡ* .sS!r<1SՈi L~Ȁ(oXI nJXmLx҂/3 ' …O#8ϲ2K?LbG?|>@a- ^''z995~tYHϺӱيt WQ|?Qf]8?KMs. (D.}{s6\Q=šhuۢ;="wb~ub>Q$!VƱVo?U_al[(ܱ" YOmm:6rbC|@FT?`l}tQ.],ܮ$wrۢ=TI[ڣtF >U>}pxAR^!-`e,Ǩ !Igph$lx:; Es!O]4 RYk.aWЃ~ 㨟:!7:X,W384N`KimGG%:.ׅBS~ _2~ \y,13?zt/?TzCޜe,J&"̌hDi)g *F3Ƌ:zЏ?Fm`t $?`=4  s`ƭk ܶ5kq*-ʎ6 5n9ww98uu+<_lx:VCSKw?ř| V(.3w&5(ago08gxҋo+ܞ<N^{w!jTJveb֧'ًbr,ׇtnm IsJLu'`opz9n5)ncx@T°x4W)C$k?~ EՉ8wCY;z1=]]lBXHDGGl2N?NII ݎ`'?>h.lLK'~ʏUV%]9YQ& -qgjvoh8fNmLɬX;*p(;Tffz B uΓm6\:_{z_m Ct>ZhE>ŭ_43t5ϖ^Ȑ@JM\fT1*cYPǽëdl+}܇zs{ vkq$ ͍-/L \ʺ/X^p+o|ӍyV1B"]ff& TVVr!l$&&ru8P5RayLVqrȏC|00s6pT\VT U;7ffM_x{cM 7󑕙ChXD}4Unm'IO"L jx+ns#ssǷxfc߾4mD&Sq;]4U@Ii?*i9ӆ'RЇGyM@63l/='ÑJ3ɽP 3T''ӎZGcq)]ZEj: E9}Nεʵsi.+{ztLr{8jr054Hů?cDzoyw5ٺT|(2xQȪѼRNwg7iK,cJaa!=O>$Yv 0\{FI,[wkgeu9^ZEs?8 X~σ95K| kGɂ ~O{3fعs* h}T<ZFq}kȑ%Z7Wę Xb"OඛtŸ}c:+$ĩ@㥌$ƮW^!BK=\:z2n6`qYf߼Uw5~|`D5_B3Qd,ײCGw-PȾC SּOsḌ@{jc=A\:&Wֵ|;?weKÿ#]芑Դ .nw7ͷ_*e'bBuCSYg,7+>g7`>O;kLj<~K%XHIc4wme_}S||[>f~?Va&QSM1a~Sh$v BPRy$'OLcړK/R]%wZ_>JɦP Ju|▄ DOPCl ']Ի 1?_xG6?֮bݏ}?{t>C{~b$廟Ͼc6}Nqx2"l˯~󭬸P0wx.7< 8PP ,xͧ2rIԭؙL[ͬmQ}b#6=1zT{4]vgCq7AJ|tr# =t7VPrEo.%N=Z b!kK)m7 +RGB!BЛYձO9٣aM]ƇoEEgFyܵ7Ll-bnܵsk|Y?n0esS~&yطA,,{fGeL'k~Gx1%B!51e^Mװ 7;vox]A)K5>ьy܇QSK,r\tU\0vZڃ^qמzHGuNg鬨Үy ɲ*hel "B!6'* ""эA8\F{pNeݘqi48Y%xa9 -:[6j“"{f(TΆ%"""""ObbbB$&& HHHubb"EbgRG_z#,ƧWTYn^œAVX ,)XƗʏebzr%0~0 `jnkY>χuDD`=訯x;u<# +Wj}q=8jCͪˢgJ+Ӧ22V1ٵd>kMp0(+},R=pj.+(ɒtN_ʅDJV"ȱrL~O7c0,{ % 9- 8Iq&eْޕ. 8ؿ#aPe-Z@ /}/ \9. #agfK ol"3=p{ɯÙzM#"""""G(DH`I lzFօ\FŠ*}(W>ML͐gB5sy|XU׮Nqѥ|x9r:=$qԌ"""""ǔG->uC o^c&4) PqeUpGHUaD%tDV-]Ā!O#"""nܤ+QaO&*mOcAi.gؠ%"""""bB Q"""m(`)V⃇64dm_Dz/VKl{9) [KXzsͿ`D#g/bә"PunF~IuŏytMg~8m:7c?w^qUQ$W>w z٪uNb{wE5հ*1'ȯ4o<߿-+D۴X:1߾{>G^|dn|~2g=Ÿ\=|o@d <ٞS׷r~sN55}^,˕ΑzwJiQyu6;,iGy:W\u߆ڳ}GGzugIm5'V(ߩO}vc,~Ab·o'Sʶyo># i697î_ǿ̣3;vTpwS,[nDV×x|:FV6!:!<`y)ڷ%A"S3%ŷTz2Ng_^Lv|Λ/<Tx·U7p`rMQ>.y pZ4Xww<|'o߇<|!ǴfkYfly9Vs:߻;ISqbutozzAVFq==3o/GiM?m{<:gm5'2'""bz'Æe^x5/͌?bȄ>2v2pT3WY^Ÿ_==Sj=^zʿGƛ审M\2؟ˆD䎷X<2~qLlU‚g%?kF׌n3{3(=57^ovCkޘΧEL7{gUx=O|]q? aܹZƢEzcpN;,4||EJ` 1՟Έ֗|lQ,Zv#uE=<8ѽcAZc췴[ _b*g_knPs{zՓdN ÀP.k]KO]E43cլ5е,f{GSs* ҏkټG a6^1g}t:ϒ]372a~ V}4gw/=}z\<0C-͜?mx<:Ű}~_i=NlrCDDZII[{,Hd,V9UbX1ebRTL8Y{ir"e* y&W=/.w[zz#jֽ{"gu4Ch.<4&M;/1L϶j13m#1ƒsZDg_xofMax,X:_YMtZZz\ ~u:jN8 7CL{HGWy%-IIѫ9l$2vV7>_ ۲0" T/}1 ;*k+H[z Ο xtTo~jŶmDHdvO77NK%ư(Cc_ibdRgid9aO+[dBd.^yMƕC8 oo!Rp,\p|$vd&7\UbGFipӵG|r6kr ?ŧ/癟^ŇF3vpFɰIҺ3|ږMAI@`;Yj`F1AǍAPK3Nf :.>i kok۴{6]cxԩ'OכkQ[}*khB)nLfz#0o&viض.ˬ. #'ț '^È$s89n1mz-;l!Swv }vr`z޶qD{ k)j=Nt Q""$J˛R, *&ާ# kJFxXU^| $<Ë+<b8!৛OUMI#, ߏVV;\/\3M fʙ)bmwX/dmU=jtԄN.ap]VO̥?ii]vc<7c\cK-ď糖z9ܺkJ N{Q/f_~k}ߨǣCZ}Mf#YODDڍG/:M7ak[Yٍ&0+ؿ^˧rҹU1tKp`a&Qn:I@ʈ#+ ^?e_aŦABRb? NtXrɫW(]uшȠ{gfnUc|dy,Zφ'Fdݒ(*O`~[SS1lw7IӍ4vڴMk W\wƯYܖ!y dn1mz-;֡me/خdo48VO{_9)DhP8fLzc3cf _NwM_d8GV KDЈgfCQc Pkӓ0|@3g"Lg]!9Uʂ7gWe2wr*DFݗ!LQss CQzE}&e9M&tmތQVŚxaj~ ޕ" p8- k~Q fm`Pe -dsp+iSmQqtp/} Y;hshA>՞}%P X%O'giw?Ʌ sI<u|cfSJ.yF }ű}V|1Wbrlcҏy}<0~#bIgB${ҷ٘}˽׎ ɱQ%oAai%tO'g _}/XMAD2aAJcCt|]ҝfn0{Y}619w53} Gmj=F]sz֮J .0o![>wZ F&;')|/Y3b<0q#H3;Xǟf/?N[59<%,}>Z#pTs^y-,ą7\ønlY]cӣ)\!˷dJz-Kyy'cMN=#>'A&>sFjSl%0g̨]|>@RС,IM9cv#,f88G&~ #)u7xtPo{MfO݇,l)'gpn:aȱz!?/̌>յ|jʈˮ_~zE0_6og0-0H|iQ#/A˘<ۙM}CSpNǝL{>|~7c~?XZri7F?oc Gx2C.a*OF|_ y/y Wlo.Ln$PR#lEߧ1V k+|#2>Wc]I#~|V jG2kpm?-\7g>ZVWW[gX XLq> +ɦ9 c}E,B#s~~3w<9ܺcژo%И ?v})vϳT+'.#'ZթoS74u>f±KDkE 2xa_|BHK'A|};grPKkr{- L$1&=PG$ZPt$iS2rs ('[QdCxSSIhQہ$*LהF?)SS!' tI ,@ IljR^S#6sXJ9m9cʾs~/~3ofo}(Y[o4~[Y::־qPDDDDDSA󉈈MN\saѽͿuT#rP$}w"4;"' '"""""bB Q""""""6(Dؠ%"""""bB Q""""""6(Dؠ%"""""bB Q""""""6(Dؠ%"""""bB Q""""""6(Dؠ%"""""bB Q""""""6(Dؠ%"""""bB Q""""""6(Dؠ%"""""bB Q""""""6(Dؠ%"""""bB Q""""""6(Dؠ%"""""bB Q""""""6(Dؠ%"""""bB Q""""""6(Dؠ%"""""bB Q""""""6(Dؠ%"""""bB Q""""""6(Dؠ%"""""bB Q""""""6(Dؠ%"""""bB Q""""""6(Dؠ%"""""bB Q""""""6(Dؠ%"""""bB Q""""""6(Dؠ%"""""bB Q""""""6(Dؠ%"""""bB Q""""""6(Dؠ%"""""bB Q""""""6(Dؠ%"""""bB Q""""""6(Dؠ%"""""bB Q""""""6(D}R IDATؠ%"""""bB Q""""""6(Dؠ%"""""bB Q""""""6(Dؠ%"""""bB Q""""""6(Dؠ%"""""bB Q""""""6(Dؠ%"""""bB Q""""""6(Dؠ%"""""bB Q""""""6(Dؠ%"""""bB Q""""""6(Dؠ%"""""bB Q""""""6(Dؠ%"""""bB Q""""""6(Dؠ%"""""bB Q""""""6(Dؠ%"""""bB Q""""""6(Dؠ%"""""bB Q""""""6(Dؠ%"""""bB Q""""""6(Dؠ%"""""bB Q""""""6(Dؠ%"""""bB Q""""""6(Dؠ%"""""bB Q""""""6(Dؠ%"""""bB Q""""""6(Dؠ%"""""bB Q""""""6(Dؠ%"""""bB Q""""""6(Dؠ%"""""bB Q""""""6(Dؠ%"""""bB Q""""""6(Dؠ%"""""bB Q""""""6(Dؠ%"""""bB Q""""""6(Dؠ%"""""bx ""G=%RZB 7r9t; %"""r,x r0 U9JaJOe@%"""rXeY`*Px%rs8G܉X^R9‚o'"""r(SKDW Tu(,a9#HH`AT\"QQ{,O}93UI* #*&Nb/;'< huyG`/=/nxd>'ѮH}iK|?`MCΤpnDP7&&0 ]9 /bxr +- Ár0NFpP=q|d> Q""'P(+wx<8N|~[ðAq:ǻ\S؉9tehj!hv˸n6o7loX0~4wa{8|,DDNR")!^=p߇tKtd4dޝx+rjlOI}TW˟P= âtLtߓ|]F==z3+:P'T9I,bHV"݋q:YXNFl]qK6zфB&OeCT-Vȏ祡G3}wnf>`<>]?ETlgWp9C%ۉ\a= [e9l-zgȒ9яOBf^:-/yװzG.f8i}8mXofVe&vPizO}Ip9s9d93$Oٽ*$tNjDCޙO %#u kX +1 Q-ykK} ";u#3F*ۙALjO2b%{X~+*ӵLk-)DaxB9X Xf,pǂĚRENiV;\"L$2eV5>8,Grٛ@:7{?ϦҬGdgn~ui_"k _uenx8g/ ;![ƴȧKY~/|k'^L\4;qV1;}Ko@X?ѿ¨×"mLBISr E儅î9,'Ytv %$'RENmh[yeVeNI’J?.yƷy[8:lhlf)tGӴˠ_mgsҩ T{ۆ '`f+ Av4vexj0- r=r/gYL.&uO؅՝?4`&]ogG|Ć_dΤ?pq#GL_Ov(yV-Ȝ̔O`za:ڵ8}aM᝝~ŝKBL 'CF%TU۲>lg%_YY놢v-K0rDRֱoz%ǙBƏOĵ??0KqeW#i^[TjJ* >Vm;5_ & |x*++IM|"ǓՊ^sGuhB_ʦ]E(dnL?)0fhoM+в 2s7 <&sNq5wy2pYF @0$  5"o/̆mݶaaf^Ig^?@c\x>C1 T@nɧ BW,g>^"F~kBBXdxAhz֗G*XϪl EY]rFV` kf|Y 87އ?$d .D`teqٝqX,_%eVj[1_#r$Oņ / `(@0^]Ƙ 63%t%JD$v9"23|2JJ(-)cҹqII>%[@e>VnCmt`͎#,`-5e{oRիqga$䶻/+&5= 3Ts˟e  &^+ U~?~;cXW?gZqcPOj;H|>|lR.̠I8*`.3yxWqEٺl z3?=z{!k68u-Q ?vz|vb[Z{ 3ֆjs+ =3`,/ȹo#EKo:;c2c-[0qҭoO`YSV(@ h%"rKJLࡃTVVǹgMs)--%x("~k0Ͽv>09 ٷsUPs;-WaG w}Ͻ[n bfײywE>;r^j7I ?[ˆ"6,bռdVЮÏ?jx@A,,E#Fõt}sQcpcÚ]}5,lX ${֮topP)%LEB|tq$.p;zy6uwfr9f{f-t$0DZiXJ8PTS{Bõڏ%"rs`ܽN|8{&bդw[WDh7_1}.{(bcU8'oqpÈZf`D$ӵG7Âۨwy:gy{L9##0\.F>+6+2\ `[ `,3@ Ȉ< Fù7mfuCdY tb6duka:33"jxyJ +NKoa/)=l m\ĢGʙ?qVs8uw (IOms)/`x'rk~a&Qb"(//msU$ʈQfW01 *LA~ֽooHO͔Tg5Tg'}"ڍc aH9INp! x lrB-iYĻ"`zh ~_ܰ[~ƵK3΃;b vAk}J"PU[ng Q""p^{a.CD)%H.xMs`\0G1q^Ro(^\Y+x݆R݋ͽC&/vO=7kf s s 6#{\͐qYAxo>>qBUKvJ>_ya.V~gwM]u o>V8#'#ӏ( 80qQVuiWȼ3x~&KW/ uډt!^u3#'ZO}&޼nLDDV-]Ā!Ïw"r, 4 Bj=8%5ExL.BUTZ3$uDdϞ_Nu7e?Ӊ0&m8t%JDDDD -;u}pR3{Xo㤪Ə33ޤ QTTQh|GI4QP{y b J/eʝ²;[l_y`vνΜ={ι[KCpق*PRodYi2my 4~|A?#Q,ݚrei5teѽe&+'SSkyExLmoYh$e`ail;%BцhLnY<)mX F ODSqcI1Yfo|K'J!-5%!D{%K !B!D%BBMZBƦ B!h#a*׵h+iB!mtTu6| H'J!UR4 0v]i䓔vBl6E:QB!LQTU+![6PRTaN,a))*dǖ p'4PžΤB!8QuG*=# A\ۅͦj dDbrjX(5d䘰3(B!NTX4 4Reɳh1h(:B!D{V#*iJIVT݉<؝(!B;nIgJS;YL6D !BڍΓ-%˲p{+0M3&^bItE-KBؓ:JtEQMvjb\A](* EbIte%!DI%Hg fIZ'$4 H%H.H, !hq͞%DW9QBtuBOB4?"X_,2KXH~ӑ:i,I.mQt`R|'o~g{ 1ovDPf4[tOwxznXCPMJsE\ܿ{XeŽmK>'91gSUݎVdl0:ϛ̥\gmf[ouUQd1?:=Vgi6li*'~Ǧ'C&)= jJEE}s;X|W ??A-ʎCc;s4AMČlj _JHeBK!Bkf^a6ȅڏSϝƍמϐfViR{G뷞n=^ETtvh[j|sᲩw^***x<|>t]GUU~h U,eQ^^o~K/վfQ}7XU``)N SNgW=nfr煍_m?"#q8||>c߿덿rU؜bjߢi_y[9_ TU$ײQQyKxIF#*;JBsd 9Q7[ )G],4+Z̓O?ȅ=bq_uǚ>`C5n;^N,oVMm;m2vt:1 j MMӰlfG* i$$$r[^ G$zfT((;cu.zO qFo_}a2>g'{~iyl „_'i0EAZkeދ]|CLL0~ћݝwq{g_U4cť/'z*Ю^U!'W`|=?ޕ<S,3'U6rnjzz l: ZVu?w*W=Ӈ4YlOeƉߚ i{ Ug+z._}o~w89;xvUMA2ҡbu/y+]eԃ40dEm=O vg78=ͤQo|s1 cchԦwP/oe,d)L^)N~+4Id60rײy]V L ѧrx~y;fWϊ w_]"iqΜ$ &5k8#^~4}V<;{~[_~LWuNMAc0g;yB+enO垿>̴trNbQd{{,j*(ڡk>i|pnKJ3FԏϏfiLi /Z~Y_|`_B!%+SҺBQH~ջ vo2ON Ќ 4WM \׾NE!`Z_g*8*mAç1u\^vA;rUC %Z".VDq'K2𦏂}Xii^s|,g`_'3/j' #kU\>p/m_s _p/Ȇ7dM3?r1~֭ K="MdA^9W7|IYo"vhZ'OlaPW Q[˰w5UTc`,4^LpKGnE 60tJ\Ě9s}X6kk>-H窙E=ް~HcSnCpJk-ۙC}13vƟ6gw²*ETQHKMC%bR^V9-̊\0OYֽ,]U.i;.Gޗ!_ɸ3ѽ{qďo-.^#"52>_cME)гn>N 2E! %Ǚ̲RLH7Z z/%:w| !V)Yd]N/%/ S`ϧi n4pPbW֫{f懾(-WOэ ?UfLjh-j 8:(IMIy Jrimٜvh Pٝͫ_O`ft?&2BY811Q=bCs#=Q{wz5˲g%sKZ+us{Ws  6%<4{eqAMzd-/>8A\ǜɄ ]l6oxg6|1JWMۮP 4z<$ti]eWw̕(Qr7@3gmݏh_5 Yg&߾k߯c+:&_-b/q^ZZ(`Tؗ"%θ;yvo /zo.@q<}SL!oǐ$`m@w&nВaN7 f,f`ji6[贊֬3}6Ԭp84ײcVEI~47^0|ߢX⋹89KgZ#uC5ZCBE~7躝s`1֯b|l9K2 J>PkV.S8U w >Ϭ~0coV9*1{aec͊:]݋(o$w(2 K+i0%yJI8< 5","SA u4U!Xy_enPGiWƲ⌫,KhN3Ǘ":#k<ujV{=p`L^:yP3wv ?^_fO޺Ԋ+_U/5#nƨ^IBQ͆vsQܽzrν|ѿx<t]oz#[ˇswќmfRf/-T >lz+R #ӣb[hn}fЏ[7 {9\u;᪾6]rfѫ=\!;={㷥|QLlF&x^~G}ʥwi.0 N J1A(/4ƃ v >Y~/\*=_ˠh>& Zz*Q;/!hsc,0|GOĴк݆B{)(ثYPSHUArc1+Z!YѽEvˣ]ęgNڨ7=y\PRZLYE~?}|%F%+6b޳m5[dCS**$X7IU]noy l8.ªcU1&׬cvVoR 9nTXBjӨ~>]rtuN.#-%|ه#;7(Eآ[hgJ?ee #a*Bˤ`*6,X})YM;({L|=b^VCA3E/.p8((g߁}?SƌB(..‚Wqq%,?\GG_ENdyfeV\?zh:?gCfjl,Ǫ>xx13ی՝pR4 T-ޜ1 >Ɵ<U }zM_Bp\^< m1QNc4"O͠G پ~ ڟ e$`)#MQR˗ {L*J =`3y_ `E3'r'g )V my{(sFvUY Fw_m_B#ui獆nbO U184%^YWq9YCɗ2.^3?y }'XȞKYZwλN&%Z5C w5s.ͫW7I^Ĥ v2Ng_~;+:f9!KȣiFsޢdŧ,.0Q\czNMTN)w)\e7creYε}=wm_J_=-=/+p|Rle0Ҹ%N{d$O;>[+ oqsgq+{~~v"!ݦsl:.]AS#|-\rsfD{0lXg8j9ϭǬŮIgrJO'XZ|?c&Vkߑ 0G7fT,`Pnxl3m<ȮUEu3 λkH%zpǟÍ{|k⑫4^W껬]za\3}2+\+W!nL74|r0bb6.M=m*8dɳ˷gd2\-ӧs@Auу߳eѻlYC7rZkGX [M j Ў a`O85PX;L pVbSW31νAi<)')TTAGc~z7:z3;Pu5^AօTņa*C_8wƅEb$:JF߉j c7f'K~w>͈$"B!DWӬNTWnICQ\U/ O,⠫ǒ q D{%)D ;QaYFu6D+3ME{W%5T, !bO(4ZThi(!9n|H&DVt|KiXBĞQjU}KKQR(*,4+2M|RNHI,*ֱ$=DWiլ;Q*\ زB #tb!:RRTȎ-p%$O3m$DWҒ$=DWm9QՁ H.Œ"BHvI$&Ƽ'$%!DI%h먰VӴTI)i$$<>OԾ*itu>%ٵt, !bO(UĢ NT)$:꠪X߉%5d, !bO(ѕDSGy^[Hp΢vn KkXBĞQEV'I*FٴFX]i!:&DWM !XB^I%D}(j!B!tB!" ҉B!B00'/K&B!@-r'J!B!Py'*IQB!UE9jIENDB`Solaar-1.1.14/docs/screenshots/Solaar-menu.png000066400000000000000000001613471473525213700212250ustar00rootroot00000000000000PNG  IHDR= zTXtRaw profile type exifxڭu8Ê1zN{0w!)S E 𖻀tuUz.k=8x??wkO^x8x:a~ߵ' ?_IWu_׋ڛ}|>SyϟzFvB)ƓBAz/ sO%y|`ȷ}kO? yw{ku#W"Z;| Áʷڞw/Rzd庐#p+,WL{-YqÍFvjrR9\?[q84 o>;^5[ f}ŊyE5PHH5'/R078| 1KU[sW g=!څɄD| h!F3)IB)q3ɘSYlQ ϱ6D"J =Er.ԏF J.b˨ZjVrÒe+VͬYR˭ڬ'0ڭ .4kpgyY6YyU;]'R|ʩN;Kt-^;>F;kɔϬ3"NrFbdܔ :*g2idQn)'rg~e?ʛ+?[9#s1o-[O^]D]+c1c)na3X8_cRU-s:n,ZYt[kt`RvlҚwO)f)233ruM(y#4nܑ\;v$&0`fo /(wV-BЗ"ׂXę)s( ޥHxeusE:1lt:uNaEjN"-3.'Z kSxkJ9 sܹ"-SLvrp+"5nfc\bw>3"Q@LJfMMD\li| c!Lل^* pdshgo̼GۥSW}u2 RQ1#ܗr!b뒞Z@Řaΰix㲔?WY^9"KkziCDAa`ͷ)M(l!ۥhHes@(xc TƘիRl k{Zxi}GȯɗUȞ@6f*FąZa8쌷oVi>>@oӶ˹\ONz''=d.3{c0@Xz ݣبC5'%3䦋: tJT#D10Po.Kǁ`)O4zMAP#۷#K7E`8ė:1֙iZJx4m]]45*@avd\"3jM@!Kh 4h'>gHه(Z D&yKViHd :#kڮ qmCx ZUrHL"*D%6ejʴ\Ӏ{-tj ~L :Qo 2#Ă8[aDҙŢ@;UE,@]Is6wa(|T[Oo3>IE" aCNF|&pKDLIje*CanzP_HXYqREMо0BC 56n>6bF؎|DçAM:/"`%b5 < ADʦDRsJY߄R<p? tD rf4Wdt++΀*KTl-|áNG5~zT%:CDngX9hA#Vgj$jzptY< .~S PY -}3#Av©:ѸHI)(UX%J}?~..2_C<ҭ9BHዲ6"F*zl*-a0L4&R>1ߙG."?E` ơ#Mzd0hQ62b;SۅwYU˱81W-dQҐ6NRew.aq`1]XrcFq|&SD$ɡ" ![x J +em՝]%P`wBP{_N 夳RG3GF+!mVrl!jҀؓ~dSA& ? *5pѺn1 xYOucEɌ9@Vf]:C^AwYRN@[cVs(CTn.RگJgZ8`V+DZ%D7V2W_2#fß.qՆ\n?LF 7 Jp;>πeqB#rw76`ڃi#k cP ,1ջqr@@)hVbp8O"*끿$ 3J&&5mEfF~Y)BGg+9GQʑtcԖ]_KT#@[^扆0I`/H{a)*M䛘fI8۰'mP D>rQ@قxHumK83 DszmQɐ;nec6}K4<6>5-78  #|x\?:"ľWFW0͆6k0;-b|^o)_pJr \s# & `\m SX.9W9+u.O"39ֶ?+|F1ؚU] 6١8wqgܘdcyt+J^Ǖ , ɛW.F$Z̏Z41RhVb{Y )n#ekrHfߙB[I'=w~vs`\ .v<DƉXJ&(HYPĪuk&UVa3:E^Է p`XPU?5pBޓT0P|FsqUAvypBal ^R Wn4=돃4y Y2ʜkQPF`#ΐsY<$^Qi4 P 2 mqq~'=BQ`%"(:Rc6R oPMh!j ]|7@Q久@zEmDT'>_C ^DYSG%Eu뭺fL ~Z#!Q:|uw 97(~w!©k\ڄ" A2#_~Ӳ"5B\Yh+2״1X^~҅k>"aFR=.ݍ ޥEQU6VNet +U2v`mtK'ٮz nqA5 mLem6)a[}I*kӚ4\6#yŌ\D(R-(-ѫ".g3cp/3ۮQT'wt<@ $M$-vJ(g&(t$@:ݐ,G#3gb6Wp٧%vQ-lp1* #dɻ@}$V`!4þSWմ)7f jh롚}r_*hyHPIHA!nK.V8( ^`"=N8ZU1\f1QO]㘭Ce{gEVP"ƨmTZHhR" Ց ̴3:ՈK ȫ=ʐ˳,R?@quO:K+QN5tFBbX[6+ԦLjE '&b_A!=_|T])lF R$-t}5;E%3⻓hh442*[+@L 囃vʳӈA{ݯ7 ñtˈWRV ?4;')icX!*ʖv64-0UZ KɉD -d4,.l'C+$Qa`o|C<,ဈsb̴uq5n15 nuzv1t2Ün!c@E4"9.RaJzL$C;_E sᵋC. /.+XU&@lv:h0%F"f3A] `Xڏ2$GsAd@ -Bgne-rȭT |;@zφh]Ԥh?Ѽ;D%FsbnwǺp:u$A W85|FE\ˍSG! i'butkyrBo;z :DJ [Ɍ?>6tD=OVOMt 047 D\pz %!Klsni{_%ſ>Ax)|JQ9?1os ~N3ϓ-~*eq~I]lO#L4kXVtOZ}E:Bhs?z*jVBmC(59s\Rp{h_J7EZ uHD=6.a/YtczΖUs,b=UcфBN=҄Ra-?Ez{cI )ߞ|ɓy-֑C%ڮvob4L@ktkCjE]i.VOriCCPICC profilex}=H@_SKEZA!C "Z"TB&~A$Qp-8XupqU?@]%ݽ;@hTfndBV+ Q29IJwqܟ#-DYf6mp'tAG+q.,̨IGb+JFWfY]t)((Xz޽{ |A9p d?rhΜEp]NL웞>x`OJT^^-,0V8D@Fd 6 d yj[|TxlDhLbNT&\-A#tE:>/G""" >.N0 HTL'JN%j Wb5+D%%Җ*FqQ4RGh 7__jZ? DvԅPxi(p Q]tE @'n"2 rGyw狸ȰFy__{@k~S~6x|2Zd&$kݔDFZY*xхnS&CA؅#H TjX""E,٢mzޢ,H!" V^PP^B%ìlUCP1HI#8$r>7_{-m@ˊ>\Ꚛ<8|A,<@,tמ:SAԔ(??Q#_K{_6!wWܕů,ZXSjogm_j=g?UHû2@nҫ1HɁ" :+KŖa).{|C!VڕE]Ђ%ݭYBS'Rk-xtTIU q0:1&><6gmyaeюUn[㋞?O` յE]KX "(=˧L5q&4XfWMvl&!N*CYyEUmmSϽۯV|a I u Ŀ| Uuh`JvD)N3WDn !ʕ`4*r4K{(Sf@7 $Bzg!8c"ߑTf??UmÆ8#2]tѡ巐 6:S.A WW4655;mQQ!a&{/\kӦO]ч#;>n!/C W5niᘸ*,QSD>C5:U6ScTjj QGɒƊjPgUF=:uQԔ* ʛ!*GI.Xi((kkBZr8c*lޭС}b -qW:r>0"7+ޢ Vzr!T&WJx/@ΐ0⦧o?~ ;[v_reUUpɪF'1F$88=|39n1Ӈ N啡XO9GQGɲF t띬 [yAM^|EM gD&8]th9Ų@7S#B{UƕuG~/~T[m OZxqTsyr욾3W޿`{=.N8N|Zb-DVuuӏ ޹OˣfCCY >:"g&3;NH5bi{CN37RCKdc Q@VٿkqeM>W+ 0Ta]K]5+vﮞ#%&8~K5bK0K)k>>vkՌ Ed6ub2Rn# j1*~c;M?[],yݯ$Wm0`P"y+ťQlpiۿvG9;``7~ V-Z]a{~~}CC22GmO;ɲS^<˷~ wM `6ne?/[3kܙĒ5¨MeooxB֥JȐ5eB"=fReSҕ>ЪtKP D(躔IBȶ[,}|ﱦ~ŧe`0+JF]RZGR{E_zeӽ)spT]QEhVg>o:C|FJvl)2lE&k=_w'Ԕ9T]Q)9砃@+.MbK_!> r0 v6]WWyfm%!ïjN#6<|_h`kR;k|ƭXF\qe/4u_nW BL\#{[]31֭2gMP^\ .Ⱥ:KWZ:2iti&Ok~%97|{~bJؑ+&qʏ>Se㕕) ]AԿd1R3}Lhx%/ba%o'Oޓ\;/@۶TꌵyoHnVƆZx`0ZΦOr6O?i{z]9#.tȺxY E yr!G˞=;Ǐr9K |¾aƆGn޼#an4X`/ _^7X Ix=R> Rlk2 z%lzۺw R%&q⌁էj*fHm?xaܜXvQJ3[X P+e٬;4=gs{j(Ew3!ޣYRd&|G˰m)T^S&dy{'NG9uG e~%k`udE.4!E+S@x",g@/Gap4n lf5d€wQi)q$wojnj5/׷7u KewM21X~#TX1 dZڏKw>bsg{ʔ NR^ (ۣ<}Rz6IyGj>/S/޻1龙R[K.`NSl5;`fFIGLf#&Ҫ'>*<}Oiឃw&⩃ ]u ]tѡarxs')1-浆aM9$Ighr4UU@؊0MF:XeBbqB`":j#cSSCP0 ԼM5'=i?_Vw>۸ĭK ͨm5.)‘1VB-%p IRM26䷠)_2xtMOo,]#UbT&UWKg%3P!UUB =<2/]'ᢩ>8umcw7&u>g.jF1{&taΤ$I=H<$v-\1Hنꃰ+ReR qe*sB2+*(N2#ˈ2b:mr7EopvX)6JQ\Zc6nQIR/J!ϯ12{& bs:2@ʳZ{|o^kYQ&1@*sgI3e)~E+>ݍ;yZO/.:+(F3,)]LACs9jp_FbRl>ԸĆ? Nsv.aPOJDR=*^B|Q A}T!J) 0زbH@F3ԓ'c C~}gVg5Gi@1OSSE1H*r;8ueY$UŞ3?ēt<!5>fۚ*GO1*L[ZjR\u VQ;rYK7|5. ̛{Y(Ppܔ{ܜŽ8a5{翶nΙ8yxh)ٺ%̸ͦp\=ݿ$g˷|،w٦[m>tx]vpSiCݼ}fFN(C9z,/lep$xbeG aHΜbY(%(@DDdiyIxDĹL%l7_|ܪels+o3_DGg`"+~ԔR0dj/A„i> iQ?Zr"?b_+xckbCT>wϼ>F0`!V+r"-!P 8X+Ì IDAT zDmgGSs'q˒1$[q)L)*N 1b流Ks,7C)AXF҆,f/ zՊU";ܪ4A55 s袋.t`.3,y&WmH4@+d^J_H/?XQut׎#!m÷4sۭy6Q7wL3>ylj gc[E<1ﮋ#8RR˥ T,[뢋.]-(5pa XbH[c}}[, G|i as荃S6 h4TE\_M]w;V sn\" ^CȿlxT%.ҵEJ)Ew!NMM"6WޛlxjEŅQ%1b(htaPJG$gmuC!111!~ȂE]t*hQ7j يf3H`qWBϯ&(!6EwKV1W-/g%!W@1!Tx欱13Z:oMt|E]k{bwu(&9cQKn|!vGUVMe(ˮPKuᙕXd]tE⊷#RK ,p <lr70!o?thOY+y$j|gk *Šb{P^)X?S`~/ZfpGE']t3B~-Dz=. jwZ`(PS?;`EEvd|>UXQ5yEޱ&87!W:]tѡ#a)!DoIo1E_Xڪ 3:G WrOeBԗ51BH$EJ CQMg,h$m"3p#" .:!{x7%H#,Ypl f0fbbe07 F\DPYa1˘aRB(`0#"IrC7ȊIFp"Xk0 xܢܠ3 @]th 領lsۛ#C j1YrBS`]1,GCMp+c 5'KIIx<<F}ŗ@TQkU%Z>Jj)gRB+)>/H HZJJ)#T颋-d2ǎN&YB- H"oknnl+),1pQu>{Fv˸@K=|!"0f!@(}e` ͋2D۫cPs b`̰Do.ҚZჇ >{lYYy/(8g`z~aaa,7WXH됣:Q|BF!ޣF@ƈaX D(JfLdM con|W\SIkeFBa颋-q}AA$Q%IbeYa83~|AFS0Egy#J( *>IWw!|.{mFeW qQ—&U9x7( 1]tѡC$`0rNBՀb?tQDUTPRɒ20|^+`1@֤SmFZŋ]H+QIR+T%jd,`آgGꢋ-jlnX"#lPBF0J = 75z?zkmX͘<+^,MG>NXC KT#T.fd`}otE?kNٽW)Hb{EaY3aSz'X,.mi,>qҕ2ٖqSJiU@K,<߸v&wҵ~xg6,*@TFyQ"ZA"Ie8VEBfY0:葉gEdדs8uU4 dNM1y*N5SStԘViġ²F2G'28-ZGg~9zH~SLp+3c;o"͸÷nHnvOܽ4_ OE10emQAPm1I0^*uS5zK@[r9LANRŸ$MDyGCOL/xGkșOAH07IsppOu-=رW:T'>s,lT˿_xwfՌyyc.:hٺuk^^O?|333'Lp/Ggq\>-T H {}Hrs D@ 2R o\hwQ|B0 #W IRT0֚ҼQ`tMW "U\2H-[D1JGZDK4!pXjꬄ!X,!3'.:<wԀn-TQ[/椫pqRi.>"5 wa/}GŚtn׮]9%gK ԞC 9rd޼yyGr7O~K.=PD֨~\὇<ƚ_]ڔ@DᡎcٹS΃wֿ@2 #卽||4nR [# rrY>cH;i:V=*N^䊛zQэKhxr2-+q/}ĉnlk|fk/>2?G2$;x]:k+{i\vѲwVx NWghnظ>qqq={2LReMF 70iZ܅Rp8{/~ze8n^Lq{nQ~l(G1fy)>i}y.]RSce U75!-9\<@Q9cQJ*8|!R P ) '8L}WUAѓUNbk3%Wd/>vdEKư}C2dA^qES` ާ5|䛢u #ҝ~[%N3$yl~nd K7_p?-H +lӳGʊmC}g-Ot e<wڝX*?r!aCYχZ5\0ɗΛӷ2Nugxt.Z,^8..ni&B8cY611133v+V|=l!.KV8yHFLy>w !34|ރ{5#8D;fP&,B%+B., wYyIqfDa1o>}IdD6\|>{@bRe4\wP)nz+|߽[bCz[%b: ~x aJQ}BZ]|?j| oZ挬e4awMˍ}対ݡj^1 b_lP,Bޞ6=oR[.7#{7"7k]XF@آ,cRqiw@K(tJ5#M?8~jbr K2]qr>Rbʭ۶N6h4v.Exٜv_ɓ&|M現 V sONT+!SX/ Xד]x`~3Ss =G֪z@!g{oЁ' ĞG*SvPR_/wlZҢ7O361P%+j6w#rKv _{@ŏΐ2Y2" `jBoߔ!Ҵ}c`1k' H0ugw/>m~;7>Zx^ f a>+߾Yc6Z"_<ܼ!%O!irAI`+96~ƒvGDtF/~㯿R;w޾k~˸f:P7NvdZ۟N6Wqӳwa;~fD]Z$Izm櫮jeYE~ለ)Sٳgaq압Z,l]uuL=2fuI57@!ޟ]KCb!C99%$ =R #Rxb e[6%]hB0J_.Z]a{y{X(!J?ltWn{jOmq V4R*XS74Da?|TWXȧ7LyX `߻aga2n|~ &L3U{wЯKw:w3/ܚ.0d1Xp0K r R8iڠerv4zoOjm8DLjjZ0ϡPM9x} |RYҌwMp:9(!c0LٹMT8P"ءY 0fEb5J}'EѣFnUE]JNuu9[GιaʩP9WaຝiJG7m:-u䜹0g1sR;k P &H)bYƌ;_wn`!L]6D(0ƌ;_wP""A =kW\m4k8e%۴ +U(4k֥}X*m^Թl( aϛ}V/lpJ@,cMi{uD"ژ8u؝xǟ]Sn`e'vwN-NAP F@v^N.ohԨQǎs8DI_,3<x$IR1Nȑ#?zp>lwo5=uNi#" Z?,FD>lΜ늊<ֳJ\0LO ][L@ǨOG6TԈpz7.RbS#pU7d\ zl߽oXfe =LXU|ز/z䇷Bff=́qbWq 6ptÆܜ7~&wj]&:: Ck jʪV㛵ZUW]GnZxq?v?>LN뮠L}lI癦;?jN|01D# c],YtԨѵ6MR$& bb٪}wɒg-j)Qh,"M Eϑyn0u!ԑ0 f6d2j98Jz!50"n[{x^x^yؤ0NRIՐ3* @Q%(Ss\\ Ț9-u^Q؞?yk7^=oN8QTZ3羺'n9w,?{]Z}N)b[j8h$F:xoXJLCL O("e[r PQ:KY8ӡ/: 8 h24~]6!>zS" n'8,<jSc-%%'uU x~ESO⋧OfF{ <33_w%''uAXS05rd_6pu~~)p5!X5?xZ!0 ޠ.McJ$IR6" o-"ZɅl%WLlU]43ڛ4#uE*ᘈIgL<'޻k' 7h33UDdL!;dnݸa m7ΈڹiSz}M?3w*j5u|tP!֛lnlj}w7,|//ceyu:3qy[  ,ᘾNj`Hc5^}Lԥ+e󦔔ZNǼpIڱc '|2--MFl޼k#HtVk$Ss\͔#*_yevIFHӛ48ZoWh}y  '~]K)酺AEB)niM 04RS3 ZҡFZu}{V᠁ 9g<<;IwP?Yr664IrsNj_ԝݧ6),#)S,ĚGK+b%W>,p-{%2b:0~cF`b߷y+AJn<."׸1tҢKAKcc !á‰(MMM2@ !d6m|Ӊr:MMM]2 o?<7nY(魬 X! vp2dXja:|'eRFUEa1XeYHDEHZ#Kt_:5=g~|'y/ItS/ϲ wo~UR!O,x޹{D??.=(bRRҰM>쓣ϩ#A{u7bq,"؎ E>:%N:>}F߿B=E߬Q3/㸤I [}0a|ҰGQf0zũS/˲ o*ԺAg>8c( G>}oPV:frŬ>mΛ˕{.eFwYt>#>s&&&VUUW]W_L&o(C+VIX}G_z饃j ;}}&k֬q*B}|=Q#]S~id<"ݑ{N>:M (N AE1Ppeޜs=98g떜72(N v-{`nI{Fs|mqΖ-9%nwߐi8aWyc>x{q{LJqέ{J\ʕ nm3G?xSHKHcEQέ{Ίl'rF@b$zaLISrng=-{L8 *ؼvY'w}wOQjԋ^to:Yu͹ƙ7OuTnvOt'OU_"OKIޘ/y翶nΙ#8NU1,{ߴ~A9g͉#UnČ5JKZW.!u5pg~x0=}h/G׌.wVd'>4h=_.ښZrd>C 4(++W_㌵B)]l(ӦM8嗳UtquL&L0n851=ӳgϞ9s&B袋.JOOע!$++G5Lpĉ{f0trF]*Z1R~?gB߼ ʮ(FMNBTHv'FHޒl@˟Zc|Inn٭[7-M4{+ݻW_ ɞV]^yn6ԜM Bs0rba͇> B)PO(U8e0FAZ3E]w~9 T|\ߠ|+??]w|U?kBM $TA* ڕ]*.6쿵,V]t-+(J@HBHHWʊO%/wfysy<$noY@3 0-.+#b }\ijJ  R_{ RB c.htZLVbXkuZ5**V4IZZZ/^gqf 07x#>>mSG bc נ\m„hs=TP!OCf4I"zڇrP^!aqػ!WEUHLnݺBQ֭A]LC<9\u\O]g pʚBOO({0d4/2 >RAV "]{lAGR}UTQZUBBB䟤ݜ".+{>$ $^XT<Dr/^ZI(eYPM<qIN"ϙT޷a\^P5YUThƏ#00꧞zjܯ'O~뭷ΐ_sDޓafBˎ:wcv*ͬC)!%IbT=N%JI]_..2D}KUQ'^յ`Qٳgϟ?S{N+VDDD= pY/$n m)!_E+ղ\+J #χ(rYLt4_y!.B5c,<DY#.ryZ-fjN~{tu.]j;վ{8\!,_\ޠau\bQfRe.^ZJ>Y ?r TB _BC` h"WEUp*~j{I&qj*2Ϟ=;޽r8m]Pa=o]GTs^/A,sF0MAiHh)÷QF"ynN7h&WEU%O~f[t]U؂ p8o^e4xV d0B-ɕB ʚF!\Ldl+S[Eۈv 0T(Xn}KL*ğΕ[Y{/IUQE?x ???FhZ[[,Yظx⬬, zGD\ڴhC ҳ!hqU I\G|@\TTY=K ; A*JX,[ b A2@zP^UTQ'r{p"DzԩS9>v| 'Fq괩eYu‹zע 09"'.F^%trH'م &>1(DGdHt+TUQEj)STUUFEEiZdZK.-((иV lnjra o6HЧbbh w;Xi ;σ9rL(n*;%!!~ذaэ=eٲeEEE g^xEK{,Q4)eLמ1- blP7$īHi~_i!oHF|r)xlf)+:qzaE0"Uz -x&Y,K}fʜ9j~~~%%EBMsLu*,g Ii@r6~ cmIus5՝js!"))Tg;-wcox-KNpyﶗvcoY|*r7HE ga>}oᤳ$=VQPplSG%뇝TwS >,))t|8taVDLuEy'O׷:4%!@݊;oHTRA1zg&4TOX]JH h{֭_>ٳ\3cg]]]^EE[{cZXx )++mljW8O7t3sf~_{s>YX8 Zvgo]^O\mhI"lsOel>~ş5OQ4da~+b|oU%lu#;>FX֕?tZO[}r^f$sɬyi{{q+ڰa7-zvhZ`ΜŧN0ֵ@nnqEջ\@)//̌yGLJJri.;!@~c˲ft͞% GV-;r{|Ӟ57izM|~{ә{PNAb]t@||X2'awly 7DNJ7vR'y]1o#||ЗUO wtmpH1.h=ZlG飒_8xֆO5uDB(ݹң߾ȼ2 U?.~r6Ipq}-Gw[ok^2|%"R~nffRyo^~ζE-Ģʉ' b2/M&[\~~~111삯!##)!1!,$ѣ"1Iz҆Գ~JHBcRGs-[g\a#m:倀¬\i˚=iH?l(5HEkc[OBVr53k}lU3xfs̝ܹJ-8o\홂m+yz?XF]1!ǖ|%i#mT?^=75ly4wib4)f/, KDV LўE߼SEN{'Gڹ TϷhOWmƼCk_<'+6|asb?Mj<7w޺//quW~c_np\^yƍ=x`Sl6k>ODDF͙4i⫯|ylFFDt/zפzRΚEf5;zI#ͷ&#K~qq3EԱAB`TqqF)...jglq/EbA=T_tͽy3[,sw,wlݼa[9YźE aFЌ(N.fK=u>/gs۱a߮߼u^EIM:j*.SEM?x6~FoppD^kOk뷧b6KQQ+q=1aT {i2( w{N{cF?ef6)mkԉRZ 8t7J !j/9Y^+<}}-v }9"aw 8}Sûk˖- 8pdl\J.0Nt&d2=̳#F xlaN{ 1tؤm18f̘U-٥lzZX@>bֈu[;\h. 02=0?}_iyO(>iRJ@[s}/5 la߄+S "m־Wa߄]E_K;YY.z ѯK~au0fXsvmϻ+%]zfM&cZ!pUCÛ]|ͯl9u|CdݫW5z7}Yb~6+™IO\0&mn;@c@:fAL.׫='{:X# 5\91-_{MKq&(ap8Ǹ!(؀FƦz1O5?;Dq豃Ltf=͎Ja#>_f]_ (Vd2TWWy7 9`mNB%HXQL1_We/5 #9D:bX죧tg43&{2VE`]r逡S(Юc+}rmͧߴn&=1?e5߽c䒮ܕ;0mٳɿoo)iqAY뫯v~dQqyjocI7 ?j7fQH\Tw&hܑSmi$h@~~1RS2##Xf^qi an,׊F\+<}Bۿ x-?}?,p zԩӦNmnn>p@Nnn~~g.&&&\u &y-/m?̏vc{gͯ-ddw!cu x̎ X1qjN[5}nyu## w}c>vJNҴgG E\3c}}$ڲiZS ƪ΄ IDAT+ Wsw16znދvM߽doM7dDS&uB>k2)`~{ł}BcGNs jܬ21*_Qӱoϱ!uƌ|bo|WF>3]yzwnih"|]Gb(e 3dh%,,l<lO5pvIn.T˃S-DpIؐR q#ok/m6;#ӓ`LѣqCr9G۟qm(J3q0;vvP7yDl(hsZqM^D'v8qyU[P VJü_G\9}ʜ}Yw7O)6M׼<N!:tv|מZt#ueC7\;pG{ޟ@7V, ;nޫjj?--cop/lmj’w_o3HhD)8|eBQQaaږUEvܒ&IQS^  1p_٫J֐|#sVhv6VVGxyzoQ:M@cp|Gh4]I=]gr\#1ĉE'#5[RH5H }^|$JcG&C3(=O8QROC K QrHp8thO|r*;Y&dwE^}3mx̑q \r+a{R]. #²*\fbn Y'|[x.K"|GaؽtHz0?ř+aHHo9Yy&ǦyG2inhY};(PKSC@=I{ _xwacǍ;8 4yWNJ7,z{ 2ֻ}"wn,ض0SwagLug0a.eB14V !lrUsX;Ntl+uԴǗloqk+ {WϞ^SW#Ӳms)0:N&L/l/~?-Giֱ69]K6|o}Յc°g5ٺJ=cVW==C4l펕X| 2-,G]wU4pd= 9>s9vG^9q;K+|J7Q}$nGgc-;V@،83}2Gcx~cc iGg#rfW DE1Gf1DE1غX, ȝr2K$ ٰJB}hhgΆ]U^nH6}6-#w6wxNM>p5@ڨh.Quevҝ+r|{pM]T/1PE%6MY mmi'8 0Pb; IՏ+[GVr#lggWyz O-+*=O6` 4 .,յ:[zdMY goHE}ߗ˩K/qB8O;Б/&y@()8_b̀ISJ0B0 Ph$H͑çJCǪ;=f"jRv8qF" -ZNvԑSJG]yH=mÞ#1iz7-mX,.;i&4i7Mk`ж6Vz)bmarF-/:,^lI^xoQT$VTi }dT(`K>{hkje/{58%X wX݅\mWYG-x<*Hm_gkr^m/9UK""C~y=ضǍ x P"Uw9b -V!׎wnja7NNj'Qԍ qu,Bx."T(r ("ecyMV?bT:*M悍?(N)#ARL&4>Qݵ%f" C<_cBcIe,~e_grNR'`ҲgӋ_{a=vB'ۿ׊bN9с4s=rްj{L[K׭@p9m\8Hǥn׿, ,λU{<O_H+9~`" ;>T2S8fH`R/}qR!7ҵ,'ϙ5X`/¥zJ 5ҢIyC=8z /\zǏߕsԌa[SydˏQ3Sߟ\G"s@t[*>t֡M~ޔ { O熵Ֆ:덊 3}Rޟ-L)ӓ`sx\K;G-}xA̫9ZNg&&r#M" 8郾.=YZ_3՟\Xo8Ft7ۅwm>;^m([~TG>{rpG?>|t+ң;wpK ;ԱeȌ 87D2W,|>->D^ ^س~{0|ΕN; Gi'iYOSwK1_kOn1az1O&'>'wqcEYG LO,vit{|%)))>>ң`ww:6mi3FQILnpDAmWt@#A4׾"?]/g˚ D^{}0g}קqDKҰ=0ǽ9۾9ʧ`!o!#7eY[sʳvͼ{oɭfޕ{"j3Y z_~v$N|v+*:P|DCgpGF0z3^.R]YiW7 c?c]eUц 1 !/15Vmkcćy]=6^bשׂn1ڑ> 46>5UW6[. ,6.ZL;*Ni!A}&z;k.y?ޜZ}͊";aԩ~~~E34+P0_2.|~SʧlI)Z8:c U?r$)K 9KJegGuqg )55XТ*\*Т4/4q[~GXNA#9}U kO BLLFXd`[ۈRÒwYv*)7o*߆ڊFc럌Ʈ.SW҆j&X..Ε#,J;uW,Y6TA #,g BBga"z.r0q;E}'UQEIE_~-466Μ1oߤ/TֹEHR^` \-=0Mqz^CS Tnk#-g8s:zdFބn_*ި -݉YUU]YY**+ϖIMum~NCBz"'|n>w)D!$ȍA{nR @fKHUTQN"1PMOס %ć2HL_b 5bn,w<HgGs2[C~ C)͝ < %|V 1!9yUUTQ@|9}Ѕ*PSIl.ı 0̩!TJn!3@ !Bȁ?2f*u!'ݪ*Zvdr*k`Yeݎ0Fs||p^3q@]9,j|AE% I҄cP ?"9[4z^@D**Ehijj?s\e쪫ojn Gc1YzuidRzxxOJ}D|;HmEtRˬ bS=~C vQw` HLjɤ*rСKe䮧>h P)A=l0սf FL" l\a'ǥQƩ*`L [Au&rfB*rb-OHecV pA`zEBP RVjjpd$fTk&i"b%8(UYZt~ C'PRWvx9hHùȂz!"Xl. };eu>Y uƔTfHIbőka$$(+L.%!9(Hۭ$lsw(QCUTQ[Lchq8Bf dc+* \Җ_JqVx.`TIR!GM"N >:>d5r YxDAngEƋ^H.{TQEB::+k 6H1#w]0B<+؉kDB$ M@bn$No!IKdqj<)Š'.[Y/~8_hNƖ}_-|i#@"j)≧>1#2fXRt9͕%ٿ8TEq[_wA:=_UTQE¢. #UZV)zYuI#,BZ:;lPA8BNz۴3ݲ`s+bTvF-11=ÈjFͭ\a.,@;$_tAvSO3λ'^Xy|G'~mx:< fՑ?-EE\_XX0LTTCx)F*1!VKuᲘy686ޚ^񅼅$KsƖB)[;3Jܿd7?4X#m)PG)Rh?{l;,#5S@g 6vA0x>AZYekJ:>o001VVwRCxRp=5֕U8TRji8}BFܤ#kanI7V 0c଒/h :m谀!(2a}m \__xdg|%FΚS5= >QW?')!j*~Iznr IDATߠNlgY)jM%Pp1f#pW;:'xeSH9oXMUz/߬D2?&`8+QB 2mBf훫nɽ{p(P`o^򇻪,B@f?xUьt{,}ǼF;BP] ?)AGtodܽzżXvS{}l{Vߟ}3: PժgQܔ{obz;ݱc?rB 5-{=u:%-.: /2j?~z\h"9Ԭhc!:ks?ߐ5+_#zZ-O ru G/8\il`7JVZX1J JN3.t F1![z mBb Va7g10j/ M>|_fGH#0V?nLk}RMOZÛ4M)gH&[{D+ys;q}[vYX}MTte |1 Ji}xvc|0YKԙ6h;+!d̎e9d]?oҦv7wrڔG sI9wּ?@^[07S + YdOm0}o_|^щ{ԸǽM#8 U~'0UT?tlUMMm}K0M(P~pqķBSs76f' lcj:X[ZmibčM~͙VYd`2\\ 3c=77sz~)HV\|s%2ٳR4g)vPߵ m[JHa@ aw)a9vSL]034مO~jפbGKo?\sn[*;Y~ X!Vnº:TB7\;PC={KYd_\oߍ9>iC|WW^J̐~CU\QE Z,k2:;;;;;FgV kmJx5I(73!IKĕsC)='k&Rp$$t6-0CnOh))bǖc6:c# 2c[*t JbP~&"y`0FuE%,EqCRB1ȝE$b`Nwp\PFz-39 6䪯?]_fKcn.://gOSQ;NC:}ApLjJt;u7dM2bd?XJXjXN+H,r8x"x(8e"<Opo B b,.DNRěUF O:Jktq^ K+B (bxzbe̼:kPî훳MÃh#(6@8<*@3mmi%€|Pm,d {7.s]b?} 7ↆ-:~Blsc3`"]LhX(fJ!кnڥ^RshF }h,r@ ~$@p(QB;u֡@9wUsIz0'4aN{ݞrւ }%7gyWa#3D;)Pw~O0,R Fۺӂ6Pt:-P[De8C!FzVE)huHiLm!`CԀQQFM݋ڊ/g6|U&38^i4Ee(uTGV=_#gƅF~"^gKߖXz/MӨȢ*G>"Y-@3,K9!,:R[-G<{KE_Q>u{ cy.)/ t_ZA5ԚH0n<)G##v`9z } :bp&J+;ۋw !8[vϾ/(o--6<" #(5~Bu>2*#`) N@1+n Y%ZN8^X3enzv˴DMf@8*6z0&l`d4p[~(fȍLɪ멩|x :>i:2RUTWa|*tKB'[ JXa u8JXFb;CgHh1575fGo5ryBj@E )Bx KXu `A.Xg;J/EQX^I͹U:fi7FSI]'|]YJkh=3v3D H3p!z8aՎZ?Hp9WEq> %G}^]0[hmin!iN?O}~Y6( ֺßbEa1fb鍫8YK׭@pWE!'GDG`$滑 Y(Ǒ?x@,*\ƚN@:N.j Z4^WLX{W w6ivČIF;mZsd6Lz1rszSV8qBN|n24u:!D2qJ~m&HqhD"9N9Ym%8aU>Ptʹ~Qm5ԏT.?w?3sXGK][TpGLNϕ~]VXӫN3՟K~_xS3'gmxkqרh|`ޘ\[SxS:y'2ЀYKGcũ%u,࠴_ޱ<=#93h9#&&r#vWj k7-y2AC q/ѷ:vRȦ9{};y{ڏ~ gRbrBKK}y[[f0d2jBBCͧM,%ZAg#D: ;OJt5t4S?_А_qƈ%%-y])3sN(l/[)<"GʌPนW9_eLА_Swg͇FBR~à CH՛pBwͿF^CN Qco_ C@6;A@vrOONҢJ%%\')vC'eUKF tյ23-L#vwӫn6,y]:*rpg^կې'8!7>Լo ˷߿"ug^?;?'†s_}꞉ю۷|jGMa[ $Ej,,eqM0$R2A( )hٖ):ɳRj]qٍ +pgm<y$-+=Ph5| h5@-WE\.VJ#yi(@hI1b% A0*9kqʢ$cO$.Re+=P)/_GQ|ȃOYYP4z-ȋ&WEUThdnwXϵAy]D Q|4" \]4G 9P#I.'>a ad{$)%BWwQђ:,+?+)7IA 0 Q6Eo#HD @lin*ިρt9İ)y$Z #,6R87y³,9/q=?͆tZAtNNUThC K@X&05b2Rň! y0y QH*FAr~HxJW r)"qb8 .6O!ɧ- ۨT~= kv1*BKObۋ򋊊jjkTDG OK4HA,CK, (6R*C bC1 >J eK r7s!:Elu !DVB)1=**x#eee6t$++<,,0((44o~~~~¢̜=~] !DT,!J7_XN9`C~(ƘB m̀De%_J2Hb1?I 1`ڔ#,_`TN|ࡤ_UTQ̙3>>66+dl6[Ɔ^GDDӇRQQkKbcc??))-\T"H.Cs1O@,$\]t!8D_#R"e.ϛbTΑ"8J/PSֵK" Bsn07i'FUTQgkߟa2!\[kkX,и_ n<]d!tvFHQw`ӃT(6TzIBDm.*SD0ʊV0 *ZFR>xE,4(EIjZQ$$%PYJ3[`AXi(kj _U.oEqMM 6ϯpor}y]Ut[SLUI2"(b;tO(Ldpj6bmk=j+ 8" c2TR$JMw>q}nUB"_~7nuaJrgϞb|^xW\kC_K,Th8Q!Hs+ȴ $$r)Hؑ?e"e8",*&*w'~:H,B*4^)oiY@hٹk׍7aYVVq")( J*P=FGתl%#/׭[~{{Ǎ7|+c$/D5$H 8DEBb{Ie-vd#!HT)-T "'\݁:s>AP^R˪bz 1b6q7 FZXS-uPe {+BS2-݅l,,fxBu[n%_(X8_xģPS:%1`.x Xuom~ NExSE@}e&M9"%AAQK'1=LpūmIsmy-\za sxAMG' ,]WԈXNn߸iΑZ%G2go/%CNSI !ozw^AJK(ȐIi&:yٴc>ϔIIԓLA*Su_@u䟛~''u -?HGG<]K!D6hz ,lX]Yo.OL/37oZ:]]]Cgw?яU︮44c 8w=췜sy0h. QLtQOq05,w}ZխRi%eN{SB8%h9&Z.jրcƎROǡ2.d IDAT^Rɸ̎ +2G3ia@$7n4%,-G'"e},%E#¤@:)5R量뮚 Y>Ckܼ]N>>8z]Ӵo~[7xた~|y`^~u?)eiqZ?L|t. n5c\1k1',?|v~yW拗Ej'MOQӮ/r5Nһn9S';4H~ wLIS[ OT2HXB B1eE3hٲeB9\`,0S>r6| zb훏͍'\—՗ݎʒk(73*SLLU HJĞO$r{wh%[o`~V򮍛6n>"$Deh㆗6li^޸im{m!% ~b/=uӆFm ",y$jhDݔ=( EIOH`LSDD$*ΠuNxˁeNw]aqYh<5k(RJ } =9sl~lZj3Nx㧍Ӯ~1.Yvi/V9m)i|וfݍWw,' ۘ[ .z{'z=!eYVr>6 A+` PoIRzMɜ.닯sw~`k/K. P͑KG:$e1 MvbX1鷩)Sɢ,>>9vQ/"kAlƍ1ƘK:ѡ'rwn=ot-?C9~]ꏁ)).N پgK;v,L 0c6 ,]zāC !RJ)4uY

[u&D%,$[ˮ[ }Gs|+-3;}Ru2t85Aؘ/&ZwULL˒]CAWY&,^;188is{뺔E脂1b H![2 *s޺I`:Ubؒӕٮ UB{%kuڅƿ7~ooYpd{Eھؤ6FcP[H@WL@[qtc9dZUR 0dΊ_=Gm&Ʋ*Fs" ӟF[ɰW3qF*m Z)ӗcTgU,BÛr1,Eo*>:U_ Rk."=9jZ>xX8F΅F|he/3uc+d1GQ*%x,N$_ҩF/Y4u22]9\jba  4+#?(h%0D Q,S2`MS^M,b&CKVS~ f yٽKK"_R'/j;$L~ef}lHOxy'XwN!Xn!j`+'x7~,ou^0{wY 4 _N]9Rq"i@֪u (1?F̙Q >cJ+_O, Ćbq5J81dpLT3F%eY$TԤY,?4Mgdz"-癦"n6Cd> A_RH!#uVSJeeZy|ʷ#R@Eo\l)?/Ν7YWBRhyPqAE^=vwj]x#٫W:]]Yiwvb644O!H vI݂lvHB-HLH0-` S 㴃q-ioȨ)PySʘyQkӳ⛉ʿW }Ld8*r(S#fȾYd1b .'7pκIBDpcR< =_Ii76[w!5FmB^7T0!iYxE/[*"y㸎1/9zB}#vm.Cl^tU&ru(`tnoCic1SĹ8H*`l`Ч޿۸?wkD4@-xs{uR!ksΥrs>>.qδɄB4;\@XS*n;#@ 3EQDYB*E(I )B:=Q :ƞ@rMRrPqYdk%^Nyއ?A%[FI^֑Nl]mi/75tG^E;s5)b-4JGQJNxQ[W yA 4ޮL@ϾYd񚈋. ^4M[}=|X H^$Gl~FJ)>$bYw;ɥюKYs|k_tlnipg]І|h@U^:&M2A, zq3y{vϯy9]D5&N'4׵pir D2ƬZ\W"ɸ&cI<R%A SyQ$%N$,%YnWlɁ.C"Vs}Sk }`5bPB֚'{tgy͹B=dّo7zXe3],mjdYU.;dʼJUM|"LG_7&PakGA2PDC/xbsDF 2"}2%8$C2M)K O5e6:Ȍ(䮥,UrJ tвDž}m&5k׮=c8: H@(VhZ$msݏ]isZp6bÈ .yzkVhQ__!5!wdk:\Sܿe'}_oLXa=t!jl8TԠTcHĎP`\TMR ѽgRth2)QuT;Wɟ",Ƃ |~>v\{ݬYZ "P}Ód{q#a.h,w%\]B W#aji\}g5GxlJ]BH>eu'/쁇nKeEԊEb&&UcMZDE g@8$ZDWT  RH ÿ*CFs̛e]Ԣ",`7gyvSDG$‰][6rLl#(\r. KN(twwܹ3N?d) GQ5S-}YU|d;h պP_[`)1,AM N]bP'b8A@%B!ȒI i#NM\LMT," /P*:;;'&&%}SB1]!ӺU_ !ݓ}<j=j/MG 4Q߾!%$V&N$Jc1ԧ IeIDxZ!IO[hvbdtWR.RQ,Y˴^O|gœN3s&C럽W_}eY1bLdN̙c޽{/Zj>& ihجQ0W TkџlLa: IM4Q7d2S[bvbʻm8NOODS=1M/*Qk_2S@,\smֿ9sfOWw/US=Mw)*yJԏևF6kA_:E^}=;IUSR&"J/'ȉgbEfU0BE TiR33f0+eفߥ:ŋM&&&Mӌ#xJ@ ^?j|>GLNN 7va:Cީ^N*JiIlFE,?Ty98.\:x SdhҌ-[eH 21Z, BD%VEh4J+fRLW7~nϞ 6̟?'.T\ "GJI)߰q75=#oYLǒB1JAg_N8+"DW?NdTTU⍑bc "]93 \,e{h&9a]J ]dB+˖E׷^{5"JFٶh+d}vO/]0 uhJMTDBX5o޼E ?0J u]}!d\ NWkն&N`%%1`C3(I Tb>qvnPA'D$$+ѤT`d <8}p'$U;KʅkL ^eǙr̻VC|d];cS?X 繞g7zplvlvmw?hZ[|NT*Fh;Բ,˲bkk+|``ॗ^ji)t)sniajniYiwtժ%'(ՁWH2X,B(GeYR<&Ju "1 bM NI0ebjcʱXś/XH9+S52b&|fZ4!h !4i[CKL;:8:8خh3{/5k|>;FGGΛwQGϞ3[t4t0 0t0 ]_z`)}h uvv5 UAS5L/TkXCbR8LA-dɩ=ϑǑ ( fvoQCeR?`BM" Zfu}_ݽ{w+- I)\}Ƕ|s]7wlwh#åRq7P40Z[fYط`^<)et]5Mui1z@-n9;91Q.mVƚ'~&5&# }?SB}}Mp@ B D DJMm)i28(!-j^T 0J A4UռeYnQ—RZD:ezj $h=2J)!PB 8pרΨy!t7麞븦e6}dV.WȷH)+Պ{Bc;B0Ds Q}Nq)ו(" )g{V7n866vGLLL[*k*ZZޑC@266r} B 4MB>RrZ,YTvT :1(c£l:{BHPjubjkQ&89( PJ P| %"uyyvJiZӨTʮIH'E%9g^lo#Ia%Bv=B7L H]nxohPRRks|6l|8IDATs)$r-yS3$B M ;w#c;:c]VjU+Wu lT*\!~R\a1Jj&e B9jCc\ >\.32(RBxM_`.k4{e?0M} "erOR:K5HOI3T̾0 B( H: [Hb}$hksiORF %(\@ ƨn;G)RF덪ieD!xZ s@Zۋ\nȮx.+Uˣ\KFhQm+Z9zógu'F/r1SZE{GixgRuv6PMqtZ|^V˄Fݙ,kLQ^C3LӲz<4䄩Xk[h^5 rBybе\*+*r9's%H"n4* /! 5h:CœP7= vuWstǛHScLC\7ssm(3M].<ϣQB4](= )4@IIA3RHe _li)j5ϩZ F8 I1jaXl-5]qV^|G;FXli@lޒ/+Lk-\#`h\4굜i!A.넀yncttDbKqΜF0Ɖ#GJ) Ӳ9'֭.89+)m߱G{),?/Xo?EWJexa݋/;[[[{FF~Wj}ui&c%i ap)4 ) !@x@J9E"$f69^4(]۩V\FuMl44 Ւe(|Z,ZzBH ne4quE.8=(GY |.Dž,5B<88>!H1u۾<>HBKAt sA"$\'(J)EiR"!솃@f4l{bbL40}_p-JYVO9W>p}Mue\}ay'P>s獟`ޓ,i'+׽m|erŗ~G\\O]w|9NޑSw 4?m/f6D+!9"RJu|Tiz`v5L2 (ck:"}y>Hb&%Lpx^Z}2sQ,kZlC ݠZ 8b>FolM!h4!: Q羇 )AB\!8AB[ A s+vöm[cTcTӂ"uMB:S)%ȘfBzQ%}M9JhPJ$qPB"a4PBP&\)W+>NNJ2dt[0#7z2ւ3/_`sg>{Ao]9>tc?r' ޣ,|/Ǿt_ɏ|;Wc.xĽۺ؍/P֞#;4ҦݻCIENDB`Solaar-1.1.14/docs/screenshots/Solaar-rule-editor.png000066400000000000000000004312601473525213700225060ustar00rootroot00000000000000PNG  IHDRobjsBIT|d IDATxwW}>3sVz/lYmlcc!`C3 @Hw|EzS/]AAAA9C6I~E oAAAA$ԉ-x$ݦWDAAAaz9ܦÞ    L/M-o6/m|[m<4(kpd݊HAAA81X} ضU)'hV 9w7ő!qӨڱ+L/ ׎d7Tڮw>Y40ТP/_k77YKbTmI-   GJ3!&}ZaX Pں 08-S hŘTQzԛ9Qz鍵bޘ`|e[~5 T36KWPmArc Eɉ*-YBiK06TaTetjH   tQ GCxCys,x4S{,o&BL+VQo?n= xs㘈sG=ƼQԞMػ~J+\bE^8e$VĩƎ[CCd7(R*ωGAAA8q(!\Totxnhx.E2r?i'k9J_%4G:kD&G7s8/P1M$(ԞMx``j,nꅦ_aM9&qIegy^RT%ݦ=&AAAAxí=7:@YvU~X>Q}jѨK:DyrR؁;qԫwLy2Mmة4&a2 2(c,Џ/8,PiT4 &8cٔ>ܽwt-dۀզڞg AAA#dBoc5j{X h,1z|fE)o]&3RGge*9 Š ^T0y%R˘ Lbp}ROIFձѭ-d)mFB~)'=J+)AAA(u'͎%Oo$Šdeam=?i95BQ`32:V}ߑ{LT*15ݾ`l+ ݴM0k/k3BDŽ"MfVytNJxƥJwa 5] )mn   tQ4+|xW?R $U3H 2Zk6x:|Q/YɔSCu^?`L$IY 0& IPc$4QOtG=& |e 4OjW/.7~Qt[KGyh?{R oBpjD#NWMu>q[> 9ژL@Ġ8FW--,ȅ/__=_,A〲V$xڭ_.a<6[?}7ZZ@r>NhQd ʔ]8vuڭMۺcs<ަϡ:㨥ib,n&:It/</OR2yUFA l+oٽԯpJGJ0m P'cQW =-)r\F >\`h[U--k,|SS{|X|`sū.LLoM牄:ZyӍU}|S{O{UVǢ%9ԾO/GƶAIB˘q4[_U ƯRs,6d7>Q5bo8h TףL4$ASg\z9_zdTN+W+y/7LYp8no kGyJBɼ#Ȑ{!m|o=bqŢ杫}1)uANDZ.Kx%jSX5Ay○X8,ԫY{')S&UYB&}L9YWe/śF.mY\J{yeq3b7\@jYU8o;)DX e ¤Q,*n~JY طYn}]eFE6c\~57^E)hxoO#zzd?}p?WC[k RJѾp5N~|r{J O;HTy4C3fJFU_~xţ#OgT*pp"h߫T(Y:IJ#cۘsEĈڱT2`1Q3V)͜,,o h_4q~ٮF\Q=6/ALHS~?N"C37Mfގ#_b׷xA{b+IEVlr\׸$z$:E:ρ炓G_T_>ɢ, O*(L*<,:.sdth0m7ZcЕ CO2ǎke)Rhꤳ@8X@6-nS $l9/ z7;755{urz7[yqRs̓NAĞ=sZccAD@>Z3AU\ԺEE鶓|Řkkn+X$AubuVHeFCw֦hzGܨ|!\EەXDgԵ `j8G^\:)ڠ|߷pS)v(Ҹ%Mg:SG H÷#B>%/-*hz~*Is&>i2T=f=W P ΅4L~aXӈ۔ hn}퓴J^yj!m(X^7]WbV6b=xhs|ytCX9oX^:YԖ!gJ"'٤Xr5ڹ?}I6x+/4ƠT\`'/(8{S-\ xe=w(w7ts%E"w+n=x$‚mx*^j6s\(o7s߷S%grڹ!m<᧶廷 _s1kanZIu- @䜋9˛g3^ߏk8 4tMo6Ork;) XVhU,ԦE`g8mfT YGM%OVNUS[*zݱ+^ /tR'kQ?JP l~r)ɪ`\[-TL8-o*~l5}34o?8nL?Y_v&^Rl90{)A<מMAy2andy뛮o.ꢭvKy=8oi||C7mNz*>+qQN;{`~ڝ|w/52=u٬X{:o=/}ϟv +;Xp}?X bVk_Xe٧<}c=hlVq:o]od.X/_9Zr̟཯xǭ%GO5qӻ++qkV3)N? zZTw>탊YKK{7ee+y˲S}|^F㪧Z;޾ '( jrOj T5*3zm߫p6ޱ8 (lW %?RGH 5fu׶VacHD*J ZYE4Y|R^AT.컧ӖRx0-SLLdɼ8`q#6`q\&}[k{m]i[ >x 򿾍`$y3%hJc |۔ $N k6gg#O_ò|]V_<[Y\ ~n]ͯ]҃.}F[ i\}\ CfR>/?WO)kP=gϣt SG%]l e9׾7ed?InOoIMgZ_:>q|YZ]gw˅-g{2&WqK_w_c96[^nưo|p`! Ik .RH0mzr2(% t|~~x>Ցټ{`DK\$gx_My~&nr"sy'~'s^Ţ|y&aǞKmkr o]7pӊU.Mxji֎  J. ]'n6\TlRm8o6oӾZRRi )I$xT.URi-)Z:YacYXv8B&0*)AI=erW+T^v9vr<_m;IuzxUhW)[n"4;\_jY^Js} |jM3y'O Mf*@$',m !'➍ُsP0qA8 HM}N=[?_8oJV6sƥd+ˉ{Oʏ-Wm5iXГUd>$CH%_E<,i||q )+[-3Xnk[رT8uw1su ]f, <\HK6M*LчB( ]x J,W"m|h2M%`bmk&xqp:UԡUJ( Icy:% JkvmJa nm;‡tFYJx 7,^ ׄo7[p>w,ڹX0|X,0#=|( 3gNF]0envS(.c%Xm,P}a'iQ|Vl+]gvUqOѝ!6 MMD$3-/W/Ed=)F8 x }|4d!/ʅ};Fְ-<\=ѫ#d&A8]2Ɵ|og;NP'[i'&ː!Кet *ݶ,-@θ,liaQk up>%?ul͂RU0Z߆5:@-Z4 HF`"YmJQ>O+xŧ 93{(e=w>͝u/>O Gw=ħ~jۗfeTˣL VmH- d4CdW lT? 4OlN#ƞ?gP KU]桟cHxyI0\/'0Sz9؊%sXjfq%t"2di9zaE^83wMV!DOƢ'ki|;;.-T&'RRUv;%Ҕ:lzҩq'3vyLᑪ5磢X2NaXV:lHC,\"mlPayYL1|¶~g du&햰Ё""EG+[;]YvG(r*&[ Ny+t nYSVX*mvtג:ˆ#xS,L׍YZpz $,>ѭgo MUqA8?xn+6=pѪ\j>YݙPnk_ ^~}-1 IDATӼ_־ŢH1j*٣l\+ sTC lekf15MK±-I~'.㱫0%)*ڄE6o~/e 7Xӆ}/΅nrnW-^xwbT ]d9Z-m? #6[ASXJE;(9Dx(RTmRQa,]xz1ux Ojɣ!T+j9D 8_{J[tà8c?FL ?;\?pLXCX-$'HjDS\Fndk0ծ7N:K|'E&e|7'iI:@^@/n÷8x. [gM%wR\~QlR0*icW]H;)\jkq5P yWk㌾pe*eF}韸M L7qA1, t=~~$ *ǽ騋&0m39,_2~̳c:=F/h<{tzim:J\g#|5!:ʝo[F_zwOdqP)K<;dY((xnep4ZrܼؖMg>Mk6K ʗuuP6A,WER=.}^P(;1yTbs1^)Uܩ|JhYq_ܧAжrwg0JϘ\کu^|p`ObeQ&pN/W3Bbt^GO3{wkBl25}ux{7wl@ٝΛ/+jxΕMk5sL8pUmE9,fރC@1&ӡrl3S=Zך 0b\*Nq镼_%4nbRJ՝O05c+,{Z5tê Ug\U[H "mňP[^־Wc.GjXGm?=(J-7R1n .2y<HD_o2-VuE돣0/(m*6LϺDY :~.swv {]@:On*$~?A>E79szG5ڸvCONh~~l jw"z[+5iI  'mÎI%Ɵͻ^?ge::>x{bo8ܳuHl\JшVjp\TP5CQhqxŨʶΝ̣reNxVP_HMxŬT؎.sd7\晃R>eÃɆ4lg~V \.' dN/OW'6մ[5o<<4My_}p(owWfq_u|Vlxb?W^1)V+lCfÕfr/SX+udSdRxTϚW\gן#5$ `u7lFox?o^?nDNnb*?xl5Be/.б՚vT5AaJD.Se* q|r1t0}#&-: oWFJS嗱'R&^*+R=m܊^tEU9 ܲ%{K/jG/@3zѫ $0#| ]v">o憧rcyp|0V.7.:Ic)0߽oNky,s;2|֧/Zr|rQ0>O~u;Ͼw_ycËU#^fiW]o[ï/-P<^6-EY_\W/ivx<4`\.4敳H[ sͫ ȶH)yyݻX=w`<[(>3~}Y0z??Xw ׷+2 wε'~jG- 7׼QS.t[_//{`)zfU79D1Xnb/DeJ/"þ#zH٭\uݵ\Jh'/e\|:*X4nks`w>ε& L:@ƸU:L@ڦpxj;! fltXoxv; b$`Yvq4j+ږJeJ@WqXotU6mTy8Y/돻vj(4`G-nFe; DF^MC+㨚懩\E`UY =fP#*(ôFSm2۔ )G\.5bǾ5?a|=yAY|v+iƧ|7sMϲsSMCvd00oVNFv7}뾹FO6DA|3,NOz:0Cح;7Y{Y͋`~敲fSd6_na-G0ͼ듷?ơ 22A~;waT]=_o6HSdԿ/}Vn~q\zjGnzMSuMO90ך 05OU:=AP9۶]%Tvm;SOV`P)qhez"IqF'#XFWzbQ\6ΟR k1k#ޯ^*e>I1qD2maǟT;>cYx-]( YTCٱ0L h 5*dܡ,.~sJ#xB*PaEkx>zM, Ԡzz:Yޕ+R8<4C+Ya.Vtfۆb~nR9V-fY[ [?S7Pd[[묒8u86.łp}ӉFւ6_"Ool;al'Զ٣.w.]#Vƣ5fS†RBNR|IZmUL 1|sJaYZXSA(V쥔x7S9n"{Y,PG-7\s]0s7qg=ƧHAiQYk#FXBnG P:-&NX\EuZj(9066%   F/@]QU$OHPh(zl7rߪ:DHkCWP\w}2E2͸L՟!m!]#nS   ±$bXn^5&Vѩ꾔g?Pǿ&u>RdMq磖 4WZD!(AVU#6*8QZQRuYly1<ѡ'ך CfA#  `C[$pb (p)>7r 1`mAY*\a1ƠT,aR\rq\r ±g&mAa<" L#o0R*(w/!O5A8~k[AM $ZqÌ>ɵ& kAfoAf\k  ̈۔ 4#o ab+ r] by#0M ;¡Zen%}#'7T9fj9n\(aEKjk[A898uAyPdF=3ea՜_Fxm}Op$h_k@=  +M;x IDAT,T̞Ş uނ7_~N;3A>wΜ˵- ,* DŽc Ηk^ z|/aκky+V81΁ ' )Q?g| 5,mYsż s,`Sg.P̍[mAdAA?*6&6r / ӑU#}|'uw2ූ17KXq R=϶{t-l|ȇ…=ն8_W=%Ss,9rm '." $GYAGm | ,Ohm0P uͫ<΍%%sOe*S/x'Gm*q8u/o`S zpkwן#.J\l~t~\p9AoAN&ʛylscup_bSuk3yz}ϫ]_h1Lm>Mlzz{ xv9K9sā8)򳗰tVf}r8P[jH̦ؾ9w9gmXͼܑJN6m®CNWt5Z?}~GcDg`5lXIwÂ^\V-6%=neDg眽Fē1b[W"ˡ;9Ph9td}_?"͒UgfA1]NLK3 -‹o<'||)=tbٲ5O?^T'g̭+OgV<='c>FMq޺L4 ׶\ۂ " }l NI4Pd.#b xWar9 +C|5 |]^|/.`._cZɻϼ=Wط?g~?; :1ca[WOΤc*yn䀛J}֫ '|/}S|~JQEf%?{^Uq}- ޑ*EA,ꊂ}YvW׮+bwmułi3?B IB <rΙ3w&ޙ9Uu5eO^ھyK 9ϗ';KӮ䑻;1d=6w\M&SoMaU^xlysEys?NTefej 0faplkԢaJUEy^?O|3]˽=83POPi.Ǭm _ߗdٕxXPTqO~P/F>t:mk{)2_.ݛ_`o>4ߝW_i]+>< !:ĽETφ_Fq˓ɌnBY"o2~1oG2*Z&-]SOFreBAvW-8l'Db?sNi|eL[V-g6ػ\˵O0#Gچ c`mb':95bPy瘰:Ht~\;B5şɟÓa+R}[}[DKt+篧yJ'Vj1Kj#H{A~}uɜܮ1)|"Z(3Vܕ&aE:?9Gm>?}j}g;~*&O@؊┳zSg׽Vk2Q1 '`hD]*?-wO9xK>jM/cYJLvu}ʛ.ޕ_gNew~U]r~5m/>88Oy`e)h김~IՏ8apƀӨ}w\~]=֭" k$ p8H(" 0PW2udV^ [V, bt[ټr5N˽KʤYG#on\i2 w}0@iugZaФ9v"yw о-""7""UPMEVRy?e.gҸL'z-ҩIt=4oHB9ɭPa@ppآmrE71`з6_|su%-7bsn]wdXC@1\ŭi^ʁ7AOʦ0 (? ~E5`bX8o@ꅋسUP8ղm?go;[1u}yלKoZuĨ//(4=.!H:~ƴ1K{!)H)"/?m{> !2I6 H#"#,xo&\rwӺu[z>#))R*HMNVPcGwQG#nÖBW]FKW>+,5c{@ؽ)ap{IQoH?EDDK&dѰ_yg َ.eo|q7)lbOMiwb}$(4^, `6';F.Ci/a?Ȓ_ٱI~=v/rt\4ІrƩMka +!XR,_[5'tР{\߿yeXVt:45<9 Q"*_Tk7H-κ > .nE&LӺѬt@.zib=\V羚Ƽp„ ׵5+ g>1>Y僨tI)$xqX~.W&Fߒ_[oSۧ/I_;uܓ<{GūX  '*JXp'y>>6!vMrQ9gG\WdŶ{m{v>S%W.=kغt&_-W!E_F.nA]y`xkv=i - eYЬNdplZ7^;YMBś@>¶mr{~,V]nwel\v%Z7""U{޼1n:bj?oTv|YO%q'Tۄﻋ勌KFU}l,%ƭc;Fu]6,2Ń;U48'1+ $X0W2 ]1V41:/uABO/x*"0Z䇃kψ"/~>yLM@Cޗs_+y ߘ5s:} KWȿʣ_Į̞7YCR}M ,u~UMGfд48'c&ɭpb߫JRٺWQ.V1eڜ)*1K,?lbm,"R}aP h_ʛA kb~f9{-(LJAJN=o;n:\:>}~;6o`#Ԟ :'_Y8ӯپ WmucbuSx7vmuanVnP@:Ժ,)63#{ _ ( zi .0oY.m竟7Ui sn - ko)װsxO`ɟWt6C%>_UmxO[ cǎrobKMS}[}[DMd*IΜ̼ә=_Mڲl=뺽C,q} FehS,/ 6㣰x`.u]p}xr_ߙ7s! g03i{:$:vj -cmv. Wt-`]9aVg<{VCHмY<6al5>4Nt#g&k~9}EܭsJ Y98mUyްll6}w&{=Ry10.ϥU^dǓr@:EicrUW# DRjVZ4adN8l7\-""e(x#"R*02,夳zQO\)qu_0U!~Qn~;,.^J٬@:D[N`786H(:XQX"]/*~[ciܴ>vzU9x` mk`os$?6W>ueQٶ˷ҪSr i_emxo[ }Gx8v鶪-""=oDDPe>ΦJ|}cr [  p[@0…I`7¿ĦYo׹REkZ7N%!}JL'? vNxl$b(V֮g̛֯΢=!u̟6`{T&|E 7 B]Эg۽uL>)9=)ws,n|j ޺k)MvS,`Co㚎LE=o-0>'=•Scm2ή 8rR?J$? ѧeD<7gHz*kY; }r0Z~ KA=[A㥼ב֣;Cǡ(k~Cx/}kZtT$=o,n[]F֖̙ ]z߱rO{ε,p=S^ 2gM\vPۗIԡȰH YRP>⺯lۯ6l:u:M3Yݷ''Տh2O ,2SߕȾ-""Jě0 PPG8qx=QnRSkrٶ-Cu={mذ|.[8Dzݚg^CΦ]l@xobu۟X=Xv u;-G+:4V|;mK<7SN3hث?Ǚv=aJ| &-u|AkbUgzʻW̜cu'@R:؈HuS"o[g,ٲ;WF괒G諭DRK^dTǓY,PvlN>:nmrqDDm!zbj[k_;@QQ!t|>>gAjjmbbby1q~Ӳl(/n c [3(􇱢YP6<,֯FEr&4LGȱpE 폢F`{ZXbc=X RXt͆mw(lؚMҀF㰃> nETMA&6'UhK 1zL"&*>m۳qhuq~m(( '&ȝ]4S녢[؜CaELb* cJ ֬XJ6q\ضNI_{'x((Dz]yw.]nQQ}v]8+18Nx`JOQ~~iWWY,7(;)$ڍS㎋k{Dշ-""{5vk4FDH߶7 7d5kAn={'H(Hj0Vq()jM&ͩ8~ ~\ĸC eR0B r+t9B~,o2*oOd}JX88|;=TDAnGv??ْGVWglܰ-9҂dQ݈Ƶ]eR|=B`֤QӔ]LQ^uY%%&I͚yYObcpc`!e}%~a偝@&Iw (pDԷSo(x#"R*3[$==nmݧB.Tdc8PTygrD* v86Z p{Ľe?ݱI8']bF"vT}\=O/1aL!9&. u+ ,툓U.[oH1oDDyRIdW4rsΙėn9q,5[Xɧq`t8*Z4k:61|{J""r0 ވT A91t˼t ?_aɴ;7^N A}^F($?OԷED 9̴ITC(M?ء۹y~7Z>11;D}M""r` ވTv/0!_!XQF'QC;)TIP_~ˊTX|Gr38c{krQ9vO,deiO"qܱHToH9,¶mb≉O`Œdevw IDATDp8DNV&+,$6!ظxl>l{ިUٷED,y#"Rv*k#} ׯ vD(">>M|wk"GGUmٗ7"". 5O1;SxWr mض²)5#H(9mxI $jopϼ5*K*Rɒh5?*72U_9TB%?H6&r䩏9 ވ!+rdF yw!""""""""jLjL,??%K_昂7""""""""Gٺ¬[1oDDDDDDDD2q$oDDDDDDDD1OG斝 uIM{-eeg!Ɲ@:Ix_VYIrԙTg̛`+a4癃_aμexG[ni݈FMz9ÓfB^ӕ JIb0oFrBF48sSVU*$ҢL6BHvglظ33s'RSkӣ4lיc$u~z 8W,̒i}pq*/^%J~u26`p\]6u)""""""qD7LBFvPPϷQ&tHGJ3;>e1먾>6r҉!eS6g붭\x bb vj6o|#qru7-ڵ&hGDDDDDDD̛3ӷϙft֝33xХG<Ʒųfxm:~Z hur7qJ g޴,^A^8Ԧ޻+MLj΂ĦtlBp4~;NeeRˏ٬ӗl&$Ш}O<*rikYƪ5l7CE̙U'5#)g WgbĆ*.9 6S֑%ygۙUNGvV*fcbЮ]vs2Y` Y&Q #m""""""rJi2929S^|ycv|bmfmrs֭ҥƍ̓O=ͯ~Q/8A΍<2T"lWNncˊ1k\(`x`6.ueĶgξ>b--W[dQ'0U.yt8342ZJR/L,[`!31O/513DVb- fՖIu7&Q KRǁ'd}910TN 1;PPNE|].2Ƙ#E}~~>EEb6~ߏ_"/?Ka?o6\\ڣ%!FOp12Qӳi;2vfj:7xg StI{YDY/Xʫ⚽J<Yi~b^ʃ@ 6QO0b"JIGzML|a>]c'wkcMūS߅8A4kf?gQ~/m\}=¶+""""""Ǽo>C> %4tP.Tm[qyyx<`01M6R;N%~`鎇.~ƸڲT2shЭ7/niѥkq=m/.ӎ?}k{Sgsux޿F45wqqn8=k ? }8gvsWzӛzO|Iy {`_u\ߧ66Z.<'Nۨf9xΫ?o7Mf!iur@| YH""ڮ8`Ȑ! 2䠉,pp DGo{˯朳Vܑq bx t)>G ~|}!{xORY?Ƀɟ_ns*ΖO n$rEYڬT4"DOM{ݔDX\>mAx#k7ATY{DmWDDDDDD]rΐ!CHMM%&&WǼ֭x<^</[n7ޠn:Ԫ\+ˮ˩5mn=Y}NqJ9k3aܴՃZ]-ѽ..`JtoϤ e| nZܑrNIr'^t8+ ;G=I&9SHa92FZ'TY{$mWDDDDDD ~wY"/ m|՗|9+Yf|ԫWHN'Ѧun"|~[&{إFdң &:rߧޟJ]9.Tjs/Hۉu&233x=K<0P@OJJ]4/'|Ge'+;q:d:+T  h'dw sf6Xq$[N ^"QV 0f"Lp ~`U5ZGi#C'#2!mdQE#i""""""r\ 4d…ԨQ$&&+1II5INIZZM4-'(z0նq}ʍ'KsK Rl=kWK]Y`l- fx<ʄ3m8m_noEQ|^JqUt[1:94m9 ΎlJ>y~]ONqW4=+""""""ǃJ/oo bCB_S1_xs.8f;IN]bVFeN% =)fz 3dG>ح3{튈q ڧMḺ¿'ȳMZ^c?e&^gsoYC_j~~jszxJ\e\ M#m~V޵~0um}ktfk_ou;ddbIv[{򴼵MoEٿ۹ v=s3|yMhy4(V8Rca\-13G$3mS̵2R"'(ef2nsw}>$Sg{mWDDDDDD1dJiU"c]G2ntBYɺ\v,qBDUhKMkXy'!q5F$tCXAEz-hٴyF.s Vo#nKN::孜yD*Lަ,ߐI *m[Q7PDr].Z;'u37CDDDDDDDSFDDDDDDDSFDDDDDDDSFDDDDDDDSFDDDDDDDSFDDDDDDDSFDDDDDDDtfߪ""""""""RJo槥UE9DDDDDDDDZ6%"""""""R)x#"""""""R)x#"""""""R)x#"""""""R)x#"""""""Rv|lYq'PN]9"(˺+4,\8RӸ^aX[i3 Oi?M7EoDDDDDDD4|<&NG FZZ&"""""""S\'z4.=*b]G0;\\,"""""""9f ̯5wasItm]Uqk?ǭ #h 8"""""""c&xqw}>|>?>χ">*L4zM%fpn_ \ElLlڵGL˝LV/XC~v/s3o  /Cjtݕ M'pՐ+uyWE9z-›X) ݀!;1kc*uisX ENSɍβ\[*[؉mz*TcLf] srq;Ą*~z6 _bG<~lCMg;O>;q dsp ?+T#+tСCaAe9^sM&uap5KO9|RiO5ӥٜ{t63x JDDDDDDXrDFC aȐ!=o}>}{ӻO/u?;D4lԀx=8N p[MduZv2g{=*3{%A;S&O`'9ȃEDDDDDDhǖOi]{lwf!C7pТy >R嬚=o|3<O9zg,o}>yU6&6M^=Ւ⚻3NC" x݈2_"3sFc"pMiLR|;ٰ%U}3[q⇉M[ na"F͛@9f w,zYRy?=^Yi}j.N70S8S:.'7 `B~|De<ǂk?'+E@Dy:hh;M߭dM3hI,>jw COyx^l"6&L&|4葏q{1vSSvkyos>؛N0ԋ*bIGVxi9?] 9(xS\eWAz7 lG-s,fs޺7[`ٹCkQ|1i,eװ8 h7ccr@ػzruG Nǵ0y|C&mEC]Pm%+ePDQ^ Z6I6"""""""Ecͼ9j\$4l) g6qu[srև3Q9aH5H5H5H5H5H5H5.S| NmvQ;x:Q^?tDDDDDDDDexY(cϿ;vxgtju:鈈ϴlJDDDDDDDSFDDDDDDDSFDDDDDDDSFDDDDDDDSFDDDDDDDs9&mRc,p Y])"1&C~C9:q.m(* ("""r9,qSÆ ; I6 [|G"{Uc pؑ& N-X31&ΡxlfѪ[-^=3¥’G=.4*E ;3dArYԭ&Uܗ Yrxce`XT{ꨖN!ŁUYkQc{CeE]upMhɁ+ѸFgavŏU7VL4!!M.{̨?P UK fM,lZQ  ]o?6qn8,[gK:!Jff4({[1m^!ӷ)r۴l=]Q;GEMf|.Co [+b8DE|2ph(x L&Z?ImxD+WSx8gvO y"\~ |.LDDD$"g~{:A~J  /9G>cFqe5j. cM#/-\چ 7(yEnj07}4Ed*n'ֵ C7 xcD{btlq;f|YX7-l,gJE7j vG2j4&rؑ"#I{^²,6(r`?}KX7yihe 9!l mV6CtcQA'q`[$ufpIn\w6 IDATNgSXqB^,U7_sZXuDY`ZFhΥy|tV-ӽv{8g%d-1\mwf߈m8m}02xce3pEL^N?۶)0qfn#8V/ɐr|hdfٜܭC};p!&œ OOv6K3"rIN& []XI"RSpŊr\M\bISxr.zIS># xjB.Sr*6㣹ʚ\P \$ƞ_cUzdzAƾ-:Nk^P`;&ϔ{,&xqקwvvL(9<>% y%KcޘexQ2ÚO2q|^C |ͽk|,+o ? sڸ8ሯko 87_a6`97AAe>g8A&c8͊ވwHېIӀ|p+kUZ1qf a ry6>u=_܉Px>ߦԬe%M\$MM3Sx?? 9b-~SHuepӯA8L_PDF8jO|˦ڠu \\ȷUu"ǟ^Z,(=3r3Z&&q8ŀ5 me%g%<'fd8Xg<1⮷u'Y^n8piaw|#0xfXQ\7&?{^E>p;sKF$zޢHtk@bmWWQ, # wiv˼? P({f=̜w9e +`8pyobUɥgqfd0^ʢOsHavzv`GXGE>kk` l3[xv_NnM&J%z,R3+^m0ƃO? ! N+TJ)=>G݆<ŞwzUJẳbrn+&B/3]|7܌u3 X˙nD&NF&ylVg =!z8yj\*m?aWGɕ.5i .jkE>J݊v)d,YN}2&\ ??n~ k%iy!/Xz9]Cxsc.phqޗ||plts,WEs{kmwp1 op`}:O*DyL08XYG: q6LqycѩTv2PdB[깸`[&7!&_ %!>"sHDrҹayYLk 7@,Rs©DY>,Ěne冺¹c0]ىv@^)|(*nY!sILa&9/V>AD/3SJ):>!%U=teLY}y86|> 08K"?/IGn3ԉ) @j!xc <{LLX$Uygᢩ d{7eL6&.aYn~^solHhƄ00ހ~Z﫸#iɠԯ?BL\6!@!kWJ)RrR76V,dѶJ2#,[:2f=MÀǯ&3͡Br Iqq9-xu $~Y}bH5ͱ 3{~4CRx1:&&¶R_!>VlLt`_R5y,$ rx{$ rk(Unb(oD^BIf\;c~nf`3! 1lK(;3ˑk2yi12l$6rwd෹lF9? mLL`.ac힢1!qdJёVeYdv`G㌰kW`r [u&ˬ9?+:Eqc#^Ŷ|gÆI& /ޏꭅ,%ٗCiQJ)=WD&1i+P'FOBr <~{27+%$Ƹxxu ɅVY?g{vY,Otr۠(v}vF^Rƶqۈ,~oezs^{'=7Ɓ]|ZUX:= r6tУ]}<9'VxKwNv%hg'6QvRQ7cm8=1uU}5U)<ŴW|Ҳ+A(XX0CM H/qPȩ aU ) $Y9eZ'q1%EA@;2`) ;TҩsK EqM'㧻|n>.{0jիӹ|;BVMK 6qIRJ)SSqՒɫrym n=%Uu/Jn kgrxLΗ32E!d854p1>v{ٝW2Bg1we>Z,W[Ή_z>?n$):,/~l]cr ,,^V7ذ y}~[&0PPtLA1aS Gvz gH.(_4 2+aZG)RJ:m7ݒ)syu nؒW-g꼓9,;%ǧ.-zD|W;rPi0*)yٱ&eK OGC\WշJ)$,/)GV5 +rc.c΢zLzުw>ȯ׸.W+EwU:/a9tiݏ1զ:?z/`Ti\q;vرb;>+v%)IMmG#aNw&`و/ 6*Y!ӛ.Ϋo >s6~|L7ߔxOs;6`7aY~QQBZmӝOz6 -hJ)RN Dbz<S{kqJbnscxYmu.K ʃ./ʓW08mXcef 涿HbXD)ul#B4 og(>eiY6z!>2 IQMX)ww4с aBwmzњZ0u~ͮsS"euBH>p/bJH. +{IɅv.(6/ҏ١\^̻}a&2c\ӤiqY>fNdw )ppv};0,c'esutj]Kih4<;=tp=ty|ÍAqc=RT oVi_|6#KRgnXmwO!+3LRܘɛ_K̰ :F<,߭RJ@hTH1b:,9%;7)JĞ4;r1%=aߺ<8v)e#̿Y>!7n廵n^ӑQ<=3:sFY΄]DN<8;7Ia4nˎ.z6sq}z|,Yf籥lIŐ=|@{$7pЯ~24/\0 + KhqPyL Ӹ5;HnNvG2C<>5/>g;;9e01Ks˭S/~^4 E؈euC6oLa~VMN y`H hڴ3Y|W:0H.I ģg8rImL;[ٕRJ)T^1II7}"=ϝdviib@vp\჏0jȩR/iIpCH3WJ@9MBFy#Ax0bwzmT-rEiaYN9~[&}˴JX):o "exٝ+'tav3"oCw>8ȧ78N:&"8eo%_P: .N>Ujv!dfؕeU3M7X0i?,?A3RJm[r8^#oslyr񿯔c랊F]L2k%k'L^U }ގY$7++OWѽA=s5^6[0J'47!+CV%ixpX9|V7J)R:,>~AX^~GfN(a,f@\B^~;_q1+e:=W)R*`:4aFsm "n$.l^;4rRIr9%\\wa8}JLs3h߻5ؿ*ׅRJ)CGޜ&P'$qU;'u-/sq߬BOvƔR4phcOss}3Tb[d=<5]X)Rzti·/#e.vR^ª9i\Fa5᯻gMv:]J)R4xs!-K·RJb_~Bf?J)R6RJ)RJ)u RJ)RJ))ԴEO%7EYTj︦RJ)RJ)Wwț+WH&jj;J)RJ)Re:mJ)RJ)RRJ)RJSoRJ)RJ)NaQJ)RJ):iF)RJ)Rf?WRJtؓé[/',7:9?I#Ol t3MNᘕRJ)t͉&Y\ʒ%K*g+NŸyÆ4K˶yԏ'&67cICdnՐ%i\nZƒ%KY'fC~SDMp'RJ)Ut {.f2gԴvs.4ixsLn׏YYؼ"i#: UX v (5׆1ӥxlqtpmc:o.N\&?ԋ'J>iXׅS㚡C)RO7sfѵsWOod@jպj^tKݦ=MBN)^Vn ?I{tSqb,YH*f-65ٟC0aav ,C9ZSDZ 5CˇRJ)jo\ANrP> @-pt+~7zInGN@ꀕEfiֶәQ)Xu┹fhPJ)T p9w voiؚi[r"A뵐m%neW9;WdL3`{nFgЪn+J;ldAD)ǚXm?Y&DZvNk:ǼTV24M9Ws"+}Er#pWŝsO*k ȵ)_rS"*Ccc/\}ma5nG=hQ3xS7j{&4h׆F*y5{]hEfTQ:X$2WRJ)U^؛GJ#25R;Q۩)67~]lBɖ}S6lX_;*Yn1OU]cS0cRWs+gM0T2{ ia‘?mΕTrAF4S<$mśb qt^{0kGD 䧡eM?zW*@o(q~U6)/t jwysE^d܃Apzb'Jɦ#{:n(#2]#AAձbbJU_IZ·yr6&݆mϭupDeGŒYh?ɞVAb!rIŏ \3T/2|(RJUWxoJ8!joD, Іu1Jm[2}xsl(u!(.1#;ȵ/|!/%)1 IDAT&˧^)m ^Z.vw@&\!^rۈ۟'w$CEil9\7>1{~ZieGk^%G(_/_ 4JUC[8:I>^N"F=.4r!=Ge+nl,v34K6,?]1TDᐤa?K2u|'g}*zAljK+F>EOS#`+,Ng_]-ǒKa'ޖ/'N'ɐqb3 ,)ه | 6W _͔Kߑ4:ϖ.aU ވ]'KYG@Fsܖ.w-)?SήgC\=^U%w,oC],O?AMfOF޸ /M8PQ<=\ )͇=暾.암^3ZZRJ:7ϟ'1R~?׭_+1Rm|$[ʺukk&ވ*?ݒ$vÔ}ɞqb^(;v/yZ; 1m_ג? 6?_F<*ie7p% +KE2M0 -Voҋv)γխpmE Ôɺ2Os)m^e6{ 19iwJsbUoy7ʈ ޣr/'#X(=npad;;W|qy8ۋ\[$OvW\OyvѺh [R@s2m.-R?`Hxߑl?&`֓~9:?%Soo,6 W= xJ^;/NL. X6UaH`\;ׅc}h +RJՆnEp=hoӺ-Zf8N{&3q5?3I>><4)tR# _-l!_=.{}%\p͂1=ꔙow#Ɂb5vph_z1#J Dc>_VRJ)jI]T{;ug~ɼ=׬]ͪ+{صk3ɥ^^fe {A"PӨ3?YWt|Yp7>}1:-uf,j=L}nz'}_W|B@RSJ):Nj[o9<3,L>W^ŏ?}܏dr,rڟѡsĀWɕ{AHt'E{I? IxAė]{,@XƥtO B۽4 [ = WjI1ٰ#8^vi_6` VFdO2M|<ޯ((]k4oD&|%,3ŬO?;Gc:vtW!ɜ?|-ԣab&=㇛J?i4B 3g>;iw$Tp2ܜ\,ˇU@pyyF yE!]aG0Wa\CF#OࢫX9~|_Jwbow欜2e {]?ŨO!X=wӧW_qq.[_`BA~BdIn69*HA>l ([߹/s%]ǧ?f'yo"|:ֻl&^z]8S*RJX.VFH<νF~â94Xfk#nќm|6QvpQuHv x퉽Ѐx]Ôi; Gs i))d\@q oekiL{^Z_*sm\TJ)R5@7@^^ٰa.W_} ]t=:!c$x3c&O:k%nmc۔Y{MMGotx:v|9u,;Av/7a `žY3X}^K +u2ݻyq`!3~B ']{'YTWjp= -F_dǏSbfpMR/{Y=;>bFL LEo:֘gDbc/it6`C!E̚SF,:ΒEfԍ["YǧFRNFgԐ$lyyǘ^Vc FRJ)ԩE7 fŗcru۫N-))47FpQxߞWSp$7N72r{[/>m)ta.y% mcy xq|@Ѣ `Nٙn=a?7Ϟ}%ͿR'NCn!eYOY|݇}!^HC\q3*n0 #k ;mC//qXqz8|g: ~߇e8hѺi]C}ĘB7R,1OVkAƿ?:0O#hі.+d6qao J N&>p=oj@ ù=\'FpSQ/U:fjpJ)R*]hѼ%O=W7kMc@5KFa{]yأs]<}L ^Ν=\E[h<7~Ȝ'q`מ|vɸg9I[^^GZ[7dg+{"+4gbÎ^ŵFr~ ́.{b09W\71>ka01i]ƌ[udl S`:l-ۓ9k0>}%l{#z .A9V8ˈaH0)y zatמ1 ;g0ة 9{70o<~m#mBO".~'zOx|Fùjޛ|tTeZ b5t[̐LRWM䣏rt ӡrkkƠ g ̽FQ{5&gNE|:o|y0~y dοLh+bDɼ>O3;w'&73t]8gARJSV찗%{HwGNvt b̢WT) wLB/&bKN [~{8 C=,sJ\R&=/t+AF6%C_Uze:G}\в؏3 MQW1 m*>/X$ |wT^eI2n L$DzB_)w73VM.Y#raˈ]nׁWKZ<"F!$+ZΗGZltl)씈xʯ#HFab+y\b o"}oy]~*oUgH8/YWX,O f>߆+'$kl7GF j# N>~%˞]cH5CGm"%?G: CG~]+ʍxd{Ha=ywRׅ]3^WRJ)jVx;e(|yoq-jz;*6GAr $4EhC?غ'OH]nF\EGlqwP,M۴$u̝lڞhܸ.N7iW:0%Y=1GF gw_Nx&sVN#k#4:fyWNʆ-&8vma{sDѨUkW/{ !ikZ )5ʛ}!Ɔ(w~מsm\__^RJ)Nq7xoR/ַΥ㽿wX;iU)RJSZ޾+_E<=\[sny (RJ)' +_s{WRR3y( CywΚRJ)R4xRAցy|+͸wxѵRJ)RJ;= ד ^FĆ\)RJ)4xRE0⛵!dC)RJ)Um(V)RJ)RRJ)RJSX7;vvRJ)RJ)ʪYre츦RJ)RJ)WӦRJ)RJ)NaQJ)RJ):iF)RJ)RRJ)RJSoRJ)RJ)NaR@!!p֋qsO.Ynus(u,M2ÈMCϟ7R5D7'dj|3[= C+ƒ}d'o ~1;i~7k7i)~[)#|IVqވ3ZYt^`iB1؎!+m+gݖoqܝta?o)wShnvՍ+(O59Q;Fq`rʗWR {.f2gԴvs.4ixsLn׿ZYؼ"i#󪔪q>־6.chә80lm6|6QeCq/dm*%F8}o?N7?p9p3qλoxq/<ޡ>=O%ݕsfѡQ%|rםy`{ K>10ÚqoS; ʓRUp=ryUJ)(mJ_̢kԯW|ɀiժu-FNX+=MBj'qztO$lI`RT^[l4k~?G)\1,`Xt98&j =⋕x10#:C^Փ繛^ߞf's:hJ N뎐6<= ћC~|p{a 9{s{q˓RP=* os)p}`#kvd#hvlJT렐s Bp<5 ̝/{;gdBkҞ3tI/7g1f Kn}1{ͬܖi6rClYtqQm[)+ټw,u#KXf#85 4JJ f"lOV+&ڬ1QOz~ ;7\4lM^ɴ d}m #q;zAc)Pt[! Z> 5AJ +^T%[Y.9l_ Ws״ }w茐m%neWYd-f,ڦo;rٹ7EA.}۱AoZvىmс&;IFQ'&4Um3xs9Y#/u ˬ1.\ٷ=oqwoyE Y98J@pV+W_kxgL~Jvp;~,XU^nOtrtUQa^ ͻѷ_'*LXŶury ZR@˫RJ{Ybo)7xwJ}Dmlذ^|uٖE %''[~Mٰa}g)?9$VvL>H_ϭ6GRM)GbF;WReȝ"?<_% NI|]2r蓋%߲g{c)F6ird_@Yc|4BрGwtd܃Ap:b'JɦI V _f92a^QٿŒf ξoEim_j9Uj/q&(Ql3S?^Fc jwysE~,ZB* [b3J&G/ f5P#+ v3VM*go9E4x_fXS̸OK8{-e/ ""'.!FCE?|ޒgLnZkRm92_Z{ם%4ٓI,rs)Q2dTPupmo"w]3甯sI;*uġZ_k>:-V_WRUQ2^NHK}{4!f+d̮wVL\)u!o$} -apv:o|cʎ`o!׼4JFP^(@h % x1ZO-_N&3O񣟓!fYRڝߨ IDAT+nl,v34K6,?]1TDᐤa?ʁ @fy{`by 2mo2{7=}$nf}nI '$1&v7LBknKzxWK)H]qt*w3o|L>E&z\.hC{^l|0M.mo=Z>I#x@\ QɌYSeܛIw rQ)ؒ*)ػN,Y)۳J@ș 30ɐl9٥a0gI:y:bbJUcegySF{hqF*UwݩV:I;b׶=i~ӱPW!ͮ!c^'e{y \uXmQx@k-g|jkTCEU)Tϓ)ϟ֯)۶m>yWeݺ5joDJnIaJȾdyP8 1c/[K!<-ζr/ekɁJcb/w!ie7p% +K߮X3dxs`I[ov)γ2@TC!^\'r"yCzʳ_6pd+)mao#O,>q(xYXMu粲Oճdņ!ޑ@NUꙈ-<&s3*JU|)Q_ueLS%F!$[j720#dT\ē\o0[3yp^7~>8Mn@qԹO};s]bؚ=K~qޏo'rY=S0Sλ۳Cn`6;թCIڱr{P1 sUcՒu._9\lEd - ^_k>#XX 05[^RtV2^s-hA-^mZUL:Ird&|;saqfr2Ӈ&eNjp+gͰ_*܄ۢɗ1 $,GJ} BP'psl,WsA]# <ω>*cHhF&'+:۵)qtNOC:X?W.dž)Q wlUU ;,wEFf9{۳%ע?ϰ6(* +cY^OzhQGDbhiCq`,^ 1,6l܀.]Yp:dJvd~jt>/o#[VΜ ZD:@7v p6' fEAF S}?w}m𶱷E\-Sq*l>]x 5wju]ͧ{O2,P#$*h ՞ 8/$"I @ͬ/tL&wR&/w!4*h/Ź!62ӧ T4I@FS{?5V|FξX>o3f| 8p.b x`0ZZjd L%Qg]{5?8C9YqpJL).?OuD5x쭐vť![xV2 = k1gn[JDDXwKdM#42?ހNù,Xt:upMSR6/@Am.~7õ}?wԨYSBrJDD0`媕\/hZ$$ƌ3ѣ۳_~IW-orkGoYPc(]Ū/bz|4Q LH8PQ)ꊨZQ 3'ڔl'A _Ĕs$27'r0%u[~E̡?jrm=[x,#>ԕ2cƄ3'䱯#t"PX2ega?Fj9/DWϜqfo°pt{q+~.N\\ ۯn/_|;N P~5-qwof]Nn∝uvxOIwL}҇;.[ qӞy:۩"{[)GwD]BA~k%"{kׯØ6[<\O4hаAoc̓1'x U)8u8 WDՋ .Ĭ$ռ:40u8l¾"餁Srɗ4 `I\g\ڴ=3[Ha"kC \UɌ8jmgoA2fr 7oY`eØ0—+ { GWQPWF^P#w"9Ws`eH(UPTÀ`K,ګ ?6}#HѠڋ30i, W4LnK/8 ,lkOfp)8NO"} +m\s4YRg;]ⵔw4uݯ\O{V&戇;8p|6FӾ ulKEki;ϰot|W_g$4I 3 R^(@'/?/_y;jED̉Z-+O6J)ԗvQ"jEIvS>1K"Pr):d⣂(*/q块:KjQrm&I>KaU@TeH'|:N<.\WP 4ҳ_jE*Y^z5y?3?'l|3LRQE.LBL$F'CD(T5Y{-N&&?.TE+z#͐S>מODVSReCƻ7S(j?i22]*@q&l f{ˉ־(*1hvvi U \e1D+~Q~sωK@DҏԶeET^" xY!KɈmY A)św{ , +߾[w]Ir|U$.)Aj\OHoY P 2m嫣,[uk5Ep)T Idk*7ؤ=U\G֊r {OH~ͼ|k{%oD/_"jI~v\rk%t,(nʗmDE<}#VsOxR%?.boeS8u*RLj B >\:n$C|uR]|;G |Bu?ln_A>>zG "ګȈ 7o2.7ܚ.TA`k{q-!/݀^qAR vlfOڮD"㵴g7EW"Gp7_ Q>$2>^O\sK7?eJDx%"ɛR&Qic]A.9$wbՇsqάB֫jID㕈8(ڈ71"<1|9{;wE0oߗSP|q㟃o$*QW""b򆈈(??؂?kSboK@Gcе:gw *qW""b򆈈A4Ah7jڍ.k8y#b9[:Qx%"!&o L j ,zу1^߃bLb6'oꇇE= 7bcDDDDDDDDdo""""""""*Ř!"""""""*Ř!"""""""*Ř!"""""""*Ř!"""""""*4%]* 5\ x ڒ &o Jńx$j|Qְ5!#DD2{CDDDDDDH`ҷ⣶OǫSTp_{@D9ޙFDDDDDDDcfjx Qs}Ү=+&k0k|f&"""""""<4Yfc}6y:8`kAW~¢Pٵ~cExh`T"""""""C3g.Fyf zzzzzҰp"H<3*v>A!pkXyifzWO k%gq5 aa̵@̷KHm |+AӖIT~m*өS"̓(Bu*cNęD3 Ǜ,>=:GbD~Xa Ya:n޲ TvD3>TJ&y3FYnM ;<=0k"7xX="z;x-\uϗ`'q>}36碫 bI,\ .,в{F^r>gluΛu0m \PGY՜k'1Hӆe1M{s4?~ŽO`^ߕ[&r%.WΙmZelCXPT A;:-,3ܹSØ՘:=zf75͝jF;#1u(+}j$^l‚Q)cț3gY}~p4M^ <7Pj58hM,9oX9Z+zbE2fQtAhyX0,E6Ɯ ZDYF]QMޛ};J:EDDDDDD+y3t`6W #sOӧgvէ"s Z_ 7`QGajytRӱ1"=Y~F|;l݁$6r4my%A˸eP\jԏDje|gbqvkH ܃=WpnPBrxTF>.a߆9q& n-{ݛ=n߰o U(Tv;ғ`=8z*WP "QGkY=}pɹN?0X`An=y՞9 n][Zi@aK[n[ó6=cFǚUDEs"g$4I 3 \q%yXMryY,ٖ 6%HJZLy|2oW$]bF &"+yBw T) g?*ɖe,7d}$[ Tl;J1f.?ͽv1#D@t-'ɐܲŊ v-3RIiW/F`g%GA i"g-`k?u'@>s3:,Emﱡ=O\ }N:h3YMFNq?IZ@шo䛲/1k ϏztnQ(nl{>]PSrkt{<1z )!z|ݱ{QrF>C @z 3?BF83:{E`ިg`l5>iVͽ8ƚ~τ| N}=>:VcgcŚX6]7YKf0&/MQrj' I1[5kH%}3DDDDD6yFL-V]~@ne2c2smڛ(""SW+ˍKYjVE&ol.ȕxt̙_]DV}rXr.#uW&26S]s9g)Ga(Z*]"r%@QDTWg[+gu* n=RU Q-7s$D Q42fv?yv_#^R Jjtdr~Ⱦ6~YoP2Fy\rs;RGT2~"""""[<#onGt=t3 &tlAo4똻8v Dz-%P[#vΝ[ӺLg>5/BR6a6]O@V=_K>'wGD6u( zZ("4sĨ{(b<#1`E8cVU3W\{?>k!x,]?ldW`gcQUF")nM[¶Z^BhiqkK2֯QQ+ѫ3gz녣IDD",Ű.u1wINKFT#2ʹ1mY,^z̜ ZD9O4꽉طsT*Tf1cDV}Vy3-׺ht3# Gax@Hgޓc>{a!Q>WWE_\}&wp{{87DNzCRV6r]NvIG!I 3Q*!u{Þ 1<úc~ۀ;tLy)>  +iBQ/?^X."W]>zŐ q>c7wa2Ұ%o;uM*D{Õ wOܣZTS1_KfZcN)5X._ gwՎ޾GpxI1G凎GQܼॵՙ\6s@[+e+R_pɸn'""""H6 "X P[z f%[F}LE1&b2@[ TV U W޺81[8sf{ =b&: =*#CɝnFsrrS&.>I7X@?\%s0{sb %6+|[+.pΣN}у<Q=/qmTu˖SRPТmX; ;4h_̈/$n]uW˱w " =`HG.GTrW*wR!9 B(%g1ÒW޾G~ATJ ;j,ġ6s@[> iy>͐Y=7d$""""^cz#U7j4P`B©BMZnxkү9'^ Bw D(JZQ 3'H83*VμG9ZEL0YI( 3??QʡnѣGQ9doճHc-{cƥaM`)""""*blm ib|l?T@'n@CVaӫW"L, _av7/Ü"j˵Dv塂W.^TDEE-Bg7Qk.DѡaHgX.#)WՂK`{۬m}wc֔DZ3,PF:9#25Np.9nrQ#ST1伄H;<CU\ZW;vǀ/ݷs'bi%X-תzoOEF` 8hhP`D*H?0 91i80u,$*$tVE$7v6X4G{K$,7{5 i$|06 <dDDDDDHyτ@Ui _Z-DhDwNi8`'P},A1vwxcXLWWe-HxۖGӡ*FwZj?{ 3v'/s ruaK05K8~!f<ۚxvdD>uسhbw̞-p3rFJNy}>sX26ܽ՞dmtwZ$X8VQ""""*>'ϠI3hķ3fhZ-]bF &29ҳ8)x PTV /54.eڣFF!,D{ ڞCdM|dજ f/- DSclO$t /r$-,lDDDDDȚQ|N(fx`gY:dpFZϣÂ;qLXP|5T\N1f):)&5ZHxi.ay 7TG: x;D5jc8vN>Z T =Eɞ6>!#D48r4ePf-{f)""""*Z>| {?4Tp 5m(Wp-.x W֭`sZ"*RF 04!3d)J$s 'jS֍9UbLbLb󆈈( ]OmHXt~ 7DDDY(^P' o""""""""*Ř!"""""""*lNwȆ"""""""37bcaGߌMbLbLbLbLb.\Mh<E$n2BQ:ثڅH5<|+ɛ&p@<ӥeTT|]#rBƐzh""0A{qY/W_vy.:;XuK7*Z;Uň-G0)A p50 a./g7E7/+0 j-""" 7ΟOh42 UT-oGmƏW-/k'6 ,qU+›*zf JA5fr ^j_?a];XV3< b׻PsO}gG7*<GM1mn/]DmsF塿`6ێ ww X;(Ps!k_וbG!W!"""0y tpw@~0{OP  "+{.ݻaF Qɰ',pjw͔̋t[U@S8m6}=cF H1*z?ؖ< ل?VNOuB`bLxކˢjxUxURr..Ԫ gL g%$iT10mٍZUxrNazo@\V>(5 aat<c^#DRp칪s@-~gol%7NakGeԭ aFl=fWVmVm 0IzlߺG]Erj!Y$jd1̺ݸ<I",H9w{\JgiFCMpp} @8>u#GXPMs!AʢG:XwX[-ؾQmw]gd[$:KzjFM=d2UR.{PmXWarBѲ׽GߍMWp]jyE6okM[l}nݍ7xVBЦ'ֿs2ҴިXsw# (>&ϠIRΘYk=r9} II-|;U= V T8Ily@UNuF9޷\])*4p@ _KJ@pOg;)Y@|˷dicȵ: zݘ+!ԑ^P6G ZB.noyV _ȉ 6U{N: ˢ%9G8I7IޖωQW);eRw *~gG\OLf:E-M/XXnH&v,?c]Ύ-LO,//#+jn0L]$=fF#ҫu-dҚONuWdcZX}T+?ώk6INmwDw-3_--ʊ *)kcӦر@[O>: ?H<断 v-3R9qX]S+T,~+G+wga7qD9A$}kgoE#oւkɢX8LވX~RA QuYT-7d+!QTRWr4뵘l. DUWz/kw=V=$](5~d; \߇NQDL^,],i hTVT"k{",g7ϕ{KM s 9{e'*⧂hȫm}%`dͺղdhyN(dɺ[.ʒ+FQDU0aݱGX' ' **WHAb w, fˬGU ̘9SfQ& uZOc#E(k2A"WEK גc3?Vqzfgy/Y(y#&o 2Kw[7"eϞXI-%GgMDQKݜ.⩨ZK8ؼd۾M~*%myk⧂{ʯ׭U4]y1@% +g5(<" Dqi&MyA4Ȋ5e٬wJ{\]\V~Z[[rMhG.^IVȚäF(9P.ȪG:y}6I}6>rHj>}^端Ç9Nވ%Y\E4JK+ejQ<)3Oe?{7yOhQta9k+H%5D(3귏EH4rc(NdQ1e_gJkũ?[0}1VQw/p__S Jjtdr}6RU Q-7s5DфʘɛLc2V'i7mw˘P@q_5JOLGZQWi;l.󻺉4 y61c-rua)@2-G_(8F^ R oyMDcdt-@qvYZ*]rxQr%@QDTWg_3ʺu7^es-_[<@-?9xGh_R6!"G[|#w7nxٽPF-^ $K.rpMұh|Z?og/Ój\-{2,;1qgd߈sp:a$ƴ.sOċMX<,w-'L{W0Nx7Ⱇ>PtPOEh)0ܚFcg0W غpΘ8z119 IDATo6̵z?>k!x,]>h 1gm>1G0 t0Ƶ(tpGΕ L)_? [؀ c[xqSr*x7ƒ ?ԯ% ż`%ؖл{E.^9bW,7Ah~ƍqu( ]#UO EFCz7B- 8{_V4u@cK1-[Bc-`IU1Ex~DDDxY^GzhQGDbhiCq`,^ 1,6l܀.]Yp:dN*2ɬױ${-|_ƙ=q!fN]aa-Pڙ:@7v p6' fEAF \Q6ֿEŹ!62y Դ̫<)ޓ *GB5Ó jT3m>0QqwW3,Y*<=} qU7c5./LjPDΑ,q@w8 ׯ w+hb?ѿ/ی?B2b3/u{A-']{) J-9T6aЫiĮEKqʬB'q}ܵ.<%s3# G_Y&,zoEX\KMq@(T_mt -""*M 2ط42 M#c9^ t:KL L箨X] m؋z<' \,G9*ȹ"/Xs?zՐ qY."b#}| |Qܸ2**>F u~vR|/hQ>8V5X._  쁁ź_p6|:"t!]{u&)o*?t|wkS[q99o3.>xwGo3g^{1oALNMѯw(+PN|Srmmqz,Zs=f ;U~ܯ#ﶋ#qwOX{@RPa)g$_VDu@)e+BGzTM` x""z6SXlIf Ν9s.Sщ .ށ FpA(5k*PYꕵa0f<ALzժj9]ɰa0djzgD(6Fʭ(qn4f[<S2^6\%MTƀYmK`,+ + וֹî!᯹;W`ָ#**6ob߿rDneV PkQMҲ?zh`:4sb% \ſY@%֖:UZEp-u_[ ѯm1]iqkq[Q)>89:]FIƢ㉈R#oJ#Gc_1ztN8{4~G<ۣ'+InnMIB $9 Cl僪dIqwU]{KzB i@QDE:]AAHGi{{Kz(&C̖{̬X&V-En`',4 ;'gaOԖ_paz awCyh|65A22.ɳ<7{GmV*uBs5X?Ń|]XQ#DsU\kdl~]”p񏌈D@~B-5ҹ?&ZX7e6g9y6|z^;kw̲ci\{bWf鵎t``P}7(}GedԞtoAb#"#=CxRXt1^j%0 Ə6VZV-qr1;>ct0Dh`K?5d13`#dADL_.'fC5,S[~n%T@"k gcXNoNp,c7w3kql~7A7/_׶N7t?~h W"W1vƱ;mFf_S 3&*E d|f|;uNyl?^|r˼N~n:Yq,0hG:.9ˮ#sa0v/oDOvNe,=V˵s|!E!SJ)ҁop߸ѣeh^z=?^=E;R+iN'>`L8\͇Fr(:F~g-ع})OɛOWr_6&9-x>+~qpb%cr Sns!Np;zȍq{93^xpr$؃gzt v$sf[)~rn L;j7`y(e$Þ븛~ }pO7/p4b) ^YQe?M+Q9 W3U sLT沯WKh<`5߬EE/Ҳfa̗db^6=QcmGCٷ%rKkjv!4p+ fťt><7|3Ξf\ i^Ke E4Q{u^z9b9j!Rv#G':!%Km)8g7?l 4Z<.vBi,;SԱ6!Ojh]ȀluA)4{vF1b6(iѕf[P(*orĥIMh?sjAy?s6?͈т/uleyww&zk\93FШv~ ![AJyVJ)Nz|+^=WǍOzGNbtU3I+%Jl87!d% Bd《d\ql/s X<8qoĽh: qTP9mBLz(G+~LKz,MB9PZ[o )DgD?@@eJjsL>ipcQp[d`YYJ^"SwHDbdR=K^#ecpۯla~^S~79ps|kL啕6Ls ;p#e[%l8Ka健wϧSrʖ`aTx>cyOȢRt{8wr- &kP?Y)rFΒv.Jބ;3[RɗmnE(b)l>V 9qR͙r,"@)+n`ye»I{ƌ@޽R$ʨ԰|W;`)σsObxo㚟RK/wd`#1N%+-zq"&x'?&Im 1 <Ɖ72{PD9Jp{դ@*o Ɏ|^5Ou4/\#4֌{”*]֤env "*Qz k'r>S/521ɼdH*=OW'Nxl:-OZ ) &’SSK)J|=q/^׳q&Tys4CRLg\$J)RJhF),U?`\qO1\uRJCشg vnRJe (ԿRr(ԿӇJU|J)cQRJ)RJ)T6RJ)RJl,jUKRJ)RJoۗ.vRJ)RJ)tؔRJ)RJ)T6RJ)RJlL7J)RJ)R٘oRJ)RJ)1 (RJ)RJec.R*:#Oȉ53s"~D.|x3#XB._V{BvɌN)ݰpcE^h=aq(n"O33U>O+'4S&o?@?݀Jl)MPJ)RYLouRZu=Ӝ˗pь00eYړX7fӦ#C ĹtnuԐ2ނ;1|†a24:NxרwpӪt– g'1069588`RJ)TaSw+W&h".ju!T l& qpe_ݞp`sq-;ٵe%3vkHWr%͓7W7?4? Wjf5yohNpo:zHV$t-_ gl'ck#gm{&"I]t#6=ck=K&wt_o ܟxOX~UKw:Kb*NOR!j.ql~b^ϟeմjB]44 0gLa{;Aiv켊3k9cG/` IDAT.5ay=.5ɟڙ7MzҀ=m:Kuj=`mknr{NxDQQOr47#m= RH!A )R,yʨsT8՘!bmL}?e?mϝd](V &NphX^)RJeg7˒|kզ_m8oĹL1Ͽ@*gPrдk[LS9gT1: εұf q!X)X;.T3_ Ƨ^;ۯ1ExQlE'v@8c+Lvyԕ+/ ?N ;`!wy}Znç?$ҥ_>5=3*yQ+0@>L~*P0;$)='9F%̘^qC7٠#އasߍmC2cv['|"Z7 r%C*guj G# ,%z0zd[3$hf"w\bo Dp&:RJ)2nTbysn9w.ɓ'sT)7wJbMc;Fn'l5K%v0pvv0.ӝEt'-6r6+z7Bml]obޕfO"22xdR.&*J՚1Mc_&nA"7dzs> W\D=$::u+uҡ 0\ߑl=f;f|OYçim'bOOQ1TSB%c} G:T͓Qg;CV%*,9J)RJi#G3kwNNΜ={_'FmRjZ.+a PLY{N0 7 @ qwWmY䣺G[.w{5򛸾g2o6Ą3w#;D6qIN>Ȭ^$ueo;aaɘ#'Ҙ!V ANcq7$޸GFD`OJl=DfMx1РAirlf.%Yy ]zȱ[k_1E [=N,vgPUDpx<6 ]bcupdzɈXTnҙd%^\_PYSǂA,6a=bwF7nADLӘho/g?EN纕,nRIBrgNso+ #|̎O2=:]c>9.-Kvnݸs\xjF)RJe \qG˘ѼR{zT%==h9sFa5m_IRs TXqv*:ƪÉu;W.qȄ൫8p{AX?  +#o=6. ;.bXqfb:zbl+RIkΞxX`BU*:HA:@ɥ$(Bl^pN%'B6~Ja{HNlG{(1X|RJ)(PtY>,.A]i]`6bґ5]s~"XW/=U1Z5Ib)F#3Aٷk eߖƷ.Y؅,~iLch0g-|^1V~UNhQ6eGcp*::^O~,`Ç(*N9Q ЗW%4o֢ڢiY0{Y2k1 y~YM<rTcGϺUYطVn˵AXW/VhR`ިϙLM0|3Ξf\ i^{زTctQ|1֬Yޮ*ۜ۳?fo 0L6W&C6rc8ɦA/O_`o0<2a٦~ }pO7/p@&_Sc'МHvm{2/nf)2j̘ѕRJ)JߊWs$q㓵JDb$Xd؉ #e[%l8Ka健_Wq3\Nn8T(-W;`H':Е%QqN)o)$br/)-X#"ˠr\Zl:6H *uy9n^b,.{ K/"%K珓au$(&4T(oCL%[S@=1Q< \fݥ{61rbLs2!ǓDԹdh lܿv R͙r,[+un4~s ZGM@oYD~LKz,MBY2vStF.>!5jx_6 JsN2A888W nMyv4pF%zۦMH\ CO|#G5iJ)RJeëǷq &3~}{J1 <Ɖ72{PD9JpKW8v:^PoUӜpX3y StQr&>]v‚Op< avgr,Ey&n[ĸTR{i*v#ǃX|؄.[q"&x'?q`j*CRJ)RJGIznLBנ.~UXRKRhayFXXXFFЁe݄fSa4[Lp 7 r%D ߳SՊe $sRJ)RJe3y3fWZ'kt^&R%Kѧg3c漉fq|Xtܙqp*L㏦2_i2ߍ-.`Âoڎ1v-o̻γg_+CRJ)RJGH7}{JUfp3g̘36U77bx{$yob7q ӎKy$:tbE; U&;E]!y١RJ)RJ)>a?NNNL4R5yݷ7Ey=V1ztO])qN4]s̄o =xS7V27MTFHTƳ0%=׉،.RJ)RJ)Umm۶DGG$+p0eTv _T4QQQ""#1cfoLJ#2(NL|dt)m%00+>Ċ3>`RJ)RJLe On枒%J2R&cIg|p__6;i00!Av2W;8|,'c <H,~(RJ)R*xdf?;vR7a¯[H#7}!yJqpb%nNUH&#?Oł3s0Jv.n_7tȔRJ)RJ)zd7 # y[{k J殿oY̙&~2+4a{hoMh5"s-W&L)RJ)R*eٰ/E:yu Ƽ m@w}ѱPm `|\Қ] ʂkq9τ ;d 2Ki6|X*oZ\ؿYdez}Ϡ:pRJ)RQ`.A'h^ F\o3v:< L%h&s ("3s2=dsoyQzW]dp/Rn_S@g3VJ)RJ)9Wo &` IopWr^'ًQlv‚Op< avgr,Eyp֠RJ)RJ)Hs5&˘,K"M].=7RJ)RJ,+RJ)RJecQJ)RJ)4xRJ)RJ)iF)RJ)R*RJ)RJ)T6RJ)RJlL7J)RJ)RX7ժTImߑ.QJ)RJ)7KqfϾ}qzmG)RJ)RLM)RJ)RJecQJ)RJ)4xRJ)RJ)iF)RJ)R*RJ)RJ)T6f(RD^pI9fT>Q7 W\8gT>˖2HH_F8;.Of%%n-Dn.RJ)& 1JTt\9΁c!oʔȥT)r {O]fTR~d|;rnG0 oTx&Uzdhͨ(OKCAwf㸜7KώSCeg|;kNJ8,ie{D-{mkƍ9}ݎk!_| Tւm㥩l8K%<6nd˄xNzUN"Бt*a8+a HŏoQ<"}éRJ)4xyNB2kU9c3/=kVZĂ1vf:ucF-bU3\J%c귙Nsf\CL+i<9d'_bNͭpT>V".a(T7 ]+{>xab6c30d}|1՜m#4,9)Ӹ;?ȣ3Ci֓~#cgi4nK';5Ex88- kqοS1z_*P*o쎋ipS [ҾG?7+GR7YLv*ΐA)ooܙu+&-;ӶA'Lss۬3h苯vEVJ%K,79vt=oH~H lW4k[n %YjW![AߵY/lxc%ĹtnLw$#}F W Vm@zU)Q Kؽeq|Z;e,arhSQ[{LBzEbI*N.4/妏K]+n`K͈\QM)Njc`g]^hG8x$fϦ9?mu'4-g` FCoEF)R*Mz|+^=WǍOz2uL\vX"cd݁ !Se\Q{^,-Ws0K˞Ќ+R* Iׄ"uOȪkϸ|ږt32$O?j1HrNb`Hfl H["Vob*[GCNb`3p^Eqb6ù_;Weʖ䦊K)WC sdHSgTlĤ4i`&)y\@o̗ S.yz ~KTM\ C?QiO*^U;4 q.[%G=VvG{)`2l OtERJ@xӶS('//CÚ}AlQ dIQm KM$]B^!J╨U&=) ,H(d53.XO÷n{bo,Z2th/9Av.GAQsJa__ &""_$'D1%ʉ">v=#ޔXw#e}06Dmb\SF}WN|_ø^B ܺis#oP)-X0̻hzV 7ZY\85dÌ'<Փq;lvBf󶃜Eك|>y`%yYeH([װ~_7mn+8>N!IWƙ\ܟ&D6&nHV%OXKe^y_lRC^xUAA$ IDAT׶L=26]Ґ.y?h;^h6~k;3+/ W9 >1a\''l_$zRj+o ].&>fl&}C>` iyXQ 4˕/J7ޔfİVn=Pg'͈a L{] Rc{po+['<% R9T%Mb[Jeb/, '˹-_34E٥v~cFp/ԦKM~}r7K9=VH&Oz.Ml"\8X**{cҐN$m?| q~f\J,AJUVX,´IB)Ry3w :5)ϲNTȨX"bixa8GdX`mUyNdHy$;?{_ps!CX{[lZQtR]vjWڱXA"*L+$<>'s|0p]86; C'Ϯ8nR-=-VE#*j#s\`D*ab9Hrzj!!G%kH$5c~򿹐Gښ6w>!V zP;#\ ߣG%#rE ޻ n˭3 ݣsG8(bfTw=a#_.|$pl:E֎5_^ ˱-l{X>f6^=]j䇰l> (j6Hg33pi.}%DY>୾$_\ PKҩikQ_HMM ׮@! ,ٻkfneLiZ'Ma>u}_Tsz1cv<=C,'r4NٗR$?[$*МqqN霮D K;| Cv 2JS=ȁքB~zo)S3Gm-oѷuyfnk~Qfv {Eݯߕ bhRl eX1Pn$&VMo.q`=~]z`VŬFʉ9lr xB${ij=Ǟ7XI.3/J5Ot{r?^:oU/a)v;#C=zgBv guBpAl?bމ\Jdq |ݫ@Ӛͻb3eN4WN5pia+w/Io#"N3yJ0(^29FO?0k5*ԪZhCLl*%+ f˽rMkl7%*xS"Q\6,f3XDƐܐ4*Ԭ,\r 6cv>=;LC.oa &͂զ3k`X'l.]4 {Pe`Mw() ɋs\ B!DM޽Z}[W`k":Oo7a);1cE}MSj,ƭ22l\K3mo|ΤO _la\bnMog dž"%n tѣP;_އa1,8Y^"׾ﲧ,[uǧßMGkZ#y Ka= ka\ވ1lk ߼I}3=7T a&4k՚}pʂ\.{q`]ضX:K73O}?5[KU0`_vIjb@U? ޸t 'NlB@ŗ$^e)-la\ <=\995ٳRVsY|1 3IWlg}<@ѳ\M-V)#9'9cv8=ncU[Ҹv40c2܋%t/ibgo%K7n}7Y,tvwXD$B4yf> =-CT!z7-Jq32.7b%nt(vEf6iMbt>IّR-oAe1rOQ__=2Uv[`:a9-|;t_!耽1&)@>۟3ݫPf_ZZ|(gCfcLџvRJ7~ H]b!.TlL~hCvK ;FM(mriVX~6l7>:<[@ԎM1+ltMjrdxL&hƴ/Oi_.,`9uS/x(kn%Z#wt{wχUy\&Osih3Aa3=mPwR]nr2.=la>r +:\OTxFi;8ܻG\9 !/rI!][3]w (q3$7:,;l+IC3.`7Mg)Vݦe{-XIr]ߠj+!MsI11(e%q5*>erOQW9mk%;0cw9<\:ơ-z46Z=/фƅб-l17#>e %6i+YhTdhK2p:ۈƆ[6]0R?9uʣ#Q)R܌;76?F_ No>\aҬdpOXӰwl7aJķ.p!:6|y>Zp"V4 R@hl KXl͙ g')t/wpB!z*oҥR C:TFI/vo| z1cqwi3xˀ [ȀsiiB`JةV ""~?վov)+B Qr6$׬󗰢aW0Rx!jޔoҕE+eDZǤHJ)2qLÄaOֱ0"ncE6ʃf f(l 3T?_z-[/{#;5<Dx* ?VB΄${,9Xs֗4lO X3eb?qMCUd|vn9JК X1R㽯4In3IFcN-'z [Izě3~At-{4r5pǎtxD5/++Vp4٥l\ ^|(Sk9uYOܠҕ{!^(ԩ[ V._侩fHfG5p Xv$* =$\Tn2md{:5>~kL`vfOQ hZ*ٰ~ԯp&-bg-ش*&m7l&ΛZu[8?9 kYق+ٸ{t-syNGq=UEr'zo>mW)1Xs)WBH͈ɫq;h.+o=zcnͮ*z5vi\zL RgU."!a]'+zB[ҵ:\0l\pԲ[3ݿ :A0 (<=|94,~1Y8Dz܊R tӳE. 7*_[  T֋Nlƹ$r݂ lWӯY[;{>=WiFJ>4z+WS[RO߱£!i n+ѳeDW]Dm'+'0o[4`B wm/o9թ깦*ʔf=a9' as'DV(TvU7Qu57|kGy@Z+ޛ)?Xl*غ+|e@ <:8?e7CPqG9LFֱ;䀺Ar.*r/bIt/p'2?Je~5eG^̽rP=e*drB! NΫ;VȢIM {*~A'إ#Wa{%B=u*꿽-C#},guï׏  ;z%)\A-r?gso ,{:0_غL_qi,gQu1$Zɗʲh\0lDd:iƮD?w.o&^s/IN9P( ^*ojK*>iK4]EASzª^֫Թ]Uq'mWI;OMEK}fUNi$' M\|>^y$* -0#[ʡ}wR%A܂ו T;IcROx5s.?S?hZ֡tyzզdbZ)tǪ6xmudת[}?˨%:QZ U]NtfV|PpYn7Rfo乜ëTjQǿOP}}Ti.Ð[mZu!ݿUIcμx*p$lTe;VVuq|#iX;u2 [!i0_y]z =LO^ieQ?FNЧt &=a tzӗ"0]SyP ϝK8w&Q=ι[̇r"Cok )&*ۗr58 :7|P%K6<63p̎^.[uΟ;&4G7P[z'O\Ϣ%rLTa{8G<<\1f{yD:cvqJe]|U=RIs4]'FKR-5]6l=/M:Ί|!"<{~σ|$o^HVڄ m9ԇ K!HlW2OnNgSg{2gwB! @/B!B!aB!B!x9ySBLqf#B!B_́C2eǙՏB!B!6%B!B B!B!3L7B!B!0I!B!B<$y#B!B 3dwB̊ŕ1(+^yrbxB!/$yT$!w~ܴ>vSs?߷r)(xԟ>>;}F!B p%ϜƜ8\̼U߿GR ZXW !B!/"lJ*4{9Vԩ"4y#h^J;dkHB!B!&bŕno~lKخ]vڂgXW?9fK(B!B!xȄ@ÆYV-[/oPDɧA-;2 F߱rTt~|PopZ,\vMSQ\Fˍ g )T !B!|/|I,_<8*b+܎ǐ7X1U\ΖgbNF̝Q_La‡qEs83d<7NU?f4>מٴjwZ(Mv}2a,]ϩv.?s_H`J1{pw|#,)ذ? 2f-l)}(B\*CB!BGmS(QΕKvO*W_e䮻3eKS-G2ٕa&4SZY:͚ػ}Ӈ$QrѢg{m^5W&Ec*oҵ“zz{; Џ yì^cR nG9`{ T & wxjx1{}y6!1w֭]ɂ)_ѭ 7|OF/>#mu{_(|ߜʟGMB!Yţhs9FƄSR'vܮrwҥ ֭߰_p55VG#׮w|W*U3K5]/c@麵ޏZ5ā]uAY}xby\X8f/HiԯL̺Txy$vG\z>q?i#^\*Ly 'B!x -yӲyloYV9r˗~P̝ëF39n*j Xʰs]z/ΰ:>)$ la\bnMog dž"%n tѣP;_1,8Y^"GG9,OoOt\qK攗|I+=>#Cǰݯ1|&m(R%ЬUkW)F]GaoGs`>5B!B(^ۦRrA-^x?qcŋ!̘1W^nMr0RlӚ">}.;` ^m#2I)^ e1rOQ__ayVMȧrqtE`D`@K 4GRHpO|!iv%mi9kڣ U !2uD*J*-8"ъRi#^<ܶ--B!B(d"ĉ_ Ξ ?Ѿ]*E{?3rOۇ{G>ЗR4HsE5}>:Wc85ARfM.ZV#庾AtߡbM!ks*X28> T);}ʑmX?g/a߁<]D6Ⅲ6rʴ/'l=VdY"B!B&FVFBC3yڵiOjF^̘>m\] HDҤ0 hX=&;B`JةV ""~?վov)MzfxQ7/p):ǯ^Ê!oSyFǗM&]h_[IRFԭuL{ k&}zY - q#fwhB!B$onEDpqOwr*F߱`\sؑ.Oh/++VpԜ\?6bdzwA='n>KW3VtC:+KT6lz? =+%5w|aټˎ\m6]BUzڈ+.F=ޜnEaDOXHU!B!2$oN;uffϝC7QJW|vk&A]'h@'sEO7ߦA ˱_`~MbO˭(mqA==[B b{s8r<591Y0b,{_1ߏьyVo|׬-=w&(w\Ŧ+Y}:O{?F!BdXq>Ev񐖛ƣ0TN0KG|7 ױR NtjZ\>q&^?2C2ӪG[.…ty{"wI@2/kciTuݺD<:/cj3ROS-=h1n'_+W_qvvK"ܹezh9zlg݊,'oB!Bc9FyRc)NBuw)WlvcW^ih]-bMؑ YPKk[ N9V?nRaűS ,nPWE߬1ŝ2v\ڨmSJkƢ9WN&[6`g StS (}6hJZXzՒ69v'}ԬNOlvYB!B!gy*gT.6V/pi=pKSJ c=|JY$ e?i#B!Bd=ㅣ->L>{,B!B!YRy]\  ɲ۹`rGSV9CB!B!cHaiq՗V3zw)!B!B<yA*aɮ4;PA<3~]o~` 6;/db_!B!"KHEw۷ޙإ*B!BLU+B!B B!B!3,ɛJ*dEB!B!"iN8t(+B!B!ɐۦB!B!aB!B!xIF!B!&!B!Bg$oB!B!a@R-܌A\ʓcv#B!BB7" 9|f㚆Ι(7GXSPJCLl;6B!B!^I-6j2n1OytgCw;ӄB!B~I3=y[R`%6'rAEТL]?7aB!B!D<7Ys4)[={fr$BK8.o|͆ׄ{#8B!B!H&y3cL BɄ).8qqqa3gzXΙɛ'q`a6WOV\I|R=֛g8t6)?K1is-/?%3-KWlϭ0#4)<˗irB!Bxw ;;;O啕j?8x`ULYztO_)ev4r%NbHz:zT*ʼn)^ e1BV5?E]n%cB!B!DʖզڶmK||<@73g΢[7P(,f LLD?qq̝g$oRT?aQ_8=CeB\iOجԓ7@sE5}f}H!B!OU-ڤE&kIkW~fo4uKN4 P3d4׹Y -d@;c!\(b̄q!B!~2id^SAjh>b iXMg빓$AA[5­I;qu:&͋:Jc5'+z:BnB!B!CMyyƨY83]>Iѯ `E$z|~~!7?)4ȡa9 OT-bO˭(mqB!B!lmKG.czfY}|AqqQ}t:C;Cҡ.xDbq%]f~0T>+ԃ_ǮK24 ;ѩi9rz[Ǚh;zڲHB!BMVϙS#3wa\x\va/GXι>YʴA.Pe"ΔFgXw!MsZ~=Gf3B!B!;G M@`┩VpOr)N]EEK)wوzB‰¹B!B\=5Rym(M٧gT. !B!"ɄB!B!0I!B!B<$y#B!B B!B!3L7B!B!0I!B!B<$y#B!B KsR {2!B!B4'o:);ά~B!B!)!B!Bg$oB!B!aB!B!xIF!B!&!B!Bg!B!ŕ1(+^yrb̪nv# ng~Y-;=FqMbnޞ;׸cpvG.G!Ӧ" 9|f "(':Q}}[ru5P/"OHtݿU*џ5M 7rIӇ+ߧvf㊏(!"$yLXr[NZ}[ڝUvH?X-ZrC`@WU1kB<*7}F6sk߆7E-glx-H4}'UDb꺳ܵGtNW|G=1X:$;H؞>`|zDZIz45CNw瓯?'jn9+Y.v~/4ǂ43SnM4'k>O7]rOϿ9hN{Ӓ`vytF.LP:^eK>LM}n_6hE}^i:Vz wʌ#4m?.Ǯ_+KB 9;/7gl̐.ղ5&Eiփ!64Gм>Rd!DX82U'7AԧY켣p.ڀZRL!ܝtϾ̟[ӳ "61l旄طCyN2lOV\GiX)'k޿'h6.!hJf(guڄ^wE%ȭnrr,u߷票4$ԭ|dP&EGlxrofӲxq~g-}qvK m-UAjGa 8z 1'> K10=Jsѷ軦 Lz-3er !"C-ϫ}`)et*oEF7[f^B9b6QWRe`Sۥ*A@S%z%aǛceSuPyt2v& \!0_VތL>OW^WǮcfá0z(äeGGXǠpԄ{X*> PopZ,\vMpEZEq)4\ W WlP9{>$28m{9y&ڃy$jP# w9z&N)^U|O*G(.ޛzKR㯄fSp,J+SbԨ^4 ۸n8-Hҥɛt΋+?~*^I"B}go)YZ~[p:,~UQwʓjڽ*]|(V'U^Ml*>b+K[wK%߁c|l 4`L$‚tf#~v>ʹw׻Y VhG+{”/AEkpgkP|.T¡r֢ߴ/؎ ̹]LW8uݔX yqIt*0o%QZje։o7v@1zVͧh^k6~׻ߕ|+ ҫBFw >dyF!L/bͷvWWqbe`p{Z=DEŚ?fMN WlOkT:tʽ޷j'\^WGUr4QJaë[j~GCrSLk10Q3'/̬=X_p^hU$ml՞_ I^(4GU $AAZЫbPO,R3SBzLN݂ IDAT1̇԰Ku^t#tTnڽ_q*G WLJ-x]9&HCԒA T$XSy>R'> [>K԰ʨ%~?u(8e3cU{M9cO+K*ݿPX}TH^FboQڔT9tIk~ޣb▽ri(?3W֫/uEo7ӏTf^\QTr c4}g< {9l.ƫC~c5[S[U.o`_<bΔ0?:̔)ޗ'\T~`1쾝D hҨ zɎ-1'z0=qW瀣M;Ns6jް bl*6oƢ+YHpmX:4LlwoĘ|5oV]łicto>|WNZSs lYxܐ]SqJmxiܓ)vLslֳLԚ!N_%C&-d͖l^?WI}Кw'hQyqotdr\+7p`4ohmX\(X<|p\86Rd*Jl^ں> ?MYqX e*NarAʔ/+2.t{} /:#;V: *YB!Hf߇Ԅ%ݘx5}Y5~)5eigԪ}RJ~a5qqu;ʤFܭ~[|0Hzu,S)tm_rFMiv"\KU!=Jsm\+~RʠTs bjFiN >.ċ(*ob6Qh˝G^U(PJ *x0#&X -<+~QPy^gOo> ;oGTX ꥼu(cx`ԧe PMXN ޫy805%Gi9UyRUAuT$OQUE76UҤVΣtGS)Itf*a@a(>ߟtHBJW )r%}U!= ډv:U+x,GcB \MNwPkƳk,@wKruLó@?{^EwoIoЫz "tETDbPiRC'kH|? 7 ">r洙9;;[ՌĬdƼ3ޣn$a yl;8-ׂO*$R "(_nsIwn"kgMã KyDjfzH~ n [- Y+'V(;OM@#s!tn^By(Y =ˌUjMgGiaݻa?CvvKXaZCT (f=+#>5){ga+HJhM<^?Ҭ 1Ǩݚ; <5ɬ,k0 pVWe3V|y[6tVIɗ- {IP~uPC)pMٯ}C@;>] \l 7BB aD?;*82Uz%w4gbMP( ~1~kTZ:jeاb;de<x &MOZ\W'r=6^tiVF-gz,lK.^ů@~_AbFdb 6n: KAb*W7l߸!x`t^4ɒ]?756ϝ.'z6bPpDHqzNeK1R^UF%]+-g9}'W ԮSSA6}ЙjƔs;m9PpJ= V-bmv 9 3=&f{|Ѳ'-L8+WE߉XRr8iJ^h3N% &Ԭ)3&wu~Yz՜e'G80K+kqFw`]Ml`:ej`Z7zAl|ΚyԠnu' Wxp}P( EpO7߮ y;d.7M00 ](g2ϝh0]\ΕBؐ2"+kH߿1}_JN &e(N¾4%^!i.ۡ :,3cd&Ƀa^كXEo⿋;M=]dzTtōi9``Bb$P~> vo4Sk"638S^ xF4kނV?cuBʅZ4G{b8{#׮%bb/ׄ{$b\ɷDGk`^۱k8/@ ވn\:ᏕVe3Q_Yr{쀉|\^9y74bLX8h^x,׮'M-0 r^%xTF:Ҽw:\.~,GH:=M۸[!32MgׯDc0 $ \F7LXHL*ք䢉{ ;W/]A_N>@˺2'okUndHP( K]]ӅӏeO֣$>U̥ inK`CkP2zNh!oy 8O(v> >ʤ|*jt~)Bϱ{2.YciEܶMwoWA?L`6|{+VV4FXsj]F +8#h#&46y Zi^|-Y0 JnNlL,vѓ 3ԭW/fតvI, ̛YA?mX ?-J:^3音Ly qټSf?%޺#_n%[̚= 5=RI$-PR28yvBGOaCóXIMy:мߴNL2s O[3b 5a?4נGjUIۘ=_latRAoBc(%+Ǐ f-8vnEQ(@<ܢ+Ƚ Tތ\Yҗ3$W0gXYua=Qbn^cJ#+o^)Qq]|lX4qV>V#yF"Ƣ):qqK<_Ӭ# `Oo)\_5zEKh/Hͭ{XIl {=-$\>~<t 7x<9Y ǨD_ZZ}PzAXxg!2$6LͫfIʌfҰI- >V{<_ 0ϱf4ϥۮgpkЃ_Op+ӗgO6CB lt,?+__)B$cU'+,O 參y$>[e*_N*Mпry.o T-|6'j=1>X9##Vk9/n{v^*|ue= N*w B%T hW%(-/x?sJP;Ox#,mUNL,İܪk(WȲGҷ7C!1{L ᷉h3p6G eO(^5%ۈ52necD[Ԑg|y/6eM7%:6BTF E̿ 5zNLVf_ח䥺T*^+B`w+4ZxVyk'YxPok\2pKȑ! 5fMvJƒ1oZ"C™ݬ;ՉmilЋbW1n[ѪC]sY&XVtN؀ՌW2̺QثٟT!G8|kf>OMSiٲx1&b /u|p[$*y[%pE'ړr+Lקè8|2te?uGlh4 BP$A>c%X Lu8H@<grT]441)_t_5?fUeL 2I(_jB…xI9<1i#g eRvWdiIJ{5JV.19hb-&5iOt2^ڭc=OSw]MFwk*eD#=4Ԕ^$kΦ_C hitGD ~fIۂ9ψK Sbd߬X:]\<]vp`?/H1+K4%q/d Ak=Zv~O7)-y̚.5 F8n٬eͤ^R, X\5fM@sdmeOc7c,odLh4:/U}XS̚&ie;M!=LyeɌ[*}4Y< JڏIe,ۺɩS\c96 BPLzSAK~rWX4[z({Hݝ%)],*frTyVU9t*F /K^wX ĵG9z21V#y STbvC'ru$}(]%&#!)sϗl޹mgaOr%ɗ{w{9Ʌkh,Q> [yt>-W( "=Uy(qTyiOd~U#,BP(\+U3]S>/8B}_^ˤP( ŃO⍚?t"?KUFP( ;!X85}m4|Ϝ`:~6UFP(GMو7\#*7Tze*#zk Bx td.Xw2Yb0Em#|>ڨXP(\@oØ1L<>r>jLa) Bfw~Bs^}`'ꏁts5IqBP(½wRq0U]l@hQFo BP( BP(c\.TR%GnSQ( BP( B/rfΝ9r㜺BP( BP( ڔBP( BP( }*( BP( BPǨBP( BP( }*( BP( BPǨBP( BP( }^ P( \9{x͛'Fu)'Z{F$\8Kx%Ӵ.b%zf|ܴ{-^^r ʼn|u|`l{hҦ65eGeqVm:wne˖-Ym{-Bpc7ŊPwC7^4 "^KwHŃ)Bh孬ȞY|xK=3d۞(K`P^wܸ@֭ٹ '.`2"L%.bPBB0zK z?cTT.O\ПRm<6}`ɠ_MJGd~%d'ٵ5vqٻ-[elw=cۓu!ƲzS+oa IDAT3gNlR&3uQxܻe-?]uܚ0rkm9e'_Q%u}+jtȝGzq@@w̨G#ܰfi/4ӽ/96/{1XҺ9/E.ީ -IͦөWz*Z~шh@QefdcL\-'O4_|/{!?|g+cٿUJĬF˗U׭`Xk82Yj\U0[EsUV^Ij5(T0~I-[.j@,<ᄕc[ScgV'UV-B M8x㤽Ӫ(6\K4W6ԫJ˵(ݺٿͮt?W#3xWD..bIbX_Y?)0S#k^ћ`ƅs0||hYH x|\ [ftd`c44bX1m6'Xf]e;+Pl=~lhj{^+xGNHr>>t?M 8&j2yH8A.y!lԼג(ĭc8v3aӋڏðx eռ!TJ;67ߜfzpD}bJu]&JV(Χ&j NWŝ̟|2}ާmsjm-64\ Ȩ>kc*:u?1FT0fxh↟2eCCQNmq_8>koc2۬|OA> Th,Ex)T],N҅9y1DI7bND\"xn` 9q6ODF(h%>̡֭X[-ll#x; ʯP%iR"-vi)1ظ'yTM7JLv1\[8p2!U?rA}Go1* ”x˗y2VTl؂Fomdf3˖o5HeGLW%\inc'6osH2/"u֦i{6*21~') ='aLJ(Z΅DIڱx1Sٲ,c^\y'@0qU<)\?,%7DQ4bġ]ǹʐֈ5=#l?r}rb-Kiߩ6S69_~WZZ c3;fwKfrۏr%&%W86/N)cV/tƑ-CVn/!4YRAFI8ǞM9v SZ5Jd8hb3MkYK]Mk HgX eoU8Mׇיɨgߵp?ά_ d_s[U˩mkذ4~VoL*p'sr:DPsᱹ!-wv~ʠ9!yиK=(-| 6=:}_Lx惆Dvȵ70̄=(q(S\<.+AJVf,wS8_O'ǎD!/%.wy+bwtұA0Jo-HPJOvI$R ~K%g%yo4"Zs47)5XRj ʏ;껲zӥoEDyȄQJ^n]] Eַ*IsFn\?/H1 WfbңS2qW\Kٯ6"ib D(,̖C $&y4ĭo8L>fد֯I1xĭHzW|zYtǕs$jPِsP2哧ˉ!r-M2=ʵ|f/:oVqI7 2h>3ȧNw*-;sKnĞ$%/T Cj5_-j.huǔ5O) w<}_LfЫ\;rS7dՎҹcf5ʜc{yF<]_g&n4tdޙk}KD$lX;Hk=A(c eD72vɇRMc@YQ*:G:Đj[r$DMDDdZib,]8l-&sdEfeuo6nnRkT&w⍈]CeTY-Kf_Ɂ=ZV"^b,}alYH~Ah'>.y~A-%n&'? ?~$=j[-\/dtsRք`*->"S~'n:I_.C2`"r蛉Ti4H ~S,]$s& !nGا?uأdnP1i֐̔6lOzH<A3K^ɅT "qqo@̕JBQeɟ?vCC4Rn<2U|TK$ ='.)\G5N/ O}V R"L$ksPbJ<#,YXN&M-u>q-9{:+ vĻ3ikD9Q4/"Hu\Y SN>AfG-](sQijHْvu_lWkVD3iQK #,ZyiP$xPv.d0˨gf4c,;~meR(#*')ȲKdiyileȚT$U|v`.t3ʅiOfȥ9+⍈$/8CN](#zK"_y@sixwx~:򫱲~:޷|X9vW_!]%Oq1i)79r_fM AeQ mdDs v0ͷLM5KX?Tʚ4Ѽ䃝nm^&K]|r@)rxxi@8ʯPd{yScW$up3qkA)TcdcpI+:2ʧqH1qo*oU4b77̈́gʣiSt9qI_)`@ y@7emcuk5hhTIy9Թ%ܲ;䖜=}$@C421XDȧ RVp8o/OyYj~O "eDeWGFE:c3#[荙܊w o㱲7nxS*5P@z.}^F~|aiFO?fqޙk}.n'~6qOTQ4j̸ٟd̡$9Mxi5lhdzOd> x&i6qf(/T5#1+1Lg8W[@Ur|QoB(%5}s}R6&h6iA '/:=F|ZÚPSIݎDΜqGA Η4{^̈݁5j[ >ѫD0ibħv=*Ap2,ً;֗_aޖD3UҾ;KuOd^:}⩽hrn3~6+Q(O%봡c W3[чgGiaݻOEꐝVq0rbֳ22|f$󳂁z\[s::1}u<2yG?Dc ƤIjʎ-6Sɞ[w&{s6Ʋgzm㉻2;œ~kTZ:jeڵ7sj s={wkN 'Yb9O>Teu#[tNǹk|ˬl1_y2mw;gB"8Tl'ظ 6LThX@gYXzu1n=I[aL[)3&wuW\K0ƥk7:)ͳ9\Oqqŧt(lr ;FJ kð,Ƿr؀PLiS;@y+U!4|I H_I >,ى;Aw0eS6łj%聧qgi',92m1C0QfU'9H6mHvdɮc4(}^+2ײot"LT~ ͸E+0_Voשl\˒}u/.'xiqsY7u9ݻy<8u3fΠ]ۧHĝ_lwg޴d|w/ɇxkx/מtO8}z*Ӝo]x"v3l(NG%/_p>2-?t9e;KW]Dܛ Ii&5bL[-3lE ܓ*GqSlc|`4||i0ܖ%;q8AYD~s{pr,IiEY^k+MxQNkXK"9}*o5a?2y >ZyzGlҮfX| *4{#j"r:n ?VZMDG[8lY*H];?xqssԩL>nOJ9]I3O! ɓy:ހ"oMïb}ur5I"1)y#k&A(/ݒ$l6gClW~".g *i߹I$&%dw f*XIJ[jR˪<و,֙R^o4_lTo-J$fÉ|1} qY>T Sp: W71is6(wA 9` y *ְwP޾k1lDVMX͉7eLiٲԭGv㖶w3ۣص<6 *p9B<ֳ~e_I f:{hdHͿo/c0ylGƿv_|>ǻd@-6LYӟGwsOTHRR~ߦ ?}HIL9Hl|Ezn$ίlxxgP2a|Nv_Pג/}LY?!9'd{_赱Nci2$ś?P91ḹs1:vDUx"VNI!9 Єy;5o|}4LQ_慷؉N@άpiU^lae)ִtHͭbc_I9W:ݺrP"k?Ǹw᧥\IEIeY-aع ߞ=Ċǰ(l Ceլ42VqFфGLeiT?mVҼZVm`(ΣRzN.{yi͛Mيќk=:y|9$1 |S:ğce,]~x鱋m aJ#+o^)C"77ל&nJj5oErZ7[^Y/ңmP *; 4zۣ׾̟NTNcvih:C r67".dPy3regLzu[\.Or*$Hq64L}}nӟOc0保sN99;ؗ#_U1RKwD-W -r%azWPM>G2[3u5g_6;ܪ=:tpoEed'Ur*coxNT^^ ¥_#H1 Τ*}Ʊ71-? Wcis6rk]L`5( '/e_:/}t-M)AI\Lavέ\L#(վّ_qkY.C_ ӠaZgΚek#593GWzzr\u;` w-5 IDAT$ YYdD~ԑN/ ׾fK|旒KlͫR@6-mŜ7=;i/KD?{ȶ^sm_O>ي@1VG;vb7:f3U{z<"c6H_pkfJ+064b-\s8XXcA3W] l=2Qm9zӘ%#[H)Mܥt߿5e%Ȁh)תJF0H`ӱ3!)}i]/@ ȇ #?!#>>!FTQfIU2Q ~ʋ/[F+zȖĄY9$6'G$C$˥!`^R/>o&D3哚Ͻ)N"ƍO?3?//AW4RɹϳoGs9?_ 1*nsML? ьNwdWoRG\u9I=#%=4D3HڏJn}_~ҧ[GiU4MA edeqLS:uc=(1 K(kfOEvBj.  E,GJe A3Ѡ{rA|ћS^A!zWHw7 K d`Ɇ=rDsKƞ~e|_ W iMyd mBYB}E 1 ʚAѲbHͳ 7n|0./Mr+vcӹ-47b- |!r[+FL|0Q) Y/R)-a3];mt;fquIoeo79ZO_ ]ˈ< ƒ:I߉|)9LJo,^ (iqM9ꏕ#&gvFD*7dvs3j!|uɱqݾ씋_q-5xs5ąA [*b?<) psޙݐ5W7M7 ߓN󋻖ACK/VI#f yKHQNJ|zW(\|fL䭭ΝJJ벩c A󐂵'KO+NFĥʂ^2@k\AӲfRCu-ߐ?%uY^#oNK&kȞ&cݚJ@[3OHMyIlFGɬxs|j3t}-Dѳ:Yj| }B6Б3_5Y< JڏIe|Wל/lN/1bL-#}I>d͹m籙-4c- K; yzj}y7Gai,99UɊ]MclYFFDWwZH%"[ k يO5)F^:5kcJxHjd#ngjy[uei|]ee1ki븤ߟ'0c1+s(tS\ڹA(~q˾ވn%N&^7*Xc1cC3=C1uOëa׫p[#[^ZRd:~e7c1c#GWG=:څxBzGm:c7͏X( c1cGAőQc '6;4 >>$VZq?c1ce,W"|2߽;u/p1c1X't:u,f1c1+8xc1c1ViPENla1c1{9sENla1c1{mS1c1coc1c1J10c1cba1c1+8xc1c1VJ:1V6(s2)J]+1Ewddqg ^ 9* 鹐;F(䰒Pf^^&)|jc-}E~˭2Ye[y@y-,;o7JGܹS6HVt{i.BNJ{@n$4PLDDnI,P|ySel2&󫋐o 9?a߰vBoX:8.,wUiS\UτhEe)wQ>\BKS,3urQj]U20YmEqo-E#\Bn)y-,9;oܹ@.yTN:o:0شk~?֦MOD-*W;Y{qQ-Cpv:eNrєtp~/`R#[ 7ΰwc\iƬ8 v@Gx=NK77 ؖQ%NIQfYc\B&B#@hacb)b^Z*&E)Q/}AQD"8|0&}5.vF ШA#֡CpT^g§~40G렢˰P¬h+~+J z{+Cڐ/\^@VХTK#8Vn!E x8HJK`p>W mw"valm +B)8\?qt~;?}eO ̯uNߺ+pd fve+D'^ yo5m{Ej%S@LQږ \ 9 rA=E6Q{=}~°[X9쬞4_V<ʂ 1AXj9X9#C`@[=NK:%>=G!/ސ\W'i@Y8e,88ј4iƴK +0{36iZ2T X|SYzWU9l[< _؀K{ˋ2Ͳ,e!|Ek;)`RE)Pr!C1Ml*T=8[9q<8x}pܵݻu_s .UIˡrp/ dގ{J5j±UӇ!&>{xUmBQѹJn%׻Lƾ0lL]6|QAp E2}画G$->KO_%.*N.9hWO$P=D]r@6ڎ:@RєMS^:U niݯbr@ t,!"MD&Ayɑ4W rO]L4i~НY}]M9ʂ嚿_lWxl A-{bꗈ(4I[{Qz,U#M?Eeۯ`O:L-qzy ejVX.>]B9TpS{ omEQty"Ss,wQZڤ0ܶr1MY jf#ۈ?M-\8fo*/I{Ѫ|m*Sd:̣+Sm*Pĵ. v;EGvҊoPM' PgHgOۆW%A oK1wڴ}m^>5.GA jchCЭUd.AL}/%Kџ'MbϘ4Cs}A?xeUzvl(M_7g'mX8:!5Im"m|7d@rhuX4:D %ANla-MC[QH߻߿^JvHoIL<+i뿻hOkP^Z:-#+bp+2ߦADFUNSmݑ(߿hmW&$:cpK3er$"lrh( ZxQYJB/YBKc 92I!*SҾс$ڑl$0L%9$=x;eQC7E9c6wh=~]Rvi+=.Oir}a Ӿ^o|MEOW*<[[,D/)zv$@ ǚoҴ?щSQ4C 5hE ot}_hݶݴwڰERA _QtkR7 ejVD= TnO9Oa4Ms?f(j##94X5p-]N62ԩ!bSs,FwQ~9Ns*uI7Q@;uh!cBJ ;-/bxR՗@hbۿqDlRo;B?͛EGK/Of͛מp<.]h!"m 3 HF'TS.ij+-;FNu 6!GΫ[Q$8w%G'Pu@C3vNYjSҘR`KMg\yɣ5ڀ`ӊbĦ1siSg{>W$T.'T[}c4=מ~H A^EjwR ISN>;I{""KA ALcu|֦=sr1o "ՉI.uH :JE~j DG_ dJ}ϡu.Pbqcز!UTN-/ah<5|% yi })dޥAl,p= +35w8H@rpNͱviEN=>@L}X\-Ɩ=(H MK}O\j7M .kRVwpoњIwQi];rjJStwzƾi.kIfł$#=q7"۵4k3/C"f>5bb}1o[ZJ^ ͍Tq+u_) ~)>$Ht]5fۍO*Ķ1qĴka_TFuij׼t :y} N5BPZ w'@.AͰqz D#vBfЬȿ6#CP]XR$5@}+6gTr.ZK*LiVG^&`D}9(ngsq|ퟸ::7 mWcĨ诀%d~3_zWh8I*cQ(x+c-ܼw;8n=4ؖ.ڲ{1Ŧg.x>iM | k<#NMZ;$-E|>Ɩؽ c:5@P]I2Z2NT ~.}(9USLRg׊+$>3i$B &ap9Mޝ'^iz(ͼukcEF ]GcPUA {IiB,00zEL[ oڕ6bbᒥfN447 mgIh3x/na}uӪKGףqzqﳅW4--:ǡZ )F[Љiqh CH7KѢ/p);M< 7,؂69~+' 82)Z4pC+ܜ ;9' NRTm >hI]ĝ9B.,8t@.թ -9'i1__.:[+n }Q/OBRAV#Gj{FQ: D_mى=7vFa˷gaM ,&L]'q<_s@xgTMy&DJN|{6g.gy#k媻zW ψqvm bKQY[bbq| Pq}=cݺ!(5ʚP$ŲzE$3LZ |ԮĶ1܀ t{Kyhެ_p[ق>666NN-:9o qn5߇߭gs* [D$ \V!+!Nhm"~ %MKl_}eTg`2ȶ-/8I>nO]M"$i-Y _Hq{Qpr  N.΅)t* qaD߅mF!vlȘ1SBm$'wn8qZAlw y=tm4Q ct_B#~玺c,8߽@i>^G)A.npP +zw[ 1}ZKyg}<ֽ?"PD9BN'hT8Jڠ}ҽ'^iZ~ M}} 0nB?w6//Hpڇ)xLn(ͼ ujkKcEѦ-9KZ*5389J!JKC6b0yEԗ%+bygZ sڿ)JlX<&JW)7Ν?ތq-nߎUЫWoԩcM{w5/!\p4ʅ27դVB0P{m{\(y{~r43f&}^5LPdkgcr(<-4 ER06=f+E|(ew9g24l*:Vr9$1gU4< @ѷ~8?O`.-aZ#,&L!A";ᚦjjOcTHABK)qRKI  A,WcpU 4+/KWD3η)Jlɍ+fU1lNFL ˗/a0y֭mF_ԫ[g:|P>  ;&bmeh%Eg'ͧ~Ƶ-2e"Z \|M?cf΁b6')BvV6vrntp*MW"ElEAJ6sdAZ-سnb}VL;y5sGaXF@Vqb$LFq9-j:ڴ^TC8xZI`8:cqڂMQ*@pDvMa G-S&:?"W]9  栬 dj ,d^h3,(' Ş][ǚ-8uf%>|%6`x% ^g׌,?R@a9Jhʈ4f^:-4Y ɘ(rVrҐ2CC*ø]31FS<£7Δ@;0Z|z=|m#k`ЯEfsB|c ;ߛ ,A%4vO_j474m`X[{HYET A7RZ+Hhw-E4vh(UP+5)bĦ1s#\Y&- zܧԈg-3P#_)3ck-h GE*'iƒ8+2Jܻ‚}?ξ"H232–DG>Q8pY<^ؠaXs8iby('h]K+Kk8Cxy2sjib#~%>λ'*1>_H#nǷ1n_הŢuW>MejfXNnvyu k6iQ̧ib٘s[xLU}S/a͸_l]NA Uf\1fOICD]-]ۥ7x ݿ]PWsLx\_.oEÆJ:YE 3#\U߿&Sp\]ؤçbACFȠ]wnN}qJ|ѢU0d־=TժqPm=dvN 6 -Ņ"{@r~̑}{.BU몏^\X'P:HA>"+1"6b JGZ޾zmV]ŠAIȈAU>;b>3s(JX5 آrpVoSu +#w hٯ95C#DFH!O[jr~y?!A#8Ք/ɳ̪ ͏nmWHc'W~كkӑ rߴ m;hq?~^0MejfXNQ8p$ 8ZHQAUEQLy(bҴf1DZujB5.=Bŕ5o/5vńN"1 uxof%Z-<@ok>QwFJ'oE`ͺ@4ndW ^8y3C`&.:68pGH{h"@}q>>uK 1hS3?9R A3_0y| nw]h=^׾*){)"MgP\O?L29d=M߃Ħ1r "`4-28 W(s98?*Vk )Yqׯĕ|$ P̡yRƶo燁䬢EƎBm[ x{KSOT V[ i֝A# ,HODjRd Fހơ+ 3VUXO{4DqplPBFoͫDGtڣu.cd@;;Tw MZƠᇓ/8rbph5*C27Ŗc@0m6uNL, kxyJ6iĬKӚ{~-o;<T'48M`nh2O*F1Vg6ՆcOdvPk>Q{FJ琙9tyEXxQc(` ٺz~Twb$ihm?@JlZݏ<2j$7Od{%[ړAp賙sӔ@l)xVJ:;yJ@ĕjtJ&׃ꔓ yEQ_HHV^ iW5GV^JբOAz A}y~PQ!$K=PAg8}4_BxҐߐCg' HyQh毋ioWk$+ЛoZGѾBS?ʑ)U4oyy=AV<ϙܯ/,NB1HC '$H/hּ4m` L6ѺS' 8Q?]_"]Y&U r7ܤ3yw85ۇ4Ln2髈;L,UTN-/L"S"Ӽ0[$ t !D ۶N/b>YU!W[ y?@Mut;*Y̨Ų}O|"s% @Ce0t}ݷ4yD7 qS~SR}|qBl }5c͝;MN]kDȶJ{_b}͈|c?r@oл-ȥz7yh4TA r4l+4ņ7^O;4]F̋^7wbyP"eߡ\ޘcGGë̏z2 iA c4[2W0:ړ^ԂBABB?q*.3YsLoيPFsij>k8x=]\„? CJP=[Ek%th|-5"uNNq; Bx<8oIDLzɞ'ıuh](嵿Ö#ı2uf?SD2*}tcTAxz}_[l3?LNg7*"LZ=A#&oӌ= 0 ԯ(8 umԞ턧$T1|"}SIDt6-ϻhWGs݈ң 7ﶣ6O'%Oҡ"ʦ[㩚.J}קJQo+"ٸ} Uk tIEĶUYDDCC.w7.OCd'E["5ZVp"i4B sE 6%=.R7 օ2t_3׼FvٴEI4wMr;]S SǦNfެ5iZ~G.' AbO~ޠ\zo><. &w3{kؓ.C jDXͷic&#W.B^k ^3_Ej %=BB WBE_'#zm6Qxj>˹H%=ޗ>j ǜ}pW'!^A\%;±\T-ϐ(}SlZ˕ȖBtiMJmFNMf>R?iȵu=}Vu|}_y)!ػr05ځyaiHɭG'0c1+s(tS\ڹA(~q˾ވn%NVrz)URc1c9ټ菩{Rt^u^ :BגJ%+I/Ro1c1+!бzY~ zé]'7pt& 3MҋF8xc1c|sq$vԘ/o‰  zϧO>^6c1ce,W"|2߽;u/0i#a1c1V_.%Vz6,f1c1+8xc1c1ViP5c1c109xs9k1c1cM1c1cba1c1+8xc1c1Vq1c1c c1c1X)&+0#)l>tzc1c1\pXHQRGz VU'řhc1ccڢ'H`S|?P31c1cqdRvC 9pYy[gîMb5x23c1c1)3QUW3&Sv 4)јX?{ ~[[ Cyހc1c1LfU8e<4j5J%  %   %gukY!xS;TVw"}؞4#B=pf*!!!~/x!Gzax>| 1ëb4o9b1c1ʪ2R*?ФcwnGwiM4 !h1hS_;IwU]_F"m2"6J,@R\B1c1+LfႅX`aI'hڴTiHlŨ 9(\IԾieǍX?+"or}Y/%1c1Yn֭[g{ ~W଩ϼUC?ۦC] q@-+^c9?ß#G6G`v;iԢ3^i0wSȷP1c1ces߿?_ n`Uhۮ ڴmf͛AUUVF`xzy r9Z Y& va&4w!ZZ넹8j5.N0[( 6#AvC1c1+eDYdoP}IP@7Pr:ʤtZ7> r=zH8# X;*F'@BZLi0Z4T?1)c1c 9| 3gEXpZg:شx[wS!|4L89q &"ҼRH -w@ S1c1XXܿ`ŊOm-U+W-sgM:WZ1d@a6h7t dǔfasAe+nr%R+P83 ib1c1ƘEȯM`TVZVO RP`ݺ?1>;:G^XV-6g'ͧ~1c1c* 76hDJ1~D3o sn;smA7+trN VA'HIcf IDAT*,*A2Ԉ5 @>c1c1Vz,ZIIL[tMWB_KV @ch!YSH:IFȠ]&-FġG|c1c1VMY!x{KP1@:o5QUhS`SDkkG{!ヌv.YY9bѦf(&V9c1c=w%vԋKf۵o,~B[W!}0 ;%}[;!4DZ5Z5 Kq$h~mۊO۠޾ߩlsp>s/e xLn?c1c5i% m2-詽pbv8&>Z ?Ƣylc%tOtœ@J(޷>~I1}; ?͘1c1+s!3 bXd)FNB #*z\[OTY ŦEVR,KFn~U\l9hc1c)^Cx )1R8q%?SGh[ݒc1c,f1c1+8xc1c1Vq1c1c c1c1X)c1cR71c1coc1c1J17 ճȉOD0c1cLޜ9w"'0c1c)c1cR71c1coc1c1J10c1cba1c1+d%cL!>ɜ ΣLEbJ&'xZ4$=a2}l%EwNc1F;JP O.J2xTJnQʄ zg?FpGU^f,!'"NFd+4ڹÿj6 ENVݶ> UܪpL4zn$4cdc@qOp?=R{WxFƨ?,V(w}"[dMcGNj#(e&-9-:g8JY 8u^, ֦>| ttӱt[:;N&-++-oL>uM8 c1ƌ X&zֵTMW9/OցZ 03Uߺbfd}".e#9>Ц'Z^+ݤ[B՘:,{Z*AV1s! L*F*PiR۷_p-])\>)"VMIK Q&Hghh8Yjߋob~&|/Ư5>X{VԳ{HqO*d ؖQ%N:Ie%Eŷbe_w 1L_o7Gl+v]5QVM&2y's$Bx+2c(j\wW"q%CYQCNSq,X *-ڃk_1e<؅-%ĶZyE|ޞ/Bs'{oNm꣒ d9{ v;va`ׯ;rH PoO;ٕ 8@k@8*q~*,m_q|VMhzG,5ĸߠӃ_: I䆺ojpv2,ovbQT._TqOH -*< 9i*b\Ñ spd/a ,Ŕ-¨1LzKKc19d&yECg1,^b'](K)KywOX4CB-u_^$Ckۓ Hɻ:i0ƊQL-Hy9 u%wI 6h inv$@Bg,oSJ#ې\,[&lbfr$>_&~d(GoC!lkѸt{ǤX1Gh)7C\Z;f_sr@CGZ`Jʦ#j\ۛ(%}$n~IR܀>(3V?KׇB>$ϯ9e{%@t,c1f5/Λk7ê!=g\"vE.JًaAǎ~1]h2q1'#CcuмM(*:F2q;*)8W)>:UI` R\ѸrrxЖPR$!DbnCRTk >IH;kIGT.2qB4Ha[&<F$Hq6w̘1 gEZWƙxH _g^ťKw xW RNA8Au3\EZpL[n%"Wuel(r~&䥹B{l;or3ݸX\=)4SBi6)xمͣ8 ;Y$@J>0Ri]o' jʬıJCwVFd!i3[V>% ٶMן<c?^SZ`5D}c;xDѫE446W髣&J^pmm&.E|_Ei88#^W?m v;#;bjoT^ ]cY(@pGס} "u lN*R@ i.ޮnb( G9gc8uli:ކݓq^p Ϋ)`ݶػg6,;i8 r<g@uY0N_%KcZ*(j~ Ц`Y8o0@V;Tq4,T:/ȐFG7F ^#G.ջQ2>?D{ ;6m߁˧cP#';4};{^Eq[PCB HQª *uU\;bYw+(+Az $@HHB~3s;3o99KXKWAI~F7շ+oi,ghEG z_]R@\)RKXv_w)[ߨ-C:ffM|> XOr{/~94{]q+y vjXaJiE[w"8ԝi߯CvS^Mv*yC<)26T6;i{.\YT管UrnSt}eQ^ݣkROR;5+hB[Z|XTϮ+F[+9nvRnEt{fS 5iVI94ŗ4#?L7O]}'NMn5=:<+<0FԴ!~gE@:g>Ľ_6xfev?aT ʶ7W"hJh^wo4V78toIp,YyHtPGvPCkb>R|U;>Z3m*V)cFoѐQ~QZ =oU*k?v-;A OܵK( u󛫟RS!ŝ^O5]uNLmZDy0Q7+5x?5zEQ/:=BOeD uR!Dkx+ZΪrWɻnpVʔ-kNz󄨲Qw{yth[(}( {vjҸS7wԘIuh6C >ToRe*RzfVM5b\5W9pWRz%*pT4Mn]ߧ<)thwCn[q%'{IP罫T!ȭNmoRװոu{W=ytҭG>9r \N^=t&sVFC{ %%W$9h'ܬcٟY5fnڤ~Geu֒k.Yrmڢ̳%bTO:˪ ?Z!h`ϝ %''kՂ4JaTfъWk;]lYwlКM5~@+. .՗3{vV҂]dmRXbj%9ک} 9 8^v9њ|:Q쭧ZRn\Nǚ+c3=v9nPR^fMRDn3`2MvȻFnX*Z7ON{R2X?H듕{xlx[UGJ:%b*zxeڮ)8)GHѠ.JYLq&W&wrOn:Jʯm_r%oWrz9Ӗ鷤D+XΦ3-+~y֛qpaO)#w$5V_leoUrRMjGXSWŢ B@(˧sꞽ{ n*?N}4oLez-imӱfܯK+$2mwZTCu%3yЉuצrˢzQVRbkdٔ[)ꊈ-=zQ%NERgIs?7[,MݢC'R߸T3H!-Dʩ*ũm~YIk6w'_Ie]NoVO@k֦ݤfUJʒlJ( ܇qQeUhxFdXmVrq;LK.c P!)7 S{|^-ۣ*>暼p SySV "^X_zX/Nᯫ.!ܔHjɮ>?|<(/N`dqzVݫg&Vî7`u+]O݂%#{e+MGݒQYg XUF5Y&ϱ4HU 7ڶۨa-KRrvMJ^^f WmsquozV5Olsz OHZA_Sjpc˯įR,;+"|}23+S|.Jriٴɚh6ا\9ݝՆOJ28w_.-Bz++vY?~Qs'n/: q<8)G鬇bYY PHry$⊖Ng# ާ+eGΓ'|}bQh }J oqwܩ >peމҸpz&SdDj^{:iEVnҞE>>X^~7E 7ieӇ_#GaQoڪVUQ3EKSdtj^\6>D RPZ5O}%t4 \qixxuFwjU˸xǟ{*#+Z1mRLtb|%92Z6)6n]*N1^4n(JRޛ]fӒ5;t,RVVMz蹡=zknVLt*CY׼9O7_˹m_E?[.K"нV\ ۭwQzώamq*ew`7,܏#-J1чWІSӦJk#mjײ>>#'J1us*&:Jۏl>ߗS:kR^)OU"{yUnoۭ9w=uZԫ@_/l6Y.76)/qrB.<8-(6E^z~{=-?CRѨ[CJ u`C DJjE 8>ZqR7nnmwP(5tWEg|WyMk_EE(ͺ16 }VVIaUo@5Qp:N5>oLoLoLoLoLoLoLoLoLoLoLoLoLoLoLoLoLoLoLoLoLoLoLoLl,Kw|.EkF?-ԓ02sT+oK˷ ,yk|mطWg._N(j۱;Oݨ_жrTZ.g]i۔Wp)Jpn׺]UDDm3cڱv{T'"Bu|=:N-լUx27yVʧV sn׎)#m.Y{*SaJh^gÓSkv*y <ڽlKQO[έT _FxರTVCrXd=gȓXo?e.k;G'>{cx@@3T}뺵a|qG:ߥ϶Zd -E(cP]{t[U+Og?9STgr^ui7חTջFɪO;i-ƷQiR3r+IOz ڢf3Rêj[ڶ&J琦 j+=ӺS3Uqm]~cG7O{) XI=WGӑ4k4=3Cu l#<-s>h;H ώTg"UcrbwJFE~nG?7o}Du -{[OC߶9{95_zߚ ]műCtó-T|@i9o2Gn>CK}q巿,ߩ6J#&hBA=tzQsmrg--R~;Z%ިqj_19H7u%EIΤqQu ?=26:n5SӞ/.I~/cGu2ߪQ7q/{c_5I6>GMneo٠.7Z6a;3Z1\!/=)onլ+TrxLфc 6Z3ݯS_ZyZwJq*7E>+izu'"Mv׫Ͽ;wvq]W)37SQ[N$SǶ>V:/h>bRZjQtgNr>5lZ_ŵ[<(lj޺5+ʪј~稰'Uꥇ&UP6qE]R-m%ک+UMM,yj$˲R֬1OjivJX ^UU )'[9(ټLK]?Nt/CYy9:7M|:S]OQxv`RjfGq:.zA;dQpyN\\)!m.rIv:L%VY%< *RVY !nc K5 ߪm-4CӞz{ W7{k2%ߺju}/imUne 7~ד<$Cv]^lԫN9=r;#&ܥ:qV%ɐWf*q)xcoeEnD>>_s~?5$OK^{XɒѽOv9A||5|qo IneefܪyMѾjqDLi/Kr<.eG9 EExr]sfG.ٕW&>^єeOjjR<ǑG Miz8T_E!:W\`ʢHe(q: FJ;S6(7RvYڽk\2Pu*[ʄeQꜙ!Xa MdDF55k=薻HdTSXUs'.  |m#4mpeDAC0}5cNButw-.5Gi{;ȏ CVj6Wtk5UEoܧ^$7<}Us_h+泧4x0Ϳj[|;G-X͙ޙ%I, uh{]|V^vmkD+ߋ~Ip=1K,M&{7RgdC)7ȸyںal0{cF<mNՒ:/멗o kohh --help` to see the arguments to any of the commands. ## Solaar settings Solaar supports at least the following settings: Setting | Description ---------------------------------|------------ Hand Detection | Turn on backlight when your hands hover over the keyboard Scroll Wheel Smooth Scrolling | Higher-speed vertical scrolling Side Scrolling | When off, side scrolling sends custom button events Scroll Wheel High Resolution | Higher-speed vertical scrolling Scroll Wheel HID++ Scrolling | When on, vertical scrolling uses HID++ events Scroll Wheel Direction | Reverse direction of vertical scrolling Scroll Wheel Resolution | Higher-speed vertical scrolling (use with caution) Scroll Wheel Rachet | Shift wheel ratchet on and off based on wheel speed Thumb Wheel HID++ Scrolling | When on, thumb-wheel scrolling uses HID++ events Thumb Wheel Direction | Reverse direction of thumb-wheel scrolling Sensitivity (DPI) | Mouse movement sensitivity Sensitivity (Pointer Speed) | Mouse movement sensitivity Backlight | Turn on backlight Swap Fx function | Change Fn keys to normally do their special action DPI Sliding Adjustment | Change Sensitivity (DPI) by holding a button and moving the mouse Mouse Gestures | Create HID++ events by holding a button and moving the mouse Key/Button Actions | Change what a key or button does Key/Button Diversion | Divert keys and buttons to create HID++ events Divert crown events | Divert crown actions to create HID++ events Divert G Keys | Divert G keys to create HID++ events Disable keys | Disable one or more keys Set OS | Change keys to match OS Change Host | Connect to a different host Gestures | Turn on and off various (mostly touchpad) gestures Gesture params | Modify parameters for gestures HID++ events are mostly not processed by Linux input drivers. Settings that involve sending HID++ events exist so that these events can be processed by Solaar rules instead of by Linux. Different Logitech devices may implement the same functionality in different ways, thus the different settings that do the same thing. Solaar-1.1.14/docs/usb.ids.txt000066400000000000000000000276231473525213700161020ustar00rootroot00000000000000# # List of USB ID's # # Maintained by Stephen J. Gowdy # If you have any new entries, please submit them via # http://www.linux-usb.org/usb-ids.html # or send entries as patches (diff -u old new) in the # body of your email (a bot will attempt to deal with it). # The latest version can be obtained from # http://www.linux-usb.org/usb.ids # # Version: 2019.11.05 # Date: 2019-11-05 20:34:06 # # Vendors, devices and interfaces. Please keep sorted. # Syntax: # vendor vendor_name # device device_name <-- single tab # interface interface_name <-- two tabs 046d Logitech, Inc. 0082 Acer Aspire 5672 Webcam 0200 WingMan Extreme Joystick 0203 M2452 Keyboard 0242 Chillstream for Xbox 360 0301 M4848 Mouse 0401 HP PageScan 0402 NEC PageScan 040f Logitech/Storm PageScan 0430 Mic (Cordless) 0801 QuickCam Home 0802 Webcam C200 0804 Webcam C250 0805 Webcam C300 0807 Webcam B500 0808 Webcam C600 0809 Webcam Pro 9000 080a Portable Webcam C905 080f Webcam C120 0810 QuickCam Pro 0819 Webcam C210 081a Webcam C260 081b Webcam C310 081d HD Webcam C510 0820 QuickCam VC 0821 HD Webcam C910 0823 HD Webcam B910 0825 Webcam C270 0826 HD Webcam C525 0828 HD Webcam B990 082b Webcam C170 082c HD Webcam C615 082d HD Pro Webcam C920 0830 QuickClip 0836 B525 HD Webcam 0837 BCC950 ConferenceCam 0840 QuickCam Express 0843 Webcam C930e 0850 QuickCam Web 085c C922 Pro Stream Webcam 0870 QuickCam Express 0890 QuickCam Traveler 0892 OrbiCam 0894 CrystalCam 0895 QuickCam for Dell Notebooks 0896 OrbiCam 0897 QuickCam for Dell Notebooks 0899 QuickCam for Dell Notebooks 089d QuickCam E2500 series 08a0 QuickCam IM 08a1 QuickCam IM with sound 08a2 Labtec Webcam Pro 08a3 QuickCam QuickCam Chat 08a6 QuickCam IM 08a7 QuickCam Image 08a9 Notebook Deluxe 08aa Labtec Notebooks 08ac QuickCam Cool 08ad QuickCam Communicate STX 08ae QuickCam for Notebooks 08af QuickCam Easy/Cool 08b0 QuickCam 3000 Pro [pwc] 08b1 QuickCam Notebook Pro 08b2 QuickCam Pro 4000 08b3 QuickCam Zoom 08b4 QuickCam Zoom 08b5 QuickCam Sphere 08b9 QuickCam IM 08bd Microphone (Pro 4000) 08c0 QuickCam Pro 3000 08c1 QuickCam Fusion 08c2 QuickCam PTZ 08c3 Camera (Notebooks Pro) 08c5 QuickCam Pro 5000 08c6 QuickCam for DELL Notebooks 08c7 QuickCam OEM Cisco VT Camera II 08c9 QuickCam Ultra Vision 08ca Mic (Fusion) 08cb Mic (Notebooks Pro) 08cc Mic (PTZ) 08ce QuickCam Pro 5000 08cf QuickCam UpdateMe 08d0 QuickCam Express 08d7 QuickCam Communicate STX 08d8 QuickCam for Notebook Deluxe 08d9 QuickCam IM/Connect 08da QuickCam Messenger 08dd QuickCam for Notebooks 08e0 QuickCam Express 08e1 Labtec Webcam 08f0 QuickCam Messenger 08f1 QuickCam Express 08f2 Microphone (Messenger) 08f3 QuickCam Express 08f4 Labtec Webcam 08f5 QuickCam Messenger Communicate 08f6 QuickCam Messenger Plus 0900 ClickSmart 310 0901 ClickSmart 510 0903 ClickSmart 820 0905 ClickSmart 820 0910 QuickCam Cordless 0920 QuickCam Express 0921 Labtec Webcam 0922 QuickCam Live 0928 QuickCam Express 0929 Labtec Webcam Pro 092a QuickCam for Notebooks 092b Labtec Webcam Plus 092c QuickCam Chat 092d QuickCam Express / Go 092e QuickCam Chat 092f QuickCam Express Plus 0950 Pocket Camera 0960 ClickSmart 420 0970 Pocket750 0990 QuickCam Pro 9000 0991 QuickCam Pro for Notebooks 0992 QuickCam Communicate Deluxe 0994 QuickCam Orbit/Sphere AF 09a1 QuickCam Communicate MP/S5500 09a2 QuickCam Communicate Deluxe/S7500 09a4 QuickCam E 3500 09a5 Quickcam 3000 For Business 09a6 QuickCam Vision Pro 09b0 Acer OrbiCam 09b2 Fujitsu Webcam 09c0 QuickCam for Dell Notebooks Mic 09c1 QuickCam Deluxe for Notebooks 0a01 USB Headset 0a02 Premium Stereo USB Headset 350 0a03 Logitech USB Microphone 0a04 V20 portable speakers (USB powered) 0a07 Z-10 Speakers 0a0b ClearChat Pro USB 0a0c Clear Chat Comfort USB Headset 0a13 Z-5 Speakers 0a14 USB Headset 0a15 G35 Headset 0a17 G330 Headset 0a1f G930 0a29 H600 [Wireless Headset] 0a37 USB Headset H540 0a38 Headset H340 0a44 Headset H390 0a45 960 Headset 0a4d G430 Surround Sound Gaming Headset 0a5b G933 Wireless Headset Dongle 0a66 [G533 Wireless Headset Dongle] 0b02 C-UV35 [Bluetooth Mini-Receiver] (HID proxy mode) 8801 Video Camera b014 Bluetooth Mouse M336/M337/M535 b305 BT Mini-Receiver bfe4 Premium Optical Wheel Mouse c000 N43 [Pilot Mouse] c001 N48/M-BB48/M-UK96A [FirstMouse Plus] c002 M-BA47 [MouseMan Plus] c003 MouseMan c004 WingMan Gaming Mouse c005 WingMan Gaming Wheel Mouse c00b MouseMan Wheel c00c Optical Wheel Mouse c00d MouseMan Wheel+ c00e M-BJ58/M-BJ69 Optical Wheel Mouse c00f MouseMan Traveler/Mobile c011 Optical MouseMan c012 Mouseman Dual Optical c014 Corded Workstation Mouse c015 Corded Workstation Mouse c016 Optical Wheel Mouse c018 Optical Wheel Mouse c019 Optical Tilt Wheel Mouse c01a M-BQ85 Optical Wheel Mouse c01b MX310 Optical Mouse c01c Optical Mouse c01d MX510 Optical Mouse c01e MX518 Optical Mouse c024 MX300 Optical Mouse c025 MX500 Optical Mouse c030 iFeel Mouse c031 iFeel Mouse+ c032 MouseMan iFeel c033 iFeel MouseMan+ c034 MouseMan Optical c035 Mouse c036 Mouse c037 Mouse c038 Mouse c03d M-BT96a Pilot Optical Mouse c03e Premium Optical Wheel Mouse (M-BT58) c03f M-BT85 [UltraX Optical Mouse] c040 Corded Tilt-Wheel Mouse c041 G5 Laser Mouse c042 G3 Laser Mouse c043 MX320/MX400 Laser Mouse c044 LX3 Optical Mouse c045 Optical Mouse c046 RX1000 Laser Mouse c047 Laser Mouse M-UAL120 c048 G9 Laser Mouse c049 G5 Laser Mouse c050 RX 250 Optical Mouse c051 G3 (MX518) Optical Mouse c053 Laser Mouse c054 Bluetooth mini-receiver c058 M115 Mouse c05a M90/M100 Optical Mouse c05b M-U0004 810-001317 [B110 Optical USB Mouse] c05d Optical Mouse c05f M115 Optical Mouse c061 RX1500 Laser Mouse c062 M-UAS144 [LS1 Laser Mouse] c063 DELL Laser Mouse c064 M110 corded optical mouse (M-B0001) c066 G9x Laser Mouse c068 G500 Laser Mouse c069 M-U0007 [Corded Mouse M500] c06a USB Optical Mouse c06b G700 Wireless Gaming Mouse c06c Optical Mouse c077 M105 Optical Mouse c07c M-R0017 [G700s Rechargeable Gaming Mouse] c07d G502 Mouse c07e G402 Gaming Mouse c080 G303 Gaming Mouse c083 G403 Prodigy Gaming Mouse c084 G203 Gaming Mouse c101 UltraX Media Remote c110 Harmony 785/880/885 Remote c111 Harmony 525 Remote c112 Harmony 890 Remote c11f Harmony 900/1100 Remote c121 Harmony One Remote c122 Harmony 650/700 Remote c124 Harmony 300/350 Remote c125 Harmony 200 Remote c126 Harmony Link c129 Harmony Hub c12b Harmony Touch/Ultimate Remote c201 WingMan Extreme Joystick with Throttle c202 WingMan Formula c207 WingMan Extreme Digital 3D c208 WingMan Gamepad Extreme c209 WingMan Gamepad c20a WingMan RumblePad c20b WingMan Action Pad c20c WingMan Precision c20d WingMan Attack 2 c20e WingMan Formula GP c211 iTouch Cordless Receiver c212 WingMan Extreme Digital 3D c213 J-UH16 (Freedom 2.4 Cordless Joystick) c214 ATK3 (Attack III Joystick) c215 Extreme 3D Pro c216 F310 Gamepad [DirectInput Mode] c218 F510 Gamepad [DirectInput Mode] c219 F710 Gamepad [DirectInput Mode] c21a Precision Gamepad c21c G13 Advanced Gameboard c21d F310 Gamepad [XInput Mode] c21e F510 Gamepad [XInput Mode] c21f F710 Wireless Gamepad [XInput Mode] c221 G11/G15 Keyboard / Keyboard c222 G15 Keyboard / LCD c223 G11/G15 Keyboard / USB Hub c225 G11/G15 Keyboard / G keys c226 G15 Refresh Keyboard c227 G15 Refresh Keyboard c228 G19 Gaming Keyboard c229 G19 Gaming Keyboard Macro Interface c22a Gaming Keyboard G110 c22b Gaming Keyboard G110 G-keys c22d G510 Gaming Keyboard c22e G510 Gaming Keyboard onboard audio c231 G13 Virtual Mouse c245 G400 Optical Mouse c246 Gaming Mouse G300 c248 G105 Gaming Keyboard c24a G600 Gaming Mouse c24c G400s Optical Mouse c24d G710 Gaming Keyboard c24e G500s Laser Gaming Mouse c281 WingMan Force c283 WingMan Force 3D c285 WingMan Strike Force 3D c286 Force 3D Pro c287 Flight System G940 c291 WingMan Formula Force c293 WingMan Formula Force GP c294 Driving Force c295 Momo Force Steering Wheel c298 Driving Force Pro c299 G25 Racing Wheel c29b G27 Racing Wheel c29c Speed Force Wireless Wheel for Wii c2a0 Wingman Force Feedback Mouse c2a1 WingMan Force Feedback Mouse c2ab G13 Joystick c301 iTouch Keyboard c302 iTouch Pro Keyboard c303 iTouch Keyboard c305 Internet Keyboard c307 Internet Keyboard c308 Internet Navigator Keyboard c309 Y-BF37 [Internet Navigator Keyboard] c30a iTouch Composite c30b NetPlay Keyboard c30c Internet Keys (X) c30d Internet Keys c30e UltraX Keyboard (Y-BL49) c30f Logicool HID-Compliant Keyboard (106 key) c311 Y-UF49 [Internet Pro Keyboard] c312 DeLuxe 250 Keyboard c313 Internet 350 Keyboard c315 Classic Keyboard 200 c316 HID-Compliant Keyboard c317 Wave Corded Keyboard c318 Illuminated Keyboard c31a Comfort Wave 450 c31b Compact Keyboard K300 c31c Keyboard K120 c31d Media Keyboard K200 c31f Comfort Keyboard K290 c326 Washable Keyboard K310 c328 Corded Keyboard K280e c332 G502 Proteus Spectrum Optical Mouse c335 G910 Orion Spectrum Mechanical Keyboard c33a G413 Gaming Keyboard c401 TrackMan Marble Wheel c402 Marble Mouse (2-button) c403 Turbo TrackMan Marble FX c404 TrackMan Wheel c408 Marble Mouse (4-button) c501 Cordless Mouse Receiver c502 Cordless Mouse & iTouch Keys c503 Cordless Mouse+Keyboard Receiver c504 Cordless Mouse+Keyboard Receiver c505 Cordless Mouse+Keyboard Receiver c506 MX700 Cordless Mouse Receiver c508 Cordless Trackball c509 Cordless Keyboard & Mouse c50a Cordless Mouse c50b Cordless Desktop Optical c50c Cordless Desktop S510 c50d Cordless Mouse c50e Cordless Mouse Receiver c510 Cordless Mouse c512 LX-700 Cordless Desktop Receiver c513 MX3000 Cordless Desktop Receiver c514 Cordless Mouse c515 Cordless 2.4 GHz Presenter Presentation remote control c517 LX710 Cordless Desktop Laser c518 MX610 Laser Cordless Mouse c51a MX Revolution/G7 Cordless Mouse c51b V220 Cordless Optical Mouse for Notebooks c521 Cordless Mouse Receiver c525 MX Revolution Cordless Mouse c526 Nano Receiver c529 Logitech Keyboard + Mice c52b Unifying Receiver c52d R700 Remote Presenter receiver c52e MK260 Wireless Combo Receiver c52f Unifying Receiver c531 C-U0007 [Unifying Receiver] c532 Unifying Receiver c534 Unifying Receiver c603 3Dconnexion Spacemouse Plus XT c605 3Dconnexion CADman c606 3Dconnexion Spacemouse Classic c621 3Dconnexion Spaceball 5000 c623 3Dconnexion Space Traveller 3D Mouse c625 3Dconnexion Space Pilot 3D Mouse c626 3Dconnexion Space Navigator 3D Mouse c627 3Dconnexion Space Explorer 3D Mouse c628 3Dconnexion Space Navigator for Notebooks c629 3Dconnexion SpacePilot Pro 3D Mouse c62b 3Dconnexion Space Mouse Pro c640 NuLOOQ navigator c702 Cordless Presenter c703 Elite Keyboard Y-RP20 + Mouse MX900 (Bluetooth) c704 diNovo Wireless Desktop c705 MX900 Bluetooth Wireless Hub (C-UJ16A) c707 Bluetooth wireless hub c708 Bluetooth wireless hub c709 BT Mini-Receiver (HCI mode) c70a MX5000 Cordless Desktop c70b BT Mini-Receiver (HID proxy mode) c70c BT Mini-Receiver (HID proxy mode) c70d Bluetooth wireless hub c70e MX1000 Bluetooth Laser Mouse c70f Bluetooth wireless hub c712 Bluetooth wireless hub c714 diNovo Edge Keyboard c715 Bluetooth wireless hub c71a Bluetooth wireless hub c71d Bluetooth wireless hub c71f diNovo Mini Wireless Keyboard c720 Bluetooth wireless hub ca03 MOMO Racing ca04 Formula Vibration Feedback Wheel ca84 Cordless Controller for Xbox ca88 Thunderpad for Xbox ca8a Precision Vibration Feedback Wheel for Xbox caa3 DriveFX Racing Wheel cab1 Cordless Keyboard for Wii HID Receiver d001 QuickCam Pro f301 Controller Solaar-1.1.14/lib/000077500000000000000000000000001473525213700135765ustar00rootroot00000000000000Solaar-1.1.14/lib/hid_parser/000077500000000000000000000000001473525213700157165ustar00rootroot00000000000000Solaar-1.1.14/lib/hid_parser/__init__.py000066400000000000000000001031401473525213700200260ustar00rootroot00000000000000# SPDX-License-Identifier: MIT from __future__ import annotations # noqa:F407 import functools import struct import sys import textwrap import typing import warnings from typing import Any from typing import Dict from typing import Iterable from typing import Iterator from typing import List from typing import Literal from typing import Optional from typing import Sequence from typing import TextIO from typing import Tuple from typing import Union import hid_parser.data __version__ = "0.0.3" class HIDWarning(Warning): pass class HIDComplianceWarning(HIDWarning): pass class HIDReportWarning(HIDWarning): pass class HIDUnsupportedWarning(HIDWarning): pass class Type: MAIN = 0 GLOBAL = 1 LOCAL = 2 class TagMain: INPUT = 0b1000 OUTPUT = 0b1001 FEATURE = 0b1011 COLLECTION = 0b1010 END_COLLECTION = 0b1100 class TagGlobal: USAGE_PAGE = 0b0000 LOGICAL_MINIMUM = 0b0001 LOGICAL_MAXIMUM = 0b0010 PHYSICAL_MINIMUM = 0b0011 PHYSICAL_MAXIMUM = 0b0100 UNIT_EXPONENT = 0b0101 UNIT = 0b0110 REPORT_SIZE = 0b0111 REPORT_ID = 0b1000 REPORT_COUNT = 0b1001 PUSH = 0b1010 POP = 0b1011 class TagLocal: USAGE = 0b0000 USAGE_MINIMUM = 0b0001 USAGE_MAXIMUM = 0b0010 DESIGNATOR_INDEX = 0b0011 DESIGNATOR_MINIMUM = 0b0100 DESIGNATOR_MAXIMUM = 0b0101 STRING_INDEX = 0b0111 STRING_MINIMUM = 0b1000 STRING_MAXIMUM = 0b1001 DELIMITER = 0b1010 def _data_bit_shift(data: Sequence[int], offset: int, length: int) -> Sequence[int]: if not length > 0: raise ValueError(f"Invalid specified length: {length}") left_extra = offset % 8 right_extra = 8 - (offset + length) % 8 start_offset = offset // 8 end_offset = (offset + length - 1) // 8 byte_length = (length - 1) // 8 + 1 if not end_offset < len(data): raise ValueError(f"Invalid data length: {len(data)} (expecting {end_offset + 1})") shifted = [0] * byte_length if right_extra == 8: right_extra = 0 i = end_offset shifted_offset = byte_length - 1 while shifted_offset >= 0: shifted[shifted_offset] = data[i] >> right_extra if i - start_offset >= 0: shifted[shifted_offset] |= (data[i - 1] & (0xFF >> (8 - right_extra))) << (8 - right_extra) shifted_offset -= 1 i -= 1 shifted[0] &= 0xFF >> ((left_extra + right_extra) % 8) if not len(shifted) == byte_length: raise ValueError("Invalid data") return shifted class BitNumber(int): def __init__(self, value: int): self._value = value def __int__(self) -> int: return self._value def __eq__(self, other: Any) -> bool: try: return self._value == int(other) except: # noqa: E722 return False @property def byte(self) -> int: """ Number of bytes """ return self._value // 8 @property def bit(self) -> int: """ Number of unaligned bits n.byte * 8 + n.bits = n """ if self.byte == 0: return self._value return self._value % (self.byte * 8) @staticmethod def _param_repr(value: int, unit: str) -> str: if value != 1: unit += "s" return f"{value}{unit}" def __repr__(self) -> str: byte_str = self._param_repr(self.byte, "byte") bit_str = self._param_repr(self.bit, "bit") if self.byte == 0 and self.bit == 0: return bit_str parts = [] if self.byte != 0: parts.append(byte_str) if self.bit != 0: parts.append(bit_str) return " ".join(parts) class Usage: def __init__( self, page: Optional[int] = None, usage: Optional[int] = None, *, extended_usage: Optional[int] = None ) -> None: if extended_usage and page and usage: raise ValueError("You need to specify either the usage page and usage or the extended usage") if extended_usage is not None: self.page = extended_usage >> (2 * 8) self.usage = extended_usage & 0xFFFF elif page is not None and usage is not None: self.page = page self.usage = usage else: raise ValueError("No usage specified") def __int__(self) -> int: return self.page << (2 * 8) | self.usage def __eq__(self, other: Any) -> bool: if not isinstance(other, self.__class__): return False return self.page == other.page and self.usage == other.usage def __hash__(self) -> int: return self.usage << (2 * 8) + self.page def __repr__(self) -> str: try: page_str = hid_parser.data.UsagePages.get_description(self.page) except KeyError: page_str = f"0x{self.page:04x}" usage_str = f"0x{self.usage:04x}" else: try: page = hid_parser.data.UsagePages.get_subdata(self.page) usage_str = page.get_description(self.usage) except (KeyError, ValueError): usage_str = f"0x{self.usage:04x}" return f"Usage(page={page_str}, usage={usage_str})" @property def usage_types(self) -> Tuple[hid_parser.data.UsageTypes]: subdata = hid_parser.data.UsagePages.get_subdata(self.page).get_subdata(self.usage) if isinstance(subdata, tuple): types = subdata else: types = (subdata,) for typ in types: if not isinstance(typ, hid_parser.data.UsageTypes): raise ValueError(f"Expecting usage type but got '{type(typ)}'") return typing.cast(Tuple[hid_parser.data.UsageTypes], types) class UsageValue: def __init__(self, item: MainItem, value: int): self._item = item self._value = value def __int__(self) -> int: return self.value def __repr__(self) -> str: return repr(self.value) @property def value(self) -> Union[int, bool]: return self._value @property def constant(self) -> bool: return self._item.constant @property def data(self) -> bool: return self._item.data @property def relative(self) -> bool: return self._item.relative @property def absolute(self) -> bool: return self._item.absolute class VendorUsageValue(UsageValue): def __init__( self, item: MainItem, *, value: Optional[int] = None, value_list: Optional[List[int]] = None, ): self._item = item if value: self._list = [value] elif value_list: self._list = value_list else: self._list = [] def __int__(self) -> int: return self.value def __iter__(self) -> Iterator[int]: return iter(self.list) @property def value(self) -> Union[int, bool]: return int.from_bytes(self._list, byteorder="little") @property def list(self) -> List[int]: return self._list class BaseItem: def __init__(self, offset: int, size: int): self._offset = BitNumber(offset) self._size = BitNumber(size) @property def offset(self) -> BitNumber: return self._offset @property def size(self) -> BitNumber: return self._size def __repr__(self) -> str: return f"{self.__class__.__name__}(offset={self.offset}, size={self.size})" class PaddingItem(BaseItem): pass class MainItem(BaseItem): def __init__( self, offset: int, size: int, flags: int, logical_min: int, logical_max: int, physical_min: Optional[int] = None, physical_max: Optional[int] = None, ): super().__init__(offset, size) self._flags = flags self._logical_min = logical_min self._logical_max = logical_max self._physical_min = physical_min self._physical_max = physical_max # TODO: unit @property def offset(self) -> BitNumber: return self._offset @property def size(self) -> BitNumber: return self._size @property def logical_min(self) -> int: return self._logical_min @property def logical_max(self) -> int: return self._logical_max @property def physical_min(self) -> Optional[int]: return self._physical_min @property def physical_max(self) -> Optional[int]: return self._physical_max # flags @property def constant(self) -> bool: return self._flags & (1 << 0) != 0 @property def data(self) -> bool: return self._flags & (1 << 0) == 0 @property def relative(self) -> bool: return self._flags & (1 << 2) != 0 @property def absolute(self) -> bool: return self._flags & (1 << 2) == 0 class VariableItem(MainItem): _INCOMPATIBLE_TYPES = ( # array types hid_parser.data.UsageTypes.SELECTOR, # collection types hid_parser.data.UsageTypes.NAMED_ARRAY, hid_parser.data.UsageTypes.COLLECTION_APPLICATION, hid_parser.data.UsageTypes.COLLECTION_LOGICAL, hid_parser.data.UsageTypes.COLLECTION_PHYSICAL, hid_parser.data.UsageTypes.USAGE_SWITCH, hid_parser.data.UsageTypes.USAGE_MODIFIER, ) def __init__( self, offset: int, size: int, flags: int, usage: Usage, logical_min: int, logical_max: int, physical_min: Optional[int] = None, physical_max: Optional[int] = None, ): super().__init__(offset, size, flags, logical_min, logical_max, physical_min, physical_max) self._usage = usage try: if all(usage_type in self._INCOMPATIBLE_TYPES for usage_type in usage.usage_types): warnings.warn(HIDComplianceWarning(f"{usage} has no compatible usage types with a variable item")) # noqa except (KeyError, ValueError): pass def __repr__(self) -> str: return f"VariableItem(offset={self.offset}, size={self.size}, usage={self.usage})" def parse(self, data: Sequence[int]) -> UsageValue: data = _data_bit_shift(data, self.offset, self.size) if hid_parser.data.UsageTypes.LINEAR_CONTROL in self.usage.usage_types or any( usage_type in hid_parser.data.UsageTypesData and usage_type != hid_parser.data.UsageTypes.SELECTOR for usage_type in self.usage.usage_types ): # int value = int.from_bytes(data, byteorder="little") elif ( hid_parser.data.UsageTypes.ON_OFF_CONTROL in self.usage.usage_types and not self.preferred_state and self.logical_min == -1 and self.logical_max == 1 ): # bool - -1 is false value = int.from_bytes(data, byteorder="little") == 1 else: # bool value = bool.from_bytes(data, byteorder="little") return UsageValue(self, value) @property def usage(self) -> Usage: return self._usage # flags (variable only, see HID spec 1.11 page 32) @property def wrap(self) -> bool: return self._flags & (1 << 3) != 0 @property def linear(self) -> bool: return self._flags & (1 << 4) != 0 @property def preferred_state(self) -> bool: return self._flags & (1 << 5) != 0 @property def null_state(self) -> bool: return self._flags & (1 << 6) != 0 @property def buffered_bytes(self) -> bool: return self._flags & (1 << 7) != 0 @property def bitfield(self) -> bool: return self._flags & (1 << 7) == 0 class ArrayItem(MainItem): _INCOMPATIBLE_TYPES = ( # variable types hid_parser.data.UsageTypes.LINEAR_CONTROL, hid_parser.data.UsageTypes.ON_OFF_CONTROL, hid_parser.data.UsageTypes.MOMENTARY_CONTROL, hid_parser.data.UsageTypes.ONE_SHOT_CONTROL, hid_parser.data.UsageTypes.RE_TRIGGER_CONTROL, hid_parser.data.UsageTypes.STATIC_VALUE, hid_parser.data.UsageTypes.STATIC_FLAG, hid_parser.data.UsageTypes.DYNAMIC_VALUE, hid_parser.data.UsageTypes.DYNAMIC_FLAG, # collection types hid_parser.data.UsageTypes.NAMED_ARRAY, hid_parser.data.UsageTypes.COLLECTION_APPLICATION, hid_parser.data.UsageTypes.COLLECTION_LOGICAL, hid_parser.data.UsageTypes.COLLECTION_PHYSICAL, hid_parser.data.UsageTypes.USAGE_SWITCH, hid_parser.data.UsageTypes.USAGE_MODIFIER, ) _IGNORE_USAGE_VALUES = ((hid_parser.data.UsagePages.KEYBOARD_KEYPAD_PAGE, hid_parser.data.KeyboardKeypad.NO_EVENT),) def __init__( self, offset: int, size: int, count: int, flags: int, usages: List[Usage], logical_min: int, logical_max: int, physical_min: Optional[int] = None, physical_max: Optional[int] = None, ): super().__init__(offset, size, flags, logical_min, logical_max, physical_min, physical_max) self._count = count self._usages = usages self._page = self._usages[0].page if usages else None for usage in self._usages: if usage.page != self._page: raise ValueError(f"Mismatching usage page in usage: {usage} (expecting {self._usages[0]})") try: if all(usage_type in self._INCOMPATIBLE_TYPES for usage_type in usage.usage_types): warnings.warn(HIDComplianceWarning(f"{usage} has no compatible usage types with an array item")) # noqa except (KeyError, ValueError): pass self._ignore_usages: List[Usage] = [] for page, usage_id in self._IGNORE_USAGE_VALUES: assert isinstance(page, int) and isinstance(usage_id, int) self._ignore_usages.append(Usage(page, usage_id)) def __repr__(self) -> str: return ( textwrap.dedent( """ ArrayItem( offset={}, size={}, count={}, usages=[ {}, ], ) """ ) .strip() .format( self.offset, self.size, self.count, ",\n ".join(repr(usage) for usage in self.usages), ) ) def parse(self, data: Sequence[int]) -> Dict[Usage, UsageValue]: usage_values: Dict[Usage, UsageValue] = {} for i in range(self.count): aligned_data = _data_bit_shift(data, self.offset + i * 8, self.size) usage = Usage(self._page, int.from_bytes(aligned_data, byteorder="little")) if usage in self._ignore_usages: continue # vendor usages don't have usage any standard type - just save the raw data if usage.page in hid_parser.data.UsagePages.VENDOR_PAGE: if usage not in usage_values: usage_values[usage] = VendorUsageValue( self, value=int.from_bytes(aligned_data, byteorder="little"), ) typing.cast(VendorUsageValue, usage_values[usage]).list.append( int.from_bytes(aligned_data, byteorder="little") ) continue not_incompatible_type = all(usage_type not in self._INCOMPATIBLE_TYPES for usage_type in usage.usage_types) if usage in self._usages and not_incompatible_type: usage_values[usage] = UsageValue(self, True) return usage_values @property def count(self) -> int: return self._count @property def usages(self) -> List[Usage]: return self._usages class InvalidReportDescriptor(Exception): pass # report ID (None for no report ID), item list _ITEM_POOL = Dict[Optional[int], List[BaseItem]] class ReportDescriptor: def __init__(self, data: Sequence[int]) -> None: self._data = data for byte in data: if byte < 0 or byte > 255: raise InvalidReportDescriptor( f"A report descriptor should be represented by a list of bytes: found value {byte}" ) self._input: _ITEM_POOL = {} self._output: _ITEM_POOL = {} self._feature: _ITEM_POOL = {} self._parse() @property def data(self) -> Sequence[int]: return self._data @property def input_report_ids(self) -> List[Optional[int]]: return list(self._input.keys()) @property def output_report_ids(self) -> List[Optional[int]]: return list(self._output.keys()) @property def feature_report_ids(self) -> List[Optional[int]]: return list(self._feature.keys()) def _get_report_size(self, items: List[BaseItem]) -> BitNumber: size = 0 for item in items: if isinstance(item, ArrayItem): size += item.size * item.count else: size += item.size return BitNumber(size) def get_input_items(self, report_id: Optional[int] = None) -> List[BaseItem]: return self._input[report_id] @functools.lru_cache(maxsize=16) # noqa def get_input_report_size(self, report_id: Optional[int] = None) -> BitNumber: return self._get_report_size(self.get_input_items(report_id)) def get_output_items(self, report_id: Optional[int] = None) -> List[BaseItem]: return self._output[report_id] @functools.lru_cache(maxsize=16) # noqa def get_output_report_size(self, report_id: Optional[int] = None) -> BitNumber: return self._get_report_size(self.get_output_items(report_id)) def get_feature_items(self, report_id: Optional[int] = None) -> List[BaseItem]: return self._feature[report_id] @functools.lru_cache(maxsize=16) # noqa def get_feature_report_size(self, report_id: Optional[int] = None) -> BitNumber: return self._get_report_size(self.get_feature_items(report_id)) def _parse_report_items(self, items: List[BaseItem], data: Sequence[int]) -> Dict[Usage, UsageValue]: parsed: Dict[Usage, UsageValue] = {} for item in items: if isinstance(item, VariableItem): parsed[item.usage] = item.parse(data) elif isinstance(item, ArrayItem): usage_values = item.parse(data) for usage in usage_values: if usage in parsed: warnings.warn(HIDReportWarning(f"Overriding usage: {usage}")) # noqa parsed.update(usage_values) elif isinstance(item, PaddingItem): pass else: raise TypeError(f"Unknown item: {item}") return parsed def _parse_report(self, item_poll: _ITEM_POOL, data: Sequence[int]) -> Dict[Usage, UsageValue]: if None in item_poll: # unnumbered reports return self._parse_report_items(item_poll[None], data) else: # numbered reports return self._parse_report_items(item_poll[data[0]], data[1:]) def parse_input_report(self, data: Sequence[int]) -> Dict[Usage, UsageValue]: return self._parse_report(self._input, data) def parse_output_report(self, data: Sequence[int]) -> Dict[Usage, UsageValue]: return self._parse_report(self._output, data) def parse_feature_report(self, data: Sequence[int]) -> Dict[Usage, UsageValue]: return self._parse_report(self._feature, data) def _iterate_raw(self) -> Iterable[Tuple[int, int, Optional[int]]]: i = 0 while i < len(self.data): prefix = self.data[i] tag = (prefix & 0b11110000) >> 4 typ = (prefix & 0b00001100) >> 2 size = prefix & 0b00000011 if size == 3: # 6.2.2.2 size = 4 if size == 0: data = None elif size == 1: if i + 1 >= len(self.data): raise InvalidReportDescriptor(f"Invalid size: expecting >={i + 1}, got {len(self.data)}") data = self.data[i + 1] else: if i + 1 + size >= len(self.data): raise InvalidReportDescriptor(f"Invalid size: expecting >={i + 1 + size}, got {len(self.data)}") if size == 2: pack_type = "H" elif size == 4: pack_type = "L" else: raise ValueError(f"Invalid item size: {size}") data = struct.unpack(f"<{pack_type}", bytes(self.data[i + 1 : i + 1 + size]))[0] yield typ, tag, data i += size + 1 def _append_item( self, offset_list: Dict[Optional[int], int], pool: _ITEM_POOL, report_id: Optional[int], item: BaseItem, ) -> None: offset_list[report_id] += item.size if report_id in pool: pool[report_id].append(item) else: pool[report_id] = [item] def _append_items( self, offset_list: Dict[Optional[int], int], pool: _ITEM_POOL, report_id: Optional[int], report_count: int, report_size: int, usages: List[Usage], flags: int, data: Dict[str, Any], ) -> None: item: BaseItem is_array = flags & (1 << 1) == 0 # otherwise variable """ HID 1.11, 6.2.2.9 says reports can be byte aligned by declaring a main item without usage. A main item can have multiple usages, as I interpret it, items are only considered padding when they have NO usages. """ if len(usages) == 0 or not usages: for _ in range(report_count): item = PaddingItem(offset_list[report_id], report_size) self._append_item(offset_list, pool, report_id, item) return if is_array: item = ArrayItem( offset=offset_list[report_id], size=report_size, usages=usages, count=report_count, flags=flags, **data, ) self._append_item(offset_list, pool, report_id, item) else: if len(usages) != report_count: error_str = f"Expecting {report_count} usages but got {len(usages)}" if len(usages) == 1: warnings.warn(HIDComplianceWarning(error_str)) # noqa usages *= report_count else: raise InvalidReportDescriptor(error_str) for usage in usages: item = VariableItem( offset=offset_list[report_id], size=report_size, usage=usage, flags=flags, **data, ) self._append_item(offset_list, pool, report_id, item) def _parse(self, level: int = 0, file: TextIO = sys.stdout) -> None: # noqa: C901 offset_input: Dict[Optional[int], int] = { None: 0, } offset_output: Dict[Optional[int], int] = { None: 0, } offset_feature: Dict[Optional[int], int] = { None: 0, } report_id: Optional[int] = None report_count: Optional[int] = None report_size: Optional[int] = None usage_page: Optional[int] = None usages: List[Usage] = [] usage_min: Optional[int] = None glob: Dict[str, Any] = {} local: Dict[str, Any] = {} for typ, tag, data in self._iterate_raw(): if typ == Type.MAIN: if tag in (TagMain.COLLECTION, TagMain.END_COLLECTION): usages = [] # we only care about input, output and features for now if tag not in (TagMain.INPUT, TagMain.OUTPUT, TagMain.FEATURE): continue if report_count is None: raise InvalidReportDescriptor("Trying to append an item but no report count given") if report_size is None: raise InvalidReportDescriptor("Trying to append an item but no report size given") if tag == TagMain.INPUT: if data is None: raise InvalidReportDescriptor("Invalid input item") self._append_items( offset_input, self._input, report_id, report_count, report_size, usages, data, {**glob, **local} ) elif tag == TagMain.OUTPUT: if data is None: raise InvalidReportDescriptor("Invalid output item") self._append_items( offset_output, self._output, report_id, report_count, report_size, usages, data, {**glob, **local}, ) elif tag == TagMain.FEATURE: if data is None: raise InvalidReportDescriptor("Invalid feature item") self._append_items( offset_feature, self._feature, report_id, report_count, report_size, usages, data, {**glob, **local}, ) # clear local usages = [] usage_min = None local = {} # we don't care about collections for now, maybe in the future... elif typ == Type.GLOBAL: if tag == TagGlobal.USAGE_PAGE: usage_page = data elif tag == TagGlobal.LOGICAL_MINIMUM: glob["logical_min"] = data elif tag == TagGlobal.LOGICAL_MAXIMUM: glob["logical_max"] = data elif tag == TagGlobal.PHYSICAL_MINIMUM: glob["physical_min"] = data elif tag == TagGlobal.PHYSICAL_MAXIMUM: glob["physical_max"] = data elif tag == TagGlobal.REPORT_SIZE: report_size = data elif tag == TagGlobal.REPORT_ID: if not report_id and (self._input or self._output or self._feature): raise InvalidReportDescriptor("Tried to set a report ID in a report that does not use them") report_id = data # initialize the item offset for this report ID for offset_list in (offset_input, offset_output, offset_feature): if report_id not in offset_list: offset_list[report_id] = 0 elif tag in (TagGlobal.UNIT, TagGlobal.UNIT_EXPONENT): warnings.warn( # noqa HIDUnsupportedWarning("Data specifies a unit or unit exponent, but we don't support those yet") ) elif tag in (TagGlobal.PUSH, TagGlobal.POP): warnings.warn(HIDUnsupportedWarning("Push and pop are not supported yet")) # noqa elif tag == TagGlobal.REPORT_COUNT: report_count = data else: raise NotImplementedError(f"Unsupported global tag: {bin(tag)}") elif typ == Type.LOCAL: if tag == TagLocal.USAGE: if usage_page is None: raise InvalidReportDescriptor("Usage field found but no usage page") usages.append(Usage(usage_page, data)) elif tag == TagLocal.USAGE_MINIMUM: usage_min = data elif tag == TagLocal.USAGE_MAXIMUM: if usage_min is None: raise InvalidReportDescriptor("Usage maximum set but no usage minimum") if data is None: raise InvalidReportDescriptor("Invalid usage maximum value") for i in range(usage_min, data + 1): usages.append(Usage(usage_page, i)) usage_min = None elif tag in (TagLocal.STRING_INDEX, TagLocal.STRING_MINIMUM, TagLocal.STRING_MAXIMUM): pass # we don't care about this information to parse the reports else: raise NotImplementedError(f"Unsupported local tag: {bin(tag)}") @staticmethod def _get_main_item_desc(value: int) -> str: fields = [ "Constant" if value & (1 << 0) else "Data", "Variable" if value & (1 << 1) else "Array", "Relative" if value & (1 << 2) else "Absolute", ] if value & (1 << 1): # variable only fields += [ "Wrap" if value & (1 << 3) else "No Wrap", "Non Linear" if value & (1 << 4) else "Linear", "No Preferred State" if value & (1 << 5) else "Preferred State", "Null State" if value & (1 << 6) else "No Null position", "Buffered Bytes" if value & (1 << 8) else "Bit Field", ] return ", ".join(fields) def print(self, level: int = 0, file: TextIO = sys.stdout) -> None: # noqa: C901 def printl(string: str) -> None: print(" " * level + string, file=file) usage_data: Union[Literal[False], Optional[hid_parser.data._Data]] = False for typ, tag, data in self._iterate_raw(): if typ == Type.MAIN: if tag == TagMain.INPUT: if data is None: raise InvalidReportDescriptor("Invalid input item") printl(f"Input ({self._get_main_item_desc(data)})") elif tag == TagMain.OUTPUT: if data is None: raise InvalidReportDescriptor("Invalid output item") printl(f"Output ({self._get_main_item_desc(data)})") elif tag == TagMain.FEATURE: if data is None: raise InvalidReportDescriptor("Invalid feature item") printl(f"Feature ({self._get_main_item_desc(data)})") elif tag == TagMain.COLLECTION: printl(f"Collection ({hid_parser.data.Collections.get_description(data)})") level += 1 elif tag == TagMain.END_COLLECTION: level -= 1 printl("End Collection") elif typ == Type.GLOBAL: if tag == TagGlobal.USAGE_PAGE: try: printl(f"Usage Page ({hid_parser.data.UsagePages.get_description(data)})") try: usage_data = hid_parser.data.UsagePages.get_subdata(data) except ValueError: usage_data = None except KeyError: printl(f"Usage Page (Unknown 0x{data:04x})") elif tag == TagGlobal.LOGICAL_MINIMUM: printl(f"Logical Minimum ({data})") elif tag == TagGlobal.LOGICAL_MAXIMUM: printl(f"Logical Maximum ({data})") elif tag == TagGlobal.PHYSICAL_MINIMUM: printl(f"Physical Minimum ({data})") elif tag == TagGlobal.PHYSICAL_MAXIMUM: printl(f"Physical Maximum ({data})") elif tag == TagGlobal.UNIT_EXPONENT: printl(f"Unit Exponent (0x{data:04x})") elif tag == TagGlobal.UNIT: printl(f"Unit (0x{data:04x})") elif tag == TagGlobal.REPORT_SIZE: printl(f"Report Size ({data})") elif tag == TagGlobal.REPORT_ID: printl(f"Report ID (0x{data:02x})") elif tag == TagGlobal.REPORT_COUNT: printl(f"Report Count ({data})") elif tag == TagGlobal.PUSH: printl(f"Push ({data})") elif tag == TagGlobal.POP: printl(f"Pop ({data})") elif typ == Type.LOCAL: if tag == TagLocal.USAGE: if usage_data is False: raise InvalidReportDescriptor("Usage field found but no usage page") if usage_data: try: printl(f"Usage ({usage_data.get_description(data)})") except KeyError: printl(f"Usage (Unknown, 0x{data:04x})") else: printl(f"Usage (0x{data:04x})") elif tag == TagLocal.USAGE_MINIMUM: printl(f"Usage Minimum ({data})") elif tag == TagLocal.USAGE_MAXIMUM: printl(f"Usage Maximum ({data})") elif tag == TagLocal.DESIGNATOR_INDEX: printl(f"Designator Index ({data})") elif tag == TagLocal.DESIGNATOR_MINIMUM: printl(f"Designator Minimum ({data})") elif tag == TagLocal.DESIGNATOR_MAXIMUM: printl(f"Designator Maximum ({data})") elif tag == TagLocal.STRING_INDEX: printl(f"String Index ({data})") elif tag == TagLocal.STRING_MINIMUM: printl(f"String Minimum ({data})") elif tag == TagLocal.STRING_MAXIMUM: printl(f"String Maximum ({data})") elif tag == TagLocal.DELIMITER: printl(f"Delemiter ({data})") Solaar-1.1.14/lib/hid_parser/data.py000066400000000000000000001605031473525213700172060ustar00rootroot00000000000000# SPDX-License-Identifier: MIT import enum from typing import Any from typing import Dict from typing import List from typing import Optional from typing import Tuple class _DataMeta(type): """ This metaclass populates _single and _range, following the structure described bellow The class should declare data as follows MY_DATA_VALUE = 0x01, 'Data description' or, for ranges, MY_DATA_RANGE = 0x02, ..., 0x06, 'Data range description' _single and _range will then be populated with MY_DATA_VALUE = 0x01 _single[0x01] = ('Data description', None) MY_DATA_RANGE = range(0x02, 0x06+1) _range.append(tuple(0x02, 0x06, ('Data range description', None))) As you can see, for single data insertions, the variable will be kept with the first value of the tuple. Both single and range data insertions will register the data into the correspondent data holders. You can also define subdata, MY_DATA_VALUE = 0x01, 'Data description', OTHER_DATA_TYPE MY_DATA_RANGE = 0x02, ..., 0x06, 'Data range description', YET_OTHER_DATA_TYPE Which will result in MY_DATA_VALUE = 0x01 _single[0x01] = ('Data description', OTHER_DATA_TYPE) _range.append(tuple(0x02, 0x06, ('Data range description', YET_OTHER_DATA_TYPE))) This metaclass also does some verification to prevent duplicated data. """ def __new__(mcs, name: str, bases: Tuple[Any], dic: Dict[str, Any]): # type: ignore # noqa: C901 dic["_single"] = {} dic["_range"] = [] # allow constructing data via a data dictionary as opposed to directly in the object body if "data" in dic: data = dic.pop("data") else: data = dic for attr in data: if not attr.startswith("_") and isinstance(data[attr], tuple): if len(data[attr]) == 2 or len(data[attr]) == 4: # missing sub data data[attr] = data[attr] + (None,) if len(data[attr]) == 3: # single num, desc, sub = data[attr] if not isinstance(num, int): raise TypeError(f"First element of '{attr}' should be an int") if not isinstance(desc, str): raise TypeError(f"Second element of '{attr}' should be a string") if num in dic["_single"]: raise ValueError(f"Duplicated value in '{attr}' ({num})") for nmin, nmax, _ in dic["_range"]: if nmin <= num <= nmax: raise ValueError(f"Duplicated value in '{attr}' ({num})") dic[attr] = num dic["_single"][num] = desc, sub elif len(data[attr]) == 5: # range nmin, el, nmax, desc, sub = data[attr] if not el == Ellipsis: raise TypeError(f"Second element of '{attr}' should be an ellipsis (...)") if not isinstance(nmin, int): raise TypeError(f"First element of '{attr}' should be an int") if not isinstance(nmax, int): raise TypeError(f"Third element of '{attr}' should be an int") if not isinstance(desc, str): raise TypeError(f"Fourth element of '{attr}' should be a string") for num in dic["_single"]: if nmin <= num <= nmax: raise ValueError(f"Duplicated value in '{attr}' ({num})") dic[attr] = range(nmin, nmax + 1) dic["_range"].append((nmin, nmax, (desc, sub))) else: raise ValueError(f"Invalid field: {attr}") return super().__new__(mcs, name, bases, dic) class _Data(metaclass=_DataMeta): """ This class provides a get_description method to get data out of _single and _range. See the _DataMeta documentation for more information. """ _DATA = Tuple[str, Optional[Any]] _single: Dict[int, _DATA] _range: List[Tuple[int, int, _DATA]] @classmethod def _get_data(cls, num: Optional[int]) -> _DATA: if num is None: raise KeyError("Data index is not an int") if num in cls._single: return cls._single[num] for nmin, nmax, data in cls._range: if nmin <= num <= nmax: return data raise KeyError(f"Data not found for index 0x{num:02x} in {cls.__name__}") @classmethod def get_description(cls, num: Optional[int]) -> str: return cls._get_data(num)[0] @classmethod def get_subdata(cls, num: Optional[int]) -> Any: subdata = cls._get_data(num)[1] if not subdata: raise ValueError("Sub-data not available") return subdata class UsageTypes(enum.Enum): # controls LINEAR_CONTROL = LC = 0 ON_OFF_CONTROL = OOC = 1 MOMENTARY_CONTROL = MC = 2 ONE_SHOT_CONTROL = OSC = 3 RE_TRIGGER_CONTROL = RTC = 4 # data SELECTOR = SEL = 5 STATIC_VALUE = SV = 6 STATIC_FLAG = SF = 7 DYNAMIC_FLAG = DF = 8 DYNAMIC_VALUE = DV = 9 # collection NAMED_ARRAY = NARY = 10 COLLECTION_APPLICATION = CA = 11 COLLECTION_LOGICAL = CL = 12 COLLECTION_PHYSICAL = CP = 13 USAGE_SWITCH = US = 14 USAGE_MODIFIER = UM = 15 UsageTypesControls = ( UsageTypes.LINEAR_CONTROL, UsageTypes.ON_OFF_CONTROL, UsageTypes.ONE_SHOT_CONTROL, UsageTypes.RE_TRIGGER_CONTROL, ) UsageTypesData = ( UsageTypes.SELECTOR, UsageTypes.STATIC_VALUE, UsageTypes.STATIC_FLAG, UsageTypes.DYNAMIC_VALUE, UsageTypes.DYNAMIC_FLAG, ) UsageTypesCollection = ( UsageTypes.NAMED_ARRAY, UsageTypes.COLLECTION_APPLICATION, UsageTypes.COLLECTION_LOGICAL, UsageTypes.COLLECTION_PHYSICAL, UsageTypes.USAGE_SWITCH, UsageTypes.USAGE_MODIFIER, ) class Collections(_Data): PHYSICAL = 0x00, "Physical" APPLICATION = 0x01, "Application" LOGICAL = 0x02, "Logical" REPORT = 0x03, "Report" NAMED_ARRAY = 0x04, "Named Array" USAGE_SWITCH = 0x05, "Usage Switch" USAGE_MODIFIER = 0x06, "Usage Modifier" VENDOR = 0x80, ..., 0xFF, "Vendor" class GenericDesktopControls(_Data): POINTER = 0x01, "Pointer", UsageTypes.CP MOUSE = 0x02, "Mouse", UsageTypes.CA JOYSTICK = 0x04, "Joystick", UsageTypes.CA GAMEPAD = 0x05, "Game Pad", UsageTypes.CA KEYBOARD = 0x06, "Keyboard", UsageTypes.CA KEYPAD = 0x07, "Keypad", UsageTypes.CA MULTI_AXIS_CONTROLLER = 0x08, "Multi-axis Controller", UsageTypes.CA TABLET_PC_SYSTEM_CONTROLS = 0x09, "Tablet PC System Controls", UsageTypes.CA X = 0x30, "X", UsageTypes.DV Y = 0x31, "Y", UsageTypes.DV Z = 0x32, "Z", UsageTypes.DV RX = 0x33, "Rx", UsageTypes.DV RY = 0x34, "Ry", UsageTypes.DV RX = 0x35, "Rz", UsageTypes.DV SLIDER = 0x36, "Slider", UsageTypes.DV DIAL = 0x37, "Dial", UsageTypes.DV WHEEL = 0x38, "Wheel", UsageTypes.DV HAT_SWITCH = 0x39, "Hat switch", UsageTypes.DV COUNTED_BUFFER = 0x3A, "Counted Buffer", UsageTypes.CL BYTE_COUNT = 0x3B, "Byte Count", UsageTypes.DV MOTION_WAKEUP = 0x3C, "Motion Wakeup", UsageTypes.OSC START = 0x3D, "Start", UsageTypes.OOC SELECT = 0x3E, "Select", UsageTypes.OOC VX = 0x40, "Vx", UsageTypes.DV VY = 0x41, "Vy", UsageTypes.DV VZ = 0x42, "Vz", UsageTypes.DV VBRX = 0x43, "Vbrx", UsageTypes.DV VBRY = 0x44, "Vbry", UsageTypes.DV VBRZ = 0x45, "Vbrz", UsageTypes.DV VNO = 0x46, "Vno", UsageTypes.DV FEATURE_NOTIFICATION = 0x47, "Feature Notification", (UsageTypes.DV, UsageTypes.DF) RESOLUTION_MULTIPLIER = 0x48, "Resolution Multiplier", UsageTypes.DV SYSTEM_CONTROL = 0x80, "System Control", UsageTypes.CA SYSTEM_POWER_CONTROL = 0x81, "System Power Down", UsageTypes.OSC SYSTEM_SLEEP = 0x82, "System Sleep", UsageTypes.OSC SYSTEM_WAKE_UP = 0x83, "System Wake Up", UsageTypes.OSC SYSTEM_CONTEXT_MENU = 0x84, "System Context Menu", UsageTypes.OSC SYSTEM_MAIN_MENU = 0x85, "System Main Menu", UsageTypes.OSC SYSTEM_APP_MENU = 0x86, "System App Menu", UsageTypes.OSC SYSTEM_MENU_HELP = 0x87, "System Menu Help", UsageTypes.OSC SYSTEM_MENU_EXIT = 0x88, "System Menu Exit", UsageTypes.OSC SYSTEM_MENU_SELECT = 0x89, "System Menu Select", UsageTypes.OSC SYSTEM_MENU_RIGHT = 0x8A, "System Menu Right", UsageTypes.RTC SYSTEM_MENU_LEFT = 0x8B, "System Menu Left", UsageTypes.RTC SYSTEM_MENU_UP = 0x8C, "System Menu Up", UsageTypes.RTC SYSTEM_MENU_DOWN = 0x8D, "System Menu Down", UsageTypes.RTC SYSTEM_COLD_RESTART = 0x8E, "System Cold Restart", UsageTypes.OSC SYSTEM_WARM_RESTART = 0x8F, "System Warm Restart", UsageTypes.OSC DPAD_UP = 0x90, "D-pad Up", UsageTypes.OOC DPAD_DOWN = 0x91, "D-pad Down", UsageTypes.OOC DPAD_RIGHT = 0x92, "D-pad Right", UsageTypes.OOC DPAD_LEFT = 0x93, "D-pad Left", UsageTypes.OOC SYSTEM_DOCK = 0xA0, "System Dock", UsageTypes.OSC SYSTEM_UNDOCK = 0xA1, "System Undock", UsageTypes.OSC SYSTEM_SETUP = 0xA2, "System Setup", UsageTypes.OSC SYSTEM_BREAK = 0xA3, "System Break", UsageTypes.OSC SYSTEM_DEBBUGER_BREAK = 0xA4, "System Debugger Break", UsageTypes.OSC APPLICATION_BREAK = 0xA5, "Application Break", UsageTypes.OSC APPLICATION_DEBBUGER_BREAK = 0xA6, "Application Debugger Break", UsageTypes.OSC SYSTEM_SPEAKER_MUTE = 0xA7, "System Speaker Mute", UsageTypes.OSC SYSTEM_HIBERNATE = 0xA8, "System Hibernate", UsageTypes.OSC SYSTEM_DISPLAY_INVERT = 0xB0, "System Display Invert", UsageTypes.OSC SYSTEM_DISPLAY_INTERNAL = 0xB1, "System Display Internal", UsageTypes.OSC SYSTEM_DISPLAY_EXTERNAL = 0xB2, "System Display External", UsageTypes.OSC SYSTEM_DISPLAY_BOTH = 0xB3, "System Display Both", UsageTypes.OSC SYSTEM_DISPLAY_DUAL = 0xB4, "System Display Dual", UsageTypes.OSC SYSTEM_DISPLAY_TOGGLE = 0xB5, "System Display Toggle Int/Ext", UsageTypes.OSC SYSTEM_DISPLAY_SWAP = 0xB6, "System Display Swap Primary/Secondary", UsageTypes.OSC SYSTEM_DISPLAY_LCD_AUTOSCALE = 0xB7, "System Display LCD Autoscale", UsageTypes.OSC class KeyboardKeypad(_Data): NO_EVENT = 0x00, "No event indicated", UsageTypes.SEL KEYBOARD_ERROR_ROLL_OVER = 0x01, "Keyboard ErrorRollOver", UsageTypes.SEL KEYBOARD_POST = 0x02, "Keyboard POSTFail", UsageTypes.SEL KEYBOARD_ERROR_UNDEFINED = 0x03, "Keyboard ErrorUndefined", UsageTypes.SEL KEYBOARD_A = 0x04, "Keyboard a and A", UsageTypes.SEL KEYBOARD_B = 0x05, "Keyboard b and B", UsageTypes.SEL KEYBOARD_C = 0x06, "Keyboard c and C", UsageTypes.SEL KEYBOARD_D = 0x07, "Keyboard d and D", UsageTypes.SEL KEYBOARD_E = 0x08, "Keyboard e and E", UsageTypes.SEL KEYBOARD_F = 0x09, "Keyboard f and F", UsageTypes.SEL KEYBOARD_G = 0x0A, "Keyboard g and G", UsageTypes.SEL KEYBOARD_H = 0x0B, "Keyboard h and H", UsageTypes.SEL KEYBOARD_I = 0x0C, "Keyboard i and I", UsageTypes.SEL KEYBOARD_J = 0x0D, "Keyboard j and J", UsageTypes.SEL KEYBOARD_K = 0x0E, "Keyboard k and K", UsageTypes.SEL KEYBOARD_L = 0x0F, "Keyboard l and L", UsageTypes.SEL KEYBOARD_M = 0x10, "Keyboard m and M", UsageTypes.SEL KEYBOARD_N = 0x11, "Keyboard n and N", UsageTypes.SEL KEYBOARD_O = 0x12, "Keyboard o and O", UsageTypes.SEL KEYBOARD_P = 0x13, "Keyboard p and P", UsageTypes.SEL KEYBOARD_Q = 0x14, "Keyboard q and Q", UsageTypes.SEL KEYBOARD_R = 0x15, "Keyboard r and R", UsageTypes.SEL KEYBOARD_S = 0x16, "Keyboard s and S", UsageTypes.SEL KEYBOARD_T = 0x17, "Keyboard t and T", UsageTypes.SEL KEYBOARD_U = 0x18, "Keyboard u and U", UsageTypes.SEL KEYBOARD_V = 0x19, "Keyboard v and V", UsageTypes.SEL KEYBOARD_W = 0x1A, "Keyboard w and W", UsageTypes.SEL KEYBOARD_X = 0x1B, "Keyboard x and X", UsageTypes.SEL KEYBOARD_Y = 0x1C, "Keyboard y and Y", UsageTypes.SEL KEYBOARD_Z = 0x1D, "Keyboard z and Z", UsageTypes.SEL KEYBOARD_1 = 0x1E, "Keyboard 1 and !", UsageTypes.SEL KEYBOARD_2 = 0x1F, "Keyboard 2 and @", UsageTypes.SEL KEYBOARD_3 = 0x20, "Keyboard 3 and #", UsageTypes.SEL KEYBOARD_4 = 0x21, "Keyboard 4 and $", UsageTypes.SEL KEYBOARD_5 = 0x22, "Keyboard 5 and %", UsageTypes.SEL KEYBOARD_6 = 0x23, "Keyboard 6 and ^", UsageTypes.SEL KEYBOARD_7 = 0x24, "Keyboard 7 and &", UsageTypes.SEL KEYBOARD_8 = 0x25, "Keyboard 8 and *", UsageTypes.SEL KEYBOARD_9 = 0x26, "Keyboard 9 and (", UsageTypes.SEL KEYBOARD_0 = 0x27, "Keyboard 0 and )", UsageTypes.SEL KEYBOARD_ENTER = 0x28, "Keyboard Return (ENTER)", UsageTypes.SEL KEYBOARD_ESCAPE = 0x29, "Keyboard ESCAPE", UsageTypes.SEL KEYBOARD_DELETE = 0x2A, "Keyboard DELETE (Backspace)", UsageTypes.SEL KEYBOARD_TAB = 0x2B, "Keyboard Tab", UsageTypes.SEL KEYBOARD_SPACEBAR = 0x2C, "Keyboard Spacebar", UsageTypes.SEL KEYBOARD_MINUS = 0x2D, "Keyboard - and (underscore)", UsageTypes.SEL KEYBOARD_PLUS = 0x2E, "Keyboard = and +", UsageTypes.SEL KEYBOARD_LEFT_BRACKET = 0x2F, "Keyboard [ and {", UsageTypes.SEL KEYBOARD_RIGHT_BRACKET = 0x30, "Keyboard ] and }", UsageTypes.SEL KEYBOARD_BACKSLASH = 0x31, "Keyboard \\ and |", UsageTypes.SEL KEYBOARD_CARDINAL = 0x32, "Keyboard Non-US # and ~", UsageTypes.SEL KEYBOARD_SEMICOLON = 0x33, "Keyboard ; and :", UsageTypes.SEL KEYBOARD_QUOTE = 0x34, "Keyboard ' and \"", UsageTypes.SEL KEYBOARD_GRAVE = 0x35, "Keyboard Grave Accent and Tilde", UsageTypes.SEL KEYBOARD_COMMA = 0x36, "Keyboard , and <", UsageTypes.SEL KEYBOARD_DOT = 0x37, "Keyboard . and >", UsageTypes.SEL KEYBOARD_SLASH = 0x38, "Keyboard / and ?", UsageTypes.SEL KEYBOARD_CAPS_LOCK = 0x39, "Keyboard Caps Lock", UsageTypes.SEL KEYBOARD_F1 = 0x3A, "Keyboard F1", UsageTypes.SEL KEYBOARD_F2 = 0x3B, "Keyboard F2", UsageTypes.SEL KEYBOARD_F3 = 0x3C, "Keyboard F3", UsageTypes.SEL KEYBOARD_F4 = 0x3D, "Keyboard F4", UsageTypes.SEL KEYBOARD_F5 = 0x3E, "Keyboard F5", UsageTypes.SEL KEYBOARD_F6 = 0x3F, "Keyboard F6", UsageTypes.SEL KEYBOARD_F7 = 0x40, "Keyboard F7", UsageTypes.SEL KEYBOARD_F8 = 0x41, "Keyboard F8", UsageTypes.SEL KEYBOARD_F9 = 0x42, "Keyboard F9", UsageTypes.SEL KEYBOARD_F10 = 0x43, "Keyboard F10", UsageTypes.SEL KEYBOARD_F11 = 0x44, "Keyboard F11", UsageTypes.SEL KEYBOARD_F12 = 0x45, "Keyboard F12", UsageTypes.SEL KEYBOARD_PRINTSCREEN = 0x46, "Keyboard PrintScreen", UsageTypes.SEL KEYBOARD_SCROLL_LOCK = 0x47, "Keyboard Scroll Lock", UsageTypes.SEL KEYBOARD_PAUSE = 0x48, "Keyboard Pause", UsageTypes.SEL KEYBOARD_INSERT = 0x49, "Keyboard Insert", UsageTypes.SEL KEYBOARD_HOME = 0x4A, "Keyboard Home", UsageTypes.SEL KEYBOARD_PAGE_UP = 0x4B, "Keyboard PageUp", UsageTypes.SEL KEYBOARD_DELETE_FORWARD = 0x4C, "Keyboard Delete Forward", UsageTypes.SEL KEYBOARD_END = 0x4D, "Keyboard End", UsageTypes.SEL KEYBOARD_PAGE_DOWN = 0x4E, "Keyboard PageDown", UsageTypes.SEL KEYBOARD_RIGHT_ARROW = 0x4F, "Keyboard RightArrow", UsageTypes.SEL KEYBOARD_LEFT_ARROW = 0x50, "Keyboard LeftArrow", UsageTypes.SEL KEYBOARD_UP_ARROW = 0x51, "Keyboard DownArrow", UsageTypes.SEL KEYBOARD_DOWN_ARROW = 0x52, "Keyboard UpArrow", UsageTypes.SEL KEYBOARD_NUM_LOCK = 0x53, "Keypad Num Lock and Clear", UsageTypes.SEL KEYPAD_SLASH = 0x54, "Keypad /", UsageTypes.SEL KEYPAD_ASTERISK = 0x55, "Keypad *", UsageTypes.SEL KEYPAD_MINUS = 0x56, "Keypad -", UsageTypes.SEL KEYPAD_PLUS = 0x57, "Keypad +", UsageTypes.SEL KEYPAD_ENTER = 0x58, "Keypad ENTER", UsageTypes.SEL KEYPAD_1 = 0x59, "Keypad 1 and End", UsageTypes.SEL KEYPAD_2 = 0x5A, "Keypad 2 and Down Arrow", UsageTypes.SEL KEYPAD_3 = 0x5B, "Keypad 3 and PageDn", UsageTypes.SEL KEYPAD_4 = 0x5C, "Keypad 4 and Left Arrow", UsageTypes.SEL KEYPAD_5 = 0x5D, "Keypad 5", UsageTypes.SEL KEYPAD_6 = 0x5E, "Keypad 6 and Right Arrow", UsageTypes.SEL KEYPAD_7 = 0x5F, "Keypad 7 and Home", UsageTypes.SEL KEYPAD_8 = 0x60, "Keypad 8 and Up Arrow", UsageTypes.SEL KEYPAD_9 = 0x61, "Keypad 9 and PageUp", UsageTypes.SEL KEYPAD_0 = 0x62, "Keypad 0 and Insert", UsageTypes.SEL KEYPAD_DOT = 0x63, "Keypad . and Delete", UsageTypes.SEL KEYPAD_BACKSLASH = 0x64, "Keyboard Non-US \\ and |", UsageTypes.SEL KEYPAD_APPLICATION = 0x65, "Keyboard Application", UsageTypes.SEL KEYPAD_POWER = 0x66, "Keyboard Power", UsageTypes.SEL KEYPAD_EQUALS = 0x67, "Keypad =", UsageTypes.SEL KEYBOARD_F13 = 0x68, "Keyboard F13", UsageTypes.SEL KEYBOARD_F14 = 0x69, "Keyboard F14", UsageTypes.SEL KEYBOARD_F15 = 0x6A, "Keyboard F15", UsageTypes.SEL KEYBOARD_F16 = 0x6B, "Keyboard F16", UsageTypes.SEL KEYBOARD_F17 = 0x6C, "Keyboard F17", UsageTypes.SEL KEYBOARD_F18 = 0x6D, "Keyboard F18", UsageTypes.SEL KEYBOARD_F19 = 0x6E, "Keyboard F19", UsageTypes.SEL KEYBOARD_F20 = 0x6F, "Keyboard F20", UsageTypes.SEL KEYBOARD_F21 = 0x70, "Keyboard F21", UsageTypes.SEL KEYBOARD_F22 = 0x71, "Keyboard F22", UsageTypes.SEL KEYBOARD_F23 = 0x72, "Keyboard F23", UsageTypes.SEL KEYBOARD_F24 = 0x73, "Keyboard F24", UsageTypes.SEL KEYBOARD_EXECUTE = 0x74, "Keyboard Execute", UsageTypes.SEL KEYBOARD_HELP = 0x75, "Keyboard Help", UsageTypes.SEL KEYBOARD_MENU = 0x76, "Keyboard Menu", UsageTypes.SEL KEYBOARD_SELECT = 0x77, "Keyboard Select", UsageTypes.SEL KEYBOARD_STOP = 0x78, "Keyboard Stop", UsageTypes.SEL KEYBOARD_AGAIN = 0x79, "Keyboard Again", UsageTypes.SEL KEYBOARD_UNDO = 0x7A, "Keyboard Undo", UsageTypes.SEL KEYBOARD_CUT = 0x7B, "Keyboard Cut", UsageTypes.SEL KEYBOARD_COPY = 0x7C, "Keyboard Copy", UsageTypes.SEL KEYBOARD_PASTE = 0x7D, "Keyboard Paste", UsageTypes.SEL KEYBOARD_FIND = 0x7E, "Keyboard Find", UsageTypes.SEL KEYBOARD_MUTE = 0x7F, "Keyboard Mute", UsageTypes.SEL KEYBOARD_VOLUME_UP = 0x80, "Keyboard Volume Up", UsageTypes.SEL KEYBOARD_VOLUME_DOWN = 0x81, "Keyboard Volume Down", UsageTypes.SEL KEYBOARD_LOCKING_CAPS_LOCK = 0x82, "Keyboard Locking Caps Lock", UsageTypes.SEL KEYBOARD_LOCKING_NUM_LOCK = 0x83, "Keyboard Locking Num Lock", UsageTypes.SEL KEYBOARD_LOCKING_SCROLL_LOCK = 0x84, "Keyboard Locking Scroll Lock", UsageTypes.SEL KEYPAD_COMMA = 0x85, "Keypad Comma", UsageTypes.SEL KEYPAD_EQUALS_SIGN = 0x86, "Keypad Equal Sign", UsageTypes.SEL KEYBOARD_INTERNATIONAL1 = 0x87, "Keyboard International1", UsageTypes.SEL KEYBOARD_INTERNATIONAL2 = 0x88, "Keyboard International2", UsageTypes.SEL KEYBOARD_INTERNATIONAL3 = 0x89, "Keyboard International3", UsageTypes.SEL KEYBOARD_INTERNATIONAL4 = 0x8A, "Keyboard International4", UsageTypes.SEL KEYBOARD_INTERNATIONAL5 = 0x8B, "Keyboard International5", UsageTypes.SEL KEYBOARD_INTERNATIONAL6 = 0x8C, "Keyboard International6", UsageTypes.SEL KEYBOARD_INTERNATIONAL7 = 0x8D, "Keyboard International7", UsageTypes.SEL KEYBOARD_INTERNATIONAL8 = 0x8E, "Keyboard International8", UsageTypes.SEL KEYBOARD_INTERNATIONAL9 = 0x8F, "Keyboard International9", UsageTypes.SEL KEYBOARD_LANG1 = 0x90, "Keyboard LANG1", UsageTypes.SEL KEYBOARD_LANG2 = 0x91, "Keyboard LANG2", UsageTypes.SEL KEYBOARD_LANG3 = 0x92, "Keyboard LANG3", UsageTypes.SEL KEYBOARD_LANG4 = 0x93, "Keyboard LANG4", UsageTypes.SEL KEYBOARD_LANG5 = 0x94, "Keyboard LANG5", UsageTypes.SEL KEYBOARD_LANG6 = 0x95, "Keyboard LANG6", UsageTypes.SEL KEYBOARD_LANG7 = 0x96, "Keyboard LANG7", UsageTypes.SEL KEYBOARD_LANG8 = 0x97, "Keyboard LANG8", UsageTypes.SEL KEYBOARD_LANG9 = 0x98, "Keyboard LANG9", UsageTypes.SEL KEYBOARD_ALTERNATE_ERASE = 0x99, "Keyboard Alternate Erase", UsageTypes.SEL KEYBOARD_SYSREQ_ATTENTION = 0x9A, "Keyboard SysReq/Attention", UsageTypes.SEL KEYBOARD_CANCEL = 0x9B, "Keyboard Cancel", UsageTypes.SEL KEYBOARD_CLEAR = 0x9C, "Keyboard Clear", UsageTypes.SEL KEYBOARD_PRIOR = 0x9D, "Keyboard Prior", UsageTypes.SEL KEYBOARD_RETURN = 0x9E, "Keyboard Return", UsageTypes.SEL KEYBOARD_SEPARATOE = 0x9F, "Keyboard Separator", UsageTypes.SEL KEYBOARD_OUT = 0xA0, "Keyboard Out", UsageTypes.SEL KEYBOARD_OPER = 0xA1, "Keyboard Oper", UsageTypes.SEL KEYBOARD_CLEAR_AGAIN = 0xA2, "Keyboard Clear/Again", UsageTypes.SEL KEYBOARD_CRSEL_PROPS = 0xA3, "Keyboard CrSel/Props", UsageTypes.SEL KEYBOARD_EXSELL = 0xA4, "Keyboard ExSel", UsageTypes.SEL KEYPAD_ZERO_ZERO = 0xB0, "Keypad 00", UsageTypes.SEL KEYPAD_ZERO_ZERO_ZERO = 0xB1, "Keypad 000", UsageTypes.SEL THOUSANDS_SEPARATOR = 0xB2, "Thousands Separator", UsageTypes.SEL DECIMAL_SEPARATOR = 0xB3, "Decimal Separator", UsageTypes.SEL CURRENCY_UNIT = 0xB4, "Currency Unit", UsageTypes.SEL CURRENCY_SUBUNIT = 0xB5, "Currency Sub-unit", UsageTypes.SEL KEYPAD_LEFT_PARENTHESIS = 0xB6, "Keypad (", UsageTypes.SEL KEYPAD_RIGHT_PARENTHESIS = 0xB7, "Keypad )", UsageTypes.SEL KEYPAD_LEFT_CURLY_BRACKET = 0xB8, "Keypad {", UsageTypes.SEL KEYPAD_RIGHT_CURLY_BRACKET = 0xB9, "Keypad }", UsageTypes.SEL KEYPAD_TAB = 0xBA, "Keypad Tab", UsageTypes.SEL KEYPAD_BACKSPACE = 0xBB, "Keypad Backspace", UsageTypes.SEL KEYPAD_A = 0xBC, "Keypad A", UsageTypes.SEL KEYPAD_B = 0xBD, "Keypad B", UsageTypes.SEL KEYPAD_C = 0xBE, "Keypad C", UsageTypes.SEL KEYPAD_D = 0xBF, "Keypad D", UsageTypes.SEL KEYPAD_E = 0xC0, "Keypad E", UsageTypes.SEL KEYPAD_F = 0xC1, "Keypad F", UsageTypes.SEL KEYPAD_XOR = 0xC2, "Keypad XOR", UsageTypes.SEL KEYPAD_AND = 0xC3, "Keypad ^", UsageTypes.SEL KEYPAD_PERCENTAGE = 0xC4, "Keypad %", UsageTypes.SEL KEYPAD_LESS_THAN = 0xC5, "Keypad <", UsageTypes.SEL KEYPAD_MORE_THAN = 0xC6, "Keypad >", UsageTypes.SEL KEYPAD_AMPERSAND = 0xC7, "Keypad &", UsageTypes.SEL KEYPAD_2_AMPERSAND = 0xC8, "Keypad &&", UsageTypes.SEL KEYPAD_VERTICAL_BAR = 0xC9, "Keypad |", UsageTypes.SEL KEYPAD_2_VERTICAL_BAR = 0xCA, "Keypad ||", UsageTypes.SEL KEYPAD_COLON = 0xCB, "Keypad :", UsageTypes.SEL KEYPAD_CARDINAL = 0xCC, "Keypad #", UsageTypes.SEL KEYPAD_SPACE = 0xCD, "Keypad Space", UsageTypes.SEL KEYPAD_AT = 0xCE, "Keypad @", UsageTypes.SEL KEYPAD_EXCLAMATION = 0xCF, "Keypad !", UsageTypes.SEL KEYPAD_MEMORY_STORE = 0xD0, "Keypad Memory Store", UsageTypes.SEL KEYPAD_MEMORY_RECALL = 0xD1, "Keypad Memory Recall", UsageTypes.SEL KEYPAD_MEMORY_CLEAR = 0xD2, "Keypad Memory Clear", UsageTypes.SEL KEYPAD_MEMORY_ADD = 0xD3, "Keypad Memory Add", UsageTypes.SEL KEYPAD_MEMORY_SUBTRACT = 0xD4, "Keypad Memory Subtract", UsageTypes.SEL KEYPAD_MEMORY_MULTIPLY = 0xD5, "Keypad Memory Multiply", UsageTypes.SEL KEYPAD_MEMORY_DIVIDE = 0xD6, "Keypad Memory Divide", UsageTypes.SEL KEYPAD_PLUS_MINUS = 0xD7, "Keypad +/-", UsageTypes.SEL KEYPAD_CLEAR = 0xD8, "Keypad Clear", UsageTypes.SEL KEYPAD_CLEAR_ENTRY = 0xD9, "Keypad Clear Entry", UsageTypes.SEL KEYPAD_BINARY = 0xDA, "Keypad Binary", UsageTypes.SEL KEYPAD_OCTAL = 0xDB, "Keypad Octal", UsageTypes.SEL KEYPAD_DECIMAL = 0xDC, "Keypad Decimal", UsageTypes.SEL KEYPAD_HEXADECIMAL = 0xDD, "Keypad Hexadecimal", UsageTypes.DV KEYBOARD_LEFT_CONTROL = 0xE0, "Keyboard LeftControl", UsageTypes.DV KEYBOARD_LEFT_SHIFT = 0xE1, "Keyboard LeftShift", UsageTypes.DV KEYBOARD_LEFT_ALT = 0xE2, "Keyboard LeftAlt", UsageTypes.DV KEYBOARD_LEFT_GUI = 0xE3, "Keyboard Left GUI", UsageTypes.DV KEYBOARD_RIGHT_CONTROL = 0xE4, "Keyboard RightControl", UsageTypes.DV KEYBOARD_RIGHT_SHIFT = 0xE5, "Keyboard RightShift", UsageTypes.DV KEYBOARD_RIGHT_ALT = 0xE6, "Keyboard RightAlt", UsageTypes.DV KEYBOARD_RIGHT_GUI = 0xE7, "Keyboard Right GUI", UsageTypes.DV class Led(_Data): NUM_LOCK = 0x01, "Num Lock", UsageTypes.OOC CAPS_LOCK = 0x02, "Caps Lock", UsageTypes.OOC SCROLL_LOCK = 0x03, "Scroll Lock", UsageTypes.OOC COMPOSE = 0x04, "Compose", UsageTypes.OOC KANA = 0x05, "Kana", UsageTypes.OOC POWER = 0x06, "Power", UsageTypes.OOC SHIFT = 0x07, "Shift", UsageTypes.OOC DO_NOT_DISTURB = 0x08, "Do Not Disturb", UsageTypes.OOC MUTE = 0x09, "Mute", UsageTypes.OOC TONE_ENABLE = 0x0A, "Tone Enable", UsageTypes.OOC HIGH_CUT_FILTER = 0x0B, "High Cut Filter", UsageTypes.OOC LOW_CUT_FILTER = 0x0C, "Low Cut Filter", UsageTypes.OOC EQUALIZER_ENABLE = 0x0D, "Equalizer Enable", UsageTypes.OOC SOUND_FIELD_ON = 0x0E, "Sound Field On", UsageTypes.OOC SURROUND_ON = 0x0F, "Surround On", UsageTypes.OOC REPEAR = 0x10, "Repeat", UsageTypes.OOC STEREO = 0x11, "Stereo", UsageTypes.OOC SAMPLING_RATE_DETECT = 0x12, "Sampling Rate Detect", UsageTypes.OOC SPINNING = 0x13, "Spinning", UsageTypes.OOC CAV = 0x14, "CAV", UsageTypes.OOC CLV = 0x15, "CLV", UsageTypes.OOC RECORDING_FORMAT_DETECT = 0x16, "Recording Format Detect", UsageTypes.OOC OFF_HOOK = 0x17, "Off-Hook", UsageTypes.OOC RING = 0x18, "Ring", UsageTypes.OOC MESSAGE_WAITING = 0x19, "Message Waiting", UsageTypes.OOC DATA_MODE = 0x1A, "Data Mode", UsageTypes.OOC BATTERY_OPERATION = 0x1B, "Battery Operation", UsageTypes.OOC BATTERY_OK = 0x1C, "Battery OK", UsageTypes.OOC BATTERY_LOW = 0x1D, "Battery Low", UsageTypes.OOC SPEAKER = 0x1E, "Speaker", UsageTypes.OOC HEAD_SET = 0x1F, "Head Set", UsageTypes.OOC HOLD = 0x20, "Hold", UsageTypes.OOC MICROPHONE = 0x21, "Microphone", UsageTypes.OOC COVERAGE = 0x22, "Coverage", UsageTypes.OOC NIGHT_MODE = 0x23, "Night Mode", UsageTypes.OOC SEND_CALLS = 0x24, "Send Calls", UsageTypes.OOC CALL_PICKUP = 0x25, "Call Pickup", UsageTypes.OOC CONFERENCE = 0x26, "Conference", UsageTypes.OOC STAND_BY = 0x27, "Stand-by", UsageTypes.OOC CAMERA_ON = 0x28, "Camera On", UsageTypes.OOC CAMERA_OFF = 0x29, "Camera Off", UsageTypes.OOC ON_LINE = 0x2A, "On-Line", UsageTypes.OOC OFF_LINE = 0x2B, "Off-Line", UsageTypes.OOC BUSY = 0x2C, "Busy", UsageTypes.OOC READY = 0x2D, "Ready", UsageTypes.OOC PAPER_OUT = 0x2E, "Paper-Out", UsageTypes.OOC PAPER_JAM = 0x2F, "Paper-Jam", UsageTypes.OOC REMOTE = 0x30, "Remote", UsageTypes.OOC FORWARD = 0x31, "Forward", UsageTypes.OOC REVERSE = 0x32, "Reverse", UsageTypes.OOC STOP = 0x33, "Stop", UsageTypes.OOC REWIND = 0x34, "Rewind", UsageTypes.OOC FAST_FORWARD = 0x35, "Fast Forward", UsageTypes.OOC PLAY = 0x36, "Play", UsageTypes.OOC PAUSE = 0x37, "Pause", UsageTypes.OOC RECORD = 0x38, "Record", UsageTypes.OOC ERROR = 0x39, "Error", UsageTypes.OOC USAGE_SELECTED_INDICATOR = 0x3A, "Usage Selected Indicator", UsageTypes.US USAGE_IN_USE_INDICATOR = 0x3B, "Usage In Use Indicator", UsageTypes.US USAGE_MULTI_MODE_INDICATOR = 0x3C, "Usage Multi Mode Indicator", UsageTypes.UM INDICATOR_ON = 0x3D, "Indicator On", UsageTypes.SEL INDICATOR_FLASH = 0x3E, "Indicator Flash", UsageTypes.SEL INDICATOR_SLOW_BLINK = 0x3F, "Indicator Slow Blink", UsageTypes.SEL INDICATOR_FAST_BLINK = 0x40, "Indicator Fast Blink", UsageTypes.SEL INDICATOR_OFF = 0x41, "Indicator Off", UsageTypes.SEL FLASH_ON_TIME = 0x42, "Flash On Time", UsageTypes.DV SLOW_BLINK_ON_TIME = 0x43, "Slow Blink On Time", UsageTypes.DV SLOW_BLINK_OFF_TIME = 0x44, "Slow Blink Off Time", UsageTypes.DV FAST_BLINK_ON_TIME = 0x45, "Fast Blink On Time", UsageTypes.DV FAST_BLINK_OFF_TIME = 0x46, "Fast Blink Off Time", UsageTypes.DV USAGE_INDICATOR_COLOR = 0x47, "Usage Indicator Color", UsageTypes.UM INDICATOR_RED = 0x48, "Indicator Red", UsageTypes.SEL INDICATOR_GREEN = 0x49, "Indicator Green", UsageTypes.SEL INDICATOR_AMBER = 0x4A, "Indicator Amber", UsageTypes.SEL GENERIC_INDICATOR = 0x4B, "Generic Indicator", UsageTypes.OOC SYSTEM_SUSPEND = 0x4C, "System Suspend", UsageTypes.OOC EXTERNAL_POWER_CONNECTED = 0x4D, "External Power Connected", UsageTypes.OOC class Button(_Data): _USAGE_TYPES = ( UsageTypes.SEL, UsageTypes.OOC, UsageTypes.MC, UsageTypes.OSC, ) data = { "NO_BUTTON": (0x0000, "Button 1 (primary/trigger)", _USAGE_TYPES), "BUTTON_1": (0x0001, "Button 1 (primary/trigger)", _USAGE_TYPES), "BUTTON_2": (0x0002, "Button 2 (secondary)", _USAGE_TYPES), "BUTTON_3": (0x0003, "Button 3 (tertiary)", _USAGE_TYPES), } for _i in range(0x0004, 0xFFFF): data[f"BUTTON_{_i}"] = _i, f"Button {_i}", _USAGE_TYPES class Consumer(_Data): CONSUMER_CONTROL = 0x0001, "Consumer Control", UsageTypes.CA NUMERIC_KEY_PAD = 0x0002, "Numeric Key Pad", UsageTypes.NARY PROGRAMMABLE_BUTTONS = 0x0003, "Programmable Buttons", UsageTypes.NARY MICROPHONE = 0x0004, "Microphone", UsageTypes.CA HEADPHONE = 0x0005, "Headphone", UsageTypes.CA GRAPHIC_EQUALIZER = 0x0006, "Graphic Equalizer", UsageTypes.CA PLUS10 = 0x0020, "+10", UsageTypes.OSC PULS100 = 0x0021, "+100", UsageTypes.OSC AM_PM = 0x0022, "AM/PM", UsageTypes.OSC POWER = 0x0030, "Power", UsageTypes.OOC REST = 0x0031, "Reset", UsageTypes.OSC SLEEP = 0x0032, "Sleep", UsageTypes.OSC SLEEP_AFTER = 0x0033, "Sleep After", UsageTypes.OSC SLEEP_MODE = 0x0034, "Sleep Mode", UsageTypes.RTC ILLUMINATION = 0x0035, "Illumination", UsageTypes.OOC FUNCTION_BUTTONS = 0x0036, "Function Buttons", UsageTypes.NARY MENU = 0x0040, "Menu", UsageTypes.OOC MENU_PICK = 0x0041, "Menu Pick", UsageTypes.OSC MENU_UP = 0x0042, "Menu Up", UsageTypes.OSC MENU_DOWN = 0x0043, "Menu Down", UsageTypes.OSC MENU_LEFT = 0x0044, "Menu Left", UsageTypes.OSC MENU_RIGHT = 0x0045, "Menu Right", UsageTypes.OSC MENU_ESCAPE = 0x0046, "Menu Escape", UsageTypes.OSC MENU_VALUE_INCREASE = 0x0047, "Menu Value Increase", UsageTypes.OSC MENU_VALUE_DECREASE = 0x0048, "Menu Value Decrease", UsageTypes.OSC DATA_ON_SCREEN = 0x0060, "Data On Screen", UsageTypes.OOC CLOSED_CAPTION = 0x0061, "Closed Caption", UsageTypes.OOC CLOSED_CAPTION_SELECT = 0x0062, "Closed Caption Select", UsageTypes.OOC VCR_TV = 0x0063, "VCR/TV", UsageTypes.OSC BROADCAST_MODE = 0x0064, "Broadcast Mode", UsageTypes.OOC SNAPSHOT = 0x0065, "Snapshot", UsageTypes.OOC STILL = 0x0066, "Still", UsageTypes.OOC SELECTION = 0x0080, "Selection", UsageTypes.NARY ASSIGN_SELECTION = 0x0081, "Assign Selection", UsageTypes.OSC MODE_STEP = 0x0082, "Mode Step", UsageTypes.OSC RECALL_LAST = 0x0083, "Recall Last", UsageTypes.OSC ENTER_CHANNEL = 0x0084, "Enter Channel", UsageTypes.OSC ORDER_MOVIE = 0x0085, "Order Movie", UsageTypes.OSC CHANNEL = 0x0086, "Channel", UsageTypes.LC MEDIA_SELECTION = 0x0087, "Media Selection", UsageTypes.NARY MEDIA_SELECT_COMPUTER = 0x0088, "Media Select Computer", UsageTypes.SEL MEDIA_SELECT_TV = 0x0089, "Media Select TV", UsageTypes.SEL MEDIA_SELECT_WWW = 0x008A, "Media Select WWW", UsageTypes.SEL MEDIA_SELECT_DVD = 0x008B, "Media Select DVD", UsageTypes.SEL MEDIA_SELECT_TELEPHONE = 0x008C, "Media Select Telephone", UsageTypes.SEL MEDIA_SELECT_PROGRAM_GUIDE = 0x008D, "Media Select Program Guide", UsageTypes.SEL MEDIA_SELECT_VIDEO_PHONE = 0x008E, "Media Select Video Phone", UsageTypes.SEL MEDIA_SELECT_GAMES = 0x008F, "Media Select Games", UsageTypes.SEL MEDIA_SELECT_MESSAGES = 0x0090, "Media Select Messages", UsageTypes.SEL MEDIA_SELECT_CD = 0x0091, "Media Select CD ", UsageTypes.SEL MEDIA_SELECT_VCR = 0x0092, "Media Select VCR", UsageTypes.SEL MEDIA_SELECT_TUNER = 0x0093, "Media Select Tuner", UsageTypes.SEL QUIT = 0x0094, "Quit", UsageTypes.OSC HELP = 0x0095, "Help", UsageTypes.OOC MEDIA_SELECT_TAPE = 0x0096, "Media Select Tape", UsageTypes.SEL MEDIA_SELECT_CABLE = 0x0097, "Media Select Cable", UsageTypes.SEL MEDIA_SELECT_SATELLITE = 0x0098, "Media Select Satellite", UsageTypes.SEL MEDIA_SELECT_SECURITY = 0x0099, "Media Select Security", UsageTypes.SEL MEDIA_SELECT_HOME = 0x009A, "Media Select Home", UsageTypes.SEL MEDIA_SELECT_CALL = 0x009B, "Media Select Call", UsageTypes.SEL CHANNEL_INCREMENT = 0x009C, "Channel Increment", UsageTypes.OSC CHANNEL_DECREMENT = 0x009D, "Channel Decrement", UsageTypes.OSC MEDIA_SELECT_SAP = 0x009E, "Media Select SAP", UsageTypes.SEL VCR_PLUS = 0x00A0, "VCR Plus", UsageTypes.OSC ONCE = 0x00A1, "Once", UsageTypes.OSC DAILY = 0x00A2, "Daily", UsageTypes.OSC WEEKLY = 0x00A3, "Weekly", UsageTypes.OSC MONTHLY = 0x00A4, "Monthly", UsageTypes.OSC PLAY = 0x00B0, "Play", UsageTypes.OOC PAUSE = 0x00B1, "Pause", UsageTypes.OOC RECORD = 0x00B2, "Record", UsageTypes.OOC FAST_FORWARD = 0x00B3, "Fast Forward", UsageTypes.OOC REWIND = 0x00B4, "Rewind", UsageTypes.OOC SCAN_NEXT_TRACK = 0x00B5, "Scan Next Track", UsageTypes.OSC SCAN_PREVIOUS_TRACK = 0x00B6, "Scan Previous Track", UsageTypes.OSC STOP = 0x00B7, "Stop", UsageTypes.OSC EJECT = 0x00B8, "Eject", UsageTypes.OSC RANDOM_PLAY = 0x00B9, "Random Play", UsageTypes.OOC SELECT_DISC = 0x00BA, "Select Disc", UsageTypes.NARY ENTER_DISC = 0x00BB, "Enter Disc", UsageTypes.MC REPEAT = 0x00BC, "Repeat", UsageTypes.OSC TRACKING = 0x00BD, "Tracking", UsageTypes.LC TRACK_NORMAL = 0x00BE, "Track Normal", UsageTypes.OSC SLOW_TRACKING = 0x00BF, "Slow Tracking", UsageTypes.LC FRAME_FORWARD = 0x00C0, "Frame Forward", UsageTypes.RTC FRAME_BACK = 0x00C1, "Frame Back", UsageTypes.RTC MARK = 0x00C2, "Mark", UsageTypes.OSC CLEAR_MARK = 0x00C3, "Clear Mark", UsageTypes.OSC REPEAT_FROM_MARK = 0x00C4, "Repeat From Mark", UsageTypes.OOC RETURN_TO_MARK = 0x00C5, "Return To Mark", UsageTypes.OSC SEARCH_MARK_FORWARD = 0x00C6, "Search Mark Forward", UsageTypes.OSC SEARCH_MARK_BACKWARDS = 0x00C7, "Search Mark Backwards", UsageTypes.OSC COUNTER_RESET = 0x00C8, "Counter Reset", UsageTypes.OSC SHOW_COUNTER = 0x00C9, "Show Counter", UsageTypes.OSC TRACKING_INCREMENT = 0x00CA, "Tracking Increment", UsageTypes.RTC TRACKING_DECREMENT = 0x00CB, "Tracking Decrement", UsageTypes.RTC STOP_EJECT = 0x00CC, "Stop/Eject", UsageTypes.OSC PLAY_PAUSE = 0x00CD, "Play/Pause", UsageTypes.OSC PLAY_SKIP = 0x00CE, "Play/Skip", UsageTypes.OSC VOLUME = 0x00E0, "Volume", UsageTypes.LC BALANCE = 0x00E1, "Balance", UsageTypes.LC MUTE = 0x00E2, "Mute", UsageTypes.OOC BASS = 0x00E3, "Bass", UsageTypes.LC TREBLE = 0x00E4, "Treble", UsageTypes.LC BASS_BOOST = 0x00E5, "Bass Boost", UsageTypes.OOC SURROUND_MODE = 0x00E6, "Surround Mode", UsageTypes.OSC LOUDNESS = 0x00E7, "Loudness", UsageTypes.OOC MPX = 0x00E8, "MPX", UsageTypes.OOC VOLUME_INCREMENT = 0x00E9, "Volume Increment", UsageTypes.RTC VOLUME_DECREMENT = 0x00EA, "Volume Decrement", UsageTypes.RTC SPEED_SELECT = 0x00F0, "Speed Select", UsageTypes.OSC PLAYBACK_SPEED = 0x00F1, "Playback Speed", UsageTypes.NARY STANDARD_PLAY = 0x00F2, "Standard Play", UsageTypes.SEL LONG_PLAY = 0x00F3, "Long Play", UsageTypes.SEL EXTENDED_PLAY = 0x00F4, "Extended Play", UsageTypes.SEL SLOW = 0x00F5, "Slow", UsageTypes.OSC FAN_ENABLE = 0x0100, "Fan Enable", UsageTypes.OOC FAN_SPEED = 0x0101, "Fan Speed", UsageTypes.LC LIGHT_ENABLE = 0x0102, "Light Enable", UsageTypes.OOC LIGHT_ILLUMINATION_LEVEL = 0x0103, "Light Illumination Level", UsageTypes.LC CLIMATE_CONTROL_ENABLE = 0x0104, "Climate Control Enable", UsageTypes.OOC ROOM_TEMPERATURE = 0x0105, "Room Temperature", UsageTypes.LC SECURITY_ENABLE = 0x0106, "Security Enable", UsageTypes.OOC FIRE_ALARM = 0x0107, "Fire Alarm", UsageTypes.OSC POLICE_ALARM = 0x0108, "Police Alarm", UsageTypes.OSC PROXIMITY = 0x0109, "Proximity", UsageTypes.LC MOTION = 0x010A, "Motion", UsageTypes.OSC DURESS_ALARM = 0x010B, "Duress Alarm", UsageTypes.OSC HOLDUP_ALARM = 0x010C, "Holdup Alarm", UsageTypes.OSC MEDICAL_ALARM = 0x010D, "Medical Alarm", UsageTypes.OSC BALANCE_RIGHT = 0x0150, "Balance Right", UsageTypes.RTC BALANCE_LEFT = 0x0151, "Balance Left", UsageTypes.RTC BASS_INCREMENT = 0x0152, "Bass Increment", UsageTypes.RTC BASS_DECREMENT = 0x0153, "Bass Decrement", UsageTypes.RTC TREBLE_INCREMENT = 0x0154, "Treble Increment", UsageTypes.RTC TREBLE_DECREMENT = 0x0155, "Treble Decrement", UsageTypes.RTC SPEAKER_SYSTEM = 0x0160, "Speaker System", UsageTypes.CL CHANNEL_LEFT = 0x0161, "Channel Left", UsageTypes.CL CHANNEL_RIGHT = 0x0162, "Channel Right", UsageTypes.CL CHANNEL_CENTER = 0x0163, "Channel Center", UsageTypes.CL CHANNEL_FRONT = 0x0164, "Channel Front", UsageTypes.CL CHANNEL_CENTER_FRONT = 0x0165, "Channel Center Front", UsageTypes.CL CHANNEL_SIDE = 0x0166, "Channel Side", UsageTypes.CL CHANNEL_SURROUND = 0x0167, "Channel Surround", UsageTypes.CL CHANNEL_LOW_FREQUENCY_ENHANCEMENT = 0x0168, "Channel Low Frequency Enhancement", UsageTypes.CL CHANNEL_TOP = 0x0169, "Channel Top", UsageTypes.CL CHANNEL_UNKNOWN = 0x016A, "Channel Unknown", UsageTypes.CL SUBCHANNEL = 0x0170, "Sub-channel", UsageTypes.LC SUBCHANNEL_INCREMENT = 0x0171, "Sub-channel Increment", UsageTypes.OSC SUBCHANNEL_DECREMENT = 0x0172, "Sub-channel Decrement", UsageTypes.OSC ALTERNATE_AUDIO_INCREMENT = 0x0173, "Alternate Audio Increment", UsageTypes.OSC ALTERNATE_AUDIO_DECREMENT = 0x0174, "Alternate Audio Decrement", UsageTypes.OSC APPLICATION_LAUNCH_BUTTONS = 0x0180, "Application Launch Buttons", UsageTypes.NARY AL_LAUCH_BUTTON_CONFIGURATION_TOOL = 0x0181, "AL Launch Button Configuration Tool", UsageTypes.SEL AL_PROGRAMMABLE_BUTTON_CONFIGURATION = 0x0182, "AL Programmable Button Configuration", UsageTypes.SEL AL_CONSUMER_CONTROL_CONFIGURATION = 0x0183, "AL Consumer Control Configuration", UsageTypes.SEL AL_WORD_PROCESSOR = 0x0184, "AL Word Processor", UsageTypes.SEL AL_TEXT_EDITOR = 0x0185, "AL Text Editor", UsageTypes.SEL AL_SPREADSHEET = 0x0186, "AL Spreadsheet", UsageTypes.SEL AL_GRAPHICS_EDITOR = 0x0187, "AL Graphics Editor", UsageTypes.SEL AL_PRESENTATION_APP = 0x0188, "AL Presentation App", UsageTypes.SEL AL_DATABASE_APP = 0x0189, "AL Database App", UsageTypes.SEL AL_EMAIL_READER = 0x018A, "AL Email Reader", UsageTypes.SEL AL_NEWSREADER = 0x018B, "AL Newsreader", UsageTypes.SEL AL_VOICEMAIL = 0x018C, "AL Voicemail", UsageTypes.SEL AL_CONTACTS_ADDRESS_BOOK = 0x018D, "AL Contacts/Address Book", UsageTypes.SEL AL_CALENDAR_SCHEDULE = 0x018E, "AL Calendar/Schedule", UsageTypes.SEL AL_TASK_PROJECT_MANAGER = 0x018F, "AL Task/Project Manager", UsageTypes.SEL AL_LOG_JOURNAL_TIMECARD = 0x0190, "AL Log/Journal/Timecard", UsageTypes.SEL AL_CHECKBOOK_FINANCE = 0x0191, "AL Checkbook/Finance", UsageTypes.SEL AL_CALCULATOR = 0x0192, "AL Calculator", UsageTypes.SEL AL_AV_CAPTURE_PLAYBACK = 0x0193, "AL A/V Capture/Playback", UsageTypes.SEL AL_LOCAL_MACHINE_BROWSER = 0x0194, "AL Local Machine Browser", UsageTypes.SEL AL_LAN_WAN_BROWSER = 0x0195, "AL LAN/WAN Browser", UsageTypes.SEL AL_INTERNET_BROWSER = 0x0196, "AL Internet Browser", UsageTypes.SEL AL_REMOTE_NETWORKING_ISP_CONNECT = 0x0197, "AL Remote Networking/ISP Connect", UsageTypes.SEL AL_NETWORK_CONFERENCE = 0x0198, "AL Network Conference", UsageTypes.SEL AL_NETWORK_CHAT = 0x0199, "AL Network Chat", UsageTypes.SEL AL_TELEPHONY_DIALER = 0x019A, "AL Telephony/Dialer", UsageTypes.SEL AL_LOGON = 0x019B, "AL Logon", UsageTypes.SEL AL_LOGOFF = 0x019C, "AL Logoff", UsageTypes.SEL AL_LOGON_LOGOFF = 0x019D, "AL Logon/Logoff", UsageTypes.SEL AL_LOCK_SCREEN_SAVER = 0x019E, "AL Terminal Lock/Screensaver", UsageTypes.SEL AL_CONTROL_PANEL = 0x019F, "AL Control Panel", UsageTypes.SEL AL_COMMAND_LINE_PROCESSOR_RUN = 0x01A0, "AL Command Line Processor/Run", UsageTypes.SEL AL_PROCESS_TASK_MANAGER = 0x01A1, "AL Process/Task Manager", UsageTypes.SEL AL_SELECT_TASK_APPLICATION = 0x01A2, "AL Select Task/Application", UsageTypes.SEL AL_NEXT_TASK_APPLICATION = 0x01A3, "AL Next Task/Application", UsageTypes.SEL AL_PREVIOUS_TASK_APPLICATION = 0x01A4, "AL Previous Task/Application", UsageTypes.SEL AL_HALT_TASK_APPLICATION = 0x01A5, "AL Preemptive Halt Task/Application", UsageTypes.SEL AL_INTEGRATED_HELP_CENTER = 0x01A6, "AL Integrated Help Center", UsageTypes.SEL AL_DOCUMENTS = 0x01A7, "AL Documents", UsageTypes.SEL AL_THESAURUS = 0x01A8, "AL Thesaurus", UsageTypes.SEL AL_DICTIONARY = 0x01A9, "AL Dictionary", UsageTypes.SEL AL_DESKTOP = 0x01AA, "AL Desktop", UsageTypes.SEL AL_SPELL_CHECK = 0x01AB, "AL Spell Check", UsageTypes.SEL AL_GRAMMAR_CHECK = 0x01AC, "AL Grammar Check", UsageTypes.SEL AL_WIRELESS_STATUS = 0x01AD, "AL Wireless Status", UsageTypes.SEL AL_KEYBOARD_LAYOUT = 0x01AE, "AL Keyboard Layout", UsageTypes.SEL AL_VIRUS_PROTECTION = 0x01AF, "AL Virus Protection", UsageTypes.SEL AL_ENCRYPTION = 0x01B0, "AL Encryption", UsageTypes.SEL AL_SCREEN_SAVER = 0x01B1, "AL Screen Saver", UsageTypes.SEL AL_ALARMS = 0x01B2, "AL Alarms", UsageTypes.SEL AL_CLOCK = 0x01B3, "AL Clock", UsageTypes.SEL AL_FILE_BROWSER = 0x01B4, "AL File Browser", UsageTypes.SEL AL_POWER_STATUS = 0x01B5, "AL Power Status", UsageTypes.SEL AL_IMAGE_BROWSER = 0x01B6, "AL Image Browser", UsageTypes.SEL AL_AUDIO_BROWSER = 0x01B7, "AL Audio Browser", UsageTypes.SEL AL_VIDEO_BROWSER = 0x01B8, "AL Movie Browser", UsageTypes.SEL AL_DIGITAL_RIGHTS_MANAGER = 0x01B9, "AL Digital Rights Manager", UsageTypes.SEL AL_DIGITAL_WALLET = 0x01BA, "AL Digital Wallet", UsageTypes.SEL AL_INSTANT_MESSAGING = 0x01BC, "AL Instant Messaging", UsageTypes.SEL AL_OEM_FEATURES_TIPS_TUTORIAL_BROWSER = 0x01BD, "AL OEM Features/ Tips/Tutorial Browser", UsageTypes.SEL AL_OEM_HELP = 0x01BE, "AL OEM Help", UsageTypes.SEL AL_ONLINE_COMMUNITY = 0x01BF, "AL Online Community", UsageTypes.SEL AL_ENTERTAINMENT_CONTENT_BROWSER = 0x01C0, "AL Entertainment Content Browser", UsageTypes.SEL AL_ONLINE_SHOPPING_BROWSER = 0x01C1, "AL Online Shopping Browser", UsageTypes.SEL AL_SMARTCARD_INFORMATION_HELP = 0x01C2, "AL SmartCard Information/Help", UsageTypes.SEL AL_MARKET_MONITOR_FINANCE_BROWSER = 0x01C3, "AL Market Monitor/Finance Browser", UsageTypes.SEL AL_CUSTOMIZED_CORPORATE_NEWS_BROWSER = 0x01C4, "AL Customized Corporate News Browser", UsageTypes.SEL AL_ONLINE_ACTIVITY_BROWSER = 0x01C5, "AL Online Activity Browser", UsageTypes.SEL AL_RESEARCH_SEARCH_BROWSER = 0x01C6, "AL Research/Search Browser", UsageTypes.SEL AL_AUDIO_PLAYER = 0x01C7, "AL Audio Player", UsageTypes.SEL GENERIC_GUI_APPLICATION_CONTROLS = 0x0200, "Generic GUI Application Controls", UsageTypes.NARY AC_NEW = 0x0201, "AC New", UsageTypes.SEL AC_OPEN = 0x0202, "AC Open", UsageTypes.SEL AC_CLOSE = 0x0203, "AC Close", UsageTypes.SEL AC_EXIT = 0x0204, "AC Exit", UsageTypes.SEL AC_MAXIMIZE = 0x0205, "AC Maximize", UsageTypes.SEL AC_MINIMIZE = 0x0206, "AC Minimize", UsageTypes.SEL AC_SAVE = 0x0207, "AC Save", UsageTypes.SEL AC_PRINT = 0x0208, "AC Print", UsageTypes.SEL AC_PROPERTIES = 0x0209, "AC Properties", UsageTypes.SEL AC_UNDO = 0x021A, "AC Undo", UsageTypes.SEL AC_COPY = 0x021B, "AC Copy", UsageTypes.SEL AC_CUT = 0x021C, "AC Cut", UsageTypes.SEL AC_PASTE = 0x021D, "AC Paste", UsageTypes.SEL AC_SELECT_ALL = 0x021E, "AC Select All", UsageTypes.SEL AC_FIND = 0x021F, "AC Find", UsageTypes.SEL AC_FIND_AND_REPLACE = 0x0220, "AC Find and Replace", UsageTypes.SEL AC_SEARCH = 0x0221, "AC Search", UsageTypes.SEL AC_GO_TO = 0x0222, "AC Go To", UsageTypes.SEL AC_HOME = 0x0223, "AC Home", UsageTypes.SEL AC_BACK = 0x0224, "AC Back", UsageTypes.SEL AC_FORWARD = 0x0225, "AC Forward", UsageTypes.SEL AC_STOP = 0x0226, "AC Stop", UsageTypes.SEL AC_REFRESH = 0x0227, "AC Refresh", UsageTypes.SEL AC_PREVIOUS_LINK = 0x0228, "AC Previous Link", UsageTypes.SEL AC_NEXT_LINK = 0x0229, "AC Next Link", UsageTypes.SEL AC_BOOKMARKS = 0x022A, "AC Bookmarks", UsageTypes.SEL AC_HISTORY = 0x022B, "AC History", UsageTypes.SEL AC_SUBSCRIPTIONS = 0x022C, "AC Subscriptions", UsageTypes.SEL AC_ZOOM_IN = 0x022D, "AC Zoom In", UsageTypes.SEL AC_ZOOM_OUT = 0x022E, "AC Zoom Out", UsageTypes.SEL AC_ZOOM = 0x022F, "AC Zoom", UsageTypes.LC AC_FULL_SCREEN_VIEW = 0x0230, "AC Full Screen View", UsageTypes.SEL AC_NORMAL_VIEW = 0x0231, "AC Normal View", UsageTypes.SEL AC_VIEW_TOGGLE = 0x0232, "AC View Toggle", UsageTypes.SEL AC_SCROLL_UP = 0x0233, "AC Scroll Up", UsageTypes.SEL AC_SCROLL_DOWN = 0x0234, "AC Scroll Down", UsageTypes.SEL AC_SCROLL = 0x0235, "AC Scroll", UsageTypes.LC AC_PAN_LEFT = 0x0236, "AC Pan Left", UsageTypes.SEL AC_PAN_RIGHT = 0x0237, "AC Pan Right", UsageTypes.SEL AC_PAN = 0x0238, "AC Pan", UsageTypes.LC AC_NEW_WINDOWS = 0x0239, "AC New Window", UsageTypes.SEL AC_TILE_HORIZONTALLY = 0x023A, "AC Tile Horizontally", UsageTypes.SEL AC_TILE_VERTICALLY = 0x023B, "AC Tile Vertically", UsageTypes.SEL AC_FORMAT = 0x023C, "AC Format", UsageTypes.SEL AC_EDIT = 0x023D, "AC Edit", UsageTypes.SEL AC_BOLD = 0x023E, "AC Bold", UsageTypes.SEL AC_ITALICS = 0x023F, "AC Italics", UsageTypes.SEL AC_UNDERLINE = 0x0240, "AC Underline", UsageTypes.SEL AC_STRIKETHROUGH = 0x0241, "AC Strikethrough", UsageTypes.SEL AC_SUBSCRIPT = 0x0242, "AC Subscript", UsageTypes.SEL AC_SUPERSCRIPT = 0x0243, "AC Superscript", UsageTypes.SEL AC_ALL_CAPS = 0x0244, "AC All Caps", UsageTypes.SEL AC_ROTATE = 0x0245, "AC Rotate", UsageTypes.SEL AC_RESIZE = 0x0246, "AC Resize", UsageTypes.SEL AC_FLIP_HORIZONTAL = 0x0247, "AC Flip horizontal", UsageTypes.SEL AC_FLIP_VERTICAL = 0x0248, "AC Flip Vertical", UsageTypes.SEL AC_MIRROR_HORIZONTAL = 0x0249, "AC Mirror Horizontal", UsageTypes.SEL AC_MIRROR_VERTICAL = 0x024A, "AC Mirror Vertical", UsageTypes.SEL AC_FONT_SELECT = 0x024B, "AC Font Select", UsageTypes.SEL AC_FONT_COLOR = 0x024C, "AC Font Color", UsageTypes.SEL AC_FONT_SIZE = 0x024D, "AC Font Size", UsageTypes.SEL AC_JUSTIFY_LEFT = 0x024E, "AC Justify Left", UsageTypes.SEL AC_JUSTIFY_CENTER_H = 0x024F, "AC Justify Center H", UsageTypes.SEL AC_JUSTIFY_RIGHT = 0x0250, "AC Justify Right", UsageTypes.SEL AC_JUSTIFY_BLOCK_H = 0x0251, "AC Justify Block H", UsageTypes.SEL AC_JUSTIFY_TOP = 0x0252, "AC Justify Top", UsageTypes.SEL AC_JUSTIFY_CENTER_V = 0x0253, "AC Justify Center V", UsageTypes.SEL AC_JUSTIFY_BOTTOM = 0x0254, "AC Justify Bottom", UsageTypes.SEL AC_JUSTIFY_BLOCK_V = 0x0255, "AC Justify Block V", UsageTypes.SEL AC_INDENT_INCREASE = 0x0256, "AC Indent Decrease", UsageTypes.SEL AC_INDENT_DECREASE = 0x0257, "AC Indent Increase", UsageTypes.SEL AC_NUMBERED_LIST = 0x0258, "AC Numbered List", UsageTypes.SEL AC_RESTART_NUMBERING = 0x0259, "AC Restart Numbering", UsageTypes.SEL AC_BULLETED_LIST = 0x025A, "AC Bulleted List", UsageTypes.SEL AC_PROMOTE = 0x025B, "AC Promote", UsageTypes.SEL AC_DEMOTE = 0x025C, "AC Demote", UsageTypes.SEL AC_YES = 0x025D, "AC Yes", UsageTypes.SEL AC_NO = 0x025E, "AC No", UsageTypes.SEL AC_CANCEL = 0x025F, "AC Cancel", UsageTypes.SEL AC_CATALOG = 0x0260, "AC Catalog", UsageTypes.SEL AC_BUY_CHECKOUT = 0x0261, "AC Buy/Checkout", UsageTypes.SEL AC_ADD_TO_CART = 0x0262, "AC Add to Cart", UsageTypes.SEL AC_EXPAND = 0x0263, "AC Expand", UsageTypes.SEL AC_EXPAND_ALL = 0x0264, "AC Expand All", UsageTypes.SEL AC_COLLAPSE = 0x0265, "AC Collapse", UsageTypes.SEL AC_COLLAPSE_ALL = 0x0266, "AC Collapse All", UsageTypes.SEL AC_PRINT_PREVIEW = 0x0267, "AC Print Preview", UsageTypes.SEL AC_PASTE_SPECIAL = 0x0268, "AC Paste Special", UsageTypes.SEL AC_INSER_MODE = 0x0269, "AC Insert Mode", UsageTypes.SEL AC_DELETE = 0x026A, "AC Delete", UsageTypes.SEL AC_LOCK = 0x026B, "AC Lock", UsageTypes.SEL AC_UNLOCK = 0x026C, "AC Unlock", UsageTypes.SEL AC_PROTECT = 0x026D, "AC Protect", UsageTypes.SEL AC_UNPROTECT = 0x026E, "AC Unprotect", UsageTypes.SEL AC_ATTACH_COMMENT = 0x026F, "AC Attach Comment", UsageTypes.SEL AC_DELETE_COMMENT = 0x0270, "AC Delete Comment", UsageTypes.SEL AC_VIEW_COMMENT = 0x0271, "AC View Comment", UsageTypes.SEL AC_SELECT_WORD = 0x0272, "AC Select Word", UsageTypes.SEL AC_SELECT_SENTENCE = 0x0273, "AC Select Sentence", UsageTypes.SEL AC_SELECT_PARAGRAPH = 0x0274, "AC Select Paragraph", UsageTypes.SEL AC_SELECT_COLUMN = 0x0275, "AC Select Column", UsageTypes.SEL AC_SELECT_ROW = 0x0276, "AC Select Row", UsageTypes.SEL AC_SELECT_TABLE = 0x0277, "AC Select Table", UsageTypes.SEL AC_SELECT_OBJECT = 0x0278, "AC Select Object", UsageTypes.SEL AC_REDO_REPEAT = 0x0279, "AC Redo/Repeat", UsageTypes.SEL AC_SORT = 0x027A, "AC Sort", UsageTypes.SEL AC_SORT_ASCENDING = 0x027B, "AC Sort Ascending", UsageTypes.SEL AC_SORT_DESCENDING = 0x027C, "AC Sort Descending", UsageTypes.SEL AC_FILTER = 0x027D, "AC Filter", UsageTypes.SEL AC_SET_CLOCK = 0x027E, "AC Set Clock", UsageTypes.SEL AC_VIEW_CLOCK = 0x027F, "AC View Clock", UsageTypes.SEL AC_SELECT_TIME_ZONE = 0x0280, "AC Select Time Zone", UsageTypes.SEL AC_EDIT_TIME_ZONES = 0x0281, "AC Edit Time Zones", UsageTypes.SEL AC_SET_ALARM = 0x0282, "AC Set Alarm", UsageTypes.SEL AC_CLEAR_ALARM = 0x0283, "AC Clear Alarm", UsageTypes.SEL AC_SNOOZE_ALARM = 0x0284, "AC Snooze Alarm", UsageTypes.SEL AC_RESET_ALARM = 0x0285, "AC Reset Alarm", UsageTypes.SEL AC_SYNCHRONIZE = 0x0286, "AC Synchronize", UsageTypes.SEL AC_SEND_RECEIVE = 0x0287, "AC Send/Receive", UsageTypes.SEL AC_SEND_TO = 0x0288, "AC Send To", UsageTypes.SEL AC_REPLY = 0x0289, "AC Reply", UsageTypes.SEL AC_REPLY_ALL = 0x028A, "AC Reply All", UsageTypes.SEL AC_FORWARD_MSG = 0x028B, "AC Forward Msg", UsageTypes.SEL AC_SEND = 0x028C, "AC Send", UsageTypes.SEL AC_ATTACH_FILE = 0x028D, "AC Attach File", UsageTypes.SEL AC_UPLOAD = 0x028E, "AC Upload", UsageTypes.SEL AC_DOWNLOAD = 0x028F, "AC Download (Save Target As)", UsageTypes.SEL AC_SET_BORDERS = 0x0290, "AC Set Borders", UsageTypes.SEL AC_INSERT_ROW = 0x0291, "AC Insert Row", UsageTypes.SEL AC_INSERT_COLUMN = 0x0292, "AC Insert Column", UsageTypes.SEL AC_INSERT_FILE = 0x0293, "AC Insert File", UsageTypes.SEL AC_INSERT_PICTURE = 0x0294, "AC Insert Picture", UsageTypes.SEL AC_INSERT_OBJECT = 0x0295, "AC Insert Object", UsageTypes.SEL AC_INSERT_SYMBOL = 0x0296, "AC Insert Symbol", UsageTypes.SEL AC_SAVE_AND_CLOSE = 0x0297, "AC Save and Close", UsageTypes.SEL AC_RENAME = 0x0298, "AC Rename", UsageTypes.SEL AC_MERGE = 0x0299, "AC Merge", UsageTypes.SEL AC_SPLIT = 0x029A, "AC Split", UsageTypes.SEL AC_DISTRIBUTE_HORIZONTICALLY = 0x029B, "AC Disribute Horizontally", UsageTypes.SEL AC_DISTRIBUTE_VERTICALLY = 0x029C, "AC Distribute Vertically", UsageTypes.SEL class PowerDevice(_Data): INAME = 0x01, "iName", UsageTypes.SV PRESENT_STATUS = 0x02, "PresentStatus", UsageTypes.CL CHARGED_STATUS = 0x03, "ChangedStatus", UsageTypes.CL UPS = 0x04, "UPS", UsageTypes.CA POWER_SUPPLY = 0x05, "PowerSupply", UsageTypes.CA BATTERY_SYSTEM = 0x10, "BatterySystem", UsageTypes.CP BATTERY_SYSTEM_ID = 0x11, "BatterySystemID", UsageTypes.SV BATTERY = 0x12, "Battery", UsageTypes.CP BATTERY_ID = 0x13, "BatteryID", UsageTypes.SV CHARGER = 0x14, "Charger", UsageTypes.CP CHARGER_ID = 0x15, "ChargerID", UsageTypes.SV POWER_CONVERTER = 0x16, "PowerConverter", UsageTypes.CP POWER_CONVERTER_ID = 0x17, "PowerConverterID", UsageTypes.SV OUTLET_SYSTEM = 0x18, "OutletSystem", UsageTypes.CP OUTLET_SYSTEM_ID = 0x19, "OutletSystemID", UsageTypes.SV INPUT = 0x1A, "Input", UsageTypes.CP INPUT_ID = 0x1B, "InputID", UsageTypes.SV OUTPUT = 0x1C, "Output", UsageTypes.CP OUTPUT_ID = 0x1D, "OutputID", UsageTypes.SV FLOW = 0x1E, "Flow", UsageTypes.CP FLOW_ID = 0x1F, "FlowID", UsageTypes.SV OUTLET = 0x20, "Outlet", UsageTypes.CP OUTLET_ID = 0x21, "OutletID", UsageTypes.SV GANG = 0x22, "Gang", UsageTypes.CP GANG_ID = 0x23, "GangID", UsageTypes.SV POWER_SUMMARY = 0x24, "PowerSummary", UsageTypes.CP POWER_SUMMARY_ID = 0x25, "PowerSummaryID", UsageTypes.SV VOLTAGE = 0x30, "Voltage", UsageTypes.DV CURRENT = 0x31, "Current", UsageTypes.DV FREQUENCY = 0x32, "Frequency", UsageTypes.DV APPARENT_POWER = 0x33, "ApparentPower", UsageTypes.DV ACTIVE_POWER = 0x34, "ActivePower", UsageTypes.DV PERCENT_LOAD = 0x35, "PercentLoad", UsageTypes.DV TEMPERATURE = 0x36, "Temperature", UsageTypes.DV HUMIFITY = 0x37, "Humidity", UsageTypes.DV BAD_COUNT = 0x38, "BadCount", UsageTypes.DV CONFIG_VOLTAGE = 0x40, "ConfigVoltage", (UsageTypes.SV, UsageTypes.DV) CONFIG_CURRENT = 0x41, "ConfigCurrent", (UsageTypes.SV, UsageTypes.DV) CONFIG_FREQUENCY = 0x42, "ConfigFrequency", (UsageTypes.SV, UsageTypes.DV) CONFIG_APPARENT_POWER = 0x43, "ConfigApparentPower", (UsageTypes.SV, UsageTypes.DV) CONFIG_ACTIVE_POWER = 0x44, "ConfigActivePower", (UsageTypes.SV, UsageTypes.DV) CONFIG_PERCENT_LOAD = 0x45, "ConfigPercentLoad", (UsageTypes.SV, UsageTypes.DV) CONFIG_TEMPERATURE = 0x46, "ConfigTemperature", (UsageTypes.SV, UsageTypes.DV) CONFIG_HUMIFITY = 0x47, "ConfigHumidity", (UsageTypes.SV, UsageTypes.DV) SWITCH_ON_CONTROL = 0x50, "SwitchOnControl", UsageTypes.DV SWITCH_OFF_CONTROL = 0x51, "SwitchOffControl", UsageTypes.DV TOGGLE_CONTROL = 0x52, "ToggleControl", UsageTypes.DV LOW_VOLTAGE_TRANSFER = 0x53, "LowVoltageTransfer", UsageTypes.DV HIGH_VOLTAGE_TRANSFER = 0x54, "HighVoltageTransfer", UsageTypes.DV DELAY_BEFORE_REBOOT = 0x55, "DelayBeforeReboot", UsageTypes.DV DELAY_BEFORE_STARTUP = 0x56, "DelayBeforeStartup", UsageTypes.DV DELAY_BEFORE_SHUTDOWN = 0x57, "DelayBeforeShutdown", UsageTypes.DV TEST = 0x58, "Test", UsageTypes.DV MODULE_RESET = 0x59, "ModuleReset", UsageTypes.DV AUDIBLE_ALARM_CONTROL = 0x5A, "AudibleAlarmControl", UsageTypes.DV PRESENT = 0x60, "Present", UsageTypes.DF GOOD = 0x61, "Good", UsageTypes.DF INTERNAL_FAILURE = 0x62, "InternalFailure", UsageTypes.DF VOLTAGE_OUT_OF_RANGE = 0x63, "VoltageOutOfRange", UsageTypes.DF FREQUENCY_OUT_OF_RANGE = 0x64, "FrequencyOutOfRange", UsageTypes.DF OVERLOAD = 0x65, "Overload", UsageTypes.DF OVERCHARGED = 0x66, "OverCharged", UsageTypes.DF OVER_TEMPERATURE = 0x67, "OverTemperature", UsageTypes.DF SHUTDOWN_REQUESTED = 0x68, "ShutdownRequested", UsageTypes.DF SHUTDOWN_IMMINEBT = 0x69, "ShutdownImminent", UsageTypes.DF SWITCH_ON_OFF = 0x6B, "SwitchOn/Off", UsageTypes.DF SWITCHABLE = 0x6C, "Switchable", UsageTypes.DF USED = 0x6D, "Used", UsageTypes.DF BOOST = 0x6E, "Boost", UsageTypes.DF BUCK = 0x6F, "Buck", UsageTypes.DF INITIALIZED = 0x70, "Initialized", UsageTypes.DF TESTED = 0x71, "Tested", UsageTypes.DF AWAITING_POWER = 0x72, "AwaitingPower", UsageTypes.DF COMMUNICATION_LOST = 0x73, "CommunicationLost", UsageTypes.DF IMANUFACTURER = 0xFD, "iManufacturer", UsageTypes.SV IPRODUCT = 0xFE, "iProduct", UsageTypes.SV ISERIALNUMBER = 0xFF, "iSerialNumber", UsageTypes.SV class FIDO(_Data): U2F_AUTHENTICATOR_DEVICEM = 0x01, "U2F Authenticator Device" INPUT_REPORT_DATA = 0x20, "Input Report Data" OUTPUT_REPORT_DATA = 0x21, "Output Report Data" class UsagePages(_Data): GENERIC_DESKTOP_CONTROLS_PAGE = 0x01, "Generic Desktop Controls", GenericDesktopControls SIMULATION_CONTROLS_PAGE = 0x02, "Simulation Controls" VR_CONTROLS_PAGE = 0x03, "VR Controls" SPORT_CONTROLS_PAGE = 0x04, "Sport Controls" GAME_CONTROLS_PAGE = 0x05, "Game Controls" GENERIC_DEVICE_CONTROLS_PAGE = 0x06, "Generic Device Controls" KEYBOARD_KEYPAD_PAGE = 0x07, "Keyboard/Keypad", KeyboardKeypad LED_PAGE = 0x08, "LED", Led BUTTON_PAGE = 0x09, "Button", Button ORDINAL_PAGE = 0x0A, "Ordinal" TELEPHONY_PAGE = 0x0B, "Telephony" CONSUMER_PAGE = 0x0C, "Consumer", Consumer DIGITIZER_PAGE = 0x0D, "Digitizer" HAPTICS_PAGE = 0x0E, "Haptics" PID_PAGE = 0x0F, "PID" UNICODE_PAGE = 0x10, "Unicode" EYE_AND_HEAD_TRACKER_PAGE = 0x12, "Eye and Head Tracker" ALPHANUMERIC_DISPLAY_PAGE = 0x14, "Alphanumeric Display" SENSOR_PAGE = 0x20, "Sensor" MEDICAL_INSTRUMENTS_PAGE = 0x40, "Medical Instruments" BRAILLE_DISPLAY_PAGE = 0x41, "Braillie" LIGHTING_AND_ILLUMINATION_PAGE = 0x59, "Lighting and Illumination" USB_MONITOR_PAGE = 0x80, "USB Monitor" USB_ENUMERATED_VALUES_PAGE = 0x81, "USB Enumerated Values" VESA_VIRTUAL_CONTROLS_PAGE = 0x82, "VESA Virtual Controls" POWER_DEVICE_PAGE = 0x84, "Power Device", PowerDevice BATTERY_SYSTEM_PAGE = 0x85, "Battery System" BARCODE_SCANNER_PAGE = 0x8C, "Barcode Scanner" WEIGHING_PAGE = 0x8D, "Weighing" MSR_PAGE = 0x8E, "MSR" RESERVED_POS_PAGE = 0x8F, "Reserved POS" CAMERA_CONTROL_PAGE = 0x90, "Camera Control" ARCADE_PAGE = 0x91, "Arcade" GAMING_DEVICE_PAGE = 0x92, "Gaming Device" FIDO_ALLIANCE_PAGE = 0xF1D0, "FIDO Alliance", FIDO VENDOR_PAGE = 0xFF00, ..., 0xFFFF, "Vendor Page" Solaar-1.1.14/lib/hidapi/000077500000000000000000000000001473525213700150345ustar00rootroot00000000000000Solaar-1.1.14/lib/hidapi/__init__.py000066400000000000000000000000001473525213700171330ustar00rootroot00000000000000Solaar-1.1.14/lib/hidapi/common.py000066400000000000000000000006151473525213700167000ustar00rootroot00000000000000from __future__ import annotations import dataclasses @dataclasses.dataclass class DeviceInfo: path: str bus_id: str | None vendor_id: str product_id: str interface: str | None driver: str | None manufacturer: str | None product: str | None serial: str | None release: str | None isDevice: bool hidpp_short: str | None hidpp_long: str | None Solaar-1.1.14/lib/hidapi/hidapi_impl.py000066400000000000000000000435351473525213700176770ustar00rootroot00000000000000## Copyright (C) 2012-2013 Daniel Pavel ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. """Generic Human Interface Device API. This provides a python interface to libusb's hidapi library which, unlike udev, is available for non-linux platforms. See https://github.com/libusb/hidapi for how to obtain binaries. Parts of this code are adapted from https://github.com/apmorton/pyhidapi which is MIT licensed. """ from __future__ import annotations import atexit import ctypes import logging import platform import typing from threading import Thread from time import sleep from typing import Any from typing import Callable from hidapi.common import DeviceInfo if typing.TYPE_CHECKING: import gi gi.require_version("Gdk", "3.0") from gi.repository import GLib # NOQA: E402 logger = logging.getLogger(__name__) ACTION_ADD = "add" ACTION_REMOVE = "remove" # Global handle to hidapi _hidapi = None # hidapi binary names for various platforms _library_paths = ( "libhidapi-hidraw.so", "libhidapi-hidraw.so.0", "libhidapi-libusb.so", "libhidapi-libusb.so.0", "libhidapi-iohidmanager.so", "libhidapi-iohidmanager.so.0", "libhidapi.dylib", "hidapi.dll", "libhidapi-0.dll", ) for lib in _library_paths: try: _hidapi = ctypes.cdll.LoadLibrary(lib) break except OSError: pass else: raise ImportError(f"Unable to load hidapi library, tried: {' '.join(_library_paths)}") # Retrieve version of hdiapi library class _cHidApiVersion(ctypes.Structure): _fields_ = [ ("major", ctypes.c_int), ("minor", ctypes.c_int), ("patch", ctypes.c_int), ] _hidapi.hid_version.argtypes = [] _hidapi.hid_version.restype = ctypes.POINTER(_cHidApiVersion) _hid_version = _hidapi.hid_version() # Construct device info struct based on API version class _cDeviceInfo(ctypes.Structure): def as_dict(self): return {name: getattr(self, name) for name, _t in self._fields_ if name != "next"} # Low level hdiapi device info struct # See https://github.com/libusb/hidapi/blob/master/hidapi/hidapi.h#L143 _cDeviceInfo_fields = [ ("path", ctypes.c_char_p), ("vendor_id", ctypes.c_ushort), ("product_id", ctypes.c_ushort), ("serial_number", ctypes.c_wchar_p), ("release_number", ctypes.c_ushort), ("manufacturer_string", ctypes.c_wchar_p), ("product_string", ctypes.c_wchar_p), ("usage_page", ctypes.c_ushort), ("usage", ctypes.c_ushort), ("interface_number", ctypes.c_int), ("next", ctypes.POINTER(_cDeviceInfo)), ] if _hid_version.contents.major >= 0 and _hid_version.contents.minor >= 13: _cDeviceInfo_fields.append(("bus_type", ctypes.c_int)) _cDeviceInfo._fields_ = _cDeviceInfo_fields # Set up hidapi functions _hidapi.hid_init.argtypes = [] _hidapi.hid_init.restype = ctypes.c_int _hidapi.hid_exit.argtypes = [] _hidapi.hid_exit.restype = ctypes.c_int _hidapi.hid_enumerate.argtypes = [ctypes.c_ushort, ctypes.c_ushort] _hidapi.hid_enumerate.restype = ctypes.POINTER(_cDeviceInfo) _hidapi.hid_free_enumeration.argtypes = [ctypes.POINTER(_cDeviceInfo)] _hidapi.hid_free_enumeration.restype = None _hidapi.hid_open.argtypes = [ctypes.c_ushort, ctypes.c_ushort, ctypes.c_wchar_p] _hidapi.hid_open.restype = ctypes.c_void_p _hidapi.hid_open_path.argtypes = [ctypes.c_char_p] _hidapi.hid_open_path.restype = ctypes.c_void_p _hidapi.hid_write.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_size_t] _hidapi.hid_write.restype = ctypes.c_int _hidapi.hid_read_timeout.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_size_t, ctypes.c_int] _hidapi.hid_read_timeout.restype = ctypes.c_int _hidapi.hid_read.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_size_t] _hidapi.hid_read.restype = ctypes.c_int _hidapi.hid_get_input_report.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_size_t] _hidapi.hid_get_input_report.restype = ctypes.c_int _hidapi.hid_set_nonblocking.argtypes = [ctypes.c_void_p, ctypes.c_int] _hidapi.hid_set_nonblocking.restype = ctypes.c_int _hidapi.hid_send_feature_report.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_int] _hidapi.hid_send_feature_report.restype = ctypes.c_int _hidapi.hid_get_feature_report.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_size_t] _hidapi.hid_get_feature_report.restype = ctypes.c_int _hidapi.hid_close.argtypes = [ctypes.c_void_p] _hidapi.hid_close.restype = None _hidapi.hid_get_manufacturer_string.argtypes = [ctypes.c_void_p, ctypes.c_wchar_p, ctypes.c_size_t] _hidapi.hid_get_manufacturer_string.restype = ctypes.c_int _hidapi.hid_get_product_string.argtypes = [ctypes.c_void_p, ctypes.c_wchar_p, ctypes.c_size_t] _hidapi.hid_get_product_string.restype = ctypes.c_int _hidapi.hid_get_serial_number_string.argtypes = [ctypes.c_void_p, ctypes.c_wchar_p, ctypes.c_size_t] _hidapi.hid_get_serial_number_string.restype = ctypes.c_int _hidapi.hid_get_indexed_string.argtypes = [ctypes.c_void_p, ctypes.c_int, ctypes.c_wchar_p, ctypes.c_size_t] _hidapi.hid_get_indexed_string.restype = ctypes.c_int _hidapi.hid_error.argtypes = [ctypes.c_void_p] _hidapi.hid_error.restype = ctypes.c_wchar_p # Initialize hidapi _hidapi.hid_init() atexit.register(_hidapi.hid_exit) # Solaar opens the same device more than once which will fail unless we # allow non-exclusive opening. On windows opening with shared access is # the default, for macOS we need to set it explicitly. if platform.system() == "Darwin": _hidapi.hid_darwin_set_open_exclusive.argtypes = [ctypes.c_int] _hidapi.hid_darwin_set_open_exclusive.restype = None _hidapi.hid_darwin_set_open_exclusive(0) class HIDError(Exception): pass def _enumerate_devices(): """Returns all HID devices which are potentially useful to us""" devices = [] c_devices = _hidapi.hid_enumerate(0, 0) p = c_devices while p: devices.append(p.contents.as_dict()) p = p.contents.next _hidapi.hid_free_enumeration(c_devices) keyboard_or_mouse = {d["path"] for d in devices if d["usage_page"] == 1 and d["usage"] in (6, 2)} unique_devices = {} for device in devices: # On macOS we cannot access keyboard or mouse devices without special permissions. Since # we don't need them anyway we remove them so opening them doesn't cause errors later. if device["path"] in keyboard_or_mouse: # print(f"Ignoring keyboard or mouse device: {device}") continue # hidapi returns separate entries for each usage page of a device. # Deduplicate by path to only keep one device entry. if device["path"] not in unique_devices: unique_devices[device["path"]] = device unique_devices = unique_devices.values() # print("Unique devices:\n" + '\n'.join([f"{dev}" for dev in unique_devices])) return unique_devices # Use a separate thread to check if devices have been removed or connected class _DeviceMonitor(Thread): def __init__(self, device_callback, polling_delay=5.0): self.device_callback = device_callback self.polling_delay = polling_delay self.prev_devices = None # daemon threads are automatically killed when main thread exits super().__init__(daemon=True) def run(self): # Populate initial set of devices so startup doesn't cause any callbacks self.prev_devices = {tuple(dev.items()): dev for dev in _enumerate_devices()} # Continously enumerate devices and raise callback for changes while True: current_devices = {tuple(dev.items()): dev for dev in _enumerate_devices()} for key, device in self.prev_devices.items(): if key not in current_devices: self.device_callback(ACTION_REMOVE, device) for key, device in current_devices.items(): if key not in self.prev_devices: self.device_callback(ACTION_ADD, device) self.prev_devices = current_devices sleep(self.polling_delay) def _match( action: str, device, filter_func: Callable[[int, int, int, bool, bool], dict[str, Any]], ): """ The filter_func is used to determine whether this is a device of interest to Solaar. It is given the bus id, vendor id, and product id and returns a dictionary with the required hid_driver and usb_interface and whether this is a receiver or device. """ vid = device["vendor_id"] pid = device["product_id"] # Translate hidapi bus_type to the bus_id values Solaar expects if device.get("bus_type") == 0x01: bus_id = 0x03 # USB elif device.get("bus_type") == 0x02: bus_id = 0x05 # Bluetooth else: bus_id = None # Check for hidpp support device["hidpp_short"] = False device["hidpp_long"] = False device_handle = None try: device_handle = open_path(device["path"]) report = _get_input_report(device_handle, 0x10, 32) if len(report) == 1 + 6 and report[0] == 0x10: device["hidpp_short"] = True report = _get_input_report(device_handle, 0x11, 32) if len(report) == 1 + 19 and report[0] == 0x11: device["hidpp_long"] = True except HIDError as e: # noqa: F841 if logger.isEnabledFor(logging.INFO): logger.info(f"Error opening device {device['path']} ({bus_id}/{vid:04X}/{pid:04X}) for hidpp check: {e}") # noqa finally: if device_handle: close(device_handle) if logger.isEnabledFor(logging.INFO): logger.info( "Found device BID %s VID %04X PID %04X HID++ %s %s", bus_id, vid, pid, device["hidpp_short"], device["hidpp_long"], ) if not device["hidpp_short"] and not device["hidpp_long"]: return None filtered_result = filter_func(bus_id, vid, pid, device["hidpp_short"], device["hidpp_long"]) if not filtered_result: return is_device = filtered_result.get("isDevice") if action == ACTION_ADD: d_info = DeviceInfo( path=device["path"].decode(), bus_id=bus_id, vendor_id=f"{vid:04X}", # noqa product_id=f"{pid:04X}", # noqa interface=None, driver=None, manufacturer=device["manufacturer_string"], product=device["product_string"], serial=device["serial_number"], release=device["release_number"], isDevice=is_device, hidpp_short=device["hidpp_short"], hidpp_long=device["hidpp_long"], ) return d_info elif action == ACTION_REMOVE: d_info = DeviceInfo( path=device["path"].decode(), bus_id=None, vendor_id=f"{vid:04X}", # noqa product_id=f"{pid:04X}", # noqa interface=None, driver=None, manufacturer=None, product=None, serial=None, release=None, isDevice=is_device, hidpp_short=None, hidpp_long=None, ) return d_info def find_paired_node(receiver_path: str, index: int, timeout: int): """Find the node of a device paired with a receiver""" return None def find_paired_node_wpid(receiver_path: str, index: int): """Find the node of a device paired with a receiver, get wpid from udev""" return None def monitor_glib( glib: GLib, callback: Callable, filter_func: Callable[[int, int, int, bool, bool], dict[str, Any]], ) -> None: """Monitor GLib. Parameters ---------- glib GLib instance. callback Called when device found. filter_func Filter devices callback. """ def device_callback(action: str, device): if action == ACTION_ADD: d_info = _match(action, device, filter_func) if d_info: glib.idle_add(callback, action, d_info) elif action == ACTION_REMOVE: # Removed devices will be detected by Solaar directly pass monitor = _DeviceMonitor(device_callback=device_callback) monitor.start() def enumerate(filter_func) -> DeviceInfo: """Enumerate the HID Devices. List all the HID devices attached to the system, optionally filtering by vendor_id, product_id, and/or interface_number. :returns: a list of matching ``DeviceInfo`` tuples. """ for device in _enumerate_devices(): d_info = _match(ACTION_ADD, device, filter_func) if d_info: yield d_info def open(vendor_id, product_id, serial=None): """Open a HID device by its Vendor ID, Product ID and optional serial number. If no serial is provided, the first device with the specified IDs is opened. :returns: an opaque device handle, or ``None``. """ if serial is not None: serial = ctypes.create_unicode_buffer(serial) device_handle = _hidapi.hid_open(vendor_id, product_id, serial) if device_handle is None: raise HIDError(_hidapi.hid_error(None)) return device_handle def open_path(device_path) -> Any: """Open a HID device by its path name. :param device_path: the path of a ``DeviceInfo`` tuple returned by enumerate(). :returns: an opaque device handle, or ``None``. """ if not isinstance(device_path, bytes): device_path = device_path.encode() device_handle = _hidapi.hid_open_path(device_path) if device_handle is None: raise HIDError(_hidapi.hid_error(None)) return device_handle def close(device_handle) -> None: """Close a HID device. :param device_handle: a device handle returned by open() or open_path(). """ assert device_handle _hidapi.hid_close(device_handle) def write(device_handle: int, data: bytes) -> int: """Write an Output report to a HID device. :param device_handle: a device handle returned by open() or open_path(). :param data: the data bytes to send including the report number as the first byte. The first byte of data[] must contain the Report ID. For devices which only support a single report, this must be set to 0x0. The remaining bytes contain the report data. Since the Report ID is mandatory, calls to hid_write() will always contain one more byte than the report contains. For example, if a hid report is 16 bytes long, 17 bytes must be passed to hid_write(), the Report ID (or 0x0, for devices with a single report), followed by the report data (16 bytes). In this example, the length passed in would be 17. write() will send the data on the first OUT endpoint, if one exists. If it does not, it will send the data through the Control Endpoint (Endpoint 0). """ assert device_handle assert data assert isinstance(data, bytes), (repr(data), type(data)) bytes_written = _hidapi.hid_write(device_handle, data, len(data)) if bytes_written < 0: raise HIDError(_hidapi.hid_error(device_handle)) return bytes_written def read(device_handle, bytes_count, timeout_ms=None): """Read an Input report from a HID device. :param device_handle: a device handle returned by open() or open_path(). :param bytes_count: maximum number of bytes to read. :param timeout_ms: can be -1 (default) to wait for data indefinitely, 0 to read whatever is in the device's input buffer, or a positive integer to wait that many milliseconds. Input reports are returned to the host through the INTERRUPT IN endpoint. The first byte will contain the Report number if the device uses numbered reports. :returns: the data packet read, an empty bytes string if a timeout was reached, or None if there was an error while reading. """ assert device_handle data = ctypes.create_string_buffer(bytes_count) if timeout_ms is None or timeout_ms < 0: bytes_read = _hidapi.hid_read(device_handle, data, bytes_count) else: bytes_read = _hidapi.hid_read_timeout(device_handle, data, bytes_count, timeout_ms) if bytes_read < 0: raise HIDError(_hidapi.hid_error(device_handle)) return data.raw[:bytes_read] def _get_input_report(device_handle, report_id, size): assert device_handle data = ctypes.create_string_buffer(size) data[0] = bytearray((report_id,)) size = _hidapi.hid_get_input_report(device_handle, data, size) if size < 0: raise HIDError(_hidapi.hid_error(device_handle)) return data.raw[:size] def _readstring(device_handle, func, max_length=255): assert device_handle buf = ctypes.create_unicode_buffer(max_length) ret = func(device_handle, buf, max_length) if ret < 0: raise HIDError("Error reading device property") return buf.value def get_manufacturer(device_handle): """Get the Manufacturer String from a HID device. :param device_handle: a device handle returned by open() or open_path(). """ return _readstring(device_handle, _hidapi.get_manufacturer_string) def get_product(device_handle): """Get the Product String from a HID device. :param device_handle: a device handle returned by open() or open_path(). """ return _readstring(device_handle, _hidapi.get_product_string) def get_serial(device_handle): """Get the serial number from a HID device. :param device_handle: a device handle returned by open() or open_path(). """ return _readstring(device_handle, _hidapi.get_serial_number_string) Solaar-1.1.14/lib/hidapi/hidconsole.py000066400000000000000000000172111473525213700175370ustar00rootroot00000000000000## Copyright (C) 2012-2013 Daniel Pavel ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import argparse import os import os.path import platform import readline import sys import time from binascii import hexlify from binascii import unhexlify from select import select from threading import Lock from threading import Thread if platform.system() == "Linux": import hidapi.udev_impl as hidapi else: import hidapi.hidapi_impl as hidapi LOGITECH_VENDOR_ID = 0x046D interactive = os.isatty(0) prompt = "?? Input: " if interactive else "" start_time = time.time() def strhex(d): return hexlify(d).decode("ascii").upper() print_lock = Lock() def _print(marker, data, scroll=False): t = time.time() - start_time if isinstance(data, str): s = marker + " " + data else: hexs = strhex(data) s = "%s (% 8.3f) [%s %s %s %s] %s" % (marker, t, hexs[0:2], hexs[2:4], hexs[4:8], hexs[8:], repr(data)) with print_lock: # allow only one thread at a time to write to the console, otherwise # the output gets garbled, especially with ANSI codes. if interactive and scroll: # scroll the entire screen above the current line up by 1 line sys.stdout.write( "\033[s" # save cursor position "\033[S" # scroll up "\033[A" # cursor up "\033[L" # insert 1 line "\033[G" ) # move cursor to column 1 sys.stdout.write(s) if interactive and scroll: # restore cursor position sys.stdout.write("\033[u") else: sys.stdout.write("\n") # flush stdout manually... # because trying to open stdin/out unbuffered programmatically # works much too differently in Python 2/3 sys.stdout.flush() def _error(text, scroll=False): _print("!!", text, scroll) def _continuous_read(handle, timeout=2000): while True: try: reply = hidapi.read(handle, 128, timeout) except OSError as e: _error("Read failed, aborting: " + str(e), True) break assert reply is not None if reply: _print(">>", reply, True) def _validate_input(line, hidpp=False): try: data = unhexlify(line.encode("ascii")) except Exception as e: _error("Invalid input: " + str(e)) return None if hidpp: if len(data) < 4: _error("Invalid HID++ request: need at least 4 bytes") return None if data[:1] not in b"\x10\x11": _error("Invalid HID++ request: first byte must be 0x10 or 0x11") return None if data[1:2] not in b"\xff\x00\x01\x02\x03\x04\x05\x06\x07": _error("Invalid HID++ request: second byte must be 0xFF or one of 0x00..0x07") return None if data[:1] == b"\x10": if len(data) > 7: _error("Invalid HID++ request: maximum length of a 0x10 request is 7 bytes") return None while len(data) < 7: data = (data + b"\x00" * 7)[:7] elif data[:1] == b"\x11": if len(data) > 20: _error("Invalid HID++ request: maximum length of a 0x11 request is 20 bytes") return None while len(data) < 20: data = (data + b"\x00" * 20)[:20] return data def _open(args): def matchfn(bid, vid, pid, _a, _b): if vid == LOGITECH_VENDOR_ID: return {"vid": vid} device = args.device if args.hidpp and not device: for d in hidapi.enumerate(matchfn): if d.driver == "logitech-djreceiver": device = d.path break if not device: sys.exit("!! No HID++ receiver found.") if not device: sys.exit("!! Device path required.") print(".. Opening device", device) handle = hidapi.open_path(device) if not handle: sys.exit(f"!! Failed to open {device}, aborting.") print( ".. Opened handle %r, vendor %r product %r serial %r." % (handle, hidapi.get_manufacturer(handle), hidapi.get_product(handle), hidapi.get_serial(handle)) ) if args.hidpp: if hidapi.get_manufacturer(handle) is not None and hidapi.get_manufacturer(handle) != b"Logitech": sys.exit("!! Only Logitech devices support the HID++ protocol.") print(".. HID++ validation enabled.") else: if hidapi.get_manufacturer(handle) == b"Logitech" and b"Receiver" in hidapi.get_product(handle): args.hidpp = True print(".. Logitech receiver detected, HID++ validation enabled.") return handle def _parse_arguments(): arg_parser = argparse.ArgumentParser() arg_parser.add_argument("--history", help="history file (default ~/.hidconsole-history)") arg_parser.add_argument("--hidpp", action="store_true", help="ensure input data is a valid HID++ request") arg_parser.add_argument( "device", nargs="?", help="linux device to connect to (/dev/hidrawX); " "may be omitted if --hidpp is given, in which case it looks for the first Logitech receiver", ) return arg_parser.parse_args() def main(): args = _parse_arguments() handle = _open(args) if interactive: print(".. Press ^C/^D to exit, or type hex bytes to write to the device.") if args.history is None: args.history = os.path.join(os.path.expanduser("~"), ".hidconsole-history") try: readline.read_history_file(args.history) except Exception: # file may not exist yet pass try: t = Thread(target=_continuous_read, args=(handle,)) t.daemon = True t.start() if interactive: # move the cursor at the bottom of the screen sys.stdout.write("\033[300B") # move cusor at most 300 lines down, don't scroll while t.is_alive(): line = input(prompt) line = line.strip().replace(" ", "") # print ("line", line) if not line: continue data = _validate_input(line, args.hidpp) if data is None: continue _print("<<", data) hidapi.write(handle, data) # wait for some kind of reply if args.hidpp and not interactive: rlist, wlist, xlist = select([handle], [], [], 1) if data[1:2] == b"\xff": # the receiver will reply very fast, in a few milliseconds time.sleep(0.010) else: # the devices might reply quite slow time.sleep(0.700) except EOFError: if interactive: print("") else: time.sleep(1) finally: print(f".. Closing handle {handle!r}") hidapi.close(handle) if interactive: readline.write_history_file(args.history) if __name__ == "__main__": main() Solaar-1.1.14/lib/hidapi/udev_impl.py000066400000000000000000000375711473525213700174070ustar00rootroot00000000000000## Copyright (C) 2012-2013 Daniel Pavel ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. """Generic Human Interface Device API. It is currently a partial pure-Python implementation of the native HID API implemented by signal11 (https://github.com/signal11/hidapi), and requires ``pyudev``. The docstrings are mostly copied from the hidapi API header, with changes where necessary. """ from __future__ import annotations import errno import logging import os import typing import warnings # the tuple object we'll expose when enumerating devices from select import select from time import sleep from time import time from typing import Callable import pyudev from hidapi.common import DeviceInfo if typing.TYPE_CHECKING: import gi gi.require_version("Gdk", "3.0") from gi.repository import GLib # NOQA: E402 logger = logging.getLogger(__name__) fileopen = open ACTION_ADD = "add" ACTION_REMOVE = "remove" # # exposed API # docstrings mostly copied from hidapi.h # def init(): """This function is a no-op, and exists only to match the native hidapi implementation. :returns: ``True``. """ return True def exit(): """This function is a no-op, and exists only to match the native hidapi implementation. :returns: ``True``. """ return True def _match(action: str, device, filter_func: typing.Callable[[int, int, int, bool, bool], dict[str, typing.Any]]): """ The filter_func is used to determine whether this is a device of interest to Solaar. It is given the bus id, vendor id, and product id and returns a dictionary with the required hid_driver and usb_interface and whether this is a receiver or device.""" if logger.isEnabledFor(logging.DEBUG): logger.debug(f"Dbus event {action} {device}") hid_device = device.find_parent("hid") if hid_device is None: # only HID devices are of interest to Solaar return hid_id = hid_device.properties.get("HID_ID") if not hid_id: return # there are reports that sometimes the id isn't set up right so be defensive bid, vid, pid = hid_id.split(":") hid_hid_device = hid_device.find_parent("hid") if hid_hid_device is not None: return # these are devices connected through a receiver so don't pick them up here try: # if report descriptor does not indicate HID++ capabilities then this device is not of interest to Solaar from hid_parser import ReportDescriptor hidpp_short = hidpp_long = False devfile = "/sys" + hid_device.properties.get("DEVPATH") + "/report_descriptor" with fileopen(devfile, "rb") as fd: with warnings.catch_warnings(): warnings.simplefilter("ignore") rd = ReportDescriptor(fd.read()) hidpp_short = 0x10 in rd.input_report_ids and 6 * 8 == int(rd.get_input_report_size(0x10)) # and _Usage(0xFF00, 0x0001) in rd.get_input_items(0x10)[0].usages # be more permissive hidpp_long = 0x11 in rd.input_report_ids and 19 * 8 == int(rd.get_input_report_size(0x11)) # and _Usage(0xFF00, 0x0002) in rd.get_input_items(0x11)[0].usages # be more permissive if not hidpp_short and not hidpp_long: return except Exception as e: # if can't process report descriptor fall back to old scheme hidpp_short = None hidpp_long = None logger.info( "Report Descriptor not processed for DEVICE %s BID %s VID %s PID %s: %s", device.device_node, bid, vid, pid, e, ) filtered_result = filter_func(int(bid, 16), int(vid, 16), int(pid, 16), hidpp_short, hidpp_long) if not filtered_result: return interface_number = filtered_result.get("usb_interface") isDevice = filtered_result.get("isDevice") if action == ACTION_ADD: hid_driver_name = hid_device.properties.get("DRIVER") intf_device = device.find_parent("usb", "usb_interface") usb_interface = None if intf_device is None else intf_device.attributes.asint("bInterfaceNumber") # print('*** usb interface', action, device, 'usb_interface:', intf_device, usb_interface, interface_number) if logger.isEnabledFor(logging.INFO): logger.info( "Found device %s BID %s VID %s PID %s HID++ %s %s USB %s %s", device.device_node, bid, vid, pid, hidpp_short, hidpp_long, usb_interface, interface_number, ) if not (hidpp_short or hidpp_long or interface_number is None or interface_number == usb_interface): return attrs = intf_device.attributes if intf_device is not None else None d_info = DeviceInfo( path=device.device_node, bus_id=int(bid, 16), vendor_id=vid[-4:], product_id=pid[-4:], interface=usb_interface, driver=hid_driver_name, manufacturer=attrs.get("manufacturer") if attrs else None, product=attrs.get("product") if attrs else None, serial=hid_device.properties.get("HID_UNIQ"), release=attrs.get("bcdDevice") if attrs else None, isDevice=isDevice, hidpp_short=hidpp_short, hidpp_long=hidpp_long, ) return d_info elif action == ACTION_REMOVE: d_info = DeviceInfo( path=device.device_node, bus_id=None, vendor_id=vid[-4:], product_id=pid[-4:], interface=None, driver=None, manufacturer=None, product=None, serial=None, release=None, isDevice=isDevice, hidpp_short=None, hidpp_long=None, ) return d_info def find_paired_node(receiver_path: str, index: int, timeout: int): """Find the node of a device paired with a receiver""" context = pyudev.Context() receiver_phys = pyudev.Devices.from_device_file(context, receiver_path).find_parent("hid").get("HID_PHYS") if not receiver_phys: return None phys = f"{receiver_phys}:{index}" # noqa: E231 timeout += time() delta = time() while delta < timeout: for dev in context.list_devices(subsystem="hidraw"): dev_phys = dev.find_parent("hid").get("HID_PHYS") if dev_phys and dev_phys == phys: return dev.device_node delta = time() return None def find_paired_node_wpid(receiver_path: str, index: int): """Find the node of a device paired with a receiver, get wpid from udev""" context = pyudev.Context() receiver_phys = pyudev.Devices.from_device_file(context, receiver_path).find_parent("hid").get("HID_PHYS") if not receiver_phys: return None phys = f"{receiver_phys}:{index}" # noqa: E231 for dev in context.list_devices(subsystem="hidraw"): dev_phys = dev.find_parent("hid").get("HID_PHYS") if dev_phys and dev_phys == phys: # get hid id like 0003:0000046D:00000065 hid_id = dev.find_parent("hid").get("HID_ID") # get wpid - last 4 symbols udev_wpid = hid_id[-4:] return udev_wpid return None def monitor_glib(glib: GLib, callback: Callable, filter_func: Callable): """Monitor GLib. Parameters ---------- glib GLib instance. """ c = pyudev.Context() m = pyudev.Monitor.from_netlink(c) m.filter_by(subsystem="hidraw") def _process_udev_event(monitor, condition, cb, filter_func): if condition == glib.IO_IN: event = monitor.receive_device() if event: action, device = event # print ("***", action, device) if action == ACTION_ADD: d_info = _match(action, device, filter_func) if d_info: glib.idle_add(cb, action, d_info) elif action == ACTION_REMOVE: # the GLib notification does _not_ match! pass return True try: # io_add_watch_full may not be available... glib.io_add_watch_full(m, glib.PRIORITY_LOW, glib.IO_IN, _process_udev_event, callback, filter_func) except AttributeError: try: # and the priority parameter appeared later in the API glib.io_add_watch(m, glib.PRIORITY_LOW, glib.IO_IN, _process_udev_event, callback, filter_func) except Exception: glib.io_add_watch(m, glib.IO_IN, _process_udev_event, callback, filter_func) if logger.isEnabledFor(logging.DEBUG): logger.debug("Starting dbus monitoring") m.start() def enumerate(filter_func: typing.Callable[[int, int, int, bool, bool], dict[str, typing.Any]]): """Enumerate the HID Devices. List all the HID devices attached to the system, optionally filtering by vendor_id, product_id, and/or interface_number. :returns: a list of matching ``DeviceInfo`` tuples. """ if logger.isEnabledFor(logging.DEBUG): logger.debug("Starting dbus enumeration") for dev in pyudev.Context().list_devices(subsystem="hidraw"): dev_info = _match(ACTION_ADD, dev, filter_func) if dev_info: yield dev_info def open(vendor_id, product_id, serial=None): """Open a HID device by its Vendor ID, Product ID and optional serial number. If no serial is provided, the first device with the specified IDs is opened. :returns: an opaque device handle, or ``None``. """ def matchfn(bid, vid, pid): return vid == vendor_id and pid == product_id for device in enumerate(matchfn): if serial is None or serial == device.serial: return open_path(device.path) def open_path(device_path): """Open a HID device by its path name. :param device_path: the path of a ``DeviceInfo`` tuple returned by enumerate(). :returns: an opaque device handle, or ``None``. """ assert device_path assert device_path.startswith("/dev/hidraw") logger.info("OPEN PATH %s", device_path) retrycount = 0 while retrycount < 3: retrycount += 1 try: return os.open(device_path, os.O_RDWR | os.O_SYNC) except OSError as e: logger.info("OPEN PATH FAILED %s ERROR %s %s", device_path, e.errno, e) if e.errno == errno.EACCES: sleep(0.1) else: raise def close(device_handle) -> None: """Close a HID device. :param device_handle: a device handle returned by open() or open_path(). """ assert device_handle os.close(device_handle) def write(device_handle, data): """Write an Output report to a HID device. :param device_handle: a device handle returned by open() or open_path(). :param data: the data bytes to send including the report number as the first byte. The first byte of data[] must contain the Report ID. For devices which only support a single report, this must be set to 0x0. The remaining bytes contain the report data. Since the Report ID is mandatory, calls to hid_write() will always contain one more byte than the report contains. For example, if a hid report is 16 bytes long, 17 bytes must be passed to hid_write(), the Report ID (or 0x0, for devices with a single report), followed by the report data (16 bytes). In this example, the length passed in would be 17. write() will send the data on the first OUT endpoint, if one exists. If it does not, it will send the data through the Control Endpoint (Endpoint 0). """ assert device_handle assert data assert isinstance(data, bytes), (repr(data), type(data)) retrycount = 0 bytes_written = 0 while retrycount < 3: try: retrycount += 1 bytes_written = os.write(device_handle, data) except OSError as e: if e.errno == errno.EPIPE: sleep(0.1) else: break if bytes_written != len(data): raise OSError(errno.EIO, f"written {int(bytes_written)} bytes out of expected {len(data)}") def read(device_handle, bytes_count, timeout_ms=-1): """Read an Input report from a HID device. :param device_handle: a device handle returned by open() or open_path(). :param bytes_count: maximum number of bytes to read. :param timeout_ms: can be -1 (default) to wait for data indefinitely, 0 to read whatever is in the device's input buffer, or a positive integer to wait that many milliseconds. Input reports are returned to the host through the INTERRUPT IN endpoint. The first byte will contain the Report number if the device uses numbered reports. :returns: the data packet read, an empty bytes string if a timeout was reached, or None if there was an error while reading. """ assert device_handle timeout = None if timeout_ms < 0 else timeout_ms / 1000.0 rlist, wlist, xlist = select([device_handle], [], [device_handle], timeout) if xlist: assert xlist == [device_handle] raise OSError(errno.EIO, f"exception on file descriptor {int(device_handle)}") if rlist: assert rlist == [device_handle] data = os.read(device_handle, bytes_count) assert data is not None assert isinstance(data, bytes), (repr(data), type(data)) return data else: return b"" _DEVICE_STRINGS = { 0: "manufacturer", 1: "product", 2: "serial", } def get_manufacturer(device_handle): """Get the Manufacturer String from a HID device. :param device_handle: a device handle returned by open() or open_path(). """ return get_indexed_string(device_handle, 0) def get_product(device_handle): """Get the Product String from a HID device. :param device_handle: a device handle returned by open() or open_path(). """ return get_indexed_string(device_handle, 1) def get_serial(device_handle): """Get the serial number from a HID device. :param device_handle: a device handle returned by open() or open_path(). """ serial = get_indexed_string(device_handle, 2) return serial def get_indexed_string(device_handle, index): """Get a string from a HID device, based on its string index. Note: currently not working in the ``hidraw`` native implementation. :param device_handle: a device handle returned by open() or open_path(). :param index: the index of the string to get. :returns: the value corresponding to index, or None if no value found :rtype: bytes or NoneType """ try: key = _DEVICE_STRINGS[index] except KeyError: return None assert device_handle stat = os.fstat(device_handle) try: dev = pyudev.Devices.from_device_number(pyudev.Context(), "char", stat.st_rdev) except (pyudev.DeviceNotFoundError, ValueError): return None hid_dev = dev.find_parent("hid") if hid_dev: assert "HID_ID" in hid_dev bus, _ignore, _ignore = hid_dev["HID_ID"].split(":") if bus == "0003": # USB usb_dev = dev.find_parent("usb", "usb_device") assert usb_dev return usb_dev.attributes.get(key) elif bus == "0005": # BLUETOOTH # TODO pass Solaar-1.1.14/lib/keysyms/000077500000000000000000000000001473525213700153025ustar00rootroot00000000000000Solaar-1.1.14/lib/keysyms/__init__.py000066400000000000000000000000001473525213700174010ustar00rootroot00000000000000Solaar-1.1.14/lib/keysyms/generate.py000077500000000000000000000023171473525213700174540ustar00rootroot00000000000000#!/usr/bin/env python3 """Extract key symbol encodings from X11 header files.""" from pathlib import Path from pprint import pprint from re import findall from subprocess import run from tempfile import TemporaryDirectory repo = "https://gitlab.freedesktop.org/xorg/proto/xorgproto.git" pattern = r"#define XK_(\w+)\s+0x(\w+)(?:\s+/\*\s+U\+(\w+))?" xf86pattern = r"#define XF86XK_(\w+)\s+0x(\w+)(?:\s+/\*\s+U\+(\w+))?" def main(): keysymdef = {} keysym_files = [ ("include/X11/keysymdef.h", pattern, ""), ("include/X11/XF86keysym.h", xf86pattern, "XF86_"), ] with TemporaryDirectory() as temp: run(["git", "clone", repo, "."], cwd=temp) for filename, extraction_pattern, prefix in keysym_files: text = Path(temp, filename).read_text() for name, sym, _ in findall(extraction_pattern, text): sym = int(sym, 16) if keysymdef.get(f"{prefix}{name}", None): print(f"KEY DUP {prefix}{name}") keysymdef[f"{prefix}{name}"] = sym with open("keysymdef.py", "w") as f: f.write("# flake8: noqa\nkey_symbols = \\\n") pprint(keysymdef, f) if __name__ == "__main__": main() Solaar-1.1.14/lib/keysyms/keysymdef.py000066400000000000000000001705701473525213700176660ustar00rootroot00000000000000# flake8: noqa key_symbols = { "0": 48, "1": 49, "2": 50, "3": 51, "3270_AltCursor": 64784, "3270_Attn": 64782, "3270_BackTab": 64773, "3270_ChangeScreen": 64793, "3270_Copy": 64789, "3270_CursorBlink": 64783, "3270_CursorSelect": 64796, "3270_DeleteWord": 64794, "3270_Duplicate": 64769, "3270_Enter": 64798, "3270_EraseEOF": 64774, "3270_EraseInput": 64775, "3270_ExSelect": 64795, "3270_FieldMark": 64770, "3270_Ident": 64787, "3270_Jump": 64786, "3270_KeyClick": 64785, "3270_Left2": 64772, "3270_PA1": 64778, "3270_PA2": 64779, "3270_PA3": 64780, "3270_Play": 64790, "3270_PrintScreen": 64797, "3270_Quit": 64777, "3270_Record": 64792, "3270_Reset": 64776, "3270_Right2": 64771, "3270_Rule": 64788, "3270_Setup": 64791, "3270_Test": 64781, "4": 52, "5": 53, "6": 54, "7": 55, "8": 56, "9": 57, "A": 65, "AE": 198, "Aacute": 193, "Abelowdot": 16785056, "Abreve": 451, "Abreveacute": 16785070, "Abrevebelowdot": 16785078, "Abrevegrave": 16785072, "Abrevehook": 16785074, "Abrevetilde": 16785076, "AccessX_Enable": 65136, "AccessX_Feedback_Enable": 65137, "Acircumflex": 194, "Acircumflexacute": 16785060, "Acircumflexbelowdot": 16785068, "Acircumflexgrave": 16785062, "Acircumflexhook": 16785064, "Acircumflextilde": 16785066, "Adiaeresis": 196, "Agrave": 192, "Ahook": 16785058, "Alt_L": 65513, "Alt_R": 65514, "Amacron": 960, "Aogonek": 417, "Arabic_0": 16778848, "Arabic_1": 16778849, "Arabic_2": 16778850, "Arabic_3": 16778851, "Arabic_4": 16778852, "Arabic_5": 16778853, "Arabic_6": 16778854, "Arabic_7": 16778855, "Arabic_8": 16778856, "Arabic_9": 16778857, "Arabic_ain": 1497, "Arabic_alef": 1479, "Arabic_alefmaksura": 1513, "Arabic_beh": 1480, "Arabic_comma": 1452, "Arabic_dad": 1494, "Arabic_dal": 1487, "Arabic_damma": 1519, "Arabic_dammatan": 1516, "Arabic_ddal": 16778888, "Arabic_farsi_yeh": 16778956, "Arabic_fatha": 1518, "Arabic_fathatan": 1515, "Arabic_feh": 1505, "Arabic_fullstop": 16778964, "Arabic_gaf": 16778927, "Arabic_ghain": 1498, "Arabic_ha": 1511, "Arabic_hah": 1485, "Arabic_hamza": 1473, "Arabic_hamza_above": 16778836, "Arabic_hamza_below": 16778837, "Arabic_hamzaonalef": 1475, "Arabic_hamzaonwaw": 1476, "Arabic_hamzaonyeh": 1478, "Arabic_hamzaunderalef": 1477, "Arabic_heh": 1511, "Arabic_heh_doachashmee": 16778942, "Arabic_heh_goal": 16778945, "Arabic_jeem": 1484, "Arabic_jeh": 16778904, "Arabic_kaf": 1507, "Arabic_kasra": 1520, "Arabic_kasratan": 1517, "Arabic_keheh": 16778921, "Arabic_khah": 1486, "Arabic_lam": 1508, "Arabic_madda_above": 16778835, "Arabic_maddaonalef": 1474, "Arabic_meem": 1509, "Arabic_noon": 1510, "Arabic_noon_ghunna": 16778938, "Arabic_peh": 16778878, "Arabic_percent": 16778858, "Arabic_qaf": 1506, "Arabic_question_mark": 1471, "Arabic_ra": 1489, "Arabic_rreh": 16778897, "Arabic_sad": 1493, "Arabic_seen": 1491, "Arabic_semicolon": 1467, "Arabic_shadda": 1521, "Arabic_sheen": 1492, "Arabic_sukun": 1522, "Arabic_superscript_alef": 16778864, "Arabic_switch": 65406, "Arabic_tah": 1495, "Arabic_tatweel": 1504, "Arabic_tcheh": 16778886, "Arabic_teh": 1482, "Arabic_tehmarbuta": 1481, "Arabic_thal": 1488, "Arabic_theh": 1483, "Arabic_tteh": 16778873, "Arabic_veh": 16778916, "Arabic_waw": 1512, "Arabic_yeh": 1514, "Arabic_yeh_baree": 16778962, "Arabic_zah": 1496, "Arabic_zain": 1490, "Aring": 197, "Armenian_AT": 16778552, "Armenian_AYB": 16778545, "Armenian_BEN": 16778546, "Armenian_CHA": 16778569, "Armenian_DA": 16778548, "Armenian_DZA": 16778561, "Armenian_E": 16778551, "Armenian_FE": 16778582, "Armenian_GHAT": 16778562, "Armenian_GIM": 16778547, "Armenian_HI": 16778565, "Armenian_HO": 16778560, "Armenian_INI": 16778555, "Armenian_JE": 16778571, "Armenian_KE": 16778580, "Armenian_KEN": 16778559, "Armenian_KHE": 16778557, "Armenian_LYUN": 16778556, "Armenian_MEN": 16778564, "Armenian_NU": 16778566, "Armenian_O": 16778581, "Armenian_PE": 16778570, "Armenian_PYUR": 16778579, "Armenian_RA": 16778572, "Armenian_RE": 16778576, "Armenian_SE": 16778573, "Armenian_SHA": 16778567, "Armenian_TCHE": 16778563, "Armenian_TO": 16778553, "Armenian_TSA": 16778558, "Armenian_TSO": 16778577, "Armenian_TYUN": 16778575, "Armenian_VEV": 16778574, "Armenian_VO": 16778568, "Armenian_VYUN": 16778578, "Armenian_YECH": 16778549, "Armenian_ZA": 16778550, "Armenian_ZHE": 16778554, "Armenian_accent": 16778587, "Armenian_amanak": 16778588, "Armenian_apostrophe": 16778586, "Armenian_at": 16778600, "Armenian_ayb": 16778593, "Armenian_ben": 16778594, "Armenian_but": 16778589, "Armenian_cha": 16778617, "Armenian_da": 16778596, "Armenian_dza": 16778609, "Armenian_e": 16778599, "Armenian_exclam": 16778588, "Armenian_fe": 16778630, "Armenian_full_stop": 16778633, "Armenian_ghat": 16778610, "Armenian_gim": 16778595, "Armenian_hi": 16778613, "Armenian_ho": 16778608, "Armenian_hyphen": 16778634, "Armenian_ini": 16778603, "Armenian_je": 16778619, "Armenian_ke": 16778628, "Armenian_ken": 16778607, "Armenian_khe": 16778605, "Armenian_ligature_ew": 16778631, "Armenian_lyun": 16778604, "Armenian_men": 16778612, "Armenian_nu": 16778614, "Armenian_o": 16778629, "Armenian_paruyk": 16778590, "Armenian_pe": 16778618, "Armenian_pyur": 16778627, "Armenian_question": 16778590, "Armenian_ra": 16778620, "Armenian_re": 16778624, "Armenian_se": 16778621, "Armenian_separation_mark": 16778589, "Armenian_sha": 16778615, "Armenian_shesht": 16778587, "Armenian_tche": 16778611, "Armenian_to": 16778601, "Armenian_tsa": 16778606, "Armenian_tso": 16778625, "Armenian_tyun": 16778623, "Armenian_verjaket": 16778633, "Armenian_vev": 16778622, "Armenian_vo": 16778616, "Armenian_vyun": 16778626, "Armenian_yech": 16778597, "Armenian_yentamna": 16778634, "Armenian_za": 16778598, "Armenian_zhe": 16778602, "Atilde": 195, "AudibleBell_Enable": 65146, "B": 66, "Babovedot": 16784898, "BackSpace": 65288, "Begin": 65368, "BounceKeys_Enable": 65140, "Break": 65387, "Byelorussian_SHORTU": 1726, "Byelorussian_shortu": 1710, "C": 67, "CH": 65186, "C_H": 65189, "C_h": 65188, "Cabovedot": 709, "Cacute": 454, "Cancel": 65385, "Caps_Lock": 65509, "Ccaron": 456, "Ccedilla": 199, "Ccircumflex": 710, "Ch": 65185, "Clear": 65291, "Codeinput": 65335, "ColonSign": 16785569, "Control_L": 65507, "Control_R": 65508, "CruzeiroSign": 16785570, "Cyrillic_A": 1761, "Cyrillic_BE": 1762, "Cyrillic_CHE": 1790, "Cyrillic_CHE_descender": 16778422, "Cyrillic_CHE_vertstroke": 16778424, "Cyrillic_DE": 1764, "Cyrillic_DZHE": 1727, "Cyrillic_E": 1788, "Cyrillic_EF": 1766, "Cyrillic_EL": 1772, "Cyrillic_EM": 1773, "Cyrillic_EN": 1774, "Cyrillic_EN_descender": 16778402, "Cyrillic_ER": 1778, "Cyrillic_ES": 1779, "Cyrillic_GHE": 1767, "Cyrillic_GHE_bar": 16778386, "Cyrillic_HA": 1768, "Cyrillic_HARDSIGN": 1791, "Cyrillic_HA_descender": 16778418, "Cyrillic_I": 1769, "Cyrillic_IE": 1765, "Cyrillic_IO": 1715, "Cyrillic_I_macron": 16778466, "Cyrillic_JE": 1720, "Cyrillic_KA": 1771, "Cyrillic_KA_descender": 16778394, "Cyrillic_KA_vertstroke": 16778396, "Cyrillic_LJE": 1721, "Cyrillic_NJE": 1722, "Cyrillic_O": 1775, "Cyrillic_O_bar": 16778472, "Cyrillic_PE": 1776, "Cyrillic_SCHWA": 16778456, "Cyrillic_SHA": 1787, "Cyrillic_SHCHA": 1789, "Cyrillic_SHHA": 16778426, "Cyrillic_SHORTI": 1770, "Cyrillic_SOFTSIGN": 1784, "Cyrillic_TE": 1780, "Cyrillic_TSE": 1763, "Cyrillic_U": 1781, "Cyrillic_U_macron": 16778478, "Cyrillic_U_straight": 16778414, "Cyrillic_U_straight_bar": 16778416, "Cyrillic_VE": 1783, "Cyrillic_YA": 1777, "Cyrillic_YERU": 1785, "Cyrillic_YU": 1760, "Cyrillic_ZE": 1786, "Cyrillic_ZHE": 1782, "Cyrillic_ZHE_descender": 16778390, "Cyrillic_a": 1729, "Cyrillic_be": 1730, "Cyrillic_che": 1758, "Cyrillic_che_descender": 16778423, "Cyrillic_che_vertstroke": 16778425, "Cyrillic_de": 1732, "Cyrillic_dzhe": 1711, "Cyrillic_e": 1756, "Cyrillic_ef": 1734, "Cyrillic_el": 1740, "Cyrillic_em": 1741, "Cyrillic_en": 1742, "Cyrillic_en_descender": 16778403, "Cyrillic_er": 1746, "Cyrillic_es": 1747, "Cyrillic_ghe": 1735, "Cyrillic_ghe_bar": 16778387, "Cyrillic_ha": 1736, "Cyrillic_ha_descender": 16778419, "Cyrillic_hardsign": 1759, "Cyrillic_i": 1737, "Cyrillic_i_macron": 16778467, "Cyrillic_ie": 1733, "Cyrillic_io": 1699, "Cyrillic_je": 1704, "Cyrillic_ka": 1739, "Cyrillic_ka_descender": 16778395, "Cyrillic_ka_vertstroke": 16778397, "Cyrillic_lje": 1705, "Cyrillic_nje": 1706, "Cyrillic_o": 1743, "Cyrillic_o_bar": 16778473, "Cyrillic_pe": 1744, "Cyrillic_schwa": 16778457, "Cyrillic_sha": 1755, "Cyrillic_shcha": 1757, "Cyrillic_shha": 16778427, "Cyrillic_shorti": 1738, "Cyrillic_softsign": 1752, "Cyrillic_te": 1748, "Cyrillic_tse": 1731, "Cyrillic_u": 1749, "Cyrillic_u_macron": 16778479, "Cyrillic_u_straight": 16778415, "Cyrillic_u_straight_bar": 16778417, "Cyrillic_ve": 1751, "Cyrillic_ya": 1745, "Cyrillic_yeru": 1753, "Cyrillic_yu": 1728, "Cyrillic_ze": 1754, "Cyrillic_zhe": 1750, "Cyrillic_zhe_descender": 16778391, "D": 68, "Dabovedot": 16784906, "Dcaron": 463, "Delete": 65535, "DongSign": 16785579, "Down": 65364, "Dstroke": 464, "E": 69, "ENG": 957, "ETH": 208, "EZH": 16777655, "Eabovedot": 972, "Eacute": 201, "Ebelowdot": 16785080, "Ecaron": 460, "Ecircumflex": 202, "Ecircumflexacute": 16785086, "Ecircumflexbelowdot": 16785094, "Ecircumflexgrave": 16785088, "Ecircumflexhook": 16785090, "Ecircumflextilde": 16785092, "EcuSign": 16785568, "Ediaeresis": 203, "Egrave": 200, "Ehook": 16785082, "Eisu_Shift": 65327, "Eisu_toggle": 65328, "Emacron": 938, "End": 65367, "Eogonek": 458, "Escape": 65307, "Eth": 208, "Etilde": 16785084, "EuroSign": 8364, "Execute": 65378, "F": 70, "F1": 65470, "F10": 65479, "F11": 65480, "F12": 65481, "F13": 65482, "F14": 65483, "F15": 65484, "F16": 65485, "F17": 65486, "F18": 65487, "F19": 65488, "F2": 65471, "F20": 65489, "F21": 65490, "F22": 65491, "F23": 65492, "F24": 65493, "F25": 65494, "F26": 65495, "F27": 65496, "F28": 65497, "F29": 65498, "F3": 65472, "F30": 65499, "F31": 65500, "F32": 65501, "F33": 65502, "F34": 65503, "F35": 65504, "F4": 65473, "F5": 65474, "F6": 65475, "F7": 65476, "F8": 65477, "F9": 65478, "FFrancSign": 16785571, "Fabovedot": 16784926, "Farsi_0": 16778992, "Farsi_1": 16778993, "Farsi_2": 16778994, "Farsi_3": 16778995, "Farsi_4": 16778996, "Farsi_5": 16778997, "Farsi_6": 16778998, "Farsi_7": 16778999, "Farsi_8": 16779000, "Farsi_9": 16779001, "Farsi_yeh": 16778956, "Find": 65384, "First_Virtual_Screen": 65232, "G": 71, "Gabovedot": 725, "Gbreve": 683, "Gcaron": 16777702, "Gcedilla": 939, "Gcircumflex": 728, "Georgian_an": 16781520, "Georgian_ban": 16781521, "Georgian_can": 16781546, "Georgian_char": 16781549, "Georgian_chin": 16781545, "Georgian_cil": 16781548, "Georgian_don": 16781523, "Georgian_en": 16781524, "Georgian_fi": 16781558, "Georgian_gan": 16781522, "Georgian_ghan": 16781542, "Georgian_hae": 16781552, "Georgian_har": 16781556, "Georgian_he": 16781553, "Georgian_hie": 16781554, "Georgian_hoe": 16781557, "Georgian_in": 16781528, "Georgian_jhan": 16781551, "Georgian_jil": 16781547, "Georgian_kan": 16781529, "Georgian_khar": 16781541, "Georgian_las": 16781530, "Georgian_man": 16781531, "Georgian_nar": 16781532, "Georgian_on": 16781533, "Georgian_par": 16781534, "Georgian_phar": 16781540, "Georgian_qar": 16781543, "Georgian_rae": 16781536, "Georgian_san": 16781537, "Georgian_shin": 16781544, "Georgian_tan": 16781527, "Georgian_tar": 16781538, "Georgian_un": 16781539, "Georgian_vin": 16781525, "Georgian_we": 16781555, "Georgian_xan": 16781550, "Georgian_zen": 16781526, "Georgian_zhar": 16781535, "Greek_ALPHA": 1985, "Greek_ALPHAaccent": 1953, "Greek_BETA": 1986, "Greek_CHI": 2007, "Greek_DELTA": 1988, "Greek_EPSILON": 1989, "Greek_EPSILONaccent": 1954, "Greek_ETA": 1991, "Greek_ETAaccent": 1955, "Greek_GAMMA": 1987, "Greek_IOTA": 1993, "Greek_IOTAaccent": 1956, "Greek_IOTAdiaeresis": 1957, "Greek_IOTAdieresis": 1957, "Greek_KAPPA": 1994, "Greek_LAMBDA": 1995, "Greek_LAMDA": 1995, "Greek_MU": 1996, "Greek_NU": 1997, "Greek_OMEGA": 2009, "Greek_OMEGAaccent": 1963, "Greek_OMICRON": 1999, "Greek_OMICRONaccent": 1959, "Greek_PHI": 2006, "Greek_PI": 2000, "Greek_PSI": 2008, "Greek_RHO": 2001, "Greek_SIGMA": 2002, "Greek_TAU": 2004, "Greek_THETA": 1992, "Greek_UPSILON": 2005, "Greek_UPSILONaccent": 1960, "Greek_UPSILONdieresis": 1961, "Greek_XI": 1998, "Greek_ZETA": 1990, "Greek_accentdieresis": 1966, "Greek_alpha": 2017, "Greek_alphaaccent": 1969, "Greek_beta": 2018, "Greek_chi": 2039, "Greek_delta": 2020, "Greek_epsilon": 2021, "Greek_epsilonaccent": 1970, "Greek_eta": 2023, "Greek_etaaccent": 1971, "Greek_finalsmallsigma": 2035, "Greek_gamma": 2019, "Greek_horizbar": 1967, "Greek_iota": 2025, "Greek_iotaaccent": 1972, "Greek_iotaaccentdieresis": 1974, "Greek_iotadieresis": 1973, "Greek_kappa": 2026, "Greek_lambda": 2027, "Greek_lamda": 2027, "Greek_mu": 2028, "Greek_nu": 2029, "Greek_omega": 2041, "Greek_omegaaccent": 1979, "Greek_omicron": 2031, "Greek_omicronaccent": 1975, "Greek_phi": 2038, "Greek_pi": 2032, "Greek_psi": 2040, "Greek_rho": 2033, "Greek_sigma": 2034, "Greek_switch": 65406, "Greek_tau": 2036, "Greek_theta": 2024, "Greek_upsilon": 2037, "Greek_upsilonaccent": 1976, "Greek_upsilonaccentdieresis": 1978, "Greek_upsilondieresis": 1977, "Greek_xi": 2030, "Greek_zeta": 2022, "H": 72, "Hangul": 65329, "Hangul_A": 3775, "Hangul_AE": 3776, "Hangul_AraeA": 3830, "Hangul_AraeAE": 3831, "Hangul_Banja": 65337, "Hangul_Cieuc": 3770, "Hangul_Codeinput": 65335, "Hangul_Dikeud": 3751, "Hangul_E": 3780, "Hangul_EO": 3779, "Hangul_EU": 3793, "Hangul_End": 65331, "Hangul_Hanja": 65332, "Hangul_Hieuh": 3774, "Hangul_I": 3795, "Hangul_Ieung": 3767, "Hangul_J_Cieuc": 3818, "Hangul_J_Dikeud": 3802, "Hangul_J_Hieuh": 3822, "Hangul_J_Ieung": 3816, "Hangul_J_Jieuj": 3817, "Hangul_J_Khieuq": 3819, "Hangul_J_Kiyeog": 3796, "Hangul_J_KiyeogSios": 3798, "Hangul_J_KkogjiDalrinIeung": 3833, "Hangul_J_Mieum": 3811, "Hangul_J_Nieun": 3799, "Hangul_J_NieunHieuh": 3801, "Hangul_J_NieunJieuj": 3800, "Hangul_J_PanSios": 3832, "Hangul_J_Phieuf": 3821, "Hangul_J_Pieub": 3812, "Hangul_J_PieubSios": 3813, "Hangul_J_Rieul": 3803, "Hangul_J_RieulHieuh": 3810, "Hangul_J_RieulKiyeog": 3804, "Hangul_J_RieulMieum": 3805, "Hangul_J_RieulPhieuf": 3809, "Hangul_J_RieulPieub": 3806, "Hangul_J_RieulSios": 3807, "Hangul_J_RieulTieut": 3808, "Hangul_J_Sios": 3814, "Hangul_J_SsangKiyeog": 3797, "Hangul_J_SsangSios": 3815, "Hangul_J_Tieut": 3820, "Hangul_J_YeorinHieuh": 3834, "Hangul_Jamo": 65333, "Hangul_Jeonja": 65336, "Hangul_Jieuj": 3768, "Hangul_Khieuq": 3771, "Hangul_Kiyeog": 3745, "Hangul_KiyeogSios": 3747, "Hangul_KkogjiDalrinIeung": 3827, "Hangul_Mieum": 3761, "Hangul_MultipleCandidate": 65341, "Hangul_Nieun": 3748, "Hangul_NieunHieuh": 3750, "Hangul_NieunJieuj": 3749, "Hangul_O": 3783, "Hangul_OE": 3786, "Hangul_PanSios": 3826, "Hangul_Phieuf": 3773, "Hangul_Pieub": 3762, "Hangul_PieubSios": 3764, "Hangul_PostHanja": 65339, "Hangul_PreHanja": 65338, "Hangul_PreviousCandidate": 65342, "Hangul_Rieul": 3753, "Hangul_RieulHieuh": 3760, "Hangul_RieulKiyeog": 3754, "Hangul_RieulMieum": 3755, "Hangul_RieulPhieuf": 3759, "Hangul_RieulPieub": 3756, "Hangul_RieulSios": 3757, "Hangul_RieulTieut": 3758, "Hangul_RieulYeorinHieuh": 3823, "Hangul_Romaja": 65334, "Hangul_SingleCandidate": 65340, "Hangul_Sios": 3765, "Hangul_Special": 65343, "Hangul_SsangDikeud": 3752, "Hangul_SsangJieuj": 3769, "Hangul_SsangKiyeog": 3746, "Hangul_SsangPieub": 3763, "Hangul_SsangSios": 3766, "Hangul_Start": 65330, "Hangul_SunkyeongeumMieum": 3824, "Hangul_SunkyeongeumPhieuf": 3828, "Hangul_SunkyeongeumPieub": 3825, "Hangul_Tieut": 3772, "Hangul_U": 3788, "Hangul_WA": 3784, "Hangul_WAE": 3785, "Hangul_WE": 3790, "Hangul_WEO": 3789, "Hangul_WI": 3791, "Hangul_YA": 3777, "Hangul_YAE": 3778, "Hangul_YE": 3782, "Hangul_YEO": 3781, "Hangul_YI": 3794, "Hangul_YO": 3787, "Hangul_YU": 3792, "Hangul_YeorinHieuh": 3829, "Hangul_switch": 65406, "Hankaku": 65321, "Hcircumflex": 678, "Hebrew_switch": 65406, "Help": 65386, "Henkan": 65315, "Henkan_Mode": 65315, "Hiragana": 65317, "Hiragana_Katakana": 65319, "Home": 65360, "Hstroke": 673, "Hyper_L": 65517, "Hyper_R": 65518, "I": 73, "ISO_Center_Object": 65075, "ISO_Continuous_Underline": 65072, "ISO_Discontinuous_Underline": 65073, "ISO_Emphasize": 65074, "ISO_Enter": 65076, "ISO_Fast_Cursor_Down": 65071, "ISO_Fast_Cursor_Left": 65068, "ISO_Fast_Cursor_Right": 65069, "ISO_Fast_Cursor_Up": 65070, "ISO_First_Group": 65036, "ISO_First_Group_Lock": 65037, "ISO_Group_Latch": 65030, "ISO_Group_Lock": 65031, "ISO_Group_Shift": 65406, "ISO_Last_Group": 65038, "ISO_Last_Group_Lock": 65039, "ISO_Left_Tab": 65056, "ISO_Level2_Latch": 65026, "ISO_Level3_Latch": 65028, "ISO_Level3_Lock": 65029, "ISO_Level3_Shift": 65027, "ISO_Level5_Latch": 65042, "ISO_Level5_Lock": 65043, "ISO_Level5_Shift": 65041, "ISO_Lock": 65025, "ISO_Move_Line_Down": 65058, "ISO_Move_Line_Up": 65057, "ISO_Next_Group": 65032, "ISO_Next_Group_Lock": 65033, "ISO_Partial_Line_Down": 65060, "ISO_Partial_Line_Up": 65059, "ISO_Partial_Space_Left": 65061, "ISO_Partial_Space_Right": 65062, "ISO_Prev_Group": 65034, "ISO_Prev_Group_Lock": 65035, "ISO_Release_Both_Margins": 65067, "ISO_Release_Margin_Left": 65065, "ISO_Release_Margin_Right": 65066, "ISO_Set_Margin_Left": 65063, "ISO_Set_Margin_Right": 65064, "Iabovedot": 681, "Iacute": 205, "Ibelowdot": 16785098, "Ibreve": 16777516, "Icircumflex": 206, "Idiaeresis": 207, "Igrave": 204, "Ihook": 16785096, "Imacron": 975, "Insert": 65379, "Iogonek": 967, "Itilde": 933, "J": 74, "Jcircumflex": 684, "K": 75, "KP_0": 65456, "KP_1": 65457, "KP_2": 65458, "KP_3": 65459, "KP_4": 65460, "KP_5": 65461, "KP_6": 65462, "KP_7": 65463, "KP_8": 65464, "KP_9": 65465, "KP_Add": 65451, "KP_Begin": 65437, "KP_Decimal": 65454, "KP_Delete": 65439, "KP_Divide": 65455, "KP_Down": 65433, "KP_End": 65436, "KP_Enter": 65421, "KP_Equal": 65469, "KP_F1": 65425, "KP_F2": 65426, "KP_F3": 65427, "KP_F4": 65428, "KP_Home": 65429, "KP_Insert": 65438, "KP_Left": 65430, "KP_Multiply": 65450, "KP_Next": 65435, "KP_Page_Down": 65435, "KP_Page_Up": 65434, "KP_Prior": 65434, "KP_Right": 65432, "KP_Separator": 65452, "KP_Space": 65408, "KP_Subtract": 65453, "KP_Tab": 65417, "KP_Up": 65431, "Kana_Lock": 65325, "Kana_Shift": 65326, "Kanji": 65313, "Kanji_Bangou": 65335, "Katakana": 65318, "Kcedilla": 979, "Korean_Won": 3839, "L": 76, "L1": 65480, "L10": 65489, "L2": 65481, "L3": 65482, "L4": 65483, "L5": 65484, "L6": 65485, "L7": 65486, "L8": 65487, "L9": 65488, "Lacute": 453, "Last_Virtual_Screen": 65236, "Lbelowdot": 16784950, "Lcaron": 421, "Lcedilla": 934, "Left": 65361, "Linefeed": 65290, "LiraSign": 16785572, "Lstroke": 419, "M": 77, "Mabovedot": 16784960, "Macedonia_DSE": 1717, "Macedonia_GJE": 1714, "Macedonia_KJE": 1724, "Macedonia_dse": 1701, "Macedonia_gje": 1698, "Macedonia_kje": 1708, "Mae_Koho": 65342, "Massyo": 65324, "Menu": 65383, "Meta_L": 65511, "Meta_R": 65512, "MillSign": 16785573, "Mode_switch": 65406, "MouseKeys_Accel_Enable": 65143, "MouseKeys_Enable": 65142, "Muhenkan": 65314, "Multi_key": 65312, "MultipleCandidate": 65341, "N": 78, "Nacute": 465, "NairaSign": 16785574, "Ncaron": 466, "Ncedilla": 977, "NewSheqelSign": 16785578, "Next": 65366, "Next_Virtual_Screen": 65234, "Ntilde": 209, "Num_Lock": 65407, "O": 79, "OE": 5052, "Oacute": 211, "Obarred": 16777631, "Obelowdot": 16785100, "Ocaron": 16777681, "Ocircumflex": 212, "Ocircumflexacute": 16785104, "Ocircumflexbelowdot": 16785112, "Ocircumflexgrave": 16785106, "Ocircumflexhook": 16785108, "Ocircumflextilde": 16785110, "Odiaeresis": 214, "Odoubleacute": 469, "Ograve": 210, "Ohook": 16785102, "Ohorn": 16777632, "Ohornacute": 16785114, "Ohornbelowdot": 16785122, "Ohorngrave": 16785116, "Ohornhook": 16785118, "Ohorntilde": 16785120, "Omacron": 978, "Ooblique": 216, "Oslash": 216, "Otilde": 213, "Overlay1_Enable": 65144, "Overlay2_Enable": 65145, "P": 80, "Pabovedot": 16784982, "Page_Down": 65366, "Page_Up": 65365, "Pause": 65299, "PesetaSign": 16785575, "Pointer_Accelerate": 65274, "Pointer_Button1": 65257, "Pointer_Button2": 65258, "Pointer_Button3": 65259, "Pointer_Button4": 65260, "Pointer_Button5": 65261, "Pointer_Button_Dflt": 65256, "Pointer_DblClick1": 65263, "Pointer_DblClick2": 65264, "Pointer_DblClick3": 65265, "Pointer_DblClick4": 65266, "Pointer_DblClick5": 65267, "Pointer_DblClick_Dflt": 65262, "Pointer_DfltBtnNext": 65275, "Pointer_DfltBtnPrev": 65276, "Pointer_Down": 65251, "Pointer_DownLeft": 65254, "Pointer_DownRight": 65255, "Pointer_Drag1": 65269, "Pointer_Drag2": 65270, "Pointer_Drag3": 65271, "Pointer_Drag4": 65272, "Pointer_Drag5": 65277, "Pointer_Drag_Dflt": 65268, "Pointer_EnableKeys": 65273, "Pointer_Left": 65248, "Pointer_Right": 65249, "Pointer_Up": 65250, "Pointer_UpLeft": 65252, "Pointer_UpRight": 65253, "Prev_Virtual_Screen": 65233, "PreviousCandidate": 65342, "Print": 65377, "Prior": 65365, "Q": 81, "R": 82, "R1": 65490, "R10": 65499, "R11": 65500, "R12": 65501, "R13": 65502, "R14": 65503, "R15": 65504, "R2": 65491, "R3": 65492, "R4": 65493, "R5": 65494, "R6": 65495, "R7": 65496, "R8": 65497, "R9": 65498, "Racute": 448, "Rcaron": 472, "Rcedilla": 931, "Redo": 65382, "RepeatKeys_Enable": 65138, "Return": 65293, "Right": 65363, "Romaji": 65316, "RupeeSign": 16785576, "S": 83, "SCHWA": 16777615, "Sabovedot": 16784992, "Sacute": 422, "Scaron": 425, "Scedilla": 426, "Scircumflex": 734, "Scroll_Lock": 65300, "Select": 65376, "Serbian_DJE": 1713, "Serbian_DZE": 1727, "Serbian_JE": 1720, "Serbian_LJE": 1721, "Serbian_NJE": 1722, "Serbian_TSHE": 1723, "Serbian_dje": 1697, "Serbian_dze": 1711, "Serbian_je": 1704, "Serbian_lje": 1705, "Serbian_nje": 1706, "Serbian_tshe": 1707, "Shift_L": 65505, "Shift_Lock": 65510, "Shift_R": 65506, "SingleCandidate": 65340, "Sinh_a": 16780677, "Sinh_aa": 16780678, "Sinh_aa2": 16780751, "Sinh_ae": 16780679, "Sinh_ae2": 16780752, "Sinh_aee": 16780680, "Sinh_aee2": 16780753, "Sinh_ai": 16780691, "Sinh_ai2": 16780763, "Sinh_al": 16780746, "Sinh_au": 16780694, "Sinh_au2": 16780766, "Sinh_ba": 16780726, "Sinh_bha": 16780727, "Sinh_ca": 16780704, "Sinh_cha": 16780705, "Sinh_dda": 16780713, "Sinh_ddha": 16780714, "Sinh_dha": 16780719, "Sinh_dhha": 16780720, "Sinh_e": 16780689, "Sinh_e2": 16780761, "Sinh_ee": 16780690, "Sinh_ee2": 16780762, "Sinh_fa": 16780742, "Sinh_ga": 16780700, "Sinh_gha": 16780701, "Sinh_h2": 16780675, "Sinh_ha": 16780740, "Sinh_i": 16780681, "Sinh_i2": 16780754, "Sinh_ii": 16780682, "Sinh_ii2": 16780755, "Sinh_ja": 16780706, "Sinh_jha": 16780707, "Sinh_jnya": 16780709, "Sinh_ka": 16780698, "Sinh_kha": 16780699, "Sinh_kunddaliya": 16780788, "Sinh_la": 16780733, "Sinh_lla": 16780741, "Sinh_lu": 16780687, "Sinh_lu2": 16780767, "Sinh_luu": 16780688, "Sinh_luu2": 16780787, "Sinh_ma": 16780728, "Sinh_mba": 16780729, "Sinh_na": 16780721, "Sinh_ndda": 16780716, "Sinh_ndha": 16780723, "Sinh_ng": 16780674, "Sinh_ng2": 16780702, "Sinh_nga": 16780703, "Sinh_nja": 16780710, "Sinh_nna": 16780715, "Sinh_nya": 16780708, "Sinh_o": 16780692, "Sinh_o2": 16780764, "Sinh_oo": 16780693, "Sinh_oo2": 16780765, "Sinh_pa": 16780724, "Sinh_pha": 16780725, "Sinh_ra": 16780731, "Sinh_ri": 16780685, "Sinh_rii": 16780686, "Sinh_ru2": 16780760, "Sinh_ruu2": 16780786, "Sinh_sa": 16780739, "Sinh_sha": 16780737, "Sinh_ssha": 16780738, "Sinh_tha": 16780717, "Sinh_thha": 16780718, "Sinh_tta": 16780711, "Sinh_ttha": 16780712, "Sinh_u": 16780683, "Sinh_u2": 16780756, "Sinh_uu": 16780684, "Sinh_uu2": 16780758, "Sinh_va": 16780736, "Sinh_ya": 16780730, "SlowKeys_Enable": 65139, "StickyKeys_Enable": 65141, "Super_L": 65515, "Super_R": 65516, "Sys_Req": 65301, "T": 84, "THORN": 222, "Tab": 65289, "Tabovedot": 16785002, "Tcaron": 427, "Tcedilla": 478, "Terminate_Server": 65237, "Thai_baht": 3551, "Thai_bobaimai": 3514, "Thai_chochan": 3496, "Thai_chochang": 3498, "Thai_choching": 3497, "Thai_chochoe": 3500, "Thai_dochada": 3502, "Thai_dodek": 3508, "Thai_fofa": 3517, "Thai_fofan": 3519, "Thai_hohip": 3531, "Thai_honokhuk": 3534, "Thai_khokhai": 3490, "Thai_khokhon": 3493, "Thai_khokhuat": 3491, "Thai_khokhwai": 3492, "Thai_khorakhang": 3494, "Thai_kokai": 3489, "Thai_lakkhangyao": 3557, "Thai_lekchet": 3575, "Thai_lekha": 3573, "Thai_lekhok": 3574, "Thai_lekkao": 3577, "Thai_leknung": 3569, "Thai_lekpaet": 3576, "Thai_leksam": 3571, "Thai_leksi": 3572, "Thai_leksong": 3570, "Thai_leksun": 3568, "Thai_lochula": 3532, "Thai_loling": 3525, "Thai_lu": 3526, "Thai_maichattawa": 3563, "Thai_maiek": 3560, "Thai_maihanakat": 3537, "Thai_maihanakat_maitho": 3550, "Thai_maitaikhu": 3559, "Thai_maitho": 3561, "Thai_maitri": 3562, "Thai_maiyamok": 3558, "Thai_moma": 3521, "Thai_ngongu": 3495, "Thai_nikhahit": 3565, "Thai_nonen": 3507, "Thai_nonu": 3513, "Thai_oang": 3533, "Thai_paiyannoi": 3535, "Thai_phinthu": 3546, "Thai_phophan": 3518, "Thai_phophung": 3516, "Thai_phosamphao": 3520, "Thai_popla": 3515, "Thai_rorua": 3523, "Thai_ru": 3524, "Thai_saraa": 3536, "Thai_saraaa": 3538, "Thai_saraae": 3553, "Thai_saraaimaimalai": 3556, "Thai_saraaimaimuan": 3555, "Thai_saraam": 3539, "Thai_sarae": 3552, "Thai_sarai": 3540, "Thai_saraii": 3541, "Thai_sarao": 3554, "Thai_sarau": 3544, "Thai_saraue": 3542, "Thai_sarauee": 3543, "Thai_sarauu": 3545, "Thai_sorusi": 3529, "Thai_sosala": 3528, "Thai_soso": 3499, "Thai_sosua": 3530, "Thai_thanthakhat": 3564, "Thai_thonangmontho": 3505, "Thai_thophuthao": 3506, "Thai_thothahan": 3511, "Thai_thothan": 3504, "Thai_thothong": 3512, "Thai_thothung": 3510, "Thai_topatak": 3503, "Thai_totao": 3509, "Thai_wowaen": 3527, "Thai_yoyak": 3522, "Thai_yoying": 3501, "Thorn": 222, "Touroku": 65323, "Tslash": 940, "U": 85, "Uacute": 218, "Ubelowdot": 16785124, "Ubreve": 733, "Ucircumflex": 219, "Udiaeresis": 220, "Udoubleacute": 475, "Ugrave": 217, "Uhook": 16785126, "Uhorn": 16777647, "Uhornacute": 16785128, "Uhornbelowdot": 16785136, "Uhorngrave": 16785130, "Uhornhook": 16785132, "Uhorntilde": 16785134, "Ukrainian_GHE_WITH_UPTURN": 1725, "Ukrainian_I": 1718, "Ukrainian_IE": 1716, "Ukrainian_YI": 1719, "Ukrainian_ghe_with_upturn": 1709, "Ukrainian_i": 1702, "Ukrainian_ie": 1700, "Ukrainian_yi": 1703, "Ukranian_I": 1718, "Ukranian_JE": 1716, "Ukranian_YI": 1719, "Ukranian_i": 1702, "Ukranian_je": 1700, "Ukranian_yi": 1703, "Umacron": 990, "Undo": 65381, "Uogonek": 985, "Up": 65362, "Uring": 473, "Utilde": 989, "V": 86, "VoidSymbol": 16777215, "W": 87, "Wacute": 16785026, "Wcircumflex": 16777588, "Wdiaeresis": 16785028, "Wgrave": 16785024, "WonSign": 16785577, "X": 88, "XF86_AddFavorite": 269025081, "XF86_ApplicationLeft": 269025104, "XF86_ApplicationRight": 269025105, "XF86_AudioCycleTrack": 269025179, "XF86_AudioForward": 269025175, "XF86_AudioLowerVolume": 269025041, "XF86_AudioMedia": 269025074, "XF86_AudioMicMute": 269025202, "XF86_AudioMute": 269025042, "XF86_AudioNext": 269025047, "XF86_AudioPause": 269025073, "XF86_AudioPlay": 269025044, "XF86_AudioPreset": 269025206, "XF86_AudioPrev": 269025046, "XF86_AudioRaiseVolume": 269025043, "XF86_AudioRandomPlay": 269025177, "XF86_AudioRecord": 269025052, "XF86_AudioRepeat": 269025176, "XF86_AudioRewind": 269025086, "XF86_AudioStop": 269025045, "XF86_Away": 269025165, "XF86_Back": 269025062, "XF86_BackForward": 269025087, "XF86_Battery": 269025171, "XF86_Blue": 269025190, "XF86_Bluetooth": 269025172, "XF86_Book": 269025106, "XF86_BrightnessAdjust": 269025083, "XF86_CD": 269025107, "XF86_Calculater": 269025108, "XF86_Calculator": 269025053, "XF86_Calendar": 269025056, "XF86_Clear": 269025109, "XF86_ClearGrab": 269024801, "XF86_Close": 269025110, "XF86_Community": 269025085, "XF86_ContrastAdjust": 269025058, "XF86_Copy": 269025111, "XF86_Cut": 269025112, "XF86_CycleAngle": 269025180, "XF86_DOS": 269025114, "XF86_Display": 269025113, "XF86_Documents": 269025115, "XF86_Eject": 269025068, "XF86_Excel": 269025116, "XF86_Explorer": 269025117, "XF86_Favorites": 269025072, "XF86_Finance": 269025084, "XF86_Forward": 269025063, "XF86_FrameBack": 269025181, "XF86_FrameForward": 269025182, "XF86_FullScreen": 269025208, "XF86_Game": 269025118, "XF86_Go": 269025119, "XF86_Green": 269025188, "XF86_Hibernate": 269025192, "XF86_History": 269025079, "XF86_HomePage": 269025048, "XF86_HotLinks": 269025082, "XF86_KbdBrightnessDown": 269025030, "XF86_KbdBrightnessUp": 269025029, "XF86_KbdLightOnOff": 269025028, "XF86_Keyboard": 269025203, "XF86_Launch0": 269025088, "XF86_Launch1": 269025089, "XF86_Launch2": 269025090, "XF86_Launch3": 269025091, "XF86_Launch4": 269025092, "XF86_Launch5": 269025093, "XF86_Launch6": 269025094, "XF86_Launch7": 269025095, "XF86_Launch8": 269025096, "XF86_Launch9": 269025097, "XF86_LaunchA": 269025098, "XF86_LaunchB": 269025099, "XF86_LaunchC": 269025100, "XF86_LaunchD": 269025101, "XF86_LaunchE": 269025102, "XF86_LaunchF": 269025103, "XF86_LightBulb": 269025077, "XF86_LogGrabInfo": 269024805, "XF86_LogOff": 269025121, "XF86_LogWindowTree": 269024804, "XF86_MacroRecordStart": 268964528, "XF86_Mail": 269025049, "XF86_MailForward": 269025168, "XF86_Market": 269025122, "XF86_Meeting": 269025123, "XF86_Memo": 269025054, "XF86_MenuKB": 269025125, "XF86_MenuPB": 269025126, "XF86_Messenger": 269025166, "XF86_ModeLock": 269025025, "XF86_MonBrightnessCycle": 269025031, "XF86_MonBrightnessDown": 269025027, "XF86_MonBrightnessUp": 269025026, "XF86_Music": 269025170, "XF86_MyComputer": 269025075, "XF86_MySites": 269025127, "XF86_New": 269025128, "XF86_News": 269025129, "XF86_Next_VMode": 269024802, "XF86_OfficeHome": 269025130, "XF86_Open": 269025131, "XF86_OpenURL": 269025080, "XF86_Option": 269025132, "XF86_Paste": 269025133, "XF86_Phone": 269025134, "XF86_Pictures": 269025169, "XF86_PowerDown": 269025057, "XF86_PowerOff": 269025066, "XF86_Prev_VMode": 269024803, "XF86_Q": 269025136, "XF86_RFKill": 269025205, "XF86_Red": 269025187, "XF86_Refresh": 269025065, "XF86_Reload": 269025139, "XF86_Reply": 269025138, "XF86_RockerDown": 269025060, "XF86_RockerEnter": 269025061, "XF86_RockerUp": 269025059, "XF86_RotateWindows": 269025140, "XF86_RotationKB": 269025142, "XF86_RotationLockToggle": 269025207, "XF86_RotationPB": 269025141, "XF86_Save": 269025143, "XF86_ScreenSaver": 269025069, "XF86_ScrollClick": 269025146, "XF86_ScrollDown": 269025145, "XF86_ScrollUp": 269025144, "XF86_Search": 269025051, "XF86_Select": 269025184, "XF86_Send": 269025147, "XF86_Shop": 269025078, "XF86_Sleep": 269025071, "XF86_Spell": 269025148, "XF86_SplitScreen": 269025149, "XF86_Standby": 269025040, "XF86_Start": 269025050, "XF86_Stop": 269025064, "XF86_Subtitle": 269025178, "XF86_Support": 269025150, "XF86_Suspend": 269025191, "XF86_Switch_VT_1": 269024769, "XF86_Switch_VT_10": 269024778, "XF86_Switch_VT_11": 269024779, "XF86_Switch_VT_12": 269024780, "XF86_Switch_VT_2": 269024770, "XF86_Switch_VT_3": 269024771, "XF86_Switch_VT_4": 269024772, "XF86_Switch_VT_5": 269024773, "XF86_Switch_VT_6": 269024774, "XF86_Switch_VT_7": 269024775, "XF86_Switch_VT_8": 269024776, "XF86_Switch_VT_9": 269024777, "XF86_TaskPane": 269025151, "XF86_Terminal": 269025152, "XF86_Time": 269025183, "XF86_ToDoList": 269025055, "XF86_Tools": 269025153, "XF86_TopMenu": 269025186, "XF86_TouchpadOff": 269025201, "XF86_TouchpadOn": 269025200, "XF86_TouchpadToggle": 269025193, "XF86_Travel": 269025154, "XF86_UWB": 269025174, "XF86_Ungrab": 269024800, "XF86_User1KB": 269025157, "XF86_User2KB": 269025158, "XF86_UserPB": 269025156, "XF86_VendorHome": 269025076, "XF86_Video": 269025159, "XF86_View": 269025185, "XF86_WLAN": 269025173, "XF86_WWAN": 269025204, "XF86_WWW": 269025070, "XF86_WakeUp": 269025067, "XF86_WebCam": 269025167, "XF86_WheelButton": 269025160, "XF86_Word": 269025161, "XF86_Xfer": 269025162, "XF86_Yellow": 269025189, "XF86_ZoomIn": 269025163, "XF86_ZoomOut": 269025164, "XF86_iTouch": 269025120, "Xabovedot": 16785034, "Y": 89, "Yacute": 221, "Ybelowdot": 16785140, "Ycircumflex": 16777590, "Ydiaeresis": 5054, "Ygrave": 16785138, "Yhook": 16785142, "Ytilde": 16785144, "Z": 90, "Zabovedot": 431, "Zacute": 428, "Zcaron": 430, "Zen_Koho": 65341, "Zenkaku": 65320, "Zenkaku_Hankaku": 65322, "Zstroke": 16777653, "a": 97, "aacute": 225, "abelowdot": 16785057, "abovedot": 511, "abreve": 483, "abreveacute": 16785071, "abrevebelowdot": 16785079, "abrevegrave": 16785073, "abrevehook": 16785075, "abrevetilde": 16785077, "acircumflex": 226, "acircumflexacute": 16785061, "acircumflexbelowdot": 16785069, "acircumflexgrave": 16785063, "acircumflexhook": 16785065, "acircumflextilde": 16785067, "acute": 180, "adiaeresis": 228, "ae": 230, "agrave": 224, "ahook": 16785059, "amacron": 992, "ampersand": 38, "aogonek": 433, "apostrophe": 39, "approxeq": 16785992, "approximate": 2248, "aring": 229, "asciicircum": 94, "asciitilde": 126, "asterisk": 42, "at": 64, "atilde": 227, "b": 98, "babovedot": 16784899, "backslash": 92, "ballotcross": 2804, "bar": 124, "because": 16785973, "blank": 2527, "botintegral": 2213, "botleftparens": 2220, "botleftsqbracket": 2216, "botleftsummation": 2226, "botrightparens": 2222, "botrightsqbracket": 2218, "botrightsummation": 2230, "bott": 2550, "botvertsummationconnector": 2228, "braceleft": 123, "braceright": 125, "bracketleft": 91, "bracketright": 93, "braille_blank": 16787456, "braille_dot_1": 65521, "braille_dot_10": 65530, "braille_dot_2": 65522, "braille_dot_3": 65523, "braille_dot_4": 65524, "braille_dot_5": 65525, "braille_dot_6": 65526, "braille_dot_7": 65527, "braille_dot_8": 65528, "braille_dot_9": 65529, "braille_dots_1": 16787457, "braille_dots_12": 16787459, "braille_dots_123": 16787463, "braille_dots_1234": 16787471, "braille_dots_12345": 16787487, "braille_dots_123456": 16787519, "braille_dots_1234567": 16787583, "braille_dots_12345678": 16787711, "braille_dots_1234568": 16787647, "braille_dots_123457": 16787551, "braille_dots_1234578": 16787679, "braille_dots_123458": 16787615, "braille_dots_12346": 16787503, "braille_dots_123467": 16787567, "braille_dots_1234678": 16787695, "braille_dots_123468": 16787631, "braille_dots_12347": 16787535, "braille_dots_123478": 16787663, "braille_dots_12348": 16787599, "braille_dots_1235": 16787479, "braille_dots_12356": 16787511, "braille_dots_123567": 16787575, "braille_dots_1235678": 16787703, "braille_dots_123568": 16787639, "braille_dots_12357": 16787543, "braille_dots_123578": 16787671, "braille_dots_12358": 16787607, "braille_dots_1236": 16787495, "braille_dots_12367": 16787559, "braille_dots_123678": 16787687, "braille_dots_12368": 16787623, "braille_dots_1237": 16787527, "braille_dots_12378": 16787655, "braille_dots_1238": 16787591, "braille_dots_124": 16787467, "braille_dots_1245": 16787483, "braille_dots_12456": 16787515, "braille_dots_124567": 16787579, "braille_dots_1245678": 16787707, "braille_dots_124568": 16787643, "braille_dots_12457": 16787547, "braille_dots_124578": 16787675, "braille_dots_12458": 16787611, "braille_dots_1246": 16787499, "braille_dots_12467": 16787563, "braille_dots_124678": 16787691, "braille_dots_12468": 16787627, "braille_dots_1247": 16787531, "braille_dots_12478": 16787659, "braille_dots_1248": 16787595, "braille_dots_125": 16787475, "braille_dots_1256": 16787507, "braille_dots_12567": 16787571, "braille_dots_125678": 16787699, "braille_dots_12568": 16787635, "braille_dots_1257": 16787539, "braille_dots_12578": 16787667, "braille_dots_1258": 16787603, "braille_dots_126": 16787491, "braille_dots_1267": 16787555, "braille_dots_12678": 16787683, "braille_dots_1268": 16787619, "braille_dots_127": 16787523, "braille_dots_1278": 16787651, "braille_dots_128": 16787587, "braille_dots_13": 16787461, "braille_dots_134": 16787469, "braille_dots_1345": 16787485, "braille_dots_13456": 16787517, "braille_dots_134567": 16787581, "braille_dots_1345678": 16787709, "braille_dots_134568": 16787645, "braille_dots_13457": 16787549, "braille_dots_134578": 16787677, "braille_dots_13458": 16787613, "braille_dots_1346": 16787501, "braille_dots_13467": 16787565, "braille_dots_134678": 16787693, "braille_dots_13468": 16787629, "braille_dots_1347": 16787533, "braille_dots_13478": 16787661, "braille_dots_1348": 16787597, "braille_dots_135": 16787477, "braille_dots_1356": 16787509, "braille_dots_13567": 16787573, "braille_dots_135678": 16787701, "braille_dots_13568": 16787637, "braille_dots_1357": 16787541, "braille_dots_13578": 16787669, "braille_dots_1358": 16787605, "braille_dots_136": 16787493, "braille_dots_1367": 16787557, "braille_dots_13678": 16787685, "braille_dots_1368": 16787621, "braille_dots_137": 16787525, "braille_dots_1378": 16787653, "braille_dots_138": 16787589, "braille_dots_14": 16787465, "braille_dots_145": 16787481, "braille_dots_1456": 16787513, "braille_dots_14567": 16787577, "braille_dots_145678": 16787705, "braille_dots_14568": 16787641, "braille_dots_1457": 16787545, "braille_dots_14578": 16787673, "braille_dots_1458": 16787609, "braille_dots_146": 16787497, "braille_dots_1467": 16787561, "braille_dots_14678": 16787689, "braille_dots_1468": 16787625, "braille_dots_147": 16787529, "braille_dots_1478": 16787657, "braille_dots_148": 16787593, "braille_dots_15": 16787473, "braille_dots_156": 16787505, "braille_dots_1567": 16787569, "braille_dots_15678": 16787697, "braille_dots_1568": 16787633, "braille_dots_157": 16787537, "braille_dots_1578": 16787665, "braille_dots_158": 16787601, "braille_dots_16": 16787489, "braille_dots_167": 16787553, "braille_dots_1678": 16787681, "braille_dots_168": 16787617, "braille_dots_17": 16787521, "braille_dots_178": 16787649, "braille_dots_18": 16787585, "braille_dots_2": 16787458, "braille_dots_23": 16787462, "braille_dots_234": 16787470, "braille_dots_2345": 16787486, "braille_dots_23456": 16787518, "braille_dots_234567": 16787582, "braille_dots_2345678": 16787710, "braille_dots_234568": 16787646, "braille_dots_23457": 16787550, "braille_dots_234578": 16787678, "braille_dots_23458": 16787614, "braille_dots_2346": 16787502, "braille_dots_23467": 16787566, "braille_dots_234678": 16787694, "braille_dots_23468": 16787630, "braille_dots_2347": 16787534, "braille_dots_23478": 16787662, "braille_dots_2348": 16787598, "braille_dots_235": 16787478, "braille_dots_2356": 16787510, "braille_dots_23567": 16787574, "braille_dots_235678": 16787702, "braille_dots_23568": 16787638, "braille_dots_2357": 16787542, "braille_dots_23578": 16787670, "braille_dots_2358": 16787606, "braille_dots_236": 16787494, "braille_dots_2367": 16787558, "braille_dots_23678": 16787686, "braille_dots_2368": 16787622, "braille_dots_237": 16787526, "braille_dots_2378": 16787654, "braille_dots_238": 16787590, "braille_dots_24": 16787466, "braille_dots_245": 16787482, "braille_dots_2456": 16787514, "braille_dots_24567": 16787578, "braille_dots_245678": 16787706, "braille_dots_24568": 16787642, "braille_dots_2457": 16787546, "braille_dots_24578": 16787674, "braille_dots_2458": 16787610, "braille_dots_246": 16787498, "braille_dots_2467": 16787562, "braille_dots_24678": 16787690, "braille_dots_2468": 16787626, "braille_dots_247": 16787530, "braille_dots_2478": 16787658, "braille_dots_248": 16787594, "braille_dots_25": 16787474, "braille_dots_256": 16787506, "braille_dots_2567": 16787570, "braille_dots_25678": 16787698, "braille_dots_2568": 16787634, "braille_dots_257": 16787538, "braille_dots_2578": 16787666, "braille_dots_258": 16787602, "braille_dots_26": 16787490, "braille_dots_267": 16787554, "braille_dots_2678": 16787682, "braille_dots_268": 16787618, "braille_dots_27": 16787522, "braille_dots_278": 16787650, "braille_dots_28": 16787586, "braille_dots_3": 16787460, "braille_dots_34": 16787468, "braille_dots_345": 16787484, "braille_dots_3456": 16787516, "braille_dots_34567": 16787580, "braille_dots_345678": 16787708, "braille_dots_34568": 16787644, "braille_dots_3457": 16787548, "braille_dots_34578": 16787676, "braille_dots_3458": 16787612, "braille_dots_346": 16787500, "braille_dots_3467": 16787564, "braille_dots_34678": 16787692, "braille_dots_3468": 16787628, "braille_dots_347": 16787532, "braille_dots_3478": 16787660, "braille_dots_348": 16787596, "braille_dots_35": 16787476, "braille_dots_356": 16787508, "braille_dots_3567": 16787572, "braille_dots_35678": 16787700, "braille_dots_3568": 16787636, "braille_dots_357": 16787540, "braille_dots_3578": 16787668, "braille_dots_358": 16787604, "braille_dots_36": 16787492, "braille_dots_367": 16787556, "braille_dots_3678": 16787684, "braille_dots_368": 16787620, "braille_dots_37": 16787524, "braille_dots_378": 16787652, "braille_dots_38": 16787588, "braille_dots_4": 16787464, "braille_dots_45": 16787480, "braille_dots_456": 16787512, "braille_dots_4567": 16787576, "braille_dots_45678": 16787704, "braille_dots_4568": 16787640, "braille_dots_457": 16787544, "braille_dots_4578": 16787672, "braille_dots_458": 16787608, "braille_dots_46": 16787496, "braille_dots_467": 16787560, "braille_dots_4678": 16787688, "braille_dots_468": 16787624, "braille_dots_47": 16787528, "braille_dots_478": 16787656, "braille_dots_48": 16787592, "braille_dots_5": 16787472, "braille_dots_56": 16787504, "braille_dots_567": 16787568, "braille_dots_5678": 16787696, "braille_dots_568": 16787632, "braille_dots_57": 16787536, "braille_dots_578": 16787664, "braille_dots_58": 16787600, "braille_dots_6": 16787488, "braille_dots_67": 16787552, "braille_dots_678": 16787680, "braille_dots_68": 16787616, "braille_dots_7": 16787520, "braille_dots_78": 16787648, "braille_dots_8": 16787584, "breve": 418, "brokenbar": 166, "c": 99, "c_h": 65187, "cabovedot": 741, "cacute": 486, "careof": 2744, "caret": 2812, "caron": 439, "ccaron": 488, "ccedilla": 231, "ccircumflex": 742, "cedilla": 184, "cent": 162, "ch": 65184, "checkerboard": 2529, "checkmark": 2803, "circle": 3023, "club": 2796, "colon": 58, "combining_acute": 16777985, "combining_belowdot": 16778019, "combining_grave": 16777984, "combining_hook": 16777993, "combining_tilde": 16777987, "comma": 44, "containsas": 16785931, "copyright": 169, "cr": 2532, "crossinglines": 2542, "cuberoot": 16785947, "currency": 164, "cursor": 2815, "d": 100, "dabovedot": 16784907, "dagger": 2801, "dcaron": 495, "dead_A": 65153, "dead_E": 65155, "dead_I": 65157, "dead_O": 65159, "dead_SCHWA": 65163, "dead_U": 65161, "dead_a": 65152, "dead_abovecomma": 65124, "dead_abovedot": 65110, "dead_abovereversedcomma": 65125, "dead_abovering": 65112, "dead_acute": 65105, "dead_belowbreve": 65131, "dead_belowcircumflex": 65129, "dead_belowcomma": 65134, "dead_belowdiaeresis": 65132, "dead_belowdot": 65120, "dead_belowmacron": 65128, "dead_belowring": 65127, "dead_belowtilde": 65130, "dead_breve": 65109, "dead_capital_schwa": 65163, "dead_caron": 65114, "dead_cedilla": 65115, "dead_circumflex": 65106, "dead_currency": 65135, "dead_dasia": 65125, "dead_diaeresis": 65111, "dead_doubleacute": 65113, "dead_doublegrave": 65126, "dead_e": 65154, "dead_grave": 65104, "dead_greek": 65164, "dead_hamza": 65165, "dead_hook": 65121, "dead_horn": 65122, "dead_i": 65156, "dead_invertedbreve": 65133, "dead_iota": 65117, "dead_macron": 65108, "dead_o": 65158, "dead_ogonek": 65116, "dead_perispomeni": 65107, "dead_psili": 65124, "dead_schwa": 65162, "dead_semivoiced_sound": 65119, "dead_small_schwa": 65162, "dead_stroke": 65123, "dead_tilde": 65107, "dead_u": 65160, "dead_voiced_sound": 65118, "decimalpoint": 2749, "degree": 176, "diaeresis": 168, "diamond": 2797, "digitspace": 2725, "dintegral": 16785964, "division": 247, "dollar": 36, "doubbaselinedot": 2735, "doubleacute": 445, "doubledagger": 2802, "doublelowquotemark": 2814, "downarrow": 2302, "downcaret": 2984, "downshoe": 3030, "downstile": 3012, "downtack": 3010, "dstroke": 496, "e": 101, "eabovedot": 1004, "eacute": 233, "ebelowdot": 16785081, "ecaron": 492, "ecircumflex": 234, "ecircumflexacute": 16785087, "ecircumflexbelowdot": 16785095, "ecircumflexgrave": 16785089, "ecircumflexhook": 16785091, "ecircumflextilde": 16785093, "ediaeresis": 235, "egrave": 232, "ehook": 16785083, "eightsubscript": 16785544, "eightsuperior": 16785528, "elementof": 16785928, "ellipsis": 2734, "em3space": 2723, "em4space": 2724, "emacron": 954, "emdash": 2729, "emfilledcircle": 2782, "emfilledrect": 2783, "emopencircle": 2766, "emopenrectangle": 2767, "emptyset": 16785925, "emspace": 2721, "endash": 2730, "enfilledcircbullet": 2790, "enfilledsqbullet": 2791, "eng": 959, "enopencircbullet": 2784, "enopensquarebullet": 2785, "enspace": 2722, "eogonek": 490, "equal": 61, "eth": 240, "etilde": 16785085, "exclam": 33, "exclamdown": 161, "ezh": 16777874, "f": 102, "fabovedot": 16784927, "femalesymbol": 2808, "ff": 2531, "figdash": 2747, "filledlefttribullet": 2780, "filledrectbullet": 2779, "filledrighttribullet": 2781, "filledtribulletdown": 2793, "filledtribulletup": 2792, "fiveeighths": 2757, "fivesixths": 2743, "fivesubscript": 16785541, "fivesuperior": 16785525, "fourfifths": 2741, "foursubscript": 16785540, "foursuperior": 16785524, "fourthroot": 16785948, "function": 2294, "g": 103, "gabovedot": 757, "gbreve": 699, "gcaron": 16777703, "gcedilla": 955, "gcircumflex": 760, "grave": 96, "greater": 62, "greaterthanequal": 2238, "guillemetleft": 171, "guillemetright": 187, "guillemotleft": 171, "guillemotright": 187, "h": 104, "hairspace": 2728, "hcircumflex": 694, "heart": 2798, "hebrew_aleph": 3296, "hebrew_ayin": 3314, "hebrew_bet": 3297, "hebrew_beth": 3297, "hebrew_chet": 3303, "hebrew_dalet": 3299, "hebrew_daleth": 3299, "hebrew_doublelowline": 3295, "hebrew_finalkaph": 3306, "hebrew_finalmem": 3309, "hebrew_finalnun": 3311, "hebrew_finalpe": 3315, "hebrew_finalzade": 3317, "hebrew_finalzadi": 3317, "hebrew_gimel": 3298, "hebrew_gimmel": 3298, "hebrew_he": 3300, "hebrew_het": 3303, "hebrew_kaph": 3307, "hebrew_kuf": 3319, "hebrew_lamed": 3308, "hebrew_mem": 3310, "hebrew_nun": 3312, "hebrew_pe": 3316, "hebrew_qoph": 3319, "hebrew_resh": 3320, "hebrew_samech": 3313, "hebrew_samekh": 3313, "hebrew_shin": 3321, "hebrew_taf": 3322, "hebrew_taw": 3322, "hebrew_tet": 3304, "hebrew_teth": 3304, "hebrew_waw": 3301, "hebrew_yod": 3305, "hebrew_zade": 3318, "hebrew_zadi": 3318, "hebrew_zain": 3302, "hebrew_zayin": 3302, "hexagram": 2778, "horizconnector": 2211, "horizlinescan1": 2543, "horizlinescan3": 2544, "horizlinescan5": 2545, "horizlinescan7": 2546, "horizlinescan9": 2547, "hstroke": 689, "ht": 2530, "hyphen": 173, "i": 105, "iacute": 237, "ibelowdot": 16785099, "ibreve": 16777517, "icircumflex": 238, "identical": 2255, "idiaeresis": 239, "idotless": 697, "ifonlyif": 2253, "igrave": 236, "ihook": 16785097, "imacron": 1007, "implies": 2254, "includedin": 2266, "includes": 2267, "infinity": 2242, "integral": 2239, "intersection": 2268, "iogonek": 999, "itilde": 949, "j": 106, "jcircumflex": 700, "jot": 3018, "k": 107, "kana_A": 1201, "kana_CHI": 1217, "kana_E": 1204, "kana_FU": 1228, "kana_HA": 1226, "kana_HE": 1229, "kana_HI": 1227, "kana_HO": 1230, "kana_HU": 1228, "kana_I": 1202, "kana_KA": 1206, "kana_KE": 1209, "kana_KI": 1207, "kana_KO": 1210, "kana_KU": 1208, "kana_MA": 1231, "kana_ME": 1234, "kana_MI": 1232, "kana_MO": 1235, "kana_MU": 1233, "kana_N": 1245, "kana_NA": 1221, "kana_NE": 1224, "kana_NI": 1222, "kana_NO": 1225, "kana_NU": 1223, "kana_O": 1205, "kana_RA": 1239, "kana_RE": 1242, "kana_RI": 1240, "kana_RO": 1243, "kana_RU": 1241, "kana_SA": 1211, "kana_SE": 1214, "kana_SHI": 1212, "kana_SO": 1215, "kana_SU": 1213, "kana_TA": 1216, "kana_TE": 1219, "kana_TI": 1217, "kana_TO": 1220, "kana_TSU": 1218, "kana_TU": 1218, "kana_U": 1203, "kana_WA": 1244, "kana_WO": 1190, "kana_YA": 1236, "kana_YO": 1238, "kana_YU": 1237, "kana_a": 1191, "kana_closingbracket": 1187, "kana_comma": 1188, "kana_conjunctive": 1189, "kana_e": 1194, "kana_fullstop": 1185, "kana_i": 1192, "kana_middledot": 1189, "kana_o": 1195, "kana_openingbracket": 1186, "kana_switch": 65406, "kana_tsu": 1199, "kana_tu": 1199, "kana_u": 1193, "kana_ya": 1196, "kana_yo": 1198, "kana_yu": 1197, "kappa": 930, "kcedilla": 1011, "kra": 930, "l": 108, "lacute": 485, "latincross": 2777, "lbelowdot": 16784951, "lcaron": 437, "lcedilla": 950, "leftanglebracket": 2748, "leftarrow": 2299, "leftcaret": 2979, "leftdoublequotemark": 2770, "leftmiddlecurlybrace": 2223, "leftopentriangle": 2764, "leftpointer": 2794, "leftradical": 2209, "leftshoe": 3034, "leftsinglequotemark": 2768, "leftt": 2548, "lefttack": 3036, "less": 60, "lessthanequal": 2236, "lf": 2533, "logicaland": 2270, "logicalor": 2271, "lowleftcorner": 2541, "lowrightcorner": 2538, "lstroke": 435, "m": 109, "mabovedot": 16784961, "macron": 175, "malesymbol": 2807, "maltesecross": 2800, "marker": 2751, "masculine": 186, "minus": 45, "minutes": 2774, "mu": 181, "multiply": 215, "musicalflat": 2806, "musicalsharp": 2805, "n": 110, "nabla": 2245, "nacute": 497, "ncaron": 498, "ncedilla": 1009, "ninesubscript": 16785545, "ninesuperior": 16785529, "nl": 2536, "nobreakspace": 160, "notapproxeq": 16785991, "notelementof": 16785929, "notequal": 2237, "notidentical": 16786018, "notsign": 172, "ntilde": 241, "numbersign": 35, "numerosign": 1712, "o": 111, "oacute": 243, "obarred": 16777845, "obelowdot": 16785101, "ocaron": 16777682, "ocircumflex": 244, "ocircumflexacute": 16785105, "ocircumflexbelowdot": 16785113, "ocircumflexgrave": 16785107, "ocircumflexhook": 16785109, "ocircumflextilde": 16785111, "odiaeresis": 246, "odoubleacute": 501, "oe": 5053, "ogonek": 434, "ograve": 242, "ohook": 16785103, "ohorn": 16777633, "ohornacute": 16785115, "ohornbelowdot": 16785123, "ohorngrave": 16785117, "ohornhook": 16785119, "ohorntilde": 16785121, "omacron": 1010, "oneeighth": 2755, "onefifth": 2738, "onehalf": 189, "onequarter": 188, "onesixth": 2742, "onesubscript": 16785537, "onesuperior": 185, "onethird": 2736, "ooblique": 248, "openrectbullet": 2786, "openstar": 2789, "opentribulletdown": 2788, "opentribulletup": 2787, "ordfeminine": 170, "ordmasculine": 186, "oslash": 248, "otilde": 245, "overbar": 3008, "overline": 1150, "p": 112, "pabovedot": 16784983, "paragraph": 182, "parenleft": 40, "parenright": 41, "partdifferential": 16785922, "partialderivative": 2287, "percent": 37, "period": 46, "periodcentered": 183, "permille": 2773, "phonographcopyright": 2811, "plus": 43, "plusminus": 177, "prescription": 2772, "prolongedsound": 1200, "punctspace": 2726, "q": 113, "quad": 3020, "question": 63, "questiondown": 191, "quotedbl": 34, "quoteleft": 96, "quoteright": 39, "r": 114, "racute": 480, "radical": 2262, "rcaron": 504, "rcedilla": 947, "registered": 174, "rightanglebracket": 2750, "rightarrow": 2301, "rightcaret": 2982, "rightdoublequotemark": 2771, "rightmiddlecurlybrace": 2224, "rightmiddlesummation": 2231, "rightopentriangle": 2765, "rightpointer": 2795, "rightshoe": 3032, "rightsinglequotemark": 2769, "rightt": 2549, "righttack": 3068, "s": 115, "sabovedot": 16784993, "sacute": 438, "scaron": 441, "scedilla": 442, "schwa": 16777817, "scircumflex": 766, "script_switch": 65406, "seconds": 2775, "section": 167, "semicolon": 59, "semivoicedsound": 1247, "seveneighths": 2758, "sevensubscript": 16785543, "sevensuperior": 16785527, "signaturemark": 2762, "signifblank": 2732, "similarequal": 2249, "singlelowquotemark": 2813, "sixsubscript": 16785542, "sixsuperior": 16785526, "slash": 47, "soliddiamond": 2528, "space": 32, "squareroot": 16785946, "ssharp": 223, "sterling": 163, "stricteq": 16786019, "t": 116, "tabovedot": 16785003, "tcaron": 443, "tcedilla": 510, "telephone": 2809, "telephonerecorder": 2810, "therefore": 2240, "thinspace": 2727, "thorn": 254, "threeeighths": 2756, "threefifths": 2740, "threequarters": 190, "threesubscript": 16785539, "threesuperior": 179, "tintegral": 16785965, "topintegral": 2212, "topleftparens": 2219, "topleftradical": 2210, "topleftsqbracket": 2215, "topleftsummation": 2225, "toprightparens": 2221, "toprightsqbracket": 2217, "toprightsummation": 2229, "topt": 2551, "topvertsummationconnector": 2227, "trademark": 2761, "trademarkincircle": 2763, "tslash": 956, "twofifths": 2739, "twosubscript": 16785538, "twosuperior": 178, "twothirds": 2737, "u": 117, "uacute": 250, "ubelowdot": 16785125, "ubreve": 765, "ucircumflex": 251, "udiaeresis": 252, "udoubleacute": 507, "ugrave": 249, "uhook": 16785127, "uhorn": 16777648, "uhornacute": 16785129, "uhornbelowdot": 16785137, "uhorngrave": 16785131, "uhornhook": 16785133, "uhorntilde": 16785135, "umacron": 1022, "underbar": 3014, "underscore": 95, "union": 2269, "uogonek": 1017, "uparrow": 2300, "upcaret": 2985, "upleftcorner": 2540, "uprightcorner": 2539, "upshoe": 3011, "upstile": 3027, "uptack": 3022, "uring": 505, "utilde": 1021, "v": 118, "variation": 2241, "vertbar": 2552, "vertconnector": 2214, "voicedsound": 1246, "vt": 2537, "w": 119, "wacute": 16785027, "wcircumflex": 16777589, "wdiaeresis": 16785029, "wgrave": 16785025, "x": 120, "xabovedot": 16785035, "y": 121, "yacute": 253, "ybelowdot": 16785141, "ycircumflex": 16777591, "ydiaeresis": 255, "yen": 165, "ygrave": 16785139, "yhook": 16785143, "ytilde": 16785145, "z": 122, "zabovedot": 447, "zacute": 444, "zcaron": 446, "zerosubscript": 16785536, "zerosuperior": 16785520, "zstroke": 16777654, } Solaar-1.1.14/lib/logitech_receiver/000077500000000000000000000000001473525213700172605ustar00rootroot00000000000000Solaar-1.1.14/lib/logitech_receiver/__init__.py000066400000000000000000000017311473525213700213730ustar00rootroot00000000000000## Copyright (C) 2012-2013 Daniel Pavel ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. """Low-level interface for devices using Logitech HID++ protocol. Uses the HID api exposed through hidapi_impl.py, a Python thin layer over a native implementation. """ import logging logger = logging.getLogger(__name__) Solaar-1.1.14/lib/logitech_receiver/base.py000066400000000000000000000613631473525213700205550ustar00rootroot00000000000000## Copyright (C) 2012-2013 Daniel Pavel ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. """Base low-level functions as API for upper layers.""" from __future__ import annotations import dataclasses import logging import platform import struct import threading import typing from contextlib import contextmanager from random import getrandbits from time import time from typing import Any from typing import Callable from . import base_usb from . import common from . import descriptors from . import exceptions from .common import LOGITECH_VENDOR_ID from .common import BusID from .hidpp10_constants import ErrorCode as Hidpp10ErrorCode from .hidpp20_constants import ErrorCode as Hidpp20ErrorCode if typing.TYPE_CHECKING: import gi from hidapi.common import DeviceInfo gi.require_version("Gdk", "3.0") from gi.repository import GLib # NOQA: E402 if platform.system() == "Linux": import hidapi.udev_impl as hidapi else: import hidapi.hidapi_impl as hidapi logger = logging.getLogger(__name__) class HIDAPI(typing.Protocol): def find_paired_node_wpid(self, receiver_path: str, index: int): ... def find_paired_node(self, receiver_path: str, index: int, timeout: int): ... def open(self, vendor_id, product_id, serial=None): ... def open_path(self, path): ... def enumerate(self, filter_func: Callable[[int, int, int, bool, bool], dict[str, typing.Any]]) -> DeviceInfo: ... def monitor_glib( self, glib: GLib, callback: Callable, filter_func: Callable[[int, int, int, bool, bool], dict[str, typing.Any]] ) -> None: ... def read(self, device_handle, bytes_count, timeout_ms): ... def write(self, device_handle: int, data: bytes) -> int: ... def close(self, device_handle) -> None: ... hidapi = typing.cast(HIDAPI, hidapi) SHORT_MESSAGE_SIZE = 7 _LONG_MESSAGE_SIZE = 20 _MEDIUM_MESSAGE_SIZE = 15 _MAX_READ_SIZE = 32 HIDPP_SHORT_MESSAGE_ID = 0x10 HIDPP_LONG_MESSAGE_ID = 0x11 DJ_MESSAGE_ID = 0x20 """Default timeout on read (in seconds).""" DEFAULT_TIMEOUT = 4 # the receiver itself should reply very fast, within 500ms _RECEIVER_REQUEST_TIMEOUT = 0.9 # devices may reply a lot slower, as the call has to go wireless to them and come back _DEVICE_REQUEST_TIMEOUT = DEFAULT_TIMEOUT # when pinging, be extra patient (no longer) _PING_TIMEOUT = DEFAULT_TIMEOUT @dataclasses.dataclass class HIDPPNotification: report_id: int devnumber: int sub_id: int address: int data: bytes def __str__(self): text_as_hex = common.strhex(self.data) return f"Notification({self.report_id:02x},{self.devnumber},{self.sub_id:02X},{self.address:02X},{text_as_hex})" def _usb_device(product_id: int, usb_interface: int) -> dict[str, Any]: return { "vendor_id": LOGITECH_VENDOR_ID, "product_id": product_id, "bus_id": BusID.USB, "usb_interface": usb_interface, "isDevice": True, } def _bluetooth_device(product_id: int) -> dict[str, Any]: return {"vendor_id": LOGITECH_VENDOR_ID, "product_id": product_id, "bus_id": BusID.BLUETOOTH, "isDevice": True} KNOWN_DEVICE_IDS = [] for _ignore, d in descriptors.DEVICES.items(): if d.usbid: usb_interface = d.interface if d.interface else 2 KNOWN_DEVICE_IDS.append(_usb_device(d.usbid, usb_interface)) if d.btid: KNOWN_DEVICE_IDS.append(_bluetooth_device(d.btid)) def _other_device_check(bus_id: int, vendor_id: int, product_id: int) -> dict[str, Any] | None: """Check whether product is a Logitech USB-connected or Bluetooth device based on bus, vendor, and product IDs This allows Solaar to support receiverless HID++ 2.0 devices that it knows nothing about""" if vendor_id != LOGITECH_VENDOR_ID: return device_info = None if bus_id == BusID.USB and (0xC07D <= product_id <= 0xC094 or 0xC32B <= product_id <= 0xC344): device_info = _usb_device(product_id, 2) elif bus_id == BusID.BLUETOOTH and (0xB012 <= product_id <= 0xB0FF or 0xB317 <= product_id <= 0xB3FF): device_info = _bluetooth_device(product_id) return device_info def product_information(usb_id: int) -> dict[str, Any]: """Returns hardcoded information from USB receiver.""" return base_usb.get_receiver_info(usb_id) def _match(record: dict[str, Any], bus_id: int, vendor_id: int, product_id: int): return ( (record.get("bus_id") is None or record.get("bus_id") == bus_id) and (record.get("vendor_id") is None or record.get("vendor_id") == vendor_id) and (record.get("product_id") is None or record.get("product_id") == product_id) ) def _filter_receivers( bus_id: int, vendor_id: int, product_id: int, _hidpp_short: bool = False, _hidpp_long: bool = False ) -> dict[str, Any]: """Check that this product is a Logitech receiver and return it. Filters based on bus_id, vendor_id and product_id. If so return the receiver record for further checking. """ try: record = base_usb.get_receiver_info(product_id) if _match(record, bus_id, vendor_id, product_id): return record except ValueError: pass if vendor_id == LOGITECH_VENDOR_ID and 0xC500 <= product_id <= 0xC5FF: # unknown receiver return {"vendor_id": vendor_id, "product_id": product_id, "bus_id": bus_id, "isDevice": False} return None def receivers(): """Enumerate all the receivers attached to the machine.""" yield from hidapi.enumerate(_filter_receivers) def _filter_products_of_interest( bus_id: int, vendor_id: int, product_id: int, hidpp_short: bool = False, hidpp_long: bool = False ) -> dict[str, Any] | None: """Check that this product is of interest and if so return the device record for further checking""" record = _filter_receivers(bus_id, vendor_id, product_id, hidpp_short, hidpp_long) if record: # known or unknown receiver return record for record in KNOWN_DEVICE_IDS: if _match(record, bus_id, vendor_id, product_id): return record if hidpp_short or hidpp_long: # unknown devices that use HID++ return {"vendor_id": vendor_id, "product_id": product_id, "bus_id": bus_id, "isDevice": True} elif hidpp_short is None and hidpp_long is None: # unknown devices in correct range of IDs return _other_device_check(bus_id, vendor_id, product_id) return None def receivers_and_devices(): """Enumerate all the receivers and devices directly attached to the machine.""" yield from hidapi.enumerate(_filter_products_of_interest) def notify_on_receivers_glib(glib: GLib, callback: Callable): """Watch for matching devices and notifies the callback on the GLib thread. Parameters ---------- glib GLib instance. """ return hidapi.monitor_glib(glib, callback, _filter_products_of_interest) def open_path(path): """Checks if the given Linux device path points to the right UR device. :param path: the Linux device path. The UR physical device may expose multiple linux devices with the same interface, so we have to check for the right one. At this moment the only way to distinguish betheen them is to do a test ping on an invalid (attached) device number (i.e., 0), expecting a 'ping failed' reply. :returns: an open receiver handle if this is the right Linux device, or ``None``. """ return hidapi.open_path(path) def open(): """Opens the first Logitech Unifying Receiver found attached to the machine. :returns: An open file handle for the found receiver, or ``None``. """ for rawdevice in receivers(): handle = open_path(rawdevice.path) if handle: return handle def close(handle): """Closes a HID device handle.""" if handle: try: if isinstance(handle, int): hidapi.close(handle) else: handle.close() return True except Exception: pass return False def write(handle, devnumber, data, long_message=False): """Writes some data to the receiver, addressed to a certain device. :param handle: an open UR handle. :param devnumber: attached device number. :param data: data to send, up to 5 bytes. The first two (required) bytes of data must be the SubId and address. :raises NoReceiver: if the receiver is no longer available, i.e. has been physically removed from the machine, or the kernel driver has been unloaded. The handle will be closed automatically. """ # the data is padded to either 5 or 18 bytes assert data is not None assert isinstance(data, bytes), (repr(data), type(data)) if long_message or len(data) > SHORT_MESSAGE_SIZE - 2 or data[:1] == b"\x82": wdata = struct.pack("!BB18s", HIDPP_LONG_MESSAGE_ID, devnumber, data) else: wdata = struct.pack("!BB5s", HIDPP_SHORT_MESSAGE_ID, devnumber, data) if logger.isEnabledFor(logging.DEBUG): logger.debug( "(%s) <= w[%02X %02X %s %s]", handle, ord(wdata[:1]), devnumber, common.strhex(wdata[2:4]), common.strhex(wdata[4:]), ) try: hidapi.write(int(handle), wdata) except Exception as reason: logger.error("write failed, assuming handle %r no longer available", handle) close(handle) raise exceptions.NoReceiver(reason=reason) from reason def read(handle, timeout=DEFAULT_TIMEOUT): """Read some data from the receiver. Usually called after a write (feature call), to get the reply. :param: handle open handle to the receiver :param: timeout how long to wait for a reply, in seconds :returns: a tuple of (devnumber, message data), or `None` :raises NoReceiver: if the receiver is no longer available, i.e. has been physically removed from the machine, or the kernel driver has been unloaded. The handle will be closed automatically. """ reply = _read(handle, timeout) if reply: return reply def _is_relevant_message(data: bytes) -> bool: """Checks if given id is a HID++ or DJ message. Applies sanity checks on message report ID and message size. """ assert isinstance(data, bytes), (repr(data), type(data)) # mapping from report_id to message length report_lengths = { HIDPP_SHORT_MESSAGE_ID: SHORT_MESSAGE_SIZE, HIDPP_LONG_MESSAGE_ID: _LONG_MESSAGE_SIZE, DJ_MESSAGE_ID: _MEDIUM_MESSAGE_SIZE, 0x21: _MAX_READ_SIZE, } report_id = ord(data[:1]) if report_id in report_lengths: if report_lengths.get(report_id) == len(data): return True else: logger.warning(f"unexpected message size: report_id {report_id:02X} message {common.strhex(data)}") return False def _read(handle, timeout): """Read an incoming packet from the receiver. :returns: a tuple of (report_id, devnumber, data), or `None`. :raises NoReceiver: if the receiver is no longer available, i.e. has been physically removed from the machine, or the kernel driver has been unloaded. The handle will be closed automatically. """ try: # convert timeout to milliseconds, the hidapi expects it timeout = int(timeout * 1000) data = hidapi.read(int(handle), _MAX_READ_SIZE, timeout) except Exception as reason: logger.warning("read failed, assuming handle %r no longer available", handle) close(handle) raise exceptions.NoReceiver(reason=reason) from reason if data and _is_relevant_message(data): # ignore messages that fail check report_id = ord(data[:1]) devnumber = ord(data[1:2]) if logger.isEnabledFor(logging.DEBUG) and ( report_id != DJ_MESSAGE_ID or ord(data[2:3]) > 0x10 ): # ignore DJ input messages logger.debug( "(%s) => r[%02X %02X %s %s]", handle, report_id, devnumber, common.strhex(data[2:4]), common.strhex(data[4:]), ) return report_id, devnumber, data[2:] def _skip_incoming(handle, ihandle, notifications_hook): """Read anything already in the input buffer. Used by request() and ping() before their write. """ while True: try: # read whatever is already in the buffer, if any data = hidapi.read(ihandle, _MAX_READ_SIZE, 0) except Exception as reason: logger.error("read failed, assuming receiver %s no longer available", handle) close(handle) raise exceptions.NoReceiver(reason=reason) from reason if data: if _is_relevant_message(data): # only process messages that pass check # report_id = ord(data[:1]) if notifications_hook: n = make_notification(ord(data[:1]), ord(data[1:2]), data[2:]) if n: notifications_hook(n) else: # nothing in the input buffer, we're done return def make_notification(report_id: int, devnumber: int, data: bytes) -> HIDPPNotification | None: """Guess if this is a notification (and not just a request reply), and return a Notification if it is.""" sub_id = ord(data[:1]) if sub_id & 0x80 == 0x80: # this is either a HID++1.0 register r/w, or an error reply return None # DJ input records are not notifications if report_id == DJ_MESSAGE_ID and (sub_id < 0x10): return None address = ord(data[1:2]) if sub_id == 0x00 and (address & 0x0F == 0x00): # this is a no-op notification - don't do anything with it return None if ( # standard HID++ 1.0 notification, SubId may be 0x40 - 0x7F (sub_id >= 0x40) # noqa: E131 or # custom HID++1.0 battery events, where SubId is 0x07/0x0D (sub_id in (0x07, 0x0D) and len(data) == 5 and data[4:5] == b"\x00") or # custom HID++1.0 illumination event, where SubId is 0x17 (sub_id == 0x17 and len(data) == 5) or # HID++ 2.0 feature notifications have the SoftwareID 0 (address & 0x0F == 0x00) ): # noqa: E129 return HIDPPNotification(report_id, devnumber, sub_id, address, data[2:]) return None request_lock = threading.Lock() # serialize all requests handles_lock = {} def handle_lock(handle): with request_lock: if handles_lock.get(handle) is None: if logger.isEnabledFor(logging.INFO): logger.info("New lock %s", repr(handle)) handles_lock[handle] = threading.Lock() # Serialize requests on the handle return handles_lock[handle] # context manager for locks with a timeout @contextmanager def acquire_timeout(lock, handle, timeout): result = lock.acquire(timeout=timeout) try: if not result: logger.error("lock on handle %d not acquired, probably due to timeout", int(handle)) yield result finally: if result: lock.release() def _get_next_sw_id() -> int: """Returns 'random' software ID to separate replies from different devices. Cycle the HID++ 2.0 software ID from 0x2 to 0xF to separate results and notifications. """ if not hasattr(_get_next_sw_id, "software_id"): _get_next_sw_id.software_id = 0xF if _get_next_sw_id.software_id < 0xF: _get_next_sw_id.software_id += 1 else: _get_next_sw_id.software_id = 2 return _get_next_sw_id.software_id def find_paired_node(receiver_path: str, index: int, timeout: int): """Find the node of a device paired with a receiver.""" return hidapi.find_paired_node(receiver_path, index, timeout) def find_paired_node_wpid(receiver_path: str, index: int): """Find the node of a device paired with a receiver. Get wpid from udev. """ return hidapi.find_paired_node_wpid(receiver_path, index) # a very few requests (e.g., host switching) do not expect a reply, but use no_reply=True with extreme caution def request( handle, devnumber, request_id: int, *params, no_reply: bool = False, return_error: bool = False, long_message: bool = False, protocol: float = 1.0, ): """Makes a feature call to a device and waits for a matching reply. :param handle: an open UR handle. :param devnumber: attached device number. :param request_id: a 16-bit integer. :param params: parameters for the feature call, 3 to 16 bytes. :returns: the reply data, or ``None`` if some error occurred. or no reply expected """ with acquire_timeout(handle_lock(handle), handle, 10.0): assert isinstance(request_id, int) if (devnumber != 0xFF or protocol >= 2.0) and request_id < 0x8000: # Always set the most significant bit (8) in SoftwareId, # to make notifications easier to distinguish from request replies. # This only applies to peripheral requests, ofc. sw_id = _get_next_sw_id() request_id = (request_id & 0xFFF0) | sw_id # was 0x08 | getrandbits(3) timeout = _RECEIVER_REQUEST_TIMEOUT if devnumber == 0xFF else _DEVICE_REQUEST_TIMEOUT # be extra patient on long register read if request_id & 0xFF00 == 0x8300: timeout *= 2 if params: params = b"".join(struct.pack("B", p) if isinstance(p, int) else p for p in params) else: params = b"" request_data = struct.pack("!H", request_id) + params ihandle = int(handle) notifications_hook = getattr(handle, "notifications_hook", None) try: _skip_incoming(handle, ihandle, notifications_hook) except exceptions.NoReceiver: logger.warning("device or receiver disconnected") return None write(ihandle, devnumber, request_data, long_message) if no_reply: return None # we consider timeout from this point request_started = time() delta = 0 while delta < timeout: reply = _read(handle, timeout) if reply: report_id, reply_devnumber, reply_data = reply if reply_devnumber == devnumber or reply_devnumber == devnumber ^ 0xFF: # BT device returning 0x00 if ( report_id == HIDPP_SHORT_MESSAGE_ID and reply_data[:1] == b"\x8f" and reply_data[1:3] == request_data[:2] ): error = ord(reply_data[3:4]) if logger.isEnabledFor(logging.DEBUG): logger.debug( "(%s) device 0x%02X error on request {%04X}: %d = %s", handle, devnumber, request_id, error, Hidpp10ErrorCode(error), ) return Hidpp10ErrorCode(error) if return_error else None if reply_data[:1] == b"\xff" and reply_data[1:3] == request_data[:2]: # a HID++ 2.0 feature call returned with an error error = ord(reply_data[3:4]) logger.error( "(%s) device %d error on feature request {%04X}: %d = %s", handle, devnumber, request_id, error, Hidpp20ErrorCode(error), ) raise exceptions.FeatureCallError( number=devnumber, request=request_id, error=error, params=params, ) if reply_data[:2] == request_data[:2]: if devnumber == 0xFF: if request_id == 0x83B5 or request_id == 0x81F1: # these replies have to match the first parameter as well if reply_data[2:3] == params[:1]: return reply_data[2:] else: # hm, not matching my request, and certainly not a notification continue else: return reply_data[2:] else: return reply_data[2:] else: # a reply was received, but did not match our request in any way # reset the timeout starting point request_started = time() if notifications_hook: n = make_notification(report_id, reply_devnumber, reply_data) if n: notifications_hook(n) delta = time() - request_started logger.warning( "timeout (%0.2f/%0.2f) on device %d request {%04X} params [%s]", delta, timeout, devnumber, request_id, common.strhex(params), ) # raise DeviceUnreachable(number=devnumber, request=request_id) def ping(handle, devnumber, long_message: bool = False): """Check if a device is connected to the receiver. :returns: The HID protocol supported by the device, as a floating point number, if the device is active. """ if logger.isEnabledFor(logging.DEBUG): logger.debug("(%s) pinging device %d", handle, devnumber) with acquire_timeout(handle_lock(handle), handle, 10.0): notifications_hook = getattr(handle, "notifications_hook", None) try: _skip_incoming(handle, int(handle), notifications_hook) except exceptions.NoReceiver: logger.warning("device or receiver disconnected") return # randomize the mark byte to be able to identify the ping reply sw_id = _get_next_sw_id() request_id = 0x0010 | sw_id # was 0x0018 | getrandbits(3) request_data = struct.pack("!HBBB", request_id, 0, 0, getrandbits(8)) write(int(handle), devnumber, request_data, long_message) request_started = time() # we consider timeout from this point delta = 0 while delta < _PING_TIMEOUT: reply = _read(handle, _PING_TIMEOUT) if reply: report_id, reply_devnumber, reply_data = reply if reply_devnumber == devnumber or reply_devnumber == devnumber ^ 0xFF: # BT device returning 0x00 if reply_data[:2] == request_data[:2] and reply_data[4:5] == request_data[-1:]: # HID++ 2.0+ device, currently connected return ord(reply_data[2:3]) + ord(reply_data[3:4]) / 10.0 if ( report_id == HIDPP_SHORT_MESSAGE_ID and reply_data[:1] == b"\x8f" and reply_data[1:3] == request_data[:2] ): # error response error = ord(reply_data[3:4]) if error == Hidpp10ErrorCode.INVALID_SUB_ID_COMMAND: # a valid reply from a HID++ 1.0 device return 1.0 if error in [Hidpp10ErrorCode.RESOURCE_ERROR, Hidpp10ErrorCode.CONNECTION_REQUEST_FAILED]: return # device unreachable if error == Hidpp10ErrorCode.UNKNOWN_DEVICE: # no paired device with that number logger.error("(%s) device %d error on ping request: unknown device", handle, devnumber) raise exceptions.NoSuchDevice(number=devnumber, request=request_id) if notifications_hook: n = make_notification(report_id, reply_devnumber, reply_data) if n: notifications_hook(n) delta = time() - request_started logger.warning("(%s) timeout (%0.2f/%0.2f) on device %d ping", handle, delta, _PING_TIMEOUT, devnumber) Solaar-1.1.14/lib/logitech_receiver/base_usb.py000066400000000000000000000157701473525213700214270ustar00rootroot00000000000000## Copyright (C) 2012-2013 Daniel Pavel ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. """Collection of known Logitech product IDs. According to Logitech, they use the following product IDs (as of September 2020) USB product IDs for receivers: 0xC526 - 0xC5xx Wireless PIDs for hidpp10 devices: 0x2006 - 0x2019 Wireless PIDs for hidpp20 devices: 0x4002 - 0x4097, 0x4101 - 0x4102 USB product IDs for hidpp20 devices: 0xC07D - 0xC094, 0xC32B - 0xC344 Bluetooth product IDs (for hidpp20 devices): 0xB012 - 0xB0xx, 0xB32A - 0xB3xx USB ids of Logitech wireless receivers. Only receivers supporting the HID++ protocol can go in here. """ from solaar.i18n import _ # max_devices is only used for receivers that do not support reading from Registers.RECEIVER_INFO offset 0x03, default # to 1. # may_unpair is only used for receivers that do not support reading from Registers.RECEIVER_INFO offset 0x03, # default to False. # unpair is for receivers that do support reading from Registers.RECEIVER_INFO offset 0x03, no default. ## should this last be changed so that may_unpair is used for all receivers? writing to Registers.RECEIVER_PAIRING ## doesn't seem right LOGITECH_VENDOR_ID = 0x046D def _bolt_receiver(product_id: int) -> dict: return { "vendor_id": LOGITECH_VENDOR_ID, "product_id": product_id, "usb_interface": 2, "name": _("Bolt Receiver"), "receiver_kind": "bolt", "max_devices": 6, "may_unpair": True, } def _unifying_receiver(product_id: int) -> dict: return { "vendor_id": LOGITECH_VENDOR_ID, "product_id": product_id, "usb_interface": 2, "name": _("Unifying Receiver"), "receiver_kind": "unifying", "may_unpair": True, } def _nano_receiver(product_id: int) -> dict: return { "vendor_id": LOGITECH_VENDOR_ID, "product_id": product_id, "usb_interface": 1, "name": _("Nano Receiver"), "receiver_kind": "nano", "may_unpair": False, "re_pairs": True, } def _nano_receiver_no_unpair(product_id: int) -> dict: return { "vendor_id": LOGITECH_VENDOR_ID, "product_id": product_id, "usb_interface": 1, "name": _("Nano Receiver"), "receiver_kind": "nano", "may_unpair": False, "unpair": False, "re_pairs": True, } def _nano_receiver_max2(product_id: int) -> dict: return { "vendor_id": LOGITECH_VENDOR_ID, "product_id": product_id, "usb_interface": 1, "name": _("Nano Receiver"), "receiver_kind": "nano", "max_devices": 2, "may_unpair": False, "re_pairs": True, } def _lenovo_receiver(product_id: int) -> dict: return { "vendor_id": 6127, "product_id": product_id, "usb_interface": 1, "name": _("Nano Receiver"), "receiver_kind": "nano", "may_unpair": False, } def _lightspeed_receiver(product_id: int) -> dict: return { "vendor_id": LOGITECH_VENDOR_ID, "product_id": product_id, "usb_interface": 2, "receiver_kind": "lightspeed", "name": _("Lightspeed Receiver"), "may_unpair": False, } def _ex100_receiver(product_id: int) -> dict: return { "vendor_id": LOGITECH_VENDOR_ID, "product_id": product_id, "usb_interface": 1, "name": _("EX100 Receiver 27 Mhz"), "receiver_kind": "27Mhz", "max_devices": 4, "may_unpair": False, "re_pairs": True, } # Receivers added here should also be listed in # share/solaar/io.github.pwr_solaar.solaar.meta-info.xml # Look in https://github.com/torvalds/linux/blob/master/drivers/hid/hid-ids.h # Bolt receivers (marked with the yellow lightning bolt logo) BOLT_RECEIVER_C548 = _bolt_receiver(0xC548) # standard Unifying receivers (marked with the orange Unifying logo) UNIFYING_RECEIVER_C52B = _unifying_receiver(0xC52B) UNIFYING_RECEIVER_C532 = _unifying_receiver(0xC532) # Nano receivers (usually sold with low-end devices) NANO_RECEIVER_ADVANCED = _nano_receiver_no_unpair(0xC52F) NANO_RECEIVER_C518 = _nano_receiver(0xC518) NANO_RECEIVER_C51A = _nano_receiver(0xC51A) NANO_RECEIVER_C51B = _nano_receiver(0xC51B) NANO_RECEIVER_C521 = _nano_receiver(0xC521) NANO_RECEIVER_C525 = _nano_receiver(0xC525) NANO_RECEIVER_C526 = _nano_receiver(0xC526) NANO_RECEIVER_C52E = _nano_receiver_no_unpair(0xC52E) NANO_RECEIVER_C531 = _nano_receiver(0xC531) NANO_RECEIVER_C534 = _nano_receiver_max2(0xC534) NANO_RECEIVER_C535 = _nano_receiver(0xC535) # branded as Dell NANO_RECEIVER_C537 = _nano_receiver(0xC537) NANO_RECEIVER_6042 = _lenovo_receiver(0x6042) # Lightspeed receivers (usually sold with gaming devices) LIGHTSPEED_RECEIVER_C539 = _lightspeed_receiver(0xC539) LIGHTSPEED_RECEIVER_C53A = _lightspeed_receiver(0xC53A) LIGHTSPEED_RECEIVER_C53D = _lightspeed_receiver(0xC53D) LIGHTSPEED_RECEIVER_C53F = _lightspeed_receiver(0xC53F) LIGHTSPEED_RECEIVER_C541 = _lightspeed_receiver(0xC541) LIGHTSPEED_RECEIVER_C545 = _lightspeed_receiver(0xC545) LIGHTSPEED_RECEIVER_C547 = _lightspeed_receiver(0xC547) # EX100 old style receiver pre-unifying protocol EX100_27MHZ_RECEIVER_C517 = _ex100_receiver(0xC517) KNOWN_RECEIVERS = ( BOLT_RECEIVER_C548, UNIFYING_RECEIVER_C52B, UNIFYING_RECEIVER_C532, NANO_RECEIVER_ADVANCED, NANO_RECEIVER_C518, NANO_RECEIVER_C51A, NANO_RECEIVER_C51B, NANO_RECEIVER_C521, NANO_RECEIVER_C525, NANO_RECEIVER_C526, NANO_RECEIVER_C52E, NANO_RECEIVER_C531, NANO_RECEIVER_C534, NANO_RECEIVER_C535, NANO_RECEIVER_C537, NANO_RECEIVER_6042, LIGHTSPEED_RECEIVER_C539, LIGHTSPEED_RECEIVER_C53A, LIGHTSPEED_RECEIVER_C53D, LIGHTSPEED_RECEIVER_C53F, LIGHTSPEED_RECEIVER_C541, LIGHTSPEED_RECEIVER_C545, LIGHTSPEED_RECEIVER_C547, EX100_27MHZ_RECEIVER_C517, ) def get_receiver_info(product_id: int) -> dict: """Returns hardcoded information about Logitech receiver. Parameters ---------- product_id Product ID of receiver e.g. 0xC548 for a Logitech Bolt receiver. Returns ------- dict Product info with mandatory vendor_id, product_id, usb_interface, name, receiver_kind """ for receiver in KNOWN_RECEIVERS: if product_id == receiver.get("product_id"): return receiver raise ValueError(f"Unknown product ID '0x{product_id:02X}") Solaar-1.1.14/lib/logitech_receiver/common.py000066400000000000000000000413741473525213700211330ustar00rootroot00000000000000## Copyright (C) 2012-2013 Daniel Pavel ## Copyright (C) 2014-2024 Solaar Contributors https://pwr-solaar.github.io/Solaar/ ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. from __future__ import annotations import binascii import dataclasses import typing from enum import IntEnum from typing import Generator from typing import Iterable from typing import Optional from typing import Union import yaml from solaar.i18n import _ if typing.TYPE_CHECKING: from logitech_receiver.hidpp20_constants import FirmwareKind LOGITECH_VENDOR_ID = 0x046D def crc16(data: bytes): """ CRC-16 (CCITT) implemented with a precomputed lookup table """ table = [ 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067, 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0, ] crc = 0xFFFF for byte in data: crc = (crc << 8) ^ table[(crc >> 8) ^ byte] crc &= 0xFFFF # important, crc must stay 16bits all the way through return crc class NamedInt(int): """A regular Python integer with an attached name. Caution: comparison with strings will also match this NamedInt's name (case-insensitive).""" def __new__(cls, value, name): assert isinstance(name, str) obj = int.__new__(cls, value) obj.name = str(name) return obj def bytes(self, count=2): return int2bytes(self, count) def __eq__(self, other): if other is None: return False if isinstance(other, NamedInt): return int(self) == int(other) and self.name == other.name if isinstance(other, int): return int(self) == int(other) if isinstance(other, str): return self.name.lower() == other.lower() # this should catch comparisons with bytes in Py3 if other is not None: raise TypeError("Unsupported type " + str(type(other))) def __ne__(self, other): return not self.__eq__(other) def __hash__(self): return int(self) def __str__(self): return self.name def __repr__(self): return f"NamedInt({int(self)}, {self.name!r})" @classmethod def from_yaml(cls, loader, node): args = loader.construct_mapping(node) return cls(value=args["value"], name=args["name"]) @classmethod def to_yaml(cls, dumper, data): return dumper.represent_mapping("!NamedInt", {"value": int(data), "name": data.name}, flow_style=True) yaml.SafeLoader.add_constructor("!NamedInt", NamedInt.from_yaml) yaml.add_representer(NamedInt, NamedInt.to_yaml) class NamedInts: """An ordered set of NamedInt values. Indexing can be made by int or string, and will return the corresponding NamedInt if it exists in this set, or `None`. Extracting slices will return all present NamedInts in the given interval (extended slices are not supported). Assigning a string to an indexed int will create a new NamedInt in this set; if the value already exists in the set (int or string), ValueError will be raised. """ __slots__ = ("__dict__", "_values", "_indexed", "_fallback", "_is_sorted") def __init__(self, dict_=None, **kwargs): def _readable_name(n): return n.replace("__", "/").replace("_", " ") # print (repr(kwargs)) elements = dict_ if dict_ else kwargs values = {k: NamedInt(v, _readable_name(k)) for (k, v) in elements.items()} self.__dict__ = values self._is_sorted = False self._values = list(values.values()) self._sort_values() self._indexed = {int(v): v for v in self._values} # assert len(values) == len(self._indexed) # "(%d) %r\n=> (%d) %r" % (len(values), values, len(self._indexed), self._indexed) self._fallback = None @classmethod def list(cls, items, name_generator=lambda x: str(x)): # noqa: B008 values = {name_generator(x): x for x in items} return NamedInts(**values) @classmethod def range(cls, from_value, to_value, name_generator=lambda x: str(x), step=1): # noqa: B008 values = {name_generator(x): x for x in range(from_value, to_value + 1, step)} return NamedInts(**values) def flag_names(self, value): unknown_bits = value for k in self._indexed: assert bin(k).count("1") == 1 if k & value == k: unknown_bits &= ~k yield str(self._indexed[k]) if unknown_bits: yield f"unknown:{unknown_bits:06X}" def _sort_values(self): self._values = sorted(self._values) self._is_sorted = True def __getitem__(self, index): if isinstance(index, int): if index in self._indexed: return self._indexed[int(index)] if self._fallback: value = NamedInt(index, self._fallback(index)) self._indexed[index] = value self._values.append(value) self._is_sorted = False self._sort_values() return value elif isinstance(index, str): if index in self.__dict__: return self.__dict__[index] return next((x for x in self._values if str(x) == index), None) elif isinstance(index, slice): values = self._values if self._is_sorted else sorted(self._values) if index.start is None and index.stop is None: return values[:] v_start = int(values[0]) if index.start is None else int(index.start) v_stop = (values[-1] + 1) if index.stop is None else int(index.stop) if v_start > v_stop or v_start > values[-1] or v_stop <= values[0]: return [] if v_start <= values[0] and v_stop > values[-1]: return values[:] start_index = 0 stop_index = len(values) for i, value in enumerate(values): if value < v_start: start_index = i + 1 elif index.stop is None: break if value >= v_stop: stop_index = i break return values[start_index:stop_index] def __setitem__(self, index, name): assert isinstance(index, int), type(index) if isinstance(name, NamedInt): assert int(index) == int(name), repr(index) + " " + repr(name) value = name elif isinstance(name, str): value = NamedInt(index, name) else: raise TypeError("name must be a string") if str(value) in self.__dict__: raise ValueError(f"{value} ({int(value)}) already known") if int(value) in self._indexed: raise ValueError(f"{int(value)} ({value}) already known") self._values.append(value) self._is_sorted = False self._sort_values() self.__dict__[str(value)] = value self._indexed[int(value)] = value def __contains__(self, value): if isinstance(value, NamedInt): return self[value] == value elif isinstance(value, int): return value in self._indexed elif isinstance(value, str): return value in self.__dict__ or value in self._values def __iter__(self): yield from self._values def __len__(self): return len(self._values) def __repr__(self): return f"NamedInts({', '.join(repr(v) for v in self._values)})" def __or__(self, other): return NamedInts(**self.__dict__, **other.__dict__) def __eq__(self, other): return isinstance(other, self.__class__) and self._values == other._values def flag_names(enum_class: Iterable, value: int) -> Generator[str]: """Extracts single bit flags from a (binary) number. Parameters ---------- enum_class Enum class to extract flags from. value Number to extract binary flags from. """ indexed = {item.value: item.name for item in enum_class} unknown_bits = value for k in indexed: # Ensure that the key (flag value) is a power of 2 (a single bit flag) assert bin(k).count("1") == 1 if k & value == k: unknown_bits &= ~k yield indexed[k].lower() # Yield any remaining unknown bits if unknown_bits != 0: yield f"unknown:{unknown_bits:06X}" class UnsortedNamedInts(NamedInts): def _sort_values(self): pass def __or__(self, other): c = UnsortedNamedInts if isinstance(other, UnsortedNamedInts) else NamedInts return c(**self.__dict__, **other.__dict__) def strhex(x): assert x is not None """Produce a hex-string representation of a sequence of bytes.""" return binascii.hexlify(x).decode("ascii").upper() def bytes2int(x, signed=False): return int.from_bytes(x, signed=signed, byteorder="big") def int2bytes(x, count=None, signed=False): if count: return x.to_bytes(length=count, byteorder="big", signed=signed) else: return x.to_bytes(length=8, byteorder="big", signed=signed).lstrip(b"\x00") class KwException(Exception): """An exception that remembers all arguments passed to the constructor. They can be later accessed by simple member access. """ def __init__(self, **kwargs): super().__init__(kwargs) def __getattr__(self, k): try: return super().__getattr__(k) except AttributeError: return self.args[0].get(k) # was self.args[0][k] class FirmwareKind(IntEnum): Firmware = 0x00 Bootloader = 0x01 Hardware = 0x02 Other = 0x03 @dataclasses.dataclass class FirmwareInfo: kind: FirmwareKind name: str version: str extras: str | None class BatteryStatus(IntEnum): DISCHARGING = 0x00 RECHARGING = 0x01 ALMOST_FULL = 0x02 FULL = 0x03 SLOW_RECHARGE = 0x04 INVALID_BATTERY = 0x05 THERMAL_ERROR = 0x06 class BatteryLevelApproximation(IntEnum): EMPTY = 0 CRITICAL = 5 LOW = 20 GOOD = 50 FULL = 90 @dataclasses.dataclass class Battery: """Information about the current state of a battery""" ATTENTION_LEVEL = 5 level: Optional[Union[BatteryLevelApproximation, int]] next_level: Optional[Union[NamedInt, int]] status: Optional[BatteryStatus] voltage: Optional[int] light_level: Optional[int] = None # light level for devices with solaar recharging def __post_init__(self): if self.level is None: # infer level from status if needed and possible if self.status == BatteryStatus.FULL: self.level = BatteryLevelApproximation.FULL elif self.status in (BatteryStatus.ALMOST_FULL, BatteryStatus.RECHARGING): self.level = BatteryLevelApproximation.GOOD elif self.status == BatteryStatus.SLOW_RECHARGE: self.level = BatteryLevelApproximation.LOW def ok(self) -> bool: return self.status not in (BatteryStatus.INVALID_BATTERY, BatteryStatus.THERMAL_ERROR) and ( self.level is None or self.level > Battery.ATTENTION_LEVEL ) def charging(self) -> bool: return self.status in ( BatteryStatus.RECHARGING, BatteryStatus.ALMOST_FULL, BatteryStatus.FULL, BatteryStatus.SLOW_RECHARGE, ) def to_str(self) -> str: if isinstance(self.level, BatteryLevelApproximation): level = self.level.name.lower() status = self.status.name.lower().replace("_", " ") if self.status is not None else "Unknown" return _("Battery: %(level)s (%(status)s)") % {"level": _(level), "status": _(status)} elif isinstance(self.level, int): status = self.status.name.lower().replace("_", " ") if self.status is not None else "Unknown" return _("Battery: %(percent)d%% (%(status)s)") % {"percent": self.level, "status": _(status)} else: return "" class Alert(IntEnum): NONE = 0x00 NOTIFICATION = 0x01 SHOW_WINDOW = 0x02 ATTENTION = 0x04 ALL = 0xFF class Notification(IntEnum): NO_OPERATION = 0x00 CONNECT_DISCONNECT = 0x40 DJ_PAIRING = 0x41 CONNECTED = 0x42 RAW_INPUT = 0x49 PAIRING_LOCK = 0x4A POWER = 0x4B class BusID(IntEnum): USB = 0x03 BLUETOOTH = 0x05 Solaar-1.1.14/lib/logitech_receiver/descriptors.py000066400000000000000000000426631473525213700222060ustar00rootroot00000000000000## Copyright (C) 2012-2013 Daniel Pavel ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. """Devices (not receivers) known to Solaar. Solaar can handle many recent devices without having any entry here. An entry should only be added to fix problems, such as - the device's device ID or WPID falls outside the range that Solaar searches - the device uses a USB interface other than 2 - the name or codename should be different from what the device reports """ from .hidpp10_constants import DEVICE_KIND from .hidpp10_constants import Registers as Reg class _DeviceDescriptor: def __init__( self, name=None, kind=None, wpid=None, codename=None, protocol=None, registers=None, usbid=None, interface=None, btid=None, ): self.name = name self.kind = kind self.wpid = wpid self.codename = codename self.protocol = protocol self.registers = registers self.usbid = usbid self.interface = interface self.btid = btid self.settings = None DEVICES_WPID = {} DEVICES = {} def _D( name, codename=None, kind=None, wpid=None, protocol=None, registers=None, settings=None, usbid=None, interface=None, btid=None, ): if kind is None: kind = ( DEVICE_KIND.mouse if "Mouse" in name else DEVICE_KIND.keyboard if "Keyboard" in name else DEVICE_KIND.numpad if "Number Pad" in name else DEVICE_KIND.touchpad if "Touchpad" in name else DEVICE_KIND.trackball if "Trackball" in name else None ) assert kind is not None, f"descriptor for {name} does not have kind set" if protocol is not None: if wpid: for w in wpid if isinstance(wpid, tuple) else (wpid,): if protocol > 1.0: assert w[0:1] == "4", f"{name} has protocol {protocol:0.1f}, wpid {w}" else: if w[0:1] == "1": assert kind == DEVICE_KIND.mouse, f"{name} has protocol {protocol:0.1f}, wpid {w}" elif w[0:1] == "2": assert kind in ( DEVICE_KIND.keyboard, DEVICE_KIND.numpad, ), f"{name} has protocol {protocol:0.1f}, wpid {w}" device_descriptor = _DeviceDescriptor( name=name, kind=kind, wpid=wpid, codename=codename, protocol=protocol, registers=registers, usbid=usbid, interface=interface, btid=btid, ) if usbid: found = get_usbid(usbid) assert found is None, f"duplicate usbid in device descriptors: {found}" if btid: found = get_btid(btid) assert found is None, f"duplicate btid in device descriptors: {found}" assert codename not in DEVICES, f"duplicate codename in device descriptors: {DEVICES[codename]}" if codename: DEVICES[codename] = device_descriptor if wpid: for w in wpid if isinstance(wpid, tuple) else (wpid,): assert w not in DEVICES_WPID, f"duplicate wpid in device descriptors: {DEVICES_WPID[w]}" DEVICES_WPID[w] = device_descriptor def get_wpid(wpid): return DEVICES_WPID.get(wpid) def get_codename(codename): return DEVICES.get(codename) def get_usbid(usbid): if isinstance(usbid, str): usbid = int(usbid, 16) found = next((x for x in DEVICES.values() if x.usbid == usbid), None) return found def get_btid(btid): if isinstance(btid, str): btid = int(btid, 16) found = next((x for x in DEVICES.values() if x.btid == btid), None) return found # Some HID++1.0 registers and HID++2.0 features can be discovered at run-time, # so they are not specified here. # # State registers (battery, leds, some features, etc) are only used by # HID++ 1.0 devices, while HID++ 2.0 devices use features for the same # functionalities. # Well-known registers (in hex): # * 00 - notification flags (all devices) # 01 - mice: smooth scrolling # 07 - battery status # 09 - keyboards: FN swap (if it has the FN key) # 0D - battery charge # a device may have either the 07 or 0D register available; # no known device uses both # 51 - leds # 63 - mice: DPI # * F1 - firmware info # Some registers appear to be universally supported, no matter the HID++ version # (marked with *). The rest may or may not be supported, and their values may or # may not mean the same thing across different devices. # The 'codename' and 'kind' fields are usually guessed from the device name, # but in some cases (like the Logitech Cube) that heuristic fails and they have # to be specified. # # The 'protocol' and 'wpid' fields are optional (they can be discovered at # runtime), but specifying them here speeds up device discovery and reduces the # USB traffic Solaar has to do to fully identify peripherals. # # The 'registers' field indicates read-only registers, specifying a state. These # are valid (AFAIK) only to HID++ 1.0 devices. # The 'settings' field indicates a read/write register; based on them Solaar # generates, at runtime, the settings controls in the device panel. # Solaar now sets up this field in settings_templates.py to eliminate a imports loop. # HID++ 1.0 devices may only have register-based settings; HID++ 2.0 devices may only have # feature-based settings. # Devices are organized by kind # Within kind devices are sorted by wpid, then by usbid, then by btid, with missing values sorted later # Keyboards _D("Wireless Keyboard EX110", codename="EX110", protocol=1.0, wpid="0055", registers=(Reg.BATTERY_STATUS,)) _D("Wireless Keyboard S510", codename="S510", protocol=1.0, wpid="0056", registers=(Reg.BATTERY_STATUS,)) _D("Wireless Wave Keyboard K550", codename="K550", protocol=1.0, wpid="0060", registers=(Reg.BATTERY_STATUS,)) _D("Wireless Keyboard EX100", codename="EX100", protocol=1.0, wpid="0065", registers=(Reg.BATTERY_STATUS,)) _D("Wireless Keyboard MK300", codename="MK300", protocol=1.0, wpid="0068", registers=(Reg.BATTERY_STATUS,)) _D("Number Pad N545", codename="N545", protocol=1.0, wpid="2006", registers=(Reg.BATTERY_STATUS,)) _D("Wireless Compact Keyboard K340", codename="K340", protocol=1.0, wpid="2007", registers=(Reg.BATTERY_STATUS,)) _D("Wireless Keyboard MK700", codename="MK700", protocol=1.0, wpid="2008", registers=(Reg.BATTERY_STATUS,)) _D("Wireless Wave Keyboard K350", codename="K350", protocol=1.0, wpid="200A", registers=(Reg.BATTERY_STATUS,)) _D("Wireless Keyboard MK320", codename="MK320", protocol=1.0, wpid="200F", registers=(Reg.BATTERY_STATUS,)) _D( "Wireless Illuminated Keyboard K800", codename="K800", protocol=1.0, wpid="2010", registers=(Reg.BATTERY_STATUS, Reg.THREE_LEDS), ) _D("Wireless Keyboard K520", codename="K520", protocol=1.0, wpid="2011", registers=(Reg.BATTERY_STATUS,)) _D("Wireless Solar Keyboard K750", codename="K750", protocol=2.0, wpid="4002") _D("Wireless Keyboard K270 (unifying)", codename="K270", protocol=2.0, wpid="4003") _D("Wireless Keyboard K360", codename="K360", protocol=2.0, wpid="4004") _D("Wireless Keyboard K230", codename="K230", protocol=2.0, wpid="400D") _D("Wireless Touch Keyboard K400", codename="K400", protocol=2.0, wpid=("400E", "4024")) _D("Wireless Keyboard MK270", codename="MK270", protocol=2.0, wpid="4023") _D("Illuminated Living-Room Keyboard K830", codename="K830", protocol=2.0, wpid="4032") _D("Wireless Touch Keyboard K400 Plus", codename="K400 Plus", protocol=2.0, wpid="404D") _D("Wireless Multi-Device Keyboard K780", codename="K780", protocol=4.5, wpid="405B") _D("Wireless Keyboard K375s", codename="K375s", protocol=2.0, wpid="4061") _D("Craft Advanced Keyboard", codename="Craft", protocol=4.5, wpid="4066", btid=0xB350) _D("Wireless Illuminated Keyboard K800 new", codename="K800 new", protocol=4.5, wpid="406E") _D("Wireless Keyboard K470", codename="K470", protocol=4.5, wpid="4075") _D("MX Keys Keyboard", codename="MX Keys", protocol=4.5, wpid="408A", btid=0xB35B) _D( "G915 TKL LIGHTSPEED Wireless RGB Mechanical Gaming Keyboard", codename="G915 TKL", protocol=4.2, wpid="408E", usbid=0xC343, ) _D("Illuminated Keyboard", codename="Illuminated", protocol=1.0, usbid=0xC318, interface=1) _D("G213 Prodigy Gaming Keyboard", codename="G213", usbid=0xC336, interface=1) _D("G512 RGB Mechanical Gaming Keyboard", codename="G512", usbid=0xC33C, interface=1) _D("G815 Mechanical Keyboard", codename="G815", usbid=0xC33F, interface=1) _D("diNovo Edge Keyboard", codename="diNovo", protocol=1.0, wpid="C714") _D("K845 Mechanical Keyboard", codename="K845", usbid=0xC341, interface=3) # Mice _D("LX5 Cordless Mouse", codename="LX5", protocol=1.0, wpid="0036", registers=(Reg.BATTERY_STATUS,)) _D("LX7 Cordless Laser Mouse", codename="LX7", protocol=1.0, wpid="0039", registers=(Reg.BATTERY_STATUS,)) _D("Wireless Wave Mouse M550", codename="M550", protocol=1.0, wpid="003C", registers=(Reg.BATTERY_STATUS,)) _D("Wireless Mouse EX100", codename="EX100m", protocol=1.0, wpid="003F", registers=(Reg.BATTERY_STATUS,)) _D("Wireless Mouse M30", codename="M30", protocol=1.0, wpid="0085", registers=(Reg.BATTERY_STATUS,)) _D("MX610 Laser Cordless Mouse", codename="MX610", protocol=1.0, wpid="1001", registers=(Reg.BATTERY_STATUS,)) _D("G7 Cordless Laser Mouse", codename="G7", protocol=1.0, wpid="1002", registers=(Reg.BATTERY_STATUS,)) _D("V400 Laser Cordless Mouse", codename="V400", protocol=1.0, wpid="1003", registers=(Reg.BATTERY_STATUS,)) _D("MX610 Left-Handled Mouse", codename="MX610L", protocol=1.0, wpid="1004", registers=(Reg.BATTERY_STATUS,)) _D("V450 Laser Cordless Mouse", codename="V450", protocol=1.0, wpid="1005", registers=(Reg.BATTERY_STATUS,)) _D( "VX Revolution", codename="VX Revolution", kind=DEVICE_KIND.mouse, protocol=1.0, wpid=("1006", "100D", "0612"), registers=(Reg.BATTERY_CHARGE,), ) _D( "MX Air", codename="MX Air", protocol=1.0, kind=DEVICE_KIND.mouse, wpid=("1007", "100E"), registers=Reg.BATTERY_CHARGE, ) _D( "MX Revolution", codename="MX Revolution", protocol=1.0, kind=DEVICE_KIND.mouse, wpid=("1008", "100C"), registers=(Reg.BATTERY_CHARGE,), ) _D( "MX620 Laser Cordless Mouse", codename="MX620", protocol=1.0, wpid=("100A", "1016"), registers=(Reg.BATTERY_CHARGE,), ) _D( "VX Nano Cordless Laser Mouse", codename="VX Nano", protocol=1.0, wpid=("100B", "100F"), registers=(Reg.BATTERY_CHARGE,), ) _D( "V450 Nano Cordless Laser Mouse", codename="V450 Nano", protocol=1.0, wpid="1011", registers=(Reg.BATTERY_CHARGE,), ) _D( "V550 Nano Cordless Laser Mouse", codename="V550 Nano", protocol=1.0, wpid="1013", registers=(Reg.BATTERY_CHARGE,), ) _D( "MX 1100 Cordless Laser Mouse", codename="MX 1100", protocol=1.0, kind=DEVICE_KIND.mouse, wpid="1014", registers=(Reg.BATTERY_CHARGE,), ) _D("Anywhere Mouse MX", codename="Anywhere MX", protocol=1.0, wpid="1017", registers=(Reg.BATTERY_CHARGE,)) _D( "Performance Mouse MX", codename="Performance MX", protocol=1.0, wpid="101A", registers=(Reg.BATTERY_STATUS, Reg.THREE_LEDS), ) _D( "Marathon Mouse M705 (M-R0009)", codename="M705 (M-R0009)", protocol=1.0, wpid="101B", registers=(Reg.BATTERY_CHARGE,), ) _D( "Wireless Mouse M350", codename="M350", protocol=1.0, wpid="101C", registers=(Reg.BATTERY_CHARGE,), ) _D( "Wireless Mouse M505", codename="M505/B605", protocol=1.0, wpid="101D", registers=(Reg.BATTERY_CHARGE,), ) _D( "Wireless Mouse M305", codename="M305", protocol=1.0, wpid="101F", registers=(Reg.BATTERY_STATUS,), ) _D( "Wireless Mouse M215", codename="M215", protocol=1.0, wpid="1020", ) _D( "G700 Gaming Mouse", codename="G700", protocol=1.0, wpid="1023", usbid=0xC06B, interface=1, registers=( Reg.BATTERY_STATUS, Reg.THREE_LEDS, ), ) _D("Wireless Mouse M310", codename="M310", protocol=1.0, wpid="1024", registers=(Reg.BATTERY_STATUS,)) _D("Wireless Mouse M510", codename="M510", protocol=1.0, wpid="1025", registers=(Reg.BATTERY_STATUS,)) _D("Fujitsu Sonic Mouse", codename="Sonic", protocol=1.0, wpid="1029") _D( "G700s Gaming Mouse", codename="G700s", protocol=1.0, wpid="102A", usbid=0xC07C, interface=1, registers=( Reg.BATTERY_STATUS, Reg.THREE_LEDS, ), ) _D("Couch Mouse M515", codename="M515", protocol=2.0, wpid="4007") _D("Wireless Mouse M175", codename="M175", protocol=2.0, wpid="4008") _D("Wireless Mouse M325", codename="M325", protocol=2.0, wpid="400A") _D("Wireless Mouse M525", codename="M525", protocol=2.0, wpid="4013") _D("Wireless Mouse M345", codename="M345", protocol=2.0, wpid="4017") _D("Wireless Mouse M187", codename="M187", protocol=2.0, wpid="4019") _D("Touch Mouse M600", codename="M600", protocol=2.0, wpid="401A") _D("Wireless Mouse M150", codename="M150", protocol=2.0, wpid="4022") _D("Wireless Mouse M185", codename="M185", protocol=2.0, wpid="4038") _D("Wireless Mouse MX Master", codename="MX Master", protocol=4.5, wpid="4041", btid=0xB012) _D("Anywhere Mouse MX 2", codename="Anywhere MX 2", protocol=4.5, wpid="404A") _D("Wireless Mouse M510", codename="M510v2", protocol=2.0, wpid="4051") _D("Wireless Mouse M185 new", codename="M185n", protocol=4.5, wpid="4054") _D("Wireless Mouse M185/M235/M310", codename="M185/M235/M310", protocol=4.5, wpid="4055") _D("Wireless Mouse MX Master 2S", codename="MX Master 2S", protocol=4.5, wpid="4069", btid=0xB019) _D("Multi Device Silent Mouse M585/M590", codename="M585/M590", protocol=4.5, wpid="406B") _D( "Marathon Mouse M705 (M-R0073)", codename="M705 (M-R0073)", protocol=4.5, wpid="406D", ) _D("MX Vertical Wireless Mouse", codename="MX Vertical", protocol=4.5, wpid="407B", btid=0xB020, usbid=0xC08A) _D("Wireless Mouse Pebble M350", codename="Pebble", protocol=2.0, wpid="4080") _D("MX Master 3 Wireless Mouse", codename="MX Master 3", protocol=4.5, wpid="4082", btid=0xB023) _D("PRO X Wireless", kind="mouse", codename="PRO X", wpid="4093", usbid=0xC094) _D("G9 Laser Mouse", codename="G9", usbid=0xC048, interface=1, protocol=1.0) _D("G9x Laser Mouse", codename="G9x", usbid=0xC066, interface=1, protocol=1.0) _D("G502 Gaming Mouse", codename="G502", usbid=0xC07D, interface=1) _D("G402 Gaming Mouse", codename="G402", usbid=0xC07E, interface=1) _D("G900 Chaos Spectrum Gaming Mouse", codename="G900", usbid=0xC081) _D("G403 Gaming Mouse", codename="G403", usbid=0xC082) _D("G903 Lightspeed Gaming Mouse", codename="G903", usbid=0xC086) _D("G703 Lightspeed Gaming Mouse", codename="G703", usbid=0xC087) _D("GPro Gaming Mouse", codename="GPro", usbid=0xC088) _D("G502 SE Hero Gaming Mouse", codename="G502 Hero", usbid=0xC08B, interface=1) _D("G502 Lightspeed Gaming Mouse", codename="G502 Lightspeed", usbid=0xC08D) _D("MX518 Gaming Mouse", codename="MX518", usbid=0xC08E, interface=1) _D("G703 Hero Gaming Mouse", codename="G703 Hero", usbid=0xC090) _D("G903 Hero Gaming Mouse", codename="G903 Hero", usbid=0xC091) _D(None, kind=DEVICE_KIND.mouse, usbid=0xC092, interface=1) # two mice share this ID _D("M500S Mouse", codename="M500S", usbid=0xC093, interface=1) # _D('G600 Gaming Mouse', codename='G600 Gaming', usbid=0xc24a, interface=1) # not an HID++ device _D("G500s Gaming Mouse", codename="G500s Gaming", usbid=0xC24E, interface=1, protocol=1.0) _D("G502 Proteus Spectrum Optical Mouse", codename="G502 Proteus Spectrum", usbid=0xC332, interface=1) _D("Logitech PRO Gaming Keyboard", codename="PRO Gaming Keyboard", usbid=0xC339, interface=1) _D("Logitech MX Revolution Mouse M-RCL 124", codename="M-RCL 124", btid=0xB007, interface=1) # Trackballs _D("Wireless Trackball M570", codename="M570") # Touchpads _D("Wireless Touchpad", codename="Wireless Touch", protocol=2.0, wpid="4011") _D("Wireless Rechargeable Touchpad T650", codename="T650", protocol=2.0, wpid="4101") _D( "G Powerplay", codename="Powerplay", protocol=2.0, kind=DEVICE_KIND.touchpad, wpid="405F" ) # To override self-identification # Headset _D("G533 Gaming Headset", codename="G533 Headset", protocol=2.0, interface=3, kind=DEVICE_KIND.headset, usbid=0x0A66) _D("G535 Gaming Headset", codename="G535 Headset", protocol=2.0, interface=3, kind=DEVICE_KIND.headset, usbid=0x0AC4) _D("G935 Gaming Headset", codename="G935 Headset", protocol=2.0, interface=3, kind=DEVICE_KIND.headset, usbid=0x0A87) _D("G733 Gaming Headset", codename="G733 Headset", protocol=2.0, interface=3, kind=DEVICE_KIND.headset, usbid=0x0AB5) _D( "G733 Gaming Headset", codename="G733 Headset New", protocol=2.0, interface=3, kind=DEVICE_KIND.headset, usbid=0x0AFE, ) _D( "PRO X Wireless Gaming Headset", codename="PRO Headset", protocol=2.0, interface=3, kind=DEVICE_KIND.headset, usbid=0x0ABA, ) Solaar-1.1.14/lib/logitech_receiver/desktop_notifications.py000066400000000000000000000110471473525213700242370ustar00rootroot00000000000000## Copyright (C) 2024 Solaar contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. """Implements the desktop notification service.""" import importlib import logging logger = logging.getLogger(__name__) def notifications_available(): """Checks if notification service is available.""" notifications_supported = False try: import gi gi.require_version("Notify", "0.7") gi.require_version("Gtk", "3.0") importlib.util.find_spec("gi.repository.GLib") importlib.util.find_spec("gi.repository.Gtk") importlib.util.find_spec("gi.repository.Notify") notifications_supported = True except ValueError as e: logger.warning(f"Notification service is not available: {e}") return notifications_supported available = notifications_available() if available: from gi.repository import GLib from gi.repository import Gtk from gi.repository import Notify # cache references to shown notifications here to allow reuse _notifications = {} _ICON_LISTS = {} def init(): """Initialize desktop notifications.""" global available if available: if not Notify.is_initted(): if logger.isEnabledFor(logging.INFO): logger.info("starting desktop notifications") try: return Notify.init("solaar") # replace with better name later except Exception: logger.exception("initializing desktop notifications") available = False return available and Notify.is_initted() def uninit(): """Stop desktop notifications.""" if available and Notify.is_initted(): if logger.isEnabledFor(logging.INFO): logger.info("stopping desktop notifications") _notifications.clear() Notify.uninit() def show(dev, message: str, icon=None): """Show a notification with title and text.""" if available and (Notify.is_initted() or init()): summary = dev.name n = _notifications.get(summary) # reuse notification of same name if n is None: n = _notifications[summary] = Notify.Notification() icon_name = device_icon_name(dev.name, dev.kind) if icon is None else icon n.update(summary, message, icon_name) n.set_urgency(Notify.Urgency.NORMAL) n.set_hint("desktop-entry", GLib.Variant("s", "solaar")) # replace with better name late try: n.show() except Exception: logger.exception(f"showing {n}") def device_icon_list(name="_", kind=None): icon_list = _ICON_LISTS.get(name) if icon_list is None: # names of possible icons, in reverse order of likelihood # the theme will hopefully pick up the most appropriate icon_list = ["preferences-desktop-peripherals"] kind = str(kind) if kind: if kind == "numpad": icon_list += ("input-keyboard", "input-dialpad") elif kind == "touchpad": icon_list += ("input-mouse", "input-tablet") elif kind == "trackball": icon_list += ("input-mouse",) elif kind == "headset": icon_list += ("audio-headphones", "audio-headset") icon_list += (f"input-{kind}",) _ICON_LISTS[name] = icon_list return icon_list def device_icon_name(name, kind=None): _default_theme = Gtk.IconTheme.get_default() icon_list = device_icon_list(name, kind) for n in reversed(icon_list): if _default_theme.has_icon(n): return n else: def init(): return False def uninit(): return None def show(dev, reason=None): return None Solaar-1.1.14/lib/logitech_receiver/device.py000066400000000000000000000576551473525213700211130ustar00rootroot00000000000000## Copyright (C) 2012-2013 Daniel Pavel ## Copyright (C) 2014-2024 Solaar Contributors https://pwr-solaar.github.io/Solaar/ ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. from __future__ import annotations import errno import logging import threading import time import typing from typing import Any from typing import Callable from typing import Optional from typing import Protocol from solaar import configuration from . import descriptors from . import exceptions from . import hidpp10 from . import hidpp10_constants from . import hidpp20 from . import settings from . import settings_templates from .common import Alert from .common import Battery from .hidpp20_constants import SupportedFeature if typing.TYPE_CHECKING: from logitech_receiver import common logger = logging.getLogger(__name__) _hidpp10 = hidpp10.Hidpp10() _hidpp20 = hidpp20.Hidpp20() class LowLevelInterface(Protocol): def open_path(self, path) -> Any: ... def find_paired_node(self, receiver_path: str, index: int, timeout: int): ... def ping(self, handle, number, long_message: bool): ... def request(self, handle, devnumber, request_id, *params, **kwargs): ... def close(self, handle, *args, **kwargs) -> bool: ... def create_device(low_level: LowLevelInterface, device_info, setting_callback=None): """Opens a Logitech Device found attached to the machine, by Linux device path. :returns: An open file handle for the found receiver, or None. """ try: handle = low_level.open_path(device_info.path) if handle: # a direct connected device might not be online (as reported by user) return Device( low_level, None, None, None, handle=handle, device_info=device_info, setting_callback=setting_callback, ) except OSError as e: logger.exception("open %s", device_info) if e.errno == errno.EACCES: raise except Exception: logger.exception("open %s", device_info) raise class Device: instances = [] read_register: Callable = hidpp10.read_register write_register: Callable = hidpp10.write_register def __init__( self, low_level: LowLevelInterface, receiver, number, online, pairing_info=None, handle=None, device_info=None, setting_callback=None, ): assert receiver or device_info if receiver: assert 0 < number <= 15 # some receivers have devices past their max # of devices self.low_level = low_level self.number = number # will be None at this point for directly connected devices self.online = online # is the device online? - gates many atempts to contact the device self.descriptor = None self.isDevice = True # some devices act as receiver so we need a property to distinguish them self.may_unpair = False self.receiver = receiver self.handle = handle self.path = device_info.path if device_info else None self.product_id = device_info.product_id if device_info else None self.hidpp_short = device_info.hidpp_short if device_info else None self.hidpp_long = device_info.hidpp_long if device_info else None self.bluetooth = device_info.bus_id == 0x0005 if device_info else False # Bluetooth needs long messages self.hid_serial = device_info.serial if device_info else None self.setting_callback = setting_callback # for changes to settings self.status_callback = None # for changes to other potentially visible aspects self.wpid = pairing_info["wpid"] if pairing_info else None # the Wireless PID is unique per device model self._kind = pairing_info["kind"] if pairing_info else None # mouse, keyboard, etc (see hidpp10.DEVICE_KIND) self._serial = pairing_info["serial"] if pairing_info else None # serial number (an 8-char hex string) self._polling_rate = pairing_info["polling"] if pairing_info else None self._power_switch = pairing_info["power_switch"] if pairing_info else None self._name = None # the full name of the model self._codename = None # Unifying peripherals report a codename. self._protocol = None # HID++ protocol version, 1.0 or 2.0 self._unitId = None # unit id (distinguishes within a model - generally the same as serial) self._modelId = None # model id (contains identifiers for the transports of the device) self._tid_map = None # map from transports to product identifiers self._persister = None # persister holds settings self._led_effects = self._firmware = self._keys = self._remap_keys = self._gestures = None self._profiles = self._backlight = self._settings = None self.registers = [] self.notification_flags = None self.battery_info = None self.link_encrypted = None self._active = None # lags self.online - is used to help determine when to setup devices self._feature_settings_checked = False self._gestures_lock = threading.Lock() self._settings_lock = threading.Lock() self._persister_lock = threading.Lock() self._notification_handlers = {} # See `add_notification_handler` self.cleanups = [] # functions to run on the device when it is closed if not self.path: self.path = self.low_level.find_paired_node(receiver.path, number, 1) if receiver else None if not self.handle: try: self.handle = self.low_level.open_path(self.path) if self.path else None except Exception: # maybe the device wasn't set up try: time.sleep(1) self.handle = self.low_level.open_path(self.path) if self.path else None except Exception: # give up self.handle = None # should this give up completely? if receiver: if not self.wpid: raise exceptions.NoSuchDevice( number=number, receiver=receiver, error="no wpid for device connected to receiver" ) self.descriptor = descriptors.get_wpid(self.wpid) if self.descriptor is None: codename = self.receiver.device_codename(self.number) # Last chance to get a descriptor, may fail if codename: self._codename = codename self.descriptor = descriptors.get_codename(self._codename) else: self.descriptor = ( descriptors.get_btid(self.product_id) if self.bluetooth else descriptors.get_usbid(self.product_id) ) if self.number is None: # for direct-connected devices get 'number' from descriptor protocol else use 0xFF if self.descriptor and self.descriptor.protocol and self.descriptor.protocol < 2.0: number = 0x00 else: number = 0xFF self.number = number self.ping() # determine whether a direct-connected device is online if self.descriptor: self._name = self.descriptor.name if self._codename is None: self._codename = self.descriptor.codename if self._kind is None: self._kind = self.descriptor.kind self._protocol = self.descriptor.protocol if self.descriptor.protocol else None self.registers = self.descriptor.registers if self.descriptor.registers else [] if self._protocol is not None: self.features = None if self._protocol < 2.0 else hidpp20.FeaturesArray(self) else: self.features = hidpp20.FeaturesArray(self) # may be a 2.0 device; if not, it will fix itself later Device.instances.append(self) def find(self, id): # find a device by serial number or unit ID assert id, "need serial number or unit ID to find a device" for device in Device.instances: if device.online and (device.unitId == id or device.serial == id): return device @property def protocol(self): if not self._protocol: self.ping() return self._protocol or 0 @property def codename(self): if not self._codename: if self.online and self.protocol >= 2.0: self._codename = _hidpp20.get_friendly_name(self) if not self._codename: self._codename = self.name.split(" ", 1)[0] if self.name else None if not self._codename and self.receiver: codename = self.receiver.device_codename(self.number) if codename: self._codename = codename elif self.protocol < 2.0: self._codename = "? (%s)" % (self.wpid or self.product_id) return self._codename or "?? (%s)" % (self.wpid or self.product_id) @property def name(self): if not self._name: if self.online and self.protocol >= 2.0: self._name = _hidpp20.get_name(self) return self._name or self._codename or "Unknown device %s" % (self.wpid or self.product_id) def get_ids(self): ids = _hidpp20.get_ids(self) if ids: self._unitId, self._modelId, self._tid_map = ids if logger.isEnabledFor(logging.INFO) and self._serial and self._serial != self._unitId: logger.info("%s: unitId %s does not match serial %s", self, self._unitId, self._serial) @property def unitId(self): if not self._unitId and self.online and self.protocol >= 2.0: self.get_ids() return self._unitId @property def modelId(self): if not self._modelId and self.online and self.protocol >= 2.0: self.get_ids() return self._modelId @property def tid_map(self): if not self._tid_map and self.online and self.protocol >= 2.0: self.get_ids() return self._tid_map @property def kind(self): if not self._kind and self.online and self.protocol >= 2.0: self._kind = _hidpp20.get_kind(self) return self._kind or "?" @property def firmware(self) -> tuple[common.FirmwareInfo]: if self._firmware is None and self.online: if self.protocol >= 2.0: self._firmware = _hidpp20.get_firmware(self) else: self._firmware = _hidpp10.get_firmware(self) return self._firmware or () @property def serial(self): return self._serial or "" @property def id(self): return self.unitId or self.serial @property def power_switch_location(self): return self._power_switch @property def polling_rate(self): if self.online and self.protocol >= 2.0: rate = _hidpp20.get_polling_rate(self) self._polling_rate = rate if rate else self._polling_rate return self._polling_rate @property def led_effects(self): if not self._led_effects and self.online and self.protocol >= 2.0: if SupportedFeature.COLOR_LED_EFFECTS in self.features: self._led_effects = hidpp20.LEDEffectsInfo(self) elif SupportedFeature.RGB_EFFECTS in self.features: self._led_effects = hidpp20.RGBEffectsInfo(self) return self._led_effects @property def keys(self): if not self._keys: if self.online and self.protocol >= 2.0: self._keys = _hidpp20.get_keys(self) or () return self._keys @property def remap_keys(self): if self._remap_keys is None: if self.online and self.protocol >= 2.0: self._remap_keys = _hidpp20.get_remap_keys(self) or () return self._remap_keys @property def gestures(self): if self._gestures is None: with self._gestures_lock: if self._gestures is None: if self.online and self.protocol >= 2.0: self._gestures = _hidpp20.get_gestures(self) or () return self._gestures @property def backlight(self): if self._backlight is None: if self.online and self.protocol >= 2.0: self._backlight = _hidpp20.get_backlight(self) return self._backlight @property def profiles(self): if self._profiles is None: if self.online and self.protocol >= 2.0: self._profiles = _hidpp20.get_profiles(self) return self._profiles def set_configuration(self, configuration_, no_reply=False): if self.online and self.protocol >= 2.0: _hidpp20.config_change(self, configuration_, no_reply=no_reply) def reset(self, no_reply=False): self.set_configuration(0, no_reply) @property def persister(self): if not self._persister: with self._persister_lock: if not self._persister: self._persister = configuration.persister(self) return self._persister @property def settings(self): if not self._settings: with self._settings_lock: if not self._settings: settings = [] if self.persister and self.descriptor and self.descriptor.settings: for sclass in self.descriptor.settings: try: setting = sclass.build(self) except Exception as e: # Do nothing if the device is offline setting = None if self.online: raise e if setting is not None: settings.append(setting) self._settings = settings if not self._feature_settings_checked: with self._settings_lock: if not self._feature_settings_checked: self._feature_settings_checked = settings_templates.check_feature_settings(self, self._settings) return self._settings def battery(self): # None or level, next, status, voltage if self.protocol < 2.0: return _hidpp10.get_battery(self) else: battery_feature = self.persister.get("_battery", None) if self.persister else None if battery_feature != 0: result = _hidpp20.get_battery(self, battery_feature) try: feature, battery = result if self.persister and battery_feature is None: self.persister["_battery"] = feature.value return battery except Exception: if self.persister and battery_feature is None: self.persister["_battery"] = result def set_battery_info(self, info): """Update battery information for device, calling changed callback if necessary""" if logger.isEnabledFor(logging.DEBUG): logger.debug("%s: battery %s, %s", self, info.level, info.status) if info.level is None and self.battery_info: # use previous level if missing from new information info.level = self.battery_info.level changed = self.battery_info != info self.battery_info, old_info = info, self.battery_info if old_info is None: old_info = Battery(None, None, None, None) alert, reason = Alert.NONE, None if not info.ok(): logger.warning("%s: battery %d%%, ALERT %s", self, info.level, info.status) if old_info.status != info.status: alert = Alert.NOTIFICATION | Alert.ATTENTION reason = info.to_str() if changed or reason: # update the leds on the device, if any _hidpp10.set_3leds(self, info.level, charging=info.charging(), warning=bool(alert)) self.changed(active=True, alert=alert, reason=reason) # Retrieve and regularize battery status def read_battery(self): if self.online: battery = self.battery() self.set_battery_info(battery if battery is not None else Battery(None, None, None, None)) def changed(self, active=None, alert=Alert.NONE, reason=None, push=False): """The status of the device had changed, so invoke the status callback. Also push notifications and settings to the device when necessary.""" if active is not None: self.online = active was_active, self._active = self._active, active if active: # Push settings for new devices when devices request software reconfiguration # and when devices become active if they don't have wireless device status feature, if ( was_active is None or not was_active or push and (not self.features or SupportedFeature.WIRELESS_DEVICE_STATUS not in self.features) ): if logger.isEnabledFor(logging.INFO): logger.info("%s pushing device settings %s", self, self.settings) settings.apply_all_settings(self) if not was_active: if self.protocol < 2.0: # Make sure to set notification flags on the device self.notification_flags = self.enable_connection_notifications() else: self.set_configuration(0x11) # signal end of configuration self.read_battery() # battery information may have changed so try to read it now elif was_active and self.receiver: # need to set configuration pending flag in receiver hidpp10.set_configuration_pending_flags(self.receiver, 0xFF) if logger.isEnabledFor(logging.DEBUG): logger.debug("device %d changed: active=%s %s", self.number, self._active, self.battery_info) if self.status_callback is not None: self.status_callback(self, alert, reason) def enable_connection_notifications(self, enable=True): """Enable or disable device (dis)connection notifications on this receiver.""" if not bool(self.receiver) or self.protocol >= 2.0: return False if enable: set_flag_bits = ( hidpp10_constants.NOTIFICATION_FLAG.battery_status | hidpp10_constants.NOTIFICATION_FLAG.ui | hidpp10_constants.NOTIFICATION_FLAG.configuration_complete ) else: set_flag_bits = 0 ok = _hidpp10.set_notification_flags(self, set_flag_bits) if not ok: logger.warning("%s: failed to %s device notifications", self, "enable" if enable else "disable") flag_bits = _hidpp10.get_notification_flags(self) if logger.isEnabledFor(logging.INFO): flag_names = None if flag_bits is None else tuple(hidpp10_constants.NOTIFICATION_FLAG.flag_names(flag_bits)) logger.info("%s: device notifications %s %s", self, "enabled" if enable else "disabled", flag_names) return flag_bits if ok else None def add_notification_handler(self, id: str, fn): """Adds the notification handling callback `fn` to this device under name `id`. If a callback has already been registered under this name, it's replaced with the argument. The callback will be invoked whenever the device emits an event message, and the resulting notification hasn't been handled by another handler on this device (order is not guaranteed, so handlers should not overlap in functionality). The callback should have type `(PairedDevice, Notification) -> Optional[bool]`. It should return `None` if it hasn't handled the notification, return `True` if it did so successfully and return `False` if an error should be reported (malformed notification, etc). """ self._notification_handlers[id] = fn def remove_notification_handler(self, id: str): """Unregisters the notification handler under name `id`.""" if id not in self._notification_handlers and logger.isEnabledFor(logging.INFO): logger.info(f"Tried to remove nonexistent notification handler {id} from device {self}.") else: del self._notification_handlers[id] def handle_notification(self, n) -> Optional[bool]: for h in self._notification_handlers.values(): ret = h(self, n) if ret is not None: return ret return None def request(self, request_id, *params, no_reply=False): if self: long = self.hidpp_long is True or ( self.hidpp_long is None and (self.bluetooth or self._protocol is not None and self._protocol >= 2.0) ) return self.low_level.request( self.handle or self.receiver.handle, self.number, request_id, *params, no_reply=no_reply, long_message=long, protocol=self.protocol, ) def feature_request(self, feature, function=0x00, *params, no_reply=False): if self.protocol >= 2.0: return hidpp20.feature_request(self, feature, function, *params, no_reply=no_reply) def ping(self): """Checks if the device is online, returns True of False""" long = self.hidpp_long is True or ( self.hidpp_long is None and (self.bluetooth or self._protocol is not None and self._protocol >= 2.0) ) handle = self.handle or self.receiver.handle protocol = self.low_level.ping(handle, self.number, long_message=long) self.online = protocol is not None if protocol: self._protocol = protocol return self.online def notify_devices(self): # no need to notify, as there are none pass def close(self): handle, self.handle = self.handle, None if self in Device.instances: Device.instances.remove(self) if hasattr(self, "cleanups"): for cleanup in self.cleanups: cleanup(self) return handle and self.low_level.close(handle) def __index__(self): return self.number __int__ = __index__ def __eq__(self, other): return other is not None and self._kind == other._kind and self.wpid == other.wpid def __ne__(self, other): return other is None or self.kind != other.kind or self.wpid != other.wpid def __hash__(self): return self.wpid.__hash__() def __bool__(self): return self.wpid is not None and self.number in self.receiver if self.receiver else self.handle is not None __nonzero__ = __bool__ def status_string(self): return self.battery_info.to_str() if self.battery_info is not None else "" def __str__(self): try: name = self._name or self._codename or "?" except exceptions.NoSuchDevice: name = "name not available" return f"" __repr__ = __str__ def __del__(self): self.close() Solaar-1.1.14/lib/logitech_receiver/diversion.py000066400000000000000000001624311473525213700216430ustar00rootroot00000000000000## Copyright (C) 2020 Peter Patel-Schneider ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. from __future__ import annotations import ctypes import logging import math import numbers import os import platform import socket import struct import subprocess import sys import time import typing from typing import Any from typing import Dict from typing import Tuple import gi import psutil import yaml from keysyms import keysymdef # There is no evdev on macOS or Windows. Diversion will not work without # it but other Solaar functionality is available. if platform.system() in ("Darwin", "Windows"): evdev = None else: import evdev from .common import NamedInt from .hidpp20 import SupportedFeature from .special_keys import CONTROL gi.require_version("Gdk", "3.0") # isort:skip from gi.repository import Gdk, GLib # NOQA: E402 # isort:skip if typing.TYPE_CHECKING: from .base import HIDPPNotification logger = logging.getLogger(__name__) # # See docs/rules.md for documentation # # Several capabilities of rules depend on aspects of GDK, X11, or XKB # As the Solaar GUI uses GTK, Glib and GDK are always available and are obtained from gi.repository # # Process condition depends on X11 from python-xlib, and is probably not possible at all in Wayland # MouseProcess condition depends on X11 from python-xlib, and is probably not possible at all in Wayland # Modifiers condition depends only on GDK # KeyPress action determines whether a keysym is a currently-down modifier using get_modifier_mapping from python-xlib; # under Wayland no modifier keys are considered down so all modifier keys are pressed, potentially leading to problems # KeyPress action translates key names to keysysms using the local file described for GUI keyname determination # KeyPress action gets the current keyboard group using XkbGetState from libX11.so using ctypes definitions # under Wayland the keyboard group is None resulting in using the first keyboard group # KeyPress action translates keysyms to keycodes using the GDK keymap # KeyPress, MouseScroll, and MouseClick actions use XTest (under X11) or uinput. # For uinput to work the user must have write access for /dev/uinput. # To get this access run sudo setfacl -m u:${user}:rw /dev/uinput # # Rule GUI keyname determination uses a local file generated # from http://cgit.freedesktop.org/xorg/proto/x11proto/plain/keysymdef.h # and http://cgit.freedesktop.org/xorg/proto/x11proto/plain/XF86keysym.h # because there does not seem to be a non-X11 file for this set of key names # Setting up is complex because there are several systems that each provide partial facilities: # GDK - always available (when running with a window system) but only provides access to keymap # X11 - provides access to active process and process with window under mouse and current modifier keys # Xtest extension to X11 - provides input simulation, partly works under Wayland # Wayland - provides input simulation XK_KEYS: Dict[str, int] = keysymdef.key_symbols # Event codes - can't use Xlib.X codes because Xlib might not be available _KEY_RELEASE = 0 _KEY_PRESS = 1 _BUTTON_RELEASE = 2 _BUTTON_PRESS = 3 CLICK, DEPRESS, RELEASE = "click", "depress", "release" gdisplay = Gdk.Display.get_default() # can be None if Solaar is run without a full window system gkeymap = Gdk.Keymap.get_for_display(gdisplay) if gdisplay else None if logger.isEnabledFor(logging.INFO): logger.info("GDK Keymap %sset up", "" if gkeymap else "not ") wayland = os.getenv("WAYLAND_DISPLAY") # is this Wayland? if wayland: logger.warning( "rules cannot access modifier keys in Wayland, " "accessing process only works on GNOME with Solaar Gnome extension installed" ) try: import Xlib _x11 = None # X11 might be available except Exception: _x11 = False # X11 is not available # Globals xtest_available = True # Xtest might be available xdisplay = None Xkbdisplay = None # xkb might be available X11Lib = None modifier_keycodes = [] XkbUseCoreKbd = 0x100 NET_ACTIVE_WINDOW = None NET_WM_PID = None WM_CLASS = None udevice = None key_down = None key_up = None keys_down = [] g_keys_down = 0 m_keys_down = 0 mr_key_down = False thumb_wheel_displacement = 0 _dbus_interface = None class XkbDisplay(ctypes.Structure): """opaque struct""" class XkbStateRec(ctypes.Structure): _fields_ = [ ("group", ctypes.c_ubyte), ("locked_group", ctypes.c_ubyte), ("base_group", ctypes.c_ushort), ("latched_group", ctypes.c_ushort), ("mods", ctypes.c_ubyte), ("base_mods", ctypes.c_ubyte), ("latched_mods", ctypes.c_ubyte), ("locked_mods", ctypes.c_ubyte), ("compat_state", ctypes.c_ubyte), ("grab_mods", ctypes.c_ubyte), ("compat_grab_mods", ctypes.c_ubyte), ("lookup_mods", ctypes.c_ubyte), ("compat_lookup_mods", ctypes.c_ubyte), ("ptr_buttons", ctypes.c_ushort), ] # something strange is happening here but it is not being used def x11_setup(): global _x11, xdisplay, modifier_keycodes, NET_ACTIVE_WINDOW, NET_WM_PID, WM_CLASS, xtest_available if _x11 is not None: return _x11 try: from Xlib.display import Display xdisplay = Display() modifier_keycodes = xdisplay.get_modifier_mapping() # there should be a way to do this in Gdk NET_ACTIVE_WINDOW = xdisplay.intern_atom("_NET_ACTIVE_WINDOW") NET_WM_PID = xdisplay.intern_atom("_NET_WM_PID") WM_CLASS = xdisplay.intern_atom("WM_CLASS") _x11 = True # X11 available if logger.isEnabledFor(logging.INFO): logger.info("X11 library loaded and display set up") except Exception: logger.warning("X11 not available - some rule capabilities inoperable", exc_info=sys.exc_info()) _x11 = False xtest_available = False return _x11 def gnome_dbus_interface_setup(): global _dbus_interface if _dbus_interface is not None: return _dbus_interface try: import dbus bus = dbus.SessionBus() remote_object = bus.get_object("org.gnome.Shell", "/io/github/pwr_solaar/solaar") _dbus_interface = dbus.Interface(remote_object, "io.github.pwr_solaar.solaar") except dbus.exceptions.DBusException: logger.warning( "Solaar Gnome extension not installed - some rule capabilities inoperable", exc_info=sys.exc_info(), ) _dbus_interface = False return _dbus_interface def xkb_setup(): global X11Lib, Xkbdisplay if Xkbdisplay is not None: return Xkbdisplay try: # set up to get keyboard state using ctypes interface to libx11 X11Lib = ctypes.cdll.LoadLibrary("libX11.so") X11Lib.XOpenDisplay.restype = ctypes.POINTER(XkbDisplay) X11Lib.XkbGetState.argtypes = [ctypes.POINTER(XkbDisplay), ctypes.c_uint, ctypes.POINTER(XkbStateRec)] Xkbdisplay = X11Lib.XOpenDisplay(None) if logger.isEnabledFor(logging.INFO): logger.info("XKB display set up") except Exception: logger.warning("XKB display not available - rules cannot access keyboard group", exc_info=sys.exc_info()) Xkbdisplay = False return Xkbdisplay if evdev: buttons = { "unknown": (None, None), "left": (1, evdev.ecodes.ecodes["BTN_LEFT"]), "middle": (2, evdev.ecodes.ecodes["BTN_MIDDLE"]), "right": (3, evdev.ecodes.ecodes["BTN_RIGHT"]), "scroll_up": (4, evdev.ecodes.ecodes["BTN_4"]), "scroll_down": (5, evdev.ecodes.ecodes["BTN_5"]), "scroll_left": (6, evdev.ecodes.ecodes["BTN_6"]), "scroll_right": (7, evdev.ecodes.ecodes["BTN_7"]), "button8": (8, evdev.ecodes.ecodes["BTN_8"]), "button9": (9, evdev.ecodes.ecodes["BTN_9"]), } # uinput capability for keyboard keys, mouse buttons, and scrolling key_events = [c for n, c in evdev.ecodes.ecodes.items() if n.startswith("KEY") and n != "KEY_CNT"] for _, evcode in buttons.values(): if evcode: key_events.append(evcode) devicecap = { evdev.ecodes.EV_KEY: key_events, evdev.ecodes.EV_REL: [evdev.ecodes.REL_WHEEL, evdev.ecodes.REL_HWHEEL], } else: # Just mock these since they won't be useful without evdev anyway buttons = {} key_events = [] devicecap = {} def setup_uinput(): global udevice if udevice is not None: return udevice try: udevice = evdev.uinput.UInput(events=devicecap, name="solaar-keyboard") if logger.isEnabledFor(logging.INFO): logger.info("uinput device set up") return True except Exception as e: logger.warning("cannot create uinput device: %s", e) if wayland: # Wayland can't use xtest so may as well set up uinput now setup_uinput() def kbdgroup(): if xkb_setup(): state = XkbStateRec() X11Lib.XkbGetState(Xkbdisplay, XkbUseCoreKbd, ctypes.pointer(state)) return state.group else: return None def modifier_code(keycode): if wayland or not x11_setup() or keycode == 0: return None for m in range(0, len(modifier_keycodes)): if keycode in modifier_keycodes[m]: return m def signed(bytes_: bytes) -> int: return int.from_bytes(bytes_, "big", signed=True) def xy_direction(_x, _y): # normalize x and y m = math.sqrt((_x * _x) + (_y * _y)) if m == 0: return "noop" x = round(_x / m) y = round(_y / m) if x < 0 and y < 0: return "Mouse Up-left" elif x > 0 > y: return "Mouse Up-right" elif x < 0 < y: return "Mouse Down-left" elif x > 0 and y > 0: return "Mouse Down-right" elif x > 0: return "Mouse Right" elif x < 0: return "Mouse Left" elif y > 0: return "Mouse Down" elif y < 0: return "Mouse Up" else: return "noop" def simulate_xtest(code, event): global xtest_available if x11_setup() and xtest_available: try: event = ( Xlib.X.KeyPress if event == _KEY_PRESS else Xlib.X.KeyRelease if event == _KEY_RELEASE else Xlib.X.ButtonPress if event == _BUTTON_PRESS else Xlib.X.ButtonRelease if event == _BUTTON_RELEASE else None ) Xlib.ext.xtest.fake_input(xdisplay, event, code) xdisplay.sync() if logger.isEnabledFor(logging.DEBUG): logger.debug("xtest simulated input %s %s %s", xdisplay, event, code) return True except Exception as e: xtest_available = False logger.warning("xtest fake input failed: %s", e) def simulate_uinput(what, code, arg): global udevice if setup_uinput(): try: udevice.write(what, code, arg) udevice.syn() if logger.isEnabledFor(logging.DEBUG): logger.debug("uinput simulated input %s %s %s", what, code, arg) return True except Exception as e: udevice = None logger.warning("uinput write failed: %s", e) def simulate_key(code, event): # X11 keycode but Solaar event code if not wayland and simulate_xtest(code, event): return True if simulate_uinput(evdev.ecodes.EV_KEY, code - 8, event): return True logger.warning("no way to simulate key input") def click_xtest(button, count): if isinstance(count, int): for _ in range(count): if not simulate_xtest(button[0], _BUTTON_PRESS): return False if not simulate_xtest(button[0], _BUTTON_RELEASE): return False else: if count != RELEASE: if not simulate_xtest(button[0], _BUTTON_PRESS): return False if count != DEPRESS: if not simulate_xtest(button[0], _BUTTON_RELEASE): return False return True def click_uinput(button, count): if isinstance(count, int): for _ in range(count): if not simulate_uinput(evdev.ecodes.EV_KEY, button[1], 1): return False if not simulate_uinput(evdev.ecodes.EV_KEY, button[1], 0): return False else: if count != RELEASE: if not simulate_uinput(evdev.ecodes.EV_KEY, button[1], 1): return False if count != DEPRESS: if not simulate_uinput(evdev.ecodes.EV_KEY, button[1], 0): return False return True def click(button, count): if not wayland and click_xtest(button, count): return True if click_uinput(button, count): return True logger.warning("no way to simulate mouse click") return False def simulate_scroll(dx, dy): if not wayland and xtest_available: success = True if dx: success = click_xtest(buttons["scroll_right" if dx > 0 else "scroll_left"], count=abs(dx)) if dy and success: success = click_xtest(buttons["scroll_up" if dy > 0 else "scroll_down"], count=abs(dy)) if success: return True if setup_uinput(): success = True if dx: success = simulate_uinput(evdev.ecodes.EV_REL, evdev.ecodes.REL_HWHEEL, dx) if dy and success: success = simulate_uinput(evdev.ecodes.EV_REL, evdev.ecodes.REL_WHEEL, dy) if success: return True logger.warning("no way to simulate scrolling") def thumb_wheel_up(f, r, d, a): global thumb_wheel_displacement if f != SupportedFeature.THUMB_WHEEL or r != 0: return False if a is None: return signed(d[0:2]) < 0 and signed(d[0:2]) elif thumb_wheel_displacement <= -a: thumb_wheel_displacement += a return 1 else: return False def thumb_wheel_down(f, r, d, a): global thumb_wheel_displacement if f != SupportedFeature.THUMB_WHEEL or r != 0: return False if a is None: return signed(d[0:2]) > 0 and signed(d[0:2]) elif thumb_wheel_displacement >= a: thumb_wheel_displacement -= a return 1 else: return False def charging(f, r, d, _a): if ( (f == SupportedFeature.BATTERY_STATUS and r == 0 and 1 <= d[2] <= 4) or (f == SupportedFeature.BATTERY_VOLTAGE and r == 0 and d[2] & (1 << 7)) or (f == SupportedFeature.UNIFIED_BATTERY and r == 0 and 1 <= d[2] <= 3) ): return 1 else: return False TESTS = { "crown_right": [lambda f, r, d, a: f == SupportedFeature.CROWN and r == 0 and d[1] < 128 and d[1], False], "crown_left": [lambda f, r, d, a: f == SupportedFeature.CROWN and r == 0 and d[1] >= 128 and 256 - d[1], False], "crown_right_ratchet": [lambda f, r, d, a: f == SupportedFeature.CROWN and r == 0 and d[2] < 128 and d[2], False], "crown_left_ratchet": [lambda f, r, d, a: f == SupportedFeature.CROWN and r == 0 and d[2] >= 128 and 256 - d[2], False], "crown_tap": [lambda f, r, d, a: f == SupportedFeature.CROWN and r == 0 and d[5] == 0x01 and d[5], False], "crown_start_press": [lambda f, r, d, a: f == SupportedFeature.CROWN and r == 0 and d[6] == 0x01 and d[6], False], "crown_end_press": [lambda f, r, d, a: f == SupportedFeature.CROWN and r == 0 and d[6] == 0x05 and d[6], False], "crown_pressed": [lambda f, r, d, a: f == SupportedFeature.CROWN and r == 0 and 0x01 <= d[6] <= 0x04 and d[6], False], "thumb_wheel_up": [thumb_wheel_up, True], "thumb_wheel_down": [thumb_wheel_down, True], "lowres_wheel_up": [ lambda f, r, d, a: f == SupportedFeature.LOWRES_WHEEL and r == 0 and signed(d[0:1]) > 0 and signed(d[0:1]), False, ], "lowres_wheel_down": [ lambda f, r, d, a: f == SupportedFeature.LOWRES_WHEEL and r == 0 and signed(d[0:1]) < 0 and signed(d[0:1]), False, ], "hires_wheel_up": [ lambda f, r, d, a: f == SupportedFeature.HIRES_WHEEL and r == 0 and signed(d[1:3]) > 0 and signed(d[1:3]), False, ], "hires_wheel_down": [ lambda f, r, d, a: f == SupportedFeature.HIRES_WHEEL and r == 0 and signed(d[1:3]) < 0 and signed(d[1:3]), False, ], "charging": [charging, False], "False": [lambda f, r, d, a: False, False], "True": [lambda f, r, d, a: True, False], } MOUSE_GESTURE_TESTS = { "mouse-down": ["Mouse Down"], "mouse-up": ["Mouse Up"], "mouse-left": ["Mouse Left"], "mouse-right": ["Mouse Right"], "mouse-noop": [], } # COMPONENTS = {} class RuleComponent: def compile(self, c): if isinstance(c, RuleComponent): return c elif isinstance(c, dict) and len(c) == 1: k, v = next(iter(c.items())) if k in COMPONENTS: return COMPONENTS[k](v) logger.warning("illegal component in rule: %s", c) return Condition() def _evaluate(components, feature, notification: HIDPPNotification, device, result) -> Any: res = True for component in components: res = component.evaluate(feature, notification, device, result) if not isinstance(component, Action) and res is None: return None if isinstance(component, Condition) and not res: return res return res class Rule(RuleComponent): def __init__(self, args, source=None, warn=True): self.components = [self.compile(a) for a in args] self.source = source def __str__(self): source = "(" + self.source + ")" if self.source else "" return f"Rule{source}[{', '.join([c.__str__() for c in self.components])}]" def evaluate(self, feature, notification: HIDPPNotification, device, last_result): if logger.isEnabledFor(logging.DEBUG): logger.debug("evaluate rule: %s", self) return _evaluate(self.components, feature, notification, device, True) def once(self, feature, notification: HIDPPNotification, device, last_result): self.evaluate(feature, notification, device, last_result) return False def data(self): return {"Rule": [c.data() for c in self.components]} class Condition(RuleComponent): def __init__(self, *args): pass def __str__(self): return "CONDITION" def evaluate(self, feature, notification: HIDPPNotification, device, last_result): if logger.isEnabledFor(logging.DEBUG): logger.debug("evaluate condition: %s", self) return False class Not(Condition): def __init__(self, op, warn=True): if isinstance(op, list) and len(op) == 1: op = op[0] self.op = op self.component = self.compile(op) def __str__(self): return "Not: " + str(self.component) def evaluate(self, feature, notification: HIDPPNotification, device, last_result): if logger.isEnabledFor(logging.DEBUG): logger.debug("evaluate condition: %s", self) result = self.component.evaluate(feature, notification, device, last_result) return None if result is None else not result def data(self): return {"Not": self.component.data()} class Or(Condition): def __init__(self, args, warn=True): self.components = [self.compile(a) for a in args] def __str__(self): return "Or: [" + ", ".join(str(c) for c in self.components) + "]" def evaluate(self, feature, notification: HIDPPNotification, device, last_result): if logger.isEnabledFor(logging.DEBUG): logger.debug("evaluate condition: %s", self) result = False for component in self.components: result = component.evaluate(feature, notification, device, last_result) if not isinstance(component, Action) and result is None: return None if isinstance(component, Condition) and result: return result return result def data(self): return {"Or": [c.data() for c in self.components]} class And(Condition): def __init__(self, args, warn=True): self.components = [self.compile(a) for a in args] def __str__(self): return "And: [" + ", ".join(str(c) for c in self.components) + "]" def evaluate(self, feature, notification: HIDPPNotification, device, last_result): if logger.isEnabledFor(logging.DEBUG): logger.debug("evaluate condition: %s", self) return _evaluate(self.components, feature, notification, device, last_result) def data(self): return {"And": [c.data() for c in self.components]} def x11_focus_prog(): if not x11_setup(): return None pid = wm_class = None window = xdisplay.get_input_focus().focus while window: pid = window.get_full_property(NET_WM_PID, 0) wm_class = window.get_wm_class() if wm_class and pid: break window = window.query_tree().parent try: name = psutil.Process(pid.value[0]).name() if pid else "" except Exception: name = "" return (wm_class[0], wm_class[1], name) if wm_class else (name,) def x11_pointer_prog(): if not x11_setup(): return None pid = wm_class = None window = xdisplay.screen().root.query_pointer().child for child in reversed(window.query_tree().children): pid = child.get_full_property(NET_WM_PID, 0) wm_class = child.get_wm_class() if wm_class: break name = psutil.Process(pid.value[0]).name() if pid else "" return (wm_class[0], wm_class[1], name) if wm_class else (name,) def gnome_dbus_focus_prog(): if not gnome_dbus_interface_setup(): return None wm_class = _dbus_interface.ActiveWindow() return (wm_class,) if wm_class else None def gnome_dbus_pointer_prog(): if not gnome_dbus_interface_setup(): return None wm_class = _dbus_interface.PointerOverWindow() return (wm_class,) if wm_class else None class Process(Condition): def __init__(self, process, warn=True): self.process = process if (not wayland and not x11_setup()) or (wayland and not gnome_dbus_interface_setup()): if warn: logger.warning( "rules can only access active process in X11 or in Wayland under GNOME with Solaar Gnome " "extension - %s", self, ) if not isinstance(process, str): if warn: logger.warning("rule Process argument not a string: %s", process) self.process = str(process) def __str__(self): return "Process: " + str(self.process) def evaluate(self, feature, notification: HIDPPNotification, device, last_result): if logger.isEnabledFor(logging.DEBUG): logger.debug("evaluate condition: %s", self) if not isinstance(self.process, str): return False focus = x11_focus_prog() if not wayland else gnome_dbus_focus_prog() result = any(bool(s and s.startswith(self.process)) for s in focus) if focus else None return result def data(self): return {"Process": str(self.process)} class MouseProcess(Condition): def __init__(self, process, warn=True): self.process = process if (not wayland and not x11_setup()) or (wayland and not gnome_dbus_interface_setup()): if warn: logger.warning( "rules cannot access active mouse process " "in X11 or in Wayland under GNOME with Solaar Extension for GNOME - %s", self, ) if not isinstance(process, str): if warn: logger.warning("rule MouseProcess argument not a string: %s", process) self.process = str(process) def __str__(self): return "MouseProcess: " + str(self.process) def evaluate(self, feature, notification: HIDPPNotification, device, last_result): if logger.isEnabledFor(logging.DEBUG): logger.debug("evaluate condition: %s", self) if not isinstance(self.process, str): return False pointer_focus = x11_pointer_prog() if not wayland else gnome_dbus_pointer_prog() result = any(bool(s and s.startswith(self.process)) for s in pointer_focus) if pointer_focus else None return result def data(self): return {"MouseProcess": str(self.process)} class Feature(Condition): def __init__(self, feature: str, warn: bool = True): try: self.feature = SupportedFeature[feature] except KeyError: self.feature = None if warn: logger.warning("rule Feature argument not name of a feature: %s", feature) def __str__(self): return "Feature: " + str(self.feature) def evaluate(self, feature, notification: HIDPPNotification, device, last_result): if logger.isEnabledFor(logging.DEBUG): logger.debug("evaluate condition: %s", self) return feature == self.feature def data(self): return {"Feature": str(self.feature)} class Report(Condition): def __init__(self, report, warn=True): if not (isinstance(report, int)): if warn: logger.warning("rule Report argument not an integer: %s", report) self.report = -1 else: self.report = report def __str__(self): return "Report: " + str(self.report) def evaluate(self, report, notification: HIDPPNotification, device, last_result): if logger.isEnabledFor(logging.DEBUG): logger.debug("evaluate condition: %s", self) return (notification.address >> 4) == self.report def data(self): return {"Report": self.report} # Setting(device, setting, [key], value...) class Setting(Condition): def __init__(self, args, warn=True): if not (isinstance(args, list) and len(args) > 2): if warn: logger.warning("rule Setting argument not list with minimum length 3: %s", args) self.args = [] else: self.args = args def __str__(self): return "Setting: " + " ".join([str(a) for a in self.args]) def evaluate(self, report, notification: HIDPPNotification, device, last_result): if logger.isEnabledFor(logging.DEBUG): logger.debug("evaluate condition: %s", self) if len(self.args) < 3: return None dev = device.find(self.args[0]) if self.args[0] is not None else device if dev is None: logger.warning("Setting condition: device %s is not known", self.args[0]) return False setting = next((s for s in dev.settings if s.name == self.args[1]), None) if setting is None: logger.warning("Setting condition: setting %s is not the name of a setting for %s", self.args[1], dev.name) return None # should the value argument be checked to be sure it is acceptable?? needs to be careful about boolean toggle # TODO add compare methods for more validators try: result = setting.compare(self.args[2:], setting.read()) except Exception as e: logger.warning("Setting condition: error when checking setting %s: %s", self.args, e) result = False return result def data(self): return {"Setting": self.args[:]} MODIFIERS = { "Shift": int(Gdk.ModifierType.SHIFT_MASK), "Control": int(Gdk.ModifierType.CONTROL_MASK), "Alt": int(Gdk.ModifierType.MOD1_MASK), "Super": int(Gdk.ModifierType.MOD4_MASK), } MODIFIER_MASK = MODIFIERS["Shift"] + MODIFIERS["Control"] + MODIFIERS["Alt"] + MODIFIERS["Super"] class Modifiers(Condition): def __init__(self, modifiers, warn=True): modifiers = [modifiers] if isinstance(modifiers, str) else modifiers self.desired = 0 self.modifiers = [] for k in modifiers: if k in MODIFIERS: self.desired += MODIFIERS.get(k, 0) self.modifiers.append(k) else: if warn: logger.warning("unknown rule Modifier value: %s", k) def __str__(self): return "Modifiers: " + str(self.desired) def evaluate(self, feature, notification: HIDPPNotification, device, last_result): if logger.isEnabledFor(logging.DEBUG): logger.debug("evaluate condition: %s", self) if gkeymap: current = gkeymap.get_modifier_state() # get the current keyboard modifier return self.desired == (current & MODIFIER_MASK) else: logger.warning("no keymap so cannot determine modifier keys") return False def data(self): return {"Modifiers": [str(m) for m in self.modifiers]} class Key(Condition): DOWN = "pressed" UP = "released" def __init__(self, args, warn=True): default_key = 0 default_action = self.DOWN key, action = None, None if not args or not isinstance(args, (list, str)): if warn: logger.warning(f"rule Key arguments unknown: {args}") key = default_key action = default_action elif isinstance(args, str): logger.debug(f'rule Key assuming action "{default_action}" for "{args}"') key = args action = default_action elif isinstance(args, list): if len(args) == 1: logger.debug(f'rule Key assuming action "{default_action}" for "{args}"') key, action = args[0], default_action elif len(args) >= 2: key, action = args[:2] if isinstance(key, str) and key in CONTROL: self.key = CONTROL[key] elif isinstance(key, str) and key.startswith("unknown:"): logger.info(f"rule Key key name currently unknown: {key}") self.key = CONTROL[int(key[-4:], 16)] else: if warn: logger.warning(f"rule Key key name not name of a Logitech key: {key}") self.key = default_key if isinstance(action, str) and action in (self.DOWN, self.UP): self.action = action else: if warn: logger.warning(f"rule Key action unknown: {action}, assuming {default_action}") self.action = default_action def __str__(self): return f"Key: {str(self.key) if self.key else 'None'} ({self.action})" def evaluate(self, feature, notification: HIDPPNotification, device, last_result): if logger.isEnabledFor(logging.DEBUG): logger.debug("evaluate condition: %s", self) return bool(self.key and self.key == (key_down if self.action == self.DOWN else key_up)) def data(self): return {"Key": [str(self.key), self.action]} class KeyIsDown(Condition): def __init__(self, args, warn=True): default_key = 0 key = None if not args or not isinstance(args, str): if warn: logger.warning(f"rule KeyDown arguments unknown: {args}") key = default_key elif isinstance(args, str): key = args if isinstance(key, str) and key in CONTROL: self.key = CONTROL[key] else: if warn: logger.warning(f"rule Key key name not name of a Logitech key: {key}") self.key = default_key def __str__(self): return f"KeyIsDown: {str(self.key) if self.key else 'None'}" def evaluate(self, feature, notification: HIDPPNotification, device, last_result): if logger.isEnabledFor(logging.DEBUG): logger.debug("evaluate condition: %s", self) return key_is_down(self.key) def data(self): return {"KeyIsDown": str(self.key)} def bit_test(start, end, bits): return lambda f, r, d: int.from_bytes(d[start:end], byteorder="big", signed=True) & bits def range_test(start, end, min, max): def range_test_helper(_f, _r, d): value = int.from_bytes(d[start:end], byteorder="big", signed=True) return min <= value <= max and (value if value else True) return range_test_helper class Test(Condition): def __init__(self, test, warn=True): self.test = "" self.parameter = None if isinstance(test, str): test = [test] if isinstance(test, list) and all(isinstance(t, int) for t in test): if warn: logger.warning("Test rules consisting of numbers are deprecated, converting to a TestBytes condition") self.__class__ = TestBytes self.__init__(test, warn=warn) elif isinstance(test, list): if test[0] in MOUSE_GESTURE_TESTS: if warn: logger.warning("mouse movement test %s deprecated, converting to a MouseGesture", test) self.__class__ = MouseGesture self.__init__(MOUSE_GESTURE_TESTS[0][test], warn=warn) elif test[0] in TESTS: self.test = test[0] self.function = TESTS[test[0]][0] self.parameter = test[1] if len(test) > 1 else None else: if warn: logger.warning("rule Test name not name of a test: %s", test) self.test = "False" self.function = TESTS["False"][0] else: if warn: logger.warning("rule Test argument not valid %s", test) def __str__(self): return "Test: " + str(self.test) def evaluate(self, feature, notification: HIDPPNotification, device, last_result): if logger.isEnabledFor(logging.DEBUG): logger.debug("evaluate condition: %s", self) return self.function(feature, notification.address, notification.data, self.parameter) def data(self): return {"Test": ([self.test, self.parameter] if self.parameter is not None else [self.test])} class TestBytes(Condition): def __init__(self, test, warn=True): self.test = test if ( isinstance(test, list) and 2 < len(test) <= 4 and all(isinstance(t, int) for t in test) and 0 <= test[0] <= 16 and 0 <= test[1] <= 16 and test[0] < test[1] ): self.function = bit_test(*test) if len(test) == 3 else range_test(*test) else: if warn: logger.warning("rule TestBytes argument not valid %s", test) def __str__(self): return "TestBytes: " + str(self.test) def evaluate(self, feature, notification: HIDPPNotification, device, last_result): if logger.isEnabledFor(logging.DEBUG): logger.debug("evaluate condition: %s", self) return self.function(feature, notification.address, notification.data) def data(self): return {"TestBytes": self.test[:]} class MouseGesture(Condition): MOVEMENTS = [ "Mouse Up", "Mouse Down", "Mouse Left", "Mouse Right", "Mouse Up-left", "Mouse Up-right", "Mouse Down-left", "Mouse Down-right", ] def __init__(self, movements, warn=True): if isinstance(movements, str): movements = [movements] for x in movements: if x not in self.MOVEMENTS and x not in CONTROL: if warn: logger.warning("rule Mouse Gesture argument not direction or name of a Logitech key: %s", x) self.movements = movements def __str__(self): return "MouseGesture: " + " ".join(self.movements) def evaluate(self, feature, notification: HIDPPNotification, device, last_result): if logger.isEnabledFor(logging.DEBUG): logger.debug("evaluate condition: %s", self) if feature == SupportedFeature.MOUSE_GESTURE: d = notification.data data = struct.unpack("!" + (int(len(d) / 2) * "h"), d) data_offset = 1 movement_offset = 0 if self.movements and self.movements[0] not in self.MOVEMENTS: # matching against initiating key movement_offset = 1 if self.movements[0] != str(CONTROL[data[0]]): return False for m in self.movements[movement_offset:]: if data_offset >= len(data): return False if data[data_offset] == 0: direction = xy_direction(data[data_offset + 1], data[data_offset + 2]) if m != direction: return False data_offset += 3 elif data[data_offset] == 1: if m != str(CONTROL[data[data_offset + 1]]): return False data_offset += 2 return data_offset == len(data) return False def data(self): return {"MouseGesture": [str(m) for m in self.movements]} class Active(Condition): def __init__(self, devID, warn=True): if not (isinstance(devID, str)): if warn: logger.warning("rule Active argument not a string: %s", devID) self.devID = "" self.devID = devID def __str__(self): return "Active: " + str(self.devID) def evaluate(self, feature, notification: HIDPPNotification, device, last_result): if logger.isEnabledFor(logging.DEBUG): logger.debug("evaluate condition: %s", self) dev = device.find(self.devID) return bool(dev and dev.ping()) def data(self): return {"Active": self.devID} class Device(Condition): def __init__(self, devID, warn=True): if not (isinstance(devID, str)): if warn: logger.warning("rule Device argument not a string: %s", devID) self.devID = "" self.devID = devID def __str__(self): return "Device: " + str(self.devID) def evaluate(self, feature, notification: HIDPPNotification, device, last_result): if logger.isEnabledFor(logging.DEBUG): logger.debug("evaluate condition: %s", self) return device.unitId == self.devID or device.serial == self.devID def data(self): return {"Device": self.devID} class Host(Condition): def __init__(self, host, warn=True): if not (isinstance(host, str)): if warn: logger.warning("rule Host Name argument not a string: %s", host) self.host = "" self.host = host def __str__(self): return "Host: " + str(self.host) def evaluate(self, feature, notification: HIDPPNotification, device, last_result): if logger.isEnabledFor(logging.DEBUG): logger.debug("evaluate condition: %s", self) hostname = socket.getfqdn() return hostname.startswith(self.host) def data(self): return {"Host": self.host} class Action(RuleComponent): def __init__(self, *args): pass def evaluate(self, feature, notification: HIDPPNotification, device, last_result): return None def keysym_to_keycode(keysym, _modifiers) -> Tuple[int, int]: # maybe should take shift into account """Reverse the keycode to keysym mapping. Warning: This is an attempt to reverse the keycode to keysym mappping in XKB. It may not be completely general. """ group = kbdgroup() or 0 keycodes = gkeymap.get_entries_for_keyval(keysym) (keycode, level) = (None, None) for k in keycodes.keys: # mappings that have the correct group if group == k.group and k.keycode < 256 and (level is None or k.level < level): keycode = k.keycode level = k.level if keycode or group == 0: return keycode, level for k in keycodes.keys: # mappings for group 0 where keycode only has group 0 mappings if 0 == k.group and k.keycode < 256 and (level is None or k.level < level): (a, m, vs) = gkeymap.get_entries_for_keycode(k.keycode) if a and all(mk.group == 0 for mk in m): keycode = k.keycode level = k.level return keycode, level class KeyPress(Action): def __init__(self, args, warn=True): self.key_names, self.action = self.regularize_args(args) if not isinstance(self.key_names, list): if warn: logger.warning("rule KeyPress keys not key names %s", self.keys_names) self.key_symbols = [] else: self.key_symbols = [XK_KEYS.get(k, None) for k in self.key_names] if not all(self.key_symbols): if warn: logger.warning("rule KeyPress keys not key names %s", self.key_names) self.key_symbols = [] def regularize_args(self, args): action = CLICK if not isinstance(args, list): args = [args] keys = args if len(args) == 2 and args[1] in [CLICK, DEPRESS, RELEASE]: keys = [args[0]] if isinstance(args[0], str) else args[0] action = args[1] return keys, action def __str__(self): return "KeyPress: " + " ".join(self.key_names) + " " + self.action def needed(self, k, modifiers): code = modifier_code(k) return not (code is not None and modifiers & (1 << code)) def mods(self, level, modifiers, direction): if level == 2 or level == 3: (sk, _) = keysym_to_keycode(XK_KEYS.get("ISO_Level3_Shift", None), modifiers) if sk and self.needed(sk, modifiers): simulate_key(sk, direction) if level == 1 or level == 3: (sk, _) = keysym_to_keycode(XK_KEYS.get("Shift_L", None), modifiers) if sk and self.needed(sk, modifiers): simulate_key(sk, direction) def keyDown(self, keysyms_, modifiers): for k in keysyms_: (keycode, level) = keysym_to_keycode(k, modifiers) if keycode is None: logger.warning("rule KeyPress key symbol not currently available %s", self) elif self.action != CLICK or self.needed(keycode, modifiers): # only check needed when clicking self.mods(level, modifiers, _KEY_PRESS) simulate_key(keycode, _KEY_PRESS) def keyUp(self, keysyms_, modifiers): for k in keysyms_: (keycode, level) = keysym_to_keycode(k, modifiers) if keycode and (self.action != CLICK or self.needed(keycode, modifiers)): # only check needed when clicking simulate_key(keycode, _KEY_RELEASE) self.mods(level, modifiers, _KEY_RELEASE) def evaluate(self, feature, notification: HIDPPNotification, device, last_result): if gkeymap: current = gkeymap.get_modifier_state() if logger.isEnabledFor(logging.INFO): logger.info( "KeyPress action: %s %s, group %s, modifiers %s", self.key_names, self.action, kbdgroup(), current, ) if self.action != RELEASE: self.keyDown(self.key_symbols, current) if self.action != DEPRESS: self.keyUp(reversed(self.key_symbols), current) time.sleep(0.01) else: logger.warning("no keymap so cannot determine which keycode to send") return None def data(self): return {"KeyPress": [[str(k) for k in self.key_names], self.action]} # KeyDown is dangerous as the key can auto-repeat and make your system unusable # class KeyDown(KeyPress): # def evaluate(self, feature, notification: HIDPPNotification, device, last_result): # super().keyDown(self.keys, current_key_modifiers) # class KeyUp(KeyPress): # def evaluate(self, feature, notification: HIDPPNotification, device, last_result): # super().keyUp(self.keys, current_key_modifiers) class MouseScroll(Action): def __init__(self, amounts, warn=True): if len(amounts) == 1 and isinstance(amounts[0], list): amounts = amounts[0] if not (len(amounts) == 2 and all([isinstance(a, numbers.Number) for a in amounts])): if warn: logger.warning("rule MouseScroll argument not two numbers %s", amounts) amounts = [0, 0] self.amounts = amounts def __str__(self): return "MouseScroll: " + " ".join([str(a) for a in self.amounts]) def evaluate(self, feature, notification: HIDPPNotification, device, last_result): amounts = self.amounts if isinstance(last_result, numbers.Number): amounts = [math.floor(last_result * a) for a in self.amounts] if logger.isEnabledFor(logging.INFO): logger.info("MouseScroll action: %s %s %s", self.amounts, last_result, amounts) dx, dy = amounts simulate_scroll(dx, dy) time.sleep(0.01) return None def data(self): return {"MouseScroll": self.amounts[:]} class MouseClick(Action): def __init__(self, args, warn=True): if len(args) == 1 and isinstance(args[0], list): args = args[0] if not isinstance(args, list): args = [args] self.button = str(args[0]) if len(args) >= 0 else None if self.button not in buttons: if warn: logger.warning("rule MouseClick action: button %s not known", self.button) self.button = None count = args[1] if len(args) >= 2 else 1 try: self.count = int(count) except (ValueError, TypeError): if count in [CLICK, DEPRESS, RELEASE]: self.count = count elif warn: logger.warning( "rule MouseClick action: argument %s should be an integer or CLICK, PRESS, or RELEASE", count, ) self.count = 1 def __str__(self): return f"MouseClick: {self.button} ({int(self.count)})" def evaluate(self, feature, notification: HIDPPNotification, device, last_result): if logger.isEnabledFor(logging.INFO): logger.info(f"MouseClick action: {int(self.count)} {self.button}") if self.button and self.count: click(buttons[self.button], self.count) time.sleep(0.01) return None def data(self): return {"MouseClick": [self.button, self.count]} class Set(Action): def __init__(self, args, warn=True): if not (isinstance(args, list) and len(args) > 2): if warn: logger.warning("rule Set argument not list with minimum length 3: %s", args) self.args = [] else: self.args = args def __str__(self): return "Set: " + " ".join([str(a) for a in self.args]) def evaluate(self, feature, notification: HIDPPNotification, device, last_result): if len(self.args) < 3: return None if logger.isEnabledFor(logging.INFO): logger.info("Set action: %s", self.args) dev = device.find(self.args[0]) if self.args[0] is not None else device if dev is None: logger.warning("Set action: device %s is not known", self.args[0]) return None setting = next((s for s in dev.settings if s.name == self.args[1]), None) if setting is None: logger.warning("Set action: setting %s is not the name of a setting for %s", self.args[1], dev.name) return None args = setting.acceptable(self.args[2:], setting.read()) if args is None: logger.warning( "Set Action: invalid args %s for setting %s of %s", self.args[2:], self.args[1], self.args[0], ) return None if len(args) > 1: setting.write_key_value(args[0], args[1]) else: setting.write(args[0]) if device.setting_callback: device.setting_callback(device, type(setting), args) return None def data(self): return {"Set": self.args[:]} class Execute(Action): def __init__(self, args, warn=True): if isinstance(args, str): args = [args] if not (isinstance(args, list) and all(isinstance(arg), str) for arg in args): if warn: logger.warning("rule Execute argument not list of strings: %s", args) self.args = [] else: self.args = args def __str__(self): return "Execute: " + " ".join([a for a in self.args]) def evaluate(self, feature, notification: HIDPPNotification, device, last_result): if logger.isEnabledFor(logging.INFO): logger.info("Execute action: %s", self.args) subprocess.Popen(self.args) return None def data(self): return {"Execute": self.args[:]} class Later(Action): def __init__(self, args, warn=True): self.delay = 0 self.rule = Rule([]) self.components = [] if not (isinstance(args, list)): args = [args] if not (isinstance(args, list) and len(args) >= 1): if warn: logger.warning("rule Later argument not list with minimum length 1: %s", args) elif not (isinstance(args[0], (int, float))) or not 0.01 <= args[0] <= 100: if warn: logger.warning("rule Later delay not between 0.01 and 100: %s", args) else: self.delay = args[0] self.rule = Rule(args[1:], warn=warn) self.components = self.rule.components def __str__(self): return "Later: [" + str(self.delay) + ", " + ", ".join(str(c) for c in self.components) + "]" def evaluate(self, feature, notification: HIDPPNotification, device, last_result): if self.delay and self.rule: if self.delay >= 1: GLib.timeout_add_seconds(int(self.delay), Rule.once, self.rule, feature, notification, device, last_result) else: GLib.timeout_add(int(self.delay * 1000), Rule.once, self.rule, feature, notification, device, last_result) return None def data(self): data = [c.data() for c in self.components] data.insert(0, self.delay) return {"Later": data} COMPONENTS = { "Rule": Rule, "Not": Not, "Or": Or, "And": And, "Process": Process, "MouseProcess": MouseProcess, "Feature": Feature, "Report": Report, "Setting": Setting, "Modifiers": Modifiers, "Key": Key, "KeyIsDown": KeyIsDown, "Test": Test, "TestBytes": TestBytes, "MouseGesture": MouseGesture, "Active": Active, "Device": Device, "Host": Host, "KeyPress": KeyPress, "MouseScroll": MouseScroll, "MouseClick": MouseClick, "Set": Set, "Execute": Execute, "Later": Later, } built_in_rules = Rule( [ { "Rule": [ # Implement problematic keys for Craft and MX Master {"Rule": [{"Key": ["Brightness Down", "pressed"]}, {"KeyPress": "XF86_MonBrightnessDown"}]}, {"Rule": [{"Key": ["Brightness Up", "pressed"]}, {"KeyPress": "XF86_MonBrightnessUp"}]}, ] }, ] ) def key_is_down(key: NamedInt) -> bool: """Checks if given key is pressed or not.""" if key == CONTROL.MR: return mr_key_down elif CONTROL.M1 <= key <= CONTROL.M8: return bool(m_keys_down & (0x01 << (key - CONTROL.M1))) elif CONTROL.G1 <= key <= CONTROL.G32: return bool(g_keys_down & (0x01 << (key - CONTROL.G1))) return key in keys_down def evaluate_rules(feature, notification: HIDPPNotification, device): if logger.isEnabledFor(logging.DEBUG): logger.debug("evaluating rules on %s", notification) rules.evaluate(feature, notification, device, True) def process_notification(device, notification: HIDPPNotification, feature) -> None: """Processes HID++ notifications.""" global keys_down, g_keys_down, m_keys_down, mr_key_down, key_down, key_up, thumb_wheel_displacement key_down, key_up = None, None # need to keep track of keys that are down to find a new key down if notification.address == 0x00: if feature == SupportedFeature.REPROG_CONTROLS_V4: new_keys_down = struct.unpack("!4H", notification.data[:8]) for key in new_keys_down: if key and key not in keys_down: key_down = key for key in keys_down: if key and key not in new_keys_down: key_up = key keys_down = new_keys_down # and also G keys down elif feature == SupportedFeature.GKEY: new_g_keys_down = struct.unpack(" Rule: loaded_rules = [] try: with open(file_path) as config_file: loaded_rules = [] for loaded_rule in yaml.safe_load_all(config_file): rule = Rule(loaded_rule, source=file_path) if logger.isEnabledFor(logging.DEBUG): logger.debug("load rule: %s", rule) loaded_rules.append(rule) if logger.isEnabledFor(logging.INFO): logger.info("loaded %d rules from %s", len(loaded_rules), config_file.name) except Exception as e: logger.error("failed to load from %s\n%s", file_path, e) return Rule([Rule(loaded_rules, source=file_path), built_in_rules]) load_config_rule_file() Solaar-1.1.14/lib/logitech_receiver/exceptions.py000066400000000000000000000032761473525213700220230ustar00rootroot00000000000000## Copyright (C) 2012-2013 Daniel Pavel ## Copyright (C) 2014-2024 Solaar Contributors https://pwr-solaar.github.io/Solaar/ ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. from .common import KwException """Exceptions that may be raised by this API.""" class NoReceiver(KwException): """Raised when trying to talk through a previously open handle, when the receiver is no longer available. Should only happen if the receiver is physically disconnected from the machine, or its kernel driver module is unloaded.""" pass class NoSuchDevice(KwException): """Raised when trying to reach a device number not paired to the receiver.""" pass class DeviceUnreachable(KwException): """Raised when a request is made to an unreachable (turned off) device.""" pass class FeatureNotSupported(KwException): """Raised when trying to request a feature not supported by the device.""" pass class FeatureCallError(KwException): """Raised if the device replied to a feature call with an error.""" pass Solaar-1.1.14/lib/logitech_receiver/hidpp10.py000066400000000000000000000243641473525213700211100ustar00rootroot00000000000000## Copyright (C) 2012-2013 Daniel Pavel ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. from __future__ import annotations import logging from typing import Any from typing_extensions import Protocol from . import common from .common import Battery from .common import BatteryLevelApproximation from .common import BatteryStatus from .common import FirmwareKind from .hidpp10_constants import Registers logger = logging.getLogger(__name__) class Device(Protocol): def request(self, request_id, *params): ... @property def kind(self) -> Any: ... @property def online(self) -> bool: ... @property def protocol(self) -> Any: ... @property def registers(self) -> list: ... def read_register(device: Device, register: Registers | int, *params) -> Any: assert device is not None, f"tried to read register {register:02X} from invalid device {device}" # support long registers by adding a 2 in front of the register number request_id = 0x8100 | (int(register) & 0x2FF) return device.request(request_id, *params) def write_register(device: Device, register: Registers | int, *value) -> Any: assert device is not None, f"tried to write register {register:02X} to invalid device {device}" # support long registers by adding a 2 in front of the register number request_id = 0x8000 | (int(register) & 0x2FF) return device.request(request_id, *value) def get_configuration_pending_flags(receiver): assert not receiver.isDevice result = read_register(receiver, Registers.DEVICES_CONFIGURATION) if result is not None: return ord(result[:1]) def set_configuration_pending_flags(receiver, devices): assert not receiver.isDevice result = write_register(receiver, Registers.DEVICES_CONFIGURATION, devices) return result is not None class Hidpp10: def get_battery(self, device: Device): assert device is not None assert device.kind is not None if not device.online: return """Reads a device's battery level, if provided by the HID++ 1.0 protocol.""" if device.protocol and device.protocol >= 2.0: # let's just assume HID++ 2.0 devices do not provide the battery info in a register return for r in (Registers.BATTERY_STATUS, Registers.BATTERY_CHARGE): if r in device.registers: reply = read_register(device, r) if reply: return parse_battery_status(r, reply) return # the descriptor does not tell us which register this device has, try them both reply = read_register(device, Registers.BATTERY_CHARGE) if reply: # remember this for the next time device.registers.append(Registers.BATTERY_CHARGE) return parse_battery_status(Registers.BATTERY_CHARGE, reply) reply = read_register(device, Registers.BATTERY_STATUS) if reply: # remember this for the next time device.registers.append(Registers.BATTERY_STATUS) return parse_battery_status(Registers.BATTERY_STATUS, reply) def get_firmware(self, device: Device) -> tuple[common.FirmwareInfo] | None: assert device is not None firmware = [None, None, None] reply = read_register(device, Registers.FIRMWARE, 0x01) if not reply: # won't be able to read any of it now... return fw_version = common.strhex(reply[1:3]) fw_version = f"{fw_version[0:2]}.{fw_version[2:4]}" reply = read_register(device, Registers.FIRMWARE, 0x02) if reply: fw_version += ".B" + common.strhex(reply[1:3]) fw = common.FirmwareInfo(FirmwareKind.Firmware, "", fw_version, None) firmware[0] = fw reply = read_register(device, Registers.FIRMWARE, 0x04) if reply: bl_version = common.strhex(reply[1:3]) bl_version = f"{bl_version[0:2]}.{bl_version[2:4]}" bl = common.FirmwareInfo(FirmwareKind.Bootloader, "", bl_version, None) firmware[1] = bl reply = read_register(device, Registers.FIRMWARE, 0x03) if reply: o_version = common.strhex(reply[1:3]) o_version = f"{o_version[0:2]}.{o_version[2:4]}" o = common.FirmwareInfo(FirmwareKind.Other, "", o_version, None) firmware[2] = o if any(firmware): return tuple(f for f in firmware if f) def set_3leds(self, device: Device, battery_level=None, charging=None, warning=None): assert device is not None assert device.kind is not None if not device.online: return if Registers.THREE_LEDS not in device.registers: return if battery_level is not None: if battery_level < BatteryLevelApproximation.CRITICAL: # 1 orange, and force blink v1, v2 = 0x22, 0x00 warning = True elif battery_level < BatteryLevelApproximation.LOW: # 1 orange v1, v2 = 0x22, 0x00 elif battery_level < BatteryLevelApproximation.GOOD: # 1 green v1, v2 = 0x20, 0x00 elif battery_level < BatteryLevelApproximation.FULL: # 2 greens v1, v2 = 0x20, 0x02 else: # all 3 green v1, v2 = 0x20, 0x22 if warning: # set the blinking flag for the leds already set v1 |= v1 >> 1 v2 |= v2 >> 1 elif charging: # blink all green v1, v2 = 0x30, 0x33 elif warning: # 1 red v1, v2 = 0x02, 0x00 else: # turn off all leds v1, v2 = 0x11, 0x11 write_register(device, Registers.THREE_LEDS, v1, v2) def get_notification_flags(self, device: Device): return self._get_register(device, Registers.NOTIFICATIONS) def set_notification_flags(self, device: Device, *flag_bits): assert device is not None # Avoid a call if the device is not online, # or the device does not support registers. if device.kind is not None: # peripherals with protocol >= 2.0 don't support registers if device.protocol and device.protocol >= 2.0: return flag_bits = sum(int(b) for b in flag_bits) assert flag_bits & 0x00FFFFFF == flag_bits result = write_register(device, Registers.NOTIFICATIONS, common.int2bytes(flag_bits, 3)) return result is not None def get_device_features(self, device: Device): return self._get_register(device, Registers.MOUSE_BUTTON_FLAGS) def _get_register(self, device: Device, register: Registers | int): assert device is not None # Avoid a call if the device is not online, # or the device does not support registers. if device.kind is not None: # peripherals with protocol >= 2.0 don't support registers if device.protocol and device.protocol >= 2.0: return flags = read_register(device, register) if flags is not None: assert len(flags) == 3 return common.bytes2int(flags) def parse_battery_status(register: Registers | int, reply) -> Battery | None: def status_byte_to_charge(status_byte_: int) -> BatteryLevelApproximation: if status_byte_ == 7: charge_ = BatteryLevelApproximation.FULL elif status_byte_ == 5: charge_ = BatteryLevelApproximation.GOOD elif status_byte_ == 3: charge_ = BatteryLevelApproximation.LOW elif status_byte_ == 1: charge_ = BatteryLevelApproximation.CRITICAL else: # pure 'charging' notifications may come without a status charge_ = BatteryLevelApproximation.EMPTY return charge_ def status_byte_to_battery_status(status_byte_: int) -> BatteryStatus: if status_byte_ == 0x30: status_text_ = BatteryStatus.DISCHARGING elif status_byte_ == 0x50: status_text_ = BatteryStatus.RECHARGING elif status_byte_ == 0x90: status_text_ = BatteryStatus.FULL else: status_text_ = None return status_text_ def charging_byte_to_status_text(charging_byte_: int) -> BatteryStatus: if charging_byte_ == 0x00: status_text_ = BatteryStatus.DISCHARGING elif charging_byte_ & 0x21 == 0x21: status_text_ = BatteryStatus.RECHARGING elif charging_byte_ & 0x22 == 0x22: status_text_ = BatteryStatus.FULL else: logger.warning("could not parse 0x07 battery status: %02X (level %02X)", charging_byte_, status_byte) status_text_ = None return status_text_ if register == Registers.BATTERY_CHARGE: charge = ord(reply[:1]) status_byte = ord(reply[2:3]) & 0xF0 battery_status = status_byte_to_battery_status(status_byte) return Battery(charge, None, battery_status, None) if register == Registers.BATTERY_STATUS: status_byte = ord(reply[:1]) charging_byte = ord(reply[1:2]) status_text = charging_byte_to_status_text(charging_byte) charge = status_byte_to_charge(status_byte) if charging_byte & 0x03 and status_byte == 0: # some 'charging' notifications may come with no battery level information charge = None # Return None for next charge level and voltage as these are not in HID++ 1.0 spec return Battery(charge, None, status_text, None) Solaar-1.1.14/lib/logitech_receiver/hidpp10_constants.py000066400000000000000000000140341473525213700231750ustar00rootroot00000000000000## Copyright (C) 2012-2013 Daniel Pavel ## Copyright (C) 2014-2024 Solaar Contributors https://pwr-solaar.github.io/Solaar/ ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. from enum import IntEnum from .common import NamedInts """HID constants for HID++ 1.0. Most of them as defined by the official Logitech HID++ 1.0 documentation, some of them guessed. """ DEVICE_KIND = NamedInts( unknown=0x00, keyboard=0x01, mouse=0x02, numpad=0x03, presenter=0x04, remote=0x07, trackball=0x08, touchpad=0x09, tablet=0x0A, gamepad=0x0B, joystick=0x0C, headset=0x0D, # not from Logitech documentation remote_control=0x0E, # for compatibility with HID++ 2.0 receiver=0x0F, # for compatibility with HID++ 2.0 ) POWER_SWITCH_LOCATION = NamedInts( base=0x01, top_case=0x02, edge_of_top_right_corner=0x03, top_left_corner=0x05, bottom_left_corner=0x06, top_right_corner=0x07, bottom_right_corner=0x08, top_edge=0x09, right_edge=0x0A, left_edge=0x0B, bottom_edge=0x0C, ) # Some flags are used both by devices and receivers. The Logitech documentation # mentions that the first and last (third) byte are used for devices while the # second is used for the receiver. In practise, the second byte is also used for # some device-specific notifications (keyboard illumination level). Do not # simply set all notification bits if the software does not support it. For # example, enabling keyboard_sleep_raw makes the Sleep key a no-operation unless # the software is updated to handle that event. # Observations: # - wireless and software present were seen on receivers, reserved_r1b4 as well # - the rest work only on devices as far as we can tell right now # In the future would be useful to have separate enums for receiver and device notification flags, # but right now we don't know enough. # additional flags taken from https://drive.google.com/file/d/0BxbRzx7vEV7eNDBheWY0UHM5dEU/view?usp=sharing NOTIFICATION_FLAG = NamedInts( numpad_numerical_keys=0x800000, f_lock_status=0x400000, roller_H=0x200000, battery_status=0x100000, # send battery charge notifications (0x07 or 0x0D) mouse_extra_buttons=0x080000, roller_V=0x040000, power_keys=0x020000, # system control keys such as Sleep keyboard_multimedia_raw=0x010000, # consumer controls such as Mute and Calculator multi_touch=0x001000, # notify on multi-touch changes software_present=0x000800, # software is controlling part of device behaviour link_quality=0x000400, # notify on link quality changes ui=0x000200, # notify on UI changes wireless=0x000100, # notify when the device wireless goes on/off-line configuration_complete=0x000004, voip_telephony=0x000002, threed_gesture=0x000001, ) class ErrorCode(IntEnum): INVALID_SUB_ID_COMMAND = 0x01 INVALID_ADDRESS = 0x02 INVALID_VALUE = 0x03 CONNECTION_REQUEST_FAILED = 0x04 TOO_MANY_DEVICES = 0x05 ALREADY_EXISTS = 0x06 BUSY = 0x07 UNKNOWN_DEVICE = 0x08 RESOURCE_ERROR = 0x09 REQUEST_UNAVAILABLE = 0x0A UNSUPPORTED_PARAMETER_VALUE = 0x0B WRONG_PIN_CODE = 0x0C class PairingError(IntEnum): DEVICE_TIMEOUT = 0x01 DEVICE_NOT_SUPPORTED = 0x02 TOO_MANY_DEVICES = 0x03 SEQUENCE_TIMEOUT = 0x06 class BoltPairingError(IntEnum): DEVICE_TIMEOUT = 0x01 FAILED = 0x02 class Registers(IntEnum): """Known HID registers. Devices usually have a (small) sub-set of these. Some registers are only applicable to certain device kinds (e.g. smooth_scroll only applies to mice). """ # Generally applicable NOTIFICATIONS = 0x00 FIRMWARE = 0xF1 # only apply to receivers RECEIVER_CONNECTION = 0x02 RECEIVER_PAIRING = 0xB2 DEVICES_ACTIVITY = 0x2B3 RECEIVER_INFO = 0x2B5 BOLT_DEVICE_DISCOVERY = 0xC0 BOLT_PAIRING = 0x2C1 BOLT_UNIQUE_ID = 0x02FB # only apply to devices MOUSE_BUTTON_FLAGS = 0x01 KEYBOARD_HAND_DETECTION = 0x01 DEVICES_CONFIGURATION = 0x03 BATTERY_STATUS = 0x07 KEYBOARD_FN_SWAP = 0x09 BATTERY_CHARGE = 0x0D KEYBOARD_ILLUMINATION = 0x17 THREE_LEDS = 0x51 MOUSE_DPI = 0x63 # notifications PASSKEY_REQUEST_NOTIFICATION = 0x4D PASSKEY_PRESSED_NOTIFICATION = 0x4E DEVICE_DISCOVERY_NOTIFICATION = 0x4F DISCOVERY_STATUS_NOTIFICATION = 0x53 PAIRING_STATUS_NOTIFICATION = 0x54 # Subregisters for receiver_info register INFO_SUBREGISTERS = NamedInts( serial_number=0x01, # not found on many receivers fw_version=0x02, receiver_information=0x03, pairing_information=0x20, # 0x2N, by connected device extended_pairing_information=0x30, # 0x3N, by connected device device_name=0x40, # 0x4N, by connected device bolt_pairing_information=0x50, # 0x5N, by connected device bolt_device_name=0x60, # 0x6N01, by connected device, ) # Flags taken from https://drive.google.com/file/d/0BxbRzx7vEV7eNDBheWY0UHM5dEU/view?usp=sharing DEVICE_FEATURES = NamedInts( reserved1=0x010000, special_buttons=0x020000, enhanced_key_usage=0x040000, fast_fw_rev=0x080000, reserved2=0x100000, reserved3=0x200000, scroll_accel=0x400000, buttons_control_resolution=0x800000, inhibit_lock_key_sound=0x000001, reserved4=0x000002, mx_air_3d_engine=0x000004, host_control_leds=0x000008, reserved5=0x000010, reserved6=0x000020, reserved7=0x000040, reserved8=0x000080, ) Solaar-1.1.14/lib/logitech_receiver/hidpp20.py000066400000000000000000002273051473525213700211110ustar00rootroot00000000000000## Copyright (C) 2012-2013 Daniel Pavel ## Copyright (C) 2014-2024 Solaar Contributors https://pwr-solaar.github.io/Solaar/ ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. from __future__ import annotations import logging import socket import struct import threading from typing import Any from typing import Dict from typing import Generator from typing import List from typing import Optional from typing import Tuple import yaml from solaar.i18n import _ from typing_extensions import Protocol from . import common from . import exceptions from . import hidpp10_constants from . import special_keys from .common import Battery from .common import BatteryLevelApproximation from .common import BatteryStatus from .common import FirmwareKind from .common import NamedInt from .hidpp20_constants import CHARGE_STATUS from .hidpp20_constants import DEVICE_KIND from .hidpp20_constants import ChargeLevel from .hidpp20_constants import ChargeType from .hidpp20_constants import ErrorCode from .hidpp20_constants import GestureId from .hidpp20_constants import ParamId from .hidpp20_constants import SupportedFeature logger = logging.getLogger(__name__) FixedBytes5 = bytes KIND_MAP = {kind: hidpp10_constants.DEVICE_KIND[str(kind)] for kind in DEVICE_KIND} class Device(Protocol): def feature_request(self, feature, function=0x00, *params, no_reply=False) -> Any: ... @property def features(self) -> Any: ... @property def _gestures(self) -> Any: ... @property def _backlight(self) -> Any: ... @property def _profiles(self) -> Any: ... class FeaturesArray(dict): def __init__(self, device): assert device is not None self.supported = True # Actually don't know whether it is supported yet self.device = device self.inverse = {} self.version = {} self.count = 0 def _check(self) -> bool: if not self.device.online: return False if self.supported is False: return False if self.device.protocol and self.device.protocol < 2.0: self.supported = False return False if self.count > 0: return True reply = self.device.request(0x0000, struct.pack("!H", SupportedFeature.FEATURE_SET)) if reply is not None: fs_index = reply[0] if fs_index: count = self.device.request(fs_index << 8) if count is None: logger.warning("FEATURE_SET found, but failed to read features count") return False else: self.count = count[0] + 1 # ROOT feature not included in count self[SupportedFeature.ROOT] = 0 self[SupportedFeature.FEATURE_SET] = fs_index return True else: self.supported = False return False def get_feature(self, index: int) -> SupportedFeature | None: feature = self.inverse.get(index) if feature is not None: return feature elif self._check(): feature = self.inverse.get(index) if feature is not None: return feature response = self.device.feature_request(SupportedFeature.FEATURE_SET, 0x10, index) if response: data = struct.unpack("!H", response[:2])[0] try: feature = SupportedFeature(data) except ValueError: feature = f"unknown:{data:04X}" self[feature] = index self.version[feature] = response[3] return feature def enumerate(self): # return all features and their index, ordered by index if self._check(): for index in range(self.count): feature = self.get_feature(index) yield feature, index def get_feature_version(self, feature: NamedInt) -> Optional[int]: if self[feature]: return self.version.get(feature, 0) def __contains__(self, feature: NamedInt) -> bool: try: index = self.__getitem__(feature) return index is not None and index is not False except exceptions.FeatureCallError: return False def __getitem__(self, feature: NamedInt) -> Optional[int]: index = super().get(feature) if index is not None: return index elif self._check(): index = super().get(feature) if index is not None: return index response = self.device.request(0x0000, struct.pack("!H", feature)) if response: index = response[0] self[feature] = index if index else False self.version[feature] = response[2] return index if index else False def __setitem__(self, feature, index): if isinstance(super().get(feature), int): self.inverse.pop(super().get(feature)) super().__setitem__(feature, index) if index is not False: self.inverse[index] = feature def __delitem__(self, feature): raise ValueError("Don't delete features from FeatureArray") def __len__(self) -> int: return self.count __bool__ = __nonzero__ = _check class ReprogrammableKey: """Information about a control present on a device with the `REPROG_CONTROLS` feature. Ref: https://drive.google.com/file/d/0BxbRzx7vEV7eU3VfMnRuRXktZ3M/view Read-only properties: - index {int} -- index in the control ID table - key {NamedInt} -- the name of this control - default_task {NamedInt} -- the native function of this control - flags {List[str]} -- capabilities and desired software handling of the control """ def __init__(self, device: Device, index, cid, tid, flags): self._device = device self.index = index self._cid = cid self._tid = tid self._flags = flags @property def key(self) -> NamedInt: return special_keys.CONTROL[self._cid] @property def default_task(self) -> NamedInt: """NOTE: This NamedInt is a bit mixed up, because its value is the Control ID while the name is the Control ID's native task. But this makes more sense than presenting details of controls vs tasks in the interface. The same convention applies to `mapped_to`, `remappable_to`, `remap` in `ReprogrammableKeyV4`.""" task = str(special_keys.TASK[self._tid]) return NamedInt(self._cid, task) @property def flags(self) -> List[str]: return special_keys.KEY_FLAG.flag_names(self._flags) class ReprogrammableKeyV4(ReprogrammableKey): """Information about a control present on a device with the `REPROG_CONTROLS_V4` feature. Ref (v2): https://lekensteyn.nl/files/logitech/x1b04_specialkeysmsebuttons.html Ref (v4): https://drive.google.com/file/d/10imcbmoxTJ1N510poGdsviEhoFfB_Ua4/view Contains all the functionality of `ReprogrammableKey` plus remapping keys and /diverting/ them in order to handle keypresses in a custom way. Additional read-only properties: - pos {int} -- position of this control on the device; 1-16 for FN-keys, otherwise 0 - group {int} -- the group this control belongs to; other controls with this group in their `group_mask` can be remapped to this control - group_mask {List[str]} -- this control can be remapped to any control ID in these groups - mapped_to {NamedInt} -- which action this control is mapped to; usually itself - remappable_to {List[NamedInt]} -- list of actions which this control can be remapped to - mapping_flags {List[str]} -- mapping flags set on the control """ def __init__(self, device: Device, index, cid, tid, flags, pos, group, gmask): ReprogrammableKey.__init__(self, device, index, cid, tid, flags) self.pos = pos self.group = group self._gmask = gmask self._mapping_flags = None self._mapped_to = None @property def group_mask(self) -> Generator[str]: return common.flag_names(special_keys.CIDGroupBit, self._gmask) @property def mapped_to(self) -> NamedInt: if self._mapped_to is None: self._getCidReporting() self._device.keys._ensure_all_keys_queried() task = str(special_keys.TASK[self._device.keys.cid_to_tid[self._mapped_to]]) return NamedInt(self._mapped_to, task) @property def remappable_to(self) -> common.NamedInts: self._device.keys._ensure_all_keys_queried() ret = common.UnsortedNamedInts() if self.group_mask: # only keys with a non-zero gmask are remappable ret[self.default_task] = self.default_task # it should always be possible to map the key to itself for g in self.group_mask: g = special_keys.CidGroup[str(g)] for tgt_cid in self._device.keys.group_cids[g]: tgt_task = str(special_keys.TASK[self._device.keys.cid_to_tid[tgt_cid]]) tgt_task = NamedInt(tgt_cid, tgt_task) if tgt_task != self.default_task: # don't put itself in twice ret[tgt_task] = tgt_task return ret @property def mapping_flags(self) -> List[str]: if self._mapping_flags is None: self._getCidReporting() return special_keys.MAPPING_FLAG.flag_names(self._mapping_flags) def set_diverted(self, value: bool): """If set, the control is diverted temporarily and reports presses as HID++ events.""" flags = {special_keys.MAPPING_FLAG.diverted: value} self._setCidReporting(flags=flags) def set_persistently_diverted(self, value: bool): """If set, the control is diverted permanently and reports presses as HID++ events.""" flags = {special_keys.MAPPING_FLAG.persistently_diverted: value} self._setCidReporting(flags=flags) def set_rawXY_reporting(self, value: bool): """If set, the mouse temporarily reports all its raw XY events while this control is pressed as HID++ events.""" flags = {special_keys.MAPPING_FLAG.raw_XY_diverted: value} self._setCidReporting(flags=flags) def remap(self, to: NamedInt): """Temporarily remaps this control to another action.""" self._setCidReporting(remap=int(to)) def _getCidReporting(self): try: mapped_data = self._device.feature_request( SupportedFeature.REPROG_CONTROLS_V4, 0x20, *tuple(struct.pack("!H", self._cid)), ) if mapped_data: cid, mapping_flags_1, mapped_to = struct.unpack("!HBH", mapped_data[:5]) if cid != self._cid and logger.isEnabledFor(logging.WARNING): logger.warning( f"REPROG_CONTROLS_V4 endpoint getCidReporting on device {self._device} replied " + f"with a different control ID ({cid}) than requested ({self._cid})." ) self._mapped_to = mapped_to if mapped_to != 0 else self._cid if len(mapped_data) > 5: (mapping_flags_2,) = struct.unpack("!B", mapped_data[5:6]) else: mapping_flags_2 = 0 self._mapping_flags = mapping_flags_1 | (mapping_flags_2 << 8) else: raise exceptions.FeatureCallError(msg="No reply from device.") except exceptions.FeatureCallError: # if the key hasn't ever been configured only produce a warning if logger.isEnabledFor(logging.WARNING): logger.warning( f"Feature Call Error in _getCidReporting on device {self._device} for cid {self._cid} - use defaults" ) # Clear flags and set mapping target to self self._mapping_flags = 0 self._mapped_to = self._cid def _setCidReporting(self, flags: Dict[NamedInt, bool] = None, remap: int = 0): """Sends a `setCidReporting` request with the given parameters. Raises an exception if the parameters are invalid. Parameters ---------- flags A dictionary of which mapping flags to set/unset. remap Which control ID to remap to; or 0 to keep current mapping. """ flags = flags if flags else {} # See flake8 B006 # if special_keys.MAPPING_FLAG.raw_XY_diverted in flags and flags[special_keys.MAPPING_FLAG.raw_XY_diverted]: # We need diversion to report raw XY, so divert temporarily (since XY reporting is also temporary) # flags[special_keys.MAPPING_FLAG.diverted] = True # if special_keys.MAPPING_FLAG.diverted in flags and not flags[special_keys.MAPPING_FLAG.diverted]: # flags[special_keys.MAPPING_FLAG.raw_XY_diverted] = False # The capability required to set a given reporting flag. FLAG_TO_CAPABILITY = { special_keys.MAPPING_FLAG.diverted: special_keys.KEY_FLAG.divertable, special_keys.MAPPING_FLAG.persistently_diverted: special_keys.KEY_FLAG.persistently_divertable, special_keys.MAPPING_FLAG.analytics_key_events_reporting: special_keys.KEY_FLAG.analytics_key_events, special_keys.MAPPING_FLAG.force_raw_XY_diverted: special_keys.KEY_FLAG.force_raw_XY, special_keys.MAPPING_FLAG.raw_XY_diverted: special_keys.KEY_FLAG.raw_XY, } bfield = 0 for f, v in flags.items(): if v and FLAG_TO_CAPABILITY[f] not in self.flags: raise exceptions.FeatureNotSupported( msg=f'Tried to set mapping flag "{f}" on control "{self.key}" ' + f'which does not support "{FLAG_TO_CAPABILITY[f]}" on device {self._device}.' ) bfield |= int(f) if v else 0 bfield |= int(f) << 1 # The 'Xvalid' bit if self._mapping_flags: # update flags if already read if v: self._mapping_flags |= int(f) else: self._mapping_flags &= ~int(f) if remap != 0 and remap not in self.remappable_to: raise exceptions.FeatureNotSupported( msg=f'Tried to remap control "{self.key}" to a control ID {remap} which it is not remappable to ' + f"on device {self._device}." ) if remap != 0: # update mapping if changing (even if not already read) self._mapped_to = remap pkt = tuple(struct.pack("!HBH", self._cid, bfield & 0xFF, remap)) # TODO: to fully support version 4 of REPROG_CONTROLS_V4, append `(bfield >> 8) & 0xff` here. # But older devices might behave oddly given that byte, so we don't send it. ret = self._device.feature_request(SupportedFeature.REPROG_CONTROLS_V4, 0x30, *pkt) if ret is None or struct.unpack("!BBBBB", ret[:5]) != pkt and logger.isEnabledFor(logging.DEBUG): logger.debug(f"REPROG_CONTROLS_v4 setCidReporting on device {self._device} didn't echo request packet.") class PersistentRemappableAction: def __init__(self, device, index, cid, actionId, remapped, modifierMask, cidStatus): self._device = device self.index = index self._cid = cid self.actionId = actionId self.remapped = remapped self._modifierMask = modifierMask self.cidStatus = cidStatus @property def key(self) -> NamedInt: return special_keys.CONTROL[self._cid] @property def actionType(self) -> NamedInt: return special_keys.ACTIONID[self.actionId] @property def action(self): if self.actionId == special_keys.ACTIONID.Empty: return None elif self.actionId == special_keys.ACTIONID.Key: return "Key: " + str(self.modifiers) + str(self.remapped) elif self.actionId == special_keys.ACTIONID.Mouse: return "Mouse Button: " + str(self.remapped) elif self.actionId == special_keys.ACTIONID.Xdisp: return "X Displacement " + str(self.remapped) elif self.actionId == special_keys.ACTIONID.Ydisp: return "Y Displacement " + str(self.remapped) elif self.actionId == special_keys.ACTIONID.Vscroll: return "Vertical Scroll " + str(self.remapped) elif self.actionId == special_keys.ACTIONID.Hscroll: return "Horizontal Scroll: " + str(self.remapped) elif self.actionId == special_keys.ACTIONID.Consumer: return "Consumer: " + str(self.remapped) elif self.actionId == special_keys.ACTIONID.Internal: return "Internal Action " + str(self.remapped) elif self.actionId == special_keys.ACTIONID.Internal: return "Power " + str(self.remapped) else: return "Unknown" @property def modifiers(self): return special_keys.modifiers[self._modifierMask] @property def data_bytes(self): return ( common.int2bytes(self.actionId, 1) + common.int2bytes(self.remapped, 2) + common.int2bytes(self._modifierMask, 1) ) def remap(self, data_bytes): cid = common.int2bytes(self._cid, 2) if common.bytes2int(data_bytes) == special_keys.KEYS_Default: # map back to default self._device.feature_request(SupportedFeature.PERSISTENT_REMAPPABLE_ACTION, 0x50, cid, 0xFF) self._device.remap_keys._query_key(self.index) return self._device.remap_keys.keys[self.index].data_bytes else: self.actionId, self.remapped, self._modifierMask = struct.unpack("!BHB", data_bytes) self.cidStatus = 0x01 self._device.feature_request(SupportedFeature.PERSISTENT_REMAPPABLE_ACTION, 0x40, cid, 0xFF, data_bytes) return True class KeysArray: """A sequence of key mappings supported by a HID++ 2.0 device.""" def __init__(self, device, count, version): assert device is not None self.device = device self.lock = threading.Lock() if SupportedFeature.REPROG_CONTROLS_V4 in self.device.features: self.keyversion = SupportedFeature.REPROG_CONTROLS_V4 elif SupportedFeature.REPROG_CONTROLS_V2 in self.device.features: self.keyversion = SupportedFeature.REPROG_CONTROLS_V2 else: if logger.isEnabledFor(logging.ERROR): logger.error(f"Trying to read keys on device {device} which has no REPROG_CONTROLS(_VX) support.") self.keyversion = None self.keys = [None] * count def _ensure_all_keys_queried(self): """The retrieval of key information is lazy, but for certain functionality we need to know all keys. This function makes sure that's the case.""" with self.lock: # don't want two threads doing this for i, k in enumerate(self.keys): if k is None: self._query_key(i) def __getitem__(self, index): if isinstance(index, int): if index < 0 or index >= len(self.keys): raise IndexError(index) if self.keys[index] is None: self._query_key(index) return self.keys[index] elif isinstance(index, slice): indices = index.indices(len(self.keys)) return [self.__getitem__(i) for i in range(*indices)] def index(self, value): self._ensure_all_keys_queried() for index, k in enumerate(self.keys): if k is not None and int(value) == int(k.key): return index def __iter__(self): for k in range(0, len(self.keys)): yield self.__getitem__(k) def __len__(self): return len(self.keys) class KeysArrayV2(KeysArray): def __init__(self, device: Device, count, version=1): super().__init__(device, count, version) """The mapping from Control IDs to their native Task IDs. For example, Control "Left Button" is mapped to Task "Left Click". When remapping controls, we point the control we want to remap at a target Control ID rather than a target Task ID. This has the effect of performing the native task of the target control, even if the target itself is also remapped. So remapping is not recursive.""" self.cid_to_tid = {} """The mapping from Control ID groups to Controls IDs that belong to it. A key k can only be remapped to targets in groups within k.group_mask.""" self.group_cids = {g: [] for g in special_keys.CidGroup} def _query_key(self, index: int): if index < 0 or index >= len(self.keys): raise IndexError(index) keydata = self.device.feature_request(SupportedFeature.REPROG_CONTROLS, 0x10, index) if keydata: cid, tid, flags = struct.unpack("!HHB", keydata[:5]) self.keys[index] = ReprogrammableKey(self.device, index, cid, tid, flags) self.cid_to_tid[cid] = tid elif logger.isEnabledFor(logging.WARNING): logger.warning(f"Key with index {index} was expected to exist but device doesn't report it.") class KeysArrayV4(KeysArrayV2): def __init__(self, device, count): super().__init__(device, count, 4) def _query_key(self, index: int): if index < 0 or index >= len(self.keys): raise IndexError(index) keydata = self.device.feature_request(SupportedFeature.REPROG_CONTROLS_V4, 0x10, index) if keydata: cid, tid, flags1, pos, group, gmask, flags2 = struct.unpack("!HHBBBBB", keydata[:9]) flags = flags1 | (flags2 << 8) self.keys[index] = ReprogrammableKeyV4(self.device, index, cid, tid, flags, pos, group, gmask) self.cid_to_tid[cid] = tid if group != 0: # 0 = does not belong to a group self.group_cids[special_keys.CidGroup(group)].append(cid) elif logger.isEnabledFor(logging.WARNING): logger.warning(f"Key with index {index} was expected to exist but device doesn't report it.") # we are only interested in the current host, so use 0xFF for the host throughout class KeysArrayPersistent(KeysArray): def __init__(self, device, count): super().__init__(device, count, 5) self._capabilities = None @property def capabilities(self): if self._capabilities is None and self.device.online: capabilities = self.device.feature_request(SupportedFeature.PERSISTENT_REMAPPABLE_ACTION, 0x00) assert capabilities, "Oops, persistent remappable key capabilities cannot be retrieved!" self._capabilities = struct.unpack("!H", capabilities[:2])[0] # flags saying what the mappings are possible return self._capabilities def _query_key(self, index: int): if index < 0 or index >= len(self.keys): raise IndexError(index) keydata = self.device.feature_request(SupportedFeature.PERSISTENT_REMAPPABLE_ACTION, 0x20, index, 0xFF) if keydata: key = struct.unpack("!H", keydata[:2])[0] mapped_data = self.device.feature_request( SupportedFeature.PERSISTENT_REMAPPABLE_ACTION, 0x30, key >> 8, key & 0xFF, 0xFF, ) if mapped_data: _ignore, _ignore, actionId, remapped, modifiers, status = struct.unpack("!HBBHBB", mapped_data[:8]) else: actionId = remapped = modifiers = status = 0 actionId = special_keys.ACTIONID[actionId] if actionId == special_keys.ACTIONID.Key: remapped = special_keys.USB_HID_KEYCODES[remapped] elif actionId == special_keys.ACTIONID.Mouse: remapped = special_keys.MOUSE_BUTTONS[remapped] elif actionId == special_keys.ACTIONID.Hscroll: remapped = special_keys.HORIZONTAL_SCROLL[remapped] elif actionId == special_keys.ACTIONID.Consumer: remapped = special_keys.HID_CONSUMERCODES[remapped] elif actionId == special_keys.ACTIONID.Empty: # purge data from empty value remapped = modifiers = 0 self.keys[index] = PersistentRemappableAction( self.device, index, key, actionId, remapped, modifiers, status, ) elif logger.isEnabledFor(logging.WARNING): logger.warning(f"Key with index {index} was expected to exist but device doesn't report it.") class SubParam: __slots__ = ("id", "length", "minimum", "maximum", "widget") def __init__(self, id, length, minimum=None, maximum=None, widget=None): self.id = id self.length = length self.minimum = minimum if minimum is not None else 0 self.maximum = maximum if maximum is not None else ((1 << 8 * length) - 1) self.widget = widget if widget is not None else "Scale" def __str__(self): return self.id def __repr__(self): return self.id SUB_PARAM = { # (byte count, minimum, maximum) ParamId.EXTRA_CAPABILITIES: None, # ignore ParamId.PIXEL_ZONE: ( # TODO: replace min and max with the correct values SubParam("left", 2, 0x0000, 0xFFFF, "SpinButton"), SubParam("bottom", 2, 0x0000, 0xFFFF, "SpinButton"), SubParam("width", 2, 0x0000, 0xFFFF, "SpinButton"), SubParam("height", 2, 0x0000, 0xFFFF, "SpinButton"), ), ParamId.RATIO_ZONE: ( # TODO: replace min and max with the correct values SubParam("left", 1, 0x00, 0xFF, "SpinButton"), SubParam("bottom", 1, 0x00, 0xFF, "SpinButton"), SubParam("width", 1, 0x00, 0xFF, "SpinButton"), SubParam("height", 1, 0x00, 0xFF, "SpinButton"), ), ParamId.SCALE_FACTOR: (SubParam("scale", 2, 0x002E, 0x01FF, "Scale"),), } # Spec Ids for feature GESTURE_2 SPEC = common.NamedInts( DVI_field_width=1, field_widths=2, period_unit=3, resolution=4, multiplier=5, sensor_size=6, finger_width_and_height=7, finger_major_minor_axis=8, finger_force=9, zone=10, ) SPEC._fallback = lambda x: f"unknown:{x:04X}" # Action Ids for feature GESTURE_2 ACTION_ID = common.NamedInts( MovePointer=1, ScrollHorizontal=2, WheelScrolling=3, ScrollVertial=4, ScrollOrPageXY=5, ScrollOrPageHorizontal=6, PageScreen=7, Drag=8, SecondaryDrag=9, Zoom=10, ScrollHorizontalOnly=11, ScrollVerticalOnly=12, ) ACTION_ID._fallback = lambda x: f"unknown:{x:04X}" class Gesture: def __init__(self, device, low, high, next_index, next_diversion_index): self._device = device self.id = low self.gesture = GestureId(low) self.can_be_enabled = high & 0x01 self.can_be_diverted = high & 0x02 self.show_in_ui = high & 0x04 self.desired_software_default = high & 0x08 self.persistent = high & 0x10 self.default_enabled = high & 0x20 self.index = next_index if self.can_be_enabled or self.default_enabled else None self.diversion_index = next_diversion_index if self.can_be_diverted else None self._enabled = None self._diverted = None def _offset_mask(self, index): # offset and mask if index is not None: offset = index >> 3 # 8 gestures per byte mask = 0x1 << (index % 8) return offset, mask else: return None, None def enable_offset_mask(self): return self._offset_mask(self.index) def diversion_offset_mask(self): return self._offset_mask(self.diversion_index) def enabled(self): # is the gesture enabled? if self._enabled is None and self.index is not None: offset, mask = self.enable_offset_mask() result = self._device.feature_request(SupportedFeature.GESTURE_2, 0x10, offset, 0x01, mask) self._enabled = bool(result[0] & mask) if result else None return self._enabled def set(self, enable): # enable or disable the gesture if not self.can_be_enabled: return None if self.index is not None: offset, mask = self.enable_offset_mask() reply = self._device.feature_request( SupportedFeature.GESTURE_2, 0x20, offset, 0x01, mask, mask if enable else 0x00 ) return reply def diverted(self): # is the gesture diverted? if self._diverted is None and self.diversion_index is not None: offset, mask = self.diversion_offset_mask() result = self._device.feature_request(SupportedFeature.GESTURE_2, 0x30, offset, 0x01, mask) self._diverted = bool(result[0] & mask) if result else None return self._diverted def divert(self, diverted): # divert or undivert the gesture if not self.can_be_diverted: return None if self.diversion_index is not None: offset, mask = self.diversion_offset_mask() reply = self._device.feature_request( SupportedFeature.GESTURE_2, 0x40, offset, 0x01, mask, mask if diverted else 0x00, ) return reply def as_int(self): return self.gesture def __int__(self): return self.id def __repr__(self): return f"" # allow a gesture to be used as a settings reader/writer to enable and disable the gesture read = enabled write = set class Param: def __init__(self, device, low: int, high, next_param_index): self._device = device self.id = low self.param = ParamId(low) self.size = high & 0x0F self.show_in_ui = bool(high & 0x1F) self._value = None self._default_value = None self.index = next_param_index @property def sub_params(self): return SUB_PARAM.get(self.id, None) @property def value(self): return self._value if self._value is not None else self.read() def read(self): # returns the bytes for the parameter result = self._device.feature_request(SupportedFeature.GESTURE_2, 0x70, self.index, 0xFF) if result: self._value = common.bytes2int(result[: self.size]) return self._value @property def default_value(self): if self._default_value is None: self._default_value = self._read_default() return self._default_value def _read_default(self): result = self._device.feature_request(SupportedFeature.GESTURE_2, 0x60, self.index, 0xFF) if result: self._default_value = common.bytes2int(result[: self.size]) return self._default_value def write(self, bytes): self._value = bytes return self._device.feature_request(SupportedFeature.GESTURE_2, 0x80, self.index, bytes, 0xFF) def __str__(self): return str(self.param) def __int__(self): return self.id class Spec: def __init__(self, device, low, high): self._device = device self.id = low self.spec = SPEC[low] self.byte_count = high & 0x0F self._value = None @property def value(self): if self._value is None: self._value = self.read() return self._value def read(self): try: value = self._device.feature_request(SupportedFeature.GESTURE_2, 0x50, self.id, 0xFF) except exceptions.FeatureCallError: # some calls produce an error (notably spec 5 multiplier on K400Plus) if logger.isEnabledFor(logging.WARNING): logger.warning( f"Feature Call Error reading Gesture Spec on device {self._device} for spec {self.id} - use None" ) return None return common.bytes2int(value[: self.byte_count]) def __repr__(self): return f"[{self.spec}={self.value}]" class Gestures: """Information about the gestures that a device supports. Right now only some information fields are supported. WARNING: Assumes that parameters are always global, which is not the case. """ def __init__(self, device): self.device = device self.gestures = {} self.params = {} self.specs = {} index = 0 next_gesture_index = next_divsn_index = next_param_index = 0 field_high = 0x00 while field_high != 0x01: # end of fields # retrieve the next eight fields fields = device.feature_request(SupportedFeature.GESTURE_2, 0x00, index >> 8, index & 0xFF) if not fields: break for offset in range(8): field_high = fields[offset * 2] field_low = fields[offset * 2 + 1] if field_high == 0x1: # end of fields break elif field_high & 0x80: gesture = Gesture(device, field_low, field_high, next_gesture_index, next_divsn_index) next_gesture_index = next_gesture_index if gesture.index is None else next_gesture_index + 1 next_divsn_index = next_divsn_index if gesture.diversion_index is None else next_divsn_index + 1 self.gestures[gesture.gesture] = gesture elif field_high & 0xF0 == 0x30 or field_high & 0xF0 == 0x20: param = Param(device, field_low, field_high, next_param_index) next_param_index = next_param_index + 1 self.params[param.param] = param elif field_high == 0x04: if field_low != 0x00: logger.error(f"Unimplemented GESTURE_2 grouping {field_low} {field_high} found.") elif field_high & 0xF0 == 0x40: spec = Spec(device, field_low, field_high) self.specs[spec.spec] = spec else: logger.warning(f"Unimplemented GESTURE_2 field {field_low} {field_high} found.") index += 1 def gesture(self, gesture): return self.gestures.get(gesture, None) def gesture_enabled(self, gesture): # is the gesture enabled? g = self.gestures.get(gesture, None) return g.enabled() if g else None def enable_gesture(self, gesture): g = self.gestures.get(gesture, None) return g.set(True) if g else None def disable_gesture(self, gesture): g = self.gestures.get(gesture, None) return g.set(False) if g else None def param(self, param): return self.params.get(param, None) def get_param(self, param): g = self.params.get(param, None) return g.read() if g else None def set_param(self, param, value): g = self.params.get(param, None) return g.write(value) if g else None class Backlight: """Information about the current settings of x1982 Backlight2 v3, but also works for previous versions""" def __init__(self, device): response = device.feature_request(SupportedFeature.BACKLIGHT2, 0x00) if not response: raise exceptions.FeatureCallError(msg="No reply from device.") self.device = device self.enabled, self.options, supported, effects, self.level, self.dho, self.dhi, self.dpow = struct.unpack( "> 3) & 0x03 def write(self): self.options = (self.options & 0x07) | (self.mode << 3) level = self.level if self.mode == 0x3 else 0 data_bytes = struct.pack("> 4] if behavior == ButtonBehaviors.MacroExecute or behavior == ButtonBehaviors.MacroStop: sector = ((bytes[0] & 0x0F) << 8) + bytes[1] address = (bytes[2] << 8) + bytes[3] result = cls(behavior=behavior, sector=sector, address=address) elif behavior == ButtonBehaviors.Send: mapping_type = ButtonMappingTypes[bytes[1]] if mapping_type == ButtonMappingTypes.Button: value = ButtonButtons[(bytes[2] << 8) + bytes[3]] result = cls(behavior=behavior, type=mapping_type, value=value) elif mapping_type == ButtonMappingTypes.Modifier_And_Key: modifiers = bytes[2] value = ButtonKeys[bytes[3]] result = cls(behavior=behavior, type=mapping_type, modifiers=modifiers, value=value) elif mapping_type == ButtonMappingTypes.Consumer_Key: value = ButtonConsumerKeys[(bytes[2] << 8) + bytes[3]] result = cls(behavior=behavior, type=mapping_type, value=value) elif mapping_type == ButtonMappingTypes.No_Action: result = cls(behavior=behavior, type=mapping_type) elif behavior == ButtonBehaviors.Function: value = ButtonFunctions[bytes[1]] if ButtonFunctions[bytes[1]] is not None else bytes[1] data = bytes[3] result = cls(behavior=behavior, value=value, data=data) else: result = cls(behavior=bytes[0] >> 4, bytes=bytes) return result def to_bytes(self): bytes = common.int2bytes(self.behavior << 4, 1) if self.behavior is not None else None if self.behavior == ButtonBehaviors.MacroExecute or self.behavior == ButtonBehaviors.MacroStop: bytes = common.int2bytes((self.behavior << 12) + self.sector, 2) + common.int2bytes(self.address, 2) elif self.behavior == ButtonBehaviors.Send: bytes += common.int2bytes(self.type, 1) if self.type == ButtonMappingTypes.Button: bytes += common.int2bytes(self.value, 2) elif self.type == ButtonMappingTypes.Modifier_And_Key: bytes += common.int2bytes(self.modifiers, 1) bytes += common.int2bytes(self.value, 1) elif self.type == ButtonMappingTypes.Consumer_Key: bytes += common.int2bytes(self.value, 2) elif self.type == ButtonMappingTypes.No_Action: bytes += b"\xff\xff" elif self.behavior == ButtonBehaviors.Function: data = common.int2bytes(self.data, 1) if self.data else b"\x00" bytes += common.int2bytes(self.value, 1) + b"\xff" + data else: bytes = self.bytes if self.bytes else b"\xff\xff\xff\xff" return bytes def __repr__(self): return "%s{%s}" % ( self.__class__.__name__, ", ".join([str(key) + ":" + str(val) for key, val in self.__dict__.items()]), ) yaml.SafeLoader.add_constructor("!Button", Button.from_yaml) yaml.add_representer(Button, Button.to_yaml) class OnboardProfile: """A single onboard profile""" def __init__(self, **kwargs): for key, val in kwargs.items(): setattr(self, key, val) @classmethod def from_yaml(cls, loader, node): args = loader.construct_mapping(node) return cls(**args) @classmethod def to_yaml(cls, dumper, data): return dumper.represent_mapping("!OnboardProfile", data.__dict__) @classmethod def from_bytes(cls, sector, enabled, buttons, gbuttons, bytes): return cls( sector=sector, enabled=enabled, report_rate=bytes[0], resolution_default_index=bytes[1], resolution_shift_index=bytes[2], resolutions=[struct.unpack(" 0x04: return count, oob, buttons, sectors, size, shift = struct.unpack("!BBBBHB", response[3:10]) gbuttons = buttons if (shift & 0x3 == 0x2) else 0 headers = OnboardProfiles.get_profile_headers(device) profiles = {} i = 0 for sector, enabled in headers: profiles[i + 1] = OnboardProfile.from_dev(device, i, sector, size, enabled, buttons, gbuttons) i += 1 return cls( version=OnboardProfilesVersion, name=device.name, count=count, buttons=buttons, gbuttons=gbuttons, sectors=sectors, size=size, profiles=profiles, ) def to_bytes(self): bytes = b"" for i in range(1, len(self.profiles) + 1): profiles_sector = common.int2bytes(self.profiles[i].sector, 2) profiles_enabled = common.int2bytes(self.profiles[i].enabled, 1) bytes += profiles_sector + profiles_enabled + b"\x00" bytes += b"\xff\xff\x00\x00" # marker after last profile while len(bytes) < self.size - 2: # leave room for CRC bytes += b"\xff" bytes += common.int2bytes(common.crc16(bytes), 2) return bytes @classmethod def read_sector(cls, dev, sector, s): # doesn't check for valid sector or size bytes = b"" o = 0 while o < s - 15: chunk = dev.feature_request(SupportedFeature.ONBOARD_PROFILES, 0x50, sector >> 8, sector & 0xFF, o >> 8, o & 0xFF) bytes += chunk o += 16 chunk = dev.feature_request( SupportedFeature.ONBOARD_PROFILES, 0x50, sector >> 8, sector & 0xFF, (s - 16) >> 8, (s - 16) & 0xFF, ) bytes += chunk[16 + o - s :] # the last chunk has to be read in an awkward way return bytes @classmethod def write_sector(cls, device, s, bs): # doesn't check for valid sector or size rbs = OnboardProfiles.read_sector(device, s, len(bs)) if rbs[:-2] == bs[:-2]: return False device.feature_request(SupportedFeature.ONBOARD_PROFILES, 0x60, s >> 8, s & 0xFF, 0, 0, len(bs) >> 8, len(bs) & 0xFF) o = 0 while o < len(bs) - 1: device.feature_request(SupportedFeature.ONBOARD_PROFILES, 0x70, bs[o : o + 16]) o += 16 device.feature_request(SupportedFeature.ONBOARD_PROFILES, 0x80) return True def write(self, device): try: written = 1 if OnboardProfiles.write_sector(device, 0, self.to_bytes()) else 0 except Exception as e: logger.warning("Exception writing onboard profile control sector") raise e for p in self.profiles.values(): try: if p.sector >= self.sectors: raise Exception(f"Sector {p.sector} not a writable sector") written += 1 if OnboardProfiles.write_sector(device, p.sector, p.to_bytes(self.size)) else 0 except Exception as e: logger.warning(f"Exception writing onboard profile sector {p.sector}") raise e return written def show(self): print(yaml.dump(self)) yaml.SafeLoader.add_constructor("!OnboardProfiles", OnboardProfiles.from_yaml) yaml.add_representer(OnboardProfiles, OnboardProfiles.to_yaml) def feature_request(device, feature, function=0x00, *params, no_reply=False): if device.online and device.features: if feature in device.features: feature_index = device.features[feature] return device.request((feature_index << 8) + (function & 0xFF), *params, no_reply=no_reply) # voltage to remaining charge from Logitech battery_voltage_remaining = ( (4186, 100), (4067, 90), (3989, 80), (3922, 70), (3859, 60), (3811, 50), (3778, 40), (3751, 30), (3717, 20), (3671, 10), (3646, 5), (3579, 2), (3500, 0), (-1000, 0), ) class Hidpp20: def get_firmware(self, device) -> tuple[common.FirmwareInfo] | None: """Reads a device's firmware info. :returns: a list of FirmwareInfo tuples, ordered by firmware layer. """ count = device.feature_request(SupportedFeature.DEVICE_FW_VERSION) if count: count = ord(count[:1]) fw = [] for index in range(0, count): fw_info = device.feature_request(SupportedFeature.DEVICE_FW_VERSION, 0x10, index) if fw_info: level = ord(fw_info[:1]) & 0x0F if level == 0 or level == 1: name, version_major, version_minor, build = struct.unpack("!3sBBH", fw_info[1:8]) version = f"{version_major:02X}.{version_minor:02X}" if build: version += f".B{build:04X}" extras = fw_info[9:].rstrip(b"\x00") or None fw_info = common.FirmwareInfo(FirmwareKind(level), name.decode("ascii"), version, extras) elif level == FirmwareKind.Hardware: fw_info = common.FirmwareInfo(FirmwareKind.Hardware, "", str(ord(fw_info[1:2])), None) else: fw_info = common.FirmwareInfo(FirmwareKind.Other, "", "", None) fw.append(fw_info) return tuple(fw) def get_ids(self, device): """Reads a device's ids (unit and model numbers)""" ids = device.feature_request(SupportedFeature.DEVICE_FW_VERSION) if ids: unitId = ids[1:5] modelId = ids[7:13] transport_bits = ord(ids[6:7]) offset = 0 tid_map = {} for transport, flag in [("btid", 0x1), ("btleid", 0x02), ("wpid", 0x04), ("usbid", 0x08)]: if transport_bits & flag: tid_map[transport] = modelId[offset : offset + 2].hex().upper() offset = offset + 2 return unitId.hex().upper(), modelId.hex().upper(), tid_map def get_kind(self, device: Device): """Reads a device's type. :see DEVICE_KIND: :returns: a string describing the device type, or ``None`` if the device is not available or does not support the ``DEVICE_NAME`` feature. """ kind = device.feature_request(SupportedFeature.DEVICE_NAME, 0x20) if kind: kind = ord(kind[:1]) try: return KIND_MAP[DEVICE_KIND[kind]] except Exception: return None def get_name(self, device: Device): """Reads a device's name. :returns: a string with the device name, or ``None`` if the device is not available or does not support the ``DEVICE_NAME`` feature. """ name_length = device.feature_request(SupportedFeature.DEVICE_NAME) if name_length: name_length = ord(name_length[:1]) name = b"" while len(name) < name_length: fragment = device.feature_request(SupportedFeature.DEVICE_NAME, 0x10, len(name)) if fragment: name += fragment[: name_length - len(name)] else: logger.error("failed to read whole name of %s (expected %d chars)", device, name_length) return None return name.decode("utf-8") def get_friendly_name(self, device: Device): """Reads a device's friendly name. :returns: a string with the device name, or ``None`` if the device is not available or does not support the ``DEVICE_NAME`` feature. """ name_length = device.feature_request(SupportedFeature.DEVICE_FRIENDLY_NAME) if name_length: name_length = ord(name_length[:1]) name = b"" while len(name) < name_length: fragment = device.feature_request(SupportedFeature.DEVICE_FRIENDLY_NAME, 0x10, len(name)) if fragment: name += fragment[1 : name_length - len(name) + 1] else: logger.error("failed to read whole name of %s (expected %d chars)", device, name_length) return None return name.decode("utf-8") def get_battery_status(self, device: Device): report = device.feature_request(SupportedFeature.BATTERY_STATUS) if report: return decipher_battery_status(report) def get_battery_unified(self, device: Device): report = device.feature_request(SupportedFeature.UNIFIED_BATTERY, 0x10) if report is not None: return decipher_battery_unified(report) def get_battery_voltage(self, device: Device): report = device.feature_request(SupportedFeature.BATTERY_VOLTAGE) if report is not None: return decipher_battery_voltage(report) def get_adc_measurement(self, device: Device): try: # this feature call produces an error for headsets that are connected but inactive report = device.feature_request(SupportedFeature.ADC_MEASUREMENT) if report is not None: return decipher_adc_measurement(report) except exceptions.FeatureCallError: return SupportedFeature.ADC_MEASUREMENT if SupportedFeature.ADC_MEASUREMENT in device.features else None def get_battery(self, device, feature): """Return battery information - feature, approximate level, next, charging, voltage or battery feature if there is one but it is not responding or None for no battery feature""" if feature is not None: battery_function = battery_functions.get(feature, None) if battery_function: result = battery_function(self, device) if result: return result else: for battery_function in battery_functions.values(): result = battery_function(self, device) if result: return result return 0 def get_keys(self, device: Device): # TODO: add here additional variants for other REPROG_CONTROLS count = None if SupportedFeature.REPROG_CONTROLS_V2 in device.features: count = device.feature_request(SupportedFeature.REPROG_CONTROLS_V2) return KeysArrayV2(device, ord(count[:1])) elif SupportedFeature.REPROG_CONTROLS_V4 in device.features: count = device.feature_request(SupportedFeature.REPROG_CONTROLS_V4) return KeysArrayV4(device, ord(count[:1])) return None def get_remap_keys(self, device: Device): count = device.feature_request(SupportedFeature.PERSISTENT_REMAPPABLE_ACTION, 0x10) if count: return KeysArrayPersistent(device, ord(count[:1])) def get_gestures(self, device: Device): if getattr(device, "_gestures", None) is not None: return device._gestures if SupportedFeature.GESTURE_2 in device.features: return Gestures(device) def get_backlight(self, device: Device): if getattr(device, "_backlight", None) is not None: return device._backlight if SupportedFeature.BACKLIGHT2 in device.features: return Backlight(device) def get_profiles(self, device: Device): if getattr(device, "_profiles", None) is not None: return device._profiles if SupportedFeature.ONBOARD_PROFILES in device.features: return OnboardProfiles.from_device(device) def get_mouse_pointer_info(self, device: Device): pointer_info = device.feature_request(SupportedFeature.MOUSE_POINTER) if pointer_info: dpi, flags = struct.unpack("!HB", pointer_info[:3]) acceleration = ("none", "low", "med", "high")[flags & 0x3] suggest_os_ballistics = (flags & 0x04) != 0 suggest_vertical_orientation = (flags & 0x08) != 0 return { "dpi": dpi, "acceleration": acceleration, "suggest_os_ballistics": suggest_os_ballistics, "suggest_vertical_orientation": suggest_vertical_orientation, } def get_vertical_scrolling_info(self, device: Device): vertical_scrolling_info = device.feature_request(SupportedFeature.VERTICAL_SCROLLING) if vertical_scrolling_info: roller, ratchet, lines = struct.unpack("!BBB", vertical_scrolling_info[:3]) roller_type = ( "reserved", "standard", "reserved", "3G", "micro", "normal touch pad", "inverted touch pad", "reserved", )[roller] return {"roller": roller_type, "ratchet": ratchet, "lines": lines} def get_hi_res_scrolling_info(self, device: Device): hi_res_scrolling_info = device.feature_request(SupportedFeature.HI_RES_SCROLLING) if hi_res_scrolling_info: mode, resolution = struct.unpack("!BB", hi_res_scrolling_info[:2]) return mode, resolution def get_pointer_speed_info(self, device: Device): pointer_speed_info = device.feature_request(SupportedFeature.POINTER_SPEED) if pointer_speed_info: pointer_speed_hi, pointer_speed_lo = struct.unpack("!BB", pointer_speed_info[:2]) # if pointer_speed_lo > 0: # pointer_speed_lo = pointer_speed_lo return pointer_speed_hi + pointer_speed_lo / 256 def get_lowres_wheel_status(self, device: Device): lowres_wheel_status = device.feature_request(SupportedFeature.LOWRES_WHEEL) if lowres_wheel_status: wheel_flag = struct.unpack("!B", lowres_wheel_status[:1])[0] wheel_reporting = ("HID", "HID++")[wheel_flag & 0x01] return wheel_reporting def get_hires_wheel(self, device: Device): caps = device.feature_request(SupportedFeature.HIRES_WHEEL, 0x00) mode = device.feature_request(SupportedFeature.HIRES_WHEEL, 0x10) ratchet = device.feature_request(SupportedFeature.HIRES_WHEEL, 0x030) if caps and mode and ratchet: # Parse caps multi, flags = struct.unpack("!BB", caps[:2]) has_invert = (flags & 0x08) != 0 has_ratchet = (flags & 0x04) != 0 # Parse mode wheel_mode, reserved = struct.unpack("!BB", mode[:2]) target = (wheel_mode & 0x01) != 0 res = (wheel_mode & 0x02) != 0 inv = (wheel_mode & 0x04) != 0 # Parse Ratchet switch ratchet_mode, reserved = struct.unpack("!BB", ratchet[:2]) ratchet = (ratchet_mode & 0x01) != 0 return multi, has_invert, has_ratchet, inv, res, target, ratchet def get_new_fn_inversion(self, device: Device): state = device.feature_request(SupportedFeature.NEW_FN_INVERSION, 0x00) if state: inverted, default_inverted = struct.unpack("!BB", state[:2]) inverted = (inverted & 0x01) != 0 default_inverted = (default_inverted & 0x01) != 0 return inverted, default_inverted def get_host_names(self, device: Device): state = device.feature_request(SupportedFeature.HOSTS_INFO, 0x00) host_names = {} if state: capability_flags, _ignore, numHosts, currentHost = struct.unpack("!BBBB", state[:4]) if capability_flags & 0x01: # device can get host names for host in range(0, numHosts): hostinfo = device.feature_request(SupportedFeature.HOSTS_INFO, 0x10, host) _ignore, status, _ignore, _ignore, nameLen, _ignore = struct.unpack("!BBBBBB", hostinfo[:6]) name = "" remaining = nameLen while remaining > 0: name_piece = device.feature_request(SupportedFeature.HOSTS_INFO, 0x30, host, nameLen - remaining) if name_piece: name += name_piece[2 : 2 + min(remaining, 14)].decode() remaining = max(0, remaining - 14) else: remaining = 0 host_names[host] = (bool(status), name) if host_names: # update the current host's name if it doesn't match the system name hostname = socket.gethostname().partition(".")[0] if host_names[currentHost][1] != hostname: self.set_host_name(device, hostname, host_names[currentHost][1]) host_names[currentHost] = (host_names[currentHost][0], hostname) return host_names def set_host_name(self, device: Device, name, currentName=""): name = bytearray(name, "utf-8") currentName = bytearray(currentName, "utf-8") if logger.isEnabledFor(logging.INFO): logger.info("Setting host name to %s", name) state = device.feature_request(SupportedFeature.HOSTS_INFO, 0x00) if state: flags, _ignore, _ignore, currentHost = struct.unpack("!BBBB", state[:4]) if flags & 0x02: hostinfo = device.feature_request(SupportedFeature.HOSTS_INFO, 0x10, currentHost) _ignore, _ignore, _ignore, _ignore, _ignore, maxNameLen = struct.unpack("!BBBBBB", hostinfo[:6]) if name[:maxNameLen] == currentName[:maxNameLen] and False: return True length = min(maxNameLen, len(name)) chunk = 0 while chunk < length: response = device.feature_request( SupportedFeature.HOSTS_INFO, 0x40, currentHost, chunk, name[chunk : chunk + 14] ) if not response: return False chunk += 14 return True def get_onboard_mode(self, device: Device): state = device.feature_request(SupportedFeature.ONBOARD_PROFILES, 0x20) if state: mode = struct.unpack("!B", state[:1])[0] return mode def set_onboard_mode(self, device: Device, mode): state = device.feature_request(SupportedFeature.ONBOARD_PROFILES, 0x10, mode) return state def get_polling_rate(self, device: Device): state = device.feature_request(SupportedFeature.REPORT_RATE, 0x10) if state: rate = struct.unpack("!B", state[:1])[0] return str(rate) + "ms" else: rates = ["8ms", "4ms", "2ms", "1ms", "500us", "250us", "125us"] state = device.feature_request(SupportedFeature.EXTENDED_ADJUSTABLE_REPORT_RATE, 0x20) if state: rate = struct.unpack("!B", state[:1])[0] return rates[rate] def get_remaining_pairing(self, device: Device): result = device.feature_request(SupportedFeature.REMAINING_PAIRING, 0x0) if result: result = struct.unpack("!B", result[:1])[0] SupportedFeature._fallback = lambda x: f"unknown:{x:04X}" return result def config_change(self, device: Device, configuration, no_reply=False): return device.feature_request(SupportedFeature.CONFIG_CHANGE, 0x10, configuration, no_reply=no_reply) battery_functions = { SupportedFeature.BATTERY_STATUS: Hidpp20.get_battery_status, SupportedFeature.BATTERY_VOLTAGE: Hidpp20.get_battery_voltage, SupportedFeature.UNIFIED_BATTERY: Hidpp20.get_battery_unified, SupportedFeature.ADC_MEASUREMENT: Hidpp20.get_adc_measurement, } def decipher_battery_status(report: FixedBytes5) -> Tuple[Any, Battery]: battery_discharge_level, battery_discharge_next_level, battery_status = struct.unpack("!BBB", report[:3]) if battery_discharge_level == 0: battery_discharge_level = None try: status = BatteryStatus(battery_status) except ValueError: status = None logger.debug(f"Unknown battery status byte 0x{battery_status:02X}") if logger.isEnabledFor(logging.DEBUG): logger.debug( "battery status %s%% charged, next %s%%, status %s", battery_discharge_level, battery_discharge_next_level, status ) return SupportedFeature.BATTERY_STATUS, Battery(battery_discharge_level, battery_discharge_next_level, status, None) def decipher_battery_voltage(report: bytes): voltage, flags = struct.unpack(">HB", report[:3]) status = BatteryStatus.DISCHARGING charge_sts = ErrorCode.UNKNOWN charge_lvl = ChargeLevel.AVERAGE charge_type = ChargeType.STANDARD if flags & (1 << 7): status = BatteryStatus.RECHARGING charge_sts = CHARGE_STATUS[flags & 0x03] if charge_sts is None: charge_sts = ErrorCode.UNKNOWN elif charge_sts == CHARGE_STATUS.full: charge_lvl = ChargeLevel.FULL status = BatteryStatus.FULL if flags & (1 << 3): charge_type = ChargeType.FAST elif flags & (1 << 4): charge_type = ChargeType.SLOW status = BatteryStatus.SLOW_RECHARGE elif flags & (1 << 5): charge_lvl = ChargeLevel.CRITICAL for level in battery_voltage_remaining: if level[0] < voltage: charge_lvl = level[1] break if logger.isEnabledFor(logging.DEBUG): logger.debug( "battery voltage %d mV, charging %s, status %d = %s, level %s, type %s", voltage, status, (flags & 0x03), charge_sts, charge_lvl, charge_type, ) return SupportedFeature.BATTERY_VOLTAGE, Battery(charge_lvl, None, status, voltage) def decipher_battery_unified(report): discharge, level, status_byte, _ignore = struct.unpack("!BBBB", report[:4]) try: status = BatteryStatus(status_byte) except ValueError: status = None logger.debug(f"Unknown battery status byte 0x{status_byte:02X}") if logger.isEnabledFor(logging.DEBUG): logger.debug("battery unified %s%% charged, level %s, charging %s", discharge, level, status) if level == 8: level = BatteryLevelApproximation.FULL elif level == 4: level = BatteryLevelApproximation.GOOD elif level == 2: level = BatteryLevelApproximation.LOW elif level == 1: level = BatteryLevelApproximation.CRITICAL else: level = BatteryLevelApproximation.EMPTY return SupportedFeature.UNIFIED_BATTERY, Battery(discharge if discharge else level, None, status, None) def decipher_adc_measurement(report): # partial implementation - needs mapping to levels charge_level = None adc, flags = struct.unpack("!HB", report[:3]) for level in battery_voltage_remaining: if level[0] < adc: charge_level = level[1] break if flags & 0x01: status = BatteryStatus.RECHARGING if flags & 0x02 else BatteryStatus.DISCHARGING return SupportedFeature.ADC_MEASUREMENT, Battery(charge_level, None, status, adc) Solaar-1.1.14/lib/logitech_receiver/hidpp20_constants.py000066400000000000000000000204701473525213700231770ustar00rootroot00000000000000## Copyright (C) 2012-2013 Daniel Pavel ## Copyright (C) 2014-2024 Solaar Contributors https://pwr-solaar.github.io/Solaar/ ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. from enum import IntEnum from enum import IntFlag from .common import NamedInts # /,/p}' | sort -t= -k2 # additional features names taken from https://github.com/cvuchener/hidpp and # https://github.com/Logitech/cpg-docs/tree/master/hidpp20 """Possible features available on a Logitech device. A particular device might not support all these features, and may support other unknown features as well. """ class SupportedFeature(IntEnum): ROOT = 0x0000 FEATURE_SET = 0x0001 FEATURE_INFO = 0x0002 # Common DEVICE_FW_VERSION = 0x0003 DEVICE_UNIT_ID = 0x0004 DEVICE_NAME = 0x0005 DEVICE_GROUPS = 0x0006 DEVICE_FRIENDLY_NAME = 0x0007 KEEP_ALIVE = 0x0008 CONFIG_CHANGE = 0x0020 CRYPTO_ID = 0x0021 TARGET_SOFTWARE = 0x0030 WIRELESS_SIGNAL_STRENGTH = 0x0080 DFUCONTROL_LEGACY = 0x00C0 DFUCONTROL_UNSIGNED = 0x00C1 DFUCONTROL_SIGNED = 0x00C2 DFUCONTROL = 0x00C3 DFU = 0x00D0 BATTERY_STATUS = 0x1000 BATTERY_VOLTAGE = 0x1001 UNIFIED_BATTERY = 0x1004 CHARGING_CONTROL = 0x1010 LED_CONTROL = 0x1300 FORCE_PAIRING = 0x1500 GENERIC_TEST = 0x1800 DEVICE_RESET = 0x1802 OOBSTATE = 0x1805 CONFIG_DEVICE_PROPS = 0x1806 CHANGE_HOST = 0x1814 HOSTS_INFO = 0x1815 BACKLIGHT = 0x1981 BACKLIGHT2 = 0x1982 BACKLIGHT3 = 0x1983 ILLUMINATION = 0x1990 PRESENTER_CONTROL = 0x1A00 SENSOR_3D = 0x1A01 REPROG_CONTROLS = 0x1B00 REPROG_CONTROLS_V2 = 0x1B01 REPROG_CONTROLS_V2_2 = 0x1B02 # LogiOptions 2.10.73 features.xml REPROG_CONTROLS_V3 = 0x1B03 REPROG_CONTROLS_V4 = 0x1B04 REPORT_HID_USAGE = 0x1BC0 PERSISTENT_REMAPPABLE_ACTION = 0x1C00 WIRELESS_DEVICE_STATUS = 0x1D4B REMAINING_PAIRING = 0x1DF0 FIRMWARE_PROPERTIES = 0x1F1F ADC_MEASUREMENT = 0x1F20 # Mouse LEFT_RIGHT_SWAP = 0x2001 SWAP_BUTTON_CANCEL = 0x2005 POINTER_AXIS_ORIENTATION = 0x2006 VERTICAL_SCROLLING = 0x2100 SMART_SHIFT = 0x2110 SMART_SHIFT_ENHANCED = 0x2111 HI_RES_SCROLLING = 0x2120 HIRES_WHEEL = 0x2121 LOWRES_WHEEL = 0x2130 THUMB_WHEEL = 0x2150 MOUSE_POINTER = 0x2200 ADJUSTABLE_DPI = 0x2201 EXTENDED_ADJUSTABLE_DPI = 0x2202 POINTER_SPEED = 0x2205 ANGLE_SNAPPING = 0x2230 SURFACE_TUNING = 0x2240 XY_STATS = 0x2250 WHEEL_STATS = 0x2251 HYBRID_TRACKING = 0x2400 # Keyboard FN_INVERSION = 0x40A0 NEW_FN_INVERSION = 0x40A2 K375S_FN_INVERSION = 0x40A3 ENCRYPTION = 0x4100 LOCK_KEY_STATE = 0x4220 SOLAR_DASHBOARD = 0x4301 KEYBOARD_LAYOUT = 0x4520 KEYBOARD_DISABLE_KEYS = 0x4521 KEYBOARD_DISABLE_BY_USAGE = 0x4522 DUALPLATFORM = 0x4530 MULTIPLATFORM = 0x4531 KEYBOARD_LAYOUT_2 = 0x4540 CROWN = 0x4600 # Touchpad TOUCHPAD_FW_ITEMS = 0x6010 TOUCHPAD_SW_ITEMS = 0x6011 TOUCHPAD_WIN8_FW_ITEMS = 0x6012 TAP_ENABLE = 0x6020 TAP_ENABLE_EXTENDED = 0x6021 CURSOR_BALLISTIC = 0x6030 TOUCHPAD_RESOLUTION = 0x6040 TOUCHPAD_RAW_XY = 0x6100 TOUCHMOUSE_RAW_POINTS = 0x6110 TOUCHMOUSE_6120 = 0x6120 GESTURE = 0x6500 GESTURE_2 = 0x6501 # Gaming Devices GKEY = 0x8010 MKEYS = 0x8020 MR = 0x8030 BRIGHTNESS_CONTROL = 0x8040 REPORT_RATE = 0x8060 EXTENDED_ADJUSTABLE_REPORT_RATE = 0x8061 COLOR_LED_EFFECTS = 0x8070 RGB_EFFECTS = 0x8071 PER_KEY_LIGHTING = 0x8080 PER_KEY_LIGHTING_V2 = 0x8081 MODE_STATUS = 0x8090 ONBOARD_PROFILES = 0x8100 MOUSE_BUTTON_SPY = 0x8110 LATENCY_MONITORING = 0x8111 GAMING_ATTACHMENTS = 0x8120 FORCE_FEEDBACK = 0x8123 # Headsets SIDETONE = 0x8300 EQUALIZER = 0x8310 HEADSET_OUT = 0x8320 # Fake features for Solaar internal use MOUSE_GESTURE = 0xFE00 def __str__(self): return self.name.replace("_", " ") class FeatureFlag(IntFlag): """Single bit flags.""" INTERNAL = 0x20 HIDDEN = 0x40 OBSOLETE = 0x80 DEVICE_KIND = NamedInts( keyboard=0x00, remote_control=0x01, numpad=0x02, mouse=0x03, touchpad=0x04, trackball=0x05, presenter=0x06, receiver=0x07, ) class OnboardMode(IntEnum): MODE_NO_CHANGE = 0x00 MODE_ONBOARD = 0x01 MODE_HOST = 0x02 CHARGE_STATUS = NamedInts(charging=0x00, full=0x01, not_charging=0x02, error=0x07) class ChargeStatus(IntEnum): CHARGING = 0x00 FULL = 0x01 NOT_CHARGING = 0x02 ERROR = 0x07 class ChargeLevel(IntEnum): AVERAGE = 50 FULL = 90 CRITICAL = 5 class ChargeType(IntEnum): STANDARD = 0x00 FAST = 0x01 SLOW = 0x02 class ErrorCode(IntEnum): UNKNOWN = 0x01 INVALID_ARGUMENT = 0x02 OUT_OF_RANGE = 0x03 HARDWARE_ERROR = 0x04 LOGITECH_ERROR = 0x05 INVALID_FEATURE_INDEX = 0x06 INVALID_FUNCTION = 0x07 BUSY = 0x08 UNSUPPORTED = 0x09 class GestureId(IntEnum): """Gesture IDs for feature GESTURE_2.""" TAP_1_FINGER = 1 # task Left_Click TAP_2_FINGER = 2 # task Right_Click TAP_3_FINGER = 3 CLICK_1_FINGER = 4 # task Left_Click CLICK_2_FINGER = 5 # task Right_Click CLICK_3_FINGER = 6 DOUBLE_TAP_1_FINGER = 10 DOUBLE_TAP_2_FINGER = 11 DOUBLE_TAP_3_FINGER = 12 TRACK_1_FINGER = 20 # action MovePointer TRACKING_ACCELERATION = 21 TAP_DRAG_1_FINGER = 30 # action Drag TAP_DRAG_2_FINGER = 31 # action SecondaryDrag DRAG_3_FINGER = 32 TAP_GESTURES = 33 # group all tap gestures under a single UI setting FN_CLICK_GESTURE_SUPPRESSION = 34 # suppresses Tap and Edge gestures, toggled by Fn+Click SCROLL_1_FINGER = 40 # action ScrollOrPageXY / ScrollHorizontal SCROLL_2_FINGER = 41 # action ScrollOrPageXY / ScrollHorizontal SCROLL_2_FINGER_HORIZONTAL = 42 # action ScrollHorizontal SCROLL_2_FINGER_VERTICAL = 43 # action WheelScrolling SCROLL_2_FINGER_STATELESS = 44 NATURAL_SCROLLING = 45 # affects native HID wheel reporting by gestures, not when diverted THUMBWHEEL = (46,) # action WheelScrolling V_SCROLL_INTERTIA = 48 V_SCROLL_BALLISTICS = 49 SWIPE_2_FINGER_HORIZONTAL = 50 # action PageScreen SWIPE_3_FINGER_HORIZONTAL = 51 # action PageScreen SWIPE_4_FINGER_HORIZONTAL = 52 # action PageScreen SWIPE_3_FINGER_VERTICAL = 53 SWIPE_4_FINGER_VERTICAL = 54 LEFT_EDGE_SWIPE_1_FINGER = 60 RIGHT_EDGE_SWIPE_1_FINGER = 61 BOTTOM_EDGE_SWIPE_1_FINGER = 62 TOP_EDGE_SWIPE_1_FINGER = 63 LEFT_EDGE_SWIPE_1_FINGER_2 = 64 # task HorzScrollNoRepeatSet RIGHT_EDGE_SWIPE_1_FINGER_2 = 65 BOTTOM_EDGE_SWIPE_1_FINGER_2 = 66 TOP_EDGE_SWIPE_1_FINGER_2 = 67 LEFT_EDGE_SWIPE_2_FINGER = 70 RIGHT_EDGE_SWIPE_2_FINGER = 71 BottomEdgeSwipe2Finger = 72 BOTTOM_EDGE_SWIPE_2_FINGER = 72 TOP_EDGE_SWIPE_2_FINGER = 73 ZOOM_2_FINGER = 80 # action Zoom ZOOM_2_FINGER_PINCH = 81 # ZoomBtnInSet ZOOM_2_FINGER_SPREAD = 82 # ZoomBtnOutSet ZOOM_3_FINGER = 83 ZOOM_2_FINGER_STATELESS = 84 TWO_FINGERS_PRESENT = 85 ROTATE_2_FINGER = 87 FINGER_1 = 90 FINGER_2 = 91 FINGER_3 = 92 FINGER_4 = 93 FINGER_5 = 94 FINGER_6 = 95 FINGER_7 = 96 FINGER_8 = 97 FINGER_9 = 98 FINGER_10 = 99 DEVICE_SPECIFIC_RAW_DATA = 100 class ParamId(IntEnum): """Param Ids for feature GESTURE_2""" EXTRA_CAPABILITIES = 1 # not suitable for use PIXEL_ZONE = 2 # 4 2-byte integers, left, bottom, width, height; pixels RATIO_ZONE = 3 # 4 bytes, left, bottom, width, height; unit 1/240 pad size SCALE_FACTOR = 4 # 2-byte integer, with 256 as normal scale Solaar-1.1.14/lib/logitech_receiver/i18n.py000066400000000000000000000042441473525213700204150ustar00rootroot00000000000000## Copyright (C) 2012-2013 Daniel Pavel ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # Translation support for the Logitech receivers library import gettext _ = gettext.gettext ngettext = gettext.ngettext # A few common strings, not always accessible as such in the code. _DUMMY = ( # approximative battery levels _("empty"), _("critical"), _("low"), _("average"), _("good"), _("full"), # battery charging statuses _("discharging"), _("recharging"), _("charging"), _("not charging"), _("almost full"), _("charged"), _("slow recharge"), _("invalid battery"), _("thermal error"), _("error"), _("standard"), _("fast"), _("slow"), # pairing errors _("device timeout"), _("device not supported"), _("too many devices"), _("sequence timeout"), # firmware kinds _("Firmware"), _("Bootloader"), _("Hardware"), _("Other"), # common button and task names (from special_keys.py) _("Left Button"), _("Right Button"), _("Middle Button"), _("Back Button"), _("Forward Button"), _("Mouse Gesture Button"), _("Smart Shift"), _("DPI Switch"), _("Left Tilt"), _("Right Tilt"), _("Left Click"), _("Right Click"), _("Mouse Middle Button"), _("Mouse Back Button"), _("Mouse Forward Button"), _("Gesture Button Navigation"), _("Mouse Scroll Left Button"), _("Mouse Scroll Right Button"), # key/button statuses _("pressed"), _("released"), ) Solaar-1.1.14/lib/logitech_receiver/listener.py000066400000000000000000000146301473525213700214630ustar00rootroot00000000000000## Copyright (C) 2012-2013 Daniel Pavel ## Copyright (C) 2014-2024 Solaar Contributors https://pwr-solaar.github.io/Solaar/ ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import logging import queue import threading from . import base from . import exceptions logger = logging.getLogger(__name__) class _ThreadedHandle: """A thread-local wrapper with different open handles for each thread. Closing a ThreadedHandle will close all handles. """ __slots__ = ("path", "_local", "_handles", "_listener") def __init__(self, listener, path, handle): assert listener is not None assert path is not None assert handle is not None assert isinstance(handle, int) self._listener = listener self.path = path self._local = threading.local() # take over the current handle for the thread doing the replacement self._local.handle = handle self._handles = [handle] def _open(self): handle = base.open_path(self.path) if handle is None: logger.error("%r failed to open new handle", self) else: # if logger.isEnabledFor(logging.DEBUG): # logger.debug("%r opened new handle %d", self, handle) self._local.handle = handle self._handles.append(handle) return handle def close(self): if self._local: self._local = None handles, self._handles = self._handles, [] if logger.isEnabledFor(logging.DEBUG): logger.debug("%r closing %s", self, handles) for h in handles: base.close(h) @property def notifications_hook(self): if self._listener: assert isinstance(self._listener, threading.Thread) if threading.current_thread() == self._listener: return self._listener._notifications_hook def __del__(self): self._listener = None self.close() def __index__(self): if self._local: try: return self._local.handle except Exception: return self._open() else: return -1 __int__ = __index__ def __str__(self): if self._local: return str(int(self)) def __repr__(self): return f"<_ThreadedHandle({self.path})>" def __bool__(self): return bool(self._local) __nonzero__ = __bool__ # How long to wait during a read for the next packet, in seconds. # Ideally this should be rather long (10s ?), but the read is blocking and this means that when the thread # is signalled to stop, it would take a while for it to acknowledge it. # Forcibly closing the file handle on another thread does _not_ interrupt the read on Linux systems. _EVENT_READ_TIMEOUT = 1.0 # in seconds class EventsListener(threading.Thread): """Listener thread for notifications from the Unifying Receiver. Incoming packets will be passed to the callback function in sequence. """ def __init__(self, receiver, notifications_callback): try: path_name = receiver.path.split("/")[2] except IndexError: path_name = receiver.path super().__init__(name=self.__class__.__name__ + ":" + path_name) self.daemon = True self._active = False self.receiver = receiver self._queued_notifications = queue.Queue(16) self._notifications_callback = notifications_callback def run(self): self._active = True # replace the handle with a threaded one self.receiver.handle = _ThreadedHandle(self, self.receiver.path, self.receiver.handle) if logger.isEnabledFor(logging.INFO): logger.info("started with %s (%d)", self.receiver, int(self.receiver.handle)) self.has_started() if self.receiver.isDevice: # ping (wired or BT) devices to see if they are really online if self.receiver.ping(): self.receiver.changed(active=True, reason="initialization") while self._active: if self._queued_notifications.empty(): try: n = base.read(self.receiver.handle, _EVENT_READ_TIMEOUT) except exceptions.NoReceiver: logger.warning("%s disconnected", self.receiver.name) self.receiver.close() break if n: n = base.make_notification(*n) else: n = self._queued_notifications.get() # deliver any queued notifications if n: try: self._notifications_callback(n) except Exception: logger.exception("processing %s", n) del self._queued_notifications self.has_stopped() def stop(self): """Tells the listener to stop as soon as possible.""" self._active = False def has_started(self): """Called right after the thread has started, and before it starts reading notification packets.""" pass def has_stopped(self): """Called right before the thread stops.""" pass def _notifications_hook(self, n): # Only consider unhandled notifications that were sent from this thread, # i.e. triggered by a callback handling a previous notification. assert threading.current_thread() == self if self._active: # and threading.current_thread() == self: # if logger.isEnabledFor(logging.DEBUG): # logger.debug("queueing unhandled %s", n) if not self._queued_notifications.full(): self._queued_notifications.put(n) def __bool__(self): return bool(self._active and self.receiver) __nonzero__ = __bool__ Solaar-1.1.14/lib/logitech_receiver/notifications.py000066400000000000000000000521701473525213700225100ustar00rootroot00000000000000## Copyright (C) 2012-2013 Daniel Pavel ## Copyright (C) 2014-2024 Solaar Contributors https://pwr-solaar.github.io/Solaar/ ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. """Handles incoming events from the receiver/devices, updating the object as appropriate. """ from __future__ import annotations import logging import struct import threading import typing from solaar.i18n import _ from . import base from . import common from . import diversion from . import hidpp10 from . import hidpp10_constants from . import hidpp20 from . import hidpp20_constants from . import settings_templates from .common import Alert from .common import BatteryStatus from .common import Notification from .hidpp10_constants import Registers if typing.TYPE_CHECKING: from .base import HIDPPNotification from .receiver import Receiver logger = logging.getLogger(__name__) _hidpp10 = hidpp10.Hidpp10() _hidpp20 = hidpp20.Hidpp20() _F = hidpp20_constants.SupportedFeature notification_lock = threading.Lock() def process(device, notification): assert device assert notification if not device.isDevice: return _process_receiver_notification(device, notification) return _process_device_notification(device, notification) def _process_receiver_notification(receiver: Receiver, hidpp_notification: HIDPPNotification) -> bool | None: # supposedly only 0x4x notifications arrive for the receiver assert hidpp_notification.sub_id in [ Notification.CONNECT_DISCONNECT, Notification.DJ_PAIRING, Notification.CONNECTED, Notification.RAW_INPUT, Notification.PAIRING_LOCK, Notification.POWER, Registers.DEVICE_DISCOVERY_NOTIFICATION, Registers.DISCOVERY_STATUS_NOTIFICATION, Registers.PAIRING_STATUS_NOTIFICATION, Registers.PASSKEY_PRESSED_NOTIFICATION, Registers.PASSKEY_REQUEST_NOTIFICATION, ] if hidpp_notification.sub_id == Notification.PAIRING_LOCK: receiver.pairing.lock_open = bool(hidpp_notification.address & 0x01) reason = _("pairing lock is open") if receiver.pairing.lock_open else _("pairing lock is closed") if logger.isEnabledFor(logging.INFO): logger.info("%s: %s", receiver, reason) receiver.pairing.error = None if receiver.pairing.lock_open: receiver.pairing.new_device = None pair_error = ord(hidpp_notification.data[:1]) if pair_error: receiver.pairing.error = error_string = hidpp10_constants.PairingError(pair_error) receiver.pairing.new_device = None logger.warning("pairing error %d: %s", pair_error, error_string) receiver.changed(reason=reason) return True elif hidpp_notification.sub_id == Registers.DISCOVERY_STATUS_NOTIFICATION: # Bolt pairing with notification_lock: receiver.pairing.discovering = hidpp_notification.address == 0x00 reason = _("discovery lock is open") if receiver.pairing.discovering else _("discovery lock is closed") if logger.isEnabledFor(logging.INFO): logger.info("%s: %s", receiver, reason) receiver.pairing.error = None if receiver.pairing.discovering: receiver.pairing.counter = receiver.pairing.device_address = None receiver.pairing.device_authentication = receiver.pairing.device_name = None receiver.pairing.device_passkey = None discover_error = ord(hidpp_notification.data[:1]) if discover_error: receiver.pairing.error = discover_string = hidpp10_constants.BoltPairingError(discover_error) logger.warning("bolt discovering error %d: %s", discover_error, discover_string) receiver.changed(reason=reason) return True elif hidpp_notification.sub_id == Registers.DEVICE_DISCOVERY_NOTIFICATION: # Bolt pairing with notification_lock: counter = hidpp_notification.address + hidpp_notification.data[0] * 256 # notification counter if receiver.pairing.counter is None: receiver.pairing.counter = counter else: if not receiver.pairing.counter == counter: return None if hidpp_notification.data[1] == 0: receiver.pairing.device_kind = hidpp_notification.data[3] receiver.pairing.device_address = hidpp_notification.data[6:12] receiver.pairing.device_authentication = hidpp_notification.data[14] elif hidpp_notification.data[1] == 1: receiver.pairing.device_name = hidpp_notification.data[3 : 3 + hidpp_notification.data[2]].decode("utf-8") return True elif hidpp_notification.sub_id == Registers.PAIRING_STATUS_NOTIFICATION: # Bolt pairing with notification_lock: receiver.pairing.device_passkey = None receiver.pairing.lock_open = hidpp_notification.address == 0x00 reason = _("pairing lock is open") if receiver.pairing.lock_open else _("pairing lock is closed") if logger.isEnabledFor(logging.INFO): logger.info("%s: %s", receiver, reason) receiver.pairing.error = None if not receiver.pairing.lock_open: receiver.pairing.counter = None receiver.pairing.device_address = None receiver.pairing.device_authentication = None receiver.pairing.device_name = None pair_error = hidpp_notification.data[0] if receiver.pairing.lock_open: receiver.pairing.new_device = None elif hidpp_notification.address == 0x02 and not pair_error: receiver.pairing.new_device = receiver.register_new_device(hidpp_notification.data[7]) if pair_error: receiver.pairing.error = error_string = hidpp10_constants.BoltPairingError(pair_error) receiver.pairing.new_device = None logger.warning("pairing error %d: %s", pair_error, error_string) receiver.changed(reason=reason) return True elif hidpp_notification.sub_id == Registers.PASSKEY_REQUEST_NOTIFICATION: # Bolt pairing with notification_lock: receiver.pairing.device_passkey = hidpp_notification.data[0:6].decode("utf-8") return True elif hidpp_notification.sub_id == Registers.PASSKEY_PRESSED_NOTIFICATION: # Bolt pairing return True logger.warning("%s: unhandled notification %s", receiver, hidpp_notification) def _process_device_notification(device, n): # incoming packets with SubId >= 0x80 are supposedly replies from HID++ 1.0 requests, should never get here assert n.sub_id & 0x80 == 0 if n.sub_id == Notification.NO_OPERATION: # dispose it return False # Allow the device object to handle the notification using custom per-device state. handling_ret = device.handle_notification(n) if handling_ret is not None: return handling_ret # 0x40 to 0x7F appear to be HID++ 1.0 or DJ notifications if n.sub_id >= 0x40: if n.report_id == base.DJ_MESSAGE_ID: return _process_dj_notification(device, n) else: return _process_hidpp10_notification(device, n) # These notifications are from the device itself, so it must be active device.online = True # At this point, we need to know the device's protocol, otherwise it's possible to not know how to handle it. assert device.protocol is not None # some custom battery events for HID++ 1.0 devices if device.protocol < 2.0: return _process_hidpp10_custom_notification(device, n) # assuming 0x00 to 0x3F are feature (HID++ 2.0) notifications if not device.features: logger.warning("%s: feature notification but features not set up: %02X %s", device, n.sub_id, n) return False try: feature = device.features.get_feature(n.sub_id) except IndexError: logger.warning("%s: notification from invalid feature index %02X: %s", device, n.sub_id, n) return False return _process_feature_notification(device, n, feature) def _process_dj_notification(device, n): if logger.isEnabledFor(logging.DEBUG): logger.debug("%s (%s) DJ %s", device, device.protocol, n) if n.sub_id == Notification.CONNECT_DISCONNECT: # do all DJ paired notifications also show up as HID++ 1.0 notifications? if logger.isEnabledFor(logging.INFO): logger.info("%s: ignoring DJ unpaired: %s", device, n) return True if n.sub_id == Notification.DJ_PAIRING: # do all DJ paired notifications also show up as HID++ 1.0 notifications? if logger.isEnabledFor(logging.INFO): logger.info("%s: ignoring DJ paired: %s", device, n) return True if n.sub_id == Notification.CONNECTED: connected = not n.address & 0x01 if logger.isEnabledFor(logging.INFO): logger.info("%s: DJ connection: %s %s", device, connected, n) device.changed(active=connected, alert=Alert.NONE, reason=_("connected") if connected else _("disconnected")) return True logger.warning("%s: unrecognized DJ %s", device, n) def _process_hidpp10_custom_notification(device, n): if logger.isEnabledFor(logging.DEBUG): logger.debug("%s (%s) custom notification %s", device, device.protocol, n) if n.sub_id in (Registers.BATTERY_STATUS, Registers.BATTERY_CHARGE): assert n.data[-1:] == b"\x00" data = chr(n.address).encode() + n.data device.set_battery_info(hidpp10.parse_battery_status(n.sub_id, data)) return True logger.warning("%s: unrecognized %s", device, n) def _process_hidpp10_notification(device, n): if n.sub_id == Notification.CONNECT_DISCONNECT: # device unpairing if n.address == 0x02: # device un-paired device.wpid = None if device.number in device.receiver: del device.receiver[device.number] device.changed(active=False, alert=Alert.ALL, reason=_("unpaired")) ## device.status = None else: logger.warning("%s: disconnection with unknown type %02X: %s", device, n.address, n) return True if n.sub_id == Notification.DJ_PAIRING: # device connection (and disconnection) flags = ord(n.data[:1]) & 0xF0 if n.address == 0x02: # very old 27 MHz protocol wpid = "00" + common.strhex(n.data[2:3]) link_established = True link_encrypted = bool(flags & 0x80) elif n.address > 0x00: # all other protocols are supposed to be almost the same wpid = common.strhex(n.data[2:3] + n.data[1:2]) link_established = not (flags & 0x40) link_encrypted = bool(flags & 0x20) or n.address == 0x10 # Bolt protocol always encrypted else: logger.warning("%s: connection notification with unknown protocol %02X: %s", device.number, n.address, n) return True if wpid != device.wpid: logger.warning("%s wpid mismatch, got %s", device, wpid) if logger.isEnabledFor(logging.DEBUG): logger.debug( "%s: protocol %s connection notification: software=%s, encrypted=%s, link=%s, payload=%s", device, n.address, bool(flags & 0x10), link_encrypted, link_established, bool(flags & 0x80), ) device.link_encrypted = link_encrypted if not link_established and device.receiver: hidpp10.set_configuration_pending_flags(device.receiver, 0xFF) device.changed(active=link_established) return True if n.sub_id == Notification.RAW_INPUT: # raw input event? just ignore it # if n.address == 0x01, no idea what it is, but they keep on coming # if n.address == 0x03, appears to be an actual input event, because they only come when input happents return True if n.sub_id == Notification.POWER: if n.address == 0x01: if logger.isEnabledFor(logging.DEBUG): logger.debug("%s: device powered on", device) reason = device.status_string() or _("powered on") device.changed(active=True, alert=Alert.NOTIFICATION, reason=reason) else: logger.warning("%s: unknown %s", device, n) return True logger.warning("%s: unrecognized %s", device, n) def _process_feature_notification(device, n, feature): if logger.isEnabledFor(logging.DEBUG): logger.debug( "%s: notification for feature %s, report %s, data %s", device, feature, n.address >> 4, common.strhex(n.data) ) if feature == _F.BATTERY_STATUS: if n.address == 0x00: device.set_battery_info(hidpp20.decipher_battery_status(n.data)[1]) elif n.address == 0x10: if logger.isEnabledFor(logging.INFO): logger.info("%s: spurious BATTERY status %s", device, n) else: logger.warning("%s: unknown BATTERY %s", device, n) elif feature == _F.BATTERY_VOLTAGE: if n.address == 0x00: device.set_battery_info(hidpp20.decipher_battery_voltage(n.data)[1]) else: logger.warning("%s: unknown VOLTAGE %s", device, n) elif feature == _F.UNIFIED_BATTERY: if n.address == 0x00: device.set_battery_info(hidpp20.decipher_battery_unified(n.data)[1]) else: logger.warning("%s: unknown UNIFIED BATTERY %s", device, n) elif feature == _F.ADC_MEASUREMENT: if n.address == 0x00: result = hidpp20.decipher_adc_measurement(n.data) if result: device.set_battery_info(result[1]) else: # this feature is used to signal device becoming inactive device.changed(active=False) else: logger.warning("%s: unknown ADC MEASUREMENT %s", device, n) elif feature == _F.SOLAR_DASHBOARD: if n.data[5:9] == b"GOOD": charge, lux, adc = struct.unpack("!BHH", n.data[:5]) # guesstimate the battery voltage, emphasis on 'guess' # status_text = '%1.2fV' % (adc * 2.67793237653 / 0x0672) status_text = BatteryStatus.DISCHARGING if n.address == 0x00: device.set_battery_info(common.Battery(charge, None, status_text, None)) elif n.address == 0x10: if lux > 200: status_text = BatteryStatus.RECHARGING device.set_battery_info(common.Battery(charge, None, status_text, None, lux)) elif n.address == 0x20: if logger.isEnabledFor(logging.DEBUG): logger.debug("%s: Light Check button pressed", device) device.changed(alert=Alert.SHOW_WINDOW) # first cancel any reporting # device.feature_request(_F.SOLAR_DASHBOARD) # trigger a new report chain reports_count = 15 reports_period = 2 # seconds device.feature_request(_F.SOLAR_DASHBOARD, 0x00, reports_count, reports_period) else: logger.warning("%s: unknown SOLAR CHARGE %s", device, n) else: logger.warning("%s: SOLAR CHARGE not GOOD? %s", device, n) elif feature == _F.WIRELESS_DEVICE_STATUS: if n.address == 0x00: if logger.isEnabledFor(logging.DEBUG): logger.debug("wireless status: %s", n) reason = "powered on" if n.data[2] == 1 else None if n.data[1] == 1: # device is asking for software reconfiguration so need to change status alert = Alert.NONE device.changed(active=True, alert=alert, reason=reason, push=True) else: logger.warning("%s: unknown WIRELESS %s", device, n) elif feature == _F.TOUCHMOUSE_RAW_POINTS: if n.address == 0x00: if logger.isEnabledFor(logging.INFO): logger.info("%s: TOUCH MOUSE points %s", device, n) elif n.address == 0x10: touch = ord(n.data[:1]) button_down = bool(touch & 0x02) mouse_lifted = bool(touch & 0x01) if logger.isEnabledFor(logging.INFO): logger.info("%s: TOUCH MOUSE status: button_down=%s mouse_lifted=%s", device, button_down, mouse_lifted) else: logger.warning("%s: unknown TOUCH MOUSE %s", device, n) # TODO: what are REPROG_CONTROLS_V{2,3}? elif feature == _F.REPROG_CONTROLS: if n.address == 0x00: if logger.isEnabledFor(logging.INFO): logger.info("%s: reprogrammable key: %s", device, n) else: logger.warning("%s: unknown REPROG_CONTROLS %s", device, n) elif feature == _F.BACKLIGHT2: if n.address == 0x00: level = struct.unpack("!B", n.data[1:2])[0] if device.setting_callback: device.setting_callback(device, settings_templates.Backlight2Level, [level]) elif feature == _F.REPROG_CONTROLS_V4: if n.address == 0x00: if logger.isEnabledFor(logging.DEBUG): cid1, cid2, cid3, cid4 = struct.unpack("!HHHH", n.data[:8]) logger.debug("%s: diverted controls pressed: 0x%x, 0x%x, 0x%x, 0x%x", device, cid1, cid2, cid3, cid4) elif n.address == 0x10: if logger.isEnabledFor(logging.DEBUG): dx, dy = struct.unpack("!hh", n.data[:4]) logger.debug("%s: rawXY dx=%i dy=%i", device, dx, dy) elif n.address == 0x20: if logger.isEnabledFor(logging.DEBUG): logger.debug("%s: received analyticsKeyEvents", device) elif logger.isEnabledFor(logging.INFO): logger.info("%s: unknown REPROG_CONTROLS_V4 %s", device, n) elif feature == _F.HIRES_WHEEL: if n.address == 0x00: if logger.isEnabledFor(logging.INFO): flags, delta_v = struct.unpack(">bh", n.data[:3]) high_res = (flags & 0x10) != 0 periods = flags & 0x0F logger.info("%s: WHEEL: res: %d periods: %d delta V:%-3d", device, high_res, periods, delta_v) elif n.address == 0x10: ratchet = n.data[0] if logger.isEnabledFor(logging.INFO): logger.info("%s: WHEEL: ratchet: %d", device, ratchet) if ratchet < 2: # don't process messages with unusual ratchet values if device.setting_callback: device.setting_callback(device, settings_templates.ScrollRatchet, [2 if ratchet else 1]) else: if logger.isEnabledFor(logging.INFO): logger.info("%s: unknown WHEEL %s", device, n) elif feature == _F.ONBOARD_PROFILES: if n.address > 0x10: if logger.isEnabledFor(logging.INFO): logger.info("%s: unknown ONBOARD PROFILES %s", device, n) else: if n.address == 0x00: profile_sector = struct.unpack("!H", n.data[:2])[0] if profile_sector: settings_templates.profile_change(device, profile_sector) elif n.address == 0x10: resolution_index = struct.unpack("!B", n.data[:1])[0] profile_sector = struct.unpack("!H", device.feature_request(_F.ONBOARD_PROFILES, 0x40)[:2])[0] if device.setting_callback: for profile in device.profiles.profiles.values() if device.profiles else []: if profile.sector == profile_sector: device.setting_callback( device, settings_templates.AdjustableDpi, [profile.resolutions[resolution_index]] ) break elif feature == _F.BRIGHTNESS_CONTROL: if n.address > 0x10: if logger.isEnabledFor(logging.INFO): logger.info("%s: unknown BRIGHTNESS CONTROL %s", device, n) else: if n.address == 0x00: brightness = struct.unpack("!H", n.data[:2])[0] device.setting_callback(device, settings_templates.BrightnessControl, [brightness]) elif n.address == 0x10: brightness = n.data[0] & 0x01 if brightness: brightness = struct.unpack("!H", device.feature_request(_F.BRIGHTNESS_CONTROL, 0x10)[:2])[0] device.setting_callback(device, settings_templates.BrightnessControl, [brightness]) diversion.process_notification(device, n, feature) return True Solaar-1.1.14/lib/logitech_receiver/receiver.py000066400000000000000000000520721473525213700214440ustar00rootroot00000000000000## Copyright (C) 2012-2013 Daniel Pavel ## Copyright (C) 2014-2024 Solaar Contributors https://pwr-solaar.github.io/Solaar/ ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. from __future__ import annotations import errno import logging import time import typing from dataclasses import dataclass from typing import Callable from typing import Optional from typing import Protocol from solaar.i18n import _ from solaar.i18n import ngettext from . import exceptions from . import hidpp10 from . import hidpp10_constants from .common import Alert from .common import Notification from .device import Device from .hidpp10_constants import Registers if typing.TYPE_CHECKING: from logitech_receiver import common logger = logging.getLogger(__name__) _hidpp10 = hidpp10.Hidpp10() _IR = hidpp10_constants.INFO_SUBREGISTERS class LowLevelInterface(Protocol): def open_path(self, path): ... def find_paired_node_wpid(self, receiver_path: str, index: int): ... def ping(self, handle, number, long_message=False): ... def request(self, handle, devnumber, request_id, *params, **kwargs): ... def close(self, handle): ... @dataclass class Pairing: """Information about the current or most recent pairing""" lock_open: bool = False discovering: bool = False counter: Optional[int] = None device_address: Optional[bytes] = None device_authentication: Optional[int] = None device_kind: Optional[int] = None device_name: Optional[str] = None device_passkey: Optional[str] = None new_device: Optional[Device] = None error: Optional[any] = None class Receiver: """A generic Receiver instance, mostly implementing the interface used on Unifying, Nano, and LightSpeed receivers" The paired devices are available through the sequence interface. """ read_register: Callable = hidpp10.read_register write_register: Callable = hidpp10.write_register number = 0xFF kind = None def __init__( self, low_level: LowLevelInterface, receiver_kind, product_info, handle, path, product_id, setting_callback=None, ): assert handle self.low_level = low_level self.isDevice = False # some devices act as receiver so we need a property to distinguish them self.handle = handle self.path = path self.product_id = product_id self.setting_callback = setting_callback # for changes to settings self.status_callback = None # for changes to other potentially visible aspects self.receiver_kind = receiver_kind self.serial = None self.max_devices = None self._firmware = None self._remaining_pairings = None self._devices = {} self.name = product_info.get("name", "Receiver") self.may_unpair = product_info.get("may_unpair", False) self.re_pairs = product_info.get("re_pairs", False) self.notification_flags = None self.pairing = Pairing() self.initialize(product_info) hidpp10.set_configuration_pending_flags(self, 0xFF) def initialize(self, product_info: dict): # read the receiver information subregister, so we can find out max_devices serial_reply = self.read_register(Registers.RECEIVER_INFO, _IR.receiver_information) if serial_reply: self.serial = serial_reply[1:5].hex().upper() self.max_devices = serial_reply[6] if self.max_devices <= 0 or self.max_devices > 6: self.max_devices = product_info.get("max_devices", 1) else: # handle receivers that don't have a serial number specially (i.e., c534) self.serial = None self.max_devices = product_info.get("max_devices", 1) def close(self): handle, self.handle = self.handle, None for _n, d in self._devices.items(): if d: d.close() self._devices.clear() return handle and self.low_level.close(handle) def __del__(self): self.close() def changed(self, alert=Alert.NOTIFICATION, reason=None): """The status of the device had changed, so invoke the status callback""" if self.status_callback is not None: self.status_callback(self, alert=alert, reason=reason) @property def firmware(self) -> tuple[common.FirmwareInfo]: if self._firmware is None and self.handle: self._firmware = _hidpp10.get_firmware(self) return self._firmware # how many pairings remain (None for unknown, -1 for unlimited) def remaining_pairings(self, cache=True): if self._remaining_pairings is None or not cache: ps = self.read_register(Registers.RECEIVER_CONNECTION) if ps is not None: ps = ord(ps[2:3]) self._remaining_pairings = ps - 5 if ps >= 5 else -1 return self._remaining_pairings def enable_connection_notifications(self, enable=True): """Enable or disable device (dis)connection notifications on this receiver.""" if not self.handle: return False if enable: set_flag_bits = hidpp10_constants.NOTIFICATION_FLAG.wireless | hidpp10_constants.NOTIFICATION_FLAG.software_present else: set_flag_bits = 0 ok = _hidpp10.set_notification_flags(self, set_flag_bits) if ok is None: logger.warning("%s: failed to %s receiver notifications", self, "enable" if enable else "disable") return None flag_bits = _hidpp10.get_notification_flags(self) flag_names = None if flag_bits is None else tuple(hidpp10_constants.NOTIFICATION_FLAG.flag_names(flag_bits)) if logger.isEnabledFor(logging.INFO): logger.info("%s: receiver notifications %s => %s", self, "enabled" if enable else "disabled", flag_names) return flag_bits def device_codename(self, n): codename = self.read_register(Registers.RECEIVER_INFO, _IR.device_name + n - 1) if codename: codename = codename[2 : 2 + ord(codename[1:2])] return codename.decode("ascii") def notify_devices(self): """Scan all devices.""" if self.handle: if not self.write_register(Registers.RECEIVER_CONNECTION, 0x02): logger.warning("%s: failed to trigger device link notifications", self) def notification_information(self, number, notification): """Extract information from unifying-style notification""" assert notification.address != 0x02 online = not bool(notification.data[0] & 0x40) encrypted = bool(notification.data[0] & 0x20) or notification.address == 0x10 kind = hidpp10_constants.DEVICE_KIND[notification.data[0] & 0x0F] wpid = (notification.data[2:3] + notification.data[1:2]).hex().upper() return online, encrypted, wpid, kind def device_pairing_information(self, n: int) -> dict: """Return information from pairing registers (and elsewhere when necessary)""" polling_rate = "" serial = None power_switch = "(unknown)" pair_info = self.read_register(Registers.RECEIVER_INFO, _IR.pairing_information + n - 1) if pair_info: # a receiver that uses Unifying-style pairing registers wpid = pair_info[3:5].hex().upper() kind = hidpp10_constants.DEVICE_KIND[pair_info[7] & 0x0F] polling_rate = str(pair_info[2]) + "ms" elif not self.receiver_kind == "unifying": # may be an old Nano receiver device_info = self.read_register(Registers.RECEIVER_INFO, 0x04) # undocumented if device_info: logger.warning("using undocumented register for device wpid") wpid = device_info[3:5].hex().upper() kind = hidpp10_constants.DEVICE_KIND[0x00] # unknown kind else: raise exceptions.NoSuchDevice(number=n, receiver=self, error="read pairing information - non-unifying") else: raise exceptions.NoSuchDevice(number=n, receiver=self, error="read pairing information") pair_info = self.read_register(Registers.RECEIVER_INFO, _IR.extended_pairing_information + n - 1) if pair_info: power_switch = hidpp10_constants.POWER_SWITCH_LOCATION[pair_info[9] & 0x0F] serial = pair_info[1:5].hex().upper() else: # some Nano receivers? pair_info = self.read_register(0x2D5) # undocumented and questionable if pair_info: logger.warning("using undocumented register for device serial number") serial = pair_info[1:5].hex().upper() return {"wpid": wpid, "kind": kind, "polling": polling_rate, "serial": serial, "power_switch": power_switch} def register_new_device(self, number, notification=None): if self._devices.get(number) is not None: raise IndexError(f"{self}: device number {int(number)} already registered") assert notification is None or notification.devnumber == number assert notification is None or notification.sub_id == Notification.DJ_PAIRING try: time.sleep(0.05) # let receiver settle info = self.device_pairing_information(number) if notification is not None: online, _e, nwpid, nkind = self.notification_information(number, notification) if info["wpid"] is None: info["wpid"] = nwpid elif nwpid is not None and info["wpid"] != nwpid: logger.warning("mismatch on device WPID %s %s", info["wpid"], nwpid) if info["kind"] is None: info["kind"] = nkind elif nkind is not None and info["kind"] != nkind: logger.warning("mismatch on device kind %s %s", info["kind"], nkind) else: online = True dev = Device(self.low_level, self, number, online, pairing_info=info, setting_callback=self.setting_callback) if logger.isEnabledFor(logging.INFO): logger.info("%s: found new device %d (%s)", self, number, dev.wpid) self._devices[number] = dev return dev except exceptions.NoSuchDevice as e: logger.warning("register new device failed for %s device %d error %s", e.receiver, e.number, e.error) logger.warning("%s: looked for device %d, not found", self, number) self._devices[number] = None def set_lock(self, lock_closed=True, device=0, timeout=0): if self.handle: action = 0x02 if lock_closed else 0x01 reply = self.write_register(Registers.RECEIVER_PAIRING, action, device, timeout) if reply: return True logger.warning("%s: failed to %s the receiver lock", self, "close" if lock_closed else "open") def count(self): count = self.read_register(Registers.RECEIVER_CONNECTION) return 0 if count is None else ord(count[1:2]) def request(self, request_id, *params): if bool(self): return self.low_level.request(self.handle, 0xFF, request_id, *params) def reset_pairing(self): self.pairing = Pairing() def __iter__(self): connected_devices = self.count() found_devices = 0 for number in range(1, 8): # some receivers have devices past their max # devices if found_devices >= connected_devices: return if number in self._devices: dev = self._devices[number] else: dev = self.__getitem__(number) if dev is not None: found_devices += 1 yield dev def __getitem__(self, key): if not bool(self): return None dev = self._devices.get(key) if dev is not None: return dev if not isinstance(key, int): raise TypeError("key must be an integer") if key < 1 or key > 15: # some receivers have devices past their max # devices raise IndexError(key) return self.register_new_device(key) def __delitem__(self, key): self._unpair_device(key, False) def _unpair_device(self, key, force=False): key = int(key) if self._devices.get(key) is None: raise IndexError(key) dev = self._devices[key] if not dev: if key in self._devices: del self._devices[key] return if self.re_pairs and not force: # invalidate the device, but these receivers don't unpair per se dev.online = False dev.wpid = None if key in self._devices: del self._devices[key] logger.warning("%s removed device %s", self, dev) else: reply = self._unpair_device_per_receiver(key) if reply: # invalidate the device dev.online = False dev.wpid = None if key in self._devices: del self._devices[key] if logger.isEnabledFor(logging.INFO): logger.info("%s unpaired device %s", self, dev) else: logger.error("%s failed to unpair device %s", self, dev) raise Exception(f"failed to unpair device {dev.name}: {key}") def _unpair_device_per_receiver(self, key): """Receiver specific unpairing.""" return self.write_register(Registers.RECEIVER_PAIRING, 0x03, key) def __len__(self): return len([d for d in self._devices.values() if d is not None]) def __contains__(self, dev): if isinstance(dev, int): return self._devices.get(dev) is not None return self.__contains__(dev.number) def __eq__(self, other): return other is not None and self.kind == other.kind and self.path == other.path def __ne__(self, other): return other is None or self.kind != other.kind or self.path != other.path def __hash__(self): return self.path.__hash__() def status_string(self): count = len(self) return ( _("No paired devices.") if count == 0 else ngettext("%(count)s paired device.", "%(count)s paired devices.", count) % {"count": count} ) def __str__(self): return "<%s(%s,%s%s)>" % ( self.name.replace(" ", ""), self.path, "" if isinstance(self.handle, int) else "T", self.handle, ) __repr__ = __str__ __bool__ = __nonzero__ = lambda self: self.handle is not None class BoltReceiver(Receiver): """Bolt receivers use a different pairing prototol and have different pairing registers""" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) def initialize(self, product_info: dict): serial_reply = self.read_register(Registers.BOLT_UNIQUE_ID) self.serial = serial_reply.hex().upper() self.max_devices = product_info.get("max_devices", 1) def device_codename(self, n): codename = self.read_register(Registers.RECEIVER_INFO, _IR.bolt_device_name + n, 0x01) if codename: codename = codename[3 : 3 + min(14, ord(codename[2:3]))] return codename.decode("ascii") def device_pairing_information(self, n: int) -> dict: pair_info = self.read_register(Registers.RECEIVER_INFO, _IR.bolt_pairing_information + n) if pair_info: wpid = (pair_info[3:4] + pair_info[2:3]).hex().upper() kind = hidpp10_constants.DEVICE_KIND[pair_info[1] & 0x0F] serial = pair_info[4:8].hex().upper() return {"wpid": wpid, "kind": kind, "polling": None, "serial": serial, "power_switch": "(unknown)"} else: raise exceptions.NoSuchDevice(number=n, receiver=self, error="can't read Bolt pairing register") def discover(self, cancel=False, timeout=30): """Discover Logitech Bolt devices.""" if self.handle: action = 0x02 if cancel else 0x01 reply = self.write_register(Registers.BOLT_DEVICE_DISCOVERY, timeout, action) if reply: return True logger.warning("%s: failed to %s device discovery", self, "cancel" if cancel else "start") def pair_device(self, pair=True, slot=0, address=b"\0\0\0\0\0\0", authentication=0x00, entropy=20): """Pair a Bolt device.""" if self.handle: action = 0x01 if pair is True else 0x03 if pair is False else 0x02 reply = self.write_register(Registers.BOLT_PAIRING, action, slot, address, authentication, entropy) if reply: return True logger.warning("%s: failed to %s device %s", self, "pair" if pair else "unpair", address) def _unpair_device_per_receiver(self, key): """Receiver specific unpairing.""" return self.write_register(Registers.BOLT_PAIRING, 0x03, key) class UnifyingReceiver(Receiver): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) class NanoReceiver(Receiver): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) class LightSpeedReceiver(Receiver): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) class Ex100Receiver(Receiver): """A very old style receiver, somewhat different from newer receivers""" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) def initialize(self, product_info: dict): self.serial = None self.max_devices = product_info.get("max_devices", 1) def notification_information(self, number, notification): """Extract information from 27Mz-style notification and device index""" assert notification.address == 0x02 online = True encrypted = bool(notification.data[0] & 0x80) kind = hidpp10_constants.DEVICE_KIND[_get_kind_from_index(self, number)] wpid = "00" + notification.data[2:3].hex().upper() return online, encrypted, wpid, kind def device_pairing_information(self, number: int) -> dict: # extract WPID from udev path wpid = self.low_level.find_paired_node_wpid(self.path, number) if not wpid: logger.error("Unable to get wpid from udev for device %d of %s", number, self) raise exceptions.NoSuchDevice(number=number, receiver=self, error="Not present 27Mhz device") kind = hidpp10_constants.DEVICE_KIND[_get_kind_from_index(self, number)] return {"wpid": wpid, "kind": kind, "polling": "", "serial": None, "power_switch": "(unknown)"} def _get_kind_from_index(receiver, index): """Get device kind from 27Mhz device index""" # From drivers/hid/hid-logitech-dj.c if index == 1: # mouse kind = 2 elif index == 2: # mouse kind = 2 elif index == 3: # keyboard kind = 1 elif index == 4: # numpad kind = 3 else: # unknown device number on 27Mhz receiver logger.error("failed to calculate device kind for device %d of %s", index, receiver) raise exceptions.NoSuchDevice(number=index, receiver=receiver, error="Unknown 27Mhz device number") return kind receiver_class_mapping = { "bolt": BoltReceiver, "unifying": UnifyingReceiver, "lightspeed": LightSpeedReceiver, "nano": NanoReceiver, "27Mhz": Ex100Receiver, } def create_receiver(low_level: LowLevelInterface, device_info, setting_callback=None) -> Optional[Receiver]: """Opens a Logitech Receiver found attached to the machine, by Linux device path.""" try: handle = low_level.open_path(device_info.path) if handle: usb_id = device_info.product_id if isinstance(usb_id, str): usb_id = int(usb_id, 16) try: product_info = low_level.product_information(usb_id) except ValueError: product_info = {} kind = product_info.get("receiver_kind", "unknown") rclass = receiver_class_mapping.get(kind, Receiver) return rclass( low_level, kind, product_info, handle, device_info.path, device_info.product_id, setting_callback, ) except OSError as e: logger.exception("open %s", device_info) if e.errno == errno.EACCES: raise except Exception: logger.exception("open %s", device_info) Solaar-1.1.14/lib/logitech_receiver/settings.py000066400000000000000000001746031473525213700215050ustar00rootroot00000000000000## Copyright (C) 2012-2013 Daniel Pavel ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. from __future__ import annotations import logging import math import struct import time from solaar.i18n import _ from . import common from . import hidpp20_constants from .common import NamedInt from .common import NamedInts logger = logging.getLogger(__name__) SENSITIVITY_IGNORE = "ignore" KIND = NamedInts( toggle=0x01, choice=0x02, range=0x04, map_choice=0x0A, multiple_toggle=0x10, packed_range=0x20, multiple_range=0x40, hetero=0x80, ) def bool_or_toggle(current: bool | str, new: bool | str) -> bool: if isinstance(new, bool): return new try: return bool(int(new)) except (TypeError, ValueError): new = str(new).lower() if new in ("true", "yes", "on", "t", "y"): return True if new in ("false", "no", "off", "f", "n"): return False if new in ("~", "toggle"): return not current return None class Setting: """A setting descriptor. Needs to be instantiated for each specific device.""" name = label = description = "" feature = register = kind = None min_version = 0 persist = True rw_options = {} validator_class = None validator_options = {} def __init__(self, device, rw, validator): self._device = device self._rw = rw self._validator = validator self.kind = getattr(self._validator, "kind", None) self._value = None @classmethod def build(cls, device): assert cls.feature or cls.register, "Settings require either a feature or a register" rw_class = cls.rw_class if hasattr(cls, "rw_class") else FeatureRW if cls.feature else RegisterRW rw = rw_class(cls.feature if cls.feature else cls.register, **cls.rw_options) p = device.protocol if p == 1.0: # HID++ 1.0 devices do not support features assert rw.kind == RegisterRW.kind elif p >= 2.0: # HID++ 2.0 devices do not support registers assert rw.kind == FeatureRW.kind validator_class = cls.validator_class validator = validator_class.build(cls, device, **cls.validator_options) if validator: assert cls.kind is None or cls.kind & validator.kind != 0 return cls(device, rw, validator) def val_to_string(self, value): return self._validator.to_string(value) @property def choices(self): assert hasattr(self, "_value") assert hasattr(self, "_device") return self._validator.choices if self._validator and self._validator.kind & KIND.choice else None @property def range(self): assert hasattr(self, "_value") assert hasattr(self, "_device") if self._validator.kind == KIND.range: return self._validator.min_value, self._validator.max_value def _pre_read(self, cached, key=None): if self.persist and self._value is None and getattr(self._device, "persister", None): # We haven't read a value from the device yet, # maybe we have something in the configuration. self._value = self._device.persister.get(self.name) if cached and self._value is not None: if getattr(self._device, "persister", None) and self.name not in self._device.persister: # If this is a new device (or a new setting for an old device), # make sure to save its current value for the next time. self._device.persister[self.name] = self._value if self.persist else None def read(self, cached=True): assert hasattr(self, "_value") assert hasattr(self, "_device") self._pre_read(cached) if cached and self._value is not None: if logger.isEnabledFor(logging.DEBUG): logger.debug("%s: cached value %r on %s", self.name, self._value, self._device) return self._value if self._device.online: reply = self._rw.read(self._device) if reply: self._value = self._validator.validate_read(reply) if self._value is not None and self._device.persister and self.name not in self._device.persister: # Don't update the persister if it already has a value, # otherwise the first read might overwrite the value we wanted. self._device.persister[self.name] = self._value if self.persist else None if logger.isEnabledFor(logging.DEBUG): logger.debug("%s: read value %r on %s", self.name, self._value, self._device) return self._value def _pre_write(self, save=True): # Remember the value we're trying to set, even if the write fails. # This way even if the device is offline or some other error occurs, # the last value we've tried to write is remembered in the configuration. if self._device.persister and save: self._device.persister[self.name] = self._value if self.persist else None def update(self, value, save=True): self._value = value self._pre_write(save) def write(self, value, save=True): assert hasattr(self, "_value") assert hasattr(self, "_device") assert value is not None if logger.isEnabledFor(logging.DEBUG): logger.debug("%s: write %r to %s", self.name, value, self._device) if self._device.online: if self._value != value: self.update(value, save) current_value = None if self._validator.needs_current_value: # the _validator needs the current value, possibly to merge flag values current_value = self._rw.read(self._device) if logger.isEnabledFor(logging.DEBUG): logger.debug("%s: current value %r on %s", self.name, current_value, self._device) data_bytes = self._validator.prepare_write(value, current_value) if data_bytes is not None: if logger.isEnabledFor(logging.DEBUG): logger.debug("%s: prepare write(%s) => %r", self.name, value, data_bytes) reply = self._rw.write(self._device, data_bytes) if not reply: # tell whomever is calling that the write failed return None return value def acceptable(self, args, current): return self._validator.acceptable(args, current) if self._validator else None def compare(self, args, current): return self._validator.compare(args, current) if self._validator else None def apply(self): assert hasattr(self, "_value") assert hasattr(self, "_device") if logger.isEnabledFor(logging.DEBUG): logger.debug("%s: apply (%s)", self.name, self._device) try: value = self.read(self.persist) # Don't use persisted value if setting doesn't persist if self.persist and value is not None: # If setting doesn't persist no need to write value just read self.write(value, save=False) except Exception as e: if logger.isEnabledFor(logging.WARNING): logger.warning("%s: error applying %s so ignore it (%s): %s", self.name, self._value, self._device, repr(e)) def __str__(self): if hasattr(self, "_value"): assert hasattr(self, "_device") return "" % ( self._rw.kind, self._validator.kind if self._validator else None, self._device.codename, self.name, self._value, ) return f"" __repr__ = __str__ class Settings(Setting): """A setting descriptor for multiple choices, being a map from keys to values. Needs to be instantiated for each specific device.""" def read(self, cached=True): assert hasattr(self, "_value") assert hasattr(self, "_device") if logger.isEnabledFor(logging.DEBUG): logger.debug("%s: settings read %r from %s", self.name, self._value, self._device) self._pre_read(cached) if cached and self._value is not None: return self._value if self._device.online: reply_map = {} for key in self._validator.choices: reply = self._rw.read(self._device, key) if reply: reply_map[int(key)] = self._validator.validate_read(reply, key) self._value = reply_map if getattr(self._device, "persister", None) and self.name not in self._device.persister: # Don't update the persister if it already has a value, # otherwise the first read might overwrite the value we wanted. self._device.persister[self.name] = self._value if self.persist else None return self._value def read_key(self, key, cached=True): assert hasattr(self, "_value") assert hasattr(self, "_device") assert key is not None if logger.isEnabledFor(logging.DEBUG): logger.debug("%s: settings read %r key %r from %s", self.name, self._value, key, self._device) self._pre_read(cached) if cached and self._value is not None: return self._value[int(key)] if self._device.online: reply = self._rw.read(self._device, key) if reply: self._value[int(key)] = self._validator.validate_read(reply, key) if getattr(self._device, "persister", None) and self.name not in self._device.persister: self._device.persister[self.name] = self._value if self.persist else None return self._value[int(key)] def write(self, map, save=True): assert hasattr(self, "_value") assert hasattr(self, "_device") assert map is not None if logger.isEnabledFor(logging.DEBUG): logger.debug("%s: settings write %r to %s", self.name, map, self._device) if self._device.online: self.update(map, save) for key, value in map.items(): data_bytes = self._validator.prepare_write(int(key), value) if data_bytes is not None: if logger.isEnabledFor(logging.DEBUG): logger.debug("%s: settings prepare map write(%s,%s) => %r", self.name, key, value, data_bytes) reply = self._rw.write(self._device, int(key), data_bytes) if not reply: return None return map def update_key_value(self, key, value, save=True): self._value[int(key)] = value self._pre_write(save) def write_key_value(self, key, value, save=True): assert hasattr(self, "_value") assert hasattr(self, "_device") assert key is not None assert value is not None if logger.isEnabledFor(logging.DEBUG): logger.debug("%s: settings write key %r value %r to %s", self.name, key, value, self._device) if self._device.online: if not self._value: self.read() try: data_bytes = self._validator.prepare_write(int(key), value) # always need to write to configuration because dictionary is shared and could have changed self.update_key_value(key, value, save) except ValueError: data_bytes = value = None if data_bytes is not None: if logger.isEnabledFor(logging.DEBUG): logger.debug("%s: settings prepare key value write(%s,%s) => %r", self.name, key, value, data_bytes) reply = self._rw.write(self._device, int(key), data_bytes) if not reply: return None return value class LongSettings(Setting): """A setting descriptor for multiple choices, being a map from keys to values. Allows multiple write requests, if the options don't fit in 16 bytes. The validator must return a list. Needs to be instantiated for each specific device.""" def read(self, cached=True): assert hasattr(self, "_value") assert hasattr(self, "_device") if logger.isEnabledFor(logging.DEBUG): logger.debug("%s: settings read %r from %s", self.name, self._value, self._device) self._pre_read(cached) if cached and self._value is not None: return self._value if self._device.online: reply_map = {} # Reading one item at a time. This can probably be optimised for item in self._validator.items: r = self._validator.prepare_read_item(item) reply = self._rw.read(self._device, r) if reply: reply_map[int(item)] = self._validator.validate_read_item(reply, item) self._value = reply_map if getattr(self._device, "persister", None) and self.name not in self._device.persister: # Don't update the persister if it already has a value, # otherwise the first read might overwrite the value we wanted. self._device.persister[self.name] = self._value if self.persist else None return self._value def read_item(self, item, cached=True): assert hasattr(self, "_value") assert hasattr(self, "_device") assert item is not None if logger.isEnabledFor(logging.DEBUG): logger.debug("%s: settings read %r item %r from %s", self.name, self._value, item, self._device) self._pre_read(cached) if cached and self._value is not None: return self._value[int(item)] if self._device.online: r = self._validator.prepare_read_item(item) reply = self._rw.read(self._device, r) if reply: self._value[int(item)] = self._validator.validate_read_item(reply, item) if getattr(self._device, "persister", None) and self.name not in self._device.persister: self._device.persister[self.name] = self._value if self.persist else None return self._value[int(item)] def write(self, map, save=True): assert hasattr(self, "_value") assert hasattr(self, "_device") assert map is not None if logger.isEnabledFor(logging.DEBUG): logger.debug("%s: long settings write %r to %s", self.name, map, self._device) if self._device.online: self.update(map, save) for item, value in map.items(): data_bytes_list = self._validator.prepare_write(self._value) if data_bytes_list is not None: for data_bytes in data_bytes_list: if data_bytes is not None: if logger.isEnabledFor(logging.DEBUG): logger.debug("%s: settings prepare map write(%s,%s) => %r", self.name, item, value, data_bytes) reply = self._rw.write(self._device, data_bytes) if not reply: return None return map def update_key_value(self, key, value, save=True): self._value[int(key)] = value self._pre_write(save) def write_key_value(self, item, value, save=True): assert hasattr(self, "_value") assert hasattr(self, "_device") assert item is not None assert value is not None if logger.isEnabledFor(logging.DEBUG): logger.debug("%s: long settings write item %r value %r to %s", self.name, item, value, self._device) if self._device.online: if not self._value: self.read() data_bytes = self._validator.prepare_write_item(item, value) self.update_key_value(item, value, save) if data_bytes is not None: if logger.isEnabledFor(logging.DEBUG): logger.debug("%s: settings prepare item value write(%s,%s) => %r", self.name, item, value, data_bytes) reply = self._rw.write(self._device, data_bytes) if not reply: return None return value class BitFieldSetting(Setting): """A setting descriptor for a set of choices represented by one bit each, being a map from options to booleans. Needs to be instantiated for each specific device.""" def read(self, cached=True): assert hasattr(self, "_value") assert hasattr(self, "_device") if logger.isEnabledFor(logging.DEBUG): logger.debug("%s: settings read %r from %s", self.name, self._value, self._device) self._pre_read(cached) if cached and self._value is not None: return self._value if self._device.online: reply_map = {} reply = self._do_read() if reply: reply_map = self._validator.validate_read(reply) self._value = reply_map if getattr(self._device, "persister", None) and self.name not in self._device.persister: # Don't update the persister if it already has a value, # otherwise the first read might overwrite the value we wanted. self._device.persister[self.name] = self._value if self.persist else None return self._value def _do_read(self): return self._rw.read(self._device) def read_key(self, key, cached=True): assert hasattr(self, "_value") assert hasattr(self, "_device") assert key is not None if logger.isEnabledFor(logging.DEBUG): logger.debug("%s: settings read %r key %r from %s", self.name, self._value, key, self._device) self._pre_read(cached) if cached and self._value is not None: return self._value[int(key)] if self._device.online: reply = self._do_read_key(key) if reply: self._value = self._validator.validate_read(reply) if getattr(self._device, "persister", None) and self.name not in self._device.persister: self._device.persister[self.name] = self._value if self.persist else None return self._value[int(key)] def _do_read_key(self, key): return self._rw.read(self._device, key) def write(self, map, save=True): assert hasattr(self, "_value") assert hasattr(self, "_device") assert map is not None if logger.isEnabledFor(logging.DEBUG): logger.debug("%s: bit field settings write %r to %s", self.name, map, self._device) if self._device.online: self.update(map, save) data_bytes = self._validator.prepare_write(self._value) if data_bytes is not None: if logger.isEnabledFor(logging.DEBUG): logger.debug("%s: settings prepare map write(%s) => %r", self.name, self._value, data_bytes) # if prepare_write returns a list, write one item at a time seq = data_bytes if isinstance(data_bytes, list) else [data_bytes] for b in seq: reply = self._rw.write(self._device, b) if not reply: return None return map def update_key_value(self, key, value, save=True): self._value[int(key)] = value self._pre_write(save) def write_key_value(self, key, value, save=True): assert hasattr(self, "_value") assert hasattr(self, "_device") assert key is not None assert value is not None if logger.isEnabledFor(logging.DEBUG): logger.debug("%s: bit field settings write key %r value %r to %s", self.name, key, value, self._device) if self._device.online: if not self._value: self.read() value = bool(value) self.update_key_value(key, value, save) data_bytes = self._validator.prepare_write(self._value) if data_bytes is not None: if logger.isEnabledFor(logging.DEBUG): logger.debug("%s: settings prepare key value write(%s,%s) => %r", self.name, key, str(value), data_bytes) # if prepare_write returns a list, write one item at a time seq = data_bytes if isinstance(data_bytes, list) else [data_bytes] for b in seq: reply = self._rw.write(self._device, b) if not reply: return None return value class BitFieldWithOffsetAndMaskSetting(BitFieldSetting): """A setting descriptor for a set of choices represented by one bit each, each one having an offset, being a map from options to booleans. Needs to be instantiated for each specific device.""" def _do_read(self): return {r: self._rw.read(self._device, r) for r in self._validator.prepare_read()} def _do_read_key(self, key): r = self._validator.prepare_read_key(key) return {r: self._rw.read(self._device, r)} class RangeFieldSetting(Setting): """A setting descriptor for a set of choices represented by one field each, with map from option names to range(0,n). Needs to be instantiated for each specific device.""" def read(self, cached=True): assert hasattr(self, "_value") assert hasattr(self, "_device") if logger.isEnabledFor(logging.DEBUG): logger.debug("%s: settings read %r from %s", self.name, self._value, self._device) self._pre_read(cached) if cached and self._value is not None: return self._value if self._device.online: reply_map = {} reply = self._do_read() if reply: reply_map = self._validator.validate_read(reply) self._value = reply_map if getattr(self._device, "persister", None) and self.name not in self._device.persister: # Don't update the persister if it already has a value, # otherwise the first read might overwrite the value we wanted. self._device.persister[self.name] = self._value if self.persist else None return self._value def _do_read(self): return self._rw.read(self._device) def read_key(self, key, cached=True): return self.read(cached)[int(key)] def write(self, map, save=True): assert hasattr(self, "_value") assert hasattr(self, "_device") assert map is not None if logger.isEnabledFor(logging.DEBUG): logger.debug("%s: range field setting write %r to %s", self.name, map, self._device) if self._device.online: self.update(map, save) data_bytes = self._validator.prepare_write(self._value) if data_bytes is not None: if logger.isEnabledFor(logging.DEBUG): logger.debug("%s: range field setting prepare map write(%s) => %r", self.name, self._value, data_bytes) reply = self._rw.write(self._device, data_bytes) if not reply: return None elif logger.isEnabledFor(logging.WARNING): logger.warning("%s: range field setting no data to write", self.name) return map def write_key_value(self, key, value, save=True): assert key is not None assert value is not None if logger.isEnabledFor(logging.DEBUG): logger.debug("%s: range field setting write key %r value %r to %s", self.name, key, value, self._device) if self._device.online: if not self._value: self.read() map = self._value map[int(key)] = value self.write(map, save) return value # # read/write low-level operators # class RegisterRW: __slots__ = ("register",) kind = NamedInt(0x01, _("register")) def __init__(self, register: int): assert isinstance(register, int) self.register = register def read(self, device): return device.read_register(self.register) def write(self, device, data_bytes): return device.write_register(self.register, data_bytes) class FeatureRW: kind = NamedInt(0x02, _("feature")) default_read_fnid = 0x00 default_write_fnid = 0x10 def __init__( self, feature: hidpp20_constants.SupportedFeature, read_fnid=0x00, write_fnid=0x10, prefix=b"", suffix=b"", read_prefix=b"", no_reply=False, ): assert isinstance(feature, hidpp20_constants.SupportedFeature) self.feature = feature self.read_fnid = read_fnid self.write_fnid = write_fnid self.no_reply = no_reply self.prefix = prefix self.suffix = suffix self.read_prefix = read_prefix def read(self, device, data_bytes=b""): assert self.feature is not None return device.feature_request(self.feature, self.read_fnid, self.prefix, self.read_prefix, data_bytes) def write(self, device, data_bytes): assert self.feature is not None write_bytes = self.prefix + (data_bytes.to_bytes(1) if isinstance(data_bytes, int) else data_bytes) + self.suffix reply = device.feature_request(self.feature, self.write_fnid, write_bytes, no_reply=self.no_reply) return reply if not self.no_reply else True class FeatureRWMap(FeatureRW): kind = NamedInt(0x02, _("feature")) default_read_fnid = 0x00 default_write_fnid = 0x10 default_key_byte_count = 1 def __init__( self, feature: hidpp20_constants.SupportedFeature, read_fnid=default_read_fnid, write_fnid=default_write_fnid, key_byte_count=default_key_byte_count, no_reply=False, ): assert isinstance(feature, hidpp20_constants.SupportedFeature) self.feature = feature self.read_fnid = read_fnid self.write_fnid = write_fnid self.key_byte_count = key_byte_count self.no_reply = no_reply def read(self, device, key): assert self.feature is not None key_bytes = common.int2bytes(key, self.key_byte_count) return device.feature_request(self.feature, self.read_fnid, key_bytes) def write(self, device, key, data_bytes): assert self.feature is not None key_bytes = common.int2bytes(key, self.key_byte_count) reply = device.feature_request(self.feature, self.write_fnid, key_bytes, data_bytes, no_reply=self.no_reply) return reply if not self.no_reply else True class Validator: @classmethod def build(cls, setting_class, device, **kwargs): return cls(**kwargs) @classmethod def to_string(cls, value): return str(value) def compare(self, args, current): if len(args) != 1: return False return args[0] == current class BooleanValidator(Validator): __slots__ = ("true_value", "false_value", "read_skip_byte_count", "write_prefix_bytes", "mask", "needs_current_value") kind = KIND.toggle default_true = 0x01 default_false = 0x00 # mask specifies all the affected bits in the value default_mask = 0xFF def __init__( self, true_value=default_true, false_value=default_false, mask=default_mask, read_skip_byte_count=0, write_prefix_bytes=b"", ): if isinstance(true_value, int): assert isinstance(false_value, int) if mask is None: mask = self.default_mask else: assert isinstance(mask, int) assert true_value & false_value == 0 assert true_value & mask == true_value assert false_value & mask == false_value self.needs_current_value = mask != self.default_mask elif isinstance(true_value, bytes): if false_value is None or false_value == self.default_false: false_value = b"\x00" * len(true_value) else: assert isinstance(false_value, bytes) if mask is None or mask == self.default_mask: mask = b"\xff" * len(true_value) else: assert isinstance(mask, bytes) assert len(mask) == len(true_value) == len(false_value) tv = common.bytes2int(true_value) fv = common.bytes2int(false_value) mv = common.bytes2int(mask) assert tv != fv # true and false might be something other than bit values assert tv & mv == tv assert fv & mv == fv self.needs_current_value = any(m != 0xFF for m in mask) else: raise Exception(f"invalid mask '{mask!r}', type {type(mask)}") self.true_value = true_value self.false_value = false_value self.mask = mask self.read_skip_byte_count = read_skip_byte_count self.write_prefix_bytes = write_prefix_bytes def validate_read(self, reply_bytes): reply_bytes = reply_bytes[self.read_skip_byte_count :] if isinstance(self.mask, int): reply_value = ord(reply_bytes[:1]) & self.mask if logger.isEnabledFor(logging.DEBUG): logger.debug("BooleanValidator: validate read %r => %02X", reply_bytes, reply_value) if reply_value == self.true_value: return True if reply_value == self.false_value: return False logger.warning( "BooleanValidator: reply %02X mismatched %02X/%02X/%02X", reply_value, self.true_value, self.false_value, self.mask, ) return False count = len(self.mask) mask = common.bytes2int(self.mask) reply_value = common.bytes2int(reply_bytes[:count]) & mask true_value = common.bytes2int(self.true_value) if reply_value == true_value: return True false_value = common.bytes2int(self.false_value) if reply_value == false_value: return False logger.warning( "BooleanValidator: reply %r mismatched %r/%r/%r", reply_bytes, self.true_value, self.false_value, self.mask ) return False def prepare_write(self, new_value, current_value=None): if new_value is None: new_value = False else: assert isinstance(new_value, bool), f"New value {new_value} for boolean setting is not a boolean" to_write = self.true_value if new_value else self.false_value if isinstance(self.mask, int): if current_value is not None and self.needs_current_value: to_write |= ord(current_value[:1]) & (0xFF ^ self.mask) if current_value is not None and to_write == ord(current_value[:1]): return None to_write = bytes([to_write]) else: to_write = bytearray(to_write) count = len(self.mask) for i in range(0, count): b = ord(to_write[i : i + 1]) m = ord(self.mask[i : i + 1]) assert b & m == b # b &= m if current_value is not None and self.needs_current_value: b |= ord(current_value[i : i + 1]) & (0xFF ^ m) to_write[i] = b to_write = bytes(to_write) if current_value is not None and to_write == current_value[: len(to_write)]: return None if logger.isEnabledFor(logging.DEBUG): logger.debug("BooleanValidator: prepare_write(%s, %s) => %r", new_value, current_value, to_write) return self.write_prefix_bytes + to_write def acceptable(self, args, current): if len(args) != 1: return None val = bool_or_toggle(current, args[0]) return [val] if val is not None else None class BitFieldValidator(Validator): __slots__ = ("byte_count", "options") kind = KIND.multiple_toggle def __init__(self, options, byte_count=None): assert isinstance(options, list) self.options = options self.byte_count = (max(x.bit_length() for x in options) + 7) // 8 if byte_count: assert isinstance(byte_count, int) and byte_count >= self.byte_count self.byte_count = byte_count def to_string(self, value): def element_to_string(key, val): k = next((k for k in self.options if int(key) == k), None) return str(k) + ":" + str(val) if k is not None else "?" return "{" + ", ".join([element_to_string(k, value[k]) for k in value]) + "}" def validate_read(self, reply_bytes): r = common.bytes2int(reply_bytes[: self.byte_count]) value = {int(k): False for k in self.options} m = 1 for _ignore in range(8 * self.byte_count): if m in self.options: value[int(m)] = bool(r & m) m <<= 1 return value def prepare_write(self, new_value): assert isinstance(new_value, dict) w = 0 for k, v in new_value.items(): if v: w |= int(k) return common.int2bytes(w, self.byte_count) def get_options(self): return self.options def acceptable(self, args, current): if len(args) != 2: return None key = next((key for key in self.options if key == args[0]), None) if key is None: return None val = bool_or_toggle(current[int(key)], args[1]) return None if val is None else [int(key), val] def compare(self, args, current): if len(args) != 2: return False key = next((key for key in self.options if key == args[0]), None) if key is None: return False return args[1] == current[int(key)] class BitFieldWithOffsetAndMaskValidator(Validator): __slots__ = ("byte_count", "options", "_option_from_key", "_mask_from_offset", "_option_from_offset_mask") kind = KIND.multiple_toggle sep = 0x01 def __init__(self, options, om_method=None, byte_count=None): assert isinstance(options, list) # each element of options is an instance of a class # that has an id (which is used as an index in other dictionaries) # and where om_method is a method that returns a byte offset and byte mask # that says how to access and modify the bit toggle for the option self.options = options self.om_method = om_method # to retrieve the options efficiently: self._option_from_key = {} self._mask_from_offset = {} self._option_from_offset_mask = {} for opt in options: offset, mask = om_method(opt) self._option_from_key[int(opt)] = opt try: self._mask_from_offset[offset] |= mask except KeyError: self._mask_from_offset[offset] = mask try: mask_to_opt = self._option_from_offset_mask[offset] except KeyError: mask_to_opt = {} self._option_from_offset_mask[offset] = mask_to_opt mask_to_opt[mask] = opt self.byte_count = (max(om_method(x)[1].bit_length() for x in options) + 7) // 8 # is this correct?? if byte_count: assert isinstance(byte_count, int) and byte_count >= self.byte_count self.byte_count = byte_count def prepare_read(self): r = [] for offset, mask in self._mask_from_offset.items(): b = offset << (8 * (self.byte_count + 1)) b |= (self.sep << (8 * self.byte_count)) | mask r.append(common.int2bytes(b, self.byte_count + 2)) return r def prepare_read_key(self, key): option = self._option_from_key.get(key, None) if option is None: return None offset, mask = option.om_method(option) b = offset << (8 * (self.byte_count + 1)) b |= (self.sep << (8 * self.byte_count)) | mask return common.int2bytes(b, self.byte_count + 2) def validate_read(self, reply_bytes_dict): values = {int(k): False for k in self.options} for query, b in reply_bytes_dict.items(): offset = common.bytes2int(query[0:1]) b += (self.byte_count - len(b)) * b"\x00" value = common.bytes2int(b[: self.byte_count]) mask_to_opt = self._option_from_offset_mask.get(offset, {}) m = 1 for _ignore in range(8 * self.byte_count): if m in mask_to_opt: values[int(mask_to_opt[m])] = bool(value & m) m <<= 1 return values def prepare_write(self, new_value): assert isinstance(new_value, dict) w = {} for k, v in new_value.items(): option = self._option_from_key[int(k)] offset, mask = self.om_method(option) if offset not in w: w[offset] = 0 if v: w[offset] |= mask return [ common.int2bytes( (offset << (8 * (2 * self.byte_count + 1))) | (self.sep << (16 * self.byte_count)) | (self._mask_from_offset[offset] << (8 * self.byte_count)) | value, 2 * self.byte_count + 2, ) for offset, value in w.items() ] def get_options(self): return [int(opt) if isinstance(opt, int) else opt.as_int() for opt in self.options] def acceptable(self, args, current): if len(args) != 2: return None key = next((option.id for option in self.options if option.as_int() == args[0]), None) if key is None: return None val = bool_or_toggle(current[int(key)], args[1]) return None if val is None else [int(key), val] def compare(self, args, current): if len(args) != 2: return False key = next((option.id for option in self.options if option.as_int() == args[0]), None) if key is None: return False return args[1] == current[int(key)] class ChoicesValidator(Validator): """Translates between NamedInts and a byte sequence. :param choices: a list of NamedInts :param byte_count: the size of the derived byte sequence. If None, it will be calculated from the choices.""" kind = KIND.choice def __init__(self, choices=None, byte_count=None, read_skip_byte_count=0, write_prefix_bytes=b""): assert choices is not None assert isinstance(choices, NamedInts) assert len(choices) > 1 self.choices = choices self.needs_current_value = False max_bits = max(x.bit_length() for x in choices) self._byte_count = (max_bits // 8) + (1 if max_bits % 8 else 0) if byte_count: assert self._byte_count <= byte_count self._byte_count = byte_count assert self._byte_count < 8 self._read_skip_byte_count = read_skip_byte_count self._write_prefix_bytes = write_prefix_bytes if write_prefix_bytes else b"" assert self._byte_count + self._read_skip_byte_count <= 14 assert self._byte_count + len(self._write_prefix_bytes) <= 14 def to_string(self, value): return str(self.choices[value]) if isinstance(value, int) else str(value) def validate_read(self, reply_bytes): reply_value = common.bytes2int(reply_bytes[self._read_skip_byte_count : self._read_skip_byte_count + self._byte_count]) valid_value = self.choices[reply_value] assert valid_value is not None, f"{self.__class__.__name__}: failed to validate read value {reply_value:02X}" return valid_value def prepare_write(self, new_value, current_value=None): if new_value is None: value = self.choices[:][0] else: value = self.choice(new_value) if value is None: raise ValueError(f"invalid choice {new_value!r}") assert isinstance(value, NamedInt) return self._write_prefix_bytes + value.bytes(self._byte_count) def choice(self, value): if isinstance(value, int): return self.choices[value] try: int(value) if int(value) in self.choices: return self.choices[int(value)] except Exception: pass if value in self.choices: return self.choices[value] else: return None def acceptable(self, args, current): choice = self.choice(args[0]) if len(args) == 1 else None return None if choice is None else [choice] class ChoicesMapValidator(ChoicesValidator): kind = KIND.map_choice def __init__( self, choices_map, key_byte_count=0, key_postfix_bytes=b"", byte_count=0, read_skip_byte_count=0, write_prefix_bytes=b"", extra_default=None, mask=-1, activate=0, ): assert choices_map is not None assert isinstance(choices_map, dict) max_key_bits = 0 max_value_bits = 0 for key, choices in choices_map.items(): assert isinstance(key, NamedInt) assert isinstance(choices, NamedInts) max_key_bits = max(max_key_bits, key.bit_length()) for key_value in choices: assert isinstance(key_value, NamedInt) max_value_bits = max(max_value_bits, key_value.bit_length()) self._key_byte_count = (max_key_bits + 7) // 8 if key_byte_count: assert self._key_byte_count <= key_byte_count self._key_byte_count = key_byte_count self._byte_count = (max_value_bits + 7) // 8 if byte_count: assert self._byte_count <= byte_count self._byte_count = byte_count self.choices = choices_map self.needs_current_value = False self.extra_default = extra_default self._key_postfix_bytes = key_postfix_bytes self._read_skip_byte_count = read_skip_byte_count if read_skip_byte_count else 0 self._write_prefix_bytes = write_prefix_bytes if write_prefix_bytes else b"" self.activate = activate self.mask = mask assert self._byte_count + self._read_skip_byte_count + self._key_byte_count <= 14 assert self._byte_count + len(self._write_prefix_bytes) + self._key_byte_count <= 14 def to_string(self, value): def element_to_string(key, val): k, c = next(((k, c) for k, c in self.choices.items() if int(key) == k), (None, None)) return str(k) + ":" + str(c[val]) if k is not None else "?" return "{" + ", ".join([element_to_string(k, value[k]) for k in sorted(value)]) + "}" def validate_read(self, reply_bytes, key): start = self._key_byte_count + self._read_skip_byte_count end = start + self._byte_count reply_value = common.bytes2int(reply_bytes[start:end]) & self.mask # reprogrammable keys starts out as 0, which is not a choice, so don't use assert here if self.extra_default is not None and self.extra_default == reply_value: return int(self.choices[key][0]) if reply_value not in self.choices[key]: assert reply_value in self.choices[key], "%s: failed to validate read value %02X" % ( self.__class__.__name__, reply_value, ) return reply_value def prepare_key(self, key): return key.to_bytes(self._key_byte_count, "big") + self._key_postfix_bytes def prepare_write(self, key, new_value): choices = self.choices.get(key) if choices is None or (new_value not in choices and new_value != self.extra_default): logger.error("invalid choice %r for %s", new_value, key) return None new_value = new_value | self.activate return self._write_prefix_bytes + new_value.to_bytes(self._byte_count, "big") def acceptable(self, args, current): if len(args) != 2: return None key, choices = next(((key, item) for key, item in self.choices.items() if key == args[0]), (None, None)) if choices is None or args[1] not in choices: return None choice = next((item for item in choices if item == args[1]), None) return [int(key), int(choice)] if choice is not None else None def compare(self, args, current): if len(args) != 2: return False key = next((key for key in self.choices if key == int(args[0])), None) if key is None: return False return args[1] == current[int(key)] class RangeValidator(Validator): kind = KIND.range """Translates between integers and a byte sequence. :param min_value: minimum accepted value (inclusive) :param max_value: maximum accepted value (inclusive) :param byte_count: the size of the derived byte sequence. If None, it will be calculated from the range.""" min_value = 0 max_value = 255 @classmethod def build(cls, setting_class, device, **kwargs): kwargs["min_value"] = setting_class.min_value kwargs["max_value"] = setting_class.max_value return cls(**kwargs) def __init__(self, min_value=0, max_value=255, byte_count=1): assert max_value > min_value self.min_value = min_value self.max_value = max_value self.needs_current_value = True # read and check before write (needed for ADC power and probably a good idea anyway) self._byte_count = math.ceil(math.log(max_value + 1, 256)) if byte_count: assert self._byte_count <= byte_count self._byte_count = byte_count assert self._byte_count < 8 def validate_read(self, reply_bytes): reply_value = common.bytes2int(reply_bytes[: self._byte_count]) assert reply_value >= self.min_value, f"{self.__class__.__name__}: failed to validate read value {reply_value:02X}" assert reply_value <= self.max_value, f"{self.__class__.__name__}: failed to validate read value {reply_value:02X}" return reply_value def prepare_write(self, new_value, current_value=None): if new_value < self.min_value or new_value > self.max_value: raise ValueError(f"invalid choice {new_value!r}") current_value = self.validate_read(current_value) if current_value is not None else None to_write = common.int2bytes(new_value, self._byte_count) # current value is known and same as value to be written return None to signal not to write it return None if current_value is not None and current_value == new_value else to_write def acceptable(self, args, current): arg = args[0] # None if len(args) != 1 or type(arg) != int or arg < self.min_value or arg > self.max_value else args) return None if len(args) != 1 or isinstance(arg, int) or arg < self.min_value or arg > self.max_value else args def compare(self, args, current): if len(args) == 1: return args[0] == current elif len(args) == 2: return args[0] <= current <= args[1] else: return False class HeteroValidator(Validator): kind = KIND.hetero @classmethod def build(cls, setting_class, device, **kwargs): return cls(**kwargs) def __init__(self, data_class=None, options=None, readable=True): assert data_class is not None and options is not None self.data_class = data_class self.options = options self.readable = readable self.needs_current_value = False def validate_read(self, reply_bytes): if self.readable: reply_value = self.data_class.from_bytes(reply_bytes, options=self.options) return reply_value def prepare_write(self, new_value, current_value=None): to_write = new_value.to_bytes(options=self.options) return to_write def acceptable(self, args, current): # should this actually do some checking? return True class PackedRangeValidator(Validator): kind = KIND.packed_range """Several range values, all the same size, all the same min and max""" min_value = 0 max_value = 255 count = 1 rsbc = 0 write_prefix_bytes = b"" def __init__( self, keys, min_value=0, max_value=255, count=1, byte_count=1, read_skip_byte_count=0, write_prefix_bytes=b"" ): assert max_value > min_value self.needs_current_value = True self.keys = keys self.min_value = min_value self.max_value = max_value self.count = count self.bc = math.ceil(math.log(max_value + 1 - min(0, min_value), 256)) if byte_count: assert self.bc <= byte_count self.bc = byte_count assert self.bc * self.count self.rsbc = read_skip_byte_count self.write_prefix_bytes = write_prefix_bytes def validate_read(self, reply_bytes): rvs = { n: common.bytes2int(reply_bytes[self.rsbc + n * self.bc : self.rsbc + (n + 1) * self.bc], signed=True) for n in range(self.count) } for n in range(self.count): assert rvs[n] >= self.min_value, f"{self.__class__.__name__}: failed to validate read value {rvs[n]:02X}" assert rvs[n] <= self.max_value, f"{self.__class__.__name__}: failed to validate read value {rvs[n]:02X}" return rvs def prepare_write(self, new_values): if len(new_values) != self.count: raise ValueError(f"wrong number of values {new_values!r}") for new_value in new_values.values(): if new_value < self.min_value or new_value > self.max_value: raise ValueError(f"invalid value {new_value!r}") bytes = self.write_prefix_bytes + b"".join( common.int2bytes(new_values[n], self.bc, signed=True) for n in range(self.count) ) return bytes def acceptable(self, args, current): if len(args) != 2 or int(args[0]) < 0 or int(args[0]) >= self.count: return None return None if not isinstance(args[1], int) or args[1] < self.min_value or args[1] > self.max_value else args def compare(self, args, current): logger.warning("compare not implemented for packed range settings") return False class MultipleRangeValidator(Validator): kind = KIND.multiple_range def __init__(self, items, sub_items): assert isinstance(items, list) # each element must have .index and its __int__ must return its id (not its index) assert isinstance(sub_items, dict) # sub_items: items -> class with .minimum, .maximum, .length (in bytes), .id (a string) and .widget (e.g. 'Scale') self.items = items self.keys = NamedInts(**{str(item): int(item) for item in items}) self._item_from_id = {int(k): k for k in items} self.sub_items = sub_items def prepare_read_item(self, item): return common.int2bytes((self._item_from_id[int(item)].index << 1) | 0xFF, 2) def validate_read_item(self, reply_bytes, item): item = self._item_from_id[int(item)] start = 0 value = {} for sub_item in self.sub_items[item]: r = reply_bytes[start : start + sub_item.length] if len(r) < sub_item.length: r += b"\x00" * (sub_item.length - len(value)) v = common.bytes2int(r) if not (sub_item.minimum < v < sub_item.maximum): logger.warning( f"{self.__class__.__name__}: failed to validate read value for {item}.{sub_item}: " + f"{v} not in [{sub_item.minimum}..{sub_item.maximum}]" ) value[str(sub_item)] = v start += sub_item.length return value def prepare_write(self, value): seq = [] w = b"" for item in value.keys(): _item = self._item_from_id[int(item)] b = common.int2bytes(_item.index, 1) for sub_item in self.sub_items[_item]: try: v = value[int(item)][str(sub_item)] except KeyError: return None if not (sub_item.minimum <= v <= sub_item.maximum): raise ValueError( f"invalid choice for {item}.{sub_item}: {v} not in [{sub_item.minimum}..{sub_item.maximum}]" ) b += common.int2bytes(v, sub_item.length) if len(w) + len(b) > 15: seq.append(b + b"\xff") w = b"" w += b seq.append(w + b"\xff") return seq def prepare_write_item(self, item, value): _item = self._item_from_id[int(item)] w = common.int2bytes(_item.index, 1) for sub_item in self.sub_items[_item]: try: v = value[str(sub_item)] except KeyError: return None if not (sub_item.minimum <= v <= sub_item.maximum): raise ValueError(f"invalid choice for {item}.{sub_item}: {v} not in [{sub_item.minimum}..{sub_item.maximum}]") w += common.int2bytes(v, sub_item.length) return w + b"\xff" def acceptable(self, args, current): # just one item, with at least one sub-item if not isinstance(args, list) or len(args) != 2 or not isinstance(args[1], dict): return None item = next((p for p in self.items if p.id == args[0] or str(p) == args[0]), None) if not item: return None for sub_key, value in args[1].items(): sub_item = next((it for it in self.sub_items[item] if it.id == sub_key), None) if not sub_item: return None if not isinstance(value, int) or not (sub_item.minimum <= value <= sub_item.maximum): return None return [int(item), {**args[1]}] def compare(self, args, current): logger.warning("compare not implemented for multiple range settings") return False class ActionSettingRW: """Special RW class for settings that turn on and off special processing when a key or button is depressed""" def __init__(self, feature, name="", divert_setting_name="divert-keys"): self.feature = feature # not used? self.name = name self.divert_setting_name = divert_setting_name self.kind = FeatureRW.kind # pretend to be FeatureRW as required for HID++ 2.0 devices self.device = None self.key = None self.active = False self.pressed = False def activate_action(self): # action to take when setting is activated (write non-false) pass def deactivate_action(self): # action to take when setting is deactivated (write false) pass def press_action(self): # action to take when key is pressed pass def release_action(self): # action to take when key is released pass def move_action(self, dx, dy): # action to take when mouse is moved while key is down pass def key_action(self, key): # acction to take when some other diverted key is pressed pass def read(self, device): # need to return bytes, as if read from device return common.int2bytes(self.key.key, 2) if self.active and self.key else b"\x00\x00" def write(self, device, data_bytes): def handler(device, n): # Called on notification events from the device if ( n.sub_id < 0x40 and device.features.get_feature(n.sub_id) == hidpp20_constants.SupportedFeature.REPROG_CONTROLS_V4 ): if n.address == 0x00: cids = struct.unpack("!HHHH", n.data[:8]) if not self.pressed and int(self.key.key) in cids: # trigger key pressed self.pressed = True self.press_action() elif self.pressed: if int(self.key.key) not in cids: # trigger key released self.pressed = False self.release_action() else: for key in cids: if key and not key == self.key.key: # some other diverted key pressed self.key_action(key) elif n.address == 0x10: if self.pressed: dx, dy = struct.unpack("!hh", n.data[:4]) self.move_action(dx, dy) divertSetting = next(filter(lambda s: s.name == self.divert_setting_name, device.settings), None) if divertSetting is None: logger.warning("setting %s not found on %s", self.divert_setting_name, device.name) return None self.device = device key = common.bytes2int(data_bytes) if key: # Enable self.key = next((k for k in device.keys if k.key == key), None) if self.key: self.active = True if divertSetting: divertSetting.write_key_value(int(self.key.key), 1) if self.device.setting_callback: self.device.setting_callback(device, type(divertSetting), [self.key.key, 1]) device.add_notification_handler(self.name, handler) self.activate_action() else: logger.error("cannot enable %s on %s for key %s", self.name, device, key) else: # Disable if self.active: self.active = False if divertSetting: divertSetting.write_key_value(int(self.key.key), 0) if self.device.setting_callback: self.device.setting_callback(device, type(divertSetting), [self.key.key, 0]) try: device.remove_notification_handler(self.name) except Exception: if logger.isEnabledFor(logging.WARNING): logger.warning("cannot disable %s on %s", self.name, device) self.deactivate_action() return data_bytes class RawXYProcessing: """Special class for processing RawXY action messages initiated by pressing a key with rawXY diversion capability""" def __init__(self, device, name=""): self.device = device self.name = name self.keys = [] # the keys that can initiate processing self.initiating_key = None # the key that did initiate processing self.active = False self.feature_offset = device.features[hidpp20_constants.SupportedFeature.REPROG_CONTROLS_V4] assert self.feature_offset is not False def handler(self, device, n): # Called on notification events from the device if n.sub_id < 0x40 and device.features.get_feature(n.sub_id) == hidpp20_constants.SupportedFeature.REPROG_CONTROLS_V4: if n.address == 0x00: cids = struct.unpack("!HHHH", n.data[:8]) ## generalize to list of keys if not self.initiating_key: # no initiating key pressed for k in self.keys: if int(k.key) in cids: # initiating key that was pressed self.initiating_key = k if self.initiating_key: self.press_action(self.initiating_key) else: if int(self.initiating_key.key) not in cids: # initiating key released self.initiating_key = None self.release_action() else: for key in cids: if key and key != self.initiating_key.key: self.key_action(key) elif n.address == 0x10: if self.initiating_key: dx, dy = struct.unpack("!hh", n.data[:4]) self.move_action(dx, dy) def start(self, key): device_key = next((k for k in self.device.keys if k.key == key), None) if device_key: self.keys.append(device_key) if not self.active: self.active = True self.activate_action() self.device.add_notification_handler(self.name, self.handler) device_key.set_rawXY_reporting(True) def stop(self, key): # only stop if this is the active key if self.active: processing_key = next((k for k in self.keys if k.key == key), None) if processing_key: processing_key.set_rawXY_reporting(False) self.keys.remove(processing_key) if not self.keys: try: self.device.remove_notification_handler(self.name) except Exception: if logger.isEnabledFor(logging.WARNING): logger.warning("cannot disable %s on %s", self.name, self.device) self.deactivate_action() self.active = False def activate_action(self): # action to take when processing is activated pass def deactivate_action(self): # action to take when processing is deactivated pass def press_action(self, key): # action to take when an initiating key is pressed pass def release_action(self): # action to take when key is released pass def move_action(self, dx, dy): # action to take when mouse is moved while key is down pass def key_action(self, key): # acction to take when some other diverted key is pressed pass def apply_all_settings(device): if device.features and hidpp20_constants.SupportedFeature.HIRES_WHEEL in device.features: time.sleep(0.2) # delay to try to get out of race condition with Linux HID++ driver persister = getattr(device, "persister", None) sensitives = persister.get("_sensitive", {}) if persister else {} for s in device.settings: ignore = sensitives.get(s.name, False) if ignore != SENSITIVITY_IGNORE: s.apply() Setting.validator_class = BooleanValidator Solaar-1.1.14/lib/logitech_receiver/settings_templates.py000066400000000000000000002377701473525213700235700ustar00rootroot00000000000000## Copyright (C) 2012-2013 Daniel Pavel ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. from __future__ import annotations import enum import logging import socket import struct import traceback from time import time from typing import Any from typing import Callable from solaar.i18n import _ from . import base from . import common from . import descriptors from . import desktop_notifications from . import diversion from . import hidpp10_constants from . import hidpp20 from . import hidpp20_constants from . import settings from . import special_keys from .hidpp10_constants import Registers from .hidpp20_constants import GestureId from .hidpp20_constants import ParamId logger = logging.getLogger(__name__) _hidpp20 = hidpp20.Hidpp20() _DK = hidpp10_constants.DEVICE_KIND _F = hidpp20_constants.SupportedFeature class State(enum.Enum): IDLE = "idle" PRESSED = "pressed" MOVED = "moved" # Setting classes are used to control the settings that the Solaar GUI shows and manipulates. # Each setting class has to several class variables: # name, which is used as a key when storing information about the setting, # setting classes can have the same name, as long as devices only have one setting with the same name; # label, which is shown in the Solaar main window; # description, which is shown when the mouse hovers over the setting in the main window; # either register or feature, the register or feature that the setting uses; # rw_class, the class of the reader/writer (if it is not the standard one), # rw_options, a dictionary of options for the reader/writer. # validator_class, the class of the validator (default settings.BooleanValidator) # validator_options, a dictionary of options for the validator # persist (inherited True), which is whether to store the value and apply it when setting up the device. # # The different setting classes imported from settings.py are for different numbers and kinds of arguments. # Setting is for settings with a single value (boolean, number in a range, and symbolic choice). # Settings is for settings that are maps from keys to values # and permit reading or writing the entire map or just one key/value pair. # The BitFieldSetting class is for settings that have multiple boolean values packed into a bit field. # BitFieldWithOffsetAndMaskSetting is similar. # The RangeFieldSetting class is for settings that have multiple ranges packed into a byte string. # LongSettings is for settings that have an even more complex structure. # # When settings are created a reader/writer and a validator are created. # If the setting class has a value for rw_class then an instance of that class is created. # Otherwise if the setting has a register then an instance of RegisterRW is created. # and if the setting has a feature then an instance of FeatureRW is created. # The instance is created with the register or feature as the first argument and rw_options as keyword arguments. # RegisterRW doesn't use any options. # FeatureRW options include # read_fnid - the feature function (times 16) to read the value (default 0x00), # write_fnid - the feature function (times 16) to write the value (default 0x10), # prefix - a prefix to add to the data being written and the read request (default b''), used for features # that provide and set multiple settings (e.g., to read and write function key inversion for current host) # no_reply - whether to wait for a reply (default false) (USE WITH EXTREME CAUTION). # # There are three simple validator classes - BooleanV, RangeValidator, and ChoicesValidator # BooleanV is for boolean values and is the default. It takes # true_value is the raw value for true (default 0x01), this can be an integer or a byte string, # false_value is the raw value for false (default 0x00), this can be an integer or a byte string, # mask is used to keep only some bits from a sequence of bits, this can be an integer or a byte string, # read_skip_byte_count is the number of bytes to ignore at the beginning of the read value (default 0), # write_prefix_bytes is a byte string to write before the value (default empty). # RangeValidator is for an integer in a range. It takes # byte_count is number of bytes that the value is stored in (defaults to size of max_value). # RangeValidator uses min_value and max_value from the setting class as minimum and maximum. # ChoicesValidator is for symbolic choices. It takes one positional and three keyword arguments: # choices is a list of named integers that are the valid choices, # byte_count is the number of bytes for the integer (default size of largest choice integer), # read_skip_byte_count is as for BooleanV, # write_prefix_bytes is as for BooleanV. # Settings that use ChoicesValidator should have a choices_universe class variable of the potential choices, # or None for no limitation and optionally a choices_extra class variable with an extra choice. # The choices_extra is so that there is no need to specially extend a large existing NamedInts. # ChoicesMapValidator validator is for map settings that map onto symbolic choices. It takes # choices_map is a map from keys to possible values # byte_count is as for ChoicesValidator, # read_skip_byte_count is as for ChoicesValidator, # write_prefix_bytes is as for ChoicesValidator, # key_byte_count is the number of bytes for the key integer (default size of largest key), # extra_default is an extra raw value that is used as a default value (default None). # Settings that use ChoicesValidator should have keys_universe and choices_universe class variable of # the potential keys and potential choices or None for no limitation. # BitFieldValidator validator is for bit field settings. It takes one positional and one keyword argument # the positional argument is the number of bits in the bit field # byte_count is the size of the bit field (default size of the bit field) # # A few settings work very differently. They divert a key, which is then used to start and stop some special action. # These settings have reader/writer classes that perform special processing instead of sending commands to the device. class FnSwapVirtual(settings.Setting): # virtual setting to hold fn swap strings name = "fn-swap" label = _("Swap Fx function") description = ( _( "When set, the F1..F12 keys will activate their special function,\n" "and you must hold the FN key to activate their standard function." ) + "\n\n" + _( "When unset, the F1..F12 keys will activate their standard function,\n" "and you must hold the FN key to activate their special function." ) ) class RegisterHandDetection(settings.Setting): name = "hand-detection" label = _("Hand Detection") description = _("Turn on illumination when the hands hover over the keyboard.") register = Registers.KEYBOARD_HAND_DETECTION validator_options = {"true_value": b"\x00\x00\x00", "false_value": b"\x00\x00\x30", "mask": b"\x00\x00\xff"} class RegisterSmoothScroll(settings.Setting): name = "smooth-scroll" label = _("Scroll Wheel Smooth Scrolling") description = _("High-sensitivity mode for vertical scroll with the wheel.") register = Registers.MOUSE_BUTTON_FLAGS validator_options = {"true_value": 0x40, "mask": 0x40} class RegisterSideScroll(settings.Setting): name = "side-scroll" label = _("Side Scrolling") description = _( "When disabled, pushing the wheel sideways sends custom button events\n" "instead of the standard side-scrolling events." ) register = Registers.MOUSE_BUTTON_FLAGS validator_options = {"true_value": 0x02, "mask": 0x02} # different devices have different sets of permissible dpis, so this should be subclassed class RegisterDpi(settings.Setting): name = "dpi-old" label = _("Sensitivity (DPI - older mice)") description = _("Mouse movement sensitivity") register = Registers.MOUSE_DPI choices_universe = common.NamedInts.range(0x81, 0x8F, lambda x: str((x - 0x80) * 100)) validator_class = settings.ChoicesValidator validator_options = {"choices": choices_universe} class RegisterFnSwap(FnSwapVirtual): register = Registers.KEYBOARD_FN_SWAP validator_options = {"true_value": b"\x00\x01", "mask": b"\x00\x01"} class _PerformanceMXDpi(RegisterDpi): choices_universe = common.NamedInts.range(0x81, 0x8F, lambda x: str((x - 0x80) * 100)) validator_options = {"choices": choices_universe} # set up register settings for devices - this is done here to break up an import loop descriptors.get_wpid("0060").settings = [RegisterFnSwap] descriptors.get_wpid("2008").settings = [RegisterFnSwap] descriptors.get_wpid("2010").settings = [RegisterFnSwap, RegisterHandDetection] descriptors.get_wpid("2011").settings = [RegisterFnSwap] descriptors.get_usbid(0xC318).settings = [RegisterFnSwap] descriptors.get_wpid("C714").settings = [RegisterFnSwap] descriptors.get_wpid("100B").settings = [RegisterSmoothScroll, RegisterSideScroll] descriptors.get_wpid("100F").settings = [RegisterSmoothScroll, RegisterSideScroll] descriptors.get_wpid("1013").settings = [RegisterSmoothScroll, RegisterSideScroll] descriptors.get_wpid("1014").settings = [RegisterSmoothScroll, RegisterSideScroll] descriptors.get_wpid("1017").settings = [RegisterSmoothScroll, RegisterSideScroll] descriptors.get_wpid("1023").settings = [RegisterSmoothScroll, RegisterSideScroll] descriptors.get_wpid("4004").settings = [_PerformanceMXDpi, RegisterSmoothScroll, RegisterSideScroll] descriptors.get_wpid("101A").settings = [_PerformanceMXDpi, RegisterSmoothScroll, RegisterSideScroll] descriptors.get_wpid("101B").settings = [RegisterSmoothScroll, RegisterSideScroll] descriptors.get_wpid("101D").settings = [RegisterSmoothScroll, RegisterSideScroll] descriptors.get_wpid("101F").settings = [RegisterSideScroll] descriptors.get_usbid(0xC06B).settings = [RegisterSmoothScroll, RegisterSideScroll] descriptors.get_wpid("1025").settings = [RegisterSideScroll] descriptors.get_wpid("102A").settings = [RegisterSmoothScroll, RegisterSideScroll] descriptors.get_usbid(0xC048).settings = [_PerformanceMXDpi, RegisterSmoothScroll, RegisterSideScroll] descriptors.get_usbid(0xC066).settings = [_PerformanceMXDpi, RegisterSmoothScroll, RegisterSideScroll] # ignore the capabilities part of the feature - all devices should be able to swap Fn state # can't just use the first byte = 0xFF (for current host) because of a bug in the firmware of the MX Keys S class K375sFnSwap(FnSwapVirtual): feature = _F.K375S_FN_INVERSION validator_options = {"true_value": b"\x01", "false_value": b"\x00", "read_skip_byte_count": 1} class rw_class(settings.FeatureRW): def find_current_host(self, device): if not self.prefix: response = device.feature_request(_F.HOSTS_INFO, 0x00) self.prefix = response[3:4] if response else b"\xff" def read(self, device, data_bytes=b""): self.find_current_host(device) return super().read(device, data_bytes) def write(self, device, data_bytes): self.find_current_host(device) return super().write(device, data_bytes) class FnSwap(FnSwapVirtual): feature = _F.FN_INVERSION class NewFnSwap(FnSwapVirtual): feature = _F.NEW_FN_INVERSION class Backlight(settings.Setting): name = "backlight-qualitative" label = _("Backlight Timed") description = _("Set illumination time for keyboard.") feature = _F.BACKLIGHT choices_universe = common.NamedInts(Off=0, Varying=2, VeryShort=5, Short=10, Medium=20, Long=60, VeryLong=180) validator_class = settings.ChoicesValidator validator_options = {"choices": choices_universe} # MX Keys S requires some extra values, as in 11 02 0c1a 000dff000b000b003c00000000000000 # on/off options (from current) effect (FF-no change) level (from current) durations[6] (from current) class Backlight2(settings.Setting): name = "backlight" label = _("Backlight") description = _("Illumination level on keyboard. Changes made are only applied in Manual mode.") feature = _F.BACKLIGHT2 choices_universe = common.NamedInts(Disabled=0xFF, Enabled=0x00, Automatic=0x01, Manual=0x02) min_version = 0 class rw_class: def __init__(self, feature): self.feature = feature self.kind = settings.FeatureRW.kind def read(self, device): backlight = device.backlight if not backlight.enabled: return b"\xff" else: return common.int2bytes(backlight.mode, 1) def write(self, device, data_bytes): backlight = device.backlight backlight.enabled = data_bytes[0] != 0xFF if data_bytes[0] != 0xFF: backlight.mode = data_bytes[0] backlight.write() return True class validator_class(settings.ChoicesValidator): @classmethod def build(cls, setting_class, device): backlight = device.backlight choices = common.NamedInts() choices[0xFF] = _("Disabled") if backlight.auto_supported: choices[0x1] = _("Automatic") if backlight.perm_supported: choices[0x3] = _("Manual") if not (backlight.auto_supported or backlight.temp_supported or backlight.perm_supported): choices[0x0] = _("Enabled") return cls(choices=choices, byte_count=1) class Backlight2Level(settings.Setting): name = "backlight_level" label = _("Backlight Level") description = _("Illumination level on keyboard when in Manual mode.") feature = _F.BACKLIGHT2 min_version = 3 class rw_class: def __init__(self, feature): self.feature = feature self.kind = settings.FeatureRW.kind def read(self, device): backlight = device.backlight return common.int2bytes(backlight.level, 1) def write(self, device, data_bytes): if device.backlight.level != common.bytes2int(data_bytes): device.backlight.level = common.bytes2int(data_bytes) device.backlight.write() return True class validator_class(settings.RangeValidator): @classmethod def build(cls, setting_class, device): reply = device.feature_request(_F.BACKLIGHT2, 0x20) assert reply, "Oops, backlight range cannot be retrieved!" if reply[0] > 1: return cls(min_value=0, max_value=reply[0] - 1, byte_count=1) class Backlight2Duration(settings.Setting): feature = _F.BACKLIGHT2 min_version = 3 validator_class = settings.RangeValidator min_value = 1 max_value = 600 # 10 minutes - actual maximum is 2 hours validator_options = {"byte_count": 2} class rw_class: def __init__(self, feature, field): self.feature = feature self.kind = settings.FeatureRW.kind self.field = field def read(self, device): backlight = device.backlight return common.int2bytes(getattr(backlight, self.field) * 5, 2) # use seconds instead of 5-second units def write(self, device, data_bytes): backlight = device.backlight new_duration = (int.from_bytes(data_bytes, byteorder="big") + 4) // 5 # use ceiling in 5-second units if new_duration != getattr(backlight, self.field): setattr(backlight, self.field, new_duration) backlight.write() return True class Backlight2DurationHandsOut(Backlight2Duration): name = "backlight_duration_hands_out" label = _("Backlight Delay Hands Out") description = _("Delay in seconds until backlight fades out with hands away from keyboard.") feature = _F.BACKLIGHT2 validator_class = settings.RangeValidator rw_options = {"field": "dho"} class Backlight2DurationHandsIn(Backlight2Duration): name = "backlight_duration_hands_in" label = _("Backlight Delay Hands In") description = _("Delay in seconds until backlight fades out with hands near keyboard.") feature = _F.BACKLIGHT2 validator_class = settings.RangeValidator rw_options = {"field": "dhi"} class Backlight2DurationPowered(Backlight2Duration): name = "backlight_duration_powered" label = _("Backlight Delay Powered") description = _("Delay in seconds until backlight fades out with external power.") feature = _F.BACKLIGHT2 validator_class = settings.RangeValidator rw_options = {"field": "dpow"} class Backlight3(settings.Setting): name = "backlight-timed" label = _("Backlight (Seconds)") description = _("Set illumination time for keyboard.") feature = _F.BACKLIGHT3 rw_options = {"read_fnid": 0x10, "write_fnid": 0x20, "suffix": b"\x09"} validator_class = settings.RangeValidator min_value = 0 max_value = 1000 validator_options = {"byte_count": 2} class HiResScroll(settings.Setting): name = "hi-res-scroll" label = _("Scroll Wheel High Resolution") description = ( _("High-sensitivity mode for vertical scroll with the wheel.") + "\n" + _("Set to ignore if scrolling is abnormally fast or slow") ) feature = _F.HI_RES_SCROLLING class LowresMode(settings.Setting): name = "lowres-scroll-mode" label = _("Scroll Wheel Diversion") description = _( "Make scroll wheel send LOWRES_WHEEL HID++ notifications (which trigger Solaar rules but are otherwise ignored)." ) feature = _F.LOWRES_WHEEL class HiresSmoothInvert(settings.Setting): name = "hires-smooth-invert" label = _("Scroll Wheel Direction") description = _("Invert direction for vertical scroll with wheel.") feature = _F.HIRES_WHEEL rw_options = {"read_fnid": 0x10, "write_fnid": 0x20} validator_options = {"true_value": 0x04, "mask": 0x04} class HiresSmoothResolution(settings.Setting): name = "hires-smooth-resolution" label = _("Scroll Wheel Resolution") description = ( _("High-sensitivity mode for vertical scroll with the wheel.") + "\n" + _("Set to ignore if scrolling is abnormally fast or slow") ) feature = _F.HIRES_WHEEL rw_options = {"read_fnid": 0x10, "write_fnid": 0x20} validator_options = {"true_value": 0x02, "mask": 0x02} class HiresMode(settings.Setting): name = "hires-scroll-mode" label = _("Scroll Wheel Diversion") description = _( "Make scroll wheel send HIRES_WHEEL HID++ notifications (which trigger Solaar rules but are otherwise ignored)." ) feature = _F.HIRES_WHEEL rw_options = {"read_fnid": 0x10, "write_fnid": 0x20} validator_options = {"true_value": 0x01, "mask": 0x01} class PointerSpeed(settings.Setting): name = "pointer_speed" label = _("Sensitivity (Pointer Speed)") description = _("Speed multiplier for mouse (256 is normal multiplier).") feature = _F.POINTER_SPEED validator_class = settings.RangeValidator min_value = 0x002E max_value = 0x01FF validator_options = {"byte_count": 2} class ThumbMode(settings.Setting): name = "thumb-scroll-mode" label = _("Thumb Wheel Diversion") description = _( "Make thumb wheel send THUMB_WHEEL HID++ notifications (which trigger Solaar rules but are otherwise ignored)." ) feature = _F.THUMB_WHEEL rw_options = {"read_fnid": 0x10, "write_fnid": 0x20} validator_options = {"true_value": b"\x01\x00", "false_value": b"\x00\x00", "mask": b"\x01\x00"} class ThumbInvert(settings.Setting): name = "thumb-scroll-invert" label = _("Thumb Wheel Direction") description = _("Invert thumb wheel scroll direction.") feature = _F.THUMB_WHEEL rw_options = {"read_fnid": 0x10, "write_fnid": 0x20} validator_options = {"true_value": b"\x00\x01", "false_value": b"\x00\x00", "mask": b"\x00\x01"} # change UI to show result of onboard profile change def profile_change(device, profile_sector): if device.setting_callback: device.setting_callback(device, OnboardProfiles, [profile_sector]) for profile in device.profiles.profiles.values() if device.profiles else []: if profile.sector == profile_sector: resolution_index = profile.resolution_default_index device.setting_callback(device, AdjustableDpi, [profile.resolutions[resolution_index]]) device.setting_callback(device, ReportRate, [profile.report_rate]) break class OnboardProfiles(settings.Setting): name = "onboard_profiles" label = _("Onboard Profiles") description = _("Enable an onboard profile, which controls report rate, sensitivity, and button actions") feature = _F.ONBOARD_PROFILES choices_universe = common.NamedInts(Disabled=0) for i in range(1, 16): choices_universe[i] = f"Profile {i}" choices_universe[i + 0x100] = f"Read-Only Profile {i}" validator_class = settings.ChoicesValidator class rw_class: def __init__(self, feature): self.feature = feature self.kind = settings.FeatureRW.kind def read(self, device): enabled = device.feature_request(_F.ONBOARD_PROFILES, 0x20)[0] if enabled == 0x01: active = device.feature_request(_F.ONBOARD_PROFILES, 0x40) return active[:2] else: return b"\x00\x00" def write(self, device, data_bytes): if data_bytes == b"\x00\x00": result = device.feature_request(_F.ONBOARD_PROFILES, 0x10, b"\x02") else: device.feature_request(_F.ONBOARD_PROFILES, 0x10, b"\x01") result = device.feature_request(_F.ONBOARD_PROFILES, 0x30, data_bytes) profile_change(device, common.bytes2int(data_bytes)) return result class validator_class(settings.ChoicesValidator): @classmethod def build(cls, setting_class, device): headers = hidpp20.OnboardProfiles.get_profile_headers(device) profiles_list = [setting_class.choices_universe[0]] if headers: for sector, enabled in headers: if enabled and setting_class.choices_universe[sector]: profiles_list.append(setting_class.choices_universe[sector]) return cls(choices=common.NamedInts.list(profiles_list), byte_count=2) if len(profiles_list) > 1 else None class ReportRate(settings.Setting): name = "report_rate" label = _("Report Rate") description = ( _("Frequency of device movement reports") + "\n" + _("May need Onboard Profiles set to Disable to be effective.") ) feature = _F.REPORT_RATE rw_options = {"read_fnid": 0x10, "write_fnid": 0x20} choices_universe = common.NamedInts() choices_universe[1] = "1ms" choices_universe[2] = "2ms" choices_universe[3] = "3ms" choices_universe[4] = "4ms" choices_universe[5] = "5ms" choices_universe[6] = "6ms" choices_universe[7] = "7ms" choices_universe[8] = "8ms" class validator_class(settings.ChoicesValidator): @classmethod def build(cls, setting_class, device): # if device.wpid == '408E': # return None # host mode borks the function keys on the G915 TKL keyboard reply = device.feature_request(_F.REPORT_RATE, 0x00) assert reply, "Oops, report rate choices cannot be retrieved!" rate_list = [] rate_flags = common.bytes2int(reply[0:1]) for i in range(0, 8): if (rate_flags >> i) & 0x01: rate_list.append(setting_class.choices_universe[i + 1]) return cls(choices=common.NamedInts.list(rate_list), byte_count=1) if rate_list else None class ExtendedReportRate(settings.Setting): name = "report_rate_extended" label = _("Report Rate") description = ( _("Frequency of device movement reports") + "\n" + _("May need Onboard Profiles set to Disable to be effective.") ) feature = _F.EXTENDED_ADJUSTABLE_REPORT_RATE rw_options = {"read_fnid": 0x20, "write_fnid": 0x30} choices_universe = common.NamedInts() choices_universe[0] = "8ms" choices_universe[1] = "4ms" choices_universe[2] = "2ms" choices_universe[3] = "1ms" choices_universe[4] = "500us" choices_universe[5] = "250us" choices_universe[6] = "125us" class validator_class(settings.ChoicesValidator): @classmethod def build(cls, setting_class, device): reply = device.feature_request(_F.EXTENDED_ADJUSTABLE_REPORT_RATE, 0x10) assert reply, "Oops, report rate choices cannot be retrieved!" rate_list = [] rate_flags = common.bytes2int(reply[0:2]) for i in range(0, 7): if rate_flags & (0x01 << i): rate_list.append(setting_class.choices_universe[i]) return cls(choices=common.NamedInts.list(rate_list), byte_count=1) if rate_list else None class DivertCrown(settings.Setting): name = "divert-crown" label = _("Divert crown events") description = _("Make crown send CROWN HID++ notifications (which trigger Solaar rules but are otherwise ignored).") feature = _F.CROWN rw_options = {"read_fnid": 0x10, "write_fnid": 0x20} validator_options = {"true_value": 0x02, "false_value": 0x01, "mask": 0xFF} class CrownSmooth(settings.Setting): name = "crown-smooth" label = _("Crown smooth scroll") description = _("Set crown smooth scroll") feature = _F.CROWN rw_options = {"read_fnid": 0x10, "write_fnid": 0x20} validator_options = {"true_value": 0x01, "false_value": 0x02, "read_skip_byte_count": 1, "write_prefix_bytes": b"\x00"} class DivertGkeys(settings.Setting): name = "divert-gkeys" label = _("Divert G and M Keys") description = _("Make G and M keys send HID++ notifications (which trigger Solaar rules but are otherwise ignored).") feature = _F.GKEY validator_options = {"true_value": 0x01, "false_value": 0x00, "mask": 0xFF} class rw_class(settings.FeatureRW): def __init__(self, feature): super().__init__(feature, write_fnid=0x20) def read(self, device): # no way to read, so just assume not diverted return b"\x00" class ScrollRatchet(settings.Setting): name = "scroll-ratchet" label = _("Scroll Wheel Ratcheted") description = _("Switch the mouse wheel between speed-controlled ratcheting and always freespin.") feature = _F.SMART_SHIFT choices_universe = common.NamedInts(**{_("Freespinning"): 1, _("Ratcheted"): 2}) validator_class = settings.ChoicesValidator validator_options = {"choices": choices_universe} class SmartShift(settings.Setting): name = "smart-shift" label = _("Scroll Wheel Ratchet Speed") description = _( "Use the mouse wheel speed to switch between ratcheted and freespinning.\n" "The mouse wheel is always ratcheted at 50." ) feature = _F.SMART_SHIFT rw_options = {"read_fnid": 0x00, "write_fnid": 0x10} class rw_class(settings.FeatureRW): MIN_VALUE = 1 MAX_VALUE = 50 def __init__(self, feature, read_fnid, write_fnid): super().__init__(feature, read_fnid, write_fnid) def read(self, device): value = super().read(device) if common.bytes2int(value[0:1]) == 1: # Mode = Freespin, map to minimum return common.int2bytes(self.MIN_VALUE, count=1) else: # Mode = smart shift, map to the value, capped at maximum threshold = min(common.bytes2int(value[1:2]), self.MAX_VALUE) return common.int2bytes(threshold, count=1) def write(self, device, data_bytes): threshold = common.bytes2int(data_bytes) # Freespin at minimum mode = 0 # 1 if threshold <= self.MIN_VALUE else 2 # Ratchet at maximum if threshold >= self.MAX_VALUE: threshold = 255 data = common.int2bytes(mode, count=1) + common.int2bytes(max(0, threshold), count=1) return super().write(device, data) min_value = rw_class.MIN_VALUE max_value = rw_class.MAX_VALUE validator_class = settings.RangeValidator class SmartShiftEnhanced(SmartShift): feature = _F.SMART_SHIFT_ENHANCED rw_options = {"read_fnid": 0x10, "write_fnid": 0x20} class ScrollRatchetEnhanced(ScrollRatchet): feature = _F.SMART_SHIFT_ENHANCED rw_options = {"read_fnid": 0x10, "write_fnid": 0x20} # the keys for the choice map are Logitech controls (from special_keys) # each choice value is a NamedInt with the string from a task (to be shown to the user) # and the integer being the control number for that task (to be written to the device) # Solaar only remaps keys (controlled by key gmask and group), not other key reprogramming class ReprogrammableKeys(settings.Settings): name = "reprogrammable-keys" label = _("Key/Button Actions") description = ( _("Change the action for the key or button.") + " " + _("Overridden by diversion.") + "\n" + _("Changing important actions (such as for the left mouse button) can result in an unusable system.") ) feature = _F.REPROG_CONTROLS_V4 keys_universe = special_keys.CONTROL choices_universe = special_keys.CONTROL class rw_class: def __init__(self, feature): self.feature = feature self.kind = settings.FeatureRW.kind def read(self, device, key): key_index = device.keys.index(key) key_struct = device.keys[key_index] return b"\x00\x00" + common.int2bytes(int(key_struct.mapped_to), 2) def write(self, device, key, data_bytes): key_index = device.keys.index(key) key_struct = device.keys[key_index] key_struct.remap(special_keys.CONTROL[common.bytes2int(data_bytes)]) return True class validator_class(settings.ChoicesMapValidator): @classmethod def build(cls, setting_class, device): choices = {} if device.keys: for k in device.keys: tgts = k.remappable_to if len(tgts) > 1: choices[k.key] = tgts return cls(choices, key_byte_count=2, byte_count=2, extra_default=0) if choices else None class DpiSlidingXY(settings.RawXYProcessing): def __init__( self, *args, show_notification: Callable[[str, str], bool], **kwargs, ): super().__init__(*args, **kwargs) self.fsmState = None self._show_notification = show_notification def activate_action(self): self.dpiSetting = next(filter(lambda s: s.name == "dpi" or s.name == "dpi_extended", self.device.settings), None) self.dpiChoices = list(self.dpiSetting.choices) self.otherDpiIdx = self.device.persister.get("_dpi-sliding", -1) if self.device.persister else -1 if not isinstance(self.otherDpiIdx, int) or self.otherDpiIdx < 0 or self.otherDpiIdx >= len(self.dpiChoices): self.otherDpiIdx = self.dpiChoices.index(self.dpiSetting.read()) self.fsmState = State.IDLE self.dx = 0.0 self.movingDpiIdx = None def setNewDpi(self, newDpiIdx): newDpi = self.dpiChoices[newDpiIdx] self.dpiSetting.write(newDpi) if self.device.setting_callback: self.device.setting_callback(self.device, type(self.dpiSetting), [newDpi]) def displayNewDpi(self, newDpiIdx): selected_dpi = self.dpiChoices[newDpiIdx] min_dpi = self.dpiChoices[0] max_dpi = self.dpiChoices[-1] reason = f"DPI {selected_dpi} [min {min_dpi}, max {max_dpi}]" self._show_notification(self.device, reason) def press_action(self, key): # start tracking self.starting = True if self.fsmState == State.IDLE: self.fsmState = State.PRESSED self.dx = 0.0 # While in 'moved' state, the index into 'dpiChoices' of the currently selected DPI setting self.movingDpiIdx = None def release_action(self): # adjust DPI and stop tracking if self.fsmState == State.PRESSED: # Swap with other DPI thisIdx = self.dpiChoices.index(self.dpiSetting.read()) newDpiIdx, self.otherDpiIdx = self.otherDpiIdx, thisIdx if self.device.persister: self.device.persister["_dpi-sliding"] = self.otherDpiIdx self.setNewDpi(newDpiIdx) self.displayNewDpi(newDpiIdx) elif self.fsmState == State.MOVED: # Set DPI according to displacement self.setNewDpi(self.movingDpiIdx) self.fsmState = State.IDLE def move_action(self, dx, dy): if self.device.features.get_feature_version(_F.REPROG_CONTROLS_V4) >= 5 and self.starting: self.starting = False # hack to ignore strange first movement report from MX Master 3S return currDpi = self.dpiSetting.read() self.dx += float(dx) / float(currDpi) * 15.0 # yields a more-or-less DPI-independent dx of about 5/cm if self.fsmState == State.PRESSED: if abs(self.dx) >= 1.0: self.fsmState = State.MOVED self.movingDpiIdx = self.dpiChoices.index(currDpi) elif self.fsmState == State.MOVED: currIdx = self.dpiChoices.index(self.dpiSetting.read()) newMovingDpiIdx = min(max(currIdx + int(self.dx), 0), len(self.dpiChoices) - 1) if newMovingDpiIdx != self.movingDpiIdx: self.movingDpiIdx = newMovingDpiIdx self.displayNewDpi(newMovingDpiIdx) class MouseGesturesXY(settings.RawXYProcessing): def activate_action(self): self.dpiSetting = next(filter(lambda s: s.name == "dpi" or s.name == "dpi_extended", self.device.settings), None) self.fsmState = State.IDLE self.initialize_data() def initialize_data(self): self.dx = 0.0 self.dy = 0.0 self.lastEv = None self.data = [] def press_action(self, key): self.starting = True if self.fsmState == State.IDLE: self.fsmState = State.PRESSED self.initialize_data() self.data = [key.key] def release_action(self): if self.fsmState == State.PRESSED: # emit mouse gesture notification self.push_mouse_event() if logger.isEnabledFor(logging.INFO): logger.info("mouse gesture notification %s", self.data) payload = struct.pack("!" + (len(self.data) * "h"), *self.data) notification = base.HIDPPNotification(0, 0, 0, 0, payload) diversion.process_notification(self.device, notification, _F.MOUSE_GESTURE) self.fsmState = State.IDLE def move_action(self, dx, dy): if self.fsmState == State.PRESSED: now = time() * 1000 # time_ns() / 1e6 if self.device.features.get_feature_version(_F.REPROG_CONTROLS_V4) >= 5 and self.starting: self.starting = False # hack to ignore strange first movement report from MX Master 3S return if self.lastEv is not None and now - self.lastEv > 200.0: self.push_mouse_event() dpi = self.dpiSetting.read() if self.dpiSetting else 1000 dx = float(dx) / float(dpi) * 15.0 # This multiplier yields a more-or-less DPI-independent dx of about 5/cm self.dx += dx dy = float(dy) / float(dpi) * 15.0 # This multiplier yields a more-or-less DPI-independent dx of about 5/cm self.dy += dy self.lastEv = now def key_action(self, key): self.push_mouse_event() self.data.append(1) self.data.append(key) self.lastEv = time() * 1000 # time_ns() / 1e6 if logger.isEnabledFor(logging.DEBUG): logger.debug("mouse gesture key event %d %s", key, self.data) def push_mouse_event(self): x = int(self.dx) y = int(self.dy) if x == 0 and y == 0: return self.data.append(0) self.data.append(x) self.data.append(y) self.dx = 0.0 self.dy = 0.0 if logger.isEnabledFor(logging.DEBUG): logger.debug("mouse gesture move event %d %d %s", x, y, self.data) class DivertKeys(settings.Settings): name = "divert-keys" label = _("Key/Button Diversion") description = _("Make the key or button send HID++ notifications (Diverted) or initiate Mouse Gestures or Sliding DPI") feature = _F.REPROG_CONTROLS_V4 keys_universe = special_keys.CONTROL choices_universe = common.NamedInts(**{_("Regular"): 0, _("Diverted"): 1, _("Mouse Gestures"): 2, _("Sliding DPI"): 3}) choices_gesture = common.NamedInts(**{_("Regular"): 0, _("Diverted"): 1, _("Mouse Gestures"): 2}) choices_divert = common.NamedInts(**{_("Regular"): 0, _("Diverted"): 1}) class rw_class: def __init__(self, feature): self.feature = feature self.kind = settings.FeatureRW.kind def read(self, device, key): key_index = device.keys.index(key) key_struct = device.keys[key_index] return b"\x00\x00\x01" if "diverted" in key_struct.mapping_flags else b"\x00\x00\x00" def write(self, device, key, data_bytes): key_index = device.keys.index(key) key_struct = device.keys[key_index] key_struct.set_diverted(common.bytes2int(data_bytes) != 0) # not regular return True class validator_class(settings.ChoicesMapValidator): def __init__(self, choices, key_byte_count=2, byte_count=1, mask=0x01): super().__init__(choices, key_byte_count, byte_count, mask) def prepare_write(self, key, new_value): if self.gestures and new_value != 2: # mouse gestures self.gestures.stop(key) if self.sliding and new_value != 3: # sliding DPI self.sliding.stop(key) if self.gestures and new_value == 2: # mouse gestures self.gestures.start(key) if self.sliding and new_value == 3: # sliding DPI self.sliding.start(key) return super().prepare_write(key, new_value) @classmethod def build(cls, setting_class, device): sliding = gestures = None choices = {} if device.keys: for k in device.keys: if "divertable" in k.flags and "virtual" not in k.flags: if "raw XY" in k.flags: choices[k.key] = setting_class.choices_gesture if gestures is None: gestures = MouseGesturesXY(device, name="MouseGestures") if _F.ADJUSTABLE_DPI in device.features: choices[k.key] = setting_class.choices_universe if sliding is None: sliding = DpiSlidingXY( device, name="DpiSliding", show_notification=desktop_notifications.show ) else: choices[k.key] = setting_class.choices_divert if not choices: return None validator = cls(choices, key_byte_count=2, byte_count=1, mask=0x01) validator.sliding = sliding validator.gestures = gestures return validator def produce_dpi_list(feature, function, ignore, device, direction): dpi_bytes = b"" for i in range(0, 0x100): # there will be only a very few iterations performed reply = device.feature_request(feature, function, 0x00, direction, i) assert reply, "Oops, DPI list cannot be retrieved!" dpi_bytes += reply[ignore:] if dpi_bytes[-2:] == b"\x00\x00": break dpi_list = [] i = 0 while i < len(dpi_bytes): val = common.bytes2int(dpi_bytes[i : i + 2]) if val == 0: break if val >> 13 == 0b111: step = val & 0x1FFF last = common.bytes2int(dpi_bytes[i + 2 : i + 4]) assert len(dpi_list) > 0 and last > dpi_list[-1], f"Invalid DPI list item: {val!r}" dpi_list += range(dpi_list[-1] + step, last + 1, step) i += 4 else: dpi_list.append(val) i += 2 return dpi_list class AdjustableDpi(settings.Setting): name = "dpi" label = _("Sensitivity (DPI)") description = _("Mouse movement sensitivity") feature = _F.ADJUSTABLE_DPI rw_options = {"read_fnid": 0x20, "write_fnid": 0x30} choices_universe = common.NamedInts.range(100, 4000, str, 50) class validator_class(settings.ChoicesValidator): @classmethod def build(cls, setting_class, device): dpilist = produce_dpi_list(setting_class.feature, 0x10, 1, device, 0) setting = ( cls(choices=common.NamedInts.list(dpilist), byte_count=2, write_prefix_bytes=b"\x00") if dpilist else None ) setting.dpilist = dpilist return setting def validate_read(self, reply_bytes): # special validator to use default DPI if needed reply_value = common.bytes2int(reply_bytes[1:3]) if reply_value == 0: # use default value instead reply_value = common.bytes2int(reply_bytes[3:5]) valid_value = self.choices[reply_value] assert valid_value is not None, f"{self.__class__.__name__}: failed to validate read value {reply_value:02X}" return valid_value class ExtendedAdjustableDpi(settings.Setting): # the extended version allows for two dimensions, longer dpi descriptions, but still assume only one sensor name = "dpi_extended" label = _("Sensitivity (DPI)") description = _("Mouse movement sensitivity") + "\n" + _("May need Onboard Profiles set to Disable to be effective.") feature = _F.EXTENDED_ADJUSTABLE_DPI rw_options = {"read_fnid": 0x50, "write_fnid": 0x60} keys_universe = common.NamedInts(X=0, Y=1, LOD=2) choices_universe = common.NamedInts.range(100, 4000, str, 50) choices_universe[0] = "LOW" choices_universe[1] = "MEDIUM" choices_universe[2] = "HIGH" keys = common.NamedInts(X=0, Y=1, LOD=2) def write_key_value(self, key, value, save=True): if isinstance(self._value, dict): self._value[key] = value else: self._value = {key: value} result = self.write(self._value, save) return result[key] if isinstance(result, dict) else result class validator_class(settings.ChoicesMapValidator): @classmethod def build(cls, setting_class, device): reply = device.feature_request(setting_class.feature, 0x10, 0x00) y = bool(reply[2] & 0x01) lod = bool(reply[2] & 0x02) choices_map = {} dpilist_x = produce_dpi_list(setting_class.feature, 0x20, 3, device, 0) choices_map[setting_class.keys["X"]] = common.NamedInts.list(dpilist_x) if y: dpilist_y = produce_dpi_list(setting_class.feature, 0x20, 3, device, 1) choices_map[setting_class.keys["Y"]] = common.NamedInts.list(dpilist_y) if lod: choices_map[setting_class.keys["LOD"]] = common.NamedInts(LOW=0, MEDIUM=1, HIGH=2) validator = cls(choices_map=choices_map, byte_count=2, write_prefix_bytes=b"\x00") validator.y = y validator.lod = lod validator.keys = setting_class.keys return validator def validate_read(self, reply_bytes): # special validator to read entire setting dpi_x = common.bytes2int(reply_bytes[3:5]) if reply_bytes[1:3] == 0 else common.bytes2int(reply_bytes[1:3]) assert dpi_x in self.choices[0], f"{self.__class__.__name__}: failed to validate dpi_x value {dpi_x:04X}" value = {self.keys["X"]: dpi_x} if self.y: dpi_y = common.bytes2int(reply_bytes[7:9]) if reply_bytes[5:7] == 0 else common.bytes2int(reply_bytes[5:7]) assert dpi_y in self.choices[1], f"{self.__class__.__name__}: failed to validate dpi_y value {dpi_y:04X}" value[self.keys["Y"]] = dpi_y if self.lod: lod = reply_bytes[9] assert lod in self.choices[2], f"{self.__class__.__name__}: failed to validate lod value {lod:02X}" value[self.keys["LOD"]] = lod return value def prepare_write(self, new_value, current_value=None): # special preparer to write entire setting data_bytes = self._write_prefix_bytes if new_value[self.keys["X"]] not in self.choices[self.keys["X"]]: raise ValueError(f"invalid value {new_value!r}") data_bytes += common.int2bytes(new_value[0], 2) if self.y: if new_value[self.keys["Y"]] not in self.choices[self.keys["Y"]]: raise ValueError(f"invalid value {new_value!r}") data_bytes += common.int2bytes(new_value[self.keys["Y"]], 2) else: data_bytes += b"\x00\x00" if self.lod: if new_value[self.keys["LOD"]] not in self.choices[self.keys["LOD"]]: raise ValueError(f"invalid value {new_value!r}") data_bytes += common.int2bytes(new_value[self.keys["LOD"]], 1) else: data_bytes += b"\x00" return data_bytes class SpeedChange(settings.Setting): """Implements the ability to switch Sensitivity by clicking on the DPI_Change button.""" name = "speed-change" label = _("Sensitivity Switching") description = _( "Switch the current sensitivity and the remembered sensitivity when the key or button is pressed.\n" "If there is no remembered sensitivity, just remember the current sensitivity" ) choices_universe = special_keys.CONTROL choices_extra = common.NamedInt(0, _("Off")) feature = _F.POINTER_SPEED rw_options = {"name": "speed change"} class rw_class(settings.ActionSettingRW): def press_action(self): # switch sensitivity currentSpeed = self.device.persister.get("pointer_speed", None) if self.device.persister else None newSpeed = self.device.persister.get("_speed-change", None) if self.device.persister else None speed_setting = next(filter(lambda s: s.name == "pointer_speed", self.device.settings), None) if newSpeed is not None: if speed_setting: speed_setting.write(newSpeed) if self.device.setting_callback: self.device.setting_callback(self.device, type(speed_setting), [newSpeed]) else: logger.error("cannot save sensitivity setting on %s", self.device) if self.device.persister: self.device.persister["_speed-change"] = currentSpeed class validator_class(settings.ChoicesValidator): @classmethod def build(cls, setting_class, device): key_index = device.keys.index(special_keys.CONTROL.DPI_Change) key = device.keys[key_index] if key_index is not None else None if key is not None and "divertable" in key.flags: keys = [setting_class.choices_extra, key.key] return cls(choices=common.NamedInts.list(keys), byte_count=2) class DisableKeyboardKeys(settings.BitFieldSetting): name = "disable-keyboard-keys" label = _("Disable keys") description = _("Disable specific keyboard keys.") feature = _F.KEYBOARD_DISABLE_KEYS rw_options = {"read_fnid": 0x10, "write_fnid": 0x20} _labels = {k: (None, _("Disables the %s key.") % k) for k in special_keys.DISABLE} choices_universe = special_keys.DISABLE class validator_class(settings.BitFieldValidator): @classmethod def build(cls, setting_class, device): mask = device.feature_request(_F.KEYBOARD_DISABLE_KEYS, 0x00)[0] options = [special_keys.DISABLE[1 << i] for i in range(8) if mask & (1 << i)] return cls(options) if options else None class Multiplatform(settings.Setting): name = "multiplatform" label = _("Set OS") description = _("Change keys to match OS.") feature = _F.MULTIPLATFORM rw_options = {"read_fnid": 0x00, "write_fnid": 0x30} choices_universe = common.NamedInts(**{"OS " + str(i + 1): i for i in range(8)}) # multiplatform OS bits OSS = [ ("Linux", 0x0400), ("MacOS", 0x2000), ("Windows", 0x0100), ("iOS", 0x4000), ("Android", 0x1000), ("WebOS", 0x8000), ("Chrome", 0x0800), ("WinEmb", 0x0200), ("Tizen", 0x0001), ] # the problem here is how to construct the right values for the rules Set GUI, # as, for example, the integer value for 'Windows' can be different on different devices class validator_class(settings.ChoicesValidator): @classmethod def build(cls, setting_class, device): def _str_os_versions(low, high): def _str_os_version(version): if version == 0: return "" elif version & 0xFF: return str(version >> 8) + "." + str(version & 0xFF) else: return str(version >> 8) return "" if low == 0 and high == 0 else " " + _str_os_version(low) + "-" + _str_os_version(high) infos = device.feature_request(_F.MULTIPLATFORM) assert infos, "Oops, multiplatform count cannot be retrieved!" flags, _ignore, num_descriptors = struct.unpack("!BBB", infos[:3]) if not (flags & 0x02): # can't set platform so don't create setting return [] descriptors = [] for index in range(0, num_descriptors): descriptor = device.feature_request(_F.MULTIPLATFORM, 0x10, index) platform, _ignore, os_flags, low, high = struct.unpack("!BBHHH", descriptor[:8]) descriptors.append((platform, os_flags, low, high)) choices = common.NamedInts() for os_name, os_bit in setting_class.OSS: for platform, os_flags, low, high in descriptors: os = os_name + _str_os_versions(low, high) if os_bit & os_flags and platform not in choices and os not in choices: choices[platform] = os return cls(choices=choices, read_skip_byte_count=6, write_prefix_bytes=b"\xff") if choices else None class DualPlatform(settings.Setting): name = "multiplatform" label = _("Set OS") description = _("Change keys to match OS.") choices_universe = common.NamedInts() choices_universe[0x00] = "iOS, MacOS" choices_universe[0x01] = "Android, Windows" feature = _F.DUALPLATFORM rw_options = {"read_fnid": 0x00, "write_fnid": 0x20} validator_class = settings.ChoicesValidator validator_options = {"choices": choices_universe} class ChangeHost(settings.Setting): name = "change-host" label = _("Change Host") description = _("Switch connection to a different host") persist = False # persisting this setting is harmful feature = _F.CHANGE_HOST rw_options = {"read_fnid": 0x00, "write_fnid": 0x10, "no_reply": True} choices_universe = common.NamedInts(**{"Host " + str(i + 1): i for i in range(3)}) class validator_class(settings.ChoicesValidator): @classmethod def build(cls, setting_class, device): infos = device.feature_request(_F.CHANGE_HOST) assert infos, "Oops, host count cannot be retrieved!" numHosts, currentHost = struct.unpack("!BB", infos[:2]) hostNames = _hidpp20.get_host_names(device) hostNames = hostNames if hostNames is not None else {} if currentHost not in hostNames or hostNames[currentHost][1] == "": hostNames[currentHost] = (True, socket.gethostname().partition(".")[0]) choices = common.NamedInts() for host in range(0, numHosts): paired, hostName = hostNames.get(host, (True, "")) choices[host] = str(host + 1) + ":" + hostName if hostName else str(host + 1) return cls(choices=choices, read_skip_byte_count=1) if choices and len(choices) > 1 else None _GESTURE2_GESTURES_LABELS = { GestureId.TAP_1_FINGER: (_("Single tap"), _("Performs a left click.")), GestureId.TAP_2_FINGER: (_("Single tap with two fingers"), _("Performs a right click.")), GestureId.TAP_3_FINGER: (_("Single tap with three fingers"), None), GestureId.CLICK_1_FINGER: (None, None), GestureId.CLICK_2_FINGER: (None, None), GestureId.CLICK_3_FINGER: (None, None), GestureId.DOUBLE_TAP_1_FINGER: (_("Double tap"), _("Performs a double click.")), GestureId.DOUBLE_TAP_2_FINGER: (_("Double tap with two fingers"), None), GestureId.DOUBLE_TAP_3_FINGER: (_("Double tap with three fingers"), None), GestureId.TRACK_1_FINGER: (None, None), GestureId.TRACKING_ACCELERATION: (None, None), GestureId.TAP_DRAG_1_FINGER: (_("Tap and drag"), _("Drags items by dragging the finger after double tapping.")), GestureId.TAP_DRAG_2_FINGER: ( _("Tap and drag with two fingers"), _("Drags items by dragging the fingers after double tapping."), ), GestureId.DRAG_3_FINGER: (_("Tap and drag with three fingers"), None), GestureId.TAP_GESTURES: (None, None), GestureId.FN_CLICK_GESTURE_SUPPRESSION: ( _("Suppress tap and edge gestures"), _("Disables tap and edge gestures (equivalent to pressing Fn+LeftClick)."), ), GestureId.SCROLL_1_FINGER: (_("Scroll with one finger"), _("Scrolls.")), GestureId.SCROLL_2_FINGER: (_("Scroll with two fingers"), _("Scrolls.")), GestureId.SCROLL_2_FINGER_HORIZONTAL: (_("Scroll horizontally with two fingers"), _("Scrolls horizontally.")), GestureId.SCROLL_2_FINGER_VERTICAL: (_("Scroll vertically with two fingers"), _("Scrolls vertically.")), GestureId.SCROLL_2_FINGER_STATELESS: (_("Scroll with two fingers"), _("Scrolls.")), GestureId.NATURAL_SCROLLING: (_("Natural scrolling"), _("Inverts the scrolling direction.")), GestureId.THUMBWHEEL: (_("Thumbwheel"), _("Enables the thumbwheel.")), GestureId.V_SCROLL_INTERTIA: (None, None), GestureId.V_SCROLL_BALLISTICS: (None, None), GestureId.SWIPE_2_FINGER_HORIZONTAL: (None, None), GestureId.SWIPE_3_FINGER_HORIZONTAL: (None, None), GestureId.SWIPE_4_FINGER_HORIZONTAL: (None, None), GestureId.SWIPE_3_FINGER_VERTICAL: (None, None), GestureId.SWIPE_4_FINGER_VERTICAL: (None, None), GestureId.LEFT_EDGE_SWIPE_1_FINGER: (None, None), GestureId.RIGHT_EDGE_SWIPE_1_FINGER: (None, None), GestureId.BOTTOM_EDGE_SWIPE_1_FINGER: (None, None), GestureId.TOP_EDGE_SWIPE_1_FINGER: (_("Swipe from the top edge"), None), GestureId.LEFT_EDGE_SWIPE_1_FINGER_2: (_("Swipe from the left edge"), None), GestureId.RIGHT_EDGE_SWIPE_1_FINGER_2: (_("Swipe from the right edge"), None), GestureId.BOTTOM_EDGE_SWIPE_1_FINGER_2: (_("Swipe from the bottom edge"), None), GestureId.TOP_EDGE_SWIPE_1_FINGER_2: (_("Swipe from the top edge"), None), GestureId.LEFT_EDGE_SWIPE_2_FINGER: (_("Swipe two fingers from the left edge"), None), GestureId.RIGHT_EDGE_SWIPE_2_FINGER: (_("Swipe two fingers from the right edge"), None), GestureId.BOTTOM_EDGE_SWIPE_2_FINGER: (_("Swipe two fingers from the bottom edge"), None), GestureId.TOP_EDGE_SWIPE_2_FINGER: (_("Swipe two fingers from the top edge"), None), GestureId.ZOOM_2_FINGER: (_("Zoom with two fingers."), _("Pinch to zoom out; spread to zoom in.")), GestureId.ZOOM_2_FINGER_PINCH: (_("Pinch to zoom out."), _("Pinch to zoom out.")), GestureId.ZOOM_2_FINGER_SPREAD: (_("Spread to zoom in."), _("Spread to zoom in.")), GestureId.ZOOM_3_FINGER: (_("Zoom with three fingers."), None), GestureId.ZOOM_2_FINGER_STATELESS: (_("Zoom with two fingers"), _("Pinch to zoom out; spread to zoom in.")), GestureId.TWO_FINGERS_PRESENT: (None, None), GestureId.ROTATE_2_FINGER: (None, None), GestureId.FINGER_1: (None, None), GestureId.FINGER_2: (None, None), GestureId.FINGER_3: (None, None), GestureId.FINGER_4: (None, None), GestureId.FINGER_5: (None, None), GestureId.FINGER_6: (None, None), GestureId.FINGER_7: (None, None), GestureId.FINGER_8: (None, None), GestureId.FINGER_9: (None, None), GestureId.FINGER_10: (None, None), GestureId.DEVICE_SPECIFIC_RAW_DATA: (None, None), } _GESTURE2_PARAMS_LABELS = { ParamId.EXTRA_CAPABILITIES: (None, None), # not supported ParamId.PIXEL_ZONE: (_("Pixel zone"), None), # TO DO: replace None with a short description ParamId.RATIO_ZONE: (_("Ratio zone"), None), # TO DO: replace None with a short description ParamId.SCALE_FACTOR: (_("Scale factor"), _("Sets the cursor speed.")), } _GESTURE2_PARAMS_LABELS_SUB = { "left": (_("Left"), _("Left-most coordinate.")), "bottom": (_("Bottom"), _("Bottom coordinate.")), "width": (_("Width"), _("Width.")), "height": (_("Height"), _("Height.")), "scale": (_("Scale"), _("Cursor speed.")), } class Gesture2Gestures(settings.BitFieldWithOffsetAndMaskSetting): name = "gesture2-gestures" label = _("Gestures") description = _("Tweak the mouse/touchpad behaviour.") feature = _F.GESTURE_2 rw_options = {"read_fnid": 0x10, "write_fnid": 0x20} validator_options = {"om_method": hidpp20.Gesture.enable_offset_mask} choices_universe = hidpp20_constants.GestureId _labels = _GESTURE2_GESTURES_LABELS class validator_class(settings.BitFieldWithOffsetAndMaskValidator): @classmethod def build(cls, setting_class, device, om_method=None): options = [g for g in device.gestures.gestures.values() if g.can_be_enabled or g.default_enabled] return cls(options, om_method=om_method) if options else None class Gesture2Divert(settings.BitFieldWithOffsetAndMaskSetting): name = "gesture2-divert" label = _("Gestures Diversion") description = _("Divert mouse/touchpad gestures.") feature = _F.GESTURE_2 rw_options = {"read_fnid": 0x30, "write_fnid": 0x40} validator_options = {"om_method": hidpp20.Gesture.diversion_offset_mask} choices_universe = hidpp20_constants.GestureId _labels = _GESTURE2_GESTURES_LABELS class validator_class(settings.BitFieldWithOffsetAndMaskValidator): @classmethod def build(cls, setting_class, device, om_method=None): options = [g for g in device.gestures.gestures.values() if g.can_be_diverted] return cls(options, om_method=om_method) if options else None class Gesture2Params(settings.LongSettings): name = "gesture2-params" label = _("Gesture params") description = _("Change numerical parameters of a mouse/touchpad.") feature = _F.GESTURE_2 rw_options = {"read_fnid": 0x70, "write_fnid": 0x80} choices_universe = hidpp20_constants.ParamId sub_items_universe = hidpp20.SUB_PARAM # item (NamedInt) -> list/tuple of objects that have the following attributes # .id (sub-item text), .length (in bytes), .minimum and .maximum _labels = _GESTURE2_PARAMS_LABELS _labels_sub = _GESTURE2_PARAMS_LABELS_SUB class validator_class(settings.MultipleRangeValidator): @classmethod def build(cls, setting_class, device): params = _hidpp20.get_gestures(device).params.values() items = [i for i in params if i.sub_params] if not items: return None sub_items = {i: i.sub_params for i in items} return cls(items, sub_items) class MKeyLEDs(settings.BitFieldSetting): name = "m-key-leds" label = _("M-Key LEDs") description = ( _("Control the M-Key LEDs.") + "\n" + _("May need Onboard Profiles set to Disable to be effective.") + "\n" + _("May need G Keys diverted to be effective.") ) feature = _F.MKEYS choices_universe = common.NamedInts() for i in range(8): choices_universe[1 << i] = "M" + str(i + 1) _labels = {k: (None, _("Lights up the %s key.") % k) for k in choices_universe} class rw_class(settings.FeatureRW): def __init__(self, feature): super().__init__(feature, write_fnid=0x10) def read(self, device): # no way to read, so just assume off return b"\x00" class validator_class(settings.BitFieldValidator): @classmethod def build(cls, setting_class, device): number = device.feature_request(setting_class.feature, 0x00)[0] options = [setting_class.choices_universe[1 << i] for i in range(number)] return cls(options) if options else None class MRKeyLED(settings.Setting): name = "mr-key-led" label = _("MR-Key LED") description = ( _("Control the MR-Key LED.") + "\n" + _("May need Onboard Profiles set to Disable to be effective.") + "\n" + _("May need G Keys diverted to be effective.") ) feature = _F.MR class rw_class(settings.FeatureRW): def __init__(self, feature): super().__init__(feature, write_fnid=0x00) def read(self, device): # no way to read, so just assume off return b"\x00" ## Only implemented for devices that can produce Key and Consumer Codes (e.g., Craft) ## and devices that can produce Key, Mouse, and Horizontal Scroll (e.g., M720) ## Only interested in current host, so use 0xFF for it class PersistentRemappableAction(settings.Settings): name = "persistent-remappable-keys" label = _("Persistent Key/Button Mapping") description = ( _("Permanently change the mapping for the key or button.") + "\n" + _("Changing important keys or buttons (such as for the left mouse button) can result in an unusable system.") ) persist = False # This setting is persistent in the device so no need to persist it here feature = _F.PERSISTENT_REMAPPABLE_ACTION keys_universe = special_keys.CONTROL choices_universe = special_keys.KEYS class rw_class: def __init__(self, feature): self.feature = feature self.kind = settings.FeatureRW.kind def read(self, device, key): ks = device.remap_keys[device.remap_keys.index(key)] return b"\x00\x00" + ks.data_bytes def write(self, device, key, data_bytes): ks = device.remap_keys[device.remap_keys.index(key)] v = ks.remap(data_bytes) return v class validator_class(settings.ChoicesMapValidator): @classmethod def build(cls, setting_class, device): remap_keys = device.remap_keys if not remap_keys: return None capabilities = device.remap_keys.capabilities if capabilities & 0x0041 == 0x0041: # Key and Consumer Codes keys = special_keys.KEYS_KEYS_CONSUMER elif capabilities & 0x0023 == 0x0023: # Key, Mouse, and HScroll Codes keys = special_keys.KEYS_KEYS_MOUSE_HSCROLL else: if logger.isEnabledFor(logging.WARNING): logger.warning("%s: unimplemented Persistent Remappable capability %s", device.name, hex(capabilities)) return None choices = {} for k in remap_keys: if k is not None: key = special_keys.CONTROL[k.key] choices[key] = keys # TO RECOVER FROM BAD VALUES use special_keys.KEYS return cls(choices, key_byte_count=2, byte_count=4) if choices else None def validate_read(self, reply_bytes, key): start = self._key_byte_count + self._read_skip_byte_count end = start + self._byte_count reply_value = common.bytes2int(reply_bytes[start:end]) & self.mask # Craft keyboard has a value that isn't valid so fudge these values if reply_value not in self.choices[key]: if logger.isEnabledFor(logging.WARNING): logger.warning("unusual persistent remappable action mapping %x: use Default", reply_value) reply_value = special_keys.KEYS_Default return reply_value class Sidetone(settings.Setting): name = "sidetone" label = _("Sidetone") description = _("Set sidetone level.") feature = _F.SIDETONE validator_class = settings.RangeValidator min_value = 0 max_value = 100 class Equalizer(settings.RangeFieldSetting): name = "equalizer" label = _("Equalizer") description = _("Set equalizer levels.") feature = _F.EQUALIZER rw_options = {"read_fnid": 0x20, "write_fnid": 0x30, "read_prefix": b"\x00"} keys_universe = [] class validator_class(settings.PackedRangeValidator): @classmethod def build(cls, setting_class, device): data = device.feature_request(_F.EQUALIZER, 0x00) if not data: return None count, dbRange, _x, dbMin, dbMax = struct.unpack("!BBBBB", data[:5]) if dbMin == 0: dbMin = -dbRange if dbMax == 0: dbMax = dbRange map = common.NamedInts() for g in range((count + 6) // 7): freqs = device.feature_request(_F.EQUALIZER, 0x10, g * 7) for b in range(7): if g * 7 + b >= count: break map[g * 7 + b] = str(int.from_bytes(freqs[2 * b + 1 : 2 * b + 3], "big")) + _("Hz") return cls(map, min_value=dbMin, max_value=dbMax, count=count, write_prefix_bytes=b"\x02") class ADCPower(settings.Setting): name = "adc_power_management" label = _("Power Management") description = _("Power off in minutes (0 for never).") feature = _F.ADC_MEASUREMENT rw_options = {"read_fnid": 0x10, "write_fnid": 0x20} validator_class = settings.RangeValidator min_value = 0x00 max_value = 0xFF validator_options = {"byte_count": 1} class BrightnessControl(settings.Setting): name = "brightness_control" label = _("Brightness Control") description = _("Control overall brightness") feature = _F.BRIGHTNESS_CONTROL rw_options = {"read_fnid": 0x10, "write_fnid": 0x20} validator_class = settings.RangeValidator def __init__(self, device, rw, validator): super().__init__(device, rw, validator) rw.on_off = validator.on_off rw.min_nonzero_value = validator.min_value validator.min_value = 0 if validator.on_off else validator.min_value class rw_class(settings.FeatureRW): def read(self, device, data_bytes=b""): if self.on_off: reply = device.feature_request(self.feature, 0x30) if not reply[0] & 0x01: return b"\x00\x00" return super().read(device, data_bytes) def write(self, device, data_bytes): if self.on_off: off = int.from_bytes(data_bytes, byteorder="big") < self.min_nonzero_value reply = device.feature_request(self.feature, 0x40, b"\x00" if off else b"\x01", no_reply=False) if off: return reply return super().write(device, data_bytes) class validator_class(settings.RangeValidator): @classmethod def build(cls, setting_class, device): reply = device.feature_request(_F.BRIGHTNESS_CONTROL) assert reply, "Oops, brightness range cannot be retrieved!" if reply: max_value = int.from_bytes(reply[0:2], byteorder="big") min_value = int.from_bytes(reply[4:6], byteorder="big") on_off = bool(reply[3] & 0x04) # separate on/off control validator = cls(min_value=min_value, max_value=max_value, byte_count=2) validator.on_off = on_off return validator class LEDControl(settings.Setting): name = "led_control" label = _("LED Control") description = _("Switch control of LED zones between device and Solaar") feature = _F.COLOR_LED_EFFECTS rw_options = {"read_fnid": 0x70, "write_fnid": 0x80} choices_universe = common.NamedInts(Device=0, Solaar=1) validator_class = settings.ChoicesValidator validator_options = {"choices": choices_universe} colors = special_keys.COLORS _LEDP = hidpp20.LEDParam # an LED Zone has an index, a set of possible LED effects, and an LED effect setting class LEDZoneSetting(settings.Setting): name = "led_zone_" label = _("LED Zone Effects") description = _("Set effect for LED Zone") + "\n" + _("LED Control needs to be set to Solaar to be effective.") feature = _F.COLOR_LED_EFFECTS color_field = {"name": _LEDP.color, "kind": settings.KIND.choice, "label": None, "choices": colors} speed_field = {"name": _LEDP.speed, "kind": settings.KIND.range, "label": _("Speed"), "min": 0, "max": 255} period_field = {"name": _LEDP.period, "kind": settings.KIND.range, "label": _("Period"), "min": 100, "max": 5000} intensity_field = {"name": _LEDP.intensity, "kind": settings.KIND.range, "label": _("Intensity"), "min": 0, "max": 100} ramp_field = {"name": _LEDP.ramp, "kind": settings.KIND.choice, "label": _("Ramp"), "choices": hidpp20.LEDRampChoices} # form_field = {"name": _LEDP.form, "kind": settings.KIND.choice, "label": _("Form"), "choices": _hidpp20.LEDFormChoices} possible_fields = [color_field, speed_field, period_field, intensity_field, ramp_field] @classmethod def setup(cls, device, read_fnid, write_fnid, suffix): infos = device.led_effects settings_ = [] for zone in infos.zones: prefix = common.int2bytes(zone.index, 1) rw = settings.FeatureRW(cls.feature, read_fnid, write_fnid, prefix=prefix, suffix=suffix) validator = settings.HeteroValidator( data_class=hidpp20.LEDEffectSetting, options=zone.effects, readable=infos.readable ) setting = cls(device, rw, validator) setting.name = cls.name + str(int(zone.location)) setting.label = _("LEDs") + " " + str(hidpp20.LEDZoneLocations[zone.location]) choices = [hidpp20.LEDEffects[e.ID][0] for e in zone.effects if e.ID in hidpp20.LEDEffects] ID_field = {"name": "ID", "kind": settings.KIND.choice, "label": None, "choices": choices} setting.possible_fields = [ID_field] + cls.possible_fields setting.fields_map = hidpp20.LEDEffects settings_.append(setting) return settings_ @classmethod def build(cls, device): return cls.setup(device, 0xE0, 0x30, b"") class RGBControl(settings.Setting): name = "rgb_control" label = _("LED Control") description = _("Switch control of LED zones between device and Solaar") feature = _F.RGB_EFFECTS rw_options = {"read_fnid": 0x50, "write_fnid": 0x50} choices_universe = common.NamedInts(Device=0, Solaar=1) validator_class = settings.ChoicesValidator validator_options = {"choices": choices_universe, "write_prefix_bytes": b"\x01", "read_skip_byte_count": 1} class RGBEffectSetting(LEDZoneSetting): name = "rgb_zone_" label = _("LED Zone Effects") description = _("Set effect for LED Zone") + "\n" + _("LED Control needs to be set to Solaar to be effective.") feature = _F.RGB_EFFECTS @classmethod def build(cls, device): return cls.setup(device, 0xE0, 0x10, b"\x01") class PerKeyLighting(settings.Settings): name = "per-key-lighting" label = _("Per-key Lighting") description = _("Control per-key lighting.") feature = _F.PER_KEY_LIGHTING_V2 keys_universe = special_keys.KEYCODES choices_universe = special_keys.COLORSPLUS def read(self, cached=True): self._pre_read(cached) if cached and self._value is not None: return self._value reply_map = {} for key in self._validator.choices: reply_map[int(key)] = special_keys.COLORSPLUS["No change"] # this signals no change self._value = reply_map return reply_map def write(self, map, save=True): if self._device.online: self.update(map, save) table = {} for key, value in map.items(): if value in table: table[value].append(key) # keys will be in order from small to large else: table[value] = [key] if len(table) == 1: # use range update for value, keys in table.items(): # only one, of course if value != special_keys.COLORSPLUS["No change"]: # this signals no change, so don't update at all data_bytes = keys[0].to_bytes(1, "big") + keys[-1].to_bytes(1, "big") + value.to_bytes(3, "big") self._device.feature_request(self.feature, 0x50, data_bytes) # range update command to update all keys self._device.feature_request(self.feature, 0x70, 0x00) # signal device to make the changes else: data_bytes = b"" for value, keys in table.items(): # only one, of course if value != special_keys.COLORSPLUS["No change"]: # this signals no change, so ignore it while len(keys) > 3: # use an optimized update command that can update up to 13 keys data = value.to_bytes(3, "big") + b"".join([key.to_bytes(1, "big") for key in keys[0:13]]) self._device.feature_request(self.feature, 0x60, data) # single-value multiple-keys update keys = keys[13:] for key in keys: data_bytes += key.to_bytes(1, "big") + value.to_bytes(3, "big") if len(data_bytes) >= 16: # up to four values are packed into a regular update self._device.feature_request(self.feature, 0x10, data_bytes) data_bytes = b"" if len(data_bytes) > 0: # update any remaining keys self._device.feature_request(self.feature, 0x10, data_bytes) self._device.feature_request(self.feature, 0x70, 0x00) # signal device to make the changes return map def write_key_value(self, key, value, save=True): if value != special_keys.COLORSPLUS["No change"]: # this signals no change result = super().write_key_value(int(key), value, save) if self._device.online: self._device.feature_request(self.feature, 0x70, 0x00) # signal device to make the change return result else: return True class rw_class(settings.FeatureRWMap): pass class validator_class(settings.ChoicesMapValidator): @classmethod def build(cls, setting_class, device): choices_map = {} key_bitmap = device.feature_request(setting_class.feature, 0x00, 0x00, 0x00)[2:] key_bitmap += device.feature_request(setting_class.feature, 0x00, 0x00, 0x01)[2:] key_bitmap += device.feature_request(setting_class.feature, 0x00, 0x00, 0x02)[2:] for i in range(1, 255): if (key_bitmap[i // 8] >> i % 8) & 0x01: key = ( setting_class.keys_universe[i] if i in setting_class.keys_universe else common.NamedInt(i, "KEY " + str(i)) ) choices_map[key] = setting_class.choices_universe result = cls(choices_map) if choices_map else None return result SETTINGS = [ RegisterHandDetection, # simple RegisterSmoothScroll, # simple RegisterSideScroll, # simple RegisterDpi, RegisterFnSwap, # working HiResScroll, # simple LowresMode, # simple HiresSmoothInvert, # working HiresSmoothResolution, # working HiresMode, # simple ScrollRatchet, # simple SmartShift, # working ScrollRatchetEnhanced, SmartShiftEnhanced, # simple ThumbInvert, # working ThumbMode, # working OnboardProfiles, ReportRate, # working ExtendedReportRate, PointerSpeed, # simple AdjustableDpi, # working ExtendedAdjustableDpi, SpeedChange, # Backlight, # not working - disabled temporarily Backlight2, # working Backlight2Level, Backlight2DurationHandsOut, Backlight2DurationHandsIn, Backlight2DurationPowered, Backlight3, LEDControl, LEDZoneSetting, RGBControl, RGBEffectSetting, BrightnessControl, PerKeyLighting, FnSwap, # simple NewFnSwap, # simple K375sFnSwap, # working ReprogrammableKeys, # working PersistentRemappableAction, DivertKeys, # working DisableKeyboardKeys, # working CrownSmooth, # working DivertCrown, # working DivertGkeys, # working MKeyLEDs, # working MRKeyLED, # working Multiplatform, # working DualPlatform, # simple ChangeHost, # working Gesture2Gestures, # working Gesture2Divert, Gesture2Params, # working Sidetone, Equalizer, ADCPower, ] def check_feature(device, settings_class: settings.Setting) -> None | bool | Any: if settings_class.feature not in device.features: return if settings_class.min_version > device.features.get_feature_version(settings_class.feature): return try: detected = settings_class.build(device) if logger.isEnabledFor(logging.DEBUG): logger.debug("check_feature %s [%s] detected %s", settings_class.name, settings_class.feature, detected) return detected except Exception as e: logger.error( "check_feature %s [%s] error %s\n%s", settings_class.name, settings_class.feature, e, traceback.format_exc() ) return False # differentiate from an error-free determination that the setting is not supported # Returns True if device was queried to find features, False otherwise def check_feature_settings(device, already_known): """Auto-detect device settings by the HID++ 2.0 features they have.""" if not device.features or not device.online: return False if device.protocol and device.protocol < 2.0: return False absent = device.persister.get("_absent", []) if device.persister else [] newAbsent = [] for sclass in SETTINGS: if sclass.feature: known_present = device.persister and sclass.name in device.persister if not any(s.name == sclass.name for s in already_known) and (known_present or sclass.name not in absent): setting = check_feature(device, sclass) if isinstance(setting, list): for s in setting: already_known.append(s) if sclass.name in newAbsent: newAbsent.remove(sclass.name) elif setting: already_known.append(setting) if sclass.name in newAbsent: newAbsent.remove(sclass.name) elif setting is None: if sclass.name not in newAbsent and sclass.name not in absent and sclass.name not in device.persister: newAbsent.append(sclass.name) if device.persister and newAbsent: absent.extend(newAbsent) device.persister["_absent"] = absent return True def check_feature_setting(device, setting_name): for sclass in SETTINGS: if sclass.feature and sclass.name == setting_name and device.features: setting = check_feature(device, sclass) if setting: return setting Solaar-1.1.14/lib/logitech_receiver/special_keys.py000066400000000000000000001355001473525213700223110ustar00rootroot00000000000000## Copyright (C) 2012-2013 Daniel Pavel ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # Reprogrammable keys information # Mostly from Logitech documentation, but with some edits for better Linux compatibility import os from enum import IntEnum import yaml from .common import NamedInts from .common import UnsortedNamedInts _XDG_CONFIG_HOME = os.environ.get("XDG_CONFIG_HOME") or os.path.expanduser(os.path.join("~", ".config")) _keys_file_path = os.path.join(_XDG_CONFIG_HOME, "solaar", "keys.yaml") # tasks.py Switch_Presentation__Switch_Screen=0x0093, # on K400 Plus Minimize_Window=0x0094, Maximize_Window=0x0095, # on K400 Plus MultiPlatform_App_Switch=0x0096, MultiPlatform_Home=0x0097, MultiPlatform_Menu=0x0098, MultiPlatform_Back=0x0099, Switch_Language=0x009A, # Mac_switch_language Screen_Capture=0x009B, # Mac_screen_Capture, on Craft Keyboard Gesture_Button=0x009C, Smart_Shift=0x009D, AppExpose=0x009E, Smart_Zoom=0x009F, Lookup=0x00A0, Microphone_on__off=0x00A1, Wifi_on__off=0x00A2, Brightness_Down=0x00A3, Brightness_Up=0x00A4, Display_Out=0x00A5, View_Open_Apps=0x00A6, View_All_Open_Apps=0x00A7, AppSwitch=0x00A8, Gesture_Button_Navigation=0x00A9, # Mouse_Thumb_Button on MX Master Fn_inversion=0x00AA, Multiplatform_Back=0x00AB, Multiplatform_Forward=0x00AC, Multiplatform_Gesture_Button=0x00AD, HostSwitch_Channel_1=0x00AE, HostSwitch_Channel_2=0x00AF, HostSwitch_Channel_3=0x00B0, Multiplatform_Search=0x00B1, Multiplatform_Home__Mission_Control=0x00B2, Multiplatform_Menu__Launchpad=0x00B3, Virtual_Gesture_Button=0x00B4, Cursor=0x00B5, Keyboard_Right_Arrow=0x00B6, SW_Custom_Highlight=0x00B7, Keyboard_Left_Arrow=0x00B8, TBD=0x00B9, Multiplatform_Language_Switch=0x00BA, SW_Custom_Highlight_2=0x00BB, Fast_Forward=0x00BC, Fast_Backward=0x00BD, Switch_Highlighting=0x00BE, Mission_Control__Task_View=0x00BF, # Switch_Workspace on Craft Keyboard Dashboard_Launchpad__Action_Center=0x00C0, # Application_Launcher on Craft Keyboard Backlight_Down=0x00C1, # Backlight_Down_FW_internal_function Backlight_Up=0x00C2, # Backlight_Up_FW_internal_function Right_Click__App_Contextual_Menu=0x00C3, # Context_Menu on Craft Keyboard DPI_Change=0x00C4, New_Tab=0x00C5, F2=0x00C6, F3=0x00C7, F4=0x00C8, F5=0x00C9, F6=0x00CA, F7=0x00CB, F8=0x00CC, F1=0x00CD, Laser_Button=0x00CE, Laser_Button_Long_Press=0x00CF, Start_Presentation=0x00D0, Blank_Screen=0x00D1, DPI_Switch=0x00D2, # AdjustDPI on MX Vertical Home__Show_Desktop=0x00D3, App_Switch__Dashboard=0x00D4, App_Switch=0x00D5, Fn_Inversion=0x00D6, LeftAndRightClick=0x00D7, Voice_Dictation=0x00D8, Emoji_Smiling_Face_With_Heart_Shaped_Eyes=0x00D9, Emoji_Loudly_Crying_Face=0x00DA, Emoji_Smiley=0x00DB, Emoji_Smiley_With_Tears=0x00DC, Open_Emoji_Panel=0x00DD, Multiplatform_App_Switch__Launchpad=0x00DE, Snipping_Tool=0x00DF, Grave_Accent=0x00E0, Standard_Tab_Key=0x00E1, Caps_Lock=0x00E2, Left_Shift=0x00E3, Left_Control=0x00E4, Left_Option__Start=0x00E5, Left_Command__Alt=0x00E6, Right_Command__Alt=0x00E7, Right_Option__Start=0x00E8, Right_Control=0x00E9, Right_Shift=0x0EA, Insert=0x00EB, Delete=0x00EC, Home=0x00ED, End=0x00EE, Page_Up=0x00EF, Page_Down=0x00F0, Mute_Microphone=0x00F1, Do_Not_Disturb=0x00F2, Backslash=0x00F3, Refresh=0x00F4, Close_Tab=0x00F5, Lang_Switch=0x00F6, Standard_Alphabetical_Key=0x00F7, Right_Option__Start__2=0x00F8, Left_Option=0x00F9, Right_Option=0x00FA, Left_Cmd=0x00FB, Right_Cmd=0x00FC, ) TASK._fallback = lambda x: f"unknown:{x:04X}" # Capabilities and desired software handling for a control # Ref: https://drive.google.com/file/d/10imcbmoxTJ1N510poGdsviEhoFfB_Ua4/view # We treat bytes 4 and 8 of `getCidInfo` as a single bitfield KEY_FLAG = NamedInts( analytics_key_events=0x400, force_raw_XY=0x200, raw_XY=0x100, virtual=0x80, persistently_divertable=0x40, divertable=0x20, reprogrammable=0x10, FN_sensitive=0x08, nonstandard=0x04, is_FN=0x02, mse=0x01, ) # Flags describing the reporting method of a control # We treat bytes 2 and 5 of `get/setCidReporting` as a single bitfield MAPPING_FLAG = NamedInts( analytics_key_events_reporting=0x100, force_raw_XY_diverted=0x40, raw_XY_diverted=0x10, persistently_diverted=0x04, diverted=0x01, ) class CIDGroupBit(IntEnum): g1 = 0x01 g2 = 0x02 g3 = 0x04 g4 = 0x08 g5 = 0x10 g6 = 0x20 g7 = 0x40 g8 = 0x80 class CidGroup(IntEnum): g1 = 1 g2 = 2 g3 = 3 g4 = 4 g5 = 5 g6 = 6 g7 = 7 g8 = 8 DISABLE = NamedInts( Caps_Lock=0x01, Num_Lock=0x02, Scroll_Lock=0x04, Insert=0x08, Win=0x10, # aka Super ) DISABLE._fallback = lambda x: f"unknown:{x:02X}" # HID USB Keycodes from https://www.usb.org/sites/default/files/documents/hut1_12v2.pdf # Modified by information from Linux HID driver linux/drivers/hid/hid-input.c USB_HID_KEYCODES = NamedInts( A=0x04, B=0x05, C=0x06, D=0x07, E=0x08, F=0x09, G=0x0A, H=0x0B, I=0x0C, J=0x0D, K=0x0E, L=0x0F, M=0x10, N=0x11, O=0x12, P=0x13, Q=0x14, R=0x15, S=0x16, T=0x17, U=0x18, V=0x19, W=0x1A, X=0x1B, Y=0x1C, Z=0x1D, ENTER=0x28, ESC=0x29, BACKSPACE=0x2A, TAB=0x2B, SPACE=0x2C, MINUS=0x2D, EQUAL=0x2E, LEFTBRACE=0x2F, RIGHTBRACE=0x30, BACKSLASH=0x31, HASHTILDE=0x32, SEMICOLON=0x33, APOSTROPHE=0x34, GRAVE=0x35, COMMA=0x36, DOT=0x37, SLASH=0x38, CAPSLOCK=0x39, F1=0x3A, F2=0x3B, F3=0x3C, F4=0x3D, F5=0x3E, F6=0x3F, F7=0x40, F8=0x41, F9=0x42, F10=0x43, F11=0x44, F12=0x45, SYSRQ=0x46, SCROLLLOCK=0x47, PAUSE=0x48, INSERT=0x49, HOME=0x4A, PAGEUP=0x4B, DELETE=0x4C, END=0x4D, PAGEDOWN=0x4E, RIGHT=0x4F, LEFT=0x50, DOWN=0x51, UP=0x52, NUMLOCK=0x53, KPSLASH=0x54, KPASTERISK=0x55, KPMINUS=0x56, KPPLUS=0x57, KPENTER=0x58, KP1=0x59, KP2=0x5A, KP3=0x5B, KP4=0x5C, KP5=0x5D, KP6=0x5E, KP7=0x5F, KP8=0x60, KP9=0x61, KP0=0x62, KPDOT=0x63, COMPOSE=0x65, POWER=0x66, KPEQUAL=0x67, F13=0x68, F14=0x69, F15=0x6A, F16=0x6B, F17=0x6C, F18=0x6D, F19=0x6E, F20=0x6F, F21=0x70, F22=0x71, F23=0x72, F24=0x73, OPEN=0x74, HELP=0x75, PROPS=0x76, FRONT=0x77, STOP=0x78, AGAIN=0x79, UNDO=0x7A, CUT=0x7B, COPY=0x7C, PASTE=0x7D, FIND=0x7E, MUTE=0x7F, VOLUMEUP=0x80, VOLUMEDOWN=0x81, KPCOMMA=0x85, RO=0x87, KATAKANAHIRAGANA=0x88, YEN=0x89, HENKAN=0x8A, MUHENKAN=0x8B, KPJPCOMMA=0x8C, HANGEUL=0x90, HANJA=0x91, KATAKANA=0x92, HIRAGANA=0x93, ZENKAKUHANKAKU=0x94, KPLEFTPAREN=0xB6, KPRIGHTPAREN=0xB7, LEFTCTRL=0xE0, LEFTSHIFT=0xE1, LEFTALT=0xE2, LEFTWINDOWS=0xE3, RIGHTCTRL=0xE4, RIGHTSHIFT=0xE5, RIGHTALT=0xE6, RIGHTMETA=0xE7, MEDIA_PLAYPAUSE=0xE8, MEDIA_STOPCD=0xE9, MEDIA_PREVIOUSSONG=0xEA, MEDIA_NEXTSONG=0xEB, MEDIA_EJECTCD=0xEC, MEDIA_VOLUMEUP=0xED, MEDIA_VOLUMEDOWN=0xEE, MEDIA_MUTE=0xEF, MEDIA_WWW=0xF0, MEDIA_BACK=0xF1, MEDIA_FORWARD=0xF2, MEDIA_STOP=0xF3, MEDIA_FIND=0xF4, MEDIA_SCROLLUP=0xF5, MEDIA_SCROLLDOWN=0xF6, MEDIA_EDIT=0xF7, MEDIA_SLEEP=0xF8, MEDIA_COFFEE=0xF9, MEDIA_REFRESH=0xFA, MEDIA_CALC=0xFB, ) USB_HID_KEYCODES[0] = "No Output" USB_HID_KEYCODES[0x1E] = "1" USB_HID_KEYCODES[0x1F] = "2" USB_HID_KEYCODES[0x20] = "3" USB_HID_KEYCODES[0x21] = "4" USB_HID_KEYCODES[0x22] = "5" USB_HID_KEYCODES[0x23] = "6" USB_HID_KEYCODES[0x24] = "7" USB_HID_KEYCODES[0x25] = "8" USB_HID_KEYCODES[0x26] = "9" USB_HID_KEYCODES[0x27] = "0" USB_HID_KEYCODES[0x64] = "102ND" HID_CONSUMERCODES = NamedInts( { # Unassigned=0x00, # Consumer_Control=0x01, # Numeric_Key_Pad=0x02, # Programmable_Buttons=0x03, # Microphone=0x04, # Headphone=0x05, # Graphic_Equalizer=0x06, # AM__PM=0x22, "Power": 0x30, "Reset": 0x31, "Sleep": 0x32, "Sleep_After": 0x33, "Sleep_Mode": 0x34, "Illumination": 0x35, "Function_Buttons": 0x36, "Menu": 0x40, "Menu__Pick": 0x41, "Menu_Up": 0x42, "Menu_Down": 0x43, "Menu_Left": 0x44, "Menu_Right": 0x45, "Menu_Escape": 0x46, "Menu_Value_Increase": 0x47, "Menu_Value_Decrease": 0x48, "Data_On_Screen": 0x60, "Closed_Caption": 0x61, # Closed_Caption_Select=0x62, "VCR__TV": 0x63, # Broadcast_Mode=0x64, "Snapshot": 0x65, # Still=0x66, "Red": 0x69, "Green": 0x6A, "Blue": 0x6B, "Yellow": 0x6C, "Aspect_Ratio": 0x6D, "Brightness_Up": 0x6F, "Brightness_Down": 0x70, "Brightness_Toggle": 0x72, "Brightness_Min": 0x73, "Brightness_Max": 0x74, "Brightness_Auto": 0x75, "Keyboard_Illumination_Up": 0x79, "Keyboard_Illumination_Down": 0x7A, "Keyboard_Illumination_Toggle": 0x7C, # Selection=0x80, # Assign_Selection=0x81, "Mode_Step": 0x82, "Recall_Last": 0x83, "Enter_Channel": 0x84, # Order_Movie=0x85, # Channel=0x86, # Media_Selection=0x87, "Media_Select_Computer": 0x88, "Media_Select_TV": 0x89, "Media_Select_WWW": 0x8A, "Media_Select_DVD": 0x8B, "Media_Select_Telephone": 0x8C, "Media_Select_Program_Guide": 0x8D, "Media_Select_Video_Phone": 0x8E, "Media_Select_Games": 0x8F, "Media_Select_Messages": 0x90, "Media_Select_CD": 0x91, "Media_Select_VCR": 0x92, "Media_Select_Tuner": 0x93, "Quit": 0x94, "Help": 0x95, "Media_Select_Tape": 0x96, "Media_Select_Cable": 0x97, "Media_Select_Satellite": 0x98, "Media_Select_Security": 0x99, "Media_Select_Home": 0x9A, # Media_Select_Call=0x9B, "Channel_Increment": 0x9C, "Channel_Decrement": 0x9D, # Media_Select_SAP=0x9E, "VCR_Plus": 0xA0, # Once=0xA1, # Daily=0xA2, # Weekly=0xA3, # Monthly=0xA4, "Play": 0xB0, "Pause": 0xB1, "Record": 0xB2, "Fast_Forward": 0xB3, "Rewind": 0xB4, "Scan_Next_Track": 0xB5, "Scan_Previous_Track": 0xB6, "Stop": 0xB7, "Eject": 0xB8, "Random_Play": 0xB9, "Select_DisC": 0xBA, "Enter_Disc": 0xBB, "Repeat": 0xBC, "Tracking": 0xBD, "Track_Normal": 0xBE, "Slow_Tracking": 0xBF, # Frame_Forward=0xC0, # Frame_Back=0xC1, # Mark=0xC2, # Clear_Mark=0xC3, # Repeat_From_Mark=0xC4, # Return_To_Mark=0xC5, # Search_Mark_Forward=0xC6, # Search_Mark_Backwards=0xC7, # Counter_Reset=0xC8, # Show_Counter=0xC9, # Tracking_Increment=0xCA, # Tracking_Decrement=0xCB, # Stop__Eject=0xCC, "Play__Pause": 0xCD, # Play__Skip=0xCE, "Volume": 0xE0, # Balance=0xE1, "Mute": 0xE2, # Bass=0xE3, # Treble=0xE4, "Bass_Boost": 0xE5, # Surround_Mode=0xE6, # Loudness=0xE7, # MPX=0xE8, "Volume_Up": 0xE9, "Volume_Down": 0xEA, # Speed_Select=0xF0, # Playback_Speed=0xF1, # Standard_Play=0xF2, # Long_Play=0xF3, # Extended_Play=0xF4, "Slow": 0xF5, "Fan_Enable": 0x100, "Fan_Speed": 0x101, "Light": 0x102, "Light_Illumination_Level": 0x103, "Climate_Control_Enable": 0x104, "Room_Temperature": 0x105, "Security_Enable": 0x106, "Fire_Alarm": 0x107, "Police_Alarm": 0x108, "Proximity": 0x109, "Motion": 0x10A, "Duress_Alarm": 0x10B, "Holdup_Alarm": 0x10C, "Medical_Alarm": 0x10D, "Balance_Right": 0x150, "Balance_Left": 0x151, "Bass_Increment": 0x152, "Bass_Decrement": 0x153, "Treble_Increment": 0x154, "Treble_Decrement": 0x155, "Speaker_System": 0x160, "Channel_Left": 0x161, "Channel_Right": 0x162, "Channel_Center": 0x163, "Channel_Front": 0x164, "Channel_Center_Front": 0x165, "Channel_Side": 0x166, "Channel_Surround": 0x167, "Channel_Low_Frequency_Enhancement": 0x168, "Channel_Top": 0x169, "Channel_Unknown": 0x16A, "Subchannel": 0x170, "Subchannel_Increment": 0x171, "Subchannel_Decrement": 0x172, "Alternate_Audio_Increment": 0x173, "Alternate_Audio_Decrement": 0x174, "Application_Launch_Buttons": 0x180, "AL_Launch_Button_Configuration_Tool": 0x181, "AL_Programmable_Button_Configuration": 0x182, "AL_Consumer_Control_Configuration": 0x183, "AL_Word_Processor": 0x184, "AL_Text_Editor": 0x185, "AL_Spreadsheet": 0x186, "AL_Graphics_Editor": 0x187, "AL_Presentation_App": 0x188, "AL_Database_App": 0x189, "AL_Email_Reader": 0x18A, "AL_Newsreader": 0x18B, "AL_Voicemail": 0x18C, "AL_Contacts__Address_Book": 0x18D, "AL_Calendar__Schedule": 0x18E, "AL_Task__Project_Manager": 0x18F, "AL_Log__Journal__Timecard": 0x190, "AL_Checkbook__Finance": 0x191, "AL_Calculator": 0x192, "AL_A__V_Capture__Playback": 0x193, "AL_Local_Machine_Browser": 0x194, "AL_LAN__WAN_Browser": 0x195, "AL_Internet_Browser": 0x196, "AL_Remote_Networking__ISP_Connect": 0x197, "AL_Network_Conference": 0x198, "AL_Network_Chat": 0x199, "AL_Telephony__Dialer": 0x19A, "AL_Logon": 0x19B, "AL_Logoff": 0x19C, "AL_Logon__Logoff": 0x19D, "AL_Terminal_Lock__Screensaver": 0x19E, "AL_Control_Panel": 0x19F, "AL_Command_Line_Processor__Run": 0x1A0, "AL_Process__Task_Manager": 0x1A1, "AL_Select_Tast__Application": 0x1A2, "AL_Next_Task__Application": 0x1A3, "AL_Previous_Task__Application": 0x1A4, "AL_Preemptive_Halt_Task__Application": 0x1A5, "AL_Integrated_Help_Center": 0x1A6, "AL_Documents": 0x1A7, "AL_Thesaurus": 0x1A8, "AL_Dictionary": 0x1A9, "AL_Desktop": 0x1AA, "AL_Spell_Check": 0x1AB, "AL_Grammar_Check": 0x1AC, "AL_Wireless_Status": 0x1AD, "AL_Keyboard_Layout": 0x1AE, "AL_Virus_Protection": 0x1AF, "AL_Encryption": 0x1B0, "AL_Screen_Saver": 0x1B1, "AL_Alarms": 0x1B2, "AL_Clock": 0x1B3, "AL_File_Browser": 0x1B4, "AL_Power_Status": 0x1B5, "AL_Image_Browser": 0x1B6, "AL_Audio_Browser": 0x1B7, "AL_Movie_Browser": 0x1B8, "AL_Digital_Rights_Manager": 0x1B9, "AL_Digital_Wallet": 0x1BA, "AL_Instant_Messaging": 0x1BC, "AL_OEM_Features___Tips__Tutorial_Browser": 0x1BD, "AL_OEM_Help": 0x1BE, "AL_Online_Community": 0x1BF, "AL_Entertainment_Content_Browser": 0x1C0, "AL_Online_Shopping_Browser": 0x1C1, "AL_SmartCard_Information__Help": 0x1C2, "AL_Market_Monitor__Finance_Browser": 0x1C3, "AL_Customized_Corporate_News_Browser": 0x1C4, "AL_Online_Activity_Browser": 0x1C5, "AL_Research__Search_Browser": 0x1C6, "AL_Audio_Player": 0x1C7, "Generic_GUI_Application_Controls": 0x200, "AC_New": 0x201, "AC_Open": 0x202, "AC_Close": 0x203, "AC_Exit": 0x204, "AC_Maximize": 0x205, "AC_Minimize": 0x206, "AC_Save": 0x207, "AC_Print": 0x208, "AC_Properties": 0x209, "AC_Undo": 0x21A, "AC_Copy": 0x21B, "AC_Cut": 0x21C, "AC_Paste": 0x21D, "AC_Select_All": 0x21E, "AC_Find": 0x21F, "AC_Find_and_Replace": 0x220, "AC_Search": 0x221, "AC_Go_To": 0x222, "AC_Home": 0x223, "AC_Back": 0x224, "AC_Forward": 0x225, "AC_Stop": 0x226, "AC_Refresh": 0x227, "AC_Previous_Link": 0x228, "AC_Next_Link": 0x229, "AC_Bookmarks": 0x22A, "AC_History": 0x22B, "AC_Subscriptions": 0x22C, "AC_Zoom_In": 0x22D, "AC_Zoom_Out": 0x22E, "AC_Zoom": 0x22F, "AC_Full_Screen_View": 0x230, "AC_Normal_View": 0x231, "AC_View_Toggle": 0x232, "AC_Scroll_Up": 0x233, "AC_Scroll_Down": 0x234, "AC_Scroll": 0x235, "AC_Pan_Left": 0x236, "AC_Pan_Right": 0x237, "AC_Pan": 0x238, "AC_New_Window": 0x239, "AC_Tile_Horizontally": 0x23A, "AC_Tile_Vertically": 0x23B, "AC_Format": 0x23C, "AC_Edit": 0x23D, "AC_Bold": 0x23E, "AC_Italics": 0x23F, "AC_Underline": 0x240, "AC_Strikethrough": 0x241, "AC_Subscript": 0x242, "AC_Superscript": 0x243, "AC_All_Caps": 0x244, "AC_Rotate": 0x245, "AC_Resize": 0x246, "AC_Flip_horizontal": 0x247, "AC_Flip_Vertical": 0x248, "AC_Mirror_Horizontal": 0x249, "AC_Mirror_Vertical": 0x24A, "AC_Font_Select": 0x24B, "AC_Font_Color": 0x24C, "AC_Font_Size": 0x24D, "AC_Justify_Left": 0x24E, "AC_Justify_Center_H": 0x24F, "AC_Justify_Right": 0x250, "AC_Justify_Block_H": 0x251, "AC_Justify_Top": 0x252, "AC_Justify_Center_V": 0x253, "AC_Justify_Bottom": 0x254, "AC_Justify_Block_V": 0x255, "AC_Indent_Decrease": 0x256, "AC_Indent_Increase": 0x257, "AC_Numbered_List": 0x258, "AC_Restart_Numbering": 0x259, "AC_Bulleted_List": 0x25A, "AC_Promote": 0x25B, "AC_Demote": 0x25C, "AC_Yes": 0x25D, "AC_No": 0x25E, "AC_Cancel": 0x25F, "AC_Catalog": 0x260, "AC_Buy__Checkout": 0x261, "AC_Add_to_Cart": 0x262, "AC_Expand": 0x263, "AC_Expand_All": 0x264, "AC_Collapse": 0x265, "AC_Collapse_All": 0x266, "AC_Print_Preview": 0x267, "AC_Paste_Special": 0x268, "AC_Insert_Mode": 0x269, "AC_Delete": 0x26A, "AC_Lock": 0x26B, "AC_Unlock": 0x26C, "AC_Protect": 0x26D, "AC_Unprotect": 0x26E, "AC_Attach_Comment": 0x26F, "AC_Delete_Comment": 0x270, "AC_View_Comment": 0x271, "AC_Select_Word": 0x272, "AC_Select_Sentence": 0x273, "AC_Select_Paragraph": 0x274, "AC_Select_Column": 0x275, "AC_Select_Row": 0x276, "AC_Select_Table": 0x277, "AC_Select_Object": 0x278, "AC_Redo__Repeat": 0x279, "AC_Sort": 0x27A, "AC_Sort_Ascending": 0x27B, "AC_Sort_Descending": 0x27C, "AC_Filter": 0x27D, "AC_Set_Clock": 0x27E, "AC_View_Clock": 0x27F, "AC_Select_Time_Zone": 0x280, "AC_Edit_Time_Zones": 0x281, "AC_Set_Alarm": 0x282, "AC_Clear_Alarm": 0x283, "AC_Snooze_Alarm": 0x284, "AC_Reset_Alarm": 0x285, "AC_Synchronize": 0x286, "AC_Send__Receive": 0x287, "AC_Send_To": 0x288, "AC_Reply": 0x289, "AC_Reply_All": 0x28A, "AC_Forward_Msg": 0x28B, "AC_Send": 0x28C, "AC_Attach_File": 0x28D, "AC_Upload": 0x28E, "AC_Download_Save_Target_As": 0x28F, "AC_Set_Borders": 0x290, "AC_Insert_Row": 0x291, "AC_Insert_Column": 0x292, "AC_Insert_File": 0x293, "AC_Insert_Picture": 0x294, "AC_Insert_Object": 0x295, "AC_Insert_Symbol": 0x296, "AC_Save_and_Close": 0x297, "AC_Rename": 0x298, "AC_Merge": 0x299, "AC_Split": 0x29A, "AC_Distribute_Horizontally": 0x29B, "AC_Distribute_Vertically": 0x29C, } ) HID_CONSUMERCODES[0x20] = "+10" HID_CONSUMERCODES[0x21] = "+100" HID_CONSUMERCODES._fallback = lambda x: f"unknown:{x:04X}" ## Information for x1c00 Persistent from https://drive.google.com/drive/folders/0BxbRzx7vEV7eWmgwazJ3NUFfQ28 KEYMOD = NamedInts(CTRL=0x01, SHIFT=0x02, ALT=0x04, META=0x08, RCTRL=0x10, RSHIFT=0x20, RALT=0x40, RMETA=0x80) ACTIONID = NamedInts( Empty=0x00, Key=0x01, Mouse=0x02, Xdisp=0x03, Ydisp=0x04, Vscroll=0x05, Hscroll=0x06, Consumer=0x07, Internal=0x08, Power=0x09, ) MOUSE_BUTTONS = NamedInts( Mouse_Button_Left=0x0001, Mouse_Button_Right=0x0002, Mouse_Button_Middle=0x0004, Mouse_Button_Back=0x0008, Mouse_Button_Forward=0x0010, Mouse_Button_6=0x0020, Mouse_Button_Scroll_Left=0x0040, Mouse_Button_Scroll_Right=0x0080, Mouse_Button_9=0x0100, Mouse_Button_10=0x0200, Mouse_Button_11=0x0400, Mouse_Button_12=0x0800, Mouse_Button_13=0x1000, Mouse_Button_DPI=0x2000, Mouse_Button_15=0x4000, Mouse_Button_16=0x8000, ) MOUSE_BUTTONS._fallback = lambda x: f"unknown mouse button:{x:04X}" HORIZONTAL_SCROLL = NamedInts( Horizontal_Scroll_Left=0x4000, Horizontal_Scroll_Right=0x8000, ) HORIZONTAL_SCROLL._fallback = lambda x: f"unknown horizontal scroll:{x:04X}" # Construct universe for Persistent Remappable Keys setting (only for supported values) KEYS = UnsortedNamedInts() KEYS_Default = 0x7FFFFFFF # Special value to reset key to default - has to be different from all others KEYS[KEYS_Default] = "Default" # Value to reset to default KEYS[0] = "None" # Value for no output # Add HID keys plus modifiers modifiers = { 0x00: "", 0x01: "Cntrl+", 0x02: "Shift+", 0x04: "Alt+", 0x08: "Meta+", 0x03: "Cntrl+Shift+", 0x05: "Alt+Cntrl+", 0x09: "Meta+Cntrl+", 0x06: "Alt+Shift+", 0x0A: "Meta+Shift+", 0x0C: "Meta+Alt+", } for val, name in modifiers.items(): for key in USB_HID_KEYCODES: KEYS[(ACTIONID.Key << 24) + (int(key) << 8) + val] = name + str(key) # Add HID Consumer Codes for code in HID_CONSUMERCODES: KEYS[(ACTIONID.Consumer << 24) + (int(code) << 8)] = str(code) # Add Mouse Buttons for code in MOUSE_BUTTONS: KEYS[(ACTIONID.Mouse << 24) + (int(code) << 8)] = str(code) # Add Horizontal Scroll for code in HORIZONTAL_SCROLL: KEYS[(ACTIONID.Hscroll << 24) + (int(code) << 8)] = str(code) # Construct subsets for known devices def persistent_keys(action_ids): keys = UnsortedNamedInts() keys[KEYS_Default] = "Default" # Value to reset to default keys[0] = "No Output (only as default)" for key in KEYS: if (int(key) >> 24) in action_ids: keys[int(key)] = str(key) return keys KEYS_KEYS_CONSUMER = persistent_keys([ACTIONID.Key, ACTIONID.Consumer]) KEYS_KEYS_MOUSE_HSCROLL = persistent_keys([ACTIONID.Key, ACTIONID.Mouse, ACTIONID.Hscroll]) COLORS = UnsortedNamedInts( { # from Xorg rgb.txt,v 1.3 2000/08/17 "red": 0xFF0000, "orange": 0xFFA500, "yellow": 0xFFFF00, "green": 0x00FF00, "blue": 0x0000FF, "purple": 0xA020F0, "violet": 0xEE82EE, "black": 0x000000, "white": 0xFFFFFF, "gray": 0xBEBEBE, "brown": 0xA52A2A, "cyan": 0x00FFFF, "magenta": 0xFF00FF, "pink": 0xFFC0CB, "maroon": 0xB03060, "turquoise": 0x40E0D0, "gold": 0xFFD700, "tan": 0xD2B48C, "snow": 0xFFFAFA, "ghost white": 0xF8F8FF, "white smoke": 0xF5F5F5, "gainsboro": 0xDCDCDC, "floral white": 0xFFFAF0, "old lace": 0xFDF5E6, "linen": 0xFAF0E6, "antique white": 0xFAEBD7, "papaya whip": 0xFFEFD5, "blanched almond": 0xFFEBCD, "bisque": 0xFFE4C4, "peach puff": 0xFFDAB9, "navajo white": 0xFFDEAD, "moccasin": 0xFFE4B5, "cornsilk": 0xFFF8DC, "ivory": 0xFFFFF0, "lemon chiffon": 0xFFFACD, "seashell": 0xFFF5EE, "honeydew": 0xF0FFF0, "mint cream": 0xF5FFFA, "azure": 0xF0FFFF, "alice blue": 0xF0F8FF, "lavender": 0xE6E6FA, "lavender blush": 0xFFF0F5, "misty rose": 0xFFE4E1, "dark slate gray": 0x2F4F4F, "dim gray": 0x696969, "slate gray": 0x708090, "light slate gray": 0x778899, "light gray": 0xD3D3D3, "midnight blue": 0x191970, "navy blue": 0x000080, "cornflower blue": 0x6495ED, "dark slate blue": 0x483D8B, "slate blue": 0x6A5ACD, "medium slate blue": 0x7B68EE, "light slate blue": 0x8470FF, "medium blue": 0x0000CD, "royal blue": 0x4169E1, "dodger blue": 0x1E90FF, "deep sky blue": 0x00BFFF, "sky blue": 0x87CEEB, "light sky blue": 0x87CEFA, "steel blue": 0x4682B4, "light steel blue": 0xB0C4DE, "light blue": 0xADD8E6, "powder blue": 0xB0E0E6, "pale turquoise": 0xAFEEEE, "dark turquoise": 0x00CED1, "medium turquoise": 0x48D1CC, "light cyan": 0xE0FFFF, "cadet blue": 0x5F9EA0, "medium aquamarine": 0x66CDAA, "aquamarine": 0x7FFFD4, "dark green": 0x006400, "dark olive green": 0x556B2F, "dark sea green": 0x8FBC8F, "sea green": 0x2E8B57, "medium sea green": 0x3CB371, "light sea green": 0x20B2AA, "pale green": 0x98FB98, "spring green": 0x00FF7F, "lawn green": 0x7CFC00, "chartreuse": 0x7FFF00, "medium spring green": 0x00FA9A, "green yellow": 0xADFF2F, "lime green": 0x32CD32, "yellow green": 0x9ACD32, "forest green": 0x228B22, "olive drab": 0x6B8E23, "dark khaki": 0xBDB76B, "khaki": 0xF0E68C, "pale goldenrod": 0xEEE8AA, "light goldenrod yellow": 0xFAFAD2, "light yellow": 0xFFFFE0, "light goldenrod": 0xEEDD82, "goldenrod": 0xDAA520, "dark goldenrod": 0xB8860B, "rosy brown": 0xBC8F8F, "indian red": 0xCD5C5C, "saddle brown": 0x8B4513, "sienna": 0xA0522D, "peru": 0xCD853F, "burlywood": 0xDEB887, "beige": 0xF5F5DC, "wheat": 0xF5DEB3, "sandy brown": 0xF4A460, "chocolate": 0xD2691E, "firebrick": 0xB22222, "dark salmon": 0xE9967A, "salmon": 0xFA8072, "light salmon": 0xFFA07A, "dark orange": 0xFF8C00, "coral": 0xFF7F50, "light coral": 0xF08080, "tomato": 0xFF6347, "orange red": 0xFF4500, "hot pink": 0xFF69B4, "deep pink": 0xFF1493, "light pink": 0xFFB6C1, "pale violet red": 0xDB7093, "medium violet red": 0xC71585, "violet red": 0xD02090, "plum": 0xDDA0DD, "orchid": 0xDA70D6, "medium orchid": 0xBA55D3, "dark orchid": 0x9932CC, "dark violet": 0x9400D3, "blue violet": 0x8A2BE2, "medium purple": 0x9370DB, "thistle": 0xD8BFD8, "dark gray": 0xA9A9A9, "dark blue": 0x00008B, "dark cyan": 0x008B8B, "dark magenta": 0x8B008B, "dark red": 0x8B0000, "light green": 0x90EE90, } ) COLORSPLUS = UnsortedNamedInts({"No change": -1}) for i in COLORS: COLORSPLUS[int(i)] = str(i) KEYCODES = NamedInts( { "A": 1, "B": 2, "C": 3, "D": 4, "E": 5, "F": 6, "G": 7, "H": 8, "I": 9, "J": 10, "K": 11, "L": 12, "M": 13, "N": 14, "O": 15, "P": 16, "Q": 17, "R": 18, "S": 19, "T": 20, "U": 21, "V": 22, "W": 23, "X": 24, "Y": 25, "Z": 26, "1": 27, "2": 28, "3": 29, "4": 30, "5": 31, "6": 32, "7": 33, "8": 34, "9": 35, "0": 36, "ENTER": 37, "ESC": 38, "BACKSPACE": 39, "TAB": 40, "SPACE": 41, "-": 42, "=": 43, "[": 44, "]": 45, "\\": 45, "~": 47, ";": 48, "'": 49, "`": 50, ",": 51, ".": 52, "/": 53, "CAPS LOCK": 54, "F1": 55, "F2": 56, "F3": 57, "F4": 58, "F5": 59, "F6": 60, "F7": 61, "F8": 62, "F9": 63, "F10": 64, "F11": 65, "F12": 66, "PRINT": 67, "SCROLL LOCK": 68, "PASTE": 69, "INSERT": 70, "HOME": 71, "PAGE UP": 72, "DELETE": 73, "END": 74, "PAGE DOWN": 75, "RIGHT": 76, "LEFT": 77, "DOWN": 78, "UP": 79, "NUMLOCK": 80, "KEYPAD /": 81, "KEYPAD *": 82, "KEYPAD -": 83, "KEYPAD +": 84, "KEYPAD ENTER": 85, "KEYPAD 1": 86, "KEYPAD 2": 87, "KEYPAD 3": 88, "KEYPAD 4": 89, "KEYPAD 5": 90, "KEYPAD 6": 91, "KEYPAD 7": 92, "KEYPAD 8": 93, "KEYPAD 9": 94, "KEYPAD 0": 95, "KEYPAD .": 96, "COMPOSE": 98, "POWER": 99, "LEFT CTRL": 104, "LEFT SHIFT": 105, "LEFT ALT": 106, "LEFT WINDOWS": 107, "RIGHT CTRL": 108, "RIGHT SHIFT": 109, "RIGHT ALTGR": 110, "RIGHT WINDOWS": 111, "BRIGHTNESS": 153, "PAUSE": 155, "MUTE": 156, "NEXT": 157, "PREVIOUS": 158, "G1": 180, "G2": 181, "G3": 182, "G4": 183, "G5": 184, "LOGO": 210, } ) # load in override dictionary for KEYCODES try: if os.path.isfile(_keys_file_path): with open(_keys_file_path) as keys_file: keys = yaml.safe_load(keys_file) if isinstance(keys, dict): keys = NamedInts(**keys) for k in KEYCODES: if int(k) not in keys and str(k) not in keys: keys[int(k)] = str(k) KEYCODES = keys except Exception as e: print(e) Solaar-1.1.14/lib/solaar/000077500000000000000000000000001473525213700150575ustar00rootroot00000000000000Solaar-1.1.14/lib/solaar/__init__.py000066400000000000000000000024371473525213700171760ustar00rootroot00000000000000## Copyright (C) 2012-2013 Daniel Pavel ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import pkgutil import subprocess import sys NAME = "Solaar" try: __version__ = ( subprocess.check_output( [ "git", "describe", "--always", ], cwd=sys.path[0], stderr=subprocess.DEVNULL, ) .strip() .decode() ) except Exception: try: __version__ = pkgutil.get_data("solaar", "commit").strip().decode() except Exception: __version__ = pkgutil.get_data("solaar", "version").strip().decode() Solaar-1.1.14/lib/solaar/cli/000077500000000000000000000000001473525213700156265ustar00rootroot00000000000000Solaar-1.1.14/lib/solaar/cli/__init__.py000066400000000000000000000171621473525213700177460ustar00rootroot00000000000000## Copyright (C) 2012-2013 Daniel Pavel ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import argparse import logging import sys from importlib import import_module from traceback import extract_tb from traceback import format_exc from logitech_receiver import base from logitech_receiver import device from logitech_receiver import receiver from solaar import NAME logger = logging.getLogger(__name__) def _create_parser(): parser = argparse.ArgumentParser( prog=NAME.lower(), add_help=False, epilog=f"For details on individual actions, run `{NAME.lower()} --help`.", ) subparsers = parser.add_subparsers(title="actions", help="command-line action to perform") sp = subparsers.add_parser("show", help="show information about devices") sp.add_argument( "device", nargs="?", default="all", help="device to show information about; may be a device number (1..6), a serial number, " 'a substring of a device\'s name, or "all" (the default)', ) sp.set_defaults(action="show") sp = subparsers.add_parser("probe", help="probe a receiver (debugging use only)") sp.add_argument( "receiver", nargs="?", help="select receiver by name substring or serial number when more than one is present" ) sp.set_defaults(action="probe") sp = subparsers.add_parser( "profiles", help="read or write onboard profiles", epilog="Only works on active devices.", ) sp.add_argument( "device", help="device to read or write profiles of; may be a device number (1..6), a serial number, " "a substring of a device's name", ) sp.add_argument("profiles", nargs="?", help="file containing YAML dump of profiles") sp.set_defaults(action="profiles") sp = subparsers.add_parser( "config", help="read/write device-specific settings", epilog="Please note that configuration only works on active devices.", ) sp.add_argument( "device", help=( "device to configure; may be a device number (1..6), a serial number, ", "or a substring of a device's name", ), ) sp.add_argument("setting", nargs="?", help="device-specific setting; leave empty to list available settings") sp.add_argument("value_key", nargs="?", help="new value for the setting or key for keyed settings") sp.add_argument("extra_subkey", nargs="?", help="value for keyed or subkey for subkeyed settings") sp.add_argument("extra2", nargs="?", help="value for subkeyed settings") sp.set_defaults(action="config") sp = subparsers.add_parser( "pair", help="pair a new device", epilog="The Logitech Unifying Receiver supports up to 6 paired devices at the same time.", ) sp.add_argument( "receiver", nargs="?", help="select receiver by name substring or serial number when more than one is present" ) sp.set_defaults(action="pair") sp = subparsers.add_parser("unpair", help="unpair a device") sp.add_argument( "device", help="device to unpair; may be a device number (1..6), a serial number, " "or a substring of a device's name.", ) sp.set_defaults(action="unpair") return parser, subparsers.choices _cli_parser, actions = _create_parser() print_help = _cli_parser.print_help def _receivers(dev_path=None): for dev_info in base.receivers(): if dev_path is not None and dev_path != dev_info.path: continue try: r = receiver.create_receiver(base, dev_info) if logger.isEnabledFor(logging.DEBUG): logger.debug("[%s] => %s", dev_info.path, r) if r: yield r except Exception as e: logger.exception("opening " + str(dev_info)) sys.exit(f"{NAME.lower()}: error: {str(e)}") def _receivers_and_devices(dev_path=None): for dev_info in base.receivers_and_devices(): if dev_path is not None and dev_path != dev_info.path: continue try: if dev_info.isDevice: d = device.create_device(base, dev_info) else: d = receiver.create_receiver(base, dev_info) if logger.isEnabledFor(logging.DEBUG): logger.debug("[%s] => %s", dev_info.path, d) if d is not None: yield d except Exception as e: logger.exception("opening " + str(dev_info)) sys.exit(f"{NAME.lower()}: error: {str(e)}") def _find_receiver(receivers, name): assert receivers assert name for r in receivers: if name in r.name.lower() or (r.serial is not None and name == r.serial.lower()): return r def _find_device(receivers, name): assert receivers assert name number = None if len(name) == 1: try: number = int(name) except Exception: pass else: assert not (number < 0) if number > 6: number = None for r in receivers: if not r.isDevice: # look for nth device of receiver if number: dev = r[number] if dev: yield dev count = r.count() else: # wired device, make a device list from it r.ping() r = [r] count = 1 for dev in r: if ( name == dev.serial.lower() or name == dev.codename.lower() or name == str(dev.kind).lower() or name in dev.name.lower() ): yield dev count -= 1 if not count: break def run(cli_args=None, hidraw_path=None): if cli_args: action = cli_args[0] args = _cli_parser.parse_args(cli_args) else: args = _cli_parser.parse_args() # Python 3 has an undocumented 'feature' that breaks parsing empty args # http://bugs.python.org/issue16308 if "cmd" not in args: _cli_parser.print_usage(sys.stderr) sys.stderr.write(f"{NAME.lower()}: error: too few arguments\n") sys.exit(2) action = args.action assert action in actions try: if action == "show" or action == "probe" or action == "config" or action == "profiles": c = list(_receivers_and_devices(hidraw_path)) else: c = list(_receivers(hidraw_path)) if not c: raise Exception( 'No supported device found. Use "lsusb" and "bluetoothctl devices Connected" to list connected devices.' ) m = import_module("." + action, package=__name__) m.run(c, args, _find_receiver, _find_device) except AssertionError: tb_last = extract_tb(sys.exc_info()[2])[-1] sys.exit(f"{NAME.lower()}: assertion failed: {tb_last[0]} line {int(tb_last[1])}") except Exception: sys.exit(f"{NAME.lower()}: error: {format_exc()}") Solaar-1.1.14/lib/solaar/cli/config.py000066400000000000000000000272131473525213700174520ustar00rootroot00000000000000## Copyright (C) 2012-2013 Daniel Pavel ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import yaml from logitech_receiver import settings from logitech_receiver import settings_templates from logitech_receiver.common import NamedInts from solaar import configuration APP_ID = "io.github.pwr_solaar.solaar" def _print_setting(s, verbose=True): print("#", s.label) if verbose: if s.description: print("#", s.description.replace("\n", " ")) if s.kind == settings.KIND.toggle: print("# possible values: on/true/t/yes/y/1 or off/false/f/no/n/0 or Toggle/~") elif s.kind == settings.KIND.choice: print( "# possible values: one of [", ", ".join(str(v) for v in s.choices), "], or higher/lower/highest/max/lowest/min", ) else: # wtf? pass value = s.read(cached=False) if value is None: print(s.name, "= ? (failed to read from device)") else: print(s.name, "=", s.val_to_string(value)) def _print_setting_keyed(s, key, verbose=True): print("#", s.label) if verbose: if s.description: print("#", s.description.replace("\n", " ")) if s.kind == settings.KIND.multiple_toggle: k = next((k for k in s._labels if key == k), None) if k is None: print(s.name, "=? (key not found)") else: print("# possible values: on/true/t/yes/y/1 or off/false/f/no/n/0 or Toggle/~") value = s.read(cached=False) if value is None: print(s.name, "= ? (failed to read from device)") else: print(s.name, s.val_to_string({k: value[str(int(k))]})) elif s.kind == settings.KIND.map_choice: k = next((k for k in s.choices.keys() if key == k), None) if k is None: print(s.name, "=? (key not found)") else: print("# possible values: one of [", ", ".join(str(v) for v in s.choices[k]), "]") value = s.read(cached=False) if value is None: print(s.name, "= ? (failed to read from device)") else: print(s.name, s.val_to_string({k: value[int(k)]})) def to_int(s): try: return int(s) except ValueError: return None def select_choice(value, choices, setting, key): lvalue = value.lower() ivalue = to_int(value) val = None for choice in choices: if value == str(choice): val = choice break if val is not None: value = val elif ivalue is not None and 1 <= ivalue <= len(choices): value = choices[ivalue - 1] elif lvalue in ("higher", "lower"): old_value = setting.read() if key is None else setting.read_key(key) if old_value is None: raise Exception(f"{setting.name}: could not read current value") if lvalue == "lower": lower_values = choices[:old_value] value = lower_values[-1] if lower_values else choices[:][0] elif lvalue == "higher": higher_values = choices[old_value + 1 :] value = higher_values[0] if higher_values else choices[:][-1] elif lvalue in ("highest", "max", "first"): value = choices[:][-1] elif lvalue in ("lowest", "min", "last"): value = choices[:][0] else: raise Exception(f"{setting.name}: possible values are [{', '.join(str(v) for v in choices)}]") return value def select_toggle(value, setting, key=None): if value.lower() in ("toggle", "~"): value = not (setting.read() if key is None else setting.read()[str(int(key))]) else: try: value = bool(int(value)) except Exception as exc: if value.lower() in ("true", "yes", "on", "t", "y"): value = True elif value.lower() in ("false", "no", "off", "f", "n"): value = False else: raise Exception(f"{setting.name}: don't know how to interpret '{value}' as boolean") from exc return value def select_range(value, setting): try: value = int(value) except ValueError as exc: raise Exception(f"{setting.name}: can't interpret '{value}' as integer") from exc minimum, maximum = setting.range if value < minimum or value > maximum: raise Exception(f"{setting.name}: value '{value}' out of bounds") return value def run(receivers, args, _find_receiver, find_device): assert receivers assert args.device device_name = args.device.lower() dev = None for dev in find_device(receivers, device_name): if dev.ping(): break dev = None if not dev: raise Exception(f"no online device found matching '{device_name}'") if not args.setting: # print all settings, so first set them all up if not dev.settings: raise Exception(f"no settings for {dev.name}") configuration.attach_to(dev) print(dev.name, f"({dev.codename}) [{dev.wpid}:{dev.serial}]") for s in dev.settings: print("") _print_setting(s) return setting_name = args.setting.lower() setting = settings_templates.check_feature_setting(dev, setting_name) if not setting and dev.descriptor and dev.descriptor.settings: for sclass in dev.descriptor.settings: if sclass.register and sclass.name == setting_name: try: setting = sclass.build(dev) except Exception: setting = None if setting is None: raise Exception(f"no setting '{args.setting}' for {dev.name}") if args.value_key is None: _print_setting(setting) return remote = False try: import gi gi.require_version("Gtk", "3.0") from gi.repository import Gio from gi.repository import Gtk if Gtk.init_check()[0]: # can Gtk be initialized? application = Gtk.Application.new(APP_ID, Gio.ApplicationFlags.HANDLES_COMMAND_LINE) application.register() remote = application.get_is_remote() except Exception: pass # don't save configuration here because we might want the Solaar GUI to make the change result, message, value = set(dev, setting, args, False) if message is not None: print(message) if result is None: raise Exception(f"{setting.name}: failed to set value '{str(value)}' [{value!r}]") # if the Solaar UI is running tell it to also perform the set, otherwise save the change in the configuration file if remote: argl = ["config", dev.serial or dev.unitId, setting.name] argl.extend([a for a in [args.value_key, args.extra_subkey, args.extra2] if a is not None]) application.run(yaml.safe_dump(argl)) else: if dev.persister and setting.persist: dev.persister[setting.name] = setting._value def set(dev, setting, args, save): if setting.kind == settings.KIND.toggle: value = select_toggle(args.value_key, setting) args.value_key = value message = f"Setting {setting.name} of {dev.name} to {value}" result = setting.write(value, save=save) elif setting.kind == settings.KIND.range: value = select_range(args.value_key, setting) args.value_key = value message = f"Setting {setting.name} of {dev.name} to {value}" result = setting.write(value, save=save) elif setting.kind == settings.KIND.choice: value = select_choice(args.value_key, setting.choices, setting, None) args.value_key = int(value) message = f"Setting {setting.name} of {dev.name} to {value}" result = setting.write(value, save=save) elif setting.kind == settings.KIND.map_choice: if args.extra_subkey is None: _print_setting_keyed(setting, args.value_key) return None, None, None key = args.value_key ikey = to_int(key) k = next((k for k in setting.choices.keys() if key == k), None) if k is None and ikey is not None: k = next((k for k in setting.choices.keys() if ikey == k), None) if k is not None: value = select_choice(args.extra_subkey, setting.choices[k], setting, key) args.extra_subkey = int(value) args.value_key = str(int(k)) else: raise Exception(f"{setting.name}: key '{key}' not in setting") message = f"Setting {setting.name} of {dev.name} key {k!r} to {value!r}" result = setting.write_key_value(int(k), value, save=save) elif setting.kind == settings.KIND.multiple_toggle: if args.extra_subkey is None: _print_setting_keyed(setting, args.value_key) return None, None, None key = args.value_key all_keys = getattr(setting, "choices_universe", None) ikey = all_keys[int(key) if key.isdigit() else key] if isinstance(all_keys, NamedInts) else to_int(key) k = next((k for k in setting._labels if key == k), None) if k is None and ikey is not None: k = next((k for k in setting._labels if ikey == k), None) if k is not None: value = select_toggle(args.extra_subkey, setting, key=k) args.extra_subkey = value args.value_key = str(int(k)) else: raise Exception(f"{setting.name}: key '{key}' not in setting") message = f"Setting {setting.name} key {k!r} to {value!r}" result = setting.write_key_value(str(int(k)), value, save=save) elif setting.kind == settings.KIND.multiple_range: if args.extra_subkey is None: raise Exception(f"{setting.name}: setting needs both key and value to set") key = args.value_key all_keys = getattr(setting, "choices_universe", None) ikey = all_keys[int(key) if key.isdigit() else key] if isinstance(all_keys, NamedInts) else to_int(key) if args.extra2 is None or to_int(args.extra2) is None: raise Exception(f"{setting.name}: setting needs an integer value, not {args.extra2}") if not setting._value: # ensure that there are values to look through setting.read() k = next((k for k in setting._value if key == ikey or key.isdigit() and ikey == int(key)), None) if k is None and ikey is not None: k = next((k for k in setting._value if ikey == k), None) item = setting._value[k] if args.extra_subkey in item.keys(): item[args.extra_subkey] = to_int(args.extra2) args.value_key = str(int(k)) else: raise Exception(f"{setting.name}: key '{key}' not in setting") message = f"Setting {setting.name} key {k} parameter {args.extra_subkey} to {item[args.extra_subkey]!r}" result = setting.write_key_value(int(k), item, save=save) value = item else: raise Exception("NotImplemented") return result, message, value Solaar-1.1.14/lib/solaar/cli/pair.py000066400000000000000000000143411473525213700171360ustar00rootroot00000000000000## Copyright (C) 2012-2013 Daniel Pavel ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. from time import time from logitech_receiver import base from logitech_receiver import hidpp10 from logitech_receiver import hidpp10_constants from logitech_receiver import notifications _hidpp10 = hidpp10.Hidpp10() def run(receivers, args, find_receiver, _ignore): assert receivers if args.receiver: receiver_name = args.receiver.lower() receiver = find_receiver(receivers, receiver_name) if not receiver: raise Exception(f"no receiver found matching '{receiver_name}'") else: receiver = receivers[0] assert receiver # check if it's necessary to set the notification flags old_notification_flags = _hidpp10.get_notification_flags(receiver) or 0 if not (old_notification_flags & hidpp10_constants.NOTIFICATION_FLAG.wireless): _hidpp10.set_notification_flags(receiver, old_notification_flags | hidpp10_constants.NOTIFICATION_FLAG.wireless) # get all current devices known_devices = [dev.number for dev in receiver] class _HandleWithNotificationHook(int): def notifications_hook(self, n): nonlocal known_devices assert n if n.devnumber == 0xFF: notifications.process(receiver, n) elif n.sub_id == 0x41 and len(n.data) == base.SHORT_MESSAGE_SIZE - 4: kd, known_devices = known_devices, None # only process one connection notification if kd is not None: if n.devnumber not in kd: receiver.pairing.new_device = receiver.register_new_device(n.devnumber, n) elif receiver.re_pairs: del receiver[n.devnumber] # get rid of information on device re-paired away receiver.pairing.new_device = receiver.register_new_device(n.devnumber, n) timeout = 30 # seconds receiver.handle = _HandleWithNotificationHook(receiver.handle) if receiver.receiver_kind == "bolt": # Bolt receivers require authentication to pair a device receiver.discover(timeout=timeout) print("Bolt Pairing: long-press the pairing key or button on your device (timing out in", timeout, "seconds).") pairing_start = time() patience = 5 # the discovering notification may come slightly later, so be patient while receiver.pairing.discovering or time() - pairing_start < patience: if receiver.pairing.device_address and receiver.pairing.device_authentication and receiver.pairing.device_name: break n = base.read(receiver.handle) n = base.make_notification(*n) if n else None if n: receiver.handle.notifications_hook(n) address = receiver.pairing.device_address name = receiver.pairing.device_name authentication = receiver.pairing.device_authentication kind = receiver.pairing.device_kind print(f"Bolt Pairing: discovered {name}") receiver.pair_device( address=address, authentication=authentication, entropy=20 if kind == hidpp10_constants.DEVICE_KIND.keyboard else 10, ) pairing_start = time() patience = 5 # the discovering notification may come slightly later, so be patient while receiver.pairing.lock_open or time() - pairing_start < patience: if receiver.pairing.device_passkey: break n = base.read(receiver.handle) n = base.make_notification(*n) if n else None if n: receiver.handle.notifications_hook(n) if authentication & 0x01: print(f"Bolt Pairing: type passkey {receiver.pairing.device_passkey} and then press the enter key") else: passkey = f"{int(receiver.pairing.device_passkey):010b}" passkey = ", ".join(["right" if bit == "1" else "left" for bit in passkey]) print(f"Bolt Pairing: press {passkey}") print("and then press left and right buttons simultaneously") while receiver.pairing.lock_open: n = base.read(receiver.handle) n = base.make_notification(*n) if n else None if n: receiver.handle.notifications_hook(n) else: receiver.set_lock(False, timeout=timeout) print("Pairing: Turn your device on or press, hold, and release") print("a channel button or the channel switch button.") print("Timing out in", timeout, "seconds.") pairing_start = time() patience = 5 # the lock-open notification may come slightly later, wait for it a bit while receiver.pairing.lock_open or time() - pairing_start < patience: n = base.read(receiver.handle) if n: n = base.make_notification(*n) if n: receiver.handle.notifications_hook(n) if not (old_notification_flags & hidpp10_constants.NOTIFICATION_FLAG.wireless): # only clear the flags if they weren't set before, otherwise a # concurrently running Solaar app might stop working properly _hidpp10.set_notification_flags(receiver, old_notification_flags) if receiver.pairing.new_device: dev = receiver.pairing.new_device print(f"Paired device {int(dev.number)}: {dev.name} ({dev.codename}) [{dev.wpid}:{dev.serial}]") else: error = receiver.pairing.error if error: raise Exception(f"pairing failed: {error}") else: print("Paired device") # this is better than an error Solaar-1.1.14/lib/solaar/cli/probe.py000066400000000000000000000111731473525213700173120ustar00rootroot00000000000000## Copyright (C) 2020 ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. from logitech_receiver import base from logitech_receiver.common import strhex from logitech_receiver.hidpp10_constants import ErrorCode from logitech_receiver.hidpp10_constants import Registers from solaar.cli.show import _print_device from solaar.cli.show import _print_receiver def run(receivers, args, find_receiver, _ignore): assert receivers if args.receiver: receiver_name = args.receiver.lower() receiver = find_receiver(receivers, receiver_name) if not receiver: raise Exception(f"no receiver found matching '{receiver_name}'") else: receiver = receivers[0] assert receiver is not None if receiver.isDevice: _print_device(receiver, 1) return _print_receiver(receiver) print("") print(" Register Dump") rgst = receiver.read_register(Registers.NOTIFICATIONS) print(" Notifications %#04x: %s" % (Registers.NOTIFICATIONS % 0x100, "0x" + strhex(rgst) if rgst else "None")) rgst = receiver.read_register(Registers.RECEIVER_CONNECTION) print( " Connection State %#04x: %s" % (Registers.RECEIVER_CONNECTION % 0x100, "0x" + strhex(rgst) if rgst else "None") ) rgst = receiver.read_register(Registers.DEVICES_ACTIVITY) print( " Device Activity %#04x: %s" % (Registers.DEVICES_ACTIVITY % 0x100, "0x" + strhex(rgst) if rgst else "None") ) for sub_reg in range(0, 16): rgst = receiver.read_register(Registers.RECEIVER_INFO, sub_reg) print( " Pairing Register %#04x %#04x: %s" % (Registers.RECEIVER_INFO % 0x100, sub_reg, "0x" + strhex(rgst) if rgst else "None") ) for device in range(0, 7): for sub_reg in [0x10, 0x20, 0x30, 0x50]: rgst = receiver.read_register(Registers.RECEIVER_INFO, sub_reg + device) print( " Pairing Register %#04x %#04x: %s" % (Registers.RECEIVER_INFO % 0x100, sub_reg + device, "0x" + strhex(rgst) if rgst else "None") ) rgst = receiver.read_register(Registers.RECEIVER_INFO, 0x40 + device) print( " Pairing Name %#04x %#02x: %s" % (Registers.RECEIVER_INFO % 0x100, 0x40 + device, rgst[2 : 2 + ord(rgst[1:2])] if rgst else "None") ) for part in range(1, 4): rgst = receiver.read_register(Registers.RECEIVER_INFO, 0x60 + device, part) print( " Pairing Name %#04x %#02x %#02x: %2d %s" % ( Registers.RECEIVER_INFO % 0x100, 0x60 + device, part, ord(rgst[2:3]) if rgst else 0, rgst[3 : 3 + ord(rgst[2:3])] if rgst else "None", ) ) for sub_reg in range(0, 5): rgst = receiver.read_register(Registers.FIRMWARE, sub_reg) print( " Firmware %#04x %#04x: %s" % (Registers.FIRMWARE % 0x100, sub_reg, "0x" + strhex(rgst) if rgst is not None else "None") ) print("") for reg in range(0, 0xFF): for offset, reg_type in [(0x00, "Short"), (0x200, "Long")]: last = None for sub in range(0, 0xFF): rgst = base.request(receiver.handle, 0xFF, 0x8100 | (offset + reg), sub, return_error=True) if isinstance(rgst, int) and rgst == ErrorCode.INVALID_ADDRESS: break elif isinstance(rgst, int) and rgst == ErrorCode.INVALID_VALUE: continue else: if not isinstance(last, bytes) or not isinstance(rgst, bytes) or last != rgst: print( " Register %s %#04x %#04x: %s" % (reg_type, reg, sub, "0x" + strhex(rgst) if isinstance(rgst, bytes) else str(rgst)) ) last = rgst Solaar-1.1.14/lib/solaar/cli/profiles.py000066400000000000000000000051231473525213700200240ustar00rootroot00000000000000## Copyright (C) 2012-2013 Daniel Pavel ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import traceback import yaml from logitech_receiver.hidpp20 import OnboardProfiles from logitech_receiver.hidpp20 import OnboardProfilesVersion def run(receivers, args, find_receiver, find_device): assert receivers assert args.device device_name = args.device.lower() profiles_file = args.profiles dev = None for dev in find_device(receivers, device_name): if dev.ping(): break dev = None if not dev: raise Exception(f"no online device found matching '{device_name}'") if not (dev.online and dev.profiles): print(f"Device {dev.name} is either offline or has no onboard profiles") elif not profiles_file: print(f"#Dumping profiles from {dev.name}") print(yaml.dump(dev.profiles)) else: try: with open(profiles_file, "r") as f: print(f"Reading profiles from {profiles_file}") profiles = yaml.safe_load(f) if not isinstance(profiles, OnboardProfiles): print("Profiles file does not contain current onboard profiles") elif getattr(profiles, "version", None) != OnboardProfilesVersion: version = getattr(profiles, "version", None) print(f"Missing or incorrect profile version {version} in loaded profile") elif getattr(profiles, "name", None) != dev.name: name = getattr(profiles, "name", None) print(f"Different device name {name} in loaded profile") else: print(f"Loading profiles into {dev.name}") written = profiles.write(dev) print(f"Wrote {written} sectors to {dev.name}") except Exception as exc: print("Profiles not written:", exc) print(traceback.format_exc()) Solaar-1.1.14/lib/solaar/cli/show.py000066400000000000000000000403431473525213700171640ustar00rootroot00000000000000## Copyright (C) 2012-2013 Daniel Pavel ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. from logitech_receiver import common from logitech_receiver import exceptions from logitech_receiver import hidpp10 from logitech_receiver import hidpp10_constants from logitech_receiver import hidpp20 from logitech_receiver import hidpp20_constants from logitech_receiver import receiver from logitech_receiver import settings_templates from logitech_receiver.common import LOGITECH_VENDOR_ID from logitech_receiver.common import NamedInt from logitech_receiver.common import strhex from logitech_receiver.hidpp20_constants import SupportedFeature from solaar import NAME from solaar import __version__ _hidpp10 = hidpp10.Hidpp10() _hidpp20 = hidpp20.Hidpp20() def _print_receiver(receiver): paired_count = receiver.count() print(receiver.name) print(" Device path :", receiver.path) print(f" USB id : {LOGITECH_VENDOR_ID:04x}:{receiver.product_id}") print(" Serial :", receiver.serial) pending = hidpp10.get_configuration_pending_flags(receiver) if pending: print(f" C Pending : {pending:02x}") if receiver.firmware: for f in receiver.firmware: print(" %-11s: %s" % (f.kind, f.version)) print(" Has", paired_count, f"paired device(s) out of a maximum of {int(receiver.max_devices)}.") if receiver.remaining_pairings() and receiver.remaining_pairings() >= 0: print(f" Has {int(receiver.remaining_pairings())} successful pairing(s) remaining.") notification_flags = _hidpp10.get_notification_flags(receiver) if notification_flags is not None: if notification_flags: notification_names = hidpp10_constants.NOTIFICATION_FLAG.flag_names(notification_flags) print(f" Notifications: {', '.join(notification_names)} (0x{notification_flags:06X})") else: print(" Notifications: (none)") activity = receiver.read_register(hidpp10_constants.Registers.DEVICES_ACTIVITY) if activity: activity = [(d, ord(activity[d - 1 : d])) for d in range(1, receiver.max_devices)] activity_text = ", ".join(f"{int(d)}={int(a)}" for d, a in activity if a > 0) print(" Device activity counters:", activity_text or "(empty)") def _battery_text(level) -> str: if level is None: return "N/A" elif isinstance(level, NamedInt): return str(level) else: return f"{int(level)}%" def _battery_line(dev): battery = dev.battery() if battery is not None: level, nextLevel, status, voltage = battery.level, battery.next_level, battery.status, battery.voltage text = _battery_text(level) if voltage is not None: text = text + f" {voltage}mV " nextText = "" if nextLevel is None else ", next level " + _battery_text(nextLevel) print(f" Battery: {text}, {status}{nextText}.") else: print(" Battery status unavailable.") def _print_device(dev, num=None): assert dev is not None # try to ping the device to see if it actually exists and to wake it up try: dev.ping() except exceptions.NoSuchDevice: print(f" {num}: Device not found" or dev.number) return if num or dev.number < 8: print(f" {int(num or dev.number)}: {dev.name}") else: print(f"{dev.name}") print(" Device path :", dev.path) if dev.wpid: print(f" WPID : {dev.wpid}") if dev.product_id: print(f" USB id : {LOGITECH_VENDOR_ID:04x}:{dev.product_id}") print(" Codename :", dev.codename) print(" Kind :", dev.kind) if dev.protocol: print(f" Protocol : HID++ {dev.protocol:1.1f}") else: print(" Protocol : unknown (device is offline)") if dev.polling_rate: print(" Report Rate :", dev.polling_rate) print(" Serial number:", dev.serial) if dev.modelId: print(" Model ID: ", dev.modelId) if dev.unitId: print(" Unit ID: ", dev.unitId) if dev.firmware: for fw in dev.firmware: print(f" {fw.kind:11}:", (fw.name + " " + fw.version).strip()) if dev.power_switch_location: print(f" The power switch is located on the {dev.power_switch_location}.") if dev.online: notification_flags = _hidpp10.get_notification_flags(dev) if notification_flags is not None: if notification_flags: notification_names = hidpp10_constants.NOTIFICATION_FLAG.flag_names(notification_flags) print(f" Notifications: {', '.join(notification_names)} (0x{notification_flags:06X}).") else: print(" Notifications: (none).") device_features = _hidpp10.get_device_features(dev) if device_features is not None: if device_features: device_features_names = hidpp10_constants.DEVICE_FEATURES.flag_names(device_features) print(f" Features: {', '.join(device_features_names)} (0x{device_features:06X})") else: print(" Features: (none)") if dev.online and dev.features: print(f" Supports {len(dev.features)} HID++ 2.0 features:") dev_settings = [] settings_templates.check_feature_settings(dev, dev_settings) for feature, index in dev.features.enumerate(): if isinstance(feature, str): feature_bytes = bytes.fromhex(feature[-4:]) else: feature_bytes = feature.to_bytes(2) feature_int = int.from_bytes(feature_bytes) flags = dev.request(0x0000, feature_bytes) flags = 0 if flags is None else ord(flags[1:2]) flags = common.flag_names(hidpp20_constants.FeatureFlag, flags) version = dev.features.get_feature_version(feature_int) version = version if version else 0 print(" %2d: %-22s {%04X} V%s %s " % (index, feature, feature_int, version, ", ".join(flags))) if feature == SupportedFeature.HIRES_WHEEL: wheel = _hidpp20.get_hires_wheel(dev) if wheel: multi, has_invert, has_switch, inv, res, target, ratchet = wheel print(f" Multiplier: {multi}") if has_invert: print(" Has invert:", "Inverse wheel motion" if inv else "Normal wheel motion") if has_switch: print(" Has ratchet switch:", "Normal wheel mode" if ratchet else "Free wheel mode") if res: print(" High resolution mode") else: print(" Low resolution mode") if target: print(" HID++ notification") else: print(" HID notification") elif feature == SupportedFeature.MOUSE_POINTER: mouse_pointer = _hidpp20.get_mouse_pointer_info(dev) if mouse_pointer: print(f" DPI: {mouse_pointer['dpi']}") print(f" Acceleration: {mouse_pointer['acceleration']}") if mouse_pointer["suggest_os_ballistics"]: print(" Use OS ballistics") else: print(" Override OS ballistics") if mouse_pointer["suggest_vertical_orientation"]: print(" Provide vertical tuning, trackball") else: print(" No vertical tuning, standard mice") elif feature == SupportedFeature.VERTICAL_SCROLLING: vertical_scrolling_info = _hidpp20.get_vertical_scrolling_info(dev) if vertical_scrolling_info: print(f" Roller type: {vertical_scrolling_info['roller']}") print(f" Ratchet per turn: {vertical_scrolling_info['ratchet']}") print(f" Scroll lines: {vertical_scrolling_info['lines']}") elif feature == SupportedFeature.HI_RES_SCROLLING: scrolling_mode, scrolling_resolution = _hidpp20.get_hi_res_scrolling_info(dev) if scrolling_mode: print(" Hi-res scrolling enabled") else: print(" Hi-res scrolling disabled") if scrolling_resolution: print(f" Hi-res scrolling multiplier: {scrolling_resolution}") elif feature == SupportedFeature.POINTER_SPEED: pointer_speed = _hidpp20.get_pointer_speed_info(dev) if pointer_speed: print(f" Pointer Speed: {pointer_speed}") elif feature == SupportedFeature.LOWRES_WHEEL: wheel_status = _hidpp20.get_lowres_wheel_status(dev) if wheel_status: print(f" Wheel Reports: {wheel_status}") elif feature == SupportedFeature.NEW_FN_INVERSION: inversion = _hidpp20.get_new_fn_inversion(dev) if inversion: inverted, default_inverted = inversion print(" Fn-swap:", "enabled" if inverted else "disabled") print(" Fn-swap default:", "enabled" if default_inverted else "disabled") elif feature == SupportedFeature.HOSTS_INFO: host_names = _hidpp20.get_host_names(dev) for host, (paired, name) in host_names.items(): print(f" Host {host} ({'paired' if paired else 'unpaired'}): {name}") elif feature == SupportedFeature.DEVICE_NAME: print(f" Name: {_hidpp20.get_name(dev)}") print(f" Kind: {_hidpp20.get_kind(dev)}") elif feature == SupportedFeature.DEVICE_FRIENDLY_NAME: print(f" Friendly Name: {_hidpp20.get_friendly_name(dev)}") elif feature == SupportedFeature.DEVICE_FW_VERSION: for fw in _hidpp20.get_firmware(dev): extras = strhex(fw.extras) if fw.extras else "" print(f" Firmware: {fw.kind} {fw.name} {fw.version} {extras}") ids = _hidpp20.get_ids(dev) if ids: unitId, modelId, tid_map = ids print(f" Unit ID: {unitId} Model ID: {modelId} Transport IDs: {tid_map}") elif feature == SupportedFeature.REPORT_RATE or feature == SupportedFeature.EXTENDED_ADJUSTABLE_REPORT_RATE: print(f" Report Rate: {_hidpp20.get_polling_rate(dev)}") elif feature == SupportedFeature.CONFIG_CHANGE: response = dev.feature_request(SupportedFeature.CONFIG_CHANGE, 0x00) print(f" Configuration: {response.hex()}") elif feature == SupportedFeature.REMAINING_PAIRING: print(f" Remaining Pairings: {int(_hidpp20.get_remaining_pairing(dev))}") elif feature == SupportedFeature.ONBOARD_PROFILES: if _hidpp20.get_onboard_mode(dev) == hidpp20_constants.OnboardMode.MODE_HOST: mode = "Host" else: mode = "On-Board" print(f" Device Mode: {mode}") elif hidpp20.battery_functions.get(feature, None): print("", end=" ") _battery_line(dev) for setting in dev_settings: if setting.feature == feature: if ( setting._device and getattr(setting._device, "persister", None) and setting._device.persister.get(setting.name) is not None ): v = setting.val_to_string(setting._device.persister.get(setting.name)) print(f" {setting.label} (saved): {v}") try: v = setting.val_to_string(setting.read(False)) except exceptions.FeatureCallError as e: v = "HID++ error " + str(e) except AssertionError as e: v = "AssertionError " + str(e) print(f" {setting.label} : {v}") if dev.online and dev.keys: print(f" Has {len(dev.keys)} reprogrammable keys:") for k in dev.keys: # TODO: add here additional variants for other REPROG_CONTROLS if dev.keys.keyversion == SupportedFeature.REPROG_CONTROLS_V2: print(" %2d: %-26s => %-27s %s" % (k.index, k.key, k.default_task, ", ".join(k.flags))) if dev.keys.keyversion == SupportedFeature.REPROG_CONTROLS_V4: print(" %2d: %-26s, default: %-27s => %-26s" % (k.index, k.key, k.default_task, k.mapped_to)) gmask_fmt = ",".join(k.group_mask) gmask_fmt = gmask_fmt if gmask_fmt else "empty" print(f" {', '.join(k.flags)}, pos:{int(k.pos)}, group:{int(k.group):1}, group mask:{gmask_fmt}") report_fmt = ", ".join(k.mapping_flags) report_fmt = report_fmt if report_fmt else "default" print(f" reporting: {report_fmt}") if dev.online and dev.remap_keys: print(f" Has {len(dev.remap_keys)} persistent remappable keys:") for k in dev.remap_keys: print(" %2d: %-26s => %s%s" % (k.index, k.key, k.action, " (remapped)" if k.cidStatus else "")) if dev.online and dev.gestures: print( " Has %d gesture(s), %d param(s) and %d spec(s):" % (len(dev.gestures.gestures), len(dev.gestures.params), len(dev.gestures.specs)) ) for k in dev.gestures.gestures.values(): print( " %-26s Enabled(%4s): %-5s Diverted:(%4s) %s" % (k.gesture, k.index, k.enabled(), k.diversion_index, k.diverted()) ) for k in dev.gestures.params.values(): print(" %-26s Value (%4s): %s [Default: %s]" % (k.param, k.index, k.value, k.default_value)) for k in dev.gestures.specs.values(): print(" %-26s Spec (%4s): %s" % (k.spec, k.id, k.value)) if dev.online: _battery_line(dev) else: print(" Battery: unknown (device is offline).") def run(devices, args, find_receiver, find_device): assert devices assert args.device print(f"{NAME.lower()} version {__version__}") print("") device_name = args.device.lower() if device_name == "all": for d in devices: if isinstance(d, receiver.Receiver): _print_receiver(d) count = d.count() if count: for dev in d: print("") _print_device(dev, dev.number) count -= 1 if not count: break print("") else: print("") _print_device(d) return dev = find_receiver(devices, device_name) if dev and not dev.isDevice: _print_receiver(dev) return dev = next(find_device(devices, device_name), None) if not dev: raise Exception(f"no device found matching '{device_name}'") _print_device(dev) Solaar-1.1.14/lib/solaar/cli/unpair.py000066400000000000000000000031231473525213700174750ustar00rootroot00000000000000## Copyright (C) 2012-2013 Daniel Pavel ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. def run(receivers, args, find_receiver, find_device): assert receivers assert args.device device_name = args.device.lower() dev = next(find_device(receivers, device_name), None) if not dev: raise Exception(f"no device found matching '{device_name}'") if not dev.receiver.may_unpair: print( "Receiver with USB id %s for %s [%s:%s] does not unpair, but attempting anyway." % (dev.receiver.product_id, dev.name, dev.wpid, dev.serial) ) try: # query these now, it's last chance to get them number, codename, wpid, serial = dev.number, dev.codename, dev.wpid, dev.serial dev.receiver._unpair_device(number, True) # force an unpair print(f"Unpaired {int(number)}: {dev.name} ({codename}) [{wpid}:{serial}]") except Exception as e: raise e Solaar-1.1.14/lib/solaar/configuration.py000066400000000000000000000236731473525213700203130ustar00rootroot00000000000000## Copyright (C) 2012-2013 Daniel Pavel ## Copyright (C) 2014-2024 Solaar Contributors https://pwr-solaar.github.io/Solaar/ ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import json import logging import os import threading import yaml from logitech_receiver.common import NamedInt from solaar import __version__ logger = logging.getLogger(__name__) _XDG_CONFIG_HOME = os.environ.get("XDG_CONFIG_HOME") or os.path.expanduser(os.path.join("~", ".config")) _yaml_file_path = os.path.join(_XDG_CONFIG_HOME, "solaar", "config.yaml") _json_file_path = os.path.join(_XDG_CONFIG_HOME, "solaar", "config.json") _KEY_VERSION = "_version" _KEY_NAME = "_NAME" _KEY_WPID = "_wpid" _KEY_SERIAL = "_serial" _KEY_MODEL_ID = "_modelId" _KEY_UNIT_ID = "_unitId" _KEY_ABSENT = "_absent" _KEY_SENSITIVE = "_sensitive" _config = [] def _load(): loaded_config = [] if os.path.isfile(_yaml_file_path): path = _yaml_file_path try: with open(_yaml_file_path) as config_file: loaded_config = yaml.safe_load(config_file) except Exception as e: logger.error("failed to load from %s: %s", _yaml_file_path, e) elif os.path.isfile(_json_file_path): path = _json_file_path try: with open(_json_file_path) as config_file: loaded_config = json.load(config_file) except Exception as e: logger.error("failed to load from %s: %s", _json_file_path, e) loaded_config = _convert_json(loaded_config) else: path = None if logger.isEnabledFor(logging.DEBUG): logger.debug("load => %s", loaded_config) global _config _config = _parse_config(loaded_config, path) def _parse_config(loaded_config, config_path): current_version = __version__ parsed_config = [current_version] try: if not loaded_config: return parsed_config loaded_version = loaded_config[0] discard_derived_properties = loaded_version != current_version if discard_derived_properties: if logger.isEnabledFor(logging.INFO): logger.info( "config file '%s' was generated by another version of solaar " "(config: %s, current: %s). refreshing detected device capabilities", config_path, loaded_version, current_version, ) for device in loaded_config[1:]: assert isinstance(device, dict) parsed_config.append(_device_entry_from_config_dict(device, discard_derived_properties)) except Exception as e: logger.warning("Exception processing config file '%s', ignoring contents: %s", config_path, e) return parsed_config def _device_entry_from_config_dict(data, discard_derived_properties): divert = data.get("divert-keys") if divert: sliding = data.get("dpi-sliding") if sliding: # convert old-style dpi-sliding setting to divert-keys entry divert[int(sliding)] = 3 data.pop("dpi-sliding", None) gestures = data.get("mouse-gestures") if gestures: # convert old-style mouse-gestures setting to divert-keys entry divert[int(gestures)] = 2 data.pop("mouse-gestures", None) # remove any string entries (from bad conversions) data["divert-keys"] = {k: v for k, v in divert.items() if isinstance(k, int)} if data.get("_sensitive", None) is None: # make scroll wheel settings default to ignore data["_sensitive"] = { "hires-smooth-resolution": "ignore", "hires-smooth-invert": "ignore", "hires-scroll-mode": "ignore", } if discard_derived_properties: data.pop("_absent", None) data.pop("_battery", None) return _DeviceEntry(**data) save_timer = None configuration_lock = threading.Lock() defer_saves = False # don't allow configuration saves to be deferred def save(defer=False): global save_timer if not _config: return dirname = os.path.dirname(_yaml_file_path) if not os.path.isdir(dirname): try: os.makedirs(dirname) except Exception: logger.error("failed to create %s", dirname) return if not defer or not defer_saves: do_save() else: with configuration_lock: if not save_timer: save_timer = threading.Timer(5.0, do_save) save_timer.start() def do_save(): global save_timer with configuration_lock: if save_timer: save_timer.cancel() save_timer = None try: with open(_yaml_file_path, "w") as config_file: yaml.dump(_config, config_file, default_flow_style=None, width=150) if logger.isEnabledFor(logging.INFO): logger.info("saved %s to %s", _config, _yaml_file_path) except Exception as e: logger.error("failed to save to %s: %s", _yaml_file_path, e) def _convert_json(json_dict): config = [json_dict.get(_KEY_VERSION)] for key, dev in json_dict.items(): key = key.split(":") if len(key) == 2: dev[_KEY_WPID] = dev.get(_KEY_WPID) if dev.get(_KEY_WPID) else key[0] dev[_KEY_SERIAL] = dev.get(_KEY_SERIAL) if dev.get(_KEY_SERIAL) else key[1] for k, v in dev.items(): if isinstance(k, str) and not k.startswith("_") and isinstance(v, dict): # convert string keys to ints v = {int(dk) if isinstance(dk, str) else dk: dv for dk, dv in v.items()} dev[k] = v for k in ["mouse-gestures", "dpi-sliding"]: v = dev.get(k, None) if v is True or v is False: dev.pop(k) if "_name" in dev: dev[_KEY_NAME] = dev["_name"] dev.pop("_name") config.append(dev) return config class _DeviceEntry(dict): def __init__(self, **kwargs): super().__init__(**kwargs) def __setitem__(self, key, value): super().__setitem__(key, value) save(defer=True) def update(self, name, wpid, serial, modelId, unitId): if name and name != self.get(_KEY_NAME): super().__setitem__(_KEY_NAME, name) if wpid and wpid != self.get(_KEY_WPID): super().__setitem__(_KEY_WPID, wpid) if serial and serial != self.get(_KEY_SERIAL): super().__setitem__(_KEY_SERIAL, serial) if modelId and modelId != self.get(_KEY_MODEL_ID): super().__setitem__(_KEY_MODEL_ID, modelId) if unitId and unitId != self.get(_KEY_UNIT_ID): super().__setitem__(_KEY_UNIT_ID, unitId) def get_sensitivity(self, name): return self.get(_KEY_SENSITIVE, {}).get(name, False) def set_sensitivity(self, name, value): sensitives = self.get(_KEY_SENSITIVE, {}) if sensitives.get(name) != value: sensitives[name] = value self.__setitem__(_KEY_SENSITIVE, sensitives) def device_representer(dumper, data): return dumper.represent_mapping("tag:yaml.org,2002:map", data) yaml.add_representer(_DeviceEntry, device_representer) def named_int_representer(dumper, data): return dumper.represent_scalar("tag:yaml.org,2002:int", str(int(data))) yaml.add_representer(NamedInt, named_int_representer) # A device can be identified by a combination of WPID and serial number (for receiver-connected devices) # or a combination of modelId and unitId (for direct-connected devices). # But some devices have empty (all zero) modelIds and unitIds. Use the device name as a backup for the modelId. # The worst situation is a receiver-connected device that Solaar has never seen on-line # that is directly connected. Here there is no way to realize that the two devices are the same. # So new entries are not created for unseen off-line receiver-connected devices def persister(device): def match(wpid, serial, modelId, unitId, c): return (wpid and wpid == c.get(_KEY_WPID) and serial and serial == c.get(_KEY_SERIAL)) or ( modelId and modelId == c.get(_KEY_MODEL_ID) and unitId and unitId == c.get(_KEY_UNIT_ID) ) with configuration_lock: if not _config: _load() entry = None # some devices report modelId and unitId as zero so use name and serial for them modelId = device.modelId if device.modelId != "000000000000" else device._name if device.modelId else None unitId = device.unitId if device.modelId != "000000000000" else device._serial if device.unitId else None for c in _config: if isinstance(c, _DeviceEntry) and match(device.wpid, device._serial, modelId, unitId, c): entry = c break if not entry: if not device.online: # don't create entry for offline devices if logger.isEnabledFor(logging.INFO): logger.info("not setting up persister for offline device %s", device._name) return if logger.isEnabledFor(logging.INFO): logger.info("setting up persister for device %s", device.name) entry = _DeviceEntry() _config.append(entry) entry.update(device.name, device.wpid, device.serial, modelId, unitId) return entry def attach_to(device): pass Solaar-1.1.14/lib/solaar/dbus.py000066400000000000000000000057131473525213700163740ustar00rootroot00000000000000## Copyright (C) 2012-2013 Daniel Pavel ## Copyright (C) 2014-2024 Solaar Contributors https://pwr-solaar.github.io/Solaar/ ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. from __future__ import annotations import logging from typing import Callable logger = logging.getLogger(__name__) try: import dbus from dbus.mainloop.glib import DBusGMainLoop # integration into the main GLib loop DBusGMainLoop(set_as_default=True) bus = dbus.SystemBus() assert bus except Exception: # Either the dbus library is not available or the system dbus is not running logger.warning("failed to set up dbus") bus = None _suspend_callback = None _resume_callback = None def _suspend_or_resume(suspend): if suspend and _suspend_callback: _suspend_callback() if not suspend and _resume_callback: _resume_callback() _LOGIND_PATH = "/org/freedesktop/login1" _LOGIND_INTERFACE = "org.freedesktop.login1.Manager" def watch_suspend_resume( on_resume_callback: Callable[[], None] | None = None, on_suspend_callback: Callable[[], None] | None = None, ): """Register callback for suspend/resume events. They are called only if the system DBus is running, and the Login daemon is available.""" global _resume_callback, _suspend_callback _suspend_callback = on_suspend_callback _resume_callback = on_resume_callback if bus is not None and on_resume_callback is not None or on_suspend_callback is not None: bus.add_signal_receiver( _suspend_or_resume, "PrepareForSleep", dbus_interface=_LOGIND_INTERFACE, path=_LOGIND_PATH, ) if logger.isEnabledFor(logging.INFO): logger.info("connected to system dbus, watching for suspend/resume events") _BLUETOOTH_PATH_PREFIX = "/org/bluez/hci0/dev_" _BLUETOOTH_INTERFACE = "org.freedesktop.DBus.Properties" _bluetooth_callbacks = {} def watch_bluez_connect(serial, callback=None): if _bluetooth_callbacks.get(serial): _bluetooth_callbacks.get(serial).remove() path = _BLUETOOTH_PATH_PREFIX + serial.replace(":", "_").upper() if bus is not None and callback is not None: _bluetooth_callbacks[serial] = bus.add_signal_receiver( callback, "PropertiesChanged", path=path, dbus_interface=_BLUETOOTH_INTERFACE ) Solaar-1.1.14/lib/solaar/gtk.py000077500000000000000000000153241473525213700162260ustar00rootroot00000000000000#!/usr/bin/env python3 ## Copyright (C) 2012-2013 Daniel Pavel ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import argparse import faulthandler import importlib import locale import logging import os.path import platform import signal import sys import tempfile from traceback import format_exc from solaar import NAME from solaar import __version__ from solaar import cli from solaar import configuration from solaar import dbus from solaar import listener from solaar import ui logger = logging.getLogger(__name__) def _require(module, os_package, gi=None, gi_package=None, gi_version=None): try: if gi is not None: gi.require_version(gi_package, gi_version) return importlib.import_module(module) except (ImportError, ValueError): sys.exit(f"{NAME.lower()}: missing required system package {os_package}") battery_icons_style = "regular" tray_icon_size = None temp = tempfile.NamedTemporaryFile(prefix="Solaar_", mode="w", delete=True) def _parse_arguments(): arg_parser = argparse.ArgumentParser( prog=NAME.lower(), epilog="For more information see https://pwr-solaar.github.io/Solaar" ) arg_parser.add_argument( "-d", "--debug", action="count", default=0, help="print logging messages, for debugging purposes (may be repeated for extra verbosity)", ) arg_parser.add_argument( "-D", "--hidraw", action="store", dest="hidraw_path", metavar="PATH", help="unifying receiver to use; the first detected receiver if unspecified. Example: /dev/hidraw2", ) arg_parser.add_argument( "--restart-on-wake-up", action="store_true", help="restart Solaar on sleep wake-up (experimental)", ) arg_parser.add_argument( "-w", "--window", choices=("show", "hide", "only"), help="start with window showing / hidden / only (no tray icon)", ) arg_parser.add_argument( "-b", "--battery-icons", choices=("regular", "symbolic", "solaar"), help="prefer regular battery / symbolic battery / solaar icons", ) arg_parser.add_argument("--tray-icon-size", type=int, help="explicit size for tray icons") arg_parser.add_argument("-V", "--version", action="version", version="%(prog)s " + __version__) arg_parser.add_argument("--help-actions", action="store_true", help="describe the command-line actions") arg_parser.add_argument( "action", nargs=argparse.REMAINDER, choices=cli.actions, help="command-line action to perform (optional); append ' --help' to show args", ) args = arg_parser.parse_args() if args.help_actions: cli.print_help() return if args.window is None: args.window = "show" # default behaviour is to show main window global battery_icons_style battery_icons_style = args.battery_icons if args.battery_icons is not None else "regular" global tray_icon_size tray_icon_size = args.tray_icon_size log_format = "%(asctime)s,%(msecs)03d %(levelname)8s [%(threadName)s] %(name)s: %(message)s" log_level = logging.ERROR - 10 * args.debug logging.getLogger("").setLevel(min(log_level, logging.WARNING)) file_handler = logging.StreamHandler(temp) file_handler.setLevel(max(min(log_level, logging.WARNING), logging.INFO)) file_handler.setFormatter(logging.Formatter(log_format)) logging.getLogger("").addHandler(file_handler) if args.debug > 0: stream_handler = logging.StreamHandler() stream_handler.setFormatter(logging.Formatter(log_format)) stream_handler.setLevel(log_level) logging.getLogger("").addHandler(stream_handler) if not args.action: if logger.isEnabledFor(logging.INFO): language, encoding = locale.getlocale() logger.info("version %s, language %s (%s)", __version__, language, encoding) return args # On first SIGINT, dump threads to stderr; on second, exit def _handlesig(signl, stack): signal.signal(signal.SIGINT, signal.SIG_DFL) signal.signal(signal.SIGTERM, signal.SIG_DFL) if signl == int(signal.SIGINT): if logger.isEnabledFor(logging.INFO): faulthandler.dump_traceback() sys.exit(f"{NAME.lower()}: exit due to keyboard interrupt") else: sys.exit(0) def main(): if platform.system() not in ("Darwin", "Windows"): _require("pyudev", "python3-pyudev") args = _parse_arguments() if not args: return if args.action: # if any argument, run comandline and exit return cli.run(args.action, args.hidraw_path) gi = _require("gi", "python3-gi (in Ubuntu) or python3-gobject (in Fedora)") _require("gi.repository.Gtk", "gir1.2-gtk-3.0", gi, "Gtk", "3.0") # handle ^C in console signal.signal(signal.SIGINT, signal.SIG_DFL) signal.signal(signal.SIGINT, _handlesig) signal.signal(signal.SIGTERM, _handlesig) udev_file = "42-logitech-unify-permissions.rules" if ( logger.isEnabledFor(logging.WARNING) and not os.path.isfile("/etc/udev/rules.d/" + udev_file) and not os.path.isfile("/usr/lib/udev/rules.d/" + udev_file) and not os.path.isfile("/usr/local/lib/udev/rules.d/" + udev_file) ): logger.warning("Solaar udev file not found in expected location") logger.warning("See https://pwr-solaar.github.io/Solaar/installation for more information") try: listener.setup_scanner(ui.status_changed, ui.setting_changed, ui.common.error_dialog) if args.restart_on_wake_up: dbus.watch_suspend_resume(listener.start_all, listener.stop_all) else: dbus.watch_suspend_resume(lambda: listener.ping_all(True)) configuration.defer_saves = True # allow configuration saves to be deferred # main UI event loop ui.run_loop(listener.start_all, listener.stop_all, args.window != "only", args.window != "hide") except Exception: sys.exit(f"{NAME.lower()}: error: {format_exc()}") temp.close() if __name__ == "__main__": main() Solaar-1.1.14/lib/solaar/i18n.py000066400000000000000000000041101473525213700162040ustar00rootroot00000000000000## Copyright (C) 2012-2013 Daniel Pavel ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import gettext import locale import os import sys from glob import glob from solaar import NAME _LOCALE_DOMAIN = NAME.lower() def _find_locale_path(locale_domain: str) -> str: prefix_share = os.path.normpath(os.path.join(os.path.realpath(sys.path[0]), "..")) src_share = os.path.normpath(os.path.join(os.path.realpath(sys.path[0]), "..", "share")) for location in prefix_share, src_share: mo_files = glob(os.path.join(location, "locale", "*", "LC_MESSAGES", locale_domain + ".mo")) if mo_files: return os.path.join(location, "locale") raise FileNotFoundError(f"Could not find locale path for {locale_domain}") def set_locale_to_system_default(): """Sets locale for translations to the system default. Set LC_ALL environment variable to enforce a locale setting e.g. 'de_DE.UTF-8'. Run Solaar with your desired localization, for German use: 'LC_ALL=de_DE.UTF-8 solaar' """ try: locale.setlocale(locale.LC_ALL, "") except PermissionError: pass try: path = _find_locale_path(_LOCALE_DOMAIN) except FileNotFoundError: path = None gettext.bindtextdomain(_LOCALE_DOMAIN, path) gettext.textdomain(_LOCALE_DOMAIN) gettext.install(_LOCALE_DOMAIN) set_locale_to_system_default() _ = gettext.gettext ngettext = gettext.ngettext Solaar-1.1.14/lib/solaar/listener.py000066400000000000000000000373621473525213700172710ustar00rootroot00000000000000## Copyright (C) 2012-2013 Daniel Pavel ## Copyright (C) 2014-2024 Solaar Contributors https://pwr-solaar.github.io/Solaar/ ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import errno import logging import subprocess import time from collections import namedtuple from functools import partial import gi import logitech_receiver from logitech_receiver import base from logitech_receiver import exceptions from logitech_receiver import hidpp10_constants from logitech_receiver import listener from logitech_receiver import notifications from . import configuration from . import dbus from . import i18n gi.require_version("Gtk", "3.0") # NOQA: E402 from gi.repository import GLib # NOQA: E402 # isort:skip logger = logging.getLogger(__name__) ACTION_ADD = "add" _GHOST_DEVICE = namedtuple("_GHOST_DEVICE", ("receiver", "number", "name", "kind", "online")) _GHOST_DEVICE.__bool__ = lambda self: False _GHOST_DEVICE.__nonzero__ = _GHOST_DEVICE.__bool__ def _ghost(device): return _GHOST_DEVICE( receiver=device.receiver, number=device.number, name=device.name, kind=device.kind, online=False, ) class SolaarListener(listener.EventsListener): """Keeps the status of a Receiver or Device (member name is receiver but it can also be a device).""" def __init__(self, receiver, status_changed_callback): assert status_changed_callback super().__init__(receiver, self._notifications_handler) self.status_changed_callback = status_changed_callback receiver.status_callback = self._status_changed def has_started(self): if logger.isEnabledFor(logging.INFO): logger.info("%s: notifications listener has started (%s)", self.receiver, self.receiver.handle) nfs = self.receiver.enable_connection_notifications() if logger.isEnabledFor(logging.WARNING): if not self.receiver.isDevice and not ((nfs if nfs else 0) & hidpp10_constants.NOTIFICATION_FLAG.wireless): logger.warning( "Receiver on %s might not support connection notifications, GUI might not show its devices", self.receiver.path, ) self.receiver.notification_flags = nfs self.receiver.notify_devices() self._status_changed(self.receiver) def has_stopped(self): r, self.receiver = self.receiver, None assert r is not None if logger.isEnabledFor(logging.INFO): logger.info("%s: notifications listener has stopped", r) # because udev is not notifying us about device removal, make sure to clean up in _all_listeners _all_listeners.pop(r.path, None) # this causes problems but what is it doing (pfps) - r.status = _('The receiver was unplugged.') if r: try: r.close() except Exception: logger.exception(f"closing receiver {r.path}") self.status_changed_callback(r) def _status_changed(self, device, alert=None, reason=None): assert device is not None if logger.isEnabledFor(logging.INFO): try: if device.kind is None: logger.info( "status_changed %r: %s (%X) %s", device, "present" if bool(device) else "removed", alert if alert is not None else 0, reason or "", ) else: device.ping() logger.info( "status_changed %r: %s %s (%X) %s", device, "paired" if bool(device) else "unpaired", "online" if device.online else "offline", alert if alert is not None else 0, reason or "", ) except Exception as e: logger.info("status_changed for unknown device: %s", e) if device.kind is None: assert device == self.receiver # the status of the receiver changed self.status_changed_callback(device, alert, reason) return # not true for wired devices - assert device.receiver == self.receiver if not device: # Device was unpaired, and isn't valid anymore. # We replace it with a ghost so that the UI has something to work with while cleaning up. if logger.isEnabledFor(logging.INFO): logger.info("device %s was unpaired, ghosting", device) device = _ghost(device) self.status_changed_callback(device, alert, reason) if not device: # the device was just unpaired, need to update the status of the receiver as well self.status_changed_callback(self.receiver) def _notifications_handler(self, n): assert self.receiver if n.devnumber == 0xFF: # a receiver notification notifications.process(self.receiver, n) return # a notification that came in to the device listener - strange, but nothing needs to be done here if self.receiver.isDevice: if logger.isEnabledFor(logging.DEBUG): logger.debug("Notification %s via device %s being ignored.", n, self.receiver) return # DJ pairing notification - ignore - hid++ 1.0 pairing notification is all that is needed if n.sub_id == 0x41 and n.report_id == base.DJ_MESSAGE_ID: if logger.isEnabledFor(logging.INFO): logger.info("ignoring DJ pairing notification %s", n) return # a device notification if not (0 < n.devnumber <= 16): # some receivers have devices past their max # devices if logger.isEnabledFor(logging.WARNING): logger.warning("Unexpected device number (%s) in notification %s.", n.devnumber, n) return already_known = n.devnumber in self.receiver # FIXME: hacky fix for kernel/hardware race condition # If the device was just turned on or woken up from sleep, it may not be ready to receive commands. # The "payload" bit of the wireless status notification seems to tell us this. If this is the case, we # must wait a short amount of time to avoid causing a broken pipe error. device_ready = not bool(ord(n.data[0:1]) & 0x80) or n.sub_id != 0x41 if not device_ready: time.sleep(0.01) if n.sub_id == 0x40 and not already_known: return # disconnecting something that is not known - nothing to do if n.sub_id == 0x41: if not already_known: if n.address == 0x0A and not self.receiver.receiver_kind == "bolt": # some Nanos send a notification even if no new pairing - check that there really is a device there if ( self.receiver.read_register( hidpp10_constants.Registers.RECEIVER_INFO, hidpp10_constants.INFO_SUBREGISTERS.pairing_information + n.devnumber - 1, ) is None ): return dev = self.receiver.register_new_device(n.devnumber, n) elif self.receiver.pairing.lock_open and self.receiver.re_pairs and not ord(n.data[0:1]) & 0x40: dev = self.receiver[n.devnumber] del self.receiver[n.devnumber] # get rid of information on device re-paired away self._status_changed(dev) # signal that this device has changed dev = self.receiver.register_new_device(n.devnumber, n) self.receiver.pairing.new_device = self.receiver[n.devnumber] else: dev = self.receiver[n.devnumber] else: dev = self.receiver[n.devnumber] if not dev: logger.warning("%s: received %s for invalid device %d: %r", self.receiver, n, n.devnumber, dev) return # Apply settings every time the device connects if n.sub_id == 0x41: if logger.isEnabledFor(logging.INFO): logger.info("connection %s for device wpid %s kind %s serial %s", n, dev.wpid, dev.kind, dev._serial) # If there are saved configs, bring the device's settings up-to-date. # They will be applied when the device is marked as online. configuration.attach_to(dev) dev.status_callback = self._status_changed # the receiver changed status as well self._status_changed(self.receiver) notifications.process(dev, n) if self.receiver.pairing.lock_open and not already_known: # this should be the first notification after a device was paired if logger.isEnabledFor(logging.WARNING): logger.warning("first notification was not a connection notification") if logger.isEnabledFor(logging.INFO): logger.info("%s: pairing detected new device", self.receiver) self.receiver.pairing.new_device = dev elif dev.online is None: dev.ping() def __str__(self): return f"" def _process_bluez_dbus(device, path, dictionary, signature): """Process bluez dbus property changed signals for device status changes to discover disconnections and connections. """ if device: if dictionary.get("Connected") is not None: connected = dictionary.get("Connected") if logger.isEnabledFor(logging.INFO): logger.info("bluez dbus for %s: %s", device, "CONNECTED" if connected else "DISCONNECTED") device.changed(connected, reason=i18n._("connected") if connected else i18n._("disconnected")) elif device is not None: if logger.isEnabledFor(logging.INFO): logger.info("bluez cleanup for %s", device) _cleanup_bluez_dbus(device) def _cleanup_bluez_dbus(device): """Remove dbus signal receiver for device""" if logger.isEnabledFor(logging.INFO): logger.info("bluez cleanup for %s", device) dbus.watch_bluez_connect(device.hid_serial, None) _all_listeners = {} # all known receiver listeners, listeners that stop on their own may remain here def _start(device_info): assert _status_callback and _setting_callback isDevice = device_info.isDevice if not isDevice: receiver_ = logitech_receiver.receiver.create_receiver(base, device_info, _setting_callback) else: receiver_ = logitech_receiver.device.create_device(base, device_info, _setting_callback) if receiver_: configuration.attach_to(receiver_) if receiver_.bluetooth and receiver_.hid_serial: dbus.watch_bluez_connect(receiver_.hid_serial, partial(_process_bluez_dbus, receiver_)) receiver_.cleanups.append(_cleanup_bluez_dbus) if receiver_: rl = SolaarListener(receiver_, _status_callback) rl.start() _all_listeners[device_info.path] = rl return rl logger.warning("failed to open %s", device_info) def start_all(): stop_all() # just in case this it called twice in a row... if logger.isEnabledFor(logging.INFO): logger.info("starting receiver listening threads") for device_info in base.receivers_and_devices(): _process_receiver_event(ACTION_ADD, device_info) def stop_all(): listeners = list(_all_listeners.values()) _all_listeners.clear() if listeners: if logger.isEnabledFor(logging.INFO): logger.info("stopping receiver listening threads %s", listeners) for listener_thread in listeners: listener_thread.stop() configuration.save() if listeners: for listener_thread in listeners: listener_thread.join() # after a resume, the device may have been off so mark its saved status to ensure # that the status is pushed to the device when it comes back def ping_all(resuming=False): if logger.isEnabledFor(logging.INFO): logger.info("ping all devices%s", " when resuming" if resuming else "") for listener_thread in _all_listeners.values(): if listener_thread.receiver.isDevice: if resuming: listener_thread.receiver._active = None # ensure that settings are pushed if listener_thread.receiver.ping(): listener_thread.receiver.changed(active=True, push=True) listener_thread._status_changed(listener_thread.receiver) else: count = listener_thread.receiver.count() if count: for dev in listener_thread.receiver: if resuming: dev._active = None # ensure that settings are pushed if dev.ping(): dev.changed(active=True, push=True) listener_thread._status_changed(dev) count -= 1 if not count: break _status_callback = None # GUI callback to change UI in response to changes to receiver or device status _setting_callback = None # GUI callback to change UI in response to changes to status _error_callback = None # GUI callback to report errors def setup_scanner(status_changed_callback, setting_changed_callback, error_callback): global _status_callback, _error_callback, _setting_callback assert _status_callback is None, "scanner was already set-up" _status_callback = status_changed_callback _setting_callback = setting_changed_callback _error_callback = error_callback base.notify_on_receivers_glib(GLib, _process_receiver_event) def _process_add(device_info, retry): try: _start(device_info) except OSError as e: if e.errno == errno.EACCES: try: output = subprocess.check_output(["/usr/bin/getfacl", "-p", device_info.path], text=True) if logger.isEnabledFor(logging.WARNING): logger.warning("Missing permissions on %s\n%s.", device_info.path, output) except Exception: pass if retry: GLib.timeout_add(2000.0, _process_add, device_info, retry - 1) else: _error_callback("permissions", device_info.path) else: _error_callback("nodevice", device_info.path) except exceptions.NoReceiver: _error_callback("nodevice", device_info.path) # receiver add/remove events will start/stop listener threads def _process_receiver_event(action, device_info): assert action is not None assert device_info is not None assert _error_callback if logger.isEnabledFor(logging.INFO): logger.info("receiver event %s %s", action, device_info) # whatever the action, stop any previous receivers at this path listener_thread = _all_listeners.pop(device_info.path, None) if listener_thread is not None: assert isinstance(listener_thread, SolaarListener) listener_thread.stop() if action == ACTION_ADD: _process_add(device_info, 3) return False Solaar-1.1.14/lib/solaar/tasks.py000066400000000000000000000035121473525213700165570ustar00rootroot00000000000000#!/usr/bin/env python3 ## Copyright (C) 2012-2013 Daniel Pavel ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import logging from threading import Thread logger = logging.getLogger(__name__) try: from Queue import Queue except ImportError: from queue import Queue class TaskRunner(Thread): def __init__(self, name): super().__init__(name=name) self.daemon = True self.queue = Queue(16) self.alive = False def __call__(self, function, *args, **kwargs): task = (function, args, kwargs) self.queue.put(task) def stop(self): self.alive = False self.queue.put(None) def run(self): self.alive = True if logger.isEnabledFor(logging.DEBUG): logger.debug("started") while self.alive: task = self.queue.get() if task: function, args, kwargs = task assert function try: function(*args, **kwargs) except Exception: logger.exception("calling %s", function) if logger.isEnabledFor(logging.DEBUG): logger.debug("stopped") Solaar-1.1.14/lib/solaar/ui/000077500000000000000000000000001473525213700154745ustar00rootroot00000000000000Solaar-1.1.14/lib/solaar/ui/__init__.py000066400000000000000000000107271473525213700176140ustar00rootroot00000000000000## Copyright (C) 2012-2013 Daniel Pavel ## Copyright (C) 2014-2024 Solaar Contributors https://pwr-solaar.github.io/Solaar/ ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import logging from typing import Callable import gi import yaml from logitech_receiver.common import Alert from solaar.i18n import _ from solaar.ui.config_panel import change_setting from solaar.ui.config_panel import record_setting from solaar.ui.window import find_device from . import common from . import desktop_notifications from . import diversion_rules from . import tray from . import window gi.require_version("Gtk", "3.0") from gi.repository import Gio # NOQA: E402 from gi.repository import GLib # NOQA: E402 from gi.repository import Gtk # NOQA: E402 logger = logging.getLogger(__name__) assert Gtk.get_major_version() > 2, "Solaar requires Gtk 3 python bindings" APP_ID = "io.github.pwr_solaar.solaar" def _startup(app, startup_hook, use_tray, show_window): if logger.isEnabledFor(logging.DEBUG): logger.debug("startup registered=%s, remote=%s", app.get_is_registered(), app.get_is_remote()) common.start_async() desktop_notifications.init() if use_tray: tray.init(lambda _ignore: window.destroy()) window.init(show_window, use_tray) startup_hook() def _activate(app): if logger.isEnabledFor(logging.DEBUG): logger.debug("activate") if app.get_windows(): window.popup() else: app.add_window(window._window) def _command_line(app, command_line): args = command_line.get_arguments() args = yaml.safe_load("".join(args)) if args else args if not args: _activate(app) elif args[0] == "config": # config call from remote instance if logger.isEnabledFor(logging.INFO): logger.info("remote command line %s", args) dev = find_device(args[1]) if dev: setting = next((s for s in dev.settings if s.name == args[2]), None) if setting: change_setting(dev, setting, args[3:]) return 0 def _shutdown(_app, shutdown_hook): if logger.isEnabledFor(logging.DEBUG): logger.debug("shutdown") shutdown_hook() common.stop_async() tray.destroy() desktop_notifications.uninit() def run_loop( startup_hook: Callable[[], None], shutdown_hook: Callable[[], None], use_tray: bool, show_window: bool, ): assert use_tray or show_window, "need either tray or visible window" application = Gtk.Application.new(APP_ID, Gio.ApplicationFlags.HANDLES_COMMAND_LINE) application.connect( "startup", lambda app, startup_hook: _startup(app, startup_hook, use_tray, show_window), startup_hook, ) application.connect("command-line", _command_line) application.connect("activate", _activate) application.connect("shutdown", _shutdown, shutdown_hook) application.register() if application.get_is_remote(): print(_("Another Solaar process is already running so just expose its window")) application.run() def _status_changed(device, alert, reason, refresh=False): assert device is not None if logger.isEnabledFor(logging.DEBUG): logger.debug("status changed: %s (%s) %s", device, alert, reason) if alert is None: alert = Alert.NONE tray.update(device) if alert & Alert.ATTENTION: tray.attention(reason) need_popup = alert & Alert.SHOW_WINDOW window.update(device, need_popup, refresh) diversion_rules.update_devices() if alert & (Alert.NOTIFICATION | Alert.ATTENTION): desktop_notifications.show(device, reason) def status_changed(device, alert=Alert.NONE, reason=None, refresh=False): GLib.idle_add(_status_changed, device, alert, reason, refresh) def setting_changed(device, setting_class, vals): record_setting(device, setting_class, vals) Solaar-1.1.14/lib/solaar/ui/about/000077500000000000000000000000001473525213700166065ustar00rootroot00000000000000Solaar-1.1.14/lib/solaar/ui/about/__init__.py000066400000000000000000000000001473525213700207050ustar00rootroot00000000000000Solaar-1.1.14/lib/solaar/ui/about/about.py000066400000000000000000000023071473525213700202740ustar00rootroot00000000000000## Copyright (C) Solaar Contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. from solaar.ui.about.model import AboutModel from solaar.ui.about.presenter import Presenter from solaar.ui.about.view import AboutView def show(_=None, model=None, view=None): """Opens the About dialog.""" if model is None: model = AboutModel() if view is None: view = AboutView() presenter = Presenter(model, view) presenter.run() if __name__ == "__main__": from gi.repository import Gtk show(None) Gtk.main() Solaar-1.1.14/lib/solaar/ui/about/model.py000066400000000000000000000056141473525213700202660ustar00rootroot00000000000000## Copyright (C) Solaar Contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. from __future__ import annotations from datetime import datetime from typing import List from typing import Tuple from solaar import __version__ from solaar.i18n import _ def _get_current_year() -> int: return datetime.now().year class AboutModel: def get_version(self) -> str: return __version__ def get_description(self) -> str: return _("Manages Logitech receivers,\nkeyboards, mice, and tablets.") def get_copyright(self) -> str: return f"© 2012-{_get_current_year()} Daniel Pavel and contributors to the Solaar project" def get_authors(self) -> List[str]: return [ "Daniel Pavel http://github.com/pwr", ] def get_translators(self) -> List[str]: return [ "gogo (croatian)", "Papoteur, David Geiger, Damien Lallement (français)", "Michele Olivo (italiano)", "Adrian Piotrowicz (polski)", "Drovetto, JrBenito (Portuguese-BR)", "Daniel Pavel (română)", "Daniel Zippert, Emelie Snecker (svensk)", "Dimitriy Ryazantcev (Russian)", "El Jinete Sin Cabeza (Español)", "Ferdina Kusumah (Indonesia)", "John Erling Blad (Norwegian Bokmål, Norwegian Nynorsk)", ] def get_credit_sections(self) -> List[Tuple[str, List[str]]]: return [ (_("Additional Programming"), ["Filipe Laíns", "Peter F. Patel-Schneider"]), (_("GUI design"), ["Julien Gascard", "Daniel Pavel"]), ( _("Testing"), [ "Douglas Wagner", "Julien Gascard", "Peter Wu http://www.lekensteyn.nl/logitech-unifying.html", ], ), ( _("Logitech documentation"), [ "Julien Danjou http://julien.danjou.info/blog/2012/logitech-unifying-upower", "Nestor Lopez Casado http://drive.google.com/folderview?id=0BxbRzx7vEV7eWmgwazJ3NUFfQ28", ], ), ] def get_website(self): return "https://pwr-solaar.github.io/Solaar" Solaar-1.1.14/lib/solaar/ui/about/presenter.py000066400000000000000000000055521473525213700211760ustar00rootroot00000000000000## Copyright (C) Solaar Contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. from __future__ import annotations from typing_extensions import Protocol from solaar.ui.about.model import AboutModel class AboutViewProtocol(Protocol): def init_ui(self) -> None: ... def update_version_info(self, version: str) -> None: ... def update_description(self, comments: str) -> None: ... def update_copyright(self, copyright_text: str) -> None: ... def update_authors(self, authors: list[str]) -> None: ... def update_translators(self, translators: list[str]) -> None: ... def update_website(self, website): ... def update_credits(self, credit_sections: list[tuple[str, list[str]]]) -> None: ... def show(self) -> None: ... class Presenter: def __init__(self, model: AboutModel, view: AboutViewProtocol) -> None: self.model = model self.view = view def update_version_info(self) -> None: version = self.model.get_version() self.view.update_version_info(version) def update_credits(self) -> None: credit_sections = self.model.get_credit_sections() self.view.update_credits(credit_sections) def update_description(self) -> None: comments = self.model.get_description() self.view.update_description(comments) def update_copyright(self) -> None: copyright_text = self.model.get_copyright() self.view.update_copyright(copyright_text) def update_authors(self) -> None: authors = self.model.get_authors() self.view.update_authors(authors) def update_translators(self) -> None: translators = self.model.get_translators() self.view.update_translators(translators) def update_website(self) -> None: website = self.model.get_website() self.view.update_website(website) def run(self) -> None: self.view.init_ui() self.update_version_info() self.update_description() self.update_website() self.update_copyright() self.update_authors() self.update_credits() self.update_translators() self.view.show() Solaar-1.1.14/lib/solaar/ui/about/view.py000066400000000000000000000043761473525213700201440ustar00rootroot00000000000000## Copyright (C) Solaar Contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. from typing import List from typing import Tuple from typing import Union from gi.repository import Gtk from solaar import NAME class AboutView: def __init__(self) -> None: self.view: Union[Gtk.AboutDialog, None] = None def init_ui(self) -> None: self.view = Gtk.AboutDialog() self.view.set_program_name(NAME) self.view.set_icon_name(NAME.lower()) self.view.set_license_type(Gtk.License.GPL_2_0) self.view.connect("response", lambda x, y: self.handle_close(x)) def update_version_info(self, version: str) -> None: self.view.set_version(version) def update_description(self, comments: str) -> None: self.view.set_comments(comments) def update_copyright(self, copyright_text: str): self.view.set_copyright(copyright_text) def update_authors(self, authors: List[str]) -> None: self.view.set_authors(authors) def update_credits(self, credit_sections: List[Tuple[str, List[str]]]) -> None: for section_name, people in credit_sections: self.view.add_credit_section(section_name, people) def update_translators(self, translators: List[str]) -> None: translator_credits = "\n".join(translators) self.view.set_translator_credits(translator_credits) def update_website(self, website): self.view.set_website_label(NAME) self.view.set_website(website) def show(self) -> None: self.view.present() def handle_close(self, event) -> None: event.hide() Solaar-1.1.14/lib/solaar/ui/action.py000066400000000000000000000063531473525213700173320ustar00rootroot00000000000000## Copyright (C) 2012-2013 Daniel Pavel ## Copyright (C) 2014-2024 Solaar Contributors https://pwr-solaar.github.io/Solaar/ ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. from gi.repository import Gdk from gi.repository import Gtk from solaar.i18n import _ from . import pair_window from .common import error_dialog def make_image_menu_item(label, icon_name, function, *args): box = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 6) label = Gtk.Label(label=label) icon = Gtk.Image.new_from_icon_name(icon_name, Gtk.IconSize.LARGE_TOOLBAR) if icon_name is not None else Gtk.Image() box.add(icon) box.add(label) menu_item = Gtk.MenuItem() menu_item.add(box) menu_item.show_all() menu_item.connect("activate", function, *args) menu_item.label = label menu_item.icon = icon return menu_item def make(name, label, function, stock_id=None, *args): action = Gtk.Action(name=name, label=label, tooltip=label, stock_id=None) action.set_icon_name(name) if stock_id is not None: action.set_stock_id(stock_id) if function: action.connect("activate", function, *args) return action def make_toggle(name, label, function, stock_id=None, *args): action = Gtk.ToggleAction(name=name, label=label, tooltip=label, stock_id=None) action.set_icon_name(name) if stock_id is not None: action.set_stock_id(stock_id) action.connect("activate", function, *args) return action def pair(window, receiver): assert receiver assert receiver.kind is None pair_dialog = pair_window.create(receiver) pair_dialog.set_transient_for(window) pair_dialog.set_destroy_with_parent(True) pair_dialog.set_modal(True) pair_dialog.set_type_hint(Gdk.WindowTypeHint.DIALOG) pair_dialog.set_position(Gtk.WindowPosition.CENTER) pair_dialog.present() def unpair(window, device): assert device assert device.kind is not None qdialog = Gtk.MessageDialog( transient_for=window, flags=0, message_type=Gtk.MessageType.QUESTION, buttons=Gtk.ButtonsType.NONE, text=_("Unpair") + " " + device.name + " ?", ) qdialog.set_icon_name("remove") qdialog.add_button(_("Cancel"), Gtk.ResponseType.CANCEL) qdialog.add_button(_("Unpair"), Gtk.ResponseType.ACCEPT) choice = qdialog.run() qdialog.destroy() if choice == Gtk.ResponseType.ACCEPT: receiver = device.receiver assert receiver device_number = device.number try: del receiver[device_number] except Exception: error_dialog("unpair", device) Solaar-1.1.14/lib/solaar/ui/common.py000066400000000000000000000057501473525213700173450ustar00rootroot00000000000000## Copyright (C) 2012-2013 Daniel Pavel ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import logging from typing import Tuple import gi from solaar.i18n import _ from solaar.tasks import TaskRunner gi.require_version("Gtk", "3.0") from gi.repository import GLib # NOQA: E402 from gi.repository import Gtk # NOQA: E402 logger = logging.getLogger(__name__) def _create_error_text(reason: str, object_) -> Tuple[str, str]: if reason == "permissions": title = _("Permissions error") text = ( _("Found a Logitech receiver or device (%s), but did not have permission to open it.") % object_ + "\n\n" + _("If you've just installed Solaar, try disconnecting the receiver or device and then reconnecting it.") ) elif reason == "nodevice": title = _("Cannot connect to device error") text = ( _("Found a Logitech receiver or device at %s, but encountered an error connecting to it.") % object_ + "\n\n" + _("Try disconnecting the device and then reconnecting it or turning it off and then on.") ) elif reason == "unpair": title = _("Unpairing failed") text = ( _("Failed to unpair %{device} from %{receiver}.").format( device=object_.name, receiver=object_.receiver.name, ) + "\n\n" + _("The receiver returned an error, with no further details.") ) else: raise Exception("ui.error_dialog: don't know how to handle (%s, %s)", reason, object_) return title, text def _error_dialog(reason: str, object_): logger.error("error: %s %s", reason, object_) title, text = _create_error_text(reason, object_) m = Gtk.MessageDialog(None, Gtk.DialogFlags.MODAL, Gtk.MessageType.ERROR, Gtk.ButtonsType.CLOSE, text) m.set_title(title) m.run() m.destroy() def error_dialog(reason, object_): assert reason is not None GLib.idle_add(_error_dialog, reason, object_) _task_runner = None def start_async(): global _task_runner _task_runner = TaskRunner("AsyncUI") _task_runner.start() def stop_async(): global _task_runner _task_runner.stop() _task_runner = None def ui_async(function, *args, **kwargs): if _task_runner: _task_runner(function, *args, **kwargs) Solaar-1.1.14/lib/solaar/ui/config_panel.py000066400000000000000000000766011473525213700205040ustar00rootroot00000000000000## Copyright (C) 2012-2013 Daniel Pavel ## Copyright (C) 2014-2024 Solaar Contributors https://pwr-solaar.github.io/Solaar/ ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import logging import traceback from threading import Timer import gi from logitech_receiver import hidpp20 from logitech_receiver import settings from solaar.i18n import _ from solaar.i18n import ngettext from .common import ui_async gi.require_version("Gtk", "3.0") from gi.repository import Gdk # NOQA: E402 from gi.repository import GLib # NOQA: E402 from gi.repository import Gtk # NOQA: E402 logger = logging.getLogger(__name__) def _read_async(setting, force_read, sbox, device_is_online, sensitive): def _do_read(s, force, sb, online, sensitive): try: v = s.read(not force) except Exception as e: v = None if logger.isEnabledFor(logging.WARNING): logger.warning("%s: error reading so use None (%s): %s", s.name, s._device, repr(e)) GLib.idle_add(_update_setting_item, sb, v, online, sensitive, True, priority=99) ui_async(_do_read, setting, force_read, sbox, device_is_online, sensitive) def _write_async(setting, value, sbox, sensitive=True, key=None): def _do_write(_s, v, sb, key): try: if key is None: v = setting.write(v) else: v = setting.write_key_value(key, v) v = {key: v} except Exception: traceback.print_exc() v = None if sb: GLib.idle_add(_update_setting_item, sb, v, True, sensitive, priority=99) if sbox: sbox._control.set_sensitive(False) sbox._failed.set_visible(False) sbox._spinner.set_visible(True) sbox._spinner.start() ui_async(_do_write, setting, value, sbox, key) class ComboBoxText(Gtk.ComboBoxText): def get_value(self): return int(self.get_active_id()) def set_value(self, value): return self.set_active_id(str(int(value))) class Scale(Gtk.Scale): def get_value(self): return int(super().get_value()) class Control: def __init__(self, **kwargs): self.sbox = None self.delegate = None def init(self, sbox, delegate): self.sbox = sbox self.delegate = delegate if delegate else self def changed(self, *args): if self.get_sensitive(): self.delegate.update() def update(self): _write_async(self.sbox.setting, self.get_value(), self.sbox) def layout(self, sbox, label, change, spinner, failed): sbox.pack_start(label, False, False, 0) sbox.pack_end(change, False, False, 0) fill = sbox.setting.kind == settings.KIND.range or sbox.setting.kind == settings.KIND.hetero sbox.pack_end(self, fill, fill, 0) sbox.pack_end(spinner, False, False, 0) sbox.pack_end(failed, False, False, 0) return self class ToggleControl(Gtk.Switch, Control): def __init__(self, sbox, delegate=None): super().__init__(halign=Gtk.Align.CENTER, valign=Gtk.Align.CENTER) self.init(sbox, delegate) self.connect("notify::active", self.changed) def set_value(self, value): if value is not None: self.set_state(value) def get_value(self): return self.get_state() class SliderControl(Gtk.Scale, Control): def __init__(self, sbox, delegate=None): super().__init__(halign=Gtk.Align.FILL) self.init(sbox, delegate) self.timer = None self.set_range(*self.sbox.setting.range) self.set_round_digits(0) self.set_digits(0) self.set_increments(1, 5) self.connect("value-changed", self.changed) def get_value(self): return int(super().get_value()) def changed(self, *args): if self.get_sensitive(): if self.timer: self.timer.cancel() self.timer = Timer(0.5, lambda: GLib.idle_add(self.do_change)) self.timer.start() def do_change(self): self.timer.cancel() self.update() def _create_choice_control(sbox, delegate=None, choices=None): if 50 > len(choices if choices else sbox.setting.choices): return ChoiceControlLittle(sbox, choices=choices, delegate=delegate) else: return ChoiceControlBig(sbox, choices=choices, delegate=delegate) # GTK boxes have property lists, but the keys must be strings class ChoiceControlLittle(Gtk.ComboBoxText, Control): def __init__(self, sbox, delegate=None, choices=None): super().__init__(halign=Gtk.Align.FILL) self.init(sbox, delegate) self.choices = choices if choices is not None else sbox.setting.choices for entry in self.choices: self.append(str(int(entry)), str(entry)) self.connect("changed", self.changed) def get_value(self): return int(self.get_active_id()) if self.get_active_id() is not None else None def set_value(self, value): if value is not None: self.set_active_id(str(int(value))) def get_choice(self): id = self.get_value() return next((x for x in self.choices if x == id), None) def set_choices(self, choices): self.remove_all() for choice in choices: self.append(str(int(choice)), _(str(choice))) class ChoiceControlBig(Gtk.Entry, Control): def __init__(self, sbox, delegate=None, choices=None): super().__init__(halign=Gtk.Align.FILL) self.init(sbox, delegate) self.choices = choices if choices is not None else sbox.setting.choices self.value = None self.set_width_chars(max([len(str(x)) for x in self.choices]) + 5) liststore = Gtk.ListStore(int, str) for v in self.choices: liststore.append((int(v), str(v))) completion = Gtk.EntryCompletion() completion.set_model(liststore) def norm(s): return s.replace("_", "").replace(" ", "").lower() completion.set_match_func(lambda completion, key, it: norm(key) in norm(completion.get_model()[it][1])) completion.set_text_column(1) self.set_completion(completion) self.connect("changed", self.changed) self.connect("activate", self.activate) completion.connect("match_selected", self.select) def get_value(self): choice = self.get_choice() return int(choice) if choice is not None else None def set_value(self, value): if value is not None: self.set_text(str(next((x for x in self.choices if x == value), None))) def get_choice(self): key = self.get_text() return next((x for x in self.choices if x == key), None) def changed(self, *args): self.value = self.get_choice() icon = "dialog-warning" if self.value is None else "dialog-question" if self.get_sensitive() else "" self.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, icon) tooltip = _("Incomplete") if self.value is None else _("Complete - ENTER to change") self.set_icon_tooltip_text(Gtk.EntryIconPosition.SECONDARY, tooltip) def activate(self, *_args): if self.value is not None and self.get_sensitive(): self.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, "") self.delegate.update() def select(self, _completion, model, iter): self.set_value(model.get(iter, 0)[0]) if self.value and self.get_sensitive(): self.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, "") self.delegate.update() class MapChoiceControl(Gtk.HBox, Control): def __init__(self, sbox, delegate=None): super().__init__(homogeneous=False, spacing=6) self.init(sbox, delegate) self.keyBox = Gtk.ComboBoxText() for entry in sbox.setting.choices: self.keyBox.append(str(int(entry)), _(str(entry))) self.keyBox.set_active(0) key_choice = int(self.keyBox.get_active_id()) self.value_choices = self.sbox.setting.choices[key_choice] self.valueBox = _create_choice_control(sbox.setting, choices=self.value_choices, delegate=self) self.pack_start(self.keyBox, False, False, 0) self.pack_end(self.valueBox, False, False, 0) self.keyBox.connect("changed", self.map_value_notify_key) def get_value(self): key_choice = int(self.keyBox.get_active_id()) if key_choice is not None and self.valueBox.get_value() is not None: return self.valueBox.get_value() def set_value(self, value): if value is None: return self.valueBox.set_sensitive(self.get_sensitive()) key = int(self.keyBox.get_active_id()) if value.get(key) is not None: self.valueBox.set_value(value.get(key)) self.valueBox.set_sensitive(True) def map_populate_value_box(self, key_choice): choices = self.sbox.setting.choices[key_choice] if choices != self.value_choices: self.value_choices = choices self.valueBox.remove_all() self.valueBox.set_choices(choices) current = self.sbox.setting._value.get(key_choice) if self.sbox.setting._value else None if current is not None: self.valueBox.set_value(current) def map_value_notify_key(self, *_args): key_choice = int(self.keyBox.get_active_id()) if self.keyBox.get_sensitive(): self.map_populate_value_box(key_choice) def update(self): key_choice = int(self.keyBox.get_active_id()) value = self.get_value() if value is not None and self.valueBox.get_sensitive() and self.sbox.setting._value.get(key_choice) != value: self.sbox.setting._value[int(key_choice)] = value _write_async(self.sbox.setting, value, self.sbox, key=int(key_choice)) class MultipleControl(Gtk.ListBox, Control): def __init__(self, sbox, change, button_label="...", delegate=None): super().__init__() self.init(sbox, delegate) self.set_selection_mode(Gtk.SelectionMode.NONE) self.set_no_show_all(True) self._showing = True self.setup(sbox.setting) # set up the data and boxes for the sub-controls btn = Gtk.Button(label=button_label) btn.connect("clicked", self.toggle_display) self._button = btn hbox = Gtk.HBox(homogeneous=False, spacing=6) hbox.pack_end(change, False, False, 0) hbox.pack_end(btn, False, False, 0) self._header = hbox vbox = Gtk.VBox(homogeneous=False, spacing=6) vbox.pack_start(hbox, True, True, 0) vbox.pack_end(self, True, True, 0) self.vbox = vbox self.toggle_display() _disable_listbox_highlight_bg(self) def layout(self, sbox, label, change, spinner, failed): self._header.pack_start(label, False, False, 0) self._header.pack_end(spinner, False, False, 0) self._header.pack_end(failed, False, False, 0) sbox.pack_start(self.vbox, True, True, 0) sbox._button = self._button return True def toggle_display(self, *_args): self._showing = not self._showing if not self._showing: for c in self.get_children(): c.hide() self.hide() else: self.show() for c in self.get_children(): c.show_all() class MultipleToggleControl(MultipleControl): def setup(self, setting): self._label_control_pairs = [] for k in setting._validator.get_options(): h = Gtk.HBox(homogeneous=False, spacing=0) lbl_text = str(k) lbl_tooltip = None if hasattr(setting, "_labels"): l1, l2 = setting._labels.get(k, (None, None)) lbl_text = l1 if l1 else lbl_text lbl_tooltip = l2 if l2 else lbl_tooltip lbl = Gtk.Label(label=lbl_text) h.set_tooltip_text(lbl_tooltip or " ") control = Gtk.Switch() control._setting_key = int(k) control.connect("notify::active", self.toggle_notify) h.pack_start(lbl, False, False, 0) h.pack_end(control, False, False, 0) lbl.set_margin_start(30) self.add(h) self._label_control_pairs.append((lbl, control)) def toggle_notify(self, switch, _active): if switch.get_sensitive(): key = switch._setting_key new_state = switch.get_state() if self.sbox.setting._value[key] != new_state: self.sbox.setting._value[key] = new_state _write_async(self.sbox.setting, new_state, self.sbox, key=int(key)) def set_value(self, value): if value is None: return active = 0 total = len(self._label_control_pairs) to_join = [] for lbl, elem in self._label_control_pairs: v = value.get(elem._setting_key, None) if v is not None: elem.set_state(v) if elem.get_state(): active += 1 to_join.append(lbl.get_text() + ": " + str(elem.get_state())) b = ", ".join(to_join) self._button.set_label(f"{active} / {total}") self._button.set_tooltip_text(b) class MultipleRangeControl(MultipleControl): def setup(self, setting): self._items = [] for item in setting._validator.items: lbl_text = str(item) lbl_tooltip = None if hasattr(setting, "_labels"): l1, l2 = setting._labels.get(int(item), (None, None)) lbl_text = l1 if l1 else lbl_text lbl_tooltip = l2 if l2 else lbl_tooltip item_lbl = Gtk.Label(label=lbl_text) self.add(item_lbl) self.set_tooltip_text(lbl_tooltip or " ") item_lb = Gtk.ListBox() item_lb.set_selection_mode(Gtk.SelectionMode.NONE) item_lb._sub_items = [] for sub_item in setting._validator.sub_items[item]: h = Gtk.HBox(homogeneous=False, spacing=20) lbl_text = str(sub_item) lbl_tooltip = None if hasattr(setting, "_labels_sub"): l1, l2 = setting._labels_sub.get(str(sub_item), (None, None)) lbl_text = l1 if l1 else lbl_text lbl_tooltip = l2 if l2 else lbl_tooltip sub_item_lbl = Gtk.Label(label=lbl_text) h.set_tooltip_text(lbl_tooltip or " ") h.pack_start(sub_item_lbl, False, False, 0) sub_item_lbl.set_margin_start(30) if sub_item.widget == "Scale": control = Gtk.Scale.new_with_range( Gtk.Orientation.HORIZONTAL, sub_item.minimum, sub_item.maximum, 1, ) control.set_round_digits(0) control.set_digits(0) h.pack_end(control, True, True, 0) elif sub_item.widget == "SpinButton": control = Gtk.SpinButton.new_with_range(sub_item.minimum, sub_item.maximum, 1) control.set_digits(0) h.pack_end(control, False, False, 0) else: raise NotImplementedError control.connect("value-changed", self.changed, item, sub_item) item_lb.add(h) h._setting_sub_item = sub_item h._label, h._control = sub_item_lbl, control item_lb._sub_items.append(h) item_lb._setting_item = item _disable_listbox_highlight_bg(item_lb) self.add(item_lb) self._items.append(item_lb) def changed(self, control, item, sub_item): if control.get_sensitive(): if hasattr(control, "_timer"): control._timer.cancel() control._timer = Timer(0.5, lambda: GLib.idle_add(self._write, control, item, sub_item)) control._timer.start() def _write(self, control, item, sub_item): control._timer.cancel() delattr(control, "_timer") new_state = int(control.get_value()) if self.sbox.setting._value[int(item)][str(sub_item)] != new_state: self.sbox.setting._value[int(item)][str(sub_item)] = new_state _write_async(self.sbox.setting, self.sbox.setting._value[int(item)], self.sbox, key=int(item)) def set_value(self, value): if value is None: return b = "" n = 0 for ch in self._items: item = ch._setting_item v = value.get(int(item), None) if v is not None: b += str(item) + ": (" to_join = [] for c in ch._sub_items: sub_item = c._setting_sub_item try: sub_item_value = v[str(sub_item)] except KeyError: sub_item_value = c._control.get_value() c._control.set_value(sub_item_value) n += 1 to_join.append(str(sub_item) + f"={sub_item_value}") b += ", ".join(to_join) + ") " lbl_text = ngettext("%d value", "%d values", n) % n self._button.set_label(lbl_text) self._button.set_tooltip_text(b) class PackedRangeControl(MultipleRangeControl): def setup(self, setting): self._items = [] validator = setting._validator for item in range(validator.count): h = Gtk.HBox(homogeneous=False, spacing=0) lbl = Gtk.Label(label=str(validator.keys[item])) control = Gtk.Scale.new_with_range(Gtk.Orientation.HORIZONTAL, validator.min_value, validator.max_value, 1) control.set_round_digits(0) control.set_digits(0) control.connect("value-changed", self.changed, validator.keys[item]) h.pack_start(lbl, False, False, 0) h.pack_end(control, True, True, 0) h._setting_item = validator.keys[item] h.control = control lbl.set_margin_start(30) self.add(h) self._items.append(h) def changed(self, control, item): if control.get_sensitive(): if hasattr(control, "_timer"): control._timer.cancel() control._timer = Timer(0.5, lambda: GLib.idle_add(self._write, control, item)) control._timer.start() def _write(self, control, item): control._timer.cancel() delattr(control, "_timer") new_state = int(control.get_value()) if self.sbox.setting._value[int(item)] != new_state: self.sbox.setting._value[int(item)] = new_state _write_async(self.sbox.setting, self.sbox.setting._value[int(item)], self.sbox, key=int(item)) def set_value(self, value): if value is None: return b = "" n = len(self._items) for h in self._items: item = h._setting_item v = value.get(int(item), None) if v is not None: h.control.set_value(v) else: v = self.sbox.setting._value[int(item)] b += str(item) + ": (" + str(v) + ") " lbl_text = ngettext("%d value", "%d values", n) % n self._button.set_label(lbl_text) self._button.set_tooltip_text(b) # control with an ID key that determines what else to show class HeteroKeyControl(Gtk.HBox, Control): def __init__(self, sbox, delegate=None): super().__init__(homogeneous=False, spacing=6) self.init(sbox, delegate) self._items = {} for item in sbox.setting.possible_fields: if item["label"]: item_lblbox = Gtk.Label(label=item["label"]) self.pack_start(item_lblbox, False, False, 0) item_lblbox.set_visible(False) else: item_lblbox = None item_box = ComboBoxText() if item["kind"] == settings.KIND.choice: for entry in item["choices"]: item_box.append(str(int(entry)), str(entry)) item_box.set_active(0) item_box.connect("changed", self.changed) self.pack_start(item_box, False, False, 0) elif item["kind"] == settings.KIND.range: item_box = Scale() item_box.set_range(item["min"], item["max"]) item_box.set_round_digits(0) item_box.set_digits(0) item_box.set_increments(1, 5) item_box.connect("value-changed", self.changed) self.pack_start(item_box, True, True, 0) item_box.set_visible(False) self._items[str(item["name"])] = (item_lblbox, item_box) def get_value(self): result = {} for k, (_lblbox, box) in self._items.items(): result[str(k)] = box.get_value() result = hidpp20.LEDEffectSetting(**result) return result def set_value(self, value): self.set_sensitive(False) if value is not None: for k, v in value.__dict__.items(): if k in self._items: (lblbox, box) = self._items[k] box.set_value(v) else: self.sbox._failed.set_visible(True) self.setup_visibles(value.ID if value is not None else 0) def setup_visibles(self, id_): fields = self.sbox.setting.fields_map[id_][1] if id_ in self.sbox.setting.fields_map else {} for name, (lblbox, box) in self._items.items(): visible = name in fields or name == "ID" if lblbox: lblbox.set_visible(visible) box.set_visible(visible) def changed(self, control): if self.get_sensitive() and control.get_sensitive(): if "ID" in self._items and control == self._items["ID"][1]: self.setup_visibles(int(self._items["ID"][1].get_value())) if hasattr(control, "_timer"): control._timer.cancel() control._timer = Timer(0.3, lambda: GLib.idle_add(self._write, control)) control._timer.start() def _write(self, control): control._timer.cancel() delattr(control, "_timer") new_state = self.get_value() if self.sbox.setting._value != new_state: _write_async(self.sbox.setting, new_state, self.sbox) _allowables_icons = {True: "changes-allow", False: "changes-prevent", settings.SENSITIVITY_IGNORE: "dialog-error"} _allowables_tooltips = { True: _("Changes allowed"), False: _("No changes allowed"), settings.SENSITIVITY_IGNORE: _("Ignore this setting"), } _next_allowable = {True: False, False: settings.SENSITIVITY_IGNORE, settings.SENSITIVITY_IGNORE: True} _icons_allowables = {v: k for k, v in _allowables_icons.items()} # clicking on the lock icon changes from changeable to unchangeable to ignore def _change_click(button, sbox): icon = button.get_children()[0] icon_name, _ = icon.get_icon_name() allowed = _icons_allowables.get(icon_name, True) new_allowed = _next_allowable[allowed] sbox._control.set_sensitive(new_allowed is True) _change_icon(new_allowed, icon) if sbox.setting._device.persister: # remember the new setting sensitivity sbox.setting._device.persister.set_sensitivity(sbox.setting.name, new_allowed) if allowed == settings.SENSITIVITY_IGNORE: # update setting if it was being ignored setting = next((s for s in sbox.setting._device.settings if s.name == sbox.setting.name), None) if setting: persisted = sbox.setting._device.persister.get(setting.name) if sbox.setting._device.persister else None if setting.persist and persisted is not None: _write_async(setting, persisted, sbox) else: _read_async(setting, True, sbox, bool(sbox.setting._device.online), sbox._control.get_sensitive()) return True def _change_icon(allowed, icon): if allowed in _allowables_icons: icon._allowed = allowed icon.set_from_icon_name(_allowables_icons[allowed], Gtk.IconSize.LARGE_TOOLBAR) icon.set_tooltip_text(_allowables_tooltips[allowed]) def _create_sbox(s, _device): sbox = Gtk.HBox(homogeneous=False, spacing=6) sbox.setting = s sbox.kind = s.kind if s.description: sbox.set_tooltip_text(s.description) lbl = Gtk.Label(label=s.label) label = Gtk.EventBox() label.add(lbl) spinner = Gtk.Spinner() spinner.set_tooltip_text(_("Working") + "...") sbox._spinner = spinner failed = Gtk.Image.new_from_icon_name("dialog-warning", Gtk.IconSize.SMALL_TOOLBAR) failed.set_tooltip_text(_("Read/write operation failed.")) sbox._failed = failed change_icon = Gtk.Image.new_from_icon_name("changes-prevent", Gtk.IconSize.LARGE_TOOLBAR) sbox._change_icon = change_icon _change_icon(False, change_icon) change = Gtk.Button() change.set_relief(Gtk.ReliefStyle.NONE) change.add(change_icon) change.set_sensitive(True) change.connect("clicked", _change_click, sbox) if s.kind == settings.KIND.toggle: control = ToggleControl(sbox) elif s.kind == settings.KIND.range: control = SliderControl(sbox) elif s.kind == settings.KIND.choice: control = _create_choice_control(sbox) elif s.kind == settings.KIND.map_choice: control = MapChoiceControl(sbox) elif s.kind == settings.KIND.multiple_toggle: control = MultipleToggleControl(sbox, change) elif s.kind == settings.KIND.multiple_range: control = MultipleRangeControl(sbox, change) elif s.kind == settings.KIND.packed_range: control = PackedRangeControl(sbox, change) elif s.kind == settings.KIND.hetero: control = HeteroKeyControl(sbox, change) else: if logger.isEnabledFor(logging.WARNING): logger.warning("setting %s display not implemented", s.label) return None control.set_sensitive(False) # the first read will enable it control.layout(sbox, label, change, spinner, failed) sbox._control = control sbox.show_all() spinner.start() # the first read will stop it failed.set_visible(False) return sbox def _update_setting_item(sbox, value, is_online=True, sensitive=True, null_okay=False): sbox._spinner.stop() sensitive = sbox._change_icon._allowed if sensitive is None else sensitive if value is None and not null_okay: sbox._control.set_sensitive(sensitive is True) _change_icon(sensitive, sbox._change_icon) sbox._failed.set_visible(is_online) return sbox._failed.set_visible(False) sbox._control.set_sensitive(False) sbox._control.set_value(value) sbox._control.set_sensitive(sensitive is True) _change_icon(sensitive, sbox._change_icon) def _disable_listbox_highlight_bg(lb): colour = Gdk.RGBA() colour.parse("rgba(0,0,0,0)") for child in lb.get_children(): child.override_background_color(Gtk.StateFlags.PRELIGHT, colour) # config panel _box = None _items = {} def create(): global _box assert _box is None _box = Gtk.VBox(homogeneous=False, spacing=4) _box._last_device = None config_scroll = Gtk.ScrolledWindow() config_scroll.add(_box) config_scroll.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) config_scroll.set_shadow_type(Gtk.ShadowType.NONE) # was IN config_scroll.set_size_request(0, 350) # ask for enough vertical space for about eight settings return config_scroll def update(device, is_online=None): assert _box is not None assert device device_id = (device.receiver.path if device.receiver else device.path, device.number) if is_online is None: is_online = bool(device.online) # if the device changed since last update, clear the box first if device_id != _box._last_device: _box.set_visible(False) _box._last_device = device_id # hide controls belonging to other devices for k, sbox in _items.items(): sbox = _items[k] sbox.set_visible(k[0:2] == device_id) for s in device.settings: k = (device_id[0], device_id[1], s.name) if k in _items: sbox = _items[k] else: sbox = _create_sbox(s, device) if sbox is None: continue _items[k] = sbox _box.pack_start(sbox, False, False, 0) sensitive = device.persister.get_sensitivity(s.name) if device.persister else True _read_async(s, False, sbox, is_online, sensitive) _box.set_visible(True) def clean(device): """Remove the controls for a given device serial. Needed after the device has been unpaired. """ assert _box is not None device_id = (device.receiver.path if device.receiver else device.path, device.number) for k in list(_items.keys()): if k[0:2] == device_id: _box.remove(_items[k]) del _items[k] def destroy(): global _box _box = None _items.clear() def change_setting(device, setting, values): """External interface to change a setting and have the GUI show the change""" assert device == setting._device GLib.idle_add(_change_setting, device, setting, values, priority=99) def _change_setting(device, setting, values): device_path = device.receiver.path if device.receiver else device.path if (device_path, device.number, setting.name) in _items: sbox = _items[(device_path, device.number, setting.name)] else: sbox = None _write_async(setting, values[-1], sbox, None, key=values[0] if len(values) > 1 else None) def record_setting(device, setting, values): """External interface to have the GUI show a change to a setting. Doesn't write to the device""" GLib.idle_add(_record_setting, device, setting, values, priority=99) def _record_setting(device, setting_class, values): if logger.isEnabledFor(logging.DEBUG): logger.debug("on %s changing setting %s to %s", device, setting_class.name, values) setting = next((s for s in device.settings if s.name == setting_class.name), None) if setting is None and logger.isEnabledFor(logging.DEBUG): logger.debug( "No setting for %s found on %s when trying to record a change made elsewhere", setting_class.name, device, ) if setting: assert device == setting._device if len(values) > 1: setting.update_key_value(values[0], values[-1]) value = {values[0]: values[-1]} else: setting.update(values[-1]) value = values[-1] device_path = device.receiver.path if device.receiver else device.path if (device_path, device.number, setting.name) in _items: sbox = _items[(device_path, device.number, setting.name)] if sbox: _update_setting_item(sbox, value, sensitive=None) Solaar-1.1.14/lib/solaar/ui/desktop_notifications.py000066400000000000000000000114151473525213700224520ustar00rootroot00000000000000## Copyright (C) 2012-2013 Daniel Pavel ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import importlib # Optional desktop notifications. import logging from solaar import NAME from solaar.i18n import _ from . import icons logger = logging.getLogger(__name__) def notifications_available(): """Checks if notification service is available.""" notifications_supported = False try: import gi gi.require_version("Notify", "0.7") importlib.util.find_spec("gi.repository.GLib") importlib.util.find_spec("gi.repository.Notify") notifications_supported = True except ValueError as e: logger.warning(f"Notification service is not available: {e}") return notifications_supported available = notifications_available() if available: from gi.repository import GLib from gi.repository import Notify # cache references to shown notifications here, so if another status comes # while its notification is still visible we don't create another one _notifications = {} def init(): """Initialize desktop notifications.""" global available if available: if not Notify.is_initted(): if logger.isEnabledFor(logging.INFO): logger.info("starting desktop notifications") try: return Notify.init(NAME.lower()) except Exception: logger.exception("initializing desktop notifications") available = False return available and Notify.is_initted() def uninit(): """Stop desktop notifications.""" if available and Notify.is_initted(): if logger.isEnabledFor(logging.INFO): logger.info("stopping desktop notifications") _notifications.clear() Notify.uninit() def alert(reason, icon=None): assert reason if available and Notify.is_initted(): n = _notifications.get(NAME.lower()) if n is None: n = _notifications[NAME.lower()] = Notify.Notification() # we need to use the filename here because the notifications daemon # is an external application that does not know about our icon sets icon_file = icons.icon_file(NAME.lower()) if icon is None else icons.icon_file(icon) n.update(NAME.lower(), reason, icon_file) n.set_urgency(Notify.Urgency.NORMAL) n.set_hint("desktop-entry", GLib.Variant("s", NAME.lower())) try: n.show() except Exception: logger.exception("showing %s", n) def show(dev, reason=None, icon=None, progress=None): """Show a notification with title and text. Optionally displays the `progress` integer value in [0, 100] as a progress bar.""" if available and Notify.is_initted(): summary = dev.name # if a notification with same name is already visible, reuse it to avoid spamming n = _notifications.get(summary) if n is None: n = _notifications[summary] = Notify.Notification() if reason: message = reason else: message = _("unspecified reason") # we need to use the filename here because the notifications daemon # is an external application that does not know about our icon sets icon_file = icons.device_icon_file(dev.name, dev.kind) if icon is None else icons.icon_file(icon) n.update(summary, message, icon_file) n.set_urgency(Notify.Urgency.NORMAL) n.set_hint("desktop-entry", GLib.Variant("s", NAME.lower())) if progress: n.set_hint("value", GLib.Variant("i", progress)) try: n.show() except Exception: logger.exception(f"showing {n}") else: def init(): return False def uninit(): return None # toggle = lambda action: False def alert(reason): return None def show(dev, reason=None): return None Solaar-1.1.14/lib/solaar/ui/diversion_rules.py000066400000000000000000002217521473525213700212730ustar00rootroot00000000000000## Copyright (C) Solaar Contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. from __future__ import annotations import dataclasses import logging import string import threading from collections import defaultdict from copy import copy from dataclasses import dataclass from dataclasses import field from enum import Enum from shlex import quote as shlex_quote from typing import Any from typing import Callable from typing import Dict from typing import Optional from gi.repository import Gdk from gi.repository import GObject from gi.repository import Gtk from logitech_receiver import diversion as _DIV from logitech_receiver.common import NamedInt from logitech_receiver.common import NamedInts from logitech_receiver.common import UnsortedNamedInts from logitech_receiver.settings import KIND as _SKIND from logitech_receiver.settings import Setting from logitech_receiver.settings_templates import SETTINGS from solaar.i18n import _ from solaar.ui import rule_actions from solaar.ui import rule_conditions from solaar.ui.rule_base import RuleComponentUI from solaar.ui.rule_base import norm from solaar.ui.rule_conditions import ConditionUI logger = logging.getLogger(__name__) _diversion_dialog = None _rule_component_clipboard = None class RuleComponentWrapper(GObject.GObject): def __init__(self, component, level=0, editable=False): self.component = component self.level = level self.editable = editable GObject.GObject.__init__(self) def display_left(self): if isinstance(self.component, _DIV.Rule): if self.level == 0: return _("Built-in rules") if not self.editable else _("User-defined rules") if self.level == 1: return " " + _("Rule") return " " + _("Sub-rule") if self.component is None: return _("[empty]") return " " + self.__component_ui().left_label(self.component) def display_right(self): if self.component is None: return "" return self.__component_ui().right_label(self.component) def display_icon(self): if self.component is None: return "" if isinstance(self.component, _DIV.Rule) and self.level == 0: return "emblem-system" if not self.editable else "avatar-default" return self.__component_ui().icon_name() def __component_ui(self): return COMPONENT_UI.get(type(self.component), UnsupportedRuleComponentUI) def _create_close_dialog(window: Gtk.Window) -> Gtk.MessageDialog: """Creates rule editor close dialog, when unsaved changes are present.""" dialog = Gtk.MessageDialog( window, type=Gtk.MessageType.QUESTION, title=_("Make changes permanent?"), flags=Gtk.DialogFlags.MODAL, ) dialog.set_default_size(400, 100) dialog.add_buttons( _("Yes"), Gtk.ResponseType.YES, _("No"), Gtk.ResponseType.NO, _("Cancel"), Gtk.ResponseType.CANCEL, ) dialog.set_markup(_("If you choose No, changes will be lost when Solaar is closed.")) return dialog def _create_selected_rule_edit_panel() -> Gtk.Grid: """Creates the edit Condition/Actions panel for a rule. Shows the UI for the selected rule component. """ grid = Gtk.Grid() grid.set_margin_start(10) grid.set_margin_end(10) grid.set_row_spacing(10) grid.set_column_spacing(10) grid.set_halign(Gtk.Align.CENTER) grid.set_valign(Gtk.Align.CENTER) grid.set_size_request(0, 120) return grid def _populate_model( model: Gtk.TreeStore, it: Gtk.TreeIter, rule_component: Any, level: int = 0, pos: int = -1, editable: Optional[bool] = None, ): if isinstance(rule_component, list): for c in rule_component: _populate_model(model, it, c, level=level, pos=pos, editable=editable) if pos >= 0: pos += 1 return if editable is None: editable = model[it][0].editable if it is not None else False if isinstance(rule_component, _DIV.Rule): editable = editable or (rule_component.source is not None) wrapped = RuleComponentWrapper(rule_component, level, editable=editable) piter = model.insert(it, pos, (wrapped,)) if isinstance(rule_component, (_DIV.Rule, _DIV.And, _DIV.Or, _DIV.Later)): for c in rule_component.components: ed = editable or (isinstance(c, _DIV.Rule) and c.source is not None) _populate_model(model, piter, c, level + 1, editable=ed) if len(rule_component.components) == 0: _populate_model(model, piter, None, level + 1, editable=editable) elif isinstance(rule_component, _DIV.Not): _populate_model(model, piter, rule_component.component, level + 1, editable=editable) @dataclasses.dataclass class AllowedActions: c: Any copy: bool delete: bool flatten: bool insert: bool insert_only_rule: bool insert_root: bool wrap: bool def allowed_actions(m: Gtk.TreeStore, it: Gtk.TreeIter) -> AllowedActions: row = m[it] wrapped = row[0] c = wrapped.component parent_it = m.iter_parent(it) parent_c = m[parent_it][0].component if wrapped.level > 0 else None can_wrap = wrapped.editable and wrapped.component is not None and wrapped.level >= 2 can_delete = wrapped.editable and not isinstance(parent_c, _DIV.Not) and c is not None and wrapped.level >= 1 can_insert = wrapped.editable and not isinstance(parent_c, _DIV.Not) and wrapped.level >= 2 can_insert_only_rule = wrapped.editable and wrapped.level == 1 can_flatten = ( wrapped.editable and not isinstance(parent_c, _DIV.Not) and isinstance(c, (_DIV.Rule, _DIV.And, _DIV.Or)) and wrapped.level >= 2 and len(c.components) ) can_copy = wrapped.level >= 1 can_insert_root = wrapped.editable and wrapped.level == 0 return AllowedActions( c, can_copy, can_delete, can_flatten, can_insert, can_insert_only_rule, can_insert_root, can_wrap, ) class ActionMenu: def __init__( self, window: Gtk.Window, tree_view: Gtk.TreeView, populate_model_func: Callable, on_update: Callable, ): self.window = window self.tree_view = tree_view self._populate_model_func = populate_model_func self._on_update = on_update def create_menu_event_button_released(self, v, e): if e.button == Gdk.BUTTON_SECONDARY: # right click menu = Gtk.Menu() m, it = v.get_selection().get_selected() enabled_actions = allowed_actions(m, it) for item in self.get_insert_menus(m, it, enabled_actions): menu.append(item) if enabled_actions.flatten: menu.append(self._menu_flatten(m, it)) if enabled_actions.wrap: menu.append(self._menu_wrap(m, it)) menu.append(self._menu_negate(m, it)) if menu.get_children(): menu.append(Gtk.SeparatorMenuItem(visible=True)) if enabled_actions.delete: menu.append(self._menu_cut(m, it)) if enabled_actions.copy and enabled_actions.c is not None: menu.append(self._menu_copy(m, it)) if enabled_actions.insert and _rule_component_clipboard is not None: p = self._menu_paste(m, it) menu.append(p) if enabled_actions.c is None: # just a placeholder p.set_label(_("Paste here")) else: p.set_label(_("Paste above")) p2 = self._menu_paste(m, it, below=True) p2.set_label(_("Paste below")) menu.append(p2) elif enabled_actions.insert_only_rule and isinstance(_rule_component_clipboard, _DIV.Rule): p = self._menu_paste(m, it) menu.append(p) if enabled_actions.c is None: p.set_label(_("Paste rule here")) else: p.set_label(_("Paste rule above")) p2 = self._menu_paste(m, it, below=True) p2.set_label(_("Paste rule below")) menu.append(p2) elif enabled_actions.insert_root and isinstance(_rule_component_clipboard, _DIV.Rule): p = self._menu_paste(m, m.iter_nth_child(it, 0)) p.set_label(_("Paste rule")) menu.append(p) if menu.get_children() and enabled_actions.delete: menu.append(Gtk.SeparatorMenuItem(visible=True)) if enabled_actions.delete: menu.append(self._menu_delete(m, it)) if menu.get_children(): menu.popup_at_pointer(e) def get_insert_menus(self, m, it, enabled_actions: AllowedActions): items = [] if enabled_actions.insert: ins = self._menu_insert(m, it) items.append(ins) if enabled_actions.c is None: # just a placeholder ins.set_label(_("Insert here")) else: ins.set_label(_("Insert above")) ins2 = self._menu_insert(m, it, below=True) ins2.set_label(_("Insert below")) items.append(ins2) elif enabled_actions.insert_only_rule: ins = self._menu_create_rule(m, it) items.append(ins) if enabled_actions.c is None: ins.set_label(_("Insert new rule here")) else: ins.set_label(_("Insert new rule above")) ins2 = self._menu_create_rule(m, it, below=True) ins2.set_label(_("Insert new rule below")) items.append(ins2) elif enabled_actions.insert_root: ins = self._menu_create_rule(m, m.iter_nth_child(it, 0)) items.append(ins) return items def menu_do_flatten(self, _mitem, m, it): wrapped = m[it][0] c = wrapped.component parent_it = m.iter_parent(it) parent_c = m[parent_it][0].component idx = parent_c.components.index(c) if isinstance(c, _DIV.Not): parent_c.components = [*parent_c.components[:idx], c.component, *parent_c.components[idx + 1 :]] children = [next(m[it].iterchildren())[0].component] else: parent_c.components = [*parent_c.components[:idx], *c.components, *parent_c.components[idx + 1 :]] children = [child[0].component for child in m[it].iterchildren()] m.remove(it) self._populate_model_func(m, parent_it, children, level=wrapped.level, pos=idx) new_iter = m.iter_nth_child(parent_it, idx) self.tree_view.expand_row(m.get_path(parent_it), True) self.tree_view.get_selection().select_iter(new_iter) self._on_update() def _menu_flatten(self, m, it): menu_flatten = Gtk.MenuItem(_("Flatten")) menu_flatten.connect("activate", self.menu_do_flatten, m, it) menu_flatten.show() return menu_flatten def _menu_do_insert(self, _mitem, m, it, new_c, below=False): wrapped = m[it][0] c = wrapped.component parent_it = m.iter_parent(it) parent_c = m[parent_it][0].component if len(parent_c.components) == 0: # we had only a placeholder idx = 0 else: idx = parent_c.components.index(c) if isinstance(new_c, _DIV.Rule) and wrapped.level == 1: new_c.source = _DIV._file_path # new rules will be saved to the YAML file idx += int(below) parent_c.components.insert(idx, new_c) self._populate_model_func(m, parent_it, new_c, level=wrapped.level, pos=idx) self._on_update() if len(parent_c.components) == 1: m.remove(it) # remove placeholder in the end new_iter = m.iter_nth_child(parent_it, idx) self.tree_view.get_selection().select_iter(new_iter) if isinstance(new_c, (_DIV.Rule, _DIV.And, _DIV.Or, _DIV.Not)): self.tree_view.expand_row(m.get_path(new_iter), True) def _menu_do_insert_new(self, _mitem, m, it, cls, initial_value, below=False): new_c = cls(initial_value, warn=False) return self._menu_do_insert(_mitem, m, it, new_c, below=below) def _menu_insert(self, m, it, below=False): elements = [ _("Insert"), [ (_("Sub-rule"), _DIV.Rule, []), (_("Or"), _DIV.Or, []), (_("And"), _DIV.And, []), [ _("Condition"), [ (_("Feature"), _DIV.Feature, rule_conditions.FeatureUI.FEATURES_WITH_DIVERSION[0]), (_("Report"), _DIV.Report, 0), (_("Process"), _DIV.Process, ""), (_("Mouse process"), _DIV.MouseProcess, ""), (_("Modifiers"), _DIV.Modifiers, []), (_("Key"), _DIV.Key, ""), (_("KeyIsDown"), _DIV.KeyIsDown, ""), (_("Active"), _DIV.Active, ""), (_("Device"), _DIV.Device, ""), (_("Host"), _DIV.Host, ""), (_("Setting"), _DIV.Setting, [None, "", None]), (_("Test"), _DIV.Test, next(iter(_DIV.TESTS))), (_("Test bytes"), _DIV.TestBytes, [0, 1, 0]), (_("Mouse Gesture"), _DIV.MouseGesture, ""), ], ], [ _("Action"), [ (_("Key press"), _DIV.KeyPress, "space"), (_("Mouse scroll"), _DIV.MouseScroll, [0, 0]), (_("Mouse click"), _DIV.MouseClick, ["left", 1]), (_("Set"), _DIV.Set, [None, "", None]), (_("Execute"), _DIV.Execute, [""]), (_("Later"), _DIV.Later, [1]), ], ], ], ] def build(spec): if isinstance(spec, list): # has sub-menu label, children = spec item = Gtk.MenuItem(label) submenu = Gtk.Menu() item.set_submenu(submenu) for child in children: submenu.append(build(child)) return item elif isinstance(spec, tuple): # has click action label, feature, *args = spec item = Gtk.MenuItem(label) args = [a.copy() if isinstance(a, list) else a for a in args] item.connect("activate", self._menu_do_insert_new, m, it, feature, *args, below) return item else: return None menu_insert = build(elements) menu_insert.show_all() return menu_insert def _menu_create_rule(self, m, it, below=False) -> Gtk.MenuItem: menu_create_rule = Gtk.MenuItem(_("Insert new rule")) menu_create_rule.connect("activate", self._menu_do_insert_new, m, it, _DIV.Rule, [], below) menu_create_rule.show() return menu_create_rule def menu_do_delete(self, _mitem, m, it): wrapped = m[it][0] c = wrapped.component parent_it = m.iter_parent(it) parent_c = m[parent_it][0].component idx = parent_c.components.index(c) parent_c.components.pop(idx) if len(parent_c.components) == 0: # placeholder self._populate_model_func(m, parent_it, None, level=wrapped.level) m.remove(it) self.tree_view.get_selection().select_iter(m.iter_nth_child(parent_it, max(0, min(idx, len(parent_c.components) - 1)))) self._on_update() return c def _menu_delete(self, m, it) -> Gtk.MenuItem: menu_delete = Gtk.MenuItem(_("Delete")) menu_delete.connect("activate", self.menu_do_delete, m, it) menu_delete.show() return menu_delete def menu_do_negate(self, _mitem, m, it): wrapped = m[it][0] c = wrapped.component parent_it = m.iter_parent(it) parent_c = m[parent_it][0].component if isinstance(c, _DIV.Not): # avoid double negation self.menu_do_flatten(_mitem, m, it) self.tree_view.expand_row(m.get_path(parent_it), True) elif isinstance(parent_c, _DIV.Not): # avoid double negation self.menu_do_flatten(_mitem, m, parent_it) else: idx = parent_c.components.index(c) self._menu_do_insert_new(_mitem, m, it, _DIV.Not, c, below=True) self.menu_do_delete(_mitem, m, m.iter_nth_child(parent_it, idx)) self._on_update() def _menu_negate(self, m, it) -> Gtk.MenuItem: menu_negate = Gtk.MenuItem(_("Negate")) menu_negate.connect("activate", self.menu_do_negate, m, it) menu_negate.show() return menu_negate def menu_do_wrap(self, _mitem, m, it, cls): wrapped = m[it][0] c = wrapped.component parent_it = m.iter_parent(it) parent_c = m[parent_it][0].component if isinstance(parent_c, _DIV.Not): new_c = cls([c], warn=False) parent_c.component = new_c m.remove(it) self._populate_model_func(m, parent_it, new_c, level=wrapped.level, pos=0) self.tree_view.expand_row(m.get_path(parent_it), True) self.tree_view.get_selection().select_iter(m.iter_nth_child(parent_it, 0)) else: idx = parent_c.components.index(c) self._menu_do_insert_new(_mitem, m, it, cls, [c], below=True) self.menu_do_delete(_mitem, m, m.iter_nth_child(parent_it, idx)) self._on_update() def _menu_wrap(self, m, it) -> Gtk.MenuItem: menu_wrap = Gtk.MenuItem(_("Wrap with")) submenu_wrap = Gtk.Menu() menu_sub_rule = Gtk.MenuItem(_("Sub-rule")) menu_and = Gtk.MenuItem(_("And")) menu_or = Gtk.MenuItem(_("Or")) menu_sub_rule.connect("activate", self.menu_do_wrap, m, it, _DIV.Rule) menu_and.connect("activate", self.menu_do_wrap, m, it, _DIV.And) menu_or.connect("activate", self.menu_do_wrap, m, it, _DIV.Or) submenu_wrap.append(menu_sub_rule) submenu_wrap.append(menu_and) submenu_wrap.append(menu_or) menu_wrap.set_submenu(submenu_wrap) menu_wrap.show_all() return menu_wrap def menu_do_copy(self, _mitem: Gtk.MenuItem, m: Gtk.TreeStore, it: Gtk.TreeIter): global _rule_component_clipboard wrapped = m[it][0] c = wrapped.component _rule_component_clipboard = _DIV.RuleComponent().compile(c.data()) def menu_do_cut(self, _mitem, m, it): global _rule_component_clipboard c = self.menu_do_delete(_mitem, m, it) self._on_update() _rule_component_clipboard = c def _menu_cut(self, m, it): menu_cut = Gtk.MenuItem(_("Cut")) menu_cut.connect("activate", self.menu_do_cut, m, it) menu_cut.show() return menu_cut def menu_do_paste(self, _mitem, m, it, below=False): global _rule_component_clipboard c = _rule_component_clipboard _rule_component_clipboard = None if c: _rule_component_clipboard = _DIV.RuleComponent().compile(c.data()) self._menu_do_insert(_mitem, m, it, new_c=c, below=below) self._on_update() def _menu_paste(self, m, it, below=False): menu_paste = Gtk.MenuItem(_("Paste")) menu_paste.connect("activate", self.menu_do_paste, m, it, below) menu_paste.show() return menu_paste def _menu_copy(self, m, it): menu_copy = Gtk.MenuItem(_("Copy")) menu_copy.connect("activate", self.menu_do_copy, m, it) menu_copy.show() return menu_copy class DiversionDialog: def __init__(self, action_menu): window = Gtk.Window() window.set_title(_("Solaar Rule Editor")) window.connect("delete-event", self._closing) vbox = Gtk.VBox() self.top_panel, self.view = self._create_top_panel() for col in self._create_view_columns(): self.view.append_column(col) vbox.pack_start(self.top_panel, True, True, 0) self._action_menu = action_menu( window, self.view, populate_model_func=_populate_model, on_update=self.on_update, ) self.dirty = False # if dirty, there are pending changes to be saved self.type_ui = {} self.update_ui = {} self.selected_rule_edit_panel = _create_selected_rule_edit_panel() self.ui = defaultdict(lambda: UnsupportedRuleComponentUI(self.selected_rule_edit_panel)) self.ui.update( { # one instance per type rc_class: rc_ui_class(self.selected_rule_edit_panel, on_update=self.on_update) for rc_class, rc_ui_class in COMPONENT_UI.items() } ) vbox.pack_start(self.selected_rule_edit_panel, False, False, 10) self.model = self._create_model() self.view.set_model(self.model) self.view.expand_all() window.add(vbox) geometry = Gdk.Geometry() geometry.min_width = 600 # don't ask for so much space geometry.min_height = 400 window.set_geometry_hints(None, geometry, Gdk.WindowHints.MIN_SIZE) window.set_position(Gtk.WindowPosition.CENTER) window.show_all() window.connect("delete-event", lambda w, e: w.hide_on_delete() or True) style = window.get_style_context() style.add_class("solaar") self.window = window self._editing_component = None def _closing(self, window: Gtk.Window, e: Gdk.Event): if self.dirty: dialog = _create_close_dialog(window) response = dialog.run() dialog.destroy() if response == Gtk.ResponseType.NO: window.hide() elif response == Gtk.ResponseType.YES: self._save_yaml_file() window.hide() else: # don't close return True else: window.hide() def _reload_yaml_file(self): self.discard_btn.set_sensitive(False) self.save_btn.set_sensitive(False) self.dirty = False for c in self.selected_rule_edit_panel.get_children(): self.selected_rule_edit_panel.remove(c) _DIV.load_config_rule_file() self.model = self._create_model() self.view.set_model(self.model) self.view.expand_all() def _save_yaml_file(self): if _DIV._save_config_rule_file(): self.dirty = False self.save_btn.set_sensitive(False) self.discard_btn.set_sensitive(False) def _create_top_panel(self): sw = Gtk.ScrolledWindow() sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.ALWAYS) view = Gtk.TreeView() view.set_headers_visible(False) view.set_enable_tree_lines(True) view.set_reorderable(False) view.connect("key-press-event", self._event_key_pressed) view.connect("button-release-event", self._event_button_released) view.get_selection().connect("changed", self._selection_changed) sw.add(view) sw.set_size_request(0, 300) # don't ask for so much height button_box = Gtk.HBox(spacing=20) self.save_btn = Gtk.Button.new_from_icon_name("document-save", Gtk.IconSize.BUTTON) self.save_btn.set_label(_("Save changes")) self.save_btn.set_always_show_image(True) self.save_btn.set_sensitive(False) self.save_btn.set_valign(Gtk.Align.CENTER) self.discard_btn = Gtk.Button.new_from_icon_name("document-revert", Gtk.IconSize.BUTTON) self.discard_btn.set_label(_("Discard changes")) self.discard_btn.set_always_show_image(True) self.discard_btn.set_sensitive(False) self.discard_btn.set_valign(Gtk.Align.CENTER) self.save_btn.connect("clicked", lambda *_args: self._save_yaml_file()) self.discard_btn.connect("clicked", lambda *_args: self._reload_yaml_file()) button_box.pack_start(self.save_btn, False, False, 0) button_box.pack_start(self.discard_btn, False, False, 0) button_box.set_halign(Gtk.Align.CENTER) button_box.set_valign(Gtk.Align.CENTER) button_box.set_size_request(0, 50) vbox = Gtk.VBox() vbox.pack_start(button_box, False, False, 0) vbox.pack_start(sw, True, True, 0) return vbox, view def _create_model(self): model = Gtk.TreeStore(RuleComponentWrapper) if len(_DIV.rules.components) == 1: # only built-in rules - add empty user rule list _DIV.rules.components.insert(0, _DIV.Rule([], source=_DIV._file_path)) _populate_model(model, None, _DIV.rules.components) return model def _create_view_columns(self): cell_icon = Gtk.CellRendererPixbuf() cell1 = Gtk.CellRendererText() col1 = Gtk.TreeViewColumn("Type") col1.pack_start(cell_icon, False) col1.pack_start(cell1, True) col1.set_cell_data_func(cell1, lambda _c, c, m, it, _d: c.set_property("text", m.get_value(it, 0).display_left())) cell2 = Gtk.CellRendererText() col2 = Gtk.TreeViewColumn("Summary") col2.pack_start(cell2, True) col2.set_cell_data_func(cell2, lambda _c, c, m, it, _d: c.set_property("text", m.get_value(it, 0).display_right())) col2.set_cell_data_func( cell_icon, lambda _c, c, m, it, _d: c.set_property("icon-name", m.get_value(it, 0).display_icon()) ) return col1, col2 def on_update(self): self.view.queue_draw() self.dirty = True self.save_btn.set_sensitive(True) self.discard_btn.set_sensitive(True) def _selection_changed(self, selection): self.selected_rule_edit_panel.set_sensitive(False) (model, it) = selection.get_selected() if it is None: return wrapped = model[it][0] component = wrapped.component self._editing_component = component self.ui[type(component)].show(component, wrapped.editable) self.selected_rule_edit_panel.set_sensitive(wrapped.editable) def _event_key_pressed(self, v, e): """ Shortcuts: Ctrl + I insert component Ctrl + Delete delete row & wrap with And | wrap with Or Shift + R wrap with Rule ! negate Ctrl + X cut Ctrl + C copy Ctrl + V paste below (or here if empty) Ctrl + Shift + V paste above * flatten Ctrl + S save changes """ state = e.state & (Gdk.ModifierType.CONTROL_MASK | Gdk.ModifierType.SHIFT_MASK) m, it = v.get_selection().get_selected() enabled_actions = allowed_actions(m, it) if state & Gdk.ModifierType.CONTROL_MASK: if enabled_actions.delete and e.keyval in [Gdk.KEY_x, Gdk.KEY_X]: self._action_menu.menu_do_cut(None, m, it) elif enabled_actions.copy and e.keyval in [Gdk.KEY_c, Gdk.KEY_C] and enabled_actions.c is not None: self._action_menu.menu_do_copy(None, m, it) elif enabled_actions.insert and _rule_component_clipboard is not None and e.keyval in [Gdk.KEY_v, Gdk.KEY_V]: self._action_menu.menu_do_paste( None, m, it, below=enabled_actions.c is not None and not (state & Gdk.ModifierType.SHIFT_MASK) ) elif ( enabled_actions.insert_only_rule and isinstance(_rule_component_clipboard, _DIV.Rule) and e.keyval in [Gdk.KEY_v, Gdk.KEY_V] ): self._action_menu.menu_do_paste( None, m, it, below=enabled_actions.c is not None and not (state & Gdk.ModifierType.SHIFT_MASK) ) elif ( enabled_actions.insert_root and isinstance(_rule_component_clipboard, _DIV.Rule) and e.keyval in [Gdk.KEY_v, Gdk.KEY_V] ): self._action_menu.menu_do_paste(None, m, m.iter_nth_child(it, 0)) elif enabled_actions.delete and e.keyval in [Gdk.KEY_KP_Delete, Gdk.KEY_Delete]: self._action_menu.menu_do_delete(None, m, it) elif (enabled_actions.insert or enabled_actions.insert_only_rule or enabled_actions.insert_root) and e.keyval in [ Gdk.KEY_i, Gdk.KEY_I, ]: menu = Gtk.Menu() for item in self._action_menu.get_insert_menus( m, it, enabled_actions, ): menu.append(item) menu.show_all() rect = self.view.get_cell_area(m.get_path(it), self.view.get_column(1)) menu.popup_at_rect(self.window.get_window(), rect, Gdk.Gravity.WEST, Gdk.Gravity.CENTER, e) elif self.dirty and e.keyval in [Gdk.KEY_s, Gdk.KEY_S]: self._save_yaml_file() else: if enabled_actions.wrap: if e.keyval == Gdk.KEY_exclam: self._action_menu.menu_do_negate(None, m, it) elif e.keyval == Gdk.KEY_ampersand: self._action_menu.menu_do_wrap(None, m, it, _DIV.And) elif e.keyval == Gdk.KEY_bar: self._action_menu.menu_do_wrap(None, m, it, _DIV.Or) elif e.keyval in [Gdk.KEY_r, Gdk.KEY_R] and (state & Gdk.ModifierType.SHIFT_MASK): self._action_menu.menu_do_wrap(None, m, it, _DIV.Rule) if enabled_actions.flatten and e.keyval in [Gdk.KEY_asterisk, Gdk.KEY_KP_Multiply]: self._action_menu.menu_do_flatten(None, m, it) def _event_button_released(self, v, e): self._action_menu.create_menu_event_button_released(v, e) def update_devices(self): for rc in self.ui.values(): rc.update_devices() self.view.queue_draw() class CompletionEntry(Gtk.Entry): def __init__(self, values, *args, **kwargs): super().__init__(*args, **kwargs) CompletionEntry.add_completion_to_entry(self, values) @classmethod def add_completion_to_entry(cls, entry, values): completion = entry.get_completion() if not completion: liststore = Gtk.ListStore(str) completion = Gtk.EntryCompletion() completion.set_model(liststore) completion.set_match_func(lambda completion, key, it: norm(key) in norm(completion.get_model()[it][0])) completion.set_text_column(0) entry.set_completion(completion) else: liststore = completion.get_model() liststore.clear() for v in sorted(set(values), key=str.casefold): liststore.append((v,)) class SmartComboBox(Gtk.ComboBox): """A custom ComboBox with some extra features. The constructor requires a collection of allowed values. Each element must be a single value or a non-empty tuple containing: - a value (any hashable object) - a name (optional; str(value) is used if not provided) - alternative names. Example: (some_object, 'object name', 'other name', 'also accept this'). It is assumed that the same string cannot be the name or an alternative name of more than one value. The widget displays the names, but the alternative names are also suggested and accepted as input. If `has_entry` is `True`, then the user can insert arbitrary text (possibly with auto-complete if `completion` is True). Otherwise, only a drop-down list is shown, with an extra blank item in the beginning (correspondent to `None`). The display text of the blank item is defined by the parameter `blank`. If `case_insensitive` is `True`, then upper-case and lower-case letters are treated as equal. If `replace_with_default_name`, then the field text is immediately replaced with the default name of a value as soon as the user finishes typing any accepted name. """ def __init__( self, all_values, blank="", completion=False, case_insensitive=False, replace_with_default_name=False, **kwargs ): super().__init__(**kwargs) self._name_to_idx = {} self._value_to_idx = {} self._hidden_idx = set() self._all_values = [] self._blank = blank self._model = None self._commpletion = completion self._case_insensitive = case_insensitive self._norm = lambda s: None if s is None else s if not case_insensitive else str(s).upper() self._replace_with_default_name = replace_with_default_name def replace_with(value): if self.get_has_entry() and self._replace_with_default_name and value is not None: item = self._all_values[self._value_to_idx[value]] name = item[1] if len(item) > 1 else str(item[0]) if name != self.get_child().get_text(): self.get_child().set_text(name) self.connect("changed", lambda *a: replace_with(self.get_value(invalid_as_str=False))) self.set_id_column(0) if self.get_has_entry(): self.set_entry_text_column(1) else: renderer = Gtk.CellRendererText() self.pack_start(renderer, True) self.add_attribute(renderer, "text", 1) self.set_all_values(all_values) self.set_active_id("") @classmethod def new_model(cls): model = Gtk.ListStore(str, str, bool) # (index: int converted to str, name: str, visible: bool) filtered_model = model.filter_new() filtered_model.set_visible_column(2) return model, filtered_model def set_all_values(self, all_values, visible_fn=(lambda value: True)): old_value = self.get_value() self._name_to_idx = {} self._value_to_idx = {} self._hidden_idx = set() self._all_values = [v if isinstance(v, tuple) else (v,) for v in all_values] model, filtered_model = SmartComboBox.new_model() # creating a new model seems to be necessary to avoid firing 'changed' event once per inserted item model.append(("", self._blank, True)) self._model = model to_complete = [self._blank] for idx, item in enumerate(self._all_values): value, *names = item if isinstance(item, tuple) else (item,) visible = visible_fn(value) self._include(model, idx, value, visible, *names) if visible: to_complete += names if names else [str(value).strip()] self.set_model(filtered_model) if self.get_has_entry() and self._commpletion: CompletionEntry.add_completion_to_entry(self.get_child(), to_complete) if self._find_idx(old_value) is not None: self.set_value(old_value) else: self.set_value(self._blank) self.queue_draw() def _include(self, model, idx, value, visible, *names): name = str(names[0]) if names else str(value).strip() self._name_to_idx[self._norm(name)] = idx if isinstance(value, NamedInt): self._name_to_idx[self._norm(str(name))] = idx model.append((str(idx), name, visible)) for alt in names[1:]: self._name_to_idx[self._norm(str(alt).strip())] = idx self._value_to_idx[value] = idx if self._case_insensitive and isinstance(value, str): self._name_to_idx[self._norm(value)] = idx def get_value(self, invalid_as_str=True, accept_hidden=True): """Return the selected value or the typed text. If the typed or selected text corresponds to one of the allowed values (or their names and alternative names), then the value is returned. Otherwise, the raw text is returned as string if the widget has an entry and `invalid_as_str` is `True`; if the widget has no entry or `invalid_as_str` is `False`, then `None` is returned. """ tree_iter = self.get_active_iter() if tree_iter is not None: t = self.get_model()[tree_iter] number = t[0] return self._all_values[int(number)][0] if number != "" and (accept_hidden or t[2]) else None elif self.get_has_entry(): text = self.get_child().get_text().strip() if text == self._blank: return None idx = self._find_idx(text) if idx is None: return text if invalid_as_str else None item = self._all_values[idx] return item[0] return None def _find_idx(self, search): if search == self._blank: return None try: return self._value_to_idx[search] except KeyError: pass try: return self._name_to_idx[self._norm(search)] except KeyError: pass return None def set_value(self, value, accept_invalid=True): """Set a specific value. Raw values, their names and alternative names are accepted. Base-10 representations of int values as strings are also accepted. The actual value is used in all cases. If `value` is invalid, then the entry text is set to the provided value if the widget has an entry and `accept_invalid` is True, or else the blank value is set. """ idx = self._find_idx(value) if value != self._blank else "" if idx is not None: self.set_active_id(str(idx)) else: if self.get_has_entry() and accept_invalid: self.get_child().set_text(str(value or "") if value != "" else self._blank) else: self.set_active_id("") def show_only(self, only, include_new=False): """Hide items not present in `only`. Only values are accepted (not their names and alternative names). If `include_new` is True, then the values in `only` not currently present are included with their string representation as names; otherwise, they are ignored. If `only` is new, then the visibility status is reset and all values are shown. """ values = self._all_values[:] if include_new and only is not None: values += [v for v in only if v not in self._value_to_idx] self.set_all_values(values, (lambda v: only is None or (v in only))) @dataclass class DeviceInfo: serial: str = "" unitId: str = "" codename: str = "" settings: Dict[str, Setting] = field(default_factory=dict) def __post_init__(self): if self.serial is None or self.serial == "?": self.serial = "" if self.unitId is None or self.unitId == "?": self.unitId = "" @property def id(self) -> str: return self.serial or self.unitId or "" @property def identifiers(self) -> list[str]: return [id for id in (self.serial, self.unitId) if id] @property def display_name(self) -> str: return f"{self.codename} ({self.id})" def matches(self, search: str) -> bool: return search and search in (self.serial, self.unitId, self.display_name) def update(self, device: DeviceInfo) -> None: for k in ("serial", "unitId", "codename", "settings"): if not getattr(self, k, None): v = getattr(device, k, None) if v and v != "?": setattr(self, k, copy(v) if k != "settings" else {s.name: s for s in v}) class DeviceInfoFactory: @classmethod def create_device_info(cls, device) -> DeviceInfo: d = DeviceInfo() d.update(device) return d class AllDevicesInfo: def __init__(self): self._devices: list[DeviceInfo] = [] self._lock = threading.Lock() def __iter__(self): return iter(self._devices) def __getitem__(self, search): if not search: return search assert isinstance(search, str) # linear search - ok because it is always a small list return next((d for d in self._devices if d.matches(search)), None) def refresh(self): updated = False def dev_in_row(_store, _treepath, row): nonlocal updated device = _dev_model.get_value(row, 7) if device and device.kind and (device.serial and device.serial != "?" or device.unitId and device.unitId != "?"): existing = self[device.serial] or self[device.unitId] if not existing: updated = True device_info = DeviceInfoFactory.create_device_info(device) self._devices.append(device_info) elif not existing.settings and device.settings: updated = True existing.update(device) with self._lock: _dev_model.foreach(dev_in_row) return updated class UnsupportedRuleComponentUI(RuleComponentUI): CLASS = None def create_widgets(self): self.label = Gtk.Label(valign=Gtk.Align.CENTER, hexpand=True) self.label.set_text(_("This editor does not support the selected rule component yet.")) self.widgets[self.label] = (0, 0, 1, 1) @classmethod def right_label(cls, component): return str(component) class RuleUI(RuleComponentUI): CLASS = _DIV.Rule def create_widgets(self): self.widgets = {} def collect_value(self): return self.component.components[:] # not editable on the bottom panel @classmethod def left_label(cls, component): return _("Rule") @classmethod def icon_name(cls): return "format-justify-fill" class AndUI(RuleComponentUI): CLASS = _DIV.And def create_widgets(self): self.widgets = {} def collect_value(self): return self.component.components[:] # not editable on the bottom panel @classmethod def left_label(cls, component): return _("And") class OrUI(RuleComponentUI): CLASS = _DIV.Or def create_widgets(self): self.widgets = {} def collect_value(self): return self.component.components[:] # not editable on the bottom panel @classmethod def left_label(cls, component): return _("Or") class LaterUI(RuleComponentUI): CLASS = _DIV.Later MIN_VALUE = 0.01 MAX_VALUE = 100 def create_widgets(self): self.widgets = {} self.label = Gtk.Label(valign=Gtk.Align.CENTER, hexpand=True) self.label.set_text(_("Number of seconds to delay. Delay between 0 and 1 is done with higher precision.")) self.widgets[self.label] = (0, 0, 1, 1) self.field = Gtk.SpinButton.new_with_range(self.MIN_VALUE, self.MAX_VALUE, 1) self.field.set_digits(3) self.field.set_halign(Gtk.Align.CENTER) self.field.set_valign(Gtk.Align.CENTER) self.field.set_hexpand(True) self.field.connect("value-changed", self._on_update) self.widgets[self.field] = (0, 1, 1, 1) def show(self, component, editable): super().show(component, editable) with self.ignore_changes(): self.field.set_value(component.delay) def collect_value(self): return [float(int((self.field.get_value() + 0.0001) * 1000)) / 1000] + self.component.components @classmethod def left_label(cls, component): return _("Later") @classmethod def right_label(cls, component): return str(component.delay) class NotUI(RuleComponentUI): CLASS = _DIV.Not def create_widgets(self): self.widgets = {} def collect_value(self): return self.component.component # not editable on the bottom panel @classmethod def left_label(cls, component): return _("Not") class ActionUI(RuleComponentUI): CLASS = _DIV.Action @classmethod def icon_name(cls): return "go-next" def _from_named_ints(v, all_values): """Obtain a NamedInt from NamedInts given its numeric value (as int) or name.""" if all_values and (v in all_values): return all_values[v] return v class SetValueControlKinds(Enum): TOGGLE = "toggle" RANGE = "range" RANGE_WITH_KEY = "range_with_key" CHOICE = "choice" class SetValueControl(Gtk.HBox): def __init__(self, on_change, *args, accept_toggle=True, **kwargs): super().__init__(*args, **kwargs) self.on_change = on_change self.toggle_widget = SmartComboBox( [ *([("Toggle", _("Toggle"), "~")] if accept_toggle else []), (True, _("True"), "True", "yes", "on", "t", "y"), (False, _("False"), "False", "no", "off", "f", "n"), ], case_insensitive=True, ) self.toggle_widget.connect("changed", self._changed) self.range_widget = Gtk.SpinButton.new_with_range(0, 0xFFFF, 1) self.range_widget.connect("value-changed", self._changed) self.choice_widget = SmartComboBox( [], completion=True, has_entry=True, case_insensitive=True, replace_with_default_name=True ) self.choice_widget.connect("changed", self._changed) self.sub_key_widget = SmartComboBox([]) self.sub_key_widget.connect("changed", self._changed) self.unsupported_label = Gtk.Label(label=_("Unsupported setting")) self.pack_start(self.sub_key_widget, False, False, 0) self.sub_key_widget.set_hexpand(False) self.sub_key_widget.set_size_request(120, 0) self.sub_key_widget.hide() for w in [self.toggle_widget, self.range_widget, self.choice_widget, self.unsupported_label]: self.pack_end(w, True, True, 0) w.hide() self.unsupp_value = None self.current_kind: Optional[SetValueControlKinds] = None self.sub_key_range_items = None def _changed(self, widget, *args): if widget.get_visible(): value = self.get_value() if self.current_kind == SetValueControlKinds.CHOICE: value = widget.get_value() icon = "dialog-warning" if widget._allowed_values and (value not in widget._allowed_values) else "" widget.get_child().set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, icon) elif self.current_kind == SetValueControlKinds.RANGE_WITH_KEY and widget == self.sub_key_widget: key = self.sub_key_widget.get_value() selected_item = ( next((item for item in self.sub_key_range_items if key == item.id), None) if self.sub_key_range_items else None ) (minimum, maximum) = (selected_item.minimum, selected_item.maximum) if selected_item else (0, 0xFFFF) self.range_widget.set_range(minimum, maximum) self.on_change(value) def _hide_all(self): for w in self.get_children(): w.hide() def get_value(self): if self.current_kind == SetValueControlKinds.TOGGLE: return self.toggle_widget.get_value() if self.current_kind == SetValueControlKinds.RANGE: return int(self.range_widget.get_value()) if self.current_kind == SetValueControlKinds.RANGE_WITH_KEY: return {self.sub_key_widget.get_value(): int(self.range_widget.get_value())} if self.current_kind == SetValueControlKinds.CHOICE: return self.choice_widget.get_value() return self.unsupp_value def set_value(self, value): if self.current_kind == SetValueControlKinds.TOGGLE: self.toggle_widget.set_value(value if value is not None else "") elif self.current_kind == SetValueControlKinds.RANGE: minimum, maximum = self.range_widget.get_range() try: v = round(float(value)) except (ValueError, TypeError): v = minimum self.range_widget.set_value(max(minimum, min(maximum, v))) elif self.current_kind == SetValueControlKinds.RANGE_WITH_KEY: if not (isinstance(value, dict) and len(value) == 1): value = {None: None} key = next(iter(value.keys())) selected_item = ( next((item for item in self.sub_key_range_items if key == item.id), None) if self.sub_key_range_items else None ) (minimum, maximum) = (selected_item.minimum, selected_item.maximum) if selected_item else (0, 0xFFFF) try: v = round(float(next(iter(value.values())))) except (ValueError, TypeError): v = minimum self.sub_key_widget.set_value(key or "") self.range_widget.set_value(max(minimum, min(maximum, v))) elif self.current_kind == SetValueControlKinds.CHOICE: self.choice_widget.set_value(value) else: self.unsupp_value = value if value is None or value == "": # reset all self.range_widget.set_range(0x0000, 0xFFFF) self.range_widget.set_value(0) self.toggle_widget.set_active_id("") self.sub_key_widget.set_value("") self.choice_widget.set_value("") def make_toggle(self): self.current_kind = SetValueControlKinds.TOGGLE self._hide_all() self.toggle_widget.show() def make_range(self, minimum, maximum): self.current_kind = SetValueControlKinds.RANGE self._hide_all() self.range_widget.set_range(minimum, maximum) self.range_widget.show() def make_range_with_key(self, items, labels=None): self.current_kind = SetValueControlKinds.RANGE_WITH_KEY self._hide_all() self.sub_key_range_items = items or None if not labels: labels = {} self.sub_key_widget.set_all_values( map(lambda item: (item.id, labels.get(item.id, [str(item.id)])[0]), items) if items else [] ) self.sub_key_widget.show() self.range_widget.show() def make_choice(self, values, extra=None): # if extra is not in values, it is ignored self.current_kind = SetValueControlKinds.CHOICE self._hide_all() sort_key = int if all((v == extra or str(v).isdigit()) for v in values) else str if extra is not None and extra in values: values = [extra] + sorted((v for v in values if v != extra), key=sort_key) else: values = sorted(values, key=sort_key) self.choice_widget.set_all_values(values) self.choice_widget._allowed_values = values self.choice_widget.show() def make_unsupported(self): self.current_kind = None self._hide_all() self.unsupported_label.show() def _all_settings(): settings = {} for s in sorted(SETTINGS, key=lambda setting: setting.label): if s.name not in settings: settings[s.name] = [s] else: prev_setting = settings[s.name][0] prev_kind = prev_setting.validator_class.kind if prev_kind != s.validator_class.kind: logger.warning( "ignoring setting {} - same name of {}, but different kind ({} != {})".format( s.__name__, prev_setting.__name__, prev_kind, s.validator_class.kind ) ) continue settings[s.name].append(s) return settings class _DeviceUI: label_text = "" def show(self, component, editable): super().show(component, editable) with self.ignore_changes(): same = not component.devID device = _all_devices[component.devID] self.device_field.set_value(device.id if device else "" if same else component.devID or "") def create_widgets(self): self.widgets = {} self.label = Gtk.Label(valign=Gtk.Align.CENTER, hexpand=True) self.label.set_text(self.label_text) self.widgets[self.label] = (0, 0, 5, 1) lbl = Gtk.Label(label=_("Device"), halign=Gtk.Align.CENTER, valign=Gtk.Align.CENTER, hexpand=True) self.widgets[lbl] = (0, 1, 1, 1) self.device_field = SmartComboBox( [], completion=True, has_entry=True, blank=_("Originating device"), case_insensitive=True, replace_with_default_name=True, ) self.device_field.set_value("") self.device_field.set_valign(Gtk.Align.CENTER) self.device_field.set_size_request(400, 0) self.device_field.connect("changed", self._on_update) self.widgets[self.device_field] = (1, 1, 1, 1) def update_devices(self): self._update_device_list() def _update_device_list(self): with self.ignore_changes(): self.device_field.set_all_values([(d.id, d.display_name, *d.identifiers[1:]) for d in _all_devices]) def collect_value(self): device_str = self.device_field.get_value() same = device_str in ["", _("Originating device")] device = None if same else _all_devices[device_str] device_value = device.id if device else None if same else device_str return device_value @classmethod def right_label(cls, component): device = _all_devices[component.devID] return device.display_name if device else shlex_quote(component.devID) class ActiveUI(_DeviceUI, ConditionUI): CLASS = _DIV.Active label_text = _("Device is active and its settings can be changed.") @classmethod def left_label(cls, component): return _("Active") class DeviceUI(_DeviceUI, ConditionUI): CLASS = _DIV.Device label_text = _("Device that originated the current notification.") @classmethod def left_label(cls, component): return _("Device") class HostUI(ConditionUI): CLASS = _DIV.Host def create_widgets(self): self.widgets = {} self.label = Gtk.Label(valign=Gtk.Align.CENTER, hexpand=True) self.label.set_text(_("Name of host computer.")) self.widgets[self.label] = (0, 0, 1, 1) self.field = Gtk.Entry(halign=Gtk.Align.CENTER, valign=Gtk.Align.CENTER, hexpand=True) self.field.set_size_request(600, 0) self.field.connect("changed", self._on_update) self.widgets[self.field] = (0, 1, 1, 1) def show(self, component, editable): super().show(component, editable) with self.ignore_changes(): self.field.set_text(component.host) def collect_value(self): return self.field.get_text() @classmethod def left_label(cls, component): return _("Host") @classmethod def right_label(cls, component): return str(component.host) class _SettingWithValueUI: ALL_SETTINGS = _all_settings() MULTIPLE = [_SKIND.multiple_toggle, _SKIND.map_choice, _SKIND.multiple_range] ACCEPT_TOGGLE = True label_text = "" def create_widgets(self): self.widgets = {} self.label = Gtk.Label(valign=Gtk.Align.CENTER, hexpand=True) self.label.set_text(self.label_text) self.widgets[self.label] = (0, 0, 5, 1) m = 20 lbl = Gtk.Label(label=_("Device"), halign=Gtk.Align.CENTER, valign=Gtk.Align.CENTER, hexpand=True, margin_top=m) self.widgets[lbl] = (0, 1, 1, 1) self.device_field = SmartComboBox( [], completion=True, has_entry=True, blank=_("Originating device"), case_insensitive=True, replace_with_default_name=True, ) self.device_field.set_value("") self.device_field.set_valign(Gtk.Align.CENTER) self.device_field.set_size_request(400, 0) self.device_field.set_margin_top(m) self.device_field.connect("changed", self._changed_device) self.device_field.connect("changed", self._on_update) self.widgets[self.device_field] = (1, 1, 1, 1) lbl = Gtk.Label( label=_("Setting"), halign=Gtk.Align.CENTER, valign=Gtk.Align.CENTER, hexpand=True, vexpand=False, ) self.widgets[lbl] = (0, 2, 1, 1) self.setting_field = SmartComboBox([(s[0].name, s[0].label) for s in self.ALL_SETTINGS.values()]) self.setting_field.set_valign(Gtk.Align.CENTER) self.setting_field.connect("changed", self._changed_setting) self.setting_field.connect("changed", self._on_update) self.widgets[self.setting_field] = (1, 2, 1, 1) self.value_lbl = Gtk.Label( label=_("Value"), halign=Gtk.Align.CENTER, valign=Gtk.Align.CENTER, hexpand=True, vexpand=False ) self.widgets[self.value_lbl] = (2, 2, 1, 1) self.value_field = SetValueControl(self._on_update, accept_toggle=self.ACCEPT_TOGGLE) self.value_field.set_valign(Gtk.Align.CENTER) self.value_field.set_size_request(250, 35) self.widgets[self.value_field] = (3, 2, 1, 1) self.key_lbl = Gtk.Label( label=_("Item"), halign=Gtk.Align.CENTER, valign=Gtk.Align.CENTER, hexpand=True, vexpand=False, margin_top=m ) self.key_lbl.hide() self.widgets[self.key_lbl] = (2, 1, 1, 1) self.key_field = SmartComboBox( [], has_entry=True, completion=True, case_insensitive=True, replace_with_default_name=True ) self.key_field.set_margin_top(m) self.key_field.hide() self.key_field.set_valign(Gtk.Align.CENTER) self.key_field.connect("changed", self._changed_key) self.key_field.connect("changed", self._on_update) self.widgets[self.key_field] = (3, 1, 1, 1) @classmethod def _all_choices(cls, setting): # choice and map-choice """Return a NamedInts instance with the choices for a setting. If the argument `setting` is a Setting instance or subclass, then the choices are taken only from it. If instead it is a name, then the function returns the union of the choices for each setting with that name. Only one label per number is kept. The function returns a 2-tuple whose first element is a NamedInts instance with the possible choices (including the extra value if it exists) and the second element is the extra value to be pinned to the start of the list (or `None` if there is no extra value). """ if isinstance(setting, Setting): setting = type(setting) if isinstance(setting, type) and issubclass(setting, Setting): choices = UnsortedNamedInts() universe = getattr(setting, "choices_universe", None) if universe: choices |= universe extra = getattr(setting, "choices_extra", None) if extra is not None: choices |= NamedInts(**{str(extra): int(extra)}) return choices, extra settings = cls.ALL_SETTINGS.get(setting, []) choices = UnsortedNamedInts() extra = None for s in settings: ch, ext = cls._all_choices(s) choices |= ch if ext is not None: extra = ext return choices, extra @classmethod def _setting_attributes(cls, setting_name, device=None): if device and setting_name in device.settings: setting = device.settings.get(setting_name, None) settings = [type(setting)] if setting else None else: settings = cls.ALL_SETTINGS.get(setting_name, [None]) setting = settings[0] # if settings have the same name, use the first one to get the basic data val_class = setting.validator_class if setting else None kind = val_class.kind if val_class else None if kind in cls.MULTIPLE: keys = UnsortedNamedInts() for s in settings: universe = getattr(s, "keys_universe" if kind == _SKIND.map_choice else "choices_universe", None) if universe: keys |= universe # only one key per number is used else: keys = None return setting, val_class, kind, keys def _changed_device(self, *args): device = _all_devices[self.device_field.get_value()] setting_name = self.setting_field.get_value() if not device or not device.settings or setting_name in device.settings: kind = self._setting_attributes(setting_name, device)[2] key = self.key_field.get_value() if kind in self.MULTIPLE else None else: setting_name = kind = key = None with self.ignore_changes(): self._update_setting_list(device) self._update_key_list(setting_name, device) self._update_value_list(setting_name, device, key) def _changed_setting(self, *args): with self.ignore_changes(): device = _all_devices[self.device_field.get_value()] setting_name = self.setting_field.get_value() self._update_key_list(setting_name, device) key = self.key_field.get_value() self._update_value_list(setting_name, device, key) def _changed_key(self, *args): with self.ignore_changes(): setting_name = self.setting_field.get_value() device = _all_devices[self.device_field.get_value()] key = self.key_field.get_value() self._update_value_list(setting_name, device, key) def update_devices(self): self._update_device_list() def _update_device_list(self): with self.ignore_changes(): self.device_field.set_all_values([(d.id, d.display_name, *d.identifiers[1:]) for d in _all_devices]) def _update_setting_list(self, device=None): supported_settings = device.settings.keys() if device else {} with self.ignore_changes(): self.setting_field.show_only(supported_settings or None) def _update_key_list(self, setting_name, device=None): setting, val_class, kind, keys = self._setting_attributes(setting_name, device) multiple = kind in self.MULTIPLE self.key_field.set_visible(multiple) self.key_lbl.set_visible(multiple) if not multiple: return labels = getattr(setting, "_labels", {}) def item(k): lbl = labels.get(k, None) if lbl and isinstance(lbl, tuple) and lbl[0]: label = lbl[0] else: label = str(k) return k, label with self.ignore_changes(): self.key_field.set_all_values(sorted(map(item, keys), key=lambda k: k[1])) ds = device.settings if device else {} device_setting = ds.get(setting_name, None) supported_keys = None if device_setting: val = device_setting._validator if device_setting.kind == _SKIND.multiple_toggle: supported_keys = val.get_options() or None elif device_setting.kind == _SKIND.map_choice: choices = val.choices or None supported_keys = choices.keys() if choices else None elif device_setting.kind == _SKIND.multiple_range: supported_keys = val.keys self.key_field.show_only(supported_keys, include_new=True) self._update_validation() def _update_value_list(self, setting_name, device=None, key=None): setting, val_class, kind, keys = self._setting_attributes(setting_name, device) ds = device.settings if device else {} device_setting = ds.get(setting_name, None) if kind in (_SKIND.toggle, _SKIND.multiple_toggle): self.value_field.make_toggle() elif kind in (_SKIND.choice, _SKIND.map_choice): all_values, extra = self._all_choices(device_setting or setting_name) self.value_field.make_choice(all_values, extra) supported_values = None if device_setting: val = device_setting._validator choices = getattr(val, "choices", None) or None if kind == _SKIND.choice: supported_values = choices elif kind == _SKIND.map_choice and isinstance(choices, dict): supported_values = choices.get(key, None) or None self.value_field.choice_widget.show_only(supported_values, include_new=True) self._update_validation() elif kind == _SKIND.range: self.value_field.make_range(val_class.min_value, val_class.max_value) elif kind == _SKIND.multiple_range: self.value_field.make_range_with_key( getattr(setting, "sub_items_universe", {}).get(key, {}) if setting else {}, getattr(setting, "_labels_sub", None) if setting else None, ) else: self.value_field.make_unsupported() def _on_update(self, *_args): if not self._ignore_changes and self.component: self._update_validation() def _update_validation(self): device_str = self.device_field.get_value() device = _all_devices[device_str] if device_str and not device: icon = ( "dialog-question" if len(device_str) == 8 and all(c in string.hexdigits for c in device_str) else "dialog-warning" ) else: icon = "" self.device_field.get_child().set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, icon) setting_name = self.setting_field.get_value() setting, val_class, kind, keys = self._setting_attributes(setting_name, device) multiple = kind in self.MULTIPLE if multiple: key = self.key_field.get_value(invalid_as_str=False, accept_hidden=False) icon = "dialog-warning" if key is None else "" self.key_field.get_child().set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, icon) if kind in (_SKIND.choice, _SKIND.map_choice): value = self.value_field.choice_widget.get_value(invalid_as_str=False, accept_hidden=False) icon = "dialog-warning" if value is None else "" self.value_field.choice_widget.get_child().set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, icon) def show(self, component, editable): a = iter(component.args) with self.ignore_changes(): device_str = next(a, None) same = not device_str device = _all_devices[device_str] self.device_field.set_value(device.id if device else "" if same else device_str or "") setting_name = next(a, "") setting, _v, kind, keys = self._setting_attributes(setting_name, device) self.setting_field.set_value(setting.name if setting else "") self._changed_setting() key = None if kind in self.MULTIPLE or kind is None and len(self.component.args) > 3: key = _from_named_ints(next(a, ""), keys) self.key_field.set_value(key) self.value_field.set_value(next(a, "")) self._update_validation() def collect_value(self): device_str = self.device_field.get_value() same = device_str in ["", _("Originating device")] device = None if same else _all_devices[device_str] device_value = device.id if device else None if same else device_str setting_name = self.setting_field.get_value() setting, val_class, kind, keys = self._setting_attributes(setting_name, device) key_value = [] if kind in self.MULTIPLE or kind is None and len(self.component.args) > 3: key = self.key_field.get_value() key = _from_named_ints(key, keys) key_value.append(key) key_value.append(self.value_field.get_value()) return [device_value, setting_name, *key_value] @classmethod def right_label(cls, component): a = iter(component.args) device_str = next(a, None) device = None if not device_str else _all_devices[device_str] device_disp = _("Originating device") if not device_str else device.display_name if device else shlex_quote(device_str) setting_name = next(a, None) setting, val_class, kind, keys = cls._setting_attributes(setting_name, device) device_setting = (device.settings if device else {}).get(setting_name, None) disp = [setting.label or setting.name if setting else setting_name] key = None if kind in cls.MULTIPLE: key = next(a, None) key = _from_named_ints(key, keys) if keys else key key_label = getattr(setting, "_labels", {}).get(key, [None])[0] if setting else None disp.append(key_label or key) value = next(a, None) if setting and (kind in (_SKIND.choice, _SKIND.map_choice)): all_values = cls._all_choices(setting or setting_name)[0] supported_values = None if device_setting: val = device_setting._validator choices = getattr(val, "choices", None) or None if kind == _SKIND.choice: supported_values = choices elif kind == _SKIND.map_choice and isinstance(choices, dict): supported_values = choices.get(key, None) or None if supported_values and isinstance(supported_values, NamedInts): value = supported_values[value] if not supported_values and all_values and isinstance(all_values, NamedInts): value = all_values[value] disp.append(value) elif kind == _SKIND.multiple_range and isinstance(value, dict) and len(value) == 1: k, v = next(iter(value.items())) k = (getattr(setting, "_labels_sub", {}).get(k, (None,))[0] if setting else None) or k disp.append(f"{k}={v}") elif kind in (_SKIND.toggle, _SKIND.multiple_toggle): disp.append(_(str(value))) else: disp.append(value) return device_disp + " " + " ".join(map(lambda s: shlex_quote(str(s)), [*disp, *a])) class SetUI(_SettingWithValueUI, ActionUI): CLASS = _DIV.Set ACCEPT_TOGGLE = True label_text = _("Change setting on device") def show(self, component, editable): ActionUI.show(self, component, editable) _SettingWithValueUI.show(self, component, editable) def _on_update(self, *_args): if not self._ignore_changes and self.component: ActionUI._on_update(self, *_args) _SettingWithValueUI._on_update(self, *_args) class SettingUI(_SettingWithValueUI, ConditionUI): CLASS = _DIV.Setting ACCEPT_TOGGLE = False label_text = _("Setting on device") def show(self, component, editable): ConditionUI.show(self, component, editable) _SettingWithValueUI.show(self, component, editable) def _on_update(self, *_args): if not self._ignore_changes and self.component: ConditionUI._on_update(self, *_args) _SettingWithValueUI._on_update(self, *_args) COMPONENT_UI = { _DIV.Rule: RuleUI, _DIV.Not: NotUI, _DIV.Or: OrUI, _DIV.And: AndUI, _DIV.Later: LaterUI, _DIV.Process: rule_conditions.ProcessUI, _DIV.MouseProcess: rule_conditions.MouseProcessUI, _DIV.Active: ActiveUI, _DIV.Device: DeviceUI, _DIV.Host: HostUI, _DIV.Feature: rule_conditions.FeatureUI, _DIV.Report: rule_conditions.ReportUI, _DIV.Modifiers: rule_conditions.ModifiersUI, _DIV.Key: rule_conditions.KeyUI, _DIV.KeyIsDown: rule_conditions.KeyIsDownUI, _DIV.Test: rule_conditions.TestUI, _DIV.TestBytes: rule_conditions.TestBytesUI, _DIV.Setting: SettingUI, _DIV.MouseGesture: rule_conditions.MouseGestureUI, _DIV.KeyPress: rule_actions.KeyPressUI, _DIV.MouseScroll: rule_actions.MouseScrollUI, _DIV.MouseClick: rule_actions.MouseClickUI, _DIV.Execute: rule_actions.ExecuteUI, _DIV.Set: SetUI, type(None): RuleComponentUI, # placeholders for empty rule/And/Or } _all_devices = AllDevicesInfo() _dev_model = None def update_devices(): global _dev_model global _all_devices global _diversion_dialog if _dev_model and _all_devices.refresh() and _diversion_dialog: _diversion_dialog.update_devices() def show_window(model: Gtk.TreeStore): GObject.type_register(RuleComponentWrapper) global _diversion_dialog global _dev_model _dev_model = model if _diversion_dialog is None: _diversion_dialog = DiversionDialog(ActionMenu) update_devices() _diversion_dialog.window.present() Solaar-1.1.14/lib/solaar/ui/icons.py000066400000000000000000000113011473525213700171550ustar00rootroot00000000000000## Copyright (C) 2012-2013 Daniel Pavel ## Copyright (C) 2014-2024 Solaar Contributors https://pwr-solaar.github.io/Solaar/ ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import logging from gi.repository import Gtk import solaar.gtk as gtk logger = logging.getLogger(__name__) LARGE_SIZE = Gtk.IconSize.DIALOG # was 64 TRAY_INIT = "solaar-init" TRAY_OKAY = "solaar" TRAY_ATTENTION = "solaar-attention" _default_theme = None def _init_icon_paths(): global _default_theme if _default_theme: return _default_theme = Gtk.IconTheme.get_default() if logger.isEnabledFor(logging.DEBUG): logger.debug("icon theme paths: %s", _default_theme.get_search_path()) if gtk.battery_icons_style == "symbolic": global TRAY_OKAY TRAY_OKAY = TRAY_INIT # use monochrome tray icon if not _default_theme.has_icon("battery-good-symbolic"): logger.warning("failed to detect symbolic icons") gtk.battery_icons_style = "regular" if gtk.battery_icons_style == "regular": if not _default_theme.has_icon("battery-good"): logger.warning("failed to detect icons") gtk.battery_icons_style = "solaar" def battery(level=None, charging=False): icon_name = _battery_icon_name(level, charging) if not _default_theme.has_icon(icon_name): logger.warning("icon %s not found in current theme", icon_name) return TRAY_OKAY # use Solaar icon if battery icon not available elif logger.isEnabledFor(logging.DEBUG): logger.debug("battery icon for %s:%s = %s", level, charging, icon_name) return icon_name # return first res where val >= guard # _first_res(val,((guard,res),...)) def _first_res(val, pairs): return next((res for guard, res in pairs if val >= guard), None) def _battery_icon_name(level, charging): _init_icon_paths() if level is None or level < 0: return "battery-missing" + ("-symbolic" if gtk.battery_icons_style == "symbolic" else "") level_name = _first_res(level, ((90, "full"), (30, "good"), (20, "low"), (5, "caution"), (0, "empty"))) return "battery-%s%s%s" % ( level_name, "-charging" if charging else "", "-symbolic" if gtk.battery_icons_style == "symbolic" else "", ) def lux(level=None): if level is None or level < 0: return "light_unknown" return f"solaar-light_{int(20 * ((level + 50) // 100)):03}" _ICON_SETS = {} def device_icon_set(name="_", kind=None): icon_set = _ICON_SETS.get(name) if icon_set is None: # names of possible icons, in reverse desirability icon_set = ["preferences-desktop-peripherals"] if kind: if str(kind) == "numpad": icon_set += ("input-keyboard", "input-dialpad") elif str(kind) == "touchpad": icon_set += ("input-mouse", "input-tablet") elif str(kind) == "trackball": icon_set += ("input-mouse",) elif str(kind) == "headset": icon_set += ("audio-headphones", "audio-headset") icon_set += ("input-" + str(kind),) # icon_set += (name.replace(' ', '-'),) _ICON_SETS[name] = icon_set return icon_set def device_icon_file(name, kind=None, size=LARGE_SIZE): icon_name = device_icon_name(name, kind) return _default_theme.lookup_icon(icon_name, size, 0).get_filename() if icon_name is not None else None def device_icon_name(name, kind=None): _init_icon_paths() icon_set = device_icon_set(name, kind) assert icon_set for n in reversed(icon_set): if _default_theme.has_icon(n): return n def icon_file(name, size=LARGE_SIZE): _init_icon_paths() # has_icon() somehow returned False while lookup_icon returns non-None. # I guess it happens because share/solaar/icons/ has no hicolor and resolution subdirs theme_icon = _default_theme.lookup_icon(name, size, 0) if theme_icon: file_name = theme_icon.get_filename() return file_name logger.warning("icon %s(%d) not found in current theme", name, size) Solaar-1.1.14/lib/solaar/ui/pair_window.py000066400000000000000000000260011473525213700203670ustar00rootroot00000000000000## Copyright (C) 2012-2013 Daniel Pavel ## Copyright (C) 2014-2024 Solaar Contributors https://pwr-solaar.github.io/Solaar/ ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import logging from gi.repository import GLib from gi.repository import Gtk from logitech_receiver import hidpp10_constants from solaar.i18n import _ from solaar.i18n import ngettext from . import icons logger = logging.getLogger(__name__) _PAIRING_TIMEOUT = 30 # seconds _STATUS_CHECK = 500 # milliseconds def create(receiver): receiver.reset_pairing() # clear out any information on previous pairing title = _("%(receiver_name)s: pair new device") % {"receiver_name": receiver.name} if receiver.receiver_kind == "bolt": text = _("Bolt receivers are only compatible with Bolt devices.") text += "\n\n" text += _("Press a pairing button or key until the pairing light flashes quickly.") else: if receiver.receiver_kind == "unifying": text = _("Unifying receivers are only compatible with Unifying devices.") else: text = _("Other receivers are only compatible with a few devices.") text += "\n\n" text += _("For most devices, turn on the device you want to pair.") text += _("If the device is already turned on, turn it off and on again.") text += "\n" text += _("The device must not be paired with a nearby powered-on receiver.") text += "\n" text += _( "For devices with multiple channels, " "press, hold, and release the button for the channel you wish to pair" "\n" "or use the channel switch button to select a channel " "and then press, hold, and release the channel switch button." ) text += "\n" text += _("The channel indicator light should be blinking rapidly.") if receiver.remaining_pairings() and receiver.remaining_pairings() >= 0: text += ( ngettext( "\n\nThis receiver has %d pairing remaining.", "\n\nThis receiver has %d pairings remaining.", receiver.remaining_pairings(), ) % receiver.remaining_pairings() ) text += _("\nCancelling at this point will not use up a pairing.") ok = prepare(receiver) assistant = _create_assistant(receiver, ok, _finish, title, text) if ok: GLib.timeout_add(_STATUS_CHECK, check_lock_state, assistant, receiver) return assistant def prepare(receiver): if receiver.receiver_kind == "bolt": if receiver.discover(timeout=_PAIRING_TIMEOUT): return True else: receiver.pairing.error = "discovery did not start" return False elif receiver.set_lock(False, timeout=_PAIRING_TIMEOUT): return True else: receiver.pairing.error = "the pairing lock did not open" return False def check_lock_state(assistant, receiver, count=2): if not assistant.is_drawable(): if logger.isEnabledFor(logging.DEBUG): logger.debug("assistant %s destroyed, bailing out", assistant) return False return _check_lock_state(assistant, receiver, count) def _check_lock_state(assistant, receiver, count): if receiver.pairing.error: _pairing_failed(assistant, receiver, receiver.pairing.error) return False elif receiver.pairing.new_device: receiver.remaining_pairings(False) # Update remaining pairings _pairing_succeeded(assistant, receiver, receiver.pairing.new_device) return False elif not receiver.pairing.lock_open and not receiver.pairing.discovering: if count > 0: # the actual device notification may arrive later so have a little patience GLib.timeout_add(_STATUS_CHECK, check_lock_state, assistant, receiver, count - 1) else: _pairing_failed(assistant, receiver, "failed to open pairing lock") return False elif receiver.pairing.lock_open and receiver.pairing.device_passkey: _show_passcode(assistant, receiver, receiver.pairing.device_passkey) return True elif receiver.pairing.discovering and receiver.pairing.device_address and receiver.pairing.device_name: add = receiver.pairing.device_address ent = 20 if receiver.pairing.device_kind == hidpp10_constants.DEVICE_KIND.keyboard else 10 if receiver.pair_device(address=add, authentication=receiver.pairing.device_authentication, entropy=ent): return True else: _pairing_failed(assistant, receiver, "failed to open pairing lock") return False return True def _pairing_failed(assistant, receiver, error): assistant.remove_page(0) # needed to reset the window size if logger.isEnabledFor(logging.DEBUG): logger.debug("%s fail: %s", receiver, error) _create_failure_page(assistant, error) def _pairing_succeeded(assistant, receiver, device): assistant.remove_page(0) # needed to reset the window size if logger.isEnabledFor(logging.DEBUG): logger.debug("%s success: %s", receiver, device) _create_success_page(assistant, device) def _finish(assistant, receiver): if logger.isEnabledFor(logging.DEBUG): logger.debug("finish %s", assistant) assistant.destroy() receiver.pairing.new_device = None if receiver.pairing.lock_open: if receiver.receiver_kind == "bolt": receiver.pair_device("cancel") else: receiver.set_lock() if receiver.pairing.discovering: receiver.discover(True) if not receiver.pairing.lock_open and not receiver.pairing.discovering: receiver.pairing.error = None def _show_passcode(assistant, receiver, passkey): if logger.isEnabledFor(logging.DEBUG): logger.debug("%s show passkey: %s", receiver, passkey) name = receiver.pairing.device_name authentication = receiver.pairing.device_authentication intro_text = _("%(receiver_name)s: pair new device") % {"receiver_name": receiver.name} page_text = _("Enter passcode on %(name)s.") % {"name": name} page_text += "\n" if authentication & 0x01: page_text += _("Type %(passcode)s and then press the enter key.") % { "passcode": receiver.pairing.device_passkey, } else: passcode = ", ".join( [_("right") if bit == "1" else _("left") for bit in f"{int(receiver.pairing.device_passkey):010b}"] ) page_text += _("Press %(code)s\nand then press left and right buttons simultaneously.") % {"code": passcode} page = _create_page( assistant, Gtk.AssistantPageType.PROGRESS, intro_text, "preferences-desktop-peripherals", page_text, ) assistant.set_page_complete(page, True) assistant.next_page() def _create_assistant(receiver, ok, finish, title, text): assistant = Gtk.Assistant() assistant.set_title(title) assistant.set_icon_name("list-add") assistant.set_size_request(400, 240) assistant.set_resizable(False) assistant.set_role("pair-device") if ok: page_intro = _create_page( assistant, Gtk.AssistantPageType.PROGRESS, title, "preferences-desktop-peripherals", text, ) spinner = Gtk.Spinner() spinner.set_visible(True) spinner.start() page_intro.pack_end(spinner, True, True, 24) assistant.set_page_complete(page_intro, True) else: page_intro = _create_failure_page(assistant, receiver.pairing.error) assistant.connect("cancel", finish, receiver) assistant.connect("close", finish, receiver) return assistant def _create_success_page(assistant, device): def _check_encrypted(device, assistant, hbox): if assistant.is_drawable() and device.link_encrypted is False: hbox.pack_start(Gtk.Image.new_from_icon_name("security-low", Gtk.IconSize.MENU), False, False, 0) hbox.pack_start(Gtk.Label(label=_("The wireless link is not encrypted")), False, False, 0) hbox.show_all() return False page = _create_page(assistant, Gtk.AssistantPageType.SUMMARY) header = Gtk.Label(label=_("Found a new device:")) page.pack_start(header, False, False, 0) device_icon = Gtk.Image() icon_name = icons.device_icon_name(device.name, device.kind) device_icon.set_from_icon_name(icon_name, icons.LARGE_SIZE) page.pack_start(device_icon, True, True, 0) device_label = Gtk.Label() device_label.set_markup(f"{device.name}") page.pack_start(device_label, True, True, 0) hbox = Gtk.HBox(homogeneous=False, spacing=8) hbox.pack_start(Gtk.Label(label=" "), False, False, 0) hbox.set_property("expand", False) hbox.set_property("halign", Gtk.Align.CENTER) page.pack_start(hbox, False, False, 0) GLib.timeout_add(_STATUS_CHECK, _check_encrypted, device, assistant, hbox) # wait a bit to check link status page.show_all() assistant.next_page() assistant.commit() def _create_failure_page(assistant, error) -> None: header = _("Pairing failed") + ": " + _(str(error)) + "." if "timeout" in str(error): text = _("Make sure your device is within range, and has a decent battery charge.") elif str(error) == "device not supported": text = _("A new device was detected, but it is not compatible with this receiver.") elif "many" in str(error): text = _("More paired devices than receiver can support.") else: text = _("No further details are available about the error.") _create_page(assistant, Gtk.AssistantPageType.SUMMARY, header, "dialog-error", text) assistant.next_page() assistant.commit() def _create_page(assistant, kind, header=None, icon_name=None, text=None) -> Gtk.VBox: p = Gtk.VBox(homogeneous=False, spacing=8) assistant.append_page(p) assistant.set_page_type(p, kind) if header: item = Gtk.HBox(homogeneous=False, spacing=16) p.pack_start(item, False, True, 0) label = Gtk.Label(label=header) label.set_line_wrap(True) item.pack_start(label, True, True, 0) if icon_name: icon = Gtk.Image.new_from_icon_name(icon_name, Gtk.IconSize.DIALOG) item.pack_start(icon, False, False, 0) if text: label = Gtk.Label(label=text) label.set_line_wrap(True) p.pack_start(label, False, False, 0) p.show_all() return p Solaar-1.1.14/lib/solaar/ui/rule_actions.py000066400000000000000000000314471473525213700205460ustar00rootroot00000000000000## Copyright (C) Solaar Contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. from shlex import quote as shlex_quote from gi.repository import Gtk from logitech_receiver import diversion from logitech_receiver.diversion import CLICK from logitech_receiver.diversion import DEPRESS from logitech_receiver.diversion import RELEASE from logitech_receiver.diversion import XK_KEYS from logitech_receiver.diversion import buttons from solaar.i18n import _ from solaar.ui.rule_base import CompletionEntry from solaar.ui.rule_base import RuleComponentUI class ActionUI(RuleComponentUI): CLASS = diversion.Action @classmethod def icon_name(cls): return "go-next" class KeyPressUI(ActionUI): CLASS = diversion.KeyPress KEY_NAMES = [k[3:] if k.startswith("XK_") else k for k, v in XK_KEYS.items() if isinstance(v, int)] def create_widgets(self): self.widgets = {} self.fields = [] self.label = Gtk.Label( label=_("Simulate a chorded key click or depress or release.\nOn Wayland requires write access to /dev/uinput."), halign=Gtk.Align.CENTER, ) self.widgets[self.label] = (0, 0, 5, 1) self.del_btns = [] self.add_btn = Gtk.Button(label=_("Add key"), halign=Gtk.Align.CENTER, valign=Gtk.Align.END, hexpand=True) self.add_btn.connect("clicked", self._clicked_add) self.widgets[self.add_btn] = (1, 1, 1, 1) self.action_clicked_radio = Gtk.RadioButton.new_with_label_from_widget(None, _("Click")) self.action_clicked_radio.connect("toggled", self._on_update, CLICK) self.widgets[self.action_clicked_radio] = (0, 3, 1, 1) self.action_pressed_radio = Gtk.RadioButton.new_with_label_from_widget(self.action_clicked_radio, _("Depress")) self.action_pressed_radio.connect("toggled", self._on_update, DEPRESS) self.widgets[self.action_pressed_radio] = (1, 3, 1, 1) self.action_released_radio = Gtk.RadioButton.new_with_label_from_widget(self.action_pressed_radio, _("Release")) self.action_released_radio.connect("toggled", self._on_update, RELEASE) self.widgets[self.action_released_radio] = (2, 3, 1, 1) def _create_field(self): field_entry = CompletionEntry(self.KEY_NAMES, halign=Gtk.Align.CENTER, valign=Gtk.Align.END, hexpand=True) field_entry.connect("changed", self._on_update) self.fields.append(field_entry) self.widgets[field_entry] = (len(self.fields) - 1, 1, 1, 1) return field_entry def _create_del_btn(self): btn = Gtk.Button(label=_("Delete"), halign=Gtk.Align.CENTER, valign=Gtk.Align.START, hexpand=True) self.del_btns.append(btn) self.widgets[btn] = (len(self.del_btns) - 1, 2, 1, 1) btn.connect("clicked", self._clicked_del, len(self.del_btns) - 1) return btn def _clicked_add(self, _btn): keys, action = self.component.regularize_args(self.collect_value()) self.component.__init__([keys + [""], action], warn=False) self.show(self.component, editable=True) self.fields[len(self.component.key_names) - 1].grab_focus() def _clicked_del(self, _btn, pos): keys, action = self.component.regularize_args(self.collect_value()) keys.pop(pos) self.component.__init__([keys, action], warn=False) self.show(self.component, editable=True) self._on_update_callback() def _on_update(self, *args): super()._on_update(*args) for i, f in enumerate(self.fields): if f.get_visible(): icon = ( "dialog-warning" if i < len(self.component.key_names) and self.component.key_names[i] not in self.KEY_NAMES else "" ) f.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, icon) def show(self, component, editable=True): n = len(component.key_names) while len(self.fields) < n: self._create_field() self._create_del_btn() self.widgets[self.add_btn] = (n, 1, 1, 1) super().show(component, editable) for i in range(n): field_entry = self.fields[i] with self.ignore_changes(): field_entry.set_text(component.key_names[i]) field_entry.set_size_request(int(0.3 * self.panel.get_toplevel().get_size()[0]), 0) field_entry.show_all() self.del_btns[i].show() for i in range(n, len(self.fields)): self.fields[i].hide() self.del_btns[i].hide() def collect_value(self): action = ( CLICK if self.action_clicked_radio.get_active() else DEPRESS if self.action_pressed_radio.get_active() else RELEASE ) return [[f.get_text().strip() for f in self.fields if f.get_visible()], action] @classmethod def left_label(cls, component): return _("Key press") @classmethod def right_label(cls, component): return " + ".join(component.key_names) + (" (" + component.action + ")" if component.action != CLICK else "") class MouseScrollUI(ActionUI): CLASS = diversion.MouseScroll MIN_VALUE = -2000 MAX_VALUE = 2000 def create_widgets(self): self.widgets = {} self.label = Gtk.Label( label=_("Simulate a mouse scroll.\nOn Wayland requires write access to /dev/uinput."), halign=Gtk.Align.CENTER ) self.widgets[self.label] = (0, 0, 4, 1) self.label_x = Gtk.Label(label="x", halign=Gtk.Align.END, valign=Gtk.Align.END, hexpand=True) self.label_y = Gtk.Label(label="y", halign=Gtk.Align.END, valign=Gtk.Align.END, hexpand=True) self.field_x = Gtk.SpinButton.new_with_range(self.MIN_VALUE, self.MAX_VALUE, 1) self.field_y = Gtk.SpinButton.new_with_range(self.MIN_VALUE, self.MAX_VALUE, 1) for f in [self.field_x, self.field_y]: f.set_halign(Gtk.Align.CENTER) f.set_valign(Gtk.Align.START) self.field_x.connect("changed", self._on_update) self.field_y.connect("changed", self._on_update) self.widgets[self.label_x] = (0, 1, 1, 1) self.widgets[self.field_x] = (1, 1, 1, 1) self.widgets[self.label_y] = (2, 1, 1, 1) self.widgets[self.field_y] = (3, 1, 1, 1) @classmethod def __parse(cls, v): try: # allow floats, but round them down return int(float(v)) except (TypeError, ValueError): return 0 def show(self, component, editable=True): super().show(component, editable) with self.ignore_changes(): self.field_x.set_value(self.__parse(component.amounts[0] if len(component.amounts) >= 1 else 0)) self.field_y.set_value(self.__parse(component.amounts[1] if len(component.amounts) >= 2 else 0)) def collect_value(self): return [int(self.field_x.get_value()), int(self.field_y.get_value())] @classmethod def left_label(cls, component): return _("Mouse scroll") @classmethod def right_label(cls, component): x = y = 0 x = cls.__parse(component.amounts[0] if len(component.amounts) >= 1 else 0) y = cls.__parse(component.amounts[1] if len(component.amounts) >= 2 else 0) return f"{x}, {y}" class MouseClickUI(ActionUI): CLASS = diversion.MouseClick MIN_VALUE = 1 MAX_VALUE = 9 BUTTONS = list(buttons.keys()) ACTIONS = [CLICK, DEPRESS, RELEASE] def create_widgets(self): self.widgets = {} self.label = Gtk.Label( label=_("Simulate a mouse click.\nOn Wayland requires write access to /dev/uinput."), halign=Gtk.Align.CENTER ) self.widgets[self.label] = (0, 0, 4, 1) self.label_b = Gtk.Label(label=_("Button"), halign=Gtk.Align.END, valign=Gtk.Align.CENTER, hexpand=True) self.label_c = Gtk.Label(label=_("Count and Action"), halign=Gtk.Align.END, valign=Gtk.Align.CENTER, hexpand=True) self.field_b = CompletionEntry(self.BUTTONS) self.field_c = Gtk.SpinButton.new_with_range(self.MIN_VALUE, self.MAX_VALUE, 1) self.field_d = CompletionEntry(self.ACTIONS) for f in [self.field_b, self.field_c]: f.set_halign(Gtk.Align.CENTER) f.set_valign(Gtk.Align.START) self.field_b.connect("changed", self._on_update) self.field_c.connect("changed", self._on_update) self.field_d.connect("changed", self._on_update) self.widgets[self.label_b] = (0, 1, 1, 1) self.widgets[self.field_b] = (1, 1, 1, 1) self.widgets[self.label_c] = (2, 1, 1, 1) self.widgets[self.field_c] = (3, 1, 1, 1) self.widgets[self.field_d] = (4, 1, 1, 1) def show(self, component, editable=True): super().show(component, editable) with self.ignore_changes(): self.field_b.set_text(component.button) if isinstance(component.count, int): self.field_c.set_value(component.count) self.field_d.set_text(CLICK) else: self.field_c.set_value(1) self.field_d.set_text(component.count) def collect_value(self): b, c, d = self.field_b.get_text(), int(self.field_c.get_value()), self.field_d.get_text() if b not in self.BUTTONS: b = "unknown" if d != CLICK: c = d return [b, c] @classmethod def left_label(cls, component): return _("Mouse click") @classmethod def right_label(cls, component): return f'{component.button} ({"x" if isinstance(component.count, int) else ""}{component.count})' class ExecuteUI(ActionUI): CLASS = diversion.Execute def create_widgets(self): self.widgets = {} self.label = Gtk.Label(label=_("Execute a command with arguments."), halign=Gtk.Align.CENTER) self.widgets[self.label] = (0, 0, 5, 1) self.fields = [] self.add_btn = Gtk.Button(label=_("Add argument"), halign=Gtk.Align.CENTER, valign=Gtk.Align.END, hexpand=True) self.del_btns = [] self.add_btn.connect("clicked", self._clicked_add) self.widgets[self.add_btn] = (1, 1, 1, 1) def _create_field(self): field_entry = Gtk.Entry(halign=Gtk.Align.CENTER, valign=Gtk.Align.END, hexpand=True) field_entry.set_size_request(150, 0) field_entry.connect("changed", self._on_update) self.fields.append(field_entry) self.widgets[field_entry] = (len(self.fields) - 1, 1, 1, 1) return field_entry def _create_del_btn(self): btn = Gtk.Button(label=_("Delete"), halign=Gtk.Align.CENTER, valign=Gtk.Align.START, hexpand=True) btn.set_size_request(150, 0) self.del_btns.append(btn) self.widgets[btn] = (len(self.del_btns) - 1, 2, 1, 1) btn.connect("clicked", self._clicked_del, len(self.del_btns) - 1) return btn def _clicked_add(self, *_args): self.component.__init__(self.collect_value() + [""], warn=False) self.show(self.component, editable=True) self.fields[len(self.component.args) - 1].grab_focus() def _clicked_del(self, _btn, pos): v = self.collect_value() v.pop(pos) self.component.__init__(v, warn=False) self.show(self.component, editable=True) self._on_update_callback() def show(self, component, editable=True): n = len(component.args) while len(self.fields) < n: self._create_field() self._create_del_btn() for i in range(n): field_entry = self.fields[i] with self.ignore_changes(): field_entry.set_text(component.args[i]) self.del_btns[i].show() self.widgets[self.add_btn] = (n + 1, 1, 1, 1) super().show(component, editable) for i in range(n, len(self.fields)): self.fields[i].hide() self.del_btns[i].hide() self.add_btn.set_valign(Gtk.Align.END if n >= 1 else Gtk.Align.CENTER) def collect_value(self): return [f.get_text() for f in self.fields if f.get_visible()] @classmethod def left_label(cls, component): return _("Execute") @classmethod def right_label(cls, component): return " ".join([shlex_quote(a) for a in component.args]) Solaar-1.1.14/lib/solaar/ui/rule_base.py000066400000000000000000000066751473525213700200250ustar00rootroot00000000000000## Copyright (C) Solaar Contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. from contextlib import contextmanager as contextlib_contextmanager from typing import Callable from gi.repository import Gtk from logitech_receiver import diversion def norm(s): return s.replace("_", "").replace(" ", "").lower() class CompletionEntry(Gtk.Entry): def __init__(self, values, *args, **kwargs): super().__init__(*args, **kwargs) CompletionEntry.add_completion_to_entry(self, values) @classmethod def add_completion_to_entry(cls, entry, values): completion = entry.get_completion() if not completion: liststore = Gtk.ListStore(str) completion = Gtk.EntryCompletion() completion.set_model(liststore) completion.set_match_func(lambda completion, key, it: norm(key) in norm(completion.get_model()[it][0])) completion.set_text_column(0) entry.set_completion(completion) else: liststore = completion.get_model() liststore.clear() for v in sorted(set(values), key=str.casefold): liststore.append((v,)) class RuleComponentUI: CLASS = diversion.RuleComponent def __init__(self, panel, on_update: Callable = None): self.panel = panel self.widgets = {} # widget -> coord. in grid self.component = None self._ignore_changes = 0 self._on_update_callback = (lambda: None) if on_update is None else on_update self.create_widgets() def create_widgets(self): pass def show(self, component, editable=True): self._show_widgets(editable) self.component = component def collect_value(self): return None @contextlib_contextmanager def ignore_changes(self): self._ignore_changes += 1 yield None self._ignore_changes -= 1 def _on_update(self, *_args): if not self._ignore_changes and self.component is not None: value = self.collect_value() self.component.__init__(value, warn=False) self._on_update_callback() return value return None def _show_widgets(self, editable): self._remove_panel_items() for widget, coord in self.widgets.items(): self.panel.attach(widget, *coord) widget.set_sensitive(editable) widget.show() @classmethod def left_label(cls, component) -> str: return type(component).__name__ @classmethod def right_label(cls, _component) -> str: return "" @classmethod def icon_name(cls) -> str: return "" def _remove_panel_items(self): for c in self.panel.get_children(): self.panel.remove(c) def update_devices(self): pass Solaar-1.1.14/lib/solaar/ui/rule_conditions.py000066400000000000000000000554511473525213700212600ustar00rootroot00000000000000## Copyright (C) Solaar Contributors ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. from dataclasses import dataclass from gi.repository import Gtk from logitech_receiver import diversion from logitech_receiver.diversion import Key from logitech_receiver.hidpp20 import SupportedFeature from logitech_receiver.special_keys import CONTROL from solaar.i18n import _ from solaar.ui.rule_base import CompletionEntry from solaar.ui.rule_base import RuleComponentUI class ConditionUI(RuleComponentUI): CLASS = diversion.Condition @classmethod def icon_name(cls): return "dialog-question" class ProcessUI(ConditionUI): CLASS = diversion.Process def create_widgets(self): self.widgets = {} self.label = Gtk.Label(valign=Gtk.Align.CENTER, hexpand=True) self.label.set_text(_("X11 active process. For use in X11 only.")) self.widgets[self.label] = (0, 0, 1, 1) self.field = Gtk.Entry(halign=Gtk.Align.CENTER, valign=Gtk.Align.CENTER, hexpand=True) self.field.set_size_request(600, 0) self.field.connect("changed", self._on_update) self.widgets[self.field] = (0, 1, 1, 1) def show(self, component, editable=True): super().show(component, editable) with self.ignore_changes(): self.field.set_text(component.process) def collect_value(self): return self.field.get_text() @classmethod def left_label(cls, component): return _("Process") @classmethod def right_label(cls, component): return str(component.process) class MouseProcessUI(ConditionUI): CLASS = diversion.MouseProcess def create_widgets(self): self.widgets = {} self.label = Gtk.Label(valign=Gtk.Align.CENTER, hexpand=True) self.label.set_text(_("X11 mouse process. For use in X11 only.")) self.widgets[self.label] = (0, 0, 1, 1) self.field = Gtk.Entry(halign=Gtk.Align.CENTER, valign=Gtk.Align.CENTER, hexpand=True) self.field.set_size_request(600, 0) self.field.connect("changed", self._on_update) self.widgets[self.field] = (0, 1, 1, 1) def show(self, component, editable=True): super().show(component, editable) with self.ignore_changes(): self.field.set_text(component.process) def collect_value(self): return self.field.get_text() @classmethod def left_label(cls, component): return _("MouseProcess") @classmethod def right_label(cls, component): return str(component.process) class FeatureUI(ConditionUI): CLASS = diversion.Feature FEATURES_WITH_DIVERSION = [ str(SupportedFeature.CROWN), str(SupportedFeature.THUMB_WHEEL), str(SupportedFeature.LOWRES_WHEEL), str(SupportedFeature.HIRES_WHEEL), str(SupportedFeature.GESTURE_2), str(SupportedFeature.REPROG_CONTROLS_V4), str(SupportedFeature.GKEY), str(SupportedFeature.MKEYS), str(SupportedFeature.MR), ] def create_widgets(self): self.widgets = {} self.label = Gtk.Label(valign=Gtk.Align.CENTER, hexpand=True) self.label.set_text(_("Feature name of notification triggering rule processing.")) self.widgets[self.label] = (0, 0, 1, 1) self.field = Gtk.ComboBoxText.new_with_entry() self.field.append("", "") for feature in self.FEATURES_WITH_DIVERSION: self.field.append(feature, feature) self.field.set_valign(Gtk.Align.CENTER) self.field.set_size_request(600, 0) self.field.connect("changed", self._on_update) all_features = [str(f) for f in SupportedFeature] CompletionEntry.add_completion_to_entry(self.field.get_child(), all_features) self.widgets[self.field] = (0, 1, 1, 1) def show(self, component, editable=True): super().show(component, editable) with self.ignore_changes(): f = str(component.feature) if component.feature else "" self.field.set_active_id(f) if f not in self.FEATURES_WITH_DIVERSION: self.field.get_child().set_text(f) def collect_value(self): return (self.field.get_active_text() or "").strip() def _on_update(self, *args): super()._on_update(*args) icon = "dialog-warning" if not self.component.feature else "" self.field.get_child().set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, icon) @classmethod def left_label(cls, component): return _("Feature") @classmethod def right_label(cls, component): return f"{str(component.feature)} ({int(component.feature or 0):04X})" class ReportUI(ConditionUI): CLASS = diversion.Report MIN_VALUE = -1 # for invalid values MAX_VALUE = 15 def create_widgets(self): self.widgets = {} self.label = Gtk.Label(valign=Gtk.Align.CENTER, hexpand=True) self.label.set_text(_("Report number of notification triggering rule processing.")) self.widgets[self.label] = (0, 0, 1, 1) self.field = Gtk.SpinButton.new_with_range(self.MIN_VALUE, self.MAX_VALUE, 1) self.field.set_halign(Gtk.Align.CENTER) self.field.set_valign(Gtk.Align.CENTER) self.field.set_hexpand(True) self.field.connect("changed", self._on_update) self.widgets[self.field] = (0, 1, 1, 1) def show(self, component, editable=True): super().show(component, editable) with self.ignore_changes(): self.field.set_value(component.report) def collect_value(self): return int(self.field.get_value()) @classmethod def left_label(cls, component): return _("Report") @classmethod def right_label(cls, component): return str(component.report) class ModifiersUI(ConditionUI): CLASS = diversion.Modifiers def create_widgets(self): self.widgets = {} self.label = Gtk.Label(valign=Gtk.Align.CENTER, hexpand=True) self.label.set_text(_("Active keyboard modifiers. Not always available in Wayland.")) self.widgets[self.label] = (0, 0, 5, 1) self.labels = {} self.switches = {} for i, m in enumerate(diversion.MODIFIERS): switch = Gtk.Switch(halign=Gtk.Align.CENTER, valign=Gtk.Align.START, hexpand=True) label = Gtk.Label(label=m, halign=Gtk.Align.CENTER, valign=Gtk.Align.END, hexpand=True) self.widgets[label] = (i, 1, 1, 1) self.widgets[switch] = (i, 2, 1, 1) self.labels[m] = label self.switches[m] = switch switch.connect("notify::active", self._on_update) def show(self, component, editable=True): super().show(component, editable) with self.ignore_changes(): for m in diversion.MODIFIERS: self.switches[m].set_active(m in component.modifiers) def collect_value(self): return [m for m, s in self.switches.items() if s.get_active()] @classmethod def left_label(cls, component): return _("Modifiers") @classmethod def right_label(cls, component): return "+".join(component.modifiers) or "None" class KeyUI(ConditionUI): CLASS = diversion.Key KEY_NAMES = map(str, CONTROL) def create_widgets(self): self.widgets = {} self.label = Gtk.Label(valign=Gtk.Align.CENTER, hexpand=True) self.label.set_text( _( "Diverted key or button depressed or released.\n" "Use the Key/Button Diversion and Divert G Keys settings to divert keys and buttons." ) ) self.widgets[self.label] = (0, 0, 5, 1) self.key_field = CompletionEntry(self.KEY_NAMES, halign=Gtk.Align.CENTER, valign=Gtk.Align.CENTER, hexpand=True) self.key_field.set_size_request(600, 0) self.key_field.connect("changed", self._on_update) self.widgets[self.key_field] = (0, 1, 2, 1) self.action_pressed_radio = Gtk.RadioButton.new_with_label_from_widget(None, _("Key down")) self.action_pressed_radio.connect("toggled", self._on_update, Key.DOWN) self.widgets[self.action_pressed_radio] = (2, 1, 1, 1) self.action_released_radio = Gtk.RadioButton.new_with_label_from_widget(self.action_pressed_radio, _("Key up")) self.action_released_radio.connect("toggled", self._on_update, Key.UP) self.widgets[self.action_released_radio] = (3, 1, 1, 1) def show(self, component, editable=True): super().show(component, editable) with self.ignore_changes(): self.key_field.set_text(str(component.key) if self.component.key else "") if not component.action or component.action == Key.DOWN: self.action_pressed_radio.set_active(True) else: self.action_released_radio.set_active(True) def collect_value(self): action = Key.UP if self.action_released_radio.get_active() else Key.DOWN return [self.key_field.get_text(), action] def _on_update(self, *args): super()._on_update(*args) icon = "dialog-warning" if not self.component.key or not self.component.action else "" self.key_field.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, icon) @classmethod def left_label(cls, component): return _("Key") @classmethod def right_label(cls, component): return f"{str(component.key)} ({int(component.key):04X}) ({_(component.action)})" if component.key else "None" class KeyIsDownUI(ConditionUI): CLASS = diversion.KeyIsDown KEY_NAMES = map(str, CONTROL) def create_widgets(self): self.widgets = {} self.label = Gtk.Label(valign=Gtk.Align.CENTER, hexpand=True) self.label.set_text( _( "Diverted key or button is currently down.\n" "Use the Key/Button Diversion and Divert G Keys settings to divert keys and buttons." ) ) self.widgets[self.label] = (0, 0, 5, 1) self.key_field = CompletionEntry(self.KEY_NAMES, halign=Gtk.Align.CENTER, valign=Gtk.Align.CENTER, hexpand=True) self.key_field.set_size_request(600, 0) self.key_field.connect("changed", self._on_update) self.widgets[self.key_field] = (0, 1, 1, 1) def show(self, component, editable=True): super().show(component, editable) with self.ignore_changes(): self.key_field.set_text(str(component.key) if self.component.key else "") def collect_value(self): return self.key_field.get_text() def _on_update(self, *args): super()._on_update(*args) icon = "dialog-warning" if not self.component.key else "" self.key_field.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, icon) @classmethod def left_label(cls, component): return _("KeyIsDown") @classmethod def right_label(cls, component): return f"{str(component.key)} ({int(component.key):04X})" if component.key else "None" class TestUI(ConditionUI): CLASS = diversion.Test def create_widgets(self): self.widgets = {} self.label = Gtk.Label(valign=Gtk.Align.CENTER, hexpand=True) self.label.set_text(_("Test condition on notification triggering rule processing.")) self.widgets[self.label] = (0, 0, 4, 1) lbl = Gtk.Label(label=_("Test"), halign=Gtk.Align.CENTER, valign=Gtk.Align.END, hexpand=False, vexpand=False) self.widgets[lbl] = (0, 1, 1, 1) lbl = Gtk.Label(label=_("Parameter"), halign=Gtk.Align.CENTER, valign=Gtk.Align.END, hexpand=False, vexpand=False) self.widgets[lbl] = (2, 1, 1, 1) self.test = Gtk.ComboBoxText.new_with_entry() self.test.append("", "") for t in diversion.TESTS: self.test.append(t, t) self.test.set_halign(Gtk.Align.END) self.test.set_valign(Gtk.Align.CENTER) self.test.set_hexpand(False) self.test.set_size_request(300, 0) CompletionEntry.add_completion_to_entry(self.test.get_child(), diversion.TESTS) self.test.connect("changed", self._on_update) self.widgets[self.test] = (1, 1, 1, 1) self.parameter = Gtk.Entry(halign=Gtk.Align.CENTER, valign=Gtk.Align.END, hexpand=True) self.parameter.set_size_request(150, 0) self.parameter.connect("changed", self._on_update) self.widgets[self.parameter] = (3, 1, 1, 1) def show(self, component, editable=True): super().show(component, editable) with self.ignore_changes(): self.test.set_active_id(component.test) self.parameter.set_text(str(component.parameter) if component.parameter is not None else "") if component.test not in diversion.TESTS: self.test.get_child().set_text(component.test) self._change_status_icon() def collect_value(self): try: param = int(self.parameter.get_text()) if self.parameter.get_text() else None except Exception: param = self.parameter.get_text() test = (self.test.get_active_text() or "").strip() return [test, param] if param is not None else [test] def _on_update(self, *args): super()._on_update(*args) self._change_status_icon() def _change_status_icon(self): icon = "dialog-warning" if (self.test.get_active_text() or "").strip() not in diversion.TESTS else "" self.test.get_child().set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, icon) @classmethod def left_label(cls, component): return _("Test") @classmethod def right_label(cls, component): return component.test + (" " + repr(component.parameter) if component.parameter is not None else "") @dataclass class TestBytesElement: id: str label: str min: int max: int @dataclass class TestBytesMode: label: str elements: list label_fn: callable class TestBytesUI(ConditionUI): CLASS = diversion.TestBytes _common_elements = [ TestBytesElement("begin", _("begin (inclusive)"), 0, 16), TestBytesElement("end", _("end (exclusive)"), 0, 16), ] _global_min = -(2**31) _global_max = 2**31 - 1 _modes = { "range": TestBytesMode( _("range"), _common_elements + [ TestBytesElement("minimum", _("minimum"), _global_min, _global_max), # uint32 TestBytesElement("maximum", _("maximum"), _global_min, _global_max), ], lambda e: _("bytes %(0)d to %(1)d, ranging from %(2)d to %(3)d" % {str(i): v for i, v in enumerate(e)}), ), "mask": TestBytesMode( _("mask"), _common_elements + [TestBytesElement("mask", _("mask"), _global_min, _global_max)], lambda e: _("bytes %(0)d to %(1)d, mask %(2)d" % {str(i): v for i, v in enumerate(e)}), ), } def create_widgets(self): self.fields = {} self.field_labels = {} self.widgets = {} self.label = Gtk.Label(valign=Gtk.Align.CENTER, hexpand=True) self.label.set_text(_("Bit or range test on bytes in notification message triggering rule processing.")) self.widgets[self.label] = (0, 0, 5, 1) col = 0 mode_col = 2 self.mode_field = Gtk.ComboBox.new_with_model(Gtk.ListStore(str, str)) mode_renderer = Gtk.CellRendererText() self.mode_field.set_id_column(0) self.mode_field.pack_start(mode_renderer, True) self.mode_field.add_attribute(mode_renderer, "text", 1) self.widgets[self.mode_field] = (mode_col, 2, 1, 1) mode_label = Gtk.Label(label=_("type"), margin_top=20) self.widgets[mode_label] = (mode_col, 1, 1, 1) for mode_id, mode in TestBytesUI._modes.items(): self.mode_field.get_model().append([mode_id, mode.label]) for element in mode.elements: if element.id not in self.fields: field = Gtk.SpinButton.new_with_range(element.min, element.max, 1) field.set_value(0) field.set_size_request(150, 0) field.connect("value-changed", self._on_update) label = Gtk.Label(label=element.label, margin_top=20) self.fields[element.id] = field self.field_labels[element.id] = label self.widgets[label] = (col, 1, 1, 1) self.widgets[field] = (col, 2, 1, 1) col += 1 if col != mode_col - 1 else 2 self.mode_field.connect("changed", lambda cb: (self._on_update(), self._only_mode(cb.get_active_id()))) self.mode_field.set_active_id("range") def show(self, component, editable=True): super().show(component, editable) with self.ignore_changes(): mode_id = {3: "mask", 4: "range"}.get(len(component.test), None) self._only_mode(mode_id) if not mode_id: return self.mode_field.set_active_id(mode_id) if mode_id: mode = TestBytesUI._modes[mode_id] for i, element in enumerate(mode.elements): self.fields[element.id].set_value(component.test[i]) def collect_value(self): mode_id = self.mode_field.get_active_id() return [self.fields[element.id].get_value_as_int() for element in TestBytesUI._modes[mode_id].elements] def _only_mode(self, mode_id): if not mode_id: return keep = {element.id for element in TestBytesUI._modes[mode_id].elements} for element_id, f in self.fields.items(): visible = element_id in keep f.set_visible(visible) self.field_labels[element_id].set_visible(visible) def _on_update(self, *args): super()._on_update(*args) if not self.component: return begin, end, *etc = self.component.test icon = "dialog-warning" if end <= begin else "" self.fields["end"].set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, icon) if len(self.component.test) == 4: *etc, minimum, maximum = self.component.test icon = "dialog-warning" if maximum < minimum else "" self.fields["maximum"].set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, icon) @classmethod def left_label(cls, component): return _("Test bytes") @classmethod def right_label(cls, component): mode_id = {3: "mask", 4: "range"}.get(len(component.test), None) if not mode_id: return str(component.test) return TestBytesUI._modes[mode_id].label_fn(component.test) class MouseGestureUI(ConditionUI): CLASS = diversion.MouseGesture MOUSE_GESTURE_NAMES = [ "Mouse Up", "Mouse Down", "Mouse Left", "Mouse Right", "Mouse Up-left", "Mouse Up-right", "Mouse Down-left", "Mouse Down-right", ] MOVE_NAMES = list(map(str, CONTROL)) + MOUSE_GESTURE_NAMES def create_widgets(self): self.widgets = {} self.fields = [] self.label = Gtk.Label( label=_("Mouse gesture with optional initiating button followed by zero or more mouse movements."), halign=Gtk.Align.CENTER, ) self.widgets[self.label] = (0, 0, 5, 1) self.del_btns = [] self.add_btn = Gtk.Button(label=_("Add movement"), halign=Gtk.Align.CENTER, valign=Gtk.Align.END, hexpand=True) self.add_btn.connect("clicked", self._clicked_add) self.widgets[self.add_btn] = (1, 1, 1, 1) def _create_field(self): field = Gtk.ComboBoxText.new_with_entry() for g in self.MOUSE_GESTURE_NAMES: field.append(g, g) CompletionEntry.add_completion_to_entry(field.get_child(), self.MOVE_NAMES) field.connect("changed", self._on_update) self.fields.append(field) self.widgets[field] = (len(self.fields) - 1, 1, 1, 1) return field def _create_del_btn(self): btn = Gtk.Button(label=_("Delete"), halign=Gtk.Align.CENTER, valign=Gtk.Align.START, hexpand=True) self.del_btns.append(btn) self.widgets[btn] = (len(self.del_btns) - 1, 2, 1, 1) btn.connect("clicked", self._clicked_del, len(self.del_btns) - 1) return btn def _clicked_add(self, _btn): self.component.__init__(self.collect_value() + [""], warn=False) self.show(self.component, editable=True) self.fields[len(self.component.movements) - 1].grab_focus() def _clicked_del(self, _btn, pos): v = self.collect_value() v.pop(pos) self.component.__init__(v, warn=False) self.show(self.component, editable=True) self._on_update_callback() def _on_update(self, *args): super()._on_update(*args) for i, f in enumerate(self.fields): if f.get_visible(): icon = ( "dialog-warning" if i < len(self.component.movements) and self.component.movements[i] not in self.MOVE_NAMES else "" ) f.get_child().set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, icon) def show(self, component, editable=True): n = len(component.movements) while len(self.fields) < n: self._create_field() self._create_del_btn() self.widgets[self.add_btn] = (n + 1, 1, 1, 1) super().show(component, editable) for i in range(n): field = self.fields[i] with self.ignore_changes(): field.get_child().set_text(component.movements[i]) field.set_size_request(int(0.3 * self.panel.get_toplevel().get_size()[0]), 0) field.show_all() self.del_btns[i].show() for i in range(n, len(self.fields)): self.fields[i].hide() self.del_btns[i].hide() self.add_btn.set_valign(Gtk.Align.END if n >= 1 else Gtk.Align.CENTER) def collect_value(self): return [f.get_active_text().strip() for f in self.fields if f.get_visible()] @classmethod def left_label(cls, component): return _("Mouse Gesture") @classmethod def right_label(cls, component): if len(component.movements) == 0: return "No-op" else: return " -> ".join(component.movements) Solaar-1.1.14/lib/solaar/ui/tray.py000066400000000000000000000404061473525213700170310ustar00rootroot00000000000000## Copyright (C) 2012-2013 Daniel Pavel ## Copyright (C) 2014-2024 Solaar Contributors https://pwr-solaar.github.io/Solaar/ ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import logging import os from time import time import gi from gi.repository import GLib from gi.repository import Gtk from gi.repository.Gdk import ScrollDirection import solaar.gtk as gtk from solaar import NAME from solaar.i18n import _ from . import action from . import icons from . import window from .about import about logger = logging.getLogger(__name__) _TRAY_ICON_SIZE = 48 _MENU_ICON_SIZE = Gtk.IconSize.LARGE_TOOLBAR def _create_menu(quit_handler): # per-device menu entries will be generated as-needed menu = Gtk.Menu() no_receiver = Gtk.MenuItem.new_with_label(_("No supported device found")) no_receiver.set_sensitive(False) menu.append(no_receiver) menu.append(Gtk.SeparatorMenuItem.new()) menu.append(action.make_image_menu_item(_("About %s") % NAME, "help-about", about.show)) menu.append(action.make_image_menu_item(_("Quit %s") % NAME, "application-exit", quit_handler)) menu.show_all() return menu _last_scroll = 0 def _scroll(tray_icon, event, direction=None): if direction is None: direction = event.direction now = event.time / 1000.0 else: now = None if direction != ScrollDirection.UP and direction != ScrollDirection.DOWN: # ignore all other directions return # don't bother even trying to scroll if less than two devices if sum(map(lambda i: i[1] is not None, _devices_info)) < 2: return # scroll events come way too fast (at least 5-6 at once) so take a little break between them global _last_scroll now = now or time() if now - _last_scroll < 0.33: # seconds return _last_scroll = now global _picked_device candidate = None if _picked_device is None: for info in _devices_info: # pick first peripheral found if info[1] is not None: candidate = info break else: found = False for info in _devices_info: if not info[1]: # only conside peripherals continue # compare peripherals if info[0:2] == _picked_device[0:2]: if direction == ScrollDirection.UP and candidate: # select previous device break found = True else: if found: candidate = info if direction == ScrollDirection.DOWN: break # if direction is up, but no candidate found before _picked, # let it run through all candidates, will get stuck with the last one else: if direction == ScrollDirection.DOWN: # only use the first one, in case no candidates are after _picked if candidate is None: candidate = info else: candidate = info # if the last _picked_device is gone, clear it # the candidate will be either the first or last one remaining, # depending on the scroll direction if not found: _picked_device = None _picked_device = candidate or _picked_device if logger.isEnabledFor(logging.DEBUG): logger.debug("scroll: picked %s", _picked_device) _update_tray_icon() try: try: gi.require_version("AyatanaAppIndicator3", "0.1") from gi.repository import AyatanaAppIndicator3 as AppIndicator3 ayatana_appindicator_found = True except ValueError: try: gi.require_version("AppIndicator3", "0.1") from gi.repository import AppIndicator3 ayatana_appindicator_found = False except ValueError as exc: # treat unavailable versions the same as unavailable packages raise ImportError from exc if logger.isEnabledFor(logging.DEBUG): logger.debug(f"using {'Ayatana ' if ayatana_appindicator_found else ''}AppIndicator3") # Defense against AppIndicator3 bug that treats files in current directory as icon files # https://bugs.launchpad.net/ubuntu/+source/libappindicator/+bug/1363277 # Defense against bug that shows up in XFCE 4.16 where icons are not upscaled def _icon_file(icon_name): if gtk.tray_icon_size is None and not os.path.isfile(icon_name): return icon_name icon_info = Gtk.IconTheme.get_default().lookup_icon( icon_name, gtk.tray_icon_size or _TRAY_ICON_SIZE, Gtk.IconLookupFlags.FORCE_SVG ) return icon_info.get_filename() if icon_info else icon_name def _create(menu): icons._init_icon_paths() ind = AppIndicator3.Indicator.new( "indicator-solaar", _icon_file(icons.TRAY_INIT), AppIndicator3.IndicatorCategory.HARDWARE ) ind.set_title(NAME) ind.set_status(AppIndicator3.IndicatorStatus.ACTIVE) # ind.set_attention_icon_full(_icon_file(icons.TRAY_ATTENTION), '') # works poorly for XFCE 16 # ind.set_label(NAME.lower(), NAME.lower()) ind.set_menu(menu) ind.connect("scroll-event", _scroll) return ind def _hide(indicator): indicator.set_status(AppIndicator3.IndicatorStatus.PASSIVE) def _show(indicator): indicator.set_status(AppIndicator3.IndicatorStatus.ACTIVE) def _update_tray_icon(): if _picked_device and gtk.battery_icons_style != "solaar": _ignore, _ignore, name, device = _picked_device battery_level = device.battery_info.level if device.battery_info is not None else None battery_charging = device.battery_info.charging() if device.battery_info is not None else None tray_icon_name = icons.battery(battery_level, battery_charging) description = f"{name}: {device.status_string()}" else: # there may be a receiver, but no peripherals tray_icon_name = icons.TRAY_OKAY if _devices_info else icons.TRAY_INIT description_lines = _generate_description_lines() description = "\n".join(description_lines).rstrip("\n") # icon_file = icons.icon_file(icon_name, _TRAY_ICON_SIZE) _icon.set_icon_full(_icon_file(tray_icon_name), description) def attention(reason=None): if _icon.get_status() != AppIndicator3.IndicatorStatus.ATTENTION: # _icon.set_attention_icon_full(_icon_file(icons.TRAY_ATTENTION), reason or '') # works poorly for XFCe 16 _icon.set_status(AppIndicator3.IndicatorStatus.ATTENTION) GLib.timeout_add(10 * 1000, _icon.set_status, AppIndicator3.IndicatorStatus.ACTIVE) except ImportError: if logger.isEnabledFor(logging.DEBUG): logger.debug("using StatusIcon") def _create(menu): icon = Gtk.StatusIcon.new_from_icon_name(icons.TRAY_INIT) icon.set_name(NAME.lower()) icon.set_title(NAME) icon.set_tooltip_text(NAME) icon.connect("activate", window.toggle) icon.connect("scroll-event", _scroll) icon.connect( "popup-menu", lambda icon, button, time: menu.popup(None, None, icon.position_menu, icon, button, time), ) return icon def _hide(icon): icon.set_visible(False) def _show(icon): icon.set_visible(True) def _update_tray_icon(): tooltip_lines = _generate_tooltip_lines() tooltip = "\n".join(tooltip_lines).rstrip("\n") _icon.set_tooltip_markup(tooltip) if _picked_device and gtk.battery_icons_style != "solaar": _ignore, _ignore, name, device = _picked_device battery_level = device.battery_info.level if device.battery_info is not None else None battery_charging = device.battery_info.charging() if device.battery_info is not None else None tray_icon_name = icons.battery(battery_level, battery_charging) else: # there may be a receiver, but no peripherals tray_icon_name = icons.TRAY_OKAY if _devices_info else icons.TRAY_ATTENTION _icon.set_from_icon_name(tray_icon_name) _icon_before_attention = None def _blink(count): global _icon_before_attention if count % 2: _icon.set_from_icon_name(icons.TRAY_ATTENTION) else: _icon.set_from_icon_name(_icon_before_attention) if count > 0: GLib.timeout_add(1000, _blink, count - 1) else: _icon_before_attention = None def attention(reason=None): global _icon_before_attention if _icon_before_attention is None: _icon_before_attention = _icon.get_icon_name() GLib.idle_add(_blink, 9) def _generate_tooltip_lines(): if not _devices_info: yield f"{NAME}: " + _("no receiver") return yield from _generate_description_lines() def _generate_description_lines(): if not _devices_info: yield _("no receiver") return for _ignore, number, name, device in _devices_info: if number is None: # receiver continue p = device.status_string() if p: # does it have any properties to print? yield f"{name}" if device.online: yield f"\t{p}" else: yield f"\t{p} (" + _("offline") + ")" else: if device.online: yield f"{name} (" + _("no status") + ")" else: yield f"{name} (" + _("offline") + ")" def _pick_device_with_lowest_battery(): if not _devices_info: return None picked = None picked_level = 1000 for info in _devices_info: if info[1] is None: # is receiver continue level = info[-1].battery_info.level if info[-1].battery_info is not None else None if level is not None and picked_level > level: picked = info picked_level = level or 0 if logger.isEnabledFor(logging.DEBUG): logger.debug("picked device with lowest battery: %s", picked) return picked def _add_device(device): assert device index = 0 receiver_path = device.receiver.path if device.receiver is not None else device.path if device.receiver is not None: # if receiver insert into devices for the receiver in device number order for idx, (path, _ignore, _ignore, _ignore) in enumerate(_devices_info): if path and path == receiver_path: index = idx + 1 # the first entry matching the receiver serial should be for the receiver itself break while index < len(_devices_info): path, number, _ignore, _ignore = _devices_info[index] if not path == receiver_path: break assert number != device.number if number > device.number: break index = index + 1 new_device_info = (receiver_path, device.number, device.name, device) _devices_info.insert(index, new_device_info) label = (" " if device.number else "") + device.name new_menu_item = action.make_image_menu_item(label, None, window.popup, receiver_path, device.number) _menu.insert(new_menu_item, index) return index def _remove_device(index): assert index is not None menu_items = _menu.get_children() _menu.remove(menu_items[index]) removed_device = _devices_info.pop(index) global _picked_device if _picked_device and _picked_device[0:2] == removed_device[0:2]: # the current pick was unpaired _picked_device = None def _add_receiver(receiver): index = len(_devices_info) new_receiver_info = (receiver.path, None, receiver.name, None) _devices_info.insert(index, new_receiver_info) icon_name = icons.device_icon_name(receiver.name, receiver.kind) new_menu_item = action.make_image_menu_item(receiver.name, icon_name, window.popup, receiver.path) _menu.insert(new_menu_item, index) return 0 def _remove_receiver(receiver): index = 0 # remove all entries in devices_info that match this receiver while index < len(_devices_info): path, _ignore, _ignore, _ignore = _devices_info[index] if path == receiver.path: _remove_device(index) else: index += 1 def _update_menu_item(index, device): if device is None: logger.warning("updating an inactive device %s, assuming disconnected", device) return None menu_items = _menu.get_children() menu_item = menu_items[index] level = device.battery_info.level if device.battery_info is not None else None charging = device.battery_info.charging() if device.battery_info is not None else None icon_name = icons.battery(level, charging) menu_item.label.set_label((" " if 0 < device.number <= 6 else "") + device.name + ": " + device.status_string()) image_widget = menu_item.icon image_widget.set_sensitive(bool(device.online)) image_widget.set_from_icon_name(icon_name, _MENU_ICON_SIZE) # for which device to show the battery info in systray, if more than one # it's actually an entry in _devices_info _picked_device = None # cached list of devices and some of their properties # contains tuples of (receiver path, device number, name, device) _devices_info = [] _menu = None _icon = None def init(_quit_handler): global _menu, _icon assert _menu is None _menu = _create_menu(_quit_handler) assert _icon is None _icon = _create(_menu) update() def destroy(): global _icon, _menu, _devices_info if _icon is not None: i, _icon = _icon, None _hide(i) i = None _icon = None _menu = None _devices_info = None def update(device=None): if _icon is None: return if device is not None: if device.kind is None: # receiver is_alive = bool(device) receiver_path = device.path if is_alive: index = None for idx, (path, _ignore, _ignore, _ignore) in enumerate(_devices_info): if path == receiver_path: index = idx break if index is None: _add_receiver(device) else: _remove_receiver(device) else: # peripheral is_paired = bool(device) receiver_path = device.receiver.path if device.receiver is not None else device.path index = None for idx, (path, number, _ignore, _ignore) in enumerate(_devices_info): if path == receiver_path and number == device.number: index = idx if is_paired: if index is None: index = _add_device(device) _update_menu_item(index, device) else: # was just unpaired or unplugged if index is not None: _remove_device(index) menu_items = _menu.get_children() no_receivers_index = len(_devices_info) menu_items[no_receivers_index].set_visible(not _devices_info) global _picked_device if (not _picked_device or _last_scroll == 0) and device is not None and device.kind is not None: # if it's just a receiver update, it's unlikely the picked device would change _picked_device = _pick_device_with_lowest_battery() _update_tray_icon() if _icon: if not _devices_info: _hide(_icon) else: _show(_icon) Solaar-1.1.14/lib/solaar/ui/window.py000066400000000000000000000763301473525213700173660ustar00rootroot00000000000000## Copyright (C) 2012-2013 Daniel Pavel ## Copyright (C) 2014-2024 Solaar Contributors https://pwr-solaar.github.io/Solaar/ ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import logging import gi from gi.repository.GObject import TYPE_PYOBJECT from logitech_receiver import hidpp10_constants from logitech_receiver.common import LOGITECH_VENDOR_ID from logitech_receiver.common import NamedInt from logitech_receiver.common import NamedInts from solaar import NAME from solaar.i18n import _ from solaar.i18n import ngettext from . import action from . import config_panel from . import diversion_rules from . import icons from .about import about from .common import ui_async gi.require_version("Gdk", "3.0") from gi.repository import Gdk # NOQA: E402 from gi.repository import GLib # NOQA: E402 from gi.repository import Gtk # NOQA: E402 logger = logging.getLogger(__name__) _SMALL_BUTTON_ICON_SIZE = Gtk.IconSize.MENU _NORMAL_BUTTON_ICON_SIZE = Gtk.IconSize.BUTTON _TREE_ICON_SIZE = Gtk.IconSize.BUTTON _INFO_ICON_SIZE = Gtk.IconSize.LARGE_TOOLBAR _DEVICE_ICON_SIZE = Gtk.IconSize.DND try: gi.check_version("3.7.4") _CAN_SET_ROW_NONE = None except (ValueError, AttributeError): _CAN_SET_ROW_NONE = "" # tree model columns _COLUMN = NamedInts(PATH=0, NUMBER=1, ACTIVE=2, NAME=3, ICON=4, STATUS_TEXT=5, STATUS_ICON=6, DEVICE=7) _COLUMN_TYPES = (str, int, bool, str, str, str, str, TYPE_PYOBJECT) _TREE_SEPATATOR = (None, 0, False, None, None, None, None, None) assert len(_TREE_SEPATATOR) == len(_COLUMN_TYPES) assert len(_COLUMN_TYPES) == len(_COLUMN) def _new_button(label, icon_name=None, icon_size=_NORMAL_BUTTON_ICON_SIZE, tooltip=None, toggle=False, clicked=None): b = Gtk.ToggleButton() if toggle else Gtk.Button() c = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 5) if icon_name: c.pack_start(Gtk.Image.new_from_icon_name(icon_name, icon_size), True, True, 0) if label: c.pack_start(Gtk.Label(label=label), True, True, 0) b.add(c) if clicked is not None: b.connect("clicked", clicked) if tooltip: b.set_tooltip_text(tooltip) if not label and icon_size < _NORMAL_BUTTON_ICON_SIZE: b.set_relief(Gtk.ReliefStyle.NONE) b.set_focus_on_click(False) return b def _create_receiver_panel(): p = Gtk.Box.new(Gtk.Orientation.VERTICAL, 4) p._count = Gtk.Label() p._count.set_margin_top(24) p.pack_start(p._count, True, True, 0) p._scanning = Gtk.Label(label=_("Scanning") + "...") p._spinner = Gtk.Spinner() bp = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 8) bp.pack_start(Gtk.Label(label=" "), True, True, 0) bp.pack_start(p._scanning, False, False, 0) bp.pack_end(p._spinner, False, False, 0) p.pack_end(bp, False, False, 0) return p def _create_device_panel(): p = Gtk.Box.new(Gtk.Orientation.VERTICAL, 4) def _status_line(label_text): b = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 8) b.set_size_request(10, 28) b._label = Gtk.Label(label=label_text) b._label.set_size_request(170, 10) b.pack_start(b._label, False, False, 0) b._icon = Gtk.Image() b.pack_start(b._icon, False, False, 0) b._text = Gtk.Label() b.pack_start(b._text, False, False, 0) return b p._battery = _status_line(_("Battery")) p.pack_start(p._battery, False, False, 0) p._secure = _status_line(_("Wireless Link")) p._secure._icon.set_from_icon_name("dialog-warning", _INFO_ICON_SIZE) p.pack_start(p._secure, False, False, 0) p._lux = _status_line(_("Lighting")) p.pack_start(p._lux, False, False, 0) p.pack_start(Gtk.Separator.new(Gtk.Orientation.HORIZONTAL), False, False, 0) # spacer p._config = config_panel.create() p.pack_end(p._config, True, True, 4) return p def _create_details_panel(): p = Gtk.Frame() p.set_shadow_type(Gtk.ShadowType.NONE) p.set_size_request(240, 0) p.set_state_flags(Gtk.StateFlags.ACTIVE, True) p._text = Gtk.Label() p._text.set_margin_start(6) p._text.set_margin_end(4) p._text.set_selectable(True) p.add(p._text) return p def _create_buttons_box(): bb = Gtk.HButtonBox() bb.set_layout(Gtk.ButtonBoxStyle.END) bb._details = _new_button( None, "dialog-information", _SMALL_BUTTON_ICON_SIZE, tooltip=_("Show Technical Details"), toggle=True, clicked=_update_details, ) bb.add(bb._details) bb.set_child_secondary(bb._details, True) bb.set_child_non_homogeneous(bb._details, True) def _pair_new_device(trigger): assert _find_selected_device_id() is not None receiver = _find_selected_device() assert receiver is not None assert bool(receiver) assert receiver.kind is None action.pair(_window, receiver) bb._pair = _new_button(_("Pair new device"), "list-add", clicked=_pair_new_device) bb.add(bb._pair) def _unpair_current_device(trigger): assert _find_selected_device_id() is not None device = _find_selected_device() assert device is not None assert device.kind is not None action.unpair(_window, device) bb._unpair = _new_button(_("Unpair"), "edit-delete", clicked=_unpair_current_device) bb.add(bb._unpair) return bb def _create_empty_panel(): p = Gtk.Label() p.set_markup("" + _("Select a device") + "") p.set_sensitive(False) return p def _create_info_panel(): p = Gtk.Box.new(Gtk.Orientation.VERTICAL, 4) p._title = Gtk.Label(label=" ") p._icon = Gtk.Image() b1 = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 4) b1.pack_start(p._title, True, True, 0) b1.pack_start(p._icon, False, False, 0) p.pack_start(b1, False, False, 0) p.pack_start(Gtk.Separator.new(Gtk.Orientation.HORIZONTAL), False, False, 0) # spacer p._receiver = _create_receiver_panel() p.pack_start(p._receiver, True, True, 0) p._device = _create_device_panel() p.pack_start(p._device, True, True, 0) p.pack_start(Gtk.Separator.new(Gtk.Orientation.HORIZONTAL), False, False, 0) # spacer p._buttons = _create_buttons_box() p.pack_end(p._buttons, False, False, 0) return p def _create_tree(model): tree = Gtk.TreeView() tree.set_size_request(330, 0) # enough width for simple setups tree.set_headers_visible(False) tree.set_show_expanders(False) tree.set_level_indentation(20) tree.set_enable_tree_lines(True) tree.set_reorderable(False) tree.set_enable_search(False) tree.set_model(model) def _is_separator(model, item, _ignore=None): return model.get_value(item, _COLUMN.PATH) is None tree.set_row_separator_func(_is_separator, None) icon_cell_renderer = Gtk.CellRendererPixbuf() icon_cell_renderer.set_property("stock-size", _TREE_ICON_SIZE) icon_column = Gtk.TreeViewColumn("Icon", icon_cell_renderer) icon_column.add_attribute(icon_cell_renderer, "sensitive", _COLUMN.ACTIVE) icon_column.add_attribute(icon_cell_renderer, "icon-name", _COLUMN.ICON) tree.append_column(icon_column) name_cell_renderer = Gtk.CellRendererText() name_column = Gtk.TreeViewColumn("device name", name_cell_renderer) name_column.add_attribute(name_cell_renderer, "sensitive", _COLUMN.ACTIVE) name_column.add_attribute(name_cell_renderer, "text", _COLUMN.NAME) name_column.set_expand(True) tree.append_column(name_column) tree.set_expander_column(name_column) status_cell_renderer = Gtk.CellRendererText() status_cell_renderer.set_property("scale", 0.85) status_cell_renderer.set_property("xalign", 1) status_column = Gtk.TreeViewColumn("status text", status_cell_renderer) status_column.add_attribute(status_cell_renderer, "sensitive", _COLUMN.ACTIVE) status_column.add_attribute(status_cell_renderer, "text", _COLUMN.STATUS_TEXT) status_column.set_expand(True) tree.append_column(status_column) battery_cell_renderer = Gtk.CellRendererPixbuf() battery_cell_renderer.set_property("stock-size", _TREE_ICON_SIZE) battery_column = Gtk.TreeViewColumn("status icon", battery_cell_renderer) battery_column.add_attribute(battery_cell_renderer, "sensitive", _COLUMN.ACTIVE) battery_column.add_attribute(battery_cell_renderer, "icon-name", _COLUMN.STATUS_ICON) tree.append_column(battery_column) return tree def _create_window_layout(): assert _tree is not None assert _details is not None assert _info is not None assert _empty is not None assert _tree.get_selection().get_mode() == Gtk.SelectionMode.SINGLE _tree.get_selection().connect("changed", _device_selected) tree_scroll = Gtk.ScrolledWindow() tree_scroll.add(_tree) tree_scroll.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) tree_scroll.set_shadow_type(Gtk.ShadowType.IN) tree_panel = Gtk.Box.new(Gtk.Orientation.VERTICAL, 0) tree_panel.set_homogeneous(False) tree_panel.pack_start(tree_scroll, True, True, 0) tree_panel.pack_start(_details, False, False, 0) panel = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 16) panel.pack_start(tree_panel, False, False, 0) panel.pack_start(_info, True, True, 0) panel.pack_start(_empty, True, True, 0) bottom_buttons_box = Gtk.HButtonBox() bottom_buttons_box.set_layout(Gtk.ButtonBoxStyle.START) bottom_buttons_box.set_spacing(20) quit_button = _new_button(_("Quit %s") % NAME, "application-exit", _SMALL_BUTTON_ICON_SIZE, clicked=destroy) bottom_buttons_box.add(quit_button) about_button = _new_button(_("About %s") % NAME, "help-about", _SMALL_BUTTON_ICON_SIZE, clicked=about.show) bottom_buttons_box.add(about_button) diversion_button = _new_button( _("Rule Editor"), "", _SMALL_BUTTON_ICON_SIZE, clicked=lambda *_trigger: diversion_rules.show_window(_model) ) bottom_buttons_box.add(diversion_button) bottom_buttons_box.set_child_secondary(diversion_button, True) vbox = Gtk.Box.new(Gtk.Orientation.VERTICAL, 8) vbox.set_border_width(8) vbox.pack_start(panel, True, True, 0) vbox.pack_end(bottom_buttons_box, False, False, 0) vbox.show_all() _details.set_visible(False) _info.set_visible(False) return vbox def _create(delete_action): window = Gtk.Window() window.set_title(NAME) window.set_role("status-window") window.connect("delete-event", delete_action) vbox = _create_window_layout() window.add(vbox) geometry = Gdk.Geometry() geometry.min_width = 600 geometry.min_height = 320 window.set_geometry_hints(vbox, geometry, Gdk.WindowHints.MIN_SIZE) window.set_position(Gtk.WindowPosition.CENTER) style = window.get_style_context() style.add_class("solaar") return window def _find_selected_device(): selection = _tree.get_selection() model, item = selection.get_selected() return model.get_value(item, _COLUMN.DEVICE) if item else None def _find_selected_device_id(): selection = _tree.get_selection() model, item = selection.get_selected() if item: return _model.get_value(item, _COLUMN.PATH), _model.get_value(item, _COLUMN.NUMBER) # triggered by changing selection in the tree def _device_selected(selection): model, item = selection.get_selected() device = model.get_value(item, _COLUMN.DEVICE) if item else None if device: _update_info_panel(device, full=True) else: # When removing a receiver, one of its children may get automatically selected # before the tree had time to remove them as well. # Rather than chase around for another device to select, just clear the selection. _tree.get_selection().unselect_all() _update_info_panel(None, full=True) def _receiver_row(receiver_path, receiver=None): assert receiver_path r = _model.get_iter_first() while r: r = _model.iter_next(r) item = _model.get_iter_first() while item: # first row matching the path must be the receiver one if _model.get_value(item, _COLUMN.PATH) == receiver_path: return item item = _model.iter_next(item) if not item and receiver: icon_name = icons.device_icon_name(receiver.name) status_text = None status_icon = None row_data = (receiver_path, 0, True, receiver.name, icon_name, status_text, status_icon, receiver) assert len(row_data) == len(_TREE_SEPATATOR) if logger.isEnabledFor(logging.DEBUG): logger.debug("new receiver row %s", row_data) item = _model.append(None, row_data) if _TREE_SEPATATOR: _model.append(None, _TREE_SEPATATOR) return item or None def _device_row(receiver_path, device_number, device=None): assert receiver_path assert device_number is not None receiver_row = _receiver_row(receiver_path, None if device is None else device.receiver) if device_number == 0xFF or device_number == 0x0: # direct-connected device, receiver row is device row if receiver_row: return receiver_row item = None new_child_index = 0 else: item = _model.iter_children(receiver_row) new_child_index = 0 while item: if _model.get_value(item, _COLUMN.PATH) != receiver_path: logger.warning( "path for device row %s different from path for receiver %s", _model.get_value(item, _COLUMN.PATH), receiver_path, ) item_number = _model.get_value(item, _COLUMN.NUMBER) if item_number == device_number: return item if item_number > device_number: item = None break new_child_index += 1 item = _model.iter_next(item) if not item and device: icon_name = icons.device_icon_name(device.name, device.kind) status_text = None status_icon = None row_data = ( receiver_path, device_number, bool(device.online), device.codename, icon_name, status_text, status_icon, device, ) assert len(row_data) == len(_TREE_SEPATATOR) if logger.isEnabledFor(logging.DEBUG): logger.debug("new device row %s at index %d", row_data, new_child_index) item = _model.insert(receiver_row, new_child_index, row_data) return item or None def select(receiver_path, device_number=None): assert _window assert receiver_path is not None if device_number is None: item = _receiver_row(receiver_path) else: item = _device_row(receiver_path, device_number) if item: selection = _tree.get_selection() selection.select_iter(item) else: logger.warning("select(%s, %s) failed to find an item", receiver_path, device_number) def _hide(w, _ignore=None): assert w == _window # some window managers move the window to 0,0 after hide() # so try to remember the last position position = _window.get_position() _window.hide() _window.move(*position) return True def popup(trigger=None, receiver_path=None, device_id=None): if receiver_path: select(receiver_path, device_id) _window.present() return True def toggle(trigger=None): if _window.get_visible(): _hide(_window) else: _window.present() def _update_details(button): assert button visible = button.get_active() if visible: # _details._text.set_markup('reading...') def _details_items(device, read_all=False): # If read_all is False, only return stuff that is ~100% already # cached, and involves no HID++ calls. yield _("Path"), device.path if device.kind is None: yield _("USB ID"), f"{LOGITECH_VENDOR_ID:04x}:" + device.product_id if read_all: yield _("Serial"), device.serial else: yield _("Serial"), "..." else: # yield ('Codename', device.codename) yield _("Index"), device.number if device.wpid: yield _("Wireless PID"), device.wpid if device.product_id: yield _("Product ID"), f"{LOGITECH_VENDOR_ID:04x}:" + device.product_id hid_version = device.protocol yield _("Protocol"), f"HID++ {hid_version:1.1f}" if hid_version else _("Unknown") if read_all and device.polling_rate: yield _("Polling rate"), device.polling_rate if read_all or not device.online: yield _("Serial"), device.serial else: yield _("Serial"), "..." if read_all and device.unitId and device.unitId != device.serial: yield _("Unit ID"), device.unitId if read_all: if device.firmware: for fw in list(device.firmware): yield " " + _(str(fw.kind)), (fw.name + " " + fw.version).strip() elif device.kind is None or device.online: yield f" {_('Firmware')}", "..." flag_bits = device.notification_flags if flag_bits is not None: flag_names = ( (f"({_('none')})",) if flag_bits == 0 else hidpp10_constants.NOTIFICATION_FLAG.flag_names(flag_bits) ) yield _("Notifications"), f"\n{' ':15}".join(flag_names) def _set_details(text): _details._text.set_markup(text) def _make_text(items): text = "\n".join("%-13s: %s" % (name, value) for name, value in items) return "" + text + "" def _displayable_items(items): for name, value in items: value = GLib.markup_escape_text(str(value).replace("\x00", "")).strip() if value: yield name, value def _read_slow(device): items = _details_items(selected_device, True) items = _displayable_items(items) text = _make_text(items) if device == _details._current_device: GLib.idle_add(_set_details, text) selected_device = _find_selected_device() assert selected_device _details._current_device = selected_device read_all = not (selected_device.kind is None or selected_device.online) items = _details_items(selected_device, read_all) _set_details(_make_text(items)) if read_all: _details._current_device = None else: ui_async(_read_slow, selected_device) _details.set_visible(visible) def _update_receiver_panel(receiver, panel, buttons, full=False): assert receiver devices_count = len(receiver) paired_text = ( _(_("No device paired.")) if devices_count == 0 else ngettext("%(count)s paired device.", "%(count)s paired devices.", devices_count) % {"count": devices_count} ) if receiver.max_devices > 0: paired_text += ( "\n\n%s" % ngettext( "Up to %(max_count)s device can be paired to this receiver.", "Up to %(max_count)s devices can be paired to this receiver.", receiver.max_devices, ) % {"max_count": receiver.max_devices} ) elif devices_count > 0: paired_text += f"\n\n{_('Only one device can be paired to this receiver.')}" pairings = receiver.remaining_pairings() if pairings is not None and pairings >= 0: paired_text += "\n%s" % ( ngettext("This receiver has %d pairing remaining.", "This receiver has %d pairings remaining.", pairings) % pairings ) panel._count.set_markup(paired_text) is_pairing = receiver.pairing.lock_open if is_pairing: panel._scanning.set_visible(True) if not panel._spinner.get_visible(): panel._spinner.start() panel._spinner.set_visible(True) else: panel._scanning.set_visible(False) if panel._spinner.get_visible(): panel._spinner.stop() panel._spinner.set_visible(False) panel.set_visible(True) # b._insecure.set_visible(False) buttons._unpair.set_visible(False) if ( not is_pairing and (receiver.remaining_pairings() is None or receiver.remaining_pairings() != 0) and (receiver.re_pairs or devices_count < receiver.max_devices) ): buttons._pair.set_sensitive(True) else: buttons._pair.set_sensitive(False) buttons._pair.set_visible(True) def _update_device_panel(device, panel, buttons, full=False): assert device is_online = bool(device.online) panel.set_sensitive(is_online) if device.battery_info is None or device.battery_info.level is None: device.read_battery() battery_level = device.battery_info.level if device.battery_info is not None else None battery_voltage = device.battery_info.voltage if device.battery_info is not None else None if battery_level is None and battery_voltage is None: panel._battery.set_visible(False) else: panel._battery.set_visible(True) battery_next_level = device.battery_info.next_level charging = device.battery_info.charging() if device.battery_info is not None else None icon_name = icons.battery(battery_level, charging) panel._battery._icon.set_from_icon_name(icon_name, _INFO_ICON_SIZE) panel._battery._icon.set_sensitive(True) panel._battery._text.set_sensitive(is_online) if battery_voltage is not None: panel._battery._label.set_text(_("Battery Voltage")) text = f"{int(battery_voltage)}mV" tooltip_text = _("Voltage reported by battery") else: panel._battery._label.set_text(_("Battery Level")) text = "" tooltip_text = _("Approximate level reported by battery") if battery_voltage is not None and battery_level is not None: text += ", " if battery_level is not None: text += _(str(battery_level)) if isinstance(battery_level, NamedInt) else f"{int(battery_level)}%" if battery_next_level is not None and not charging: if isinstance(battery_next_level, NamedInt): text += " (" + _("next reported ") + _(str(battery_next_level)) + ")" else: text += " (" + _("next reported ") + f"{int(battery_next_level)}%" + ")" tooltip_text = tooltip_text + _(" and next level to be reported.") if is_online: if charging: text += f" ({_('charging')})" else: text += f" ({_('last known')})" panel._battery._text.set_markup(text) panel._battery.set_tooltip_text(tooltip_text) if device.link_encrypted is None: panel._secure.set_visible(False) elif is_online: panel._secure.set_visible(True) panel._secure._icon.set_visible(True) if device.link_encrypted is True: panel._secure._text.set_text(_("encrypted")) panel._secure._icon.set_from_icon_name("security-high", _INFO_ICON_SIZE) panel._secure.set_tooltip_text(_("The wireless link between this device and its receiver is encrypted.")) else: panel._secure._text.set_text(_("not encrypted")) panel._secure._icon.set_from_icon_name("security-low", _INFO_ICON_SIZE) panel._secure.set_tooltip_text( _( "The wireless link between this device and its receiver is not encrypted.\n" "This is a security issue for pointing devices, and a major security issue for text-input devices." ) ) else: panel._secure.set_visible(True) panel._secure._icon.set_visible(False) panel._secure._text.set_markup(f"{_('offline')}") panel._secure.set_tooltip_text("") if is_online: light_level = device.battery_info.light_level if device.battery_info is not None else None if light_level is None: panel._lux.set_visible(False) else: panel._lux._icon.set_from_icon_name(icons.lux(light_level), _INFO_ICON_SIZE) panel._lux._text.set_text(_("%(light_level)d lux") % {"light_level": light_level}) panel._lux.set_visible(True) else: panel._lux.set_visible(False) buttons._pair.set_visible(False) buttons._unpair.set_sensitive(device.receiver.may_unpair if device.receiver else False) buttons._unpair.set_visible(True) panel.set_visible(True) if full: config_panel.update(device, is_online) def _update_info_panel(device, full=False): if device is None: # no selected device, show the 'empty' panel _details.set_visible(False) _info.set_visible(False) _empty.set_visible(True) return # a receiver must be valid # a device must be paired assert device _info._title.set_markup(f"{device.name}") icon_name = icons.device_icon_name(device.name, device.kind) _info._icon.set_from_icon_name(icon_name, _DEVICE_ICON_SIZE) if device.kind is None: _info._device.set_visible(False) _info._icon.set_sensitive(True) _info._title.set_sensitive(True) _update_receiver_panel(device, _info._receiver, _info._buttons, full) else: _info._receiver.set_visible(False) is_online = bool(device.online) _info._icon.set_sensitive(is_online) _info._title.set_sensitive(is_online) _update_device_panel(device, _info._device, _info._buttons, full) _empty.set_visible(False) _info.set_visible(True) if full: _update_details(_info._buttons._details) # window layout: # +--------------------------------+ # | tree | receiver | empty | # | | or device | | # |------------| status | | # | details | | | # |--------------------------------| # | (about) | # +--------------------------------| # either the status or empty panel is visible at any point # the details panel can be toggle on/off _model = None _tree = None _details = None _info = None _empty = None _window = None def init(show_window, hide_on_close): Gtk.Window.set_default_icon_name(NAME.lower()) global _model, _tree, _details, _info, _empty, _window _model = Gtk.TreeStore(*_COLUMN_TYPES) _tree = _create_tree(_model) _details = _create_details_panel() _info = _create_info_panel() _empty = _create_empty_panel() _window = _create(_hide if hide_on_close else destroy) if show_window: _window.present() def destroy(_ignore1=None, _ignore2=None): global _model, _tree, _details, _info, _empty, _window w, _window = _window, None w.destroy() w = None config_panel.destroy() _empty = None _info = None _details = None _tree = None _model = None def update(device, need_popup=False, refresh=False): if _window is None: return assert device is not None if need_popup: popup() selected_device_id = _find_selected_device_id() if device.kind is None: # receiver # receiver is_alive = bool(device) item = _receiver_row(device.path, device if is_alive else None) if is_alive and item: was_pairing = bool(_model.get_value(item, _COLUMN.STATUS_ICON)) is_pairing = (not device.isDevice) and bool(device.pairing.lock_open) _model.set_value(item, _COLUMN.STATUS_ICON, "network-wireless" if is_pairing else _CAN_SET_ROW_NONE) if selected_device_id == (device.path, 0): full_update = need_popup or was_pairing != is_pairing _update_info_panel(device, full=full_update) elif item: if _TREE_SEPATATOR: separator = _model.iter_next(item) if separator: _model.remove(separator) _model.remove(item) else: path = device.receiver.path if device.receiver is not None else device.path assert device.number is not None and device.number >= 0, "invalid device number" + str(device.number) item = _device_row(path, device.number, device if bool(device) else None) if bool(device) and item: update_device(device, item, selected_device_id, need_popup, full=refresh) elif item: _model.remove(item) config_panel.clean(device) # make sure all rows are visible _tree.expand_all() def update_device(device, item, selected_device_id, need_popup, full=False): was_online = _model.get_value(item, _COLUMN.ACTIVE) is_online = bool(device.online) _model.set_value(item, _COLUMN.ACTIVE, is_online) battery_level = device.battery_info.level if device.battery_info is not None else None battery_voltage = device.battery_info.voltage if device.battery_info is not None else None if battery_level is None: _model.set_value(item, _COLUMN.STATUS_TEXT, _CAN_SET_ROW_NONE) _model.set_value(item, _COLUMN.STATUS_ICON, _CAN_SET_ROW_NONE) else: if battery_voltage is not None and False: # Use levels instead of voltage here status_text = f"{int(battery_voltage)}mV" elif isinstance(battery_level, NamedInt): status_text = _(str(battery_level)) else: status_text = f"{int(battery_level)}%" _model.set_value(item, _COLUMN.STATUS_TEXT, status_text) charging = device.battery_info.charging() if device.battery_info is not None else None icon_name = icons.battery(battery_level, charging) _model.set_value(item, _COLUMN.STATUS_ICON, icon_name) _model.set_value(item, _COLUMN.NAME, device.codename) if selected_device_id is None or need_popup: select(device.receiver.path if device.receiver else device.path, device.number) elif selected_device_id == (device.receiver.path if device.receiver else device.path, device.number): full_update = full or was_online != is_online _update_info_panel(device, full=full_update) def find_device(serial): assert serial, "need serial number or unit ID to find a device" result = None def check(_store, _treepath, row): nonlocal result device = _model.get_value(row, _COLUMN.DEVICE) if device and device.kind and (device.unitId == serial or device.serial == serial): result = device return True _model.foreach(check) return result Solaar-1.1.14/lib/solaar/version000066400000000000000000000000071473525213700164640ustar00rootroot000000000000001.1.14 Solaar-1.1.14/mkdocs.yml000066400000000000000000000013341473525213700150340ustar00rootroot00000000000000site_name: Solaar Documentation site_description: Linux Device Manager for Logitech Unifying Receivers and Devices. site_author: pwr-Solaar repo_url: https://github.com/pwr-Solaar/Solaar repo_name: Solaar logo: img/favicon.png theme: name: readthedocs docs_dir: docs nav: - Solaar: index.md - Capabilities: capabilities.md - Debian: debian.md - Devices: devices.md - Features: features.md - Translation: i18n.md - Implementation: implementation.md - Installation: installation.md - Rules: rules.md - Usage: usage.md plugins: - search - mkdocstrings: handlers: python: setup_commands: - python -m pip install . - mermaid2 markdown_extensions: - pymdownx.superfences Solaar-1.1.14/po/000077500000000000000000000000001473525213700134465ustar00rootroot00000000000000Solaar-1.1.14/po/README000066400000000000000000000001141473525213700143220ustar00rootroot00000000000000See docs/i18n.md for instructions about creating or updating a translation. Solaar-1.1.14/po/ca.po000066400000000000000000001571271473525213700144060ustar00rootroot00000000000000# Catalan translations for Solaar package. # Copyright (C) 2021 THE Solaar's COPYRIGHT HOLDER. # This file is distributed under the same license as the Solaar package. # Marc Serra , 2021. # msgid "" msgstr "Project-Id-Version: solaar 0.9.3\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-12-28 17:40+0100\n" "PO-Revision-Date: 2021-10-22 19:19+0200\n" "Last-Translator: \n" "Language-Team: none\n" "Language: ca\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 2.3\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: lib/logitech_receiver/base_usb.py:46 msgid "Bolt Receiver" msgstr "" #: lib/logitech_receiver/base_usb.py:57 msgid "Unifying Receiver" msgstr "Receptor Unifying" #: lib/logitech_receiver/base_usb.py:67 lib/logitech_receiver/base_usb.py:78 #: lib/logitech_receiver/base_usb.py:90 lib/logitech_receiver/base_usb.py:102 #: lib/logitech_receiver/base_usb.py:114 msgid "Nano Receiver" msgstr "Receptor Nano" #: lib/logitech_receiver/base_usb.py:124 msgid "Lightspeed Receiver" msgstr "Receptor Lightspeed" #: lib/logitech_receiver/base_usb.py:133 msgid "EX100 Receiver 27 Mhz" msgstr "Receptor EX100 27 Mhz" #: lib/logitech_receiver/i18n.py:30 msgid "empty" msgstr "buida" #: lib/logitech_receiver/i18n.py:31 msgid "critical" msgstr "crítica" #: lib/logitech_receiver/i18n.py:32 msgid "low" msgstr "baixa" #: lib/logitech_receiver/i18n.py:33 msgid "average" msgstr "mitja" #: lib/logitech_receiver/i18n.py:34 msgid "good" msgstr "bona" #: lib/logitech_receiver/i18n.py:35 msgid "full" msgstr "plena" #: lib/logitech_receiver/i18n.py:38 msgid "discharging" msgstr "descarregant" #: lib/logitech_receiver/i18n.py:39 msgid "recharging" msgstr "recarregant" #: lib/logitech_receiver/i18n.py:40 lib/solaar/ui/window.py:711 msgid "charging" msgstr "carregant" #: lib/logitech_receiver/i18n.py:41 msgid "not charging" msgstr "no carregant" #: lib/logitech_receiver/i18n.py:42 msgid "almost full" msgstr "quasi plena" #: lib/logitech_receiver/i18n.py:43 msgid "charged" msgstr "carregant" #: lib/logitech_receiver/i18n.py:44 msgid "slow recharge" msgstr "recàrrega lenta" #: lib/logitech_receiver/i18n.py:45 msgid "invalid battery" msgstr "bateria no vàlida" #: lib/logitech_receiver/i18n.py:46 msgid "thermal error" msgstr "error tèrmic" #: lib/logitech_receiver/i18n.py:47 msgid "error" msgstr "error" #: lib/logitech_receiver/i18n.py:48 msgid "standard" msgstr "estàndard" #: lib/logitech_receiver/i18n.py:49 msgid "fast" msgstr "ràpid" #: lib/logitech_receiver/i18n.py:50 msgid "slow" msgstr "lent" #: lib/logitech_receiver/i18n.py:53 msgid "device timeout" msgstr "temps exhaurit pel dispositiu" #: lib/logitech_receiver/i18n.py:54 msgid "device not supported" msgstr "dispositiu no suportat" #: lib/logitech_receiver/i18n.py:55 msgid "too many devices" msgstr "masses dispositius" #: lib/logitech_receiver/i18n.py:56 msgid "sequence timeout" msgstr "temps de seqüència exhaurit" #: lib/logitech_receiver/i18n.py:59 lib/solaar/ui/window.py:572 msgid "Firmware" msgstr "Firmware" #: lib/logitech_receiver/i18n.py:60 msgid "Bootloader" msgstr "Gestor d'arrencada" #: lib/logitech_receiver/i18n.py:61 msgid "Hardware" msgstr "Hardware" #: lib/logitech_receiver/i18n.py:62 msgid "Other" msgstr "Altres" #: lib/logitech_receiver/i18n.py:65 msgid "Left Button" msgstr "Botó Esquerra" #: lib/logitech_receiver/i18n.py:66 msgid "Right Button" msgstr "Botó Dret" #: lib/logitech_receiver/i18n.py:67 msgid "Middle Button" msgstr "Botó Central" #: lib/logitech_receiver/i18n.py:68 msgid "Back Button" msgstr "Botó Endarrere" #: lib/logitech_receiver/i18n.py:69 msgid "Forward Button" msgstr "Botó Endavant" #: lib/logitech_receiver/i18n.py:70 msgid "Mouse Gesture Button" msgstr "Botó Gestos Ratolí" #: lib/logitech_receiver/i18n.py:71 msgid "Smart Shift" msgstr "Canvi intel·ligent" #: lib/logitech_receiver/i18n.py:72 msgid "DPI Switch" msgstr "Selector DPI" #: lib/logitech_receiver/i18n.py:73 msgid "Left Tilt" msgstr "Inclinació a l'Esquerra" #: lib/logitech_receiver/i18n.py:74 msgid "Right Tilt" msgstr "Inclinació a la Dreta" #: lib/logitech_receiver/i18n.py:75 msgid "Left Click" msgstr "Clic Esquerra" #: lib/logitech_receiver/i18n.py:76 msgid "Right Click" msgstr "Clic Dreta" #: lib/logitech_receiver/i18n.py:77 msgid "Mouse Middle Button" msgstr "Clic Central Ratolí" #: lib/logitech_receiver/i18n.py:78 msgid "Mouse Back Button" msgstr "Clic Endarrere Ratolí" #: lib/logitech_receiver/i18n.py:79 msgid "Mouse Forward Button" msgstr "Clik Endavant Ratolí" #: lib/logitech_receiver/i18n.py:80 msgid "Gesture Button Navigation" msgstr "Navegació amb el Botó de Gestos" #: lib/logitech_receiver/i18n.py:81 msgid "Mouse Scroll Left Button" msgstr "Botó Scroll a l'esquerra" #: lib/logitech_receiver/i18n.py:82 msgid "Mouse Scroll Right Button" msgstr "Botó Scroll a la dreta" #: lib/logitech_receiver/i18n.py:85 msgid "pressed" msgstr "pressionat" #: lib/logitech_receiver/i18n.py:86 msgid "released" msgstr "alliberat" #: lib/logitech_receiver/notifications.py:75 #: lib/logitech_receiver/notifications.py:126 msgid "pairing lock is closed" msgstr "el bloqueig de vinculació està tancat" #: lib/logitech_receiver/notifications.py:75 #: lib/logitech_receiver/notifications.py:126 msgid "pairing lock is open" msgstr "el bloqueig de vinculació està obert" #: lib/logitech_receiver/notifications.py:92 msgid "discovery lock is closed" msgstr "" #: lib/logitech_receiver/notifications.py:92 msgid "discovery lock is open" msgstr "" #: lib/logitech_receiver/notifications.py:224 lib/solaar/ui/notify.py:122 msgid "connected" msgstr "connectat" #: lib/logitech_receiver/notifications.py:224 msgid "disconnected" msgstr "desconnectat" #: lib/logitech_receiver/notifications.py:262 lib/solaar/ui/notify.py:120 msgid "unpaired" msgstr "desvinculat" #: lib/logitech_receiver/notifications.py:304 msgid "powered on" msgstr "alimentat" #: lib/logitech_receiver/settings.py:750 msgid "register" msgstr "registrar" #: lib/logitech_receiver/settings.py:764 lib/logitech_receiver/settings.py:791 msgid "feature" msgstr "característica" #: lib/logitech_receiver/settings_templates.py:139 msgid "Swap Fx function" msgstr "Intercanviar funció Fx" #: lib/logitech_receiver/settings_templates.py:140 msgid "When set, the F1..F12 keys will activate their special function,\n" "and you must hold the FN key to activate their standard function." msgstr "Al activar-se, les tecles F1..F12 activaran les seves funcions " "especials,\n" "i s'ha de mantenir pressionada la tecla FN per activar la seva " "funció estàndard." #: lib/logitech_receiver/settings_templates.py:142 msgid "When unset, the F1..F12 keys will activate their standard function,\n" "and you must hold the FN key to activate their special function." msgstr "Al desactivar-se, les tecles F1..F12 activaran les seves funcions " "estàndard,\n" "i s'ha de mantenir pressionada la tecla FN per activar la seva " "funció especial." #: lib/logitech_receiver/settings_templates.py:149 msgid "Hand Detection" msgstr "Detecció de mans" #: lib/logitech_receiver/settings_templates.py:150 msgid "Turn on illumination when the hands hover over the keyboard." msgstr "Encendra la i·il·luminació quan les mans passin pel damunt el teclat." #: lib/logitech_receiver/settings_templates.py:157 msgid "Scroll Wheel Smooth Scrolling" msgstr "Suavitat Scroll Rodeta" #: lib/logitech_receiver/settings_templates.py:158 #: lib/logitech_receiver/settings_templates.py:239 #: lib/logitech_receiver/settings_templates.py:267 msgid "High-sensitivity mode for vertical scroll with the wheel." msgstr "Mode d'alta sensibilitat per l'scroll vertical amb la rodeta." #: lib/logitech_receiver/settings_templates.py:165 msgid "Side Scrolling" msgstr "Scroll Lateral" #: lib/logitech_receiver/settings_templates.py:167 msgid "When disabled, pushing the wheel sideways sends custom button " "events\n" "instead of the standard side-scrolling events." msgstr "Al desactivar-se, pressionar la rodeta lateralment envia " "esdeveniments de botons personalitzats\n" "en lloc dels esdeveniments estàndard d'scroll lateral." #: lib/logitech_receiver/settings_templates.py:177 msgid "Sensitivity (DPI - older mice)" msgstr "" #: lib/logitech_receiver/settings_templates.py:178 #: lib/logitech_receiver/settings_templates.py:712 msgid "Mouse movement sensitivity" msgstr "Sensibilitat del moviment del ratolí" #: lib/logitech_receiver/settings_templates.py:208 #: lib/logitech_receiver/settings_templates.py:218 #: lib/logitech_receiver/settings_templates.py:225 msgid "Backlight" msgstr "Retroiluminació" #: lib/logitech_receiver/settings_templates.py:209 #: lib/logitech_receiver/settings_templates.py:226 msgid "Set illumination time for keyboard." msgstr "" #: lib/logitech_receiver/settings_templates.py:219 msgid "Turn illumination on or off on keyboard." msgstr "Encendre o apagar la il·luminació del teclat." #: lib/logitech_receiver/settings_templates.py:237 msgid "Scroll Wheel High Resolution" msgstr "Rodeta d'Alta Resolució" #: lib/logitech_receiver/settings_templates.py:240 #: lib/logitech_receiver/settings_templates.py:268 msgid "Set to ignore if scrolling is abnormally fast or slow" msgstr "" #: lib/logitech_receiver/settings_templates.py:247 #: lib/logitech_receiver/settings_templates.py:277 msgid "Scroll Wheel Diversion" msgstr "Desviar Rodeta" #: lib/logitech_receiver/settings_templates.py:249 msgid "Make scroll wheel send LOWRES_WHEEL HID++ notifications (which " "trigger Solaar rules but are otherwise ignored)." msgstr "" #: lib/logitech_receiver/settings_templates.py:256 msgid "Scroll Wheel Direction" msgstr "Direcció Desplaçament Rodeta" #: lib/logitech_receiver/settings_templates.py:257 msgid "Invert direction for vertical scroll with wheel." msgstr "Invertir direcció del desplaçament vertical amb la rodeta." #: lib/logitech_receiver/settings_templates.py:265 msgid "Scroll Wheel Resolution" msgstr "Resolució Rodeta Desplaçament" #: lib/logitech_receiver/settings_templates.py:279 msgid "Make scroll wheel send HIRES_WHEEL HID++ notifications (which " "trigger Solaar rules but are otherwise ignored)." msgstr "" #: lib/logitech_receiver/settings_templates.py:288 msgid "Sensitivity (Pointer Speed)" msgstr "Sensibilitat (Velocitat del punter)" #: lib/logitech_receiver/settings_templates.py:289 msgid "Speed multiplier for mouse (256 is normal multiplier)." msgstr "Multiplicador de velocitat pel ratolí (256 és un multiplicador " "normal)" #: lib/logitech_receiver/settings_templates.py:299 msgid "Thumb Wheel Diversion" msgstr "Desviament de la Rodeta del Polze" #: lib/logitech_receiver/settings_templates.py:301 msgid "Make thumb wheel send THUMB_WHEEL HID++ notifications (which trigger " "Solaar rules but are otherwise ignored)." msgstr "" #: lib/logitech_receiver/settings_templates.py:310 msgid "Thumb Wheel Direction" msgstr "Direcció de la Rodeta del Polze" #: lib/logitech_receiver/settings_templates.py:311 msgid "Invert thumb wheel scroll direction." msgstr "Invertir la direcció de desplaçament de la rodeta del polze." #: lib/logitech_receiver/settings_templates.py:319 msgid "Onboard Profiles" msgstr "" #: lib/logitech_receiver/settings_templates.py:320 msgid "Enable onboard profiles, which often control report rate and " "keyboard lighting" msgstr "" #: lib/logitech_receiver/settings_templates.py:330 msgid "Polling Rate (ms)" msgstr "Taxa de Sondeig (ms)" #: lib/logitech_receiver/settings_templates.py:332 msgid "Frequency of device polling, in milliseconds" msgstr "Freqüència de sondeig del dispositiu, en milisegons" #: lib/logitech_receiver/settings_templates.py:333 #: lib/logitech_receiver/settings_templates.py:1046 #: lib/logitech_receiver/settings_templates.py:1076 msgid "May need Onboard Profiles set to Disable to be effective." msgstr "" #: lib/logitech_receiver/settings_templates.py:365 msgid "Divert crown events" msgstr "Desviar esdeveniments de la corona" #: lib/logitech_receiver/settings_templates.py:366 msgid "Make crown send CROWN HID++ notifications (which trigger Solaar " "rules but are otherwise ignored)." msgstr "Fer que la corona enviï notificacions de CROWN HID ++ (que activen " "les regles de Solaar, del contrari s'ignoren)." #: lib/logitech_receiver/settings_templates.py:374 msgid "Crown smooth scroll" msgstr "" #: lib/logitech_receiver/settings_templates.py:375 msgid "Set crown smooth scroll" msgstr "" #: lib/logitech_receiver/settings_templates.py:383 msgid "Divert G Keys" msgstr "Desviar Tecles G" #: lib/logitech_receiver/settings_templates.py:385 msgid "Make G keys send GKEY HID++ notifications (which trigger Solaar " "rules but are otherwise ignored)." msgstr "Fer que les tecles G enviïn notificacions GKEY HID ++ (que activen " "les regles de Solaar, del contrari, s'ignoren)." #: lib/logitech_receiver/settings_templates.py:386 msgid "May also make M keys and MR key send HID++ notifications" msgstr "" #: lib/logitech_receiver/settings_templates.py:402 msgid "Scroll Wheel Ratcheted" msgstr "" #: lib/logitech_receiver/settings_templates.py:403 msgid "Switch the mouse wheel between speed-controlled ratcheting and " "always freespin." msgstr "" #: lib/logitech_receiver/settings_templates.py:405 msgid "Freespinning" msgstr "" #: lib/logitech_receiver/settings_templates.py:405 msgid "Ratcheted" msgstr "" #: lib/logitech_receiver/settings_templates.py:412 msgid "Scroll Wheel Ratchet Speed" msgstr "" #: lib/logitech_receiver/settings_templates.py:414 msgid "Use the mouse wheel speed to switch between ratcheted and " "freespinning.\n" "The mouse wheel is always ratcheted at 50." msgstr "" #: lib/logitech_receiver/settings_templates.py:463 msgid "Key/Button Actions" msgstr "Accions de Tecla/Botó" #: lib/logitech_receiver/settings_templates.py:465 msgid "Change the action for the key or button." msgstr "Canviar l'acció per la tecla o botó." #: lib/logitech_receiver/settings_templates.py:465 msgid "Overridden by diversion." msgstr "" #: lib/logitech_receiver/settings_templates.py:466 msgid "Changing important actions (such as for the left mouse button) can " "result in an unusable system." msgstr "Canviar accions importants (per exemple el botó esquerra del ratolí) " "pot deixar el sistema inservible." #: lib/logitech_receiver/settings_templates.py:639 msgid "Key/Button Diversion" msgstr "Desviament de Tecla/Botó" #: lib/logitech_receiver/settings_templates.py:640 msgid "Make the key or button send HID++ notifications (Diverted) or " "initiate Mouse Gestures or Sliding DPI" msgstr "" #: lib/logitech_receiver/settings_templates.py:643 #: lib/logitech_receiver/settings_templates.py:644 #: lib/logitech_receiver/settings_templates.py:645 msgid "Diverted" msgstr "Desviat" #: lib/logitech_receiver/settings_templates.py:643 #: lib/logitech_receiver/settings_templates.py:644 msgid "Mouse Gestures" msgstr "Gestos de Ratolí" #: lib/logitech_receiver/settings_templates.py:643 #: lib/logitech_receiver/settings_templates.py:644 #: lib/logitech_receiver/settings_templates.py:645 msgid "Regular" msgstr "Normal" #: lib/logitech_receiver/settings_templates.py:643 msgid "Sliding DPI" msgstr "" #: lib/logitech_receiver/settings_templates.py:711 msgid "Sensitivity (DPI)" msgstr "Sensibilitat (DPI)" #: lib/logitech_receiver/settings_templates.py:752 msgid "Sensitivity Switching" msgstr "" #: lib/logitech_receiver/settings_templates.py:754 msgid "Switch the current sensitivity and the remembered sensitivity when " "the key or button is pressed.\n" "If there is no remembered sensitivity, just remember the current " "sensitivity" msgstr "" #: lib/logitech_receiver/settings_templates.py:758 msgid "Off" msgstr "Off" #: lib/logitech_receiver/settings_templates.py:791 msgid "Disable keys" msgstr "Desactivar tecles" #: lib/logitech_receiver/settings_templates.py:792 msgid "Disable specific keyboard keys." msgstr "Desactivar tecles específiques del teclat." #: lib/logitech_receiver/settings_templates.py:795 #, python-format msgid "Disables the %s key." msgstr "Desactiva la tecla %s." #: lib/logitech_receiver/settings_templates.py:809 #: lib/logitech_receiver/settings_templates.py:860 msgid "Set OS" msgstr "Especificar S.O." #: lib/logitech_receiver/settings_templates.py:810 #: lib/logitech_receiver/settings_templates.py:861 msgid "Change keys to match OS." msgstr "Canviar tecles per coincidir amb el S.O." #: lib/logitech_receiver/settings_templates.py:873 msgid "Change Host" msgstr "Canviar Host" #: lib/logitech_receiver/settings_templates.py:874 msgid "Switch connection to a different host" msgstr "Canviar connexió a un host diferent" #: lib/logitech_receiver/settings_templates.py:900 msgid "Performs a left click." msgstr "Realitza un clic esquerra." #: lib/logitech_receiver/settings_templates.py:900 msgid "Single tap" msgstr "Un toc" #: lib/logitech_receiver/settings_templates.py:901 msgid "Performs a right click." msgstr "Realitza un clic dret." #: lib/logitech_receiver/settings_templates.py:901 msgid "Single tap with two fingers" msgstr "Un toc amb dos dits" #: lib/logitech_receiver/settings_templates.py:902 msgid "Single tap with three fingers" msgstr "Un toc amb tres dits" #: lib/logitech_receiver/settings_templates.py:906 msgid "Double tap" msgstr "Doble toc" #: lib/logitech_receiver/settings_templates.py:906 msgid "Performs a double click." msgstr "Realitza un clic doble." #: lib/logitech_receiver/settings_templates.py:907 msgid "Double tap with two fingers" msgstr "Doble toc amb dos dits" #: lib/logitech_receiver/settings_templates.py:908 msgid "Double tap with three fingers" msgstr "Doble toc amb tres dits" #: lib/logitech_receiver/settings_templates.py:911 msgid "Drags items by dragging the finger after double tapping." msgstr "Arrossega elements arrossegant el dit després de tocar dues vegades." #: lib/logitech_receiver/settings_templates.py:911 msgid "Tap and drag" msgstr "Tocar i arrossegar" #: lib/logitech_receiver/settings_templates.py:913 msgid "Drags items by dragging the fingers after double tapping." msgstr "Arrossega elements arrossegant els dits després de tocar dues " "vegades." #: lib/logitech_receiver/settings_templates.py:913 msgid "Tap and drag with two fingers" msgstr "Tocar i arrossegar amb dos dits" #: lib/logitech_receiver/settings_templates.py:914 msgid "Tap and drag with three fingers" msgstr "Tocar i arrossegar amb tres dits" #: lib/logitech_receiver/settings_templates.py:917 msgid "Disables tap and edge gestures (equivalent to pressing Fn+LeftClick)." msgstr "Desactiva els gestos de toc i marge (equivalent a pressionar " "FN+LeftClick)." #: lib/logitech_receiver/settings_templates.py:917 msgid "Suppress tap and edge gestures" msgstr "Elimina els gestos de toc i marge" #: lib/logitech_receiver/settings_templates.py:918 msgid "Scroll with one finger" msgstr "Desplaça amb un dit" #: lib/logitech_receiver/settings_templates.py:918 #: lib/logitech_receiver/settings_templates.py:919 #: lib/logitech_receiver/settings_templates.py:922 msgid "Scrolls." msgstr "Desplaçament." #: lib/logitech_receiver/settings_templates.py:919 #: lib/logitech_receiver/settings_templates.py:922 msgid "Scroll with two fingers" msgstr "Desplaça amb dos dits" #: lib/logitech_receiver/settings_templates.py:920 msgid "Scroll horizontally with two fingers" msgstr "Desplaça horitzontalment amb dos dits" #: lib/logitech_receiver/settings_templates.py:920 msgid "Scrolls horizontally." msgstr "Desplaça horitzontalment." #: lib/logitech_receiver/settings_templates.py:921 msgid "Scroll vertically with two fingers" msgstr "Desplaçar verticalment amb dos dits" #: lib/logitech_receiver/settings_templates.py:921 msgid "Scrolls vertically." msgstr "Desplaça verticalment." #: lib/logitech_receiver/settings_templates.py:923 msgid "Inverts the scrolling direction." msgstr "Invertir la direcció de desplaçament." #: lib/logitech_receiver/settings_templates.py:923 msgid "Natural scrolling" msgstr "Desplaçament natural" #: lib/logitech_receiver/settings_templates.py:924 msgid "Enables the thumbwheel." msgstr "Activa la rodeta del polze." #: lib/logitech_receiver/settings_templates.py:924 msgid "Thumbwheel" msgstr "Rodeta del polze" #: lib/logitech_receiver/settings_templates.py:935 #: lib/logitech_receiver/settings_templates.py:939 msgid "Swipe from the top edge" msgstr "Lliscar des del marge superior" #: lib/logitech_receiver/settings_templates.py:936 msgid "Swipe from the left edge" msgstr "Lliscar des del marge esquerra" #: lib/logitech_receiver/settings_templates.py:937 msgid "Swipe from the right edge" msgstr "Lliscar des del marge dret" #: lib/logitech_receiver/settings_templates.py:938 msgid "Swipe from the bottom edge" msgstr "Lliscar des del marge inferior" #: lib/logitech_receiver/settings_templates.py:940 msgid "Swipe two fingers from the left edge" msgstr "Lliscar dos dits des del marge esquerra" #: lib/logitech_receiver/settings_templates.py:941 msgid "Swipe two fingers from the right edge" msgstr "Lliscar dos dits des del marge dret" #: lib/logitech_receiver/settings_templates.py:942 msgid "Swipe two fingers from the bottom edge" msgstr "Lliscar dos dits des del marge inferior" #: lib/logitech_receiver/settings_templates.py:943 msgid "Swipe two fingers from the top edge" msgstr "Lliscar dos dits des del marge superior" #: lib/logitech_receiver/settings_templates.py:944 #: lib/logitech_receiver/settings_templates.py:948 msgid "Pinch to zoom out; spread to zoom in." msgstr "Pessigar per allunyar; Expandir per acostar." #: lib/logitech_receiver/settings_templates.py:944 msgid "Zoom with two fingers." msgstr "Zoom amb dos dits." #: lib/logitech_receiver/settings_templates.py:945 msgid "Pinch to zoom out." msgstr "Pessigar per allunyar." #: lib/logitech_receiver/settings_templates.py:946 msgid "Spread to zoom in." msgstr "Expandir per acostar." #: lib/logitech_receiver/settings_templates.py:947 msgid "Zoom with three fingers." msgstr "Zoom amb tres dits." #: lib/logitech_receiver/settings_templates.py:948 msgid "Zoom with two fingers" msgstr "Zoom amb dos dits" #: lib/logitech_receiver/settings_templates.py:966 msgid "Pixel zone" msgstr "Zona de píxels" #: lib/logitech_receiver/settings_templates.py:967 msgid "Ratio zone" msgstr "Zona de relació" #: lib/logitech_receiver/settings_templates.py:968 msgid "Scale factor" msgstr "Factor d'escalat" #: lib/logitech_receiver/settings_templates.py:968 msgid "Sets the cursor speed." msgstr "Estableix la velocitat del cursor." #: lib/logitech_receiver/settings_templates.py:972 msgid "Left" msgstr "Esquerra" #: lib/logitech_receiver/settings_templates.py:972 msgid "Left-most coordinate." msgstr "Coordenada més a la esquerra." #: lib/logitech_receiver/settings_templates.py:973 msgid "Bottom" msgstr "" #: lib/logitech_receiver/settings_templates.py:973 msgid "Bottom coordinate." msgstr "" #: lib/logitech_receiver/settings_templates.py:974 msgid "Width" msgstr "Amplada" #: lib/logitech_receiver/settings_templates.py:974 msgid "Width." msgstr "Amplada." #: lib/logitech_receiver/settings_templates.py:975 msgid "Height" msgstr "Alçada" #: lib/logitech_receiver/settings_templates.py:975 msgid "Height." msgstr "Alçada." #: lib/logitech_receiver/settings_templates.py:976 msgid "Cursor speed." msgstr "Velocitat del cursor." #: lib/logitech_receiver/settings_templates.py:976 msgid "Scale" msgstr "Escala" #: lib/logitech_receiver/settings_templates.py:982 msgid "Gestures" msgstr "Gestos" #: lib/logitech_receiver/settings_templates.py:983 msgid "Tweak the mouse/touchpad behaviour." msgstr "Modificar el comportament del ratolí/panell tàctil." #: lib/logitech_receiver/settings_templates.py:1000 msgid "Gestures Diversion" msgstr "" #: lib/logitech_receiver/settings_templates.py:1001 msgid "Divert mouse/touchpad gestures." msgstr "" #: lib/logitech_receiver/settings_templates.py:1018 msgid "Gesture params" msgstr "Paràmetres de gestos" #: lib/logitech_receiver/settings_templates.py:1019 msgid "Change numerical parameters of a mouse/touchpad." msgstr "Canviar paràmetres numèrics de un ratolí/panell tàctil." #: lib/logitech_receiver/settings_templates.py:1044 msgid "M-Key LEDs" msgstr "" #: lib/logitech_receiver/settings_templates.py:1046 msgid "Control the M-Key LEDs." msgstr "" #: lib/logitech_receiver/settings_templates.py:1047 #: lib/logitech_receiver/settings_templates.py:1077 msgid "May need G Keys diverted to be effective." msgstr "" #: lib/logitech_receiver/settings_templates.py:1053 #, python-format msgid "Lights up the %s key." msgstr "" #: lib/logitech_receiver/settings_templates.py:1074 msgid "MR-Key LED" msgstr "" #: lib/logitech_receiver/settings_templates.py:1076 msgid "Control the MR-Key LED." msgstr "" #: lib/logitech_receiver/settings_templates.py:1095 msgid "Persistent Key/Button Mapping" msgstr "" #: lib/logitech_receiver/settings_templates.py:1097 msgid "Permanently change the mapping for the key or button." msgstr "" #: lib/logitech_receiver/settings_templates.py:1098 msgid "Changing important keys or buttons (such as for the left mouse " "button) can result in an unusable system." msgstr "" #: lib/logitech_receiver/settings_templates.py:1157 msgid "Sidetone" msgstr "" #: lib/logitech_receiver/settings_templates.py:1158 msgid "Set sidetone level." msgstr "" #: lib/logitech_receiver/settings_templates.py:1167 msgid "Equalizer" msgstr "" #: lib/logitech_receiver/settings_templates.py:1168 msgid "Set equalizer levels." msgstr "" #: lib/logitech_receiver/settings_templates.py:1191 msgid "Hz" msgstr "" #: lib/logitech_receiver/settings_templates.py:1197 msgid "Power Management" msgstr "" #: lib/logitech_receiver/settings_templates.py:1198 msgid "Power off in minutes (0 for never)." msgstr "" #: lib/logitech_receiver/status.py:114 msgid "No paired devices." msgstr "Cap dispositiu connectat." #: lib/logitech_receiver/status.py:115 lib/solaar/ui/window.py:622 #, python-format msgid "%(count)s paired device." msgid_plural "%(count)s paired devices." msgstr[0] "%(count)s dispositiu vinculat." msgstr[1] "%(count)s dispositius vinculats." #: lib/logitech_receiver/status.py:170 #, python-format msgid "Battery: %(level)s" msgstr "Bateria: %(level)s" #: lib/logitech_receiver/status.py:172 #, python-format msgid "Battery: %(percent)d%%" msgstr "Bateria: %(percent)d%%" #: lib/logitech_receiver/status.py:184 #, python-format msgid "Lighting: %(level)s lux" msgstr "Il·luminació: %(level)s lux" #: lib/logitech_receiver/status.py:239 #, python-format msgid "Battery: %(level)s (%(status)s)" msgstr "Bateria: %(level)s (%(status)s)" #: lib/logitech_receiver/status.py:241 #, python-format msgid "Battery: %(percent)d%% (%(status)s)" msgstr "Bateria: %(percent)d%% (%(status)s)" #: lib/solaar/ui/__init__.py:52 msgid "Permissions error" msgstr "Error de permisos" #: lib/solaar/ui/__init__.py:54 #, python-format msgid "Found a Logitech receiver or device (%s), but did not have " "permission to open it." msgstr "" #: lib/solaar/ui/__init__.py:55 msgid "If you've just installed Solaar, try disconnecting the receiver or " "device and then reconnecting it." msgstr "" #: lib/solaar/ui/__init__.py:58 msgid "Cannot connect to device error" msgstr "Error al connectar al dispositiu" #: lib/solaar/ui/__init__.py:60 #, python-format msgid "Found a Logitech receiver or device at %s, but encountered an error " "connecting to it." msgstr "S'ha trobat un receptor o dispositiu Logitech a %s, però s'ha " "produït un error al connectar-se." #: lib/solaar/ui/__init__.py:61 msgid "Try disconnecting the device and then reconnecting it or turning it " "off and then on." msgstr "" #: lib/solaar/ui/__init__.py:64 msgid "Unpairing failed" msgstr "Ha fallat la desvinculació" #: lib/solaar/ui/__init__.py:66 #, python-brace-format msgid "Failed to unpair %{device} from %{receiver}." msgstr "Error al desvincular %{device} de %{receiver}." #: lib/solaar/ui/__init__.py:67 msgid "The receiver returned an error, with no further details." msgstr "El receptor ha tornat un error, sense detalls addicionals." #: lib/solaar/ui/__init__.py:177 msgid "Another Solaar process is already running so just expose its window" msgstr "" #: lib/solaar/ui/about.py:36 msgid "Manages Logitech receivers,\n" "keyboards, mice, and tablets." msgstr "Gestiona receptors Logitech,\n" "teclats, ratolins i tauletes." #: lib/solaar/ui/about.py:44 msgid "Additional Programming" msgstr "Programació addicional" #: lib/solaar/ui/about.py:45 msgid "GUI design" msgstr "Disseny de la interfície gràfica" #: lib/solaar/ui/about.py:47 msgid "Testing" msgstr "Provant" #: lib/solaar/ui/about.py:54 msgid "Logitech documentation" msgstr "Documentació de Logitech" #: lib/solaar/ui/action.py:85 lib/solaar/ui/action.py:89 #: lib/solaar/ui/window.py:197 msgid "Unpair" msgstr "Desvincular" #: lib/solaar/ui/action.py:88 lib/solaar/ui/diversion_rules.py:150 msgid "Cancel" msgstr "Cancel·lar" #: lib/solaar/ui/config_panel.py:212 msgid "Complete - ENTER to change" msgstr "" #: lib/solaar/ui/config_panel.py:212 msgid "Incomplete" msgstr "" #: lib/solaar/ui/config_panel.py:455 lib/solaar/ui/config_panel.py:507 #, python-format msgid "%d value" msgid_plural "%d values" msgstr[0] "%d valor" msgstr[1] "%d valors" #: lib/solaar/ui/config_panel.py:518 msgid "Changes allowed" msgstr "Canvis permesos" #: lib/solaar/ui/config_panel.py:519 msgid "No changes allowed" msgstr "Canvis no permesos" #: lib/solaar/ui/config_panel.py:520 msgid "Ignore this setting" msgstr "Ignorar aquesta opció" #: lib/solaar/ui/config_panel.py:565 msgid "Working" msgstr "Funcionant" #: lib/solaar/ui/config_panel.py:568 msgid "Read/write operation failed." msgstr "Ha fallat l'operació de lectura/escriptura." #: lib/solaar/ui/diversion_rules.py:65 msgid "Built-in rules" msgstr "Regles integrades" #: lib/solaar/ui/diversion_rules.py:65 msgid "User-defined rules" msgstr "Regles definides per l'usuari" #: lib/solaar/ui/diversion_rules.py:67 lib/solaar/ui/diversion_rules.py:1083 msgid "Rule" msgstr "Regla" #: lib/solaar/ui/diversion_rules.py:68 lib/solaar/ui/diversion_rules.py:509 #: lib/solaar/ui/diversion_rules.py:636 msgid "Sub-rule" msgstr "Sub-regla" #: lib/solaar/ui/diversion_rules.py:70 msgid "[empty]" msgstr "[buit]" #: lib/solaar/ui/diversion_rules.py:94 msgid "Solaar Rule Editor" msgstr "Editor de Regles Solaar" #: lib/solaar/ui/diversion_rules.py:141 msgid "Make changes permanent?" msgstr "¿Fer els canvis permanents?" #: lib/solaar/ui/diversion_rules.py:146 msgid "Yes" msgstr "Sí" #: lib/solaar/ui/diversion_rules.py:148 msgid "No" msgstr "No" #: lib/solaar/ui/diversion_rules.py:153 msgid "If you choose No, changes will be lost when Solaar is closed." msgstr "Si escull No, els canvis es perdran quan es tanqui Solaar." #: lib/solaar/ui/diversion_rules.py:201 msgid "Save changes" msgstr "Guardar canvis" #: lib/solaar/ui/diversion_rules.py:206 msgid "Discard changes" msgstr "Descartar canvis" #: lib/solaar/ui/diversion_rules.py:372 msgid "Insert here" msgstr "Inserir aquí" #: lib/solaar/ui/diversion_rules.py:374 msgid "Insert above" msgstr "Inserir a dalt" #: lib/solaar/ui/diversion_rules.py:376 msgid "Insert below" msgstr "Inserir a baix" #: lib/solaar/ui/diversion_rules.py:382 msgid "Insert new rule here" msgstr "Inserir nova regla aquí" #: lib/solaar/ui/diversion_rules.py:384 msgid "Insert new rule above" msgstr "Inserir nova regla a dalt" #: lib/solaar/ui/diversion_rules.py:386 msgid "Insert new rule below" msgstr "Inserir nova regla a baix" #: lib/solaar/ui/diversion_rules.py:427 msgid "Paste here" msgstr "Enganxar aquí" #: lib/solaar/ui/diversion_rules.py:429 msgid "Paste above" msgstr "Enganxar a dalt" #: lib/solaar/ui/diversion_rules.py:431 msgid "Paste below" msgstr "Enganxar a baix" #: lib/solaar/ui/diversion_rules.py:437 msgid "Paste rule here" msgstr "Enganxar regla aquí" #: lib/solaar/ui/diversion_rules.py:439 msgid "Paste rule above" msgstr "Enganxar regla a dalt" #: lib/solaar/ui/diversion_rules.py:441 msgid "Paste rule below" msgstr "Enganxar regla a baix" #: lib/solaar/ui/diversion_rules.py:445 msgid "Paste rule" msgstr "Enganxar regla" #: lib/solaar/ui/diversion_rules.py:474 msgid "Flatten" msgstr "Aplanar" #: lib/solaar/ui/diversion_rules.py:507 msgid "Insert" msgstr "Insertar" #: lib/solaar/ui/diversion_rules.py:510 lib/solaar/ui/diversion_rules.py:638 #: lib/solaar/ui/diversion_rules.py:1126 msgid "Or" msgstr "O" #: lib/solaar/ui/diversion_rules.py:511 lib/solaar/ui/diversion_rules.py:637 #: lib/solaar/ui/diversion_rules.py:1111 msgid "And" msgstr "I" #: lib/solaar/ui/diversion_rules.py:513 msgid "Condition" msgstr "Condició" #: lib/solaar/ui/diversion_rules.py:515 lib/solaar/ui/diversion_rules.py:1292 msgid "Feature" msgstr "Característica" #: lib/solaar/ui/diversion_rules.py:516 lib/solaar/ui/diversion_rules.py:1328 msgid "Report" msgstr "Informar" #: lib/solaar/ui/diversion_rules.py:517 lib/solaar/ui/diversion_rules.py:1204 msgid "Process" msgstr "Procés" #: lib/solaar/ui/diversion_rules.py:518 msgid "Mouse process" msgstr "" #: lib/solaar/ui/diversion_rules.py:519 lib/solaar/ui/diversion_rules.py:1366 msgid "Modifiers" msgstr "Modificadors" #: lib/solaar/ui/diversion_rules.py:520 lib/solaar/ui/diversion_rules.py:1419 msgid "Key" msgstr "Tecla" #: lib/solaar/ui/diversion_rules.py:521 lib/solaar/ui/diversion_rules.py:1461 msgid "KeyIsDown" msgstr "" #: lib/solaar/ui/diversion_rules.py:522 lib/solaar/ui/diversion_rules.py:2260 msgid "Active" msgstr "Actiu" #: lib/solaar/ui/diversion_rules.py:523 lib/solaar/ui/diversion_rules.py:2218 #: lib/solaar/ui/diversion_rules.py:2270 lib/solaar/ui/diversion_rules.py:2323 msgid "Device" msgstr "" #: lib/solaar/ui/diversion_rules.py:524 lib/solaar/ui/diversion_rules.py:2297 msgid "Host" msgstr "" #: lib/solaar/ui/diversion_rules.py:525 lib/solaar/ui/diversion_rules.py:2339 msgid "Setting" msgstr "" #: lib/solaar/ui/diversion_rules.py:526 lib/solaar/ui/diversion_rules.py:1477 #: lib/solaar/ui/diversion_rules.py:1526 msgid "Test" msgstr "Test" #: lib/solaar/ui/diversion_rules.py:527 lib/solaar/ui/diversion_rules.py:1643 msgid "Test bytes" msgstr "" #: lib/solaar/ui/diversion_rules.py:528 lib/solaar/ui/diversion_rules.py:1736 msgid "Mouse Gesture" msgstr "Gest de Ratolí" #: lib/solaar/ui/diversion_rules.py:532 msgid "Action" msgstr "Acció" #: lib/solaar/ui/diversion_rules.py:534 lib/solaar/ui/diversion_rules.py:1845 msgid "Key press" msgstr "Pulsació de tecla" #: lib/solaar/ui/diversion_rules.py:535 lib/solaar/ui/diversion_rules.py:1897 msgid "Mouse scroll" msgstr "Desplaçament rodeta del ratolí" #: lib/solaar/ui/diversion_rules.py:536 lib/solaar/ui/diversion_rules.py:1959 msgid "Mouse click" msgstr "Clic del ratolí" #: lib/solaar/ui/diversion_rules.py:537 msgid "Set" msgstr "" #: lib/solaar/ui/diversion_rules.py:538 lib/solaar/ui/diversion_rules.py:2030 msgid "Execute" msgstr "Executar" #: lib/solaar/ui/diversion_rules.py:539 lib/solaar/ui/diversion_rules.py:1158 msgid "Later" msgstr "" #: lib/solaar/ui/diversion_rules.py:568 msgid "Insert new rule" msgstr "Afegir nova regla" #: lib/solaar/ui/diversion_rules.py:588 lib/solaar/ui/diversion_rules.py:1686 #: lib/solaar/ui/diversion_rules.py:1790 lib/solaar/ui/diversion_rules.py:1989 msgid "Delete" msgstr "Esborrar" #: lib/solaar/ui/diversion_rules.py:610 msgid "Negate" msgstr "Negar" #: lib/solaar/ui/diversion_rules.py:634 msgid "Wrap with" msgstr "Embolicar amb" #: lib/solaar/ui/diversion_rules.py:656 msgid "Cut" msgstr "Tallar" #: lib/solaar/ui/diversion_rules.py:671 msgid "Paste" msgstr "Enganxar" #: lib/solaar/ui/diversion_rules.py:683 msgid "Copy" msgstr "Copiar" #: lib/solaar/ui/diversion_rules.py:1063 msgid "This editor does not support the selected rule component yet." msgstr "Aquest editor encara no és compatible amb el component de regla " "seleccionat." #: lib/solaar/ui/diversion_rules.py:1138 msgid "Number of seconds to delay." msgstr "" #: lib/solaar/ui/diversion_rules.py:1177 msgid "Not" msgstr "No" #: lib/solaar/ui/diversion_rules.py:1187 msgid "X11 active process. For use in X11 only." msgstr "" #: lib/solaar/ui/diversion_rules.py:1218 msgid "X11 mouse process. For use in X11 only." msgstr "" #: lib/solaar/ui/diversion_rules.py:1235 msgid "MouseProcess" msgstr "ProcésRatolí" #: lib/solaar/ui/diversion_rules.py:1260 msgid "Feature name of notification triggering rule processing." msgstr "" #: lib/solaar/ui/diversion_rules.py:1308 msgid "Report number of notification triggering rule processing." msgstr "" #: lib/solaar/ui/diversion_rules.py:1342 msgid "Active keyboard modifiers. Not always available in Wayland." msgstr "" #: lib/solaar/ui/diversion_rules.py:1383 msgid "Diverted key or button depressed or released.\n" "Use the Key/Button Diversion and Divert G Keys settings to divert " "keys and buttons." msgstr "" #: lib/solaar/ui/diversion_rules.py:1392 msgid "Key down" msgstr "Tecla avall" #: lib/solaar/ui/diversion_rules.py:1395 msgid "Key up" msgstr "Tecla amunt" #: lib/solaar/ui/diversion_rules.py:1436 msgid "Diverted key or button is currently down.\n" "Use the Key/Button Diversion and Divert G Keys settings to divert " "keys and buttons." msgstr "" #: lib/solaar/ui/diversion_rules.py:1475 msgid "Test condition on notification triggering rule processing." msgstr "" #: lib/solaar/ui/diversion_rules.py:1479 msgid "Parameter" msgstr "" #: lib/solaar/ui/diversion_rules.py:1542 msgid "begin (inclusive)" msgstr "" #: lib/solaar/ui/diversion_rules.py:1543 msgid "end (exclusive)" msgstr "" #: lib/solaar/ui/diversion_rules.py:1552 msgid "range" msgstr "" #: lib/solaar/ui/diversion_rules.py:1554 msgid "minimum" msgstr "" #: lib/solaar/ui/diversion_rules.py:1555 msgid "maximum" msgstr "" #: lib/solaar/ui/diversion_rules.py:1557 #, python-format msgid "bytes %(0)d to %(1)d, ranging from %(2)d to %(3)d" msgstr "" #: lib/solaar/ui/diversion_rules.py:1562 msgid "mask" msgstr "" #: lib/solaar/ui/diversion_rules.py:1563 #, python-format msgid "bytes %(0)d to %(1)d, mask %(2)d" msgstr "" #: lib/solaar/ui/diversion_rules.py:1573 msgid "Bit or range test on bytes in notification message triggering rule " "processing." msgstr "" #: lib/solaar/ui/diversion_rules.py:1583 msgid "type" msgstr "" #: lib/solaar/ui/diversion_rules.py:1666 msgid "Mouse gesture with optional initiating button followed by zero or " "more mouse movements." msgstr "" #: lib/solaar/ui/diversion_rules.py:1671 msgid "Add movement" msgstr "" #: lib/solaar/ui/diversion_rules.py:1764 msgid "Simulate a chorded key click or depress or release.\n" "On Wayland requires write access to /dev/uinput." msgstr "" #: lib/solaar/ui/diversion_rules.py:1769 msgid "Add key" msgstr "Afegir tecla" #: lib/solaar/ui/diversion_rules.py:1772 msgid "Click" msgstr "" #: lib/solaar/ui/diversion_rules.py:1775 msgid "Depress" msgstr "" #: lib/solaar/ui/diversion_rules.py:1778 msgid "Release" msgstr "" #: lib/solaar/ui/diversion_rules.py:1861 msgid "Simulate a mouse scroll.\n" "On Wayland requires write access to /dev/uinput." msgstr "" #: lib/solaar/ui/diversion_rules.py:1918 msgid "Simulate a mouse click.\n" "On Wayland requires write access to /dev/uinput." msgstr "" #: lib/solaar/ui/diversion_rules.py:1921 msgid "Button" msgstr "Botó" #: lib/solaar/ui/diversion_rules.py:1922 msgid "Count and Action" msgstr "" #: lib/solaar/ui/diversion_rules.py:1972 msgid "Execute a command with arguments." msgstr "" #: lib/solaar/ui/diversion_rules.py:1975 msgid "Add argument" msgstr "Afegir argument" #: lib/solaar/ui/diversion_rules.py:2050 msgid "Toggle" msgstr "" #: lib/solaar/ui/diversion_rules.py:2050 msgid "True" msgstr "" #: lib/solaar/ui/diversion_rules.py:2051 msgid "False" msgstr "" #: lib/solaar/ui/diversion_rules.py:2065 msgid "Unsupported setting" msgstr "" #: lib/solaar/ui/diversion_rules.py:2223 lib/solaar/ui/diversion_rules.py:2242 #: lib/solaar/ui/diversion_rules.py:2328 lib/solaar/ui/diversion_rules.py:2570 #: lib/solaar/ui/diversion_rules.py:2588 msgid "Originating device" msgstr "" #: lib/solaar/ui/diversion_rules.py:2256 msgid "Device is active and its settings can be changed." msgstr "" #: lib/solaar/ui/diversion_rules.py:2266 msgid "Device that originated the current notification." msgstr "" #: lib/solaar/ui/diversion_rules.py:2280 msgid "Name of host computer." msgstr "" #: lib/solaar/ui/diversion_rules.py:2347 msgid "Value" msgstr "Valor" #: lib/solaar/ui/diversion_rules.py:2355 msgid "Item" msgstr "" #: lib/solaar/ui/diversion_rules.py:2630 msgid "Change setting on device" msgstr "" #: lib/solaar/ui/diversion_rules.py:2647 msgid "Setting on device" msgstr "" #: lib/solaar/ui/notify.py:124 lib/solaar/ui/tray.py:318 #: lib/solaar/ui/tray.py:323 lib/solaar/ui/window.py:739 msgid "offline" msgstr "desconectat" #: lib/solaar/ui/pair_window.py:122 lib/solaar/ui/pair_window.py:256 #: lib/solaar/ui/pair_window.py:288 #, python-format msgid "%(receiver_name)s: pair new device" msgstr "%(receiver_name)s: vincular nou dispositiu" #: lib/solaar/ui/pair_window.py:123 #, python-format msgid "Enter passcode on %(name)s." msgstr "" #: lib/solaar/ui/pair_window.py:126 #, python-format msgid "Type %(passcode)s and then press the enter key." msgstr "" #: lib/solaar/ui/pair_window.py:129 msgid "left" msgstr "" #: lib/solaar/ui/pair_window.py:129 msgid "right" msgstr "" #: lib/solaar/ui/pair_window.py:131 #, python-format msgid "Press %(code)s\n" "and then press left and right buttons simultaneously." msgstr "" #: lib/solaar/ui/pair_window.py:188 msgid "Pairing failed" msgstr "Ha falla la vinculació" #: lib/solaar/ui/pair_window.py:190 msgid "Make sure your device is within range, and has a decent battery " "charge." msgstr "Asseguri's que el seu dispositiu estigui dins l'abast del receptor i " "que la bateria tingui suficient càrrega." #: lib/solaar/ui/pair_window.py:192 msgid "A new device was detected, but it is not compatible with this " "receiver." msgstr "S'ha detectat un nou dispositiu, però no és compatible amb aquest " "receptor." #: lib/solaar/ui/pair_window.py:194 msgid "More paired devices than receiver can support." msgstr "Més dispositius emparellats de els que admet el receptor." #: lib/solaar/ui/pair_window.py:196 msgid "No further details are available about the error." msgstr "No hi ha més detalls disponibles sobre aquest error." #: lib/solaar/ui/pair_window.py:210 msgid "Found a new device:" msgstr "S'ha trobat un nou dispositiu:" #: lib/solaar/ui/pair_window.py:235 msgid "The wireless link is not encrypted" msgstr "La connexió sense fils no és xifrada" #: lib/solaar/ui/pair_window.py:264 msgid "Unifying receivers are only compatible with Unifying devices." msgstr "" #: lib/solaar/ui/pair_window.py:266 msgid "Bolt receivers are only compatible with Bolt devices." msgstr "" #: lib/solaar/ui/pair_window.py:268 msgid "Other receivers are only compatible with a few devices." msgstr "" #: lib/solaar/ui/pair_window.py:270 msgid "The device must not be paired with a nearby powered-on receiver." msgstr "" #: lib/solaar/ui/pair_window.py:274 msgid "Press a pairing button or key until the pairing light flashes " "quickly." msgstr "" #: lib/solaar/ui/pair_window.py:276 msgid "You may have to first turn the device off and on again." msgstr "" #: lib/solaar/ui/pair_window.py:278 msgid "Turn on the device you want to pair." msgstr "Engegui el dispositiu que vol vincular." #: lib/solaar/ui/pair_window.py:280 msgid "If the device is already turned on, turn it off and on again." msgstr "Si el dispositiu ja està encès, apagui'l i torni'l a encendre." #: lib/solaar/ui/pair_window.py:283 #, python-format msgid "\n" "\n" "This receiver has %d pairing remaining." msgid_plural "\n" "\n" "This receiver has %d pairings remaining." msgstr[0] "\n" "\n" "Aquest receptor té %d aparellament restant." msgstr[1] "\n" "\n" "Aquest receptor té %d aparellaments restants." #: lib/solaar/ui/pair_window.py:286 msgid "\n" "Cancelling at this point will not use up a pairing." msgstr "\n" "Si cancel·la en aquest punt no es farà servir un aparellament." #: lib/solaar/ui/tray.py:58 msgid "No supported device found" msgstr "" #: lib/solaar/ui/tray.py:64 lib/solaar/ui/window.py:319 #, python-format msgid "About %s" msgstr "Sobre %s" #: lib/solaar/ui/tray.py:65 lib/solaar/ui/window.py:317 #, python-format msgid "Quit %s" msgstr "Sortir %s" #: lib/solaar/ui/tray.py:297 lib/solaar/ui/tray.py:305 msgid "no receiver" msgstr "sense receptor" #: lib/solaar/ui/tray.py:321 msgid "no status" msgstr "sense estat" #: lib/solaar/ui/window.py:96 msgid "Scanning" msgstr "Explorant" #: lib/solaar/ui/window.py:129 msgid "Battery" msgstr "Bateria" #: lib/solaar/ui/window.py:132 msgid "Wireless Link" msgstr "Enllaç sense fils" #: lib/solaar/ui/window.py:136 msgid "Lighting" msgstr "Il·luminació" #: lib/solaar/ui/window.py:170 msgid "Show Technical Details" msgstr "Mostrar detalls tècnics" #: lib/solaar/ui/window.py:186 msgid "Pair new device" msgstr "Vincular nou dispositiu" #: lib/solaar/ui/window.py:205 msgid "Select a device" msgstr "Seleccionar un dispositiu" #: lib/solaar/ui/window.py:322 msgid "Rule Editor" msgstr "Editor de Regles" #: lib/solaar/ui/window.py:533 msgid "Path" msgstr "Ruta" #: lib/solaar/ui/window.py:536 msgid "USB ID" msgstr "ID USB" #: lib/solaar/ui/window.py:539 lib/solaar/ui/window.py:541 #: lib/solaar/ui/window.py:561 lib/solaar/ui/window.py:563 msgid "Serial" msgstr "Serial" #: lib/solaar/ui/window.py:545 msgid "Index" msgstr "Índex" #: lib/solaar/ui/window.py:547 msgid "Wireless PID" msgstr "PID sense fils" #: lib/solaar/ui/window.py:549 msgid "Product ID" msgstr "ID del producte" #: lib/solaar/ui/window.py:551 msgid "Protocol" msgstr "Protocol" #: lib/solaar/ui/window.py:551 msgid "Unknown" msgstr "Desconegut" #: lib/solaar/ui/window.py:554 #, python-format msgid "%(rate)d ms (%(rate_hz)dHz)" msgstr "%(rate)d ms (%(rate_hz)dHz)" #: lib/solaar/ui/window.py:554 msgid "Polling rate" msgstr "Taxa de sondeig" #: lib/solaar/ui/window.py:565 msgid "Unit ID" msgstr "ID Unitat" #: lib/solaar/ui/window.py:576 msgid "none" msgstr "cap" #: lib/solaar/ui/window.py:577 msgid "Notifications" msgstr "Notificacions" #: lib/solaar/ui/window.py:621 msgid "No device paired." msgstr "No hi ha dispositius vinculats." #: lib/solaar/ui/window.py:628 #, python-format msgid "Up to %(max_count)s device can be paired to this receiver." msgid_plural "Up to %(max_count)s devices can be paired to this receiver." msgstr[0] "Es pot vincular fins a %(max_count)s dispositiu a aquest " "receptor." msgstr[1] "Es pot vincular fins a %(max_count)s dispositius a aquest " "receptor." #: lib/solaar/ui/window.py:634 msgid "Only one device can be paired to this receiver." msgstr "Només es pot vincular un dispositiu a aquest receptor." #: lib/solaar/ui/window.py:638 #, python-format msgid "This receiver has %d pairing remaining." msgid_plural "This receiver has %d pairings remaining." msgstr[0] "Aquest receptor té %d aparellament restant." msgstr[1] "Aquest receptor té %d aparellaments restants." #: lib/solaar/ui/window.py:692 msgid "Battery Voltage" msgstr "Voltatge de la bateria" #: lib/solaar/ui/window.py:694 msgid "Voltage reported by battery" msgstr "Voltatge informat per la bateria" #: lib/solaar/ui/window.py:696 msgid "Battery Level" msgstr "Nivell de Bateria" #: lib/solaar/ui/window.py:698 msgid "Approximate level reported by battery" msgstr "Nivell aproximat informat per la bateria" #: lib/solaar/ui/window.py:705 lib/solaar/ui/window.py:707 msgid "next reported " msgstr "següent informe " #: lib/solaar/ui/window.py:708 msgid " and next level to be reported." msgstr " i següent nivell que serà informat." #: lib/solaar/ui/window.py:713 msgid "last known" msgstr "últim conegut" #: lib/solaar/ui/window.py:724 msgid "encrypted" msgstr "xifrat" #: lib/solaar/ui/window.py:726 msgid "The wireless link between this device and its receiver is encrypted." msgstr "La connexió sense fils entre el dispositiu i el seu receptor està " "xifrada." #: lib/solaar/ui/window.py:728 msgid "not encrypted" msgstr "no xifrat" #: lib/solaar/ui/window.py:732 msgid "The wireless link between this device and its receiver is not " "encrypted.\n" "This is a security issue for pointing devices, and a major security " "issue for text-input devices." msgstr "" #: lib/solaar/ui/window.py:748 #, python-format msgid "%(light_level)d lux" msgstr "%(light_level)d lux" #~ msgid "\n" #~ "\n" #~ "This receiver has %d pairing(s) remaining." #~ msgstr "\n" #~ "\n" #~ "Aquest receptor té %d aparellament(s) disponibles." #~ msgid "%(battery_level)s" #~ msgstr "%(battery_level)s" #~ msgid "%(battery_percent)d%%" #~ msgstr "%(battery_percent)d%%" #~ msgid "Add action" #~ msgstr "Afegir acció" #~ msgid "Adjust the DPI by sliding the mouse horizontally while " #~ "holding the button down." #~ msgstr "Ajustar els DPI lliscant el ratolí horitzontalment mentre es " #~ "manté el botó pressionat." #~ msgid "Automatically switch the mouse wheel between ratchet and " #~ "freespin mode.\n" #~ "The mouse wheel is always free at 0, and always locked at 50" #~ msgstr "Canvia automàticament el mode de la rodeta del ratolí entre " #~ "bloquejat i gir lliure.\n" #~ "La rodeta del ratolí sempre està lliure a 0 i sempre bloquejat a 50" #~ msgid "Automatically switch the mouse wheel between ratchet and " #~ "freespin mode.\n" #~ "The mouse wheel is always free at 0, and always ratcheted at 50" #~ msgstr "Canviar automàticament la rodeta del ratolí entre el mode de " #~ "trinquet i el mode de gir lliure.\n" #~ "La rodeta del ratolí sempre està lliure a 0 i sempre amb trinquet a " #~ "50" #~ msgid "Battery information unknown." #~ msgstr "Informació de la bateria desconeguda." #~ msgid "Count" #~ msgstr "Conta" #~ msgid "DPI Sliding Adjustment" #~ msgstr "Ajustar DPI lliscant" #~ msgid "Effectively turns off thumb scrolling in Linux." #~ msgstr "Desactiva eficaçment el desplaçament amb el polze en Linux." #~ msgid "Effectively turns off wheel scrolling in Linux." #~ msgstr "Desactiva eficaçment l'scroll de la rodeta amb Linux." #, python-format #~ msgid "Found a Logitech Receiver (%s), but did not have permission " #~ "to open it." #~ msgstr "S'ha trobat receptor Logitech (%s), però no té permisos per " #~ "obrir-lo." #~ msgid "HID++ Scrolling" #~ msgstr "Desplaçament HID++" #~ msgid "HID++ mode for horizontal scroll with the thumb wheel." #~ msgstr "Mode HID++ per desplaçament horitzontal amb la rodeta del " #~ "polze." #~ msgid "HID++ mode for vertical scroll with the wheel." #~ msgstr "Mode HID++ pel desplaçament vertical amb la rodeta." #~ msgid "High Resolution Scrolling" #~ msgstr "Desplaçament d'alta resolució" #~ msgid "High Resolution Wheel Invert" #~ msgstr "Invertir rodeta a l'alta resolució " #~ msgid "High-sensitivity wheel invert mode for vertical scroll." #~ msgstr "Mode invers d'alta resolució a la rodeta per desplaçament " #~ "vertical." #~ msgid "If the device is already turned on, turn if off and on again." #~ msgstr "Si el dispositiu ja està encès, apagui'l i torni'l a " #~ "encendre." #~ msgid "If you've just installed Solaar, try removing the receiver " #~ "and plugging it back in." #~ msgstr "Si acaba d'instal·lar Solaar, intenta treure el receptor i " #~ "connectar-lo de nou." #~ msgid "Make the key or button send HID++ notifications (which " #~ "trigger Solaar rules but are otherwise ignored)." #~ msgstr "Fer que la tecla o el botó enviï notificacions HID ++ (que " #~ "activen les regles de Solaar, del contrari s'ignoren)." #~ msgid "No Logitech receiver found" #~ msgstr "No s'ha trobat cap receptor Logitech" #~ msgid "Scroll Wheel Rachet" #~ msgstr "Trinquet Rodeta Desplaçament" #~ msgid "Send a gesture by sliding the mouse while holding the button " #~ "down." #~ msgstr "Enviar un gest lliscant el ratolí mentre manté pressionat el " #~ "botó." #~ msgid "Shows status of devices connected\n" #~ "through wireless Logitech receivers." #~ msgstr "Mostrar l'estat de dispositius connectats\n" #~ "mitjançant receptors sense fils Logitech." #~ msgid "Smooth Scrolling" #~ msgstr "Desplaçament suau" #~ msgid "The receiver only supports %d paired device(s)." #~ msgstr "El receptor només soporta %d dispositiu(s) aparellats(s)." #~ msgid "The receiver was unplugged." #~ msgstr "El receptor s'ha desconnectat." #~ msgid "The wireless link between this device and its receiver is " #~ "not encrypted.\n" #~ "\n" #~ "For pointing devices (mice, trackballs, trackpads), this is a minor " #~ "security issue.\n" #~ "\n" #~ "It is, however, a major security issue for text-input devices " #~ "(keyboards, numpads),\n" #~ "because typed text can be sniffed inconspicuously by 3rd parties " #~ "within range." #~ msgstr "La conexió inalámbrica entre el dispositiu i el receptor no " #~ "està cifrada.\n" #~ "\n" #~ "Per dispositius apuntadors (ratolins, trackballs, trackpads), aquest " #~ "és un problema menor de seguretat.\n" #~ "\n" #~ "Tot i això, per dispositius d'entrada de text (teclats, teclats " #~ "numèrics) sí que és un problema greu,\n" #~ "doncs el text introduït pot ser capturat de forma inadvertida per " #~ "tercers que estiguin aprop." #~ msgid "This receiver has %d pairing(s) remaining." #~ msgstr "Este receptor té %d aparellament(s) disponibles." #~ msgid "Top-most coordinate." #~ msgstr "Coordenada superior." #~ msgid "Try removing the device and plugging it back in or turning " #~ "it off and then on." #~ msgstr "Intenti treure el dispositiu i tornar a endollar-lo o apagar-" #~ "lo i encendre'l." #~ msgid "USB id" #~ msgstr "id USB" #~ msgid "Wheel Resolution" #~ msgstr "Resolució de la rodeta" #~ msgid "height" #~ msgstr "alçada" #~ msgid "top" #~ msgstr "superior" #~ msgid "unknown" #~ msgstr "desconegut" #~ msgid "width" #~ msgstr "amplada" Solaar-1.1.14/po/cs.po000066400000000000000000001521061473525213700144200ustar00rootroot00000000000000# Czech translations for solaar package. # Copyright (C) 2020 THE solaar'S COPYRIGHT HOLDER # This file is distributed under the same license as the solaar package. # # Automatically generated, 2020. # Marián Kyral , 2020. msgid "" msgstr "Project-Id-Version: solaar 1.0.4\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-12-28 17:40+0100\n" "PO-Revision-Date: 2020-12-30 17:48+0100\n" "Last-Translator: Marián Kyral \n" "Language-Team: Czech \n" "Language: cs\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : " "2;\n" "X-Generator: Lokalize 20.12.0\n" #: lib/logitech_receiver/base_usb.py:46 msgid "Bolt Receiver" msgstr "" #: lib/logitech_receiver/base_usb.py:57 msgid "Unifying Receiver" msgstr "" #: lib/logitech_receiver/base_usb.py:67 lib/logitech_receiver/base_usb.py:78 #: lib/logitech_receiver/base_usb.py:90 lib/logitech_receiver/base_usb.py:102 #: lib/logitech_receiver/base_usb.py:114 msgid "Nano Receiver" msgstr "" #: lib/logitech_receiver/base_usb.py:124 msgid "Lightspeed Receiver" msgstr "" #: lib/logitech_receiver/base_usb.py:133 msgid "EX100 Receiver 27 Mhz" msgstr "" #: lib/logitech_receiver/i18n.py:30 msgid "empty" msgstr "prázdná" #: lib/logitech_receiver/i18n.py:31 msgid "critical" msgstr "kritická" #: lib/logitech_receiver/i18n.py:32 msgid "low" msgstr "nízká" #: lib/logitech_receiver/i18n.py:33 msgid "average" msgstr "" #: lib/logitech_receiver/i18n.py:34 msgid "good" msgstr "dobrá" #: lib/logitech_receiver/i18n.py:35 msgid "full" msgstr "plně nabito" #: lib/logitech_receiver/i18n.py:38 msgid "discharging" msgstr "vybíjení" #: lib/logitech_receiver/i18n.py:39 msgid "recharging" msgstr "nabíjení" #: lib/logitech_receiver/i18n.py:40 lib/solaar/ui/window.py:711 msgid "charging" msgstr "nabíjení" #: lib/logitech_receiver/i18n.py:41 msgid "not charging" msgstr "" #: lib/logitech_receiver/i18n.py:42 msgid "almost full" msgstr "téměř nabito" #: lib/logitech_receiver/i18n.py:43 msgid "charged" msgstr "nabito" #: lib/logitech_receiver/i18n.py:44 msgid "slow recharge" msgstr "pomalé nabíjení" #: lib/logitech_receiver/i18n.py:45 msgid "invalid battery" msgstr "vadná baterie" #: lib/logitech_receiver/i18n.py:46 msgid "thermal error" msgstr "teplotní chyba" #: lib/logitech_receiver/i18n.py:47 msgid "error" msgstr "" #: lib/logitech_receiver/i18n.py:48 msgid "standard" msgstr "" #: lib/logitech_receiver/i18n.py:49 msgid "fast" msgstr "" #: lib/logitech_receiver/i18n.py:50 msgid "slow" msgstr "" #: lib/logitech_receiver/i18n.py:53 msgid "device timeout" msgstr "vypršel časový limit zařízení" #: lib/logitech_receiver/i18n.py:54 msgid "device not supported" msgstr "nepodporované zařízení" #: lib/logitech_receiver/i18n.py:55 msgid "too many devices" msgstr "příliš mnoho zařízení" #: lib/logitech_receiver/i18n.py:56 msgid "sequence timeout" msgstr "vypršel časový limit sekvence" #: lib/logitech_receiver/i18n.py:59 lib/solaar/ui/window.py:572 msgid "Firmware" msgstr "Firmware" #: lib/logitech_receiver/i18n.py:60 msgid "Bootloader" msgstr "Bootloader" #: lib/logitech_receiver/i18n.py:61 msgid "Hardware" msgstr "Hardware" #: lib/logitech_receiver/i18n.py:62 msgid "Other" msgstr "Další" #: lib/logitech_receiver/i18n.py:65 msgid "Left Button" msgstr "" #: lib/logitech_receiver/i18n.py:66 msgid "Right Button" msgstr "" #: lib/logitech_receiver/i18n.py:67 msgid "Middle Button" msgstr "" #: lib/logitech_receiver/i18n.py:68 msgid "Back Button" msgstr "" #: lib/logitech_receiver/i18n.py:69 msgid "Forward Button" msgstr "" #: lib/logitech_receiver/i18n.py:70 msgid "Mouse Gesture Button" msgstr "" #: lib/logitech_receiver/i18n.py:71 msgid "Smart Shift" msgstr "" #: lib/logitech_receiver/i18n.py:72 msgid "DPI Switch" msgstr "" #: lib/logitech_receiver/i18n.py:73 msgid "Left Tilt" msgstr "" #: lib/logitech_receiver/i18n.py:74 msgid "Right Tilt" msgstr "" #: lib/logitech_receiver/i18n.py:75 msgid "Left Click" msgstr "" #: lib/logitech_receiver/i18n.py:76 msgid "Right Click" msgstr "" #: lib/logitech_receiver/i18n.py:77 msgid "Mouse Middle Button" msgstr "" #: lib/logitech_receiver/i18n.py:78 msgid "Mouse Back Button" msgstr "" #: lib/logitech_receiver/i18n.py:79 msgid "Mouse Forward Button" msgstr "" #: lib/logitech_receiver/i18n.py:80 msgid "Gesture Button Navigation" msgstr "" #: lib/logitech_receiver/i18n.py:81 msgid "Mouse Scroll Left Button" msgstr "" #: lib/logitech_receiver/i18n.py:82 msgid "Mouse Scroll Right Button" msgstr "" #: lib/logitech_receiver/i18n.py:85 msgid "pressed" msgstr "" #: lib/logitech_receiver/i18n.py:86 msgid "released" msgstr "" #: lib/logitech_receiver/notifications.py:75 #: lib/logitech_receiver/notifications.py:126 msgid "pairing lock is closed" msgstr "zámek připojení byl uzavřen" #: lib/logitech_receiver/notifications.py:75 #: lib/logitech_receiver/notifications.py:126 msgid "pairing lock is open" msgstr "zámek připojení byl otevřen" #: lib/logitech_receiver/notifications.py:92 msgid "discovery lock is closed" msgstr "" #: lib/logitech_receiver/notifications.py:92 msgid "discovery lock is open" msgstr "" #: lib/logitech_receiver/notifications.py:224 lib/solaar/ui/notify.py:122 msgid "connected" msgstr "připojeno" #: lib/logitech_receiver/notifications.py:224 msgid "disconnected" msgstr "odpojeno" #: lib/logitech_receiver/notifications.py:262 lib/solaar/ui/notify.py:120 msgid "unpaired" msgstr "nepřipojeno" #: lib/logitech_receiver/notifications.py:304 msgid "powered on" msgstr "zapnuto" #: lib/logitech_receiver/settings.py:750 msgid "register" msgstr "" #: lib/logitech_receiver/settings.py:764 lib/logitech_receiver/settings.py:791 msgid "feature" msgstr "" #: lib/logitech_receiver/settings_templates.py:139 msgid "Swap Fx function" msgstr "Přepnout Fx funkci" #: lib/logitech_receiver/settings_templates.py:140 msgid "When set, the F1..F12 keys will activate their special function,\n" "and you must hold the FN key to activate their standard function." msgstr "Pokud je nastaveno, budou klávesy F1..F12 spouštět jejich speciální " "funkce\n" "a musíte držet Fn abyste aktivovali jejich standardní funkce." #: lib/logitech_receiver/settings_templates.py:142 msgid "When unset, the F1..F12 keys will activate their standard function,\n" "and you must hold the FN key to activate their special function." msgstr "Pokud není nastaveno, budou klávesy F1..F12 spouštět jejich " "standardní funkce\n" "a musíte držet Fn abyste aktivovali jejich speciální funkce." #: lib/logitech_receiver/settings_templates.py:149 msgid "Hand Detection" msgstr "Detekce rukou" #: lib/logitech_receiver/settings_templates.py:150 msgid "Turn on illumination when the hands hover over the keyboard." msgstr "Zapne podsvícení pokud se ruce objeví nad klávesnicí." #: lib/logitech_receiver/settings_templates.py:157 msgid "Scroll Wheel Smooth Scrolling" msgstr "Plynulý posuv kolečkem" #: lib/logitech_receiver/settings_templates.py:158 #: lib/logitech_receiver/settings_templates.py:239 #: lib/logitech_receiver/settings_templates.py:267 msgid "High-sensitivity mode for vertical scroll with the wheel." msgstr "Vysoce citlivý režim svislého posuvu kolečkem." #: lib/logitech_receiver/settings_templates.py:165 msgid "Side Scrolling" msgstr "Boční posuv" #: lib/logitech_receiver/settings_templates.py:167 msgid "When disabled, pushing the wheel sideways sends custom button " "events\n" "instead of the standard side-scrolling events." msgstr "Pokud je zakázáno, boční stlačení kolečka pošle vlastní události " "tlačítek\n" "místo standardních událostí bočního posuvu." #: lib/logitech_receiver/settings_templates.py:177 msgid "Sensitivity (DPI - older mice)" msgstr "" #: lib/logitech_receiver/settings_templates.py:178 #: lib/logitech_receiver/settings_templates.py:712 msgid "Mouse movement sensitivity" msgstr "Citlivost kurzoru myši" #: lib/logitech_receiver/settings_templates.py:208 #: lib/logitech_receiver/settings_templates.py:218 #: lib/logitech_receiver/settings_templates.py:225 msgid "Backlight" msgstr "Podsvícení" #: lib/logitech_receiver/settings_templates.py:209 #: lib/logitech_receiver/settings_templates.py:226 msgid "Set illumination time for keyboard." msgstr "" #: lib/logitech_receiver/settings_templates.py:219 msgid "Turn illumination on or off on keyboard." msgstr "Vypne/zapne podsvícení klávesnice." #: lib/logitech_receiver/settings_templates.py:237 msgid "Scroll Wheel High Resolution" msgstr "Vysoké rozlišení posuvného kolečka" #: lib/logitech_receiver/settings_templates.py:240 #: lib/logitech_receiver/settings_templates.py:268 msgid "Set to ignore if scrolling is abnormally fast or slow" msgstr "" #: lib/logitech_receiver/settings_templates.py:247 #: lib/logitech_receiver/settings_templates.py:277 msgid "Scroll Wheel Diversion" msgstr "" #: lib/logitech_receiver/settings_templates.py:249 msgid "Make scroll wheel send LOWRES_WHEEL HID++ notifications (which " "trigger Solaar rules but are otherwise ignored)." msgstr "" #: lib/logitech_receiver/settings_templates.py:256 msgid "Scroll Wheel Direction" msgstr "Směr kolečka" #: lib/logitech_receiver/settings_templates.py:257 msgid "Invert direction for vertical scroll with wheel." msgstr "Mění směr svislého otáčení kolečka." #: lib/logitech_receiver/settings_templates.py:265 msgid "Scroll Wheel Resolution" msgstr "Rozlišení posuvného kolečka" #: lib/logitech_receiver/settings_templates.py:279 msgid "Make scroll wheel send HIRES_WHEEL HID++ notifications (which " "trigger Solaar rules but are otherwise ignored)." msgstr "" #: lib/logitech_receiver/settings_templates.py:288 msgid "Sensitivity (Pointer Speed)" msgstr "Citlivost (rychlost kurzoru)" #: lib/logitech_receiver/settings_templates.py:289 msgid "Speed multiplier for mouse (256 is normal multiplier)." msgstr "Rychlostní násobitel myši (256 je běžný násobitel)." #: lib/logitech_receiver/settings_templates.py:299 msgid "Thumb Wheel Diversion" msgstr "" #: lib/logitech_receiver/settings_templates.py:301 msgid "Make thumb wheel send THUMB_WHEEL HID++ notifications (which trigger " "Solaar rules but are otherwise ignored)." msgstr "" #: lib/logitech_receiver/settings_templates.py:310 msgid "Thumb Wheel Direction" msgstr "Směr posuvného kolečka" #: lib/logitech_receiver/settings_templates.py:311 msgid "Invert thumb wheel scroll direction." msgstr "Mění směr otáčení kolečka." #: lib/logitech_receiver/settings_templates.py:319 msgid "Onboard Profiles" msgstr "" #: lib/logitech_receiver/settings_templates.py:320 msgid "Enable onboard profiles, which often control report rate and " "keyboard lighting" msgstr "" #: lib/logitech_receiver/settings_templates.py:330 msgid "Polling Rate (ms)" msgstr "" #: lib/logitech_receiver/settings_templates.py:332 msgid "Frequency of device polling, in milliseconds" msgstr "" #: lib/logitech_receiver/settings_templates.py:333 #: lib/logitech_receiver/settings_templates.py:1046 #: lib/logitech_receiver/settings_templates.py:1076 msgid "May need Onboard Profiles set to Disable to be effective." msgstr "" #: lib/logitech_receiver/settings_templates.py:365 msgid "Divert crown events" msgstr "Odklonění událostí korunky" #: lib/logitech_receiver/settings_templates.py:366 msgid "Make crown send CROWN HID++ notifications (which trigger Solaar " "rules but are otherwise ignored)." msgstr "Korunka bude zasílat CROWN HID++ oznámení (která spustí Solaar " "pravidla, ale jinak budou ignorována)." #: lib/logitech_receiver/settings_templates.py:374 msgid "Crown smooth scroll" msgstr "" #: lib/logitech_receiver/settings_templates.py:375 msgid "Set crown smooth scroll" msgstr "" #: lib/logitech_receiver/settings_templates.py:383 msgid "Divert G Keys" msgstr "Odklonit G klávesy" #: lib/logitech_receiver/settings_templates.py:385 msgid "Make G keys send GKEY HID++ notifications (which trigger Solaar " "rules but are otherwise ignored)." msgstr "G klávesy budou zasílat GKEY HID++ oznámení (která spustí Solaar " "pravidla, ale jinak budou ignorována)." #: lib/logitech_receiver/settings_templates.py:386 msgid "May also make M keys and MR key send HID++ notifications" msgstr "" #: lib/logitech_receiver/settings_templates.py:402 msgid "Scroll Wheel Ratcheted" msgstr "" #: lib/logitech_receiver/settings_templates.py:403 msgid "Switch the mouse wheel between speed-controlled ratcheting and " "always freespin." msgstr "" #: lib/logitech_receiver/settings_templates.py:405 msgid "Freespinning" msgstr "" #: lib/logitech_receiver/settings_templates.py:405 msgid "Ratcheted" msgstr "" #: lib/logitech_receiver/settings_templates.py:412 msgid "Scroll Wheel Ratchet Speed" msgstr "" #: lib/logitech_receiver/settings_templates.py:414 msgid "Use the mouse wheel speed to switch between ratcheted and " "freespinning.\n" "The mouse wheel is always ratcheted at 50." msgstr "" #: lib/logitech_receiver/settings_templates.py:463 msgid "Key/Button Actions" msgstr "Akce klávesy/tlačítka" #: lib/logitech_receiver/settings_templates.py:465 msgid "Change the action for the key or button." msgstr "Změní akci klávesy nebo tlačítka." #: lib/logitech_receiver/settings_templates.py:465 msgid "Overridden by diversion." msgstr "" #: lib/logitech_receiver/settings_templates.py:466 msgid "Changing important actions (such as for the left mouse button) can " "result in an unusable system." msgstr "Změna důležité akce (například pro levé tlačítko myši) může vést k " "nepoužitelnému systému." #: lib/logitech_receiver/settings_templates.py:639 msgid "Key/Button Diversion" msgstr "Odklonění klávesy/tlačítka" #: lib/logitech_receiver/settings_templates.py:640 msgid "Make the key or button send HID++ notifications (Diverted) or " "initiate Mouse Gestures or Sliding DPI" msgstr "" #: lib/logitech_receiver/settings_templates.py:643 #: lib/logitech_receiver/settings_templates.py:644 #: lib/logitech_receiver/settings_templates.py:645 msgid "Diverted" msgstr "" #: lib/logitech_receiver/settings_templates.py:643 #: lib/logitech_receiver/settings_templates.py:644 msgid "Mouse Gestures" msgstr "" #: lib/logitech_receiver/settings_templates.py:643 #: lib/logitech_receiver/settings_templates.py:644 #: lib/logitech_receiver/settings_templates.py:645 msgid "Regular" msgstr "" #: lib/logitech_receiver/settings_templates.py:643 msgid "Sliding DPI" msgstr "" #: lib/logitech_receiver/settings_templates.py:711 msgid "Sensitivity (DPI)" msgstr "Citlivost (DPI)" #: lib/logitech_receiver/settings_templates.py:752 msgid "Sensitivity Switching" msgstr "" #: lib/logitech_receiver/settings_templates.py:754 msgid "Switch the current sensitivity and the remembered sensitivity when " "the key or button is pressed.\n" "If there is no remembered sensitivity, just remember the current " "sensitivity" msgstr "" #: lib/logitech_receiver/settings_templates.py:758 msgid "Off" msgstr "" #: lib/logitech_receiver/settings_templates.py:791 msgid "Disable keys" msgstr "Zakázat klávesy" #: lib/logitech_receiver/settings_templates.py:792 msgid "Disable specific keyboard keys." msgstr "Zakáže specifické klávesy." #: lib/logitech_receiver/settings_templates.py:795 #, python-format msgid "Disables the %s key." msgstr "Zakáže %s klávesu." #: lib/logitech_receiver/settings_templates.py:809 #: lib/logitech_receiver/settings_templates.py:860 msgid "Set OS" msgstr "Nastavit OS" #: lib/logitech_receiver/settings_templates.py:810 #: lib/logitech_receiver/settings_templates.py:861 msgid "Change keys to match OS." msgstr "Změní klávesy dle operačního systému." #: lib/logitech_receiver/settings_templates.py:873 msgid "Change Host" msgstr "Změnit hostitele" #: lib/logitech_receiver/settings_templates.py:874 msgid "Switch connection to a different host" msgstr "Přepnout spojení na jiného hostitele" #: lib/logitech_receiver/settings_templates.py:900 msgid "Performs a left click." msgstr "Provede levý klik." #: lib/logitech_receiver/settings_templates.py:900 msgid "Single tap" msgstr "Jednoduché kliknutí" #: lib/logitech_receiver/settings_templates.py:901 msgid "Performs a right click." msgstr "Provede pravý klik." #: lib/logitech_receiver/settings_templates.py:901 msgid "Single tap with two fingers" msgstr "Jedno klepnutí dvěma prsty" #: lib/logitech_receiver/settings_templates.py:902 msgid "Single tap with three fingers" msgstr "Jedno klepnutí třemi prsty" #: lib/logitech_receiver/settings_templates.py:906 msgid "Double tap" msgstr "\t" #: lib/logitech_receiver/settings_templates.py:906 msgid "Performs a double click." msgstr "Provede dvouklik." #: lib/logitech_receiver/settings_templates.py:907 msgid "Double tap with two fingers" msgstr "Dvojité klepnutí dvěma prsty" #: lib/logitech_receiver/settings_templates.py:908 msgid "Double tap with three fingers" msgstr "Dvojité klepnutí třemi prsty" #: lib/logitech_receiver/settings_templates.py:911 msgid "Drags items by dragging the finger after double tapping." msgstr "Táhnutí položek tažením prstu po dvojitém poklepání." #: lib/logitech_receiver/settings_templates.py:911 msgid "Tap and drag" msgstr "Táhni a pusť" #: lib/logitech_receiver/settings_templates.py:913 msgid "Drags items by dragging the fingers after double tapping." msgstr "Táhnutí položek tažením prstů po dvojitém poklepání." #: lib/logitech_receiver/settings_templates.py:913 msgid "Tap and drag with two fingers" msgstr "Klepnutí a táhnutí dvěma prsty" #: lib/logitech_receiver/settings_templates.py:914 msgid "Tap and drag with three fingers" msgstr "Klepnutí a táhnutí třemi prsty" #: lib/logitech_receiver/settings_templates.py:917 msgid "Disables tap and edge gestures (equivalent to pressing Fn+LeftClick)." msgstr "Potlačí gesta klepnutí a hrany (ekvivalent stisknutí Fn + levý klik)." #: lib/logitech_receiver/settings_templates.py:917 msgid "Suppress tap and edge gestures" msgstr "Potlačit gesta klepnutí a hrany" #: lib/logitech_receiver/settings_templates.py:918 msgid "Scroll with one finger" msgstr "Posuv jedním prstem." #: lib/logitech_receiver/settings_templates.py:918 #: lib/logitech_receiver/settings_templates.py:919 #: lib/logitech_receiver/settings_templates.py:922 msgid "Scrolls." msgstr "Posuvy." #: lib/logitech_receiver/settings_templates.py:919 #: lib/logitech_receiver/settings_templates.py:922 msgid "Scroll with two fingers" msgstr "Posuv dvěma prsty" #: lib/logitech_receiver/settings_templates.py:920 msgid "Scroll horizontally with two fingers" msgstr "Vodorovný posuv dvěma prsty" #: lib/logitech_receiver/settings_templates.py:920 msgid "Scrolls horizontally." msgstr "Vodorovný posuv." #: lib/logitech_receiver/settings_templates.py:921 msgid "Scroll vertically with two fingers" msgstr "Svislý posuv dvěma prsty" #: lib/logitech_receiver/settings_templates.py:921 msgid "Scrolls vertically." msgstr "Svislý posuv." #: lib/logitech_receiver/settings_templates.py:923 msgid "Inverts the scrolling direction." msgstr "Mění směr otáčení." #: lib/logitech_receiver/settings_templates.py:923 msgid "Natural scrolling" msgstr "Přirozený posuv" #: lib/logitech_receiver/settings_templates.py:924 msgid "Enables the thumbwheel." msgstr "Povolí kolečko." #: lib/logitech_receiver/settings_templates.py:924 msgid "Thumbwheel" msgstr "Kolečko" #: lib/logitech_receiver/settings_templates.py:935 #: lib/logitech_receiver/settings_templates.py:939 msgid "Swipe from the top edge" msgstr "Táhnutí z horní hrany" #: lib/logitech_receiver/settings_templates.py:936 msgid "Swipe from the left edge" msgstr "Táhnutí z levé hrany" #: lib/logitech_receiver/settings_templates.py:937 msgid "Swipe from the right edge" msgstr "Táhnutí z pravé hrany" #: lib/logitech_receiver/settings_templates.py:938 msgid "Swipe from the bottom edge" msgstr "Táhnutí ze spodní hrany" #: lib/logitech_receiver/settings_templates.py:940 msgid "Swipe two fingers from the left edge" msgstr "Táhnutí dvěma prsty z levé hrany" #: lib/logitech_receiver/settings_templates.py:941 msgid "Swipe two fingers from the right edge" msgstr "Táhnutí dvěma prsty z pravé hrany" #: lib/logitech_receiver/settings_templates.py:942 msgid "Swipe two fingers from the bottom edge" msgstr "Táhnutí dvěma prsty ze spodní hrany" #: lib/logitech_receiver/settings_templates.py:943 msgid "Swipe two fingers from the top edge" msgstr "Táhnutí dvěma prsty z horní hrany" #: lib/logitech_receiver/settings_templates.py:944 #: lib/logitech_receiver/settings_templates.py:948 msgid "Pinch to zoom out; spread to zoom in." msgstr "Sevření pro přiblížení; rozevření pro oddálení." #: lib/logitech_receiver/settings_templates.py:944 msgid "Zoom with two fingers." msgstr "Přiblížení dvěma prsty." #: lib/logitech_receiver/settings_templates.py:945 msgid "Pinch to zoom out." msgstr "Sevřít pro přiblížení." #: lib/logitech_receiver/settings_templates.py:946 msgid "Spread to zoom in." msgstr "Rozevřít pro oddálení." #: lib/logitech_receiver/settings_templates.py:947 msgid "Zoom with three fingers." msgstr "Přiblížení třemi prsty." #: lib/logitech_receiver/settings_templates.py:948 msgid "Zoom with two fingers" msgstr "Přiblížení dvěma prsty" #: lib/logitech_receiver/settings_templates.py:966 msgid "Pixel zone" msgstr "" #: lib/logitech_receiver/settings_templates.py:967 msgid "Ratio zone" msgstr "" #: lib/logitech_receiver/settings_templates.py:968 msgid "Scale factor" msgstr "Faktor škálování" #: lib/logitech_receiver/settings_templates.py:968 msgid "Sets the cursor speed." msgstr "" #: lib/logitech_receiver/settings_templates.py:972 msgid "Left" msgstr "Vlevo" #: lib/logitech_receiver/settings_templates.py:972 msgid "Left-most coordinate." msgstr "Souřadnice nejvíce vlevo." #: lib/logitech_receiver/settings_templates.py:973 msgid "Bottom" msgstr "" #: lib/logitech_receiver/settings_templates.py:973 msgid "Bottom coordinate." msgstr "" #: lib/logitech_receiver/settings_templates.py:974 msgid "Width" msgstr "Šířka" #: lib/logitech_receiver/settings_templates.py:974 msgid "Width." msgstr "Šířka." #: lib/logitech_receiver/settings_templates.py:975 msgid "Height" msgstr "Výška" #: lib/logitech_receiver/settings_templates.py:975 msgid "Height." msgstr "Výška." #: lib/logitech_receiver/settings_templates.py:976 msgid "Cursor speed." msgstr "Rychlost kurzoru." #: lib/logitech_receiver/settings_templates.py:976 msgid "Scale" msgstr "Měřítko" #: lib/logitech_receiver/settings_templates.py:982 msgid "Gestures" msgstr "Gesta" #: lib/logitech_receiver/settings_templates.py:983 msgid "Tweak the mouse/touchpad behaviour." msgstr "Ladí chování myši/touchpadu." #: lib/logitech_receiver/settings_templates.py:1000 msgid "Gestures Diversion" msgstr "" #: lib/logitech_receiver/settings_templates.py:1001 msgid "Divert mouse/touchpad gestures." msgstr "" #: lib/logitech_receiver/settings_templates.py:1018 msgid "Gesture params" msgstr "Parametry gesta" #: lib/logitech_receiver/settings_templates.py:1019 msgid "Change numerical parameters of a mouse/touchpad." msgstr "Změňte číselné parametry myši/touchpadu." #: lib/logitech_receiver/settings_templates.py:1044 msgid "M-Key LEDs" msgstr "" #: lib/logitech_receiver/settings_templates.py:1046 msgid "Control the M-Key LEDs." msgstr "" #: lib/logitech_receiver/settings_templates.py:1047 #: lib/logitech_receiver/settings_templates.py:1077 msgid "May need G Keys diverted to be effective." msgstr "" #: lib/logitech_receiver/settings_templates.py:1053 #, python-format msgid "Lights up the %s key." msgstr "" #: lib/logitech_receiver/settings_templates.py:1074 msgid "MR-Key LED" msgstr "" #: lib/logitech_receiver/settings_templates.py:1076 msgid "Control the MR-Key LED." msgstr "" #: lib/logitech_receiver/settings_templates.py:1095 msgid "Persistent Key/Button Mapping" msgstr "" #: lib/logitech_receiver/settings_templates.py:1097 msgid "Permanently change the mapping for the key or button." msgstr "" #: lib/logitech_receiver/settings_templates.py:1098 msgid "Changing important keys or buttons (such as for the left mouse " "button) can result in an unusable system." msgstr "" #: lib/logitech_receiver/settings_templates.py:1157 msgid "Sidetone" msgstr "" #: lib/logitech_receiver/settings_templates.py:1158 msgid "Set sidetone level." msgstr "" #: lib/logitech_receiver/settings_templates.py:1167 msgid "Equalizer" msgstr "" #: lib/logitech_receiver/settings_templates.py:1168 msgid "Set equalizer levels." msgstr "" #: lib/logitech_receiver/settings_templates.py:1191 msgid "Hz" msgstr "" #: lib/logitech_receiver/settings_templates.py:1197 msgid "Power Management" msgstr "" #: lib/logitech_receiver/settings_templates.py:1198 msgid "Power off in minutes (0 for never)." msgstr "" #: lib/logitech_receiver/status.py:114 msgid "No paired devices." msgstr "Nepřipojené zařízení." #: lib/logitech_receiver/status.py:115 lib/solaar/ui/window.py:622 #, python-format msgid "%(count)s paired device." msgid_plural "%(count)s paired devices." msgstr[0] "%(count)s připojené zařízení." msgstr[1] "%(count)s připojené zařízení." msgstr[2] "%(count)s připojených zařízení." #: lib/logitech_receiver/status.py:170 #, python-format msgid "Battery: %(level)s" msgstr "Baterie: %(level)s" #: lib/logitech_receiver/status.py:172 #, python-format msgid "Battery: %(percent)d%%" msgstr "Baterie: %(percent)d%%" #: lib/logitech_receiver/status.py:184 #, python-format msgid "Lighting: %(level)s lux" msgstr "Osvětlení: %(level)s lux" #: lib/logitech_receiver/status.py:239 #, python-format msgid "Battery: %(level)s (%(status)s)" msgstr "Baterie: %(level)s (%(status)s)" #: lib/logitech_receiver/status.py:241 #, python-format msgid "Battery: %(percent)d%% (%(status)s)" msgstr "Baterie: %(percent)d%% (%(status)s)" #: lib/solaar/ui/__init__.py:52 msgid "Permissions error" msgstr "Chyba oprávnění" #: lib/solaar/ui/__init__.py:54 #, python-format msgid "Found a Logitech receiver or device (%s), but did not have " "permission to open it." msgstr "" #: lib/solaar/ui/__init__.py:55 msgid "If you've just installed Solaar, try disconnecting the receiver or " "device and then reconnecting it." msgstr "" #: lib/solaar/ui/__init__.py:58 msgid "Cannot connect to device error" msgstr "" #: lib/solaar/ui/__init__.py:60 #, python-format msgid "Found a Logitech receiver or device at %s, but encountered an error " "connecting to it." msgstr "" #: lib/solaar/ui/__init__.py:61 msgid "Try disconnecting the device and then reconnecting it or turning it " "off and then on." msgstr "" #: lib/solaar/ui/__init__.py:64 msgid "Unpairing failed" msgstr "Odpojení selhalo" #: lib/solaar/ui/__init__.py:66 #, python-brace-format msgid "Failed to unpair %{device} from %{receiver}." msgstr "Selhalo odpojení %{device} od %{receiver}." #: lib/solaar/ui/__init__.py:67 msgid "The receiver returned an error, with no further details." msgstr "Přijímač vrátil chybu bez dalších detailů." #: lib/solaar/ui/__init__.py:177 msgid "Another Solaar process is already running so just expose its window" msgstr "" #: lib/solaar/ui/about.py:36 msgid "Manages Logitech receivers,\n" "keyboards, mice, and tablets." msgstr "" #: lib/solaar/ui/about.py:44 msgid "Additional Programming" msgstr "" #: lib/solaar/ui/about.py:45 msgid "GUI design" msgstr "Vzhled aplikace" #: lib/solaar/ui/about.py:47 msgid "Testing" msgstr "Testování" #: lib/solaar/ui/about.py:54 msgid "Logitech documentation" msgstr "Dokumentace Logitechu" #: lib/solaar/ui/action.py:85 lib/solaar/ui/action.py:89 #: lib/solaar/ui/window.py:197 msgid "Unpair" msgstr "Odpojit" #: lib/solaar/ui/action.py:88 lib/solaar/ui/diversion_rules.py:150 msgid "Cancel" msgstr "" #: lib/solaar/ui/config_panel.py:212 msgid "Complete - ENTER to change" msgstr "" #: lib/solaar/ui/config_panel.py:212 msgid "Incomplete" msgstr "" #: lib/solaar/ui/config_panel.py:455 lib/solaar/ui/config_panel.py:507 #, python-format msgid "%d value" msgid_plural "%d values" msgstr[0] "%d hodnota" msgstr[1] "%d hodnoty" msgstr[2] "%d hodnot" #: lib/solaar/ui/config_panel.py:518 msgid "Changes allowed" msgstr "" #: lib/solaar/ui/config_panel.py:519 msgid "No changes allowed" msgstr "" #: lib/solaar/ui/config_panel.py:520 msgid "Ignore this setting" msgstr "" #: lib/solaar/ui/config_panel.py:565 msgid "Working" msgstr "Pracuji" #: lib/solaar/ui/config_panel.py:568 msgid "Read/write operation failed." msgstr "Operace čtení/zápisu selhala." #: lib/solaar/ui/diversion_rules.py:65 msgid "Built-in rules" msgstr "Zabudovaná pravidla" #: lib/solaar/ui/diversion_rules.py:65 msgid "User-defined rules" msgstr "Uživatelem definované pravidla" #: lib/solaar/ui/diversion_rules.py:67 lib/solaar/ui/diversion_rules.py:1083 msgid "Rule" msgstr "Pravidlo" #: lib/solaar/ui/diversion_rules.py:68 lib/solaar/ui/diversion_rules.py:509 #: lib/solaar/ui/diversion_rules.py:636 msgid "Sub-rule" msgstr "Podpravidlo" #: lib/solaar/ui/diversion_rules.py:70 msgid "[empty]" msgstr "[prázdné]" #: lib/solaar/ui/diversion_rules.py:94 msgid "Solaar Rule Editor" msgstr "Editor pravidel Solaar" #: lib/solaar/ui/diversion_rules.py:141 msgid "Make changes permanent?" msgstr "Nastavit změnu jako trvalou?" #: lib/solaar/ui/diversion_rules.py:146 msgid "Yes" msgstr "Ano" #: lib/solaar/ui/diversion_rules.py:148 msgid "No" msgstr "" #: lib/solaar/ui/diversion_rules.py:153 msgid "If you choose No, changes will be lost when Solaar is closed." msgstr "Pokud vyberete Ne, budou změny po ukončení aplikace ztraceny." #: lib/solaar/ui/diversion_rules.py:201 msgid "Save changes" msgstr "" #: lib/solaar/ui/diversion_rules.py:206 msgid "Discard changes" msgstr "" #: lib/solaar/ui/diversion_rules.py:372 msgid "Insert here" msgstr "Vložit tady" #: lib/solaar/ui/diversion_rules.py:374 msgid "Insert above" msgstr "Vložit nad" #: lib/solaar/ui/diversion_rules.py:376 msgid "Insert below" msgstr "Vložit pod" #: lib/solaar/ui/diversion_rules.py:382 msgid "Insert new rule here" msgstr "Vložit nové pravidlo tady" #: lib/solaar/ui/diversion_rules.py:384 msgid "Insert new rule above" msgstr "Vložit nové pravidlo nad" #: lib/solaar/ui/diversion_rules.py:386 msgid "Insert new rule below" msgstr "Vložit nové pravidlo pod" #: lib/solaar/ui/diversion_rules.py:427 msgid "Paste here" msgstr "Vložit tady" #: lib/solaar/ui/diversion_rules.py:429 msgid "Paste above" msgstr "Vložit nad" #: lib/solaar/ui/diversion_rules.py:431 msgid "Paste below" msgstr "Vložit pod" #: lib/solaar/ui/diversion_rules.py:437 msgid "Paste rule here" msgstr "Vložit pravidlo tady" #: lib/solaar/ui/diversion_rules.py:439 msgid "Paste rule above" msgstr "Vložit pravidlo nad" #: lib/solaar/ui/diversion_rules.py:441 msgid "Paste rule below" msgstr "Vložit pravidlo pod" #: lib/solaar/ui/diversion_rules.py:445 msgid "Paste rule" msgstr "Vložit pravidlo" #: lib/solaar/ui/diversion_rules.py:474 msgid "Flatten" msgstr "Srovnání" #: lib/solaar/ui/diversion_rules.py:507 msgid "Insert" msgstr "Vložit" #: lib/solaar/ui/diversion_rules.py:510 lib/solaar/ui/diversion_rules.py:638 #: lib/solaar/ui/diversion_rules.py:1126 msgid "Or" msgstr "Nebo" #: lib/solaar/ui/diversion_rules.py:511 lib/solaar/ui/diversion_rules.py:637 #: lib/solaar/ui/diversion_rules.py:1111 msgid "And" msgstr "A" #: lib/solaar/ui/diversion_rules.py:513 msgid "Condition" msgstr "podmínka" #: lib/solaar/ui/diversion_rules.py:515 lib/solaar/ui/diversion_rules.py:1292 msgid "Feature" msgstr "Vlastnost" #: lib/solaar/ui/diversion_rules.py:516 lib/solaar/ui/diversion_rules.py:1328 msgid "Report" msgstr "Report" #: lib/solaar/ui/diversion_rules.py:517 lib/solaar/ui/diversion_rules.py:1204 msgid "Process" msgstr "Proces" #: lib/solaar/ui/diversion_rules.py:518 msgid "Mouse process" msgstr "" #: lib/solaar/ui/diversion_rules.py:519 lib/solaar/ui/diversion_rules.py:1366 msgid "Modifiers" msgstr "Modifikátory" #: lib/solaar/ui/diversion_rules.py:520 lib/solaar/ui/diversion_rules.py:1419 msgid "Key" msgstr "Klávesa" #: lib/solaar/ui/diversion_rules.py:521 lib/solaar/ui/diversion_rules.py:1461 msgid "KeyIsDown" msgstr "" #: lib/solaar/ui/diversion_rules.py:522 lib/solaar/ui/diversion_rules.py:2260 msgid "Active" msgstr "Aktivní" #: lib/solaar/ui/diversion_rules.py:523 lib/solaar/ui/diversion_rules.py:2218 #: lib/solaar/ui/diversion_rules.py:2270 lib/solaar/ui/diversion_rules.py:2323 msgid "Device" msgstr "" #: lib/solaar/ui/diversion_rules.py:524 lib/solaar/ui/diversion_rules.py:2297 msgid "Host" msgstr "" #: lib/solaar/ui/diversion_rules.py:525 lib/solaar/ui/diversion_rules.py:2339 msgid "Setting" msgstr "" #: lib/solaar/ui/diversion_rules.py:526 lib/solaar/ui/diversion_rules.py:1477 #: lib/solaar/ui/diversion_rules.py:1526 msgid "Test" msgstr "Test" #: lib/solaar/ui/diversion_rules.py:527 lib/solaar/ui/diversion_rules.py:1643 msgid "Test bytes" msgstr "" #: lib/solaar/ui/diversion_rules.py:528 lib/solaar/ui/diversion_rules.py:1736 msgid "Mouse Gesture" msgstr "" #: lib/solaar/ui/diversion_rules.py:532 msgid "Action" msgstr "Akce" #: lib/solaar/ui/diversion_rules.py:534 lib/solaar/ui/diversion_rules.py:1845 msgid "Key press" msgstr "Stisknutí klávesy" #: lib/solaar/ui/diversion_rules.py:535 lib/solaar/ui/diversion_rules.py:1897 msgid "Mouse scroll" msgstr "Otočení kolečkem" #: lib/solaar/ui/diversion_rules.py:536 lib/solaar/ui/diversion_rules.py:1959 msgid "Mouse click" msgstr "Kliknutí myši" #: lib/solaar/ui/diversion_rules.py:537 msgid "Set" msgstr "" #: lib/solaar/ui/diversion_rules.py:538 lib/solaar/ui/diversion_rules.py:2030 msgid "Execute" msgstr "Vykonat" #: lib/solaar/ui/diversion_rules.py:539 lib/solaar/ui/diversion_rules.py:1158 msgid "Later" msgstr "" #: lib/solaar/ui/diversion_rules.py:568 msgid "Insert new rule" msgstr "Vložit nové pravidlo" #: lib/solaar/ui/diversion_rules.py:588 lib/solaar/ui/diversion_rules.py:1686 #: lib/solaar/ui/diversion_rules.py:1790 lib/solaar/ui/diversion_rules.py:1989 msgid "Delete" msgstr "Smazat" #: lib/solaar/ui/diversion_rules.py:610 msgid "Negate" msgstr "Negovat" #: lib/solaar/ui/diversion_rules.py:634 msgid "Wrap with" msgstr "Obalit s" #: lib/solaar/ui/diversion_rules.py:656 msgid "Cut" msgstr "Vyjmout" #: lib/solaar/ui/diversion_rules.py:671 msgid "Paste" msgstr "Vložit" #: lib/solaar/ui/diversion_rules.py:683 msgid "Copy" msgstr "Kopírovat" #: lib/solaar/ui/diversion_rules.py:1063 msgid "This editor does not support the selected rule component yet." msgstr "Vybraná komponenta pravidla není editorem zatím podporována." #: lib/solaar/ui/diversion_rules.py:1138 msgid "Number of seconds to delay." msgstr "" #: lib/solaar/ui/diversion_rules.py:1177 msgid "Not" msgstr "Opak" #: lib/solaar/ui/diversion_rules.py:1187 msgid "X11 active process. For use in X11 only." msgstr "" #: lib/solaar/ui/diversion_rules.py:1218 msgid "X11 mouse process. For use in X11 only." msgstr "" #: lib/solaar/ui/diversion_rules.py:1235 msgid "MouseProcess" msgstr "" #: lib/solaar/ui/diversion_rules.py:1260 msgid "Feature name of notification triggering rule processing." msgstr "" #: lib/solaar/ui/diversion_rules.py:1308 msgid "Report number of notification triggering rule processing." msgstr "" #: lib/solaar/ui/diversion_rules.py:1342 msgid "Active keyboard modifiers. Not always available in Wayland." msgstr "" #: lib/solaar/ui/diversion_rules.py:1383 msgid "Diverted key or button depressed or released.\n" "Use the Key/Button Diversion and Divert G Keys settings to divert " "keys and buttons." msgstr "" #: lib/solaar/ui/diversion_rules.py:1392 msgid "Key down" msgstr "" #: lib/solaar/ui/diversion_rules.py:1395 msgid "Key up" msgstr "" #: lib/solaar/ui/diversion_rules.py:1436 msgid "Diverted key or button is currently down.\n" "Use the Key/Button Diversion and Divert G Keys settings to divert " "keys and buttons." msgstr "" #: lib/solaar/ui/diversion_rules.py:1475 msgid "Test condition on notification triggering rule processing." msgstr "" #: lib/solaar/ui/diversion_rules.py:1479 msgid "Parameter" msgstr "" #: lib/solaar/ui/diversion_rules.py:1542 msgid "begin (inclusive)" msgstr "" #: lib/solaar/ui/diversion_rules.py:1543 msgid "end (exclusive)" msgstr "" #: lib/solaar/ui/diversion_rules.py:1552 msgid "range" msgstr "" #: lib/solaar/ui/diversion_rules.py:1554 msgid "minimum" msgstr "" #: lib/solaar/ui/diversion_rules.py:1555 msgid "maximum" msgstr "" #: lib/solaar/ui/diversion_rules.py:1557 #, python-format msgid "bytes %(0)d to %(1)d, ranging from %(2)d to %(3)d" msgstr "" #: lib/solaar/ui/diversion_rules.py:1562 msgid "mask" msgstr "" #: lib/solaar/ui/diversion_rules.py:1563 #, python-format msgid "bytes %(0)d to %(1)d, mask %(2)d" msgstr "" #: lib/solaar/ui/diversion_rules.py:1573 msgid "Bit or range test on bytes in notification message triggering rule " "processing." msgstr "" #: lib/solaar/ui/diversion_rules.py:1583 msgid "type" msgstr "" #: lib/solaar/ui/diversion_rules.py:1666 msgid "Mouse gesture with optional initiating button followed by zero or " "more mouse movements." msgstr "" #: lib/solaar/ui/diversion_rules.py:1671 msgid "Add movement" msgstr "" #: lib/solaar/ui/diversion_rules.py:1764 msgid "Simulate a chorded key click or depress or release.\n" "On Wayland requires write access to /dev/uinput." msgstr "" #: lib/solaar/ui/diversion_rules.py:1769 msgid "Add key" msgstr "Přidat klávesu" #: lib/solaar/ui/diversion_rules.py:1772 msgid "Click" msgstr "" #: lib/solaar/ui/diversion_rules.py:1775 msgid "Depress" msgstr "" #: lib/solaar/ui/diversion_rules.py:1778 msgid "Release" msgstr "" #: lib/solaar/ui/diversion_rules.py:1861 msgid "Simulate a mouse scroll.\n" "On Wayland requires write access to /dev/uinput." msgstr "" #: lib/solaar/ui/diversion_rules.py:1918 msgid "Simulate a mouse click.\n" "On Wayland requires write access to /dev/uinput." msgstr "" #: lib/solaar/ui/diversion_rules.py:1921 msgid "Button" msgstr "Tlačítko" #: lib/solaar/ui/diversion_rules.py:1922 msgid "Count and Action" msgstr "" #: lib/solaar/ui/diversion_rules.py:1972 msgid "Execute a command with arguments." msgstr "" #: lib/solaar/ui/diversion_rules.py:1975 msgid "Add argument" msgstr "Přidat argument" #: lib/solaar/ui/diversion_rules.py:2050 msgid "Toggle" msgstr "" #: lib/solaar/ui/diversion_rules.py:2050 msgid "True" msgstr "" #: lib/solaar/ui/diversion_rules.py:2051 msgid "False" msgstr "" #: lib/solaar/ui/diversion_rules.py:2065 msgid "Unsupported setting" msgstr "" #: lib/solaar/ui/diversion_rules.py:2223 lib/solaar/ui/diversion_rules.py:2242 #: lib/solaar/ui/diversion_rules.py:2328 lib/solaar/ui/diversion_rules.py:2570 #: lib/solaar/ui/diversion_rules.py:2588 msgid "Originating device" msgstr "" #: lib/solaar/ui/diversion_rules.py:2256 msgid "Device is active and its settings can be changed." msgstr "" #: lib/solaar/ui/diversion_rules.py:2266 msgid "Device that originated the current notification." msgstr "" #: lib/solaar/ui/diversion_rules.py:2280 msgid "Name of host computer." msgstr "" #: lib/solaar/ui/diversion_rules.py:2347 msgid "Value" msgstr "Hodnota" #: lib/solaar/ui/diversion_rules.py:2355 msgid "Item" msgstr "" #: lib/solaar/ui/diversion_rules.py:2630 msgid "Change setting on device" msgstr "" #: lib/solaar/ui/diversion_rules.py:2647 msgid "Setting on device" msgstr "" #: lib/solaar/ui/notify.py:124 lib/solaar/ui/tray.py:318 #: lib/solaar/ui/tray.py:323 lib/solaar/ui/window.py:739 msgid "offline" msgstr "vypnuto" #: lib/solaar/ui/pair_window.py:122 lib/solaar/ui/pair_window.py:256 #: lib/solaar/ui/pair_window.py:288 #, python-format msgid "%(receiver_name)s: pair new device" msgstr "%(receiver_name)s: nově připojení zařízení" #: lib/solaar/ui/pair_window.py:123 #, python-format msgid "Enter passcode on %(name)s." msgstr "" #: lib/solaar/ui/pair_window.py:126 #, python-format msgid "Type %(passcode)s and then press the enter key." msgstr "" #: lib/solaar/ui/pair_window.py:129 msgid "left" msgstr "" #: lib/solaar/ui/pair_window.py:129 msgid "right" msgstr "" #: lib/solaar/ui/pair_window.py:131 #, python-format msgid "Press %(code)s\n" "and then press left and right buttons simultaneously." msgstr "" #: lib/solaar/ui/pair_window.py:188 msgid "Pairing failed" msgstr "Připojení selhalo." #: lib/solaar/ui/pair_window.py:190 msgid "Make sure your device is within range, and has a decent battery " "charge." msgstr "Ujistěte se, že je zařízení v dosahu je dostatečně nabito." #: lib/solaar/ui/pair_window.py:192 msgid "A new device was detected, but it is not compatible with this " "receiver." msgstr "Nové zařízení bylo detekováno, ale není kompatibilní s tímto " "přijímačem." #: lib/solaar/ui/pair_window.py:194 msgid "More paired devices than receiver can support." msgstr "Více připojených zařízení než přijímač dokáže zvládnout." #: lib/solaar/ui/pair_window.py:196 msgid "No further details are available about the error." msgstr "Další detaily o chybě nejsou dostupné." #: lib/solaar/ui/pair_window.py:210 msgid "Found a new device:" msgstr "Nalezeno nové zařízení:" #: lib/solaar/ui/pair_window.py:235 msgid "The wireless link is not encrypted" msgstr "Bezdrátové spojení není šifrováno" #: lib/solaar/ui/pair_window.py:264 msgid "Unifying receivers are only compatible with Unifying devices." msgstr "" #: lib/solaar/ui/pair_window.py:266 msgid "Bolt receivers are only compatible with Bolt devices." msgstr "" #: lib/solaar/ui/pair_window.py:268 msgid "Other receivers are only compatible with a few devices." msgstr "" #: lib/solaar/ui/pair_window.py:270 msgid "The device must not be paired with a nearby powered-on receiver." msgstr "" #: lib/solaar/ui/pair_window.py:274 msgid "Press a pairing button or key until the pairing light flashes " "quickly." msgstr "" #: lib/solaar/ui/pair_window.py:276 msgid "You may have to first turn the device off and on again." msgstr "" #: lib/solaar/ui/pair_window.py:278 msgid "Turn on the device you want to pair." msgstr "Zapněte zařízení které chcete připojit." #: lib/solaar/ui/pair_window.py:280 msgid "If the device is already turned on, turn it off and on again." msgstr "" #: lib/solaar/ui/pair_window.py:283 #, python-format msgid "\n" "\n" "This receiver has %d pairing remaining." msgid_plural "\n" "\n" "This receiver has %d pairings remaining." msgstr[0] "\n" "\n" "K přijímači lze připojit ještě %d zařízení." msgstr[1] "\n" "\n" "K přijímači lze připojit ještě %d zařízení." msgstr[2] "\n" "\n" "K přijímači lze připojit ještě %d zařízení." #: lib/solaar/ui/pair_window.py:286 msgid "\n" "Cancelling at this point will not use up a pairing." msgstr "\n" "Zrušení v tomto okamžiku nevyužije připojení." #: lib/solaar/ui/tray.py:58 msgid "No supported device found" msgstr "" #: lib/solaar/ui/tray.py:64 lib/solaar/ui/window.py:319 #, python-format msgid "About %s" msgstr "O aplikaci %s" #: lib/solaar/ui/tray.py:65 lib/solaar/ui/window.py:317 #, python-format msgid "Quit %s" msgstr "Ukončit %s" #: lib/solaar/ui/tray.py:297 lib/solaar/ui/tray.py:305 msgid "no receiver" msgstr "bez přijímače" #: lib/solaar/ui/tray.py:321 msgid "no status" msgstr "bez statusu" #: lib/solaar/ui/window.py:96 msgid "Scanning" msgstr "Skenování" #: lib/solaar/ui/window.py:129 msgid "Battery" msgstr "Baterie" #: lib/solaar/ui/window.py:132 msgid "Wireless Link" msgstr "Bezdrátové propojení" #: lib/solaar/ui/window.py:136 msgid "Lighting" msgstr "Osvětlení" #: lib/solaar/ui/window.py:170 msgid "Show Technical Details" msgstr "Ukázat technické detaily" #: lib/solaar/ui/window.py:186 msgid "Pair new device" msgstr "Připojit nové zařízení" #: lib/solaar/ui/window.py:205 msgid "Select a device" msgstr "Vyberte zařízení" #: lib/solaar/ui/window.py:322 msgid "Rule Editor" msgstr "Editor pravidel" #: lib/solaar/ui/window.py:533 msgid "Path" msgstr "Cesta" #: lib/solaar/ui/window.py:536 msgid "USB ID" msgstr "USB ID" #: lib/solaar/ui/window.py:539 lib/solaar/ui/window.py:541 #: lib/solaar/ui/window.py:561 lib/solaar/ui/window.py:563 msgid "Serial" msgstr "Sériové číslo" #: lib/solaar/ui/window.py:545 msgid "Index" msgstr "Index" #: lib/solaar/ui/window.py:547 msgid "Wireless PID" msgstr "Bezdrát. PID" #: lib/solaar/ui/window.py:549 msgid "Product ID" msgstr "ID výrobku" #: lib/solaar/ui/window.py:551 msgid "Protocol" msgstr "Protokol" #: lib/solaar/ui/window.py:551 msgid "Unknown" msgstr "Neznámý" #: lib/solaar/ui/window.py:554 #, python-format msgid "%(rate)d ms (%(rate_hz)dHz)" msgstr "%(rate)d ms (%(rate_hz)dHz)" #: lib/solaar/ui/window.py:554 msgid "Polling rate" msgstr "Četn. dotazů" #: lib/solaar/ui/window.py:565 msgid "Unit ID" msgstr "Číslo jednotky" #: lib/solaar/ui/window.py:576 msgid "none" msgstr "žádný" #: lib/solaar/ui/window.py:577 msgid "Notifications" msgstr "Oznámení" #: lib/solaar/ui/window.py:621 msgid "No device paired." msgstr "Žádné připojené zařízení." #: lib/solaar/ui/window.py:628 #, python-format msgid "Up to %(max_count)s device can be paired to this receiver." msgid_plural "Up to %(max_count)s devices can be paired to this receiver." msgstr[0] "K přijímači může být připojeno %(max_count)s zařízení." msgstr[1] "K přijímači můžou být připojena až %(max_count)s zařízení." msgstr[2] "K přijímači může být připojeno až %(max_count)s zařízení." #: lib/solaar/ui/window.py:634 msgid "Only one device can be paired to this receiver." msgstr "K tomuto přijímači je možné připojit jen jedno zařízení." #: lib/solaar/ui/window.py:638 #, python-format msgid "This receiver has %d pairing remaining." msgid_plural "This receiver has %d pairings remaining." msgstr[0] "K přijímači je možné připojit ještě %d zařízení." msgstr[1] "K přijímači je možné připojit ještě %d zařízení." msgstr[2] "K přijímači je možné připojit ještě %d zařízení." #: lib/solaar/ui/window.py:692 msgid "Battery Voltage" msgstr "Napětí baterie" #: lib/solaar/ui/window.py:694 msgid "Voltage reported by battery" msgstr "Napětí hlášené baterií" #: lib/solaar/ui/window.py:696 msgid "Battery Level" msgstr "Úroveň baterie" #: lib/solaar/ui/window.py:698 msgid "Approximate level reported by battery" msgstr "Přibližná úroveň hlášená baterií" #: lib/solaar/ui/window.py:705 lib/solaar/ui/window.py:707 msgid "next reported " msgstr "další hlášené " #: lib/solaar/ui/window.py:708 msgid " and next level to be reported." msgstr " a další úroveň pro hlášení." #: lib/solaar/ui/window.py:713 msgid "last known" msgstr "poslední známý" #: lib/solaar/ui/window.py:724 msgid "encrypted" msgstr "šifrováno" #: lib/solaar/ui/window.py:726 msgid "The wireless link between this device and its receiver is encrypted." msgstr "Bezdrátové spojení mezi zařízením a přijímačem je šifrováno." #: lib/solaar/ui/window.py:728 msgid "not encrypted" msgstr "nešifrováno" #: lib/solaar/ui/window.py:732 msgid "The wireless link between this device and its receiver is not " "encrypted.\n" "This is a security issue for pointing devices, and a major security " "issue for text-input devices." msgstr "" #: lib/solaar/ui/window.py:748 #, python-format msgid "%(light_level)d lux" msgstr "%(light_level)d lux" #~ msgid "Adjust the DPI by sliding the mouse horizontally while " #~ "holding the DPI button." #~ msgstr "Přizpůsobení DPI vodorovného posunu myši při současném " #~ "držením stisku DPI tlačítka." #~ msgid "Automatically switch the mouse wheel between ratchet and " #~ "freespin mode.\n" #~ "The mouse wheel is always free at 0, and always ratcheted at 50" #~ msgstr "Automatické přepíná režim kolečka myši mezi krokováním a " #~ "volným otáčením.\n" #~ "Kolečko je při hodnotě 0 vždy volné a při hodnotě 50 vždy krokuje." #~ msgid "Battery information unknown." #~ msgstr "Informace o baterii nejsou známy." #~ msgid "Click to allow changes." #~ msgstr "Klikněte pro povolení změn." #~ msgid "Click to prevent changes." #~ msgstr "Klikněte pro zakázání změn." #~ msgid "Count" #~ msgstr "Počet" #~ msgid "DPI Sliding Adjustment" #~ msgstr "Úprava DPI klouzání" #~ msgid "Effectively turns off thumb scrolling in Linux." #~ msgstr "Efektivně vypne rolování palcem v Linuxu." #~ msgid "Effectively turns off wheel scrolling in Linux." #~ msgstr "Efektivně vypne posuv kolečkem v Linuxu." #~ msgid "For more information see the Solaar installation directions\n" #~ "at https://pwr-solaar.github.io/Solaar/installation" #~ msgstr "Další instalační informace pro Solaar naleznete na \n" #~ "https://pwr-solaar.github.io/Solaar/installation" #, python-format #~ msgid "Found a Logitech Receiver (%s), but did not have permission " #~ "to open it." #~ msgstr "Nalezen přijímač Logitech (%s), ale chybí práva pro jeho " #~ "otevření." #~ msgid "HID++ mode for horizontal scroll with the thumb wheel." #~ msgstr "HID++ režim pro vodorovný posun pomocí kolečka." #~ msgid "HID++ mode for vertical scroll with the wheel." #~ msgstr "HID++ režim pro svislý posuv kolečkem." #~ msgid "If the device is already turned on, turn if off and on again." #~ msgstr "Pokud je zařízení zapnuto, vypněte jej a znovu zapněte" #~ msgid "If you've just installed Solaar, try removing the receiver " #~ "and plugging it back in." #~ msgstr "Pokud jste Solaar právě nainstalovali, zkuste přijímač " #~ "odpojit a znova připojit." #~ msgid "Make the key or button send HID++ notifications (which " #~ "trigger Solaar rules but are otherwise ignored)." #~ msgstr "Klávesa nebo tlačítko budou zasílat HID++ oznámení (která " #~ "spustí Solaar pravidla, ale jinak budou ignorována)." #~ msgid "No Logitech receiver found" #~ msgstr "Logitech přijímač nebyl nalezen" #~ msgid "Scroll Wheel HID++ Scrolling" #~ msgstr "HID++ posuv kolečka" #~ msgid "Scroll Wheel Rachet" #~ msgstr "Krokování kolečka" #~ msgid "Shows status of devices connected\n" #~ "through wireless Logitech receivers." #~ msgstr "Zobrazí stav zařízení připojených\n" #~ "přes bezdrátový přijímač Logitech." #~ msgid "Solaar depends on a udev file that is not present" #~ msgstr "Solaar závisí na udev souboru, který chybí" #~ msgid "The wireless link between this device and its receiver is " #~ "not encrypted.\n" #~ "\n" #~ "For pointing devices (mice, trackballs, trackpads), this is a minor " #~ "security issue.\n" #~ "\n" #~ "It is, however, a major security issue for text-input devices " #~ "(keyboards, numpads),\n" #~ "because typed text can be sniffed inconspicuously by 3rd parties " #~ "within range." #~ msgstr "Bezdrátové spojení mezi zařízením a přijímačem není " #~ "šifrováno.\n" #~ "\n" #~ "U ukazovacích zařízení (myši, trackbally, trackpady) je to jen " #~ "drobný bezpečnostní problém.\n" #~ "\n" #~ "Ovšem pro textové vstupní zařízení (klávesnice, numerické " #~ "klávesnice) se jedná\n" #~ "o významný bezpečnostní problém, protože psaný text může být\n" #~ "nepozorovaně odposlechnut třetí stranou, pokud je v dosahu." #~ msgid "Thumb Wheel HID++ Scrolling" #~ msgstr "HID++ posun kolečkem" #~ msgid "Top-most coordinate." #~ msgstr "Nejvyšší souřadnice." #~ msgid "height" #~ msgstr "výška" #~ msgid "top" #~ msgstr "nahoře" #~ msgid "unknown" #~ msgstr "neznámý" #~ msgid "width" #~ msgstr "šířka" Solaar-1.1.14/po/da.po000066400000000000000000001434571473525213700144100ustar00rootroot00000000000000# Danish translations for solaar package. # Copyright (C) 2020 THE solaar'S COPYRIGHT HOLDER # This file is distributed under the same license as the solaar package. # Automatically generated, 2020. # msgid "" msgstr "Project-Id-Version: solaar 1.0.3\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-12-28 17:40+0100\n" "PO-Revision-Date: 2020-08-03 02:47+0200\n" "Last-Translator: John Erling Blad \n" "Language-Team: none\n" "Language: da\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Poedit 2.3\n" #: lib/logitech_receiver/base_usb.py:46 msgid "Bolt Receiver" msgstr "" #: lib/logitech_receiver/base_usb.py:57 msgid "Unifying Receiver" msgstr "" #: lib/logitech_receiver/base_usb.py:67 lib/logitech_receiver/base_usb.py:78 #: lib/logitech_receiver/base_usb.py:90 lib/logitech_receiver/base_usb.py:102 #: lib/logitech_receiver/base_usb.py:114 msgid "Nano Receiver" msgstr "" #: lib/logitech_receiver/base_usb.py:124 msgid "Lightspeed Receiver" msgstr "" #: lib/logitech_receiver/base_usb.py:133 msgid "EX100 Receiver 27 Mhz" msgstr "" #: lib/logitech_receiver/i18n.py:30 msgid "empty" msgstr "tom" #: lib/logitech_receiver/i18n.py:31 msgid "critical" msgstr "kritisk" #: lib/logitech_receiver/i18n.py:32 msgid "low" msgstr "lav" #: lib/logitech_receiver/i18n.py:33 msgid "average" msgstr "" #: lib/logitech_receiver/i18n.py:34 msgid "good" msgstr "god" #: lib/logitech_receiver/i18n.py:35 msgid "full" msgstr "fuld" #: lib/logitech_receiver/i18n.py:38 msgid "discharging" msgstr "afladning" #: lib/logitech_receiver/i18n.py:39 msgid "recharging" msgstr "genopladning" #: lib/logitech_receiver/i18n.py:40 lib/solaar/ui/window.py:711 msgid "charging" msgstr "opladning" #: lib/logitech_receiver/i18n.py:41 msgid "not charging" msgstr "" #: lib/logitech_receiver/i18n.py:42 msgid "almost full" msgstr "næsten fuld" #: lib/logitech_receiver/i18n.py:43 msgid "charged" msgstr "opladt" #: lib/logitech_receiver/i18n.py:44 msgid "slow recharge" msgstr "langsom opladning" #: lib/logitech_receiver/i18n.py:45 msgid "invalid battery" msgstr "batterifejl" #: lib/logitech_receiver/i18n.py:46 msgid "thermal error" msgstr "termisk fejl" #: lib/logitech_receiver/i18n.py:47 msgid "error" msgstr "" #: lib/logitech_receiver/i18n.py:48 msgid "standard" msgstr "" #: lib/logitech_receiver/i18n.py:49 msgid "fast" msgstr "" #: lib/logitech_receiver/i18n.py:50 msgid "slow" msgstr "" #: lib/logitech_receiver/i18n.py:53 msgid "device timeout" msgstr "enheden svarede ikke" #: lib/logitech_receiver/i18n.py:54 msgid "device not supported" msgstr "enhed understøttes ikke" #: lib/logitech_receiver/i18n.py:55 msgid "too many devices" msgstr "for mange enheder" #: lib/logitech_receiver/i18n.py:56 msgid "sequence timeout" msgstr "sekvens timeout" #: lib/logitech_receiver/i18n.py:59 lib/solaar/ui/window.py:572 msgid "Firmware" msgstr "Firmware" #: lib/logitech_receiver/i18n.py:60 msgid "Bootloader" msgstr "Opstartsindlæser" #: lib/logitech_receiver/i18n.py:61 msgid "Hardware" msgstr "Hardware" #: lib/logitech_receiver/i18n.py:62 msgid "Other" msgstr "Andet" #: lib/logitech_receiver/i18n.py:65 msgid "Left Button" msgstr "" #: lib/logitech_receiver/i18n.py:66 msgid "Right Button" msgstr "" #: lib/logitech_receiver/i18n.py:67 msgid "Middle Button" msgstr "" #: lib/logitech_receiver/i18n.py:68 msgid "Back Button" msgstr "" #: lib/logitech_receiver/i18n.py:69 msgid "Forward Button" msgstr "" #: lib/logitech_receiver/i18n.py:70 msgid "Mouse Gesture Button" msgstr "" #: lib/logitech_receiver/i18n.py:71 msgid "Smart Shift" msgstr "Smart skift" #: lib/logitech_receiver/i18n.py:72 msgid "DPI Switch" msgstr "" #: lib/logitech_receiver/i18n.py:73 msgid "Left Tilt" msgstr "" #: lib/logitech_receiver/i18n.py:74 msgid "Right Tilt" msgstr "" #: lib/logitech_receiver/i18n.py:75 msgid "Left Click" msgstr "" #: lib/logitech_receiver/i18n.py:76 msgid "Right Click" msgstr "" #: lib/logitech_receiver/i18n.py:77 msgid "Mouse Middle Button" msgstr "" #: lib/logitech_receiver/i18n.py:78 msgid "Mouse Back Button" msgstr "" #: lib/logitech_receiver/i18n.py:79 msgid "Mouse Forward Button" msgstr "" #: lib/logitech_receiver/i18n.py:80 msgid "Gesture Button Navigation" msgstr "" #: lib/logitech_receiver/i18n.py:81 msgid "Mouse Scroll Left Button" msgstr "" #: lib/logitech_receiver/i18n.py:82 msgid "Mouse Scroll Right Button" msgstr "" #: lib/logitech_receiver/i18n.py:85 msgid "pressed" msgstr "" #: lib/logitech_receiver/i18n.py:86 msgid "released" msgstr "" #: lib/logitech_receiver/notifications.py:75 #: lib/logitech_receiver/notifications.py:126 msgid "pairing lock is closed" msgstr "parlåsen er lukket" #: lib/logitech_receiver/notifications.py:75 #: lib/logitech_receiver/notifications.py:126 msgid "pairing lock is open" msgstr "parlåsen er åben" #: lib/logitech_receiver/notifications.py:92 msgid "discovery lock is closed" msgstr "" #: lib/logitech_receiver/notifications.py:92 msgid "discovery lock is open" msgstr "" #: lib/logitech_receiver/notifications.py:224 lib/solaar/ui/notify.py:122 msgid "connected" msgstr "tilsluttet" #: lib/logitech_receiver/notifications.py:224 msgid "disconnected" msgstr "" #: lib/logitech_receiver/notifications.py:262 lib/solaar/ui/notify.py:120 msgid "unpaired" msgstr "uparret" #: lib/logitech_receiver/notifications.py:304 msgid "powered on" msgstr "tændt" #: lib/logitech_receiver/settings.py:750 msgid "register" msgstr "" #: lib/logitech_receiver/settings.py:764 lib/logitech_receiver/settings.py:791 msgid "feature" msgstr "" #: lib/logitech_receiver/settings_templates.py:139 msgid "Swap Fx function" msgstr "Byt Fx-funktion" #: lib/logitech_receiver/settings_templates.py:140 msgid "When set, the F1..F12 keys will activate their special function,\n" "and you must hold the FN key to activate their standard function." msgstr "Hvis aktiveret, vil F1–F12-tastene aktivere sin spesialfunksjon,\n" "og du må holde FN-tasten nede for at aktivere jeres standardfunktion." #: lib/logitech_receiver/settings_templates.py:142 msgid "When unset, the F1..F12 keys will activate their standard function,\n" "and you must hold the FN key to activate their special function." msgstr "Hvis deaktiveret, vil F1–F12-tastene aktivere sin standardfunktion,\n" "og du må holde FN-tasten nede for at aktivere deres spesialfunktion." #: lib/logitech_receiver/settings_templates.py:149 msgid "Hand Detection" msgstr "Hånddetektion" #: lib/logitech_receiver/settings_templates.py:150 msgid "Turn on illumination when the hands hover over the keyboard." msgstr "Tænd belysning når hænderne er over tastaturet." #: lib/logitech_receiver/settings_templates.py:157 msgid "Scroll Wheel Smooth Scrolling" msgstr "" #: lib/logitech_receiver/settings_templates.py:158 #: lib/logitech_receiver/settings_templates.py:239 #: lib/logitech_receiver/settings_templates.py:267 msgid "High-sensitivity mode for vertical scroll with the wheel." msgstr "Højfølsom tilstand til lodret rulling med hjulet." #: lib/logitech_receiver/settings_templates.py:165 msgid "Side Scrolling" msgstr "Siderulling" #: lib/logitech_receiver/settings_templates.py:167 msgid "When disabled, pushing the wheel sideways sends custom button " "events\n" "instead of the standard side-scrolling events." msgstr "Når deaktiveret, sender et tryk på hjulet sidelæns brugerdefinerede " "knaphændelser\n" "i stedet for standard siderullehændelser." #: lib/logitech_receiver/settings_templates.py:177 msgid "Sensitivity (DPI - older mice)" msgstr "" #: lib/logitech_receiver/settings_templates.py:178 #: lib/logitech_receiver/settings_templates.py:712 msgid "Mouse movement sensitivity" msgstr "" #: lib/logitech_receiver/settings_templates.py:208 #: lib/logitech_receiver/settings_templates.py:218 #: lib/logitech_receiver/settings_templates.py:225 msgid "Backlight" msgstr "Bagbelysning" #: lib/logitech_receiver/settings_templates.py:209 #: lib/logitech_receiver/settings_templates.py:226 msgid "Set illumination time for keyboard." msgstr "" #: lib/logitech_receiver/settings_templates.py:219 msgid "Turn illumination on or off on keyboard." msgstr "Tænd eller sluk for belysning på tastaturet." #: lib/logitech_receiver/settings_templates.py:237 msgid "Scroll Wheel High Resolution" msgstr "" #: lib/logitech_receiver/settings_templates.py:240 #: lib/logitech_receiver/settings_templates.py:268 msgid "Set to ignore if scrolling is abnormally fast or slow" msgstr "" #: lib/logitech_receiver/settings_templates.py:247 #: lib/logitech_receiver/settings_templates.py:277 msgid "Scroll Wheel Diversion" msgstr "" #: lib/logitech_receiver/settings_templates.py:249 msgid "Make scroll wheel send LOWRES_WHEEL HID++ notifications (which " "trigger Solaar rules but are otherwise ignored)." msgstr "" #: lib/logitech_receiver/settings_templates.py:256 msgid "Scroll Wheel Direction" msgstr "" #: lib/logitech_receiver/settings_templates.py:257 msgid "Invert direction for vertical scroll with wheel." msgstr "" #: lib/logitech_receiver/settings_templates.py:265 msgid "Scroll Wheel Resolution" msgstr "" #: lib/logitech_receiver/settings_templates.py:279 msgid "Make scroll wheel send HIRES_WHEEL HID++ notifications (which " "trigger Solaar rules but are otherwise ignored)." msgstr "" #: lib/logitech_receiver/settings_templates.py:288 msgid "Sensitivity (Pointer Speed)" msgstr "Følsomhed (pegerhastighed)" #: lib/logitech_receiver/settings_templates.py:289 msgid "Speed multiplier for mouse (256 is normal multiplier)." msgstr "Hastighedsmultiplikator for mus (256 er normal multiplikator)." #: lib/logitech_receiver/settings_templates.py:299 msgid "Thumb Wheel Diversion" msgstr "" #: lib/logitech_receiver/settings_templates.py:301 msgid "Make thumb wheel send THUMB_WHEEL HID++ notifications (which trigger " "Solaar rules but are otherwise ignored)." msgstr "" #: lib/logitech_receiver/settings_templates.py:310 msgid "Thumb Wheel Direction" msgstr "" #: lib/logitech_receiver/settings_templates.py:311 msgid "Invert thumb wheel scroll direction." msgstr "" #: lib/logitech_receiver/settings_templates.py:319 msgid "Onboard Profiles" msgstr "" #: lib/logitech_receiver/settings_templates.py:320 msgid "Enable onboard profiles, which often control report rate and " "keyboard lighting" msgstr "" #: lib/logitech_receiver/settings_templates.py:330 msgid "Polling Rate (ms)" msgstr "" #: lib/logitech_receiver/settings_templates.py:332 msgid "Frequency of device polling, in milliseconds" msgstr "" #: lib/logitech_receiver/settings_templates.py:333 #: lib/logitech_receiver/settings_templates.py:1046 #: lib/logitech_receiver/settings_templates.py:1076 msgid "May need Onboard Profiles set to Disable to be effective." msgstr "" #: lib/logitech_receiver/settings_templates.py:365 msgid "Divert crown events" msgstr "" #: lib/logitech_receiver/settings_templates.py:366 msgid "Make crown send CROWN HID++ notifications (which trigger Solaar " "rules but are otherwise ignored)." msgstr "" #: lib/logitech_receiver/settings_templates.py:374 msgid "Crown smooth scroll" msgstr "" #: lib/logitech_receiver/settings_templates.py:375 msgid "Set crown smooth scroll" msgstr "" #: lib/logitech_receiver/settings_templates.py:383 msgid "Divert G Keys" msgstr "" #: lib/logitech_receiver/settings_templates.py:385 msgid "Make G keys send GKEY HID++ notifications (which trigger Solaar " "rules but are otherwise ignored)." msgstr "" #: lib/logitech_receiver/settings_templates.py:386 msgid "May also make M keys and MR key send HID++ notifications" msgstr "" #: lib/logitech_receiver/settings_templates.py:402 msgid "Scroll Wheel Ratcheted" msgstr "" #: lib/logitech_receiver/settings_templates.py:403 msgid "Switch the mouse wheel between speed-controlled ratcheting and " "always freespin." msgstr "" #: lib/logitech_receiver/settings_templates.py:405 msgid "Freespinning" msgstr "" #: lib/logitech_receiver/settings_templates.py:405 msgid "Ratcheted" msgstr "" #: lib/logitech_receiver/settings_templates.py:412 msgid "Scroll Wheel Ratchet Speed" msgstr "" #: lib/logitech_receiver/settings_templates.py:414 msgid "Use the mouse wheel speed to switch between ratcheted and " "freespinning.\n" "The mouse wheel is always ratcheted at 50." msgstr "" #: lib/logitech_receiver/settings_templates.py:463 msgid "Key/Button Actions" msgstr "" #: lib/logitech_receiver/settings_templates.py:465 msgid "Change the action for the key or button." msgstr "Skift handlingen for tasten eller knappen." #: lib/logitech_receiver/settings_templates.py:465 msgid "Overridden by diversion." msgstr "" #: lib/logitech_receiver/settings_templates.py:466 msgid "Changing important actions (such as for the left mouse button) can " "result in an unusable system." msgstr "Ændring af vigtige handlinger (for eksempel venstre museknap) kan " "resultere i et ubrugeligt system." #: lib/logitech_receiver/settings_templates.py:639 msgid "Key/Button Diversion" msgstr "" #: lib/logitech_receiver/settings_templates.py:640 msgid "Make the key or button send HID++ notifications (Diverted) or " "initiate Mouse Gestures or Sliding DPI" msgstr "" #: lib/logitech_receiver/settings_templates.py:643 #: lib/logitech_receiver/settings_templates.py:644 #: lib/logitech_receiver/settings_templates.py:645 msgid "Diverted" msgstr "" #: lib/logitech_receiver/settings_templates.py:643 #: lib/logitech_receiver/settings_templates.py:644 msgid "Mouse Gestures" msgstr "" #: lib/logitech_receiver/settings_templates.py:643 #: lib/logitech_receiver/settings_templates.py:644 #: lib/logitech_receiver/settings_templates.py:645 msgid "Regular" msgstr "" #: lib/logitech_receiver/settings_templates.py:643 msgid "Sliding DPI" msgstr "" #: lib/logitech_receiver/settings_templates.py:711 msgid "Sensitivity (DPI)" msgstr "Følsomhed (DPI)" #: lib/logitech_receiver/settings_templates.py:752 msgid "Sensitivity Switching" msgstr "" #: lib/logitech_receiver/settings_templates.py:754 msgid "Switch the current sensitivity and the remembered sensitivity when " "the key or button is pressed.\n" "If there is no remembered sensitivity, just remember the current " "sensitivity" msgstr "" #: lib/logitech_receiver/settings_templates.py:758 msgid "Off" msgstr "" #: lib/logitech_receiver/settings_templates.py:791 msgid "Disable keys" msgstr "Deaktiver taster" #: lib/logitech_receiver/settings_templates.py:792 msgid "Disable specific keyboard keys." msgstr "Deaktiver specifikke tastaturtaster." #: lib/logitech_receiver/settings_templates.py:795 #, python-format msgid "Disables the %s key." msgstr "" #: lib/logitech_receiver/settings_templates.py:809 #: lib/logitech_receiver/settings_templates.py:860 msgid "Set OS" msgstr "Indstil OS" #: lib/logitech_receiver/settings_templates.py:810 #: lib/logitech_receiver/settings_templates.py:861 msgid "Change keys to match OS." msgstr "Skift taster for at matche OS." #: lib/logitech_receiver/settings_templates.py:873 msgid "Change Host" msgstr "Indstil vært" #: lib/logitech_receiver/settings_templates.py:874 msgid "Switch connection to a different host" msgstr "Skift forbindelse til en anden vært" #: lib/logitech_receiver/settings_templates.py:900 msgid "Performs a left click." msgstr "" #: lib/logitech_receiver/settings_templates.py:900 msgid "Single tap" msgstr "" #: lib/logitech_receiver/settings_templates.py:901 msgid "Performs a right click." msgstr "" #: lib/logitech_receiver/settings_templates.py:901 msgid "Single tap with two fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:902 msgid "Single tap with three fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:906 msgid "Double tap" msgstr "" #: lib/logitech_receiver/settings_templates.py:906 msgid "Performs a double click." msgstr "" #: lib/logitech_receiver/settings_templates.py:907 msgid "Double tap with two fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:908 msgid "Double tap with three fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:911 msgid "Drags items by dragging the finger after double tapping." msgstr "" #: lib/logitech_receiver/settings_templates.py:911 msgid "Tap and drag" msgstr "" #: lib/logitech_receiver/settings_templates.py:913 msgid "Drags items by dragging the fingers after double tapping." msgstr "" #: lib/logitech_receiver/settings_templates.py:913 msgid "Tap and drag with two fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:914 msgid "Tap and drag with three fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:917 msgid "Disables tap and edge gestures (equivalent to pressing Fn+LeftClick)." msgstr "" #: lib/logitech_receiver/settings_templates.py:917 msgid "Suppress tap and edge gestures" msgstr "" #: lib/logitech_receiver/settings_templates.py:918 msgid "Scroll with one finger" msgstr "" #: lib/logitech_receiver/settings_templates.py:918 #: lib/logitech_receiver/settings_templates.py:919 #: lib/logitech_receiver/settings_templates.py:922 msgid "Scrolls." msgstr "" #: lib/logitech_receiver/settings_templates.py:919 #: lib/logitech_receiver/settings_templates.py:922 msgid "Scroll with two fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:920 msgid "Scroll horizontally with two fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:920 msgid "Scrolls horizontally." msgstr "" #: lib/logitech_receiver/settings_templates.py:921 msgid "Scroll vertically with two fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:921 msgid "Scrolls vertically." msgstr "" #: lib/logitech_receiver/settings_templates.py:923 msgid "Inverts the scrolling direction." msgstr "" #: lib/logitech_receiver/settings_templates.py:923 msgid "Natural scrolling" msgstr "" #: lib/logitech_receiver/settings_templates.py:924 msgid "Enables the thumbwheel." msgstr "" #: lib/logitech_receiver/settings_templates.py:924 msgid "Thumbwheel" msgstr "" #: lib/logitech_receiver/settings_templates.py:935 #: lib/logitech_receiver/settings_templates.py:939 msgid "Swipe from the top edge" msgstr "" #: lib/logitech_receiver/settings_templates.py:936 msgid "Swipe from the left edge" msgstr "" #: lib/logitech_receiver/settings_templates.py:937 msgid "Swipe from the right edge" msgstr "" #: lib/logitech_receiver/settings_templates.py:938 msgid "Swipe from the bottom edge" msgstr "" #: lib/logitech_receiver/settings_templates.py:940 msgid "Swipe two fingers from the left edge" msgstr "" #: lib/logitech_receiver/settings_templates.py:941 msgid "Swipe two fingers from the right edge" msgstr "" #: lib/logitech_receiver/settings_templates.py:942 msgid "Swipe two fingers from the bottom edge" msgstr "" #: lib/logitech_receiver/settings_templates.py:943 msgid "Swipe two fingers from the top edge" msgstr "" #: lib/logitech_receiver/settings_templates.py:944 #: lib/logitech_receiver/settings_templates.py:948 msgid "Pinch to zoom out; spread to zoom in." msgstr "" #: lib/logitech_receiver/settings_templates.py:944 msgid "Zoom with two fingers." msgstr "" #: lib/logitech_receiver/settings_templates.py:945 msgid "Pinch to zoom out." msgstr "" #: lib/logitech_receiver/settings_templates.py:946 msgid "Spread to zoom in." msgstr "" #: lib/logitech_receiver/settings_templates.py:947 msgid "Zoom with three fingers." msgstr "" #: lib/logitech_receiver/settings_templates.py:948 msgid "Zoom with two fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:966 msgid "Pixel zone" msgstr "" #: lib/logitech_receiver/settings_templates.py:967 msgid "Ratio zone" msgstr "" #: lib/logitech_receiver/settings_templates.py:968 msgid "Scale factor" msgstr "Skaleringsfaktor" #: lib/logitech_receiver/settings_templates.py:968 msgid "Sets the cursor speed." msgstr "" #: lib/logitech_receiver/settings_templates.py:972 msgid "Left" msgstr "" #: lib/logitech_receiver/settings_templates.py:972 msgid "Left-most coordinate." msgstr "" #: lib/logitech_receiver/settings_templates.py:973 msgid "Bottom" msgstr "" #: lib/logitech_receiver/settings_templates.py:973 msgid "Bottom coordinate." msgstr "" #: lib/logitech_receiver/settings_templates.py:974 msgid "Width" msgstr "Bredde" #: lib/logitech_receiver/settings_templates.py:974 msgid "Width." msgstr "" #: lib/logitech_receiver/settings_templates.py:975 msgid "Height" msgstr "Højde" #: lib/logitech_receiver/settings_templates.py:975 msgid "Height." msgstr "" #: lib/logitech_receiver/settings_templates.py:976 msgid "Cursor speed." msgstr "" #: lib/logitech_receiver/settings_templates.py:976 msgid "Scale" msgstr "" #: lib/logitech_receiver/settings_templates.py:982 msgid "Gestures" msgstr "Gestusser" #: lib/logitech_receiver/settings_templates.py:983 msgid "Tweak the mouse/touchpad behaviour." msgstr "" #: lib/logitech_receiver/settings_templates.py:1000 msgid "Gestures Diversion" msgstr "" #: lib/logitech_receiver/settings_templates.py:1001 msgid "Divert mouse/touchpad gestures." msgstr "" #: lib/logitech_receiver/settings_templates.py:1018 msgid "Gesture params" msgstr "" #: lib/logitech_receiver/settings_templates.py:1019 msgid "Change numerical parameters of a mouse/touchpad." msgstr "" #: lib/logitech_receiver/settings_templates.py:1044 msgid "M-Key LEDs" msgstr "" #: lib/logitech_receiver/settings_templates.py:1046 msgid "Control the M-Key LEDs." msgstr "" #: lib/logitech_receiver/settings_templates.py:1047 #: lib/logitech_receiver/settings_templates.py:1077 msgid "May need G Keys diverted to be effective." msgstr "" #: lib/logitech_receiver/settings_templates.py:1053 #, python-format msgid "Lights up the %s key." msgstr "" #: lib/logitech_receiver/settings_templates.py:1074 msgid "MR-Key LED" msgstr "" #: lib/logitech_receiver/settings_templates.py:1076 msgid "Control the MR-Key LED." msgstr "" #: lib/logitech_receiver/settings_templates.py:1095 msgid "Persistent Key/Button Mapping" msgstr "" #: lib/logitech_receiver/settings_templates.py:1097 msgid "Permanently change the mapping for the key or button." msgstr "" #: lib/logitech_receiver/settings_templates.py:1098 msgid "Changing important keys or buttons (such as for the left mouse " "button) can result in an unusable system." msgstr "" #: lib/logitech_receiver/settings_templates.py:1157 msgid "Sidetone" msgstr "" #: lib/logitech_receiver/settings_templates.py:1158 msgid "Set sidetone level." msgstr "" #: lib/logitech_receiver/settings_templates.py:1167 msgid "Equalizer" msgstr "" #: lib/logitech_receiver/settings_templates.py:1168 msgid "Set equalizer levels." msgstr "" #: lib/logitech_receiver/settings_templates.py:1191 msgid "Hz" msgstr "" #: lib/logitech_receiver/settings_templates.py:1197 msgid "Power Management" msgstr "" #: lib/logitech_receiver/settings_templates.py:1198 msgid "Power off in minutes (0 for never)." msgstr "" #: lib/logitech_receiver/status.py:114 msgid "No paired devices." msgstr "Ingen forbundne enheder." #: lib/logitech_receiver/status.py:115 lib/solaar/ui/window.py:622 #, python-format msgid "%(count)s paired device." msgid_plural "%(count)s paired devices." msgstr[0] "%(count)s parret enhed." msgstr[1] "%(count)s parrede enheder." #: lib/logitech_receiver/status.py:170 #, python-format msgid "Battery: %(level)s" msgstr "Batteri: %(level)s" #: lib/logitech_receiver/status.py:172 #, python-format msgid "Battery: %(percent)d%%" msgstr "Batteri: %(percent)d%%" #: lib/logitech_receiver/status.py:184 #, python-format msgid "Lighting: %(level)s lux" msgstr "Belysning: %(level)s lux" #: lib/logitech_receiver/status.py:239 #, python-format msgid "Battery: %(level)s (%(status)s)" msgstr "Batteri: %(level)s (%(status)s)" #: lib/logitech_receiver/status.py:241 #, python-format msgid "Battery: %(percent)d%% (%(status)s)" msgstr "Batteri: %(percent)d%% (%(status)s)" #: lib/solaar/ui/__init__.py:52 msgid "Permissions error" msgstr "Tilladelsesfejl" #: lib/solaar/ui/__init__.py:54 #, python-format msgid "Found a Logitech receiver or device (%s), but did not have " "permission to open it." msgstr "" #: lib/solaar/ui/__init__.py:55 msgid "If you've just installed Solaar, try disconnecting the receiver or " "device and then reconnecting it." msgstr "" #: lib/solaar/ui/__init__.py:58 msgid "Cannot connect to device error" msgstr "" #: lib/solaar/ui/__init__.py:60 #, python-format msgid "Found a Logitech receiver or device at %s, but encountered an error " "connecting to it." msgstr "" #: lib/solaar/ui/__init__.py:61 msgid "Try disconnecting the device and then reconnecting it or turning it " "off and then on." msgstr "" #: lib/solaar/ui/__init__.py:64 msgid "Unpairing failed" msgstr "Frakobling mislykkedes" #: lib/solaar/ui/__init__.py:66 #, python-brace-format msgid "Failed to unpair %{device} from %{receiver}." msgstr "Mislykkedes med at bryde paret %{device} og %{receiver}." #: lib/solaar/ui/__init__.py:67 msgid "The receiver returned an error, with no further details." msgstr "Modtageren returnerede en fejl uden yderligere detaljer." #: lib/solaar/ui/__init__.py:177 msgid "Another Solaar process is already running so just expose its window" msgstr "" #: lib/solaar/ui/about.py:36 msgid "Manages Logitech receivers,\n" "keyboards, mice, and tablets." msgstr "" #: lib/solaar/ui/about.py:44 msgid "Additional Programming" msgstr "" #: lib/solaar/ui/about.py:45 msgid "GUI design" msgstr "GUI-design" #: lib/solaar/ui/about.py:47 msgid "Testing" msgstr "Testing" #: lib/solaar/ui/about.py:54 msgid "Logitech documentation" msgstr "Logitech-dokumentation" #: lib/solaar/ui/action.py:85 lib/solaar/ui/action.py:89 #: lib/solaar/ui/window.py:197 msgid "Unpair" msgstr "Fjern parring" #: lib/solaar/ui/action.py:88 lib/solaar/ui/diversion_rules.py:150 msgid "Cancel" msgstr "" #: lib/solaar/ui/config_panel.py:212 msgid "Complete - ENTER to change" msgstr "" #: lib/solaar/ui/config_panel.py:212 msgid "Incomplete" msgstr "" #: lib/solaar/ui/config_panel.py:455 lib/solaar/ui/config_panel.py:507 #, python-format msgid "%d value" msgid_plural "%d values" msgstr[0] "" msgstr[1] "" #: lib/solaar/ui/config_panel.py:518 msgid "Changes allowed" msgstr "" #: lib/solaar/ui/config_panel.py:519 msgid "No changes allowed" msgstr "" #: lib/solaar/ui/config_panel.py:520 msgid "Ignore this setting" msgstr "" #: lib/solaar/ui/config_panel.py:565 msgid "Working" msgstr "Succes" #: lib/solaar/ui/config_panel.py:568 msgid "Read/write operation failed." msgstr "Læse-/skriveoperationen mislykkedes." #: lib/solaar/ui/diversion_rules.py:65 msgid "Built-in rules" msgstr "" #: lib/solaar/ui/diversion_rules.py:65 msgid "User-defined rules" msgstr "" #: lib/solaar/ui/diversion_rules.py:67 lib/solaar/ui/diversion_rules.py:1083 msgid "Rule" msgstr "" #: lib/solaar/ui/diversion_rules.py:68 lib/solaar/ui/diversion_rules.py:509 #: lib/solaar/ui/diversion_rules.py:636 msgid "Sub-rule" msgstr "" #: lib/solaar/ui/diversion_rules.py:70 msgid "[empty]" msgstr "" #: lib/solaar/ui/diversion_rules.py:94 msgid "Solaar Rule Editor" msgstr "" #: lib/solaar/ui/diversion_rules.py:141 msgid "Make changes permanent?" msgstr "" #: lib/solaar/ui/diversion_rules.py:146 msgid "Yes" msgstr "Ja" #: lib/solaar/ui/diversion_rules.py:148 msgid "No" msgstr "" #: lib/solaar/ui/diversion_rules.py:153 msgid "If you choose No, changes will be lost when Solaar is closed." msgstr "" #: lib/solaar/ui/diversion_rules.py:201 msgid "Save changes" msgstr "" #: lib/solaar/ui/diversion_rules.py:206 msgid "Discard changes" msgstr "" #: lib/solaar/ui/diversion_rules.py:372 msgid "Insert here" msgstr "" #: lib/solaar/ui/diversion_rules.py:374 msgid "Insert above" msgstr "" #: lib/solaar/ui/diversion_rules.py:376 msgid "Insert below" msgstr "" #: lib/solaar/ui/diversion_rules.py:382 msgid "Insert new rule here" msgstr "" #: lib/solaar/ui/diversion_rules.py:384 msgid "Insert new rule above" msgstr "" #: lib/solaar/ui/diversion_rules.py:386 msgid "Insert new rule below" msgstr "" #: lib/solaar/ui/diversion_rules.py:427 msgid "Paste here" msgstr "" #: lib/solaar/ui/diversion_rules.py:429 msgid "Paste above" msgstr "" #: lib/solaar/ui/diversion_rules.py:431 msgid "Paste below" msgstr "" #: lib/solaar/ui/diversion_rules.py:437 msgid "Paste rule here" msgstr "" #: lib/solaar/ui/diversion_rules.py:439 msgid "Paste rule above" msgstr "" #: lib/solaar/ui/diversion_rules.py:441 msgid "Paste rule below" msgstr "" #: lib/solaar/ui/diversion_rules.py:445 msgid "Paste rule" msgstr "" #: lib/solaar/ui/diversion_rules.py:474 msgid "Flatten" msgstr "" #: lib/solaar/ui/diversion_rules.py:507 msgid "Insert" msgstr "" #: lib/solaar/ui/diversion_rules.py:510 lib/solaar/ui/diversion_rules.py:638 #: lib/solaar/ui/diversion_rules.py:1126 msgid "Or" msgstr "" #: lib/solaar/ui/diversion_rules.py:511 lib/solaar/ui/diversion_rules.py:637 #: lib/solaar/ui/diversion_rules.py:1111 msgid "And" msgstr "" #: lib/solaar/ui/diversion_rules.py:513 msgid "Condition" msgstr "" #: lib/solaar/ui/diversion_rules.py:515 lib/solaar/ui/diversion_rules.py:1292 msgid "Feature" msgstr "" #: lib/solaar/ui/diversion_rules.py:516 lib/solaar/ui/diversion_rules.py:1328 msgid "Report" msgstr "" #: lib/solaar/ui/diversion_rules.py:517 lib/solaar/ui/diversion_rules.py:1204 msgid "Process" msgstr "" #: lib/solaar/ui/diversion_rules.py:518 msgid "Mouse process" msgstr "" #: lib/solaar/ui/diversion_rules.py:519 lib/solaar/ui/diversion_rules.py:1366 msgid "Modifiers" msgstr "" #: lib/solaar/ui/diversion_rules.py:520 lib/solaar/ui/diversion_rules.py:1419 msgid "Key" msgstr "" #: lib/solaar/ui/diversion_rules.py:521 lib/solaar/ui/diversion_rules.py:1461 msgid "KeyIsDown" msgstr "" #: lib/solaar/ui/diversion_rules.py:522 lib/solaar/ui/diversion_rules.py:2260 msgid "Active" msgstr "Aktiv" #: lib/solaar/ui/diversion_rules.py:523 lib/solaar/ui/diversion_rules.py:2218 #: lib/solaar/ui/diversion_rules.py:2270 lib/solaar/ui/diversion_rules.py:2323 msgid "Device" msgstr "" #: lib/solaar/ui/diversion_rules.py:524 lib/solaar/ui/diversion_rules.py:2297 msgid "Host" msgstr "" #: lib/solaar/ui/diversion_rules.py:525 lib/solaar/ui/diversion_rules.py:2339 msgid "Setting" msgstr "" #: lib/solaar/ui/diversion_rules.py:526 lib/solaar/ui/diversion_rules.py:1477 #: lib/solaar/ui/diversion_rules.py:1526 msgid "Test" msgstr "" #: lib/solaar/ui/diversion_rules.py:527 lib/solaar/ui/diversion_rules.py:1643 msgid "Test bytes" msgstr "" #: lib/solaar/ui/diversion_rules.py:528 lib/solaar/ui/diversion_rules.py:1736 msgid "Mouse Gesture" msgstr "" #: lib/solaar/ui/diversion_rules.py:532 msgid "Action" msgstr "Handling" #: lib/solaar/ui/diversion_rules.py:534 lib/solaar/ui/diversion_rules.py:1845 msgid "Key press" msgstr "" #: lib/solaar/ui/diversion_rules.py:535 lib/solaar/ui/diversion_rules.py:1897 msgid "Mouse scroll" msgstr "" #: lib/solaar/ui/diversion_rules.py:536 lib/solaar/ui/diversion_rules.py:1959 msgid "Mouse click" msgstr "" #: lib/solaar/ui/diversion_rules.py:537 msgid "Set" msgstr "" #: lib/solaar/ui/diversion_rules.py:538 lib/solaar/ui/diversion_rules.py:2030 msgid "Execute" msgstr "" #: lib/solaar/ui/diversion_rules.py:539 lib/solaar/ui/diversion_rules.py:1158 msgid "Later" msgstr "" #: lib/solaar/ui/diversion_rules.py:568 msgid "Insert new rule" msgstr "" #: lib/solaar/ui/diversion_rules.py:588 lib/solaar/ui/diversion_rules.py:1686 #: lib/solaar/ui/diversion_rules.py:1790 lib/solaar/ui/diversion_rules.py:1989 msgid "Delete" msgstr "" #: lib/solaar/ui/diversion_rules.py:610 msgid "Negate" msgstr "" #: lib/solaar/ui/diversion_rules.py:634 msgid "Wrap with" msgstr "" #: lib/solaar/ui/diversion_rules.py:656 msgid "Cut" msgstr "Klip" #: lib/solaar/ui/diversion_rules.py:671 msgid "Paste" msgstr "Indsæt" #: lib/solaar/ui/diversion_rules.py:683 msgid "Copy" msgstr "Kopiér" #: lib/solaar/ui/diversion_rules.py:1063 msgid "This editor does not support the selected rule component yet." msgstr "" #: lib/solaar/ui/diversion_rules.py:1138 msgid "Number of seconds to delay." msgstr "" #: lib/solaar/ui/diversion_rules.py:1177 msgid "Not" msgstr "" #: lib/solaar/ui/diversion_rules.py:1187 msgid "X11 active process. For use in X11 only." msgstr "" #: lib/solaar/ui/diversion_rules.py:1218 msgid "X11 mouse process. For use in X11 only." msgstr "" #: lib/solaar/ui/diversion_rules.py:1235 msgid "MouseProcess" msgstr "" #: lib/solaar/ui/diversion_rules.py:1260 msgid "Feature name of notification triggering rule processing." msgstr "" #: lib/solaar/ui/diversion_rules.py:1308 msgid "Report number of notification triggering rule processing." msgstr "" #: lib/solaar/ui/diversion_rules.py:1342 msgid "Active keyboard modifiers. Not always available in Wayland." msgstr "" #: lib/solaar/ui/diversion_rules.py:1383 msgid "Diverted key or button depressed or released.\n" "Use the Key/Button Diversion and Divert G Keys settings to divert " "keys and buttons." msgstr "" #: lib/solaar/ui/diversion_rules.py:1392 msgid "Key down" msgstr "" #: lib/solaar/ui/diversion_rules.py:1395 msgid "Key up" msgstr "" #: lib/solaar/ui/diversion_rules.py:1436 msgid "Diverted key or button is currently down.\n" "Use the Key/Button Diversion and Divert G Keys settings to divert " "keys and buttons." msgstr "" #: lib/solaar/ui/diversion_rules.py:1475 msgid "Test condition on notification triggering rule processing." msgstr "" #: lib/solaar/ui/diversion_rules.py:1479 msgid "Parameter" msgstr "" #: lib/solaar/ui/diversion_rules.py:1542 msgid "begin (inclusive)" msgstr "" #: lib/solaar/ui/diversion_rules.py:1543 msgid "end (exclusive)" msgstr "" #: lib/solaar/ui/diversion_rules.py:1552 msgid "range" msgstr "" #: lib/solaar/ui/diversion_rules.py:1554 msgid "minimum" msgstr "" #: lib/solaar/ui/diversion_rules.py:1555 msgid "maximum" msgstr "" #: lib/solaar/ui/diversion_rules.py:1557 #, python-format msgid "bytes %(0)d to %(1)d, ranging from %(2)d to %(3)d" msgstr "" #: lib/solaar/ui/diversion_rules.py:1562 msgid "mask" msgstr "" #: lib/solaar/ui/diversion_rules.py:1563 #, python-format msgid "bytes %(0)d to %(1)d, mask %(2)d" msgstr "" #: lib/solaar/ui/diversion_rules.py:1573 msgid "Bit or range test on bytes in notification message triggering rule " "processing." msgstr "" #: lib/solaar/ui/diversion_rules.py:1583 msgid "type" msgstr "" #: lib/solaar/ui/diversion_rules.py:1666 msgid "Mouse gesture with optional initiating button followed by zero or " "more mouse movements." msgstr "" #: lib/solaar/ui/diversion_rules.py:1671 msgid "Add movement" msgstr "" #: lib/solaar/ui/diversion_rules.py:1764 msgid "Simulate a chorded key click or depress or release.\n" "On Wayland requires write access to /dev/uinput." msgstr "" #: lib/solaar/ui/diversion_rules.py:1769 msgid "Add key" msgstr "" #: lib/solaar/ui/diversion_rules.py:1772 msgid "Click" msgstr "" #: lib/solaar/ui/diversion_rules.py:1775 msgid "Depress" msgstr "" #: lib/solaar/ui/diversion_rules.py:1778 msgid "Release" msgstr "" #: lib/solaar/ui/diversion_rules.py:1861 msgid "Simulate a mouse scroll.\n" "On Wayland requires write access to /dev/uinput." msgstr "" #: lib/solaar/ui/diversion_rules.py:1918 msgid "Simulate a mouse click.\n" "On Wayland requires write access to /dev/uinput." msgstr "" #: lib/solaar/ui/diversion_rules.py:1921 msgid "Button" msgstr "" #: lib/solaar/ui/diversion_rules.py:1922 msgid "Count and Action" msgstr "" #: lib/solaar/ui/diversion_rules.py:1972 msgid "Execute a command with arguments." msgstr "" #: lib/solaar/ui/diversion_rules.py:1975 msgid "Add argument" msgstr "" #: lib/solaar/ui/diversion_rules.py:2050 msgid "Toggle" msgstr "" #: lib/solaar/ui/diversion_rules.py:2050 msgid "True" msgstr "" #: lib/solaar/ui/diversion_rules.py:2051 msgid "False" msgstr "" #: lib/solaar/ui/diversion_rules.py:2065 msgid "Unsupported setting" msgstr "" #: lib/solaar/ui/diversion_rules.py:2223 lib/solaar/ui/diversion_rules.py:2242 #: lib/solaar/ui/diversion_rules.py:2328 lib/solaar/ui/diversion_rules.py:2570 #: lib/solaar/ui/diversion_rules.py:2588 msgid "Originating device" msgstr "" #: lib/solaar/ui/diversion_rules.py:2256 msgid "Device is active and its settings can be changed." msgstr "" #: lib/solaar/ui/diversion_rules.py:2266 msgid "Device that originated the current notification." msgstr "" #: lib/solaar/ui/diversion_rules.py:2280 msgid "Name of host computer." msgstr "" #: lib/solaar/ui/diversion_rules.py:2347 msgid "Value" msgstr "Værdi" #: lib/solaar/ui/diversion_rules.py:2355 msgid "Item" msgstr "" #: lib/solaar/ui/diversion_rules.py:2630 msgid "Change setting on device" msgstr "" #: lib/solaar/ui/diversion_rules.py:2647 msgid "Setting on device" msgstr "" #: lib/solaar/ui/notify.py:124 lib/solaar/ui/tray.py:318 #: lib/solaar/ui/tray.py:323 lib/solaar/ui/window.py:739 msgid "offline" msgstr "afslået" #: lib/solaar/ui/pair_window.py:122 lib/solaar/ui/pair_window.py:256 #: lib/solaar/ui/pair_window.py:288 #, python-format msgid "%(receiver_name)s: pair new device" msgstr "%(receiver_name)s: parre ny enhed" #: lib/solaar/ui/pair_window.py:123 #, python-format msgid "Enter passcode on %(name)s." msgstr "" #: lib/solaar/ui/pair_window.py:126 #, python-format msgid "Type %(passcode)s and then press the enter key." msgstr "" #: lib/solaar/ui/pair_window.py:129 msgid "left" msgstr "" #: lib/solaar/ui/pair_window.py:129 msgid "right" msgstr "" #: lib/solaar/ui/pair_window.py:131 #, python-format msgid "Press %(code)s\n" "and then press left and right buttons simultaneously." msgstr "" #: lib/solaar/ui/pair_window.py:188 msgid "Pairing failed" msgstr "Parring mislykkedes" #: lib/solaar/ui/pair_window.py:190 msgid "Make sure your device is within range, and has a decent battery " "charge." msgstr "Sørg for, at din enhed er inden for rækkevidde og har en anstændig " "batteriopladning." #: lib/solaar/ui/pair_window.py:192 msgid "A new device was detected, but it is not compatible with this " "receiver." msgstr "Der blev fundet en ny enhed, men den er ikke kompatibel med denne " "modtager." #: lib/solaar/ui/pair_window.py:194 msgid "More paired devices than receiver can support." msgstr "Flere parrede enheder end modtageren kan understøtte." #: lib/solaar/ui/pair_window.py:196 msgid "No further details are available about the error." msgstr "Ingen yderligere oplysninger er tilgængelige om fejlen." #: lib/solaar/ui/pair_window.py:210 msgid "Found a new device:" msgstr "Fandt en ny enhed:" #: lib/solaar/ui/pair_window.py:235 msgid "The wireless link is not encrypted" msgstr "Den trådløse tilslutningen er ukryptert" #: lib/solaar/ui/pair_window.py:264 msgid "Unifying receivers are only compatible with Unifying devices." msgstr "" #: lib/solaar/ui/pair_window.py:266 msgid "Bolt receivers are only compatible with Bolt devices." msgstr "" #: lib/solaar/ui/pair_window.py:268 msgid "Other receivers are only compatible with a few devices." msgstr "" #: lib/solaar/ui/pair_window.py:270 msgid "The device must not be paired with a nearby powered-on receiver." msgstr "" #: lib/solaar/ui/pair_window.py:274 msgid "Press a pairing button or key until the pairing light flashes " "quickly." msgstr "" #: lib/solaar/ui/pair_window.py:276 msgid "You may have to first turn the device off and on again." msgstr "" #: lib/solaar/ui/pair_window.py:278 msgid "Turn on the device you want to pair." msgstr "Tænd for den enhed, du vil parre." #: lib/solaar/ui/pair_window.py:280 msgid "If the device is already turned on, turn it off and on again." msgstr "" #: lib/solaar/ui/pair_window.py:283 #, python-format msgid "\n" "\n" "This receiver has %d pairing remaining." msgid_plural "\n" "\n" "This receiver has %d pairings remaining." msgstr[0] "\n" "\n" "Denne modtager har %d parring tilbage." msgstr[1] "\n" "\n" "Denne modtager har %d parringer tilbage." #: lib/solaar/ui/pair_window.py:286 msgid "\n" "Cancelling at this point will not use up a pairing." msgstr "\n" "Annullering på dette tidspunkt bruger ikke en parring." #: lib/solaar/ui/tray.py:58 msgid "No supported device found" msgstr "" #: lib/solaar/ui/tray.py:64 lib/solaar/ui/window.py:319 #, python-format msgid "About %s" msgstr "Om %s" #: lib/solaar/ui/tray.py:65 lib/solaar/ui/window.py:317 #, python-format msgid "Quit %s" msgstr "Afslut %s" #: lib/solaar/ui/tray.py:297 lib/solaar/ui/tray.py:305 msgid "no receiver" msgstr "ingen modtager" #: lib/solaar/ui/tray.py:321 msgid "no status" msgstr "ingen status" #: lib/solaar/ui/window.py:96 msgid "Scanning" msgstr "Søger" #: lib/solaar/ui/window.py:129 msgid "Battery" msgstr "Batteri" #: lib/solaar/ui/window.py:132 msgid "Wireless Link" msgstr "Trådløs tilslutning" #: lib/solaar/ui/window.py:136 msgid "Lighting" msgstr "Belysning" #: lib/solaar/ui/window.py:170 msgid "Show Technical Details" msgstr "Vis tekniske detaljer" #: lib/solaar/ui/window.py:186 msgid "Pair new device" msgstr "Parre ny enhed" #: lib/solaar/ui/window.py:205 msgid "Select a device" msgstr "Vælg en enhed" #: lib/solaar/ui/window.py:322 msgid "Rule Editor" msgstr "" #: lib/solaar/ui/window.py:533 msgid "Path" msgstr "Sti" #: lib/solaar/ui/window.py:536 msgid "USB ID" msgstr "" #: lib/solaar/ui/window.py:539 lib/solaar/ui/window.py:541 #: lib/solaar/ui/window.py:561 lib/solaar/ui/window.py:563 msgid "Serial" msgstr "Seriel" #: lib/solaar/ui/window.py:545 msgid "Index" msgstr "Indeks" #: lib/solaar/ui/window.py:547 msgid "Wireless PID" msgstr "Trådløs-PID" #: lib/solaar/ui/window.py:549 msgid "Product ID" msgstr "Produkt-id" #: lib/solaar/ui/window.py:551 msgid "Protocol" msgstr "Protokol" #: lib/solaar/ui/window.py:551 msgid "Unknown" msgstr "Ukendt" #: lib/solaar/ui/window.py:554 #, python-format msgid "%(rate)d ms (%(rate_hz)dHz)" msgstr "%(rate)d ms (%(rate_hz)dHz)" #: lib/solaar/ui/window.py:554 msgid "Polling rate" msgstr "Spørgerate" #: lib/solaar/ui/window.py:565 msgid "Unit ID" msgstr "" #: lib/solaar/ui/window.py:576 msgid "none" msgstr "ingen" #: lib/solaar/ui/window.py:577 msgid "Notifications" msgstr "Varsler" #: lib/solaar/ui/window.py:621 msgid "No device paired." msgstr "Ingen enhed parret." #: lib/solaar/ui/window.py:628 #, python-format msgid "Up to %(max_count)s device can be paired to this receiver." msgid_plural "Up to %(max_count)s devices can be paired to this receiver." msgstr[0] "Op til %(max_count)s enhed kan parres med denne modtager." msgstr[1] "Op til %(max_count)s enheder kan parres med denne modtager." #: lib/solaar/ui/window.py:634 msgid "Only one device can be paired to this receiver." msgstr "Kun en enhed kan parres med denne modtager." #: lib/solaar/ui/window.py:638 #, python-format msgid "This receiver has %d pairing remaining." msgid_plural "This receiver has %d pairings remaining." msgstr[0] "Denne modtager har %d parring tilbage." msgstr[1] "Denne modtager har %d parringer tilbage." #: lib/solaar/ui/window.py:692 msgid "Battery Voltage" msgstr "" #: lib/solaar/ui/window.py:694 msgid "Voltage reported by battery" msgstr "" #: lib/solaar/ui/window.py:696 msgid "Battery Level" msgstr "" #: lib/solaar/ui/window.py:698 msgid "Approximate level reported by battery" msgstr "" #: lib/solaar/ui/window.py:705 lib/solaar/ui/window.py:707 msgid "next reported " msgstr "" #: lib/solaar/ui/window.py:708 msgid " and next level to be reported." msgstr "" #: lib/solaar/ui/window.py:713 msgid "last known" msgstr "sidst kendt" #: lib/solaar/ui/window.py:724 msgid "encrypted" msgstr "kryptert" #: lib/solaar/ui/window.py:726 msgid "The wireless link between this device and its receiver is encrypted." msgstr "Den trådløse forbindelse mellem denne enhed og dens modtager er " "krypteret." #: lib/solaar/ui/window.py:728 msgid "not encrypted" msgstr "ukrypteret" #: lib/solaar/ui/window.py:732 msgid "The wireless link between this device and its receiver is not " "encrypted.\n" "This is a security issue for pointing devices, and a major security " "issue for text-input devices." msgstr "" #: lib/solaar/ui/window.py:748 #, python-format msgid "%(light_level)d lux" msgstr "%(light_level)d lux" #~ msgid "\n" #~ "\n" #~ "This receiver has %d pairing(s) remaining." #~ msgstr "\n" #~ "\n" #~ "Denne modtager har %d parring(er) tilbage." #~ msgid "Actions" #~ msgstr "Handlinger" #~ msgid "Automatically switch the mouse wheel between ratchet and " #~ "freespin mode.\n" #~ "The mouse wheel is always free at 0, and always locked at 50" #~ msgstr "Skift automatisk musehjulet mellem skralde og freespin-" #~ "tilstand.\n" #~ "Musehjulet er altid frit ved 0, og altid låst ved 50" #~ msgid "Count" #~ msgstr "Antal" #~ msgid "Effectively turns off thumb scrolling in Linux." #~ msgstr "Slår i praksis af tommelrulling i Linux." #~ msgid "Effectively turns off wheel scrolling in Linux." #~ msgstr "Slår i praksis af hjulrulling i Linux." #~ msgid "For more information see the Solaar installation directions\n" #~ "at https://pwr-solaar.github.io/Solaar/installation" #~ msgstr "Yderligere oplysninger finder du i Installationsvejledningen " #~ "til Solaar\n" #~ "på https://pwr-solaar.github.io/Solaar/installation" #, python-format #~ msgid "Found a Logitech Receiver (%s), but did not have permission " #~ "to open it." #~ msgstr "Fundet en Logitech-modtager (%s), men havde ikke tilladelse " #~ "til at åbne den." #~ msgid "HID++ Scrolling" #~ msgstr "HID++ rulling" #~ msgid "HID++ Thumb Scrolling" #~ msgstr "HID++ tommelrulling" #~ msgid "HID++ mode for horizontal scroll with the thumb wheel." #~ msgstr "HID ++ -tilstand til vandret rulling med tommelhjulet." #~ msgid "HID++ mode for vertical scroll with the wheel." #~ msgstr "HID++ tilstand til lodret rulling med hjulet." #~ msgid "High Resolution Scrolling" #~ msgstr "Høyfølsom rulling" #~ msgid "High Resolution Wheel Invert" #~ msgstr "Inverter høyfølsom rulling" #~ msgid "High-sensitivity wheel invert direction for vertical scroll." #~ msgstr "Inverter retning til høyfølsom lodret rulling med hjulet." #~ msgid "If the device is already turned on, turn if off and on again." #~ msgstr "Hvis enheden allerede er tændt, slå den fra og til igen." #~ msgid "If you've just installed Solaar, try removing the receiver " #~ "and plugging it back in." #~ msgstr "Hvis du lige har installeret Solaar, kan du prøve at fjerne " #~ "modtageren og tilslutte den igen." #~ msgid "Invert thumb scroll direction." #~ msgstr "Inverter retning på tommelrulling." #~ msgid "No Logitech receiver found" #~ msgstr "Der blev ikke fundet nogen Logitech-modtager" #~ msgid "Shows status of devices connected\n" #~ "through wireless Logitech receivers." #~ msgstr "Viser status for enheder, der er tilsluttet\n" #~ "via trådløse Logitech-modtagere." #~ msgid "Smooth Scrolling" #~ msgstr "Glat rulling" #~ msgid "Solaar depends on a udev file that is not present" #~ msgstr "Solaar er afhængig af en udev-fil, der ikke er til stede" #~ msgid "The receiver only supports %d paired device(s)." #~ msgstr "Modtageren understøtter kun %d parrede enhed(er)." #~ msgid "The receiver was unplugged." #~ msgstr "Mottakeren fjernedes." #~ msgid "The wireless link between this device and its receiver is " #~ "not encrypted.\n" #~ "\n" #~ "For pointing devices (mice, trackballs, trackpads), this is a minor " #~ "security issue.\n" #~ "\n" #~ "It is, however, a major security issue for text-input devices " #~ "(keyboards, numpads),\n" #~ "because typed text can be sniffed inconspicuously by 3rd parties " #~ "within range." #~ msgstr "Den trådløse forbindelse mellem denne enhed og dens modtager " #~ "er ikke krypteret.\n" #~ "\n" #~ "For pegeenheder (mus, trackballs, pegefelter) er dette et mindre " #~ "sikkerhedsproblem.\n" #~ "\n" #~ "Det er imidlertid et vigtigt sikkerhedsproblem for tekstinputenheder " #~ "(tastaturer, numpads),\n" #~ "fordi maskinskrevet tekst kan sniffes upåfaldende af 3rd parter " #~ "inden for rækkevidde." #~ msgid "This receiver has %d pairing(s) remaining." #~ msgstr "Denne modtager har %d parring(er) tilbage." #~ msgid "Thumb Scroll Invert" #~ msgstr "Inverter tommelrulling" #~ msgid "USB id" #~ msgstr "USB-id" #~ msgid "Unexpected device number (%s) in notification %s." #~ msgstr "Uventet enhedsnummer (%s) i varsel %s." #~ msgid "Wheel Resolution" #~ msgstr "Hjulopløsning" #~ msgid "height" #~ msgstr "højde" #~ msgid "next " #~ msgstr "næste " #~ msgid "unknown" #~ msgstr "ukendt" #~ msgid "width" #~ msgstr "bredde" Solaar-1.1.14/po/de.po000066400000000000000000001701431473525213700144040ustar00rootroot00000000000000# German translations for solaar package. # Copyright (C) 2014 THE solaar'S COPYRIGHT HOLDER # This file is distributed under the same license as the solaar package. # Automatically generated, 2014. # msgid "" msgstr "Project-Id-Version: solaar 1.0.1\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-12-28 17:40+0100\n" "PO-Revision-Date: 2023-07-07 11:06+0200\n" "Last-Translator: Daniel Frost \n" "Language-Team: none\n" "Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Launchpad-Export-Date: 2021-04-17 16:52+0000\n" "X-Generator: Poedit 3.3.2\n" #: lib/logitech_receiver/base_usb.py:46 msgid "Bolt Receiver" msgstr "Bolt Empfänger" #: lib/logitech_receiver/base_usb.py:57 msgid "Unifying Receiver" msgstr "Unifying Empfänger" #: lib/logitech_receiver/base_usb.py:67 lib/logitech_receiver/base_usb.py:78 #: lib/logitech_receiver/base_usb.py:90 lib/logitech_receiver/base_usb.py:102 #: lib/logitech_receiver/base_usb.py:114 msgid "Nano Receiver" msgstr "Nano Empfänger" #: lib/logitech_receiver/base_usb.py:124 msgid "Lightspeed Receiver" msgstr "Lightspeed Empfänger" #: lib/logitech_receiver/base_usb.py:133 msgid "EX100 Receiver 27 Mhz" msgstr "EX100 Empfänger 27 Mhz" #: lib/logitech_receiver/i18n.py:30 msgid "empty" msgstr "entladen" #: lib/logitech_receiver/i18n.py:31 msgid "critical" msgstr "kritisch" #: lib/logitech_receiver/i18n.py:32 msgid "low" msgstr "niedrig" #: lib/logitech_receiver/i18n.py:33 msgid "average" msgstr "mittel" #: lib/logitech_receiver/i18n.py:34 msgid "good" msgstr "gut" #: lib/logitech_receiver/i18n.py:35 msgid "full" msgstr "voll" #: lib/logitech_receiver/i18n.py:38 msgid "discharging" msgstr "entlädt" #: lib/logitech_receiver/i18n.py:39 msgid "recharging" msgstr "lädt wieder auf" #: lib/logitech_receiver/i18n.py:40 lib/solaar/ui/window.py:711 msgid "charging" msgstr "lädt" #: lib/logitech_receiver/i18n.py:41 msgid "not charging" msgstr "lädt nicht" #: lib/logitech_receiver/i18n.py:42 msgid "almost full" msgstr "nahezu voll" #: lib/logitech_receiver/i18n.py:43 msgid "charged" msgstr "geladen" #: lib/logitech_receiver/i18n.py:44 msgid "slow recharge" msgstr "langsames Aufladen" #: lib/logitech_receiver/i18n.py:45 msgid "invalid battery" msgstr "unzulässiger/defekter Akku" #: lib/logitech_receiver/i18n.py:46 msgid "thermal error" msgstr "thermischer Fehler" #: lib/logitech_receiver/i18n.py:47 msgid "error" msgstr "Fehler" #: lib/logitech_receiver/i18n.py:48 msgid "standard" msgstr "standard" #: lib/logitech_receiver/i18n.py:49 msgid "fast" msgstr "schnell" #: lib/logitech_receiver/i18n.py:50 msgid "slow" msgstr "langsam" #: lib/logitech_receiver/i18n.py:53 msgid "device timeout" msgstr "Geräte Zeitüberschreitung" #: lib/logitech_receiver/i18n.py:54 msgid "device not supported" msgstr "nicht unterstütztes Gerät" #: lib/logitech_receiver/i18n.py:55 msgid "too many devices" msgstr "zu viele Geräte" #: lib/logitech_receiver/i18n.py:56 msgid "sequence timeout" msgstr "Zeitüberschreitung im Ablauf" #: lib/logitech_receiver/i18n.py:59 lib/solaar/ui/window.py:572 msgid "Firmware" msgstr "Firmware" #: lib/logitech_receiver/i18n.py:60 msgid "Bootloader" msgstr "Bootloader" #: lib/logitech_receiver/i18n.py:61 msgid "Hardware" msgstr "Hardware" #: lib/logitech_receiver/i18n.py:62 msgid "Other" msgstr "Andere" #: lib/logitech_receiver/i18n.py:65 msgid "Left Button" msgstr "Linke Taste" #: lib/logitech_receiver/i18n.py:66 msgid "Right Button" msgstr "Rechte Taste" #: lib/logitech_receiver/i18n.py:67 msgid "Middle Button" msgstr "Mittlere Taste" #: lib/logitech_receiver/i18n.py:68 msgid "Back Button" msgstr "Zurück Taste" #: lib/logitech_receiver/i18n.py:69 msgid "Forward Button" msgstr "Vorwärts Taste" #: lib/logitech_receiver/i18n.py:70 msgid "Mouse Gesture Button" msgstr "Gestentaste" #: lib/logitech_receiver/i18n.py:71 msgid "Smart Shift" msgstr "Smart Shift" #: lib/logitech_receiver/i18n.py:72 msgid "DPI Switch" msgstr "DPI Schalter" #: lib/logitech_receiver/i18n.py:73 msgid "Left Tilt" msgstr "Links kippen" #: lib/logitech_receiver/i18n.py:74 msgid "Right Tilt" msgstr "Rechts kippen" #: lib/logitech_receiver/i18n.py:75 msgid "Left Click" msgstr "Linksklick" #: lib/logitech_receiver/i18n.py:76 msgid "Right Click" msgstr "Rechtsklick" #: lib/logitech_receiver/i18n.py:77 msgid "Mouse Middle Button" msgstr "Maus mittlere Taste" #: lib/logitech_receiver/i18n.py:78 msgid "Mouse Back Button" msgstr "Maus \"Zurück\" Taste" #: lib/logitech_receiver/i18n.py:79 msgid "Mouse Forward Button" msgstr "Maus \"Vorwärts\"\n" " Taste" #: lib/logitech_receiver/i18n.py:80 msgid "Gesture Button Navigation" msgstr "Gestentaste-Navigation" #: lib/logitech_receiver/i18n.py:81 msgid "Mouse Scroll Left Button" msgstr "Maus \"Links scrollen\" Taste" #: lib/logitech_receiver/i18n.py:82 msgid "Mouse Scroll Right Button" msgstr "Maus \"Rechts scrollen\" Taste" #: lib/logitech_receiver/i18n.py:85 msgid "pressed" msgstr "gedrückt" #: lib/logitech_receiver/i18n.py:86 msgid "released" msgstr "losgelassen" #: lib/logitech_receiver/notifications.py:75 #: lib/logitech_receiver/notifications.py:126 msgid "pairing lock is closed" msgstr "Koppelsperre ist inaktiv" #: lib/logitech_receiver/notifications.py:75 #: lib/logitech_receiver/notifications.py:126 msgid "pairing lock is open" msgstr "Koppelsperre ist aktiv" #: lib/logitech_receiver/notifications.py:92 msgid "discovery lock is closed" msgstr "Entdeckungssperre ist inaktiv" #: lib/logitech_receiver/notifications.py:92 msgid "discovery lock is open" msgstr "Entdeckungssperre ist aktiv" #: lib/logitech_receiver/notifications.py:224 lib/solaar/ui/notify.py:122 msgid "connected" msgstr "verbunden" #: lib/logitech_receiver/notifications.py:224 msgid "disconnected" msgstr "nicht verbunden" #: lib/logitech_receiver/notifications.py:262 lib/solaar/ui/notify.py:120 msgid "unpaired" msgstr "nicht gekoppelt" #: lib/logitech_receiver/notifications.py:304 msgid "powered on" msgstr "eingeschaltet" #: lib/logitech_receiver/settings.py:750 msgid "register" msgstr "registrieren" #: lib/logitech_receiver/settings.py:764 lib/logitech_receiver/settings.py:791 msgid "feature" msgstr "Feature" #: lib/logitech_receiver/settings_templates.py:139 msgid "Swap Fx function" msgstr "Fn-Tastenbelegung umkehren" #: lib/logitech_receiver/settings_templates.py:140 msgid "When set, the F1..F12 keys will activate their special function,\n" "and you must hold the FN key to activate their standard function." msgstr "Wenn aktiviert, dann behalten die Tasten F1-F12 ihre " "Sonderbelegung.\n" "Die jeweilige Standardbelegung kann über die FN-Taste aktiviert " "werden." #: lib/logitech_receiver/settings_templates.py:142 msgid "When unset, the F1..F12 keys will activate their standard function,\n" "and you must hold the FN key to activate their special function." msgstr "Wenn nicht aktiviert, dann behalten die Tasten F1-F12 ihre " "Standardbelegung.\n" "Die jeweilige Sonderbelegung kann über die FN-Taste aktiviert werden." #: lib/logitech_receiver/settings_templates.py:149 msgid "Hand Detection" msgstr "Handerkennung" #: lib/logitech_receiver/settings_templates.py:150 msgid "Turn on illumination when the hands hover over the keyboard." msgstr "Schaltet die Beleuchtung ein, wenn sich die Hand über der Tastatur " "befindet." #: lib/logitech_receiver/settings_templates.py:157 msgid "Scroll Wheel Smooth Scrolling" msgstr "Weiches Mausrad-Scrollen" #: lib/logitech_receiver/settings_templates.py:158 #: lib/logitech_receiver/settings_templates.py:239 #: lib/logitech_receiver/settings_templates.py:267 msgid "High-sensitivity mode for vertical scroll with the wheel." msgstr "Hohe Empfindlichkeit für vertikales Scrollen mit dem Mausrad." #: lib/logitech_receiver/settings_templates.py:165 msgid "Side Scrolling" msgstr "Seitliches Scrollen" #: lib/logitech_receiver/settings_templates.py:167 msgid "When disabled, pushing the wheel sideways sends custom button " "events\n" "instead of the standard side-scrolling events." msgstr "Wenn ausgeschaltet, werden durch seitliches Kippen des Mausrads " "benutzer-\n" "definierte Maustastenereignisse gesendet, anstatt der vorgegebenen " "Seitlich-Scrollen-\n" "Ereignisse." #: lib/logitech_receiver/settings_templates.py:177 msgid "Sensitivity (DPI - older mice)" msgstr "Empfindlichkeit (DPI - ältere Mäuse)" #: lib/logitech_receiver/settings_templates.py:178 #: lib/logitech_receiver/settings_templates.py:712 msgid "Mouse movement sensitivity" msgstr "Mauszeigerempfindlichkeit" #: lib/logitech_receiver/settings_templates.py:208 #: lib/logitech_receiver/settings_templates.py:218 #: lib/logitech_receiver/settings_templates.py:225 msgid "Backlight" msgstr "Hintergrundbeleuchtung" #: lib/logitech_receiver/settings_templates.py:209 #: lib/logitech_receiver/settings_templates.py:226 msgid "Set illumination time for keyboard." msgstr "Die Beleuchtungszeit für die Tastatur einstellen." #: lib/logitech_receiver/settings_templates.py:219 msgid "Turn illumination on or off on keyboard." msgstr "Schaltet die Tastaturbeleuchtung an oder aus." #: lib/logitech_receiver/settings_templates.py:237 msgid "Scroll Wheel High Resolution" msgstr "Hohe Auflösung für Mausrad-Scrollen" #: lib/logitech_receiver/settings_templates.py:240 #: lib/logitech_receiver/settings_templates.py:268 msgid "Set to ignore if scrolling is abnormally fast or slow" msgstr "Aktivieren zum Ignorieren, falls Scrollen ungewöhnlich langsam oder " "schnell ist" #: lib/logitech_receiver/settings_templates.py:247 #: lib/logitech_receiver/settings_templates.py:277 msgid "Scroll Wheel Diversion" msgstr "Mausrad Umleitung" #: lib/logitech_receiver/settings_templates.py:249 msgid "Make scroll wheel send LOWRES_WHEEL HID++ notifications (which " "trigger Solaar rules but are otherwise ignored)." msgstr "Lasse das Mausrad LOWRES_WHEEL HID++ Nachrichten senden (diese lösen " "Solaar Regeln aus, werden sonst aber ignoriert)." #: lib/logitech_receiver/settings_templates.py:256 msgid "Scroll Wheel Direction" msgstr "Mausrad-Scroll-Richtung" #: lib/logitech_receiver/settings_templates.py:257 msgid "Invert direction for vertical scroll with wheel." msgstr "Kehrt die vertikale Scroll-Richtung des Mausrads um." #: lib/logitech_receiver/settings_templates.py:265 msgid "Scroll Wheel Resolution" msgstr "Auflösung für Mausrad-Scrollen" #: lib/logitech_receiver/settings_templates.py:279 msgid "Make scroll wheel send HIRES_WHEEL HID++ notifications (which " "trigger Solaar rules but are otherwise ignored)." msgstr "Lasse das Mausrad HIRES_WHEEL HID++ Nachrichten senden (diese lösen " "Solaar Regeln aus, werden sonst aber ignoriert)." #: lib/logitech_receiver/settings_templates.py:288 msgid "Sensitivity (Pointer Speed)" msgstr "Empfindlichkeit (Zeigergeschwindigkeit)" #: lib/logitech_receiver/settings_templates.py:289 msgid "Speed multiplier for mouse (256 is normal multiplier)." msgstr "Geschwindigkeits-Multiplikator für die Maus (256 ist der normale " "Multiplikator)." #: lib/logitech_receiver/settings_templates.py:299 msgid "Thumb Wheel Diversion" msgstr "Daumenrad Umleitung" #: lib/logitech_receiver/settings_templates.py:301 msgid "Make thumb wheel send THUMB_WHEEL HID++ notifications (which trigger " "Solaar rules but are otherwise ignored)." msgstr "Lasse das Daumenrad THUMB_WHEEL HID++ Nachrichten senden (diese " "lösen Solaar Regeln aus, werden sonst aber ignoriert)." #: lib/logitech_receiver/settings_templates.py:310 msgid "Thumb Wheel Direction" msgstr "Daumenrad-Scroll-Richtung" #: lib/logitech_receiver/settings_templates.py:311 msgid "Invert thumb wheel scroll direction." msgstr "Kehrt die Scroll-Richtung des Daumenrads um." #: lib/logitech_receiver/settings_templates.py:319 msgid "Onboard Profiles" msgstr "Onboard-Profile" #: lib/logitech_receiver/settings_templates.py:320 msgid "Enable onboard profiles, which often control report rate and " "keyboard lighting" msgstr "Schaltet Onboard-Profile an, welche oft die Wiederholrate und die " "Tastenbeleuchtung steuern" #: lib/logitech_receiver/settings_templates.py:330 msgid "Polling Rate (ms)" msgstr "Abtastrate (ms)" #: lib/logitech_receiver/settings_templates.py:332 msgid "Frequency of device polling, in milliseconds" msgstr "Frequenz der Geräteabtastung, in Millisekunden" #: lib/logitech_receiver/settings_templates.py:333 #: lib/logitech_receiver/settings_templates.py:1046 #: lib/logitech_receiver/settings_templates.py:1076 msgid "May need Onboard Profiles set to Disable to be effective." msgstr "Onboard-Profile müssen eventuell deaktivert sein damit dies " "funktioniert." #: lib/logitech_receiver/settings_templates.py:365 msgid "Divert crown events" msgstr "Drehregler-Ereignisse umleiten" #: lib/logitech_receiver/settings_templates.py:366 msgid "Make crown send CROWN HID++ notifications (which trigger Solaar " "rules but are otherwise ignored)." msgstr "Das Senden von CROWN-HID++-Mitteilungen beim Bedienen des " "Drehreglers erzwingen (diese lösen Solaar Regeln aus, werden sonst " "aber ignoriert)." #: lib/logitech_receiver/settings_templates.py:374 msgid "Crown smooth scroll" msgstr "Crown weiches scrollen" #: lib/logitech_receiver/settings_templates.py:375 msgid "Set crown smooth scroll" msgstr "Aktiviere Crown weiches scrollen" #: lib/logitech_receiver/settings_templates.py:383 msgid "Divert G Keys" msgstr "G-Tasten umleiten" #: lib/logitech_receiver/settings_templates.py:385 msgid "Make G keys send GKEY HID++ notifications (which trigger Solaar " "rules but are otherwise ignored)." msgstr "Das Senden von GKEY-HID++-Mitteilungen beim Drücken der G-Tasten " "erzwingen (diese lösen Solaar Regeln aus, werden sonst aber " "ignoriert)." #: lib/logitech_receiver/settings_templates.py:386 msgid "May also make M keys and MR key send HID++ notifications" msgstr "Die M und MR Tasten senden ggf. auch HID++ Benachrichtigungen" #: lib/logitech_receiver/settings_templates.py:402 msgid "Scroll Wheel Ratcheted" msgstr "Mausrad-Rastfunktion" #: lib/logitech_receiver/settings_templates.py:403 msgid "Switch the mouse wheel between speed-controlled ratcheting and " "always freespin." msgstr "Schaltet beim Mausrad um zwischen geschwindigkeitskontrolliert mit " "einrasten und immer freilaufend." #: lib/logitech_receiver/settings_templates.py:405 msgid "Freespinning" msgstr "Freilaufen" #: lib/logitech_receiver/settings_templates.py:405 msgid "Ratcheted" msgstr "Einrastend" #: lib/logitech_receiver/settings_templates.py:412 msgid "Scroll Wheel Ratchet Speed" msgstr "Mausrad-Rastgeschwindigkeit" #: lib/logitech_receiver/settings_templates.py:414 msgid "Use the mouse wheel speed to switch between ratcheted and " "freespinning.\n" "The mouse wheel is always ratcheted at 50." msgstr "Mausrad automatisch zwischen einrastendem und freilaufendem Modus " "umschalten.\n" "Das Mausrad läuft bei 0 stets frei und verhält sich bei 50 stets " "einrastend." #: lib/logitech_receiver/settings_templates.py:463 msgid "Key/Button Actions" msgstr "Tasten-/Maustastenaktionen" #: lib/logitech_receiver/settings_templates.py:465 msgid "Change the action for the key or button." msgstr "Die Aktion für die Taste oder die Maustaste ändern." #: lib/logitech_receiver/settings_templates.py:465 msgid "Overridden by diversion." msgstr "Durch Umleitung überschrieben." #: lib/logitech_receiver/settings_templates.py:466 msgid "Changing important actions (such as for the left mouse button) can " "result in an unusable system." msgstr "Das Ändern wichtiger Aktionen (wie z.B. für die linke Maustaste) " "kann zu einem unbenutzbaren System führen." #: lib/logitech_receiver/settings_templates.py:639 msgid "Key/Button Diversion" msgstr "Taste/Maustaste umleiten" #: lib/logitech_receiver/settings_templates.py:640 msgid "Make the key or button send HID++ notifications (Diverted) or " "initiate Mouse Gestures or Sliding DPI" msgstr "Die Taste/Maustaste sendet HID++-Mitteilungen (umgeleitet) oder " "initiiert Mausgesten oder Gleiten DPI" #: lib/logitech_receiver/settings_templates.py:643 #: lib/logitech_receiver/settings_templates.py:644 #: lib/logitech_receiver/settings_templates.py:645 msgid "Diverted" msgstr "Umgeleitet" #: lib/logitech_receiver/settings_templates.py:643 #: lib/logitech_receiver/settings_templates.py:644 msgid "Mouse Gestures" msgstr "Mausgesten" #: lib/logitech_receiver/settings_templates.py:643 #: lib/logitech_receiver/settings_templates.py:644 #: lib/logitech_receiver/settings_templates.py:645 msgid "Regular" msgstr "Normal" #: lib/logitech_receiver/settings_templates.py:643 msgid "Sliding DPI" msgstr "Gleiten DPI" #: lib/logitech_receiver/settings_templates.py:711 msgid "Sensitivity (DPI)" msgstr "Empfindlichkeit (DPI)" #: lib/logitech_receiver/settings_templates.py:752 msgid "Sensitivity Switching" msgstr "Umschaltung Empfindlichkeit" #: lib/logitech_receiver/settings_templates.py:754 msgid "Switch the current sensitivity and the remembered sensitivity when " "the key or button is pressed.\n" "If there is no remembered sensitivity, just remember the current " "sensitivity" msgstr "Tauscht die aktuelle Empfindlichkeit und die abgespeicherte " "Empfindlichkeit wenn die Taste gedrückt wird.\n" "Falls keine gespeicherte Empfindlichkeit vorhanden ist, wird die " "aktuelle Empfindlichkeit abgespeichert" #: lib/logitech_receiver/settings_templates.py:758 msgid "Off" msgstr "Aus" #: lib/logitech_receiver/settings_templates.py:791 msgid "Disable keys" msgstr "Tasten abschalten" #: lib/logitech_receiver/settings_templates.py:792 msgid "Disable specific keyboard keys." msgstr "Bestimmte Tastatur-Tasten abschalten." #: lib/logitech_receiver/settings_templates.py:795 #, python-format msgid "Disables the %s key." msgstr "Deaktiviert die %s-Taste." #: lib/logitech_receiver/settings_templates.py:809 #: lib/logitech_receiver/settings_templates.py:860 msgid "Set OS" msgstr "Betriebssystem auswählen" #: lib/logitech_receiver/settings_templates.py:810 #: lib/logitech_receiver/settings_templates.py:861 msgid "Change keys to match OS." msgstr "Ändert Tasten so, dass Sie zum BS passen." #: lib/logitech_receiver/settings_templates.py:873 msgid "Change Host" msgstr "Anderen Empfänger wählen" #: lib/logitech_receiver/settings_templates.py:874 msgid "Switch connection to a different host" msgstr "Verbindung zu einem anderen Empfänger umschalten" #: lib/logitech_receiver/settings_templates.py:900 msgid "Performs a left click." msgstr "Führt einen Linksklick aus." #: lib/logitech_receiver/settings_templates.py:900 msgid "Single tap" msgstr "Einfaches Tippen" #: lib/logitech_receiver/settings_templates.py:901 msgid "Performs a right click." msgstr "Führt einen Rechtsklick aus." #: lib/logitech_receiver/settings_templates.py:901 msgid "Single tap with two fingers" msgstr "Einfaches Tippen mit zwei Fingern" #: lib/logitech_receiver/settings_templates.py:902 msgid "Single tap with three fingers" msgstr "Einfaches Tippen mit drei Fingern" #: lib/logitech_receiver/settings_templates.py:906 msgid "Double tap" msgstr "Doppeltes Tippen" #: lib/logitech_receiver/settings_templates.py:906 msgid "Performs a double click." msgstr "Führt einen Doppelklick aus." #: lib/logitech_receiver/settings_templates.py:907 msgid "Double tap with two fingers" msgstr "Doppeltes Tippen mit zwei Fingern" #: lib/logitech_receiver/settings_templates.py:908 msgid "Double tap with three fingers" msgstr "Doppeltes Tippen mit drei Fingern" #: lib/logitech_receiver/settings_templates.py:911 msgid "Drags items by dragging the finger after double tapping." msgstr "Zieht Objekte durch Ziehen des Fingers nach doppeltem Tippen." #: lib/logitech_receiver/settings_templates.py:911 msgid "Tap and drag" msgstr "Tippen-und-Ziehen" #: lib/logitech_receiver/settings_templates.py:913 msgid "Drags items by dragging the fingers after double tapping." msgstr "Zieht Objekte durch Ziehen der Finger nach doppeltem Tippen." #: lib/logitech_receiver/settings_templates.py:913 msgid "Tap and drag with two fingers" msgstr "Tippen und mit zwei Fingern ziehen" #: lib/logitech_receiver/settings_templates.py:914 msgid "Tap and drag with three fingers" msgstr "Tippen und mit drei Fingern ziehen" #: lib/logitech_receiver/settings_templates.py:917 msgid "Disables tap and edge gestures (equivalent to pressing Fn+LeftClick)." msgstr "Schaltet Tipp- und Bildschirmrand-Gesten ab (entspricht dem Drücken " "von Fn+Linksklick)." #: lib/logitech_receiver/settings_templates.py:917 msgid "Suppress tap and edge gestures" msgstr "Tipp- und Bildschirmrand-Gesten abschalten" #: lib/logitech_receiver/settings_templates.py:918 msgid "Scroll with one finger" msgstr "Mit einem Finger scrollen" #: lib/logitech_receiver/settings_templates.py:918 #: lib/logitech_receiver/settings_templates.py:919 #: lib/logitech_receiver/settings_templates.py:922 msgid "Scrolls." msgstr "Scrollt." #: lib/logitech_receiver/settings_templates.py:919 #: lib/logitech_receiver/settings_templates.py:922 msgid "Scroll with two fingers" msgstr "Mit zwei Fingern scrollen" #: lib/logitech_receiver/settings_templates.py:920 msgid "Scroll horizontally with two fingers" msgstr "Horizontales Scrollen mit zwei Fingern" #: lib/logitech_receiver/settings_templates.py:920 msgid "Scrolls horizontally." msgstr "Scrollt horizontal." #: lib/logitech_receiver/settings_templates.py:921 msgid "Scroll vertically with two fingers" msgstr "Vertikales Scrollen mit zwei Fingern" #: lib/logitech_receiver/settings_templates.py:921 msgid "Scrolls vertically." msgstr "Scrollt vertikal." #: lib/logitech_receiver/settings_templates.py:923 msgid "Inverts the scrolling direction." msgstr "Kehrt die Scroll-Richtung um." #: lib/logitech_receiver/settings_templates.py:923 msgid "Natural scrolling" msgstr "Natürlicher Bildlauf" #: lib/logitech_receiver/settings_templates.py:924 msgid "Enables the thumbwheel." msgstr "Aktiviert das Daumenrad." #: lib/logitech_receiver/settings_templates.py:924 msgid "Thumbwheel" msgstr "Daumenrad" #: lib/logitech_receiver/settings_templates.py:935 #: lib/logitech_receiver/settings_templates.py:939 msgid "Swipe from the top edge" msgstr "Vom oberen Bildschirmrand wischen" #: lib/logitech_receiver/settings_templates.py:936 msgid "Swipe from the left edge" msgstr "Vom linken Bildschirmrand wischen" #: lib/logitech_receiver/settings_templates.py:937 msgid "Swipe from the right edge" msgstr "Vom rechten Bildschirmrand wischen" #: lib/logitech_receiver/settings_templates.py:938 msgid "Swipe from the bottom edge" msgstr "Vom unteren Bildschirmrand wischen" #: lib/logitech_receiver/settings_templates.py:940 msgid "Swipe two fingers from the left edge" msgstr "Mit zwei Fingern vom linken Bildschirmrand wischen" #: lib/logitech_receiver/settings_templates.py:941 msgid "Swipe two fingers from the right edge" msgstr "Mit zwei Fingern vom rechten Bildschirmrand wischen" #: lib/logitech_receiver/settings_templates.py:942 msgid "Swipe two fingers from the bottom edge" msgstr "Mit zwei Fingern vom unteren Bildschirmrand wischen" #: lib/logitech_receiver/settings_templates.py:943 msgid "Swipe two fingers from the top edge" msgstr "Mit zwei Fingern vom oberen Bildschirmrand wischen" #: lib/logitech_receiver/settings_templates.py:944 #: lib/logitech_receiver/settings_templates.py:948 msgid "Pinch to zoom out; spread to zoom in." msgstr "Kneifgeste zum Verkleinern; Spreizgeste zum Vergrößern." #: lib/logitech_receiver/settings_templates.py:944 msgid "Zoom with two fingers." msgstr "Vergrößerung mit zwei Fingern ändern." #: lib/logitech_receiver/settings_templates.py:945 msgid "Pinch to zoom out." msgstr "Kneifgeste zum Verkleinern." #: lib/logitech_receiver/settings_templates.py:946 msgid "Spread to zoom in." msgstr "Spreizgeste zum Vergrößern." #: lib/logitech_receiver/settings_templates.py:947 msgid "Zoom with three fingers." msgstr "Vergrößerung mit drei Fingern ändern." #: lib/logitech_receiver/settings_templates.py:948 msgid "Zoom with two fingers" msgstr "Vergrößerung mit zwei Fingern" #: lib/logitech_receiver/settings_templates.py:966 msgid "Pixel zone" msgstr "Pixel Zone" #: lib/logitech_receiver/settings_templates.py:967 msgid "Ratio zone" msgstr "Verhältnis Zone" #: lib/logitech_receiver/settings_templates.py:968 msgid "Scale factor" msgstr "Skalierungsfaktor" #: lib/logitech_receiver/settings_templates.py:968 msgid "Sets the cursor speed." msgstr "Stellt die Cursorgeschwindigkeit ein." #: lib/logitech_receiver/settings_templates.py:972 msgid "Left" msgstr "Links" #: lib/logitech_receiver/settings_templates.py:972 msgid "Left-most coordinate." msgstr "Äußerste linke Koordinate." #: lib/logitech_receiver/settings_templates.py:973 msgid "Bottom" msgstr "Ende" #: lib/logitech_receiver/settings_templates.py:973 msgid "Bottom coordinate." msgstr "Ende Koordinate." #: lib/logitech_receiver/settings_templates.py:974 msgid "Width" msgstr "Breite" #: lib/logitech_receiver/settings_templates.py:974 msgid "Width." msgstr "Breite." #: lib/logitech_receiver/settings_templates.py:975 msgid "Height" msgstr "Höhe" #: lib/logitech_receiver/settings_templates.py:975 msgid "Height." msgstr "Höhe." #: lib/logitech_receiver/settings_templates.py:976 msgid "Cursor speed." msgstr "Zeigergeschwindigkeit." #: lib/logitech_receiver/settings_templates.py:976 msgid "Scale" msgstr "Skalierung" #: lib/logitech_receiver/settings_templates.py:982 msgid "Gestures" msgstr "Gesten" #: lib/logitech_receiver/settings_templates.py:983 msgid "Tweak the mouse/touchpad behaviour." msgstr "Das Maus-/Tastfeldverhalten einstellen." #: lib/logitech_receiver/settings_templates.py:1000 msgid "Gestures Diversion" msgstr "Gesten Umleitung" #: lib/logitech_receiver/settings_templates.py:1001 msgid "Divert mouse/touchpad gestures." msgstr "Maus/Tastfeldgesten umleiten." #: lib/logitech_receiver/settings_templates.py:1018 msgid "Gesture params" msgstr "Gestenparameter" #: lib/logitech_receiver/settings_templates.py:1019 msgid "Change numerical parameters of a mouse/touchpad." msgstr "Numerische Parameter einer Maus/eines Tastfelds ändern." #: lib/logitech_receiver/settings_templates.py:1044 msgid "M-Key LEDs" msgstr "M-Taste LED's" #: lib/logitech_receiver/settings_templates.py:1046 msgid "Control the M-Key LEDs." msgstr "Steuert die M-Tasten LED's." #: lib/logitech_receiver/settings_templates.py:1047 #: lib/logitech_receiver/settings_templates.py:1077 msgid "May need G Keys diverted to be effective." msgstr "G Tasten müssen evtl. umgeleitet sein um zu funktionieren." #: lib/logitech_receiver/settings_templates.py:1053 #, python-format msgid "Lights up the %s key." msgstr "Beleuchtet die %s Tasten." #: lib/logitech_receiver/settings_templates.py:1074 msgid "MR-Key LED" msgstr "MR-Taste LED" #: lib/logitech_receiver/settings_templates.py:1076 msgid "Control the MR-Key LED." msgstr "Steuert die MR-Taste LED." #: lib/logitech_receiver/settings_templates.py:1095 msgid "Persistent Key/Button Mapping" msgstr "Dauerhafte Tasten-/Maustastenzuordnung" #: lib/logitech_receiver/settings_templates.py:1097 msgid "Permanently change the mapping for the key or button." msgstr "Ändert die Zuordung für die Taste/Maustaste dauerhaft." #: lib/logitech_receiver/settings_templates.py:1098 msgid "Changing important keys or buttons (such as for the left mouse " "button) can result in an unusable system." msgstr "Änderungen an wichtigen Tasten oder Maustasten (wie an der linken " "Maustaste) können zu einem unbenutzbaren System führen." #: lib/logitech_receiver/settings_templates.py:1157 msgid "Sidetone" msgstr "Nebenton" #: lib/logitech_receiver/settings_templates.py:1158 msgid "Set sidetone level." msgstr "Setze Nebentonlautstärke." #: lib/logitech_receiver/settings_templates.py:1167 msgid "Equalizer" msgstr "Equalizer" #: lib/logitech_receiver/settings_templates.py:1168 msgid "Set equalizer levels." msgstr "Setze Equalizerlautstärke." #: lib/logitech_receiver/settings_templates.py:1191 msgid "Hz" msgstr "Hz" #: lib/logitech_receiver/settings_templates.py:1197 msgid "Power Management" msgstr "Energieverwaltung" #: lib/logitech_receiver/settings_templates.py:1198 msgid "Power off in minutes (0 for never)." msgstr "Ausschalten in Minuten (0 für nie)" #: lib/logitech_receiver/status.py:114 msgid "No paired devices." msgstr "Keine gekoppelten Geräte." #: lib/logitech_receiver/status.py:115 lib/solaar/ui/window.py:622 #, python-format msgid "%(count)s paired device." msgid_plural "%(count)s paired devices." msgstr[0] "%(count)s gekoppeltes Gerät." msgstr[1] "%(count)s gekoppelte Geräte." #: lib/logitech_receiver/status.py:170 #, python-format msgid "Battery: %(level)s" msgstr "Akku: %(level)s" #: lib/logitech_receiver/status.py:172 #, python-format msgid "Battery: %(percent)d%%" msgstr "Akku: %(percent)d%%" #: lib/logitech_receiver/status.py:184 #, python-format msgid "Lighting: %(level)s lux" msgstr "Beleuchtung: %(level)s lux" #: lib/logitech_receiver/status.py:239 #, python-format msgid "Battery: %(level)s (%(status)s)" msgstr "Akku: %(level)s (%(status)s)" #: lib/logitech_receiver/status.py:241 #, python-format msgid "Battery: %(percent)d%% (%(status)s)" msgstr "Akku: %(percent)d%% (%(status)s)" #: lib/solaar/ui/__init__.py:52 msgid "Permissions error" msgstr "Zugriffsfehler" #: lib/solaar/ui/__init__.py:54 #, python-format msgid "Found a Logitech receiver or device (%s), but did not have " "permission to open it." msgstr "Es wurde ein Logitech Empfänger oder Gerät (%s) gefunden, aber es " "fehlen die Berechtigungen dieses zu öffnen." #: lib/solaar/ui/__init__.py:55 msgid "If you've just installed Solaar, try disconnecting the receiver or " "device and then reconnecting it." msgstr "Falls du Solaar gerade erst installiert hast, versuche den Empfänger " "oder das Geräte zu trennen und dann erneut zu verbinden." #: lib/solaar/ui/__init__.py:58 msgid "Cannot connect to device error" msgstr "Kann nicht mit dem Gerät verbinden Fehler" #: lib/solaar/ui/__init__.py:60 #, python-format msgid "Found a Logitech receiver or device at %s, but encountered an error " "connecting to it." msgstr "Ein Logitech Empfänger oder Gerät wurde auf %s gefunden, aber beim " "Verbinden ist ein Fehler aufgetreten." #: lib/solaar/ui/__init__.py:61 msgid "Try disconnecting the device and then reconnecting it or turning it " "off and then on." msgstr "Versuche die Verbindung zu dem Geräte zu trennen und neu zu " "verbinden oder dieses aus- und wieder einzuschalten." #: lib/solaar/ui/__init__.py:64 msgid "Unpairing failed" msgstr "Entkopplung fehlgeschlagen" #: lib/solaar/ui/__init__.py:66 #, python-brace-format msgid "Failed to unpair %{device} from %{receiver}." msgstr "Kopplung zwischen Gerät %{device} und Empfänger %{receiver} konnte " "nicht gelöst werden." #: lib/solaar/ui/__init__.py:67 msgid "The receiver returned an error, with no further details." msgstr "Der Empfänger gab einen Fehler zurück, ohne weitere Details." #: lib/solaar/ui/__init__.py:177 msgid "Another Solaar process is already running so just expose its window" msgstr "Ein anderer Solaar-Prozess läuft bereits deshalb nur dessen Fenster " "in den Vordergrund holen" #: lib/solaar/ui/about.py:36 msgid "Manages Logitech receivers,\n" "keyboards, mice, and tablets." msgstr "Verwaltet Logitech Empfänger, Tastaturen, Mäuse und Grafiktablets." #: lib/solaar/ui/about.py:44 msgid "Additional Programming" msgstr "Zusätzliche Programmierung" #: lib/solaar/ui/about.py:45 msgid "GUI design" msgstr "GUI Design" #: lib/solaar/ui/about.py:47 msgid "Testing" msgstr "Tester" #: lib/solaar/ui/about.py:54 msgid "Logitech documentation" msgstr "Logitech-Dokumentation" #: lib/solaar/ui/action.py:85 lib/solaar/ui/action.py:89 #: lib/solaar/ui/window.py:197 msgid "Unpair" msgstr "Entkoppeln" #: lib/solaar/ui/action.py:88 lib/solaar/ui/diversion_rules.py:150 msgid "Cancel" msgstr "Abbrechen" #: lib/solaar/ui/config_panel.py:212 msgid "Complete - ENTER to change" msgstr "Abgeschlossen - ENTER zu ändern" #: lib/solaar/ui/config_panel.py:212 msgid "Incomplete" msgstr "Unvollständig" #: lib/solaar/ui/config_panel.py:455 lib/solaar/ui/config_panel.py:507 #, python-format msgid "%d value" msgid_plural "%d values" msgstr[0] "%d Wert" msgstr[1] "%d Werte" #: lib/solaar/ui/config_panel.py:518 msgid "Changes allowed" msgstr "Änderungen erlaubt" #: lib/solaar/ui/config_panel.py:519 msgid "No changes allowed" msgstr "Änderungen nicht erlaubt" #: lib/solaar/ui/config_panel.py:520 msgid "Ignore this setting" msgstr "Diese Einstellung ignorieren" #: lib/solaar/ui/config_panel.py:565 msgid "Working" msgstr "Beschäftigt" #: lib/solaar/ui/config_panel.py:568 msgid "Read/write operation failed." msgstr "Lese-/Schreiboperation fehlgeschlagen." #: lib/solaar/ui/diversion_rules.py:65 msgid "Built-in rules" msgstr "Integrierte Regeln" #: lib/solaar/ui/diversion_rules.py:65 msgid "User-defined rules" msgstr "Benutzerdefinierte Regeln" #: lib/solaar/ui/diversion_rules.py:67 lib/solaar/ui/diversion_rules.py:1083 msgid "Rule" msgstr "Regel" #: lib/solaar/ui/diversion_rules.py:68 lib/solaar/ui/diversion_rules.py:509 #: lib/solaar/ui/diversion_rules.py:636 msgid "Sub-rule" msgstr "Unterregel" #: lib/solaar/ui/diversion_rules.py:70 msgid "[empty]" msgstr "[leer]" #: lib/solaar/ui/diversion_rules.py:94 msgid "Solaar Rule Editor" msgstr "Solaar-Regelbearbeitung" #: lib/solaar/ui/diversion_rules.py:141 msgid "Make changes permanent?" msgstr "Änderungen dauerhaft anwenden?" #: lib/solaar/ui/diversion_rules.py:146 msgid "Yes" msgstr "Ja" #: lib/solaar/ui/diversion_rules.py:148 msgid "No" msgstr "Nein" #: lib/solaar/ui/diversion_rules.py:153 msgid "If you choose No, changes will be lost when Solaar is closed." msgstr "Wenn Sie »Nein« wählen, gehen alle Änderungen beim Schließen von " "Solaar verloren." #: lib/solaar/ui/diversion_rules.py:201 msgid "Save changes" msgstr "Änderungen speichern" #: lib/solaar/ui/diversion_rules.py:206 msgid "Discard changes" msgstr "Änderungen verwerfen" #: lib/solaar/ui/diversion_rules.py:372 msgid "Insert here" msgstr "Hier hinzufügen" #: lib/solaar/ui/diversion_rules.py:374 msgid "Insert above" msgstr "Darüber hinzufügen" #: lib/solaar/ui/diversion_rules.py:376 msgid "Insert below" msgstr "Darunter hinzufügen" #: lib/solaar/ui/diversion_rules.py:382 msgid "Insert new rule here" msgstr "Neue Regel hier hinzufügen" #: lib/solaar/ui/diversion_rules.py:384 msgid "Insert new rule above" msgstr "Neue Regel darüber hinzufügen" #: lib/solaar/ui/diversion_rules.py:386 msgid "Insert new rule below" msgstr "Neue Regel darunter hinzufügen" #: lib/solaar/ui/diversion_rules.py:427 msgid "Paste here" msgstr "Hier einfügen" #: lib/solaar/ui/diversion_rules.py:429 msgid "Paste above" msgstr "Darüber einfügen" #: lib/solaar/ui/diversion_rules.py:431 msgid "Paste below" msgstr "Darunter einfügen" #: lib/solaar/ui/diversion_rules.py:437 msgid "Paste rule here" msgstr "Regel hier einfügen" #: lib/solaar/ui/diversion_rules.py:439 msgid "Paste rule above" msgstr "Regel darüber einfügen" #: lib/solaar/ui/diversion_rules.py:441 msgid "Paste rule below" msgstr "Regel darunter einfügen" #: lib/solaar/ui/diversion_rules.py:445 msgid "Paste rule" msgstr "Regel einfügen" #: lib/solaar/ui/diversion_rules.py:474 msgid "Flatten" msgstr "Einrückung aufheben" #: lib/solaar/ui/diversion_rules.py:507 msgid "Insert" msgstr "Hinzufügen" #: lib/solaar/ui/diversion_rules.py:510 lib/solaar/ui/diversion_rules.py:638 #: lib/solaar/ui/diversion_rules.py:1126 msgid "Or" msgstr "Oder" #: lib/solaar/ui/diversion_rules.py:511 lib/solaar/ui/diversion_rules.py:637 #: lib/solaar/ui/diversion_rules.py:1111 msgid "And" msgstr "Und" #: lib/solaar/ui/diversion_rules.py:513 msgid "Condition" msgstr "Bedingung" #: lib/solaar/ui/diversion_rules.py:515 lib/solaar/ui/diversion_rules.py:1292 msgid "Feature" msgstr "Funktion" #: lib/solaar/ui/diversion_rules.py:516 lib/solaar/ui/diversion_rules.py:1328 msgid "Report" msgstr "Rückgabewert" #: lib/solaar/ui/diversion_rules.py:517 lib/solaar/ui/diversion_rules.py:1204 msgid "Process" msgstr "Prozessname (Anwendung)" #: lib/solaar/ui/diversion_rules.py:518 msgid "Mouse process" msgstr "Maus Prozess" #: lib/solaar/ui/diversion_rules.py:519 lib/solaar/ui/diversion_rules.py:1366 msgid "Modifiers" msgstr "Zusatztasten" #: lib/solaar/ui/diversion_rules.py:520 lib/solaar/ui/diversion_rules.py:1419 msgid "Key" msgstr "Taste" #: lib/solaar/ui/diversion_rules.py:521 lib/solaar/ui/diversion_rules.py:1461 msgid "KeyIsDown" msgstr "TasteIstGedrückt" #: lib/solaar/ui/diversion_rules.py:522 lib/solaar/ui/diversion_rules.py:2260 msgid "Active" msgstr "Aktiv" #: lib/solaar/ui/diversion_rules.py:523 lib/solaar/ui/diversion_rules.py:2218 #: lib/solaar/ui/diversion_rules.py:2270 lib/solaar/ui/diversion_rules.py:2323 msgid "Device" msgstr "Gerät" #: lib/solaar/ui/diversion_rules.py:524 lib/solaar/ui/diversion_rules.py:2297 msgid "Host" msgstr "" #: lib/solaar/ui/diversion_rules.py:525 lib/solaar/ui/diversion_rules.py:2339 msgid "Setting" msgstr "Einstellung" #: lib/solaar/ui/diversion_rules.py:526 lib/solaar/ui/diversion_rules.py:1477 #: lib/solaar/ui/diversion_rules.py:1526 msgid "Test" msgstr "Prüfwert/Ereignis" #: lib/solaar/ui/diversion_rules.py:527 lib/solaar/ui/diversion_rules.py:1643 msgid "Test bytes" msgstr "Prüfwert Bytes" #: lib/solaar/ui/diversion_rules.py:528 lib/solaar/ui/diversion_rules.py:1736 msgid "Mouse Gesture" msgstr "Mausgeste" #: lib/solaar/ui/diversion_rules.py:532 msgid "Action" msgstr "Aktion" #: lib/solaar/ui/diversion_rules.py:534 lib/solaar/ui/diversion_rules.py:1845 msgid "Key press" msgstr "Tastendruck" #: lib/solaar/ui/diversion_rules.py:535 lib/solaar/ui/diversion_rules.py:1897 msgid "Mouse scroll" msgstr "Mausrad-Scrollen" #: lib/solaar/ui/diversion_rules.py:536 lib/solaar/ui/diversion_rules.py:1959 msgid "Mouse click" msgstr "Mausklick" #: lib/solaar/ui/diversion_rules.py:537 msgid "Set" msgstr "Aktivieren" #: lib/solaar/ui/diversion_rules.py:538 lib/solaar/ui/diversion_rules.py:2030 msgid "Execute" msgstr "Ausführen" #: lib/solaar/ui/diversion_rules.py:539 lib/solaar/ui/diversion_rules.py:1158 msgid "Later" msgstr "Später" #: lib/solaar/ui/diversion_rules.py:568 msgid "Insert new rule" msgstr "Neue Regel hinzufügen" #: lib/solaar/ui/diversion_rules.py:588 lib/solaar/ui/diversion_rules.py:1686 #: lib/solaar/ui/diversion_rules.py:1790 lib/solaar/ui/diversion_rules.py:1989 msgid "Delete" msgstr "Löschen" #: lib/solaar/ui/diversion_rules.py:610 msgid "Negate" msgstr "Negieren" #: lib/solaar/ui/diversion_rules.py:634 msgid "Wrap with" msgstr "Einrücken unter" #: lib/solaar/ui/diversion_rules.py:656 msgid "Cut" msgstr "Ausschneiden" #: lib/solaar/ui/diversion_rules.py:671 msgid "Paste" msgstr "Einfügen" #: lib/solaar/ui/diversion_rules.py:683 msgid "Copy" msgstr "Kopieren" #: lib/solaar/ui/diversion_rules.py:1063 msgid "This editor does not support the selected rule component yet." msgstr "Diese Regelbearbeitung unterstützt die ausgewählte Regelkomponente " "noch nicht." #: lib/solaar/ui/diversion_rules.py:1138 msgid "Number of seconds to delay." msgstr "Angabe der Sekunden für die Verzögerung." #: lib/solaar/ui/diversion_rules.py:1177 msgid "Not" msgstr "Nicht" #: lib/solaar/ui/diversion_rules.py:1187 msgid "X11 active process. For use in X11 only." msgstr "Aktiver X11 Prozess. Nur zur Verwendung mit X11." #: lib/solaar/ui/diversion_rules.py:1218 msgid "X11 mouse process. For use in X11 only." msgstr "X11 Mausprozess. Nur zur Verwendung mit X11." #: lib/solaar/ui/diversion_rules.py:1235 msgid "MouseProcess" msgstr "Mausprozess" #: lib/solaar/ui/diversion_rules.py:1260 msgid "Feature name of notification triggering rule processing." msgstr "Funktionsname der Benachrichtigung, die die Regelverarbeitung " "auslöst." #: lib/solaar/ui/diversion_rules.py:1308 msgid "Report number of notification triggering rule processing." msgstr "" #: lib/solaar/ui/diversion_rules.py:1342 msgid "Active keyboard modifiers. Not always available in Wayland." msgstr "Aktive Keyboardanpassung. Unter Wayland nicht immer verfügbar." #: lib/solaar/ui/diversion_rules.py:1383 msgid "Diverted key or button depressed or released.\n" "Use the Key/Button Diversion and Divert G Keys settings to divert " "keys and buttons." msgstr "Umgeleitete Taste oder Maustaste ist gedrückt oder losgelassen.\n" "Verwende die Tasten-/Maustastenumleitungs- und G " "Tastenumleitungseinstellungen um Tasten sowie Maustasten umzuleiten." #: lib/solaar/ui/diversion_rules.py:1392 msgid "Key down" msgstr "Taste gedrückt" #: lib/solaar/ui/diversion_rules.py:1395 msgid "Key up" msgstr "Taste losgelassen" #: lib/solaar/ui/diversion_rules.py:1436 msgid "Diverted key or button is currently down.\n" "Use the Key/Button Diversion and Divert G Keys settings to divert " "keys and buttons." msgstr "Umgeleitete Taste oder Maustaste ist unten. Verwende die Tasten-/" "Maustastenumleitungs- und G Tastenumleitungseinstellungen um Tasten " "sowie Maustasten umzuleiten." #: lib/solaar/ui/diversion_rules.py:1475 msgid "Test condition on notification triggering rule processing." msgstr "" #: lib/solaar/ui/diversion_rules.py:1479 msgid "Parameter" msgstr "Parameter" #: lib/solaar/ui/diversion_rules.py:1542 msgid "begin (inclusive)" msgstr "Anfang (inklusive)" #: lib/solaar/ui/diversion_rules.py:1543 msgid "end (exclusive)" msgstr "Ende (exklusive)" #: lib/solaar/ui/diversion_rules.py:1552 msgid "range" msgstr "Bereich" #: lib/solaar/ui/diversion_rules.py:1554 msgid "minimum" msgstr "Minimum" #: lib/solaar/ui/diversion_rules.py:1555 msgid "maximum" msgstr "Maximum" #: lib/solaar/ui/diversion_rules.py:1557 #, python-format msgid "bytes %(0)d to %(1)d, ranging from %(2)d to %(3)d" msgstr "Bytes %(0)d bis %(1)d, beginnend von %(2)d bis %(3)d" #: lib/solaar/ui/diversion_rules.py:1562 msgid "mask" msgstr "Maske" #: lib/solaar/ui/diversion_rules.py:1563 #, python-format msgid "bytes %(0)d to %(1)d, mask %(2)d" msgstr "Bytes %(0)d bis %(1)d, Maske %(2)d" #: lib/solaar/ui/diversion_rules.py:1573 msgid "Bit or range test on bytes in notification message triggering rule " "processing." msgstr "" #: lib/solaar/ui/diversion_rules.py:1583 msgid "type" msgstr "Typ" #: lib/solaar/ui/diversion_rules.py:1666 msgid "Mouse gesture with optional initiating button followed by zero or " "more mouse movements." msgstr "Mausgeste mit optionaler initiierender Maustaste gefolgt von keiner " "oder mehreren Bewegungen." #: lib/solaar/ui/diversion_rules.py:1671 msgid "Add movement" msgstr "Bewegung hinzufügen" #: lib/solaar/ui/diversion_rules.py:1764 msgid "Simulate a chorded key click or depress or release.\n" "On Wayland requires write access to /dev/uinput." msgstr "Simuliert einen kombinierten Mausklick oder Tastendruck bzw. " "loslassen einer Taste.\n" "Benötigt unter Wayland Schreibzugriff auf /dev/uinput." #: lib/solaar/ui/diversion_rules.py:1769 msgid "Add key" msgstr "Weitere Taste hinzufügen" #: lib/solaar/ui/diversion_rules.py:1772 msgid "Click" msgstr "Linksklick" #: lib/solaar/ui/diversion_rules.py:1775 msgid "Depress" msgstr "Abgesenkt" #: lib/solaar/ui/diversion_rules.py:1778 msgid "Release" msgstr "Loslassen" #: lib/solaar/ui/diversion_rules.py:1861 msgid "Simulate a mouse scroll.\n" "On Wayland requires write access to /dev/uinput." msgstr "Simuliert eine Mausradbewegung. Benötigt unter Wayland " "Schreibzugriff auf /dev/uinput." #: lib/solaar/ui/diversion_rules.py:1918 msgid "Simulate a mouse click.\n" "On Wayland requires write access to /dev/uinput." msgstr "Simuliert einen Mausklick. Benötigt unter Wayland Schreibzugriff " "auf /dev/uinput." #: lib/solaar/ui/diversion_rules.py:1921 msgid "Button" msgstr "Maustaste" #: lib/solaar/ui/diversion_rules.py:1922 msgid "Count and Action" msgstr "" #: lib/solaar/ui/diversion_rules.py:1972 msgid "Execute a command with arguments." msgstr "Einen Befehl mit Parametern ausführen." #: lib/solaar/ui/diversion_rules.py:1975 msgid "Add argument" msgstr "Argument hinzufügen" #: lib/solaar/ui/diversion_rules.py:2050 msgid "Toggle" msgstr "Umschalten" #: lib/solaar/ui/diversion_rules.py:2050 msgid "True" msgstr "Wahr" #: lib/solaar/ui/diversion_rules.py:2051 msgid "False" msgstr "Falsch" #: lib/solaar/ui/diversion_rules.py:2065 msgid "Unsupported setting" msgstr "Nicht unterstützte Einstellung" #: lib/solaar/ui/diversion_rules.py:2223 lib/solaar/ui/diversion_rules.py:2242 #: lib/solaar/ui/diversion_rules.py:2328 lib/solaar/ui/diversion_rules.py:2570 #: lib/solaar/ui/diversion_rules.py:2588 msgid "Originating device" msgstr "Quellgerät" #: lib/solaar/ui/diversion_rules.py:2256 msgid "Device is active and its settings can be changed." msgstr "Das Gerät ist aktiv und die Einstellungen können geändert werden." #: lib/solaar/ui/diversion_rules.py:2266 msgid "Device that originated the current notification." msgstr "" #: lib/solaar/ui/diversion_rules.py:2280 msgid "Name of host computer." msgstr "" #: lib/solaar/ui/diversion_rules.py:2347 msgid "Value" msgstr "Wert" #: lib/solaar/ui/diversion_rules.py:2355 msgid "Item" msgstr "Item" #: lib/solaar/ui/diversion_rules.py:2630 msgid "Change setting on device" msgstr "Einstellung auf dem Gerät ändern" #: lib/solaar/ui/diversion_rules.py:2647 msgid "Setting on device" msgstr "Einstellung auf dem Gerät" #: lib/solaar/ui/notify.py:124 lib/solaar/ui/tray.py:318 #: lib/solaar/ui/tray.py:323 lib/solaar/ui/window.py:739 msgid "offline" msgstr "offline" #: lib/solaar/ui/pair_window.py:122 lib/solaar/ui/pair_window.py:256 #: lib/solaar/ui/pair_window.py:288 #, python-format msgid "%(receiver_name)s: pair new device" msgstr "%(receiver_name)s: neues Gerät koppeln" #: lib/solaar/ui/pair_window.py:123 #, python-format msgid "Enter passcode on %(name)s." msgstr "Gib das Passwort ein auf %(name)s." #: lib/solaar/ui/pair_window.py:126 #, python-format msgid "Type %(passcode)s and then press the enter key." msgstr "Gib %(passcode)s ein und drücke die ENTER Taste." #: lib/solaar/ui/pair_window.py:129 msgid "left" msgstr "links" #: lib/solaar/ui/pair_window.py:129 msgid "right" msgstr "rechts" #: lib/solaar/ui/pair_window.py:131 #, python-format msgid "Press %(code)s\n" "and then press left and right buttons simultaneously." msgstr "Drücke %(code)s\n" "und drücke dann die links und rechts Tasten gleichzeitig." #: lib/solaar/ui/pair_window.py:188 msgid "Pairing failed" msgstr "Kopplung fehlgeschlagen" #: lib/solaar/ui/pair_window.py:190 msgid "Make sure your device is within range, and has a decent battery " "charge." msgstr "Überprüfen Sie, ob sich das Gerät in Reichweite befindet und der " "Akku ausreichend geladen bzw. die Batterie voll genug ist." #: lib/solaar/ui/pair_window.py:192 msgid "A new device was detected, but it is not compatible with this " "receiver." msgstr "Es wurde ein neues Gerät festgestellt, aber es ist nicht mit diesem " "Empfänger kompatibel." #: lib/solaar/ui/pair_window.py:194 msgid "More paired devices than receiver can support." msgstr "Es sind mehr Geräte gekoppelt als der Empfänger unterstützt." #: lib/solaar/ui/pair_window.py:196 msgid "No further details are available about the error." msgstr "Es gibt keine näheren Informationen zu diesem Fehler." #: lib/solaar/ui/pair_window.py:210 msgid "Found a new device:" msgstr "Neues Gerät gefunden:" #: lib/solaar/ui/pair_window.py:235 msgid "The wireless link is not encrypted" msgstr "Die drahtlose Verbindung ist nicht verschlüsselt" #: lib/solaar/ui/pair_window.py:264 msgid "Unifying receivers are only compatible with Unifying devices." msgstr "Unifying Empfänger sind nur mit Unifying Geräten kompatibel." #: lib/solaar/ui/pair_window.py:266 msgid "Bolt receivers are only compatible with Bolt devices." msgstr "Bolt Empfänger sind nur mit Bolt Geräten kompatibel." #: lib/solaar/ui/pair_window.py:268 msgid "Other receivers are only compatible with a few devices." msgstr "Andere Empfänger sind nur mit wenigen Geräten kompatibel." #: lib/solaar/ui/pair_window.py:270 msgid "The device must not be paired with a nearby powered-on receiver." msgstr "Das Gerät darf nicht mit mit anderen, eingeschalteten Empfängern in " "der Nähe gekoppelt sein." #: lib/solaar/ui/pair_window.py:274 msgid "Press a pairing button or key until the pairing light flashes " "quickly." msgstr "Drücke die Kopplungstaste bis das Kopplungslicht schnell blinkt." #: lib/solaar/ui/pair_window.py:276 msgid "You may have to first turn the device off and on again." msgstr "Eventuell muss das Gerät dazu aus- und wieder eingeschaltet werden." #: lib/solaar/ui/pair_window.py:278 msgid "Turn on the device you want to pair." msgstr "Schalten Sie das zu koppelnde Gerät ein." #: lib/solaar/ui/pair_window.py:280 msgid "If the device is already turned on, turn it off and on again." msgstr "Falls das Gerät eingeschaltet ist, schalte es aus und wieder ein." #: lib/solaar/ui/pair_window.py:283 #, python-format msgid "\n" "\n" "This receiver has %d pairing remaining." msgid_plural "\n" "\n" "This receiver has %d pairings remaining." msgstr[0] "\n" "\n" "Dieser Empfänger kann an %d weiteres Gerät gekoppelt werden." msgstr[1] "\n" "\n" "Dieser Empfänger kann an %d weitere Geräte gekoppelt werden." #: lib/solaar/ui/pair_window.py:286 msgid "\n" "Cancelling at this point will not use up a pairing." msgstr "\n" "Ein Abbruch zu diesem Zeitpunkt wird keine Kopplung aufbrauchen." #: lib/solaar/ui/tray.py:58 msgid "No supported device found" msgstr "" #: lib/solaar/ui/tray.py:64 lib/solaar/ui/window.py:319 #, python-format msgid "About %s" msgstr "Info zu %s" #: lib/solaar/ui/tray.py:65 lib/solaar/ui/window.py:317 #, python-format msgid "Quit %s" msgstr "%s beenden" #: lib/solaar/ui/tray.py:297 lib/solaar/ui/tray.py:305 msgid "no receiver" msgstr "kein Empfänger" #: lib/solaar/ui/tray.py:321 msgid "no status" msgstr "kein Status" #: lib/solaar/ui/window.py:96 msgid "Scanning" msgstr "Scanne" #: lib/solaar/ui/window.py:129 msgid "Battery" msgstr "Akku" #: lib/solaar/ui/window.py:132 msgid "Wireless Link" msgstr "Drahtlose Verbindung" #: lib/solaar/ui/window.py:136 msgid "Lighting" msgstr "Beleuchtung" #: lib/solaar/ui/window.py:170 msgid "Show Technical Details" msgstr "Technische Details anzeigen" #: lib/solaar/ui/window.py:186 msgid "Pair new device" msgstr "Neues Gerät koppeln" #: lib/solaar/ui/window.py:205 msgid "Select a device" msgstr "Gerät auswählen" #: lib/solaar/ui/window.py:322 msgid "Rule Editor" msgstr "Regeln bearbeiten" #: lib/solaar/ui/window.py:533 msgid "Path" msgstr "Pfad" #: lib/solaar/ui/window.py:536 msgid "USB ID" msgstr "USB-ID" #: lib/solaar/ui/window.py:539 lib/solaar/ui/window.py:541 #: lib/solaar/ui/window.py:561 lib/solaar/ui/window.py:563 msgid "Serial" msgstr "Seriennummer" #: lib/solaar/ui/window.py:545 msgid "Index" msgstr "Index" #: lib/solaar/ui/window.py:547 msgid "Wireless PID" msgstr "Wireless PID" #: lib/solaar/ui/window.py:549 msgid "Product ID" msgstr "Produkt-ID" #: lib/solaar/ui/window.py:551 msgid "Protocol" msgstr "Protokoll" #: lib/solaar/ui/window.py:551 msgid "Unknown" msgstr "Unbekannt" #: lib/solaar/ui/window.py:554 #, python-format msgid "%(rate)d ms (%(rate_hz)dHz)" msgstr "%(rate)d ms (%(rate_hz)dHz)" #: lib/solaar/ui/window.py:554 msgid "Polling rate" msgstr "Abfragerate" #: lib/solaar/ui/window.py:565 msgid "Unit ID" msgstr "Geräte ID" #: lib/solaar/ui/window.py:576 msgid "none" msgstr "keine" #: lib/solaar/ui/window.py:577 msgid "Notifications" msgstr "Mitteilungen" #: lib/solaar/ui/window.py:621 msgid "No device paired." msgstr "Kein Gerät gekoppelt." #: lib/solaar/ui/window.py:628 #, python-format msgid "Up to %(max_count)s device can be paired to this receiver." msgid_plural "Up to %(max_count)s devices can be paired to this receiver." msgstr[0] "Nur %(max_count)s Gerät kann an diesen Empfänger gekoppelt " "werden." msgstr[1] "Bis zu %(max_count)s Geräte können an diesen Empfänger " "gekoppelt werden." #: lib/solaar/ui/window.py:634 msgid "Only one device can be paired to this receiver." msgstr "An diesen Empfänger kann nur ein Gerät gekoppelt werden." #: lib/solaar/ui/window.py:638 #, python-format msgid "This receiver has %d pairing remaining." msgid_plural "This receiver has %d pairings remaining." msgstr[0] "Dieser Empfänger kann an %d weiteres Gerät gekoppelt werden." msgstr[1] "Dieser Empfänger kann an %d weitere Geräte gekoppelt werden." #: lib/solaar/ui/window.py:692 msgid "Battery Voltage" msgstr "Akkuspannung" #: lib/solaar/ui/window.py:694 msgid "Voltage reported by battery" msgstr "Spannung, die vom Akku gemeldet wird" #: lib/solaar/ui/window.py:696 msgid "Battery Level" msgstr "Akkuladung" #: lib/solaar/ui/window.py:698 msgid "Approximate level reported by battery" msgstr "Ungefährer Ladezustand des Akkus, den das Gerät meldet" #: lib/solaar/ui/window.py:705 lib/solaar/ui/window.py:707 msgid "next reported " msgstr "nächste Meldung bei " #: lib/solaar/ui/window.py:708 msgid " and next level to be reported." msgstr " und der nächste, der vom Gerät mitgeteilt werden wird." #: lib/solaar/ui/window.py:713 msgid "last known" msgstr "letzte bekannte" #: lib/solaar/ui/window.py:724 msgid "encrypted" msgstr "verschlüsselt" #: lib/solaar/ui/window.py:726 msgid "The wireless link between this device and its receiver is encrypted." msgstr "Die drahtlose Verbindung zwischen diesem Gerät und dem Empfänger ist " "verschlüsselt." #: lib/solaar/ui/window.py:728 msgid "not encrypted" msgstr "nicht verschlüsselt" #: lib/solaar/ui/window.py:732 msgid "The wireless link between this device and its receiver is not " "encrypted.\n" "This is a security issue for pointing devices, and a major security " "issue for text-input devices." msgstr "Die drahtlose Verbindung zwischen diesem Gerät und dem Empfänger ist " "nicht verschlüsselt.\n" "Dies ist ein Sicherheitsproblem für Zeigegeräte, aber ein großes " "Sicherheitsrisiko für Texteingabegeräte." #: lib/solaar/ui/window.py:748 #, python-format msgid "%(light_level)d lux" msgstr "%(light_level)d Lux" #~ msgid "About" #~ msgstr "Über" #~ msgid "Add action" #~ msgstr "Aktion hinzufügen" #~ msgid "Adjust the DPI by sliding the mouse horizontally while " #~ "holding the DPI button." #~ msgstr "Die Empfindlichkeit (in DPI) durch seitliches Bewegen der " #~ "Maus ändern, während der DPI-Knopf gehalten wird." #~ msgid "Adjust the DPI by sliding the mouse horizontally while " #~ "holding the button down." #~ msgstr "Passe die DPI an durch horizontale Mausbewegung bei " #~ "gleichzeitig gedrückter Taste." #~ msgid "Battery information unknown." #~ msgstr "Akkuzustand unbekannt." #~ msgid "Click to allow changes." #~ msgstr "Anklicken, um Änderungen zuzulassen." #~ msgid "Click to prevent changes." #~ msgstr "Anklicken, um Änderungen zu verhindern." #~ msgid "Count" #~ msgstr "Klickanzahl" #~ msgid "DPI Sliding Adjustment" #~ msgstr "DPI-Anpassung durch seitliches Bewegen" #~ msgid "Device originated the current notification." #~ msgstr "Die aktuelle Be­nach­rich­ti­gung entspringt dem Gerät." #~ msgid "Diverted key or button depressed or released.\n" #~ "Use the Key/Button Diversion setting to divert keys and buttons." #~ msgstr "Umgeleitete Taste oder Maustaste gedrückt bzw. losgelassen.\n" #~ "Verwende die Einstellungen für Tasten-/Maustastenumleitung um Tasten " #~ "oder Maustasten umzuleiten." #~ msgid "Effectively turns off thumb scrolling in Linux." #~ msgstr "Schaltet das Scrollen mit dem Daumen unter Linux faktisch " #~ "aus." #~ msgid "Effectively turns off wheel scrolling in Linux." #~ msgstr "Schaltet das Scrollen mit dem Mausrad unter Linux faktisch " #~ "aus." #~ msgid "For more information see the Solaar installation directions\n" #~ "at https://pwr-solaar.github.io/Solaar/installation" #~ msgstr "Weitere Informationen erhalten Sie in den Solaar-" #~ "Installationshinweisen\n" #~ "unter https://pwr-solaar.github.io/Solaar/installation" #, python-format #~ msgid "Found a Logitech Receiver (%s), but did not have permission " #~ "to open it." #~ msgstr "Ein Logitech-Empfänger (%s) wurde gefunden, wegen fehlender " #~ "Berechtigung kann aber nicht darauf zugegriffen werden." #~ msgid "HID++ mode for horizontal scroll with the thumb wheel." #~ msgstr "HID++-Modus für horizontales Scrollen mit dem Daumenrad." #~ msgid "HID++ mode for vertical scroll with the wheel." #~ msgstr "HID++ Modus für vertikales Mausrad-Scrollen." #~ msgid "If the device is already turned on, turn if off and on again." #~ msgstr "Wenn das Gerät bereits eingeschaltet ist, schalte es aus und " #~ "wieder ein." #~ msgid "If you've just installed Solaar, try removing the receiver " #~ "and plugging it back in." #~ msgstr "Wenn Sie Solaar gerade neu installiert haben, versuchen Sie " #~ "den Empfänger aus- und wieder einzustecken." #~ msgid "No Logitech device found" #~ msgstr "Kein Logitechgerät gefunden" #~ msgid "No Logitech receiver found" #~ msgstr "Kein Logitech-Empfänger gefunden" #~ msgid "Quit" #~ msgstr "Beenden" #~ msgid "Scroll Wheel HID++ Scrolling" #~ msgstr "Mausrad-HID++-Scrollen" #~ msgid "Send a gesture by sliding the mouse while holding the button " #~ "down." #~ msgstr "Erstelle eine Geste durch Mausbewegung bei gleichzeitig " #~ "gedrückter Taste." #~ msgid "Set to ignore if unusual device behaviour is experienced" #~ msgstr "Aktivieren zum Ignorieren, falls das Gerät sich ungewöhnlich " #~ "verhält" #~ msgid "Shows status of devices connected\n" #~ "through wireless Logitech receivers." #~ msgstr "Zeigt den Status von Geräten an, die über\n" #~ "Logitech-Empfänger drahtlos verbunden sind." #~ msgid "Solaar depends on a udev file that is not present" #~ msgstr "Solaar benötigt eine udev-Datei, die nicht gefunden wurde" #~ msgid "Thumb Wheel HID++ Scrolling" #~ msgstr "Daumenrad-HID++-Scrollen" #~ msgid "Top-most coordinate." #~ msgstr "Äußerste obere Koordinate." #~ msgid "Try removing the device and plugging it back in or turning " #~ "it off and then on." #~ msgstr "Versuche das Gerät herauszuziehen und neu einzustecken bzw. " #~ "es aus und wieder an zuschalten." #~ msgid "height" #~ msgstr "Höhe" #~ msgid "top" #~ msgstr "oben" #~ msgid "unknown" #~ msgstr "unbekannt" #~ msgid "width" #~ msgstr "Breite" Solaar-1.1.14/po/el.po000066400000000000000000002242121473525213700144110ustar00rootroot00000000000000# Greek translations for solaar package. # Copyright (C) 2014 THE solaar'S COPYRIGHT HOLDER # This file is distributed under the same license as the solaar package. # Automatically generated, 2014. # Giannis Tsagatakis # Vangelis Skarmoutsos , 2017 # Athanasios Nektarios Karachalios Stagkas 2023-2024 msgid "" msgstr "" "Project-Id-Version: solaar 0.9.2\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2024-03-14 13:23+0700\n" "PO-Revision-Date: 2024-04-01 03:51+0300\n" "Last-Translator: Vangelis Skarmoutsos \n" "Language-Team: none\n" "Language: el\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Poedit 3.4.2\n" #: lib/logitech_receiver/base_usb.py:45 msgid "Bolt Receiver" msgstr "Δέκτης Bolt" #: lib/logitech_receiver/base_usb.py:58 msgid "Unifying Receiver" msgstr "Δέκτης Unifying" #: lib/logitech_receiver/base_usb.py:70 lib/logitech_receiver/base_usb.py:83 #: lib/logitech_receiver/base_usb.py:97 lib/logitech_receiver/base_usb.py:111 #: lib/logitech_receiver/base_usb.py:125 msgid "Nano Receiver" msgstr "Δέκτης Nano" #: lib/logitech_receiver/base_usb.py:138 msgid "Lightspeed Receiver" msgstr "Δέκτης Lightspeed" #: lib/logitech_receiver/base_usb.py:149 msgid "EX100 Receiver 27 Mhz" msgstr "Δέκτης EX100 27 Mhz" #: lib/logitech_receiver/common.py:604 #, python-format msgid "Battery: %(level)s (%(status)s)" msgstr "Μπαταρία: %(level)s (%(status)s)" #: lib/logitech_receiver/common.py:606 #, python-format msgid "Battery: %(percent)d%% (%(status)s)" msgstr "Μπαταρία: %(percent)d%% (%(status)s)" #: lib/logitech_receiver/hidpp20.py:926 #: lib/logitech_receiver/settings_templates.py:291 msgid "Disabled" msgstr "Απενεργοποιημένο" #: lib/logitech_receiver/hidpp20.py:927 msgid "Static" msgstr "Στατικό" #: lib/logitech_receiver/hidpp20.py:928 msgid "Pulse" msgstr "Παλμός" #: lib/logitech_receiver/hidpp20.py:929 msgid "Cycle" msgstr "Κύκλος" #: lib/logitech_receiver/hidpp20.py:930 msgid "Boot" msgstr "Εκκίνηση" #: lib/logitech_receiver/hidpp20.py:931 msgid "Demo" msgstr "Ντέμο" #: lib/logitech_receiver/hidpp20.py:932 msgid "Breathe" msgstr "Αναπνοή" #: lib/logitech_receiver/hidpp20.py:933 msgid "Ripple" msgstr "Κυματισμός" #: lib/logitech_receiver/hidpp20.py:994 msgid "Unknown Location" msgstr "Άγνωστη τοποθεσία" #: lib/logitech_receiver/hidpp20.py:995 msgid "Primary" msgstr "Πρωτεύον" #: lib/logitech_receiver/hidpp20.py:996 msgid "Logo" msgstr "Λογότυπο" #: lib/logitech_receiver/hidpp20.py:997 msgid "Left Side" msgstr "Αριστερή Πλεύση" #: lib/logitech_receiver/hidpp20.py:998 msgid "Right Side" msgstr "Δεξιά Πλεύση" #: lib/logitech_receiver/hidpp20.py:999 msgid "Combined" msgstr "Συνδυασμένο" #: lib/logitech_receiver/hidpp20.py:1000 msgid "Primary 1" msgstr "Πρωτεύον 1" #: lib/logitech_receiver/hidpp20.py:1001 msgid "Primary 2" msgstr "Πρωτεύον 2" #: lib/logitech_receiver/hidpp20.py:1002 msgid "Primary 3" msgstr "Πρωτεύον 3" #: lib/logitech_receiver/hidpp20.py:1003 msgid "Primary 4" msgstr "Πρωτεύον 4" #: lib/logitech_receiver/hidpp20.py:1004 msgid "Primary 5" msgstr "Πρωτεύον 5" #: lib/logitech_receiver/hidpp20.py:1005 msgid "Primary 6" msgstr "Πρωτεύον 6" #: lib/logitech_receiver/i18n.py:28 msgid "empty" msgstr "άδεια" #: lib/logitech_receiver/i18n.py:29 msgid "critical" msgstr "κρίσιμη" #: lib/logitech_receiver/i18n.py:30 msgid "low" msgstr "χαμηλή" #: lib/logitech_receiver/i18n.py:31 msgid "average" msgstr "μέση τιμή" #: lib/logitech_receiver/i18n.py:32 msgid "good" msgstr "καλή" #: lib/logitech_receiver/i18n.py:33 msgid "full" msgstr "γεμάτη" #: lib/logitech_receiver/i18n.py:35 msgid "discharging" msgstr "εκφορτίζεται" #: lib/logitech_receiver/i18n.py:36 msgid "recharging" msgstr "επαναφορτίζεται" #: lib/logitech_receiver/i18n.py:37 msgid "charging" msgstr "φορτίζεται" #: lib/logitech_receiver/i18n.py:38 msgid "not charging" msgstr "χωρίς φόρτιση" #: lib/logitech_receiver/i18n.py:39 msgid "almost full" msgstr "σχεδόν γεμάτη" #: lib/logitech_receiver/i18n.py:40 msgid "charged" msgstr "φορτισμένο" #: lib/logitech_receiver/i18n.py:41 msgid "slow recharge" msgstr "αργή επαναφόρτιση" #: lib/logitech_receiver/i18n.py:42 msgid "invalid battery" msgstr "μη έγκυρη μπαταρία" #: lib/logitech_receiver/i18n.py:43 msgid "thermal error" msgstr "θερμικό σφάλμα" #: lib/logitech_receiver/i18n.py:44 msgid "error" msgstr "σφάλμα" #: lib/logitech_receiver/i18n.py:45 msgid "standard" msgstr "πρότυπο" #: lib/logitech_receiver/i18n.py:46 msgid "fast" msgstr "γρήγορο" #: lib/logitech_receiver/i18n.py:47 msgid "slow" msgstr "αργό" #: lib/logitech_receiver/i18n.py:49 msgid "device timeout" msgstr "ορίου χρόνου συσκευής" #: lib/logitech_receiver/i18n.py:50 msgid "device not supported" msgstr "η συσκευή δεν υποστηρίζεται" #: lib/logitech_receiver/i18n.py:51 msgid "too many devices" msgstr "πάρα πολλές συσκευές" #: lib/logitech_receiver/i18n.py:52 msgid "sequence timeout" msgstr "ορίου χρόνου ακολουθίας" #: lib/logitech_receiver/i18n.py:54 msgid "Firmware" msgstr "Firmware" #: lib/logitech_receiver/i18n.py:55 msgid "Bootloader" msgstr "Bootloader" #: lib/logitech_receiver/i18n.py:56 msgid "Hardware" msgstr "Υλισμικό" #: lib/logitech_receiver/i18n.py:57 msgid "Other" msgstr "Άλλο" #: lib/logitech_receiver/i18n.py:59 msgid "Left Button" msgstr "Αριστερό κουμπί" #: lib/logitech_receiver/i18n.py:60 msgid "Right Button" msgstr "Δεξί κουμπί" #: lib/logitech_receiver/i18n.py:61 msgid "Middle Button" msgstr "Κεντρικό κουμπί" #: lib/logitech_receiver/i18n.py:62 msgid "Back Button" msgstr "Πίσω κουμπί" #: lib/logitech_receiver/i18n.py:63 msgid "Forward Button" msgstr "Εμπρός Κουμπί" #: lib/logitech_receiver/i18n.py:64 msgid "Mouse Gesture Button" msgstr "Κουμπί χειρονομιών ποντικιού" #: lib/logitech_receiver/i18n.py:65 msgid "Smart Shift" msgstr "Έξυπνος Διακόπτης" #: lib/logitech_receiver/i18n.py:66 msgid "DPI Switch" msgstr "Διακόπτης DPI" #: lib/logitech_receiver/i18n.py:67 msgid "Left Tilt" msgstr "Αριστερή κλίση" #: lib/logitech_receiver/i18n.py:68 msgid "Right Tilt" msgstr "Δεξιά κλίση" #: lib/logitech_receiver/i18n.py:69 msgid "Left Click" msgstr "Αριστερό κλικ" #: lib/logitech_receiver/i18n.py:70 msgid "Right Click" msgstr "Δεξί κλικ" #: lib/logitech_receiver/i18n.py:71 msgid "Mouse Middle Button" msgstr "Μεσαίο κουμπί ποντικιού" #: lib/logitech_receiver/i18n.py:72 msgid "Mouse Back Button" msgstr "Πίσω κουμπί ποντικιού" #: lib/logitech_receiver/i18n.py:73 msgid "Mouse Forward Button" msgstr "Εμπρός κουμπί ποντικιού" #: lib/logitech_receiver/i18n.py:74 msgid "Gesture Button Navigation" msgstr "Πλοήγηση με κουμπιά χειρονομιών" #: lib/logitech_receiver/i18n.py:75 msgid "Mouse Scroll Left Button" msgstr "Κύλιση ποντικιού Αριστερό κουμπί" #: lib/logitech_receiver/i18n.py:76 msgid "Mouse Scroll Right Button" msgstr "Κύλιση ποντικιού Δεξί κουμπί" #: lib/logitech_receiver/i18n.py:78 msgid "pressed" msgstr "πατημένο" #: lib/logitech_receiver/i18n.py:79 msgid "released" msgstr "αφημένο" #: lib/logitech_receiver/notifications.py:63 #: lib/logitech_receiver/notifications.py:115 msgid "pairing lock is closed" msgstr "η δικλείδα σύζευξης είναι κλειστή" #: lib/logitech_receiver/notifications.py:63 #: lib/logitech_receiver/notifications.py:115 msgid "pairing lock is open" msgstr "η δικλείδα σύζευξης είναι ανοιχτή" #: lib/logitech_receiver/notifications.py:80 msgid "discovery lock is closed" msgstr "η δικλείδα ανακάλυψης είναι κλειστή" #: lib/logitech_receiver/notifications.py:80 msgid "discovery lock is open" msgstr "η δικλείδα ανακάλυψης είναι ανοιχτή" #: lib/logitech_receiver/notifications.py:207 msgid "connected" msgstr "συνδεμένη" #: lib/logitech_receiver/notifications.py:207 msgid "disconnected" msgstr "αποσυνδεδεμένη" #: lib/logitech_receiver/notifications.py:233 msgid "unpaired" msgstr "διαχωρισμένη" #: lib/logitech_receiver/notifications.py:280 msgid "powered on" msgstr "ανοιχτή" #: lib/logitech_receiver/receiver.py:345 msgid "No paired devices." msgstr "Δεν έχω συνταιριασμένες συσκευές." #: lib/logitech_receiver/receiver.py:347 lib/solaar/ui/window.py:630 #, python-format msgid "%(count)s paired device." msgid_plural "%(count)s paired devices." msgstr[0] "%(count)s συζευγμένη συσκευή." msgstr[1] "%(count)s συζευγμένες συσκευές." #: lib/logitech_receiver/settings.py:617 msgid "register" msgstr "καταχώρηση" #: lib/logitech_receiver/settings.py:631 lib/logitech_receiver/settings.py:658 msgid "feature" msgstr "χαρακτηριστικό" #: lib/logitech_receiver/settings_templates.py:144 msgid "Swap Fx function" msgstr "Αντιστροφή λειτουργιών Fx" #: lib/logitech_receiver/settings_templates.py:147 msgid "" "When set, the F1..F12 keys will activate their special function,\n" "and you must hold the FN key to activate their standard function." msgstr "" "Όταν είναι ορισμένο, τα πλήκτρα F1..F12 θα ενεργοποιήσουν τις ειδικές τους " "λειτουργίες,\n" "και πρέπει να κρατήσετε το πλήκτρο FN για να ενεργοποιήσετε την τυπική " "λειτουργία τους." #: lib/logitech_receiver/settings_templates.py:152 msgid "" "When unset, the F1..F12 keys will activate their standard function,\n" "and you must hold the FN key to activate their special function." msgstr "" "Όταν δεν είναι ορισμένο, τα πλήκτρα F1..F12 θα ενεργοποιήσουν την τυπική " "λειτουργία τους,\n" "και πρέπει να κρατήσετε το πλήκτρο FN για να ενεργοποιήσετε την ειδική " "λειτουργία τους." #: lib/logitech_receiver/settings_templates.py:160 msgid "Hand Detection" msgstr "Ανίχνευση χεριού" #: lib/logitech_receiver/settings_templates.py:161 msgid "Turn on illumination when the hands hover over the keyboard." msgstr "Άνοιγμα φωτισμού όταν τα χέρια βρεθούν πάνω από το πληκτρολόγιο." #: lib/logitech_receiver/settings_templates.py:168 msgid "Scroll Wheel Smooth Scrolling" msgstr "Ομαλή κύλιση του τροχού κύλισης" #: lib/logitech_receiver/settings_templates.py:169 #: lib/logitech_receiver/settings_templates.py:402 #: lib/logitech_receiver/settings_templates.py:431 msgid "High-sensitivity mode for vertical scroll with the wheel." msgstr "Κατάσταση υψηλής ευαισθησίας για κατακόρυφη ολίσθηση με τον τροχό." #: lib/logitech_receiver/settings_templates.py:176 msgid "Side Scrolling" msgstr "Πλάγια κύλιση" #: lib/logitech_receiver/settings_templates.py:178 msgid "" "When disabled, pushing the wheel sideways sends custom button events\n" "instead of the standard side-scrolling events." msgstr "" "Όταν είναι απενεργοποιημένο, πιέζοντας τον τροχό στο πλάι στέλνει " "προσαρμοσμένα γεγονότα κουμπιού\n" "αντί των τυπικών γεγονότων πλάγιας ολίσθησης." #: lib/logitech_receiver/settings_templates.py:188 msgid "Sensitivity (DPI - older mice)" msgstr "Ευαισθησία (DPI - παλαιά ποντίκια)" #: lib/logitech_receiver/settings_templates.py:189 #: lib/logitech_receiver/settings_templates.py:958 msgid "Mouse movement sensitivity" msgstr "Ευαισθησία κίνησης ποντικιού" #: lib/logitech_receiver/settings_templates.py:249 #: lib/logitech_receiver/settings_templates.py:261 #: lib/logitech_receiver/settings_templates.py:388 msgid "Backlight" msgstr "Οπίσθιος φωτισμός" #: lib/logitech_receiver/settings_templates.py:250 #: lib/logitech_receiver/settings_templates.py:389 msgid "Set illumination time for keyboard." msgstr "Ρύθμιση χρόνου φωτισμού για το πληκτρολόγιο." #: lib/logitech_receiver/settings_templates.py:262 msgid "" "Illumination level on keyboard. Changes made are only applied in Manual " "mode." msgstr "" "Επίπεδο φωτισμού στο πληκτρολόγιο. Οι αλλαγές που πραγματοποιούνται " "εφαρμόζονται μόνο στη χειροκίνητη λειτουργία." #: lib/logitech_receiver/settings_templates.py:293 msgid "Automatic" msgstr "Αυτόματο" #: lib/logitech_receiver/settings_templates.py:295 msgid "Manual" msgstr "Χειροκίνητο" #: lib/logitech_receiver/settings_templates.py:297 msgid "Enabled" msgstr "Ενεργοποιημένο" #: lib/logitech_receiver/settings_templates.py:303 msgid "Backlight Level" msgstr "Επίπεδο οπίσθιου φωτισμού" #: lib/logitech_receiver/settings_templates.py:304 msgid "Illumination level on keyboard when in Manual mode." msgstr "" "Επίπεδο φωτισμού στο πληκτρολόγιο όταν βρίσκεται σε χειροκίνητη λειτουργία." #: lib/logitech_receiver/settings_templates.py:361 msgid "Backlight Delay Hands Out" msgstr "Καθυστέρηση οπίσθιου φωτισμού Χέρια Εκτός" #: lib/logitech_receiver/settings_templates.py:362 msgid "" "Delay in seconds until backlight fades out with hands away from keyboard." msgstr "" "Καθυστέρηση σε δευτερόλεπτα μέχρι να σβήσει ο οπίσθιος φωτισμός με τα χέρια " "μακριά από το πληκτρολόγιο." #: lib/logitech_receiver/settings_templates.py:370 msgid "Backlight Delay Hands In" msgstr "Καθυστέρηση οπίσθιου φωτισμού Χέρια Eντός" #: lib/logitech_receiver/settings_templates.py:371 msgid "Delay in seconds until backlight fades out with hands near keyboard." msgstr "" "Καθυστέρηση σε δευτερόλεπτα μέχρι να σβήσει ο οπίσθιος φωτισμός με τα χέρια " "κοντά στο πληκτρολόγιο." #: lib/logitech_receiver/settings_templates.py:379 msgid "Backlight Delay Powered" msgstr "Καθυστέρηση οπίσθιου φωτισμού Με τροφοδοσία" #: lib/logitech_receiver/settings_templates.py:380 msgid "Delay in seconds until backlight fades out with external power." msgstr "" "Καθυστέρηση σε δευτερόλεπτα μέχρι να σβήσει ο οπίσθιος φωτισμός με εξωτερική " "τροφοδοσία." #: lib/logitech_receiver/settings_templates.py:400 msgid "Scroll Wheel High Resolution" msgstr "Τροχός κύλισης υψηλής ανάλυσης" #: lib/logitech_receiver/settings_templates.py:404 #: lib/logitech_receiver/settings_templates.py:433 msgid "Set to ignore if scrolling is abnormally fast or slow" msgstr "Ρύθμιση για να αγνοείται εάν η κύλιση είναι ασυνήθιστα γρήγορη ή αργή" #: lib/logitech_receiver/settings_templates.py:411 #: lib/logitech_receiver/settings_templates.py:442 msgid "Scroll Wheel Diversion" msgstr "Εκτροπή τροχού κύλισης" #: lib/logitech_receiver/settings_templates.py:413 msgid "" "Make scroll wheel send LOWRES_WHEEL HID++ notifications (which trigger " "Solaar rules but are otherwise ignored)." msgstr "" "Κάντε τον τροχό κύλισης να στέλνει ειδοποιήσεις LOWRES_WHEEL HID++ (οι " "οποίες ενεργοποιούν τους κανόνες Solaar αλλά αγνοούνται κατά τα άλλα)." #: lib/logitech_receiver/settings_templates.py:420 msgid "Scroll Wheel Direction" msgstr "Κατεύθυνση τροχού κύλισης" #: lib/logitech_receiver/settings_templates.py:421 msgid "Invert direction for vertical scroll with wheel." msgstr "Αντιστροφή κατεύθυνσης για κάθετη κύλιση με τον τροχό." #: lib/logitech_receiver/settings_templates.py:429 msgid "Scroll Wheel Resolution" msgstr "Ανάλυση τροχού κύλισης" #: lib/logitech_receiver/settings_templates.py:444 msgid "" "Make scroll wheel send HIRES_WHEEL HID++ notifications (which trigger Solaar " "rules but are otherwise ignored)." msgstr "" "Κάντε τον τροχό κύλισης να στέλνει ειδοποιήσεις HIRES_WHEEL HID++ (οι οποίες " "ενεργοποιούν τους κανόνες Solaar αλλά αγνοούνται διαφορετικά)." #: lib/logitech_receiver/settings_templates.py:453 msgid "Sensitivity (Pointer Speed)" msgstr "Ευαισθησία (ταχύτητα δείκτη)" #: lib/logitech_receiver/settings_templates.py:454 msgid "Speed multiplier for mouse (256 is normal multiplier)." msgstr "" "Πολλαπλασιαστής ταχύτητας για το ποντίκι (256 είναι ο κανονικός " "πολλαπλασιαστής)." #: lib/logitech_receiver/settings_templates.py:464 msgid "Thumb Wheel Diversion" msgstr "Εκτροπή τροχού αντίχειρα" #: lib/logitech_receiver/settings_templates.py:466 msgid "" "Make thumb wheel send THUMB_WHEEL HID++ notifications (which trigger Solaar " "rules but are otherwise ignored)." msgstr "" "Κάντε τον τροχό αντίχειρα να στέλνει ειδοποιήσεις THUMB_WHEEL HID++ (οι " "οποίες ενεργοποιούν τους κανόνες του Solaar αλλά αγνοούνται διαφορετικά)." #: lib/logitech_receiver/settings_templates.py:475 msgid "Thumb Wheel Direction" msgstr "Κατεύθυνση τροχού αντίχειρα" #: lib/logitech_receiver/settings_templates.py:476 msgid "Invert thumb wheel scroll direction." msgstr "Αντιστροφή της κατεύθυνσης κύλισης του τροχού αντίχειρα." #: lib/logitech_receiver/settings_templates.py:496 msgid "Onboard Profiles" msgstr "Ενσωματωμένα Προφίλ" #: lib/logitech_receiver/settings_templates.py:497 msgid "" "Enable an onboard profile, which controls report rate, sensitivity, and " "button actions" msgstr "" "Ενεργοποιήστε ένα ενσωματωμένο προφίλ, το οποίο ελέγχει το ρυθμό αναφοράς, " "την ευαισθησία και τις ενέργειες των κουμπιών" #: lib/logitech_receiver/settings_templates.py:541 #: lib/logitech_receiver/settings_templates.py:581 msgid "Report Rate" msgstr "Ρυθμός αναφοράς" #: lib/logitech_receiver/settings_templates.py:543 #: lib/logitech_receiver/settings_templates.py:583 msgid "Frequency of device movement reports" msgstr "Συχνότητα των αναφορών κίνησης της συσκευής" #: lib/logitech_receiver/settings_templates.py:543 #: lib/logitech_receiver/settings_templates.py:583 #: lib/logitech_receiver/settings_templates.py:1295 #: lib/logitech_receiver/settings_templates.py:1326 msgid "May need Onboard Profiles set to Disable to be effective." msgstr "" "Ενδέχεται να χρειαστεί να θέσετε τα Προφίλ Ενσωματωμένου σε Απενεργοποίηση " "για να είναι αποτελεσματικά." #: lib/logitech_receiver/settings_templates.py:623 msgid "Divert crown events" msgstr "Εκτροπή εκδηλώσεων crown" #: lib/logitech_receiver/settings_templates.py:624 msgid "" "Make crown send CROWN HID++ notifications (which trigger Solaar rules but " "are otherwise ignored)." msgstr "" "Κάντε το crown να στέλνει ειδοποιήσεις CROWN HID++ (οι οποίες ενεργοποιούν " "τους κανόνες Solaar αλλά αγνοούνται κατά τα άλλα)." #: lib/logitech_receiver/settings_templates.py:632 msgid "Crown smooth scroll" msgstr "Ομαλή κύλιση Crown" #: lib/logitech_receiver/settings_templates.py:633 msgid "Set crown smooth scroll" msgstr "Ρύθμιση ομαλής κύλισης Crown" #: lib/logitech_receiver/settings_templates.py:641 msgid "Divert G and M Keys" msgstr "Εκτροπή πλήκτρων G και M" #: lib/logitech_receiver/settings_templates.py:642 msgid "" "Make G and M keys send HID++ notifications (which trigger Solaar rules but " "are otherwise ignored)." msgstr "" "Κάντε τα πλήκτρα G και M να στέλνουν ειδοποιήσεις HID++ (οι οποίες " "ενεργοποιούν τους κανόνες Solaar αλλά αγνοούνται κατά τα άλλα)." #: lib/logitech_receiver/settings_templates.py:656 msgid "Scroll Wheel Ratcheted" msgstr "Τροχός κύλισης Αγκιστρωμένος" #: lib/logitech_receiver/settings_templates.py:657 msgid "" "Switch the mouse wheel between speed-controlled ratcheting and always " "freespin." msgstr "" "Αλλάξτε τον τροχό του ποντικιού μεταξύ ρυθμιζόμενης ταχύτητας Αγκιστρωμένου " "και πάντα ελεύθερης περιστροφής." #: lib/logitech_receiver/settings_templates.py:659 msgid "Freespinning" msgstr "Ελεύθερη περιστροφή" #: lib/logitech_receiver/settings_templates.py:659 msgid "Ratcheted" msgstr "Αγκιστρωμένη" #: lib/logitech_receiver/settings_templates.py:666 msgid "Scroll Wheel Ratchet Speed" msgstr "Ταχύτητα Αγκιστρωμένου Τροχού Κύλισης" #: lib/logitech_receiver/settings_templates.py:668 msgid "" "Use the mouse wheel speed to switch between ratcheted and freespinning.\n" "The mouse wheel is always ratcheted at 50." msgstr "" "Χρησιμοποιήστε την ταχύτητα του τροχού του ποντικιού για να εναλλάσσεστε " "ανάμεσα σε Αγκιστρωμένου και ελεύθερη περιστροφή.\n" "Ο τροχός του ποντικιού είναι πάντα ρυθμισμένος στο 50." #: lib/logitech_receiver/settings_templates.py:717 msgid "Key/Button Actions" msgstr "Ενέργειες πλήκτρων/κουμπιών" #: lib/logitech_receiver/settings_templates.py:719 msgid "Change the action for the key or button." msgstr "Αλλαγή της ενέργειας για το πλήκτρο ή το κουμπί." #: lib/logitech_receiver/settings_templates.py:721 msgid "Overridden by diversion." msgstr "Παρακάμπτεται από εκτροπή." #: lib/logitech_receiver/settings_templates.py:723 msgid "" "Changing important actions (such as for the left mouse button) can result in " "an unusable system." msgstr "" "Η αλλαγή σημαντικών ενεργειών (όπως για το αριστερό κουμπί του ποντικιού) " "μπορεί να οδηγήσει σε ένα σύστημα που δεν μπορεί να χρησιμοποιηθεί." #: lib/logitech_receiver/settings_templates.py:886 msgid "Key/Button Diversion" msgstr "Εκτροπή πλήκτρων/κουμπιών" #: lib/logitech_receiver/settings_templates.py:887 msgid "" "Make the key or button send HID++ notifications (Diverted) or initiate Mouse " "Gestures or Sliding DPI" msgstr "" "Κάντε το πλήκτρο ή το κουμπί να στέλνει ειδοποιήσεις HID++ (εκτροπή) ή να " "ξεκινάει χειρονομίες ποντικιού ή συρόμενο DPI" #: lib/logitech_receiver/settings_templates.py:890 #: lib/logitech_receiver/settings_templates.py:891 #: lib/logitech_receiver/settings_templates.py:892 msgid "Diverted" msgstr "Εκτρεπόμενο" #: lib/logitech_receiver/settings_templates.py:890 #: lib/logitech_receiver/settings_templates.py:891 msgid "Mouse Gestures" msgstr "Χειρονομίες ποντικιού" #: lib/logitech_receiver/settings_templates.py:890 #: lib/logitech_receiver/settings_templates.py:891 #: lib/logitech_receiver/settings_templates.py:892 msgid "Regular" msgstr "Κανονικό" #: lib/logitech_receiver/settings_templates.py:890 msgid "Sliding DPI" msgstr "Ολίσθαινόμενο DPI" #: lib/logitech_receiver/settings_templates.py:957 msgid "Sensitivity (DPI)" msgstr "Ευαισθησία (DPI)" #: lib/logitech_receiver/settings_templates.py:997 msgid "Sensitivity Switching" msgstr "Εναλλαγή ευαισθησίας" #: lib/logitech_receiver/settings_templates.py:999 msgid "" "Switch the current sensitivity and the remembered sensitivity when the key " "or button is pressed.\n" "If there is no remembered sensitivity, just remember the current sensitivity" msgstr "" "Εναλλαγή της τρέχουσας ευαισθησίας και της ευαισθησίας που έχει " "απομνημονευθεί όταν πατηθεί το πλήκτρο ή το κουμπί.\n" "Εάν δεν υπάρχει απομνημονευμένη ευαισθησία, απλά θυμηθείτε την τρέχουσα " "ευαισθησία" #: lib/logitech_receiver/settings_templates.py:1003 msgid "Off" msgstr "Απενεργοποίηση" #: lib/logitech_receiver/settings_templates.py:1034 msgid "Disable keys" msgstr "Απενεργοποίηση πλήκτρων" #: lib/logitech_receiver/settings_templates.py:1035 msgid "Disable specific keyboard keys." msgstr "Απενεργοποίηση συγκεκριμένων πλήκτρων πληκτρολογίου." #: lib/logitech_receiver/settings_templates.py:1038 #, python-format msgid "Disables the %s key." msgstr "Απενεργοποίηση του πλήκτρου %s." #: lib/logitech_receiver/settings_templates.py:1051 #: lib/logitech_receiver/settings_templates.py:1108 msgid "Set OS" msgstr "Ορισμός λειτουργικού συστήματος" #: lib/logitech_receiver/settings_templates.py:1052 #: lib/logitech_receiver/settings_templates.py:1109 msgid "Change keys to match OS." msgstr "Αλλάξτε τα πλήκτρα για να ταιριάζουν με το λειτουργικό σύστημα." #: lib/logitech_receiver/settings_templates.py:1121 msgid "Change Host" msgstr "Αλλαγή κεντρικού υπολογιστή" #: lib/logitech_receiver/settings_templates.py:1122 msgid "Switch connection to a different host" msgstr "Αλλαγή σύνδεσης σε διαφορετικό κεντρικό υπολογιστή" #: lib/logitech_receiver/settings_templates.py:1146 msgid "Performs a left click." msgstr "Πραγματοποιεί αριστερό κλικ." #: lib/logitech_receiver/settings_templates.py:1146 msgid "Single tap" msgstr "Απλό πάτημα" #: lib/logitech_receiver/settings_templates.py:1147 msgid "Performs a right click." msgstr "Πραγματοποιεί δεξί κλικ." #: lib/logitech_receiver/settings_templates.py:1147 msgid "Single tap with two fingers" msgstr "Απλό πάτημα με δύο δάχτυλα" #: lib/logitech_receiver/settings_templates.py:1148 msgid "Single tap with three fingers" msgstr "Απλό πάτημα με τρία δάχτυλα" #: lib/logitech_receiver/settings_templates.py:1152 msgid "Double tap" msgstr "Διπλό πάτημα" #: lib/logitech_receiver/settings_templates.py:1152 msgid "Performs a double click." msgstr "Πραγματοποιεί διπλό κλικ." #: lib/logitech_receiver/settings_templates.py:1153 msgid "Double tap with two fingers" msgstr "Διπλό πάτημα με δύο δάχτυλα" #: lib/logitech_receiver/settings_templates.py:1154 msgid "Double tap with three fingers" msgstr "Διπλό πάτημα με τρία δάχτυλα" #: lib/logitech_receiver/settings_templates.py:1157 msgid "Drags items by dragging the finger after double tapping." msgstr "Σύρετε αντικείμενα σύροντας το δάχτυλο μετά από διπλό πάτημα." #: lib/logitech_receiver/settings_templates.py:1157 msgid "Tap and drag" msgstr "Πάτημα και σύρσιμο" #: lib/logitech_receiver/settings_templates.py:1159 msgid "Tap and drag with two fingers" msgstr "Πάτημα και σύρσιμο με δύο δάχτυλα" #: lib/logitech_receiver/settings_templates.py:1160 msgid "Drags items by dragging the fingers after double tapping." msgstr "Σύρετε αντικείμενα σύροντας τα δάχτυλα μετά από διπλό πάτημα." #: lib/logitech_receiver/settings_templates.py:1162 msgid "Tap and drag with three fingers" msgstr "Πάτημα και σύρσιμο με τρία δάχτυλα" #: lib/logitech_receiver/settings_templates.py:1165 msgid "Suppress tap and edge gestures" msgstr "Καταστολή χειρονομιών tap και edge" #: lib/logitech_receiver/settings_templates.py:1166 msgid "Disables tap and edge gestures (equivalent to pressing Fn+LeftClick)." msgstr "" "Απενεργοποιεί τις χειρονομίες tap και edge (ισοδύναμο με το πάτημα των " "πλήκτρων Fn+LeftClick)." #: lib/logitech_receiver/settings_templates.py:1168 msgid "Scroll with one finger" msgstr "Κύλιση με ένα δάχτυλο" #: lib/logitech_receiver/settings_templates.py:1168 #: lib/logitech_receiver/settings_templates.py:1169 #: lib/logitech_receiver/settings_templates.py:1172 msgid "Scrolls." msgstr "Κύλιση." #: lib/logitech_receiver/settings_templates.py:1169 #: lib/logitech_receiver/settings_templates.py:1172 msgid "Scroll with two fingers" msgstr "Κύλιση με δύο δάχτυλα" #: lib/logitech_receiver/settings_templates.py:1170 msgid "Scroll horizontally with two fingers" msgstr "Οριζόντια κύλιση με δύο δάχτυλα" #: lib/logitech_receiver/settings_templates.py:1170 msgid "Scrolls horizontally." msgstr "Κύλιση οριζόντια." #: lib/logitech_receiver/settings_templates.py:1171 msgid "Scroll vertically with two fingers" msgstr "Κατακόρυφη κύλιση με δύο δάχτυλα" #: lib/logitech_receiver/settings_templates.py:1171 msgid "Scrolls vertically." msgstr "Κατακόρυφη Κύλιση." #: lib/logitech_receiver/settings_templates.py:1173 msgid "Inverts the scrolling direction." msgstr "Αναστροφή της κατεύθυνσης κύλισης." #: lib/logitech_receiver/settings_templates.py:1173 msgid "Natural scrolling" msgstr "Φυσική κύλιση" #: lib/logitech_receiver/settings_templates.py:1174 msgid "Enables the thumbwheel." msgstr "Ενεργοποίηση του τροχού αντίχειρα." #: lib/logitech_receiver/settings_templates.py:1174 msgid "Thumbwheel" msgstr "Τροχός Αντίχειρα" #: lib/logitech_receiver/settings_templates.py:1185 #: lib/logitech_receiver/settings_templates.py:1189 msgid "Swipe from the top edge" msgstr "Σύρσιμο από την επάνω άκρη" #: lib/logitech_receiver/settings_templates.py:1186 msgid "Swipe from the left edge" msgstr "Σύρσιμο από την αριστερή άκρη" #: lib/logitech_receiver/settings_templates.py:1187 msgid "Swipe from the right edge" msgstr "Σύρσιμο από τη δεξιά άκρη" #: lib/logitech_receiver/settings_templates.py:1188 msgid "Swipe from the bottom edge" msgstr "Σύρσιμο από την κάτω άκρη" #: lib/logitech_receiver/settings_templates.py:1190 msgid "Swipe two fingers from the left edge" msgstr "Σύρσιμο δύο δάχτυλων από την αριστερή άκρη" #: lib/logitech_receiver/settings_templates.py:1191 msgid "Swipe two fingers from the right edge" msgstr "Σύρσιμο δύο δάχτυλων από τη δεξιά άκρη" #: lib/logitech_receiver/settings_templates.py:1192 msgid "Swipe two fingers from the bottom edge" msgstr "Σύρσιμο δύο δάχτυλων από την κάτω άκρη" #: lib/logitech_receiver/settings_templates.py:1193 msgid "Swipe two fingers from the top edge" msgstr "Σύρσιμο δύο δάχτυλων από την επάνω άκρη" #: lib/logitech_receiver/settings_templates.py:1194 #: lib/logitech_receiver/settings_templates.py:1198 msgid "Pinch to zoom out; spread to zoom in." msgstr "Τσιμπήστε για σμίκρυνση, απλώστε για μεγέθυνση." #: lib/logitech_receiver/settings_templates.py:1194 msgid "Zoom with two fingers." msgstr "Ζουμ με δύο δάχτυλα." #: lib/logitech_receiver/settings_templates.py:1195 msgid "Pinch to zoom out." msgstr "Τσιμπήστε για σμίκρυνση." #: lib/logitech_receiver/settings_templates.py:1196 msgid "Spread to zoom in." msgstr "Απλώστε για μεγέθυνση." #: lib/logitech_receiver/settings_templates.py:1197 msgid "Zoom with three fingers." msgstr "Ζουμ με τρία δάχτυλα." #: lib/logitech_receiver/settings_templates.py:1198 msgid "Zoom with two fingers" msgstr "Ζουμ με δύο δάχτυλα" #: lib/logitech_receiver/settings_templates.py:1216 msgid "Pixel zone" msgstr "Ζώνη pixel" #: lib/logitech_receiver/settings_templates.py:1217 msgid "Ratio zone" msgstr "Ζώνη αναλογίας" #: lib/logitech_receiver/settings_templates.py:1218 msgid "Scale factor" msgstr "Συντελεστής κλίμακας" #: lib/logitech_receiver/settings_templates.py:1218 msgid "Sets the cursor speed." msgstr "Ορίζει την ταχύτητα του δρομέα." #: lib/logitech_receiver/settings_templates.py:1222 msgid "Left" msgstr "Αριστερά" #: lib/logitech_receiver/settings_templates.py:1222 msgid "Left-most coordinate." msgstr "Αριστερότερη συντεταγμένη." #: lib/logitech_receiver/settings_templates.py:1223 msgid "Bottom" msgstr "Κάτω" #: lib/logitech_receiver/settings_templates.py:1223 msgid "Bottom coordinate." msgstr "Κάτωθεν συντεταγμένη." #: lib/logitech_receiver/settings_templates.py:1224 msgid "Width" msgstr "Πλάτος" #: lib/logitech_receiver/settings_templates.py:1224 msgid "Width." msgstr "Πλάτος." #: lib/logitech_receiver/settings_templates.py:1225 msgid "Height" msgstr "Ύψος" #: lib/logitech_receiver/settings_templates.py:1225 msgid "Height." msgstr "Ύψος." #: lib/logitech_receiver/settings_templates.py:1226 msgid "Cursor speed." msgstr "Ταχύτητα δρομέα." #: lib/logitech_receiver/settings_templates.py:1226 msgid "Scale" msgstr "Κλίμακα" #: lib/logitech_receiver/settings_templates.py:1232 msgid "Gestures" msgstr "Χειρονομίες" #: lib/logitech_receiver/settings_templates.py:1233 msgid "Tweak the mouse/touchpad behaviour." msgstr "Ρύθμιση της συμπεριφοράς του ποντικιού/του touchpad." #: lib/logitech_receiver/settings_templates.py:1249 msgid "Gestures Diversion" msgstr "Χειρονομίες εκτροπής" #: lib/logitech_receiver/settings_templates.py:1250 msgid "Divert mouse/touchpad gestures." msgstr "Εκτροπή χειρονομιών του ποντικιού/του touchpad." #: lib/logitech_receiver/settings_templates.py:1266 msgid "Gesture params" msgstr "Παράμετροι χειρονομιών" #: lib/logitech_receiver/settings_templates.py:1267 msgid "Change numerical parameters of a mouse/touchpad." msgstr "Αλλαγή των αριθμητικών παραμέτρων ενός ποντικιού/του touchpad." #: lib/logitech_receiver/settings_templates.py:1291 msgid "M-Key LEDs" msgstr "Λυχνίες LED M-Key" #: lib/logitech_receiver/settings_templates.py:1293 msgid "Control the M-Key LEDs." msgstr "Έλεγχος των λυχνιών LED του M-Key." #: lib/logitech_receiver/settings_templates.py:1297 #: lib/logitech_receiver/settings_templates.py:1328 msgid "May need G Keys diverted to be effective." msgstr "" "Μπορεί να χρειαστεί εκτροπή των πλήκτρων G για να είναι αποτελεσματική." #: lib/logitech_receiver/settings_templates.py:1303 #, python-format msgid "Lights up the %s key." msgstr "Ανάβει το πλήκτρο %s." #: lib/logitech_receiver/settings_templates.py:1322 msgid "MR-Key LED" msgstr "MR-Πλήκτρο LED" #: lib/logitech_receiver/settings_templates.py:1324 msgid "Control the MR-Key LED." msgstr "Έλεγχος της λυχνίας LED πλήκτρου MR." #: lib/logitech_receiver/settings_templates.py:1345 msgid "Persistent Key/Button Mapping" msgstr "Μόνιμη αντιστοίχιση πλήκτρων/κουμπιών" #: lib/logitech_receiver/settings_templates.py:1347 msgid "Permanently change the mapping for the key or button." msgstr "Μόνιμη αλλαγή της αντιστοίχισης για το πλήκτρο ή το κουμπί." #: lib/logitech_receiver/settings_templates.py:1349 msgid "" "Changing important keys or buttons (such as for the left mouse button) can " "result in an unusable system." msgstr "" "Η αλλαγή σημαντικών πλήκτρων ή κουμπιών (όπως για παράδειγμα του αριστερού " "κουμπιού του ποντικιού) μπορεί να οδηγήσει σε ένα άχρηστο σύστημα." #: lib/logitech_receiver/settings_templates.py:1406 msgid "Sidetone" msgstr "Sidetone" #: lib/logitech_receiver/settings_templates.py:1407 msgid "Set sidetone level." msgstr "Ορισμός επιπέδου sidetone." #: lib/logitech_receiver/settings_templates.py:1416 msgid "Equalizer" msgstr "Ισοσταθμιστής" #: lib/logitech_receiver/settings_templates.py:1417 msgid "Set equalizer levels." msgstr "Ρύθμιση επιπέδων ισοσταθμιστή." #: lib/logitech_receiver/settings_templates.py:1439 msgid "Hz" msgstr "Hz" #: lib/logitech_receiver/settings_templates.py:1445 msgid "Power Management" msgstr "Διαχείριση ενέργειας" #: lib/logitech_receiver/settings_templates.py:1446 msgid "Power off in minutes (0 for never)." msgstr "Απενεργοποίηση σε λεπτά (0 για ποτέ)." #: lib/logitech_receiver/settings_templates.py:1457 msgid "LED Control" msgstr "Έλεγχος LED" #: lib/logitech_receiver/settings_templates.py:1458 msgid "Switch control of LEDs between device and Solaar" msgstr "Εναλλαγή ελέγχου των LED μεταξύ συσκευής και Solaar" #: lib/logitech_receiver/settings_templates.py:1473 msgid "LED Zone Effects" msgstr "Εφέ ζώνης LED" #: lib/logitech_receiver/settings_templates.py:1474 msgid "Set effect for LED Zone" msgstr "Ρύθμιση εφέ για τη ζώνη LED" #: lib/logitech_receiver/settings_templates.py:1477 msgid "Speed" msgstr "Ταχύτητα" #: lib/logitech_receiver/settings_templates.py:1478 msgid "Period" msgstr "Περίοδος" #: lib/logitech_receiver/settings_templates.py:1479 msgid "Intensity" msgstr "Ένταση" #: lib/logitech_receiver/settings_templates.py:1480 msgid "Ramp" msgstr "Κλίμακα" #: lib/logitech_receiver/settings_templates.py:1494 msgid "LEDs" msgstr "Λυχνίες LED" #: lib/solaar/ui/__init__.py:112 msgid "Another Solaar process is already running so just expose its window" msgstr "" "Μια άλλη διεργασία Solaar εκτελείται ήδη, οπότε απλά ανοίξτε το παράθυρό της" #: lib/solaar/ui/about.py:38 msgid "" "Manages Logitech receivers,\n" "keyboards, mice, and tablets." msgstr "" "Διαχειρίζεται δέκτες Logitech,\n" "πληκτρολόγια, ποντίκια και τάμπλετ." #: lib/solaar/ui/about.py:46 msgid "Additional Programming" msgstr "Πρόσθετος προγραμματισμός" #: lib/solaar/ui/about.py:47 msgid "GUI design" msgstr "Σχεδιασμός GUI" #: lib/solaar/ui/about.py:49 msgid "Testing" msgstr "Δοκιμές" #: lib/solaar/ui/about.py:57 msgid "Logitech documentation" msgstr "Τεκμηρίωση Logitech" #: lib/solaar/ui/action.py:83 lib/solaar/ui/action.py:87 #: lib/solaar/ui/window.py:199 msgid "Unpair" msgstr "Διαχωρισμός" #: lib/solaar/ui/action.py:86 lib/solaar/ui/diversion_rules.py:104 msgid "Cancel" msgstr "Ακύρωση" #: lib/solaar/ui/common.py:35 msgid "Permissions error" msgstr "Σφάλμα δικαιωμάτων" #: lib/solaar/ui/common.py:37 #, python-format msgid "" "Found a Logitech receiver or device (%s), but did not have permission to " "open it." msgstr "" "Βρέθηκε ένας δέκτης ή μια συσκευή Logitech (%s), αλλά δεν είχατε την άδεια " "να την ανοίξετε." #: lib/solaar/ui/common.py:39 msgid "" "If you've just installed Solaar, try disconnecting the receiver or device " "and then reconnecting it." msgstr "" "Εάν μόλις εγκαταστήσατε το Solaar, δοκιμάστε να αποσυνδέσετε το δέκτη ή τη " "συσκευή και στη συνέχεια να το επανασυνδέσετε." #: lib/solaar/ui/common.py:42 msgid "Cannot connect to device error" msgstr "Δεν είναι δυνατή η σύνδεση σφάλμα συσκευής" #: lib/solaar/ui/common.py:44 #, python-format msgid "" "Found a Logitech receiver or device at %s, but encountered an error " "connecting to it." msgstr "" "Βρέθηκε ένας δέκτης ή μια συσκευή Logitech στη διεύθυνση %s, αλλά " "παρουσιάστηκε σφάλμα σύνδεσης." #: lib/solaar/ui/common.py:46 msgid "" "Try disconnecting the device and then reconnecting it or turning it off and " "then on." msgstr "" "Δοκιμάστε να αποσυνδέσετε τη συσκευή και στη συνέχεια να την επανασυνδέσετε " "ή να την απενεργοποιήσετε και στη συνέχεια να την ενεργοποιήσετε." #: lib/solaar/ui/common.py:49 msgid "Unpairing failed" msgstr "Αποτυχία διαχωρισμού" #: lib/solaar/ui/common.py:51 #, python-brace-format msgid "Failed to unpair %{device} from %{receiver}." msgstr "Απέτυχα να διαχωρίσω την συσκευή %{device} από τον %{receiver}." #: lib/solaar/ui/common.py:53 msgid "The receiver returned an error, with no further details." msgstr "Ο δέκτης επέστρεψε ένα λάθος, χωρίς άλλες πληροφορίες." #: lib/solaar/ui/config_panel.py:231 msgid "Complete - ENTER to change" msgstr "Ολοκλήρωση - ENTER για αλλαγή" #: lib/solaar/ui/config_panel.py:231 msgid "Incomplete" msgstr "Ελλιπής" #: lib/solaar/ui/config_panel.py:476 lib/solaar/ui/config_panel.py:529 #, python-format msgid "%d value" msgid_plural "%d values" msgstr[0] "%d Τιμή" msgstr[1] "%d τιμές" #: lib/solaar/ui/config_panel.py:612 msgid "Changes allowed" msgstr "Επιτρεπτές αλλαγές" #: lib/solaar/ui/config_panel.py:613 msgid "No changes allowed" msgstr "Μη επιτρεπτές αλλαγές" #: lib/solaar/ui/config_panel.py:614 msgid "Ignore this setting" msgstr "Αγνόηση αυτής της ρύθμισης" #: lib/solaar/ui/config_panel.py:659 msgid "Working" msgstr "Εργάζεται" #: lib/solaar/ui/config_panel.py:662 msgid "Read/write operation failed." msgstr "Αποτυχία λειτουργίας ανάγνωσης/εγγραφής." #: lib/solaar/ui/diversion_rules.py:66 msgid "Built-in rules" msgstr "Ενσωματωμένοι κανόνες" #: lib/solaar/ui/diversion_rules.py:66 msgid "User-defined rules" msgstr "Κανόνες που ορίζονται από το χρήστη" #: lib/solaar/ui/diversion_rules.py:68 lib/solaar/ui/diversion_rules.py:1089 msgid "Rule" msgstr "Κανόνας" #: lib/solaar/ui/diversion_rules.py:69 lib/solaar/ui/diversion_rules.py:538 #: lib/solaar/ui/diversion_rules.py:665 msgid "Sub-rule" msgstr "Υποκανόνας" #: lib/solaar/ui/diversion_rules.py:71 msgid "[empty]" msgstr "[κενό]" #: lib/solaar/ui/diversion_rules.py:95 msgid "Make changes permanent?" msgstr "Να κάνετε τις αλλαγές μόνιμες;" #: lib/solaar/ui/diversion_rules.py:100 msgid "Yes" msgstr "Ναι" #: lib/solaar/ui/diversion_rules.py:102 msgid "No" msgstr "Όχι" #: lib/solaar/ui/diversion_rules.py:107 msgid "If you choose No, changes will be lost when Solaar is closed." msgstr "Αν επιλέξετε Όχι, οι αλλαγές θα χαθούν όταν κλείσει το Solaar." #: lib/solaar/ui/diversion_rules.py:138 msgid "Solaar Rule Editor" msgstr "Συντάκτης κανόνων Solaar" #: lib/solaar/ui/diversion_rules.py:231 msgid "Save changes" msgstr "Αποθήκευση αλλαγών" #: lib/solaar/ui/diversion_rules.py:236 msgid "Discard changes" msgstr "Απόρριψη αλλαγών" #: lib/solaar/ui/diversion_rules.py:397 msgid "Insert here" msgstr "Εισαγωγή εδώ" #: lib/solaar/ui/diversion_rules.py:399 msgid "Insert above" msgstr "Εισαγωγή επάνω" #: lib/solaar/ui/diversion_rules.py:401 msgid "Insert below" msgstr "Εισαγωγή από κάτω" #: lib/solaar/ui/diversion_rules.py:407 msgid "Insert new rule here" msgstr "Εισαγωγή νέου κανόνα εδώ" #: lib/solaar/ui/diversion_rules.py:409 msgid "Insert new rule above" msgstr "Εισαγωγή νέου κανόνα επάνω" #: lib/solaar/ui/diversion_rules.py:411 msgid "Insert new rule below" msgstr "Εισαγωγή νέου κανόνα από κάτω" #: lib/solaar/ui/diversion_rules.py:456 msgid "Paste here" msgstr "Επικόλληση εδώ" #: lib/solaar/ui/diversion_rules.py:458 msgid "Paste above" msgstr "Επικόλληση επάνω" #: lib/solaar/ui/diversion_rules.py:460 msgid "Paste below" msgstr "Επικόλληση από κάτω" #: lib/solaar/ui/diversion_rules.py:466 msgid "Paste rule here" msgstr "Επικόλληση κανόνα εδώ" #: lib/solaar/ui/diversion_rules.py:468 msgid "Paste rule above" msgstr "Επικόλληση κανόνα επάνω" #: lib/solaar/ui/diversion_rules.py:470 msgid "Paste rule below" msgstr "Επικόλληση κανόνα από κάτω" #: lib/solaar/ui/diversion_rules.py:474 msgid "Paste rule" msgstr "Επικόλληση κανόνα" #: lib/solaar/ui/diversion_rules.py:503 msgid "Flatten" msgstr "Ισοπέδωση" #: lib/solaar/ui/diversion_rules.py:536 msgid "Insert" msgstr "Εισαγωγή" #: lib/solaar/ui/diversion_rules.py:539 lib/solaar/ui/diversion_rules.py:667 #: lib/solaar/ui/diversion_rules.py:1129 msgid "Or" msgstr "Ή" #: lib/solaar/ui/diversion_rules.py:540 lib/solaar/ui/diversion_rules.py:666 #: lib/solaar/ui/diversion_rules.py:1115 msgid "And" msgstr "Και" #: lib/solaar/ui/diversion_rules.py:542 msgid "Condition" msgstr "Συνθήκη" #: lib/solaar/ui/diversion_rules.py:544 lib/solaar/ui/diversion_rules.py:1290 msgid "Feature" msgstr "Χαρακτηριστικό" #: lib/solaar/ui/diversion_rules.py:545 lib/solaar/ui/diversion_rules.py:1325 msgid "Report" msgstr "Αναφορά" #: lib/solaar/ui/diversion_rules.py:546 lib/solaar/ui/diversion_rules.py:1204 msgid "Process" msgstr "Διεργασία" #: lib/solaar/ui/diversion_rules.py:547 msgid "Mouse process" msgstr "Διεργασία ποντικιού" #: lib/solaar/ui/diversion_rules.py:548 lib/solaar/ui/diversion_rules.py:1362 msgid "Modifiers" msgstr "Τροποποιητικοί Παράγοντες" #: lib/solaar/ui/diversion_rules.py:549 lib/solaar/ui/diversion_rules.py:1414 msgid "Key" msgstr "Πλήκτρο" #: lib/solaar/ui/diversion_rules.py:550 lib/solaar/ui/diversion_rules.py:1455 msgid "KeyIsDown" msgstr "ΤοΠλήκτροΕίναιΠατημένο" #: lib/solaar/ui/diversion_rules.py:551 lib/solaar/ui/diversion_rules.py:2260 msgid "Active" msgstr "Ενεργό" #: lib/solaar/ui/diversion_rules.py:552 lib/solaar/ui/diversion_rules.py:2217 #: lib/solaar/ui/diversion_rules.py:2269 lib/solaar/ui/diversion_rules.py:2319 msgid "Device" msgstr "Συσκευή" #: lib/solaar/ui/diversion_rules.py:553 lib/solaar/ui/diversion_rules.py:2295 msgid "Host" msgstr "ΟικοδεσπότηςΥποδοχέας" #: lib/solaar/ui/diversion_rules.py:554 lib/solaar/ui/diversion_rules.py:2337 msgid "Setting" msgstr "Ρύθμιση" #: lib/solaar/ui/diversion_rules.py:555 lib/solaar/ui/diversion_rules.py:1470 #: lib/solaar/ui/diversion_rules.py:1519 msgid "Test" msgstr "Δοκιμή" #: lib/solaar/ui/diversion_rules.py:556 lib/solaar/ui/diversion_rules.py:1633 msgid "Test bytes" msgstr "Δοκιμαστικά bytes" #: lib/solaar/ui/diversion_rules.py:557 lib/solaar/ui/diversion_rules.py:1734 msgid "Mouse Gesture" msgstr "Χειρονομία ποντικιού" #: lib/solaar/ui/diversion_rules.py:561 msgid "Action" msgstr "Ενέργεια" #: lib/solaar/ui/diversion_rules.py:563 lib/solaar/ui/diversion_rules.py:1844 msgid "Key press" msgstr "Πάτημα πλήκτρου" #: lib/solaar/ui/diversion_rules.py:564 lib/solaar/ui/diversion_rules.py:1895 msgid "Mouse scroll" msgstr "Κύλιση ποντικιού" #: lib/solaar/ui/diversion_rules.py:565 lib/solaar/ui/diversion_rules.py:1956 msgid "Mouse click" msgstr "Κλικ ποντικιού" #: lib/solaar/ui/diversion_rules.py:566 msgid "Set" msgstr "Ορισμός" #: lib/solaar/ui/diversion_rules.py:567 lib/solaar/ui/diversion_rules.py:2026 msgid "Execute" msgstr "Εκτέλεση" #: lib/solaar/ui/diversion_rules.py:568 lib/solaar/ui/diversion_rules.py:1160 msgid "Later" msgstr "Αργότερα" #: lib/solaar/ui/diversion_rules.py:597 msgid "Insert new rule" msgstr "Εισαγωγή νέου κανόνα" #: lib/solaar/ui/diversion_rules.py:617 lib/solaar/ui/diversion_rules.py:1681 #: lib/solaar/ui/diversion_rules.py:1786 lib/solaar/ui/diversion_rules.py:1985 msgid "Delete" msgstr "Διαγραφή" #: lib/solaar/ui/diversion_rules.py:639 msgid "Negate" msgstr "Αρνηση" #: lib/solaar/ui/diversion_rules.py:663 msgid "Wrap with" msgstr "Τύλιγμα με" #: lib/solaar/ui/diversion_rules.py:685 msgid "Cut" msgstr "Αποκοπή" #: lib/solaar/ui/diversion_rules.py:700 msgid "Paste" msgstr "Επικόλληση" #: lib/solaar/ui/diversion_rules.py:706 msgid "Copy" msgstr "Αντιγραφή" #: lib/solaar/ui/diversion_rules.py:1070 msgid "This editor does not support the selected rule component yet." msgstr "" "Αυτός ο συντάκτης δεν υποστηρίζει ακόμη την επιλεγμένη συνιστώσα κανόνα." #: lib/solaar/ui/diversion_rules.py:1140 msgid "Number of seconds to delay." msgstr "Αριθμός δευτερολέπτων καθυστέρησης." #: lib/solaar/ui/diversion_rules.py:1178 msgid "Not" msgstr "Μη" #: lib/solaar/ui/diversion_rules.py:1187 msgid "X11 active process. For use in X11 only." msgstr "Ενεργή διαδικασία X11. Για χρήση μόνο στο X11." #: lib/solaar/ui/diversion_rules.py:1217 msgid "X11 mouse process. For use in X11 only." msgstr "Διαδικασία ποντικιού X11. Για χρήση μόνο στο X11." #: lib/solaar/ui/diversion_rules.py:1234 msgid "MouseProcess" msgstr "ΔιαδικασίαΠοντικιού" #: lib/solaar/ui/diversion_rules.py:1258 msgid "Feature name of notification triggering rule processing." msgstr "" "Όνομα χαρακτηριστικού της ειδοποίησης που ενεργοποιεί την επεξεργασία κανόνα." #: lib/solaar/ui/diversion_rules.py:1305 msgid "Report number of notification triggering rule processing." msgstr "" "Αριθμός αναφοράς της ειδοποίησης που ενεργοποιεί την επεξεργασία του κανόνα." #: lib/solaar/ui/diversion_rules.py:1338 msgid "Active keyboard modifiers. Not always available in Wayland." msgstr "" "Ενεργοί τροποποιούμενοι παράγοντες πληκτρολογίου. Δεν είναι πάντα διαθέσιμο " "στο Wayland." #: lib/solaar/ui/diversion_rules.py:1378 msgid "" "Diverted key or button depressed or released.\n" "Use the Key/Button Diversion and Divert G Keys settings to divert keys and " "buttons." msgstr "" "Εκτροπή πλήκτρου ή κουμπιού πατημένο ή απελευθερωμένο.\n" "Χρησιμοποιήστε τις ρυθμίσεις εκτροπής πλήκτρων/κουμπιών και εκτροπής " "πλήκτρων G για να εκτρέψετε πλήκτρα και κουμπιά." #: lib/solaar/ui/diversion_rules.py:1387 msgid "Key down" msgstr "Πλήκτρο κάτω" #: lib/solaar/ui/diversion_rules.py:1390 msgid "Key up" msgstr "Πλήκτρο πάνω" #: lib/solaar/ui/diversion_rules.py:1430 msgid "" "Diverted key or button is currently down.\n" "Use the Key/Button Diversion and Divert G Keys settings to divert keys and " "buttons." msgstr "" "Το πλήκτρο ή το κουμπί εκτροπής είναι προς το παρόν κατεβασμένο.\n" "Χρησιμοποιήστε τις ρυθμίσεις εκτροπής πλήκτρων/κουμπιών και εκτροπής " "πλήκτρων G για να εκτρέψετε πλήκτρα και κουμπιά." #: lib/solaar/ui/diversion_rules.py:1468 msgid "Test condition on notification triggering rule processing." msgstr "" "Συνθήκη δοκιμής για την ειδοποίηση που ενεργοποιεί την επεξεργασία κανόνα." #: lib/solaar/ui/diversion_rules.py:1472 msgid "Parameter" msgstr "Παράμετρος" #: lib/solaar/ui/diversion_rules.py:1534 msgid "begin (inclusive)" msgstr "αρχή ( συμπεριλαμβανομένου)" #: lib/solaar/ui/diversion_rules.py:1535 msgid "end (exclusive)" msgstr "τέλος ( αποκλειστικό)" #: lib/solaar/ui/diversion_rules.py:1543 msgid "range" msgstr "εύρος" #: lib/solaar/ui/diversion_rules.py:1546 msgid "minimum" msgstr "ελάχιστο" #: lib/solaar/ui/diversion_rules.py:1547 msgid "maximum" msgstr "μέγιστο" #: lib/solaar/ui/diversion_rules.py:1549 #, python-format msgid "bytes %(0)d to %(1)d, ranging from %(2)d to %(3)d" msgstr "bytes %(0)d έως %(1)d, από %(2)d έως %(3)d" #: lib/solaar/ui/diversion_rules.py:1552 lib/solaar/ui/diversion_rules.py:1553 msgid "mask" msgstr "μάσκα" #: lib/solaar/ui/diversion_rules.py:1554 #, python-format msgid "bytes %(0)d to %(1)d, mask %(2)d" msgstr "bytes %(0)d έως %(1)d, μάσκα %(2)d" #: lib/solaar/ui/diversion_rules.py:1563 msgid "" "Bit or range test on bytes in notification message triggering rule " "processing." msgstr "" "Δοκιμή bit ή εύρους σε bytes στο μήνυμα ειδοποίησης που ενεργοποιεί την " "επεξεργασία κανόνα." #: lib/solaar/ui/diversion_rules.py:1573 msgid "type" msgstr "τύπος" #: lib/solaar/ui/diversion_rules.py:1661 msgid "" "Mouse gesture with optional initiating button followed by zero or more mouse " "movements." msgstr "" "Χειρονομία ποντικιού με προαιρετικό κουμπί εκκίνησης που ακολουθείται από " "μηδέν ή περισσότερες κινήσεις του ποντικιού." #: lib/solaar/ui/diversion_rules.py:1666 msgid "Add movement" msgstr "Προσθήκη κίνησης" #: lib/solaar/ui/diversion_rules.py:1760 msgid "" "Simulate a chorded key click or depress or release.\n" "On Wayland requires write access to /dev/uinput." msgstr "" "Προσομοίωση ενός χορδισμένου πλήκτρου κλικ ή πάτημα ή απελευθέρωση.\n" "Στο Wayland απαιτεί πρόσβαση εγγραφής στο /dev/uinput." #: lib/solaar/ui/diversion_rules.py:1765 msgid "Add key" msgstr "Προσθήκη πλήκτρου" #: lib/solaar/ui/diversion_rules.py:1768 msgid "Click" msgstr "Κλικ" #: lib/solaar/ui/diversion_rules.py:1771 msgid "Depress" msgstr "Απελευθέρωση" #: lib/solaar/ui/diversion_rules.py:1774 msgid "Release" msgstr "Αποδέσμευση" #: lib/solaar/ui/diversion_rules.py:1859 msgid "" "Simulate a mouse scroll.\n" "On Wayland requires write access to /dev/uinput." msgstr "" "Προσομοίωση κύλισης ποντικιού.\n" "Στο Wayland απαιτεί πρόσβαση εγγραφής στο /dev/uinput." #: lib/solaar/ui/diversion_rules.py:1915 msgid "" "Simulate a mouse click.\n" "On Wayland requires write access to /dev/uinput." msgstr "" "Προσομοίωση ενός κλικ του ποντικιού.\n" "Στο Wayland απαιτεί πρόσβαση εγγραφής στο /dev/uinput." #: lib/solaar/ui/diversion_rules.py:1918 msgid "Button" msgstr "Κουμπί" #: lib/solaar/ui/diversion_rules.py:1919 msgid "Count and Action" msgstr "Καταμέτρηση και Δράση" #: lib/solaar/ui/diversion_rules.py:1968 msgid "Execute a command with arguments." msgstr "Εκτέλεση μιας εντολής με ορίσματα." #: lib/solaar/ui/diversion_rules.py:1971 msgid "Add argument" msgstr "Προσθήκη ορίσματος" #: lib/solaar/ui/diversion_rules.py:2046 msgid "Toggle" msgstr "Εναλλαγή" #: lib/solaar/ui/diversion_rules.py:2047 msgid "True" msgstr "Αληθές" #: lib/solaar/ui/diversion_rules.py:2048 msgid "False" msgstr "Ψευδές" #: lib/solaar/ui/diversion_rules.py:2061 msgid "Unsupported setting" msgstr "Μη υποστηριζόμενη ρύθμιση" #: lib/solaar/ui/diversion_rules.py:2223 lib/solaar/ui/diversion_rules.py:2243 #: lib/solaar/ui/diversion_rules.py:2325 lib/solaar/ui/diversion_rules.py:2568 #: lib/solaar/ui/diversion_rules.py:2586 msgid "Originating device" msgstr "Συσκευή προέλευσης" #: lib/solaar/ui/diversion_rules.py:2256 msgid "Device is active and its settings can be changed." msgstr "Η συσκευή είναι ενεργή και οι ρυθμίσεις της μπορούν να αλλάξουν." #: lib/solaar/ui/diversion_rules.py:2265 msgid "Device that originated the current notification." msgstr "Συσκευή από την οποία προήλθε η τρέχουσα ειδοποίηση." #: lib/solaar/ui/diversion_rules.py:2278 msgid "Name of host computer." msgstr "Όνομα του υπολογιστή υποδοχής." #: lib/solaar/ui/diversion_rules.py:2345 msgid "Value" msgstr "Τιμή" #: lib/solaar/ui/diversion_rules.py:2353 msgid "Item" msgstr "Στοιχείο" #: lib/solaar/ui/diversion_rules.py:2627 msgid "Change setting on device" msgstr "Αλλαγή ρύθμισης στη συσκευή" #: lib/solaar/ui/diversion_rules.py:2643 msgid "Setting on device" msgstr "Ρύθμιση στη συσκευή" #: lib/solaar/ui/notify.py:118 msgid "unspecified reason" msgstr "αδιευκρίνιστος λόγος" #: lib/solaar/ui/pair_window.py:126 lib/solaar/ui/pair_window.py:260 #: lib/solaar/ui/pair_window.py:296 #, python-format msgid "%(receiver_name)s: pair new device" msgstr "%(receiver_name)s: ζεύξη νέας συσκευής" #: lib/solaar/ui/pair_window.py:127 #, python-format msgid "Enter passcode on %(name)s." msgstr "Πληκτρολογήστε τον κωδικό πρόσβασης στο %(name)s." #: lib/solaar/ui/pair_window.py:130 #, python-format msgid "Type %(passcode)s and then press the enter key." msgstr "" "Πληκτρολογήστε %(passcode)s και, στη συνέχεια, πατήστε το πλήκτρο enter." #: lib/solaar/ui/pair_window.py:133 msgid "left" msgstr "αριστερά" #: lib/solaar/ui/pair_window.py:133 msgid "right" msgstr "δεξιά" #: lib/solaar/ui/pair_window.py:135 #, python-format msgid "" "Press %(code)s\n" "and then press left and right buttons simultaneously." msgstr "" "Πατήστε %(code)s\n" "και στη συνέχεια πατήστε ταυτόχρονα το αριστερό και το δεξί κουμπί." #: lib/solaar/ui/pair_window.py:192 msgid "Pairing failed" msgstr "Αποτυχία συνταιρίασματος" #: lib/solaar/ui/pair_window.py:194 msgid "Make sure your device is within range, and has a decent battery charge." msgstr "" "Σιγουρέψου ότι η συσκευή είναι εντός εμβέλειας, και έχει επαρκώς φορτισμένη " "μπαταρία." #: lib/solaar/ui/pair_window.py:196 msgid "A new device was detected, but it is not compatible with this receiver." msgstr "Εντοπίστηκε μια συσκευή, αλλά δεν είναι συμβατή με αυτόν τον δέκτη." #: lib/solaar/ui/pair_window.py:198 msgid "More paired devices than receiver can support." msgstr "" "Περισσότερες συζευγμένες συσκευές από όσες μπορεί να υποστηρίξει ο δέκτης." #: lib/solaar/ui/pair_window.py:200 msgid "No further details are available about the error." msgstr "Δεν υπάρχουν άλλες πληροφορίες για το σφάλμα." #: lib/solaar/ui/pair_window.py:214 msgid "Found a new device:" msgstr "Βρήκα μια νέα συσκευή:" #: lib/solaar/ui/pair_window.py:239 msgid "The wireless link is not encrypted" msgstr "Η ασύρματη σύνδεση είναι χωρίς κρυπτογράφηση" #: lib/solaar/ui/pair_window.py:268 msgid "Unifying receivers are only compatible with Unifying devices." msgstr "Οι δέκτες Unifying είναι συμβατοί μόνο με συσκευές Unifying." #: lib/solaar/ui/pair_window.py:270 msgid "Bolt receivers are only compatible with Bolt devices." msgstr "Οι δέκτες Bolt είναι συμβατοί μόνο με συσκευές Bolt." #: lib/solaar/ui/pair_window.py:272 msgid "Other receivers are only compatible with a few devices." msgstr "Άλλοι δέκτες είναι συμβατοί μόνο με λίγες συσκευές." #: lib/solaar/ui/pair_window.py:274 msgid "The device must not be paired with a nearby powered-on receiver." msgstr "" "Η συσκευή δεν πρέπει να είναι συνδεδεμένη με έναν κοντινό ενεργοποιημένο " "δέκτη." #: lib/solaar/ui/pair_window.py:278 msgid "Press a pairing button or key until the pairing light flashes quickly." msgstr "" "Πιέστε ένα κουμπί ή πλήκτρο ζεύξης μέχρι να αναβοσβήσει γρήγορα η φωτεινή " "ένδειξη ζεύξης." #: lib/solaar/ui/pair_window.py:280 msgid "You may have to first turn the device off and on again." msgstr "" "Ίσως χρειαστεί πρώτα να απενεργοποιήσετε και να ενεργοποιήσετε ξανά τη " "συσκευή." #: lib/solaar/ui/pair_window.py:282 msgid "Turn on the device you want to pair." msgstr "Άνοιξε την συσκευή που θέλεις να συνταιριάξεις." #: lib/solaar/ui/pair_window.py:284 msgid "If the device is already turned on, turn it off and on again." msgstr "" "Εάν η συσκευή είναι ήδη ενεργοποιημένη, απενεργοποιήστε την και " "ενεργοποιήστε την ξανά." #: lib/solaar/ui/pair_window.py:288 #, python-format msgid "" "\n" "\n" "This receiver has %d pairing remaining." msgid_plural "" "\n" "\n" "This receiver has %d pairings remaining." msgstr[0] "" "\n" "\n" "Αυτός ο δέκτης έχει %d ζεύξη που απομένει." msgstr[1] "" "\n" "\n" "Αυτός ο δέκτης έχει %d ζεύξεις που απομένουν." #: lib/solaar/ui/pair_window.py:294 msgid "" "\n" "Cancelling at this point will not use up a pairing." msgstr "" "\n" "Η ακύρωση σε αυτό το σημείο δεν θα χρησιμοποιήσει μια αντιστοίχιση." #: lib/solaar/ui/tray.py:58 msgid "No supported device found" msgstr "Δεν βρέθηκε υποστηριζόμενη συσκευή" #: lib/solaar/ui/tray.py:63 lib/solaar/ui/window.py:321 #, python-format msgid "About %s" msgstr "Περί %s" #: lib/solaar/ui/tray.py:64 lib/solaar/ui/window.py:319 #, python-format msgid "Quit %s" msgstr "Έξοδος %s" #: lib/solaar/ui/tray.py:294 lib/solaar/ui/tray.py:302 msgid "no receiver" msgstr "χωρίς δέκτη" #: lib/solaar/ui/tray.py:315 lib/solaar/ui/tray.py:320 msgid "offline" msgstr "εκτός σύνδεσης" #: lib/solaar/ui/tray.py:318 msgid "no status" msgstr "χωρίς κατάσταση" #: lib/solaar/ui/window.py:99 msgid "Scanning" msgstr "Σάρωση" #: lib/solaar/ui/window.py:132 msgid "Battery" msgstr "Μπαταρία" #: lib/solaar/ui/window.py:135 msgid "Wireless Link" msgstr "Ασύρματη ζεύξη" #: lib/solaar/ui/window.py:139 msgid "Lighting" msgstr "Φωτισμός" #: lib/solaar/ui/window.py:173 msgid "Show Technical Details" msgstr "Εμφάνιση τεχνικών λεπτομερειών" #: lib/solaar/ui/window.py:189 msgid "Pair new device" msgstr "Συνταίριασμα νέας συσκευής" #: lib/solaar/ui/window.py:207 msgid "Select a device" msgstr "Επέλεξε μια συσκευή" #: lib/solaar/ui/window.py:324 msgid "Rule Editor" msgstr "Συντάκτης κανόνων" #: lib/solaar/ui/window.py:543 msgid "Path" msgstr "Διαδρομή" #: lib/solaar/ui/window.py:546 msgid "USB ID" msgstr "USB ID" #: lib/solaar/ui/window.py:549 lib/solaar/ui/window.py:551 #: lib/solaar/ui/window.py:566 lib/solaar/ui/window.py:568 msgid "Serial" msgstr "Serial" #: lib/solaar/ui/window.py:555 msgid "Index" msgstr "Index" #: lib/solaar/ui/window.py:557 msgid "Wireless PID" msgstr "Wireless PID" #: lib/solaar/ui/window.py:559 msgid "Product ID" msgstr "Αναγνωριστικό προϊόντος" #: lib/solaar/ui/window.py:561 msgid "Protocol" msgstr "Πρωτόκολλο" #: lib/solaar/ui/window.py:561 msgid "Unknown" msgstr "Άγνωστο" #: lib/solaar/ui/window.py:563 msgid "Polling rate" msgstr "Ρυθμός ανανέωσης" #: lib/solaar/ui/window.py:570 msgid "Unit ID" msgstr "Unit ID" #: lib/solaar/ui/window.py:584 msgid "Notifications" msgstr "Ειδοποιήσεις" #: lib/solaar/ui/window.py:628 msgid "No device paired." msgstr "Δεν υπάρχει αντιστοιχισμένη συσκευή." #: lib/solaar/ui/window.py:637 #, python-format msgid "Up to %(max_count)s device can be paired to this receiver." msgid_plural "Up to %(max_count)s devices can be paired to this receiver." msgstr[0] "" "Έως και %(max_count)s συσκευή μπορεί να συνδεθεί με αυτόν τον δέκτη." msgstr[1] "" "Έως και %(max_count)s συσκευές μπορούν να αντιστοιχιστούν σε αυτόν τον δέκτη." #: lib/solaar/ui/window.py:648 #, python-format msgid "This receiver has %d pairing remaining." msgid_plural "This receiver has %d pairings remaining." msgstr[0] "Αυτός ο δέκτης έχει %d ζεύξη που απομένει." msgstr[1] "Αυτός ο δέκτης έχει %d ζεύξεις που απομένουν." #: lib/solaar/ui/window.py:705 msgid "Battery Voltage" msgstr "Τάση μπαταρίας" #: lib/solaar/ui/window.py:707 msgid "Voltage reported by battery" msgstr "Τάση που αναφέρεται από την μπαταρία" #: lib/solaar/ui/window.py:709 msgid "Battery Level" msgstr "Στάθμη μπαταρίας" #: lib/solaar/ui/window.py:711 msgid "Approximate level reported by battery" msgstr "Προσεγγιστικό επίπεδο που αναφέρεται από την μπαταρία" #: lib/solaar/ui/window.py:718 lib/solaar/ui/window.py:720 msgid "next reported " msgstr "επόμενο αναφερόμενο " #: lib/solaar/ui/window.py:721 msgid " and next level to be reported." msgstr " και το επόμενο επίπεδο που θα αναφερθεί." #: lib/solaar/ui/window.py:737 msgid "encrypted" msgstr "κρυπτογραφημένη" #: lib/solaar/ui/window.py:739 msgid "The wireless link between this device and its receiver is encrypted." msgstr "" "Η ασύρματη ζεύξη μεταξύ αυτής της συσκευής και του δέκτη είναι " "κρυπτογραφημένη." #: lib/solaar/ui/window.py:741 msgid "not encrypted" msgstr "χωρίς κρυπτογράφηση" #: lib/solaar/ui/window.py:745 msgid "" "The wireless link between this device and its receiver is not encrypted.\n" "This is a security issue for pointing devices, and a major security issue " "for text-input devices." msgstr "" "Η ασύρματη σύνδεση μεταξύ αυτής της συσκευής και του δέκτη της δεν είναι " "κρυπτογραφημένη.\n" "Αυτό είναι ένα ζήτημα ασφάλειας για τις συσκευές επισήμανσης και ένα " "σημαντικό ζήτημα ασφάλειας για τις συσκευές εισαγωγής κειμένου." #: lib/solaar/ui/window.py:761 #, python-format msgid "%(light_level)d lux" msgstr "%(light_level)d lux" #~ msgid "Turn illumination on or off on keyboard." #~ msgstr "Ενεργοποίηση ή απενεργοποίηση του φωτισμού στο πληκτρολόγιο." #~ msgid "Polling Rate (ms)" #~ msgstr "Ρυθμός κλήσεων (ms)" #~ msgid "May also make M keys and MR key send HID++ notifications" #~ msgstr "" #~ "Μπορεί επίσης να κάνει τα κλειδιά M και MR να στέλνουν ειδοποιήσεις HID++" #, python-format #~ msgid "Battery: %(level)s" #~ msgstr "Μπαταρία: %(level)s" #, python-format #~ msgid "Battery: %(percent)d%%" #~ msgstr "Μπαταρία: %(percent)d%%" #, python-format #~ msgid "Lighting: %(level)s lux" #~ msgstr "Φωτισμός: %(level)s lux" #, python-format #~ msgid "%(rate)d ms (%(rate_hz)dHz)" #~ msgstr "%(rate)d ms (%(rate_hz)dHz)" #~ msgid "none" #~ msgstr "καμία" #~ msgid "Only one device can be paired to this receiver." #~ msgstr "Μόνο μία συσκευή μπορεί να συνδεθεί με αυτόν τον δέκτη." #~ msgid "last known" #~ msgstr "τελευταία γνωστή" #~ msgid " paired devices." #~ msgstr " συνταιριασμένες συσκευές." #~ msgid "1 paired device." #~ msgstr "Μία συνταιριασμένη συσκευή" #~ msgid "Count" #~ msgstr "Πλήθος" #~ msgid "Device originated the current notification." #~ msgstr "Η συσκευή από την οποία προήλθε η τρέχουσα ειδοποίηση." #, python-format #~ msgid "" #~ "Found a Logitech Receiver (%s), but did not have permission to open it." #~ msgstr "" #~ "Βρέθηκε ένας δέκτης Logitech (%s), αλλά δεν έχετε δικαίωμα να τον " #~ "ανοίξετε." #~ msgid "Found a new device" #~ msgstr "Βρέθηκε νέα συσκευή" #~ msgid "" #~ "If the device is already turned on,\n" #~ "turn if off and on again." #~ msgstr "" #~ "Αν η συσκευή είναι ήδη ανοικτή,\n" #~ "κλείσε την και μετά άνοιξε την ξανά." #~ msgid "" #~ "If you've just installed Solaar, try removing the receiver and plugging " #~ "it back in." #~ msgstr "" #~ "Εάν μόλις κάνατε εγκατάσταση του Solaar, δοκιμάστε να αφαιρέσετε τον " #~ "δέκτη και να τον ξανασυνδέσετε." #~ msgid "No Logitech device found" #~ msgstr "Δεν βρέθηκε συσκευή Logitech" #~ msgid "No Logitech receiver found" #~ msgstr "Δεν βρέθηκε δέκτης Logitech" #~ msgid "No device paired" #~ msgstr "Χωρίς συνταιριασμένες συσκευές" #~ msgid "Only one device can be paired to this receiver" #~ msgstr "Μόνο μία συσκευή μπορεί να συνταιριαστεί σε αυτόν τον δέκτη" #~ msgid "" #~ "Shows status of devices connected\n" #~ "through wireless Logitech receivers." #~ msgstr "" #~ "Δείχνει την κατάσταση των συσκευών που συνδέονται\n" #~ "ασύρματα σε δέκτες Logitech." #~ msgid "Smooth Scrolling" #~ msgstr "Ομαλή κύλιση" #, python-format #~ msgid "The receiver only supports %d paired device(s)." #~ msgstr "Ο δέκτης υποστηρίζει μέχρι %d συνταιριασμένες συσκευές." #~ msgid "The receiver was unplugged." #~ msgstr "Ο δέκτης αποσυνδέθηκε." #~ msgid "" #~ "The wireless link between this device and its receiver is not encrypted.\n" #~ "\n" #~ "For pointing devices (mice, trackballs, trackpads), this is a minor " #~ "security issue.\n" #~ "\n" #~ "It is, however, a major security issue for text-input devices (keyboards, " #~ "numpads),\n" #~ "because typed text can be sniffed inconspicuously by 3rd parties within " #~ "range." #~ msgstr "" #~ "Η ασύρματη ζεύξη μεταξύ αυτής της συσκευής και του δέκτη της είναι χωρίς " #~ "κρυπτογράφηση.\n" #~ "\n" #~ "Για συσκευές κατάδειξης (ποντίκια, ιχνόσφαιρες, επιφάνειες αφής), αυτό " #~ "είναι ένα μικρό πρόβλημα ασφάλειας.\n" #~ "\n" #~ "Είναι όμως μεγάλο πρόβλημα ασφάλειας για συσκευές εισαγωγής κειμένου " #~ "(πληκτρολόγια),\n" #~ "μιας και το κείμενο μπορεί να υποκλαπεί από κακόβουλους εντός εμβέλειας." #~ msgid "USB id" #~ msgstr "USB id" #~ msgid "Up to %d devices can be paired to this receiver" #~ msgstr "Μέχρι %d συσκευές μπορούν να συνταιριαστούν σε αυτόν τον δέκτη" #~ msgid "closed" #~ msgstr "κλειστή" #~ msgid "height" #~ msgstr "ύψος" #~ msgid "lux" #~ msgstr "lux" #~ msgid "open" #~ msgstr "ανοιχτή" #~ msgid "pair new device" #~ msgstr "συνταίριασε μια νέα συσκευή" #~ msgid "paired devices" #~ msgstr "συνταιριασμένες συσκευές" #~ msgid "pairing lock is " #~ msgstr "το κλείδωμα ταιριάσματος είναι" #~ msgid "unknown" #~ msgstr "άγνωστο" #~ msgid "width" #~ msgstr "πλάτος" Solaar-1.1.14/po/es.po000066400000000000000000001665641473525213700144370ustar00rootroot00000000000000# Spanish translations for Solaar package. # Copyright (C) 2021 THE Solaar's COPYRIGHT HOLDER. # This file is distributed under the same license as the Solaar package. # Marc Serra , 2021. # msgid "" msgstr "" "Project-Id-Version: solaar 1.1.7\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-12-28 17:40+0100\n" "PO-Revision-Date: 2023-12-28 18:53+0100\n" "Last-Translator: Jose Luis Tirado \n" "Language-Team: none\n" "Language: es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Poedit 3.4.1\n" #: lib/logitech_receiver/base_usb.py:46 msgid "Bolt Receiver" msgstr "Receptor Bolt" #: lib/logitech_receiver/base_usb.py:57 msgid "Unifying Receiver" msgstr "Receptor Unifying" #: lib/logitech_receiver/base_usb.py:67 lib/logitech_receiver/base_usb.py:78 #: lib/logitech_receiver/base_usb.py:90 lib/logitech_receiver/base_usb.py:102 #: lib/logitech_receiver/base_usb.py:114 msgid "Nano Receiver" msgstr "Receptor Nano" #: lib/logitech_receiver/base_usb.py:124 msgid "Lightspeed Receiver" msgstr "Receptor Lightspeed" #: lib/logitech_receiver/base_usb.py:133 msgid "EX100 Receiver 27 Mhz" msgstr "Receptor EX100 27 Mhz" #: lib/logitech_receiver/i18n.py:30 msgid "empty" msgstr "vacía" #: lib/logitech_receiver/i18n.py:31 msgid "critical" msgstr "crítica" #: lib/logitech_receiver/i18n.py:32 msgid "low" msgstr "baja" #: lib/logitech_receiver/i18n.py:33 msgid "average" msgstr "media" #: lib/logitech_receiver/i18n.py:34 msgid "good" msgstr "buena" #: lib/logitech_receiver/i18n.py:35 msgid "full" msgstr "llena" #: lib/logitech_receiver/i18n.py:38 msgid "discharging" msgstr "descargando" #: lib/logitech_receiver/i18n.py:39 msgid "recharging" msgstr "recargando" #: lib/logitech_receiver/i18n.py:40 lib/solaar/ui/window.py:711 msgid "charging" msgstr "cargando" #: lib/logitech_receiver/i18n.py:41 msgid "not charging" msgstr "no cargando" #: lib/logitech_receiver/i18n.py:42 msgid "almost full" msgstr "casi llena" #: lib/logitech_receiver/i18n.py:43 msgid "charged" msgstr "cargado" #: lib/logitech_receiver/i18n.py:44 msgid "slow recharge" msgstr "recarga lenta" #: lib/logitech_receiver/i18n.py:45 msgid "invalid battery" msgstr "batería no válida" #: lib/logitech_receiver/i18n.py:46 msgid "thermal error" msgstr "error térmico" #: lib/logitech_receiver/i18n.py:47 msgid "error" msgstr "error" #: lib/logitech_receiver/i18n.py:48 msgid "standard" msgstr "estándar" #: lib/logitech_receiver/i18n.py:49 msgid "fast" msgstr "rápido" #: lib/logitech_receiver/i18n.py:50 msgid "slow" msgstr "lento" #: lib/logitech_receiver/i18n.py:53 msgid "device timeout" msgstr "tiempo agotado para el dispositivo" #: lib/logitech_receiver/i18n.py:54 msgid "device not supported" msgstr "dispositivo no soportado" #: lib/logitech_receiver/i18n.py:55 msgid "too many devices" msgstr "demasiados dispositivos" #: lib/logitech_receiver/i18n.py:56 msgid "sequence timeout" msgstr "tiempo agotado de secuencia" #: lib/logitech_receiver/i18n.py:59 lib/solaar/ui/window.py:572 msgid "Firmware" msgstr "Firmware" #: lib/logitech_receiver/i18n.py:60 msgid "Bootloader" msgstr "Gestor de arranque" #: lib/logitech_receiver/i18n.py:61 msgid "Hardware" msgstr "Hardware" #: lib/logitech_receiver/i18n.py:62 msgid "Other" msgstr "Otros" #: lib/logitech_receiver/i18n.py:65 msgid "Left Button" msgstr "Botón Izquierdo" #: lib/logitech_receiver/i18n.py:66 msgid "Right Button" msgstr "Botón Derecho" #: lib/logitech_receiver/i18n.py:67 msgid "Middle Button" msgstr "Botón Central" #: lib/logitech_receiver/i18n.py:68 msgid "Back Button" msgstr "Botón Atrás" #: lib/logitech_receiver/i18n.py:69 msgid "Forward Button" msgstr "Botón Adelante" #: lib/logitech_receiver/i18n.py:70 msgid "Mouse Gesture Button" msgstr "Botón Gestos Ratón" #: lib/logitech_receiver/i18n.py:71 msgid "Smart Shift" msgstr "Cambio inteligente" #: lib/logitech_receiver/i18n.py:72 msgid "DPI Switch" msgstr "Selector DPI" #: lib/logitech_receiver/i18n.py:73 msgid "Left Tilt" msgstr "Inclinación a la Izquierda" #: lib/logitech_receiver/i18n.py:74 msgid "Right Tilt" msgstr "Inclinación a la Derecha" #: lib/logitech_receiver/i18n.py:75 msgid "Left Click" msgstr "Clic Izquierdo" #: lib/logitech_receiver/i18n.py:76 msgid "Right Click" msgstr "Clic Derecho" #: lib/logitech_receiver/i18n.py:77 msgid "Mouse Middle Button" msgstr "Clic Central Ratón" #: lib/logitech_receiver/i18n.py:78 msgid "Mouse Back Button" msgstr "Clic Atrás Ratón" #: lib/logitech_receiver/i18n.py:79 msgid "Mouse Forward Button" msgstr "Clic Adelante Ratón" #: lib/logitech_receiver/i18n.py:80 msgid "Gesture Button Navigation" msgstr "Navegación con Botón de Gestos" #: lib/logitech_receiver/i18n.py:81 msgid "Mouse Scroll Left Button" msgstr "Botón desplazamiento a la Izquierda" #: lib/logitech_receiver/i18n.py:82 msgid "Mouse Scroll Right Button" msgstr "Botón de desplazamiento a la Derecha" #: lib/logitech_receiver/i18n.py:85 msgid "pressed" msgstr "pulsado" #: lib/logitech_receiver/i18n.py:86 msgid "released" msgstr "liberado" #: lib/logitech_receiver/notifications.py:75 #: lib/logitech_receiver/notifications.py:126 msgid "pairing lock is closed" msgstr "el bloqueo de vinculación está cerrado" #: lib/logitech_receiver/notifications.py:75 #: lib/logitech_receiver/notifications.py:126 msgid "pairing lock is open" msgstr "el bloqueo de vinculación está abierto" #: lib/logitech_receiver/notifications.py:92 msgid "discovery lock is closed" msgstr "el bloqueo de descubrimiento está cerrado" #: lib/logitech_receiver/notifications.py:92 msgid "discovery lock is open" msgstr "el bloqueo de detección está abierto" #: lib/logitech_receiver/notifications.py:224 lib/solaar/ui/notify.py:122 msgid "connected" msgstr "conectado" #: lib/logitech_receiver/notifications.py:224 msgid "disconnected" msgstr "desconectado" #: lib/logitech_receiver/notifications.py:262 lib/solaar/ui/notify.py:120 msgid "unpaired" msgstr "desvinculado" #: lib/logitech_receiver/notifications.py:304 msgid "powered on" msgstr "encendido" #: lib/logitech_receiver/settings.py:750 msgid "register" msgstr "registrar" #: lib/logitech_receiver/settings.py:764 lib/logitech_receiver/settings.py:791 msgid "feature" msgstr "característica" #: lib/logitech_receiver/settings_templates.py:139 msgid "Swap Fx function" msgstr "Intercambiar función Fx" #: lib/logitech_receiver/settings_templates.py:140 msgid "" "When set, the F1..F12 keys will activate their special function,\n" "and you must hold the FN key to activate their standard function." msgstr "" "Al activarse, las teclas F1..F12 activarán sus funciones especiales,\n" "y debe mantener pulsada la tecla FN para activar su función estándar." #: lib/logitech_receiver/settings_templates.py:142 msgid "" "When unset, the F1..F12 keys will activate their standard function,\n" "and you must hold the FN key to activate their special function." msgstr "" "Al desactivarse, las teclas F1..F12 activarán sus funciones estándar,\n" "y debe mantener pulsada la tecla FN para activar su función especial." #: lib/logitech_receiver/settings_templates.py:149 msgid "Hand Detection" msgstr "Detección de manos" #: lib/logitech_receiver/settings_templates.py:150 msgid "Turn on illumination when the hands hover over the keyboard." msgstr "Encender la iluminación cuando las manos pasen sobre el teclado." #: lib/logitech_receiver/settings_templates.py:157 msgid "Scroll Wheel Smooth Scrolling" msgstr "Desplazamiento suave de la rueda de desplazamiento" #: lib/logitech_receiver/settings_templates.py:158 #: lib/logitech_receiver/settings_templates.py:239 #: lib/logitech_receiver/settings_templates.py:267 msgid "High-sensitivity mode for vertical scroll with the wheel." msgstr "Modo de alta sensibilidad para desplazamiento vertical con la rueda." #: lib/logitech_receiver/settings_templates.py:165 msgid "Side Scrolling" msgstr "Desplazamiento lateral" #: lib/logitech_receiver/settings_templates.py:167 msgid "" "When disabled, pushing the wheel sideways sends custom button events\n" "instead of the standard side-scrolling events." msgstr "" "Al desactivarse, presionar la rueda lateralmente envía eventos de botones " "personalizados\n" "en vez de los eventos estándar de desplazamiento lateral." #: lib/logitech_receiver/settings_templates.py:177 msgid "Sensitivity (DPI - older mice)" msgstr "Sensibilidad (DPI - ratones más viejos)" #: lib/logitech_receiver/settings_templates.py:178 #: lib/logitech_receiver/settings_templates.py:712 msgid "Mouse movement sensitivity" msgstr "Sensibildad de movimientro del ratón" #: lib/logitech_receiver/settings_templates.py:208 #: lib/logitech_receiver/settings_templates.py:218 #: lib/logitech_receiver/settings_templates.py:225 msgid "Backlight" msgstr "Retroiluminación" #: lib/logitech_receiver/settings_templates.py:209 #: lib/logitech_receiver/settings_templates.py:226 msgid "Set illumination time for keyboard." msgstr "Establecer el tiempo de iluminación para el teclado." #: lib/logitech_receiver/settings_templates.py:219 msgid "Turn illumination on or off on keyboard." msgstr "Encencer o apagar la iluminación del teclado." #: lib/logitech_receiver/settings_templates.py:237 msgid "Scroll Wheel High Resolution" msgstr "Rueda de Desplazamiento de Alta Resolución" #: lib/logitech_receiver/settings_templates.py:240 #: lib/logitech_receiver/settings_templates.py:268 msgid "Set to ignore if scrolling is abnormally fast or slow" msgstr "" "Establecido para ignorar si el desplazamiento es anormalmente rápido o lento" #: lib/logitech_receiver/settings_templates.py:247 #: lib/logitech_receiver/settings_templates.py:277 msgid "Scroll Wheel Diversion" msgstr "Desvío de la Rueda de Desplazamiento" #: lib/logitech_receiver/settings_templates.py:249 msgid "" "Make scroll wheel send LOWRES_WHEEL HID++ notifications (which trigger " "Solaar rules but are otherwise ignored)." msgstr "" "Hacer que la rueda de desplazamiento envíe notificaciones HID++ de " "LOWRES_WHEEL (que activan reglas de Solaar, pero que, por lo demás, se " "ignoran)." #: lib/logitech_receiver/settings_templates.py:256 msgid "Scroll Wheel Direction" msgstr "Dirección de la Rueda de Desplazamiento" #: lib/logitech_receiver/settings_templates.py:257 msgid "Invert direction for vertical scroll with wheel." msgstr "Invertir dirección para desplazamiento vertical con la rueda." #: lib/logitech_receiver/settings_templates.py:265 msgid "Scroll Wheel Resolution" msgstr "Resolución de la Rueda de Desplazamiento" #: lib/logitech_receiver/settings_templates.py:279 msgid "" "Make scroll wheel send HIRES_WHEEL HID++ notifications (which trigger Solaar " "rules but are otherwise ignored)." msgstr "" "Hacer que la rueda de desplazamiento envíe notificaciones HID++ de " "HIRES_WHEEL (que activan las reglas de Solaar, pero que, por lo demás, se " "ignoran)." #: lib/logitech_receiver/settings_templates.py:288 msgid "Sensitivity (Pointer Speed)" msgstr "Sensibilidad (Velocidad del puntero)" #: lib/logitech_receiver/settings_templates.py:289 msgid "Speed multiplier for mouse (256 is normal multiplier)." msgstr "" "Multiplicador de velocidad para el ratón (256 es un multiplicador normal)" #: lib/logitech_receiver/settings_templates.py:299 msgid "Thumb Wheel Diversion" msgstr "Desvío de la Rueda del Pulgar" #: lib/logitech_receiver/settings_templates.py:301 msgid "" "Make thumb wheel send THUMB_WHEEL HID++ notifications (which trigger Solaar " "rules but are otherwise ignored)." msgstr "" "Hacer que la rueda del pulgar envíe notificaciones THUMB_WHEEL HID++ (que " "desencadenan reglas de Solaar, pero que, por lo demás, se ignoran)." #: lib/logitech_receiver/settings_templates.py:310 msgid "Thumb Wheel Direction" msgstr "Dirección de la Rueda del Pulgar" #: lib/logitech_receiver/settings_templates.py:311 msgid "Invert thumb wheel scroll direction." msgstr "Invertir la dirección de desplazamiento de la rueda del pulgar." #: lib/logitech_receiver/settings_templates.py:319 msgid "Onboard Profiles" msgstr "Perfiles integrados" #: lib/logitech_receiver/settings_templates.py:320 msgid "" "Enable onboard profiles, which often control report rate and keyboard " "lighting" msgstr "" "Habilite los perfiles integrados, que a menudo controlan la tasa de informes " "y iluminación del teclado" #: lib/logitech_receiver/settings_templates.py:330 msgid "Polling Rate (ms)" msgstr "Tasa de Sondeo (ms)" #: lib/logitech_receiver/settings_templates.py:332 msgid "Frequency of device polling, in milliseconds" msgstr "Frecuencia de sondeo del dispositivo, en milisegundos" #: lib/logitech_receiver/settings_templates.py:333 #: lib/logitech_receiver/settings_templates.py:1046 #: lib/logitech_receiver/settings_templates.py:1076 msgid "May need Onboard Profiles set to Disable to be effective." msgstr "" "Es posible que los Perfiles integrados se desactiven para que sean efectivos." #: lib/logitech_receiver/settings_templates.py:365 msgid "Divert crown events" msgstr "Desviar eventos de la corona" #: lib/logitech_receiver/settings_templates.py:366 msgid "" "Make crown send CROWN HID++ notifications (which trigger Solaar rules but " "are otherwise ignored)." msgstr "" "Hacer que la corona envíe notificaciones de CROWN HID ++ (que activan las " "reglas de Solaar, pero que, por lo demás, se ignoran)." #: lib/logitech_receiver/settings_templates.py:374 msgid "Crown smooth scroll" msgstr "Desplazamiento suave de la corona" #: lib/logitech_receiver/settings_templates.py:375 msgid "Set crown smooth scroll" msgstr "Establecer desplazamiento suave de la corona" #: lib/logitech_receiver/settings_templates.py:383 msgid "Divert G Keys" msgstr "Desviar Teclas G" #: lib/logitech_receiver/settings_templates.py:385 msgid "" "Make G keys send GKEY HID++ notifications (which trigger Solaar rules but " "are otherwise ignored)." msgstr "" "Hacer que las teclas G envíen notificaciones GKEY HID ++ (que activan las " "reglas de Solaar, pero que, por lo demás, se ignoran)." #: lib/logitech_receiver/settings_templates.py:386 msgid "May also make M keys and MR key send HID++ notifications" msgstr "También puede hacer que las teclas M y MR envíen notificaciones HID++" #: lib/logitech_receiver/settings_templates.py:402 msgid "Scroll Wheel Ratcheted" msgstr "Rueda de desplazamiento con carraca" #: lib/logitech_receiver/settings_templates.py:403 msgid "" "Switch the mouse wheel between speed-controlled ratcheting and always " "freespin." msgstr "" "Cambie la rueda del ratón entre carraca controlada por velocidad y siempre " "giro libre." #: lib/logitech_receiver/settings_templates.py:405 msgid "Freespinning" msgstr "Giro libre" #: lib/logitech_receiver/settings_templates.py:405 msgid "Ratcheted" msgstr "Carraca" #: lib/logitech_receiver/settings_templates.py:412 msgid "Scroll Wheel Ratchet Speed" msgstr "Rueda de desplazamiento Velocidad de carraca" #: lib/logitech_receiver/settings_templates.py:414 msgid "" "Use the mouse wheel speed to switch between ratcheted and freespinning.\n" "The mouse wheel is always ratcheted at 50." msgstr "" "Utilice la velocidad de la rueda del ratón para cambiar entre carraca y giro " "libre.\n" "La rueda del mouse siempre está ajustada a 50." #: lib/logitech_receiver/settings_templates.py:463 msgid "Key/Button Actions" msgstr "Acciones de Tecla/Botón" #: lib/logitech_receiver/settings_templates.py:465 msgid "Change the action for the key or button." msgstr "Cambiar la acción para la tecla o botón." #: lib/logitech_receiver/settings_templates.py:465 msgid "Overridden by diversion." msgstr "Anulado por la diversión." #: lib/logitech_receiver/settings_templates.py:466 msgid "" "Changing important actions (such as for the left mouse button) can result in " "an unusable system." msgstr "" "Cambiar acciones importantes (por ejemplo el botón izquierdo del ratón) " "puede dejar su sistema inutilizable." #: lib/logitech_receiver/settings_templates.py:639 msgid "Key/Button Diversion" msgstr "Desvio de Tecla/Botón" #: lib/logitech_receiver/settings_templates.py:640 msgid "" "Make the key or button send HID++ notifications (Diverted) or initiate Mouse " "Gestures or Sliding DPI" msgstr "" "Haga que la tecla o el botón envíe notificaciones HID++ (Desviado) o iniciar " "gestos de mouse o DPI deslizante" #: lib/logitech_receiver/settings_templates.py:643 #: lib/logitech_receiver/settings_templates.py:644 #: lib/logitech_receiver/settings_templates.py:645 msgid "Diverted" msgstr "Desviado" #: lib/logitech_receiver/settings_templates.py:643 #: lib/logitech_receiver/settings_templates.py:644 msgid "Mouse Gestures" msgstr "Gestos de Ratón" #: lib/logitech_receiver/settings_templates.py:643 #: lib/logitech_receiver/settings_templates.py:644 #: lib/logitech_receiver/settings_templates.py:645 msgid "Regular" msgstr "Normal" #: lib/logitech_receiver/settings_templates.py:643 msgid "Sliding DPI" msgstr "DPI deslizante" #: lib/logitech_receiver/settings_templates.py:711 msgid "Sensitivity (DPI)" msgstr "Sensibilidad (PPP)" #: lib/logitech_receiver/settings_templates.py:752 msgid "Sensitivity Switching" msgstr "Cambio de sensibilidad" #: lib/logitech_receiver/settings_templates.py:754 msgid "" "Switch the current sensitivity and the remembered sensitivity when the key " "or button is pressed.\n" "If there is no remembered sensitivity, just remember the current sensitivity" msgstr "" "Cambie la sensibilidad actual y la sensibilidad recordada cuando se presiona " "la tecla o el botón.\n" "Si no hay sensibilidad recordada, solo recuerde la corriente sensibilidad" #: lib/logitech_receiver/settings_templates.py:758 msgid "Off" msgstr "Off" #: lib/logitech_receiver/settings_templates.py:791 msgid "Disable keys" msgstr "Desactivar teclas" #: lib/logitech_receiver/settings_templates.py:792 msgid "Disable specific keyboard keys." msgstr "Desactivar teclas específicas del teclado." #: lib/logitech_receiver/settings_templates.py:795 #, python-format msgid "Disables the %s key." msgstr "Desactiva la tecla %s." #: lib/logitech_receiver/settings_templates.py:809 #: lib/logitech_receiver/settings_templates.py:860 msgid "Set OS" msgstr "Especificar SO" #: lib/logitech_receiver/settings_templates.py:810 #: lib/logitech_receiver/settings_templates.py:861 msgid "Change keys to match OS." msgstr "Cambiar teclas para coincidir son el S.O." #: lib/logitech_receiver/settings_templates.py:873 msgid "Change Host" msgstr "Cambiar Equipo" #: lib/logitech_receiver/settings_templates.py:874 msgid "Switch connection to a different host" msgstr "Cambiar la conexión a un equipo diferente" #: lib/logitech_receiver/settings_templates.py:900 msgid "Performs a left click." msgstr "Realiza un clic izquierdo." #: lib/logitech_receiver/settings_templates.py:900 msgid "Single tap" msgstr "Un toque" #: lib/logitech_receiver/settings_templates.py:901 msgid "Performs a right click." msgstr "Realiza un click derecho." #: lib/logitech_receiver/settings_templates.py:901 msgid "Single tap with two fingers" msgstr "Un toque con dos dedos" #: lib/logitech_receiver/settings_templates.py:902 msgid "Single tap with three fingers" msgstr "Un toque con tres dedos" #: lib/logitech_receiver/settings_templates.py:906 msgid "Double tap" msgstr "Doble toque" #: lib/logitech_receiver/settings_templates.py:906 msgid "Performs a double click." msgstr "Realiza un click doble." #: lib/logitech_receiver/settings_templates.py:907 msgid "Double tap with two fingers" msgstr "Doble toque con dos dedos" #: lib/logitech_receiver/settings_templates.py:908 msgid "Double tap with three fingers" msgstr "Doble toque con tres dedos" #: lib/logitech_receiver/settings_templates.py:911 msgid "Drags items by dragging the finger after double tapping." msgstr "Arrastra elementos arrastrando el dedo después de tocar dos veces." #: lib/logitech_receiver/settings_templates.py:911 msgid "Tap and drag" msgstr "Tocar y arrastrar" #: lib/logitech_receiver/settings_templates.py:913 msgid "Drags items by dragging the fingers after double tapping." msgstr "Arrastra elementos arrastrando los dedos después de tocar dos veces." #: lib/logitech_receiver/settings_templates.py:913 msgid "Tap and drag with two fingers" msgstr "Tocar y arrastrar con dos dedos" #: lib/logitech_receiver/settings_templates.py:914 msgid "Tap and drag with three fingers" msgstr "Tocar y arrastrar con tres dedos" #: lib/logitech_receiver/settings_templates.py:917 msgid "Disables tap and edge gestures (equivalent to pressing Fn+LeftClick)." msgstr "" "Desactiva los gestos de toque y borde (equivalente a pulsar " "Fn+ClicIzquierdo)." #: lib/logitech_receiver/settings_templates.py:917 msgid "Suppress tap and edge gestures" msgstr "Elimina los gestos de toque y borde" #: lib/logitech_receiver/settings_templates.py:918 msgid "Scroll with one finger" msgstr "Desplaza con un dedo" #: lib/logitech_receiver/settings_templates.py:918 #: lib/logitech_receiver/settings_templates.py:919 #: lib/logitech_receiver/settings_templates.py:922 msgid "Scrolls." msgstr "Desplazamiento." #: lib/logitech_receiver/settings_templates.py:919 #: lib/logitech_receiver/settings_templates.py:922 msgid "Scroll with two fingers" msgstr "Desplaza con dos dedos" #: lib/logitech_receiver/settings_templates.py:920 msgid "Scroll horizontally with two fingers" msgstr "Desplaza horizontalmente con dos dedos" #: lib/logitech_receiver/settings_templates.py:920 msgid "Scrolls horizontally." msgstr "Desplaza horizontalmente." #: lib/logitech_receiver/settings_templates.py:921 msgid "Scroll vertically with two fingers" msgstr "Desplazar verticalmente con dos dedos" #: lib/logitech_receiver/settings_templates.py:921 msgid "Scrolls vertically." msgstr "Desplaza verticalmente." #: lib/logitech_receiver/settings_templates.py:923 msgid "Inverts the scrolling direction." msgstr "Invierte la dirección de desplazamiento." #: lib/logitech_receiver/settings_templates.py:923 msgid "Natural scrolling" msgstr "Desplazamiento natural" #: lib/logitech_receiver/settings_templates.py:924 msgid "Enables the thumbwheel." msgstr "Activa la rueda del pulgar." #: lib/logitech_receiver/settings_templates.py:924 msgid "Thumbwheel" msgstr "Rueda del pulgar" #: lib/logitech_receiver/settings_templates.py:935 #: lib/logitech_receiver/settings_templates.py:939 msgid "Swipe from the top edge" msgstr "Deslizar desde el borde superior" #: lib/logitech_receiver/settings_templates.py:936 msgid "Swipe from the left edge" msgstr "Deslizar desde el borde izquierdo" #: lib/logitech_receiver/settings_templates.py:937 msgid "Swipe from the right edge" msgstr "Deslizar desde el borde derecho" #: lib/logitech_receiver/settings_templates.py:938 msgid "Swipe from the bottom edge" msgstr "Deslizar desde el borde inferior" #: lib/logitech_receiver/settings_templates.py:940 msgid "Swipe two fingers from the left edge" msgstr "Deslizar dos dedos desde el borde izquierdo" #: lib/logitech_receiver/settings_templates.py:941 msgid "Swipe two fingers from the right edge" msgstr "Deslizar dos dedos desde el borde derecho" #: lib/logitech_receiver/settings_templates.py:942 msgid "Swipe two fingers from the bottom edge" msgstr "Deslizar dos dedos desde el borde inferior" #: lib/logitech_receiver/settings_templates.py:943 msgid "Swipe two fingers from the top edge" msgstr "Deslizar dos dedos desde el borde superior" #: lib/logitech_receiver/settings_templates.py:944 #: lib/logitech_receiver/settings_templates.py:948 msgid "Pinch to zoom out; spread to zoom in." msgstr "Pellizcar para alejar; Extender para acercar." #: lib/logitech_receiver/settings_templates.py:944 msgid "Zoom with two fingers." msgstr "Zoom con dos dedos." #: lib/logitech_receiver/settings_templates.py:945 msgid "Pinch to zoom out." msgstr "Pellizcar para alejar." #: lib/logitech_receiver/settings_templates.py:946 msgid "Spread to zoom in." msgstr "Extender para acercar." #: lib/logitech_receiver/settings_templates.py:947 msgid "Zoom with three fingers." msgstr "Zoom con tres dedos." #: lib/logitech_receiver/settings_templates.py:948 msgid "Zoom with two fingers" msgstr "Zoom con dos dedos" #: lib/logitech_receiver/settings_templates.py:966 msgid "Pixel zone" msgstr "Zona de píxeles" #: lib/logitech_receiver/settings_templates.py:967 msgid "Ratio zone" msgstr "Zona de relación" #: lib/logitech_receiver/settings_templates.py:968 msgid "Scale factor" msgstr "Factor de escalado" #: lib/logitech_receiver/settings_templates.py:968 msgid "Sets the cursor speed." msgstr "Establece la velocidad del cursor." #: lib/logitech_receiver/settings_templates.py:972 msgid "Left" msgstr "Izquierda" #: lib/logitech_receiver/settings_templates.py:972 msgid "Left-most coordinate." msgstr "Coordenada más a la izquierda." #: lib/logitech_receiver/settings_templates.py:973 msgid "Bottom" msgstr "Abajo" #: lib/logitech_receiver/settings_templates.py:973 msgid "Bottom coordinate." msgstr "Coordenada inferior." #: lib/logitech_receiver/settings_templates.py:974 msgid "Width" msgstr "Anchura" #: lib/logitech_receiver/settings_templates.py:974 msgid "Width." msgstr "Anchura." #: lib/logitech_receiver/settings_templates.py:975 msgid "Height" msgstr "Altura" #: lib/logitech_receiver/settings_templates.py:975 msgid "Height." msgstr "Altura." #: lib/logitech_receiver/settings_templates.py:976 msgid "Cursor speed." msgstr "Velocidad del cursor." #: lib/logitech_receiver/settings_templates.py:976 msgid "Scale" msgstr "Escala" #: lib/logitech_receiver/settings_templates.py:982 msgid "Gestures" msgstr "Gestos" #: lib/logitech_receiver/settings_templates.py:983 msgid "Tweak the mouse/touchpad behaviour." msgstr "Modificar el comportamiento del mouse/panel táctil." #: lib/logitech_receiver/settings_templates.py:1000 msgid "Gestures Diversion" msgstr "Desvío de gestos" #: lib/logitech_receiver/settings_templates.py:1001 msgid "Divert mouse/touchpad gestures." msgstr "Desviar los gestos del mouse/panel táctil." #: lib/logitech_receiver/settings_templates.py:1018 msgid "Gesture params" msgstr "Parámetros de gestos" #: lib/logitech_receiver/settings_templates.py:1019 msgid "Change numerical parameters of a mouse/touchpad." msgstr "Cambiar parámetros numéricos de un mouse/panel táctil." #: lib/logitech_receiver/settings_templates.py:1044 msgid "M-Key LEDs" msgstr "LEDs de teclas M" #: lib/logitech_receiver/settings_templates.py:1046 msgid "Control the M-Key LEDs." msgstr "Control de los LEDs de teclas M." #: lib/logitech_receiver/settings_templates.py:1047 #: lib/logitech_receiver/settings_templates.py:1077 msgid "May need G Keys diverted to be effective." msgstr "Puede necesitar desviar las llaves G para ser efectiva." #: lib/logitech_receiver/settings_templates.py:1053 #, python-format msgid "Lights up the %s key." msgstr "Se enciende la tecla %s." #: lib/logitech_receiver/settings_templates.py:1074 msgid "MR-Key LED" msgstr "MR-Key LED" #: lib/logitech_receiver/settings_templates.py:1076 msgid "Control the MR-Key LED." msgstr "Controle el LED MR-Key." #: lib/logitech_receiver/settings_templates.py:1095 msgid "Persistent Key/Button Mapping" msgstr "Asignación persistente de teclas/botones" #: lib/logitech_receiver/settings_templates.py:1097 msgid "Permanently change the mapping for the key or button." msgstr "Cambie permanentemente la asignación de la tecla o el botón." #: lib/logitech_receiver/settings_templates.py:1098 msgid "" "Changing important keys or buttons (such as for the left mouse button) can " "result in an unusable system." msgstr "" "Cambiar teclas o botones importantes (como el botón izquierdo del ratón) " "puede resultar en un sistema inutilizable." #: lib/logitech_receiver/settings_templates.py:1157 msgid "Sidetone" msgstr "Tono lateral" #: lib/logitech_receiver/settings_templates.py:1158 msgid "Set sidetone level." msgstr "Establezca el nivel de tono lateral." #: lib/logitech_receiver/settings_templates.py:1167 msgid "Equalizer" msgstr "Ecualizador" #: lib/logitech_receiver/settings_templates.py:1168 msgid "Set equalizer levels." msgstr "Establecer niveles del ecualizador." #: lib/logitech_receiver/settings_templates.py:1191 msgid "Hz" msgstr "Hz" #: lib/logitech_receiver/settings_templates.py:1197 msgid "Power Management" msgstr "Gestión de energía" #: lib/logitech_receiver/settings_templates.py:1198 msgid "Power off in minutes (0 for never)." msgstr "Apagar a los minutos (0 para nunca)" #: lib/logitech_receiver/status.py:114 msgid "No paired devices." msgstr "Ningún dispositivo conectado." #: lib/logitech_receiver/status.py:115 lib/solaar/ui/window.py:622 #, python-format msgid "%(count)s paired device." msgid_plural "%(count)s paired devices." msgstr[0] "%(count)s dispositivo vinculado." msgstr[1] "%(count)s dispositivos vinculados." #: lib/logitech_receiver/status.py:170 #, python-format msgid "Battery: %(level)s" msgstr "Batería: %(level)s" #: lib/logitech_receiver/status.py:172 #, python-format msgid "Battery: %(percent)d%%" msgstr "Batería: %(percent)d%%" #: lib/logitech_receiver/status.py:184 #, python-format msgid "Lighting: %(level)s lux" msgstr "Iluminación: %(level)s lux" #: lib/logitech_receiver/status.py:239 #, python-format msgid "Battery: %(level)s (%(status)s)" msgstr "Batería: %(level)s (%(status)s)" #: lib/logitech_receiver/status.py:241 #, python-format msgid "Battery: %(percent)d%% (%(status)s)" msgstr "Batería: %(percent)d%% (%(status)s)" #: lib/solaar/ui/__init__.py:52 msgid "Permissions error" msgstr "Error de permisos" #: lib/solaar/ui/__init__.py:54 #, python-format msgid "" "Found a Logitech receiver or device (%s), but did not have permission to " "open it." msgstr "" "Se ha encontrado un receptor Logitech o dispositivo (%s), pero no se dispone " "de permisos para abrirlo." #: lib/solaar/ui/__init__.py:55 msgid "" "If you've just installed Solaar, try disconnecting the receiver or device " "and then reconnecting it." msgstr "" "Si acaba de instalar Solaar, pruebe a desconectar y volver a conectar el " "receptor o dispositivo." #: lib/solaar/ui/__init__.py:58 msgid "Cannot connect to device error" msgstr "Error al conectar al dispositivo" #: lib/solaar/ui/__init__.py:60 #, python-format msgid "" "Found a Logitech receiver or device at %s, but encountered an error " "connecting to it." msgstr "" "Se encontró un receptor o dispositivo Logitech en %s, pero se produjo un " "error al conectarse." #: lib/solaar/ui/__init__.py:61 msgid "" "Try disconnecting the device and then reconnecting it or turning it off and " "then on." msgstr "" "Pruebe a desconectar y volver a conectar el dispositivo o a apagarlo y " "encenderlo de nuevo." #: lib/solaar/ui/__init__.py:64 msgid "Unpairing failed" msgstr "Desvinculación fallida" #: lib/solaar/ui/__init__.py:66 #, python-brace-format msgid "Failed to unpair %{device} from %{receiver}." msgstr "Fallo al desvincular %{device} de %{receiver}." #: lib/solaar/ui/__init__.py:67 msgid "The receiver returned an error, with no further details." msgstr "El receptor devolvió un error, sin detalles adicionales." #: lib/solaar/ui/__init__.py:177 msgid "Another Solaar process is already running so just expose its window" msgstr "" "Ya se está ejecutando otro proceso de Solaar, así que simplemente se muestra " "su ventana" #: lib/solaar/ui/about.py:36 msgid "" "Manages Logitech receivers,\n" "keyboards, mice, and tablets." msgstr "" "Gestiona receptores Logitech,\n" "teclados, ratones y tabletas." #: lib/solaar/ui/about.py:44 msgid "Additional Programming" msgstr "Programación adicional" #: lib/solaar/ui/about.py:45 msgid "GUI design" msgstr "Diseño de la interfaz gráfica" #: lib/solaar/ui/about.py:47 msgid "Testing" msgstr "Prueba" #: lib/solaar/ui/about.py:54 msgid "Logitech documentation" msgstr "Documentación de Logitech" #: lib/solaar/ui/action.py:85 lib/solaar/ui/action.py:89 #: lib/solaar/ui/window.py:197 msgid "Unpair" msgstr "Desvincular" #: lib/solaar/ui/action.py:88 lib/solaar/ui/diversion_rules.py:150 msgid "Cancel" msgstr "Cancelar" #: lib/solaar/ui/config_panel.py:212 msgid "Complete - ENTER to change" msgstr "Completa - ENTER para cambiar" #: lib/solaar/ui/config_panel.py:212 msgid "Incomplete" msgstr "Incompleta" #: lib/solaar/ui/config_panel.py:455 lib/solaar/ui/config_panel.py:507 #, python-format msgid "%d value" msgid_plural "%d values" msgstr[0] "%d valor" msgstr[1] "%d valores" #: lib/solaar/ui/config_panel.py:518 msgid "Changes allowed" msgstr "Cambios permitidos" #: lib/solaar/ui/config_panel.py:519 msgid "No changes allowed" msgstr "Cambios no permitidos" #: lib/solaar/ui/config_panel.py:520 msgid "Ignore this setting" msgstr "Ignorar esta opción" #: lib/solaar/ui/config_panel.py:565 msgid "Working" msgstr "Funcionando" #: lib/solaar/ui/config_panel.py:568 msgid "Read/write operation failed." msgstr "Operación de lectura/escritura fallida." #: lib/solaar/ui/diversion_rules.py:65 msgid "Built-in rules" msgstr "Reglas integradas" #: lib/solaar/ui/diversion_rules.py:65 msgid "User-defined rules" msgstr "Reglas definidas por el usuario" #: lib/solaar/ui/diversion_rules.py:67 lib/solaar/ui/diversion_rules.py:1083 msgid "Rule" msgstr "Regla" #: lib/solaar/ui/diversion_rules.py:68 lib/solaar/ui/diversion_rules.py:509 #: lib/solaar/ui/diversion_rules.py:636 msgid "Sub-rule" msgstr "Subregla" #: lib/solaar/ui/diversion_rules.py:70 msgid "[empty]" msgstr "[vacío]" #: lib/solaar/ui/diversion_rules.py:94 msgid "Solaar Rule Editor" msgstr "Editor de Reglas Solaar" #: lib/solaar/ui/diversion_rules.py:141 msgid "Make changes permanent?" msgstr "¿Hacer los cambios permanentes?" #: lib/solaar/ui/diversion_rules.py:146 msgid "Yes" msgstr "Sí" #: lib/solaar/ui/diversion_rules.py:148 msgid "No" msgstr "No" #: lib/solaar/ui/diversion_rules.py:153 msgid "If you choose No, changes will be lost when Solaar is closed." msgstr "Si elige No, los cambios se perderán cuando se cierre Solaar." #: lib/solaar/ui/diversion_rules.py:201 msgid "Save changes" msgstr "Guardar cambios" #: lib/solaar/ui/diversion_rules.py:206 msgid "Discard changes" msgstr "Descartar cambios" #: lib/solaar/ui/diversion_rules.py:372 msgid "Insert here" msgstr "Insertar aquí" #: lib/solaar/ui/diversion_rules.py:374 msgid "Insert above" msgstr "Insertar encima" #: lib/solaar/ui/diversion_rules.py:376 msgid "Insert below" msgstr "Insertar debajo" #: lib/solaar/ui/diversion_rules.py:382 msgid "Insert new rule here" msgstr "Insertar nueva regla aquí" #: lib/solaar/ui/diversion_rules.py:384 msgid "Insert new rule above" msgstr "Insertar nueva regla encima" #: lib/solaar/ui/diversion_rules.py:386 msgid "Insert new rule below" msgstr "Insertar nueva regla debajo" #: lib/solaar/ui/diversion_rules.py:427 msgid "Paste here" msgstr "Pegar aquí" #: lib/solaar/ui/diversion_rules.py:429 msgid "Paste above" msgstr "Pegar encima" #: lib/solaar/ui/diversion_rules.py:431 msgid "Paste below" msgstr "Pegar debajo" #: lib/solaar/ui/diversion_rules.py:437 msgid "Paste rule here" msgstr "Pegar regla aquí" #: lib/solaar/ui/diversion_rules.py:439 msgid "Paste rule above" msgstr "Pegar regla encima" #: lib/solaar/ui/diversion_rules.py:441 msgid "Paste rule below" msgstr "Pegar regla debajo" #: lib/solaar/ui/diversion_rules.py:445 msgid "Paste rule" msgstr "Pegar regla" #: lib/solaar/ui/diversion_rules.py:474 msgid "Flatten" msgstr "Aplanar" #: lib/solaar/ui/diversion_rules.py:507 msgid "Insert" msgstr "Insertar" #: lib/solaar/ui/diversion_rules.py:510 lib/solaar/ui/diversion_rules.py:638 #: lib/solaar/ui/diversion_rules.py:1126 msgid "Or" msgstr "O" #: lib/solaar/ui/diversion_rules.py:511 lib/solaar/ui/diversion_rules.py:637 #: lib/solaar/ui/diversion_rules.py:1111 msgid "And" msgstr "Y" #: lib/solaar/ui/diversion_rules.py:513 msgid "Condition" msgstr "Condición" #: lib/solaar/ui/diversion_rules.py:515 lib/solaar/ui/diversion_rules.py:1292 msgid "Feature" msgstr "Característica" #: lib/solaar/ui/diversion_rules.py:516 lib/solaar/ui/diversion_rules.py:1328 msgid "Report" msgstr "Informar" #: lib/solaar/ui/diversion_rules.py:517 lib/solaar/ui/diversion_rules.py:1204 msgid "Process" msgstr "Proceso" #: lib/solaar/ui/diversion_rules.py:518 msgid "Mouse process" msgstr "Proceso de ratón" #: lib/solaar/ui/diversion_rules.py:519 lib/solaar/ui/diversion_rules.py:1366 msgid "Modifiers" msgstr "Modificadores" #: lib/solaar/ui/diversion_rules.py:520 lib/solaar/ui/diversion_rules.py:1419 msgid "Key" msgstr "Tecla" #: lib/solaar/ui/diversion_rules.py:521 lib/solaar/ui/diversion_rules.py:1461 msgid "KeyIsDown" msgstr "TeclaPulsada" #: lib/solaar/ui/diversion_rules.py:522 lib/solaar/ui/diversion_rules.py:2260 msgid "Active" msgstr "Activo" #: lib/solaar/ui/diversion_rules.py:523 lib/solaar/ui/diversion_rules.py:2218 #: lib/solaar/ui/diversion_rules.py:2270 lib/solaar/ui/diversion_rules.py:2323 msgid "Device" msgstr "Dispositivo" #: lib/solaar/ui/diversion_rules.py:524 lib/solaar/ui/diversion_rules.py:2297 msgid "Host" msgstr "Equipo" #: lib/solaar/ui/diversion_rules.py:525 lib/solaar/ui/diversion_rules.py:2339 msgid "Setting" msgstr "Ajuste" #: lib/solaar/ui/diversion_rules.py:526 lib/solaar/ui/diversion_rules.py:1477 #: lib/solaar/ui/diversion_rules.py:1526 msgid "Test" msgstr "Prueba" #: lib/solaar/ui/diversion_rules.py:527 lib/solaar/ui/diversion_rules.py:1643 msgid "Test bytes" msgstr "Bytes de prueba" #: lib/solaar/ui/diversion_rules.py:528 lib/solaar/ui/diversion_rules.py:1736 msgid "Mouse Gesture" msgstr "Gesto de Ratón" #: lib/solaar/ui/diversion_rules.py:532 msgid "Action" msgstr "Acción" #: lib/solaar/ui/diversion_rules.py:534 lib/solaar/ui/diversion_rules.py:1845 msgid "Key press" msgstr "Pulsación de tecla" #: lib/solaar/ui/diversion_rules.py:535 lib/solaar/ui/diversion_rules.py:1897 msgid "Mouse scroll" msgstr "Desplazamiento rueda del ratón" #: lib/solaar/ui/diversion_rules.py:536 lib/solaar/ui/diversion_rules.py:1959 msgid "Mouse click" msgstr "Click del ratón" #: lib/solaar/ui/diversion_rules.py:537 msgid "Set" msgstr "Establecer" #: lib/solaar/ui/diversion_rules.py:538 lib/solaar/ui/diversion_rules.py:2030 msgid "Execute" msgstr "Ejecutar" #: lib/solaar/ui/diversion_rules.py:539 lib/solaar/ui/diversion_rules.py:1158 msgid "Later" msgstr "Luego" #: lib/solaar/ui/diversion_rules.py:568 msgid "Insert new rule" msgstr "Añadir nueva regla" #: lib/solaar/ui/diversion_rules.py:588 lib/solaar/ui/diversion_rules.py:1686 #: lib/solaar/ui/diversion_rules.py:1790 lib/solaar/ui/diversion_rules.py:1989 msgid "Delete" msgstr "Borrar" #: lib/solaar/ui/diversion_rules.py:610 msgid "Negate" msgstr "Negar" #: lib/solaar/ui/diversion_rules.py:634 msgid "Wrap with" msgstr "Envolver con" #: lib/solaar/ui/diversion_rules.py:656 msgid "Cut" msgstr "Cortar" #: lib/solaar/ui/diversion_rules.py:671 msgid "Paste" msgstr "Pegar" #: lib/solaar/ui/diversion_rules.py:683 msgid "Copy" msgstr "Copiar" #: lib/solaar/ui/diversion_rules.py:1063 msgid "This editor does not support the selected rule component yet." msgstr "" "Este editor aún no es compatible con el componente de regla seleccionado." #: lib/solaar/ui/diversion_rules.py:1138 msgid "Number of seconds to delay." msgstr "Número de segundos a retrasar." #: lib/solaar/ui/diversion_rules.py:1177 msgid "Not" msgstr "No" #: lib/solaar/ui/diversion_rules.py:1187 msgid "X11 active process. For use in X11 only." msgstr "Proceso activo X11. Solo para uso en X11." #: lib/solaar/ui/diversion_rules.py:1218 msgid "X11 mouse process. For use in X11 only." msgstr "Proceso de ratón X11. Solo para uso en X11." #: lib/solaar/ui/diversion_rules.py:1235 msgid "MouseProcess" msgstr "ProcesoRatón" #: lib/solaar/ui/diversion_rules.py:1260 msgid "Feature name of notification triggering rule processing." msgstr "" "Nombre de la característica del procesamiento de la regla de activación de " "notificaciones." #: lib/solaar/ui/diversion_rules.py:1308 msgid "Report number of notification triggering rule processing." msgstr "" "Número de informe de procesamiento de regla de activación de notificación." #: lib/solaar/ui/diversion_rules.py:1342 msgid "Active keyboard modifiers. Not always available in Wayland." msgstr "Modificadores de teclado activos. No siempre disponible en Wayland." #: lib/solaar/ui/diversion_rules.py:1383 msgid "" "Diverted key or button depressed or released.\n" "Use the Key/Button Diversion and Divert G Keys settings to divert keys and " "buttons." msgstr "" "Tecla desviada o botón pulsado o liberado.\n" "Use Desvío de Tecla/Botón y Desviar de Teclas G para desviar teclas y " "botones." #: lib/solaar/ui/diversion_rules.py:1392 msgid "Key down" msgstr "Tecla abajo" #: lib/solaar/ui/diversion_rules.py:1395 msgid "Key up" msgstr "Tecla arriba" #: lib/solaar/ui/diversion_rules.py:1436 msgid "" "Diverted key or button is currently down.\n" "Use the Key/Button Diversion and Divert G Keys settings to divert keys and " "buttons." msgstr "" "La tecla o botón desviado está actualmente pulsado.\n" "Use los ajustes Desvío de Tecla/Botón y Desviar Teclas G para desviar teclas " "y botones." #: lib/solaar/ui/diversion_rules.py:1475 msgid "Test condition on notification triggering rule processing." msgstr "" "Condición de prueba en el procesamiento de la regla de activación de " "notificación." #: lib/solaar/ui/diversion_rules.py:1479 msgid "Parameter" msgstr "Parámetro" #: lib/solaar/ui/diversion_rules.py:1542 msgid "begin (inclusive)" msgstr "inicio (incluido)" #: lib/solaar/ui/diversion_rules.py:1543 msgid "end (exclusive)" msgstr "fin (excluido)" #: lib/solaar/ui/diversion_rules.py:1552 msgid "range" msgstr "rango" #: lib/solaar/ui/diversion_rules.py:1554 msgid "minimum" msgstr "mínimo" #: lib/solaar/ui/diversion_rules.py:1555 msgid "maximum" msgstr "máximo" #: lib/solaar/ui/diversion_rules.py:1557 #, python-format msgid "bytes %(0)d to %(1)d, ranging from %(2)d to %(3)d" msgstr "bytes %(0)d a %(1)d, que van desde %(2)d a %(3)d" #: lib/solaar/ui/diversion_rules.py:1562 msgid "mask" msgstr "máscara" #: lib/solaar/ui/diversion_rules.py:1563 #, python-format msgid "bytes %(0)d to %(1)d, mask %(2)d" msgstr "bytes %(0)d a %(1)d, máscara %(2)d" #: lib/solaar/ui/diversion_rules.py:1573 msgid "" "Bit or range test on bytes in notification message triggering rule " "processing." msgstr "" "Prueba de bit o rango en bytes en el mensaje de notificación que activa el " "procesamiento de reglas." #: lib/solaar/ui/diversion_rules.py:1583 msgid "type" msgstr "tipo" #: lib/solaar/ui/diversion_rules.py:1666 msgid "" "Mouse gesture with optional initiating button followed by zero or more mouse " "movements." msgstr "" "Gesto del mouse con botón de inicio opcional seguido de cero o más " "movimientos del mouse." #: lib/solaar/ui/diversion_rules.py:1671 msgid "Add movement" msgstr "Añadir movimiento" #: lib/solaar/ui/diversion_rules.py:1764 msgid "" "Simulate a chorded key click or depress or release.\n" "On Wayland requires write access to /dev/uinput." msgstr "" "Simular un clic de teclas combinadas o pulsación o liberación.\n" "En Wayland requiere acceso de escritura a /dev/uinput." #: lib/solaar/ui/diversion_rules.py:1769 msgid "Add key" msgstr "Añadir tecla" #: lib/solaar/ui/diversion_rules.py:1772 msgid "Click" msgstr "Hacer clic" #: lib/solaar/ui/diversion_rules.py:1775 msgid "Depress" msgstr "Pulsar" #: lib/solaar/ui/diversion_rules.py:1778 msgid "Release" msgstr "Liberar" #: lib/solaar/ui/diversion_rules.py:1861 msgid "" "Simulate a mouse scroll.\n" "On Wayland requires write access to /dev/uinput." msgstr "" "Simular un desplazamiento del ratón.\n" "En Wayland requiere acceso de escritura a /dev/uinput." #: lib/solaar/ui/diversion_rules.py:1918 msgid "" "Simulate a mouse click.\n" "On Wayland requires write access to /dev/uinput." msgstr "" "Simular un clic del ratón.\n" "En Wayland requiere acceso de escritura a /dev/uinput." #: lib/solaar/ui/diversion_rules.py:1921 msgid "Button" msgstr "Botón" #: lib/solaar/ui/diversion_rules.py:1922 msgid "Count and Action" msgstr "Pulsaciones y Acción" #: lib/solaar/ui/diversion_rules.py:1972 msgid "Execute a command with arguments." msgstr "Ejecutar un comando con parámetros." #: lib/solaar/ui/diversion_rules.py:1975 msgid "Add argument" msgstr "Añadir parámetro" #: lib/solaar/ui/diversion_rules.py:2050 msgid "Toggle" msgstr "Invertir" #: lib/solaar/ui/diversion_rules.py:2050 msgid "True" msgstr "Verdadero" #: lib/solaar/ui/diversion_rules.py:2051 msgid "False" msgstr "Falso" #: lib/solaar/ui/diversion_rules.py:2065 msgid "Unsupported setting" msgstr "Configuración no admitida" #: lib/solaar/ui/diversion_rules.py:2223 lib/solaar/ui/diversion_rules.py:2242 #: lib/solaar/ui/diversion_rules.py:2328 lib/solaar/ui/diversion_rules.py:2570 #: lib/solaar/ui/diversion_rules.py:2588 msgid "Originating device" msgstr "Dispositivo emisor" #: lib/solaar/ui/diversion_rules.py:2256 msgid "Device is active and its settings can be changed." msgstr "El dispositivo está activo y su configuración se puede cambiar." #: lib/solaar/ui/diversion_rules.py:2266 msgid "Device that originated the current notification." msgstr "Dispositivo que ha causado la notificación actual." #: lib/solaar/ui/diversion_rules.py:2280 msgid "Name of host computer." msgstr "Nombre del equipo." #: lib/solaar/ui/diversion_rules.py:2347 msgid "Value" msgstr "Valor" #: lib/solaar/ui/diversion_rules.py:2355 msgid "Item" msgstr "Artículo" #: lib/solaar/ui/diversion_rules.py:2630 msgid "Change setting on device" msgstr "Cambiar la configuración en el dispositivo" #: lib/solaar/ui/diversion_rules.py:2647 msgid "Setting on device" msgstr "Configuración en el dispositivo" #: lib/solaar/ui/notify.py:124 lib/solaar/ui/tray.py:318 #: lib/solaar/ui/tray.py:323 lib/solaar/ui/window.py:739 msgid "offline" msgstr "desconectado" #: lib/solaar/ui/pair_window.py:122 lib/solaar/ui/pair_window.py:256 #: lib/solaar/ui/pair_window.py:288 #, python-format msgid "%(receiver_name)s: pair new device" msgstr "%(receiver_name)s: vincular nuevo dispositivo" #: lib/solaar/ui/pair_window.py:123 #, python-format msgid "Enter passcode on %(name)s." msgstr "Introduzca el código de acceso en %(name)s." #: lib/solaar/ui/pair_window.py:126 #, python-format msgid "Type %(passcode)s and then press the enter key." msgstr "Escriba %(passcode)s y luego presione la tecla Enter." #: lib/solaar/ui/pair_window.py:129 msgid "left" msgstr "izquierda" #: lib/solaar/ui/pair_window.py:129 msgid "right" msgstr "derecha" #: lib/solaar/ui/pair_window.py:131 #, python-format msgid "" "Press %(code)s\n" "and then press left and right buttons simultaneously." msgstr "" "Pulse %(code)s\n" "y luego los botones izquierdo y derecho simultáneamente." #: lib/solaar/ui/pair_window.py:188 msgid "Pairing failed" msgstr "Vinculación fallida" #: lib/solaar/ui/pair_window.py:190 msgid "Make sure your device is within range, and has a decent battery charge." msgstr "" "Asegúrese de que su dispositivo esté dentro del alcance del receptor y que " "la batería tenga suficiente carga." #: lib/solaar/ui/pair_window.py:192 msgid "A new device was detected, but it is not compatible with this receiver." msgstr "" "Se detectó un nuevo dispositivo, pero no es compatible con este receptor." #: lib/solaar/ui/pair_window.py:194 msgid "More paired devices than receiver can support." msgstr "Más dispositivos emparejados de los que admite el receptor." #: lib/solaar/ui/pair_window.py:196 msgid "No further details are available about the error." msgstr "No hay más detalles disponibles sobre este error." #: lib/solaar/ui/pair_window.py:210 msgid "Found a new device:" msgstr "Se encontró un nuevo dispositivo:" #: lib/solaar/ui/pair_window.py:235 msgid "The wireless link is not encrypted" msgstr "La conexión inalámbrica no está cifrada" #: lib/solaar/ui/pair_window.py:264 msgid "Unifying receivers are only compatible with Unifying devices." msgstr "" "Los receptores Unifying solo son compatibles con dispositivos Unifying." #: lib/solaar/ui/pair_window.py:266 msgid "Bolt receivers are only compatible with Bolt devices." msgstr "Los receptores Bolt solo son compatibles con dispositivos Bolt." #: lib/solaar/ui/pair_window.py:268 msgid "Other receivers are only compatible with a few devices." msgstr "Otros receptores son solo compatibles con unos pocos dispositivos." #: lib/solaar/ui/pair_window.py:270 msgid "The device must not be paired with a nearby powered-on receiver." msgstr "" "El dispositivo no se debe estar emparejado con un receptor cercano encendido." #: lib/solaar/ui/pair_window.py:274 msgid "Press a pairing button or key until the pairing light flashes quickly." msgstr "" "Presione un botón o tecla de emparejamiento hasta que la luz de " "emparejamiento parpadee rápidamente." #: lib/solaar/ui/pair_window.py:276 msgid "You may have to first turn the device off and on again." msgstr "" "Es posible que primero deba apagar el dispositivo y volver a encenderlo." #: lib/solaar/ui/pair_window.py:278 msgid "Turn on the device you want to pair." msgstr "Encienda el dispositivo que desea vincular." #: lib/solaar/ui/pair_window.py:280 msgid "If the device is already turned on, turn it off and on again." msgstr "Si el dispositivo ya está encendido, apague y vuelva a encenderlo." #: lib/solaar/ui/pair_window.py:283 #, python-format msgid "" "\n" "\n" "This receiver has %d pairing remaining." msgid_plural "" "\n" "\n" "This receiver has %d pairings remaining." msgstr[0] "" "\n" "\n" "Este receptor tiene %d emparejamiento restante." msgstr[1] "" "\n" "\n" "Este receptor tiene %d emparejamientos restantes." #: lib/solaar/ui/pair_window.py:286 msgid "" "\n" "Cancelling at this point will not use up a pairing." msgstr "" "\n" "Si cancela en este punto no se usará un vínculo." #: lib/solaar/ui/tray.py:58 msgid "No supported device found" msgstr "No se ha encontrado ningún dispositivo compatible" #: lib/solaar/ui/tray.py:64 lib/solaar/ui/window.py:319 #, python-format msgid "About %s" msgstr "Acerca de %s" #: lib/solaar/ui/tray.py:65 lib/solaar/ui/window.py:317 #, python-format msgid "Quit %s" msgstr "Salir de %s" #: lib/solaar/ui/tray.py:297 lib/solaar/ui/tray.py:305 msgid "no receiver" msgstr "sin receptor" #: lib/solaar/ui/tray.py:321 msgid "no status" msgstr "sin estado" #: lib/solaar/ui/window.py:96 msgid "Scanning" msgstr "Explorando" #: lib/solaar/ui/window.py:129 msgid "Battery" msgstr "Batería" #: lib/solaar/ui/window.py:132 msgid "Wireless Link" msgstr "Enlace inalámbrico" #: lib/solaar/ui/window.py:136 msgid "Lighting" msgstr "Iluminación" #: lib/solaar/ui/window.py:170 msgid "Show Technical Details" msgstr "Mostrar detalles técnicos" #: lib/solaar/ui/window.py:186 msgid "Pair new device" msgstr "Vincular nuevo dispositivo" #: lib/solaar/ui/window.py:205 msgid "Select a device" msgstr "Seleccionar un dispositivo" #: lib/solaar/ui/window.py:322 msgid "Rule Editor" msgstr "Editor de Reglas" #: lib/solaar/ui/window.py:533 msgid "Path" msgstr "Ruta" #: lib/solaar/ui/window.py:536 msgid "USB ID" msgstr "ID USB" #: lib/solaar/ui/window.py:539 lib/solaar/ui/window.py:541 #: lib/solaar/ui/window.py:561 lib/solaar/ui/window.py:563 msgid "Serial" msgstr "Serie" #: lib/solaar/ui/window.py:545 msgid "Index" msgstr "Índice" #: lib/solaar/ui/window.py:547 msgid "Wireless PID" msgstr "PID inalámbrico" #: lib/solaar/ui/window.py:549 msgid "Product ID" msgstr "ID del producto" #: lib/solaar/ui/window.py:551 msgid "Protocol" msgstr "Protocolo" #: lib/solaar/ui/window.py:551 msgid "Unknown" msgstr "Desconocido" #: lib/solaar/ui/window.py:554 #, python-format msgid "%(rate)d ms (%(rate_hz)dHz)" msgstr "%(rate)d ms (%(rate_hz)dHz)" #: lib/solaar/ui/window.py:554 msgid "Polling rate" msgstr "Tasa de sondeo" #: lib/solaar/ui/window.py:565 msgid "Unit ID" msgstr "ID Unidad" #: lib/solaar/ui/window.py:576 msgid "none" msgstr "ninguno" #: lib/solaar/ui/window.py:577 msgid "Notifications" msgstr "Notificaciones" #: lib/solaar/ui/window.py:621 msgid "No device paired." msgstr "No hay dispositivos vinculados." #: lib/solaar/ui/window.py:628 #, python-format msgid "Up to %(max_count)s device can be paired to this receiver." msgid_plural "Up to %(max_count)s devices can be paired to this receiver." msgstr[0] "Se puede vincular hasta %(max_count)s dispositivo a este receptor." msgstr[1] "" "Se pueden vincular hasta %(max_count)s dispositivos a este receptor." #: lib/solaar/ui/window.py:634 msgid "Only one device can be paired to this receiver." msgstr "Solo un dispositivo se puede vincular a este receptor." #: lib/solaar/ui/window.py:638 #, python-format msgid "This receiver has %d pairing remaining." msgid_plural "This receiver has %d pairings remaining." msgstr[0] "Este receptor tiene %d emparejamiento restante." msgstr[1] "Este receptor tiene %d emparejamientos restantes." #: lib/solaar/ui/window.py:692 msgid "Battery Voltage" msgstr "Voltaje de la batería" #: lib/solaar/ui/window.py:694 msgid "Voltage reported by battery" msgstr "Voltaje informado por la batería" #: lib/solaar/ui/window.py:696 msgid "Battery Level" msgstr "Nivel de Batería" #: lib/solaar/ui/window.py:698 msgid "Approximate level reported by battery" msgstr "Nivel aproximado informado por batería" #: lib/solaar/ui/window.py:705 lib/solaar/ui/window.py:707 msgid "next reported " msgstr "siguiente informe " #: lib/solaar/ui/window.py:708 msgid " and next level to be reported." msgstr " y siguiente nivel que será reportado." #: lib/solaar/ui/window.py:713 msgid "last known" msgstr "último conocido" #: lib/solaar/ui/window.py:724 msgid "encrypted" msgstr "cifrado" #: lib/solaar/ui/window.py:726 msgid "The wireless link between this device and its receiver is encrypted." msgstr "" "La conexión inalámbrica entre el dispositivo y su receptor está cifrada." #: lib/solaar/ui/window.py:728 msgid "not encrypted" msgstr "no cifrado" #: lib/solaar/ui/window.py:732 msgid "" "The wireless link between this device and its receiver is not encrypted.\n" "This is a security issue for pointing devices, and a major security issue " "for text-input devices." msgstr "" "El enlace inalámbrico entre este dispositivo y su receptor no está " "encriptado.\n" "Este es un problema de seguridad para los dispositivos señaladores y un " "problema de seguridad importante para los dispositivos de entrada de texto." #: lib/solaar/ui/window.py:748 #, python-format msgid "%(light_level)d lux" msgstr "%(light_level)d lux" #~ msgid "" #~ "\n" #~ "\n" #~ "This receiver has %d pairing(s) remaining." #~ msgstr "" #~ "\n" #~ "\n" #~ "Este receptor tiene %d vínculo(s) disponibles." #~ msgid "%(battery_level)s" #~ msgstr "%(battery_level)s" #~ msgid "%(battery_percent)d%%" #~ msgstr "%(battery_percent)d%%" #~ msgid "Add action" #~ msgstr "Añadir acción" #~ msgid "" #~ "Adjust the DPI by sliding the mouse horizontally while holding the button " #~ "down." #~ msgstr "" #~ "Ajustar los DPI deslizando el ratón horizontalmente mientras se mantiene " #~ "el botón presionado." #~ msgid "" #~ "Automatically switch the mouse wheel between ratchet and freespin mode.\n" #~ "The mouse wheel is always free at 0, and always locked at 50" #~ msgstr "" #~ "Cambia automáticamente el modo de la rueda del ratón entre bloqueado y " #~ "giro libre.\n" #~ "La rueda del ratón siempre está libre a 0 y siempre bloqueado a 50" #~ msgid "" #~ "Automatically switch the mouse wheel between ratchet and freespin mode.\n" #~ "The mouse wheel is always free at 0, and always ratcheted at 50" #~ msgstr "" #~ "Cambiar automáticamente la rueda del mouse entre el modo de trinquete y " #~ "el modo de giro libre.\n" #~ "La rueda del mouse siempre está libre en 0 y siempre con trinquete en 50" #~ msgid "Battery information unknown." #~ msgstr "Información de la batería desconocida." #~ msgid "Count" #~ msgstr "Cuenta" #~ msgid "DPI Sliding Adjustment" #~ msgstr "Ajustar DPI deslizando" #~ msgid "" #~ "Diverted key or button depressed or released.\n" #~ "Use the Key/Button Diversion setting to divert keys and buttons." #~ msgstr "" #~ "Tecla desviada o botón presionado o liberado.\n" #~ "Use la configuración Desvío de teclas/botones para desviar teclas y " #~ "botones." #~ msgid "Effectively turns off thumb scrolling in Linux." #~ msgstr "Desactiva eficazmente el desplazamiento con el pulgar en Linux." #~ msgid "Effectively turns off wheel scrolling in Linux." #~ msgstr "Desactiva eficazmente el desplazamiento de la rueda en Linux." #, python-format #~ msgid "" #~ "Found a Logitech Receiver (%s), but did not have permission to open it." #~ msgstr "" #~ "Se encontró un receptor Logitech (%s), pero no tiene permisos para " #~ "abrirlo." #~ msgid "HID++ Scrolling" #~ msgstr "Desplazamiento HID++" #~ msgid "HID++ mode for horizontal scroll with the thumb wheel." #~ msgstr "Modo HID++ para desplazamientor horizontal con la rueda del pulgar." #~ msgid "HID++ mode for vertical scroll with the wheel." #~ msgstr "Modo HID++ para el desplazamiento vertical con la rueda." #~ msgid "High Resolution Scrolling" #~ msgstr "Desplazamiento de alta resolución" #~ msgid "High Resolution Wheel Invert" #~ msgstr "Invertir rueda en alta resolución " #~ msgid "High-sensitivity wheel invert mode for vertical scroll." #~ msgstr "" #~ "Modo inverso de alta resolución en la rueda para desplazamiento vertical." #~ msgid "If the device is already turned on, turn if off and on again." #~ msgstr "" #~ "Si el dispositivo ya está encendido, apáguelo y vuelva a encenderlo." #~ msgid "" #~ "If you've just installed Solaar, try removing the receiver and plugging " #~ "it back in." #~ msgstr "" #~ "Si acaba de instalar Solaar, intente quitar el receptor y conectarlo de " #~ "nuevo." #~ msgid "" #~ "Make the key or button send HID++ notifications (which trigger Solaar " #~ "rules but are otherwise ignored)." #~ msgstr "" #~ "Haga que la tecla o el botón envíe notificaciones HID ++ (que activan las " #~ "reglas de Solaar, de lo contrario se ignoran)." #~ msgid "No Logitech device found" #~ msgstr "No se encontró ningún dispositivo Logitech" #~ msgid "No Logitech receiver found" #~ msgstr "No se encontró ningún receptor Logitech" #~ msgid "Scroll Wheel Rachet" #~ msgstr "Trinquete Rueda Desplazamiento" #~ msgid "Send a gesture by sliding the mouse while holding the button down." #~ msgstr "" #~ "Envíe un gesto deslizando el ratón mientras mantiene presionado el botón." #~ msgid "" #~ "Shows status of devices connected\n" #~ "through wireless Logitech receivers." #~ msgstr "" #~ "Mostrar el estado de dispositivos conectados\n" #~ "mediante receptores inalámbricos Logitech." #~ msgid "Smooth Scrolling" #~ msgstr "Desplazamiento suave" #~ msgid "The receiver only supports %d paired device(s)." #~ msgstr "El receptor solo soporta %d dispositivo(s) vinculado(s)." #~ msgid "The receiver was unplugged." #~ msgstr "El receptor se desconectó." #~ msgid "" #~ "The wireless link between this device and its receiver is not encrypted.\n" #~ "\n" #~ "For pointing devices (mice, trackballs, trackpads), this is a minor " #~ "security issue.\n" #~ "\n" #~ "It is, however, a major security issue for text-input devices (keyboards, " #~ "numpads),\n" #~ "because typed text can be sniffed inconspicuously by 3rd parties within " #~ "range." #~ msgstr "" #~ "La conexión inalámbrica entre el dispositivo y su receptor no está " #~ "cifrada.\n" #~ "\n" #~ "Para dispositivos apuntadores (ratones, trackballs, trackpads), este es " #~ "un problema menor de seguridad.\n" #~ "\n" #~ "Sin embargo, para dispositivos de entrada de texto (teclados, teclados " #~ "numéricos) sí es un problema grave,\n" #~ "pues el texto introducido puede ser capturado de forma inadvertida por " #~ "terceros que estén cerca." #~ msgid "This receiver has %d pairing(s) remaining." #~ msgstr "Este receptor tiene %d vínculo(s) disponibles." #~ msgid "Top-most coordinate." #~ msgstr "Coordenada superior." #~ msgid "" #~ "Try removing the device and plugging it back in or turning it off and " #~ "then on." #~ msgstr "" #~ "Intente quitar el dispositivo y volver a enchufarlo o apagarlo y " #~ "encenderlo." #~ msgid "USB id" #~ msgstr "id USB" #~ msgid "Wheel Resolution" #~ msgstr "Resolución de la rueda" #~ msgid "height" #~ msgstr "altura" #~ msgid "top" #~ msgstr "superior" #~ msgid "unknown" #~ msgstr "desconocido" #~ msgid "width" #~ msgstr "anchura" Solaar-1.1.14/po/fi.po000066400000000000000000001362231473525213700144130ustar00rootroot00000000000000# Finnish translations for solaar package. # Copyright (C) 2013 THE solaar'S COPYRIGHT HOLDER # This file is distributed under the same license as the solaar package. # Automatically generated, 2013. # msgid "" msgstr "Project-Id-Version: solaar 0.9.2\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-12-28 17:40+0100\n" "PO-Revision-Date: 2013-08-05 18:49+0300\n" "Last-Translator: Tomi Leppänen\n" "Language-Team: none\n" "Language: fi\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: lib/logitech_receiver/base_usb.py:46 msgid "Bolt Receiver" msgstr "" #: lib/logitech_receiver/base_usb.py:57 msgid "Unifying Receiver" msgstr "" #: lib/logitech_receiver/base_usb.py:67 lib/logitech_receiver/base_usb.py:78 #: lib/logitech_receiver/base_usb.py:90 lib/logitech_receiver/base_usb.py:102 #: lib/logitech_receiver/base_usb.py:114 msgid "Nano Receiver" msgstr "" #: lib/logitech_receiver/base_usb.py:124 msgid "Lightspeed Receiver" msgstr "" #: lib/logitech_receiver/base_usb.py:133 msgid "EX100 Receiver 27 Mhz" msgstr "" #: lib/logitech_receiver/i18n.py:30 msgid "empty" msgstr "tyhjä" #: lib/logitech_receiver/i18n.py:31 msgid "critical" msgstr "kriittinen" #: lib/logitech_receiver/i18n.py:32 msgid "low" msgstr "matala" #: lib/logitech_receiver/i18n.py:33 msgid "average" msgstr "" #: lib/logitech_receiver/i18n.py:34 msgid "good" msgstr "hyvä" #: lib/logitech_receiver/i18n.py:35 msgid "full" msgstr "täysi" #: lib/logitech_receiver/i18n.py:38 msgid "discharging" msgstr "tyhjenee" #: lib/logitech_receiver/i18n.py:39 msgid "recharging" msgstr "latautuu" #: lib/logitech_receiver/i18n.py:40 lib/solaar/ui/window.py:711 msgid "charging" msgstr "lataa" #: lib/logitech_receiver/i18n.py:41 msgid "not charging" msgstr "" #: lib/logitech_receiver/i18n.py:42 msgid "almost full" msgstr "lähes täysi" #: lib/logitech_receiver/i18n.py:43 msgid "charged" msgstr "" #: lib/logitech_receiver/i18n.py:44 msgid "slow recharge" msgstr "hidas lataus" #: lib/logitech_receiver/i18n.py:45 #, fuzzy msgid "invalid battery" msgstr "virheellinen akku" #: lib/logitech_receiver/i18n.py:46 msgid "thermal error" msgstr "lämpötilasta johtuva virhe" #: lib/logitech_receiver/i18n.py:47 msgid "error" msgstr "" #: lib/logitech_receiver/i18n.py:48 msgid "standard" msgstr "" #: lib/logitech_receiver/i18n.py:49 msgid "fast" msgstr "" #: lib/logitech_receiver/i18n.py:50 msgid "slow" msgstr "" #: lib/logitech_receiver/i18n.py:53 msgid "device timeout" msgstr "laitteen aikakatkaisu" #: lib/logitech_receiver/i18n.py:54 msgid "device not supported" msgstr "laite ei ole tuettu" #: lib/logitech_receiver/i18n.py:55 msgid "too many devices" msgstr "liian monta laitetta" #: lib/logitech_receiver/i18n.py:56 msgid "sequence timeout" msgstr "sarjan aikakatkaisu" #: lib/logitech_receiver/i18n.py:59 lib/solaar/ui/window.py:572 msgid "Firmware" msgstr "Laiteohjelmisto" #: lib/logitech_receiver/i18n.py:60 msgid "Bootloader" msgstr "Alkulatausohjelma" #: lib/logitech_receiver/i18n.py:61 msgid "Hardware" msgstr "Laitteisto" #: lib/logitech_receiver/i18n.py:62 msgid "Other" msgstr "Muu" #: lib/logitech_receiver/i18n.py:65 msgid "Left Button" msgstr "" #: lib/logitech_receiver/i18n.py:66 msgid "Right Button" msgstr "" #: lib/logitech_receiver/i18n.py:67 msgid "Middle Button" msgstr "" #: lib/logitech_receiver/i18n.py:68 msgid "Back Button" msgstr "" #: lib/logitech_receiver/i18n.py:69 msgid "Forward Button" msgstr "" #: lib/logitech_receiver/i18n.py:70 msgid "Mouse Gesture Button" msgstr "" #: lib/logitech_receiver/i18n.py:71 msgid "Smart Shift" msgstr "" #: lib/logitech_receiver/i18n.py:72 msgid "DPI Switch" msgstr "" #: lib/logitech_receiver/i18n.py:73 msgid "Left Tilt" msgstr "" #: lib/logitech_receiver/i18n.py:74 msgid "Right Tilt" msgstr "" #: lib/logitech_receiver/i18n.py:75 msgid "Left Click" msgstr "" #: lib/logitech_receiver/i18n.py:76 msgid "Right Click" msgstr "" #: lib/logitech_receiver/i18n.py:77 msgid "Mouse Middle Button" msgstr "" #: lib/logitech_receiver/i18n.py:78 msgid "Mouse Back Button" msgstr "" #: lib/logitech_receiver/i18n.py:79 msgid "Mouse Forward Button" msgstr "" #: lib/logitech_receiver/i18n.py:80 msgid "Gesture Button Navigation" msgstr "" #: lib/logitech_receiver/i18n.py:81 msgid "Mouse Scroll Left Button" msgstr "" #: lib/logitech_receiver/i18n.py:82 msgid "Mouse Scroll Right Button" msgstr "" #: lib/logitech_receiver/i18n.py:85 msgid "pressed" msgstr "" #: lib/logitech_receiver/i18n.py:86 msgid "released" msgstr "" #: lib/logitech_receiver/notifications.py:75 #: lib/logitech_receiver/notifications.py:126 msgid "pairing lock is closed" msgstr "" #: lib/logitech_receiver/notifications.py:75 #: lib/logitech_receiver/notifications.py:126 msgid "pairing lock is open" msgstr "" #: lib/logitech_receiver/notifications.py:92 msgid "discovery lock is closed" msgstr "" #: lib/logitech_receiver/notifications.py:92 msgid "discovery lock is open" msgstr "" #: lib/logitech_receiver/notifications.py:224 lib/solaar/ui/notify.py:122 msgid "connected" msgstr "yhdistetty" #: lib/logitech_receiver/notifications.py:224 msgid "disconnected" msgstr "" #: lib/logitech_receiver/notifications.py:262 lib/solaar/ui/notify.py:120 msgid "unpaired" msgstr "parittamaton" #: lib/logitech_receiver/notifications.py:304 msgid "powered on" msgstr "virta päällä" #: lib/logitech_receiver/settings.py:750 msgid "register" msgstr "" #: lib/logitech_receiver/settings.py:764 lib/logitech_receiver/settings.py:791 msgid "feature" msgstr "" #: lib/logitech_receiver/settings_templates.py:139 msgid "Swap Fx function" msgstr "Vaihda Fx" #: lib/logitech_receiver/settings_templates.py:140 msgid "When set, the F1..F12 keys will activate their special function,\n" "and you must hold the FN key to activate their standard function." msgstr "Asetettu: F1..F12 näppäimet aktivoivat erityistoimintonsa ja \n" "FN-näppäintä on painettava, jotta ne aktivoisivat normaalin\n" "toimintonsa." #: lib/logitech_receiver/settings_templates.py:142 msgid "When unset, the F1..F12 keys will activate their standard function,\n" "and you must hold the FN key to activate their special function." msgstr "Ei asetettu: F1..F12 näppäimet aktivoivat tavallisen toimintonsa\n" "ja FN-näppäintä on painettava, jotta ne aktivoisivat\n" "erityistoimintonsa." #: lib/logitech_receiver/settings_templates.py:149 msgid "Hand Detection" msgstr "Käsien tunnistus" #: lib/logitech_receiver/settings_templates.py:150 msgid "Turn on illumination when the hands hover over the keyboard." msgstr "Käynnistä valaistus, kun kädet ovat näppämistön yläpuolella." #: lib/logitech_receiver/settings_templates.py:157 msgid "Scroll Wheel Smooth Scrolling" msgstr "" #: lib/logitech_receiver/settings_templates.py:158 #: lib/logitech_receiver/settings_templates.py:239 #: lib/logitech_receiver/settings_templates.py:267 msgid "High-sensitivity mode for vertical scroll with the wheel." msgstr "Korkean herkkyyden tila pystyvieritykseen hiiren rullalla." #: lib/logitech_receiver/settings_templates.py:165 msgid "Side Scrolling" msgstr "Sivuttaisvieritys" #: lib/logitech_receiver/settings_templates.py:167 msgid "When disabled, pushing the wheel sideways sends custom button " "events\n" "instead of the standard side-scrolling events." msgstr "Pois päältä ollessaan, rullan painaminen sivuttain lähettää " "muokattuja\n" "toimintoja normaalien sivuttaisvieritystoimintojen sijaan." #: lib/logitech_receiver/settings_templates.py:177 msgid "Sensitivity (DPI - older mice)" msgstr "" #: lib/logitech_receiver/settings_templates.py:178 #: lib/logitech_receiver/settings_templates.py:712 msgid "Mouse movement sensitivity" msgstr "" #: lib/logitech_receiver/settings_templates.py:208 #: lib/logitech_receiver/settings_templates.py:218 #: lib/logitech_receiver/settings_templates.py:225 msgid "Backlight" msgstr "" #: lib/logitech_receiver/settings_templates.py:209 #: lib/logitech_receiver/settings_templates.py:226 msgid "Set illumination time for keyboard." msgstr "" #: lib/logitech_receiver/settings_templates.py:219 msgid "Turn illumination on or off on keyboard." msgstr "" #: lib/logitech_receiver/settings_templates.py:237 msgid "Scroll Wheel High Resolution" msgstr "" #: lib/logitech_receiver/settings_templates.py:240 #: lib/logitech_receiver/settings_templates.py:268 msgid "Set to ignore if scrolling is abnormally fast or slow" msgstr "" #: lib/logitech_receiver/settings_templates.py:247 #: lib/logitech_receiver/settings_templates.py:277 msgid "Scroll Wheel Diversion" msgstr "" #: lib/logitech_receiver/settings_templates.py:249 msgid "Make scroll wheel send LOWRES_WHEEL HID++ notifications (which " "trigger Solaar rules but are otherwise ignored)." msgstr "" #: lib/logitech_receiver/settings_templates.py:256 msgid "Scroll Wheel Direction" msgstr "" #: lib/logitech_receiver/settings_templates.py:257 msgid "Invert direction for vertical scroll with wheel." msgstr "" #: lib/logitech_receiver/settings_templates.py:265 msgid "Scroll Wheel Resolution" msgstr "" #: lib/logitech_receiver/settings_templates.py:279 msgid "Make scroll wheel send HIRES_WHEEL HID++ notifications (which " "trigger Solaar rules but are otherwise ignored)." msgstr "" #: lib/logitech_receiver/settings_templates.py:288 msgid "Sensitivity (Pointer Speed)" msgstr "" #: lib/logitech_receiver/settings_templates.py:289 msgid "Speed multiplier for mouse (256 is normal multiplier)." msgstr "" #: lib/logitech_receiver/settings_templates.py:299 msgid "Thumb Wheel Diversion" msgstr "" #: lib/logitech_receiver/settings_templates.py:301 msgid "Make thumb wheel send THUMB_WHEEL HID++ notifications (which trigger " "Solaar rules but are otherwise ignored)." msgstr "" #: lib/logitech_receiver/settings_templates.py:310 msgid "Thumb Wheel Direction" msgstr "" #: lib/logitech_receiver/settings_templates.py:311 msgid "Invert thumb wheel scroll direction." msgstr "" #: lib/logitech_receiver/settings_templates.py:319 msgid "Onboard Profiles" msgstr "" #: lib/logitech_receiver/settings_templates.py:320 msgid "Enable onboard profiles, which often control report rate and " "keyboard lighting" msgstr "" #: lib/logitech_receiver/settings_templates.py:330 msgid "Polling Rate (ms)" msgstr "" #: lib/logitech_receiver/settings_templates.py:332 msgid "Frequency of device polling, in milliseconds" msgstr "" #: lib/logitech_receiver/settings_templates.py:333 #: lib/logitech_receiver/settings_templates.py:1046 #: lib/logitech_receiver/settings_templates.py:1076 msgid "May need Onboard Profiles set to Disable to be effective." msgstr "" #: lib/logitech_receiver/settings_templates.py:365 msgid "Divert crown events" msgstr "" #: lib/logitech_receiver/settings_templates.py:366 msgid "Make crown send CROWN HID++ notifications (which trigger Solaar " "rules but are otherwise ignored)." msgstr "" #: lib/logitech_receiver/settings_templates.py:374 msgid "Crown smooth scroll" msgstr "" #: lib/logitech_receiver/settings_templates.py:375 msgid "Set crown smooth scroll" msgstr "" #: lib/logitech_receiver/settings_templates.py:383 msgid "Divert G Keys" msgstr "" #: lib/logitech_receiver/settings_templates.py:385 msgid "Make G keys send GKEY HID++ notifications (which trigger Solaar " "rules but are otherwise ignored)." msgstr "" #: lib/logitech_receiver/settings_templates.py:386 msgid "May also make M keys and MR key send HID++ notifications" msgstr "" #: lib/logitech_receiver/settings_templates.py:402 msgid "Scroll Wheel Ratcheted" msgstr "" #: lib/logitech_receiver/settings_templates.py:403 msgid "Switch the mouse wheel between speed-controlled ratcheting and " "always freespin." msgstr "" #: lib/logitech_receiver/settings_templates.py:405 msgid "Freespinning" msgstr "" #: lib/logitech_receiver/settings_templates.py:405 msgid "Ratcheted" msgstr "" #: lib/logitech_receiver/settings_templates.py:412 msgid "Scroll Wheel Ratchet Speed" msgstr "" #: lib/logitech_receiver/settings_templates.py:414 msgid "Use the mouse wheel speed to switch between ratcheted and " "freespinning.\n" "The mouse wheel is always ratcheted at 50." msgstr "" #: lib/logitech_receiver/settings_templates.py:463 msgid "Key/Button Actions" msgstr "" #: lib/logitech_receiver/settings_templates.py:465 msgid "Change the action for the key or button." msgstr "" #: lib/logitech_receiver/settings_templates.py:465 msgid "Overridden by diversion." msgstr "" #: lib/logitech_receiver/settings_templates.py:466 msgid "Changing important actions (such as for the left mouse button) can " "result in an unusable system." msgstr "" #: lib/logitech_receiver/settings_templates.py:639 msgid "Key/Button Diversion" msgstr "" #: lib/logitech_receiver/settings_templates.py:640 msgid "Make the key or button send HID++ notifications (Diverted) or " "initiate Mouse Gestures or Sliding DPI" msgstr "" #: lib/logitech_receiver/settings_templates.py:643 #: lib/logitech_receiver/settings_templates.py:644 #: lib/logitech_receiver/settings_templates.py:645 msgid "Diverted" msgstr "" #: lib/logitech_receiver/settings_templates.py:643 #: lib/logitech_receiver/settings_templates.py:644 msgid "Mouse Gestures" msgstr "" #: lib/logitech_receiver/settings_templates.py:643 #: lib/logitech_receiver/settings_templates.py:644 #: lib/logitech_receiver/settings_templates.py:645 msgid "Regular" msgstr "" #: lib/logitech_receiver/settings_templates.py:643 msgid "Sliding DPI" msgstr "" #: lib/logitech_receiver/settings_templates.py:711 msgid "Sensitivity (DPI)" msgstr "Herkkyys (DPI)" #: lib/logitech_receiver/settings_templates.py:752 msgid "Sensitivity Switching" msgstr "" #: lib/logitech_receiver/settings_templates.py:754 msgid "Switch the current sensitivity and the remembered sensitivity when " "the key or button is pressed.\n" "If there is no remembered sensitivity, just remember the current " "sensitivity" msgstr "" #: lib/logitech_receiver/settings_templates.py:758 msgid "Off" msgstr "" #: lib/logitech_receiver/settings_templates.py:791 msgid "Disable keys" msgstr "" #: lib/logitech_receiver/settings_templates.py:792 msgid "Disable specific keyboard keys." msgstr "" #: lib/logitech_receiver/settings_templates.py:795 #, python-format msgid "Disables the %s key." msgstr "" #: lib/logitech_receiver/settings_templates.py:809 #: lib/logitech_receiver/settings_templates.py:860 msgid "Set OS" msgstr "" #: lib/logitech_receiver/settings_templates.py:810 #: lib/logitech_receiver/settings_templates.py:861 msgid "Change keys to match OS." msgstr "" #: lib/logitech_receiver/settings_templates.py:873 msgid "Change Host" msgstr "" #: lib/logitech_receiver/settings_templates.py:874 msgid "Switch connection to a different host" msgstr "" #: lib/logitech_receiver/settings_templates.py:900 msgid "Performs a left click." msgstr "" #: lib/logitech_receiver/settings_templates.py:900 msgid "Single tap" msgstr "" #: lib/logitech_receiver/settings_templates.py:901 msgid "Performs a right click." msgstr "" #: lib/logitech_receiver/settings_templates.py:901 msgid "Single tap with two fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:902 msgid "Single tap with three fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:906 msgid "Double tap" msgstr "" #: lib/logitech_receiver/settings_templates.py:906 msgid "Performs a double click." msgstr "" #: lib/logitech_receiver/settings_templates.py:907 msgid "Double tap with two fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:908 msgid "Double tap with three fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:911 msgid "Drags items by dragging the finger after double tapping." msgstr "" #: lib/logitech_receiver/settings_templates.py:911 msgid "Tap and drag" msgstr "" #: lib/logitech_receiver/settings_templates.py:913 msgid "Drags items by dragging the fingers after double tapping." msgstr "" #: lib/logitech_receiver/settings_templates.py:913 msgid "Tap and drag with two fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:914 msgid "Tap and drag with three fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:917 msgid "Disables tap and edge gestures (equivalent to pressing Fn+LeftClick)." msgstr "" #: lib/logitech_receiver/settings_templates.py:917 msgid "Suppress tap and edge gestures" msgstr "" #: lib/logitech_receiver/settings_templates.py:918 msgid "Scroll with one finger" msgstr "" #: lib/logitech_receiver/settings_templates.py:918 #: lib/logitech_receiver/settings_templates.py:919 #: lib/logitech_receiver/settings_templates.py:922 msgid "Scrolls." msgstr "" #: lib/logitech_receiver/settings_templates.py:919 #: lib/logitech_receiver/settings_templates.py:922 msgid "Scroll with two fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:920 msgid "Scroll horizontally with two fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:920 msgid "Scrolls horizontally." msgstr "" #: lib/logitech_receiver/settings_templates.py:921 msgid "Scroll vertically with two fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:921 msgid "Scrolls vertically." msgstr "" #: lib/logitech_receiver/settings_templates.py:923 msgid "Inverts the scrolling direction." msgstr "" #: lib/logitech_receiver/settings_templates.py:923 msgid "Natural scrolling" msgstr "" #: lib/logitech_receiver/settings_templates.py:924 msgid "Enables the thumbwheel." msgstr "" #: lib/logitech_receiver/settings_templates.py:924 msgid "Thumbwheel" msgstr "" #: lib/logitech_receiver/settings_templates.py:935 #: lib/logitech_receiver/settings_templates.py:939 msgid "Swipe from the top edge" msgstr "" #: lib/logitech_receiver/settings_templates.py:936 msgid "Swipe from the left edge" msgstr "" #: lib/logitech_receiver/settings_templates.py:937 msgid "Swipe from the right edge" msgstr "" #: lib/logitech_receiver/settings_templates.py:938 msgid "Swipe from the bottom edge" msgstr "" #: lib/logitech_receiver/settings_templates.py:940 msgid "Swipe two fingers from the left edge" msgstr "" #: lib/logitech_receiver/settings_templates.py:941 msgid "Swipe two fingers from the right edge" msgstr "" #: lib/logitech_receiver/settings_templates.py:942 msgid "Swipe two fingers from the bottom edge" msgstr "" #: lib/logitech_receiver/settings_templates.py:943 msgid "Swipe two fingers from the top edge" msgstr "" #: lib/logitech_receiver/settings_templates.py:944 #: lib/logitech_receiver/settings_templates.py:948 msgid "Pinch to zoom out; spread to zoom in." msgstr "" #: lib/logitech_receiver/settings_templates.py:944 msgid "Zoom with two fingers." msgstr "" #: lib/logitech_receiver/settings_templates.py:945 msgid "Pinch to zoom out." msgstr "" #: lib/logitech_receiver/settings_templates.py:946 msgid "Spread to zoom in." msgstr "" #: lib/logitech_receiver/settings_templates.py:947 msgid "Zoom with three fingers." msgstr "" #: lib/logitech_receiver/settings_templates.py:948 msgid "Zoom with two fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:966 msgid "Pixel zone" msgstr "" #: lib/logitech_receiver/settings_templates.py:967 msgid "Ratio zone" msgstr "" #: lib/logitech_receiver/settings_templates.py:968 msgid "Scale factor" msgstr "Skaalauskerroin" #: lib/logitech_receiver/settings_templates.py:968 msgid "Sets the cursor speed." msgstr "" #: lib/logitech_receiver/settings_templates.py:972 msgid "Left" msgstr "" #: lib/logitech_receiver/settings_templates.py:972 msgid "Left-most coordinate." msgstr "" #: lib/logitech_receiver/settings_templates.py:973 msgid "Bottom" msgstr "" #: lib/logitech_receiver/settings_templates.py:973 msgid "Bottom coordinate." msgstr "" #: lib/logitech_receiver/settings_templates.py:974 msgid "Width" msgstr "Leveys" #: lib/logitech_receiver/settings_templates.py:974 msgid "Width." msgstr "" #: lib/logitech_receiver/settings_templates.py:975 msgid "Height" msgstr "Korkeus" #: lib/logitech_receiver/settings_templates.py:975 msgid "Height." msgstr "" #: lib/logitech_receiver/settings_templates.py:976 msgid "Cursor speed." msgstr "" #: lib/logitech_receiver/settings_templates.py:976 msgid "Scale" msgstr "" #: lib/logitech_receiver/settings_templates.py:982 msgid "Gestures" msgstr "Eleet" #: lib/logitech_receiver/settings_templates.py:983 msgid "Tweak the mouse/touchpad behaviour." msgstr "" #: lib/logitech_receiver/settings_templates.py:1000 msgid "Gestures Diversion" msgstr "" #: lib/logitech_receiver/settings_templates.py:1001 msgid "Divert mouse/touchpad gestures." msgstr "" #: lib/logitech_receiver/settings_templates.py:1018 msgid "Gesture params" msgstr "" #: lib/logitech_receiver/settings_templates.py:1019 msgid "Change numerical parameters of a mouse/touchpad." msgstr "" #: lib/logitech_receiver/settings_templates.py:1044 msgid "M-Key LEDs" msgstr "" #: lib/logitech_receiver/settings_templates.py:1046 msgid "Control the M-Key LEDs." msgstr "" #: lib/logitech_receiver/settings_templates.py:1047 #: lib/logitech_receiver/settings_templates.py:1077 msgid "May need G Keys diverted to be effective." msgstr "" #: lib/logitech_receiver/settings_templates.py:1053 #, python-format msgid "Lights up the %s key." msgstr "" #: lib/logitech_receiver/settings_templates.py:1074 msgid "MR-Key LED" msgstr "" #: lib/logitech_receiver/settings_templates.py:1076 msgid "Control the MR-Key LED." msgstr "" #: lib/logitech_receiver/settings_templates.py:1095 msgid "Persistent Key/Button Mapping" msgstr "" #: lib/logitech_receiver/settings_templates.py:1097 msgid "Permanently change the mapping for the key or button." msgstr "" #: lib/logitech_receiver/settings_templates.py:1098 msgid "Changing important keys or buttons (such as for the left mouse " "button) can result in an unusable system." msgstr "" #: lib/logitech_receiver/settings_templates.py:1157 msgid "Sidetone" msgstr "" #: lib/logitech_receiver/settings_templates.py:1158 msgid "Set sidetone level." msgstr "" #: lib/logitech_receiver/settings_templates.py:1167 msgid "Equalizer" msgstr "" #: lib/logitech_receiver/settings_templates.py:1168 msgid "Set equalizer levels." msgstr "" #: lib/logitech_receiver/settings_templates.py:1191 msgid "Hz" msgstr "" #: lib/logitech_receiver/settings_templates.py:1197 msgid "Power Management" msgstr "" #: lib/logitech_receiver/settings_templates.py:1198 msgid "Power off in minutes (0 for never)." msgstr "" #: lib/logitech_receiver/status.py:114 msgid "No paired devices." msgstr "Ei paritettuja laitteita." #: lib/logitech_receiver/status.py:115 lib/solaar/ui/window.py:622 #, python-format msgid "%(count)s paired device." msgid_plural "%(count)s paired devices." msgstr[0] "" msgstr[1] "" #: lib/logitech_receiver/status.py:170 #, python-format msgid "Battery: %(level)s" msgstr "" #: lib/logitech_receiver/status.py:172 #, python-format msgid "Battery: %(percent)d%%" msgstr "" #: lib/logitech_receiver/status.py:184 #, python-format msgid "Lighting: %(level)s lux" msgstr "" #: lib/logitech_receiver/status.py:239 #, python-format msgid "Battery: %(level)s (%(status)s)" msgstr "" #: lib/logitech_receiver/status.py:241 #, python-format msgid "Battery: %(percent)d%% (%(status)s)" msgstr "" #: lib/solaar/ui/__init__.py:52 msgid "Permissions error" msgstr "Virheelliset käyttöoikeudet" #: lib/solaar/ui/__init__.py:54 #, python-format msgid "Found a Logitech receiver or device (%s), but did not have " "permission to open it." msgstr "" #: lib/solaar/ui/__init__.py:55 msgid "If you've just installed Solaar, try disconnecting the receiver or " "device and then reconnecting it." msgstr "" #: lib/solaar/ui/__init__.py:58 msgid "Cannot connect to device error" msgstr "" #: lib/solaar/ui/__init__.py:60 #, python-format msgid "Found a Logitech receiver or device at %s, but encountered an error " "connecting to it." msgstr "" #: lib/solaar/ui/__init__.py:61 msgid "Try disconnecting the device and then reconnecting it or turning it " "off and then on." msgstr "" #: lib/solaar/ui/__init__.py:64 msgid "Unpairing failed" msgstr "Parituksen poisto epäonnistui" #: lib/solaar/ui/__init__.py:66 #, python-brace-format msgid "Failed to unpair %{device} from %{receiver}." msgstr "%{device}n parituksen poisto epäonnistui %{receiver}sta." #: lib/solaar/ui/__init__.py:67 msgid "The receiver returned an error, with no further details." msgstr "Vastaanotin palautti virheen ilman lisätietoja." #: lib/solaar/ui/__init__.py:177 msgid "Another Solaar process is already running so just expose its window" msgstr "" #: lib/solaar/ui/about.py:36 msgid "Manages Logitech receivers,\n" "keyboards, mice, and tablets." msgstr "" #: lib/solaar/ui/about.py:44 msgid "Additional Programming" msgstr "" #: lib/solaar/ui/about.py:45 msgid "GUI design" msgstr "Käyttöliittymäsuunnittelu" #: lib/solaar/ui/about.py:47 msgid "Testing" msgstr "Testaus" #: lib/solaar/ui/about.py:54 #, fuzzy msgid "Logitech documentation" msgstr "Logitechin dokumentointi" #: lib/solaar/ui/action.py:85 lib/solaar/ui/action.py:89 #: lib/solaar/ui/window.py:197 msgid "Unpair" msgstr "Poista paritus" #: lib/solaar/ui/action.py:88 lib/solaar/ui/diversion_rules.py:150 msgid "Cancel" msgstr "" #: lib/solaar/ui/config_panel.py:212 msgid "Complete - ENTER to change" msgstr "" #: lib/solaar/ui/config_panel.py:212 msgid "Incomplete" msgstr "" #: lib/solaar/ui/config_panel.py:455 lib/solaar/ui/config_panel.py:507 #, python-format msgid "%d value" msgid_plural "%d values" msgstr[0] "" msgstr[1] "" #: lib/solaar/ui/config_panel.py:518 msgid "Changes allowed" msgstr "" #: lib/solaar/ui/config_panel.py:519 msgid "No changes allowed" msgstr "" #: lib/solaar/ui/config_panel.py:520 msgid "Ignore this setting" msgstr "" #: lib/solaar/ui/config_panel.py:565 msgid "Working" msgstr "Työskentelee" #: lib/solaar/ui/config_panel.py:568 msgid "Read/write operation failed." msgstr "Luku/kirjoitus operaatio epäonnistui." #: lib/solaar/ui/diversion_rules.py:65 msgid "Built-in rules" msgstr "" #: lib/solaar/ui/diversion_rules.py:65 msgid "User-defined rules" msgstr "" #: lib/solaar/ui/diversion_rules.py:67 lib/solaar/ui/diversion_rules.py:1083 msgid "Rule" msgstr "" #: lib/solaar/ui/diversion_rules.py:68 lib/solaar/ui/diversion_rules.py:509 #: lib/solaar/ui/diversion_rules.py:636 msgid "Sub-rule" msgstr "" #: lib/solaar/ui/diversion_rules.py:70 msgid "[empty]" msgstr "" #: lib/solaar/ui/diversion_rules.py:94 msgid "Solaar Rule Editor" msgstr "" #: lib/solaar/ui/diversion_rules.py:141 msgid "Make changes permanent?" msgstr "" #: lib/solaar/ui/diversion_rules.py:146 msgid "Yes" msgstr "Kyllä" #: lib/solaar/ui/diversion_rules.py:148 msgid "No" msgstr "" #: lib/solaar/ui/diversion_rules.py:153 msgid "If you choose No, changes will be lost when Solaar is closed." msgstr "" #: lib/solaar/ui/diversion_rules.py:201 msgid "Save changes" msgstr "" #: lib/solaar/ui/diversion_rules.py:206 msgid "Discard changes" msgstr "" #: lib/solaar/ui/diversion_rules.py:372 msgid "Insert here" msgstr "" #: lib/solaar/ui/diversion_rules.py:374 msgid "Insert above" msgstr "" #: lib/solaar/ui/diversion_rules.py:376 msgid "Insert below" msgstr "" #: lib/solaar/ui/diversion_rules.py:382 msgid "Insert new rule here" msgstr "" #: lib/solaar/ui/diversion_rules.py:384 msgid "Insert new rule above" msgstr "" #: lib/solaar/ui/diversion_rules.py:386 msgid "Insert new rule below" msgstr "" #: lib/solaar/ui/diversion_rules.py:427 msgid "Paste here" msgstr "" #: lib/solaar/ui/diversion_rules.py:429 msgid "Paste above" msgstr "" #: lib/solaar/ui/diversion_rules.py:431 msgid "Paste below" msgstr "" #: lib/solaar/ui/diversion_rules.py:437 msgid "Paste rule here" msgstr "" #: lib/solaar/ui/diversion_rules.py:439 msgid "Paste rule above" msgstr "" #: lib/solaar/ui/diversion_rules.py:441 msgid "Paste rule below" msgstr "" #: lib/solaar/ui/diversion_rules.py:445 msgid "Paste rule" msgstr "" #: lib/solaar/ui/diversion_rules.py:474 msgid "Flatten" msgstr "" #: lib/solaar/ui/diversion_rules.py:507 msgid "Insert" msgstr "" #: lib/solaar/ui/diversion_rules.py:510 lib/solaar/ui/diversion_rules.py:638 #: lib/solaar/ui/diversion_rules.py:1126 msgid "Or" msgstr "" #: lib/solaar/ui/diversion_rules.py:511 lib/solaar/ui/diversion_rules.py:637 #: lib/solaar/ui/diversion_rules.py:1111 msgid "And" msgstr "" #: lib/solaar/ui/diversion_rules.py:513 msgid "Condition" msgstr "" #: lib/solaar/ui/diversion_rules.py:515 lib/solaar/ui/diversion_rules.py:1292 msgid "Feature" msgstr "" #: lib/solaar/ui/diversion_rules.py:516 lib/solaar/ui/diversion_rules.py:1328 msgid "Report" msgstr "" #: lib/solaar/ui/diversion_rules.py:517 lib/solaar/ui/diversion_rules.py:1204 msgid "Process" msgstr "" #: lib/solaar/ui/diversion_rules.py:518 msgid "Mouse process" msgstr "" #: lib/solaar/ui/diversion_rules.py:519 lib/solaar/ui/diversion_rules.py:1366 msgid "Modifiers" msgstr "" #: lib/solaar/ui/diversion_rules.py:520 lib/solaar/ui/diversion_rules.py:1419 msgid "Key" msgstr "" #: lib/solaar/ui/diversion_rules.py:521 lib/solaar/ui/diversion_rules.py:1461 msgid "KeyIsDown" msgstr "" #: lib/solaar/ui/diversion_rules.py:522 lib/solaar/ui/diversion_rules.py:2260 msgid "Active" msgstr "Aktiivinen" #: lib/solaar/ui/diversion_rules.py:523 lib/solaar/ui/diversion_rules.py:2218 #: lib/solaar/ui/diversion_rules.py:2270 lib/solaar/ui/diversion_rules.py:2323 msgid "Device" msgstr "" #: lib/solaar/ui/diversion_rules.py:524 lib/solaar/ui/diversion_rules.py:2297 msgid "Host" msgstr "" #: lib/solaar/ui/diversion_rules.py:525 lib/solaar/ui/diversion_rules.py:2339 msgid "Setting" msgstr "" #: lib/solaar/ui/diversion_rules.py:526 lib/solaar/ui/diversion_rules.py:1477 #: lib/solaar/ui/diversion_rules.py:1526 msgid "Test" msgstr "" #: lib/solaar/ui/diversion_rules.py:527 lib/solaar/ui/diversion_rules.py:1643 msgid "Test bytes" msgstr "" #: lib/solaar/ui/diversion_rules.py:528 lib/solaar/ui/diversion_rules.py:1736 msgid "Mouse Gesture" msgstr "" #: lib/solaar/ui/diversion_rules.py:532 msgid "Action" msgstr "Toiminto" #: lib/solaar/ui/diversion_rules.py:534 lib/solaar/ui/diversion_rules.py:1845 msgid "Key press" msgstr "" #: lib/solaar/ui/diversion_rules.py:535 lib/solaar/ui/diversion_rules.py:1897 msgid "Mouse scroll" msgstr "" #: lib/solaar/ui/diversion_rules.py:536 lib/solaar/ui/diversion_rules.py:1959 msgid "Mouse click" msgstr "" #: lib/solaar/ui/diversion_rules.py:537 msgid "Set" msgstr "" #: lib/solaar/ui/diversion_rules.py:538 lib/solaar/ui/diversion_rules.py:2030 msgid "Execute" msgstr "" #: lib/solaar/ui/diversion_rules.py:539 lib/solaar/ui/diversion_rules.py:1158 msgid "Later" msgstr "" #: lib/solaar/ui/diversion_rules.py:568 msgid "Insert new rule" msgstr "" #: lib/solaar/ui/diversion_rules.py:588 lib/solaar/ui/diversion_rules.py:1686 #: lib/solaar/ui/diversion_rules.py:1790 lib/solaar/ui/diversion_rules.py:1989 msgid "Delete" msgstr "" #: lib/solaar/ui/diversion_rules.py:610 msgid "Negate" msgstr "" #: lib/solaar/ui/diversion_rules.py:634 msgid "Wrap with" msgstr "" #: lib/solaar/ui/diversion_rules.py:656 msgid "Cut" msgstr "Leikkaa" #: lib/solaar/ui/diversion_rules.py:671 msgid "Paste" msgstr "Liitä" #: lib/solaar/ui/diversion_rules.py:683 msgid "Copy" msgstr "Kopioi" #: lib/solaar/ui/diversion_rules.py:1063 msgid "This editor does not support the selected rule component yet." msgstr "" #: lib/solaar/ui/diversion_rules.py:1138 msgid "Number of seconds to delay." msgstr "" #: lib/solaar/ui/diversion_rules.py:1177 msgid "Not" msgstr "" #: lib/solaar/ui/diversion_rules.py:1187 msgid "X11 active process. For use in X11 only." msgstr "" #: lib/solaar/ui/diversion_rules.py:1218 msgid "X11 mouse process. For use in X11 only." msgstr "" #: lib/solaar/ui/diversion_rules.py:1235 msgid "MouseProcess" msgstr "" #: lib/solaar/ui/diversion_rules.py:1260 msgid "Feature name of notification triggering rule processing." msgstr "" #: lib/solaar/ui/diversion_rules.py:1308 msgid "Report number of notification triggering rule processing." msgstr "" #: lib/solaar/ui/diversion_rules.py:1342 msgid "Active keyboard modifiers. Not always available in Wayland." msgstr "" #: lib/solaar/ui/diversion_rules.py:1383 msgid "Diverted key or button depressed or released.\n" "Use the Key/Button Diversion and Divert G Keys settings to divert " "keys and buttons." msgstr "" #: lib/solaar/ui/diversion_rules.py:1392 msgid "Key down" msgstr "" #: lib/solaar/ui/diversion_rules.py:1395 msgid "Key up" msgstr "" #: lib/solaar/ui/diversion_rules.py:1436 msgid "Diverted key or button is currently down.\n" "Use the Key/Button Diversion and Divert G Keys settings to divert " "keys and buttons." msgstr "" #: lib/solaar/ui/diversion_rules.py:1475 msgid "Test condition on notification triggering rule processing." msgstr "" #: lib/solaar/ui/diversion_rules.py:1479 msgid "Parameter" msgstr "" #: lib/solaar/ui/diversion_rules.py:1542 msgid "begin (inclusive)" msgstr "" #: lib/solaar/ui/diversion_rules.py:1543 msgid "end (exclusive)" msgstr "" #: lib/solaar/ui/diversion_rules.py:1552 msgid "range" msgstr "" #: lib/solaar/ui/diversion_rules.py:1554 msgid "minimum" msgstr "" #: lib/solaar/ui/diversion_rules.py:1555 msgid "maximum" msgstr "" #: lib/solaar/ui/diversion_rules.py:1557 #, python-format msgid "bytes %(0)d to %(1)d, ranging from %(2)d to %(3)d" msgstr "" #: lib/solaar/ui/diversion_rules.py:1562 msgid "mask" msgstr "" #: lib/solaar/ui/diversion_rules.py:1563 #, python-format msgid "bytes %(0)d to %(1)d, mask %(2)d" msgstr "" #: lib/solaar/ui/diversion_rules.py:1573 msgid "Bit or range test on bytes in notification message triggering rule " "processing." msgstr "" #: lib/solaar/ui/diversion_rules.py:1583 msgid "type" msgstr "" #: lib/solaar/ui/diversion_rules.py:1666 msgid "Mouse gesture with optional initiating button followed by zero or " "more mouse movements." msgstr "" #: lib/solaar/ui/diversion_rules.py:1671 msgid "Add movement" msgstr "" #: lib/solaar/ui/diversion_rules.py:1764 msgid "Simulate a chorded key click or depress or release.\n" "On Wayland requires write access to /dev/uinput." msgstr "" #: lib/solaar/ui/diversion_rules.py:1769 msgid "Add key" msgstr "" #: lib/solaar/ui/diversion_rules.py:1772 msgid "Click" msgstr "" #: lib/solaar/ui/diversion_rules.py:1775 msgid "Depress" msgstr "" #: lib/solaar/ui/diversion_rules.py:1778 msgid "Release" msgstr "" #: lib/solaar/ui/diversion_rules.py:1861 msgid "Simulate a mouse scroll.\n" "On Wayland requires write access to /dev/uinput." msgstr "" #: lib/solaar/ui/diversion_rules.py:1918 msgid "Simulate a mouse click.\n" "On Wayland requires write access to /dev/uinput." msgstr "" #: lib/solaar/ui/diversion_rules.py:1921 msgid "Button" msgstr "" #: lib/solaar/ui/diversion_rules.py:1922 msgid "Count and Action" msgstr "" #: lib/solaar/ui/diversion_rules.py:1972 msgid "Execute a command with arguments." msgstr "" #: lib/solaar/ui/diversion_rules.py:1975 msgid "Add argument" msgstr "" #: lib/solaar/ui/diversion_rules.py:2050 msgid "Toggle" msgstr "" #: lib/solaar/ui/diversion_rules.py:2050 msgid "True" msgstr "" #: lib/solaar/ui/diversion_rules.py:2051 msgid "False" msgstr "" #: lib/solaar/ui/diversion_rules.py:2065 msgid "Unsupported setting" msgstr "" #: lib/solaar/ui/diversion_rules.py:2223 lib/solaar/ui/diversion_rules.py:2242 #: lib/solaar/ui/diversion_rules.py:2328 lib/solaar/ui/diversion_rules.py:2570 #: lib/solaar/ui/diversion_rules.py:2588 msgid "Originating device" msgstr "" #: lib/solaar/ui/diversion_rules.py:2256 msgid "Device is active and its settings can be changed." msgstr "" #: lib/solaar/ui/diversion_rules.py:2266 msgid "Device that originated the current notification." msgstr "" #: lib/solaar/ui/diversion_rules.py:2280 msgid "Name of host computer." msgstr "" #: lib/solaar/ui/diversion_rules.py:2347 msgid "Value" msgstr "Arvo" #: lib/solaar/ui/diversion_rules.py:2355 msgid "Item" msgstr "" #: lib/solaar/ui/diversion_rules.py:2630 msgid "Change setting on device" msgstr "" #: lib/solaar/ui/diversion_rules.py:2647 msgid "Setting on device" msgstr "" #: lib/solaar/ui/notify.py:124 lib/solaar/ui/tray.py:318 #: lib/solaar/ui/tray.py:323 lib/solaar/ui/window.py:739 msgid "offline" msgstr "ei yhdistetty" #: lib/solaar/ui/pair_window.py:122 lib/solaar/ui/pair_window.py:256 #: lib/solaar/ui/pair_window.py:288 #, python-format msgid "%(receiver_name)s: pair new device" msgstr "" #: lib/solaar/ui/pair_window.py:123 #, python-format msgid "Enter passcode on %(name)s." msgstr "" #: lib/solaar/ui/pair_window.py:126 #, python-format msgid "Type %(passcode)s and then press the enter key." msgstr "" #: lib/solaar/ui/pair_window.py:129 msgid "left" msgstr "" #: lib/solaar/ui/pair_window.py:129 msgid "right" msgstr "" #: lib/solaar/ui/pair_window.py:131 #, python-format msgid "Press %(code)s\n" "and then press left and right buttons simultaneously." msgstr "" #: lib/solaar/ui/pair_window.py:188 msgid "Pairing failed" msgstr "Paritus epäonnistui." #: lib/solaar/ui/pair_window.py:190 msgid "Make sure your device is within range, and has a decent battery " "charge." msgstr "Varmista, että laitteesi on kantaman sisällä ja siinä on riittävä " "lataus." #: lib/solaar/ui/pair_window.py:192 msgid "A new device was detected, but it is not compatible with this " "receiver." msgstr "Uusi laite tunnistettiin, mutta se ei ole yhteensopiva käytetyn " "vastaanottimen kanssa." #: lib/solaar/ui/pair_window.py:194 msgid "More paired devices than receiver can support." msgstr "" #: lib/solaar/ui/pair_window.py:196 msgid "No further details are available about the error." msgstr "Virheestä ei ole saatavilla enempää tietoja." #: lib/solaar/ui/pair_window.py:210 msgid "Found a new device:" msgstr "" #: lib/solaar/ui/pair_window.py:235 msgid "The wireless link is not encrypted" msgstr "Langaton yhteys ei ole salattu" #: lib/solaar/ui/pair_window.py:264 msgid "Unifying receivers are only compatible with Unifying devices." msgstr "" #: lib/solaar/ui/pair_window.py:266 msgid "Bolt receivers are only compatible with Bolt devices." msgstr "" #: lib/solaar/ui/pair_window.py:268 msgid "Other receivers are only compatible with a few devices." msgstr "" #: lib/solaar/ui/pair_window.py:270 msgid "The device must not be paired with a nearby powered-on receiver." msgstr "" #: lib/solaar/ui/pair_window.py:274 msgid "Press a pairing button or key until the pairing light flashes " "quickly." msgstr "" #: lib/solaar/ui/pair_window.py:276 msgid "You may have to first turn the device off and on again." msgstr "" #: lib/solaar/ui/pair_window.py:278 msgid "Turn on the device you want to pair." msgstr "Käynnistä laite, jonka haluat parittaa." #: lib/solaar/ui/pair_window.py:280 msgid "If the device is already turned on, turn it off and on again." msgstr "" #: lib/solaar/ui/pair_window.py:283 #, python-format msgid "\n" "\n" "This receiver has %d pairing remaining." msgid_plural "\n" "\n" "This receiver has %d pairings remaining." msgstr[0] "" msgstr[1] "" #: lib/solaar/ui/pair_window.py:286 msgid "\n" "Cancelling at this point will not use up a pairing." msgstr "" #: lib/solaar/ui/tray.py:58 msgid "No supported device found" msgstr "" #: lib/solaar/ui/tray.py:64 lib/solaar/ui/window.py:319 #, python-format msgid "About %s" msgstr "Tietoja %s" #: lib/solaar/ui/tray.py:65 lib/solaar/ui/window.py:317 #, python-format msgid "Quit %s" msgstr "Lopeta %s" #: lib/solaar/ui/tray.py:297 lib/solaar/ui/tray.py:305 msgid "no receiver" msgstr "ei vastaanotinta" #: lib/solaar/ui/tray.py:321 msgid "no status" msgstr "ei tietoja" #: lib/solaar/ui/window.py:96 msgid "Scanning" msgstr "Skannataan" #: lib/solaar/ui/window.py:129 msgid "Battery" msgstr "Akku" #: lib/solaar/ui/window.py:132 msgid "Wireless Link" msgstr "Langaton yhteys" #: lib/solaar/ui/window.py:136 msgid "Lighting" msgstr "Valaistus" #: lib/solaar/ui/window.py:170 msgid "Show Technical Details" msgstr "Näytä tekniset tiedot" #: lib/solaar/ui/window.py:186 msgid "Pair new device" msgstr "Parita uusi laite" #: lib/solaar/ui/window.py:205 msgid "Select a device" msgstr "Valitse laite" #: lib/solaar/ui/window.py:322 msgid "Rule Editor" msgstr "" #: lib/solaar/ui/window.py:533 msgid "Path" msgstr "Polku" #: lib/solaar/ui/window.py:536 msgid "USB ID" msgstr "" #: lib/solaar/ui/window.py:539 lib/solaar/ui/window.py:541 #: lib/solaar/ui/window.py:561 lib/solaar/ui/window.py:563 msgid "Serial" msgstr "Sarjanumero" #: lib/solaar/ui/window.py:545 msgid "Index" msgstr "Järjestysnumero" #: lib/solaar/ui/window.py:547 msgid "Wireless PID" msgstr "Langaton PID" #: lib/solaar/ui/window.py:549 msgid "Product ID" msgstr "" #: lib/solaar/ui/window.py:551 msgid "Protocol" msgstr "Yhteyskäytäntö" #: lib/solaar/ui/window.py:551 msgid "Unknown" msgstr "Tuntematon" #: lib/solaar/ui/window.py:554 #, python-format msgid "%(rate)d ms (%(rate_hz)dHz)" msgstr "" #: lib/solaar/ui/window.py:554 msgid "Polling rate" msgstr "Päivitysnopeus" #: lib/solaar/ui/window.py:565 msgid "Unit ID" msgstr "" #: lib/solaar/ui/window.py:576 msgid "none" msgstr "ei mitään" #: lib/solaar/ui/window.py:577 msgid "Notifications" msgstr "Ilmoitukset" #: lib/solaar/ui/window.py:621 msgid "No device paired." msgstr "" #: lib/solaar/ui/window.py:628 #, python-format msgid "Up to %(max_count)s device can be paired to this receiver." msgid_plural "Up to %(max_count)s devices can be paired to this receiver." msgstr[0] "" msgstr[1] "" #: lib/solaar/ui/window.py:634 msgid "Only one device can be paired to this receiver." msgstr "" #: lib/solaar/ui/window.py:638 #, python-format msgid "This receiver has %d pairing remaining." msgid_plural "This receiver has %d pairings remaining." msgstr[0] "" msgstr[1] "" #: lib/solaar/ui/window.py:692 msgid "Battery Voltage" msgstr "" #: lib/solaar/ui/window.py:694 msgid "Voltage reported by battery" msgstr "" #: lib/solaar/ui/window.py:696 msgid "Battery Level" msgstr "" #: lib/solaar/ui/window.py:698 msgid "Approximate level reported by battery" msgstr "" #: lib/solaar/ui/window.py:705 lib/solaar/ui/window.py:707 msgid "next reported " msgstr "" #: lib/solaar/ui/window.py:708 msgid " and next level to be reported." msgstr "" #: lib/solaar/ui/window.py:713 msgid "last known" msgstr "viimeisin tunnettu" #: lib/solaar/ui/window.py:724 msgid "encrypted" msgstr "salattu" #: lib/solaar/ui/window.py:726 msgid "The wireless link between this device and its receiver is encrypted." msgstr "Langaton yhteys laitteen ja vastaanottimen välillä on salattu." #: lib/solaar/ui/window.py:728 msgid "not encrypted" msgstr "salaamaton" #: lib/solaar/ui/window.py:732 msgid "The wireless link between this device and its receiver is not " "encrypted.\n" "This is a security issue for pointing devices, and a major security " "issue for text-input devices." msgstr "" #: lib/solaar/ui/window.py:748 #, python-format msgid "%(light_level)d lux" msgstr "" #~ msgid " paired devices." #~ msgstr " paritettua laitetta" #~ msgid "1 paired device." #~ msgstr "Yksi paritettu laite." #, python-format #~ msgid "Found a Logitech Receiver (%s), but did not have permission " #~ "to open it." #~ msgstr "Logitechin vastaanotin (%s) löydettiin, mutta sitä ei ollut " #~ "oikeutta avata." #~ msgid "Found a new device" #~ msgstr "Löydetty uusi laite." #~ msgid "If the device is already turned on,\n" #~ "turn if off and on again." #~ msgstr "Jos laite on jo päällä, sammuta se\n" #~ "ja kytke virta uudelleen päälle." #~ msgid "If you've just installed Solaar, try removing the receiver " #~ "and plugging it back in." #~ msgstr "Jos olet juuri asentanut Solaarin, yritä vastaanottimen " #~ "irroittamista ja liittämistä uudelleen." #~ msgid "No Logitech receiver found" #~ msgstr "Ei löydetty Logitechin vastaanottimia." #~ msgid "No device paired" #~ msgstr "Ei paritettuja laitteita" #~ msgid "Only one device can be paired to this receiver" #~ msgstr "Vain yksi laite voidaan parittaa tähän vastaanottimeen" #~ msgid "Shows status of devices connected\n" #~ "through wireless Logitech receivers." #~ msgstr "Näyttää Logitechin langattoman \n" #~ "vastaanottimen kautta liitettyjen\n" #~ "laitteiden tilan." #~ msgid "Smooth Scrolling" #~ msgstr "Pehmeä vieritys" #, python-format #~ msgid "The receiver only supports %d paired device(s)." #~ msgstr "Vastaanotin tukee vain %d liitettyä laitetta." #~ msgid "The receiver was unplugged." #~ msgstr "Vastaanotin irroitettiin." #~ msgid "The wireless link between this device and its receiver is " #~ "not encrypted.\n" #~ "\n" #~ "For pointing devices (mice, trackballs, trackpads), this is a minor " #~ "security issue.\n" #~ "\n" #~ "It is, however, a major security issue for text-input devices " #~ "(keyboards, numpads),\n" #~ "because typed text can be sniffed inconspicuously by 3rd parties " #~ "within range." #~ msgstr "Langaton yhteys laitteen ja vastaanottimen välillä ei ole " #~ "salattu.\n" #~ "\n" #~ "Osoitinlaitteita (hiiret, ohjainpallot, tasohiiret) käytettäessä se " #~ "on pieni tietoturvallisuusriski.\n" #~ "\n" #~ "Kuitenkin, näppäimistöjen kanssa se on suuri tietoturvallisuusriski " #~ "sillä kolmannet osapuolet\n" #~ "voivat lukea huomaamattomasti kirjoitetun tekstin yhteyden kantaman " #~ "sisällä." #~ msgid "USB id" #~ msgstr "USB id" #~ msgid "Up to %d devices can be paired to this receiver" #~ msgstr "Korkeintaan %d laitetta voidaan parittaa tähän " #~ "vastaanottimeen" #~ msgid "closed" #~ msgstr "suljettu" #~ msgid "height" #~ msgstr "korkeus" #~ msgid "lux" #~ msgstr "luksia" #~ msgid "open" #~ msgstr "avoin" #~ msgid "pair new device" #~ msgstr "liitä uusi laite" #~ msgid "paired devices" #~ msgstr "paritettua laitetta" #~ msgid "pairing lock is " #~ msgstr "parituslukko on " #~ msgid "unknown" #~ msgstr "tuntematon" #~ msgid "width" #~ msgstr "leveys" Solaar-1.1.14/po/fr.po000066400000000000000000001720721473525213700144260ustar00rootroot00000000000000# French translations for solaar package. # Copyright (C) 2013 THE solaar'S COPYRIGHT HOLDER # This file is distributed under the same license as the solaar package. # Automatically generated, 2013. # msgid "" msgstr "" "Project-Id-Version: solaar 1.1.13\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2024-05-12 03:30+0200\n" "PO-Revision-Date: 2024-05-12 03:35+0200\n" "Last-Translator: David Geiger \n" "Language-Team: Language: fr\n" "Language: fr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" "X-Generator: Poedit 3.4.3\n" #: lib/logitech_receiver/base_usb.py:45 msgid "Bolt Receiver" msgstr "Récepteur Bolt" #: lib/logitech_receiver/base_usb.py:58 msgid "Unifying Receiver" msgstr "Récepteur Unifying" #: lib/logitech_receiver/base_usb.py:70 lib/logitech_receiver/base_usb.py:83 #: lib/logitech_receiver/base_usb.py:97 lib/logitech_receiver/base_usb.py:111 #: lib/logitech_receiver/base_usb.py:125 msgid "Nano Receiver" msgstr "Récepteur Nano" #: lib/logitech_receiver/base_usb.py:138 msgid "Lightspeed Receiver" msgstr "Récepteur Lightspeed" #: lib/logitech_receiver/base_usb.py:149 msgid "EX100 Receiver 27 Mhz" msgstr "Récepteur EX100 27 Mhz" #: lib/logitech_receiver/common.py:605 #, python-format msgid "Battery: %(level)s (%(status)s)" msgstr "Batterie : %(level)s (%(status)s)" #: lib/logitech_receiver/common.py:607 #, python-format msgid "Battery: %(percent)d%% (%(status)s)" msgstr "Batterie : %(percent)d%% (%(status)s)" #: lib/logitech_receiver/hidpp20.py:894 #: lib/logitech_receiver/settings_templates.py:307 msgid "Disabled" msgstr "Désactivé" #: lib/logitech_receiver/hidpp20.py:895 msgid "Static" msgstr "Statique" #: lib/logitech_receiver/hidpp20.py:896 msgid "Pulse" msgstr "Pulsation" #: lib/logitech_receiver/hidpp20.py:897 msgid "Cycle" msgstr "Cycle" #: lib/logitech_receiver/hidpp20.py:898 msgid "Boot" msgstr "Boot" #: lib/logitech_receiver/hidpp20.py:899 msgid "Demo" msgstr "Démo" #: lib/logitech_receiver/hidpp20.py:900 msgid "Breathe" msgstr "" #: lib/logitech_receiver/hidpp20.py:901 msgid "Ripple" msgstr "" #: lib/logitech_receiver/hidpp20.py:902 msgid "Decomposition" msgstr "Décomposition" #: lib/logitech_receiver/hidpp20.py:903 msgid "Signature1" msgstr "Signature 1" #: lib/logitech_receiver/hidpp20.py:904 msgid "Signature2" msgstr "Signature 2" #: lib/logitech_receiver/hidpp20.py:905 msgid "CycleS" msgstr "Cycle S" #: lib/logitech_receiver/hidpp20.py:969 msgid "Unknown Location" msgstr "Emplacement inconnu" #: lib/logitech_receiver/hidpp20.py:970 msgid "Primary" msgstr "Primaire" #: lib/logitech_receiver/hidpp20.py:971 msgid "Logo" msgstr "Logo" #: lib/logitech_receiver/hidpp20.py:972 msgid "Left Side" msgstr "Côté gauche" #: lib/logitech_receiver/hidpp20.py:973 msgid "Right Side" msgstr "Côté droit" #: lib/logitech_receiver/hidpp20.py:974 msgid "Combined" msgstr "Combiné" #: lib/logitech_receiver/hidpp20.py:975 msgid "Primary 1" msgstr "Primaire 1" #: lib/logitech_receiver/hidpp20.py:976 msgid "Primary 2" msgstr "Primaire 2" #: lib/logitech_receiver/hidpp20.py:977 msgid "Primary 3" msgstr "Primaire 3" #: lib/logitech_receiver/hidpp20.py:978 msgid "Primary 4" msgstr "Primaire 4" #: lib/logitech_receiver/hidpp20.py:979 msgid "Primary 5" msgstr "Primaire 5" #: lib/logitech_receiver/hidpp20.py:980 msgid "Primary 6" msgstr "Primaire 6" #: lib/logitech_receiver/i18n.py:28 msgid "empty" msgstr "vide" #: lib/logitech_receiver/i18n.py:29 msgid "critical" msgstr "critique" #: lib/logitech_receiver/i18n.py:30 msgid "low" msgstr "faible" #: lib/logitech_receiver/i18n.py:31 msgid "average" msgstr "moyenne" #: lib/logitech_receiver/i18n.py:32 msgid "good" msgstr "bonne" #: lib/logitech_receiver/i18n.py:33 msgid "full" msgstr "pleine" #: lib/logitech_receiver/i18n.py:35 msgid "discharging" msgstr "En décharge" #: lib/logitech_receiver/i18n.py:36 msgid "recharging" msgstr "En charge" #: lib/logitech_receiver/i18n.py:37 msgid "charging" msgstr "en charge" #: lib/logitech_receiver/i18n.py:38 msgid "not charging" msgstr "pas en charge" #: lib/logitech_receiver/i18n.py:39 msgid "almost full" msgstr "presque pleine" #: lib/logitech_receiver/i18n.py:40 msgid "charged" msgstr "chargée" #: lib/logitech_receiver/i18n.py:41 msgid "slow recharge" msgstr "recharge lente" #: lib/logitech_receiver/i18n.py:42 msgid "invalid battery" msgstr "batterie invalide" #: lib/logitech_receiver/i18n.py:43 msgid "thermal error" msgstr "erreur thermique" #: lib/logitech_receiver/i18n.py:44 msgid "error" msgstr "erreur" #: lib/logitech_receiver/i18n.py:45 msgid "standard" msgstr "standard" #: lib/logitech_receiver/i18n.py:46 msgid "fast" msgstr "rapide" #: lib/logitech_receiver/i18n.py:47 msgid "slow" msgstr "lente" #: lib/logitech_receiver/i18n.py:49 msgid "device timeout" msgstr "le périphérique ne répond pas" #: lib/logitech_receiver/i18n.py:50 msgid "device not supported" msgstr "périphérique non pris en charge" #: lib/logitech_receiver/i18n.py:51 msgid "too many devices" msgstr "trop de périphériques" #: lib/logitech_receiver/i18n.py:52 msgid "sequence timeout" msgstr "délai dépassé" #: lib/logitech_receiver/i18n.py:54 msgid "Firmware" msgstr "Micrologiciel" #: lib/logitech_receiver/i18n.py:55 msgid "Bootloader" msgstr "Chargeur d'amorçage" #: lib/logitech_receiver/i18n.py:56 msgid "Hardware" msgstr "Matériel" #: lib/logitech_receiver/i18n.py:57 msgid "Other" msgstr "Autre" #: lib/logitech_receiver/i18n.py:59 msgid "Left Button" msgstr "Bouton gauche" #: lib/logitech_receiver/i18n.py:60 msgid "Right Button" msgstr "Bouton droit" #: lib/logitech_receiver/i18n.py:61 msgid "Middle Button" msgstr "Bouton central" #: lib/logitech_receiver/i18n.py:62 msgid "Back Button" msgstr "Bouton retour" #: lib/logitech_receiver/i18n.py:63 msgid "Forward Button" msgstr "Bouton avant" #: lib/logitech_receiver/i18n.py:64 msgid "Mouse Gesture Button" msgstr "Bouton gestuel de la souris" #: lib/logitech_receiver/i18n.py:65 msgid "Smart Shift" msgstr "Changement intelligent" #: lib/logitech_receiver/i18n.py:66 msgid "DPI Switch" msgstr "Commutateur DPI" #: lib/logitech_receiver/i18n.py:67 msgid "Left Tilt" msgstr "Inclinaison à gauche" #: lib/logitech_receiver/i18n.py:68 msgid "Right Tilt" msgstr "Inclinaison à droite" #: lib/logitech_receiver/i18n.py:69 msgid "Left Click" msgstr "Clic gauche" #: lib/logitech_receiver/i18n.py:70 msgid "Right Click" msgstr "Clic droit" #: lib/logitech_receiver/i18n.py:71 msgid "Mouse Middle Button" msgstr "Bouton central de la souris" #: lib/logitech_receiver/i18n.py:72 msgid "Mouse Back Button" msgstr "Bouton de retour de la souris" #: lib/logitech_receiver/i18n.py:73 msgid "Mouse Forward Button" msgstr "Bouton avant de la souris" #: lib/logitech_receiver/i18n.py:74 msgid "Gesture Button Navigation" msgstr "Navigation par bouton de gestuelle" #: lib/logitech_receiver/i18n.py:75 msgid "Mouse Scroll Left Button" msgstr "Bouton de défilement vers la gauche de la souris" #: lib/logitech_receiver/i18n.py:76 msgid "Mouse Scroll Right Button" msgstr "Bouton de défilement vers la droite de la souris" #: lib/logitech_receiver/i18n.py:78 msgid "pressed" msgstr "pressé" #: lib/logitech_receiver/i18n.py:79 msgid "released" msgstr "relâché" #: lib/logitech_receiver/notifications.py:64 #: lib/logitech_receiver/notifications.py:116 msgid "pairing lock is closed" msgstr "le verrou de jumelage est fermé" #: lib/logitech_receiver/notifications.py:64 #: lib/logitech_receiver/notifications.py:116 msgid "pairing lock is open" msgstr "le verrou de jumelage est ouvert" #: lib/logitech_receiver/notifications.py:81 msgid "discovery lock is closed" msgstr "le verrou de détection est fermé" #: lib/logitech_receiver/notifications.py:81 msgid "discovery lock is open" msgstr "le verrou de détection est ouvert" #: lib/logitech_receiver/notifications.py:208 lib/solaar/listener.py:238 msgid "connected" msgstr "connecté" #: lib/logitech_receiver/notifications.py:208 lib/solaar/listener.py:238 msgid "disconnected" msgstr "déconnecté" #: lib/logitech_receiver/notifications.py:234 msgid "unpaired" msgstr "non jumelé" #: lib/logitech_receiver/notifications.py:281 msgid "powered on" msgstr "sous tension" #: lib/logitech_receiver/receiver.py:351 msgid "No paired devices." msgstr "Aucun périphérique jumelé." #: lib/logitech_receiver/receiver.py:353 lib/solaar/ui/window.py:604 #, python-format msgid "%(count)s paired device." msgid_plural "%(count)s paired devices." msgstr[0] "%(count)s périphérique jumelé." msgstr[1] "%(count)s périphériques jumelés." #: lib/logitech_receiver/settings.py:616 msgid "register" msgstr "registre" #: lib/logitech_receiver/settings.py:630 lib/logitech_receiver/settings.py:656 msgid "feature" msgstr "fonctionnalité" #: lib/logitech_receiver/settings_templates.py:146 msgid "Swap Fx function" msgstr "Fonction Swap Fx" #: lib/logitech_receiver/settings_templates.py:149 msgid "" "When set, the F1..F12 keys will activate their special function,\n" "and you must hold the FN key to activate their standard function." msgstr "" "Lorsque défini, les touches F1..F12 activeront leurs fonctions spéciales,\n" "et vous devez maintenir la touche FN pour activer leurs fonctions standards." #: lib/logitech_receiver/settings_templates.py:154 msgid "" "When unset, the F1..F12 keys will activate their standard function,\n" "and you must hold the FN key to activate their special function." msgstr "" "Lorsque non défini, les touches F1..F12 activeront leurs fonctions " "standards,\n" "et vous devez maintenir la touche FN pour activer leurs fonctions spéciales." #: lib/logitech_receiver/settings_templates.py:162 msgid "Hand Detection" msgstr "Détection manuelle" #: lib/logitech_receiver/settings_templates.py:163 msgid "Turn on illumination when the hands hover over the keyboard." msgstr "Allume l'éclairage lorsque les mains passent au-dessus du clavier." #: lib/logitech_receiver/settings_templates.py:170 msgid "Scroll Wheel Smooth Scrolling" msgstr "Défilement fluide à la molette" #: lib/logitech_receiver/settings_templates.py:171 #: lib/logitech_receiver/settings_templates.py:418 #: lib/logitech_receiver/settings_templates.py:447 msgid "High-sensitivity mode for vertical scroll with the wheel." msgstr "Mode haute sensibilité pour défilement vertical avec la roulette." #: lib/logitech_receiver/settings_templates.py:178 msgid "Side Scrolling" msgstr "Défilement latéral" #: lib/logitech_receiver/settings_templates.py:180 msgid "" "When disabled, pushing the wheel sideways sends custom button events\n" "instead of the standard side-scrolling events." msgstr "" "Lorsque désactivé, l'appui sur les côtés de la roulette envoie des " "évènements\n" " de bouton personnalisés à la place des évènements standards de défilement " "latéral." #: lib/logitech_receiver/settings_templates.py:190 msgid "Sensitivity (DPI - older mice)" msgstr "Sensibilité (DPI - souris plus anciennne)" #: lib/logitech_receiver/settings_templates.py:191 #: lib/logitech_receiver/settings_templates.py:979 #: lib/logitech_receiver/settings_templates.py:1005 msgid "Mouse movement sensitivity" msgstr "Sensibilité du mouvement de la souris" #: lib/logitech_receiver/settings_templates.py:264 msgid "Backlight Timed" msgstr "Rétroéclairage chronométré" #: lib/logitech_receiver/settings_templates.py:265 #: lib/logitech_receiver/settings_templates.py:405 msgid "Set illumination time for keyboard." msgstr "Définir le temps d'éclairage du clavier." #: lib/logitech_receiver/settings_templates.py:276 msgid "Backlight" msgstr "Rétroéclairage" #: lib/logitech_receiver/settings_templates.py:277 msgid "" "Illumination level on keyboard. Changes made are only applied in Manual " "mode." msgstr "" "Niveau d'éclairage du clavier. Les modifications apportées ne sont " "appliquées qu'en mode manuel." #: lib/logitech_receiver/settings_templates.py:309 msgid "Automatic" msgstr "Automatique" #: lib/logitech_receiver/settings_templates.py:311 msgid "Manual" msgstr "Manuel" #: lib/logitech_receiver/settings_templates.py:313 msgid "Enabled" msgstr "Activé" #: lib/logitech_receiver/settings_templates.py:319 msgid "Backlight Level" msgstr "Niveau de rétroéclairage" #: lib/logitech_receiver/settings_templates.py:320 msgid "Illumination level on keyboard when in Manual mode." msgstr "Niveau d'éclairage du clavier en mode manuel." #: lib/logitech_receiver/settings_templates.py:377 msgid "Backlight Delay Hands Out" msgstr "Délai de rétroéclairage sans les mains" #: lib/logitech_receiver/settings_templates.py:378 msgid "" "Delay in seconds until backlight fades out with hands away from keyboard." msgstr "" "Délai jusqu'à ce que le rétroéclairage s'éteigne avec les mains éloignées du " "clavier." #: lib/logitech_receiver/settings_templates.py:386 msgid "Backlight Delay Hands In" msgstr "Délai de rétroéclairage avec les mains" #: lib/logitech_receiver/settings_templates.py:387 msgid "Delay in seconds until backlight fades out with hands near keyboard." msgstr "" "Délai jusqu'à ce que le rétroéclairage s'éteigne avec les mains près du " "clavier." #: lib/logitech_receiver/settings_templates.py:395 msgid "Backlight Delay Powered" msgstr "Délai de rétroéclairage alimenté" #: lib/logitech_receiver/settings_templates.py:396 msgid "Delay in seconds until backlight fades out with external power." msgstr "" "Délai jusqu'à ce que le rétroéclairage s'éteigne avec une alimentation " "externe." #: lib/logitech_receiver/settings_templates.py:404 msgid "Backlight (Seconds)" msgstr "Rétroéclairage (Secondes)" #: lib/logitech_receiver/settings_templates.py:416 msgid "Scroll Wheel High Resolution" msgstr "Molette haute résolution" #: lib/logitech_receiver/settings_templates.py:420 #: lib/logitech_receiver/settings_templates.py:449 msgid "Set to ignore if scrolling is abnormally fast or slow" msgstr "Définir pour ignorer si le défilement est anormalement rapide ou lent" #: lib/logitech_receiver/settings_templates.py:427 #: lib/logitech_receiver/settings_templates.py:458 msgid "Scroll Wheel Diversion" msgstr "Interception de la molette" #: lib/logitech_receiver/settings_templates.py:429 msgid "" "Make scroll wheel send LOWRES_WHEEL HID++ notifications (which trigger " "Solaar rules but are otherwise ignored)." msgstr "" "Active l'envoi par la molette de notifications LOWRES_WHEEL HID++ (ce qui " "déclenche les règles Solaar qui sont ignorées sinon)." #: lib/logitech_receiver/settings_templates.py:436 msgid "Scroll Wheel Direction" msgstr "Direction de la molette" #: lib/logitech_receiver/settings_templates.py:437 msgid "Invert direction for vertical scroll with wheel." msgstr "Direction inversée pour le défilement vertical avec la molette." #: lib/logitech_receiver/settings_templates.py:445 msgid "Scroll Wheel Resolution" msgstr "Résolution de la molette" #: lib/logitech_receiver/settings_templates.py:460 msgid "" "Make scroll wheel send HIRES_WHEEL HID++ notifications (which trigger Solaar " "rules but are otherwise ignored)." msgstr "" "Active l'envoi par la molette de notifications HIRES_WHEEL HID++ (ce qui " "déclenche les règles Solaar qui sont ignorées sinon)." #: lib/logitech_receiver/settings_templates.py:469 msgid "Sensitivity (Pointer Speed)" msgstr "Sensibilité (vitesse du pointeur)" #: lib/logitech_receiver/settings_templates.py:470 msgid "Speed multiplier for mouse (256 is normal multiplier)." msgstr "" "Multiplicateur de vitesse pour la souris (256 est le multiplicateur normal)." #: lib/logitech_receiver/settings_templates.py:480 msgid "Thumb Wheel Diversion" msgstr "Interception de la molette de pouce" #: lib/logitech_receiver/settings_templates.py:482 msgid "" "Make thumb wheel send THUMB_WHEEL HID++ notifications (which trigger Solaar " "rules but are otherwise ignored)." msgstr "" "Active l'envoi par la molette de pouce de notifications THUMB_WHEEL HID++ " "(ce qui déclenche les règles Solaar qui sont ignorées sinon)." #: lib/logitech_receiver/settings_templates.py:491 msgid "Thumb Wheel Direction" msgstr "Direction de la molette de pouce" #: lib/logitech_receiver/settings_templates.py:492 msgid "Invert thumb wheel scroll direction." msgstr "Inverser la direction du défilement de la molette de pouce." #: lib/logitech_receiver/settings_templates.py:512 msgid "Onboard Profiles" msgstr "Profils embarqués" #: lib/logitech_receiver/settings_templates.py:513 msgid "" "Enable an onboard profile, which controls report rate, sensitivity, and " "button actions" msgstr "" "Activer un profil intégré, qui contrôle le taux de rapport, la sensibilité " "et les actions des boutons" #: lib/logitech_receiver/settings_templates.py:557 #: lib/logitech_receiver/settings_templates.py:590 msgid "Report Rate" msgstr "Taux de rapport" #: lib/logitech_receiver/settings_templates.py:559 #: lib/logitech_receiver/settings_templates.py:592 msgid "Frequency of device movement reports" msgstr "Fréquence des rapports de mouvement des périphériques" #: lib/logitech_receiver/settings_templates.py:559 #: lib/logitech_receiver/settings_templates.py:592 #: lib/logitech_receiver/settings_templates.py:1005 #: lib/logitech_receiver/settings_templates.py:1379 #: lib/logitech_receiver/settings_templates.py:1410 msgid "May need Onboard Profiles set to Disable to be effective." msgstr "" "Peut nécessiter de désactiver les profils embarqués pour être effectif." #: lib/logitech_receiver/settings_templates.py:620 msgid "Divert crown events" msgstr "Définir les évènements de la couronne" #: lib/logitech_receiver/settings_templates.py:621 msgid "" "Make crown send CROWN HID++ notifications (which trigger Solaar rules but " "are otherwise ignored)." msgstr "" "Active l'envoi par la couronne de notifications CROWN HID++ (ce qui " "déclenche les règles Solaar qui sont ignorées sinon)." #: lib/logitech_receiver/settings_templates.py:629 msgid "Crown smooth scroll" msgstr "Défilement fluide de la couronne" #: lib/logitech_receiver/settings_templates.py:630 msgid "Set crown smooth scroll" msgstr "Définir le défilement fluide de la couronne" #: lib/logitech_receiver/settings_templates.py:638 msgid "Divert G and M Keys" msgstr "Interception des touches G et M" #: lib/logitech_receiver/settings_templates.py:639 msgid "" "Make G and M keys send HID++ notifications (which trigger Solaar rules but " "are otherwise ignored)." msgstr "" "Active l'envoi par les touches G et M de notifications HID++ (ce qui " "déclenche les règles Solaar qui sont ignorées sinon)." #: lib/logitech_receiver/settings_templates.py:653 msgid "Scroll Wheel Ratcheted" msgstr "Molette mode cliquet" #: lib/logitech_receiver/settings_templates.py:654 msgid "" "Switch the mouse wheel between speed-controlled ratcheting and always " "freespin." msgstr "" "Basculez la molette de la souris entre le mode cliquet à vitesse contrôlée " "et toujours en roue libre." #: lib/logitech_receiver/settings_templates.py:656 msgid "Freespinning" msgstr "Roue libre" #: lib/logitech_receiver/settings_templates.py:656 msgid "Ratcheted" msgstr "Cliquet" #: lib/logitech_receiver/settings_templates.py:663 msgid "Scroll Wheel Ratchet Speed" msgstr "Vitesse de la molette mode cliquet" #: lib/logitech_receiver/settings_templates.py:665 msgid "" "Use the mouse wheel speed to switch between ratcheted and freespinning.\n" "The mouse wheel is always ratcheted at 50." msgstr "" "Utilisez la vitesse de la molette de la souris pour basculer entre le mode " "cliquet et le mode roue libre.\n" "La molette est toujours en mode cliquet à 50." #: lib/logitech_receiver/settings_templates.py:714 msgid "Key/Button Actions" msgstr "Actions des boutons" #: lib/logitech_receiver/settings_templates.py:716 msgid "Change the action for the key or button." msgstr "Modifiez l'action pour la touche ou pour le bouton." #: lib/logitech_receiver/settings_templates.py:718 msgid "Overridden by diversion." msgstr "Remplacé par interception." #: lib/logitech_receiver/settings_templates.py:720 msgid "" "Changing important actions (such as for the left mouse button) can result in " "an unusable system." msgstr "" "La modification d'importantes actions (comme le bouton gauche de la souris) " "pourrait rendre le système inutilisable." #: lib/logitech_receiver/settings_templates.py:886 msgid "Key/Button Diversion" msgstr "Interception des boutons/touches" #: lib/logitech_receiver/settings_templates.py:887 msgid "" "Make the key or button send HID++ notifications (Diverted) or initiate Mouse " "Gestures or Sliding DPI" msgstr "" "Active l'envoi par la touche ou le bouton de notifications HID++ " "(interception), ou une gestuelle de souris ou l'échelle de déplacement" #: lib/logitech_receiver/settings_templates.py:890 #: lib/logitech_receiver/settings_templates.py:891 #: lib/logitech_receiver/settings_templates.py:892 msgid "Diverted" msgstr "Interception" #: lib/logitech_receiver/settings_templates.py:890 #: lib/logitech_receiver/settings_templates.py:891 msgid "Mouse Gestures" msgstr "Gestuelle à la souris" #: lib/logitech_receiver/settings_templates.py:890 #: lib/logitech_receiver/settings_templates.py:891 #: lib/logitech_receiver/settings_templates.py:892 msgid "Regular" msgstr "Normal" #: lib/logitech_receiver/settings_templates.py:890 msgid "Sliding DPI" msgstr "Amplification du déplacement" #: lib/logitech_receiver/settings_templates.py:978 #: lib/logitech_receiver/settings_templates.py:1004 msgid "Sensitivity (DPI)" msgstr "Sensibilité (DPI)" #: lib/logitech_receiver/settings_templates.py:1081 msgid "Sensitivity Switching" msgstr "Basculement de la sensibilité" #: lib/logitech_receiver/settings_templates.py:1083 msgid "" "Switch the current sensitivity and the remembered sensitivity when the key " "or button is pressed.\n" "If there is no remembered sensitivity, just remember the current sensitivity" msgstr "" "Bascule la sensibilité actuelle et la sensibilité enregistrée quand la " "touche ou le bouton sont appuyés.\n" "S'il n'y aucune sensibilité enregistrée, rappelle uniquement la sensibilité " "courante" #: lib/logitech_receiver/settings_templates.py:1087 msgid "Off" msgstr "Eteint" #: lib/logitech_receiver/settings_templates.py:1118 msgid "Disable keys" msgstr "Désactiver les touches" #: lib/logitech_receiver/settings_templates.py:1119 msgid "Disable specific keyboard keys." msgstr "Désactiver des touches spécifiques du clavier." #: lib/logitech_receiver/settings_templates.py:1122 #, python-format msgid "Disables the %s key." msgstr "Désactive la touche %s." #: lib/logitech_receiver/settings_templates.py:1135 #: lib/logitech_receiver/settings_templates.py:1192 msgid "Set OS" msgstr "Définir le système d'exploitation" #: lib/logitech_receiver/settings_templates.py:1136 #: lib/logitech_receiver/settings_templates.py:1193 msgid "Change keys to match OS." msgstr "Modifier les touches pour correspondre au système d'exploitation." #: lib/logitech_receiver/settings_templates.py:1205 msgid "Change Host" msgstr "Changer d'hôte" #: lib/logitech_receiver/settings_templates.py:1206 msgid "Switch connection to a different host" msgstr "Commuter la connexion vers un autre hôte" #: lib/logitech_receiver/settings_templates.py:1230 msgid "Performs a left click." msgstr "Réalise un clic gauche." #: lib/logitech_receiver/settings_templates.py:1230 msgid "Single tap" msgstr "Une tape simple" #: lib/logitech_receiver/settings_templates.py:1231 msgid "Performs a right click." msgstr "Réalise un clic droit." #: lib/logitech_receiver/settings_templates.py:1231 msgid "Single tap with two fingers" msgstr "Une tape simple avec deux doigts" #: lib/logitech_receiver/settings_templates.py:1232 msgid "Single tap with three fingers" msgstr "Une tape simple avec trois doigts" #: lib/logitech_receiver/settings_templates.py:1236 msgid "Double tap" msgstr "Une double tape" #: lib/logitech_receiver/settings_templates.py:1236 msgid "Performs a double click." msgstr "Réalise un double clic." #: lib/logitech_receiver/settings_templates.py:1237 msgid "Double tap with two fingers" msgstr "Une double tape avec deux doigts" #: lib/logitech_receiver/settings_templates.py:1238 msgid "Double tap with three fingers" msgstr "Une double tape avec trois doigts" #: lib/logitech_receiver/settings_templates.py:1241 msgid "Drags items by dragging the finger after double tapping." msgstr "Déplace des items en faisant glisser le doigt après la double tape." #: lib/logitech_receiver/settings_templates.py:1241 msgid "Tap and drag" msgstr "Taper-glisser" #: lib/logitech_receiver/settings_templates.py:1243 msgid "Tap and drag with two fingers" msgstr "Taper-glisser avec deux doigts" #: lib/logitech_receiver/settings_templates.py:1244 msgid "Drags items by dragging the fingers after double tapping." msgstr "Déplace des items en faisant glisser les doigts après la double tape." #: lib/logitech_receiver/settings_templates.py:1246 msgid "Tap and drag with three fingers" msgstr "Taper-glisser avec trois doigts" #: lib/logitech_receiver/settings_templates.py:1249 msgid "Suppress tap and edge gestures" msgstr "Supprimer les tapes et les gestes" #: lib/logitech_receiver/settings_templates.py:1250 msgid "Disables tap and edge gestures (equivalent to pressing Fn+LeftClick)." msgstr "" "Désactive les tapes et les gestes de bords (équivaut à presser " "Fn+ClicGauche)." #: lib/logitech_receiver/settings_templates.py:1252 msgid "Scroll with one finger" msgstr "Défilement à un doigt" #: lib/logitech_receiver/settings_templates.py:1252 #: lib/logitech_receiver/settings_templates.py:1253 #: lib/logitech_receiver/settings_templates.py:1256 msgid "Scrolls." msgstr "Défilements." #: lib/logitech_receiver/settings_templates.py:1253 #: lib/logitech_receiver/settings_templates.py:1256 msgid "Scroll with two fingers" msgstr "Défilement à deux doigts" #: lib/logitech_receiver/settings_templates.py:1254 msgid "Scroll horizontally with two fingers" msgstr "Défilement horizontal à deux doigts" #: lib/logitech_receiver/settings_templates.py:1254 msgid "Scrolls horizontally." msgstr "Défile horizontalement." #: lib/logitech_receiver/settings_templates.py:1255 msgid "Scroll vertically with two fingers" msgstr "Défilement vertical à deux doigts" #: lib/logitech_receiver/settings_templates.py:1255 msgid "Scrolls vertically." msgstr "Défile verticalement." #: lib/logitech_receiver/settings_templates.py:1257 msgid "Inverts the scrolling direction." msgstr "Inverse la direction de défilement." #: lib/logitech_receiver/settings_templates.py:1257 msgid "Natural scrolling" msgstr "Défilement naturel" #: lib/logitech_receiver/settings_templates.py:1258 msgid "Enables the thumbwheel." msgstr "Active la molette de pouce." #: lib/logitech_receiver/settings_templates.py:1258 msgid "Thumbwheel" msgstr "Molette de pouce" #: lib/logitech_receiver/settings_templates.py:1269 #: lib/logitech_receiver/settings_templates.py:1273 msgid "Swipe from the top edge" msgstr "Glissement depuis le bord supérieur" #: lib/logitech_receiver/settings_templates.py:1270 msgid "Swipe from the left edge" msgstr "Glissement depuis le bord gauche" #: lib/logitech_receiver/settings_templates.py:1271 msgid "Swipe from the right edge" msgstr "Glissement depuis le bord droit" #: lib/logitech_receiver/settings_templates.py:1272 msgid "Swipe from the bottom edge" msgstr "Glissement depuis le bord inférieur" #: lib/logitech_receiver/settings_templates.py:1274 msgid "Swipe two fingers from the left edge" msgstr "Glissement à deux doigts depuis le bord gauche" #: lib/logitech_receiver/settings_templates.py:1275 msgid "Swipe two fingers from the right edge" msgstr "Glissement à deux doigts depuis le bord droit" #: lib/logitech_receiver/settings_templates.py:1276 msgid "Swipe two fingers from the bottom edge" msgstr "Glissement à deux doigts depuis le bord inférieur" #: lib/logitech_receiver/settings_templates.py:1277 msgid "Swipe two fingers from the top edge" msgstr "Glissement à deux doigts depuis le bord supérieur" #: lib/logitech_receiver/settings_templates.py:1278 #: lib/logitech_receiver/settings_templates.py:1282 msgid "Pinch to zoom out; spread to zoom in." msgstr "Pincer pour réduire le zoom; écarter pour agrandir le zoom." #: lib/logitech_receiver/settings_templates.py:1278 msgid "Zoom with two fingers." msgstr "Zoom à deux doigts." #: lib/logitech_receiver/settings_templates.py:1279 msgid "Pinch to zoom out." msgstr "Pincer pour réduire le zoom." #: lib/logitech_receiver/settings_templates.py:1280 msgid "Spread to zoom in." msgstr "Écarter pour agrandir le zoom." #: lib/logitech_receiver/settings_templates.py:1281 msgid "Zoom with three fingers." msgstr "Zoom à trois doigts." #: lib/logitech_receiver/settings_templates.py:1282 msgid "Zoom with two fingers" msgstr "Zoom à deux doigts" #: lib/logitech_receiver/settings_templates.py:1300 msgid "Pixel zone" msgstr "Zone de pixels" #: lib/logitech_receiver/settings_templates.py:1301 msgid "Ratio zone" msgstr "Zone de rapport" #: lib/logitech_receiver/settings_templates.py:1302 msgid "Scale factor" msgstr "Facteur d’échelle" #: lib/logitech_receiver/settings_templates.py:1302 msgid "Sets the cursor speed." msgstr "Définit la vitesse du curseur." #: lib/logitech_receiver/settings_templates.py:1306 msgid "Left" msgstr "Gauche" #: lib/logitech_receiver/settings_templates.py:1306 msgid "Left-most coordinate." msgstr "Coordonnée la plus à gauche." #: lib/logitech_receiver/settings_templates.py:1307 msgid "Bottom" msgstr "Bas" #: lib/logitech_receiver/settings_templates.py:1307 msgid "Bottom coordinate." msgstr "Coordonnée du bas." #: lib/logitech_receiver/settings_templates.py:1308 msgid "Width" msgstr "Largeur" #: lib/logitech_receiver/settings_templates.py:1308 msgid "Width." msgstr "Largeur." #: lib/logitech_receiver/settings_templates.py:1309 msgid "Height" msgstr "Hauteur" #: lib/logitech_receiver/settings_templates.py:1309 msgid "Height." msgstr "Hauteur." #: lib/logitech_receiver/settings_templates.py:1310 msgid "Cursor speed." msgstr "Vitesse du curseur." #: lib/logitech_receiver/settings_templates.py:1310 msgid "Scale" msgstr "Échelle" #: lib/logitech_receiver/settings_templates.py:1316 msgid "Gestures" msgstr "Gestes" #: lib/logitech_receiver/settings_templates.py:1317 msgid "Tweak the mouse/touchpad behaviour." msgstr "Personnaliser le comportement de la souris ou du pavé tactile." #: lib/logitech_receiver/settings_templates.py:1333 msgid "Gestures Diversion" msgstr "Interception de la gestuelle" #: lib/logitech_receiver/settings_templates.py:1334 msgid "Divert mouse/touchpad gestures." msgstr "Interception de la gestuelle de la souris ou du pavé tactile." #: lib/logitech_receiver/settings_templates.py:1350 msgid "Gesture params" msgstr "Paramètres de la gestuelle" #: lib/logitech_receiver/settings_templates.py:1351 msgid "Change numerical parameters of a mouse/touchpad." msgstr "Modifier les paramètres numérique de la souris ou du pavé tactile." #: lib/logitech_receiver/settings_templates.py:1375 msgid "M-Key LEDs" msgstr "LEDs de touche M" #: lib/logitech_receiver/settings_templates.py:1377 msgid "Control the M-Key LEDs." msgstr "Contrôler les LEDs de touche M." #: lib/logitech_receiver/settings_templates.py:1381 #: lib/logitech_receiver/settings_templates.py:1412 msgid "May need G Keys diverted to be effective." msgstr "" "Peut nécessiter que les touches G soient interceptées pour être effectif." #: lib/logitech_receiver/settings_templates.py:1387 #, python-format msgid "Lights up the %s key." msgstr "Allume la touche %s." #: lib/logitech_receiver/settings_templates.py:1406 msgid "MR-Key LED" msgstr "LED de touche MR" #: lib/logitech_receiver/settings_templates.py:1408 msgid "Control the MR-Key LED." msgstr "Contrôler la LED de touche MR." #: lib/logitech_receiver/settings_templates.py:1429 msgid "Persistent Key/Button Mapping" msgstr "Mappage touche/bouton persistant" #: lib/logitech_receiver/settings_templates.py:1431 msgid "Permanently change the mapping for the key or button." msgstr "Modifiez de manière permanente le mappage de la touche ou du bouton." #: lib/logitech_receiver/settings_templates.py:1433 msgid "" "Changing important keys or buttons (such as for the left mouse button) can " "result in an unusable system." msgstr "" "La modification de touches ou de boutons importants (comme le bouton gauche " "de la souris) pourrait rendre le système inutilisable." #: lib/logitech_receiver/settings_templates.py:1490 msgid "Sidetone" msgstr "Tonalité latérale" #: lib/logitech_receiver/settings_templates.py:1491 msgid "Set sidetone level." msgstr "Régler le niveau de tonalité latérale." #: lib/logitech_receiver/settings_templates.py:1500 msgid "Equalizer" msgstr "Égaliseur" #: lib/logitech_receiver/settings_templates.py:1501 msgid "Set equalizer levels." msgstr "Définir les niveaux de l'égaliseur." #: lib/logitech_receiver/settings_templates.py:1523 msgid "Hz" msgstr "Hz" #: lib/logitech_receiver/settings_templates.py:1529 msgid "Power Management" msgstr "Gestion de l'alimentation" #: lib/logitech_receiver/settings_templates.py:1530 msgid "Power off in minutes (0 for never)." msgstr "Éteindre en quelques minutes (0 pour jamais)." #: lib/logitech_receiver/settings_templates.py:1541 msgid "Brightness Control" msgstr "Contrôle de la luminosité" #: lib/logitech_receiver/settings_templates.py:1542 msgid "Control overall brightness" msgstr "Contrôle de la luminosité globale" #: lib/logitech_receiver/settings_templates.py:1585 #: lib/logitech_receiver/settings_templates.py:1637 msgid "LED Control" msgstr "Contrôle de LED" #: lib/logitech_receiver/settings_templates.py:1586 #: lib/logitech_receiver/settings_templates.py:1638 msgid "Switch control of LED zones between device and Solaar" msgstr "Basculer le contrôle des zones LED entre le périphérique et Solaar" #: lib/logitech_receiver/settings_templates.py:1601 #: lib/logitech_receiver/settings_templates.py:1648 msgid "LED Zone Effects" msgstr "Effet de zone LED" #: lib/logitech_receiver/settings_templates.py:1602 #: lib/logitech_receiver/settings_templates.py:1649 msgid "LED Control needs to be set to Solaar to be effective." msgstr "Le contrôle de LED doit être defini sur Solaar pour être effectif." #: lib/logitech_receiver/settings_templates.py:1602 #: lib/logitech_receiver/settings_templates.py:1649 msgid "Set effect for LED Zone" msgstr "Définir l'effet pour la zone LED" #: lib/logitech_receiver/settings_templates.py:1605 msgid "Speed" msgstr "Vitesse" #: lib/logitech_receiver/settings_templates.py:1606 msgid "Period" msgstr "Période" #: lib/logitech_receiver/settings_templates.py:1607 msgid "Intensity" msgstr "Intensité" #: lib/logitech_receiver/settings_templates.py:1608 msgid "Ramp" msgstr "" #: lib/logitech_receiver/settings_templates.py:1622 msgid "LEDs" msgstr "LEDs" #: lib/logitech_receiver/settings_templates.py:1659 msgid "Per-key Lighting" msgstr "Éclairage par touche" #: lib/logitech_receiver/settings_templates.py:1660 msgid "Control per-key lighting." msgstr "Contrôler l’éclairage par touche." #: lib/solaar/ui/__init__.py:103 msgid "Another Solaar process is already running so just expose its window" msgstr "" "Un autre processus Solaar est déjà en cours d'exécution, il suffit donc " "d'afficher sa fenêtre" #: lib/solaar/ui/about.py:38 msgid "" "Manages Logitech receivers,\n" "keyboards, mice, and tablets." msgstr "" "Gère les récepteurs, claviers,\n" "souris et tablettes Logitech." #: lib/solaar/ui/about.py:47 msgid "Additional Programming" msgstr "Programmation supplémentaire" #: lib/solaar/ui/about.py:48 msgid "GUI design" msgstr "Interface graphique" #: lib/solaar/ui/about.py:50 msgid "Testing" msgstr "Testeur" #: lib/solaar/ui/about.py:58 msgid "Logitech documentation" msgstr "Documentațion Logitech" #: lib/solaar/ui/action.py:83 lib/solaar/ui/action.py:87 #: lib/solaar/ui/window.py:189 msgid "Unpair" msgstr "Déconnecter" #: lib/solaar/ui/action.py:86 lib/solaar/ui/diversion_rules.py:102 msgid "Cancel" msgstr "Annuler" #: lib/solaar/ui/common.py:35 msgid "Permissions error" msgstr "Erreur de permissions" #: lib/solaar/ui/common.py:37 #, python-format msgid "" "Found a Logitech receiver or device (%s), but did not have permission to " "open it." msgstr "" "Un récepteur ou un périphérique Logitech (%s) a été trouvé, mais sans " "l'autorisation de l'ouvrir." #: lib/solaar/ui/common.py:39 msgid "" "If you've just installed Solaar, try disconnecting the receiver or device " "and then reconnecting it." msgstr "" "Si vous venez d'installer Solaar, essayez de déconnecter le récepteur ou le " "périphérique, puis de le reconnecter." #: lib/solaar/ui/common.py:42 msgid "Cannot connect to device error" msgstr "Impossible de se connecter au périphérique" #: lib/solaar/ui/common.py:44 #, python-format msgid "" "Found a Logitech receiver or device at %s, but encountered an error " "connecting to it." msgstr "" "Un récepteur ou un périphérique Logitech a été trouvé à %s, mais une erreur " "s'est produite lors de la connexion." #: lib/solaar/ui/common.py:46 msgid "" "Try disconnecting the device and then reconnecting it or turning it off and " "then on." msgstr "" "Essayez de déconnecter le périphérique, puis de le reconnecter ou de " "l'éteindre puis de le rallumer." #: lib/solaar/ui/common.py:49 msgid "Unpairing failed" msgstr "La déconnexion a échouée" #: lib/solaar/ui/common.py:51 #, python-brace-format msgid "Failed to unpair %{device} from %{receiver}." msgstr "Impossible de dissocier %{device} de %{receiver}." #: lib/solaar/ui/common.py:53 msgid "The receiver returned an error, with no further details." msgstr "Le récepteur a retourné une erreur, sans plus de détails." #: lib/solaar/ui/config_panel.py:228 msgid "Complete - ENTER to change" msgstr "Complet - ENTRER pour modifier" #: lib/solaar/ui/config_panel.py:228 msgid "Incomplete" msgstr "Incomplet" #: lib/solaar/ui/config_panel.py:470 lib/solaar/ui/config_panel.py:522 #, python-format msgid "%d value" msgid_plural "%d values" msgstr[0] "valeur %d" msgstr[1] "valeurs %d" #: lib/solaar/ui/config_panel.py:607 msgid "Changes allowed" msgstr "Modifications autorisées" #: lib/solaar/ui/config_panel.py:608 msgid "No changes allowed" msgstr "Aucune modification autorisée" #: lib/solaar/ui/config_panel.py:609 msgid "Ignore this setting" msgstr "Ignorer ce réglage" #: lib/solaar/ui/config_panel.py:653 msgid "Working" msgstr "En fonctionnement" #: lib/solaar/ui/config_panel.py:656 msgid "Read/write operation failed." msgstr "Les opérations de lecture/écriture ont échouée." #: lib/solaar/ui/diversion_rules.py:64 msgid "Built-in rules" msgstr "Règles pré-définies" #: lib/solaar/ui/diversion_rules.py:64 msgid "User-defined rules" msgstr "Règles définies par l'utilisateur" #: lib/solaar/ui/diversion_rules.py:66 lib/solaar/ui/diversion_rules.py:1029 msgid "Rule" msgstr "Règle" #: lib/solaar/ui/diversion_rules.py:67 lib/solaar/ui/diversion_rules.py:544 #: lib/solaar/ui/diversion_rules.py:671 msgid "Sub-rule" msgstr "Sous-règle" #: lib/solaar/ui/diversion_rules.py:69 msgid "[empty]" msgstr "[vide]" #: lib/solaar/ui/diversion_rules.py:93 msgid "Make changes permanent?" msgstr "Rendre les modifications permanentes ?" #: lib/solaar/ui/diversion_rules.py:98 msgid "Yes" msgstr "Oui" #: lib/solaar/ui/diversion_rules.py:100 msgid "No" msgstr "Non" #: lib/solaar/ui/diversion_rules.py:105 msgid "If you choose No, changes will be lost when Solaar is closed." msgstr "" "Si vous choisissez Non, les changements seront perdus à la fermeture de " "Solaar." #: lib/solaar/ui/diversion_rules.py:166 msgid "Solaar Rule Editor" msgstr "Éditeur de règle Solaar" #: lib/solaar/ui/diversion_rules.py:259 msgid "Save changes" msgstr "Sauvegarder les modifications" #: lib/solaar/ui/diversion_rules.py:264 msgid "Discard changes" msgstr "Annuler les modifications" #: lib/solaar/ui/diversion_rules.py:403 msgid "Insert here" msgstr "Insérer ici" #: lib/solaar/ui/diversion_rules.py:405 msgid "Insert above" msgstr "Insérer ci-dessus" #: lib/solaar/ui/diversion_rules.py:407 msgid "Insert below" msgstr "Insérer ci-dessous" #: lib/solaar/ui/diversion_rules.py:413 msgid "Insert new rule here" msgstr "Insérer une nouvelle règle ici" #: lib/solaar/ui/diversion_rules.py:415 msgid "Insert new rule above" msgstr "Insérer une nouvelle règle ci-dessus" #: lib/solaar/ui/diversion_rules.py:417 msgid "Insert new rule below" msgstr "Insérer une nouvelle règle ci-dessous" #: lib/solaar/ui/diversion_rules.py:462 msgid "Paste here" msgstr "Coller ici" #: lib/solaar/ui/diversion_rules.py:464 msgid "Paste above" msgstr "Coller ci-dessus" #: lib/solaar/ui/diversion_rules.py:466 msgid "Paste below" msgstr "Coller ci-dessous" #: lib/solaar/ui/diversion_rules.py:472 msgid "Paste rule here" msgstr "Coller la règle ici" #: lib/solaar/ui/diversion_rules.py:474 msgid "Paste rule above" msgstr "Coller la règle ci-dessus" #: lib/solaar/ui/diversion_rules.py:476 msgid "Paste rule below" msgstr "Coller la règle ci-dessous" #: lib/solaar/ui/diversion_rules.py:480 msgid "Paste rule" msgstr "Coller la règle" #: lib/solaar/ui/diversion_rules.py:509 msgid "Flatten" msgstr "Aplanir" #: lib/solaar/ui/diversion_rules.py:542 msgid "Insert" msgstr "Insérer" #: lib/solaar/ui/diversion_rules.py:545 lib/solaar/ui/diversion_rules.py:673 #: lib/solaar/ui/diversion_rules.py:1061 msgid "Or" msgstr "Ou" #: lib/solaar/ui/diversion_rules.py:546 lib/solaar/ui/diversion_rules.py:672 #: lib/solaar/ui/diversion_rules.py:1047 msgid "And" msgstr "Et" #: lib/solaar/ui/diversion_rules.py:548 msgid "Condition" msgstr "Condition" #: lib/solaar/ui/diversion_rules.py:550 lib/solaar/ui/rule_conditions.py:146 msgid "Feature" msgstr "Fonctionnalité" #: lib/solaar/ui/diversion_rules.py:551 lib/solaar/ui/rule_conditions.py:181 msgid "Report" msgstr "Rapport" #: lib/solaar/ui/diversion_rules.py:552 lib/solaar/ui/rule_conditions.py:60 msgid "Process" msgstr "Processus" #: lib/solaar/ui/diversion_rules.py:553 msgid "Mouse process" msgstr "Processus de la souris" #: lib/solaar/ui/diversion_rules.py:554 lib/solaar/ui/rule_conditions.py:218 msgid "Modifiers" msgstr "Modificateurs" #: lib/solaar/ui/diversion_rules.py:555 lib/solaar/ui/rule_conditions.py:270 msgid "Key" msgstr "Touche" #: lib/solaar/ui/diversion_rules.py:556 lib/solaar/ui/rule_conditions.py:311 msgid "KeyIsDown" msgstr "" #: lib/solaar/ui/diversion_rules.py:557 lib/solaar/ui/diversion_rules.py:1349 msgid "Active" msgstr "Actif" #: lib/solaar/ui/diversion_rules.py:558 lib/solaar/ui/diversion_rules.py:1306 #: lib/solaar/ui/diversion_rules.py:1358 lib/solaar/ui/diversion_rules.py:1408 msgid "Device" msgstr "Périphérique" #: lib/solaar/ui/diversion_rules.py:559 lib/solaar/ui/diversion_rules.py:1384 msgid "Host" msgstr "Hôte" #: lib/solaar/ui/diversion_rules.py:560 lib/solaar/ui/diversion_rules.py:1426 msgid "Setting" msgstr "Réglage" #: lib/solaar/ui/diversion_rules.py:561 lib/solaar/ui/rule_conditions.py:326 #: lib/solaar/ui/rule_conditions.py:375 msgid "Test" msgstr "Test" #: lib/solaar/ui/diversion_rules.py:562 lib/solaar/ui/rule_conditions.py:500 msgid "Test bytes" msgstr "Octets de test" #: lib/solaar/ui/diversion_rules.py:563 lib/solaar/ui/rule_conditions.py:601 msgid "Mouse Gesture" msgstr "Geste à la souris" #: lib/solaar/ui/diversion_rules.py:567 msgid "Action" msgstr "Action" #: lib/solaar/ui/diversion_rules.py:569 lib/solaar/ui/rule_actions.py:132 msgid "Key press" msgstr "Appui de touche" #: lib/solaar/ui/diversion_rules.py:570 lib/solaar/ui/rule_actions.py:183 msgid "Mouse scroll" msgstr "Défilement de la souris" #: lib/solaar/ui/diversion_rules.py:571 lib/solaar/ui/rule_actions.py:244 msgid "Mouse click" msgstr "Clic de la souris" #: lib/solaar/ui/diversion_rules.py:572 msgid "Set" msgstr "Définir" #: lib/solaar/ui/diversion_rules.py:573 lib/solaar/ui/rule_actions.py:314 msgid "Execute" msgstr "Exécuter" #: lib/solaar/ui/diversion_rules.py:574 lib/solaar/ui/diversion_rules.py:1093 msgid "Later" msgstr "Plus tard" #: lib/solaar/ui/diversion_rules.py:603 msgid "Insert new rule" msgstr "Insérer nouvelle règle" #: lib/solaar/ui/diversion_rules.py:623 lib/solaar/ui/rule_actions.py:74 #: lib/solaar/ui/rule_actions.py:273 lib/solaar/ui/rule_conditions.py:548 msgid "Delete" msgstr "Effacer" #: lib/solaar/ui/diversion_rules.py:645 msgid "Negate" msgstr "Nier" #: lib/solaar/ui/diversion_rules.py:669 msgid "Wrap with" msgstr "Entoure" #: lib/solaar/ui/diversion_rules.py:691 msgid "Cut" msgstr "Couper" #: lib/solaar/ui/diversion_rules.py:706 msgid "Paste" msgstr "Coller" #: lib/solaar/ui/diversion_rules.py:712 msgid "Copy" msgstr "Copier" #: lib/solaar/ui/diversion_rules.py:1010 msgid "This editor does not support the selected rule component yet." msgstr "Cet éditeur ne gère pas encore le composant de règle sélectionné." #: lib/solaar/ui/diversion_rules.py:1072 msgid "" "Number of seconds to delay. Delay between 0 and 1 is done with higher " "precision." msgstr "" "Nombre de secondes de report. Le délai entre 0 et 1 est effectué avec une " "plus grande précision." #: lib/solaar/ui/diversion_rules.py:1111 msgid "Not" msgstr "Non" #: lib/solaar/ui/diversion_rules.py:1135 msgid "Toggle" msgstr "Basculer" #: lib/solaar/ui/diversion_rules.py:1136 msgid "True" msgstr "Vrai" #: lib/solaar/ui/diversion_rules.py:1137 msgid "False" msgstr "Faux" #: lib/solaar/ui/diversion_rules.py:1150 msgid "Unsupported setting" msgstr "Réglage non pris en charge" #: lib/solaar/ui/diversion_rules.py:1312 lib/solaar/ui/diversion_rules.py:1332 #: lib/solaar/ui/diversion_rules.py:1414 lib/solaar/ui/diversion_rules.py:1659 #: lib/solaar/ui/diversion_rules.py:1677 msgid "Originating device" msgstr "Périphérique d'origine" #: lib/solaar/ui/diversion_rules.py:1345 msgid "Device is active and its settings can be changed." msgstr "Le périphérique est actif et ses paramètres peuvent être modifiés." #: lib/solaar/ui/diversion_rules.py:1354 msgid "Device that originated the current notification." msgstr "Périphérique à l’origine de la notification actuelle." #: lib/solaar/ui/diversion_rules.py:1367 msgid "Name of host computer." msgstr "Nom de l'ordinateur hôte." #: lib/solaar/ui/diversion_rules.py:1435 msgid "Value" msgstr "Valeur" #: lib/solaar/ui/diversion_rules.py:1444 msgid "Item" msgstr "Item" #: lib/solaar/ui/diversion_rules.py:1718 msgid "Change setting on device" msgstr "Modifier le réglage sur le périphérique" #: lib/solaar/ui/diversion_rules.py:1734 msgid "Setting on device" msgstr "Réglage sur le périphérique" #: lib/solaar/ui/notify.py:118 msgid "unspecified reason" msgstr "raison non précisée" #: lib/solaar/ui/pair_window.py:37 lib/solaar/ui/pair_window.py:155 #, python-format msgid "%(receiver_name)s: pair new device" msgstr "%(receiver_name)s : jumele le nouveau périphérique" #: lib/solaar/ui/pair_window.py:39 msgid "Bolt receivers are only compatible with Bolt devices." msgstr "" "Les récepteurs Bolt ne sont compatibles qu'avec des périphériques Bolt." #: lib/solaar/ui/pair_window.py:41 msgid "Press a pairing button or key until the pairing light flashes quickly." msgstr "" "Appuyez sur un bouton ou une touche d'appairage jusqu'à ce que le voyant " "clignote rapidement." #: lib/solaar/ui/pair_window.py:44 msgid "Unifying receivers are only compatible with Unifying devices." msgstr "" "Les récepteurs Unifying ne sont compatibles qu'avec des périphériques " "Unifying." #: lib/solaar/ui/pair_window.py:46 msgid "Other receivers are only compatible with a few devices." msgstr "" "Les autres récepteurs sont uniquement compatibles avec quelques " "périphériques." #: lib/solaar/ui/pair_window.py:48 msgid "Turn on the device you want to pair." msgstr "Allumez le périphérique que vous souhaitez jumeler." #: lib/solaar/ui/pair_window.py:49 msgid "The device must not be paired with a nearby powered-on receiver." msgstr "" "Le périphérique doit être jumelé avec un récepteur de proximité allumé." #: lib/solaar/ui/pair_window.py:51 msgid "If the device is already turned on, turn it off and on again." msgstr "" "Si le périphérique est déjà allumé, éteignez-le et rallumez-le à nouveau." #: lib/solaar/ui/pair_window.py:55 #, python-format msgid "" "\n" "\n" "This receiver has %d pairing remaining." msgid_plural "" "\n" "\n" "This receiver has %d pairings remaining." msgstr[0] "" "\n" "\n" "Ce récepteur a %d jumelage restant." msgstr[1] "" "\n" "\n" "Ce récepteur a %d jumelages restants." #: lib/solaar/ui/pair_window.py:61 msgid "" "\n" "Cancelling at this point will not use up a pairing." msgstr "" "\n" "L'annulation à ce stade n'utilisera pas de jumelage." #: lib/solaar/ui/pair_window.py:156 #, python-format msgid "Enter passcode on %(name)s." msgstr "Entrer le mot de passe sur %(name)s." #: lib/solaar/ui/pair_window.py:159 #, python-format msgid "Type %(passcode)s and then press the enter key." msgstr "Taper %(passcode)s puis appuyez sur la touche Entrée." #: lib/solaar/ui/pair_window.py:162 msgid "left" msgstr "gauche" #: lib/solaar/ui/pair_window.py:162 msgid "right" msgstr "droit" #: lib/solaar/ui/pair_window.py:164 #, python-format msgid "" "Press %(code)s\n" "and then press left and right buttons simultaneously." msgstr "" "Appuyez %(code)s\n" "puis appuyez simultanément sur les boutons gauche et droit." #: lib/solaar/ui/pair_window.py:195 msgid "The wireless link is not encrypted" msgstr "La connexion sans fil n'est pas chiffrée" #: lib/solaar/ui/pair_window.py:200 msgid "Found a new device:" msgstr "Nouveau périphérique disponible :" #: lib/solaar/ui/pair_window.py:221 msgid "Pairing failed" msgstr "Le jumelage a échoué" #: lib/solaar/ui/pair_window.py:223 msgid "Make sure your device is within range, and has a decent battery charge." msgstr "" "Assurez-vous que votre périphérique soit à portée, et que sa batterie soit " "suffisamment chargée." #: lib/solaar/ui/pair_window.py:225 msgid "A new device was detected, but it is not compatible with this receiver." msgstr "" "Un nouveau périphérique a été détecté, mais il n'est pas compatible avec ce " "récepteur." #: lib/solaar/ui/pair_window.py:227 msgid "More paired devices than receiver can support." msgstr "" "Il y a plus de périphériques jumelés que le récepteur ne peut en supporter." #: lib/solaar/ui/pair_window.py:229 msgid "No further details are available about the error." msgstr "Aucun autre détail n'est disponible à propos de l'erreur." #: lib/solaar/ui/rule_actions.py:48 msgid "" "Simulate a chorded key click or depress or release.\n" "On Wayland requires write access to /dev/uinput." msgstr "" "Simule un clic combiné ou une pression ou un relâchement.\n" "Sous Wayland cela nécessite un accès en écriture au périphérique /dev/uinput." #: lib/solaar/ui/rule_actions.py:53 msgid "Add key" msgstr "Ajouter une touche" #: lib/solaar/ui/rule_actions.py:56 msgid "Click" msgstr "Clic" #: lib/solaar/ui/rule_actions.py:59 msgid "Depress" msgstr "Appuyer" #: lib/solaar/ui/rule_actions.py:62 msgid "Release" msgstr "Relâcher" #: lib/solaar/ui/rule_actions.py:147 msgid "" "Simulate a mouse scroll.\n" "On Wayland requires write access to /dev/uinput." msgstr "" "Simule un défilement de souris.\n" "Sous Wayland cela nécessite un accès en écriture au périphérique /dev/uinput." #: lib/solaar/ui/rule_actions.py:203 msgid "" "Simulate a mouse click.\n" "On Wayland requires write access to /dev/uinput." msgstr "" "Simule un clic de souris.\n" "Sous Wayland cela nécessite un accès en écriture au périphérique /dev/uinput." #: lib/solaar/ui/rule_actions.py:206 msgid "Button" msgstr "Bouton" #: lib/solaar/ui/rule_actions.py:207 msgid "Count and Action" msgstr "Nombre et action" #: lib/solaar/ui/rule_actions.py:256 msgid "Execute a command with arguments." msgstr "Exécuter une commande avec paramètres." #: lib/solaar/ui/rule_actions.py:259 msgid "Add argument" msgstr "Ajouter un paramètre" #: lib/solaar/ui/rule_conditions.py:43 msgid "X11 active process. For use in X11 only." msgstr "Processus actif X11. Pour une utilisation dans X11 uniquement." #: lib/solaar/ui/rule_conditions.py:73 msgid "X11 mouse process. For use in X11 only." msgstr "Processus de la souris X11. Pour une utilisation dans X11 uniquement." #: lib/solaar/ui/rule_conditions.py:90 msgid "MouseProcess" msgstr "Processus de la souris" #: lib/solaar/ui/rule_conditions.py:114 msgid "Feature name of notification triggering rule processing." msgstr "" "Nom de la fonction de notification déclenchant le traitement de la règle." #: lib/solaar/ui/rule_conditions.py:161 msgid "Report number of notification triggering rule processing." msgstr "" "Indique le nombre de notifications déclenchant le traitement de la règle." #: lib/solaar/ui/rule_conditions.py:194 msgid "Active keyboard modifiers. Not always available in Wayland." msgstr "Modificateurs de clavier actifs. Pas toujours disponible sous Wayland." #: lib/solaar/ui/rule_conditions.py:234 msgid "" "Diverted key or button depressed or released.\n" "Use the Key/Button Diversion and Divert G Keys settings to divert keys and " "buttons." msgstr "" "Interception de touche ou bouton appuyé ou relâché.\n" "Utiliser l'interception de touche/bouton et les paramètres Interception des " "touches G pour intercepter les touches et les boutons." #: lib/solaar/ui/rule_conditions.py:243 msgid "Key down" msgstr "Enfoncement de touche" #: lib/solaar/ui/rule_conditions.py:246 msgid "Key up" msgstr "Relâchement de touche" #: lib/solaar/ui/rule_conditions.py:286 msgid "" "Diverted key or button is currently down.\n" "Use the Key/Button Diversion and Divert G Keys settings to divert keys and " "buttons." msgstr "" "La touche/le bouton intercepté est actuellement enfoncé.\n" "Utiliser l'interception de touche/bouton et les paramètres Interception des " "touches G pour intercepter les touches et les boutons." #: lib/solaar/ui/rule_conditions.py:324 msgid "Test condition on notification triggering rule processing." msgstr "" "Condition de test déclenchant le traitement de la règle en cas de " "notification." #: lib/solaar/ui/rule_conditions.py:328 msgid "Parameter" msgstr "Paramètre" #: lib/solaar/ui/rule_conditions.py:401 msgid "begin (inclusive)" msgstr "début (inclusif)" #: lib/solaar/ui/rule_conditions.py:402 msgid "end (exclusive)" msgstr "fin (exclusif)" #: lib/solaar/ui/rule_conditions.py:410 msgid "range" msgstr "plage" #: lib/solaar/ui/rule_conditions.py:413 msgid "minimum" msgstr "minimum" #: lib/solaar/ui/rule_conditions.py:414 msgid "maximum" msgstr "maximum" #: lib/solaar/ui/rule_conditions.py:416 #, python-format msgid "bytes %(0)d to %(1)d, ranging from %(2)d to %(3)d" msgstr "octets %(0)d à %(1)d, allant de %(2)d à %(3)d" #: lib/solaar/ui/rule_conditions.py:419 lib/solaar/ui/rule_conditions.py:420 msgid "mask" msgstr "masque" #: lib/solaar/ui/rule_conditions.py:421 #, python-format msgid "bytes %(0)d to %(1)d, mask %(2)d" msgstr "octets %(0)d à %(1)d, masque %(2)d" #: lib/solaar/ui/rule_conditions.py:430 msgid "" "Bit or range test on bytes in notification message triggering rule " "processing." msgstr "" "Bit ou plage d'octets dans un message de notification déclenchant le " "traitement d'une règle." #: lib/solaar/ui/rule_conditions.py:440 msgid "type" msgstr "type" #: lib/solaar/ui/rule_conditions.py:528 msgid "" "Mouse gesture with optional initiating button followed by zero or more mouse " "movements." msgstr "" "Geste de la souris avec bouton d'initialisation optionnel suivi ou non par " "des déplacements de souris." #: lib/solaar/ui/rule_conditions.py:533 msgid "Add movement" msgstr "Ajouter un mouvement" #: lib/solaar/ui/tray.py:50 msgid "No supported device found" msgstr "Aucun périphérique pris en charge trouvé" #: lib/solaar/ui/tray.py:55 lib/solaar/ui/window.py:310 #, python-format msgid "About %s" msgstr "À propos de %s" #: lib/solaar/ui/tray.py:56 lib/solaar/ui/window.py:308 #, python-format msgid "Quit %s" msgstr "Quitter %s" #: lib/solaar/ui/tray.py:267 lib/solaar/ui/tray.py:275 msgid "no receiver" msgstr "aucun récepteur" #: lib/solaar/ui/tray.py:288 lib/solaar/ui/tray.py:293 msgid "offline" msgstr "non connecté" #: lib/solaar/ui/tray.py:291 msgid "no status" msgstr "aucun statut" #: lib/solaar/ui/window.py:91 msgid "Scanning" msgstr "Balayage" #: lib/solaar/ui/window.py:122 msgid "Battery" msgstr "Batterie" #: lib/solaar/ui/window.py:125 msgid "Wireless Link" msgstr "Connexion sans fil" #: lib/solaar/ui/window.py:129 msgid "Lighting" msgstr "Éclairage" #: lib/solaar/ui/window.py:163 msgid "Show Technical Details" msgstr "Afficher les détails techniques" #: lib/solaar/ui/window.py:179 msgid "Pair new device" msgstr "Jumeler un nouveau périphérique" #: lib/solaar/ui/window.py:197 msgid "Select a device" msgstr "Sélectionner un périphérique" #: lib/solaar/ui/window.py:313 msgid "Rule Editor" msgstr "Éditeur de règles" #: lib/solaar/ui/window.py:517 msgid "Path" msgstr "Chemin" #: lib/solaar/ui/window.py:520 msgid "USB ID" msgstr "ID USB" #: lib/solaar/ui/window.py:523 lib/solaar/ui/window.py:525 #: lib/solaar/ui/window.py:540 lib/solaar/ui/window.py:542 msgid "Serial" msgstr "Numéro de série" #: lib/solaar/ui/window.py:529 msgid "Index" msgstr "Index" #: lib/solaar/ui/window.py:531 msgid "Wireless PID" msgstr "PID sans fil" #: lib/solaar/ui/window.py:533 msgid "Product ID" msgstr "Identifiant produit" #: lib/solaar/ui/window.py:535 msgid "Protocol" msgstr "Protocole" #: lib/solaar/ui/window.py:535 msgid "Unknown" msgstr "Inconnu" #: lib/solaar/ui/window.py:537 msgid "Polling rate" msgstr "Taux de scrutation" #: lib/solaar/ui/window.py:544 msgid "Unit ID" msgstr "ID d'unité" #: lib/solaar/ui/window.py:558 msgid "Notifications" msgstr "Notifications" #: lib/solaar/ui/window.py:602 msgid "No device paired." msgstr "Aucun périphérique jumelé." #: lib/solaar/ui/window.py:611 #, python-format msgid "Up to %(max_count)s device can be paired to this receiver." msgid_plural "Up to %(max_count)s devices can be paired to this receiver." msgstr[0] "Jusqu'à %(max_count)s périphérique peut être jumelé à ce récepteur." msgstr[1] "" "Jusqu'à %(max_count)s périphériques peuvent être jumelés à ce récepteur." #: lib/solaar/ui/window.py:622 #, python-format msgid "This receiver has %d pairing remaining." msgid_plural "This receiver has %d pairings remaining." msgstr[0] "Ce récepteur a %d jumelage restant." msgstr[1] "Ce récepteur a %d jumelages restants." #: lib/solaar/ui/window.py:679 msgid "Battery Voltage" msgstr "Tension de la batterie" #: lib/solaar/ui/window.py:681 msgid "Voltage reported by battery" msgstr "Tension indiquée par la batterie" #: lib/solaar/ui/window.py:683 msgid "Battery Level" msgstr "Niveau de la batterie" #: lib/solaar/ui/window.py:685 msgid "Approximate level reported by battery" msgstr "Niveau approximatif indiqué par la batterie" #: lib/solaar/ui/window.py:692 lib/solaar/ui/window.py:694 msgid "next reported " msgstr "prochain rapport " #: lib/solaar/ui/window.py:695 msgid " and next level to be reported." msgstr " et prochain niveau à rapporter." #: lib/solaar/ui/window.py:711 msgid "encrypted" msgstr "chiffrée" #: lib/solaar/ui/window.py:713 msgid "The wireless link between this device and its receiver is encrypted." msgstr "" "La connexion sans fil entre ce périphérique et son récepteur est chiffrée." #: lib/solaar/ui/window.py:715 msgid "not encrypted" msgstr "non chiffrée" #: lib/solaar/ui/window.py:719 msgid "" "The wireless link between this device and its receiver is not encrypted.\n" "This is a security issue for pointing devices, and a major security issue " "for text-input devices." msgstr "" "La connexion sans fil entre ce périphérique et son récepteur n'est pas " "chiffrée.\n" "Ceci est un problème de sécurité pour les dispositifs de pointage, et un " "problème majeur pour les périphériques de saisie de texte." #: lib/solaar/ui/window.py:735 #, python-format msgid "%(light_level)d lux" msgstr "%(light_level)d lux" #, python-format #~ msgid "%(rate)d ms (%(rate_hz)dHz)" #~ msgstr "%(rate)d ms (%(rate_hz)dHz)" #, python-format #~ msgid "Battery: %(level)s" #~ msgstr "Batterie : %(level)s" #, python-format #~ msgid "Battery: %(percent)d%%" #~ msgstr "Batterie : %(percent)d%%" #~ msgid "Divert G Keys" #~ msgstr "Définir les touches G" #~ msgid "" #~ "Enable onboard profiles, which often control report rate and keyboard " #~ "lighting" #~ msgstr "" #~ "Active les profils embarqués, qui contrôlent souvent le taux de rapport " #~ "et l'éclairage du clavier" #~ msgid "Frequency of device polling, in milliseconds" #~ msgstr "Fréquence de scrutation du périphérique, en millisecondes" #, python-format #~ msgid "Lighting: %(level)s lux" #~ msgstr "Éclairage : %(level)s lux" #~ msgid "" #~ "Make G keys send GKEY HID++ notifications (which trigger Solaar rules but " #~ "are otherwise ignored)." #~ msgstr "" #~ "Active l'envoi par les touches G de notifications GKEY HID++ (ce qui " #~ "déclenche les règles Solaar qui sont ignorées sinon)." #~ msgid "May also make M keys and MR key send HID++ notifications" #~ msgstr "" #~ "Peut aussi permettre que les touches M et la touche MR envoient des " #~ "notifications HID++" #~ msgid "Number of seconds to delay." #~ msgstr "Nombre de secondes de report." #~ msgid "Only one device can be paired to this receiver." #~ msgstr "Un seul périphérique peut être jumelé à ce récepteur." #~ msgid "Polling Rate (ms)" #~ msgstr "Taux de scrutation (ms)" #~ msgid "Turn illumination on or off on keyboard." #~ msgstr "Activez ou désactivez l'éclairage sur le clavier." #~ msgid "You may have to first turn the device off and on again." #~ msgstr "Vous devriez tout d' abord éteindre le périphérique et le rallumer." #~ msgid "last known" #~ msgstr "dernière valeur connue" #~ msgid "none" #~ msgstr "aucun" Solaar-1.1.14/po/hr.po000066400000000000000000001713421473525213700144270ustar00rootroot00000000000000# Croatian translations for solaar package # Copyright (C) YEAR THE solaar'S COPYRIGHT HOLDER # This file is distributed under the same license as the solaar package. # gogo , 2014. # msgid "" msgstr "Project-Id-Version: solaar 1.0.1\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-12-28 17:40+0100\n" "PO-Revision-Date: 2022-10-28 10:37+0200\n" "Last-Translator: gogo \n" "Language-Team: Croatian \n" "Language: hr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 " "&& n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" "X-Generator: Poedit 3.0.1\n" #: lib/logitech_receiver/base_usb.py:46 msgid "Bolt Receiver" msgstr "Bolt prijemnik" #: lib/logitech_receiver/base_usb.py:57 msgid "Unifying Receiver" msgstr "Unifying prijemnik" #: lib/logitech_receiver/base_usb.py:67 lib/logitech_receiver/base_usb.py:78 #: lib/logitech_receiver/base_usb.py:90 lib/logitech_receiver/base_usb.py:102 #: lib/logitech_receiver/base_usb.py:114 msgid "Nano Receiver" msgstr "Nano prijemnik" #: lib/logitech_receiver/base_usb.py:124 msgid "Lightspeed Receiver" msgstr "Lightspeed prijemnik" #: lib/logitech_receiver/base_usb.py:133 msgid "EX100 Receiver 27 Mhz" msgstr "EX100 prijemnik 27 Mhz" #: lib/logitech_receiver/i18n.py:30 msgid "empty" msgstr "prazna" #: lib/logitech_receiver/i18n.py:31 msgid "critical" msgstr "kritično" #: lib/logitech_receiver/i18n.py:32 msgid "low" msgstr "slaba" #: lib/logitech_receiver/i18n.py:33 msgid "average" msgstr "prosječna" #: lib/logitech_receiver/i18n.py:34 msgid "good" msgstr "dobra" #: lib/logitech_receiver/i18n.py:35 msgid "full" msgstr "napunjena" #: lib/logitech_receiver/i18n.py:38 msgid "discharging" msgstr "pražnjenje" #: lib/logitech_receiver/i18n.py:39 msgid "recharging" msgstr "punjenje" #: lib/logitech_receiver/i18n.py:40 lib/solaar/ui/window.py:711 msgid "charging" msgstr "punjenje" #: lib/logitech_receiver/i18n.py:41 msgid "not charging" msgstr "ne puni se" #: lib/logitech_receiver/i18n.py:42 msgid "almost full" msgstr "uskoro puno" #: lib/logitech_receiver/i18n.py:43 msgid "charged" msgstr "napunjena" #: lib/logitech_receiver/i18n.py:44 msgid "slow recharge" msgstr "sporo punjenje" #: lib/logitech_receiver/i18n.py:45 msgid "invalid battery" msgstr "neispravna baterija" #: lib/logitech_receiver/i18n.py:46 msgid "thermal error" msgstr "toplinska greška" #: lib/logitech_receiver/i18n.py:47 msgid "error" msgstr "greška" #: lib/logitech_receiver/i18n.py:48 msgid "standard" msgstr "standardno" #: lib/logitech_receiver/i18n.py:49 msgid "fast" msgstr "brzo" #: lib/logitech_receiver/i18n.py:50 msgid "slow" msgstr "sporo" #: lib/logitech_receiver/i18n.py:53 msgid "device timeout" msgstr "istek čekanja uređaja" #: lib/logitech_receiver/i18n.py:54 msgid "device not supported" msgstr "uređaj nije podržan" #: lib/logitech_receiver/i18n.py:55 msgid "too many devices" msgstr "previše uređaja" #: lib/logitech_receiver/i18n.py:56 msgid "sequence timeout" msgstr "redoslijed isteka vremena" #: lib/logitech_receiver/i18n.py:59 lib/solaar/ui/window.py:572 msgid "Firmware" msgstr "Firmver" #: lib/logitech_receiver/i18n.py:60 msgid "Bootloader" msgstr "Učitač pokretanja" #: lib/logitech_receiver/i18n.py:61 msgid "Hardware" msgstr "Hardver" #: lib/logitech_receiver/i18n.py:62 msgid "Other" msgstr "Ostalo" #: lib/logitech_receiver/i18n.py:65 msgid "Left Button" msgstr "Lijeva tipka" #: lib/logitech_receiver/i18n.py:66 msgid "Right Button" msgstr "Desna tipka" #: lib/logitech_receiver/i18n.py:67 msgid "Middle Button" msgstr "Srednja tipka" #: lib/logitech_receiver/i18n.py:68 msgid "Back Button" msgstr "Tipka za natrag" #: lib/logitech_receiver/i18n.py:69 msgid "Forward Button" msgstr "Tipka za naprijed" #: lib/logitech_receiver/i18n.py:70 msgid "Mouse Gesture Button" msgstr "Tipka gesta miša" #: lib/logitech_receiver/i18n.py:71 msgid "Smart Shift" msgstr "Pametan pomak" #: lib/logitech_receiver/i18n.py:72 msgid "DPI Switch" msgstr "DPI prebacivač" #: lib/logitech_receiver/i18n.py:73 msgid "Left Tilt" msgstr "Lijevi nagib" #: lib/logitech_receiver/i18n.py:74 msgid "Right Tilt" msgstr "Desni nagib" #: lib/logitech_receiver/i18n.py:75 msgid "Left Click" msgstr "Lijevi klik" #: lib/logitech_receiver/i18n.py:76 msgid "Right Click" msgstr "Desni klik" #: lib/logitech_receiver/i18n.py:77 msgid "Mouse Middle Button" msgstr "Srednja tipka miša" #: lib/logitech_receiver/i18n.py:78 msgid "Mouse Back Button" msgstr "Tipka miša za natrag" #: lib/logitech_receiver/i18n.py:79 msgid "Mouse Forward Button" msgstr "Tipka miša za naprijed" #: lib/logitech_receiver/i18n.py:80 msgid "Gesture Button Navigation" msgstr "Tipka navigacije gesta" #: lib/logitech_receiver/i18n.py:81 msgid "Mouse Scroll Left Button" msgstr "Lijeva tipka pomicanja miša" #: lib/logitech_receiver/i18n.py:82 msgid "Mouse Scroll Right Button" msgstr "Desna tipka pomicanja miša" #: lib/logitech_receiver/i18n.py:85 msgid "pressed" msgstr "pritisak" #: lib/logitech_receiver/i18n.py:86 msgid "released" msgstr "otpuštanje" #: lib/logitech_receiver/notifications.py:75 #: lib/logitech_receiver/notifications.py:126 msgid "pairing lock is closed" msgstr "uparivanje je zatvoreno" #: lib/logitech_receiver/notifications.py:75 #: lib/logitech_receiver/notifications.py:126 msgid "pairing lock is open" msgstr "uparivanje je otvoreno" #: lib/logitech_receiver/notifications.py:92 msgid "discovery lock is closed" msgstr "otkrivanje je zatvoreno" #: lib/logitech_receiver/notifications.py:92 msgid "discovery lock is open" msgstr "otkrivanje je otvoreno" #: lib/logitech_receiver/notifications.py:224 lib/solaar/ui/notify.py:122 msgid "connected" msgstr "povezano" #: lib/logitech_receiver/notifications.py:224 msgid "disconnected" msgstr "nije povezano" #: lib/logitech_receiver/notifications.py:262 lib/solaar/ui/notify.py:120 msgid "unpaired" msgstr "neupareno" #: lib/logitech_receiver/notifications.py:304 msgid "powered on" msgstr "uključen" #: lib/logitech_receiver/settings.py:750 msgid "register" msgstr "registar" #: lib/logitech_receiver/settings.py:764 lib/logitech_receiver/settings.py:791 msgid "feature" msgstr "značajka" #: lib/logitech_receiver/settings_templates.py:139 msgid "Swap Fx function" msgstr "Zamijeni Fx funkciju" #: lib/logitech_receiver/settings_templates.py:140 msgid "When set, the F1..F12 keys will activate their special function,\n" "and you must hold the FN key to activate their standard function." msgstr "Kada je omogućeno, tipke F1..F12 će aktivirati svoje dodatne " "funkcije,\n" "a za aktivaciju njihovih osnovnih funkcija morate držati FN tipku." #: lib/logitech_receiver/settings_templates.py:142 msgid "When unset, the F1..F12 keys will activate their standard function,\n" "and you must hold the FN key to activate their special function." msgstr "Kada nije omogućeno, tipke F1..F12 će aktivirati svoje osnovne " "funkcije,\n" "a za aktivaciju njihovih dodatnih funkcija morate držati FN tipku." #: lib/logitech_receiver/settings_templates.py:149 msgid "Hand Detection" msgstr "Otkrivanje ruku" #: lib/logitech_receiver/settings_templates.py:150 msgid "Turn on illumination when the hands hover over the keyboard." msgstr "Uključite osvjetljenje kada ruke lebde nad tipkovnicom." #: lib/logitech_receiver/settings_templates.py:157 msgid "Scroll Wheel Smooth Scrolling" msgstr "Glatko pomicanje kotačićem" #: lib/logitech_receiver/settings_templates.py:158 #: lib/logitech_receiver/settings_templates.py:239 #: lib/logitech_receiver/settings_templates.py:267 msgid "High-sensitivity mode for vertical scroll with the wheel." msgstr "Visokoosjetljivi način okomitog pomicanja kotačićem." #: lib/logitech_receiver/settings_templates.py:165 msgid "Side Scrolling" msgstr "Bočno pomicanje kotačićem" #: lib/logitech_receiver/settings_templates.py:167 msgid "When disabled, pushing the wheel sideways sends custom button " "events\n" "instead of the standard side-scrolling events." msgstr "Kad je onemogućeno, guranje kotačića ustranu šalje prilagođeni " "događaj tipke\n" "umjesto uobičajanog događaja bočnog pomicanja." #: lib/logitech_receiver/settings_templates.py:177 msgid "Sensitivity (DPI - older mice)" msgstr "Osjetljivost (DPI - stariji miševi)" #: lib/logitech_receiver/settings_templates.py:178 #: lib/logitech_receiver/settings_templates.py:712 msgid "Mouse movement sensitivity" msgstr "Osjetljivost pomicanja miša" #: lib/logitech_receiver/settings_templates.py:208 #: lib/logitech_receiver/settings_templates.py:218 #: lib/logitech_receiver/settings_templates.py:225 msgid "Backlight" msgstr "Osvjetljenje" #: lib/logitech_receiver/settings_templates.py:209 #: lib/logitech_receiver/settings_templates.py:226 msgid "Set illumination time for keyboard." msgstr "Postavite vrijeme osvjetljenja tipkovnice." #: lib/logitech_receiver/settings_templates.py:219 msgid "Turn illumination on or off on keyboard." msgstr "Uključite ili isključite osvjetljenje tipkovnice." #: lib/logitech_receiver/settings_templates.py:237 msgid "Scroll Wheel High Resolution" msgstr "Visokoosjetljivi način pomicanja kotačićem" #: lib/logitech_receiver/settings_templates.py:240 #: lib/logitech_receiver/settings_templates.py:268 msgid "Set to ignore if scrolling is abnormally fast or slow" msgstr "Postavite na zanemareno ako je pomicanje prebrzo ili presporo" #: lib/logitech_receiver/settings_templates.py:247 #: lib/logitech_receiver/settings_templates.py:277 msgid "Scroll Wheel Diversion" msgstr "Preusmjeravanje pomicanja kotačića" #: lib/logitech_receiver/settings_templates.py:249 msgid "Make scroll wheel send LOWRES_WHEEL HID++ notifications (which " "trigger Solaar rules but are otherwise ignored)." msgstr "Neka kotačić pomicanja šalje LOWRES_WHEEL HID++ obavijesti (što " "pokreće Solaar pravila, u suprotnome ih zanemaruje)." #: lib/logitech_receiver/settings_templates.py:256 msgid "Scroll Wheel Direction" msgstr "Smjer pomicanja kotačića" #: lib/logitech_receiver/settings_templates.py:257 msgid "Invert direction for vertical scroll with wheel." msgstr "Obrnuti smjer za okomito pomicanje kotačićem." #: lib/logitech_receiver/settings_templates.py:265 msgid "Scroll Wheel Resolution" msgstr "Osjetljivost pomicanja kotačićem" #: lib/logitech_receiver/settings_templates.py:279 msgid "Make scroll wheel send HIRES_WHEEL HID++ notifications (which " "trigger Solaar rules but are otherwise ignored)." msgstr "Neka kotačić pomicanja šalje HIRES_WHEEL HID++ obavijesti (što " "pokreće Solaar pravila, u suprotnome ih zanemaruje)." #: lib/logitech_receiver/settings_templates.py:288 msgid "Sensitivity (Pointer Speed)" msgstr "Osjetljivost (Brzina pokazivača)" #: lib/logitech_receiver/settings_templates.py:289 msgid "Speed multiplier for mouse (256 is normal multiplier)." msgstr "Množitelj brzine za miš (256 je normalan množitelj)." #: lib/logitech_receiver/settings_templates.py:299 msgid "Thumb Wheel Diversion" msgstr "Preusmjeravanje pomicanja kotačića palca" #: lib/logitech_receiver/settings_templates.py:301 msgid "Make thumb wheel send THUMB_WHEEL HID++ notifications (which trigger " "Solaar rules but are otherwise ignored)." msgstr "Neka kotačić palca šalje THUMB_WHEEL HID++ obavijesti (što pokreće " "Solaar pravila, u suprotnome ih zanemaruje)." #: lib/logitech_receiver/settings_templates.py:310 msgid "Thumb Wheel Direction" msgstr "Smjer pomicanja kotačića palca" #: lib/logitech_receiver/settings_templates.py:311 msgid "Invert thumb wheel scroll direction." msgstr "Obrnuti smjer pomicanja kotačića palca." #: lib/logitech_receiver/settings_templates.py:319 msgid "Onboard Profiles" msgstr "Onboard profili" #: lib/logitech_receiver/settings_templates.py:320 msgid "Enable onboard profiles, which often control report rate and " "keyboard lighting" msgstr "Omogućite onboard profile, koji najčešće upravljaju brzinom " "pozivanja i osvjetljenjem tipkovnice" #: lib/logitech_receiver/settings_templates.py:330 msgid "Polling Rate (ms)" msgstr "Brzina pozivanja (ms)" #: lib/logitech_receiver/settings_templates.py:332 msgid "Frequency of device polling, in milliseconds" msgstr "Učestalost pozivanja uređaja, u milisekundama" #: lib/logitech_receiver/settings_templates.py:333 #: lib/logitech_receiver/settings_templates.py:1046 #: lib/logitech_receiver/settings_templates.py:1076 msgid "May need Onboard Profiles set to Disable to be effective." msgstr "Možda je potrebno postaviti Onboard profile na 'Onemogućeno' kako bi " "bili učinkoviti." #: lib/logitech_receiver/settings_templates.py:365 msgid "Divert crown events" msgstr "Preusmjeri crown događaje" #: lib/logitech_receiver/settings_templates.py:366 msgid "Make crown send CROWN HID++ notifications (which trigger Solaar " "rules but are otherwise ignored)." msgstr "Neka crown šalje CROWN HID++ obavijesti (što pokreće Solaar pravila, " "u suprotnome ih zanemaruje)." #: lib/logitech_receiver/settings_templates.py:374 msgid "Crown smooth scroll" msgstr "Crown glatko pomicanje" #: lib/logitech_receiver/settings_templates.py:375 msgid "Set crown smooth scroll" msgstr "Postavite Crown glatko pomicanje" #: lib/logitech_receiver/settings_templates.py:383 msgid "Divert G Keys" msgstr "Preusmjeri G tipke" #: lib/logitech_receiver/settings_templates.py:385 msgid "Make G keys send GKEY HID++ notifications (which trigger Solaar " "rules but are otherwise ignored)." msgstr "Neka G tipke šalju GKEY HID++ obavijesti (što pokreće Solaar " "pravila, u suprotnome ih zanemaruje)." #: lib/logitech_receiver/settings_templates.py:386 msgid "May also make M keys and MR key send HID++ notifications" msgstr "Neka isto M tipke i MR tipka šalje HID++ obavijesti" #: lib/logitech_receiver/settings_templates.py:402 msgid "Scroll Wheel Ratcheted" msgstr "Ustavljanje pomicanja kotačića" #: lib/logitech_receiver/settings_templates.py:403 msgid "Switch the mouse wheel between speed-controlled ratcheting and " "always freespin." msgstr "Prebaci kotačić miša između ustavljačkog brzinom upravljanog načina " "i slobodnog načina vrtnje kotačića." #: lib/logitech_receiver/settings_templates.py:405 msgid "Freespinning" msgstr "Slobodna vrtnja" #: lib/logitech_receiver/settings_templates.py:405 msgid "Ratcheted" msgstr "Ustavljanje" #: lib/logitech_receiver/settings_templates.py:412 msgid "Scroll Wheel Ratchet Speed" msgstr "Brzina ustavljanja pomicanja kotačića" #: lib/logitech_receiver/settings_templates.py:414 msgid "Use the mouse wheel speed to switch between ratcheted and " "freespinning.\n" "The mouse wheel is always ratcheted at 50." msgstr "Koristite brzinu kotačića miša za prebacivanje između ustavljačkog i " "slobodnog načina vrtnje kotačića.\n" "Kotačić miša je uvijek ustavljen pri 50." #: lib/logitech_receiver/settings_templates.py:463 msgid "Key/Button Actions" msgstr "Radnje tipki" #: lib/logitech_receiver/settings_templates.py:465 msgid "Change the action for the key or button." msgstr "Promijeni radnju za tipku." #: lib/logitech_receiver/settings_templates.py:465 msgid "Overridden by diversion." msgstr "Preusmjereno skretanjem." #: lib/logitech_receiver/settings_templates.py:466 msgid "Changing important actions (such as for the left mouse button) can " "result in an unusable system." msgstr "Promjena bitnih radnji (poput lijeve tipke miša) može prouzrokovati " "nestabilnosti u sustavu." #: lib/logitech_receiver/settings_templates.py:639 msgid "Key/Button Diversion" msgstr "Preusmjeravanje tipki" #: lib/logitech_receiver/settings_templates.py:640 msgid "Make the key or button send HID++ notifications (Diverted) or " "initiate Mouse Gestures or Sliding DPI" msgstr "Neka tipka ili šalje HID++ obavijesti (preusmjerene) ili pokreće " "gete mišem ili klizni DPI" #: lib/logitech_receiver/settings_templates.py:643 #: lib/logitech_receiver/settings_templates.py:644 #: lib/logitech_receiver/settings_templates.py:645 msgid "Diverted" msgstr "Preusmjerno" #: lib/logitech_receiver/settings_templates.py:643 #: lib/logitech_receiver/settings_templates.py:644 msgid "Mouse Gestures" msgstr "Geste miša" #: lib/logitech_receiver/settings_templates.py:643 #: lib/logitech_receiver/settings_templates.py:644 #: lib/logitech_receiver/settings_templates.py:645 msgid "Regular" msgstr "Normalna" #: lib/logitech_receiver/settings_templates.py:643 msgid "Sliding DPI" msgstr "Klizni DPI" #: lib/logitech_receiver/settings_templates.py:711 msgid "Sensitivity (DPI)" msgstr "Osjetljivost (DPI)" #: lib/logitech_receiver/settings_templates.py:752 msgid "Sensitivity Switching" msgstr "Prebacivanje osjetljivosti" #: lib/logitech_receiver/settings_templates.py:754 msgid "Switch the current sensitivity and the remembered sensitivity when " "the key or button is pressed.\n" "If there is no remembered sensitivity, just remember the current " "sensitivity" msgstr "Prebacivanje trenutne osjetljivosti i zapamćene osjetljivosti kada " "se tipka pritisne.\n" "Ako ne postoji zapamćena osjetljivost, zapamti trenutnu osjetljivost" #: lib/logitech_receiver/settings_templates.py:758 msgid "Off" msgstr "Isključeno" #: lib/logitech_receiver/settings_templates.py:791 msgid "Disable keys" msgstr "Onemogući tipke" #: lib/logitech_receiver/settings_templates.py:792 msgid "Disable specific keyboard keys." msgstr "Onemogući određene tipke tipkovnice." #: lib/logitech_receiver/settings_templates.py:795 #, python-format msgid "Disables the %s key." msgstr "Onemogućuje %s tipku." #: lib/logitech_receiver/settings_templates.py:809 #: lib/logitech_receiver/settings_templates.py:860 msgid "Set OS" msgstr "Postavi OS" #: lib/logitech_receiver/settings_templates.py:810 #: lib/logitech_receiver/settings_templates.py:861 msgid "Change keys to match OS." msgstr "Promijeni tipke tako da se podudaraju sa OS-om." #: lib/logitech_receiver/settings_templates.py:873 msgid "Change Host" msgstr "Promijeni računalo" #: lib/logitech_receiver/settings_templates.py:874 msgid "Switch connection to a different host" msgstr "Prebaci povezivanje na drugo računalo" #: lib/logitech_receiver/settings_templates.py:900 msgid "Performs a left click." msgstr "Izvodi lijevi klik." #: lib/logitech_receiver/settings_templates.py:900 msgid "Single tap" msgstr "Jednostruki dodir" #: lib/logitech_receiver/settings_templates.py:901 msgid "Performs a right click." msgstr "Izvodi desni klik." #: lib/logitech_receiver/settings_templates.py:901 msgid "Single tap with two fingers" msgstr "Jednostruki dodir s dva prsta" #: lib/logitech_receiver/settings_templates.py:902 msgid "Single tap with three fingers" msgstr "Jednostruki dodir s tri prsta" #: lib/logitech_receiver/settings_templates.py:906 msgid "Double tap" msgstr "Dvostruki dodir" #: lib/logitech_receiver/settings_templates.py:906 msgid "Performs a double click." msgstr "Izvodi dvostruki klik." #: lib/logitech_receiver/settings_templates.py:907 msgid "Double tap with two fingers" msgstr "Dvostruki dodir s dva prsta" #: lib/logitech_receiver/settings_templates.py:908 msgid "Double tap with three fingers" msgstr "Dvostruki dodir s tri prsta" #: lib/logitech_receiver/settings_templates.py:911 msgid "Drags items by dragging the finger after double tapping." msgstr "Povlačite stavke povlačenjem prsta nakon dvostrukog dodira." #: lib/logitech_receiver/settings_templates.py:911 msgid "Tap and drag" msgstr "Dodirni i povuci" #: lib/logitech_receiver/settings_templates.py:913 msgid "Drags items by dragging the fingers after double tapping." msgstr "Povlačite stavke povlačenjem prsta nakon dvostrukog dodira." #: lib/logitech_receiver/settings_templates.py:913 msgid "Tap and drag with two fingers" msgstr "Dodirni i povuci s dva prsta" #: lib/logitech_receiver/settings_templates.py:914 msgid "Tap and drag with three fingers" msgstr "Dodirni i povuci s tri prsta" #: lib/logitech_receiver/settings_templates.py:917 msgid "Disables tap and edge gestures (equivalent to pressing Fn+LeftClick)." msgstr "Onemogućuje dodir i rubne geste (ekvivalent pritiska na Fn+Lijevi " "klik)" #: lib/logitech_receiver/settings_templates.py:917 msgid "Suppress tap and edge gestures" msgstr "Onemogući dodir i rubne geste" #: lib/logitech_receiver/settings_templates.py:918 msgid "Scroll with one finger" msgstr "Pomicanje s jednim prstom" #: lib/logitech_receiver/settings_templates.py:918 #: lib/logitech_receiver/settings_templates.py:919 #: lib/logitech_receiver/settings_templates.py:922 msgid "Scrolls." msgstr "Pomicanje." #: lib/logitech_receiver/settings_templates.py:919 #: lib/logitech_receiver/settings_templates.py:922 msgid "Scroll with two fingers" msgstr "Pomicanje s dva prsta" #: lib/logitech_receiver/settings_templates.py:920 msgid "Scroll horizontally with two fingers" msgstr "Vodoravno pomicanje s dva prsta" #: lib/logitech_receiver/settings_templates.py:920 msgid "Scrolls horizontally." msgstr "Vodoravno pomicanje." #: lib/logitech_receiver/settings_templates.py:921 msgid "Scroll vertically with two fingers" msgstr "Okomito pomicanje s dva prsta" #: lib/logitech_receiver/settings_templates.py:921 msgid "Scrolls vertically." msgstr "Okomito pomicanje." #: lib/logitech_receiver/settings_templates.py:923 msgid "Inverts the scrolling direction." msgstr "Obrnite smjer pomicanja." #: lib/logitech_receiver/settings_templates.py:923 msgid "Natural scrolling" msgstr "Prirodno pomicanje" #: lib/logitech_receiver/settings_templates.py:924 msgid "Enables the thumbwheel." msgstr "Omogućite kotačić palca." #: lib/logitech_receiver/settings_templates.py:924 msgid "Thumbwheel" msgstr "Kotačić palca" #: lib/logitech_receiver/settings_templates.py:935 #: lib/logitech_receiver/settings_templates.py:939 msgid "Swipe from the top edge" msgstr "Prijeđite prstom od gornjeg ruba" #: lib/logitech_receiver/settings_templates.py:936 msgid "Swipe from the left edge" msgstr "Prijeđite prstom od lijevog ruba" #: lib/logitech_receiver/settings_templates.py:937 msgid "Swipe from the right edge" msgstr "Prijeđite prstom od desnog ruba" #: lib/logitech_receiver/settings_templates.py:938 msgid "Swipe from the bottom edge" msgstr "Prijeđite prstom od donjeg ruba" #: lib/logitech_receiver/settings_templates.py:940 msgid "Swipe two fingers from the left edge" msgstr "Prijeđite s dva prsta od lijevog ruba" #: lib/logitech_receiver/settings_templates.py:941 msgid "Swipe two fingers from the right edge" msgstr "Prijeđite s dva prsta od desnog ruba" #: lib/logitech_receiver/settings_templates.py:942 msgid "Swipe two fingers from the bottom edge" msgstr "Prijeđite s dva prsta od donjeg ruba" #: lib/logitech_receiver/settings_templates.py:943 msgid "Swipe two fingers from the top edge" msgstr "Prijeđite s dva prsta od gornjeg ruba" #: lib/logitech_receiver/settings_templates.py:944 #: lib/logitech_receiver/settings_templates.py:948 msgid "Pinch to zoom out; spread to zoom in." msgstr "Skupite prste za smanjivanje; raširite prste za uvećanje." #: lib/logitech_receiver/settings_templates.py:944 msgid "Zoom with two fingers." msgstr "Uvećanje/Smanjenje s dva prsta." #: lib/logitech_receiver/settings_templates.py:945 msgid "Pinch to zoom out." msgstr "Skupite prste za smanjivanje." #: lib/logitech_receiver/settings_templates.py:946 msgid "Spread to zoom in." msgstr "Raširite prste za uvećanje." #: lib/logitech_receiver/settings_templates.py:947 msgid "Zoom with three fingers." msgstr "Uvećanje/Smanjenje s tri prsta." #: lib/logitech_receiver/settings_templates.py:948 msgid "Zoom with two fingers" msgstr "Uvećanje/Smanjenje s dva prsta" #: lib/logitech_receiver/settings_templates.py:966 msgid "Pixel zone" msgstr "Zona piksela" #: lib/logitech_receiver/settings_templates.py:967 msgid "Ratio zone" msgstr "Zona omjera" #: lib/logitech_receiver/settings_templates.py:968 msgid "Scale factor" msgstr "Faktor veličine" #: lib/logitech_receiver/settings_templates.py:968 msgid "Sets the cursor speed." msgstr "Postavlja brzinu pokazivača." #: lib/logitech_receiver/settings_templates.py:972 msgid "Left" msgstr "Lijevo" #: lib/logitech_receiver/settings_templates.py:972 msgid "Left-most coordinate." msgstr "Najčešća lijeva koordinata." #: lib/logitech_receiver/settings_templates.py:973 msgid "Bottom" msgstr "Dno" #: lib/logitech_receiver/settings_templates.py:973 msgid "Bottom coordinate." msgstr "Koordinata na dnu." #: lib/logitech_receiver/settings_templates.py:974 msgid "Width" msgstr "Širina" #: lib/logitech_receiver/settings_templates.py:974 msgid "Width." msgstr "Širina." #: lib/logitech_receiver/settings_templates.py:975 msgid "Height" msgstr "Visina" #: lib/logitech_receiver/settings_templates.py:975 msgid "Height." msgstr "Visina." #: lib/logitech_receiver/settings_templates.py:976 msgid "Cursor speed." msgstr "Brzina pokazivača." #: lib/logitech_receiver/settings_templates.py:976 msgid "Scale" msgstr "Prilagodba veličine" #: lib/logitech_receiver/settings_templates.py:982 msgid "Gestures" msgstr "Geste" #: lib/logitech_receiver/settings_templates.py:983 msgid "Tweak the mouse/touchpad behaviour." msgstr "Prilagodite ponašanje miša/touchpada." #: lib/logitech_receiver/settings_templates.py:1000 msgid "Gestures Diversion" msgstr "Preusmjeravanje gesta" #: lib/logitech_receiver/settings_templates.py:1001 msgid "Divert mouse/touchpad gestures." msgstr "Preusmjerite ponašanje miša/touchpada." #: lib/logitech_receiver/settings_templates.py:1018 msgid "Gesture params" msgstr "Parametri gesta" #: lib/logitech_receiver/settings_templates.py:1019 msgid "Change numerical parameters of a mouse/touchpad." msgstr "Promijenite brojčane parametre miša/touchpada." #: lib/logitech_receiver/settings_templates.py:1044 msgid "M-Key LEDs" msgstr "LED osvjetljenje M-tipke" #: lib/logitech_receiver/settings_templates.py:1046 msgid "Control the M-Key LEDs." msgstr "Upravljajte LED osvjetljenjem M-tipke." #: lib/logitech_receiver/settings_templates.py:1047 #: lib/logitech_receiver/settings_templates.py:1077 msgid "May need G Keys diverted to be effective." msgstr "Možda je potrebno preusmjeravanje G tipka kako bi bile učinkovite." #: lib/logitech_receiver/settings_templates.py:1053 #, python-format msgid "Lights up the %s key." msgstr "Osvijetli %s tipku." #: lib/logitech_receiver/settings_templates.py:1074 msgid "MR-Key LED" msgstr "LED osvjetljenje MR-tipke" #: lib/logitech_receiver/settings_templates.py:1076 msgid "Control the MR-Key LED." msgstr "Upravljajte LED osvjetljenjem MR-tipke." #: lib/logitech_receiver/settings_templates.py:1095 msgid "Persistent Key/Button Mapping" msgstr "Trajno mapiranje tipka" #: lib/logitech_receiver/settings_templates.py:1097 msgid "Permanently change the mapping for the key or button." msgstr "Trajno promijenite mapiranje za tipku." #: lib/logitech_receiver/settings_templates.py:1098 msgid "Changing important keys or buttons (such as for the left mouse " "button) can result in an unusable system." msgstr "Promjena bitnih tipka (poput lijeve tipke miša) može prouzrokovati " "nestabilnosti u sustavu." #: lib/logitech_receiver/settings_templates.py:1157 msgid "Sidetone" msgstr "Bočni ton" #: lib/logitech_receiver/settings_templates.py:1158 msgid "Set sidetone level." msgstr "Postavi razinu bočnog tona." #: lib/logitech_receiver/settings_templates.py:1167 msgid "Equalizer" msgstr "Ekvalizator" #: lib/logitech_receiver/settings_templates.py:1168 msgid "Set equalizer levels." msgstr "Postavi razine ekvilizatora." #: lib/logitech_receiver/settings_templates.py:1191 msgid "Hz" msgstr "Hz" #: lib/logitech_receiver/settings_templates.py:1197 msgid "Power Management" msgstr "" #: lib/logitech_receiver/settings_templates.py:1198 msgid "Power off in minutes (0 for never)." msgstr "" #: lib/logitech_receiver/status.py:114 msgid "No paired devices." msgstr "Nema uparenih uređaja." #: lib/logitech_receiver/status.py:115 lib/solaar/ui/window.py:622 #, python-format msgid "%(count)s paired device." msgid_plural "%(count)s paired devices." msgstr[0] "%(count)s upareni uređaj." msgstr[1] "%(count)s uparena uređaja." msgstr[2] "%(count)s uparenih uređaj." #: lib/logitech_receiver/status.py:170 #, python-format msgid "Battery: %(level)s" msgstr "Baterija: %(level)s" #: lib/logitech_receiver/status.py:172 #, python-format msgid "Battery: %(percent)d%%" msgstr "Baterija: %(percent)d%%" #: lib/logitech_receiver/status.py:184 #, python-format msgid "Lighting: %(level)s lux" msgstr "Osvjetljenje: %(level)s lux" #: lib/logitech_receiver/status.py:239 #, python-format msgid "Battery: %(level)s (%(status)s)" msgstr "Baterija: %(level)s (%(status)s)" #: lib/logitech_receiver/status.py:241 #, python-format msgid "Battery: %(percent)d%% (%(status)s)" msgstr "Baterija: %(percent)d%% (%(status)s)" #: lib/solaar/ui/__init__.py:52 msgid "Permissions error" msgstr "Greška dozvole" #: lib/solaar/ui/__init__.py:54 #, python-format msgid "Found a Logitech receiver or device (%s), but did not have " "permission to open it." msgstr "" #: lib/solaar/ui/__init__.py:55 msgid "If you've just installed Solaar, try disconnecting the receiver or " "device and then reconnecting it." msgstr "" #: lib/solaar/ui/__init__.py:58 msgid "Cannot connect to device error" msgstr "Nemoguće povezivanje s uređajem, greška" #: lib/solaar/ui/__init__.py:60 #, python-format msgid "Found a Logitech receiver or device at %s, but encountered an error " "connecting to it." msgstr "Pronađen je Logitech prijemnik ili uređaj na %s, ali je naišao na " "grešku pri povezivanju." #: lib/solaar/ui/__init__.py:61 msgid "Try disconnecting the device and then reconnecting it or turning it " "off and then on." msgstr "" #: lib/solaar/ui/__init__.py:64 msgid "Unpairing failed" msgstr "Uparivanje neuspjelo" #: lib/solaar/ui/__init__.py:66 #, python-brace-format msgid "Failed to unpair %{device} from %{receiver}." msgstr "Neuspjelo uparivanje %{device} s %{receiver}." #: lib/solaar/ui/__init__.py:67 msgid "The receiver returned an error, with no further details." msgstr "Došlo je do greške kod prijemnika, bez više pojedinosti." #: lib/solaar/ui/__init__.py:177 msgid "Another Solaar process is already running so just expose its window" msgstr "Drugi Solaar proces je već pokrenut stoga samo prikaži njegov prozor" #: lib/solaar/ui/about.py:36 msgid "Manages Logitech receivers,\n" "keyboards, mice, and tablets." msgstr "Upravljajte Logitechovim prijemnicima,\n" "tipkovnicama, miševima, i tabletima." #: lib/solaar/ui/about.py:44 msgid "Additional Programming" msgstr "Dodatno programiranje" #: lib/solaar/ui/about.py:45 msgid "GUI design" msgstr "GUI dizajn" #: lib/solaar/ui/about.py:47 msgid "Testing" msgstr "Testiranje" #: lib/solaar/ui/about.py:54 msgid "Logitech documentation" msgstr "Logitech dokumentacija" #: lib/solaar/ui/action.py:85 lib/solaar/ui/action.py:89 #: lib/solaar/ui/window.py:197 msgid "Unpair" msgstr "Odpari" #: lib/solaar/ui/action.py:88 lib/solaar/ui/diversion_rules.py:150 msgid "Cancel" msgstr "Odustani" #: lib/solaar/ui/config_panel.py:212 msgid "Complete - ENTER to change" msgstr "Potpuno - ENTER za promjenu" #: lib/solaar/ui/config_panel.py:212 msgid "Incomplete" msgstr "Nepotpuno" #: lib/solaar/ui/config_panel.py:455 lib/solaar/ui/config_panel.py:507 #, python-format msgid "%d value" msgid_plural "%d values" msgstr[0] "%d vrijednost" msgstr[1] "%d vrijednosti" msgstr[2] "%d vrijednosti" #: lib/solaar/ui/config_panel.py:518 msgid "Changes allowed" msgstr "Promjene su dopuštene" #: lib/solaar/ui/config_panel.py:519 msgid "No changes allowed" msgstr "Promjene nisu dopuštene" #: lib/solaar/ui/config_panel.py:520 msgid "Ignore this setting" msgstr "Zanemari ovu postavku" #: lib/solaar/ui/config_panel.py:565 msgid "Working" msgstr "Izvodi se" #: lib/solaar/ui/config_panel.py:568 msgid "Read/write operation failed." msgstr "Radnja čitanja/pisanja neuspjela." #: lib/solaar/ui/diversion_rules.py:65 msgid "Built-in rules" msgstr "Ugrađena pravila" #: lib/solaar/ui/diversion_rules.py:65 msgid "User-defined rules" msgstr "Korisnički određena pravila" #: lib/solaar/ui/diversion_rules.py:67 lib/solaar/ui/diversion_rules.py:1083 msgid "Rule" msgstr "Pravilo" #: lib/solaar/ui/diversion_rules.py:68 lib/solaar/ui/diversion_rules.py:509 #: lib/solaar/ui/diversion_rules.py:636 msgid "Sub-rule" msgstr "Podpravilo" #: lib/solaar/ui/diversion_rules.py:70 msgid "[empty]" msgstr "[prazno]" #: lib/solaar/ui/diversion_rules.py:94 msgid "Solaar Rule Editor" msgstr "Solaar uređivač pravila" #: lib/solaar/ui/diversion_rules.py:141 msgid "Make changes permanent?" msgstr "Učini promjene trajnima?" #: lib/solaar/ui/diversion_rules.py:146 msgid "Yes" msgstr "Da" #: lib/solaar/ui/diversion_rules.py:148 msgid "No" msgstr "Ne" #: lib/solaar/ui/diversion_rules.py:153 msgid "If you choose No, changes will be lost when Solaar is closed." msgstr "Ako odaberete 'Ne', promjene će biti izgubljene kada se Solaar " "zatvori." #: lib/solaar/ui/diversion_rules.py:201 msgid "Save changes" msgstr "Spremi promjene" #: lib/solaar/ui/diversion_rules.py:206 msgid "Discard changes" msgstr "Odbaci promjene" #: lib/solaar/ui/diversion_rules.py:372 msgid "Insert here" msgstr "Umetni ovdje" #: lib/solaar/ui/diversion_rules.py:374 msgid "Insert above" msgstr "Umetni iznad" #: lib/solaar/ui/diversion_rules.py:376 msgid "Insert below" msgstr "Umetni ispod" #: lib/solaar/ui/diversion_rules.py:382 msgid "Insert new rule here" msgstr "Umetni novo pravilo ovdje" #: lib/solaar/ui/diversion_rules.py:384 msgid "Insert new rule above" msgstr "Umetni novo pravilo iznad" #: lib/solaar/ui/diversion_rules.py:386 msgid "Insert new rule below" msgstr "Umetni novo pravilo ispod" #: lib/solaar/ui/diversion_rules.py:427 msgid "Paste here" msgstr "Zalijepi ovdje" #: lib/solaar/ui/diversion_rules.py:429 msgid "Paste above" msgstr "Zalijepi iznad" #: lib/solaar/ui/diversion_rules.py:431 msgid "Paste below" msgstr "Zalijepi ispod" #: lib/solaar/ui/diversion_rules.py:437 msgid "Paste rule here" msgstr "Zalijepi pravilo ovdje" #: lib/solaar/ui/diversion_rules.py:439 msgid "Paste rule above" msgstr "Zalijepi pravilo iznad" #: lib/solaar/ui/diversion_rules.py:441 msgid "Paste rule below" msgstr "Zalijepi pravilo ispod" #: lib/solaar/ui/diversion_rules.py:445 msgid "Paste rule" msgstr "Zalijepi pravilo" #: lib/solaar/ui/diversion_rules.py:474 msgid "Flatten" msgstr "Poravnaj" #: lib/solaar/ui/diversion_rules.py:507 msgid "Insert" msgstr "Umetni" #: lib/solaar/ui/diversion_rules.py:510 lib/solaar/ui/diversion_rules.py:638 #: lib/solaar/ui/diversion_rules.py:1126 msgid "Or" msgstr "Ili" #: lib/solaar/ui/diversion_rules.py:511 lib/solaar/ui/diversion_rules.py:637 #: lib/solaar/ui/diversion_rules.py:1111 msgid "And" msgstr "I" #: lib/solaar/ui/diversion_rules.py:513 msgid "Condition" msgstr "Stanje" #: lib/solaar/ui/diversion_rules.py:515 lib/solaar/ui/diversion_rules.py:1292 msgid "Feature" msgstr "Značajka" #: lib/solaar/ui/diversion_rules.py:516 lib/solaar/ui/diversion_rules.py:1328 msgid "Report" msgstr "Prijavak" #: lib/solaar/ui/diversion_rules.py:517 lib/solaar/ui/diversion_rules.py:1204 msgid "Process" msgstr "Proces" #: lib/solaar/ui/diversion_rules.py:518 msgid "Mouse process" msgstr "Proces miša" #: lib/solaar/ui/diversion_rules.py:519 lib/solaar/ui/diversion_rules.py:1366 msgid "Modifiers" msgstr "Tipkovnički izmjenjivači" #: lib/solaar/ui/diversion_rules.py:520 lib/solaar/ui/diversion_rules.py:1419 msgid "Key" msgstr "Ključ" #: lib/solaar/ui/diversion_rules.py:521 lib/solaar/ui/diversion_rules.py:1461 msgid "KeyIsDown" msgstr "" #: lib/solaar/ui/diversion_rules.py:522 lib/solaar/ui/diversion_rules.py:2260 msgid "Active" msgstr "Aktivan" #: lib/solaar/ui/diversion_rules.py:523 lib/solaar/ui/diversion_rules.py:2218 #: lib/solaar/ui/diversion_rules.py:2270 lib/solaar/ui/diversion_rules.py:2323 msgid "Device" msgstr "Uređaj" #: lib/solaar/ui/diversion_rules.py:524 lib/solaar/ui/diversion_rules.py:2297 msgid "Host" msgstr "" #: lib/solaar/ui/diversion_rules.py:525 lib/solaar/ui/diversion_rules.py:2339 msgid "Setting" msgstr "Postavka" #: lib/solaar/ui/diversion_rules.py:526 lib/solaar/ui/diversion_rules.py:1477 #: lib/solaar/ui/diversion_rules.py:1526 msgid "Test" msgstr "Test" #: lib/solaar/ui/diversion_rules.py:527 lib/solaar/ui/diversion_rules.py:1643 msgid "Test bytes" msgstr "Testni bajtovi" #: lib/solaar/ui/diversion_rules.py:528 lib/solaar/ui/diversion_rules.py:1736 msgid "Mouse Gesture" msgstr "Gesta miša" #: lib/solaar/ui/diversion_rules.py:532 msgid "Action" msgstr "Radnja" #: lib/solaar/ui/diversion_rules.py:534 lib/solaar/ui/diversion_rules.py:1845 msgid "Key press" msgstr "Pritisak tipke" #: lib/solaar/ui/diversion_rules.py:535 lib/solaar/ui/diversion_rules.py:1897 msgid "Mouse scroll" msgstr "Pomicanje kotačića" #: lib/solaar/ui/diversion_rules.py:536 lib/solaar/ui/diversion_rules.py:1959 msgid "Mouse click" msgstr "Klik kotačića" #: lib/solaar/ui/diversion_rules.py:537 msgid "Set" msgstr "Postavka" #: lib/solaar/ui/diversion_rules.py:538 lib/solaar/ui/diversion_rules.py:2030 msgid "Execute" msgstr "Pokretanje" #: lib/solaar/ui/diversion_rules.py:539 lib/solaar/ui/diversion_rules.py:1158 msgid "Later" msgstr "Odgoda" #: lib/solaar/ui/diversion_rules.py:568 msgid "Insert new rule" msgstr "Umetni novo pravilo" #: lib/solaar/ui/diversion_rules.py:588 lib/solaar/ui/diversion_rules.py:1686 #: lib/solaar/ui/diversion_rules.py:1790 lib/solaar/ui/diversion_rules.py:1989 msgid "Delete" msgstr "Obriši" #: lib/solaar/ui/diversion_rules.py:610 msgid "Negate" msgstr "Negacija" #: lib/solaar/ui/diversion_rules.py:634 msgid "Wrap with" msgstr "Umetni iznad" #: lib/solaar/ui/diversion_rules.py:656 msgid "Cut" msgstr "Izreži" #: lib/solaar/ui/diversion_rules.py:671 msgid "Paste" msgstr "Zalijepi" #: lib/solaar/ui/diversion_rules.py:683 msgid "Copy" msgstr "Kopiraj" #: lib/solaar/ui/diversion_rules.py:1063 msgid "This editor does not support the selected rule component yet." msgstr "Ovaj uređivač još ne podržava komponentu odabira pravila." #: lib/solaar/ui/diversion_rules.py:1138 msgid "Number of seconds to delay." msgstr "Broj sekundi odgode." #: lib/solaar/ui/diversion_rules.py:1177 msgid "Not" msgstr "Ne" #: lib/solaar/ui/diversion_rules.py:1187 msgid "X11 active process. For use in X11 only." msgstr "X11 aktivni proces. Samo za korištenje u X11." #: lib/solaar/ui/diversion_rules.py:1218 msgid "X11 mouse process. For use in X11 only." msgstr "X11 proces miša. Samo za korištenje u X11." #: lib/solaar/ui/diversion_rules.py:1235 msgid "MouseProcess" msgstr "Proces miša" #: lib/solaar/ui/diversion_rules.py:1260 msgid "Feature name of notification triggering rule processing." msgstr "Naziv značajke obrade pravila za pokretanje obavijesti." #: lib/solaar/ui/diversion_rules.py:1308 msgid "Report number of notification triggering rule processing." msgstr "Prijavi broj obrade pravila za pokretanje obavijesti." #: lib/solaar/ui/diversion_rules.py:1342 msgid "Active keyboard modifiers. Not always available in Wayland." msgstr "Aktivni tipkovnički izmjenjivači. Nije uvijek dostupno u Waylandu." #: lib/solaar/ui/diversion_rules.py:1383 msgid "Diverted key or button depressed or released.\n" "Use the Key/Button Diversion and Divert G Keys settings to divert " "keys and buttons." msgstr "" #: lib/solaar/ui/diversion_rules.py:1392 msgid "Key down" msgstr "Tipka za dolje" #: lib/solaar/ui/diversion_rules.py:1395 msgid "Key up" msgstr "Tipka za gore" #: lib/solaar/ui/diversion_rules.py:1436 msgid "Diverted key or button is currently down.\n" "Use the Key/Button Diversion and Divert G Keys settings to divert " "keys and buttons." msgstr "" #: lib/solaar/ui/diversion_rules.py:1475 msgid "Test condition on notification triggering rule processing." msgstr "Uvjet testiranja za obradu pravila pokretanja obavijesti." #: lib/solaar/ui/diversion_rules.py:1479 msgid "Parameter" msgstr "" #: lib/solaar/ui/diversion_rules.py:1542 msgid "begin (inclusive)" msgstr "početak (uključiv)" #: lib/solaar/ui/diversion_rules.py:1543 msgid "end (exclusive)" msgstr "završetak (isključiv)" #: lib/solaar/ui/diversion_rules.py:1552 msgid "range" msgstr "raspon" #: lib/solaar/ui/diversion_rules.py:1554 msgid "minimum" msgstr "minimum" #: lib/solaar/ui/diversion_rules.py:1555 msgid "maximum" msgstr "maksimum" #: lib/solaar/ui/diversion_rules.py:1557 #, python-format msgid "bytes %(0)d to %(1)d, ranging from %(2)d to %(3)d" msgstr "bajtovi %(0)d do %(1)d, u rasponu od %(2)d do %(3)d" #: lib/solaar/ui/diversion_rules.py:1562 msgid "mask" msgstr "maska" #: lib/solaar/ui/diversion_rules.py:1563 #, python-format msgid "bytes %(0)d to %(1)d, mask %(2)d" msgstr "bajtovi %(0)d to %(1)d, maska %(2)d" #: lib/solaar/ui/diversion_rules.py:1573 msgid "Bit or range test on bytes in notification message triggering rule " "processing." msgstr "Test bita ili raspona na bajtovima u poruci obavijesti koja pokreće " "obradu pravila." #: lib/solaar/ui/diversion_rules.py:1583 msgid "type" msgstr "vrsta" #: lib/solaar/ui/diversion_rules.py:1666 msgid "Mouse gesture with optional initiating button followed by zero or " "more mouse movements." msgstr "Geste mišem s neobaveznom tipkom pokretanja nakon kojeg slijedi nula " "ili više pokreta mišem." #: lib/solaar/ui/diversion_rules.py:1671 msgid "Add movement" msgstr "Dodaj pokret" #: lib/solaar/ui/diversion_rules.py:1764 msgid "Simulate a chorded key click or depress or release.\n" "On Wayland requires write access to /dev/uinput." msgstr "Simuliraj klik tipke s akordima ili pritisnite ili pustite.\n" "Na Waylandu je potrebna dozvola pisanje u /dev/uinput." #: lib/solaar/ui/diversion_rules.py:1769 msgid "Add key" msgstr "Dodaj tipku" #: lib/solaar/ui/diversion_rules.py:1772 msgid "Click" msgstr "Klik" #: lib/solaar/ui/diversion_rules.py:1775 msgid "Depress" msgstr "Pritisak" #: lib/solaar/ui/diversion_rules.py:1778 msgid "Release" msgstr "Otpuštanje" #: lib/solaar/ui/diversion_rules.py:1861 msgid "Simulate a mouse scroll.\n" "On Wayland requires write access to /dev/uinput." msgstr "Simuliraj pomicanje kotačića miša.\n" "Na Waylandu je potrebna dozvola pisanje u /dev/uinput." #: lib/solaar/ui/diversion_rules.py:1918 msgid "Simulate a mouse click.\n" "On Wayland requires write access to /dev/uinput." msgstr "Simuliraj klik miša.\n" "Na Waylandu je potrebna dozvola pisanje u /dev/uinput." #: lib/solaar/ui/diversion_rules.py:1921 msgid "Button" msgstr "Tipka" #: lib/solaar/ui/diversion_rules.py:1922 msgid "Count and Action" msgstr "" #: lib/solaar/ui/diversion_rules.py:1972 msgid "Execute a command with arguments." msgstr "Pokreni naredbu s argumentima." #: lib/solaar/ui/diversion_rules.py:1975 msgid "Add argument" msgstr "Dodaj argument" #: lib/solaar/ui/diversion_rules.py:2050 msgid "Toggle" msgstr "Uklj/Isklj" #: lib/solaar/ui/diversion_rules.py:2050 msgid "True" msgstr "Istina" #: lib/solaar/ui/diversion_rules.py:2051 msgid "False" msgstr "Laž" #: lib/solaar/ui/diversion_rules.py:2065 msgid "Unsupported setting" msgstr "Nepodržana postavka" #: lib/solaar/ui/diversion_rules.py:2223 lib/solaar/ui/diversion_rules.py:2242 #: lib/solaar/ui/diversion_rules.py:2328 lib/solaar/ui/diversion_rules.py:2570 #: lib/solaar/ui/diversion_rules.py:2588 msgid "Originating device" msgstr "Izvorni uređaj" #: lib/solaar/ui/diversion_rules.py:2256 msgid "Device is active and its settings can be changed." msgstr "Uređaj je aktivan i njegove postavke se mogu promijeniti." #: lib/solaar/ui/diversion_rules.py:2266 msgid "Device that originated the current notification." msgstr "" #: lib/solaar/ui/diversion_rules.py:2280 msgid "Name of host computer." msgstr "" #: lib/solaar/ui/diversion_rules.py:2347 msgid "Value" msgstr "Vrijednost" #: lib/solaar/ui/diversion_rules.py:2355 msgid "Item" msgstr "Stavka" #: lib/solaar/ui/diversion_rules.py:2630 msgid "Change setting on device" msgstr "Promjena postavke na uređaju" #: lib/solaar/ui/diversion_rules.py:2647 msgid "Setting on device" msgstr "Postavka na uređaju" #: lib/solaar/ui/notify.py:124 lib/solaar/ui/tray.py:318 #: lib/solaar/ui/tray.py:323 lib/solaar/ui/window.py:739 msgid "offline" msgstr "nespojivo" #: lib/solaar/ui/pair_window.py:122 lib/solaar/ui/pair_window.py:256 #: lib/solaar/ui/pair_window.py:288 #, python-format msgid "%(receiver_name)s: pair new device" msgstr "%(receiver_name)s: upari novi uređaj" #: lib/solaar/ui/pair_window.py:123 #, python-format msgid "Enter passcode on %(name)s." msgstr "Upišite lozinku na %(name)s." #: lib/solaar/ui/pair_window.py:126 #, python-format msgid "Type %(passcode)s and then press the enter key." msgstr "Upišite %(passcode)s i zatim pritisnite Enter tipku." #: lib/solaar/ui/pair_window.py:129 msgid "left" msgstr "lijevo" #: lib/solaar/ui/pair_window.py:129 msgid "right" msgstr "desno" #: lib/solaar/ui/pair_window.py:131 #, python-format msgid "Press %(code)s\n" "and then press left and right buttons simultaneously." msgstr "Pritisnite %(code)s\n" "i zatim istovremeno pritisnite lijevu i desnu tipku." #: lib/solaar/ui/pair_window.py:188 msgid "Pairing failed" msgstr "Neuspjelo uparivanje" #: lib/solaar/ui/pair_window.py:190 msgid "Make sure your device is within range, and has a decent battery " "charge." msgstr "Pobrinite se da je vaš uređaj u dometu bežičnog povezivanja i da je " "dovoljno napunjen." #: lib/solaar/ui/pair_window.py:192 msgid "A new device was detected, but it is not compatible with this " "receiver." msgstr "Novi uređaj je otkriven, ali nije kompatibilan s ovim uređajem." #: lib/solaar/ui/pair_window.py:194 msgid "More paired devices than receiver can support." msgstr "Postoji više uparenih uređaja nego što prijemnik podržava." #: lib/solaar/ui/pair_window.py:196 msgid "No further details are available about the error." msgstr "Nema dostupnih više pojedinosti o ovoj grešci." #: lib/solaar/ui/pair_window.py:210 msgid "Found a new device:" msgstr "Novi uređaj je pronađen:" #: lib/solaar/ui/pair_window.py:235 msgid "The wireless link is not encrypted" msgstr "Bežično povezivanje nije šifrirano" #: lib/solaar/ui/pair_window.py:264 msgid "Unifying receivers are only compatible with Unifying devices." msgstr "" #: lib/solaar/ui/pair_window.py:266 msgid "Bolt receivers are only compatible with Bolt devices." msgstr "" #: lib/solaar/ui/pair_window.py:268 msgid "Other receivers are only compatible with a few devices." msgstr "" #: lib/solaar/ui/pair_window.py:270 msgid "The device must not be paired with a nearby powered-on receiver." msgstr "" #: lib/solaar/ui/pair_window.py:274 msgid "Press a pairing button or key until the pairing light flashes " "quickly." msgstr "Pritisnite tipku uparivanja sve dok svjetlo uparivanja ne počne brzo " "treptati." #: lib/solaar/ui/pair_window.py:276 msgid "You may have to first turn the device off and on again." msgstr "Možda trebate prvo isključiti uređaj pa ponovno uključiti." #: lib/solaar/ui/pair_window.py:278 msgid "Turn on the device you want to pair." msgstr "Uključite uređaj koji želite upariti." #: lib/solaar/ui/pair_window.py:280 msgid "If the device is already turned on, turn it off and on again." msgstr "Ako je uređaj već uključen, isključite ga i ponovno uključite." #: lib/solaar/ui/pair_window.py:283 #, python-format msgid "\n" "\n" "This receiver has %d pairing remaining." msgid_plural "\n" "\n" "This receiver has %d pairings remaining." msgstr[0] "\n" "\n" "Ovaj prijemnik ima još %d preostalo uparivanje." msgstr[1] "\n" "\n" "Ovaj prijemnik ima još %d preostala uparivanja." msgstr[2] "\n" "\n" "Ovaj prijemnik ima još %d preostalih uparivanja." #: lib/solaar/ui/pair_window.py:286 msgid "\n" "Cancelling at this point will not use up a pairing." msgstr "\n" "Prekidanje u ovom trenutku neće izvršiti uparivanje." #: lib/solaar/ui/tray.py:58 msgid "No supported device found" msgstr "" #: lib/solaar/ui/tray.py:64 lib/solaar/ui/window.py:319 #, python-format msgid "About %s" msgstr "O %su" #: lib/solaar/ui/tray.py:65 lib/solaar/ui/window.py:317 #, python-format msgid "Quit %s" msgstr "Zatvori %s" #: lib/solaar/ui/tray.py:297 lib/solaar/ui/tray.py:305 msgid "no receiver" msgstr "nema prijemnika" #: lib/solaar/ui/tray.py:321 msgid "no status" msgstr "bez stanja" #: lib/solaar/ui/window.py:96 msgid "Scanning" msgstr "Pretraživanje" #: lib/solaar/ui/window.py:129 msgid "Battery" msgstr "Baterija" #: lib/solaar/ui/window.py:132 msgid "Wireless Link" msgstr "Bežično povezivanje" #: lib/solaar/ui/window.py:136 msgid "Lighting" msgstr "Osvjetljenje" #: lib/solaar/ui/window.py:170 msgid "Show Technical Details" msgstr "Prikaži tehničke pojedinosti" #: lib/solaar/ui/window.py:186 msgid "Pair new device" msgstr "Upari novi uređaj" #: lib/solaar/ui/window.py:205 msgid "Select a device" msgstr "Odaberi uređaj" #: lib/solaar/ui/window.py:322 msgid "Rule Editor" msgstr "Uređivač pravila" #: lib/solaar/ui/window.py:533 msgid "Path" msgstr "Putanja" #: lib/solaar/ui/window.py:536 msgid "USB ID" msgstr "USB ID" #: lib/solaar/ui/window.py:539 lib/solaar/ui/window.py:541 #: lib/solaar/ui/window.py:561 lib/solaar/ui/window.py:563 msgid "Serial" msgstr "Serijski broj" #: lib/solaar/ui/window.py:545 msgid "Index" msgstr "Indeks" #: lib/solaar/ui/window.py:547 msgid "Wireless PID" msgstr "PID bežične mreže" #: lib/solaar/ui/window.py:549 msgid "Product ID" msgstr "ID proizvoda" #: lib/solaar/ui/window.py:551 msgid "Protocol" msgstr "Protokol" #: lib/solaar/ui/window.py:551 msgid "Unknown" msgstr "Nepoznato" #: lib/solaar/ui/window.py:554 #, python-format msgid "%(rate)d ms (%(rate_hz)dHz)" msgstr "%(rate)d ms (%(rate_hz)dHz)" #: lib/solaar/ui/window.py:554 msgid "Polling rate" msgstr "Cikličko osvježavanje" #: lib/solaar/ui/window.py:565 msgid "Unit ID" msgstr "ID jedinice" #: lib/solaar/ui/window.py:576 msgid "none" msgstr "nepoznato" #: lib/solaar/ui/window.py:577 msgid "Notifications" msgstr "Obavijesti" #: lib/solaar/ui/window.py:621 msgid "No device paired." msgstr "Nema uparenih uređaja." #: lib/solaar/ui/window.py:628 #, python-format msgid "Up to %(max_count)s device can be paired to this receiver." msgid_plural "Up to %(max_count)s devices can be paired to this receiver." msgstr[0] "Najviše %(max_count)s uređaj može biti uparen s ovim " "prijemnikom." msgstr[1] "Najviše %(max_count)s uređaja može biti upareno s ovim " "prijemnikom." msgstr[2] "Najviše %(max_count)s uređaja može biti upareno s ovim " "prijemnikom." #: lib/solaar/ui/window.py:634 msgid "Only one device can be paired to this receiver." msgstr "Samo jedan uređaj može biti uparen s ovim prijemnikom." #: lib/solaar/ui/window.py:638 #, python-format msgid "This receiver has %d pairing remaining." msgid_plural "This receiver has %d pairings remaining." msgstr[0] "Ovaj prijemnik ima još %d preostalo uparivanje." msgstr[1] "Ovaj prijemnik ima još %d preostala uparivanja." msgstr[2] "Ovaj prijemnik ima još %d preostalih uparivanja." #: lib/solaar/ui/window.py:692 msgid "Battery Voltage" msgstr "Napon baterije" #: lib/solaar/ui/window.py:694 msgid "Voltage reported by battery" msgstr "Napon prijavljen od strane baterije" #: lib/solaar/ui/window.py:696 msgid "Battery Level" msgstr "Razina baterije" #: lib/solaar/ui/window.py:698 msgid "Approximate level reported by battery" msgstr "Približna razina prijavljena od strane baterije" #: lib/solaar/ui/window.py:705 lib/solaar/ui/window.py:707 msgid "next reported " msgstr "sljedeće poznato " #: lib/solaar/ui/window.py:708 msgid " and next level to be reported." msgstr " i sljedeća razina za prijaviti." #: lib/solaar/ui/window.py:713 msgid "last known" msgstr "posljednje poznato" #: lib/solaar/ui/window.py:724 msgid "encrypted" msgstr "šifrirano" #: lib/solaar/ui/window.py:726 msgid "The wireless link between this device and its receiver is encrypted." msgstr "Bežično povezivanje između ovog uređaja i njegovog prijemnika je " "šifrirano." #: lib/solaar/ui/window.py:728 msgid "not encrypted" msgstr "nije šifrirano" #: lib/solaar/ui/window.py:732 msgid "The wireless link between this device and its receiver is not " "encrypted.\n" "This is a security issue for pointing devices, and a major security " "issue for text-input devices." msgstr "Bežično povezivanje između ovog uređaja i njegovog prijemnika nije " "šifrirano.\n" "Ovo je sigurnosni problem za pokazivačke uređaje i veliki sigurnosni " "problem za uređaje za unos teksta." #: lib/solaar/ui/window.py:748 #, python-format msgid "%(light_level)d lux" msgstr "%(light_level)d lux" #~ msgid " paired devices." #~ msgstr " uparena uređaja." #~ msgid "%(battery_level)s" #~ msgstr "%(battery_level)s" #~ msgid "%(battery_percent)d%%" #~ msgstr "%(battery_percent)d%%" #~ msgid "1 paired device." #~ msgstr "1 upareni uređaj." #~ msgid "Actions" #~ msgstr "Radnje" #~ msgid "Add action" #~ msgstr "Dodaj radnju" #~ msgid "Adjust the DPI by sliding the mouse horizontally while " #~ "holding the button down." #~ msgstr "Prilagodite DPI pomicanjem miša vodoravno držeći pritisnutu " #~ "tipku." #~ msgid "Automatically switch the mouse wheel between ratchet and " #~ "freespin mode.\n" #~ "The mouse wheel is always free at 0, and always locked at 50" #~ msgstr "Automatski prebaci kotačić miša između ustavljačkog i " #~ "slobodnog načina vrtnje kotačića.\n" #~ "Kotačić miša je uvijek slobodan pri 0 i uvijek zaključan pri 50" #~ msgid "Automatically switch the mouse wheel between ratchet and " #~ "freespin mode.\n" #~ "The mouse wheel is always free at 0, and always ratcheted at 50" #~ msgstr "Automatski prebaci kotačić miša između ustavljačkog i " #~ "slobodnog načina vrtnje kotačića.\n" #~ "Kotačić miša je uvijek slobodan pri 0 i uvijek zaključan pri 50" #~ msgid "Battery information unknown." #~ msgstr "Informacije baterije nepoznate." #~ msgid "Count" #~ msgstr "Brojač" #~ msgid "DPI Sliding Adjustment" #~ msgstr "Prilagodba DPI pomicanjem" #~ msgid "Diverted key or button depressed or released.\n" #~ "Use the Key/Button Diversion setting to divert keys and buttons." #~ msgstr "Preusmjerena tipka pritisnuta ili otpuštena.\n" #~ "Koristite postavku Preusmjeravanje tipke za preusmjeravanje tipki." #~ msgid "Effectively turns off thumb scrolling in Linux." #~ msgstr "Učinkovito isključuje pomicanje palcem u Linuxu." #~ msgid "Effectively turns off wheel scrolling in Linux." #~ msgstr "Učinkovito isključuje pomicanje kotačića u Linuxu." #~ msgid "For more information see the Solaar installation directions\n" #~ "at https://pwr-solaar.github.io/Solaar/installation" #~ msgstr "Za više informacija pogledajte upute Solaar instalacije\n" #~ "na https://pwr-solaar.github.io/Solaar/installation" #, python-format #~ msgid "Found a Logitech Receiver (%s), but did not have permission " #~ "to open it." #~ msgstr "Pronađen je (%s) Logitech Receiver, ali nemate ovlasti za " #~ "otvoriti ga." #~ msgid "HID++ Scrolling" #~ msgstr "HID++ Scrolling" #~ msgid "HID++ Thumb Scrolling" #~ msgstr "HID++ pomicanje palcem" #~ msgid "HID++ mode for horizontal scroll with the thumb wheel." #~ msgstr "HID++ način rada za okomito pomicanje kotačića palca." #~ msgid "HID++ mode for vertical scroll with the wheel." #~ msgstr "HID++ način rada za okomito pomicanje kotačića." #~ msgid "High Resolution Scrolling" #~ msgstr "Pomicanje kotačićem visoke razlučivosti" #~ msgid "High Resolution Wheel Invert" #~ msgstr "Obrnuto pomicanje kotačićem visoke razlučivosti" #~ msgid "High-sensitivity wheel invert direction for vertical scroll." #~ msgstr "Obrnuti visoko osjetljivi smjer kotačića za okomito " #~ "pomicanje." #~ msgid "If the device is already turned on,\n" #~ "turn if off and on again." #~ msgstr "Ako je uređaj već uključen,\n" #~ "isključite ga i ponovno uključite." #~ msgid "If the device is already turned on, turn if off and on again." #~ msgstr "Ako je uređaj već uključen, isključite ga i ponovno " #~ "uključite." #~ msgid "If you've just installed Solaar, try removing the receiver " #~ "and plugging it back in." #~ msgstr "Ako ste upravo instalirali Solaar, pokušajte odspojiti " #~ "prijemnik i ponovno ga spojiti." #~ msgid "Invert thumb scroll direction." #~ msgstr "Obrnuti smjer pomicanja kotačića palca." #~ msgid "Make the key or button send HID++ notifications (which " #~ "trigger Solaar rules but are otherwise ignored)." #~ msgstr "Neka tipka šalje HID++ obavijesti (što pokreće Solaar " #~ "pravila, u suprotnome ih zanemaruje)." #~ msgid "No Logitech device found" #~ msgstr "Nema pronađenih Logitechovih uređaja" #~ msgid "Scroll Wheel Rachet" #~ msgstr "Ustavljanje pomicanja kotačića" #~ msgid "Send a gesture by sliding the mouse while holding the button " #~ "down." #~ msgstr "Prilagodite geste pomicanjem miša držeći pritisnutu tipku." #~ msgid "Shows status of devices connected\n" #~ "through wireless Logitech receivers." #~ msgstr "Prikazuje stanje uređaja povezanih\n" #~ "putem Logitech bežičnih prijemnika." #~ msgid "Solaar depends on a udev file that is not present" #~ msgstr "Solaar ovisi o udev datoteci koja nije prisutna" #~ msgid "The receiver only supports %d paired device(s)." #~ msgstr "Prijemnik samo podržava %d upareni uređaj(e)." #~ msgid "The receiver was unplugged." #~ msgstr "Prijemnik je odspojen." #~ msgid "The wireless link between this device and its receiver is " #~ "not encrypted.\n" #~ "\n" #~ "For pointing devices (mice, trackballs, trackpads), this is a minor " #~ "security issue.\n" #~ "\n" #~ "It is, however, a major security issue for text-input devices " #~ "(keyboards, numpads),\n" #~ "because typed text can be sniffed inconspicuously by 3rd parties " #~ "within range." #~ msgstr "Bežično povezivanje između ovog uređaja i njegovog " #~ "prijemnika nije šifrirano.\n" #~ "\n" #~ "Za uređaje s pokazivačem (miš, trackballs, trackpads), to je manji " #~ "sigurnosni problem.\n" #~ "\n" #~ "A za uređaje za unos teksta, je veći sigurnosni problem (tipkovnice, " #~ "numpads),\n" #~ "zato jer upisani tekst može biti neprimjentno nadziran u dometu " #~ "treće strane." #~ msgid "Thumb Scroll Invert" #~ msgstr "Obrnuto pomicanje kotačića palca" #~ msgid "Try removing the device and plugging it back in or turning " #~ "it off and then on." #~ msgstr "Pokušajte uređaj ukloniti pa ponovno priključiti s računalom " #~ "ili ga isključiti pa ponovno uključiti." #~ msgid "USB id" #~ msgstr "USB id" #~ msgid "Wheel Resolution" #~ msgstr "Pomicanje kotačićem" #~ msgid "closed" #~ msgstr "završeno" #~ msgid "lux" #~ msgstr "lux" #~ msgid "next " #~ msgstr "sljedeće " #~ msgid "open" #~ msgstr "pokrenuto" #~ msgid "paired devices" #~ msgstr "upareni uređaji" #~ msgid "unknown" #~ msgstr "nepoznato" Solaar-1.1.14/po/id.po000066400000000000000000001620101473525213700144020ustar00rootroot00000000000000# Indonesian translations for solaar package. # Copyright (C) 2024 THE solaar'S COPYRIGHT HOLDER # This file is distributed under the same license as the solaar package. # Ferdina kusumah , 2024. # msgid "" msgstr "Project-Id-Version: solaar 1.1.11\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2024-03-14 13:11+0700\n" "PO-Revision-Date: 2024-03-14 09:00+0700\n" "Last-Translator: Ferdina Kusumah \n" "Language-Team: none\n" "Language: id\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Poedit 3.0\n" #: lib/logitech_receiver/base_usb.py:45 msgid "Bolt Receiver" msgstr "Reseptor bolt" #: lib/logitech_receiver/base_usb.py:58 msgid "Unifying Receiver" msgstr "Reseptor unifying" #: lib/logitech_receiver/base_usb.py:70 lib/logitech_receiver/base_usb.py:83 #: lib/logitech_receiver/base_usb.py:97 lib/logitech_receiver/base_usb.py:111 #: lib/logitech_receiver/base_usb.py:125 msgid "Nano Receiver" msgstr "Reseptor nano" #: lib/logitech_receiver/base_usb.py:138 msgid "Lightspeed Receiver" msgstr "Reseptor lightspeed" #: lib/logitech_receiver/base_usb.py:149 msgid "EX100 Receiver 27 Mhz" msgstr "Reseptor EX100 27 Mhz" #: lib/logitech_receiver/common.py:604 #, python-format msgid "Battery: %(level)s (%(status)s)" msgstr "Baterai: %(level)s (%(status)s)" #: lib/logitech_receiver/common.py:606 #, python-format msgid "Battery: %(percent)d%% (%(status)s)" msgstr "Baterai: %(percent)d%% (%(status)s)" #: lib/logitech_receiver/hidpp20.py:926 #: lib/logitech_receiver/settings_templates.py:291 msgid "Disabled" msgstr "Dinonaktifkan" #: lib/logitech_receiver/hidpp20.py:927 msgid "Static" msgstr "" #: lib/logitech_receiver/hidpp20.py:928 msgid "Pulse" msgstr "Pulsa" #: lib/logitech_receiver/hidpp20.py:929 msgid "Cycle" msgstr "" #: lib/logitech_receiver/hidpp20.py:930 msgid "Boot" msgstr "" #: lib/logitech_receiver/hidpp20.py:931 msgid "Demo" msgstr "" #: lib/logitech_receiver/hidpp20.py:932 msgid "Breathe" msgstr "" #: lib/logitech_receiver/hidpp20.py:933 msgid "Ripple" msgstr "" #: lib/logitech_receiver/hidpp20.py:994 msgid "Unknown Location" msgstr "Lokasi tidak ditemukan" #: lib/logitech_receiver/hidpp20.py:995 msgid "Primary" msgstr "Utama" #: lib/logitech_receiver/hidpp20.py:996 msgid "Logo" msgstr "Logo" #: lib/logitech_receiver/hidpp20.py:997 msgid "Left Side" msgstr "Sisi kiri" #: lib/logitech_receiver/hidpp20.py:998 msgid "Right Side" msgstr "Sisi kanan" #: lib/logitech_receiver/hidpp20.py:999 msgid "Combined" msgstr "Kombinasi" #: lib/logitech_receiver/hidpp20.py:1000 msgid "Primary 1" msgstr "Utama 1" #: lib/logitech_receiver/hidpp20.py:1001 msgid "Primary 2" msgstr "Utama 2" #: lib/logitech_receiver/hidpp20.py:1002 msgid "Primary 3" msgstr "Utama 3" #: lib/logitech_receiver/hidpp20.py:1003 msgid "Primary 4" msgstr "Utama 4" #: lib/logitech_receiver/hidpp20.py:1004 msgid "Primary 5" msgstr "Utama 5" #: lib/logitech_receiver/hidpp20.py:1005 msgid "Primary 6" msgstr "Utama 6" #: lib/logitech_receiver/i18n.py:28 msgid "empty" msgstr "habis" #: lib/logitech_receiver/i18n.py:29 msgid "critical" msgstr "hampir habis" #: lib/logitech_receiver/i18n.py:30 msgid "low" msgstr "rendah" #: lib/logitech_receiver/i18n.py:31 msgid "average" msgstr "setengah" #: lib/logitech_receiver/i18n.py:32 msgid "good" msgstr "baik" #: lib/logitech_receiver/i18n.py:33 msgid "full" msgstr "penuh" #: lib/logitech_receiver/i18n.py:35 msgid "discharging" msgstr "pemakaian" #: lib/logitech_receiver/i18n.py:36 msgid "recharging" msgstr "pengisian ulang" #: lib/logitech_receiver/i18n.py:37 msgid "charging" msgstr "pengisian daya" #: lib/logitech_receiver/i18n.py:38 msgid "not charging" msgstr "tidak mengisi daya" #: lib/logitech_receiver/i18n.py:39 msgid "almost full" msgstr "hampir penuh" #: lib/logitech_receiver/i18n.py:40 msgid "charged" msgstr "mengisi" #: lib/logitech_receiver/i18n.py:41 msgid "slow recharge" msgstr "pengisian lambat" #: lib/logitech_receiver/i18n.py:42 msgid "invalid battery" msgstr "baterai tidak valid" #: lib/logitech_receiver/i18n.py:43 msgid "thermal error" msgstr "kesalahan termal" #: lib/logitech_receiver/i18n.py:44 msgid "error" msgstr "" #: lib/logitech_receiver/i18n.py:45 msgid "standard" msgstr "" #: lib/logitech_receiver/i18n.py:46 msgid "fast" msgstr "cepat" #: lib/logitech_receiver/i18n.py:47 msgid "slow" msgstr "lambat" #: lib/logitech_receiver/i18n.py:49 msgid "device timeout" msgstr "batas waktu perangkat habis" #: lib/logitech_receiver/i18n.py:50 msgid "device not supported" msgstr "perangkat tidak didukung" #: lib/logitech_receiver/i18n.py:51 msgid "too many devices" msgstr "terlalu banyak perangkat" #: lib/logitech_receiver/i18n.py:52 msgid "sequence timeout" msgstr "batas waktu urutan habis" #: lib/logitech_receiver/i18n.py:54 msgid "Firmware" msgstr "" #: lib/logitech_receiver/i18n.py:55 msgid "Bootloader" msgstr "" #: lib/logitech_receiver/i18n.py:56 msgid "Hardware" msgstr "Perangkat keras" #: lib/logitech_receiver/i18n.py:57 msgid "Other" msgstr "Lainnya" #: lib/logitech_receiver/i18n.py:59 msgid "Left Button" msgstr "Tombol Kiri" #: lib/logitech_receiver/i18n.py:60 msgid "Right Button" msgstr "Tombol Kanan" #: lib/logitech_receiver/i18n.py:61 msgid "Middle Button" msgstr "Tombol Tengah" #: lib/logitech_receiver/i18n.py:62 msgid "Back Button" msgstr "Tombol kembali" #: lib/logitech_receiver/i18n.py:63 msgid "Forward Button" msgstr "Tombol Depan" #: lib/logitech_receiver/i18n.py:64 msgid "Mouse Gesture Button" msgstr "Tombol Gerakan Mouse" #: lib/logitech_receiver/i18n.py:65 msgid "Smart Shift" msgstr "Pergeseran Cerdas" #: lib/logitech_receiver/i18n.py:66 msgid "DPI Switch" msgstr "" #: lib/logitech_receiver/i18n.py:67 msgid "Left Tilt" msgstr "Kemiringan Kiri" #: lib/logitech_receiver/i18n.py:68 msgid "Right Tilt" msgstr "Kemiringan Kanan" #: lib/logitech_receiver/i18n.py:69 msgid "Left Click" msgstr "Klik Kiri" #: lib/logitech_receiver/i18n.py:70 msgid "Right Click" msgstr "Klik Kanan" #: lib/logitech_receiver/i18n.py:71 msgid "Mouse Middle Button" msgstr "Tombol Tengah Mouse" #: lib/logitech_receiver/i18n.py:72 msgid "Mouse Back Button" msgstr "Tombol Kembali Mouse" #: lib/logitech_receiver/i18n.py:73 msgid "Mouse Forward Button" msgstr "Tombol Depan Mouse" #: lib/logitech_receiver/i18n.py:74 msgid "Gesture Button Navigation" msgstr "Navigasi Tombol Gerakan" #: lib/logitech_receiver/i18n.py:75 msgid "Mouse Scroll Left Button" msgstr "Tombol Gulir Kiri Mouse" #: lib/logitech_receiver/i18n.py:76 msgid "Mouse Scroll Right Button" msgstr "Tombol Gulir Kanan Mouse" #: lib/logitech_receiver/i18n.py:78 msgid "pressed" msgstr "ditekan" #: lib/logitech_receiver/i18n.py:79 msgid "released" msgstr "dilepaskan" #: lib/logitech_receiver/notifications.py:63 #: lib/logitech_receiver/notifications.py:115 msgid "pairing lock is closed" msgstr "kunci penyandingan ditutup" #: lib/logitech_receiver/notifications.py:63 #: lib/logitech_receiver/notifications.py:115 msgid "pairing lock is open" msgstr "kunci penyandingan terbuka" #: lib/logitech_receiver/notifications.py:80 msgid "discovery lock is closed" msgstr "kunci penemuan ditutup" #: lib/logitech_receiver/notifications.py:80 msgid "discovery lock is open" msgstr "kunci penemuan terbuka" #: lib/logitech_receiver/notifications.py:207 msgid "connected" msgstr "terhubung" #: lib/logitech_receiver/notifications.py:207 msgid "disconnected" msgstr "terputus" #: lib/logitech_receiver/notifications.py:233 msgid "unpaired" msgstr "tidak berpasangan" #: lib/logitech_receiver/notifications.py:280 msgid "powered on" msgstr "dihidupkan" #: lib/logitech_receiver/receiver.py:345 msgid "No paired devices." msgstr "Tidak ada perangkat yang dipasangkan" #: lib/logitech_receiver/receiver.py:347 lib/solaar/ui/window.py:630 #, python-format msgid "%(count)s paired device." msgid_plural "%(count)s paired devices." msgstr[0] "%(count)s perangkat yang dipasangkan." #: lib/logitech_receiver/settings.py:617 msgid "register" msgstr "daftar" #: lib/logitech_receiver/settings.py:631 lib/logitech_receiver/settings.py:658 msgid "feature" msgstr "fitur" #: lib/logitech_receiver/settings_templates.py:144 msgid "Swap Fx function" msgstr "Tukar fungsi Fx" #: lib/logitech_receiver/settings_templates.py:147 msgid "When set, the F1..F12 keys will activate their special function,\n" "and you must hold the FN key to activate their standard function." msgstr "Jika disetel, tombol F1..F12 akan mengaktifkan fungsi khususnya,\n" "dan Anda harus menahan tombol FN untuk mengaktifkan fungsi " "standarnya." #: lib/logitech_receiver/settings_templates.py:152 msgid "When unset, the F1..F12 keys will activate their standard function,\n" "and you must hold the FN key to activate their special function." msgstr "Jika tidak disetel, tombol F1..F12 akan mengaktifkan fungsi " "standarnya,\n" "dan Anda harus menahan tombol FN untuk mengaktifkan fungsi khususnya." #: lib/logitech_receiver/settings_templates.py:160 msgid "Hand Detection" msgstr "Deteksi Tangan" #: lib/logitech_receiver/settings_templates.py:161 msgid "Turn on illumination when the hands hover over the keyboard." msgstr "Nyalakan penerangan saat tangan melayang di atas keyboard." #: lib/logitech_receiver/settings_templates.py:168 msgid "Scroll Wheel Smooth Scrolling" msgstr "Roda Gulir Pengguliran Halus" #: lib/logitech_receiver/settings_templates.py:169 #: lib/logitech_receiver/settings_templates.py:402 #: lib/logitech_receiver/settings_templates.py:431 msgid "High-sensitivity mode for vertical scroll with the wheel." msgstr "Mode sensitivitas tinggi untuk gulir vertikal dengan roda." #: lib/logitech_receiver/settings_templates.py:176 msgid "Side Scrolling" msgstr "Menggulir Sisi" #: lib/logitech_receiver/settings_templates.py:178 msgid "When disabled, pushing the wheel sideways sends custom button " "events\n" "instead of the standard side-scrolling events." msgstr "Saat dinonaktifkan, mendorong roda ke samping akan mengirimkan " "tombol khususacara\n" "bukannya acara side-scrolling standar." #: lib/logitech_receiver/settings_templates.py:188 msgid "Sensitivity (DPI - older mice)" msgstr "Sensitivitas (DPI - older mouse)" #: lib/logitech_receiver/settings_templates.py:189 #: lib/logitech_receiver/settings_templates.py:958 msgid "Mouse movement sensitivity" msgstr "Sensitivitas gerakan mouse" #: lib/logitech_receiver/settings_templates.py:249 #: lib/logitech_receiver/settings_templates.py:261 #: lib/logitech_receiver/settings_templates.py:388 msgid "Backlight" msgstr "Lampu latar" #: lib/logitech_receiver/settings_templates.py:250 #: lib/logitech_receiver/settings_templates.py:389 msgid "Set illumination time for keyboard." msgstr "Atur waktu penerangan untuk keyboard." #: lib/logitech_receiver/settings_templates.py:262 msgid "Illumination level on keyboard. Changes made are only applied in " "Manual mode." msgstr "Tingkat penerangan pada keyboard. Perubahan yang dilakukan hanya " "diterapkan di Mode manual." #: lib/logitech_receiver/settings_templates.py:293 msgid "Automatic" msgstr "Otomatis" #: lib/logitech_receiver/settings_templates.py:295 msgid "Manual" msgstr "" #: lib/logitech_receiver/settings_templates.py:297 msgid "Enabled" msgstr "Difungsikan" #: lib/logitech_receiver/settings_templates.py:303 msgid "Backlight Level" msgstr "Tingkat Lampu Latar" #: lib/logitech_receiver/settings_templates.py:304 msgid "Illumination level on keyboard when in Manual mode." msgstr "Tingkat penerangan pada keyboard saat dalam mode Manual." #: lib/logitech_receiver/settings_templates.py:361 msgid "Backlight Delay Hands Out" msgstr "Penundaan Lampu Latar Hands Out" #: lib/logitech_receiver/settings_templates.py:362 msgid "Delay in seconds until backlight fades out with hands away from " "keyboard." msgstr "Tunda dalam hitungan detik hingga cahaya latar memudar dengan tangan " "menjauhipapan ketik." #: lib/logitech_receiver/settings_templates.py:370 msgid "Backlight Delay Hands In" msgstr "Penundaan Lampu Latar Masuk" #: lib/logitech_receiver/settings_templates.py:371 msgid "Delay in seconds until backlight fades out with hands near keyboard." msgstr "Tunda dalam hitungan detik hingga lampu latar memudar dengan tangan " "di dekat keyboard." #: lib/logitech_receiver/settings_templates.py:379 msgid "Backlight Delay Powered" msgstr "Penundaan Lampu Latar Didukung" #: lib/logitech_receiver/settings_templates.py:380 msgid "Delay in seconds until backlight fades out with external power." msgstr "Penundaan dalam hitungan detik hingga lampu latar memudar dengan " "daya eksternal." #: lib/logitech_receiver/settings_templates.py:400 msgid "Scroll Wheel High Resolution" msgstr "Resolusi Tinggi Scroll Wheel" #: lib/logitech_receiver/settings_templates.py:404 #: lib/logitech_receiver/settings_templates.py:433 msgid "Set to ignore if scrolling is abnormally fast or slow" msgstr "Setel ke abaikan jika pengguliran terlalu cepat atau lambat" #: lib/logitech_receiver/settings_templates.py:411 #: lib/logitech_receiver/settings_templates.py:442 msgid "Scroll Wheel Diversion" msgstr "Pengalihan Scroll Wheel" #: lib/logitech_receiver/settings_templates.py:413 msgid "Make scroll wheel send LOWRES_WHEEL HID++ notifications (which " "trigger Solaar rules but are otherwise ignored)." msgstr "Buat roda gulir mengirimkan notifikasi LOWRES_WHEEL HID++ (yang mana " "memicu aturan Solaar tetapi sebaliknya diabaikan)." #: lib/logitech_receiver/settings_templates.py:420 msgid "Scroll Wheel Direction" msgstr "Arah Scroll Wheel" #: lib/logitech_receiver/settings_templates.py:421 msgid "Invert direction for vertical scroll with wheel." msgstr "Balikkan arah untuk gulir vertikal scroll with wheel." #: lib/logitech_receiver/settings_templates.py:429 msgid "Scroll Wheel Resolution" msgstr "Resolusi Scroll Wheel" #: lib/logitech_receiver/settings_templates.py:444 msgid "Make scroll wheel send HIRES_WHEEL HID++ notifications (which " "trigger Solaar rules but are otherwise ignored)." msgstr "Buat roda gulir mengirimkan notifikasi HIRES_WHEEL HID++ (yang mana " "memicu aturan Solaar tetapi sebaliknya diabaikan)." #: lib/logitech_receiver/settings_templates.py:453 msgid "Sensitivity (Pointer Speed)" msgstr "Sensitivitas (Kecepatan Pointer)" #: lib/logitech_receiver/settings_templates.py:454 msgid "Speed multiplier for mouse (256 is normal multiplier)." msgstr "Pengganda kecepatan untuk mouse (256 adalah pengganda normal)." #: lib/logitech_receiver/settings_templates.py:464 msgid "Thumb Wheel Diversion" msgstr "Pengalihan Roda Jempol" #: lib/logitech_receiver/settings_templates.py:466 msgid "Make thumb wheel send THUMB_WHEEL HID++ notifications (which trigger " "Solaar rules but are otherwise ignored)." msgstr "Buat roda jempol mengirimkan notifikasi THUMB_WHEEL HID++ (yang " "memicuPeraturan Solar tetapi sebaliknya diabaikan)." #: lib/logitech_receiver/settings_templates.py:475 msgid "Thumb Wheel Direction" msgstr "Arah Roda Ibu Jari" #: lib/logitech_receiver/settings_templates.py:476 msgid "Invert thumb wheel scroll direction." msgstr "Balikkan arah gulir roda ibu jari." #: lib/logitech_receiver/settings_templates.py:496 msgid "Onboard Profiles" msgstr "" #: lib/logitech_receiver/settings_templates.py:497 msgid "Enable an onboard profile, which controls report rate, sensitivity, " "and button actions" msgstr "Aktifkan profil onboard, yang mengontrol kecepatan laporan, " "sensitivitas, dan tindakan tombol" #: lib/logitech_receiver/settings_templates.py:541 #: lib/logitech_receiver/settings_templates.py:581 msgid "Report Rate" msgstr "Tingkat Laporan" #: lib/logitech_receiver/settings_templates.py:543 #: lib/logitech_receiver/settings_templates.py:583 msgid "Frequency of device movement reports" msgstr "Frekuensi laporan pergerakan perangkat" #: lib/logitech_receiver/settings_templates.py:543 #: lib/logitech_receiver/settings_templates.py:583 #: lib/logitech_receiver/settings_templates.py:1295 #: lib/logitech_receiver/settings_templates.py:1326 msgid "May need Onboard Profiles set to Disable to be effective." msgstr "Mungkin perlu Profil Onboard diatur ke Nonaktifkan agar efektif." #: lib/logitech_receiver/settings_templates.py:623 msgid "Divert crown events" msgstr "Alihkan acara mahkota" #: lib/logitech_receiver/settings_templates.py:624 msgid "Make crown send CROWN HID++ notifications (which trigger Solaar " "rules but are otherwise ignored)." msgstr "Buat mahkota mengirimkan notifikasi CROWN HID++ (yang memicu " "Solaarperaturan tetapi sebaliknya diabaikan)." #: lib/logitech_receiver/settings_templates.py:632 msgid "Crown smooth scroll" msgstr "Gulir halus mahkota" #: lib/logitech_receiver/settings_templates.py:633 msgid "Set crown smooth scroll" msgstr "Setel mahkota gulir halus" #: lib/logitech_receiver/settings_templates.py:641 msgid "Divert G and M Keys" msgstr "Alihkan Tombol G dan M" #: lib/logitech_receiver/settings_templates.py:642 msgid "Make G and M keys send HID++ notifications (which trigger Solaar " "rules but are otherwise ignored)." msgstr "Buat kunci G dan M mengirimkan notifikasi HID++ (yang memicu " "Solaarperaturan tetapi sebaliknya diabaikan)." #: lib/logitech_receiver/settings_templates.py:656 msgid "Scroll Wheel Ratcheted" msgstr "Roda Gulir Bergerigi" #: lib/logitech_receiver/settings_templates.py:657 msgid "Switch the mouse wheel between speed-controlled ratcheting and " "always freespin." msgstr "Ganti roda mouse antara ratcheting yang dikontrol kecepatan dan " "selalu berputar bebas." #: lib/logitech_receiver/settings_templates.py:659 msgid "Freespinning" msgstr "Putar bebas" #: lib/logitech_receiver/settings_templates.py:659 msgid "Ratcheted" msgstr "Digeser" #: lib/logitech_receiver/settings_templates.py:666 msgid "Scroll Wheel Ratchet Speed" msgstr "Kecepatan Ratchet Roda Gulir" #: lib/logitech_receiver/settings_templates.py:668 msgid "Use the mouse wheel speed to switch between ratcheted and " "freespinning.\n" "The mouse wheel is always ratcheted at 50." msgstr "Gunakan kecepatan roda mouse untuk beralih antara ratcheted dan " "berputar bebas.\n" "Roda tikus selalu diputar pada kecepatan 50." #: lib/logitech_receiver/settings_templates.py:717 msgid "Key/Button Actions" msgstr "Tindakan Kunci/Tombol" #: lib/logitech_receiver/settings_templates.py:719 msgid "Change the action for the key or button." msgstr "Ubah tindakan untuk kunci atau tombol tersebut." #: lib/logitech_receiver/settings_templates.py:721 msgid "Overridden by diversion." msgstr "Ditimpa oleh pengalihan." #: lib/logitech_receiver/settings_templates.py:723 msgid "Changing important actions (such as for the left mouse button) can " "result in an unusable system." msgstr "Mengubah tindakan penting (misalnya tombol kiri mouse) dapat " "menghasilkan sistem yang tidak dapat digunakan." #: lib/logitech_receiver/settings_templates.py:886 msgid "Key/Button Diversion" msgstr "Pengalihan Kunci/Tombol" #: lib/logitech_receiver/settings_templates.py:887 msgid "Make the key or button send HID++ notifications (Diverted) or " "initiate Mouse Gestures or Sliding DPI" msgstr "Buat kunci atau tombol mengirimkan notifikasi HID++ (Dialihkan) " "ataumemulai Gerakan Mouse atau DPI Geser" #: lib/logitech_receiver/settings_templates.py:890 #: lib/logitech_receiver/settings_templates.py:891 #: lib/logitech_receiver/settings_templates.py:892 msgid "Diverted" msgstr "Dialihkan" #: lib/logitech_receiver/settings_templates.py:890 #: lib/logitech_receiver/settings_templates.py:891 msgid "Mouse Gestures" msgstr "Gerakan Mouse" #: lib/logitech_receiver/settings_templates.py:890 #: lib/logitech_receiver/settings_templates.py:891 #: lib/logitech_receiver/settings_templates.py:892 msgid "Regular" msgstr "" #: lib/logitech_receiver/settings_templates.py:890 msgid "Sliding DPI" msgstr "DPI geser" #: lib/logitech_receiver/settings_templates.py:957 msgid "Sensitivity (DPI)" msgstr "Sensitivitas (DPI)" #: lib/logitech_receiver/settings_templates.py:997 msgid "Sensitivity Switching" msgstr "Peralihan Sensitivitas" #: lib/logitech_receiver/settings_templates.py:999 msgid "Switch the current sensitivity and the remembered sensitivity when " "the key or button is pressed.\n" "If there is no remembered sensitivity, just remember the current " "sensitivity" msgstr "Ganti sensitivitas saat ini dan sensitivitas yang diingat ketika " "tombol atau tombol ditekan.\n" "Jika tidak ada kepekaan yang diingat, ingat saja arusnyakepekaan" #: lib/logitech_receiver/settings_templates.py:1003 msgid "Off" msgstr "Mati" #: lib/logitech_receiver/settings_templates.py:1034 msgid "Disable keys" msgstr "Nonaktifkan kunci" #: lib/logitech_receiver/settings_templates.py:1035 msgid "Disable specific keyboard keys." msgstr "Nonaktifkan tombol keyboard tertentu." #: lib/logitech_receiver/settings_templates.py:1038 #, python-format msgid "Disables the %s key." msgstr "Menonaktifkan kunci %s." #: lib/logitech_receiver/settings_templates.py:1051 #: lib/logitech_receiver/settings_templates.py:1108 msgid "Set OS" msgstr "Atur OS" #: lib/logitech_receiver/settings_templates.py:1052 #: lib/logitech_receiver/settings_templates.py:1109 msgid "Change keys to match OS." msgstr "Ubah kunci agar sesuai dengan OS." #: lib/logitech_receiver/settings_templates.py:1121 msgid "Change Host" msgstr "Ganti Host" #: lib/logitech_receiver/settings_templates.py:1122 msgid "Switch connection to a different host" msgstr "Alihkan koneksi ke host lain" #: lib/logitech_receiver/settings_templates.py:1146 msgid "Performs a left click." msgstr "Melakukan klik kiri." #: lib/logitech_receiver/settings_templates.py:1146 msgid "Single tap" msgstr "Ketuk sekali" #: lib/logitech_receiver/settings_templates.py:1147 msgid "Performs a right click." msgstr "Melakukan klik kanan." #: lib/logitech_receiver/settings_templates.py:1147 msgid "Single tap with two fingers" msgstr "Ketuk satu kali dengan dua jari" #: lib/logitech_receiver/settings_templates.py:1148 msgid "Single tap with three fingers" msgstr "Ketuk satu kali dengan tiga jari" #: lib/logitech_receiver/settings_templates.py:1152 msgid "Double tap" msgstr "Ketuk dua kali" #: lib/logitech_receiver/settings_templates.py:1152 msgid "Performs a double click." msgstr "Melakukan klik dua kali." #: lib/logitech_receiver/settings_templates.py:1153 msgid "Double tap with two fingers" msgstr "Ketuk dua kali dengan dua jari" #: lib/logitech_receiver/settings_templates.py:1154 msgid "Double tap with three fingers" msgstr "Ketuk dua kali dengan tiga jari" #: lib/logitech_receiver/settings_templates.py:1157 msgid "Drags items by dragging the finger after double tapping." msgstr "Menyeret item dengan menyeret jari setelah mengetuk dua kali." #: lib/logitech_receiver/settings_templates.py:1157 msgid "Tap and drag" msgstr "Ketuk dan seret" #: lib/logitech_receiver/settings_templates.py:1159 msgid "Tap and drag with two fingers" msgstr "Ketuk dan seret dengan dua jari" #: lib/logitech_receiver/settings_templates.py:1160 msgid "Drags items by dragging the fingers after double tapping." msgstr "Menyeret item dengan menyeret jari setelah mengetuk dua kali." #: lib/logitech_receiver/settings_templates.py:1162 msgid "Tap and drag with three fingers" msgstr "Ketuk dan seret dengan tiga jari" #: lib/logitech_receiver/settings_templates.py:1165 msgid "Suppress tap and edge gestures" msgstr "Menekan gerakan ketuk dan tepi" #: lib/logitech_receiver/settings_templates.py:1166 msgid "Disables tap and edge gestures (equivalent to pressing Fn+LeftClick)." msgstr "Menonaktifkan gerakan ketuk dan tepi (setara dengan menekan Fn+Klik " "Kiri)." #: lib/logitech_receiver/settings_templates.py:1168 msgid "Scroll with one finger" msgstr "Gulir dengan satu jari" #: lib/logitech_receiver/settings_templates.py:1168 #: lib/logitech_receiver/settings_templates.py:1169 #: lib/logitech_receiver/settings_templates.py:1172 msgid "Scrolls." msgstr "Penggulir" #: lib/logitech_receiver/settings_templates.py:1169 #: lib/logitech_receiver/settings_templates.py:1172 msgid "Scroll with two fingers" msgstr "Gulir dengan dua jari" #: lib/logitech_receiver/settings_templates.py:1170 msgid "Scroll horizontally with two fingers" msgstr "Gulir secara horizontal dengan dua jari" #: lib/logitech_receiver/settings_templates.py:1170 msgid "Scrolls horizontally." msgstr "Menggulir secara horizontal." #: lib/logitech_receiver/settings_templates.py:1171 msgid "Scroll vertically with two fingers" msgstr "Gulir secara vertikal dengan dua jari" #: lib/logitech_receiver/settings_templates.py:1171 msgid "Scrolls vertically." msgstr "Gulir secara vertikal." #: lib/logitech_receiver/settings_templates.py:1173 msgid "Inverts the scrolling direction." msgstr "Membalikkan arah pengguliran." #: lib/logitech_receiver/settings_templates.py:1173 msgid "Natural scrolling" msgstr "Pengguliran alami" #: lib/logitech_receiver/settings_templates.py:1174 msgid "Enables the thumbwheel." msgstr "Mengaktifkan roda jempol." #: lib/logitech_receiver/settings_templates.py:1174 msgid "Thumbwheel" msgstr "Roda jempol" #: lib/logitech_receiver/settings_templates.py:1185 #: lib/logitech_receiver/settings_templates.py:1189 msgid "Swipe from the top edge" msgstr "Geser dari tepi atas" #: lib/logitech_receiver/settings_templates.py:1186 msgid "Swipe from the left edge" msgstr "Geser dari tepi kiri" #: lib/logitech_receiver/settings_templates.py:1187 msgid "Swipe from the right edge" msgstr "Geser dari tepi kanan" #: lib/logitech_receiver/settings_templates.py:1188 msgid "Swipe from the bottom edge" msgstr "Geser dari tepi bawah" #: lib/logitech_receiver/settings_templates.py:1190 msgid "Swipe two fingers from the left edge" msgstr "Geser dua jari dari tepi kiri" #: lib/logitech_receiver/settings_templates.py:1191 msgid "Swipe two fingers from the right edge" msgstr "Geser dua jari dari tepi kanan" #: lib/logitech_receiver/settings_templates.py:1192 msgid "Swipe two fingers from the bottom edge" msgstr "Geser dua jari dari tepi bawah" #: lib/logitech_receiver/settings_templates.py:1193 msgid "Swipe two fingers from the top edge" msgstr "Geser dua jari dari tepi atas" #: lib/logitech_receiver/settings_templates.py:1194 #: lib/logitech_receiver/settings_templates.py:1198 msgid "Pinch to zoom out; spread to zoom in." msgstr "Jepit untuk memperkecil; sebarkan untuk memperbesar." #: lib/logitech_receiver/settings_templates.py:1194 msgid "Zoom with two fingers." msgstr "Perbesar dengan dua jari." #: lib/logitech_receiver/settings_templates.py:1195 msgid "Pinch to zoom out." msgstr "Jepit untuk memperkecil." #: lib/logitech_receiver/settings_templates.py:1196 msgid "Spread to zoom in." msgstr "Sebarkan untuk memperbesar." #: lib/logitech_receiver/settings_templates.py:1197 msgid "Zoom with three fingers." msgstr "Perbesar dengan tiga jari." #: lib/logitech_receiver/settings_templates.py:1198 msgid "Zoom with two fingers" msgstr "Zoom dengan dua jari" #: lib/logitech_receiver/settings_templates.py:1216 msgid "Pixel zone" msgstr "Zona piksel" #: lib/logitech_receiver/settings_templates.py:1217 msgid "Ratio zone" msgstr "Zona rasio" #: lib/logitech_receiver/settings_templates.py:1218 msgid "Scale factor" msgstr "Faktor skala" #: lib/logitech_receiver/settings_templates.py:1218 msgid "Sets the cursor speed." msgstr "Mengatur kecepatan kursor." #: lib/logitech_receiver/settings_templates.py:1222 msgid "Left" msgstr "Kiri" #: lib/logitech_receiver/settings_templates.py:1222 msgid "Left-most coordinate." msgstr "Koordinat paling kiri." #: lib/logitech_receiver/settings_templates.py:1223 msgid "Bottom" msgstr "Dasar" #: lib/logitech_receiver/settings_templates.py:1223 msgid "Bottom coordinate." msgstr "Koordinat bawah." #: lib/logitech_receiver/settings_templates.py:1224 msgid "Width" msgstr "Lebar" #: lib/logitech_receiver/settings_templates.py:1224 msgid "Width." msgstr "" #: lib/logitech_receiver/settings_templates.py:1225 msgid "Height" msgstr "Tinggi" #: lib/logitech_receiver/settings_templates.py:1225 msgid "Height." msgstr "" #: lib/logitech_receiver/settings_templates.py:1226 msgid "Cursor speed." msgstr "Kecepatan kursor." #: lib/logitech_receiver/settings_templates.py:1226 msgid "Scale" msgstr "Skala" #: lib/logitech_receiver/settings_templates.py:1232 msgid "Gestures" msgstr "Gestur" #: lib/logitech_receiver/settings_templates.py:1233 msgid "Tweak the mouse/touchpad behaviour." msgstr "Sesuaikan perilaku mouse/touchpad." #: lib/logitech_receiver/settings_templates.py:1249 msgid "Gestures Diversion" msgstr "Pengalihan Gerakan" #: lib/logitech_receiver/settings_templates.py:1250 msgid "Divert mouse/touchpad gestures." msgstr "Alihkan gerakan mouse/touchpad." #: lib/logitech_receiver/settings_templates.py:1266 msgid "Gesture params" msgstr "Param isyarat" #: lib/logitech_receiver/settings_templates.py:1267 msgid "Change numerical parameters of a mouse/touchpad." msgstr "Ubah parameter numerik mouse/touchpad." #: lib/logitech_receiver/settings_templates.py:1291 msgid "M-Key LEDs" msgstr "LED M-Key" #: lib/logitech_receiver/settings_templates.py:1293 msgid "Control the M-Key LEDs." msgstr "Kontrol LED M-Key." #: lib/logitech_receiver/settings_templates.py:1297 #: lib/logitech_receiver/settings_templates.py:1328 msgid "May need G Keys diverted to be effective." msgstr "Mungkin perlu tombol G dialihkan agar efektif." #: lib/logitech_receiver/settings_templates.py:1303 #, python-format msgid "Lights up the %s key." msgstr "Nyalakan tombol %s." #: lib/logitech_receiver/settings_templates.py:1322 msgid "MR-Key LED" msgstr "LED Kunci MR" #: lib/logitech_receiver/settings_templates.py:1324 msgid "Control the MR-Key LED." msgstr "Kontrol LED MR-Key." #: lib/logitech_receiver/settings_templates.py:1345 msgid "Persistent Key/Button Mapping" msgstr "Pemetaan Kunci/Tombol Persisten" #: lib/logitech_receiver/settings_templates.py:1347 msgid "Permanently change the mapping for the key or button." msgstr "Ubah pemetaan kunci atau tombol secara permanen." #: lib/logitech_receiver/settings_templates.py:1349 msgid "Changing important keys or buttons (such as for the left mouse " "button) can result in an unusable system." msgstr "Mengganti tombol atau tombol penting (misalnya untuk mouse sebelah " "kiritombol) dapat mengakibatkan sistem tidak dapat digunakan." #: lib/logitech_receiver/settings_templates.py:1406 msgid "Sidetone" msgstr "Nada samping" #: lib/logitech_receiver/settings_templates.py:1407 msgid "Set sidetone level." msgstr "Setel level nada samping." #: lib/logitech_receiver/settings_templates.py:1416 msgid "Equalizer" msgstr "" #: lib/logitech_receiver/settings_templates.py:1417 msgid "Set equalizer levels." msgstr "Tetapkan level equalizer." #: lib/logitech_receiver/settings_templates.py:1439 msgid "Hz" msgstr "" #: lib/logitech_receiver/settings_templates.py:1445 msgid "Power Management" msgstr "Manajemen daya" #: lib/logitech_receiver/settings_templates.py:1446 msgid "Power off in minutes (0 for never)." msgstr "Matikan dalam hitungan menit (0 untuk tidak pernah)." #: lib/logitech_receiver/settings_templates.py:1457 msgid "LED Control" msgstr "Kontrol LED" #: lib/logitech_receiver/settings_templates.py:1458 msgid "Switch control of LEDs between device and Solaar" msgstr "Ganti kontrol LED antara perangkat dan Solar" #: lib/logitech_receiver/settings_templates.py:1473 msgid "LED Zone Effects" msgstr "Efek Zona LED" #: lib/logitech_receiver/settings_templates.py:1474 msgid "Set effect for LED Zone" msgstr "Atur efek untuk Zona LED" #: lib/logitech_receiver/settings_templates.py:1477 msgid "Speed" msgstr "Kecepatan" #: lib/logitech_receiver/settings_templates.py:1478 msgid "Period" msgstr "Periode" #: lib/logitech_receiver/settings_templates.py:1479 msgid "Intensity" msgstr "Intensitas" #: lib/logitech_receiver/settings_templates.py:1480 msgid "Ramp" msgstr "" #: lib/logitech_receiver/settings_templates.py:1494 msgid "LEDs" msgstr "" #: lib/solaar/ui/__init__.py:112 msgid "Another Solaar process is already running so just expose its window" msgstr "Proses Solaar lain sudah berjalan jadi buka saja jendelanya" #: lib/solaar/ui/about.py:38 msgid "Manages Logitech receivers,\n" "keyboards, mice, and tablets." msgstr "Mengelola receiver Logitech,\n" "keyboard, mouse, dan tablet." #: lib/solaar/ui/about.py:46 msgid "Additional Programming" msgstr "Pemrograman Tambahan" #: lib/solaar/ui/about.py:47 msgid "GUI design" msgstr "desain GUI" #: lib/solaar/ui/about.py:49 msgid "Testing" msgstr "Pengujian" #: lib/solaar/ui/about.py:57 msgid "Logitech documentation" msgstr "Dokumentasi Logitech" #: lib/solaar/ui/action.py:83 lib/solaar/ui/action.py:87 #: lib/solaar/ui/window.py:199 msgid "Unpair" msgstr "Lepaskan pasangan" #: lib/solaar/ui/action.py:86 lib/solaar/ui/diversion_rules.py:104 msgid "Cancel" msgstr "Membatalkan" #: lib/solaar/ui/common.py:35 msgid "Permissions error" msgstr "Kesalahan izin" #: lib/solaar/ui/common.py:37 #, python-format msgid "Found a Logitech receiver or device (%s), but did not have " "permission to open it." msgstr "Menemukan receiver atau perangkat Logitech (%s), namun tidak " "memiliki izin untuk membukanya." #: lib/solaar/ui/common.py:39 msgid "If you've just installed Solaar, try disconnecting the receiver or " "device and then reconnecting it." msgstr "Kalau baru install Solaar, coba cabut receivernya atauperangkat dan " "kemudian menyambungkannya kembali." #: lib/solaar/ui/common.py:42 msgid "Cannot connect to device error" msgstr "Tidak dapat terhubung ke perangkat kesalahan" #: lib/solaar/ui/common.py:44 #, python-format msgid "Found a Logitech receiver or device at %s, but encountered an error " "connecting to it." msgstr "Menemukan receiver atau perangkat Logitech di %s, namun mengalami " "kesalahanmenghubungkannya." #: lib/solaar/ui/common.py:46 msgid "Try disconnecting the device and then reconnecting it or turning it " "off and then on." msgstr "Coba putuskan sambungan perangkat lalu sambungkan kembali atau " "putarmati lalu hidupkan." #: lib/solaar/ui/common.py:49 msgid "Unpairing failed" msgstr "Pembatalan pasangan gagal" #: lib/solaar/ui/common.py:51 #, python-brace-format msgid "Failed to unpair %{device} from %{receiver}." msgstr "Gagal melepaskan pasangan %{device} dari %{receiver}." #: lib/solaar/ui/common.py:53 msgid "The receiver returned an error, with no further details." msgstr "Penerima mengembalikan kesalahan, tanpa rincian lebih lanjut." #: lib/solaar/ui/config_panel.py:231 msgid "Complete - ENTER to change" msgstr "Selesai - ENTER untuk mengubah" #: lib/solaar/ui/config_panel.py:231 msgid "Incomplete" msgstr "Tidak lengkap" #: lib/solaar/ui/config_panel.py:476 lib/solaar/ui/config_panel.py:529 #, python-format msgid "%d value" msgid_plural "%d values" msgstr[0] "%d nilai" #: lib/solaar/ui/config_panel.py:612 msgid "Changes allowed" msgstr "Perubahan diperbolehkan" #: lib/solaar/ui/config_panel.py:613 msgid "No changes allowed" msgstr "Tidak ada perubahan yang diizinkan" #: lib/solaar/ui/config_panel.py:614 msgid "Ignore this setting" msgstr "Abaikan pengaturan ini" #: lib/solaar/ui/config_panel.py:659 msgid "Working" msgstr "Bekerja" #: lib/solaar/ui/config_panel.py:662 msgid "Read/write operation failed." msgstr "Operasi baca/tulis gagal." #: lib/solaar/ui/diversion_rules.py:66 msgid "Built-in rules" msgstr "Aturan bawaan" #: lib/solaar/ui/diversion_rules.py:66 msgid "User-defined rules" msgstr "Aturan yang ditentukan pengguna" #: lib/solaar/ui/diversion_rules.py:68 lib/solaar/ui/diversion_rules.py:1089 msgid "Rule" msgstr "Aturan" #: lib/solaar/ui/diversion_rules.py:69 lib/solaar/ui/diversion_rules.py:538 #: lib/solaar/ui/diversion_rules.py:665 msgid "Sub-rule" msgstr "Sub-aturan" #: lib/solaar/ui/diversion_rules.py:71 msgid "[empty]" msgstr "[kosong]" #: lib/solaar/ui/diversion_rules.py:95 msgid "Make changes permanent?" msgstr "Membuat perubahan permanen?" #: lib/solaar/ui/diversion_rules.py:100 msgid "Yes" msgstr "Ya" #: lib/solaar/ui/diversion_rules.py:102 msgid "No" msgstr "Tidak" #: lib/solaar/ui/diversion_rules.py:107 msgid "If you choose No, changes will be lost when Solaar is closed." msgstr "Jika Anda memilih Tidak, perubahan akan hilang saat Solaar ditutup." #: lib/solaar/ui/diversion_rules.py:138 msgid "Solaar Rule Editor" msgstr "Editor Aturan Surya" #: lib/solaar/ui/diversion_rules.py:231 msgid "Save changes" msgstr "Simpan perubahan" #: lib/solaar/ui/diversion_rules.py:236 msgid "Discard changes" msgstr "Membuang perubahan" #: lib/solaar/ui/diversion_rules.py:397 msgid "Insert here" msgstr "Masukkan di sini" #: lib/solaar/ui/diversion_rules.py:399 msgid "Insert above" msgstr "Masukkan di atas" #: lib/solaar/ui/diversion_rules.py:401 msgid "Insert below" msgstr "Masuk di bawah" #: lib/solaar/ui/diversion_rules.py:407 msgid "Insert new rule here" msgstr "Masukkan aturan baru di sini" #: lib/solaar/ui/diversion_rules.py:409 msgid "Insert new rule above" msgstr "Masukkan aturan baru di atas" #: lib/solaar/ui/diversion_rules.py:411 msgid "Insert new rule below" msgstr "Masukkan aturan baru di bawah" #: lib/solaar/ui/diversion_rules.py:456 msgid "Paste here" msgstr "Tempel di sini" #: lib/solaar/ui/diversion_rules.py:458 msgid "Paste above" msgstr "Tempel di atas" #: lib/solaar/ui/diversion_rules.py:460 msgid "Paste below" msgstr "Tempel di bawah" #: lib/solaar/ui/diversion_rules.py:466 msgid "Paste rule here" msgstr "Tempel aturan di sini" #: lib/solaar/ui/diversion_rules.py:468 msgid "Paste rule above" msgstr "Tempel aturan di atas" #: lib/solaar/ui/diversion_rules.py:470 msgid "Paste rule below" msgstr "Tempel aturan di bawah" #: lib/solaar/ui/diversion_rules.py:474 msgid "Paste rule" msgstr "Tempel aturan" #: lib/solaar/ui/diversion_rules.py:503 msgid "Flatten" msgstr "Meratakan" #: lib/solaar/ui/diversion_rules.py:536 msgid "Insert" msgstr "Menyisipkan" #: lib/solaar/ui/diversion_rules.py:539 lib/solaar/ui/diversion_rules.py:667 #: lib/solaar/ui/diversion_rules.py:1129 msgid "Or" msgstr "Atau" #: lib/solaar/ui/diversion_rules.py:540 lib/solaar/ui/diversion_rules.py:666 #: lib/solaar/ui/diversion_rules.py:1115 msgid "And" msgstr "Dan" #: lib/solaar/ui/diversion_rules.py:542 msgid "Condition" msgstr "Kondisi" #: lib/solaar/ui/diversion_rules.py:544 lib/solaar/ui/diversion_rules.py:1290 msgid "Feature" msgstr "Fitur" #: lib/solaar/ui/diversion_rules.py:545 lib/solaar/ui/diversion_rules.py:1325 msgid "Report" msgstr "Laporan" #: lib/solaar/ui/diversion_rules.py:546 lib/solaar/ui/diversion_rules.py:1204 msgid "Process" msgstr "Proses" #: lib/solaar/ui/diversion_rules.py:547 msgid "Mouse process" msgstr "Proses tikus" #: lib/solaar/ui/diversion_rules.py:548 lib/solaar/ui/diversion_rules.py:1362 msgid "Modifiers" msgstr "Pengubah" #: lib/solaar/ui/diversion_rules.py:549 lib/solaar/ui/diversion_rules.py:1414 msgid "Key" msgstr "Kunci" #: lib/solaar/ui/diversion_rules.py:550 lib/solaar/ui/diversion_rules.py:1455 msgid "KeyIsDown" msgstr "" #: lib/solaar/ui/diversion_rules.py:551 lib/solaar/ui/diversion_rules.py:2260 msgid "Active" msgstr "Aktif" #: lib/solaar/ui/diversion_rules.py:552 lib/solaar/ui/diversion_rules.py:2217 #: lib/solaar/ui/diversion_rules.py:2269 lib/solaar/ui/diversion_rules.py:2319 msgid "Device" msgstr "Perangkat" #: lib/solaar/ui/diversion_rules.py:553 lib/solaar/ui/diversion_rules.py:2295 msgid "Host" msgstr "" #: lib/solaar/ui/diversion_rules.py:554 lib/solaar/ui/diversion_rules.py:2337 msgid "Setting" msgstr "Pengaturan" #: lib/solaar/ui/diversion_rules.py:555 lib/solaar/ui/diversion_rules.py:1470 #: lib/solaar/ui/diversion_rules.py:1519 msgid "Test" msgstr "" #: lib/solaar/ui/diversion_rules.py:556 lib/solaar/ui/diversion_rules.py:1633 msgid "Test bytes" msgstr "Uji byte" #: lib/solaar/ui/diversion_rules.py:557 lib/solaar/ui/diversion_rules.py:1734 msgid "Mouse Gesture" msgstr "Gerakan Mouse" #: lib/solaar/ui/diversion_rules.py:561 msgid "Action" msgstr "Aksi" #: lib/solaar/ui/diversion_rules.py:563 lib/solaar/ui/diversion_rules.py:1844 msgid "Key press" msgstr "Tekan tombol" #: lib/solaar/ui/diversion_rules.py:564 lib/solaar/ui/diversion_rules.py:1895 msgid "Mouse scroll" msgstr "Gulir mouse" #: lib/solaar/ui/diversion_rules.py:565 lib/solaar/ui/diversion_rules.py:1956 msgid "Mouse click" msgstr "Klik mouse" #: lib/solaar/ui/diversion_rules.py:566 msgid "Set" msgstr "Mengatur" #: lib/solaar/ui/diversion_rules.py:567 lib/solaar/ui/diversion_rules.py:2026 msgid "Execute" msgstr "Menjalankan" #: lib/solaar/ui/diversion_rules.py:568 lib/solaar/ui/diversion_rules.py:1160 msgid "Later" msgstr "Nanti" #: lib/solaar/ui/diversion_rules.py:597 msgid "Insert new rule" msgstr "Masukkan aturan baru" #: lib/solaar/ui/diversion_rules.py:617 lib/solaar/ui/diversion_rules.py:1681 #: lib/solaar/ui/diversion_rules.py:1786 lib/solaar/ui/diversion_rules.py:1985 msgid "Delete" msgstr "Menghapus" #: lib/solaar/ui/diversion_rules.py:639 msgid "Negate" msgstr "Meniadakan" #: lib/solaar/ui/diversion_rules.py:663 msgid "Wrap with" msgstr "Bungkus dengan" #: lib/solaar/ui/diversion_rules.py:685 msgid "Cut" msgstr "Potong" #: lib/solaar/ui/diversion_rules.py:700 msgid "Paste" msgstr "Tempel" #: lib/solaar/ui/diversion_rules.py:706 msgid "Copy" msgstr "Salin" #: lib/solaar/ui/diversion_rules.py:1070 msgid "This editor does not support the selected rule component yet." msgstr "Editor ini belum mendukung komponen aturan yang dipilih." #: lib/solaar/ui/diversion_rules.py:1140 msgid "Number of seconds to delay." msgstr "Jumlah detik untuk ditunda." #: lib/solaar/ui/diversion_rules.py:1178 msgid "Not" msgstr "Bukan" #: lib/solaar/ui/diversion_rules.py:1187 msgid "X11 active process. For use in X11 only." msgstr "Proses aktif X11. Hanya untuk digunakan di X11." #: lib/solaar/ui/diversion_rules.py:1217 msgid "X11 mouse process. For use in X11 only." msgstr "Proses mouse X11. Hanya untuk digunakan di X11." #: lib/solaar/ui/diversion_rules.py:1234 msgid "MouseProcess" msgstr "Proses Tikus" #: lib/solaar/ui/diversion_rules.py:1258 msgid "Feature name of notification triggering rule processing." msgstr "Nama fitur notifikasi yang memicu pemrosesan aturan." #: lib/solaar/ui/diversion_rules.py:1305 msgid "Report number of notification triggering rule processing." msgstr "Laporkan jumlah pemberitahuan yang memicu pemrosesan aturan." #: lib/solaar/ui/diversion_rules.py:1338 msgid "Active keyboard modifiers. Not always available in Wayland." msgstr "Pengubah keyboard aktif. Tidak selalu tersedia di Wayland." #: lib/solaar/ui/diversion_rules.py:1378 msgid "Diverted key or button depressed or released.\n" "Use the Key/Button Diversion and Divert G Keys settings to divert " "keys and buttons." msgstr "Tombol atau tombol yang dialihkan ditekan atau dilepaskan.\n" "Gunakan pengaturan Pengalihan Tombol/Tombol dan Pengalihan Tombol G " "untuk mengalihkankunci dan tombol." #: lib/solaar/ui/diversion_rules.py:1387 msgid "Key down" msgstr "Kunci bawah" #: lib/solaar/ui/diversion_rules.py:1390 msgid "Key up" msgstr "Kunci atas" #: lib/solaar/ui/diversion_rules.py:1430 msgid "Diverted key or button is currently down.\n" "Use the Key/Button Diversion and Divert G Keys settings to divert " "keys and buttons." msgstr "Tombol atau tombol yang dialihkan sedang tidak aktif.\n" "Gunakan pengaturan Pengalihan Tombol/Tombol dan Pengalihan Tombol G " "untuk mengalihkankunci dan tombol." #: lib/solaar/ui/diversion_rules.py:1468 msgid "Test condition on notification triggering rule processing." msgstr "Uji kondisi pada notifikasi yang memicu pemrosesan aturan." #: lib/solaar/ui/diversion_rules.py:1472 msgid "Parameter" msgstr "" #: lib/solaar/ui/diversion_rules.py:1534 msgid "begin (inclusive)" msgstr "mulai (inklusif)" #: lib/solaar/ui/diversion_rules.py:1535 msgid "end (exclusive)" msgstr "akhir (eksklusif)" #: lib/solaar/ui/diversion_rules.py:1543 msgid "range" msgstr "jangkauan" #: lib/solaar/ui/diversion_rules.py:1546 msgid "minimum" msgstr "minimum" #: lib/solaar/ui/diversion_rules.py:1547 msgid "maximum" msgstr "maksimum" #: lib/solaar/ui/diversion_rules.py:1549 #, python-format msgid "bytes %(0)d to %(1)d, ranging from %(2)d to %(3)d" msgstr "byte %(0)d hingga %(1)d, mulai dari %(2)d hingga %(3)d" #: lib/solaar/ui/diversion_rules.py:1552 lib/solaar/ui/diversion_rules.py:1553 msgid "mask" msgstr "" #: lib/solaar/ui/diversion_rules.py:1554 #, python-format msgid "bytes %(0)d to %(1)d, mask %(2)d" msgstr "byte %(0)d hingga %(1)d, sembunyikan %(2)d" #: lib/solaar/ui/diversion_rules.py:1563 msgid "Bit or range test on bytes in notification message triggering rule " "processing." msgstr "Pengujian bit atau rentang pada byte dalam aturan pemicu pesan " "notifikasipengolahan." #: lib/solaar/ui/diversion_rules.py:1573 msgid "type" msgstr "jenis" #: lib/solaar/ui/diversion_rules.py:1661 msgid "Mouse gesture with optional initiating button followed by zero or " "more mouse movements." msgstr "Gerakan mouse dengan tombol memulai opsional diikuti dengan nol atau " "lebih banyak gerakan tikus." #: lib/solaar/ui/diversion_rules.py:1666 msgid "Add movement" msgstr "Tambahkan gerakan" #: lib/solaar/ui/diversion_rules.py:1760 msgid "Simulate a chorded key click or depress or release.\n" "On Wayland requires write access to /dev/uinput." msgstr "Simulasikan klik atau tekan atau lepaskan kunci chord.\n" "On Wayland memerlukan akses tulis ke /dev/uinput." #: lib/solaar/ui/diversion_rules.py:1765 msgid "Add key" msgstr "Tambahkan kunci" #: lib/solaar/ui/diversion_rules.py:1768 msgid "Click" msgstr "Klik" #: lib/solaar/ui/diversion_rules.py:1771 msgid "Depress" msgstr "Menekan" #: lib/solaar/ui/diversion_rules.py:1774 msgid "Release" msgstr "Melepaskan" #: lib/solaar/ui/diversion_rules.py:1859 msgid "Simulate a mouse scroll.\n" "On Wayland requires write access to /dev/uinput." msgstr "Simulasikan gulir mouse.\n" "On Wayland memerlukan akses tulis ke /dev/uinput." #: lib/solaar/ui/diversion_rules.py:1915 msgid "Simulate a mouse click.\n" "On Wayland requires write access to /dev/uinput." msgstr "Simulasikan klik mouse.\n" "On Wayland memerlukan akses tulis ke /dev/uinput." #: lib/solaar/ui/diversion_rules.py:1918 msgid "Button" msgstr "Tombol" #: lib/solaar/ui/diversion_rules.py:1919 msgid "Count and Action" msgstr "Hitungan dan Tindakan" #: lib/solaar/ui/diversion_rules.py:1968 msgid "Execute a command with arguments." msgstr "Jalankan perintah dengan argumen." #: lib/solaar/ui/diversion_rules.py:1971 msgid "Add argument" msgstr "Tambahkan argumen" #: lib/solaar/ui/diversion_rules.py:2046 msgid "Toggle" msgstr "Beralih" #: lib/solaar/ui/diversion_rules.py:2047 msgid "True" msgstr "" #: lib/solaar/ui/diversion_rules.py:2048 msgid "False" msgstr "" #: lib/solaar/ui/diversion_rules.py:2061 msgid "Unsupported setting" msgstr "Pengaturan tidak didukung" #: lib/solaar/ui/diversion_rules.py:2223 lib/solaar/ui/diversion_rules.py:2243 #: lib/solaar/ui/diversion_rules.py:2325 lib/solaar/ui/diversion_rules.py:2568 #: lib/solaar/ui/diversion_rules.py:2586 msgid "Originating device" msgstr "Perangkat asal" #: lib/solaar/ui/diversion_rules.py:2256 msgid "Device is active and its settings can be changed." msgstr "Perangkat aktif dan pengaturannya dapat diubah." #: lib/solaar/ui/diversion_rules.py:2265 msgid "Device that originated the current notification." msgstr "Perangkat yang memunculkan notifikasi saat ini." #: lib/solaar/ui/diversion_rules.py:2278 msgid "Name of host computer." msgstr "Nama komputer induk." #: lib/solaar/ui/diversion_rules.py:2345 msgid "Value" msgstr "Nilai" #: lib/solaar/ui/diversion_rules.py:2353 msgid "Item" msgstr "" #: lib/solaar/ui/diversion_rules.py:2627 msgid "Change setting on device" msgstr "Ubah pengaturan pada perangkat" #: lib/solaar/ui/diversion_rules.py:2643 msgid "Setting on device" msgstr "Pengaturan pada perangkat" #: lib/solaar/ui/notify.py:118 msgid "unspecified reason" msgstr "alasan yang tidak ditentukan" #: lib/solaar/ui/pair_window.py:126 lib/solaar/ui/pair_window.py:260 #: lib/solaar/ui/pair_window.py:296 #, python-format msgid "%(receiver_name)s: pair new device" msgstr "%(receiver_name)s: memasangkan perangkat baru" #: lib/solaar/ui/pair_window.py:127 #, python-format msgid "Enter passcode on %(name)s." msgstr "Masukkan kode sandi pada %(name)s." #: lib/solaar/ui/pair_window.py:130 #, python-format msgid "Type %(passcode)s and then press the enter key." msgstr "Ketik %(passcode)s lalu tekan tombol enter." #: lib/solaar/ui/pair_window.py:133 msgid "left" msgstr "Kiri" #: lib/solaar/ui/pair_window.py:133 msgid "right" msgstr "Kanan" #: lib/solaar/ui/pair_window.py:135 #, python-format msgid "Press %(code)s\n" "and then press left and right buttons simultaneously." msgstr "Tekan %(code)s\n" "lalu tekan tombol kiri dan kanan secara bersamaan." #: lib/solaar/ui/pair_window.py:192 msgid "Pairing failed" msgstr "Pemasangan gagal" #: lib/solaar/ui/pair_window.py:194 msgid "Make sure your device is within range, and has a decent battery " "charge." msgstr "Pastikan perangkat Anda berada dalam jangkauan dan memiliki baterai " "yang layakmengenakan biaya." #: lib/solaar/ui/pair_window.py:196 msgid "A new device was detected, but it is not compatible with this " "receiver." msgstr "Perangkat baru terdeteksi, tetapi tidak kompatibel dengan " "inipenerima." #: lib/solaar/ui/pair_window.py:198 msgid "More paired devices than receiver can support." msgstr "Lebih banyak perangkat berpasangan daripada yang dapat didukung oleh " "receiver." #: lib/solaar/ui/pair_window.py:200 msgid "No further details are available about the error." msgstr "Tidak ada rincian lebih lanjut yang tersedia tentang kesalahan " "tersebut." #: lib/solaar/ui/pair_window.py:214 msgid "Found a new device:" msgstr "Menemukan perangkat baru:" #: lib/solaar/ui/pair_window.py:239 msgid "The wireless link is not encrypted" msgstr "Tautan nirkabel tidak dienkripsi" #: lib/solaar/ui/pair_window.py:268 msgid "Unifying receivers are only compatible with Unifying devices." msgstr "Penerima Unifying hanya kompatibel dengan perangkat Unifying." #: lib/solaar/ui/pair_window.py:270 msgid "Bolt receivers are only compatible with Bolt devices." msgstr "Penerima Bolt hanya kompatibel dengan perangkat Bolt." #: lib/solaar/ui/pair_window.py:272 msgid "Other receivers are only compatible with a few devices." msgstr "Receiver lain hanya kompatibel dengan beberapa perangkat." #: lib/solaar/ui/pair_window.py:274 msgid "The device must not be paired with a nearby powered-on receiver." msgstr "Perangkat tidak boleh dipasangkan dengan receiver terdekat yang " "aktif." #: lib/solaar/ui/pair_window.py:278 msgid "Press a pairing button or key until the pairing light flashes " "quickly." msgstr "Tekan tombol atau kunci penyandingan hingga lampu penyandingan " "berkedipdengan cepat." #: lib/solaar/ui/pair_window.py:280 msgid "You may have to first turn the device off and on again." msgstr "Anda mungkin harus mematikan dan menghidupkan kembali perangkat " "terlebih dahulu." #: lib/solaar/ui/pair_window.py:282 msgid "Turn on the device you want to pair." msgstr "Nyalakan perangkat yang ingin Anda pasangkan." #: lib/solaar/ui/pair_window.py:284 msgid "If the device is already turned on, turn it off and on again." msgstr "Jika perangkat sudah dihidupkan, matikan dan hidupkan kembali." #: lib/solaar/ui/pair_window.py:288 #, python-format msgid "\n" "\n" "This receiver has %d pairing remaining." msgid_plural "\n" "\n" "This receiver has %d pairings remaining." msgstr[0] "\n" "\n" "Penerima ini memiliki %d sisa penyandingan." #: lib/solaar/ui/pair_window.py:294 msgid "\n" "Cancelling at this point will not use up a pairing." msgstr "\n" "Membatalkan pada saat ini tidak akan menghabiskan pasangan." #: lib/solaar/ui/tray.py:58 msgid "No supported device found" msgstr "Tidak ditemukan perangkat yang didukung" #: lib/solaar/ui/tray.py:63 lib/solaar/ui/window.py:321 #, python-format msgid "About %s" msgstr "Tentang %s" #: lib/solaar/ui/tray.py:64 lib/solaar/ui/window.py:319 #, python-format msgid "Quit %s" msgstr "Keluar %s" #: lib/solaar/ui/tray.py:294 lib/solaar/ui/tray.py:302 msgid "no receiver" msgstr "tidak ada penerima" #: lib/solaar/ui/tray.py:315 lib/solaar/ui/tray.py:320 msgid "offline" msgstr "" #: lib/solaar/ui/tray.py:318 msgid "no status" msgstr "tidak ada status" #: lib/solaar/ui/window.py:99 msgid "Scanning" msgstr "Memindai" #: lib/solaar/ui/window.py:132 msgid "Battery" msgstr "Baterai" #: lib/solaar/ui/window.py:135 msgid "Wireless Link" msgstr "Tautan Nirkabel" #: lib/solaar/ui/window.py:139 msgid "Lighting" msgstr "" #: lib/solaar/ui/window.py:173 msgid "Show Technical Details" msgstr "Tampilkan Detail Teknis" #: lib/solaar/ui/window.py:189 msgid "Pair new device" msgstr "Pasangkan perangkat baru" #: lib/solaar/ui/window.py:207 msgid "Select a device" msgstr "Pilih perangkat" #: lib/solaar/ui/window.py:324 msgid "Rule Editor" msgstr "Editor Aturan" #: lib/solaar/ui/window.py:543 msgid "Path" msgstr "Path" #: lib/solaar/ui/window.py:546 msgid "USB ID" msgstr "ID USB" #: lib/solaar/ui/window.py:549 lib/solaar/ui/window.py:551 #: lib/solaar/ui/window.py:566 lib/solaar/ui/window.py:568 msgid "Serial" msgstr "" #: lib/solaar/ui/window.py:555 msgid "Index" msgstr "Indeks" #: lib/solaar/ui/window.py:557 msgid "Wireless PID" msgstr "PID Nirkabel" #: lib/solaar/ui/window.py:559 msgid "Product ID" msgstr "ID Produk" #: lib/solaar/ui/window.py:561 msgid "Protocol" msgstr "Protokol" #: lib/solaar/ui/window.py:561 msgid "Unknown" msgstr "Tidak Diketahui" #: lib/solaar/ui/window.py:563 msgid "Polling rate" msgstr "Tingkat pemungutan suara" #: lib/solaar/ui/window.py:570 msgid "Unit ID" msgstr "ID Satuan" #: lib/solaar/ui/window.py:584 msgid "Notifications" msgstr "Pemberitahuan" #: lib/solaar/ui/window.py:628 msgid "No device paired." msgstr "Tidak ada perangkat yang dipasangkan." #: lib/solaar/ui/window.py:637 #, python-format msgid "Up to %(max_count)s device can be paired to this receiver." msgid_plural "Up to %(max_count)s devices can be paired to this receiver." msgstr[0] "Hingga %(max_count)s perangkat dapat dipasangkan ke receiver " "ini." #: lib/solaar/ui/window.py:648 #, python-format msgid "This receiver has %d pairing remaining." msgid_plural "This receiver has %d pairings remaining." msgstr[0] "Penerima ini memiliki %d sisa penyandingan." #: lib/solaar/ui/window.py:705 msgid "Battery Voltage" msgstr "Tegangan Baterai" #: lib/solaar/ui/window.py:707 msgid "Voltage reported by battery" msgstr "Tegangan dilaporkan oleh baterai" #: lib/solaar/ui/window.py:709 msgid "Battery Level" msgstr "Tingkat Baterai" #: lib/solaar/ui/window.py:711 msgid "Approximate level reported by battery" msgstr "Perkiraan level yang dilaporkan oleh baterai" #: lib/solaar/ui/window.py:718 lib/solaar/ui/window.py:720 msgid "next reported " msgstr "selanjutnya dilaporkan " #: lib/solaar/ui/window.py:721 msgid " and next level to be reported." msgstr " dan tingkat selanjutnya yang akan dilaporkan." #: lib/solaar/ui/window.py:737 msgid "encrypted" msgstr "terenkripsi" #: lib/solaar/ui/window.py:739 msgid "The wireless link between this device and its receiver is encrypted." msgstr "Hubungan nirkabel antara perangkat ini dan penerimanya dienkripsi." #: lib/solaar/ui/window.py:741 msgid "not encrypted" msgstr "tidak dienkripsi" #: lib/solaar/ui/window.py:745 msgid "The wireless link between this device and its receiver is not " "encrypted.\n" "This is a security issue for pointing devices, and a major security " "issue for text-input devices." msgstr "Hubungan nirkabel antara perangkat ini dan penerimanya tidak " "terenkripsi.\n" "Ini adalah masalah keamanan untuk perangkat penunjuk, dan merupakan " "masalah keamanan utamamasalah untuk perangkat input teks." #: lib/solaar/ui/window.py:761 #, python-format msgid "%(light_level)d lux" msgstr "%(light_level)d mewah" Solaar-1.1.14/po/it.po000066400000000000000000001461461473525213700144360ustar00rootroot00000000000000# Italian translations for solaar package. # Copyright (C) 2013 THE solaar'S COPYRIGHT HOLDER # This file is distributed under the same license as the solaar package. # Automatically generated, 2013. # msgid "" msgstr "Project-Id-Version: solaar 0.9.2\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-12-28 17:40+0100\n" "PO-Revision-Date: 2021-01-28 19:21+0000\n" "Last-Translator: Lorenzo \n" "Language-Team: none\n" "Language: it\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Launchpad-Export-Date: 2021-02-17 00:56+0000\n" "X-Generator: Launchpad (build " "b3a93345a124168b715ec9ae0945884caa15f58f)\n" #: lib/logitech_receiver/base_usb.py:46 msgid "Bolt Receiver" msgstr "" #: lib/logitech_receiver/base_usb.py:57 msgid "Unifying Receiver" msgstr "" #: lib/logitech_receiver/base_usb.py:67 lib/logitech_receiver/base_usb.py:78 #: lib/logitech_receiver/base_usb.py:90 lib/logitech_receiver/base_usb.py:102 #: lib/logitech_receiver/base_usb.py:114 msgid "Nano Receiver" msgstr "" #: lib/logitech_receiver/base_usb.py:124 msgid "Lightspeed Receiver" msgstr "" #: lib/logitech_receiver/base_usb.py:133 msgid "EX100 Receiver 27 Mhz" msgstr "" #: lib/logitech_receiver/i18n.py:30 msgid "empty" msgstr "vuoto" #: lib/logitech_receiver/i18n.py:31 msgid "critical" msgstr "critico" #: lib/logitech_receiver/i18n.py:32 msgid "low" msgstr "bassa" #: lib/logitech_receiver/i18n.py:33 msgid "average" msgstr "" #: lib/logitech_receiver/i18n.py:34 msgid "good" msgstr "buono" #: lib/logitech_receiver/i18n.py:35 msgid "full" msgstr "piena" #: lib/logitech_receiver/i18n.py:38 msgid "discharging" msgstr "in scarica" #: lib/logitech_receiver/i18n.py:39 msgid "recharging" msgstr "in ricarica" #: lib/logitech_receiver/i18n.py:40 lib/solaar/ui/window.py:711 msgid "charging" msgstr "in ricarica" #: lib/logitech_receiver/i18n.py:41 msgid "not charging" msgstr "" #: lib/logitech_receiver/i18n.py:42 msgid "almost full" msgstr "quasi piena" #: lib/logitech_receiver/i18n.py:43 msgid "charged" msgstr "carico" #: lib/logitech_receiver/i18n.py:44 msgid "slow recharge" msgstr "ricarica lenta" #: lib/logitech_receiver/i18n.py:45 msgid "invalid battery" msgstr "batteria non valida" #: lib/logitech_receiver/i18n.py:46 msgid "thermal error" msgstr "errore di temperatura" #: lib/logitech_receiver/i18n.py:47 msgid "error" msgstr "" #: lib/logitech_receiver/i18n.py:48 msgid "standard" msgstr "" #: lib/logitech_receiver/i18n.py:49 msgid "fast" msgstr "" #: lib/logitech_receiver/i18n.py:50 msgid "slow" msgstr "" #: lib/logitech_receiver/i18n.py:53 msgid "device timeout" msgstr "timeout del dispositivo" #: lib/logitech_receiver/i18n.py:54 msgid "device not supported" msgstr "dispositivo non supportato" #: lib/logitech_receiver/i18n.py:55 msgid "too many devices" msgstr "troppe periferiche" #: lib/logitech_receiver/i18n.py:56 msgid "sequence timeout" msgstr "timeout della sequenza" #: lib/logitech_receiver/i18n.py:59 lib/solaar/ui/window.py:572 msgid "Firmware" msgstr "Firmware" #: lib/logitech_receiver/i18n.py:60 msgid "Bootloader" msgstr "Bootloader" #: lib/logitech_receiver/i18n.py:61 msgid "Hardware" msgstr "Hardware" #: lib/logitech_receiver/i18n.py:62 msgid "Other" msgstr "Altro" #: lib/logitech_receiver/i18n.py:65 msgid "Left Button" msgstr "" #: lib/logitech_receiver/i18n.py:66 msgid "Right Button" msgstr "" #: lib/logitech_receiver/i18n.py:67 msgid "Middle Button" msgstr "" #: lib/logitech_receiver/i18n.py:68 msgid "Back Button" msgstr "" #: lib/logitech_receiver/i18n.py:69 msgid "Forward Button" msgstr "" #: lib/logitech_receiver/i18n.py:70 msgid "Mouse Gesture Button" msgstr "" #: lib/logitech_receiver/i18n.py:71 msgid "Smart Shift" msgstr "" #: lib/logitech_receiver/i18n.py:72 msgid "DPI Switch" msgstr "" #: lib/logitech_receiver/i18n.py:73 msgid "Left Tilt" msgstr "" #: lib/logitech_receiver/i18n.py:74 msgid "Right Tilt" msgstr "" #: lib/logitech_receiver/i18n.py:75 msgid "Left Click" msgstr "" #: lib/logitech_receiver/i18n.py:76 msgid "Right Click" msgstr "" #: lib/logitech_receiver/i18n.py:77 msgid "Mouse Middle Button" msgstr "" #: lib/logitech_receiver/i18n.py:78 msgid "Mouse Back Button" msgstr "" #: lib/logitech_receiver/i18n.py:79 msgid "Mouse Forward Button" msgstr "" #: lib/logitech_receiver/i18n.py:80 msgid "Gesture Button Navigation" msgstr "" #: lib/logitech_receiver/i18n.py:81 msgid "Mouse Scroll Left Button" msgstr "" #: lib/logitech_receiver/i18n.py:82 msgid "Mouse Scroll Right Button" msgstr "" #: lib/logitech_receiver/i18n.py:85 msgid "pressed" msgstr "" #: lib/logitech_receiver/i18n.py:86 msgid "released" msgstr "" #: lib/logitech_receiver/notifications.py:75 #: lib/logitech_receiver/notifications.py:126 msgid "pairing lock is closed" msgstr "" #: lib/logitech_receiver/notifications.py:75 #: lib/logitech_receiver/notifications.py:126 msgid "pairing lock is open" msgstr "" #: lib/logitech_receiver/notifications.py:92 msgid "discovery lock is closed" msgstr "" #: lib/logitech_receiver/notifications.py:92 msgid "discovery lock is open" msgstr "" #: lib/logitech_receiver/notifications.py:224 lib/solaar/ui/notify.py:122 msgid "connected" msgstr "connesso" #: lib/logitech_receiver/notifications.py:224 msgid "disconnected" msgstr "disconnesso" #: lib/logitech_receiver/notifications.py:262 lib/solaar/ui/notify.py:120 msgid "unpaired" msgstr "non associato" #: lib/logitech_receiver/notifications.py:304 msgid "powered on" msgstr "acceso" #: lib/logitech_receiver/settings.py:750 msgid "register" msgstr "" #: lib/logitech_receiver/settings.py:764 lib/logitech_receiver/settings.py:791 msgid "feature" msgstr "" #: lib/logitech_receiver/settings_templates.py:139 msgid "Swap Fx function" msgstr "Inverti funzioni Fx" #: lib/logitech_receiver/settings_templates.py:140 msgid "When set, the F1..F12 keys will activate their special function,\n" "and you must hold the FN key to activate their standard function." msgstr "Quando abilitato, i tasti F1..F12 attivano le funzioni speciali,\n" "e dovrai tenere premuto il tasto FN per attivare le funzioni " "standard." #: lib/logitech_receiver/settings_templates.py:142 msgid "When unset, the F1..F12 keys will activate their standard function,\n" "and you must hold the FN key to activate their special function." msgstr "Quando disabilitato, i tasti F1..F12 attivano le funzioni standard,\n" "dovrai tenere premuto il tasto FN per attivare le funzioni speciali." #: lib/logitech_receiver/settings_templates.py:149 msgid "Hand Detection" msgstr "Rilevamento mano" #: lib/logitech_receiver/settings_templates.py:150 msgid "Turn on illumination when the hands hover over the keyboard." msgstr "Accendi l'illuminazione quando le mani sono sopra la tastiera." #: lib/logitech_receiver/settings_templates.py:157 msgid "Scroll Wheel Smooth Scrolling" msgstr "" #: lib/logitech_receiver/settings_templates.py:158 #: lib/logitech_receiver/settings_templates.py:239 #: lib/logitech_receiver/settings_templates.py:267 msgid "High-sensitivity mode for vertical scroll with the wheel." msgstr "Modalità di alta sensibilità per lo scorrimento verticale con la " "rotella." #: lib/logitech_receiver/settings_templates.py:165 msgid "Side Scrolling" msgstr "Scorrimento Laterale" #: lib/logitech_receiver/settings_templates.py:167 msgid "When disabled, pushing the wheel sideways sends custom button " "events\n" "instead of the standard side-scrolling events." msgstr "Quando disabilitato, spingendo la rotella lateralmente vengono " "inviati eventi personalizzati\n" "invece degli eventi standard di scorrimento laterale." #: lib/logitech_receiver/settings_templates.py:177 msgid "Sensitivity (DPI - older mice)" msgstr "" #: lib/logitech_receiver/settings_templates.py:178 #: lib/logitech_receiver/settings_templates.py:712 msgid "Mouse movement sensitivity" msgstr "Sensibilità al movimento del mouse" #: lib/logitech_receiver/settings_templates.py:208 #: lib/logitech_receiver/settings_templates.py:218 #: lib/logitech_receiver/settings_templates.py:225 msgid "Backlight" msgstr "Retroilluminazione" #: lib/logitech_receiver/settings_templates.py:209 #: lib/logitech_receiver/settings_templates.py:226 msgid "Set illumination time for keyboard." msgstr "" #: lib/logitech_receiver/settings_templates.py:219 msgid "Turn illumination on or off on keyboard." msgstr "Accendi o spegni l'illuminazione della tastiera" #: lib/logitech_receiver/settings_templates.py:237 msgid "Scroll Wheel High Resolution" msgstr "" #: lib/logitech_receiver/settings_templates.py:240 #: lib/logitech_receiver/settings_templates.py:268 msgid "Set to ignore if scrolling is abnormally fast or slow" msgstr "" #: lib/logitech_receiver/settings_templates.py:247 #: lib/logitech_receiver/settings_templates.py:277 msgid "Scroll Wheel Diversion" msgstr "" #: lib/logitech_receiver/settings_templates.py:249 msgid "Make scroll wheel send LOWRES_WHEEL HID++ notifications (which " "trigger Solaar rules but are otherwise ignored)." msgstr "" #: lib/logitech_receiver/settings_templates.py:256 msgid "Scroll Wheel Direction" msgstr "Direzione di scorrimento con la rotella" #: lib/logitech_receiver/settings_templates.py:257 msgid "Invert direction for vertical scroll with wheel." msgstr "Inverti la direzione per lo scorrimento verticale con la rotella" #: lib/logitech_receiver/settings_templates.py:265 msgid "Scroll Wheel Resolution" msgstr "Risoluzione dello scorrimento con la rotella" #: lib/logitech_receiver/settings_templates.py:279 msgid "Make scroll wheel send HIRES_WHEEL HID++ notifications (which " "trigger Solaar rules but are otherwise ignored)." msgstr "" #: lib/logitech_receiver/settings_templates.py:288 msgid "Sensitivity (Pointer Speed)" msgstr "Sensibilità (velocità del puntatore)" #: lib/logitech_receiver/settings_templates.py:289 msgid "Speed multiplier for mouse (256 is normal multiplier)." msgstr "Moltiplicatore della velocità del mouse (256 è il moltiplicatore " "normale)" #: lib/logitech_receiver/settings_templates.py:299 msgid "Thumb Wheel Diversion" msgstr "" #: lib/logitech_receiver/settings_templates.py:301 msgid "Make thumb wheel send THUMB_WHEEL HID++ notifications (which trigger " "Solaar rules but are otherwise ignored)." msgstr "" #: lib/logitech_receiver/settings_templates.py:310 msgid "Thumb Wheel Direction" msgstr "" #: lib/logitech_receiver/settings_templates.py:311 msgid "Invert thumb wheel scroll direction." msgstr "" #: lib/logitech_receiver/settings_templates.py:319 msgid "Onboard Profiles" msgstr "" #: lib/logitech_receiver/settings_templates.py:320 msgid "Enable onboard profiles, which often control report rate and " "keyboard lighting" msgstr "" #: lib/logitech_receiver/settings_templates.py:330 msgid "Polling Rate (ms)" msgstr "" #: lib/logitech_receiver/settings_templates.py:332 msgid "Frequency of device polling, in milliseconds" msgstr "" #: lib/logitech_receiver/settings_templates.py:333 #: lib/logitech_receiver/settings_templates.py:1046 #: lib/logitech_receiver/settings_templates.py:1076 msgid "May need Onboard Profiles set to Disable to be effective." msgstr "" #: lib/logitech_receiver/settings_templates.py:365 msgid "Divert crown events" msgstr "" #: lib/logitech_receiver/settings_templates.py:366 msgid "Make crown send CROWN HID++ notifications (which trigger Solaar " "rules but are otherwise ignored)." msgstr "" #: lib/logitech_receiver/settings_templates.py:374 msgid "Crown smooth scroll" msgstr "" #: lib/logitech_receiver/settings_templates.py:375 msgid "Set crown smooth scroll" msgstr "" #: lib/logitech_receiver/settings_templates.py:383 msgid "Divert G Keys" msgstr "" #: lib/logitech_receiver/settings_templates.py:385 msgid "Make G keys send GKEY HID++ notifications (which trigger Solaar " "rules but are otherwise ignored)." msgstr "" #: lib/logitech_receiver/settings_templates.py:386 msgid "May also make M keys and MR key send HID++ notifications" msgstr "" #: lib/logitech_receiver/settings_templates.py:402 msgid "Scroll Wheel Ratcheted" msgstr "" #: lib/logitech_receiver/settings_templates.py:403 msgid "Switch the mouse wheel between speed-controlled ratcheting and " "always freespin." msgstr "" #: lib/logitech_receiver/settings_templates.py:405 msgid "Freespinning" msgstr "" #: lib/logitech_receiver/settings_templates.py:405 msgid "Ratcheted" msgstr "" #: lib/logitech_receiver/settings_templates.py:412 msgid "Scroll Wheel Ratchet Speed" msgstr "" #: lib/logitech_receiver/settings_templates.py:414 msgid "Use the mouse wheel speed to switch between ratcheted and " "freespinning.\n" "The mouse wheel is always ratcheted at 50." msgstr "" #: lib/logitech_receiver/settings_templates.py:463 msgid "Key/Button Actions" msgstr "Azioni tasto/pulsante" #: lib/logitech_receiver/settings_templates.py:465 msgid "Change the action for the key or button." msgstr "Cambia l'azione per il tasto o il pulsante" #: lib/logitech_receiver/settings_templates.py:465 msgid "Overridden by diversion." msgstr "" #: lib/logitech_receiver/settings_templates.py:466 msgid "Changing important actions (such as for the left mouse button) can " "result in an unusable system." msgstr "Cambiare azioni importanti (come per il bottone sinistro del mouse) " "può rendere inutilizzabile il sistema" #: lib/logitech_receiver/settings_templates.py:639 msgid "Key/Button Diversion" msgstr "" #: lib/logitech_receiver/settings_templates.py:640 msgid "Make the key or button send HID++ notifications (Diverted) or " "initiate Mouse Gestures or Sliding DPI" msgstr "" #: lib/logitech_receiver/settings_templates.py:643 #: lib/logitech_receiver/settings_templates.py:644 #: lib/logitech_receiver/settings_templates.py:645 msgid "Diverted" msgstr "" #: lib/logitech_receiver/settings_templates.py:643 #: lib/logitech_receiver/settings_templates.py:644 msgid "Mouse Gestures" msgstr "" #: lib/logitech_receiver/settings_templates.py:643 #: lib/logitech_receiver/settings_templates.py:644 #: lib/logitech_receiver/settings_templates.py:645 msgid "Regular" msgstr "" #: lib/logitech_receiver/settings_templates.py:643 msgid "Sliding DPI" msgstr "" #: lib/logitech_receiver/settings_templates.py:711 msgid "Sensitivity (DPI)" msgstr "Sensibilità (DPI)" #: lib/logitech_receiver/settings_templates.py:752 msgid "Sensitivity Switching" msgstr "" #: lib/logitech_receiver/settings_templates.py:754 msgid "Switch the current sensitivity and the remembered sensitivity when " "the key or button is pressed.\n" "If there is no remembered sensitivity, just remember the current " "sensitivity" msgstr "" #: lib/logitech_receiver/settings_templates.py:758 msgid "Off" msgstr "" #: lib/logitech_receiver/settings_templates.py:791 msgid "Disable keys" msgstr "Disabilita i pulsanti" #: lib/logitech_receiver/settings_templates.py:792 msgid "Disable specific keyboard keys." msgstr "Disabilita dei pulsanti specifici della tastiera" #: lib/logitech_receiver/settings_templates.py:795 #, python-format msgid "Disables the %s key." msgstr "Disabilita il tasto %s" #: lib/logitech_receiver/settings_templates.py:809 #: lib/logitech_receiver/settings_templates.py:860 msgid "Set OS" msgstr "Specifica il sistema operativo" #: lib/logitech_receiver/settings_templates.py:810 #: lib/logitech_receiver/settings_templates.py:861 msgid "Change keys to match OS." msgstr "Cambia i pulsanti in funzione del sistema operativo" #: lib/logitech_receiver/settings_templates.py:873 msgid "Change Host" msgstr "Cambia Host" #: lib/logitech_receiver/settings_templates.py:874 msgid "Switch connection to a different host" msgstr "Connetti ad un host differente" #: lib/logitech_receiver/settings_templates.py:900 msgid "Performs a left click." msgstr "Clicca con il pulsante sinistro" #: lib/logitech_receiver/settings_templates.py:900 msgid "Single tap" msgstr "tocco singolo" #: lib/logitech_receiver/settings_templates.py:901 msgid "Performs a right click." msgstr "Clicca con il pulsante destro" #: lib/logitech_receiver/settings_templates.py:901 msgid "Single tap with two fingers" msgstr "tocco singolo con due dita" #: lib/logitech_receiver/settings_templates.py:902 msgid "Single tap with three fingers" msgstr "tocco singolo con tre dita" #: lib/logitech_receiver/settings_templates.py:906 msgid "Double tap" msgstr "doppio tocco" #: lib/logitech_receiver/settings_templates.py:906 msgid "Performs a double click." msgstr "Esegue un doppio click" #: lib/logitech_receiver/settings_templates.py:907 msgid "Double tap with two fingers" msgstr "doppio tocco con due dita" #: lib/logitech_receiver/settings_templates.py:908 msgid "Double tap with three fingers" msgstr "doppio tocco con tre dita" #: lib/logitech_receiver/settings_templates.py:911 msgid "Drags items by dragging the finger after double tapping." msgstr "Trascina gli elementi muovendo il dito dopo aver fatto un doppio tap" #: lib/logitech_receiver/settings_templates.py:911 msgid "Tap and drag" msgstr "tocca e trascina" #: lib/logitech_receiver/settings_templates.py:913 msgid "Drags items by dragging the fingers after double tapping." msgstr "Trascina gli elementi muovendo il dito dopo aver fatto un doppio tap" #: lib/logitech_receiver/settings_templates.py:913 msgid "Tap and drag with two fingers" msgstr "tocca e trascina con due dita" #: lib/logitech_receiver/settings_templates.py:914 msgid "Tap and drag with three fingers" msgstr "tocca e trascina con tre dita" #: lib/logitech_receiver/settings_templates.py:917 msgid "Disables tap and edge gestures (equivalent to pressing Fn+LeftClick)." msgstr "" #: lib/logitech_receiver/settings_templates.py:917 msgid "Suppress tap and edge gestures" msgstr "" #: lib/logitech_receiver/settings_templates.py:918 msgid "Scroll with one finger" msgstr "scorri con un dito" #: lib/logitech_receiver/settings_templates.py:918 #: lib/logitech_receiver/settings_templates.py:919 #: lib/logitech_receiver/settings_templates.py:922 msgid "Scrolls." msgstr "Scorre." #: lib/logitech_receiver/settings_templates.py:919 #: lib/logitech_receiver/settings_templates.py:922 msgid "Scroll with two fingers" msgstr "scorri con due dita" #: lib/logitech_receiver/settings_templates.py:920 msgid "Scroll horizontally with two fingers" msgstr "scorri orizzontalmente con due dita" #: lib/logitech_receiver/settings_templates.py:920 msgid "Scrolls horizontally." msgstr "scorre orizzontalmente" #: lib/logitech_receiver/settings_templates.py:921 msgid "Scroll vertically with two fingers" msgstr "scorri verticalmente con due dita" #: lib/logitech_receiver/settings_templates.py:921 msgid "Scrolls vertically." msgstr "scorre verticalmente" #: lib/logitech_receiver/settings_templates.py:923 msgid "Inverts the scrolling direction." msgstr "inverte la direzione di scorrimento" #: lib/logitech_receiver/settings_templates.py:923 msgid "Natural scrolling" msgstr "Scorrimento naturale" #: lib/logitech_receiver/settings_templates.py:924 msgid "Enables the thumbwheel." msgstr "" #: lib/logitech_receiver/settings_templates.py:924 msgid "Thumbwheel" msgstr "" #: lib/logitech_receiver/settings_templates.py:935 #: lib/logitech_receiver/settings_templates.py:939 msgid "Swipe from the top edge" msgstr "scorri dal bordo superiore" #: lib/logitech_receiver/settings_templates.py:936 msgid "Swipe from the left edge" msgstr "scorri dal bordo sinistro" #: lib/logitech_receiver/settings_templates.py:937 msgid "Swipe from the right edge" msgstr "scorri dal bordo destro" #: lib/logitech_receiver/settings_templates.py:938 msgid "Swipe from the bottom edge" msgstr "scorri dal bordo inferiore" #: lib/logitech_receiver/settings_templates.py:940 msgid "Swipe two fingers from the left edge" msgstr "scorri con due dita dal bordo sinistro" #: lib/logitech_receiver/settings_templates.py:941 msgid "Swipe two fingers from the right edge" msgstr "scorri con due dita dal bordo detro" #: lib/logitech_receiver/settings_templates.py:942 msgid "Swipe two fingers from the bottom edge" msgstr "scorri con due dita dal bordo inferiore" #: lib/logitech_receiver/settings_templates.py:943 msgid "Swipe two fingers from the top edge" msgstr "scorri con due dita dal bordo superiore" #: lib/logitech_receiver/settings_templates.py:944 #: lib/logitech_receiver/settings_templates.py:948 msgid "Pinch to zoom out; spread to zoom in." msgstr "" #: lib/logitech_receiver/settings_templates.py:944 msgid "Zoom with two fingers." msgstr "fai lo zoom con due dita" #: lib/logitech_receiver/settings_templates.py:945 msgid "Pinch to zoom out." msgstr "pizzica per rimpicciolire" #: lib/logitech_receiver/settings_templates.py:946 msgid "Spread to zoom in." msgstr "" #: lib/logitech_receiver/settings_templates.py:947 msgid "Zoom with three fingers." msgstr "fai lo zoom con tre dita" #: lib/logitech_receiver/settings_templates.py:948 msgid "Zoom with two fingers" msgstr "fai lo zoom con due dita" #: lib/logitech_receiver/settings_templates.py:966 msgid "Pixel zone" msgstr "" #: lib/logitech_receiver/settings_templates.py:967 msgid "Ratio zone" msgstr "" #: lib/logitech_receiver/settings_templates.py:968 msgid "Scale factor" msgstr "Fattore di scala" #: lib/logitech_receiver/settings_templates.py:968 msgid "Sets the cursor speed." msgstr "" #: lib/logitech_receiver/settings_templates.py:972 msgid "Left" msgstr "Sinistra" #: lib/logitech_receiver/settings_templates.py:972 msgid "Left-most coordinate." msgstr "" #: lib/logitech_receiver/settings_templates.py:973 msgid "Bottom" msgstr "" #: lib/logitech_receiver/settings_templates.py:973 msgid "Bottom coordinate." msgstr "" #: lib/logitech_receiver/settings_templates.py:974 msgid "Width" msgstr "Larghezza" #: lib/logitech_receiver/settings_templates.py:974 msgid "Width." msgstr "Larghezza" #: lib/logitech_receiver/settings_templates.py:975 msgid "Height" msgstr "Altezza" #: lib/logitech_receiver/settings_templates.py:975 msgid "Height." msgstr "Altezza" #: lib/logitech_receiver/settings_templates.py:976 msgid "Cursor speed." msgstr "Velocità del cursore" #: lib/logitech_receiver/settings_templates.py:976 msgid "Scale" msgstr "Scala" #: lib/logitech_receiver/settings_templates.py:982 msgid "Gestures" msgstr "Gesti" #: lib/logitech_receiver/settings_templates.py:983 msgid "Tweak the mouse/touchpad behaviour." msgstr "Personalizza il comportamento del mouse/touchpad" #: lib/logitech_receiver/settings_templates.py:1000 msgid "Gestures Diversion" msgstr "" #: lib/logitech_receiver/settings_templates.py:1001 msgid "Divert mouse/touchpad gestures." msgstr "" #: lib/logitech_receiver/settings_templates.py:1018 msgid "Gesture params" msgstr "" #: lib/logitech_receiver/settings_templates.py:1019 msgid "Change numerical parameters of a mouse/touchpad." msgstr "Cambia i paramentri numerici di un mouse/touchpad" #: lib/logitech_receiver/settings_templates.py:1044 msgid "M-Key LEDs" msgstr "" #: lib/logitech_receiver/settings_templates.py:1046 msgid "Control the M-Key LEDs." msgstr "" #: lib/logitech_receiver/settings_templates.py:1047 #: lib/logitech_receiver/settings_templates.py:1077 msgid "May need G Keys diverted to be effective." msgstr "" #: lib/logitech_receiver/settings_templates.py:1053 #, python-format msgid "Lights up the %s key." msgstr "" #: lib/logitech_receiver/settings_templates.py:1074 msgid "MR-Key LED" msgstr "" #: lib/logitech_receiver/settings_templates.py:1076 msgid "Control the MR-Key LED." msgstr "" #: lib/logitech_receiver/settings_templates.py:1095 msgid "Persistent Key/Button Mapping" msgstr "" #: lib/logitech_receiver/settings_templates.py:1097 msgid "Permanently change the mapping for the key or button." msgstr "" #: lib/logitech_receiver/settings_templates.py:1098 msgid "Changing important keys or buttons (such as for the left mouse " "button) can result in an unusable system." msgstr "" #: lib/logitech_receiver/settings_templates.py:1157 msgid "Sidetone" msgstr "" #: lib/logitech_receiver/settings_templates.py:1158 msgid "Set sidetone level." msgstr "" #: lib/logitech_receiver/settings_templates.py:1167 msgid "Equalizer" msgstr "" #: lib/logitech_receiver/settings_templates.py:1168 msgid "Set equalizer levels." msgstr "" #: lib/logitech_receiver/settings_templates.py:1191 msgid "Hz" msgstr "" #: lib/logitech_receiver/settings_templates.py:1197 msgid "Power Management" msgstr "" #: lib/logitech_receiver/settings_templates.py:1198 msgid "Power off in minutes (0 for never)." msgstr "" #: lib/logitech_receiver/status.py:114 msgid "No paired devices." msgstr "Nessun dispositivo associato." #: lib/logitech_receiver/status.py:115 lib/solaar/ui/window.py:622 #, python-format msgid "%(count)s paired device." msgid_plural "%(count)s paired devices." msgstr[0] "%(count)s dispositivo connesso." msgstr[1] "%(count)s dispositivi connessi." #: lib/logitech_receiver/status.py:170 #, python-format msgid "Battery: %(level)s" msgstr "Batteria: %(level)s" #: lib/logitech_receiver/status.py:172 #, python-format msgid "Battery: %(percent)d%%" msgstr "Batteria: %(percent)d%%" #: lib/logitech_receiver/status.py:184 #, python-format msgid "Lighting: %(level)s lux" msgstr "Illuminazione: %(level)s lux" #: lib/logitech_receiver/status.py:239 #, python-format msgid "Battery: %(level)s (%(status)s)" msgstr "Batteria: %(level)s (%(status)s)" #: lib/logitech_receiver/status.py:241 #, python-format msgid "Battery: %(percent)d%% (%(status)s)" msgstr "Batteria: %(percent)d%% (%(status)s)" #: lib/solaar/ui/__init__.py:52 msgid "Permissions error" msgstr "Errore di permessi" #: lib/solaar/ui/__init__.py:54 #, python-format msgid "Found a Logitech receiver or device (%s), but did not have " "permission to open it." msgstr "" #: lib/solaar/ui/__init__.py:55 msgid "If you've just installed Solaar, try disconnecting the receiver or " "device and then reconnecting it." msgstr "" #: lib/solaar/ui/__init__.py:58 msgid "Cannot connect to device error" msgstr "" #: lib/solaar/ui/__init__.py:60 #, python-format msgid "Found a Logitech receiver or device at %s, but encountered an error " "connecting to it." msgstr "" #: lib/solaar/ui/__init__.py:61 msgid "Try disconnecting the device and then reconnecting it or turning it " "off and then on." msgstr "" #: lib/solaar/ui/__init__.py:64 msgid "Unpairing failed" msgstr "Disassociazione fallita" #: lib/solaar/ui/__init__.py:66 #, python-brace-format msgid "Failed to unpair %{device} from %{receiver}." msgstr "Disassociazione fallita di %{device} da %{receiver}." #: lib/solaar/ui/__init__.py:67 msgid "The receiver returned an error, with no further details." msgstr "Il ricevitore ha ritornato un errore senza ulteriori dettagli." #: lib/solaar/ui/__init__.py:177 msgid "Another Solaar process is already running so just expose its window" msgstr "" #: lib/solaar/ui/about.py:36 msgid "Manages Logitech receivers,\n" "keyboards, mice, and tablets." msgstr "" #: lib/solaar/ui/about.py:44 msgid "Additional Programming" msgstr "" #: lib/solaar/ui/about.py:45 msgid "GUI design" msgstr "GUI design" #: lib/solaar/ui/about.py:47 msgid "Testing" msgstr "Testing" #: lib/solaar/ui/about.py:54 msgid "Logitech documentation" msgstr "Documentazione Logitech" #: lib/solaar/ui/action.py:85 lib/solaar/ui/action.py:89 #: lib/solaar/ui/window.py:197 msgid "Unpair" msgstr "Disassocia" #: lib/solaar/ui/action.py:88 lib/solaar/ui/diversion_rules.py:150 msgid "Cancel" msgstr "" #: lib/solaar/ui/config_panel.py:212 msgid "Complete - ENTER to change" msgstr "" #: lib/solaar/ui/config_panel.py:212 msgid "Incomplete" msgstr "" #: lib/solaar/ui/config_panel.py:455 lib/solaar/ui/config_panel.py:507 #, python-format msgid "%d value" msgid_plural "%d values" msgstr[0] "%d valore" msgstr[1] "%d valori" #: lib/solaar/ui/config_panel.py:518 msgid "Changes allowed" msgstr "" #: lib/solaar/ui/config_panel.py:519 msgid "No changes allowed" msgstr "" #: lib/solaar/ui/config_panel.py:520 msgid "Ignore this setting" msgstr "" #: lib/solaar/ui/config_panel.py:565 msgid "Working" msgstr "Lavorando" #: lib/solaar/ui/config_panel.py:568 msgid "Read/write operation failed." msgstr "Operazione di Lettura/Scrittura fallita." #: lib/solaar/ui/diversion_rules.py:65 msgid "Built-in rules" msgstr "Regole integrate" #: lib/solaar/ui/diversion_rules.py:65 msgid "User-defined rules" msgstr "Regole definite dall'utente" #: lib/solaar/ui/diversion_rules.py:67 lib/solaar/ui/diversion_rules.py:1083 msgid "Rule" msgstr "Regola" #: lib/solaar/ui/diversion_rules.py:68 lib/solaar/ui/diversion_rules.py:509 #: lib/solaar/ui/diversion_rules.py:636 msgid "Sub-rule" msgstr "" #: lib/solaar/ui/diversion_rules.py:70 msgid "[empty]" msgstr "[vuoto]" #: lib/solaar/ui/diversion_rules.py:94 msgid "Solaar Rule Editor" msgstr "Editor di regole di Solaar" #: lib/solaar/ui/diversion_rules.py:141 msgid "Make changes permanent?" msgstr "Rendi le modifiche permanenti?" #: lib/solaar/ui/diversion_rules.py:146 msgid "Yes" msgstr "Sì" #: lib/solaar/ui/diversion_rules.py:148 msgid "No" msgstr "" #: lib/solaar/ui/diversion_rules.py:153 msgid "If you choose No, changes will be lost when Solaar is closed." msgstr "Se scegli No, le modifiche saranno perse quando Solaar viene chiuso" #: lib/solaar/ui/diversion_rules.py:201 msgid "Save changes" msgstr "" #: lib/solaar/ui/diversion_rules.py:206 msgid "Discard changes" msgstr "" #: lib/solaar/ui/diversion_rules.py:372 msgid "Insert here" msgstr "Inserisci qui" #: lib/solaar/ui/diversion_rules.py:374 msgid "Insert above" msgstr "Inserisci sopra" #: lib/solaar/ui/diversion_rules.py:376 msgid "Insert below" msgstr "Inserisci sotto" #: lib/solaar/ui/diversion_rules.py:382 msgid "Insert new rule here" msgstr "Inserisci una nuova regola qui" #: lib/solaar/ui/diversion_rules.py:384 msgid "Insert new rule above" msgstr "Inserisci una nuova regola sopra" #: lib/solaar/ui/diversion_rules.py:386 msgid "Insert new rule below" msgstr "Inserisci una nuova regola sotto" #: lib/solaar/ui/diversion_rules.py:427 msgid "Paste here" msgstr "Incolla qui" #: lib/solaar/ui/diversion_rules.py:429 msgid "Paste above" msgstr "Incolla sopra" #: lib/solaar/ui/diversion_rules.py:431 msgid "Paste below" msgstr "Incolla sotto" #: lib/solaar/ui/diversion_rules.py:437 msgid "Paste rule here" msgstr "Incolla la regola qui" #: lib/solaar/ui/diversion_rules.py:439 msgid "Paste rule above" msgstr "Incolla la regola sopra" #: lib/solaar/ui/diversion_rules.py:441 msgid "Paste rule below" msgstr "Incolla la regola sotto" #: lib/solaar/ui/diversion_rules.py:445 msgid "Paste rule" msgstr "Incolla la regola" #: lib/solaar/ui/diversion_rules.py:474 msgid "Flatten" msgstr "Appiattisci" #: lib/solaar/ui/diversion_rules.py:507 msgid "Insert" msgstr "Inserisci" #: lib/solaar/ui/diversion_rules.py:510 lib/solaar/ui/diversion_rules.py:638 #: lib/solaar/ui/diversion_rules.py:1126 msgid "Or" msgstr "Oppure" #: lib/solaar/ui/diversion_rules.py:511 lib/solaar/ui/diversion_rules.py:637 #: lib/solaar/ui/diversion_rules.py:1111 msgid "And" msgstr "E" #: lib/solaar/ui/diversion_rules.py:513 msgid "Condition" msgstr "Condizione" #: lib/solaar/ui/diversion_rules.py:515 lib/solaar/ui/diversion_rules.py:1292 msgid "Feature" msgstr "Funzionalità" #: lib/solaar/ui/diversion_rules.py:516 lib/solaar/ui/diversion_rules.py:1328 msgid "Report" msgstr "Segnala" #: lib/solaar/ui/diversion_rules.py:517 lib/solaar/ui/diversion_rules.py:1204 msgid "Process" msgstr "Processo" #: lib/solaar/ui/diversion_rules.py:518 msgid "Mouse process" msgstr "" #: lib/solaar/ui/diversion_rules.py:519 lib/solaar/ui/diversion_rules.py:1366 msgid "Modifiers" msgstr "Modificatori" #: lib/solaar/ui/diversion_rules.py:520 lib/solaar/ui/diversion_rules.py:1419 msgid "Key" msgstr "Chiave" #: lib/solaar/ui/diversion_rules.py:521 lib/solaar/ui/diversion_rules.py:1461 msgid "KeyIsDown" msgstr "" #: lib/solaar/ui/diversion_rules.py:522 lib/solaar/ui/diversion_rules.py:2260 msgid "Active" msgstr "Attivo" #: lib/solaar/ui/diversion_rules.py:523 lib/solaar/ui/diversion_rules.py:2218 #: lib/solaar/ui/diversion_rules.py:2270 lib/solaar/ui/diversion_rules.py:2323 msgid "Device" msgstr "" #: lib/solaar/ui/diversion_rules.py:524 lib/solaar/ui/diversion_rules.py:2297 msgid "Host" msgstr "" #: lib/solaar/ui/diversion_rules.py:525 lib/solaar/ui/diversion_rules.py:2339 msgid "Setting" msgstr "" #: lib/solaar/ui/diversion_rules.py:526 lib/solaar/ui/diversion_rules.py:1477 #: lib/solaar/ui/diversion_rules.py:1526 msgid "Test" msgstr "Test" #: lib/solaar/ui/diversion_rules.py:527 lib/solaar/ui/diversion_rules.py:1643 msgid "Test bytes" msgstr "" #: lib/solaar/ui/diversion_rules.py:528 lib/solaar/ui/diversion_rules.py:1736 msgid "Mouse Gesture" msgstr "" #: lib/solaar/ui/diversion_rules.py:532 msgid "Action" msgstr "Azione" #: lib/solaar/ui/diversion_rules.py:534 lib/solaar/ui/diversion_rules.py:1845 msgid "Key press" msgstr "Pressione del tasto" #: lib/solaar/ui/diversion_rules.py:535 lib/solaar/ui/diversion_rules.py:1897 msgid "Mouse scroll" msgstr "Scorrimento del mouse" #: lib/solaar/ui/diversion_rules.py:536 lib/solaar/ui/diversion_rules.py:1959 msgid "Mouse click" msgstr "Click del mouse" #: lib/solaar/ui/diversion_rules.py:537 msgid "Set" msgstr "" #: lib/solaar/ui/diversion_rules.py:538 lib/solaar/ui/diversion_rules.py:2030 msgid "Execute" msgstr "Esegui" #: lib/solaar/ui/diversion_rules.py:539 lib/solaar/ui/diversion_rules.py:1158 msgid "Later" msgstr "" #: lib/solaar/ui/diversion_rules.py:568 msgid "Insert new rule" msgstr "Inserisci una nuova regola" #: lib/solaar/ui/diversion_rules.py:588 lib/solaar/ui/diversion_rules.py:1686 #: lib/solaar/ui/diversion_rules.py:1790 lib/solaar/ui/diversion_rules.py:1989 msgid "Delete" msgstr "Elimina" #: lib/solaar/ui/diversion_rules.py:610 msgid "Negate" msgstr "Nega" #: lib/solaar/ui/diversion_rules.py:634 msgid "Wrap with" msgstr "" #: lib/solaar/ui/diversion_rules.py:656 msgid "Cut" msgstr "Taglia" #: lib/solaar/ui/diversion_rules.py:671 msgid "Paste" msgstr "Incolla" #: lib/solaar/ui/diversion_rules.py:683 msgid "Copy" msgstr "Copia" #: lib/solaar/ui/diversion_rules.py:1063 msgid "This editor does not support the selected rule component yet." msgstr "Questo editor ancora non supporta la componente selezionata" #: lib/solaar/ui/diversion_rules.py:1138 msgid "Number of seconds to delay." msgstr "" #: lib/solaar/ui/diversion_rules.py:1177 msgid "Not" msgstr "" #: lib/solaar/ui/diversion_rules.py:1187 msgid "X11 active process. For use in X11 only." msgstr "" #: lib/solaar/ui/diversion_rules.py:1218 msgid "X11 mouse process. For use in X11 only." msgstr "" #: lib/solaar/ui/diversion_rules.py:1235 msgid "MouseProcess" msgstr "" #: lib/solaar/ui/diversion_rules.py:1260 msgid "Feature name of notification triggering rule processing." msgstr "" #: lib/solaar/ui/diversion_rules.py:1308 msgid "Report number of notification triggering rule processing." msgstr "" #: lib/solaar/ui/diversion_rules.py:1342 msgid "Active keyboard modifiers. Not always available in Wayland." msgstr "" #: lib/solaar/ui/diversion_rules.py:1383 msgid "Diverted key or button depressed or released.\n" "Use the Key/Button Diversion and Divert G Keys settings to divert " "keys and buttons." msgstr "" #: lib/solaar/ui/diversion_rules.py:1392 msgid "Key down" msgstr "" #: lib/solaar/ui/diversion_rules.py:1395 msgid "Key up" msgstr "" #: lib/solaar/ui/diversion_rules.py:1436 msgid "Diverted key or button is currently down.\n" "Use the Key/Button Diversion and Divert G Keys settings to divert " "keys and buttons." msgstr "" #: lib/solaar/ui/diversion_rules.py:1475 msgid "Test condition on notification triggering rule processing." msgstr "" #: lib/solaar/ui/diversion_rules.py:1479 msgid "Parameter" msgstr "" #: lib/solaar/ui/diversion_rules.py:1542 msgid "begin (inclusive)" msgstr "" #: lib/solaar/ui/diversion_rules.py:1543 msgid "end (exclusive)" msgstr "" #: lib/solaar/ui/diversion_rules.py:1552 msgid "range" msgstr "" #: lib/solaar/ui/diversion_rules.py:1554 msgid "minimum" msgstr "" #: lib/solaar/ui/diversion_rules.py:1555 msgid "maximum" msgstr "" #: lib/solaar/ui/diversion_rules.py:1557 #, python-format msgid "bytes %(0)d to %(1)d, ranging from %(2)d to %(3)d" msgstr "" #: lib/solaar/ui/diversion_rules.py:1562 msgid "mask" msgstr "" #: lib/solaar/ui/diversion_rules.py:1563 #, python-format msgid "bytes %(0)d to %(1)d, mask %(2)d" msgstr "" #: lib/solaar/ui/diversion_rules.py:1573 msgid "Bit or range test on bytes in notification message triggering rule " "processing." msgstr "" #: lib/solaar/ui/diversion_rules.py:1583 msgid "type" msgstr "" #: lib/solaar/ui/diversion_rules.py:1666 msgid "Mouse gesture with optional initiating button followed by zero or " "more mouse movements." msgstr "" #: lib/solaar/ui/diversion_rules.py:1671 msgid "Add movement" msgstr "" #: lib/solaar/ui/diversion_rules.py:1764 msgid "Simulate a chorded key click or depress or release.\n" "On Wayland requires write access to /dev/uinput." msgstr "" #: lib/solaar/ui/diversion_rules.py:1769 msgid "Add key" msgstr "Aggiungi chiave" #: lib/solaar/ui/diversion_rules.py:1772 msgid "Click" msgstr "" #: lib/solaar/ui/diversion_rules.py:1775 msgid "Depress" msgstr "" #: lib/solaar/ui/diversion_rules.py:1778 msgid "Release" msgstr "" #: lib/solaar/ui/diversion_rules.py:1861 msgid "Simulate a mouse scroll.\n" "On Wayland requires write access to /dev/uinput." msgstr "" #: lib/solaar/ui/diversion_rules.py:1918 msgid "Simulate a mouse click.\n" "On Wayland requires write access to /dev/uinput." msgstr "" #: lib/solaar/ui/diversion_rules.py:1921 msgid "Button" msgstr "Pulsante" #: lib/solaar/ui/diversion_rules.py:1922 msgid "Count and Action" msgstr "" #: lib/solaar/ui/diversion_rules.py:1972 msgid "Execute a command with arguments." msgstr "" #: lib/solaar/ui/diversion_rules.py:1975 msgid "Add argument" msgstr "" #: lib/solaar/ui/diversion_rules.py:2050 msgid "Toggle" msgstr "" #: lib/solaar/ui/diversion_rules.py:2050 msgid "True" msgstr "" #: lib/solaar/ui/diversion_rules.py:2051 msgid "False" msgstr "" #: lib/solaar/ui/diversion_rules.py:2065 msgid "Unsupported setting" msgstr "" #: lib/solaar/ui/diversion_rules.py:2223 lib/solaar/ui/diversion_rules.py:2242 #: lib/solaar/ui/diversion_rules.py:2328 lib/solaar/ui/diversion_rules.py:2570 #: lib/solaar/ui/diversion_rules.py:2588 msgid "Originating device" msgstr "" #: lib/solaar/ui/diversion_rules.py:2256 msgid "Device is active and its settings can be changed." msgstr "" #: lib/solaar/ui/diversion_rules.py:2266 msgid "Device that originated the current notification." msgstr "" #: lib/solaar/ui/diversion_rules.py:2280 msgid "Name of host computer." msgstr "" #: lib/solaar/ui/diversion_rules.py:2347 msgid "Value" msgstr "Valore" #: lib/solaar/ui/diversion_rules.py:2355 msgid "Item" msgstr "" #: lib/solaar/ui/diversion_rules.py:2630 msgid "Change setting on device" msgstr "" #: lib/solaar/ui/diversion_rules.py:2647 msgid "Setting on device" msgstr "" #: lib/solaar/ui/notify.py:124 lib/solaar/ui/tray.py:318 #: lib/solaar/ui/tray.py:323 lib/solaar/ui/window.py:739 msgid "offline" msgstr "disconnesso" #: lib/solaar/ui/pair_window.py:122 lib/solaar/ui/pair_window.py:256 #: lib/solaar/ui/pair_window.py:288 #, python-format msgid "%(receiver_name)s: pair new device" msgstr "%(receiver_name)s: accoppia un nuovo dispositivo" #: lib/solaar/ui/pair_window.py:123 #, python-format msgid "Enter passcode on %(name)s." msgstr "" #: lib/solaar/ui/pair_window.py:126 #, python-format msgid "Type %(passcode)s and then press the enter key." msgstr "" #: lib/solaar/ui/pair_window.py:129 msgid "left" msgstr "" #: lib/solaar/ui/pair_window.py:129 msgid "right" msgstr "" #: lib/solaar/ui/pair_window.py:131 #, python-format msgid "Press %(code)s\n" "and then press left and right buttons simultaneously." msgstr "" #: lib/solaar/ui/pair_window.py:188 msgid "Pairing failed" msgstr "Associazione fallita" #: lib/solaar/ui/pair_window.py:190 msgid "Make sure your device is within range, and has a decent battery " "charge." msgstr "Assicurati che il dispositivo sia vicino al ricevitore e che abbia " "le batterie cariche." #: lib/solaar/ui/pair_window.py:192 msgid "A new device was detected, but it is not compatible with this " "receiver." msgstr "Un nuovo dispositivo è stato rilevato, ma non è compatibile con " "questo ricevitore." #: lib/solaar/ui/pair_window.py:194 msgid "More paired devices than receiver can support." msgstr "Più dispositivi accoppiati di quanti il ricevitore possa supportarne" #: lib/solaar/ui/pair_window.py:196 msgid "No further details are available about the error." msgstr "Non sono disponibili ulteriori dettagli per l'errore." #: lib/solaar/ui/pair_window.py:210 msgid "Found a new device:" msgstr "Trovato un nuovo dispositivo:" #: lib/solaar/ui/pair_window.py:235 msgid "The wireless link is not encrypted" msgstr "Il collegamento wireless non è ciftrato." #: lib/solaar/ui/pair_window.py:264 msgid "Unifying receivers are only compatible with Unifying devices." msgstr "" #: lib/solaar/ui/pair_window.py:266 msgid "Bolt receivers are only compatible with Bolt devices." msgstr "" #: lib/solaar/ui/pair_window.py:268 msgid "Other receivers are only compatible with a few devices." msgstr "" #: lib/solaar/ui/pair_window.py:270 msgid "The device must not be paired with a nearby powered-on receiver." msgstr "" #: lib/solaar/ui/pair_window.py:274 msgid "Press a pairing button or key until the pairing light flashes " "quickly." msgstr "" #: lib/solaar/ui/pair_window.py:276 msgid "You may have to first turn the device off and on again." msgstr "" #: lib/solaar/ui/pair_window.py:278 msgid "Turn on the device you want to pair." msgstr "Accendi il dispositivo che vuoi associare." #: lib/solaar/ui/pair_window.py:280 msgid "If the device is already turned on, turn it off and on again." msgstr "" #: lib/solaar/ui/pair_window.py:283 #, python-format msgid "\n" "\n" "This receiver has %d pairing remaining." msgid_plural "\n" "\n" "This receiver has %d pairings remaining." msgstr[0] "\n" "\n" "Questo ricevitore ha %d accoppiamento rimanente." msgstr[1] "\n" "\n" "Questo ricevitore ha %d accoppiamenti rimanenti." #: lib/solaar/ui/pair_window.py:286 msgid "\n" "Cancelling at this point will not use up a pairing." msgstr "\n" "Annullando a questo punto non costerà un accoppiamento" #: lib/solaar/ui/tray.py:58 msgid "No supported device found" msgstr "" #: lib/solaar/ui/tray.py:64 lib/solaar/ui/window.py:319 #, python-format msgid "About %s" msgstr "Informazioni %s" #: lib/solaar/ui/tray.py:65 lib/solaar/ui/window.py:317 #, python-format msgid "Quit %s" msgstr "Esci %s" #: lib/solaar/ui/tray.py:297 lib/solaar/ui/tray.py:305 msgid "no receiver" msgstr "nessun ricevitore" #: lib/solaar/ui/tray.py:321 msgid "no status" msgstr "nessuno stato" #: lib/solaar/ui/window.py:96 msgid "Scanning" msgstr "Ricerca" #: lib/solaar/ui/window.py:129 msgid "Battery" msgstr "Batteria" #: lib/solaar/ui/window.py:132 msgid "Wireless Link" msgstr "Collegamento Wireless" #: lib/solaar/ui/window.py:136 msgid "Lighting" msgstr "Luminosità" #: lib/solaar/ui/window.py:170 msgid "Show Technical Details" msgstr "Mostra Dettagli Tecnici" #: lib/solaar/ui/window.py:186 msgid "Pair new device" msgstr "Associa un nuovo dispositivo" #: lib/solaar/ui/window.py:205 msgid "Select a device" msgstr "Seleziona un dispositivo" #: lib/solaar/ui/window.py:322 msgid "Rule Editor" msgstr "Editor regole" #: lib/solaar/ui/window.py:533 msgid "Path" msgstr "Percorso" #: lib/solaar/ui/window.py:536 msgid "USB ID" msgstr "ID USB" #: lib/solaar/ui/window.py:539 lib/solaar/ui/window.py:541 #: lib/solaar/ui/window.py:561 lib/solaar/ui/window.py:563 msgid "Serial" msgstr "Seriale" #: lib/solaar/ui/window.py:545 msgid "Index" msgstr "Indice" #: lib/solaar/ui/window.py:547 msgid "Wireless PID" msgstr "PID Wireless" #: lib/solaar/ui/window.py:549 msgid "Product ID" msgstr "ID Prodotto" #: lib/solaar/ui/window.py:551 msgid "Protocol" msgstr "Protocollo" #: lib/solaar/ui/window.py:551 msgid "Unknown" msgstr "Sconosciuto" #: lib/solaar/ui/window.py:554 #, python-format msgid "%(rate)d ms (%(rate_hz)dHz)" msgstr "%(rate)d ms (%(rate_hz)dHz)" #: lib/solaar/ui/window.py:554 msgid "Polling rate" msgstr "Frequenza di aggiornamento" #: lib/solaar/ui/window.py:565 msgid "Unit ID" msgstr "ID unità" #: lib/solaar/ui/window.py:576 msgid "none" msgstr "nessuno" #: lib/solaar/ui/window.py:577 msgid "Notifications" msgstr "Notifiche" #: lib/solaar/ui/window.py:621 msgid "No device paired." msgstr "Nessun dispositivo accoppiato" #: lib/solaar/ui/window.py:628 #, python-format msgid "Up to %(max_count)s device can be paired to this receiver." msgid_plural "Up to %(max_count)s devices can be paired to this receiver." msgstr[0] "Fino a %(max_count)s dispositivo può essere accoppiato con " "questo ricevitore" msgstr[1] "Fino a %(max_count)s dispositivi possono essere accoppiati " "con questo ricevitore" #: lib/solaar/ui/window.py:634 msgid "Only one device can be paired to this receiver." msgstr "Solo un dispositivo può essere collegato a questo ricevitore" #: lib/solaar/ui/window.py:638 #, python-format msgid "This receiver has %d pairing remaining." msgid_plural "This receiver has %d pairings remaining." msgstr[0] "Questo ricevitore ha %d accoppiamento rimanente" msgstr[1] "Questo ricevitore ha %d accoppiamenti rimanenti" #: lib/solaar/ui/window.py:692 msgid "Battery Voltage" msgstr "Tensione della batteria" #: lib/solaar/ui/window.py:694 msgid "Voltage reported by battery" msgstr "Tensione riportata dalla batteria" #: lib/solaar/ui/window.py:696 msgid "Battery Level" msgstr "Livello batteria" #: lib/solaar/ui/window.py:698 msgid "Approximate level reported by battery" msgstr "Livello approssimativo riportato dalla batteria" #: lib/solaar/ui/window.py:705 lib/solaar/ui/window.py:707 msgid "next reported " msgstr "Prossimo livello " #: lib/solaar/ui/window.py:708 msgid " and next level to be reported." msgstr " e il prossimo livello che verrà riportato" #: lib/solaar/ui/window.py:713 msgid "last known" msgstr "ultimo conosciuto" #: lib/solaar/ui/window.py:724 msgid "encrypted" msgstr "cifrato" #: lib/solaar/ui/window.py:726 msgid "The wireless link between this device and its receiver is encrypted." msgstr "Il collegamento wireless tra il dispositivo e il suo ricevitore è " "cifrato." #: lib/solaar/ui/window.py:728 msgid "not encrypted" msgstr "non cifrato" #: lib/solaar/ui/window.py:732 msgid "The wireless link between this device and its receiver is not " "encrypted.\n" "This is a security issue for pointing devices, and a major security " "issue for text-input devices." msgstr "" #: lib/solaar/ui/window.py:748 #, python-format msgid "%(light_level)d lux" msgstr "%(light_level)d lux" #~ msgid "Adjust the DPI by sliding the mouse horizontally while " #~ "holding the DPI button." #~ msgstr "Modifica i DPI facendo scorrere il mouse orizzontalmente " #~ "mentre si preme il pulsante DPI" #~ msgid "Battery information unknown." #~ msgstr "Informazioni sulla batteria sconosciute" #~ msgid "Click to allow changes." #~ msgstr "Premi per consentire modifiche" #~ msgid "Click to prevent changes." #~ msgstr "Clicca per evitare modifiche" #~ msgid "Count" #~ msgstr "Numero" #~ msgid "Effectively turns off wheel scrolling in Linux." #~ msgstr "Di fatto in Linux disattiva lo scorrimento con la rotella" #~ msgid "For more information see the Solaar installation directions\n" #~ "at https://pwr-solaar.github.io/Solaar/installation" #~ msgstr "Per ulteriori informazioni consulta le istruzioni " #~ "all'installazione di Solaar\n" #~ "su https://pwr-solaar.github.io/Solaar/installation" #, python-format #~ msgid "Found a Logitech Receiver (%s), but did not have permission " #~ "to open it." #~ msgstr "Rilevato un ricevitore Logitech (%s), ma non si hanno i " #~ "permessi per aprirlo." #~ msgid "HID++ mode for vertical scroll with the wheel." #~ msgstr "Modalità HID++ per lo scorrimento verticale con la rotella" #~ msgid "If the device is already turned on, turn if off and on again." #~ msgstr "Se il dispositivo è già acceso, spegnilo e accendilo " #~ "nuovamente." #~ msgid "If you've just installed Solaar, try removing the receiver " #~ "and plugging it back in." #~ msgstr "Se hai appena installato Solaar prova a scollegare e " #~ "ricollegare il ricevitore." #~ msgid "No Logitech receiver found" #~ msgstr "Nessun ricevitore Logitech trovato" #~ msgid "Shows status of devices connected\n" #~ "through wireless Logitech receivers." #~ msgstr "Mostra lo stato dei dispositivi connessi\n" #~ "attraverso i ricevitori Logitech." #~ msgid "Solaar depends on a udev file that is not present" #~ msgstr "Solaar dipende da un file udev non presente" #~ msgid "The wireless link between this device and its receiver is " #~ "not encrypted.\n" #~ "\n" #~ "For pointing devices (mice, trackballs, trackpads), this is a minor " #~ "security issue.\n" #~ "\n" #~ "It is, however, a major security issue for text-input devices " #~ "(keyboards, numpads),\n" #~ "because typed text can be sniffed inconspicuously by 3rd parties " #~ "within range." #~ msgstr "Il collegamento wireless tra il dispositivo e il suo " #~ "ricevitore non è cifrato.\n" #~ "\n" #~ "Per i dispositivi di puntamento (mouse, trackball, trackpad) non è " #~ "un grave problema.\n" #~ "\n" #~ "E' un problema maggiore per i dispositivi di immissione di testo " #~ "(tastiera, numpad),\n" #~ "perché il testo digitato potrebbe essere intercettato da dispositivi " #~ "di terze parti vicini." #~ msgid "Thumb Wheel HID++ Scrolling" #~ msgstr "Rotella di scorrimento Scorrimento HID++" #~ msgid "height" #~ msgstr "altezza" #~ msgid "top" #~ msgstr "in alto" #~ msgid "unknown" #~ msgstr "sconosciuto" #~ msgid "width" #~ msgstr "larghezza" Solaar-1.1.14/po/ja.po000066400000000000000000001523731473525213700144130ustar00rootroot00000000000000# Japanese translations for solaar package # Copyright (C) 2021 SOLAAR'S COPYRIGHT HOLDER # This file is distributed under the same license as the Solaar package. # msgid "" msgstr "Project-Id-Version: solaar 1.0.6\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-12-28 17:40+0100\n" "PO-Revision-Date: 2021-09-24 00:25+0900\n" "Last-Translator: Ryunosuke Toda \n" "Language-Team: \n" "Language: ja\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Poedit 3.0\n" #: lib/logitech_receiver/base_usb.py:46 msgid "Bolt Receiver" msgstr "" #: lib/logitech_receiver/base_usb.py:57 msgid "Unifying Receiver" msgstr "Unifying レシーバー" #: lib/logitech_receiver/base_usb.py:67 lib/logitech_receiver/base_usb.py:78 #: lib/logitech_receiver/base_usb.py:90 lib/logitech_receiver/base_usb.py:102 #: lib/logitech_receiver/base_usb.py:114 msgid "Nano Receiver" msgstr "Nano レシーバー" #: lib/logitech_receiver/base_usb.py:124 msgid "Lightspeed Receiver" msgstr "Lightspeed レシーバー" #: lib/logitech_receiver/base_usb.py:133 msgid "EX100 Receiver 27 Mhz" msgstr "EX100 レシーバー 27 Mhz" #: lib/logitech_receiver/i18n.py:30 msgid "empty" msgstr "空" #: lib/logitech_receiver/i18n.py:31 msgid "critical" msgstr "わずか" #: lib/logitech_receiver/i18n.py:32 msgid "low" msgstr "低" #: lib/logitech_receiver/i18n.py:33 msgid "average" msgstr "中" #: lib/logitech_receiver/i18n.py:34 msgid "good" msgstr "良好" #: lib/logitech_receiver/i18n.py:35 msgid "full" msgstr "満充電" #: lib/logitech_receiver/i18n.py:38 msgid "discharging" msgstr "低下" #: lib/logitech_receiver/i18n.py:39 msgid "recharging" msgstr "再充電中" #: lib/logitech_receiver/i18n.py:40 lib/solaar/ui/window.py:711 msgid "charging" msgstr "充電中" #: lib/logitech_receiver/i18n.py:41 msgid "not charging" msgstr "充電していません" #: lib/logitech_receiver/i18n.py:42 msgid "almost full" msgstr "ほぼ満充電" #: lib/logitech_receiver/i18n.py:43 msgid "charged" msgstr "充電完了" #: lib/logitech_receiver/i18n.py:44 msgid "slow recharge" msgstr "低速充電" #: lib/logitech_receiver/i18n.py:45 msgid "invalid battery" msgstr "バッテリー不正" #: lib/logitech_receiver/i18n.py:46 msgid "thermal error" msgstr "温度エラー" #: lib/logitech_receiver/i18n.py:47 msgid "error" msgstr "エラー" #: lib/logitech_receiver/i18n.py:48 msgid "standard" msgstr "標準" #: lib/logitech_receiver/i18n.py:49 msgid "fast" msgstr "高速" #: lib/logitech_receiver/i18n.py:50 msgid "slow" msgstr "低速" #: lib/logitech_receiver/i18n.py:53 msgid "device timeout" msgstr "デバイス応答なし" #: lib/logitech_receiver/i18n.py:54 msgid "device not supported" msgstr "非対応のデバイス" #: lib/logitech_receiver/i18n.py:55 msgid "too many devices" msgstr "デバイス数超過" #: lib/logitech_receiver/i18n.py:56 msgid "sequence timeout" msgstr "シーケンス応答なし" #: lib/logitech_receiver/i18n.py:59 lib/solaar/ui/window.py:572 msgid "Firmware" msgstr "Firmware" #: lib/logitech_receiver/i18n.py:60 msgid "Bootloader" msgstr "Bootloader" #: lib/logitech_receiver/i18n.py:61 msgid "Hardware" msgstr "Hardware" #: lib/logitech_receiver/i18n.py:62 msgid "Other" msgstr "Other" #: lib/logitech_receiver/i18n.py:65 msgid "Left Button" msgstr "左ボタン" #: lib/logitech_receiver/i18n.py:66 msgid "Right Button" msgstr "右ボタン" #: lib/logitech_receiver/i18n.py:67 msgid "Middle Button" msgstr "中ボタン" #: lib/logitech_receiver/i18n.py:68 msgid "Back Button" msgstr "戻るボタン" #: lib/logitech_receiver/i18n.py:69 msgid "Forward Button" msgstr "進むボタン" #: lib/logitech_receiver/i18n.py:70 msgid "Mouse Gesture Button" msgstr "マウスジェスチャーボタン" #: lib/logitech_receiver/i18n.py:71 msgid "Smart Shift" msgstr "SmartShift" #: lib/logitech_receiver/i18n.py:72 msgid "DPI Switch" msgstr "DPIスイッチ" #: lib/logitech_receiver/i18n.py:73 msgid "Left Tilt" msgstr "左チルト" #: lib/logitech_receiver/i18n.py:74 msgid "Right Tilt" msgstr "右チルト" #: lib/logitech_receiver/i18n.py:75 msgid "Left Click" msgstr "左クリック" #: lib/logitech_receiver/i18n.py:76 msgid "Right Click" msgstr "右クリック" #: lib/logitech_receiver/i18n.py:77 msgid "Mouse Middle Button" msgstr "マウス中ボタン" #: lib/logitech_receiver/i18n.py:78 msgid "Mouse Back Button" msgstr "マウス戻るボタン" #: lib/logitech_receiver/i18n.py:79 msgid "Mouse Forward Button" msgstr "マウス進むボタン" #: lib/logitech_receiver/i18n.py:80 msgid "Gesture Button Navigation" msgstr "" #: lib/logitech_receiver/i18n.py:81 msgid "Mouse Scroll Left Button" msgstr "" #: lib/logitech_receiver/i18n.py:82 msgid "Mouse Scroll Right Button" msgstr "" #: lib/logitech_receiver/i18n.py:85 msgid "pressed" msgstr "" #: lib/logitech_receiver/i18n.py:86 msgid "released" msgstr "" #: lib/logitech_receiver/notifications.py:75 #: lib/logitech_receiver/notifications.py:126 msgid "pairing lock is closed" msgstr "ペアリング試行終了" #: lib/logitech_receiver/notifications.py:75 #: lib/logitech_receiver/notifications.py:126 msgid "pairing lock is open" msgstr "ペアリング試行開始" #: lib/logitech_receiver/notifications.py:92 msgid "discovery lock is closed" msgstr "" #: lib/logitech_receiver/notifications.py:92 msgid "discovery lock is open" msgstr "" #: lib/logitech_receiver/notifications.py:224 lib/solaar/ui/notify.py:122 msgid "connected" msgstr "接続" #: lib/logitech_receiver/notifications.py:224 msgid "disconnected" msgstr "切断" #: lib/logitech_receiver/notifications.py:262 lib/solaar/ui/notify.py:120 msgid "unpaired" msgstr "ペアリング解除" #: lib/logitech_receiver/notifications.py:304 msgid "powered on" msgstr "電源オン" #: lib/logitech_receiver/settings.py:750 msgid "register" msgstr "" #: lib/logitech_receiver/settings.py:764 lib/logitech_receiver/settings.py:791 msgid "feature" msgstr "" #: lib/logitech_receiver/settings_templates.py:139 msgid "Swap Fx function" msgstr "ファンクションキー動作を入替" #: lib/logitech_receiver/settings_templates.py:140 msgid "When set, the F1..F12 keys will activate their special function,\n" "and you must hold the FN key to activate their standard function." msgstr "オンにすると、F1〜F12をカスタマイズキーとして使用します。\n" "標準のファンクションキーとして使用するには、fnキーを押しながら操作し" "ます。" #: lib/logitech_receiver/settings_templates.py:142 msgid "When unset, the F1..F12 keys will activate their standard function,\n" "and you must hold the FN key to activate their special function." msgstr "オフにすると、F1〜F12を標準のファンクションキーとして使用します。\n" "カスタマイズキーとして使用するには、fnキーを押しながら操作します。" #: lib/logitech_receiver/settings_templates.py:149 msgid "Hand Detection" msgstr "手の検出" #: lib/logitech_receiver/settings_templates.py:150 msgid "Turn on illumination when the hands hover over the keyboard." msgstr "手がキーボードの上にあるとき照明をオンにします。" #: lib/logitech_receiver/settings_templates.py:157 msgid "Scroll Wheel Smooth Scrolling" msgstr "" #: lib/logitech_receiver/settings_templates.py:158 #: lib/logitech_receiver/settings_templates.py:239 #: lib/logitech_receiver/settings_templates.py:267 msgid "High-sensitivity mode for vertical scroll with the wheel." msgstr "ホイール縦スクロール高感度モード。" #: lib/logitech_receiver/settings_templates.py:165 msgid "Side Scrolling" msgstr "サイドスクロール" #: lib/logitech_receiver/settings_templates.py:167 msgid "When disabled, pushing the wheel sideways sends custom button " "events\n" "instead of the standard side-scrolling events." msgstr "" #: lib/logitech_receiver/settings_templates.py:177 msgid "Sensitivity (DPI - older mice)" msgstr "" #: lib/logitech_receiver/settings_templates.py:178 #: lib/logitech_receiver/settings_templates.py:712 msgid "Mouse movement sensitivity" msgstr "マウスの動きの感度" #: lib/logitech_receiver/settings_templates.py:208 #: lib/logitech_receiver/settings_templates.py:218 #: lib/logitech_receiver/settings_templates.py:225 msgid "Backlight" msgstr "バックライト" #: lib/logitech_receiver/settings_templates.py:209 #: lib/logitech_receiver/settings_templates.py:226 msgid "Set illumination time for keyboard." msgstr "" #: lib/logitech_receiver/settings_templates.py:219 msgid "Turn illumination on or off on keyboard." msgstr "バックライトのON/OFFを切り替えます。" #: lib/logitech_receiver/settings_templates.py:237 msgid "Scroll Wheel High Resolution" msgstr "高解像度スクロールホイール" #: lib/logitech_receiver/settings_templates.py:240 #: lib/logitech_receiver/settings_templates.py:268 msgid "Set to ignore if scrolling is abnormally fast or slow" msgstr "" #: lib/logitech_receiver/settings_templates.py:247 #: lib/logitech_receiver/settings_templates.py:277 msgid "Scroll Wheel Diversion" msgstr "スクロールホイール迂回" #: lib/logitech_receiver/settings_templates.py:249 msgid "Make scroll wheel send LOWRES_WHEEL HID++ notifications (which " "trigger Solaar rules but are otherwise ignored)." msgstr "" #: lib/logitech_receiver/settings_templates.py:256 msgid "Scroll Wheel Direction" msgstr "ホイールのスクロール方向" #: lib/logitech_receiver/settings_templates.py:257 msgid "Invert direction for vertical scroll with wheel." msgstr "ホイールでの縦スクロール方向を逆にします。" #: lib/logitech_receiver/settings_templates.py:265 msgid "Scroll Wheel Resolution" msgstr "ホイールのスクロール解像度" #: lib/logitech_receiver/settings_templates.py:279 msgid "Make scroll wheel send HIRES_WHEEL HID++ notifications (which " "trigger Solaar rules but are otherwise ignored)." msgstr "" #: lib/logitech_receiver/settings_templates.py:288 msgid "Sensitivity (Pointer Speed)" msgstr "感度(ポインタ速度)" #: lib/logitech_receiver/settings_templates.py:289 msgid "Speed multiplier for mouse (256 is normal multiplier)." msgstr "マウス速度の倍率(256で標準)。" #: lib/logitech_receiver/settings_templates.py:299 msgid "Thumb Wheel Diversion" msgstr "" #: lib/logitech_receiver/settings_templates.py:301 msgid "Make thumb wheel send THUMB_WHEEL HID++ notifications (which trigger " "Solaar rules but are otherwise ignored)." msgstr "" #: lib/logitech_receiver/settings_templates.py:310 msgid "Thumb Wheel Direction" msgstr "サムホイールの方向" #: lib/logitech_receiver/settings_templates.py:311 msgid "Invert thumb wheel scroll direction." msgstr "サムホイールのスクロール方向を反転します。" #: lib/logitech_receiver/settings_templates.py:319 msgid "Onboard Profiles" msgstr "" #: lib/logitech_receiver/settings_templates.py:320 msgid "Enable onboard profiles, which often control report rate and " "keyboard lighting" msgstr "" #: lib/logitech_receiver/settings_templates.py:330 msgid "Polling Rate (ms)" msgstr "ポーリングレート(ミリ秒)" #: lib/logitech_receiver/settings_templates.py:332 msgid "Frequency of device polling, in milliseconds" msgstr "" #: lib/logitech_receiver/settings_templates.py:333 #: lib/logitech_receiver/settings_templates.py:1046 #: lib/logitech_receiver/settings_templates.py:1076 msgid "May need Onboard Profiles set to Disable to be effective." msgstr "" #: lib/logitech_receiver/settings_templates.py:365 msgid "Divert crown events" msgstr "クラウンをルールへ迂回" #: lib/logitech_receiver/settings_templates.py:366 msgid "Make crown send CROWN HID++ notifications (which trigger Solaar " "rules but are otherwise ignored)." msgstr "CrownにCROWNHID++通知を送信させます(Solaarルールをトリガーしますが、" "それ以外の場合は無視されます)。" #: lib/logitech_receiver/settings_templates.py:374 msgid "Crown smooth scroll" msgstr "" #: lib/logitech_receiver/settings_templates.py:375 msgid "Set crown smooth scroll" msgstr "" #: lib/logitech_receiver/settings_templates.py:383 msgid "Divert G Keys" msgstr "" #: lib/logitech_receiver/settings_templates.py:385 msgid "Make G keys send GKEY HID++ notifications (which trigger Solaar " "rules but are otherwise ignored)." msgstr "" #: lib/logitech_receiver/settings_templates.py:386 msgid "May also make M keys and MR key send HID++ notifications" msgstr "" #: lib/logitech_receiver/settings_templates.py:402 msgid "Scroll Wheel Ratcheted" msgstr "" #: lib/logitech_receiver/settings_templates.py:403 msgid "Switch the mouse wheel between speed-controlled ratcheting and " "always freespin." msgstr "" #: lib/logitech_receiver/settings_templates.py:405 msgid "Freespinning" msgstr "" #: lib/logitech_receiver/settings_templates.py:405 msgid "Ratcheted" msgstr "" #: lib/logitech_receiver/settings_templates.py:412 msgid "Scroll Wheel Ratchet Speed" msgstr "" #: lib/logitech_receiver/settings_templates.py:414 msgid "Use the mouse wheel speed to switch between ratcheted and " "freespinning.\n" "The mouse wheel is always ratcheted at 50." msgstr "" #: lib/logitech_receiver/settings_templates.py:463 msgid "Key/Button Actions" msgstr "キー/ボタン動作" #: lib/logitech_receiver/settings_templates.py:465 msgid "Change the action for the key or button." msgstr "キーやボタンの動作を変更します。" #: lib/logitech_receiver/settings_templates.py:465 msgid "Overridden by diversion." msgstr "" #: lib/logitech_receiver/settings_templates.py:466 msgid "Changing important actions (such as for the left mouse button) can " "result in an unusable system." msgstr "重要なアクション(マウスの左ボタンなど)を変更すると、システムが使用" "できなくなる可能性があります。" #: lib/logitech_receiver/settings_templates.py:639 msgid "Key/Button Diversion" msgstr "キー/ボタンをルールへ迂回" #: lib/logitech_receiver/settings_templates.py:640 msgid "Make the key or button send HID++ notifications (Diverted) or " "initiate Mouse Gestures or Sliding DPI" msgstr "" #: lib/logitech_receiver/settings_templates.py:643 #: lib/logitech_receiver/settings_templates.py:644 #: lib/logitech_receiver/settings_templates.py:645 msgid "Diverted" msgstr "迂回する" #: lib/logitech_receiver/settings_templates.py:643 #: lib/logitech_receiver/settings_templates.py:644 msgid "Mouse Gestures" msgstr "マウスジェスチャー" #: lib/logitech_receiver/settings_templates.py:643 #: lib/logitech_receiver/settings_templates.py:644 #: lib/logitech_receiver/settings_templates.py:645 msgid "Regular" msgstr "通常通り" #: lib/logitech_receiver/settings_templates.py:643 msgid "Sliding DPI" msgstr "" #: lib/logitech_receiver/settings_templates.py:711 msgid "Sensitivity (DPI)" msgstr "感度(DPI)" #: lib/logitech_receiver/settings_templates.py:752 msgid "Sensitivity Switching" msgstr "" #: lib/logitech_receiver/settings_templates.py:754 msgid "Switch the current sensitivity and the remembered sensitivity when " "the key or button is pressed.\n" "If there is no remembered sensitivity, just remember the current " "sensitivity" msgstr "" #: lib/logitech_receiver/settings_templates.py:758 msgid "Off" msgstr "オフ" #: lib/logitech_receiver/settings_templates.py:791 msgid "Disable keys" msgstr "無効キー" #: lib/logitech_receiver/settings_templates.py:792 msgid "Disable specific keyboard keys." msgstr "特定のキーを無効にします。" #: lib/logitech_receiver/settings_templates.py:795 #, python-format msgid "Disables the %s key." msgstr "キー %s を無効にする。" #: lib/logitech_receiver/settings_templates.py:809 #: lib/logitech_receiver/settings_templates.py:860 msgid "Set OS" msgstr "OS設定" #: lib/logitech_receiver/settings_templates.py:810 #: lib/logitech_receiver/settings_templates.py:861 msgid "Change keys to match OS." msgstr "OSに合わせたキーに変更します。" #: lib/logitech_receiver/settings_templates.py:873 msgid "Change Host" msgstr "ホスト変更" #: lib/logitech_receiver/settings_templates.py:874 msgid "Switch connection to a different host" msgstr "接続を他のホストへ切り替えます" #: lib/logitech_receiver/settings_templates.py:900 msgid "Performs a left click." msgstr "" #: lib/logitech_receiver/settings_templates.py:900 msgid "Single tap" msgstr "" #: lib/logitech_receiver/settings_templates.py:901 msgid "Performs a right click." msgstr "" #: lib/logitech_receiver/settings_templates.py:901 msgid "Single tap with two fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:902 msgid "Single tap with three fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:906 msgid "Double tap" msgstr "" #: lib/logitech_receiver/settings_templates.py:906 msgid "Performs a double click." msgstr "" #: lib/logitech_receiver/settings_templates.py:907 msgid "Double tap with two fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:908 msgid "Double tap with three fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:911 msgid "Drags items by dragging the finger after double tapping." msgstr "" #: lib/logitech_receiver/settings_templates.py:911 msgid "Tap and drag" msgstr "" #: lib/logitech_receiver/settings_templates.py:913 msgid "Drags items by dragging the fingers after double tapping." msgstr "" #: lib/logitech_receiver/settings_templates.py:913 msgid "Tap and drag with two fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:914 msgid "Tap and drag with three fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:917 msgid "Disables tap and edge gestures (equivalent to pressing Fn+LeftClick)." msgstr "タップおよびエッジジェスチャを無効にします(Fn + LeftClickを押すのと" "同等)。" #: lib/logitech_receiver/settings_templates.py:917 msgid "Suppress tap and edge gestures" msgstr "タップとエッジジェスチャーの無効化" #: lib/logitech_receiver/settings_templates.py:918 msgid "Scroll with one finger" msgstr "" #: lib/logitech_receiver/settings_templates.py:918 #: lib/logitech_receiver/settings_templates.py:919 #: lib/logitech_receiver/settings_templates.py:922 msgid "Scrolls." msgstr "" #: lib/logitech_receiver/settings_templates.py:919 #: lib/logitech_receiver/settings_templates.py:922 msgid "Scroll with two fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:920 msgid "Scroll horizontally with two fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:920 msgid "Scrolls horizontally." msgstr "" #: lib/logitech_receiver/settings_templates.py:921 msgid "Scroll vertically with two fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:921 msgid "Scrolls vertically." msgstr "" #: lib/logitech_receiver/settings_templates.py:923 msgid "Inverts the scrolling direction." msgstr "" #: lib/logitech_receiver/settings_templates.py:923 msgid "Natural scrolling" msgstr "" #: lib/logitech_receiver/settings_templates.py:924 msgid "Enables the thumbwheel." msgstr "" #: lib/logitech_receiver/settings_templates.py:924 msgid "Thumbwheel" msgstr "" #: lib/logitech_receiver/settings_templates.py:935 #: lib/logitech_receiver/settings_templates.py:939 msgid "Swipe from the top edge" msgstr "" #: lib/logitech_receiver/settings_templates.py:936 msgid "Swipe from the left edge" msgstr "" #: lib/logitech_receiver/settings_templates.py:937 msgid "Swipe from the right edge" msgstr "" #: lib/logitech_receiver/settings_templates.py:938 msgid "Swipe from the bottom edge" msgstr "" #: lib/logitech_receiver/settings_templates.py:940 msgid "Swipe two fingers from the left edge" msgstr "" #: lib/logitech_receiver/settings_templates.py:941 msgid "Swipe two fingers from the right edge" msgstr "" #: lib/logitech_receiver/settings_templates.py:942 msgid "Swipe two fingers from the bottom edge" msgstr "" #: lib/logitech_receiver/settings_templates.py:943 msgid "Swipe two fingers from the top edge" msgstr "" #: lib/logitech_receiver/settings_templates.py:944 #: lib/logitech_receiver/settings_templates.py:948 msgid "Pinch to zoom out; spread to zoom in." msgstr "" #: lib/logitech_receiver/settings_templates.py:944 msgid "Zoom with two fingers." msgstr "" #: lib/logitech_receiver/settings_templates.py:945 msgid "Pinch to zoom out." msgstr "" #: lib/logitech_receiver/settings_templates.py:946 msgid "Spread to zoom in." msgstr "" #: lib/logitech_receiver/settings_templates.py:947 msgid "Zoom with three fingers." msgstr "" #: lib/logitech_receiver/settings_templates.py:948 msgid "Zoom with two fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:966 msgid "Pixel zone" msgstr "" #: lib/logitech_receiver/settings_templates.py:967 msgid "Ratio zone" msgstr "" #: lib/logitech_receiver/settings_templates.py:968 msgid "Scale factor" msgstr "" #: lib/logitech_receiver/settings_templates.py:968 msgid "Sets the cursor speed." msgstr "カーソル速度を設定します。" #: lib/logitech_receiver/settings_templates.py:972 msgid "Left" msgstr "" #: lib/logitech_receiver/settings_templates.py:972 msgid "Left-most coordinate." msgstr "" #: lib/logitech_receiver/settings_templates.py:973 msgid "Bottom" msgstr "" #: lib/logitech_receiver/settings_templates.py:973 msgid "Bottom coordinate." msgstr "" #: lib/logitech_receiver/settings_templates.py:974 msgid "Width" msgstr "幅" #: lib/logitech_receiver/settings_templates.py:974 msgid "Width." msgstr "" #: lib/logitech_receiver/settings_templates.py:975 msgid "Height" msgstr "高さ" #: lib/logitech_receiver/settings_templates.py:975 msgid "Height." msgstr "" #: lib/logitech_receiver/settings_templates.py:976 msgid "Cursor speed." msgstr "カーソル速度。" #: lib/logitech_receiver/settings_templates.py:976 msgid "Scale" msgstr "" #: lib/logitech_receiver/settings_templates.py:982 msgid "Gestures" msgstr "ジェスチャー" #: lib/logitech_receiver/settings_templates.py:983 msgid "Tweak the mouse/touchpad behaviour." msgstr "マウス/タッチパッドの動作を微調整します。" #: lib/logitech_receiver/settings_templates.py:1000 msgid "Gestures Diversion" msgstr "" #: lib/logitech_receiver/settings_templates.py:1001 msgid "Divert mouse/touchpad gestures." msgstr "" #: lib/logitech_receiver/settings_templates.py:1018 msgid "Gesture params" msgstr "ジェスチャーパラメータ" #: lib/logitech_receiver/settings_templates.py:1019 msgid "Change numerical parameters of a mouse/touchpad." msgstr "マウス/タッチパッドの数値パラメータを変更します。" #: lib/logitech_receiver/settings_templates.py:1044 msgid "M-Key LEDs" msgstr "" #: lib/logitech_receiver/settings_templates.py:1046 msgid "Control the M-Key LEDs." msgstr "" #: lib/logitech_receiver/settings_templates.py:1047 #: lib/logitech_receiver/settings_templates.py:1077 msgid "May need G Keys diverted to be effective." msgstr "" #: lib/logitech_receiver/settings_templates.py:1053 #, python-format msgid "Lights up the %s key." msgstr "" #: lib/logitech_receiver/settings_templates.py:1074 msgid "MR-Key LED" msgstr "" #: lib/logitech_receiver/settings_templates.py:1076 msgid "Control the MR-Key LED." msgstr "" #: lib/logitech_receiver/settings_templates.py:1095 msgid "Persistent Key/Button Mapping" msgstr "" #: lib/logitech_receiver/settings_templates.py:1097 msgid "Permanently change the mapping for the key or button." msgstr "" #: lib/logitech_receiver/settings_templates.py:1098 msgid "Changing important keys or buttons (such as for the left mouse " "button) can result in an unusable system." msgstr "" #: lib/logitech_receiver/settings_templates.py:1157 msgid "Sidetone" msgstr "" #: lib/logitech_receiver/settings_templates.py:1158 msgid "Set sidetone level." msgstr "" #: lib/logitech_receiver/settings_templates.py:1167 msgid "Equalizer" msgstr "" #: lib/logitech_receiver/settings_templates.py:1168 msgid "Set equalizer levels." msgstr "" #: lib/logitech_receiver/settings_templates.py:1191 msgid "Hz" msgstr "" #: lib/logitech_receiver/settings_templates.py:1197 msgid "Power Management" msgstr "" #: lib/logitech_receiver/settings_templates.py:1198 msgid "Power off in minutes (0 for never)." msgstr "" #: lib/logitech_receiver/status.py:114 msgid "No paired devices." msgstr "ペアリングされたデバイスはありません。" #: lib/logitech_receiver/status.py:115 lib/solaar/ui/window.py:622 #, python-format msgid "%(count)s paired device." msgid_plural "%(count)s paired devices." msgstr[0] "%(count)s デバイスとペアリングされています。" #: lib/logitech_receiver/status.py:170 #, python-format msgid "Battery: %(level)s" msgstr "バッテリー残量: %(level)s" #: lib/logitech_receiver/status.py:172 #, python-format msgid "Battery: %(percent)d%%" msgstr "バッテリー残量: %(percent)d%%" #: lib/logitech_receiver/status.py:184 #, python-format msgid "Lighting: %(level)s lux" msgstr "照明: %(level)s ルクス" #: lib/logitech_receiver/status.py:239 #, python-format msgid "Battery: %(level)s (%(status)s)" msgstr "バッテリー残量: %(level)s (%(status)s)" #: lib/logitech_receiver/status.py:241 #, python-format msgid "Battery: %(percent)d%% (%(status)s)" msgstr "バッテリー残量: %(percent)d%% (%(status)s)" #: lib/solaar/ui/__init__.py:52 msgid "Permissions error" msgstr "権限エラー" #: lib/solaar/ui/__init__.py:54 #, python-format msgid "Found a Logitech receiver or device (%s), but did not have " "permission to open it." msgstr "" #: lib/solaar/ui/__init__.py:55 msgid "If you've just installed Solaar, try disconnecting the receiver or " "device and then reconnecting it." msgstr "" #: lib/solaar/ui/__init__.py:58 msgid "Cannot connect to device error" msgstr "デバイスに接続できません" #: lib/solaar/ui/__init__.py:60 #, python-format msgid "Found a Logitech receiver or device at %s, but encountered an error " "connecting to it." msgstr "%s で Logitech のレシーバーまたはデバイスが見つかりましたが、接続中に" "エラーが発生しました。" #: lib/solaar/ui/__init__.py:61 msgid "Try disconnecting the device and then reconnecting it or turning it " "off and then on." msgstr "" #: lib/solaar/ui/__init__.py:64 msgid "Unpairing failed" msgstr "ペアリング解除失敗" #: lib/solaar/ui/__init__.py:66 #, python-brace-format msgid "Failed to unpair %{device} from %{receiver}." msgstr "%{receiver} からの %{device} のペアリング解除に失敗しました。" #: lib/solaar/ui/__init__.py:67 msgid "The receiver returned an error, with no further details." msgstr "レシーバーがこれ以上の詳細なしでエラーを返しました。" #: lib/solaar/ui/__init__.py:177 msgid "Another Solaar process is already running so just expose its window" msgstr "" #: lib/solaar/ui/about.py:36 msgid "Manages Logitech receivers,\n" "keyboards, mice, and tablets." msgstr "Logitechのレシーバー、キーボード、\n" "マウス、タブレットを管理。" #: lib/solaar/ui/about.py:44 msgid "Additional Programming" msgstr "追加のプログラミング" #: lib/solaar/ui/about.py:45 msgid "GUI design" msgstr "GUIデザイン" #: lib/solaar/ui/about.py:47 msgid "Testing" msgstr "テスト" #: lib/solaar/ui/about.py:54 msgid "Logitech documentation" msgstr "" #: lib/solaar/ui/action.py:85 lib/solaar/ui/action.py:89 #: lib/solaar/ui/window.py:197 msgid "Unpair" msgstr "ペアリング解除" #: lib/solaar/ui/action.py:88 lib/solaar/ui/diversion_rules.py:150 msgid "Cancel" msgstr "キャンセル" #: lib/solaar/ui/config_panel.py:212 msgid "Complete - ENTER to change" msgstr "" #: lib/solaar/ui/config_panel.py:212 msgid "Incomplete" msgstr "" #: lib/solaar/ui/config_panel.py:455 lib/solaar/ui/config_panel.py:507 #, python-format msgid "%d value" msgid_plural "%d values" msgstr[0] "" #: lib/solaar/ui/config_panel.py:518 msgid "Changes allowed" msgstr "変更を許可する" #: lib/solaar/ui/config_panel.py:519 msgid "No changes allowed" msgstr "変更を許可しない" #: lib/solaar/ui/config_panel.py:520 msgid "Ignore this setting" msgstr "この設定を無視する" #: lib/solaar/ui/config_panel.py:565 msgid "Working" msgstr "処理中" #: lib/solaar/ui/config_panel.py:568 msgid "Read/write operation failed." msgstr "読取または書込処理に失敗しました。" #: lib/solaar/ui/diversion_rules.py:65 msgid "Built-in rules" msgstr "組み込みのルール" #: lib/solaar/ui/diversion_rules.py:65 msgid "User-defined rules" msgstr "ユーザー定義のルール" #: lib/solaar/ui/diversion_rules.py:67 lib/solaar/ui/diversion_rules.py:1083 msgid "Rule" msgstr "ルール" #: lib/solaar/ui/diversion_rules.py:68 lib/solaar/ui/diversion_rules.py:509 #: lib/solaar/ui/diversion_rules.py:636 msgid "Sub-rule" msgstr "サブルール" #: lib/solaar/ui/diversion_rules.py:70 msgid "[empty]" msgstr "[空]" #: lib/solaar/ui/diversion_rules.py:94 msgid "Solaar Rule Editor" msgstr "Solaarルールエディタ" #: lib/solaar/ui/diversion_rules.py:141 msgid "Make changes permanent?" msgstr "変更を保存しますか?" #: lib/solaar/ui/diversion_rules.py:146 msgid "Yes" msgstr "はい" #: lib/solaar/ui/diversion_rules.py:148 msgid "No" msgstr "いいえ" #: lib/solaar/ui/diversion_rules.py:153 msgid "If you choose No, changes will be lost when Solaar is closed." msgstr "[いいえ]を選択すると、Solaarを閉じるときに変更が失われます。" #: lib/solaar/ui/diversion_rules.py:201 msgid "Save changes" msgstr "保存" #: lib/solaar/ui/diversion_rules.py:206 msgid "Discard changes" msgstr "変更を破棄" #: lib/solaar/ui/diversion_rules.py:372 msgid "Insert here" msgstr "ここに挿入" #: lib/solaar/ui/diversion_rules.py:374 msgid "Insert above" msgstr "上に挿入" #: lib/solaar/ui/diversion_rules.py:376 msgid "Insert below" msgstr "下に挿入" #: lib/solaar/ui/diversion_rules.py:382 msgid "Insert new rule here" msgstr "新しいルールをここに挿入" #: lib/solaar/ui/diversion_rules.py:384 msgid "Insert new rule above" msgstr "新しいルールを上に挿入" #: lib/solaar/ui/diversion_rules.py:386 msgid "Insert new rule below" msgstr "新しいルールを下に挿入" #: lib/solaar/ui/diversion_rules.py:427 msgid "Paste here" msgstr "ここに貼り付け" #: lib/solaar/ui/diversion_rules.py:429 msgid "Paste above" msgstr "上に貼り付け" #: lib/solaar/ui/diversion_rules.py:431 msgid "Paste below" msgstr "下に貼り付け" #: lib/solaar/ui/diversion_rules.py:437 msgid "Paste rule here" msgstr "ここにルールを貼り付け" #: lib/solaar/ui/diversion_rules.py:439 msgid "Paste rule above" msgstr "上にルールを貼り付け" #: lib/solaar/ui/diversion_rules.py:441 msgid "Paste rule below" msgstr "下にルールを貼り付け" #: lib/solaar/ui/diversion_rules.py:445 msgid "Paste rule" msgstr "ルールを貼り付け" #: lib/solaar/ui/diversion_rules.py:474 msgid "Flatten" msgstr "フラット化" #: lib/solaar/ui/diversion_rules.py:507 msgid "Insert" msgstr "挿入" #: lib/solaar/ui/diversion_rules.py:510 lib/solaar/ui/diversion_rules.py:638 #: lib/solaar/ui/diversion_rules.py:1126 msgid "Or" msgstr "どれか" #: lib/solaar/ui/diversion_rules.py:511 lib/solaar/ui/diversion_rules.py:637 #: lib/solaar/ui/diversion_rules.py:1111 msgid "And" msgstr "すべて" #: lib/solaar/ui/diversion_rules.py:513 msgid "Condition" msgstr "状態" #: lib/solaar/ui/diversion_rules.py:515 lib/solaar/ui/diversion_rules.py:1292 msgid "Feature" msgstr "機能" #: lib/solaar/ui/diversion_rules.py:516 lib/solaar/ui/diversion_rules.py:1328 msgid "Report" msgstr "通知" #: lib/solaar/ui/diversion_rules.py:517 lib/solaar/ui/diversion_rules.py:1204 msgid "Process" msgstr "プロセス" #: lib/solaar/ui/diversion_rules.py:518 msgid "Mouse process" msgstr "" #: lib/solaar/ui/diversion_rules.py:519 lib/solaar/ui/diversion_rules.py:1366 msgid "Modifiers" msgstr "修飾キー" #: lib/solaar/ui/diversion_rules.py:520 lib/solaar/ui/diversion_rules.py:1419 msgid "Key" msgstr "キー" #: lib/solaar/ui/diversion_rules.py:521 lib/solaar/ui/diversion_rules.py:1461 msgid "KeyIsDown" msgstr "" #: lib/solaar/ui/diversion_rules.py:522 lib/solaar/ui/diversion_rules.py:2260 msgid "Active" msgstr "有効" #: lib/solaar/ui/diversion_rules.py:523 lib/solaar/ui/diversion_rules.py:2218 #: lib/solaar/ui/diversion_rules.py:2270 lib/solaar/ui/diversion_rules.py:2323 msgid "Device" msgstr "" #: lib/solaar/ui/diversion_rules.py:524 lib/solaar/ui/diversion_rules.py:2297 msgid "Host" msgstr "" #: lib/solaar/ui/diversion_rules.py:525 lib/solaar/ui/diversion_rules.py:2339 msgid "Setting" msgstr "" #: lib/solaar/ui/diversion_rules.py:526 lib/solaar/ui/diversion_rules.py:1477 #: lib/solaar/ui/diversion_rules.py:1526 msgid "Test" msgstr "評価" #: lib/solaar/ui/diversion_rules.py:527 lib/solaar/ui/diversion_rules.py:1643 msgid "Test bytes" msgstr "" #: lib/solaar/ui/diversion_rules.py:528 lib/solaar/ui/diversion_rules.py:1736 msgid "Mouse Gesture" msgstr "マウスジェスチャー" #: lib/solaar/ui/diversion_rules.py:532 msgid "Action" msgstr "アクション" #: lib/solaar/ui/diversion_rules.py:534 lib/solaar/ui/diversion_rules.py:1845 msgid "Key press" msgstr "キー押下" #: lib/solaar/ui/diversion_rules.py:535 lib/solaar/ui/diversion_rules.py:1897 msgid "Mouse scroll" msgstr "マウススクロール" #: lib/solaar/ui/diversion_rules.py:536 lib/solaar/ui/diversion_rules.py:1959 msgid "Mouse click" msgstr "マウスクリック" #: lib/solaar/ui/diversion_rules.py:537 msgid "Set" msgstr "" #: lib/solaar/ui/diversion_rules.py:538 lib/solaar/ui/diversion_rules.py:2030 msgid "Execute" msgstr "実行" #: lib/solaar/ui/diversion_rules.py:539 lib/solaar/ui/diversion_rules.py:1158 msgid "Later" msgstr "" #: lib/solaar/ui/diversion_rules.py:568 msgid "Insert new rule" msgstr "新しいルールを挿入" #: lib/solaar/ui/diversion_rules.py:588 lib/solaar/ui/diversion_rules.py:1686 #: lib/solaar/ui/diversion_rules.py:1790 lib/solaar/ui/diversion_rules.py:1989 msgid "Delete" msgstr "削除" #: lib/solaar/ui/diversion_rules.py:610 msgid "Negate" msgstr "否" #: lib/solaar/ui/diversion_rules.py:634 msgid "Wrap with" msgstr "次で覆う" #: lib/solaar/ui/diversion_rules.py:656 msgid "Cut" msgstr "切り取り" #: lib/solaar/ui/diversion_rules.py:671 msgid "Paste" msgstr "貼り付け" #: lib/solaar/ui/diversion_rules.py:683 msgid "Copy" msgstr "コピー" #: lib/solaar/ui/diversion_rules.py:1063 msgid "This editor does not support the selected rule component yet." msgstr "このエディタは、選択したルールコンポーネントをまだサポートしていませ" "ん。" #: lib/solaar/ui/diversion_rules.py:1138 msgid "Number of seconds to delay." msgstr "" #: lib/solaar/ui/diversion_rules.py:1177 msgid "Not" msgstr "否" #: lib/solaar/ui/diversion_rules.py:1187 msgid "X11 active process. For use in X11 only." msgstr "" #: lib/solaar/ui/diversion_rules.py:1218 msgid "X11 mouse process. For use in X11 only." msgstr "" #: lib/solaar/ui/diversion_rules.py:1235 msgid "MouseProcess" msgstr "マウスプロセス" #: lib/solaar/ui/diversion_rules.py:1260 msgid "Feature name of notification triggering rule processing." msgstr "" #: lib/solaar/ui/diversion_rules.py:1308 msgid "Report number of notification triggering rule processing." msgstr "" #: lib/solaar/ui/diversion_rules.py:1342 msgid "Active keyboard modifiers. Not always available in Wayland." msgstr "" #: lib/solaar/ui/diversion_rules.py:1383 msgid "Diverted key or button depressed or released.\n" "Use the Key/Button Diversion and Divert G Keys settings to divert " "keys and buttons." msgstr "" #: lib/solaar/ui/diversion_rules.py:1392 msgid "Key down" msgstr "押す" #: lib/solaar/ui/diversion_rules.py:1395 msgid "Key up" msgstr "離す" #: lib/solaar/ui/diversion_rules.py:1436 msgid "Diverted key or button is currently down.\n" "Use the Key/Button Diversion and Divert G Keys settings to divert " "keys and buttons." msgstr "" #: lib/solaar/ui/diversion_rules.py:1475 msgid "Test condition on notification triggering rule processing." msgstr "" #: lib/solaar/ui/diversion_rules.py:1479 msgid "Parameter" msgstr "" #: lib/solaar/ui/diversion_rules.py:1542 msgid "begin (inclusive)" msgstr "" #: lib/solaar/ui/diversion_rules.py:1543 msgid "end (exclusive)" msgstr "" #: lib/solaar/ui/diversion_rules.py:1552 msgid "range" msgstr "" #: lib/solaar/ui/diversion_rules.py:1554 msgid "minimum" msgstr "" #: lib/solaar/ui/diversion_rules.py:1555 msgid "maximum" msgstr "" #: lib/solaar/ui/diversion_rules.py:1557 #, python-format msgid "bytes %(0)d to %(1)d, ranging from %(2)d to %(3)d" msgstr "" #: lib/solaar/ui/diversion_rules.py:1562 msgid "mask" msgstr "" #: lib/solaar/ui/diversion_rules.py:1563 #, python-format msgid "bytes %(0)d to %(1)d, mask %(2)d" msgstr "" #: lib/solaar/ui/diversion_rules.py:1573 msgid "Bit or range test on bytes in notification message triggering rule " "processing." msgstr "" #: lib/solaar/ui/diversion_rules.py:1583 msgid "type" msgstr "" #: lib/solaar/ui/diversion_rules.py:1666 msgid "Mouse gesture with optional initiating button followed by zero or " "more mouse movements." msgstr "" #: lib/solaar/ui/diversion_rules.py:1671 msgid "Add movement" msgstr "" #: lib/solaar/ui/diversion_rules.py:1764 msgid "Simulate a chorded key click or depress or release.\n" "On Wayland requires write access to /dev/uinput." msgstr "" #: lib/solaar/ui/diversion_rules.py:1769 msgid "Add key" msgstr "キー追加" #: lib/solaar/ui/diversion_rules.py:1772 msgid "Click" msgstr "" #: lib/solaar/ui/diversion_rules.py:1775 msgid "Depress" msgstr "" #: lib/solaar/ui/diversion_rules.py:1778 msgid "Release" msgstr "" #: lib/solaar/ui/diversion_rules.py:1861 msgid "Simulate a mouse scroll.\n" "On Wayland requires write access to /dev/uinput." msgstr "" #: lib/solaar/ui/diversion_rules.py:1918 msgid "Simulate a mouse click.\n" "On Wayland requires write access to /dev/uinput." msgstr "" #: lib/solaar/ui/diversion_rules.py:1921 msgid "Button" msgstr "ボタン" #: lib/solaar/ui/diversion_rules.py:1922 msgid "Count and Action" msgstr "" #: lib/solaar/ui/diversion_rules.py:1972 msgid "Execute a command with arguments." msgstr "" #: lib/solaar/ui/diversion_rules.py:1975 msgid "Add argument" msgstr "引数を追加" #: lib/solaar/ui/diversion_rules.py:2050 msgid "Toggle" msgstr "" #: lib/solaar/ui/diversion_rules.py:2050 msgid "True" msgstr "" #: lib/solaar/ui/diversion_rules.py:2051 msgid "False" msgstr "" #: lib/solaar/ui/diversion_rules.py:2065 msgid "Unsupported setting" msgstr "" #: lib/solaar/ui/diversion_rules.py:2223 lib/solaar/ui/diversion_rules.py:2242 #: lib/solaar/ui/diversion_rules.py:2328 lib/solaar/ui/diversion_rules.py:2570 #: lib/solaar/ui/diversion_rules.py:2588 msgid "Originating device" msgstr "" #: lib/solaar/ui/diversion_rules.py:2256 msgid "Device is active and its settings can be changed." msgstr "" #: lib/solaar/ui/diversion_rules.py:2266 msgid "Device that originated the current notification." msgstr "" #: lib/solaar/ui/diversion_rules.py:2280 msgid "Name of host computer." msgstr "" #: lib/solaar/ui/diversion_rules.py:2347 msgid "Value" msgstr "値" #: lib/solaar/ui/diversion_rules.py:2355 msgid "Item" msgstr "" #: lib/solaar/ui/diversion_rules.py:2630 msgid "Change setting on device" msgstr "" #: lib/solaar/ui/diversion_rules.py:2647 msgid "Setting on device" msgstr "" #: lib/solaar/ui/notify.py:124 lib/solaar/ui/tray.py:318 #: lib/solaar/ui/tray.py:323 lib/solaar/ui/window.py:739 msgid "offline" msgstr "未接続" #: lib/solaar/ui/pair_window.py:122 lib/solaar/ui/pair_window.py:256 #: lib/solaar/ui/pair_window.py:288 #, python-format msgid "%(receiver_name)s: pair new device" msgstr "%(receiver_name)s: 新しいデバイスをペアリング" #: lib/solaar/ui/pair_window.py:123 #, python-format msgid "Enter passcode on %(name)s." msgstr "" #: lib/solaar/ui/pair_window.py:126 #, python-format msgid "Type %(passcode)s and then press the enter key." msgstr "" #: lib/solaar/ui/pair_window.py:129 msgid "left" msgstr "" #: lib/solaar/ui/pair_window.py:129 msgid "right" msgstr "" #: lib/solaar/ui/pair_window.py:131 #, python-format msgid "Press %(code)s\n" "and then press left and right buttons simultaneously." msgstr "" #: lib/solaar/ui/pair_window.py:188 msgid "Pairing failed" msgstr "ペアリング失敗" #: lib/solaar/ui/pair_window.py:190 msgid "Make sure your device is within range, and has a decent battery " "charge." msgstr "デバイスが範囲内にあり、適切なバッテリー充電が行われていることを確認" "してください。" #: lib/solaar/ui/pair_window.py:192 msgid "A new device was detected, but it is not compatible with this " "receiver." msgstr "新しいデバイスが検出されましたが、このレシーバーと互換性がありませ" "ん。" #: lib/solaar/ui/pair_window.py:194 msgid "More paired devices than receiver can support." msgstr "レシーバーのサポートより多くペアリングされたデバイス。" #: lib/solaar/ui/pair_window.py:196 msgid "No further details are available about the error." msgstr "エラーに関するこれ以上の詳細はありません。" #: lib/solaar/ui/pair_window.py:210 msgid "Found a new device:" msgstr "新しいデバイスが見つかりました:" #: lib/solaar/ui/pair_window.py:235 msgid "The wireless link is not encrypted" msgstr "この無線通信は暗号化されていません" #: lib/solaar/ui/pair_window.py:264 msgid "Unifying receivers are only compatible with Unifying devices." msgstr "" #: lib/solaar/ui/pair_window.py:266 msgid "Bolt receivers are only compatible with Bolt devices." msgstr "" #: lib/solaar/ui/pair_window.py:268 msgid "Other receivers are only compatible with a few devices." msgstr "" #: lib/solaar/ui/pair_window.py:270 msgid "The device must not be paired with a nearby powered-on receiver." msgstr "" #: lib/solaar/ui/pair_window.py:274 msgid "Press a pairing button or key until the pairing light flashes " "quickly." msgstr "" #: lib/solaar/ui/pair_window.py:276 msgid "You may have to first turn the device off and on again." msgstr "" #: lib/solaar/ui/pair_window.py:278 msgid "Turn on the device you want to pair." msgstr "ペアリングしたいデバイスの電源を入れて下さい。" #: lib/solaar/ui/pair_window.py:280 msgid "If the device is already turned on, turn it off and on again." msgstr "デバイスの電源が既にオンになっている場合、オフにしてからもう一度オン" "にします。" #: lib/solaar/ui/pair_window.py:283 #, python-format msgid "\n" "\n" "This receiver has %d pairing remaining." msgid_plural "\n" "\n" "This receiver has %d pairings remaining." msgstr[0] "\n" "\n" "このレシーバーはあと %d デバイスとペアリングできます。" #: lib/solaar/ui/pair_window.py:286 msgid "\n" "Cancelling at this point will not use up a pairing." msgstr "" #: lib/solaar/ui/tray.py:58 msgid "No supported device found" msgstr "" #: lib/solaar/ui/tray.py:64 lib/solaar/ui/window.py:319 #, python-format msgid "About %s" msgstr "%sについて" #: lib/solaar/ui/tray.py:65 lib/solaar/ui/window.py:317 #, python-format msgid "Quit %s" msgstr "%sを終了" #: lib/solaar/ui/tray.py:297 lib/solaar/ui/tray.py:305 msgid "no receiver" msgstr "レシーバーなし" #: lib/solaar/ui/tray.py:321 msgid "no status" msgstr "ステータスなし" #: lib/solaar/ui/window.py:96 msgid "Scanning" msgstr "スキャン中" #: lib/solaar/ui/window.py:129 msgid "Battery" msgstr "バッテリー" #: lib/solaar/ui/window.py:132 msgid "Wireless Link" msgstr "無線通信" #: lib/solaar/ui/window.py:136 msgid "Lighting" msgstr "照明" #: lib/solaar/ui/window.py:170 msgid "Show Technical Details" msgstr "技術的な詳細を表示" #: lib/solaar/ui/window.py:186 msgid "Pair new device" msgstr "デバイスの追加" #: lib/solaar/ui/window.py:205 msgid "Select a device" msgstr "デバイスを選択して下さい" #: lib/solaar/ui/window.py:322 msgid "Rule Editor" msgstr "ルールエディタ" #: lib/solaar/ui/window.py:533 msgid "Path" msgstr "Path" #: lib/solaar/ui/window.py:536 msgid "USB ID" msgstr "USB ID" #: lib/solaar/ui/window.py:539 lib/solaar/ui/window.py:541 #: lib/solaar/ui/window.py:561 lib/solaar/ui/window.py:563 msgid "Serial" msgstr "Serial" #: lib/solaar/ui/window.py:545 msgid "Index" msgstr "Index" #: lib/solaar/ui/window.py:547 msgid "Wireless PID" msgstr "Wireless PID" #: lib/solaar/ui/window.py:549 msgid "Product ID" msgstr "Product ID" #: lib/solaar/ui/window.py:551 msgid "Protocol" msgstr "Protocol" #: lib/solaar/ui/window.py:551 msgid "Unknown" msgstr "Unknown" #: lib/solaar/ui/window.py:554 #, python-format msgid "%(rate)d ms (%(rate_hz)dHz)" msgstr "%(rate)d ms (%(rate_hz)dHz)" #: lib/solaar/ui/window.py:554 msgid "Polling rate" msgstr "Polling rate" #: lib/solaar/ui/window.py:565 msgid "Unit ID" msgstr "Unit ID" #: lib/solaar/ui/window.py:576 msgid "none" msgstr "none" #: lib/solaar/ui/window.py:577 msgid "Notifications" msgstr "Notifications" #: lib/solaar/ui/window.py:621 msgid "No device paired." msgstr "ペアリングされたデバイスはありません。" #: lib/solaar/ui/window.py:628 #, python-format msgid "Up to %(max_count)s device can be paired to this receiver." msgid_plural "Up to %(max_count)s devices can be paired to this receiver." msgstr[0] "このレシーバーは最大 %(max_count)s デバイスとペアリングできま" "す。" #: lib/solaar/ui/window.py:634 msgid "Only one device can be paired to this receiver." msgstr "このレシーバーは1つのデバイスのみとペアリングできます。" #: lib/solaar/ui/window.py:638 #, python-format msgid "This receiver has %d pairing remaining." msgid_plural "This receiver has %d pairings remaining." msgstr[0] "このレシーバーはあと %d デバイスとペアリングできます。" #: lib/solaar/ui/window.py:692 msgid "Battery Voltage" msgstr "バッテリー電圧" #: lib/solaar/ui/window.py:694 msgid "Voltage reported by battery" msgstr "バッテリーから報告された電圧" #: lib/solaar/ui/window.py:696 msgid "Battery Level" msgstr "バッテリー残量" #: lib/solaar/ui/window.py:698 msgid "Approximate level reported by battery" msgstr "バッテリーから報告されたおおよその残量" #: lib/solaar/ui/window.py:705 lib/solaar/ui/window.py:707 msgid "next reported " msgstr "〜 " #: lib/solaar/ui/window.py:708 msgid " and next level to be reported." msgstr " と 次に報告される残量。" #: lib/solaar/ui/window.py:713 msgid "last known" msgstr "最後に認識された値" #: lib/solaar/ui/window.py:724 msgid "encrypted" msgstr "暗号化あり" #: lib/solaar/ui/window.py:726 msgid "The wireless link between this device and its receiver is encrypted." msgstr "このデバイスとレシーバー間の通信は暗号化されています。" #: lib/solaar/ui/window.py:728 msgid "not encrypted" msgstr "暗号化なし" #: lib/solaar/ui/window.py:732 msgid "The wireless link between this device and its receiver is not " "encrypted.\n" "This is a security issue for pointing devices, and a major security " "issue for text-input devices." msgstr "" #: lib/solaar/ui/window.py:748 #, python-format msgid "%(light_level)d lux" msgstr "%(light_level)d ルクス" #~ msgid "Add action" #~ msgstr "操作追加" #~ msgid "Adjust the DPI by sliding the mouse horizontally while " #~ "holding the button down." #~ msgstr "ボタンを押したままマウスを横に動かすことで感度(DPI)を調整しま" #~ "す。" #~ msgid "Automatically switch the mouse wheel between ratchet and " #~ "freespin mode.\n" #~ "The mouse wheel is always free at 0, and always ratcheted at 50" #~ msgstr "マウスホイールのラチェットモードとフリースピンモードを自動的" #~ "に切り替えます。\n" #~ "0で常にフリー、50で常にラチェットモードになります" #~ msgid "Battery information unknown." #~ msgstr "バッテリー情報は不明です。" #~ msgid "Count" #~ msgstr "回数" #~ msgid "DPI Sliding Adjustment" #~ msgstr "DPIスライディング調整" #~ msgid "ERROR: " #~ msgstr "エラー: " #~ msgid "Effectively turns off thumb scrolling in Linux." #~ msgstr "事実上、Linuxでサムスクロールをオフにします。" #~ msgid "Effectively turns off wheel scrolling in Linux." #~ msgstr "事実上Linuxでのホイールスクロールを無効にします。" #~ msgid "For more information see the Solaar installation directions\n" #~ "at https://pwr-solaar.github.io/Solaar/installation" #~ msgstr "詳細については、下記よりSolaarのインストール手順を参照してく" #~ "ださい\n" #~ "https://pwr-solaar.github.io/Solaar/installation" #, python-format #~ msgid "Found a Logitech Receiver (%s), but did not have permission " #~ "to open it." #~ msgstr "Logitechレシーバー (%s) が見つかりましたが、開く権限がありま" #~ "せんでした。" #~ msgid "HID++ mode for vertical scroll with the wheel." #~ msgstr "ホイールでのHID++モード縦スクロール。" #~ msgid "If you've just installed Solaar, try removing the receiver " #~ "and plugging it back in." #~ msgstr "Solaarをインストールしたばかりの場合は、レシーバーを取り外し" #~ "て、再び接続してみてください。" #~ msgid "Make the key or button send HID++ notifications (which " #~ "trigger Solaar rules but are otherwise ignored)." #~ msgstr "キーまたはボタンにHID ++通知を送信させます(Solaarルールをト" #~ "リガーしますが、それ以外の場合は無視されます)。" #~ msgid "No Logitech receiver found" #~ msgstr "Logitechレシーバーが見つかりません" #~ msgid "Scroll Wheel HID++ Scrolling" #~ msgstr "HID++スクロールホイール" #~ msgid "Scroll Wheel Rachet" #~ msgstr "スクロールホイールラチェット" #~ msgid "Send a gesture by sliding the mouse while holding the button " #~ "down." #~ msgstr "ボタンを押したままマウスをスライドさせることで任意のジェス" #~ "チャーを送信します。" #~ msgid "Solaar depends on a udev file that is not present" #~ msgstr "Solaarは存在しないudevファイルに依存しています" #~ msgid "The wireless link between this device and its receiver is " #~ "not encrypted.\n" #~ "\n" #~ "For pointing devices (mice, trackballs, trackpads), this is a minor " #~ "security issue.\n" #~ "\n" #~ "It is, however, a major security issue for text-input devices " #~ "(keyboards, numpads),\n" #~ "because typed text can be sniffed inconspicuously by 3rd parties " #~ "within range." #~ msgstr "このデバイスとレシーバー間の無線通信は暗号化されていませ" #~ "ん。\n" #~ "\n" #~ "ポインティングデバイス(マウス、トラックボール、トラックパッド)の場" #~ "合、重大ではありませんがセキュリティの問題があります。\n" #~ "\n" #~ "テキスト入力デバイス(キーボード、テンキー)の場合、重要なセキュリ" #~ "ティの問題があります。\n" #~ "入力した文字は、範囲内の第三者によって盗聴される可能性があります。" #~ msgid "Try removing the device and plugging it back in or turning " #~ "it off and then on." #~ msgstr "デバイスを取り外して再度接続するか、電源をオフにしてからオン" #~ "にしてみてください。" #~ msgid "height" #~ msgstr "高さ" #~ msgid "unknown" #~ msgstr "不明" #~ msgid "width" #~ msgstr "幅" Solaar-1.1.14/po/nb.po000066400000000000000000002015211473525213700144060ustar00rootroot00000000000000# Norwegian Bokmal translations for solaar package. # Copyright (C) 2020 THE solaar'S COPYRIGHT HOLDER # This file is distributed under the same license as the solaar package. # Automatically generated, 2020. # John Erling Blad , 2020-2024. # msgid "" msgstr "" "Project-Id-Version: solaar 1.0.3\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2024-10-24 03:15+0200\n" "PO-Revision-Date: 2024-10-31 02:21+0100\n" "Last-Translator: John Erling Blad \n" "Language-Team: \n" "Language: nb\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n!=1)\n" "X-Generator: Gtranslator 47.0\n" #: lib/logitech_receiver/base_usb.py:48 msgid "Bolt Receiver" msgstr "Bolt mottaker" #: lib/logitech_receiver/base_usb.py:60 msgid "Unifying Receiver" msgstr "Unifying mottaker" #: lib/logitech_receiver/base_usb.py:71 lib/logitech_receiver/base_usb.py:83 #: lib/logitech_receiver/base_usb.py:96 lib/logitech_receiver/base_usb.py:109 msgid "Nano Receiver" msgstr "Nano mottaker" #: lib/logitech_receiver/base_usb.py:121 msgid "Lightspeed Receiver" msgstr "Lightspeed mottaker" #: lib/logitech_receiver/base_usb.py:131 msgid "EX100 Receiver 27 Mhz" msgstr "EX100 mottaker 27 Mhz mottaker" #: lib/logitech_receiver/common.py:648 #, python-format msgid "Battery: %(level)s (%(status)s)" msgstr "Batteri: %(level)s (%(status)s)" #: lib/logitech_receiver/common.py:651 #, python-format msgid "Battery: %(percent)d%% (%(status)s)" msgstr "Batteri: %(percent)d%% (%(status)s)" #: lib/logitech_receiver/hidpp20.py:962 #: lib/logitech_receiver/settings_templates.py:294 msgid "Disabled" msgstr "Avslått" #: lib/logitech_receiver/hidpp20.py:963 msgid "Static" msgstr "Statisk" #: lib/logitech_receiver/hidpp20.py:964 msgid "Pulse" msgstr "Puls" #: lib/logitech_receiver/hidpp20.py:965 msgid "Cycle" msgstr "Syklus" #: lib/logitech_receiver/hidpp20.py:966 msgid "Boot" msgstr "Boot" #: lib/logitech_receiver/hidpp20.py:967 msgid "Demo" msgstr "Demo" #: lib/logitech_receiver/hidpp20.py:969 msgid "Breathe" msgstr "Pust" #: lib/logitech_receiver/hidpp20.py:972 msgid "Ripple" msgstr "Krusning" #: lib/logitech_receiver/hidpp20.py:973 msgid "Decomposition" msgstr "Dekomponering" #: lib/logitech_receiver/hidpp20.py:974 msgid "Signature1" msgstr "Signatur1" #: lib/logitech_receiver/hidpp20.py:975 msgid "Signature2" msgstr "Signatur2" #: lib/logitech_receiver/hidpp20.py:976 msgid "CycleS" msgstr "SyklusS" #: lib/logitech_receiver/hidpp20.py:1040 msgid "Unknown Location" msgstr "Ukjent lokasjon" #: lib/logitech_receiver/hidpp20.py:1041 msgid "Primary" msgstr "Primær" #: lib/logitech_receiver/hidpp20.py:1042 msgid "Logo" msgstr "Logo" #: lib/logitech_receiver/hidpp20.py:1043 msgid "Left Side" msgstr "Venstre side" #: lib/logitech_receiver/hidpp20.py:1044 msgid "Right Side" msgstr "Høyre side" #: lib/logitech_receiver/hidpp20.py:1045 msgid "Combined" msgstr "Kombinert" #: lib/logitech_receiver/hidpp20.py:1046 msgid "Primary 1" msgstr "Primær 1" #: lib/logitech_receiver/hidpp20.py:1047 msgid "Primary 2" msgstr "Primær 2" #: lib/logitech_receiver/hidpp20.py:1048 msgid "Primary 3" msgstr "Primær 3" #: lib/logitech_receiver/hidpp20.py:1049 msgid "Primary 4" msgstr "Primær 4" #: lib/logitech_receiver/hidpp20.py:1050 msgid "Primary 5" msgstr "Primær 5" #: lib/logitech_receiver/hidpp20.py:1051 msgid "Primary 6" msgstr "Primær 6" #: lib/logitech_receiver/i18n.py:28 msgid "empty" msgstr "tom" #: lib/logitech_receiver/i18n.py:29 msgid "critical" msgstr "kritisk" #: lib/logitech_receiver/i18n.py:30 msgid "low" msgstr "lav" #: lib/logitech_receiver/i18n.py:31 msgid "average" msgstr "middels" #: lib/logitech_receiver/i18n.py:32 msgid "good" msgstr "god" #: lib/logitech_receiver/i18n.py:33 msgid "full" msgstr "full" #: lib/logitech_receiver/i18n.py:35 msgid "discharging" msgstr "utlading" #: lib/logitech_receiver/i18n.py:36 msgid "recharging" msgstr "opplading" #: lib/logitech_receiver/i18n.py:37 msgid "charging" msgstr "lader" #: lib/logitech_receiver/i18n.py:38 msgid "not charging" msgstr "lader ikke" #: lib/logitech_receiver/i18n.py:39 msgid "almost full" msgstr "nesten full" #: lib/logitech_receiver/i18n.py:40 msgid "charged" msgstr "ladet" #: lib/logitech_receiver/i18n.py:41 msgid "slow recharge" msgstr "langsom lading" #: lib/logitech_receiver/i18n.py:42 msgid "invalid battery" msgstr "batterifeil" #: lib/logitech_receiver/i18n.py:43 msgid "thermal error" msgstr "termisk feil" #: lib/logitech_receiver/i18n.py:44 msgid "error" msgstr "feil" #: lib/logitech_receiver/i18n.py:45 msgid "standard" msgstr "standard" #: lib/logitech_receiver/i18n.py:46 msgid "fast" msgstr "rask" #: lib/logitech_receiver/i18n.py:47 msgid "slow" msgstr "langsom" #: lib/logitech_receiver/i18n.py:49 msgid "device timeout" msgstr "enheten svarte ikke" #: lib/logitech_receiver/i18n.py:50 msgid "device not supported" msgstr "enheten er ikke støttet" #: lib/logitech_receiver/i18n.py:51 msgid "too many devices" msgstr "for mange enheter" #: lib/logitech_receiver/i18n.py:52 msgid "sequence timeout" msgstr "sekvens timeout" #: lib/logitech_receiver/i18n.py:54 msgid "Firmware" msgstr "Fastvare" #: lib/logitech_receiver/i18n.py:55 msgid "Bootloader" msgstr "Oppstartslaster" #: lib/logitech_receiver/i18n.py:56 msgid "Hardware" msgstr "Maskinvare" #: lib/logitech_receiver/i18n.py:57 msgid "Other" msgstr "Annet" #: lib/logitech_receiver/i18n.py:59 msgid "Left Button" msgstr "Venstreknapp" #: lib/logitech_receiver/i18n.py:60 msgid "Right Button" msgstr "Høyreknapp" #: lib/logitech_receiver/i18n.py:61 msgid "Middle Button" msgstr "Midtknapp" #: lib/logitech_receiver/i18n.py:62 msgid "Back Button" msgstr "Tilbakeknapp" #: lib/logitech_receiver/i18n.py:63 msgid "Forward Button" msgstr "Foroverknapp" #: lib/logitech_receiver/i18n.py:64 msgid "Mouse Gesture Button" msgstr "Bevegelsesknapp" #: lib/logitech_receiver/i18n.py:65 msgid "Smart Shift" msgstr "Smart skift" #: lib/logitech_receiver/i18n.py:66 msgid "DPI Switch" msgstr "DPI-bryter" #: lib/logitech_receiver/i18n.py:67 msgid "Left Tilt" msgstr "Venstre-tilt" #: lib/logitech_receiver/i18n.py:68 msgid "Right Tilt" msgstr "Høyre-tilt" #: lib/logitech_receiver/i18n.py:69 msgid "Left Click" msgstr "Venstre-klikk" #: lib/logitech_receiver/i18n.py:70 msgid "Right Click" msgstr "Høyre-klikk" #: lib/logitech_receiver/i18n.py:71 msgid "Mouse Middle Button" msgstr "Midtknapp for mus" #: lib/logitech_receiver/i18n.py:72 msgid "Mouse Back Button" msgstr "Tilbakeknapp for mus" #: lib/logitech_receiver/i18n.py:73 msgid "Mouse Forward Button" msgstr "Foroverknapp for mus" #: lib/logitech_receiver/i18n.py:74 msgid "Gesture Button Navigation" msgstr "Navigering med bevegelsesknapp" #: lib/logitech_receiver/i18n.py:75 msgid "Mouse Scroll Left Button" msgstr "Venstre rulleknapp for mus" #: lib/logitech_receiver/i18n.py:76 msgid "Mouse Scroll Right Button" msgstr "Høyre rulleknapp for mus" #: lib/logitech_receiver/i18n.py:78 msgid "pressed" msgstr "nedtrykt" #: lib/logitech_receiver/i18n.py:79 msgid "released" msgstr "utløst" #: lib/logitech_receiver/notifications.py:86 #: lib/logitech_receiver/notifications.py:138 msgid "pairing lock is closed" msgstr "parlåsen er lukket" #: lib/logitech_receiver/notifications.py:86 #: lib/logitech_receiver/notifications.py:138 msgid "pairing lock is open" msgstr "parlåsen er åpen" #: lib/logitech_receiver/notifications.py:103 msgid "discovery lock is closed" msgstr "oppdagelseslåsen er lukket" #: lib/logitech_receiver/notifications.py:103 msgid "discovery lock is open" msgstr "oppdagelseslåsen er åpen" #: lib/logitech_receiver/notifications.py:232 lib/solaar/listener.py:247 msgid "connected" msgstr "tilkoblet" #: lib/logitech_receiver/notifications.py:232 lib/solaar/listener.py:247 msgid "disconnected" msgstr "frakoblet" #: lib/logitech_receiver/notifications.py:258 msgid "unpaired" msgstr "uparet" #: lib/logitech_receiver/notifications.py:305 msgid "powered on" msgstr "påslått" #: lib/logitech_receiver/receiver.py:383 msgid "No paired devices." msgstr "Ingen paret enhet." #: lib/logitech_receiver/receiver.py:385 lib/solaar/ui/window.py:590 #, python-format msgid "%(count)s paired device." msgid_plural "%(count)s paired devices." msgstr[0] "%(count)s paret enhet." msgstr[1] "%(count)s parede enheter." #: lib/logitech_receiver/settings.py:614 msgid "register" msgstr "register" #: lib/logitech_receiver/settings.py:628 lib/logitech_receiver/settings.py:663 msgid "feature" msgstr "karakteristikk" #: lib/logitech_receiver/settings_templates.py:133 msgid "Swap Fx function" msgstr "Bytt Fx-funksjon" #: lib/logitech_receiver/settings_templates.py:136 msgid "" "When set, the F1..F12 keys will activate their special function,\n" "and you must hold the FN key to activate their standard function." msgstr "" "Hvis aktivert, vil F1–F12-tastene aktivere sin spesialfunksjon,\n" "og du må holde FN-tasten nede for å aktivere deres standardfunksjon." #: lib/logitech_receiver/settings_templates.py:141 msgid "" "When unset, the F1..F12 keys will activate their standard function,\n" "and you must hold the FN key to activate their special function." msgstr "" "Hvis deaktivert, vil F1–F12-tastene aktivere sin standardfunksjon,\n" "og du må holde FN-tasten nede for å aktivere deres spesialfunksjon." #: lib/logitech_receiver/settings_templates.py:149 msgid "Hand Detection" msgstr "Håndpåvisning" #: lib/logitech_receiver/settings_templates.py:150 msgid "Turn on illumination when the hands hover over the keyboard." msgstr "Slå på belysning når hendene er over tastaturet." #: lib/logitech_receiver/settings_templates.py:157 msgid "Scroll Wheel Smooth Scrolling" msgstr "Myk rulling med rullehjul" #: lib/logitech_receiver/settings_templates.py:158 #: lib/logitech_receiver/settings_templates.py:405 #: lib/logitech_receiver/settings_templates.py:434 msgid "High-sensitivity mode for vertical scroll with the wheel." msgstr "Høysensitiv modus for vertikal rulling med hjulet." #: lib/logitech_receiver/settings_templates.py:165 msgid "Side Scrolling" msgstr "Siderulling" #: lib/logitech_receiver/settings_templates.py:167 msgid "" "When disabled, pushing the wheel sideways sends custom button events\n" "instead of the standard side-scrolling events." msgstr "" "Hvis avslått; trykkes hjulet sideveis så sendes en tilpasset knappehendelse\n" "istedenfor standard siderulling." #: lib/logitech_receiver/settings_templates.py:177 msgid "Sensitivity (DPI - older mice)" msgstr "Følsomhet (DPI – eldre mus)" #: lib/logitech_receiver/settings_templates.py:178 #: lib/logitech_receiver/settings_templates.py:977 #: lib/logitech_receiver/settings_templates.py:1005 msgid "Mouse movement sensitivity" msgstr "Følsomhet for musbevegelser" #: lib/logitech_receiver/settings_templates.py:251 msgid "Backlight Timed" msgstr "Tidsbestemt bakgrunnslys" #: lib/logitech_receiver/settings_templates.py:252 #: lib/logitech_receiver/settings_templates.py:392 msgid "Set illumination time for keyboard." msgstr "Still inn belysningstid for tastatur." #: lib/logitech_receiver/settings_templates.py:263 msgid "Backlight" msgstr "Bakbelysning" #: lib/logitech_receiver/settings_templates.py:264 msgid "" "Illumination level on keyboard. Changes made are only applied in Manual " "mode." msgstr "" "Lysnivå på tastaturet. Endringer som er gjort, brukes kun i manuell modus." #: lib/logitech_receiver/settings_templates.py:296 msgid "Automatic" msgstr "Automatisk" #: lib/logitech_receiver/settings_templates.py:298 msgid "Manual" msgstr "Manuell" #: lib/logitech_receiver/settings_templates.py:300 msgid "Enabled" msgstr "Påslått" #: lib/logitech_receiver/settings_templates.py:306 msgid "Backlight Level" msgstr "Bakgrunnslysnivå" #: lib/logitech_receiver/settings_templates.py:307 msgid "Illumination level on keyboard when in Manual mode." msgstr "Lysnivå på tastaturet i manuell modus." #: lib/logitech_receiver/settings_templates.py:364 msgid "Backlight Delay Hands Out" msgstr "Forsinkelse på bakgrunnsbelysning når hendene fjernes" #: lib/logitech_receiver/settings_templates.py:365 msgid "" "Delay in seconds until backlight fades out with hands away from keyboard." msgstr "" "Forsinkelse i sekunder til bakgrunnsbelysningen toner ut når hendene er " "borte fra tastaturet." #: lib/logitech_receiver/settings_templates.py:373 msgid "Backlight Delay Hands In" msgstr "Forsinkelse på bakgrunnsbelysning når hendene er nære" #: lib/logitech_receiver/settings_templates.py:374 msgid "Delay in seconds until backlight fades out with hands near keyboard." msgstr "" "Forsinkelse i sekunder til bakgrunnsbelysningen toner ut når hendene er nære " "tastaturet." #: lib/logitech_receiver/settings_templates.py:382 msgid "Backlight Delay Powered" msgstr "Forsinkelse på bakgrunnsbelysning når strømforsynt" #: lib/logitech_receiver/settings_templates.py:383 msgid "Delay in seconds until backlight fades out with external power." msgstr "" "Forsinkelse i sekunder til bakgrunnsbelysningen toner ut med ekstern " "strømforsyning." #: lib/logitech_receiver/settings_templates.py:391 msgid "Backlight (Seconds)" msgstr "Bakgrunnslys (sekunder)" #: lib/logitech_receiver/settings_templates.py:403 msgid "Scroll Wheel High Resolution" msgstr "Høyoppløselig rullehjul" #: lib/logitech_receiver/settings_templates.py:407 #: lib/logitech_receiver/settings_templates.py:436 msgid "Set to ignore if scrolling is abnormally fast or slow" msgstr "Sett til å ignorere hvis rullingen er unormalt rask eller sakte" #: lib/logitech_receiver/settings_templates.py:414 #: lib/logitech_receiver/settings_templates.py:445 msgid "Scroll Wheel Diversion" msgstr "Omdirigering for rullehjul" #: lib/logitech_receiver/settings_templates.py:416 msgid "" "Make scroll wheel send LOWRES_WHEEL HID++ notifications (which trigger " "Solaar rules but are otherwise ignored)." msgstr "" "Få rullehjulet til å sende LOWRES_WHEEL HID++-varsler (som utløser Solaar-" "regler, men som ellers ignoreres)." #: lib/logitech_receiver/settings_templates.py:423 msgid "Scroll Wheel Direction" msgstr "Retning på rullehjul" #: lib/logitech_receiver/settings_templates.py:424 msgid "Invert direction for vertical scroll with wheel." msgstr "Inverter retning for vertikal rulling med hjul." #: lib/logitech_receiver/settings_templates.py:432 msgid "Scroll Wheel Resolution" msgstr "Oppløsning på rullehjul" #: lib/logitech_receiver/settings_templates.py:447 msgid "" "Make scroll wheel send HIRES_WHEEL HID++ notifications (which trigger Solaar " "rules but are otherwise ignored)." msgstr "" "Få rullehjulet til å sende HIRES_WHEEL HID++-varsler (som utløser Solaar-" "regler, men som ellers ignoreres)." #: lib/logitech_receiver/settings_templates.py:456 msgid "Sensitivity (Pointer Speed)" msgstr "Følsomhet (pekerfart)" #: lib/logitech_receiver/settings_templates.py:457 msgid "Speed multiplier for mouse (256 is normal multiplier)." msgstr "Fartsmultiplikator for mus (256 er normal multiplikator)." #: lib/logitech_receiver/settings_templates.py:467 msgid "Thumb Wheel Diversion" msgstr "Omdirigering for tommelhjul" #: lib/logitech_receiver/settings_templates.py:469 msgid "" "Make thumb wheel send THUMB_WHEEL HID++ notifications (which trigger Solaar " "rules but are otherwise ignored)." msgstr "" "Få tommelhjulet til å sende THUMB_WHEEL HID++-varsler (som utløser Solaar-" "regler, men som ellers ignoreres)." #: lib/logitech_receiver/settings_templates.py:478 msgid "Thumb Wheel Direction" msgstr "Retning på tommelhjul" #: lib/logitech_receiver/settings_templates.py:479 msgid "Invert thumb wheel scroll direction." msgstr "Inverter rulleretning for tommelhjul." #: lib/logitech_receiver/settings_templates.py:499 msgid "Onboard Profiles" msgstr "Innebygde profiler" #: lib/logitech_receiver/settings_templates.py:500 msgid "" "Enable an onboard profile, which controls report rate, sensitivity, and " "button actions" msgstr "" "Aktiver en innebygd profil, som kontrollerer rapporthyppighet, følsomhet og " "knappehandlinger" #: lib/logitech_receiver/settings_templates.py:544 #: lib/logitech_receiver/settings_templates.py:577 msgid "Report Rate" msgstr "Rapporthyppighet" #: lib/logitech_receiver/settings_templates.py:546 #: lib/logitech_receiver/settings_templates.py:579 msgid "Frequency of device movement reports" msgstr "Frekvensen til rapporter om enhetsbevegelser" #: lib/logitech_receiver/settings_templates.py:546 #: lib/logitech_receiver/settings_templates.py:579 #: lib/logitech_receiver/settings_templates.py:1005 #: lib/logitech_receiver/settings_templates.py:1379 #: lib/logitech_receiver/settings_templates.py:1410 msgid "May need Onboard Profiles set to Disable to be effective." msgstr "" "Kan trenge at innebygde profiler settes til avslått for å være effektive." #: lib/logitech_receiver/settings_templates.py:607 msgid "Divert crown events" msgstr "Omdiriger krone-hendelser" #: lib/logitech_receiver/settings_templates.py:608 msgid "" "Make crown send CROWN HID++ notifications (which trigger Solaar rules but " "are otherwise ignored)." msgstr "" "Få kronen til å sende CROWN HID++-varsler (som utløser Solaar-regler, men " "som ellers ignoreres)." #: lib/logitech_receiver/settings_templates.py:616 msgid "Crown smooth scroll" msgstr "Glatt krone-rulling" #: lib/logitech_receiver/settings_templates.py:617 msgid "Set crown smooth scroll" msgstr "Still inn glatt krone-rulling" #: lib/logitech_receiver/settings_templates.py:625 msgid "Divert G and M Keys" msgstr "Omdiriger G- og M-taster" #: lib/logitech_receiver/settings_templates.py:626 msgid "" "Make G and M keys send HID++ notifications (which trigger Solaar rules but " "are otherwise ignored)." msgstr "" "Få G- og M-tastene til å sende HID++-varsler (som utløser Solaar-regler, men " "som ellers ignoreres)." #: lib/logitech_receiver/settings_templates.py:640 msgid "Scroll Wheel Ratcheted" msgstr "Rullehjul sperrehaket" #: lib/logitech_receiver/settings_templates.py:641 msgid "" "Switch the mouse wheel between speed-controlled ratcheting and always " "freespin." msgstr "" "Bytt musehjulet mellom hastighetskontrollert sperrehake og alltid " "frittløpende." #: lib/logitech_receiver/settings_templates.py:643 msgid "Freespinning" msgstr "Frittløpende" #: lib/logitech_receiver/settings_templates.py:643 msgid "Ratcheted" msgstr "Sperrehake" #: lib/logitech_receiver/settings_templates.py:650 msgid "Scroll Wheel Ratchet Speed" msgstr "Rullehjul sperrehakefart" #: lib/logitech_receiver/settings_templates.py:652 msgid "" "Use the mouse wheel speed to switch between ratcheted and freespinning.\n" "The mouse wheel is always ratcheted at 50." msgstr "" "Bruk musehjulfarten til å bytte mellom sperrehake og frittløpende.\n" "Musehjulet er alltid satt til 50." #: lib/logitech_receiver/settings_templates.py:701 msgid "Key/Button Actions" msgstr "Taste-/knappehandling" #: lib/logitech_receiver/settings_templates.py:703 msgid "Change the action for the key or button." msgstr "Endre handling for en tast eller knapp." #: lib/logitech_receiver/settings_templates.py:705 msgid "Overridden by diversion." msgstr "Overstyrt av omdirigering." #: lib/logitech_receiver/settings_templates.py:707 msgid "" "Changing important actions (such as for the left mouse button) can result in " "an unusable system." msgstr "" "Endring av viktige handlinger (for eksempel venstre musknapp) kan resultere " "i et ubrukbart system." #: lib/logitech_receiver/settings_templates.py:882 msgid "Key/Button Diversion" msgstr "Taste-/knappeomdirigering" #: lib/logitech_receiver/settings_templates.py:883 msgid "" "Make the key or button send HID++ notifications (Diverted) or initiate Mouse " "Gestures or Sliding DPI" msgstr "" "Få nøkkelen eller knappen til å sende HID++-varsler (omdirigert) eller " "initier musebevegelser eller glidende DPI" #: lib/logitech_receiver/settings_templates.py:886 #: lib/logitech_receiver/settings_templates.py:887 #: lib/logitech_receiver/settings_templates.py:888 msgid "Diverted" msgstr "Omdirigert" #: lib/logitech_receiver/settings_templates.py:886 #: lib/logitech_receiver/settings_templates.py:887 msgid "Mouse Gestures" msgstr "Musebevegelser" #: lib/logitech_receiver/settings_templates.py:886 #: lib/logitech_receiver/settings_templates.py:887 #: lib/logitech_receiver/settings_templates.py:888 msgid "Regular" msgstr "Vanlig" #: lib/logitech_receiver/settings_templates.py:886 msgid "Sliding DPI" msgstr "Glidende DPI" #: lib/logitech_receiver/settings_templates.py:976 #: lib/logitech_receiver/settings_templates.py:1004 msgid "Sensitivity (DPI)" msgstr "Følsomhet (DPI)" #: lib/logitech_receiver/settings_templates.py:1081 msgid "Sensitivity Switching" msgstr "Følsomhetsbytte" #: lib/logitech_receiver/settings_templates.py:1083 msgid "" "Switch the current sensitivity and the remembered sensitivity when the key " "or button is pressed.\n" "If there is no remembered sensitivity, just remember the current sensitivity" msgstr "" "Bytt gjeldende følsomhet og tidligere følsomhet når tasten eller knappen " "trykkes.\n" "Hvis det ikke er noen tidligere følsomhet, sett kun gjeldende følsomhet" #: lib/logitech_receiver/settings_templates.py:1087 msgid "Off" msgstr "Av" #: lib/logitech_receiver/settings_templates.py:1118 msgid "Disable keys" msgstr "Slå av taster" #: lib/logitech_receiver/settings_templates.py:1119 msgid "Disable specific keyboard keys." msgstr "Slå av spesifikke tastaturtaster." #: lib/logitech_receiver/settings_templates.py:1122 #, python-format msgid "Disables the %s key." msgstr "Slår av %s-tasten." #: lib/logitech_receiver/settings_templates.py:1135 #: lib/logitech_receiver/settings_templates.py:1192 msgid "Set OS" msgstr "Still OS" #: lib/logitech_receiver/settings_templates.py:1136 #: lib/logitech_receiver/settings_templates.py:1193 msgid "Change keys to match OS." msgstr "Endre taster så de stemmer med OS." #: lib/logitech_receiver/settings_templates.py:1205 msgid "Change Host" msgstr "Endre tjener" #: lib/logitech_receiver/settings_templates.py:1206 msgid "Switch connection to a different host" msgstr "Endre koblingen til en annen tjener" #: lib/logitech_receiver/settings_templates.py:1230 msgid "Performs a left click." msgstr "Utfører et venstreklikk." #: lib/logitech_receiver/settings_templates.py:1230 msgid "Single tap" msgstr "Enkeltklikk" #: lib/logitech_receiver/settings_templates.py:1231 msgid "Performs a right click." msgstr "Utfører et høyreklikk." #: lib/logitech_receiver/settings_templates.py:1231 msgid "Single tap with two fingers" msgstr "Enkelt trykk med to fingre" #: lib/logitech_receiver/settings_templates.py:1232 msgid "Single tap with three fingers" msgstr "Enkelt trykk med tre fingre" #: lib/logitech_receiver/settings_templates.py:1236 msgid "Double tap" msgstr "Dobbeltklikk" #: lib/logitech_receiver/settings_templates.py:1236 msgid "Performs a double click." msgstr "Utfører et dobbeltklikk." #: lib/logitech_receiver/settings_templates.py:1237 msgid "Double tap with two fingers" msgstr "Dobbeltklikk med to fingre" #: lib/logitech_receiver/settings_templates.py:1238 msgid "Double tap with three fingers" msgstr "Dobbeltklikk med tre fingre" #: lib/logitech_receiver/settings_templates.py:1241 msgid "Drags items by dragging the finger after double tapping." msgstr "Dra elementer med fingeren etter dobbeltklikk." #: lib/logitech_receiver/settings_templates.py:1241 msgid "Tap and drag" msgstr "Klikk og dra" #: lib/logitech_receiver/settings_templates.py:1243 msgid "Tap and drag with two fingers" msgstr "Klikk og dra med to fingre" #: lib/logitech_receiver/settings_templates.py:1244 msgid "Drags items by dragging the fingers after double tapping." msgstr "Dra elementer med fingrene etter dobbeltklikk." #: lib/logitech_receiver/settings_templates.py:1246 msgid "Tap and drag with three fingers" msgstr "Klikk og dra med tre fingre" #: lib/logitech_receiver/settings_templates.py:1249 msgid "Suppress tap and edge gestures" msgstr "Undertrykk klikk- og kantbevegelser" #: lib/logitech_receiver/settings_templates.py:1250 msgid "Disables tap and edge gestures (equivalent to pressing Fn+LeftClick)." msgstr "Slår av klikk- og kantbevegelser (tilsvarer å trykke Fn+Venstreklikk)." #: lib/logitech_receiver/settings_templates.py:1252 msgid "Scroll with one finger" msgstr "Rull med en finger" #: lib/logitech_receiver/settings_templates.py:1252 #: lib/logitech_receiver/settings_templates.py:1253 #: lib/logitech_receiver/settings_templates.py:1256 msgid "Scrolls." msgstr "Ruller." #: lib/logitech_receiver/settings_templates.py:1253 #: lib/logitech_receiver/settings_templates.py:1256 msgid "Scroll with two fingers" msgstr "Rull med to fingre" #: lib/logitech_receiver/settings_templates.py:1254 msgid "Scroll horizontally with two fingers" msgstr "Rull horisontalt med to fingre" #: lib/logitech_receiver/settings_templates.py:1254 msgid "Scrolls horizontally." msgstr "Rull horisontalt." #: lib/logitech_receiver/settings_templates.py:1255 msgid "Scroll vertically with two fingers" msgstr "Rull vertikalt med to fingre" #: lib/logitech_receiver/settings_templates.py:1255 msgid "Scrolls vertically." msgstr "Rull vertikalt." #: lib/logitech_receiver/settings_templates.py:1257 msgid "Inverts the scrolling direction." msgstr "Inverterer rulleretningen." #: lib/logitech_receiver/settings_templates.py:1257 msgid "Natural scrolling" msgstr "Naturlig rulling" #: lib/logitech_receiver/settings_templates.py:1258 msgid "Enables the thumbwheel." msgstr "Aktiverer tommelhjulet." #: lib/logitech_receiver/settings_templates.py:1258 msgid "Thumbwheel" msgstr "Tommelhjul" #: lib/logitech_receiver/settings_templates.py:1269 #: lib/logitech_receiver/settings_templates.py:1273 msgid "Swipe from the top edge" msgstr "Sveip fra øvre kant" #: lib/logitech_receiver/settings_templates.py:1270 msgid "Swipe from the left edge" msgstr "Sveip fra venstre kant" #: lib/logitech_receiver/settings_templates.py:1271 msgid "Swipe from the right edge" msgstr "Sveip fra høyre kant" #: lib/logitech_receiver/settings_templates.py:1272 msgid "Swipe from the bottom edge" msgstr "Sveip fra nedre kant" #: lib/logitech_receiver/settings_templates.py:1274 msgid "Swipe two fingers from the left edge" msgstr "Sveip to fingre fra venstre kant" #: lib/logitech_receiver/settings_templates.py:1275 msgid "Swipe two fingers from the right edge" msgstr "Sveip to fingre fra høyre kant" #: lib/logitech_receiver/settings_templates.py:1276 msgid "Swipe two fingers from the bottom edge" msgstr "Sveip to fingre fra nedre kant" #: lib/logitech_receiver/settings_templates.py:1277 msgid "Swipe two fingers from the top edge" msgstr "Sveip to fingre fra øvre kant" #: lib/logitech_receiver/settings_templates.py:1278 #: lib/logitech_receiver/settings_templates.py:1282 msgid "Pinch to zoom out; spread to zoom in." msgstr "Knip for å zoome ut; spre for å zoome inn." #: lib/logitech_receiver/settings_templates.py:1278 msgid "Zoom with two fingers." msgstr "Zoom med to fingre." #: lib/logitech_receiver/settings_templates.py:1279 msgid "Pinch to zoom out." msgstr "Knip for å zoome ut." #: lib/logitech_receiver/settings_templates.py:1280 msgid "Spread to zoom in." msgstr "Spre for å zoome inn." #: lib/logitech_receiver/settings_templates.py:1281 msgid "Zoom with three fingers." msgstr "Zoom med tre fingre." #: lib/logitech_receiver/settings_templates.py:1282 msgid "Zoom with two fingers" msgstr "Zoom med to fingre" #: lib/logitech_receiver/settings_templates.py:1300 msgid "Pixel zone" msgstr "Pikselsone" #: lib/logitech_receiver/settings_templates.py:1301 msgid "Ratio zone" msgstr "Forholdssone" #: lib/logitech_receiver/settings_templates.py:1302 msgid "Scale factor" msgstr "Skalafaktor" #: lib/logitech_receiver/settings_templates.py:1302 msgid "Sets the cursor speed." msgstr "Still inn markørhastigheten." #: lib/logitech_receiver/settings_templates.py:1306 msgid "Left" msgstr "Venstre" #: lib/logitech_receiver/settings_templates.py:1306 msgid "Left-most coordinate." msgstr "Koordinat lengst til venstre." #: lib/logitech_receiver/settings_templates.py:1307 msgid "Bottom" msgstr "Nederst" #: lib/logitech_receiver/settings_templates.py:1307 msgid "Bottom coordinate." msgstr "Nederste koordinat." #: lib/logitech_receiver/settings_templates.py:1308 msgid "Width" msgstr "Bredde" #: lib/logitech_receiver/settings_templates.py:1308 msgid "Width." msgstr "Bredde." #: lib/logitech_receiver/settings_templates.py:1309 msgid "Height" msgstr "Høyde" #: lib/logitech_receiver/settings_templates.py:1309 msgid "Height." msgstr "Høyde." #: lib/logitech_receiver/settings_templates.py:1310 msgid "Cursor speed." msgstr "Markørhastighet." #: lib/logitech_receiver/settings_templates.py:1310 msgid "Scale" msgstr "Skala" #: lib/logitech_receiver/settings_templates.py:1316 msgid "Gestures" msgstr "Bevegelser" #: lib/logitech_receiver/settings_templates.py:1317 msgid "Tweak the mouse/touchpad behaviour." msgstr "Juster musens/berøringsplatens oppførsel." #: lib/logitech_receiver/settings_templates.py:1333 msgid "Gestures Diversion" msgstr "Omdirigering av bevegelser" #: lib/logitech_receiver/settings_templates.py:1334 msgid "Divert mouse/touchpad gestures." msgstr "Omdiriger musens/berøringsflatens bevegelser." #: lib/logitech_receiver/settings_templates.py:1350 msgid "Gesture params" msgstr "Parametre for bevegelser" #: lib/logitech_receiver/settings_templates.py:1351 msgid "Change numerical parameters of a mouse/touchpad." msgstr "Endre numeriske parametere for en mus/berøringsplate." #: lib/logitech_receiver/settings_templates.py:1375 msgid "M-Key LEDs" msgstr "M-tast LED-ene" #: lib/logitech_receiver/settings_templates.py:1377 msgid "Control the M-Key LEDs." msgstr "Kontroller M-tast LED-ene." #: lib/logitech_receiver/settings_templates.py:1381 #: lib/logitech_receiver/settings_templates.py:1412 msgid "May need G Keys diverted to be effective." msgstr "Kan trenge omdirigering av G-taster for å være effektivt." #: lib/logitech_receiver/settings_templates.py:1387 #, python-format msgid "Lights up the %s key." msgstr "Lyser opp %s-tasten." #: lib/logitech_receiver/settings_templates.py:1406 msgid "MR-Key LED" msgstr "MR-tast LED" #: lib/logitech_receiver/settings_templates.py:1408 msgid "Control the MR-Key LED." msgstr "Kontroller MR-tast LED." #: lib/logitech_receiver/settings_templates.py:1429 msgid "Persistent Key/Button Mapping" msgstr "Vedvarende tast-/knapptilordning" #: lib/logitech_receiver/settings_templates.py:1431 msgid "Permanently change the mapping for the key or button." msgstr "Endre tilordningen for tasten eller knappen permanent." #: lib/logitech_receiver/settings_templates.py:1433 msgid "" "Changing important keys or buttons (such as for the left mouse button) can " "result in an unusable system." msgstr "" "Endring av viktige taster eller knapper (som venstre museknapp) kan " "resultere i et ubrukelig system." #: lib/logitech_receiver/settings_templates.py:1490 msgid "Sidetone" msgstr "Sidetone" #: lib/logitech_receiver/settings_templates.py:1491 msgid "Set sidetone level." msgstr "Still inn sidetone nivå." #: lib/logitech_receiver/settings_templates.py:1500 msgid "Equalizer" msgstr "Equalizer" #: lib/logitech_receiver/settings_templates.py:1501 msgid "Set equalizer levels." msgstr "Still inn equalizer nivå." #: lib/logitech_receiver/settings_templates.py:1523 msgid "Hz" msgstr "Hz" #: lib/logitech_receiver/settings_templates.py:1529 msgid "Power Management" msgstr "Strømstyring" #: lib/logitech_receiver/settings_templates.py:1530 msgid "Power off in minutes (0 for never)." msgstr "Slå av i minutter (0 for aldri)." #: lib/logitech_receiver/settings_templates.py:1541 msgid "Brightness Control" msgstr "Lysstyrkekontroll" #: lib/logitech_receiver/settings_templates.py:1542 msgid "Control overall brightness" msgstr "Kontroller den generelle lysstyrken" #: lib/logitech_receiver/settings_templates.py:1585 #: lib/logitech_receiver/settings_templates.py:1639 msgid "LED Control" msgstr "LED-kontroll" #: lib/logitech_receiver/settings_templates.py:1586 #: lib/logitech_receiver/settings_templates.py:1640 msgid "Switch control of LED zones between device and Solaar" msgstr "Bytt kontroll av LED-soner mellom enhet og Solaar" #: lib/logitech_receiver/settings_templates.py:1601 #: lib/logitech_receiver/settings_templates.py:1650 msgid "LED Zone Effects" msgstr "LED-soneeffekter" #: lib/logitech_receiver/settings_templates.py:1602 #: lib/logitech_receiver/settings_templates.py:1651 msgid "LED Control needs to be set to Solaar to be effective." msgstr "LED-kontroll må settes til Solaar for å være effektiv." #: lib/logitech_receiver/settings_templates.py:1602 #: lib/logitech_receiver/settings_templates.py:1651 msgid "Set effect for LED Zone" msgstr "Still inn effekt for LED-sone" #: lib/logitech_receiver/settings_templates.py:1605 msgid "Speed" msgstr "Hastighet" #: lib/logitech_receiver/settings_templates.py:1606 msgid "Period" msgstr "Periode" #: lib/logitech_receiver/settings_templates.py:1607 msgid "Intensity" msgstr "Intensitet" #: lib/logitech_receiver/settings_templates.py:1608 msgid "Ramp" msgstr "Stigning" #: lib/logitech_receiver/settings_templates.py:1624 msgid "LEDs" msgstr "LEDs" #: lib/logitech_receiver/settings_templates.py:1661 msgid "Per-key Lighting" msgstr "Lys for hver tast" #: lib/logitech_receiver/settings_templates.py:1662 msgid "Control per-key lighting." msgstr "Kontroller lys for hver tast." #: lib/solaar/ui/__init__.py:117 msgid "Another Solaar process is already running so just expose its window" msgstr "En annen Solaar-prosess kjører allerede, så bare vis vinduet" #: lib/solaar/ui/about/model.py:36 msgid "" "Manages Logitech receivers,\n" "keyboards, mice, and tablets." msgstr "" "Administrerer Logitech-mottakere,\n" "tastaturer, mus og nettbrett." #: lib/solaar/ui/about/model.py:62 msgid "Additional Programming" msgstr "Ekstra programmering" #: lib/solaar/ui/about/model.py:63 msgid "GUI design" msgstr "GUI utseende" #: lib/solaar/ui/about/model.py:65 msgid "Testing" msgstr "Testing" #: lib/solaar/ui/about/model.py:73 msgid "Logitech documentation" msgstr "Logitech dokumentasjon" #: lib/solaar/ui/action.py:83 lib/solaar/ui/action.py:87 #: lib/solaar/ui/window.py:188 msgid "Unpair" msgstr "Fjern paring" #: lib/solaar/ui/action.py:86 lib/solaar/ui/diversion_rules.py:107 msgid "Cancel" msgstr "Avbryt" #: lib/solaar/ui/common.py:35 msgid "Permissions error" msgstr "Rettighetsfeil" #: lib/solaar/ui/common.py:37 #, python-format msgid "" "Found a Logitech receiver or device (%s), but did not have permission to " "open it." msgstr "" "Fant en Logitech-mottaker eller enhet (%s), men hadde ikke tillatelse til å " "åpne den." #: lib/solaar/ui/common.py:39 msgid "" "If you've just installed Solaar, try disconnecting the receiver or device " "and then reconnecting it." msgstr "" "Hvis du nettopp har installert Solaar, kan du prøve å koble fra mottakeren " "eller enheten og deretter koble den til igjen." #: lib/solaar/ui/common.py:42 msgid "Cannot connect to device error" msgstr "Kan ikke koble til enhet-feil" #: lib/solaar/ui/common.py:44 #, python-format msgid "" "Found a Logitech receiver or device at %s, but encountered an error " "connecting to it." msgstr "" "Fant en Logitech-mottaker eller -enhet på %s, men oppdaget en feil når den " "ble tilkoblet." #: lib/solaar/ui/common.py:46 msgid "" "Try disconnecting the device and then reconnecting it or turning it off and " "then on." msgstr "" "Prøv å koble fra enheten og deretter koble den til igjen eller slå den av og " "på igjen." #: lib/solaar/ui/common.py:49 msgid "Unpairing failed" msgstr "Kunne ikke bryte paringen" #: lib/solaar/ui/common.py:51 #, python-brace-format msgid "Failed to unpair %{device} from %{receiver}." msgstr "Mislyktes med å bryte paret %{device} og %{receiver}." #: lib/solaar/ui/common.py:56 msgid "The receiver returned an error, with no further details." msgstr "Mottakeren rapporterte en feil, uten flere detaljer." #: lib/solaar/ui/config_panel.py:228 msgid "Complete - ENTER to change" msgstr "Fullfør - ENTER for å endre" #: lib/solaar/ui/config_panel.py:228 msgid "Incomplete" msgstr "Ufullstendig" #: lib/solaar/ui/config_panel.py:475 lib/solaar/ui/config_panel.py:527 #, python-format msgid "%d value" msgid_plural "%d values" msgstr[0] "%d verdi" msgstr[1] "%d verdier" #: lib/solaar/ui/config_panel.py:609 msgid "Changes allowed" msgstr "Endringer tillatt" #: lib/solaar/ui/config_panel.py:610 msgid "No changes allowed" msgstr "Ingen endringer tillatt" #: lib/solaar/ui/config_panel.py:611 msgid "Ignore this setting" msgstr "Ignorer denne innstillingen" #: lib/solaar/ui/config_panel.py:655 msgid "Working" msgstr "Suksess" #: lib/solaar/ui/config_panel.py:658 msgid "Read/write operation failed." msgstr "Lese-/skriveoperasjonen mislyktes." #: lib/solaar/ui/desktop_notifications.py:114 msgid "unspecified reason" msgstr "uspesifisert årsak" #: lib/solaar/ui/diversion_rules.py:69 msgid "Built-in rules" msgstr "Faste regler" #: lib/solaar/ui/diversion_rules.py:69 msgid "User-defined rules" msgstr "Brukerdefinerte regler" #: lib/solaar/ui/diversion_rules.py:71 lib/solaar/ui/diversion_rules.py:1089 msgid "Rule" msgstr "Regel" #: lib/solaar/ui/diversion_rules.py:72 lib/solaar/ui/diversion_rules.py:348 #: lib/solaar/ui/diversion_rules.py:475 msgid "Sub-rule" msgstr "Underregel" #: lib/solaar/ui/diversion_rules.py:74 msgid "[empty]" msgstr "[tom]" #: lib/solaar/ui/diversion_rules.py:98 msgid "Make changes permanent?" msgstr "Gjøre endringer permanente?" #: lib/solaar/ui/diversion_rules.py:103 msgid "Yes" msgstr "Ja" #: lib/solaar/ui/diversion_rules.py:105 msgid "No" msgstr "Nei" #: lib/solaar/ui/diversion_rules.py:110 msgid "If you choose No, changes will be lost when Solaar is closed." msgstr "Hvis du velger Nei, vil endringer gå tapt når Solaar lukkes." #: lib/solaar/ui/diversion_rules.py:239 msgid "Paste here" msgstr "Lim inn her" #: lib/solaar/ui/diversion_rules.py:241 msgid "Paste above" msgstr "Lim inn ovenfor" #: lib/solaar/ui/diversion_rules.py:243 msgid "Paste below" msgstr "Lim inn nedenfor" #: lib/solaar/ui/diversion_rules.py:249 msgid "Paste rule here" msgstr "Lim inn regel her" #: lib/solaar/ui/diversion_rules.py:251 msgid "Paste rule above" msgstr "Lim inn regel ovenfor" #: lib/solaar/ui/diversion_rules.py:253 msgid "Paste rule below" msgstr "Lim inn regel nedenfor" #: lib/solaar/ui/diversion_rules.py:257 msgid "Paste rule" msgstr "Lim inn regel" #: lib/solaar/ui/diversion_rules.py:272 msgid "Insert here" msgstr "Sett inn her" #: lib/solaar/ui/diversion_rules.py:274 msgid "Insert above" msgstr "Sett inn ovenfor" #: lib/solaar/ui/diversion_rules.py:276 msgid "Insert below" msgstr "Sett inn nedenfor" #: lib/solaar/ui/diversion_rules.py:282 msgid "Insert new rule here" msgstr "Sett inn ny regel her" #: lib/solaar/ui/diversion_rules.py:284 msgid "Insert new rule above" msgstr "Sett inn ny regel ovenfor" #: lib/solaar/ui/diversion_rules.py:286 msgid "Insert new rule below" msgstr "Sett inn ny regel nedenfor" #: lib/solaar/ui/diversion_rules.py:313 msgid "Flatten" msgstr "Utflate" #: lib/solaar/ui/diversion_rules.py:346 msgid "Insert" msgstr "Sett inn" #: lib/solaar/ui/diversion_rules.py:349 lib/solaar/ui/diversion_rules.py:477 #: lib/solaar/ui/diversion_rules.py:1121 msgid "Or" msgstr "Eller" #: lib/solaar/ui/diversion_rules.py:350 lib/solaar/ui/diversion_rules.py:476 #: lib/solaar/ui/diversion_rules.py:1107 msgid "And" msgstr "Og" #: lib/solaar/ui/diversion_rules.py:352 msgid "Condition" msgstr "Betingelse" #: lib/solaar/ui/diversion_rules.py:354 lib/solaar/ui/rule_conditions.py:145 msgid "Feature" msgstr "Karakteristikk" #: lib/solaar/ui/diversion_rules.py:355 lib/solaar/ui/rule_conditions.py:179 msgid "Report" msgstr "Rapport" #: lib/solaar/ui/diversion_rules.py:356 lib/solaar/ui/rule_conditions.py:60 msgid "Process" msgstr "Prosess" #: lib/solaar/ui/diversion_rules.py:357 msgid "Mouse process" msgstr "Musprosess" #: lib/solaar/ui/diversion_rules.py:358 lib/solaar/ui/rule_conditions.py:216 msgid "Modifiers" msgstr "Modifikatorer" #: lib/solaar/ui/diversion_rules.py:359 lib/solaar/ui/rule_conditions.py:268 msgid "Key" msgstr "Tast" #: lib/solaar/ui/diversion_rules.py:360 lib/solaar/ui/rule_conditions.py:309 msgid "KeyIsDown" msgstr "TastErNede" #: lib/solaar/ui/diversion_rules.py:361 lib/solaar/ui/diversion_rules.py:1414 msgid "Active" msgstr "Aktiv" #: lib/solaar/ui/diversion_rules.py:362 lib/solaar/ui/diversion_rules.py:1372 #: lib/solaar/ui/diversion_rules.py:1423 lib/solaar/ui/diversion_rules.py:1470 msgid "Device" msgstr "Enhet" #: lib/solaar/ui/diversion_rules.py:363 lib/solaar/ui/diversion_rules.py:1449 msgid "Host" msgstr "Vert" #: lib/solaar/ui/diversion_rules.py:364 lib/solaar/ui/diversion_rules.py:1489 msgid "Setting" msgstr "Innstilling" #: lib/solaar/ui/diversion_rules.py:365 lib/solaar/ui/rule_conditions.py:324 #: lib/solaar/ui/rule_conditions.py:373 msgid "Test" msgstr "Test" #: lib/solaar/ui/diversion_rules.py:366 lib/solaar/ui/rule_conditions.py:498 msgid "Test bytes" msgstr "Test bytes" #: lib/solaar/ui/diversion_rules.py:367 lib/solaar/ui/rule_conditions.py:599 msgid "Mouse Gesture" msgstr "Musebevegelse" #: lib/solaar/ui/diversion_rules.py:371 msgid "Action" msgstr "Handling" #: lib/solaar/ui/diversion_rules.py:373 lib/solaar/ui/rule_actions.py:131 msgid "Key press" msgstr "Tastetrykk" #: lib/solaar/ui/diversion_rules.py:374 lib/solaar/ui/rule_actions.py:182 msgid "Mouse scroll" msgstr "Muserulling" #: lib/solaar/ui/diversion_rules.py:375 lib/solaar/ui/rule_actions.py:243 msgid "Mouse click" msgstr "Museklikk" #: lib/solaar/ui/diversion_rules.py:376 msgid "Set" msgstr "Still inn" #: lib/solaar/ui/diversion_rules.py:377 lib/solaar/ui/rule_actions.py:313 msgid "Execute" msgstr "Utfør" #: lib/solaar/ui/diversion_rules.py:378 lib/solaar/ui/diversion_rules.py:1152 msgid "Later" msgstr "Senere" #: lib/solaar/ui/diversion_rules.py:407 msgid "Insert new rule" msgstr "Sett inn ny regel" #: lib/solaar/ui/diversion_rules.py:427 lib/solaar/ui/rule_actions.py:74 #: lib/solaar/ui/rule_actions.py:272 lib/solaar/ui/rule_conditions.py:546 msgid "Delete" msgstr "Slett" #: lib/solaar/ui/diversion_rules.py:449 msgid "Negate" msgstr "Omvendt" #: lib/solaar/ui/diversion_rules.py:473 msgid "Wrap with" msgstr "Pakk inn med" #: lib/solaar/ui/diversion_rules.py:503 msgid "Cut" msgstr "Klipp ut" #: lib/solaar/ui/diversion_rules.py:519 msgid "Paste" msgstr "Lim inn" #: lib/solaar/ui/diversion_rules.py:525 msgid "Copy" msgstr "Kopier" #: lib/solaar/ui/diversion_rules.py:534 msgid "Solaar Rule Editor" msgstr "Solaar regel editor" #: lib/solaar/ui/diversion_rules.py:634 msgid "Save changes" msgstr "Lagre endringer" #: lib/solaar/ui/diversion_rules.py:639 msgid "Discard changes" msgstr "Forkast endringer" #: lib/solaar/ui/diversion_rules.py:1070 msgid "This editor does not support the selected rule component yet." msgstr "Denne editoren støtter foreløpig ikke den valgte regelkomponenten." #: lib/solaar/ui/diversion_rules.py:1132 msgid "" "Number of seconds to delay. Delay between 0 and 1 is done with higher " "precision." msgstr "" "Antall sekunder med forsinkelse. Forsinkelse mellom 0 og 1 gjøres med høyere " "presisjon." #: lib/solaar/ui/diversion_rules.py:1170 msgid "Not" msgstr "Ikke" #: lib/solaar/ui/diversion_rules.py:1201 msgid "Toggle" msgstr "Veksle" #: lib/solaar/ui/diversion_rules.py:1202 msgid "True" msgstr "Sann" #: lib/solaar/ui/diversion_rules.py:1203 msgid "False" msgstr "Falsk" #: lib/solaar/ui/diversion_rules.py:1216 msgid "Unsupported setting" msgstr "Innstillingen støttes ikke" #: lib/solaar/ui/diversion_rules.py:1378 lib/solaar/ui/diversion_rules.py:1397 #: lib/solaar/ui/diversion_rules.py:1476 lib/solaar/ui/diversion_rules.py:1731 #: lib/solaar/ui/diversion_rules.py:1749 msgid "Originating device" msgstr "Kildeenhet" #: lib/solaar/ui/diversion_rules.py:1410 msgid "Device is active and its settings can be changed." msgstr "Enheten er aktiv og innstillingene kan endres." #: lib/solaar/ui/diversion_rules.py:1419 msgid "Device that originated the current notification." msgstr "Enheten som ga det gjeldende varselet." #: lib/solaar/ui/diversion_rules.py:1432 msgid "Name of host computer." msgstr "Navn på vertsmaskin." #: lib/solaar/ui/diversion_rules.py:1503 msgid "Value" msgstr "Verdi" #: lib/solaar/ui/diversion_rules.py:1512 msgid "Item" msgstr "Punkt" #: lib/solaar/ui/diversion_rules.py:1791 msgid "Change setting on device" msgstr "Endre innstilling på enhet" #: lib/solaar/ui/diversion_rules.py:1807 msgid "Setting on device" msgstr "Innstilling på enhet" #: lib/solaar/ui/pair_window.py:37 lib/solaar/ui/pair_window.py:165 #, python-format msgid "%(receiver_name)s: pair new device" msgstr "%(receiver_name)s: par ny enhet" #: lib/solaar/ui/pair_window.py:39 msgid "Bolt receivers are only compatible with Bolt devices." msgstr "Bolt-mottakere er kun kompatible med Bolt-enheter." #: lib/solaar/ui/pair_window.py:41 msgid "Press a pairing button or key until the pairing light flashes quickly." msgstr "" "Trykk på sammenkoblingsknappen eller -tasten til sammenkoblingslampen " "blinker raskt." #: lib/solaar/ui/pair_window.py:44 msgid "Unifying receivers are only compatible with Unifying devices." msgstr "Unifying-mottakere er bare kompatible med Unifying-enheter." #: lib/solaar/ui/pair_window.py:46 msgid "Other receivers are only compatible with a few devices." msgstr "Andre mottakere er kun kompatible med noen få enheter." #: lib/solaar/ui/pair_window.py:48 msgid "For most devices, turn on the device you want to pair." msgstr "For de fleste enheter slår du på enheten du vil pare." #: lib/solaar/ui/pair_window.py:49 msgid "If the device is already turned on, turn it off and on again." msgstr "Hvis enheten allerede er påslått, slå den av og på igjen." #: lib/solaar/ui/pair_window.py:51 msgid "The device must not be paired with a nearby powered-on receiver." msgstr "Enheten må ikke pares med en påslått mottaker i nærheten." #: lib/solaar/ui/pair_window.py:54 msgid "" "For devices with multiple channels, press, hold, and release the button for " "the channel you wish to pair\n" "or use the channel switch button to select a channel and then press, hold, " "and release the channel switch button." msgstr "" "For enheter med flere kanaler, trykk, hold og slipp knappen for kanalen du " "ønsker å pare\n" "eller bruk kanalbryterknappen for å velge en kanal og trykk, hold og slipp " "kanalbryterknappen." #: lib/solaar/ui/pair_window.py:61 msgid "The channel indicator light should be blinking rapidly." msgstr "Kanalindikatorlampen skal blinke raskt." #: lib/solaar/ui/pair_window.py:65 #, python-format msgid "" "\n" "\n" "This receiver has %d pairing remaining." msgid_plural "" "\n" "\n" "This receiver has %d pairings remaining." msgstr[0] "" "\n" "\n" "Denne mottakeren har %d gjenstående paring." msgstr[1] "" "\n" "\n" "Denne mottakeren har %d gjenstående paringer." #: lib/solaar/ui/pair_window.py:71 msgid "" "\n" "Cancelling at this point will not use up a pairing." msgstr "" "\n" "Kansellering nå vil ikke bruke opp en paring." #: lib/solaar/ui/pair_window.py:166 #, python-format msgid "Enter passcode on %(name)s." msgstr "Skriv inn passordet på %(name)s." #: lib/solaar/ui/pair_window.py:169 #, python-format msgid "Type %(passcode)s and then press the enter key." msgstr "Skriv inn %(passcode)s og trykk deretter på enter-tasten." #: lib/solaar/ui/pair_window.py:174 msgid "left" msgstr "venstre" #: lib/solaar/ui/pair_window.py:174 msgid "right" msgstr "høyre" #: lib/solaar/ui/pair_window.py:176 #, python-format msgid "" "Press %(code)s\n" "and then press left and right buttons simultaneously." msgstr "" "Trykk %(code)s\n" "og trykk deretter på venstre og høyre knapp samtidig." #: lib/solaar/ui/pair_window.py:219 msgid "The wireless link is not encrypted" msgstr "Den trådløse koblingen er ukryptert" #: lib/solaar/ui/pair_window.py:224 msgid "Found a new device:" msgstr "Fant ny enhet:" #: lib/solaar/ui/pair_window.py:245 msgid "Pairing failed" msgstr "Paringen feilet" #: lib/solaar/ui/pair_window.py:247 msgid "Make sure your device is within range, and has a decent battery charge." msgstr "" "Forsikre deg om at enheten er innen rekkevidde, og at den er tilstrekkelig " "oppladet." #: lib/solaar/ui/pair_window.py:249 msgid "A new device was detected, but it is not compatible with this receiver." msgstr "En ny enhet ble påvist, men den er ukompatibel med denne mottakeren." #: lib/solaar/ui/pair_window.py:251 msgid "More paired devices than receiver can support." msgstr "Fler parede enheter enn mottakeren kan støtte." #: lib/solaar/ui/pair_window.py:253 msgid "No further details are available about the error." msgstr "Ingen ytterligere detaljer er tilgjengelig om feilen." #: lib/solaar/ui/rule_actions.py:48 msgid "" "Simulate a chorded key click or depress or release.\n" "On Wayland requires write access to /dev/uinput." msgstr "" "Simuler en chorded (?) tast, klikk eller trykk ned eller slipp.\n" "På Wayland krever dette skrivetilgang til /dev/uinput." #: lib/solaar/ui/rule_actions.py:53 msgid "Add key" msgstr "Legg til tast" #: lib/solaar/ui/rule_actions.py:56 msgid "Click" msgstr "Klikk" #: lib/solaar/ui/rule_actions.py:59 msgid "Depress" msgstr "Nedtrykk" #: lib/solaar/ui/rule_actions.py:62 msgid "Release" msgstr "Utløs" #: lib/solaar/ui/rule_actions.py:146 msgid "" "Simulate a mouse scroll.\n" "On Wayland requires write access to /dev/uinput." msgstr "" "Simuler en muserulling.\n" "På Wayland krever dette skrivetilgang til /dev/uinput." #: lib/solaar/ui/rule_actions.py:202 msgid "" "Simulate a mouse click.\n" "On Wayland requires write access to /dev/uinput." msgstr "" "Simuler et museklikk.\n" "På Wayland krever dette skrivetilgang til /dev/uinput." #: lib/solaar/ui/rule_actions.py:205 msgid "Button" msgstr "Knapp" #: lib/solaar/ui/rule_actions.py:206 msgid "Count and Action" msgstr "Antall og handling" #: lib/solaar/ui/rule_actions.py:255 msgid "Execute a command with arguments." msgstr "Utfør en kommando med argumenter." #: lib/solaar/ui/rule_actions.py:258 msgid "Add argument" msgstr "Legg til argument" #: lib/solaar/ui/rule_conditions.py:43 msgid "X11 active process. For use in X11 only." msgstr "X11 aktiv prosess. Kun til bruk i X11." #: lib/solaar/ui/rule_conditions.py:73 msgid "X11 mouse process. For use in X11 only." msgstr "X11-museprosess. Kun til bruk i X11." #: lib/solaar/ui/rule_conditions.py:90 msgid "MouseProcess" msgstr "MusProsess" #: lib/solaar/ui/rule_conditions.py:114 msgid "Feature name of notification triggering rule processing." msgstr "Funksjonsnavn på varsel som utløste regelbehandling." #: lib/solaar/ui/rule_conditions.py:160 msgid "Report number of notification triggering rule processing." msgstr "Rapportnummer for varsling som utløser regelbehandling." #: lib/solaar/ui/rule_conditions.py:192 msgid "Active keyboard modifiers. Not always available in Wayland." msgstr "Aktive tastaturmodifikatorer. Ikke alltid tilgjengelig i Wayland." #: lib/solaar/ui/rule_conditions.py:232 msgid "" "Diverted key or button depressed or released.\n" "Use the Key/Button Diversion and Divert G Keys settings to divert keys and " "buttons." msgstr "" "Omdirigert tast eller knapp som er trykket eller sluppet.\n" "Bruk innstillingene for omdirigering av tast/knapp og omdiriger G-taster for " "å videresende taster og knapper." #: lib/solaar/ui/rule_conditions.py:241 msgid "Key down" msgstr "Tast ned" #: lib/solaar/ui/rule_conditions.py:244 msgid "Key up" msgstr "Tast opp" #: lib/solaar/ui/rule_conditions.py:284 msgid "" "Diverted key or button is currently down.\n" "Use the Key/Button Diversion and Divert G Keys settings to divert keys and " "buttons." msgstr "" "Omdirigert tast eller knapp er nede.\n" "Bruk innstillingene for omdirigering av tast/knapp og omdirigerte G-taster " "for å videresende taster og knapper." #: lib/solaar/ui/rule_conditions.py:322 msgid "Test condition on notification triggering rule processing." msgstr "Testbetingelse på varsel som utløser regelbehandling." #: lib/solaar/ui/rule_conditions.py:326 msgid "Parameter" msgstr "Parameter" #: lib/solaar/ui/rule_conditions.py:399 msgid "begin (inclusive)" msgstr "begynn (inklusiv)" #: lib/solaar/ui/rule_conditions.py:400 msgid "end (exclusive)" msgstr "slutt (eksklusiv)" #: lib/solaar/ui/rule_conditions.py:408 msgid "range" msgstr "område" #: lib/solaar/ui/rule_conditions.py:411 msgid "minimum" msgstr "minimum" #: lib/solaar/ui/rule_conditions.py:412 msgid "maximum" msgstr "maksimum" #: lib/solaar/ui/rule_conditions.py:414 #, python-format msgid "bytes %(0)d to %(1)d, ranging from %(2)d to %(3)d" msgstr "byte %(0)d til %(1)d, fra %(2)d til %(3)d" #: lib/solaar/ui/rule_conditions.py:417 lib/solaar/ui/rule_conditions.py:418 msgid "mask" msgstr "maske" #: lib/solaar/ui/rule_conditions.py:419 #, python-format msgid "bytes %(0)d to %(1)d, mask %(2)d" msgstr "byte %(0)d til %(1)d, maske %(2)d" #: lib/solaar/ui/rule_conditions.py:428 msgid "" "Bit or range test on bytes in notification message triggering rule " "processing." msgstr "" "Bit- eller områdetest på bytes i varselmelding som utløste regelbehandling." #: lib/solaar/ui/rule_conditions.py:438 msgid "type" msgstr "type" #: lib/solaar/ui/rule_conditions.py:526 msgid "" "Mouse gesture with optional initiating button followed by zero or more mouse " "movements." msgstr "" "Musebevegelse med valgfri startknapp etterfulgt av null eller flere " "musebevegelser." #: lib/solaar/ui/rule_conditions.py:531 msgid "Add movement" msgstr "Legg til bevegelse" #: lib/solaar/ui/tray.py:49 msgid "No supported device found" msgstr "Ingen støttede enheter er funnet" #: lib/solaar/ui/tray.py:54 lib/solaar/ui/window.py:308 #, python-format msgid "About %s" msgstr "Om %s" #: lib/solaar/ui/tray.py:55 lib/solaar/ui/window.py:306 #, python-format msgid "Quit %s" msgstr "Avslutt %s" #: lib/solaar/ui/tray.py:270 lib/solaar/ui/tray.py:278 msgid "no receiver" msgstr "ingen mottaker" #: lib/solaar/ui/tray.py:291 lib/solaar/ui/tray.py:296 msgid "offline" msgstr "avslått" #: lib/solaar/ui/tray.py:294 msgid "no status" msgstr "ingen status" #: lib/solaar/ui/window.py:90 msgid "Scanning" msgstr "Søker" #: lib/solaar/ui/window.py:121 msgid "Battery" msgstr "Batteri" #: lib/solaar/ui/window.py:124 msgid "Wireless Link" msgstr "Trådløs kobling" #: lib/solaar/ui/window.py:128 msgid "Lighting" msgstr "Belysning" #: lib/solaar/ui/window.py:162 msgid "Show Technical Details" msgstr "Vis tekniske detaljer" #: lib/solaar/ui/window.py:178 msgid "Pair new device" msgstr "Par ny enhet" #: lib/solaar/ui/window.py:196 msgid "Select a device" msgstr "Velg en enhet" #: lib/solaar/ui/window.py:311 msgid "Rule Editor" msgstr "Regel editor" #: lib/solaar/ui/window.py:504 msgid "Path" msgstr "Sti" #: lib/solaar/ui/window.py:506 msgid "USB ID" msgstr "USB-ID" #: lib/solaar/ui/window.py:509 lib/solaar/ui/window.py:511 #: lib/solaar/ui/window.py:526 lib/solaar/ui/window.py:528 msgid "Serial" msgstr "Seriell" #: lib/solaar/ui/window.py:515 msgid "Index" msgstr "Indeks" #: lib/solaar/ui/window.py:517 msgid "Wireless PID" msgstr "Trådløs-PID" #: lib/solaar/ui/window.py:519 msgid "Product ID" msgstr "Produkt-ID" #: lib/solaar/ui/window.py:521 msgid "Protocol" msgstr "Protokoll" #: lib/solaar/ui/window.py:521 msgid "Unknown" msgstr "Ukjent" #: lib/solaar/ui/window.py:523 msgid "Polling rate" msgstr "Spørrerate" #: lib/solaar/ui/window.py:530 msgid "Unit ID" msgstr "Enhets-ID" #: lib/solaar/ui/window.py:544 msgid "Notifications" msgstr "Varsler" #: lib/solaar/ui/window.py:588 msgid "No device paired." msgstr "Ingen enhet paret." #: lib/solaar/ui/window.py:597 #, python-format msgid "Up to %(max_count)s device can be paired to this receiver." msgid_plural "Up to %(max_count)s devices can be paired to this receiver." msgstr[0] "Inntil %(max_count)s enhet kan bli paret med denne mottakeren." msgstr[1] "Inntil %(max_count)s enheter kan bli paret med denne mottakeren." #: lib/solaar/ui/window.py:608 #, python-format msgid "This receiver has %d pairing remaining." msgid_plural "This receiver has %d pairings remaining." msgstr[0] "Denne mottakeren har %d gjenstående paring." msgstr[1] "Denne mottakeren har %d gjenstående paringer." #: lib/solaar/ui/window.py:665 msgid "Battery Voltage" msgstr "Batterispenning" #: lib/solaar/ui/window.py:667 msgid "Voltage reported by battery" msgstr "Spenning rapportert av batteri" #: lib/solaar/ui/window.py:669 msgid "Battery Level" msgstr "Batterinivå" #: lib/solaar/ui/window.py:671 msgid "Approximate level reported by battery" msgstr "Omtrentlig nivå rapportert fra batteri" #: lib/solaar/ui/window.py:678 lib/solaar/ui/window.py:680 msgid "next reported " msgstr "neste rapportert " #: lib/solaar/ui/window.py:681 msgid " and next level to be reported." msgstr " og neste nivå som blir rapportert." #: lib/solaar/ui/window.py:697 msgid "encrypted" msgstr "kryptert" #: lib/solaar/ui/window.py:699 msgid "The wireless link between this device and its receiver is encrypted." msgstr "Den trådløse tilkoblingen mellom enheten og mottakeren er kryptert." #: lib/solaar/ui/window.py:701 msgid "not encrypted" msgstr "ukryptert" #: lib/solaar/ui/window.py:705 msgid "" "The wireless link between this device and its receiver is not encrypted.\n" "This is a security issue for pointing devices, and a major security issue " "for text-input devices." msgstr "" "Den trådløse koblingen mellom denne enheten og mottakeren er ikke kryptert.\n" "Dette er et sikkerhetsproblem for pekeenheter, og et stort sikkerhetsproblem " "for enheter for tekstinndata." #: lib/solaar/ui/window.py:721 #, python-format msgid "%(light_level)d lux" msgstr "%(light_level)d lux" #~ msgid "" #~ "\n" #~ "\n" #~ "This receiver has %d pairing(s) remaining." #~ msgstr "" #~ "\n" #~ "\n" #~ "Denne mottakeren har %d gjenstående paringer." #, python-format #~ msgid "%(rate)d ms (%(rate_hz)dHz)" #~ msgstr "%(rate)d ms (%(rate_hz)dHz)" #~ msgid "Actions" #~ msgstr "Handlinger" #~ msgid "Add action" #~ msgstr "Legg til handling" #~ msgid "" #~ "Adjust the DPI by sliding the mouse horizontally while holding the button " #~ "down." #~ msgstr "" #~ "Juster DPI ved å skyve musen horisontalt mens du holder knappen nede." #~ msgid "" #~ "Automatically switch the mouse wheel between ratchet and freespin mode.\n" #~ "The mouse wheel is always free at 0, and always locked at 50" #~ msgstr "" #~ "Bytt mushjulet automatisk mellom sperre- og frispinnmodus.\n" #~ "Mushjulet er alltid fritt på 0, og låses alltid på 50" #~ msgid "" #~ "Automatically switch the mouse wheel between ratchet and freespin mode.\n" #~ "The mouse wheel is always free at 0, and always ratcheted at 50" #~ msgstr "" #~ "Bytt automatisk rullehjulet mellom skralle- og frispinn-modus.\n" #~ "Rullehjulet er alltid fritt ved 0, og med skralle ved 50" #~ msgid "Battery information unknown." #~ msgstr "Ukjent batteriinformasjon" #, python-format #~ msgid "Battery: %(level)s" #~ msgstr "Batteri: %(level)s" #, python-format #~ msgid "Battery: %(percent)d%%" #~ msgstr "Batteri: %(percent)d%%" #~ msgid "Count" #~ msgstr "Antall" #~ msgid "DPI Sliding Adjustment" #~ msgstr "DPI-glidejustering" #~ msgid "Divert G Keys" #~ msgstr "Omdiriger G-taster" #~ msgid "" #~ "Diverted key or button depressed or released.\n" #~ "Use the Key/Button Diversion setting to divert keys and buttons." #~ msgstr "" #~ "Omdirigert tast eller knapp, trykket eller sluppet.\n" #~ "Bruk innstillingen for omdirigering av tast/knapp for å viderekoble " #~ "taster og knapper." #~ msgid "Effectively turns off thumb scrolling in Linux." #~ msgstr "Slår i praksis av tommelrulling i Linux." #~ msgid "Effectively turns off wheel scrolling in Linux." #~ msgstr "Slår i praksis av hjulrulling i Linux." #~ msgid "" #~ "Enable onboard profiles, which often control report rate and keyboard " #~ "lighting" #~ msgstr "" #~ "Aktiver innebygde profiler, som ofte kontrollerer rapporthastighet og " #~ "tastaturbelysning" #~ msgid "" #~ "For more information see the Solaar installation directions\n" #~ "at https://pwr-solaar.github.io/Solaar/installation" #~ msgstr "" #~ "For mer informasjon, se Solaar-installasjonsanvisningene\n" #~ "på https://pwr-solaar.github.io/Solaar/installation" #, python-format #~ msgid "" #~ "Found a Logitech Receiver (%s), but did not have permission to open it." #~ msgstr "" #~ "Fant en Logitech-mottaker (%s), men mangler rettigheter til å åpne den." #~ msgid "Frequency of device polling, in milliseconds" #~ msgstr "Frekvens på enhetsspørring, i millisekunder" #~ msgid "HID++ Scrolling" #~ msgstr "HID++ rulling" #~ msgid "HID++ Thumb Scrolling" #~ msgstr "HID++ tommelrulling" #~ msgid "HID++ mode for horizontal scroll with the thumb wheel." #~ msgstr "HID++ modus for horisontal rulling med tommelhjulet." #~ msgid "HID++ mode for vertical scroll with the wheel." #~ msgstr "HID++ modus for vertikal rulling med hjulet." #~ msgid "High Resolution Scrolling" #~ msgstr "Høyoppløselig rulling" #~ msgid "High Resolution Wheel Invert" #~ msgstr "Inverter høy rulleoppløsning" #~ msgid "High-sensitivity wheel invert direction for vertical scroll." #~ msgstr "Inverter retning for høy vertikal rulleoppløsning." #~ msgid "If the device is already turned on, turn if off and on again." #~ msgstr "Hvis enheten allerede er slått på, slå den av og på igjen." #~ msgid "" #~ "If you've just installed Solaar, try removing the receiver and plugging " #~ "it back in." #~ msgstr "" #~ "Hvis du nettopp har installert Solaar, forsøk å fjerne mottakeren og " #~ "plugg den inn igjen." #~ msgid "Invert thumb scroll direction." #~ msgstr "Inverter retning på tommelrulling." #, python-format #~ msgid "Lighting: %(level)s lux" #~ msgstr "Belysning: %(level)s lux" #~ msgid "" #~ "Make G keys send GKEY HID++ notifications (which trigger Solaar rules but " #~ "are otherwise ignored)." #~ msgstr "" #~ "Få G-taster til å sende GKEY HID++-varsler (som utløser Solaar-regler, " #~ "men som ellers ignoreres)." #~ msgid "" #~ "Make the key or button send HID++ notifications (which trigger Solaar " #~ "rules but are otherwise ignored)." #~ msgstr "" #~ "Få tasten eller knappen til å sende HID++-varsler (som utløser Solaar-" #~ "regler, men ellers ignoreres)." #~ msgid "May also make M keys and MR key send HID++ notifications" #~ msgstr "Kan også få M-taster og MR-taster til å sende HID++-varsler" #~ msgid "No Logitech device found" #~ msgstr "Finner ingen Logitech-enhet" #~ msgid "No Logitech receiver found" #~ msgstr "Ingen Logitech-mottaker funnet" #~ msgid "Only one device can be paired to this receiver." #~ msgstr "Kun én enhet kan pares med denne mottakeren." #~ msgid "Polling Rate (ms)" #~ msgstr "Spørrerate (ms)" #~ msgid "Report number of notification triggering rule processing." #~ msgstr "Rapportnummer på varsel som utløste regelbehandling." #~ msgid "Scroll Wheel Rachet" #~ msgstr "Skrallen for rullehjulet" #~ msgid "Send a gesture by sliding the mouse while holding the button down." #~ msgstr "Send en bevegelse ved å skyve musen mens du holder knappen nede." #~ msgid "" #~ "Shows status of devices connected\n" #~ "through wireless Logitech receivers." #~ msgstr "" #~ "Viser status for enheter tilkoblet\n" #~ "via trådløse Logitech-mottakere." #~ msgid "Smooth Scrolling" #~ msgstr "Myk rulling" #~ msgid "Solaar depends on a udev file that is not present" #~ msgstr "Solaar er avhengig av en udev-fil som ikke finnes" #~ msgid "The receiver only supports %d paired device(s)." #~ msgstr "Mottakeren kan kun støtte %d parede enhet(er)." #~ msgid "The receiver was unplugged." #~ msgstr "Mottakeren ble fjernet." #~ msgid "" #~ "The wireless link between this device and its receiver is not encrypted.\n" #~ "\n" #~ "For pointing devices (mice, trackballs, trackpads), this is a minor " #~ "security issue.\n" #~ "\n" #~ "It is, however, a major security issue for text-input devices (keyboards, " #~ "numpads),\n" #~ "because typed text can be sniffed inconspicuously by 3rd parties within " #~ "range." #~ msgstr "" #~ "Den trådløse koblingen mellom denne enheten og mottakeren er ukryptert.\n" #~ "\n" #~ "For pekeenheter (mus, styrekuler, styreflater) er dette en mindre " #~ "sikkerhetsproblem.\n" #~ "\n" #~ "Det er imidlertid et viktig sikkerhetsproblem for tekstinndataenheter " #~ "(tastaturer, numpads),\n" #~ "fordi maskinskrevet tekst kan bli sniffet ubemerket av tredjeparter innen " #~ "rekkevidde." #~ msgid "This receiver has %d pairing(s) remaining." #~ msgstr "Denne mottakeren har %d gjenstående paring(er)." #~ msgid "Thumb Scroll Invert" #~ msgstr "Inverter tommelrulling" #~ msgid "Top-most coordinate." #~ msgstr "Koordinat lengst oppe." #~ msgid "" #~ "Try removing the device and plugging it back in or turning it off and " #~ "then on." #~ msgstr "" #~ "Prøv å fjerne enheten og koble den til igjen eller slå den av og på." #~ msgid "Turn illumination on or off on keyboard." #~ msgstr "Slå på eller av belysning for tastaturet." #~ msgid "Turn on the device you want to pair." #~ msgstr "Slå på enheten du ønsker å pare." #~ msgid "USB id" #~ msgstr "USB-id" #~ msgid "Unexpected device number (%s) in notification %s." #~ msgstr "Uventet enhetsnummer (%s) i varsel %s." #~ msgid "Wheel Resolution" #~ msgstr "Hjuloppløsning" #~ msgid "You may have to first turn the device off and on again." #~ msgstr "Først må du kanskje slå enheten av og på igjen." #~ msgid "height" #~ msgstr "høyde" #~ msgid "last known" #~ msgstr "sist kjent" #~ msgid "next " #~ msgstr "neste " #~ msgid "none" #~ msgstr "ingen" #~ msgid "top" #~ msgstr "top" #~ msgid "unknown" #~ msgstr "ukjent" #~ msgid "width" #~ msgstr "bredde" Solaar-1.1.14/po/nl.po000066400000000000000000001536561473525213700144370ustar00rootroot00000000000000# Dutch translations for solaar package # Copyright (C) 2021 SOLAAR'S COPYRIGHT HOLDER # This file is distributed under the same license as the Solaar package. # msgid "" msgstr "Project-Id-Version: solaar 1.0.1\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-12-28 17:40+0100\n" "PO-Revision-Date: 2021-01-15 22:03+0100\n" "Last-Translator: Heimen Stoffels \n" "Language-Team: \n" "Language: nl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Poedit 2.4.2\n" #: lib/logitech_receiver/base_usb.py:46 msgid "Bolt Receiver" msgstr "" #: lib/logitech_receiver/base_usb.py:57 msgid "Unifying Receiver" msgstr "" #: lib/logitech_receiver/base_usb.py:67 lib/logitech_receiver/base_usb.py:78 #: lib/logitech_receiver/base_usb.py:90 lib/logitech_receiver/base_usb.py:102 #: lib/logitech_receiver/base_usb.py:114 msgid "Nano Receiver" msgstr "" #: lib/logitech_receiver/base_usb.py:124 msgid "Lightspeed Receiver" msgstr "" #: lib/logitech_receiver/base_usb.py:133 msgid "EX100 Receiver 27 Mhz" msgstr "" #: lib/logitech_receiver/i18n.py:30 msgid "empty" msgstr "leeg" #: lib/logitech_receiver/i18n.py:31 msgid "critical" msgstr "bijna leeg" #: lib/logitech_receiver/i18n.py:32 msgid "low" msgstr "laag" #: lib/logitech_receiver/i18n.py:33 msgid "average" msgstr "" #: lib/logitech_receiver/i18n.py:34 msgid "good" msgstr "goed" #: lib/logitech_receiver/i18n.py:35 msgid "full" msgstr "opgeladen" #: lib/logitech_receiver/i18n.py:38 msgid "discharging" msgstr "bezig met ontladen" #: lib/logitech_receiver/i18n.py:39 msgid "recharging" msgstr "bezig met opladen" #: lib/logitech_receiver/i18n.py:40 lib/solaar/ui/window.py:711 msgid "charging" msgstr "bezig met opladen" #: lib/logitech_receiver/i18n.py:41 msgid "not charging" msgstr "" #: lib/logitech_receiver/i18n.py:42 msgid "almost full" msgstr "bijna opgeladen" #: lib/logitech_receiver/i18n.py:43 msgid "charged" msgstr "opgeladen" #: lib/logitech_receiver/i18n.py:44 msgid "slow recharge" msgstr "langzaam opladen" #: lib/logitech_receiver/i18n.py:45 msgid "invalid battery" msgstr "verkeerde batterij" #: lib/logitech_receiver/i18n.py:46 msgid "thermal error" msgstr "temperatuurfout" #: lib/logitech_receiver/i18n.py:47 msgid "error" msgstr "" #: lib/logitech_receiver/i18n.py:48 msgid "standard" msgstr "" #: lib/logitech_receiver/i18n.py:49 msgid "fast" msgstr "" #: lib/logitech_receiver/i18n.py:50 msgid "slow" msgstr "" #: lib/logitech_receiver/i18n.py:53 msgid "device timeout" msgstr "time-out opgetreden" #: lib/logitech_receiver/i18n.py:54 msgid "device not supported" msgstr "apparaat wordt niet ondersteund" #: lib/logitech_receiver/i18n.py:55 msgid "too many devices" msgstr "te veel apparaten" #: lib/logitech_receiver/i18n.py:56 msgid "sequence timeout" msgstr "meerdere time-outs" #: lib/logitech_receiver/i18n.py:59 lib/solaar/ui/window.py:572 msgid "Firmware" msgstr "Firmware" #: lib/logitech_receiver/i18n.py:60 msgid "Bootloader" msgstr "Opstartlader" #: lib/logitech_receiver/i18n.py:61 msgid "Hardware" msgstr "Hardware" #: lib/logitech_receiver/i18n.py:62 msgid "Other" msgstr "Overig" #: lib/logitech_receiver/i18n.py:65 msgid "Left Button" msgstr "" #: lib/logitech_receiver/i18n.py:66 msgid "Right Button" msgstr "" #: lib/logitech_receiver/i18n.py:67 msgid "Middle Button" msgstr "" #: lib/logitech_receiver/i18n.py:68 msgid "Back Button" msgstr "" #: lib/logitech_receiver/i18n.py:69 msgid "Forward Button" msgstr "" #: lib/logitech_receiver/i18n.py:70 msgid "Mouse Gesture Button" msgstr "" #: lib/logitech_receiver/i18n.py:71 msgid "Smart Shift" msgstr "Slim schakelen" #: lib/logitech_receiver/i18n.py:72 msgid "DPI Switch" msgstr "" #: lib/logitech_receiver/i18n.py:73 msgid "Left Tilt" msgstr "" #: lib/logitech_receiver/i18n.py:74 msgid "Right Tilt" msgstr "" #: lib/logitech_receiver/i18n.py:75 msgid "Left Click" msgstr "" #: lib/logitech_receiver/i18n.py:76 msgid "Right Click" msgstr "" #: lib/logitech_receiver/i18n.py:77 msgid "Mouse Middle Button" msgstr "" #: lib/logitech_receiver/i18n.py:78 msgid "Mouse Back Button" msgstr "" #: lib/logitech_receiver/i18n.py:79 msgid "Mouse Forward Button" msgstr "" #: lib/logitech_receiver/i18n.py:80 msgid "Gesture Button Navigation" msgstr "" #: lib/logitech_receiver/i18n.py:81 msgid "Mouse Scroll Left Button" msgstr "" #: lib/logitech_receiver/i18n.py:82 msgid "Mouse Scroll Right Button" msgstr "" #: lib/logitech_receiver/i18n.py:85 msgid "pressed" msgstr "" #: lib/logitech_receiver/i18n.py:86 msgid "released" msgstr "" #: lib/logitech_receiver/notifications.py:75 #: lib/logitech_receiver/notifications.py:126 msgid "pairing lock is closed" msgstr "koppelvergendeling ingeschakeld" #: lib/logitech_receiver/notifications.py:75 #: lib/logitech_receiver/notifications.py:126 msgid "pairing lock is open" msgstr "koppelvergendeling uitgeschakeld" #: lib/logitech_receiver/notifications.py:92 msgid "discovery lock is closed" msgstr "" #: lib/logitech_receiver/notifications.py:92 msgid "discovery lock is open" msgstr "" #: lib/logitech_receiver/notifications.py:224 lib/solaar/ui/notify.py:122 msgid "connected" msgstr "verbonden" #: lib/logitech_receiver/notifications.py:224 msgid "disconnected" msgstr "verbinding verbroken" #: lib/logitech_receiver/notifications.py:262 lib/solaar/ui/notify.py:120 msgid "unpaired" msgstr "niet gekoppeld" #: lib/logitech_receiver/notifications.py:304 msgid "powered on" msgstr "ingeschakeld" #: lib/logitech_receiver/settings.py:750 msgid "register" msgstr "" #: lib/logitech_receiver/settings.py:764 lib/logitech_receiver/settings.py:791 msgid "feature" msgstr "" #: lib/logitech_receiver/settings_templates.py:139 msgid "Swap Fx function" msgstr "Fx-functies omdraaien" #: lib/logitech_receiver/settings_templates.py:140 msgid "When set, the F1..F12 keys will activate their special function,\n" "and you must hold the FN key to activate their standard function." msgstr "Schakel dit in om de speciale functies van F1-F12 te gebruiken -\n" "de standaard functies kunnen worden geactiveerd met Fn + F1-F12." #: lib/logitech_receiver/settings_templates.py:142 msgid "When unset, the F1..F12 keys will activate their standard function,\n" "and you must hold the FN key to activate their special function." msgstr "Schakel dit in om de standaard functies van F1-F12 te gebruiken -\n" "de speciale functies kunnen worden geactiveerd met Fn + F1-F12." #: lib/logitech_receiver/settings_templates.py:149 msgid "Hand Detection" msgstr "Handpalmdetectie" #: lib/logitech_receiver/settings_templates.py:150 msgid "Turn on illumination when the hands hover over the keyboard." msgstr "Schakelt de verlichting in als u beide handen boven het toetsenbord " "houdt." #: lib/logitech_receiver/settings_templates.py:157 msgid "Scroll Wheel Smooth Scrolling" msgstr "Vloeiend scrollen" #: lib/logitech_receiver/settings_templates.py:158 #: lib/logitech_receiver/settings_templates.py:239 #: lib/logitech_receiver/settings_templates.py:267 msgid "High-sensitivity mode for vertical scroll with the wheel." msgstr "Hogegevoeligheidsmodus voor verticaal scrollen." #: lib/logitech_receiver/settings_templates.py:165 msgid "Side Scrolling" msgstr "Zijwaarts scrollen" #: lib/logitech_receiver/settings_templates.py:167 msgid "When disabled, pushing the wheel sideways sends custom button " "events\n" "instead of the standard side-scrolling events." msgstr "Schakel dit uit om aangepaste knopgebeurtenissen te versturen als u " "het scrollwiel\n" "opzijduwt in plaats van de standaard gebeurtenissen." #: lib/logitech_receiver/settings_templates.py:177 msgid "Sensitivity (DPI - older mice)" msgstr "" #: lib/logitech_receiver/settings_templates.py:178 #: lib/logitech_receiver/settings_templates.py:712 msgid "Mouse movement sensitivity" msgstr "Muisgevoeligheid" #: lib/logitech_receiver/settings_templates.py:208 #: lib/logitech_receiver/settings_templates.py:218 #: lib/logitech_receiver/settings_templates.py:225 msgid "Backlight" msgstr "Achtergrondverlichting" #: lib/logitech_receiver/settings_templates.py:209 #: lib/logitech_receiver/settings_templates.py:226 msgid "Set illumination time for keyboard." msgstr "" #: lib/logitech_receiver/settings_templates.py:219 msgid "Turn illumination on or off on keyboard." msgstr "Schakel de toetsenbordverlichting in of uit." #: lib/logitech_receiver/settings_templates.py:237 msgid "Scroll Wheel High Resolution" msgstr "Scrollen met hoge resolutie" #: lib/logitech_receiver/settings_templates.py:240 #: lib/logitech_receiver/settings_templates.py:268 msgid "Set to ignore if scrolling is abnormally fast or slow" msgstr "" #: lib/logitech_receiver/settings_templates.py:247 #: lib/logitech_receiver/settings_templates.py:277 msgid "Scroll Wheel Diversion" msgstr "" #: lib/logitech_receiver/settings_templates.py:249 msgid "Make scroll wheel send LOWRES_WHEEL HID++ notifications (which " "trigger Solaar rules but are otherwise ignored)." msgstr "" #: lib/logitech_receiver/settings_templates.py:256 msgid "Scroll Wheel Direction" msgstr "Scrollrichting" #: lib/logitech_receiver/settings_templates.py:257 msgid "Invert direction for vertical scroll with wheel." msgstr "Keer de modus van verticaal scrollen met het scrollwiel om." #: lib/logitech_receiver/settings_templates.py:265 msgid "Scroll Wheel Resolution" msgstr "Scrollresolutie" #: lib/logitech_receiver/settings_templates.py:279 msgid "Make scroll wheel send HIRES_WHEEL HID++ notifications (which " "trigger Solaar rules but are otherwise ignored)." msgstr "" #: lib/logitech_receiver/settings_templates.py:288 msgid "Sensitivity (Pointer Speed)" msgstr "Gevoeligheid (cursorsnelheid)" #: lib/logitech_receiver/settings_templates.py:289 msgid "Speed multiplier for mouse (256 is normal multiplier)." msgstr "Muisversnelling (256 is de standaard versnelling)." #: lib/logitech_receiver/settings_templates.py:299 msgid "Thumb Wheel Diversion" msgstr "" #: lib/logitech_receiver/settings_templates.py:301 msgid "Make thumb wheel send THUMB_WHEEL HID++ notifications (which trigger " "Solaar rules but are otherwise ignored)." msgstr "" #: lib/logitech_receiver/settings_templates.py:310 msgid "Thumb Wheel Direction" msgstr "Duimwielrichting" #: lib/logitech_receiver/settings_templates.py:311 msgid "Invert thumb wheel scroll direction." msgstr "Keer de scrollrichting van het duimwiel om." #: lib/logitech_receiver/settings_templates.py:319 msgid "Onboard Profiles" msgstr "" #: lib/logitech_receiver/settings_templates.py:320 msgid "Enable onboard profiles, which often control report rate and " "keyboard lighting" msgstr "" #: lib/logitech_receiver/settings_templates.py:330 msgid "Polling Rate (ms)" msgstr "" #: lib/logitech_receiver/settings_templates.py:332 msgid "Frequency of device polling, in milliseconds" msgstr "" #: lib/logitech_receiver/settings_templates.py:333 #: lib/logitech_receiver/settings_templates.py:1046 #: lib/logitech_receiver/settings_templates.py:1076 msgid "May need Onboard Profiles set to Disable to be effective." msgstr "" #: lib/logitech_receiver/settings_templates.py:365 msgid "Divert crown events" msgstr "Kroongebeurtenissen omleiden" #: lib/logitech_receiver/settings_templates.py:366 msgid "Make crown send CROWN HID++ notifications (which trigger Solaar " "rules but are otherwise ignored)." msgstr "Laat de kroon een HID++-melding versturen (welke Solaar's regels " "volgen, maar verder genegeerd worden)." #: lib/logitech_receiver/settings_templates.py:374 msgid "Crown smooth scroll" msgstr "" #: lib/logitech_receiver/settings_templates.py:375 msgid "Set crown smooth scroll" msgstr "" #: lib/logitech_receiver/settings_templates.py:383 msgid "Divert G Keys" msgstr "G-toetsen omleiden" #: lib/logitech_receiver/settings_templates.py:385 msgid "Make G keys send GKEY HID++ notifications (which trigger Solaar " "rules but are otherwise ignored)." msgstr "Laat de G-toetsen een HID++-melding versturen (welke Solaar's regels " "volgen, maar verder genegeerd worden)." #: lib/logitech_receiver/settings_templates.py:386 msgid "May also make M keys and MR key send HID++ notifications" msgstr "" #: lib/logitech_receiver/settings_templates.py:402 msgid "Scroll Wheel Ratcheted" msgstr "" #: lib/logitech_receiver/settings_templates.py:403 msgid "Switch the mouse wheel between speed-controlled ratcheting and " "always freespin." msgstr "" #: lib/logitech_receiver/settings_templates.py:405 msgid "Freespinning" msgstr "" #: lib/logitech_receiver/settings_templates.py:405 msgid "Ratcheted" msgstr "" #: lib/logitech_receiver/settings_templates.py:412 msgid "Scroll Wheel Ratchet Speed" msgstr "" #: lib/logitech_receiver/settings_templates.py:414 msgid "Use the mouse wheel speed to switch between ratcheted and " "freespinning.\n" "The mouse wheel is always ratcheted at 50." msgstr "" #: lib/logitech_receiver/settings_templates.py:463 msgid "Key/Button Actions" msgstr "Toets-/Knopacties" #: lib/logitech_receiver/settings_templates.py:465 msgid "Change the action for the key or button." msgstr "Pas de actie van een toets of knop aan." #: lib/logitech_receiver/settings_templates.py:465 msgid "Overridden by diversion." msgstr "" #: lib/logitech_receiver/settings_templates.py:466 msgid "Changing important actions (such as for the left mouse button) can " "result in an unusable system." msgstr "Let op: het aanpassen van belangrijke acties (zoals die van de " "linkermuisknop) kan leiden tot een onbruikbaar systeem." #: lib/logitech_receiver/settings_templates.py:639 msgid "Key/Button Diversion" msgstr "Toets-/Knopomleiding" #: lib/logitech_receiver/settings_templates.py:640 msgid "Make the key or button send HID++ notifications (Diverted) or " "initiate Mouse Gestures or Sliding DPI" msgstr "" #: lib/logitech_receiver/settings_templates.py:643 #: lib/logitech_receiver/settings_templates.py:644 #: lib/logitech_receiver/settings_templates.py:645 msgid "Diverted" msgstr "" #: lib/logitech_receiver/settings_templates.py:643 #: lib/logitech_receiver/settings_templates.py:644 msgid "Mouse Gestures" msgstr "" #: lib/logitech_receiver/settings_templates.py:643 #: lib/logitech_receiver/settings_templates.py:644 #: lib/logitech_receiver/settings_templates.py:645 msgid "Regular" msgstr "" #: lib/logitech_receiver/settings_templates.py:643 msgid "Sliding DPI" msgstr "" #: lib/logitech_receiver/settings_templates.py:711 msgid "Sensitivity (DPI)" msgstr "Gevoeligheid (DPI)" #: lib/logitech_receiver/settings_templates.py:752 msgid "Sensitivity Switching" msgstr "" #: lib/logitech_receiver/settings_templates.py:754 msgid "Switch the current sensitivity and the remembered sensitivity when " "the key or button is pressed.\n" "If there is no remembered sensitivity, just remember the current " "sensitivity" msgstr "" #: lib/logitech_receiver/settings_templates.py:758 msgid "Off" msgstr "" #: lib/logitech_receiver/settings_templates.py:791 msgid "Disable keys" msgstr "Toetsen uitschakelen" #: lib/logitech_receiver/settings_templates.py:792 msgid "Disable specific keyboard keys." msgstr "Schakel specifieke toetsen op het toetsenbord uit." #: lib/logitech_receiver/settings_templates.py:795 #, python-format msgid "Disables the %s key." msgstr "Schakelt de %s-toets uit." #: lib/logitech_receiver/settings_templates.py:809 #: lib/logitech_receiver/settings_templates.py:860 msgid "Set OS" msgstr "Besturingssysteem instellen" #: lib/logitech_receiver/settings_templates.py:810 #: lib/logitech_receiver/settings_templates.py:861 msgid "Change keys to match OS." msgstr "Pas de toetsen aan aan het besturingssysteem." #: lib/logitech_receiver/settings_templates.py:873 msgid "Change Host" msgstr "Host veranderen" #: lib/logitech_receiver/settings_templates.py:874 msgid "Switch connection to a different host" msgstr "Schakel over naar een andere host." #: lib/logitech_receiver/settings_templates.py:900 msgid "Performs a left click." msgstr "Voert een linkermuisklik uit." #: lib/logitech_receiver/settings_templates.py:900 msgid "Single tap" msgstr "Eenmaal tikken" #: lib/logitech_receiver/settings_templates.py:901 msgid "Performs a right click." msgstr "Voert een rechtermuisklik uit." #: lib/logitech_receiver/settings_templates.py:901 msgid "Single tap with two fingers" msgstr "Eenmaal tikken met twee vingers" #: lib/logitech_receiver/settings_templates.py:902 msgid "Single tap with three fingers" msgstr "Eenmaal tikken met drie vingers" #: lib/logitech_receiver/settings_templates.py:906 msgid "Double tap" msgstr "Dubbeltikken" #: lib/logitech_receiver/settings_templates.py:906 msgid "Performs a double click." msgstr "Voert een dubbele klik uit." #: lib/logitech_receiver/settings_templates.py:907 msgid "Double tap with two fingers" msgstr "Dubbeltikken met twee vingers" #: lib/logitech_receiver/settings_templates.py:908 msgid "Double tap with three fingers" msgstr "Dubbeltikken met drie vingers" #: lib/logitech_receiver/settings_templates.py:911 msgid "Drags items by dragging the finger after double tapping." msgstr "Verplaats items door na het dubbeltikken uw vinger te verplaatsen." #: lib/logitech_receiver/settings_templates.py:911 msgid "Tap and drag" msgstr "Tikken-en-slepen" #: lib/logitech_receiver/settings_templates.py:913 msgid "Drags items by dragging the fingers after double tapping." msgstr "Verplaats items door na het dubbeltikken uw vingers te verplaatsen." #: lib/logitech_receiver/settings_templates.py:913 msgid "Tap and drag with two fingers" msgstr "Tikken-en-slepen met twee vingers" #: lib/logitech_receiver/settings_templates.py:914 msgid "Tap and drag with three fingers" msgstr "Tikken-en-slepen met drie vingers" #: lib/logitech_receiver/settings_templates.py:917 msgid "Disables tap and edge gestures (equivalent to pressing Fn+LeftClick)." msgstr "Schakel tik- en randgebaren uit (gelijk aan Fn+linkermuisklik)." #: lib/logitech_receiver/settings_templates.py:917 msgid "Suppress tap and edge gestures" msgstr "Tik- en randgebaren uitschakelen" #: lib/logitech_receiver/settings_templates.py:918 msgid "Scroll with one finger" msgstr "Scrollen met één vinger" #: lib/logitech_receiver/settings_templates.py:918 #: lib/logitech_receiver/settings_templates.py:919 #: lib/logitech_receiver/settings_templates.py:922 msgid "Scrolls." msgstr "Scrollt." #: lib/logitech_receiver/settings_templates.py:919 #: lib/logitech_receiver/settings_templates.py:922 msgid "Scroll with two fingers" msgstr "Scrollen met twee vingers" #: lib/logitech_receiver/settings_templates.py:920 msgid "Scroll horizontally with two fingers" msgstr "Horizontaal scrollen met twee vingers" #: lib/logitech_receiver/settings_templates.py:920 msgid "Scrolls horizontally." msgstr "Scrollt horizontaal." #: lib/logitech_receiver/settings_templates.py:921 msgid "Scroll vertically with two fingers" msgstr "Verticaal scrollen met twee vingers" #: lib/logitech_receiver/settings_templates.py:921 msgid "Scrolls vertically." msgstr "Scrollt verticaal." #: lib/logitech_receiver/settings_templates.py:923 msgid "Inverts the scrolling direction." msgstr "Draai de scrollrichting om." #: lib/logitech_receiver/settings_templates.py:923 msgid "Natural scrolling" msgstr "Natuurlijk scrollen" #: lib/logitech_receiver/settings_templates.py:924 msgid "Enables the thumbwheel." msgstr "Schakel het duimwiel in." #: lib/logitech_receiver/settings_templates.py:924 msgid "Thumbwheel" msgstr "Duimwiel" #: lib/logitech_receiver/settings_templates.py:935 #: lib/logitech_receiver/settings_templates.py:939 msgid "Swipe from the top edge" msgstr "Vegen vanaf bovenkant" #: lib/logitech_receiver/settings_templates.py:936 msgid "Swipe from the left edge" msgstr "Vegen vanaf linkerkant" #: lib/logitech_receiver/settings_templates.py:937 msgid "Swipe from the right edge" msgstr "Vegen vanaf rechterkant" #: lib/logitech_receiver/settings_templates.py:938 msgid "Swipe from the bottom edge" msgstr "Vegen vanaf onderkant" #: lib/logitech_receiver/settings_templates.py:940 msgid "Swipe two fingers from the left edge" msgstr "Vegen vanaf linkerkant (twee vingers)" #: lib/logitech_receiver/settings_templates.py:941 msgid "Swipe two fingers from the right edge" msgstr "Vegen vanaf rechterkant (twee vingers)" #: lib/logitech_receiver/settings_templates.py:942 msgid "Swipe two fingers from the bottom edge" msgstr "Vegen vanaf onderkant (twee vingers)" #: lib/logitech_receiver/settings_templates.py:943 msgid "Swipe two fingers from the top edge" msgstr "Vegen vanaf bovenkant (twee vingers)" #: lib/logitech_receiver/settings_templates.py:944 #: lib/logitech_receiver/settings_templates.py:948 msgid "Pinch to zoom out; spread to zoom in." msgstr "Knijp om uit te zoomen; spreid om in te zoomen." #: lib/logitech_receiver/settings_templates.py:944 msgid "Zoom with two fingers." msgstr "Zoomen met twee vingers." #: lib/logitech_receiver/settings_templates.py:945 msgid "Pinch to zoom out." msgstr "Knijp om uit te zoomen." #: lib/logitech_receiver/settings_templates.py:946 msgid "Spread to zoom in." msgstr "Spreid om in te zoomen." #: lib/logitech_receiver/settings_templates.py:947 msgid "Zoom with three fingers." msgstr "Zoomen met drie vingers." #: lib/logitech_receiver/settings_templates.py:948 msgid "Zoom with two fingers" msgstr "Zoomen met twee vingers" #: lib/logitech_receiver/settings_templates.py:966 msgid "Pixel zone" msgstr "" #: lib/logitech_receiver/settings_templates.py:967 msgid "Ratio zone" msgstr "" #: lib/logitech_receiver/settings_templates.py:968 msgid "Scale factor" msgstr "Schaalfactor" #: lib/logitech_receiver/settings_templates.py:968 msgid "Sets the cursor speed." msgstr "" #: lib/logitech_receiver/settings_templates.py:972 msgid "Left" msgstr "Links" #: lib/logitech_receiver/settings_templates.py:972 msgid "Left-most coordinate." msgstr "Het meest linker coördinaat." #: lib/logitech_receiver/settings_templates.py:973 msgid "Bottom" msgstr "" #: lib/logitech_receiver/settings_templates.py:973 msgid "Bottom coordinate." msgstr "" #: lib/logitech_receiver/settings_templates.py:974 msgid "Width" msgstr "Breedte" #: lib/logitech_receiver/settings_templates.py:974 msgid "Width." msgstr "Breedte." #: lib/logitech_receiver/settings_templates.py:975 msgid "Height" msgstr "Hoogte" #: lib/logitech_receiver/settings_templates.py:975 msgid "Height." msgstr "Hoogte." #: lib/logitech_receiver/settings_templates.py:976 msgid "Cursor speed." msgstr "Cursorsnelheid." #: lib/logitech_receiver/settings_templates.py:976 msgid "Scale" msgstr "Schaal" #: lib/logitech_receiver/settings_templates.py:982 msgid "Gestures" msgstr "Gebaren" #: lib/logitech_receiver/settings_templates.py:983 msgid "Tweak the mouse/touchpad behaviour." msgstr "Pas het gedrag van het trackpad/de muis aan." #: lib/logitech_receiver/settings_templates.py:1000 msgid "Gestures Diversion" msgstr "" #: lib/logitech_receiver/settings_templates.py:1001 msgid "Divert mouse/touchpad gestures." msgstr "" #: lib/logitech_receiver/settings_templates.py:1018 msgid "Gesture params" msgstr "Gebarenwaarden" #: lib/logitech_receiver/settings_templates.py:1019 msgid "Change numerical parameters of a mouse/touchpad." msgstr "Pas de numerieke waarden van het trackpad/de muis aan." #: lib/logitech_receiver/settings_templates.py:1044 msgid "M-Key LEDs" msgstr "" #: lib/logitech_receiver/settings_templates.py:1046 msgid "Control the M-Key LEDs." msgstr "" #: lib/logitech_receiver/settings_templates.py:1047 #: lib/logitech_receiver/settings_templates.py:1077 msgid "May need G Keys diverted to be effective." msgstr "" #: lib/logitech_receiver/settings_templates.py:1053 #, python-format msgid "Lights up the %s key." msgstr "" #: lib/logitech_receiver/settings_templates.py:1074 msgid "MR-Key LED" msgstr "" #: lib/logitech_receiver/settings_templates.py:1076 msgid "Control the MR-Key LED." msgstr "" #: lib/logitech_receiver/settings_templates.py:1095 msgid "Persistent Key/Button Mapping" msgstr "" #: lib/logitech_receiver/settings_templates.py:1097 msgid "Permanently change the mapping for the key or button." msgstr "" #: lib/logitech_receiver/settings_templates.py:1098 msgid "Changing important keys or buttons (such as for the left mouse " "button) can result in an unusable system." msgstr "" #: lib/logitech_receiver/settings_templates.py:1157 msgid "Sidetone" msgstr "" #: lib/logitech_receiver/settings_templates.py:1158 msgid "Set sidetone level." msgstr "" #: lib/logitech_receiver/settings_templates.py:1167 msgid "Equalizer" msgstr "" #: lib/logitech_receiver/settings_templates.py:1168 msgid "Set equalizer levels." msgstr "" #: lib/logitech_receiver/settings_templates.py:1191 msgid "Hz" msgstr "" #: lib/logitech_receiver/settings_templates.py:1197 msgid "Power Management" msgstr "" #: lib/logitech_receiver/settings_templates.py:1198 msgid "Power off in minutes (0 for never)." msgstr "" #: lib/logitech_receiver/status.py:114 msgid "No paired devices." msgstr "Geen apparaten gekoppeld." #: lib/logitech_receiver/status.py:115 lib/solaar/ui/window.py:622 #, python-format msgid "%(count)s paired device." msgid_plural "%(count)s paired devices." msgstr[0] "%(count)s gekoppeld apparaat." msgstr[1] "%(count)s gekoppelde apparaten." #: lib/logitech_receiver/status.py:170 #, python-format msgid "Battery: %(level)s" msgstr "Batterijniveau: %(level)s" #: lib/logitech_receiver/status.py:172 #, python-format msgid "Battery: %(percent)d%%" msgstr "Batterijniveau: %(percent)d%%" #: lib/logitech_receiver/status.py:184 #, python-format msgid "Lighting: %(level)s lux" msgstr "Verlichting: %(level)s lux" #: lib/logitech_receiver/status.py:239 #, python-format msgid "Battery: %(level)s (%(status)s)" msgstr "Batterijniveau: %(level)s (%(status)s)" #: lib/logitech_receiver/status.py:241 #, python-format msgid "Battery: %(percent)d%% (%(status)s)" msgstr "Batterijniveau: %(percent)d%% (%(status)s)" #: lib/solaar/ui/__init__.py:52 msgid "Permissions error" msgstr "Machtigingsfout" #: lib/solaar/ui/__init__.py:54 #, python-format msgid "Found a Logitech receiver or device (%s), but did not have " "permission to open it." msgstr "" #: lib/solaar/ui/__init__.py:55 msgid "If you've just installed Solaar, try disconnecting the receiver or " "device and then reconnecting it." msgstr "" #: lib/solaar/ui/__init__.py:58 msgid "Cannot connect to device error" msgstr "" #: lib/solaar/ui/__init__.py:60 #, python-format msgid "Found a Logitech receiver or device at %s, but encountered an error " "connecting to it." msgstr "" #: lib/solaar/ui/__init__.py:61 msgid "Try disconnecting the device and then reconnecting it or turning it " "off and then on." msgstr "" #: lib/solaar/ui/__init__.py:64 msgid "Unpairing failed" msgstr "Loskoppelen mislukt" #: lib/solaar/ui/__init__.py:66 #, python-brace-format msgid "Failed to unpair %{device} from %{receiver}." msgstr "'%{device}' kan niet worden losgekoppeld van '%{receiver}'." #: lib/solaar/ui/__init__.py:67 msgid "The receiver returned an error, with no further details." msgstr "Er is een fout opgetreden zonder nadere informatie." #: lib/solaar/ui/__init__.py:177 msgid "Another Solaar process is already running so just expose its window" msgstr "" #: lib/solaar/ui/about.py:36 msgid "Manages Logitech receivers,\n" "keyboards, mice, and tablets." msgstr "" #: lib/solaar/ui/about.py:44 msgid "Additional Programming" msgstr "" #: lib/solaar/ui/about.py:45 msgid "GUI design" msgstr "Toepassingsontwerp" #: lib/solaar/ui/about.py:47 msgid "Testing" msgstr "Testen" #: lib/solaar/ui/about.py:54 msgid "Logitech documentation" msgstr "Logitech-documentatie" #: lib/solaar/ui/action.py:85 lib/solaar/ui/action.py:89 #: lib/solaar/ui/window.py:197 msgid "Unpair" msgstr "Loskoppelen" #: lib/solaar/ui/action.py:88 lib/solaar/ui/diversion_rules.py:150 msgid "Cancel" msgstr "" #: lib/solaar/ui/config_panel.py:212 msgid "Complete - ENTER to change" msgstr "" #: lib/solaar/ui/config_panel.py:212 msgid "Incomplete" msgstr "" #: lib/solaar/ui/config_panel.py:455 lib/solaar/ui/config_panel.py:507 #, python-format msgid "%d value" msgid_plural "%d values" msgstr[0] "%d waarde" msgstr[1] "%d waarden" #: lib/solaar/ui/config_panel.py:518 msgid "Changes allowed" msgstr "" #: lib/solaar/ui/config_panel.py:519 msgid "No changes allowed" msgstr "" #: lib/solaar/ui/config_panel.py:520 msgid "Ignore this setting" msgstr "" #: lib/solaar/ui/config_panel.py:565 msgid "Working" msgstr "Aan het werk" #: lib/solaar/ui/config_panel.py:568 msgid "Read/write operation failed." msgstr "Uitlezen/Wegschrijven mislukt." #: lib/solaar/ui/diversion_rules.py:65 msgid "Built-in rules" msgstr "Meegeleverde regels" #: lib/solaar/ui/diversion_rules.py:65 msgid "User-defined rules" msgstr "Eigen regels" #: lib/solaar/ui/diversion_rules.py:67 lib/solaar/ui/diversion_rules.py:1083 msgid "Rule" msgstr "Regel" #: lib/solaar/ui/diversion_rules.py:68 lib/solaar/ui/diversion_rules.py:509 #: lib/solaar/ui/diversion_rules.py:636 msgid "Sub-rule" msgstr "Aanvullende regel" #: lib/solaar/ui/diversion_rules.py:70 msgid "[empty]" msgstr "[leeg]" #: lib/solaar/ui/diversion_rules.py:94 msgid "Solaar Rule Editor" msgstr "Solaar-regelbewerker" #: lib/solaar/ui/diversion_rules.py:141 msgid "Make changes permanent?" msgstr "Wilt u de wijzigingen permanent opslaan?" #: lib/solaar/ui/diversion_rules.py:146 msgid "Yes" msgstr "Ja" #: lib/solaar/ui/diversion_rules.py:148 msgid "No" msgstr "" #: lib/solaar/ui/diversion_rules.py:153 msgid "If you choose No, changes will be lost when Solaar is closed." msgstr "Als u 'Nee' kiest, dan worden ze verworpen nadat Solaar is " "afgesloten." #: lib/solaar/ui/diversion_rules.py:201 msgid "Save changes" msgstr "" #: lib/solaar/ui/diversion_rules.py:206 msgid "Discard changes" msgstr "" #: lib/solaar/ui/diversion_rules.py:372 msgid "Insert here" msgstr "Hier invoegen" #: lib/solaar/ui/diversion_rules.py:374 msgid "Insert above" msgstr "Hierboven invoegen" #: lib/solaar/ui/diversion_rules.py:376 msgid "Insert below" msgstr "Hieronder invoegen" #: lib/solaar/ui/diversion_rules.py:382 msgid "Insert new rule here" msgstr "Nieuwe regel hier invoegen" #: lib/solaar/ui/diversion_rules.py:384 msgid "Insert new rule above" msgstr "Nieuwe regel hierboven invoegen" #: lib/solaar/ui/diversion_rules.py:386 msgid "Insert new rule below" msgstr "Nieuwe regel hieronder invoegen" #: lib/solaar/ui/diversion_rules.py:427 msgid "Paste here" msgstr "Hier plakken" #: lib/solaar/ui/diversion_rules.py:429 msgid "Paste above" msgstr "Hierboven plakken" #: lib/solaar/ui/diversion_rules.py:431 msgid "Paste below" msgstr "Hieronder plakken" #: lib/solaar/ui/diversion_rules.py:437 msgid "Paste rule here" msgstr "Regel hier plakken" #: lib/solaar/ui/diversion_rules.py:439 msgid "Paste rule above" msgstr "Regel hierboven plakken" #: lib/solaar/ui/diversion_rules.py:441 msgid "Paste rule below" msgstr "Regel hieronder plakken" #: lib/solaar/ui/diversion_rules.py:445 msgid "Paste rule" msgstr "Regel plakken" #: lib/solaar/ui/diversion_rules.py:474 msgid "Flatten" msgstr "Vlakmaken" #: lib/solaar/ui/diversion_rules.py:507 msgid "Insert" msgstr "Invoegen" #: lib/solaar/ui/diversion_rules.py:510 lib/solaar/ui/diversion_rules.py:638 #: lib/solaar/ui/diversion_rules.py:1126 msgid "Or" msgstr "Of" #: lib/solaar/ui/diversion_rules.py:511 lib/solaar/ui/diversion_rules.py:637 #: lib/solaar/ui/diversion_rules.py:1111 msgid "And" msgstr "En" #: lib/solaar/ui/diversion_rules.py:513 msgid "Condition" msgstr "Omstandigheid" #: lib/solaar/ui/diversion_rules.py:515 lib/solaar/ui/diversion_rules.py:1292 msgid "Feature" msgstr "Functie" #: lib/solaar/ui/diversion_rules.py:516 lib/solaar/ui/diversion_rules.py:1328 msgid "Report" msgstr "Melden" #: lib/solaar/ui/diversion_rules.py:517 lib/solaar/ui/diversion_rules.py:1204 msgid "Process" msgstr "Proces" #: lib/solaar/ui/diversion_rules.py:518 msgid "Mouse process" msgstr "" #: lib/solaar/ui/diversion_rules.py:519 lib/solaar/ui/diversion_rules.py:1366 msgid "Modifiers" msgstr "Sneltoetsen" #: lib/solaar/ui/diversion_rules.py:520 lib/solaar/ui/diversion_rules.py:1419 msgid "Key" msgstr "Toets" #: lib/solaar/ui/diversion_rules.py:521 lib/solaar/ui/diversion_rules.py:1461 msgid "KeyIsDown" msgstr "" #: lib/solaar/ui/diversion_rules.py:522 lib/solaar/ui/diversion_rules.py:2260 msgid "Active" msgstr "Actief" #: lib/solaar/ui/diversion_rules.py:523 lib/solaar/ui/diversion_rules.py:2218 #: lib/solaar/ui/diversion_rules.py:2270 lib/solaar/ui/diversion_rules.py:2323 msgid "Device" msgstr "" #: lib/solaar/ui/diversion_rules.py:524 lib/solaar/ui/diversion_rules.py:2297 msgid "Host" msgstr "" #: lib/solaar/ui/diversion_rules.py:525 lib/solaar/ui/diversion_rules.py:2339 msgid "Setting" msgstr "" #: lib/solaar/ui/diversion_rules.py:526 lib/solaar/ui/diversion_rules.py:1477 #: lib/solaar/ui/diversion_rules.py:1526 msgid "Test" msgstr "Testen" #: lib/solaar/ui/diversion_rules.py:527 lib/solaar/ui/diversion_rules.py:1643 msgid "Test bytes" msgstr "" #: lib/solaar/ui/diversion_rules.py:528 lib/solaar/ui/diversion_rules.py:1736 msgid "Mouse Gesture" msgstr "" #: lib/solaar/ui/diversion_rules.py:532 msgid "Action" msgstr "Actie" #: lib/solaar/ui/diversion_rules.py:534 lib/solaar/ui/diversion_rules.py:1845 msgid "Key press" msgstr "Toetsaanslag" #: lib/solaar/ui/diversion_rules.py:535 lib/solaar/ui/diversion_rules.py:1897 msgid "Mouse scroll" msgstr "Scrollbeweging" #: lib/solaar/ui/diversion_rules.py:536 lib/solaar/ui/diversion_rules.py:1959 msgid "Mouse click" msgstr "Muisklik" #: lib/solaar/ui/diversion_rules.py:537 msgid "Set" msgstr "" #: lib/solaar/ui/diversion_rules.py:538 lib/solaar/ui/diversion_rules.py:2030 msgid "Execute" msgstr "Uitvoeren" #: lib/solaar/ui/diversion_rules.py:539 lib/solaar/ui/diversion_rules.py:1158 msgid "Later" msgstr "" #: lib/solaar/ui/diversion_rules.py:568 msgid "Insert new rule" msgstr "Nieuwe regel invoegen" #: lib/solaar/ui/diversion_rules.py:588 lib/solaar/ui/diversion_rules.py:1686 #: lib/solaar/ui/diversion_rules.py:1790 lib/solaar/ui/diversion_rules.py:1989 msgid "Delete" msgstr "Verwijderen" #: lib/solaar/ui/diversion_rules.py:610 msgid "Negate" msgstr "Negeren" #: lib/solaar/ui/diversion_rules.py:634 msgid "Wrap with" msgstr "Omslaan met" #: lib/solaar/ui/diversion_rules.py:656 msgid "Cut" msgstr "Knippen" #: lib/solaar/ui/diversion_rules.py:671 msgid "Paste" msgstr "Plakken" #: lib/solaar/ui/diversion_rules.py:683 msgid "Copy" msgstr "Kopiëren" #: lib/solaar/ui/diversion_rules.py:1063 msgid "This editor does not support the selected rule component yet." msgstr "Deze bewerker heeft nog geen ondersteuning voor het geselecteerde " "onderdeel." #: lib/solaar/ui/diversion_rules.py:1138 msgid "Number of seconds to delay." msgstr "" #: lib/solaar/ui/diversion_rules.py:1177 msgid "Not" msgstr "Niet" #: lib/solaar/ui/diversion_rules.py:1187 msgid "X11 active process. For use in X11 only." msgstr "" #: lib/solaar/ui/diversion_rules.py:1218 msgid "X11 mouse process. For use in X11 only." msgstr "" #: lib/solaar/ui/diversion_rules.py:1235 msgid "MouseProcess" msgstr "" #: lib/solaar/ui/diversion_rules.py:1260 msgid "Feature name of notification triggering rule processing." msgstr "" #: lib/solaar/ui/diversion_rules.py:1308 msgid "Report number of notification triggering rule processing." msgstr "" #: lib/solaar/ui/diversion_rules.py:1342 msgid "Active keyboard modifiers. Not always available in Wayland." msgstr "" #: lib/solaar/ui/diversion_rules.py:1383 msgid "Diverted key or button depressed or released.\n" "Use the Key/Button Diversion and Divert G Keys settings to divert " "keys and buttons." msgstr "" #: lib/solaar/ui/diversion_rules.py:1392 msgid "Key down" msgstr "" #: lib/solaar/ui/diversion_rules.py:1395 msgid "Key up" msgstr "" #: lib/solaar/ui/diversion_rules.py:1436 msgid "Diverted key or button is currently down.\n" "Use the Key/Button Diversion and Divert G Keys settings to divert " "keys and buttons." msgstr "" #: lib/solaar/ui/diversion_rules.py:1475 msgid "Test condition on notification triggering rule processing." msgstr "" #: lib/solaar/ui/diversion_rules.py:1479 msgid "Parameter" msgstr "" #: lib/solaar/ui/diversion_rules.py:1542 msgid "begin (inclusive)" msgstr "" #: lib/solaar/ui/diversion_rules.py:1543 msgid "end (exclusive)" msgstr "" #: lib/solaar/ui/diversion_rules.py:1552 msgid "range" msgstr "" #: lib/solaar/ui/diversion_rules.py:1554 msgid "minimum" msgstr "" #: lib/solaar/ui/diversion_rules.py:1555 msgid "maximum" msgstr "" #: lib/solaar/ui/diversion_rules.py:1557 #, python-format msgid "bytes %(0)d to %(1)d, ranging from %(2)d to %(3)d" msgstr "" #: lib/solaar/ui/diversion_rules.py:1562 msgid "mask" msgstr "" #: lib/solaar/ui/diversion_rules.py:1563 #, python-format msgid "bytes %(0)d to %(1)d, mask %(2)d" msgstr "" #: lib/solaar/ui/diversion_rules.py:1573 msgid "Bit or range test on bytes in notification message triggering rule " "processing." msgstr "" #: lib/solaar/ui/diversion_rules.py:1583 msgid "type" msgstr "" #: lib/solaar/ui/diversion_rules.py:1666 msgid "Mouse gesture with optional initiating button followed by zero or " "more mouse movements." msgstr "" #: lib/solaar/ui/diversion_rules.py:1671 msgid "Add movement" msgstr "" #: lib/solaar/ui/diversion_rules.py:1764 msgid "Simulate a chorded key click or depress or release.\n" "On Wayland requires write access to /dev/uinput." msgstr "" #: lib/solaar/ui/diversion_rules.py:1769 msgid "Add key" msgstr "Toets toevoegen" #: lib/solaar/ui/diversion_rules.py:1772 msgid "Click" msgstr "" #: lib/solaar/ui/diversion_rules.py:1775 msgid "Depress" msgstr "" #: lib/solaar/ui/diversion_rules.py:1778 msgid "Release" msgstr "" #: lib/solaar/ui/diversion_rules.py:1861 msgid "Simulate a mouse scroll.\n" "On Wayland requires write access to /dev/uinput." msgstr "" #: lib/solaar/ui/diversion_rules.py:1918 msgid "Simulate a mouse click.\n" "On Wayland requires write access to /dev/uinput." msgstr "" #: lib/solaar/ui/diversion_rules.py:1921 msgid "Button" msgstr "Knop" #: lib/solaar/ui/diversion_rules.py:1922 msgid "Count and Action" msgstr "" #: lib/solaar/ui/diversion_rules.py:1972 msgid "Execute a command with arguments." msgstr "" #: lib/solaar/ui/diversion_rules.py:1975 msgid "Add argument" msgstr "Aanvullende optie toevoegen" #: lib/solaar/ui/diversion_rules.py:2050 msgid "Toggle" msgstr "" #: lib/solaar/ui/diversion_rules.py:2050 msgid "True" msgstr "" #: lib/solaar/ui/diversion_rules.py:2051 msgid "False" msgstr "" #: lib/solaar/ui/diversion_rules.py:2065 msgid "Unsupported setting" msgstr "" #: lib/solaar/ui/diversion_rules.py:2223 lib/solaar/ui/diversion_rules.py:2242 #: lib/solaar/ui/diversion_rules.py:2328 lib/solaar/ui/diversion_rules.py:2570 #: lib/solaar/ui/diversion_rules.py:2588 msgid "Originating device" msgstr "" #: lib/solaar/ui/diversion_rules.py:2256 msgid "Device is active and its settings can be changed." msgstr "" #: lib/solaar/ui/diversion_rules.py:2266 msgid "Device that originated the current notification." msgstr "" #: lib/solaar/ui/diversion_rules.py:2280 msgid "Name of host computer." msgstr "" #: lib/solaar/ui/diversion_rules.py:2347 msgid "Value" msgstr "Waarde" #: lib/solaar/ui/diversion_rules.py:2355 msgid "Item" msgstr "" #: lib/solaar/ui/diversion_rules.py:2630 msgid "Change setting on device" msgstr "" #: lib/solaar/ui/diversion_rules.py:2647 msgid "Setting on device" msgstr "" #: lib/solaar/ui/notify.py:124 lib/solaar/ui/tray.py:318 #: lib/solaar/ui/tray.py:323 lib/solaar/ui/window.py:739 msgid "offline" msgstr "offline" #: lib/solaar/ui/pair_window.py:122 lib/solaar/ui/pair_window.py:256 #: lib/solaar/ui/pair_window.py:288 #, python-format msgid "%(receiver_name)s: pair new device" msgstr "%(receiver_name)s: nieuw apparaat aankoppelen" #: lib/solaar/ui/pair_window.py:123 #, python-format msgid "Enter passcode on %(name)s." msgstr "" #: lib/solaar/ui/pair_window.py:126 #, python-format msgid "Type %(passcode)s and then press the enter key." msgstr "" #: lib/solaar/ui/pair_window.py:129 msgid "left" msgstr "" #: lib/solaar/ui/pair_window.py:129 msgid "right" msgstr "" #: lib/solaar/ui/pair_window.py:131 #, python-format msgid "Press %(code)s\n" "and then press left and right buttons simultaneously." msgstr "" #: lib/solaar/ui/pair_window.py:188 msgid "Pairing failed" msgstr "Koppelen mislukt" #: lib/solaar/ui/pair_window.py:190 msgid "Make sure your device is within range, and has a decent battery " "charge." msgstr "Zorg er voor dat het apparaat binnen handbereik is en voldoende is " "opgeladen." #: lib/solaar/ui/pair_window.py:192 msgid "A new device was detected, but it is not compatible with this " "receiver." msgstr "Er is een nieuw apparaat aangetroffen dat niet compatibel is met " "deze ontvanger." #: lib/solaar/ui/pair_window.py:194 msgid "More paired devices than receiver can support." msgstr "Er zijn meer gekoppelde apparaten dan de ontvanger aankan." #: lib/solaar/ui/pair_window.py:196 msgid "No further details are available about the error." msgstr "Er is een fout opgetreden zonder nadere informatie." #: lib/solaar/ui/pair_window.py:210 msgid "Found a new device:" msgstr "Nieuw apparaat aangetroffen:" #: lib/solaar/ui/pair_window.py:235 msgid "The wireless link is not encrypted" msgstr "De draadloze verbinding is onversleuteld" #: lib/solaar/ui/pair_window.py:264 msgid "Unifying receivers are only compatible with Unifying devices." msgstr "" #: lib/solaar/ui/pair_window.py:266 msgid "Bolt receivers are only compatible with Bolt devices." msgstr "" #: lib/solaar/ui/pair_window.py:268 msgid "Other receivers are only compatible with a few devices." msgstr "" #: lib/solaar/ui/pair_window.py:270 msgid "The device must not be paired with a nearby powered-on receiver." msgstr "" #: lib/solaar/ui/pair_window.py:274 msgid "Press a pairing button or key until the pairing light flashes " "quickly." msgstr "" #: lib/solaar/ui/pair_window.py:276 msgid "You may have to first turn the device off and on again." msgstr "" #: lib/solaar/ui/pair_window.py:278 msgid "Turn on the device you want to pair." msgstr "Schakel het te koppelen apparaat in." #: lib/solaar/ui/pair_window.py:280 msgid "If the device is already turned on, turn it off and on again." msgstr "" #: lib/solaar/ui/pair_window.py:283 #, python-format msgid "\n" "\n" "This receiver has %d pairing remaining." msgid_plural "\n" "\n" "This receiver has %d pairings remaining." msgstr[0] "\n" "\n" "Er kan nog %d apparaat worden gekoppeld aan deze ontvanger." msgstr[1] "\n" "\n" "Er kunnen nog %d apparaten worden gekoppeld aan deze ontvanger." #: lib/solaar/ui/pair_window.py:286 msgid "\n" "Cancelling at this point will not use up a pairing." msgstr "\n" "Als u nu afbreekt, kunt u nog steeds iets koppelen." #: lib/solaar/ui/tray.py:58 msgid "No supported device found" msgstr "" #: lib/solaar/ui/tray.py:64 lib/solaar/ui/window.py:319 #, python-format msgid "About %s" msgstr "Info %s" #: lib/solaar/ui/tray.py:65 lib/solaar/ui/window.py:317 #, python-format msgid "Quit %s" msgstr "%s afsluiten" #: lib/solaar/ui/tray.py:297 lib/solaar/ui/tray.py:305 msgid "no receiver" msgstr "geen ontvanger" #: lib/solaar/ui/tray.py:321 msgid "no status" msgstr "geen status" #: lib/solaar/ui/window.py:96 msgid "Scanning" msgstr "Bezig met zoeken..." #: lib/solaar/ui/window.py:129 msgid "Battery" msgstr "Batterij" #: lib/solaar/ui/window.py:132 msgid "Wireless Link" msgstr "Draadloze verbinding" #: lib/solaar/ui/window.py:136 msgid "Lighting" msgstr "Verlichting" #: lib/solaar/ui/window.py:170 msgid "Show Technical Details" msgstr "Technische informatie tonen" #: lib/solaar/ui/window.py:186 msgid "Pair new device" msgstr "Nieuw apparaat aankoppelen" #: lib/solaar/ui/window.py:205 msgid "Select a device" msgstr "Kies een apparaat" #: lib/solaar/ui/window.py:322 msgid "Rule Editor" msgstr "Regelbewerker" #: lib/solaar/ui/window.py:533 msgid "Path" msgstr "Pad" #: lib/solaar/ui/window.py:536 msgid "USB ID" msgstr "USB-id" #: lib/solaar/ui/window.py:539 lib/solaar/ui/window.py:541 #: lib/solaar/ui/window.py:561 lib/solaar/ui/window.py:563 msgid "Serial" msgstr "Serie" #: lib/solaar/ui/window.py:545 msgid "Index" msgstr "Index" #: lib/solaar/ui/window.py:547 msgid "Wireless PID" msgstr "Draadloze pid" #: lib/solaar/ui/window.py:549 msgid "Product ID" msgstr "Product-id" #: lib/solaar/ui/window.py:551 msgid "Protocol" msgstr "Protocol" #: lib/solaar/ui/window.py:551 msgid "Unknown" msgstr "Onbekend" #: lib/solaar/ui/window.py:554 #, python-format msgid "%(rate)d ms (%(rate_hz)dHz)" msgstr "%(rate)d ms (%(rate_hz)dHz)" #: lib/solaar/ui/window.py:554 msgid "Polling rate" msgstr "Controlesnelheid" #: lib/solaar/ui/window.py:565 msgid "Unit ID" msgstr "Apparaat-id" #: lib/solaar/ui/window.py:576 msgid "none" msgstr "geen" #: lib/solaar/ui/window.py:577 msgid "Notifications" msgstr "Meldingen" #: lib/solaar/ui/window.py:621 msgid "No device paired." msgstr "Geen apparaat gekoppeld." #: lib/solaar/ui/window.py:628 #, python-format msgid "Up to %(max_count)s device can be paired to this receiver." msgid_plural "Up to %(max_count)s devices can be paired to this receiver." msgstr[0] "Er kan slechts %(max_count)s apparaat worden gekoppeld aan " "deze ontvanger." msgstr[1] "Er kunnen slechts %(max_count)s apparaten worden gekoppeld " "aan deze ontvanger." #: lib/solaar/ui/window.py:634 msgid "Only one device can be paired to this receiver." msgstr "Er kan slechts één apparaat worden gekoppeld aan deze ontvanger." #: lib/solaar/ui/window.py:638 #, python-format msgid "This receiver has %d pairing remaining." msgid_plural "This receiver has %d pairings remaining." msgstr[0] "Er kan nog %d apparaat worden gekoppeld aan deze ontvanger." msgstr[1] "Er kunnen nog %d apparaten worden gekoppeld aan deze " "ontvanger." #: lib/solaar/ui/window.py:692 msgid "Battery Voltage" msgstr "Batterijvoltage" #: lib/solaar/ui/window.py:694 msgid "Voltage reported by battery" msgstr "Het door de batterij gemelde voltage" #: lib/solaar/ui/window.py:696 msgid "Battery Level" msgstr "Batterijniveau" #: lib/solaar/ui/window.py:698 msgid "Approximate level reported by battery" msgstr "Het door de batterij geschatte niveau" #: lib/solaar/ui/window.py:705 lib/solaar/ui/window.py:707 msgid "next reported " msgstr "volgende melding " #: lib/solaar/ui/window.py:708 msgid " and next level to be reported." msgstr " en het volgende te melden niveau." #: lib/solaar/ui/window.py:713 msgid "last known" msgstr "laatst bekend" #: lib/solaar/ui/window.py:724 msgid "encrypted" msgstr "versleuteld" #: lib/solaar/ui/window.py:726 msgid "The wireless link between this device and its receiver is encrypted." msgstr "De draadloze verbinding tussen dit apparaat en de ontvanger is " "versleuteld." #: lib/solaar/ui/window.py:728 msgid "not encrypted" msgstr "onversleuteld" #: lib/solaar/ui/window.py:732 msgid "The wireless link between this device and its receiver is not " "encrypted.\n" "This is a security issue for pointing devices, and a major security " "issue for text-input devices." msgstr "" #: lib/solaar/ui/window.py:748 #, python-format msgid "%(light_level)d lux" msgstr "%(light_level)d lux" #, python-format #~ msgid "%(battery_level)s" #~ msgstr "%(battery_level)s" #, python-format #~ msgid "%(battery_percent)d%%" #~ msgstr "%(battery_percent)d%%" #~ msgid "About" #~ msgstr "Over" #~ msgid "Adjust the DPI by sliding the mouse horizontally while " #~ "holding the DPI button." #~ msgstr "Pas het aantal dpi aan door de dpi-knop ingedrukt te houden " #~ "en de muis horizontaal te verplaatsen." #~ msgid "Automatically switch the mouse wheel between ratchet and " #~ "freespin mode.\n" #~ "The mouse wheel is always free at 0, and always ratcheted at 50" #~ msgstr "Schakel het scrollwiel automatisch over van regel-voor-" #~ "regelmodus naar vrij scrollen.\n" #~ "Bij een waarde van 0 is vrij scrollen altijd ingeschakeld; bij een " #~ "waarde van 50 nooit." #~ msgid "Battery information unknown." #~ msgstr "Geen batterij-informatie beschikbaar." #~ msgid "Click to allow changes." #~ msgstr "Klik om wijzigingen toe te staan." #~ msgid "Click to prevent changes." #~ msgstr "Klik om wijzigingen te voorkomen." #~ msgid "Count" #~ msgstr "Teller" #~ msgid "DPI Sliding Adjustment" #~ msgstr "DPI aanpassen middels verplaatsen" #~ msgid "Effectively turns off thumb scrolling in Linux." #~ msgstr "Schakelt scrollen met het duimwiel op Linux uit." #~ msgid "Effectively turns off wheel scrolling in Linux." #~ msgstr "Schakelt scrollen met het scrollwiel op Linux uit." #~ msgid "For more information see the Solaar installation directions\n" #~ "at https://pwr-solaar.github.io/Solaar/installation" #~ msgstr "Bekijk voor meer informatie de installatiehulp op https://" #~ "pwr-solaar.github.io/Solaar/installation" #, python-format #~ msgid "Found a Logitech Receiver (%s), but did not have permission " #~ "to open it." #~ msgstr "Er is een Logitech-ontvanger aangetroffen ('%s'), maar " #~ "Solaar is niet gemachtigd om deze te tonen." #~ msgid "HID++ mode for horizontal scroll with the thumb wheel." #~ msgstr "HID++-modus voor horizontaal scrollen met het duimwiel." #~ msgid "HID++ mode for vertical scroll with the wheel." #~ msgstr "HID++-modus voor verticaal scrollen." #~ msgid "High Resolution Scrolling" #~ msgstr "Scrollen met hoge resolutie" #~ msgid "High Resolution Wheel Invert" #~ msgstr "Andersom scrollen met hoge resolutie" #~ msgid "High-sensitivity wheel invert mode for vertical scroll." #~ msgstr "Hogegevoeligheidsmodus voor andersom verticaal scrollen." #~ msgid "If the device is already turned on,\n" #~ "turn if off and on again." #~ msgstr "Als het apparaat reeds is ingeschakeld,\n" #~ "schakel het dan uit en weer in." #~ msgid "If the device is already turned on, turn if off and on again." #~ msgstr "Als het apparaat al is ingeschakeld, schakel het dan uit en " #~ "weer in." #~ msgid "If you've just installed Solaar, try removing the receiver " #~ "and plugging it back in." #~ msgstr "Als u Solaar nét heeft geïnstalleerd, plug dan de ontvanger " #~ "opnieuw in." #~ msgid "Make the key or button send HID++ notifications (which " #~ "trigger Solaar rules but are otherwise ignored)." #~ msgstr "Laat de toets of knop een HID++-melding versturen (welke " #~ "Solaar's regels volgen, maar verder genegeerd worden)." #~ msgid "No Logitech receiver found" #~ msgstr "Geen Logitech-ontvanger aangetroffen" #~ msgid "Quit" #~ msgstr "Afsluiten" #~ msgid "Scroll Wheel HID++ Scrolling" #~ msgstr "HID++-scrollen" #~ msgid "Scroll Wheel Rachet" #~ msgstr "Vrij scrollen" #~ msgid "Shows status of devices connected\n" #~ "through wireless Logitech receivers." #~ msgstr "Toont de status van apparaten die verbonden\n" #~ "zijn middels draadloze Logitech-ontvangers." #~ msgid "Solaar depends on a udev file that is not present" #~ msgstr "Solaar is afhankelijk van een udev-bestand dat niet aanwezig " #~ "is" #, python-format #~ msgid "The receiver only supports %d paired device(s)." #~ msgstr "Dit apparaat ondersteunt slechts %d gekoppeld apparaat/" #~ "apparaten." #~ msgid "The receiver was unplugged." #~ msgstr "De ontvanger is niet meer aanwezig." #~ msgid "The wireless link between this device and its receiver is " #~ "not encrypted.\n" #~ "\n" #~ "For pointing devices (mice, trackballs, trackpads), this is a minor " #~ "security issue.\n" #~ "\n" #~ "It is, however, a major security issue for text-input devices " #~ "(keyboards, numpads),\n" #~ "because typed text can be sniffed inconspicuously by 3rd parties " #~ "within range." #~ msgstr "De draadloze verbinding tussen dit apparaat en de ontvanger " #~ "is onversleuteld.\n" #~ "\n" #~ "Bij aanwijsapparaten (zoals muizen, trackballs en trackpads) vormt " #~ "dit een heel klein veiligheidsrisico.\n" #~ "\n" #~ "Echter, bij tekstinvoerapparaten (zoals (numerieke) toetsenborden) " #~ "vormt dit een groot probleem\n" #~ "omdat ingevoerde tekst stiekem kan worden uitgelezen door derden " #~ "binnen handbereik." #~ msgid "Thumb Wheel HID++ Scrolling" #~ msgstr "HID++-scrollen met duimwiel" #~ msgid "Top-most coordinate." #~ msgstr "Het bovenste coördinaat." #~ msgid "height" #~ msgstr "hoogte" #~ msgid "top" #~ msgstr "boven" #~ msgid "unknown" #~ msgstr "onbekend" #~ msgid "width" #~ msgstr "breedte" Solaar-1.1.14/po/nn.po000066400000000000000000001753311473525213700144330ustar00rootroot00000000000000# Norwegian Nynorsk translations for solaar package. # Copyright (C) 2020 THE solaar'S COPYRIGHT HOLDER # This file is distributed under the same license as the solaar package. # Automatically generated, 2020. # John Erling Blad , 2020-2024. # msgid "" msgstr "" "Project-Id-Version: solaar 1.0.3\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2024-10-31 01:42+0100\n" "PO-Revision-Date: 2024-10-31 02:20+0100\n" "Last-Translator: John Erling Blad \n" "Language-Team: \n" "Language: nb\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n!=1)\n" "X-Generator: Gtranslator 47.0\n" #: lib/logitech_receiver/base_usb.py:48 msgid "Bolt Receiver" msgstr "Bolt mottakar" #: lib/logitech_receiver/base_usb.py:60 msgid "Unifying Receiver" msgstr "Unifying mottakar" #: lib/logitech_receiver/base_usb.py:71 lib/logitech_receiver/base_usb.py:83 #: lib/logitech_receiver/base_usb.py:96 lib/logitech_receiver/base_usb.py:109 msgid "Nano Receiver" msgstr "Nano mottakar" #: lib/logitech_receiver/base_usb.py:121 msgid "Lightspeed Receiver" msgstr "Lightspeed mottakar" #: lib/logitech_receiver/base_usb.py:131 msgid "EX100 Receiver 27 Mhz" msgstr "EX100 27 Mhz mottakar" #: lib/logitech_receiver/common.py:648 #, python-format msgid "Battery: %(level)s (%(status)s)" msgstr "Batteri: %(level)s (%(status)s)" #: lib/logitech_receiver/common.py:651 #, python-format msgid "Battery: %(percent)d%% (%(status)s)" msgstr "Batteri: %(percent)d%% (%(status)s)" #: lib/logitech_receiver/hidpp20.py:962 #: lib/logitech_receiver/settings_templates.py:294 msgid "Disabled" msgstr "Avslått" #: lib/logitech_receiver/hidpp20.py:963 msgid "Static" msgstr "Statisk" #: lib/logitech_receiver/hidpp20.py:964 msgid "Pulse" msgstr "Puls" #: lib/logitech_receiver/hidpp20.py:965 msgid "Cycle" msgstr "Syklus" #: lib/logitech_receiver/hidpp20.py:966 msgid "Boot" msgstr "Boot" #: lib/logitech_receiver/hidpp20.py:967 msgid "Demo" msgstr "Demo" #: lib/logitech_receiver/hidpp20.py:969 msgid "Breathe" msgstr "Pust" #: lib/logitech_receiver/hidpp20.py:972 msgid "Ripple" msgstr "Krusning" #: lib/logitech_receiver/hidpp20.py:973 msgid "Decomposition" msgstr "Dekomponering" #: lib/logitech_receiver/hidpp20.py:974 msgid "Signature1" msgstr "Signatur1" #: lib/logitech_receiver/hidpp20.py:975 msgid "Signature2" msgstr "Signatur2" #: lib/logitech_receiver/hidpp20.py:976 msgid "CycleS" msgstr "SyklusS" #: lib/logitech_receiver/hidpp20.py:1040 msgid "Unknown Location" msgstr "Ukjend lokasjon" #: lib/logitech_receiver/hidpp20.py:1041 msgid "Primary" msgstr "Primær" #: lib/logitech_receiver/hidpp20.py:1042 msgid "Logo" msgstr "Logo" #: lib/logitech_receiver/hidpp20.py:1043 msgid "Left Side" msgstr "Venstre side" #: lib/logitech_receiver/hidpp20.py:1044 msgid "Right Side" msgstr "Høgre side" #: lib/logitech_receiver/hidpp20.py:1045 msgid "Combined" msgstr "Kombinert" #: lib/logitech_receiver/hidpp20.py:1046 msgid "Primary 1" msgstr "Primær 1" #: lib/logitech_receiver/hidpp20.py:1047 msgid "Primary 2" msgstr "Primær 2" #: lib/logitech_receiver/hidpp20.py:1048 msgid "Primary 3" msgstr "Primær 3" #: lib/logitech_receiver/hidpp20.py:1049 msgid "Primary 4" msgstr "Primær 4" #: lib/logitech_receiver/hidpp20.py:1050 msgid "Primary 5" msgstr "Primær 5" #: lib/logitech_receiver/hidpp20.py:1051 msgid "Primary 6" msgstr "Primær 6" #: lib/logitech_receiver/i18n.py:28 msgid "empty" msgstr "tom" #: lib/logitech_receiver/i18n.py:29 msgid "critical" msgstr "kritisk" #: lib/logitech_receiver/i18n.py:30 msgid "low" msgstr "låg" #: lib/logitech_receiver/i18n.py:31 msgid "average" msgstr "middels" #: lib/logitech_receiver/i18n.py:32 msgid "good" msgstr "god" #: lib/logitech_receiver/i18n.py:33 msgid "full" msgstr "full" #: lib/logitech_receiver/i18n.py:35 msgid "discharging" msgstr "utlading" #: lib/logitech_receiver/i18n.py:36 msgid "recharging" msgstr "opplading" #: lib/logitech_receiver/i18n.py:37 msgid "charging" msgstr "lader" #: lib/logitech_receiver/i18n.py:38 msgid "not charging" msgstr "lader ikkje" #: lib/logitech_receiver/i18n.py:39 msgid "almost full" msgstr "nesten full" #: lib/logitech_receiver/i18n.py:40 msgid "charged" msgstr "ladet" #: lib/logitech_receiver/i18n.py:41 msgid "slow recharge" msgstr "langsom lading" #: lib/logitech_receiver/i18n.py:42 msgid "invalid battery" msgstr "batterifeil" #: lib/logitech_receiver/i18n.py:43 msgid "thermal error" msgstr "termisk feil" #: lib/logitech_receiver/i18n.py:44 msgid "error" msgstr "feil" #: lib/logitech_receiver/i18n.py:45 msgid "standard" msgstr "standard" #: lib/logitech_receiver/i18n.py:46 msgid "fast" msgstr "rask" #: lib/logitech_receiver/i18n.py:47 msgid "slow" msgstr "langsom" #: lib/logitech_receiver/i18n.py:49 msgid "device timeout" msgstr "eininga svarte ikkje" #: lib/logitech_receiver/i18n.py:50 msgid "device not supported" msgstr "eininga er ikkje støtta" #: lib/logitech_receiver/i18n.py:51 msgid "too many devices" msgstr "for mange einingar" #: lib/logitech_receiver/i18n.py:52 msgid "sequence timeout" msgstr "sekvens timeout" #: lib/logitech_receiver/i18n.py:54 msgid "Firmware" msgstr "Fastvare" #: lib/logitech_receiver/i18n.py:55 msgid "Bootloader" msgstr "Oppstartslaster" #: lib/logitech_receiver/i18n.py:56 msgid "Hardware" msgstr "Maskinvare" #: lib/logitech_receiver/i18n.py:57 msgid "Other" msgstr "Anna" #: lib/logitech_receiver/i18n.py:59 msgid "Left Button" msgstr "Venstreknapp" #: lib/logitech_receiver/i18n.py:60 msgid "Right Button" msgstr "Høgreknapp" #: lib/logitech_receiver/i18n.py:61 msgid "Middle Button" msgstr "Midtknapp" #: lib/logitech_receiver/i18n.py:62 msgid "Back Button" msgstr "Tlbakeknapp" #: lib/logitech_receiver/i18n.py:63 msgid "Forward Button" msgstr "Framoverknapp" #: lib/logitech_receiver/i18n.py:64 msgid "Mouse Gesture Button" msgstr "Rørsleknapp" #: lib/logitech_receiver/i18n.py:65 msgid "Smart Shift" msgstr "Smart skift" #: lib/logitech_receiver/i18n.py:66 msgid "DPI Switch" msgstr "DPI-bryter" #: lib/logitech_receiver/i18n.py:67 msgid "Left Tilt" msgstr "Venstre-tilt" #: lib/logitech_receiver/i18n.py:68 msgid "Right Tilt" msgstr "Høgre-tilt" #: lib/logitech_receiver/i18n.py:69 msgid "Left Click" msgstr "Venstre-klikk" #: lib/logitech_receiver/i18n.py:70 msgid "Right Click" msgstr "Høgre-klikk" #: lib/logitech_receiver/i18n.py:71 msgid "Mouse Middle Button" msgstr "Midtknapp for mus" #: lib/logitech_receiver/i18n.py:72 msgid "Mouse Back Button" msgstr "Tilbakeknapp for mus" #: lib/logitech_receiver/i18n.py:73 msgid "Mouse Forward Button" msgstr "Framoverknapp for mus" #: lib/logitech_receiver/i18n.py:74 msgid "Gesture Button Navigation" msgstr "Navigering med rørsleknapp" #: lib/logitech_receiver/i18n.py:75 msgid "Mouse Scroll Left Button" msgstr "Venstre rulleknapp for mus" #: lib/logitech_receiver/i18n.py:76 msgid "Mouse Scroll Right Button" msgstr "Høgre rulleknapp for mus" #: lib/logitech_receiver/i18n.py:78 msgid "pressed" msgstr "nedtrykt" #: lib/logitech_receiver/i18n.py:79 msgid "released" msgstr "utløyst" #: lib/logitech_receiver/notifications.py:86 #: lib/logitech_receiver/notifications.py:138 msgid "pairing lock is closed" msgstr "parlåsen er lukka" #: lib/logitech_receiver/notifications.py:86 #: lib/logitech_receiver/notifications.py:138 msgid "pairing lock is open" msgstr "parlåsen er open" #: lib/logitech_receiver/notifications.py:103 msgid "discovery lock is closed" msgstr "oppdagelseslåsen er lukka" #: lib/logitech_receiver/notifications.py:103 msgid "discovery lock is open" msgstr "oppdagelseslåsen er open" #: lib/logitech_receiver/notifications.py:232 lib/solaar/listener.py:247 msgid "connected" msgstr "tilkopla" #: lib/logitech_receiver/notifications.py:232 lib/solaar/listener.py:247 msgid "disconnected" msgstr "fråkopla" #: lib/logitech_receiver/notifications.py:258 msgid "unpaired" msgstr "upara" #: lib/logitech_receiver/notifications.py:305 msgid "powered on" msgstr "påslått" #: lib/logitech_receiver/receiver.py:383 msgid "No paired devices." msgstr "Inga para eining." #: lib/logitech_receiver/receiver.py:385 lib/solaar/ui/window.py:590 #, python-format msgid "%(count)s paired device." msgid_plural "%(count)s paired devices." msgstr[0] "%(count)s para eining." msgstr[1] "%(count)s para einingar." #: lib/logitech_receiver/settings.py:614 msgid "register" msgstr "register" #: lib/logitech_receiver/settings.py:628 lib/logitech_receiver/settings.py:663 msgid "feature" msgstr "karakteristikk" #: lib/logitech_receiver/settings_templates.py:133 msgid "Swap Fx function" msgstr "Bytt Fx-funksjon" #: lib/logitech_receiver/settings_templates.py:136 msgid "" "When set, the F1..F12 keys will activate their special function,\n" "and you must hold the FN key to activate their standard function." msgstr "" "Viss aktivert, vil F1–F12-tastane aktivera sin spesialfunksjon,\n" "og du må halda FN-tasten nede for å aktivera deira standardfunksjon." #: lib/logitech_receiver/settings_templates.py:141 msgid "" "When unset, the F1..F12 keys will activate their standard function,\n" "and you must hold the FN key to activate their special function." msgstr "" "Viss deaktivert, vil F1–F12-tastane aktivera sin standardfunksjon,\n" "og du må holde FN-tasten nede for å aktivere deira spesialfunksjon." #: lib/logitech_receiver/settings_templates.py:149 msgid "Hand Detection" msgstr "Håndpåvisning" #: lib/logitech_receiver/settings_templates.py:150 msgid "Turn on illumination when the hands hover over the keyboard." msgstr "Slå på belysning når hendene er over tastaturet." #: lib/logitech_receiver/settings_templates.py:157 msgid "Scroll Wheel Smooth Scrolling" msgstr "Mjuk rulling med rullehjul" #: lib/logitech_receiver/settings_templates.py:158 #: lib/logitech_receiver/settings_templates.py:405 #: lib/logitech_receiver/settings_templates.py:434 msgid "High-sensitivity mode for vertical scroll with the wheel." msgstr "Høgsensitiv modus for vertikal rulling med hjulet." #: lib/logitech_receiver/settings_templates.py:165 msgid "Side Scrolling" msgstr "Siderulling" #: lib/logitech_receiver/settings_templates.py:167 msgid "" "When disabled, pushing the wheel sideways sends custom button events\n" "instead of the standard side-scrolling events." msgstr "" "Viss deaktivert; blir trykt hjula sidevegs så sendast ein tilpassa " "knapphending\n" "i staden for standard siderulling." #: lib/logitech_receiver/settings_templates.py:177 msgid "Sensitivity (DPI - older mice)" msgstr "Følsomhet (DPI – eldre mus)" #: lib/logitech_receiver/settings_templates.py:178 #: lib/logitech_receiver/settings_templates.py:977 #: lib/logitech_receiver/settings_templates.py:1005 msgid "Mouse movement sensitivity" msgstr "Sensibilitet for musrørsler" #: lib/logitech_receiver/settings_templates.py:251 msgid "Backlight Timed" msgstr "Tidfesta bakgrunnslys" #: lib/logitech_receiver/settings_templates.py:252 #: lib/logitech_receiver/settings_templates.py:392 msgid "Set illumination time for keyboard." msgstr "Still inn belysningstid for tastatur." #: lib/logitech_receiver/settings_templates.py:263 msgid "Backlight" msgstr "Bakbelysning" #: lib/logitech_receiver/settings_templates.py:264 msgid "" "Illumination level on keyboard. Changes made are only applied in Manual " "mode." msgstr "" "Lysnivå på tastaturet. Endringar som er gjorde, blir berre brukte i manuell " "modus." #: lib/logitech_receiver/settings_templates.py:296 msgid "Automatic" msgstr "Automatisk" #: lib/logitech_receiver/settings_templates.py:298 msgid "Manual" msgstr "Manuell" #: lib/logitech_receiver/settings_templates.py:300 msgid "Enabled" msgstr "Påslått" #: lib/logitech_receiver/settings_templates.py:306 msgid "Backlight Level" msgstr "Bakgrunnslysnivå" #: lib/logitech_receiver/settings_templates.py:307 msgid "Illumination level on keyboard when in Manual mode." msgstr "Lysnivå på tastaturet i manuell modus." #: lib/logitech_receiver/settings_templates.py:364 msgid "Backlight Delay Hands Out" msgstr "Forseinking på bakgrunnsbelysning når hendene blir fjerna" #: lib/logitech_receiver/settings_templates.py:365 msgid "" "Delay in seconds until backlight fades out with hands away from keyboard." msgstr "" "Forseinking i sekund til bakgrunnsbelysninga tonar ut når hendene er borte " "frå tastaturet." #: lib/logitech_receiver/settings_templates.py:373 msgid "Backlight Delay Hands In" msgstr "Forseinking på bakgrunnsbelysning når hendene er nære" #: lib/logitech_receiver/settings_templates.py:374 msgid "Delay in seconds until backlight fades out with hands near keyboard." msgstr "" "Forseinking i sekund til bakgrunnsbelysninga tonar ut når hendene er nære " "tastaturet." #: lib/logitech_receiver/settings_templates.py:382 msgid "Backlight Delay Powered" msgstr "Forseinking på bakgrunnsbelysning når straumforsynt" #: lib/logitech_receiver/settings_templates.py:383 msgid "Delay in seconds until backlight fades out with external power." msgstr "" "Forseinking i sekund til bakgrunnsbelysninga tonar ut med ekstern " "straumforsyning." #: lib/logitech_receiver/settings_templates.py:391 msgid "Backlight (Seconds)" msgstr "Bakgrunnslys (sekund)" #: lib/logitech_receiver/settings_templates.py:403 msgid "Scroll Wheel High Resolution" msgstr "Høgopplausleg rullehjul" #: lib/logitech_receiver/settings_templates.py:407 #: lib/logitech_receiver/settings_templates.py:436 msgid "Set to ignore if scrolling is abnormally fast or slow" msgstr "Sett til å ignorera viss rullinga er unormalt rask eller sakte" #: lib/logitech_receiver/settings_templates.py:414 #: lib/logitech_receiver/settings_templates.py:445 msgid "Scroll Wheel Diversion" msgstr "Omdirigering for rullehjul" #: lib/logitech_receiver/settings_templates.py:416 msgid "" "Make scroll wheel send LOWRES_WHEEL HID++ notifications (which trigger " "Solaar rules but are otherwise ignored)." msgstr "" "Få rullehjulet til å senda LOWRES_WHEEL HID++-varslar (som utløyser Solaar-" "reglar, men som elles blir ignorert)." #: lib/logitech_receiver/settings_templates.py:423 msgid "Scroll Wheel Direction" msgstr "Retning på rullehjul" #: lib/logitech_receiver/settings_templates.py:424 msgid "Invert direction for vertical scroll with wheel." msgstr "Inverter retning for vertikal rulling med hjul." #: lib/logitech_receiver/settings_templates.py:432 msgid "Scroll Wheel Resolution" msgstr "Oppløysing på rullehjul" #: lib/logitech_receiver/settings_templates.py:447 msgid "" "Make scroll wheel send HIRES_WHEEL HID++ notifications (which trigger Solaar " "rules but are otherwise ignored)." msgstr "" "Få rullehjulet til å senda HIRS_WHEEL HID++-varslar (som utløyser Solaar-" "reglar, men som elles blir ignorert)." #: lib/logitech_receiver/settings_templates.py:456 msgid "Sensitivity (Pointer Speed)" msgstr "Følsomhet (pekerfart)" #: lib/logitech_receiver/settings_templates.py:457 msgid "Speed multiplier for mouse (256 is normal multiplier)." msgstr "Fartsmultiplikator for mus (256 er normal multiplikator)." #: lib/logitech_receiver/settings_templates.py:467 msgid "Thumb Wheel Diversion" msgstr "Omdirigering for tommelhjul" #: lib/logitech_receiver/settings_templates.py:469 msgid "" "Make thumb wheel send THUMB_WHEEL HID++ notifications (which trigger Solaar " "rules but are otherwise ignored)." msgstr "" "Få tommelhjulet til å senda THUMB_WHEEL HID++-varslar (som utløyser Solaar-" "reglar, men som elles blir ignorert)." #: lib/logitech_receiver/settings_templates.py:478 msgid "Thumb Wheel Direction" msgstr "Retning på tommelhjul" #: lib/logitech_receiver/settings_templates.py:479 msgid "Invert thumb wheel scroll direction." msgstr "Inverter rulleretning for tommelhjul." #: lib/logitech_receiver/settings_templates.py:499 msgid "Onboard Profiles" msgstr "Innebygde profilar" #: lib/logitech_receiver/settings_templates.py:500 msgid "" "Enable an onboard profile, which controls report rate, sensitivity, and " "button actions" msgstr "" "Aktiver ein innebygd profil, som kontrollerer rapporthyppigheit, " "sensibilitet og knappehandlingar" #: lib/logitech_receiver/settings_templates.py:544 #: lib/logitech_receiver/settings_templates.py:577 msgid "Report Rate" msgstr "Rapporthyppigheit" #: lib/logitech_receiver/settings_templates.py:546 #: lib/logitech_receiver/settings_templates.py:579 msgid "Frequency of device movement reports" msgstr "Frekvensen til rapportar om einingsrørsler" #: lib/logitech_receiver/settings_templates.py:546 #: lib/logitech_receiver/settings_templates.py:579 #: lib/logitech_receiver/settings_templates.py:1005 #: lib/logitech_receiver/settings_templates.py:1379 #: lib/logitech_receiver/settings_templates.py:1410 msgid "May need Onboard Profiles set to Disable to be effective." msgstr "" "Kan trenga at innebygde profilar blir sette til avslått for å vera effektive." #: lib/logitech_receiver/settings_templates.py:607 msgid "Divert crown events" msgstr "Omdiriger krone-hendingar" #: lib/logitech_receiver/settings_templates.py:608 msgid "" "Make crown send CROWN HID++ notifications (which trigger Solaar rules but " "are otherwise ignored)." msgstr "" "Få krona til å senda CROWN HID++-varslar (som utløyser Solaar-reglar, men " "som elles blir ignorert)." #: lib/logitech_receiver/settings_templates.py:616 msgid "Crown smooth scroll" msgstr "Glatt krone-rulling" #: lib/logitech_receiver/settings_templates.py:617 msgid "Set crown smooth scroll" msgstr "Still inn glatt krone-rulling" #: lib/logitech_receiver/settings_templates.py:625 msgid "Divert G and M Keys" msgstr "Omdiriger G- og M-tastar" #: lib/logitech_receiver/settings_templates.py:626 msgid "" "Make G and M keys send HID++ notifications (which trigger Solaar rules but " "are otherwise ignored)." msgstr "" "Få G- og M-tastane til å senda HID++-varslar (som utløyser Solaar-reglar, " "men som elles blir ignorerte)." #: lib/logitech_receiver/settings_templates.py:640 msgid "Scroll Wheel Ratcheted" msgstr "Rullehjul sperrehaka" #: lib/logitech_receiver/settings_templates.py:641 msgid "" "Switch the mouse wheel between speed-controlled ratcheting and always " "freespin." msgstr "" "Byt musehjulet mellom fartskontrollert sperrehake og alltid frittløpende." #: lib/logitech_receiver/settings_templates.py:643 msgid "Freespinning" msgstr "Frittløpende" #: lib/logitech_receiver/settings_templates.py:643 msgid "Ratcheted" msgstr "Sperrehake" #: lib/logitech_receiver/settings_templates.py:650 msgid "Scroll Wheel Ratchet Speed" msgstr "Rullehjul sperrehakefart" #: lib/logitech_receiver/settings_templates.py:652 msgid "" "Use the mouse wheel speed to switch between ratcheted and freespinning.\n" "The mouse wheel is always ratcheted at 50." msgstr "" "Bruk musehjulfarten til å byta mellom sperrehake og frittløpende.\n" "Musehjulet er alltid sett til 50." #: lib/logitech_receiver/settings_templates.py:701 msgid "Key/Button Actions" msgstr "Taste-/knappehandling" #: lib/logitech_receiver/settings_templates.py:703 msgid "Change the action for the key or button." msgstr "Endre handlinga for ein tast eller knapp." #: lib/logitech_receiver/settings_templates.py:705 msgid "Overridden by diversion." msgstr "Overstyrt av omdirigering." #: lib/logitech_receiver/settings_templates.py:707 msgid "" "Changing important actions (such as for the left mouse button) can result in " "an unusable system." msgstr "" "Endring av viktige handlingar (til dømes venstre musknapp) kan resultera i " "eit ubrukande system." #: lib/logitech_receiver/settings_templates.py:882 msgid "Key/Button Diversion" msgstr "Taste-/knappeomdirigering" #: lib/logitech_receiver/settings_templates.py:883 msgid "" "Make the key or button send HID++ notifications (Diverted) or initiate Mouse " "Gestures or Sliding DPI" msgstr "" "Få nøkkelen eller knappen til å senda HID++-varslar (omdirigert) eller " "initier muserørsler eller glidande Dpi" #: lib/logitech_receiver/settings_templates.py:886 #: lib/logitech_receiver/settings_templates.py:887 #: lib/logitech_receiver/settings_templates.py:888 msgid "Diverted" msgstr "Omdirigert" #: lib/logitech_receiver/settings_templates.py:886 #: lib/logitech_receiver/settings_templates.py:887 msgid "Mouse Gestures" msgstr "Muserørsler" #: lib/logitech_receiver/settings_templates.py:886 #: lib/logitech_receiver/settings_templates.py:887 #: lib/logitech_receiver/settings_templates.py:888 msgid "Regular" msgstr "Vanleg" #: lib/logitech_receiver/settings_templates.py:886 msgid "Sliding DPI" msgstr "Glidande DPI" #: lib/logitech_receiver/settings_templates.py:976 #: lib/logitech_receiver/settings_templates.py:1004 msgid "Sensitivity (DPI)" msgstr "Følsomheit (DPI)" #: lib/logitech_receiver/settings_templates.py:1081 msgid "Sensitivity Switching" msgstr "Følsomheitsbyte" #: lib/logitech_receiver/settings_templates.py:1083 msgid "" "Switch the current sensitivity and the remembered sensitivity when the key " "or button is pressed.\n" "If there is no remembered sensitivity, just remember the current sensitivity" msgstr "" "Byt gjeldande følsomheit og tidlegare følsomheit når tasten eller knappen " "blir trykt.\n" "Viss det ikkje er nokon tidlegare følsomheit, sett berre gjeldande følsomheit" #: lib/logitech_receiver/settings_templates.py:1087 msgid "Off" msgstr "Av" #: lib/logitech_receiver/settings_templates.py:1118 msgid "Disable keys" msgstr "Slå av tastar" #: lib/logitech_receiver/settings_templates.py:1119 msgid "Disable specific keyboard keys." msgstr "Slå av spesifikke tastaturtastar." #: lib/logitech_receiver/settings_templates.py:1122 #, python-format msgid "Disables the %s key." msgstr "Slår av %s-tasten." #: lib/logitech_receiver/settings_templates.py:1135 #: lib/logitech_receiver/settings_templates.py:1192 msgid "Set OS" msgstr "Sett OS" #: lib/logitech_receiver/settings_templates.py:1136 #: lib/logitech_receiver/settings_templates.py:1193 msgid "Change keys to match OS." msgstr "Endre tastar så dei stemmer med OS-et." #: lib/logitech_receiver/settings_templates.py:1205 msgid "Change Host" msgstr "Endre tjener" #: lib/logitech_receiver/settings_templates.py:1206 msgid "Switch connection to a different host" msgstr "Endre koplinga til ein annan tenar" #: lib/logitech_receiver/settings_templates.py:1230 msgid "Performs a left click." msgstr "Utfører eit venstreklikk." #: lib/logitech_receiver/settings_templates.py:1230 msgid "Single tap" msgstr "Enkeltklikk" #: lib/logitech_receiver/settings_templates.py:1231 msgid "Performs a right click." msgstr "Utfører eit høgreklikk." #: lib/logitech_receiver/settings_templates.py:1231 msgid "Single tap with two fingers" msgstr "Enkelt trykk med to fingrar" #: lib/logitech_receiver/settings_templates.py:1232 msgid "Single tap with three fingers" msgstr "Enkelt trykk med tre fingrar" #: lib/logitech_receiver/settings_templates.py:1236 msgid "Double tap" msgstr "Dobbeltklikk" #: lib/logitech_receiver/settings_templates.py:1236 msgid "Performs a double click." msgstr "Utfører eit dobbeltklikk" #: lib/logitech_receiver/settings_templates.py:1237 msgid "Double tap with two fingers" msgstr "Dobbeltklikk med to fingrar" #: lib/logitech_receiver/settings_templates.py:1238 msgid "Double tap with three fingers" msgstr "Dobbeltklikk med to fingrar" #: lib/logitech_receiver/settings_templates.py:1241 msgid "Drags items by dragging the finger after double tapping." msgstr "Dra element med fingeren etter dobbeltklikk." #: lib/logitech_receiver/settings_templates.py:1241 msgid "Tap and drag" msgstr "Klikk og dra" #: lib/logitech_receiver/settings_templates.py:1243 msgid "Tap and drag with two fingers" msgstr "Klikk og dra med to fingrar" #: lib/logitech_receiver/settings_templates.py:1244 msgid "Drags items by dragging the fingers after double tapping." msgstr "Dra element med fingrane etter dobbeltklikk." #: lib/logitech_receiver/settings_templates.py:1246 msgid "Tap and drag with three fingers" msgstr "Klikk og dra med tre fingrar" #: lib/logitech_receiver/settings_templates.py:1249 msgid "Suppress tap and edge gestures" msgstr "Undertrykk klikk- og kantrørsler" #: lib/logitech_receiver/settings_templates.py:1250 msgid "Disables tap and edge gestures (equivalent to pressing Fn+LeftClick)." msgstr "Slår av klikk- og kantrørsler (svarer til å trykkja Fn+Venstreklikk)." #: lib/logitech_receiver/settings_templates.py:1252 msgid "Scroll with one finger" msgstr "Rull med ein finger" #: lib/logitech_receiver/settings_templates.py:1252 #: lib/logitech_receiver/settings_templates.py:1253 #: lib/logitech_receiver/settings_templates.py:1256 msgid "Scrolls." msgstr "Rullar" #: lib/logitech_receiver/settings_templates.py:1253 #: lib/logitech_receiver/settings_templates.py:1256 msgid "Scroll with two fingers" msgstr "Rull med to fingre" #: lib/logitech_receiver/settings_templates.py:1254 msgid "Scroll horizontally with two fingers" msgstr "Rull horisontalt med to fingrar" #: lib/logitech_receiver/settings_templates.py:1254 msgid "Scrolls horizontally." msgstr "Rull horisontalt." #: lib/logitech_receiver/settings_templates.py:1255 msgid "Scroll vertically with two fingers" msgstr "Rull vertikalt med to fingrar" #: lib/logitech_receiver/settings_templates.py:1255 msgid "Scrolls vertically." msgstr "Rull vertikalt." #: lib/logitech_receiver/settings_templates.py:1257 msgid "Inverts the scrolling direction." msgstr "Inverterer rulleretninga." #: lib/logitech_receiver/settings_templates.py:1257 msgid "Natural scrolling" msgstr "Naturleg rulling" #: lib/logitech_receiver/settings_templates.py:1258 msgid "Enables the thumbwheel." msgstr "Aktiverer tommelhjulet." #: lib/logitech_receiver/settings_templates.py:1258 msgid "Thumbwheel" msgstr "Tommelhjul" #: lib/logitech_receiver/settings_templates.py:1269 #: lib/logitech_receiver/settings_templates.py:1273 msgid "Swipe from the top edge" msgstr "Sveip frå øvre kant" #: lib/logitech_receiver/settings_templates.py:1270 msgid "Swipe from the left edge" msgstr "Sveip frå venstre kant" #: lib/logitech_receiver/settings_templates.py:1271 msgid "Swipe from the right edge" msgstr "Sveip frå høgre kant" #: lib/logitech_receiver/settings_templates.py:1272 msgid "Swipe from the bottom edge" msgstr "Sveip frå nedre kant" #: lib/logitech_receiver/settings_templates.py:1274 msgid "Swipe two fingers from the left edge" msgstr "Sveip to fingrar frå venstre kant" #: lib/logitech_receiver/settings_templates.py:1275 msgid "Swipe two fingers from the right edge" msgstr "Sveip to fingrar frå høgre kant" #: lib/logitech_receiver/settings_templates.py:1276 msgid "Swipe two fingers from the bottom edge" msgstr "Sveip to fingrar frå nedre kant" #: lib/logitech_receiver/settings_templates.py:1277 msgid "Swipe two fingers from the top edge" msgstr "Sveip to fingrar frå øvre kant" #: lib/logitech_receiver/settings_templates.py:1278 #: lib/logitech_receiver/settings_templates.py:1282 msgid "Pinch to zoom out; spread to zoom in." msgstr "Knip for å zooma ut; sprei for å zooma inn." #: lib/logitech_receiver/settings_templates.py:1278 msgid "Zoom with two fingers." msgstr "Zoom med to fingrar." #: lib/logitech_receiver/settings_templates.py:1279 msgid "Pinch to zoom out." msgstr "Knip for å zooma ut." #: lib/logitech_receiver/settings_templates.py:1280 msgid "Spread to zoom in." msgstr "Sprei for å zooma inn." #: lib/logitech_receiver/settings_templates.py:1281 msgid "Zoom with three fingers." msgstr "Zoom med tre fingrar." #: lib/logitech_receiver/settings_templates.py:1282 msgid "Zoom with two fingers" msgstr "Zoom med to fingrar" #: lib/logitech_receiver/settings_templates.py:1300 msgid "Pixel zone" msgstr "Pikselsone" #: lib/logitech_receiver/settings_templates.py:1301 msgid "Ratio zone" msgstr "Forholdssone" #: lib/logitech_receiver/settings_templates.py:1302 msgid "Scale factor" msgstr "Skalafaktor" #: lib/logitech_receiver/settings_templates.py:1302 msgid "Sets the cursor speed." msgstr "Still inn markørfarten." #: lib/logitech_receiver/settings_templates.py:1306 msgid "Left" msgstr "Venstre" #: lib/logitech_receiver/settings_templates.py:1306 msgid "Left-most coordinate." msgstr "Koordinat lengst til venstre." #: lib/logitech_receiver/settings_templates.py:1307 msgid "Bottom" msgstr "Nederst" #: lib/logitech_receiver/settings_templates.py:1307 msgid "Bottom coordinate." msgstr "Nederste koordinat." #: lib/logitech_receiver/settings_templates.py:1308 msgid "Width" msgstr "Breidde" #: lib/logitech_receiver/settings_templates.py:1308 msgid "Width." msgstr "Breidde." #: lib/logitech_receiver/settings_templates.py:1309 msgid "Height" msgstr "Høgde" #: lib/logitech_receiver/settings_templates.py:1309 msgid "Height." msgstr "Høgde." #: lib/logitech_receiver/settings_templates.py:1310 msgid "Cursor speed." msgstr "Markørhastigheit." #: lib/logitech_receiver/settings_templates.py:1310 msgid "Scale" msgstr "Skala" #: lib/logitech_receiver/settings_templates.py:1316 msgid "Gestures" msgstr "Rørsler" #: lib/logitech_receiver/settings_templates.py:1317 msgid "Tweak the mouse/touchpad behaviour." msgstr "Juster musas/berøringsplata åtferd." #: lib/logitech_receiver/settings_templates.py:1333 msgid "Gestures Diversion" msgstr "Omdirigering av rørsler" #: lib/logitech_receiver/settings_templates.py:1334 msgid "Divert mouse/touchpad gestures." msgstr "Omdiriger musas/berøringsflatas rørsler." #: lib/logitech_receiver/settings_templates.py:1350 msgid "Gesture params" msgstr "Parametrar for rørsler" #: lib/logitech_receiver/settings_templates.py:1351 msgid "Change numerical parameters of a mouse/touchpad." msgstr "Endre numeriske parametrar for ei mus/berøringsplate." #: lib/logitech_receiver/settings_templates.py:1375 msgid "M-Key LEDs" msgstr "M-tast LED-ene" #: lib/logitech_receiver/settings_templates.py:1377 msgid "Control the M-Key LEDs." msgstr "Kontroller M-tast LED-ene." #: lib/logitech_receiver/settings_templates.py:1381 #: lib/logitech_receiver/settings_templates.py:1412 msgid "May need G Keys diverted to be effective." msgstr "Kan trenga omdirigering av G-tastar for å vera effektivt." #: lib/logitech_receiver/settings_templates.py:1387 #, python-format msgid "Lights up the %s key." msgstr "Lyser opp %s-tasten." #: lib/logitech_receiver/settings_templates.py:1406 msgid "MR-Key LED" msgstr "MR-tast LED" #: lib/logitech_receiver/settings_templates.py:1408 msgid "Control the MR-Key LED." msgstr "Kontroller MR-tast LED." #: lib/logitech_receiver/settings_templates.py:1429 msgid "Persistent Key/Button Mapping" msgstr "Vedvarande tast-/knapptilordning" #: lib/logitech_receiver/settings_templates.py:1431 msgid "Permanently change the mapping for the key or button." msgstr "Endre tilordninga for tasten eller knappen permanent." #: lib/logitech_receiver/settings_templates.py:1433 msgid "" "Changing important keys or buttons (such as for the left mouse button) can " "result in an unusable system." msgstr "" "Endring av viktige tastar eller knappar (som venstre museknapp) kan " "resultera i eit ubrukeleg system." #: lib/logitech_receiver/settings_templates.py:1490 msgid "Sidetone" msgstr "Sidetone" #: lib/logitech_receiver/settings_templates.py:1491 msgid "Set sidetone level." msgstr "Still inn sidetone nivå." #: lib/logitech_receiver/settings_templates.py:1500 msgid "Equalizer" msgstr "Equalizer" #: lib/logitech_receiver/settings_templates.py:1501 msgid "Set equalizer levels." msgstr "Still inn equalizer nivå." #: lib/logitech_receiver/settings_templates.py:1523 msgid "Hz" msgstr "Hz" #: lib/logitech_receiver/settings_templates.py:1529 msgid "Power Management" msgstr "Strømstyring" #: lib/logitech_receiver/settings_templates.py:1530 msgid "Power off in minutes (0 for never)." msgstr "Slå av i minutt (0 for aldri)." #: lib/logitech_receiver/settings_templates.py:1541 msgid "Brightness Control" msgstr "Lysstyrkekontroll" #: lib/logitech_receiver/settings_templates.py:1542 msgid "Control overall brightness" msgstr "Kontroller den generelle lysstyrken" #: lib/logitech_receiver/settings_templates.py:1585 #: lib/logitech_receiver/settings_templates.py:1639 msgid "LED Control" msgstr "LED-kontroll" #: lib/logitech_receiver/settings_templates.py:1586 #: lib/logitech_receiver/settings_templates.py:1640 msgid "Switch control of LED zones between device and Solaar" msgstr "Byt kontroll av LED-soner mellom eining og Solaar" #: lib/logitech_receiver/settings_templates.py:1601 #: lib/logitech_receiver/settings_templates.py:1650 msgid "LED Zone Effects" msgstr "LED-soneeffektar" #: lib/logitech_receiver/settings_templates.py:1602 #: lib/logitech_receiver/settings_templates.py:1651 msgid "LED Control needs to be set to Solaar to be effective." msgstr "LED-kontroll må setjast til Solaar for å vera effektiv." #: lib/logitech_receiver/settings_templates.py:1602 #: lib/logitech_receiver/settings_templates.py:1651 msgid "Set effect for LED Zone" msgstr "Still inn effekt for LED-sone" #: lib/logitech_receiver/settings_templates.py:1605 msgid "Speed" msgstr "Fart" #: lib/logitech_receiver/settings_templates.py:1606 msgid "Period" msgstr "Periode" #: lib/logitech_receiver/settings_templates.py:1607 msgid "Intensity" msgstr "Intensitet" #: lib/logitech_receiver/settings_templates.py:1608 msgid "Ramp" msgstr "Stigning" #: lib/logitech_receiver/settings_templates.py:1624 msgid "LEDs" msgstr "LEDs" #: lib/logitech_receiver/settings_templates.py:1661 msgid "Per-key Lighting" msgstr "Lys for kvar tast" #: lib/logitech_receiver/settings_templates.py:1662 msgid "Control per-key lighting." msgstr "Kontrollar lys for kvar tast." #: lib/solaar/ui/__init__.py:117 msgid "Another Solaar process is already running so just expose its window" msgstr "Ein annan Solaar-prosess køyrer allereie, så berre vis vindauget" #: lib/solaar/ui/about/model.py:36 msgid "" "Manages Logitech receivers,\n" "keyboards, mice, and tablets." msgstr "" "Administrerer Logitech-mottakarar,\n" "tastatur, mus og nettbrett." #: lib/solaar/ui/about/model.py:62 msgid "Additional Programming" msgstr "Ekstra programmering" #: lib/solaar/ui/about/model.py:63 msgid "GUI design" msgstr "GUI utsjånad" #: lib/solaar/ui/about/model.py:65 msgid "Testing" msgstr "Testing" #: lib/solaar/ui/about/model.py:73 msgid "Logitech documentation" msgstr "Logitech dokumentasjon" #: lib/solaar/ui/action.py:83 lib/solaar/ui/action.py:87 #: lib/solaar/ui/window.py:188 msgid "Unpair" msgstr "Fjern paring" #: lib/solaar/ui/action.py:86 lib/solaar/ui/diversion_rules.py:107 msgid "Cancel" msgstr "Avbryt" #: lib/solaar/ui/common.py:35 msgid "Permissions error" msgstr "Rettighetsfeil" #: lib/solaar/ui/common.py:37 #, python-format msgid "" "Found a Logitech receiver or device (%s), but did not have permission to " "open it." msgstr "" "Fann ein Logitech-mottakar eller eining (%s), men hadde ikkje løyve til å " "opna han." #: lib/solaar/ui/common.py:39 msgid "" "If you've just installed Solaar, try disconnecting the receiver or device " "and then reconnecting it." msgstr "" "Viss du nettopp har installert Solaar, kan du prøva å kopla frå mottakaren " "eller eininga og deretter kopla ho til igjen." #: lib/solaar/ui/common.py:42 msgid "Cannot connect to device error" msgstr "Kan ikkje kopla til eining-feil" #: lib/solaar/ui/common.py:44 #, python-format msgid "" "Found a Logitech receiver or device at %s, but encountered an error " "connecting to it." msgstr "" "Fann ein Logitech-mottakar eller -eining på %s, men oppdaga ein feil når han " "vart tilkopla." #: lib/solaar/ui/common.py:46 msgid "" "Try disconnecting the device and then reconnecting it or turning it off and " "then on." msgstr "" "Prøv å kopla frå eininga og deretter kopla ho til igjen eller slå ho av og " "på igjen." #: lib/solaar/ui/common.py:49 msgid "Unpairing failed" msgstr "Kunne ikkje bryta paringa" #: lib/solaar/ui/common.py:51 #, python-brace-format msgid "Failed to unpair %{device} from %{receiver}." msgstr "Mislykkast med å bryta paret %{device} og %{receiver}." #: lib/solaar/ui/common.py:56 msgid "The receiver returned an error, with no further details." msgstr "Mottakaren rapporterte ein feil, utan fleire detaljar." #: lib/solaar/ui/config_panel.py:228 msgid "Complete - ENTER to change" msgstr "Fullfør - ENTER for å endra" #: lib/solaar/ui/config_panel.py:228 msgid "Incomplete" msgstr "Ufullstendig" #: lib/solaar/ui/config_panel.py:475 lib/solaar/ui/config_panel.py:527 #, python-format msgid "%d value" msgid_plural "%d values" msgstr[0] "%d verdi" msgstr[1] "%d verdiar" #: lib/solaar/ui/config_panel.py:609 msgid "Changes allowed" msgstr "Endringar tillatne" #: lib/solaar/ui/config_panel.py:610 msgid "No changes allowed" msgstr "Ingen endringar tillatne" #: lib/solaar/ui/config_panel.py:611 msgid "Ignore this setting" msgstr "Ignorer denne innstillinga" #: lib/solaar/ui/config_panel.py:655 msgid "Working" msgstr "Suksess" #: lib/solaar/ui/config_panel.py:658 msgid "Read/write operation failed." msgstr "Lese-/skriveoperasjonen mislykkast." #: lib/solaar/ui/desktop_notifications.py:114 msgid "unspecified reason" msgstr "uspesifisert årsak" #: lib/solaar/ui/diversion_rules.py:69 msgid "Built-in rules" msgstr "Faste reglar" #: lib/solaar/ui/diversion_rules.py:69 msgid "User-defined rules" msgstr "Brukardefinerte reglar" #: lib/solaar/ui/diversion_rules.py:71 lib/solaar/ui/diversion_rules.py:1089 msgid "Rule" msgstr "Regel" #: lib/solaar/ui/diversion_rules.py:72 lib/solaar/ui/diversion_rules.py:348 #: lib/solaar/ui/diversion_rules.py:475 msgid "Sub-rule" msgstr "Underregel" #: lib/solaar/ui/diversion_rules.py:74 msgid "[empty]" msgstr "[tom]" #: lib/solaar/ui/diversion_rules.py:98 msgid "Make changes permanent?" msgstr "Gjera endringar permanente?" #: lib/solaar/ui/diversion_rules.py:103 msgid "Yes" msgstr "Ja" #: lib/solaar/ui/diversion_rules.py:105 msgid "No" msgstr "Nei" #: lib/solaar/ui/diversion_rules.py:110 msgid "If you choose No, changes will be lost when Solaar is closed." msgstr "Viss du vel Nei, vil endringar gå tapt når Solaar blir lukka." #: lib/solaar/ui/diversion_rules.py:239 msgid "Paste here" msgstr "Lim inn her" #: lib/solaar/ui/diversion_rules.py:241 msgid "Paste above" msgstr "Lim inn ovanfor" #: lib/solaar/ui/diversion_rules.py:243 msgid "Paste below" msgstr "Lim inn nedanfor" #: lib/solaar/ui/diversion_rules.py:249 msgid "Paste rule here" msgstr "Lim inn regel her" #: lib/solaar/ui/diversion_rules.py:251 msgid "Paste rule above" msgstr "Lim inn regel ovanfor" #: lib/solaar/ui/diversion_rules.py:253 msgid "Paste rule below" msgstr "Lim inn regel nedanfor" #: lib/solaar/ui/diversion_rules.py:257 msgid "Paste rule" msgstr "Lim inn regel" #: lib/solaar/ui/diversion_rules.py:272 msgid "Insert here" msgstr "Set inn her" #: lib/solaar/ui/diversion_rules.py:274 msgid "Insert above" msgstr "Set inn ovanfor" #: lib/solaar/ui/diversion_rules.py:276 msgid "Insert below" msgstr "Set inn nedanfor" #: lib/solaar/ui/diversion_rules.py:282 msgid "Insert new rule here" msgstr "Set inn ny regel her" #: lib/solaar/ui/diversion_rules.py:284 msgid "Insert new rule above" msgstr "Set inn ny regel ovanfor" #: lib/solaar/ui/diversion_rules.py:286 msgid "Insert new rule below" msgstr "Set inn ny regel nedanfor" #: lib/solaar/ui/diversion_rules.py:313 msgid "Flatten" msgstr "Utfalte" #: lib/solaar/ui/diversion_rules.py:346 msgid "Insert" msgstr "Set inn" #: lib/solaar/ui/diversion_rules.py:349 lib/solaar/ui/diversion_rules.py:477 #: lib/solaar/ui/diversion_rules.py:1121 msgid "Or" msgstr "Eller" #: lib/solaar/ui/diversion_rules.py:350 lib/solaar/ui/diversion_rules.py:476 #: lib/solaar/ui/diversion_rules.py:1107 msgid "And" msgstr "Og" #: lib/solaar/ui/diversion_rules.py:352 msgid "Condition" msgstr "Vilkår" #: lib/solaar/ui/diversion_rules.py:354 lib/solaar/ui/rule_conditions.py:145 msgid "Feature" msgstr "Karakteristikk" #: lib/solaar/ui/diversion_rules.py:355 lib/solaar/ui/rule_conditions.py:179 msgid "Report" msgstr "Rapport" #: lib/solaar/ui/diversion_rules.py:356 lib/solaar/ui/rule_conditions.py:60 msgid "Process" msgstr "Prosess" #: lib/solaar/ui/diversion_rules.py:357 msgid "Mouse process" msgstr "Musprosess" #: lib/solaar/ui/diversion_rules.py:358 lib/solaar/ui/rule_conditions.py:216 msgid "Modifiers" msgstr "Modifikatorer" #: lib/solaar/ui/diversion_rules.py:359 lib/solaar/ui/rule_conditions.py:268 msgid "Key" msgstr "Tast" #: lib/solaar/ui/diversion_rules.py:360 lib/solaar/ui/rule_conditions.py:309 msgid "KeyIsDown" msgstr "TastErNede" #: lib/solaar/ui/diversion_rules.py:361 lib/solaar/ui/diversion_rules.py:1414 msgid "Active" msgstr "Aktiv" #: lib/solaar/ui/diversion_rules.py:362 lib/solaar/ui/diversion_rules.py:1372 #: lib/solaar/ui/diversion_rules.py:1423 lib/solaar/ui/diversion_rules.py:1470 msgid "Device" msgstr "Eining" #: lib/solaar/ui/diversion_rules.py:363 lib/solaar/ui/diversion_rules.py:1449 msgid "Host" msgstr "Vert" #: lib/solaar/ui/diversion_rules.py:364 lib/solaar/ui/diversion_rules.py:1489 msgid "Setting" msgstr "Innstilling" #: lib/solaar/ui/diversion_rules.py:365 lib/solaar/ui/rule_conditions.py:324 #: lib/solaar/ui/rule_conditions.py:373 msgid "Test" msgstr "Test" #: lib/solaar/ui/diversion_rules.py:366 lib/solaar/ui/rule_conditions.py:498 msgid "Test bytes" msgstr "Test bytes" #: lib/solaar/ui/diversion_rules.py:367 lib/solaar/ui/rule_conditions.py:599 msgid "Mouse Gesture" msgstr "Muserørsle" #: lib/solaar/ui/diversion_rules.py:371 msgid "Action" msgstr "Handling" #: lib/solaar/ui/diversion_rules.py:373 lib/solaar/ui/rule_actions.py:131 msgid "Key press" msgstr "Tastetrykk" #: lib/solaar/ui/diversion_rules.py:374 lib/solaar/ui/rule_actions.py:182 msgid "Mouse scroll" msgstr "Muserulling" #: lib/solaar/ui/diversion_rules.py:375 lib/solaar/ui/rule_actions.py:243 msgid "Mouse click" msgstr "Museklikk" #: lib/solaar/ui/diversion_rules.py:376 msgid "Set" msgstr "Still inn" #: lib/solaar/ui/diversion_rules.py:377 lib/solaar/ui/rule_actions.py:313 msgid "Execute" msgstr "Utfør" #: lib/solaar/ui/diversion_rules.py:378 lib/solaar/ui/diversion_rules.py:1152 msgid "Later" msgstr "Seinare" #: lib/solaar/ui/diversion_rules.py:407 msgid "Insert new rule" msgstr "Set inn ny regel" #: lib/solaar/ui/diversion_rules.py:427 lib/solaar/ui/rule_actions.py:74 #: lib/solaar/ui/rule_actions.py:272 lib/solaar/ui/rule_conditions.py:546 msgid "Delete" msgstr "Slett" #: lib/solaar/ui/diversion_rules.py:449 msgid "Negate" msgstr "Omvendt" #: lib/solaar/ui/diversion_rules.py:473 msgid "Wrap with" msgstr "Pakk inn med" #: lib/solaar/ui/diversion_rules.py:503 msgid "Cut" msgstr "KLipp ut" #: lib/solaar/ui/diversion_rules.py:519 msgid "Paste" msgstr "Lim inn" #: lib/solaar/ui/diversion_rules.py:525 msgid "Copy" msgstr "Kopier" #: lib/solaar/ui/diversion_rules.py:534 msgid "Solaar Rule Editor" msgstr "Solaar regel editor" #: lib/solaar/ui/diversion_rules.py:634 msgid "Save changes" msgstr "Lagre endringar" #: lib/solaar/ui/diversion_rules.py:639 msgid "Discard changes" msgstr "Forkast endringar" #: lib/solaar/ui/diversion_rules.py:1070 msgid "This editor does not support the selected rule component yet." msgstr "Denne editoren støttar førebels ikkje den valde regelkomponenten." #: lib/solaar/ui/diversion_rules.py:1132 msgid "" "Number of seconds to delay. Delay between 0 and 1 is done with higher " "precision." msgstr "" "Talet på sekund med forseinking. Forseinking mellom 0 og 1 blir gjort med " "høgare presisjon." #: lib/solaar/ui/diversion_rules.py:1170 msgid "Not" msgstr "Ikkje" #: lib/solaar/ui/diversion_rules.py:1201 msgid "Toggle" msgstr "Veksle" #: lib/solaar/ui/diversion_rules.py:1202 msgid "True" msgstr "Sann" #: lib/solaar/ui/diversion_rules.py:1203 msgid "False" msgstr "Falsk" #: lib/solaar/ui/diversion_rules.py:1216 msgid "Unsupported setting" msgstr "Innstillinga blir ikkje støtta" #: lib/solaar/ui/diversion_rules.py:1378 lib/solaar/ui/diversion_rules.py:1397 #: lib/solaar/ui/diversion_rules.py:1476 lib/solaar/ui/diversion_rules.py:1731 #: lib/solaar/ui/diversion_rules.py:1749 msgid "Originating device" msgstr "Kildeeining" #: lib/solaar/ui/diversion_rules.py:1410 msgid "Device is active and its settings can be changed." msgstr "Eininga er aktiv og innstillingane kan endrast." #: lib/solaar/ui/diversion_rules.py:1419 msgid "Device that originated the current notification." msgstr "Eininga som gav det gjeldande varselet." #: lib/solaar/ui/diversion_rules.py:1432 msgid "Name of host computer." msgstr "Namn på vertsmaskin." #: lib/solaar/ui/diversion_rules.py:1503 msgid "Value" msgstr "Verdi" #: lib/solaar/ui/diversion_rules.py:1512 msgid "Item" msgstr "Punkt" #: lib/solaar/ui/diversion_rules.py:1791 msgid "Change setting on device" msgstr "Endre innstilling på eining" #: lib/solaar/ui/diversion_rules.py:1807 msgid "Setting on device" msgstr "Innstilling på eining" #: lib/solaar/ui/pair_window.py:37 lib/solaar/ui/pair_window.py:165 #, python-format msgid "%(receiver_name)s: pair new device" msgstr "%(receiver_name)s: par ny eining" #: lib/solaar/ui/pair_window.py:39 msgid "Bolt receivers are only compatible with Bolt devices." msgstr "Bolt-mottakarar er berre kompatible med Bolt-einingar." #: lib/solaar/ui/pair_window.py:41 msgid "Press a pairing button or key until the pairing light flashes quickly." msgstr "" "Trykk på samankoplingsknappen eller -tasten til samankoplingslampa blinkar " "raskt." #: lib/solaar/ui/pair_window.py:44 msgid "Unifying receivers are only compatible with Unifying devices." msgstr "Unifying-mottakarar er berre kompatible med Unifying-einingar." #: lib/solaar/ui/pair_window.py:46 msgid "Other receivers are only compatible with a few devices." msgstr "Andre mottakarar er berre kompatible med nokre få einingar." #: lib/solaar/ui/pair_window.py:48 msgid "For most devices, turn on the device you want to pair." msgstr "For dei fleste einingar slår du på eininga du vil para." #: lib/solaar/ui/pair_window.py:49 msgid "If the device is already turned on, turn it off and on again." msgstr "Viss eininga allereie er pålått, slå ho av og på igjen." #: lib/solaar/ui/pair_window.py:51 msgid "The device must not be paired with a nearby powered-on receiver." msgstr "Eininga må ikkje parast med ein pålått mottakar i nærleiken." #: lib/solaar/ui/pair_window.py:54 msgid "" "For devices with multiple channels, press, hold, and release the button for " "the channel you wish to pair\n" "or use the channel switch button to select a channel and then press, hold, " "and release the channel switch button." msgstr "" "For einingar med fleire kanalar, trykk, hald og slipp knappen for kanalen du " "ønskjer å para\n" "eller bruk kanalbrytarknappen for å velja ein kanal og trykk, hald og slepp " "kanalbrytarknappen." #: lib/solaar/ui/pair_window.py:61 msgid "The channel indicator light should be blinking rapidly." msgstr "Kanalindikatorlampa skal blinka raskt." #: lib/solaar/ui/pair_window.py:65 #, python-format msgid "" "\n" "\n" "This receiver has %d pairing remaining." msgid_plural "" "\n" "\n" "This receiver has %d pairings remaining." msgstr[0] "" "\n" "\n" "Denne mottakaren har %d gjenståande paring." msgstr[1] "" "\n" "\n" "Denne mottakaren har %d gjenståande paringar." #: lib/solaar/ui/pair_window.py:71 msgid "" "\n" "Cancelling at this point will not use up a pairing." msgstr "" "\n" "Kansellering no vil ikkje bruka opp ein paring." #: lib/solaar/ui/pair_window.py:166 #, python-format msgid "Enter passcode on %(name)s." msgstr "Skriv inn passordet på %(name)s." #: lib/solaar/ui/pair_window.py:169 #, python-format msgid "Type %(passcode)s and then press the enter key." msgstr "Skriv inn %(passcode)s og trykk deretter på enter-tasten." #: lib/solaar/ui/pair_window.py:174 msgid "left" msgstr "venstre" #: lib/solaar/ui/pair_window.py:174 msgid "right" msgstr "høgre" #: lib/solaar/ui/pair_window.py:176 #, python-format msgid "" "Press %(code)s\n" "and then press left and right buttons simultaneously." msgstr "" "Trykk %(code)s\n" "og trykk deretter på venstre og høgre knapp samtidig." #: lib/solaar/ui/pair_window.py:219 msgid "The wireless link is not encrypted" msgstr "Den trådlause koplinga er ukryptert" #: lib/solaar/ui/pair_window.py:224 msgid "Found a new device:" msgstr "Fant ny eining:" #: lib/solaar/ui/pair_window.py:245 msgid "Pairing failed" msgstr "Paringa feila" #: lib/solaar/ui/pair_window.py:247 msgid "Make sure your device is within range, and has a decent battery charge." msgstr "" "Forsikre deg om at eininga er innan rekkevidde, og at den er tilstrekkeleg " "opplada." #: lib/solaar/ui/pair_window.py:249 msgid "A new device was detected, but it is not compatible with this receiver." msgstr "" "Ei ny eining vart påvist, men ho er ikkje kompatibel med denne mottakaren." #: lib/solaar/ui/pair_window.py:251 msgid "More paired devices than receiver can support." msgstr "Fleir para einingar enn mottakaren kan støtte." #: lib/solaar/ui/pair_window.py:253 msgid "No further details are available about the error." msgstr "Ingen ytterligare detaljar er tilgjengeleg om feilen." #: lib/solaar/ui/rule_actions.py:48 msgid "" "Simulate a chorded key click or depress or release.\n" "On Wayland requires write access to /dev/uinput." msgstr "" "Simuler ein chorded (?) tast, klikk eller trykk ned eller slipp.\n" "På Wayland krev dette skrivetilgang til /dev/uinput." #: lib/solaar/ui/rule_actions.py:53 msgid "Add key" msgstr "Legg til tast" #: lib/solaar/ui/rule_actions.py:56 msgid "Click" msgstr "Klikk" #: lib/solaar/ui/rule_actions.py:59 msgid "Depress" msgstr "Nedtrykk" #: lib/solaar/ui/rule_actions.py:62 msgid "Release" msgstr "Utløs" #: lib/solaar/ui/rule_actions.py:146 msgid "" "Simulate a mouse scroll.\n" "On Wayland requires write access to /dev/uinput." msgstr "" "Simuler ei muserulling.\n" "På Wayland krev dette skrivetilgang til /dev/uinput." #: lib/solaar/ui/rule_actions.py:202 msgid "" "Simulate a mouse click.\n" "On Wayland requires write access to /dev/uinput." msgstr "" "Simuler eit museklikk.\n" "På Wayland krev dette skrivetilgang til /dev/uinput." #: lib/solaar/ui/rule_actions.py:205 msgid "Button" msgstr "Knapp" #: lib/solaar/ui/rule_actions.py:206 msgid "Count and Action" msgstr "Antall og handling" #: lib/solaar/ui/rule_actions.py:255 msgid "Execute a command with arguments." msgstr "Utfør ein kommando med argument." #: lib/solaar/ui/rule_actions.py:258 msgid "Add argument" msgstr "Legg til argument" #: lib/solaar/ui/rule_conditions.py:43 msgid "X11 active process. For use in X11 only." msgstr "X11 aktiv prosess. Berre til bruk i X11." #: lib/solaar/ui/rule_conditions.py:73 msgid "X11 mouse process. For use in X11 only." msgstr "X11-museprosess. Berre til bruk i X11." #: lib/solaar/ui/rule_conditions.py:90 msgid "MouseProcess" msgstr "MusProsess" #: lib/solaar/ui/rule_conditions.py:114 msgid "Feature name of notification triggering rule processing." msgstr "Funksjonsnamn på varsel som utløyste regelbehandling." #: lib/solaar/ui/rule_conditions.py:160 msgid "Report number of notification triggering rule processing." msgstr "Rapportnummer for varsling som utløyser regelbehandling." #: lib/solaar/ui/rule_conditions.py:192 msgid "Active keyboard modifiers. Not always available in Wayland." msgstr "Aktive tastaturmodifikatorer. Ikkje alltid tilgjengeleg i Wayland." #: lib/solaar/ui/rule_conditions.py:232 msgid "" "Diverted key or button depressed or released.\n" "Use the Key/Button Diversion and Divert G Keys settings to divert keys and " "buttons." msgstr "" "Omdirigert tast eller knapp som er trykt eller sleppt.\n" "Bruk innstillingane for omdirigering av tast/knapp og omdiriger G-tastar for " "å vidaresenda tastar og knappar." #: lib/solaar/ui/rule_conditions.py:241 msgid "Key down" msgstr "Tast ned" #: lib/solaar/ui/rule_conditions.py:244 msgid "Key up" msgstr "Tast opp" #: lib/solaar/ui/rule_conditions.py:284 msgid "" "Diverted key or button is currently down.\n" "Use the Key/Button Diversion and Divert G Keys settings to divert keys and " "buttons." msgstr "" "Omdirigert tast eller knapp er nede.\n" "Bruk innstillingane for omdirigering av tast/knapp og omdirigerte G-tastar " "for å vidaresenda tastar og knappar." #: lib/solaar/ui/rule_conditions.py:322 msgid "Test condition on notification triggering rule processing." msgstr "Testvilkår på varsel som utløyser regelbehandling." #: lib/solaar/ui/rule_conditions.py:326 msgid "Parameter" msgstr "Parameter" #: lib/solaar/ui/rule_conditions.py:399 msgid "begin (inclusive)" msgstr "byrj (inklusiv)" #: lib/solaar/ui/rule_conditions.py:400 msgid "end (exclusive)" msgstr "slutt (eksklusiv)" #: lib/solaar/ui/rule_conditions.py:408 msgid "range" msgstr "område" #: lib/solaar/ui/rule_conditions.py:411 msgid "minimum" msgstr "minimum" #: lib/solaar/ui/rule_conditions.py:412 msgid "maximum" msgstr "maksimum" #: lib/solaar/ui/rule_conditions.py:414 #, python-format msgid "bytes %(0)d to %(1)d, ranging from %(2)d to %(3)d" msgstr "byte %(0)d til %(1)d, fra %(2)d til %(3)d" #: lib/solaar/ui/rule_conditions.py:417 lib/solaar/ui/rule_conditions.py:418 msgid "mask" msgstr "maske" #: lib/solaar/ui/rule_conditions.py:419 #, python-format msgid "bytes %(0)d to %(1)d, mask %(2)d" msgstr "byte %(0)d til %(1)d, maske %(2)d" #: lib/solaar/ui/rule_conditions.py:428 msgid "" "Bit or range test on bytes in notification message triggering rule " "processing." msgstr "" "Bit- eller områdetest på bytes i varselmelding som utløyste regelbehandling." #: lib/solaar/ui/rule_conditions.py:438 msgid "type" msgstr "type" #: lib/solaar/ui/rule_conditions.py:526 msgid "" "Mouse gesture with optional initiating button followed by zero or more mouse " "movements." msgstr "" "Muserørsle med valfri startknapp etterfølgd av null eller fleire muserørsler." #: lib/solaar/ui/rule_conditions.py:531 msgid "Add movement" msgstr "Legg til rørsle" #: lib/solaar/ui/tray.py:49 msgid "No supported device found" msgstr "Ingen støtta einingar er funne" #: lib/solaar/ui/tray.py:54 lib/solaar/ui/window.py:308 #, python-format msgid "About %s" msgstr "Om %s" #: lib/solaar/ui/tray.py:55 lib/solaar/ui/window.py:306 #, python-format msgid "Quit %s" msgstr "Avslutt %s" #: lib/solaar/ui/tray.py:270 lib/solaar/ui/tray.py:278 msgid "no receiver" msgstr "ingen mottakar" #: lib/solaar/ui/tray.py:291 lib/solaar/ui/tray.py:296 msgid "offline" msgstr "avslått" #: lib/solaar/ui/tray.py:294 msgid "no status" msgstr "ingen status" #: lib/solaar/ui/window.py:90 msgid "Scanning" msgstr "Søker" #: lib/solaar/ui/window.py:121 msgid "Battery" msgstr "Batteri" #: lib/solaar/ui/window.py:124 msgid "Wireless Link" msgstr "Trådlaus kopling" #: lib/solaar/ui/window.py:128 msgid "Lighting" msgstr "Belysning" #: lib/solaar/ui/window.py:162 msgid "Show Technical Details" msgstr "Vis tekniske detaljar" #: lib/solaar/ui/window.py:178 msgid "Pair new device" msgstr "Par ny eining" #: lib/solaar/ui/window.py:196 msgid "Select a device" msgstr "Vel ei eining" #: lib/solaar/ui/window.py:311 msgid "Rule Editor" msgstr "Regel editor" #: lib/solaar/ui/window.py:504 msgid "Path" msgstr "Sti" #: lib/solaar/ui/window.py:506 msgid "USB ID" msgstr "USB-ID" #: lib/solaar/ui/window.py:509 lib/solaar/ui/window.py:511 #: lib/solaar/ui/window.py:526 lib/solaar/ui/window.py:528 msgid "Serial" msgstr "Seriell" #: lib/solaar/ui/window.py:515 msgid "Index" msgstr "Indeks" #: lib/solaar/ui/window.py:517 msgid "Wireless PID" msgstr "Trådlaus-PID" #: lib/solaar/ui/window.py:519 msgid "Product ID" msgstr "Produkt-ID" #: lib/solaar/ui/window.py:521 msgid "Protocol" msgstr "Protokoll" #: lib/solaar/ui/window.py:521 msgid "Unknown" msgstr "Ukjend" #: lib/solaar/ui/window.py:523 msgid "Polling rate" msgstr "Spørjarate" #: lib/solaar/ui/window.py:530 msgid "Unit ID" msgstr "Einings-ID" #: lib/solaar/ui/window.py:544 msgid "Notifications" msgstr "Varslar" #: lib/solaar/ui/window.py:588 msgid "No device paired." msgstr "Inga eining para." #: lib/solaar/ui/window.py:597 #, python-format msgid "Up to %(max_count)s device can be paired to this receiver." msgid_plural "Up to %(max_count)s devices can be paired to this receiver." msgstr[0] "Inntil %(max_count)s eining kan bli para med denne mottakaren." msgstr[1] "Inntil %(max_count)s einingar kan bli para med denne mottakaren." #: lib/solaar/ui/window.py:608 #, python-format msgid "This receiver has %d pairing remaining." msgid_plural "This receiver has %d pairings remaining." msgstr[0] "Denne mottakaren har %d gjenståande paring." msgstr[1] "Denne mottakaren har %d gjenståande paringer." #: lib/solaar/ui/window.py:665 msgid "Battery Voltage" msgstr "Batterispenning" #: lib/solaar/ui/window.py:667 msgid "Voltage reported by battery" msgstr "Spenning rapportert av batteri" #: lib/solaar/ui/window.py:669 msgid "Battery Level" msgstr "Batterinivå" #: lib/solaar/ui/window.py:671 msgid "Approximate level reported by battery" msgstr "Omtrentleg nivå rapportert frå batteri" #: lib/solaar/ui/window.py:678 lib/solaar/ui/window.py:680 msgid "next reported " msgstr "neste rapportert " #: lib/solaar/ui/window.py:681 msgid " and next level to be reported." msgstr " og neste nivå som blir rapportert." #: lib/solaar/ui/window.py:697 msgid "encrypted" msgstr "kryptert" #: lib/solaar/ui/window.py:699 msgid "The wireless link between this device and its receiver is encrypted." msgstr "Den trådlause tilkoplinga mellom eininga og mottakaren er kryptert." #: lib/solaar/ui/window.py:701 msgid "not encrypted" msgstr "ikkje kryptert" #: lib/solaar/ui/window.py:705 msgid "" "The wireless link between this device and its receiver is not encrypted.\n" "This is a security issue for pointing devices, and a major security issue " "for text-input devices." msgstr "" "Den trådlause koplinga mellom denne eininga og mottakaren er ikkje " "kryptert.\n" "Dette er eit tryggingsproblem for peikeeiningar, og eit stort " "tryggingsproblem for einingar for tekstinndata." #: lib/solaar/ui/window.py:721 #, python-format msgid "%(light_level)d lux" msgstr "%(light_level)d lux" #~ msgid "" #~ "\n" #~ "\n" #~ "This receiver has %d pairing(s) remaining." #~ msgstr "" #~ "\n" #~ "\n" #~ "Denne mottakaren har %d gjenståande paringar." #, python-format #~ msgid "%(rate)d ms (%(rate_hz)dHz)" #~ msgstr "%(rate)d ms (%(rate_hz)dHz)" #~ msgid "Actions" #~ msgstr "Handlingar" #~ msgid "" #~ "Automatically switch the mouse wheel between ratchet and freespin mode.\n" #~ "The mouse wheel is always free at 0, and always locked at 50" #~ msgstr "" #~ "Byt mushjulet automatisk mellom sperre- og frispinnmodus.\n" #~ "Mushjulet er alltid fritt på 0, og blir alltid låste på 50" #, python-format #~ msgid "Battery: %(level)s" #~ msgstr "Batteri: %(level)s" #, python-format #~ msgid "Battery: %(percent)d%%" #~ msgstr "Batteri: %(percent)d%%" #~ msgid "Divert G Keys" #~ msgstr "Omdiriger G tastar" #~ msgid "Effectively turns off thumb scrolling in Linux." #~ msgstr "Slår i praksis av tommelrulling i Linux." #~ msgid "Effectively turns off wheel scrolling in Linux." #~ msgstr "Slår i praksis av hjulrulling i Linux." #~ msgid "" #~ "Enable onboard profiles, which often control report rate and keyboard " #~ "lighting" #~ msgstr "" #~ "Aktiver ein innebygd profil, som kontrollerer rapporthyppigheit, " #~ "følsomheit, og knappehandlingar" #~ msgid "" #~ "For more information see the Solaar installation directions\n" #~ "at https://pwr-solaar.github.io/Solaar/installation" #~ msgstr "" #~ "For meir informasjon, sjå Solaar-installasjonsanvisningene\n" #~ "på https://pwr-solaar.github.io/Solaar/installation" #, python-format #~ msgid "" #~ "Found a Logitech Receiver (%s), but did not have permission to open it." #~ msgstr "Fant ein Logitech-mottakar (%s), men manglar rettar til å opna den." #~ msgid "Frequency of device polling, in milliseconds" #~ msgstr "Frekvensen til rapportar om einingsrørsler" #~ msgid "HID++ Scrolling" #~ msgstr "HID++ rulling" #~ msgid "HID++ Thumb Scrolling" #~ msgstr "HID++ tommelrulling" #~ msgid "HID++ mode for horizontal scroll with the thumb wheel." #~ msgstr "HID++ modus for horisontal rulling med tommelhjulet." #~ msgid "HID++ mode for vertical scroll with the wheel." #~ msgstr "HID++ modus for vertikal rulling med hjulet." #~ msgid "High Resolution Scrolling" #~ msgstr "Høgoppløyseleg rulling" #~ msgid "High Resolution Wheel Invert" #~ msgstr "Inverter høg rulleoppløysning" #~ msgid "High-sensitivity wheel invert direction for vertical scroll." #~ msgstr "Inverter retning for høg vertikal rulleoppløysning." #~ msgid "If the device is already turned on, turn if off and on again." #~ msgstr "Viss eininga allereie er slått på, slå ho av og på igjen." #~ msgid "" #~ "If you've just installed Solaar, try removing the receiver and plugging " #~ "it back in." #~ msgstr "" #~ "Viss du nettopp har installert Solaar, forsøk å fjerna mottakaren og " #~ "plugg den inn igjen." #~ msgid "Invert thumb scroll direction." #~ msgstr "Inverter retning på tommelrulling." #, python-format #~ msgid "Lighting: %(level)s lux" #~ msgstr "Belysning: %(level)s lux" #~ msgid "" #~ "Make G keys send GKEY HID++ notifications (which trigger Solaar rules but " #~ "are otherwise ignored)." #~ msgstr "" #~ "Få G-tastane til å senda HID++-varslar (som utløyser Solaar-reglar, men " #~ "som elles blir ignorerte)." #~ msgid "No Logitech receiver found" #~ msgstr "Ingen Logitech-mottakar funnet" #~ msgid "Number of seconds to delay." #~ msgstr "Talet på sekund med forseinking." #~ msgid "Only one device can be paired to this receiver." #~ msgstr "Berre éi eining kan parast med denne mottakaren." #~ msgid "Polling Rate (ms)" #~ msgstr "Rapporthyppigheit" #~ msgid "" #~ "Shows status of devices connected\n" #~ "through wireless Logitech receivers." #~ msgstr "" #~ "Viser status for einingar tilkopla\n" #~ "via trådlause Logitech-mottakere." #~ msgid "Smooth Scrolling" #~ msgstr "Mjuk rulling" #~ msgid "Solaar depends on a udev file that is not present" #~ msgstr "Solaar er avhengig av ein udev-fil som ikkje finnast" #~ msgid "The receiver only supports %d paired device(s)." #~ msgstr "Mottakaren kan kun støtte %d para eining(er)." #~ msgid "The receiver was unplugged." #~ msgstr "Mottakaren vart fjerna." #~ msgid "" #~ "The wireless link between this device and its receiver is not encrypted.\n" #~ "\n" #~ "For pointing devices (mice, trackballs, trackpads), this is a minor " #~ "security issue.\n" #~ "\n" #~ "It is, however, a major security issue for text-input devices (keyboards, " #~ "numpads),\n" #~ "because typed text can be sniffed inconspicuously by 3rd parties within " #~ "range." #~ msgstr "" #~ "Den trådlause koplinga mellom denne eininga og mottakaren er ukryptert.\n" #~ "\n" #~ "For peikeeeiningar (mus, styrekuler, styreflater) er dette eit mindre " #~ "tryggingsproblem.\n" #~ "\n" #~ "Det er likevel eit viktig tryggingsproblem for tekstinndataeiningar " #~ "(tastatur, numpads),\n" #~ "fordi maskinskriven tekst kan bli sniffa umerka av tredjepartar innan " #~ "rekkevidde." #~ msgid "This receiver has %d pairing(s) remaining." #~ msgstr "Denne mottakaren har %d gjenståande paring(er)." #~ msgid "Thumb Scroll Invert" #~ msgstr "Inverter tommelrulling" #~ msgid "Turn illumination on or off on keyboard." #~ msgstr "Slå belysning på eller av for tastaturet." #~ msgid "Turn on the device you want to pair." #~ msgstr "Slå på eininga du ynsker å pare." #~ msgid "USB id" #~ msgstr "USB-id" #~ msgid "Unexpected device number (%s) in notification %s." #~ msgstr "Uventa einingsnummer (%s) i varsel %s." #~ msgid "Wheel Resolution" #~ msgstr "Hjuloppløysning" #~ msgid "height" #~ msgstr "høgde" #~ msgid "last known" #~ msgstr "sist kjend" #~ msgid "next " #~ msgstr "neste " #~ msgid "none" #~ msgstr "ingen" #~ msgid "unknown" #~ msgstr "ukjend" #~ msgid "width" #~ msgstr "breidde" Solaar-1.1.14/po/pl.po000066400000000000000000001672761473525213700144440ustar00rootroot00000000000000# Polish translations for solaar package. # Copyright (C) 2013 THE solaar'S COPYRIGHT HOLDER # This file is distributed under the same license as the solaar package. # Automatically generated, 2013. # msgid "" msgstr "" "Project-Id-Version: solaar 1.1.14\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2024-12-23 17:02+0100\n" "PO-Revision-Date: 2024-12-23 17:06+0100\n" "Last-Translator: Matthaiks\n" "Language-Team: none\n" "Language: pl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 " "|| n%100>=20) ? 1 : 2);\n" "X-Generator: Poedit 3.5\n" #: lib/logitech_receiver/base_usb.py:48 msgid "Bolt Receiver" msgstr "Odbiornik Bolt" #: lib/logitech_receiver/base_usb.py:60 msgid "Unifying Receiver" msgstr "Odbiornik Unifying" #: lib/logitech_receiver/base_usb.py:71 lib/logitech_receiver/base_usb.py:83 #: lib/logitech_receiver/base_usb.py:96 lib/logitech_receiver/base_usb.py:109 msgid "Nano Receiver" msgstr "Odbiornik Nano" #: lib/logitech_receiver/base_usb.py:121 msgid "Lightspeed Receiver" msgstr "Odbiornik Lightspeed" #: lib/logitech_receiver/base_usb.py:131 msgid "EX100 Receiver 27 Mhz" msgstr "Odbiornik EX100 27 MHz" #: lib/logitech_receiver/common.py:648 #, python-format msgid "Battery: %(level)s (%(status)s)" msgstr "Akumulator: %(level)s (%(status)s)" #: lib/logitech_receiver/common.py:651 #, python-format msgid "Battery: %(percent)d%% (%(status)s)" msgstr "Akumulator: %(percent)d%% (%(status)s)" #: lib/logitech_receiver/hidpp20.py:953 #: lib/logitech_receiver/settings_templates.py:293 msgid "Disabled" msgstr "Wyłączone" #: lib/logitech_receiver/hidpp20.py:954 msgid "Static" msgstr "Statyczny" #: lib/logitech_receiver/hidpp20.py:955 msgid "Pulse" msgstr "Puls" #: lib/logitech_receiver/hidpp20.py:956 msgid "Cycle" msgstr "Cykl" #: lib/logitech_receiver/hidpp20.py:957 msgid "Boot" msgstr "Rozruch" #: lib/logitech_receiver/hidpp20.py:958 msgid "Demo" msgstr "Demo" #: lib/logitech_receiver/hidpp20.py:960 msgid "Breathe" msgstr "Oddech" #: lib/logitech_receiver/hidpp20.py:963 msgid "Ripple" msgstr "Plusk" #: lib/logitech_receiver/hidpp20.py:964 msgid "Decomposition" msgstr "Dekompozycja" #: lib/logitech_receiver/hidpp20.py:965 msgid "Signature1" msgstr "Podpis1" #: lib/logitech_receiver/hidpp20.py:966 msgid "Signature2" msgstr "Podpis2" #: lib/logitech_receiver/hidpp20.py:967 msgid "CycleS" msgstr "Cykle" #: lib/logitech_receiver/hidpp20.py:1031 msgid "Unknown Location" msgstr "Nieznana lokalizacja" #: lib/logitech_receiver/hidpp20.py:1032 msgid "Primary" msgstr "Podstawowy" #: lib/logitech_receiver/hidpp20.py:1033 msgid "Logo" msgstr "Logo" #: lib/logitech_receiver/hidpp20.py:1034 msgid "Left Side" msgstr "Lewa strona" #: lib/logitech_receiver/hidpp20.py:1035 msgid "Right Side" msgstr "Prawa strona" #: lib/logitech_receiver/hidpp20.py:1036 msgid "Combined" msgstr "Łączony" #: lib/logitech_receiver/hidpp20.py:1037 msgid "Primary 1" msgstr "Podstawowy 1" #: lib/logitech_receiver/hidpp20.py:1038 msgid "Primary 2" msgstr "Podstawowy 2" #: lib/logitech_receiver/hidpp20.py:1039 msgid "Primary 3" msgstr "Podstawowy 3" #: lib/logitech_receiver/hidpp20.py:1040 msgid "Primary 4" msgstr "Podstawowy 4" #: lib/logitech_receiver/hidpp20.py:1041 msgid "Primary 5" msgstr "Podstawowy 5" #: lib/logitech_receiver/hidpp20.py:1042 msgid "Primary 6" msgstr "Podstawowy 6" #: lib/logitech_receiver/i18n.py:28 msgid "empty" msgstr "pusty" #: lib/logitech_receiver/i18n.py:29 msgid "critical" msgstr "krytyczny" #: lib/logitech_receiver/i18n.py:30 msgid "low" msgstr "niski" #: lib/logitech_receiver/i18n.py:31 msgid "average" msgstr "przeciętny" #: lib/logitech_receiver/i18n.py:32 msgid "good" msgstr "dobry" #: lib/logitech_receiver/i18n.py:33 msgid "full" msgstr "pełny" #: lib/logitech_receiver/i18n.py:35 msgid "discharging" msgstr "rozładowywanie" #: lib/logitech_receiver/i18n.py:36 msgid "recharging" msgstr "ładowanie" #: lib/logitech_receiver/i18n.py:37 msgid "charging" msgstr "ładowanie" #: lib/logitech_receiver/i18n.py:38 msgid "not charging" msgstr "brak ładowania" #: lib/logitech_receiver/i18n.py:39 msgid "almost full" msgstr "prawie pełny" #: lib/logitech_receiver/i18n.py:40 msgid "charged" msgstr "naładowany" #: lib/logitech_receiver/i18n.py:41 msgid "slow recharge" msgstr "powolne ładowanie" #: lib/logitech_receiver/i18n.py:42 msgid "invalid battery" msgstr "nieodpowiedni akumulator" #: lib/logitech_receiver/i18n.py:43 msgid "thermal error" msgstr "błąd termiczny" #: lib/logitech_receiver/i18n.py:44 msgid "error" msgstr "błąd" #: lib/logitech_receiver/i18n.py:45 msgid "standard" msgstr "standardowe" #: lib/logitech_receiver/i18n.py:46 msgid "fast" msgstr "szybkie" #: lib/logitech_receiver/i18n.py:47 msgid "slow" msgstr "powolne" #: lib/logitech_receiver/i18n.py:49 msgid "device timeout" msgstr "upłynął limit czasu urządzenia" #: lib/logitech_receiver/i18n.py:50 msgid "device not supported" msgstr "urządzenie nie jest obsługiwane" #: lib/logitech_receiver/i18n.py:51 msgid "too many devices" msgstr "za dużo urządzeń" #: lib/logitech_receiver/i18n.py:52 msgid "sequence timeout" msgstr "upłynął limit czasu sekwencji" #: lib/logitech_receiver/i18n.py:54 msgid "Firmware" msgstr "Firmware" #: lib/logitech_receiver/i18n.py:55 msgid "Bootloader" msgstr "Bootloader" #: lib/logitech_receiver/i18n.py:56 msgid "Hardware" msgstr "Sprzęt" #: lib/logitech_receiver/i18n.py:57 msgid "Other" msgstr "Inne" #: lib/logitech_receiver/i18n.py:59 msgid "Left Button" msgstr "Przycisk lewy" #: lib/logitech_receiver/i18n.py:60 msgid "Right Button" msgstr "Przycisk prawy" #: lib/logitech_receiver/i18n.py:61 msgid "Middle Button" msgstr "Przycisk środkowy" #: lib/logitech_receiver/i18n.py:62 msgid "Back Button" msgstr "Przycisk wstecz" #: lib/logitech_receiver/i18n.py:63 msgid "Forward Button" msgstr "Przycisk dalej" #: lib/logitech_receiver/i18n.py:64 msgid "Mouse Gesture Button" msgstr "Przycisk gestów myszy" #: lib/logitech_receiver/i18n.py:65 msgid "Smart Shift" msgstr "SmartShift" #: lib/logitech_receiver/i18n.py:66 msgid "DPI Switch" msgstr "Przełącznik DPI" #: lib/logitech_receiver/i18n.py:67 msgid "Left Tilt" msgstr "Pochylenie w lewo" #: lib/logitech_receiver/i18n.py:68 msgid "Right Tilt" msgstr "Pochylenie w prawo" #: lib/logitech_receiver/i18n.py:69 msgid "Left Click" msgstr "Kliknięcie LPM" #: lib/logitech_receiver/i18n.py:70 msgid "Right Click" msgstr "Kliknięcie PPM" #: lib/logitech_receiver/i18n.py:71 msgid "Mouse Middle Button" msgstr "Przycisk środkowy myszy" #: lib/logitech_receiver/i18n.py:72 msgid "Mouse Back Button" msgstr "Przycisk wstecz myszy" #: lib/logitech_receiver/i18n.py:73 msgid "Mouse Forward Button" msgstr "Przycisk dalej myszy" #: lib/logitech_receiver/i18n.py:74 msgid "Gesture Button Navigation" msgstr "Nawigacja przyciskiem gestów" #: lib/logitech_receiver/i18n.py:75 msgid "Mouse Scroll Left Button" msgstr "Lewy przycisk kółka myszy" #: lib/logitech_receiver/i18n.py:76 msgid "Mouse Scroll Right Button" msgstr "Prawy przycisk kółka myszy" #: lib/logitech_receiver/i18n.py:78 msgid "pressed" msgstr "wciśnięty" #: lib/logitech_receiver/i18n.py:79 msgid "released" msgstr "zwolniony" #: lib/logitech_receiver/notifications.py:86 #: lib/logitech_receiver/notifications.py:138 msgid "pairing lock is closed" msgstr "blokada sparowania jest zamknięta" #: lib/logitech_receiver/notifications.py:86 #: lib/logitech_receiver/notifications.py:138 msgid "pairing lock is open" msgstr "blokada sparowania jest otwarta" #: lib/logitech_receiver/notifications.py:103 msgid "discovery lock is closed" msgstr "blokada wykrywania jest zamknięta" #: lib/logitech_receiver/notifications.py:103 msgid "discovery lock is open" msgstr "blokada wykrywania jest otwarta" #: lib/logitech_receiver/notifications.py:232 lib/solaar/listener.py:247 msgid "connected" msgstr "podłączone" #: lib/logitech_receiver/notifications.py:232 lib/solaar/listener.py:247 msgid "disconnected" msgstr "rozłączone" #: lib/logitech_receiver/notifications.py:258 msgid "unpaired" msgstr "niesparowane" #: lib/logitech_receiver/notifications.py:305 msgid "powered on" msgstr "włączone" #: lib/logitech_receiver/receiver.py:383 msgid "No paired devices." msgstr "Brak sparowanych urządzeń." #: lib/logitech_receiver/receiver.py:385 lib/solaar/ui/window.py:590 #, python-format msgid "%(count)s paired device." msgid_plural "%(count)s paired devices." msgstr[0] "%(count)s sparowane urządzenie." msgstr[1] "%(count)s sparowane urządzenia." msgstr[2] "%(count)s sparowanych urządzeń." #: lib/logitech_receiver/settings.py:614 msgid "register" msgstr "rejestr" #: lib/logitech_receiver/settings.py:628 lib/logitech_receiver/settings.py:663 msgid "feature" msgstr "funkcja" #: lib/logitech_receiver/settings_templates.py:132 msgid "Swap Fx function" msgstr "Zamień funkcję Fx" #: lib/logitech_receiver/settings_templates.py:135 msgid "" "When set, the F1..F12 keys will activate their special function,\n" "and you must hold the FN key to activate their standard function." msgstr "" "Gdy włączona, po wciśnięciu klawiszy F1..F12 aktywowana zostanie\n" "ich funkcja pomocnicza. Aby aktywować funkcję standardową, należy\n" "przytrzymać klawisz FN." #: lib/logitech_receiver/settings_templates.py:140 msgid "" "When unset, the F1..F12 keys will activate their standard function,\n" "and you must hold the FN key to activate their special function." msgstr "" "Gdy wyłączona, po wciśnięciu klawiszy F1..F12 aktywowana zostanie\n" "ich funkcja standardowa. Aby aktywować funkcję pomocniczą, należy\n" "przytrzymać klawisz FN." #: lib/logitech_receiver/settings_templates.py:148 msgid "Hand Detection" msgstr "Wykrywanie dłoni" #: lib/logitech_receiver/settings_templates.py:149 msgid "Turn on illumination when the hands hover over the keyboard." msgstr "Włącz podświetlenie, gdy dłonie znajdą się nad klawiaturą." #: lib/logitech_receiver/settings_templates.py:156 msgid "Scroll Wheel Smooth Scrolling" msgstr "Płynne przewijanie kółkiem przewijania" #: lib/logitech_receiver/settings_templates.py:157 #: lib/logitech_receiver/settings_templates.py:404 #: lib/logitech_receiver/settings_templates.py:433 msgid "High-sensitivity mode for vertical scroll with the wheel." msgstr "" "Tryb wysokiej rozdzielczości do przewijania pionowego\n" "przy użyciu kółka myszy." #: lib/logitech_receiver/settings_templates.py:164 msgid "Side Scrolling" msgstr "Przewijanie na boki" #: lib/logitech_receiver/settings_templates.py:166 msgid "" "When disabled, pushing the wheel sideways sends custom button events\n" "instead of the standard side-scrolling events." msgstr "" "Gdy wyłączone, przesuwanie kółka na boki powoduje wysyłanie " "niestandardowych\n" "zdarzeń przycisków zamiast standardowych zdarzeń przewijania na boki." #: lib/logitech_receiver/settings_templates.py:176 msgid "Sensitivity (DPI - older mice)" msgstr "Czułość (DPI - starsze myszy)" #: lib/logitech_receiver/settings_templates.py:177 #: lib/logitech_receiver/settings_templates.py:981 #: lib/logitech_receiver/settings_templates.py:1009 msgid "Mouse movement sensitivity" msgstr "Czułość ruchu myszy" #: lib/logitech_receiver/settings_templates.py:250 msgid "Backlight Timed" msgstr "Podświetlenie czasowe" #: lib/logitech_receiver/settings_templates.py:251 #: lib/logitech_receiver/settings_templates.py:391 msgid "Set illumination time for keyboard." msgstr "Ustaw czas podświetlenia klawiatury." #: lib/logitech_receiver/settings_templates.py:262 msgid "Backlight" msgstr "Podświetlenie" #: lib/logitech_receiver/settings_templates.py:263 msgid "" "Illumination level on keyboard. Changes made are only applied in Manual " "mode." msgstr "" "Poziom podświetlenia klawiatury. Wprowadzone zmiany zostaną zastosowane " "wyłącznie w trybie ręcznym." #: lib/logitech_receiver/settings_templates.py:295 msgid "Automatic" msgstr "Automatyczne" #: lib/logitech_receiver/settings_templates.py:297 msgid "Manual" msgstr "Ręczne" #: lib/logitech_receiver/settings_templates.py:299 msgid "Enabled" msgstr "Włączone" #: lib/logitech_receiver/settings_templates.py:305 msgid "Backlight Level" msgstr "Poziom podświetlenia" #: lib/logitech_receiver/settings_templates.py:306 msgid "Illumination level on keyboard when in Manual mode." msgstr "Poziom podświetlenia klawiatury w trybie ręcznym." #: lib/logitech_receiver/settings_templates.py:363 msgid "Backlight Delay Hands Out" msgstr "Opóźnienie podświetlenia bez rąk" #: lib/logitech_receiver/settings_templates.py:364 msgid "" "Delay in seconds until backlight fades out with hands away from keyboard." msgstr "" "Opóźnienie w sekundach, aż podświetlenie zniknie, gdy ręce będą oddalone od " "klawiatury." #: lib/logitech_receiver/settings_templates.py:372 msgid "Backlight Delay Hands In" msgstr "Opóźnienie podświetlenia z rękoma" #: lib/logitech_receiver/settings_templates.py:373 msgid "Delay in seconds until backlight fades out with hands near keyboard." msgstr "" "Opóźnienie w sekundach, aż podświetlenie zniknie, gdy ręce będą blisko " "klawiatury." #: lib/logitech_receiver/settings_templates.py:381 msgid "Backlight Delay Powered" msgstr "Opóźnienie podświetlenia z zasilanem" #: lib/logitech_receiver/settings_templates.py:382 msgid "Delay in seconds until backlight fades out with external power." msgstr "" "Opóźnienie w sekundach, aż podświetlenie zaniknie przy zasilaniu zewnętrznym." #: lib/logitech_receiver/settings_templates.py:390 msgid "Backlight (Seconds)" msgstr "Podświetlenie (sekundy)" #: lib/logitech_receiver/settings_templates.py:402 msgid "Scroll Wheel High Resolution" msgstr "Wysoka rozdzielczość kółka przewijania" #: lib/logitech_receiver/settings_templates.py:406 #: lib/logitech_receiver/settings_templates.py:435 msgid "Set to ignore if scrolling is abnormally fast or slow" msgstr "" "Ustaw, aby ignorować, jeśli przewijanie jest nienormalnie szybkie lub wolne" #: lib/logitech_receiver/settings_templates.py:413 #: lib/logitech_receiver/settings_templates.py:444 msgid "Scroll Wheel Diversion" msgstr "Przekierowanie kółka przewijania" #: lib/logitech_receiver/settings_templates.py:415 msgid "" "Make scroll wheel send LOWRES_WHEEL HID++ notifications (which trigger " "Solaar rules but are otherwise ignored)." msgstr "" "Spraw, aby kółko przewijania wysyłało powiadomienia LOWRES_WHEEL HID++ " "(które wyzwalają reguły Solaar, ale w przeciwnym razie są ignorowane)." #: lib/logitech_receiver/settings_templates.py:422 msgid "Scroll Wheel Direction" msgstr "Kierunek kółka przewijania" #: lib/logitech_receiver/settings_templates.py:423 msgid "Invert direction for vertical scroll with wheel." msgstr "Odwróć kierunek przewijania w pionie za pomocą kółka." #: lib/logitech_receiver/settings_templates.py:431 msgid "Scroll Wheel Resolution" msgstr "Rozdzielczość kółka przewijania" #: lib/logitech_receiver/settings_templates.py:446 msgid "" "Make scroll wheel send HIRES_WHEEL HID++ notifications (which trigger Solaar " "rules but are otherwise ignored)." msgstr "" "Spraw, aby kółko przewijania wysyłało powiadomienia HIRES_WHEEL HID++ (które " "wyzwalają reguły Solaar, ale w przeciwnym razie są ignorowane)." #: lib/logitech_receiver/settings_templates.py:455 msgid "Sensitivity (Pointer Speed)" msgstr "Czułość (prędkość wskaźnika)" #: lib/logitech_receiver/settings_templates.py:456 msgid "Speed multiplier for mouse (256 is normal multiplier)." msgstr "Mnożnik prędkości myszy (256 to normalny mnożnik)." #: lib/logitech_receiver/settings_templates.py:466 msgid "Thumb Wheel Diversion" msgstr "Przekierowanie kółka obsługiwanego kciukiem" #: lib/logitech_receiver/settings_templates.py:468 msgid "" "Make thumb wheel send THUMB_WHEEL HID++ notifications (which trigger Solaar " "rules but are otherwise ignored)." msgstr "" "Spraw, aby kółko obsługiwane kciukiem wysyłało powiadomienia THUMB_WHEEL HID+" "+ (które wyzwalają reguły Solaar, ale w przeciwnym razie są ignorowane)." #: lib/logitech_receiver/settings_templates.py:477 msgid "Thumb Wheel Direction" msgstr "Kierunek kółka obsługiwanego kciukiem" #: lib/logitech_receiver/settings_templates.py:478 msgid "Invert thumb wheel scroll direction." msgstr "Odwróć kierunek przewijania kółkiem obsługiwanym kciukiem." #: lib/logitech_receiver/settings_templates.py:498 msgid "Onboard Profiles" msgstr "Profile wbudowane" #: lib/logitech_receiver/settings_templates.py:499 msgid "" "Enable an onboard profile, which controls report rate, sensitivity, and " "button actions" msgstr "" "Włącz wbudowany profil, który kontroluje częstotliwość raportowania, czułość " "i czynności przycisków" #: lib/logitech_receiver/settings_templates.py:543 #: lib/logitech_receiver/settings_templates.py:576 msgid "Report Rate" msgstr "Szybkość raportów" #: lib/logitech_receiver/settings_templates.py:545 #: lib/logitech_receiver/settings_templates.py:578 msgid "Frequency of device movement reports" msgstr "Częstotliwość raportów o ruchu urządzenia" #: lib/logitech_receiver/settings_templates.py:545 #: lib/logitech_receiver/settings_templates.py:578 #: lib/logitech_receiver/settings_templates.py:1009 #: lib/logitech_receiver/settings_templates.py:1383 #: lib/logitech_receiver/settings_templates.py:1414 msgid "May need Onboard Profiles set to Disable to be effective." msgstr "Aby działało, może być konieczne wyłączenie profili wbudowanych." #: lib/logitech_receiver/settings_templates.py:606 msgid "Divert crown events" msgstr "Przekieruj zdarzenia korony" #: lib/logitech_receiver/settings_templates.py:607 msgid "" "Make crown send CROWN HID++ notifications (which trigger Solaar rules but " "are otherwise ignored)." msgstr "" "Spraw, aby korona wysyłała powiadomienia CROWN HID++ (które wyzwalają reguły " "Solaar, ale w przeciwnym razie są ignorowane)." #: lib/logitech_receiver/settings_templates.py:615 msgid "Crown smooth scroll" msgstr "Płynne przewijanie koroną" #: lib/logitech_receiver/settings_templates.py:616 msgid "Set crown smooth scroll" msgstr "Ustaw płynne przewijanie koroną" #: lib/logitech_receiver/settings_templates.py:624 msgid "Divert G and M Keys" msgstr "Przekieruj klawisze G i M" #: lib/logitech_receiver/settings_templates.py:625 msgid "" "Make G and M keys send HID++ notifications (which trigger Solaar rules but " "are otherwise ignored)." msgstr "" "Spraw, aby klawisze G i M wysyłały powiadomienia HID++ (które wyzwalają " "reguły Solaar, ale w przeciwnym razie są ignorowane)." #: lib/logitech_receiver/settings_templates.py:639 msgid "Scroll Wheel Ratcheted" msgstr "Zapadkowe kółko przewijania" #: lib/logitech_receiver/settings_templates.py:640 msgid "" "Switch the mouse wheel between speed-controlled ratcheting and always " "freespin." msgstr "" "Przełącz kółko myszy między zapadką z kontrolowaną prędkością a zawsze " "swobodnym obrotem." #: lib/logitech_receiver/settings_templates.py:642 msgid "Freespinning" msgstr "Obrót swobodny" #: lib/logitech_receiver/settings_templates.py:642 msgid "Ratcheted" msgstr "Obrót zapadkowy" #: lib/logitech_receiver/settings_templates.py:649 msgid "Scroll Wheel Ratchet Speed" msgstr "Prędkość zapadki kółka przewijania" #: lib/logitech_receiver/settings_templates.py:651 msgid "" "Use the mouse wheel speed to switch between ratcheted and freespinning.\n" "The mouse wheel is always ratcheted at 50." msgstr "" "Użyj prędkości kółka myszy, aby przełączać się między obrotem zapadkowym a " "swobodnym.\n" "Kółko myszy jest zawsze ustawione na 50." #: lib/logitech_receiver/settings_templates.py:705 msgid "Key/Button Actions" msgstr "Czynności klawiszy/przycisków" #: lib/logitech_receiver/settings_templates.py:707 msgid "Change the action for the key or button." msgstr "Zmień czynność klawisza lub przycisku." #: lib/logitech_receiver/settings_templates.py:709 msgid "Overridden by diversion." msgstr "Omijane przez przekierowanie." #: lib/logitech_receiver/settings_templates.py:711 msgid "" "Changing important actions (such as for the left mouse button) can result in " "an unusable system." msgstr "" "Zmiana ważnych czynności (np. lewego przycisku myszy) może spowodować, że " "system będzie nieużywalny." #: lib/logitech_receiver/settings_templates.py:886 msgid "Key/Button Diversion" msgstr "Przekierowanie klawiszy/przycisków" #: lib/logitech_receiver/settings_templates.py:887 msgid "" "Make the key or button send HID++ notifications (Diverted) or initiate Mouse " "Gestures or Sliding DPI" msgstr "" "Spraw, aby klawisz lub przycisk wysyłał powiadomienia HID++ (przekierowany) " "lub inicjował gesty myszy lub przesuwnie DPI" #: lib/logitech_receiver/settings_templates.py:890 #: lib/logitech_receiver/settings_templates.py:891 #: lib/logitech_receiver/settings_templates.py:892 msgid "Diverted" msgstr "Przekierowany" #: lib/logitech_receiver/settings_templates.py:890 #: lib/logitech_receiver/settings_templates.py:891 msgid "Mouse Gestures" msgstr "Gesty myszy" #: lib/logitech_receiver/settings_templates.py:890 #: lib/logitech_receiver/settings_templates.py:891 #: lib/logitech_receiver/settings_templates.py:892 msgid "Regular" msgstr "Zwykły" #: lib/logitech_receiver/settings_templates.py:890 msgid "Sliding DPI" msgstr "Przesuwnie DPI" #: lib/logitech_receiver/settings_templates.py:980 #: lib/logitech_receiver/settings_templates.py:1008 msgid "Sensitivity (DPI)" msgstr "Czułość (DPI)" #: lib/logitech_receiver/settings_templates.py:1085 msgid "Sensitivity Switching" msgstr "Przełączanie czułości" #: lib/logitech_receiver/settings_templates.py:1087 msgid "" "Switch the current sensitivity and the remembered sensitivity when the key " "or button is pressed.\n" "If there is no remembered sensitivity, just remember the current sensitivity" msgstr "" "Przełącz bieżącą czułość i zapamiętaną czułość po naciśnięciu klawisza lub " "przycisku.\n" "Jeśli nie ma zapamiętanej czułości, po prostu zapamiętaj bieżącą czułość" #: lib/logitech_receiver/settings_templates.py:1091 msgid "Off" msgstr "Wyłączone" #: lib/logitech_receiver/settings_templates.py:1122 msgid "Disable keys" msgstr "Wyłącz klawisze" #: lib/logitech_receiver/settings_templates.py:1123 msgid "Disable specific keyboard keys." msgstr "Wyłącz określone klawisze klawiatury." #: lib/logitech_receiver/settings_templates.py:1126 #, python-format msgid "Disables the %s key." msgstr "Wyłącza klawisz %s." #: lib/logitech_receiver/settings_templates.py:1139 #: lib/logitech_receiver/settings_templates.py:1196 msgid "Set OS" msgstr "Ustaw system operacyjny" #: lib/logitech_receiver/settings_templates.py:1140 #: lib/logitech_receiver/settings_templates.py:1197 msgid "Change keys to match OS." msgstr "Zmień klawisze, aby pasowały do systemu operacyjnego." #: lib/logitech_receiver/settings_templates.py:1209 msgid "Change Host" msgstr "Zmiana hosta" #: lib/logitech_receiver/settings_templates.py:1210 msgid "Switch connection to a different host" msgstr "Przełącz połączenie na innego hosta" #: lib/logitech_receiver/settings_templates.py:1234 msgid "Performs a left click." msgstr "Wykonuje kliknięcie lewym przyciskiem myszy." #: lib/logitech_receiver/settings_templates.py:1234 msgid "Single tap" msgstr "Pojedyncze stuknięcie" #: lib/logitech_receiver/settings_templates.py:1235 msgid "Performs a right click." msgstr "Wykonuje kliknięcie prawym przyciskiem myszy." #: lib/logitech_receiver/settings_templates.py:1235 msgid "Single tap with two fingers" msgstr "Pojedyncze stuknięcie dwoma palcami" #: lib/logitech_receiver/settings_templates.py:1236 msgid "Single tap with three fingers" msgstr "Pojedyncze stuknięcie trzema palcami" #: lib/logitech_receiver/settings_templates.py:1240 msgid "Double tap" msgstr "Podwójne stuknięcie" #: lib/logitech_receiver/settings_templates.py:1240 msgid "Performs a double click." msgstr "Wykonuje podwójne kliknięcie." #: lib/logitech_receiver/settings_templates.py:1241 msgid "Double tap with two fingers" msgstr "Podwójne stuknięcie dwoma palcami" #: lib/logitech_receiver/settings_templates.py:1242 msgid "Double tap with three fingers" msgstr "Podwójne stuknięcie trzema palcami" #: lib/logitech_receiver/settings_templates.py:1245 msgid "Drags items by dragging the finger after double tapping." msgstr "" "Przeciąga elementy poprzez przeciąganie palcem po dwukrotnym stuknięciu." #: lib/logitech_receiver/settings_templates.py:1245 msgid "Tap and drag" msgstr "Stuknięcie i przeciągnięcie" #: lib/logitech_receiver/settings_templates.py:1247 msgid "Tap and drag with two fingers" msgstr "Stuknięcie i przeciągnięcie dwoma palcami" #: lib/logitech_receiver/settings_templates.py:1248 msgid "Drags items by dragging the fingers after double tapping." msgstr "" "Przeciąga elementy poprzez przeciąganie palcami po dwukrotnym stuknięciu." #: lib/logitech_receiver/settings_templates.py:1250 msgid "Tap and drag with three fingers" msgstr "Stuknięcie i przeciągnięcie trzema palcami" #: lib/logitech_receiver/settings_templates.py:1253 msgid "Suppress tap and edge gestures" msgstr "Stłumienie gestów stukania i krawędziowych" #: lib/logitech_receiver/settings_templates.py:1254 msgid "Disables tap and edge gestures (equivalent to pressing Fn+LeftClick)." msgstr "" "Wyłącza gesty stukania i krawędziowe (odpowiednik naciśnięcia Fn + lewy " "przycisk myszy)." #: lib/logitech_receiver/settings_templates.py:1256 msgid "Scroll with one finger" msgstr "Przewijanie jednym palcem" #: lib/logitech_receiver/settings_templates.py:1256 #: lib/logitech_receiver/settings_templates.py:1257 #: lib/logitech_receiver/settings_templates.py:1260 msgid "Scrolls." msgstr "Przewija." #: lib/logitech_receiver/settings_templates.py:1257 #: lib/logitech_receiver/settings_templates.py:1260 msgid "Scroll with two fingers" msgstr "Przewijanie dwoma palcami" #: lib/logitech_receiver/settings_templates.py:1258 msgid "Scroll horizontally with two fingers" msgstr "Przewijanie w poziomie dwoma palcami" #: lib/logitech_receiver/settings_templates.py:1258 msgid "Scrolls horizontally." msgstr "Przewija w poziomie." #: lib/logitech_receiver/settings_templates.py:1259 msgid "Scroll vertically with two fingers" msgstr "Przewijanie w pionie dwoma palcami" #: lib/logitech_receiver/settings_templates.py:1259 msgid "Scrolls vertically." msgstr "Przewija w pionie." #: lib/logitech_receiver/settings_templates.py:1261 msgid "Inverts the scrolling direction." msgstr "Odwraca kierunek przewijania." #: lib/logitech_receiver/settings_templates.py:1261 msgid "Natural scrolling" msgstr "Przewijanie naturalne" #: lib/logitech_receiver/settings_templates.py:1262 msgid "Enables the thumbwheel." msgstr "Włącza kółko obsługiwane kciukiem." #: lib/logitech_receiver/settings_templates.py:1262 msgid "Thumbwheel" msgstr "Kółko obsługiwane kciukiem" #: lib/logitech_receiver/settings_templates.py:1273 #: lib/logitech_receiver/settings_templates.py:1277 msgid "Swipe from the top edge" msgstr "Przesuwanie od górnej krawędzi" #: lib/logitech_receiver/settings_templates.py:1274 msgid "Swipe from the left edge" msgstr "Przesuwanie od lewej krawędzi" #: lib/logitech_receiver/settings_templates.py:1275 msgid "Swipe from the right edge" msgstr "Przesuwanie od prawej krawędzi" #: lib/logitech_receiver/settings_templates.py:1276 msgid "Swipe from the bottom edge" msgstr "Przesuwanie od dolnej krawędzi" #: lib/logitech_receiver/settings_templates.py:1278 msgid "Swipe two fingers from the left edge" msgstr "Przesuwanie dwoma palcami od lewej krawędzi" #: lib/logitech_receiver/settings_templates.py:1279 msgid "Swipe two fingers from the right edge" msgstr "Przesuwanie dwoma palcami od prawej krawędzi" #: lib/logitech_receiver/settings_templates.py:1280 msgid "Swipe two fingers from the bottom edge" msgstr "Przesuwanie dwoma palcami od dolnej krawędzi" #: lib/logitech_receiver/settings_templates.py:1281 msgid "Swipe two fingers from the top edge" msgstr "Przesuwanie dwoma palcami od górnej krawędzi" #: lib/logitech_receiver/settings_templates.py:1282 #: lib/logitech_receiver/settings_templates.py:1286 msgid "Pinch to zoom out; spread to zoom in." msgstr "Ściśnięcie, aby pomniejszyć; rozłożenie, aby powiększyć." #: lib/logitech_receiver/settings_templates.py:1282 msgid "Zoom with two fingers." msgstr "Zoom dwoma palcami." #: lib/logitech_receiver/settings_templates.py:1283 msgid "Pinch to zoom out." msgstr "Ściśnięcie, aby pomniejszyć." #: lib/logitech_receiver/settings_templates.py:1284 msgid "Spread to zoom in." msgstr "Rozłożenie, aby powiększyć." #: lib/logitech_receiver/settings_templates.py:1285 msgid "Zoom with three fingers." msgstr "Zoom trzema palcami." #: lib/logitech_receiver/settings_templates.py:1286 msgid "Zoom with two fingers" msgstr "Zoom dwoma palcami" #: lib/logitech_receiver/settings_templates.py:1304 msgid "Pixel zone" msgstr "Strefa pikseli" #: lib/logitech_receiver/settings_templates.py:1305 msgid "Ratio zone" msgstr "Strefa współczynnika" #: lib/logitech_receiver/settings_templates.py:1306 msgid "Scale factor" msgstr "Współczynnik skalowania" #: lib/logitech_receiver/settings_templates.py:1306 msgid "Sets the cursor speed." msgstr "Ustawia szybkość kursora." #: lib/logitech_receiver/settings_templates.py:1310 msgid "Left" msgstr "Lewo" #: lib/logitech_receiver/settings_templates.py:1310 msgid "Left-most coordinate." msgstr "Współrzędna najbardziej z lewej." #: lib/logitech_receiver/settings_templates.py:1311 msgid "Bottom" msgstr "Dół" #: lib/logitech_receiver/settings_templates.py:1311 msgid "Bottom coordinate." msgstr "Współrzędna dolna." #: lib/logitech_receiver/settings_templates.py:1312 msgid "Width" msgstr "Szerokość" #: lib/logitech_receiver/settings_templates.py:1312 msgid "Width." msgstr "Szerokość." #: lib/logitech_receiver/settings_templates.py:1313 msgid "Height" msgstr "Wysokość" #: lib/logitech_receiver/settings_templates.py:1313 msgid "Height." msgstr "Wysokość." #: lib/logitech_receiver/settings_templates.py:1314 msgid "Cursor speed." msgstr "Prędkość kursora." #: lib/logitech_receiver/settings_templates.py:1314 msgid "Scale" msgstr "Skala" #: lib/logitech_receiver/settings_templates.py:1320 msgid "Gestures" msgstr "Gesty" #: lib/logitech_receiver/settings_templates.py:1321 msgid "Tweak the mouse/touchpad behaviour." msgstr "Dostosuj zachowania myszy/panelu dotykowego." #: lib/logitech_receiver/settings_templates.py:1337 msgid "Gestures Diversion" msgstr "Przekierowanie gestów" #: lib/logitech_receiver/settings_templates.py:1338 msgid "Divert mouse/touchpad gestures." msgstr "Przekieruj gesty myszy/panelu dotykowego." #: lib/logitech_receiver/settings_templates.py:1354 msgid "Gesture params" msgstr "Parametry gestu" #: lib/logitech_receiver/settings_templates.py:1355 msgid "Change numerical parameters of a mouse/touchpad." msgstr "Zmień parametry numeryczne myszy/panelu dotykowego." #: lib/logitech_receiver/settings_templates.py:1379 msgid "M-Key LEDs" msgstr "Diody LED klawiszy M" #: lib/logitech_receiver/settings_templates.py:1381 msgid "Control the M-Key LEDs." msgstr "Kontroluj diody LED klawiszy M." #: lib/logitech_receiver/settings_templates.py:1385 #: lib/logitech_receiver/settings_templates.py:1416 msgid "May need G Keys diverted to be effective." msgstr "Aby działało, może być konieczne włączenie przekierowania klawiszy G." #: lib/logitech_receiver/settings_templates.py:1391 #, python-format msgid "Lights up the %s key." msgstr "Zapala klawisz %s." #: lib/logitech_receiver/settings_templates.py:1410 msgid "MR-Key LED" msgstr "Dioda LED klawisza MR" #: lib/logitech_receiver/settings_templates.py:1412 msgid "Control the MR-Key LED." msgstr "Kontroluj diody LED klawiszy MR." #: lib/logitech_receiver/settings_templates.py:1433 msgid "Persistent Key/Button Mapping" msgstr "Trwałe mapowanie klawiszy/przycisków" #: lib/logitech_receiver/settings_templates.py:1435 msgid "Permanently change the mapping for the key or button." msgstr "Trwale zmień mapowania klawisza lub przycisku." #: lib/logitech_receiver/settings_templates.py:1437 msgid "" "Changing important keys or buttons (such as for the left mouse button) can " "result in an unusable system." msgstr "" "Zmiana ważnych klawiszy lub przycisków (takich jak lewy przycisk myszy) może " "spowodować, że system stanie się bezużyteczny." #: lib/logitech_receiver/settings_templates.py:1494 msgid "Sidetone" msgstr "Efekt lokalny" #: lib/logitech_receiver/settings_templates.py:1495 msgid "Set sidetone level." msgstr "Ustaw poziom efektu lokalnego." #: lib/logitech_receiver/settings_templates.py:1504 msgid "Equalizer" msgstr "Korektor" #: lib/logitech_receiver/settings_templates.py:1505 msgid "Set equalizer levels." msgstr "Ustaw poziomy korektora." #: lib/logitech_receiver/settings_templates.py:1527 msgid "Hz" msgstr "Hz" #: lib/logitech_receiver/settings_templates.py:1533 msgid "Power Management" msgstr "Zarządzanie energią" #: lib/logitech_receiver/settings_templates.py:1534 msgid "Power off in minutes (0 for never)." msgstr "Wyłączenie zasilania w minutach (0 oznacza nigdy)." #: lib/logitech_receiver/settings_templates.py:1545 msgid "Brightness Control" msgstr "Sterowanie jasnością" #: lib/logitech_receiver/settings_templates.py:1546 msgid "Control overall brightness" msgstr "Steruj ogólną jasnością" #: lib/logitech_receiver/settings_templates.py:1589 #: lib/logitech_receiver/settings_templates.py:1643 msgid "LED Control" msgstr "Sterowanie diodami LED" #: lib/logitech_receiver/settings_templates.py:1590 #: lib/logitech_receiver/settings_templates.py:1644 msgid "Switch control of LED zones between device and Solaar" msgstr "Przełącz sterowanie strefami LED pomiędzy urządzeniem a Solaar" #: lib/logitech_receiver/settings_templates.py:1605 #: lib/logitech_receiver/settings_templates.py:1654 msgid "LED Zone Effects" msgstr "Efekty strefowe diod LED" #: lib/logitech_receiver/settings_templates.py:1606 #: lib/logitech_receiver/settings_templates.py:1655 msgid "LED Control needs to be set to Solaar to be effective." msgstr "" "Sterowanie diodami LED musi być ustawione na Solaar, aby było skuteczne." #: lib/logitech_receiver/settings_templates.py:1606 #: lib/logitech_receiver/settings_templates.py:1655 msgid "Set effect for LED Zone" msgstr "Ustaw efekt dla strefy diod LED" #: lib/logitech_receiver/settings_templates.py:1609 msgid "Speed" msgstr "Szybkość" #: lib/logitech_receiver/settings_templates.py:1610 msgid "Period" msgstr "Okres" #: lib/logitech_receiver/settings_templates.py:1611 msgid "Intensity" msgstr "Intensywność" #: lib/logitech_receiver/settings_templates.py:1612 msgid "Ramp" msgstr "Narastanie" #: lib/logitech_receiver/settings_templates.py:1628 msgid "LEDs" msgstr "Diody LED" #: lib/logitech_receiver/settings_templates.py:1665 msgid "Per-key Lighting" msgstr "Podświetlenie poszczególnych klawiszy" #: lib/logitech_receiver/settings_templates.py:1666 msgid "Control per-key lighting." msgstr "Steruj podświetleniem poszczególnych klawiszy." #: lib/solaar/ui/__init__.py:117 msgid "Another Solaar process is already running so just expose its window" msgstr "Inny proces programu Solaar już działa, więc po prostu pokaż jego okno" #: lib/solaar/ui/about/model.py:36 msgid "" "Manages Logitech receivers,\n" "keyboards, mice, and tablets." msgstr "" "Zarządza odbiornikami, klawiaturami,\n" "myszami i tabletami firmy Logitech." #: lib/solaar/ui/about/model.py:63 msgid "Additional Programming" msgstr "Dodatkowe programowanie" #: lib/solaar/ui/about/model.py:64 msgid "GUI design" msgstr "Projekt GUI" #: lib/solaar/ui/about/model.py:66 msgid "Testing" msgstr "Testowanie" #: lib/solaar/ui/about/model.py:74 msgid "Logitech documentation" msgstr "Dokumentacja firmy Logitech" #: lib/solaar/ui/action.py:83 lib/solaar/ui/action.py:87 #: lib/solaar/ui/window.py:188 msgid "Unpair" msgstr "Usuń sparowanie" #: lib/solaar/ui/action.py:86 lib/solaar/ui/diversion_rules.py:107 msgid "Cancel" msgstr "Anuluj" #: lib/solaar/ui/common.py:35 msgid "Permissions error" msgstr "Błąd uprawnień" #: lib/solaar/ui/common.py:37 #, python-format msgid "" "Found a Logitech receiver or device (%s), but did not have permission to " "open it." msgstr "" "Znaleziono odbiornik lub urządzenie firmy Logitech (%s), ale nie ma " "uprawnień do jego otwarcia." #: lib/solaar/ui/common.py:39 msgid "" "If you've just installed Solaar, try disconnecting the receiver or device " "and then reconnecting it." msgstr "" "Jeśli właśnie zainstalowano Solaar, spróbuj odłączyć odbiornik lub " "urządzenie, a następnie podłączyć je ponownie." #: lib/solaar/ui/common.py:42 msgid "Cannot connect to device error" msgstr "Błąd łączenia się z urządzeniem" #: lib/solaar/ui/common.py:44 #, python-format msgid "" "Found a Logitech receiver or device at %s, but encountered an error " "connecting to it." msgstr "" "Znaleziono odbiornik lub urządzenie firmy Logitech w %s, ale wystąpił błąd " "podczas łączenia się z nim." #: lib/solaar/ui/common.py:46 msgid "" "Try disconnecting the device and then reconnecting it or turning it off and " "then on." msgstr "" "Spróbuj odłączyć urządzenie, a następnie podłączyć je ponownie lub wyłączyć " "i ponownie włączyć." #: lib/solaar/ui/common.py:49 msgid "Unpairing failed" msgstr "Usunięcie sparowania nie powiodło się" #: lib/solaar/ui/common.py:51 #, python-brace-format msgid "Failed to unpair %{device} from %{receiver}." msgstr "Nie udało się sparować %{device} z %{receiver}." #: lib/solaar/ui/common.py:56 msgid "The receiver returned an error, with no further details." msgstr "Odbiornik zwrócił błąd bez dodatkowych informacji." #: lib/solaar/ui/config_panel.py:228 msgid "Complete - ENTER to change" msgstr "Kompletne - ENTER, aby zmienić" #: lib/solaar/ui/config_panel.py:228 msgid "Incomplete" msgstr "Niekompletne" #: lib/solaar/ui/config_panel.py:475 lib/solaar/ui/config_panel.py:527 #, python-format msgid "%d value" msgid_plural "%d values" msgstr[0] "%d wartość" msgstr[1] "%d wartości" msgstr[2] "%d wartości" #: lib/solaar/ui/config_panel.py:609 msgid "Changes allowed" msgstr "Zmiany dozwolone" #: lib/solaar/ui/config_panel.py:610 msgid "No changes allowed" msgstr "Zmiany niedozwolone" #: lib/solaar/ui/config_panel.py:611 msgid "Ignore this setting" msgstr "Ignoruj to ustawienie" #: lib/solaar/ui/config_panel.py:655 msgid "Working" msgstr "Pracuję" #: lib/solaar/ui/config_panel.py:658 msgid "Read/write operation failed." msgstr "Operacja odczytu/zapisu nie powiodła się." #: lib/solaar/ui/desktop_notifications.py:114 msgid "unspecified reason" msgstr "nieokreślony powód" #: lib/solaar/ui/diversion_rules.py:69 msgid "Built-in rules" msgstr "Wbudowane reguły" #: lib/solaar/ui/diversion_rules.py:69 msgid "User-defined rules" msgstr "Reguły zdefiniowane przez użytkownika" #: lib/solaar/ui/diversion_rules.py:71 lib/solaar/ui/diversion_rules.py:1089 msgid "Rule" msgstr "Reguła" #: lib/solaar/ui/diversion_rules.py:72 lib/solaar/ui/diversion_rules.py:348 #: lib/solaar/ui/diversion_rules.py:475 msgid "Sub-rule" msgstr "Reguła podrzędna" #: lib/solaar/ui/diversion_rules.py:74 msgid "[empty]" msgstr "[pusta]" #: lib/solaar/ui/diversion_rules.py:98 msgid "Make changes permanent?" msgstr "Wprowadzić zmiany na stałe?" #: lib/solaar/ui/diversion_rules.py:103 msgid "Yes" msgstr "Tak" #: lib/solaar/ui/diversion_rules.py:105 msgid "No" msgstr "Nie" #: lib/solaar/ui/diversion_rules.py:110 msgid "If you choose No, changes will be lost when Solaar is closed." msgstr "Jeśli wybierzesz Nie, zmiany zostaną utracone po zamknięciu Solaar." #: lib/solaar/ui/diversion_rules.py:239 msgid "Paste here" msgstr "Wklej tutaj" #: lib/solaar/ui/diversion_rules.py:241 msgid "Paste above" msgstr "Wklej powyżej" #: lib/solaar/ui/diversion_rules.py:243 msgid "Paste below" msgstr "Wklej poniżej" #: lib/solaar/ui/diversion_rules.py:249 msgid "Paste rule here" msgstr "Wklej regułę tutaj" #: lib/solaar/ui/diversion_rules.py:251 msgid "Paste rule above" msgstr "Wklej regułę powyżej" #: lib/solaar/ui/diversion_rules.py:253 msgid "Paste rule below" msgstr "Wklej regułę poniżej" #: lib/solaar/ui/diversion_rules.py:257 msgid "Paste rule" msgstr "Wklej regułę" #: lib/solaar/ui/diversion_rules.py:272 msgid "Insert here" msgstr "Wstaw tutaj" #: lib/solaar/ui/diversion_rules.py:274 msgid "Insert above" msgstr "Wstaw powyżej" #: lib/solaar/ui/diversion_rules.py:276 msgid "Insert below" msgstr "Wstaw poniżej" #: lib/solaar/ui/diversion_rules.py:282 msgid "Insert new rule here" msgstr "Wstaw nową regułę tutaj" #: lib/solaar/ui/diversion_rules.py:284 msgid "Insert new rule above" msgstr "Wstaw nową regułę powyżej" #: lib/solaar/ui/diversion_rules.py:286 msgid "Insert new rule below" msgstr "Wstaw nową regułę poniżej" #: lib/solaar/ui/diversion_rules.py:313 msgid "Flatten" msgstr "Spłaszcz" #: lib/solaar/ui/diversion_rules.py:346 msgid "Insert" msgstr "Wstaw" #: lib/solaar/ui/diversion_rules.py:349 lib/solaar/ui/diversion_rules.py:477 #: lib/solaar/ui/diversion_rules.py:1121 msgid "Or" msgstr "Lub" #: lib/solaar/ui/diversion_rules.py:350 lib/solaar/ui/diversion_rules.py:476 #: lib/solaar/ui/diversion_rules.py:1107 msgid "And" msgstr "Oraz" #: lib/solaar/ui/diversion_rules.py:352 msgid "Condition" msgstr "Warunek" #: lib/solaar/ui/diversion_rules.py:354 lib/solaar/ui/rule_conditions.py:145 msgid "Feature" msgstr "Funkcja" #: lib/solaar/ui/diversion_rules.py:355 lib/solaar/ui/rule_conditions.py:179 msgid "Report" msgstr "Zgłoszenie" #: lib/solaar/ui/diversion_rules.py:356 lib/solaar/ui/rule_conditions.py:60 msgid "Process" msgstr "Proces" #: lib/solaar/ui/diversion_rules.py:357 msgid "Mouse process" msgstr "Proces myszy" #: lib/solaar/ui/diversion_rules.py:358 lib/solaar/ui/rule_conditions.py:216 msgid "Modifiers" msgstr "Modyfikatory" #: lib/solaar/ui/diversion_rules.py:359 lib/solaar/ui/rule_conditions.py:268 msgid "Key" msgstr "Klawisz" #: lib/solaar/ui/diversion_rules.py:360 lib/solaar/ui/rule_conditions.py:309 msgid "KeyIsDown" msgstr "KlawiszJestWciśnięty" #: lib/solaar/ui/diversion_rules.py:361 lib/solaar/ui/diversion_rules.py:1414 msgid "Active" msgstr "Aktywny" #: lib/solaar/ui/diversion_rules.py:362 lib/solaar/ui/diversion_rules.py:1372 #: lib/solaar/ui/diversion_rules.py:1423 lib/solaar/ui/diversion_rules.py:1470 msgid "Device" msgstr "Urządzenie" #: lib/solaar/ui/diversion_rules.py:363 lib/solaar/ui/diversion_rules.py:1449 msgid "Host" msgstr "Host" #: lib/solaar/ui/diversion_rules.py:364 lib/solaar/ui/diversion_rules.py:1489 msgid "Setting" msgstr "Ustawienie" #: lib/solaar/ui/diversion_rules.py:365 lib/solaar/ui/rule_conditions.py:324 #: lib/solaar/ui/rule_conditions.py:373 msgid "Test" msgstr "Test" #: lib/solaar/ui/diversion_rules.py:366 lib/solaar/ui/rule_conditions.py:498 msgid "Test bytes" msgstr "Bajty testowe" #: lib/solaar/ui/diversion_rules.py:367 lib/solaar/ui/rule_conditions.py:599 msgid "Mouse Gesture" msgstr "Gest myszy" #: lib/solaar/ui/diversion_rules.py:371 msgid "Action" msgstr "Czynność" #: lib/solaar/ui/diversion_rules.py:373 lib/solaar/ui/rule_actions.py:131 msgid "Key press" msgstr "Naciśnięcie klawisza" #: lib/solaar/ui/diversion_rules.py:374 lib/solaar/ui/rule_actions.py:182 msgid "Mouse scroll" msgstr "Przewijanie myszą" #: lib/solaar/ui/diversion_rules.py:375 lib/solaar/ui/rule_actions.py:243 msgid "Mouse click" msgstr "Kliknięcie myszą" #: lib/solaar/ui/diversion_rules.py:376 msgid "Set" msgstr "Ustaw" #: lib/solaar/ui/diversion_rules.py:377 lib/solaar/ui/rule_actions.py:313 msgid "Execute" msgstr "Wykonaj" #: lib/solaar/ui/diversion_rules.py:378 lib/solaar/ui/diversion_rules.py:1152 msgid "Later" msgstr "Później" #: lib/solaar/ui/diversion_rules.py:407 msgid "Insert new rule" msgstr "Wstaw nową regułę" #: lib/solaar/ui/diversion_rules.py:427 lib/solaar/ui/rule_actions.py:74 #: lib/solaar/ui/rule_actions.py:272 lib/solaar/ui/rule_conditions.py:546 msgid "Delete" msgstr "Usuń" #: lib/solaar/ui/diversion_rules.py:449 msgid "Negate" msgstr "Neguj" #: lib/solaar/ui/diversion_rules.py:473 msgid "Wrap with" msgstr "Obwiąż" #: lib/solaar/ui/diversion_rules.py:503 msgid "Cut" msgstr "Wytnij" #: lib/solaar/ui/diversion_rules.py:519 msgid "Paste" msgstr "Wklej" #: lib/solaar/ui/diversion_rules.py:525 msgid "Copy" msgstr "Skopiuj" #: lib/solaar/ui/diversion_rules.py:534 msgid "Solaar Rule Editor" msgstr "Edytor reguł Solaar" #: lib/solaar/ui/diversion_rules.py:634 msgid "Save changes" msgstr "Zapisz zmiany" #: lib/solaar/ui/diversion_rules.py:639 msgid "Discard changes" msgstr "Odrzuć zmiany" #: lib/solaar/ui/diversion_rules.py:1070 msgid "This editor does not support the selected rule component yet." msgstr "Ten edytor nie obsługuje jeszcze wybranego składnika reguły." #: lib/solaar/ui/diversion_rules.py:1132 msgid "" "Number of seconds to delay. Delay between 0 and 1 is done with higher " "precision." msgstr "" "Liczba sekund opóźnienia. Opóźnienie pomiędzy 0 i 1 odbywa się z większą " "precyzją." #: lib/solaar/ui/diversion_rules.py:1170 msgid "Not" msgstr "Nie" #: lib/solaar/ui/diversion_rules.py:1201 msgid "Toggle" msgstr "Przełącz" #: lib/solaar/ui/diversion_rules.py:1202 msgid "True" msgstr "Prawda" #: lib/solaar/ui/diversion_rules.py:1203 msgid "False" msgstr "Fałsz" #: lib/solaar/ui/diversion_rules.py:1216 msgid "Unsupported setting" msgstr "Nieobsługiwane ustawienie" #: lib/solaar/ui/diversion_rules.py:1378 lib/solaar/ui/diversion_rules.py:1397 #: lib/solaar/ui/diversion_rules.py:1476 lib/solaar/ui/diversion_rules.py:1731 #: lib/solaar/ui/diversion_rules.py:1749 msgid "Originating device" msgstr "Urządzenie inicjujące" #: lib/solaar/ui/diversion_rules.py:1410 msgid "Device is active and its settings can be changed." msgstr "Urządzenie jest aktywne i można zmienić jego ustawienia." #: lib/solaar/ui/diversion_rules.py:1419 msgid "Device that originated the current notification." msgstr "Urządzenie, z którego pochodzi bieżące powiadomienie." #: lib/solaar/ui/diversion_rules.py:1432 msgid "Name of host computer." msgstr "Nazwa komputera hosta." #: lib/solaar/ui/diversion_rules.py:1503 msgid "Value" msgstr "Wartość" #: lib/solaar/ui/diversion_rules.py:1512 msgid "Item" msgstr "Pozycja" #: lib/solaar/ui/diversion_rules.py:1791 msgid "Change setting on device" msgstr "Zmień ustawienie w urządzeniu" #: lib/solaar/ui/diversion_rules.py:1807 msgid "Setting on device" msgstr "Ustawienie w urządzeniu" #: lib/solaar/ui/pair_window.py:37 lib/solaar/ui/pair_window.py:165 #, python-format msgid "%(receiver_name)s: pair new device" msgstr "%(receiver_name)s: sparuj nowe urządzenie" #: lib/solaar/ui/pair_window.py:39 msgid "Bolt receivers are only compatible with Bolt devices." msgstr "Odbiorniki Bolt są kompatybilne tylko z urządzeniami Bolt." #: lib/solaar/ui/pair_window.py:41 msgid "Press a pairing button or key until the pairing light flashes quickly." msgstr "" "Naciśnij przycisk lub klawisz parowania, aż kontrolka parowania zacznie " "szybko migać." #: lib/solaar/ui/pair_window.py:44 msgid "Unifying receivers are only compatible with Unifying devices." msgstr "Odbiorniki Unifying są kompatybilne tylko z urządzeniami Unifying." #: lib/solaar/ui/pair_window.py:46 msgid "Other receivers are only compatible with a few devices." msgstr "Inne odbiorniki są kompatybilne tylko z kilkoma urządzeniami." #: lib/solaar/ui/pair_window.py:48 msgid "For most devices, turn on the device you want to pair." msgstr "W przypadku większości urządzeń należy je włączyć do parowania." #: lib/solaar/ui/pair_window.py:49 msgid "If the device is already turned on, turn it off and on again." msgstr "Jeśli urządzenie jest już włączone, wyłącz je i włącz ponownie." #: lib/solaar/ui/pair_window.py:51 msgid "The device must not be paired with a nearby powered-on receiver." msgstr "" "Urządzenia nie można parować ze znajdującym się w pobliżu włączonym " "odbiornikiem." #: lib/solaar/ui/pair_window.py:54 msgid "" "For devices with multiple channels, press, hold, and release the button for " "the channel you wish to pair\n" "or use the channel switch button to select a channel and then press, hold, " "and release the channel switch button." msgstr "" "W przypadku urządzeń z wieloma kanałami naciśnij, przytrzymaj i zwolnij " "przycisk kanału do parowania\n" "lub użyj przycisku przełączania kanałów, aby wybrać kanał, a następnie " "naciśnij, przytrzymaj i zwolnij przycisk przełączania kanałów." #: lib/solaar/ui/pair_window.py:61 msgid "The channel indicator light should be blinking rapidly." msgstr "Kontrolka kanału powinna szybko migać." #: lib/solaar/ui/pair_window.py:65 #, python-format msgid "" "\n" "\n" "This receiver has %d pairing remaining." msgid_plural "" "\n" "\n" "This receiver has %d pairings remaining." msgstr[0] "" "\n" "\n" "Ten odbiornik ma jeszcze %d sparowanie." msgstr[1] "" "\n" "\n" "Ten odbiornik ma jeszcze %d sparowania." msgstr[2] "" "\n" "\n" "Ten odbiornik ma jeszcze %d sparowań." #: lib/solaar/ui/pair_window.py:71 msgid "" "\n" "Cancelling at this point will not use up a pairing." msgstr "" "\n" "Anulowanie w tym momencie nie spowoduje zużycia sparowania." #: lib/solaar/ui/pair_window.py:166 #, python-format msgid "Enter passcode on %(name)s." msgstr "Wprowadź kod dostępu do %(name)s." #: lib/solaar/ui/pair_window.py:169 #, python-format msgid "Type %(passcode)s and then press the enter key." msgstr "Wpisz %(passcode)s i naciśnij klawisz Enter." #: lib/solaar/ui/pair_window.py:174 msgid "left" msgstr "lewo" #: lib/solaar/ui/pair_window.py:174 msgid "right" msgstr "prawo" #: lib/solaar/ui/pair_window.py:176 #, python-format msgid "" "Press %(code)s\n" "and then press left and right buttons simultaneously." msgstr "" "Naciśnij %(code)s\n" "i przyciśnij jednocześnie lewy i prawy przycisk." #: lib/solaar/ui/pair_window.py:219 msgid "The wireless link is not encrypted" msgstr "Połączenie nie jest szyfrowane" #: lib/solaar/ui/pair_window.py:224 msgid "Found a new device:" msgstr "Znaleziono nowe urządzenie:" #: lib/solaar/ui/pair_window.py:245 msgid "Pairing failed" msgstr "Sparowanie nie powiodło się" #: lib/solaar/ui/pair_window.py:247 msgid "Make sure your device is within range, and has a decent battery charge." msgstr "Upewnij się, że urządzenie jest w zasięgu i ma naładowany akumulator." #: lib/solaar/ui/pair_window.py:249 msgid "A new device was detected, but it is not compatible with this receiver." msgstr "" "Wykryto nowe urządzenie, jednak nie jest ono kompatybilne z tym odbiornikiem." #: lib/solaar/ui/pair_window.py:251 msgid "More paired devices than receiver can support." msgstr "Więcej sparowanych urządzeń niż obsługuje odbiornik." #: lib/solaar/ui/pair_window.py:253 msgid "No further details are available about the error." msgstr "Brak dodatkowych informacji na temat błędu." #: lib/solaar/ui/rule_actions.py:48 msgid "" "Simulate a chorded key click or depress or release.\n" "On Wayland requires write access to /dev/uinput." msgstr "" "Symuluj akordowe kliknięcie klawisza, wciśnięcie lub zwolnenie.\n" "W Wayland wymaga dostępu do zapisu w /dev/uinput." #: lib/solaar/ui/rule_actions.py:53 msgid "Add key" msgstr "Dodaj klawisz" #: lib/solaar/ui/rule_actions.py:56 msgid "Click" msgstr "Kliknij" #: lib/solaar/ui/rule_actions.py:59 msgid "Depress" msgstr "Wciśnij" #: lib/solaar/ui/rule_actions.py:62 msgid "Release" msgstr "Zwolnij" #: lib/solaar/ui/rule_actions.py:146 msgid "" "Simulate a mouse scroll.\n" "On Wayland requires write access to /dev/uinput." msgstr "" "Symuluj przewijanie myszy.\n" "W Wayland wymaga dostępu do zapisu w /dev/uinput." #: lib/solaar/ui/rule_actions.py:202 msgid "" "Simulate a mouse click.\n" "On Wayland requires write access to /dev/uinput." msgstr "" "Symuluj kliknięcie myszą.\n" "W Wayland wymaga dostępu do zapisu w /dev/uinput." #: lib/solaar/ui/rule_actions.py:205 msgid "Button" msgstr "Przycisk" #: lib/solaar/ui/rule_actions.py:206 msgid "Count and Action" msgstr "Liczba i czynność" #: lib/solaar/ui/rule_actions.py:255 msgid "Execute a command with arguments." msgstr "Wykonaj polecenie z argumentami." #: lib/solaar/ui/rule_actions.py:258 msgid "Add argument" msgstr "Dodaj argument" #: lib/solaar/ui/rule_conditions.py:43 msgid "X11 active process. For use in X11 only." msgstr "Aktywny proces X11. Do użytku tylko w X11." #: lib/solaar/ui/rule_conditions.py:73 msgid "X11 mouse process. For use in X11 only." msgstr "Proces myszy X11. Do użytku tylko w X11." #: lib/solaar/ui/rule_conditions.py:90 msgid "MouseProcess" msgstr "ProcesMyszy" #: lib/solaar/ui/rule_conditions.py:114 msgid "Feature name of notification triggering rule processing." msgstr "Nazwa funkcji powiadomienia wyzwalająca przetwarzanie reguły." #: lib/solaar/ui/rule_conditions.py:160 msgid "Report number of notification triggering rule processing." msgstr "Numer zgłoszenia powiadomienia wyzwalającego przetwarzanie reguły." #: lib/solaar/ui/rule_conditions.py:192 msgid "Active keyboard modifiers. Not always available in Wayland." msgstr "Aktywne modyfikatory klawiatury. Nie zawsze dostępne w Wayland." #: lib/solaar/ui/rule_conditions.py:232 msgid "" "Diverted key or button depressed or released.\n" "Use the Key/Button Diversion and Divert G Keys settings to divert keys and " "buttons." msgstr "" "Przekierowany klawisz lub przycisk wciśnięto lub zwolniono.\n" "Użyj ustawień przekierowania klawiszy/przycisków oraz przekierowania " "klawiszy G, aby przekierować klawisze i przyciski." #: lib/solaar/ui/rule_conditions.py:241 msgid "Key down" msgstr "Klawisz w dół" #: lib/solaar/ui/rule_conditions.py:244 msgid "Key up" msgstr "Klawisz w górę" #: lib/solaar/ui/rule_conditions.py:284 msgid "" "Diverted key or button is currently down.\n" "Use the Key/Button Diversion and Divert G Keys settings to divert keys and " "buttons." msgstr "" "Przekierowany klawisz lub przycisk jest obecnie wciśnięty.\n" "Użyj ustawień przekierowania klawiszy/przycisków oraz przekierowania " "klawiszy G, aby przekierować klawisze i przyciski." #: lib/solaar/ui/rule_conditions.py:322 msgid "Test condition on notification triggering rule processing." msgstr "Warunek testowy przy powiadomieniu wyzwalający przetwarzanie reguły." #: lib/solaar/ui/rule_conditions.py:326 msgid "Parameter" msgstr "Parametr" #: lib/solaar/ui/rule_conditions.py:399 msgid "begin (inclusive)" msgstr "rozpocznij (włącznie)" #: lib/solaar/ui/rule_conditions.py:400 msgid "end (exclusive)" msgstr "zakończ (wyłącznie)" #: lib/solaar/ui/rule_conditions.py:408 msgid "range" msgstr "zakres" #: lib/solaar/ui/rule_conditions.py:411 msgid "minimum" msgstr "minimum" #: lib/solaar/ui/rule_conditions.py:412 msgid "maximum" msgstr "maksimum" #: lib/solaar/ui/rule_conditions.py:414 #, python-format msgid "bytes %(0)d to %(1)d, ranging from %(2)d to %(3)d" msgstr "bajty %(0)d do %(1)d, począwszy od %(2)d do %(3)d" #: lib/solaar/ui/rule_conditions.py:417 lib/solaar/ui/rule_conditions.py:418 msgid "mask" msgstr "maska" #: lib/solaar/ui/rule_conditions.py:419 #, python-format msgid "bytes %(0)d to %(1)d, mask %(2)d" msgstr "bajty %(0)d do %(1)d, maska %(2)d" #: lib/solaar/ui/rule_conditions.py:428 msgid "" "Bit or range test on bytes in notification message triggering rule " "processing." msgstr "" "Test bitowy lub zakresowy na bajtach w komunikacie powiadomienia " "uruchamiający przetwarzanie reguł." #: lib/solaar/ui/rule_conditions.py:438 msgid "type" msgstr "typ" #: lib/solaar/ui/rule_conditions.py:526 msgid "" "Mouse gesture with optional initiating button followed by zero or more mouse " "movements." msgstr "" "Gest myszy z opcjonalnym przyciskiem inicjującym, po którym następuje zero " "lub więcej ruchów myszy." #: lib/solaar/ui/rule_conditions.py:531 msgid "Add movement" msgstr "Dodaj ruch" #: lib/solaar/ui/tray.py:49 msgid "No supported device found" msgstr "Nie znaleziono obsługiwanego urządzenia" #: lib/solaar/ui/tray.py:54 lib/solaar/ui/window.py:308 #, python-format msgid "About %s" msgstr "O programie %s" #: lib/solaar/ui/tray.py:55 lib/solaar/ui/window.py:306 #, python-format msgid "Quit %s" msgstr "Zakończ %s" #: lib/solaar/ui/tray.py:270 lib/solaar/ui/tray.py:278 msgid "no receiver" msgstr "brak odbiornika" #: lib/solaar/ui/tray.py:291 lib/solaar/ui/tray.py:296 msgid "offline" msgstr "wyłączone" #: lib/solaar/ui/tray.py:294 msgid "no status" msgstr "brak statusu" #: lib/solaar/ui/window.py:90 msgid "Scanning" msgstr "Skanowanie" #: lib/solaar/ui/window.py:121 msgid "Battery" msgstr "Akumulator" #: lib/solaar/ui/window.py:124 msgid "Wireless Link" msgstr "Połączenie bezprzewodowe" #: lib/solaar/ui/window.py:128 msgid "Lighting" msgstr "Podświetlenie" #: lib/solaar/ui/window.py:162 msgid "Show Technical Details" msgstr "Wyświetl szczegóły techniczne" #: lib/solaar/ui/window.py:178 msgid "Pair new device" msgstr "Sparuj nowe urządzenie" #: lib/solaar/ui/window.py:196 msgid "Select a device" msgstr "Wybierz urządzenie" #: lib/solaar/ui/window.py:311 msgid "Rule Editor" msgstr "Edytor reguł" #: lib/solaar/ui/window.py:504 msgid "Path" msgstr "Ścieżka" #: lib/solaar/ui/window.py:506 msgid "USB ID" msgstr "ID USB" #: lib/solaar/ui/window.py:509 lib/solaar/ui/window.py:511 #: lib/solaar/ui/window.py:526 lib/solaar/ui/window.py:528 msgid "Serial" msgstr "Nr seryjny" #: lib/solaar/ui/window.py:515 msgid "Index" msgstr "Indeks" #: lib/solaar/ui/window.py:517 msgid "Wireless PID" msgstr "PID bezprz." #: lib/solaar/ui/window.py:519 msgid "Product ID" msgstr "ID produktu" #: lib/solaar/ui/window.py:521 msgid "Protocol" msgstr "Protokół" #: lib/solaar/ui/window.py:521 msgid "Unknown" msgstr "Nieznany" #: lib/solaar/ui/window.py:523 msgid "Polling rate" msgstr "Odpytywanie" #: lib/solaar/ui/window.py:530 msgid "Unit ID" msgstr "ID jednostki" #: lib/solaar/ui/window.py:544 msgid "Notifications" msgstr "Powiadomienia" #: lib/solaar/ui/window.py:588 msgid "No device paired." msgstr "Brak sparowanych urządzeń." #: lib/solaar/ui/window.py:597 #, python-format msgid "Up to %(max_count)s device can be paired to this receiver." msgid_plural "Up to %(max_count)s devices can be paired to this receiver." msgstr[0] "" "Można sparować maksymalnie %(max_count)s urządzenie z tym odbiornikiem." msgstr[1] "" "Można sparować maksymalnie %(max_count)s urządzenia z tym odbiornikiem." msgstr[2] "" "Można sparować maksymalnie %(max_count)s urządzeń z tym odbiornikiem." #: lib/solaar/ui/window.py:608 #, python-format msgid "This receiver has %d pairing remaining." msgid_plural "This receiver has %d pairings remaining." msgstr[0] "Ten odbiornik ma jeszcze %d sparowanie." msgstr[1] "Ten odbiornik ma jeszcze %d sparowania." msgstr[2] "Ten odbiornik ma jeszcze %d sparowań." #: lib/solaar/ui/window.py:665 msgid "Battery Voltage" msgstr "Napięcie akumulatora" #: lib/solaar/ui/window.py:667 msgid "Voltage reported by battery" msgstr "Napięcie zgłoszone przez akumulator" #: lib/solaar/ui/window.py:669 msgid "Battery Level" msgstr "Poziom akumulatora" #: lib/solaar/ui/window.py:671 msgid "Approximate level reported by battery" msgstr "Przybliżony poziom zgłoszony przez akumulator" #: lib/solaar/ui/window.py:678 lib/solaar/ui/window.py:680 msgid "next reported " msgstr "następny zgłoszony " #: lib/solaar/ui/window.py:681 msgid " and next level to be reported." msgstr " oraz następny poziom do zgłoszenia." #: lib/solaar/ui/window.py:697 msgid "encrypted" msgstr "szyfrowane" #: lib/solaar/ui/window.py:699 msgid "The wireless link between this device and its receiver is encrypted." msgstr "" "Połączenie bezprzewodowe pomiędzy tym urządzeniem i odbiornikiem jest " "szyfrowane." #: lib/solaar/ui/window.py:701 msgid "not encrypted" msgstr "nieszyfrowane" #: lib/solaar/ui/window.py:705 msgid "" "The wireless link between this device and its receiver is not encrypted.\n" "This is a security issue for pointing devices, and a major security issue " "for text-input devices." msgstr "" "Łącze bezprzewodowe między tym urządzeniem a jego odbiornikiem nie jest " "szyfrowane.\n" "Jest to problem dotyczący zabezpieczeń urządzeń wskazujących i poważny " "problem dotyczący zabezpieczeń urządzeń do wprowadzania tekstu." #: lib/solaar/ui/window.py:721 #, python-format msgid "%(light_level)d lux" msgstr "%(light_level)d lx" Solaar-1.1.14/po/pt.po000066400000000000000000001372101473525213700144350ustar00rootroot00000000000000# Translation of solaar's ui's messages to European Portuguese # Copyright (C) 2014 THE solaar'S COPYRIGHT HOLDER # This file is distributed under the same license as the solaar package. # # Américo Monteiro , 2014. msgid "" msgstr "Project-Id-Version: solaar 0.9.2\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-12-28 17:40+0100\n" "PO-Revision-Date: 2014-09-14 20:57+0100\n" "Last-Translator: Américo Monteiro \n" "Language-Team: Portuguese \n" "Language: pt\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Lokalize 1.4\n" #: lib/logitech_receiver/base_usb.py:46 msgid "Bolt Receiver" msgstr "" #: lib/logitech_receiver/base_usb.py:57 msgid "Unifying Receiver" msgstr "" #: lib/logitech_receiver/base_usb.py:67 lib/logitech_receiver/base_usb.py:78 #: lib/logitech_receiver/base_usb.py:90 lib/logitech_receiver/base_usb.py:102 #: lib/logitech_receiver/base_usb.py:114 msgid "Nano Receiver" msgstr "" #: lib/logitech_receiver/base_usb.py:124 msgid "Lightspeed Receiver" msgstr "" #: lib/logitech_receiver/base_usb.py:133 msgid "EX100 Receiver 27 Mhz" msgstr "" #: lib/logitech_receiver/i18n.py:30 msgid "empty" msgstr "vazio" #: lib/logitech_receiver/i18n.py:31 msgid "critical" msgstr "crítico" #: lib/logitech_receiver/i18n.py:32 msgid "low" msgstr "baixo" #: lib/logitech_receiver/i18n.py:33 msgid "average" msgstr "" #: lib/logitech_receiver/i18n.py:34 msgid "good" msgstr "bom" #: lib/logitech_receiver/i18n.py:35 msgid "full" msgstr "cheio" #: lib/logitech_receiver/i18n.py:38 msgid "discharging" msgstr "a descarregar" #: lib/logitech_receiver/i18n.py:39 msgid "recharging" msgstr "a carregar" #: lib/logitech_receiver/i18n.py:40 lib/solaar/ui/window.py:711 msgid "charging" msgstr "a carregar" #: lib/logitech_receiver/i18n.py:41 msgid "not charging" msgstr "" #: lib/logitech_receiver/i18n.py:42 msgid "almost full" msgstr "quase cheiro" #: lib/logitech_receiver/i18n.py:43 msgid "charged" msgstr "" #: lib/logitech_receiver/i18n.py:44 msgid "slow recharge" msgstr "carga lenta" #: lib/logitech_receiver/i18n.py:45 msgid "invalid battery" msgstr "bateria inválida" #: lib/logitech_receiver/i18n.py:46 msgid "thermal error" msgstr "erro térmico" #: lib/logitech_receiver/i18n.py:47 msgid "error" msgstr "" #: lib/logitech_receiver/i18n.py:48 msgid "standard" msgstr "" #: lib/logitech_receiver/i18n.py:49 msgid "fast" msgstr "" #: lib/logitech_receiver/i18n.py:50 msgid "slow" msgstr "" #: lib/logitech_receiver/i18n.py:53 msgid "device timeout" msgstr "tempo limite do dispositivo" #: lib/logitech_receiver/i18n.py:54 msgid "device not supported" msgstr "dispositivo não suportado" #: lib/logitech_receiver/i18n.py:55 msgid "too many devices" msgstr "demasiados dispositivos" #: lib/logitech_receiver/i18n.py:56 msgid "sequence timeout" msgstr "tempo limite da sequência" #: lib/logitech_receiver/i18n.py:59 lib/solaar/ui/window.py:572 msgid "Firmware" msgstr "Firmware" #: lib/logitech_receiver/i18n.py:60 msgid "Bootloader" msgstr "Gestor de arranque" #: lib/logitech_receiver/i18n.py:61 msgid "Hardware" msgstr "Hardware" #: lib/logitech_receiver/i18n.py:62 msgid "Other" msgstr "Outro" #: lib/logitech_receiver/i18n.py:65 msgid "Left Button" msgstr "" #: lib/logitech_receiver/i18n.py:66 msgid "Right Button" msgstr "" #: lib/logitech_receiver/i18n.py:67 msgid "Middle Button" msgstr "" #: lib/logitech_receiver/i18n.py:68 msgid "Back Button" msgstr "" #: lib/logitech_receiver/i18n.py:69 msgid "Forward Button" msgstr "" #: lib/logitech_receiver/i18n.py:70 msgid "Mouse Gesture Button" msgstr "" #: lib/logitech_receiver/i18n.py:71 msgid "Smart Shift" msgstr "" #: lib/logitech_receiver/i18n.py:72 msgid "DPI Switch" msgstr "" #: lib/logitech_receiver/i18n.py:73 msgid "Left Tilt" msgstr "" #: lib/logitech_receiver/i18n.py:74 msgid "Right Tilt" msgstr "" #: lib/logitech_receiver/i18n.py:75 msgid "Left Click" msgstr "" #: lib/logitech_receiver/i18n.py:76 msgid "Right Click" msgstr "" #: lib/logitech_receiver/i18n.py:77 msgid "Mouse Middle Button" msgstr "" #: lib/logitech_receiver/i18n.py:78 msgid "Mouse Back Button" msgstr "" #: lib/logitech_receiver/i18n.py:79 msgid "Mouse Forward Button" msgstr "" #: lib/logitech_receiver/i18n.py:80 msgid "Gesture Button Navigation" msgstr "" #: lib/logitech_receiver/i18n.py:81 msgid "Mouse Scroll Left Button" msgstr "" #: lib/logitech_receiver/i18n.py:82 msgid "Mouse Scroll Right Button" msgstr "" #: lib/logitech_receiver/i18n.py:85 msgid "pressed" msgstr "" #: lib/logitech_receiver/i18n.py:86 msgid "released" msgstr "" #: lib/logitech_receiver/notifications.py:75 #: lib/logitech_receiver/notifications.py:126 msgid "pairing lock is closed" msgstr "" #: lib/logitech_receiver/notifications.py:75 #: lib/logitech_receiver/notifications.py:126 msgid "pairing lock is open" msgstr "" #: lib/logitech_receiver/notifications.py:92 msgid "discovery lock is closed" msgstr "" #: lib/logitech_receiver/notifications.py:92 msgid "discovery lock is open" msgstr "" #: lib/logitech_receiver/notifications.py:224 lib/solaar/ui/notify.py:122 msgid "connected" msgstr "ligado" #: lib/logitech_receiver/notifications.py:224 msgid "disconnected" msgstr "" #: lib/logitech_receiver/notifications.py:262 lib/solaar/ui/notify.py:120 msgid "unpaired" msgstr "desemparelhado" #: lib/logitech_receiver/notifications.py:304 msgid "powered on" msgstr "com energia ligada" #: lib/logitech_receiver/settings.py:750 msgid "register" msgstr "" #: lib/logitech_receiver/settings.py:764 lib/logitech_receiver/settings.py:791 msgid "feature" msgstr "" #: lib/logitech_receiver/settings_templates.py:139 msgid "Swap Fx function" msgstr "Trocar função Fx" #: lib/logitech_receiver/settings_templates.py:140 msgid "When set, the F1..F12 keys will activate their special function,\n" "and you must hold the FN key to activate their standard function." msgstr "Quando definido, as teclas F1..F12 irão activar as suas funções " "especiais,\n" "e você tem manter a tecla FN carregada para activar as suas funções " "standard." #: lib/logitech_receiver/settings_templates.py:142 msgid "When unset, the F1..F12 keys will activate their standard function,\n" "and you must hold the FN key to activate their special function." msgstr "Quando não definido, as teclas F1..F12 irão activar as suas funções " "standard,\n" "e você tem manter a tecla FN carregada para activar as suas funções " "especiais." #: lib/logitech_receiver/settings_templates.py:149 msgid "Hand Detection" msgstr "Detecção de Mão" #: lib/logitech_receiver/settings_templates.py:150 msgid "Turn on illumination when the hands hover over the keyboard." msgstr "Liga a iluminação quando as mãos estão sobre o teclado." #: lib/logitech_receiver/settings_templates.py:157 msgid "Scroll Wheel Smooth Scrolling" msgstr "" #: lib/logitech_receiver/settings_templates.py:158 #: lib/logitech_receiver/settings_templates.py:239 #: lib/logitech_receiver/settings_templates.py:267 msgid "High-sensitivity mode for vertical scroll with the wheel." msgstr "Modo de alta-sensibilidade para deslocamento vertical com a roda." #: lib/logitech_receiver/settings_templates.py:165 msgid "Side Scrolling" msgstr "Deslocamento Lateral" #: lib/logitech_receiver/settings_templates.py:167 msgid "When disabled, pushing the wheel sideways sends custom button " "events\n" "instead of the standard side-scrolling events." msgstr "Quando desactivado, premir a roda para os lados envia eventos de " "botões personalizados\n" "em vez dos eventos de deslocamento lateral standard." #: lib/logitech_receiver/settings_templates.py:177 msgid "Sensitivity (DPI - older mice)" msgstr "" #: lib/logitech_receiver/settings_templates.py:178 #: lib/logitech_receiver/settings_templates.py:712 msgid "Mouse movement sensitivity" msgstr "" #: lib/logitech_receiver/settings_templates.py:208 #: lib/logitech_receiver/settings_templates.py:218 #: lib/logitech_receiver/settings_templates.py:225 msgid "Backlight" msgstr "" #: lib/logitech_receiver/settings_templates.py:209 #: lib/logitech_receiver/settings_templates.py:226 msgid "Set illumination time for keyboard." msgstr "" #: lib/logitech_receiver/settings_templates.py:219 msgid "Turn illumination on or off on keyboard." msgstr "" #: lib/logitech_receiver/settings_templates.py:237 msgid "Scroll Wheel High Resolution" msgstr "" #: lib/logitech_receiver/settings_templates.py:240 #: lib/logitech_receiver/settings_templates.py:268 msgid "Set to ignore if scrolling is abnormally fast or slow" msgstr "" #: lib/logitech_receiver/settings_templates.py:247 #: lib/logitech_receiver/settings_templates.py:277 msgid "Scroll Wheel Diversion" msgstr "" #: lib/logitech_receiver/settings_templates.py:249 msgid "Make scroll wheel send LOWRES_WHEEL HID++ notifications (which " "trigger Solaar rules but are otherwise ignored)." msgstr "" #: lib/logitech_receiver/settings_templates.py:256 msgid "Scroll Wheel Direction" msgstr "" #: lib/logitech_receiver/settings_templates.py:257 msgid "Invert direction for vertical scroll with wheel." msgstr "" #: lib/logitech_receiver/settings_templates.py:265 msgid "Scroll Wheel Resolution" msgstr "" #: lib/logitech_receiver/settings_templates.py:279 msgid "Make scroll wheel send HIRES_WHEEL HID++ notifications (which " "trigger Solaar rules but are otherwise ignored)." msgstr "" #: lib/logitech_receiver/settings_templates.py:288 msgid "Sensitivity (Pointer Speed)" msgstr "" #: lib/logitech_receiver/settings_templates.py:289 msgid "Speed multiplier for mouse (256 is normal multiplier)." msgstr "" #: lib/logitech_receiver/settings_templates.py:299 msgid "Thumb Wheel Diversion" msgstr "" #: lib/logitech_receiver/settings_templates.py:301 msgid "Make thumb wheel send THUMB_WHEEL HID++ notifications (which trigger " "Solaar rules but are otherwise ignored)." msgstr "" #: lib/logitech_receiver/settings_templates.py:310 msgid "Thumb Wheel Direction" msgstr "" #: lib/logitech_receiver/settings_templates.py:311 msgid "Invert thumb wheel scroll direction." msgstr "" #: lib/logitech_receiver/settings_templates.py:319 msgid "Onboard Profiles" msgstr "" #: lib/logitech_receiver/settings_templates.py:320 msgid "Enable onboard profiles, which often control report rate and " "keyboard lighting" msgstr "" #: lib/logitech_receiver/settings_templates.py:330 msgid "Polling Rate (ms)" msgstr "" #: lib/logitech_receiver/settings_templates.py:332 msgid "Frequency of device polling, in milliseconds" msgstr "" #: lib/logitech_receiver/settings_templates.py:333 #: lib/logitech_receiver/settings_templates.py:1046 #: lib/logitech_receiver/settings_templates.py:1076 msgid "May need Onboard Profiles set to Disable to be effective." msgstr "" #: lib/logitech_receiver/settings_templates.py:365 msgid "Divert crown events" msgstr "" #: lib/logitech_receiver/settings_templates.py:366 msgid "Make crown send CROWN HID++ notifications (which trigger Solaar " "rules but are otherwise ignored)." msgstr "" #: lib/logitech_receiver/settings_templates.py:374 msgid "Crown smooth scroll" msgstr "" #: lib/logitech_receiver/settings_templates.py:375 msgid "Set crown smooth scroll" msgstr "" #: lib/logitech_receiver/settings_templates.py:383 msgid "Divert G Keys" msgstr "" #: lib/logitech_receiver/settings_templates.py:385 msgid "Make G keys send GKEY HID++ notifications (which trigger Solaar " "rules but are otherwise ignored)." msgstr "" #: lib/logitech_receiver/settings_templates.py:386 msgid "May also make M keys and MR key send HID++ notifications" msgstr "" #: lib/logitech_receiver/settings_templates.py:402 msgid "Scroll Wheel Ratcheted" msgstr "" #: lib/logitech_receiver/settings_templates.py:403 msgid "Switch the mouse wheel between speed-controlled ratcheting and " "always freespin." msgstr "" #: lib/logitech_receiver/settings_templates.py:405 msgid "Freespinning" msgstr "" #: lib/logitech_receiver/settings_templates.py:405 msgid "Ratcheted" msgstr "" #: lib/logitech_receiver/settings_templates.py:412 msgid "Scroll Wheel Ratchet Speed" msgstr "" #: lib/logitech_receiver/settings_templates.py:414 msgid "Use the mouse wheel speed to switch between ratcheted and " "freespinning.\n" "The mouse wheel is always ratcheted at 50." msgstr "" #: lib/logitech_receiver/settings_templates.py:463 msgid "Key/Button Actions" msgstr "" #: lib/logitech_receiver/settings_templates.py:465 msgid "Change the action for the key or button." msgstr "" #: lib/logitech_receiver/settings_templates.py:465 msgid "Overridden by diversion." msgstr "" #: lib/logitech_receiver/settings_templates.py:466 msgid "Changing important actions (such as for the left mouse button) can " "result in an unusable system." msgstr "" #: lib/logitech_receiver/settings_templates.py:639 msgid "Key/Button Diversion" msgstr "" #: lib/logitech_receiver/settings_templates.py:640 msgid "Make the key or button send HID++ notifications (Diverted) or " "initiate Mouse Gestures or Sliding DPI" msgstr "" #: lib/logitech_receiver/settings_templates.py:643 #: lib/logitech_receiver/settings_templates.py:644 #: lib/logitech_receiver/settings_templates.py:645 msgid "Diverted" msgstr "" #: lib/logitech_receiver/settings_templates.py:643 #: lib/logitech_receiver/settings_templates.py:644 msgid "Mouse Gestures" msgstr "" #: lib/logitech_receiver/settings_templates.py:643 #: lib/logitech_receiver/settings_templates.py:644 #: lib/logitech_receiver/settings_templates.py:645 msgid "Regular" msgstr "" #: lib/logitech_receiver/settings_templates.py:643 msgid "Sliding DPI" msgstr "" #: lib/logitech_receiver/settings_templates.py:711 msgid "Sensitivity (DPI)" msgstr "Sensibilidade (DPI)" #: lib/logitech_receiver/settings_templates.py:752 msgid "Sensitivity Switching" msgstr "" #: lib/logitech_receiver/settings_templates.py:754 msgid "Switch the current sensitivity and the remembered sensitivity when " "the key or button is pressed.\n" "If there is no remembered sensitivity, just remember the current " "sensitivity" msgstr "" #: lib/logitech_receiver/settings_templates.py:758 msgid "Off" msgstr "" #: lib/logitech_receiver/settings_templates.py:791 msgid "Disable keys" msgstr "" #: lib/logitech_receiver/settings_templates.py:792 msgid "Disable specific keyboard keys." msgstr "" #: lib/logitech_receiver/settings_templates.py:795 #, python-format msgid "Disables the %s key." msgstr "" #: lib/logitech_receiver/settings_templates.py:809 #: lib/logitech_receiver/settings_templates.py:860 msgid "Set OS" msgstr "" #: lib/logitech_receiver/settings_templates.py:810 #: lib/logitech_receiver/settings_templates.py:861 msgid "Change keys to match OS." msgstr "" #: lib/logitech_receiver/settings_templates.py:873 msgid "Change Host" msgstr "" #: lib/logitech_receiver/settings_templates.py:874 msgid "Switch connection to a different host" msgstr "" #: lib/logitech_receiver/settings_templates.py:900 msgid "Performs a left click." msgstr "" #: lib/logitech_receiver/settings_templates.py:900 msgid "Single tap" msgstr "" #: lib/logitech_receiver/settings_templates.py:901 msgid "Performs a right click." msgstr "" #: lib/logitech_receiver/settings_templates.py:901 msgid "Single tap with two fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:902 msgid "Single tap with three fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:906 msgid "Double tap" msgstr "" #: lib/logitech_receiver/settings_templates.py:906 msgid "Performs a double click." msgstr "" #: lib/logitech_receiver/settings_templates.py:907 msgid "Double tap with two fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:908 msgid "Double tap with three fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:911 msgid "Drags items by dragging the finger after double tapping." msgstr "" #: lib/logitech_receiver/settings_templates.py:911 msgid "Tap and drag" msgstr "" #: lib/logitech_receiver/settings_templates.py:913 msgid "Drags items by dragging the fingers after double tapping." msgstr "" #: lib/logitech_receiver/settings_templates.py:913 msgid "Tap and drag with two fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:914 msgid "Tap and drag with three fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:917 msgid "Disables tap and edge gestures (equivalent to pressing Fn+LeftClick)." msgstr "" #: lib/logitech_receiver/settings_templates.py:917 msgid "Suppress tap and edge gestures" msgstr "" #: lib/logitech_receiver/settings_templates.py:918 msgid "Scroll with one finger" msgstr "" #: lib/logitech_receiver/settings_templates.py:918 #: lib/logitech_receiver/settings_templates.py:919 #: lib/logitech_receiver/settings_templates.py:922 msgid "Scrolls." msgstr "" #: lib/logitech_receiver/settings_templates.py:919 #: lib/logitech_receiver/settings_templates.py:922 msgid "Scroll with two fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:920 msgid "Scroll horizontally with two fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:920 msgid "Scrolls horizontally." msgstr "" #: lib/logitech_receiver/settings_templates.py:921 msgid "Scroll vertically with two fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:921 msgid "Scrolls vertically." msgstr "" #: lib/logitech_receiver/settings_templates.py:923 msgid "Inverts the scrolling direction." msgstr "" #: lib/logitech_receiver/settings_templates.py:923 msgid "Natural scrolling" msgstr "" #: lib/logitech_receiver/settings_templates.py:924 msgid "Enables the thumbwheel." msgstr "" #: lib/logitech_receiver/settings_templates.py:924 msgid "Thumbwheel" msgstr "" #: lib/logitech_receiver/settings_templates.py:935 #: lib/logitech_receiver/settings_templates.py:939 msgid "Swipe from the top edge" msgstr "" #: lib/logitech_receiver/settings_templates.py:936 msgid "Swipe from the left edge" msgstr "" #: lib/logitech_receiver/settings_templates.py:937 msgid "Swipe from the right edge" msgstr "" #: lib/logitech_receiver/settings_templates.py:938 msgid "Swipe from the bottom edge" msgstr "" #: lib/logitech_receiver/settings_templates.py:940 msgid "Swipe two fingers from the left edge" msgstr "" #: lib/logitech_receiver/settings_templates.py:941 msgid "Swipe two fingers from the right edge" msgstr "" #: lib/logitech_receiver/settings_templates.py:942 msgid "Swipe two fingers from the bottom edge" msgstr "" #: lib/logitech_receiver/settings_templates.py:943 msgid "Swipe two fingers from the top edge" msgstr "" #: lib/logitech_receiver/settings_templates.py:944 #: lib/logitech_receiver/settings_templates.py:948 msgid "Pinch to zoom out; spread to zoom in." msgstr "" #: lib/logitech_receiver/settings_templates.py:944 msgid "Zoom with two fingers." msgstr "" #: lib/logitech_receiver/settings_templates.py:945 msgid "Pinch to zoom out." msgstr "" #: lib/logitech_receiver/settings_templates.py:946 msgid "Spread to zoom in." msgstr "" #: lib/logitech_receiver/settings_templates.py:947 msgid "Zoom with three fingers." msgstr "" #: lib/logitech_receiver/settings_templates.py:948 msgid "Zoom with two fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:966 msgid "Pixel zone" msgstr "" #: lib/logitech_receiver/settings_templates.py:967 msgid "Ratio zone" msgstr "" #: lib/logitech_receiver/settings_templates.py:968 msgid "Scale factor" msgstr "Fator de escala" #: lib/logitech_receiver/settings_templates.py:968 msgid "Sets the cursor speed." msgstr "" #: lib/logitech_receiver/settings_templates.py:972 msgid "Left" msgstr "" #: lib/logitech_receiver/settings_templates.py:972 msgid "Left-most coordinate." msgstr "" #: lib/logitech_receiver/settings_templates.py:973 msgid "Bottom" msgstr "" #: lib/logitech_receiver/settings_templates.py:973 msgid "Bottom coordinate." msgstr "" #: lib/logitech_receiver/settings_templates.py:974 msgid "Width" msgstr "Largura" #: lib/logitech_receiver/settings_templates.py:974 msgid "Width." msgstr "" #: lib/logitech_receiver/settings_templates.py:975 msgid "Height" msgstr "Altura" #: lib/logitech_receiver/settings_templates.py:975 msgid "Height." msgstr "" #: lib/logitech_receiver/settings_templates.py:976 msgid "Cursor speed." msgstr "" #: lib/logitech_receiver/settings_templates.py:976 msgid "Scale" msgstr "" #: lib/logitech_receiver/settings_templates.py:982 msgid "Gestures" msgstr "Gestos" #: lib/logitech_receiver/settings_templates.py:983 msgid "Tweak the mouse/touchpad behaviour." msgstr "" #: lib/logitech_receiver/settings_templates.py:1000 msgid "Gestures Diversion" msgstr "" #: lib/logitech_receiver/settings_templates.py:1001 msgid "Divert mouse/touchpad gestures." msgstr "" #: lib/logitech_receiver/settings_templates.py:1018 msgid "Gesture params" msgstr "" #: lib/logitech_receiver/settings_templates.py:1019 msgid "Change numerical parameters of a mouse/touchpad." msgstr "" #: lib/logitech_receiver/settings_templates.py:1044 msgid "M-Key LEDs" msgstr "" #: lib/logitech_receiver/settings_templates.py:1046 msgid "Control the M-Key LEDs." msgstr "" #: lib/logitech_receiver/settings_templates.py:1047 #: lib/logitech_receiver/settings_templates.py:1077 msgid "May need G Keys diverted to be effective." msgstr "" #: lib/logitech_receiver/settings_templates.py:1053 #, python-format msgid "Lights up the %s key." msgstr "" #: lib/logitech_receiver/settings_templates.py:1074 msgid "MR-Key LED" msgstr "" #: lib/logitech_receiver/settings_templates.py:1076 msgid "Control the MR-Key LED." msgstr "" #: lib/logitech_receiver/settings_templates.py:1095 msgid "Persistent Key/Button Mapping" msgstr "" #: lib/logitech_receiver/settings_templates.py:1097 msgid "Permanently change the mapping for the key or button." msgstr "" #: lib/logitech_receiver/settings_templates.py:1098 msgid "Changing important keys or buttons (such as for the left mouse " "button) can result in an unusable system." msgstr "" #: lib/logitech_receiver/settings_templates.py:1157 msgid "Sidetone" msgstr "" #: lib/logitech_receiver/settings_templates.py:1158 msgid "Set sidetone level." msgstr "" #: lib/logitech_receiver/settings_templates.py:1167 msgid "Equalizer" msgstr "" #: lib/logitech_receiver/settings_templates.py:1168 msgid "Set equalizer levels." msgstr "" #: lib/logitech_receiver/settings_templates.py:1191 msgid "Hz" msgstr "" #: lib/logitech_receiver/settings_templates.py:1197 msgid "Power Management" msgstr "" #: lib/logitech_receiver/settings_templates.py:1198 msgid "Power off in minutes (0 for never)." msgstr "" #: lib/logitech_receiver/status.py:114 msgid "No paired devices." msgstr "Nenhum dispositivo emparelhado." #: lib/logitech_receiver/status.py:115 lib/solaar/ui/window.py:622 #, python-format msgid "%(count)s paired device." msgid_plural "%(count)s paired devices." msgstr[0] "" msgstr[1] "" #: lib/logitech_receiver/status.py:170 #, python-format msgid "Battery: %(level)s" msgstr "" #: lib/logitech_receiver/status.py:172 #, python-format msgid "Battery: %(percent)d%%" msgstr "" #: lib/logitech_receiver/status.py:184 #, python-format msgid "Lighting: %(level)s lux" msgstr "" #: lib/logitech_receiver/status.py:239 #, python-format msgid "Battery: %(level)s (%(status)s)" msgstr "" #: lib/logitech_receiver/status.py:241 #, python-format msgid "Battery: %(percent)d%% (%(status)s)" msgstr "" #: lib/solaar/ui/__init__.py:52 msgid "Permissions error" msgstr "Erro de permissões" #: lib/solaar/ui/__init__.py:54 #, python-format msgid "Found a Logitech receiver or device (%s), but did not have " "permission to open it." msgstr "" #: lib/solaar/ui/__init__.py:55 msgid "If you've just installed Solaar, try disconnecting the receiver or " "device and then reconnecting it." msgstr "" #: lib/solaar/ui/__init__.py:58 msgid "Cannot connect to device error" msgstr "" #: lib/solaar/ui/__init__.py:60 #, python-format msgid "Found a Logitech receiver or device at %s, but encountered an error " "connecting to it." msgstr "" #: lib/solaar/ui/__init__.py:61 msgid "Try disconnecting the device and then reconnecting it or turning it " "off and then on." msgstr "" #: lib/solaar/ui/__init__.py:64 msgid "Unpairing failed" msgstr "Desemparelhamento falhado" #: lib/solaar/ui/__init__.py:66 #, python-brace-format msgid "Failed to unpair %{device} from %{receiver}." msgstr "" #: lib/solaar/ui/__init__.py:67 msgid "The receiver returned an error, with no further details." msgstr "O receptor devolveu um erro, sem mais detalhes." #: lib/solaar/ui/__init__.py:177 msgid "Another Solaar process is already running so just expose its window" msgstr "" #: lib/solaar/ui/about.py:36 msgid "Manages Logitech receivers,\n" "keyboards, mice, and tablets." msgstr "" #: lib/solaar/ui/about.py:44 msgid "Additional Programming" msgstr "" #: lib/solaar/ui/about.py:45 msgid "GUI design" msgstr "Desenho da GUI" #: lib/solaar/ui/about.py:47 msgid "Testing" msgstr "A testar" #: lib/solaar/ui/about.py:54 msgid "Logitech documentation" msgstr "Documentação da Logitech" #: lib/solaar/ui/action.py:85 lib/solaar/ui/action.py:89 #: lib/solaar/ui/window.py:197 msgid "Unpair" msgstr "Desemparelhar" #: lib/solaar/ui/action.py:88 lib/solaar/ui/diversion_rules.py:150 msgid "Cancel" msgstr "" #: lib/solaar/ui/config_panel.py:212 msgid "Complete - ENTER to change" msgstr "" #: lib/solaar/ui/config_panel.py:212 msgid "Incomplete" msgstr "" #: lib/solaar/ui/config_panel.py:455 lib/solaar/ui/config_panel.py:507 #, python-format msgid "%d value" msgid_plural "%d values" msgstr[0] "" msgstr[1] "" #: lib/solaar/ui/config_panel.py:518 msgid "Changes allowed" msgstr "" #: lib/solaar/ui/config_panel.py:519 msgid "No changes allowed" msgstr "" #: lib/solaar/ui/config_panel.py:520 msgid "Ignore this setting" msgstr "" #: lib/solaar/ui/config_panel.py:565 msgid "Working" msgstr "A funcionar" #: lib/solaar/ui/config_panel.py:568 msgid "Read/write operation failed." msgstr "Operação de leitura/escrita falhada." #: lib/solaar/ui/diversion_rules.py:65 msgid "Built-in rules" msgstr "" #: lib/solaar/ui/diversion_rules.py:65 msgid "User-defined rules" msgstr "" #: lib/solaar/ui/diversion_rules.py:67 lib/solaar/ui/diversion_rules.py:1083 msgid "Rule" msgstr "" #: lib/solaar/ui/diversion_rules.py:68 lib/solaar/ui/diversion_rules.py:509 #: lib/solaar/ui/diversion_rules.py:636 msgid "Sub-rule" msgstr "" #: lib/solaar/ui/diversion_rules.py:70 msgid "[empty]" msgstr "" #: lib/solaar/ui/diversion_rules.py:94 msgid "Solaar Rule Editor" msgstr "" #: lib/solaar/ui/diversion_rules.py:141 msgid "Make changes permanent?" msgstr "" #: lib/solaar/ui/diversion_rules.py:146 msgid "Yes" msgstr "Sim" #: lib/solaar/ui/diversion_rules.py:148 msgid "No" msgstr "" #: lib/solaar/ui/diversion_rules.py:153 msgid "If you choose No, changes will be lost when Solaar is closed." msgstr "" #: lib/solaar/ui/diversion_rules.py:201 msgid "Save changes" msgstr "" #: lib/solaar/ui/diversion_rules.py:206 msgid "Discard changes" msgstr "" #: lib/solaar/ui/diversion_rules.py:372 msgid "Insert here" msgstr "" #: lib/solaar/ui/diversion_rules.py:374 msgid "Insert above" msgstr "" #: lib/solaar/ui/diversion_rules.py:376 msgid "Insert below" msgstr "" #: lib/solaar/ui/diversion_rules.py:382 msgid "Insert new rule here" msgstr "" #: lib/solaar/ui/diversion_rules.py:384 msgid "Insert new rule above" msgstr "" #: lib/solaar/ui/diversion_rules.py:386 msgid "Insert new rule below" msgstr "" #: lib/solaar/ui/diversion_rules.py:427 msgid "Paste here" msgstr "" #: lib/solaar/ui/diversion_rules.py:429 msgid "Paste above" msgstr "" #: lib/solaar/ui/diversion_rules.py:431 msgid "Paste below" msgstr "" #: lib/solaar/ui/diversion_rules.py:437 msgid "Paste rule here" msgstr "" #: lib/solaar/ui/diversion_rules.py:439 msgid "Paste rule above" msgstr "" #: lib/solaar/ui/diversion_rules.py:441 msgid "Paste rule below" msgstr "" #: lib/solaar/ui/diversion_rules.py:445 msgid "Paste rule" msgstr "" #: lib/solaar/ui/diversion_rules.py:474 msgid "Flatten" msgstr "" #: lib/solaar/ui/diversion_rules.py:507 msgid "Insert" msgstr "" #: lib/solaar/ui/diversion_rules.py:510 lib/solaar/ui/diversion_rules.py:638 #: lib/solaar/ui/diversion_rules.py:1126 msgid "Or" msgstr "" #: lib/solaar/ui/diversion_rules.py:511 lib/solaar/ui/diversion_rules.py:637 #: lib/solaar/ui/diversion_rules.py:1111 msgid "And" msgstr "" #: lib/solaar/ui/diversion_rules.py:513 msgid "Condition" msgstr "" #: lib/solaar/ui/diversion_rules.py:515 lib/solaar/ui/diversion_rules.py:1292 msgid "Feature" msgstr "" #: lib/solaar/ui/diversion_rules.py:516 lib/solaar/ui/diversion_rules.py:1328 msgid "Report" msgstr "" #: lib/solaar/ui/diversion_rules.py:517 lib/solaar/ui/diversion_rules.py:1204 msgid "Process" msgstr "" #: lib/solaar/ui/diversion_rules.py:518 msgid "Mouse process" msgstr "" #: lib/solaar/ui/diversion_rules.py:519 lib/solaar/ui/diversion_rules.py:1366 msgid "Modifiers" msgstr "" #: lib/solaar/ui/diversion_rules.py:520 lib/solaar/ui/diversion_rules.py:1419 msgid "Key" msgstr "" #: lib/solaar/ui/diversion_rules.py:521 lib/solaar/ui/diversion_rules.py:1461 msgid "KeyIsDown" msgstr "" #: lib/solaar/ui/diversion_rules.py:522 lib/solaar/ui/diversion_rules.py:2260 msgid "Active" msgstr "Ativo" #: lib/solaar/ui/diversion_rules.py:523 lib/solaar/ui/diversion_rules.py:2218 #: lib/solaar/ui/diversion_rules.py:2270 lib/solaar/ui/diversion_rules.py:2323 msgid "Device" msgstr "" #: lib/solaar/ui/diversion_rules.py:524 lib/solaar/ui/diversion_rules.py:2297 msgid "Host" msgstr "" #: lib/solaar/ui/diversion_rules.py:525 lib/solaar/ui/diversion_rules.py:2339 msgid "Setting" msgstr "" #: lib/solaar/ui/diversion_rules.py:526 lib/solaar/ui/diversion_rules.py:1477 #: lib/solaar/ui/diversion_rules.py:1526 msgid "Test" msgstr "" #: lib/solaar/ui/diversion_rules.py:527 lib/solaar/ui/diversion_rules.py:1643 msgid "Test bytes" msgstr "" #: lib/solaar/ui/diversion_rules.py:528 lib/solaar/ui/diversion_rules.py:1736 msgid "Mouse Gesture" msgstr "" #: lib/solaar/ui/diversion_rules.py:532 msgid "Action" msgstr "Ação" #: lib/solaar/ui/diversion_rules.py:534 lib/solaar/ui/diversion_rules.py:1845 msgid "Key press" msgstr "" #: lib/solaar/ui/diversion_rules.py:535 lib/solaar/ui/diversion_rules.py:1897 msgid "Mouse scroll" msgstr "" #: lib/solaar/ui/diversion_rules.py:536 lib/solaar/ui/diversion_rules.py:1959 msgid "Mouse click" msgstr "" #: lib/solaar/ui/diversion_rules.py:537 msgid "Set" msgstr "" #: lib/solaar/ui/diversion_rules.py:538 lib/solaar/ui/diversion_rules.py:2030 msgid "Execute" msgstr "" #: lib/solaar/ui/diversion_rules.py:539 lib/solaar/ui/diversion_rules.py:1158 msgid "Later" msgstr "" #: lib/solaar/ui/diversion_rules.py:568 msgid "Insert new rule" msgstr "" #: lib/solaar/ui/diversion_rules.py:588 lib/solaar/ui/diversion_rules.py:1686 #: lib/solaar/ui/diversion_rules.py:1790 lib/solaar/ui/diversion_rules.py:1989 msgid "Delete" msgstr "" #: lib/solaar/ui/diversion_rules.py:610 msgid "Negate" msgstr "" #: lib/solaar/ui/diversion_rules.py:634 msgid "Wrap with" msgstr "" #: lib/solaar/ui/diversion_rules.py:656 msgid "Cut" msgstr "Cortar" #: lib/solaar/ui/diversion_rules.py:671 msgid "Paste" msgstr "Colar" #: lib/solaar/ui/diversion_rules.py:683 msgid "Copy" msgstr "Copiar" #: lib/solaar/ui/diversion_rules.py:1063 msgid "This editor does not support the selected rule component yet." msgstr "" #: lib/solaar/ui/diversion_rules.py:1138 msgid "Number of seconds to delay." msgstr "" #: lib/solaar/ui/diversion_rules.py:1177 msgid "Not" msgstr "" #: lib/solaar/ui/diversion_rules.py:1187 msgid "X11 active process. For use in X11 only." msgstr "" #: lib/solaar/ui/diversion_rules.py:1218 msgid "X11 mouse process. For use in X11 only." msgstr "" #: lib/solaar/ui/diversion_rules.py:1235 msgid "MouseProcess" msgstr "" #: lib/solaar/ui/diversion_rules.py:1260 msgid "Feature name of notification triggering rule processing." msgstr "" #: lib/solaar/ui/diversion_rules.py:1308 msgid "Report number of notification triggering rule processing." msgstr "" #: lib/solaar/ui/diversion_rules.py:1342 msgid "Active keyboard modifiers. Not always available in Wayland." msgstr "" #: lib/solaar/ui/diversion_rules.py:1383 msgid "Diverted key or button depressed or released.\n" "Use the Key/Button Diversion and Divert G Keys settings to divert " "keys and buttons." msgstr "" #: lib/solaar/ui/diversion_rules.py:1392 msgid "Key down" msgstr "" #: lib/solaar/ui/diversion_rules.py:1395 msgid "Key up" msgstr "" #: lib/solaar/ui/diversion_rules.py:1436 msgid "Diverted key or button is currently down.\n" "Use the Key/Button Diversion and Divert G Keys settings to divert " "keys and buttons." msgstr "" #: lib/solaar/ui/diversion_rules.py:1475 msgid "Test condition on notification triggering rule processing." msgstr "" #: lib/solaar/ui/diversion_rules.py:1479 msgid "Parameter" msgstr "" #: lib/solaar/ui/diversion_rules.py:1542 msgid "begin (inclusive)" msgstr "" #: lib/solaar/ui/diversion_rules.py:1543 msgid "end (exclusive)" msgstr "" #: lib/solaar/ui/diversion_rules.py:1552 msgid "range" msgstr "" #: lib/solaar/ui/diversion_rules.py:1554 msgid "minimum" msgstr "" #: lib/solaar/ui/diversion_rules.py:1555 msgid "maximum" msgstr "" #: lib/solaar/ui/diversion_rules.py:1557 #, python-format msgid "bytes %(0)d to %(1)d, ranging from %(2)d to %(3)d" msgstr "" #: lib/solaar/ui/diversion_rules.py:1562 msgid "mask" msgstr "" #: lib/solaar/ui/diversion_rules.py:1563 #, python-format msgid "bytes %(0)d to %(1)d, mask %(2)d" msgstr "" #: lib/solaar/ui/diversion_rules.py:1573 msgid "Bit or range test on bytes in notification message triggering rule " "processing." msgstr "" #: lib/solaar/ui/diversion_rules.py:1583 msgid "type" msgstr "" #: lib/solaar/ui/diversion_rules.py:1666 msgid "Mouse gesture with optional initiating button followed by zero or " "more mouse movements." msgstr "" #: lib/solaar/ui/diversion_rules.py:1671 msgid "Add movement" msgstr "" #: lib/solaar/ui/diversion_rules.py:1764 msgid "Simulate a chorded key click or depress or release.\n" "On Wayland requires write access to /dev/uinput." msgstr "" #: lib/solaar/ui/diversion_rules.py:1769 msgid "Add key" msgstr "" #: lib/solaar/ui/diversion_rules.py:1772 msgid "Click" msgstr "" #: lib/solaar/ui/diversion_rules.py:1775 msgid "Depress" msgstr "" #: lib/solaar/ui/diversion_rules.py:1778 msgid "Release" msgstr "" #: lib/solaar/ui/diversion_rules.py:1861 msgid "Simulate a mouse scroll.\n" "On Wayland requires write access to /dev/uinput." msgstr "" #: lib/solaar/ui/diversion_rules.py:1918 msgid "Simulate a mouse click.\n" "On Wayland requires write access to /dev/uinput." msgstr "" #: lib/solaar/ui/diversion_rules.py:1921 msgid "Button" msgstr "" #: lib/solaar/ui/diversion_rules.py:1922 msgid "Count and Action" msgstr "" #: lib/solaar/ui/diversion_rules.py:1972 msgid "Execute a command with arguments." msgstr "" #: lib/solaar/ui/diversion_rules.py:1975 msgid "Add argument" msgstr "" #: lib/solaar/ui/diversion_rules.py:2050 msgid "Toggle" msgstr "" #: lib/solaar/ui/diversion_rules.py:2050 msgid "True" msgstr "" #: lib/solaar/ui/diversion_rules.py:2051 msgid "False" msgstr "" #: lib/solaar/ui/diversion_rules.py:2065 msgid "Unsupported setting" msgstr "" #: lib/solaar/ui/diversion_rules.py:2223 lib/solaar/ui/diversion_rules.py:2242 #: lib/solaar/ui/diversion_rules.py:2328 lib/solaar/ui/diversion_rules.py:2570 #: lib/solaar/ui/diversion_rules.py:2588 msgid "Originating device" msgstr "" #: lib/solaar/ui/diversion_rules.py:2256 msgid "Device is active and its settings can be changed." msgstr "" #: lib/solaar/ui/diversion_rules.py:2266 msgid "Device that originated the current notification." msgstr "" #: lib/solaar/ui/diversion_rules.py:2280 msgid "Name of host computer." msgstr "" #: lib/solaar/ui/diversion_rules.py:2347 msgid "Value" msgstr "Valor" #: lib/solaar/ui/diversion_rules.py:2355 msgid "Item" msgstr "" #: lib/solaar/ui/diversion_rules.py:2630 msgid "Change setting on device" msgstr "" #: lib/solaar/ui/diversion_rules.py:2647 msgid "Setting on device" msgstr "" #: lib/solaar/ui/notify.py:124 lib/solaar/ui/tray.py:318 #: lib/solaar/ui/tray.py:323 lib/solaar/ui/window.py:739 msgid "offline" msgstr "offline" #: lib/solaar/ui/pair_window.py:122 lib/solaar/ui/pair_window.py:256 #: lib/solaar/ui/pair_window.py:288 #, python-format msgid "%(receiver_name)s: pair new device" msgstr "" #: lib/solaar/ui/pair_window.py:123 #, python-format msgid "Enter passcode on %(name)s." msgstr "" #: lib/solaar/ui/pair_window.py:126 #, python-format msgid "Type %(passcode)s and then press the enter key." msgstr "" #: lib/solaar/ui/pair_window.py:129 msgid "left" msgstr "" #: lib/solaar/ui/pair_window.py:129 msgid "right" msgstr "" #: lib/solaar/ui/pair_window.py:131 #, python-format msgid "Press %(code)s\n" "and then press left and right buttons simultaneously." msgstr "" #: lib/solaar/ui/pair_window.py:188 msgid "Pairing failed" msgstr "Emparelhamento falhado" #: lib/solaar/ui/pair_window.py:190 msgid "Make sure your device is within range, and has a decent battery " "charge." msgstr "Certifique-se que o seu dispositivo está dentro do alcance, e tem " "uma carga de bateria decente." #: lib/solaar/ui/pair_window.py:192 msgid "A new device was detected, but it is not compatible with this " "receiver." msgstr "Foi detectado um novo dispositivo, mas não é compatível com este " "receptor." #: lib/solaar/ui/pair_window.py:194 msgid "More paired devices than receiver can support." msgstr "" #: lib/solaar/ui/pair_window.py:196 msgid "No further details are available about the error." msgstr "Não há mais detalhes disponíveis acerca do erro." #: lib/solaar/ui/pair_window.py:210 msgid "Found a new device:" msgstr "" #: lib/solaar/ui/pair_window.py:235 msgid "The wireless link is not encrypted" msgstr "A ligação sem fios não está encriptada" #: lib/solaar/ui/pair_window.py:264 msgid "Unifying receivers are only compatible with Unifying devices." msgstr "" #: lib/solaar/ui/pair_window.py:266 msgid "Bolt receivers are only compatible with Bolt devices." msgstr "" #: lib/solaar/ui/pair_window.py:268 msgid "Other receivers are only compatible with a few devices." msgstr "" #: lib/solaar/ui/pair_window.py:270 msgid "The device must not be paired with a nearby powered-on receiver." msgstr "" #: lib/solaar/ui/pair_window.py:274 msgid "Press a pairing button or key until the pairing light flashes " "quickly." msgstr "" #: lib/solaar/ui/pair_window.py:276 msgid "You may have to first turn the device off and on again." msgstr "" #: lib/solaar/ui/pair_window.py:278 msgid "Turn on the device you want to pair." msgstr "Ligue o dispositivo que deseja emparelhar." #: lib/solaar/ui/pair_window.py:280 msgid "If the device is already turned on, turn it off and on again." msgstr "" #: lib/solaar/ui/pair_window.py:283 #, python-format msgid "\n" "\n" "This receiver has %d pairing remaining." msgid_plural "\n" "\n" "This receiver has %d pairings remaining." msgstr[0] "" msgstr[1] "" #: lib/solaar/ui/pair_window.py:286 msgid "\n" "Cancelling at this point will not use up a pairing." msgstr "" #: lib/solaar/ui/tray.py:58 msgid "No supported device found" msgstr "" #: lib/solaar/ui/tray.py:64 lib/solaar/ui/window.py:319 #, python-format msgid "About %s" msgstr "Acerca de %s" #: lib/solaar/ui/tray.py:65 lib/solaar/ui/window.py:317 #, python-format msgid "Quit %s" msgstr "Terminar %s" #: lib/solaar/ui/tray.py:297 lib/solaar/ui/tray.py:305 msgid "no receiver" msgstr "nenhum receptor" #: lib/solaar/ui/tray.py:321 msgid "no status" msgstr "nenhum estado" #: lib/solaar/ui/window.py:96 msgid "Scanning" msgstr "A sondar" #: lib/solaar/ui/window.py:129 msgid "Battery" msgstr "Bateria" #: lib/solaar/ui/window.py:132 msgid "Wireless Link" msgstr "Ligação Sem Fios" #: lib/solaar/ui/window.py:136 msgid "Lighting" msgstr "Iluminação" #: lib/solaar/ui/window.py:170 msgid "Show Technical Details" msgstr "Mostrar Detalhes Técnicos" #: lib/solaar/ui/window.py:186 msgid "Pair new device" msgstr "Emparelhar novo dispositivo" #: lib/solaar/ui/window.py:205 msgid "Select a device" msgstr "Seleccionar um dispositivo" #: lib/solaar/ui/window.py:322 msgid "Rule Editor" msgstr "" #: lib/solaar/ui/window.py:533 msgid "Path" msgstr "Caminho" #: lib/solaar/ui/window.py:536 msgid "USB ID" msgstr "" #: lib/solaar/ui/window.py:539 lib/solaar/ui/window.py:541 #: lib/solaar/ui/window.py:561 lib/solaar/ui/window.py:563 msgid "Serial" msgstr "Série" #: lib/solaar/ui/window.py:545 msgid "Index" msgstr "Índice" #: lib/solaar/ui/window.py:547 msgid "Wireless PID" msgstr "PID de ligação sem fios" #: lib/solaar/ui/window.py:549 msgid "Product ID" msgstr "ID do produto" #: lib/solaar/ui/window.py:551 msgid "Protocol" msgstr "Protocolo" #: lib/solaar/ui/window.py:551 msgid "Unknown" msgstr "Desconhecido" #: lib/solaar/ui/window.py:554 #, python-format msgid "%(rate)d ms (%(rate_hz)dHz)" msgstr "" #: lib/solaar/ui/window.py:554 msgid "Polling rate" msgstr "Rácio de votações" #: lib/solaar/ui/window.py:565 msgid "Unit ID" msgstr "" #: lib/solaar/ui/window.py:576 msgid "none" msgstr "nenhum" #: lib/solaar/ui/window.py:577 msgid "Notifications" msgstr "Notificações" #: lib/solaar/ui/window.py:621 msgid "No device paired." msgstr "" #: lib/solaar/ui/window.py:628 #, python-format msgid "Up to %(max_count)s device can be paired to this receiver." msgid_plural "Up to %(max_count)s devices can be paired to this receiver." msgstr[0] "" msgstr[1] "" #: lib/solaar/ui/window.py:634 msgid "Only one device can be paired to this receiver." msgstr "" #: lib/solaar/ui/window.py:638 #, python-format msgid "This receiver has %d pairing remaining." msgid_plural "This receiver has %d pairings remaining." msgstr[0] "" msgstr[1] "" #: lib/solaar/ui/window.py:692 msgid "Battery Voltage" msgstr "" #: lib/solaar/ui/window.py:694 msgid "Voltage reported by battery" msgstr "" #: lib/solaar/ui/window.py:696 msgid "Battery Level" msgstr "" #: lib/solaar/ui/window.py:698 msgid "Approximate level reported by battery" msgstr "" #: lib/solaar/ui/window.py:705 lib/solaar/ui/window.py:707 msgid "next reported " msgstr "" #: lib/solaar/ui/window.py:708 msgid " and next level to be reported." msgstr "" #: lib/solaar/ui/window.py:713 msgid "last known" msgstr "último conhecido" #: lib/solaar/ui/window.py:724 msgid "encrypted" msgstr "encriptado" #: lib/solaar/ui/window.py:726 msgid "The wireless link between this device and its receiver is encrypted." msgstr "A ligação sem fios entre este dispositivo e o seu receptor está " "encriptada." #: lib/solaar/ui/window.py:728 msgid "not encrypted" msgstr "não encriptado" #: lib/solaar/ui/window.py:732 msgid "The wireless link between this device and its receiver is not " "encrypted.\n" "This is a security issue for pointing devices, and a major security " "issue for text-input devices." msgstr "" #: lib/solaar/ui/window.py:748 #, python-format msgid "%(light_level)d lux" msgstr "" #~ msgid " paired devices." #~ msgstr " dispositivos emparelhados." #~ msgid "1 paired device." #~ msgstr "1 dispositivo emparelhado." #~ msgid "Count" #~ msgstr "Contagem" #, python-format #~ msgid "Failed to unpair %s from %s." #~ msgstr "Falha ao desemparelhar %s de %s." #, python-format #~ msgid "Found a Logitech Receiver (%s), but did not have permission " #~ "to open it." #~ msgstr "Encontrado um Receptor Logitech (%s), mas não teve " #~ "permissões para o abrir." #~ msgid "Found a new device" #~ msgstr "Encontrado um novo dispositivo" #~ msgid "If the device is already turned on,\n" #~ "turn if off and on again." #~ msgstr "Se o dispositivo já está ligado,\n" #~ "desligue-o e volte a ligá-lo." #~ msgid "If you've just installed Solaar, try removing the receiver " #~ "and plugging it back in." #~ msgstr "Se você acabou de instalar o Solaar, tente remover o " #~ "receptor e ligá-lo de novo." #~ msgid "No Logitech receiver found" #~ msgstr "Nenhum receptor Logitech encontrado" #~ msgid "No device paired" #~ msgstr "Nenhum dispositivo emparelhado" #~ msgid "Only one device can be paired to this receiver" #~ msgstr "Apenas um dispositivo pode ser emparelhado com este receptor" #~ msgid "Shows status of devices connected\n" #~ "through wireless Logitech receivers." #~ msgstr "Mostra o estado dos dispositivos ligados\n" #~ "através de receptores sem fio da Logitech." #~ msgid "Smooth Scrolling" #~ msgstr "Deslocamento de Ecrã Suave" #, python-format #~ msgid "The receiver only supports %d paired device(s)." #~ msgstr "O receptor apenas suporta %d dispositivo(s) emparelhados." #~ msgid "The receiver was unplugged." #~ msgstr "O receptor foi desligado." #~ msgid "The wireless link between this device and its receiver is " #~ "not encrypted.\n" #~ "\n" #~ "For pointing devices (mice, trackballs, trackpads), this is a minor " #~ "security issue.\n" #~ "\n" #~ "It is, however, a major security issue for text-input devices " #~ "(keyboards, numpads),\n" #~ "because typed text can be sniffed inconspicuously by 3rd parties " #~ "within range." #~ msgstr "A ligação sem fios entre este dispositivo e o seu receptor " #~ "não está encriptada.\n" #~ "\n" #~ "Para dispositivos apontadores (ratos, trackballs, trackpads), este é " #~ "um problema de segurança menor.\n" #~ "\n" #~ "No entanto, é um problema de segurança maior para dispositivos de " #~ "entrada de texto (teclados, teclados numéricos).\n" #~ "porque o texto teclado pode ser \"cheirado\" de modo imperceptível " #~ "por terceiros que estejam dentro do alcance da ligação." #~ msgid "USB id" #~ msgstr "id de USB" #, python-format #~ msgid "Up to %d devices can be paired to this receiver" #~ msgstr "Podem ser emparelhados até %d dispositivos com este receptor" #~ msgid "closed" #~ msgstr "fechado" #~ msgid "height" #~ msgstr "altura" #~ msgid "lux" #~ msgstr "lux" #~ msgid "open" #~ msgstr "aberto" #~ msgid "pair new device" #~ msgstr "emparelhar o novo dispositivo" #~ msgid "paired devices" #~ msgstr "dispositivos emparelhados" #~ msgid "pairing lock is " #~ msgstr "tranca de emparelhamento é " #~ msgid "unknown" #~ msgstr "desconhecido" #~ msgid "width" #~ msgstr "largura" Solaar-1.1.14/po/pt_BR.po000066400000000000000000001237371473525213700150310ustar00rootroot00000000000000# Portuguese translations for solaar package. # Copyright (C) 2015 THE solaar'S COPYRIGHT HOLDER # This file is distributed under the same license as the solaar package. # Automatically generated, 2015. # msgid "" msgstr "" "Project-Id-Version: solaar 0.9.2\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-08-20 18:21-0300\n" "PO-Revision-Date: 2021-08-20 18:29-0300\n" "Last-Translator: Vinícius \n" "Language-Team: Sídnei A. Drovetto Jr.\n" "Language: pt_BR\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Launchpad-Export-Date: 2020-10-21 16:52+0000\n" "X-Generator: Poedit 3.0\n" #: lib/logitech_receiver/base_usb.py:50 msgid "Unifying Receiver" msgstr "Receptor Unifying" #: lib/logitech_receiver/base_usb.py:58 lib/logitech_receiver/base_usb.py:68 #: lib/logitech_receiver/base_usb.py:79 lib/logitech_receiver/base_usb.py:90 #: lib/logitech_receiver/base_usb.py:101 msgid "Nano Receiver" msgstr "Receptor Nano" #: lib/logitech_receiver/base_usb.py:109 msgid "Lightspeed Receiver" msgstr "Receptor Lightspeed" #: lib/logitech_receiver/base_usb.py:117 msgid "EX100 Receiver 27 Mhz" msgstr "Receptor EX100 27 Mhz" #: lib/logitech_receiver/device.py:134 lib/solaar/ui/window.py:693 msgid "unknown" msgstr "desconhecida" #: lib/logitech_receiver/i18n.py:38 msgid "empty" msgstr "vazia" #: lib/logitech_receiver/i18n.py:39 msgid "critical" msgstr "crítica" #: lib/logitech_receiver/i18n.py:40 msgid "low" msgstr "fraca" #: lib/logitech_receiver/i18n.py:41 msgid "average" msgstr "média" #: lib/logitech_receiver/i18n.py:42 msgid "good" msgstr "boa" #: lib/logitech_receiver/i18n.py:43 msgid "full" msgstr "cheia" #: lib/logitech_receiver/i18n.py:46 msgid "discharging" msgstr "descarregando" #: lib/logitech_receiver/i18n.py:47 msgid "recharging" msgstr "recarregando" #: lib/logitech_receiver/i18n.py:48 lib/solaar/ui/window.py:721 msgid "charging" msgstr "carregando" #: lib/logitech_receiver/i18n.py:49 msgid "not charging" msgstr "não carregando" #: lib/logitech_receiver/i18n.py:50 msgid "almost full" msgstr "quase cheia" #: lib/logitech_receiver/i18n.py:51 msgid "charged" msgstr "carregada" #: lib/logitech_receiver/i18n.py:52 msgid "slow recharge" msgstr "recarga lenta" #: lib/logitech_receiver/i18n.py:53 msgid "invalid battery" msgstr "bateria inválida" #: lib/logitech_receiver/i18n.py:54 msgid "thermal error" msgstr "erro térmico" #: lib/logitech_receiver/i18n.py:55 msgid "error" msgstr "erro" #: lib/logitech_receiver/i18n.py:56 msgid "standard" msgstr "padrão" #: lib/logitech_receiver/i18n.py:57 msgid "fast" msgstr "rápido" #: lib/logitech_receiver/i18n.py:58 msgid "slow" msgstr "lento" #: lib/logitech_receiver/i18n.py:61 msgid "device timeout" msgstr "tempo esgotado do dispositivo" #: lib/logitech_receiver/i18n.py:62 msgid "device not supported" msgstr "dispositivo não suportado" #: lib/logitech_receiver/i18n.py:63 msgid "too many devices" msgstr "número excessivo de dispositivos" #: lib/logitech_receiver/i18n.py:64 msgid "sequence timeout" msgstr "tempo esgotado da sequência" #: lib/logitech_receiver/i18n.py:67 lib/solaar/ui/window.py:580 msgid "Firmware" msgstr "Firmware" #: lib/logitech_receiver/i18n.py:68 msgid "Bootloader" msgstr "Bootloader" #: lib/logitech_receiver/i18n.py:69 msgid "Hardware" msgstr "Hardware" #: lib/logitech_receiver/i18n.py:70 msgid "Other" msgstr "Outro" #: lib/logitech_receiver/i18n.py:73 msgid "Left Button" msgstr "Botão da esquerda" #: lib/logitech_receiver/i18n.py:74 msgid "Right Button" msgstr "Botão da direita" #: lib/logitech_receiver/i18n.py:75 msgid "Middle Button" msgstr "Botão do meio" #: lib/logitech_receiver/i18n.py:76 msgid "Back Button" msgstr "Botão Voltar" #: lib/logitech_receiver/i18n.py:77 msgid "Forward Button" msgstr "Botão Avançar" #: lib/logitech_receiver/i18n.py:78 msgid "Mouse Gesture Button" msgstr "Botão de gestos do mouse" #: lib/logitech_receiver/i18n.py:79 msgid "Smart Shift" msgstr "Smart Shift" #: lib/logitech_receiver/i18n.py:80 msgid "DPI Switch" msgstr "Mudança de DPI" #: lib/logitech_receiver/i18n.py:81 msgid "Left Tilt" msgstr "Inclinação para a esquerda" #: lib/logitech_receiver/i18n.py:82 msgid "Right Tilt" msgstr "Inclinação para a direita" #: lib/logitech_receiver/i18n.py:83 msgid "Left Click" msgstr "Clique com o botão esquerdo" #: lib/logitech_receiver/i18n.py:84 msgid "Right Click" msgstr "Clique com o botão direito" #: lib/logitech_receiver/i18n.py:85 msgid "Mouse Middle Button" msgstr "Botão do meio do mouse" #: lib/logitech_receiver/i18n.py:86 msgid "Mouse Back Button" msgstr "Botão de voltar do mouse" #: lib/logitech_receiver/i18n.py:87 msgid "Mouse Forward Button" msgstr "Botão de avançar do mouse" #: lib/logitech_receiver/i18n.py:88 msgid "Gesture Button Navigation" msgstr "Botão de navegação por gestos" #: lib/logitech_receiver/i18n.py:89 msgid "Mouse Scroll Left Button" msgstr "Botão de rolagem esquerda do mouse" #: lib/logitech_receiver/i18n.py:90 msgid "Mouse Scroll Right Button" msgstr "Botão de rolagem direita do mouse" #: lib/logitech_receiver/i18n.py:93 msgid "pressed" msgstr "pressionada" #: lib/logitech_receiver/i18n.py:94 msgid "released" msgstr "liberada" #: lib/logitech_receiver/notifications.py:76 msgid "pairing lock is closed" msgstr "trava de emparelhamento está fechada" #: lib/logitech_receiver/notifications.py:76 msgid "pairing lock is open" msgstr "trava de emparelhamento está aberta" #: lib/logitech_receiver/notifications.py:160 lib/solaar/ui/notify.py:123 msgid "connected" msgstr "conectado" #: lib/logitech_receiver/notifications.py:160 msgid "disconnected" msgstr "desconectado" #: lib/logitech_receiver/notifications.py:198 lib/solaar/ui/notify.py:121 msgid "unpaired" msgstr "desemparelhado" #: lib/logitech_receiver/notifications.py:248 msgid "powered on" msgstr "ligado" #: lib/logitech_receiver/settings.py:524 msgid "register" msgstr "register" #: lib/logitech_receiver/settings.py:540 lib/logitech_receiver/settings.py:566 msgid "feature" msgstr "feature" #: lib/logitech_receiver/settings_templates.py:71 msgid "Hand Detection" msgstr "Detecção de Mão" #: lib/logitech_receiver/settings_templates.py:71 msgid "Turn on illumination when the hands hover over the keyboard." msgstr "Ligar a iluminação quando a mão estiver sobre o telcado." #: lib/logitech_receiver/settings_templates.py:72 msgid "Scroll Wheel Smooth Scrolling" msgstr "Rolagem suave com a roda" #: lib/logitech_receiver/settings_templates.py:73 #: lib/logitech_receiver/settings_templates.py:78 #: lib/logitech_receiver/settings_templates.py:85 msgid "High-sensitivity mode for vertical scroll with the wheel." msgstr "Modo de alta-sensibilidade para rolagem vertical com a roda." #: lib/logitech_receiver/settings_templates.py:74 msgid "Side Scrolling" msgstr "Rolagem Lateral" #: lib/logitech_receiver/settings_templates.py:75 msgid "" "When disabled, pushing the wheel sideways sends custom button events\n" "instead of the standard side-scrolling events." msgstr "" "Quando desabilitado, pressionar a roda lateralmente envia eventos " "customizados\n" "ao invés dos eventos tradicionais de rolagem lateral." #: lib/logitech_receiver/settings_templates.py:77 msgid "Scroll Wheel High Resolution" msgstr "Alta resolução de rolagem com a roda" #: lib/logitech_receiver/settings_templates.py:79 msgid "Scroll Wheel HID++ Scrolling" msgstr "Rolagem com a roda usando HID++" #: lib/logitech_receiver/settings_templates.py:80 msgid "HID++ mode for vertical scroll with the wheel." msgstr "Modo HID++ para a rolagem vertical com a roda." #: lib/logitech_receiver/settings_templates.py:81 msgid "Effectively turns off wheel scrolling in Linux." msgstr "Na prática, desativa a rolagem com a roda no Linux." #: lib/logitech_receiver/settings_templates.py:82 msgid "Scroll Wheel Direction" msgstr "Direção de rolagem" #: lib/logitech_receiver/settings_templates.py:83 msgid "Invert direction for vertical scroll with wheel." msgstr "Inverte a direção da rolagem vertical." #: lib/logitech_receiver/settings_templates.py:84 msgid "Scroll Wheel Resolution" msgstr "Resolução da roda de rolagem" #: lib/logitech_receiver/settings_templates.py:86 msgid "Frequency of device polling, in milliseconds" msgstr "Frequência de sondagem, em milissegundos" #: lib/logitech_receiver/settings_templates.py:86 msgid "Polling Rate (ms)" msgstr "Taxa de Sondagem (ms)" #: lib/logitech_receiver/settings_templates.py:87 msgid "Swap Fx function" msgstr "Trocar função de teclas Fx" #: lib/logitech_receiver/settings_templates.py:88 msgid "" "When set, the F1..F12 keys will activate their special function,\n" "and you must hold the FN key to activate their standard function." msgstr "" "Quando marcada, as teclas F1..F12 vão acionar sua função especial,\n" "e você precisa manter pressionada a tecla FN para acionar sua função padrão." #: lib/logitech_receiver/settings_templates.py:90 msgid "" "When unset, the F1..F12 keys will activate their standard function,\n" "and you must hold the FN key to activate their special function." msgstr "" "Quando desmarcada, as teclas F1..F12 vão acionar sua função padrão,\n" "e você precisa pressionar a tecla FN para acionar sua função especial." #: lib/logitech_receiver/settings_templates.py:92 msgid "Mouse movement sensitivity" msgstr "Sensibilidade do movimento do mouse" #: lib/logitech_receiver/settings_templates.py:92 msgid "Sensitivity (DPI)" msgstr "Sensibilidade (DPI)" #: lib/logitech_receiver/settings_templates.py:93 msgid "Sensitivity (Pointer Speed)" msgstr "Sensibilidade (Velocidade do Ponteiro)" #: lib/logitech_receiver/settings_templates.py:94 msgid "Speed multiplier for mouse (256 is normal multiplier)." msgstr "Fator de multiplicação da velocidade do mouse (256 é o fator normal)." #: lib/logitech_receiver/settings_templates.py:95 msgid "Scroll Wheel Rachet" msgstr "Rolagem no modo catraca" #: lib/logitech_receiver/settings_templates.py:96 msgid "" "Automatically switch the mouse wheel between ratchet and freespin mode.\n" "The mouse wheel is always free at 0, and always ratcheted at 50" msgstr "" "Alternar automaticamente a roda do mouse entre o modo de catraca e o modo de " "rotação livre.\n" "A roda do mouse fica sempre livre no valor 0, e sempre no modo catraca no " "valor 50" #: lib/logitech_receiver/settings_templates.py:98 msgid "Backlight" msgstr "Luz de fundo" #: lib/logitech_receiver/settings_templates.py:98 msgid "Turn illumination on or off on keyboard." msgstr "Ativar ou desativar a iluminação do teclado." #: lib/logitech_receiver/settings_templates.py:99 msgid "Key/Button Actions" msgstr "Ações das teclas/botões" #: lib/logitech_receiver/settings_templates.py:100 msgid "Change the action for the key or button." msgstr "Trocar a ação da tecla/botão." #: lib/logitech_receiver/settings_templates.py:101 msgid "" "Changing important actions (such as for the left mouse button) can result in " "an unusable system." msgstr "" "Trocar ações importantes (como a do botão esquerdo do mouse) pode tornar o " "sistema inutilizável." #: lib/logitech_receiver/settings_templates.py:102 msgid "Key/Button Diversion" msgstr "Desvio de Tecla/Botão" #: lib/logitech_receiver/settings_templates.py:103 msgid "" "Make the key or button send HID++ notifications (which trigger Solaar rules " "but are otherwise ignored)." msgstr "" "Fazer as teclas ou botões enviarem notificações HID++ (que desencadeiam " "regras do Solaar, mas são ignoradas fora disso)." #: lib/logitech_receiver/settings_templates.py:104 msgid "Disable keys" msgstr "Desativar teclas" #: lib/logitech_receiver/settings_templates.py:104 msgid "Disable specific keyboard keys." msgstr "Desativar teclas específicas do teclado." #: lib/logitech_receiver/settings_templates.py:105 msgid "Change keys to match OS." msgstr "Trocar as teclas para corresponder ao SO." #: lib/logitech_receiver/settings_templates.py:105 msgid "Set OS" msgstr "Definir SO" #: lib/logitech_receiver/settings_templates.py:106 msgid "Change Host" msgstr "Trocar Hospedeiro" #: lib/logitech_receiver/settings_templates.py:106 msgid "Switch connection to a different host" msgstr "Trocar a conexão para um hospedeiro diferente" #: lib/logitech_receiver/settings_templates.py:107 msgid "Thumb Wheel HID++ Scrolling" msgstr "Rolagem com o polegar usando HID++" #: lib/logitech_receiver/settings_templates.py:108 msgid "HID++ mode for horizontal scroll with the thumb wheel." msgstr "Modo HID++ para a rolagem horizontal com o polegar." #: lib/logitech_receiver/settings_templates.py:109 msgid "Effectively turns off thumb scrolling in Linux." msgstr "Na prática, desativa a rolagem com o polegar no Linux." #: lib/logitech_receiver/settings_templates.py:110 msgid "Invert thumb wheel scroll direction." msgstr "Inverter a direção da rolagem com o polegar." #: lib/logitech_receiver/settings_templates.py:110 msgid "Thumb Wheel Direction" msgstr "Direção da rolagem com a roda" #: lib/logitech_receiver/settings_templates.py:111 msgid "Gestures" msgstr "Gestos" #: lib/logitech_receiver/settings_templates.py:111 msgid "Tweak the mouse/touchpad behaviour." msgstr "Ajustar o comportamento do mouse/touchpad." #: lib/logitech_receiver/settings_templates.py:112 msgid "Change numerical parameters of a mouse/touchpad." msgstr "Alterar parâmetros numéricos de um mouse/touchpad." #: lib/logitech_receiver/settings_templates.py:112 msgid "Gesture params" msgstr "Parâmetros de gestos" #: lib/logitech_receiver/settings_templates.py:113 msgid "DPI Sliding Adjustment" msgstr "Ajuste de DPI por deslizamento" #: lib/logitech_receiver/settings_templates.py:114 msgid "" "Adjust the DPI by sliding the mouse horizontally while holding the button " "down." msgstr "Ajustar DPI deslizando o mouse horizontalmente ao segurar o botão." #: lib/logitech_receiver/settings_templates.py:115 msgid "Mouse Gestures" msgstr "Gestos do mouse" #: lib/logitech_receiver/settings_templates.py:116 msgid "Send a gesture by sliding the mouse while holding the button down." msgstr "Enviar um gesto deslizando o mouse ao segurar o botão." #: lib/logitech_receiver/settings_templates.py:117 msgid "Divert crown events" msgstr "Desviar eventos da coroa" #: lib/logitech_receiver/settings_templates.py:118 msgid "" "Make crown send CROWN HID++ notifications (which trigger Solaar rules but " "are otherwise ignored)." msgstr "" "Faz a coroa enviar notificações HID++ CROWN (que desencadeiam regras do " "Solaar, mas são ignoradas fora disso)." #: lib/logitech_receiver/settings_templates.py:119 msgid "Divert G Keys" msgstr "Desviar teclas G" #: lib/logitech_receiver/settings_templates.py:120 msgid "" "Make G keys send GKEY HID++ notifications (which trigger Solaar rules but " "are otherwise ignored)." msgstr "" "Fazer as teclas G enviarem notificações HID++ GKEY (que desencadeiam regras " "do Solaar, mas são ignoradas fora disso)." #: lib/logitech_receiver/settings_templates.py:123 msgid "Performs a left click." msgstr "Executa um clique com botão esquerdo." #: lib/logitech_receiver/settings_templates.py:123 msgid "Single tap" msgstr "Toque único" #: lib/logitech_receiver/settings_templates.py:124 msgid "Performs a right click." msgstr "Executa um clique com botão direito." #: lib/logitech_receiver/settings_templates.py:124 msgid "Single tap with two fingers" msgstr "Toque único com dois dedos" #: lib/logitech_receiver/settings_templates.py:125 msgid "Single tap with three fingers" msgstr "Toque único com três dedos" #: lib/logitech_receiver/settings_templates.py:129 msgid "Double tap" msgstr "Toque duplo" #: lib/logitech_receiver/settings_templates.py:129 msgid "Performs a double click." msgstr "Executa um clique duplo." #: lib/logitech_receiver/settings_templates.py:130 msgid "Double tap with two fingers" msgstr "Toque duplo com dois dedos" #: lib/logitech_receiver/settings_templates.py:131 msgid "Double tap with three fingers" msgstr "Toque duplo com três dedos" #: lib/logitech_receiver/settings_templates.py:134 msgid "Drags items by dragging the finger after double tapping." msgstr "Deslizar itens arrastando o dedo após um toque duplo." #: lib/logitech_receiver/settings_templates.py:134 msgid "Tap and drag" msgstr "Tocar e arrastar" #: lib/logitech_receiver/settings_templates.py:135 msgid "Tap and drag with two fingers" msgstr "Tocar e arrastar com dois dedos" #: lib/logitech_receiver/settings_templates.py:136 msgid "Drags items by dragging the fingers after double tapping." msgstr "Arrasta itens arrastando os dedos após um toque duplo." #: lib/logitech_receiver/settings_templates.py:137 msgid "Tap and drag with three fingers" msgstr "Tocar e arrastar com três dedos" #: lib/logitech_receiver/settings_templates.py:139 msgid "Suppress tap and edge gestures" msgstr "Impedir gestos de toque e borda" #: lib/logitech_receiver/settings_templates.py:140 msgid "Disables tap and edge gestures (equivalent to pressing Fn+LeftClick)." msgstr "" "Desativa os gestos de toque e borda (equivalente a apertar Fn + clique " "esquerdo)." #: lib/logitech_receiver/settings_templates.py:141 msgid "Scroll with one finger" msgstr "Rolar com um dedo" #: lib/logitech_receiver/settings_templates.py:141 #: lib/logitech_receiver/settings_templates.py:142 #: lib/logitech_receiver/settings_templates.py:145 msgid "Scrolls." msgstr "Executa a rolagem." #: lib/logitech_receiver/settings_templates.py:142 #: lib/logitech_receiver/settings_templates.py:145 msgid "Scroll with two fingers" msgstr "Rolar com dois dedos" #: lib/logitech_receiver/settings_templates.py:143 msgid "Scroll horizontally with two fingers" msgstr "Rolar horizontalmente com dois dedos" #: lib/logitech_receiver/settings_templates.py:143 msgid "Scrolls horizontally." msgstr "Faz rolagem horizontal." #: lib/logitech_receiver/settings_templates.py:144 msgid "Scroll vertically with two fingers" msgstr "Rolar verticalmente com dois dedos" #: lib/logitech_receiver/settings_templates.py:144 msgid "Scrolls vertically." msgstr "Faz rolagem vertical." #: lib/logitech_receiver/settings_templates.py:146 msgid "Inverts the scrolling direction." msgstr "Inverte a direção de rolagem." #: lib/logitech_receiver/settings_templates.py:146 msgid "Natural scrolling" msgstr "Rolagem natural" #: lib/logitech_receiver/settings_templates.py:147 msgid "Enables the thumbwheel." msgstr "Ativa a roda do polegar." #: lib/logitech_receiver/settings_templates.py:147 msgid "Thumbwheel" msgstr "Roda do polegar" #: lib/logitech_receiver/settings_templates.py:158 #: lib/logitech_receiver/settings_templates.py:162 msgid "Swipe from the top edge" msgstr "Deslizar da borda superior" #: lib/logitech_receiver/settings_templates.py:159 msgid "Swipe from the left edge" msgstr "Deslizar da borda esquerda" #: lib/logitech_receiver/settings_templates.py:160 msgid "Swipe from the right edge" msgstr "Deslizar da borda direita" #: lib/logitech_receiver/settings_templates.py:161 msgid "Swipe from the bottom edge" msgstr "Deslizar da borda inferior" #: lib/logitech_receiver/settings_templates.py:163 msgid "Swipe two fingers from the left edge" msgstr "Deslizar dois dedos da borda esquerda" #: lib/logitech_receiver/settings_templates.py:164 msgid "Swipe two fingers from the right edge" msgstr "Deslizar dois dedos da borda direita" #: lib/logitech_receiver/settings_templates.py:165 msgid "Swipe two fingers from the bottom edge" msgstr "Deslizar dois dedos da borda inferior" #: lib/logitech_receiver/settings_templates.py:166 msgid "Swipe two fingers from the top edge" msgstr "Deslizar dois dedos da borda superior" #: lib/logitech_receiver/settings_templates.py:167 #: lib/logitech_receiver/settings_templates.py:171 msgid "Pinch to zoom out; spread to zoom in." msgstr "Aproximar os dedos para diminuir o zoom; afastá-los para aumentar." #: lib/logitech_receiver/settings_templates.py:167 msgid "Zoom with two fingers." msgstr "Fazer zoom com dois dedos." #: lib/logitech_receiver/settings_templates.py:168 msgid "Pinch to zoom out." msgstr "Aproximar os dedos para diminuir o zoom." #: lib/logitech_receiver/settings_templates.py:169 msgid "Spread to zoom in." msgstr "Afastar os dedos para aumentar o zoom." #: lib/logitech_receiver/settings_templates.py:170 msgid "Zoom with three fingers." msgstr "Fazer zoom com três dedos." #: lib/logitech_receiver/settings_templates.py:171 msgid "Zoom with two fingers" msgstr "Fazer zoom com dois dedos" #: lib/logitech_receiver/settings_templates.py:189 msgid "Pixel zone" msgstr "Região de pixels" #: lib/logitech_receiver/settings_templates.py:190 msgid "Ratio zone" msgstr "Região de proporção" #: lib/logitech_receiver/settings_templates.py:191 msgid "Scale factor" msgstr "Fator de escala" #: lib/logitech_receiver/settings_templates.py:191 msgid "Sets the cursor speed." msgstr "Define a velocidade do cursor." #: lib/logitech_receiver/settings_templates.py:195 msgid "Left" msgstr "Esquerda" #: lib/logitech_receiver/settings_templates.py:195 msgid "Left-most coordinate." msgstr "Coordenada do ponto mais à esquerda." #: lib/logitech_receiver/settings_templates.py:196 msgid "Top-most coordinate." msgstr "Coordenada do ponto mais acima." #: lib/logitech_receiver/settings_templates.py:196 msgid "top" msgstr "topo" #: lib/logitech_receiver/settings_templates.py:197 msgid "Width." msgstr "Largura." #: lib/logitech_receiver/settings_templates.py:197 msgid "width" msgstr "largura" #: lib/logitech_receiver/settings_templates.py:198 msgid "Height." msgstr "Altura." #: lib/logitech_receiver/settings_templates.py:198 msgid "height" msgstr "altura" #: lib/logitech_receiver/settings_templates.py:199 msgid "Cursor speed." msgstr "Velocidade do cursor." #: lib/logitech_receiver/settings_templates.py:199 msgid "Scale" msgstr "Escala" #: lib/logitech_receiver/settings_templates.py:202 #, python-format msgid "Disables the %s key." msgstr "Desativa a tecla %s." #: lib/logitech_receiver/settings_templates.py:521 #: lib/logitech_receiver/settings_templates.py:573 msgid "Off" msgstr "Desativado" #: lib/logitech_receiver/settings_templates.py:667 msgid "Diverted" msgstr "Desviado" #: lib/logitech_receiver/settings_templates.py:667 msgid "Regular" msgstr "Regular" #: lib/logitech_receiver/status.py:109 msgid "No paired devices." msgstr "Nenhum dispositivo emparelhado." #: lib/logitech_receiver/status.py:110 lib/solaar/ui/window.py:623 #, python-format msgid "%(count)s paired device." msgid_plural "%(count)s paired devices." msgstr[0] "%(count)s dispositivo emparelhado." msgstr[1] "%(count)s dispositivos emparelhados." #: lib/logitech_receiver/status.py:165 #, python-format msgid "Battery: %(level)s" msgstr "Bateria: %(level)s" #: lib/logitech_receiver/status.py:167 #, python-format msgid "Battery: %(percent)d%%" msgstr "Bateria: %(percent)d%%" #: lib/logitech_receiver/status.py:179 #, python-format msgid "Lighting: %(level)s lux" msgstr "Iluminação: %(level)s lux" #: lib/logitech_receiver/status.py:234 #, python-format msgid "Battery: %(level)s (%(status)s)" msgstr "Bateria: %(level)s (%(status)s)" #: lib/logitech_receiver/status.py:236 #, python-format msgid "Battery: %(percent)d%% (%(status)s)" msgstr "Bateria: %(percent)d%% (%(status)s)" #: lib/solaar/ui/__init__.py:52 msgid "Permissions error" msgstr "Erro de permissão" #: lib/solaar/ui/__init__.py:54 #, python-format msgid "Found a Logitech Receiver (%s), but did not have permission to open it." msgstr "" "Foi encontrado um Receptor da Logitech (%s), mas sem permissão para abri-lo." #: lib/solaar/ui/__init__.py:55 msgid "" "If you've just installed Solaar, try removing the receiver and plugging it " "back in." msgstr "" "Se você acabou de instalar o Solaar, tente retirar o receptor e conectá-lo " "novamente." #: lib/solaar/ui/__init__.py:58 msgid "Cannot connect to device error" msgstr "Erro - não foi possível conectar-se ao dispositivo" #: lib/solaar/ui/__init__.py:60 #, python-format msgid "" "Found a Logitech receiver or device at %s, but encountered an error " "connecting to it." msgstr "" "Foi encontrado um receptor ou dispositivo Logitech em %s, mas houve um erro " "ao conectar-se a ele." #: lib/solaar/ui/__init__.py:61 msgid "" "Try removing the device and plugging it back in or turning it off and then " "on." msgstr "" "Tente remover o dispositivo e conectá-lo novamente, ou desligá-lo e ligá-lo." #: lib/solaar/ui/__init__.py:64 msgid "Unpairing failed" msgstr "Desemparelhamento falhou" #: lib/solaar/ui/__init__.py:66 #, python-brace-format msgid "Failed to unpair %{device} from %{receiver}." msgstr "Falha ao desemparelhar %{device} do %{receiver}." #: lib/solaar/ui/__init__.py:67 msgid "The receiver returned an error, with no further details." msgstr "O receptor retornou um erro, sem maiores detalhes." #: lib/solaar/ui/about.py:39 msgid "" "Manages Logitech receivers,\n" "keyboards, mice, and tablets." msgstr "" "Gerencia receptores, teclados,\n" "mouses e tablets da Logitech." #: lib/solaar/ui/about.py:47 msgid "Additional Programming" msgstr "Programação adicional" #: lib/solaar/ui/about.py:48 msgid "GUI design" msgstr "Design da GUI" #: lib/solaar/ui/about.py:50 msgid "Testing" msgstr "Teste" #: lib/solaar/ui/about.py:57 msgid "Logitech documentation" msgstr "Documentação da Logitech" #: lib/solaar/ui/action.py:70 lib/solaar/ui/window.py:328 msgid "About %s" msgstr "Sobre o %s" #: lib/solaar/ui/action.py:96 lib/solaar/ui/action.py:100 #: lib/solaar/ui/window.py:205 msgid "Unpair" msgstr "Desemparelhar" #: lib/solaar/ui/action.py:99 lib/solaar/ui/diversion_rules.py:144 msgid "Cancel" msgstr "Cancelar" #: lib/solaar/ui/config_panel.py:358 msgid "Changes allowed" msgstr "Mudanças permitidas" #: lib/solaar/ui/config_panel.py:359 msgid "No changes allowed" msgstr "Mudanças não permitidas" #: lib/solaar/ui/config_panel.py:360 msgid "Ignore this setting" msgstr "Ignorar esta configuração" #: lib/solaar/ui/config_panel.py:401 msgid "Working" msgstr "Funcionando" #: lib/solaar/ui/config_panel.py:404 msgid "Read/write operation failed." msgstr "Operação de leitura/escrita falhou." #: lib/solaar/ui/config_panel.py:522 #, python-format msgid "%d value" msgid_plural "%d values" msgstr[0] "%d valor" msgstr[1] "%d valores" #: lib/solaar/ui/diversion_rules.py:57 msgid "Built-in rules" msgstr "Regras predefinidas" #: lib/solaar/ui/diversion_rules.py:57 msgid "User-defined rules" msgstr "Regras do usuário" #: lib/solaar/ui/diversion_rules.py:59 lib/solaar/ui/diversion_rules.py:792 msgid "Rule" msgstr "Regra" #: lib/solaar/ui/diversion_rules.py:60 lib/solaar/ui/diversion_rules.py:503 #: lib/solaar/ui/diversion_rules.py:622 msgid "Sub-rule" msgstr "Sub-regra" #: lib/solaar/ui/diversion_rules.py:62 msgid "[empty]" msgstr "[vazio]" #: lib/solaar/ui/diversion_rules.py:85 msgid "Solaar Rule Editor" msgstr "Editor de Regras do Solaar" #: lib/solaar/ui/diversion_rules.py:135 msgid "Make changes permanent?" msgstr "Tornar as alterações permanentes?" #: lib/solaar/ui/diversion_rules.py:140 msgid "Yes" msgstr "Sim" #: lib/solaar/ui/diversion_rules.py:142 msgid "No" msgstr "Não" #: lib/solaar/ui/diversion_rules.py:147 msgid "If you choose No, changes will be lost when Solaar is closed." msgstr "" "Se você escolher Não, as alterações serão perdidas quando o Solaar for " "fechado." #: lib/solaar/ui/diversion_rules.py:195 msgid "Save changes" msgstr "Salvar alterações" #: lib/solaar/ui/diversion_rules.py:200 msgid "Discard changes" msgstr "Descartar alterações" #: lib/solaar/ui/diversion_rules.py:366 msgid "Insert here" msgstr "Inserir aqui" #: lib/solaar/ui/diversion_rules.py:368 msgid "Insert above" msgstr "Inserir acima" #: lib/solaar/ui/diversion_rules.py:370 msgid "Insert below" msgstr "Inserir abaixo" #: lib/solaar/ui/diversion_rules.py:376 msgid "Insert new rule here" msgstr "Inserir nova regra aqui" #: lib/solaar/ui/diversion_rules.py:378 msgid "Insert new rule above" msgstr "Inserir nova regra acima" #: lib/solaar/ui/diversion_rules.py:380 msgid "Insert new rule below" msgstr "Inserir nova regra abaixo" #: lib/solaar/ui/diversion_rules.py:421 msgid "Paste here" msgstr "Colar aqui" #: lib/solaar/ui/diversion_rules.py:423 msgid "Paste above" msgstr "Colar acima" #: lib/solaar/ui/diversion_rules.py:425 msgid "Paste below" msgstr "Colar abaixo" #: lib/solaar/ui/diversion_rules.py:431 msgid "Paste rule here" msgstr "Colar regra aqui" #: lib/solaar/ui/diversion_rules.py:433 msgid "Paste rule above" msgstr "Colar regra acima" #: lib/solaar/ui/diversion_rules.py:435 msgid "Paste rule below" msgstr "Colar regra abaixo" #: lib/solaar/ui/diversion_rules.py:439 msgid "Paste rule" msgstr "Colar regra" #: lib/solaar/ui/diversion_rules.py:468 msgid "Flatten" msgstr "Desaninhar" #: lib/solaar/ui/diversion_rules.py:501 msgid "Insert" msgstr "Inserir" #: lib/solaar/ui/diversion_rules.py:504 lib/solaar/ui/diversion_rules.py:624 #: lib/solaar/ui/diversion_rules.py:835 msgid "Or" msgstr "Ou" #: lib/solaar/ui/diversion_rules.py:505 lib/solaar/ui/diversion_rules.py:623 #: lib/solaar/ui/diversion_rules.py:820 msgid "And" msgstr "E" #: lib/solaar/ui/diversion_rules.py:507 msgid "Condition" msgstr "Condição" #: lib/solaar/ui/diversion_rules.py:509 lib/solaar/ui/diversion_rules.py:951 msgid "Feature" msgstr "Feature" #: lib/solaar/ui/diversion_rules.py:510 lib/solaar/ui/diversion_rules.py:874 msgid "Process" msgstr "Processo" #: lib/solaar/ui/diversion_rules.py:511 lib/solaar/ui/diversion_rules.py:902 msgid "MouseProcess" msgstr "Processo (mouse)" #: lib/solaar/ui/diversion_rules.py:512 lib/solaar/ui/diversion_rules.py:984 msgid "Report" msgstr "Report" #: lib/solaar/ui/diversion_rules.py:513 lib/solaar/ui/diversion_rules.py:1019 msgid "Modifiers" msgstr "Teclas modificadoras" #: lib/solaar/ui/diversion_rules.py:514 lib/solaar/ui/diversion_rules.py:1066 msgid "Key" msgstr "Tecla" #: lib/solaar/ui/diversion_rules.py:515 lib/solaar/ui/diversion_rules.py:1111 msgid "Test" msgstr "Teste" #: lib/solaar/ui/diversion_rules.py:516 lib/solaar/ui/diversion_rules.py:1196 msgid "Mouse Gesture" msgstr "Gesto do mouse" #: lib/solaar/ui/diversion_rules.py:520 msgid "Action" msgstr "Ação" #: lib/solaar/ui/diversion_rules.py:522 lib/solaar/ui/diversion_rules.py:1286 msgid "Key press" msgstr "Pressionamento de tecla" #: lib/solaar/ui/diversion_rules.py:523 lib/solaar/ui/diversion_rules.py:1335 msgid "Mouse scroll" msgstr "Rolagem do mouse" #: lib/solaar/ui/diversion_rules.py:524 lib/solaar/ui/diversion_rules.py:1383 msgid "Mouse click" msgstr "Clique do mouse" #: lib/solaar/ui/diversion_rules.py:525 lib/solaar/ui/diversion_rules.py:1452 msgid "Execute" msgstr "Executar" #: lib/solaar/ui/diversion_rules.py:554 msgid "Insert new rule" msgstr "Inserir nova regra" #: lib/solaar/ui/diversion_rules.py:574 lib/solaar/ui/diversion_rules.py:1146 #: lib/solaar/ui/diversion_rules.py:1236 lib/solaar/ui/diversion_rules.py:1411 msgid "Delete" msgstr "Apagar" #: lib/solaar/ui/diversion_rules.py:596 msgid "Negate" msgstr "Negar" #: lib/solaar/ui/diversion_rules.py:620 msgid "Wrap with" msgstr "Envolver com" #: lib/solaar/ui/diversion_rules.py:642 msgid "Cut" msgstr "Recortar" #: lib/solaar/ui/diversion_rules.py:657 msgid "Paste" msgstr "Colar" #: lib/solaar/ui/diversion_rules.py:669 msgid "Copy" msgstr "Copiar" #: lib/solaar/ui/diversion_rules.py:772 msgid "This editor does not support the selected rule component yet." msgstr "Este editor ainda não suporta o componente de regra selecionado." #: lib/solaar/ui/diversion_rules.py:850 msgid "Not" msgstr "Não" #: lib/solaar/ui/diversion_rules.py:1039 msgid "Key down" msgstr "Tecla pressionada" #: lib/solaar/ui/diversion_rules.py:1042 msgid "Key up" msgstr "Tecla liberada" #: lib/solaar/ui/diversion_rules.py:1131 msgid "Add action" msgstr "Adicionar ação" #: lib/solaar/ui/diversion_rules.py:1224 msgid "Add key" msgstr "Adicionar tecla" #: lib/solaar/ui/diversion_rules.py:1354 msgid "Button" msgstr "Botão" #: lib/solaar/ui/diversion_rules.py:1355 msgid "Count" msgstr "Número" #: lib/solaar/ui/diversion_rules.py:1397 msgid "Add argument" msgstr "Adicionar argumento" #: lib/solaar/ui/notify.py:125 lib/solaar/ui/tray.py:319 #: lib/solaar/ui/tray.py:324 lib/solaar/ui/window.py:749 msgid "offline" msgstr "offline" #: lib/solaar/ui/pair_window.py:134 msgid "Pairing failed" msgstr "Emparelhamento falhou" #: lib/solaar/ui/pair_window.py:136 msgid "Make sure your device is within range, and has a decent battery charge." msgstr "" "Certifique-se de que o seu dispositivo esteja dentro do alcance e, que " "possui bateria com carga aceitável." #: lib/solaar/ui/pair_window.py:138 msgid "A new device was detected, but it is not compatible with this receiver." msgstr "" "Um novo dispositivo foi detectado, mas não é compatível com este receptor." #: lib/solaar/ui/pair_window.py:140 msgid "More paired devices than receiver can support." msgstr "Há mais dispositivos pareados do que o receptor suporta." #: lib/solaar/ui/pair_window.py:142 msgid "No further details are available about the error." msgstr "Sem maiores detalhes sobre o erro." #: lib/solaar/ui/pair_window.py:156 msgid "Found a new device:" msgstr "Encontrado um novo dispositivo:" #: lib/solaar/ui/pair_window.py:181 msgid "The wireless link is not encrypted" msgstr "A conexão sem fio não é criptografada" #: lib/solaar/ui/pair_window.py:199 #, python-format msgid "%(receiver_name)s: pair new device" msgstr "%(receiver_name)s: new dispositivo emparelhado" #: lib/solaar/ui/pair_window.py:206 msgid "If the device is already turned on, turn it off and on again." msgstr "Se o dispositivo já estiver ligado, desligue-o e ligue-o novamente." #: lib/solaar/ui/pair_window.py:209 #, python-format msgid "" "\n" "\n" "This receiver has %d pairing remaining." msgid_plural "" "\n" "\n" "This receiver has %d pairings remaining." msgstr[0] "" "\n" "\n" "Este dispositivo tem %d pareamento disponível." msgstr[1] "" "\n" "\n" "Este dispositivo tem %d pareamentos disponíveis." #: lib/solaar/ui/pair_window.py:212 msgid "" "\n" "Cancelling at this point will not use up a pairing." msgstr "" "\n" "Cancelar neste ponto não usará um pareamento." #: lib/solaar/ui/pair_window.py:215 msgid "Turn on the device you want to pair." msgstr "Ligue o dispositivo que você quer emparelhar." #: lib/solaar/ui/tray.py:61 msgid "No Logitech receiver found" msgstr "Nenhum receptor da Logitech encontrado" #: lib/solaar/ui/tray.py:68 lib/solaar/ui/window.py:325 msgid "Quit %s" msgstr "Sair do %s" #: lib/solaar/ui/tray.py:298 lib/solaar/ui/tray.py:306 msgid "no receiver" msgstr "nenhum receptor" #: lib/solaar/ui/tray.py:322 msgid "no status" msgstr "sem status" #: lib/solaar/ui/window.py:104 msgid "Scanning" msgstr "Procurando" #: lib/solaar/ui/window.py:137 lib/solaar/ui/window.py:692 msgid "Battery" msgstr "Bateria" #: lib/solaar/ui/window.py:140 msgid "Wireless Link" msgstr "Conexão sem fio" #: lib/solaar/ui/window.py:144 msgid "Lighting" msgstr "Iluminação" #: lib/solaar/ui/window.py:178 msgid "Show Technical Details" msgstr "Mostrar detalhes técnicos" #: lib/solaar/ui/window.py:194 msgid "Pair new device" msgstr "Emparelhar novo dispositivo" #: lib/solaar/ui/window.py:213 msgid "Select a device" msgstr "Selecione um dispositivo" #: lib/solaar/ui/window.py:331 msgid "Rule Editor" msgstr "Editor de regras" #: lib/solaar/ui/window.py:541 msgid "Path" msgstr "Caminho" #: lib/solaar/ui/window.py:544 msgid "USB ID" msgstr "ID USB" #: lib/solaar/ui/window.py:547 lib/solaar/ui/window.py:549 #: lib/solaar/ui/window.py:569 lib/solaar/ui/window.py:571 msgid "Serial" msgstr "Número de série" #: lib/solaar/ui/window.py:553 msgid "Index" msgstr "Índice" #: lib/solaar/ui/window.py:555 msgid "Wireless PID" msgstr "PID sem fio" #: lib/solaar/ui/window.py:557 msgid "Product ID" msgstr "ID do produto" #: lib/solaar/ui/window.py:559 msgid "Protocol" msgstr "Protocolo" #: lib/solaar/ui/window.py:559 msgid "Unknown" msgstr "Desconhecido" #: lib/solaar/ui/window.py:562 #, python-format msgid "%(rate)d ms (%(rate_hz)dHz)" msgstr "%(rate)d ms (%(rate_hz)dHz)" #: lib/solaar/ui/window.py:562 msgid "Polling rate" msgstr "Taxa de sondagem" #: lib/solaar/ui/window.py:573 msgid "Unit ID" msgstr "Unit ID" #: lib/solaar/ui/window.py:584 msgid "none" msgstr "nenhum" #: lib/solaar/ui/window.py:585 msgid "Notifications" msgstr "Notificações" #: lib/solaar/ui/window.py:622 msgid "No device paired." msgstr "Nenhum dispositivo emparelhado." #: lib/solaar/ui/window.py:629 #, python-format msgid "Up to %(max_count)s device can be paired to this receiver." msgid_plural "Up to %(max_count)s devices can be paired to this receiver." msgstr[0] "Até %(max_count)s dispositivo pode ser emparelhado neste receptor." msgstr[1] "" "Até %(max_count)s dispositivos podem ser emparelhados neste receptor." #: lib/solaar/ui/window.py:635 msgid "Only one device can be paired to this receiver." msgstr "Apenas um dispositivo pode ser emparelhado neste receptor." #: lib/solaar/ui/window.py:639 #, python-format msgid "This receiver has %d pairing remaining." msgid_plural "This receiver has %d pairings remaining." msgstr[0] "Este dispositivo tem %d pareamento disponível." msgstr[1] "Este dispositivo tem %d pareamentos disponíveis." #: lib/solaar/ui/window.py:694 msgid "Battery information unknown." msgstr "A informação sobre a bateria é desconhecida." #: lib/solaar/ui/window.py:702 msgid "Battery Voltage" msgstr "Tensão da Bateria" #: lib/solaar/ui/window.py:704 msgid "Voltage reported by battery" msgstr "Tensão informada pela bateria" #: lib/solaar/ui/window.py:706 lib/solaar/ui/window.py:710 msgid "Battery Level" msgstr "Nível de bateria" #: lib/solaar/ui/window.py:708 lib/solaar/ui/window.py:712 msgid "Approximate level reported by battery" msgstr "Nível aproximado informado pela bateria" #: lib/solaar/ui/window.py:715 lib/solaar/ui/window.py:717 msgid "next reported " msgstr "próximo valor informado: " #: lib/solaar/ui/window.py:718 msgid " and next level to be reported." msgstr " e o próximo nível que será informado." #: lib/solaar/ui/window.py:723 msgid "last known" msgstr "último conhecido" #: lib/solaar/ui/window.py:731 msgid "not encrypted" msgstr "não criptografada" #: lib/solaar/ui/window.py:735 msgid "" "The wireless link between this device and its receiver is not encrypted.\n" "\n" "For pointing devices (mice, trackballs, trackpads), this is a minor security " "issue.\n" "\n" "It is, however, a major security issue for text-input devices (keyboards, " "numpads),\n" "because typed text can be sniffed inconspicuously by 3rd parties within " "range." msgstr "" "A conexão sem fio entre o dispositivo e seu receptor não é criptografada.\n" "\n" "Para dispositivos de apontamento (mouses, trackballs, trackpads), isto não " "representa um grande problema de segurança.\n" "\n" "No entanto, é um grande problema de segurança para dispositivos de entrada " "de texto (teclados, teclados numéricos), pois\n" "o texto digitado pode ser capturado, de forma indetectável, por terceiros " "que estejam dentro do alcance." #: lib/solaar/ui/window.py:744 msgid "encrypted" msgstr "criptografada" #: lib/solaar/ui/window.py:746 msgid "The wireless link between this device and its receiver is encrypted." msgstr "A conexão sem fio entre o dispositivo e seu receptor é criptografada." #: lib/solaar/ui/window.py:759 #, python-format msgid "%(light_level)d lux" msgstr "%(light_level)d lux" #~ msgid "DPI Sliding Ajustment" #~ msgstr "Ajuste de DPI por deslizamento" #~ msgid "" #~ "Adjust the DPI by sliding the mouse horizontally while holding the DPI " #~ "button." #~ msgstr "" #~ "Ajustar o DPI deslizando o mouse horizontalmente ao segurar o botão DPI." #~ msgid "Triple tap" #~ msgstr "Toque triplo" #~ msgid "Solaar depends on a udev file that is not present" #~ msgstr "Solaar depende de um arquivo udev que não está presente." #~ msgid "" #~ "For more information see the Solaar installation directions\n" #~ "at https://pwr-solaar.github.io/Solaar/installation" #~ msgstr "" #~ "Para mais informações, consulte as instruções de instalação do Solaar\n" #~ "em https://pwr-solaar.github.io/Solaar/installation" #~ msgid "" #~ "Shows status of devices connected\n" #~ "through wireless Logitech receivers." #~ msgstr "" #~ "Mostra o status dos dispositivos conectados\n" #~ "através de receptores sem fio da Logitech." #~ msgid "If the device is already turned on, turn if off and on again." #~ msgstr "Se o dispositivo já estiver ligado, desligue-o e ligue-o novamente." #~ msgid "Smooth Scrolling" #~ msgstr "Rolagem suave" #~ msgid "High Resolution Wheel Invert" #~ msgstr "Roda Invertida de Alta Resolução" #~ msgid "High-sensitivity wheel invert mode for vertical scroll." #~ msgstr "Roda invertida de alta sensibilidade para rolagem vertical." #~ msgid "Wheel Resolution" #~ msgstr "Resolução da Roda" #~ msgid "" #~ "Automatically switch the mouse wheel between ratchet and freespin mode.\n" #~ "The mouse wheel is always free at 0, and always locked at 50" #~ msgstr "" #~ "Automaticamente alterna a roda do mouse entre modo catraca e modo livre.\n" #~ "A roda do mouse está sempre live em 0, e sempre em modo catraca em 50" #~ msgid "The receiver was unplugged." #~ msgstr "O receptor foi desplugado." #~ msgid "The receiver only supports %d paired device(s)." #~ msgstr "O receptor só suporta %d dispositivo(s) emparelhado(s)" #~ msgid "" #~ "If the device is already turned on,\n" #~ "turn if off and on again." #~ msgstr "" #~ "Se o dispositivo já encontra-se ligado,\n" #~ "desligue-o e ligue-o novamente." #~ msgid "USB id" #~ msgstr "ID do USB" #~ msgid "%(battery_percent)d%%" #~ msgstr "%(battery_percent)d%%" #~ msgid "%(battery_level)s" #~ msgstr "%(battery_level)s" #~ msgid "Only one device can be paired to this receiver" #~ msgstr "Apenas um dispositivo pode ser emparelhado com este receptor" #~ msgid "paired devices" #~ msgstr "dispositivos emparelhados" #~ msgid "Up to %d devices can be paired to this receiver" #~ msgstr "Até %d dispositivos podem ser emparelhados com este receptor" #~ msgid "No device paired" #~ msgstr "Nenhum dispositivo emparelhado" #~ msgid "pair new device" #~ msgstr "emparelhar novo dispositivo" #~ msgid "Found a new device" #~ msgstr "Novo dispositivo encontrado" #~ msgid "lux" #~ msgstr "lux" #~ msgid " paired devices." #~ msgstr " dispositivos emparelhados." #~ msgid "1 paired device." #~ msgstr "1 dispositivo emparelhado." #~ msgid "pairing lock is " #~ msgstr "trava de emparelhamento está " #~ msgid "open" #~ msgstr "aberto" #~ msgid "closed" #~ msgstr "fechado" Solaar-1.1.14/po/ro.po000066400000000000000000001351731473525213700144400ustar00rootroot00000000000000# Romanian translations for solaar package. # Copyright (C) 2013 THE solaar'S COPYRIGHT HOLDER # This file is distributed under the same license as the solaar package. # Automatically generated, 2013. # msgid "" msgstr "Project-Id-Version: solaar 0.9.1\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-12-28 17:40+0100\n" "PO-Revision-Date: 2013-07-17 20:27+0100\n" "Last-Translator: Daniel Pavel \n" "Language-Team: none\n" "Language: ro\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n==1 ? 0 : (n==0 || (n%100 > 0 && " "n%100 < 20)) ? 1 : 2;\n" "X-Generator: Poedit 1.5.4\n" #: lib/logitech_receiver/base_usb.py:46 msgid "Bolt Receiver" msgstr "" #: lib/logitech_receiver/base_usb.py:57 msgid "Unifying Receiver" msgstr "" #: lib/logitech_receiver/base_usb.py:67 lib/logitech_receiver/base_usb.py:78 #: lib/logitech_receiver/base_usb.py:90 lib/logitech_receiver/base_usb.py:102 #: lib/logitech_receiver/base_usb.py:114 msgid "Nano Receiver" msgstr "" #: lib/logitech_receiver/base_usb.py:124 msgid "Lightspeed Receiver" msgstr "" #: lib/logitech_receiver/base_usb.py:133 msgid "EX100 Receiver 27 Mhz" msgstr "" #: lib/logitech_receiver/i18n.py:30 msgid "empty" msgstr "descărcată" #: lib/logitech_receiver/i18n.py:31 msgid "critical" msgstr "aproape descărcată" #: lib/logitech_receiver/i18n.py:32 msgid "low" msgstr "joasă" #: lib/logitech_receiver/i18n.py:33 msgid "average" msgstr "" #: lib/logitech_receiver/i18n.py:34 msgid "good" msgstr "bună" #: lib/logitech_receiver/i18n.py:35 msgid "full" msgstr "plină" #: lib/logitech_receiver/i18n.py:38 msgid "discharging" msgstr "în descarcare" #: lib/logitech_receiver/i18n.py:39 msgid "recharging" msgstr "re-încărcare" #: lib/logitech_receiver/i18n.py:40 lib/solaar/ui/window.py:711 msgid "charging" msgstr "se încarcă" #: lib/logitech_receiver/i18n.py:41 msgid "not charging" msgstr "" #: lib/logitech_receiver/i18n.py:42 msgid "almost full" msgstr "aproape plină" #: lib/logitech_receiver/i18n.py:43 msgid "charged" msgstr "" #: lib/logitech_receiver/i18n.py:44 msgid "slow recharge" msgstr "încarcare inceată" #: lib/logitech_receiver/i18n.py:45 msgid "invalid battery" msgstr "baterie necorespunzătoare" #: lib/logitech_receiver/i18n.py:46 msgid "thermal error" msgstr "eroare termică" #: lib/logitech_receiver/i18n.py:47 msgid "error" msgstr "" #: lib/logitech_receiver/i18n.py:48 msgid "standard" msgstr "" #: lib/logitech_receiver/i18n.py:49 msgid "fast" msgstr "" #: lib/logitech_receiver/i18n.py:50 msgid "slow" msgstr "" #: lib/logitech_receiver/i18n.py:53 msgid "device timeout" msgstr "" #: lib/logitech_receiver/i18n.py:54 msgid "device not supported" msgstr "periferic incompatibil" #: lib/logitech_receiver/i18n.py:55 msgid "too many devices" msgstr "prea multe periferice" #: lib/logitech_receiver/i18n.py:56 msgid "sequence timeout" msgstr "" #: lib/logitech_receiver/i18n.py:59 lib/solaar/ui/window.py:572 msgid "Firmware" msgstr "" #: lib/logitech_receiver/i18n.py:60 msgid "Bootloader" msgstr "" #: lib/logitech_receiver/i18n.py:61 msgid "Hardware" msgstr "" #: lib/logitech_receiver/i18n.py:62 msgid "Other" msgstr "" #: lib/logitech_receiver/i18n.py:65 msgid "Left Button" msgstr "" #: lib/logitech_receiver/i18n.py:66 msgid "Right Button" msgstr "" #: lib/logitech_receiver/i18n.py:67 msgid "Middle Button" msgstr "" #: lib/logitech_receiver/i18n.py:68 msgid "Back Button" msgstr "" #: lib/logitech_receiver/i18n.py:69 msgid "Forward Button" msgstr "" #: lib/logitech_receiver/i18n.py:70 msgid "Mouse Gesture Button" msgstr "" #: lib/logitech_receiver/i18n.py:71 msgid "Smart Shift" msgstr "" #: lib/logitech_receiver/i18n.py:72 msgid "DPI Switch" msgstr "" #: lib/logitech_receiver/i18n.py:73 msgid "Left Tilt" msgstr "" #: lib/logitech_receiver/i18n.py:74 msgid "Right Tilt" msgstr "" #: lib/logitech_receiver/i18n.py:75 msgid "Left Click" msgstr "" #: lib/logitech_receiver/i18n.py:76 msgid "Right Click" msgstr "" #: lib/logitech_receiver/i18n.py:77 msgid "Mouse Middle Button" msgstr "" #: lib/logitech_receiver/i18n.py:78 msgid "Mouse Back Button" msgstr "" #: lib/logitech_receiver/i18n.py:79 msgid "Mouse Forward Button" msgstr "" #: lib/logitech_receiver/i18n.py:80 msgid "Gesture Button Navigation" msgstr "" #: lib/logitech_receiver/i18n.py:81 msgid "Mouse Scroll Left Button" msgstr "" #: lib/logitech_receiver/i18n.py:82 msgid "Mouse Scroll Right Button" msgstr "" #: lib/logitech_receiver/i18n.py:85 msgid "pressed" msgstr "" #: lib/logitech_receiver/i18n.py:86 msgid "released" msgstr "" #: lib/logitech_receiver/notifications.py:75 #: lib/logitech_receiver/notifications.py:126 msgid "pairing lock is closed" msgstr "" #: lib/logitech_receiver/notifications.py:75 #: lib/logitech_receiver/notifications.py:126 msgid "pairing lock is open" msgstr "" #: lib/logitech_receiver/notifications.py:92 msgid "discovery lock is closed" msgstr "" #: lib/logitech_receiver/notifications.py:92 msgid "discovery lock is open" msgstr "" #: lib/logitech_receiver/notifications.py:224 lib/solaar/ui/notify.py:122 msgid "connected" msgstr "conectat(ă)" #: lib/logitech_receiver/notifications.py:224 msgid "disconnected" msgstr "" #: lib/logitech_receiver/notifications.py:262 lib/solaar/ui/notify.py:120 msgid "unpaired" msgstr "deconectat(ă)" #: lib/logitech_receiver/notifications.py:304 msgid "powered on" msgstr "a pornit" #: lib/logitech_receiver/settings.py:750 msgid "register" msgstr "" #: lib/logitech_receiver/settings.py:764 lib/logitech_receiver/settings.py:791 msgid "feature" msgstr "" #: lib/logitech_receiver/settings_templates.py:139 msgid "Swap Fx function" msgstr "Inversează funcțiile Fx" #: lib/logitech_receiver/settings_templates.py:140 msgid "When set, the F1..F12 keys will activate their special function,\n" "and you must hold the FN key to activate their standard function." msgstr "Când este activ, tastele F1..F12 vor opera funcțiile speciale,\n" "și trebuie să țineți apăsată tasta FN pentru a folosi funcțiile lor " "standard." #: lib/logitech_receiver/settings_templates.py:142 msgid "When unset, the F1..F12 keys will activate their standard function,\n" "and you must hold the FN key to activate their special function." msgstr "Când nu este activ, tastele F1..F12 vor opera functiile standard,\n" "și trebuie să țineți apăsată tasta FN pentru a folosi funcțiile lor " "speciale." #: lib/logitech_receiver/settings_templates.py:149 msgid "Hand Detection" msgstr "" #: lib/logitech_receiver/settings_templates.py:150 msgid "Turn on illumination when the hands hover over the keyboard." msgstr "" #: lib/logitech_receiver/settings_templates.py:157 msgid "Scroll Wheel Smooth Scrolling" msgstr "" #: lib/logitech_receiver/settings_templates.py:158 #: lib/logitech_receiver/settings_templates.py:239 #: lib/logitech_receiver/settings_templates.py:267 msgid "High-sensitivity mode for vertical scroll with the wheel." msgstr "Senzitivitate crescută la derularea verticală cu rotița." #: lib/logitech_receiver/settings_templates.py:165 msgid "Side Scrolling" msgstr "Derulare orizontală" #: lib/logitech_receiver/settings_templates.py:167 msgid "When disabled, pushing the wheel sideways sends custom button " "events\n" "instead of the standard side-scrolling events." msgstr "" #: lib/logitech_receiver/settings_templates.py:177 msgid "Sensitivity (DPI - older mice)" msgstr "" #: lib/logitech_receiver/settings_templates.py:178 #: lib/logitech_receiver/settings_templates.py:712 msgid "Mouse movement sensitivity" msgstr "" #: lib/logitech_receiver/settings_templates.py:208 #: lib/logitech_receiver/settings_templates.py:218 #: lib/logitech_receiver/settings_templates.py:225 msgid "Backlight" msgstr "" #: lib/logitech_receiver/settings_templates.py:209 #: lib/logitech_receiver/settings_templates.py:226 msgid "Set illumination time for keyboard." msgstr "" #: lib/logitech_receiver/settings_templates.py:219 msgid "Turn illumination on or off on keyboard." msgstr "" #: lib/logitech_receiver/settings_templates.py:237 msgid "Scroll Wheel High Resolution" msgstr "" #: lib/logitech_receiver/settings_templates.py:240 #: lib/logitech_receiver/settings_templates.py:268 msgid "Set to ignore if scrolling is abnormally fast or slow" msgstr "" #: lib/logitech_receiver/settings_templates.py:247 #: lib/logitech_receiver/settings_templates.py:277 msgid "Scroll Wheel Diversion" msgstr "" #: lib/logitech_receiver/settings_templates.py:249 msgid "Make scroll wheel send LOWRES_WHEEL HID++ notifications (which " "trigger Solaar rules but are otherwise ignored)." msgstr "" #: lib/logitech_receiver/settings_templates.py:256 msgid "Scroll Wheel Direction" msgstr "" #: lib/logitech_receiver/settings_templates.py:257 msgid "Invert direction for vertical scroll with wheel." msgstr "" #: lib/logitech_receiver/settings_templates.py:265 msgid "Scroll Wheel Resolution" msgstr "" #: lib/logitech_receiver/settings_templates.py:279 msgid "Make scroll wheel send HIRES_WHEEL HID++ notifications (which " "trigger Solaar rules but are otherwise ignored)." msgstr "" #: lib/logitech_receiver/settings_templates.py:288 msgid "Sensitivity (Pointer Speed)" msgstr "" #: lib/logitech_receiver/settings_templates.py:289 msgid "Speed multiplier for mouse (256 is normal multiplier)." msgstr "" #: lib/logitech_receiver/settings_templates.py:299 msgid "Thumb Wheel Diversion" msgstr "" #: lib/logitech_receiver/settings_templates.py:301 msgid "Make thumb wheel send THUMB_WHEEL HID++ notifications (which trigger " "Solaar rules but are otherwise ignored)." msgstr "" #: lib/logitech_receiver/settings_templates.py:310 msgid "Thumb Wheel Direction" msgstr "" #: lib/logitech_receiver/settings_templates.py:311 msgid "Invert thumb wheel scroll direction." msgstr "" #: lib/logitech_receiver/settings_templates.py:319 msgid "Onboard Profiles" msgstr "" #: lib/logitech_receiver/settings_templates.py:320 msgid "Enable onboard profiles, which often control report rate and " "keyboard lighting" msgstr "" #: lib/logitech_receiver/settings_templates.py:330 msgid "Polling Rate (ms)" msgstr "" #: lib/logitech_receiver/settings_templates.py:332 msgid "Frequency of device polling, in milliseconds" msgstr "" #: lib/logitech_receiver/settings_templates.py:333 #: lib/logitech_receiver/settings_templates.py:1046 #: lib/logitech_receiver/settings_templates.py:1076 msgid "May need Onboard Profiles set to Disable to be effective." msgstr "" #: lib/logitech_receiver/settings_templates.py:365 msgid "Divert crown events" msgstr "" #: lib/logitech_receiver/settings_templates.py:366 msgid "Make crown send CROWN HID++ notifications (which trigger Solaar " "rules but are otherwise ignored)." msgstr "" #: lib/logitech_receiver/settings_templates.py:374 msgid "Crown smooth scroll" msgstr "" #: lib/logitech_receiver/settings_templates.py:375 msgid "Set crown smooth scroll" msgstr "" #: lib/logitech_receiver/settings_templates.py:383 msgid "Divert G Keys" msgstr "" #: lib/logitech_receiver/settings_templates.py:385 msgid "Make G keys send GKEY HID++ notifications (which trigger Solaar " "rules but are otherwise ignored)." msgstr "" #: lib/logitech_receiver/settings_templates.py:386 msgid "May also make M keys and MR key send HID++ notifications" msgstr "" #: lib/logitech_receiver/settings_templates.py:402 msgid "Scroll Wheel Ratcheted" msgstr "" #: lib/logitech_receiver/settings_templates.py:403 msgid "Switch the mouse wheel between speed-controlled ratcheting and " "always freespin." msgstr "" #: lib/logitech_receiver/settings_templates.py:405 msgid "Freespinning" msgstr "" #: lib/logitech_receiver/settings_templates.py:405 msgid "Ratcheted" msgstr "" #: lib/logitech_receiver/settings_templates.py:412 msgid "Scroll Wheel Ratchet Speed" msgstr "" #: lib/logitech_receiver/settings_templates.py:414 msgid "Use the mouse wheel speed to switch between ratcheted and " "freespinning.\n" "The mouse wheel is always ratcheted at 50." msgstr "" #: lib/logitech_receiver/settings_templates.py:463 msgid "Key/Button Actions" msgstr "" #: lib/logitech_receiver/settings_templates.py:465 msgid "Change the action for the key or button." msgstr "" #: lib/logitech_receiver/settings_templates.py:465 msgid "Overridden by diversion." msgstr "" #: lib/logitech_receiver/settings_templates.py:466 msgid "Changing important actions (such as for the left mouse button) can " "result in an unusable system." msgstr "" #: lib/logitech_receiver/settings_templates.py:639 msgid "Key/Button Diversion" msgstr "" #: lib/logitech_receiver/settings_templates.py:640 msgid "Make the key or button send HID++ notifications (Diverted) or " "initiate Mouse Gestures or Sliding DPI" msgstr "" #: lib/logitech_receiver/settings_templates.py:643 #: lib/logitech_receiver/settings_templates.py:644 #: lib/logitech_receiver/settings_templates.py:645 msgid "Diverted" msgstr "" #: lib/logitech_receiver/settings_templates.py:643 #: lib/logitech_receiver/settings_templates.py:644 msgid "Mouse Gestures" msgstr "" #: lib/logitech_receiver/settings_templates.py:643 #: lib/logitech_receiver/settings_templates.py:644 #: lib/logitech_receiver/settings_templates.py:645 msgid "Regular" msgstr "" #: lib/logitech_receiver/settings_templates.py:643 msgid "Sliding DPI" msgstr "" #: lib/logitech_receiver/settings_templates.py:711 msgid "Sensitivity (DPI)" msgstr "Sentivitivate (PPI)" #: lib/logitech_receiver/settings_templates.py:752 msgid "Sensitivity Switching" msgstr "" #: lib/logitech_receiver/settings_templates.py:754 msgid "Switch the current sensitivity and the remembered sensitivity when " "the key or button is pressed.\n" "If there is no remembered sensitivity, just remember the current " "sensitivity" msgstr "" #: lib/logitech_receiver/settings_templates.py:758 msgid "Off" msgstr "" #: lib/logitech_receiver/settings_templates.py:791 msgid "Disable keys" msgstr "" #: lib/logitech_receiver/settings_templates.py:792 msgid "Disable specific keyboard keys." msgstr "" #: lib/logitech_receiver/settings_templates.py:795 #, python-format msgid "Disables the %s key." msgstr "" #: lib/logitech_receiver/settings_templates.py:809 #: lib/logitech_receiver/settings_templates.py:860 msgid "Set OS" msgstr "" #: lib/logitech_receiver/settings_templates.py:810 #: lib/logitech_receiver/settings_templates.py:861 msgid "Change keys to match OS." msgstr "" #: lib/logitech_receiver/settings_templates.py:873 msgid "Change Host" msgstr "" #: lib/logitech_receiver/settings_templates.py:874 msgid "Switch connection to a different host" msgstr "" #: lib/logitech_receiver/settings_templates.py:900 msgid "Performs a left click." msgstr "" #: lib/logitech_receiver/settings_templates.py:900 msgid "Single tap" msgstr "" #: lib/logitech_receiver/settings_templates.py:901 msgid "Performs a right click." msgstr "" #: lib/logitech_receiver/settings_templates.py:901 msgid "Single tap with two fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:902 msgid "Single tap with three fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:906 msgid "Double tap" msgstr "" #: lib/logitech_receiver/settings_templates.py:906 msgid "Performs a double click." msgstr "" #: lib/logitech_receiver/settings_templates.py:907 msgid "Double tap with two fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:908 msgid "Double tap with three fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:911 msgid "Drags items by dragging the finger after double tapping." msgstr "" #: lib/logitech_receiver/settings_templates.py:911 msgid "Tap and drag" msgstr "" #: lib/logitech_receiver/settings_templates.py:913 msgid "Drags items by dragging the fingers after double tapping." msgstr "" #: lib/logitech_receiver/settings_templates.py:913 msgid "Tap and drag with two fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:914 msgid "Tap and drag with three fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:917 msgid "Disables tap and edge gestures (equivalent to pressing Fn+LeftClick)." msgstr "" #: lib/logitech_receiver/settings_templates.py:917 msgid "Suppress tap and edge gestures" msgstr "" #: lib/logitech_receiver/settings_templates.py:918 msgid "Scroll with one finger" msgstr "" #: lib/logitech_receiver/settings_templates.py:918 #: lib/logitech_receiver/settings_templates.py:919 #: lib/logitech_receiver/settings_templates.py:922 msgid "Scrolls." msgstr "" #: lib/logitech_receiver/settings_templates.py:919 #: lib/logitech_receiver/settings_templates.py:922 msgid "Scroll with two fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:920 msgid "Scroll horizontally with two fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:920 msgid "Scrolls horizontally." msgstr "" #: lib/logitech_receiver/settings_templates.py:921 msgid "Scroll vertically with two fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:921 msgid "Scrolls vertically." msgstr "" #: lib/logitech_receiver/settings_templates.py:923 msgid "Inverts the scrolling direction." msgstr "" #: lib/logitech_receiver/settings_templates.py:923 msgid "Natural scrolling" msgstr "" #: lib/logitech_receiver/settings_templates.py:924 msgid "Enables the thumbwheel." msgstr "" #: lib/logitech_receiver/settings_templates.py:924 msgid "Thumbwheel" msgstr "" #: lib/logitech_receiver/settings_templates.py:935 #: lib/logitech_receiver/settings_templates.py:939 msgid "Swipe from the top edge" msgstr "" #: lib/logitech_receiver/settings_templates.py:936 msgid "Swipe from the left edge" msgstr "" #: lib/logitech_receiver/settings_templates.py:937 msgid "Swipe from the right edge" msgstr "" #: lib/logitech_receiver/settings_templates.py:938 msgid "Swipe from the bottom edge" msgstr "" #: lib/logitech_receiver/settings_templates.py:940 msgid "Swipe two fingers from the left edge" msgstr "" #: lib/logitech_receiver/settings_templates.py:941 msgid "Swipe two fingers from the right edge" msgstr "" #: lib/logitech_receiver/settings_templates.py:942 msgid "Swipe two fingers from the bottom edge" msgstr "" #: lib/logitech_receiver/settings_templates.py:943 msgid "Swipe two fingers from the top edge" msgstr "" #: lib/logitech_receiver/settings_templates.py:944 #: lib/logitech_receiver/settings_templates.py:948 msgid "Pinch to zoom out; spread to zoom in." msgstr "" #: lib/logitech_receiver/settings_templates.py:944 msgid "Zoom with two fingers." msgstr "" #: lib/logitech_receiver/settings_templates.py:945 msgid "Pinch to zoom out." msgstr "" #: lib/logitech_receiver/settings_templates.py:946 msgid "Spread to zoom in." msgstr "" #: lib/logitech_receiver/settings_templates.py:947 msgid "Zoom with three fingers." msgstr "" #: lib/logitech_receiver/settings_templates.py:948 msgid "Zoom with two fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:966 msgid "Pixel zone" msgstr "" #: lib/logitech_receiver/settings_templates.py:967 msgid "Ratio zone" msgstr "" #: lib/logitech_receiver/settings_templates.py:968 msgid "Scale factor" msgstr "Factor de scalare" #: lib/logitech_receiver/settings_templates.py:968 msgid "Sets the cursor speed." msgstr "" #: lib/logitech_receiver/settings_templates.py:972 msgid "Left" msgstr "" #: lib/logitech_receiver/settings_templates.py:972 msgid "Left-most coordinate." msgstr "" #: lib/logitech_receiver/settings_templates.py:973 msgid "Bottom" msgstr "" #: lib/logitech_receiver/settings_templates.py:973 msgid "Bottom coordinate." msgstr "" #: lib/logitech_receiver/settings_templates.py:974 msgid "Width" msgstr "Lățime" #: lib/logitech_receiver/settings_templates.py:974 msgid "Width." msgstr "" #: lib/logitech_receiver/settings_templates.py:975 msgid "Height" msgstr "Înălțime" #: lib/logitech_receiver/settings_templates.py:975 msgid "Height." msgstr "" #: lib/logitech_receiver/settings_templates.py:976 msgid "Cursor speed." msgstr "" #: lib/logitech_receiver/settings_templates.py:976 msgid "Scale" msgstr "" #: lib/logitech_receiver/settings_templates.py:982 msgid "Gestures" msgstr "Gesturi" #: lib/logitech_receiver/settings_templates.py:983 msgid "Tweak the mouse/touchpad behaviour." msgstr "" #: lib/logitech_receiver/settings_templates.py:1000 msgid "Gestures Diversion" msgstr "" #: lib/logitech_receiver/settings_templates.py:1001 msgid "Divert mouse/touchpad gestures." msgstr "" #: lib/logitech_receiver/settings_templates.py:1018 msgid "Gesture params" msgstr "" #: lib/logitech_receiver/settings_templates.py:1019 msgid "Change numerical parameters of a mouse/touchpad." msgstr "" #: lib/logitech_receiver/settings_templates.py:1044 msgid "M-Key LEDs" msgstr "" #: lib/logitech_receiver/settings_templates.py:1046 msgid "Control the M-Key LEDs." msgstr "" #: lib/logitech_receiver/settings_templates.py:1047 #: lib/logitech_receiver/settings_templates.py:1077 msgid "May need G Keys diverted to be effective." msgstr "" #: lib/logitech_receiver/settings_templates.py:1053 #, python-format msgid "Lights up the %s key." msgstr "" #: lib/logitech_receiver/settings_templates.py:1074 msgid "MR-Key LED" msgstr "" #: lib/logitech_receiver/settings_templates.py:1076 msgid "Control the MR-Key LED." msgstr "" #: lib/logitech_receiver/settings_templates.py:1095 msgid "Persistent Key/Button Mapping" msgstr "" #: lib/logitech_receiver/settings_templates.py:1097 msgid "Permanently change the mapping for the key or button." msgstr "" #: lib/logitech_receiver/settings_templates.py:1098 msgid "Changing important keys or buttons (such as for the left mouse " "button) can result in an unusable system." msgstr "" #: lib/logitech_receiver/settings_templates.py:1157 msgid "Sidetone" msgstr "" #: lib/logitech_receiver/settings_templates.py:1158 msgid "Set sidetone level." msgstr "" #: lib/logitech_receiver/settings_templates.py:1167 msgid "Equalizer" msgstr "" #: lib/logitech_receiver/settings_templates.py:1168 msgid "Set equalizer levels." msgstr "" #: lib/logitech_receiver/settings_templates.py:1191 msgid "Hz" msgstr "" #: lib/logitech_receiver/settings_templates.py:1197 msgid "Power Management" msgstr "" #: lib/logitech_receiver/settings_templates.py:1198 msgid "Power off in minutes (0 for never)." msgstr "" #: lib/logitech_receiver/status.py:114 msgid "No paired devices." msgstr "Nici un periferic contectat." #: lib/logitech_receiver/status.py:115 lib/solaar/ui/window.py:622 #, python-format msgid "%(count)s paired device." msgid_plural "%(count)s paired devices." msgstr[0] "" msgstr[1] "" msgstr[2] "" #: lib/logitech_receiver/status.py:170 #, python-format msgid "Battery: %(level)s" msgstr "" #: lib/logitech_receiver/status.py:172 #, python-format msgid "Battery: %(percent)d%%" msgstr "" #: lib/logitech_receiver/status.py:184 #, python-format msgid "Lighting: %(level)s lux" msgstr "" #: lib/logitech_receiver/status.py:239 #, python-format msgid "Battery: %(level)s (%(status)s)" msgstr "" #: lib/logitech_receiver/status.py:241 #, python-format msgid "Battery: %(percent)d%% (%(status)s)" msgstr "" #: lib/solaar/ui/__init__.py:52 msgid "Permissions error" msgstr "Eroare de permisiuni" #: lib/solaar/ui/__init__.py:54 #, python-format msgid "Found a Logitech receiver or device (%s), but did not have " "permission to open it." msgstr "" #: lib/solaar/ui/__init__.py:55 msgid "If you've just installed Solaar, try disconnecting the receiver or " "device and then reconnecting it." msgstr "" #: lib/solaar/ui/__init__.py:58 msgid "Cannot connect to device error" msgstr "" #: lib/solaar/ui/__init__.py:60 #, python-format msgid "Found a Logitech receiver or device at %s, but encountered an error " "connecting to it." msgstr "" #: lib/solaar/ui/__init__.py:61 msgid "Try disconnecting the device and then reconnecting it or turning it " "off and then on." msgstr "" #: lib/solaar/ui/__init__.py:64 msgid "Unpairing failed" msgstr "Deconectare eșuată" #: lib/solaar/ui/__init__.py:66 #, python-brace-format msgid "Failed to unpair %{device} from %{receiver}." msgstr "Deconectarea %{device} de la %{receiver} a eșuat." #: lib/solaar/ui/__init__.py:67 msgid "The receiver returned an error, with no further details." msgstr "Receptorul a semnalat o eroare, fără alte detalii." #: lib/solaar/ui/__init__.py:177 msgid "Another Solaar process is already running so just expose its window" msgstr "" #: lib/solaar/ui/about.py:36 msgid "Manages Logitech receivers,\n" "keyboards, mice, and tablets." msgstr "" #: lib/solaar/ui/about.py:44 msgid "Additional Programming" msgstr "" #: lib/solaar/ui/about.py:45 msgid "GUI design" msgstr "Interfață grafica" #: lib/solaar/ui/about.py:47 msgid "Testing" msgstr "Testare" #: lib/solaar/ui/about.py:54 msgid "Logitech documentation" msgstr "Documentație Logitech" #: lib/solaar/ui/action.py:85 lib/solaar/ui/action.py:89 #: lib/solaar/ui/window.py:197 msgid "Unpair" msgstr "Deconectează" #: lib/solaar/ui/action.py:88 lib/solaar/ui/diversion_rules.py:150 msgid "Cancel" msgstr "" #: lib/solaar/ui/config_panel.py:212 msgid "Complete - ENTER to change" msgstr "" #: lib/solaar/ui/config_panel.py:212 msgid "Incomplete" msgstr "" #: lib/solaar/ui/config_panel.py:455 lib/solaar/ui/config_panel.py:507 #, python-format msgid "%d value" msgid_plural "%d values" msgstr[0] "" msgstr[1] "" msgstr[2] "" #: lib/solaar/ui/config_panel.py:518 msgid "Changes allowed" msgstr "" #: lib/solaar/ui/config_panel.py:519 msgid "No changes allowed" msgstr "" #: lib/solaar/ui/config_panel.py:520 msgid "Ignore this setting" msgstr "" #: lib/solaar/ui/config_panel.py:565 msgid "Working" msgstr "Prelucrez" #: lib/solaar/ui/config_panel.py:568 msgid "Read/write operation failed." msgstr "Operațiunea a eșuat." #: lib/solaar/ui/diversion_rules.py:65 msgid "Built-in rules" msgstr "" #: lib/solaar/ui/diversion_rules.py:65 msgid "User-defined rules" msgstr "" #: lib/solaar/ui/diversion_rules.py:67 lib/solaar/ui/diversion_rules.py:1083 msgid "Rule" msgstr "" #: lib/solaar/ui/diversion_rules.py:68 lib/solaar/ui/diversion_rules.py:509 #: lib/solaar/ui/diversion_rules.py:636 msgid "Sub-rule" msgstr "" #: lib/solaar/ui/diversion_rules.py:70 msgid "[empty]" msgstr "" #: lib/solaar/ui/diversion_rules.py:94 msgid "Solaar Rule Editor" msgstr "" #: lib/solaar/ui/diversion_rules.py:141 msgid "Make changes permanent?" msgstr "" #: lib/solaar/ui/diversion_rules.py:146 msgid "Yes" msgstr "Da" #: lib/solaar/ui/diversion_rules.py:148 msgid "No" msgstr "" #: lib/solaar/ui/diversion_rules.py:153 msgid "If you choose No, changes will be lost when Solaar is closed." msgstr "" #: lib/solaar/ui/diversion_rules.py:201 msgid "Save changes" msgstr "" #: lib/solaar/ui/diversion_rules.py:206 msgid "Discard changes" msgstr "" #: lib/solaar/ui/diversion_rules.py:372 msgid "Insert here" msgstr "" #: lib/solaar/ui/diversion_rules.py:374 msgid "Insert above" msgstr "" #: lib/solaar/ui/diversion_rules.py:376 msgid "Insert below" msgstr "" #: lib/solaar/ui/diversion_rules.py:382 msgid "Insert new rule here" msgstr "" #: lib/solaar/ui/diversion_rules.py:384 msgid "Insert new rule above" msgstr "" #: lib/solaar/ui/diversion_rules.py:386 msgid "Insert new rule below" msgstr "" #: lib/solaar/ui/diversion_rules.py:427 msgid "Paste here" msgstr "" #: lib/solaar/ui/diversion_rules.py:429 msgid "Paste above" msgstr "" #: lib/solaar/ui/diversion_rules.py:431 msgid "Paste below" msgstr "" #: lib/solaar/ui/diversion_rules.py:437 msgid "Paste rule here" msgstr "" #: lib/solaar/ui/diversion_rules.py:439 msgid "Paste rule above" msgstr "" #: lib/solaar/ui/diversion_rules.py:441 msgid "Paste rule below" msgstr "" #: lib/solaar/ui/diversion_rules.py:445 msgid "Paste rule" msgstr "" #: lib/solaar/ui/diversion_rules.py:474 msgid "Flatten" msgstr "" #: lib/solaar/ui/diversion_rules.py:507 msgid "Insert" msgstr "" #: lib/solaar/ui/diversion_rules.py:510 lib/solaar/ui/diversion_rules.py:638 #: lib/solaar/ui/diversion_rules.py:1126 msgid "Or" msgstr "" #: lib/solaar/ui/diversion_rules.py:511 lib/solaar/ui/diversion_rules.py:637 #: lib/solaar/ui/diversion_rules.py:1111 msgid "And" msgstr "" #: lib/solaar/ui/diversion_rules.py:513 msgid "Condition" msgstr "" #: lib/solaar/ui/diversion_rules.py:515 lib/solaar/ui/diversion_rules.py:1292 msgid "Feature" msgstr "" #: lib/solaar/ui/diversion_rules.py:516 lib/solaar/ui/diversion_rules.py:1328 msgid "Report" msgstr "" #: lib/solaar/ui/diversion_rules.py:517 lib/solaar/ui/diversion_rules.py:1204 msgid "Process" msgstr "" #: lib/solaar/ui/diversion_rules.py:518 msgid "Mouse process" msgstr "" #: lib/solaar/ui/diversion_rules.py:519 lib/solaar/ui/diversion_rules.py:1366 msgid "Modifiers" msgstr "" #: lib/solaar/ui/diversion_rules.py:520 lib/solaar/ui/diversion_rules.py:1419 msgid "Key" msgstr "" #: lib/solaar/ui/diversion_rules.py:521 lib/solaar/ui/diversion_rules.py:1461 msgid "KeyIsDown" msgstr "" #: lib/solaar/ui/diversion_rules.py:522 lib/solaar/ui/diversion_rules.py:2260 msgid "Active" msgstr "Activ" #: lib/solaar/ui/diversion_rules.py:523 lib/solaar/ui/diversion_rules.py:2218 #: lib/solaar/ui/diversion_rules.py:2270 lib/solaar/ui/diversion_rules.py:2323 msgid "Device" msgstr "" #: lib/solaar/ui/diversion_rules.py:524 lib/solaar/ui/diversion_rules.py:2297 msgid "Host" msgstr "" #: lib/solaar/ui/diversion_rules.py:525 lib/solaar/ui/diversion_rules.py:2339 msgid "Setting" msgstr "" #: lib/solaar/ui/diversion_rules.py:526 lib/solaar/ui/diversion_rules.py:1477 #: lib/solaar/ui/diversion_rules.py:1526 msgid "Test" msgstr "" #: lib/solaar/ui/diversion_rules.py:527 lib/solaar/ui/diversion_rules.py:1643 msgid "Test bytes" msgstr "" #: lib/solaar/ui/diversion_rules.py:528 lib/solaar/ui/diversion_rules.py:1736 msgid "Mouse Gesture" msgstr "" #: lib/solaar/ui/diversion_rules.py:532 msgid "Action" msgstr "Acțiune" #: lib/solaar/ui/diversion_rules.py:534 lib/solaar/ui/diversion_rules.py:1845 msgid "Key press" msgstr "" #: lib/solaar/ui/diversion_rules.py:535 lib/solaar/ui/diversion_rules.py:1897 msgid "Mouse scroll" msgstr "" #: lib/solaar/ui/diversion_rules.py:536 lib/solaar/ui/diversion_rules.py:1959 msgid "Mouse click" msgstr "" #: lib/solaar/ui/diversion_rules.py:537 msgid "Set" msgstr "" #: lib/solaar/ui/diversion_rules.py:538 lib/solaar/ui/diversion_rules.py:2030 msgid "Execute" msgstr "" #: lib/solaar/ui/diversion_rules.py:539 lib/solaar/ui/diversion_rules.py:1158 msgid "Later" msgstr "" #: lib/solaar/ui/diversion_rules.py:568 msgid "Insert new rule" msgstr "" #: lib/solaar/ui/diversion_rules.py:588 lib/solaar/ui/diversion_rules.py:1686 #: lib/solaar/ui/diversion_rules.py:1790 lib/solaar/ui/diversion_rules.py:1989 msgid "Delete" msgstr "" #: lib/solaar/ui/diversion_rules.py:610 msgid "Negate" msgstr "" #: lib/solaar/ui/diversion_rules.py:634 msgid "Wrap with" msgstr "" #: lib/solaar/ui/diversion_rules.py:656 msgid "Cut" msgstr "Taie" #: lib/solaar/ui/diversion_rules.py:671 msgid "Paste" msgstr "Lipește" #: lib/solaar/ui/diversion_rules.py:683 msgid "Copy" msgstr "Copiază" #: lib/solaar/ui/diversion_rules.py:1063 msgid "This editor does not support the selected rule component yet." msgstr "" #: lib/solaar/ui/diversion_rules.py:1138 msgid "Number of seconds to delay." msgstr "" #: lib/solaar/ui/diversion_rules.py:1177 msgid "Not" msgstr "" #: lib/solaar/ui/diversion_rules.py:1187 msgid "X11 active process. For use in X11 only." msgstr "" #: lib/solaar/ui/diversion_rules.py:1218 msgid "X11 mouse process. For use in X11 only." msgstr "" #: lib/solaar/ui/diversion_rules.py:1235 msgid "MouseProcess" msgstr "" #: lib/solaar/ui/diversion_rules.py:1260 msgid "Feature name of notification triggering rule processing." msgstr "" #: lib/solaar/ui/diversion_rules.py:1308 msgid "Report number of notification triggering rule processing." msgstr "" #: lib/solaar/ui/diversion_rules.py:1342 msgid "Active keyboard modifiers. Not always available in Wayland." msgstr "" #: lib/solaar/ui/diversion_rules.py:1383 msgid "Diverted key or button depressed or released.\n" "Use the Key/Button Diversion and Divert G Keys settings to divert " "keys and buttons." msgstr "" #: lib/solaar/ui/diversion_rules.py:1392 msgid "Key down" msgstr "" #: lib/solaar/ui/diversion_rules.py:1395 msgid "Key up" msgstr "" #: lib/solaar/ui/diversion_rules.py:1436 msgid "Diverted key or button is currently down.\n" "Use the Key/Button Diversion and Divert G Keys settings to divert " "keys and buttons." msgstr "" #: lib/solaar/ui/diversion_rules.py:1475 msgid "Test condition on notification triggering rule processing." msgstr "" #: lib/solaar/ui/diversion_rules.py:1479 msgid "Parameter" msgstr "" #: lib/solaar/ui/diversion_rules.py:1542 msgid "begin (inclusive)" msgstr "" #: lib/solaar/ui/diversion_rules.py:1543 msgid "end (exclusive)" msgstr "" #: lib/solaar/ui/diversion_rules.py:1552 msgid "range" msgstr "" #: lib/solaar/ui/diversion_rules.py:1554 msgid "minimum" msgstr "" #: lib/solaar/ui/diversion_rules.py:1555 msgid "maximum" msgstr "" #: lib/solaar/ui/diversion_rules.py:1557 #, python-format msgid "bytes %(0)d to %(1)d, ranging from %(2)d to %(3)d" msgstr "" #: lib/solaar/ui/diversion_rules.py:1562 msgid "mask" msgstr "" #: lib/solaar/ui/diversion_rules.py:1563 #, python-format msgid "bytes %(0)d to %(1)d, mask %(2)d" msgstr "" #: lib/solaar/ui/diversion_rules.py:1573 msgid "Bit or range test on bytes in notification message triggering rule " "processing." msgstr "" #: lib/solaar/ui/diversion_rules.py:1583 msgid "type" msgstr "" #: lib/solaar/ui/diversion_rules.py:1666 msgid "Mouse gesture with optional initiating button followed by zero or " "more mouse movements." msgstr "" #: lib/solaar/ui/diversion_rules.py:1671 msgid "Add movement" msgstr "" #: lib/solaar/ui/diversion_rules.py:1764 msgid "Simulate a chorded key click or depress or release.\n" "On Wayland requires write access to /dev/uinput." msgstr "" #: lib/solaar/ui/diversion_rules.py:1769 msgid "Add key" msgstr "" #: lib/solaar/ui/diversion_rules.py:1772 msgid "Click" msgstr "" #: lib/solaar/ui/diversion_rules.py:1775 msgid "Depress" msgstr "" #: lib/solaar/ui/diversion_rules.py:1778 msgid "Release" msgstr "" #: lib/solaar/ui/diversion_rules.py:1861 msgid "Simulate a mouse scroll.\n" "On Wayland requires write access to /dev/uinput." msgstr "" #: lib/solaar/ui/diversion_rules.py:1918 msgid "Simulate a mouse click.\n" "On Wayland requires write access to /dev/uinput." msgstr "" #: lib/solaar/ui/diversion_rules.py:1921 msgid "Button" msgstr "" #: lib/solaar/ui/diversion_rules.py:1922 msgid "Count and Action" msgstr "" #: lib/solaar/ui/diversion_rules.py:1972 msgid "Execute a command with arguments." msgstr "" #: lib/solaar/ui/diversion_rules.py:1975 msgid "Add argument" msgstr "" #: lib/solaar/ui/diversion_rules.py:2050 msgid "Toggle" msgstr "" #: lib/solaar/ui/diversion_rules.py:2050 msgid "True" msgstr "" #: lib/solaar/ui/diversion_rules.py:2051 msgid "False" msgstr "" #: lib/solaar/ui/diversion_rules.py:2065 msgid "Unsupported setting" msgstr "" #: lib/solaar/ui/diversion_rules.py:2223 lib/solaar/ui/diversion_rules.py:2242 #: lib/solaar/ui/diversion_rules.py:2328 lib/solaar/ui/diversion_rules.py:2570 #: lib/solaar/ui/diversion_rules.py:2588 msgid "Originating device" msgstr "" #: lib/solaar/ui/diversion_rules.py:2256 msgid "Device is active and its settings can be changed." msgstr "" #: lib/solaar/ui/diversion_rules.py:2266 msgid "Device that originated the current notification." msgstr "" #: lib/solaar/ui/diversion_rules.py:2280 msgid "Name of host computer." msgstr "" #: lib/solaar/ui/diversion_rules.py:2347 msgid "Value" msgstr "Valoare" #: lib/solaar/ui/diversion_rules.py:2355 msgid "Item" msgstr "" #: lib/solaar/ui/diversion_rules.py:2630 msgid "Change setting on device" msgstr "" #: lib/solaar/ui/diversion_rules.py:2647 msgid "Setting on device" msgstr "" #: lib/solaar/ui/notify.py:124 lib/solaar/ui/tray.py:318 #: lib/solaar/ui/tray.py:323 lib/solaar/ui/window.py:739 msgid "offline" msgstr "inactivă" #: lib/solaar/ui/pair_window.py:122 lib/solaar/ui/pair_window.py:256 #: lib/solaar/ui/pair_window.py:288 #, python-format msgid "%(receiver_name)s: pair new device" msgstr "" #: lib/solaar/ui/pair_window.py:123 #, python-format msgid "Enter passcode on %(name)s." msgstr "" #: lib/solaar/ui/pair_window.py:126 #, python-format msgid "Type %(passcode)s and then press the enter key." msgstr "" #: lib/solaar/ui/pair_window.py:129 msgid "left" msgstr "" #: lib/solaar/ui/pair_window.py:129 msgid "right" msgstr "" #: lib/solaar/ui/pair_window.py:131 #, python-format msgid "Press %(code)s\n" "and then press left and right buttons simultaneously." msgstr "" #: lib/solaar/ui/pair_window.py:188 msgid "Pairing failed" msgstr "Conectare eșuată" #: lib/solaar/ui/pair_window.py:190 msgid "Make sure your device is within range, and has a decent battery " "charge." msgstr "Asigurați-vă că dispozitivul este în apropiere, iar bateria este " "încarcată." #: lib/solaar/ui/pair_window.py:192 msgid "A new device was detected, but it is not compatible with this " "receiver." msgstr "A fost detectat un nou periferic, dar nu este compatibil cu acest " "receptor." #: lib/solaar/ui/pair_window.py:194 msgid "More paired devices than receiver can support." msgstr "" #: lib/solaar/ui/pair_window.py:196 msgid "No further details are available about the error." msgstr "Alte detalii despre eroare nu sunt disponibile." #: lib/solaar/ui/pair_window.py:210 msgid "Found a new device:" msgstr "" #: lib/solaar/ui/pair_window.py:235 msgid "The wireless link is not encrypted" msgstr "Legătura fără fir nu este criptată" #: lib/solaar/ui/pair_window.py:264 msgid "Unifying receivers are only compatible with Unifying devices." msgstr "" #: lib/solaar/ui/pair_window.py:266 msgid "Bolt receivers are only compatible with Bolt devices." msgstr "" #: lib/solaar/ui/pair_window.py:268 msgid "Other receivers are only compatible with a few devices." msgstr "" #: lib/solaar/ui/pair_window.py:270 msgid "The device must not be paired with a nearby powered-on receiver." msgstr "" #: lib/solaar/ui/pair_window.py:274 msgid "Press a pairing button or key until the pairing light flashes " "quickly." msgstr "" #: lib/solaar/ui/pair_window.py:276 msgid "You may have to first turn the device off and on again." msgstr "" #: lib/solaar/ui/pair_window.py:278 msgid "Turn on the device you want to pair." msgstr "Porniți dispozitivul pe care doriți să-l conectați." #: lib/solaar/ui/pair_window.py:280 msgid "If the device is already turned on, turn it off and on again." msgstr "" #: lib/solaar/ui/pair_window.py:283 #, python-format msgid "\n" "\n" "This receiver has %d pairing remaining." msgid_plural "\n" "\n" "This receiver has %d pairings remaining." msgstr[0] "" msgstr[1] "" msgstr[2] "" #: lib/solaar/ui/pair_window.py:286 msgid "\n" "Cancelling at this point will not use up a pairing." msgstr "" #: lib/solaar/ui/tray.py:58 msgid "No supported device found" msgstr "" #: lib/solaar/ui/tray.py:64 lib/solaar/ui/window.py:319 #, python-format msgid "About %s" msgstr "Despre %s" #: lib/solaar/ui/tray.py:65 lib/solaar/ui/window.py:317 #, python-format msgid "Quit %s" msgstr "Ieșire %s" #: lib/solaar/ui/tray.py:297 lib/solaar/ui/tray.py:305 msgid "no receiver" msgstr "nici un receptor" #: lib/solaar/ui/tray.py:321 msgid "no status" msgstr "stare necunoscută" #: lib/solaar/ui/window.py:96 msgid "Scanning" msgstr "Caut" #: lib/solaar/ui/window.py:129 msgid "Battery" msgstr "Baterie" #: lib/solaar/ui/window.py:132 msgid "Wireless Link" msgstr "Legatură fără fir" #: lib/solaar/ui/window.py:136 msgid "Lighting" msgstr "Lumină" #: lib/solaar/ui/window.py:170 msgid "Show Technical Details" msgstr "Detalii tehnice" #: lib/solaar/ui/window.py:186 msgid "Pair new device" msgstr "Conectează periferic" #: lib/solaar/ui/window.py:205 msgid "Select a device" msgstr "Selectați un dispozitiv" #: lib/solaar/ui/window.py:322 msgid "Rule Editor" msgstr "" #: lib/solaar/ui/window.py:533 msgid "Path" msgstr "Cale" #: lib/solaar/ui/window.py:536 msgid "USB ID" msgstr "" #: lib/solaar/ui/window.py:539 lib/solaar/ui/window.py:541 #: lib/solaar/ui/window.py:561 lib/solaar/ui/window.py:563 msgid "Serial" msgstr "Serial" #: lib/solaar/ui/window.py:545 msgid "Index" msgstr "Index" #: lib/solaar/ui/window.py:547 msgid "Wireless PID" msgstr "Cod WPID" #: lib/solaar/ui/window.py:549 msgid "Product ID" msgstr "ID produs" #: lib/solaar/ui/window.py:551 msgid "Protocol" msgstr "Protocol" #: lib/solaar/ui/window.py:551 msgid "Unknown" msgstr "Necunoscut" #: lib/solaar/ui/window.py:554 #, python-format msgid "%(rate)d ms (%(rate_hz)dHz)" msgstr "" #: lib/solaar/ui/window.py:554 msgid "Polling rate" msgstr "Rată acces" #: lib/solaar/ui/window.py:565 msgid "Unit ID" msgstr "" #: lib/solaar/ui/window.py:576 msgid "none" msgstr "nici una" #: lib/solaar/ui/window.py:577 msgid "Notifications" msgstr "Notificări" #: lib/solaar/ui/window.py:621 msgid "No device paired." msgstr "" #: lib/solaar/ui/window.py:628 #, python-format msgid "Up to %(max_count)s device can be paired to this receiver." msgid_plural "Up to %(max_count)s devices can be paired to this receiver." msgstr[0] "" msgstr[1] "" msgstr[2] "" #: lib/solaar/ui/window.py:634 msgid "Only one device can be paired to this receiver." msgstr "" #: lib/solaar/ui/window.py:638 #, python-format msgid "This receiver has %d pairing remaining." msgid_plural "This receiver has %d pairings remaining." msgstr[0] "" msgstr[1] "" msgstr[2] "" #: lib/solaar/ui/window.py:692 msgid "Battery Voltage" msgstr "" #: lib/solaar/ui/window.py:694 msgid "Voltage reported by battery" msgstr "" #: lib/solaar/ui/window.py:696 msgid "Battery Level" msgstr "" #: lib/solaar/ui/window.py:698 msgid "Approximate level reported by battery" msgstr "" #: lib/solaar/ui/window.py:705 lib/solaar/ui/window.py:707 msgid "next reported " msgstr "" #: lib/solaar/ui/window.py:708 msgid " and next level to be reported." msgstr "" #: lib/solaar/ui/window.py:713 msgid "last known" msgstr "ultima valoare" #: lib/solaar/ui/window.py:724 msgid "encrypted" msgstr "criptată" #: lib/solaar/ui/window.py:726 msgid "The wireless link between this device and its receiver is encrypted." msgstr "Legătura fără fir este criptată." #: lib/solaar/ui/window.py:728 msgid "not encrypted" msgstr "ne-criptată" #: lib/solaar/ui/window.py:732 msgid "The wireless link between this device and its receiver is not " "encrypted.\n" "This is a security issue for pointing devices, and a major security " "issue for text-input devices." msgstr "" #: lib/solaar/ui/window.py:748 #, python-format msgid "%(light_level)d lux" msgstr "" #~ msgid " paired devices." #~ msgstr " periferice contectate." #~ msgid "1 paired device." #~ msgstr "Un periferic contectat." #~ msgid "Count" #~ msgstr "Contor" #, python-format #~ msgid "Found a Logitech Receiver (%s), but did not have permission " #~ "to open it." #~ msgstr "Receptor Logitech detectat (%s), dar nu am permisiunea să-l " #~ "deschid." #~ msgid "Found a new device" #~ msgstr "Periferic nou detectat" #~ msgid "If the device is already turned on,\n" #~ "turn if off and on again." #~ msgstr "Dacă dispozitivul este deja pornit,\n" #~ "opriți-l și porniți-l din nou." #~ msgid "If you've just installed Solaar, try removing the receiver " #~ "and plugging it back in." #~ msgstr "Dacă tocmai ați instalat Solaar, scoateți receptorul și re-" #~ "introduceți-l." #~ msgid "No Logitech receiver found" #~ msgstr "Nu am găsit nici un receptor Logitech" #~ msgid "No device paired" #~ msgstr "Nici un periferic conectat" #~ msgid "Only one device can be paired to this receiver" #~ msgstr "Acest receptor suportă un singur periferic conectat" #~ msgid "Shows status of devices connected\n" #~ "through wireless Logitech receivers." #~ msgstr "Afișează starea perifericelor conectate\n" #~ "printr-un receptor Logitech fără fir." #~ msgid "Smooth Scrolling" #~ msgstr "Derulare fină" #, python-format #~ msgid "The receiver only supports %d paired device(s)." #~ msgstr "Receptorul suportă maxim %d periferic(e) contectate." #~ msgid "The receiver was unplugged." #~ msgstr "Receptor deconectat." #~ msgid "The wireless link between this device and its receiver is " #~ "not encrypted.\n" #~ "\n" #~ "For pointing devices (mice, trackballs, trackpads), this is a minor " #~ "security issue.\n" #~ "\n" #~ "It is, however, a major security issue for text-input devices " #~ "(keyboards, numpads),\n" #~ "because typed text can be sniffed inconspicuously by 3rd parties " #~ "within range." #~ msgstr "Legătura fără fir nu este criptată." #~ msgid "USB id" #~ msgstr "USB" #~ msgid "Up to %d devices can be paired to this receiver" #~ msgstr "Acest receptor suportă maxim %d periferice conectate" #~ msgid "closed" #~ msgstr "închis" #~ msgid "height" #~ msgstr "înălțime" #~ msgid "lux" #~ msgstr "lucși" #~ msgid "open" #~ msgstr "deschis" #~ msgid "pair new device" #~ msgstr "conectează periferic nou" #~ msgid "paired devices" #~ msgstr "periferice conectate" #~ msgid "pairing lock is " #~ msgstr "lacătul de contectare este " #~ msgid "unknown" #~ msgstr "necunoscută" #~ msgid "width" #~ msgstr "lățime" Solaar-1.1.14/po/ru.po000066400000000000000000002176541473525213700144530ustar00rootroot00000000000000# Russian translations for solaar package. # Copyright (C) 2013 THE solaar'S COPYRIGHT HOLDER # This file is distributed under the same license as the solaar package. # # Automatically generated, 2013. # Dmitry Ryazantcev , 2013-2016. # Vadim Illarionov , 2021. # Olesya Gerasimenko , 2021, 2022. # MistificaT0r <0mistificator0@gmail.com>, 2024. msgid "" msgstr "Project-Id-Version: solaar 1.1.13\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2024-05-09 01:08+0300\n" "PO-Revision-Date: 2024-04-21 01:38+0300\n" "Last-Translator: MistificaT0r <0mistificator0@gmail.com>\n" "Language-Team: ru\n" "Language: ru\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 " "&& n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" "X-Generator: Lokalize 24.02.2\n" #: lib/logitech_receiver/base_usb.py:45 msgid "Bolt Receiver" msgstr "Приёмник «Bolt»" #: lib/logitech_receiver/base_usb.py:58 msgid "Unifying Receiver" msgstr "Приёмник «Unifying»" #: lib/logitech_receiver/base_usb.py:70 lib/logitech_receiver/base_usb.py:83 #: lib/logitech_receiver/base_usb.py:97 lib/logitech_receiver/base_usb.py:111 #: lib/logitech_receiver/base_usb.py:125 msgid "Nano Receiver" msgstr "Приёмник «Nano»" #: lib/logitech_receiver/base_usb.py:138 msgid "Lightspeed Receiver" msgstr "Приёмник «Lightspeed»" #: lib/logitech_receiver/base_usb.py:149 msgid "EX100 Receiver 27 Mhz" msgstr "Приёмник «EX100» 27 МГц" #: lib/logitech_receiver/common.py:605 #, python-format msgid "Battery: %(level)s (%(status)s)" msgstr "Батарея: %(level)s (%(status)s)" #: lib/logitech_receiver/common.py:607 #, python-format msgid "Battery: %(percent)d%% (%(status)s)" msgstr "Батарея: %(percent)d%% (%(status)s)" #: lib/logitech_receiver/hidpp20.py:894 #: lib/logitech_receiver/settings_templates.py:307 msgid "Disabled" msgstr "Отключено" #: lib/logitech_receiver/hidpp20.py:895 msgid "Static" msgstr "Статичный" #: lib/logitech_receiver/hidpp20.py:896 msgid "Pulse" msgstr "Пульс" #: lib/logitech_receiver/hidpp20.py:897 msgid "Cycle" msgstr "Цикл" #: lib/logitech_receiver/hidpp20.py:898 msgid "Boot" msgstr "Запуск" #: lib/logitech_receiver/hidpp20.py:899 msgid "Demo" msgstr "Демонстрация" #: lib/logitech_receiver/hidpp20.py:900 msgid "Breathe" msgstr "Дышать" #: lib/logitech_receiver/hidpp20.py:901 msgid "Ripple" msgstr "Пульсация" #: lib/logitech_receiver/hidpp20.py:902 msgid "Decomposition" msgstr "Разложение" #: lib/logitech_receiver/hidpp20.py:903 msgid "Signature1" msgstr "Подпись1" #: lib/logitech_receiver/hidpp20.py:904 msgid "Signature2" msgstr "Подпись2" #: lib/logitech_receiver/hidpp20.py:905 msgid "CycleS" msgstr "Циклы" #: lib/logitech_receiver/hidpp20.py:969 msgid "Unknown Location" msgstr "Неизвестное местоположение" #: lib/logitech_receiver/hidpp20.py:970 msgid "Primary" msgstr "Первичный" #: lib/logitech_receiver/hidpp20.py:971 msgid "Logo" msgstr "Логотип" #: lib/logitech_receiver/hidpp20.py:972 msgid "Left Side" msgstr "Левая сторона" #: lib/logitech_receiver/hidpp20.py:973 msgid "Right Side" msgstr "Правая сторона" #: lib/logitech_receiver/hidpp20.py:974 msgid "Combined" msgstr "Комбинированный" #: lib/logitech_receiver/hidpp20.py:975 msgid "Primary 1" msgstr "Первичный 1" #: lib/logitech_receiver/hidpp20.py:976 msgid "Primary 2" msgstr "Первичный 2" #: lib/logitech_receiver/hidpp20.py:977 msgid "Primary 3" msgstr "Первичный 3" #: lib/logitech_receiver/hidpp20.py:978 msgid "Primary 4" msgstr "Первичный 4" #: lib/logitech_receiver/hidpp20.py:979 msgid "Primary 5" msgstr "Первичный 5" #: lib/logitech_receiver/hidpp20.py:980 msgid "Primary 6" msgstr "Первичный 6" #: lib/logitech_receiver/i18n.py:28 msgid "empty" msgstr "пусто" #: lib/logitech_receiver/i18n.py:29 msgid "critical" msgstr "критический" #: lib/logitech_receiver/i18n.py:30 msgid "low" msgstr "низкий" #: lib/logitech_receiver/i18n.py:31 msgid "average" msgstr "средний" #: lib/logitech_receiver/i18n.py:32 msgid "good" msgstr "нормальный" #: lib/logitech_receiver/i18n.py:33 msgid "full" msgstr "полный" #: lib/logitech_receiver/i18n.py:35 msgid "discharging" msgstr "разряжается" #: lib/logitech_receiver/i18n.py:36 msgid "recharging" msgstr "перезаряжается" #: lib/logitech_receiver/i18n.py:37 msgid "charging" msgstr "заряжается" #: lib/logitech_receiver/i18n.py:38 msgid "not charging" msgstr "не заряжается" #: lib/logitech_receiver/i18n.py:39 msgid "almost full" msgstr "почти полная" #: lib/logitech_receiver/i18n.py:40 msgid "charged" msgstr "заряжено" #: lib/logitech_receiver/i18n.py:41 msgid "slow recharge" msgstr "медленная разрядка" #: lib/logitech_receiver/i18n.py:42 msgid "invalid battery" msgstr "недопустимая батарея" #: lib/logitech_receiver/i18n.py:43 msgid "thermal error" msgstr "тепловая ошибка" #: lib/logitech_receiver/i18n.py:44 msgid "error" msgstr "ошибка" #: lib/logitech_receiver/i18n.py:45 msgid "standard" msgstr "стандартно" #: lib/logitech_receiver/i18n.py:46 msgid "fast" msgstr "быстро" #: lib/logitech_receiver/i18n.py:47 msgid "slow" msgstr "медленно" #: lib/logitech_receiver/i18n.py:49 msgid "device timeout" msgstr "тайм-аут устройства" #: lib/logitech_receiver/i18n.py:50 msgid "device not supported" msgstr "устройство не поддерживается" #: lib/logitech_receiver/i18n.py:51 msgid "too many devices" msgstr "слишком много устройств" #: lib/logitech_receiver/i18n.py:52 msgid "sequence timeout" msgstr "тайм-аут последовательности" #: lib/logitech_receiver/i18n.py:54 msgid "Firmware" msgstr "Прошивка" #: lib/logitech_receiver/i18n.py:55 msgid "Bootloader" msgstr "Загрузчик" #: lib/logitech_receiver/i18n.py:56 msgid "Hardware" msgstr "Оборудование" #: lib/logitech_receiver/i18n.py:57 msgid "Other" msgstr "Другое" #: lib/logitech_receiver/i18n.py:59 msgid "Left Button" msgstr "Левая кнопка" #: lib/logitech_receiver/i18n.py:60 msgid "Right Button" msgstr "Правая кнопка" #: lib/logitech_receiver/i18n.py:61 msgid "Middle Button" msgstr "Средняя кнопка" #: lib/logitech_receiver/i18n.py:62 msgid "Back Button" msgstr "Кнопка назад" #: lib/logitech_receiver/i18n.py:63 msgid "Forward Button" msgstr "Кнопка вперёд" #: lib/logitech_receiver/i18n.py:64 msgid "Mouse Gesture Button" msgstr "Кнопка жестов" #: lib/logitech_receiver/i18n.py:65 msgid "Smart Shift" msgstr "Умный Переход" #: lib/logitech_receiver/i18n.py:66 msgid "DPI Switch" msgstr "Переключатель чувствительности" #: lib/logitech_receiver/i18n.py:67 msgid "Left Tilt" msgstr "Наклон влево" #: lib/logitech_receiver/i18n.py:68 msgid "Right Tilt" msgstr "Наклон вправо" #: lib/logitech_receiver/i18n.py:69 msgid "Left Click" msgstr "Щелчок левой кнопкой" #: lib/logitech_receiver/i18n.py:70 msgid "Right Click" msgstr "Щелчок правой кнопкой" #: lib/logitech_receiver/i18n.py:71 msgid "Mouse Middle Button" msgstr "Средняя кнопка мыши" #: lib/logitech_receiver/i18n.py:72 msgid "Mouse Back Button" msgstr "Кнопка мыши Назад" #: lib/logitech_receiver/i18n.py:73 msgid "Mouse Forward Button" msgstr "Кнопка мыши Вперед" #: lib/logitech_receiver/i18n.py:74 msgid "Gesture Button Navigation" msgstr "Навигация кнопкой жестов" #: lib/logitech_receiver/i18n.py:75 msgid "Mouse Scroll Left Button" msgstr "Кнопка прокрутки влево" #: lib/logitech_receiver/i18n.py:76 msgid "Mouse Scroll Right Button" msgstr "Кнопка прокрутки вправо" #: lib/logitech_receiver/i18n.py:78 msgid "pressed" msgstr "нажата" #: lib/logitech_receiver/i18n.py:79 msgid "released" msgstr "отпущена" #: lib/logitech_receiver/notifications.py:64 #: lib/logitech_receiver/notifications.py:116 msgid "pairing lock is closed" msgstr "сопряжение заблокировано" #: lib/logitech_receiver/notifications.py:64 #: lib/logitech_receiver/notifications.py:116 msgid "pairing lock is open" msgstr "сопряжение разблокировано" #: lib/logitech_receiver/notifications.py:81 msgid "discovery lock is closed" msgstr "обнаружение заблокировано" #: lib/logitech_receiver/notifications.py:81 msgid "discovery lock is open" msgstr "обнаружение разблокировано" #: lib/logitech_receiver/notifications.py:208 lib/solaar/listener.py:238 msgid "connected" msgstr "соединено" #: lib/logitech_receiver/notifications.py:208 lib/solaar/listener.py:238 msgid "disconnected" msgstr "разъединено" #: lib/logitech_receiver/notifications.py:234 msgid "unpaired" msgstr "не сопряжено" #: lib/logitech_receiver/notifications.py:281 msgid "powered on" msgstr "включено" #: lib/logitech_receiver/receiver.py:351 msgid "No paired devices." msgstr "Нет сопряжённых устройств." #: lib/logitech_receiver/receiver.py:353 lib/solaar/ui/window.py:604 #, python-format msgid "%(count)s paired device." msgid_plural "%(count)s paired devices." msgstr[0] "%(count)s сопряжённое устройство." msgstr[1] "%(count)s сопряжённых устройства." msgstr[2] "%(count)s сопряжённых устройств." #: lib/logitech_receiver/settings.py:616 msgid "register" msgstr "зарегистрировано" #: lib/logitech_receiver/settings.py:630 lib/logitech_receiver/settings.py:656 msgid "feature" msgstr "особенность" #: lib/logitech_receiver/settings_templates.py:146 msgid "Swap Fx function" msgstr "Сменить поведение Fx" #: lib/logitech_receiver/settings_templates.py:149 msgid "When set, the F1..F12 keys will activate their special function,\n" "and you must hold the FN key to activate their standard function." msgstr "Когда включено, кнопки F1..F12 работают по дополнительным функциям,\n" "а выбор основных производится с зажатой кнопкой «FN»." #: lib/logitech_receiver/settings_templates.py:154 msgid "When unset, the F1..F12 keys will activate their standard function,\n" "and you must hold the FN key to activate their special function." msgstr "Когда выключено, кнопки F1..F12 работают по основным функциям,\n" "а выбор дополнительных производится с зажатой кнопкой «FN»." #: lib/logitech_receiver/settings_templates.py:162 msgid "Hand Detection" msgstr "Обнаружение рук" #: lib/logitech_receiver/settings_templates.py:163 msgid "Turn on illumination when the hands hover over the keyboard." msgstr "Включать подсветку, когда руки над клавиатурой." #: lib/logitech_receiver/settings_templates.py:170 msgid "Scroll Wheel Smooth Scrolling" msgstr "Плавная прокрутка колесом мыши" #: lib/logitech_receiver/settings_templates.py:171 #: lib/logitech_receiver/settings_templates.py:418 #: lib/logitech_receiver/settings_templates.py:447 msgid "High-sensitivity mode for vertical scroll with the wheel." msgstr "Режим высокой чувствительности для вертикальной прокрутки колесом." #: lib/logitech_receiver/settings_templates.py:178 msgid "Side Scrolling" msgstr "Горизонтальная прокрутка" #: lib/logitech_receiver/settings_templates.py:180 msgid "When disabled, pushing the wheel sideways sends custom button " "events\n" "instead of the standard side-scrolling events." msgstr "Когда отключено, нажатие на колесо сбоку\n" "вместо стандартных событий горизонтальной прокрутки\n" "будет отправлять пользовательские события нажатия." #: lib/logitech_receiver/settings_templates.py:190 msgid "Sensitivity (DPI - older mice)" msgstr "Чувствительность (тчк/дюйм — старые мыши)" #: lib/logitech_receiver/settings_templates.py:191 #: lib/logitech_receiver/settings_templates.py:979 #: lib/logitech_receiver/settings_templates.py:1005 msgid "Mouse movement sensitivity" msgstr "Чувствительность перемещения мыши" #: lib/logitech_receiver/settings_templates.py:264 msgid "Backlight Timed" msgstr "Подсветка по времени" #: lib/logitech_receiver/settings_templates.py:265 #: lib/logitech_receiver/settings_templates.py:405 msgid "Set illumination time for keyboard." msgstr "Настройка времени подсветки клавиатуры." #: lib/logitech_receiver/settings_templates.py:276 msgid "Backlight" msgstr "Подсветка" #: lib/logitech_receiver/settings_templates.py:277 msgid "Illumination level on keyboard. Changes made are only applied in " "Manual mode." msgstr "Уровень подсветки клавиатуры. Внесенные изменения применяются только " "в Ручном режиме." #: lib/logitech_receiver/settings_templates.py:309 msgid "Automatic" msgstr "Автоматический" #: lib/logitech_receiver/settings_templates.py:311 msgid "Manual" msgstr "Ручной" #: lib/logitech_receiver/settings_templates.py:313 msgid "Enabled" msgstr "Включенный" #: lib/logitech_receiver/settings_templates.py:319 msgid "Backlight Level" msgstr "Уровень подсветки" #: lib/logitech_receiver/settings_templates.py:320 msgid "Illumination level on keyboard when in Manual mode." msgstr "Уровень освещенности клавиатуры в ручном режиме." #: lib/logitech_receiver/settings_templates.py:377 msgid "Backlight Delay Hands Out" msgstr "Задержка подсветки" #: lib/logitech_receiver/settings_templates.py:378 msgid "Delay in seconds until backlight fades out with hands away from " "keyboard." msgstr "Задержка в секундах, пока подсветка не погаснет, если руки не будут " "лежать на клавиатуре." #: lib/logitech_receiver/settings_templates.py:386 msgid "Backlight Delay Hands In" msgstr "Задержка подсветки стрелок в" #: lib/logitech_receiver/settings_templates.py:387 msgid "Delay in seconds until backlight fades out with hands near keyboard." msgstr "Задержка в секундах, пока подсветка не погаснет, если руки находятся " "рядом с клавиатурой." #: lib/logitech_receiver/settings_templates.py:395 msgid "Backlight Delay Powered" msgstr "Включение подсветки с задержкой" #: lib/logitech_receiver/settings_templates.py:396 msgid "Delay in seconds until backlight fades out with external power." msgstr "Задержка в секундах до тех пор, пока подсветка не погаснет при " "включении внешнего питания." #: lib/logitech_receiver/settings_templates.py:404 msgid "Backlight (Seconds)" msgstr "Подсветка (секунды)" #: lib/logitech_receiver/settings_templates.py:416 msgid "Scroll Wheel High Resolution" msgstr "Высокое разрешение колеса прокрутки" #: lib/logitech_receiver/settings_templates.py:420 #: lib/logitech_receiver/settings_templates.py:449 msgid "Set to ignore if scrolling is abnormally fast or slow" msgstr "Настройка игнорирования слишком быстрой или медленной прокрутки" #: lib/logitech_receiver/settings_templates.py:427 #: lib/logitech_receiver/settings_templates.py:458 msgid "Scroll Wheel Diversion" msgstr "Поведение колеса прокрутки" #: lib/logitech_receiver/settings_templates.py:429 msgid "Make scroll wheel send LOWRES_WHEEL HID++ notifications (which " "trigger Solaar rules but are otherwise ignored)." msgstr "Заставить колесо прокрутки отправлять уведомления LOWRES_WHEEL HID++ " "(которые запускают правила Solaar, но в остальном игнорируются)." #: lib/logitech_receiver/settings_templates.py:436 msgid "Scroll Wheel Direction" msgstr "Направление прокрутки" #: lib/logitech_receiver/settings_templates.py:437 msgid "Invert direction for vertical scroll with wheel." msgstr "Обратная прокрутка колесом." #: lib/logitech_receiver/settings_templates.py:445 msgid "Scroll Wheel Resolution" msgstr "Разрешение прокрутки колесом" #: lib/logitech_receiver/settings_templates.py:460 msgid "Make scroll wheel send HIRES_WHEEL HID++ notifications (which " "trigger Solaar rules but are otherwise ignored)." msgstr "Заставить колесо прокрутки отправлять уведомления HIRES_WHEEL HID++ " "(которые запускают правила Solaar, но в остальном игнорируются)." #: lib/logitech_receiver/settings_templates.py:469 msgid "Sensitivity (Pointer Speed)" msgstr "Чувствительность (скорость указателя)" #: lib/logitech_receiver/settings_templates.py:470 msgid "Speed multiplier for mouse (256 is normal multiplier)." msgstr "Множитель скорости мыши (обычно - 256)." #: lib/logitech_receiver/settings_templates.py:480 msgid "Thumb Wheel Diversion" msgstr "Поведение бокового колеса" #: lib/logitech_receiver/settings_templates.py:482 msgid "Make thumb wheel send THUMB_WHEEL HID++ notifications (which trigger " "Solaar rules but are otherwise ignored)." msgstr "Заставить колесо большого пальца отправлять уведомления THUMB_WHEEL " "HID++ (которые запускают Правила Solaar, но в остальном " "игнорируются)." #: lib/logitech_receiver/settings_templates.py:491 msgid "Thumb Wheel Direction" msgstr "Направление вращения бокового колеса" #: lib/logitech_receiver/settings_templates.py:492 msgid "Invert thumb wheel scroll direction." msgstr "Изменяет направление вращения бокового колеса." #: lib/logitech_receiver/settings_templates.py:512 msgid "Onboard Profiles" msgstr "Встроенные профили" #: lib/logitech_receiver/settings_templates.py:513 msgid "Enable an onboard profile, which controls report rate, sensitivity, " "and button actions" msgstr "Включите встроенный профиль, который контролирует частоту отчетов, " "чувствительность, и действия кнопок" #: lib/logitech_receiver/settings_templates.py:557 #: lib/logitech_receiver/settings_templates.py:590 msgid "Report Rate" msgstr "Частота отчетов" #: lib/logitech_receiver/settings_templates.py:559 #: lib/logitech_receiver/settings_templates.py:592 msgid "Frequency of device movement reports" msgstr "Частота сообщений о перемещении устройства" #: lib/logitech_receiver/settings_templates.py:559 #: lib/logitech_receiver/settings_templates.py:592 #: lib/logitech_receiver/settings_templates.py:1005 #: lib/logitech_receiver/settings_templates.py:1379 #: lib/logitech_receiver/settings_templates.py:1410 msgid "May need Onboard Profiles set to Disable to be effective." msgstr "Для работы может потребоваться отключить параметр «Встроенные " "профили»." #: lib/logitech_receiver/settings_templates.py:620 msgid "Divert crown events" msgstr "Поведение верньера" #: lib/logitech_receiver/settings_templates.py:621 msgid "Make crown send CROWN HID++ notifications (which trigger Solaar " "rules but are otherwise ignored)." msgstr "Включает отправку верньером оповещения CROWN HID++ (применимо только " "к правилам Solaar, остальными игнорируется)." #: lib/logitech_receiver/settings_templates.py:629 msgid "Crown smooth scroll" msgstr "Плавная прокрутка верньера" #: lib/logitech_receiver/settings_templates.py:630 msgid "Set crown smooth scroll" msgstr "Настройка плавной прокрутки верньера" #: lib/logitech_receiver/settings_templates.py:638 msgid "Divert G and M Keys" msgstr "Перенаправление клавиш G и M" #: lib/logitech_receiver/settings_templates.py:639 msgid "Make G and M keys send HID++ notifications (which trigger Solaar " "rules but are otherwise ignored)." msgstr "Заставить клавиши G и M отправлять уведомления HID++ (которые " "запускают Solaar правила, но в остальном игнорируются)." #: lib/logitech_receiver/settings_templates.py:653 msgid "Scroll Wheel Ratcheted" msgstr "Колесо прокрутки с храповым механизмом" #: lib/logitech_receiver/settings_templates.py:654 msgid "Switch the mouse wheel between speed-controlled ratcheting and " "always freespin." msgstr "Переключение колеса мыши между вращением с храповым механизмом и " "свободным вращением." #: lib/logitech_receiver/settings_templates.py:656 msgid "Freespinning" msgstr "Свободное вращение" #: lib/logitech_receiver/settings_templates.py:656 msgid "Ratcheted" msgstr "Вращение с храповым механизмом" #: lib/logitech_receiver/settings_templates.py:663 msgid "Scroll Wheel Ratchet Speed" msgstr "Скорость вращения колеса прокрутки" #: lib/logitech_receiver/settings_templates.py:665 msgid "Use the mouse wheel speed to switch between ratcheted and " "freespinning.\n" "The mouse wheel is always ratcheted at 50." msgstr "Используйте скорость вращения колесика мыши для переключения между " "вращением с храповым механизмом и свободным вращением.\n" "Колесо мыши всегда вращается с храповым механизмом на 50 градусов." #: lib/logitech_receiver/settings_templates.py:714 msgid "Key/Button Actions" msgstr "Действия с клавишами/кнопками" #: lib/logitech_receiver/settings_templates.py:716 msgid "Change the action for the key or button." msgstr "Изменить действие кнопки или клавиши." #: lib/logitech_receiver/settings_templates.py:718 msgid "Overridden by diversion." msgstr "Отменено перенаправлением." #: lib/logitech_receiver/settings_templates.py:720 msgid "Changing important actions (such as for the left mouse button) can " "result in an unusable system." msgstr "(изменение важных действий — например, левой кнопки мыши - может " "вывести систему из строя)." #: lib/logitech_receiver/settings_templates.py:886 msgid "Key/Button Diversion" msgstr "Поведение клавиш/кнопок" #: lib/logitech_receiver/settings_templates.py:887 msgid "Make the key or button send HID++ notifications (Diverted) or " "initiate Mouse Gestures or Sliding DPI" msgstr "Заставьте клавишу или кнопку отправлять уведомления HID++ " "(перенаправленные) или инициировать жесты мыши или скользящее " "значение DPI" #: lib/logitech_receiver/settings_templates.py:890 #: lib/logitech_receiver/settings_templates.py:891 #: lib/logitech_receiver/settings_templates.py:892 msgid "Diverted" msgstr "Перенаправлено" #: lib/logitech_receiver/settings_templates.py:890 #: lib/logitech_receiver/settings_templates.py:891 msgid "Mouse Gestures" msgstr "Жесты мышью" #: lib/logitech_receiver/settings_templates.py:890 #: lib/logitech_receiver/settings_templates.py:891 #: lib/logitech_receiver/settings_templates.py:892 msgid "Regular" msgstr "Обычное" #: lib/logitech_receiver/settings_templates.py:890 msgid "Sliding DPI" msgstr "Скользящий тчк/дюйм" #: lib/logitech_receiver/settings_templates.py:978 #: lib/logitech_receiver/settings_templates.py:1004 msgid "Sensitivity (DPI)" msgstr "Чувствительность (тчк/дюйм)" #: lib/logitech_receiver/settings_templates.py:1081 msgid "Sensitivity Switching" msgstr "Переключение чувствительности" #: lib/logitech_receiver/settings_templates.py:1083 msgid "Switch the current sensitivity and the remembered sensitivity when " "the key or button is pressed.\n" "If there is no remembered sensitivity, just remember the current " "sensitivity" msgstr "Переключение текущей чувствительности и сохранённой чувствительности " "при нажатии клавиши или кнопки.\n" "Если сохранённой чувствительности нет, сохраняется текущая " "чувствительность" #: lib/logitech_receiver/settings_templates.py:1087 msgid "Off" msgstr "Откл" #: lib/logitech_receiver/settings_templates.py:1118 msgid "Disable keys" msgstr "Отключить клавиши" #: lib/logitech_receiver/settings_templates.py:1119 msgid "Disable specific keyboard keys." msgstr "Отключение определённых клавиш клавиатуры." #: lib/logitech_receiver/settings_templates.py:1122 #, python-format msgid "Disables the %s key." msgstr "Отключение клавиши %s." #: lib/logitech_receiver/settings_templates.py:1135 #: lib/logitech_receiver/settings_templates.py:1192 msgid "Set OS" msgstr "Операционная система" #: lib/logitech_receiver/settings_templates.py:1136 #: lib/logitech_receiver/settings_templates.py:1193 msgid "Change keys to match OS." msgstr "Изменяет назначение клавиш соответственно ОС." #: lib/logitech_receiver/settings_templates.py:1205 msgid "Change Host" msgstr "Подключено к" #: lib/logitech_receiver/settings_templates.py:1206 msgid "Switch connection to a different host" msgstr "Переключает соединение на выбранный хост." #: lib/logitech_receiver/settings_templates.py:1230 msgid "Performs a left click." msgstr "Осуществляет щелчок левой кнопкой." #: lib/logitech_receiver/settings_templates.py:1230 msgid "Single tap" msgstr "Одиночное касание" #: lib/logitech_receiver/settings_templates.py:1231 msgid "Performs a right click." msgstr "Осуществляет щелчок правой кнопкой." #: lib/logitech_receiver/settings_templates.py:1231 msgid "Single tap with two fingers" msgstr "Одиночное касание двумя пальцами" #: lib/logitech_receiver/settings_templates.py:1232 msgid "Single tap with three fingers" msgstr "Одиночное касание тремя пальцами" #: lib/logitech_receiver/settings_templates.py:1236 msgid "Double tap" msgstr "Двойное касание" #: lib/logitech_receiver/settings_templates.py:1236 msgid "Performs a double click." msgstr "Осуществление двойного щелчка." #: lib/logitech_receiver/settings_templates.py:1237 msgid "Double tap with two fingers" msgstr "Двойное касание двумя пальцами" #: lib/logitech_receiver/settings_templates.py:1238 msgid "Double tap with three fingers" msgstr "Двойное касание тремя пальцами" #: lib/logitech_receiver/settings_templates.py:1241 msgid "Drags items by dragging the finger after double tapping." msgstr "Перетаскивать объекты перемещением пальца после двойного касания." #: lib/logitech_receiver/settings_templates.py:1241 msgid "Tap and drag" msgstr "Касание и перетаскивание" #: lib/logitech_receiver/settings_templates.py:1243 msgid "Tap and drag with two fingers" msgstr "Касание и перетаскивание двумя пальцами" #: lib/logitech_receiver/settings_templates.py:1244 msgid "Drags items by dragging the fingers after double tapping." msgstr "Перетаскивать объекты перемещением пальцев после двойного касания." #: lib/logitech_receiver/settings_templates.py:1246 msgid "Tap and drag with three fingers" msgstr "Касание и перетаскивание тремя пальцами" #: lib/logitech_receiver/settings_templates.py:1249 msgid "Suppress tap and edge gestures" msgstr "Подавление касаний и жестов по краям" #: lib/logitech_receiver/settings_templates.py:1250 msgid "Disables tap and edge gestures (equivalent to pressing Fn+LeftClick)." msgstr "Отключает касания и жесты по краям (то же что FN+ЩелчокЛевойКнопкой)." #: lib/logitech_receiver/settings_templates.py:1252 msgid "Scroll with one finger" msgstr "Вертикальная прокрутка одним пальцем" #: lib/logitech_receiver/settings_templates.py:1252 #: lib/logitech_receiver/settings_templates.py:1253 #: lib/logitech_receiver/settings_templates.py:1256 msgid "Scrolls." msgstr "Прокручивать вертикально." #: lib/logitech_receiver/settings_templates.py:1253 #: lib/logitech_receiver/settings_templates.py:1256 msgid "Scroll with two fingers" msgstr "Вертикальная прокрутка двумя пальцами" #: lib/logitech_receiver/settings_templates.py:1254 msgid "Scroll horizontally with two fingers" msgstr "Горизонтальная прокрутка двумя пальцами" #: lib/logitech_receiver/settings_templates.py:1254 msgid "Scrolls horizontally." msgstr "Прокручивать горизонтально." #: lib/logitech_receiver/settings_templates.py:1255 msgid "Scroll vertically with two fingers" msgstr "Вертикальная прокрутка двумя пальцами" #: lib/logitech_receiver/settings_templates.py:1255 msgid "Scrolls vertically." msgstr "Прокручивать вертикально." #: lib/logitech_receiver/settings_templates.py:1257 msgid "Inverts the scrolling direction." msgstr "Инвертирует направления прокрутки." #: lib/logitech_receiver/settings_templates.py:1257 msgid "Natural scrolling" msgstr "Естественная прокрутка" #: lib/logitech_receiver/settings_templates.py:1258 msgid "Enables the thumbwheel." msgstr "Включение бокового колеса прокрутки." #: lib/logitech_receiver/settings_templates.py:1258 msgid "Thumbwheel" msgstr "Боковое колесо прокрутки" #: lib/logitech_receiver/settings_templates.py:1269 #: lib/logitech_receiver/settings_templates.py:1273 msgid "Swipe from the top edge" msgstr "Листание от верхнего края" #: lib/logitech_receiver/settings_templates.py:1270 msgid "Swipe from the left edge" msgstr "Листание от левого края" #: lib/logitech_receiver/settings_templates.py:1271 msgid "Swipe from the right edge" msgstr "Листание от правого края" #: lib/logitech_receiver/settings_templates.py:1272 msgid "Swipe from the bottom edge" msgstr "Листание от нижнего края" #: lib/logitech_receiver/settings_templates.py:1274 msgid "Swipe two fingers from the left edge" msgstr "Листание двумя пальцами от левого края" #: lib/logitech_receiver/settings_templates.py:1275 msgid "Swipe two fingers from the right edge" msgstr "Листание двумя пальцами от правого края" #: lib/logitech_receiver/settings_templates.py:1276 msgid "Swipe two fingers from the bottom edge" msgstr "Листание двумя пальцами от нижнего края" #: lib/logitech_receiver/settings_templates.py:1277 msgid "Swipe two fingers from the top edge" msgstr "Листание двумя пальцами от верхнего края" #: lib/logitech_receiver/settings_templates.py:1278 #: lib/logitech_receiver/settings_templates.py:1282 msgid "Pinch to zoom out; spread to zoom in." msgstr "Щипок - уменьшить, раздвинуть - увеличить." #: lib/logitech_receiver/settings_templates.py:1278 msgid "Zoom with two fingers." msgstr "Масштабирование двумя пальцами." #: lib/logitech_receiver/settings_templates.py:1279 msgid "Pinch to zoom out." msgstr "Щипок - уменьшить." #: lib/logitech_receiver/settings_templates.py:1280 msgid "Spread to zoom in." msgstr "Раздвинуть - увеличить." #: lib/logitech_receiver/settings_templates.py:1281 msgid "Zoom with three fingers." msgstr "Масштабирование тремя пальцами." #: lib/logitech_receiver/settings_templates.py:1282 msgid "Zoom with two fingers" msgstr "Масштабирование двумя пальцами" #: lib/logitech_receiver/settings_templates.py:1300 msgid "Pixel zone" msgstr "Зона пикселей" #: lib/logitech_receiver/settings_templates.py:1301 msgid "Ratio zone" msgstr "Зона масштаба" #: lib/logitech_receiver/settings_templates.py:1302 msgid "Scale factor" msgstr "Коэффициент масштабирования" #: lib/logitech_receiver/settings_templates.py:1302 msgid "Sets the cursor speed." msgstr "Установка скорости курсора." #: lib/logitech_receiver/settings_templates.py:1306 msgid "Left" msgstr "Лево" #: lib/logitech_receiver/settings_templates.py:1306 msgid "Left-most coordinate." msgstr "Самая левая позиция." #: lib/logitech_receiver/settings_templates.py:1307 msgid "Bottom" msgstr "Низ" #: lib/logitech_receiver/settings_templates.py:1307 msgid "Bottom coordinate." msgstr "Нижняя позиция." #: lib/logitech_receiver/settings_templates.py:1308 msgid "Width" msgstr "Ширина" #: lib/logitech_receiver/settings_templates.py:1308 msgid "Width." msgstr "Ширина." #: lib/logitech_receiver/settings_templates.py:1309 msgid "Height" msgstr "Высота" #: lib/logitech_receiver/settings_templates.py:1309 msgid "Height." msgstr "Высота." #: lib/logitech_receiver/settings_templates.py:1310 msgid "Cursor speed." msgstr "Скорость курсора." #: lib/logitech_receiver/settings_templates.py:1310 msgid "Scale" msgstr "Масштаб" #: lib/logitech_receiver/settings_templates.py:1316 msgid "Gestures" msgstr "Жесты" #: lib/logitech_receiver/settings_templates.py:1317 msgid "Tweak the mouse/touchpad behaviour." msgstr "Регулировка поведения мыши/сенсорной панели." #: lib/logitech_receiver/settings_templates.py:1333 msgid "Gestures Diversion" msgstr "Поведение жестов" #: lib/logitech_receiver/settings_templates.py:1334 msgid "Divert mouse/touchpad gestures." msgstr "Регулировка поведения жестов мыши/сенсорной панели." #: lib/logitech_receiver/settings_templates.py:1350 msgid "Gesture params" msgstr "Параметры жестов" #: lib/logitech_receiver/settings_templates.py:1351 msgid "Change numerical parameters of a mouse/touchpad." msgstr "Изменение числовых параметров мыши/сенсорной панели." #: lib/logitech_receiver/settings_templates.py:1375 msgid "M-Key LEDs" msgstr "Светодиоды клавиш M" #: lib/logitech_receiver/settings_templates.py:1377 msgid "Control the M-Key LEDs." msgstr "Управление светодиодами клавиш M." #: lib/logitech_receiver/settings_templates.py:1381 #: lib/logitech_receiver/settings_templates.py:1412 msgid "May need G Keys diverted to be effective." msgstr "Возможно, для эффективности потребуется перенаправить G-клавиши." #: lib/logitech_receiver/settings_templates.py:1387 #, python-format msgid "Lights up the %s key." msgstr "Подсветка клавиши %s." #: lib/logitech_receiver/settings_templates.py:1406 msgid "MR-Key LED" msgstr "Светодиод клавиши MR" #: lib/logitech_receiver/settings_templates.py:1408 msgid "Control the MR-Key LED." msgstr "Управление светодиодом клавиши MR." #: lib/logitech_receiver/settings_templates.py:1429 msgid "Persistent Key/Button Mapping" msgstr "Постоянное сопоставление клавиши или кнопки" #: lib/logitech_receiver/settings_templates.py:1431 msgid "Permanently change the mapping for the key or button." msgstr "Навсегда изменить сопоставление для клавиши или кнопки." #: lib/logitech_receiver/settings_templates.py:1433 msgid "Changing important keys or buttons (such as for the left mouse " "button) can result in an unusable system." msgstr "Изменение важных клавиш или кнопок (например, левой мыши кнопку) " "может привести к выводу системы из строя." #: lib/logitech_receiver/settings_templates.py:1490 msgid "Sidetone" msgstr "Самопрослушивание" #: lib/logitech_receiver/settings_templates.py:1491 msgid "Set sidetone level." msgstr "Установите уровень самопрослушивания." #: lib/logitech_receiver/settings_templates.py:1500 msgid "Equalizer" msgstr "Эквалайзер" #: lib/logitech_receiver/settings_templates.py:1501 msgid "Set equalizer levels." msgstr "Установите уровни эквалайзера." #: lib/logitech_receiver/settings_templates.py:1523 msgid "Hz" msgstr "Гц" #: lib/logitech_receiver/settings_templates.py:1529 msgid "Power Management" msgstr "Управление питанием" #: lib/logitech_receiver/settings_templates.py:1530 msgid "Power off in minutes (0 for never)." msgstr "Выключите питание через несколько минут (0 означает \"никогда\")." #: lib/logitech_receiver/settings_templates.py:1541 msgid "Brightness Control" msgstr "Регулировка яркости" #: lib/logitech_receiver/settings_templates.py:1542 msgid "Control overall brightness" msgstr "Контролируйте общую яркость" #: lib/logitech_receiver/settings_templates.py:1585 #: lib/logitech_receiver/settings_templates.py:1637 msgid "LED Control" msgstr "Светодиодное управление" #: lib/logitech_receiver/settings_templates.py:1586 #: lib/logitech_receiver/settings_templates.py:1638 msgid "Switch control of LED zones between device and Solaar" msgstr "Переключайте управление светодиодными зонами между устройством и " "Solaar" #: lib/logitech_receiver/settings_templates.py:1601 #: lib/logitech_receiver/settings_templates.py:1648 msgid "LED Zone Effects" msgstr "Эффекты светодиодной зоны" #: lib/logitech_receiver/settings_templates.py:1602 #: lib/logitech_receiver/settings_templates.py:1649 msgid "LED Control needs to be set to Solaar to be effective." msgstr "Для эффективного управления светодиодом необходимо установить режим " "Solaar." #: lib/logitech_receiver/settings_templates.py:1602 #: lib/logitech_receiver/settings_templates.py:1649 msgid "Set effect for LED Zone" msgstr "Установите эффект для светодиодной зоны" #: lib/logitech_receiver/settings_templates.py:1605 msgid "Speed" msgstr "Скорость" #: lib/logitech_receiver/settings_templates.py:1606 msgid "Period" msgstr "Период" #: lib/logitech_receiver/settings_templates.py:1607 msgid "Intensity" msgstr "Интенсивность" #: lib/logitech_receiver/settings_templates.py:1608 msgid "Ramp" msgstr "Рампа" #: lib/logitech_receiver/settings_templates.py:1622 msgid "LEDs" msgstr "Светодиоды" #: lib/logitech_receiver/settings_templates.py:1659 msgid "Per-key Lighting" msgstr "Индивидуальное освещение" #: lib/logitech_receiver/settings_templates.py:1660 msgid "Control per-key lighting." msgstr "Управление подсветкой каждой клавиши." #: lib/solaar/ui/__init__.py:103 msgid "Another Solaar process is already running so just expose its window" msgstr "Другой Solaar процесс уже запущен, так что просто откройте его окно" #: lib/solaar/ui/about.py:38 msgid "Manages Logitech receivers,\n" "keyboards, mice, and tablets." msgstr "Управление приёмниками, клавиатурами,\n" "мышами и другими устройствами Logitech." #: lib/solaar/ui/about.py:47 msgid "Additional Programming" msgstr "Дополнительные параметры" #: lib/solaar/ui/about.py:48 msgid "GUI design" msgstr "Дизайн интерфейса" #: lib/solaar/ui/about.py:50 msgid "Testing" msgstr "Тестирование" #: lib/solaar/ui/about.py:58 msgid "Logitech documentation" msgstr "Документация Logitech" #: lib/solaar/ui/action.py:83 lib/solaar/ui/action.py:87 #: lib/solaar/ui/window.py:189 msgid "Unpair" msgstr "Отмена сопряжения" #: lib/solaar/ui/action.py:86 lib/solaar/ui/diversion_rules.py:102 msgid "Cancel" msgstr "Отмена" #: lib/solaar/ui/common.py:35 msgid "Permissions error" msgstr "Ошибка доступа" #: lib/solaar/ui/common.py:37 #, python-format msgid "Found a Logitech receiver or device (%s), but did not have " "permission to open it." msgstr "Найден приёмник Logitech (%s), но нет прав доступ открыть его." #: lib/solaar/ui/common.py:39 msgid "If you've just installed Solaar, try disconnecting the receiver or " "device and then reconnecting it." msgstr "Если вы только что установили Solaar, попробуйте отключить и снова " "подключить приёмник или устройство." #: lib/solaar/ui/common.py:42 msgid "Cannot connect to device error" msgstr "Невозможно подключиться к устройству" #: lib/solaar/ui/common.py:44 #, python-format msgid "Found a Logitech receiver or device at %s, but encountered an error " "connecting to it." msgstr "Найден приёмник или устройство Logitech на %s, но не удалось к нему " "подключиться." #: lib/solaar/ui/common.py:46 msgid "Try disconnecting the device and then reconnecting it or turning it " "off and then on." msgstr "Попробуйте отключить и снова подключить устройство или выключить и " "затем снова включить его." #: lib/solaar/ui/common.py:49 msgid "Unpairing failed" msgstr "Отмена сопряжения не удалась" #: lib/solaar/ui/common.py:51 #, python-brace-format msgid "Failed to unpair %{device} from %{receiver}." msgstr "Отмена сопряжения %{device} с %{receiver} не удалась." #: lib/solaar/ui/common.py:53 msgid "The receiver returned an error, with no further details." msgstr "Приёмник вернул ошибку без дополнительных сведений." #: lib/solaar/ui/config_panel.py:228 msgid "Complete - ENTER to change" msgstr "Завершено - «ВВОД» для изменения" #: lib/solaar/ui/config_panel.py:228 msgid "Incomplete" msgstr "Незавершено" #: lib/solaar/ui/config_panel.py:470 lib/solaar/ui/config_panel.py:522 #, python-format msgid "%d value" msgid_plural "%d values" msgstr[0] "%d значение" msgstr[1] "%d значения" msgstr[2] "%d значений" #: lib/solaar/ui/config_panel.py:607 msgid "Changes allowed" msgstr "Изменения разрешены" #: lib/solaar/ui/config_panel.py:608 msgid "No changes allowed" msgstr "Изменения запрещены" #: lib/solaar/ui/config_panel.py:609 msgid "Ignore this setting" msgstr "Игнорировать данный параметр" #: lib/solaar/ui/config_panel.py:653 msgid "Working" msgstr "Работает" #: lib/solaar/ui/config_panel.py:656 msgid "Read/write operation failed." msgstr "Операция чтения/записи не удалась." #: lib/solaar/ui/diversion_rules.py:64 msgid "Built-in rules" msgstr "Встроенные правила" #: lib/solaar/ui/diversion_rules.py:64 msgid "User-defined rules" msgstr "Пользовательские правила" #: lib/solaar/ui/diversion_rules.py:66 lib/solaar/ui/diversion_rules.py:1029 msgid "Rule" msgstr "Правило" #: lib/solaar/ui/diversion_rules.py:67 lib/solaar/ui/diversion_rules.py:544 #: lib/solaar/ui/diversion_rules.py:671 msgid "Sub-rule" msgstr "Подправило" #: lib/solaar/ui/diversion_rules.py:69 msgid "[empty]" msgstr "[пусто]" #: lib/solaar/ui/diversion_rules.py:93 msgid "Make changes permanent?" msgstr "Сохранить изменения навсегда?" #: lib/solaar/ui/diversion_rules.py:98 msgid "Yes" msgstr "Да" #: lib/solaar/ui/diversion_rules.py:100 msgid "No" msgstr "Нет" #: lib/solaar/ui/diversion_rules.py:105 msgid "If you choose No, changes will be lost when Solaar is closed." msgstr "Если выбрать «Нет», при закрытии программы изменения пропадут." #: lib/solaar/ui/diversion_rules.py:166 msgid "Solaar Rule Editor" msgstr "Редактор правил" #: lib/solaar/ui/diversion_rules.py:259 msgid "Save changes" msgstr "Сохранить изменения" #: lib/solaar/ui/diversion_rules.py:264 msgid "Discard changes" msgstr "Отменить изменения" #: lib/solaar/ui/diversion_rules.py:403 msgid "Insert here" msgstr "Вставить сюда" #: lib/solaar/ui/diversion_rules.py:405 msgid "Insert above" msgstr "Вставить выше" #: lib/solaar/ui/diversion_rules.py:407 msgid "Insert below" msgstr "Вставить ниже" #: lib/solaar/ui/diversion_rules.py:413 msgid "Insert new rule here" msgstr "Вставить новое правило сюда" #: lib/solaar/ui/diversion_rules.py:415 msgid "Insert new rule above" msgstr "Вставить новое правило выше" #: lib/solaar/ui/diversion_rules.py:417 msgid "Insert new rule below" msgstr "Вставить новое правило ниже" #: lib/solaar/ui/diversion_rules.py:462 msgid "Paste here" msgstr "Скопировать сюда" #: lib/solaar/ui/diversion_rules.py:464 msgid "Paste above" msgstr "Скопировать выше" #: lib/solaar/ui/diversion_rules.py:466 msgid "Paste below" msgstr "Скопировать ниже" #: lib/solaar/ui/diversion_rules.py:472 msgid "Paste rule here" msgstr "Скопировать правило сюда" #: lib/solaar/ui/diversion_rules.py:474 msgid "Paste rule above" msgstr "Скопировать правило выше" #: lib/solaar/ui/diversion_rules.py:476 msgid "Paste rule below" msgstr "Скопировать правило ниже" #: lib/solaar/ui/diversion_rules.py:480 msgid "Paste rule" msgstr "Скопировать правило" #: lib/solaar/ui/diversion_rules.py:509 msgid "Flatten" msgstr "Снять обёртку" #: lib/solaar/ui/diversion_rules.py:542 msgid "Insert" msgstr "Вставка" #: lib/solaar/ui/diversion_rules.py:545 lib/solaar/ui/diversion_rules.py:673 #: lib/solaar/ui/diversion_rules.py:1061 msgid "Or" msgstr "Или" #: lib/solaar/ui/diversion_rules.py:546 lib/solaar/ui/diversion_rules.py:672 #: lib/solaar/ui/diversion_rules.py:1047 msgid "And" msgstr "И" #: lib/solaar/ui/diversion_rules.py:548 msgid "Condition" msgstr "Компонент" #: lib/solaar/ui/diversion_rules.py:550 lib/solaar/ui/rule_conditions.py:146 msgid "Feature" msgstr "Feature" #: lib/solaar/ui/diversion_rules.py:551 lib/solaar/ui/rule_conditions.py:181 msgid "Report" msgstr "Отчёт" #: lib/solaar/ui/diversion_rules.py:552 lib/solaar/ui/rule_conditions.py:60 msgid "Process" msgstr "Процесс" #: lib/solaar/ui/diversion_rules.py:553 msgid "Mouse process" msgstr "Действие мыши" #: lib/solaar/ui/diversion_rules.py:554 lib/solaar/ui/rule_conditions.py:218 msgid "Modifiers" msgstr "Модификаторы" #: lib/solaar/ui/diversion_rules.py:555 lib/solaar/ui/rule_conditions.py:270 msgid "Key" msgstr "Клавиша" #: lib/solaar/ui/diversion_rules.py:556 lib/solaar/ui/rule_conditions.py:311 msgid "KeyIsDown" msgstr "Клавиша нажата" #: lib/solaar/ui/diversion_rules.py:557 lib/solaar/ui/diversion_rules.py:1349 msgid "Active" msgstr "Активный" #: lib/solaar/ui/diversion_rules.py:558 lib/solaar/ui/diversion_rules.py:1306 #: lib/solaar/ui/diversion_rules.py:1358 lib/solaar/ui/diversion_rules.py:1408 msgid "Device" msgstr "Устройство" #: lib/solaar/ui/diversion_rules.py:559 lib/solaar/ui/diversion_rules.py:1384 msgid "Host" msgstr "Хост" #: lib/solaar/ui/diversion_rules.py:560 lib/solaar/ui/diversion_rules.py:1426 msgid "Setting" msgstr "Установка" #: lib/solaar/ui/diversion_rules.py:561 lib/solaar/ui/rule_conditions.py:326 #: lib/solaar/ui/rule_conditions.py:375 msgid "Test" msgstr "Условие" #: lib/solaar/ui/diversion_rules.py:562 lib/solaar/ui/rule_conditions.py:500 msgid "Test bytes" msgstr "Тестовые байты" #: lib/solaar/ui/diversion_rules.py:563 lib/solaar/ui/rule_conditions.py:601 msgid "Mouse Gesture" msgstr "Жест мышью" #: lib/solaar/ui/diversion_rules.py:567 msgid "Action" msgstr "Действие" #: lib/solaar/ui/diversion_rules.py:569 lib/solaar/ui/rule_actions.py:132 msgid "Key press" msgstr "Нажатие клавиши" #: lib/solaar/ui/diversion_rules.py:570 lib/solaar/ui/rule_actions.py:183 msgid "Mouse scroll" msgstr "Прокрутка мышью" #: lib/solaar/ui/diversion_rules.py:571 lib/solaar/ui/rule_actions.py:244 msgid "Mouse click" msgstr "Щелчок мышью" #: lib/solaar/ui/diversion_rules.py:572 msgid "Set" msgstr "Установить" #: lib/solaar/ui/diversion_rules.py:573 lib/solaar/ui/rule_actions.py:314 msgid "Execute" msgstr "Исполнить" #: lib/solaar/ui/diversion_rules.py:574 lib/solaar/ui/diversion_rules.py:1093 msgid "Later" msgstr "Задержка" #: lib/solaar/ui/diversion_rules.py:603 msgid "Insert new rule" msgstr "Добавить правило" #: lib/solaar/ui/diversion_rules.py:623 lib/solaar/ui/rule_actions.py:74 #: lib/solaar/ui/rule_actions.py:273 lib/solaar/ui/rule_conditions.py:548 msgid "Delete" msgstr "Удалить" #: lib/solaar/ui/diversion_rules.py:645 msgid "Negate" msgstr "Инвертировать" #: lib/solaar/ui/diversion_rules.py:669 msgid "Wrap with" msgstr "Завернуть в" #: lib/solaar/ui/diversion_rules.py:691 msgid "Cut" msgstr "Вырезать" #: lib/solaar/ui/diversion_rules.py:706 msgid "Paste" msgstr "Вставить" #: lib/solaar/ui/diversion_rules.py:712 msgid "Copy" msgstr "Копировать" #: lib/solaar/ui/diversion_rules.py:1010 msgid "This editor does not support the selected rule component yet." msgstr "Редактор пока не поддерживает выбранную компоненту правила." #: lib/solaar/ui/diversion_rules.py:1072 msgid "Number of seconds to delay. Delay between 0 and 1 is done with " "higher precision." msgstr "Количество секунд задержки. Задержка между 0 и 1 выполняется с " "помощью более высокая точность." #: lib/solaar/ui/diversion_rules.py:1111 msgid "Not" msgstr "Не" #: lib/solaar/ui/diversion_rules.py:1135 msgid "Toggle" msgstr "Переключение" #: lib/solaar/ui/diversion_rules.py:1136 msgid "True" msgstr "Истина" #: lib/solaar/ui/diversion_rules.py:1137 msgid "False" msgstr "Ложь" #: lib/solaar/ui/diversion_rules.py:1150 msgid "Unsupported setting" msgstr "Неподдерживаемый параметр" #: lib/solaar/ui/diversion_rules.py:1312 lib/solaar/ui/diversion_rules.py:1332 #: lib/solaar/ui/diversion_rules.py:1414 lib/solaar/ui/diversion_rules.py:1659 #: lib/solaar/ui/diversion_rules.py:1677 msgid "Originating device" msgstr "Исходное устройство" #: lib/solaar/ui/diversion_rules.py:1345 msgid "Device is active and its settings can be changed." msgstr "Устройство активно, и его настройки могут быть изменены." #: lib/solaar/ui/diversion_rules.py:1354 msgid "Device that originated the current notification." msgstr "Устройство, отправившее текущее уведомление." #: lib/solaar/ui/diversion_rules.py:1367 msgid "Name of host computer." msgstr "Имя главного компьютера." #: lib/solaar/ui/diversion_rules.py:1435 msgid "Value" msgstr "Значение" #: lib/solaar/ui/diversion_rules.py:1444 msgid "Item" msgstr "Элемент" #: lib/solaar/ui/diversion_rules.py:1718 msgid "Change setting on device" msgstr "Изменить настройку на устройстве" #: lib/solaar/ui/diversion_rules.py:1734 msgid "Setting on device" msgstr "Настройка на устройстве" #: lib/solaar/ui/notify.py:118 msgid "unspecified reason" msgstr "неустановленная причина" #: lib/solaar/ui/pair_window.py:37 lib/solaar/ui/pair_window.py:155 #, python-format msgid "%(receiver_name)s: pair new device" msgstr "%(receiver_name)s: новое сопряжённое устройство" #: lib/solaar/ui/pair_window.py:39 msgid "Bolt receivers are only compatible with Bolt devices." msgstr "Приемники Bolt совместимы только с устройствами Bolt." #: lib/solaar/ui/pair_window.py:41 msgid "Press a pairing button or key until the pairing light flashes " "quickly." msgstr "Нажимайте кнопку или клавишу сопряжения, пока индикатор сопряжения " "не начнёт быстро мигать." #: lib/solaar/ui/pair_window.py:44 msgid "Unifying receivers are only compatible with Unifying devices." msgstr "Приемники Unifying совместимы только с устройствами Unifying." #: lib/solaar/ui/pair_window.py:46 msgid "Other receivers are only compatible with a few devices." msgstr "Другие ресиверы совместимы только с некоторыми устройствами." #: lib/solaar/ui/pair_window.py:48 msgid "Turn on the device you want to pair." msgstr "Включите устройство, с которым нужно установить сопряжение." #: lib/solaar/ui/pair_window.py:49 msgid "The device must not be paired with a nearby powered-on receiver." msgstr "Устройство не должно быть сопряжено с находящимся поблизости " "включенным приемником." #: lib/solaar/ui/pair_window.py:51 msgid "If the device is already turned on, turn it off and on again." msgstr "Если устройство включено, выключите его и включите снова." #: lib/solaar/ui/pair_window.py:55 #, python-format msgid "\n" "\n" "This receiver has %d pairing remaining." msgid_plural "\n" "\n" "This receiver has %d pairings remaining." msgstr[0] "\n" "Данному приёмнику доступно %d сопряжение." msgstr[1] "\n" "Данному приёмнику доступно %d сопряжения." msgstr[2] "\n" "Данному приёмнику доступно %d сопряжений." #: lib/solaar/ui/pair_window.py:61 msgid "\n" "Cancelling at this point will not use up a pairing." msgstr "\n" "Прерывание в данный момент отменит сопряжение." #: lib/solaar/ui/pair_window.py:156 #, python-format msgid "Enter passcode on %(name)s." msgstr "Введите код доступа на %(name)s." #: lib/solaar/ui/pair_window.py:159 #, python-format msgid "Type %(passcode)s and then press the enter key." msgstr "Введите %(passcode)s и затем нажмите клавишу «Ввод»." #: lib/solaar/ui/pair_window.py:162 msgid "left" msgstr "слева" #: lib/solaar/ui/pair_window.py:162 msgid "right" msgstr "справа" #: lib/solaar/ui/pair_window.py:164 #, python-format msgid "Press %(code)s\n" "and then press left and right buttons simultaneously." msgstr "Нажмите %(code)s\n" "и затем нажмите одновременно левую и правую кнопки." #: lib/solaar/ui/pair_window.py:195 msgid "The wireless link is not encrypted" msgstr "Беспроводное соединение не зашифровано" #: lib/solaar/ui/pair_window.py:200 msgid "Found a new device:" msgstr "Найдено новое устройство:" #: lib/solaar/ui/pair_window.py:221 msgid "Pairing failed" msgstr "Сопряжение не удалось" #: lib/solaar/ui/pair_window.py:223 msgid "Make sure your device is within range, and has a decent battery " "charge." msgstr "Удостоверьтесь, что ваше устройство в радиусе приёма и батарея " "заряжена." #: lib/solaar/ui/pair_window.py:225 msgid "A new device was detected, but it is not compatible with this " "receiver." msgstr "Обнаружено новое устройство, несовместимое с данным приёмником." #: lib/solaar/ui/pair_window.py:227 msgid "More paired devices than receiver can support." msgstr "Превышено число сопрягаемых устройств, поддерживаемое приёмником." #: lib/solaar/ui/pair_window.py:229 msgid "No further details are available about the error." msgstr "Дополнительные сведения об этой ошибке отсутствуют." #: lib/solaar/ui/rule_actions.py:48 msgid "Simulate a chorded key click or depress or release.\n" "On Wayland requires write access to /dev/uinput." msgstr "Имитируйте щелчок аккордовой клавиши, нажмите или отпустите.\n" "В Wayland требуется доступ на запись в /dev/uinput." #: lib/solaar/ui/rule_actions.py:53 msgid "Add key" msgstr "Добавить клавишу" #: lib/solaar/ui/rule_actions.py:56 msgid "Click" msgstr "Щелчок" #: lib/solaar/ui/rule_actions.py:59 msgid "Depress" msgstr "Подавить" #: lib/solaar/ui/rule_actions.py:62 msgid "Release" msgstr "Выпускать" #: lib/solaar/ui/rule_actions.py:147 msgid "Simulate a mouse scroll.\n" "On Wayland requires write access to /dev/uinput." msgstr "Имитация прокрутки мыши.\n" "В Wayland требуется доступ на запись в /dev/uinput." #: lib/solaar/ui/rule_actions.py:203 msgid "Simulate a mouse click.\n" "On Wayland requires write access to /dev/uinput." msgstr "Имитация щелчка мыши.В Wayland требуется доступ на запись в /dev/" "uinput." #: lib/solaar/ui/rule_actions.py:206 msgid "Button" msgstr "Кнопка" #: lib/solaar/ui/rule_actions.py:207 msgid "Count and Action" msgstr "Счет и действие" #: lib/solaar/ui/rule_actions.py:256 msgid "Execute a command with arguments." msgstr "Выполните команду с аргументами." #: lib/solaar/ui/rule_actions.py:259 msgid "Add argument" msgstr "Добавить аргумент" #: lib/solaar/ui/rule_conditions.py:43 msgid "X11 active process. For use in X11 only." msgstr "Активный процесс X11. Только для использования в X11." #: lib/solaar/ui/rule_conditions.py:73 msgid "X11 mouse process. For use in X11 only." msgstr "Процесс работы с мышью X11. Только для использования в X11." #: lib/solaar/ui/rule_conditions.py:90 msgid "MouseProcess" msgstr "Действие мыши" #: lib/solaar/ui/rule_conditions.py:114 msgid "Feature name of notification triggering rule processing." msgstr "Название функции обработки правила запуска уведомления." #: lib/solaar/ui/rule_conditions.py:161 msgid "Report number of notification triggering rule processing." msgstr "Сообщите номер уведомления, запускающего обработку правила." #: lib/solaar/ui/rule_conditions.py:194 msgid "Active keyboard modifiers. Not always available in Wayland." msgstr "Активные модификаторы клавиатуры. Не всегда доступны в Wayland." #: lib/solaar/ui/rule_conditions.py:234 msgid "Diverted key or button depressed or released.\n" "Use the Key/Button Diversion and Divert G Keys settings to divert " "keys and buttons." msgstr "Перенаправленная клавиша или кнопка нажата или отпущена.\n" "Для переадресации используйте настройки «Переадресация клавиш/" "кнопок» и «Переадресация G-клавиш» ключи и кнопки." #: lib/solaar/ui/rule_conditions.py:243 msgid "Key down" msgstr "Нажать клавишу" #: lib/solaar/ui/rule_conditions.py:246 msgid "Key up" msgstr "Отпустить клавишу" #: lib/solaar/ui/rule_conditions.py:286 msgid "Diverted key or button is currently down.\n" "Use the Key/Button Diversion and Divert G Keys settings to divert " "keys and buttons." msgstr "Переадресованная клавиша или кнопка в данный момент нажата.\n" "Используйте настройки Переадресации клавиш/кнопок и Переадресации " "клавиш G для переадресации клавиш и кнопок." #: lib/solaar/ui/rule_conditions.py:324 msgid "Test condition on notification triggering rule processing." msgstr "Тестовое условие для обработки правила запуска уведомления." #: lib/solaar/ui/rule_conditions.py:328 msgid "Parameter" msgstr "Параметр" #: lib/solaar/ui/rule_conditions.py:401 msgid "begin (inclusive)" msgstr "начало (включительно)" #: lib/solaar/ui/rule_conditions.py:402 msgid "end (exclusive)" msgstr "конец (исключительно)" #: lib/solaar/ui/rule_conditions.py:410 msgid "range" msgstr "диапазон" #: lib/solaar/ui/rule_conditions.py:413 msgid "minimum" msgstr "минимум" #: lib/solaar/ui/rule_conditions.py:414 msgid "maximum" msgstr "максимум" #: lib/solaar/ui/rule_conditions.py:416 #, python-format msgid "bytes %(0)d to %(1)d, ranging from %(2)d to %(3)d" msgstr "байты с %(0)d по %(1)d, в диапазоне от %(2)d до %(3)d" #: lib/solaar/ui/rule_conditions.py:419 lib/solaar/ui/rule_conditions.py:420 msgid "mask" msgstr "маска" #: lib/solaar/ui/rule_conditions.py:421 #, python-format msgid "bytes %(0)d to %(1)d, mask %(2)d" msgstr "байты с %(0)d по %(1)d, маска %(2)d" #: lib/solaar/ui/rule_conditions.py:430 msgid "Bit or range test on bytes in notification message triggering rule " "processing." msgstr "Проверка битов или диапазона на байтах в уведомительном сообщении, " "запускающем обработку правила." #: lib/solaar/ui/rule_conditions.py:440 msgid "type" msgstr "тип" #: lib/solaar/ui/rule_conditions.py:528 msgid "Mouse gesture with optional initiating button followed by zero or " "more mouse movements." msgstr "Жест мыши с дополнительной кнопкой запуска, за которым следует ноль " "или более движений мыши." #: lib/solaar/ui/rule_conditions.py:533 msgid "Add movement" msgstr "Добавить движение" #: lib/solaar/ui/tray.py:50 msgid "No supported device found" msgstr "Поддерживаемое устройство не найдено" #: lib/solaar/ui/tray.py:55 lib/solaar/ui/window.py:310 #, python-format msgid "About %s" msgstr "О программе %s" #: lib/solaar/ui/tray.py:56 lib/solaar/ui/window.py:308 #, python-format msgid "Quit %s" msgstr "Выйти из %s" #: lib/solaar/ui/tray.py:267 lib/solaar/ui/tray.py:275 msgid "no receiver" msgstr "отсутствует приёмник" #: lib/solaar/ui/tray.py:288 lib/solaar/ui/tray.py:293 msgid "offline" msgstr "нет соединения" #: lib/solaar/ui/tray.py:291 msgid "no status" msgstr "отсутствует статус" #: lib/solaar/ui/window.py:91 msgid "Scanning" msgstr "Сканирование" #: lib/solaar/ui/window.py:122 msgid "Battery" msgstr "Батарея" #: lib/solaar/ui/window.py:125 msgid "Wireless Link" msgstr "Беспроводное соединение" #: lib/solaar/ui/window.py:129 msgid "Lighting" msgstr "Освещённость" #: lib/solaar/ui/window.py:163 msgid "Show Technical Details" msgstr "Технические сведения об устройстве" #: lib/solaar/ui/window.py:179 msgid "Pair new device" msgstr "Новое сопряженное устройство" #: lib/solaar/ui/window.py:197 msgid "Select a device" msgstr "Выбор устройства" #: lib/solaar/ui/window.py:313 msgid "Rule Editor" msgstr "Редактор правил" #: lib/solaar/ui/window.py:517 msgid "Path" msgstr "Расположение" #: lib/solaar/ui/window.py:520 msgid "USB ID" msgstr "Идент. USB" #: lib/solaar/ui/window.py:523 lib/solaar/ui/window.py:525 #: lib/solaar/ui/window.py:540 lib/solaar/ui/window.py:542 msgid "Serial" msgstr "Серийный №" #: lib/solaar/ui/window.py:529 msgid "Index" msgstr "Индекс" #: lib/solaar/ui/window.py:531 msgid "Wireless PID" msgstr "Б/проводн.PID" #: lib/solaar/ui/window.py:533 msgid "Product ID" msgstr "ID изделия" #: lib/solaar/ui/window.py:535 msgid "Protocol" msgstr "Протокол" #: lib/solaar/ui/window.py:535 msgid "Unknown" msgstr "Неизвестен" #: lib/solaar/ui/window.py:537 msgid "Polling rate" msgstr "Част. опроса" #: lib/solaar/ui/window.py:544 msgid "Unit ID" msgstr "ID устройства" #: lib/solaar/ui/window.py:558 msgid "Notifications" msgstr "Уведомления" #: lib/solaar/ui/window.py:602 msgid "No device paired." msgstr "Нет сопряжённых устройств." #: lib/solaar/ui/window.py:611 #, python-format msgid "Up to %(max_count)s device can be paired to this receiver." msgid_plural "Up to %(max_count)s devices can be paired to this receiver." msgstr[0] "С этим приёмником может быть сопряжено до %(max_count)s " "устройства." msgstr[1] "С этим приёмником может быть сопряжено до %(max_count)s " "устройств." msgstr[2] "С этим приёмником может быть сопряжено до %(max_count)s " "устройств." #: lib/solaar/ui/window.py:622 #, python-format msgid "This receiver has %d pairing remaining." msgid_plural "This receiver has %d pairings remaining." msgstr[0] "Данному приёмнику доступно ещё %d сопряжение." msgstr[1] "Данному приёмнику доступно ещё %d сопряжения." msgstr[2] "Данному приёмнику доступно ещё %d сопряжений." #: lib/solaar/ui/window.py:679 msgid "Battery Voltage" msgstr "Напряжение батареи" #: lib/solaar/ui/window.py:681 msgid "Voltage reported by battery" msgstr "Напряжение батареи" #: lib/solaar/ui/window.py:683 msgid "Battery Level" msgstr "Уровень заряда" #: lib/solaar/ui/window.py:685 msgid "Approximate level reported by battery" msgstr "Приблизительный уровень заряда, cообщаемый батареей" #: lib/solaar/ui/window.py:692 lib/solaar/ui/window.py:694 msgid "next reported " msgstr "следующий доклад на " #: lib/solaar/ui/window.py:695 msgid " and next level to be reported." msgstr " \n" "(следующий уровень для отчёта)." #: lib/solaar/ui/window.py:711 msgid "encrypted" msgstr "зашифровано" #: lib/solaar/ui/window.py:713 msgid "The wireless link between this device and its receiver is encrypted." msgstr "Беспроводное соединение между этим устройством и его приёмником " "зашифровано." #: lib/solaar/ui/window.py:715 msgid "not encrypted" msgstr "не зашифровано" #: lib/solaar/ui/window.py:719 msgid "The wireless link between this device and its receiver is not " "encrypted.\n" "This is a security issue for pointing devices, and a major security " "issue for text-input devices." msgstr "Беспроводное соединение между этим устройством и его приёмником не " "зашифровано.\n" "Это проблема безопасности для указывающих устройств и серьезная " "проблема безопасности для устройств ввода текста." #: lib/solaar/ui/window.py:735 #, python-format msgid "%(light_level)d lux" msgstr "%(light_level)d люкс" Solaar-1.1.14/po/sk.po000066400000000000000000001554061473525213700144360ustar00rootroot00000000000000# Slovak translations for solaar package. # Copyright (C) 2021 THE solaar'S COPYRIGHT HOLDER # This file is distributed under the same license as the solaar package. # Automatically generated, 2021. # msgid "" msgstr "Project-Id-Version: solaar 1.0.6\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-12-28 17:40+0100\n" "PO-Revision-Date: 2021-08-24 09:07+0200\n" "Last-Translator: Jose Riha \n" "Language-Team: none\n" "Language: sk\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : " "2;\n" "X-Generator: Poedit 3.0\n" #: lib/logitech_receiver/base_usb.py:46 msgid "Bolt Receiver" msgstr "" #: lib/logitech_receiver/base_usb.py:57 msgid "Unifying Receiver" msgstr "Prijímač Unifying" #: lib/logitech_receiver/base_usb.py:67 lib/logitech_receiver/base_usb.py:78 #: lib/logitech_receiver/base_usb.py:90 lib/logitech_receiver/base_usb.py:102 #: lib/logitech_receiver/base_usb.py:114 msgid "Nano Receiver" msgstr "Prijímač Nano" #: lib/logitech_receiver/base_usb.py:124 msgid "Lightspeed Receiver" msgstr "Prijímač Lightspeed" #: lib/logitech_receiver/base_usb.py:133 msgid "EX100 Receiver 27 Mhz" msgstr "Prijímač EX100 27 Mhz" #: lib/logitech_receiver/i18n.py:30 msgid "empty" msgstr "prázdna" #: lib/logitech_receiver/i18n.py:31 msgid "critical" msgstr "kritická" #: lib/logitech_receiver/i18n.py:32 msgid "low" msgstr "nízka" #: lib/logitech_receiver/i18n.py:33 msgid "average" msgstr "priemerná" #: lib/logitech_receiver/i18n.py:34 msgid "good" msgstr "dobrá" #: lib/logitech_receiver/i18n.py:35 msgid "full" msgstr "plná" #: lib/logitech_receiver/i18n.py:38 msgid "discharging" msgstr "vybíja sa" #: lib/logitech_receiver/i18n.py:39 msgid "recharging" msgstr "nabíja sa" #: lib/logitech_receiver/i18n.py:40 lib/solaar/ui/window.py:711 msgid "charging" msgstr "nabíja sa" #: lib/logitech_receiver/i18n.py:41 msgid "not charging" msgstr "nenabíja sa" #: lib/logitech_receiver/i18n.py:42 msgid "almost full" msgstr "takmer nabitá" #: lib/logitech_receiver/i18n.py:43 msgid "charged" msgstr "nabitá" #: lib/logitech_receiver/i18n.py:44 msgid "slow recharge" msgstr "pomalé nabíjanie" #: lib/logitech_receiver/i18n.py:45 msgid "invalid battery" msgstr "chybná batéria" #: lib/logitech_receiver/i18n.py:46 msgid "thermal error" msgstr "teplotná chyba" #: lib/logitech_receiver/i18n.py:47 msgid "error" msgstr "chyba" #: lib/logitech_receiver/i18n.py:48 msgid "standard" msgstr "štandardné" #: lib/logitech_receiver/i18n.py:49 msgid "fast" msgstr "rýchle" #: lib/logitech_receiver/i18n.py:50 msgid "slow" msgstr "pomalé" #: lib/logitech_receiver/i18n.py:53 msgid "device timeout" msgstr "vypršal časový limit zariadenia" #: lib/logitech_receiver/i18n.py:54 msgid "device not supported" msgstr "nepodporované zariadenie" #: lib/logitech_receiver/i18n.py:55 msgid "too many devices" msgstr "príliš veľa zariadení" #: lib/logitech_receiver/i18n.py:56 msgid "sequence timeout" msgstr "vypršal časový limit sekvencie" #: lib/logitech_receiver/i18n.py:59 lib/solaar/ui/window.py:572 msgid "Firmware" msgstr "Firmvér" #: lib/logitech_receiver/i18n.py:60 msgid "Bootloader" msgstr "Bootloader" #: lib/logitech_receiver/i18n.py:61 msgid "Hardware" msgstr "Hardvér" #: lib/logitech_receiver/i18n.py:62 msgid "Other" msgstr "Ďalší" #: lib/logitech_receiver/i18n.py:65 msgid "Left Button" msgstr "" #: lib/logitech_receiver/i18n.py:66 msgid "Right Button" msgstr "" #: lib/logitech_receiver/i18n.py:67 msgid "Middle Button" msgstr "" #: lib/logitech_receiver/i18n.py:68 msgid "Back Button" msgstr "" #: lib/logitech_receiver/i18n.py:69 msgid "Forward Button" msgstr "" #: lib/logitech_receiver/i18n.py:70 msgid "Mouse Gesture Button" msgstr "Tlačidlo gesta myši" #: lib/logitech_receiver/i18n.py:71 msgid "Smart Shift" msgstr "" #: lib/logitech_receiver/i18n.py:72 msgid "DPI Switch" msgstr "" #: lib/logitech_receiver/i18n.py:73 msgid "Left Tilt" msgstr "" #: lib/logitech_receiver/i18n.py:74 msgid "Right Tilt" msgstr "" #: lib/logitech_receiver/i18n.py:75 msgid "Left Click" msgstr "" #: lib/logitech_receiver/i18n.py:76 msgid "Right Click" msgstr "" #: lib/logitech_receiver/i18n.py:77 msgid "Mouse Middle Button" msgstr "" #: lib/logitech_receiver/i18n.py:78 msgid "Mouse Back Button" msgstr "" #: lib/logitech_receiver/i18n.py:79 msgid "Mouse Forward Button" msgstr "" #: lib/logitech_receiver/i18n.py:80 msgid "Gesture Button Navigation" msgstr "" #: lib/logitech_receiver/i18n.py:81 msgid "Mouse Scroll Left Button" msgstr "" #: lib/logitech_receiver/i18n.py:82 msgid "Mouse Scroll Right Button" msgstr "" #: lib/logitech_receiver/i18n.py:85 msgid "pressed" msgstr "" #: lib/logitech_receiver/i18n.py:86 msgid "released" msgstr "" #: lib/logitech_receiver/notifications.py:75 #: lib/logitech_receiver/notifications.py:126 msgid "pairing lock is closed" msgstr "zámok párovania bol uzatvorený" #: lib/logitech_receiver/notifications.py:75 #: lib/logitech_receiver/notifications.py:126 msgid "pairing lock is open" msgstr "zámok párovania bol otvorený" #: lib/logitech_receiver/notifications.py:92 msgid "discovery lock is closed" msgstr "" #: lib/logitech_receiver/notifications.py:92 msgid "discovery lock is open" msgstr "" #: lib/logitech_receiver/notifications.py:224 lib/solaar/ui/notify.py:122 msgid "connected" msgstr "pripojené" #: lib/logitech_receiver/notifications.py:224 msgid "disconnected" msgstr "odpojené" #: lib/logitech_receiver/notifications.py:262 lib/solaar/ui/notify.py:120 msgid "unpaired" msgstr "nespárované" #: lib/logitech_receiver/notifications.py:304 msgid "powered on" msgstr "zapnuté" #: lib/logitech_receiver/settings.py:750 msgid "register" msgstr "prihlásiť" #: lib/logitech_receiver/settings.py:764 lib/logitech_receiver/settings.py:791 msgid "feature" msgstr "vlastnosť" #: lib/logitech_receiver/settings_templates.py:139 msgid "Swap Fx function" msgstr "Prepnúť funkciu Fx" #: lib/logitech_receiver/settings_templates.py:140 msgid "When set, the F1..F12 keys will activate their special function,\n" "and you must hold the FN key to activate their standard function." msgstr "Ak je nastavené, budú klávesy F1..F12 zapínať ich špeciálne " "funkcie.\n" "Pre aktiváciu ich štandardnej funkcie budete musieť súčasne stlačiť " "Fn." #: lib/logitech_receiver/settings_templates.py:142 msgid "When unset, the F1..F12 keys will activate their standard function,\n" "and you must hold the FN key to activate their special function." msgstr "Ak nie je nastavené, budú klávesy F1..F12 zapínať ich štandardné " "funkcie.\n" "Pre aktiváciu špeciálnych funkcií budete musieť súčasne stlačiť Fn." #: lib/logitech_receiver/settings_templates.py:149 msgid "Hand Detection" msgstr "Detekcia rúk" #: lib/logitech_receiver/settings_templates.py:150 msgid "Turn on illumination when the hands hover over the keyboard." msgstr "Zapne podsvietenie, keď sa nad klávesnicou objavia ruky." #: lib/logitech_receiver/settings_templates.py:157 msgid "Scroll Wheel Smooth Scrolling" msgstr "Plynulé skrolovanie" #: lib/logitech_receiver/settings_templates.py:158 #: lib/logitech_receiver/settings_templates.py:239 #: lib/logitech_receiver/settings_templates.py:267 msgid "High-sensitivity mode for vertical scroll with the wheel." msgstr "Vysokocitlivý režim zvislého posunu kolieska myši." #: lib/logitech_receiver/settings_templates.py:165 msgid "Side Scrolling" msgstr "Bočné skrolovanie" #: lib/logitech_receiver/settings_templates.py:167 msgid "When disabled, pushing the wheel sideways sends custom button " "events\n" "instead of the standard side-scrolling events." msgstr "Ak je vypnuté, bočné stlačenie kolieska vygeneruje vlastné udalosti " "tlačidiel\n" "namiesto štandardných udalostí skrolovania do strán." #: lib/logitech_receiver/settings_templates.py:177 msgid "Sensitivity (DPI - older mice)" msgstr "" #: lib/logitech_receiver/settings_templates.py:178 #: lib/logitech_receiver/settings_templates.py:712 msgid "Mouse movement sensitivity" msgstr "Citlivosť kurzora myši" #: lib/logitech_receiver/settings_templates.py:208 #: lib/logitech_receiver/settings_templates.py:218 #: lib/logitech_receiver/settings_templates.py:225 msgid "Backlight" msgstr "Podsvietenie" #: lib/logitech_receiver/settings_templates.py:209 #: lib/logitech_receiver/settings_templates.py:226 msgid "Set illumination time for keyboard." msgstr "" #: lib/logitech_receiver/settings_templates.py:219 msgid "Turn illumination on or off on keyboard." msgstr "Vypne/zapne podsvietenie klávesnice." #: lib/logitech_receiver/settings_templates.py:237 msgid "Scroll Wheel High Resolution" msgstr "Vysoké rozlíšenie skrolovania myši" #: lib/logitech_receiver/settings_templates.py:240 #: lib/logitech_receiver/settings_templates.py:268 msgid "Set to ignore if scrolling is abnormally fast or slow" msgstr "" #: lib/logitech_receiver/settings_templates.py:247 #: lib/logitech_receiver/settings_templates.py:277 msgid "Scroll Wheel Diversion" msgstr "" #: lib/logitech_receiver/settings_templates.py:249 msgid "Make scroll wheel send LOWRES_WHEEL HID++ notifications (which " "trigger Solaar rules but are otherwise ignored)." msgstr "" #: lib/logitech_receiver/settings_templates.py:256 msgid "Scroll Wheel Direction" msgstr "Smer skrolovania" #: lib/logitech_receiver/settings_templates.py:257 msgid "Invert direction for vertical scroll with wheel." msgstr "Zmeniť zmer zvislého skrolovania myši." #: lib/logitech_receiver/settings_templates.py:265 msgid "Scroll Wheel Resolution" msgstr "Rozlíšenie skrolovania myši" #: lib/logitech_receiver/settings_templates.py:279 msgid "Make scroll wheel send HIRES_WHEEL HID++ notifications (which " "trigger Solaar rules but are otherwise ignored)." msgstr "" #: lib/logitech_receiver/settings_templates.py:288 msgid "Sensitivity (Pointer Speed)" msgstr "Citlivosť (rýchlosť kurzora)" #: lib/logitech_receiver/settings_templates.py:289 msgid "Speed multiplier for mouse (256 is normal multiplier)." msgstr "Násobič rýchlosti myši (256 je bežný násobič)." #: lib/logitech_receiver/settings_templates.py:299 msgid "Thumb Wheel Diversion" msgstr "" #: lib/logitech_receiver/settings_templates.py:301 msgid "Make thumb wheel send THUMB_WHEEL HID++ notifications (which trigger " "Solaar rules but are otherwise ignored)." msgstr "" #: lib/logitech_receiver/settings_templates.py:310 msgid "Thumb Wheel Direction" msgstr "Smer posuvného kolieska" #: lib/logitech_receiver/settings_templates.py:311 msgid "Invert thumb wheel scroll direction." msgstr "Zmeniť smer otáčania kolieska." #: lib/logitech_receiver/settings_templates.py:319 msgid "Onboard Profiles" msgstr "" #: lib/logitech_receiver/settings_templates.py:320 msgid "Enable onboard profiles, which often control report rate and " "keyboard lighting" msgstr "" #: lib/logitech_receiver/settings_templates.py:330 msgid "Polling Rate (ms)" msgstr "Intenzita dopytovania (ms)" #: lib/logitech_receiver/settings_templates.py:332 msgid "Frequency of device polling, in milliseconds" msgstr "Frekvencia dopytov na zariadenie, v ms" #: lib/logitech_receiver/settings_templates.py:333 #: lib/logitech_receiver/settings_templates.py:1046 #: lib/logitech_receiver/settings_templates.py:1076 msgid "May need Onboard Profiles set to Disable to be effective." msgstr "" #: lib/logitech_receiver/settings_templates.py:365 msgid "Divert crown events" msgstr "Odklonenie udalostí korunky" #: lib/logitech_receiver/settings_templates.py:366 msgid "Make crown send CROWN HID++ notifications (which trigger Solaar " "rules but are otherwise ignored)." msgstr "Korunka bude zasielať CROWN HID++ notifikácie (tie aktivujú Solaar " "pravidlá, ale inak budú ignorované)." #: lib/logitech_receiver/settings_templates.py:374 msgid "Crown smooth scroll" msgstr "" #: lib/logitech_receiver/settings_templates.py:375 msgid "Set crown smooth scroll" msgstr "" #: lib/logitech_receiver/settings_templates.py:383 msgid "Divert G Keys" msgstr "Odkloniť G klávesy" #: lib/logitech_receiver/settings_templates.py:385 msgid "Make G keys send GKEY HID++ notifications (which trigger Solaar " "rules but are otherwise ignored)." msgstr "G klávesy budú zasielať GKEY HID++ notifikácie (tie aktivujú Solaar " "pravidlá, ale inak budú ignorované)." #: lib/logitech_receiver/settings_templates.py:386 msgid "May also make M keys and MR key send HID++ notifications" msgstr "" #: lib/logitech_receiver/settings_templates.py:402 msgid "Scroll Wheel Ratcheted" msgstr "" #: lib/logitech_receiver/settings_templates.py:403 msgid "Switch the mouse wheel between speed-controlled ratcheting and " "always freespin." msgstr "" #: lib/logitech_receiver/settings_templates.py:405 msgid "Freespinning" msgstr "" #: lib/logitech_receiver/settings_templates.py:405 msgid "Ratcheted" msgstr "" #: lib/logitech_receiver/settings_templates.py:412 msgid "Scroll Wheel Ratchet Speed" msgstr "" #: lib/logitech_receiver/settings_templates.py:414 msgid "Use the mouse wheel speed to switch between ratcheted and " "freespinning.\n" "The mouse wheel is always ratcheted at 50." msgstr "" #: lib/logitech_receiver/settings_templates.py:463 msgid "Key/Button Actions" msgstr "Akcie klávesu/tlačidla" #: lib/logitech_receiver/settings_templates.py:465 msgid "Change the action for the key or button." msgstr "Zmeniť akciu klávesy alebo tlačidla." #: lib/logitech_receiver/settings_templates.py:465 msgid "Overridden by diversion." msgstr "" #: lib/logitech_receiver/settings_templates.py:466 msgid "Changing important actions (such as for the left mouse button) can " "result in an unusable system." msgstr "Zmenou dôležitej akcie (napríklad pre ľavé tlačidlo myši) môžete " "systém uviesť do stavu, kedy sa nebude dať používať." #: lib/logitech_receiver/settings_templates.py:639 msgid "Key/Button Diversion" msgstr "Odklonenie klávesu/tlačidla" #: lib/logitech_receiver/settings_templates.py:640 msgid "Make the key or button send HID++ notifications (Diverted) or " "initiate Mouse Gestures or Sliding DPI" msgstr "" #: lib/logitech_receiver/settings_templates.py:643 #: lib/logitech_receiver/settings_templates.py:644 #: lib/logitech_receiver/settings_templates.py:645 msgid "Diverted" msgstr "Odklonené" #: lib/logitech_receiver/settings_templates.py:643 #: lib/logitech_receiver/settings_templates.py:644 msgid "Mouse Gestures" msgstr "Gestá myši" #: lib/logitech_receiver/settings_templates.py:643 #: lib/logitech_receiver/settings_templates.py:644 #: lib/logitech_receiver/settings_templates.py:645 msgid "Regular" msgstr "Normálne" #: lib/logitech_receiver/settings_templates.py:643 msgid "Sliding DPI" msgstr "" #: lib/logitech_receiver/settings_templates.py:711 msgid "Sensitivity (DPI)" msgstr "Citlivosť (DPI)" #: lib/logitech_receiver/settings_templates.py:752 msgid "Sensitivity Switching" msgstr "" #: lib/logitech_receiver/settings_templates.py:754 msgid "Switch the current sensitivity and the remembered sensitivity when " "the key or button is pressed.\n" "If there is no remembered sensitivity, just remember the current " "sensitivity" msgstr "" #: lib/logitech_receiver/settings_templates.py:758 msgid "Off" msgstr "Vyp" #: lib/logitech_receiver/settings_templates.py:791 msgid "Disable keys" msgstr "Zakázať klávesy" #: lib/logitech_receiver/settings_templates.py:792 msgid "Disable specific keyboard keys." msgstr "Zakázať špecifické klávesy." #: lib/logitech_receiver/settings_templates.py:795 #, python-format msgid "Disables the %s key." msgstr "Zakáže %s kláves." #: lib/logitech_receiver/settings_templates.py:809 #: lib/logitech_receiver/settings_templates.py:860 msgid "Set OS" msgstr "Nastaviť OS" #: lib/logitech_receiver/settings_templates.py:810 #: lib/logitech_receiver/settings_templates.py:861 msgid "Change keys to match OS." msgstr "Zmeniť klávesy podľa operačného systému." #: lib/logitech_receiver/settings_templates.py:873 msgid "Change Host" msgstr "Zmeniť hostiteľa" #: lib/logitech_receiver/settings_templates.py:874 msgid "Switch connection to a different host" msgstr "Prepnúť spojenie na iného hostiteľa" #: lib/logitech_receiver/settings_templates.py:900 msgid "Performs a left click." msgstr "Vykoná kliknutie ľavým tlačidlom." #: lib/logitech_receiver/settings_templates.py:900 msgid "Single tap" msgstr "Dotyk" #: lib/logitech_receiver/settings_templates.py:901 msgid "Performs a right click." msgstr "Vykoná kliknutie pravým tlačidlom." #: lib/logitech_receiver/settings_templates.py:901 msgid "Single tap with two fingers" msgstr "Dotyk dvoma prstami" #: lib/logitech_receiver/settings_templates.py:902 msgid "Single tap with three fingers" msgstr "Dotyk tromi prstami" #: lib/logitech_receiver/settings_templates.py:906 msgid "Double tap" msgstr "Dvojdotyk" #: lib/logitech_receiver/settings_templates.py:906 msgid "Performs a double click." msgstr "Vykoná dvojité kliknutie." #: lib/logitech_receiver/settings_templates.py:907 msgid "Double tap with two fingers" msgstr "Dvojdotyk dvoma prstami" #: lib/logitech_receiver/settings_templates.py:908 msgid "Double tap with three fingers" msgstr "Dvojdotyk troma prstami" #: lib/logitech_receiver/settings_templates.py:911 msgid "Drags items by dragging the finger after double tapping." msgstr "Presúvanie položiek ťahaním prstu po dvojdotyku." #: lib/logitech_receiver/settings_templates.py:911 msgid "Tap and drag" msgstr "Ťahaj a pusti" #: lib/logitech_receiver/settings_templates.py:913 msgid "Drags items by dragging the fingers after double tapping." msgstr "Presúvanie položiek ťahaním prstov po dvojdotyku." #: lib/logitech_receiver/settings_templates.py:913 msgid "Tap and drag with two fingers" msgstr "Dotyk a ťahanie dvomi prstami" #: lib/logitech_receiver/settings_templates.py:914 msgid "Tap and drag with three fingers" msgstr "Dotyk a ťahanie tromi prstami" #: lib/logitech_receiver/settings_templates.py:917 msgid "Disables tap and edge gestures (equivalent to pressing Fn+LeftClick)." msgstr "Vypnúť gestá dotyku a hrany (ekvivalent stlačenia Fn + ľavý klik)." #: lib/logitech_receiver/settings_templates.py:917 msgid "Suppress tap and edge gestures" msgstr "Potlačiť gestá dotyku a hrany" #: lib/logitech_receiver/settings_templates.py:918 msgid "Scroll with one finger" msgstr "Skrolovanie jedným prstom" #: lib/logitech_receiver/settings_templates.py:918 #: lib/logitech_receiver/settings_templates.py:919 #: lib/logitech_receiver/settings_templates.py:922 msgid "Scrolls." msgstr "Skrolovania." #: lib/logitech_receiver/settings_templates.py:919 #: lib/logitech_receiver/settings_templates.py:922 msgid "Scroll with two fingers" msgstr "Skrolovanie dvoma prstami" #: lib/logitech_receiver/settings_templates.py:920 msgid "Scroll horizontally with two fingers" msgstr "Vodorovné skrolovanie dvoma prstami" #: lib/logitech_receiver/settings_templates.py:920 msgid "Scrolls horizontally." msgstr "Vodorovné skrolovanie." #: lib/logitech_receiver/settings_templates.py:921 msgid "Scroll vertically with two fingers" msgstr "Zvislé skrolovanie dvoma prstami" #: lib/logitech_receiver/settings_templates.py:921 msgid "Scrolls vertically." msgstr "Zvislé skrolovanie." #: lib/logitech_receiver/settings_templates.py:923 msgid "Inverts the scrolling direction." msgstr "Zmeniť smer otáčania." #: lib/logitech_receiver/settings_templates.py:923 msgid "Natural scrolling" msgstr "Prirodzené skrolovanie" #: lib/logitech_receiver/settings_templates.py:924 msgid "Enables the thumbwheel." msgstr "Povoliť koliesko." #: lib/logitech_receiver/settings_templates.py:924 msgid "Thumbwheel" msgstr "Koliesko" #: lib/logitech_receiver/settings_templates.py:935 #: lib/logitech_receiver/settings_templates.py:939 msgid "Swipe from the top edge" msgstr "Ťahanie z hornej hrany" #: lib/logitech_receiver/settings_templates.py:936 msgid "Swipe from the left edge" msgstr "Ťahanie z ľavej hrany" #: lib/logitech_receiver/settings_templates.py:937 msgid "Swipe from the right edge" msgstr "Ťahanie z pravej hrany" #: lib/logitech_receiver/settings_templates.py:938 msgid "Swipe from the bottom edge" msgstr "Ťahanie zo spodnej hrany" #: lib/logitech_receiver/settings_templates.py:940 msgid "Swipe two fingers from the left edge" msgstr "Ťahanie dvoma prstami z ľavej hrany" #: lib/logitech_receiver/settings_templates.py:941 msgid "Swipe two fingers from the right edge" msgstr "Ťahanie dvoma prstami z pravej hrany" #: lib/logitech_receiver/settings_templates.py:942 msgid "Swipe two fingers from the bottom edge" msgstr "Ťahanie dvoma prstami zo spodnej hrany" #: lib/logitech_receiver/settings_templates.py:943 msgid "Swipe two fingers from the top edge" msgstr "Ťahanie dvoma prstami z hornej hrany" #: lib/logitech_receiver/settings_templates.py:944 #: lib/logitech_receiver/settings_templates.py:948 msgid "Pinch to zoom out; spread to zoom in." msgstr "Zovrieť pre priblíženie; rozovrieť pre oddialenie." #: lib/logitech_receiver/settings_templates.py:944 msgid "Zoom with two fingers." msgstr "Priblíženie dvoma prstami." #: lib/logitech_receiver/settings_templates.py:945 msgid "Pinch to zoom out." msgstr "Zovrieť pre priblíženie." #: lib/logitech_receiver/settings_templates.py:946 msgid "Spread to zoom in." msgstr "Rozovrieť pre oddialenie." #: lib/logitech_receiver/settings_templates.py:947 msgid "Zoom with three fingers." msgstr "Priblíženie troma prstami." #: lib/logitech_receiver/settings_templates.py:948 msgid "Zoom with two fingers" msgstr "Priblíženie dvoma pstami" #: lib/logitech_receiver/settings_templates.py:966 msgid "Pixel zone" msgstr "" #: lib/logitech_receiver/settings_templates.py:967 msgid "Ratio zone" msgstr "" #: lib/logitech_receiver/settings_templates.py:968 msgid "Scale factor" msgstr "Faktor mierky" #: lib/logitech_receiver/settings_templates.py:968 msgid "Sets the cursor speed." msgstr "" #: lib/logitech_receiver/settings_templates.py:972 msgid "Left" msgstr "Vľavo" #: lib/logitech_receiver/settings_templates.py:972 msgid "Left-most coordinate." msgstr "Súradnica najviac vľavo." #: lib/logitech_receiver/settings_templates.py:973 msgid "Bottom" msgstr "" #: lib/logitech_receiver/settings_templates.py:973 msgid "Bottom coordinate." msgstr "" #: lib/logitech_receiver/settings_templates.py:974 msgid "Width" msgstr "Šírka" #: lib/logitech_receiver/settings_templates.py:974 msgid "Width." msgstr "Šírka." #: lib/logitech_receiver/settings_templates.py:975 msgid "Height" msgstr "Výška" #: lib/logitech_receiver/settings_templates.py:975 msgid "Height." msgstr "Výška." #: lib/logitech_receiver/settings_templates.py:976 msgid "Cursor speed." msgstr "Rýchlosť kurzora." #: lib/logitech_receiver/settings_templates.py:976 msgid "Scale" msgstr "Mierka" #: lib/logitech_receiver/settings_templates.py:982 msgid "Gestures" msgstr "Gestá" #: lib/logitech_receiver/settings_templates.py:983 msgid "Tweak the mouse/touchpad behaviour." msgstr "Upraviť správanie myši/touchpadu." #: lib/logitech_receiver/settings_templates.py:1000 msgid "Gestures Diversion" msgstr "" #: lib/logitech_receiver/settings_templates.py:1001 msgid "Divert mouse/touchpad gestures." msgstr "" #: lib/logitech_receiver/settings_templates.py:1018 msgid "Gesture params" msgstr "Parametre gesta" #: lib/logitech_receiver/settings_templates.py:1019 msgid "Change numerical parameters of a mouse/touchpad." msgstr "Zmeňte číselné parametre myši/touchpadu." #: lib/logitech_receiver/settings_templates.py:1044 msgid "M-Key LEDs" msgstr "" #: lib/logitech_receiver/settings_templates.py:1046 msgid "Control the M-Key LEDs." msgstr "" #: lib/logitech_receiver/settings_templates.py:1047 #: lib/logitech_receiver/settings_templates.py:1077 msgid "May need G Keys diverted to be effective." msgstr "" #: lib/logitech_receiver/settings_templates.py:1053 #, python-format msgid "Lights up the %s key." msgstr "" #: lib/logitech_receiver/settings_templates.py:1074 msgid "MR-Key LED" msgstr "" #: lib/logitech_receiver/settings_templates.py:1076 msgid "Control the MR-Key LED." msgstr "" #: lib/logitech_receiver/settings_templates.py:1095 msgid "Persistent Key/Button Mapping" msgstr "" #: lib/logitech_receiver/settings_templates.py:1097 msgid "Permanently change the mapping for the key or button." msgstr "" #: lib/logitech_receiver/settings_templates.py:1098 msgid "Changing important keys or buttons (such as for the left mouse " "button) can result in an unusable system." msgstr "" #: lib/logitech_receiver/settings_templates.py:1157 msgid "Sidetone" msgstr "" #: lib/logitech_receiver/settings_templates.py:1158 msgid "Set sidetone level." msgstr "" #: lib/logitech_receiver/settings_templates.py:1167 msgid "Equalizer" msgstr "" #: lib/logitech_receiver/settings_templates.py:1168 msgid "Set equalizer levels." msgstr "" #: lib/logitech_receiver/settings_templates.py:1191 msgid "Hz" msgstr "" #: lib/logitech_receiver/settings_templates.py:1197 msgid "Power Management" msgstr "" #: lib/logitech_receiver/settings_templates.py:1198 msgid "Power off in minutes (0 for never)." msgstr "" #: lib/logitech_receiver/status.py:114 msgid "No paired devices." msgstr "Žiadne spárované zariadenia." #: lib/logitech_receiver/status.py:115 lib/solaar/ui/window.py:622 #, python-format msgid "%(count)s paired device." msgid_plural "%(count)s paired devices." msgstr[0] "%(count)s spárované zariadenie." msgstr[1] "%(count)s spárované zariadenia." msgstr[2] "%(count)s spárovaných zariadení." #: lib/logitech_receiver/status.py:170 #, python-format msgid "Battery: %(level)s" msgstr "Batéria: %(level)s" #: lib/logitech_receiver/status.py:172 #, python-format msgid "Battery: %(percent)d%%" msgstr "Batéria: %(percent)d%%" #: lib/logitech_receiver/status.py:184 #, python-format msgid "Lighting: %(level)s lux" msgstr "Osvetlenie: %(level)s lux" #: lib/logitech_receiver/status.py:239 #, python-format msgid "Battery: %(level)s (%(status)s)" msgstr "Batéria: %(level)s (%(status)s)" #: lib/logitech_receiver/status.py:241 #, python-format msgid "Battery: %(percent)d%% (%(status)s)" msgstr "Batéria: %(percent)d%% (%(status)s)" #: lib/solaar/ui/__init__.py:52 msgid "Permissions error" msgstr "Chyba oprávnenia" #: lib/solaar/ui/__init__.py:54 #, python-format msgid "Found a Logitech receiver or device (%s), but did not have " "permission to open it." msgstr "" #: lib/solaar/ui/__init__.py:55 msgid "If you've just installed Solaar, try disconnecting the receiver or " "device and then reconnecting it." msgstr "" #: lib/solaar/ui/__init__.py:58 msgid "Cannot connect to device error" msgstr "Chyba počas pripájania k zariadeniu" #: lib/solaar/ui/__init__.py:60 #, python-format msgid "Found a Logitech receiver or device at %s, but encountered an error " "connecting to it." msgstr "Našiel sa Logitech prijímač alebo zariadenie na %s, ale pri pokuse o " "pripojenie nastala chyba." #: lib/solaar/ui/__init__.py:61 msgid "Try disconnecting the device and then reconnecting it or turning it " "off and then on." msgstr "" #: lib/solaar/ui/__init__.py:64 msgid "Unpairing failed" msgstr "Rušenie spárovania skončilo s chybou" #: lib/solaar/ui/__init__.py:66 #, python-brace-format msgid "Failed to unpair %{device} from %{receiver}." msgstr "Zlyhalo zrušenie spárovania zariadenia %{device} k %{receiver}." #: lib/solaar/ui/__init__.py:67 msgid "The receiver returned an error, with no further details." msgstr "Prijímač vrátil chybu, ale nie sú dostupné žiadne podrobnosti o nej." #: lib/solaar/ui/__init__.py:177 msgid "Another Solaar process is already running so just expose its window" msgstr "" #: lib/solaar/ui/about.py:36 msgid "Manages Logitech receivers,\n" "keyboards, mice, and tablets." msgstr "Správa prijímačov Logitech,\n" "klávesníc, myší a tabletov." #: lib/solaar/ui/about.py:44 msgid "Additional Programming" msgstr "Dodatočné programovanie" #: lib/solaar/ui/about.py:45 msgid "GUI design" msgstr "Návrh GUI" #: lib/solaar/ui/about.py:47 msgid "Testing" msgstr "Testovanie" #: lib/solaar/ui/about.py:54 msgid "Logitech documentation" msgstr "Dokumentácia Logitechu" #: lib/solaar/ui/action.py:85 lib/solaar/ui/action.py:89 #: lib/solaar/ui/window.py:197 msgid "Unpair" msgstr "Zrušiť párovanie" #: lib/solaar/ui/action.py:88 lib/solaar/ui/diversion_rules.py:150 msgid "Cancel" msgstr "Zrušiť" #: lib/solaar/ui/config_panel.py:212 msgid "Complete - ENTER to change" msgstr "" #: lib/solaar/ui/config_panel.py:212 msgid "Incomplete" msgstr "" #: lib/solaar/ui/config_panel.py:455 lib/solaar/ui/config_panel.py:507 #, python-format msgid "%d value" msgid_plural "%d values" msgstr[0] "%d hodnota" msgstr[1] "%d hodnoty" msgstr[2] "%d hodnôt" #: lib/solaar/ui/config_panel.py:518 msgid "Changes allowed" msgstr "Zmeny sú povolené" #: lib/solaar/ui/config_panel.py:519 msgid "No changes allowed" msgstr "Zmeny nie sú povolené" #: lib/solaar/ui/config_panel.py:520 msgid "Ignore this setting" msgstr "Ignorovať toto nastavenie" #: lib/solaar/ui/config_panel.py:565 msgid "Working" msgstr "Pracujem" #: lib/solaar/ui/config_panel.py:568 msgid "Read/write operation failed." msgstr "Operácia čítania/zápisu zlyhala." #: lib/solaar/ui/diversion_rules.py:65 msgid "Built-in rules" msgstr "Zabudované pravidlá" #: lib/solaar/ui/diversion_rules.py:65 msgid "User-defined rules" msgstr "Používateľom definované pravidlá" #: lib/solaar/ui/diversion_rules.py:67 lib/solaar/ui/diversion_rules.py:1083 msgid "Rule" msgstr "Pravidlo" #: lib/solaar/ui/diversion_rules.py:68 lib/solaar/ui/diversion_rules.py:509 #: lib/solaar/ui/diversion_rules.py:636 msgid "Sub-rule" msgstr "Podpravidlo" #: lib/solaar/ui/diversion_rules.py:70 msgid "[empty]" msgstr "[prázdne]" #: lib/solaar/ui/diversion_rules.py:94 msgid "Solaar Rule Editor" msgstr "Editor pravidiel Solaar" #: lib/solaar/ui/diversion_rules.py:141 msgid "Make changes permanent?" msgstr "Označiť zmeny ako trvalé?" #: lib/solaar/ui/diversion_rules.py:146 msgid "Yes" msgstr "Áno" #: lib/solaar/ui/diversion_rules.py:148 msgid "No" msgstr "Nie" #: lib/solaar/ui/diversion_rules.py:153 msgid "If you choose No, changes will be lost when Solaar is closed." msgstr "Ak vyberiete Nie, zmeny, ktoré ste vykonali sa po ukončení " "aplikácie, stratia." #: lib/solaar/ui/diversion_rules.py:201 msgid "Save changes" msgstr "Uložiť zmeny" #: lib/solaar/ui/diversion_rules.py:206 msgid "Discard changes" msgstr "Zahodiť zmeny" #: lib/solaar/ui/diversion_rules.py:372 msgid "Insert here" msgstr "Vložiť sem" #: lib/solaar/ui/diversion_rules.py:374 msgid "Insert above" msgstr "Vložiť nad" #: lib/solaar/ui/diversion_rules.py:376 msgid "Insert below" msgstr "Vložiť pod" #: lib/solaar/ui/diversion_rules.py:382 msgid "Insert new rule here" msgstr "Vložiť nové pravidlo sem" #: lib/solaar/ui/diversion_rules.py:384 msgid "Insert new rule above" msgstr "Vložiť nové pravidlo nad" #: lib/solaar/ui/diversion_rules.py:386 msgid "Insert new rule below" msgstr "Vložiť nové pravidlo pod" #: lib/solaar/ui/diversion_rules.py:427 msgid "Paste here" msgstr "Vložiť sem" #: lib/solaar/ui/diversion_rules.py:429 msgid "Paste above" msgstr "Vložiť nad" #: lib/solaar/ui/diversion_rules.py:431 msgid "Paste below" msgstr "Vložiť pod" #: lib/solaar/ui/diversion_rules.py:437 msgid "Paste rule here" msgstr "Vložiť pravidlo sem" #: lib/solaar/ui/diversion_rules.py:439 msgid "Paste rule above" msgstr "Vložiť pravidlo nad" #: lib/solaar/ui/diversion_rules.py:441 msgid "Paste rule below" msgstr "Vložiť pravidlo pod" #: lib/solaar/ui/diversion_rules.py:445 msgid "Paste rule" msgstr "Vložiť pravidlo" #: lib/solaar/ui/diversion_rules.py:474 msgid "Flatten" msgstr "Sploštiť" #: lib/solaar/ui/diversion_rules.py:507 msgid "Insert" msgstr "Vložiť" #: lib/solaar/ui/diversion_rules.py:510 lib/solaar/ui/diversion_rules.py:638 #: lib/solaar/ui/diversion_rules.py:1126 msgid "Or" msgstr "Alebo" #: lib/solaar/ui/diversion_rules.py:511 lib/solaar/ui/diversion_rules.py:637 #: lib/solaar/ui/diversion_rules.py:1111 msgid "And" msgstr "A" #: lib/solaar/ui/diversion_rules.py:513 msgid "Condition" msgstr "Podmienka" #: lib/solaar/ui/diversion_rules.py:515 lib/solaar/ui/diversion_rules.py:1292 msgid "Feature" msgstr "Vlastnosť" #: lib/solaar/ui/diversion_rules.py:516 lib/solaar/ui/diversion_rules.py:1328 msgid "Report" msgstr "Hlásenie" #: lib/solaar/ui/diversion_rules.py:517 lib/solaar/ui/diversion_rules.py:1204 msgid "Process" msgstr "Proces" #: lib/solaar/ui/diversion_rules.py:518 msgid "Mouse process" msgstr "" #: lib/solaar/ui/diversion_rules.py:519 lib/solaar/ui/diversion_rules.py:1366 msgid "Modifiers" msgstr "Modifikátory" #: lib/solaar/ui/diversion_rules.py:520 lib/solaar/ui/diversion_rules.py:1419 msgid "Key" msgstr "Kláves" #: lib/solaar/ui/diversion_rules.py:521 lib/solaar/ui/diversion_rules.py:1461 msgid "KeyIsDown" msgstr "" #: lib/solaar/ui/diversion_rules.py:522 lib/solaar/ui/diversion_rules.py:2260 msgid "Active" msgstr "Aktívne" #: lib/solaar/ui/diversion_rules.py:523 lib/solaar/ui/diversion_rules.py:2218 #: lib/solaar/ui/diversion_rules.py:2270 lib/solaar/ui/diversion_rules.py:2323 msgid "Device" msgstr "" #: lib/solaar/ui/diversion_rules.py:524 lib/solaar/ui/diversion_rules.py:2297 msgid "Host" msgstr "" #: lib/solaar/ui/diversion_rules.py:525 lib/solaar/ui/diversion_rules.py:2339 msgid "Setting" msgstr "" #: lib/solaar/ui/diversion_rules.py:526 lib/solaar/ui/diversion_rules.py:1477 #: lib/solaar/ui/diversion_rules.py:1526 msgid "Test" msgstr "Test" #: lib/solaar/ui/diversion_rules.py:527 lib/solaar/ui/diversion_rules.py:1643 msgid "Test bytes" msgstr "" #: lib/solaar/ui/diversion_rules.py:528 lib/solaar/ui/diversion_rules.py:1736 msgid "Mouse Gesture" msgstr "Gesto myši" #: lib/solaar/ui/diversion_rules.py:532 msgid "Action" msgstr "Akcia" #: lib/solaar/ui/diversion_rules.py:534 lib/solaar/ui/diversion_rules.py:1845 msgid "Key press" msgstr "Stlačenie klávesu" #: lib/solaar/ui/diversion_rules.py:535 lib/solaar/ui/diversion_rules.py:1897 msgid "Mouse scroll" msgstr "Skrolovanie kolieskom" #: lib/solaar/ui/diversion_rules.py:536 lib/solaar/ui/diversion_rules.py:1959 msgid "Mouse click" msgstr "Kliknutie myšou" #: lib/solaar/ui/diversion_rules.py:537 msgid "Set" msgstr "" #: lib/solaar/ui/diversion_rules.py:538 lib/solaar/ui/diversion_rules.py:2030 msgid "Execute" msgstr "Spustiť program" #: lib/solaar/ui/diversion_rules.py:539 lib/solaar/ui/diversion_rules.py:1158 msgid "Later" msgstr "" #: lib/solaar/ui/diversion_rules.py:568 msgid "Insert new rule" msgstr "Vložiť nové pravidlo" #: lib/solaar/ui/diversion_rules.py:588 lib/solaar/ui/diversion_rules.py:1686 #: lib/solaar/ui/diversion_rules.py:1790 lib/solaar/ui/diversion_rules.py:1989 msgid "Delete" msgstr "Odstrániť" #: lib/solaar/ui/diversion_rules.py:610 msgid "Negate" msgstr "Negovať" #: lib/solaar/ui/diversion_rules.py:634 msgid "Wrap with" msgstr "Obaliť čím" #: lib/solaar/ui/diversion_rules.py:656 msgid "Cut" msgstr "Vystrihnúť" #: lib/solaar/ui/diversion_rules.py:671 msgid "Paste" msgstr "Vložiť" #: lib/solaar/ui/diversion_rules.py:683 msgid "Copy" msgstr "Kopírovať" #: lib/solaar/ui/diversion_rules.py:1063 msgid "This editor does not support the selected rule component yet." msgstr "Vybraný komponent nie je editorom ešte podporovaný." #: lib/solaar/ui/diversion_rules.py:1138 msgid "Number of seconds to delay." msgstr "" #: lib/solaar/ui/diversion_rules.py:1177 msgid "Not" msgstr "Opak" #: lib/solaar/ui/diversion_rules.py:1187 msgid "X11 active process. For use in X11 only." msgstr "" #: lib/solaar/ui/diversion_rules.py:1218 msgid "X11 mouse process. For use in X11 only." msgstr "" #: lib/solaar/ui/diversion_rules.py:1235 msgid "MouseProcess" msgstr "ProcesMyši" #: lib/solaar/ui/diversion_rules.py:1260 msgid "Feature name of notification triggering rule processing." msgstr "" #: lib/solaar/ui/diversion_rules.py:1308 msgid "Report number of notification triggering rule processing." msgstr "" #: lib/solaar/ui/diversion_rules.py:1342 msgid "Active keyboard modifiers. Not always available in Wayland." msgstr "" #: lib/solaar/ui/diversion_rules.py:1383 msgid "Diverted key or button depressed or released.\n" "Use the Key/Button Diversion and Divert G Keys settings to divert " "keys and buttons." msgstr "" #: lib/solaar/ui/diversion_rules.py:1392 msgid "Key down" msgstr "" #: lib/solaar/ui/diversion_rules.py:1395 msgid "Key up" msgstr "" #: lib/solaar/ui/diversion_rules.py:1436 msgid "Diverted key or button is currently down.\n" "Use the Key/Button Diversion and Divert G Keys settings to divert " "keys and buttons." msgstr "" #: lib/solaar/ui/diversion_rules.py:1475 msgid "Test condition on notification triggering rule processing." msgstr "" #: lib/solaar/ui/diversion_rules.py:1479 msgid "Parameter" msgstr "" #: lib/solaar/ui/diversion_rules.py:1542 msgid "begin (inclusive)" msgstr "" #: lib/solaar/ui/diversion_rules.py:1543 msgid "end (exclusive)" msgstr "" #: lib/solaar/ui/diversion_rules.py:1552 msgid "range" msgstr "" #: lib/solaar/ui/diversion_rules.py:1554 msgid "minimum" msgstr "" #: lib/solaar/ui/diversion_rules.py:1555 msgid "maximum" msgstr "" #: lib/solaar/ui/diversion_rules.py:1557 #, python-format msgid "bytes %(0)d to %(1)d, ranging from %(2)d to %(3)d" msgstr "" #: lib/solaar/ui/diversion_rules.py:1562 msgid "mask" msgstr "" #: lib/solaar/ui/diversion_rules.py:1563 #, python-format msgid "bytes %(0)d to %(1)d, mask %(2)d" msgstr "" #: lib/solaar/ui/diversion_rules.py:1573 msgid "Bit or range test on bytes in notification message triggering rule " "processing." msgstr "" #: lib/solaar/ui/diversion_rules.py:1583 msgid "type" msgstr "" #: lib/solaar/ui/diversion_rules.py:1666 msgid "Mouse gesture with optional initiating button followed by zero or " "more mouse movements." msgstr "" #: lib/solaar/ui/diversion_rules.py:1671 msgid "Add movement" msgstr "" #: lib/solaar/ui/diversion_rules.py:1764 msgid "Simulate a chorded key click or depress or release.\n" "On Wayland requires write access to /dev/uinput." msgstr "" #: lib/solaar/ui/diversion_rules.py:1769 msgid "Add key" msgstr "Pridať kláves" #: lib/solaar/ui/diversion_rules.py:1772 msgid "Click" msgstr "" #: lib/solaar/ui/diversion_rules.py:1775 msgid "Depress" msgstr "" #: lib/solaar/ui/diversion_rules.py:1778 msgid "Release" msgstr "" #: lib/solaar/ui/diversion_rules.py:1861 msgid "Simulate a mouse scroll.\n" "On Wayland requires write access to /dev/uinput." msgstr "" #: lib/solaar/ui/diversion_rules.py:1918 msgid "Simulate a mouse click.\n" "On Wayland requires write access to /dev/uinput." msgstr "" #: lib/solaar/ui/diversion_rules.py:1921 msgid "Button" msgstr "Tlačidlo" #: lib/solaar/ui/diversion_rules.py:1922 msgid "Count and Action" msgstr "" #: lib/solaar/ui/diversion_rules.py:1972 msgid "Execute a command with arguments." msgstr "" #: lib/solaar/ui/diversion_rules.py:1975 msgid "Add argument" msgstr "Pridať argument" #: lib/solaar/ui/diversion_rules.py:2050 msgid "Toggle" msgstr "" #: lib/solaar/ui/diversion_rules.py:2050 msgid "True" msgstr "" #: lib/solaar/ui/diversion_rules.py:2051 msgid "False" msgstr "" #: lib/solaar/ui/diversion_rules.py:2065 msgid "Unsupported setting" msgstr "" #: lib/solaar/ui/diversion_rules.py:2223 lib/solaar/ui/diversion_rules.py:2242 #: lib/solaar/ui/diversion_rules.py:2328 lib/solaar/ui/diversion_rules.py:2570 #: lib/solaar/ui/diversion_rules.py:2588 msgid "Originating device" msgstr "" #: lib/solaar/ui/diversion_rules.py:2256 msgid "Device is active and its settings can be changed." msgstr "" #: lib/solaar/ui/diversion_rules.py:2266 msgid "Device that originated the current notification." msgstr "" #: lib/solaar/ui/diversion_rules.py:2280 msgid "Name of host computer." msgstr "" #: lib/solaar/ui/diversion_rules.py:2347 msgid "Value" msgstr "Hodnota" #: lib/solaar/ui/diversion_rules.py:2355 msgid "Item" msgstr "" #: lib/solaar/ui/diversion_rules.py:2630 msgid "Change setting on device" msgstr "" #: lib/solaar/ui/diversion_rules.py:2647 msgid "Setting on device" msgstr "" #: lib/solaar/ui/notify.py:124 lib/solaar/ui/tray.py:318 #: lib/solaar/ui/tray.py:323 lib/solaar/ui/window.py:739 msgid "offline" msgstr "offline" #: lib/solaar/ui/pair_window.py:122 lib/solaar/ui/pair_window.py:256 #: lib/solaar/ui/pair_window.py:288 #, python-format msgid "%(receiver_name)s: pair new device" msgstr "%(receiver_name)s: spárovať nové zariadenie" #: lib/solaar/ui/pair_window.py:123 #, python-format msgid "Enter passcode on %(name)s." msgstr "" #: lib/solaar/ui/pair_window.py:126 #, python-format msgid "Type %(passcode)s and then press the enter key." msgstr "" #: lib/solaar/ui/pair_window.py:129 msgid "left" msgstr "" #: lib/solaar/ui/pair_window.py:129 msgid "right" msgstr "" #: lib/solaar/ui/pair_window.py:131 #, python-format msgid "Press %(code)s\n" "and then press left and right buttons simultaneously." msgstr "" #: lib/solaar/ui/pair_window.py:188 msgid "Pairing failed" msgstr "Párovanie skončilo s chybou" #: lib/solaar/ui/pair_window.py:190 msgid "Make sure your device is within range, and has a decent battery " "charge." msgstr "Uistite sa, že je zariadenie v dosahu a je dostatočne nabité." #: lib/solaar/ui/pair_window.py:192 msgid "A new device was detected, but it is not compatible with this " "receiver." msgstr "Bolo zistené nové zariadenie, ale nie je kompatibilné s týmto " "prijímačom." #: lib/solaar/ui/pair_window.py:194 msgid "More paired devices than receiver can support." msgstr "Viac pripojených zariadení ako dokáže prijímač zvládnuť." #: lib/solaar/ui/pair_window.py:196 msgid "No further details are available about the error." msgstr "Ďalšie podrobnosti o chybe nie sú dostupné." #: lib/solaar/ui/pair_window.py:210 msgid "Found a new device:" msgstr "Nájdené nové zariadenie:" #: lib/solaar/ui/pair_window.py:235 msgid "The wireless link is not encrypted" msgstr "Bezdrôtové pripojenie nepoužíva šifrovanie" #: lib/solaar/ui/pair_window.py:264 msgid "Unifying receivers are only compatible with Unifying devices." msgstr "" #: lib/solaar/ui/pair_window.py:266 msgid "Bolt receivers are only compatible with Bolt devices." msgstr "" #: lib/solaar/ui/pair_window.py:268 msgid "Other receivers are only compatible with a few devices." msgstr "" #: lib/solaar/ui/pair_window.py:270 msgid "The device must not be paired with a nearby powered-on receiver." msgstr "" #: lib/solaar/ui/pair_window.py:274 msgid "Press a pairing button or key until the pairing light flashes " "quickly." msgstr "" #: lib/solaar/ui/pair_window.py:276 msgid "You may have to first turn the device off and on again." msgstr "" #: lib/solaar/ui/pair_window.py:278 msgid "Turn on the device you want to pair." msgstr "Zapnite zariadenie, ktoré chcete spárovať." #: lib/solaar/ui/pair_window.py:280 msgid "If the device is already turned on, turn it off and on again." msgstr "Ak je zariadenie už zapnuté, vypnite ho a opäť zapnite." #: lib/solaar/ui/pair_window.py:283 #, python-format msgid "\n" "\n" "This receiver has %d pairing remaining." msgid_plural "\n" "\n" "This receiver has %d pairings remaining." msgstr[0] "\n" "\n" "Tomuto prijímaču ostáva ešte %d párovanie." msgstr[1] "\n" "\n" "Tomuto prijímaču ostáva ešte %d párovania." msgstr[2] "\n" "\n" "Tomuto prijímaču ostáva ešte %d párovaní." #: lib/solaar/ui/pair_window.py:286 msgid "\n" "Cancelling at this point will not use up a pairing." msgstr "\n" "Ak proces teraz zrušíte, spárovanie sa nespotrebuje." #: lib/solaar/ui/tray.py:58 msgid "No supported device found" msgstr "" #: lib/solaar/ui/tray.py:64 lib/solaar/ui/window.py:319 #, python-format msgid "About %s" msgstr "O programe %s" #: lib/solaar/ui/tray.py:65 lib/solaar/ui/window.py:317 #, python-format msgid "Quit %s" msgstr "Ukončiť %s" #: lib/solaar/ui/tray.py:297 lib/solaar/ui/tray.py:305 msgid "no receiver" msgstr "bez prijímača" #: lib/solaar/ui/tray.py:321 msgid "no status" msgstr "žiaden stav" #: lib/solaar/ui/window.py:96 msgid "Scanning" msgstr "Hľadá sa" #: lib/solaar/ui/window.py:129 msgid "Battery" msgstr "Batéria" #: lib/solaar/ui/window.py:132 msgid "Wireless Link" msgstr "Bezdrôtové prepojenie" #: lib/solaar/ui/window.py:136 msgid "Lighting" msgstr "Osvetlenie" #: lib/solaar/ui/window.py:170 msgid "Show Technical Details" msgstr "Zobraziť technické podrobnosti" #: lib/solaar/ui/window.py:186 msgid "Pair new device" msgstr "Spárovať nové zariadenie" #: lib/solaar/ui/window.py:205 msgid "Select a device" msgstr "Vyberte zariadenie" #: lib/solaar/ui/window.py:322 msgid "Rule Editor" msgstr "Editor pravidiel" #: lib/solaar/ui/window.py:533 msgid "Path" msgstr "Cesta" #: lib/solaar/ui/window.py:536 msgid "USB ID" msgstr "USB ID" #: lib/solaar/ui/window.py:539 lib/solaar/ui/window.py:541 #: lib/solaar/ui/window.py:561 lib/solaar/ui/window.py:563 msgid "Serial" msgstr "Sériové číslo" #: lib/solaar/ui/window.py:545 msgid "Index" msgstr "Index" #: lib/solaar/ui/window.py:547 msgid "Wireless PID" msgstr "Bezdrôt. PID" #: lib/solaar/ui/window.py:549 msgid "Product ID" msgstr "Identifikátor produktu" #: lib/solaar/ui/window.py:551 msgid "Protocol" msgstr "Protokol" #: lib/solaar/ui/window.py:551 msgid "Unknown" msgstr "Neznámy" #: lib/solaar/ui/window.py:554 #, python-format msgid "%(rate)d ms (%(rate_hz)dHz)" msgstr "%(rate)d ms (%(rate_hz)dHz)" #: lib/solaar/ui/window.py:554 msgid "Polling rate" msgstr "Intenzita dopytovania" #: lib/solaar/ui/window.py:565 msgid "Unit ID" msgstr "Číslo (ID) jednotky" #: lib/solaar/ui/window.py:576 msgid "none" msgstr "žiadne" #: lib/solaar/ui/window.py:577 msgid "Notifications" msgstr "Notifikácie" #: lib/solaar/ui/window.py:621 msgid "No device paired." msgstr "Žiadne spárované zariadenie." #: lib/solaar/ui/window.py:628 #, python-format msgid "Up to %(max_count)s device can be paired to this receiver." msgid_plural "Up to %(max_count)s devices can be paired to this receiver." msgstr[0] "K tomuto prijímaču môžete pripojiť %(max_count)s zariadenie." msgstr[1] "K tomuto prijímaču môžete pripojiť až %(max_count)s " "zariadenia." msgstr[2] "K tomuto prijímaču môžete pripojiť až %(max_count)s " "zariadení." #: lib/solaar/ui/window.py:634 msgid "Only one device can be paired to this receiver." msgstr "K tomuto prijímaču sa dá pripojiť len jedno zariadenie." #: lib/solaar/ui/window.py:638 #, python-format msgid "This receiver has %d pairing remaining." msgid_plural "This receiver has %d pairings remaining." msgstr[0] "Tomuto prijímaču ostáva %d párovanie." msgstr[1] "Tomuto prijímaču ostávajú %d párovania." msgstr[2] "Tomuto prijímaču ostáva %d párovaní." #: lib/solaar/ui/window.py:692 msgid "Battery Voltage" msgstr "Napätie batérie" #: lib/solaar/ui/window.py:694 msgid "Voltage reported by battery" msgstr "Napätie hlásené batériou" #: lib/solaar/ui/window.py:696 msgid "Battery Level" msgstr "Úroveň batérie" #: lib/solaar/ui/window.py:698 msgid "Approximate level reported by battery" msgstr "Približná úroveň hlásená batériou" #: lib/solaar/ui/window.py:705 lib/solaar/ui/window.py:707 msgid "next reported " msgstr "ďalšie hlásené " #: lib/solaar/ui/window.py:708 msgid " and next level to be reported." msgstr " a ďalšia úroveň, ktorá bude hlásená." #: lib/solaar/ui/window.py:713 msgid "last known" msgstr "naposledy známa" #: lib/solaar/ui/window.py:724 msgid "encrypted" msgstr "šifrované" #: lib/solaar/ui/window.py:726 msgid "The wireless link between this device and its receiver is encrypted." msgstr "Bezdrôtové spojenie medzi zariadením a prijímačom je šifrované." #: lib/solaar/ui/window.py:728 msgid "not encrypted" msgstr "bez šifrovania" #: lib/solaar/ui/window.py:732 msgid "The wireless link between this device and its receiver is not " "encrypted.\n" "This is a security issue for pointing devices, and a major security " "issue for text-input devices." msgstr "" #: lib/solaar/ui/window.py:748 #, python-format msgid "%(light_level)d lux" msgstr "%(light_level)d lux" #~ msgid "About" #~ msgstr "O programe" #~ msgid "Add action" #~ msgstr "Pridať akciu" #~ msgid "Adjust the DPI by sliding the mouse horizontally while " #~ "holding the DPI button." #~ msgstr "Upraviť DPI pohybom myši vo vodorovnom smere pri súčasnom " #~ "držaní tlačidla DPI." #~ msgid "Adjust the DPI by sliding the mouse horizontally while " #~ "holding the button down." #~ msgstr "Úprava DPI pohybom myši vo vodorovnom smere pri súčasnom " #~ "držaní tlačidla." #~ msgid "Automatically switch the mouse wheel between ratchet and " #~ "freespin mode.\n" #~ "The mouse wheel is always free at 0, and always ratcheted at 50" #~ msgstr "Automaticky prepnúť režim kolieska myši medzi krokovaním a " #~ "voľným otáčaním.\n" #~ "Koliesko je pri hodnote 0 vždy voľné a pri hodnote 50 vždy krokuje" #~ msgid "Battery information unknown." #~ msgstr "Informácie o batérii nie sú známe." #~ msgid "Click to allow changes." #~ msgstr "Kliknite pre povolenie zmien." #~ msgid "Click to prevent changes." #~ msgstr "Kliknite pre zakázanie zmien." #~ msgid "Count" #~ msgstr "Počet" #~ msgid "DPI Sliding Adjustment" #~ msgstr "Úprava DPI pomocou myši" #~ msgid "ERROR: " #~ msgstr "CHYBA: " #~ msgid "Effectively turns off thumb scrolling in Linux." #~ msgstr "Vypne skrolovanie palcom v Linuxe." #~ msgid "Effectively turns off wheel scrolling in Linux." #~ msgstr "Vypne skrolovanie kolieskom myši v Linuxe." #~ msgid "For more information see the Solaar installation directions\n" #~ "at https://pwr-solaar.github.io/Solaar/installation" #~ msgstr "Ďalšie informácie o inštalácii Solaaru nájdete na\n" #~ "https://pwr-solaar.github.io/Solaar/installation" #, python-format #~ msgid "Found a Logitech Receiver (%s), but did not have permission " #~ "to open it." #~ msgstr "Nájdený prijímač Logitech (%s), ale chýbajú práva na jeho " #~ "otvorenie." #~ msgid "HID++ mode for horizontal scroll with the thumb wheel." #~ msgstr "HID++ režim pre vodorovný posun pomocou kolieska." #~ msgid "HID++ mode for vertical scroll with the wheel." #~ msgstr "HID++ režim pre vertikálne skrolovanie myši." #~ msgid "If the device is already turned on, turn if off and on again." #~ msgstr "Ak je zariadenie už zapnuté, vypnite ho a znovu zapnite." #~ msgid "If you've just installed Solaar, try removing the receiver " #~ "and plugging it back in." #~ msgstr "Ak ste Solaar práve nainštalovali, skúste prijímač odpojiť a " #~ "znova pripojiť." #~ msgid "Make the key or button send HID++ notifications (which " #~ "trigger Solaar rules but are otherwise ignored)." #~ msgstr "Kláves alebo tlačidlo budú zasielať HID++ notifikácie (tie " #~ "aktivujú Solaar pravidlá, ale inak budú ignorované)." #~ msgid "No Logitech receiver found" #~ msgstr "Nebol nájdený žiaden prijímač Logitech" #~ msgid "Scroll Wheel HID++ Scrolling" #~ msgstr "HID++ skrolovanie" #~ msgid "Scroll Wheel Rachet" #~ msgstr "Krokovanie kolieska" #~ msgid "Send a gesture by sliding the mouse while holding the button " #~ "down." #~ msgstr "Pošlite gesto pohybom myši pri súčasnom držaní tlačidla." #~ msgid "Shows status of devices connected\n" #~ "through wireless Logitech receivers." #~ msgstr "Zobrazí stav zariadení pripojených\n" #~ "pomocou bezdrôtového prijímača Logitech." #~ msgid "Solaar depends on a udev file that is not present" #~ msgstr "Solaar závisí na udev súbore, ktorý chýba" #~ msgid "The wireless link between this device and its receiver is " #~ "not encrypted.\n" #~ "\n" #~ "For pointing devices (mice, trackballs, trackpads), this is a minor " #~ "security issue.\n" #~ "\n" #~ "It is, however, a major security issue for text-input devices " #~ "(keyboards, numpads),\n" #~ "because typed text can be sniffed inconspicuously by 3rd parties " #~ "within range." #~ msgstr "Bezdrôtové spojenie medzi zariadením a prijímačom nie je " #~ "šifrované.\n" #~ "\n" #~ "Pri ukazovacích zariadeniach (myši, trackbally, trackpady) to " #~ "nepredstavuje veľký bezpečnostný problém.\n" #~ "\n" #~ "Avšak pri vstupných zariadeniach zadávajúcich text (klávesnica, " #~ "numerická \n" #~ "klávesnica) to predstavuje veľké riziko z hľadiska bezpečnosti, " #~ "pretože \n" #~ "písaný text môže byť nepozorovane zachytený treťou stranou, ak je " #~ "signál v jej dosahu." #~ msgid "Thumb Wheel HID++ Scrolling" #~ msgstr "HID++ posun kolieskom" #~ msgid "Top-most coordinate." #~ msgstr "Najvyššia súradnica." #~ msgid "Try removing the device and plugging it back in or turning " #~ "it off and then on." #~ msgstr "Skúste zariadenie odpojiť a znovu ho pripojiť alebo ho " #~ "skúste vypnúť a opätovne zapnúť." #~ msgid "height" #~ msgstr "výška" #~ msgid "top" #~ msgstr "hore" #~ msgid "unknown" #~ msgstr "neznáme" #~ msgid "width" #~ msgstr "šírka" Solaar-1.1.14/po/solaar.pot000066400000000000000000001334741473525213700154670ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the solaar package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "Project-Id-Version: solaar 1.1.13\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2024-07-01 13:49+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" #: lib/logitech_receiver/base_usb.py:45 msgid "Bolt Receiver" msgstr "" #: lib/logitech_receiver/base_usb.py:57 msgid "Unifying Receiver" msgstr "" #: lib/logitech_receiver/base_usb.py:68 lib/logitech_receiver/base_usb.py:80 #: lib/logitech_receiver/base_usb.py:93 lib/logitech_receiver/base_usb.py:106 #: lib/logitech_receiver/base_usb.py:119 msgid "Nano Receiver" msgstr "" #: lib/logitech_receiver/base_usb.py:131 msgid "Lightspeed Receiver" msgstr "" #: lib/logitech_receiver/base_usb.py:141 msgid "EX100 Receiver 27 Mhz" msgstr "" #: lib/logitech_receiver/common.py:610 #, python-format msgid "Battery: %(level)s (%(status)s)" msgstr "" #: lib/logitech_receiver/common.py:613 #, python-format msgid "Battery: %(percent)d%% (%(status)s)" msgstr "" #: lib/logitech_receiver/hidpp20.py:921 #: lib/logitech_receiver/settings_templates.py:283 msgid "Disabled" msgstr "" #: lib/logitech_receiver/hidpp20.py:922 msgid "Static" msgstr "" #: lib/logitech_receiver/hidpp20.py:923 msgid "Pulse" msgstr "" #: lib/logitech_receiver/hidpp20.py:924 msgid "Cycle" msgstr "" #: lib/logitech_receiver/hidpp20.py:925 msgid "Boot" msgstr "" #: lib/logitech_receiver/hidpp20.py:926 msgid "Demo" msgstr "" #: lib/logitech_receiver/hidpp20.py:927 msgid "Breathe" msgstr "" #: lib/logitech_receiver/hidpp20.py:928 msgid "Ripple" msgstr "" #: lib/logitech_receiver/hidpp20.py:929 msgid "Decomposition" msgstr "" #: lib/logitech_receiver/hidpp20.py:930 msgid "Signature1" msgstr "" #: lib/logitech_receiver/hidpp20.py:931 msgid "Signature2" msgstr "" #: lib/logitech_receiver/hidpp20.py:932 msgid "CycleS" msgstr "" #: lib/logitech_receiver/hidpp20.py:996 msgid "Unknown Location" msgstr "" #: lib/logitech_receiver/hidpp20.py:997 msgid "Primary" msgstr "" #: lib/logitech_receiver/hidpp20.py:998 msgid "Logo" msgstr "" #: lib/logitech_receiver/hidpp20.py:999 msgid "Left Side" msgstr "" #: lib/logitech_receiver/hidpp20.py:1000 msgid "Right Side" msgstr "" #: lib/logitech_receiver/hidpp20.py:1001 msgid "Combined" msgstr "" #: lib/logitech_receiver/hidpp20.py:1002 msgid "Primary 1" msgstr "" #: lib/logitech_receiver/hidpp20.py:1003 msgid "Primary 2" msgstr "" #: lib/logitech_receiver/hidpp20.py:1004 msgid "Primary 3" msgstr "" #: lib/logitech_receiver/hidpp20.py:1005 msgid "Primary 4" msgstr "" #: lib/logitech_receiver/hidpp20.py:1006 msgid "Primary 5" msgstr "" #: lib/logitech_receiver/hidpp20.py:1007 msgid "Primary 6" msgstr "" #: lib/logitech_receiver/i18n.py:28 msgid "empty" msgstr "" #: lib/logitech_receiver/i18n.py:29 msgid "critical" msgstr "" #: lib/logitech_receiver/i18n.py:30 msgid "low" msgstr "" #: lib/logitech_receiver/i18n.py:31 msgid "average" msgstr "" #: lib/logitech_receiver/i18n.py:32 msgid "good" msgstr "" #: lib/logitech_receiver/i18n.py:33 msgid "full" msgstr "" #: lib/logitech_receiver/i18n.py:35 msgid "discharging" msgstr "" #: lib/logitech_receiver/i18n.py:36 msgid "recharging" msgstr "" #: lib/logitech_receiver/i18n.py:37 msgid "charging" msgstr "" #: lib/logitech_receiver/i18n.py:38 msgid "not charging" msgstr "" #: lib/logitech_receiver/i18n.py:39 msgid "almost full" msgstr "" #: lib/logitech_receiver/i18n.py:40 msgid "charged" msgstr "" #: lib/logitech_receiver/i18n.py:41 msgid "slow recharge" msgstr "" #: lib/logitech_receiver/i18n.py:42 msgid "invalid battery" msgstr "" #: lib/logitech_receiver/i18n.py:43 msgid "thermal error" msgstr "" #: lib/logitech_receiver/i18n.py:44 msgid "error" msgstr "" #: lib/logitech_receiver/i18n.py:45 msgid "standard" msgstr "" #: lib/logitech_receiver/i18n.py:46 msgid "fast" msgstr "" #: lib/logitech_receiver/i18n.py:47 msgid "slow" msgstr "" #: lib/logitech_receiver/i18n.py:49 msgid "device timeout" msgstr "" #: lib/logitech_receiver/i18n.py:50 msgid "device not supported" msgstr "" #: lib/logitech_receiver/i18n.py:51 msgid "too many devices" msgstr "" #: lib/logitech_receiver/i18n.py:52 msgid "sequence timeout" msgstr "" #: lib/logitech_receiver/i18n.py:54 msgid "Firmware" msgstr "" #: lib/logitech_receiver/i18n.py:55 msgid "Bootloader" msgstr "" #: lib/logitech_receiver/i18n.py:56 msgid "Hardware" msgstr "" #: lib/logitech_receiver/i18n.py:57 msgid "Other" msgstr "" #: lib/logitech_receiver/i18n.py:59 msgid "Left Button" msgstr "" #: lib/logitech_receiver/i18n.py:60 msgid "Right Button" msgstr "" #: lib/logitech_receiver/i18n.py:61 msgid "Middle Button" msgstr "" #: lib/logitech_receiver/i18n.py:62 msgid "Back Button" msgstr "" #: lib/logitech_receiver/i18n.py:63 msgid "Forward Button" msgstr "" #: lib/logitech_receiver/i18n.py:64 msgid "Mouse Gesture Button" msgstr "" #: lib/logitech_receiver/i18n.py:65 msgid "Smart Shift" msgstr "" #: lib/logitech_receiver/i18n.py:66 msgid "DPI Switch" msgstr "" #: lib/logitech_receiver/i18n.py:67 msgid "Left Tilt" msgstr "" #: lib/logitech_receiver/i18n.py:68 msgid "Right Tilt" msgstr "" #: lib/logitech_receiver/i18n.py:69 msgid "Left Click" msgstr "" #: lib/logitech_receiver/i18n.py:70 msgid "Right Click" msgstr "" #: lib/logitech_receiver/i18n.py:71 msgid "Mouse Middle Button" msgstr "" #: lib/logitech_receiver/i18n.py:72 msgid "Mouse Back Button" msgstr "" #: lib/logitech_receiver/i18n.py:73 msgid "Mouse Forward Button" msgstr "" #: lib/logitech_receiver/i18n.py:74 msgid "Gesture Button Navigation" msgstr "" #: lib/logitech_receiver/i18n.py:75 msgid "Mouse Scroll Left Button" msgstr "" #: lib/logitech_receiver/i18n.py:76 msgid "Mouse Scroll Right Button" msgstr "" #: lib/logitech_receiver/i18n.py:78 msgid "pressed" msgstr "" #: lib/logitech_receiver/i18n.py:79 msgid "released" msgstr "" #: lib/logitech_receiver/notifications.py:64 #: lib/logitech_receiver/notifications.py:116 msgid "pairing lock is closed" msgstr "" #: lib/logitech_receiver/notifications.py:64 #: lib/logitech_receiver/notifications.py:116 msgid "pairing lock is open" msgstr "" #: lib/logitech_receiver/notifications.py:81 msgid "discovery lock is closed" msgstr "" #: lib/logitech_receiver/notifications.py:81 msgid "discovery lock is open" msgstr "" #: lib/logitech_receiver/notifications.py:210 lib/solaar/listener.py:238 msgid "connected" msgstr "" #: lib/logitech_receiver/notifications.py:210 lib/solaar/listener.py:238 msgid "disconnected" msgstr "" #: lib/logitech_receiver/notifications.py:236 msgid "unpaired" msgstr "" #: lib/logitech_receiver/notifications.py:283 msgid "powered on" msgstr "" #: lib/logitech_receiver/receiver.py:371 msgid "No paired devices." msgstr "" #: lib/logitech_receiver/receiver.py:373 lib/solaar/ui/window.py:604 #, python-format msgid "%(count)s paired device." msgid_plural "%(count)s paired devices." msgstr[0] "" msgstr[1] "" #: lib/logitech_receiver/settings.py:611 msgid "register" msgstr "" #: lib/logitech_receiver/settings.py:625 lib/logitech_receiver/settings.py:651 msgid "feature" msgstr "" #: lib/logitech_receiver/settings_templates.py:122 msgid "Swap Fx function" msgstr "" #: lib/logitech_receiver/settings_templates.py:125 msgid "When set, the F1..F12 keys will activate their special function,\n" "and you must hold the FN key to activate their standard function." msgstr "" #: lib/logitech_receiver/settings_templates.py:130 msgid "When unset, the F1..F12 keys will activate their standard function,\n" "and you must hold the FN key to activate their special function." msgstr "" #: lib/logitech_receiver/settings_templates.py:138 msgid "Hand Detection" msgstr "" #: lib/logitech_receiver/settings_templates.py:139 msgid "Turn on illumination when the hands hover over the keyboard." msgstr "" #: lib/logitech_receiver/settings_templates.py:146 msgid "Scroll Wheel Smooth Scrolling" msgstr "" #: lib/logitech_receiver/settings_templates.py:147 #: lib/logitech_receiver/settings_templates.py:394 #: lib/logitech_receiver/settings_templates.py:423 msgid "High-sensitivity mode for vertical scroll with the wheel." msgstr "" #: lib/logitech_receiver/settings_templates.py:154 msgid "Side Scrolling" msgstr "" #: lib/logitech_receiver/settings_templates.py:156 msgid "When disabled, pushing the wheel sideways sends custom button " "events\n" "instead of the standard side-scrolling events." msgstr "" #: lib/logitech_receiver/settings_templates.py:166 msgid "Sensitivity (DPI - older mice)" msgstr "" #: lib/logitech_receiver/settings_templates.py:167 #: lib/logitech_receiver/settings_templates.py:955 #: lib/logitech_receiver/settings_templates.py:983 msgid "Mouse movement sensitivity" msgstr "" #: lib/logitech_receiver/settings_templates.py:240 msgid "Backlight Timed" msgstr "" #: lib/logitech_receiver/settings_templates.py:241 #: lib/logitech_receiver/settings_templates.py:381 msgid "Set illumination time for keyboard." msgstr "" #: lib/logitech_receiver/settings_templates.py:252 msgid "Backlight" msgstr "" #: lib/logitech_receiver/settings_templates.py:253 msgid "Illumination level on keyboard. Changes made are only applied in " "Manual mode." msgstr "" #: lib/logitech_receiver/settings_templates.py:285 msgid "Automatic" msgstr "" #: lib/logitech_receiver/settings_templates.py:287 msgid "Manual" msgstr "" #: lib/logitech_receiver/settings_templates.py:289 msgid "Enabled" msgstr "" #: lib/logitech_receiver/settings_templates.py:295 msgid "Backlight Level" msgstr "" #: lib/logitech_receiver/settings_templates.py:296 msgid "Illumination level on keyboard when in Manual mode." msgstr "" #: lib/logitech_receiver/settings_templates.py:353 msgid "Backlight Delay Hands Out" msgstr "" #: lib/logitech_receiver/settings_templates.py:354 msgid "Delay in seconds until backlight fades out with hands away from " "keyboard." msgstr "" #: lib/logitech_receiver/settings_templates.py:362 msgid "Backlight Delay Hands In" msgstr "" #: lib/logitech_receiver/settings_templates.py:363 msgid "Delay in seconds until backlight fades out with hands near keyboard." msgstr "" #: lib/logitech_receiver/settings_templates.py:371 msgid "Backlight Delay Powered" msgstr "" #: lib/logitech_receiver/settings_templates.py:372 msgid "Delay in seconds until backlight fades out with external power." msgstr "" #: lib/logitech_receiver/settings_templates.py:380 msgid "Backlight (Seconds)" msgstr "" #: lib/logitech_receiver/settings_templates.py:392 msgid "Scroll Wheel High Resolution" msgstr "" #: lib/logitech_receiver/settings_templates.py:396 #: lib/logitech_receiver/settings_templates.py:425 msgid "Set to ignore if scrolling is abnormally fast or slow" msgstr "" #: lib/logitech_receiver/settings_templates.py:403 #: lib/logitech_receiver/settings_templates.py:434 msgid "Scroll Wheel Diversion" msgstr "" #: lib/logitech_receiver/settings_templates.py:405 msgid "Make scroll wheel send LOWRES_WHEEL HID++ notifications (which " "trigger Solaar rules but are otherwise ignored)." msgstr "" #: lib/logitech_receiver/settings_templates.py:412 msgid "Scroll Wheel Direction" msgstr "" #: lib/logitech_receiver/settings_templates.py:413 msgid "Invert direction for vertical scroll with wheel." msgstr "" #: lib/logitech_receiver/settings_templates.py:421 msgid "Scroll Wheel Resolution" msgstr "" #: lib/logitech_receiver/settings_templates.py:436 msgid "Make scroll wheel send HIRES_WHEEL HID++ notifications (which " "trigger Solaar rules but are otherwise ignored)." msgstr "" #: lib/logitech_receiver/settings_templates.py:445 msgid "Sensitivity (Pointer Speed)" msgstr "" #: lib/logitech_receiver/settings_templates.py:446 msgid "Speed multiplier for mouse (256 is normal multiplier)." msgstr "" #: lib/logitech_receiver/settings_templates.py:456 msgid "Thumb Wheel Diversion" msgstr "" #: lib/logitech_receiver/settings_templates.py:458 msgid "Make thumb wheel send THUMB_WHEEL HID++ notifications (which trigger " "Solaar rules but are otherwise ignored)." msgstr "" #: lib/logitech_receiver/settings_templates.py:467 msgid "Thumb Wheel Direction" msgstr "" #: lib/logitech_receiver/settings_templates.py:468 msgid "Invert thumb wheel scroll direction." msgstr "" #: lib/logitech_receiver/settings_templates.py:488 msgid "Onboard Profiles" msgstr "" #: lib/logitech_receiver/settings_templates.py:489 msgid "Enable an onboard profile, which controls report rate, sensitivity, " "and button actions" msgstr "" #: lib/logitech_receiver/settings_templates.py:533 #: lib/logitech_receiver/settings_templates.py:566 msgid "Report Rate" msgstr "" #: lib/logitech_receiver/settings_templates.py:535 #: lib/logitech_receiver/settings_templates.py:568 msgid "Frequency of device movement reports" msgstr "" #: lib/logitech_receiver/settings_templates.py:535 #: lib/logitech_receiver/settings_templates.py:568 #: lib/logitech_receiver/settings_templates.py:983 #: lib/logitech_receiver/settings_templates.py:1357 #: lib/logitech_receiver/settings_templates.py:1388 msgid "May need Onboard Profiles set to Disable to be effective." msgstr "" #: lib/logitech_receiver/settings_templates.py:596 msgid "Divert crown events" msgstr "" #: lib/logitech_receiver/settings_templates.py:597 msgid "Make crown send CROWN HID++ notifications (which trigger Solaar " "rules but are otherwise ignored)." msgstr "" #: lib/logitech_receiver/settings_templates.py:605 msgid "Crown smooth scroll" msgstr "" #: lib/logitech_receiver/settings_templates.py:606 msgid "Set crown smooth scroll" msgstr "" #: lib/logitech_receiver/settings_templates.py:614 msgid "Divert G and M Keys" msgstr "" #: lib/logitech_receiver/settings_templates.py:615 msgid "Make G and M keys send HID++ notifications (which trigger Solaar " "rules but are otherwise ignored)." msgstr "" #: lib/logitech_receiver/settings_templates.py:629 msgid "Scroll Wheel Ratcheted" msgstr "" #: lib/logitech_receiver/settings_templates.py:630 msgid "Switch the mouse wheel between speed-controlled ratcheting and " "always freespin." msgstr "" #: lib/logitech_receiver/settings_templates.py:632 msgid "Freespinning" msgstr "" #: lib/logitech_receiver/settings_templates.py:632 msgid "Ratcheted" msgstr "" #: lib/logitech_receiver/settings_templates.py:639 msgid "Scroll Wheel Ratchet Speed" msgstr "" #: lib/logitech_receiver/settings_templates.py:641 msgid "Use the mouse wheel speed to switch between ratcheted and " "freespinning.\n" "The mouse wheel is always ratcheted at 50." msgstr "" #: lib/logitech_receiver/settings_templates.py:690 msgid "Key/Button Actions" msgstr "" #: lib/logitech_receiver/settings_templates.py:692 msgid "Change the action for the key or button." msgstr "" #: lib/logitech_receiver/settings_templates.py:694 msgid "Overridden by diversion." msgstr "" #: lib/logitech_receiver/settings_templates.py:696 msgid "Changing important actions (such as for the left mouse button) can " "result in an unusable system." msgstr "" #: lib/logitech_receiver/settings_templates.py:862 msgid "Key/Button Diversion" msgstr "" #: lib/logitech_receiver/settings_templates.py:863 msgid "Make the key or button send HID++ notifications (Diverted) or " "initiate Mouse Gestures or Sliding DPI" msgstr "" #: lib/logitech_receiver/settings_templates.py:866 #: lib/logitech_receiver/settings_templates.py:867 #: lib/logitech_receiver/settings_templates.py:868 msgid "Diverted" msgstr "" #: lib/logitech_receiver/settings_templates.py:866 #: lib/logitech_receiver/settings_templates.py:867 msgid "Mouse Gestures" msgstr "" #: lib/logitech_receiver/settings_templates.py:866 #: lib/logitech_receiver/settings_templates.py:867 #: lib/logitech_receiver/settings_templates.py:868 msgid "Regular" msgstr "" #: lib/logitech_receiver/settings_templates.py:866 msgid "Sliding DPI" msgstr "" #: lib/logitech_receiver/settings_templates.py:954 #: lib/logitech_receiver/settings_templates.py:982 msgid "Sensitivity (DPI)" msgstr "" #: lib/logitech_receiver/settings_templates.py:1059 msgid "Sensitivity Switching" msgstr "" #: lib/logitech_receiver/settings_templates.py:1061 msgid "Switch the current sensitivity and the remembered sensitivity when " "the key or button is pressed.\n" "If there is no remembered sensitivity, just remember the current " "sensitivity" msgstr "" #: lib/logitech_receiver/settings_templates.py:1065 msgid "Off" msgstr "" #: lib/logitech_receiver/settings_templates.py:1096 msgid "Disable keys" msgstr "" #: lib/logitech_receiver/settings_templates.py:1097 msgid "Disable specific keyboard keys." msgstr "" #: lib/logitech_receiver/settings_templates.py:1100 #, python-format msgid "Disables the %s key." msgstr "" #: lib/logitech_receiver/settings_templates.py:1113 #: lib/logitech_receiver/settings_templates.py:1170 msgid "Set OS" msgstr "" #: lib/logitech_receiver/settings_templates.py:1114 #: lib/logitech_receiver/settings_templates.py:1171 msgid "Change keys to match OS." msgstr "" #: lib/logitech_receiver/settings_templates.py:1183 msgid "Change Host" msgstr "" #: lib/logitech_receiver/settings_templates.py:1184 msgid "Switch connection to a different host" msgstr "" #: lib/logitech_receiver/settings_templates.py:1208 msgid "Performs a left click." msgstr "" #: lib/logitech_receiver/settings_templates.py:1208 msgid "Single tap" msgstr "" #: lib/logitech_receiver/settings_templates.py:1209 msgid "Performs a right click." msgstr "" #: lib/logitech_receiver/settings_templates.py:1209 msgid "Single tap with two fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:1210 msgid "Single tap with three fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:1214 msgid "Double tap" msgstr "" #: lib/logitech_receiver/settings_templates.py:1214 msgid "Performs a double click." msgstr "" #: lib/logitech_receiver/settings_templates.py:1215 msgid "Double tap with two fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:1216 msgid "Double tap with three fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:1219 msgid "Drags items by dragging the finger after double tapping." msgstr "" #: lib/logitech_receiver/settings_templates.py:1219 msgid "Tap and drag" msgstr "" #: lib/logitech_receiver/settings_templates.py:1221 msgid "Tap and drag with two fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:1222 msgid "Drags items by dragging the fingers after double tapping." msgstr "" #: lib/logitech_receiver/settings_templates.py:1224 msgid "Tap and drag with three fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:1227 msgid "Suppress tap and edge gestures" msgstr "" #: lib/logitech_receiver/settings_templates.py:1228 msgid "Disables tap and edge gestures (equivalent to pressing Fn+LeftClick)." msgstr "" #: lib/logitech_receiver/settings_templates.py:1230 msgid "Scroll with one finger" msgstr "" #: lib/logitech_receiver/settings_templates.py:1230 #: lib/logitech_receiver/settings_templates.py:1231 #: lib/logitech_receiver/settings_templates.py:1234 msgid "Scrolls." msgstr "" #: lib/logitech_receiver/settings_templates.py:1231 #: lib/logitech_receiver/settings_templates.py:1234 msgid "Scroll with two fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:1232 msgid "Scroll horizontally with two fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:1232 msgid "Scrolls horizontally." msgstr "" #: lib/logitech_receiver/settings_templates.py:1233 msgid "Scroll vertically with two fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:1233 msgid "Scrolls vertically." msgstr "" #: lib/logitech_receiver/settings_templates.py:1235 msgid "Inverts the scrolling direction." msgstr "" #: lib/logitech_receiver/settings_templates.py:1235 msgid "Natural scrolling" msgstr "" #: lib/logitech_receiver/settings_templates.py:1236 msgid "Enables the thumbwheel." msgstr "" #: lib/logitech_receiver/settings_templates.py:1236 msgid "Thumbwheel" msgstr "" #: lib/logitech_receiver/settings_templates.py:1247 #: lib/logitech_receiver/settings_templates.py:1251 msgid "Swipe from the top edge" msgstr "" #: lib/logitech_receiver/settings_templates.py:1248 msgid "Swipe from the left edge" msgstr "" #: lib/logitech_receiver/settings_templates.py:1249 msgid "Swipe from the right edge" msgstr "" #: lib/logitech_receiver/settings_templates.py:1250 msgid "Swipe from the bottom edge" msgstr "" #: lib/logitech_receiver/settings_templates.py:1252 msgid "Swipe two fingers from the left edge" msgstr "" #: lib/logitech_receiver/settings_templates.py:1253 msgid "Swipe two fingers from the right edge" msgstr "" #: lib/logitech_receiver/settings_templates.py:1254 msgid "Swipe two fingers from the bottom edge" msgstr "" #: lib/logitech_receiver/settings_templates.py:1255 msgid "Swipe two fingers from the top edge" msgstr "" #: lib/logitech_receiver/settings_templates.py:1256 #: lib/logitech_receiver/settings_templates.py:1260 msgid "Pinch to zoom out; spread to zoom in." msgstr "" #: lib/logitech_receiver/settings_templates.py:1256 msgid "Zoom with two fingers." msgstr "" #: lib/logitech_receiver/settings_templates.py:1257 msgid "Pinch to zoom out." msgstr "" #: lib/logitech_receiver/settings_templates.py:1258 msgid "Spread to zoom in." msgstr "" #: lib/logitech_receiver/settings_templates.py:1259 msgid "Zoom with three fingers." msgstr "" #: lib/logitech_receiver/settings_templates.py:1260 msgid "Zoom with two fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:1278 msgid "Pixel zone" msgstr "" #: lib/logitech_receiver/settings_templates.py:1279 msgid "Ratio zone" msgstr "" #: lib/logitech_receiver/settings_templates.py:1280 msgid "Scale factor" msgstr "" #: lib/logitech_receiver/settings_templates.py:1280 msgid "Sets the cursor speed." msgstr "" #: lib/logitech_receiver/settings_templates.py:1284 msgid "Left" msgstr "" #: lib/logitech_receiver/settings_templates.py:1284 msgid "Left-most coordinate." msgstr "" #: lib/logitech_receiver/settings_templates.py:1285 msgid "Bottom" msgstr "" #: lib/logitech_receiver/settings_templates.py:1285 msgid "Bottom coordinate." msgstr "" #: lib/logitech_receiver/settings_templates.py:1286 msgid "Width" msgstr "" #: lib/logitech_receiver/settings_templates.py:1286 msgid "Width." msgstr "" #: lib/logitech_receiver/settings_templates.py:1287 msgid "Height" msgstr "" #: lib/logitech_receiver/settings_templates.py:1287 msgid "Height." msgstr "" #: lib/logitech_receiver/settings_templates.py:1288 msgid "Cursor speed." msgstr "" #: lib/logitech_receiver/settings_templates.py:1288 msgid "Scale" msgstr "" #: lib/logitech_receiver/settings_templates.py:1294 msgid "Gestures" msgstr "" #: lib/logitech_receiver/settings_templates.py:1295 msgid "Tweak the mouse/touchpad behaviour." msgstr "" #: lib/logitech_receiver/settings_templates.py:1311 msgid "Gestures Diversion" msgstr "" #: lib/logitech_receiver/settings_templates.py:1312 msgid "Divert mouse/touchpad gestures." msgstr "" #: lib/logitech_receiver/settings_templates.py:1328 msgid "Gesture params" msgstr "" #: lib/logitech_receiver/settings_templates.py:1329 msgid "Change numerical parameters of a mouse/touchpad." msgstr "" #: lib/logitech_receiver/settings_templates.py:1353 msgid "M-Key LEDs" msgstr "" #: lib/logitech_receiver/settings_templates.py:1355 msgid "Control the M-Key LEDs." msgstr "" #: lib/logitech_receiver/settings_templates.py:1359 #: lib/logitech_receiver/settings_templates.py:1390 msgid "May need G Keys diverted to be effective." msgstr "" #: lib/logitech_receiver/settings_templates.py:1365 #, python-format msgid "Lights up the %s key." msgstr "" #: lib/logitech_receiver/settings_templates.py:1384 msgid "MR-Key LED" msgstr "" #: lib/logitech_receiver/settings_templates.py:1386 msgid "Control the MR-Key LED." msgstr "" #: lib/logitech_receiver/settings_templates.py:1407 msgid "Persistent Key/Button Mapping" msgstr "" #: lib/logitech_receiver/settings_templates.py:1409 msgid "Permanently change the mapping for the key or button." msgstr "" #: lib/logitech_receiver/settings_templates.py:1411 msgid "Changing important keys or buttons (such as for the left mouse " "button) can result in an unusable system." msgstr "" #: lib/logitech_receiver/settings_templates.py:1468 msgid "Sidetone" msgstr "" #: lib/logitech_receiver/settings_templates.py:1469 msgid "Set sidetone level." msgstr "" #: lib/logitech_receiver/settings_templates.py:1478 msgid "Equalizer" msgstr "" #: lib/logitech_receiver/settings_templates.py:1479 msgid "Set equalizer levels." msgstr "" #: lib/logitech_receiver/settings_templates.py:1501 msgid "Hz" msgstr "" #: lib/logitech_receiver/settings_templates.py:1507 msgid "Power Management" msgstr "" #: lib/logitech_receiver/settings_templates.py:1508 msgid "Power off in minutes (0 for never)." msgstr "" #: lib/logitech_receiver/settings_templates.py:1519 msgid "Brightness Control" msgstr "" #: lib/logitech_receiver/settings_templates.py:1520 msgid "Control overall brightness" msgstr "" #: lib/logitech_receiver/settings_templates.py:1563 #: lib/logitech_receiver/settings_templates.py:1617 msgid "LED Control" msgstr "" #: lib/logitech_receiver/settings_templates.py:1564 #: lib/logitech_receiver/settings_templates.py:1618 msgid "Switch control of LED zones between device and Solaar" msgstr "" #: lib/logitech_receiver/settings_templates.py:1579 #: lib/logitech_receiver/settings_templates.py:1628 msgid "LED Zone Effects" msgstr "" #: lib/logitech_receiver/settings_templates.py:1580 #: lib/logitech_receiver/settings_templates.py:1629 msgid "LED Control needs to be set to Solaar to be effective." msgstr "" #: lib/logitech_receiver/settings_templates.py:1580 #: lib/logitech_receiver/settings_templates.py:1629 msgid "Set effect for LED Zone" msgstr "" #: lib/logitech_receiver/settings_templates.py:1583 msgid "Speed" msgstr "" #: lib/logitech_receiver/settings_templates.py:1584 msgid "Period" msgstr "" #: lib/logitech_receiver/settings_templates.py:1585 msgid "Intensity" msgstr "" #: lib/logitech_receiver/settings_templates.py:1586 msgid "Ramp" msgstr "" #: lib/logitech_receiver/settings_templates.py:1602 msgid "LEDs" msgstr "" #: lib/logitech_receiver/settings_templates.py:1639 msgid "Per-key Lighting" msgstr "" #: lib/logitech_receiver/settings_templates.py:1640 msgid "Control per-key lighting." msgstr "" #: lib/solaar/ui/__init__.py:103 msgid "Another Solaar process is already running so just expose its window" msgstr "" #: lib/solaar/ui/about.py:34 msgid "Manages Logitech receivers,\n" "keyboards, mice, and tablets." msgstr "" #: lib/solaar/ui/about.py:43 msgid "Additional Programming" msgstr "" #: lib/solaar/ui/about.py:44 msgid "GUI design" msgstr "" #: lib/solaar/ui/about.py:46 msgid "Testing" msgstr "" #: lib/solaar/ui/about.py:54 msgid "Logitech documentation" msgstr "" #: lib/solaar/ui/action.py:83 lib/solaar/ui/action.py:87 #: lib/solaar/ui/window.py:189 msgid "Unpair" msgstr "" #: lib/solaar/ui/action.py:86 lib/solaar/ui/diversion_rules.py:103 msgid "Cancel" msgstr "" #: lib/solaar/ui/common.py:35 msgid "Permissions error" msgstr "" #: lib/solaar/ui/common.py:37 #, python-format msgid "Found a Logitech receiver or device (%s), but did not have " "permission to open it." msgstr "" #: lib/solaar/ui/common.py:39 msgid "If you've just installed Solaar, try disconnecting the receiver or " "device and then reconnecting it." msgstr "" #: lib/solaar/ui/common.py:42 msgid "Cannot connect to device error" msgstr "" #: lib/solaar/ui/common.py:44 #, python-format msgid "Found a Logitech receiver or device at %s, but encountered an error " "connecting to it." msgstr "" #: lib/solaar/ui/common.py:46 msgid "Try disconnecting the device and then reconnecting it or turning it " "off and then on." msgstr "" #: lib/solaar/ui/common.py:49 msgid "Unpairing failed" msgstr "" #: lib/solaar/ui/common.py:51 #, python-brace-format msgid "Failed to unpair %{device} from %{receiver}." msgstr "" #: lib/solaar/ui/common.py:53 msgid "The receiver returned an error, with no further details." msgstr "" #: lib/solaar/ui/config_panel.py:228 msgid "Complete - ENTER to change" msgstr "" #: lib/solaar/ui/config_panel.py:228 msgid "Incomplete" msgstr "" #: lib/solaar/ui/config_panel.py:470 lib/solaar/ui/config_panel.py:522 #, python-format msgid "%d value" msgid_plural "%d values" msgstr[0] "" msgstr[1] "" #: lib/solaar/ui/config_panel.py:603 msgid "Changes allowed" msgstr "" #: lib/solaar/ui/config_panel.py:604 msgid "No changes allowed" msgstr "" #: lib/solaar/ui/config_panel.py:605 msgid "Ignore this setting" msgstr "" #: lib/solaar/ui/config_panel.py:649 msgid "Working" msgstr "" #: lib/solaar/ui/config_panel.py:652 msgid "Read/write operation failed." msgstr "" #: lib/solaar/ui/diversion_rules.py:65 msgid "Built-in rules" msgstr "" #: lib/solaar/ui/diversion_rules.py:65 msgid "User-defined rules" msgstr "" #: lib/solaar/ui/diversion_rules.py:67 lib/solaar/ui/diversion_rules.py:1033 msgid "Rule" msgstr "" #: lib/solaar/ui/diversion_rules.py:68 lib/solaar/ui/diversion_rules.py:545 #: lib/solaar/ui/diversion_rules.py:672 msgid "Sub-rule" msgstr "" #: lib/solaar/ui/diversion_rules.py:70 msgid "[empty]" msgstr "" #: lib/solaar/ui/diversion_rules.py:94 msgid "Make changes permanent?" msgstr "" #: lib/solaar/ui/diversion_rules.py:99 msgid "Yes" msgstr "" #: lib/solaar/ui/diversion_rules.py:101 msgid "No" msgstr "" #: lib/solaar/ui/diversion_rules.py:106 msgid "If you choose No, changes will be lost when Solaar is closed." msgstr "" #: lib/solaar/ui/diversion_rules.py:167 msgid "Solaar Rule Editor" msgstr "" #: lib/solaar/ui/diversion_rules.py:260 msgid "Save changes" msgstr "" #: lib/solaar/ui/diversion_rules.py:265 msgid "Discard changes" msgstr "" #: lib/solaar/ui/diversion_rules.py:404 msgid "Insert here" msgstr "" #: lib/solaar/ui/diversion_rules.py:406 msgid "Insert above" msgstr "" #: lib/solaar/ui/diversion_rules.py:408 msgid "Insert below" msgstr "" #: lib/solaar/ui/diversion_rules.py:414 msgid "Insert new rule here" msgstr "" #: lib/solaar/ui/diversion_rules.py:416 msgid "Insert new rule above" msgstr "" #: lib/solaar/ui/diversion_rules.py:418 msgid "Insert new rule below" msgstr "" #: lib/solaar/ui/diversion_rules.py:463 msgid "Paste here" msgstr "" #: lib/solaar/ui/diversion_rules.py:465 msgid "Paste above" msgstr "" #: lib/solaar/ui/diversion_rules.py:467 msgid "Paste below" msgstr "" #: lib/solaar/ui/diversion_rules.py:473 msgid "Paste rule here" msgstr "" #: lib/solaar/ui/diversion_rules.py:475 msgid "Paste rule above" msgstr "" #: lib/solaar/ui/diversion_rules.py:477 msgid "Paste rule below" msgstr "" #: lib/solaar/ui/diversion_rules.py:481 msgid "Paste rule" msgstr "" #: lib/solaar/ui/diversion_rules.py:510 msgid "Flatten" msgstr "" #: lib/solaar/ui/diversion_rules.py:543 msgid "Insert" msgstr "" #: lib/solaar/ui/diversion_rules.py:546 lib/solaar/ui/diversion_rules.py:674 #: lib/solaar/ui/diversion_rules.py:1065 msgid "Or" msgstr "" #: lib/solaar/ui/diversion_rules.py:547 lib/solaar/ui/diversion_rules.py:673 #: lib/solaar/ui/diversion_rules.py:1051 msgid "And" msgstr "" #: lib/solaar/ui/diversion_rules.py:549 msgid "Condition" msgstr "" #: lib/solaar/ui/diversion_rules.py:551 lib/solaar/ui/rule_conditions.py:146 msgid "Feature" msgstr "" #: lib/solaar/ui/diversion_rules.py:552 lib/solaar/ui/rule_conditions.py:181 msgid "Report" msgstr "" #: lib/solaar/ui/diversion_rules.py:553 lib/solaar/ui/rule_conditions.py:60 msgid "Process" msgstr "" #: lib/solaar/ui/diversion_rules.py:554 msgid "Mouse process" msgstr "" #: lib/solaar/ui/diversion_rules.py:555 lib/solaar/ui/rule_conditions.py:218 msgid "Modifiers" msgstr "" #: lib/solaar/ui/diversion_rules.py:556 lib/solaar/ui/rule_conditions.py:270 msgid "Key" msgstr "" #: lib/solaar/ui/diversion_rules.py:557 lib/solaar/ui/rule_conditions.py:311 msgid "KeyIsDown" msgstr "" #: lib/solaar/ui/diversion_rules.py:558 lib/solaar/ui/diversion_rules.py:1359 msgid "Active" msgstr "" #: lib/solaar/ui/diversion_rules.py:559 lib/solaar/ui/diversion_rules.py:1316 #: lib/solaar/ui/diversion_rules.py:1368 lib/solaar/ui/diversion_rules.py:1418 msgid "Device" msgstr "" #: lib/solaar/ui/diversion_rules.py:560 lib/solaar/ui/diversion_rules.py:1394 msgid "Host" msgstr "" #: lib/solaar/ui/diversion_rules.py:561 lib/solaar/ui/diversion_rules.py:1436 msgid "Setting" msgstr "" #: lib/solaar/ui/diversion_rules.py:562 lib/solaar/ui/rule_conditions.py:326 #: lib/solaar/ui/rule_conditions.py:375 msgid "Test" msgstr "" #: lib/solaar/ui/diversion_rules.py:563 lib/solaar/ui/rule_conditions.py:500 msgid "Test bytes" msgstr "" #: lib/solaar/ui/diversion_rules.py:564 lib/solaar/ui/rule_conditions.py:601 msgid "Mouse Gesture" msgstr "" #: lib/solaar/ui/diversion_rules.py:568 msgid "Action" msgstr "" #: lib/solaar/ui/diversion_rules.py:570 lib/solaar/ui/rule_actions.py:132 msgid "Key press" msgstr "" #: lib/solaar/ui/diversion_rules.py:571 lib/solaar/ui/rule_actions.py:183 msgid "Mouse scroll" msgstr "" #: lib/solaar/ui/diversion_rules.py:572 lib/solaar/ui/rule_actions.py:244 msgid "Mouse click" msgstr "" #: lib/solaar/ui/diversion_rules.py:573 msgid "Set" msgstr "" #: lib/solaar/ui/diversion_rules.py:574 lib/solaar/ui/rule_actions.py:314 msgid "Execute" msgstr "" #: lib/solaar/ui/diversion_rules.py:575 lib/solaar/ui/diversion_rules.py:1096 msgid "Later" msgstr "" #: lib/solaar/ui/diversion_rules.py:604 msgid "Insert new rule" msgstr "" #: lib/solaar/ui/diversion_rules.py:624 lib/solaar/ui/rule_actions.py:74 #: lib/solaar/ui/rule_actions.py:273 lib/solaar/ui/rule_conditions.py:548 msgid "Delete" msgstr "" #: lib/solaar/ui/diversion_rules.py:646 msgid "Negate" msgstr "" #: lib/solaar/ui/diversion_rules.py:670 msgid "Wrap with" msgstr "" #: lib/solaar/ui/diversion_rules.py:692 msgid "Cut" msgstr "" #: lib/solaar/ui/diversion_rules.py:707 msgid "Paste" msgstr "" #: lib/solaar/ui/diversion_rules.py:713 msgid "Copy" msgstr "" #: lib/solaar/ui/diversion_rules.py:1014 msgid "This editor does not support the selected rule component yet." msgstr "" #: lib/solaar/ui/diversion_rules.py:1076 msgid "Number of seconds to delay. Delay between 0 and 1 is done with " "higher precision." msgstr "" #: lib/solaar/ui/diversion_rules.py:1114 msgid "Not" msgstr "" #: lib/solaar/ui/diversion_rules.py:1145 msgid "Toggle" msgstr "" #: lib/solaar/ui/diversion_rules.py:1146 msgid "True" msgstr "" #: lib/solaar/ui/diversion_rules.py:1147 msgid "False" msgstr "" #: lib/solaar/ui/diversion_rules.py:1160 msgid "Unsupported setting" msgstr "" #: lib/solaar/ui/diversion_rules.py:1322 lib/solaar/ui/diversion_rules.py:1342 #: lib/solaar/ui/diversion_rules.py:1424 lib/solaar/ui/diversion_rules.py:1669 #: lib/solaar/ui/diversion_rules.py:1687 msgid "Originating device" msgstr "" #: lib/solaar/ui/diversion_rules.py:1355 msgid "Device is active and its settings can be changed." msgstr "" #: lib/solaar/ui/diversion_rules.py:1364 msgid "Device that originated the current notification." msgstr "" #: lib/solaar/ui/diversion_rules.py:1377 msgid "Name of host computer." msgstr "" #: lib/solaar/ui/diversion_rules.py:1445 msgid "Value" msgstr "" #: lib/solaar/ui/diversion_rules.py:1454 msgid "Item" msgstr "" #: lib/solaar/ui/diversion_rules.py:1728 msgid "Change setting on device" msgstr "" #: lib/solaar/ui/diversion_rules.py:1744 msgid "Setting on device" msgstr "" #: lib/solaar/ui/notify.py:115 msgid "unspecified reason" msgstr "" #: lib/solaar/ui/pair_window.py:37 lib/solaar/ui/pair_window.py:155 #, python-format msgid "%(receiver_name)s: pair new device" msgstr "" #: lib/solaar/ui/pair_window.py:39 msgid "Bolt receivers are only compatible with Bolt devices." msgstr "" #: lib/solaar/ui/pair_window.py:41 msgid "Press a pairing button or key until the pairing light flashes " "quickly." msgstr "" #: lib/solaar/ui/pair_window.py:44 msgid "Unifying receivers are only compatible with Unifying devices." msgstr "" #: lib/solaar/ui/pair_window.py:46 msgid "Other receivers are only compatible with a few devices." msgstr "" #: lib/solaar/ui/pair_window.py:48 msgid "Turn on the device you want to pair." msgstr "" #: lib/solaar/ui/pair_window.py:49 msgid "The device must not be paired with a nearby powered-on receiver." msgstr "" #: lib/solaar/ui/pair_window.py:51 msgid "If the device is already turned on, turn it off and on again." msgstr "" #: lib/solaar/ui/pair_window.py:55 #, python-format msgid "\n" "\n" "This receiver has %d pairing remaining." msgid_plural "\n" "\n" "This receiver has %d pairings remaining." msgstr[0] "" msgstr[1] "" #: lib/solaar/ui/pair_window.py:61 msgid "\n" "Cancelling at this point will not use up a pairing." msgstr "" #: lib/solaar/ui/pair_window.py:156 #, python-format msgid "Enter passcode on %(name)s." msgstr "" #: lib/solaar/ui/pair_window.py:159 #, python-format msgid "Type %(passcode)s and then press the enter key." msgstr "" #: lib/solaar/ui/pair_window.py:162 msgid "left" msgstr "" #: lib/solaar/ui/pair_window.py:162 msgid "right" msgstr "" #: lib/solaar/ui/pair_window.py:164 #, python-format msgid "Press %(code)s\n" "and then press left and right buttons simultaneously." msgstr "" #: lib/solaar/ui/pair_window.py:195 msgid "The wireless link is not encrypted" msgstr "" #: lib/solaar/ui/pair_window.py:200 msgid "Found a new device:" msgstr "" #: lib/solaar/ui/pair_window.py:221 msgid "Pairing failed" msgstr "" #: lib/solaar/ui/pair_window.py:223 msgid "Make sure your device is within range, and has a decent battery " "charge." msgstr "" #: lib/solaar/ui/pair_window.py:225 msgid "A new device was detected, but it is not compatible with this " "receiver." msgstr "" #: lib/solaar/ui/pair_window.py:227 msgid "More paired devices than receiver can support." msgstr "" #: lib/solaar/ui/pair_window.py:229 msgid "No further details are available about the error." msgstr "" #: lib/solaar/ui/rule_actions.py:48 msgid "Simulate a chorded key click or depress or release.\n" "On Wayland requires write access to /dev/uinput." msgstr "" #: lib/solaar/ui/rule_actions.py:53 msgid "Add key" msgstr "" #: lib/solaar/ui/rule_actions.py:56 msgid "Click" msgstr "" #: lib/solaar/ui/rule_actions.py:59 msgid "Depress" msgstr "" #: lib/solaar/ui/rule_actions.py:62 msgid "Release" msgstr "" #: lib/solaar/ui/rule_actions.py:147 msgid "Simulate a mouse scroll.\n" "On Wayland requires write access to /dev/uinput." msgstr "" #: lib/solaar/ui/rule_actions.py:203 msgid "Simulate a mouse click.\n" "On Wayland requires write access to /dev/uinput." msgstr "" #: lib/solaar/ui/rule_actions.py:206 msgid "Button" msgstr "" #: lib/solaar/ui/rule_actions.py:207 msgid "Count and Action" msgstr "" #: lib/solaar/ui/rule_actions.py:256 msgid "Execute a command with arguments." msgstr "" #: lib/solaar/ui/rule_actions.py:259 msgid "Add argument" msgstr "" #: lib/solaar/ui/rule_conditions.py:43 msgid "X11 active process. For use in X11 only." msgstr "" #: lib/solaar/ui/rule_conditions.py:73 msgid "X11 mouse process. For use in X11 only." msgstr "" #: lib/solaar/ui/rule_conditions.py:90 msgid "MouseProcess" msgstr "" #: lib/solaar/ui/rule_conditions.py:114 msgid "Feature name of notification triggering rule processing." msgstr "" #: lib/solaar/ui/rule_conditions.py:161 msgid "Report number of notification triggering rule processing." msgstr "" #: lib/solaar/ui/rule_conditions.py:194 msgid "Active keyboard modifiers. Not always available in Wayland." msgstr "" #: lib/solaar/ui/rule_conditions.py:234 msgid "Diverted key or button depressed or released.\n" "Use the Key/Button Diversion and Divert G Keys settings to divert " "keys and buttons." msgstr "" #: lib/solaar/ui/rule_conditions.py:243 msgid "Key down" msgstr "" #: lib/solaar/ui/rule_conditions.py:246 msgid "Key up" msgstr "" #: lib/solaar/ui/rule_conditions.py:286 msgid "Diverted key or button is currently down.\n" "Use the Key/Button Diversion and Divert G Keys settings to divert " "keys and buttons." msgstr "" #: lib/solaar/ui/rule_conditions.py:324 msgid "Test condition on notification triggering rule processing." msgstr "" #: lib/solaar/ui/rule_conditions.py:328 msgid "Parameter" msgstr "" #: lib/solaar/ui/rule_conditions.py:401 msgid "begin (inclusive)" msgstr "" #: lib/solaar/ui/rule_conditions.py:402 msgid "end (exclusive)" msgstr "" #: lib/solaar/ui/rule_conditions.py:410 msgid "range" msgstr "" #: lib/solaar/ui/rule_conditions.py:413 msgid "minimum" msgstr "" #: lib/solaar/ui/rule_conditions.py:414 msgid "maximum" msgstr "" #: lib/solaar/ui/rule_conditions.py:416 #, python-format msgid "bytes %(0)d to %(1)d, ranging from %(2)d to %(3)d" msgstr "" #: lib/solaar/ui/rule_conditions.py:419 lib/solaar/ui/rule_conditions.py:420 msgid "mask" msgstr "" #: lib/solaar/ui/rule_conditions.py:421 #, python-format msgid "bytes %(0)d to %(1)d, mask %(2)d" msgstr "" #: lib/solaar/ui/rule_conditions.py:430 msgid "Bit or range test on bytes in notification message triggering rule " "processing." msgstr "" #: lib/solaar/ui/rule_conditions.py:440 msgid "type" msgstr "" #: lib/solaar/ui/rule_conditions.py:528 msgid "Mouse gesture with optional initiating button followed by zero or " "more mouse movements." msgstr "" #: lib/solaar/ui/rule_conditions.py:533 msgid "Add movement" msgstr "" #: lib/solaar/ui/tray.py:50 msgid "No supported device found" msgstr "" #: lib/solaar/ui/tray.py:55 lib/solaar/ui/window.py:310 #, python-format msgid "About %s" msgstr "" #: lib/solaar/ui/tray.py:56 lib/solaar/ui/window.py:308 #, python-format msgid "Quit %s" msgstr "" #: lib/solaar/ui/tray.py:267 lib/solaar/ui/tray.py:275 msgid "no receiver" msgstr "" #: lib/solaar/ui/tray.py:288 lib/solaar/ui/tray.py:293 msgid "offline" msgstr "" #: lib/solaar/ui/tray.py:291 msgid "no status" msgstr "" #: lib/solaar/ui/window.py:91 msgid "Scanning" msgstr "" #: lib/solaar/ui/window.py:122 msgid "Battery" msgstr "" #: lib/solaar/ui/window.py:125 msgid "Wireless Link" msgstr "" #: lib/solaar/ui/window.py:129 msgid "Lighting" msgstr "" #: lib/solaar/ui/window.py:163 msgid "Show Technical Details" msgstr "" #: lib/solaar/ui/window.py:179 msgid "Pair new device" msgstr "" #: lib/solaar/ui/window.py:197 msgid "Select a device" msgstr "" #: lib/solaar/ui/window.py:313 msgid "Rule Editor" msgstr "" #: lib/solaar/ui/window.py:517 msgid "Path" msgstr "" #: lib/solaar/ui/window.py:520 msgid "USB ID" msgstr "" #: lib/solaar/ui/window.py:523 lib/solaar/ui/window.py:525 #: lib/solaar/ui/window.py:540 lib/solaar/ui/window.py:542 msgid "Serial" msgstr "" #: lib/solaar/ui/window.py:529 msgid "Index" msgstr "" #: lib/solaar/ui/window.py:531 msgid "Wireless PID" msgstr "" #: lib/solaar/ui/window.py:533 msgid "Product ID" msgstr "" #: lib/solaar/ui/window.py:535 msgid "Protocol" msgstr "" #: lib/solaar/ui/window.py:535 msgid "Unknown" msgstr "" #: lib/solaar/ui/window.py:537 msgid "Polling rate" msgstr "" #: lib/solaar/ui/window.py:544 msgid "Unit ID" msgstr "" #: lib/solaar/ui/window.py:558 msgid "Notifications" msgstr "" #: lib/solaar/ui/window.py:602 msgid "No device paired." msgstr "" #: lib/solaar/ui/window.py:611 #, python-format msgid "Up to %(max_count)s device can be paired to this receiver." msgid_plural "Up to %(max_count)s devices can be paired to this receiver." msgstr[0] "" msgstr[1] "" #: lib/solaar/ui/window.py:622 #, python-format msgid "This receiver has %d pairing remaining." msgid_plural "This receiver has %d pairings remaining." msgstr[0] "" msgstr[1] "" #: lib/solaar/ui/window.py:679 msgid "Battery Voltage" msgstr "" #: lib/solaar/ui/window.py:681 msgid "Voltage reported by battery" msgstr "" #: lib/solaar/ui/window.py:683 msgid "Battery Level" msgstr "" #: lib/solaar/ui/window.py:685 msgid "Approximate level reported by battery" msgstr "" #: lib/solaar/ui/window.py:692 lib/solaar/ui/window.py:694 msgid "next reported " msgstr "" #: lib/solaar/ui/window.py:695 msgid " and next level to be reported." msgstr "" #: lib/solaar/ui/window.py:711 msgid "encrypted" msgstr "" #: lib/solaar/ui/window.py:713 msgid "The wireless link between this device and its receiver is encrypted." msgstr "" #: lib/solaar/ui/window.py:715 msgid "not encrypted" msgstr "" #: lib/solaar/ui/window.py:719 msgid "The wireless link between this device and its receiver is not " "encrypted.\n" "This is a security issue for pointing devices, and a major security " "issue for text-input devices." msgstr "" #: lib/solaar/ui/window.py:735 #, python-format msgid "%(light_level)d lux" msgstr "" Solaar-1.1.14/po/sr.po000066400000000000000000001547131473525213700144450ustar00rootroot00000000000000# Language rs translations for solaar package. # Copyright (C) 2022 THE solaar'S COPYRIGHT HOLDER # This file is distributed under the same license as the solaar package. # Automatically generated, 2022. # msgid "" msgstr "Project-Id-Version: solaar 1.1.4\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-12-28 17:40+0100\n" "PO-Revision-Date: 2022-08-09 17:35+0200\n" "Last-Translator: Automatically generated\n" "Language-Team: github.com/renatoka\n" "Language: sr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" #: lib/logitech_receiver/base_usb.py:46 msgid "Bolt Receiver" msgstr "Bolt prijemnik" #: lib/logitech_receiver/base_usb.py:57 msgid "Unifying Receiver" msgstr "Unifying prijemnik" #: lib/logitech_receiver/base_usb.py:67 lib/logitech_receiver/base_usb.py:78 #: lib/logitech_receiver/base_usb.py:90 lib/logitech_receiver/base_usb.py:102 #: lib/logitech_receiver/base_usb.py:114 msgid "Nano Receiver" msgstr "Nano prijemnik" #: lib/logitech_receiver/base_usb.py:124 msgid "Lightspeed Receiver" msgstr "Lightspeed prijemnik" #: lib/logitech_receiver/base_usb.py:133 msgid "EX100 Receiver 27 Mhz" msgstr "EX100 prijemnik 27 MHZ" #: lib/logitech_receiver/i18n.py:30 msgid "empty" msgstr "prazna" #: lib/logitech_receiver/i18n.py:31 msgid "critical" msgstr "kritično" #: lib/logitech_receiver/i18n.py:32 msgid "low" msgstr "slabo" #: lib/logitech_receiver/i18n.py:33 msgid "average" msgstr "prosečna" #: lib/logitech_receiver/i18n.py:34 msgid "good" msgstr "dobra" #: lib/logitech_receiver/i18n.py:35 msgid "full" msgstr "puna" #: lib/logitech_receiver/i18n.py:38 msgid "discharging" msgstr "pražnjenje" #: lib/logitech_receiver/i18n.py:39 msgid "recharging" msgstr "punjenje" #: lib/logitech_receiver/i18n.py:40 lib/solaar/ui/window.py:711 msgid "charging" msgstr "punjenje" #: lib/logitech_receiver/i18n.py:41 msgid "not charging" msgstr "ne puni se" #: lib/logitech_receiver/i18n.py:42 msgid "almost full" msgstr "skoro puna" #: lib/logitech_receiver/i18n.py:43 msgid "charged" msgstr "napunjeno" #: lib/logitech_receiver/i18n.py:44 msgid "slow recharge" msgstr "sporo punjenje" #: lib/logitech_receiver/i18n.py:45 msgid "invalid battery" msgstr "neispravna baterija" #: lib/logitech_receiver/i18n.py:46 msgid "thermal error" msgstr "toplotna greška" #: lib/logitech_receiver/i18n.py:47 msgid "error" msgstr "greška" #: lib/logitech_receiver/i18n.py:48 msgid "standard" msgstr "standardno" #: lib/logitech_receiver/i18n.py:49 msgid "fast" msgstr "brzo" #: lib/logitech_receiver/i18n.py:50 msgid "slow" msgstr "sporo" #: lib/logitech_receiver/i18n.py:53 msgid "device timeout" msgstr "istek čekanja uređaja" #: lib/logitech_receiver/i18n.py:54 msgid "device not supported" msgstr "uređaj nije podržan" #: lib/logitech_receiver/i18n.py:55 msgid "too many devices" msgstr "previše uređaja" #: lib/logitech_receiver/i18n.py:56 msgid "sequence timeout" msgstr "istek vremenske sekvence" #: lib/logitech_receiver/i18n.py:59 lib/solaar/ui/window.py:572 msgid "Firmware" msgstr "Firmver" #: lib/logitech_receiver/i18n.py:60 msgid "Bootloader" msgstr "Bootloader" #: lib/logitech_receiver/i18n.py:61 msgid "Hardware" msgstr "Hardver" #: lib/logitech_receiver/i18n.py:62 msgid "Other" msgstr "Ostalo" #: lib/logitech_receiver/i18n.py:65 msgid "Left Button" msgstr "Levo dugme" #: lib/logitech_receiver/i18n.py:66 msgid "Right Button" msgstr "Desno dugme" #: lib/logitech_receiver/i18n.py:67 msgid "Middle Button" msgstr "Srednje dugme" #: lib/logitech_receiver/i18n.py:68 msgid "Back Button" msgstr "Dugme za unazad" #: lib/logitech_receiver/i18n.py:69 msgid "Forward Button" msgstr "Dugme za unapred" #: lib/logitech_receiver/i18n.py:70 msgid "Mouse Gesture Button" msgstr "Dugme za pokrete miša" #: lib/logitech_receiver/i18n.py:71 msgid "Smart Shift" msgstr "Pametan pomak" #: lib/logitech_receiver/i18n.py:72 msgid "DPI Switch" msgstr "DPI svitch" #: lib/logitech_receiver/i18n.py:73 msgid "Left Tilt" msgstr "Levi nagib" #: lib/logitech_receiver/i18n.py:74 msgid "Right Tilt" msgstr "Desni nagib" #: lib/logitech_receiver/i18n.py:75 msgid "Left Click" msgstr "Levi lik" #: lib/logitech_receiver/i18n.py:76 msgid "Right Click" msgstr "Desni klik" #: lib/logitech_receiver/i18n.py:77 msgid "Mouse Middle Button" msgstr "Srednje dugme miša" #: lib/logitech_receiver/i18n.py:78 msgid "Mouse Back Button" msgstr "Dugme za povratak na mišu" #: lib/logitech_receiver/i18n.py:79 msgid "Mouse Forward Button" msgstr "Dugme za unapred na mišu" #: lib/logitech_receiver/i18n.py:80 msgid "Gesture Button Navigation" msgstr "Navigacija pomoću dugmadi" #: lib/logitech_receiver/i18n.py:81 msgid "Mouse Scroll Left Button" msgstr "Levo dugme za pomeranje miša" #: lib/logitech_receiver/i18n.py:82 msgid "Mouse Scroll Right Button" msgstr "Desno dugme za pomeranje miša" #: lib/logitech_receiver/i18n.py:85 msgid "pressed" msgstr "pritisnuto" #: lib/logitech_receiver/i18n.py:86 msgid "released" msgstr "otpušteno" #: lib/logitech_receiver/notifications.py:75 #: lib/logitech_receiver/notifications.py:126 msgid "pairing lock is closed" msgstr "uparivanje je zatvoreno" #: lib/logitech_receiver/notifications.py:75 #: lib/logitech_receiver/notifications.py:126 msgid "pairing lock is open" msgstr "uparivanje je otvoreno" #: lib/logitech_receiver/notifications.py:92 msgid "discovery lock is closed" msgstr "otkrivanje je otvoreno" #: lib/logitech_receiver/notifications.py:92 msgid "discovery lock is open" msgstr "otkrivanje je zatvoreno" #: lib/logitech_receiver/notifications.py:224 lib/solaar/ui/notify.py:122 msgid "connected" msgstr "povezano" #: lib/logitech_receiver/notifications.py:224 msgid "disconnected" msgstr "odspojeno" #: lib/logitech_receiver/notifications.py:262 lib/solaar/ui/notify.py:120 msgid "unpaired" msgstr "nije upareno" #: lib/logitech_receiver/notifications.py:304 msgid "powered on" msgstr "uključen" #: lib/logitech_receiver/settings.py:750 msgid "register" msgstr "registriraj se" #: lib/logitech_receiver/settings.py:764 lib/logitech_receiver/settings.py:791 msgid "feature" msgstr "značajka" #: lib/logitech_receiver/settings_templates.py:139 msgid "Swap Fx function" msgstr "Zameni Fx funkciju" #: lib/logitech_receiver/settings_templates.py:140 msgid "When set, the F1..F12 keys will activate their special function,\n" "and you must hold the FN key to activate their standard function." msgstr "Kada su podešeni, tasteri F1..F12 će aktivirati svoju posebnu " "funkciju,\n" "i morate držati FN taster da biste aktivirali njihovu standardnu " "funkciju." #: lib/logitech_receiver/settings_templates.py:142 msgid "When unset, the F1..F12 keys will activate their standard function,\n" "and you must hold the FN key to activate their special function." msgstr "Kada nisu omogućeni, tasteri F1..F12 će aktivirati svoju standardnu " "funkciju, \n" "i morate držati FN taster da biste aktivirali njihovu posebnu " "funkciju." #: lib/logitech_receiver/settings_templates.py:149 msgid "Hand Detection" msgstr "Detekcija ruku" #: lib/logitech_receiver/settings_templates.py:150 msgid "Turn on illumination when the hands hover over the keyboard." msgstr "Uključite osvetljenje kada ruke lebde preko tastature." #: lib/logitech_receiver/settings_templates.py:157 msgid "Scroll Wheel Smooth Scrolling" msgstr "Točak za pomeranje, glatko pomeranje" #: lib/logitech_receiver/settings_templates.py:158 #: lib/logitech_receiver/settings_templates.py:239 #: lib/logitech_receiver/settings_templates.py:267 msgid "High-sensitivity mode for vertical scroll with the wheel." msgstr "Režim visoke osetljivosti za vertikalno pomeranje pomoću točkića." #: lib/logitech_receiver/settings_templates.py:165 msgid "Side Scrolling" msgstr "Bočno pomeranje" #: lib/logitech_receiver/settings_templates.py:167 msgid "When disabled, pushing the wheel sideways sends custom button " "events\n" "instead of the standard side-scrolling events." msgstr "Kada je onemogućeno, pomeranje točkića u stranu šalje događaje " "prilagođenog dugmeta \n" "umesto standardnih događaja bočnog pomeranja." #: lib/logitech_receiver/settings_templates.py:177 msgid "Sensitivity (DPI - older mice)" msgstr "Osetljivost (DPI - stariji miševi)" #: lib/logitech_receiver/settings_templates.py:178 #: lib/logitech_receiver/settings_templates.py:712 msgid "Mouse movement sensitivity" msgstr "Osetljivost pokreta miša" #: lib/logitech_receiver/settings_templates.py:208 #: lib/logitech_receiver/settings_templates.py:218 #: lib/logitech_receiver/settings_templates.py:225 msgid "Backlight" msgstr "Pozadinsko osvetljenje" #: lib/logitech_receiver/settings_templates.py:209 #: lib/logitech_receiver/settings_templates.py:226 msgid "Set illumination time for keyboard." msgstr "Podesite vreme osvetljenja za tastaturu." #: lib/logitech_receiver/settings_templates.py:219 msgid "Turn illumination on or off on keyboard." msgstr "Uključite ili isključite osvetljenje na tastaturi." #: lib/logitech_receiver/settings_templates.py:237 msgid "Scroll Wheel High Resolution" msgstr "Visoka rezolucija točkića za pomeranje." #: lib/logitech_receiver/settings_templates.py:240 #: lib/logitech_receiver/settings_templates.py:268 msgid "Set to ignore if scrolling is abnormally fast or slow" msgstr "Podesite da ignorišete ako je pomeranje nenormalno brzo ili sporo" #: lib/logitech_receiver/settings_templates.py:247 #: lib/logitech_receiver/settings_templates.py:277 msgid "Scroll Wheel Diversion" msgstr "Preusmeravanje točkića za pomeranje" #: lib/logitech_receiver/settings_templates.py:249 msgid "Make scroll wheel send LOWRES_WHEEL HID++ notifications (which " "trigger Solaar rules but are otherwise ignored)." msgstr "Neka točkić za pomeranje šalje LOWRES_WHEEL HID++ obaveštenja (koja " "pokrećuSolaar pravila, ali se inače ignorišu." #: lib/logitech_receiver/settings_templates.py:256 msgid "Scroll Wheel Direction" msgstr "Smer točkića za pomeranje" #: lib/logitech_receiver/settings_templates.py:257 msgid "Invert direction for vertical scroll with wheel." msgstr "Obrni smer za vertikalno pomeranje sa točkom." #: lib/logitech_receiver/settings_templates.py:265 msgid "Scroll Wheel Resolution" msgstr "Rezolucija točkića za pomeranje" #: lib/logitech_receiver/settings_templates.py:279 msgid "Make scroll wheel send HIRES_WHEEL HID++ notifications (which " "trigger Solaar rules but are otherwise ignored)." msgstr "Neka točkić za pomeranje šalje HIRES_WHEEL HID++ obaveštenja (koja " "pokrećuSolaar pravila, ali se inače ignorišu." #: lib/logitech_receiver/settings_templates.py:288 msgid "Sensitivity (Pointer Speed)" msgstr "Osetljivost (brzina pokazivača)" #: lib/logitech_receiver/settings_templates.py:289 msgid "Speed multiplier for mouse (256 is normal multiplier)." msgstr "Množilac brzine za miš (256 je normalan množilac)." #: lib/logitech_receiver/settings_templates.py:299 msgid "Thumb Wheel Diversion" msgstr "Diverzija sa palcem" #: lib/logitech_receiver/settings_templates.py:301 msgid "Make thumb wheel send THUMB_WHEEL HID++ notifications (which trigger " "Solaar rules but are otherwise ignored)." msgstr "Neka kotačić palca šalje THUMB_WHEEL HID++ obavijesti (što pokreće " "Solaar pravila, u suprotnome ih zanemaruje)." #: lib/logitech_receiver/settings_templates.py:310 msgid "Thumb Wheel Direction" msgstr "Preusmeravanje pomicanja kotačića palca" #: lib/logitech_receiver/settings_templates.py:311 msgid "Invert thumb wheel scroll direction." msgstr "Obrnite smer pomeranja točkića" #: lib/logitech_receiver/settings_templates.py:319 msgid "Onboard Profiles" msgstr "Onboard profili" #: lib/logitech_receiver/settings_templates.py:320 msgid "Enable onboard profiles, which often control report rate and " "keyboard lighting" msgstr "Omogućite onboard profile, koji najčešće kontrolišu brzinom " "pozivanja i osvetljenje tipkovnice" #: lib/logitech_receiver/settings_templates.py:330 msgid "Polling Rate (ms)" msgstr "Brzina pozivanja (ms)" #: lib/logitech_receiver/settings_templates.py:332 msgid "Frequency of device polling, in milliseconds" msgstr "Frekvencija brzine pozivanja uređaja, u milisekundama." #: lib/logitech_receiver/settings_templates.py:333 #: lib/logitech_receiver/settings_templates.py:1046 #: lib/logitech_receiver/settings_templates.py:1076 msgid "May need Onboard Profiles set to Disable to be effective." msgstr "Možda će biti potrebni ugrađeni profili podešeni na 'Onemogućeno' da " "bi bili efikasni." #: lib/logitech_receiver/settings_templates.py:365 msgid "Divert crown events" msgstr "Preusmerite crown događaje" #: lib/logitech_receiver/settings_templates.py:366 msgid "Make crown send CROWN HID++ notifications (which trigger Solaar " "rules but are otherwise ignored)." msgstr "Neka crown šalje CROWN HID++ obaveštenja (koja pokreću Solaar " "pravila, alineka se inače ignorišu." #: lib/logitech_receiver/settings_templates.py:374 msgid "Crown smooth scroll" msgstr "Crown glatko pomicanje" #: lib/logitech_receiver/settings_templates.py:375 msgid "Set crown smooth scroll" msgstr "Podesi Crown glatko pomicanje" #: lib/logitech_receiver/settings_templates.py:383 msgid "Divert G Keys" msgstr "Preusmeri G tastere" #: lib/logitech_receiver/settings_templates.py:385 msgid "Make G keys send GKEY HID++ notifications (which trigger Solaar " "rules but are otherwise ignored)." msgstr "Neka G tasteri šalju GKEY HID++ obaveštenja (koja pokreću Solaar " "pravila, alineka se inače ignorišu." #: lib/logitech_receiver/settings_templates.py:386 msgid "May also make M keys and MR key send HID++ notifications" msgstr "Takođe može da učini da M i MR tasteri šalju HID++ obaveštenja" #: lib/logitech_receiver/settings_templates.py:402 msgid "Scroll Wheel Ratcheted" msgstr "" #: lib/logitech_receiver/settings_templates.py:403 msgid "Switch the mouse wheel between speed-controlled ratcheting and " "always freespin." msgstr "" #: lib/logitech_receiver/settings_templates.py:405 msgid "Freespinning" msgstr "" #: lib/logitech_receiver/settings_templates.py:405 msgid "Ratcheted" msgstr "" #: lib/logitech_receiver/settings_templates.py:412 msgid "Scroll Wheel Ratchet Speed" msgstr "" #: lib/logitech_receiver/settings_templates.py:414 msgid "Use the mouse wheel speed to switch between ratcheted and " "freespinning.\n" "The mouse wheel is always ratcheted at 50." msgstr "" #: lib/logitech_receiver/settings_templates.py:463 msgid "Key/Button Actions" msgstr "Radnje tastera" #: lib/logitech_receiver/settings_templates.py:465 msgid "Change the action for the key or button." msgstr "Promenite radnju za taster ili dugme." #: lib/logitech_receiver/settings_templates.py:465 msgid "Overridden by diversion." msgstr "Preusmjeravanje tipki." #: lib/logitech_receiver/settings_templates.py:466 msgid "Changing important actions (such as for the left mouse button) can " "result in an unusable system." msgstr "Promena važnih radnji (kao što je levi taster miša) može dovesti do " "neupotrebljivosti sistem." #: lib/logitech_receiver/settings_templates.py:639 msgid "Key/Button Diversion" msgstr "Preusmeravanje tipki" #: lib/logitech_receiver/settings_templates.py:640 msgid "Make the key or button send HID++ notifications (Diverted) or " "initiate Mouse Gestures or Sliding DPI" msgstr "Neka taster ili dugme šalje HID++ obaveštenja (preusmereno) ili " "pokrenite miš pokretom" #: lib/logitech_receiver/settings_templates.py:643 #: lib/logitech_receiver/settings_templates.py:644 #: lib/logitech_receiver/settings_templates.py:645 msgid "Diverted" msgstr "Preusmereno" #: lib/logitech_receiver/settings_templates.py:643 #: lib/logitech_receiver/settings_templates.py:644 msgid "Mouse Gestures" msgstr "Pokreti miša" #: lib/logitech_receiver/settings_templates.py:643 #: lib/logitech_receiver/settings_templates.py:644 #: lib/logitech_receiver/settings_templates.py:645 msgid "Regular" msgstr "Redovno" #: lib/logitech_receiver/settings_templates.py:643 msgid "Sliding DPI" msgstr "Klizni DPI" #: lib/logitech_receiver/settings_templates.py:711 msgid "Sensitivity (DPI)" msgstr "Osetljivost (DPI)" #: lib/logitech_receiver/settings_templates.py:752 msgid "Sensitivity Switching" msgstr "Promena osetljivosti" #: lib/logitech_receiver/settings_templates.py:754 msgid "Switch the current sensitivity and the remembered sensitivity when " "the key or button is pressed.\n" "If there is no remembered sensitivity, just remember the current " "sensitivity" msgstr "Promenite trenutnu osetljivost i zapamćenu osetljivost kada " "pritisnete taster ili kada je dugme pritisnuto. \n" "Ako nema zapamćene osetljivosti, samo zapamtite trenutnu osetljivost" #: lib/logitech_receiver/settings_templates.py:758 msgid "Off" msgstr "Ugašen" #: lib/logitech_receiver/settings_templates.py:791 msgid "Disable keys" msgstr "Onemogući tipke" #: lib/logitech_receiver/settings_templates.py:792 msgid "Disable specific keyboard keys." msgstr "Onemogući određene tipke na tipkovnici." #: lib/logitech_receiver/settings_templates.py:795 #, python-format msgid "Disables the %s key." msgstr "Onemogućuje %s tipku." #: lib/logitech_receiver/settings_templates.py:809 #: lib/logitech_receiver/settings_templates.py:860 msgid "Set OS" msgstr "Postavi OS" #: lib/logitech_receiver/settings_templates.py:810 #: lib/logitech_receiver/settings_templates.py:861 msgid "Change keys to match OS." msgstr "Promeni tastere da se poklapaju sa OS." #: lib/logitech_receiver/settings_templates.py:873 msgid "Change Host" msgstr "Promeni računalo" #: lib/logitech_receiver/settings_templates.py:874 msgid "Switch connection to a different host" msgstr "Prebaci vezu na drugo računalo" #: lib/logitech_receiver/settings_templates.py:900 msgid "Performs a left click." msgstr "Izvodi levi klik." #: lib/logitech_receiver/settings_templates.py:900 msgid "Single tap" msgstr "Jedan dodir" #: lib/logitech_receiver/settings_templates.py:901 msgid "Performs a right click." msgstr "Izvodi desni klik." #: lib/logitech_receiver/settings_templates.py:901 msgid "Single tap with two fingers" msgstr "Jedan dodir sa dva prsta" #: lib/logitech_receiver/settings_templates.py:902 msgid "Single tap with three fingers" msgstr "Jedan dodir sa tri prsta" #: lib/logitech_receiver/settings_templates.py:906 msgid "Double tap" msgstr "Dvostruki dodir" #: lib/logitech_receiver/settings_templates.py:906 msgid "Performs a double click." msgstr "Izvodi dvostruki klik." #: lib/logitech_receiver/settings_templates.py:907 msgid "Double tap with two fingers" msgstr "Dvoklik sa dva prsta" #: lib/logitech_receiver/settings_templates.py:908 msgid "Double tap with three fingers" msgstr "Dvoklik sa tri prsta" #: lib/logitech_receiver/settings_templates.py:911 msgid "Drags items by dragging the finger after double tapping." msgstr "Prevlači stavke prevlačenjem prsta nakon dvostrukog dodira." #: lib/logitech_receiver/settings_templates.py:911 msgid "Tap and drag" msgstr "Dodirnite i prevucite" #: lib/logitech_receiver/settings_templates.py:913 msgid "Drags items by dragging the fingers after double tapping." msgstr "Prevlači stavke prevlačenjem prstiju nakon dvostrukog dodira." #: lib/logitech_receiver/settings_templates.py:913 msgid "Tap and drag with two fingers" msgstr "Dodirnite i prevucite sa dva prsta" #: lib/logitech_receiver/settings_templates.py:914 msgid "Tap and drag with three fingers" msgstr "Dodirnite i prevucite sa tri prsta" #: lib/logitech_receiver/settings_templates.py:917 msgid "Disables tap and edge gestures (equivalent to pressing Fn+LeftClick)." msgstr "Onemogućava pokrete dodira i rubnih gesta (ekvivalentno pritiskanju " "Fn+LeftClick)." #: lib/logitech_receiver/settings_templates.py:917 msgid "Suppress tap and edge gestures" msgstr "Zaustavite pokrete dodira i ivica" #: lib/logitech_receiver/settings_templates.py:918 msgid "Scroll with one finger" msgstr "Pomerite se jednim prstom" #: lib/logitech_receiver/settings_templates.py:918 #: lib/logitech_receiver/settings_templates.py:919 #: lib/logitech_receiver/settings_templates.py:922 msgid "Scrolls." msgstr "Pomeranje." #: lib/logitech_receiver/settings_templates.py:919 #: lib/logitech_receiver/settings_templates.py:922 msgid "Scroll with two fingers" msgstr "Pomeranje sa dva prsta" #: lib/logitech_receiver/settings_templates.py:920 msgid "Scroll horizontally with two fingers" msgstr "Pomeranje horizontalno sa dva prsta" #: lib/logitech_receiver/settings_templates.py:920 msgid "Scrolls horizontally." msgstr "Pomeranje horizontalno." #: lib/logitech_receiver/settings_templates.py:921 msgid "Scroll vertically with two fingers" msgstr "Pomeranje vertikalno sa dva prsta" #: lib/logitech_receiver/settings_templates.py:921 msgid "Scrolls vertically." msgstr "Pomeranje vertikalno." #: lib/logitech_receiver/settings_templates.py:923 msgid "Inverts the scrolling direction." msgstr "Obrče smer pomeranja." #: lib/logitech_receiver/settings_templates.py:923 msgid "Natural scrolling" msgstr "Prirodno pomeranje" #: lib/logitech_receiver/settings_templates.py:924 msgid "Enables the thumbwheel." msgstr "Omogućava točkić." #: lib/logitech_receiver/settings_templates.py:924 msgid "Thumbwheel" msgstr "Kotačić palca." #: lib/logitech_receiver/settings_templates.py:935 #: lib/logitech_receiver/settings_templates.py:939 msgid "Swipe from the top edge" msgstr "Povucite od gornjeg ruba" #: lib/logitech_receiver/settings_templates.py:936 msgid "Swipe from the left edge" msgstr "Povucite od levog ruba" #: lib/logitech_receiver/settings_templates.py:937 msgid "Swipe from the right edge" msgstr "Povucite od desnog ruba" #: lib/logitech_receiver/settings_templates.py:938 msgid "Swipe from the bottom edge" msgstr "Povucite od donjeg ruba" #: lib/logitech_receiver/settings_templates.py:940 msgid "Swipe two fingers from the left edge" msgstr "Povucite sa dva prsta od levog ruba" #: lib/logitech_receiver/settings_templates.py:941 msgid "Swipe two fingers from the right edge" msgstr "Povucite sa dva prsta od desnog ruba" #: lib/logitech_receiver/settings_templates.py:942 msgid "Swipe two fingers from the bottom edge" msgstr "Povucite sa dva prsta od donjeg ruba" #: lib/logitech_receiver/settings_templates.py:943 msgid "Swipe two fingers from the top edge" msgstr "Povucite sa dva prsta od gornjeg ruba" #: lib/logitech_receiver/settings_templates.py:944 #: lib/logitech_receiver/settings_templates.py:948 msgid "Pinch to zoom out; spread to zoom in." msgstr "Stisnite da biste umanjili; proširite da biste uvećali." #: lib/logitech_receiver/settings_templates.py:944 msgid "Zoom with two fingers." msgstr "Zumirajte sa dva prsta." #: lib/logitech_receiver/settings_templates.py:945 msgid "Pinch to zoom out." msgstr "Stisnite prste da biste umanjili prikaz." #: lib/logitech_receiver/settings_templates.py:946 msgid "Spread to zoom in." msgstr "Raširite prste da biste uvećali." #: lib/logitech_receiver/settings_templates.py:947 msgid "Zoom with three fingers." msgstr "Zumirajte sa tri prsta." #: lib/logitech_receiver/settings_templates.py:948 msgid "Zoom with two fingers" msgstr "Zumirajte sa dva prsta." #: lib/logitech_receiver/settings_templates.py:966 msgid "Pixel zone" msgstr "Zona piksela" #: lib/logitech_receiver/settings_templates.py:967 msgid "Ratio zone" msgstr "Zona omera" #: lib/logitech_receiver/settings_templates.py:968 msgid "Scale factor" msgstr "Faktor veličine" #: lib/logitech_receiver/settings_templates.py:968 msgid "Sets the cursor speed." msgstr "Podešava brzinu pokazivača." #: lib/logitech_receiver/settings_templates.py:972 msgid "Left" msgstr "Levo" #: lib/logitech_receiver/settings_templates.py:972 msgid "Left-most coordinate." msgstr "Najučestalija leva koordinata." #: lib/logitech_receiver/settings_templates.py:973 msgid "Bottom" msgstr "Dno" #: lib/logitech_receiver/settings_templates.py:973 msgid "Bottom coordinate." msgstr "Donja koordinata." #: lib/logitech_receiver/settings_templates.py:974 msgid "Width" msgstr "Širina" #: lib/logitech_receiver/settings_templates.py:974 msgid "Width." msgstr "Širina" #: lib/logitech_receiver/settings_templates.py:975 msgid "Height" msgstr "Visina" #: lib/logitech_receiver/settings_templates.py:975 msgid "Height." msgstr "Visina." #: lib/logitech_receiver/settings_templates.py:976 msgid "Cursor speed." msgstr "Brzina kursora." #: lib/logitech_receiver/settings_templates.py:976 msgid "Scale" msgstr "Skala" #: lib/logitech_receiver/settings_templates.py:982 msgid "Gestures" msgstr "Geste" #: lib/logitech_receiver/settings_templates.py:983 msgid "Tweak the mouse/touchpad behaviour." msgstr "Podesite ponašanje miša/tačpeda." #: lib/logitech_receiver/settings_templates.py:1000 msgid "Gestures Diversion" msgstr "Preusmjeravanje gesta" #: lib/logitech_receiver/settings_templates.py:1001 msgid "Divert mouse/touchpad gestures." msgstr "Preusmeri ponašanje miša/tačpeda." #: lib/logitech_receiver/settings_templates.py:1018 msgid "Gesture params" msgstr "Parametri pokreta" #: lib/logitech_receiver/settings_templates.py:1019 msgid "Change numerical parameters of a mouse/touchpad." msgstr "Promenite numeričke parametre miša/tačpeda." #: lib/logitech_receiver/settings_templates.py:1044 msgid "M-Key LEDs" msgstr "LED osvetljenje M-tastera" #: lib/logitech_receiver/settings_templates.py:1046 msgid "Control the M-Key LEDs." msgstr "Kontrolišite LED osvetljenje M-tastera." #: lib/logitech_receiver/settings_templates.py:1047 #: lib/logitech_receiver/settings_templates.py:1077 msgid "May need G Keys diverted to be effective." msgstr "Možda će biti potrebno ponovo preusmeriti taster G da bi bili " "efikasni." #: lib/logitech_receiver/settings_templates.py:1053 #, python-format msgid "Lights up the %s key." msgstr "Osvetli %s taster." #: lib/logitech_receiver/settings_templates.py:1074 msgid "MR-Key LED" msgstr "LED MR-tastera" #: lib/logitech_receiver/settings_templates.py:1076 msgid "Control the MR-Key LED." msgstr "Kontroliše LED osvetljenje MR-tastera." #: lib/logitech_receiver/settings_templates.py:1095 msgid "Persistent Key/Button Mapping" msgstr "Trajno mapiranje tastera/dugmada" #: lib/logitech_receiver/settings_templates.py:1097 msgid "Permanently change the mapping for the key or button." msgstr "Trajno promenite mapiranje za taster ili dugme." #: lib/logitech_receiver/settings_templates.py:1098 msgid "Changing important keys or buttons (such as for the left mouse " "button) can result in an unusable system." msgstr "Promena važnih tastera ili dugmadi (kao što je levi taster miša) " "može rezultiraju nestabilnim sistemom." #: lib/logitech_receiver/settings_templates.py:1157 msgid "Sidetone" msgstr "Bočni ton" #: lib/logitech_receiver/settings_templates.py:1158 msgid "Set sidetone level." msgstr "Podesite nivo bočnih tonova." #: lib/logitech_receiver/settings_templates.py:1167 msgid "Equalizer" msgstr "Ekvilajzer" #: lib/logitech_receiver/settings_templates.py:1168 msgid "Set equalizer levels." msgstr "Podesite nivo ekvilajzera." #: lib/logitech_receiver/settings_templates.py:1191 msgid "Hz" msgstr "Hz" #: lib/logitech_receiver/settings_templates.py:1197 msgid "Power Management" msgstr "" #: lib/logitech_receiver/settings_templates.py:1198 msgid "Power off in minutes (0 for never)." msgstr "" #: lib/logitech_receiver/status.py:114 msgid "No paired devices." msgstr "Nema uparenih uređaja" #: lib/logitech_receiver/status.py:115 lib/solaar/ui/window.py:622 #, python-format msgid "%(count)s paired device." msgid_plural "%(count)s paired devices." msgstr[0] "%(count)s uparen uređaj." msgstr[1] "%(count)s uparena uređaja." #: lib/logitech_receiver/status.py:170 #, python-format msgid "Battery: %(level)s" msgstr "Baterija: %(level)s" #: lib/logitech_receiver/status.py:172 #, python-format msgid "Battery: %(percent)d%%" msgstr "Baterija: %(percent)d%%" #: lib/logitech_receiver/status.py:184 #, python-format msgid "Lighting: %(level)s lux" msgstr "Osvetljenje %(level)s lux" #: lib/logitech_receiver/status.py:239 #, python-format msgid "Battery: %(level)s (%(status)s)" msgstr "Baterija: %(level)s (%(status)s)" #: lib/logitech_receiver/status.py:241 #, python-format msgid "Battery: %(percent)d%% (%(status)s)" msgstr "Baterija: %(percent)d%% (%(status)s)" #: lib/solaar/ui/__init__.py:52 msgid "Permissions error" msgstr "Greška u dozvolama" #: lib/solaar/ui/__init__.py:54 #, python-format msgid "Found a Logitech receiver or device (%s), but did not have " "permission to open it." msgstr "" #: lib/solaar/ui/__init__.py:55 msgid "If you've just installed Solaar, try disconnecting the receiver or " "device and then reconnecting it." msgstr "" #: lib/solaar/ui/__init__.py:58 msgid "Cannot connect to device error" msgstr "Ne mogu da se povežem sa uređajem, greška" #: lib/solaar/ui/__init__.py:60 #, python-format msgid "Found a Logitech receiver or device at %s, but encountered an error " "connecting to it." msgstr "Pronašli smo Logitech prijemnik ili uređaj na %s, ali smo naišli na " "greškuprilikom povezivanja." #: lib/solaar/ui/__init__.py:61 msgid "Try disconnecting the device and then reconnecting it or turning it " "off and then on." msgstr "" #: lib/solaar/ui/__init__.py:64 msgid "Unpairing failed" msgstr "Uparivanje neuspelo" #: lib/solaar/ui/__init__.py:66 #, python-brace-format msgid "Failed to unpair %{device} from %{receiver}." msgstr "Odparivanje %{device} sa %{receiver} nije uspelo." #: lib/solaar/ui/__init__.py:67 msgid "The receiver returned an error, with no further details." msgstr "Prijemnik je vratio grešku, bez dodatnih detalja." #: lib/solaar/ui/__init__.py:177 msgid "Another Solaar process is already running so just expose its window" msgstr "Još jedan Solaar proces je već pokrenut, pa samo otvorite njegov " "prozor" #: lib/solaar/ui/about.py:36 msgid "Manages Logitech receivers,\n" "keyboards, mice, and tablets." msgstr "Upravlja Logitech prijemnicima,\n" "tastature, miševi i tableti." #: lib/solaar/ui/about.py:44 msgid "Additional Programming" msgstr "Dodatno programiranje" #: lib/solaar/ui/about.py:45 msgid "GUI design" msgstr "GUI dizajn" #: lib/solaar/ui/about.py:47 msgid "Testing" msgstr "Testiranje" #: lib/solaar/ui/about.py:54 msgid "Logitech documentation" msgstr "Logitech dokumentacija" #: lib/solaar/ui/action.py:85 lib/solaar/ui/action.py:89 #: lib/solaar/ui/window.py:197 msgid "Unpair" msgstr "Odspoji" #: lib/solaar/ui/action.py:88 lib/solaar/ui/diversion_rules.py:150 msgid "Cancel" msgstr "Poništi" #: lib/solaar/ui/config_panel.py:212 msgid "Complete - ENTER to change" msgstr "Završeno - pritisnite ENTER da biste promenili" #: lib/solaar/ui/config_panel.py:212 msgid "Incomplete" msgstr "Nepotpuno" #: lib/solaar/ui/config_panel.py:455 lib/solaar/ui/config_panel.py:507 #, python-format msgid "%d value" msgid_plural "%d values" msgstr[0] "%d vrednost" msgstr[1] "%d vrednosti" #: lib/solaar/ui/config_panel.py:518 msgid "Changes allowed" msgstr "Promene dozvoljene" #: lib/solaar/ui/config_panel.py:519 msgid "No changes allowed" msgstr "Promene zabranjene" #: lib/solaar/ui/config_panel.py:520 msgid "Ignore this setting" msgstr "Ignoriši ovu postavku" #: lib/solaar/ui/config_panel.py:565 msgid "Working" msgstr "Radim" #: lib/solaar/ui/config_panel.py:568 msgid "Read/write operation failed." msgstr "Operacija čitanja/pisanja nije uspela." #: lib/solaar/ui/diversion_rules.py:65 msgid "Built-in rules" msgstr "Ugrađena pravila" #: lib/solaar/ui/diversion_rules.py:65 msgid "User-defined rules" msgstr "Korisnički definisana pravila" #: lib/solaar/ui/diversion_rules.py:67 lib/solaar/ui/diversion_rules.py:1083 msgid "Rule" msgstr "Pravilo" #: lib/solaar/ui/diversion_rules.py:68 lib/solaar/ui/diversion_rules.py:509 #: lib/solaar/ui/diversion_rules.py:636 msgid "Sub-rule" msgstr "Podpravilo" #: lib/solaar/ui/diversion_rules.py:70 msgid "[empty]" msgstr "[prazno]" #: lib/solaar/ui/diversion_rules.py:94 msgid "Solaar Rule Editor" msgstr "Solaar uređivač pravila" #: lib/solaar/ui/diversion_rules.py:141 msgid "Make changes permanent?" msgstr "Želite li da promene budu trajne?" #: lib/solaar/ui/diversion_rules.py:146 msgid "Yes" msgstr "Da" #: lib/solaar/ui/diversion_rules.py:148 msgid "No" msgstr "Ne" #: lib/solaar/ui/diversion_rules.py:153 msgid "If you choose No, changes will be lost when Solaar is closed." msgstr "Ako izaberete Ne, promene će biti izgubljene kada se Solaar zatvori." #: lib/solaar/ui/diversion_rules.py:201 msgid "Save changes" msgstr "Spremi promene" #: lib/solaar/ui/diversion_rules.py:206 msgid "Discard changes" msgstr "Odbaci promene" #: lib/solaar/ui/diversion_rules.py:372 msgid "Insert here" msgstr "Unesi ovde" #: lib/solaar/ui/diversion_rules.py:374 msgid "Insert above" msgstr "Unesi iznad" #: lib/solaar/ui/diversion_rules.py:376 msgid "Insert below" msgstr "Unesi ispod" #: lib/solaar/ui/diversion_rules.py:382 msgid "Insert new rule here" msgstr "Unesi novo pravilo ovde" #: lib/solaar/ui/diversion_rules.py:384 msgid "Insert new rule above" msgstr "Unesi novo pravilo iznad" #: lib/solaar/ui/diversion_rules.py:386 msgid "Insert new rule below" msgstr "Unesi novo pravilo ispod" #: lib/solaar/ui/diversion_rules.py:427 msgid "Paste here" msgstr "Zalepi ovde" #: lib/solaar/ui/diversion_rules.py:429 msgid "Paste above" msgstr "Zalepi iznad" #: lib/solaar/ui/diversion_rules.py:431 msgid "Paste below" msgstr "Zalepi ispod" #: lib/solaar/ui/diversion_rules.py:437 msgid "Paste rule here" msgstr "Zalepi pravilo ovde" #: lib/solaar/ui/diversion_rules.py:439 msgid "Paste rule above" msgstr "Zalepi pravilo iznad" #: lib/solaar/ui/diversion_rules.py:441 msgid "Paste rule below" msgstr "Zalepi pravilo ispod" #: lib/solaar/ui/diversion_rules.py:445 msgid "Paste rule" msgstr "Zalepi pravilo" #: lib/solaar/ui/diversion_rules.py:474 msgid "Flatten" msgstr "Poravnaj" #: lib/solaar/ui/diversion_rules.py:507 msgid "Insert" msgstr "Unesi" #: lib/solaar/ui/diversion_rules.py:510 lib/solaar/ui/diversion_rules.py:638 #: lib/solaar/ui/diversion_rules.py:1126 msgid "Or" msgstr "Ili" #: lib/solaar/ui/diversion_rules.py:511 lib/solaar/ui/diversion_rules.py:637 #: lib/solaar/ui/diversion_rules.py:1111 msgid "And" msgstr "I" #: lib/solaar/ui/diversion_rules.py:513 msgid "Condition" msgstr "Stanje" #: lib/solaar/ui/diversion_rules.py:515 lib/solaar/ui/diversion_rules.py:1292 msgid "Feature" msgstr "Značajka" #: lib/solaar/ui/diversion_rules.py:516 lib/solaar/ui/diversion_rules.py:1328 msgid "Report" msgstr "Izveštaj" #: lib/solaar/ui/diversion_rules.py:517 lib/solaar/ui/diversion_rules.py:1204 msgid "Process" msgstr "Proces" #: lib/solaar/ui/diversion_rules.py:518 msgid "Mouse process" msgstr "Proces miša" #: lib/solaar/ui/diversion_rules.py:519 lib/solaar/ui/diversion_rules.py:1366 msgid "Modifiers" msgstr "Modifikatori" #: lib/solaar/ui/diversion_rules.py:520 lib/solaar/ui/diversion_rules.py:1419 msgid "Key" msgstr "Taster" #: lib/solaar/ui/diversion_rules.py:521 lib/solaar/ui/diversion_rules.py:1461 msgid "KeyIsDown" msgstr "" #: lib/solaar/ui/diversion_rules.py:522 lib/solaar/ui/diversion_rules.py:2260 msgid "Active" msgstr "Радан" #: lib/solaar/ui/diversion_rules.py:523 lib/solaar/ui/diversion_rules.py:2218 #: lib/solaar/ui/diversion_rules.py:2270 lib/solaar/ui/diversion_rules.py:2323 msgid "Device" msgstr "Uređaj" #: lib/solaar/ui/diversion_rules.py:524 lib/solaar/ui/diversion_rules.py:2297 msgid "Host" msgstr "" #: lib/solaar/ui/diversion_rules.py:525 lib/solaar/ui/diversion_rules.py:2339 msgid "Setting" msgstr "Postavka" #: lib/solaar/ui/diversion_rules.py:526 lib/solaar/ui/diversion_rules.py:1477 #: lib/solaar/ui/diversion_rules.py:1526 msgid "Test" msgstr "Test" #: lib/solaar/ui/diversion_rules.py:527 lib/solaar/ui/diversion_rules.py:1643 msgid "Test bytes" msgstr "Testiraj bajtove" #: lib/solaar/ui/diversion_rules.py:528 lib/solaar/ui/diversion_rules.py:1736 msgid "Mouse Gesture" msgstr "Gesta miša" #: lib/solaar/ui/diversion_rules.py:532 msgid "Action" msgstr "Akcija" #: lib/solaar/ui/diversion_rules.py:534 lib/solaar/ui/diversion_rules.py:1845 msgid "Key press" msgstr "Pritisak tipke" #: lib/solaar/ui/diversion_rules.py:535 lib/solaar/ui/diversion_rules.py:1897 msgid "Mouse scroll" msgstr "Pomeranje miša" #: lib/solaar/ui/diversion_rules.py:536 lib/solaar/ui/diversion_rules.py:1959 msgid "Mouse click" msgstr "Klik mišem" #: lib/solaar/ui/diversion_rules.py:537 msgid "Set" msgstr "Postavi" #: lib/solaar/ui/diversion_rules.py:538 lib/solaar/ui/diversion_rules.py:2030 msgid "Execute" msgstr "Izvrši" #: lib/solaar/ui/diversion_rules.py:539 lib/solaar/ui/diversion_rules.py:1158 msgid "Later" msgstr "" #: lib/solaar/ui/diversion_rules.py:568 msgid "Insert new rule" msgstr "Unesi novo pravilo" #: lib/solaar/ui/diversion_rules.py:588 lib/solaar/ui/diversion_rules.py:1686 #: lib/solaar/ui/diversion_rules.py:1790 lib/solaar/ui/diversion_rules.py:1989 msgid "Delete" msgstr "Obriši" #: lib/solaar/ui/diversion_rules.py:610 msgid "Negate" msgstr "Negiraj" #: lib/solaar/ui/diversion_rules.py:634 msgid "Wrap with" msgstr "Zamotajte sa" #: lib/solaar/ui/diversion_rules.py:656 msgid "Cut" msgstr "Izreži" #: lib/solaar/ui/diversion_rules.py:671 msgid "Paste" msgstr "Zalepi" #: lib/solaar/ui/diversion_rules.py:683 msgid "Copy" msgstr "Kopiraj" #: lib/solaar/ui/diversion_rules.py:1063 msgid "This editor does not support the selected rule component yet." msgstr "Ovaj tekstualni uređivač još uvek ne podržava izabranu komponentu " "pravila." #: lib/solaar/ui/diversion_rules.py:1138 msgid "Number of seconds to delay." msgstr "" #: lib/solaar/ui/diversion_rules.py:1177 msgid "Not" msgstr "Ne" #: lib/solaar/ui/diversion_rules.py:1187 msgid "X11 active process. For use in X11 only." msgstr "" #: lib/solaar/ui/diversion_rules.py:1218 msgid "X11 mouse process. For use in X11 only." msgstr "" #: lib/solaar/ui/diversion_rules.py:1235 msgid "MouseProcess" msgstr "Proces miša" #: lib/solaar/ui/diversion_rules.py:1260 msgid "Feature name of notification triggering rule processing." msgstr "" #: lib/solaar/ui/diversion_rules.py:1308 msgid "Report number of notification triggering rule processing." msgstr "" #: lib/solaar/ui/diversion_rules.py:1342 msgid "Active keyboard modifiers. Not always available in Wayland." msgstr "" #: lib/solaar/ui/diversion_rules.py:1383 msgid "Diverted key or button depressed or released.\n" "Use the Key/Button Diversion and Divert G Keys settings to divert " "keys and buttons." msgstr "" #: lib/solaar/ui/diversion_rules.py:1392 msgid "Key down" msgstr "Tipka za dolje" #: lib/solaar/ui/diversion_rules.py:1395 msgid "Key up" msgstr "Tipka za gore" #: lib/solaar/ui/diversion_rules.py:1436 msgid "Diverted key or button is currently down.\n" "Use the Key/Button Diversion and Divert G Keys settings to divert " "keys and buttons." msgstr "" #: lib/solaar/ui/diversion_rules.py:1475 msgid "Test condition on notification triggering rule processing." msgstr "" #: lib/solaar/ui/diversion_rules.py:1479 msgid "Parameter" msgstr "" #: lib/solaar/ui/diversion_rules.py:1542 msgid "begin (inclusive)" msgstr "početak (uključiv)" #: lib/solaar/ui/diversion_rules.py:1543 msgid "end (exclusive)" msgstr "završetak (isključiv)" #: lib/solaar/ui/diversion_rules.py:1552 msgid "range" msgstr "domet" #: lib/solaar/ui/diversion_rules.py:1554 msgid "minimum" msgstr "minimum" #: lib/solaar/ui/diversion_rules.py:1555 msgid "maximum" msgstr "maximum" #: lib/solaar/ui/diversion_rules.py:1557 #, python-format msgid "bytes %(0)d to %(1)d, ranging from %(2)d to %(3)d" msgstr "bajtovi %(0)d do %(1)d, u dometu od %(2)d do %(3)d" #: lib/solaar/ui/diversion_rules.py:1562 msgid "mask" msgstr "mask" #: lib/solaar/ui/diversion_rules.py:1563 #, python-format msgid "bytes %(0)d to %(1)d, mask %(2)d" msgstr "bajtovi %(0)d do %(1)d, maska %(2)d" #: lib/solaar/ui/diversion_rules.py:1573 msgid "Bit or range test on bytes in notification message triggering rule " "processing." msgstr "" #: lib/solaar/ui/diversion_rules.py:1583 msgid "type" msgstr "tip" #: lib/solaar/ui/diversion_rules.py:1666 msgid "Mouse gesture with optional initiating button followed by zero or " "more mouse movements." msgstr "" #: lib/solaar/ui/diversion_rules.py:1671 msgid "Add movement" msgstr "" #: lib/solaar/ui/diversion_rules.py:1764 msgid "Simulate a chorded key click or depress or release.\n" "On Wayland requires write access to /dev/uinput." msgstr "" #: lib/solaar/ui/diversion_rules.py:1769 msgid "Add key" msgstr "Dodaj tipku" #: lib/solaar/ui/diversion_rules.py:1772 msgid "Click" msgstr "Kliknite" #: lib/solaar/ui/diversion_rules.py:1775 msgid "Depress" msgstr "Odpustite" #: lib/solaar/ui/diversion_rules.py:1778 msgid "Release" msgstr "Odpustite" #: lib/solaar/ui/diversion_rules.py:1861 msgid "Simulate a mouse scroll.\n" "On Wayland requires write access to /dev/uinput." msgstr "" #: lib/solaar/ui/diversion_rules.py:1918 msgid "Simulate a mouse click.\n" "On Wayland requires write access to /dev/uinput." msgstr "" #: lib/solaar/ui/diversion_rules.py:1921 msgid "Button" msgstr "Taster" #: lib/solaar/ui/diversion_rules.py:1922 msgid "Count and Action" msgstr "" #: lib/solaar/ui/diversion_rules.py:1972 msgid "Execute a command with arguments." msgstr "" #: lib/solaar/ui/diversion_rules.py:1975 msgid "Add argument" msgstr "Dodajte argument" #: lib/solaar/ui/diversion_rules.py:2050 msgid "Toggle" msgstr "Uključi/Isključi" #: lib/solaar/ui/diversion_rules.py:2050 msgid "True" msgstr "Istina" #: lib/solaar/ui/diversion_rules.py:2051 msgid "False" msgstr "Laž" #: lib/solaar/ui/diversion_rules.py:2065 msgid "Unsupported setting" msgstr "Nepodržana postavka" #: lib/solaar/ui/diversion_rules.py:2223 lib/solaar/ui/diversion_rules.py:2242 #: lib/solaar/ui/diversion_rules.py:2328 lib/solaar/ui/diversion_rules.py:2570 #: lib/solaar/ui/diversion_rules.py:2588 msgid "Originating device" msgstr "Izvorni uređaj" #: lib/solaar/ui/diversion_rules.py:2256 msgid "Device is active and its settings can be changed." msgstr "" #: lib/solaar/ui/diversion_rules.py:2266 msgid "Device that originated the current notification." msgstr "" #: lib/solaar/ui/diversion_rules.py:2280 msgid "Name of host computer." msgstr "" #: lib/solaar/ui/diversion_rules.py:2347 msgid "Value" msgstr "Vrednost" #: lib/solaar/ui/diversion_rules.py:2355 msgid "Item" msgstr "Stavka" #: lib/solaar/ui/diversion_rules.py:2630 msgid "Change setting on device" msgstr "" #: lib/solaar/ui/diversion_rules.py:2647 msgid "Setting on device" msgstr "" #: lib/solaar/ui/notify.py:124 lib/solaar/ui/tray.py:318 #: lib/solaar/ui/tray.py:323 lib/solaar/ui/window.py:739 msgid "offline" msgstr "ugašeno" #: lib/solaar/ui/pair_window.py:122 lib/solaar/ui/pair_window.py:256 #: lib/solaar/ui/pair_window.py:288 #, python-format msgid "%(receiver_name)s: pair new device" msgstr "%(receiver_name)s: uparite novi uređaj" #: lib/solaar/ui/pair_window.py:123 #, python-format msgid "Enter passcode on %(name)s." msgstr "Unesite lozinku na %(name)s." #: lib/solaar/ui/pair_window.py:126 #, python-format msgid "Type %(passcode)s and then press the enter key." msgstr "Unesite %(passcode)s, a zatim pritisnite tipku enter." #: lib/solaar/ui/pair_window.py:129 msgid "left" msgstr "levo" #: lib/solaar/ui/pair_window.py:129 msgid "right" msgstr "desno" #: lib/solaar/ui/pair_window.py:131 #, python-format msgid "Press %(code)s\n" "and then press left and right buttons simultaneously." msgstr "Pritisnite %(code)s\n" "a zatim istovremeno pritisnite levo i desno dugme." #: lib/solaar/ui/pair_window.py:188 msgid "Pairing failed" msgstr "Uparivanje neuspelo" #: lib/solaar/ui/pair_window.py:190 msgid "Make sure your device is within range, and has a decent battery " "charge." msgstr "Uverite se da je vaš uređaj u dometu i da ima pristojno punjenje " "baterije." #: lib/solaar/ui/pair_window.py:192 msgid "A new device was detected, but it is not compatible with this " "receiver." msgstr "Otkriven je novi uređaj, ali nije kompatibilan sa ovim prijemnikom." #: lib/solaar/ui/pair_window.py:194 msgid "More paired devices than receiver can support." msgstr "Više uparenih uređaja nego što prijemnik može da podrži." #: lib/solaar/ui/pair_window.py:196 msgid "No further details are available about the error." msgstr "Nisu dostupni dodatni detalji o grešci." #: lib/solaar/ui/pair_window.py:210 msgid "Found a new device:" msgstr "Pronađen novi uređaj:" #: lib/solaar/ui/pair_window.py:235 msgid "The wireless link is not encrypted" msgstr "Bežična veza nije šifrovana" #: lib/solaar/ui/pair_window.py:264 msgid "Unifying receivers are only compatible with Unifying devices." msgstr "" #: lib/solaar/ui/pair_window.py:266 msgid "Bolt receivers are only compatible with Bolt devices." msgstr "" #: lib/solaar/ui/pair_window.py:268 msgid "Other receivers are only compatible with a few devices." msgstr "" #: lib/solaar/ui/pair_window.py:270 msgid "The device must not be paired with a nearby powered-on receiver." msgstr "" #: lib/solaar/ui/pair_window.py:274 msgid "Press a pairing button or key until the pairing light flashes " "quickly." msgstr "Pritisnite dugme ili taster za uparivanje dok lampica za uparivanje " "ne počne brzo da treperi." #: lib/solaar/ui/pair_window.py:276 msgid "You may have to first turn the device off and on again." msgstr "Možda ćete morati prvo da isključite i ponovo uključite uređaj." #: lib/solaar/ui/pair_window.py:278 msgid "Turn on the device you want to pair." msgstr "Uključite uređaj koji želite da uparite." #: lib/solaar/ui/pair_window.py:280 msgid "If the device is already turned on, turn it off and on again." msgstr "Ako je uređaj već uključen, isključite ga i ponovo uključite." #: lib/solaar/ui/pair_window.py:283 #, python-format msgid "\n" "\n" "This receiver has %d pairing remaining." msgid_plural "\n" "\n" "This receiver has %d pairings remaining." msgstr[0] "\n" "\n" "Ovaj prijemnik ima još %d uparivanje." msgstr[1] "\n" "\n" "Ovaj prijemnik ima još %d uparivanja." #: lib/solaar/ui/pair_window.py:286 msgid "\n" "Cancelling at this point will not use up a pairing." msgstr "\n" "Otkazivanje u ovom trenutku neće potrošiti uparivanje." #: lib/solaar/ui/tray.py:58 msgid "No supported device found" msgstr "" #: lib/solaar/ui/tray.py:64 lib/solaar/ui/window.py:319 #, python-format msgid "About %s" msgstr "O %s" #: lib/solaar/ui/tray.py:65 lib/solaar/ui/window.py:317 #, python-format msgid "Quit %s" msgstr "Zatvori %s" #: lib/solaar/ui/tray.py:297 lib/solaar/ui/tray.py:305 msgid "no receiver" msgstr "nema prijemnika" #: lib/solaar/ui/tray.py:321 msgid "no status" msgstr "nema statusa" #: lib/solaar/ui/window.py:96 msgid "Scanning" msgstr "Skeniranje" #: lib/solaar/ui/window.py:129 msgid "Battery" msgstr "Baterija" #: lib/solaar/ui/window.py:132 msgid "Wireless Link" msgstr "Bežična veza" #: lib/solaar/ui/window.py:136 msgid "Lighting" msgstr "Osvetljenje" #: lib/solaar/ui/window.py:170 msgid "Show Technical Details" msgstr "Prikaži tehničke detalje" #: lib/solaar/ui/window.py:186 msgid "Pair new device" msgstr "Uparite novi uređaj" #: lib/solaar/ui/window.py:205 msgid "Select a device" msgstr "Odaberite uređaj" #: lib/solaar/ui/window.py:322 msgid "Rule Editor" msgstr "Tekstualni uređivač prava" #: lib/solaar/ui/window.py:533 msgid "Path" msgstr "Put" #: lib/solaar/ui/window.py:536 msgid "USB ID" msgstr "USB ID" #: lib/solaar/ui/window.py:539 lib/solaar/ui/window.py:541 #: lib/solaar/ui/window.py:561 lib/solaar/ui/window.py:563 msgid "Serial" msgstr "Serijski" #: lib/solaar/ui/window.py:545 msgid "Index" msgstr "Indeks" #: lib/solaar/ui/window.py:547 msgid "Wireless PID" msgstr "Wireless PID" #: lib/solaar/ui/window.py:549 msgid "Product ID" msgstr "Product ID" #: lib/solaar/ui/window.py:551 msgid "Protocol" msgstr "Protokol" #: lib/solaar/ui/window.py:551 msgid "Unknown" msgstr "Nepoznato" #: lib/solaar/ui/window.py:554 #, python-format msgid "%(rate)d ms (%(rate_hz)dHz)" msgstr "%(rate)d ms (%(rate_hz)dHz)" #: lib/solaar/ui/window.py:554 msgid "Polling rate" msgstr "Brzina pozivanja" #: lib/solaar/ui/window.py:565 msgid "Unit ID" msgstr "Unit ID" #: lib/solaar/ui/window.py:576 msgid "none" msgstr "nema" #: lib/solaar/ui/window.py:577 msgid "Notifications" msgstr "Obaveštenja" #: lib/solaar/ui/window.py:621 msgid "No device paired." msgstr "Nijedan uređaj nije uparen." #: lib/solaar/ui/window.py:628 #, python-format msgid "Up to %(max_count)s device can be paired to this receiver." msgid_plural "Up to %(max_count)s devices can be paired to this receiver." msgstr[0] "Do %(max_count)s uređaj može da se upari sa ovim prijemnikom." msgstr[1] "Do %(max_count)s uređaja može da se upari sa ovim " "prijemnikom." #: lib/solaar/ui/window.py:634 msgid "Only one device can be paired to this receiver." msgstr "Samo jedan uređaj može biti uparen sa ovim prijemnikom." #: lib/solaar/ui/window.py:638 #, python-format msgid "This receiver has %d pairing remaining." msgid_plural "This receiver has %d pairings remaining." msgstr[0] "Ovaj prijemnik ima još %d uparivanje." msgstr[1] "Ovaj prijemnik ima još %d uparivanja." #: lib/solaar/ui/window.py:692 msgid "Battery Voltage" msgstr "Voltaža baterije" #: lib/solaar/ui/window.py:694 msgid "Voltage reported by battery" msgstr "Napon koji javlja baterija" #: lib/solaar/ui/window.py:696 msgid "Battery Level" msgstr "Nivo baterije" #: lib/solaar/ui/window.py:698 msgid "Approximate level reported by battery" msgstr "Baterija prijavljuje približan nivo" #: lib/solaar/ui/window.py:705 lib/solaar/ui/window.py:707 msgid "next reported " msgstr "sledeći izveštaj " #: lib/solaar/ui/window.py:708 msgid " and next level to be reported." msgstr " i sledeći nivo koji treba izvesti." #: lib/solaar/ui/window.py:713 msgid "last known" msgstr "poslednje poznato" #: lib/solaar/ui/window.py:724 msgid "encrypted" msgstr "šifrovano" #: lib/solaar/ui/window.py:726 msgid "The wireless link between this device and its receiver is encrypted." msgstr "Bežična veza između ovog uređaja i njegovog prijemnika je šifrovana." #: lib/solaar/ui/window.py:728 msgid "not encrypted" msgstr "nije šifrovano" #: lib/solaar/ui/window.py:732 msgid "The wireless link between this device and its receiver is not " "encrypted.\n" "This is a security issue for pointing devices, and a major security " "issue for text-input devices." msgstr "" #: lib/solaar/ui/window.py:748 #, python-format msgid "%(light_level)d lux" msgstr "%(light_level)d lux" #~ msgid "Add action" #~ msgstr "Dodaj akciju" #~ msgid "Adjust the DPI by sliding the mouse horizontally while " #~ "holding the button down." #~ msgstr "Podesite DPI pomeranjem miša horizontalno dok držite " #~ "tasterza dolje." #~ msgid "Automatically switch the mouse wheel between ratchet and " #~ "freespin mode.\n" #~ "The mouse wheel is always free at 0, and always ratcheted at 50" #~ msgstr "Automatski prebacite točkić miša između režima ratchet i " #~ "slobodnog načina. \n" #~ "Točak miša je uvek slobodan na 0, i uvek zaključan na 50" #~ msgid "Battery information unknown." #~ msgstr "Informacije o bateriji nepoznate." #~ msgid "Count" #~ msgstr "Brojilo" #~ msgid "DPI Sliding Adjustment" #~ msgstr "DPI klizno podešavanje" #, python-format #~ msgid "Found a Logitech Receiver (%s), but did not have permission " #~ "to open it." #~ msgstr "Pronašao sam Logitech prijemnik (%s), ali nisam imao dozvolu " #~ "da ga otvorim." #~ msgid "If you've just installed Solaar, try removing the receiver " #~ "and plugging it back in." #~ msgstr "Ako ste upravo instalirali Solaar, pokušajte da odspojite " #~ "prijemnik i priključite gaponovo." #~ msgid "No Logitech device found" #~ msgstr "Nije pronađen nijedan Logitech uređaj" #~ msgid "Scroll Wheel Rachet" #~ msgstr "Ustavljanje pomicanja kotačića" #~ msgid "Send a gesture by sliding the mouse while holding the button " #~ "down." #~ msgstr "Pošaljite pokret prevlačenjem miša dok držite dugme za dole." #~ msgid "The wireless link between this device and its receiver is " #~ "not encrypted.\n" #~ "\n" #~ "For pointing devices (mice, trackballs, trackpads), this is a minor " #~ "security issue.\n" #~ "\n" #~ "It is, however, a major security issue for text-input devices " #~ "(keyboards, numpads),\n" #~ "because typed text can be sniffed inconspicuously by 3rd parties " #~ "within range." #~ msgstr "Bežična veza između ovog uređaja i njegovog prijemnika nije " #~ "šifrovana.\n" #~ "\n" #~ "Za pokazivačke uređaje (miševi, kuglice za praćenje, dodirne table) " #~ "ovo je manji bezbednosni problem.\n" #~ "Međutim, to je glavni bezbednosni problem za uređaje za unos teksta " #~ "(tastature, numeričke podloge),\n" #~ "jer otkucani tekst mogu neprimetno da nanjuše treće strane u dometu" #~ msgid "Try removing the device and plugging it back in or turning " #~ "it off and then on." #~ msgstr "Pokušajte da odspojite uređaj i ponovo ga uključite ili " #~ "isključite, a zatimupalite." #~ msgid "unknown" #~ msgstr "nepoznato" Solaar-1.1.14/po/sv.po000066400000000000000000001635521473525213700144520ustar00rootroot00000000000000# Swedish translations for solaar package. # Copyright (C) 2013 THE solaar'S COPYRIGHT HOLDER # This file is distributed under the same license as the solaar package. # Automatically generated, 2013. # msgid "" msgstr "" "Project-Id-Version: solaar 1.0.3\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-12-28 17:40+0100\n" "PO-Revision-Date: 2024-11-08 22:12+0200\n" "Last-Translator: Jonatan Nyberg\n" "Language-Team: none\n" "Language: sv\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Poedit 3.2.2\n" "X-Poedit-Basepath: /pwr/Solaar/po\n" "X-Poedit-SearchPath-0: /pwr/Solaar/po\n" #: lib/logitech_receiver/base_usb.py:46 msgid "Bolt Receiver" msgstr "Bolt-mottagare" #: lib/logitech_receiver/base_usb.py:57 msgid "Unifying Receiver" msgstr "Unifying-mottagare" #: lib/logitech_receiver/base_usb.py:67 lib/logitech_receiver/base_usb.py:78 #: lib/logitech_receiver/base_usb.py:90 lib/logitech_receiver/base_usb.py:102 #: lib/logitech_receiver/base_usb.py:114 msgid "Nano Receiver" msgstr "Nano-mottagare" #: lib/logitech_receiver/base_usb.py:124 msgid "Lightspeed Receiver" msgstr "Lightspeed-mottagare" #: lib/logitech_receiver/base_usb.py:133 msgid "EX100 Receiver 27 Mhz" msgstr "EX100-mottagare 27 Mhz" #: lib/logitech_receiver/i18n.py:30 msgid "empty" msgstr "tom" #: lib/logitech_receiver/i18n.py:31 msgid "critical" msgstr "kritisk" #: lib/logitech_receiver/i18n.py:32 msgid "low" msgstr "låg" #: lib/logitech_receiver/i18n.py:33 msgid "average" msgstr "genomsnitt" #: lib/logitech_receiver/i18n.py:34 msgid "good" msgstr "god" #: lib/logitech_receiver/i18n.py:35 msgid "full" msgstr "full" #: lib/logitech_receiver/i18n.py:38 msgid "discharging" msgstr "urladdning" #: lib/logitech_receiver/i18n.py:39 msgid "recharging" msgstr "uppladdning" #: lib/logitech_receiver/i18n.py:40 lib/solaar/ui/window.py:711 msgid "charging" msgstr "laddar" #: lib/logitech_receiver/i18n.py:41 msgid "not charging" msgstr "laddar inte" #: lib/logitech_receiver/i18n.py:42 msgid "almost full" msgstr "nästan full" #: lib/logitech_receiver/i18n.py:43 msgid "charged" msgstr "laddad" #: lib/logitech_receiver/i18n.py:44 msgid "slow recharge" msgstr "långsam laddning" #: lib/logitech_receiver/i18n.py:45 msgid "invalid battery" msgstr "batterifel" #: lib/logitech_receiver/i18n.py:46 msgid "thermal error" msgstr "termiskt fel" #: lib/logitech_receiver/i18n.py:47 msgid "error" msgstr "fel" #: lib/logitech_receiver/i18n.py:48 msgid "standard" msgstr "standard" #: lib/logitech_receiver/i18n.py:49 msgid "fast" msgstr "snabb" #: lib/logitech_receiver/i18n.py:50 msgid "slow" msgstr "långsam" #: lib/logitech_receiver/i18n.py:53 msgid "device timeout" msgstr "enheten svarade inte" #: lib/logitech_receiver/i18n.py:54 msgid "device not supported" msgstr "enheten stöds inte" #: lib/logitech_receiver/i18n.py:55 msgid "too many devices" msgstr "för många enheter" #: lib/logitech_receiver/i18n.py:56 msgid "sequence timeout" msgstr "sekvens timout" #: lib/logitech_receiver/i18n.py:59 lib/solaar/ui/window.py:572 msgid "Firmware" msgstr "Programvara" #: lib/logitech_receiver/i18n.py:60 msgid "Bootloader" msgstr "Startladdare" #: lib/logitech_receiver/i18n.py:61 msgid "Hardware" msgstr "Hårdvara" #: lib/logitech_receiver/i18n.py:62 msgid "Other" msgstr "Annan" #: lib/logitech_receiver/i18n.py:65 msgid "Left Button" msgstr "Vänster knapp" #: lib/logitech_receiver/i18n.py:66 msgid "Right Button" msgstr "Höger knapp" #: lib/logitech_receiver/i18n.py:67 msgid "Middle Button" msgstr "Mittenknapp" #: lib/logitech_receiver/i18n.py:68 msgid "Back Button" msgstr "Tillbakaknapp" #: lib/logitech_receiver/i18n.py:69 msgid "Forward Button" msgstr "Framåtknapp" #: lib/logitech_receiver/i18n.py:70 msgid "Mouse Gesture Button" msgstr "Mus gestknapp" #: lib/logitech_receiver/i18n.py:71 msgid "Smart Shift" msgstr "Smart skift" #: lib/logitech_receiver/i18n.py:72 msgid "DPI Switch" msgstr "DPI-växel" #: lib/logitech_receiver/i18n.py:73 msgid "Left Tilt" msgstr "Vänster lutning" #: lib/logitech_receiver/i18n.py:74 msgid "Right Tilt" msgstr "Höger lutning" #: lib/logitech_receiver/i18n.py:75 msgid "Left Click" msgstr "Vänsterklick" #: lib/logitech_receiver/i18n.py:76 msgid "Right Click" msgstr "Högerklick" #: lib/logitech_receiver/i18n.py:77 msgid "Mouse Middle Button" msgstr "Mus mittenknapp" #: lib/logitech_receiver/i18n.py:78 msgid "Mouse Back Button" msgstr "Mus tillbakaknapp" #: lib/logitech_receiver/i18n.py:79 msgid "Mouse Forward Button" msgstr "Mus framåtknapp" #: lib/logitech_receiver/i18n.py:80 msgid "Gesture Button Navigation" msgstr "Gestknapp navigering" #: lib/logitech_receiver/i18n.py:81 msgid "Mouse Scroll Left Button" msgstr "Vänster musrullningsknapp" #: lib/logitech_receiver/i18n.py:82 msgid "Mouse Scroll Right Button" msgstr "Höger musrullningsknapp" #: lib/logitech_receiver/i18n.py:85 msgid "pressed" msgstr "nedtryckt" #: lib/logitech_receiver/i18n.py:86 msgid "released" msgstr "släpptes" #: lib/logitech_receiver/notifications.py:75 #: lib/logitech_receiver/notifications.py:126 msgid "pairing lock is closed" msgstr "parkopplingslåset är stängt" #: lib/logitech_receiver/notifications.py:75 #: lib/logitech_receiver/notifications.py:126 msgid "pairing lock is open" msgstr "parkopplingslåset är öppet" #: lib/logitech_receiver/notifications.py:92 msgid "discovery lock is closed" msgstr "upptäcktslåset är stängt" #: lib/logitech_receiver/notifications.py:92 msgid "discovery lock is open" msgstr "upptäcktslåset är öppet" #: lib/logitech_receiver/notifications.py:224 lib/solaar/ui/notify.py:122 msgid "connected" msgstr "ansluten" #: lib/logitech_receiver/notifications.py:224 msgid "disconnected" msgstr "frånkopplad" #: lib/logitech_receiver/notifications.py:262 lib/solaar/ui/notify.py:120 msgid "unpaired" msgstr "inte parkopplad" #: lib/logitech_receiver/notifications.py:304 msgid "powered on" msgstr "påslagen" #: lib/logitech_receiver/settings.py:750 msgid "register" msgstr "registrera" #: lib/logitech_receiver/settings.py:764 lib/logitech_receiver/settings.py:791 msgid "feature" msgstr "funktion" #: lib/logitech_receiver/settings_templates.py:139 msgid "Swap Fx function" msgstr "Skifta Fx-funktion" #: lib/logitech_receiver/settings_templates.py:140 msgid "" "When set, the F1..F12 keys will activate their special function,\n" "and you must hold the FN key to activate their standard function." msgstr "" "När de är inställda kommer F1..F12-tangenterna att aktivera sin " "specialfunktion,\n" "och du måste hålla ner FN-tangenten för att aktivera deras standardfunktion." #: lib/logitech_receiver/settings_templates.py:142 msgid "" "When unset, the F1..F12 keys will activate their standard function,\n" "and you must hold the FN key to activate their special function." msgstr "" "När de inte är inställda kommer F1..F12-tangenterna att aktivera sin " "standardfunktion,\n" "och du måste hålla ner FN-tangenten för att aktivera deras specialfunktion." #: lib/logitech_receiver/settings_templates.py:149 msgid "Hand Detection" msgstr "Handavkänning" #: lib/logitech_receiver/settings_templates.py:150 msgid "Turn on illumination when the hands hover over the keyboard." msgstr "Tänd belysning när händerna hålls över tangentbordet." #: lib/logitech_receiver/settings_templates.py:157 msgid "Scroll Wheel Smooth Scrolling" msgstr "Rullningshjul mjuk rullning" #: lib/logitech_receiver/settings_templates.py:158 #: lib/logitech_receiver/settings_templates.py:239 #: lib/logitech_receiver/settings_templates.py:267 msgid "High-sensitivity mode for vertical scroll with the wheel." msgstr "Högkänsligt läge för vertikal rullning med hjulet." #: lib/logitech_receiver/settings_templates.py:165 msgid "Side Scrolling" msgstr "Sidrullning" #: lib/logitech_receiver/settings_templates.py:167 msgid "" "When disabled, pushing the wheel sideways sends custom button events\n" "instead of the standard side-scrolling events." msgstr "" "Vid inaktivering skickas anpassade knapphändelser om du trycker på hjulet i " "sidled\n" "istället för standardhändelserna för sidrullning." #: lib/logitech_receiver/settings_templates.py:177 msgid "Sensitivity (DPI - older mice)" msgstr "Känslighet (DPI - äldre möss)" #: lib/logitech_receiver/settings_templates.py:178 #: lib/logitech_receiver/settings_templates.py:712 msgid "Mouse movement sensitivity" msgstr "Musrörelsekänslighet" #: lib/logitech_receiver/settings_templates.py:208 #: lib/logitech_receiver/settings_templates.py:218 #: lib/logitech_receiver/settings_templates.py:225 msgid "Backlight" msgstr "Bakbelysning" #: lib/logitech_receiver/settings_templates.py:209 #: lib/logitech_receiver/settings_templates.py:226 msgid "Set illumination time for keyboard." msgstr "Ställ in belysningstid för tangentbordet." #: lib/logitech_receiver/settings_templates.py:219 msgid "Turn illumination on or off on keyboard." msgstr "Slå på eller stäng av belysningen på tangentbordet." #: lib/logitech_receiver/settings_templates.py:237 msgid "Scroll Wheel High Resolution" msgstr "Rullningshjul hög upplösning" #: lib/logitech_receiver/settings_templates.py:240 #: lib/logitech_receiver/settings_templates.py:268 msgid "Set to ignore if scrolling is abnormally fast or slow" msgstr "Ställ in att ignorera om rullningen är onormalt snabb eller långsam" #: lib/logitech_receiver/settings_templates.py:247 #: lib/logitech_receiver/settings_templates.py:277 msgid "Scroll Wheel Diversion" msgstr "Rullhjulsavledning" #: lib/logitech_receiver/settings_templates.py:249 msgid "" "Make scroll wheel send LOWRES_WHEEL HID++ notifications (which trigger " "Solaar rules but are otherwise ignored)." msgstr "" "Få rullhjulet att skicka LOWRES_WHEEL HID++-aviseringar (som utlöser Solaar-" "regler men som annars ignoreras)." #: lib/logitech_receiver/settings_templates.py:256 msgid "Scroll Wheel Direction" msgstr "Rullhjulets riktning" #: lib/logitech_receiver/settings_templates.py:257 msgid "Invert direction for vertical scroll with wheel." msgstr "Invertera riktning för vertikal rullning med hjul." #: lib/logitech_receiver/settings_templates.py:265 msgid "Scroll Wheel Resolution" msgstr "Rullhjulsupplösning" #: lib/logitech_receiver/settings_templates.py:279 msgid "" "Make scroll wheel send HIRES_WHEEL HID++ notifications (which trigger Solaar " "rules but are otherwise ignored)." msgstr "" "Få rullhjulet att skicka HIRES_WHEEL HID++-aviseringar (som utlöser Solaar-" "regler men som annars ignoreras)." #: lib/logitech_receiver/settings_templates.py:288 msgid "Sensitivity (Pointer Speed)" msgstr "Känslighet (pekarhastighet)" #: lib/logitech_receiver/settings_templates.py:289 msgid "Speed multiplier for mouse (256 is normal multiplier)." msgstr "Hastighetsmultiplikator för mus (256 är normal multiplikator)." #: lib/logitech_receiver/settings_templates.py:299 msgid "Thumb Wheel Diversion" msgstr "Tumhjulsavledning" #: lib/logitech_receiver/settings_templates.py:301 msgid "" "Make thumb wheel send THUMB_WHEEL HID++ notifications (which trigger Solaar " "rules but are otherwise ignored)." msgstr "" "Få tumhjulet att skicka THUMB_WHEEL HID++-aviseringar (som utlöser Solaar-" "regler men som annars ignoreras)." #: lib/logitech_receiver/settings_templates.py:310 msgid "Thumb Wheel Direction" msgstr "Tumhjulets riktning" #: lib/logitech_receiver/settings_templates.py:311 msgid "Invert thumb wheel scroll direction." msgstr "Invertera tumhjulets rullningsriktning." #: lib/logitech_receiver/settings_templates.py:319 msgid "Onboard Profiles" msgstr "Inbyggda profiler" #: lib/logitech_receiver/settings_templates.py:320 msgid "" "Enable onboard profiles, which often control report rate and keyboard " "lighting" msgstr "" "Aktivera inbyggda profiler, som ofta styr rapporthastighet och " "tangentbordsbelysning" #: lib/logitech_receiver/settings_templates.py:330 msgid "Polling Rate (ms)" msgstr "Uppdateringsfrekvens (ms)" #: lib/logitech_receiver/settings_templates.py:332 msgid "Frequency of device polling, in milliseconds" msgstr "Frekvens för enhetsavfrågning, i millisekunder" #: lib/logitech_receiver/settings_templates.py:333 #: lib/logitech_receiver/settings_templates.py:1046 #: lib/logitech_receiver/settings_templates.py:1076 msgid "May need Onboard Profiles set to Disable to be effective." msgstr "" "Kan behöva inbyggda profiler inställda på Inaktivera för att vara effektiva." #: lib/logitech_receiver/settings_templates.py:365 msgid "Divert crown events" msgstr "Avled kronhändelser" #: lib/logitech_receiver/settings_templates.py:366 msgid "" "Make crown send CROWN HID++ notifications (which trigger Solaar rules but " "are otherwise ignored)." msgstr "" "Få kronan att skicka CROWN HID++-meddelanden (som utlöser Solaar-regler men " "annars ignoreras)." #: lib/logitech_receiver/settings_templates.py:374 msgid "Crown smooth scroll" msgstr "Kronans mjuka rullning" #: lib/logitech_receiver/settings_templates.py:375 msgid "Set crown smooth scroll" msgstr "Ställ in kronans mjuka rullning" #: lib/logitech_receiver/settings_templates.py:383 msgid "Divert G Keys" msgstr "Avleda G Keys" #: lib/logitech_receiver/settings_templates.py:385 msgid "" "Make G keys send GKEY HID++ notifications (which trigger Solaar rules but " "are otherwise ignored)." msgstr "" "Få G keys att skicka GKEY HID++-aviseringar (som utlöser Solaar-regler men " "som annars ignoreras)." #: lib/logitech_receiver/settings_templates.py:386 msgid "May also make M keys and MR key send HID++ notifications" msgstr "Kan också få M keys och MR key att skicka HID++-aviseringar" #: lib/logitech_receiver/settings_templates.py:402 msgid "Scroll Wheel Ratcheted" msgstr "Rullningshjul spärrat" #: lib/logitech_receiver/settings_templates.py:403 msgid "" "Switch the mouse wheel between speed-controlled ratcheting and always " "freespin." msgstr "" "Växla mushjulet mellan hastighetskontrollerad spärrning och alltid fri snurr." #: lib/logitech_receiver/settings_templates.py:405 msgid "Freespinning" msgstr "Fri snurrning" #: lib/logitech_receiver/settings_templates.py:405 msgid "Ratcheted" msgstr "Spärrad" #: lib/logitech_receiver/settings_templates.py:412 msgid "Scroll Wheel Ratchet Speed" msgstr "Rullningshjulets spärrhastighet" #: lib/logitech_receiver/settings_templates.py:414 msgid "" "Use the mouse wheel speed to switch between ratcheted and freespinning.\n" "The mouse wheel is always ratcheted at 50." msgstr "" "Använd mushjulets hastighet för att växla mellan spärrad och fri snurrning.\n" "Mushjulet är alltid spärrat till 50." #: lib/logitech_receiver/settings_templates.py:463 msgid "Key/Button Actions" msgstr "Tangent-/knappåtgärder" #: lib/logitech_receiver/settings_templates.py:465 msgid "Change the action for the key or button." msgstr "Ändra åtgärden för tangenten eller knappen." #: lib/logitech_receiver/settings_templates.py:465 msgid "Overridden by diversion." msgstr "Åsidosatt av avledning." #: lib/logitech_receiver/settings_templates.py:466 msgid "" "Changing important actions (such as for the left mouse button) can result in " "an unusable system." msgstr "" "Ändring av viktiga åtgärder (som för vänster musknapp) kan resultera i ett " "oanvändbart system." #: lib/logitech_receiver/settings_templates.py:639 msgid "Key/Button Diversion" msgstr "Tangent/knapp avledning" #: lib/logitech_receiver/settings_templates.py:640 msgid "" "Make the key or button send HID++ notifications (Diverted) or initiate Mouse " "Gestures or Sliding DPI" msgstr "" "Få tangenten eller knappen att skicka HID++-aviseringar (vidarekopplade) " "eller initiera musgester eller glidande DPI" #: lib/logitech_receiver/settings_templates.py:643 #: lib/logitech_receiver/settings_templates.py:644 #: lib/logitech_receiver/settings_templates.py:645 msgid "Diverted" msgstr "Avledd" #: lib/logitech_receiver/settings_templates.py:643 #: lib/logitech_receiver/settings_templates.py:644 msgid "Mouse Gestures" msgstr "Musgester" #: lib/logitech_receiver/settings_templates.py:643 #: lib/logitech_receiver/settings_templates.py:644 #: lib/logitech_receiver/settings_templates.py:645 msgid "Regular" msgstr "Regelbunden" #: lib/logitech_receiver/settings_templates.py:643 msgid "Sliding DPI" msgstr "Glidande DPI" #: lib/logitech_receiver/settings_templates.py:711 msgid "Sensitivity (DPI)" msgstr "Känslighet (DPI)" #: lib/logitech_receiver/settings_templates.py:752 msgid "Sensitivity Switching" msgstr "Känslighetsväxling" #: lib/logitech_receiver/settings_templates.py:754 msgid "" "Switch the current sensitivity and the remembered sensitivity when the key " "or button is pressed.\n" "If there is no remembered sensitivity, just remember the current sensitivity" msgstr "" "Växla den aktuella känsligheten och den ihågkomna känsligheten när knappen " "eller knappen trycks ned.\n" "Om det inte finns någon ihågkommen känslighet, kom ihåg bara den aktuella " "känsligheten" #: lib/logitech_receiver/settings_templates.py:758 msgid "Off" msgstr "Av" #: lib/logitech_receiver/settings_templates.py:791 msgid "Disable keys" msgstr "Inaktivera tangenter" #: lib/logitech_receiver/settings_templates.py:792 msgid "Disable specific keyboard keys." msgstr "Inaktivera specifika tangentbordstangenter." #: lib/logitech_receiver/settings_templates.py:795 #, python-format msgid "Disables the %s key." msgstr "Inaktiverar tangenten %s." #: lib/logitech_receiver/settings_templates.py:809 #: lib/logitech_receiver/settings_templates.py:860 msgid "Set OS" msgstr "Ställ in OS" #: lib/logitech_receiver/settings_templates.py:810 #: lib/logitech_receiver/settings_templates.py:861 msgid "Change keys to match OS." msgstr "Ändra tangenter för att matcha OS." #: lib/logitech_receiver/settings_templates.py:873 msgid "Change Host" msgstr "Byt värd" #: lib/logitech_receiver/settings_templates.py:874 msgid "Switch connection to a different host" msgstr "Byt anslutning till en annan värd" #: lib/logitech_receiver/settings_templates.py:900 msgid "Performs a left click." msgstr "Utför ett vänsterklick." #: lib/logitech_receiver/settings_templates.py:900 msgid "Single tap" msgstr "Enkelt tryck" #: lib/logitech_receiver/settings_templates.py:901 msgid "Performs a right click." msgstr "Utför ett högerklick." #: lib/logitech_receiver/settings_templates.py:901 msgid "Single tap with two fingers" msgstr "Enkel tryck med två fingrar" #: lib/logitech_receiver/settings_templates.py:902 msgid "Single tap with three fingers" msgstr "Enkel tryck med tre fingrar" #: lib/logitech_receiver/settings_templates.py:906 msgid "Double tap" msgstr "Dubbeltryck" #: lib/logitech_receiver/settings_templates.py:906 msgid "Performs a double click." msgstr "Utför ett dubbelklick." #: lib/logitech_receiver/settings_templates.py:907 msgid "Double tap with two fingers" msgstr "Dubbeltryck med två fingrar" #: lib/logitech_receiver/settings_templates.py:908 msgid "Double tap with three fingers" msgstr "Dubbeltryck med tre fingrar" #: lib/logitech_receiver/settings_templates.py:911 msgid "Drags items by dragging the finger after double tapping." msgstr "Dra objekt genom att dra fingret efter att ha dubbelklickat." #: lib/logitech_receiver/settings_templates.py:911 msgid "Tap and drag" msgstr "Tryck och dra" #: lib/logitech_receiver/settings_templates.py:913 msgid "Drags items by dragging the fingers after double tapping." msgstr "Dra objekt genom att dra fingrarna efter att du dubbelklickat." #: lib/logitech_receiver/settings_templates.py:913 msgid "Tap and drag with two fingers" msgstr "Tryck och dra med två fingrar" #: lib/logitech_receiver/settings_templates.py:914 msgid "Tap and drag with three fingers" msgstr "Tryck och dra med tre fingrar" #: lib/logitech_receiver/settings_templates.py:917 msgid "Disables tap and edge gestures (equivalent to pressing Fn+LeftClick)." msgstr "" "Inaktiverade tryck- och kantgester (motsvarar att trycka på Fn+vänsterklick)." #: lib/logitech_receiver/settings_templates.py:917 msgid "Suppress tap and edge gestures" msgstr "Inaktivera tryck- och kantgester" #: lib/logitech_receiver/settings_templates.py:918 msgid "Scroll with one finger" msgstr "Bläddra med ett finger" #: lib/logitech_receiver/settings_templates.py:918 #: lib/logitech_receiver/settings_templates.py:919 #: lib/logitech_receiver/settings_templates.py:922 msgid "Scrolls." msgstr "Rullningar." #: lib/logitech_receiver/settings_templates.py:919 #: lib/logitech_receiver/settings_templates.py:922 msgid "Scroll with two fingers" msgstr "Rulla med två fingrar" #: lib/logitech_receiver/settings_templates.py:920 msgid "Scroll horizontally with two fingers" msgstr "Rulla horisontellt med två fingrar" #: lib/logitech_receiver/settings_templates.py:920 msgid "Scrolls horizontally." msgstr "Rullar horisontellt." #: lib/logitech_receiver/settings_templates.py:921 msgid "Scroll vertically with two fingers" msgstr "Rulla vertikalt med två fingrar" #: lib/logitech_receiver/settings_templates.py:921 msgid "Scrolls vertically." msgstr "Rullar vertikalt." #: lib/logitech_receiver/settings_templates.py:923 msgid "Inverts the scrolling direction." msgstr "Inverterar rullningsriktningen." #: lib/logitech_receiver/settings_templates.py:923 msgid "Natural scrolling" msgstr "Naturlig rullning" #: lib/logitech_receiver/settings_templates.py:924 msgid "Enables the thumbwheel." msgstr "Aktiverar tumhjulet." #: lib/logitech_receiver/settings_templates.py:924 msgid "Thumbwheel" msgstr "Tumhjul" #: lib/logitech_receiver/settings_templates.py:935 #: lib/logitech_receiver/settings_templates.py:939 msgid "Swipe from the top edge" msgstr "Svep från den övre kanten" #: lib/logitech_receiver/settings_templates.py:936 msgid "Swipe from the left edge" msgstr "Svep från vänstra kanten" #: lib/logitech_receiver/settings_templates.py:937 msgid "Swipe from the right edge" msgstr "Svep från den högra kanten" #: lib/logitech_receiver/settings_templates.py:938 msgid "Swipe from the bottom edge" msgstr "Svep från den nedre kanten" #: lib/logitech_receiver/settings_templates.py:940 msgid "Swipe two fingers from the left edge" msgstr "Svep två fingrar från den vänstra kanten" #: lib/logitech_receiver/settings_templates.py:941 msgid "Swipe two fingers from the right edge" msgstr "Svep två fingrar från den högra kanten" #: lib/logitech_receiver/settings_templates.py:942 msgid "Swipe two fingers from the bottom edge" msgstr "Svep två fingrar från den nedre kanten" #: lib/logitech_receiver/settings_templates.py:943 msgid "Swipe two fingers from the top edge" msgstr "Svep två fingrar från den övre kanten" #: lib/logitech_receiver/settings_templates.py:944 #: lib/logitech_receiver/settings_templates.py:948 msgid "Pinch to zoom out; spread to zoom in." msgstr "Nyp för att zooma ut; sprida för att zooma in." #: lib/logitech_receiver/settings_templates.py:944 msgid "Zoom with two fingers." msgstr "Zooma med två fingrar." #: lib/logitech_receiver/settings_templates.py:945 msgid "Pinch to zoom out." msgstr "Nyp för att zooma ut." #: lib/logitech_receiver/settings_templates.py:946 msgid "Spread to zoom in." msgstr "Sprid för att zooma in." #: lib/logitech_receiver/settings_templates.py:947 msgid "Zoom with three fingers." msgstr "Zooma med tre fingrar." #: lib/logitech_receiver/settings_templates.py:948 msgid "Zoom with two fingers" msgstr "Zooma med två fingrar" #: lib/logitech_receiver/settings_templates.py:966 msgid "Pixel zone" msgstr "Pixelzon" #: lib/logitech_receiver/settings_templates.py:967 msgid "Ratio zone" msgstr "Ratiozon" #: lib/logitech_receiver/settings_templates.py:968 msgid "Scale factor" msgstr "Skalfaktor" #: lib/logitech_receiver/settings_templates.py:968 msgid "Sets the cursor speed." msgstr "Ställer in markörhastigheten." #: lib/logitech_receiver/settings_templates.py:972 msgid "Left" msgstr "Vänster" #: lib/logitech_receiver/settings_templates.py:972 msgid "Left-most coordinate." msgstr "Koordinaten längst till vänster." #: lib/logitech_receiver/settings_templates.py:973 msgid "Bottom" msgstr "Nedre" #: lib/logitech_receiver/settings_templates.py:973 msgid "Bottom coordinate." msgstr "Nedre koordinat." #: lib/logitech_receiver/settings_templates.py:974 msgid "Width" msgstr "Bredd" #: lib/logitech_receiver/settings_templates.py:974 msgid "Width." msgstr "Bredd." #: lib/logitech_receiver/settings_templates.py:975 msgid "Height" msgstr "Höjd" #: lib/logitech_receiver/settings_templates.py:975 msgid "Height." msgstr "Höjd." #: lib/logitech_receiver/settings_templates.py:976 msgid "Cursor speed." msgstr "Markörhastighet." #: lib/logitech_receiver/settings_templates.py:976 msgid "Scale" msgstr "Skala" #: lib/logitech_receiver/settings_templates.py:982 msgid "Gestures" msgstr "Gester" #: lib/logitech_receiver/settings_templates.py:983 msgid "Tweak the mouse/touchpad behaviour." msgstr "Justera musens/pekplattans beteende." #: lib/logitech_receiver/settings_templates.py:1000 msgid "Gestures Diversion" msgstr "Avledning av gester" #: lib/logitech_receiver/settings_templates.py:1001 msgid "Divert mouse/touchpad gestures." msgstr "Avleda mus-/pekplattagester." #: lib/logitech_receiver/settings_templates.py:1018 msgid "Gesture params" msgstr "Gestparametrar" #: lib/logitech_receiver/settings_templates.py:1019 msgid "Change numerical parameters of a mouse/touchpad." msgstr "Ändra numeriska parametrar för en mus/pekplatta." #: lib/logitech_receiver/settings_templates.py:1044 msgid "M-Key LEDs" msgstr "M-Key LED:ar" #: lib/logitech_receiver/settings_templates.py:1046 msgid "Control the M-Key LEDs." msgstr "Styr M-Key LED:ar." #: lib/logitech_receiver/settings_templates.py:1047 #: lib/logitech_receiver/settings_templates.py:1077 msgid "May need G Keys diverted to be effective." msgstr "Kan behöva omdirigera G Keys för att vara effektiva." #: lib/logitech_receiver/settings_templates.py:1053 #, python-format msgid "Lights up the %s key." msgstr "Lyser upp tangenten %s." #: lib/logitech_receiver/settings_templates.py:1074 msgid "MR-Key LED" msgstr "MR-Key LED" #: lib/logitech_receiver/settings_templates.py:1076 msgid "Control the MR-Key LED." msgstr "Styr MR-Key LED." #: lib/logitech_receiver/settings_templates.py:1095 msgid "Persistent Key/Button Mapping" msgstr "Ihållande nyckel/knappkartläggning" #: lib/logitech_receiver/settings_templates.py:1097 msgid "Permanently change the mapping for the key or button." msgstr "Ändra kartläggningen för nyckeln eller knappen permanent." #: lib/logitech_receiver/settings_templates.py:1098 msgid "" "Changing important keys or buttons (such as for the left mouse button) can " "result in an unusable system." msgstr "" "Att ändra viktiga tangenter eller knappar (som för vänster musknapp) kan " "resultera i ett oanvändbart system." #: lib/logitech_receiver/settings_templates.py:1157 msgid "Sidetone" msgstr "Sidoton" #: lib/logitech_receiver/settings_templates.py:1158 msgid "Set sidetone level." msgstr "Ställ in sidotonsnivå." #: lib/logitech_receiver/settings_templates.py:1167 msgid "Equalizer" msgstr "Utjämnare" #: lib/logitech_receiver/settings_templates.py:1168 msgid "Set equalizer levels." msgstr "Ställ in utjämningsnivåer." #: lib/logitech_receiver/settings_templates.py:1191 msgid "Hz" msgstr "Hz" #: lib/logitech_receiver/settings_templates.py:1197 msgid "Power Management" msgstr "Strömhantering" #: lib/logitech_receiver/settings_templates.py:1198 msgid "Power off in minutes (0 for never)." msgstr "Stäng av på några minuter (0 för aldrig)." #: lib/logitech_receiver/status.py:114 msgid "No paired devices." msgstr "Inga parkopplade enheter." #: lib/logitech_receiver/status.py:115 lib/solaar/ui/window.py:622 #, python-format msgid "%(count)s paired device." msgid_plural "%(count)s paired devices." msgstr[0] "%(count)s parkopplad enhet." msgstr[1] "%(count)s parkopplade enheter." #: lib/logitech_receiver/status.py:170 #, python-format msgid "Battery: %(level)s" msgstr "Batteri: %(level)s" #: lib/logitech_receiver/status.py:172 #, python-format msgid "Battery: %(percent)d%%" msgstr "Batteri: %(percent)d%%" #: lib/logitech_receiver/status.py:184 #, python-format msgid "Lighting: %(level)s lux" msgstr "Belysning: %(level)s lux" #: lib/logitech_receiver/status.py:239 #, python-format msgid "Battery: %(level)s (%(status)s)" msgstr "Batteri: %(level)s (%(status)s)" #: lib/logitech_receiver/status.py:241 #, python-format msgid "Battery: %(percent)d%% (%(status)s)" msgstr "Batteri: %(percent)d%% (%(status)s)" #: lib/solaar/ui/__init__.py:52 msgid "Permissions error" msgstr "Behörighetsfel" #: lib/solaar/ui/__init__.py:54 #, python-format msgid "" "Found a Logitech receiver or device (%s), but did not have permission to " "open it." msgstr "" "Hittade en Logitech-mottagare eller enhet (%s), men hade inte behörighet att " "öppna den." #: lib/solaar/ui/__init__.py:55 msgid "" "If you've just installed Solaar, try disconnecting the receiver or device " "and then reconnecting it." msgstr "" "Om du precis har installerat Solaar, försök att koppla bort mottagaren eller " "enheten och sedan återansluta den." #: lib/solaar/ui/__init__.py:58 msgid "Cannot connect to device error" msgstr "Kan inte ansluta till enhet-fel" #: lib/solaar/ui/__init__.py:60 #, python-format msgid "" "Found a Logitech receiver or device at %s, but encountered an error " "connecting to it." msgstr "" "Hittade en Logitech-mottagare eller enhet på %s, men stötte på ett fel vid " "anslutning till den." #: lib/solaar/ui/__init__.py:61 msgid "" "Try disconnecting the device and then reconnecting it or turning it off and " "then on." msgstr "" "Försök att koppla bort enheten och sedan återansluta den eller stänga av och " "sedan på den." #: lib/solaar/ui/__init__.py:64 msgid "Unpairing failed" msgstr "Kunde inte bryta parkoppling" #: lib/solaar/ui/__init__.py:66 #, python-brace-format msgid "Failed to unpair %{device} from %{receiver}." msgstr "Misslyckades att bryta parkoppling mellan %{device} och %{receiver}." #: lib/solaar/ui/__init__.py:67 msgid "The receiver returned an error, with no further details." msgstr "Mottagaren rapporterade ett fel, utan ytterligare detaljer." #: lib/solaar/ui/__init__.py:177 msgid "Another Solaar process is already running so just expose its window" msgstr "En annan Solaar-process körs redan så bara exponera dess fönster" #: lib/solaar/ui/about.py:36 msgid "" "Manages Logitech receivers,\n" "keyboards, mice, and tablets." msgstr "" "Hanterar Logitech-mottagare,\n" "tangentbord, möss och surfplattor." #: lib/solaar/ui/about.py:44 msgid "Additional Programming" msgstr "Ytterligare programmering" #: lib/solaar/ui/about.py:45 msgid "GUI design" msgstr "Grafisk design av användargränssnitt" #: lib/solaar/ui/about.py:47 msgid "Testing" msgstr "Testning" #: lib/solaar/ui/about.py:54 msgid "Logitech documentation" msgstr "Logitech-dokumentation" #: lib/solaar/ui/action.py:85 lib/solaar/ui/action.py:89 #: lib/solaar/ui/window.py:197 msgid "Unpair" msgstr "Bryt parkoppling" #: lib/solaar/ui/action.py:88 lib/solaar/ui/diversion_rules.py:150 msgid "Cancel" msgstr "Avbryt" #: lib/solaar/ui/config_panel.py:212 msgid "Complete - ENTER to change" msgstr "Komplett - ENTER för att ändra" #: lib/solaar/ui/config_panel.py:212 msgid "Incomplete" msgstr "Ofullständig" #: lib/solaar/ui/config_panel.py:455 lib/solaar/ui/config_panel.py:507 #, python-format msgid "%d value" msgid_plural "%d values" msgstr[0] "%d värde" msgstr[1] "%d värden" #: lib/solaar/ui/config_panel.py:518 msgid "Changes allowed" msgstr "Ändringar tillåtna" #: lib/solaar/ui/config_panel.py:519 msgid "No changes allowed" msgstr "Inga ändringar tillåtna" #: lib/solaar/ui/config_panel.py:520 msgid "Ignore this setting" msgstr "Ignorera denna inställning" #: lib/solaar/ui/config_panel.py:565 msgid "Working" msgstr "Fungerar" #: lib/solaar/ui/config_panel.py:568 msgid "Read/write operation failed." msgstr "Läs-/skrivoperationen misslyckades." #: lib/solaar/ui/diversion_rules.py:65 msgid "Built-in rules" msgstr "Inbyggda regler" #: lib/solaar/ui/diversion_rules.py:65 msgid "User-defined rules" msgstr "Användardefinierade regler" #: lib/solaar/ui/diversion_rules.py:67 lib/solaar/ui/diversion_rules.py:1083 msgid "Rule" msgstr "Regel" #: lib/solaar/ui/diversion_rules.py:68 lib/solaar/ui/diversion_rules.py:509 #: lib/solaar/ui/diversion_rules.py:636 msgid "Sub-rule" msgstr "Underregel" #: lib/solaar/ui/diversion_rules.py:70 msgid "[empty]" msgstr "[tom]" #: lib/solaar/ui/diversion_rules.py:94 msgid "Solaar Rule Editor" msgstr "Solaar regelredigerare" #: lib/solaar/ui/diversion_rules.py:141 msgid "Make changes permanent?" msgstr "Göra ändringarna permanenta?" #: lib/solaar/ui/diversion_rules.py:146 msgid "Yes" msgstr "Ja" #: lib/solaar/ui/diversion_rules.py:148 msgid "No" msgstr "Nej" #: lib/solaar/ui/diversion_rules.py:153 msgid "If you choose No, changes will be lost when Solaar is closed." msgstr "Om du väljer Nej kommer ändringar att gå förlorade när Solaar stängs." #: lib/solaar/ui/diversion_rules.py:201 msgid "Save changes" msgstr "Spara ändringar" #: lib/solaar/ui/diversion_rules.py:206 msgid "Discard changes" msgstr "Släng ändringar" #: lib/solaar/ui/diversion_rules.py:372 msgid "Insert here" msgstr "Infoga här" #: lib/solaar/ui/diversion_rules.py:374 msgid "Insert above" msgstr "Infoga ovan" #: lib/solaar/ui/diversion_rules.py:376 msgid "Insert below" msgstr "Infoga nedan" #: lib/solaar/ui/diversion_rules.py:382 msgid "Insert new rule here" msgstr "Infoga ny regel här" #: lib/solaar/ui/diversion_rules.py:384 msgid "Insert new rule above" msgstr "Infoga ny regel ovan" #: lib/solaar/ui/diversion_rules.py:386 msgid "Insert new rule below" msgstr "Infoga ny regel nedan" #: lib/solaar/ui/diversion_rules.py:427 msgid "Paste here" msgstr "Klistra in här" #: lib/solaar/ui/diversion_rules.py:429 msgid "Paste above" msgstr "Klistra in ovan" #: lib/solaar/ui/diversion_rules.py:431 msgid "Paste below" msgstr "Klistra in nedan" #: lib/solaar/ui/diversion_rules.py:437 msgid "Paste rule here" msgstr "Klistra in regeln här" #: lib/solaar/ui/diversion_rules.py:439 msgid "Paste rule above" msgstr "Klistra in regeln ovan" #: lib/solaar/ui/diversion_rules.py:441 msgid "Paste rule below" msgstr "Klistra in regeln nedan" #: lib/solaar/ui/diversion_rules.py:445 msgid "Paste rule" msgstr "Klistra in regel" #: lib/solaar/ui/diversion_rules.py:474 msgid "Flatten" msgstr "Platta till" #: lib/solaar/ui/diversion_rules.py:507 msgid "Insert" msgstr "Infoga" #: lib/solaar/ui/diversion_rules.py:510 lib/solaar/ui/diversion_rules.py:638 #: lib/solaar/ui/diversion_rules.py:1126 msgid "Or" msgstr "Eller" #: lib/solaar/ui/diversion_rules.py:511 lib/solaar/ui/diversion_rules.py:637 #: lib/solaar/ui/diversion_rules.py:1111 msgid "And" msgstr "Och" #: lib/solaar/ui/diversion_rules.py:513 msgid "Condition" msgstr "Tillstånd" #: lib/solaar/ui/diversion_rules.py:515 lib/solaar/ui/diversion_rules.py:1292 msgid "Feature" msgstr "Funktion" #: lib/solaar/ui/diversion_rules.py:516 lib/solaar/ui/diversion_rules.py:1328 msgid "Report" msgstr "Rapport" #: lib/solaar/ui/diversion_rules.py:517 lib/solaar/ui/diversion_rules.py:1204 msgid "Process" msgstr "Process" #: lib/solaar/ui/diversion_rules.py:518 msgid "Mouse process" msgstr "Musprocess" #: lib/solaar/ui/diversion_rules.py:519 lib/solaar/ui/diversion_rules.py:1366 msgid "Modifiers" msgstr "Modifierare" #: lib/solaar/ui/diversion_rules.py:520 lib/solaar/ui/diversion_rules.py:1419 msgid "Key" msgstr "Tangent" #: lib/solaar/ui/diversion_rules.py:521 lib/solaar/ui/diversion_rules.py:1461 msgid "KeyIsDown" msgstr "KeyIsDown" #: lib/solaar/ui/diversion_rules.py:522 lib/solaar/ui/diversion_rules.py:2260 msgid "Active" msgstr "Aktiv" #: lib/solaar/ui/diversion_rules.py:523 lib/solaar/ui/diversion_rules.py:2218 #: lib/solaar/ui/diversion_rules.py:2270 lib/solaar/ui/diversion_rules.py:2323 msgid "Device" msgstr "Enhet" #: lib/solaar/ui/diversion_rules.py:524 lib/solaar/ui/diversion_rules.py:2297 msgid "Host" msgstr "Värd" #: lib/solaar/ui/diversion_rules.py:525 lib/solaar/ui/diversion_rules.py:2339 msgid "Setting" msgstr "Inställning" #: lib/solaar/ui/diversion_rules.py:526 lib/solaar/ui/diversion_rules.py:1477 #: lib/solaar/ui/diversion_rules.py:1526 msgid "Test" msgstr "Testa" #: lib/solaar/ui/diversion_rules.py:527 lib/solaar/ui/diversion_rules.py:1643 msgid "Test bytes" msgstr "Testa bytes" #: lib/solaar/ui/diversion_rules.py:528 lib/solaar/ui/diversion_rules.py:1736 msgid "Mouse Gesture" msgstr "Musgest" #: lib/solaar/ui/diversion_rules.py:532 msgid "Action" msgstr "Åtgärd" #: lib/solaar/ui/diversion_rules.py:534 lib/solaar/ui/diversion_rules.py:1845 msgid "Key press" msgstr "Knapptryckning" #: lib/solaar/ui/diversion_rules.py:535 lib/solaar/ui/diversion_rules.py:1897 msgid "Mouse scroll" msgstr "Musrullning" #: lib/solaar/ui/diversion_rules.py:536 lib/solaar/ui/diversion_rules.py:1959 msgid "Mouse click" msgstr "Musklick" #: lib/solaar/ui/diversion_rules.py:537 msgid "Set" msgstr "Ställ in" #: lib/solaar/ui/diversion_rules.py:538 lib/solaar/ui/diversion_rules.py:2030 msgid "Execute" msgstr "Utför" #: lib/solaar/ui/diversion_rules.py:539 lib/solaar/ui/diversion_rules.py:1158 msgid "Later" msgstr "Senare" #: lib/solaar/ui/diversion_rules.py:568 msgid "Insert new rule" msgstr "Infoga ny regel" #: lib/solaar/ui/diversion_rules.py:588 lib/solaar/ui/diversion_rules.py:1686 #: lib/solaar/ui/diversion_rules.py:1790 lib/solaar/ui/diversion_rules.py:1989 msgid "Delete" msgstr "Ta bort" #: lib/solaar/ui/diversion_rules.py:610 msgid "Negate" msgstr "Negera" #: lib/solaar/ui/diversion_rules.py:634 msgid "Wrap with" msgstr "Omslut med" #: lib/solaar/ui/diversion_rules.py:656 msgid "Cut" msgstr "Klipp ut" #: lib/solaar/ui/diversion_rules.py:671 msgid "Paste" msgstr "Klistra in" #: lib/solaar/ui/diversion_rules.py:683 msgid "Copy" msgstr "Kopiera" #: lib/solaar/ui/diversion_rules.py:1063 msgid "This editor does not support the selected rule component yet." msgstr "Denna redigerare stöder inte den valda regelkomponenten ännu." #: lib/solaar/ui/diversion_rules.py:1138 msgid "Number of seconds to delay." msgstr "Antal sekunder att fördröja." #: lib/solaar/ui/diversion_rules.py:1177 msgid "Not" msgstr "Inte" #: lib/solaar/ui/diversion_rules.py:1187 msgid "X11 active process. For use in X11 only." msgstr "X11 aktiv process. Endast för användning i X11." #: lib/solaar/ui/diversion_rules.py:1218 msgid "X11 mouse process. For use in X11 only." msgstr "X11 musprocess. Endast för användning i X11." #: lib/solaar/ui/diversion_rules.py:1235 msgid "MouseProcess" msgstr "MouseProcess" #: lib/solaar/ui/diversion_rules.py:1260 msgid "Feature name of notification triggering rule processing." msgstr "Funktionsnamn för avisering som utlöser regelbearbetning." #: lib/solaar/ui/diversion_rules.py:1308 msgid "Report number of notification triggering rule processing." msgstr "Rapportnummer för avisering som utlöser regelbearbetning." #: lib/solaar/ui/diversion_rules.py:1342 msgid "Active keyboard modifiers. Not always available in Wayland." msgstr "Aktiva tangentbordsmodifierare. Inte alltid tillgängligt i Wayland." #: lib/solaar/ui/diversion_rules.py:1383 msgid "" "Diverted key or button depressed or released.\n" "Use the Key/Button Diversion and Divert G Keys settings to divert keys and " "buttons." msgstr "" "Omdirigerad knapp eller knapp nedtryckt eller släppt.\n" "Använd inställningarna för vidarekoppling av tangent/knapp och " "vidarekoppling av G-knappar för att vidarekoppla nycklar och knappar." #: lib/solaar/ui/diversion_rules.py:1392 msgid "Key down" msgstr "Tangent ner" #: lib/solaar/ui/diversion_rules.py:1395 msgid "Key up" msgstr "Tangent upp" #: lib/solaar/ui/diversion_rules.py:1436 msgid "" "Diverted key or button is currently down.\n" "Use the Key/Button Diversion and Divert G Keys settings to divert keys and " "buttons." msgstr "" "Vidarekopplad nyckel eller knapp är för närvarande nere.\n" "Använd inställningarna för vidarekoppling av tangent/knapp och " "vidarekoppling av G-knappar för att vidarekoppla nycklar och knappar." #: lib/solaar/ui/diversion_rules.py:1475 msgid "Test condition on notification triggering rule processing." msgstr "Testvillkor för meddelande som utlöser regelbearbetning." #: lib/solaar/ui/diversion_rules.py:1479 msgid "Parameter" msgstr "Parameter" #: lib/solaar/ui/diversion_rules.py:1542 msgid "begin (inclusive)" msgstr "börja (inklusive)" #: lib/solaar/ui/diversion_rules.py:1543 msgid "end (exclusive)" msgstr "slut (exklusivt)" #: lib/solaar/ui/diversion_rules.py:1552 msgid "range" msgstr "omfång" #: lib/solaar/ui/diversion_rules.py:1554 msgid "minimum" msgstr "minimum" #: lib/solaar/ui/diversion_rules.py:1555 msgid "maximum" msgstr "maximum" #: lib/solaar/ui/diversion_rules.py:1557 #, python-format msgid "bytes %(0)d to %(1)d, ranging from %(2)d to %(3)d" msgstr "byte %(0)d till %(1)d, från %(2)d till %(3)d" #: lib/solaar/ui/diversion_rules.py:1562 msgid "mask" msgstr "mask" #: lib/solaar/ui/diversion_rules.py:1563 #, python-format msgid "bytes %(0)d to %(1)d, mask %(2)d" msgstr "byte %(0)d till %(1)d, mask %(2)d" #: lib/solaar/ui/diversion_rules.py:1573 msgid "" "Bit or range test on bytes in notification message triggering rule " "processing." msgstr "" "Bit- eller omfångstest på bytes i aviseringsmeddelande som utlöser " "regelbearbetning." #: lib/solaar/ui/diversion_rules.py:1583 msgid "type" msgstr "typ" #: lib/solaar/ui/diversion_rules.py:1666 msgid "" "Mouse gesture with optional initiating button followed by zero or more mouse " "movements." msgstr "" "Musgest med valfri initieringsknapp följt av noll eller fler musrörelser." #: lib/solaar/ui/diversion_rules.py:1671 msgid "Add movement" msgstr "Lägg till rörelse" #: lib/solaar/ui/diversion_rules.py:1764 msgid "" "Simulate a chorded key click or depress or release.\n" "On Wayland requires write access to /dev/uinput." msgstr "" "Simulera ett ackorderat tangentklick eller tryck ner eller släpp.\n" "I Wayland krävs skrivåtkomst till /dev/uinput." #: lib/solaar/ui/diversion_rules.py:1769 msgid "Add key" msgstr "Lägg till tangent" #: lib/solaar/ui/diversion_rules.py:1772 msgid "Click" msgstr "Klick" #: lib/solaar/ui/diversion_rules.py:1775 msgid "Depress" msgstr "Tryck ned" #: lib/solaar/ui/diversion_rules.py:1778 msgid "Release" msgstr "Släppa" #: lib/solaar/ui/diversion_rules.py:1861 msgid "" "Simulate a mouse scroll.\n" "On Wayland requires write access to /dev/uinput." msgstr "" "Simulera en musrullning.\n" "I Wayland krävs skrivåtkomst till /dev/uinput." #: lib/solaar/ui/diversion_rules.py:1918 msgid "" "Simulate a mouse click.\n" "On Wayland requires write access to /dev/uinput." msgstr "" "Simulera ett musklick.\n" "I Wayland krävs skrivåtkomst till /dev/uinput." #: lib/solaar/ui/diversion_rules.py:1921 msgid "Button" msgstr "Knapp" #: lib/solaar/ui/diversion_rules.py:1922 msgid "Count and Action" msgstr "Antal och åtgärd" #: lib/solaar/ui/diversion_rules.py:1972 msgid "Execute a command with arguments." msgstr "Utför ett kommando med argument." #: lib/solaar/ui/diversion_rules.py:1975 msgid "Add argument" msgstr "Lägg till argument" #: lib/solaar/ui/diversion_rules.py:2050 msgid "Toggle" msgstr "Växla" #: lib/solaar/ui/diversion_rules.py:2050 msgid "True" msgstr "Sant" #: lib/solaar/ui/diversion_rules.py:2051 msgid "False" msgstr "Falskt" #: lib/solaar/ui/diversion_rules.py:2065 msgid "Unsupported setting" msgstr "Inställning som inte stöds" #: lib/solaar/ui/diversion_rules.py:2223 lib/solaar/ui/diversion_rules.py:2242 #: lib/solaar/ui/diversion_rules.py:2328 lib/solaar/ui/diversion_rules.py:2570 #: lib/solaar/ui/diversion_rules.py:2588 msgid "Originating device" msgstr "Ursprungsenhet" #: lib/solaar/ui/diversion_rules.py:2256 msgid "Device is active and its settings can be changed." msgstr "Enheten är aktiv och dess inställningar kan ändras." #: lib/solaar/ui/diversion_rules.py:2266 msgid "Device that originated the current notification." msgstr "Enhet som skapade det aktuella meddelandet." #: lib/solaar/ui/diversion_rules.py:2280 msgid "Name of host computer." msgstr "Namn på värddator." #: lib/solaar/ui/diversion_rules.py:2347 msgid "Value" msgstr "Värde" #: lib/solaar/ui/diversion_rules.py:2355 msgid "Item" msgstr "Objekt" #: lib/solaar/ui/diversion_rules.py:2630 msgid "Change setting on device" msgstr "Ändra inställning på enheten" #: lib/solaar/ui/diversion_rules.py:2647 msgid "Setting on device" msgstr "Inställning på enheten" #: lib/solaar/ui/notify.py:124 lib/solaar/ui/tray.py:318 #: lib/solaar/ui/tray.py:323 lib/solaar/ui/window.py:739 msgid "offline" msgstr "frånkopplad" #: lib/solaar/ui/pair_window.py:122 lib/solaar/ui/pair_window.py:256 #: lib/solaar/ui/pair_window.py:288 #, python-format msgid "%(receiver_name)s: pair new device" msgstr "%(receiver_name)s: parkoppla ny enhet" #: lib/solaar/ui/pair_window.py:123 #, python-format msgid "Enter passcode on %(name)s." msgstr "Ange lösenkoden på %(name)s." #: lib/solaar/ui/pair_window.py:126 #, python-format msgid "Type %(passcode)s and then press the enter key." msgstr "Skriv %(passcode)s och tryck sedan på enter-tangenten." #: lib/solaar/ui/pair_window.py:129 msgid "left" msgstr "vänster" #: lib/solaar/ui/pair_window.py:129 msgid "right" msgstr "höger" #: lib/solaar/ui/pair_window.py:131 #, python-format msgid "" "Press %(code)s\n" "and then press left and right buttons simultaneously." msgstr "" "Tryck på %(code)s\n" "och tryck sedan på vänster och höger knappar samtidigt." #: lib/solaar/ui/pair_window.py:188 msgid "Pairing failed" msgstr "Parkoppling misslyckades" #: lib/solaar/ui/pair_window.py:190 msgid "Make sure your device is within range, and has a decent battery charge." msgstr "" "Se till att din enhet är inom räckhåll och har tillräckligt laddat batteri." #: lib/solaar/ui/pair_window.py:192 msgid "A new device was detected, but it is not compatible with this receiver." msgstr "" "En ny enhet upptäcktes, men den är inte kompatibel med denna mottagare." #: lib/solaar/ui/pair_window.py:194 msgid "More paired devices than receiver can support." msgstr "Fler parkopplade enheter än vad mottagaren kan stöda." #: lib/solaar/ui/pair_window.py:196 msgid "No further details are available about the error." msgstr "Inga ytterligare detaljer finns tillgängliga om felet." #: lib/solaar/ui/pair_window.py:210 msgid "Found a new device:" msgstr "Hittade en ny enhet:" #: lib/solaar/ui/pair_window.py:235 msgid "The wireless link is not encrypted" msgstr "Den trådlösa anslutningen är inte krypterad" #: lib/solaar/ui/pair_window.py:264 msgid "Unifying receivers are only compatible with Unifying devices." msgstr "Unifying-mottagare är endast kompatibla med Unifying-enheter." #: lib/solaar/ui/pair_window.py:266 msgid "Bolt receivers are only compatible with Bolt devices." msgstr "Bolt-mottagare är endast kompatibla med Bolt-enheter." #: lib/solaar/ui/pair_window.py:268 msgid "Other receivers are only compatible with a few devices." msgstr "Andra mottagare är endast kompatibla med ett fåtal enheter." #: lib/solaar/ui/pair_window.py:270 msgid "The device must not be paired with a nearby powered-on receiver." msgstr "Enheten får inte parkopplas med en närliggande påslagen mottagare." #: lib/solaar/ui/pair_window.py:274 msgid "Press a pairing button or key until the pairing light flashes quickly." msgstr "" "Tryck på en parkopplingstangent eller -knapp tills parkopplingslampan " "blinkar snabbt." #: lib/solaar/ui/pair_window.py:276 msgid "You may have to first turn the device off and on again." msgstr "Du kanske först måste stänga av enheten och slå på den igen." #: lib/solaar/ui/pair_window.py:278 msgid "Turn on the device you want to pair." msgstr "Slå på enheten du vill parkoppla." #: lib/solaar/ui/pair_window.py:280 msgid "If the device is already turned on, turn it off and on again." msgstr "Om enheten redan är påslagen, stäng av och sätt på den igen." #: lib/solaar/ui/pair_window.py:283 #, python-format msgid "" "\n" "\n" "This receiver has %d pairing remaining." msgid_plural "" "\n" "\n" "This receiver has %d pairings remaining." msgstr[0] "" "\n" "\n" "Denna mottagare har %d parkoppling kvar." msgstr[1] "" "\n" "\n" "Denna mottagare har %d parkopplingar kvar." #: lib/solaar/ui/pair_window.py:286 msgid "" "\n" "Cancelling at this point will not use up a pairing." msgstr "" "\n" "Avbrytning vid detta tillfälle kommer inte att förbruka en parkoppling." #: lib/solaar/ui/tray.py:58 msgid "No supported device found" msgstr "Ingen enhet som stöds hittades" #: lib/solaar/ui/tray.py:64 lib/solaar/ui/window.py:319 #, python-format msgid "About %s" msgstr "Om %s" #: lib/solaar/ui/tray.py:65 lib/solaar/ui/window.py:317 #, python-format msgid "Quit %s" msgstr "Avsluta %s" #: lib/solaar/ui/tray.py:297 lib/solaar/ui/tray.py:305 msgid "no receiver" msgstr "ingen mottagare" #: lib/solaar/ui/tray.py:321 msgid "no status" msgstr "ingen status" #: lib/solaar/ui/window.py:96 msgid "Scanning" msgstr "Skannar" #: lib/solaar/ui/window.py:129 msgid "Battery" msgstr "Batteri" #: lib/solaar/ui/window.py:132 msgid "Wireless Link" msgstr "Trådlös anslutning" #: lib/solaar/ui/window.py:136 msgid "Lighting" msgstr "Belysning" #: lib/solaar/ui/window.py:170 msgid "Show Technical Details" msgstr "Visa tekniska detaljer" #: lib/solaar/ui/window.py:186 msgid "Pair new device" msgstr "Parkoppla ny enhet" #: lib/solaar/ui/window.py:205 msgid "Select a device" msgstr "Välj en enhet" #: lib/solaar/ui/window.py:322 msgid "Rule Editor" msgstr "Regelredigerare" #: lib/solaar/ui/window.py:533 msgid "Path" msgstr "Sökväg" #: lib/solaar/ui/window.py:536 msgid "USB ID" msgstr "USB-ID" #: lib/solaar/ui/window.py:539 lib/solaar/ui/window.py:541 #: lib/solaar/ui/window.py:561 lib/solaar/ui/window.py:563 msgid "Serial" msgstr "Seriell" #: lib/solaar/ui/window.py:545 msgid "Index" msgstr "Index" #: lib/solaar/ui/window.py:547 msgid "Wireless PID" msgstr "Trådlös-PID" #: lib/solaar/ui/window.py:549 msgid "Product ID" msgstr "Produkt-ID" #: lib/solaar/ui/window.py:551 msgid "Protocol" msgstr "Protokoll" #: lib/solaar/ui/window.py:551 msgid "Unknown" msgstr "Okänd" #: lib/solaar/ui/window.py:554 #, python-format msgid "%(rate)d ms (%(rate_hz)dHz)" msgstr "%(rate)d ms (%(rate_hz)dHz)" #: lib/solaar/ui/window.py:554 msgid "Polling rate" msgstr "Uppdateringshastighet" #: lib/solaar/ui/window.py:565 msgid "Unit ID" msgstr "Enhets-ID" #: lib/solaar/ui/window.py:576 msgid "none" msgstr "ingen" #: lib/solaar/ui/window.py:577 msgid "Notifications" msgstr "Aviseringar" #: lib/solaar/ui/window.py:621 msgid "No device paired." msgstr "Ingen enhet parkopplad." #: lib/solaar/ui/window.py:628 #, python-format msgid "Up to %(max_count)s device can be paired to this receiver." msgid_plural "Up to %(max_count)s devices can be paired to this receiver." msgstr[0] "Upp till %(max_count)s enhet kan parkopplas med denna mottagare." msgstr[1] "Upp till %(max_count)s enheter kan parkopplas med denna mottagare." #: lib/solaar/ui/window.py:634 msgid "Only one device can be paired to this receiver." msgstr "Endast en enhet kan parkopplas ihop med denna mottagare." #: lib/solaar/ui/window.py:638 #, python-format msgid "This receiver has %d pairing remaining." msgid_plural "This receiver has %d pairings remaining." msgstr[0] "Denna mottagare har %d parkoppling kvar." msgstr[1] "Denna mottagare har %d parkopplingar kvar." #: lib/solaar/ui/window.py:692 msgid "Battery Voltage" msgstr "Batterispänning" #: lib/solaar/ui/window.py:694 msgid "Voltage reported by battery" msgstr "Spänning rapporterad av batteri" #: lib/solaar/ui/window.py:696 msgid "Battery Level" msgstr "Batterinivå" #: lib/solaar/ui/window.py:698 msgid "Approximate level reported by battery" msgstr "Ungefärlig nivå rapporterad av batteriet" #: lib/solaar/ui/window.py:705 lib/solaar/ui/window.py:707 msgid "next reported " msgstr "nästa rapporterade " #: lib/solaar/ui/window.py:708 msgid " and next level to be reported." msgstr " och nästa nivå som ska rapporteras." #: lib/solaar/ui/window.py:713 msgid "last known" msgstr "senast kända" #: lib/solaar/ui/window.py:724 msgid "encrypted" msgstr "krypterad" #: lib/solaar/ui/window.py:726 msgid "The wireless link between this device and its receiver is encrypted." msgstr "" "Den trådlösa anslutningen mellan denna enheten och dess mottagare är " "krypterad." #: lib/solaar/ui/window.py:728 msgid "not encrypted" msgstr "inte krypterad" #: lib/solaar/ui/window.py:732 msgid "" "The wireless link between this device and its receiver is not encrypted.\n" "This is a security issue for pointing devices, and a major security issue " "for text-input devices." msgstr "" "Den trådlösa anslutningen mellan den här enheten och dess mottagare är inte " "krypterad.\n" "Detta är ett säkerhetsproblem för pekdon och ett stort säkerhetsproblem för " "enheter för textinmatning." #: lib/solaar/ui/window.py:748 #, python-format msgid "%(light_level)d lux" msgstr "%(light_level)d lux" #~ msgid "" #~ "\n" #~ "\n" #~ "This receiver has %d pairing(s) remaining." #~ msgstr "" #~ "\n" #~ "\n" #~ "Denna mottagare har %d parning(er) kvar." #~ msgid " paired devices." #~ msgstr "parkopplade enheter." #~ msgid "%(battery_level)s" #~ msgstr "%(battery_level)s" #~ msgid "%(battery_percent)d%%" #~ msgstr "%(battery_percent)d%%" #~ msgid "1 paired device." #~ msgstr "1 parkopplad enhet." #~ msgid "Actions" #~ msgstr "Åtgärder" #~ msgid "" #~ "Automatically switch the mouse wheel between ratchet and freespin mode.\n" #~ "The mouse wheel is always free at 0, and always locked at 50" #~ msgstr "" #~ "Växla automatiskt mushjulet mellan spärr- och frispinn-läge.\n" #~ "Mushjulet är alltid fritt vid 0 och låses alltid vid 50" #~ msgid "Count" #~ msgstr "Antal" #~ msgid "Effectively turns off thumb scrolling in Linux." #~ msgstr "Stänger effektivt av tumrullning i Linux." #~ msgid "Effectively turns off wheel scrolling in Linux." #~ msgstr "Stänger effektivt av rullning i Linux." #~ msgid "" #~ "For more information see the Solaar installation directions\n" #~ "at https://pwr-solaar.github.io/Solaar/installation" #~ msgstr "" #~ "Mer information finns i Solaar-installationsanvisningarna\n" #~ "på https://pwr-solaar.github.io/Solaar/installation" #, python-format #~ msgid "" #~ "Found a Logitech Receiver (%s), but did not have permission to open it." #~ msgstr "" #~ "En Logitech mottagare hittades (%s), fast saknar behörighet till att " #~ "öppna den." #~ msgid "Found a new device" #~ msgstr "Ny enhet har hittats" #~ msgid "HID++ Scrolling" #~ msgstr "HID++ rullning" #~ msgid "HID++ Thumb Scrolling" #~ msgstr "HID++ thumrullning" #~ msgid "HID++ mode for horizontal scroll with the thumb wheel." #~ msgstr "HID ++ -läge för horisontell rullning med tumhjulet." #~ msgid "HID++ mode for vertical scroll with the wheel." #~ msgstr "HID++ läge för vertikal rullning med hjulet." #~ msgid "High Resolution Scrolling" #~ msgstr "Rullning med hög upplösning" #~ msgid "High Resolution Wheel Invert" #~ msgstr "Invertera hög hjulupplösning" #~ msgid "High-sensitivity wheel invert direction for vertical scroll." #~ msgstr "Invertera riktning för vertikal rullning med hög känslighet." #~ msgid "High-sensitivity wheel invert mode for vertical scroll." #~ msgstr "Inverteringsläge med hög känslighet för vertikal rullning." #~ msgid "" #~ "If the device is already turned on,\n" #~ "turn if off and on again." #~ msgstr "" #~ "Om enheten redan är igång,\n" #~ "stäng av den och sätt på den igen." #~ msgid "If the device is already turned on, turn if off and on again." #~ msgstr "Om enheten redan är påslagen, stäng av og slå den på igen." #~ msgid "" #~ "If you've just installed Solaar, try removing the receiver and plugging " #~ "it back in." #~ msgstr "" #~ "Om du just installerade solaar, prova koppla ur mottagaren och anslut den " #~ "sedan igen." #~ msgid "Invert thumb scroll direction." #~ msgstr "Invertera tumrulleriktningen." #~ msgid "No Logitech receiver found" #~ msgstr "Ingen Logitech mottagare hittades" #~ msgid "No device paired" #~ msgstr "Inga enheter parkopplade" #~ msgid "Only one device can be paired to this receiver" #~ msgstr "Bara en enhet kan parkopplas till den här mottagaren" #~ msgid "" #~ "Shows status of devices connected\n" #~ "through wireless Logitech receivers." #~ msgstr "" #~ "Visar status för enheter kopplade till\n" #~ "din trådlösa Logitech mottagare." #~ msgid "Smooth Scrolling" #~ msgstr "Mjuk rullning" #~ msgid "Solaar depends on a udev file that is not present" #~ msgstr "Solaar beror på en udev-fil som inte finns" #~ msgid "The receiver only supports %d paired device(s)." #~ msgstr "Mottagaren klarar bara %d parkopplad(e) enhet(er)." #~ msgid "The receiver was unplugged." #~ msgstr "Mottagaren kopplades ur." #~ msgid "" #~ "The wireless link between this device and its receiver is not encrypted.\n" #~ "\n" #~ "For pointing devices (mice, trackballs, trackpads), this is a minor " #~ "security issue.\n" #~ "\n" #~ "It is, however, a major security issue for text-input devices (keyboards, " #~ "numpads),\n" #~ "because typed text can be sniffed inconspicuously by 3rd parties within " #~ "range." #~ msgstr "" #~ "Den trådlösa anslutningen mellan den här enheten och mottagaren är " #~ "okrypterad\n" #~ "\n" #~ "För pekdon (möss, styrkulor, pekplattor), är detta inget stort " #~ "säkerhetsproblem.\n" #~ "\n" #~ "Men för textinmatande enheter (tangentbord, numpads) är denna " #~ "säkerhetsbrist\n" #~ "allvarlig, eftersom skriven text obemärkt kan fångas upp av tredje part " #~ "som\n" #~ "befinner sig inom enhetens räckhåll." #~ msgid "This receiver has %d pairing(s) remaining." #~ msgstr "Denna mottagare har %d parning(er) kvar." #~ msgid "Thumb Scroll Invert" #~ msgstr "Invertera tumrullningen" #~ msgid "USB id" #~ msgstr "USB-id" #~ msgid "Unexpected device number (%s) in notification %s." #~ msgstr "Oväntat enhetsnummer (%s) i meddelandet %s." #~ msgid "Up to %d devices can be paired to this receiver" #~ msgstr "Upp till %d enheter kan parkopplas till mottagaren" #~ msgid "Wheel Resolution" #~ msgstr "Hjulupplösning" #~ msgid "closed" #~ msgstr "inaktiverat" #~ msgid "height" #~ msgstr "höjd" #~ msgid "lux" #~ msgstr "lux" #~ msgid "next " #~ msgstr "nästa " #~ msgid "open" #~ msgstr "aktiverat" #~ msgid "pair new device" #~ msgstr "parkoppla ny enhet" #~ msgid "paired devices" #~ msgstr "parkopplade enheter" #~ msgid "pairing lock is " #~ msgstr "parkopplingslåset är " #~ msgid "unknown" #~ msgstr "okänd" #~ msgid "width" #~ msgstr "bredd" Solaar-1.1.14/po/tr.po000066400000000000000000001633021473525213700144400ustar00rootroot00000000000000# Turkish translations for solaar package. # Copyright (C) 2015 THE solaar'S COPYRIGHT HOLDER # This file is distributed under the same license as the solaar package. # Automatically generated, 2015. # # Behzat Erte, , 2015. # Osman Karagöz, , 2024. # msgid "" msgstr "" "Project-Id-Version: solaar 0.9.2\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2024-07-01 13:49+0800\n" "PO-Revision-Date: 2024-11-17 16:01+0300\n" "Last-Translator: Osman Karagöz \n" "Language-Team: \n" "Language: tr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Poedit 3.4.2\n" "X-Poedit-SourceCharset: UTF-8\n" #: lib/logitech_receiver/base_usb.py:45 msgid "Bolt Receiver" msgstr "Bolt Alıcı" #: lib/logitech_receiver/base_usb.py:57 msgid "Unifying Receiver" msgstr "Unifying Alıcı" #: lib/logitech_receiver/base_usb.py:68 lib/logitech_receiver/base_usb.py:80 #: lib/logitech_receiver/base_usb.py:93 lib/logitech_receiver/base_usb.py:106 #: lib/logitech_receiver/base_usb.py:119 msgid "Nano Receiver" msgstr "Nano Alıcı" #: lib/logitech_receiver/base_usb.py:131 msgid "Lightspeed Receiver" msgstr "Lightspeed Alıcı" #: lib/logitech_receiver/base_usb.py:141 msgid "EX100 Receiver 27 Mhz" msgstr "EX100 27 Mhz Alıcı" #: lib/logitech_receiver/common.py:610 #, python-format msgid "Battery: %(level)s (%(status)s)" msgstr "Pil: %(level)s (%(status)s)" #: lib/logitech_receiver/common.py:613 #, python-format msgid "Battery: %(percent)d%% (%(status)s)" msgstr "Pil: %(percent)d%% (%(status)s)" #: lib/logitech_receiver/hidpp20.py:921 #: lib/logitech_receiver/settings_templates.py:283 msgid "Disabled" msgstr "Devredışı" #: lib/logitech_receiver/hidpp20.py:922 msgid "Static" msgstr "Sabit" #: lib/logitech_receiver/hidpp20.py:923 msgid "Pulse" msgstr "Nabız" #: lib/logitech_receiver/hidpp20.py:924 msgid "Cycle" msgstr "Döngü" #: lib/logitech_receiver/hidpp20.py:925 msgid "Boot" msgstr "Önyükleme" #: lib/logitech_receiver/hidpp20.py:926 msgid "Demo" msgstr "Demo" #: lib/logitech_receiver/hidpp20.py:927 msgid "Breathe" msgstr "Nefes" #: lib/logitech_receiver/hidpp20.py:928 msgid "Ripple" msgstr "Dalgalanma" #: lib/logitech_receiver/hidpp20.py:929 msgid "Decomposition" msgstr "Ayrışma" #: lib/logitech_receiver/hidpp20.py:930 msgid "Signature1" msgstr "İmza1" #: lib/logitech_receiver/hidpp20.py:931 msgid "Signature2" msgstr "İmza2" #: lib/logitech_receiver/hidpp20.py:932 msgid "CycleS" msgstr "DöngüS" #: lib/logitech_receiver/hidpp20.py:996 msgid "Unknown Location" msgstr "Bilinmeyen Konum" #: lib/logitech_receiver/hidpp20.py:997 msgid "Primary" msgstr "Birincil" #: lib/logitech_receiver/hidpp20.py:998 msgid "Logo" msgstr "Logo" #: lib/logitech_receiver/hidpp20.py:999 msgid "Left Side" msgstr "Sola Taraf" #: lib/logitech_receiver/hidpp20.py:1000 msgid "Right Side" msgstr "Sağ Taraf" #: lib/logitech_receiver/hidpp20.py:1001 msgid "Combined" msgstr "Birleşik" #: lib/logitech_receiver/hidpp20.py:1002 msgid "Primary 1" msgstr "Birincil 1" #: lib/logitech_receiver/hidpp20.py:1003 msgid "Primary 2" msgstr "Birincil 2" #: lib/logitech_receiver/hidpp20.py:1004 msgid "Primary 3" msgstr "Birincil 3" #: lib/logitech_receiver/hidpp20.py:1005 msgid "Primary 4" msgstr "Birincil 4" #: lib/logitech_receiver/hidpp20.py:1006 msgid "Primary 5" msgstr "Birincil 5" #: lib/logitech_receiver/hidpp20.py:1007 msgid "Primary 6" msgstr "Birincil 6" #: lib/logitech_receiver/i18n.py:28 msgid "empty" msgstr "boş" #: lib/logitech_receiver/i18n.py:29 msgid "critical" msgstr "kritik" #: lib/logitech_receiver/i18n.py:30 msgid "low" msgstr "az" #: lib/logitech_receiver/i18n.py:31 msgid "average" msgstr "ortalama" #: lib/logitech_receiver/i18n.py:32 msgid "good" msgstr "iyi" #: lib/logitech_receiver/i18n.py:33 msgid "full" msgstr "dolu" #: lib/logitech_receiver/i18n.py:35 msgid "discharging" msgstr "boşalıyor" #: lib/logitech_receiver/i18n.py:36 msgid "recharging" msgstr "yeniden şarj ediliyor" #: lib/logitech_receiver/i18n.py:37 msgid "charging" msgstr "şarj ediliyor" #: lib/logitech_receiver/i18n.py:38 msgid "not charging" msgstr "şarj edilmiyor" #: lib/logitech_receiver/i18n.py:39 msgid "almost full" msgstr "nerdeyse dolu" #: lib/logitech_receiver/i18n.py:40 msgid "charged" msgstr "şarj edildi" #: lib/logitech_receiver/i18n.py:41 msgid "slow recharge" msgstr "yavaş yeniden şarj edilme" #: lib/logitech_receiver/i18n.py:42 msgid "invalid battery" msgstr "geçersiz pil" #: lib/logitech_receiver/i18n.py:43 msgid "thermal error" msgstr "sıcaklık hatası" #: lib/logitech_receiver/i18n.py:44 msgid "error" msgstr "hata" #: lib/logitech_receiver/i18n.py:45 msgid "standard" msgstr "standart" #: lib/logitech_receiver/i18n.py:46 msgid "fast" msgstr "hızlı" #: lib/logitech_receiver/i18n.py:47 msgid "slow" msgstr "yavaş" #: lib/logitech_receiver/i18n.py:49 msgid "device timeout" msgstr "aygıt zaman aşımı" #: lib/logitech_receiver/i18n.py:50 msgid "device not supported" msgstr "aygıt desteklenmiyor" #: lib/logitech_receiver/i18n.py:51 msgid "too many devices" msgstr "çok fazla aygıt" #: lib/logitech_receiver/i18n.py:52 msgid "sequence timeout" msgstr "sıralama zaman aşımı" #: lib/logitech_receiver/i18n.py:54 msgid "Firmware" msgstr "Donanım Yazılımı" #: lib/logitech_receiver/i18n.py:55 msgid "Bootloader" msgstr "Önyükleme Yazılımı" #: lib/logitech_receiver/i18n.py:56 msgid "Hardware" msgstr "Donanım" #: lib/logitech_receiver/i18n.py:57 msgid "Other" msgstr "Diğer" #: lib/logitech_receiver/i18n.py:59 msgid "Left Button" msgstr "Sol Düğme" #: lib/logitech_receiver/i18n.py:60 msgid "Right Button" msgstr "Sag Düğme" #: lib/logitech_receiver/i18n.py:61 msgid "Middle Button" msgstr "Orta Düğme" #: lib/logitech_receiver/i18n.py:62 msgid "Back Button" msgstr "Geri Düğmesi" #: lib/logitech_receiver/i18n.py:63 msgid "Forward Button" msgstr "İleri Düğmesi" #: lib/logitech_receiver/i18n.py:64 msgid "Mouse Gesture Button" msgstr "Fare Hareketi Düğmesi" #: lib/logitech_receiver/i18n.py:65 msgid "Smart Shift" msgstr "Akıllı Kaydırma" #: lib/logitech_receiver/i18n.py:66 msgid "DPI Switch" msgstr "DPI Anahtarı" #: lib/logitech_receiver/i18n.py:67 msgid "Left Tilt" msgstr "Sola Eğme" #: lib/logitech_receiver/i18n.py:68 msgid "Right Tilt" msgstr "Sağa Eğme" #: lib/logitech_receiver/i18n.py:69 msgid "Left Click" msgstr "Sol Tıklama" #: lib/logitech_receiver/i18n.py:70 msgid "Right Click" msgstr "Sağ Tıklama" #: lib/logitech_receiver/i18n.py:71 msgid "Mouse Middle Button" msgstr "Fare Orta Düğmesi" #: lib/logitech_receiver/i18n.py:72 msgid "Mouse Back Button" msgstr "Fare Geri Düğmesi" #: lib/logitech_receiver/i18n.py:73 msgid "Mouse Forward Button" msgstr "Fare İleri Düğmesi" #: lib/logitech_receiver/i18n.py:74 msgid "Gesture Button Navigation" msgstr "Hareket Düğmesi Navigasyonu" #: lib/logitech_receiver/i18n.py:75 msgid "Mouse Scroll Left Button" msgstr "Fare Tekerleği Sol Düğmesi" #: lib/logitech_receiver/i18n.py:76 msgid "Mouse Scroll Right Button" msgstr "Fare Tekerleği Sağ Düğmesi" #: lib/logitech_receiver/i18n.py:78 msgid "pressed" msgstr "basıldı" #: lib/logitech_receiver/i18n.py:79 msgid "released" msgstr "bırakıldı" #: lib/logitech_receiver/notifications.py:64 #: lib/logitech_receiver/notifications.py:116 msgid "pairing lock is closed" msgstr "eşleştirme kilidi kapalı" #: lib/logitech_receiver/notifications.py:64 #: lib/logitech_receiver/notifications.py:116 msgid "pairing lock is open" msgstr "eşleştirme kilidi açık" #: lib/logitech_receiver/notifications.py:81 msgid "discovery lock is closed" msgstr "keşif kilidi kapalı" #: lib/logitech_receiver/notifications.py:81 msgid "discovery lock is open" msgstr "keşif kilidi açık" #: lib/logitech_receiver/notifications.py:210 lib/solaar/listener.py:238 msgid "connected" msgstr "bağlı" #: lib/logitech_receiver/notifications.py:210 lib/solaar/listener.py:238 msgid "disconnected" msgstr "bağlantı kesildi" #: lib/logitech_receiver/notifications.py:236 msgid "unpaired" msgstr "eşleşmemiş" #: lib/logitech_receiver/notifications.py:283 msgid "powered on" msgstr "açık" #: lib/logitech_receiver/receiver.py:371 msgid "No paired devices." msgstr "Eşleşmiş aygıt yok." #: lib/logitech_receiver/receiver.py:373 lib/solaar/ui/window.py:604 #, python-format msgid "%(count)s paired device." msgid_plural "%(count)s paired devices." msgstr[0] "%(count)s eşleşmiş aygıt." msgstr[1] "%(count)s eşleşmiş aygıt." #: lib/logitech_receiver/settings.py:611 msgid "register" msgstr "kaydet" #: lib/logitech_receiver/settings.py:625 lib/logitech_receiver/settings.py:651 msgid "feature" msgstr "özellik" #: lib/logitech_receiver/settings_templates.py:122 msgid "Swap Fx function" msgstr "Fx işlevlerini ters çevir" #: lib/logitech_receiver/settings_templates.py:125 msgid "" "When set, the F1..F12 keys will activate their special function,\n" "and you must hold the FN key to activate their standard function." msgstr "" "F1..F12 tuşları özel işlevler için ayarlandığında,\n" "FN tuşuna basılı tutarak standart işlevlerini aktif edebilirsiniz." #: lib/logitech_receiver/settings_templates.py:130 msgid "" "When unset, the F1..F12 keys will activate their standard function,\n" "and you must hold the FN key to activate their special function." msgstr "" "F1..F12 tuşları standart işlevler için ayarlandığında,\n" "FN tuşuna basılı tutarak özel işlevlerini aktif edebilirsiniz." #: lib/logitech_receiver/settings_templates.py:138 msgid "Hand Detection" msgstr "El Algılama" #: lib/logitech_receiver/settings_templates.py:139 msgid "Turn on illumination when the hands hover over the keyboard." msgstr "Eller klavyenin üzerindeyken aydınlanmayı aç." #: lib/logitech_receiver/settings_templates.py:146 msgid "Scroll Wheel Smooth Scrolling" msgstr "Kaydırma Tekerleği Yumuşak Kaydırma" #: lib/logitech_receiver/settings_templates.py:147 #: lib/logitech_receiver/settings_templates.py:394 #: lib/logitech_receiver/settings_templates.py:423 msgid "High-sensitivity mode for vertical scroll with the wheel." msgstr "Tekerleğin dikey kaydırılması için yüksek hasasiyet modu." #: lib/logitech_receiver/settings_templates.py:154 msgid "Side Scrolling" msgstr "Yatay Kaydırma" #: lib/logitech_receiver/settings_templates.py:156 msgid "" "When disabled, pushing the wheel sideways sends custom button events\n" "instead of the standard side-scrolling events." msgstr "" "Devredışı olduğunda, tekerleğin yanına basmak standart yatay kaydırma komutu " "yerine\n" "özel tuş komutunu gönderir." #: lib/logitech_receiver/settings_templates.py:166 msgid "Sensitivity (DPI - older mice)" msgstr "Hassasiyet (DPI - eski fare)" #: lib/logitech_receiver/settings_templates.py:167 #: lib/logitech_receiver/settings_templates.py:955 #: lib/logitech_receiver/settings_templates.py:983 msgid "Mouse movement sensitivity" msgstr "Fare hareketi hassasiyeti" #: lib/logitech_receiver/settings_templates.py:240 msgid "Backlight Timed" msgstr "Zamanlanmış Aydınlatma" #: lib/logitech_receiver/settings_templates.py:241 #: lib/logitech_receiver/settings_templates.py:381 msgid "Set illumination time for keyboard." msgstr "Klavye için aydınlatma zaman aşımı." #: lib/logitech_receiver/settings_templates.py:252 msgid "Backlight" msgstr "Aydınlatma" #: lib/logitech_receiver/settings_templates.py:253 msgid "" "Illumination level on keyboard. Changes made are only applied in Manual " "mode." msgstr "" "Klavyedeki aydınlatma düzeyi. Değişiklikler sadece Elle modunda uygulanır." #: lib/logitech_receiver/settings_templates.py:285 msgid "Automatic" msgstr "Otomatik" #: lib/logitech_receiver/settings_templates.py:287 msgid "Manual" msgstr "Elle" #: lib/logitech_receiver/settings_templates.py:289 msgid "Enabled" msgstr "Etkin" #: lib/logitech_receiver/settings_templates.py:295 msgid "Backlight Level" msgstr "Aydınlatma Düzeyi" #: lib/logitech_receiver/settings_templates.py:296 msgid "Illumination level on keyboard when in Manual mode." msgstr "Elle modundaki klavye aydınlatma düzeyi." #: lib/logitech_receiver/settings_templates.py:353 msgid "Backlight Delay Hands Out" msgstr "Eller Çekildiğinde Aydınlatma Gecikmesi" #: lib/logitech_receiver/settings_templates.py:354 msgid "" "Delay in seconds until backlight fades out with hands away from keyboard." msgstr "" "Eller klavyeden uzaklaştırıldığında aydınlatmanın solması için gecikme." #: lib/logitech_receiver/settings_templates.py:362 msgid "Backlight Delay Hands In" msgstr "Eller Yaklaştırıldığında Aydıtlatma Gecikmesi" #: lib/logitech_receiver/settings_templates.py:363 msgid "Delay in seconds until backlight fades out with hands near keyboard." msgstr "Eller klavyeye yaklaştırıldığında aydınlatmanın solması için gecikme." #: lib/logitech_receiver/settings_templates.py:371 msgid "Backlight Delay Powered" msgstr "Güce Bağlandığında Aydınlatma Gecikmesi" #: lib/logitech_receiver/settings_templates.py:372 msgid "Delay in seconds until backlight fades out with external power." msgstr "Güce bağlandığında aydınlatmanın solması için gecikme." #: lib/logitech_receiver/settings_templates.py:380 msgid "Backlight (Seconds)" msgstr "Aydınlatma (Saniye)" #: lib/logitech_receiver/settings_templates.py:392 msgid "Scroll Wheel High Resolution" msgstr "Kaydırma Tekerleği Yüksek Çözünürlük" #: lib/logitech_receiver/settings_templates.py:396 #: lib/logitech_receiver/settings_templates.py:425 msgid "Set to ignore if scrolling is abnormally fast or slow" msgstr "Kaydırma aşırı yavaş veya hızlıysa yoksaymak için ayarla" #: lib/logitech_receiver/settings_templates.py:403 #: lib/logitech_receiver/settings_templates.py:434 msgid "Scroll Wheel Diversion" msgstr "Kaydırma Tekerleği Yönlendirme" #: lib/logitech_receiver/settings_templates.py:405 msgid "" "Make scroll wheel send LOWRES_WHEEL HID++ notifications (which trigger " "Solaar rules but are otherwise ignored)." msgstr "" "Kaydırma tekerleğinin LOWRES_WHEEL HID++ bildirimler göndermesini sağlar (bu " "bildirimler Solaar kurallarını tetikler, ancak diğer durumlarda görmezden " "gelinir)." #: lib/logitech_receiver/settings_templates.py:412 msgid "Scroll Wheel Direction" msgstr "Kaydırma Tekerleği Yönü" #: lib/logitech_receiver/settings_templates.py:413 msgid "Invert direction for vertical scroll with wheel." msgstr "Kaydırma tekerleği ile dikey kaydırma yönünü tersine çevirir." #: lib/logitech_receiver/settings_templates.py:421 msgid "Scroll Wheel Resolution" msgstr "Kaydırma Tekerleği Çözünürlüğü" #: lib/logitech_receiver/settings_templates.py:436 msgid "" "Make scroll wheel send HIRES_WHEEL HID++ notifications (which trigger Solaar " "rules but are otherwise ignored)." msgstr "" "Kaydırma tekerleğinin HIRES_WHEEL HID++ bildirimler göndermesini sağlar (bu " "bildirimler Solaar kurallarını tetikler, ancak diğer durumlarda görmezden " "gelinir)." #: lib/logitech_receiver/settings_templates.py:445 msgid "Sensitivity (Pointer Speed)" msgstr "Hassasiyet (İşaretçi Hızı)" #: lib/logitech_receiver/settings_templates.py:446 msgid "Speed multiplier for mouse (256 is normal multiplier)." msgstr "Fare için hız çarpanı (256 normal çarpandır)." #: lib/logitech_receiver/settings_templates.py:456 msgid "Thumb Wheel Diversion" msgstr "Başparmak Tekerleği Yönlendirme" #: lib/logitech_receiver/settings_templates.py:458 msgid "" "Make thumb wheel send THUMB_WHEEL HID++ notifications (which trigger Solaar " "rules but are otherwise ignored)." msgstr "" "Başparmak tekerleğinin THUMB_WHEEL HID++ bildirimler göndermesini sağlar (bu " "bildirimler Solaar kurallarını tetikler, ancak diğer durumlarda görmezden " "gelinir)." #: lib/logitech_receiver/settings_templates.py:467 msgid "Thumb Wheel Direction" msgstr "Başparmak Tekerleği Yönü" #: lib/logitech_receiver/settings_templates.py:468 msgid "Invert thumb wheel scroll direction." msgstr "Kaydırma tekerleği ile başparmak kaydırma yönünü tersine çevirir." #: lib/logitech_receiver/settings_templates.py:488 msgid "Onboard Profiles" msgstr "Yerleşik Profiller" #: lib/logitech_receiver/settings_templates.py:489 msgid "" "Enable an onboard profile, which controls report rate, sensitivity, and " "button actions" msgstr "" "Yerleşik profilleri etkinleştir, bu rapor hızını, hassasiyeti ve düğme " "etkinliğini kontrol eder" #: lib/logitech_receiver/settings_templates.py:533 #: lib/logitech_receiver/settings_templates.py:566 msgid "Report Rate" msgstr "Rapor Hızı" #: lib/logitech_receiver/settings_templates.py:535 #: lib/logitech_receiver/settings_templates.py:568 msgid "Frequency of device movement reports" msgstr "Aygıt hareketlerinin rapor sıklığı" #: lib/logitech_receiver/settings_templates.py:535 #: lib/logitech_receiver/settings_templates.py:568 #: lib/logitech_receiver/settings_templates.py:983 #: lib/logitech_receiver/settings_templates.py:1357 #: lib/logitech_receiver/settings_templates.py:1388 msgid "May need Onboard Profiles set to Disable to be effective." msgstr "" "Etkili olabilmesi için Yerleşik Profillerin devredışı bırakılması " "gerekebilir." #: lib/logitech_receiver/settings_templates.py:596 msgid "Divert crown events" msgstr "Kadran etkinliklerini yönlendir" #: lib/logitech_receiver/settings_templates.py:597 msgid "" "Make crown send CROWN HID++ notifications (which trigger Solaar rules but " "are otherwise ignored)." msgstr "" "Kadranın CROWN HID++ bildirimler göndermesini sağlar (bu bildirimler Solaar " "kurallarını tetikler, ancak diğer durumlarda görmezden gelinir)." #: lib/logitech_receiver/settings_templates.py:605 msgid "Crown smooth scroll" msgstr "Kadran yumuşak kaydırma" #: lib/logitech_receiver/settings_templates.py:606 msgid "Set crown smooth scroll" msgstr "Kadranın yumuşak kaydırmasını ayarla" #: lib/logitech_receiver/settings_templates.py:614 msgid "Divert G and M Keys" msgstr "G ve M Tuşlarını Yönlendir" #: lib/logitech_receiver/settings_templates.py:615 msgid "" "Make G and M keys send HID++ notifications (which trigger Solaar rules but " "are otherwise ignored)." msgstr "" "G ve M Tuşlarının HID++ bildirimler göndermesini sağlar (bu bildirimler " "Solaar kurallarını tetikler, ancak diğer durumlarda görmezden gelinir)." #: lib/logitech_receiver/settings_templates.py:629 msgid "Scroll Wheel Ratcheted" msgstr "Kaydırma Tekerleği Dönüşü" #: lib/logitech_receiver/settings_templates.py:630 msgid "" "Switch the mouse wheel between speed-controlled ratcheting and always " "freespin." msgstr "" "Fare Tekerleğini, hız kontrollü dişli kaydırma ile sürekli serbest dönüş " "arasında değiştir." #: lib/logitech_receiver/settings_templates.py:632 msgid "Freespinning" msgstr "Serbest Dönüş" #: lib/logitech_receiver/settings_templates.py:632 msgid "Ratcheted" msgstr "Dişli" #: lib/logitech_receiver/settings_templates.py:639 msgid "Scroll Wheel Ratchet Speed" msgstr "Kaydırma Tekerleği Dişli Hızı" #: lib/logitech_receiver/settings_templates.py:641 msgid "" "Use the mouse wheel speed to switch between ratcheted and freespinning.\n" "The mouse wheel is always ratcheted at 50." msgstr "" "Fare tekerleği hızını kullanarak dişli kaydırma ile serbest dönüş arasında " "geçiş yap.\n" "Fare tekerleği, 50 hızda her zaman dişli kaydırmalı olur." #: lib/logitech_receiver/settings_templates.py:690 msgid "Key/Button Actions" msgstr "Tuş/Düğme Eylemleri" #: lib/logitech_receiver/settings_templates.py:692 msgid "Change the action for the key or button." msgstr "Tuş veya düğmenin eylemini değiştirir." #: lib/logitech_receiver/settings_templates.py:694 msgid "Overridden by diversion." msgstr "Yönlendirmeyle geçersiz kılındı." #: lib/logitech_receiver/settings_templates.py:696 msgid "" "Changing important actions (such as for the left mouse button) can result in " "an unusable system." msgstr "" "Önemli eylemleri değiştirmek (farenin sol tuşu gibi) sistemde " "kararsızlıklara yol açabilir." #: lib/logitech_receiver/settings_templates.py:862 msgid "Key/Button Diversion" msgstr "Tuş/Düğme Yönlendirme" #: lib/logitech_receiver/settings_templates.py:863 msgid "" "Make the key or button send HID++ notifications (Diverted) or initiate Mouse " "Gestures or Sliding DPI" msgstr "" "Tuş veya düğmenin HID++ bildirimleri (yönlendirmeler) veya Fare Hareketleri " "başlatmalar veya Kaydırma DPI'si" #: lib/logitech_receiver/settings_templates.py:866 #: lib/logitech_receiver/settings_templates.py:867 #: lib/logitech_receiver/settings_templates.py:868 msgid "Diverted" msgstr "Yönlendirilmiş" #: lib/logitech_receiver/settings_templates.py:866 #: lib/logitech_receiver/settings_templates.py:867 msgid "Mouse Gestures" msgstr "Fare Hareketleri" #: lib/logitech_receiver/settings_templates.py:866 #: lib/logitech_receiver/settings_templates.py:867 #: lib/logitech_receiver/settings_templates.py:868 msgid "Regular" msgstr "Normal" #: lib/logitech_receiver/settings_templates.py:866 msgid "Sliding DPI" msgstr "Kaydırma DPI" #: lib/logitech_receiver/settings_templates.py:954 #: lib/logitech_receiver/settings_templates.py:982 msgid "Sensitivity (DPI)" msgstr "Hassasiyet (DPI)" #: lib/logitech_receiver/settings_templates.py:1059 msgid "Sensitivity Switching" msgstr "Hassasiyet Değiştirme" #: lib/logitech_receiver/settings_templates.py:1061 msgid "" "Switch the current sensitivity and the remembered sensitivity when the key " "or button is pressed.\n" "If there is no remembered sensitivity, just remember the current sensitivity" msgstr "" "Şuanki hassasiyeti ve hatırlanan hassasiyeti tuş veya düğmeye basıldığında " "değiştir.\n" "Eğer hatırlanan hassasiyet yoksa sadece şuanki hassasiyeti hatırla" #: lib/logitech_receiver/settings_templates.py:1065 msgid "Off" msgstr "Kapalı" #: lib/logitech_receiver/settings_templates.py:1096 msgid "Disable keys" msgstr "Tuşları devredışı bırak" #: lib/logitech_receiver/settings_templates.py:1097 msgid "Disable specific keyboard keys." msgstr "Özel klavye tuşlarını devredışı bırak." #: lib/logitech_receiver/settings_templates.py:1100 #, python-format msgid "Disables the %s key." msgstr "%s tuşunu devredışı bırakır." #: lib/logitech_receiver/settings_templates.py:1113 #: lib/logitech_receiver/settings_templates.py:1170 msgid "Set OS" msgstr "İşletim Sistemi Ayarla" #: lib/logitech_receiver/settings_templates.py:1114 #: lib/logitech_receiver/settings_templates.py:1171 msgid "Change keys to match OS." msgstr "Tuşları eşleşen işletim sistemine göre değiştir." #: lib/logitech_receiver/settings_templates.py:1183 msgid "Change Host" msgstr "Bağlanılan Bilgisayarı Değiştir" #: lib/logitech_receiver/settings_templates.py:1184 msgid "Switch connection to a different host" msgstr "Bağlantıyı başka bilgisayara değiştir" #: lib/logitech_receiver/settings_templates.py:1208 msgid "Performs a left click." msgstr "Sol tıklama eylemi gerçekleştirir." #: lib/logitech_receiver/settings_templates.py:1208 msgid "Single tap" msgstr "Tek dokunma" #: lib/logitech_receiver/settings_templates.py:1209 msgid "Performs a right click." msgstr "Sağ tıklama eylemi gerçekleştirir." #: lib/logitech_receiver/settings_templates.py:1209 msgid "Single tap with two fingers" msgstr "İki parmakla tek dokunma" #: lib/logitech_receiver/settings_templates.py:1210 msgid "Single tap with three fingers" msgstr "Üç parmakla tek dokunma" #: lib/logitech_receiver/settings_templates.py:1214 msgid "Double tap" msgstr "Çift dokunma" #: lib/logitech_receiver/settings_templates.py:1214 msgid "Performs a double click." msgstr "Çift tıklama eylemi gerçekleştirir." #: lib/logitech_receiver/settings_templates.py:1215 msgid "Double tap with two fingers" msgstr "İki parmakla çift dokunma" #: lib/logitech_receiver/settings_templates.py:1216 msgid "Double tap with three fingers" msgstr "Üç parmakla çift dokunma" #: lib/logitech_receiver/settings_templates.py:1219 msgid "Drags items by dragging the finger after double tapping." msgstr "Çift tıklama eylemi ile nesneleri taşı." #: lib/logitech_receiver/settings_templates.py:1219 msgid "Tap and drag" msgstr "Dokun ve taşı" #: lib/logitech_receiver/settings_templates.py:1221 msgid "Tap and drag with two fingers" msgstr "İki parmakla dokun ve taşı" #: lib/logitech_receiver/settings_templates.py:1222 msgid "Drags items by dragging the fingers after double tapping." msgstr "Çift tıklama eylemi ile nesneleri taşı." #: lib/logitech_receiver/settings_templates.py:1224 msgid "Tap and drag with three fingers" msgstr "Üç parmakla dokun ve taşı" #: lib/logitech_receiver/settings_templates.py:1227 msgid "Suppress tap and edge gestures" msgstr "Dokunma ve kenar hareketlerini baskıla" #: lib/logitech_receiver/settings_templates.py:1228 msgid "Disables tap and edge gestures (equivalent to pressing Fn+LeftClick)." msgstr "" "Dokunma ve kenar hareketlerini devredışı bırakır (Fn + SolTıklama ile " "aynıdır)." #: lib/logitech_receiver/settings_templates.py:1230 msgid "Scroll with one finger" msgstr "Bir parmakla kaydır" #: lib/logitech_receiver/settings_templates.py:1230 #: lib/logitech_receiver/settings_templates.py:1231 #: lib/logitech_receiver/settings_templates.py:1234 msgid "Scrolls." msgstr "Kaydırır." #: lib/logitech_receiver/settings_templates.py:1231 #: lib/logitech_receiver/settings_templates.py:1234 msgid "Scroll with two fingers" msgstr "İki parmakla kaydır" #: lib/logitech_receiver/settings_templates.py:1232 msgid "Scroll horizontally with two fingers" msgstr "İki parmakla yatay kaydır" #: lib/logitech_receiver/settings_templates.py:1232 msgid "Scrolls horizontally." msgstr "Yatay kaydırır." #: lib/logitech_receiver/settings_templates.py:1233 msgid "Scroll vertically with two fingers" msgstr "İki parmakla dikey kaydır" #: lib/logitech_receiver/settings_templates.py:1233 msgid "Scrolls vertically." msgstr "Dikey kaydırır." #: lib/logitech_receiver/settings_templates.py:1235 msgid "Inverts the scrolling direction." msgstr "Kaydırma yönünü ters çevir." #: lib/logitech_receiver/settings_templates.py:1235 msgid "Natural scrolling" msgstr "Doğal kaydırma" #: lib/logitech_receiver/settings_templates.py:1236 msgid "Enables the thumbwheel." msgstr "Başparmak tekerleğini etkinleştirir." #: lib/logitech_receiver/settings_templates.py:1236 msgid "Thumbwheel" msgstr "Başparmak Tekerleği" #: lib/logitech_receiver/settings_templates.py:1247 #: lib/logitech_receiver/settings_templates.py:1251 msgid "Swipe from the top edge" msgstr "Üst kenardan vuruş" #: lib/logitech_receiver/settings_templates.py:1248 msgid "Swipe from the left edge" msgstr "Sol kenardan vuruş" #: lib/logitech_receiver/settings_templates.py:1249 msgid "Swipe from the right edge" msgstr "Sağ kenardan vuruş" #: lib/logitech_receiver/settings_templates.py:1250 msgid "Swipe from the bottom edge" msgstr "Alt kenardan vuruş" #: lib/logitech_receiver/settings_templates.py:1252 msgid "Swipe two fingers from the left edge" msgstr "Sol kenardan iki parmak vuruş" #: lib/logitech_receiver/settings_templates.py:1253 msgid "Swipe two fingers from the right edge" msgstr "Sağ kenardan iki parmak vuruş" #: lib/logitech_receiver/settings_templates.py:1254 msgid "Swipe two fingers from the bottom edge" msgstr "Alt kenardan iki parmak vuruş" #: lib/logitech_receiver/settings_templates.py:1255 msgid "Swipe two fingers from the top edge" msgstr "Üst kenardan iki parmak vuruş" #: lib/logitech_receiver/settings_templates.py:1256 #: lib/logitech_receiver/settings_templates.py:1260 msgid "Pinch to zoom out; spread to zoom in." msgstr "Uzaklaşmak için sıkıştırın, yakınlaştırmak için yayın." #: lib/logitech_receiver/settings_templates.py:1256 msgid "Zoom with two fingers." msgstr "İki parmakla yakınlaştırma." #: lib/logitech_receiver/settings_templates.py:1257 msgid "Pinch to zoom out." msgstr "Uzaklaştırmak için sıkıştırın." #: lib/logitech_receiver/settings_templates.py:1258 msgid "Spread to zoom in." msgstr "Yakınlaştırmak için yayın." #: lib/logitech_receiver/settings_templates.py:1259 msgid "Zoom with three fingers." msgstr "Üç parmakla yakınlaştırma." #: lib/logitech_receiver/settings_templates.py:1260 msgid "Zoom with two fingers" msgstr "İki parmakla yakınlaştır" #: lib/logitech_receiver/settings_templates.py:1278 msgid "Pixel zone" msgstr "Piksel alanı" #: lib/logitech_receiver/settings_templates.py:1279 msgid "Ratio zone" msgstr "Oran alanı" #: lib/logitech_receiver/settings_templates.py:1280 msgid "Scale factor" msgstr "Ölçek katsayısı" #: lib/logitech_receiver/settings_templates.py:1280 msgid "Sets the cursor speed." msgstr "İmleç hızını ayarla." #: lib/logitech_receiver/settings_templates.py:1284 msgid "Left" msgstr "Sol" #: lib/logitech_receiver/settings_templates.py:1284 msgid "Left-most coordinate." msgstr "En soldaki koordinat." #: lib/logitech_receiver/settings_templates.py:1285 msgid "Bottom" msgstr "Alt" #: lib/logitech_receiver/settings_templates.py:1285 msgid "Bottom coordinate." msgstr "Alt koordinat." #: lib/logitech_receiver/settings_templates.py:1286 msgid "Width" msgstr "Genişlik" #: lib/logitech_receiver/settings_templates.py:1286 msgid "Width." msgstr "Genişlik." #: lib/logitech_receiver/settings_templates.py:1287 msgid "Height" msgstr "Yükseklik" #: lib/logitech_receiver/settings_templates.py:1287 msgid "Height." msgstr "Yükseklik." #: lib/logitech_receiver/settings_templates.py:1288 msgid "Cursor speed." msgstr "İmleç hızı." #: lib/logitech_receiver/settings_templates.py:1288 msgid "Scale" msgstr "Ölçek" #: lib/logitech_receiver/settings_templates.py:1294 msgid "Gestures" msgstr "Hareketler" #: lib/logitech_receiver/settings_templates.py:1295 msgid "Tweak the mouse/touchpad behaviour." msgstr "Fare/dokunmatik yüzey davranışında ince ayar yapın." #: lib/logitech_receiver/settings_templates.py:1311 msgid "Gestures Diversion" msgstr "Hareketleri Yönlendir" #: lib/logitech_receiver/settings_templates.py:1312 msgid "Divert mouse/touchpad gestures." msgstr "Fare/dokunmatik yüzey hareketlerini yönlendirir." #: lib/logitech_receiver/settings_templates.py:1328 msgid "Gesture params" msgstr "Hareket parametreleri" #: lib/logitech_receiver/settings_templates.py:1329 msgid "Change numerical parameters of a mouse/touchpad." msgstr "Fare/dokunmatik yüzey sayısal parametrelerini değiştir." #: lib/logitech_receiver/settings_templates.py:1353 msgid "M-Key LEDs" msgstr "M-Tuşu LED'leri" #: lib/logitech_receiver/settings_templates.py:1355 msgid "Control the M-Key LEDs." msgstr "M-Tuşu LED'lerini kontrol eder." #: lib/logitech_receiver/settings_templates.py:1359 #: lib/logitech_receiver/settings_templates.py:1390 msgid "May need G Keys diverted to be effective." msgstr "G Tuşlarını yönlendirmek etkili olabilir." #: lib/logitech_receiver/settings_templates.py:1365 #, python-format msgid "Lights up the %s key." msgstr "%s tuşunu aydınlat." #: lib/logitech_receiver/settings_templates.py:1384 msgid "MR-Key LED" msgstr "MR-Tuşu LED'i" #: lib/logitech_receiver/settings_templates.py:1386 msgid "Control the MR-Key LED." msgstr "MR-Tuşu LED'inin kontrol eder." #: lib/logitech_receiver/settings_templates.py:1407 msgid "Persistent Key/Button Mapping" msgstr "Kalıcı Tuş/Düğme Haritalama" #: lib/logitech_receiver/settings_templates.py:1409 msgid "Permanently change the mapping for the key or button." msgstr "Tuş veya düğmenin haritalamasını kalıcı olarak değiştir." #: lib/logitech_receiver/settings_templates.py:1411 msgid "" "Changing important keys or buttons (such as for the left mouse button) can " "result in an unusable system." msgstr "" "Önemli tuşları veya düğmeleri değiştirmek (farenin sol tuşu gibi) sistemde " "kararsızlıklara yol açabilir." #: lib/logitech_receiver/settings_templates.py:1468 msgid "Sidetone" msgstr "Yan ton" #: lib/logitech_receiver/settings_templates.py:1469 msgid "Set sidetone level." msgstr "Yan ton seviyesini ayarla." #: lib/logitech_receiver/settings_templates.py:1478 msgid "Equalizer" msgstr "Ekolayzer" #: lib/logitech_receiver/settings_templates.py:1479 msgid "Set equalizer levels." msgstr "Ekolayzer seviyesini ayarla." #: lib/logitech_receiver/settings_templates.py:1501 msgid "Hz" msgstr "Hz" #: lib/logitech_receiver/settings_templates.py:1507 msgid "Power Management" msgstr "Güç Yönetimi" #: lib/logitech_receiver/settings_templates.py:1508 msgid "Power off in minutes (0 for never)." msgstr "Dakika cinsinden kapanma süresi (asla kapanmaması için 0)." #: lib/logitech_receiver/settings_templates.py:1519 msgid "Brightness Control" msgstr "Parlaklık Kontrolü" #: lib/logitech_receiver/settings_templates.py:1520 msgid "Control overall brightness" msgstr "Genel parlaklığı kontrol et" #: lib/logitech_receiver/settings_templates.py:1563 #: lib/logitech_receiver/settings_templates.py:1617 msgid "LED Control" msgstr "LED Kontrolü" #: lib/logitech_receiver/settings_templates.py:1564 #: lib/logitech_receiver/settings_templates.py:1618 msgid "Switch control of LED zones between device and Solaar" msgstr "LED bölgelerinin kontrolünü aygıt ile Solaar arasında değiştir" #: lib/logitech_receiver/settings_templates.py:1579 #: lib/logitech_receiver/settings_templates.py:1628 msgid "LED Zone Effects" msgstr "LED Bölge Efektleri" #: lib/logitech_receiver/settings_templates.py:1580 #: lib/logitech_receiver/settings_templates.py:1629 msgid "LED Control needs to be set to Solaar to be effective." msgstr "LED kontrolünün etkili olabilmesi için Solaar seçilmelidir." #: lib/logitech_receiver/settings_templates.py:1580 #: lib/logitech_receiver/settings_templates.py:1629 msgid "Set effect for LED Zone" msgstr "LED Bölgesi için efekt ayarla" #: lib/logitech_receiver/settings_templates.py:1583 msgid "Speed" msgstr "Hız" #: lib/logitech_receiver/settings_templates.py:1584 msgid "Period" msgstr "Sıklık" #: lib/logitech_receiver/settings_templates.py:1585 msgid "Intensity" msgstr "Yoğunluk" #: lib/logitech_receiver/settings_templates.py:1586 msgid "Ramp" msgstr "Eğim" #: lib/logitech_receiver/settings_templates.py:1602 msgid "LEDs" msgstr "LEDler" #: lib/logitech_receiver/settings_templates.py:1639 msgid "Per-key Lighting" msgstr "Tuş başına Aydınlatma" #: lib/logitech_receiver/settings_templates.py:1640 msgid "Control per-key lighting." msgstr "Tuş başına aydınlatmayı kontrol et." #: lib/solaar/ui/__init__.py:103 msgid "Another Solaar process is already running so just expose its window" msgstr "Başka bir Solaar işlemi çalışıyor, sadece onun penceresini görünür yap" #: lib/solaar/ui/about.py:34 msgid "" "Manages Logitech receivers,\n" "keyboards, mice, and tablets." msgstr "" "Logitech alıcılarını, klavyelerini,\n" "farelerini ve tabletlerini yönetir." #: lib/solaar/ui/about.py:43 msgid "Additional Programming" msgstr "Ek Programlama" #: lib/solaar/ui/about.py:44 msgid "GUI design" msgstr "GUI tasarımı" #: lib/solaar/ui/about.py:46 msgid "Testing" msgstr "Sınama" #: lib/solaar/ui/about.py:54 msgid "Logitech documentation" msgstr "Logitech belgeleri" #: lib/solaar/ui/action.py:83 lib/solaar/ui/action.py:87 #: lib/solaar/ui/window.py:189 msgid "Unpair" msgstr "Eşleşmeyi Kaldır" #: lib/solaar/ui/action.py:86 lib/solaar/ui/diversion_rules.py:103 msgid "Cancel" msgstr "Vazgeç" #: lib/solaar/ui/common.py:35 msgid "Permissions error" msgstr "Yetki hatası" #: lib/solaar/ui/common.py:37 #, python-format msgid "" "Found a Logitech receiver or device (%s), but did not have permission to " "open it." msgstr "Bir Logitech aygıtı (%s) bulundu ama açmak için yetki yok." #: lib/solaar/ui/common.py:39 msgid "" "If you've just installed Solaar, try disconnecting the receiver or device " "and then reconnecting it." msgstr "" "Eğer Solaar'ı yeni kurmuşsanız, aygıtı veya alıcıyı tekrar bağlamayı deneyin." #: lib/solaar/ui/common.py:42 msgid "Cannot connect to device error" msgstr "Aygıta bağlanılamıyor" #: lib/solaar/ui/common.py:44 #, python-format msgid "" "Found a Logitech receiver or device at %s, but encountered an error " "connecting to it." msgstr "" "%s konumunda bir Logitech alıcısı veya aygıtı bulundu, ancak buna " "bağlanırken bir hatayla karşılaşıldı." #: lib/solaar/ui/common.py:46 msgid "" "Try disconnecting the device and then reconnecting it or turning it off and " "then on." msgstr "Aygıtı tekrar bağlamayı deneyin veya kapatıp açın." #: lib/solaar/ui/common.py:49 msgid "Unpairing failed" msgstr "Eşleştirme kaldırılamadı" #: lib/solaar/ui/common.py:51 #, python-brace-format msgid "Failed to unpair %{device} from %{receiver}." msgstr "%{device} ve %{receiver} eşleştirmesi kaldırılamadı." #: lib/solaar/ui/common.py:53 msgid "The receiver returned an error, with no further details." msgstr "Alıcı hata verdi, bu hata hakkında ayrıntı mevcut değil." #: lib/solaar/ui/config_panel.py:228 msgid "Complete - ENTER to change" msgstr "Tamamlanmış - Değiştirmek için ENTER'a basın" #: lib/solaar/ui/config_panel.py:228 msgid "Incomplete" msgstr "Tamamlanmamış" #: lib/solaar/ui/config_panel.py:470 lib/solaar/ui/config_panel.py:522 #, python-format msgid "%d value" msgid_plural "%d values" msgstr[0] "%d değer" msgstr[1] "%d değer" #: lib/solaar/ui/config_panel.py:603 msgid "Changes allowed" msgstr "Değişiklikler kabul ediliyor" #: lib/solaar/ui/config_panel.py:604 msgid "No changes allowed" msgstr "Değişiklik kabul edilmiyor" #: lib/solaar/ui/config_panel.py:605 msgid "Ignore this setting" msgstr "Bu ayarı görmezden gel" #: lib/solaar/ui/config_panel.py:649 msgid "Working" msgstr "Çalışıyor" #: lib/solaar/ui/config_panel.py:652 msgid "Read/write operation failed." msgstr "Okuma/yazma işlemi yapılamadı." #: lib/solaar/ui/diversion_rules.py:65 msgid "Built-in rules" msgstr "Varolan kurallar" #: lib/solaar/ui/diversion_rules.py:65 msgid "User-defined rules" msgstr "Kullanıcı tanımlı kurallar" #: lib/solaar/ui/diversion_rules.py:67 lib/solaar/ui/diversion_rules.py:1033 msgid "Rule" msgstr "Kural" #: lib/solaar/ui/diversion_rules.py:68 lib/solaar/ui/diversion_rules.py:545 #: lib/solaar/ui/diversion_rules.py:672 msgid "Sub-rule" msgstr "Alt Kural" #: lib/solaar/ui/diversion_rules.py:70 msgid "[empty]" msgstr "[boş]" #: lib/solaar/ui/diversion_rules.py:94 msgid "Make changes permanent?" msgstr "Değişiklikleri kalıcı yap?" #: lib/solaar/ui/diversion_rules.py:99 msgid "Yes" msgstr "Evet" #: lib/solaar/ui/diversion_rules.py:101 msgid "No" msgstr "Hayır" #: lib/solaar/ui/diversion_rules.py:106 msgid "If you choose No, changes will be lost when Solaar is closed." msgstr "Hayır'ı seçerseniz, Solaar kapatıldığında değişiklikler kaybolacaktır." #: lib/solaar/ui/diversion_rules.py:167 msgid "Solaar Rule Editor" msgstr "Solar Kural Editörü" #: lib/solaar/ui/diversion_rules.py:260 msgid "Save changes" msgstr "Değişiklikleri kaydet" #: lib/solaar/ui/diversion_rules.py:265 msgid "Discard changes" msgstr "Değişiklikleri gözardı et" #: lib/solaar/ui/diversion_rules.py:404 msgid "Insert here" msgstr "Buraya ekle" #: lib/solaar/ui/diversion_rules.py:406 msgid "Insert above" msgstr "Yukarıya ekle" #: lib/solaar/ui/diversion_rules.py:408 msgid "Insert below" msgstr "Aşağıya ekle" #: lib/solaar/ui/diversion_rules.py:414 msgid "Insert new rule here" msgstr "Buraya yeni kural ekle" #: lib/solaar/ui/diversion_rules.py:416 msgid "Insert new rule above" msgstr "Yukarıya yeni kural ekle" #: lib/solaar/ui/diversion_rules.py:418 msgid "Insert new rule below" msgstr "Aşağıya yeni kural ekle" #: lib/solaar/ui/diversion_rules.py:463 msgid "Paste here" msgstr "Buraya yapıştır" #: lib/solaar/ui/diversion_rules.py:465 msgid "Paste above" msgstr "Yukarı yapıştır" #: lib/solaar/ui/diversion_rules.py:467 msgid "Paste below" msgstr "Aşağıya yapıştır" #: lib/solaar/ui/diversion_rules.py:473 msgid "Paste rule here" msgstr "Kuralı buraya yapıştır" #: lib/solaar/ui/diversion_rules.py:475 msgid "Paste rule above" msgstr "Kuralı yukarı yapıştır" #: lib/solaar/ui/diversion_rules.py:477 msgid "Paste rule below" msgstr "Kuralı aşağıya yapıştır" #: lib/solaar/ui/diversion_rules.py:481 msgid "Paste rule" msgstr "Kuralı yapıştır" #: lib/solaar/ui/diversion_rules.py:510 msgid "Flatten" msgstr "Kapsamdan çıkar" #: lib/solaar/ui/diversion_rules.py:543 msgid "Insert" msgstr "Ekle" #: lib/solaar/ui/diversion_rules.py:546 lib/solaar/ui/diversion_rules.py:674 #: lib/solaar/ui/diversion_rules.py:1065 msgid "Or" msgstr "Veya" #: lib/solaar/ui/diversion_rules.py:547 lib/solaar/ui/diversion_rules.py:673 #: lib/solaar/ui/diversion_rules.py:1051 msgid "And" msgstr "Ve" #: lib/solaar/ui/diversion_rules.py:549 msgid "Condition" msgstr "Durum" #: lib/solaar/ui/diversion_rules.py:551 lib/solaar/ui/rule_conditions.py:146 msgid "Feature" msgstr "Özellik" #: lib/solaar/ui/diversion_rules.py:552 lib/solaar/ui/rule_conditions.py:181 msgid "Report" msgstr "Rapor" #: lib/solaar/ui/diversion_rules.py:553 lib/solaar/ui/rule_conditions.py:60 msgid "Process" msgstr "İşlem" #: lib/solaar/ui/diversion_rules.py:554 msgid "Mouse process" msgstr "Fare İşlemi" #: lib/solaar/ui/diversion_rules.py:555 lib/solaar/ui/rule_conditions.py:218 msgid "Modifiers" msgstr "Değiştiriciler" #: lib/solaar/ui/diversion_rules.py:556 lib/solaar/ui/rule_conditions.py:270 msgid "Key" msgstr "Tuş" #: lib/solaar/ui/diversion_rules.py:557 lib/solaar/ui/rule_conditions.py:311 msgid "KeyIsDown" msgstr "TuşBasılı" #: lib/solaar/ui/diversion_rules.py:558 lib/solaar/ui/diversion_rules.py:1359 msgid "Active" msgstr "Etkin" #: lib/solaar/ui/diversion_rules.py:559 lib/solaar/ui/diversion_rules.py:1316 #: lib/solaar/ui/diversion_rules.py:1368 lib/solaar/ui/diversion_rules.py:1418 msgid "Device" msgstr "Aygıt" #: lib/solaar/ui/diversion_rules.py:560 lib/solaar/ui/diversion_rules.py:1394 msgid "Host" msgstr "Bilgisayar" #: lib/solaar/ui/diversion_rules.py:561 lib/solaar/ui/diversion_rules.py:1436 msgid "Setting" msgstr "Ayar" #: lib/solaar/ui/diversion_rules.py:562 lib/solaar/ui/rule_conditions.py:326 #: lib/solaar/ui/rule_conditions.py:375 msgid "Test" msgstr "Sınama" #: lib/solaar/ui/diversion_rules.py:563 lib/solaar/ui/rule_conditions.py:500 msgid "Test bytes" msgstr "Sınama baytları" #: lib/solaar/ui/diversion_rules.py:564 lib/solaar/ui/rule_conditions.py:601 msgid "Mouse Gesture" msgstr "Fare Hareketi" #: lib/solaar/ui/diversion_rules.py:568 msgid "Action" msgstr "Eylem" #: lib/solaar/ui/diversion_rules.py:570 lib/solaar/ui/rule_actions.py:132 msgid "Key press" msgstr "Tuş basımı" #: lib/solaar/ui/diversion_rules.py:571 lib/solaar/ui/rule_actions.py:183 msgid "Mouse scroll" msgstr "Fare kaydırma" #: lib/solaar/ui/diversion_rules.py:572 lib/solaar/ui/rule_actions.py:244 msgid "Mouse click" msgstr "Fare tıklaması" #: lib/solaar/ui/diversion_rules.py:573 msgid "Set" msgstr "Belirle" #: lib/solaar/ui/diversion_rules.py:574 lib/solaar/ui/rule_actions.py:314 msgid "Execute" msgstr "Uygula" #: lib/solaar/ui/diversion_rules.py:575 lib/solaar/ui/diversion_rules.py:1096 msgid "Later" msgstr "Sonra" #: lib/solaar/ui/diversion_rules.py:604 msgid "Insert new rule" msgstr "Yeni kural ekle" #: lib/solaar/ui/diversion_rules.py:624 lib/solaar/ui/rule_actions.py:74 #: lib/solaar/ui/rule_actions.py:273 lib/solaar/ui/rule_conditions.py:548 msgid "Delete" msgstr "Sil" #: lib/solaar/ui/diversion_rules.py:646 msgid "Negate" msgstr "Olumsuzla" #: lib/solaar/ui/diversion_rules.py:670 msgid "Wrap with" msgstr "Kapsa" #: lib/solaar/ui/diversion_rules.py:692 msgid "Cut" msgstr "Kes" #: lib/solaar/ui/diversion_rules.py:707 msgid "Paste" msgstr "Yapıştır" #: lib/solaar/ui/diversion_rules.py:713 msgid "Copy" msgstr "Kopyala" #: lib/solaar/ui/diversion_rules.py:1014 msgid "This editor does not support the selected rule component yet." msgstr "Seçilen kural bileşeni henüz desteklenmiyor." #: lib/solaar/ui/diversion_rules.py:1076 msgid "" "Number of seconds to delay. Delay between 0 and 1 is done with higher " "precision." msgstr "" "Saniye cinsinden gecikme süresi. 0 ile 1 arasındaki gecikmeler daha yüksek " "hassasiyetle yapılır." #: lib/solaar/ui/diversion_rules.py:1114 msgid "Not" msgstr "Değil" #: lib/solaar/ui/diversion_rules.py:1145 msgid "Toggle" msgstr "Değiştir" #: lib/solaar/ui/diversion_rules.py:1146 msgid "True" msgstr "Doğru" #: lib/solaar/ui/diversion_rules.py:1147 msgid "False" msgstr "Yanlış" #: lib/solaar/ui/diversion_rules.py:1160 msgid "Unsupported setting" msgstr "Desteklenmeyen ayar" #: lib/solaar/ui/diversion_rules.py:1322 lib/solaar/ui/diversion_rules.py:1342 #: lib/solaar/ui/diversion_rules.py:1424 lib/solaar/ui/diversion_rules.py:1669 #: lib/solaar/ui/diversion_rules.py:1687 msgid "Originating device" msgstr "Kaynak aygıt" #: lib/solaar/ui/diversion_rules.py:1355 msgid "Device is active and its settings can be changed." msgstr "Aygıt etkin ve ayarları değiştirilebilir." #: lib/solaar/ui/diversion_rules.py:1364 msgid "Device that originated the current notification." msgstr "Mevcut bildirimin kaynağı olan aygıt." #: lib/solaar/ui/diversion_rules.py:1377 msgid "Name of host computer." msgstr "Bilgisayarın adı." #: lib/solaar/ui/diversion_rules.py:1445 msgid "Value" msgstr "Değer" #: lib/solaar/ui/diversion_rules.py:1454 msgid "Item" msgstr "Öge" #: lib/solaar/ui/diversion_rules.py:1728 msgid "Change setting on device" msgstr "Aygıtın ayarını değiştir" #: lib/solaar/ui/diversion_rules.py:1744 msgid "Setting on device" msgstr "Aygıtta ayar" #: lib/solaar/ui/notify.py:115 msgid "unspecified reason" msgstr "tanımlanmamış sebep" #: lib/solaar/ui/pair_window.py:37 lib/solaar/ui/pair_window.py:155 #, python-format msgid "%(receiver_name)s: pair new device" msgstr "%(receiver_name)s: yeni aygıtı eşleştir" #: lib/solaar/ui/pair_window.py:39 msgid "Bolt receivers are only compatible with Bolt devices." msgstr "Bolt alıcılar sadece Bolt aygıtlarla uyumludur." #: lib/solaar/ui/pair_window.py:41 msgid "Press a pairing button or key until the pairing light flashes quickly." msgstr "" "Eşleşme ışığı hızlıca yanıp sönene kadar eşleşme düğmesi ya da tuşuna basın." #: lib/solaar/ui/pair_window.py:44 msgid "Unifying receivers are only compatible with Unifying devices." msgstr "Unifying alıcılar sadece Unifying aygıtlarla uyumludur." #: lib/solaar/ui/pair_window.py:46 msgid "Other receivers are only compatible with a few devices." msgstr "Diğer alıcılar sadece az miktarda aygıtla uyumludur." #: lib/solaar/ui/pair_window.py:48 msgid "Turn on the device you want to pair." msgstr "Eşleştirmek istediğiniz aygıtı açın." #: lib/solaar/ui/pair_window.py:49 msgid "The device must not be paired with a nearby powered-on receiver." msgstr "Aygıt yakınlarda açık olan bir alıcıyla eşleşmemiş olmalıdır." #: lib/solaar/ui/pair_window.py:51 msgid "If the device is already turned on, turn it off and on again." msgstr "Aygıt zaten açıksa, kapatıp tekrar açın." #: lib/solaar/ui/pair_window.py:55 #, python-format msgid "" "\n" "\n" "This receiver has %d pairing remaining." msgid_plural "" "\n" "\n" "This receiver has %d pairings remaining." msgstr[0] "" "\n" "\n" "Bu alıcı %d aygıtla daha eşleşebilir." msgstr[1] "" "\n" "\n" "Bu alıcı %d aygıtla daha eşleşebilir." #: lib/solaar/ui/pair_window.py:61 msgid "" "\n" "Cancelling at this point will not use up a pairing." msgstr "" "\n" "Bu adımda vezgeçerseniz eşleşme kullanılamayacak." #: lib/solaar/ui/pair_window.py:156 #, python-format msgid "Enter passcode on %(name)s." msgstr "%(name)s üzerinde geçiş kodunu girin." #: lib/solaar/ui/pair_window.py:159 #, python-format msgid "Type %(passcode)s and then press the enter key." msgstr "%(passcode)s yazın ve Enter tuşuna basın." #: lib/solaar/ui/pair_window.py:162 msgid "left" msgstr "sol" #: lib/solaar/ui/pair_window.py:162 msgid "right" msgstr "sağ" #: lib/solaar/ui/pair_window.py:164 #, python-format msgid "" "Press %(code)s\n" "and then press left and right buttons simultaneously." msgstr "" "%(code)s basın\n" "ve sonra sağ ve sol tuşlarına aynı anda basın." #: lib/solaar/ui/pair_window.py:195 msgid "The wireless link is not encrypted" msgstr "Kablosuz bağlantı şifrelenmemiş" #: lib/solaar/ui/pair_window.py:200 msgid "Found a new device:" msgstr "Yeni bir aygıt bulundu:" #: lib/solaar/ui/pair_window.py:221 msgid "Pairing failed" msgstr "Eşleşme başarısız" #: lib/solaar/ui/pair_window.py:223 msgid "Make sure your device is within range, and has a decent battery charge." msgstr "" "Aygıtınızın kapsama alanı içerisinde olduğundan, pilin çalıştığından veya " "şarjının tam olduğundan emin olun." #: lib/solaar/ui/pair_window.py:225 msgid "A new device was detected, but it is not compatible with this receiver." msgstr "Aygıt algılandı fakat bu alıcı ile uyumlu değil." #: lib/solaar/ui/pair_window.py:227 msgid "More paired devices than receiver can support." msgstr "Alıcının destekleyebileceğinden daha fazla eşleştirilmiş aygıt." #: lib/solaar/ui/pair_window.py:229 msgid "No further details are available about the error." msgstr "Hata hakkında daha fazla ayrıntı mevcut değil." #: lib/solaar/ui/rule_actions.py:48 msgid "" "Simulate a chorded key click or depress or release.\n" "On Wayland requires write access to /dev/uinput." msgstr "" "Akorlu tuş tıklaması, basması veya bırakması öykün.\n" "Wayland'da /dev/uinput'a yazma yetkisi gerektirir." #: lib/solaar/ui/rule_actions.py:53 msgid "Add key" msgstr "Tuş ekle" #: lib/solaar/ui/rule_actions.py:56 msgid "Click" msgstr "Tıklama" #: lib/solaar/ui/rule_actions.py:59 msgid "Depress" msgstr "Basma" #: lib/solaar/ui/rule_actions.py:62 msgid "Release" msgstr "Bırakma" #: lib/solaar/ui/rule_actions.py:147 msgid "" "Simulate a mouse scroll.\n" "On Wayland requires write access to /dev/uinput." msgstr "" "Fare kayrıdması öykün.\n" "Wayland'da /dev/uinput'a yazma yetkisi gerektirir." #: lib/solaar/ui/rule_actions.py:203 msgid "" "Simulate a mouse click.\n" "On Wayland requires write access to /dev/uinput." msgstr "" "Fare tıklaması öykün.\n" "Wayland'da /dev/uinput'a yazma yetkisi gerektirir." #: lib/solaar/ui/rule_actions.py:206 msgid "Button" msgstr "Düğme" #: lib/solaar/ui/rule_actions.py:207 msgid "Count and Action" msgstr "Say ve İşle" #: lib/solaar/ui/rule_actions.py:256 msgid "Execute a command with arguments." msgstr "Argümanlarla bir komut çalıştır." #: lib/solaar/ui/rule_actions.py:259 msgid "Add argument" msgstr "Argüman ekle" #: lib/solaar/ui/rule_conditions.py:43 msgid "X11 active process. For use in X11 only." msgstr "X11 etkin işlem. Sadece X11'de kullanım için." #: lib/solaar/ui/rule_conditions.py:73 msgid "X11 mouse process. For use in X11 only." msgstr "X11 fare işlemi. Sadece X11'de kullanım için." #: lib/solaar/ui/rule_conditions.py:90 msgid "MouseProcess" msgstr "Fareİşlemi" #: lib/solaar/ui/rule_conditions.py:114 msgid "Feature name of notification triggering rule processing." msgstr "Bildirim tetikleme işlemi özelliği adı." #: lib/solaar/ui/rule_conditions.py:161 msgid "Report number of notification triggering rule processing." msgstr "Bildirim tetikleme işlemi rapor numarası." #: lib/solaar/ui/rule_conditions.py:194 msgid "Active keyboard modifiers. Not always available in Wayland." msgstr "Etkin klavye değiştiriciler. Wayland'da her zaman çalışmaz." #: lib/solaar/ui/rule_conditions.py:234 msgid "" "Diverted key or button depressed or released.\n" "Use the Key/Button Diversion and Divert G Keys settings to divert keys and " "buttons." msgstr "" "Yönlendirilmiş tuş veya düğme basıldı veya bırakıldı.\n" "Tuşları ve düğmeleri yönlendirmek için Tuş/Düğme Yönlendirme ve " "Yönlendirilen G Tuşları ayarlarını kullanın." #: lib/solaar/ui/rule_conditions.py:243 msgid "Key down" msgstr "Tuş basılı" #: lib/solaar/ui/rule_conditions.py:246 msgid "Key up" msgstr "Tuş serbest" #: lib/solaar/ui/rule_conditions.py:286 msgid "" "Diverted key or button is currently down.\n" "Use the Key/Button Diversion and Divert G Keys settings to divert keys and " "buttons." msgstr "" "Yönlendirilmiş tuş veya düğme şu anda basılı.\n" "Tuşları ve düğmeleri yönlendirmek için Tuş/Düğme Yönlendirme ve " "Yönlendirilen G Tuşları ayarlarını kullanın." #: lib/solaar/ui/rule_conditions.py:324 msgid "Test condition on notification triggering rule processing." msgstr "Bildirim tetikleme işlemi test durumu." #: lib/solaar/ui/rule_conditions.py:328 msgid "Parameter" msgstr "Parametre" #: lib/solaar/ui/rule_conditions.py:401 msgid "begin (inclusive)" msgstr "baş (dahil edilen)" #: lib/solaar/ui/rule_conditions.py:402 msgid "end (exclusive)" msgstr "son (hariç tutulan)" #: lib/solaar/ui/rule_conditions.py:410 msgid "range" msgstr "aralık" #: lib/solaar/ui/rule_conditions.py:413 msgid "minimum" msgstr "en az" #: lib/solaar/ui/rule_conditions.py:414 msgid "maximum" msgstr "en çok" #: lib/solaar/ui/rule_conditions.py:416 #, python-format msgid "bytes %(0)d to %(1)d, ranging from %(2)d to %(3)d" msgstr "%(0)d bayttan %(1)d bayta, %(2)d ile %(3)d arasında" #: lib/solaar/ui/rule_conditions.py:419 lib/solaar/ui/rule_conditions.py:420 msgid "mask" msgstr "maske" #: lib/solaar/ui/rule_conditions.py:421 #, python-format msgid "bytes %(0)d to %(1)d, mask %(2)d" msgstr "%(0)d bayttan %(1)d bayta, %(2)d maskeleme" #: lib/solaar/ui/rule_conditions.py:430 msgid "" "Bit or range test on bytes in notification message triggering rule " "processing." msgstr "" "Bildirim mesajı tetikleyici kural işlemesinde baytlarda bit veya aralık " "testi." #: lib/solaar/ui/rule_conditions.py:440 msgid "type" msgstr "tür" #: lib/solaar/ui/rule_conditions.py:528 msgid "" "Mouse gesture with optional initiating button followed by zero or more mouse " "movements." msgstr "" "Sıfır veya daha fazla fare hareketinin ardından isteğe bağlı başlatma " "düğmesi ile fare hareketi." #: lib/solaar/ui/rule_conditions.py:533 msgid "Add movement" msgstr "Hareket ekle" #: lib/solaar/ui/tray.py:50 msgid "No supported device found" msgstr "Desteklenen aygıt bulunamadı" #: lib/solaar/ui/tray.py:55 lib/solaar/ui/window.py:310 #, python-format msgid "About %s" msgstr "%s Hakkında" #: lib/solaar/ui/tray.py:56 lib/solaar/ui/window.py:308 #, python-format msgid "Quit %s" msgstr "%s Çıkış" #: lib/solaar/ui/tray.py:267 lib/solaar/ui/tray.py:275 msgid "no receiver" msgstr "alıcı yok" #: lib/solaar/ui/tray.py:288 lib/solaar/ui/tray.py:293 msgid "offline" msgstr "çevrimdışı" #: lib/solaar/ui/tray.py:291 msgid "no status" msgstr "durum yok" #: lib/solaar/ui/window.py:91 msgid "Scanning" msgstr "Taranıyor" #: lib/solaar/ui/window.py:122 msgid "Battery" msgstr "Pil" #: lib/solaar/ui/window.py:125 msgid "Wireless Link" msgstr "Kablosuz Bağlantı" #: lib/solaar/ui/window.py:129 msgid "Lighting" msgstr "Aydınlatma" #: lib/solaar/ui/window.py:163 msgid "Show Technical Details" msgstr "Teknik Ayrıntıları Göster" #: lib/solaar/ui/window.py:179 msgid "Pair new device" msgstr "Yeni aygıt eşle" #: lib/solaar/ui/window.py:197 msgid "Select a device" msgstr "Aygıtı seç" #: lib/solaar/ui/window.py:313 msgid "Rule Editor" msgstr "Kural Düzenleyici" #: lib/solaar/ui/window.py:517 msgid "Path" msgstr "Yol" #: lib/solaar/ui/window.py:520 msgid "USB ID" msgstr "USB Kimliği" #: lib/solaar/ui/window.py:523 lib/solaar/ui/window.py:525 #: lib/solaar/ui/window.py:540 lib/solaar/ui/window.py:542 msgid "Serial" msgstr "Seri" #: lib/solaar/ui/window.py:529 msgid "Index" msgstr "Dizin" #: lib/solaar/ui/window.py:531 msgid "Wireless PID" msgstr "Kablosuz bağlantı kimliği" #: lib/solaar/ui/window.py:533 msgid "Product ID" msgstr "Ürün Kimliği" #: lib/solaar/ui/window.py:535 msgid "Protocol" msgstr "İletişim kuralı" #: lib/solaar/ui/window.py:535 msgid "Unknown" msgstr "Bilinmeyen" #: lib/solaar/ui/window.py:537 msgid "Polling rate" msgstr "Bağlantı sıklığı" #: lib/solaar/ui/window.py:544 msgid "Unit ID" msgstr "Birim Kimliği" #: lib/solaar/ui/window.py:558 msgid "Notifications" msgstr "Bildirimler" #: lib/solaar/ui/window.py:602 msgid "No device paired." msgstr "Eşleştirilmiş aygıt yok." #: lib/solaar/ui/window.py:611 #, python-format msgid "Up to %(max_count)s device can be paired to this receiver." msgid_plural "Up to %(max_count)s devices can be paired to this receiver." msgstr[0] "En fazla %(max_count)s aygıt bu alıcıyla eşleşebilir." msgstr[1] "En fazla %(max_count)s aygıt bu alıcıyla eşleşebilir." #: lib/solaar/ui/window.py:622 #, python-format msgid "This receiver has %d pairing remaining." msgid_plural "This receiver has %d pairings remaining." msgstr[0] "Bu alıcı %d aygıtla daha eşleşebilir." msgstr[1] "Bu alıcı %d aygıtla daha eşleşebilir." #: lib/solaar/ui/window.py:679 msgid "Battery Voltage" msgstr "Pil Gerilimi" #: lib/solaar/ui/window.py:681 msgid "Voltage reported by battery" msgstr "Pil tarafından bildirilen gerilim" #: lib/solaar/ui/window.py:683 msgid "Battery Level" msgstr "Pil Düzeyi" #: lib/solaar/ui/window.py:685 msgid "Approximate level reported by battery" msgstr "Pil tarafından bildirilen yaklaşık düzeyi" #: lib/solaar/ui/window.py:692 lib/solaar/ui/window.py:694 msgid "next reported " msgstr "sonraki rapor " #: lib/solaar/ui/window.py:695 msgid " and next level to be reported." msgstr " ve sonraki seviye raporlanması." #: lib/solaar/ui/window.py:711 msgid "encrypted" msgstr "şifreli" #: lib/solaar/ui/window.py:713 msgid "The wireless link between this device and its receiver is encrypted." msgstr "Bu aygıt ile alıcı arasındaki kablosuz bağlantı şirelenmiştir." #: lib/solaar/ui/window.py:715 msgid "not encrypted" msgstr "şifresiz" #: lib/solaar/ui/window.py:719 msgid "" "The wireless link between this device and its receiver is not encrypted.\n" "This is a security issue for pointing devices, and a major security issue " "for text-input devices." msgstr "" "Bu aygıt ile alıcı arasındaki kablosuz bağlantı şirelenmemiştir.\n" "Bu işaretçi cihazlar için güvenlik açığı ve metin-giriş aygıtları için büyük " "bir güvenlik açığı." #: lib/solaar/ui/window.py:735 #, python-format msgid "%(light_level)d lux" msgstr "%(light_level)d lüks" Solaar-1.1.14/po/zh_CN.po000066400000000000000000001701071473525213700150150ustar00rootroot00000000000000# Chinese translations for solaar package # Copyright (C) 2014 THE solaar'S COPYRIGHT HOLDER # This file is distributed under the same license as the solaar package. # Automatically generated, 2014. # ZH , 2014. # Rongrong , 2022. msgid "" msgstr "" "Project-Id-Version: solaar 1.1.13\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2024-07-01 13:49+0800\n" "PO-Revision-Date: 2024-07-01 16:23+0800\n" "Last-Translator: iskandarma \n" "Language-Team: none\n" "Language: zh_CN\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Language: zh_CN\n" "X-Source-Language: C\n" "X-Generator: Poedit 3.4.3\n" #: lib/logitech_receiver/base_usb.py:45 msgid "Bolt Receiver" msgstr "Bolt 接收器" #: lib/logitech_receiver/base_usb.py:57 msgid "Unifying Receiver" msgstr "优联接收器" #: lib/logitech_receiver/base_usb.py:68 lib/logitech_receiver/base_usb.py:80 #: lib/logitech_receiver/base_usb.py:93 lib/logitech_receiver/base_usb.py:106 #: lib/logitech_receiver/base_usb.py:119 msgid "Nano Receiver" msgstr "微型接收器" #: lib/logitech_receiver/base_usb.py:131 msgid "Lightspeed Receiver" msgstr "Lightspeed 接收器" #: lib/logitech_receiver/base_usb.py:141 msgid "EX100 Receiver 27 Mhz" msgstr "EX100 接收器 (27MHz)" #: lib/logitech_receiver/common.py:610 #, python-format msgid "Battery: %(level)s (%(status)s)" msgstr "电池: %(level)s (%(status)s)" #: lib/logitech_receiver/common.py:613 #, python-format msgid "Battery: %(percent)d%% (%(status)s)" msgstr "电池: %(percent)d%% (%(status)s)" #: lib/logitech_receiver/hidpp20.py:921 #: lib/logitech_receiver/settings_templates.py:283 msgid "Disabled" msgstr "禁用" #: lib/logitech_receiver/hidpp20.py:922 msgid "Static" msgstr "静态" #: lib/logitech_receiver/hidpp20.py:923 msgid "Pulse" msgstr "脉冲" #: lib/logitech_receiver/hidpp20.py:924 msgid "Cycle" msgstr "循环" #: lib/logitech_receiver/hidpp20.py:925 msgid "Boot" msgstr "启动" #: lib/logitech_receiver/hidpp20.py:926 msgid "Demo" msgstr "演示" #: lib/logitech_receiver/hidpp20.py:927 msgid "Breathe" msgstr "呼吸" #: lib/logitech_receiver/hidpp20.py:928 msgid "Ripple" msgstr "涟漪" #: lib/logitech_receiver/hidpp20.py:929 msgid "Decomposition" msgstr "分解" #: lib/logitech_receiver/hidpp20.py:930 msgid "Signature1" msgstr "签名1" #: lib/logitech_receiver/hidpp20.py:931 msgid "Signature2" msgstr "签名2" #: lib/logitech_receiver/hidpp20.py:932 msgid "CycleS" msgstr "饱和度循环" #: lib/logitech_receiver/hidpp20.py:996 msgid "Unknown Location" msgstr "未知位置" #: lib/logitech_receiver/hidpp20.py:997 msgid "Primary" msgstr "主要" #: lib/logitech_receiver/hidpp20.py:998 msgid "Logo" msgstr "标志" #: lib/logitech_receiver/hidpp20.py:999 msgid "Left Side" msgstr "左侧" #: lib/logitech_receiver/hidpp20.py:1000 msgid "Right Side" msgstr "右侧" #: lib/logitech_receiver/hidpp20.py:1001 msgid "Combined" msgstr "合并" #: lib/logitech_receiver/hidpp20.py:1002 msgid "Primary 1" msgstr "主要 1" #: lib/logitech_receiver/hidpp20.py:1003 msgid "Primary 2" msgstr "主要 2" #: lib/logitech_receiver/hidpp20.py:1004 msgid "Primary 3" msgstr "主要 3" #: lib/logitech_receiver/hidpp20.py:1005 msgid "Primary 4" msgstr "主要 4" #: lib/logitech_receiver/hidpp20.py:1006 msgid "Primary 5" msgstr "主要 5" #: lib/logitech_receiver/hidpp20.py:1007 msgid "Primary 6" msgstr "主要6" #: lib/logitech_receiver/i18n.py:28 msgid "empty" msgstr "空" #: lib/logitech_receiver/i18n.py:29 msgid "critical" msgstr "极低" #: lib/logitech_receiver/i18n.py:30 msgid "low" msgstr "低" #: lib/logitech_receiver/i18n.py:31 msgid "average" msgstr "平均" #: lib/logitech_receiver/i18n.py:32 msgid "good" msgstr "良好" #: lib/logitech_receiver/i18n.py:33 msgid "full" msgstr "全满" #: lib/logitech_receiver/i18n.py:35 msgid "discharging" msgstr "放电中" #: lib/logitech_receiver/i18n.py:36 msgid "recharging" msgstr "充电中" #: lib/logitech_receiver/i18n.py:37 msgid "charging" msgstr "正充电" #: lib/logitech_receiver/i18n.py:38 msgid "not charging" msgstr "未在充电" #: lib/logitech_receiver/i18n.py:39 msgid "almost full" msgstr "接近全满" #: lib/logitech_receiver/i18n.py:40 msgid "charged" msgstr "已充满" #: lib/logitech_receiver/i18n.py:41 msgid "slow recharge" msgstr "慢速充电" #: lib/logitech_receiver/i18n.py:42 msgid "invalid battery" msgstr "电池无效" #: lib/logitech_receiver/i18n.py:43 msgid "thermal error" msgstr "温度异常" #: lib/logitech_receiver/i18n.py:44 msgid "error" msgstr "异常" #: lib/logitech_receiver/i18n.py:45 msgid "standard" msgstr "标准" #: lib/logitech_receiver/i18n.py:46 msgid "fast" msgstr "快速" #: lib/logitech_receiver/i18n.py:47 msgid "slow" msgstr "慢速" #: lib/logitech_receiver/i18n.py:49 msgid "device timeout" msgstr "设备超时" #: lib/logitech_receiver/i18n.py:50 msgid "device not supported" msgstr "不支持的设备" #: lib/logitech_receiver/i18n.py:51 msgid "too many devices" msgstr "设备数量太多" #: lib/logitech_receiver/i18n.py:52 msgid "sequence timeout" msgstr "序列超时" #: lib/logitech_receiver/i18n.py:54 msgid "Firmware" msgstr "固件" #: lib/logitech_receiver/i18n.py:55 msgid "Bootloader" msgstr "启动器" #: lib/logitech_receiver/i18n.py:56 msgid "Hardware" msgstr "硬件" #: lib/logitech_receiver/i18n.py:57 msgid "Other" msgstr "其他" #: lib/logitech_receiver/i18n.py:59 msgid "Left Button" msgstr "左键" #: lib/logitech_receiver/i18n.py:60 msgid "Right Button" msgstr "右键" #: lib/logitech_receiver/i18n.py:61 msgid "Middle Button" msgstr "中键" #: lib/logitech_receiver/i18n.py:62 msgid "Back Button" msgstr "返回键" #: lib/logitech_receiver/i18n.py:63 msgid "Forward Button" msgstr "前进键" #: lib/logitech_receiver/i18n.py:64 msgid "Mouse Gesture Button" msgstr "鼠标手势按钮" #: lib/logitech_receiver/i18n.py:65 msgid "Smart Shift" msgstr "SmartShift" #: lib/logitech_receiver/i18n.py:66 msgid "DPI Switch" msgstr "DPI 切换" #: lib/logitech_receiver/i18n.py:67 msgid "Left Tilt" msgstr "滚轮向左倾斜" #: lib/logitech_receiver/i18n.py:68 msgid "Right Tilt" msgstr "滚轮向右倾斜" #: lib/logitech_receiver/i18n.py:69 msgid "Left Click" msgstr "左击" #: lib/logitech_receiver/i18n.py:70 msgid "Right Click" msgstr "右击" #: lib/logitech_receiver/i18n.py:71 msgid "Mouse Middle Button" msgstr "鼠标中键" #: lib/logitech_receiver/i18n.py:72 msgid "Mouse Back Button" msgstr "鼠标返回键" #: lib/logitech_receiver/i18n.py:73 msgid "Mouse Forward Button" msgstr "鼠标前进键" #: lib/logitech_receiver/i18n.py:74 msgid "Gesture Button Navigation" msgstr "手势按钮导航" #: lib/logitech_receiver/i18n.py:75 msgid "Mouse Scroll Left Button" msgstr "鼠标向左滚动键" #: lib/logitech_receiver/i18n.py:76 msgid "Mouse Scroll Right Button" msgstr "鼠标向右滚动键" #: lib/logitech_receiver/i18n.py:78 msgid "pressed" msgstr "按下" #: lib/logitech_receiver/i18n.py:79 msgid "released" msgstr "释放" #: lib/logitech_receiver/notifications.py:64 #: lib/logitech_receiver/notifications.py:116 msgid "pairing lock is closed" msgstr "配对锁已关闭" #: lib/logitech_receiver/notifications.py:64 #: lib/logitech_receiver/notifications.py:116 msgid "pairing lock is open" msgstr "配对锁已打开" #: lib/logitech_receiver/notifications.py:81 msgid "discovery lock is closed" msgstr "发现锁已关闭" #: lib/logitech_receiver/notifications.py:81 msgid "discovery lock is open" msgstr "发现锁已打开" #: lib/logitech_receiver/notifications.py:210 lib/solaar/listener.py:238 msgid "connected" msgstr "已连接" #: lib/logitech_receiver/notifications.py:210 lib/solaar/listener.py:238 msgid "disconnected" msgstr "已断开" #: lib/logitech_receiver/notifications.py:236 msgid "unpaired" msgstr "未配对" #: lib/logitech_receiver/notifications.py:283 msgid "powered on" msgstr "已上电" #: lib/logitech_receiver/receiver.py:371 msgid "No paired devices." msgstr "无已配对设备。" #: lib/logitech_receiver/receiver.py:373 lib/solaar/ui/window.py:604 #, python-format msgid "%(count)s paired device." msgid_plural "%(count)s paired devices." msgstr[0] "%(count)s 个已配对设备。" #: lib/logitech_receiver/settings.py:611 msgid "register" msgstr "注册" #: lib/logitech_receiver/settings.py:625 lib/logitech_receiver/settings.py:651 msgid "feature" msgstr "特性" #: lib/logitech_receiver/settings_templates.py:122 msgid "Swap Fx function" msgstr "互换 Fx 键功能" #: lib/logitech_receiver/settings_templates.py:125 msgid "" "When set, the F1..F12 keys will activate their special function,\n" "and you must hold the FN key to activate their standard function." msgstr "" "若开启,F1 到 F12 键会激活特殊功能,\n" "使用标准功能请同时按住 FN 键。" #: lib/logitech_receiver/settings_templates.py:130 msgid "" "When unset, the F1..F12 keys will activate their standard function,\n" "and you must hold the FN key to activate their special function." msgstr "" "若关闭,F1 到 F12 键会激活标准功能,\n" "使用特殊功能请同时按住 FN 键。" #: lib/logitech_receiver/settings_templates.py:138 msgid "Hand Detection" msgstr "手掌识别" #: lib/logitech_receiver/settings_templates.py:139 msgid "Turn on illumination when the hands hover over the keyboard." msgstr "手掌置于键盘上方时开启照明。" #: lib/logitech_receiver/settings_templates.py:146 msgid "Scroll Wheel Smooth Scrolling" msgstr "滚轮平滑滚动" #: lib/logitech_receiver/settings_templates.py:147 #: lib/logitech_receiver/settings_templates.py:394 #: lib/logitech_receiver/settings_templates.py:423 msgid "High-sensitivity mode for vertical scroll with the wheel." msgstr "滚轮高分辨率滚动模式。" #: lib/logitech_receiver/settings_templates.py:154 msgid "Side Scrolling" msgstr "侧向滚轮" #: lib/logitech_receiver/settings_templates.py:156 msgid "" "When disabled, pushing the wheel sideways sends custom button events\n" "instead of the standard side-scrolling events." msgstr "" "若禁用,按下滚轮侧边将发送自定义按钮事件\n" "而不是标准侧向滚动事件。" #: lib/logitech_receiver/settings_templates.py:166 msgid "Sensitivity (DPI - older mice)" msgstr "灵敏度 (DPI - 旧款鼠标)" #: lib/logitech_receiver/settings_templates.py:167 #: lib/logitech_receiver/settings_templates.py:955 #: lib/logitech_receiver/settings_templates.py:983 msgid "Mouse movement sensitivity" msgstr "鼠标移动灵敏度" #: lib/logitech_receiver/settings_templates.py:240 msgid "Backlight Timed" msgstr "定时背光" #: lib/logitech_receiver/settings_templates.py:241 #: lib/logitech_receiver/settings_templates.py:381 msgid "Set illumination time for keyboard." msgstr "为键盘设置背光时间。" #: lib/logitech_receiver/settings_templates.py:252 msgid "Backlight" msgstr "背光" #: lib/logitech_receiver/settings_templates.py:253 msgid "" "Illumination level on keyboard. Changes made are only applied in Manual " "mode." msgstr "键盘亮度等级。仅在手动模式下可用" #: lib/logitech_receiver/settings_templates.py:285 msgid "Automatic" msgstr "自动" #: lib/logitech_receiver/settings_templates.py:287 msgid "Manual" msgstr "手动" #: lib/logitech_receiver/settings_templates.py:289 msgid "Enabled" msgstr "已启用" #: lib/logitech_receiver/settings_templates.py:295 msgid "Backlight Level" msgstr "背光等级" #: lib/logitech_receiver/settings_templates.py:296 msgid "Illumination level on keyboard when in Manual mode." msgstr "手动模式下的键盘亮度等级" #: lib/logitech_receiver/settings_templates.py:353 msgid "Backlight Delay Hands Out" msgstr "手离开键盘后背光关闭的延迟" #: lib/logitech_receiver/settings_templates.py:354 msgid "" "Delay in seconds until backlight fades out with hands away from keyboard." msgstr "手离开键盘后,背光渐灭的延迟秒数" #: lib/logitech_receiver/settings_templates.py:362 msgid "Backlight Delay Hands In" msgstr "手靠近键盘后背光亮起的延迟" #: lib/logitech_receiver/settings_templates.py:363 msgid "Delay in seconds until backlight fades out with hands near keyboard." msgstr "手靠近键盘后,背光亮起的延迟秒数" #: lib/logitech_receiver/settings_templates.py:371 msgid "Backlight Delay Powered" msgstr "外接电源时的背光延迟" #: lib/logitech_receiver/settings_templates.py:372 msgid "Delay in seconds until backlight fades out with external power." msgstr "外接电源时背光渐灭的延迟秒数" #: lib/logitech_receiver/settings_templates.py:380 msgid "Backlight (Seconds)" msgstr "背光(秒)" #: lib/logitech_receiver/settings_templates.py:392 msgid "Scroll Wheel High Resolution" msgstr "滚轮高分辨率" #: lib/logitech_receiver/settings_templates.py:396 #: lib/logitech_receiver/settings_templates.py:425 msgid "Set to ignore if scrolling is abnormally fast or slow" msgstr "如果滚动异常地快或慢,请设置为忽略" #: lib/logitech_receiver/settings_templates.py:403 #: lib/logitech_receiver/settings_templates.py:434 msgid "Scroll Wheel Diversion" msgstr "滚轮重定义" #: lib/logitech_receiver/settings_templates.py:405 msgid "" "Make scroll wheel send LOWRES_WHEEL HID++ notifications (which trigger " "Solaar rules but are otherwise ignored)." msgstr "" "使滚轮发送 LOWRES_WHEEL (低精度滚轮) HID++ 报告(如有Solaar规则,则被触发)" #: lib/logitech_receiver/settings_templates.py:412 msgid "Scroll Wheel Direction" msgstr "滚轮方向" #: lib/logitech_receiver/settings_templates.py:413 msgid "Invert direction for vertical scroll with wheel." msgstr "反转滚轮的垂直滚动方向。" #: lib/logitech_receiver/settings_templates.py:421 msgid "Scroll Wheel Resolution" msgstr "滚轮分辨率" #: lib/logitech_receiver/settings_templates.py:436 msgid "" "Make scroll wheel send HIRES_WHEEL HID++ notifications (which trigger Solaar " "rules but are otherwise ignored)." msgstr "" "使滚轮发送 HIRES_WHEEL (高精度滚轮) HID++ 报告(如有Solaar规则,则被触发)" #: lib/logitech_receiver/settings_templates.py:445 msgid "Sensitivity (Pointer Speed)" msgstr "灵敏度 (指针速度)" #: lib/logitech_receiver/settings_templates.py:446 msgid "Speed multiplier for mouse (256 is normal multiplier)." msgstr "鼠标速度倍率 (256 为常规倍率)。" #: lib/logitech_receiver/settings_templates.py:456 msgid "Thumb Wheel Diversion" msgstr "拇指滚轮重定义" #: lib/logitech_receiver/settings_templates.py:458 msgid "" "Make thumb wheel send THUMB_WHEEL HID++ notifications (which trigger Solaar " "rules but are otherwise ignored)." msgstr "" "使拇指滚轮发送 THUMB_WHEEL (拇指滚轮) HID++ 报告(如有Solaar规则,则被触发)" #: lib/logitech_receiver/settings_templates.py:467 msgid "Thumb Wheel Direction" msgstr "拇指滚轮方向" #: lib/logitech_receiver/settings_templates.py:468 msgid "Invert thumb wheel scroll direction." msgstr "反转拇指滚轮的滚动方向。" #: lib/logitech_receiver/settings_templates.py:488 msgid "Onboard Profiles" msgstr "板载预设" #: lib/logitech_receiver/settings_templates.py:489 msgid "" "Enable an onboard profile, which controls report rate, sensitivity, and " "button actions" msgstr "启用板载预设,用于控制回报率、灵敏度及按键功能" #: lib/logitech_receiver/settings_templates.py:533 #: lib/logitech_receiver/settings_templates.py:566 msgid "Report Rate" msgstr "回报率" #: lib/logitech_receiver/settings_templates.py:535 #: lib/logitech_receiver/settings_templates.py:568 msgid "Frequency of device movement reports" msgstr "设备移动事件的回报频率" #: lib/logitech_receiver/settings_templates.py:535 #: lib/logitech_receiver/settings_templates.py:568 #: lib/logitech_receiver/settings_templates.py:983 #: lib/logitech_receiver/settings_templates.py:1357 #: lib/logitech_receiver/settings_templates.py:1388 msgid "May need Onboard Profiles set to Disable to be effective." msgstr "可能需要禁用板载预设才能生效。" #: lib/logitech_receiver/settings_templates.py:596 msgid "Divert crown events" msgstr "重定义旋钮事件" #: lib/logitech_receiver/settings_templates.py:597 msgid "" "Make crown send CROWN HID++ notifications (which trigger Solaar rules but " "are otherwise ignored)." msgstr "使旋钮发送 CROWN (旋钮) HID++ 报告(如有Solaar规则,则被触发)" #: lib/logitech_receiver/settings_templates.py:605 msgid "Crown smooth scroll" msgstr "旋钮平滑旋动" #: lib/logitech_receiver/settings_templates.py:606 msgid "Set crown smooth scroll" msgstr "设置旋钮的平滑旋动" #: lib/logitech_receiver/settings_templates.py:614 msgid "Divert G and M Keys" msgstr "转换G键及M键" #: lib/logitech_receiver/settings_templates.py:615 msgid "" "Make G and M keys send HID++ notifications (which trigger Solaar rules but " "are otherwise ignored)." msgstr "G键及M键将发送 HID++ 报告(如有Solaar规则,则被触发)" #: lib/logitech_receiver/settings_templates.py:629 msgid "Scroll Wheel Ratcheted" msgstr "滚轮棘轮" #: lib/logitech_receiver/settings_templates.py:630 msgid "" "Switch the mouse wheel between speed-controlled ratcheting and always " "freespin." msgstr "在滚速响应式棘轮及平滑滚动模式间切换" #: lib/logitech_receiver/settings_templates.py:632 msgid "Freespinning" msgstr "平滑滚动" #: lib/logitech_receiver/settings_templates.py:632 msgid "Ratcheted" msgstr "棘轮式" #: lib/logitech_receiver/settings_templates.py:639 msgid "Scroll Wheel Ratchet Speed" msgstr "滚轮棘轮速度" #: lib/logitech_receiver/settings_templates.py:641 msgid "" "Use the mouse wheel speed to switch between ratcheted and freespinning.\n" "The mouse wheel is always ratcheted at 50." msgstr "" "根据鼠标滚轮速度,在棘轮模式和平滑滚动模式间切换。\n" "50时将始终处于棘轮模式。" #: lib/logitech_receiver/settings_templates.py:690 msgid "Key/Button Actions" msgstr "按键/按钮行为" #: lib/logitech_receiver/settings_templates.py:692 msgid "Change the action for the key or button." msgstr "更改按键或按钮的行为。" #: lib/logitech_receiver/settings_templates.py:694 msgid "Overridden by diversion." msgstr "会被重定义配置覆盖。" #: lib/logitech_receiver/settings_templates.py:696 msgid "" "Changing important actions (such as for the left mouse button) can result in " "an unusable system." msgstr "更改关键行为 (如鼠标左键) 可能会导致无法操作。" #: lib/logitech_receiver/settings_templates.py:862 msgid "Key/Button Diversion" msgstr "按键/按钮重定义" #: lib/logitech_receiver/settings_templates.py:863 msgid "" "Make the key or button send HID++ notifications (Diverted) or initiate Mouse " "Gestures or Sliding DPI" msgstr "使按键或按钮发送 HID++ (重定义)报告 、触发鼠标手势或滑动调节 DPI" #: lib/logitech_receiver/settings_templates.py:866 #: lib/logitech_receiver/settings_templates.py:867 #: lib/logitech_receiver/settings_templates.py:868 msgid "Diverted" msgstr "重定义" #: lib/logitech_receiver/settings_templates.py:866 #: lib/logitech_receiver/settings_templates.py:867 msgid "Mouse Gestures" msgstr "鼠标手势" #: lib/logitech_receiver/settings_templates.py:866 #: lib/logitech_receiver/settings_templates.py:867 #: lib/logitech_receiver/settings_templates.py:868 msgid "Regular" msgstr "常规" #: lib/logitech_receiver/settings_templates.py:866 msgid "Sliding DPI" msgstr "滑动调节 DPI" #: lib/logitech_receiver/settings_templates.py:954 #: lib/logitech_receiver/settings_templates.py:982 msgid "Sensitivity (DPI)" msgstr "灵敏度 (DPI)" #: lib/logitech_receiver/settings_templates.py:1059 msgid "Sensitivity Switching" msgstr "灵敏度切换" #: lib/logitech_receiver/settings_templates.py:1061 msgid "" "Switch the current sensitivity and the remembered sensitivity when the key " "or button is pressed.\n" "If there is no remembered sensitivity, just remember the current sensitivity" msgstr "" "切换当前灵敏度,并记忆按键或按钮按下时的灵敏度。\n" "如果没有已记忆的灵敏度,就只会记忆当前灵敏度" #: lib/logitech_receiver/settings_templates.py:1065 msgid "Off" msgstr "关" #: lib/logitech_receiver/settings_templates.py:1096 msgid "Disable keys" msgstr "禁用按键" #: lib/logitech_receiver/settings_templates.py:1097 msgid "Disable specific keyboard keys." msgstr "禁用特定的键盘按键。" #: lib/logitech_receiver/settings_templates.py:1100 #, python-format msgid "Disables the %s key." msgstr "禁用 %s 按键。" #: lib/logitech_receiver/settings_templates.py:1113 #: lib/logitech_receiver/settings_templates.py:1170 msgid "Set OS" msgstr "设置操作系统" #: lib/logitech_receiver/settings_templates.py:1114 #: lib/logitech_receiver/settings_templates.py:1171 msgid "Change keys to match OS." msgstr "改变键位以匹配操作系统。" #: lib/logitech_receiver/settings_templates.py:1183 msgid "Change Host" msgstr "切换主机" #: lib/logitech_receiver/settings_templates.py:1184 msgid "Switch connection to a different host" msgstr "切换连接的主机" #: lib/logitech_receiver/settings_templates.py:1208 msgid "Performs a left click." msgstr "单击鼠标左键。" #: lib/logitech_receiver/settings_templates.py:1208 msgid "Single tap" msgstr "单指点击" #: lib/logitech_receiver/settings_templates.py:1209 msgid "Performs a right click." msgstr "单击鼠标右键。" #: lib/logitech_receiver/settings_templates.py:1209 msgid "Single tap with two fingers" msgstr "双指点击" #: lib/logitech_receiver/settings_templates.py:1210 msgid "Single tap with three fingers" msgstr "三指点击" #: lib/logitech_receiver/settings_templates.py:1214 msgid "Double tap" msgstr "单指双击" #: lib/logitech_receiver/settings_templates.py:1214 msgid "Performs a double click." msgstr "双击。" #: lib/logitech_receiver/settings_templates.py:1215 msgid "Double tap with two fingers" msgstr "双指双击" #: lib/logitech_receiver/settings_templates.py:1216 msgid "Double tap with three fingers" msgstr "三指双击" #: lib/logitech_receiver/settings_templates.py:1219 msgid "Drags items by dragging the finger after double tapping." msgstr "单指双击后移动手指来拖动项目。" #: lib/logitech_receiver/settings_templates.py:1219 msgid "Tap and drag" msgstr "触摸并拖动" #: lib/logitech_receiver/settings_templates.py:1221 msgid "Tap and drag with two fingers" msgstr "双指触摸并拖动" #: lib/logitech_receiver/settings_templates.py:1222 msgid "Drags items by dragging the fingers after double tapping." msgstr "多指双击后移动手指来拖动项目。" #: lib/logitech_receiver/settings_templates.py:1224 msgid "Tap and drag with three fingers" msgstr "三指触摸并拖动" #: lib/logitech_receiver/settings_templates.py:1227 msgid "Suppress tap and edge gestures" msgstr "禁用触摸和边缘手势" #: lib/logitech_receiver/settings_templates.py:1228 msgid "Disables tap and edge gestures (equivalent to pressing Fn+LeftClick)." msgstr "禁用触摸和边缘手势 (等价于按下 Fn+左键单击)。" #: lib/logitech_receiver/settings_templates.py:1230 msgid "Scroll with one finger" msgstr "单指滚动" #: lib/logitech_receiver/settings_templates.py:1230 #: lib/logitech_receiver/settings_templates.py:1231 #: lib/logitech_receiver/settings_templates.py:1234 msgid "Scrolls." msgstr "滚动。" #: lib/logitech_receiver/settings_templates.py:1231 #: lib/logitech_receiver/settings_templates.py:1234 msgid "Scroll with two fingers" msgstr "双指滚动" #: lib/logitech_receiver/settings_templates.py:1232 msgid "Scroll horizontally with two fingers" msgstr "双指水平滚动" #: lib/logitech_receiver/settings_templates.py:1232 msgid "Scrolls horizontally." msgstr "水平滚动。" #: lib/logitech_receiver/settings_templates.py:1233 msgid "Scroll vertically with two fingers" msgstr "双指垂直滚动" #: lib/logitech_receiver/settings_templates.py:1233 msgid "Scrolls vertically." msgstr "垂直滚动。" #: lib/logitech_receiver/settings_templates.py:1235 msgid "Inverts the scrolling direction." msgstr "反转滚动方向。" #: lib/logitech_receiver/settings_templates.py:1235 msgid "Natural scrolling" msgstr "自然滚动" #: lib/logitech_receiver/settings_templates.py:1236 msgid "Enables the thumbwheel." msgstr "启用拇指滚轮。" #: lib/logitech_receiver/settings_templates.py:1236 msgid "Thumbwheel" msgstr "拇指滚轮" #: lib/logitech_receiver/settings_templates.py:1247 #: lib/logitech_receiver/settings_templates.py:1251 msgid "Swipe from the top edge" msgstr "从顶部边缘滑动" #: lib/logitech_receiver/settings_templates.py:1248 msgid "Swipe from the left edge" msgstr "从左侧边缘滑动" #: lib/logitech_receiver/settings_templates.py:1249 msgid "Swipe from the right edge" msgstr "从右侧边缘滑动" #: lib/logitech_receiver/settings_templates.py:1250 msgid "Swipe from the bottom edge" msgstr "从底部边缘滑动" #: lib/logitech_receiver/settings_templates.py:1252 msgid "Swipe two fingers from the left edge" msgstr "从左侧边缘双指滑动" #: lib/logitech_receiver/settings_templates.py:1253 msgid "Swipe two fingers from the right edge" msgstr "从右侧边缘双指滑动" #: lib/logitech_receiver/settings_templates.py:1254 msgid "Swipe two fingers from the bottom edge" msgstr "从底部边缘双指滑动" #: lib/logitech_receiver/settings_templates.py:1255 msgid "Swipe two fingers from the top edge" msgstr "从顶部边缘双指滑动" #: lib/logitech_receiver/settings_templates.py:1256 #: lib/logitech_receiver/settings_templates.py:1260 msgid "Pinch to zoom out; spread to zoom in." msgstr "通过捏合手指缩小;伸展手指以放大。" #: lib/logitech_receiver/settings_templates.py:1256 msgid "Zoom with two fingers." msgstr "双指缩放。" #: lib/logitech_receiver/settings_templates.py:1257 msgid "Pinch to zoom out." msgstr "捏合手指以缩小。" #: lib/logitech_receiver/settings_templates.py:1258 msgid "Spread to zoom in." msgstr "伸展手指以放大。" #: lib/logitech_receiver/settings_templates.py:1259 msgid "Zoom with three fingers." msgstr "三指缩放。" #: lib/logitech_receiver/settings_templates.py:1260 msgid "Zoom with two fingers" msgstr "双指缩放" #: lib/logitech_receiver/settings_templates.py:1278 msgid "Pixel zone" msgstr "像素区" #: lib/logitech_receiver/settings_templates.py:1279 msgid "Ratio zone" msgstr "比例区" #: lib/logitech_receiver/settings_templates.py:1280 msgid "Scale factor" msgstr "缩放比例" #: lib/logitech_receiver/settings_templates.py:1280 msgid "Sets the cursor speed." msgstr "设置指针速度。" #: lib/logitech_receiver/settings_templates.py:1284 msgid "Left" msgstr "左侧" #: lib/logitech_receiver/settings_templates.py:1284 msgid "Left-most coordinate." msgstr "最左坐标。" #: lib/logitech_receiver/settings_templates.py:1285 msgid "Bottom" msgstr "底部" #: lib/logitech_receiver/settings_templates.py:1285 msgid "Bottom coordinate." msgstr "底部坐标。" #: lib/logitech_receiver/settings_templates.py:1286 msgid "Width" msgstr "宽度" #: lib/logitech_receiver/settings_templates.py:1286 msgid "Width." msgstr "宽度。" #: lib/logitech_receiver/settings_templates.py:1287 msgid "Height" msgstr "高度" #: lib/logitech_receiver/settings_templates.py:1287 msgid "Height." msgstr "高度。" #: lib/logitech_receiver/settings_templates.py:1288 msgid "Cursor speed." msgstr "指针速度。" #: lib/logitech_receiver/settings_templates.py:1288 msgid "Scale" msgstr "比例" #: lib/logitech_receiver/settings_templates.py:1294 msgid "Gestures" msgstr "手势" #: lib/logitech_receiver/settings_templates.py:1295 msgid "Tweak the mouse/touchpad behaviour." msgstr "调整鼠标或触摸板的行为。" #: lib/logitech_receiver/settings_templates.py:1311 msgid "Gestures Diversion" msgstr "手势重定义" #: lib/logitech_receiver/settings_templates.py:1312 msgid "Divert mouse/touchpad gestures." msgstr "重定义鼠标或触摸板手势。" #: lib/logitech_receiver/settings_templates.py:1328 msgid "Gesture params" msgstr "手势参数" #: lib/logitech_receiver/settings_templates.py:1329 msgid "Change numerical parameters of a mouse/touchpad." msgstr "更改鼠标或触摸板的数值参数。" #: lib/logitech_receiver/settings_templates.py:1353 msgid "M-Key LEDs" msgstr "M 键 LED" #: lib/logitech_receiver/settings_templates.py:1355 msgid "Control the M-Key LEDs." msgstr "控制 M 键的 LED。" #: lib/logitech_receiver/settings_templates.py:1359 #: lib/logitech_receiver/settings_templates.py:1390 msgid "May need G Keys diverted to be effective." msgstr "可能需要开启 G 键重定义。" #: lib/logitech_receiver/settings_templates.py:1365 #, python-format msgid "Lights up the %s key." msgstr "点亮 %s 按键。" #: lib/logitech_receiver/settings_templates.py:1384 msgid "MR-Key LED" msgstr "MR 键 LED" #: lib/logitech_receiver/settings_templates.py:1386 msgid "Control the MR-Key LED." msgstr "控制 MR 键的 LED。" #: lib/logitech_receiver/settings_templates.py:1407 msgid "Persistent Key/Button Mapping" msgstr "持久化按键/按钮映射" #: lib/logitech_receiver/settings_templates.py:1409 msgid "Permanently change the mapping for the key or button." msgstr "永久更改按键或按钮的映射。" #: lib/logitech_receiver/settings_templates.py:1411 msgid "" "Changing important keys or buttons (such as for the left mouse button) can " "result in an unusable system." msgstr "更改重要的按键或按钮 (如鼠标左键) 可以导致系统无法使用。" #: lib/logitech_receiver/settings_templates.py:1468 msgid "Sidetone" msgstr "侧音 (麦克风回馈)" #: lib/logitech_receiver/settings_templates.py:1469 msgid "Set sidetone level." msgstr "设置侧音水平。" #: lib/logitech_receiver/settings_templates.py:1478 msgid "Equalizer" msgstr "均衡器" #: lib/logitech_receiver/settings_templates.py:1479 msgid "Set equalizer levels." msgstr "设置均衡器水平。" #: lib/logitech_receiver/settings_templates.py:1501 msgid "Hz" msgstr "" #: lib/logitech_receiver/settings_templates.py:1507 msgid "Power Management" msgstr "电源管理" #: lib/logitech_receiver/settings_templates.py:1508 msgid "Power off in minutes (0 for never)." msgstr "分钟后关机(0为不关机)" #: lib/logitech_receiver/settings_templates.py:1519 msgid "Brightness Control" msgstr "亮度控制" #: lib/logitech_receiver/settings_templates.py:1520 msgid "Control overall brightness" msgstr "全局亮度控制" #: lib/logitech_receiver/settings_templates.py:1563 #: lib/logitech_receiver/settings_templates.py:1617 msgid "LED Control" msgstr "LED控制" #: lib/logitech_receiver/settings_templates.py:1564 #: lib/logitech_receiver/settings_templates.py:1618 msgid "Switch control of LED zones between device and Solaar" msgstr "在设备和Solaar间切换LED区域控制" #: lib/logitech_receiver/settings_templates.py:1579 #: lib/logitech_receiver/settings_templates.py:1628 msgid "LED Zone Effects" msgstr "LED区域效果" #: lib/logitech_receiver/settings_templates.py:1580 #: lib/logitech_receiver/settings_templates.py:1629 msgid "LED Control needs to be set to Solaar to be effective." msgstr "设置并启用LED控制到Solaar" #: lib/logitech_receiver/settings_templates.py:1580 #: lib/logitech_receiver/settings_templates.py:1629 msgid "Set effect for LED Zone" msgstr "设置LED区域效果" #: lib/logitech_receiver/settings_templates.py:1583 msgid "Speed" msgstr "速度" #: lib/logitech_receiver/settings_templates.py:1584 msgid "Period" msgstr "周期" #: lib/logitech_receiver/settings_templates.py:1585 msgid "Intensity" msgstr "强度" #: lib/logitech_receiver/settings_templates.py:1586 msgid "Ramp" msgstr "渐变" #: lib/logitech_receiver/settings_templates.py:1602 msgid "LEDs" msgstr "LED" #: lib/logitech_receiver/settings_templates.py:1639 msgid "Per-key Lighting" msgstr "按键独立灯光" #: lib/logitech_receiver/settings_templates.py:1640 msgid "Control per-key lighting." msgstr "设置按键独立灯光" #: lib/solaar/ui/__init__.py:103 msgid "Another Solaar process is already running so just expose its window" msgstr "另一个 Solaar 进程正在运行,已唤醒窗口" #: lib/solaar/ui/about.py:34 msgid "" "Manages Logitech receivers,\n" "keyboards, mice, and tablets." msgstr "" "管理罗技接收器、\n" "键盘、鼠标和平板电脑。" #: lib/solaar/ui/about.py:43 msgid "Additional Programming" msgstr "协作研发" #: lib/solaar/ui/about.py:44 msgid "GUI design" msgstr "界面设计" #: lib/solaar/ui/about.py:46 msgid "Testing" msgstr "软件测试" #: lib/solaar/ui/about.py:54 msgid "Logitech documentation" msgstr "罗技文档" #: lib/solaar/ui/action.py:83 lib/solaar/ui/action.py:87 #: lib/solaar/ui/window.py:189 msgid "Unpair" msgstr "取消配对" #: lib/solaar/ui/action.py:86 lib/solaar/ui/diversion_rules.py:103 msgid "Cancel" msgstr "取消" #: lib/solaar/ui/common.py:35 msgid "Permissions error" msgstr "权限错误" #: lib/solaar/ui/common.py:37 #, python-format msgid "" "Found a Logitech receiver or device (%s), but did not have permission to " "open it." msgstr "发现罗技接收器或设备(%s),但无访问权限" #: lib/solaar/ui/common.py:39 msgid "" "If you've just installed Solaar, try disconnecting the receiver or device " "and then reconnecting it." msgstr "若您是刚安装Solaar,请插拔一下罗技接收器或设备" #: lib/solaar/ui/common.py:42 msgid "Cannot connect to device error" msgstr "错误: 无法连接到设备" #: lib/solaar/ui/common.py:44 #, python-format msgid "" "Found a Logitech receiver or device at %s, but encountered an error " "connecting to it." msgstr "已在 %s 上发现 1 个罗技接收器或设备,但连接时发生错误。" #: lib/solaar/ui/common.py:46 msgid "" "Try disconnecting the device and then reconnecting it or turning it off and " "then on." msgstr "请尝试插拔设备,或关闭后重新打开设备" #: lib/solaar/ui/common.py:49 msgid "Unpairing failed" msgstr "取消配对失败" #: lib/solaar/ui/common.py:51 #, python-brace-format msgid "Failed to unpair %{device} from %{receiver}." msgstr "取消 %{device} 到 %{receiver} 的配对失败。" #: lib/solaar/ui/common.py:53 msgid "The receiver returned an error, with no further details." msgstr "接收器出现错误,无详细信息。" #: lib/solaar/ui/config_panel.py:228 msgid "Complete - ENTER to change" msgstr "完成 - 按下 ENTER 键以进行更改" #: lib/solaar/ui/config_panel.py:228 msgid "Incomplete" msgstr "未完成" #: lib/solaar/ui/config_panel.py:470 lib/solaar/ui/config_panel.py:522 #, python-format msgid "%d value" msgid_plural "%d values" msgstr[0] "%d 个值" #: lib/solaar/ui/config_panel.py:603 msgid "Changes allowed" msgstr "允许更改" #: lib/solaar/ui/config_panel.py:604 msgid "No changes allowed" msgstr "不允许更改" #: lib/solaar/ui/config_panel.py:605 msgid "Ignore this setting" msgstr "忽略此设置" #: lib/solaar/ui/config_panel.py:649 msgid "Working" msgstr "工作中" #: lib/solaar/ui/config_panel.py:652 msgid "Read/write operation failed." msgstr "读写操作失败。" #: lib/solaar/ui/diversion_rules.py:65 msgid "Built-in rules" msgstr "内置规则" #: lib/solaar/ui/diversion_rules.py:65 msgid "User-defined rules" msgstr "自定义规则" #: lib/solaar/ui/diversion_rules.py:67 lib/solaar/ui/diversion_rules.py:1033 msgid "Rule" msgstr "规则" #: lib/solaar/ui/diversion_rules.py:68 lib/solaar/ui/diversion_rules.py:545 #: lib/solaar/ui/diversion_rules.py:672 msgid "Sub-rule" msgstr "子规则" #: lib/solaar/ui/diversion_rules.py:70 msgid "[empty]" msgstr "[空]" #: lib/solaar/ui/diversion_rules.py:94 msgid "Make changes permanent?" msgstr "永久应用更改?" #: lib/solaar/ui/diversion_rules.py:99 msgid "Yes" msgstr "是" #: lib/solaar/ui/diversion_rules.py:101 msgid "No" msgstr "否" #: lib/solaar/ui/diversion_rules.py:106 msgid "If you choose No, changes will be lost when Solaar is closed." msgstr "如果选择否,改动会在 Solaar 关闭后丢失。" #: lib/solaar/ui/diversion_rules.py:167 msgid "Solaar Rule Editor" msgstr "Solaar 规则编辑器" #: lib/solaar/ui/diversion_rules.py:260 msgid "Save changes" msgstr "保存更改" #: lib/solaar/ui/diversion_rules.py:265 msgid "Discard changes" msgstr "放弃更改" #: lib/solaar/ui/diversion_rules.py:404 msgid "Insert here" msgstr "插入" #: lib/solaar/ui/diversion_rules.py:406 msgid "Insert above" msgstr "在上方插入" #: lib/solaar/ui/diversion_rules.py:408 msgid "Insert below" msgstr "在下方插入" #: lib/solaar/ui/diversion_rules.py:414 msgid "Insert new rule here" msgstr "新建规则" #: lib/solaar/ui/diversion_rules.py:416 msgid "Insert new rule above" msgstr "在上方添加规则" #: lib/solaar/ui/diversion_rules.py:418 msgid "Insert new rule below" msgstr "在下方添加规则" #: lib/solaar/ui/diversion_rules.py:463 msgid "Paste here" msgstr "在此处粘贴" #: lib/solaar/ui/diversion_rules.py:465 msgid "Paste above" msgstr "在上方粘贴" #: lib/solaar/ui/diversion_rules.py:467 msgid "Paste below" msgstr "在下方粘贴" #: lib/solaar/ui/diversion_rules.py:473 msgid "Paste rule here" msgstr "在此处粘贴规则" #: lib/solaar/ui/diversion_rules.py:475 msgid "Paste rule above" msgstr "在上方粘贴规则" #: lib/solaar/ui/diversion_rules.py:477 msgid "Paste rule below" msgstr "在下方粘贴规则" #: lib/solaar/ui/diversion_rules.py:481 msgid "Paste rule" msgstr "粘贴规则" #: lib/solaar/ui/diversion_rules.py:510 msgid "Flatten" msgstr "展平" #: lib/solaar/ui/diversion_rules.py:543 msgid "Insert" msgstr "插入" #: lib/solaar/ui/diversion_rules.py:546 lib/solaar/ui/diversion_rules.py:674 #: lib/solaar/ui/diversion_rules.py:1065 msgid "Or" msgstr "或" #: lib/solaar/ui/diversion_rules.py:547 lib/solaar/ui/diversion_rules.py:673 #: lib/solaar/ui/diversion_rules.py:1051 msgid "And" msgstr "且" #: lib/solaar/ui/diversion_rules.py:549 msgid "Condition" msgstr "条件" #: lib/solaar/ui/diversion_rules.py:551 lib/solaar/ui/rule_conditions.py:146 msgid "Feature" msgstr "特性" #: lib/solaar/ui/diversion_rules.py:552 lib/solaar/ui/rule_conditions.py:181 msgid "Report" msgstr "回报" #: lib/solaar/ui/diversion_rules.py:553 lib/solaar/ui/rule_conditions.py:60 msgid "Process" msgstr "焦点所在进程" #: lib/solaar/ui/diversion_rules.py:554 msgid "Mouse process" msgstr "光标所在进程" #: lib/solaar/ui/diversion_rules.py:555 lib/solaar/ui/rule_conditions.py:218 msgid "Modifiers" msgstr "修饰键" #: lib/solaar/ui/diversion_rules.py:556 lib/solaar/ui/rule_conditions.py:270 msgid "Key" msgstr "按键" #: lib/solaar/ui/diversion_rules.py:557 lib/solaar/ui/rule_conditions.py:311 msgid "KeyIsDown" msgstr "按键已按下" #: lib/solaar/ui/diversion_rules.py:558 lib/solaar/ui/diversion_rules.py:1359 msgid "Active" msgstr "活跃设备" #: lib/solaar/ui/diversion_rules.py:559 lib/solaar/ui/diversion_rules.py:1316 #: lib/solaar/ui/diversion_rules.py:1368 lib/solaar/ui/diversion_rules.py:1418 msgid "Device" msgstr "设备" #: lib/solaar/ui/diversion_rules.py:560 lib/solaar/ui/diversion_rules.py:1394 msgid "Host" msgstr "主机" #: lib/solaar/ui/diversion_rules.py:561 lib/solaar/ui/diversion_rules.py:1436 msgid "Setting" msgstr "设置" #: lib/solaar/ui/diversion_rules.py:562 lib/solaar/ui/rule_conditions.py:326 #: lib/solaar/ui/rule_conditions.py:375 msgid "Test" msgstr "测试" #: lib/solaar/ui/diversion_rules.py:563 lib/solaar/ui/rule_conditions.py:500 msgid "Test bytes" msgstr "字节检测" #: lib/solaar/ui/diversion_rules.py:564 lib/solaar/ui/rule_conditions.py:601 msgid "Mouse Gesture" msgstr "鼠标手势" #: lib/solaar/ui/diversion_rules.py:568 msgid "Action" msgstr "操作" #: lib/solaar/ui/diversion_rules.py:570 lib/solaar/ui/rule_actions.py:132 msgid "Key press" msgstr "按下按键" #: lib/solaar/ui/diversion_rules.py:571 lib/solaar/ui/rule_actions.py:183 msgid "Mouse scroll" msgstr "移动鼠标" #: lib/solaar/ui/diversion_rules.py:572 lib/solaar/ui/rule_actions.py:244 msgid "Mouse click" msgstr "鼠标点击" #: lib/solaar/ui/diversion_rules.py:573 msgid "Set" msgstr "更改设置" #: lib/solaar/ui/diversion_rules.py:574 lib/solaar/ui/rule_actions.py:314 msgid "Execute" msgstr "执行程序" #: lib/solaar/ui/diversion_rules.py:575 lib/solaar/ui/diversion_rules.py:1096 msgid "Later" msgstr "延迟" #: lib/solaar/ui/diversion_rules.py:604 msgid "Insert new rule" msgstr "插入新规则" #: lib/solaar/ui/diversion_rules.py:624 lib/solaar/ui/rule_actions.py:74 #: lib/solaar/ui/rule_actions.py:273 lib/solaar/ui/rule_conditions.py:548 msgid "Delete" msgstr "删除" #: lib/solaar/ui/diversion_rules.py:646 msgid "Negate" msgstr "取反" #: lib/solaar/ui/diversion_rules.py:670 msgid "Wrap with" msgstr "以…包裹" #: lib/solaar/ui/diversion_rules.py:692 msgid "Cut" msgstr "剪切" #: lib/solaar/ui/diversion_rules.py:707 msgid "Paste" msgstr "粘贴" #: lib/solaar/ui/diversion_rules.py:713 msgid "Copy" msgstr "复制" #: lib/solaar/ui/diversion_rules.py:1014 msgid "This editor does not support the selected rule component yet." msgstr "此编辑器尚未支持所选的规则组件。" #: lib/solaar/ui/diversion_rules.py:1076 msgid "" "Number of seconds to delay. Delay between 0 and 1 is done with higher " "precision." msgstr "延迟秒数。0到1之间的延迟将通过高精度实现" #: lib/solaar/ui/diversion_rules.py:1114 msgid "Not" msgstr "非" #: lib/solaar/ui/diversion_rules.py:1145 msgid "Toggle" msgstr "切换" #: lib/solaar/ui/diversion_rules.py:1146 msgid "True" msgstr "真" #: lib/solaar/ui/diversion_rules.py:1147 msgid "False" msgstr "假" #: lib/solaar/ui/diversion_rules.py:1160 msgid "Unsupported setting" msgstr "不支持的设置" #: lib/solaar/ui/diversion_rules.py:1322 lib/solaar/ui/diversion_rules.py:1342 #: lib/solaar/ui/diversion_rules.py:1424 lib/solaar/ui/diversion_rules.py:1669 #: lib/solaar/ui/diversion_rules.py:1687 msgid "Originating device" msgstr "源设备" #: lib/solaar/ui/diversion_rules.py:1355 msgid "Device is active and its settings can be changed." msgstr "设备已激活,且可更改其设置。" #: lib/solaar/ui/diversion_rules.py:1364 msgid "Device that originated the current notification." msgstr "触发当前事件的设备" #: lib/solaar/ui/diversion_rules.py:1377 msgid "Name of host computer." msgstr "主机名" #: lib/solaar/ui/diversion_rules.py:1445 msgid "Value" msgstr "值" #: lib/solaar/ui/diversion_rules.py:1454 msgid "Item" msgstr "项目" #: lib/solaar/ui/diversion_rules.py:1728 msgid "Change setting on device" msgstr "更改设备上保存的设置" #: lib/solaar/ui/diversion_rules.py:1744 msgid "Setting on device" msgstr "设备上保存的设置" #: lib/solaar/ui/notify.py:115 msgid "unspecified reason" msgstr "未定原因" #: lib/solaar/ui/pair_window.py:37 lib/solaar/ui/pair_window.py:155 #, python-format msgid "%(receiver_name)s: pair new device" msgstr "%(receiver_name)s: 配对新设备" #: lib/solaar/ui/pair_window.py:39 msgid "Bolt receivers are only compatible with Bolt devices." msgstr "Bolt接收器仅适用于Bolt设备" #: lib/solaar/ui/pair_window.py:41 msgid "Press a pairing button or key until the pairing light flashes quickly." msgstr "按住配对按钮或按键直到配对指示灯快速闪烁。" #: lib/solaar/ui/pair_window.py:44 msgid "Unifying receivers are only compatible with Unifying devices." msgstr "优联接收器仅适用于优联设备" #: lib/solaar/ui/pair_window.py:46 msgid "Other receivers are only compatible with a few devices." msgstr "其他接收器仅能用于少量设备" #: lib/solaar/ui/pair_window.py:48 msgid "Turn on the device you want to pair." msgstr "打开需配对设备的电源。" #: lib/solaar/ui/pair_window.py:49 msgid "The device must not be paired with a nearby powered-on receiver." msgstr "设备不可与附近已上电的接收器处于配对状态" #: lib/solaar/ui/pair_window.py:51 msgid "If the device is already turned on, turn it off and on again." msgstr "如果设备电源已经打开,请先关闭再重新打开。" #: lib/solaar/ui/pair_window.py:55 #, python-format msgid "" "\n" "\n" "This receiver has %d pairing remaining." msgid_plural "" "\n" "\n" "This receiver has %d pairings remaining." msgstr[0] "" "\n" "\n" "此接收器还可再与 %d 个设备配对。" #: lib/solaar/ui/pair_window.py:61 msgid "" "\n" "Cancelling at this point will not use up a pairing." msgstr "" "\n" "此时取消将不会使配对生效。" #: lib/solaar/ui/pair_window.py:156 #, python-format msgid "Enter passcode on %(name)s." msgstr "在 %(name)s 上输入密码。" #: lib/solaar/ui/pair_window.py:159 #, python-format msgid "Type %(passcode)s and then press the enter key." msgstr "输入 %(passcode)s 后按下回车键。" #: lib/solaar/ui/pair_window.py:162 msgid "left" msgstr "左键" #: lib/solaar/ui/pair_window.py:162 msgid "right" msgstr "右键" #: lib/solaar/ui/pair_window.py:164 #, python-format msgid "" "Press %(code)s\n" "and then press left and right buttons simultaneously." msgstr "" "按下 %(code)s\n" "后同时按下左键和右键。" #: lib/solaar/ui/pair_window.py:195 msgid "The wireless link is not encrypted" msgstr "无线连接未加密" #: lib/solaar/ui/pair_window.py:200 msgid "Found a new device:" msgstr "发现新设备:" #: lib/solaar/ui/pair_window.py:221 msgid "Pairing failed" msgstr "配对失败" #: lib/solaar/ui/pair_window.py:223 msgid "Make sure your device is within range, and has a decent battery charge." msgstr "请确认您的设备已在范围内,并保证其电量充足。" #: lib/solaar/ui/pair_window.py:225 msgid "A new device was detected, but it is not compatible with this receiver." msgstr "检测到 1 个新设备,但与此接收器不兼容。" #: lib/solaar/ui/pair_window.py:227 msgid "More paired devices than receiver can support." msgstr "已配对设备超出接收器所支持的数量。" #: lib/solaar/ui/pair_window.py:229 msgid "No further details are available about the error." msgstr "无此错误的详细信息。" #: lib/solaar/ui/rule_actions.py:48 msgid "" "Simulate a chorded key click or depress or release.\n" "On Wayland requires write access to /dev/uinput." msgstr "" "模拟同时点击、按住或释放一个按键。\n" "在 Wayland 上需要具备对 /dev/input 的写权限。" #: lib/solaar/ui/rule_actions.py:53 msgid "Add key" msgstr "添加按键" #: lib/solaar/ui/rule_actions.py:56 msgid "Click" msgstr "点击" #: lib/solaar/ui/rule_actions.py:59 msgid "Depress" msgstr "按住" #: lib/solaar/ui/rule_actions.py:62 msgid "Release" msgstr "释放" #: lib/solaar/ui/rule_actions.py:147 msgid "" "Simulate a mouse scroll.\n" "On Wayland requires write access to /dev/uinput." msgstr "" "模拟一次鼠标滚轮滚动。\n" "在 Wayland 上需要具备对 /dev/input 的写权限。" #: lib/solaar/ui/rule_actions.py:203 msgid "" "Simulate a mouse click.\n" "On Wayland requires write access to /dev/uinput." msgstr "" "模拟一次鼠标点击。\n" "在 Wayland 上需要具备对 /dev/input 的写权限。" #: lib/solaar/ui/rule_actions.py:206 msgid "Button" msgstr "按钮" #: lib/solaar/ui/rule_actions.py:207 msgid "Count and Action" msgstr "计数及行为" #: lib/solaar/ui/rule_actions.py:256 msgid "Execute a command with arguments." msgstr "运行一条带有参数的命令。" #: lib/solaar/ui/rule_actions.py:259 msgid "Add argument" msgstr "添加参数" #: lib/solaar/ui/rule_conditions.py:43 msgid "X11 active process. For use in X11 only." msgstr "X11 活动进程。仅在 X11 中可用。" #: lib/solaar/ui/rule_conditions.py:73 msgid "X11 mouse process. For use in X11 only." msgstr "X11 光标所在进程。仅在 X11 中可用。" #: lib/solaar/ui/rule_conditions.py:90 msgid "MouseProcess" msgstr "光标所在进程" #: lib/solaar/ui/rule_conditions.py:114 msgid "Feature name of notification triggering rule processing." msgstr "处理报告触发规则时的特性名称。" #: lib/solaar/ui/rule_conditions.py:161 msgid "Report number of notification triggering rule processing." msgstr "处理报告触发规则时的回报数字。" #: lib/solaar/ui/rule_conditions.py:194 msgid "Active keyboard modifiers. Not always available in Wayland." msgstr "激活的键盘修饰键。在 Wayland 中不总是可用。" #: lib/solaar/ui/rule_conditions.py:234 msgid "" "Diverted key or button depressed or released.\n" "Use the Key/Button Diversion and Divert G Keys settings to divert keys and " "buttons." msgstr "" "转换的键未按下或已松开。\n" "请通过“转换按键”或“转换G键”设置来配置按键转换。" #: lib/solaar/ui/rule_conditions.py:243 msgid "Key down" msgstr "按键按下" #: lib/solaar/ui/rule_conditions.py:246 msgid "Key up" msgstr "按键弹起" #: lib/solaar/ui/rule_conditions.py:286 msgid "" "Diverted key or button is currently down.\n" "Use the Key/Button Diversion and Divert G Keys settings to divert keys and " "buttons." msgstr "" "转换的键已按下。\n" "请通过“转换按键”或“转换G键”设置来配置按键转换。" #: lib/solaar/ui/rule_conditions.py:324 msgid "Test condition on notification triggering rule processing." msgstr "在规则触发时测试条件。" #: lib/solaar/ui/rule_conditions.py:328 msgid "Parameter" msgstr "参数" #: lib/solaar/ui/rule_conditions.py:401 msgid "begin (inclusive)" msgstr "起始 (含)" #: lib/solaar/ui/rule_conditions.py:402 msgid "end (exclusive)" msgstr "结束 (不含)" #: lib/solaar/ui/rule_conditions.py:410 msgid "range" msgstr "范围" #: lib/solaar/ui/rule_conditions.py:413 msgid "minimum" msgstr "最小值" #: lib/solaar/ui/rule_conditions.py:414 msgid "maximum" msgstr "最大值" #: lib/solaar/ui/rule_conditions.py:416 #, python-format msgid "bytes %(0)d to %(1)d, ranging from %(2)d to %(3)d" msgstr "从%(0)d 到 %(1)d 字节,范围从 %(2)d 到 %(3)d" #: lib/solaar/ui/rule_conditions.py:419 lib/solaar/ui/rule_conditions.py:420 msgid "mask" msgstr "掩码" #: lib/solaar/ui/rule_conditions.py:421 #, python-format msgid "bytes %(0)d to %(1)d, mask %(2)d" msgstr "从%(0)d 到 %(1)d 字节,掩码为 %(2)d" #: lib/solaar/ui/rule_conditions.py:430 msgid "" "Bit or range test on bytes in notification message triggering rule " "processing." msgstr "规则触发时对指定的比特位进行测试或值域检测。" #: lib/solaar/ui/rule_conditions.py:440 msgid "type" msgstr "类型" #: lib/solaar/ui/rule_conditions.py:528 msgid "" "Mouse gesture with optional initiating button followed by zero or more mouse " "movements." msgstr "带有可选触发键并跟随有零个或多个鼠标移动轨迹的鼠标手势。" #: lib/solaar/ui/rule_conditions.py:533 msgid "Add movement" msgstr "增加移动轨迹" #: lib/solaar/ui/tray.py:50 msgid "No supported device found" msgstr "未发现支持的设备" #: lib/solaar/ui/tray.py:55 lib/solaar/ui/window.py:310 #, python-format msgid "About %s" msgstr "关于 %s" #: lib/solaar/ui/tray.py:56 lib/solaar/ui/window.py:308 #, python-format msgid "Quit %s" msgstr "退出 %s" #: lib/solaar/ui/tray.py:267 lib/solaar/ui/tray.py:275 msgid "no receiver" msgstr "无接收器" #: lib/solaar/ui/tray.py:288 lib/solaar/ui/tray.py:293 msgid "offline" msgstr "离线" #: lib/solaar/ui/tray.py:291 msgid "no status" msgstr "无状态" #: lib/solaar/ui/window.py:91 msgid "Scanning" msgstr "扫描中" #: lib/solaar/ui/window.py:122 msgid "Battery" msgstr "电池" #: lib/solaar/ui/window.py:125 msgid "Wireless Link" msgstr "无线连接" #: lib/solaar/ui/window.py:129 msgid "Lighting" msgstr "照明" #: lib/solaar/ui/window.py:163 msgid "Show Technical Details" msgstr "显示技术细节" #: lib/solaar/ui/window.py:179 msgid "Pair new device" msgstr "配对新设备" #: lib/solaar/ui/window.py:197 msgid "Select a device" msgstr "选择 1 个设备" #: lib/solaar/ui/window.py:313 msgid "Rule Editor" msgstr "规则编辑器" #: lib/solaar/ui/window.py:517 msgid "Path" msgstr "路径" #: lib/solaar/ui/window.py:520 msgid "USB ID" msgstr "" #: lib/solaar/ui/window.py:523 lib/solaar/ui/window.py:525 #: lib/solaar/ui/window.py:540 lib/solaar/ui/window.py:542 msgid "Serial" msgstr "序列号" #: lib/solaar/ui/window.py:529 msgid "Index" msgstr "索引号" #: lib/solaar/ui/window.py:531 msgid "Wireless PID" msgstr "无线标示符" #: lib/solaar/ui/window.py:533 msgid "Product ID" msgstr "产品 ID" #: lib/solaar/ui/window.py:535 msgid "Protocol" msgstr "协议" #: lib/solaar/ui/window.py:535 msgid "Unknown" msgstr "未知" #: lib/solaar/ui/window.py:537 msgid "Polling rate" msgstr "轮询速率" #: lib/solaar/ui/window.py:544 msgid "Unit ID" msgstr "单元 ID" #: lib/solaar/ui/window.py:558 msgid "Notifications" msgstr "提示" #: lib/solaar/ui/window.py:602 msgid "No device paired." msgstr "没有已配对设备。" #: lib/solaar/ui/window.py:611 #, python-format msgid "Up to %(max_count)s device can be paired to this receiver." msgid_plural "Up to %(max_count)s devices can be paired to this receiver." msgstr[0] "此接收器最多可与 %(max_count)s 个设备配对。" #: lib/solaar/ui/window.py:622 #, python-format msgid "This receiver has %d pairing remaining." msgid_plural "This receiver has %d pairings remaining." msgstr[0] "此接收器还可再与 %d 个设备配对。" #: lib/solaar/ui/window.py:679 msgid "Battery Voltage" msgstr "电池电压" #: lib/solaar/ui/window.py:681 msgid "Voltage reported by battery" msgstr "电池报告的电压" #: lib/solaar/ui/window.py:683 msgid "Battery Level" msgstr "电量" #: lib/solaar/ui/window.py:685 msgid "Approximate level reported by battery" msgstr "电池报告的大致电量" #: lib/solaar/ui/window.py:692 lib/solaar/ui/window.py:694 msgid "next reported " msgstr "下次报告于 " #: lib/solaar/ui/window.py:695 msgid " and next level to be reported." msgstr " 以及下一个报告电量。" #: lib/solaar/ui/window.py:711 msgid "encrypted" msgstr "已加密" #: lib/solaar/ui/window.py:713 msgid "The wireless link between this device and its receiver is encrypted." msgstr "设备与接收器间的无线连接已加密。" #: lib/solaar/ui/window.py:715 msgid "not encrypted" msgstr "未加密" #: lib/solaar/ui/window.py:719 msgid "" "The wireless link between this device and its receiver is not encrypted.\n" "This is a security issue for pointing devices, and a major security issue " "for text-input devices." msgstr "" "设备和接收器间的无线连接未加密。\n" "对于指针设备,这是一个安全问题;对于文本输入设备,这是一个严重的安全问题。" #: lib/solaar/ui/window.py:735 #, python-format msgid "%(light_level)d lux" msgstr "" #~ msgid " paired devices." #~ msgstr " 个已配对设备。" #~ msgid "1 paired device." #~ msgstr "1 个已配对设备。" #~ msgid "About" #~ msgstr "关于" #~ msgid "Add action" #~ msgstr "添加动作" #~ msgid "" #~ "Adjust the DPI by sliding the mouse horizontally while holding the button " #~ "down." #~ msgstr "通过在按住按钮的同时滑动鼠标来调节 DPI。" #~ msgid "" #~ "Automatically switch the mouse wheel between ratchet and freespin mode.\n" #~ "The mouse wheel is always free at 0, and always ratcheted at 50" #~ msgstr "" #~ "自动切换滚轮的段落和自由滚动模式。\n" #~ "在 0 时,滚轮永远为自由滚动模式;在 50 时,滚轮永远为段落模式" #~ msgid "Battery information unknown." #~ msgstr "电池信息未知。" #~ msgid "Battery: %(level)s" #~ msgstr "电池: %(level)s" #~ msgid "Battery: %(percent)d%%" #~ msgstr "电池: %(percent)d%%" #~ msgid "Count" #~ msgstr "数量" #~ msgid "DPI Sliding Adjustment" #~ msgstr "DPI 滑动调节" #~ msgid "Divert G Keys" #~ msgstr "G 键重定义" #~ msgid "" #~ "Diverted key or button depressed or released.\n" #~ "Use the Key/Button Diversion setting to divert keys and buttons." #~ msgstr "" #~ "重定义的按键或按钮未按下或已释放。\n" #~ "要重定义按键/按钮,请使用按键/按钮重定义设置。" #~ msgid "Effectively turns off thumb scrolling in Linux." #~ msgstr "在 Linux 中有效关闭拇指滚轮滚动。" #~ msgid "Effectively turns off wheel scrolling in Linux." #~ msgstr "在 Linux 中有效关闭滚轮滚动。" #~ msgid "" #~ "Enable onboard profiles, which often control report rate and keyboard " #~ "lighting" #~ msgstr "启用板载预设,通常会接管回报率和键盘灯光" #~ msgid "" #~ "Found a Logitech Receiver (%s), but did not have permission to open it." #~ msgstr "已发现 1 个罗技接收器 (%s),但您没有访问权限。" #~ msgid "Found a new device" #~ msgstr "发现 1 个新设备" #~ msgid "Frequency of device polling, in milliseconds" #~ msgstr "设备轮询频率,单位为毫秒" #~ msgid "HID++ mode for horizontal scroll with the thumb wheel." #~ msgstr "为滚轮的水平滚动启用 HID++ 模式。" #~ msgid "HID++ mode for vertical scroll with the wheel." #~ msgstr "为滚轮的垂直滚动启用 HID++ 模式。" #~ msgid "" #~ "If the device is already turned on,\n" #~ "turn if off and on again." #~ msgstr "" #~ "若设备的电源已经打开,\n" #~ "请先关闭再开启。" #~ msgid "" #~ "If you've just installed Solaar, try removing the receiver and plugging " #~ "it back in." #~ msgstr "若您刚刚安装 Solaar,请尝试将接收器拔下再重新插上。" #~ msgid "Lighting: %(level)s lux" #~ msgstr "照明: %(level)s lux" #~ msgid "" #~ "Make G keys send GKEY HID++ notifications (which trigger Solaar rules but " #~ "are otherwise ignored)." #~ msgstr "" #~ "使 G 键发送 GKEY (G 键) HID++ 通告 (这会触发 Solaar 规则,但如果没有,就会" #~ "被忽略)" #~ msgid "" #~ "Make the key or button send HID++ notifications (which trigger Solaar " #~ "rules but are otherwise ignored)." #~ msgstr "" #~ "使按键或按钮发送 HID++ 通告 (这会触发 Solaar 规则,但如果没有,就会被忽略)" #~ msgid "May also make M keys and MR key send HID++ notifications" #~ msgstr "同样也使 M 键和 MR 键发送 HID++ 通告" #~ msgid "No Logitech device found" #~ msgstr "未发现罗技设备" #~ msgid "No Logitech receiver found" #~ msgstr "未发现罗技接收器" #~ msgid "No device paired" #~ msgstr "无已配对设备" #~ msgid "Only one device can be paired to this receiver" #~ msgstr "此接收器仅支持配对 1 个设备" #~ msgid "Only one device can be paired to this receiver." #~ msgstr "此接收器只能与一个设备配对。" #~ msgid "Polling Rate (ms)" #~ msgstr "轮询速率 (ms)" #~ msgid "Quit" #~ msgstr "退出" #~ msgid "Scroll Wheel Rachet" #~ msgstr "滚轮段落模式" #~ msgid "Send a gesture by sliding the mouse while holding the button down." #~ msgstr "通过在按住按钮的同时滑动鼠标来发送手势。" #~ msgid "" #~ "Shows status of devices connected\n" #~ "through wireless Logitech receivers." #~ msgstr "" #~ "显示罗技接收器\n" #~ "已连接的设备状态。" #~ msgid "Smooth Scrolling" #~ msgstr "平滑滚动" #~ msgid "The receiver only supports %d paired device(s)." #~ msgstr "此接收器仅支持 %d 个配对设备。" #~ msgid "The receiver was unplugged." #~ msgstr "接收器被移除。" #~ msgid "" #~ "The wireless link between this device and its receiver is not encrypted.\n" #~ "\n" #~ "For pointing devices (mice, trackballs, trackpads), this is a minor " #~ "security issue.\n" #~ "\n" #~ "It is, however, a major security issue for text-input devices (keyboards, " #~ "numpads),\n" #~ "because typed text can be sniffed inconspicuously by 3rd parties within " #~ "range." #~ msgstr "" #~ "设备与接收器间的无线连接未加密。\n" #~ "\n" #~ "对定位设备(如鼠标、轨迹球、触摸板)来说这不是什么问题。\n" #~ "\n" #~ "然而这对于内容输入设备(如键盘、数字键盘)却是一个重大的安全问题,\n" #~ "您输入的内容有可能被第三方偷偷监听到。" #~ msgid "" #~ "Try removing the device and plugging it back in or turning it off and " #~ "then on." #~ msgstr "请尝试将设备拔下再重新插上,或关闭电源后重新打开。" #~ msgid "Turn illumination on or off on keyboard." #~ msgstr "打开或关闭键盘背光。" #~ msgid "USB id" #~ msgstr "USB 序号" #~ msgid "Up to %d devices can be paired to this receiver" #~ msgstr "此接收器可配对至多 %d 个设备" #~ msgid "You may have to first turn the device off and on again." #~ msgstr "您可能需要先关闭设备的电源再重新打开。" #~ msgid "closed" #~ msgstr "关闭" #~ msgid "last known" #~ msgstr "最后已知" #~ msgid "lux" #~ msgstr "lux" #~ msgid "none" #~ msgstr "无" #~ msgid "open" #~ msgstr "开启" #~ msgid "pair new device" #~ msgstr "配对新设备" #~ msgid "paired devices" #~ msgstr "个已配对设备" #~ msgid "pairing lock is " #~ msgstr "配对锁已" #~ msgid "unknown" #~ msgstr "未知的" Solaar-1.1.14/po/zh_TW.po000066400000000000000000001627621473525213700150570ustar00rootroot00000000000000# Chinese translations for solaar package # Copyright (C) 2014 THE solaar'S COPYRIGHT HOLDER # This file is distributed under the same license as the solaar package. # Automatically generated, 2014. # msgid "" msgstr "" "Project-Id-Version: solaar 0.9.2\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2024-03-14 13:23+0700\n" "PO-Revision-Date: 2024-04-23 18:20+0800\n" "Last-Translator: Peter Dave Hello \n" "Language-Team: Peter Dave Hello \n" "Language: zh_TW\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Language: zh_TW\n" "X-Source-Language: C\n" "X-Generator: Poedit 3.0\n" #: lib/logitech_receiver/base_usb.py:45 msgid "Bolt Receiver" msgstr "Bolt 接收器" #: lib/logitech_receiver/base_usb.py:58 msgid "Unifying Receiver" msgstr "Unifying 接收器" #: lib/logitech_receiver/base_usb.py:70 lib/logitech_receiver/base_usb.py:83 #: lib/logitech_receiver/base_usb.py:97 lib/logitech_receiver/base_usb.py:111 #: lib/logitech_receiver/base_usb.py:125 msgid "Nano Receiver" msgstr "Nano 接收器" #: lib/logitech_receiver/base_usb.py:138 msgid "Lightspeed Receiver" msgstr "Lightspeed 接收器" #: lib/logitech_receiver/base_usb.py:149 msgid "EX100 Receiver 27 Mhz" msgstr "EX100 27 Mhz 接收器" #: lib/logitech_receiver/common.py:604 #, python-format msgid "Battery: %(level)s (%(status)s)" msgstr "電量:%(level)s (%(status)s)" #: lib/logitech_receiver/common.py:606 #, python-format msgid "Battery: %(percent)d%% (%(status)s)" msgstr "電量:%(percent)d%% (%(status)s)" #: lib/logitech_receiver/hidpp20.py:926 #: lib/logitech_receiver/settings_templates.py:291 #, fuzzy msgid "Disabled" msgstr "已停用" #: lib/logitech_receiver/hidpp20.py:927 msgid "Static" msgstr "靜態" #: lib/logitech_receiver/hidpp20.py:928 msgid "Pulse" msgstr "脈衝" #: lib/logitech_receiver/hidpp20.py:929 msgid "Cycle" msgstr "循環" #: lib/logitech_receiver/hidpp20.py:930 msgid "Boot" msgstr "開機" #: lib/logitech_receiver/hidpp20.py:931 msgid "Demo" msgstr "示範" #: lib/logitech_receiver/hidpp20.py:932 msgid "Breathe" msgstr "呼吸" #: lib/logitech_receiver/hidpp20.py:933 msgid "Ripple" msgstr "漣漪" #: lib/logitech_receiver/hidpp20.py:994 #, fuzzy msgid "Unknown Location" msgstr "未知位置" #: lib/logitech_receiver/hidpp20.py:995 msgid "Primary" msgstr "主要" #: lib/logitech_receiver/hidpp20.py:996 msgid "Logo" msgstr "標誌" #: lib/logitech_receiver/hidpp20.py:997 #, fuzzy msgid "Left Side" msgstr "左側" #: lib/logitech_receiver/hidpp20.py:998 #, fuzzy msgid "Right Side" msgstr "右側" #: lib/logitech_receiver/hidpp20.py:999 msgid "Combined" msgstr "結合" #: lib/logitech_receiver/hidpp20.py:1000 msgid "Primary 1" msgstr "主要 1" #: lib/logitech_receiver/hidpp20.py:1001 msgid "Primary 2" msgstr "主要 2" #: lib/logitech_receiver/hidpp20.py:1002 msgid "Primary 3" msgstr "主要 3" #: lib/logitech_receiver/hidpp20.py:1003 msgid "Primary 4" msgstr "主要 4" #: lib/logitech_receiver/hidpp20.py:1004 msgid "Primary 5" msgstr "主要 5" #: lib/logitech_receiver/hidpp20.py:1005 msgid "Primary 6" msgstr "主要 6" #: lib/logitech_receiver/i18n.py:28 msgid "empty" msgstr "電池已耗盡" #: lib/logitech_receiver/i18n.py:29 msgid "critical" msgstr "極低" #: lib/logitech_receiver/i18n.py:30 msgid "low" msgstr "低" #: lib/logitech_receiver/i18n.py:31 msgid "average" msgstr "平均" #: lib/logitech_receiver/i18n.py:32 msgid "good" msgstr "良好" #: lib/logitech_receiver/i18n.py:33 msgid "full" msgstr "滿格" #: lib/logitech_receiver/i18n.py:35 msgid "discharging" msgstr "放電中" #: lib/logitech_receiver/i18n.py:36 msgid "recharging" msgstr "充電中" #: lib/logitech_receiver/i18n.py:37 msgid "charging" msgstr "充電中" #: lib/logitech_receiver/i18n.py:38 msgid "not charging" msgstr "未充電" #: lib/logitech_receiver/i18n.py:39 msgid "almost full" msgstr "即將充滿" #: lib/logitech_receiver/i18n.py:40 msgid "charged" msgstr "已充滿" #: lib/logitech_receiver/i18n.py:41 msgid "slow recharge" msgstr "慢速充電" #: lib/logitech_receiver/i18n.py:42 msgid "invalid battery" msgstr "電池無效" #: lib/logitech_receiver/i18n.py:43 msgid "thermal error" msgstr "溫度異常" #: lib/logitech_receiver/i18n.py:44 msgid "error" msgstr "錯誤" #: lib/logitech_receiver/i18n.py:45 msgid "standard" msgstr "標準" #: lib/logitech_receiver/i18n.py:46 msgid "fast" msgstr "快速" #: lib/logitech_receiver/i18n.py:47 msgid "slow" msgstr "慢" #: lib/logitech_receiver/i18n.py:49 msgid "device timeout" msgstr "裝置逾時" #: lib/logitech_receiver/i18n.py:50 msgid "device not supported" msgstr "不支援的裝置" #: lib/logitech_receiver/i18n.py:51 msgid "too many devices" msgstr "裝置數量太多" #: lib/logitech_receiver/i18n.py:52 msgid "sequence timeout" msgstr "序列超時" #: lib/logitech_receiver/i18n.py:54 msgid "Firmware" msgstr "韌體" #: lib/logitech_receiver/i18n.py:55 msgid "Bootloader" msgstr "啟動器" #: lib/logitech_receiver/i18n.py:56 msgid "Hardware" msgstr "硬體" #: lib/logitech_receiver/i18n.py:57 msgid "Other" msgstr "其他" #: lib/logitech_receiver/i18n.py:59 msgid "Left Button" msgstr "左鍵" #: lib/logitech_receiver/i18n.py:60 msgid "Right Button" msgstr "右鍵" #: lib/logitech_receiver/i18n.py:61 msgid "Middle Button" msgstr "中鍵" #: lib/logitech_receiver/i18n.py:62 msgid "Back Button" msgstr "返回鍵" #: lib/logitech_receiver/i18n.py:63 msgid "Forward Button" msgstr "前進鍵" #: lib/logitech_receiver/i18n.py:64 msgid "Mouse Gesture Button" msgstr "滑鼠手勢鍵" #: lib/logitech_receiver/i18n.py:65 msgid "Smart Shift" msgstr "智慧切換" #: lib/logitech_receiver/i18n.py:66 msgid "DPI Switch" msgstr "DPI 切換" #: lib/logitech_receiver/i18n.py:67 msgid "Left Tilt" msgstr "向左傾斜" #: lib/logitech_receiver/i18n.py:68 msgid "Right Tilt" msgstr "向右傾斜" #: lib/logitech_receiver/i18n.py:69 msgid "Left Click" msgstr "左鍵" #: lib/logitech_receiver/i18n.py:70 msgid "Right Click" msgstr "右鍵" #: lib/logitech_receiver/i18n.py:71 msgid "Mouse Middle Button" msgstr "滑鼠中鍵" #: lib/logitech_receiver/i18n.py:72 msgid "Mouse Back Button" msgstr "滑鼠返回鍵" #: lib/logitech_receiver/i18n.py:73 msgid "Mouse Forward Button" msgstr "滑鼠前進鍵" #: lib/logitech_receiver/i18n.py:74 msgid "Gesture Button Navigation" msgstr "手勢按鈕導航" #: lib/logitech_receiver/i18n.py:75 msgid "Mouse Scroll Left Button" msgstr "滑鼠滾輪向左按鈕" #: lib/logitech_receiver/i18n.py:76 msgid "Mouse Scroll Right Button" msgstr "滑鼠滾輪向右按鈕" #: lib/logitech_receiver/i18n.py:78 msgid "pressed" msgstr "按下" #: lib/logitech_receiver/i18n.py:79 msgid "released" msgstr "放開" #: lib/logitech_receiver/notifications.py:63 #: lib/logitech_receiver/notifications.py:115 msgid "pairing lock is closed" msgstr "配對鎖已關閉" #: lib/logitech_receiver/notifications.py:63 #: lib/logitech_receiver/notifications.py:115 msgid "pairing lock is open" msgstr "配對鎖已開啟" #: lib/logitech_receiver/notifications.py:80 msgid "discovery lock is closed" msgstr "發現鎖已關閉" #: lib/logitech_receiver/notifications.py:80 msgid "discovery lock is open" msgstr "發現鎖已開啟" #: lib/logitech_receiver/notifications.py:207 msgid "connected" msgstr "已連線" #: lib/logitech_receiver/notifications.py:207 msgid "disconnected" msgstr "已斷線" #: lib/logitech_receiver/notifications.py:233 msgid "unpaired" msgstr "未配對" #: lib/logitech_receiver/notifications.py:280 msgid "powered on" msgstr "已啟動" #: lib/logitech_receiver/receiver.py:345 msgid "No paired devices." msgstr "沒有已配對的裝置。" #: lib/logitech_receiver/receiver.py:347 lib/solaar/ui/window.py:630 #, fuzzy, python-format msgid "%(count)s paired device." msgid_plural "%(count)s paired devices." msgstr[0] "%(count)s 個已配對裝置" #: lib/logitech_receiver/settings.py:617 msgid "register" msgstr "註冊" #: lib/logitech_receiver/settings.py:631 lib/logitech_receiver/settings.py:658 msgid "feature" msgstr "功能" #: lib/logitech_receiver/settings_templates.py:144 msgid "Swap Fx function" msgstr "互換 Fx 鍵功能" #: lib/logitech_receiver/settings_templates.py:147 msgid "" "When set, the F1..F12 keys will activate their special function,\n" "and you must hold the FN key to activate their standard function." msgstr "" "若開啟,F1 到 F12 鍵將啟用特殊功能,\n" "使用標準功能請同時按住 FN 鍵。" #: lib/logitech_receiver/settings_templates.py:152 msgid "" "When unset, the F1..F12 keys will activate their standard function,\n" "and you must hold the FN key to activate their special function." msgstr "" "若關閉,F1 到 F12 鍵將啟用標準功能,\n" "使用特殊功能請同時按住 FN 鍵。" #: lib/logitech_receiver/settings_templates.py:160 msgid "Hand Detection" msgstr "手掌識別" #: lib/logitech_receiver/settings_templates.py:161 msgid "Turn on illumination when the hands hover over the keyboard." msgstr "手掌置於鍵盤上方時開啟亮度。" #: lib/logitech_receiver/settings_templates.py:168 msgid "Scroll Wheel Smooth Scrolling" msgstr "滾輪平滑捲動" #: lib/logitech_receiver/settings_templates.py:169 #: lib/logitech_receiver/settings_templates.py:402 #: lib/logitech_receiver/settings_templates.py:431 msgid "High-sensitivity mode for vertical scroll with the wheel." msgstr "滾輪高解析度滾動模式。" #: lib/logitech_receiver/settings_templates.py:176 msgid "Side Scrolling" msgstr "側邊滾動" #: lib/logitech_receiver/settings_templates.py:178 msgid "" "When disabled, pushing the wheel sideways sends custom button events\n" "instead of the standard side-scrolling events." msgstr "" "若停用,按下滾輪側邊將傳送自定義按鈕事件\n" "而不是標準側邊滾動事件。" #: lib/logitech_receiver/settings_templates.py:188 msgid "Sensitivity (DPI - older mice)" msgstr "靈敏度(DPI - 較舊的滑鼠)" #: lib/logitech_receiver/settings_templates.py:189 #: lib/logitech_receiver/settings_templates.py:958 msgid "Mouse movement sensitivity" msgstr "滑鼠移動靈敏度" #: lib/logitech_receiver/settings_templates.py:249 #: lib/logitech_receiver/settings_templates.py:261 #: lib/logitech_receiver/settings_templates.py:388 msgid "Backlight" msgstr "背光" #: lib/logitech_receiver/settings_templates.py:250 #: lib/logitech_receiver/settings_templates.py:389 msgid "Set illumination time for keyboard." msgstr "設定鍵盤的照明時間。" #: lib/logitech_receiver/settings_templates.py:262 msgid "" "Illumination level on keyboard. Changes made are only applied in Manual " "mode." msgstr "鍵盤上的亮度等級。僅在手動模式下適用。" #: lib/logitech_receiver/settings_templates.py:293 msgid "Automatic" msgstr "自動" #: lib/logitech_receiver/settings_templates.py:295 msgid "Manual" msgstr "手動" #: lib/logitech_receiver/settings_templates.py:297 msgid "Enabled" msgstr "已啟用" #: lib/logitech_receiver/settings_templates.py:303 #, fuzzy msgid "Backlight Level" msgstr "背光" #: lib/logitech_receiver/settings_templates.py:304 msgid "Illumination level on keyboard when in Manual mode." msgstr "在手動模式下鍵盤的亮度等級。" #: lib/logitech_receiver/settings_templates.py:361 msgid "Backlight Delay Hands Out" msgstr "手離開後背光延遲" #: lib/logitech_receiver/settings_templates.py:362 msgid "" "Delay in seconds until backlight fades out with hands away from keyboard." msgstr "手離開鍵盤後,背光消失的延遲時間(以秒為單位)。" #: lib/logitech_receiver/settings_templates.py:370 msgid "Backlight Delay Hands In" msgstr "手靠近後背光延遲" #: lib/logitech_receiver/settings_templates.py:371 msgid "Delay in seconds until backlight fades out with hands near keyboard." msgstr "手靠近鍵盤後,背光消失的延遲時間(以秒為單位)。" #: lib/logitech_receiver/settings_templates.py:379 msgid "Backlight Delay Powered" msgstr "供電後背光延遲" #: lib/logitech_receiver/settings_templates.py:380 msgid "Delay in seconds until backlight fades out with external power." msgstr "外部供電後,背光消失的延遲時間(以秒為單位)。" #: lib/logitech_receiver/settings_templates.py:400 msgid "Scroll Wheel High Resolution" msgstr "滾輪高解析度" #: lib/logitech_receiver/settings_templates.py:404 #: lib/logitech_receiver/settings_templates.py:433 msgid "Set to ignore if scrolling is abnormally fast or slow" msgstr "如果滾動速度異常快或慢,設定為忽略" #: lib/logitech_receiver/settings_templates.py:411 #: lib/logitech_receiver/settings_templates.py:442 msgid "Scroll Wheel Diversion" msgstr "滾輪轉向" #: lib/logitech_receiver/settings_templates.py:413 msgid "" "Make scroll wheel send LOWRES_WHEEL HID++ notifications (which trigger " "Solaar rules but are otherwise ignored)." msgstr "" "使滾輪發送 LOWRES_WHEEL HID++ 通知(觸發 Solaar 規則,但其他情況下將被忽" "略)。" #: lib/logitech_receiver/settings_templates.py:420 msgid "Scroll Wheel Direction" msgstr "滾輪方向" #: lib/logitech_receiver/settings_templates.py:421 msgid "Invert direction for vertical scroll with wheel." msgstr "反轉滾輪的垂直滾動方向。" #: lib/logitech_receiver/settings_templates.py:429 msgid "Scroll Wheel Resolution" msgstr "滾輪解析度" #: lib/logitech_receiver/settings_templates.py:444 msgid "" "Make scroll wheel send HIRES_WHEEL HID++ notifications (which trigger Solaar " "rules but are otherwise ignored)." msgstr "" "使滾輪發送 HIRES_WHEEL HID++ 通知(觸發 Solaar 規則,但其他情況下將被忽略)。" #: lib/logitech_receiver/settings_templates.py:453 msgid "Sensitivity (Pointer Speed)" msgstr "靈敏度(指標速度)" #: lib/logitech_receiver/settings_templates.py:454 msgid "Speed multiplier for mouse (256 is normal multiplier)." msgstr "滑鼠速度乘數(256 為正常乘數)。" #: lib/logitech_receiver/settings_templates.py:464 msgid "Thumb Wheel Diversion" msgstr "拇指滾輪轉向" #: lib/logitech_receiver/settings_templates.py:466 msgid "" "Make thumb wheel send THUMB_WHEEL HID++ notifications (which trigger Solaar " "rules but are otherwise ignored)." msgstr "" "使拇指滾輪發送 THUMB_WHEEL HID++ 通知(觸發 Solaar 規則,但其他情況下將被忽" "略)。" #: lib/logitech_receiver/settings_templates.py:475 msgid "Thumb Wheel Direction" msgstr "拇指滾輪方向" #: lib/logitech_receiver/settings_templates.py:476 msgid "Invert thumb wheel scroll direction." msgstr "反轉拇指滾輪滾動方向。" #: lib/logitech_receiver/settings_templates.py:496 msgid "Onboard Profiles" msgstr "內建設定檔" #: lib/logitech_receiver/settings_templates.py:497 #, fuzzy msgid "" "Enable an onboard profile, which controls report rate, sensitivity, and " "button actions" msgstr "啟用內建設定檔,通常用於控制回報速率和鍵盤亮度" #: lib/logitech_receiver/settings_templates.py:541 #: lib/logitech_receiver/settings_templates.py:581 #, fuzzy msgid "Report Rate" msgstr "回報速率" #: lib/logitech_receiver/settings_templates.py:543 #: lib/logitech_receiver/settings_templates.py:583 #, fuzzy msgid "Frequency of device movement reports" msgstr "裝置輪詢的頻率,以毫秒為單位" #: lib/logitech_receiver/settings_templates.py:543 #: lib/logitech_receiver/settings_templates.py:583 #: lib/logitech_receiver/settings_templates.py:1295 #: lib/logitech_receiver/settings_templates.py:1326 msgid "May need Onboard Profiles set to Disable to be effective." msgstr "可能需要將內建設定檔設定為停用才能生效。" #: lib/logitech_receiver/settings_templates.py:623 msgid "Divert crown events" msgstr "轉移多功能轉鈕事件" #: lib/logitech_receiver/settings_templates.py:624 msgid "" "Make crown send CROWN HID++ notifications (which trigger Solaar rules but " "are otherwise ignored)." msgstr "" "使多功能轉鈕發送 CROWN HID++ 通知(觸發 Solaar 規則,但其他情況下將被忽略)。" #: lib/logitech_receiver/settings_templates.py:632 msgid "Crown smooth scroll" msgstr "Crown 平滑滾動" #: lib/logitech_receiver/settings_templates.py:633 msgid "Set crown smooth scroll" msgstr "設定 Crown 平滑滾動" #: lib/logitech_receiver/settings_templates.py:641 #, fuzzy msgid "Divert G and M Keys" msgstr "轉換 G 鍵" #: lib/logitech_receiver/settings_templates.py:642 #, fuzzy msgid "" "Make G and M keys send HID++ notifications (which trigger Solaar rules but " "are otherwise ignored)." msgstr "" "讓 G 鍵發送 GKEY HID++ 通知(將觸發 Solaar 規則,但其他情況下會被忽略)。" #: lib/logitech_receiver/settings_templates.py:656 msgid "Scroll Wheel Ratcheted" msgstr "滾輪棘輪" #: lib/logitech_receiver/settings_templates.py:657 msgid "" "Switch the mouse wheel between speed-controlled ratcheting and always " "freespin." msgstr "在速度控制的滾輪棘輪和始終自由滾動之間切換滑鼠滾輪。" #: lib/logitech_receiver/settings_templates.py:659 msgid "Freespinning" msgstr "自由滾動" #: lib/logitech_receiver/settings_templates.py:659 msgid "Ratcheted" msgstr "棘輪式" #: lib/logitech_receiver/settings_templates.py:666 msgid "Scroll Wheel Ratchet Speed" msgstr "滾輪棘輪速度" #: lib/logitech_receiver/settings_templates.py:668 msgid "" "Use the mouse wheel speed to switch between ratcheted and freespinning.\n" "The mouse wheel is always ratcheted at 50." msgstr "" "使用滑鼠滾輪速度在滾輪棘輪和自由滾動之間切換。\n" "滾輪在 50 時始終處於棘輪狀態。" #: lib/logitech_receiver/settings_templates.py:717 msgid "Key/Button Actions" msgstr "鍵/按鈕動作" #: lib/logitech_receiver/settings_templates.py:719 msgid "Change the action for the key or button." msgstr "更改鍵或按鈕的動作。" #: lib/logitech_receiver/settings_templates.py:721 msgid "Overridden by diversion." msgstr "由轉換覆蓋。" #: lib/logitech_receiver/settings_templates.py:723 msgid "" "Changing important actions (such as for the left mouse button) can result in " "an unusable system." msgstr "更改重要動作(例如左鍵)可能導致系統無法使用。" #: lib/logitech_receiver/settings_templates.py:886 msgid "Key/Button Diversion" msgstr "鍵/按鈕轉換" #: lib/logitech_receiver/settings_templates.py:887 msgid "" "Make the key or button send HID++ notifications (Diverted) or initiate Mouse " "Gestures or Sliding DPI" msgstr "讓鍵或按鈕發送 HID++ 通知(已轉換)或啟動滑鼠手勢或滑動 DPI" #: lib/logitech_receiver/settings_templates.py:890 #: lib/logitech_receiver/settings_templates.py:891 #: lib/logitech_receiver/settings_templates.py:892 msgid "Diverted" msgstr "已轉換" #: lib/logitech_receiver/settings_templates.py:890 #: lib/logitech_receiver/settings_templates.py:891 msgid "Mouse Gestures" msgstr "滑鼠手勢" #: lib/logitech_receiver/settings_templates.py:890 #: lib/logitech_receiver/settings_templates.py:891 #: lib/logitech_receiver/settings_templates.py:892 msgid "Regular" msgstr "常規" #: lib/logitech_receiver/settings_templates.py:890 msgid "Sliding DPI" msgstr "滑動 DPI" #: lib/logitech_receiver/settings_templates.py:957 msgid "Sensitivity (DPI)" msgstr "靈敏度 (DPI)" #: lib/logitech_receiver/settings_templates.py:997 msgid "Sensitivity Switching" msgstr "靈敏度切換" #: lib/logitech_receiver/settings_templates.py:999 msgid "" "Switch the current sensitivity and the remembered sensitivity when the key " "or button is pressed.\n" "If there is no remembered sensitivity, just remember the current sensitivity" msgstr "" "在按下鍵或按鈕時切換目前靈敏度和已記憶的靈敏度。\n" "如果沒有已記憶的靈敏度,只需記住目前的靈敏度" #: lib/logitech_receiver/settings_templates.py:1003 msgid "Off" msgstr "關" #: lib/logitech_receiver/settings_templates.py:1034 msgid "Disable keys" msgstr "停用鍵" #: lib/logitech_receiver/settings_templates.py:1035 msgid "Disable specific keyboard keys." msgstr "停用特定鍵盤按鍵。" #: lib/logitech_receiver/settings_templates.py:1038 #, python-format msgid "Disables the %s key." msgstr "停用 %s 鍵。" #: lib/logitech_receiver/settings_templates.py:1051 #: lib/logitech_receiver/settings_templates.py:1108 msgid "Set OS" msgstr "設定作業系統" #: lib/logitech_receiver/settings_templates.py:1052 #: lib/logitech_receiver/settings_templates.py:1109 msgid "Change keys to match OS." msgstr "更改按鍵以符合作業系統。" #: lib/logitech_receiver/settings_templates.py:1121 msgid "Change Host" msgstr "更改主機號碼" #: lib/logitech_receiver/settings_templates.py:1122 msgid "Switch connection to a different host" msgstr "切換到其他主機" #: lib/logitech_receiver/settings_templates.py:1146 msgid "Performs a left click." msgstr "執行左鍵點選。" #: lib/logitech_receiver/settings_templates.py:1146 msgid "Single tap" msgstr "點選" #: lib/logitech_receiver/settings_templates.py:1147 msgid "Performs a right click." msgstr "執行右鍵點選。" #: lib/logitech_receiver/settings_templates.py:1147 msgid "Single tap with two fingers" msgstr "用兩根手指點選" #: lib/logitech_receiver/settings_templates.py:1148 msgid "Single tap with three fingers" msgstr "用三根手指點選" #: lib/logitech_receiver/settings_templates.py:1152 msgid "Double tap" msgstr "點兩下" #: lib/logitech_receiver/settings_templates.py:1152 msgid "Performs a double click." msgstr "執行點兩下。" #: lib/logitech_receiver/settings_templates.py:1153 msgid "Double tap with two fingers" msgstr "用兩根手指點兩下" #: lib/logitech_receiver/settings_templates.py:1154 msgid "Double tap with three fingers" msgstr "用三根手指點兩下" #: lib/logitech_receiver/settings_templates.py:1157 msgid "Drags items by dragging the finger after double tapping." msgstr "點兩下後拖動手指來拖動物件。" #: lib/logitech_receiver/settings_templates.py:1157 msgid "Tap and drag" msgstr "點選並拖曳" #: lib/logitech_receiver/settings_templates.py:1159 msgid "Tap and drag with two fingers" msgstr "用兩根手指點兩下並拖曳" #: lib/logitech_receiver/settings_templates.py:1160 msgid "Drags items by dragging the fingers after double tapping." msgstr "點兩下後拖動手指來拖動物件。" #: lib/logitech_receiver/settings_templates.py:1162 msgid "Tap and drag with three fingers" msgstr "用三根手指點選並拖曳" #: lib/logitech_receiver/settings_templates.py:1165 msgid "Suppress tap and edge gestures" msgstr "停用輕觸和邊緣手勢" #: lib/logitech_receiver/settings_templates.py:1166 msgid "Disables tap and edge gestures (equivalent to pressing Fn+LeftClick)." msgstr "停用輕觸和邊緣手勢(相當於按 Fn+左鍵點選)。" #: lib/logitech_receiver/settings_templates.py:1168 msgid "Scroll with one finger" msgstr "用一根手指滾動" #: lib/logitech_receiver/settings_templates.py:1168 #: lib/logitech_receiver/settings_templates.py:1169 #: lib/logitech_receiver/settings_templates.py:1172 msgid "Scrolls." msgstr "滾動。" #: lib/logitech_receiver/settings_templates.py:1169 #: lib/logitech_receiver/settings_templates.py:1172 msgid "Scroll with two fingers" msgstr "用兩根手指滾動" #: lib/logitech_receiver/settings_templates.py:1170 msgid "Scroll horizontally with two fingers" msgstr "用兩根手指水平滾動" #: lib/logitech_receiver/settings_templates.py:1170 msgid "Scrolls horizontally." msgstr "水平滾動。" #: lib/logitech_receiver/settings_templates.py:1171 msgid "Scroll vertically with two fingers" msgstr "用兩根手指垂直滾動" #: lib/logitech_receiver/settings_templates.py:1171 msgid "Scrolls vertically." msgstr "垂直滾動。" #: lib/logitech_receiver/settings_templates.py:1173 msgid "Inverts the scrolling direction." msgstr "反轉滾動方向。" #: lib/logitech_receiver/settings_templates.py:1173 msgid "Natural scrolling" msgstr "自然滾動" #: lib/logitech_receiver/settings_templates.py:1174 msgid "Enables the thumbwheel." msgstr "啟用拇指滾輪。" #: lib/logitech_receiver/settings_templates.py:1174 msgid "Thumbwheel" msgstr "拇指滾輪" #: lib/logitech_receiver/settings_templates.py:1185 #: lib/logitech_receiver/settings_templates.py:1189 msgid "Swipe from the top edge" msgstr "從上緣滑動" #: lib/logitech_receiver/settings_templates.py:1186 msgid "Swipe from the left edge" msgstr "從左緣滑動" #: lib/logitech_receiver/settings_templates.py:1187 msgid "Swipe from the right edge" msgstr "從右緣滑動" #: lib/logitech_receiver/settings_templates.py:1188 msgid "Swipe from the bottom edge" msgstr "從下緣滑動" #: lib/logitech_receiver/settings_templates.py:1190 msgid "Swipe two fingers from the left edge" msgstr "從左緣滑動兩根手指" #: lib/logitech_receiver/settings_templates.py:1191 msgid "Swipe two fingers from the right edge" msgstr "從右緣滑動兩根手指" #: lib/logitech_receiver/settings_templates.py:1192 msgid "Swipe two fingers from the bottom edge" msgstr "從下緣滑動兩根手指" #: lib/logitech_receiver/settings_templates.py:1193 msgid "Swipe two fingers from the top edge" msgstr "從上緣滑動兩根手指" #: lib/logitech_receiver/settings_templates.py:1194 #: lib/logitech_receiver/settings_templates.py:1198 msgid "Pinch to zoom out; spread to zoom in." msgstr "捏合以縮小;張開以放大。" #: lib/logitech_receiver/settings_templates.py:1194 msgid "Zoom with two fingers." msgstr "用兩根手指縮放。" #: lib/logitech_receiver/settings_templates.py:1195 msgid "Pinch to zoom out." msgstr "捏合以縮小。" #: lib/logitech_receiver/settings_templates.py:1196 msgid "Spread to zoom in." msgstr "張開以放大。" #: lib/logitech_receiver/settings_templates.py:1197 msgid "Zoom with three fingers." msgstr "用三根手指縮放。" #: lib/logitech_receiver/settings_templates.py:1198 msgid "Zoom with two fingers" msgstr "用兩根手指縮放" #: lib/logitech_receiver/settings_templates.py:1216 msgid "Pixel zone" msgstr "像素區域" #: lib/logitech_receiver/settings_templates.py:1217 msgid "Ratio zone" msgstr "比例區域" #: lib/logitech_receiver/settings_templates.py:1218 msgid "Scale factor" msgstr "縮放係數" #: lib/logitech_receiver/settings_templates.py:1218 msgid "Sets the cursor speed." msgstr "設定游標速度。" #: lib/logitech_receiver/settings_templates.py:1222 msgid "Left" msgstr "左" #: lib/logitech_receiver/settings_templates.py:1222 msgid "Left-most coordinate." msgstr "最左邊的座標。" #: lib/logitech_receiver/settings_templates.py:1223 msgid "Bottom" msgstr "底部" #: lib/logitech_receiver/settings_templates.py:1223 msgid "Bottom coordinate." msgstr "底部座標。" #: lib/logitech_receiver/settings_templates.py:1224 msgid "Width" msgstr "寬度" #: lib/logitech_receiver/settings_templates.py:1224 msgid "Width." msgstr "寬度。" #: lib/logitech_receiver/settings_templates.py:1225 msgid "Height" msgstr "高度" #: lib/logitech_receiver/settings_templates.py:1225 msgid "Height." msgstr "高度。" #: lib/logitech_receiver/settings_templates.py:1226 msgid "Cursor speed." msgstr "游標速度。" #: lib/logitech_receiver/settings_templates.py:1226 msgid "Scale" msgstr "縮放" #: lib/logitech_receiver/settings_templates.py:1232 msgid "Gestures" msgstr "手勢" #: lib/logitech_receiver/settings_templates.py:1233 msgid "Tweak the mouse/touchpad behaviour." msgstr "調整滑鼠/觸控板行為。" #: lib/logitech_receiver/settings_templates.py:1249 msgid "Gestures Diversion" msgstr "手勢轉換" #: lib/logitech_receiver/settings_templates.py:1250 msgid "Divert mouse/touchpad gestures." msgstr "轉換滑鼠/觸控板手勢。" #: lib/logitech_receiver/settings_templates.py:1266 msgid "Gesture params" msgstr "手勢參數" #: lib/logitech_receiver/settings_templates.py:1267 msgid "Change numerical parameters of a mouse/touchpad." msgstr "更改滑鼠/觸控板的數值參數。" #: lib/logitech_receiver/settings_templates.py:1291 msgid "M-Key LEDs" msgstr "M 鍵 LED" #: lib/logitech_receiver/settings_templates.py:1293 msgid "Control the M-Key LEDs." msgstr "控制 M 鍵 LED。" #: lib/logitech_receiver/settings_templates.py:1297 #: lib/logitech_receiver/settings_templates.py:1328 msgid "May need G Keys diverted to be effective." msgstr "可能需要轉換 G 鍵才能生效。" #: lib/logitech_receiver/settings_templates.py:1303 #, python-format msgid "Lights up the %s key." msgstr "點亮 %s 鍵。" #: lib/logitech_receiver/settings_templates.py:1322 msgid "MR-Key LED" msgstr "MR 鍵 LED" #: lib/logitech_receiver/settings_templates.py:1324 msgid "Control the MR-Key LED." msgstr "控制 MR 鍵 LED。" #: lib/logitech_receiver/settings_templates.py:1345 msgid "Persistent Key/Button Mapping" msgstr "持久按鍵/按鈕對應" #: lib/logitech_receiver/settings_templates.py:1347 msgid "Permanently change the mapping for the key or button." msgstr "永久更改按鍵或按鈕的對應。" #: lib/logitech_receiver/settings_templates.py:1349 msgid "" "Changing important keys or buttons (such as for the left mouse button) can " "result in an unusable system." msgstr "更改重要的按鍵或按鈕(例如左鍵)可能導致系統無法使用。" #: lib/logitech_receiver/settings_templates.py:1406 msgid "Sidetone" msgstr "側音" #: lib/logitech_receiver/settings_templates.py:1407 msgid "Set sidetone level." msgstr "設定側音等級。" #: lib/logitech_receiver/settings_templates.py:1416 msgid "Equalizer" msgstr "均衡器" #: lib/logitech_receiver/settings_templates.py:1417 msgid "Set equalizer levels." msgstr "設定均衡器等級。" #: lib/logitech_receiver/settings_templates.py:1439 msgid "Hz" msgstr "赫茲" #: lib/logitech_receiver/settings_templates.py:1445 msgid "Power Management" msgstr "電源管理" #: lib/logitech_receiver/settings_templates.py:1446 msgid "Power off in minutes (0 for never)." msgstr "幾分鐘後關機(0 表示永不)。" #: lib/logitech_receiver/settings_templates.py:1457 msgid "LED Control" msgstr "LED 控制" #: lib/logitech_receiver/settings_templates.py:1458 msgid "Switch control of LEDs between device and Solaar" msgstr "在裝置與 Solaar 之間切換 LED 控制" #: lib/logitech_receiver/settings_templates.py:1473 msgid "LED Zone Effects" msgstr "LED 區域效果" #: lib/logitech_receiver/settings_templates.py:1474 msgid "Set effect for LED Zone" msgstr "設定 LED 區域的效果" #: lib/logitech_receiver/settings_templates.py:1477 msgid "Speed" msgstr "速度" #: lib/logitech_receiver/settings_templates.py:1478 msgid "Period" msgstr "週期" #: lib/logitech_receiver/settings_templates.py:1479 msgid "Intensity" msgstr "強度" #: lib/logitech_receiver/settings_templates.py:1480 msgid "Ramp" msgstr "漸變" #: lib/logitech_receiver/settings_templates.py:1494 msgid "LEDs" msgstr "LED 燈" #: lib/solaar/ui/__init__.py:112 msgid "Another Solaar process is already running so just expose its window" msgstr "已經有其他的 Solaar 處理程序在執行了,所以將會顯示它的視窗。" #: lib/solaar/ui/about.py:38 msgid "" "Manages Logitech receivers,\n" "keyboards, mice, and tablets." msgstr "管理羅技接收器、鍵盤、滑鼠和平板。" #: lib/solaar/ui/about.py:46 msgid "Additional Programming" msgstr "額外程式化" #: lib/solaar/ui/about.py:47 msgid "GUI design" msgstr "圖形介面設計" #: lib/solaar/ui/about.py:49 msgid "Testing" msgstr "軟體測試" #: lib/solaar/ui/about.py:57 msgid "Logitech documentation" msgstr "羅技文件" #: lib/solaar/ui/action.py:83 lib/solaar/ui/action.py:87 #: lib/solaar/ui/window.py:199 msgid "Unpair" msgstr "取消配對" #: lib/solaar/ui/action.py:86 lib/solaar/ui/diversion_rules.py:104 msgid "Cancel" msgstr "取消" #: lib/solaar/ui/common.py:35 msgid "Permissions error" msgstr "權限錯誤" #: lib/solaar/ui/common.py:37 #, python-format msgid "" "Found a Logitech receiver or device (%s), but did not have permission to " "open it." msgstr "找到了羅技接收器或裝置(%s),但沒有權限開啟。" #: lib/solaar/ui/common.py:39 msgid "" "If you've just installed Solaar, try disconnecting the receiver or device " "and then reconnecting it." msgstr "如果您剛剛安裝了 Solaar,請嘗試斷開接收器或裝置,然後重新連接。" #: lib/solaar/ui/common.py:42 msgid "Cannot connect to device error" msgstr "無法連接到裝置錯誤" #: lib/solaar/ui/common.py:44 #, python-format msgid "" "Found a Logitech receiver or device at %s, but encountered an error " "connecting to it." msgstr "在 %s 找到了一個羅技接收器或裝置,但連接時遇到錯誤。" #: lib/solaar/ui/common.py:46 msgid "" "Try disconnecting the device and then reconnecting it or turning it off and " "then on." msgstr "嘗試斷開裝置然後重新連接,或者關閉裝置然後再開啟。" #: lib/solaar/ui/common.py:49 msgid "Unpairing failed" msgstr "取消配對失敗" #: lib/solaar/ui/common.py:51 #, python-brace-format msgid "Failed to unpair %{device} from %{receiver}." msgstr "取消 %{device} 到 %{receiver} 的配對失敗。" #: lib/solaar/ui/common.py:53 msgid "The receiver returned an error, with no further details." msgstr "接收器出現錯誤,未提供詳細資訊。" #: lib/solaar/ui/config_panel.py:231 msgid "Complete - ENTER to change" msgstr "完成 - 按 ENTER 來修改" #: lib/solaar/ui/config_panel.py:231 msgid "Incomplete" msgstr "未完成" #: lib/solaar/ui/config_panel.py:476 lib/solaar/ui/config_panel.py:529 #, python-format msgid "%d value" msgid_plural "%d values" msgstr[0] "%d 值" #: lib/solaar/ui/config_panel.py:612 msgid "Changes allowed" msgstr "允許更改" #: lib/solaar/ui/config_panel.py:613 msgid "No changes allowed" msgstr "不允許更改" #: lib/solaar/ui/config_panel.py:614 msgid "Ignore this setting" msgstr "忽略此設定" #: lib/solaar/ui/config_panel.py:659 msgid "Working" msgstr "工作中" #: lib/solaar/ui/config_panel.py:662 msgid "Read/write operation failed." msgstr "讀/寫操作失敗。" #: lib/solaar/ui/diversion_rules.py:66 msgid "Built-in rules" msgstr "內建規則" #: lib/solaar/ui/diversion_rules.py:66 msgid "User-defined rules" msgstr "使用者自定義規則" #: lib/solaar/ui/diversion_rules.py:68 lib/solaar/ui/diversion_rules.py:1089 msgid "Rule" msgstr "規則" #: lib/solaar/ui/diversion_rules.py:69 lib/solaar/ui/diversion_rules.py:538 #: lib/solaar/ui/diversion_rules.py:665 msgid "Sub-rule" msgstr "子規則" #: lib/solaar/ui/diversion_rules.py:71 msgid "[empty]" msgstr "[空]" #: lib/solaar/ui/diversion_rules.py:95 msgid "Make changes permanent?" msgstr "使更改永久生效?" #: lib/solaar/ui/diversion_rules.py:100 msgid "Yes" msgstr "是" #: lib/solaar/ui/diversion_rules.py:102 msgid "No" msgstr "否" #: lib/solaar/ui/diversion_rules.py:107 msgid "If you choose No, changes will be lost when Solaar is closed." msgstr "如果您選擇否,當 Solaar 關閉時,更改將會遺失。" #: lib/solaar/ui/diversion_rules.py:138 msgid "Solaar Rule Editor" msgstr "Solaar 規則編輯器" #: lib/solaar/ui/diversion_rules.py:231 msgid "Save changes" msgstr "儲存修改" #: lib/solaar/ui/diversion_rules.py:236 msgid "Discard changes" msgstr "不儲存修改" #: lib/solaar/ui/diversion_rules.py:397 msgid "Insert here" msgstr "在此插入" #: lib/solaar/ui/diversion_rules.py:399 msgid "Insert above" msgstr "在上方插入" #: lib/solaar/ui/diversion_rules.py:401 msgid "Insert below" msgstr "在下方插入" #: lib/solaar/ui/diversion_rules.py:407 msgid "Insert new rule here" msgstr "在此插入新規則" #: lib/solaar/ui/diversion_rules.py:409 msgid "Insert new rule above" msgstr "在上方插入新規則" #: lib/solaar/ui/diversion_rules.py:411 msgid "Insert new rule below" msgstr "在下方插入新規則" #: lib/solaar/ui/diversion_rules.py:456 msgid "Paste here" msgstr "在此貼上" #: lib/solaar/ui/diversion_rules.py:458 msgid "Paste above" msgstr "在上方貼上" #: lib/solaar/ui/diversion_rules.py:460 msgid "Paste below" msgstr "在下方貼上" #: lib/solaar/ui/diversion_rules.py:466 msgid "Paste rule here" msgstr "在此貼上規則" #: lib/solaar/ui/diversion_rules.py:468 msgid "Paste rule above" msgstr "在上方貼上規則" #: lib/solaar/ui/diversion_rules.py:470 msgid "Paste rule below" msgstr "在下方貼上規則" #: lib/solaar/ui/diversion_rules.py:474 msgid "Paste rule" msgstr "貼上規則" #: lib/solaar/ui/diversion_rules.py:503 msgid "Flatten" msgstr "平坦化" #: lib/solaar/ui/diversion_rules.py:536 msgid "Insert" msgstr "插入" #: lib/solaar/ui/diversion_rules.py:539 lib/solaar/ui/diversion_rules.py:667 #: lib/solaar/ui/diversion_rules.py:1129 msgid "Or" msgstr "或" #: lib/solaar/ui/diversion_rules.py:540 lib/solaar/ui/diversion_rules.py:666 #: lib/solaar/ui/diversion_rules.py:1115 msgid "And" msgstr "和" #: lib/solaar/ui/diversion_rules.py:542 msgid "Condition" msgstr "條件" #: lib/solaar/ui/diversion_rules.py:544 lib/solaar/ui/diversion_rules.py:1290 msgid "Feature" msgstr "功能" #: lib/solaar/ui/diversion_rules.py:545 lib/solaar/ui/diversion_rules.py:1325 msgid "Report" msgstr "回報" #: lib/solaar/ui/diversion_rules.py:546 lib/solaar/ui/diversion_rules.py:1204 msgid "Process" msgstr "處理" #: lib/solaar/ui/diversion_rules.py:547 msgid "Mouse process" msgstr "滑鼠處理" #: lib/solaar/ui/diversion_rules.py:548 lib/solaar/ui/diversion_rules.py:1362 msgid "Modifiers" msgstr "修飾鍵" #: lib/solaar/ui/diversion_rules.py:549 lib/solaar/ui/diversion_rules.py:1414 msgid "Key" msgstr "鍵" #: lib/solaar/ui/diversion_rules.py:550 lib/solaar/ui/diversion_rules.py:1455 msgid "KeyIsDown" msgstr "按鍵處於按下狀態" #: lib/solaar/ui/diversion_rules.py:551 lib/solaar/ui/diversion_rules.py:2260 msgid "Active" msgstr "使用中" #: lib/solaar/ui/diversion_rules.py:552 lib/solaar/ui/diversion_rules.py:2217 #: lib/solaar/ui/diversion_rules.py:2269 lib/solaar/ui/diversion_rules.py:2319 msgid "Device" msgstr "裝置" #: lib/solaar/ui/diversion_rules.py:553 lib/solaar/ui/diversion_rules.py:2295 msgid "Host" msgstr "主機" #: lib/solaar/ui/diversion_rules.py:554 lib/solaar/ui/diversion_rules.py:2337 msgid "Setting" msgstr "設定" #: lib/solaar/ui/diversion_rules.py:555 lib/solaar/ui/diversion_rules.py:1470 #: lib/solaar/ui/diversion_rules.py:1519 msgid "Test" msgstr "測試" #: lib/solaar/ui/diversion_rules.py:556 lib/solaar/ui/diversion_rules.py:1633 msgid "Test bytes" msgstr "測試位元組" #: lib/solaar/ui/diversion_rules.py:557 lib/solaar/ui/diversion_rules.py:1734 msgid "Mouse Gesture" msgstr "滑鼠手勢" #: lib/solaar/ui/diversion_rules.py:561 msgid "Action" msgstr "動作" #: lib/solaar/ui/diversion_rules.py:563 lib/solaar/ui/diversion_rules.py:1844 msgid "Key press" msgstr "按鍵按下" #: lib/solaar/ui/diversion_rules.py:564 lib/solaar/ui/diversion_rules.py:1895 msgid "Mouse scroll" msgstr "滑鼠滾動" #: lib/solaar/ui/diversion_rules.py:565 lib/solaar/ui/diversion_rules.py:1956 msgid "Mouse click" msgstr "滑鼠點選" #: lib/solaar/ui/diversion_rules.py:566 msgid "Set" msgstr "設定" #: lib/solaar/ui/diversion_rules.py:567 lib/solaar/ui/diversion_rules.py:2026 msgid "Execute" msgstr "執行" #: lib/solaar/ui/diversion_rules.py:568 lib/solaar/ui/diversion_rules.py:1160 msgid "Later" msgstr "稍後" #: lib/solaar/ui/diversion_rules.py:597 msgid "Insert new rule" msgstr "插入新規則" #: lib/solaar/ui/diversion_rules.py:617 lib/solaar/ui/diversion_rules.py:1681 #: lib/solaar/ui/diversion_rules.py:1786 lib/solaar/ui/diversion_rules.py:1985 msgid "Delete" msgstr "刪除" #: lib/solaar/ui/diversion_rules.py:639 msgid "Negate" msgstr "否" #: lib/solaar/ui/diversion_rules.py:663 msgid "Wrap with" msgstr "包在一起" #: lib/solaar/ui/diversion_rules.py:685 msgid "Cut" msgstr "剪下" #: lib/solaar/ui/diversion_rules.py:700 msgid "Paste" msgstr "貼上" #: lib/solaar/ui/diversion_rules.py:706 msgid "Copy" msgstr "複製" #: lib/solaar/ui/diversion_rules.py:1070 msgid "This editor does not support the selected rule component yet." msgstr "此編輯器尚不支援所選規則元件。" #: lib/solaar/ui/diversion_rules.py:1140 msgid "Number of seconds to delay." msgstr "延遲的秒數。" #: lib/solaar/ui/diversion_rules.py:1178 msgid "Not" msgstr "非" #: lib/solaar/ui/diversion_rules.py:1187 msgid "X11 active process. For use in X11 only." msgstr "X11 活躍程序。僅適用於 X11。" #: lib/solaar/ui/diversion_rules.py:1217 msgid "X11 mouse process. For use in X11 only." msgstr "X11 滑鼠程序。僅適用於 X11。" #: lib/solaar/ui/diversion_rules.py:1234 msgid "MouseProcess" msgstr "滑鼠程序" #: lib/solaar/ui/diversion_rules.py:1258 msgid "Feature name of notification triggering rule processing." msgstr "觸發規則處理的通知功能名稱。" #: lib/solaar/ui/diversion_rules.py:1305 msgid "Report number of notification triggering rule processing." msgstr "回報觸發規則處理的通知數量。" #: lib/solaar/ui/diversion_rules.py:1338 msgid "Active keyboard modifiers. Not always available in Wayland." msgstr "活躍的鍵盤修飾鍵。在 Wayland 中並非總是可用。" #: lib/solaar/ui/diversion_rules.py:1378 msgid "" "Diverted key or button depressed or released.\n" "Use the Key/Button Diversion and Divert G Keys settings to divert keys and " "buttons." msgstr "" "轉向鍵或按鈕被按下或放開。\n" "使用按鍵 / 按鈕轉向和轉向 G 鍵設定來轉向鍵和按鈕。" #: lib/solaar/ui/diversion_rules.py:1387 msgid "Key down" msgstr "按鍵按下" #: lib/solaar/ui/diversion_rules.py:1390 msgid "Key up" msgstr "按鍵鬆開" #: lib/solaar/ui/diversion_rules.py:1430 msgid "" "Diverted key or button is currently down.\n" "Use the Key/Button Diversion and Divert G Keys settings to divert keys and " "buttons." msgstr "" "轉向鍵或按鈕目前處於按下狀態。\n" "使用鍵 / 按鈕轉向和轉向 G 鍵設定來轉向鍵和按鈕。" #: lib/solaar/ui/diversion_rules.py:1468 msgid "Test condition on notification triggering rule processing." msgstr "在通知觸發規則處理上測試條件。" #: lib/solaar/ui/diversion_rules.py:1472 msgid "Parameter" msgstr "參數" #: lib/solaar/ui/diversion_rules.py:1534 msgid "begin (inclusive)" msgstr "開始(包含)" #: lib/solaar/ui/diversion_rules.py:1535 msgid "end (exclusive)" msgstr "結束(不包含)" #: lib/solaar/ui/diversion_rules.py:1543 msgid "range" msgstr "範圍" #: lib/solaar/ui/diversion_rules.py:1546 msgid "minimum" msgstr "最小值" #: lib/solaar/ui/diversion_rules.py:1547 msgid "maximum" msgstr "最大值" #: lib/solaar/ui/diversion_rules.py:1549 #, python-format msgid "bytes %(0)d to %(1)d, ranging from %(2)d to %(3)d" msgstr "位元組 %(0)d 到 %(1)d,範圍從 %(2)d 到 %(3)d" #: lib/solaar/ui/diversion_rules.py:1552 lib/solaar/ui/diversion_rules.py:1553 msgid "mask" msgstr "遮罩" #: lib/solaar/ui/diversion_rules.py:1554 #, python-format msgid "bytes %(0)d to %(1)d, mask %(2)d" msgstr "位元組 %(0)d 到 %(1)d,遮罩 %(2)d" #: lib/solaar/ui/diversion_rules.py:1563 msgid "" "Bit or range test on bytes in notification message triggering rule " "processing." msgstr "在通知訊息觸發規則處理中對位元組進行位元或範圍測試。" #: lib/solaar/ui/diversion_rules.py:1573 msgid "type" msgstr "類型" #: lib/solaar/ui/diversion_rules.py:1661 msgid "" "Mouse gesture with optional initiating button followed by zero or more mouse " "movements." msgstr "滑鼠手勢,可選擇帶有初始按鈕,後跟零個或多個滑鼠移動。" #: lib/solaar/ui/diversion_rules.py:1666 msgid "Add movement" msgstr "新增移動" #: lib/solaar/ui/diversion_rules.py:1760 msgid "" "Simulate a chorded key click or depress or release.\n" "On Wayland requires write access to /dev/uinput." msgstr "" "模擬成組按鍵點選或按下或放開。\n" "在 Wayland 上需要對 /dev/uinput 進行寫入。" #: lib/solaar/ui/diversion_rules.py:1765 msgid "Add key" msgstr "新增按鍵" #: lib/solaar/ui/diversion_rules.py:1768 msgid "Click" msgstr "點選" #: lib/solaar/ui/diversion_rules.py:1771 msgid "Depress" msgstr "按下" #: lib/solaar/ui/diversion_rules.py:1774 msgid "Release" msgstr "放開" #: lib/solaar/ui/diversion_rules.py:1859 msgid "" "Simulate a mouse scroll.\n" "On Wayland requires write access to /dev/uinput." msgstr "" "模擬滑鼠滾動。\n" "在 Wayland 上需要對 /dev/uinput 進行寫入。" #: lib/solaar/ui/diversion_rules.py:1915 msgid "" "Simulate a mouse click.\n" "On Wayland requires write access to /dev/uinput." msgstr "" "模擬滑鼠點選。\n" "在 Wayland 上需要對 /dev/uinput 進行寫入。" #: lib/solaar/ui/diversion_rules.py:1918 msgid "Button" msgstr "按鈕" #: lib/solaar/ui/diversion_rules.py:1919 #, fuzzy msgid "Count and Action" msgstr "條件" #: lib/solaar/ui/diversion_rules.py:1968 msgid "Execute a command with arguments." msgstr "執行帶有參數的命令。" #: lib/solaar/ui/diversion_rules.py:1971 msgid "Add argument" msgstr "新增參數" #: lib/solaar/ui/diversion_rules.py:2046 msgid "Toggle" msgstr "切換" #: lib/solaar/ui/diversion_rules.py:2047 msgid "True" msgstr "真" #: lib/solaar/ui/diversion_rules.py:2048 msgid "False" msgstr "假" #: lib/solaar/ui/diversion_rules.py:2061 msgid "Unsupported setting" msgstr "不支援的設定" #: lib/solaar/ui/diversion_rules.py:2223 lib/solaar/ui/diversion_rules.py:2243 #: lib/solaar/ui/diversion_rules.py:2325 lib/solaar/ui/diversion_rules.py:2568 #: lib/solaar/ui/diversion_rules.py:2586 msgid "Originating device" msgstr "發起裝置" #: lib/solaar/ui/diversion_rules.py:2256 msgid "Device is active and its settings can be changed." msgstr "裝置處於啟用狀態,其設定可以被調整。" #: lib/solaar/ui/diversion_rules.py:2265 #, fuzzy msgid "Device that originated the current notification." msgstr "裝置發起了當前通知。" #: lib/solaar/ui/diversion_rules.py:2278 msgid "Name of host computer." msgstr "主機名稱" #: lib/solaar/ui/diversion_rules.py:2345 msgid "Value" msgstr "數值" #: lib/solaar/ui/diversion_rules.py:2353 msgid "Item" msgstr "項目" #: lib/solaar/ui/diversion_rules.py:2627 msgid "Change setting on device" msgstr "變更裝置上的設定" #: lib/solaar/ui/diversion_rules.py:2643 msgid "Setting on device" msgstr "裝置上的設定" #: lib/solaar/ui/notify.py:118 msgid "unspecified reason" msgstr "不明的原因" #: lib/solaar/ui/pair_window.py:126 lib/solaar/ui/pair_window.py:260 #: lib/solaar/ui/pair_window.py:296 #, python-format msgid "%(receiver_name)s: pair new device" msgstr "%(receiver_name)s:配對新的裝置" #: lib/solaar/ui/pair_window.py:127 #, python-format msgid "Enter passcode on %(name)s." msgstr "在 %(name)s 上輸入密碼。" #: lib/solaar/ui/pair_window.py:130 #, python-format msgid "Type %(passcode)s and then press the enter key." msgstr "輸入 %(passcode)s,然後按 Enter 鍵。" #: lib/solaar/ui/pair_window.py:133 msgid "left" msgstr "左" #: lib/solaar/ui/pair_window.py:133 msgid "right" msgstr "右" #: lib/solaar/ui/pair_window.py:135 #, python-format msgid "" "Press %(code)s\n" "and then press left and right buttons simultaneously." msgstr "" "按下 %(code)s\n" "然後同時按下左鍵和右鍵。" #: lib/solaar/ui/pair_window.py:192 msgid "Pairing failed" msgstr "配對失敗" #: lib/solaar/ui/pair_window.py:194 msgid "Make sure your device is within range, and has a decent battery charge." msgstr "請確認您的裝置已在範圍內,並保證其電量充足。" #: lib/solaar/ui/pair_window.py:196 msgid "A new device was detected, but it is not compatible with this receiver." msgstr "偵測到 1 個新裝置,但與此接收器不相容。" #: lib/solaar/ui/pair_window.py:198 msgid "More paired devices than receiver can support." msgstr "配對的裝置數量超過接收器的支援範圍。" #: lib/solaar/ui/pair_window.py:200 msgid "No further details are available about the error." msgstr "無此錯誤的詳細資訊。" #: lib/solaar/ui/pair_window.py:214 msgid "Found a new device:" msgstr "發現一個新裝置:" #: lib/solaar/ui/pair_window.py:239 msgid "The wireless link is not encrypted" msgstr "無線連線未加密" #: lib/solaar/ui/pair_window.py:268 msgid "Unifying receivers are only compatible with Unifying devices." msgstr "Unifying 接收器只支援 Unifying 裝置。" #: lib/solaar/ui/pair_window.py:270 msgid "Bolt receivers are only compatible with Bolt devices." msgstr "Bolt 接收器只支援 Bolt 裝置。" #: lib/solaar/ui/pair_window.py:272 msgid "Other receivers are only compatible with a few devices." msgstr "其他接收器僅支援少數裝置。" #: lib/solaar/ui/pair_window.py:274 msgid "The device must not be paired with a nearby powered-on receiver." msgstr "裝置不應與附近已開啟的接收器配對。" #: lib/solaar/ui/pair_window.py:278 msgid "Press a pairing button or key until the pairing light flashes quickly." msgstr "按下配對按鈕或按鍵,直到配對燈快速閃爍。" #: lib/solaar/ui/pair_window.py:280 msgid "You may have to first turn the device off and on again." msgstr "您可能需要先關閉裝置,然後再開啟。" #: lib/solaar/ui/pair_window.py:282 msgid "Turn on the device you want to pair." msgstr "開啟您要配對的裝置。" #: lib/solaar/ui/pair_window.py:284 msgid "If the device is already turned on, turn it off and on again." msgstr "如果裝置已開啟,請將其關閉並重新開啟。" #: lib/solaar/ui/pair_window.py:288 #, python-format msgid "" "\n" "\n" "This receiver has %d pairing remaining." msgid_plural "" "\n" "\n" "This receiver has %d pairings remaining." msgstr[0] "" "\n" "\n" "此接收器剩餘 %d 次配對。" #: lib/solaar/ui/pair_window.py:294 msgid "" "\n" "Cancelling at this point will not use up a pairing." msgstr "" "\n" "在此階段取消不會消耗配對次數。" #: lib/solaar/ui/tray.py:58 #, fuzzy msgid "No supported device found" msgstr "沒有找到羅技的裝置" #: lib/solaar/ui/tray.py:63 lib/solaar/ui/window.py:321 #, python-format msgid "About %s" msgstr "關於 %s" #: lib/solaar/ui/tray.py:64 lib/solaar/ui/window.py:319 #, python-format msgid "Quit %s" msgstr "結束 %s" #: lib/solaar/ui/tray.py:294 lib/solaar/ui/tray.py:302 msgid "no receiver" msgstr "無接收器" #: lib/solaar/ui/tray.py:315 lib/solaar/ui/tray.py:320 msgid "offline" msgstr "離線" #: lib/solaar/ui/tray.py:318 msgid "no status" msgstr "無狀態" #: lib/solaar/ui/window.py:99 msgid "Scanning" msgstr "掃描中" #: lib/solaar/ui/window.py:132 msgid "Battery" msgstr "電池" #: lib/solaar/ui/window.py:135 msgid "Wireless Link" msgstr "無線連線" #: lib/solaar/ui/window.py:139 msgid "Lighting" msgstr "亮度" #: lib/solaar/ui/window.py:173 msgid "Show Technical Details" msgstr "顯示細節" #: lib/solaar/ui/window.py:189 msgid "Pair new device" msgstr "配對新裝置" #: lib/solaar/ui/window.py:207 msgid "Select a device" msgstr "選擇 1 個裝置" #: lib/solaar/ui/window.py:324 msgid "Rule Editor" msgstr "規則編輯器" #: lib/solaar/ui/window.py:543 msgid "Path" msgstr "路徑" #: lib/solaar/ui/window.py:546 msgid "USB ID" msgstr "USB ID" #: lib/solaar/ui/window.py:549 lib/solaar/ui/window.py:551 #: lib/solaar/ui/window.py:566 lib/solaar/ui/window.py:568 msgid "Serial" msgstr "序號" #: lib/solaar/ui/window.py:555 msgid "Index" msgstr "索引" #: lib/solaar/ui/window.py:557 msgid "Wireless PID" msgstr "無線 PID" #: lib/solaar/ui/window.py:559 msgid "Product ID" msgstr "產品 ID" #: lib/solaar/ui/window.py:561 msgid "Protocol" msgstr "通訊協定" #: lib/solaar/ui/window.py:561 msgid "Unknown" msgstr "不明" #: lib/solaar/ui/window.py:563 msgid "Polling rate" msgstr "查詢速率" #: lib/solaar/ui/window.py:570 msgid "Unit ID" msgstr "單位 ID" #: lib/solaar/ui/window.py:584 msgid "Notifications" msgstr "通知" #: lib/solaar/ui/window.py:628 msgid "No device paired." msgstr "沒有已經配對的裝置" #: lib/solaar/ui/window.py:637 #, fuzzy, python-format msgid "Up to %(max_count)s device can be paired to this receiver." msgid_plural "Up to %(max_count)s devices can be paired to this receiver." msgstr[0] "此接收器最多可以配對到 %(max_count)s 個裝置" #: lib/solaar/ui/window.py:648 #, python-format msgid "This receiver has %d pairing remaining." msgid_plural "This receiver has %d pairings remaining." msgstr[0] "此接收器剩餘 %d 次配對。" #: lib/solaar/ui/window.py:705 msgid "Battery Voltage" msgstr "電池電壓" #: lib/solaar/ui/window.py:707 msgid "Voltage reported by battery" msgstr "電池回報的電壓" #: lib/solaar/ui/window.py:709 msgid "Battery Level" msgstr "電池電量" #: lib/solaar/ui/window.py:711 msgid "Approximate level reported by battery" msgstr "電池回報的大約電量" #: lib/solaar/ui/window.py:718 lib/solaar/ui/window.py:720 msgid "next reported " msgstr "下一次回報的 " #: lib/solaar/ui/window.py:721 msgid " and next level to be reported." msgstr " 以及下一個將被回報的等級。" #: lib/solaar/ui/window.py:737 msgid "encrypted" msgstr "已加密" #: lib/solaar/ui/window.py:739 msgid "The wireless link between this device and its receiver is encrypted." msgstr "裝置與接收器間的無線連線已加密。" #: lib/solaar/ui/window.py:741 msgid "not encrypted" msgstr "未加密" #: lib/solaar/ui/window.py:745 msgid "" "The wireless link between this device and its receiver is not encrypted.\n" "This is a security issue for pointing devices, and a major security issue " "for text-input devices." msgstr "" "此裝置與其接收器之間的無線連接未加密。\n" "這對指向性裝置是一個安全問題,對文字輸入裝置則是一個重大的安全問題。" #: lib/solaar/ui/window.py:761 #, python-format msgid "%(light_level)d lux" msgstr "%(light_level)d lux" #~ msgid "Turn illumination on or off on keyboard." #~ msgstr "開啟或關閉鍵盤照明。" #~ msgid "Polling Rate (ms)" #~ msgstr "輪詢速率(毫秒)" #~ msgid "May also make M keys and MR key send HID++ notifications" #~ msgstr "也可能讓 M 鍵和 MR 鍵發送 HID++ 通知" #~ msgid "Battery: %(level)s" #~ msgstr "電量:%(level)s " #~ msgid "Battery: %(percent)d%%" #~ msgstr "電量:%(percent)d%%" #~ msgid "Lighting: %(level)s lux" #~ msgstr "亮度: %(level)s lux" #~ msgid "Count" #~ msgstr "計數" #~ msgid "%(rate)d ms (%(rate_hz)dHz)" #~ msgstr "%(rate)d ms (%(rate_hz)dHz)" #~ msgid "none" #~ msgstr "無" #~ msgid "Only one device can be paired to this receiver." #~ msgstr "此接收器只能配對一個裝置" #~ msgid "last known" #~ msgstr "最後已知" #~ msgid "top" #~ msgstr "上" #~ msgid "width" #~ msgstr "寬度" #~ msgid "height" #~ msgstr "高度" #~ msgid "About" #~ msgstr "關於" #~ msgid "No Logitech receiver found" #~ msgstr "未發現羅技接收器" #~ msgid "Quit" #~ msgstr "退出" #~ msgid "Smooth Scrolling" #~ msgstr "平滑滾動" #~ msgid "High Resolution Wheel Invert" #~ msgstr "高解析度滾輪反轉" #~ msgid "High-sensitivity wheel invert mode for vertical scroll." #~ msgstr "垂直捲動的高解析度反轉模式" #~ msgid "Wheel Resolution" #~ msgstr "滾輪解析度" #~ msgid "" #~ "Automatically switch the mouse wheel between ratchet and freespin mode.\n" #~ "The mouse wheel is always free at 0, and always locked at 50" #~ msgstr "自動切換滑鼠滾輪的段落模式" #~ msgid "The receiver was unplugged." #~ msgstr "接收器已移除。" #~ msgid "" #~ "Shows status of devices connected\n" #~ "through wireless Logitech receivers." #~ msgstr "" #~ "顯示羅技接收器\n" #~ "已連線的裝置狀態。" #~ msgid "The receiver only supports %d paired device(s)." #~ msgstr "此接收器僅支援 %d 個配對裝置。" #~ msgid "" #~ "If the device is already turned on,\n" #~ "turn if off and on again." #~ msgstr "" #~ "若裝置的電源已經開啟,\n" #~ "請先關閉再開啟。" #~ msgid "USB id" #~ msgstr "USB ID" #~ msgid "%(battery_percent)d%%" #~ msgstr "%(battery_percent)d%%" #~ msgid "%(battery_level)s" #~ msgstr "%(battery_level)s" #~ msgid "" #~ "The wireless link between this device and its receiver is not encrypted.\n" #~ "\n" #~ "For pointing devices (mice, trackballs, trackpads), this is a minor " #~ "security issue.\n" #~ "\n" #~ "It is, however, a major security issue for text-input devices (keyboards, " #~ "numpads),\n" #~ "because typed text can be sniffed inconspicuously by 3rd parties within " #~ "range." #~ msgstr "" #~ "裝置與接收器間的無線連線未加密。\n" #~ "\n" #~ "對定位裝置(如滑鼠、軌跡球、觸控板)來說這不是什麼問題。\n" #~ "\n" #~ "然而這對於內容輸入裝置(如鍵盤、數字鍵盤)卻是一個重大的安全問題,\n" #~ "您輸入的內容有可能被第三方偷偷監聽到。" #~ msgid "Battery information unknown." #~ msgstr "電池狀態是未知的" #~ msgid "unknown" #~ msgstr "未知的" #~ msgid "Add action" #~ msgstr "增加動作" #~ msgid "" #~ "Try removing the device and plugging it back in or turning it off and " #~ "then on." #~ msgstr "嘗試移除裝置並將其插回,或是關閉然後開啟。" #~ msgid "" #~ "If you've just installed Solaar, try removing the receiver and plugging " #~ "it back in." #~ msgstr "若您剛剛安裝 Solaar,請嘗試將接收器拔下再重新插上。" #~ msgid "" #~ "Found a Logitech Receiver (%s), but did not have permission to open it." #~ msgstr "已發現 1 個羅技接收器 (%s),但您沒有權限存取。" #~ msgid "Send a gesture by sliding the mouse while holding the button down." #~ msgstr "按住按鈕時,透過滑動滑鼠來發送手勢。" #~ msgid "" #~ "Adjust the DPI by sliding the mouse horizontally while holding the button " #~ "down." #~ msgstr "按住按鈕時,透過水平滑動滑鼠來調整 DPI。" #~ msgid "DPI Sliding Adjustment" #~ msgstr "DPI 滑動調整" #~ msgid "" #~ "Automatically switch the mouse wheel between ratchet and freespin mode.\n" #~ "The mouse wheel is always free at 0, and always ratcheted at 50" #~ msgstr "" #~ "自動在滾輪棘輪模式和自由滾動模式之間切換滑鼠滾輪。\n" #~ "滾輪在 0 時始終處於自由狀態,而在 50 時始終處於棘輪狀態" #~ msgid "Scroll Wheel Rachet" #~ msgstr "滾輪棘輪" Solaar-1.1.14/pyproject.toml000066400000000000000000000004131473525213700157420ustar00rootroot00000000000000[tool.ruff] line-length = 127 target-version = "py37" [tool.ruff.lint] select = [ "F", # Pyflakes "E", # pycodestyle "W", # pycodestyle "B", # flake8-bugbear "I", # isort ] [tool.ruff.lint.isort] force-single-line = true lines-between-types = 1 Solaar-1.1.14/release.sh000077500000000000000000000121011473525213700150020ustar00rootroot00000000000000#!/bin/bash repo=pwr-Solaar/Solaar usage() { basename="$(basename $0)" cat </dev/null [ $? -eq 0 ] && prerelease=true stable_branch=stable ref=$(git symbolic-ref HEAD) [ $? -ne 0 ] && echo 'Error: Failed current branch' && exit 1 branch=${ref##*/} commit=$(git rev-list --max-count=1 HEAD) [ $? -ne 0 ] && echo 'Error: Failed to get HEAD' && exit 1 remote=$(git config --get branch.$branch.remote) [ $? -ne 0 ] && echo 'Error: Failed to get remote' && exit 1 remote_ref=$(git config --get branch.$branch.merge) [ $? -ne 0 ] && echo 'Error: Failed to get remote HEAD' && exit 1 remote_branch=${remote_ref##*/} github_token=$(cat ~/.github-token) [ $? -ne 0 ] && echo 'Error: Failed to get github token (check ~/.github_token)' && exit 1 jq -V >/dev/null [ $? -ne 0 ] && echo 'Error: jq is not installed' && exit 1 echo -e "\n\t** You are tagging a release for version $version **\n" echo "Version: $version" echo "Commit: $commit" echo "Pre-release: $prerelease" echo "Remote: $remote" if [ "$branch" == "$remote_branch" ]; then echo "Branch: $branch" else echo "Local branch: $branch" echo "Remote branch: $remote_branch" fi echo -e '\nPlease read RELEASE.md before continuing.\n' read -p 'Are you sure you want to proceed? (y/n) ' -n 1 -r < /dev/tty echo [[ ! $REPLY =~ ^[Yy]$ ]] && echo 'Release aborted.' && exit 1 read -p 'Are you *really* sure you want to proceed? (y/n) ' -n 1 -r < /dev/tty echo [[ ! $REPLY =~ ^[Yy]$ ]] && echo 'Release aborted.' && exit 1 # Check if version is in the changelog grep '^# '"$version" CHANGELOG.md >/dev/null [ $? -ne 0 ] && echo 'Error: Version is not present in the changelog' && exit 1 # Check for uncommitted changes git diff --quiet HEAD >/dev/null [ $? -ne 0 ] && echo -e '\nError: Uncommitted changes found' && exit 1 # Check if commit is a version bump git show -s --format=%B HEAD | grep "^release $version">/dev/null #[ $? -ne 0 ] && echo -e '\nError: Commit does not look like a version bump' && exit 1 # Check if commit has been pushed to remote remote_commit=$(git rev-list --max-count=1 $remote/$branch) [ $? -ne 0 ] && echo -e '\nError: Failed to get remote HEAD' && exit 1 [ "$commit" != "$remote_commit" ] && echo -e '\nError: Commit has not been pushed to the remote' && exit 1 # Check if tag already exists git rev-list --max-count=1 $version >/dev/null 2>/dev/null [ $? -eq 0 ] && echo -e '\nError: Tag already exists' && exit 1 # Check if tag already exists on remote git ls-remote $remote | grep "refs/tags/$version$" >/dev/null [ $? -eq 0 ] && echo -e '\nError: Tag already exists on remote' && exit 1 echo # Create tag echo 'Creating tag...' { echo "release $version" echo found=no while read -r line; do if [[ "$line" == "# "* ]]; then [ "$line" == "# $version" ] && found=yes || found=no fi [ "$found" == 'yes' ] && [ "${line:0:1}" == '*' ] && echo "$line" done < CHANGELOG.md } > /tmp/solaar-changelog [ -z "$DRY_RUN" ] && git tag -s $version -F /tmp/solaar-changelog >/dev/null || true [ $? -ne 0 ] && echo -e '\nError: Failed to create tag' && exit 1 # Push tag echo 'Pushing tag...' [ -z "$DRY_RUN" ] && git push $remote $version >/dev/null || true [ $? -ne 0 ] && echo -e '\nError: Failed to push tag' && exit 1 # Point stable branch to latest version tag echo 'Updating stable branch...' if [[ -z "$DRY_RUN" && $prerelease == "false" ]] then # Check if stable branch does not exist git rev-list --max-count=1 $stable_branch >/dev/null 2>/dev/null [ $? -ne 0 ] && echo -e '\nWarning: Creating stable branch for a first time' && git branch $stable_branch # Fast forward and push stable branch git checkout $stable_branch git merge --ff $version git push $remote $stable_branch >/dev/null || true git checkout $branch fi # Create github release body() { cat </dev/null | jq -r .html_url) [ -z "$DRY_RUN" ] && url=$(curl -H 'Authorization: $github_token' -X POST --data "$(body)" "https://api.github.com/repos/$repo/releases" 2>/dev/null | jq -r .html_url) [ -z "$DRY_RUN" ] && [ -z "$url" ] && echo -e '\nError: Failed to create a github release' && exit 1 [ -z "$DRY_RUN" ] && echo -e "\nRelease created: $url" rm /tmp/solaar-changelog Solaar-1.1.14/rules.d-uinput/000077500000000000000000000000001473525213700157265ustar00rootroot00000000000000Solaar-1.1.14/rules.d-uinput/42-logitech-unify-permissions.rules000066400000000000000000000016511473525213700245270ustar00rootroot00000000000000# This rule was added by Solaar. # # Allows non-root users to have raw access to Logitech devices. # Allowing users to write to the device is potentially dangerous # because they could perform firmware updates. KERNEL=="uinput", SUBSYSTEM=="misc", TAG+="uaccess", OPTIONS+="static_node=uinput" ACTION != "add", GOTO="solaar_end" SUBSYSTEM != "hidraw", GOTO="solaar_end" # USB-connected Logitech receivers and devices ATTRS{idVendor}=="046d", GOTO="solaar_apply" # Lenovo nano receiver ATTRS{idVendor}=="17ef", ATTRS{idProduct}=="6042", GOTO="solaar_apply" # Bluetooth-connected Logitech devices KERNELS == "0005:046D:*", GOTO="solaar_apply" GOTO="solaar_end" LABEL="solaar_apply" # Allow any seated user to access the receiver. # uaccess: modern ACL-enabled udev TAG+="uaccess" # Grant members of the "plugdev" group access to receiver (useful for SSH users) #MODE="0660", GROUP="plugdev" LABEL="solaar_end" # vim: ft=udevrules Solaar-1.1.14/rules.d/000077500000000000000000000000001473525213700144045ustar00rootroot00000000000000Solaar-1.1.14/rules.d/42-logitech-unify-permissions.rules000066400000000000000000000015261473525213700232060ustar00rootroot00000000000000# This rule was added by Solaar. # # Allows non-root users to have raw access to Logitech devices. # Allowing users to write to the device is potentially dangerous # because they could perform firmware updates. ACTION != "add", GOTO="solaar_end" SUBSYSTEM != "hidraw", GOTO="solaar_end" # USB-connected Logitech receivers and devices ATTRS{idVendor}=="046d", GOTO="solaar_apply" # Lenovo nano receiver ATTRS{idVendor}=="17ef", ATTRS{idProduct}=="6042", GOTO="solaar_apply" # Bluetooth-connected Logitech devices KERNELS == "0005:046D:*", GOTO="solaar_apply" GOTO="solaar_end" LABEL="solaar_apply" # Allow any seated user to access the receiver. # uaccess: modern ACL-enabled udev TAG+="uaccess" # Grant members of the "plugdev" group access to receiver (useful for SSH users) #MODE="0660", GROUP="plugdev" LABEL="solaar_end" # vim: ft=udevrules Solaar-1.1.14/setup.py000077500000000000000000000064431473525213700145540ustar00rootroot00000000000000import subprocess import textwrap from glob import glob from os.path import dirname from pathlib import Path from setuptools import find_packages from setuptools import setup NAME = "Solaar" version = Path("lib/solaar/version").read_text().strip() try: # get commit from git describe commit = subprocess.check_output(["git", "describe", "--always"], stderr=subprocess.DEVNULL).strip().decode() Path("lib/solaar/commit").write_text(f"{commit}\n") except Exception: # get commit from Ubuntu dpkg-parsechangelog try: commit = ( subprocess.check_output(["dpkg-parsechangelog", "--show-field", "Version"], stderr=subprocess.DEVNULL) .strip() .decode() ) commit = commit.split("~") Path("lib/solaar/commit").write_text(f"{commit[0]}\n") except Exception as e: print("Exception using dpkg-parsechangelog", e) def _data_files(): yield "share/icons/hicolor/scalable/apps", glob("share/solaar/icons/solaar*.svg") yield "share/icons/hicolor/32x32/apps", glob("share/solaar/icons/solaar-light_*.png") for mo in glob("share/locale/*/LC_MESSAGES/solaar.mo"): yield dirname(mo), [mo] yield "share/applications", ["share/applications/solaar.desktop"] yield "lib/udev/rules.d", ["rules.d/42-logitech-unify-permissions.rules"] yield "share/metainfo", ["share/solaar/io.github.pwr_solaar.solaar.metainfo.xml"] setup( name=NAME.lower(), version=version, description="Linux device manager for Logitech receivers, keyboards, mice, and tablets.", long_description=textwrap.dedent( """ Solaar is a Linux device manager for many Logitech peripherals that connect through Unifying and other receivers or via USB or Bluetooth. Solaar is able to pair/unpair devices with receivers and show and modify some of the modifiable features of devices. For instructions on installing Solaar see https://pwr-solaar.github.io/Solaar/installation""" ), author="Daniel Pavel", license="GPLv2", url="http://pwr-solaar.github.io/Solaar/", classifiers=[ "Development Status :: 4 - Beta", "Environment :: X11 Applications :: GTK", "Environment :: Console", "Intended Audience :: End Users/Desktop", "License :: DFSG approved", "License :: OSI Approved :: GNU General Public License v2 (GPLv2)", "Natural Language :: English", "Programming Language :: Python :: 3 :: Only", "Operating System :: POSIX :: Linux", "Topic :: Utilities", ], platforms=["linux"], python_requires=">=3.8", install_requires=[ 'evdev (>= 1.1.2) ; platform_system=="Linux"', "pyudev (>= 0.13)", "PyYAML (>= 3.12)", "python-xlib (>= 0.27)", "psutil (>= 5.4.3)", 'dbus-python ; platform_system=="Linux"', "PyGObject", ], extras_require={ "report-descriptor": ["hid-parser"], "desktop-notifications": ["Notify (>= 0.7)"], "git-commit": ["python-git-info"], "test": ["pytest", "pytest-mock", "pytest-cov", "typing_extensions"], "dev": ["ruff"], }, package_dir={"": "lib"}, packages=find_packages(where="lib"), data_files=list(_data_files()), include_package_data=True, scripts=glob("bin/*"), ) Solaar-1.1.14/share/000077500000000000000000000000001473525213700141325ustar00rootroot00000000000000Solaar-1.1.14/share/README000066400000000000000000000002231473525213700150070ustar00rootroot00000000000000Solaar application icon drawn by me, roughly guided by Logitech's Unifying logo. All other images are from the GNOME and Oxygen free icon themes. Solaar-1.1.14/share/applications/000077500000000000000000000000001473525213700166205ustar00rootroot00000000000000Solaar-1.1.14/share/applications/solaar.desktop000066400000000000000000000017251473525213700215010ustar00rootroot00000000000000[Desktop Entry] Name=Solaar Comment=Logitech Unifying Receiver peripherals manager Comment[fr]=Gestionnaire de périphériques pour les récepteurs Logitech Unifying Comment[hr]=Upravitelj Logitechovih uređaja povezanih putem Unifying i Nano prijemnika Comment[ru]=Управление приёмником Logitech Unifying Receiver Comment[de]=Logitech Unifying Empfänger Geräteverwaltung Comment[es]=Administrador de periféricos de Logitech Receptor Unifying Comment[pl]=Menedżer urządzeń peryferyjnych odbiornika Logitech Unifying Comment[sv]=Kringutrustningshanterare för Logitech Unifying-mottagare Comment[zh_CN]=罗技优联设备管理器 Comment[zh_TW]=羅技Unifying 裝置管理器 Comment[zh_HK]=羅技Unifying 裝置管理器 Exec=solaar Icon=solaar StartupNotify=true Terminal=false Type=Application Keywords=logitech;unifying;receiver;mouse;keyboard; Categories=Utility;GTK; #Categories=Utility;GTK;Settings;HardwareSettings; X-GNOME-UsesNotifications=true Solaar-1.1.14/share/autostart/000077500000000000000000000000001473525213700161605ustar00rootroot00000000000000Solaar-1.1.14/share/autostart/solaar.desktop000066400000000000000000000017061473525213700210400ustar00rootroot00000000000000[Desktop Entry] Name=Solaar Comment=Logitech Unifying Receiver peripherals manager Comment[fr]=Gestionnaire de périphériques pour les récepteurs Logitech Unifying Comment[hr]=Upravitelj Logitechovih uređaja povezanih putem Unifying i Nano prijemnika Comment[ru]=Управление приёмником Logitech Unifying Receiver Comment[de]=Logitech Unifying Empfänger Geräteverwaltung Comment[es]=Administrador de periféricos de Logitech Receptor Unifying Comment[pl]=Menedżer urządzeń peryferyjnych odbiornika Logitech Unifying Comment[sv]=Kringutrustningshanteraring för Logitech Unifying-mottagare Comment[zh_CN]=罗技优联设备管理器 Comment[zh_TW]=羅技Unifying 裝置管理器 Comment[zh_HK]=羅技Unifying 裝置管理器 Exec=solaar --window=hide Icon=solaar StartupNotify=true Terminal=false Type=Application Keywords=logitech;unifying;receiver;mouse;keyboard; Categories=Utility;GTK; #Categories=Utility;GTK;Settings;HardwareSettings; Solaar-1.1.14/share/solaar-master.svg000066400000000000000000000134571473525213700174370ustar00rootroot00000000000000 Solaar master template image/svg+xml Solaar master template Daniel Pavel 2013-06-25 solaar-master Solaar-1.1.14/share/solaar/000077500000000000000000000000001473525213700154135ustar00rootroot00000000000000Solaar-1.1.14/share/solaar/icons/000077500000000000000000000000001473525213700165265ustar00rootroot00000000000000Solaar-1.1.14/share/solaar/icons/solaar-attention.svg000066400000000000000000000067341473525213700225450ustar00rootroot00000000000000 Solaar attention image/svg+xml Solaar attention Daniel Pavel 2013-06-25 solaar-attention Solaar-1.1.14/share/solaar/icons/solaar-init.svg000066400000000000000000000064011473525213700214720ustar00rootroot00000000000000 Solaar init image/svg+xml Solaar init Daniel Pavel 2013-06-25 solaar-init Solaar-1.1.14/share/solaar/icons/solaar-light_000.png000066400000000000000000000031711473525213700222030ustar00rootroot00000000000000PNG  IHDR szz@IDATxV4g=_cl?Gq[]\۶m۶m[8y޸ުG__] }_IOߟon)xw9@e~~~[n֛\z׻pر:tvض-t]? wѡV  "laB>O .x o??Uwq 'R,zϻ `PaJE K&r*h9/| v)gy*u^70M఩R2 Z>eTX*ahh'|{e/1ždff[[[KFgWmOjZT~7e5k0F:ưU@beebCn TzmbN743Ld[VZlbluUס) {#hoL "yPbeܲ_w)j666*W\踯aF0P(݃ b.RA5h l4ua֤ױXTćpJ_/=s{huR7^p*"^* ٺpP>h6ADN9Y-Q)G?LON LzTK+2k<0e`2PlۂOK̃븊lk K!Rd;6R7y_ >(,N?L޸暫_fSRK9,B-ȤRt{k[I`蚼hl#0gB^8ɪ@>y3Ҡ4~U^DL#4C¶ZG\`qz $ x'@f[>T|T,$F@ШUh<1 lV1 RlNvD^BhJtȈNde>A'PKecfpvn3#CzO)|A1<4: j&YqN"C!{0dx64]k1chZvzKC~`qiE]vw76twpȖXG0 ¨e$|v˂09GpuT*M ޑP$ؖ3ԞR~LOSGP{aIQvpu} Os9ř *b@Z&dáe[-'HPD<~k<z;߃?R\2*M*L]6*[X\Zz<=RdG\j -~P2ؖg;S/S599x.\AGmnױ/S5~uxIENDB`Solaar-1.1.14/share/solaar/icons/solaar-light_020.png000066400000000000000000000035471473525213700222140ustar00rootroot00000000000000PNG  IHDR szz.IDATx#K[]tpmm۶m۶=݇wQ2YU'ow~p+л_;ޜ>7~t::x`, `<σJ@ }. \_6M#d~4 a1X4|x [Aч~r 1Dc du18j+׮^Ώ w xhii5b`0HPP @)}xzCH  j֬%];1\]潕3Ah.S3˲gex0nkz6(Jе9u11ǵ۪y4N[ + 8NkbK~]F*$15(}nm!cw 3ItT2n+$,J煗0m{DF0RiP(ZH>?391;|.}?3-CJ&B1( V C%umsP0ԝ!Y+ c'N_ua$^rxcwel/l{q,MCDIq1@0ci  ]ʕlYT~B'O|cG;e~B5IլWBGc8$!85BЇNV)IPiX @'sssɹe'Y0I|;[n;F0 oA4eSJ|~5Lr0jwj짿|4 ,`)BVƦڲ,TDJ%m)jܶ\sXʩs, ޝ]xu.`0Z_akƾ*!Uuarz>{9tF;23_c!yJ@O5m"5]4MlۆBEiif}tk_շ^+,Q9و1s0CCRv9]˺ #gݡk-)8y/@M* XO[ٱJ =q[~`^k_:3D*M]nqGtCse_bv-0JxƎmY?,j[61k,IA0(_^}泟u7[[['O9_o~S X{s|(ViUV66iuǶm!@UYjy>ɋrLURe[z:D]rZׯ_\ `~+ORyޫQ~tT׍g&qZ-eHA (+zIoDA@p6Imq<}ѣG^A8-!#e ˲8qQg a j5loX۲;P7|6JdµMYV=u[[/N̚/&dEYJh }D ,B]4(MA2 A PKf)ǿW퍍qWl(bqrMaӈy}I˱'2"f>0 Cɋ܀e6yPʲFヲ7/k'JӤy$}pb<4  n|Ġ[24xLf3] DۛlW*{3k>0Y Ah(P|*X[]A{Rtnk$n=Murի?K]xM .=|ziVu 4!cFf ; *0t@;,UZK G^VHF)| ^SN sgt:mPw BI) N i*eQiк6s̊Tu5-@'L8f0(.nfRJn:K3aa`LȬˍi C陡0fңl9rAa+fRFӗ?){Q_Xe:YWX#a໮ym%-h_ z6_`0і9jZfXOg&hݾ!8`?OY/oܸW.xooZ.of1%0B9GN@5+ > Q nqG?&\lomylb[ѰZM6Yd6JjT\.Lbr$x -A0ujc1|ޜ׿ₙ\|9*a紴,&}-*| F˴ԲJqh SߩecBdIENDB`Solaar-1.1.14/share/solaar/icons/solaar-light_060.png000066400000000000000000000044171473525213700222150ustar00rootroot00000000000000PNG  IHDR szzIDATx+M)u'2ڶm>X}ٶڶx2YUg7Y~1/oGnH=1 Ǯ0a5uYvtBam/(U? oʅ\tv8('*2]2!^xE>+"x| S_-0/\ҵ9Zy Fp[<<%[9lm;>l]u՟QzG|;Mtifwglj~W {@``36༆ޣ$F宙rId^ )R(Y~G$?#?[4m$W-/l-|WI c %+ p tӕgji񭜋w۲{_D>9 L 0CAP1 "u[pjl / -WopnAivE|Ξ=mQT:A֚4= ßDcڃA xK-| x5X>\(/Q^ g/-.GcU6"/f;'(KlAd9H3$ ^MxjAoށ(`x0;]0zНwn1T1e)5ub8)ct}dyFKAi~Hv` fL w2tIsf/>R~XloF[ @T*avfAhJ0uaAtֿibgQ-uZE'c.UR*3j p(J 0Q.aREl! J% >} wAĀ*OG%kuFRix d'QsYyGEaރC^g#\)eypxrb)R 2`fH9~K0F0~23"17~7Oa,W0J 63PJyXN갓T4BX֑^C i:΋9]B@Q)i%s N;I@l?혮]#e硆]"@ȫ7ujv/syӡLGK^ɈoH>>`]MgCR:< مgL eX}Lwr{1HV00?gYAۣ 9Tv\-NuWiT # CP}ъ=\`(,R LH]}G*Y C_M00(Uh_)%6: IG\_T 9g=Gȷ6D ;Yѥe$-Zֽx[%xMs㝔{Gm܊8tR23kߋۖoi4Ww=Ğ*͓5be_{}w& /}ߌK`tjWjQ|AR;4#a;ȶ}p{0I,bTQp3Ҷw4^$/4L6 &8-o.\KIZƽv7$&k( cj'S1 ڟWI+yϋ\~m;o[\- E{#m]$^9e j8|?l/i+\}+o[MA17^ԯ1%p fl@L1 aaBP$ E: (x(oMDv,~z)W2!,`Xhq4JGnncv8J.\1"_z=]jW~Z~?SNOR+)(5T:DiDS2h %o]M}k_8O?(]؇N&v{hdqor.L2JFVL Izt'YC(]\y]C-Ǚ~w30yoTf+1[5R념OFqip}8$16C|>NYZIQilcY o5{, Ze5i:NS0,̛Jy.uz'JWĕ#L#">EaHlb9]\Ms]~W婎>鵫uW ? ahRuE0.^T{^ڨE;7St|aqtE?ؼd^WWZ:b`PL&9---߷o_y5d> Xf|>uMc ~%tonUW'Ǖ{ND*&6>:c_y;iHdk+> K[q?KϤ ז!a' # Y;X̷!4~xvs&>@;Ęp5÷.-֟],q+GI9"kQr*6mgR-cXYM7gIGHc,b bP0Ԙ$يx>r5ڽCx70YITڅo f"Qqb-bKP xѨBɪ<1&ƫr/D7>͝o5Zjjr|c)VTL!LY7#{=i߸ta] &[Zgm X,@EA=H@L FNk:$/F[+W%*neSM/g$hG+"&O唈FFS@@A3ړZu?X͂fѝOby*~卩,t^i*"R "C$c@KTР%c'4ֱBap<\÷]$BpA*o曹$͍$?!T5O&*M$h4A shYrM#:u@2dT Բmlw]m/&[%&m5h0K@1%I h^bAkDPnK$kO\ymשToӭn^1Im4i "f0%?2w֕Nw_2lskMo*2W<\7pƦF5{sPj[>)fq)Nc/&'Cђ잩QS=~`קӻoX^-y5U>tk;. n&,3n4Z`[}=Pަ鍕ɯ7I^G/]yYh4P|gD4tv6nw3_2O'uU*A#\ {a1wI9kԜKm0F|>LϗUS0hܶ'[kij;*UAY'l IMwdvMcu6jރ7C2{> dQοjJ!#<Ӳ&b\!63tum/8x=%Da#3z "dcYc; {8H N67>s^|oM{(yD!wȯF'Xo]Yw> |sm@;-<`VS%d_yHX4w@ͧS x񏼸dR1@sU"@V)9npdIENDB`Solaar-1.1.14/share/solaar/icons/solaar-light_unknown.png000066400000000000000000000017571473525213700234130ustar00rootroot00000000000000PNG  IHDR szzIDATxbv,@XYd ~ ˗$e:Lb>}z!..˗Ϊ +P"HEQPTRJ(@PJ(( !o0cҰv߶sΜ=G=hgZC}g|(//GFF?J!P\\l ݁x<$Y/흝aqqӘ...,.pfa`R 8NOO1991`ss///or= p"cnn%%%GNNs*|+-deeB\P"1??Vtww{Fff&AXX5@dqii)yՠJ[[[GYwtuuChBTUU|HNNVpT6PfzuuJ$ p~~OIz|̄h|{{@SSs7// F`9Z $)7Z/;"N&l((++CMMj*wcbbs$m˶RJ ~ x__Ӄ^& U4y\oXpNYcXr;NT;d6)MPTTz;٩ޣ(,,m;ZZZ@\^Ац :csn ԗm5A-_,{5fC2Q l.\E~~>(3ALr<`M/`ww;;;xzzﵵAC0j%B nID~@ u"_A / 5޼y:33SaE W@F) 6b䁘>7?3 %}(Ѿ( Solaar image/svg+xml Solaar Daniel Pavel 2013-06-25 solaar Solaar-1.1.14/share/solaar/icons/solaar.svg000066400000000000000000000063621473525213700205370ustar00rootroot00000000000000 Solaar image/svg+xml Solaar Daniel Pavel 2013-06-25 solaar Solaar-1.1.14/share/solaar/io.github.pwr_solaar.solaar.metainfo.xml000066400000000000000000000161241473525213700252620ustar00rootroot00000000000000 io.github.pwr_solaar.solaar Solaar

Linux manager for Logitech keyboards, mice, and trackpads pwr-Solaar https://github.com/pwr-Solaar/Solaar pfpschneider_AT_gmail.com CC-BY-4.0 GPL-2.0-only pointing keyboard touch

Solaar is a Linux manager for many Logitech keyboards, mice, and trackpads that connect wirelessly to a USB, Lightspeed, or Nano receiver, connect directly via a USB cable, or connect via Bluetooth. Solaar does not work with peripherals from other companies.

Solaar can be used as a GUI application or via its command-line interface. Both interfaces are able to list the connected devices and show information about each device, often including battery status. Solaar is able to pair and unpair devices with receivers as supported by the device and receiver. Solaar can also control some changeable features of devices, such as smooth scrolling or function key behavior.

Solaar's GUI normally uses an icon in the system tray and starts with its main window visible.

solaar.desktop https://raw.githubusercontent.com/pwr-Solaar/Solaar/master/docs/screenshots/Solaar-main-window-button-actions.png https://raw.githubusercontent.com/pwr-Solaar/Solaar/master/docs/screenshots/Solaar-main-window-receiver.png https://raw.githubusercontent.com/pwr-Solaar/Solaar/master/docs/screenshots/Solaar-menu.png solaar usb:v17EFp6042d* usb:v046DpC517d* usb:v046DpC518d* usb:v046DpC51Ad* usb:v046DpC51Bd* usb:v046DpC521d* usb:v046DpC525d* usb:v046DpC526d* usb:v046DpC52Bd* usb:v046DpC52Ed* usb:v046DpC52Fd* usb:v046DpC531d* usb:v046DpC532d* usb:v046DpC534d* usb:v046DpC537d* usb:v046DpC539d* usb:v046DpC53Ad* usb:v046DpC53Dd* usb:v046DpC53Fd* usb:v046DpC541d* usb:v046DpC545d* usb:v046DpC547d* usb:v046DpC548d* usb:v046DpC06Bd* usb:v046DpC07Cd* usb:v046DpC07Dd* usb:v046DpC07Ed* usb:v046DpC07Fd* usb:v046DpC08*d* usb:v046DpC090d* usb:v046DpC091d* usb:v046DpC092d* usb:v046DpC093d* usb:v046DpC094d* usb:v046DpC32Bd* usb:v046DpC32Cd* usb:v046DpC32Dd* usb:v046DpC32Ed* usb:v046DpC32Fd* usb:v046DpC33*d* usb:v046DpC340d* usb:v046DpC341d* usb:v046DpC342d* usb:v046DpC343d* usb:v046DpC344d* usb:v046DpB012d* usb:v046DpB013d* usb:v046DpB014d* usb:v046DpB015d* usb:v046DpB016d* usb:v046DpB017d* usb:v046DpB018d* usb:v046DpB019d* usb:v046DpB01Ad* usb:v046DpB01Bd* usb:v046DpB01Cd* usb:v046DpB01Dd* usb:v046DpB01Ed* usb:v046DpB01Fd* usb:v046DpB02*d* usb:v046DpB03*d* usb:v046DpB04*d* usb:v046DpB05*d* usb:v046DpB06*d* usb:v046DpB07*d* usb:v046DpB08*d* usb:v046DpB09*d* usb:v046DpB0A*d* usb:v046DpB0B*d* usb:v046DpB0C*d* usb:v046DpB0D*d* usb:v046DpB0E*d* usb:v046DpB0F*d* usb:v046DpB32Ad* usb:v046DpB32Bd* usb:v046DpB32Cd* usb:v046DpB32Dd* usb:v046DpB32Ed* usb:v046DpB32Fd* usb:v046DpB33*d* usb:v046DpB34*d* usb:v046DpB35*d* usb:v046DpB36*d* usb:v046DpB37*d* usb:v046DpB38*d* usb:v046DpB39*d* usb:v046DpB3A*d* usb:v046DpB3B*d* usb:v046DpB3C*d* usb:v046DpB3D*d* usb:v046DpB3E*d* usb:v046DpB3F*d* Solaar-1.1.14/tests/000077500000000000000000000000001473525213700141725ustar00rootroot00000000000000Solaar-1.1.14/tests/__init__.py000066400000000000000000000000001473525213700162710ustar00rootroot00000000000000Solaar-1.1.14/tests/hid_parser/000077500000000000000000000000001473525213700163125ustar00rootroot00000000000000Solaar-1.1.14/tests/hid_parser/__init__.py000066400000000000000000000000001473525213700204110ustar00rootroot00000000000000Solaar-1.1.14/tests/hid_parser/test_data.py000066400000000000000000000004221473525213700206320ustar00rootroot00000000000000from hid_parser.data import Button from hid_parser.data import Consumer def test_consumer(): consumer = Consumer() assert consumer.PLAY_PAUSE == 0xCD def test_button(): button = Button() assert button.NO_BUTTON == 0x0 assert button.BUTTON_1 == 0x1 Solaar-1.1.14/tests/hidapi/000077500000000000000000000000001473525213700154305ustar00rootroot00000000000000Solaar-1.1.14/tests/hidapi/__init__.py000066400000000000000000000000001473525213700175270ustar00rootroot00000000000000Solaar-1.1.14/tests/hidapi/test_hidapi.py000066400000000000000000000003421473525213700202760ustar00rootroot00000000000000import platform from unittest import mock if platform.system() == "Linux": import hidapi.udev_impl as hidapi else: import hidapi.hidapi_impl as hidapi def test_find_paired_node(): hidapi.enumerate(mock.Mock()) Solaar-1.1.14/tests/logitech_receiver/000077500000000000000000000000001473525213700176545ustar00rootroot00000000000000Solaar-1.1.14/tests/logitech_receiver/__init__.py000066400000000000000000000000001473525213700217530ustar00rootroot00000000000000Solaar-1.1.14/tests/logitech_receiver/fake_hidpp.py000066400000000000000000000526211473525213700223260ustar00rootroot00000000000000## Copyright (C) 2024 Solaar Contributors https://pwr-solaar.github.io/Solaar/ ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. """HID++ data and functions common to several logitech_receiver test files""" from __future__ import annotations import errno import threading from dataclasses import dataclass from dataclasses import field from struct import pack from typing import Any from typing import Optional from logitech_receiver import device from logitech_receiver import hidpp20 from solaar import configuration def open_path(path: Optional[str]) -> int: if path is None: raise OSError(errno.EACCES, "Fake access error") return int(path, 16) # can raise exception def ping(responses, handle, devnumber, long_message=False): for r in responses: if handle == r.handle and devnumber == r.devnumber and r.id == 0x0010: return r.response def request( responses, handle, devnumber, id, *params, no_reply=False, return_error=False, long_message=False, protocol=1.0, ): params = b"".join(pack("B", p) if isinstance(p, int) else p for p in params) print("REQUEST ", hex(handle), hex(devnumber), hex(id), params.hex()) for r in responses: if handle == r.handle and devnumber == r.devnumber and r.id == id and bytes.fromhex(r.params) == params: print("RESPONSE", hex(r.handle), hex(r.devnumber), hex(r.id), r.params, r.response) return bytes.fromhex(r.response) if r.response is not None else None @dataclass class Response: response: str | float id: int params: str = "" handle: int = 0x11 devnumber: int = 0xFF no_reply: bool = False def replace_number(responses, number): # change the devnumber for a list of responses return [Response(r.response, r.id, r.params, r.handle, number, r.no_reply) for r in responses] def adjust_responses_index(index, responses): # change index-4 responses to index return [Response(r.response, r.id - 0x400 + (index << 8), r.params, r.handle, r.devnumber, r.no_reply) for r in responses] r_empty = [ # a HID++ device with no responses except for ping Response(1.0, 0x0010), # ping ] r_keyboard_1 = [ # a HID++ 1.0 keyboard Response(1.0, 0x0010), # ping Response("001234", 0x81F1, "01"), # firmware Response("003412", 0x81F1, "02"), # firmware Response("002345", 0x81F1, "03"), # firmware Response("003456", 0x81F1, "04"), # firmware Response("050050", 0x8107), # battery status ] r_keyboard_2 = [ # a HID++ 2.0 keyboard Response(4.2, 0x0010), # ping Response("010001", 0x0000, "0001"), # feature set at 0x01 Response("020003", 0x0000, "0020"), # CONFIG_CHANGE at 0x02 Response("030001", 0x0000, "0003"), # device information at 0x03 Response("040003", 0x0000, "0100"), # unknown 0100 at 0x04 Response("050003", 0x0000, "1B04"), # reprogrammable keys V4 at 0x05 Response("060003", 0x0000, "0007"), # device friendly name at 0x06 Response("070003", 0x0000, "0005"), # device name at 0x07 Response("080003", 0x0000, "1000"), # battery status at 0x08 Response("08", 0x0100), # 8 features Response("00010001", 0x0110, "01"), # feature set at 0x01 Response("00200003", 0x0110, "02"), # CONFIG_CHANGE at 0x02 Response("00030001", 0x0110, "03"), # device information at 0x03 Response("01000003", 0x0110, "04"), # unknown 0100 at 0x04 Response("1B040003", 0x0110, "05"), # reprogrammable keys V4 at 0x05 Response("00070003", 0x0000, "06"), # device friendly name at 0x06 Response("00050003", 0x0000, "07"), # device name at 0x07 Response("10000001", 0x0110, "08"), # battery status at 0x02 Response("0212345678000D1234567890ABAA01", 0x0300), # device information Response("04", 0x0500), # reprogrammable keys V4 Response("00110012AB010203CD00", 0x0510, "00"), # reprogrammable keys V4 Response("01110022AB010203CD00", 0x0510, "01"), # reprogrammable keys V4 Response("00010111AB010203CD00", 0x0510, "02"), # reprogrammable keys V4 Response("03110032AB010204CD00", 0x0510, "03"), # reprogrammable keys V4 Response("00030333AB010203CD00", 0x0510, "04"), # reprogrammable keys V4 Response("12", 0x0600), # friendly namme Response("004142434445464748494A4B4C4D4E", 0x0610, "00"), Response("0E4F50515253000000000000000000", 0x0610, "0E"), Response("12", 0x0700), # name and kind Response("4142434445464748494A4B4C4D4E4F", 0x0710, "00"), Response("505152530000000000000000000000", 0x0710, "0F"), Response("00", 0x0720), Response("12345678", 0x0800), # battery status ] r_mouse_1 = [ # a HID++ 1.0 mouse Response(1.0, 0x0010), # ping ] r_mouse_2 = [ # a HID++ 2.0 mouse with few responses except for ping Response(4.2, 0x0010), # ping ] r_mouse_3 = [ # a HID++ 2.0 mouse Response(4.5, 0x0010), # ping Response("010001", 0x0000, "0001"), # feature set at 0x01 Response("020002", 0x0000, "8060"), # report rate at 0x02 Response("040001", 0x0000, "0003"), # device information at 0x04 Response("050002", 0x0000, "0005"), # device type and name at 0x05 Response("08", 0x0100), # 8 features Response("00010001", 0x0110, "01"), # feature set at 0x01 Response("80600002", 0x0110, "02"), # report rate at 0x02 Response("00030001", 0x0110, "04"), # device information at 0x04 Response("00050002", 0x0110, "05"), # device type and name at 0x05 Response("09", 0x0210), # report rate - current rate Response("03123456790008123456780000AA01", 0x0400), # device information Response("0141424302030100", 0x0410, "00"), # firmware 0 Response("0241", 0x0410, "01"), # firmware 1 Response("05", 0x0410, "02"), # firmware 2 Response("12", 0x0500), # name count - 18 characters Response("414241424142414241424142414241", 0x0510, "00"), # name - first 15 characters Response("444544000000000000000000000000", 0x0510, "0F"), # name - last 3 characters ] responses_key = [ # responses for Reprogrammable Keys V4 at 0x05 Response("08", 0x0500), # Reprogrammable Keys V4 count Response("00500038010001010400000000000000", 0x0510, "00"), # left button Response("00510039010001010400000000000000", 0x0510, "01"), # right button Response("0052003A310003070500000000000000", 0x0510, "02"), # middle button Response("0053003C710002030100000000000000", 0x0510, "03"), # back button Response("0056003E710002030100000000000000", 0x0510, "04"), # forward button Response("00C300A9310003070300000000000000", 0x0510, "05"), # smart shift? Response("00C4009D310003070500000000000000", 0x0510, "06"), # ? Response("00D700B4A00004000300000000000000", 0x0510, "07"), # ? Response("00500000000000000000000000000000", 0x0520, "0050"), # left button Response("00510000000000000000000000000000", 0x0520, "0051"), # ... Response("00520100500000000000000000000000", 0x0520, "0052"), Response("00530500000000000000000000000000", 0x0520, "0053"), Response("00561100000000000000000000000000", 0x0520, "0056"), Response("00C30000000000000000000000000000", 0x0520, "00C3"), Response("00C40000500000000000000000000000", 0x0520, "00C4"), Response("00D70000510000000000000000000000", 0x0520, "00D7"), Response("0041", 0x0400), # flags Response("0401", 0x0410), # count Response("0050", 0x0420, "00FF"), # left button Response("0051", 0x0420, "01FF"), # right button Response("0052", 0x0420, "02FF"), # middle button Response("0053", 0x0420, "03FF"), # back button Response("0050000100500000", 0x0430, "0050FF"), # left button current Response("0051000100500001", 0x0430, "0051FF"), # right button current Response("0052000100500001", 0x0430, "0052FF"), # middle button current Response("0053000100500001", 0x0430, "0053FF"), # back button current Response("0050FF01005000", 0x0440, "0050FF01005000"), # left button write Response("0051FF01005000", 0x0440, "0051FF01005000"), # right button write Response("0051FF01005100", 0x0440, "0051FF01005100"), # right button set write ] responses_remap = [ # responses for Persistent Remappable Actions at 0x04 and reprogrammable keys at 0x05 Response("0041", 0x0400), Response("03", 0x0410), Response("0301", 0x0410, "00"), Response("0050", 0x0420, "00FF"), Response("0050000200010001", 0x0430, "0050FF"), # Left Button Response("0051", 0x0420, "01FF"), Response("0051000200010000", 0x0430, "0051FF"), # Left Button Response("0052", 0x0420, "02FF"), Response("0052000100510000", 0x0430, "0052FF"), # key DOWN Response("050002", 0x0000, "1B04"), # REPROGRAMMABLE_KEYS_V4 ] + responses_key responses_gestures = [ # the commented-out messages are not used by either the setting or other testing Response("4203410141020400320480148C21A301", 0x0400, "0000"), # items Response("A302A11EA30A4105822C852DAD2AAD2B", 0x0400, "0008"), Response("8F408F418F434204AF54912282558264", 0x0400, "0010"), Response("01000000000000000000000000000000", 0x0400, "0018"), Response("01000000000000000000000000000000", 0x0410, "000101"), # enable # Response("02000000000000000000000000000000", 0x0410, "000102"), # Response("04000000000000000000000000000000", 0x0410, "000104"), # Response("08000000000000000000000000000000", 0x0410, "000108"), Response("00000000000000000000000000000000", 0x0410, "000110"), # Response("20000000000000000000000000000000", 0x0410, "000120"), # Response("40000000000000000000000000000000", 0x0410, "000140"), # Response("00000000000000000000000000000000", 0x0410, "000180"), # Response("00000000000000000000000000000000", 0x0410, "010101"), # Response("00000000000000000000000000000000", 0x0410, "010102"), # Response("04000000000000000000000000000000", 0x0410, "010104"), # Response("00000000000000000000000000000000", 0x0410, "010108"), Response("6F000000000000000000000000000000", 0x0410, "0001FF"), Response("04000000000000000000000000000000", 0x0410, "01010F"), Response("00000000000000000000000000000000", 0x0430, "000101"), # divert # Response("00000000000000000000000000000000", 0x0430, "000102"), # Response("00000000000000000000000000000000", 0x0430, "000104"), # Response("00000000000000000000000000000000", 0x0430, "000108"), Response("00000000000000000000000000000000", 0x0430, "000110"), # Response("00000000000000000000000000000000", 0x0430, "000120"), # Response("00000000000000000000000000000000", 0x0430, "000140"), # Response("00000000000000000000000000000000", 0x0430, "000180"), # Response("00000000000000000000000000000000", 0x0430, "010101"), # Response("00000000000000000000000000000000", 0x0430, "010102"), Response("00000000000000000000000000000000", 0x0430, "0001FF"), Response("00000000000000000000000000000000", 0x0430, "010103"), Response("08000000000000000000000000000000", 0x0450, "01FF"), Response("08000000000000000000000000000000", 0x0450, "02FF"), Response("08000000000000000000000000000000", 0x0450, "03FF"), Response("00040000000000000000000000000000", 0x0450, "04FF"), Response("5C020000000000000000000000000000", 0x0450, "05FF"), Response("01000000000000000000000000000000", 0x0460, "00FF"), Response("01000000000000000000000000000000", 0x0470, "00FF"), Response("01", 0x0420, "00010101"), # set index 1 Response("00", 0x0420, "00010100"), # unset index 1 Response("01", 0x0420, "00011010"), # set index 4 Response("00", 0x0420, "00011000"), # unset index 4 Response("01", 0x0440, "00010101"), # divert index 1 Response("00", 0x0440, "00010100"), # undivert index 1 Response("000080FF", 0x0480, "000080FF"), # write param 0 Response("000180FF", 0x0480, "000180FF"), # write param 0 ] zone_responses_1 = [ # responses for COLOR LED EFFECTS Response("00000102", 0x0710, "00FF00"), Response("0000000300040005", 0x0720, "000000"), Response("0001000B00080009", 0x0720, "000100"), ] zone_responses_2 = [ # responses for RGB EFFECTS Response("0000000102", 0x0700, "00FF00"), Response("0000000300040005", 0x0700, "000000"), Response("0001000200080009", 0x0700, "000100"), ] effects_responses_1 = [Response("0100000001", 0x0700)] + zone_responses_1 effects_responses_2 = [Response("FFFF0100000001", 0x0700, "FFFF00")] + zone_responses_2 responses_profiles = [ # OnboardProfile in RAM Response("0104010101020100FE0200", 0x0900), Response("000101FF", 0x0950, "00000000"), Response("FFFFFFFF", 0x0950, "00000004"), Response("01010290018003000700140028FFFFFF", 0x0950, "00010000"), Response("FFFF0000000000000000000000000000", 0x0950, "00010010"), Response("8000FFFF900aFF00800204548000FFFF", 0x0950, "00010020"), Response("900aFF00800204548000FFFF900aFF00", 0x0950, "00010030"), Response("800204548000FFFF900aFF0080020454", 0x0950, "00010040"), Response("8000FFFF900aFF00800204548000FFFF", 0x0950, "00010050"), Response("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 0x0950, "00010060"), Response("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 0x0950, "00010070"), Response("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 0x0950, "00010080"), Response("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 0x0950, "00010090"), Response("54004500370000000000000000000000", 0x0950, "000100A0"), Response("00000000000000000000000000000000", 0x0950, "000100B0"), Response("00000000000000000000000000000000", 0x0950, "000100C0"), Response("0A01020300500407000000FFFFFFFFFF", 0x0950, "000100D0"), Response("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 0x0950, "000100E0"), Response("FFFFFFFFFFFFFFFFFFFFFFFFFF7C81AB", 0x0950, "000100EE"), ] responses_profiles_rom = [ # OnboardProfile in ROM Response("0104010101020100FE0200", 0x0900), Response("00000000", 0x0950, "00000000"), Response("010101FF", 0x0950, "01000000"), Response("FFFFFFFF", 0x0950, "01000004"), Response("01010290018003000700140028FFFFFF", 0x0950, "01010000"), Response("FFFF0000000000000000000000000000", 0x0950, "01010010"), Response("8000FFFF900aFF00800204548000FFFF", 0x0950, "01010020"), Response("900aFF00800204548000FFFF900aFF00", 0x0950, "01010030"), Response("800204548000FFFF900aFF0080020454", 0x0950, "01010040"), Response("8000FFFF900aFF00800204548000FFFF", 0x0950, "01010050"), Response("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 0x0950, "01010060"), Response("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 0x0950, "01010070"), Response("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 0x0950, "01010080"), Response("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 0x0950, "01010090"), Response("54004500370000000000000000000000", 0x0950, "010100A0"), Response("00000000000000000000000000000000", 0x0950, "010100B0"), Response("00000000000000000000000000000000", 0x0950, "010100C0"), Response("0A01020300500407000000FFFFFFFFFF", 0x0950, "010100D0"), Response("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 0x0950, "010100E0"), Response("FFFFFFFFFFFFFFFFFFFFFFFFFF7C81AB", 0x0950, "010100EE"), ] responses_profiles_rom_2 = [ # OnboardProfile in ROM Response("0104010101020100FE0200", 0x0900), Response("FFFFFFFF", 0x0950, "00000000"), Response("010101FF", 0x0950, "01000000"), Response("FFFFFFFF", 0x0950, "01000004"), Response("01010290018003000700140028FFFFFF", 0x0950, "01010000"), Response("FFFF0000000000000000000000000000", 0x0950, "01010010"), Response("8000FFFF900aFF00800204548000FFFF", 0x0950, "01010020"), Response("900aFF00800204548000FFFF900aFF00", 0x0950, "01010030"), Response("800204548000FFFF900aFF0080020454", 0x0950, "01010040"), Response("8000FFFF900aFF00800204548000FFFF", 0x0950, "01010050"), Response("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 0x0950, "01010060"), Response("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 0x0950, "01010070"), Response("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 0x0950, "01010080"), Response("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 0x0950, "01010090"), Response("54004500370000000000000000000000", 0x0950, "010100A0"), Response("00000000000000000000000000000000", 0x0950, "010100B0"), Response("00000000000000000000000000000000", 0x0950, "010100C0"), Response("0A01020300500407000000FFFFFFFFFF", 0x0950, "010100D0"), Response("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 0x0950, "010100E0"), Response("FFFFFFFFFFFFFFFFFFFFFFFFFF7C81AB", 0x0950, "010100EE"), ] complex_responses_1 = [ # COLOR_LED_EFFECTS Response(4.2, 0x0010), # ping Response("010001", 0x0000, "0001"), # FEATURE SET at x01 Response("020001", 0x0000, "0020"), # CONFIG_CHANGE at x02 Response("0A", 0x0100), # 10 features Response("070001", 0x0000, "8070"), # COLOR_LED_EFFECTS at 0x07 *effects_responses_1, ] complex_responses_2 = [ # RGB_EFFECTS + reprogrammable keys + persistent actions Response(4.2, 0x0010), # ping Response("010001", 0x0000, "0001"), # FEATURE SET at x01 Response("020001", 0x0000, "0020"), # CONFIG_CHANGE at x02 Response("0A", 0x0100), # 10 features Response("070001", 0x0000, "8071"), # RGB_EFFECTS at 0x07 *effects_responses_2, Response("040001", 0x0000, "1C00"), # Persistent Remappable Actions at 0x04 *responses_remap, Response("080001", 0x0000, "6501"), # Gestures at 0x08 *adjust_responses_index(8, responses_gestures), Response("060003", 0x0000, "1982"), # Backlight 2 at 0x06 Response("010118000001020003000400", 0x0600), Response("090003", 0x0000, "8100"), # Onboard Profiles at 0x09 *responses_profiles, ] responses_speedchange = [ Response("0100", 0x0400), Response("010001", 0x0000, "0001"), # FEATURE SET at x01 Response("0A", 0x0100), # 10 features Response("0120", 0x0410, "0120"), Response("050001", 0x0000, "1B04"), # REPROG_CONTROLS_V4 Response("01", 0x0500), Response("00ED009D310003070500000000000000", 0x0510, "00"), # DPI Change Response("00ED0000000000000000000000000000", 0x0520, "00ED"), # DPI Change current Response("060000", 0x0000, "2205"), # POINTER_SPEED ] # A fake device that uses provided data (responses) to respond to HID++ commands. # Some methods from the real device are used to set up data structures needed for settings @dataclass class Device: name: str = "TESTD" online: bool = True protocol: float = 2.0 responses: Any = field(default_factory=list) codename: str = "TESTC" feature: Optional[int] = None offset: Optional[int] = 4 version: Optional[int] = 0 wpid: Optional[str] = "0000" setting_callback: Any = None sliding = profiles = _backlight = _keys = _remap_keys = _led_effects = _gestures = None _gestures_lock = threading.Lock() read_register = device.Device.read_register write_register = device.Device.write_register backlight = device.Device.backlight keys = device.Device.keys remap_keys = device.Device.remap_keys led_effects = device.Device.led_effects gestures = device.Device.gestures __hash__ = device.Device.__hash__ feature_request = device.Device.feature_request def __post_init__(self): self._name = self.name self._protocol = self.protocol self.persister = configuration._DeviceEntry() self.features = hidpp20.FeaturesArray(self) self.settings = [] if self.feature is not None: self.features = hidpp20.FeaturesArray(self) self.responses = [ Response("010001", 0x0000, "0001"), Response("20", 0x0100), ] + self.responses self.responses.append( Response( f"{int(self.offset):0>2X}00{int(self.version):0>2X}", 0x0000, f"{int(self.feature):0>4X}", ) ) if self.setting_callback is None: self.setting_callback = lambda x, y, z: None self.add_notification_handler = lambda x, y: None def request(self, id, *params, no_reply=False, long_message=False, protocol=2.0): params = b"".join(pack("B", p) if isinstance(p, int) else p for p in params) print("REQUEST ", self._name, hex(id), params.hex().upper()) for r in self.responses: if id == r.id and params == bytes.fromhex(r.params): print("RESPONSE", self._name, hex(r.id), r.params, r.response) return bytes.fromhex(r.response) if isinstance(r.response, str) else r.response print("RESPONSE", self._name, None) def ping(self, handle=None, devnumber=None, long_message=False): print("PING", self._protocol) return self._protocol def match_requests(number, responses, call_args_list): for i in range(0 - number, 0): param = b"".join(pack("B", p) if isinstance(p, int) else p for p in call_args_list[i][0][1:]).hex().upper() print("MATCH", i, hex(call_args_list[i][0][0]), param, hex(responses[i].id), responses[i].params) assert call_args_list[i][0][0] == responses[i].id assert param == responses[i].params Solaar-1.1.14/tests/logitech_receiver/test_base.py000066400000000000000000000142711473525213700222040ustar00rootroot00000000000000import struct import sys from typing import Union from unittest import mock import pytest from logitech_receiver import base from logitech_receiver import exceptions from logitech_receiver.base import HIDPP_SHORT_MESSAGE_ID from logitech_receiver.base import request from logitech_receiver.hidpp10_constants import ErrorCode as Hidpp10Error from logitech_receiver.hidpp20_constants import ErrorCode as Hidpp20Error @pytest.mark.parametrize( "usb_id, expected_name, expected_receiver_kind", [ (0xC548, "Bolt Receiver", "bolt"), (0xC52B, "Unifying Receiver", "unifying"), (0xC531, "Nano Receiver", "nano"), (0xC53F, "Lightspeed Receiver", None), (0xC517, "EX100 Receiver 27 Mhz", "27Mhz"), ], ) def test_product_information(usb_id, expected_name, expected_receiver_kind): res = base.product_information(usb_id) assert res["name"] == expected_name assert isinstance(res["vendor_id"], int) assert isinstance(res["product_id"], int) if expected_receiver_kind: assert res["receiver_kind"] == expected_receiver_kind def test_filter_receivers_known(): bus_id = 2 vendor_id = 0x046D product_id = 0xC548 receiver_info = base._filter_receivers(bus_id, vendor_id, product_id) assert receiver_info["name"] == "Bolt Receiver" assert receiver_info["receiver_kind"] == "bolt" def test_filter_receivers_unknown(): bus_id = 1 vendor_id = 0x046D product_id = 0xC500 receiver_info = base._filter_receivers(bus_id, vendor_id, product_id) assert receiver_info["bus_id"] == bus_id assert receiver_info["product_id"] == product_id @pytest.mark.parametrize( "product_id, hidpp_short, hidpp_long", [ (0xC548, True, False), (0xC07E, True, False), (0xC07E, False, True), (0xA07E, False, True), (0xA07E, None, None), (0xA07C, False, False), ], ) def test_filter_products_of_interest(product_id, hidpp_short, hidpp_long): bus_id = 3 vendor_id = 0x046D receiver_info = base._filter_products_of_interest( bus_id, vendor_id, product_id, hidpp_short=hidpp_short, hidpp_long=hidpp_long, ) if not hidpp_short and not hidpp_long: assert receiver_info is None else: assert isinstance(receiver_info["vendor_id"], int) assert receiver_info["product_id"] == product_id @pytest.mark.parametrize( "report_id, sub_id, address, valid_notification", [ (0x1, 0x72, 0x57, True), (0x1, 0x40, 0x63, True), (0x1, 0x40, 0x71, True), (0x1, 0x80, 0x71, False), (0x1, 0x00, 0x70, False), (0x20, 0x09, 0x71, False), (0x1, 0x37, 0x71, False), ], ) def test_make_notification(report_id, sub_id, address, valid_notification): devnumber = 123 data = bytes([sub_id, address, 0x02, 0x03, 0x04]) result = base.make_notification(report_id, devnumber, data) if valid_notification: assert isinstance(result, base.HIDPPNotification) assert result.report_id == report_id assert result.devnumber == devnumber assert result.sub_id == sub_id assert result.address == address assert result.data == bytes([0x02, 0x03, 0x04]) else: assert result is None def test_get_next_sw_id(): res1 = base._get_next_sw_id() res2 = base._get_next_sw_id() assert res1 == 2 assert res2 == 3 @pytest.mark.parametrize( "prefix, error_code, return_error, raise_exception", [ (b"\x8f", Hidpp10Error.INVALID_SUB_ID_COMMAND, False, False), (b"\x8f", Hidpp10Error.INVALID_SUB_ID_COMMAND, True, False), (b"\xff", Hidpp20Error.UNKNOWN, False, True), ], ) def test_request_errors( prefix: bytes, error_code: Union[Hidpp10Error, Hidpp20Error], return_error: bool, raise_exception: bool ): handle = 0 device_number = 66 next_sw_id = 0x02 reply_data_sw_id = struct.pack("!H", 0x0000 | next_sw_id) with mock.patch( "logitech_receiver.base._read", return_value=(HIDPP_SHORT_MESSAGE_ID, device_number, prefix + reply_data_sw_id + struct.pack("B", error_code)), ), mock.patch("logitech_receiver.base._skip_incoming", return_value=None), mock.patch( "logitech_receiver.base.write", return_value=None ), mock.patch("logitech_receiver.base._get_next_sw_id", return_value=next_sw_id): if raise_exception: with pytest.raises(exceptions.FeatureCallError) as context: request(handle, device_number, next_sw_id, return_error=return_error) assert context.value.number == device_number assert context.value.request == next_sw_id assert context.value.error == error_code assert context.value.params == b"" else: result = request(handle, device_number, next_sw_id, return_error=return_error) assert result == (error_code if return_error else None) @pytest.mark.skipif(sys.platform == "darwin", reason="Test only runs on Linux") @pytest.mark.parametrize( "simulated_error, expected_result", [ (Hidpp10Error.INVALID_SUB_ID_COMMAND, 1.0), (Hidpp10Error.RESOURCE_ERROR, None), (Hidpp10Error.CONNECTION_REQUEST_FAILED, None), (Hidpp10Error.UNKNOWN_DEVICE, exceptions.NoSuchDevice), ], ) def test_ping_errors(simulated_error: Hidpp10Error, expected_result): handle = 1 device_number = 1 next_sw_id = 0x05 reply_data_sw_id = struct.pack("!H", 0x0010 | next_sw_id) with mock.patch( "logitech_receiver.base._read", return_value=(HIDPP_SHORT_MESSAGE_ID, device_number, b"\x8f" + reply_data_sw_id + bytes([simulated_error])), ), mock.patch("logitech_receiver.base._get_next_sw_id", return_value=next_sw_id): if isinstance(expected_result, type) and issubclass(expected_result, Exception): with pytest.raises(expected_result) as context: base.ping(handle=handle, devnumber=device_number) assert context.value.number == device_number assert context.value.request == struct.unpack("!H", reply_data_sw_id)[0] else: result = base.ping(handle=handle, devnumber=device_number) assert result == expected_result Solaar-1.1.14/tests/logitech_receiver/test_common.py000066400000000000000000000173071473525213700225650ustar00rootroot00000000000000from enum import IntFlag import pytest import yaml from logitech_receiver import common def test_crc16(): value = b"123456789" expected = 0x29B1 result = common.crc16(value) assert result == expected def test_named_int(): named_int = common.NamedInt(0x2, "pulse") assert named_int.name == "pulse" assert named_int == 2 assert repr(named_int) == "NamedInt(2, 'pulse')" def test_named_int_comparison(): named_int = common.NamedInt(0, "entry") named_int_equal = common.NamedInt(0, "entry") named_int_unequal_name = common.NamedInt(0, "unequal") named_int_unequal_value = common.NamedInt(5, "entry") named_int_unequal = common.NamedInt(2, "unequal") assert named_int == named_int_equal assert named_int != named_int_unequal_name assert named_int != named_int_unequal_value assert named_int != named_int_unequal assert named_int is not None assert named_int == 0 assert named_int == "entry" def test_named_int_comparison_exception(): named_int = common.NamedInt(0, "entry") with pytest.raises(TypeError): assert named_int == b"\x00" def test_named_int_conversions(): named_int = common.NamedInt(2, "two") assert named_int.bytes() == b"\x00\x02" assert str(named_int) == "two" def test_named_int_yaml(): named_int = common.NamedInt(2, "two") yaml_string = yaml.dump(named_int) # assert yaml_string == "!NamedInt {name: two, value: 2}\n" yaml_load = yaml.safe_load(yaml_string) assert yaml_load == named_int def test_named_ints(): named_ints = common.NamedInts(empty=0, critical=5, low=20, good=50, full=90) assert named_ints.empty == 0 assert named_ints.empty.name == "empty" assert named_ints.critical == 5 assert named_ints.critical.name == "critical" assert named_ints.low == 20 assert named_ints.low.name == "low" assert named_ints.good == 50 assert named_ints.good.name == "good" assert named_ints.full == 90 assert named_ints.full.name == "full" assert len(named_ints) == 5 assert 5 in named_ints assert 6 not in named_ints assert "critical" in named_ints assert "easy" not in named_ints assert common.NamedInt(5, "critical") in named_ints assert common.NamedInt(5, "five") not in named_ints assert common.NamedInt(6, "critical") not in named_ints assert named_ints[5] == "critical" assert named_ints["critical"] == "critical" assert named_ints[66] is None assert named_ints["5"] is None assert len(named_ints[:]) == len(named_ints) assert len(named_ints[0:100]) == len(named_ints) assert len(named_ints[5:90]) == 3 assert len(named_ints[5:]) == 4 assert named_ints[90:5] == [] def test_named_ints_fallback(): named_ints = common.NamedInts(empty=0, critical=5, low=20, good=50, full=90) named_ints._fallback = lambda x: str(x) fallback = named_ints[80] assert fallback == common.NamedInt(80, "80") def test_named_ints_list(): named_ints_list = common.NamedInts.list([0, 5, 20, 50, 90]) assert len(named_ints_list) == 5 assert 50 in named_ints_list assert 60 not in named_ints_list def test_named_ints_range(): named_ints_range = common.NamedInts.range(0, 5) assert len(named_ints_range) == 6 assert 4 in named_ints_range assert 6 not in named_ints_range @pytest.mark.parametrize( "code, expected_flags", [ (0, []), (0b0010, ["two"]), (0b0101, ["one", "three"]), (0b1001, ["one", "unknown:000008"]), ], ) def test_named_ints_flag_names(code, expected_flags): named_ints_flag_bits = common.NamedInts(one=0b001, two=0b010, three=0b100) flags = list(named_ints_flag_bits.flag_names(code)) assert flags == expected_flags @pytest.mark.parametrize( "code, expected_flags", [ (0, []), (0b0010, ["two"]), (0b0101, ["one", "three"]), (0b1001, ["one", "unknown:000008"]), ], ) def test_flag_names(code, expected_flags): class ExampleFlag(IntFlag): one = 0x1 two = 0x2 three = 0x4 flags = common.flag_names(ExampleFlag, code) assert list(flags) == expected_flags def test_named_ints_setitem(): named_ints = common.NamedInts(empty=0, critical=5, low=20, good=50, full=90) named_ints[55] = "better" named_ints[60] = common.NamedInt(60, "sixty") with pytest.raises(TypeError): named_ints[70] = 70 with pytest.raises(ValueError): named_ints[70] = "empty" with pytest.raises(ValueError): named_ints[50] = "new" assert named_ints[55] == "better" assert named_ints[60] == "sixty" def test_named_ints_other(): named_ints = common.NamedInts(empty=0, critical=5) named_ints_2 = common.NamedInts(good=50) union = named_ints.__or__(named_ints_2) assert list(named_ints) == [common.NamedInt(0, "empty"), common.NamedInt(5, "critical")] assert len(named_ints) == 2 assert repr(named_ints) == "NamedInts(NamedInt(0, 'empty'), NamedInt(5, 'critical'))" assert len(union) == 3 assert list(union) == [common.NamedInt(0, "empty"), common.NamedInt(5, "critical"), common.NamedInt(50, "good")] def test_unsorted_named_ints(): named_ints = common.UnsortedNamedInts(critical=5, empty=0) named_ints_2 = common.UnsortedNamedInts(good=50) union = named_ints.__or__(named_ints_2) unionr = named_ints_2.__or__(named_ints) assert len(union) == 3 assert list(union) == [common.NamedInt(5, "critical"), common.NamedInt(0, "empty"), common.NamedInt(50, "good")] assert len(unionr) == 3 assert list(unionr) == [common.NamedInt(50, "good"), common.NamedInt(5, "critical"), common.NamedInt(0, "empty")] @pytest.mark.parametrize( "bytes_input, expected_output", [ (b"\x01\x02\x03\x04", "01020304"), (b"", ""), ], ) def test_strhex(bytes_input, expected_output): result = common.strhex(bytes_input) assert result == expected_output def test_bytest2int(): value = b"\x12\x34\x56\x78" expected = 0x12345678 result = common.bytes2int(value) assert result == expected def test_int2bytes(): value = 0x12345678 expected = b"\x12\x34\x56\x78" result = common.int2bytes(value) assert result == expected def test_kw_exception(): e = common.KwException(foo=0, bar="bar") assert e.foo == 0 assert e.bar == "bar" @pytest.mark.parametrize( "status, expected_level, expected_ok, expected_charging, expected_string", [ (common.BatteryStatus.FULL, common.BatteryLevelApproximation.FULL, True, True, "Battery: full (full)"), (common.BatteryStatus.ALMOST_FULL, common.BatteryLevelApproximation.GOOD, True, True, "Battery: good (almost full)"), (common.BatteryStatus.RECHARGING, common.BatteryLevelApproximation.GOOD, True, True, "Battery: good (recharging)"), ( common.BatteryStatus.SLOW_RECHARGE, common.BatteryLevelApproximation.LOW, True, True, "Battery: low (slow recharge)", ), (common.BatteryStatus.DISCHARGING, None, True, False, ""), ], ) def test_battery(status, expected_level, expected_ok, expected_charging, expected_string): battery = common.Battery(None, None, status, None) assert battery.status == status assert battery.level == expected_level assert battery.ok() == expected_ok assert battery.charging() == expected_charging assert battery.to_str() == expected_string def test_battery_2(): battery = common.Battery(50, None, common.BatteryStatus.DISCHARGING, None) assert battery.status == common.BatteryStatus.DISCHARGING assert battery.level == 50 assert battery.ok() assert not battery.charging() assert battery.to_str() == "Battery: 50% (discharging)" Solaar-1.1.14/tests/logitech_receiver/test_desktop_notifications.py000066400000000000000000000007171473525213700256740ustar00rootroot00000000000000from unittest import mock from logitech_receiver import desktop_notifications def test_notifications_available(): result = desktop_notifications.notifications_available() assert not result def test_init(): assert not desktop_notifications.init() def test_uninit(): assert desktop_notifications.uninit() is None def test_show(): dev = mock.MagicMock() reason = "unknown" assert desktop_notifications.show(dev, reason) is None Solaar-1.1.14/tests/logitech_receiver/test_device.py000066400000000000000000000327511473525213700225340ustar00rootroot00000000000000## Copyright (C) 2024 Solaar Contributors https://pwr-solaar.github.io/Solaar/ ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. from dataclasses import dataclass from functools import partial from typing import Optional import pytest from logitech_receiver import common from logitech_receiver import device from logitech_receiver import hidpp20 from . import fake_hidpp class LowLevelInterfaceFake: def __init__(self, responses=None): self.responses = responses def open_path(self, path): return fake_hidpp.open_path(path) def find_paired_node(self, receiver_path: str, index: int, timeout: int): return None def request(self, response, *args, **kwargs): func = partial(fake_hidpp.request, self.responses) return func(response, *args, **kwargs) def ping(self, response, *args, **kwargs): func = partial(fake_hidpp.ping, self.responses) return func(response, *args, **kwargs) def close(self, *args, **kwargs): pass @dataclass class DeviceInfoStub: path: str product_id: str vendor_id: int = 1133 hidpp_short: bool = False hidpp_long: bool = True bus_id: int = 0x0003 # USB serial: str = "aa:aa:aa;aa" di_bad_handle = DeviceInfoStub(None, product_id="CCCC") di_error = DeviceInfoStub(11, product_id="CCCC") di_CCCC = DeviceInfoStub("11", product_id="CCCC") di_C318 = DeviceInfoStub("11", product_id="C318") di_B530 = DeviceInfoStub("11", product_id="B350", bus_id=0x0005) di_C068 = DeviceInfoStub("11", product_id="C06B") di_C08A = DeviceInfoStub("11", product_id="C08A") di_DDDD = DeviceInfoStub("11", product_id="DDDD") @pytest.mark.parametrize( "device_info, responses, expected_success", [ (di_bad_handle, fake_hidpp.r_empty, None), (di_error, fake_hidpp.r_empty, False), (di_CCCC, fake_hidpp.r_empty, True), ], ) def test_create_device(device_info, responses, expected_success): low_level_mock = LowLevelInterfaceFake(responses) if expected_success is None: with pytest.raises(PermissionError): device.create_device(low_level_mock, device_info) elif not expected_success: with pytest.raises(TypeError): device.create_device(low_level_mock, device_info) else: test_device = device.create_device(low_level_mock, device_info) assert bool(test_device) == expected_success @pytest.mark.parametrize( "device_info, responses, expected_codename, expected_name, expected_kind", [(di_CCCC, fake_hidpp.r_empty, "?? (CCCC)", "Unknown device CCCC", "?")], ) def test_device_name(device_info, responses, expected_codename, expected_name, expected_kind): low_level = LowLevelInterfaceFake(responses) test_device = device.create_device(low_level, device_info) assert test_device.codename == expected_codename assert test_device.name == expected_name assert test_device.kind == expected_kind @pytest.mark.parametrize( "device_info, responses, handle, _name, _codename, number, protocol, registers", zip( [di_CCCC, di_C318, di_B530, di_C068, di_C08A, di_DDDD], [ fake_hidpp.r_empty, fake_hidpp.r_keyboard_1, fake_hidpp.r_keyboard_2, fake_hidpp.r_mouse_1, fake_hidpp.r_mouse_2, fake_hidpp.r_mouse_3, ], [0x11, 0x11, 0x11, 0x11, 0x11, 0x11], [None, "Illuminated Keyboard", "Craft Advanced Keyboard", "G700 Gaming Mouse", "MX Vertical Wireless Mouse", None], [None, "Illuminated", "Craft", "G700", "MX Vertical", None], [0xFF, 0x0, 0xFF, 0x0, 0xFF, 0xFF], [1.0, 1.0, 4.5, 1.0, 4.5, 4.5], [[], [], [], (common.NamedInt(7, "battery status"), common.NamedInt(81, "three leds")), [], []], ), ) def test_device_info(device_info, responses, handle, _name, _codename, number, protocol, registers): test_device = device.Device(LowLevelInterfaceFake(responses), None, None, None, handle=handle, device_info=device_info) assert test_device.handle == handle assert test_device._name == _name assert test_device._codename == _codename assert test_device.number == number assert test_device._protocol == protocol assert test_device.registers == registers assert bool(test_device) test_device.__del__() assert not bool(test_device) @dataclass class FakeReceiver: path: str = "11" handle: int = 0x11 codename: Optional[str] = None def device_codename(self, number): return self.codename def __contains__(self, dev): return True pi_CCCC = {"wpid": "CCCC", "kind": 0, "serial": None, "polling": "1ms", "power_switch": "top"} pi_2011 = {"wpid": "2011", "kind": 1, "serial": "1234", "polling": "2ms", "power_switch": "bottom"} pi_4066 = {"wpid": "4066", "kind": 1, "serial": "5678", "polling": "4ms", "power_switch": "left"} pi_1007 = {"wpid": "1007", "kind": 2, "serial": "1234", "polling": "8ms", "power_switch": "right"} pi_407B = {"wpid": "407B", "kind": 2, "serial": "5678", "polling": "1ms", "power_switch": "left"} pi_DDDD = {"wpid": "DDDD", "kind": 2, "serial": "1234", "polling": "2ms", "power_switch": "top"} @pytest.mark.parametrize( "number, pairing_info, responses, handle, _name, codename, p, p2, name", zip( range(1, 7), [pi_CCCC, pi_2011, pi_4066, pi_1007, pi_407B, pi_DDDD], [ fake_hidpp.r_empty, fake_hidpp.r_keyboard_1, fake_hidpp.r_keyboard_2, fake_hidpp.r_mouse_1, fake_hidpp.r_mouse_2, fake_hidpp.r_mouse_3, ], [0x11, 0x11, 0x11, 0x11, 0x11, 0x11], [None, "Wireless Keyboard K520", "Craft Advanced Keyboard", "MX Air", "MX Vertical Wireless Mouse", None], ["CODE", "K520", "Craft", "MX Air", "MX Vertical", "CODE"], [None, 1.0, 4.5, 1.0, 4.5, None], [1.0, 1.0, 4.5, 1.0, 4.5, 4.5], [ "CODE", "Wireless Keyboard K520", "Craft Advanced Keyboard", "MX Air", "MX Vertical Wireless Mouse", "ABABABABABABABADED", ], ), ) def test_device_receiver(number, pairing_info, responses, handle, _name, codename, p, p2, name): low_level = LowLevelInterfaceFake(responses) low_level.request = partial(fake_hidpp.request, fake_hidpp.replace_number(responses, number)) low_level.ping = partial(fake_hidpp.ping, fake_hidpp.replace_number(responses, number)) test_device = device.Device(low_level, FakeReceiver(codename="CODE"), number, True, pairing_info, handle=handle) test_device.receiver.device = test_device assert test_device.handle == handle assert test_device._name == _name assert test_device.codename == codename assert test_device.number == number assert test_device._protocol == p assert test_device.protocol == p2 assert test_device.codename == codename assert test_device.name == name assert test_device == test_device assert not (test_device != test_device) assert bool(test_device) test_device.__del__() @pytest.mark.parametrize( "number, info, responses, handle, unitId, modelId, tid, kind, firmware, serial, id, psl, rate", zip( range(1, 7), [pi_CCCC, pi_2011, pi_4066, pi_1007, pi_407B, pi_DDDD], [ fake_hidpp.r_empty, fake_hidpp.r_keyboard_1, fake_hidpp.r_keyboard_2, fake_hidpp.r_mouse_1, fake_hidpp.r_mouse_2, fake_hidpp.r_mouse_3, ], [None, 0x11, 0x11, 0x11, 0x11, 0x11], [None, None, "12345678", None, None, "12345679"], # unitId [None, None, "1234567890AB", None, None, "123456780000"], # modelId [None, None, {"btid": "1234", "wpid": "5678", "usbid": "90AB"}, None, None, {"usbid": "1234"}], # tid_map ["?", 1, 1, 2, 2, 2], # kind [(), True, (), (), (), True], # firmware [None, "1234", "5678", "1234", "5678", "1234"], # serial ["", "1234", "12345678", "1234", "5678", "12345679"], # id ["top", "bottom", "left", "right", "left", "top"], # power switch location ["1ms", "2ms", "4ms", "8ms", "1ms", "9ms"], # polling rate ), ) def test_device_ids(number, info, responses, handle, unitId, modelId, tid, kind, firmware, serial, id, psl, rate): low_level = LowLevelInterfaceFake(responses) low_level.request = partial(fake_hidpp.request, fake_hidpp.replace_number(responses, number)) low_level.ping = partial(fake_hidpp.ping, fake_hidpp.replace_number(responses, number)) test_device = device.Device(low_level, FakeReceiver(), number, True, info, handle=handle) assert test_device.unitId == unitId assert test_device.modelId == modelId assert test_device.tid_map == tid assert test_device.kind == kind assert test_device.firmware == firmware or len(test_device.firmware) > 0 and firmware is True assert test_device.id == id assert test_device.power_switch_location == psl assert test_device.polling_rate == rate class FakeDevice(device.Device): # a fully functional Device but its HID++ functions look at local data def __init__(self, responses, *args, **kwargs): self.responses = responses super().__init__(LowLevelInterfaceFake(responses), *args, **kwargs) request = fake_hidpp.Device.request ping = fake_hidpp.Device.ping @pytest.mark.parametrize( "device_info, responses, protocol, led, keys, remap, gestures, backlight, profiles", [ (di_CCCC, fake_hidpp.r_empty, 1.0, type(None), None, None, None, None, None), (di_C318, fake_hidpp.r_empty, 1.0, type(None), None, None, None, None, None), (di_B530, fake_hidpp.r_keyboard_1, 1.0, type(None), None, None, None, None, None), (di_B530, fake_hidpp.r_keyboard_2, 2.0, type(None), 4, 0, 0, None, None), (di_B530, fake_hidpp.complex_responses_1, 4.5, hidpp20.LEDEffectsInfo, 0, 0, 0, None, None), (di_B530, fake_hidpp.complex_responses_2, 4.5, hidpp20.RGBEffectsInfo, 8, 3, 1, True, True), ], ) def test_device_complex(device_info, responses, protocol, led, keys, remap, gestures, backlight, profiles, mocker): test_device = FakeDevice(responses, None, None, True, device_info=device_info) test_device._name = "TestDevice" test_device._protocol = protocol spy_request = mocker.spy(test_device, "request") assert type(test_device.led_effects) == led if keys is None: assert test_device.keys == keys else: assert len(test_device.keys) == keys if remap is None: assert test_device.remap_keys == remap else: assert len(test_device.remap_keys) == remap assert (test_device.gestures is None) == (gestures is None) assert (test_device.backlight is None) == (backlight is None) assert (test_device.profiles is None) == (profiles is None) test_device.set_configuration(55) if protocol > 1.0: spy_request.assert_called_with(0x210, 55, no_reply=False) test_device.reset() if protocol > 1.0: spy_request.assert_called_with(0x210, 0, no_reply=False) @pytest.mark.parametrize( "device_info, responses, protocol, p, persister, settings", [ (di_CCCC, fake_hidpp.r_empty, 1.0, None, None, 0), (di_C318, fake_hidpp.r_empty, 1.0, {}, {}, 0), (di_C318, fake_hidpp.r_keyboard_1, 1.0, {"n": "n"}, {"n": "n"}, 1), (di_B530, fake_hidpp.r_keyboard_2, 4.5, {"m": "m"}, {"m": "m"}, 1), (di_C068, fake_hidpp.r_mouse_1, 1.0, {"o": "o"}, {"o": "o"}, 2), (di_C08A, fake_hidpp.r_mouse_2, 4.5, {"p": "p"}, {"p": "p"}, 0), ], ) def test_device_settings(device_info, responses, protocol, p, persister, settings, mocker): mocker.patch("solaar.configuration.persister", return_value=p) test_device = FakeDevice(responses, None, None, True, device_info=device_info) test_device._name = "TestDevice" test_device._protocol = protocol assert test_device.persister == persister assert len(test_device.settings) == settings @pytest.mark.parametrize( "device_info, responses, protocol, battery, changed", [ (di_C318, fake_hidpp.r_empty, 1.0, None, {"active": True, "alert": 0, "reason": None}), ( di_C318, fake_hidpp.r_keyboard_1, 1.0, common.Battery(50, None, 0, None), {"active": True, "alert": 0, "reason": None}, ), ( di_B530, fake_hidpp.r_keyboard_2, 4.5, common.Battery(18, 52, None, None), {"active": True, "alert": 0, "reason": None}, ), ], ) def test_device_battery(device_info, responses, protocol, battery, changed, mocker): test_device = FakeDevice(responses, None, None, online=True, device_info=device_info) test_device._name = "TestDevice" test_device._protocol = protocol spy_changed = mocker.spy(test_device, "changed") assert test_device.battery() == battery test_device.read_battery() spy_changed.assert_called_with(**changed) Solaar-1.1.14/tests/logitech_receiver/test_diversion.py000066400000000000000000000067461473525213700233040ustar00rootroot00000000000000import textwrap from unittest import mock from unittest.mock import mock_open import pytest from logitech_receiver import diversion from logitech_receiver.base import HIDPPNotification from logitech_receiver.hidpp20_constants import SupportedFeature @pytest.fixture def rule_config(): rule_content = """ %YAML 1.3 --- - MouseGesture: Mouse Left - KeyPress: - [Control_L, Alt_L, Left] - click ... --- - MouseGesture: Mouse Up - KeyPress: - [Super_L, Up] - click ... --- - Test: [thumb_wheel_up, 10] - KeyPress: - [Control_L, Page_Down] - click ... --- """ return textwrap.dedent(rule_content) def test_load_rule_config(rule_config): expected_rules = [ [ diversion.MouseGesture, diversion.KeyPress, ], [diversion.MouseGesture, diversion.KeyPress], [diversion.Test, diversion.KeyPress], ] with mock.patch("builtins.open", new=mock_open(read_data=rule_config)): loaded_rules = diversion._load_rule_config(file_path=mock.Mock()) assert len(loaded_rules.components) == 2 # predefined and user configured rules user_configured_rules = loaded_rules.components[0] assert isinstance(user_configured_rules, diversion.Rule) for components, expected_components in zip(user_configured_rules.components, expected_rules): for component, expected_component in zip(components.components, expected_components): assert isinstance(component, expected_component) def test_diversion_rule(): args = [ { "Rule": [ # Implement problematic keys for Craft and MX Master {"Rule": [{"Key": ["Brightness Down", "pressed"]}, {"KeyPress": "XF86_MonBrightnessDown"}]}, {"Rule": [{"Key": ["Brightness Up", "pressed"]}, {"KeyPress": "XF86_MonBrightnessUp"}]}, ] }, ] rule = diversion.Rule(args) assert len(rule.components) == 1 root_rule = rule.components[0] assert isinstance(root_rule, diversion.Rule) assert len(root_rule.components) == 2 for component in root_rule.components: assert isinstance(component, diversion.Rule) assert len(component.components) == 2 key = component.components[0] assert isinstance(key, diversion.Key) key = component.components[1] assert isinstance(key, diversion.KeyPress) def test_key_is_down(): result = diversion.key_is_down(key=diversion.CONTROL.G2) assert result is False def test_feature(): expected_data = {"Feature": "CONFIG CHANGE"} result = diversion.Feature("CONFIG_CHANGE") assert result.data() == expected_data @pytest.mark.parametrize( "feature, data", [ ( SupportedFeature.REPROG_CONTROLS_V4, [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08], ), (SupportedFeature.GKEY, [0x01, 0x02, 0x03, 0x04]), (SupportedFeature.MKEYS, [0x01, 0x02, 0x03, 0x04]), (SupportedFeature.MR, [0x01, 0x02, 0x03, 0x04]), (SupportedFeature.THUMB_WHEEL, [0x01, 0x02, 0x03, 0x04, 0x05]), (SupportedFeature.DEVICE_UNIT_ID, [0x01, 0x02, 0x03, 0x04, 0x05]), ], ) def test_process_notification(feature, data): device_mock = mock.Mock() notification = HIDPPNotification( report_id=0x01, devnumber=1, sub_id=0x13, address=0x00, data=bytes(data), ) diversion.process_notification(device_mock, notification, feature) Solaar-1.1.14/tests/logitech_receiver/test_hidpp10.py000066400000000000000000000255731473525213700225460ustar00rootroot00000000000000from dataclasses import dataclass from dataclasses import field from typing import Any from typing import List from typing import Optional import pytest from logitech_receiver import common from logitech_receiver import hidpp10 from logitech_receiver import hidpp10_constants from logitech_receiver.hidpp10_constants import Registers _hidpp10 = hidpp10.Hidpp10() @dataclass class Response: response: Optional[str] request_id: int params: Any @dataclass class Device: name: str = "Device" online: bool = True kind: str = "fake" protocol: float = 1.0 isDevice: bool = False # incorrect, but useful here registers: List[Registers] = field(default_factory=list) responses: List[Response] = field(default_factory=list) def request(self, id, params=None, no_reply=False): if params is None: params = [] print("REQUEST ", self.name, hex(id), params) for r in self.responses: if id == r.request_id and params == r.params: print("RESPONSE", self.name, hex(r.request_id), r.params, r.response) return bytes.fromhex(r.response) if r.response is not None else None device_offline = Device("OFFLINE", False) device_leds = Device("LEDS", True, registers=[Registers.THREE_LEDS, Registers.BATTERY_STATUS]) device_features = Device("FEATURES", True, protocol=4.5) registers_standard = [Registers.BATTERY_STATUS, Registers.FIRMWARE] responses_standard = [ Response("555555", 0x8100 | Registers.BATTERY_STATUS, 0x00), Response("666666", 0x8100 | Registers.BATTERY_STATUS, 0x10), Response("777777", 0x8000 | Registers.BATTERY_STATUS, 0x00), Response("888888", 0x8000 | Registers.BATTERY_STATUS, 0x10), Response("052100", 0x8100 | Registers.BATTERY_STATUS, []), Response("ABCDEF", 0x8100 | Registers.FIRMWARE, 0x01), Response("ABCDEF", 0x8100 | Registers.FIRMWARE, 0x02), Response("ABCDEF", 0x8100 | Registers.FIRMWARE, 0x03), Response("ABCDEF", 0x8100 | Registers.FIRMWARE, 0x04), Response("000900", 0x8100 | Registers.NOTIFICATIONS, []), Response("101010", 0x8100 | Registers.MOUSE_BUTTON_FLAGS, []), Response("010101", 0x8100 | Registers.KEYBOARD_FN_SWAP, []), Response("020202", 0x8100 | Registers.DEVICES_CONFIGURATION, []), Response("030303", 0x8000 | Registers.DEVICES_CONFIGURATION, 0x00), ] device_standard = Device("STANDARD", True, registers=registers_standard, responses=responses_standard) @pytest.mark.parametrize( "device, register, param, expected_result", [ (device_offline, Registers.THREE_LEDS, 0x00, None), (device_standard, Registers.THREE_LEDS, 0x00, None), (device_standard, Registers.BATTERY_STATUS, 0x00, "555555"), (device_standard, Registers.BATTERY_STATUS, 0x10, "666666"), ], ) def test_read_register(device, register, param, expected_result, mocker): spy_request = mocker.spy(device, "request") result = hidpp10.read_register(device, register, param) assert result == (bytes.fromhex(expected_result) if expected_result else None) spy_request.assert_called_once_with(0x8100 | register, param) @pytest.mark.parametrize( "device, register, param, expected_result", [ (device_offline, Registers.THREE_LEDS, 0x00, None), (device_standard, Registers.THREE_LEDS, 0x00, None), (device_standard, Registers.BATTERY_STATUS, 0x00, "777777"), (device_standard, Registers.BATTERY_STATUS, 0x10, "888888"), ], ) def test_write_register(device, register, param, expected_result, mocker): spy_request = mocker.spy(device, "request") result = hidpp10.write_register(device, register, param) assert result == (bytes.fromhex(expected_result) if expected_result else None) spy_request.assert_called_once_with(0x8000 | register, param) def device_charge(name, response): responses = [Response(response, 0x8100 | Registers.BATTERY_CHARGE, [])] return Device(name, registers=[], responses=responses) device_charge1 = device_charge("DISCHARGING", "550030") device_charge2 = device_charge("RECHARGING", "440050") device_charge3 = device_charge("FULL", "600090") device_charge4 = device_charge("OTHER", "220000") def device_status(name, response): responses = [Response(response, 0x8100 | Registers.BATTERY_STATUS, [])] return Device(name, registers=[], responses=responses) device_status1 = device_status("FULL", "072200") device_status2 = device_status("GOOD", "052100") device_status3 = device_status("LOW", "032200") device_status4 = device_status("CRITICAL", "010100") device_status5 = device_status("EMPTY", "000000") device_status6 = device_status("NOSTATUS", "002200") @pytest.mark.parametrize( "device, expected_result, expected_register", [ (device_offline, None, None), (device_features, None, None), (device_leds, None, None), ( device_standard, common.Battery(common.BatteryLevelApproximation.GOOD, None, common.BatteryStatus.RECHARGING, None), Registers.BATTERY_STATUS, ), (device_charge1, common.Battery(0x55, None, common.BatteryStatus.DISCHARGING, None), Registers.BATTERY_CHARGE), ( device_charge2, common.Battery(0x44, None, common.BatteryStatus.RECHARGING, None), Registers.BATTERY_CHARGE, ), ( device_charge3, common.Battery(0x60, None, common.BatteryStatus.FULL, None), Registers.BATTERY_CHARGE, ), (device_charge4, common.Battery(0x22, None, None, None), Registers.BATTERY_CHARGE), ( device_status1, common.Battery(common.BatteryLevelApproximation.FULL, None, common.BatteryStatus.FULL, None), Registers.BATTERY_STATUS, ), ( device_status2, common.Battery(common.BatteryLevelApproximation.GOOD, None, common.BatteryStatus.RECHARGING, None), Registers.BATTERY_STATUS, ), ( device_status3, common.Battery(common.BatteryLevelApproximation.LOW, None, common.BatteryStatus.FULL, None), Registers.BATTERY_STATUS, ), ( device_status4, common.Battery(common.BatteryLevelApproximation.CRITICAL, None, None, None), Registers.BATTERY_STATUS, ), ( device_status5, common.Battery(common.BatteryLevelApproximation.EMPTY, None, common.BatteryStatus.DISCHARGING, None), Registers.BATTERY_STATUS, ), ( device_status6, common.Battery(None, None, common.BatteryStatus.FULL, None), Registers.BATTERY_STATUS, ), ], ) def test_hidpp10_get_battery(device, expected_result, expected_register): result = _hidpp10.get_battery(device) assert result == expected_result if expected_register is not None: assert expected_register in device.registers @pytest.mark.parametrize( "device, expected_firmwares", [ (device_offline, []), ( device_standard, [ common.FirmwareKind.Firmware, common.FirmwareKind.Bootloader, common.FirmwareKind.Other, ], ), ], ) def test_hidpp10_get_firmware(device, expected_firmwares): firmwares = _hidpp10.get_firmware(device) if not expected_firmwares: assert firmwares is None else: firmware_types = [firmware.kind for firmware in firmwares] assert firmware_types == expected_firmwares assert len(firmwares) == len(expected_firmwares) @pytest.mark.parametrize( "device, level, charging, warning, p1, p2", [ (device_leds, common.BatteryLevelApproximation.EMPTY, False, False, 0x33, 0x00), (device_leds, common.BatteryLevelApproximation.CRITICAL, False, False, 0x22, 0x00), (device_leds, common.BatteryLevelApproximation.LOW, False, False, 0x20, 0x00), (device_leds, common.BatteryLevelApproximation.GOOD, False, False, 0x20, 0x02), (device_leds, common.BatteryLevelApproximation.FULL, False, False, 0x20, 0x22), (device_leds, None, True, False, 0x30, 0x33), (device_leds, None, False, True, 0x02, 0x00), (device_leds, None, False, False, 0x11, 0x11), ], ) def test_set_3leds(device, level, charging, warning, p1, p2, mocker): spy_request = mocker.spy(device, "request") _hidpp10.set_3leds(device, level, charging, warning) spy_request.assert_called_once_with(0x8000 | Registers.THREE_LEDS, p1, p2) @pytest.mark.parametrize("device", [device_offline, device_features]) def test_set_3leds_missing(device, mocker): spy_request = mocker.spy(device, "request") _hidpp10.set_3leds(device) assert spy_request.call_count == 0 @pytest.mark.parametrize("device", [device_standard]) def test_get_notification_flags(device): result = _hidpp10.get_notification_flags(device) assert result == int("000900", 16) def test_set_notification_flags(mocker): device = device_standard spy_request = mocker.spy(device, "request") result = _hidpp10.set_notification_flags( device, hidpp10_constants.NOTIFICATION_FLAG.battery_status, hidpp10_constants.NOTIFICATION_FLAG.wireless ) spy_request.assert_called_once_with(0x8000 | Registers.NOTIFICATIONS, b"\x10\x01\x00") assert result is not None def test_set_notification_flags_bad(mocker): device = device_features spy_request = mocker.spy(device, "request") result = _hidpp10.set_notification_flags( device, hidpp10_constants.NOTIFICATION_FLAG.battery_status, hidpp10_constants.NOTIFICATION_FLAG.wireless ) assert spy_request.call_count == 0 assert result is None def test_get_device_features(): result = _hidpp10.get_device_features(device_standard) assert result == int("101010", 16) @pytest.mark.parametrize( "device, register, expected_result", [ (device_standard, Registers.BATTERY_STATUS, "052100"), (device_standard, Registers.MOUSE_BUTTON_FLAGS, "101010"), (device_standard, Registers.KEYBOARD_ILLUMINATION, None), (device_features, Registers.KEYBOARD_ILLUMINATION, None), ], ) def test_get_register(device, register, expected_result): result = _hidpp10._get_register(device, register) assert result == (int(expected_result, 16) if expected_result is not None else None) @pytest.mark.parametrize( "device, expected_result", [ (device_standard, 2), (device_features, None), ], ) def test_get_configuration_pending_flags(device, expected_result): result = hidpp10.get_configuration_pending_flags(device) assert result == expected_result @pytest.mark.parametrize( "device, expected_result", [ (device_standard, True), (device_features, False), ], ) def test_set_configuration_pending_flags(device, expected_result): result = hidpp10.set_configuration_pending_flags(device, 0x00) assert result == expected_result Solaar-1.1.14/tests/logitech_receiver/test_hidpp20_complex.py000066400000000000000000001052071473525213700242670ustar00rootroot00000000000000## Copyright (C) 2024 Solaar Contributors https://pwr-solaar.github.io/Solaar/ ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import pytest import yaml from logitech_receiver import common from logitech_receiver import exceptions from logitech_receiver import hidpp20 from logitech_receiver import hidpp20_constants from logitech_receiver import special_keys from logitech_receiver.hidpp20_constants import GestureId from . import fake_hidpp _hidpp20 = hidpp20.Hidpp20() device_offline = fake_hidpp.Device("REGISTERS", False) device_registers = fake_hidpp.Device("OFFLINE", True, 1.0) device_nofeatures = fake_hidpp.Device("NOFEATURES", True, 4.5) device_zerofeatures = fake_hidpp.Device("ZEROFEATURES", True, 4.5, [fake_hidpp.Response("0000", 0x0000, "0001")]) device_broken = fake_hidpp.Device( "BROKEN", True, 4.5, [fake_hidpp.Response("0500", 0x0000, "0001"), fake_hidpp.Response(None, 0x0100)] ) device_standard = fake_hidpp.Device("STANDARD", True, 4.5, fake_hidpp.r_keyboard_2) @pytest.mark.parametrize( "device, expected_result, expected_count", [ (device_offline, False, 0), (device_registers, False, 0), (device_nofeatures, False, 0), (device_zerofeatures, False, 0), (device_broken, False, 0), (device_standard, True, 9), ], ) def test_FeaturesArray_check(device, expected_result, expected_count): featuresarray = hidpp20.FeaturesArray(device) result = featuresarray._check() result2 = featuresarray._check() assert result == expected_result assert result2 == expected_result assert (hidpp20_constants.SupportedFeature.ROOT in featuresarray) == expected_result assert len(featuresarray) == expected_count assert bool(featuresarray) == expected_result @pytest.mark.parametrize( "device, expected0, expected1, expected2, expected5, expected5v", [ (device_zerofeatures, None, None, None, None, None), (device_standard, 0x0000, 0x0001, 0x0020, hidpp20_constants.SupportedFeature.REPROG_CONTROLS_V4, 3), ], ) def test_FeaturesArray_get_feature(device, expected0, expected1, expected2, expected5, expected5v): featuresarray = hidpp20.FeaturesArray(device) device.features = featuresarray result0 = featuresarray.get_feature(0) result1 = featuresarray.get_feature(1) result2 = featuresarray.get_feature(2) result5 = featuresarray.get_feature(5) result2r = featuresarray.get_feature(2) result5v = featuresarray.get_feature_version(hidpp20_constants.SupportedFeature.REPROG_CONTROLS_V4) assert result0 == expected0 assert result1 == expected1 assert result2 == expected2 assert result2r == expected2 assert result5 == expected5 assert result5v == expected5v @pytest.mark.parametrize( "device, expected_result", [ (device_zerofeatures, []), ( device_standard, [ (hidpp20_constants.SupportedFeature.ROOT, 0), (hidpp20_constants.SupportedFeature.FEATURE_SET, 1), (hidpp20_constants.SupportedFeature.CONFIG_CHANGE, 2), (hidpp20_constants.SupportedFeature.DEVICE_FW_VERSION, 3), (common.NamedInt(256, "unknown:0100"), 4), (hidpp20_constants.SupportedFeature.REPROG_CONTROLS_V4, 5), (None, 6), (None, 7), (hidpp20_constants.SupportedFeature.BATTERY_STATUS, 8), ], ), ], ) def test_FeaturesArray_enumerate(device, expected_result): featuresarray = hidpp20.FeaturesArray(device) result = list(featuresarray.enumerate()) assert result == expected_result def test_FeaturesArray_setitem(): featuresarray = hidpp20.FeaturesArray(device_standard) featuresarray[hidpp20_constants.SupportedFeature.ROOT] = 3 featuresarray[hidpp20_constants.SupportedFeature.FEATURE_SET] = 5 featuresarray[hidpp20_constants.SupportedFeature.FEATURE_SET] = 4 assert featuresarray[hidpp20_constants.SupportedFeature.FEATURE_SET] == 4 assert featuresarray.inverse[4] == hidpp20_constants.SupportedFeature.FEATURE_SET def test_FeaturesArray_delitem(): featuresarray = hidpp20.FeaturesArray(device_standard) with pytest.raises(ValueError): del featuresarray[5] @pytest.mark.parametrize( "device, expected0, expected1, expected2, expected1v", [(device_zerofeatures, None, None, None, None), (device_standard, 0, 5, None, 3)], ) def test_FeaturesArray_getitem(device, expected0, expected1, expected2, expected1v): featuresarray = hidpp20.FeaturesArray(device) device.features = featuresarray result_get0 = featuresarray[hidpp20_constants.SupportedFeature.ROOT] result_get1 = featuresarray[hidpp20_constants.SupportedFeature.REPROG_CONTROLS_V4] result_get2 = featuresarray[hidpp20_constants.SupportedFeature.GKEY] result_1v = featuresarray.get_feature_version(hidpp20_constants.SupportedFeature.REPROG_CONTROLS_V4) assert result_get0 == expected0 assert result_get1 == expected1 assert result_get2 == expected2 assert result_1v == expected1v @pytest.mark.parametrize( "device, index, cid, tid, flags, default_task, flag_names", [ (device_standard, 2, 1, 1, 0x30, "Volume Up", ["reprogrammable", "divertable"]), (device_standard, 1, 2, 2, 0x20, "Volume Down", ["divertable"]), ], ) def test_ReprogrammableKey_key(device, index, cid, tid, flags, default_task, flag_names): key = hidpp20.ReprogrammableKey(device, index, cid, tid, flags) assert key._device == device assert key.index == index assert key._cid == cid assert key._tid == tid assert key._flags == flags assert key.key == special_keys.CONTROL[cid] assert key.default_task == common.NamedInt(cid, default_task) assert list(key.flags) == flag_names @pytest.mark.parametrize( "device, index, cid, tid, flags, pos, group, gmask, default_task, flag_names, group_names", [ (device_standard, 1, 0x51, 0x39, 0x60, 0, 1, 1, "Right Click", ["divertable", "persistently divertable"], ["g1"]), (device_standard, 2, 0x52, 0x3A, 0x11, 1, 2, 3, "Mouse Middle Button", ["mse", "reprogrammable"], ["g1", "g2"]), ( device_standard, 3, 0x53, 0x3C, 0x110, 2, 2, 7, "Mouse Back Button", ["reprogrammable", "raw XY"], ["g1", "g2", "g3"], ), ], ) def test_reprogrammable_key_v4_key(device, index, cid, tid, flags, pos, group, gmask, default_task, flag_names, group_names): key = hidpp20.ReprogrammableKeyV4(device, index, cid, tid, flags, pos, group, gmask) assert key._device == device assert key.index == index assert key._cid == cid assert key._tid == tid assert key._flags == flags assert key.pos == pos assert key.group == group assert key._gmask == gmask assert key.key == special_keys.CONTROL[cid] assert key.default_task == common.NamedInt(cid, default_task) assert list(key.flags) == flag_names assert list(key.group_mask) == group_names @pytest.mark.parametrize( "responses, index, mapped_to, remappable_to, mapping_flags", [ (fake_hidpp.responses_key, 1, "Right Click", common.UnsortedNamedInts(Right_Click=81, Left_Click=80), []), (fake_hidpp.responses_key, 2, "Left Click", None, ["diverted"]), (fake_hidpp.responses_key, 3, "Mouse Back Button", None, ["diverted", "persistently diverted"]), (fake_hidpp.responses_key, 4, "Mouse Forward Button", None, ["diverted", "raw XY diverted"]), ], ) # these fields need access all the key data, so start by setting up a device and its key data def test_ReprogrammableKeyV4_query(responses, index, mapped_to, remappable_to, mapping_flags): device = fake_hidpp.Device( "KEY", responses=responses, feature=hidpp20_constants.SupportedFeature.REPROG_CONTROLS_V4, offset=5 ) device._keys = _hidpp20.get_keys(device) key = device.keys[index] assert key.mapped_to == mapped_to assert (key.remappable_to == remappable_to) or remappable_to is None assert list(key.mapping_flags) == mapping_flags @pytest.mark.parametrize( "responses, index, diverted, persistently_diverted, rawXY_reporting, remap, sets", [ (fake_hidpp.responses_key, 1, True, False, True, 0x52, ["0051080000"]), (fake_hidpp.responses_key, 2, False, True, False, 0x51, ["0052020000", "0052200000", "0052000051"]), (fake_hidpp.responses_key, 3, False, True, True, 0x50, ["0053020000", "00530C0000", "0053300000", "0053000050"]), (fake_hidpp.responses_key, 4, False, False, False, 0x50, ["0056020000", "0056080000", "0056200000", "0056000050"]), ], ) def test_ReprogrammableKeyV4_set(responses, index, diverted, persistently_diverted, rawXY_reporting, remap, sets, mocker): responses += [fake_hidpp.Response(r, 0x530, r) for r in sets] device = fake_hidpp.Device( "KEY", responses=responses, feature=hidpp20_constants.SupportedFeature.REPROG_CONTROLS_V4, offset=5 ) device._keys = _hidpp20.get_keys(device) device._keys._ensure_all_keys_queried() # do this now so that the last requests are sets spy_request = mocker.spy(device, "request") key = device.keys[index] _mapping_flags = list(key.mapping_flags) if "divertable" in key.flags or not diverted: key.set_diverted(diverted) else: with pytest.raises(exceptions.FeatureNotSupported): key.set_diverted(diverted) assert ("diverted" in list(key.mapping_flags)) == (diverted and "divertable" in key.flags) if "persistently divertable" in key.flags or not persistently_diverted: key.set_persistently_diverted(persistently_diverted) else: with pytest.raises(exceptions.FeatureNotSupported): key.set_persistently_diverted(persistently_diverted) assert ("persistently diverted" in key.mapping_flags) == (persistently_diverted and "persistently divertable" in key.flags) if "raw XY" in key.flags or not rawXY_reporting: key.set_rawXY_reporting(rawXY_reporting) else: with pytest.raises(exceptions.FeatureNotSupported): key.set_rawXY_reporting(rawXY_reporting) assert ("raw XY diverted" in list(key.mapping_flags)) == (rawXY_reporting and "raw XY" in key.flags) if remap in key.remappable_to or remap == 0: key.remap(remap) else: with pytest.raises(exceptions.FeatureNotSupported): key.remap(remap) assert (key.mapped_to == remap) or (remap not in key.remappable_to and remap != 0) fake_hidpp.match_requests(len(sets), responses, spy_request.call_args_list) @pytest.mark.parametrize( "r, index, cid, actionId, remapped, mask, status, action, modifiers, byts, remap", [ (fake_hidpp.responses_key, 1, 0x0051, 0x02, 0x0002, 0x01, 0, "Mouse Button: 2", "Cntrl+", "02000201", "01000400"), (fake_hidpp.responses_key, 2, 0x0052, 0x01, 0x0001, 0x00, 1, "Key: 1", "", "01000100", "02005004"), (fake_hidpp.responses_key, 3, 0x0053, 0x02, 0x0001, 0x00, 1, "Mouse Button: 1", "", "02000100", "7FFFFFFF"), ], ) def test_remappable_action(r, index, cid, actionId, remapped, mask, status, action, modifiers, byts, remap, mocker): if int(remap, 16) == special_keys.KEYS_Default: responses = r + [ fake_hidpp.Response("040000", 0x0000, "1C00"), fake_hidpp.Response("00", 0x450, f"{cid:04X}" + "FF"), ] else: responses = r + [ fake_hidpp.Response("040000", 0x0000, "1C00"), fake_hidpp.Response("00", 0x440, f"{cid:04X}" + "FF" + remap), ] device = fake_hidpp.Device( "KEY", responses=responses, feature=hidpp20_constants.SupportedFeature.REPROG_CONTROLS_V4, offset=5 ) key = hidpp20.PersistentRemappableAction(device, index, cid, actionId, remapped, mask, status) spy_request = mocker.spy(device, "request") assert key._device == device assert key.index == index assert key._cid == cid assert key.actionId == actionId assert key.remapped == remapped assert key._modifierMask == mask assert key.cidStatus == status assert key.key == special_keys.CONTROL[cid] assert key.actionType == special_keys.ACTIONID[actionId] assert key.action == action assert key.modifiers == modifiers assert key.data_bytes.hex().upper() == byts key.remap(bytes.fromhex(remap)) assert key.data_bytes.hex().upper() == (byts if int(remap, 16) == special_keys.KEYS_Default else remap) if int(remap, 16) != special_keys.KEYS_Default: fake_hidpp.match_requests(1, responses, spy_request.call_args_list) # KeysArray methods tested in KeysArrayV4 # KeysArrayV2 not tested as there is no documentation @pytest.mark.parametrize( "device, index", [(device_zerofeatures, -1), (device_zerofeatures, 5), (device_standard, -1), (device_standard, 6)] ) def test_KeysArrayV4_index_error(device, index): keysarray = hidpp20.KeysArrayV4(device, 5) with pytest.raises(IndexError): keysarray[index] with pytest.raises(IndexError): keysarray._query_key(index) @pytest.mark.parametrize("device, index, top, cid", [(device_standard, 0, 2, 0x0011), (device_standard, 4, 5, 0x0003)]) def test_KeysArrayV4_query_key(device, index, top, cid): keysarray = hidpp20.KeysArrayV4(device, 5) keysarray._query_key(index) assert keysarray.keys[index]._cid == cid assert len(keysarray[index:top]) == top - index assert len(list(keysarray)) == 5 @pytest.mark.parametrize( "device, count, index, cid, tid, flags, pos, group, gmask", [ (device_standard, 4, 0, 0x0011, 0x0012, 0xCDAB, 1, 2, 3), (device_standard, 6, 1, 0x0111, 0x0022, 0xCDAB, 1, 2, 3), (device_standard, 8, 3, 0x0311, 0x0032, 0xCDAB, 1, 2, 4), ], ) def test_KeysArrayV4__getitem(device, count, index, cid, tid, flags, pos, group, gmask): keysarray = hidpp20.KeysArrayV4(device, count) result = keysarray[index] assert result._device == device assert result.index == index assert result._cid == cid assert result._tid == tid assert result._flags == flags assert result.pos == pos assert result.group == group assert result._gmask == gmask @pytest.mark.parametrize( "key, index", [(special_keys.CONTROL.Volume_Up, 2), (special_keys.CONTROL.Mute, 4), (special_keys.CONTROL.Next, None)] ) def test_KeysArrayV4_index(key, index): keysarray = hidpp20.KeysArrayV4(device_standard, 7) result = keysarray.index(key) assert result == index device_key = fake_hidpp.Device( "KEY", responses=fake_hidpp.responses_key, feature=hidpp20_constants.SupportedFeature.REPROG_CONTROLS_V4, offset=5 ) @pytest.mark.parametrize( "key, expected_index, expected_mapped_to, expected_remappable_to", [ ( special_keys.CONTROL.Left_Button, 0, common.NamedInt(0x50, "Left Click"), [common.NamedInt(0x50, "Left Click"), common.NamedInt(0x51, "Right Click")], ), ( special_keys.CONTROL.Right_Button, 1, common.NamedInt(0x51, "Right Click"), [common.NamedInt(0x51, "Right Click"), common.NamedInt(0x50, "Left Click")], ), (special_keys.CONTROL.Middle_Button, 2, common.NamedInt(0x50, "Left Click"), None), (special_keys.CONTROL.Back_Button, 3, common.NamedInt(0x53, "Mouse Back Button"), None), (special_keys.CONTROL.Forward_Button, 4, common.NamedInt(0x56, "Mouse Forward Button"), None), (special_keys.CONTROL.Mouse_Gesture_Button, 5, common.NamedInt(0xC3, "Gesture Button Navigation"), None), (special_keys.CONTROL.Smart_Shift, 6, common.NamedInt(0x50, "Left Click"), None), (special_keys.CONTROL.Virtual_Gesture_Button, 7, common.NamedInt(0x51, "Right Click"), None), ], ) def test_KeysArrayV4_key(key, expected_index, expected_mapped_to, expected_remappable_to): device_key._keys = _hidpp20.get_keys(device_key) device_key._keys._ensure_all_keys_queried() index = device_key._keys.index(key) mapped_to = device_key._keys[expected_index].mapped_to remappable_to = device_key._keys[expected_index].remappable_to assert index == expected_index assert mapped_to == expected_mapped_to if expected_remappable_to is not None: assert list(remappable_to) == expected_remappable_to @pytest.mark.parametrize( "device, index", [(device_zerofeatures, -1), (device_zerofeatures, 5), (device_standard, -1), (device_standard, 6)] ) def test_KeysArrayPersistent_index_error(device, index): keysarray = hidpp20.KeysArrayPersistent(device, 5) with pytest.raises(IndexError): keysarray[index] with pytest.raises(IndexError): keysarray._query_key(index) @pytest.mark.parametrize( "responses, key, index, mapped_to, capabilities", [ (fake_hidpp.responses_remap, special_keys.CONTROL.Left_Button, 0, common.NamedInt(0x01, "Mouse Button Left"), 0x41), (fake_hidpp.responses_remap, special_keys.CONTROL.Right_Button, 1, common.NamedInt(0x01, "Mouse Button Left"), 0x41), (fake_hidpp.responses_remap, special_keys.CONTROL.Middle_Button, 2, common.NamedInt(0x51, "DOWN"), 0x41), ], ) def test_KeysArrayPersistent_key(responses, key, index, mapped_to, capabilities): device = fake_hidpp.Device( "REMAP", responses=responses, feature=hidpp20_constants.SupportedFeature.PERSISTENT_REMAPPABLE_ACTION ) device._remap_keys = _hidpp20.get_remap_keys(device) device._remap_keys._ensure_all_keys_queried() assert device._remap_keys.index(key) == index assert device._remap_keys[index].remapped == mapped_to assert device._remap_keys.capabilities == capabilities @pytest.mark.parametrize( "id, length, minimum, maximum, widget, min, max, wid, string", [ ("left", 1, 5, 8, "Widget", 5, 8, "Widget", "left"), ("left", 1, None, None, None, 0, 255, "Scale", "left"), ], ) def test_SubParam(id, length, minimum, maximum, widget, min, max, wid, string): subparam = hidpp20.SubParam(id, length, minimum, maximum, widget) assert subparam.id == id assert subparam.length == length assert subparam.minimum == min assert subparam.maximum == max assert subparam.widget == wid assert subparam.__str__() == string assert subparam.__repr__() == string @pytest.mark.parametrize( "device, low, high, next_index, next_diversion_index, name, cbe, si, sdi, eom, dom", [ (device_standard, 0x01, 0x01, 5, 10, GestureId.TAP_1_FINGER, True, 5, None, (0, 0x20), (None, None)), (device_standard, 0x03, 0x02, 6, 11, GestureId.TAP_3_FINGER, False, None, 11, (None, None), (1, 0x08)), ], ) def test_gesture(device, low, high, next_index, next_diversion_index, name, cbe, si, sdi, eom, dom): gesture = hidpp20.Gesture(device, low, high, next_index, next_diversion_index) assert gesture._device == device assert gesture.id == low assert gesture.gesture == name assert gesture.can_be_enabled == cbe assert gesture.can_be_enabled == cbe assert gesture.index == si assert gesture.diversion_index == sdi assert gesture.enable_offset_mask() == eom assert gesture.diversion_offset_mask() == dom assert gesture.as_int() == low assert int(gesture) == low @pytest.mark.parametrize( "responses, gest, enabled, diverted, set_result, unset_result, divert_result, undivert_result", [ (fake_hidpp.responses_gestures, 20, None, None, None, None, None, None), (fake_hidpp.responses_gestures, 1, True, False, "01", "00", "01", "00"), (fake_hidpp.responses_gestures, 45, False, None, "01", "00", None, None), ], ) def test_Gesture_set(responses, gest, enabled, diverted, set_result, unset_result, divert_result, undivert_result): device = fake_hidpp.Device("GESTURE", responses=responses, feature=hidpp20_constants.SupportedFeature.GESTURE_2) gestures = _hidpp20.get_gestures(device) gesture = gestures.gesture(gest) assert gesture.enabled() == enabled assert gesture.diverted() == diverted assert gesture.set(True) == (bytes.fromhex(set_result) if set_result is not None else None) assert gesture.set(False) == (bytes.fromhex(unset_result) if unset_result is not None else None) assert gesture.divert(True) == (bytes.fromhex(divert_result) if divert_result is not None else None) assert gesture.divert(False) == (bytes.fromhex(undivert_result) if undivert_result is not None else None) @pytest.mark.parametrize( "responses, prm, id, index, size, value, default_value, write1, write2", [ (fake_hidpp.responses_gestures, 4, hidpp20_constants.ParamId.SCALE_FACTOR, 0, 2, 256, 256, "0080", "0180"), ], ) def test_param(responses, prm, id, index, size, value, default_value, write1, write2): device = fake_hidpp.Device("GESTURE", responses=responses, feature=hidpp20_constants.SupportedFeature.GESTURE_2) gestures = _hidpp20.get_gestures(device) param = gestures.param(prm) assert param.id == id assert param.index == index assert param.size == size assert param.value == value assert param.default_value == default_value assert param.param == id assert int(param) == id assert param.write(bytes.fromhex(write1)).hex().upper() == f"{index:02X}" + write1 + "FF" assert param.write(bytes.fromhex(write2)).hex().upper() == f"{index:02X}" + write2 + "FF" @pytest.mark.parametrize( "responses, id, s, byte_count, value, string", [ (fake_hidpp.responses_gestures, 1, "DVI field width", 1, 8, "[DVI field width=8]"), (fake_hidpp.responses_gestures, 2, "field widths", 1, 8, "[field widths=8]"), (fake_hidpp.responses_gestures, 3, "period unit", 2, 2048, "[period unit=2048]"), ], ) def test_Spec(responses, id, s, byte_count, value, string): device = fake_hidpp.Device("GESTURE", responses=responses, feature=hidpp20_constants.SupportedFeature.GESTURE_2) gestures = _hidpp20.get_gestures(device) spec = gestures.specs[id] assert spec.id == id assert spec.spec == s assert spec.byte_count == byte_count assert spec.value == value assert repr(spec) == string def test_Gestures(): device = fake_hidpp.Device( "GESTURES", responses=fake_hidpp.responses_gestures, feature=hidpp20_constants.SupportedFeature.GESTURE_2 ) gestures = _hidpp20.get_gestures(device) assert gestures assert len(gestures.gestures) == 17 assert gestures.gesture(20) == gestures.gestures[20] assert gestures.gesture_enabled(20) is None assert gestures.gesture_enabled(1) is True assert gestures.gesture_enabled(45) is False assert gestures.enable_gesture(20) is None assert gestures.enable_gesture(45) == bytes.fromhex("01") assert gestures.disable_gesture(20) is None assert gestures.disable_gesture(45) == bytes.fromhex("00") assert len(gestures.params) == 1 assert gestures.param(4) == gestures.params[4] assert gestures.get_param(4) == 256 assert gestures.set_param(4, 128) is None assert len(gestures.specs) == 5 assert gestures.specs[2].value == 8 assert gestures.specs[4].value == 4 responses_backlight = [ fake_hidpp.Response("010118000001020003000400", 0x0400), fake_hidpp.Response("0101FF00020003000400", 0x0410, "0101FF00020003000400"), ] device_backlight = fake_hidpp.Device( "BACKLIGHT", responses=responses_backlight, feature=hidpp20_constants.SupportedFeature.BACKLIGHT2 ) def test_Backlight(): backlight = _hidpp20.get_backlight(device_backlight) result = backlight.write() assert backlight assert backlight.auto_supported assert backlight.temp_supported assert not backlight.perm_supported assert backlight.dho == 0x0002 assert backlight.dhi == 0x0003 assert backlight.dpow == 0x0004 assert result is not None @pytest.mark.parametrize( "hex, ID, color, speed, period, intensity, ramp, form", [ ("FFFFFFFFFFFFFFFFFFFFFF", None, None, None, None, None, None, None), ("0000000000000000000000", common.NamedInt(0x0, "Disabled"), None, None, None, None, None, None), ("0120304010000000000000", common.NamedInt(0x1, "Static"), 0x203040, None, None, None, 0x10, None), ("0220304010000000000000", common.NamedInt(0x2, "Pulse"), 0x203040, 0x10, None, None, None, None), ("0800000000000000000000", common.NamedInt(0x8, "Boot"), None, None, None, None, None, None), ("0300000000005000000000", common.NamedInt(0x3, "Cycle"), None, None, 0x5000, 0x00, None, None), ("0A20304010005020000000", common.NamedInt(0xA, "Breathe"), 0x203040, None, 0x1000, 0x20, None, 0x50), ("0B20304000100000000000", common.NamedInt(0xB, "Ripple"), 0x203040, None, 0x1000, None, None, None), ("0A01020300500407000000", common.NamedInt(0xA, "Breathe"), 0x010203, None, 0x0050, 0x07, None, 0x04), ], ) def test_LEDEffectSetting(hex, ID, color, speed, period, intensity, ramp, form): byt = bytes.fromhex(hex) setting = hidpp20.LEDEffectSetting.from_bytes(byt) assert setting.ID == ID if ID is None: assert setting.bytes == byt else: assert getattr(setting, "color", None) == color assert getattr(setting, "speed", None) == speed assert getattr(setting, "period", None) == period assert getattr(setting, "intensity", None) == intensity assert getattr(setting, "ramp", None) == ramp assert getattr(setting, "form", None) == form assert setting.to_bytes() == byt assert yaml.safe_load(yaml.dump(setting)) == setting assert yaml.safe_load(str(setting)) == setting @pytest.mark.parametrize( "feature, function, response, ID, capabilities, period", [ [ hidpp20_constants.SupportedFeature.COLOR_LED_EFFECTS, 0x20, fake_hidpp.Response("0102000300040005", 0x0420, "010200"), 3, 4, 5, ], [ hidpp20_constants.SupportedFeature.COLOR_LED_EFFECTS, 0x20, fake_hidpp.Response("0102000700080009", 0x0420, "010200"), 7, 8, 9, ], ], ) def test_LEDEffectInfo(feature, function, response, ID, capabilities, period): device = fake_hidpp.Device(feature=feature, responses=[response]) info = hidpp20.LEDEffectInfo(feature, function, device, 1, 2) assert info.zindex == 1 assert info.index == 2 assert info.ID == ID assert info.capabilities == capabilities assert info.period == period @pytest.mark.parametrize( "feature, function, offset, effect_function, responses, index, location, count, id_1", [ [hidpp20_constants.SupportedFeature.COLOR_LED_EFFECTS, 0x10, 0, 0x20, fake_hidpp.zone_responses_1, 0, 1, 2, 0xB], [hidpp20_constants.SupportedFeature.RGB_EFFECTS, 0x00, 1, 0x00, fake_hidpp.zone_responses_2, 0, 1, 2, 2], ], ) def test_LEDZoneInfo(feature, function, offset, effect_function, responses, index, location, count, id_1): device = fake_hidpp.Device(feature=feature, responses=responses, offset=0x07) zone = hidpp20.LEDZoneInfo(feature, function, offset, effect_function, device, index) assert zone.index == index assert zone.location == location assert zone.count == count assert len(zone.effects) == count assert zone.effects[1].ID == id_1 @pytest.mark.parametrize( "responses, setting, expected_command", [ [fake_hidpp.zone_responses_1, hidpp20.LEDEffectSetting(ID=0), None], [fake_hidpp.zone_responses_1, hidpp20.LEDEffectSetting(ID=3, period=0x20, intensity=0x50), "000000000000000020500000"], [ fake_hidpp.zone_responses_1, hidpp20.LEDEffectSetting(ID=0xB, color=0x808080, period=0x20), "000180808000002000000000", ], ], ) def test_LEDZoneInfo_to_command(responses, setting, expected_command): device = fake_hidpp.Device(feature=hidpp20_constants.SupportedFeature.COLOR_LED_EFFECTS, responses=responses, offset=0x07) zone = hidpp20.LEDZoneInfo(hidpp20_constants.SupportedFeature.COLOR_LED_EFFECTS, 0x10, 0, 0x20, device, 0) command = zone.to_command(setting) assert command == (bytes.fromhex(expected_command) if expected_command is not None else None) @pytest.mark.parametrize( "feature, cls, responses, readable, count, count_0", [ [ hidpp20_constants.SupportedFeature.COLOR_LED_EFFECTS, hidpp20.LEDEffectsInfo, fake_hidpp.effects_responses_1, 1, 1, 2, ], [hidpp20_constants.SupportedFeature.RGB_EFFECTS, hidpp20.RGBEffectsInfo, fake_hidpp.effects_responses_2, 1, 1, 2], ], ) def test_LED_RGB_EffectsInfo(feature, cls, responses, readable, count, count_0): device = fake_hidpp.Device(feature=feature, responses=responses, offset=0x07) effects = cls(device) assert effects.readable == readable assert effects.count == count assert effects.zones[0].count == count_0 @pytest.mark.parametrize( "hex, behavior, sector, address, typ, val, modifiers, data, byt", [ ("05010203", 0x0, 0x501, 0x0203, None, None, None, None, None), ("15020304", 0x1, 0x502, 0x0304, None, None, None, None, None), ("8000FFFF", 0x8, None, None, 0x00, None, None, None, None), ("80010102", 0x8, None, None, 0x01, 0x0102, None, None, None), ("80020454", 0x8, None, None, 0x02, 0x54, 0x04, None, None), ("80030454", 0x8, None, None, 0x03, 0x0454, None, None, None), ("900AFF01", 0x9, None, None, None, 0x0A, None, 0x01, None), ("709090A0", 0x7, None, None, None, None, None, None, b"\x70\x90\x90\xa0"), ], ) def test_button_bytes(hex, behavior, sector, address, typ, val, modifiers, data, byt): button = hidpp20.Button.from_bytes(bytes.fromhex(hex)) assert getattr(button, "behavior", None) == behavior assert getattr(button, "sector", None) == sector assert getattr(button, "address", None) == address assert getattr(button, "type", None) == typ assert getattr(button, "value", None) == val assert getattr(button, "modifiers", None) == modifiers assert getattr(button, "data", None) == data assert getattr(button, "bytes", None) == byt assert button.to_bytes().hex().upper() == hex assert yaml.safe_load(yaml.dump(button)).to_bytes().hex().upper() == hex hex1 = ( "01010290018003000700140028FFFFFF" "FFFF0000000000000000000000000000" "8000FFFF900AFF00800204548000FFFF" "900AFF00800204548000FFFF900AFF00" "800204548000FFFF900AFF0080020454" "8000FFFF900AFF00800204548000FFFF" "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" "54004500370000000000000000000000" "00000000000000000000000000000000" "00000000000000000000000000000000" "0A01020300500407000000FFFFFFFFFF" "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" "FFFFFFFFFFFFFFFFFFFFFFFFFF7C81" ) hex2 = ( "01010290018003000700140028FFFFFF" "FFFF0000000000000000000000000000" "8000FFFF900AFF00800204548000FFFF" "900AFF00800204548000FFFF900AFF00" "800204548000FFFF900AFF0080020454" "8000FFFF900AFF00800204548000FFFF" "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" "0A01020300500407000000FFFFFFFFFF" "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" "FFFFFFFFFFFFFFFFFFFFFFFFFF27C9" ) hex3 = ( "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" "FFFFFFFFFFFFFFFFFFFFFFFFFF2307" ) @pytest.mark.parametrize( "hex, name, sector, enabled, buttons, gbuttons, resolutions, button, lighting", [ (hex1, "TE7", 2, 1, 16, 0, [0x0190, 0x0380, 0x0700, 0x1400, 0x2800], "8000FFFF", "0A01020300500407000000"), (hex2, "", 2, 1, 16, 0, [0x0190, 0x0380, 0x0700, 0x1400, 0x2800], "8000FFFF", "0A01020300500407000000"), (hex3, "", 2, 1, 16, 0, [0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF], "FFFFFFFF", "FFFFFFFFFFFFFFFFFFFFFF"), ], ) def test_OnboardProfile_bytes(hex, name, sector, enabled, buttons, gbuttons, resolutions, button, lighting): profile = hidpp20.OnboardProfile.from_bytes(sector, enabled, buttons, gbuttons, bytes.fromhex(hex)) assert profile.name == name assert profile.sector == sector assert profile.resolutions == resolutions assert profile.buttons[0].to_bytes().hex().upper() == button assert profile.lighting[0].to_bytes().hex().upper() == lighting assert profile.to_bytes(len(hex) // 2).hex().upper() == hex assert yaml.safe_load(yaml.dump(profile)).to_bytes(len(hex) // 2).hex().upper() == hex @pytest.mark.parametrize( "responses, name, count, buttons, gbuttons, sectors, size", [ (fake_hidpp.responses_profiles, "ONB", 1, 2, 2, 1, 254), (fake_hidpp.responses_profiles_rom, "ONB", 1, 2, 2, 1, 254), (fake_hidpp.responses_profiles_rom_2, "ONB", 1, 2, 2, 1, 254), ], ) def test_OnboardProfiles_device(responses, name, count, buttons, gbuttons, sectors, size): device = fake_hidpp.Device( name, True, 4.5, responses=responses, feature=hidpp20_constants.SupportedFeature.ONBOARD_PROFILES, offset=0x9 ) device._profiles = None profiles = _hidpp20.get_profiles(device) assert profiles assert profiles.version == hidpp20.OnboardProfilesVersion assert profiles.name == name assert profiles.count == count assert profiles.buttons == buttons assert profiles.gbuttons == gbuttons assert profiles.sectors == sectors assert profiles.size == size assert len(profiles.profiles) == count assert yaml.safe_load(yaml.dump(profiles)).to_bytes().hex() == profiles.to_bytes().hex() Solaar-1.1.14/tests/logitech_receiver/test_hidpp20_simple.py000066400000000000000000000343331473525213700241120ustar00rootroot00000000000000## Copyright (C) 2024 Solaar Contributors https://pwr-solaar.github.io/Solaar/ ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import pytest from logitech_receiver import common from logitech_receiver import hidpp20 from logitech_receiver import hidpp20_constants from logitech_receiver.hidpp20_constants import SupportedFeature from . import fake_hidpp _hidpp20 = hidpp20.Hidpp20() def test_get_firmware(): responses = [ fake_hidpp.Response("02FFFF", 0x0400), fake_hidpp.Response("01414243030401000101000102030405", 0x0410, "00"), fake_hidpp.Response("02414243030401000101000102030405", 0x0410, "01"), ] device = fake_hidpp.Device(responses=responses, feature=SupportedFeature.DEVICE_FW_VERSION) result = _hidpp20.get_firmware(device) assert len(result) == 2 assert isinstance(result[0], common.FirmwareInfo) assert isinstance(result[1], common.FirmwareInfo) def test_get_ids(): responses = [fake_hidpp.Response("FF12345678000D123456789ABC", 0x0400)] device = fake_hidpp.Device(responses=responses, feature=SupportedFeature.DEVICE_FW_VERSION) unitId, modelId, tid_map = _hidpp20.get_ids(device) assert unitId == "12345678" assert modelId == "123456789ABC" assert tid_map == {"btid": "1234", "wpid": "5678", "usbid": "9ABC"} def test_get_kind(): responses = [fake_hidpp.Response("00", 0x0420)] device = fake_hidpp.Device(responses=responses, feature=SupportedFeature.DEVICE_NAME) result = _hidpp20.get_kind(device) assert result == "keyboard" assert result == 1 def test_get_name(): responses = [ fake_hidpp.Response("12", 0x0400), fake_hidpp.Response("4142434445464748494A4B4C4D4E4F", 0x0410, "00"), fake_hidpp.Response("505152530000000000000000000000", 0x0410, "0F"), ] device = fake_hidpp.Device(responses=responses, feature=SupportedFeature.DEVICE_NAME) result = _hidpp20.get_name(device) assert result == "ABCDEFGHIJKLMNOPQR" def test_get_friendly_name(): responses = [ fake_hidpp.Response("12", 0x0400), fake_hidpp.Response("004142434445464748494A4B4C4D4E", 0x0410, "00"), fake_hidpp.Response("0E4F50515253000000000000000000", 0x0410, "0E"), ] device = fake_hidpp.Device(responses=responses, feature=SupportedFeature.DEVICE_FRIENDLY_NAME) result = _hidpp20.get_friendly_name(device) assert result == "ABCDEFGHIJKLMNOPQR" def test_get_battery_status(): responses = [fake_hidpp.Response("502000FFFF", 0x0400)] device = fake_hidpp.Device(responses=responses, feature=SupportedFeature.BATTERY_STATUS) feature, battery = _hidpp20.get_battery_status(device) assert feature == SupportedFeature.BATTERY_STATUS assert battery.level == 80 assert battery.next_level == 32 assert battery.status == common.BatteryStatus.DISCHARGING def test_get_battery_voltage(): responses = [fake_hidpp.Response("1000FFFFFF", 0x0400)] device = fake_hidpp.Device(responses=responses, feature=SupportedFeature.BATTERY_VOLTAGE) feature, battery = _hidpp20.get_battery_voltage(device) assert feature == SupportedFeature.BATTERY_VOLTAGE assert battery.level == 90 assert battery.status == common.BatteryStatus.RECHARGING assert battery.voltage == 0x1000 def test_get_battery_unified(): responses = [fake_hidpp.Response("500100FFFF", 0x0410)] device = fake_hidpp.Device(responses=responses, feature=SupportedFeature.UNIFIED_BATTERY) feature, battery = _hidpp20.get_battery_unified(device) assert feature == SupportedFeature.UNIFIED_BATTERY assert battery.level == 80 assert battery.status == common.BatteryStatus.DISCHARGING def test_get_adc_measurement(): responses = [fake_hidpp.Response("100003", 0x0400)] device = fake_hidpp.Device(responses=responses, feature=SupportedFeature.ADC_MEASUREMENT) feature, battery = _hidpp20.get_adc_measurement(device) assert feature == SupportedFeature.ADC_MEASUREMENT assert battery.level == 90 assert battery.status == common.BatteryStatus.RECHARGING assert battery.voltage == 0x1000 def test_get_battery(): responses = [fake_hidpp.Response("502000FFFF", 0x0400)] device = fake_hidpp.Device(responses=responses, feature=SupportedFeature.BATTERY_STATUS) feature, battery = _hidpp20.get_battery(device, SupportedFeature.BATTERY_STATUS) assert feature == SupportedFeature.BATTERY_STATUS assert battery.level == 80 assert battery.next_level == 32 assert battery.status == common.BatteryStatus.DISCHARGING def test_get_battery_none(): responses = [ fake_hidpp.Response(None, 0x0000, f"{int(SupportedFeature.BATTERY_STATUS):0>4X}"), fake_hidpp.Response(None, 0x0000, f"{int(SupportedFeature.BATTERY_VOLTAGE):0>4X}"), fake_hidpp.Response("500100ffff", 0x0410), ] device = fake_hidpp.Device(responses=responses, feature=SupportedFeature.UNIFIED_BATTERY) feature, battery = _hidpp20.get_battery(device, None) assert feature == SupportedFeature.UNIFIED_BATTERY assert battery.level == 80 assert battery.status == common.BatteryStatus.DISCHARGING # get_keys is in test_hidpp20_complex # get_remap_keys is in test_hidpp20_complex # TODO get_gestures is complex # get_backlight is in test_hidpp20_complex # get_profiles is in test_hidpp20_complex def test_get_mouse_pointer_info(): responses = [fake_hidpp.Response("01000A", 0x0400)] device = fake_hidpp.Device(responses=responses, feature=SupportedFeature.MOUSE_POINTER) result = _hidpp20.get_mouse_pointer_info(device) assert result == { "dpi": 0x100, "acceleration": "med", "suggest_os_ballistics": False, "suggest_vertical_orientation": True, } def test_get_vertical_scrolling_info(): responses = [fake_hidpp.Response("01080C", 0x0400)] device = fake_hidpp.Device(responses=responses, feature=SupportedFeature.VERTICAL_SCROLLING) result = _hidpp20.get_vertical_scrolling_info(device) assert result == {"roller": "standard", "ratchet": 8, "lines": 12} def test_get_hi_res_scrolling_info(): responses = [fake_hidpp.Response("0102", 0x0400)] device = fake_hidpp.Device(responses=responses, feature=SupportedFeature.HI_RES_SCROLLING) mode, resolution = _hidpp20.get_hi_res_scrolling_info(device) assert mode == 1 assert resolution == 2 def test_get_pointer_speed_info(): responses = [fake_hidpp.Response("0102", 0x0400)] device = fake_hidpp.Device(responses=responses, feature=SupportedFeature.POINTER_SPEED) result = _hidpp20.get_pointer_speed_info(device) assert result == 0x0102 / 256 def test_get_lowres_wheel_status(): responses = [fake_hidpp.Response("01", 0x0400)] device = fake_hidpp.Device(responses=responses, feature=SupportedFeature.LOWRES_WHEEL) result = _hidpp20.get_lowres_wheel_status(device) assert result == "HID++" def test_get_hires_wheel(): responses = [ fake_hidpp.Response("010C", 0x0400), fake_hidpp.Response("05FF", 0x0410), fake_hidpp.Response("03FF", 0x0430), ] device = fake_hidpp.Device(responses=responses, feature=SupportedFeature.HIRES_WHEEL) multi, has_invert, has_ratchet, inv, res, target, ratchet = _hidpp20.get_hires_wheel(device) assert multi == 1 assert has_invert is True assert has_ratchet is True assert inv is True assert res is False assert target is True assert ratchet is True def test_get_new_fn_inversion(): responses = [fake_hidpp.Response("0300", 0x0400)] device = fake_hidpp.Device(responses=responses, feature=SupportedFeature.NEW_FN_INVERSION) result = _hidpp20.get_new_fn_inversion(device) assert result == (True, False) @pytest.fixture def mock_gethostname(mocker): mocker.patch("socket.gethostname", return_value="ABCDEFG.foo.org") @pytest.mark.parametrize( "responses, expected_result", [ ([fake_hidpp.Response(None, 0x0400)], {}), ([fake_hidpp.Response("02000000", 0x0400)], {}), ( [ fake_hidpp.Response("03000200", 0x0400), fake_hidpp.Response("FF01FFFF05FFFF", 0x0410, "00"), fake_hidpp.Response("0000414243444500FFFFFFFFFF", 0x0430, "0000"), fake_hidpp.Response("FF01FFFF10FFFF", 0x0410, "01"), fake_hidpp.Response("01004142434445464748494A4B4C4D", 0x0430, "0100"), fake_hidpp.Response("01134E4F5000FFFFFFFFFFFFFFFFFF", 0x0430, "010E"), fake_hidpp.Response("000000000008", 0x0410, "00"), fake_hidpp.Response("0208", 0x0440, "000041424344454647"), ], {0: (True, "ABCDEFG"), 1: (True, "ABCDEFGHIJKLMNO")}, ), ], ) def test_get_host_names(responses, expected_result, mock_gethostname): device = fake_hidpp.Device(responses=responses, feature=SupportedFeature.HOSTS_INFO) result = _hidpp20.get_host_names(device) assert result == expected_result @pytest.mark.parametrize( "responses, expected_result", [ ([fake_hidpp.Response(None, 0x0400)], None), ( [ fake_hidpp.Response("03000002", 0x0400), fake_hidpp.Response("000000000008", 0x0410, "02"), fake_hidpp.Response("020E", 0x0440, "02004142434445464748494A4B4C4D4E"), ], True, ), ( [ fake_hidpp.Response("03000002", 0x0400), fake_hidpp.Response("000000000014", 0x0410, "02"), fake_hidpp.Response("020E", 0x0440, "02004142434445464748494A4B4C4D4E"), fake_hidpp.Response("0214", 0x0440, "020E4F505152535455565758"), ], True, ), ], ) def test_set_host_name(responses, expected_result): device = fake_hidpp.Device(responses=responses, feature=SupportedFeature.HOSTS_INFO) result = _hidpp20.set_host_name(device, "ABCDEFGHIJKLMNOPQRSTUVWX") assert result == expected_result def test_get_onboard_mode(): responses = [fake_hidpp.Response("03FFFFFFFF", 0x0420)] device = fake_hidpp.Device(responses=responses, feature=SupportedFeature.ONBOARD_PROFILES) result = _hidpp20.get_onboard_mode(device) assert result == 0x3 def test_set_onboard_mode(): responses = [fake_hidpp.Response("03FFFFFFFF", 0x0410, "03")] device = fake_hidpp.Device(responses=responses, feature=SupportedFeature.ONBOARD_PROFILES) res = _hidpp20.set_onboard_mode(device, 0x3) assert res is not None @pytest.mark.parametrize( "responses, expected_result", [ ([fake_hidpp.Response("03FFFF", 0x0420)], "1ms"), ( [ fake_hidpp.Response(None, 0x0000, f"{int(SupportedFeature.REPORT_RATE):04X}"), fake_hidpp.Response("04FFFF", 0x0420), ], "500us", ), ], ) def test_get_polling_rate( responses, expected_result, ): device = fake_hidpp.Device(responses=responses, feature=SupportedFeature.EXTENDED_ADJUSTABLE_REPORT_RATE) result = _hidpp20.get_polling_rate(device) assert result == expected_result def test_get_remaining_pairing(): responses = [fake_hidpp.Response("03FFFF", 0x0400)] device = fake_hidpp.Device(responses=responses, feature=SupportedFeature.REMAINING_PAIRING) result = _hidpp20.get_remaining_pairing(device) assert result == 0x03 def test_config_change(): responses = [fake_hidpp.Response("03FFFF", 0x0410, "02")] device = fake_hidpp.Device(responses=responses, feature=SupportedFeature.CONFIG_CHANGE) result = _hidpp20.config_change(device, 0x2) assert result == bytes.fromhex("03FFFF") def test_decipher_battery_status(): report = b"\x50\x20\x00\xff\xff" feature, battery = hidpp20.decipher_battery_status(report) assert feature == SupportedFeature.BATTERY_STATUS assert battery.level == 80 assert battery.next_level == 32 assert battery.status == common.BatteryStatus.DISCHARGING def test_decipher_battery_voltage(): report = b"\x10\x00\xff\xff\xff" feature, battery = hidpp20.decipher_battery_voltage(report) assert feature == SupportedFeature.BATTERY_VOLTAGE assert battery.level == 90 assert battery.status == common.BatteryStatus.RECHARGING assert battery.voltage == 0x1000 def test_decipher_battery_unified(): report = b"\x50\x01\x00\xff\xff" feature, battery = hidpp20.decipher_battery_unified(report) assert feature == SupportedFeature.UNIFIED_BATTERY assert battery.level == 80 assert battery.status == common.BatteryStatus.DISCHARGING def test_decipher_adc_measurement(): report = b"\x10\x00\x03" feature, battery = hidpp20.decipher_adc_measurement(report) assert feature == SupportedFeature.ADC_MEASUREMENT assert battery.level == 90 assert battery.status == common.BatteryStatus.RECHARGING assert battery.voltage == 0x1000 @pytest.mark.parametrize( "code, expected_flags", [ (0x01, ["unknown:000001"]), (0x0F, ["unknown:00000F"]), (0xF0, ["internal", "hidden", "obsolete", "unknown:000010"]), (0x20, ["internal"]), (0x33, ["internal", "unknown:000013"]), (0x3F, ["internal", "unknown:00001F"]), (0x40, ["hidden"]), (0x50, ["hidden", "unknown:000010"]), (0x5F, ["hidden", "unknown:00001F"]), (0x7F, ["internal", "hidden", "unknown:00001F"]), (0x80, ["obsolete"]), (0xA0, ["internal", "obsolete"]), (0xE0, ["internal", "hidden", "obsolete"]), (0xFF, ["internal", "hidden", "obsolete", "unknown:00001F"]), ], ) def test_feature_flag_names(code, expected_flags): flags = common.flag_names(hidpp20_constants.FeatureFlag, code) assert list(flags) == expected_flags Solaar-1.1.14/tests/logitech_receiver/test_notifications.py000066400000000000000000000034401473525213700241370ustar00rootroot00000000000000import pytest from logitech_receiver import notifications from logitech_receiver.base import HIDPPNotification from logitech_receiver.common import Notification from logitech_receiver.hidpp10_constants import BoltPairingError from logitech_receiver.hidpp10_constants import PairingError from logitech_receiver.hidpp10_constants import Registers from logitech_receiver.receiver import Receiver class MockLowLevelInterface: def open_path(self, path): pass def find_paired_node_wpid(self, receiver_path: str, index: int): pass def ping(self, handle, number, long_message=False): pass def request(self, handle, devnumber, request_id, *params, **kwargs): pass @pytest.mark.parametrize( "sub_id, notification_data, expected_error, expected_new_device", [ (Registers.DISCOVERY_STATUS_NOTIFICATION, b"\x01", BoltPairingError.DEVICE_TIMEOUT, None), (Registers.PAIRING_STATUS_NOTIFICATION, b"\x02", BoltPairingError.FAILED, None), (Notification.PAIRING_LOCK, b"\x01", PairingError.DEVICE_TIMEOUT, None), (Notification.PAIRING_LOCK, b"\x02", PairingError.DEVICE_NOT_SUPPORTED, None), (Notification.PAIRING_LOCK, b"\x03", PairingError.TOO_MANY_DEVICES, None), (Notification.PAIRING_LOCK, b"\x06", PairingError.SEQUENCE_TIMEOUT, None), ], ) def test_process_receiver_notification(sub_id, notification_data, expected_error, expected_new_device): receiver: Receiver = Receiver(MockLowLevelInterface(), None, {}, True, None, None) notification = HIDPPNotification(0, 0, sub_id, 0, notification_data) result = notifications._process_receiver_notification(receiver, notification) assert result assert receiver.pairing.error == expected_error assert receiver.pairing.new_device is expected_new_device Solaar-1.1.14/tests/logitech_receiver/test_receiver.py000066400000000000000000000156461473525213700231050ustar00rootroot00000000000000from dataclasses import dataclass from functools import partial from unittest import mock import pytest from logitech_receiver import base from logitech_receiver import common from logitech_receiver import exceptions from logitech_receiver import receiver from . import fake_hidpp class LowLevelInterfaceFake: def __init__(self, responses=None): self.responses = responses def open_path(self, path): return fake_hidpp.open_path(path) def product_information(self, usb_id: int) -> dict: return base.product_information(usb_id) def find_paired_node(self, receiver_path: str, index: int, timeout: int): return None def request(self, response, *args, **kwargs): func = partial(fake_hidpp.request, self.responses) return func(response, *args, **kwargs) def ping(self, response, *args, **kwargs): func = partial(fake_hidpp.ping, self.responses) return func(response, *args, **kwargs) def close(self, *args, **kwargs): pass @pytest.mark.parametrize( "index, expected_kind", [ (0, None), (1, 2), # mouse (2, 2), # mouse (3, 1), # keyboard (4, 3), # numpad (5, None), ], ) def test_get_kind_from_index(index, expected_kind): mock_receiver = mock.Mock() if expected_kind: assert receiver._get_kind_from_index(mock_receiver, index) == expected_kind else: with pytest.raises(exceptions.NoSuchDevice): receiver._get_kind_from_index(mock_receiver, index) @dataclass class DeviceInfo: path: str vendor_id: int = 1133 product_id: int = 0xC52B responses_unifying = [ fake_hidpp.Response("000000", 0x8003, "FF"), fake_hidpp.Response("000300", 0x8102), fake_hidpp.Response("0316CC9CB40506220000000000000000", 0x83B5, "03"), fake_hidpp.Response("20200840820402020700000000000000", 0x83B5, "20"), fake_hidpp.Response("21211420110400010D1A000000000000", 0x83B5, "21"), fake_hidpp.Response("22220840660402010700000000020000", 0x83B5, "22"), fake_hidpp.Response("30198E3EB80600000001000000000000", 0x83B5, "30"), fake_hidpp.Response("31811119511A40000002000000000000", 0x83B5, "31"), fake_hidpp.Response("32112C46EA1E40000003000000000000", 0x83B5, "32"), fake_hidpp.Response("400B4D58204D61737465722033000000", 0x83B5, "40"), fake_hidpp.Response("41044B35323020202020202020202020", 0x83B5, "41"), fake_hidpp.Response("42054372616674000000000000000000", 0x83B5, "42"), fake_hidpp.Response("012411", 0x81F1, "01"), fake_hidpp.Response("020036", 0x81F1, "02"), fake_hidpp.Response("03AAAC", 0x81F1, "03"), fake_hidpp.Response("040209", 0x81F1, "04"), ] responses_c534 = [ fake_hidpp.Response("000000", 0x8003, "FF", handle=0x12), fake_hidpp.Response("000209", 0x8102, handle=0x12), fake_hidpp.Response("0316CC9CB40502220000000000000000", 0x83B5, "03", handle=0x12), fake_hidpp.Response("00000445AB", 0x83B5, "04", handle=0x12), ] responses_unusual = [ fake_hidpp.Response("000000", 0x8003, "FF", handle=0x13), fake_hidpp.Response("000300", 0x8102, handle=0x13), fake_hidpp.Response("00000445AB", 0x83B5, "04", handle=0x13), fake_hidpp.Response("0326CC9CB40508220000000000000000", 0x83B5, "03", handle=0x13), ] responses_lacking = [ fake_hidpp.Response("000000", 0x8003, "FF", handle=0x14), fake_hidpp.Response("000300", 0x8102, handle=0x14), ] mouse_info = { "kind": common.NamedInt(2, "mouse"), "polling": "8ms", "power_switch": common.NamedInt(1, "base"), "serial": "198E3EB8", "wpid": "4082", } c534_info = {"kind": common.NamedInt(0, "unknown"), "polling": "", "power_switch": "(unknown)", "serial": None, "wpid": "45AB"} @pytest.mark.parametrize( "device_info, responses, handle, serial, max_devices, ", [ (DeviceInfo(path=None), [], False, None, None), (DeviceInfo(path=11), [], None, None, None), (DeviceInfo(path="11"), responses_unifying, 0x11, "16CC9CB4", 6), (DeviceInfo(path="12", product_id=0xC534), responses_c534, 0x12, "16CC9CB4", 2), (DeviceInfo(path="12", product_id=0xC539), responses_c534, 0x12, "16CC9CB4", 2), (DeviceInfo(path="13"), responses_unusual, 0x13, "26CC9CB4", 1), (DeviceInfo(path="14"), responses_lacking, 0x14, None, 1), ], ) def test_receiver_factory_create_receiver(device_info, responses, handle, serial, max_devices): mock_low_level = LowLevelInterfaceFake(responses) if handle is False: with pytest.raises(Exception): # noqa: B017 receiver.create_receiver(mock_low_level, device_info, lambda x: x) elif handle is None: r = receiver.create_receiver(mock_low_level, device_info, lambda x: x) assert r is None else: r = receiver.create_receiver(mock_low_level, device_info, lambda x: x) assert r.handle == handle assert r.serial == serial assert r.max_devices == max_devices @pytest.mark.parametrize( "device_info, responses, firmware, codename, remaining_pairings, pairing_info, count", [ (DeviceInfo("11"), responses_unifying, 3, "K520", -1, mouse_info, 3), (DeviceInfo("12", product_id=0xC534), responses_c534, None, None, 4, c534_info, 2), (DeviceInfo("13", product_id=0xCCCC), responses_unusual, None, None, -1, c534_info, 3), ], ) def test_receiver_factory_props(device_info, responses, firmware, codename, remaining_pairings, pairing_info, count): mock_low_level = LowLevelInterfaceFake(responses) r = receiver.create_receiver(mock_low_level, device_info, lambda x: x) assert len(r.firmware) == firmware if firmware is not None else firmware is None assert r.device_codename(2) == codename assert r.remaining_pairings() == remaining_pairings assert r.device_pairing_information(1) == pairing_info assert r.count() == count @pytest.mark.parametrize( "device_info, responses, status_str, strng", [ (DeviceInfo("11"), responses_unifying, "No paired devices.", ""), (DeviceInfo("12", product_id=0xC534), responses_c534, "No paired devices.", ""), (DeviceInfo("13", product_id=0xCCCC), responses_unusual, "No paired devices.", ""), ], ) def test_receiver_factory_string(device_info, responses, status_str, strng): mock_low_level = LowLevelInterfaceFake(responses) r = receiver.create_receiver(mock_low_level, device_info, lambda x: x) assert r.status_string() == status_str assert str(r) == strng @pytest.mark.parametrize( "device_info, responses", [ (DeviceInfo("14"), responses_lacking), (DeviceInfo("14", product_id="C534"), responses_lacking), ], ) def test_receiver_factory_no_device(device_info, responses): mock_low_level = LowLevelInterfaceFake(responses) r = receiver.create_receiver(mock_low_level, device_info, lambda x: x) with pytest.raises(exceptions.NoSuchDevice): r.device_pairing_information(1) Solaar-1.1.14/tests/logitech_receiver/test_setting_templates.py000066400000000000000000001135211473525213700250230ustar00rootroot00000000000000## Copyright (C) 2024 Solaar Contributors https://pwr-solaar.github.io/Solaar/ ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. """The tests work by creating a faked device (from the hidpp module) that uses provided data as responses to HID++ commands. The device uses some methods from the real device to set up data structures that are needed for some tests. """ from dataclasses import dataclass from typing import Any import pytest from logitech_receiver import common from logitech_receiver import hidpp20 from logitech_receiver import hidpp20_constants from logitech_receiver import settings_templates from logitech_receiver import special_keys from . import fake_hidpp # TODO action part of DpiSlidingXY, MouseGesturesXY class Setup: def __init__(self, test, *params): self.test = test self.responses = [r for r in params if isinstance(r, fake_hidpp.Response)] self.choices = None if isinstance(params[0], fake_hidpp.Response) else params[0] @dataclass class RegisterTest: sclass: Any initial_value: Any = False write_value: Any = True write_params: str = "01" register_tests = [ Setup( RegisterTest(settings_templates.RegisterHandDetection, False, True, [b"\x00\x00\x00"]), fake_hidpp.Response("000030", 0x8101), # keyboard_hand_detection fake_hidpp.Response("000000", 0x8001, "000000"), ), Setup( RegisterTest(settings_templates.RegisterHandDetection, True, False, [b"\x00\x00\x30"]), fake_hidpp.Response("000000", 0x8101), # keyboard_hand_detection fake_hidpp.Response("000030", 0x8001, "000030"), ), Setup( RegisterTest(settings_templates.RegisterSmoothScroll, False, True, [b"\x40"]), fake_hidpp.Response("00", 0x8101), # mouse_button_flags fake_hidpp.Response("40", 0x8001, "40"), ), Setup( RegisterTest(settings_templates.RegisterSideScroll, True, False, [b"\x00"]), fake_hidpp.Response("02", 0x8101), # mouse_button_flags fake_hidpp.Response("00", 0x8001, "00"), ), Setup( RegisterTest(settings_templates.RegisterFnSwap, False, True, [b"\x00\x01"]), fake_hidpp.Response("0000", 0x8109), # keyboard_fn_swap fake_hidpp.Response("0001", 0x8009, "0001"), ), Setup( RegisterTest( settings_templates._PerformanceMXDpi, common.NamedInt(0x88, "800"), common.NamedInt(0x89, "900"), [b"\x89"] ), fake_hidpp.Response("88", 0x8163), # mouse_dpi fake_hidpp.Response("89", 0x8063, "89"), ), ] @pytest.mark.parametrize("test", register_tests) def test_register_template(test, mocker): device = fake_hidpp.Device(protocol=1.0, responses=test.responses) spy_request = mocker.spy(device, "request") setting = test.test.sclass.build(device) value = setting.read(cached=False) cached_value = setting.read(cached=True) write_value = setting.write(test.test.write_value) assert setting is not None assert value == test.test.initial_value assert cached_value == test.test.initial_value assert write_value == test.test.write_value spy_request.assert_called_with(test.test.sclass.register + 0x8000, *test.test.write_params) @dataclass class FeatureTest: sclass: Any initial_value: Any = False write_value: Any = True matched_calls: int = 1 offset: int = 0x04 version: int = 0x00 rewrite: bool = False simple_tests = [ Setup( FeatureTest(settings_templates.K375sFnSwap, False, True, offset=0x06), fake_hidpp.Response("FF0001", 0x0600, "FF"), fake_hidpp.Response("FF0101", 0x0610, "FF01"), ), Setup( FeatureTest(settings_templates.K375sFnSwap, False, True, offset=0x06), fake_hidpp.Response("050001", 0x0000, "1815"), # HOSTS_INFO fake_hidpp.Response("FF0001", 0x0600, "FF"), fake_hidpp.Response("FF0101", 0x0610, "FF01"), ), Setup( FeatureTest(settings_templates.K375sFnSwap, False, True, offset=0x06), fake_hidpp.Response("050001", 0x0000, "1815"), # HOSTS_INFO fake_hidpp.Response("07050301", 0x0500), # current host is 0x01, i.e., host 2 fake_hidpp.Response("010001", 0x0600, "01"), fake_hidpp.Response("010101", 0x0610, "0101"), ), Setup( FeatureTest(settings_templates.FnSwap, True, False), fake_hidpp.Response("01", 0x0400), fake_hidpp.Response("00", 0x0410, "00"), ), Setup( FeatureTest(settings_templates.NewFnSwap, True, False), fake_hidpp.Response("01", 0x0400), fake_hidpp.Response("00", 0x0410, "00"), ), # Setup( # Backlight has caused problems # FeatureTest(settings_templates.Backlight, 0, 5, offset=0x06), # fake_hidpp.Response("00", 0x0600), # fake_hidpp.Response("05", 0x0610, "05"), # ), Setup( FeatureTest(settings_templates.Backlight2DurationHandsOut, 80, 160, version=0x03), fake_hidpp.Response("011830000000100040006000", 0x0400), fake_hidpp.Response("0118FF00200040006000", 0x0410, "0118FF00200040006000"), ), Setup( FeatureTest(settings_templates.Backlight2DurationHandsIn, 320, 160, version=0x03), fake_hidpp.Response("011830000000200040006000", 0x0400), fake_hidpp.Response("0118FF00200020006000", 0x0410, "0118FF00200020006000"), ), Setup( FeatureTest(settings_templates.Backlight2DurationPowered, 480, 80, version=0x03), fake_hidpp.Response("011830000000200040006000", 0x0400), fake_hidpp.Response("0118FF00200040001000", 0x0410, "0118FF00200040001000"), ), Setup( FeatureTest(settings_templates.Backlight3, 0x50, 0x70), fake_hidpp.Response("50", 0x0410), fake_hidpp.Response("70", 0x0420, "007009"), ), Setup( FeatureTest(settings_templates.HiResScroll, True, False), fake_hidpp.Response("01", 0x0400), fake_hidpp.Response("00", 0x0410, "00"), ), Setup( FeatureTest(settings_templates.LowresMode, False, True), fake_hidpp.Response("00", 0x0400), fake_hidpp.Response("01", 0x0410, "01"), ), Setup( FeatureTest(settings_templates.HiresSmoothInvert, True, False), fake_hidpp.Response("06", 0x0410), fake_hidpp.Response("02", 0x0420, "02"), ), Setup( FeatureTest(settings_templates.HiresSmoothResolution, True, False), fake_hidpp.Response("06", 0x0410), fake_hidpp.Response("04", 0x0420, "04"), ), Setup( FeatureTest(settings_templates.HiresMode, False, True), fake_hidpp.Response("06", 0x0410), fake_hidpp.Response("07", 0x0420, "07"), ), Setup( FeatureTest(settings_templates.PointerSpeed, 0x0100, 0x0120), fake_hidpp.Response("0100", 0x0400), fake_hidpp.Response("0120", 0x0410, "0120"), ), Setup( FeatureTest(settings_templates.ThumbMode, True, False), fake_hidpp.Response("0100", 0x0410), fake_hidpp.Response("0000", 0x0420, "0000"), ), Setup( FeatureTest(settings_templates.ThumbInvert, False, True), fake_hidpp.Response("0100", 0x0410), fake_hidpp.Response("0101", 0x0420, "0101"), ), Setup( FeatureTest(settings_templates.DivertCrown, False, True), fake_hidpp.Response("01", 0x0410), fake_hidpp.Response("02", 0x0420, "02"), ), Setup( FeatureTest(settings_templates.CrownSmooth, True, False), fake_hidpp.Response("0001", 0x0410), fake_hidpp.Response("0002", 0x0420, "0002"), ), Setup( FeatureTest(settings_templates.DivertGkeys, False, True), fake_hidpp.Response("01", 0x0420, "01"), ), Setup( FeatureTest(settings_templates.ScrollRatchet, 2, 1), fake_hidpp.Response("02", 0x0400), fake_hidpp.Response("01", 0x0410, "01"), ), Setup( FeatureTest(settings_templates.SmartShift, 1, 10), fake_hidpp.Response("0100", 0x0400), fake_hidpp.Response("000A", 0x0410, "000A"), ), Setup( FeatureTest(settings_templates.SmartShift, 5, 50), fake_hidpp.Response("0005", 0x0400), fake_hidpp.Response("00FF", 0x0410, "00FF"), ), Setup( FeatureTest(settings_templates.SmartShiftEnhanced, 5, 50), fake_hidpp.Response("0005", 0x0410), fake_hidpp.Response("00FF", 0x0420, "00FF"), ), Setup( FeatureTest(settings_templates.DisableKeyboardKeys, {1: True, 8: True}, {1: False, 8: True}), fake_hidpp.Response("09", 0x0400), fake_hidpp.Response("09", 0x0410), fake_hidpp.Response("08", 0x0420, "08"), ), Setup( FeatureTest(settings_templates.DualPlatform, 0, 1), fake_hidpp.Response("00", 0x0400), fake_hidpp.Response("01", 0x0420, "01"), ), Setup( FeatureTest(settings_templates.MKeyLEDs, {1: False, 2: False, 4: False}, {1: False, 2: True, 4: True}), fake_hidpp.Response("03", 0x0400), fake_hidpp.Response("06", 0x0410, "06"), ), Setup( FeatureTest(settings_templates.MRKeyLED, False, True), fake_hidpp.Response("01", 0x0400, "01"), ), Setup( FeatureTest(settings_templates.Sidetone, 5, 0xA), fake_hidpp.Response("05", 0x0400), fake_hidpp.Response("0A", 0x0410, "0A"), ), Setup( FeatureTest(settings_templates.ADCPower, 5, 0xA), fake_hidpp.Response("05", 0x0410), fake_hidpp.Response("0A", 0x0420, "0A"), ), Setup( FeatureTest(settings_templates.LEDControl, 0, 1), fake_hidpp.Response("00", 0x0470), fake_hidpp.Response("01", 0x0480, "01"), ), Setup( FeatureTest( settings_templates.LEDZoneSetting, hidpp20.LEDEffectSetting(ID=3, intensity=0x50, period=0x100), hidpp20.LEDEffectSetting(ID=3, intensity=0x50, period=0x101), ), fake_hidpp.Response("0100000001", 0x0400), fake_hidpp.Response("00000102", 0x0410, "00FF00"), fake_hidpp.Response("0000000300040005", 0x0420, "000000"), fake_hidpp.Response("0001000B00080009", 0x0420, "000100"), fake_hidpp.Response("000000000000010050", 0x04E0, "00"), fake_hidpp.Response("000000000000000101500000", 0x0430, "000000000000000101500000"), ), Setup( FeatureTest(settings_templates.RGBControl, 0, 1), fake_hidpp.Response("0000", 0x0450), fake_hidpp.Response("010100", 0x0450, "0101"), ), Setup( FeatureTest( settings_templates.RGBEffectSetting, hidpp20.LEDEffectSetting(ID=3, intensity=0x50, period=0x100), hidpp20.LEDEffectSetting(ID=2, color=0x505050, speed=0x50), ), fake_hidpp.Response("FFFF0100000001", 0x0400, "FFFF00"), fake_hidpp.Response("0000000102", 0x0400, "00FF00"), fake_hidpp.Response("0000000300040005", 0x0400, "000000"), fake_hidpp.Response("0001000200080009", 0x0400, "000100"), fake_hidpp.Response("000000000000010050", 0x04E0, "00"), fake_hidpp.Response("00015050505000000000000001", 0x0410, "00015050505000000000000001"), ), Setup( FeatureTest(settings_templates.RGBEffectSetting, None, hidpp20.LEDEffectSetting(ID=3, intensity=0x60, period=0x101)), fake_hidpp.Response("FFFF0100000001", 0x0400, "FFFF00"), fake_hidpp.Response("0000000102", 0x0400, "00FF00"), fake_hidpp.Response("0000000300040005", 0x0400, "000000"), fake_hidpp.Response("0001000200080009", 0x0400, "000100"), fake_hidpp.Response("00000000000000010160000001", 0x0410, "00000000000000010160000001"), ), Setup( FeatureTest(settings_templates.RGBEffectSetting, None, hidpp20.LEDEffectSetting(ID=3, intensity=0x60, period=0x101)), fake_hidpp.Response("FF000200020004000000000000000000", 0x0400, "FFFF00"), fake_hidpp.Response("00000002040000000000000000000000", 0x0400, "00FF00"), fake_hidpp.Response("00000000000000000000000000000000", 0x0400, "000000"), fake_hidpp.Response("00010001000000000000000000000000", 0x0400, "000100"), fake_hidpp.Response("00020003C00503E00000000000000000", 0x0400, "000200"), fake_hidpp.Response("0003000AC0011E0B0000000000000000", 0x0400, "000300"), fake_hidpp.Response("01000001070000000000000000000000", 0x0400, "01FF00"), fake_hidpp.Response("01000000000000000000000000000000", 0x0400, "010000"), fake_hidpp.Response("01010001000000000000000000000000", 0x0400, "010100"), fake_hidpp.Response("0102000AC0011E0B0000000000000000", 0x0400, "010200"), fake_hidpp.Response("01030003C00503E00000000000000000", 0x0400, "010300"), fake_hidpp.Response("01040004DCE1001E0000000000000000", 0x0400, "010400"), fake_hidpp.Response("0105000B000000320000000000000000", 0x0400, "010500"), fake_hidpp.Response("0106000C001B02340000000000000000", 0x0400, "010600"), fake_hidpp.Response("00020000000000010160000001", 0x0410, "00020000000000010160000001"), ), Setup( FeatureTest(settings_templates.Backlight2, 0xFF, 0x00), common.NamedInts(Disabled=0xFF, Enabled=0x00), fake_hidpp.Response("000201000000000000000000", 0x0400), fake_hidpp.Response("010201", 0x0410, "0102FF00000000000000"), ), Setup( FeatureTest(settings_templates.Backlight2, 0x03, 0xFF), common.NamedInts(Disabled=0xFF, Automatic=0x01, Manual=0x03), fake_hidpp.Response("011838000000000000000000", 0x0400), fake_hidpp.Response("001801", 0x0410, "0018FF00000000000000"), ), Setup( FeatureTest(settings_templates.Backlight2Level, 0, 3, version=0x03), [0, 4], fake_hidpp.Response("011830000000000000000000", 0x0400), fake_hidpp.Response("05", 0x0420), fake_hidpp.Response("01180103000000000000", 0x0410, "0118FF03000000000000"), ), Setup( FeatureTest(settings_templates.Backlight2Level, 0, 2, version=0x03), [0, 4], fake_hidpp.Response("011830000000000000000000", 0x0400), fake_hidpp.Response("05", 0x0420), fake_hidpp.Response("01180102000000000000", 0x0410, "0118FF02000000000000"), ), Setup( FeatureTest(settings_templates.OnboardProfiles, 0, 1, offset=0x0C), common.NamedInts(**{"Disabled": 0, "Profile 1": 1, "Profile 2": 2}), fake_hidpp.Response("01030001010101000101", 0x0C00), fake_hidpp.Response("00010100000201FFFFFFFFFFFFFFFFFF", 0x0C50, "00000000"), fake_hidpp.Response("000201FFFFFFFFFFFFFFFFFFFFFFFFFF", 0x0C50, "00000004"), fake_hidpp.Response("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 0x0C50, "00000008"), fake_hidpp.Response("02", 0x0C20), fake_hidpp.Response("01", 0x0C10, "01"), fake_hidpp.Response("0001", 0x0C30, "0001"), ), Setup( FeatureTest(settings_templates.OnboardProfiles, 1, 0, offset=0x0C), common.NamedInts(**{"Disabled": 0, "Profile 1": 1, "Profile 2": 2}), fake_hidpp.Response("01030001010101000101", 0x0C00), fake_hidpp.Response("00010100000201FFFFFFFFFFFFFFFFFF", 0x0C50, "00000000"), fake_hidpp.Response("000201FFFFFFFFFFFFFFFFFFFFFFFFFF", 0x0C50, "00000004"), fake_hidpp.Response("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 0x0C50, "00000008"), fake_hidpp.Response("01", 0x0C20), fake_hidpp.Response("0001", 0x0C40), fake_hidpp.Response("02", 0x0C10, "02"), ), Setup( FeatureTest(settings_templates.ReportRate, 1, 5, offset=0x0C), common.NamedInts(**{"1ms": 1, "2ms": 2, "5ms": 5, "6ms": 6}), fake_hidpp.Response("33", 0x0C00), fake_hidpp.Response("01", 0x0C10), fake_hidpp.Response("05", 0x0C20, "05"), ), Setup( FeatureTest(settings_templates.ExtendedReportRate, 1, 5, offset=0x0C), common.NamedInts(**{"8ms": 0, "4ms": 1, "500us": 4, "250us": 5}), fake_hidpp.Response("33", 0x0C10), fake_hidpp.Response("01", 0x0C20), fake_hidpp.Response("05", 0x0C30, "05"), ), Setup( FeatureTest(settings_templates.AdjustableDpi, 800, 400, version=0x03), common.NamedInts.list([400, 800, 1600]), fake_hidpp.Response("000190032006400000", 0x0410, "000000"), fake_hidpp.Response("000320", 0x0420), fake_hidpp.Response("000190", 0x0430, "000190"), ), Setup( FeatureTest(settings_templates.AdjustableDpi, 256, 512, version=0x03), common.NamedInts.list([256, 512]), fake_hidpp.Response("000100e10002000000", 0x0410, "000000"), fake_hidpp.Response("000100", 0x0420), fake_hidpp.Response("000200", 0x0430, "000200"), ), Setup( FeatureTest(settings_templates.AdjustableDpi, 400, 800, version=0x03), common.NamedInts.list([400, 800, 1200, 1600]), fake_hidpp.Response("000190E19006400000000000000000", 0x0410, "000000"), fake_hidpp.Response("000190", 0x0420), fake_hidpp.Response("000320", 0x0430, "000320"), ), Setup( FeatureTest(settings_templates.Multiplatform, 0, 1), common.NamedInts(**{"MacOS 0.1-0.5": 0, "iOS 0.1-0.7": 1, "Linux 0.2-0.9": 2, "Windows 0.3-0.9": 3}), fake_hidpp.Response("020004000001", 0x0400), fake_hidpp.Response("00FF200000010005", 0x0410, "00"), fake_hidpp.Response("01FF400000010007", 0x0410, "01"), fake_hidpp.Response("02FF040000020009", 0x0410, "02"), fake_hidpp.Response("03FF010000030009", 0x0410, "03"), fake_hidpp.Response("FF01", 0x0430, "FF01"), ), Setup( FeatureTest(settings_templates.ChangeHost, 1, 0), common.NamedInts(**{"1:ABCDEF": 0, "2:GHIJKL": 1}), fake_hidpp.Response("050003", 0x0000, "1815"), # HOSTS_INFO fake_hidpp.Response("01000200", 0x0500), fake_hidpp.Response("000100000600", 0x0510, "00"), fake_hidpp.Response("000041424344454600", 0x0530, "0000"), fake_hidpp.Response("000100000600", 0x0510, "01"), fake_hidpp.Response("00004748494A4B4C00", 0x0530, "0100"), fake_hidpp.Response("0201", 0x0400), fake_hidpp.Response(True, 0x0410, "00"), ), Setup( FeatureTest(settings_templates.BrightnessControl, 0x10, 0x20), [0, 80], fake_hidpp.Response("00505100000000", 0x0400), # 0 to 80, all acceptable, no separate on/off fake_hidpp.Response("10", 0x0410), # brightness 16 fake_hidpp.Response("0020", 0x0420, "0020"), # set brightness 32 ), Setup( FeatureTest(settings_templates.BrightnessControl, 0x10, 0x00), [0, 80], fake_hidpp.Response("00505104000000", 0x0400), # 0 to 80, all acceptable, separate on/off fake_hidpp.Response("10", 0x0410), # brightness 16 fake_hidpp.Response("01", 0x0430), # on fake_hidpp.Response("00", 0x0440), # set off fake_hidpp.Response("0000", 0x0420, "0000"), # set brightness 0 ), Setup( FeatureTest(settings_templates.BrightnessControl, 0x00, 0x20), [0, 80], fake_hidpp.Response("00505104000000", 0x0400), # 0 to 80, all acceptable, separate on/off fake_hidpp.Response("10", 0x0410), # brightness 16 fake_hidpp.Response("00", 0x0430), # off fake_hidpp.Response("01", 0x0440), # set on fake_hidpp.Response("0020", 0x0420, "0020"), # set brightness 32 ), Setup( FeatureTest(settings_templates.BrightnessControl, 0x20, 0x08), [0, 80], fake_hidpp.Response("00504104001000", 0x0400), # 16 to 80, all acceptable, separate on/off fake_hidpp.Response("20", 0x0410), # brightness 32 fake_hidpp.Response("01", 0x0430), # on fake_hidpp.Response("00", 0x0440, "00"), # set off ), Setup( FeatureTest(settings_templates.SpeedChange, 0, None, 0), # need to set up all settings to successfully write common.NamedInts(**{"Off": 0, "DPI Change": 0xED}), *fake_hidpp.responses_speedchange, ), ] @pytest.fixture def mock_gethostname(mocker): mocker.patch("socket.gethostname", return_value="ABCDEF.foo.org") @pytest.mark.parametrize("test", simple_tests) def test_simple_template(test, mocker, mock_gethostname): tst = test.test print("TEST", tst.sclass.feature) device = fake_hidpp.Device(responses=test.responses, feature=tst.sclass.feature, offset=tst.offset, version=tst.version) spy_request = mocker.spy(device, "request") setting = settings_templates.check_feature(device, tst.sclass) assert setting is not None if isinstance(setting, list): setting = setting[0] if isinstance(test.choices, list): assert setting._validator.min_value == test.choices[0] assert setting._validator.max_value == test.choices[1] elif test.choices is not None: assert setting.choices == test.choices value = setting.read(cached=False) assert value == tst.initial_value cached_value = setting.read(cached=True) assert cached_value == tst.initial_value write_value = setting.write(tst.write_value) if tst.write_value is not None else None assert write_value == tst.write_value fake_hidpp.match_requests(tst.matched_calls, test.responses, spy_request.call_args_list) responses_reprog_controls = [ fake_hidpp.Response("03", 0x0500), fake_hidpp.Response("00500038010001010400000000000000", 0x0510, "00"), # left button fake_hidpp.Response("00510039010001010400000000000000", 0x0510, "01"), # right button fake_hidpp.Response("00C4009D310003070500000000000000", 0x0510, "02"), # smart shift fake_hidpp.Response("00500000000000000000000000000000", 0x0520, "0050"), # left button current fake_hidpp.Response("00510000500000000000000000000000", 0x0520, "0051"), # right button current fake_hidpp.Response("00C40000000000000000000000000000", 0x0520, "00C4"), # smart shift current fake_hidpp.Response("00500005000000000000000000000000", 0x0530, "0050000050"), # left button write fake_hidpp.Response("00510005000000000000000000000000", 0x0530, "0051000050"), # right button write fake_hidpp.Response("00C4000C400000000000000000000000", 0x0530, "00C40000C4"), # smart shift write ] key_tests = [ Setup( FeatureTest(settings_templates.ReprogrammableKeys, {0x50: 0x50, 0x51: 0x50, 0xC4: 0xC4}, {0x51: 0x51}, 4, offset=0x05), { common.NamedInt(0x50, "Left Button"): common.UnsortedNamedInts(Left_Click=0x50, Right_Click=0x51), common.NamedInt(0x51, "Right Button"): common.UnsortedNamedInts(Right_Click=0x51, Left_Click=0x50), common.NamedInt(0xC4, "Smart Shift"): common.UnsortedNamedInts(Smart_Shift=0xC4, Left_Click=80, Right_Click=81), }, *responses_reprog_controls, fake_hidpp.Response("0051000051", 0x0530, "0051000051"), # right button set write ), Setup( FeatureTest(settings_templates.DivertKeys, {0xC4: 0}, {0xC4: 1}, 2, offset=0x05), {common.NamedInt(0xC4, "Smart Shift"): common.NamedInts(Regular=0, Diverted=1, Mouse_Gestures=2)}, *responses_reprog_controls, fake_hidpp.Response("00C4020000", 0x0530, "00C4020000"), # Smart Shift write fake_hidpp.Response("00C4030000", 0x0530, "00C4030000"), # Smart Shift divert write ), Setup( FeatureTest(settings_templates.DivertKeys, {0xC4: 0}, {0xC4: 2}, 2, offset=0x05), {common.NamedInt(0xC4, "Smart Shift"): common.NamedInts(Regular=0, Diverted=1, Mouse_Gestures=2, Sliding_DPI=3)}, *responses_reprog_controls, fake_hidpp.Response("0A0001", 0x0000, "2201"), # ADJUSTABLE_DPI fake_hidpp.Response("00C4300000", 0x0530, "00C4300000"), # Smart Shift write fake_hidpp.Response("00C4030000", 0x0530, "00C4030000"), # Smart Shift divert write ), Setup( FeatureTest(settings_templates.PersistentRemappableAction, {80: 16797696, 81: 16797696}, {0x51: 16797952}, 3), { common.NamedInt(80, "Left Button"): special_keys.KEYS_KEYS_CONSUMER, common.NamedInt(81, "Right Button"): special_keys.KEYS_KEYS_CONSUMER, }, fake_hidpp.Response("050001", 0x0000, "1B04"), # REPROG_CONTROLS_V4 *responses_reprog_controls, fake_hidpp.Response("0041", 0x0400), fake_hidpp.Response("0201", 0x0410), fake_hidpp.Response("02", 0x0400), fake_hidpp.Response("0050", 0x0420, "00FF"), # left button fake_hidpp.Response("0051", 0x0420, "01FF"), # right button fake_hidpp.Response("0050000100500000", 0x0430, "0050FF"), # left button current fake_hidpp.Response("0051000100500001", 0x0430, "0051FF"), # right button current fake_hidpp.Response("0050FF01005000", 0x0440, "0050FF01005000"), # left button write fake_hidpp.Response("0051FF01005000", 0x0440, "0051FF01005000"), # right button write fake_hidpp.Response("0051FF01005100", 0x0440, "0051FF01005100"), # right button set write ), Setup( FeatureTest( settings_templates.Gesture2Gestures, { 1: True, 2: True, 30: True, 10: True, 45: False, 42: True, 43: True, 64: False, 65: False, 67: False, 84: True, 34: False, }, {45: True}, 4, ), *fake_hidpp.responses_gestures, fake_hidpp.Response("0001FF6F", 0x0420, "0001FF6F"), # write fake_hidpp.Response("01010F04", 0x0420, "01010F04"), fake_hidpp.Response("0001FF7F", 0x0420, "0001FF7F"), # write 45 fake_hidpp.Response("01010F04", 0x0420, "01010F04"), ), Setup( FeatureTest( settings_templates.Gesture2Divert, {1: False, 2: False, 10: False, 44: False, 64: False, 65: False, 67: False, 84: False, 85: False, 100: False}, {44: True}, 4, ), *fake_hidpp.responses_gestures, fake_hidpp.Response("0001FF00", 0x0440, "0001FF00"), # write fake_hidpp.Response("01010300", 0x0440, "01010300"), fake_hidpp.Response("0001FF08", 0x0440, "0001FF08"), # write 44 fake_hidpp.Response("01010300", 0x0440, "01010300"), ), Setup( FeatureTest(settings_templates.Gesture2Params, {4: {"scale": 256}}, {4: {"scale": 128}}, 2), *fake_hidpp.responses_gestures, fake_hidpp.Response("000100FF000000000000000000000000", 0x0480, "000100FF"), fake_hidpp.Response("000080FF000000000000000000000000", 0x0480, "000080FF"), ), Setup( FeatureTest(settings_templates.Equalizer, {0: -0x20, 1: 0x10}, {1: 0x18}, 2), [-32, 32], fake_hidpp.Response("0220000000", 0x0400), fake_hidpp.Response("0000800100000000000000", 0x0410, "00"), fake_hidpp.Response("E010", 0x0420, "00"), fake_hidpp.Response("E010", 0x0430, "02E010"), fake_hidpp.Response("E018", 0x0430, "02E018"), ), Setup( FeatureTest(settings_templates.PerKeyLighting, {1: -1, 2: -1, 9: -1, 10: -1, 113: -1}, {2: 0xFF0000}, 4, 4, 0, 1), { common.NamedInt(1, "A"): special_keys.COLORSPLUS, common.NamedInt(2, "B"): special_keys.COLORSPLUS, common.NamedInt(9, "I"): special_keys.COLORSPLUS, common.NamedInt(10, "J"): special_keys.COLORSPLUS, common.NamedInt(113, "KEY 113"): special_keys.COLORSPLUS, }, fake_hidpp.Response("00000606000000000000000000000000", 0x0400, "0000"), # first group of keys fake_hidpp.Response("00000200000000000000000000000000", 0x0400, "0001"), # second group of keys fake_hidpp.Response("00000000000000000000000000000000", 0x0400, "0002"), # last group of keys fake_hidpp.Response("02FF0000", 0x0410, "02FF0000"), # write one value fake_hidpp.Response("00", 0x0470, "00"), # finish fake_hidpp.Response("02FF0000", 0x0410, "02FF0000"), # write one value fake_hidpp.Response("00", 0x0470, "00"), # finish ), Setup( FeatureTest( settings_templates.PerKeyLighting, {1: -1, 2: -1, 9: -1, 10: -1, 113: -1}, {2: 0xFF0000, 9: 0xFF0000, 10: 0xFF0000}, 8, 4, 0, 1, ), { common.NamedInt(1, "A"): special_keys.COLORSPLUS, common.NamedInt(2, "B"): special_keys.COLORSPLUS, common.NamedInt(9, "I"): special_keys.COLORSPLUS, common.NamedInt(10, "J"): special_keys.COLORSPLUS, common.NamedInt(113, "KEY 113"): special_keys.COLORSPLUS, }, fake_hidpp.Response("00000606000000000000000000000000", 0x0400, "0000"), # first group of keys fake_hidpp.Response("00000200000000000000000000000000", 0x0400, "0001"), # second group of keys fake_hidpp.Response("00000000000000000000000000000000", 0x0400, "0002"), # last group of keys fake_hidpp.Response("02FF0000", 0x0410, "02FF0000"), # write one value fake_hidpp.Response("00", 0x0470, "00"), # finish fake_hidpp.Response("09FF0000", 0x0410, "09FF0000"), # write one value fake_hidpp.Response("00", 0x0470, "00"), # finish fake_hidpp.Response("0AFF0000", 0x0410, "0AFF0000"), # write one value fake_hidpp.Response("00", 0x0470, "00"), # finish fake_hidpp.Response("02FF000009FF00000AFF0000", 0x0410, "02FF000009FF00000AFF0000"), # write three values fake_hidpp.Response("00", 0x0470, "00"), # finish ), Setup( FeatureTest( settings_templates.PerKeyLighting, {1: -1, 2: -1, 9: -1, 10: -1, 113: -1, 114: -1}, {1: 0xFF0000, 2: 0xFF0000, 9: 0xFF0000, 10: 0xFF0000, 113: 0x00FF00, 114: 0xFF0000}, 15, 4, 0, 1, ), { common.NamedInt(1, "A"): special_keys.COLORSPLUS, common.NamedInt(2, "B"): special_keys.COLORSPLUS, common.NamedInt(9, "I"): special_keys.COLORSPLUS, common.NamedInt(10, "J"): special_keys.COLORSPLUS, common.NamedInt(113, "KEY 113"): special_keys.COLORSPLUS, common.NamedInt(114, "KEY 114"): special_keys.COLORSPLUS, }, fake_hidpp.Response("00000606000000000000000000000000", 0x0400, "0000"), # first group of keys fake_hidpp.Response("00000600000000000000000000000000", 0x0400, "0001"), # second group of keys fake_hidpp.Response("00000000000000000000000000000000", 0x0400, "0002"), # last group of keys fake_hidpp.Response("01FF0000", 0x0410, "01FF0000"), # write one value fake_hidpp.Response("00", 0x0470, "00"), # finish fake_hidpp.Response("02FF0000", 0x0410, "02FF0000"), # write one value fake_hidpp.Response("00", 0x0470, "00"), # finish fake_hidpp.Response("09FF0000", 0x0410, "09FF0000"), # write one value fake_hidpp.Response("00", 0x0470, "00"), # finish fake_hidpp.Response("0AFF0000", 0x0410, "0AFF0000"), # write one value fake_hidpp.Response("00", 0x0470, "00"), # finish fake_hidpp.Response("7100FF00", 0x0410, "7100FF00"), # write one value fake_hidpp.Response("00", 0x0470, "00"), # finish fake_hidpp.Response("72FF0000", 0x0410, "72FF0000"), # write one value fake_hidpp.Response("00", 0x0470, "00"), # finish fake_hidpp.Response("FF00000102090A72", 0x460, "FF00000102090A72"), # write one value for five keys fake_hidpp.Response("7100FF00", 0x0410, "7100FF00"), # write one value fake_hidpp.Response("00", 0x0470, "00"), # finish ), Setup( FeatureTest(settings_templates.ExtendedAdjustableDpi, {0: 256}, {0: 512}, 2, offset=0x9), {common.NamedInt(0, "X"): common.NamedInts.list([256, 512])}, fake_hidpp.Response("000000", 0x0910, "00"), # no y direction, no lod fake_hidpp.Response("0000000100e10002000000", 0x0920, "000000"), fake_hidpp.Response("00010000000000000000", 0x0950), fake_hidpp.Response("000100000000", 0x0960, "000100000000"), fake_hidpp.Response("000200000000", 0x0960, "000200000000"), ), Setup( FeatureTest(settings_templates.ExtendedAdjustableDpi, {0: 0x64, 1: 0xE4}, {0: 0x164}, 2, offset=0x9), { common.NamedInt(0, "X"): common.NamedInts.list([0x064, 0x074, 0x084, 0x0A4, 0x0C4, 0x0E4, 0x0124, 0x0164, 0x01C4]), common.NamedInt(1, "Y"): common.NamedInts.list([0x064, 0x074, 0x084, 0x0A4, 0x0C4, 0x0E4, 0x0124, 0x0164]), }, fake_hidpp.Response("000001", 0x0910, "00"), # supports y direction, no lod fake_hidpp.Response("0000000064E0100084E02000C4E02000", 0x0920, "000000"), fake_hidpp.Response("000001E4E0400124E0400164E06001C4", 0x0920, "000001"), fake_hidpp.Response("00000000000000000000000000000000", 0x0920, "000002"), fake_hidpp.Response("0000000064E0100084E02000C4E02000", 0x0920, "000100"), fake_hidpp.Response("000001E4E0400124E040016400000000", 0x0920, "000101"), fake_hidpp.Response("000064007400E4007400", 0x0950), fake_hidpp.Response("00006400E400", 0x0960, "00006400E400"), fake_hidpp.Response("00016400E400", 0x0960, "00016400E400"), ), Setup( FeatureTest(settings_templates.ExtendedAdjustableDpi, {0: 0x64, 1: 0xE4, 2: 1}, {1: 0x164}, 2, offset=0x9), { common.NamedInt(0, "X"): common.NamedInts.list([0x064, 0x074, 0x084, 0x0A4, 0x0C4, 0x0E4, 0x0124, 0x0164, 0x01C4]), common.NamedInt(1, "Y"): common.NamedInts.list([0x064, 0x074, 0x084, 0x0A4, 0x0C4, 0x0E4, 0x0124, 0x0164]), common.NamedInt(2, "LOD"): common.NamedInts(LOW=0, MEDIUM=1, HIGH=2), }, fake_hidpp.Response("000003", 0x0910, "00"), # supports y direction and lod fake_hidpp.Response("0000000064E0100084E02000C4E02000", 0x0920, "000000"), fake_hidpp.Response("000001E4E0400124E0400164E06001C4", 0x0920, "000001"), fake_hidpp.Response("00000000000000000000000000000000", 0x0920, "000002"), fake_hidpp.Response("0000000064E0100084E02000C4E02000", 0x0920, "000100"), fake_hidpp.Response("000001E4E0400124E040016400000000", 0x0920, "000101"), fake_hidpp.Response("000064007400E4007401", 0x0950), fake_hidpp.Response("00006400E401", 0x0960, "00006400E401"), fake_hidpp.Response("000064016401", 0x0960, "000064016401"), ), ] @pytest.mark.parametrize("test", key_tests) def test_key_template(test, mocker): tst = test.test device = fake_hidpp.Device(responses=test.responses, feature=tst.sclass.feature, offset=tst.offset, version=tst.version) spy_request = mocker.spy(device, "request") print("FEATURE", tst.sclass.feature) setting = settings_templates.check_feature(device, tst.sclass) assert setting is not None if isinstance(setting, list): setting = setting[0] if isinstance(test.choices, list): assert setting._validator.min_value == test.choices[0] assert setting._validator.max_value == test.choices[1] elif test.choices is not None: assert setting.choices == test.choices value = setting.read(cached=False) assert value == tst.initial_value write_value = setting.write(value) assert write_value == tst.initial_value for key, val in tst.write_value.items(): write_value = setting.write_key_value(key, val) assert write_value == val value[key] = val if tst.rewrite: write_value = setting.write(value) fake_hidpp.match_requests(tst.matched_calls, test.responses, spy_request.call_args_list) @pytest.mark.parametrize( "responses, currentSpeed, newSpeed", [ (fake_hidpp.responses_speedchange, 100, 200), (fake_hidpp.responses_speedchange, None, 250), ], ) def test_SpeedChange_action(responses, currentSpeed, newSpeed, mocker): device = fake_hidpp.Device(responses=responses, feature=hidpp20_constants.SupportedFeature.POINTER_SPEED) spy_setting_callback = mocker.spy(device, "setting_callback") settings_templates.check_feature_settings(device, device.settings) # need to set up all the settings device.persister = {"pointer_speed": currentSpeed, "_speed-change": newSpeed} speed_setting = next(filter(lambda s: s.name == "speed-change", device.settings), None) pointer_setting = next(filter(lambda s: s.name == "pointer_speed", device.settings), None) speed_setting.write(237) speed_setting._rw.press_action() if newSpeed is not None and speed_setting is not None: spy_setting_callback.assert_any_call(device, type(pointer_setting), [newSpeed]) assert device.persister["_speed-change"] == currentSpeed assert device.persister["pointer_speed"] == newSpeed @pytest.mark.parametrize("test", simple_tests + key_tests) def test_check_feature_settings(test, mocker): tst = test.test device = fake_hidpp.Device(responses=test.responses, feature=tst.sclass.feature, offset=tst.offset, version=tst.version) already_known = [] setting = settings_templates.check_feature_settings(device, already_known) assert setting is True assert already_known @pytest.mark.parametrize( "test", [ Setup( FeatureTest(settings_templates.K375sFnSwap, False, True, offset=0x06), fake_hidpp.Response("FF0001", 0x0600, "FF"), fake_hidpp.Response("FF0101", 0x0610, "FF01"), ) ], ) def test_check_feature_setting(test, mocker): tst = test.test device = fake_hidpp.Device(responses=test.responses, feature=tst.sclass.feature, offset=tst.offset, version=tst.version) setting = settings_templates.check_feature_setting(device, tst.sclass.name) assert setting Solaar-1.1.14/tests/logitech_receiver/test_settings.py000066400000000000000000000012371473525213700231300ustar00rootroot00000000000000import pytest from logitech_receiver import settings @pytest.mark.parametrize( "current, new, expected", [ (False, "toggle", True), (True, "~", False), ("don't care", True, True), ("don't care", "true", True), ("don't care", "false", False), ("don't care", "no", False), ("don't care", "off", False), ("don't care", "True", True), ("don't care", "yes", True), ("don't care", "on", True), ("anything", "anything", None), ], ) def test_bool_or_toggle(current, new, expected): result = settings.bool_or_toggle(current=current, new=new) assert result == expected Solaar-1.1.14/tests/solaar/000077500000000000000000000000001473525213700154535ustar00rootroot00000000000000Solaar-1.1.14/tests/solaar/ui/000077500000000000000000000000001473525213700160705ustar00rootroot00000000000000Solaar-1.1.14/tests/solaar/ui/test_about_dialog.py000066400000000000000000000014421473525213700221330ustar00rootroot00000000000000from solaar.ui.about import about from solaar.ui.about.model import AboutModel def test_about_model(): expected_name = "Daniel Pavel" model = AboutModel() authors = model.get_authors() assert expected_name in authors[0] def test_about_dialog(mocker): view_mock = mocker.Mock() about.show(view=view_mock) assert view_mock.init_ui.call_count == 1 assert view_mock.update_version_info.call_count == 1 assert view_mock.update_description.call_count == 1 assert view_mock.update_authors.call_count == 1 assert view_mock.update_credits.call_count == 1 assert view_mock.update_copyright.call_count == 1 assert view_mock.update_translators.call_count == 1 assert view_mock.update_website.call_count == 1 assert view_mock.show.call_count == 1 Solaar-1.1.14/tests/solaar/ui/test_common.py000066400000000000000000000013501473525213700207700ustar00rootroot00000000000000from unittest import mock from unittest.mock import PropertyMock import pytest from solaar.ui import common @pytest.mark.parametrize( "reason, expected_in_title, expected_in_text", [ ( "permissions", "Permissions error", "not have permission to open", ), ("nodevice", "connect to device error", "error connecting"), ("unpair", "Unpairing failed", "receiver returned an error"), ], ) def test_create_error_text(reason, expected_in_title, expected_in_text): obj = mock.Mock() obj.name = PropertyMock(return_value="test") title, text = common._create_error_text(reason, obj) assert expected_in_title in title assert expected_in_text in text Solaar-1.1.14/tests/solaar/ui/test_desktop_notifications.py000066400000000000000000000010511473525213700241000ustar00rootroot00000000000000from unittest import mock from solaar.ui import desktop_notifications def test_notifications_available(): result = desktop_notifications.notifications_available() assert not result def test_init(): assert not desktop_notifications.init() def test_uninit(): assert desktop_notifications.uninit() is None def test_alert(): reason = "unknown" assert desktop_notifications.alert(reason) is None def test_show(): dev = mock.MagicMock() reason = "unknown" assert desktop_notifications.show(dev, reason) is None Solaar-1.1.14/tests/solaar/ui/test_i18n.py000066400000000000000000000011471473525213700202630ustar00rootroot00000000000000import locale import os import platform import pytest from solaar import i18n @pytest.fixture def set_locale_de(): backup_lang = os.environ.get("LC_ALL", "") try: yield finally: os.environ["LC_ALL"] = backup_lang i18n.set_locale_to_system_default() @pytest.mark.skipif(platform.system() == "Linux", reason="Adapt test for Linux") def test_set_locale_to_system_default(set_locale_de): os.environ["LC_ALL"] = "de_DE.UTF-8" i18n.set_locale_to_system_default() language, encoding = locale.getlocale() assert language == "de_DE" assert encoding == "UTF-8" Solaar-1.1.14/tests/solaar/ui/test_pair_window.py000066400000000000000000000212671473525213700220330ustar00rootroot00000000000000from dataclasses import dataclass from dataclasses import field from typing import Any from typing import Callable from typing import List from typing import Optional from unittest import mock import gi import pytest from logitech_receiver import receiver from solaar.ui import pair_window gi.require_version("Gtk", "3.0") from gi.repository import Gtk # NOQA: E402 gtk_init = Gtk.init_check()[0] @dataclass class Device: name: str = "test device" kind: str = "test kind" @dataclass class Receiver: find_paired_node_wpid_func: Callable[[str, int], Any] name: str receiver_kind: str _set_lock: bool = True pairing: receiver.Pairing = field(default_factory=receiver.Pairing) pairable: bool = True _remaining_pairings: Optional[int] = None def reset_pairing(self): self.receiver = receiver.Pairing() def remaining_pairings(self, cache=True): return self._remaining_pairings def set_lock(self, value=False, timeout=0): self.pairing.lock_open = self._set_lock return self._set_lock def discover(self, cancel=False, timeout=30): self.pairing.discovering = self._set_lock return self._set_lock def pair_device(self, pair=True, slot=0, address=b"\0\0\0\0\0\0", authentication=0x00, entropy=20, force=False): print("PD", self.pairable) return self.pairable @dataclass class Assistant: drawable: bool = True pages: List[Any] = field(default_factory=list) def is_drawable(self): return self.drawable def next_page(self): return True def set_page_complete(self, page, b): return True def commit(self): return True def append_page(self, page): self.pages.append(page) def remove_page(self, page): return True def set_page_type(self, page, type): return True def destroy(self): pass @pytest.mark.skipif(not gtk_init, reason="requires Gtk") @pytest.mark.parametrize( "receiver, lock_open, discovering, page_type", [ (Receiver(mock.Mock(), "unifying", "unifying", True), True, False, Gtk.AssistantPageType.PROGRESS), (Receiver(mock.Mock(), "unifying", "unifying", False), False, False, Gtk.AssistantPageType.SUMMARY), (Receiver(mock.Mock(), "nano", "nano", True, _remaining_pairings=5), True, False, Gtk.AssistantPageType.PROGRESS), (Receiver(mock.Mock(), "nano", "nano", False), False, False, Gtk.AssistantPageType.SUMMARY), (Receiver(mock.Mock(), "bolt", "bolt", True), False, True, Gtk.AssistantPageType.PROGRESS), (Receiver(mock.Mock(), "bolt", "bolt", False), False, False, Gtk.AssistantPageType.SUMMARY), ], ) def test_create(receiver, lock_open, discovering, page_type): assistant = pair_window.create(receiver) assert assistant is not None assert assistant.get_page_type(assistant.get_nth_page(0)) == page_type assert receiver.pairing.lock_open == lock_open assert receiver.pairing.discovering == discovering @pytest.mark.parametrize( "receiver, expected_result, expected_error", [ (Receiver(mock.Mock(), "unifying", "unifying", True), True, False), (Receiver(mock.Mock(), "unifying", "unifying", False), False, True), (Receiver(mock.Mock(), "bolt", "bolt", True), True, False), (Receiver(mock.Mock(), "bolt", "bolt", False), False, True), ], ) def test_prepare(receiver, expected_result, expected_error): result = pair_window.prepare(receiver) assert result == expected_result assert bool(receiver.pairing.error) == expected_error @pytest.mark.parametrize("assistant, expected_result", [(Assistant(True), True), (Assistant(False), False)]) def test_check_lock_state_drawable(assistant, expected_result): r = Receiver(mock.Mock(), "succeed", "unifying", True, receiver.Pairing(lock_open=True)) result = pair_window.check_lock_state(assistant, r, 2) assert result == expected_result @pytest.mark.skipif(not gtk_init, reason="requires Gtk") @pytest.mark.parametrize( "receiver, count, expected_result", [ (Receiver(mock.Mock(), "fail", "unifying", False, receiver.Pairing(lock_open=False)), 2, False), (Receiver(mock.Mock(), "succeed", "unifying", True, receiver.Pairing(lock_open=True)), 1, True), (Receiver(mock.Mock(), "error", "unifying", True, receiver.Pairing(error="error")), 0, False), (Receiver(mock.Mock(), "new device", "unifying", True, receiver.Pairing(new_device=Device())), 2, False), (Receiver(mock.Mock(), "closed", "unifying", True, receiver.Pairing()), 2, False), (Receiver(mock.Mock(), "closed", "unifying", True, receiver.Pairing()), 1, False), (Receiver(mock.Mock(), "closed", "unifying", True, receiver.Pairing()), 0, False), (Receiver(mock.Mock(), "fail bolt", "bolt", False), 1, False), (Receiver(mock.Mock(), "succeed bolt", "bolt", True, receiver.Pairing(lock_open=True)), 0, True), (Receiver(mock.Mock(), "error bolt", "bolt", True, receiver.Pairing(error="error")), 2, False), (Receiver(mock.Mock(), "new device", "bolt", True, receiver.Pairing(lock_open=True, new_device=Device())), 1, False), (Receiver(mock.Mock(), "discovering", "bolt", True, receiver.Pairing(lock_open=True)), 1, True), (Receiver(mock.Mock(), "closed", "bolt", True, receiver.Pairing()), 2, False), (Receiver(mock.Mock(), "closed", "bolt", True, receiver.Pairing()), 1, False), (Receiver(mock.Mock(), "closed", "bolt", True, receiver.Pairing()), 0, False), ( Receiver( mock.Mock(), "pass1", "bolt", True, receiver.Pairing(lock_open=True, device_passkey=50, device_authentication=0x01), ), 0, True, ), ( Receiver( mock.Mock(), "pass2", "bolt", True, receiver.Pairing(lock_open=True, device_passkey=50, device_authentication=0x02), ), 0, True, ), ( Receiver( mock.Mock(), "adt", "bolt", True, receiver.Pairing(discovering=True, device_address=2, device_name=5), pairable=True, ), 2, True, ), ( Receiver( mock.Mock(), "adf", "bolt", True, receiver.Pairing(discovering=True, device_address=2, device_name=5), pairable=False, ), 2, False, ), (Receiver(mock.Mock(), "add fail", "bolt", False, receiver.Pairing(device_address=2, device_passkey=5)), 2, False), ], ) def test_check_lock_state(receiver, count, expected_result): assistant = Assistant(True) check_state = pair_window._check_lock_state(assistant, receiver, count) assert check_state == expected_result @pytest.mark.parametrize( "receiver, pair_device, set_lock, discover, error", [ ( Receiver(mock.Mock(), "unifying", "unifying", pairing=receiver.Pairing(lock_open=False, error="error")), 0, 0, 0, None, ), ( Receiver(mock.Mock(), "unifying", "unifying", pairing=receiver.Pairing(lock_open=True, error="error")), 0, 1, 0, "error", ), (Receiver(mock.Mock(), "bolt", "bolt", pairing=receiver.Pairing(lock_open=False, error="error")), 0, 0, 0, None), (Receiver(mock.Mock(), "bolt", "bolt", pairing=receiver.Pairing(lock_open=True, error="error")), 1, 0, 0, "error"), (Receiver(mock.Mock(), "bolt", "bolt", pairing=receiver.Pairing(discovering=True, error="error")), 0, 0, 1, "error"), ], ) def test_finish(receiver, pair_device, set_lock, discover, error, mocker): spy_pair_device = mocker.spy(receiver, "pair_device") spy_set_lock = mocker.spy(receiver, "set_lock") spy_discover = mocker.spy(receiver, "discover") assistant = Assistant(True) pair_window._finish(assistant, receiver) assert spy_pair_device.call_count == pair_device assert spy_set_lock.call_count == set_lock assert spy_discover.call_count == discover assert receiver.pairing.error == error @pytest.mark.skipif(not gtk_init, reason="requires Gtk") @pytest.mark.parametrize("error", ["timeout", "device not supported", "too many devices"]) def test_create_failure_page(error, mocker): spy_create = mocker.spy(pair_window, "_create_page") pair_window._pairing_failed(Assistant(True), Receiver(mock.Mock(), "nano", "nano"), error) assert spy_create.call_count == 1 Solaar-1.1.14/tests/solaar/ui/test_probe.py000066400000000000000000000036771473525213700206250ustar00rootroot00000000000000from unittest import mock from logitech_receiver.hidpp10_constants import ErrorCode from logitech_receiver.hidpp10_constants import Registers from solaar.cli.probe import run # Mock receiver class class MockReceiver: handle = 1 isDevice = False def read_register(self, register, *args): return 0 if register == Registers.RECEIVER_INFO else b"\x01\x03" def test_run_register_errors(): mock_args = mock.Mock() mock_args.receiver = False mock_receiver = MockReceiver() # Define expected addresses to be called in order expected_addresses = [] for reg in range(0, 0xFF): expected_addresses.append((0x8100 | reg, 0)) # First short call, returns invalid_value (continue) expected_addresses.append((0x8100 | reg, 1)) # Second short call, returns invalid_address (stop here) expected_addresses.append((0x8100 | (0x200 + reg), 0)) # First long call, returns invalid_value (continue) expected_addresses.append((0x8100 | (0x200 + reg), 1)) # Second long call, returns invalid_address (stop here) # To record the actual addresses called called_addresses = [] def mock_base_request(handle, devnumber, reg, sub, return_error=False): called_addresses.append((reg, sub)) if sub == 0: return ErrorCode.INVALID_VALUE elif sub == 1: return ErrorCode.INVALID_ADDRESS return b"\x01\x02" with mock.patch("logitech_receiver.base.request", side_effect=mock_base_request), mock.patch( "solaar.cli.probe._print_receiver", return_value=None ): # Call the run function with mocked receivers and args (passing real find_receiver function) run([mock_receiver], mock_args, None, None) # Evaluate that the addresses called match the expected addresses assert ( called_addresses == expected_addresses ), f"Called addresses {called_addresses} do not match expected {expected_addresses}" Solaar-1.1.14/tests/test_keysyms/000077500000000000000000000000001473525213700167355ustar00rootroot00000000000000Solaar-1.1.14/tests/test_keysyms/__init__.py000066400000000000000000000000001473525213700210340ustar00rootroot00000000000000Solaar-1.1.14/tests/test_keysyms/test_keysymdef.py000066400000000000000000000003051473525213700223440ustar00rootroot00000000000000from keysyms import keysymdef def test_keysymdef(): key_mapping = keysymdef.key_symbols assert key_mapping["0"] == 48 assert key_mapping["A"] == 65 assert key_mapping["a"] == 97 Solaar-1.1.14/tools/000077500000000000000000000000001473525213700141705ustar00rootroot00000000000000Solaar-1.1.14/tools/clean.sh000077500000000000000000000002701473525213700156100ustar00rootroot00000000000000#!/bin/sh cd "$(dirname "$0")/.." find . -type f -name '*.py[co]' -delete find . -type d -name '__pycache__' -delete /bin/rm --force po/*~ /bin/rm --force --recursive share/locale/ Solaar-1.1.14/tools/hidconsole000077500000000000000000000010461473525213700162460ustar00rootroot00000000000000#!/usr/bin/env python3 # -*- python-mode -*- """Takes care of starting the main function.""" from __future__ import absolute_import def init_paths(): """Make the app work in the source tree.""" import os.path as _path import sys src_lib = _path.normpath(_path.join(_path.realpath(sys.path[0]), "..", "lib")) init_py = _path.join(src_lib, "hidapi", "__init__.py") if _path.exists(init_py): sys.path[0] = src_lib if __name__ == "__main__": init_paths() from hidapi import hidconsole hidconsole.main() Solaar-1.1.14/tools/po-compile.sh000077500000000000000000000005611473525213700165750ustar00rootroot00000000000000#!/bin/sh set -e cd "$(readlink -f "$(dirname "$0")/..")" find "$PWD/po" -type f -name '*.po' | \ while read po_file; do language="$(basename "$po_file")" language="${language%.po}" target="$PWD/share/locale/$language/LC_MESSAGES/solaar.mo" /bin/mkdir --parents "$(dirname "$target")" /usr/bin/msgfmt \ --check \ --output-file="$target" \ "$po_file" done Solaar-1.1.14/tools/po-update.sh000077500000000000000000000034521473525213700164310ustar00rootroot00000000000000#!/bin/sh set -e if test "$1" = "-h" -o "$1" = "--help"; then echo "Use: $0 []" echo "Run without arguments to update all translation files." exit 0 fi cd "$(readlink -f "$(dirname "$0")/..")" VERSION=$(python setup.py --version) DOMAIN=$(python setup.py --name) SOURCE_FILES=$(/bin/mktemp --tmpdir $DOMAIN-po-update-XXXXXX) find "lib" -name '*.py' >"$SOURCE_FILES" POT_DIR="$PWD/po" test -d "$POT_DIR" POT_FILE="$POT_DIR/$DOMAIN.pot" /usr/bin/xgettext \ --package-name "$DOMAIN" \ --package-version "$VERSION" \ --default-domain="$L_NAME" \ --language=Python --from-code=UTF-8 --files-from="$SOURCE_FILES" \ --no-escape --indent --add-location --sort-by-file \ --add-comments=I18N \ --output="$POT_FILE" /bin/sed --in-place --expression="s/charset=CHARSET/charset=UTF-8/" "$POT_FILE" unfmt() { local SOURCE="/usr/share/locale/$LL_CC/LC_MESSAGES/$1.mo" if test ! -f "$SOURCE"; then SOURCE="/usr/share/locale-langpack/$LL_CC/LC_MESSAGES/$1.mo" fi local TARGET="$(mktemp --tmpdir $1-$LL_CC-XXXXXX.po)" /usr/bin/msgunfmt \ --no-escape --indent \ --output-file="$TARGET" \ "$SOURCE" echo "$TARGET" } update_po() { local LL_CC="$1" local PO_FILE="$POT_DIR/$LL_CC.po" test -r "$PO_FILE" || /usr/bin/msginit \ --no-translator --locale="$LL_CC" \ --input="$POT_FILE" \ --output-file="$PO_FILE" /usr/bin/msgmerge \ --update --no-fuzzy-matching \ --no-escape --indent --add-location --sort-by-file \ --lang="$LL_CC" \ --compendium="$(unfmt gtk30)" \ --compendium="$(unfmt gtk30-properties)" \ "$PO_FILE" "$POT_FILE" # /bin/sed --in-place --expression="s/Language: \\\\n/Language: $L_NAME\\\\n/" "$PO_FILE" echo "Updated $PO_FILE" } if test "$1"; then update_po "$1" else for l in $(ls -1 "$POT_DIR"/??.po); do l="$(basename "$l")" update_po "${l%.po}" done fi Solaar-1.1.14/tools/scan-registers.sh000077500000000000000000000027201473525213700174610ustar00rootroot00000000000000#!/bin/sh if test -z "$1"; then echo "Use: $0 []" exit 2 fi HC="$(dirname "$(readlink -f "$0")")/hidconsole" if test "$1" = "FF" -o "$1" = "ff"; then DEVNUMBER=FF else DEVNUMBER=0$1 fi HIDRAW=$2 do_req() { "$HC" --hidpp $HIDRAW | grep "^>> " | grep -v "\[1. .. 8F.. " } req00="$(mktemp --tmpdir req00-XXXXXX)" echo "10 ${DEVNUMBER} 8100 000000" | do_req >"$req00" oldflags=$(grep -Po "^>> \([0-9. ]*\) \[10 ${DEVNUMBER} 8100 \K[0-9a-f]{6}(?=\])" "$req00") if [ -n "$oldflags" ]; then echo "# Old notification flags: $oldflags" cat >"$req00-flags" <<-_CHECK_NOTIFICATIONS 10 ${DEVNUMBER} 8000 ffffff 10 ${DEVNUMBER} 8100 000000 10 ${DEVNUMBER} 8000 ${oldflags} _CHECK_NOTIFICATIONS # set all possible flags, read the new value, then restore the old value # this will show all supported notification flags by this device cat "$req00-flags" | do_req | grep "^>>.* ${DEVNUMBER} 8100 " else echo "# Warning: hidconsole API got changed - unrecognized output" cat "$req00" fi rm --force "$req00" "$req00-flags" & echo SHORT REGISTERS # read all short registers, skipping 00 for n in $(seq 1 255); do printf "10 ${DEVNUMBER} 81%02x 000000\n" $n done | do_req echo LONG REGISTERS # read all long registers for n in $(seq 0 255); do printf "10 ${DEVNUMBER} 83%02x 000000\n" $n done | do_req echo PAIRING INFORMAITON # read all pairing information for n in $(seq 0 255); do printf "10 ${DEVNUMBER} 83B5 %02x0000\n" $n done | do_req