Repository: pwr-Solaar/Solaar Branch: master Commit: b9e0cf823543 Files: 309 Total size: 3.6 MB Directory structure: gitextract_9uiytb4r/ ├── .coveragerc ├── .git-blame-ignore-revs ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.md │ │ └── feature_request.md │ └── workflows/ │ ├── checks.yml │ ├── gh-pages.yml │ └── tests.yml ├── .gitignore ├── .pre-commit-config.yaml ├── .python-version ├── CHANGELOG.md ├── COPYRIGHT ├── LICENSE.txt ├── MANIFEST.in ├── Makefile ├── README.md ├── RELEASE.md ├── RHEL.md ├── Release_Notes.md ├── bin/ │ └── solaar ├── docs/ │ ├── .gitignore │ ├── LICENSE.txt │ ├── README.text │ ├── capabilities.md │ ├── debian.md │ ├── devices/ │ │ ├── 00README.txt │ │ ├── Bluetooth Multi-Device Keyboard K380 B342.txt │ │ ├── Bolt Receiver C548.txt │ │ ├── Candy companion chip 405F.txt │ │ ├── Couch Mouse M515 4007.txt │ │ ├── Craft Advanced Keyboard 4066.txt │ │ ├── Craft Advanced Keyboard B350.txt │ │ ├── ERGO M575 Trackball 4096.txt │ │ ├── EX100 Receiver 27 Mhz C517.text │ │ ├── G Pro Wireless Gaming Mouse 4079.txt │ │ ├── G213 Prodigy Gaming Keyboard C366.txt │ │ ├── G304 Lightspeed Wireless Gaming Mouse 4074.txt │ │ ├── G305 Lightspeed Wireless Gaming Mouse 4074.text │ │ ├── G502 Gaming Mouse C07D.text │ │ ├── G502 Lightspeed Wireless Gaming Mouse 407F.txt │ │ ├── G502 Proteus Spectrum Optical Mouse C332.txt │ │ ├── G502 SE Hero Gaming Mouse C08B.txt │ │ ├── G502 X C099.txt │ │ ├── G502 X PLUS 4099.txt │ │ ├── G515 LS TKL 40B4.text │ │ ├── G535 Wireless Gaming Headset 0AC4.txt │ │ ├── G600 Gaming Mouse C24A.txt │ │ ├── G604 Wireless Gaming Mouse 4085.txt │ │ ├── G613 Wireless Mechanical Gaming Keyboard 4065.txt │ │ ├── G703 Wired-Wireless Gaming Mouse 4070.txt │ │ ├── G733 Gaming Headset 0AB5.text │ │ ├── G733 Gaming Headset 0AFE.text │ │ ├── G815 Mechanical Keyboard C33F.txt │ │ ├── G903 LIGHTSPEED Wireless Gaming Mouse 4087.txt │ │ ├── G915 TKL LIGHTSPEED Wireless RGB Mechanical Gaming Keyboard 408E.txt │ │ ├── G915 WIRELESS RGB MECHANICAL GAMING KEYBOARD 407C.text │ │ ├── G915 WIRELESS RGB MECHANICAL GAMING KEYBOARD C33E.text │ │ ├── G915 WIRELESS RGB Mechanical Gaming Keyboard 407E.txt │ │ ├── G935 Gaming Headset 0A87.txt │ │ ├── Illuminated Keyboard C318.txt │ │ ├── Illuminated Living-Room Keyboard K830 4032.txt │ │ ├── K850 Performance Wireless Keyboard 4062.txt │ │ ├── K850 Performance Wireless Keyboard B34D.txt │ │ ├── Keyboard K600 TV 4078.txt │ │ ├── LIFT For Business B033.txt │ │ ├── LIFT VERTICAL ERGONOMIC MOUSE B031.txt │ │ ├── Lightspeed Receiver C539.txt │ │ ├── Lightspeed Receiver C53A.txt │ │ ├── Lightspeed Receiver C53D.txt │ │ ├── Lightspeed Receiver C53F.txt │ │ ├── Lightspeed Receiver C541.txt │ │ ├── Lightspeed Receiver C545.txt │ │ ├── Lightspeed Receiver C547.txt │ │ ├── Logi Pop Keys B365.txt │ │ ├── Logitech G933 Gaming Wireless Headset 0A5B.txt │ │ ├── Logitech PRO X Wireless Gaming Headset 0ABA.txt │ │ ├── M720 Triathlon Multi-Device Mouse 405E.txt │ │ ├── M720 Triathlon Multi-Device Mouse B015.txt │ │ ├── MX Anywhere 3 4090.txt │ │ ├── MX Anywhere 3 B025.txt │ │ ├── MX Anywhere 3 for Business B02D.txt │ │ ├── MX Ergo Multi-Device Trackball 406F.txt │ │ ├── MX Keys Keyboard 408A.txt │ │ ├── MX Keys Keyboard B35B.txt │ │ ├── MX Keys Mini B369.txt │ │ ├── MX Keys S B378.text │ │ ├── MX Keys for Business B363.text │ │ ├── MX Master 3 Wireless Mouse 4082.txt │ │ ├── MX Master 3 Wireless Mouse B023.txt │ │ ├── MX Master 3 for Business B028.text │ │ ├── MX Master 3S B034.txt │ │ ├── MX Master 4.text │ │ ├── MX Mechanical B366.txt │ │ ├── MX Mechanical Mini B367.txt │ │ ├── MX Vertical Wireless Mouse 407B.txt │ │ ├── MX Vertical Wireless Mouse B020.txt │ │ ├── Marathon Mouse M705 101B.txt │ │ ├── Marathon Mouse M705 406D.txt │ │ ├── Multi Device Silent Mouse M585-M590 406B.txt │ │ ├── Nano Receiver C52F.txt │ │ ├── Nano Receiver C534.txt │ │ ├── Nano Receiver C535.txt │ │ ├── Number Pad N545 2006.txt │ │ ├── PRO X 2 40A9.text │ │ ├── PRO X Wireless 4093.txt │ │ ├── Rechargeable Trackpad T651 B00C.txt │ │ ├── Signature M550.text │ │ ├── Signature M650 L Mouse B02A.txt │ │ ├── Unifying Receiver C52B.txt │ │ ├── Wireless All-in-One Keyboard TK820 4102.txt │ │ ├── Wireless Illuminated Keyboard K800 2010.txt │ │ ├── Wireless Illuminated Keyboard K800 new 406E.txt │ │ ├── Wireless Keyboard 4075.txt │ │ ├── Wireless Keyboard Dell KB714 4015.txt │ │ ├── Wireless Keyboard K220 4005.txt │ │ ├── Wireless Keyboard K230 400D.txt │ │ ├── Wireless Keyboard K270(unifying) 4003.txt │ │ ├── Wireless Keyboard K360 4004.txt │ │ ├── Wireless Keyboard K470 4075.txt │ │ ├── Wireless Keyboard K520 2011.txt │ │ ├── Wireless Keyboard MK270 4023.txt │ │ ├── Wireless Mobile Mouse MX Anywhere 2 4072.txt │ │ ├── Wireless Mobile Mouse MX Anywhere 2S 406A.txt │ │ ├── Wireless Mobile Mouse MX Anywhere 2S B01A.txt │ │ ├── Wireless Mouse 4022.txt │ │ ├── Wireless Mouse Dell WM514 4029.txt │ │ ├── Wireless Mouse M185 new 4054.txt │ │ ├── Wireless Mouse M185,M235,M310 4055.txt │ │ ├── Wireless Mouse M185.text │ │ ├── Wireless Mouse M215 2nd Gen 401B.txt │ │ ├── Wireless Mouse M310 M310t 4031.txt │ │ ├── Wireless Mouse M325 400A.txt │ │ ├── Wireless Mouse M345 4017.txt │ │ ├── Wireless Mouse M510 1025.txt │ │ ├── Wireless Mouse M510 4051.txt │ │ ├── Wireless Mouse M525 4013.txt │ │ ├── Wireless Mouse M560 402D.txt │ │ ├── Wireless Mouse MX Anywhere 2 404A.txt │ │ ├── Wireless Mouse MX Master 2S 4069.txt │ │ ├── Wireless Mouse MX Master 2S B019.txt │ │ ├── Wireless Mouse MX Master 4041.txt │ │ ├── Wireless Mouse MX Master 4071.txt │ │ ├── Wireless Mouse MX Master B012.txt │ │ ├── Wireless Mouse Pebble M350 4080.txt │ │ ├── Wireless Multi-Device Keyboard K780 405B.txt │ │ ├── Wireless Rechargeable Touchpad T650 4101.txt │ │ ├── Wireless Solar Keyboard K750 4002.txt │ │ ├── Wireless Touch Keyboard K400 4024.txt │ │ ├── Wireless Touch Keyboard K400 Plus 404D.txt │ │ ├── Wireless Trackball M570 1028.txt │ │ ├── Zone Touch Mouse T400 4026.txt │ │ ├── anywhere-mx.txt │ │ ├── mk700.txt │ │ └── performance-mx.txt │ ├── devices.md │ ├── features.md │ ├── hidpp-documentation.txt │ ├── i18n.md │ ├── implementation.md │ ├── index.md │ ├── installation.md │ ├── issues.md │ ├── rules.md │ ├── uninstallation.md │ ├── usage.md │ └── usb.ids.txt ├── lib/ │ ├── hid_parser/ │ │ ├── __init__.py │ │ └── data.py │ ├── hidapi/ │ │ ├── __init__.py │ │ ├── common.py │ │ ├── hidapi_impl.py │ │ ├── hidconsole.py │ │ └── udev_impl.py │ ├── keysyms/ │ │ ├── __init__.py │ │ ├── generate.py │ │ └── keysymdef.py │ ├── logitech_receiver/ │ │ ├── __init__.py │ │ ├── base.py │ │ ├── base_usb.py │ │ ├── common.py │ │ ├── descriptors.py │ │ ├── desktop_notifications.py │ │ ├── device.py │ │ ├── diversion.py │ │ ├── exceptions.py │ │ ├── hidpp10.py │ │ ├── hidpp10_constants.py │ │ ├── hidpp20.py │ │ ├── hidpp20_constants.py │ │ ├── i18n.py │ │ ├── listener.py │ │ ├── notifications.py │ │ ├── receiver.py │ │ ├── settings.py │ │ ├── settings_new.py │ │ ├── settings_templates.py │ │ ├── settings_validator.py │ │ └── special_keys.py │ └── solaar/ │ ├── __init__.py │ ├── cli/ │ │ ├── __init__.py │ │ ├── config.py │ │ ├── pair.py │ │ ├── probe.py │ │ ├── profiles.py │ │ ├── show.py │ │ └── unpair.py │ ├── configuration.py │ ├── custom_logger.py │ ├── dbus.py │ ├── gtk.py │ ├── i18n.py │ ├── listener.py │ ├── tasks.py │ ├── ui/ │ │ ├── __init__.py │ │ ├── about/ │ │ │ ├── __init__.py │ │ │ ├── about.py │ │ │ ├── model.py │ │ │ ├── presenter.py │ │ │ └── view.py │ │ ├── action.py │ │ ├── common.py │ │ ├── config_panel.py │ │ ├── desktop_notifications.py │ │ ├── diversion_rules.py │ │ ├── icons.py │ │ ├── pair_window.py │ │ ├── rule_actions.py │ │ ├── rule_base.py │ │ ├── rule_conditions.py │ │ ├── tray.py │ │ └── window.py │ └── version ├── mkdocs.yml ├── po/ │ ├── README │ ├── ca.po │ ├── cs.po │ ├── da.po │ ├── de.po │ ├── el.po │ ├── es.po │ ├── fi.po │ ├── fr.po │ ├── hr.po │ ├── id.po │ ├── it.po │ ├── ja.po │ ├── ka.po │ ├── nb.po │ ├── nl.po │ ├── nn.po │ ├── pl.po │ ├── pt.po │ ├── pt_BR.po │ ├── ro.po │ ├── ru.po │ ├── sk.po │ ├── solaar.pot │ ├── sr.po │ ├── sv.po │ ├── tr.po │ ├── uk.po │ ├── zh_CN.po │ └── zh_TW.po ├── pyproject.toml ├── release.sh ├── rules.d/ │ └── 42-logitech-unify-permissions.rules ├── rules.d-uinput/ │ └── 42-logitech-unify-permissions.rules ├── setup.py ├── share/ │ ├── README │ ├── applications/ │ │ └── solaar.desktop │ ├── autostart/ │ │ └── solaar.desktop │ └── solaar/ │ └── io.github.pwr_solaar.solaar.metainfo.xml ├── tests/ │ ├── __init__.py │ ├── hid_parser/ │ │ ├── __init__.py │ │ └── test_data.py │ ├── hidapi/ │ │ ├── __init__.py │ │ └── test_hidapi.py │ ├── logitech_receiver/ │ │ ├── __init__.py │ │ ├── fake_hidpp.py │ │ ├── test_base.py │ │ ├── test_base_usb.py │ │ ├── test_common.py │ │ ├── test_desktop_notifications.py │ │ ├── test_device.py │ │ ├── test_diversion.py │ │ ├── test_hidpp10.py │ │ ├── test_hidpp20_complex.py │ │ ├── test_hidpp20_simple.py │ │ ├── test_notifications.py │ │ ├── test_receiver.py │ │ ├── test_setting_templates.py │ │ └── test_settings_validator.py │ ├── solaar/ │ │ ├── test_gtk.py │ │ └── ui/ │ │ ├── test_about_dialog.py │ │ ├── test_common.py │ │ ├── test_desktop_notifications.py │ │ ├── test_i18n.py │ │ ├── test_pair_window.py │ │ └── test_probe.py │ └── test_keysyms/ │ ├── __init__.py │ └── test_keysymdef.py └── tools/ ├── clean.sh ├── create-macos-app.sh ├── create-macos-launchagent.sh ├── hidconsole ├── install-rhel.sh ├── po-compile.sh ├── po-update.sh └── scan-registers.sh ================================================ FILE CONTENTS ================================================ ================================================ FILE: .coveragerc ================================================ [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 ================================================ FILE: .git-blame-ignore-revs ================================================ # yapf bulk change 72a8d311bce64b1536c08e754d22bb91efb66460 # isort bulk change e6369e0c3c240715a0a2daede6c3b225ed63cf60 # pre-commit bulk change 33521558ed7007c24b24127f1448c2c23ecbfb35 # flake8 bulk change 627185079f65c9cc471194b7927c833682e4a7a3 # yapf style update 27c90fa736d8a7a1ef6acda4345d3599862e185c ================================================ FILE: .github/ISSUE_TEMPLATE/bug_report.md ================================================ --- 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`): - 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. ================================================ FILE: .github/ISSUE_TEMPLATE/feature_request.md ================================================ --- 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. ================================================ FILE: .github/workflows/checks.yml ================================================ name: 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 ================================================ FILE: .github/workflows/gh-pages.yml ================================================ name: 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 ================================================ FILE: .github/workflows/tests.yml ================================================ name: tests on: [push, pull_request] jobs: ubuntu-tests: runs-on: ubuntu-latest strategy: matrix: python-version: [3.13] fail-fast: false 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 for python 3.8 if: matrix.python-version == '3.8' run: | make install_apt - name: Install Ubuntu dependencies for python 3.13 if: matrix.python-version == '3.13' run: | make install_apt_python3.13 - 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.13] fail-fast: false 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: Add Homebrew's library directory to dyld search path run: | echo "DYLD_FALLBACK_LIBRARY_PATH=$(brew --prefix)/lib:$DYLD_FALLBACK_LIBRARY_PATH" >> $GITHUB_ENV - name: Install Python dependencies run: | make install_pip PIP_ARGS='.["test"]' - name: Run tests on macOS run: | 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 }} ================================================ FILE: .gitignore ================================================ *.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/ .DS_Store ._* Pipfile Pipfile.lock ================================================ FILE: .pre-commit-config.yaml ================================================ repos: - 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 ================================================ FILE: .python-version ================================================ 3.11 ================================================ FILE: CHANGELOG.md ================================================ # 1.1.19 * New Georgian translation * Remove test that doesn't work in older Pythons * Update help messages for CLI commands * Allow solaar config to change LED settings * Add python3-devel to install-dnf in Makefile * hidconsole can send an HID command non-interactively * Add info about new lightspeed receiver * Update Swedish and zh_TW translation * Fix bug when showing details about direct-connected device * Drop testing of Python before 3.13 * Fix crash in solaar show when showing notification flags. (#3070) # 1.1.18 * Fix crash when showing notification flags # 1.1.17 * Add dark icons * Permit onboard profiles version 5 * Update Russian and Polish translations * Add onboard profiles warning to sensitivity tooltip * Better error messages for solaar profile * Remove Solaar name for mice with WPID 4008 * Prevent lock failure when showing debug messages * Replace color picker (#3028) * Add setting for HAPTIC feature * Add setting to adjust force needed for force-sensing buttons * Expand new settings type * Add new settings type for structure-backed setting * Use PATH instead of hardcoded absolute paths (#3014) * Add scroll ratchet force setting * Fix debug messages for MouseClick rule * Improve debug message for rule evaluation * App wrapper and launch agent scripts for MacOS * Ignore hidden features * Don't pop up window in response to ADC changes * Update bug report template * Fixed malformed doc file by adding closing tag * Fix error in low-level request for device with no recevier * Update documentation files # 1.1.16 * Add new flags for reprogrammable keys feature * Correctly handle missing battery feature # 1.1.15 * Correctly re-raise permissions exception * Add several new special keys and tasks * Update several translations * Center labels and remove buggy entry resizing logic * Add shape keys from Key POP Icon * Device and Action rule conditions match on codename and name * Fix listing hidpp10 devices - bytes vs string concatenation (#2856) * Add present flag, unset when internal error occurs, set when notification appears * Pause setting up features when error occurs; use ADC message to signal connection and disconnection * Fix listing of hidpp10 peripherals * Complete DEVICE_FEATURES to DeviceFeature transition for hidpp10 devices * Fix NOTIFICATION_FLAG to NotificationFlag transition leftovers * Fix github workflow stopping all matrix jobs when one of them fails * Fix ubuntu github CI * Update index.md * Python documentation appears to be broken so don't set it up * Improve documentation on onboard profiles * Use correct LOD values for extended adjustable dpi * Better support RGB Effects - not readable * Fix crash when asking for help about config * Fix error when updating ChoiceControlBig box * Add uninstallation docs * Handle unknown power switch locations again * Correctly handle selection of [empty] in rule editor * Handle `HIDError` in `hidapi.hidapi_impl._match()` (#2804) * Give ghost devices a path * Guard against typeerror when setting the value of a control box * Recover from errors in ping * Replace spaces by underscores when looking up features * Rewrote string concatenation/format with f strings * Fix logo not showing in about dialog box * Make typing-extensions dependency mandatory * Properly ignore unsupported locale * hidapi: skip unsupported devices and handle exception on open * Ignore macOS junk files and pipenv config * Fix ui desktop notifications test * hidpp20: Remove dependency to NamedInts * Estimate accurate battery level for some rechargable devices (#2745) * Upgrade desktop notifications tests to take notifications availability into account * Update tests to run on Python 3.13 * Remove outdated logger enabled checks * Introduce GTK signal types * Introduce error types * Remove alias for SupportedFeature * Refactor process_device_notification * Refactor process_receiver_notification * Refactor receiver event handling * Introduce custom logger * Refactor notifications * Rename variable to full name notification * Test notifications * Test extraction of serial and max. devices * Refactor extraction of serial and max. devices * macOS: Fix int.from_bytes, int.to_bytes for show.py * macOS: Remove udev rule warning * macOS: Add support for Bluetooth devices * Add back and forward mouseclick actions * Speedup lookup of known receivers * Refactor device filtering * Reorder private functions and variable definitions * Turn filter_products_of_interest into a public function * Improve tests of known receivers * Refactor: Remove NamedInts and move enums where used * Add docstrings and type hints * Enforce rules on RuleComponentUI subclasses * Simplify settings UI class * Remove diversion alias * Refactor: Convert Kind to IntEnum * Split up huge settings module * Remove Python 2 specific path handling * Delete logging temp file on exit * Update Swedish translation # 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. ================================================ FILE: COPYRIGHT ================================================ Copyright 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 . ================================================ FILE: LICENSE.txt ================================================ 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. ================================================ FILE: MANIFEST.in ================================================ include COPYRIGHT LICENSE.txt README.md CHANGELOG.md lib/solaar/version lib/solaar/commit recursive-include rules.d * recursive-include share/locale * ================================================ FILE: Makefile ================================================ UDEV_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_apt_python3.13: @echo "Installing Solaar dependencies via apt" sudo apt update sudo apt install libdbus-1-dev libglib2.0-dev libgtk-3-dev libgirepository-2.0-dev gobject-introspection install_dnf: @echo "Installing Solaar dependencies via dnf" sudo dnf install gtk3 python3-devel 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 ================================================ FILE: README.md ================================================ # 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 - Known Issues [![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 ================================================ FILE: RELEASE.md ================================================ # 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` ================================================ FILE: RHEL.md ================================================ # Solaar installation guide for RHEL, Rocky, AlmaLinux, and CentOS Stream This guide covers manual installation and an automated install example for RHEL-family systems using `dnf`. ## Supported distributions - Red Hat Enterprise Linux (RHEL) - Rocky Linux - AlmaLinux - Oracle Linux - CentOS Stream The commands assume a minimal CLI system with `sudo` access. ## 1) Install dependencies ```bash sudo dnf makecache --refresh sudo dnf install -y \ git \ gtk3 \ python3 \ python3-devel \ python3-dbus \ python3-gobject \ python3-pip \ python3-psutil \ python3-pyudev \ python3-setuptools \ python3-xlib \ python3-yaml ``` Optional troubleshooting helpers: ```bash sudo dnf install -y \ evemu \ libinput \ usbutils ``` ## 2) Clone Solaar ```bash git clone https://github.com/pwr-Solaar/Solaar.git cd Solaar ``` ## 3) Install Solaar Install for the current user: ```bash python3 -m pip install --user . ``` Or install system-wide: ```bash sudo python3 -m pip install . ``` ## 4) Install udev rules Install the recommended `uinput` rule: ```bash sudo make install_udev_uinput ``` Verify rule installation: ```bash ls -l /etc/udev/rules.d/42-logitech-unify-permissions.rules ``` Rollback udev rule installation: ```bash sudo make uninstall_udev ``` ## 5) Run Solaar ```bash solaar ``` or: ```bash python3 -m solaar ``` ## 6) Automated install options Use the guided installer in this repository: ```bash ./tools/install-rhel.sh ``` Minimal non-interactive example script: ```bash cat > install-rhel-solaar.sh <<'SCRIPT' #!/usr/bin/env bash set -euo pipefail if [[ "${EUID}" -eq 0 ]]; then echo "Run as a regular user with sudo access, not as root." exit 1 fi sudo dnf makecache --refresh sudo dnf install -y \ git \ gtk3 \ python3 \ python3-devel \ python3-dbus \ python3-gobject \ python3-pip \ python3-psutil \ python3-pyudev \ python3-setuptools \ python3-xlib \ python3-yaml if [[ ! -d Solaar/.git ]]; then git clone https://github.com/pwr-Solaar/Solaar.git fi cd Solaar python3 -m pip install --user . sudo make install_udev_uinput ~/.local/bin/solaar --version SCRIPT chmod +x install-rhel-solaar.sh ./install-rhel-solaar.sh ``` ## 7) Verification ```bash command -v solaar solaar --version python3 -m pip show solaar ``` If installed with `--user`, ensure `~/.local/bin` is on your `PATH`: ```bash echo "$PATH" | tr ':' '\n' | grep -Fx "$HOME/.local/bin" >/dev/null || \ echo 'Add ~/.local/bin to PATH' ``` ## 8) Troubleshooting Receiver not detected: ```bash lsusb | grep -Ei 'logitech|046d' sudo udevadm trigger ``` Check access to hidraw devices: ```bash ls -l /dev/hidraw* getfacl /dev/hidraw* 2>/dev/null | sed -n '1,80p' ``` ================================================ FILE: Release_Notes.md ================================================ # Notes on Major Changes in Releases ## Version 1.1.18 * Solaar is only guaranteed to work in Python 3.13 or later. ## Version 1.1.17 * Several new features have been added related to the MX Master 4 * The scroll ratchet force can be adjusted * The force required to click the button under your thumb can be adjusted * The haptic force can be adjusted * Haptic feeback can be triggered by commands like `solaar config 'mx master 4' haptic-play 'HAPPY ALER'` ## Version 1.1.16 * Two bugs that were affecting users in 1.1.15 are fixed. ## Version 1.1.15 * Some key names have been changed to match Logitech names. Rules that use removed names will no longer work and will end up with a key of 0. * Device and Action rule conditions match on device codename and name * Solaar supports configuration of Bluetooth devices on macOS. ## 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. ================================================ FILE: bin/solaar ================================================ #!/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 root = os.path.join(os.path.realpath(sys.path[0]), "..") 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() ================================================ FILE: docs/.gitignore ================================================ # ignore documentation-specific files .jekyll-metadata Gemfile Gemfile.lock _site/ ================================================ FILE: docs/LICENSE.txt ================================================ GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 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. 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. one line to give the program's name and an idea of what it does. Copyright (C) yyyy name of author 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, see . 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. signature of Moe Ghoul, 1 April 1989 Moe Ghoul, 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. ================================================ FILE: docs/README.text ================================================ # 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/) ================================================ FILE: docs/capabilities.md ================================================ --- 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 and Bolt 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 Nano and Lightspeed receivers, like the Nano receiver with USB ID `046d:c534`, can only pair with one keyboard and one mouse 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. It should be possible to connect any device with a Unifying logo on it to any receiver with a Unifying logo on it and any device with a Bolt logo on it to any receiver with a Bolt logo on it. Many receivers without the Unifying or Bolt logo can connect only to the model of devices they were bought with and many devices without the Unifying or Bolt logo can only connect to a receiver that matches the one 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 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. ### Onboard Profiles Some mice store one or more profiles onboard. An onboard profile controls certain aspects of the behavior of the mouse, including the rate at which the mouse reports movement, the resolution of the the movement reports, what the mouse buttons do, LED effects, and maybe more. Solaar has a setting that switches between profiles or disables all profiles. When an onboard profile is active it may not be possible to change the aspects that the profile controls. This is often seen for the Report Rate setting. For some devices it is possible to make changes to the Sensitivity setting and to LED settings. These changes are likely to only be temporary and may be overridden when the device reconnects or when Solaar is restarted. This is in keeping with the intent of Onboard Profiles as controlling the device behavior. To make the changes to these settings permanent it is necessary to disable onboard profiles. Alternatively, multiple profiles can be set up as described below and these settings controlled by switching between the profiles. Solaar can dump the entire set of profiles into a YAML file and can load the entire set of profiles from a file. Users can edit the file to effect changes to the profiles. 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 usually for the logo LEDs and the second usually 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 ================================================ FILE: docs/debian.md ================================================ --- 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` ================================================ FILE: docs/devices/00README.txt ================================================ Files 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. ================================================ FILE: docs/devices/Bluetooth Multi-Device Keyboard K380 B342.txt ================================================ Solaar 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%. ================================================ FILE: docs/devices/Bolt Receiver C548.txt ================================================ Solaar 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 ================================================ FILE: docs/devices/Candy companion chip 405F.txt ================================================ 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. ================================================ FILE: docs/devices/Couch Mouse M515 4007.txt ================================================ 1: 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, ================================================ FILE: docs/devices/Craft Advanced Keyboard 4066.txt ================================================ Solaar 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%. ================================================ FILE: docs/devices/Craft Advanced Keyboard B350.txt ================================================ Solaar 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%. ================================================ FILE: docs/devices/ERGO M575 Trackball 4096.txt ================================================ Solaar 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. ================================================ FILE: docs/devices/EX100 Receiver 27 Mhz C517.text ================================================ solaar 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 ================================================ FILE: docs/devices/G Pro Wireless Gaming Mouse 4079.txt ================================================ Solaar 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. ================================================ FILE: docs/devices/G213 Prodigy Gaming Keyboard C366.txt ================================================ Solaar 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. ================================================ FILE: docs/devices/G304 Lightspeed Wireless Gaming Mouse 4074.txt ================================================ solaar 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%. ================================================ FILE: docs/devices/G305 Lightspeed Wireless Gaming Mouse 4074.text ================================================ solaar 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%. ================================================ FILE: docs/devices/G502 Gaming Mouse C07D.text ================================================ Solaar 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. ================================================ FILE: docs/devices/G502 Lightspeed Wireless Gaming Mouse 407F.txt ================================================ solaar 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. ================================================ FILE: docs/devices/G502 Proteus Spectrum Optical Mouse C332.txt ================================================ solaar 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. ================================================ FILE: docs/devices/G502 SE Hero Gaming Mouse C08B.txt ================================================ 1: 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. ================================================ FILE: docs/devices/G502 X C099.txt ================================================ Solaar 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. ================================================ FILE: docs/devices/G502 X PLUS 4099.txt ================================================ solaar version 1.1.19-25-g7520c9cc 1: G502 X PLUS Device path : /dev/hidraw8 WPID : 4099 Codename : G502 X PLUS Kind : mouse Protocol : HID++ 4.2 Report Rate : 1ms Serial number: C6884511 Model ID: 4099C0950000 Unit ID: C6884511 1: BL1 42.00.B0016 0: MPM 27.00.B0016 3: 3: 3: 3: 3: 3: 3: 3: 3: 3: 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: 1 BL1 42.00.B0016 AB0BFBB13A33 Firmware: 0 MPM 27.00.B0016 4099FBB13A33 Firmware: 3 Firmware: 3 Firmware: 3 Firmware: 3 Firmware: 3 Firmware: 3 Firmware: 3 Firmware: 3 Firmware: 3 Firmware: 3 Unit ID: C6884511 Model ID: 4099C0950000 Transport IDs: {'wpid': '4099', 'usbid': 'C095'} 3: DEVICE NAME {0005} V0 Name: G502 X PLUS Kind: mouse 4: WIRELESS DEVICE STATUS {1D4B} V0 5: CONFIG CHANGE {0020} V0 Configuration: 11000000000000000000000000000000 6: UNIFIED BATTERY {1004} V3 Battery: 77%, BatteryStatus.DISCHARGING. 7: ADJUSTABLE DPI {2201} V2 Sensitivity (DPI) (saved): 800 Sensitivity (DPI) : 800 8: 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 9: RGB EFFECTS {8071} V2 LED Control (saved): Solaar LED Control : Solaar LEDs Primary (saved): !LEDEffectSetting {ID: 0, color: 10820909, intensity: 0, period: 100, ramp: 0, speed: 0} LEDs Primary : !LEDEffectSetting {ID: 0, color: 10820909, intensity: 0, period: 100, ramp: 0, speed: 0} 10: PER KEY LIGHTING V2 {8081} V2 Per-key Lighting (saved): {A:No change, B:No change, C:No change, D:No change, E:No change, F:No change, G:No change, H:No change} Per-key Lighting : {A:No change, B:No change, C:No change, D:No change, E:No change, F:No change, G:No change, H:No change} 11: ONBOARD PROFILES {8100} V0 Device Mode: On-Board Onboard Profiles (saved): Profile 1 Onboard Profiles : Profile 1 12: MOUSE BUTTON SPY {8110} V0 13: REPORT RATE {8060} V0 Report Rate: 1ms Report Rate (saved): 1ms Report Rate : 1ms 14: FORCE PAIRING {1500} V0 15: DFU {00D0} V3 16: DEVICE RESET {1802} V0 17: unknown:1803 {0318} V0 internal, hidden, unknown:000010 18: CONFIG DEVICE PROPS {1806} V8 19: unknown:1811 {1118} V0 internal, hidden, unknown:000010 20: OOBSTATE {1805} V0 21: unknown:1830 {3018} V0 internal, hidden, unknown:000010 22: unknown:1875 {7518} V0 internal, hidden, unknown:000010 23: unknown:1861 {6118} V0 internal, hidden, unknown:000010 24: unknown:1890 {9018} V0 internal, hidden, unknown:000008 25: unknown:18A1 {A118} V0 internal, hidden, unknown:000010 26: unknown:1801 {0118} V0 internal, hidden, unknown:000010 27: unknown:1E00 {001E} V0 hidden 28: unknown:1E22 {221E} V0 internal, hidden, unknown:000010 29: unknown:1EB0 {B01E} V0 internal, hidden, unknown:000010 30: unknown:18B1 {B118} V0 internal, hidden, unknown:000010 31: unknown:18C0 {C018} V0 internal, hidden, unknown:000010 Battery: 77%, BatteryStatus.DISCHARGING. ================================================ FILE: docs/devices/G515 LS TKL 40B4.text ================================================ solaar version 1.1.13+dfsg-1 1: G515 LS TKL Device path : None WPID : 40B4 Codename : G515 LS TKL Kind : keyboard Protocol : HID++ 4.2 Report Rate : 8ms Serial number: 54FEF928 Model ID: B38940B4C355 Unit ID: 54FEF928 1: BL2 19.01.B0011 3: 0: MPK 25.01.B0011 3: The power switch is located on the top right corner. Supports 34 HID++ 2.0 features: 0: ROOT {0000} V0 1: FEATURE SET {0001} V0 2: DEVICE FW VERSION {0003} V6 Firmware: Bootloader BL2 19.01.B0011 ABD580558692 Firmware: Other Firmware: Firmware MPK 25.01.B0011 40B480558692 Firmware: Other Unit ID: 54FEF928 Model ID: B38940B4C355 Transport IDs: {'btleid': 'B389', 'wpid': '40B4', 'usbid': 'C355'} 3: DEVICE NAME {0005} V3 Name: G515 LS TKL Kind: keyboard 4: WIRELESS DEVICE STATUS {1D4B} V0 5: CONFIG CHANGE {0020} V0 Configuration: 11000000000000000000000000000000 6: DEVICE FRIENDLY NAME {0007} V0 Friendly Name: G515 LS TKL 7: unknown:0011 {0011} V0 8: UNIFIED BATTERY {1004} V5 Battery: 82%, discharging. 9: RGB EFFECTS {8071} V4 LED Control (saved): Solaar LED Control : Solaar LEDs Primary (saved): !LEDEffectSetting {ID: 1, color: 16776960, intensity: 26, period: 2167, ramp: 1, speed: 0} LEDs Primary : HID++ error {'number': 1, 'request': 2537, 'error': 7, 'params': b'\x00'} 10: PER KEY LIGHTING V2 {8081} V0 Per-key Lighting (saved): {A:indian red, B:indian red, C:indian red, D:indian red, E:indian red, F:indian red, G:indian red, H:indian red, I:indian red, J:indian red, K:indian red, L:indian red, M:indian red, N:indian red, O:indian red, P:indian red, Q:indian red, R:indian red, S:indian red, T:indian red, U:indian red, V:indian red, W:indian red, X:indian red, Y:indian red, Z:indian red, 1:orange, 2:orange, 3:orange, 4:orange, 5:orange, 6:orange, 7:orange, 8:orange, 9:orange, 0:yellow, ENTER:green, ESC:green, BACKSPACE:red, TAB:yellow, SPACE:yellow, -:indian red, =:indian red, [:indian red, \:indian red, KEY 46:white, ~:indian red, ;:indian red, ':indian red, `:indian red, ,:indian red, .:indian red, /:indian red, CAPS LOCK:red, F1:indian red, F2:indian red, F3:indian red, F4:indian red, F5:indian red, F6:indian red, F7:indian red, F8:indian red, F9:indian red, F10:indian red, F11:indian red, F12:indian red, PRINT:red, SCROLL LOCK:orange, PASTE:indian red, INSERT:green, HOME:indian red, PAGE UP:yellow, DELETE:red, END:indian red, PAGE DOWN:yellow, RIGHT:indian red, LEFT:indian red, DOWN:indian red, UP:indian red, KEY 97:indian red, COMPOSE:white, POWER:white, KEY 100:indian red, KEY 101:red, KEY 102:red, KEY 103:red, LEFT CTRL:indian red, LEFT SHIFT:yellow, LEFT ALT:indian red, LEFT WINDOWS:blue, RIGHT CTRL:indian red, RIGHT SHIFT:yellow, RIGHT ALTGR:blue, RIGHT WINDOWS:indian red, KEY 254:white} Per-key Lighting : {A:No change, B:No change, C:No change, D:No change, E:No change, F:No change, G:No change, H:No change, I:No change, J:No change, K:No change, L:No change, M:No change, N:No change, O:No change, P:No change, Q:No change, R:No change, S:No change, T:No change, U:No change, V:No change, W:No change, X:No change, Y:No change, Z:No change, 1:No change, 2:No change, 3:No change, 4:No change, 5:No change, 6:No change, 7:No change, 8:No change, 9:No change, 0:No change, ENTER:No change, ESC:No change, BACKSPACE:No change, TAB:No change, SPACE:No change, -:No change, =:No change, [:No change, \:No change, KEY 46:No change, ~:No change, ;:No change, ':No change, `:No change, ,:No change, .:No change, /:No change, CAPS LOCK:No change, F1:No change, F2:No change, F3:No change, F4:No change, F5:No change, F6:No change, F7:No change, F8:No change, F9:No change, F10:No change, F11:No change, F12:No change, PRINT:No change, SCROLL LOCK:No change, PASTE:No change, INSERT:No change, HOME:No change, PAGE UP:No change, DELETE:No change, END:No change, PAGE DOWN:No change, RIGHT:No change, LEFT:No change, DOWN:No change, UP:No change, KEY 97:No change, COMPOSE:No change, POWER:No change, KEY 100:No change, KEY 101:No change, KEY 102:No change, KEY 103:No change, LEFT CTRL:No change, LEFT SHIFT:No change, LEFT ALT:No change, LEFT WINDOWS:No change, RIGHT CTRL:No change, RIGHT SHIFT:No change, RIGHT ALTGR:No change, RIGHT WINDOWS:No change, KEY 254:No change} 11: unknown:1B10 {1B10} V0 12: unknown:4523 {4523} V1 13: KEYBOARD LAYOUT 2 {4540} V1 14: BRIGHTNESS CONTROL {8040} V0 Brightness Control (saved): 40 Brightness Control : 40 15: unknown:8101 {8101} V0 16: unknown:1B05 {1B05} V0 17: unknown:8051 {8051} V0 18: DFU {00D0} V3 19: DEVICE RESET {1802} V0 internal, hidden, unknown:000010 20: unknown:1803 {1803} V1 internal, hidden, unknown:000010 21: unknown:1807 {1807} V3 internal, hidden, unknown:000010 22: unknown:1817 {1817} V0 internal, hidden, unknown:000010 23: OOBSTATE {1805} V0 internal, hidden 24: unknown:1830 {1830} V0 internal, hidden, unknown:000010 25: unknown:1890 {1890} V9 internal, hidden, unknown:000008 26: unknown:1891 {1891} V9 internal, hidden, unknown:000008 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:18B0 {18B0} V1 internal, hidden, unknown:000010 33: unknown:1801 {1801} V0 internal, hidden, unknown:000010 Battery: 82%, discharging. ================================================ FILE: docs/devices/G535 Wireless Gaming Headset 0AC4.txt ================================================ solaar 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. ================================================ FILE: docs/devices/G600 Gaming Mouse C24A.txt ================================================ This 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 ================================================ FILE: docs/devices/G604 Wireless Gaming Mouse 4085.txt ================================================ solaar version 03cfa128 1: G604 Wireless Gaming Mouse Device path : /dev/hidraw6 WPID : 4085 Codename : G604 Kind : mouse Protocol : HID++ 4.2 Report Rate : 1ms Serial number: XXXXXXXX Model ID: B02440850000 Unit ID: XXXXXXXX 1: BL1 04.01.B0014 0: MPM 21.01.B0014 3: The power switch is located on the base. Supports 33 HID++ 2.0 features: 0: ROOT {0000} V0 1: FEATURE SET {0001} V0 2: DEVICE FW VERSION {0003} V2 Firmware: 1 BL1 04.01.B0014 0000B01B3067 Firmware: 0 MPM 21.01.B0014 4085B01B3067 Firmware: 3 Unit ID: XXXXXXXX Model ID: B02440850000 Transport IDs: {'btleid': 'B024', 'wpid': '4085'} 3: DEVICE NAME {0005} V0 Name: G604 Wireless Gaming Mouse Kind: mouse 4: WIRELESS DEVICE STATUS {1D4B} V0 5: CONFIG CHANGE {0020} V0 Configuration: 00000000000000000000000000000000 6: BATTERY STATUS {1000} V0 Battery: 30%, BatteryStatus.DISCHARGING, next level 15%. 7: COLOR LED EFFECTS {8070} V4 LED Control (saved): Device LED Control : Device LEDs Primary : 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): 800 Sensitivity (DPI) : 800 13: DFUCONTROL SIGNED {00C2} V0 14: DEVICE RESET {1802} V0 15: unknown:1803 {0318} V0 internal, hidden 16: OOBSTATE {1805} V0 17: CONFIG DEVICE PROPS {1806} V4 18: unknown:1813 {1318} V0 internal, hidden 19: unknown:1830 {3018} V0 internal, hidden 20: unknown:1890 {9018} V0 internal, hidden 21: unknown:1891 {9118} V0 internal, hidden 22: unknown:1861 {6118} V0 internal, hidden 23: unknown:1801 {0118} V0 internal, hidden 24: unknown:18B1 {B118} V0 internal, hidden 25: unknown:1DF3 {F31D} V0 internal, hidden 26: unknown:1E00 {001E} V0 hidden 27: unknown:1EB0 {B01E} V0 internal, hidden 28: unknown:1E22 {221E} 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 30: unknown:18C0 {C018} V0 internal, hidden 31: CHANGE HOST {1814} V1 Change Host : 1:host1 32: HOSTS INFO {1815} V1 Host 0 (unpaired): host1 Host 1 (paired): Battery: 30%, BatteryStatus.DISCHARGING, next level 15%. ================================================ FILE: docs/devices/G613 Wireless Mechanical Gaming Keyboard 4065.txt ================================================ solaar version 1.1.19-25-g7520c9cc 1: G613 Wireless Mechanical Gaming Keyboard Device path : None WPID : 4065 Codename : G613 Kind : keyboard Protocol : HID++ 4.2 Report Rate : 1ms Serial number: 710EC3A3 Model ID: B34F40650000 Unit ID: 2A923B25 1: BOT 46.00.B0006 0: MPK 05.02.B0021 3: 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} V2 Firmware: 1 BOT 46.00.B0006 00006E86A7BD Firmware: 0 MPK 05.02.B0021 40656E86A7BD Firmware: 3 Unit ID: 2A923B25 Model ID: B34F40650000 Transport IDs: {'btleid': 'B34F', 'wpid': '4065'} 3: DEVICE NAME {0005} V0 Name: G613 Wireless 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: G613 7: BATTERY STATUS {1000} V0 Battery: 80%, BatteryStatus.DISCHARGING, next level 50%. 8: CHANGE HOST {1814} V1 Change Host : 1:cosmo 9: HOSTS INFO {1815} V1 Host 0 (paired): cosmo Host 1 (paired): Mi 11 10: REPROG CONTROLS V4 {1B04} V3 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} V1 12: ENCRYPTION {4100} V0 13: KEYBOARD DISABLE BY USAGE {4522} V0 14: KEYBOARD LAYOUT 2 {4540} V0 15: GKEY {8010} V0 Divert G and M Keys (saved): False Divert G and M Keys : False 16: REPORT RATE {8060} V0 Report Rate: 1ms Report Rate (saved): 1ms Report Rate : 1ms 17: DFUCONTROL SIGNED {00C2} V0 18: DEVICE RESET {1802} V0 19: unknown:1803 {0318} V0 internal, hidden 20: CONFIG DEVICE PROPS {1806} V3 21: unknown:1813 {1318} V0 internal, hidden 22: OOBSTATE {1805} V0 23: unknown:1830 {3018} V0 internal, hidden 24: unknown:1890 {9018} V0 internal, hidden 25: unknown:1891 {9118} V0 internal, hidden 26: unknown:18A1 {A118} V0 internal, hidden 27: unknown:1DF3 {F31D} V0 internal, hidden 28: unknown:1E00 {001E} V0 hidden 29: unknown:1EB0 {B01E} V0 internal, hidden 30: unknown:1861 {6118} V0 internal, hidden 31: unknown:18B1 {B118} V0 internal, hidden Has 2 reprogrammable keys: 0: Host Switch Channel 1 , default: Hostswitch Channel 1 => Hostswitch Channel 1 persistently_divertable, divertable, pos:1, group:0, group mask:empty reporting: default 1: Host Switch Channel 2 , default: Hostswitch Channel 2 => Hostswitch Channel 2 persistently_divertable, divertable, pos:2, group:0, group mask:empty reporting: default Battery: 80%, BatteryStatus.DISCHARGING, next level 50%. ================================================ FILE: docs/devices/G703 Wired-Wireless Gaming Mouse 4070.txt ================================================ 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 ================================================ FILE: docs/devices/G733 Gaming Headset 0AB5.text ================================================ Solaar version 1.1.19 G733 Gaming Headset Device path : /dev/hidraw0 USB id : 046d:0AB5 Codename : G733 Headset Kind : headset Protocol : HID++ 4.2 Serial number: Model ID: 0AB500000000 Unit ID: FFFFFFFF 0: 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: 0 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} V3 Sterowanie diodami LED (saved): Device Sterowanie diodami LED : Device Diody LED None (saved): !LEDEffectSetting {ID: 0} Diody LED None : !LEDEffectSetting {ID: 0} Diody LED None (saved): !LEDEffectSetting {ID: 1, color: 131072, ramp: 0} Diody LED None : !LEDEffectSetting {ID: 1, color: 66048, ramp: 0} 5: GKEY {8010} V0 Przekieruj klawisze G i M (saved): False Przekieruj klawisze G i M : False 6: EQUALIZER {8310} V1 Korektor (saved): {0: 5, 1: 4, 2: 3, 3: 5, 4: 5, 5: 5, 6: 4, 7: 3, 8: 4, 9: 5} Korektor : {0: 5, 1: 4, 2: 3, 3: 5, 4: 5, 5: 5, 6: 4, 7: 3, 8: 4, 9: 5} 7: SIDETONE {8300} V0 Efekt lokalny (saved): 0 Efekt lokalny : 0 8: ADC MEASUREMENT {1F20} V4 Battery: 89% 4058mV , BatteryStatus.DISCHARGING. Zarządzanie energią (saved): 30 Zarządzanie energią : 30 Battery: 89% 4058mV , BatteryStatus.DISCHARGING. ================================================ FILE: docs/devices/G733 Gaming Headset 0AFE.text ================================================ solaar 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. ================================================ FILE: docs/devices/G815 Mechanical Keyboard C33F.txt ================================================ solaar 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. ================================================ FILE: docs/devices/G903 LIGHTSPEED Wireless Gaming Mouse 4087.txt ================================================ solaar 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. ================================================ FILE: docs/devices/G915 TKL LIGHTSPEED Wireless RGB Mechanical Gaming Keyboard 408E.txt ================================================ Solaar 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. ================================================ FILE: docs/devices/G915 WIRELESS RGB MECHANICAL GAMING KEYBOARD 407C.text ================================================ solaar 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. ================================================ FILE: docs/devices/G915 WIRELESS RGB MECHANICAL GAMING KEYBOARD C33E.text ================================================ solaar 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. ================================================ FILE: docs/devices/G915 WIRELESS RGB Mechanical Gaming Keyboard 407E.txt ================================================ Solaar 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. ================================================ FILE: docs/devices/G935 Gaming Headset 0A87.txt ================================================ Solaar 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. ================================================ FILE: docs/devices/Illuminated Keyboard C318.txt ================================================ solaar 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. ================================================ FILE: docs/devices/Illuminated Living-Room Keyboard K830 4032.txt ================================================ # 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. ================================================ FILE: docs/devices/K850 Performance Wireless Keyboard 4062.txt ================================================ Solaar 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%. ================================================ FILE: docs/devices/K850 Performance Wireless Keyboard B34D.txt ================================================ Solaar 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%. ================================================ FILE: docs/devices/Keyboard K600 TV 4078.txt ================================================ Solaar 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%. ================================================ FILE: docs/devices/LIFT For Business B033.txt ================================================ solaar 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. ================================================ FILE: docs/devices/LIFT VERTICAL ERGONOMIC MOUSE B031.txt ================================================ Solaar 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. ================================================ FILE: docs/devices/Lightspeed Receiver C539.txt ================================================ Solaar 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 ================================================ FILE: docs/devices/Lightspeed Receiver C53A.txt ================================================ Lightspeed 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. ================================================ FILE: docs/devices/Lightspeed Receiver C53D.txt ================================================ Solaar 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 ================================================ FILE: docs/devices/Lightspeed Receiver C53F.txt ================================================ Lightspeed 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) ================================================ FILE: docs/devices/Lightspeed Receiver C541.txt ================================================ Solaar 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 ================================================ FILE: docs/devices/Lightspeed Receiver C545.txt ================================================ Solaar 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 ================================================ FILE: docs/devices/Lightspeed Receiver C547.txt ================================================ Solaar 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 ================================================ FILE: docs/devices/Logi Pop Keys B365.txt ================================================ solaar 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. ================================================ FILE: docs/devices/Logitech G933 Gaming Wireless Headset 0A5B.txt ================================================ solaar version 1.1.19-25-g7520c9cc Logitech G933 Gaming Wireless Headset Device path : /dev/hidraw4 USB id : 046d:0A5B Codename : Logitech Kind : ? Protocol : HID++ 4.2 Serial number: Model ID: 000000000A5B Unit ID: FFFFFFFF 0: U 98.03.B0027 Supports 9 HID++ 2.0 features: 0: ROOT {0000} V0 1: FEATURE SET {0001} V0 2: DEVICE FW VERSION {0003} V2 Firmware: 0 U 98.03.B0027 0A5B Unit ID: FFFFFFFF Model ID: 000000000A5B Transport IDs: {'btid': '0000', 'btleid': '0000'} 3: DEVICE NAME {0005} V0 Name: Logitech G933 Gaming Wireless Headset Kind: None 4: COLOR LED EFFECTS {8070} V3 LED Control : HID++ error {'number': 255, 'request': 1147, 'error': 7, 'params': b''} LEDs None (saved): !LEDEffectSetting {ID: 0} LEDs None : !LEDEffectSetting {ID: 0} LEDs None (saved): !LEDEffectSetting {ID: 0, color: 9519532, intensity: 0, period: 100, ramp: 0, speed: 0} LEDs None : !LEDEffectSetting {ID: 1, color: 0, ramp: 0} 5: GKEY {8010} V0 Divert G and M Keys (saved): False Divert G and M Keys : False 6: EQUALIZER {8310} V1 Equalizer (saved): {0: 8, 1: 8, 2: 4, 3: 2, 4: 1, 5: 4, 6: 7, 7: 10, 8: 5, 9: 11} Equalizer : {0: 8, 1: 8, 2: 4, 3: 2, 4: 1, 5: 4, 6: 7, 7: 10, 8: 5, 9: 11} 7: SIDETONE {8300} V0 Sidetone (saved): 30 Sidetone : 30 8: ADC MEASUREMENT {1F20} V3 Battery: 100% 4183mV , BatteryStatus.RECHARGING. Power Management (saved): 30 Power Management : 30 Battery: 100% 4183mV , BatteryStatus.RECHARGING. ================================================ FILE: docs/devices/Logitech PRO X Wireless Gaming Headset 0ABA.txt ================================================ 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. ================================================ FILE: docs/devices/M720 Triathlon Multi-Device Mouse 405E.txt ================================================ Solaar 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%. ================================================ FILE: docs/devices/M720 Triathlon Multi-Device Mouse B015.txt ================================================ Solaar 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%. ================================================ FILE: docs/devices/MX Anywhere 3 4090.txt ================================================ Solaar 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. ================================================ FILE: docs/devices/MX Anywhere 3 B025.txt ================================================ Solaar 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. ================================================ FILE: docs/devices/MX Anywhere 3 for Business B02D.txt ================================================ solaar version 1.1.14 1: MX Anywhere 3 for Business Device path : None WPID : B02D Codename : MX Anywhere 3 Kind : mouse Protocol : HID++ 4.5 Serial number: 00000000 Model ID: B02D00000000 Unit ID: 00000000 1: BL1 36.01.B0011 0: RBM 15.01.B0011 3: 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: 1 BL1 36.01.B0011 B02D1EEFD8F8 Firmware: 0 RBM 15.01.B0011 B02D1EEFD8F8 Firmware: 3 Unit ID: 00000000 Model ID: B02D00000000 Transport IDs: {'btleid': 'B02D'} 3: DEVICE NAME {0005} V0 Name: MX Anywhere 3 for Business Kind: mouse 4: WIRELESS DEVICE STATUS {1D4B} V0 5: CONFIG CHANGE {0020} V0 Configuration: 11000000000000000000000000000000 6: CRYPTO ID {0021} V1 7: DEVICE FRIENDLY NAME {0007} V0 Friendly Name: MX Anywhere 3B 8: UNIFIED BATTERY {1004} V3 Battery: 75%, 0. 9: REPROG CONTROLS V4 {1B04} V5 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 : {Middle Button:Regular, Back Button:Regular, Forward Button:Regular, Smart Shift:Diverted} 10: CHANGE HOST {1814} V1 Change Host : 2:archlinux 11: HOSTS INFO {1815} V2 Host 0 (paired): archlinux Host 1 (paired): archlinux Host 2 (unpaired): 12: XY STATS {2250} V1 13: ADJUSTABLE DPI {2201} V2 Sensitivity (DPI) : 1000 14: SMART SHIFT ENHANCED {2111} V0 Scroll Wheel Ratcheted : Ratcheted Scroll Wheel Ratchet Speed : 15 15: 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 : False Scroll Wheel Resolution : False Scroll 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} 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:9300 {9300} V1 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 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: 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:3, group mask:empty reporting: default Battery: 75%, 0. ================================================ FILE: docs/devices/MX Ergo Multi-Device Trackball 406F.txt ================================================ Solaar 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%. ================================================ FILE: docs/devices/MX Keys Keyboard 408A.txt ================================================ Solaar 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%. ================================================ FILE: docs/devices/MX Keys Keyboard B35B.txt ================================================ Solaar 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%. ================================================ FILE: docs/devices/MX Keys Mini B369.txt ================================================ Solaar 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. ================================================ FILE: docs/devices/MX Keys S B378.text ================================================ solaar 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. ================================================ FILE: docs/devices/MX Keys for Business B363.text ================================================ Solaar 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. ================================================ FILE: docs/devices/MX Master 3 Wireless Mouse 4082.txt ================================================ Solaar 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%. ================================================ FILE: docs/devices/MX Master 3 Wireless Mouse B023.txt ================================================ Solaar 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%. ================================================ FILE: docs/devices/MX Master 3 for Business B028.text ================================================ solaar 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. ================================================ FILE: docs/devices/MX Master 3S B034.txt ================================================ Solaar 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. ================================================ FILE: docs/devices/MX Mechanical B366.txt ================================================ Solaar 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. ================================================ FILE: docs/devices/MX Mechanical Mini B367.txt ================================================ Solaar 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. ================================================ FILE: docs/devices/MX Vertical Wireless Mouse 407B.txt ================================================ Solaar 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%. ================================================ FILE: docs/devices/MX Vertical Wireless Mouse B020.txt ================================================ Solaar 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%. ================================================ FILE: docs/devices/Marathon Mouse M705 101B.txt ================================================ Solaar 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' ================================================ FILE: docs/devices/Marathon Mouse M705 406D.txt ================================================ 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%. ================================================ FILE: docs/devices/Multi Device Silent Mouse M585-M590 406B.txt ================================================ Solaar 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%. ================================================ FILE: docs/devices/Nano Receiver C52F.txt ================================================ Nano 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 ================================================ FILE: docs/devices/Nano Receiver C534.txt ================================================ Solaar 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 ================================================ FILE: docs/devices/Nano Receiver C535.txt ================================================ Solaar 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) ================================================ FILE: docs/devices/Number Pad N545 2006.txt ================================================ solaar 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. ================================================ FILE: docs/devices/PRO X 2 40A9.text ================================================ solaar 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. ================================================ FILE: docs/devices/PRO X Wireless 4093.txt ================================================ Solaar version 1.1.14 2: PRO X Wireless Device path : None WPID : 4093 Codename : PRO X Kind : mouse Protocol : HID++ 4.2 Report Rate : 1ms Serial number: 8B24D1D1 Model ID: 4093C0940000 Unit ID: 8B24D1D1 1: BL1 25.01.B0018 3: 0: MPM 25.01.B0018 Supports 28 HID++ 2.0 features: 0: ROOT {0000} V0 1: FEATURE SET {0001} V0 2: DEVICE FW VERSION {0003} V3 Firmware: 1 BL1 25.01.B0018 AB00FE92436C Firmware: 3 Firmware: 0 MPM 25.01.B0018 4093FE92436C Unit ID: 8B24D1D1 Model ID: 4093C0940000 Transport IDs: {'wpid': '4093', 'usbid': 'C094'} 3: DEVICE NAME {0005} V0 Name: PRO X Wireless Kind: mouse 4: WIRELESS DEVICE STATUS {1D4B} V0 5: CONFIG CHANGE {0020} V0 Configuration: 11000000000000000000000000000000 6: UNIFIED BATTERY {1004} V1 Battery: 71%, 0. 7: COLOR LED EFFECTS {8070} V4 internal, hidden LED Control : HID++ error {'number': 2, 'request': 1908, 'error': 5, 'params': b''} 8: ONBOARD PROFILES {8100} V0 Device Mode: On-Board Onboard Profiles (saved): Profile 1 Onboard Profiles : Profile 1 9: MOUSE BUTTON SPY {8110} V0 10: REPORT RATE {8060} V0 Report Rate: 1ms Report Rate (saved): 1ms Report Rate : 1ms 11: ADJUSTABLE DPI {2201} V2 Sensitivity (DPI) (saved): 800 Sensitivity (DPI) : 800 12: FORCE PAIRING {1500} V0 13: DEVICE RESET {1802} V0 internal, hidden 14: unknown:1803 {1803} V0 internal, hidden 15: CONFIG DEVICE PROPS {1806} V4 internal, hidden 16: unknown:1811 {1811} V0 internal, hidden 17: OOBSTATE {1805} V0 internal, hidden 18: unknown:1830 {1830} V0 internal, hidden 19: unknown:1890 {1890} V5 internal, hidden 20: unknown:1891 {1891} V5 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:1E00 {1E00} V0 hidden 25: unknown:1EB0 {1EB0} V0 internal, hidden 26: unknown:1863 {1863} V0 internal, hidden 27: unknown:1E22 {1E22} V0 internal, hidden Battery: 71%, 0. ================================================ FILE: docs/devices/Rechargeable Trackpad T651 B00C.txt ================================================ Solaar 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%. ================================================ FILE: docs/devices/Signature M550.text ================================================ solaar version 1.1.19 1: Signature M550 Device path : None WPID : B02B Codename : Logi M550 Kind : mouse Protocol : HID++ 4.5 Serial number: D074434E Model ID: B02B00000000 Unit ID: D074434E 1: BL1 39.01.B0013 0: RBM 17.01.B0013 3: The power switch is located on the (unknown). Supports 30 HID++ 2.0 features: 0: ROOT {0000} V0 1: FEATURE SET {0001} V0 2: DEVICE FW VERSION {0003} V4 Firmware: 1 BL1 39.01.B0013 B02BB0706FCD Firmware: 0 RBM 17.01.B0013 B02BB0706FCD Firmware: 3 Unit ID: D074434E Model ID: B02B00000000 Transport IDs: {'btleid': 'B02B'} 3: DEVICE NAME {0005} V0 Name: Signature M550 Kind: mouse 4: WIRELESS DEVICE STATUS {1D4B} V0 5: CONFIG CHANGE {0020} V0 Configuration: 11000000000000000000000000000000 6: DEVICE FRIENDLY NAME {0007} V0 Friendly Name: Logi M550 7: UNIFIED BATTERY {1004} V3 Battery: 35%, BatteryStatus.DISCHARGING. 8: REPROG CONTROLS V4 {1B04} V5 Key/Button Actions (saved): {Middle Button:Mouse Middle Button} Key/Button Actions : {Middle Button:Mouse Middle Button} Key/Button Diversion (saved): {Middle Button:Regular} Key/Button Diversion : {Middle Button:Regular} 9: HOSTS INFO {1815} V2 Host 0 (paired): mst 10: XY STATS {2250} V1 11: LOWRES WHEEL {2130} V0 Wheel Reports: HID Scroll Wheel Diversion (saved): False Scroll Wheel Diversion : False 12: ADJUSTABLE DPI {2201} V2 Sensitivity (DPI) (saved): 1000 Sensitivity (DPI) : 1000 13: DFUCONTROL {00C3} V0 14: DEVICE RESET {1802} V0 15: unknown:1803 {0318} V0 internal, hidden, unknown:000010 16: CONFIG DEVICE PROPS {1806} V8 17: unknown:1816 {1618} V0 internal, hidden, unknown:000010 18: OOBSTATE {1805} V0 19: unknown:1830 {3018} V0 internal, hidden, unknown:000010 20: unknown:1891 {9118} V0 internal, hidden, unknown:000008 21: unknown:18A1 {A118} V0 internal, hidden, unknown:000010 22: unknown:1E00 {001E} V0 hidden 23: unknown:1E02 {021E} V0 internal, hidden 24: unknown:1E22 {221E} V0 internal, hidden, unknown:000010 25: unknown:1602 {0216} V0 26: unknown:1EB0 {B01E} V0 internal, hidden, unknown:000010 27: unknown:1861 {6118} V0 internal, hidden, unknown:000010 28: unknown:18B1 {B118} V0 internal, hidden, unknown:000010 29: unknown:920A {0A92} V0 internal, hidden, unknown:000010 Has 4 reprogrammable keys: 0: Left Button , default: Left Click => Left Click analytics_key_events, mse, pos:0, group:1, group mask:empty reporting: default 1: Right Button , default: Right Click => Right Click analytics_key_events, mse, pos:0, group:1, group mask:empty reporting: default 2: Middle Button , default: Mouse Middle Button => Mouse Middle Button analytics_key_events, raw_xy, divertable, reprogrammable, mse, pos:0, group:2, group mask:g1,g2 reporting: default 3: Virtual Gesture Button , default: Virtual Gesture Button => Virtual Gesture Button force_raw_xy, raw_xy, virtual, divertable, pos:0, group:3, group mask:empty reporting: default Battery: 35%, BatteryStatus.DISCHARGING. ================================================ FILE: docs/devices/Signature M650 L Mouse B02A.txt ================================================ Solaar 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. ================================================ FILE: docs/devices/Unifying Receiver C52B.txt ================================================ Solaar 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 ================================================ FILE: docs/devices/Wireless All-in-One Keyboard TK820 4102.txt ================================================ Solaar 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 ================================================ FILE: docs/devices/Wireless Illuminated Keyboard K800 2010.txt ================================================ 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. ================================================ FILE: docs/devices/Wireless Illuminated Keyboard K800 new 406E.txt ================================================ 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. ================================================ FILE: docs/devices/Wireless Keyboard 4075.txt ================================================ Solaar 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%. ================================================ FILE: docs/devices/Wireless Keyboard Dell KB714 4015.txt ================================================ Solaar 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%. ================================================ FILE: docs/devices/Wireless Keyboard K220 4005.txt ================================================ 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. ================================================ FILE: docs/devices/Wireless Keyboard K230 400D.txt ================================================ 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%. ================================================ FILE: docs/devices/Wireless Keyboard K270(unifying) 4003.txt ================================================ 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. ================================================ FILE: docs/devices/Wireless Keyboard K360 4004.txt ================================================ Solaar 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%. ================================================ FILE: docs/devices/Wireless Keyboard K470 4075.txt ================================================ Solaar 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%. ================================================ FILE: docs/devices/Wireless Keyboard K520 2011.txt ================================================ 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 ================================================ FILE: docs/devices/Wireless Keyboard MK270 4023.txt ================================================ Solaar 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%. ================================================ FILE: docs/devices/Wireless Mobile Mouse MX Anywhere 2 4072.txt ================================================ 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) ================================================ FILE: docs/devices/Wireless Mobile Mouse MX Anywhere 2S 406A.txt ================================================ Solaar 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%. ================================================ FILE: docs/devices/Wireless Mobile Mouse MX Anywhere 2S B01A.txt ================================================ solaar 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%. ================================================ FILE: docs/devices/Wireless Mouse 4022.txt ================================================ 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. ================================================ FILE: docs/devices/Wireless Mouse Dell WM514 4029.txt ================================================ Solaar 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%. ================================================ FILE: docs/devices/Wireless Mouse M185 new 4054.txt ================================================ Solaar 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. ================================================ FILE: docs/devices/Wireless Mouse M185,M235,M310 4055.txt ================================================ Solaar 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%. ================================================ FILE: docs/devices/Wireless Mouse M185.text ================================================ solaar show rules cannot access modifier keys in Wayland, accessing process only works on GNOME with Solaar Gnome extension installed solaar version 1.1.14-2 Unifying Receiver Device path : /dev/hidraw1 USB id : 046d:C52B Serial : EC219AC2 C Pending : ff 0 : 12.11.B0032 1 : 04.16 3 : AA.AA Has 2 paired device(s) out of a maximum of 6. Notifications: wireless (0x000100) Device activity counters: 1=195, 2=74 1: Wireless Mouse M175 Device path : /dev/hidraw2 WPID : 4008 Codename : M175 Kind : mouse Protocol : HID++ 2.0 Report Rate : 8ms Serial number: 16E46E8C Model ID: 000000000000 Unit ID: 00000000 0: RQM 40.00.B0016 The power switch is located on the base. Supports 21 HID++ 2.0 features: 0: ROOT {0000} V0 1: FEATURE SET {0001} V0 2: DEVICE FW VERSION {0003} V0 Firmware: 0 RQM 40.00.B0016 4008 Unit ID: 00000000 Model ID: 000000000000 Transport IDs: {} 3: DEVICE NAME {0005} V0 Name: Wireless Mouse M185 Kind: mouse 4: BATTERY STATUS {1000} V0 Battery: 70%, 0, 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: 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 Battery: 70%, 0, next level 5%. ================================================ FILE: docs/devices/Wireless Mouse M215 2nd Gen 401B.txt ================================================ Solaar 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%. ================================================ FILE: docs/devices/Wireless Mouse M310 M310t 4031.txt ================================================ 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. ================================================ FILE: docs/devices/Wireless Mouse M325 400A.txt ================================================ 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 ================================================ FILE: docs/devices/Wireless Mouse M345 4017.txt ================================================ 1: 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, ================================================ FILE: docs/devices/Wireless Mouse M510 1025.txt ================================================ Solaar 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 ================================================ FILE: docs/devices/Wireless Mouse M510 4051.txt ================================================ 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. ================================================ FILE: docs/devices/Wireless Mouse M525 4013.txt ================================================ 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. ================================================ FILE: docs/devices/Wireless Mouse M560 402D.txt ================================================ 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. ================================================ FILE: docs/devices/Wireless Mouse MX Anywhere 2 404A.txt ================================================ 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. ================================================ FILE: docs/devices/Wireless Mouse MX Master 2S 4069.txt ================================================ Solaar 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%. ================================================ FILE: docs/devices/Wireless Mouse MX Master 2S B019.txt ================================================ Solaar 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%. ================================================ FILE: docs/devices/Wireless Mouse MX Master 4041.txt ================================================ 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%. ================================================ FILE: docs/devices/Wireless Mouse MX Master 4071.txt ================================================ Solaar 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%. ================================================ FILE: docs/devices/Wireless Mouse MX Master B012.txt ================================================ Solaar 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%. ================================================ FILE: docs/devices/Wireless Mouse Pebble M350 4080.txt ================================================ Solaar 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%. ================================================ FILE: docs/devices/Wireless Multi-Device Keyboard K780 405B.txt ================================================ Solaar 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%. ================================================ FILE: docs/devices/Wireless Rechargeable Touchpad T650 4101.txt ================================================ 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. ================================================ FILE: docs/devices/Wireless Solar Keyboard K750 4002.txt ================================================ 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. ================================================ FILE: docs/devices/Wireless Touch Keyboard K400 4024.txt ================================================ 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. ================================================ FILE: docs/devices/Wireless Touch Keyboard K400 Plus 404D.txt ================================================ Solaar 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%. ================================================ FILE: docs/devices/Wireless Trackball M570 1028.txt ================================================ 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. ================================================ FILE: docs/devices/Zone Touch Mouse T400 4026.txt ================================================ 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. ================================================ FILE: docs/devices/anywhere-mx.txt ================================================ Receiver 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' ================================================ FILE: docs/devices/mk700.txt ================================================ # 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' ================================================ FILE: docs/devices/performance-mx.txt ================================================ # 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' ================================================ FILE: docs/devices.md ================================================ --- 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++ | |------------------------------|------|-------| | G604 Wireless Gaming Mouse | 4085 | 4.2 | | 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 ================================================ FILE: docs/features.md ================================================ --- 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` | Supported | `Backlight` `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` | Supported | `ADCPower` `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` | Supported | `MkeyLEDs` `MR` | `0x8030` | Supported | `MRKeyLED` `BRIGHTNESS_CONTROL` | `0x8040` | Supported | `BrightnessControl` `REPORT_RATE` | `0x8060` | Supported | `ReportRate` `COLOR_LED_EFFECTS` | `0x8070` | Supported | `LEDControl`, `LEDZoneSetting` `RGB_EFFECTS` | `0X8071` | Supported | `RGBControl`, `RGBEffectSetting` `PER_KEY_LIGHTING` | `0x8080` | Unsupported | `PER_KEY_LIGHTING_V2` | `0x8081` | Supported | `PerKeyLighting` `MODE_STATUS` | `0x8090` | Unsupported | `ONBOARD_PROFILES` | `0x8100` | Supported | `MOUSE_BUTTON_SPY` | `0x8110` | Unsupported | `LATENCY_MONITORING` | `0x8111` | Unsupported | `GAMING_ATTACHMENTS` | `0x8120` | Unsupported | `FORCE_FEEDBACK` | `0x8123` | Unsupported | `SIDETONE` | `0x8300` | Supported | `Sidetone` `EQUALIZER` | `0x8310` | Supported | `Equalizer` `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`. ================================================ FILE: docs/hidpp-documentation.txt ================================================ Documentation on HID++ can be found in the Google drive directory https://drive.google.com/drive/folders/0BxbRzx7vEV7eWmgwazJ3NUFfQ28 ================================================ FILE: docs/i18n.md ================================================ --- 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, [Niko Savola][nikosavola] - 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 - Serbian: [Renato Kaurić][renatoka] - Slovak: [Jose Riha][jose1711] - Spanish, Castilian: Jose Luis Tirado - Swedish: John Erling Blad, [Daniel Zippert][zipperten], Emelie Snecker, Jonatan Nyberg - Turkish: Osman Karagöz - Ukrainian: Олександр Афанасьєв [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 [renatoka]: https://github.com/renatoka [nikosavola]: https://github.com/nikosavola ================================================ FILE: docs/implementation.md ================================================ --- 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 Onboard 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 Onboard Profiles. An Onboard 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 ================================================ FILE: docs/index.md ================================================ --- 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 that a particular device supports. 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 how 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. ## 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. ================================================ FILE: docs/installation.md ================================================ --- 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`. 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. ## 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 ``` ### Optional: Set up macOS launcher * Option A (recommended): Configure a LaunchAgent to automatically start Solaar and keep it running in the background. It will also automatically restart Solaar if it crashed or closed. ``` bash <(curl -fsSL https://raw.githubusercontent.com/pwr-Solaar/Solaar/refs/heads/master/tools/create-macos-launchagent.sh) ``` * Option B: Create Solaar.app launcher in /Applications. It can be added to Login Items to start on login, but it will not automatically recover on crashes. ``` bash <(curl -fsSL https://raw.githubusercontent.com/pwr-Solaar/Solaar/refs/heads/master/tools/create-macos-app.sh) ``` # 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`. Finally, install Solaar via `make install_pip` or `make install_pipx`. 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` and `python-typing-extensions'. 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. Solaar includes its own version of `hid_parser` because the version that is in PyPi (at https://pypi.org/project/hid-parser/) does not have some changes that are in https://github.com/usb-tools/python-hid-parser and are needed for some devices. Do not use pip to install hid_parser! Some distributions (e.g., Fedora) may separately package this code. 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`. 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`](https://github.com/pwr-Solaar/Solaar/blob/master/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. ================================================ FILE: docs/issues.md ================================================ --- title: Known Issues layout: page --- # Known Issues - Some internal structures in Solaar have been updated to use more standard Python language features. This has caused some problems and introduced bugs are still being found. - 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. - 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 uses uinput to simulate input 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. ================================================ FILE: docs/rules.md ================================================ --- 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 uinput to simulate input so the user has to have write access to /dev/uinput. The easiest way to maintain write access to /dev/uinput is to use Solaar's udev rule by downloading `https://raw.githubusercontent.com/pwr-Solaar/Solaar/master/rules.d/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, or either of its names, as shown by Solaar. Some older devices do not have a useful serial number or unit ID and so cannot distinguished from other devices with the same names. ### 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. ### Execute 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) ================================================ FILE: docs/uninstallation.md ================================================ --- title: Uninstalling Solaar layout: page --- # Uninstalling Solaar ## Uninstalling from Debian systems If you installed Solaar using `apt`, you can remove it by running: ```bash sudo apt remove --purge solaar ``` ## Uninstalling from GitHub If you cloned and installed Solaar from GitHub manually, navigate to the cloned directory and run: ```bash sudo make uninstall ``` ## Removing Configuration Files Solaar may leave behind configuration files in your home directory. To delete them, run: ```bash rm -rf ~/.config/solaar ``` ## Verifying Uninstallation To confirm that Solaar is fully removed, try running: ```bash which solaar ``` If no output is returned, Solaar has been successfully uninstalled. ================================================ FILE: docs/usage.md ================================================ --- title: Solaar GUI Usage layout: page --- # Solaar GUI Usage The Solaar GUI (the usual way to run Solaar) is meant to run continuously in the background, monitoring devices, making changes to them, and responding to some messages they emit. The Solaar GUI normally creates an icon in your system tray that provides an interface to Solaar. This icon is usually a battery icon showing the approximate battery level for your device with the lowest known battery level. If there are no devices with battery information, then the icon is one of the Solaar icons. Solaar also has a main window. When the main window is invisible, click on Solaar's icon in the system tray to bring up the Solaar menu, and then click on a receiver or device in the menu to see information about that receiver or device. The following is an image of the Solaar menu and the icon (the battery symbol is in the system tray at the left of the image). The icon can also be other battery icons or versions of the Logitech Unifying icon. ![Solaar-menu](screenshots/Solaar-menu.png) Clicking on “Quit” in the Solaar menu terminates the program. Clicking on “About Solaar” pops up a window with further information about Solaar. ## Solaar options There are several options that affect how the Solaar GUI behaves: * `--help` shows a help message and then quits * `--version` shows the version of Solaar and then quits * `--window=show` starts Solaar with the main window showing * `--window=hide` starts Solaar with the main window not showing * `--window=only` starts Solaar with no system tray icon and the main window showing * `--battery-icons=regular` uses regular icons for battery levels * `--battery-icons=symbolic` uses symbolic icons for battery levels * `--battery-icons=solaar` uses only the Solaar icon in the system tray ## Solaar main window The Solaar main window shows your Logitech receivers and devices that Solaar knows about and can be used to pair new devices, unpair paired devices, and view and change some settings of the selected receiver or device. To select a receiver or device click on it in the left side of the window. Closing the main window does not terminate Solaar (unless Solaar is not using the system tray). Clicking on “Quit Solaar” terminates the program, and “About Solaar” pops up a window with further information. The light bulb (or a similar icon) displays detailed information about the selected receiver or device (useful for debugging). ### Pairing and unpairing devices When a receiver is selected in the main window, you can pair a new device by clicking on the “Pair new device” button. Then turn on the device and it should pair with the receiver if that is possible and the device is not already paired with another active receiver. For multi-host devices first select the host position that you want. Bolt devices and some Lightspeed devices pair by pressing a special pairing button. To pair with a Bolt receiver you have to type a passcode followed by enter or click the left and right buttons in the correct sequence followed by clicking both buttons simultaneously. ![Solaar-main-window-receiver](screenshots/Solaar-main-window-receiver.png) When a device is selected you can unpair the device if your receiver supports unpairing. To unpair the device, just click on the “Unpair” button and confirm in the window that pops up. A receiver with the Unifying logo should be able to pair with any device with the Unifying logo and a receiver with the Bolt logo should be able to pair with any device with the Bolt logo. If there are no open pairing slots, however, you will not be able to pair a new device. In this case to pair a new device you first need to unpair a device. Other receivers can only pair with certain kinds of devices. Most of these receivers do not allow unpairing - instead pairing a new device replaces an existing paired device of the same kind. Some receivers can only pair a limited number of times. ### Viewing and changing device settings When a device is selected you can see the approximate battery level of the device, if that is reported by the device, and the status of the link between the device and its receiver. You can also see and change the settings of devices. Changing settings is performed by clicking on buttons, moving sliders, or selecting from alternatives. ![Solaar-main-window-keyboard](screenshots/Solaar-main-window-keyboard.png) ![Solaar-main-window-mouse](screenshots/Solaar-main-window-mouse.png) Device settings now have a clickable icon that determines whether the setting can be changed and whether the setting is ignored. ![Solaar-divert-back](screenshots/Solaar-main-window-back-divert.png) If the selected device that is paired with a receiver is powered down or otherwise disconnected its settings cannot be changed but it still can be unpaired if its receiver allows unpairing. ![Solaar-main-window-offline](screenshots/Solaar-main-window-offline.png) If a device is paired with a receiver but directly connected via USB or Bluetooth the receiver pairing will show up as well as the direct connection. The device can only be manipulated using the direct connection. ![Solaar-main-window-multiple](screenshots/Solaar-main-window-multiple.png) #### Remapping key and button actions For many devices Solaar can remap some of their keys or buttons to perform a different action. (This changes the information that the device sends when the key or button is activated.) Only some keys on some devices can be remapped and they can only be remapped to a limited number of actions. The remapping is done by selecting a key or button in the left-hand box on the “Action” setting line and then selecting the action to be performed in the right-hand box. The default action is always the one shown first in the list. As with all settings, Solaar will remember past action settings and restore them on the device from then on. ![Solaar-main-window-actions](screenshots/Solaar-main-window-button-actions.png) The names of the keys, buttons, and actions are mostly taken from Logitech documentation and may not be completely obvious. It is possible to end up with an unusable system, for example by having no way to do a mouse left click, so exercise caution when remapping keys or buttons that are needed to operate your system. ## Solaar command-line interface Solaar also has a command-line interface that can do most of what can be done using the main window. For more information on the command line interface, run `solaar --help` to see the commands and then `solaar --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. ================================================ FILE: docs/usb.ids.txt ================================================ # # 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 ================================================ FILE: lib/hid_parser/__init__.py ================================================ # 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})") ================================================ FILE: lib/hid_parser/data.py ================================================ # 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" ================================================ FILE: lib/hidapi/__init__.py ================================================ ================================================ FILE: lib/hidapi/common.py ================================================ from __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 ================================================ FILE: lib/hidapi/hidapi_impl.py ================================================ ## 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 LOGITECH_VENDOR_ID = 0x046D 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) unique_devices = {} for device in devices: # 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: dict[str, Any], 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"] hid_bus_type = device["bus_type"] # 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 logger.info(f"Device {device['path']} has an unsupported bus type {hid_bus_type:02X}") return None # Skip unlikely devices with all-zero VID PID or unsupported bus IDs if vid == 0 and pid == 0: logger.info(f"Device {device['path']} has all-zero VID and PID") logger.info(f"Skipping unlikely device {device['path']} ({bus_id}/{vid:04X}/{pid:04X})") return None # Skip Logitech webcams to prevent them from locking up during hidpp checks # (product IDs range for webcams from docs/usb.ids.txt) if vid == LOGITECH_VENDOR_ID and 0x0800 <= pid <= 0x09FF: logger.info(f"Skipping Logitech webcam {device['path']} ({bus_id}/{vid:04X}/{pid:04X})") return None # Check for hidpp support device["hidpp_short"] = False device["hidpp_long"] = False device_handle = None def check_hidpp_short(): report = _get_input_report(device_handle, 0x10, 32) if len(report) == 1 + 6 and report[0] == 0x10: device["hidpp_short"] = True def check_hidpp_long(): report = _get_input_report(device_handle, 0x11, 32) if len(report) == 1 + 19 and report[0] == 0x11: device["hidpp_long"] = True try: device_handle = open_path(device["path"]) for check_func in (check_hidpp_short, check_hidpp_long): try: check_func() except HIDError as e: logger.info( f"Error while {check_func.__name__}" f"on device {device['path']} ({bus_id}/{vid:04X}/{pid:04X}) for hidpp check: {e}" ) except HIDError as e: logger.info(f"Error opening device {device['path']} ({bus_id}/{vid:04X}/{pid:04X}) for hidpp check: {e}") finally: if device_handle: close(device_handle) logger.info( "Found device BID %s VID %04X PID %04X HID++ SHORT %s LONG %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 logger.info(f"Finished checking HIDPP support for device {device['path']} ({bus_id}/{vid:04X}/{pid:04X})") 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: str) -> int: """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) ================================================ FILE: lib/hidapi/hidconsole.py ================================================ ## 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 = f"{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(f"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(f"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.path d = None if not device: for d in hidapi.enumerate(matchfn): if (d.hidpp_short or d.hidpp_long) and (args.id is None or args.id.lower() == d.product_id.lower()): device = d.path break if not device: sys.exit("!! No HID++ receiver found.") if not device: sys.exit("!! Device path required.") handle = hidapi.open_path(device) if not handle: sys.exit(f"!! Failed to open {device}, aborting.") print( ".. Opened device %r, vendor %r product %r serial %r." % ( device, hidapi.get_manufacturer(handle) or d.vendor_id if d else None, hidapi.get_product(handle) or d.product_id if d else None, 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("command", nargs="?", help="command to send (otherwise get commands from input)") group = arg_parser.add_mutually_exclusive_group() group.add_argument("-p", "--path", help="HID raw device to connect to (/dev/hidrawX); ") group.add_argument("-i", "--id", help="product ID of device to connect to (XXXX)") return arg_parser.parse_args() def main(): args = _parse_arguments() handle = _open(args) if args.command: # send a command data = _validate_input(args.command, args.hidpp) if data: hidapi.write(handle, data) reply = hidapi.read(handle, 128, 1) if reply: hexs = strhex(reply) s = "[%s %s %s %s] %s" % (hexs[0:2], hexs[2:4], hexs[4:8], hexs[8:], repr(data)) print(s) exit() 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: hidapi.close(handle) if interactive: readline.write_history_file(args.history) if __name__ == "__main__": main() ================================================ FILE: lib/hidapi/udev_impl.py ================================================ ## 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.""" 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) 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) 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. """ 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 e 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 ================================================ FILE: lib/keysyms/__init__.py ================================================ ================================================ FILE: lib/keysyms/generate.py ================================================ #!/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() ================================================ FILE: lib/keysyms/keysymdef.py ================================================ # 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, } ================================================ FILE: lib/logitech_receiver/__init__.py ================================================ ## 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__) ================================================ FILE: lib/logitech_receiver/base.py ================================================ ## 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 HIDProtocol(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) -> int: ... 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: ... 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 hidapi = typing.cast(HIDProtocol, hidapi) request_lock = threading.Lock() # serialize all requests handles_lock = {} @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 product_information(usb_id: int) -> dict[str, Any]: """Returns hardcoded information from USB receiver.""" return base_usb.get_receiver_info(usb_id) def receivers(): """Enumerate all the receivers attached to the machine.""" yield from hidapi.enumerate(get_known_receiver_info) 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""" recv = get_known_receiver_info(bus_id, vendor_id, product_id, hidpp_short, hidpp_long) if recv: # known or unknown receiver return recv device = get_known_device_info(bus_id, vendor_id, product_id) if device: return device if hidpp_short or hidpp_long: return get_unknown_hid_device_info(bus_id, vendor_id, product_id) if hidpp_short is None and hidpp_long is None: return get_unknown_logitech_device_info(bus_id, vendor_id, product_id) return None def get_known_device_info(bus_id: int, vendor_id: int, product_id: int) -> dict[str, Any]: for recv in KNOWN_DEVICE_IDS: if _match_device(recv, bus_id, vendor_id, product_id): return recv def get_unknown_hid_device_info(bus_id: int, vendor_id: int, product_id: int) -> dict[str, Any]: return {"vendor_id": vendor_id, "product_id": product_id, "bus_id": bus_id, "isDevice": True} def get_unknown_logitech_device_info(bus_id: int, vendor_id: int, product_id: int) -> dict[str, Any] | None: """Get info from unknown device in Logitech product range. Check whether product is a Logitech USB-connected or Bluetooth device based on bus, vendor, and product ID. This allows Solaar to support receiverless HID++ 2.0 devices that it knows nothing about. """ if vendor_id != LOGITECH_VENDOR_ID: return None if bus_id == BusID.USB.value and (0xC07D <= product_id <= 0xC094 or 0xC32B <= product_id <= 0xC344): device_info = _usb_device(product_id, 2) return device_info elif bus_id == BusID.BLUETOOTH.value and (0xB012 <= product_id <= 0xB0FF or 0xB317 <= product_id <= 0xB3FF): device_info = _bluetooth_device(product_id) return device_info return None def _match_device(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 get_known_receiver_info( 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_device(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_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) -> int: """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) -> tuple[int, int, bytes]: """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 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 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 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: _read_input_buffer(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: _read_input_buffer(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 device with that number currently accessible logger.info("(%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) def _read_input_buffer(handle, ihandle, notifications_hook): """Consume 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 _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 ================================================ FILE: lib/logitech_receiver/base_usb.py ================================================ ## 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 __future__ import annotations from typing import Any 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, "re_pairs": True, } 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) LIGHTSPEED_RECEIVER_C54D = _lightspeed_receiver(0xC54D) # EX100 old style receiver pre-unifying protocol EX100_27MHZ_RECEIVER_C517 = _ex100_receiver(0xC517) KNOWN_RECEIVERS = { 0xC548: BOLT_RECEIVER_C548, 0xC52B: UNIFYING_RECEIVER_C52B, 0xC532: UNIFYING_RECEIVER_C532, 0xC52F: NANO_RECEIVER_ADVANCED, 0xC518: NANO_RECEIVER_C518, 0xC51A: NANO_RECEIVER_C51A, 0xC51B: NANO_RECEIVER_C51B, 0xC521: NANO_RECEIVER_C521, 0xC525: NANO_RECEIVER_C525, 0xC526: NANO_RECEIVER_C526, 0xC52E: NANO_RECEIVER_C52E, 0xC531: NANO_RECEIVER_C531, 0xC534: NANO_RECEIVER_C534, 0xC535: NANO_RECEIVER_C535, 0xC537: NANO_RECEIVER_C537, 0x6042: NANO_RECEIVER_6042, 0xC539: LIGHTSPEED_RECEIVER_C539, 0xC53A: LIGHTSPEED_RECEIVER_C53A, 0xC53D: LIGHTSPEED_RECEIVER_C53D, 0xC53F: LIGHTSPEED_RECEIVER_C53F, 0xC541: LIGHTSPEED_RECEIVER_C541, 0xC545: LIGHTSPEED_RECEIVER_C545, 0xC547: LIGHTSPEED_RECEIVER_C547, 0xC54D: LIGHTSPEED_RECEIVER_C54D, 0xC517: EX100_27MHZ_RECEIVER_C517, } def get_receiver_info(product_id: int) -> dict[str, Any]: """Returns hardcoded information about a Logitech receiver. Parameters ---------- product_id Product ID (pid) of the receiver, e.g. 0xC548 for a Logitech Bolt receiver. Returns ------- dict[str, Any] Receiver info with mandatory fields: - vendor_id - product_id Raises ------ ValueError If the product ID is unknown. """ try: return KNOWN_RECEIVERS[product_id] except KeyError: pass raise ValueError(f"Unknown product ID '0x{product_id:02X}'") ================================================ FILE: lib/logitech_receiver/common.py ================================================ ## 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 Flag 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(f"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), f"{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(Flag): 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)} 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 ================================================ FILE: lib/logitech_receiver/descriptors.py ================================================ ## 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") # incorrect _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("G500 Gaming Mouse", codename="G500 Gaming", usbid=0xC068, interface=1, protocol=1.0) _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, ) ================================================ FILE: lib/logitech_receiver/desktop_notifications.py ================================================ ## 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: return 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 ================================================ FILE: lib/logitech_receiver/device.py ================================================ ## 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 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 .hidpp10_constants import NotificationFlag 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) -> int: ... 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 e except Exception as e: logger.exception("open %s", device_info) raise e 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 = self._force_buttons = 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.present = True # used for devices that are integral with their receiver but that separately be disconnected self._feature_settings_checked = False self._gestures_lock = threading.Lock() self._settings_lock = threading.Lock() self._persister_lock = threading.Lock() self._simple_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) ) # for direct-connected devices get 'number' from descriptor protocol else use 0xFF self.number = 0x00 if self.descriptor and self.descriptor.protocol and self.descriptor.protocol < 2.0 else 0xFF try: # determine whether a direct-connected device is online self.ping() except exceptions.NoSuchDevice as e: if self.number == 0xFF: # guessed wrong number? self.number = 0x00 self.ping() else: raise e 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 = {} 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 or name or codename assert id, "need id to find a device" for device in Device.instances: if device.online and (device.unitId == id or device.serial == id or device.name == id or device.codename == id): return device @property def protocol(self): if not self._protocol: try: self.ping() except exceptions.NoSuchDevice: logger.warning("device %s inaccessible - no protocol set", self) 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 and self.name: names = self.name.split(" ") self._codename = names[1 if len(names) > 1 and names[0] == "Logitech" else 0] 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 f"?? ({self.wpid or self.product_id})" @property def name(self): if not self._name: with self._simple_lock: if self._name is None: if self.online and self.protocol >= 2.0: self._name = _hidpp20.get_name(self) return self._name or self._codename or f"Unknown device {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 force_buttons(self): if self._force_buttons is None: if self.online and self.protocol >= 2.0: self._force_buttons = _hidpp20.get_force_buttons(self) or () return self._force_buttons 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 and result is not None and result != 0: self.persister["_battery"] = result.value 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 logger.isEnabledFor(logging.DEBUG): logger.debug("device %d changing: active=%s %s present=%s", self.number, active, self._active, self.present) 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 = NotificationFlag.BATTERY_STATUS | NotificationFlag.UI | NotificationFlag.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): if flag_bits is None: flag_names = None else: flag_names = hidpp10_constants.NotificationFlag.flag_names(flag_bits) is_enabled = "enabled" if enable else "disabled" logger.info(f"{self}: device notifications {is_enabled} {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 if self.receiver else None), 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 and present, returns True of False. Some devices are integral with their receiver but may not be present even if the receiver responds to ping.""" 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 try: protocol = self.low_level.ping(handle, self.number, long_message=long) except exceptions.NoReceiver: # if ping fails, device is offline protocol = None self.online = protocol is not None and self.present if protocol: self._protocol = protocol if logger.isEnabledFor(logging.DEBUG): logger.debug("pinged %s: online %s protocol %s present %s", self.number, self.online, protocol, self.present) 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() ================================================ FILE: lib/logitech_receiver/diversion.py ================================================ ## 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 uinput. # For uinput to work the user must have write access for /dev/uinput. # The Solaar udev rule should set this up # Otherwise 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 # uinput and evdev - 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: _x11 = None # X11 might be available except Exception: _x11 = False # X11 is not available # Globals 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 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 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"]), "back": (10, evdev.ecodes.ecodes["BTN_SIDE"]), "forward": (11, evdev.ecodes.ecodes["BTN_EXTRA"]), } # 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) 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_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 evdev and simulate_uinput(evdev.ecodes.EV_KEY, code - 8, event): return True logger.warning("no way to simulate key input") 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 click_uinput(button, count): return True logger.warning("no way to simulate mouse click") return False def simulate_scroll(dx, dy): 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 = f"({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 f"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 f"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 f"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.replace(" ", "_")] except KeyError: self.feature = None if warn: logger.warning("rule Feature argument not name of a feature: %s", feature) def __str__(self): return f"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 f"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 f"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 f"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 f"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 f"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 f"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 or device.codename == self.devID or device.name == 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 f"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, depress, or release", count, ) self.count = 1 def __str__(self): return f"MouseClick: {self.button} ({str(self.count)})" def evaluate(self, feature, notification: HIDPPNotification, device, last_result): if logger.isEnabledFor(logging.INFO): logger.info(f"MouseClick action: {str(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 f"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 %s", feature, 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() ================================================ FILE: lib/logitech_receiver/exceptions.py ================================================ ## 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 ================================================ FILE: lib/logitech_receiver/hidpp10.py ================================================ ## 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 NotificationFlag 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): flags = self._get_register(device, Registers.NOTIFICATIONS) if flags is not None: return NotificationFlag(flags) def set_notification_flags(self, device: Device, *flag_bits: NotificationFlag): 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.value) 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) ================================================ FILE: lib/logitech_receiver/hidpp10_constants.py ================================================ ## 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 from enum import IntEnum from enum import IntFlag from typing import List 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 ) class PowerSwitchLocation(IntEnum): UNKNOWN = 0x00 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 @classmethod def location(cls, loc: int) -> PowerSwitchLocation: try: return cls(loc) except ValueError: return cls.UNKNOWN class NotificationFlag(IntFlag): """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 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 """ @classmethod def flag_names(cls, flags) -> List[str]: """Extract the names of the flags from the integer.""" return flags.name.replace("_", " ").lower().split("|") 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 def flags_to_str(flags, fallback: str) -> str: flag_names = [] if flags is not None and flags is not False: if flags.value == 0: flag_names = (fallback,) else: flag_names = NotificationFlag.flag_names(flags) return f"\n{' ':15}".join(sorted(flag_names)) 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 @property def label(self) -> str: return self.name.lower().replace("_", " ") class BoltPairingError(IntEnum): DEVICE_TIMEOUT = 0x01 FAILED = 0x02 @property def label(self) -> str: return self.name.lower().replace("_", " ") 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 class InfoSubRegisters(IntEnum): 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 class DeviceFeature(IntFlag): """Features for devices. Flags taken from https://drive.google.com/file/d/0BxbRzx7vEV7eNDBheWY0UHM5dEU/view?usp=sharing """ @classmethod def flag_names(cls, flag_bits: int) -> List[str]: """Extract the names of the flags from the integer.""" indexed = {item.value: item.name for item in cls} flag_names = [] unknown_bits = flag_bits 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 & flag_bits == k: unknown_bits &= ~k flag_names.append(indexed[k].replace("_", " ").lower()) # Yield any remaining unknown bits if unknown_bits != 0: flag_names.append(f"unknown:{unknown_bits:06X}") return flag_names 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 ================================================ FILE: lib/logitech_receiver/hidpp20.py ================================================ ## 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 collections import UserDict from enum import Flag from enum import IntEnum from typing import Any from typing import Dict from typing import Generator 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 DEVICE_KIND from .hidpp20_constants import ChargeLevel from .hidpp20_constants import ChargeType from .hidpp20_constants import ErrorCode from .hidpp20_constants import FeatureFlag 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: ... # pfps: Consider adding a class method that sanitizes inputs by removing unknown bits. class KeyFlag(Flag): """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. """ UNUSED_8000 = 0x8000 UNUSED_4000 = 0x4000 UNUSED_2000 = 0x2000 UNUSED_1000 = 0x1000 RAW_WHEEL = 0x800 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 class MappingFlag(Flag): """Flags describing the reporting method of a control. We treat bytes 2 and 5 of `get/setCidReporting` as a single bitfield """ UNUSED_4000 = 0x4000 UNUSED_1000 = 0x1000 RAW_WHEEL = 0x400 UNKNOWN_200 = 0x200 # seen on a Wireless Mouse M510 WPID 4004 ANALYTICS_KEY_EVENTS_REPORTING = 0x100 FORCE_RAW_XY_DIVERTED = 0x40 RAW_XY_DIVERTED = 0x10 PERSISTENTLY_DIVERTED = 0x04 DIVERTED = 0x01 class ChargeStatus(Flag): CHARGING = 0x00 FULL = 0x01 NOT_CHARGING = 0x02 ERROR = 0x07 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.flags = {} 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] self.flags[feature] = response[2] 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 get_flags(self, feature: NamedInt) -> Optional[int]: if self[feature]: return self.flags.get(feature, 0) def get_hidden(self, feature: NamedInt) -> Optional[bool]: if self[feature]: return self.flags.get(feature, 0) & FeatureFlag.INTERNAL return True 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] self.flags[feature] = response[1] 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. Read-only properties: - index -- index in the control ID table - key -- the name of this control - default_task -- the native function of this control - flags -- capabilities and desired software handling of the control Ref: https://drive.google.com/file/d/0BxbRzx7vEV7eU3VfMnRuRXktZ3M/view """ def __init__(self, device: Device, index: int, cid: int, task_id: int, flags: int): self._device = device self.index = index self._cid = cid self._tid = task_id 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`.""" try: task = str(special_keys.Task(self._tid)) except ValueError: task = f"unknown:{self._tid:04X}" return NamedInt(self._cid, task) @property def flags(self) -> KeyFlag: return KeyFlag(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, task_id, flags, pos, group, gmask): ReprogrammableKey.__init__(self, device, index, cid, task_id, 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() try: task = str(special_keys.Task(self._device.keys.cid_to_tid[self._mapped_to])) except ValueError: task = f"Unknown_{self._mapped_to:x}" return NamedInt(self._mapped_to, task) @property def remappable_to(self): 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]: cid = self._device.keys.cid_to_tid[tgt_cid] try: tgt_task = str(special_keys.Task(cid)) except ValueError: tgt_task = f"unknown:{cid:04X}" 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) -> MappingFlag: if self._mapping_flags is None: self._getCidReporting() return MappingFlag(self._mapping_flags) def set_diverted(self, value: bool) -> None: """If set, the control is diverted temporarily and reports presses as HID++ events.""" flags = {MappingFlag.DIVERTED: value} self._setCidReporting(flags=flags) def set_persistently_diverted(self, value: bool) -> None: """If set, the control is diverted permanently and reports presses as HID++ events.""" flags = {MappingFlag.PERSISTENTLY_DIVERTED: value} self._setCidReporting(flags=flags) def set_rawXY_reporting(self, value: bool) -> None: """If set, the mouse temporarily reports all its raw XY events while this control is pressed as HID++ events.""" flags = {MappingFlag.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 # The capability required to set a given reporting flag. FLAG_TO_CAPABILITY = { MappingFlag.DIVERTED: KeyFlag.DIVERTABLE, MappingFlag.PERSISTENTLY_DIVERTED: KeyFlag.PERSISTENTLY_DIVERTABLE, MappingFlag.ANALYTICS_KEY_EVENTS_REPORTING: KeyFlag.ANALYTICS_KEY_EVENTS, MappingFlag.FORCE_RAW_XY_DIVERTED: KeyFlag.FORCE_RAW_XY, MappingFlag.RAW_XY_DIVERTED: KeyFlag.RAW_XY, } bfield = 0 for mapping_flag, activated in flags.items(): key_flag = FLAG_TO_CAPABILITY[mapping_flag] if activated and key_flag not in self.flags: raise exceptions.FeatureNotSupported( msg=f'Tried to set mapping flag "{mapping_flag}" on control "{self.key}" ' + f'which does not support "{key_flag}" on device {self._device}.' ) bfield |= mapping_flag.value if activated else 0 bfield |= mapping_flag.value << 1 # The 'Xvalid' bit if self._mapping_flags: # update flags if already read if activated: self._mapping_flags |= mapping_flag.value else: self._mapping_flags &= ~mapping_flag.value 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 f"Key: {str(self.modifiers)}{str(self.remapped)}" elif self.actionId == special_keys.ACTIONID.Mouse: return f"Mouse Button: {str(self.remapped)}" elif self.actionId == special_keys.ACTIONID.Xdisp: return f"X Displacement {str(self.remapped)}" elif self.actionId == special_keys.ACTIONID.Ydisp: return f"Y Displacement {str(self.remapped)}" elif self.actionId == special_keys.ACTIONID.Vscroll: return f"Vertical Scroll {str(self.remapped)}" elif self.actionId == special_keys.ACTIONID.Hscroll: return f"Horizontal Scroll: {str(self.remapped)}" elif self.actionId == special_keys.ACTIONID.Consumer: return f"Consumer: {str(self.remapped)}" elif self.actionId == special_keys.ACTIONID.Internal: return f"Internal Action {str(self.remapped)}" elif self.actionId == special_keys.ACTIONID.Internal: return f"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, task_id, flags = struct.unpack("!HHB", keydata[:5]) self.keys[index] = ReprogrammableKey(self.device, index, cid, task_id, flags) self.cid_to_tid[cid] = task_id 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, task_id, flags1, pos, group, gmask, flags2 = struct.unpack("!HHBBBBB", keydata[:9]) flags = flags1 | (flags2 << 8) self.keys[index] = ReprogrammableKeyV4(self.device, index, cid, task_id, flags, pos, group, gmask) self.cid_to_tid[cid] = task_id 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: try: remapped = special_keys.HorizontalScroll(remapped) except ValueError: remapped = f"unknown horizontal scroll:{remapped:04X}" 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"),), } class SpecGesture(IntEnum): """Spec IDs for feature GESTURE_2.""" 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 def __str__(self): return f"{self.name.replace('_', ' ').lower()}" class ActionId(IntEnum): """Action IDs for feature GESTURE_2.""" MOVE_POINTER = 1 SCROLL_HORIZONTAL = 2 WHEEL_SCROLLING = 3 SCROLL_VERTICAL = 4 SCROLL_OR_PAGE_XY = 5 SCROLL_OR_PAGE_HORIZONTAL = 6 PAGE_SCREEN = 7 DRAG = 8 SECONDARY_DRAG = 9 ZOOM = 10 SCROLL_HORIZONTAL_ONLY = 11 SCROLL_VERTICAL_ONLY = 12 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: int, high): self._device = device self.id = low try: self.spec = SpecGesture(low) except ValueError: self.spec = f"unknown:{low:04X}" 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(" Button: behavior = bytes_[0] >> 4 if behavior == ButtonBehavior.MACRO_EXECUTE or behavior == ButtonBehavior.MACRO_STOP: sector = ((bytes_[0] & 0x0F) << 8) + bytes_[1] address = (bytes_[2] << 8) + bytes_[3] result = cls(behavior=behavior, sector=sector, address=address) elif behavior == ButtonBehavior.SEND: try: mapping_type = ButtonMappingType(bytes_[1]).value if mapping_type == ButtonMappingType.BUTTON: value = ButtonButtons[(bytes_[2] << 8) + bytes_[3]] result = cls(behavior=behavior, type=mapping_type, value=value) elif mapping_type == ButtonMappingType.MODIFIER_AND_KEY: modifiers = bytes_[2] value = ButtonKeys[bytes_[3]] result = cls(behavior=behavior, type=mapping_type, modifiers=modifiers, value=value) elif mapping_type == ButtonMappingType.CONSUMER_KEY: value = ButtonConsumerKeys[(bytes_[2] << 8) + bytes_[3]] result = cls(behavior=behavior, type=mapping_type, value=value) elif mapping_type == ButtonMappingType.NO_ACTION: result = cls(behavior=behavior, type=mapping_type) except Exception: pass elif behavior == ButtonBehavior.FUNCTION: second_byte = bytes_[1] try: btn_func = ButtonFunctions(second_byte).value except ValueError: btn_func = second_byte data = bytes_[3] result = cls(behavior=behavior, value=btn_func, 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 == ButtonBehavior.MACRO_EXECUTE.value or self.behavior == ButtonBehavior.MACRO_STOP.value: bytes = common.int2bytes((self.behavior << 12) + self.sector, 2) + common.int2bytes(self.address, 2) elif self.behavior == ButtonBehavior.SEND.value: bytes += common.int2bytes(self.type, 1) if self.type == ButtonMappingType.BUTTON: bytes += common.int2bytes(self.value, 2) elif self.type == ButtonMappingType.MODIFIER_AND_KEY: bytes += common.int2bytes(self.modifiers, 1) bytes += common.int2bytes(self.value, 1) elif self.type == ButtonMappingType.CONSUMER_KEY: bytes += common.int2bytes(self.value, 2) elif self.type == ButtonMappingType.NO_ACTION: bytes += b"\xff\xff" elif self.behavior == ButtonBehavior.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([f"{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(" list[tuple[int, int]]: """Returns profile headers. Returns ------- list[tuple[int, int]] Tuples contain (sector, enabled). """ i = 0 headers = [] chunk = device.feature_request(SupportedFeature.ONBOARD_PROFILES, 0x50, 0, 0, 0, i) s = 0x00 if chunk[0:4] == b"\x00\x00\x00\x00" or chunk[0:4] == b"\xff\xff\xff\xff": # look in ROM instead chunk = device.feature_request(SupportedFeature.ONBOARD_PROFILES, 0x50, 0x01, 0, 0, i) s = 0x01 while chunk[0:2] != b"\xff\xff": sector, enabled = struct.unpack("!HB", chunk[0:3]) headers.append((sector, enabled)) i += 1 chunk = device.feature_request(SupportedFeature.ONBOARD_PROFILES, 0x50, s, 0, 0, i * 4) return headers @classmethod def from_device(cls, device): if not device.online: # wake the device up if necessary device.ping() response = device.feature_request(SupportedFeature.ONBOARD_PROFILES, 0x00) memory, profile, _macro = struct.unpack("!BBB", response[0:3]) if memory != 0x01 or profile > 0x05: 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 = {} for i, (sector, enabled) in enumerate(headers, start=1): profiles[i] = OnboardProfile.from_dev(device, i, sector, size, enabled, buttons, gbuttons) 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) 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 device.features and SupportedFeature.REPROG_CONTROLS_V2 in device.features: count = device.feature_request(SupportedFeature.REPROG_CONTROLS_V2) return KeysArrayV2(device, ord(count[:1])) elif device.features and 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_force_buttons(self, device: Device): if getattr(device, "_force_buttons", None) is not None: return device._force_buttons if SupportedFeature.FORCE_SENSING_BUTTON in device.features: return ForceSensingButtonArray(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 f"{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 = ChargeStatus(flags & 0x03) if charge_sts is None: charge_sts = ErrorCode.UNKNOWN elif isinstance(charge_sts, ChargeStatus) and ChargeStatus.FULL in charge_sts: 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 charge_level = estimate_battery_level_percentage(voltage) if charge_level: charge_lvl = charge_level 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) -> tuple[SupportedFeature, Battery]: 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: approx_level = BatteryLevelApproximation.FULL elif level == 4: approx_level = BatteryLevelApproximation.GOOD elif level == 2: approx_level = BatteryLevelApproximation.LOW elif level == 1: approx_level = BatteryLevelApproximation.CRITICAL else: approx_level = BatteryLevelApproximation.EMPTY return SupportedFeature.UNIFIED_BATTERY, Battery(discharge if discharge else approx_level, None, status, None) def decipher_adc_measurement(report) -> tuple[SupportedFeature, Battery]: # partial implementation - needs mapping to levels adc_voltage, flags = struct.unpack("!HB", report[:3]) charge_level = estimate_battery_level_percentage(adc_voltage) if flags & 0x01: status = BatteryStatus.RECHARGING if flags & 0x02 else BatteryStatus.DISCHARGING return SupportedFeature.ADC_MEASUREMENT, Battery(charge_level, None, status, adc_voltage) def estimate_battery_level_percentage(value_millivolt: int) -> int | None: """Estimate battery level percentage based on battery voltage. Uses linear approximation to estimate the battery level in percent. Parameters ---------- value_millivolt Measured battery voltage in millivolt. """ battery_voltage_to_percentage = [ (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), ] if value_millivolt >= battery_voltage_to_percentage[0][0]: return battery_voltage_to_percentage[0][1] if value_millivolt <= battery_voltage_to_percentage[-1][0]: return battery_voltage_to_percentage[-1][1] for i in range(len(battery_voltage_to_percentage) - 1): v_high, p_high = battery_voltage_to_percentage[i] v_low, p_low = battery_voltage_to_percentage[i + 1] if v_low <= value_millivolt <= v_high: # Linear interpolation percent = p_low + (p_high - p_low) * (value_millivolt - v_low) / (v_high - v_low) return round(percent) return 0 class ForceSensingButton: """A button that has a force value at which to trigger the button""" @classmethod def create(cls, device, number: int): buttondata = device.feature_request(SupportedFeature.FORCE_SENSING_BUTTON, 0x10, number) buttoncurrent = device.feature_request(SupportedFeature.FORCE_SENSING_BUTTON, 0x20, number) if buttondata is not None and buttoncurrent is not None: changeable, default, max_value, min_value = struct.unpack("!HHHH", buttondata[:8]) changeable = changeable & 0x01 current = struct.unpack("!H", buttoncurrent[:2])[0] return cls(device, number, changeable, default, max_value, min_value, current) def __init__(self, device, number: int, changeable: bool, default: int, max_value: int, min_value: int, current: int): self._device = device self.number = number self.changeable = changeable self.default = default self.min_value = min_value self.max_value = max_value self._current = current def get_current(self) -> int: return self._current def set_current(self, current: int) -> None: if not self.changeable: logger.warning(f"FORCE_SENSING_BUTTON on device {self._device} does not allow changing force.") if self.min_value <= current <= self.max_value: ret = self._device.feature_request( SupportedFeature.FORCE_SENSING_BUTTON, 0x30, struct.pack("!BH", self.number, current) ) if ret is None and logger.isEnabledFor(logging.DEBUG): logger.debug(f"FORCE_SENSING_BUTTON setButtonConfig on device {self._device} didn't respond.") def acceptable_current(self, value: int) -> bool: return self.min_value <= value <= self.max_value class ForceSensingButtonArray(UserDict): """A map of buttons supporting force sensing""" def __new__(cls, device: Device): assert device is not None count = device.feature_request(SupportedFeature.FORCE_SENSING_BUTTON, 0x00) if count: instance = super().__new__(cls) instance._count = ord(count[:1]) return instance def __init__(self, device: Device): super().__init__(self) self.device = device for index in range(0, self._count): self[index] = None def __getitem__(self, index: int): item = super().__getitem__(index) if item is None: self.query_key(index) return super().__getitem__(index) def query_key(self, index): if index not in self: raise IndexError(index) button = ForceSensingButton.create(self.device, index) if button: self[index] = button return button def query(self): for index in self: button = ForceSensingButton.create(self.device, index) if button: self[index] = button return self # interface for single force button def get_current(self): return self[0].get_current() def set_current(self, current: int) -> None: self[0].set_current(current) def acceptable(self, value: int) -> bool: return self[0].acceptable(value) def acceptable_current_key(self, index: int, value: int) -> bool: return self[index].acceptable(value) ================================================ FILE: lib/logitech_receiver/hidpp20_constants.py ================================================ ## 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 PROPERTY_ACCESS = 0x0011 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 BLE_PRO_PRE_PAIRING = 0x1816 BACKLIGHT = 0x1981 BACKLIGHT2 = 0x1982 BACKLIGHT3 = 0x1983 ILLUMINATION = 0x1990 FORCE_SENSING_BUTTON = 0x19C0 HAPTIC = 0x19B0 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 FULL_KEY_CUSTOMIZATION = 0x1B05 CONTROL_LIST = 0x1B10 SWITCH_SWAPABILITY = 0x1B20 DEVICE_MODE = 0x1B30 REPORT_HID_USAGE = 0x1BC0 PERSISTENT_REMAPPABLE_ACTION = 0x1C00 WIRELESS_DEVICE_STATUS = 0x1D4B REMAINING_PAIRING = 0x1DF0 ENABLE_HIDDEN_FEATURES = 0x1E00 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 KEYBOARD_DISABLE_CONTROLS = 0x4523 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 LOGI_MODIFIERS = 0x8051 REPORT_RATE = 0x8060 EXTENDED_ADJUSTABLE_REPORT_RATE = 0x8061 COLOR_LED_EFFECTS = 0x8070 RGB_EFFECTS = 0x8071 RPM_INDICATOR = 0x807A RPM_LED_PATTERN = 0x807B PER_KEY_LIGHTING = 0x8080 PER_KEY_LIGHTING_V2 = 0x8081 MODE_STATUS = 0x8090 LEGACY_AXIS_RESPONSE_CURVE = 0x80A3 AXIS_RESPONSE_CURVE = 0x80A4 BANDED_AXIS = 0x80B1 COMBINED_PEDALS = 0x80D0 BUNNY_HOPPING = 0x80E0 ONBOARD_PROFILES = 0x8100 PROFILE_MANAGEMENT = 0x8101 MOUSE_BUTTON_SPY = 0x8110 LATENCY_MONITORING = 0x8111 GAMING_ATTACHMENTS = 0x8120 FORCE_FEEDBACK = 0x8123 DUAL_CLUTCH = 0x8127 WHEEL_CENTER_POSITION = 0x812C DISPLAY_GAME_DATA = 0x8130 CENTER_SPRING = 0x8131 AXIS_MAPPING = 0x8132 GLOBAL_DAMPING = 0x8133 BRAKE_FORCE = 0x8134 PEDAL_STATUS = 0x8135 TORQUE_LIMIT = 0x8136 CONFIGURATION_PROFILES = 0x8137 OPERATING_RANGE = 0x8138 TRUE_FORCE = 0x8139 FFB_FILTER = 0x8140 # 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 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 HapticWaveForms = NamedInts( SHARP_STATE_CHANGE=0x00, DAMP_STATE_CHANGE=0x01, SHARP_COLLISION=0x02, DAMP_COLLISION=0x03, SUBTLE_COLLISION=0x04, HAPPY_ALERT=0x05, ANGRY_ALERT=0x06, COMPLETED=0x07, SQUARE=0x08, WAVE=0x09, FIREWORK=0x0A, MAD=0x0B, KNOCK=0x0C, JINGLE=0x0D, RINGING=0xE, WHISPER_COLLISION=0x1B, ) ================================================ FILE: lib/logitech_receiver/i18n.py ================================================ ## 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"), ) ================================================ FILE: lib/logitech_receiver/listener.py ================================================ ## 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=f"{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__ ================================================ FILE: lib/logitech_receiver/notifications.py ================================================ ## 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 settings_templates from .common import Alert from .common import BatteryStatus from .common import Notification from .hidpp10_constants import Registers from .hidpp20_constants import SupportedFeature if typing.TYPE_CHECKING: from .base import HIDPPNotification from .device import Device from .receiver import Receiver logger = logging.getLogger(__name__) NotificationHandler = typing.Callable[["Receiver", "HIDPPNotification"], bool] _hidpp10 = hidpp10.Hidpp10() _hidpp20 = hidpp20.Hidpp20() notification_lock = threading.Lock() def process(device: Device | Receiver, notification: HIDPPNotification): """Handle incoming events (notification) from device or receiver.""" 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, notification: HIDPPNotification) -> bool | None: """Process event messages from receivers.""" event_handler_mapping: dict[int, NotificationHandler] = { Notification.PAIRING_LOCK: handle_pairing_lock, Registers.DEVICE_DISCOVERY_NOTIFICATION: handle_device_discovery, Registers.DISCOVERY_STATUS_NOTIFICATION: handle_discovery_status, Registers.PAIRING_STATUS_NOTIFICATION: handle_pairing_status, Registers.PASSKEY_PRESSED_NOTIFICATION: handle_passkey_pressed, Registers.PASSKEY_REQUEST_NOTIFICATION: handle_passkey_request, } try: handler_func = event_handler_mapping[notification.sub_id] return handler_func(receiver, notification) except KeyError: pass assert notification.sub_id in [ Notification.CONNECT_DISCONNECT, Notification.DJ_PAIRING, Notification.CONNECTED, Notification.RAW_INPUT, Notification.POWER, ] logger.warning(f"{receiver}: unhandled notification {notification}") def process_device_notification(device: Device, notification: HIDPPNotification): """Process event messages from devices.""" # incoming packets with SubId >= 0x80 are supposedly replies from HID++ 1.0 requests, should never get here assert notification.sub_id & 0x80 == 0 if notification.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(notification) if handling_ret is not None: return handling_ret # 0x40 to 0x7F appear to be HID++ 1.0 or DJ notifications if notification.sub_id >= 0x40: if notification.report_id == base.DJ_MESSAGE_ID: return _process_dj_notification(device, notification) else: return _process_hidpp10_notification(device, notification) # 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, notification) # 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, notification.sub_id, notification) return False return _process_feature_notification(device, notification) def _process_dj_notification(device: Device, notification: HIDPPNotification): if logger.isEnabledFor(logging.DEBUG): logger.debug("%s (%s) DJ %s", device, device.protocol, notification) if notification.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, notification) return True if notification.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, notification) return True if notification.sub_id == Notification.CONNECTED: connected = not notification.address & 0x01 if logger.isEnabledFor(logging.INFO): logger.info("%s: DJ connection: %s %s", device, connected, notification) device.changed(active=connected, alert=Alert.NONE, reason=_("connected") if connected else _("disconnected")) return True logger.warning("%s: unrecognized DJ %s", device, notification) def _process_hidpp10_custom_notification(device: Device, notification: HIDPPNotification): if logger.isEnabledFor(logging.DEBUG): logger.debug("%s (%s) custom notification %s", device, device.protocol, notification) if notification.sub_id in (Registers.BATTERY_STATUS, Registers.BATTERY_CHARGE): assert notification.data[-1:] == b"\x00" data = chr(notification.address).encode() + notification.data device.set_battery_info(hidpp10.parse_battery_status(notification.sub_id, data)) return True logger.warning("%s: unrecognized %s", device, notification) def _process_hidpp10_notification(device: Device, notification: HIDPPNotification): if notification.sub_id == Notification.CONNECT_DISCONNECT: # device unpairing if notification.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, notification.address, notification) return True if notification.sub_id == Notification.DJ_PAIRING: # device connection (and disconnection) flags = ord(notification.data[:1]) & 0xF0 if notification.address == 0x02: # very old 27 MHz protocol wpid = "00" + common.strhex(notification.data[2:3]) link_established = True link_encrypted = bool(flags & 0x80) elif notification.address > 0x00: # all other protocols are supposed to be almost the same wpid = common.strhex(notification.data[2:3] + notification.data[1:2]) link_established = not (flags & 0x40) link_encrypted = bool(flags & 0x20) or notification.address == 0x10 # Bolt protocol always encrypted else: logger.warning( "%s: connection notification with unknown protocol %02X: %s", device.number, notification.address, notification ) 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, notification.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 notification.sub_id == Notification.RAW_INPUT: # raw input event? just ignore it # if notification.address == 0x01, no idea what it is, but they keep on coming # if notification.address == 0x03, appears to be an actual input event, because they only come when input happents return True if notification.sub_id == Notification.POWER: if notification.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, notification) return True logger.warning("%s: unrecognized %s", device, notification) def _process_feature_notification(device: Device, notification: HIDPPNotification): old_present, device.present = device.present, True # the device is generating a feature notification so it must be present try: feature = device.features.get_feature(notification.sub_id) except IndexError: logger.warning("%s: notification from invalid feature index %02X: %s", device, notification.sub_id, notification) return False if logger.isEnabledFor(logging.DEBUG): logger.debug( "%s: notification for feature %s, report %s, data %s", device, feature, notification.address >> 4, common.strhex(notification.data), ) if feature == SupportedFeature.BATTERY_STATUS: if notification.address == 0x00: device.set_battery_info(hidpp20.decipher_battery_status(notification.data)[1]) elif notification.address == 0x10: if logger.isEnabledFor(logging.INFO): logger.info("%s: spurious BATTERY status %s", device, notification) else: logger.warning("%s: unknown BATTERY %s", device, notification) elif feature == SupportedFeature.BATTERY_VOLTAGE: if notification.address == 0x00: device.set_battery_info(hidpp20.decipher_battery_voltage(notification.data)[1]) else: logger.warning("%s: unknown VOLTAGE %s", device, notification) elif feature == SupportedFeature.UNIFIED_BATTERY: if notification.address == 0x00: device.set_battery_info(hidpp20.decipher_battery_unified(notification.data)[1]) else: logger.warning("%s: unknown UNIFIED BATTERY %s", device, notification) elif feature == SupportedFeature.ADC_MEASUREMENT: if notification.address == 0x00: result = hidpp20.decipher_adc_measurement(notification.data) if result: # if good data and the device was not present then a push is needed device.set_battery_info(result[1]) device.changed(active=True, alert=Alert.NONE, reason=_("ADC measurement notification"), push=not old_present) else: # this feature is also used to signal device becoming inactive device.present = False # exception to device presence device.changed(active=False) else: logger.warning("%s: unknown ADC MEASUREMENT %s", device, notification) elif feature == SupportedFeature.SOLAR_DASHBOARD: if notification.data[5:9] == b"GOOD": charge, lux, adc = struct.unpack("!BHH", notification.data[:5]) # guesstimate the battery voltage, emphasis on 'guess' # status_text = '%1.2fV' % (adc * 2.67793237653 / 0x0672) status_text = BatteryStatus.DISCHARGING if notification.address == 0x00: device.set_battery_info(common.Battery(charge, None, status_text, None)) elif notification.address == 0x10: if lux > 200: status_text = BatteryStatus.RECHARGING device.set_battery_info(common.Battery(charge, None, status_text, None, lux)) elif notification.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(SupportedFeature.SOLAR_DASHBOARD) # trigger a new report chain reports_count = 15 reports_period = 2 # seconds device.feature_request(SupportedFeature.SOLAR_DASHBOARD, 0x00, reports_count, reports_period) else: logger.warning("%s: unknown SOLAR CHARGE %s", device, notification) else: logger.warning("%s: SOLAR CHARGE not GOOD? %s", device, notification) elif feature == SupportedFeature.WIRELESS_DEVICE_STATUS: if notification.address == 0x00: if logger.isEnabledFor(logging.DEBUG): logger.debug("wireless status: %s", notification) reason = "powered on" if notification.data[2] == 1 else None if notification.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, notification) elif feature == SupportedFeature.TOUCHMOUSE_RAW_POINTS: if notification.address == 0x00: if logger.isEnabledFor(logging.INFO): logger.info("%s: TOUCH MOUSE points %s", device, notification) elif notification.address == 0x10: touch = ord(notification.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, notification) # TODO: what are REPROG_CONTROLS_V{2,3}? elif feature == SupportedFeature.REPROG_CONTROLS: if notification.address == 0x00: if logger.isEnabledFor(logging.INFO): logger.info("%s: reprogrammable key: %s", device, notification) else: logger.warning("%s: unknown REPROG_CONTROLS %s", device, notification) elif feature == SupportedFeature.BACKLIGHT2: if notification.address == 0x00: level = struct.unpack("!B", notification.data[1:2])[0] if device.setting_callback: device.setting_callback(device, settings_templates.Backlight2Level, [level]) elif feature == SupportedFeature.REPROG_CONTROLS_V4: if notification.address == 0x00: if logger.isEnabledFor(logging.DEBUG): cid1, cid2, cid3, cid4 = struct.unpack("!HHHH", notification.data[:8]) logger.debug("%s: diverted controls pressed: 0x%x, 0x%x, 0x%x, 0x%x", device, cid1, cid2, cid3, cid4) elif notification.address == 0x10: if logger.isEnabledFor(logging.DEBUG): dx, dy = struct.unpack("!hh", notification.data[:4]) logger.debug("%s: rawXY dx=%i dy=%i", device, dx, dy) elif notification.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, notification) elif feature == SupportedFeature.HIRES_WHEEL: if notification.address == 0x00: if logger.isEnabledFor(logging.INFO): flags, delta_v = struct.unpack(">bh", notification.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 notification.address == 0x10: ratchet = notification.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, notification) elif feature == SupportedFeature.ONBOARD_PROFILES: if notification.address > 0x10: if logger.isEnabledFor(logging.INFO): logger.info("%s: unknown ONBOARD PROFILES %s", device, notification) else: if notification.address == 0x00: profile_sector = struct.unpack("!H", notification.data[:2])[0] if profile_sector: settings_templates.profile_change(device, profile_sector) elif notification.address == 0x10: resolution_index = struct.unpack("!B", notification.data[:1])[0] profile_sector = struct.unpack("!H", device.feature_request(SupportedFeature.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 == SupportedFeature.BRIGHTNESS_CONTROL: if notification.address > 0x10: if logger.isEnabledFor(logging.INFO): logger.info("%s: unknown BRIGHTNESS CONTROL %s", device, notification) else: if notification.address == 0x00: brightness = struct.unpack("!H", notification.data[:2])[0] device.setting_callback(device, settings_templates.BrightnessControl, [brightness]) elif notification.address == 0x10: brightness = notification.data[0] & 0x01 if brightness: brightness = struct.unpack("!H", device.feature_request(SupportedFeature.BRIGHTNESS_CONTROL, 0x10)[:2])[0] device.setting_callback(device, settings_templates.BrightnessControl, [brightness]) diversion.process_notification(device, notification, feature) return True def handle_pairing_lock(receiver: Receiver, notification: HIDPPNotification) -> bool: receiver.pairing.lock_open = bool(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(notification.data[:1]) if pair_error: error_string = hidpp10_constants.PairingError(pair_error).label receiver.pairing.error = error_string receiver.pairing.new_device = None logger.warning("pairing error %d: %s", pair_error, error_string) receiver.changed(reason=reason) return True def handle_discovery_status(receiver: Receiver, notification: HIDPPNotification) -> bool: with notification_lock: receiver.pairing.discovering = 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(notification.data[:1]) if discover_error: receiver.pairing.error = discover_string = hidpp10_constants.BoltPairingError(discover_error).label logger.warning("bolt discovering error %d: %s", discover_error, discover_string) receiver.changed(reason=reason) return True def handle_device_discovery(receiver: Receiver, notification: HIDPPNotification) -> bool: with notification_lock: counter = notification.address + 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 notification.data[1] == 0: receiver.pairing.device_kind = notification.data[3] receiver.pairing.device_address = notification.data[6:12] receiver.pairing.device_authentication = notification.data[14] elif notification.data[1] == 1: receiver.pairing.device_name = notification.data[3 : 3 + notification.data[2]].decode("utf-8") return True def handle_pairing_status(receiver: Receiver, notification: HIDPPNotification) -> bool: with notification_lock: receiver.pairing.device_passkey = None receiver.pairing.lock_open = 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 = notification.data[0] if receiver.pairing.lock_open: receiver.pairing.new_device = None elif notification.address == 0x02 and not pair_error: receiver.pairing.new_device = receiver.register_new_device(notification.data[7]) if pair_error: receiver.pairing.error = error_string = hidpp10_constants.BoltPairingError(pair_error).label receiver.pairing.new_device = None logger.warning("pairing error %d: %s", pair_error, error_string) receiver.changed(reason=reason) return True def handle_passkey_request(receiver: Receiver, notification: HIDPPNotification) -> bool: with notification_lock: receiver.pairing.device_passkey = notification.data[0:6].decode("utf-8") return True def handle_passkey_pressed(_receiver: Receiver, _hidpp_notification: HIDPPNotification) -> bool: return True ================================================ FILE: lib/logitech_receiver/receiver.py ================================================ ## 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 InfoSubRegisters from .hidpp10_constants import NotificationFlag from .hidpp10_constants import Registers if typing.TYPE_CHECKING: from logitech_receiver import common from .base import HIDPPNotification logger = logging.getLogger(__name__) _hidpp10 = hidpp10.Hidpp10() 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 def extract_serial(response: bytes) -> str: """Extracts serial number from receiver response.""" return response.hex().upper() def extract_max_devices(response: bytes) -> int: """Extracts maximum number of supported devices from response.""" max_devices = response[6] return int(max_devices) def extract_remaining_pairings(response: bytes) -> int: ps = ord(response[2:3]) remaining_pairings = ps - 5 if ps >= 5 else -1 return int(remaining_pairings) def extract_codename(response: bytes) -> str: codename = response[2 : 2 + ord(response[1:2])] return codename.decode("ascii") def extract_power_switch_location(response: bytes) -> str: """Extracts power switch location from response.""" index = response[9] & 0x0F return hidpp10_constants.PowerSwitchLocation.location(index).name.lower() def extract_connection_count(response: bytes) -> int: """Extract connection count from receiver response.""" return ord(response[1:2]) def extract_wpid(response: bytes) -> str: """Extract wpid from receiver response.""" return response.hex().upper() def extract_polling_rate(response: bytes) -> int: """Returns polling rate in milliseconds.""" return int(response[2]) def extract_device_kind(response: int) -> str: return hidpp10_constants.DEVICE_KIND[response] 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, InfoSubRegisters.RECEIVER_INFORMATION) if serial_reply: self.serial = extract_serial(serial_reply[1:5]) self.max_devices = extract_max_devices(serial_reply) if not (1 <= 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: self._remaining_pairings = extract_remaining_pairings(ps) 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 = NotificationFlag.WIRELESS | NotificationFlag.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) if flag_bits is None: flag_names = None else: flag_names = hidpp10_constants.NotificationFlag.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, InfoSubRegisters.DEVICE_NAME + n - 1) if codename: return extract_codename(codename) 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: HIDPPNotification) -> tuple[bool, bool, typing.Any, str]: """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 = extract_device_kind(notification.data[0] & 0x0F) wpid = extract_wpid(notification.data[2:3] + notification.data[1:2]) 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, InfoSubRegisters.PAIRING_INFORMATION + n - 1) if pair_info: # a receiver that uses Unifying-style pairing registers wpid = extract_wpid(pair_info[3:5]) kind = extract_device_kind(pair_info[7] & 0x0F) polling_rate_ms = extract_polling_rate(pair_info) polling_rate = f"{polling_rate_ms}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 = extract_wpid(device_info[3:5]) kind = extract_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, InfoSubRegisters.EXTENDED_PAIRING_INFORMATION + n - 1) if pair_info: power_switch = extract_power_switch_location(pair_info) serial = extract_serial(pair_info[1:5]) 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 = extract_serial(pair_info[1:5]) 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) if count is None: return 0 return extract_connection_count(count) 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 = extract_serial(serial_reply) self.max_devices = product_info.get("max_devices", 1) def device_codename(self, n): codename = self.read_register(Registers.RECEIVER_INFO, InfoSubRegisters.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, InfoSubRegisters.BOLT_PAIRING_INFORMATION + n) if pair_info: wpid = extract_wpid(pair_info[3:4] + pair_info[2:3]) kind = extract_device_kind(pair_info[1] & 0x0F) serial = extract_serial(pair_info[4:8]) 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 = extract_device_kind(_get_kind_from_index(self, number)) wpid = "00" + extract_wpid(notification.data[2:3]) 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 = extract_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: int) -> int: """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 e except Exception: logger.exception("open %s", device_info) ================================================ FILE: lib/logitech_receiver/settings.py ================================================ ## 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 struct import time from enum import IntEnum from typing import Any from solaar.i18n import _ from . import common from . import hidpp20_constants from . import settings_validator from .common import NamedInt logger = logging.getLogger(__name__) SENSITIVITY_IGNORE = "ignore" class Kind(IntEnum): NONE = 0 TOGGLE = 0x01 CHOICE = 0x02 RANGE = 0x04 MAP_CHOICE = 0x0A MULTIPLE_TOGGLE = 0x10 PACKED_RANGE = 0x20 MULTIPLE_RANGE = 0x40 HETERO = 0x80 MAP_RANGE = 0x102 COLOR = 0x200 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 = {} display = True # display setting in UI 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) -> Any | None: 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 if self.read_fnid is not None: return device.feature_request(self.feature, self.read_fnid, self.prefix, self.read_prefix, data_bytes) else: return b"" 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 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 = settings_validator.BooleanValidator ================================================ FILE: lib/logitech_receiver/settings_new.py ================================================ ## Copyright (C) 2025 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. ## A new way of supporting settings, using a feature-specifi device class to store, read, and write relevant information ## The setting uses the device class to interact with the device feature. ## The setting uses a persist class to keep track of the setting. ## Interface: import logging from .settings import Kind logger = logging.getLogger(__name__) class Setting: name = None # Solaar internal name for the setting label = None # Solaar user name for the setting (translatable) description = None # Solaar extra desciption for the setting (translatable) feature = None # Logitech feature that the setting uses min_version = 0 # Minimum version of the feature needed setup = None # method name on Device class to get the device object get = None # method name on the device object to get the setting value set = None # method name on the device object to set the setting value acceptable = None # method name on the device object to check for acceptable values choices_universe = None # All possible acceptable keys, for settings with keys kind = Kind.NONE # What GUI interface to use persist = True # Whether to remember the setting display = True # display setting in UI _device = None # The device that this setting is for _device_object = None # The object that interacts with the feature for the device _value = None # Stored value as maintained by Solaar, used for persistence def __init__(self, device, device_object): self._device = device self._device_object = device_object @classmethod def build(cls, device): cls.check_properties(cls) device_object = getattr(device, cls.setup)() if device_object: setting = cls(device, device_object) return setting @classmethod def check_properties(cl, cls): assert cls.name and cls.label and cls.description, "New settings require a name, label, and description" assert cls.feature, "New settings require a feature" assert cls.setup, "New settings require a setup device method" assert cls.get and cls.set and cls.acceptable, "New settings require get, set, and acceptable methods" def setup_from_class(self, clss): """Copy settings methods for a new setting from a settting class""" self.name = clss.name self.label = clss.label self.description = clss.description self.feature = clss.feature self.min_version = clss.min_version self.setup = clss.setup self.get = clss.get self.set = clss.set self.acceptable = clss.acceptable self.choices_universe = clss.choices_universe self.kind = clss.kind self.persist = clss.persist def _pre_read(self, cached): """Get information from and save information to the persister""" # Get the persister map if available and not done already if self.persist and self._value is None and getattr(self._device, "persister", None): self._value = self._device.persister.get(self.name) # If this is new save its current value for the next time if cached and self._value is not None: 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 def read(self, cached=True): """Get all the data for the setting. If cached is True the data in the _value can be used.""" self._pre_read(cached) if logger.isEnabledFor(logging.DEBUG): logger.debug("%s: setting read %r from %s", self.name, self._value, self._device) if cached and self._value is not None: return self._value if cached: self._value = getattr(self._device_object, self.get)() return self._value if self._device.online: self._value = getattr(self._device_object.query(), self.get)() return self._value def write(self, value, save=True): """Write the value to the device. If saved is True also save in the persister""" pass ## fill out def apply(self): """Write saved data to the device, using persisted data if available""" if logger.isEnabledFor(logging.DEBUG): logger.debug("%s: apply (%s)", self.name, self._device) value = None 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, value, self._device, repr(e)) @property def range(self): if self.kind == Kind.RANGE: return self.min_value, self.max_value def val_to_string(self, value): return str(value) ## key mapping from symbols to values???? class Settings(Setting): """A setting descriptor for multiple keys. Supported by a class that provides the interface to the device, see ForceSensingButtonArray in hidpp20.py Picks out a field from the mapped device feature objects.""" # setup creates a dictionary with entries for all the keys # _value is a map from keys to values # get, set, and acceptable are methods of dict value objects, not of the device object itself #### FIX THIS! MAYBE?? def __init__(self, device, device_object): super().__init__(device, device_object) self._value = {} def read(self, cached=True): self._pre_read(cached) if logger.isEnabledFor(logging.DEBUG): logger.debug("%s: settings read %r from %s", self.name, self._value, self._device) for key in self._device_object: self.read_key(key, cached) return self._value def read_key(self, key, cached=True): """Get the data for the key. If cached is True the data in the device_object can be used.""" self._pre_read(cached) if key not in self._device_object: logger.error("%s: settings illegal read key %r for %s", self.name, key, self._device) return None if logger.isEnabledFor(logging.DEBUG): logger.debug("%s: settings key %r read %r from %s", self.name, key, self._value, self._device) if cached and key in self._value and self._value[key] is not None: return self._value[key] if cached: data = self._device_object[key] self._value[key] = getattr(data, self.get)() return self._value[key] if self._device.online: data = self._device_object.query_key(key) self._value[key] = getattr(data, self.get)() return self._value[key] def write(self, value, save=True): if logger.isEnabledFor(logging.DEBUG): logger.debug("%s: settings read %r from %s", self.name, self._value, self._device) if isinstance(value, dict): for key, val in value.items(): self.write_key_value(key, val, save) else: # to mimic interface for non-dict setting key = next(iter(self._device_object)) self.write_key_value(key, value, save) return value def write_key_value(self, key, value, save=True): """Write the data for the key. If saved is True also save in the persister""" if key not in self._device_object: logger.error("%s: settings illegal write key %r for %s", self.name, key, self._device) return 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 self._device_object[key] is None: self.read_key(key) if self._device_object[key] is None: logger.error("%s: settings illegal write key %r for %s", self.name, key, self._device) return None if not getattr(self._device_object[key], self.acceptable)(value): logger.error("%s: settings illegal write key %r value %r for %s", self.name, key, value, self._device) return None self._value[key] = value if self._device.persister and self.persist and save: self._device.persister[self.name][key] = value getattr(self._device_object[key], self.set)(value) return value ================================================ FILE: lib/logitech_receiver/settings_templates.py ================================================ ## 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 Callable from typing import Protocol from solaar.i18n import _ from . import base from . import common from . import descriptors from . import desktop_notifications from . import diversion from . import exceptions from . import hidpp20 from . import hidpp20_constants from . import settings from . import settings_new from . import settings_validator from . import special_keys from .hidpp10_constants import Registers from .hidpp20 import KeyFlag from .hidpp20 import MappingFlag from .hidpp20_constants import GestureId from .hidpp20_constants import ParamId logger = logging.getLogger(__name__) _hidpp20 = hidpp20.Hidpp20() _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). # read_skip_byte_count is as for BooleanV # write_prefix_bytes is as for BooleanV # 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_validator.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] # somehow messed up ? 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_validator.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_validator.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_validator.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_validator.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_validator.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_validator.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_validator.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_validator.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_validator.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_validator.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_validator.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_validator.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_validator.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_validator.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_validator.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} class ScrollRatchetTorque(settings.Setting): name = "scroll-ratchet-torque" label = _("Scroll Wheel Ratchet Torque") description = _("Change the torque needed to overcome the ratchet.") feature = _F.SMART_SHIFT_ENHANCED min_value = 1 max_value = 100 rw_options = {"read_fnid": 0x10, "write_fnid": 0x20} class rw_class(settings.FeatureRW): def write(self, device, data_bytes): ratchetSetting = next(filter(lambda s: s.name == "scroll-ratchet", device.settings), None) if ratchetSetting: # for MX Master 4, the ratchet setting needs to be written for changes to take effect ratchet_value = ratchetSetting.read(True) data_bytes = ratchet_value.to_bytes(1, "big") + data_bytes[1:] result = super().write(device, data_bytes) return result class validator_class(settings_validator.RangeValidator): @classmethod def build(cls, setting_class, device): reply = device.feature_request(_F.SMART_SHIFT_ENHANCED, 0x00) if reply[0] & 0x01: # device supports tunable torque return cls( min_value=setting_class.min_value, max_value=setting_class.max_value, byte_count=1, write_prefix_bytes=b"\x00\x00", # don't change mode or disengage, but see above read_skip_byte_count=2, ) # 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_validator.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 MappingFlag.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_validator.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 key in device.keys: if KeyFlag.DIVERTABLE in key.flags and KeyFlag.VIRTUAL not in key.flags: if KeyFlag.RAW_XY in key.flags: choices[key.key] = setting_class.choices_gesture if gestures is None: gestures = MouseGesturesXY(device, name="MouseGestures") if _F.ADJUSTABLE_DPI in device.features: choices[key.key] = setting_class.choices_universe if sliding is None: sliding = DpiSlidingXY( device, name="DpiSliding", show_notification=desktop_notifications.show ) else: choices[key.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") + "\n" + _("May need Onboard Profiles set to Disable to be effective.") 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_validator.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[1] = "LOW" choices_universe[2] = "MEDIUM" choices_universe[3] = "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_validator.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_validator.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 KeyFlag.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_validator.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_validator.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 f"{str(version >> 8)}.{str(version & 0xFF)}" else: return str(version >> 8) return "" if low == 0 and high == 0 else f" {_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_validator.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_validator.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] = f"{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_validator.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_validator.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_validator.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_validator.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_validator.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_validator.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_validator.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 min_version = 2 # documentation for version 1 does not mention this capability rw_options = {"read_fnid": 0x10, "write_fnid": 0x20} validator_class = settings_validator.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_validator.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_validator.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_validator.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_" # the trailing underscore signals that this setting creates other settings 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.COLOR, "label": _("Color")} 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.LedRampChoice} 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_validator.HeteroValidator( data_class=hidpp20.LEDEffectSetting, options=zone.effects, readable=infos.readable and read_fnid is not None ) 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_validator.ChoicesValidator validator_options = {"choices": choices_universe, "write_prefix_bytes": b"\x01", "read_skip_byte_count": 1} class RGBEffectSetting(LEDZoneSetting): name = "rgb_zone_" # the trailing underscore signals that this setting creates other settings 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, None, 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_validator.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, f"KEY {str(i)}") ) choices_map[key] = setting_class.choices_universe result = cls(choices_map) if choices_map else None return result # Allow changes to force sensing buttons class ForceSensing(settings_new.Settings): name = "force-sensing" label = _("Force Sensing Buttons") description = _("Change the force required to activate button.") feature = _F.FORCE_SENSING_BUTTON setup = "force_buttons" get = "get_current" set = "set_current" acceptable = "acceptable_current_key" choices_universe = list(range(0, 256)) kind = settings.Kind.MAP_RANGE @classmethod def build(cls, device): cls.check_properties(cls) device_object = getattr(device, cls.setup)() if device_object: setting = cls(device, device_object) if setting and len(device_object) == 1: ## If there is only one force button a simpler interface can be used setting.label = _("Force Sensing Button") setting.acceptable = "acceptable_current" setting.min_value = device_object[0].min_value setting.max_value = device_object[0].max_value setting.kind = settings.Kind.RANGE return setting class HapticLevel(settings.Setting): name = "haptic-level" label = _("Haptic Feedback Level") description = _("Change power of haptic feedback. (Zero to turn off.)") feature = _F.HAPTIC choices_universe = common.NamedInts(Off=0, Low=25, Medium=50, High=75, Maximum=100) min_value = 0 max_value = 100 class rw_class(settings.FeatureRW): def __init__(self, feature): super().__init__(feature, read_fnid=0x10, write_fnid=0x20) def read(self, device, data_bytes=b""): result = device.feature_request(self.feature, 0x10) if result[0] & 0x01 == 0: # disabled, return 0 return b"\x00" else: # enabled, return second byte return result[1:2] def write(self, device, data_bytes): if data_bytes == b"\x00": write_bytes = b"\x00\x32" # disable, at 50 percent else: write_bytes = b"\x01" + data_bytes reply = device.feature_request(self.feature, 0x20, write_bytes) return reply @classmethod def build(cls, device): response = device.feature_request(cls.feature, 0x10) if response: rw = cls.rw_class(cls.feature) levels = response[2] & 0x01 if levels: # device only has four levels validator = settings_validator.ChoicesValidator(choices=cls.choices_universe) else: # device has all levels validator = settings_validator.RangeValidator(min_value=cls.min_value, max_value=cls.max_value) return cls(device, rw, validator) # This setting is not displayed in the UI # Use `solaar config haptic-play
` to play a haptic form class PlayHapticWaveForm(settings.Setting): name = "haptic-play" label = _("Play Haptic Waveform") description = _("Tell device to play a haptic waveform.") feature = _F.HAPTIC choices_universe = hidpp20_constants.HapticWaveForms rw_options = {"read_fnid": None, "write_fnid": 0x40} # nothing to read persist = False # persisting this setting is useless display = False # don't display in UI, interact using `solaar config ...` class validator_class(settings_validator.ChoicesValidator): @classmethod def build(cls, setting_class, device): response = device.feature_request(_F.HAPTIC, 0x00) if response: waves = common.NamedInts() waveforms = int.from_bytes(response[4:8]) for waveform in hidpp20_constants.HapticWaveForms: if (1 << int(waveform)) & waveforms: waves[int(waveform)] = str(waveform) return cls(choices=waves, byte_count=1) SETTINGS: list[settings.Setting] = [ RegisterHandDetection, # simple RegisterSmoothScroll, # simple RegisterSideScroll, # simple RegisterDpi, RegisterFnSwap, # working HiResScroll, # simple LowresMode, # simple HiresSmoothInvert, # working HiresSmoothResolution, # working HiresMode, # simple ScrollRatchet, # simple ScrollRatchetTorque, 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 ForceSensing, CrownSmooth, # working DivertCrown, # working DivertGkeys, # working MKeyLEDs, # working MRKeyLED, # working Multiplatform, # working DualPlatform, # simple ChangeHost, # working Gesture2Gestures, # working Gesture2Divert, Gesture2Params, # working HapticLevel, PlayHapticWaveForm, Sidetone, Equalizer, ADCPower, ] class SettingsProtocol(Protocol): @property def name(self): ... @property def label(self): ... @property def description(self): ... @property def feature(self): ... @property def register(self): ... @property def kind(self): ... @property def min_version(self): ... @property def persist(self): ... @property def rw_options(self): ... @property def validator_class(self): ... @property def validator_options(self): ... @classmethod def build(cls, device): ... def val_to_string(self, value): ... @property def choices(self): ... @property def range(self): ... def _pre_read(self, cached, key=None): ... def read(self, cached=True): ... def _pre_write(self, save=True): ... def update(self, value, save=True): ... def write(self, value, save=True): ... def acceptable(self, args, current): ... def compare(self, args, current): ... def apply(self): ... def __str__(self): ... def check_feature(device, settings_class: SettingsProtocol) -> None | bool | SettingsProtocol: if settings_class.feature not in device.features: return if settings_class.min_version > device.features.get_feature_version(settings_class.feature): return if device.features.get_hidden(settings_class.feature): return try: detected = settings_class.build(device) if logger.isEnabledFor(logging.DEBUG): logger.debug("check_feature %s [%s] detected", settings_class.name, settings_class.feature) 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() ) raise e # differentiate from an error-free determination that the setting is not supported def check_feature_settings(device, already_known) -> bool: """Auto-detect device settings by the HID++ 2.0 features they have. Returns ------- bool True, if device was fully queried to find features, False otherwise. """ 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 [] new_absent = [] 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): try: setting = check_feature(device, sclass) except Exception as err: # on an internal HID++ error, assume offline and stop further checking if ( isinstance(err, exceptions.FeatureCallError) and err.error == hidpp20_constants.ErrorCode.LOGITECH_ERROR ): logger.warning(f"HID++ internal error checking feature {sclass.name}: make device not present") device.online = False device.present = False return False else: logger.warning(f"ignore feature {sclass.name} because of error {err}") if isinstance(setting, list): for s in setting: already_known.append(s) if sclass.name in new_absent: new_absent.remove(sclass.name) elif setting: already_known.append(setting) if sclass.name in new_absent: new_absent.remove(sclass.name) elif setting is None: if sclass.name not in new_absent and sclass.name not in absent and sclass.name not in device.persister: new_absent.append(sclass.name) if device.persister and new_absent: absent.extend(new_absent) device.persister["_absent"] = absent return True def check_feature_setting(device, setting_name: str) -> settings.Setting | None: for sclass in SETTINGS: if ( sclass.feature and device.features and (sclass.name == setting_name or sclass.name.endswith("_") and setting_name.startswith(sclass.name)) ): try: setting = check_feature(device, sclass) except Exception: return None if isinstance(setting, list): for s in setting: if s.name == setting_name: return s elif setting: return setting ================================================ FILE: lib/logitech_receiver/settings_validator.py ================================================ from __future__ import annotations import logging import math from enum import IntEnum from logitech_receiver import common from logitech_receiver.common import NamedInt from logitech_receiver.common import NamedInts logger = logging.getLogger(__name__) 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 Kind(IntEnum): TOGGLE = 0x01 CHOICE = 0x02 RANGE = 0x04 MAP_CHOICE = 0x0A MULTIPLE_TOGGLE = 0x10 PACKED_RANGE = 0x20 MULTIPLE_RANGE = 0x40 HETERO = 0x80 class Validator: @classmethod def build(cls, setting_class, device, **kwargs) -> Validator: return cls(**kwargs) @classmethod def to_string(cls, value) -> str: 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) -> str: 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) -> str: 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) -> str: 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, read_skip_byte_count=0, write_prefix_bytes=b""): assert max_value > min_value self.min_value = min_value self.max_value = max_value self.read_skip_byte_count = read_skip_byte_count self.write_prefix_bytes = write_prefix_bytes 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.read_skip_byte_count : self.read_skip_byte_count + 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 = self.write_prefix_bytes + 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 ================================================ FILE: lib/logitech_receiver/special_keys.py ================================================ ## 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") # Original set done as # tasks.py SWITCH_PRESENTATION_SWITCH_SCREEN = 0x0093 # on K400 Plus MINIMIZE_WINDOW = 0x0094 MAXIMIZE_WINDOW = 0x0095 # on K400 Plus MULTI_PLATFORM_APP_SWITCH = 0x0096 MULTI_PLATFORM_HOME = 0x0097 MULTI_PLATFORM_MENU = 0x0098 MULTI_PLATFORM_BACK = 0x0099 SWITCH_LANGUAGE = 0x009A # Mac_switch_language SCREEN_CAPTURE = 0x009B # Mac_screen_Capture, on Craft Keyboard GESTURE_BUTTON = 0x009C SMART_SHIFT = 0x009D APP_EXPOSE = 0x009E SMART_ZOOM = 0x009F LOOKUP = 0x00A0 MICROPHEON_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 APP_SWITCH = 0x00A8 GESTURE_BUTTON_NAVIGATION = 0x00A9 # Mouse_Thumb_Button on MX Master FN_INVERSION = 0x00AA MULTI_PLATFORM_BACK_2 = 0x00AB # Alternative MULTI_PLATFORM_FORWARD = 0x00AC MULTI_PLATFORM_Gesture_Button = 0x00AD HostSwitch_Channel_1 = 0x00AE HostSwitch_Channel_2 = 0x00AF HostSwitch_Channel_3 = 0x00B0 MULTI_PLATFORM_SEARCH = 0x00B1 MULTI_PLATFORM_HOME_MISSION_CONTROL = 0x00B2 MULTI_PLATFORM_MENU_LAUNCHPAD = 0x00B3 VIRTUAL_GESTURE_BUTTON = 0x00B4 CURSOR = 0x00B5 KEYBOARD_RIGHT_ARROW = 0x00B6 SW_CUSTOM_HIGHLIGHT = 0x00B7 KEYBOARD_LEFT_ARROW = 0x00B8 TBD = 0x00B9 MULTI_PLATFORM_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_CONTEXT_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_2 = 0x00D5 # Alternative FN_INVERSION_2 = 0x00D6 # Alternative LEFT_AND_RIGHT_CLICK = 0x00D7 VOICE_DICTATION = 0x00D8 EMOJI_SMILING_FACE_WITH_HEART_SHAPED_EYES = 0x00D9 EMOJI_LOUDLY_CRYING_FACE = 0x00DA EMOJI_SMILEY = 0x00DB EMOJI_SMILE_WITH_TEARS = 0x00DC OPEN_EMOJI_PANEL = 0x00DD MULTI_PLATFORM_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_2 = 0x00EF # Alternative PAGE_DOWN_2 = 0x00F0 # Alternative MUTE_MICROPHONE = 0x00F1 DO_NOT_DISTURB = 0x00F2 BACKSLASH = 0x00F3 REFRESH = 0x00F4 CLOSE_TAB = 0x00F5 LANG_SWITCH = 0x00F6 STANDARD_ALPHABETICAL_KEY = 0x00F7 RRIGH_OPTION_START_2 = 0x00F8 LEFT_OPTION = 0x00F9 RIGHT_OPTION = 0x00FA LEFT_CMD = 0x00FB RIGHT_CMD = 0x00FC def __str__(self): return self.name.replace("_", " ").title() 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}" class HorizontalScroll(IntEnum): Left = 0x4000 Right = 0x8000 # 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 HorizontalScroll: 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) ================================================ FILE: lib/solaar/__init__.py ================================================ ## 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() ================================================ FILE: lib/solaar/cli/__init__.py ================================================ ## 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", description="Show information about device or all 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", description="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", description="Print or load YAML dump of profiles.", epilog="Only works on active devices.", ) sp.add_argument( "device", help="device to read or load profiles; may be a device number (1..6), a serial number, " "or a substring of a device's name", ) sp.add_argument("profiles", nargs="?", help="file containing YAML dump of profiles to load") sp.set_defaults(action="profiles") sp = subparsers.add_parser( "config", description="Print or load device-specific settings. Only some settings can be loaded. " "Loading complex settings uses the same syntax as in ~/.config/solaar/config.yaml", 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", description="Pair a new device with a receiver. The device has to be compatible with the receiver.", 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", description="Unpair a device from its receiver. Not all receivers allow unpairing.") 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) 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) 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()}") ================================================ FILE: lib/solaar/cli/config.py ================================================ ## 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 logitech_receiver.settings_templates import SettingsProtocol 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]) args = yaml.dump(argl) application.run(args) else: if dev.persister and setting.persist: dev.persister[setting.name] = setting._value def set(dev, setting: SettingsProtocol, 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 elif setting.kind == settings.Kind.MAP_RANGE: if args.extra_subkey is None: _print_setting_keyed(setting, args.value_key) return None, None, None key = int(args.value_key) value = int(args.extra_subkey) if key not in setting._device_object: raise Exception(f"{setting.name}: key '{key}' not in setting") message = f"Setting {setting.name} of {dev.name} key {key} to {value}" result = setting.write_key_value(key, value, save=save) elif setting.kind == settings.Kind.HETERO: value = yaml.safe_load(args.value_key) args.value_key = value message = f"Setting {setting.name} of {dev.name} to {value}" result = setting.write(value, save=save) else: print(f"Setting {setting.name}, with kind {setting.kind.name}, not implemented") raise Exception("NotImplemented") return result, message, value ================================================ FILE: lib/solaar/cli/pair.py ================================================ ## 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) if not (old_notification_flags & hidpp10_constants.NotificationFlag.WIRELESS): _hidpp10.set_notification_flags(receiver, old_notification_flags | hidpp10_constants.NotificationFlag.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 if authentication is None: # no compatible device stepped forward print("No Bolt-compatible device requested pairing.") else: 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.NotificationFlag.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 ================================================ FILE: lib/solaar/cli/probe.py ================================================ ## 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, f"0x{strhex(rgst)}" if rgst else "None")) rgst = receiver.read_register(Registers.RECEIVER_CONNECTION) print( " Connection State %#04x: %s" % (Registers.RECEIVER_CONNECTION % 0x100, f"0x{strhex(rgst)}" if rgst else "None") ) rgst = receiver.read_register(Registers.DEVICES_ACTIVITY) print( " Device Activity %#04x: %s" % (Registers.DEVICES_ACTIVITY % 0x100, f"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, f"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, f"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, f"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 ================================================ FILE: lib/solaar/cli/profiles.py ================================================ ## 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: print(f"Device {dev.name} is offline.") elif not dev.profiles: print(f"Device {dev.name} has no onboard profiles that Solaar supports.") 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()) ================================================ FILE: lib/solaar/cli/show.py ================================================ ## 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.NotificationFlag.flag_names(notification_flags) print(f" Notifications: {', '.join(notification_names)} (0x{notification_flags.value: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 = f"{text} {voltage}mV " nextText = "" if nextLevel is None else f", 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.NotificationFlag.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.DeviceFeature.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, byteorder="little") feature_int = int.from_bytes(feature_bytes, byteorder="little") try: flags = dev.request(0x0000, feature_bytes) except Exception: print(" %2d: %-22s {%04X} - can't retrieve" % (index, feature, feature_int)) continue 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.read(False) v = setting.val_to_string(v) 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" flag_names = list(common.flag_names(hidpp20.KeyFlag, k.flags.value)) print( f" {', '.join(flag_names)}, pos:{int(k.pos)}, group:{int(k.group):1}, group mask:{gmask_fmt}" ) report_fmt = list(common.flag_names(hidpp20.MappingFlag, k.mapping_flags.value)) 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) ================================================ FILE: lib/solaar/cli/unpair.py ================================================ ## 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( f"Receiver with USB id {dev.receiver.product_id} for {dev.name} [{dev.wpid}:{dev.serial}] does not unpair,", "but attempting anyway.", ) 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 ================================================ FILE: lib/solaar/configuration.py ================================================ ## 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 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: 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) 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)) or ( c.get(_KEY_WPID) is None and c.get(_KEY_SERIAL) is None and c.get(_KEY_UNIT_ID) is None and modelId and modelId == c.get(_KEY_MODEL_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._name else None unitId = device.unitId if device.unitId != "00000000" else device._serial if device._serial 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 logger.info("not setting up persister for offline device %s", device._name) return 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 ================================================ FILE: lib/solaar/custom_logger.py ================================================ import logging class CustomLogger(logging.Logger): """Logger, that avoids unnecessary string computations. Does not compute messages for disabled log levels. """ def debug(self, msg, *args, **kwargs): if self.isEnabledFor(logging.DEBUG): super().debug(msg, *args, **kwargs) def info(self, msg, *args, **kwargs): if self.isEnabledFor(logging.INFO): super().info(msg, *args, **kwargs) def warning(self, msg, *args, **kwargs): if self.isEnabledFor(logging.WARNING): super().warning(msg, *args, **kwargs) def error(self, msg, *args, **kwargs): if self.isEnabledFor(logging.ERROR): super().error(msg, *args, **kwargs) def critical(self, msg, *args, **kwargs): if self.isEnabledFor(logging.CRITICAL): super().critical(msg, *args, **kwargs) ================================================ FILE: lib/solaar/dbus.py ================================================ ## 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, ) 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 ) ================================================ FILE: lib/solaar/gtk.py ================================================ #!/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 from solaar.custom_logger import CustomLogger logging.setLoggerClass(CustomLogger) 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 create_parser(): arg_parser = argparse.ArgumentParser( prog=NAME.lower(), description="Solaar is a program to manage many Logitech devices, " "changing how they operate and maintaining the changes whenever the device connects.", 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="device or receiver path to use if needed. 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", ) return arg_parser def _parse_arguments(): arg_parser = create_parser() 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: 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: # explicit close before return temp.close() return if args.action: # if any argument, run comandline and exit result = cli.run(args.action, args.hidraw_path) # explicit close before return temp.close() return result 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 ( platform.system() == "Linux" and 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() ================================================ FILE: lib/solaar/i18n.py ================================================ ## 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 logging import os import sys from glob import glob from solaar import NAME _LOCALE_DOMAIN = NAME.lower() logger = logging.getLogger(__name__) 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", f"{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() -> None: """Sets locale for translations to the system default. If locale is unsupported, fallback to standard English without translation 'C'. 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, "") # system default except locale.Error: logger.error("User locale not supported by system, using no translation.") locale.setlocale(locale.LC_ALL, "C") # untranslated (English) return 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 ================================================ FILE: lib/solaar/listener.py ================================================ ## 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 subprocess import time import typing from collections import namedtuple from functools import partial from typing import Callable 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 from .ui import common if typing.TYPE_CHECKING: from hidapi.common import DeviceInfo gi.require_version("Gtk", "3.0") # NOQA: E402 from gi.repository import GLib # NOQA: E402 # isort:skip if typing.TYPE_CHECKING: from logitech_receiver.device import Device logger = logging.getLogger(__name__) ACTION_ADD = "add" _GHOST_DEVICE = namedtuple("_GHOST_DEVICE", ("receiver", "number", "name", "kind", "online", "path")) _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, path=None ) 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): logger.info("%s: notifications listener has started (%s)", self.receiver, self.receiver.handle) nfs = self.receiver.enable_connection_notifications() if not self.receiver.isDevice and (not nfs or not (nfs & hidpp10_constants.NotificationFlag.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 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. 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: 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: 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 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.InfoSubRegisters.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: 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 logger.warning("first notification was not a connection notification") 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: Device, path, dictionary: dict, 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") 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: logger.info("bluez cleanup for %s", device) _cleanup_bluez_dbus(device) def _cleanup_bluez_dbus(device: Device): """Remove dbus signal receiver for device""" 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: DeviceInfo): assert _status_callback and _setting_callback if not device_info.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... 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: 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): 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 count -= 1 try: # sometimes the device is not set up already, it should come back later if dev.ping(): dev.changed(active=True, push=True) listener_thread._status_changed(dev) except exceptions.NoSuchDevice: logger.debug("can't ping device on resume: %s", dev) 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: Callable, setting_changed_callback: Callable, error_callback: Callable): 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: DeviceInfo, retry): try: _start(device_info) except OSError as e: if e.errno == errno.EACCES: try: output = subprocess.check_output(["getfacl", "-p", device_info.path], text=True) 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(common.ErrorReason.PERMISSIONS, device_info.path) else: _error_callback(common.ErrorReason.NO_DEVICE, device_info.path) except exceptions.NoReceiver: _error_callback(common.ErrorReason.NO_DEVICE, 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 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 ================================================ FILE: lib/solaar/tasks.py ================================================ #!/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 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) logger.debug("stopped") ================================================ FILE: lib/solaar/ui/__init__.py ================================================ ## 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 enum import Enum 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" class GtkSignal(Enum): ACTIVATE = "activate" COMMAND_LINE = "command-line" SHUTDOWN = "shutdown" def _startup(app, startup_hook, use_tray, show_window): 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): 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 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): 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(GtkSignal.COMMAND_LINE.value, _command_line) application.connect(GtkSignal.ACTIVATE.value, _activate) application.connect(GtkSignal.SHUTDOWN.value, _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): if device is None: logger.debug("status changed on nil device: %s (%s) %s", device, alert, reason) return 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) ================================================ FILE: lib/solaar/ui/about/__init__.py ================================================ ================================================ FILE: lib/solaar/ui/about/about.py ================================================ ## 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() ================================================ FILE: lib/solaar/ui/about/model.py ================================================ ## 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)", "Oleksandr Afanasiev (Ukrainian)", ] 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" ================================================ FILE: lib/solaar/ui/about/presenter.py ================================================ ## 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() ================================================ FILE: lib/solaar/ui/about/view.py ================================================ ## 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 enum import Enum from typing import List from typing import Tuple from typing import Union from gi.repository import Gtk from solaar import NAME class GtkSignal(Enum): RESPONSE = "response" 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_logo_icon_name(NAME.lower()) self.view.set_license_type(Gtk.License.GPL_2_0) self.view.connect(GtkSignal.RESPONSE.value, 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() ================================================ FILE: lib/solaar/ui/action.py ================================================ ## 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 Enum from gi.repository import Gdk from gi.repository import Gtk from solaar.i18n import _ from solaar.ui import common from . import pair_window class GtkSignal(Enum): ACTIVATE = "activate" 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(GtkSignal.ACTIVATE.value, 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(GtkSignal.ACTIVATE.value, 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(GtkSignal.ACTIVATE.value, 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: common.error_dialog(common.ErrorReason.UNPAIR, device) ================================================ FILE: lib/solaar/ui/common.py ================================================ ## 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 enum import Enum 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__) class ErrorReason(Enum): PERMISSIONS = "Permissions" NO_DEVICE = "No device" UNPAIR = "Unpair" def _create_error_text(reason: ErrorReason, object_) -> Tuple[str, str]: if reason == ErrorReason.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 == ErrorReason.NO_DEVICE: 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 == ErrorReason.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.name, object_) return title, text def _error_dialog(reason: ErrorReason, 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: ErrorReason, object_): 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): """Runs a function asynchronously.""" if _task_runner: _task_runner(function, *args, **kwargs) ================================================ FILE: lib/solaar/ui/config_panel.py ================================================ ## 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 enum import Enum 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__) class GtkSignal(Enum): ACTIVATE = "activate" CHANGED = "changed" CLICKED = "clicked" MATCH_SELECTED = "match_selected" NOTIFY_ACTIVE = "notify::active" TOGGLED = "toggled" VALUE_CHANGED = "value-changed" COLOR_SET = "color-set" 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 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: 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(GtkSignal.NOTIFY_ACTIVE.value, 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(GtkSignal.VALUE_CHANGED.value, self.changed) def set_value(self, value): if isinstance(value, dict): value = next(iter(value.values())) return super().set_value(value) 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(GtkSignal.CHANGED.value, 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(GtkSignal.CHANGED.value, self.changed) self.connect(GtkSignal.ACTIVATE.value, self.activate) completion.connect(GtkSignal.MATCH_SELECTED.value, 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 set_choices(self, choices): self.choices = choices 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(GtkSignal.CHANGED.value, 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.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(GtkSignal.CLICKED.value, 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(GtkSignal.NOTIFY_ACTIVE.value, 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(f"{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(GtkSignal.VALUE_CHANGED.value, 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 += f"{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(f"{str(sub_item)}={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(GtkSignal.VALUE_CHANGED.value, 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 += f"{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(GtkSignal.CHANGED.value, self.changed) self.pack_start(item_box, False, False, 0) elif item["kind"] == settings.Kind.COLOR: item_box = Gtk.ColorButton() item_box.connect(GtkSignal.COLOR_SET.value, 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(GtkSignal.VALUE_CHANGED.value, 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(): if isinstance(box, Gtk.ColorButton): rgba = box.get_rgba() r = int(rgba.red * 255) g = int(rgba.green * 255) b = int(rgba.blue * 255) result[str(k)] = (r << 16) | (g << 8) | b else: 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] if isinstance(box, Gtk.ColorButton): rgba = Gdk.RGBA() color_string = f"#{v:06X}" # e.g. "#FF0000" rgba.parse(color_string) box.set_rgba(rgba) else: 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): if not s.display: return 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(GtkSignal.CLICKED.value, _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: 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) try: # a call was producing a TypeError so guard against that sbox._control.set_value(value) except TypeError as e: logger.warning("%s: error setting control value (%s): %s", sbox.setting.name, sbox.setting._device, repr(e)) 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): 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: 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) ================================================ FILE: lib/solaar/ui/desktop_notifications.py ================================================ ## 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(): 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(): 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: return 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 ================================================ FILE: lib/solaar/ui/diversion_rules.py ================================================ ## 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 from logitech_receiver.common import NamedInt from logitech_receiver.common import NamedInts from logitech_receiver.common import UnsortedNamedInts from logitech_receiver.settings import Kind 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 GtkSignal(Enum): ACTIVATE = "activate" BUTTON_RELEASE_EVENT = "button-release-event" CHANGED = "changed" CLICKED = "clicked" DELETE_EVENT = "delete-event" KEY_PRESS_EVENT = "key-press-event" NOTIFY_ACTIVE = "notify::active" TOGGLED = "toggled" VALUE_CHANGED = "value_changed" def create_all_settings(all_settings: list[Setting]) -> dict[str, list[Setting]]: settings = {} for s in sorted(all_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 ALL_SETTINGS = create_all_settings(SETTINGS) 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, diversion.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, diversion.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, diversion.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, (diversion.Rule, diversion.And, diversion.Or, diversion.Later)): for c in rule_component.components: ed = editable or (isinstance(c, diversion.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, diversion.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, diversion.Not) and c is not None and wrapped.level >= 1 can_insert = wrapped.editable and not isinstance(parent_c, diversion.Not) and wrapped.level >= 2 can_insert_only_rule = wrapped.editable and wrapped.level == 1 can_flatten = ( wrapped.editable and not isinstance(parent_c, diversion.Not) and isinstance(c, (diversion.Rule, diversion.And, diversion.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, diversion.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, diversion.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, diversion.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(GtkSignal.ACTIVATE.value, 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, diversion.Rule) and wrapped.level == 1: new_c.source = diversion._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, (diversion.Rule, diversion.And, diversion.Or, diversion.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"), diversion.Rule, []), (_("Or"), diversion.Or, []), (_("And"), diversion.And, []), [ _("Condition"), [ (_("Feature"), diversion.Feature, rule_conditions.FeatureUI.FEATURES_WITH_DIVERSION[0]), (_("Report"), diversion.Report, 0), (_("Process"), diversion.Process, ""), (_("Mouse process"), diversion.MouseProcess, ""), (_("Modifiers"), diversion.Modifiers, []), (_("Key"), diversion.Key, ""), (_("KeyIsDown"), diversion.KeyIsDown, ""), (_("Active"), diversion.Active, ""), (_("Device"), diversion.Device, ""), (_("Host"), diversion.Host, ""), (_("Setting"), diversion.Setting, [None, "", None]), (_("Test"), diversion.Test, next(iter(diversion.TESTS))), (_("Test bytes"), diversion.TestBytes, [0, 1, 0]), (_("Mouse Gesture"), diversion.MouseGesture, ""), ], ], [ _("Action"), [ (_("Key press"), diversion.KeyPress, "space"), (_("Mouse scroll"), diversion.MouseScroll, [0, 0]), (_("Mouse click"), diversion.MouseClick, ["left", 1]), (_("Set"), diversion.Set, [None, "", None]), (_("Execute"), diversion.Execute, [""]), (_("Later"), diversion.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(GtkSignal.ACTIVATE.value, 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(GtkSignal.ACTIVATE.value, self._menu_do_insert_new, m, it, diversion.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(GtkSignal.ACTIVATE.value, 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, diversion.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, diversion.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, diversion.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(GtkSignal.ACTIVATE.value, 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, diversion.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(GtkSignal.ACTIVATE.value, self.menu_do_wrap, m, it, diversion.Rule) menu_and.connect(GtkSignal.ACTIVATE.value, self.menu_do_wrap, m, it, diversion.And) menu_or.connect(GtkSignal.ACTIVATE.value, self.menu_do_wrap, m, it, diversion.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 = diversion.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(GtkSignal.ACTIVATE.value, 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 = diversion.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(GtkSignal.ACTIVATE.value, 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(GtkSignal.ACTIVATE.value, 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(GtkSignal.DELETE_EVENT.value, 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(GtkSignal.DELETE_EVENT.value, 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) diversion.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 diversion._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(GtkSignal.KEY_PRESS_EVENT.value, self._event_key_pressed) view.connect(GtkSignal.BUTTON_RELEASE_EVENT.value, self._event_button_released) view.get_selection().connect(GtkSignal.CHANGED.value, 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(GtkSignal.CLICKED.value, lambda *_args: self._save_yaml_file()) self.discard_btn.connect(GtkSignal.CLICKED.value, 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(diversion.rules.components) == 1: # only built-in rules - add empty user rule list diversion.rules.components.insert(0, diversion.Rule([], source=diversion._file_path)) _populate_model(model, None, diversion.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, diversion.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, diversion.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, diversion.And) elif e.keyval == Gdk.KEY_bar: self._action_menu.menu_do_wrap(None, m, it, diversion.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, diversion.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._completion = 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(GtkSignal.CHANGED.value, 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._completion: 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.") if self.component else "") self.widgets[self.label] = (0, 0, 1, 1) def collect_value(self): return self.component.components[:] # not editable on the bottom panel @classmethod def right_label(cls, component): return str(component) class RuleUI(RuleComponentUI): CLASS = diversion.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 = diversion.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 = diversion.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 = diversion.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(GtkSignal.VALUE_CHANGED.value, 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 = diversion.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 = diversion.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(GtkSignal.CHANGED.value, self._changed) self.range_widget = Gtk.SpinButton.new_with_range(0, 0xFFFF, 1) self.range_widget.connect(GtkSignal.VALUE_CHANGED.value, self._changed) self.choice_widget = SmartComboBox( [], completion=True, has_entry=True, case_insensitive=True, replace_with_default_name=True ) self.choice_widget.connect(GtkSignal.CHANGED.value, self._changed) self.sub_key_widget = SmartComboBox([]) self.sub_key_widget.connect(GtkSignal.CHANGED.value, 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() 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(GtkSignal.CHANGED.value, 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 = diversion.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 = diversion.Device label_text = _("Device that originated the current notification.") @classmethod def left_label(cls, component): return _("Device") class HostUI(ConditionUI): CLASS = diversion.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(GtkSignal.CHANGED.value, 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: MULTIPLE = [Kind.MULTIPLE_TOGGLE, Kind.MAP_CHOICE, Kind.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(GtkSignal.CHANGED.value, self._changed_device) self.device_field.connect(GtkSignal.CHANGED.value, 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 ALL_SETTINGS.values()]) self.setting_field.set_valign(Gtk.Align.CENTER) self.setting_field.connect(GtkSignal.CHANGED.value, self._changed_setting) self.setting_field.connect(GtkSignal.CHANGED.value, 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(GtkSignal.CHANGED.value, self._changed_key) self.key_field.connect(GtkSignal.CHANGED.value, 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 = 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 = 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 == Kind.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 == Kind.MULTIPLE_TOGGLE: supported_keys = val.get_options() or None elif device_setting.kind == Kind.MAP_CHOICE: choices = val.choices or None supported_keys = choices.keys() if choices else None elif device_setting.kind == Kind.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 (Kind.TOGGLE, Kind.MULTIPLE_TOGGLE): self.value_field.make_toggle() elif kind in (Kind.CHOICE, Kind.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 == Kind.CHOICE: supported_values = choices elif kind == Kind.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 == Kind.RANGE: self.value_field.make_range(val_class.min_value, val_class.max_value) elif kind == Kind.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 (Kind.CHOICE, Kind.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 (Kind.CHOICE, Kind.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 == Kind.CHOICE: supported_values = choices elif kind == Kind.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 == Kind.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 (Kind.TOGGLE, Kind.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 = diversion.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 = diversion.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: dict[Any, RuleComponentUI] = { diversion.Rule: RuleUI, diversion.Not: NotUI, diversion.Or: OrUI, diversion.And: AndUI, diversion.Later: LaterUI, diversion.Process: rule_conditions.ProcessUI, diversion.MouseProcess: rule_conditions.MouseProcessUI, diversion.Active: ActiveUI, diversion.Device: DeviceUI, diversion.Host: HostUI, diversion.Feature: rule_conditions.FeatureUI, diversion.Report: rule_conditions.ReportUI, diversion.Modifiers: rule_conditions.ModifiersUI, diversion.Key: rule_conditions.KeyUI, diversion.KeyIsDown: rule_conditions.KeyIsDownUI, diversion.Test: rule_conditions.TestUI, diversion.TestBytes: rule_conditions.TestBytesUI, diversion.Setting: SettingUI, diversion.MouseGesture: rule_conditions.MouseGestureUI, diversion.KeyPress: rule_actions.KeyPressUI, diversion.MouseScroll: rule_actions.MouseScrollUI, diversion.MouseClick: rule_actions.MouseClickUI, diversion.Execute: rule_actions.ExecuteUI, diversion.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() ================================================ FILE: lib/solaar/ui/icons.py ================================================ ## 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() 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 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 += (f"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) ================================================ FILE: lib/solaar/ui/pair_window.py ================================================ ## 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 enum import Enum 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 class GtkSignal(Enum): CANCEL = "cancel" CLOSE = "close" 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(): 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 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 logger.debug("%s success: %s", receiver, device) _create_success_page(assistant, device) def _finish(assistant, receiver): 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): 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(GtkSignal.CANCEL.value, finish, receiver) assistant.connect(GtkSignal.CLOSE.value, 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 ================================================ FILE: lib/solaar/ui/rule_actions.py ================================================ ## 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 enum import Enum 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 GtkSignal(Enum): CHANGED = "changed" CLICKED = "clicked" TOGGLED = "toggled" 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, justify=Gtk.Justification.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(GtkSignal.CLICKED.value, 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(GtkSignal.TOGGLED.value, 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(GtkSignal.TOGGLED.value, 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(GtkSignal.TOGGLED.value, 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(GtkSignal.CHANGED.value, self._on_update) field_entry.set_size_request(250, -1) 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(GtkSignal.CLICKED.value, 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.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) + (f" ({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, justify=Gtk.Justification.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(GtkSignal.CHANGED.value, self._on_update) self.field_y.connect(GtkSignal.CHANGED.value, 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, justify=Gtk.Justification.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=_("Action (and Count, if click)"), 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(GtkSignal.CHANGED.value, self._on_update) self.field_c.connect(GtkSignal.CHANGED.value, self._on_update) self.field_d.connect(GtkSignal.CHANGED.value, 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] = (4, 1, 1, 1) self.widgets[self.field_d] = (3, 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, justify=Gtk.Justification.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(GtkSignal.CLICKED.value, 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(GtkSignal.CHANGED.value, 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(GtkSignal.CLICKED.value, 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]) ================================================ FILE: lib/solaar/ui/rule_base.py ================================================ ## 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. import abc from contextlib import contextmanager as contextlib_contextmanager from typing import Any 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(abc.ABC): 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() @abc.abstractmethod def create_widgets(self) -> dict: pass def show(self, component, editable=True): self._show_widgets(editable) self.component = component @abc.abstractmethod def collect_value(self) -> Any: pass @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): # noqa: B027 pass ================================================ FILE: lib/solaar/ui/rule_conditions.py ================================================ ## 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 enum import Enum 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 GtkSignal(Enum): CHANGED = "changed" CLICKED = "clicked" NOTIFY_ACTIVE = "notify::active" TOGGLED = "toggled" VALUE_CHANGED = "value-changed" 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, justify=Gtk.Justification.CENTER) 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(GtkSignal.CHANGED.value, 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, justify=Gtk.Justification.CENTER) 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(GtkSignal.CHANGED.value, 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, justify=Gtk.Justification.CENTER) 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(GtkSignal.CHANGED.value, 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, justify=Gtk.Justification.CENTER) 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(GtkSignal.CHANGED.value, 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, justify=Gtk.Justification.CENTER) 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(GtkSignal.NOTIFY_ACTIVE.value, 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, justify=Gtk.Justification.CENTER) 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(GtkSignal.CHANGED.value, 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(GtkSignal.TOGGLED.value, 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(GtkSignal.TOGGLED.value, 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, justify=Gtk.Justification.CENTER) 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(GtkSignal.CHANGED.value, 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, justify=Gtk.Justification.CENTER) 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(GtkSignal.CHANGED.value, 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(GtkSignal.CHANGED.value, 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 + (f" {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, justify=Gtk.Justification.CENTER) 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(GtkSignal.VALUE_CHANGED.value, 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(GtkSignal.CHANGED.value, 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, justify=Gtk.Justification.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(GtkSignal.CLICKED.value, 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(GtkSignal.CHANGED.value, 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(GtkSignal.CLICKED.value, 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.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) ================================================ FILE: lib/solaar/ui/tray.py ================================================ ## 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 enum import Enum 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 class GtkSignal(Enum): ACTIVATE = "activate" SCROLL_EVENT = "scroll-event" 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 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 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(GtkSignal.SCROLL_EVENT.value, _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: 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(GtkSignal.ACTIVATE.value, window.toggle) icon.connect(GtkSignal.SCROLL_EVENT.value, _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 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) ================================================ FILE: lib/solaar/ui/window.py ================================================ ## 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 enum import Enum from enum import IntEnum 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 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 = "" class Column(IntEnum): """Columns of tree model.""" 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) class GtkSignal(Enum): CHANGED = "changed" CLICKED = "clicked" DELETE_EVENT = "delete-event" 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(GtkSignal.CLICKED.value, 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(GtkSignal.CHANGED.value, _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(GtkSignal.DELETE_EVENT.value, 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) 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 device_number is not None if receiver_path is None: return 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) 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 = hidpp10_constants.flags_to_str(flag_bits, fallback=f"({_('none')})") yield _("Notifications"), 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 f"{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, f"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 ================================================ FILE: lib/solaar/version ================================================ 1.1.19 ================================================ FILE: mkdocs.yml ================================================ site_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 theme: name: readthedocs docs_dir: docs nav: - Solaar: index.md - Usage: usage.md - Capabilities: capabilities.md - Issues: issues.md - Rules: rules.md - Installation: installation.md - Uninstallation: uninstallation.md - Translation: i18n.md - Features: features.md - Devices: devices.md - Implementation: implementation.md - Debian: debian.md plugins: - search # - mkdocstrings: # handlers: # python: # setup_commands: # - python -m pip install . - mermaid2 markdown_extensions: - pymdownx.superfences ================================================ FILE: po/README ================================================ See docs/i18n.md for instructions about creating or updating a translation. ================================================ FILE: po/ca.po ================================================ # 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" ================================================ FILE: po/cs.po ================================================ # 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" ================================================ FILE: po/da.po ================================================ # 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" ================================================ FILE: po/de.po ================================================ # 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" ================================================ FILE: po/el.po ================================================ # 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 "πλάτος" ================================================ FILE: po/es.po ================================================ # 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" ================================================ FILE: po/fi.po ================================================ # 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: 2026-03-05 13:08+0200\n" "PO-Revision-Date: 2013-08-05 18:49+0300\n" "Last-Translator: Niko Savola\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:52 msgid "Bolt Receiver" msgstr "Bolt-vastaanotin" #: lib/logitech_receiver/base_usb.py:64 msgid "Unifying Receiver" msgstr "Unifying-vastaanotin" #: lib/logitech_receiver/base_usb.py:75 lib/logitech_receiver/base_usb.py:87 #: lib/logitech_receiver/base_usb.py:100 lib/logitech_receiver/base_usb.py:113 msgid "Nano Receiver" msgstr "Nano-vastaanotin" #: lib/logitech_receiver/base_usb.py:125 msgid "Lightspeed Receiver" msgstr "Lightspeed-vastaanotin" #: lib/logitech_receiver/base_usb.py:136 msgid "EX100 Receiver 27 Mhz" msgstr "EX100 27 MHz -vastaanotin" #: lib/logitech_receiver/common.py:649 #, python-format msgid "Battery: %(level)s (%(status)s)" msgstr "Akku: %(level)s (%(status)s)" #: lib/logitech_receiver/common.py:652 #, python-format msgid "Battery: %(percent)d%% (%(status)s)" msgstr "Akku: %(percent)d%% (%(status)s)" #: lib/logitech_receiver/hidpp20.py:1048 #: lib/logitech_receiver/settings_templates.py:299 msgid "Disabled" msgstr "Pois käytöstä" #: lib/logitech_receiver/hidpp20.py:1049 msgid "Static" msgstr "Muuttumaton" #: lib/logitech_receiver/hidpp20.py:1050 msgid "Pulse" msgstr "Pulssi" #: lib/logitech_receiver/hidpp20.py:1051 msgid "Cycle" msgstr "Jakso" #: lib/logitech_receiver/hidpp20.py:1052 msgid "Boot" msgstr "Käynnistys" #: lib/logitech_receiver/hidpp20.py:1053 msgid "Demo" msgstr "Esittely" #: lib/logitech_receiver/hidpp20.py:1055 msgid "Breathe" msgstr "Hengitys" #: lib/logitech_receiver/hidpp20.py:1058 msgid "Ripple" msgstr "Väreily" #: lib/logitech_receiver/hidpp20.py:1059 msgid "Decomposition" msgstr "Hajoaminen" #: lib/logitech_receiver/hidpp20.py:1060 msgid "Signature1" msgstr "Allekirjoitus1" #: lib/logitech_receiver/hidpp20.py:1061 msgid "Signature2" msgstr "Allekirjoitus2" #: lib/logitech_receiver/hidpp20.py:1062 msgid "CycleS" msgstr "SykliS" #: lib/logitech_receiver/hidpp20.py:1126 msgid "Unknown Location" msgstr "Tuntematon sijainti" #: lib/logitech_receiver/hidpp20.py:1127 msgid "Primary" msgstr "Ensisijainen" #: lib/logitech_receiver/hidpp20.py:1128 msgid "Logo" msgstr "Logo" #: lib/logitech_receiver/hidpp20.py:1129 msgid "Left Side" msgstr "Vasen puoli" #: lib/logitech_receiver/hidpp20.py:1130 msgid "Right Side" msgstr "Oikea puoli" #: lib/logitech_receiver/hidpp20.py:1131 msgid "Combined" msgstr "Yhdistetty" #: lib/logitech_receiver/hidpp20.py:1132 msgid "Primary 1" msgstr "Ensisijainen 1" #: lib/logitech_receiver/hidpp20.py:1133 msgid "Primary 2" msgstr "Ensisijainen 2" #: lib/logitech_receiver/hidpp20.py:1134 msgid "Primary 3" msgstr "Ensisijainen 3" #: lib/logitech_receiver/hidpp20.py:1135 msgid "Primary 4" msgstr "Ensisijainen 4" #: lib/logitech_receiver/hidpp20.py:1136 msgid "Primary 5" msgstr "Ensisijainen 5" #: lib/logitech_receiver/hidpp20.py:1137 msgid "Primary 6" msgstr "Ensisijainen 6" #: lib/logitech_receiver/i18n.py:28 msgid "empty" msgstr "tyhjä" #: lib/logitech_receiver/i18n.py:29 msgid "critical" msgstr "kriittinen" #: lib/logitech_receiver/i18n.py:30 msgid "low" msgstr "matala" #: lib/logitech_receiver/i18n.py:31 msgid "average" msgstr "keskiarvo" #: lib/logitech_receiver/i18n.py:32 msgid "good" msgstr "hyvä" #: lib/logitech_receiver/i18n.py:33 msgid "full" msgstr "täysi" #: lib/logitech_receiver/i18n.py:35 msgid "discharging" msgstr "tyhjenee" #: lib/logitech_receiver/i18n.py:36 msgid "recharging" msgstr "latautuu" #: lib/logitech_receiver/i18n.py:37 lib/solaar/ui/window.py:700 msgid "charging" msgstr "lataa" #: lib/logitech_receiver/i18n.py:38 msgid "not charging" msgstr "ei latauksessa" #: lib/logitech_receiver/i18n.py:39 msgid "almost full" msgstr "lähes täysi" #: lib/logitech_receiver/i18n.py:40 msgid "charged" msgstr "ladattu" #: lib/logitech_receiver/i18n.py:41 msgid "slow recharge" msgstr "hidas lataus" #: lib/logitech_receiver/i18n.py:42 #, fuzzy msgid "invalid battery" msgstr "virheellinen akku" #: lib/logitech_receiver/i18n.py:43 msgid "thermal error" msgstr "lämpötilasta johtuva virhe" #: lib/logitech_receiver/i18n.py:44 msgid "error" msgstr "virhe" #: lib/logitech_receiver/i18n.py:45 msgid "standard" msgstr "vakio" #: lib/logitech_receiver/i18n.py:46 msgid "fast" msgstr "nopea" #: lib/logitech_receiver/i18n.py:47 msgid "slow" msgstr "hidas" #: lib/logitech_receiver/i18n.py:49 msgid "device timeout" msgstr "laitteen aikakatkaisu" #: lib/logitech_receiver/i18n.py:50 msgid "device not supported" msgstr "laite ei ole tuettu" #: lib/logitech_receiver/i18n.py:51 msgid "too many devices" msgstr "liian monta laitetta" #: lib/logitech_receiver/i18n.py:52 msgid "sequence timeout" msgstr "sarjan aikakatkaisu" #: lib/logitech_receiver/i18n.py:54 lib/solaar/ui/window.py:555 msgid "Firmware" msgstr "Laiteohjelmisto" #: lib/logitech_receiver/i18n.py:55 msgid "Bootloader" msgstr "Alkulatausohjelma" #: lib/logitech_receiver/i18n.py:56 msgid "Hardware" msgstr "Laitteisto" #: lib/logitech_receiver/i18n.py:57 msgid "Other" msgstr "Muu" #: lib/logitech_receiver/i18n.py:59 msgid "Left Button" msgstr "Vasen painike" #: lib/logitech_receiver/i18n.py:60 msgid "Right Button" msgstr "Oikea painike" #: lib/logitech_receiver/i18n.py:61 msgid "Middle Button" msgstr "Keskipainike" #: lib/logitech_receiver/i18n.py:62 msgid "Back Button" msgstr "Edellinen-painike" #: lib/logitech_receiver/i18n.py:63 msgid "Forward Button" msgstr "Seuraava-painike" #: lib/logitech_receiver/i18n.py:64 msgid "Mouse Gesture Button" msgstr "Hiiren elepainike" #: lib/logitech_receiver/i18n.py:65 msgid "Smart Shift" msgstr "Smart Shift" #: lib/logitech_receiver/i18n.py:66 msgid "DPI Switch" msgstr "DPI-kytkin" #: lib/logitech_receiver/i18n.py:67 msgid "Left Tilt" msgstr "Kallistus vasemmalle" #: lib/logitech_receiver/i18n.py:68 msgid "Right Tilt" msgstr "Kallistus oikealle" #: lib/logitech_receiver/i18n.py:69 msgid "Left Click" msgstr "Vasen napsautus" #: lib/logitech_receiver/i18n.py:70 msgid "Right Click" msgstr "Oikea napsautus" #: lib/logitech_receiver/i18n.py:71 msgid "Mouse Middle Button" msgstr "Hiiren keskipainike" #: lib/logitech_receiver/i18n.py:72 msgid "Mouse Back Button" msgstr "Hiiren edellinen-painike" #: lib/logitech_receiver/i18n.py:73 msgid "Mouse Forward Button" msgstr "Hiiren seuraava-painike" #: lib/logitech_receiver/i18n.py:74 msgid "Gesture Button Navigation" msgstr "Elepainikkeen navigointi" #: lib/logitech_receiver/i18n.py:75 msgid "Mouse Scroll Left Button" msgstr "Hiiren vieritys vasemmalle -painike" #: lib/logitech_receiver/i18n.py:76 msgid "Mouse Scroll Right Button" msgstr "Hiiren vieritys oikealle -painike" #: lib/logitech_receiver/i18n.py:78 msgid "pressed" msgstr "painettu" #: lib/logitech_receiver/i18n.py:79 msgid "released" msgstr "vapautettu" #: lib/logitech_receiver/notifications.py:156 lib/solaar/listener.py:243 msgid "connected" msgstr "yhdistetty" #: lib/logitech_receiver/notifications.py:156 lib/solaar/listener.py:243 msgid "disconnected" msgstr "yhteys katkaistu" #: lib/logitech_receiver/notifications.py:182 msgid "unpaired" msgstr "parittamaton" #: lib/logitech_receiver/notifications.py:231 msgid "powered on" msgstr "virta päällä" #: lib/logitech_receiver/notifications.py:283 msgid "ADC measurement notification" msgstr "ADC-mittausilmoitus" #: lib/logitech_receiver/notifications.py:428 #: lib/logitech_receiver/notifications.py:484 msgid "pairing lock is closed" msgstr "parituslukko on suljettu" #: lib/logitech_receiver/notifications.py:428 #: lib/logitech_receiver/notifications.py:484 msgid "pairing lock is open" msgstr "parituslukko on auki" #: lib/logitech_receiver/notifications.py:447 msgid "discovery lock is closed" msgstr "etsintälukko on suljettu" #: lib/logitech_receiver/notifications.py:447 msgid "discovery lock is open" msgstr "etsintälukko on auki" #: lib/logitech_receiver/receiver.py:437 msgid "No paired devices." msgstr "Ei paritettuja laitteita." #: lib/logitech_receiver/receiver.py:439 lib/solaar/ui/window.py:606 #, python-format msgid "%(count)s paired device." msgid_plural "%(count)s paired devices." msgstr[0] "%(count)s paritettu laite." msgstr[1] "%(count)s paritettua laitetta." #: lib/logitech_receiver/settings.py:602 msgid "register" msgstr "rekisteri" #: lib/logitech_receiver/settings.py:616 lib/logitech_receiver/settings.py:654 msgid "feature" msgstr "ominaisuus" #: lib/logitech_receiver/settings_templates.py:138 msgid "Swap Fx function" msgstr "Vaihda Fx" #: lib/logitech_receiver/settings_templates.py:141 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:146 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:154 msgid "Hand Detection" msgstr "Käsien tunnistus" #: lib/logitech_receiver/settings_templates.py:155 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:162 msgid "Scroll Wheel Smooth Scrolling" msgstr "Vierityspyörän sulava vieritys" #: lib/logitech_receiver/settings_templates.py:163 #: lib/logitech_receiver/settings_templates.py:410 #: lib/logitech_receiver/settings_templates.py:439 msgid "High-sensitivity mode for vertical scroll with the wheel." msgstr "Korkean herkkyyden tila pystyvieritykseen hiiren rullalla." #: lib/logitech_receiver/settings_templates.py:170 msgid "Side Scrolling" msgstr "Sivuttaisvieritys" #: lib/logitech_receiver/settings_templates.py:172 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:182 msgid "Sensitivity (DPI - older mice)" msgstr "Herkkyys (DPI - vanhemmat hiiret)" #: lib/logitech_receiver/settings_templates.py:183 #: lib/logitech_receiver/settings_templates.py:1019 #: lib/logitech_receiver/settings_templates.py:1047 msgid "Mouse movement sensitivity" msgstr "Hiiren liikkeen herkkyys" #: lib/logitech_receiver/settings_templates.py:256 msgid "Backlight Timed" msgstr "Ajastettu taustavalo" #: lib/logitech_receiver/settings_templates.py:257 #: lib/logitech_receiver/settings_templates.py:397 msgid "Set illumination time for keyboard." msgstr "Aseta näppäimistön valaistusaika." #: lib/logitech_receiver/settings_templates.py:268 msgid "Backlight" msgstr "Taustavalo" #: lib/logitech_receiver/settings_templates.py:269 msgid "" "Illumination level on keyboard. Changes made are only applied in Manual " "mode." msgstr "" "Näppäimistön valaistustaso. Tehdyt muutokset otetaan käyttöön vain " "manuaalisessa tilassa." #: lib/logitech_receiver/settings_templates.py:301 msgid "Automatic" msgstr "Automaattinen" #: lib/logitech_receiver/settings_templates.py:303 msgid "Manual" msgstr "Manuaalinen" #: lib/logitech_receiver/settings_templates.py:305 msgid "Enabled" msgstr "Käytössä" #: lib/logitech_receiver/settings_templates.py:311 msgid "Backlight Level" msgstr "Taustavalon taso" #: lib/logitech_receiver/settings_templates.py:312 msgid "Illumination level on keyboard when in Manual mode." msgstr "Näppäimistön valaistustaso manuaalisessa tilassa." #: lib/logitech_receiver/settings_templates.py:369 msgid "Backlight Delay Hands Out" msgstr "Taustavalon viive, kädet poissa" #: lib/logitech_receiver/settings_templates.py:370 msgid "" "Delay in seconds until backlight fades out with hands away from keyboard." msgstr "Taustavalon viive sekunneissa, kun kädet ovat poissa näppäimistöltä." #: lib/logitech_receiver/settings_templates.py:378 msgid "Backlight Delay Hands In" msgstr "Taustavalon viive, kädet lähellä" #: lib/logitech_receiver/settings_templates.py:379 msgid "Delay in seconds until backlight fades out with hands near keyboard." msgstr "Taustavalon viive sekunneissa, kun kädet ovat näppäimistön lähellä." #: lib/logitech_receiver/settings_templates.py:387 msgid "Backlight Delay Powered" msgstr "Taustavalon viive kytkettynä" #: lib/logitech_receiver/settings_templates.py:388 msgid "Delay in seconds until backlight fades out with external power." msgstr "Taustavalon viive sekunneissa ulkoiseen virtaan kytkettynä." #: lib/logitech_receiver/settings_templates.py:396 msgid "Backlight (Seconds)" msgstr "Taustavalo (sekuntia)" #: lib/logitech_receiver/settings_templates.py:408 msgid "Scroll Wheel High Resolution" msgstr "Vierityspyörän korkea resoluutio" #: lib/logitech_receiver/settings_templates.py:412 #: lib/logitech_receiver/settings_templates.py:441 msgid "Set to ignore if scrolling is abnormally fast or slow" msgstr "Aseta sivuuttamaan, jos vieritys on epänormaalin nopeaa tai hidasta" #: lib/logitech_receiver/settings_templates.py:419 #: lib/logitech_receiver/settings_templates.py:450 msgid "Scroll Wheel Diversion" msgstr "Vierityspyörän uudelleenohjaus" #: lib/logitech_receiver/settings_templates.py:421 msgid "" "Make scroll wheel send LOWRES_WHEEL HID++ notifications (which trigger " "Solaar rules but are otherwise ignored)." msgstr "" "Aseta vierityspyörä lähettämään LOWRES_WHEEL HID++ -ilmoituksia (jotka " "laukaisevat Solaar-sääntöjä mutta muuten ohitetaan)." #: lib/logitech_receiver/settings_templates.py:428 msgid "Scroll Wheel Direction" msgstr "Vierityspyörän suunta" #: lib/logitech_receiver/settings_templates.py:429 msgid "Invert direction for vertical scroll with wheel." msgstr "Käännä vierityspyörän pystysuuntainen vierityssuunta." #: lib/logitech_receiver/settings_templates.py:437 msgid "Scroll Wheel Resolution" msgstr "Vierityspyörän resoluutio" #: lib/logitech_receiver/settings_templates.py:452 msgid "" "Make scroll wheel send HIRES_WHEEL HID++ notifications (which trigger Solaar" " rules but are otherwise ignored)." msgstr "" "Aseta vierityspyörä lähettämään HIRES_WHEEL HID++ -ilmoituksia (jotka " "laukaisevat Solaar-sääntöjä mutta muuten ohitetaan)." #: lib/logitech_receiver/settings_templates.py:461 msgid "Sensitivity (Pointer Speed)" msgstr "Herkkyys (osoittimen nopeus)" #: lib/logitech_receiver/settings_templates.py:462 msgid "Speed multiplier for mouse (256 is normal multiplier)." msgstr "Hiiren nopeuskerroin (256 on tavallinen kerroin)." #: lib/logitech_receiver/settings_templates.py:472 msgid "Thumb Wheel Diversion" msgstr "Peukalopyörän uudelleenohjaus" #: lib/logitech_receiver/settings_templates.py:474 msgid "" "Make thumb wheel send THUMB_WHEEL HID++ notifications (which trigger Solaar " "rules but are otherwise ignored)." msgstr "" "Aseta peukalopyörä lähettämään THUMB_WHEEL HID++ -ilmoituksia (jotka " "laukaisevat Solaar-sääntöjä mutta muuten ohitetaan)." #: lib/logitech_receiver/settings_templates.py:483 msgid "Thumb Wheel Direction" msgstr "Peukalopyörän suunta" #: lib/logitech_receiver/settings_templates.py:484 msgid "Invert thumb wheel scroll direction." msgstr "Käännä peukalopyörän vierityssuunta." #: lib/logitech_receiver/settings_templates.py:504 msgid "Onboard Profiles" msgstr "Laitteen omat profiilit" #: lib/logitech_receiver/settings_templates.py:505 msgid "" "Enable an onboard profile, which controls report rate, sensitivity, and " "button actions" msgstr "" "Ota käyttöön laitteen oma profiili, joka hallitsee päivitysnopeutta, " "herkkyyttä ja painiketoimintoja" #: lib/logitech_receiver/settings_templates.py:549 #: lib/logitech_receiver/settings_templates.py:582 msgid "Report Rate" msgstr "Päivitysnopeus" #: lib/logitech_receiver/settings_templates.py:551 #: lib/logitech_receiver/settings_templates.py:584 msgid "Frequency of device movement reports" msgstr "Laitteen liikeraporttien taajuus" #: lib/logitech_receiver/settings_templates.py:551 #: lib/logitech_receiver/settings_templates.py:584 #: lib/logitech_receiver/settings_templates.py:1019 #: lib/logitech_receiver/settings_templates.py:1047 #: lib/logitech_receiver/settings_templates.py:1421 #: lib/logitech_receiver/settings_templates.py:1452 msgid "May need Onboard Profiles set to Disable to be effective." msgstr "" "Voi edellyttää, että Laitteen omat profiilit -asetus on pois käytöstä " "ollakseen voimassa." #: lib/logitech_receiver/settings_templates.py:612 msgid "Divert crown events" msgstr "Uudelleenohjaa kruunun tapahtumat" #: lib/logitech_receiver/settings_templates.py:613 msgid "" "Make crown send CROWN HID++ notifications (which trigger Solaar rules but " "are otherwise ignored)." msgstr "" "Aseta kruunu lähettämään CROWN HID++ -ilmoituksia (jotka laukaisevat Solaar-" "sääntöjä mutta muuten ohitetaan)." #: lib/logitech_receiver/settings_templates.py:621 msgid "Crown smooth scroll" msgstr "Kruunun sulava vieritys" #: lib/logitech_receiver/settings_templates.py:622 msgid "Set crown smooth scroll" msgstr "Aseta kruunun sulava vieritys" #: lib/logitech_receiver/settings_templates.py:630 msgid "Divert G and M Keys" msgstr "Uudelleenohjaa G- ja M-näppäimet" #: lib/logitech_receiver/settings_templates.py:631 msgid "" "Make G and M keys send HID++ notifications (which trigger Solaar rules but " "are otherwise ignored)." msgstr "" "Aseta G- ja M-näppäimet lähettämään HID++ -ilmoituksia (jotka laukaisevat " "Solaar-sääntöjä mutta muuten ohitetaan)." #: lib/logitech_receiver/settings_templates.py:645 msgid "Scroll Wheel Ratcheted" msgstr "Vierityspyörän pykäläinen asetus" #: lib/logitech_receiver/settings_templates.py:646 msgid "" "Switch the mouse wheel between speed-controlled ratcheting and always " "freespin." msgstr "" "Vaihda hiiren pyörää nopeusohjatun pykäläisen ja aina vapaasti pyörivän " "välillä." #: lib/logitech_receiver/settings_templates.py:648 msgid "Freespinning" msgstr "Vapaasti pyörivä" #: lib/logitech_receiver/settings_templates.py:648 msgid "Ratcheted" msgstr "Pykäläinen" #: lib/logitech_receiver/settings_templates.py:655 msgid "Scroll Wheel Ratchet Speed" msgstr "Vierityspyörän pykäläisen asetuksen nopeus" #: lib/logitech_receiver/settings_templates.py:657 msgid "" "Use the mouse wheel speed to switch between ratcheted and freespinning.\n" "The mouse wheel is always ratcheted at 50." msgstr "" "Käytä hiiren pyörän nopeutta vaihtaaksesi pykäläisen ja vapaasti pyörivän välillä.\n" "Hiiren pyörä on aina pykäläinen nopeudella 50." #: lib/logitech_receiver/settings_templates.py:707 msgid "Scroll Wheel Ratchet Torque" msgstr "Vierityspyörän pykälän vääntömomentti" #: lib/logitech_receiver/settings_templates.py:708 msgid "Change the torque needed to overcome the ratchet." msgstr "Muuta pykälän ylittämiseen tarvittavaa vääntömomenttia." #: lib/logitech_receiver/settings_templates.py:743 msgid "Key/Button Actions" msgstr "Näppäin-/painiketoiminnot" #: lib/logitech_receiver/settings_templates.py:745 msgid "Change the action for the key or button." msgstr "Muuta näppäimen tai painikkeen toimintoa." #: lib/logitech_receiver/settings_templates.py:747 msgid "Overridden by diversion." msgstr "Uudelleenohjauksen ohittama." #: lib/logitech_receiver/settings_templates.py:749 msgid "" "Changing important actions (such as for the left mouse button) can result in" " an unusable system." msgstr "" "Tärkeiden toimintojen (kuten hiiren vasemman painikkeen) muuttaminen voi " "johtaa käyttökelvottomaan järjestelmään." #: lib/logitech_receiver/settings_templates.py:924 msgid "Key/Button Diversion" msgstr "Näppäimen/painikkeen uudelleenohjaus" #: lib/logitech_receiver/settings_templates.py:925 msgid "" "Make the key or button send HID++ notifications (Diverted) or initiate Mouse" " Gestures or Sliding DPI" msgstr "" "Aseta näppäin tai painike lähettämään HID++ -ilmoituksia (Uudelleenohjattu) " "tai aloita hiirieleet tai liukuva DPI" #: lib/logitech_receiver/settings_templates.py:928 #: lib/logitech_receiver/settings_templates.py:929 #: lib/logitech_receiver/settings_templates.py:930 msgid "Diverted" msgstr "Uudelleenohjattu" #: lib/logitech_receiver/settings_templates.py:928 #: lib/logitech_receiver/settings_templates.py:929 msgid "Mouse Gestures" msgstr "Hiirieleet" #: lib/logitech_receiver/settings_templates.py:928 #: lib/logitech_receiver/settings_templates.py:929 #: lib/logitech_receiver/settings_templates.py:930 msgid "Regular" msgstr "Säännöllinen" #: lib/logitech_receiver/settings_templates.py:928 msgid "Sliding DPI" msgstr "Liukuva DPI" #: lib/logitech_receiver/settings_templates.py:1018 #: lib/logitech_receiver/settings_templates.py:1046 msgid "Sensitivity (DPI)" msgstr "Herkkyys (DPI)" #: lib/logitech_receiver/settings_templates.py:1123 msgid "Sensitivity Switching" msgstr "Herkkyyden vaihtaminen" #: lib/logitech_receiver/settings_templates.py:1125 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 "" "Vaihda nykyinen herkkyys ja muistettu herkkyys, kun näppäintä tai painiketta painetaan.\n" "Jos muistettua herkkyyttä ei ole, muista vain nykyinen herkkyys" #: lib/logitech_receiver/settings_templates.py:1129 msgid "Off" msgstr "Pois" #: lib/logitech_receiver/settings_templates.py:1160 msgid "Disable keys" msgstr "Poista näppäimiä käytöstä" #: lib/logitech_receiver/settings_templates.py:1161 msgid "Disable specific keyboard keys." msgstr "Poista tiettyjä näppäimistön näppäimiä käytöstä." #: lib/logitech_receiver/settings_templates.py:1164 #, python-format msgid "Disables the %s key." msgstr "Poistaa %s-näppäimen käytöstä." #: lib/logitech_receiver/settings_templates.py:1177 #: lib/logitech_receiver/settings_templates.py:1234 msgid "Set OS" msgstr "Aseta käyttöjärjestelmä" #: lib/logitech_receiver/settings_templates.py:1178 #: lib/logitech_receiver/settings_templates.py:1235 msgid "Change keys to match OS." msgstr "Muuta näppäimet vastaamaan käyttöjärjestelmää." #: lib/logitech_receiver/settings_templates.py:1247 msgid "Change Host" msgstr "Vaihda isäntälaitetta" #: lib/logitech_receiver/settings_templates.py:1248 msgid "Switch connection to a different host" msgstr "Vaihda yhteys toiseen isäntälaitteeseen" #: lib/logitech_receiver/settings_templates.py:1272 msgid "Performs a left click." msgstr "Suorittaa vasemman napsautuksen." #: lib/logitech_receiver/settings_templates.py:1272 msgid "Single tap" msgstr "Yksi napautus" #: lib/logitech_receiver/settings_templates.py:1273 msgid "Performs a right click." msgstr "Suorittaa oikean napsautuksen." #: lib/logitech_receiver/settings_templates.py:1273 msgid "Single tap with two fingers" msgstr "Yksi napautus kahdella sormella" #: lib/logitech_receiver/settings_templates.py:1274 msgid "Single tap with three fingers" msgstr "Yksi napautus kolmella sormella" #: lib/logitech_receiver/settings_templates.py:1278 msgid "Double tap" msgstr "Kaksoisnapautus" #: lib/logitech_receiver/settings_templates.py:1278 msgid "Performs a double click." msgstr "Suorittaa kaksoisnapsautuksen." #: lib/logitech_receiver/settings_templates.py:1279 msgid "Double tap with two fingers" msgstr "Kaksoisnapautus kahdella sormella" #: lib/logitech_receiver/settings_templates.py:1280 msgid "Double tap with three fingers" msgstr "Kaksoisnapautus kolmella sormella" #: lib/logitech_receiver/settings_templates.py:1283 msgid "Drags items by dragging the finger after double tapping." msgstr "Raahaa kohteita vetämällä sormea kaksoisnapautuksen jälkeen." #: lib/logitech_receiver/settings_templates.py:1283 msgid "Tap and drag" msgstr "Napauta ja raahaa" #: lib/logitech_receiver/settings_templates.py:1285 msgid "Tap and drag with two fingers" msgstr "Napauta ja raahaa kahdella sormella" #: lib/logitech_receiver/settings_templates.py:1286 msgid "Drags items by dragging the fingers after double tapping." msgstr "Raahaa kohteita vetämällä sormia kaksoisnapautuksen jälkeen." #: lib/logitech_receiver/settings_templates.py:1288 msgid "Tap and drag with three fingers" msgstr "Napauta ja raahaa kolmella sormella" #: lib/logitech_receiver/settings_templates.py:1291 msgid "Suppress tap and edge gestures" msgstr "Estä napautus- ja reunaeleet" #: lib/logitech_receiver/settings_templates.py:1292 msgid "Disables tap and edge gestures (equivalent to pressing Fn+LeftClick)." msgstr "Poistaa napautus- ja reunaeleet käytöstä (vastaa Fn+Vasen napsautus)." #: lib/logitech_receiver/settings_templates.py:1294 msgid "Scroll with one finger" msgstr "Vieritä yhdellä sormella" #: lib/logitech_receiver/settings_templates.py:1294 #: lib/logitech_receiver/settings_templates.py:1295 #: lib/logitech_receiver/settings_templates.py:1298 msgid "Scrolls." msgstr "Vierittää." #: lib/logitech_receiver/settings_templates.py:1295 #: lib/logitech_receiver/settings_templates.py:1298 msgid "Scroll with two fingers" msgstr "Vieritä kahdella sormella" #: lib/logitech_receiver/settings_templates.py:1296 msgid "Scroll horizontally with two fingers" msgstr "Vieritä vaakasuunnassa kahdella sormella" #: lib/logitech_receiver/settings_templates.py:1296 msgid "Scrolls horizontally." msgstr "Vierittää vaakasuunnassa." #: lib/logitech_receiver/settings_templates.py:1297 msgid "Scroll vertically with two fingers" msgstr "Vieritä pystysuunnassa kahdella sormella" #: lib/logitech_receiver/settings_templates.py:1297 msgid "Scrolls vertically." msgstr "Vierittää pystysuunnassa." #: lib/logitech_receiver/settings_templates.py:1299 msgid "Inverts the scrolling direction." msgstr "Kääntää vierityssuunnan." #: lib/logitech_receiver/settings_templates.py:1299 msgid "Natural scrolling" msgstr "Luonnollinen vieritys" #: lib/logitech_receiver/settings_templates.py:1300 msgid "Enables the thumbwheel." msgstr "Ottaa peukalopyörän käyttöön." #: lib/logitech_receiver/settings_templates.py:1300 msgid "Thumbwheel" msgstr "Peukalopyörä" #: lib/logitech_receiver/settings_templates.py:1311 #: lib/logitech_receiver/settings_templates.py:1315 msgid "Swipe from the top edge" msgstr "Pyyhkäise yläreunasta" #: lib/logitech_receiver/settings_templates.py:1312 msgid "Swipe from the left edge" msgstr "Pyyhkäise vasemmasta reunasta" #: lib/logitech_receiver/settings_templates.py:1313 msgid "Swipe from the right edge" msgstr "Pyyhkäise oikeasta reunasta" #: lib/logitech_receiver/settings_templates.py:1314 msgid "Swipe from the bottom edge" msgstr "Pyyhkäise alareunasta" #: lib/logitech_receiver/settings_templates.py:1316 msgid "Swipe two fingers from the left edge" msgstr "Pyyhkäise kahdella sormella vasemmasta reunasta" #: lib/logitech_receiver/settings_templates.py:1317 msgid "Swipe two fingers from the right edge" msgstr "Pyyhkäise kahdella sormella oikeasta reunasta" #: lib/logitech_receiver/settings_templates.py:1318 msgid "Swipe two fingers from the bottom edge" msgstr "Pyyhkäise kahdella sormella alareunasta" #: lib/logitech_receiver/settings_templates.py:1319 msgid "Swipe two fingers from the top edge" msgstr "Pyyhkäise kahdella sormella yläreunasta" #: lib/logitech_receiver/settings_templates.py:1320 #: lib/logitech_receiver/settings_templates.py:1324 msgid "Pinch to zoom out; spread to zoom in." msgstr "Nipistä loitontaaksesi; levitä lähentääksesi." #: lib/logitech_receiver/settings_templates.py:1320 msgid "Zoom with two fingers." msgstr "Zoomaa kahdella sormella." #: lib/logitech_receiver/settings_templates.py:1321 msgid "Pinch to zoom out." msgstr "Nipistä loitontaaksesi." #: lib/logitech_receiver/settings_templates.py:1322 msgid "Spread to zoom in." msgstr "Levitä lähentääksesi." #: lib/logitech_receiver/settings_templates.py:1323 msgid "Zoom with three fingers." msgstr "Zoomaa kolmella sormella." #: lib/logitech_receiver/settings_templates.py:1324 msgid "Zoom with two fingers" msgstr "Zoomaa kahdella sormella" #: lib/logitech_receiver/settings_templates.py:1342 msgid "Pixel zone" msgstr "Pikselialue" #: lib/logitech_receiver/settings_templates.py:1343 msgid "Ratio zone" msgstr "Suhdealue" #: lib/logitech_receiver/settings_templates.py:1344 msgid "Scale factor" msgstr "Skaalauskerroin" #: lib/logitech_receiver/settings_templates.py:1344 msgid "Sets the cursor speed." msgstr "Asettaa osoittimen nopeuden." #: lib/logitech_receiver/settings_templates.py:1348 msgid "Left" msgstr "Vasen" #: lib/logitech_receiver/settings_templates.py:1348 msgid "Left-most coordinate." msgstr "Vasemmanpuoleisin koordinaatti." #: lib/logitech_receiver/settings_templates.py:1349 msgid "Bottom" msgstr "Alareuna" #: lib/logitech_receiver/settings_templates.py:1349 msgid "Bottom coordinate." msgstr "Alakoordinaatti." #: lib/logitech_receiver/settings_templates.py:1350 msgid "Width" msgstr "Leveys" #: lib/logitech_receiver/settings_templates.py:1350 msgid "Width." msgstr "Leveys." #: lib/logitech_receiver/settings_templates.py:1351 msgid "Height" msgstr "Korkeus" #: lib/logitech_receiver/settings_templates.py:1351 msgid "Height." msgstr "Korkeus." #: lib/logitech_receiver/settings_templates.py:1352 msgid "Cursor speed." msgstr "Osoittimen nopeus." #: lib/logitech_receiver/settings_templates.py:1352 msgid "Scale" msgstr "Skaala" #: lib/logitech_receiver/settings_templates.py:1358 msgid "Gestures" msgstr "Eleet" #: lib/logitech_receiver/settings_templates.py:1359 msgid "Tweak the mouse/touchpad behaviour." msgstr "Säädä hiiren/kosketuslevyn toimintaa." #: lib/logitech_receiver/settings_templates.py:1375 msgid "Gestures Diversion" msgstr "Eleiden uudelleenohjaus" #: lib/logitech_receiver/settings_templates.py:1376 msgid "Divert mouse/touchpad gestures." msgstr "Uudelleenohjaa hiiren/kosketuslevyn eleet." #: lib/logitech_receiver/settings_templates.py:1392 msgid "Gesture params" msgstr "Eleiden parametrit" #: lib/logitech_receiver/settings_templates.py:1393 msgid "Change numerical parameters of a mouse/touchpad." msgstr "Muuta hiiren/kosketuslevyn numeerisia parametreja." #: lib/logitech_receiver/settings_templates.py:1417 msgid "M-Key LEDs" msgstr "M-näppäinten LEDit" #: lib/logitech_receiver/settings_templates.py:1419 msgid "Control the M-Key LEDs." msgstr "Hallitse M-näppäinten LEDejä." #: lib/logitech_receiver/settings_templates.py:1423 #: lib/logitech_receiver/settings_templates.py:1454 msgid "May need G Keys diverted to be effective." msgstr "Saattaa edellyttää G-näppäinten uudelleenohjausta toimiakseen." #: lib/logitech_receiver/settings_templates.py:1429 #, python-format msgid "Lights up the %s key." msgstr "Sytyttää %s-näppäimen." #: lib/logitech_receiver/settings_templates.py:1448 msgid "MR-Key LED" msgstr "MR-näppäimen LED" #: lib/logitech_receiver/settings_templates.py:1450 msgid "Control the MR-Key LED." msgstr "Hallitse MR-näppäimen LEDiä." #: lib/logitech_receiver/settings_templates.py:1471 msgid "Persistent Key/Button Mapping" msgstr "Pysyvä näppäin-/painikemääritys" #: lib/logitech_receiver/settings_templates.py:1473 msgid "Permanently change the mapping for the key or button." msgstr "Muuta näppäimen tai painikkeen määritystä pysyvästi." #: lib/logitech_receiver/settings_templates.py:1475 msgid "" "Changing important keys or buttons (such as for the left mouse button) can " "result in an unusable system." msgstr "" "Tärkeiden näppäinten tai painikkeiden (kuten hiiren vasemman painikkeen) " "muuttaminen voi johtaa käyttökelvottomaan järjestelmään." #: lib/logitech_receiver/settings_templates.py:1532 msgid "Sidetone" msgstr "Sivuääni" #: lib/logitech_receiver/settings_templates.py:1533 msgid "Set sidetone level." msgstr "Aseta sivuäänen taso." #: lib/logitech_receiver/settings_templates.py:1542 msgid "Equalizer" msgstr "Taajuuskorjain" #: lib/logitech_receiver/settings_templates.py:1543 msgid "Set equalizer levels." msgstr "Aseta taajuuskorjaimen tasot." #: lib/logitech_receiver/settings_templates.py:1565 msgid "Hz" msgstr "Hz" #: lib/logitech_receiver/settings_templates.py:1571 msgid "Power Management" msgstr "Virranhallinta" #: lib/logitech_receiver/settings_templates.py:1572 msgid "Power off in minutes (0 for never)." msgstr "Virrankatkaisu minuuteissa (0 = ei koskaan)." #: lib/logitech_receiver/settings_templates.py:1584 msgid "Brightness Control" msgstr "Kirkkauden säätö" #: lib/logitech_receiver/settings_templates.py:1585 msgid "Control overall brightness" msgstr "Hallitse yleistä kirkkautta" #: lib/logitech_receiver/settings_templates.py:1628 #: lib/logitech_receiver/settings_templates.py:1681 msgid "LED Control" msgstr "LED-hallinta" #: lib/logitech_receiver/settings_templates.py:1629 #: lib/logitech_receiver/settings_templates.py:1682 msgid "Switch control of LED zones between device and Solaar" msgstr "Vaihda LED-alueiden hallintaa laitteen ja Solaarin välillä" #: lib/logitech_receiver/settings_templates.py:1644 #: lib/logitech_receiver/settings_templates.py:1692 msgid "LED Zone Effects" msgstr "LED-alueiden efektit" #: lib/logitech_receiver/settings_templates.py:1645 #: lib/logitech_receiver/settings_templates.py:1693 msgid "LED Control needs to be set to Solaar to be effective." msgstr "LED-hallinta on asetettava Solaariin ollakseen voimassa." #: lib/logitech_receiver/settings_templates.py:1645 #: lib/logitech_receiver/settings_templates.py:1693 msgid "Set effect for LED Zone" msgstr "Aseta efekti LED-alueelle" #: lib/logitech_receiver/settings_templates.py:1647 msgid "Color" msgstr "Väri" #: lib/logitech_receiver/settings_templates.py:1648 msgid "Speed" msgstr "Nopeus" #: lib/logitech_receiver/settings_templates.py:1649 msgid "Period" msgstr "Jakso" #: lib/logitech_receiver/settings_templates.py:1650 msgid "Intensity" msgstr "Voimakkuus" #: lib/logitech_receiver/settings_templates.py:1651 msgid "Ramp" msgstr "Nousu" #: lib/logitech_receiver/settings_templates.py:1666 msgid "LEDs" msgstr "LEDit" #: lib/logitech_receiver/settings_templates.py:1703 msgid "Per-key Lighting" msgstr "Näppäinkohtainen valaistus" #: lib/logitech_receiver/settings_templates.py:1704 msgid "Control per-key lighting." msgstr "Hallitse näppäinkohtaista valaistusta." #: lib/logitech_receiver/settings_templates.py:1786 msgid "Force Sensing Buttons" msgstr "Voimaa tunnistavat painikkeet" #: lib/logitech_receiver/settings_templates.py:1787 msgid "Change the force required to activate button." msgstr "Muuta painikkeen aktivointiin tarvittavaa voimaa." #: lib/logitech_receiver/settings_templates.py:1804 msgid "Force Sensing Button" msgstr "Voimaa tunnistava painike" #: lib/logitech_receiver/settings_templates.py:1814 msgid "Haptic Feedback Level" msgstr "Haptisen palautteen taso" #: lib/logitech_receiver/settings_templates.py:1815 msgid "Change power of haptic feedback. (Zero to turn off.)" msgstr "Muuta haptisen palautteen voimakkuutta. (Nolla poistaa käytöstä.)" #: lib/logitech_receiver/settings_templates.py:1857 msgid "Play Haptic Waveform" msgstr "Toista haptinen aaltomuoto" #: lib/logitech_receiver/settings_templates.py:1858 msgid "Tell device to play a haptic waveform." msgstr "Käske laitetta toistamaan haptinen aaltomuoto." #: lib/solaar/ui/__init__.py:120 msgid "Another Solaar process is already running so just expose its window" msgstr "" "Toinen Solaar-prosessi on jo käynnissä, joten sen ikkuna tuodaan näkyviin" #: lib/solaar/ui/about/model.py:36 msgid "" "Manages Logitech receivers,\n" "keyboards, mice, and tablets." msgstr "" "Hallitsee Logitech-vastaanottimia,\n" "näppäimistöjä, hiiriä ja piirtopöytiä." #: lib/solaar/ui/about/model.py:64 msgid "Additional Programming" msgstr "Lisäohjelmointi" #: lib/solaar/ui/about/model.py:65 msgid "GUI design" msgstr "Käyttöliittymäsuunnittelu" #: lib/solaar/ui/about/model.py:67 msgid "Testing" msgstr "Testaus" #: lib/solaar/ui/about/model.py:75 #, fuzzy msgid "Logitech documentation" msgstr "Logitechin dokumentointi" #: lib/solaar/ui/action.py:88 lib/solaar/ui/action.py:92 #: lib/solaar/ui/window.py:208 msgid "Unpair" msgstr "Poista paritus" #: lib/solaar/ui/action.py:91 lib/solaar/ui/diversion_rules.py:141 msgid "Cancel" msgstr "Peruuta" #: lib/solaar/ui/common.py:42 msgid "Permissions error" msgstr "Virheelliset käyttöoikeudet" #: lib/solaar/ui/common.py:44 #, python-format msgid "" "Found a Logitech receiver or device (%s), but did not have permission to " "open it." msgstr "" "Löydettiin Logitech-vastaanotin tai -laite (%s), mutta siihen ei ollut " "avaamisoikeutta." #: lib/solaar/ui/common.py:46 msgid "" "If you've just installed Solaar, try disconnecting the receiver or device " "and then reconnecting it." msgstr "" "Jos asensit Solaarin juuri, kokeile irrottaa vastaanotin tai laite ja " "yhdistää se sitten uudelleen." #: lib/solaar/ui/common.py:49 msgid "Cannot connect to device error" msgstr "Virhe yhdistettäessä laitteeseen" #: lib/solaar/ui/common.py:51 #, python-format msgid "" "Found a Logitech receiver or device at %s, but encountered an error " "connecting to it." msgstr "" "Löydettiin Logitech-vastaanotin tai -laite polusta %s, mutta ilmeni virhe " "yhdistettäessä siihen." #: lib/solaar/ui/common.py:53 msgid "" "Try disconnecting the device and then reconnecting it or turning it off and " "then on." msgstr "" "Kokeile laitteen irrottamista ja yhdistämistä uudelleen tai sen " "sammuttamista ja käynnistämistä uudelleen." #: lib/solaar/ui/common.py:56 msgid "Unpairing failed" msgstr "Parituksen poisto epäonnistui" #: lib/solaar/ui/common.py:58 #, python-brace-format msgid "Failed to unpair %{device} from %{receiver}." msgstr "%{device}n parituksen poisto epäonnistui %{receiver}sta." #: lib/solaar/ui/common.py:63 msgid "The receiver returned an error, with no further details." msgstr "Vastaanotin palautti virheen ilman lisätietoja." #: lib/solaar/ui/config_panel.py:245 msgid "Complete - ENTER to change" msgstr "Valmis - paina ENTER muuttaaksesi" #: lib/solaar/ui/config_panel.py:245 msgid "Incomplete" msgstr "Keskeneräinen" #: lib/solaar/ui/config_panel.py:491 lib/solaar/ui/config_panel.py:543 #, python-format msgid "%d value" msgid_plural "%d values" msgstr[0] "%d arvo" msgstr[1] "%d arvoa" #: lib/solaar/ui/config_panel.py:642 msgid "Changes allowed" msgstr "Muutokset sallittu" #: lib/solaar/ui/config_panel.py:643 msgid "No changes allowed" msgstr "Ei muutoksia sallittu" #: lib/solaar/ui/config_panel.py:644 msgid "Ignore this setting" msgstr "Ohita tämä asetus" #: lib/solaar/ui/config_panel.py:690 msgid "Working" msgstr "Työskentelee" #: lib/solaar/ui/config_panel.py:693 msgid "Read/write operation failed." msgstr "Luku/kirjoitus operaatio epäonnistui." #: lib/solaar/ui/desktop_notifications.py:112 msgid "unspecified reason" msgstr "määrittämätön syy" #: lib/solaar/ui/diversion_rules.py:103 msgid "Built-in rules" msgstr "Sisäänrakennetut säännöt" #: lib/solaar/ui/diversion_rules.py:103 msgid "User-defined rules" msgstr "Käyttäjän määrittämät säännöt" #: lib/solaar/ui/diversion_rules.py:105 lib/solaar/ui/diversion_rules.py:1126 msgid "Rule" msgstr "Sääntö" #: lib/solaar/ui/diversion_rules.py:106 lib/solaar/ui/diversion_rules.py:382 #: lib/solaar/ui/diversion_rules.py:509 msgid "Sub-rule" msgstr "Alisääntö" #: lib/solaar/ui/diversion_rules.py:108 msgid "[empty]" msgstr "[tyhjä]" #: lib/solaar/ui/diversion_rules.py:132 msgid "Make changes permanent?" msgstr "Tehdäänkö muutoksista pysyviä?" #: lib/solaar/ui/diversion_rules.py:137 msgid "Yes" msgstr "Kyllä" #: lib/solaar/ui/diversion_rules.py:139 msgid "No" msgstr "Ei" #: lib/solaar/ui/diversion_rules.py:144 msgid "If you choose No, changes will be lost when Solaar is closed." msgstr "Jos valitset Ei, muutokset menetetään, kun Solaar suljetaan." #: lib/solaar/ui/diversion_rules.py:273 msgid "Paste here" msgstr "Liitä tähän" #: lib/solaar/ui/diversion_rules.py:275 msgid "Paste above" msgstr "Liitä yläpuolelle" #: lib/solaar/ui/diversion_rules.py:277 msgid "Paste below" msgstr "Liitä alapuolelle" #: lib/solaar/ui/diversion_rules.py:283 msgid "Paste rule here" msgstr "Liitä sääntö tähän" #: lib/solaar/ui/diversion_rules.py:285 msgid "Paste rule above" msgstr "Liitä sääntö yläpuolelle" #: lib/solaar/ui/diversion_rules.py:287 msgid "Paste rule below" msgstr "Liitä sääntö alapuolelle" #: lib/solaar/ui/diversion_rules.py:291 msgid "Paste rule" msgstr "Liitä sääntö" #: lib/solaar/ui/diversion_rules.py:306 msgid "Insert here" msgstr "Lisää tähän" #: lib/solaar/ui/diversion_rules.py:308 msgid "Insert above" msgstr "Lisää yläpuolelle" #: lib/solaar/ui/diversion_rules.py:310 msgid "Insert below" msgstr "Lisää alapuolelle" #: lib/solaar/ui/diversion_rules.py:316 msgid "Insert new rule here" msgstr "Lisää uusi sääntö tähän" #: lib/solaar/ui/diversion_rules.py:318 msgid "Insert new rule above" msgstr "Lisää uusi sääntö yläpuolelle" #: lib/solaar/ui/diversion_rules.py:320 msgid "Insert new rule below" msgstr "Lisää uusi sääntö alapuolelle" #: lib/solaar/ui/diversion_rules.py:347 msgid "Flatten" msgstr "Litistä" #: lib/solaar/ui/diversion_rules.py:380 msgid "Insert" msgstr "Lisää" #: lib/solaar/ui/diversion_rules.py:383 lib/solaar/ui/diversion_rules.py:511 #: lib/solaar/ui/diversion_rules.py:1158 msgid "Or" msgstr "Tai" #: lib/solaar/ui/diversion_rules.py:384 lib/solaar/ui/diversion_rules.py:510 #: lib/solaar/ui/diversion_rules.py:1144 msgid "And" msgstr "Ja" #: lib/solaar/ui/diversion_rules.py:386 msgid "Condition" msgstr "Ehto" #: lib/solaar/ui/diversion_rules.py:388 lib/solaar/ui/rule_conditions.py:154 msgid "Feature" msgstr "Ominaisuus" #: lib/solaar/ui/diversion_rules.py:389 lib/solaar/ui/rule_conditions.py:188 msgid "Report" msgstr "Raportti" #: lib/solaar/ui/diversion_rules.py:390 lib/solaar/ui/rule_conditions.py:69 msgid "Process" msgstr "Prosessi" #: lib/solaar/ui/diversion_rules.py:391 msgid "Mouse process" msgstr "Hiiriprosessi" #: lib/solaar/ui/diversion_rules.py:392 lib/solaar/ui/rule_conditions.py:225 msgid "Modifiers" msgstr "Muuntajat" #: lib/solaar/ui/diversion_rules.py:393 lib/solaar/ui/rule_conditions.py:277 msgid "Key" msgstr "Näppäin" #: lib/solaar/ui/diversion_rules.py:394 lib/solaar/ui/rule_conditions.py:318 msgid "KeyIsDown" msgstr "Näppäin on painettu" #: lib/solaar/ui/diversion_rules.py:395 lib/solaar/ui/diversion_rules.py:1432 msgid "Active" msgstr "Aktiivinen" #: lib/solaar/ui/diversion_rules.py:396 lib/solaar/ui/diversion_rules.py:1390 #: lib/solaar/ui/diversion_rules.py:1441 lib/solaar/ui/diversion_rules.py:1487 msgid "Device" msgstr "Laite" #: lib/solaar/ui/diversion_rules.py:397 lib/solaar/ui/diversion_rules.py:1467 msgid "Host" msgstr "Isäntä" #: lib/solaar/ui/diversion_rules.py:398 lib/solaar/ui/diversion_rules.py:1506 msgid "Setting" msgstr "Asetus" #: lib/solaar/ui/diversion_rules.py:399 lib/solaar/ui/rule_conditions.py:333 #: lib/solaar/ui/rule_conditions.py:382 msgid "Test" msgstr "Testi" #: lib/solaar/ui/diversion_rules.py:400 lib/solaar/ui/rule_conditions.py:507 msgid "Test bytes" msgstr "Testaa tavut" #: lib/solaar/ui/diversion_rules.py:401 lib/solaar/ui/rule_conditions.py:608 msgid "Mouse Gesture" msgstr "Hiiriele" #: lib/solaar/ui/diversion_rules.py:405 msgid "Action" msgstr "Toiminto" #: lib/solaar/ui/diversion_rules.py:407 lib/solaar/ui/rule_actions.py:138 msgid "Key press" msgstr "Näppäimen painallus" #: lib/solaar/ui/diversion_rules.py:408 lib/solaar/ui/rule_actions.py:191 msgid "Mouse scroll" msgstr "Hiiren vieritys" #: lib/solaar/ui/diversion_rules.py:409 lib/solaar/ui/rule_actions.py:256 msgid "Mouse click" msgstr "Hiiren napsautus" #: lib/solaar/ui/diversion_rules.py:410 msgid "Set" msgstr "Aseta" #: lib/solaar/ui/diversion_rules.py:411 lib/solaar/ui/rule_actions.py:328 msgid "Execute" msgstr "Suorita" #: lib/solaar/ui/diversion_rules.py:412 lib/solaar/ui/diversion_rules.py:1189 msgid "Later" msgstr "Myöhemmin" #: lib/solaar/ui/diversion_rules.py:441 msgid "Insert new rule" msgstr "Lisää uusi sääntö" #: lib/solaar/ui/diversion_rules.py:461 lib/solaar/ui/rule_actions.py:82 #: lib/solaar/ui/rule_actions.py:287 lib/solaar/ui/rule_conditions.py:556 msgid "Delete" msgstr "Poista" #: lib/solaar/ui/diversion_rules.py:483 msgid "Negate" msgstr "Käännä" #: lib/solaar/ui/diversion_rules.py:507 msgid "Wrap with" msgstr "Kääri" #: lib/solaar/ui/diversion_rules.py:537 msgid "Cut" msgstr "Leikkaa" #: lib/solaar/ui/diversion_rules.py:553 msgid "Paste" msgstr "Liitä" #: lib/solaar/ui/diversion_rules.py:559 msgid "Copy" msgstr "Kopioi" #: lib/solaar/ui/diversion_rules.py:568 msgid "Solaar Rule Editor" msgstr "Solaarin sääntöeditori" #: lib/solaar/ui/diversion_rules.py:668 msgid "Save changes" msgstr "Tallenna muutokset" #: lib/solaar/ui/diversion_rules.py:673 msgid "Discard changes" msgstr "Hylkää muutokset" #: lib/solaar/ui/diversion_rules.py:1104 msgid "This editor does not support the selected rule component yet." msgstr "Tämä editori ei vielä tue valittua sääntökomponenttia." #: lib/solaar/ui/diversion_rules.py:1169 msgid "" "Number of seconds to delay. Delay between 0 and 1 is done with higher " "precision." msgstr "" "Viive sekunteina. Viive 0:n ja 1:n välillä tehdään suuremmalla " "tarkkuudella." #: lib/solaar/ui/diversion_rules.py:1207 msgid "Not" msgstr "Ei" #: lib/solaar/ui/diversion_rules.py:1238 msgid "Toggle" msgstr "Vaihda" #: lib/solaar/ui/diversion_rules.py:1239 msgid "True" msgstr "Tosi" #: lib/solaar/ui/diversion_rules.py:1240 msgid "False" msgstr "Epätosi" #: lib/solaar/ui/diversion_rules.py:1253 msgid "Unsupported setting" msgstr "Ei tuettu asetus" #: lib/solaar/ui/diversion_rules.py:1396 lib/solaar/ui/diversion_rules.py:1415 #: lib/solaar/ui/diversion_rules.py:1493 lib/solaar/ui/diversion_rules.py:1748 #: lib/solaar/ui/diversion_rules.py:1766 msgid "Originating device" msgstr "Alkuperäinen laite" #: lib/solaar/ui/diversion_rules.py:1428 msgid "Device is active and its settings can be changed." msgstr "Laite on aktiivinen ja sen asetuksia voidaan muuttaa." #: lib/solaar/ui/diversion_rules.py:1437 msgid "Device that originated the current notification." msgstr "Laite, josta nykyinen ilmoitus on peräisin." #: lib/solaar/ui/diversion_rules.py:1450 msgid "Name of host computer." msgstr "Isäntätietokoneen nimi." #: lib/solaar/ui/diversion_rules.py:1520 msgid "Value" msgstr "Arvo" #: lib/solaar/ui/diversion_rules.py:1529 msgid "Item" msgstr "Kohde" #: lib/solaar/ui/diversion_rules.py:1808 msgid "Change setting on device" msgstr "Muuta asetusta laitteessa" #: lib/solaar/ui/diversion_rules.py:1824 msgid "Setting on device" msgstr "Asetus laitteessa" #: lib/solaar/ui/pair_window.py:44 lib/solaar/ui/pair_window.py:167 #, python-format msgid "%(receiver_name)s: pair new device" msgstr "%(receiver_name)s: parita uusi laite" #: lib/solaar/ui/pair_window.py:46 msgid "Bolt receivers are only compatible with Bolt devices." msgstr "Bolt-vastaanottimet ovat yhteensopivia vain Bolt-laitteiden kanssa." #: lib/solaar/ui/pair_window.py:48 msgid "Press a pairing button or key until the pairing light flashes quickly." msgstr "" "Paina parituspainiketta tai -näppäintä, kunnes paritusvalo vilkkuu nopeasti." #: lib/solaar/ui/pair_window.py:51 msgid "Unifying receivers are only compatible with Unifying devices." msgstr "" "Unifying-vastaanottimet ovat yhteensopivia vain Unifying-laitteiden kanssa." #: lib/solaar/ui/pair_window.py:53 msgid "Other receivers are only compatible with a few devices." msgstr "" "Muut vastaanottimet ovat yhteensopivia vain joidenkin laitteiden kanssa." #: lib/solaar/ui/pair_window.py:55 msgid "For most devices, turn on the device you want to pair." msgstr "Useimmissa laitteissa kytke päälle laite, jonka haluat parittaa." #: lib/solaar/ui/pair_window.py:56 msgid "If the device is already turned on, turn it off and on again." msgstr "Jos laite on jo päällä, sammuta se ja käynnistä uudelleen." #: lib/solaar/ui/pair_window.py:58 msgid "The device must not be paired with a nearby powered-on receiver." msgstr "" "Laitetta ei saa olla paritettu lähellä olevaan päällä olevaan " "vastaanottimeen." #: lib/solaar/ui/pair_window.py:61 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 "" "Jos laitteessa on useita kanavia, paina, pidä painettuna ja vapauta sen kanavan painike, jonka haluat parittaa\n" "tai käytä kanavanvaihtopainiketta valitaksesi kanavan ja paina, pidä painettuna ja vapauta sitten kanavanvaihtopainike." #: lib/solaar/ui/pair_window.py:68 msgid "The channel indicator light should be blinking rapidly." msgstr "Kanavan merkkivalon pitäisi vilkkua nopeasti." #: lib/solaar/ui/pair_window.py:72 #, 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" "Tässä vastaanottimessa on %d paritus jäljellä." msgstr[1] "" "\n" "\n" "Tässä vastaanottimessa on %d paritusta jäljellä." #: lib/solaar/ui/pair_window.py:78 msgid "" "\n" "Cancelling at this point will not use up a pairing." msgstr "" "\n" "Peruuttaminen tässä vaiheessa ei kuluta paritusta." #: lib/solaar/ui/pair_window.py:168 #, python-format msgid "Enter passcode on %(name)s." msgstr "Anna pääsykoodi laitteella %(name)s." #: lib/solaar/ui/pair_window.py:171 #, python-format msgid "Type %(passcode)s and then press the enter key." msgstr "Kirjoita %(passcode)s ja paina sitten enter-näppäintä." #: lib/solaar/ui/pair_window.py:176 msgid "left" msgstr "vasen" #: lib/solaar/ui/pair_window.py:176 msgid "right" msgstr "oikea" #: lib/solaar/ui/pair_window.py:178 #, python-format msgid "" "Press %(code)s\n" "and then press left and right buttons simultaneously." msgstr "" "Paina %(code)s\n" "ja paina sitten vasenta ja oikeaa painiketta samanaikaisesti." #: lib/solaar/ui/pair_window.py:221 msgid "The wireless link is not encrypted" msgstr "Langaton yhteys ei ole salattu" #: lib/solaar/ui/pair_window.py:226 msgid "Found a new device:" msgstr "Löytyi uusi laite:" #: lib/solaar/ui/pair_window.py:247 msgid "Pairing failed" msgstr "Paritus epäonnistui." #: lib/solaar/ui/pair_window.py:249 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:251 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:253 msgid "More paired devices than receiver can support." msgstr "Enemmän paritettuja laitteita kuin vastaanotin tukee." #: lib/solaar/ui/pair_window.py:255 msgid "No further details are available about the error." msgstr "Virheestä ei ole saatavilla enempää tietoja." #: lib/solaar/ui/rule_actions.py:54 msgid "" "Simulate a chorded key click or depress or release.\n" "On Wayland requires write access to /dev/uinput." msgstr "" "Simuloi sointunäppäimen napsautusta, painallusta tai vapautusta.\n" "Waylandissa vaatii kirjoitusoikeuden laitteeseen /dev/uinput." #: lib/solaar/ui/rule_actions.py:60 msgid "Add key" msgstr "Lisää näppäin" #: lib/solaar/ui/rule_actions.py:63 msgid "Click" msgstr "Napsautus" #: lib/solaar/ui/rule_actions.py:66 msgid "Depress" msgstr "Paina" #: lib/solaar/ui/rule_actions.py:69 msgid "Release" msgstr "Vapauta" #: lib/solaar/ui/rule_actions.py:153 msgid "" "Simulate a mouse scroll.\n" "On Wayland requires write access to /dev/uinput." msgstr "" "Simuloi hiiren vieritystä.\n" "Waylandissa vaatii kirjoitusoikeuden laitteeseen /dev/uinput." #: lib/solaar/ui/rule_actions.py:211 msgid "" "Simulate a mouse click.\n" "On Wayland requires write access to /dev/uinput." msgstr "" "Simuloi hiiren napsautusta.\n" "Waylandissa vaatii kirjoitusoikeuden laitteeseen /dev/uinput." #: lib/solaar/ui/rule_actions.py:216 msgid "Button" msgstr "Painike" #: lib/solaar/ui/rule_actions.py:218 msgid "Action (and Count, if click)" msgstr "Toiminto (ja määrä, jos napsautus)" #: lib/solaar/ui/rule_actions.py:269 msgid "Execute a command with arguments." msgstr "Suorita komento argumenteilla." #: lib/solaar/ui/rule_actions.py:273 msgid "Add argument" msgstr "Lisää argumentti" #: lib/solaar/ui/rule_conditions.py:52 msgid "X11 active process. For use in X11 only." msgstr "X11 aktiivinen prosessi. Vain X11-käyttöön." #: lib/solaar/ui/rule_conditions.py:82 msgid "X11 mouse process. For use in X11 only." msgstr "X11 hiiriprosessi. Vain X11-käyttöön." #: lib/solaar/ui/rule_conditions.py:99 msgid "MouseProcess" msgstr "Hiiriprosessi" #: lib/solaar/ui/rule_conditions.py:123 msgid "Feature name of notification triggering rule processing." msgstr "Säännönkäsittelyn laukaisevan ilmoituksen ominaisuuden nimi." #: lib/solaar/ui/rule_conditions.py:169 msgid "Report number of notification triggering rule processing." msgstr "Säännönkäsittelyn laukaisevan ilmoituksen raporttinumero." #: lib/solaar/ui/rule_conditions.py:201 msgid "Active keyboard modifiers. Not always available in Wayland." msgstr "" "Aktiiviset näppäimistön muuntajat. Eivät ole aina saatavilla Waylandissa." #: lib/solaar/ui/rule_conditions.py:241 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 "" "Uudelleenohjattu näppäin tai painike painettu tai vapautettu.\n" "Käytä näppäimen/painikkeen uudelleenohjaus- ja uudelleenohjaa G-näppäimet -asetuksia ohjataksesi näppäimiä ja painikkeita." #: lib/solaar/ui/rule_conditions.py:250 msgid "Key down" msgstr "Näppäin painettuna" #: lib/solaar/ui/rule_conditions.py:253 msgid "Key up" msgstr "Näppäin vapautettuna" #: lib/solaar/ui/rule_conditions.py:293 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 "" "Uudelleenohjattu näppäin tai painike on parhaillaan painettuna.\n" "Käytä näppäimen/painikkeen uudelleenohjaus- ja uudelleenohjaa G-näppäimet -asetuksia ohjataksesi näppäimiä ja painikkeita." #: lib/solaar/ui/rule_conditions.py:331 msgid "Test condition on notification triggering rule processing." msgstr "Testaa ehto säännönkäsittelyn laukaisevassa ilmoituksessa." #: lib/solaar/ui/rule_conditions.py:335 msgid "Parameter" msgstr "Parametri" #: lib/solaar/ui/rule_conditions.py:408 msgid "begin (inclusive)" msgstr "alku (sisältyy)" #: lib/solaar/ui/rule_conditions.py:409 msgid "end (exclusive)" msgstr "loppu (ei sisälly)" #: lib/solaar/ui/rule_conditions.py:417 msgid "range" msgstr "alue" #: lib/solaar/ui/rule_conditions.py:420 msgid "minimum" msgstr "minimi" #: lib/solaar/ui/rule_conditions.py:421 msgid "maximum" msgstr "maksimi" #: lib/solaar/ui/rule_conditions.py:423 #, python-format msgid "bytes %(0)d to %(1)d, ranging from %(2)d to %(3)d" msgstr "tavut %(0)d - %(1)d, väliltä %(2)d - %(3)d" #: lib/solaar/ui/rule_conditions.py:426 lib/solaar/ui/rule_conditions.py:427 msgid "mask" msgstr "maski" #: lib/solaar/ui/rule_conditions.py:428 #, python-format msgid "bytes %(0)d to %(1)d, mask %(2)d" msgstr "tavut %(0)d - %(1)d, maski %(2)d" #: lib/solaar/ui/rule_conditions.py:437 msgid "" "Bit or range test on bytes in notification message triggering rule " "processing." msgstr "" "Bitti- tai aluetesti tavuille säännönkäsittelyn laukaisevassa " "ilmoitusviestissä." #: lib/solaar/ui/rule_conditions.py:447 msgid "type" msgstr "tyyppi" #: lib/solaar/ui/rule_conditions.py:535 msgid "" "Mouse gesture with optional initiating button followed by zero or more mouse" " movements." msgstr "" "Hiiriele valinnaisella aloituspainikkeella, jota seuraa nolla tai useampi " "hiiriliike." #: lib/solaar/ui/rule_conditions.py:541 msgid "Add movement" msgstr "Lisää liike" #: lib/solaar/ui/tray.py:55 msgid "No supported device found" msgstr "Ei löydetty tuettua laitetta" #: lib/solaar/ui/tray.py:60 lib/solaar/ui/window.py:328 #, python-format msgid "About %s" msgstr "Tietoja %s" #: lib/solaar/ui/tray.py:61 lib/solaar/ui/window.py:326 #, python-format msgid "Quit %s" msgstr "Lopeta %s" #: lib/solaar/ui/tray.py:273 lib/solaar/ui/tray.py:281 msgid "no receiver" msgstr "ei vastaanotinta" #: lib/solaar/ui/tray.py:294 lib/solaar/ui/tray.py:299 #: lib/solaar/ui/window.py:728 msgid "offline" msgstr "ei yhdistetty" #: lib/solaar/ui/tray.py:297 msgid "no status" msgstr "ei tietoja" #: lib/solaar/ui/window.py:110 msgid "Scanning" msgstr "Skannataan" #: lib/solaar/ui/window.py:141 msgid "Battery" msgstr "Akku" #: lib/solaar/ui/window.py:144 msgid "Wireless Link" msgstr "Langaton yhteys" #: lib/solaar/ui/window.py:148 msgid "Lighting" msgstr "Valaistus" #: lib/solaar/ui/window.py:182 msgid "Show Technical Details" msgstr "Näytä tekniset tiedot" #: lib/solaar/ui/window.py:198 msgid "Pair new device" msgstr "Parita uusi laite" #: lib/solaar/ui/window.py:216 msgid "Select a device" msgstr "Valitse laite" #: lib/solaar/ui/window.py:331 msgid "Rule Editor" msgstr "Sääntöeditori" #: lib/solaar/ui/window.py:522 msgid "Path" msgstr "Polku" #: lib/solaar/ui/window.py:524 msgid "USB ID" msgstr "USB-tunnus" #: lib/solaar/ui/window.py:527 lib/solaar/ui/window.py:529 #: lib/solaar/ui/window.py:544 lib/solaar/ui/window.py:546 msgid "Serial" msgstr "Sarjanumero" #: lib/solaar/ui/window.py:533 msgid "Index" msgstr "Järjestysnumero" #: lib/solaar/ui/window.py:535 msgid "Wireless PID" msgstr "Langaton PID" #: lib/solaar/ui/window.py:537 msgid "Product ID" msgstr "Tuotteen tunnistekoodi" #: lib/solaar/ui/window.py:539 msgid "Protocol" msgstr "Yhteyskäytäntö" #: lib/solaar/ui/window.py:539 msgid "Unknown" msgstr "Tuntematon" #: lib/solaar/ui/window.py:541 msgid "Polling rate" msgstr "Päivitysnopeus" #: lib/solaar/ui/window.py:548 msgid "Unit ID" msgstr "Yksikön tunnus" #: lib/solaar/ui/window.py:559 msgid "none" msgstr "ei mitään" #: lib/solaar/ui/window.py:560 msgid "Notifications" msgstr "Ilmoitukset" #: lib/solaar/ui/window.py:604 msgid "No device paired." msgstr "Ei paritettua laitetta." #: lib/solaar/ui/window.py:613 #, 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] "" "Enintään %(max_count)s laite voidaan parittaa tähän vastaanottimeen." msgstr[1] "" "Enintään %(max_count)s laitetta voidaan parittaa tähän vastaanottimeen." #: lib/solaar/ui/window.py:620 msgid "Only one device can be paired to this receiver." msgstr "Vain yksi laite voidaan parittaa tähän vastaanottimeen." #: lib/solaar/ui/window.py:624 #, python-format msgid "This receiver has %d pairing remaining." msgid_plural "This receiver has %d pairings remaining." msgstr[0] "Tässä vastaanottimessa on %d paritus jäljellä." msgstr[1] "Tässä vastaanottimessa on %d paritusta jäljellä." #: lib/solaar/ui/window.py:681 msgid "Battery Voltage" msgstr "Akun jännite" #: lib/solaar/ui/window.py:683 msgid "Voltage reported by battery" msgstr "Akun ilmoittama jännite" #: lib/solaar/ui/window.py:685 msgid "Battery Level" msgstr "Akun taso" #: lib/solaar/ui/window.py:687 msgid "Approximate level reported by battery" msgstr "Akun ilmoittama arvioitu taso" #: lib/solaar/ui/window.py:694 lib/solaar/ui/window.py:696 msgid "next reported " msgstr "seuraavaksi ilmoitettu " #: lib/solaar/ui/window.py:697 msgid " and next level to be reported." msgstr " ja seuraava ilmoitettava taso." #: lib/solaar/ui/window.py:702 msgid "last known" msgstr "viimeisin tunnettu" #: lib/solaar/ui/window.py:713 msgid "encrypted" msgstr "salattu" #: lib/solaar/ui/window.py:715 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:717 msgid "not encrypted" msgstr "salaamaton" #: lib/solaar/ui/window.py:721 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 "" "Langaton yhteys tämän laitteen ja sen vastaanottimen välillä ei ole salattu.\n" "Tämä on tietoturvaongelma osoitinlaitteille ja suuri tietoturvariski tekstinsyöttölaitteille." #: lib/solaar/ui/window.py:737 #, python-format msgid "%(light_level)d lux" msgstr "%(light_level)d luksia" #~ 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 "Sulava 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 "Turn on the device you want to pair." #~ msgstr "Käynnistä laite, jonka haluat parittaa." #~ msgid "USB id" #~ msgstr "USB-tunnus" #~ 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" ================================================ FILE: po/fr.po ================================================ # 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.19\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-01-08 19:21+0100\n" "PO-Revision-Date: 2026-01-08 19:38+0100\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.8\n" #: lib/logitech_receiver/base_usb.py:52 msgid "Bolt Receiver" msgstr "Récepteur Bolt" #: lib/logitech_receiver/base_usb.py:64 msgid "Unifying Receiver" msgstr "Récepteur Unifying" #: lib/logitech_receiver/base_usb.py:75 lib/logitech_receiver/base_usb.py:87 #: lib/logitech_receiver/base_usb.py:100 lib/logitech_receiver/base_usb.py:113 msgid "Nano Receiver" msgstr "Récepteur Nano" #: lib/logitech_receiver/base_usb.py:125 msgid "Lightspeed Receiver" msgstr "Récepteur Lightspeed" #: lib/logitech_receiver/base_usb.py:136 msgid "EX100 Receiver 27 Mhz" msgstr "Récepteur EX100 27 Mhz" #: lib/logitech_receiver/common.py:649 #, python-format msgid "Battery: %(level)s (%(status)s)" msgstr "Batterie : %(level)s (%(status)s)" #: lib/logitech_receiver/common.py:652 #, python-format msgid "Battery: %(percent)d%% (%(status)s)" msgstr "Batterie : %(percent)d%% (%(status)s)" #: lib/logitech_receiver/hidpp20.py:1048 #: lib/logitech_receiver/settings_templates.py:299 msgid "Disabled" msgstr "Désactivé" #: lib/logitech_receiver/hidpp20.py:1049 msgid "Static" msgstr "Statique" #: lib/logitech_receiver/hidpp20.py:1050 msgid "Pulse" msgstr "Pulsation" #: lib/logitech_receiver/hidpp20.py:1051 msgid "Cycle" msgstr "Cycle" #: lib/logitech_receiver/hidpp20.py:1052 msgid "Boot" msgstr "Boot" #: lib/logitech_receiver/hidpp20.py:1053 msgid "Demo" msgstr "Démo" #: lib/logitech_receiver/hidpp20.py:1055 msgid "Breathe" msgstr "" #: lib/logitech_receiver/hidpp20.py:1058 msgid "Ripple" msgstr "" #: lib/logitech_receiver/hidpp20.py:1059 msgid "Decomposition" msgstr "Décomposition" #: lib/logitech_receiver/hidpp20.py:1060 msgid "Signature1" msgstr "Signature 1" #: lib/logitech_receiver/hidpp20.py:1061 msgid "Signature2" msgstr "Signature 2" #: lib/logitech_receiver/hidpp20.py:1062 msgid "CycleS" msgstr "Cycle S" #: lib/logitech_receiver/hidpp20.py:1126 msgid "Unknown Location" msgstr "Emplacement inconnu" #: lib/logitech_receiver/hidpp20.py:1127 msgid "Primary" msgstr "Primaire" #: lib/logitech_receiver/hidpp20.py:1128 msgid "Logo" msgstr "Logo" #: lib/logitech_receiver/hidpp20.py:1129 msgid "Left Side" msgstr "Côté gauche" #: lib/logitech_receiver/hidpp20.py:1130 msgid "Right Side" msgstr "Côté droit" #: lib/logitech_receiver/hidpp20.py:1131 msgid "Combined" msgstr "Combiné" #: lib/logitech_receiver/hidpp20.py:1132 msgid "Primary 1" msgstr "Primaire 1" #: lib/logitech_receiver/hidpp20.py:1133 msgid "Primary 2" msgstr "Primaire 2" #: lib/logitech_receiver/hidpp20.py:1134 msgid "Primary 3" msgstr "Primaire 3" #: lib/logitech_receiver/hidpp20.py:1135 msgid "Primary 4" msgstr "Primaire 4" #: lib/logitech_receiver/hidpp20.py:1136 msgid "Primary 5" msgstr "Primaire 5" #: lib/logitech_receiver/hidpp20.py:1137 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 lib/solaar/ui/window.py:700 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 lib/solaar/ui/window.py:555 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:156 lib/solaar/listener.py:243 msgid "connected" msgstr "connecté" #: lib/logitech_receiver/notifications.py:156 lib/solaar/listener.py:243 msgid "disconnected" msgstr "déconnecté" #: lib/logitech_receiver/notifications.py:182 msgid "unpaired" msgstr "non jumelé" #: lib/logitech_receiver/notifications.py:231 msgid "powered on" msgstr "sous tension" #: lib/logitech_receiver/notifications.py:283 msgid "ADC measurement notification" msgstr "Notification de mesure ADC" #: lib/logitech_receiver/notifications.py:428 #: lib/logitech_receiver/notifications.py:484 msgid "pairing lock is closed" msgstr "le verrou de jumelage est fermé" #: lib/logitech_receiver/notifications.py:428 #: lib/logitech_receiver/notifications.py:484 msgid "pairing lock is open" msgstr "le verrou de jumelage est ouvert" #: lib/logitech_receiver/notifications.py:447 msgid "discovery lock is closed" msgstr "le verrou de détection est fermé" #: lib/logitech_receiver/notifications.py:447 msgid "discovery lock is open" msgstr "le verrou de détection est ouvert" #: lib/logitech_receiver/receiver.py:437 msgid "No paired devices." msgstr "Aucun périphérique jumelé." #: lib/logitech_receiver/receiver.py:439 lib/solaar/ui/window.py:606 #, 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:602 msgid "register" msgstr "registre" #: lib/logitech_receiver/settings.py:616 lib/logitech_receiver/settings.py:654 msgid "feature" msgstr "fonctionnalité" #: lib/logitech_receiver/settings_templates.py:138 msgid "Swap Fx function" msgstr "Fonction Swap Fx" #: lib/logitech_receiver/settings_templates.py:141 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:146 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:154 msgid "Hand Detection" msgstr "Détection manuelle" #: lib/logitech_receiver/settings_templates.py:155 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:162 msgid "Scroll Wheel Smooth Scrolling" msgstr "Défilement fluide à la molette" #: lib/logitech_receiver/settings_templates.py:163 #: lib/logitech_receiver/settings_templates.py:410 #: lib/logitech_receiver/settings_templates.py:439 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:170 msgid "Side Scrolling" msgstr "Défilement latéral" #: lib/logitech_receiver/settings_templates.py:172 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:182 msgid "Sensitivity (DPI - older mice)" msgstr "Sensibilité (DPI - souris plus anciennne)" #: lib/logitech_receiver/settings_templates.py:183 #: lib/logitech_receiver/settings_templates.py:1019 #: lib/logitech_receiver/settings_templates.py:1047 msgid "Mouse movement sensitivity" msgstr "Sensibilité du mouvement de la souris" #: lib/logitech_receiver/settings_templates.py:256 msgid "Backlight Timed" msgstr "Rétroéclairage chronométré" #: lib/logitech_receiver/settings_templates.py:257 #: lib/logitech_receiver/settings_templates.py:397 msgid "Set illumination time for keyboard." msgstr "Définir le temps d'éclairage du clavier." #: lib/logitech_receiver/settings_templates.py:268 msgid "Backlight" msgstr "Rétroéclairage" #: lib/logitech_receiver/settings_templates.py:269 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:301 msgid "Automatic" msgstr "Automatique" #: lib/logitech_receiver/settings_templates.py:303 msgid "Manual" msgstr "Manuel" #: lib/logitech_receiver/settings_templates.py:305 msgid "Enabled" msgstr "Activé" #: lib/logitech_receiver/settings_templates.py:311 msgid "Backlight Level" msgstr "Niveau de rétroéclairage" #: lib/logitech_receiver/settings_templates.py:312 msgid "Illumination level on keyboard when in Manual mode." msgstr "Niveau d'éclairage du clavier en mode manuel." #: lib/logitech_receiver/settings_templates.py:369 msgid "Backlight Delay Hands Out" msgstr "Délai de rétroéclairage sans les mains" #: lib/logitech_receiver/settings_templates.py:370 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:378 msgid "Backlight Delay Hands In" msgstr "Délai de rétroéclairage avec les mains" #: lib/logitech_receiver/settings_templates.py:379 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:387 msgid "Backlight Delay Powered" msgstr "Délai de rétroéclairage alimenté" #: lib/logitech_receiver/settings_templates.py:388 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:396 msgid "Backlight (Seconds)" msgstr "Rétroéclairage (Secondes)" #: lib/logitech_receiver/settings_templates.py:408 msgid "Scroll Wheel High Resolution" msgstr "Molette haute résolution" #: lib/logitech_receiver/settings_templates.py:412 #: lib/logitech_receiver/settings_templates.py:441 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:419 #: lib/logitech_receiver/settings_templates.py:450 msgid "Scroll Wheel Diversion" msgstr "Interception de la molette" #: lib/logitech_receiver/settings_templates.py:421 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:428 msgid "Scroll Wheel Direction" msgstr "Direction de la molette" #: lib/logitech_receiver/settings_templates.py:429 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:437 msgid "Scroll Wheel Resolution" msgstr "Résolution de la molette" #: lib/logitech_receiver/settings_templates.py:452 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:461 msgid "Sensitivity (Pointer Speed)" msgstr "Sensibilité (vitesse du pointeur)" #: lib/logitech_receiver/settings_templates.py:462 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:472 msgid "Thumb Wheel Diversion" msgstr "Interception de la molette de pouce" #: lib/logitech_receiver/settings_templates.py:474 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:483 msgid "Thumb Wheel Direction" msgstr "Direction de la molette de pouce" #: lib/logitech_receiver/settings_templates.py:484 msgid "Invert thumb wheel scroll direction." msgstr "Inverser la direction du défilement de la molette de pouce." #: lib/logitech_receiver/settings_templates.py:504 msgid "Onboard Profiles" msgstr "Profils embarqués" #: lib/logitech_receiver/settings_templates.py:505 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:549 #: lib/logitech_receiver/settings_templates.py:582 msgid "Report Rate" msgstr "Taux de rapport" #: lib/logitech_receiver/settings_templates.py:551 #: lib/logitech_receiver/settings_templates.py:584 msgid "Frequency of device movement reports" msgstr "Fréquence des rapports de mouvement des périphériques" #: lib/logitech_receiver/settings_templates.py:551 #: lib/logitech_receiver/settings_templates.py:584 #: lib/logitech_receiver/settings_templates.py:1019 #: lib/logitech_receiver/settings_templates.py:1047 #: lib/logitech_receiver/settings_templates.py:1421 #: lib/logitech_receiver/settings_templates.py:1452 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:612 msgid "Divert crown events" msgstr "Définir les évènements de la couronne" #: lib/logitech_receiver/settings_templates.py:613 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:621 msgid "Crown smooth scroll" msgstr "Défilement fluide de la couronne" #: lib/logitech_receiver/settings_templates.py:622 msgid "Set crown smooth scroll" msgstr "Définir le défilement fluide de la couronne" #: lib/logitech_receiver/settings_templates.py:630 msgid "Divert G and M Keys" msgstr "Interception des touches G et M" #: lib/logitech_receiver/settings_templates.py:631 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:645 msgid "Scroll Wheel Ratcheted" msgstr "Molette mode cliquet" #: lib/logitech_receiver/settings_templates.py:646 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:648 msgid "Freespinning" msgstr "Roue libre" #: lib/logitech_receiver/settings_templates.py:648 msgid "Ratcheted" msgstr "Cliquet" #: lib/logitech_receiver/settings_templates.py:655 msgid "Scroll Wheel Ratchet Speed" msgstr "Vitesse de la molette mode cliquet" #: lib/logitech_receiver/settings_templates.py:657 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:707 msgid "Scroll Wheel Ratchet Torque" msgstr "Couple de la molette mode cliquet" #: lib/logitech_receiver/settings_templates.py:708 msgid "Change the torque needed to overcome the ratchet." msgstr "Modifiez le couple nécessaire pour vaincre le mode cliquet." #: lib/logitech_receiver/settings_templates.py:743 msgid "Key/Button Actions" msgstr "Actions des boutons" #: lib/logitech_receiver/settings_templates.py:745 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:747 msgid "Overridden by diversion." msgstr "Remplacé par interception." #: lib/logitech_receiver/settings_templates.py:749 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:924 msgid "Key/Button Diversion" msgstr "Interception des boutons/touches" #: lib/logitech_receiver/settings_templates.py:925 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:928 #: lib/logitech_receiver/settings_templates.py:929 #: lib/logitech_receiver/settings_templates.py:930 msgid "Diverted" msgstr "Interception" #: lib/logitech_receiver/settings_templates.py:928 #: lib/logitech_receiver/settings_templates.py:929 msgid "Mouse Gestures" msgstr "Gestuelle à la souris" #: lib/logitech_receiver/settings_templates.py:928 #: lib/logitech_receiver/settings_templates.py:929 #: lib/logitech_receiver/settings_templates.py:930 msgid "Regular" msgstr "Normal" #: lib/logitech_receiver/settings_templates.py:928 msgid "Sliding DPI" msgstr "Amplification du déplacement" #: lib/logitech_receiver/settings_templates.py:1018 #: lib/logitech_receiver/settings_templates.py:1046 msgid "Sensitivity (DPI)" msgstr "Sensibilité (DPI)" #: lib/logitech_receiver/settings_templates.py:1123 msgid "Sensitivity Switching" msgstr "Basculement de la sensibilité" #: lib/logitech_receiver/settings_templates.py:1125 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:1129 msgid "Off" msgstr "Eteint" #: lib/logitech_receiver/settings_templates.py:1160 msgid "Disable keys" msgstr "Désactiver les touches" #: lib/logitech_receiver/settings_templates.py:1161 msgid "Disable specific keyboard keys." msgstr "Désactiver des touches spécifiques du clavier." #: lib/logitech_receiver/settings_templates.py:1164 #, python-format msgid "Disables the %s key." msgstr "Désactive la touche %s." #: lib/logitech_receiver/settings_templates.py:1177 #: lib/logitech_receiver/settings_templates.py:1234 msgid "Set OS" msgstr "Définir le système d'exploitation" #: lib/logitech_receiver/settings_templates.py:1178 #: lib/logitech_receiver/settings_templates.py:1235 msgid "Change keys to match OS." msgstr "Modifier les touches pour correspondre au système d'exploitation." #: lib/logitech_receiver/settings_templates.py:1247 msgid "Change Host" msgstr "Changer d'hôte" #: lib/logitech_receiver/settings_templates.py:1248 msgid "Switch connection to a different host" msgstr "Commuter la connexion vers un autre hôte" #: lib/logitech_receiver/settings_templates.py:1272 msgid "Performs a left click." msgstr "Réalise un clic gauche." #: lib/logitech_receiver/settings_templates.py:1272 msgid "Single tap" msgstr "Une tape simple" #: lib/logitech_receiver/settings_templates.py:1273 msgid "Performs a right click." msgstr "Réalise un clic droit." #: lib/logitech_receiver/settings_templates.py:1273 msgid "Single tap with two fingers" msgstr "Une tape simple avec deux doigts" #: lib/logitech_receiver/settings_templates.py:1274 msgid "Single tap with three fingers" msgstr "Une tape simple avec trois doigts" #: lib/logitech_receiver/settings_templates.py:1278 msgid "Double tap" msgstr "Une double tape" #: lib/logitech_receiver/settings_templates.py:1278 msgid "Performs a double click." msgstr "Réalise un double clic." #: lib/logitech_receiver/settings_templates.py:1279 msgid "Double tap with two fingers" msgstr "Une double tape avec deux doigts" #: lib/logitech_receiver/settings_templates.py:1280 msgid "Double tap with three fingers" msgstr "Une double tape avec trois doigts" #: lib/logitech_receiver/settings_templates.py:1283 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:1283 msgid "Tap and drag" msgstr "Taper-glisser" #: lib/logitech_receiver/settings_templates.py:1285 msgid "Tap and drag with two fingers" msgstr "Taper-glisser avec deux doigts" #: lib/logitech_receiver/settings_templates.py:1286 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:1288 msgid "Tap and drag with three fingers" msgstr "Taper-glisser avec trois doigts" #: lib/logitech_receiver/settings_templates.py:1291 msgid "Suppress tap and edge gestures" msgstr "Supprimer les tapes et les gestes" #: lib/logitech_receiver/settings_templates.py:1292 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:1294 msgid "Scroll with one finger" msgstr "Défilement à un doigt" #: lib/logitech_receiver/settings_templates.py:1294 #: lib/logitech_receiver/settings_templates.py:1295 #: lib/logitech_receiver/settings_templates.py:1298 msgid "Scrolls." msgstr "Défilements." #: lib/logitech_receiver/settings_templates.py:1295 #: lib/logitech_receiver/settings_templates.py:1298 msgid "Scroll with two fingers" msgstr "Défilement à deux doigts" #: lib/logitech_receiver/settings_templates.py:1296 msgid "Scroll horizontally with two fingers" msgstr "Défilement horizontal à deux doigts" #: lib/logitech_receiver/settings_templates.py:1296 msgid "Scrolls horizontally." msgstr "Défile horizontalement." #: lib/logitech_receiver/settings_templates.py:1297 msgid "Scroll vertically with two fingers" msgstr "Défilement vertical à deux doigts" #: lib/logitech_receiver/settings_templates.py:1297 msgid "Scrolls vertically." msgstr "Défile verticalement." #: lib/logitech_receiver/settings_templates.py:1299 msgid "Inverts the scrolling direction." msgstr "Inverse la direction de défilement." #: lib/logitech_receiver/settings_templates.py:1299 msgid "Natural scrolling" msgstr "Défilement naturel" #: lib/logitech_receiver/settings_templates.py:1300 msgid "Enables the thumbwheel." msgstr "Active la molette de pouce." #: lib/logitech_receiver/settings_templates.py:1300 msgid "Thumbwheel" msgstr "Molette de pouce" #: lib/logitech_receiver/settings_templates.py:1311 #: lib/logitech_receiver/settings_templates.py:1315 msgid "Swipe from the top edge" msgstr "Glissement depuis le bord supérieur" #: lib/logitech_receiver/settings_templates.py:1312 msgid "Swipe from the left edge" msgstr "Glissement depuis le bord gauche" #: lib/logitech_receiver/settings_templates.py:1313 msgid "Swipe from the right edge" msgstr "Glissement depuis le bord droit" #: lib/logitech_receiver/settings_templates.py:1314 msgid "Swipe from the bottom edge" msgstr "Glissement depuis le bord inférieur" #: lib/logitech_receiver/settings_templates.py:1316 msgid "Swipe two fingers from the left edge" msgstr "Glissement à deux doigts depuis le bord gauche" #: lib/logitech_receiver/settings_templates.py:1317 msgid "Swipe two fingers from the right edge" msgstr "Glissement à deux doigts depuis le bord droit" #: lib/logitech_receiver/settings_templates.py:1318 msgid "Swipe two fingers from the bottom edge" msgstr "Glissement à deux doigts depuis le bord inférieur" #: lib/logitech_receiver/settings_templates.py:1319 msgid "Swipe two fingers from the top edge" msgstr "Glissement à deux doigts depuis le bord supérieur" #: lib/logitech_receiver/settings_templates.py:1320 #: lib/logitech_receiver/settings_templates.py:1324 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:1320 msgid "Zoom with two fingers." msgstr "Zoom à deux doigts." #: lib/logitech_receiver/settings_templates.py:1321 msgid "Pinch to zoom out." msgstr "Pincer pour réduire le zoom." #: lib/logitech_receiver/settings_templates.py:1322 msgid "Spread to zoom in." msgstr "Écarter pour agrandir le zoom." #: lib/logitech_receiver/settings_templates.py:1323 msgid "Zoom with three fingers." msgstr "Zoom à trois doigts." #: lib/logitech_receiver/settings_templates.py:1324 msgid "Zoom with two fingers" msgstr "Zoom à deux doigts" #: lib/logitech_receiver/settings_templates.py:1342 msgid "Pixel zone" msgstr "Zone de pixels" #: lib/logitech_receiver/settings_templates.py:1343 msgid "Ratio zone" msgstr "Zone de rapport" #: lib/logitech_receiver/settings_templates.py:1344 msgid "Scale factor" msgstr "Facteur d’échelle" #: lib/logitech_receiver/settings_templates.py:1344 msgid "Sets the cursor speed." msgstr "Définit la vitesse du curseur." #: lib/logitech_receiver/settings_templates.py:1348 msgid "Left" msgstr "Gauche" #: lib/logitech_receiver/settings_templates.py:1348 msgid "Left-most coordinate." msgstr "Coordonnée la plus à gauche." #: lib/logitech_receiver/settings_templates.py:1349 msgid "Bottom" msgstr "Bas" #: lib/logitech_receiver/settings_templates.py:1349 msgid "Bottom coordinate." msgstr "Coordonnée du bas." #: lib/logitech_receiver/settings_templates.py:1350 msgid "Width" msgstr "Largeur" #: lib/logitech_receiver/settings_templates.py:1350 msgid "Width." msgstr "Largeur." #: lib/logitech_receiver/settings_templates.py:1351 msgid "Height" msgstr "Hauteur" #: lib/logitech_receiver/settings_templates.py:1351 msgid "Height." msgstr "Hauteur." #: lib/logitech_receiver/settings_templates.py:1352 msgid "Cursor speed." msgstr "Vitesse du curseur." #: lib/logitech_receiver/settings_templates.py:1352 msgid "Scale" msgstr "Échelle" #: lib/logitech_receiver/settings_templates.py:1358 msgid "Gestures" msgstr "Gestes" #: lib/logitech_receiver/settings_templates.py:1359 msgid "Tweak the mouse/touchpad behaviour." msgstr "Personnaliser le comportement de la souris ou du pavé tactile." #: lib/logitech_receiver/settings_templates.py:1375 msgid "Gestures Diversion" msgstr "Interception de la gestuelle" #: lib/logitech_receiver/settings_templates.py:1376 msgid "Divert mouse/touchpad gestures." msgstr "Interception de la gestuelle de la souris ou du pavé tactile." #: lib/logitech_receiver/settings_templates.py:1392 msgid "Gesture params" msgstr "Paramètres de la gestuelle" #: lib/logitech_receiver/settings_templates.py:1393 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:1417 msgid "M-Key LEDs" msgstr "LEDs de touche M" #: lib/logitech_receiver/settings_templates.py:1419 msgid "Control the M-Key LEDs." msgstr "Contrôler les LEDs de touche M." #: lib/logitech_receiver/settings_templates.py:1423 #: lib/logitech_receiver/settings_templates.py:1454 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:1429 #, python-format msgid "Lights up the %s key." msgstr "Allume la touche %s." #: lib/logitech_receiver/settings_templates.py:1448 msgid "MR-Key LED" msgstr "LED de touche MR" #: lib/logitech_receiver/settings_templates.py:1450 msgid "Control the MR-Key LED." msgstr "Contrôler la LED de touche MR." #: lib/logitech_receiver/settings_templates.py:1471 msgid "Persistent Key/Button Mapping" msgstr "Mappage touche/bouton persistant" #: lib/logitech_receiver/settings_templates.py:1473 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:1475 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:1532 msgid "Sidetone" msgstr "Tonalité latérale" #: lib/logitech_receiver/settings_templates.py:1533 msgid "Set sidetone level." msgstr "Régler le niveau de tonalité latérale." #: lib/logitech_receiver/settings_templates.py:1542 msgid "Equalizer" msgstr "Égaliseur" #: lib/logitech_receiver/settings_templates.py:1543 msgid "Set equalizer levels." msgstr "Définir les niveaux de l'égaliseur." #: lib/logitech_receiver/settings_templates.py:1565 msgid "Hz" msgstr "Hz" #: lib/logitech_receiver/settings_templates.py:1571 msgid "Power Management" msgstr "Gestion de l'alimentation" #: lib/logitech_receiver/settings_templates.py:1572 msgid "Power off in minutes (0 for never)." msgstr "Éteindre en quelques minutes (0 pour jamais)." #: lib/logitech_receiver/settings_templates.py:1584 msgid "Brightness Control" msgstr "Contrôle de la luminosité" #: lib/logitech_receiver/settings_templates.py:1585 msgid "Control overall brightness" msgstr "Contrôle de la luminosité globale" #: lib/logitech_receiver/settings_templates.py:1628 #: lib/logitech_receiver/settings_templates.py:1681 msgid "LED Control" msgstr "Contrôle de LED" #: lib/logitech_receiver/settings_templates.py:1629 #: lib/logitech_receiver/settings_templates.py:1682 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:1644 #: lib/logitech_receiver/settings_templates.py:1692 msgid "LED Zone Effects" msgstr "Effet de zone LED" #: lib/logitech_receiver/settings_templates.py:1645 #: lib/logitech_receiver/settings_templates.py:1693 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:1645 #: lib/logitech_receiver/settings_templates.py:1693 msgid "Set effect for LED Zone" msgstr "Définir l'effet pour la zone LED" #: lib/logitech_receiver/settings_templates.py:1647 msgid "Color" msgstr "Couleur" #: lib/logitech_receiver/settings_templates.py:1648 msgid "Speed" msgstr "Vitesse" #: lib/logitech_receiver/settings_templates.py:1649 msgid "Period" msgstr "Période" #: lib/logitech_receiver/settings_templates.py:1650 msgid "Intensity" msgstr "Intensité" #: lib/logitech_receiver/settings_templates.py:1651 msgid "Ramp" msgstr "" #: lib/logitech_receiver/settings_templates.py:1666 msgid "LEDs" msgstr "LEDs" #: lib/logitech_receiver/settings_templates.py:1703 msgid "Per-key Lighting" msgstr "Éclairage par touche" #: lib/logitech_receiver/settings_templates.py:1704 msgid "Control per-key lighting." msgstr "Contrôler l’éclairage par touche." #: lib/logitech_receiver/settings_templates.py:1786 msgid "Force Sensing Buttons" msgstr "" #: lib/logitech_receiver/settings_templates.py:1787 msgid "Change the force required to activate button." msgstr "Modifier la force nécessaire pour activer le bouton." #: lib/logitech_receiver/settings_templates.py:1804 msgid "Force Sensing Button" msgstr "" #: lib/logitech_receiver/settings_templates.py:1814 msgid "Haptic Feeback Level" msgstr "Niveau de retour haptique" #: lib/logitech_receiver/settings_templates.py:1815 msgid "Change power of haptic feedback. (Zero to turn off.)" msgstr "Modifiez l'intensité du retour haptique. (Zéro pour éteindre.)" #: lib/logitech_receiver/settings_templates.py:1857 msgid "Play Haptic Waveform" msgstr "" #: lib/logitech_receiver/settings_templates.py:1858 msgid "Tell device to play a haptic waveform." msgstr "" #: lib/solaar/ui/__init__.py:120 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/model.py:36 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/model.py:63 msgid "Additional Programming" msgstr "Programmation supplémentaire" #: lib/solaar/ui/about/model.py:64 msgid "GUI design" msgstr "Interface graphique" #: lib/solaar/ui/about/model.py:66 msgid "Testing" msgstr "Testeur" #: lib/solaar/ui/about/model.py:74 msgid "Logitech documentation" msgstr "Documentațion Logitech" #: lib/solaar/ui/action.py:88 lib/solaar/ui/action.py:92 #: lib/solaar/ui/window.py:208 msgid "Unpair" msgstr "Déconnecter" #: lib/solaar/ui/action.py:91 lib/solaar/ui/diversion_rules.py:141 msgid "Cancel" msgstr "Annuler" #: lib/solaar/ui/common.py:42 msgid "Permissions error" msgstr "Erreur de permissions" #: lib/solaar/ui/common.py:44 #, 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:46 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:49 msgid "Cannot connect to device error" msgstr "Impossible de se connecter au périphérique" #: lib/solaar/ui/common.py:51 #, 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:53 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:56 msgid "Unpairing failed" msgstr "La déconnexion a échouée" #: lib/solaar/ui/common.py:58 #, python-brace-format msgid "Failed to unpair %{device} from %{receiver}." msgstr "Impossible de dissocier %{device} de %{receiver}." #: lib/solaar/ui/common.py:63 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:245 msgid "Complete - ENTER to change" msgstr "Complet - ENTRER pour modifier" #: lib/solaar/ui/config_panel.py:245 msgid "Incomplete" msgstr "Incomplet" #: lib/solaar/ui/config_panel.py:491 lib/solaar/ui/config_panel.py:543 #, python-format msgid "%d value" msgid_plural "%d values" msgstr[0] "valeur %d" msgstr[1] "valeurs %d" #: lib/solaar/ui/config_panel.py:642 msgid "Changes allowed" msgstr "Modifications autorisées" #: lib/solaar/ui/config_panel.py:643 msgid "No changes allowed" msgstr "Aucune modification autorisée" #: lib/solaar/ui/config_panel.py:644 msgid "Ignore this setting" msgstr "Ignorer ce réglage" #: lib/solaar/ui/config_panel.py:690 msgid "Working" msgstr "En fonctionnement" #: lib/solaar/ui/config_panel.py:693 msgid "Read/write operation failed." msgstr "Les opérations de lecture/écriture ont échouée." #: lib/solaar/ui/desktop_notifications.py:112 msgid "unspecified reason" msgstr "raison non précisée" #: lib/solaar/ui/diversion_rules.py:103 msgid "Built-in rules" msgstr "Règles pré-définies" #: lib/solaar/ui/diversion_rules.py:103 msgid "User-defined rules" msgstr "Règles définies par l'utilisateur" #: lib/solaar/ui/diversion_rules.py:105 lib/solaar/ui/diversion_rules.py:1126 msgid "Rule" msgstr "Règle" #: lib/solaar/ui/diversion_rules.py:106 lib/solaar/ui/diversion_rules.py:382 #: lib/solaar/ui/diversion_rules.py:509 msgid "Sub-rule" msgstr "Sous-règle" #: lib/solaar/ui/diversion_rules.py:108 msgid "[empty]" msgstr "[vide]" #: lib/solaar/ui/diversion_rules.py:132 msgid "Make changes permanent?" msgstr "Rendre les modifications permanentes ?" #: lib/solaar/ui/diversion_rules.py:137 msgid "Yes" msgstr "Oui" #: lib/solaar/ui/diversion_rules.py:139 msgid "No" msgstr "Non" #: lib/solaar/ui/diversion_rules.py:144 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:273 msgid "Paste here" msgstr "Coller ici" #: lib/solaar/ui/diversion_rules.py:275 msgid "Paste above" msgstr "Coller ci-dessus" #: lib/solaar/ui/diversion_rules.py:277 msgid "Paste below" msgstr "Coller ci-dessous" #: lib/solaar/ui/diversion_rules.py:283 msgid "Paste rule here" msgstr "Coller la règle ici" #: lib/solaar/ui/diversion_rules.py:285 msgid "Paste rule above" msgstr "Coller la règle ci-dessus" #: lib/solaar/ui/diversion_rules.py:287 msgid "Paste rule below" msgstr "Coller la règle ci-dessous" #: lib/solaar/ui/diversion_rules.py:291 msgid "Paste rule" msgstr "Coller la règle" #: lib/solaar/ui/diversion_rules.py:306 msgid "Insert here" msgstr "Insérer ici" #: lib/solaar/ui/diversion_rules.py:308 msgid "Insert above" msgstr "Insérer ci-dessus" #: lib/solaar/ui/diversion_rules.py:310 msgid "Insert below" msgstr "Insérer ci-dessous" #: lib/solaar/ui/diversion_rules.py:316 msgid "Insert new rule here" msgstr "Insérer une nouvelle règle ici" #: lib/solaar/ui/diversion_rules.py:318 msgid "Insert new rule above" msgstr "Insérer une nouvelle règle ci-dessus" #: lib/solaar/ui/diversion_rules.py:320 msgid "Insert new rule below" msgstr "Insérer une nouvelle règle ci-dessous" #: lib/solaar/ui/diversion_rules.py:347 msgid "Flatten" msgstr "Aplanir" #: lib/solaar/ui/diversion_rules.py:380 msgid "Insert" msgstr "Insérer" #: lib/solaar/ui/diversion_rules.py:383 lib/solaar/ui/diversion_rules.py:511 #: lib/solaar/ui/diversion_rules.py:1158 msgid "Or" msgstr "Ou" #: lib/solaar/ui/diversion_rules.py:384 lib/solaar/ui/diversion_rules.py:510 #: lib/solaar/ui/diversion_rules.py:1144 msgid "And" msgstr "Et" #: lib/solaar/ui/diversion_rules.py:386 msgid "Condition" msgstr "Condition" #: lib/solaar/ui/diversion_rules.py:388 lib/solaar/ui/rule_conditions.py:154 msgid "Feature" msgstr "Fonctionnalité" #: lib/solaar/ui/diversion_rules.py:389 lib/solaar/ui/rule_conditions.py:188 msgid "Report" msgstr "Rapport" #: lib/solaar/ui/diversion_rules.py:390 lib/solaar/ui/rule_conditions.py:69 msgid "Process" msgstr "Processus" #: lib/solaar/ui/diversion_rules.py:391 msgid "Mouse process" msgstr "Processus de la souris" #: lib/solaar/ui/diversion_rules.py:392 lib/solaar/ui/rule_conditions.py:225 msgid "Modifiers" msgstr "Modificateurs" #: lib/solaar/ui/diversion_rules.py:393 lib/solaar/ui/rule_conditions.py:277 msgid "Key" msgstr "Touche" #: lib/solaar/ui/diversion_rules.py:394 lib/solaar/ui/rule_conditions.py:318 msgid "KeyIsDown" msgstr "" #: lib/solaar/ui/diversion_rules.py:395 lib/solaar/ui/diversion_rules.py:1432 msgid "Active" msgstr "Actif" #: lib/solaar/ui/diversion_rules.py:396 lib/solaar/ui/diversion_rules.py:1390 #: lib/solaar/ui/diversion_rules.py:1441 lib/solaar/ui/diversion_rules.py:1487 msgid "Device" msgstr "Périphérique" #: lib/solaar/ui/diversion_rules.py:397 lib/solaar/ui/diversion_rules.py:1467 msgid "Host" msgstr "Hôte" #: lib/solaar/ui/diversion_rules.py:398 lib/solaar/ui/diversion_rules.py:1506 msgid "Setting" msgstr "Réglage" #: lib/solaar/ui/diversion_rules.py:399 lib/solaar/ui/rule_conditions.py:333 #: lib/solaar/ui/rule_conditions.py:382 msgid "Test" msgstr "Test" #: lib/solaar/ui/diversion_rules.py:400 lib/solaar/ui/rule_conditions.py:507 msgid "Test bytes" msgstr "Octets de test" #: lib/solaar/ui/diversion_rules.py:401 lib/solaar/ui/rule_conditions.py:608 msgid "Mouse Gesture" msgstr "Geste à la souris" #: lib/solaar/ui/diversion_rules.py:405 msgid "Action" msgstr "Action" #: lib/solaar/ui/diversion_rules.py:407 lib/solaar/ui/rule_actions.py:138 msgid "Key press" msgstr "Appui de touche" #: lib/solaar/ui/diversion_rules.py:408 lib/solaar/ui/rule_actions.py:191 msgid "Mouse scroll" msgstr "Défilement de la souris" #: lib/solaar/ui/diversion_rules.py:409 lib/solaar/ui/rule_actions.py:256 msgid "Mouse click" msgstr "Clic de la souris" #: lib/solaar/ui/diversion_rules.py:410 msgid "Set" msgstr "Définir" #: lib/solaar/ui/diversion_rules.py:411 lib/solaar/ui/rule_actions.py:328 msgid "Execute" msgstr "Exécuter" #: lib/solaar/ui/diversion_rules.py:412 lib/solaar/ui/diversion_rules.py:1189 msgid "Later" msgstr "Plus tard" #: lib/solaar/ui/diversion_rules.py:441 msgid "Insert new rule" msgstr "Insérer nouvelle règle" #: lib/solaar/ui/diversion_rules.py:461 lib/solaar/ui/rule_actions.py:82 #: lib/solaar/ui/rule_actions.py:287 lib/solaar/ui/rule_conditions.py:556 msgid "Delete" msgstr "Effacer" #: lib/solaar/ui/diversion_rules.py:483 msgid "Negate" msgstr "Nier" #: lib/solaar/ui/diversion_rules.py:507 msgid "Wrap with" msgstr "Entoure" #: lib/solaar/ui/diversion_rules.py:537 msgid "Cut" msgstr "Couper" #: lib/solaar/ui/diversion_rules.py:553 msgid "Paste" msgstr "Coller" #: lib/solaar/ui/diversion_rules.py:559 msgid "Copy" msgstr "Copier" #: lib/solaar/ui/diversion_rules.py:568 msgid "Solaar Rule Editor" msgstr "Éditeur de règle Solaar" #: lib/solaar/ui/diversion_rules.py:668 msgid "Save changes" msgstr "Sauvegarder les modifications" #: lib/solaar/ui/diversion_rules.py:673 msgid "Discard changes" msgstr "Annuler les modifications" #: lib/solaar/ui/diversion_rules.py:1104 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:1169 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:1207 msgid "Not" msgstr "Non" #: lib/solaar/ui/diversion_rules.py:1238 msgid "Toggle" msgstr "Basculer" #: lib/solaar/ui/diversion_rules.py:1239 msgid "True" msgstr "Vrai" #: lib/solaar/ui/diversion_rules.py:1240 msgid "False" msgstr "Faux" #: lib/solaar/ui/diversion_rules.py:1253 msgid "Unsupported setting" msgstr "Réglage non pris en charge" #: lib/solaar/ui/diversion_rules.py:1396 lib/solaar/ui/diversion_rules.py:1415 #: lib/solaar/ui/diversion_rules.py:1493 lib/solaar/ui/diversion_rules.py:1748 #: lib/solaar/ui/diversion_rules.py:1766 msgid "Originating device" msgstr "Périphérique d'origine" #: lib/solaar/ui/diversion_rules.py:1428 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:1437 msgid "Device that originated the current notification." msgstr "Périphérique à l’origine de la notification actuelle." #: lib/solaar/ui/diversion_rules.py:1450 msgid "Name of host computer." msgstr "Nom de l'ordinateur hôte." #: lib/solaar/ui/diversion_rules.py:1520 msgid "Value" msgstr "Valeur" #: lib/solaar/ui/diversion_rules.py:1529 msgid "Item" msgstr "Item" #: lib/solaar/ui/diversion_rules.py:1808 msgid "Change setting on device" msgstr "Modifier le réglage sur le périphérique" #: lib/solaar/ui/diversion_rules.py:1824 msgid "Setting on device" msgstr "Réglage sur le périphérique" #: lib/solaar/ui/pair_window.py:44 lib/solaar/ui/pair_window.py:167 #, 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:46 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:48 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:51 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:53 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:55 msgid "For most devices, turn on the device you want to pair." msgstr "" "Pour la plupart des périphériques, allumez le périphérique que vous " "souhaitez jumeler." #: lib/solaar/ui/pair_window.py:56 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:58 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:61 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 "" "Pour les périphériques disposant de plusieurs canaux, appuyez sur le bouton " "du canal que vous souhaitez appairer, maintenez-le enfoncé, puis relâchez-" "le.\n" "\n" "Ou utilisez le bouton de changement de canal pour sélectionner un canal, " "puis appuyez sur le bouton de changement de canal, maintenez-le enfoncé, " "puis relâchez-le." #: lib/solaar/ui/pair_window.py:68 msgid "The channel indicator light should be blinking rapidly." msgstr "Le voyant indicateur du canal doit clignoter rapidement." #: lib/solaar/ui/pair_window.py:72 #, 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:78 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:168 #, python-format msgid "Enter passcode on %(name)s." msgstr "Entrer le mot de passe sur %(name)s." #: lib/solaar/ui/pair_window.py:171 #, 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:176 msgid "left" msgstr "gauche" #: lib/solaar/ui/pair_window.py:176 msgid "right" msgstr "droit" #: lib/solaar/ui/pair_window.py:178 #, 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:221 msgid "The wireless link is not encrypted" msgstr "La connexion sans fil n'est pas chiffrée" #: lib/solaar/ui/pair_window.py:226 msgid "Found a new device:" msgstr "Nouveau périphérique disponible :" #: lib/solaar/ui/pair_window.py:247 msgid "Pairing failed" msgstr "Le jumelage a échoué" #: lib/solaar/ui/pair_window.py:249 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:251 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:253 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:255 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:54 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:60 msgid "Add key" msgstr "Ajouter une touche" #: lib/solaar/ui/rule_actions.py:63 msgid "Click" msgstr "Clic" #: lib/solaar/ui/rule_actions.py:66 msgid "Depress" msgstr "Appuyer" #: lib/solaar/ui/rule_actions.py:69 msgid "Release" msgstr "Relâcher" #: lib/solaar/ui/rule_actions.py:153 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:211 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:216 msgid "Button" msgstr "Bouton" #: lib/solaar/ui/rule_actions.py:218 msgid "Action (and Count, if click)" msgstr "Action (et nombre, en cas de clic)" #: lib/solaar/ui/rule_actions.py:269 msgid "Execute a command with arguments." msgstr "Exécuter une commande avec paramètres." #: lib/solaar/ui/rule_actions.py:273 msgid "Add argument" msgstr "Ajouter un paramètre" #: lib/solaar/ui/rule_conditions.py:52 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:82 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:99 msgid "MouseProcess" msgstr "Processus de la souris" #: lib/solaar/ui/rule_conditions.py:123 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:169 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:201 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:241 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:250 msgid "Key down" msgstr "Enfoncement de touche" #: lib/solaar/ui/rule_conditions.py:253 msgid "Key up" msgstr "Relâchement de touche" #: lib/solaar/ui/rule_conditions.py:293 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:331 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:335 msgid "Parameter" msgstr "Paramètre" #: lib/solaar/ui/rule_conditions.py:408 msgid "begin (inclusive)" msgstr "début (inclusif)" #: lib/solaar/ui/rule_conditions.py:409 msgid "end (exclusive)" msgstr "fin (exclusif)" #: lib/solaar/ui/rule_conditions.py:417 msgid "range" msgstr "plage" #: lib/solaar/ui/rule_conditions.py:420 msgid "minimum" msgstr "minimum" #: lib/solaar/ui/rule_conditions.py:421 msgid "maximum" msgstr "maximum" #: lib/solaar/ui/rule_conditions.py:423 #, 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:426 lib/solaar/ui/rule_conditions.py:427 msgid "mask" msgstr "masque" #: lib/solaar/ui/rule_conditions.py:428 #, 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:437 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:447 msgid "type" msgstr "type" #: lib/solaar/ui/rule_conditions.py:535 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:541 msgid "Add movement" msgstr "Ajouter un mouvement" #: lib/solaar/ui/tray.py:55 msgid "No supported device found" msgstr "Aucun périphérique pris en charge trouvé" #: lib/solaar/ui/tray.py:60 lib/solaar/ui/window.py:328 #, python-format msgid "About %s" msgstr "À propos de %s" #: lib/solaar/ui/tray.py:61 lib/solaar/ui/window.py:326 #, python-format msgid "Quit %s" msgstr "Quitter %s" #: lib/solaar/ui/tray.py:273 lib/solaar/ui/tray.py:281 msgid "no receiver" msgstr "aucun récepteur" #: lib/solaar/ui/tray.py:294 lib/solaar/ui/tray.py:299 #: lib/solaar/ui/window.py:728 msgid "offline" msgstr "non connecté" #: lib/solaar/ui/tray.py:297 msgid "no status" msgstr "aucun statut" #: lib/solaar/ui/window.py:110 msgid "Scanning" msgstr "Balayage" #: lib/solaar/ui/window.py:141 msgid "Battery" msgstr "Batterie" #: lib/solaar/ui/window.py:144 msgid "Wireless Link" msgstr "Connexion sans fil" #: lib/solaar/ui/window.py:148 msgid "Lighting" msgstr "Éclairage" #: lib/solaar/ui/window.py:182 msgid "Show Technical Details" msgstr "Afficher les détails techniques" #: lib/solaar/ui/window.py:198 msgid "Pair new device" msgstr "Jumeler un nouveau périphérique" #: lib/solaar/ui/window.py:216 msgid "Select a device" msgstr "Sélectionner un périphérique" #: lib/solaar/ui/window.py:331 msgid "Rule Editor" msgstr "Éditeur de règles" #: lib/solaar/ui/window.py:522 msgid "Path" msgstr "Chemin" #: lib/solaar/ui/window.py:524 msgid "USB ID" msgstr "ID USB" #: lib/solaar/ui/window.py:527 lib/solaar/ui/window.py:529 #: lib/solaar/ui/window.py:544 lib/solaar/ui/window.py:546 msgid "Serial" msgstr "Numéro de série" #: lib/solaar/ui/window.py:533 msgid "Index" msgstr "Index" #: lib/solaar/ui/window.py:535 msgid "Wireless PID" msgstr "PID sans fil" #: lib/solaar/ui/window.py:537 msgid "Product ID" msgstr "Identifiant produit" #: lib/solaar/ui/window.py:539 msgid "Protocol" msgstr "Protocole" #: lib/solaar/ui/window.py:539 msgid "Unknown" msgstr "Inconnu" #: lib/solaar/ui/window.py:541 msgid "Polling rate" msgstr "Taux de scrutation" #: lib/solaar/ui/window.py:548 msgid "Unit ID" msgstr "ID d'unité" #: lib/solaar/ui/window.py:559 msgid "none" msgstr "aucun" #: lib/solaar/ui/window.py:560 msgid "Notifications" msgstr "Notifications" #: lib/solaar/ui/window.py:604 msgid "No device paired." msgstr "Aucun périphérique jumelé." #: lib/solaar/ui/window.py:613 #, 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:620 msgid "Only one device can be paired to this receiver." msgstr "Un seul périphérique peut être jumelé à ce récepteur." #: lib/solaar/ui/window.py:624 #, 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:681 msgid "Battery Voltage" msgstr "Tension de la batterie" #: lib/solaar/ui/window.py:683 msgid "Voltage reported by battery" msgstr "Tension indiquée par la batterie" #: lib/solaar/ui/window.py:685 msgid "Battery Level" msgstr "Niveau de la batterie" #: lib/solaar/ui/window.py:687 msgid "Approximate level reported by battery" msgstr "Niveau approximatif indiqué par la batterie" #: lib/solaar/ui/window.py:694 lib/solaar/ui/window.py:696 msgid "next reported " msgstr "prochain rapport " #: lib/solaar/ui/window.py:697 msgid " and next level to be reported." msgstr " et prochain niveau à rapporter." #: lib/solaar/ui/window.py:702 msgid "last known" msgstr "dernière valeur connue" #: lib/solaar/ui/window.py:713 msgid "encrypted" msgstr "chiffrée" #: lib/solaar/ui/window.py:715 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:717 msgid "not encrypted" msgstr "non chiffrée" #: lib/solaar/ui/window.py:721 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:737 #, 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 "Count and Action" #~ msgstr "Nombre et action" #~ 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 "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 "Turn on the device you want to pair." #~ msgstr "Allumez le périphérique que vous souhaitez jumeler." #~ 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." ================================================ FILE: po/hr.po ================================================ # 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" ================================================ FILE: po/id.po ================================================ # 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" ================================================ FILE: po/it.po ================================================ # 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" ================================================ FILE: po/ja.po ================================================ # 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 "幅" ================================================ FILE: po/ka.po ================================================ # translation of solaar to Georgian # Copyright (C) 2026 solaar's authors # This file is distributed under the same license as the solaar package. # Ekaterine Papava , 2026. # msgid "" msgstr "" "Project-Id-Version: solaar 1.1.17rc3\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-12-01 21:43+0300\n" "PO-Revision-Date: 2026-01-07 16:55+0100\n" "Last-Translator: Ekaterine Papava \n" "Language-Team: Georgian <(nothing)>\n" "Language: ka\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.8\n" #: lib/logitech_receiver/base_usb.py:52 msgid "Bolt Receiver" msgstr "Bolt მიმღები" #: lib/logitech_receiver/base_usb.py:64 msgid "Unifying Receiver" msgstr "Unifying მიმღები" #: lib/logitech_receiver/base_usb.py:75 lib/logitech_receiver/base_usb.py:87 #: lib/logitech_receiver/base_usb.py:100 lib/logitech_receiver/base_usb.py:113 msgid "Nano Receiver" msgstr "Nano მიმღები" #: lib/logitech_receiver/base_usb.py:125 msgid "Lightspeed Receiver" msgstr "Lightspeed მიმღები" #: lib/logitech_receiver/base_usb.py:135 msgid "EX100 Receiver 27 Mhz" msgstr "EX100 მიმღები 27 მჰც" #: lib/logitech_receiver/common.py:649 #, python-format msgid "Battery: %(level)s (%(status)s)" msgstr "ელემენტი: %(level)s (%(status)s)" #: lib/logitech_receiver/common.py:652 #, python-format msgid "Battery: %(percent)d%% (%(status)s)" msgstr "ელემენტი: %(percent)d%% (%(status)s)" #: lib/logitech_receiver/hidpp20.py:1048 #: lib/logitech_receiver/settings_templates.py:299 msgid "Disabled" msgstr "გამორთულია" #: lib/logitech_receiver/hidpp20.py:1049 msgid "Static" msgstr "სტატიკური" #: lib/logitech_receiver/hidpp20.py:1050 msgid "Pulse" msgstr "პულსი" #: lib/logitech_receiver/hidpp20.py:1051 msgid "Cycle" msgstr "ციკლი" #: lib/logitech_receiver/hidpp20.py:1052 msgid "Boot" msgstr "ჩატვირთვა" #: lib/logitech_receiver/hidpp20.py:1053 msgid "Demo" msgstr "დემო" #: lib/logitech_receiver/hidpp20.py:1055 msgid "Breathe" msgstr "სუნთქვა" #: lib/logitech_receiver/hidpp20.py:1058 msgid "Ripple" msgstr "ჭავლი" #: lib/logitech_receiver/hidpp20.py:1059 msgid "Decomposition" msgstr "დაშლა" #: lib/logitech_receiver/hidpp20.py:1060 msgid "Signature1" msgstr "ხელმოწერა1" #: lib/logitech_receiver/hidpp20.py:1061 msgid "Signature2" msgstr "ხელმოწერა2" #: lib/logitech_receiver/hidpp20.py:1062 msgid "CycleS" msgstr "ციკლები" #: lib/logitech_receiver/hidpp20.py:1126 msgid "Unknown Location" msgstr "უცნობი მდებარეობა" #: lib/logitech_receiver/hidpp20.py:1127 msgid "Primary" msgstr "ძირითადი" #: lib/logitech_receiver/hidpp20.py:1128 msgid "Logo" msgstr "ლოგო" #: lib/logitech_receiver/hidpp20.py:1129 msgid "Left Side" msgstr "მარცხენა მხარე" #: lib/logitech_receiver/hidpp20.py:1130 msgid "Right Side" msgstr "მარჯვენა მხარე" #: lib/logitech_receiver/hidpp20.py:1131 msgid "Combined" msgstr "კომბინირებული" #: lib/logitech_receiver/hidpp20.py:1132 msgid "Primary 1" msgstr "ძირითადი 1" #: lib/logitech_receiver/hidpp20.py:1133 msgid "Primary 2" msgstr "ძირითადი 2" #: lib/logitech_receiver/hidpp20.py:1134 msgid "Primary 3" msgstr "ძირითადი 3" #: lib/logitech_receiver/hidpp20.py:1135 msgid "Primary 4" msgstr "ძირითადი 4" #: lib/logitech_receiver/hidpp20.py:1136 msgid "Primary 5" msgstr "ძირითადი 5" #: lib/logitech_receiver/hidpp20.py:1137 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 lib/solaar/ui/window.py:700 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 lib/solaar/ui/window.py:555 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:156 lib/solaar/listener.py:243 msgid "connected" msgstr "დაკავშირებულია" #: lib/logitech_receiver/notifications.py:156 lib/solaar/listener.py:243 msgid "disconnected" msgstr "გათიშულია" #: lib/logitech_receiver/notifications.py:182 msgid "unpaired" msgstr "დაუწყვილებელია" #: lib/logitech_receiver/notifications.py:231 msgid "powered on" msgstr "ჩართულია" #: lib/logitech_receiver/notifications.py:283 msgid "ADC measurement notification" msgstr "ADC გაზომვის გაფრთხილება" #: lib/logitech_receiver/notifications.py:428 #: lib/logitech_receiver/notifications.py:483 msgid "pairing lock is closed" msgstr "დაწყვილება დაბლოკილია" #: lib/logitech_receiver/notifications.py:428 #: lib/logitech_receiver/notifications.py:483 msgid "pairing lock is open" msgstr "დაწყვილება ჩართულია" #: lib/logitech_receiver/notifications.py:446 msgid "discovery lock is closed" msgstr "აღმოჩენა დაბლოკილია" #: lib/logitech_receiver/notifications.py:446 msgid "discovery lock is open" msgstr "აღმოჩენა ჩართულია" #: lib/logitech_receiver/receiver.py:437 msgid "No paired devices." msgstr "შეწყვილებული მოწყობილობების გარეშე." #: lib/logitech_receiver/receiver.py:439 lib/solaar/ui/window.py:606 #, 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:602 msgid "register" msgstr "რეგისტრაცია" #: lib/logitech_receiver/settings.py:616 lib/logitech_receiver/settings.py:654 msgid "feature" msgstr "თვისება" #: lib/logitech_receiver/settings_templates.py:138 msgid "Swap Fx function" msgstr "Fx-ის ფუნქციის გაცვლა" #: lib/logitech_receiver/settings_templates.py:141 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:146 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:154 msgid "Hand Detection" msgstr "ხელის ამოცნობა" #: lib/logitech_receiver/settings_templates.py:155 msgid "Turn on illumination when the hands hover over the keyboard." msgstr "ილუმინაციის ჩართვა, როცა ხელებს კლავიატურაზე გააჩერებთ." #: lib/logitech_receiver/settings_templates.py:162 msgid "Scroll Wheel Smooth Scrolling" msgstr "გლუვი გადახვევა თაგუნას ბორბლით" #: lib/logitech_receiver/settings_templates.py:163 #: lib/logitech_receiver/settings_templates.py:410 #: lib/logitech_receiver/settings_templates.py:439 msgid "High-sensitivity mode for vertical scroll with the wheel." msgstr "მაღალი მგრძნობელობის რეჟიმი ბორბლით ვერტიკალური გადახვევისთვის." #: lib/logitech_receiver/settings_templates.py:170 msgid "Side Scrolling" msgstr "გვერდზე გადახვევა" #: lib/logitech_receiver/settings_templates.py:172 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:182 msgid "Sensitivity (DPI - older mice)" msgstr "მგრძნობელობა (DPI - ძველი თაგუნები)" #: lib/logitech_receiver/settings_templates.py:183 #: lib/logitech_receiver/settings_templates.py:1019 #: lib/logitech_receiver/settings_templates.py:1047 msgid "Mouse movement sensitivity" msgstr "თაგუნას მოძრაობის მგრძნობელობა" #: lib/logitech_receiver/settings_templates.py:256 msgid "Backlight Timed" msgstr "შენათების დრო" #: lib/logitech_receiver/settings_templates.py:257 #: lib/logitech_receiver/settings_templates.py:397 msgid "Set illumination time for keyboard." msgstr "განათების დროის დაყენება კლავიატურისთვის." #: lib/logitech_receiver/settings_templates.py:268 msgid "Backlight" msgstr "შენათება" #: lib/logitech_receiver/settings_templates.py:269 msgid "" "Illumination level on keyboard. Changes made are only applied in Manual " "mode." msgstr "" "განათების დონე კლავიატურაზე. ცვლილებები ძალაში, მხოლოდ, 'ხელით' რეჟიმში " "შედის." #: lib/logitech_receiver/settings_templates.py:301 msgid "Automatic" msgstr "ავტომატური" #: lib/logitech_receiver/settings_templates.py:303 msgid "Manual" msgstr "ხელით" #: lib/logitech_receiver/settings_templates.py:305 msgid "Enabled" msgstr "ჩართულია" #: lib/logitech_receiver/settings_templates.py:311 msgid "Backlight Level" msgstr "შენათების დონე" #: lib/logitech_receiver/settings_templates.py:312 msgid "Illumination level on keyboard when in Manual mode." msgstr "განათების დონე კლავიატურაზე რეჟიმში 'ხელით'." #: lib/logitech_receiver/settings_templates.py:369 msgid "Backlight Delay Hands Out" msgstr "შენათების დაყოვნება ხელების გაწევისას" #: lib/logitech_receiver/settings_templates.py:370 msgid "" "Delay in seconds until backlight fades out with hands away from keyboard." msgstr "" "დაყოვნება წამებში, სანამ მოხდება შენათების მინავლება, როცა ხელებს " "კლავიატურიდან შორს წაიღებთ." #: lib/logitech_receiver/settings_templates.py:378 msgid "Backlight Delay Hands In" msgstr "შენათების დაყოვნება ხელების მიახლოებისას" #: lib/logitech_receiver/settings_templates.py:379 msgid "Delay in seconds until backlight fades out with hands near keyboard." msgstr "" "დაყოვნება წამებში, სანამ მოხდება შენათების გამოჩენა, როცა ხელებს " "კლავიატურასთან მიიტანთ." #: lib/logitech_receiver/settings_templates.py:387 msgid "Backlight Delay Powered" msgstr "შენათების ჩართვის დაყოვნება" #: lib/logitech_receiver/settings_templates.py:388 msgid "Delay in seconds until backlight fades out with external power." msgstr "დაყოვნება წამებში, სანამ მოხდება შენათების მინავლება გარე კვებით." #: lib/logitech_receiver/settings_templates.py:396 msgid "Backlight (Seconds)" msgstr "შენათება (წმ)" #: lib/logitech_receiver/settings_templates.py:408 msgid "Scroll Wheel High Resolution" msgstr "გადახვევის ბორბლის მაღალი გარჩევადობა" #: lib/logitech_receiver/settings_templates.py:412 #: lib/logitech_receiver/settings_templates.py:441 msgid "Set to ignore if scrolling is abnormally fast or slow" msgstr "დააყენეთ დაიგნორებაზე, თუ გადახვევა არანორმალურად სწრაფი, ან ნელია" #: lib/logitech_receiver/settings_templates.py:419 #: lib/logitech_receiver/settings_templates.py:450 msgid "Scroll Wheel Diversion" msgstr "გადახვევის ბორბლის ქცევა" #: lib/logitech_receiver/settings_templates.py:421 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:428 msgid "Scroll Wheel Direction" msgstr "გადახვევის ბორბლის მიმართულება" #: lib/logitech_receiver/settings_templates.py:429 msgid "Invert direction for vertical scroll with wheel." msgstr "ბორბლით ვერტიკალური გადახვევის მიმართულების ინვერსია." #: lib/logitech_receiver/settings_templates.py:437 msgid "Scroll Wheel Resolution" msgstr "გადახვევის ბორბლის გარჩევადობა" #: lib/logitech_receiver/settings_templates.py:452 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:461 msgid "Sensitivity (Pointer Speed)" msgstr "მგრძნობელობა (მაჩვენებლის სიჩქარე)" #: lib/logitech_receiver/settings_templates.py:462 msgid "Speed multiplier for mouse (256 is normal multiplier)." msgstr "სიჩქარის მამრავლი თაგუნასთვის (256 ნორმალური მამრავლია)." #: lib/logitech_receiver/settings_templates.py:472 msgid "Thumb Wheel Diversion" msgstr "ცერა თითის ბორბლის ქცევა" #: lib/logitech_receiver/settings_templates.py:474 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:483 msgid "Thumb Wheel Direction" msgstr "ცერა თითის ბორბლის მიმართულება" #: lib/logitech_receiver/settings_templates.py:484 msgid "Invert thumb wheel scroll direction." msgstr "ცერა თითის ბორბლით გადახვევის მიმართულების ინვერსია." #: lib/logitech_receiver/settings_templates.py:504 msgid "Onboard Profiles" msgstr "ჩაშენებული პროფილები" #: lib/logitech_receiver/settings_templates.py:505 msgid "" "Enable an onboard profile, which controls report rate, sensitivity, and " "button actions" msgstr "" "ჩაშენებული პროფილის ჩართვა, რომელიც მართავს ანგარიშების სიხშირეს, " "მგრძნობელობას და ღილაკის ქმედებებს" #: lib/logitech_receiver/settings_templates.py:549 #: lib/logitech_receiver/settings_templates.py:582 msgid "Report Rate" msgstr "ანგარიშის სიხშირე" #: lib/logitech_receiver/settings_templates.py:551 #: lib/logitech_receiver/settings_templates.py:584 msgid "Frequency of device movement reports" msgstr "მოწყობილობის მოძრაობის ანგარიშების სიხშირე" #: lib/logitech_receiver/settings_templates.py:551 #: lib/logitech_receiver/settings_templates.py:584 #: lib/logitech_receiver/settings_templates.py:1019 #: lib/logitech_receiver/settings_templates.py:1047 #: lib/logitech_receiver/settings_templates.py:1421 #: lib/logitech_receiver/settings_templates.py:1452 msgid "May need Onboard Profiles set to Disable to be effective." msgstr "" "იმისათვის, რომ ეფექტური იყოს, შეიძლება, ჩაშენებული პროფილების გამორთვა " "დასჭირდეს." #: lib/logitech_receiver/settings_templates.py:612 msgid "Divert crown events" msgstr "ვენიერის ქცევა" #: lib/logitech_receiver/settings_templates.py:613 msgid "" "Make crown send CROWN HID++ notifications (which trigger Solaar rules but " "are otherwise ignored)." msgstr "" "ვერნიერისთვის CROWN WHEEL HID++ გაფრთხილების გაგზავნის იძულება (რომელიც " "გაუშვებს Solaar-ის წესებს, მაგრამ სხვაგვარად ყურადღებას არაფერი აქცევს)." #: lib/logitech_receiver/settings_templates.py:621 msgid "Crown smooth scroll" msgstr "ვენიერის გლუვი გადახვევა" #: lib/logitech_receiver/settings_templates.py:622 msgid "Set crown smooth scroll" msgstr "ვენიერის გლუვი გადახვევის დაყენება" #: lib/logitech_receiver/settings_templates.py:630 msgid "Divert G and M Keys" msgstr "G და M ღილაკების გადამისამართება" #: lib/logitech_receiver/settings_templates.py:631 msgid "" "Make G and M keys send HID++ notifications (which trigger Solaar rules but " "are otherwise ignored)." msgstr "" "G და M ღილაკებისთვის WHEEL HID++ გაფრთხილების გაგზავნის იძულება (რომელიც " "გაუშვებს Solaar-ის წესებს, მაგრამ სხვაგვარად ყურადღებას არაფერი აქცევს)." #: lib/logitech_receiver/settings_templates.py:645 msgid "Scroll Wheel Ratcheted" msgstr "ხრუტუნა მექანიზმით გადახვევის ბორბალი" #: lib/logitech_receiver/settings_templates.py:646 msgid "" "Switch the mouse wheel between speed-controlled ratcheting and always " "freespin." msgstr "" "თაგუნას ბორბლის გადართვა სიჩქარით მართულ ხრუტუნა მექანიზმსა და ყოველთვის " "თავისუფალ ტრიალს შორის." #: lib/logitech_receiver/settings_templates.py:648 msgid "Freespinning" msgstr "თავისუფალი ბრუნვა" #: lib/logitech_receiver/settings_templates.py:648 msgid "Ratcheted" msgstr "ბრუნვა ხრუტუნა მექანიზმით" #: lib/logitech_receiver/settings_templates.py:655 msgid "Scroll Wheel Ratchet Speed" msgstr "თაგუნას ბორბლის ტრიალის სიჩქარე" #: lib/logitech_receiver/settings_templates.py:657 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:707 msgid "Scroll Wheel Ratchet Torque" msgstr "ხრუტუნა ბორბლით გადახვევის მოჭიდება" #: lib/logitech_receiver/settings_templates.py:708 msgid "Change the torque needed to overcome the ratchet." msgstr "ხრუტუნა ბორბლის გადასაფარად მოჭიდების შეცვლაა საწირო." #: lib/logitech_receiver/settings_templates.py:743 msgid "Key/Button Actions" msgstr "ღილაკის ქმედებები" #: lib/logitech_receiver/settings_templates.py:745 msgid "Change the action for the key or button." msgstr "ქმედების შეცვლა ღილაკისთვის." #: lib/logitech_receiver/settings_templates.py:747 msgid "Overridden by diversion." msgstr "გადაფარულია გადამისამარტებით." #: lib/logitech_receiver/settings_templates.py:749 msgid "" "Changing important actions (such as for the left mouse button) can result in " "an unusable system." msgstr "" "მნიშვნელოვანი ქმედებების შეცვლამ (მაგ: მარცხენა თაგუნას ღილაკისთვის), " "შეიძლება, სისტემის არასტაბილურობამდე მიგიყვანოთ." #: lib/logitech_receiver/settings_templates.py:924 msgid "Key/Button Diversion" msgstr "ღილაკის გადამისამართება" #: lib/logitech_receiver/settings_templates.py:925 msgid "" "Make the key or button send HID++ notifications (Diverted) or initiate Mouse " "Gestures or Sliding DPI" msgstr "" "ღილაკისთვის იძულება, HD++ გაფრთხილებები გააგზავნოს (გადამისამართებული) ან " "მოახდინოს თაგუნას ჟესტების ინიციაცია, ან სრიალა DPI-ის" #: lib/logitech_receiver/settings_templates.py:928 #: lib/logitech_receiver/settings_templates.py:929 #: lib/logitech_receiver/settings_templates.py:930 msgid "Diverted" msgstr "გადამისამართებულია" #: lib/logitech_receiver/settings_templates.py:928 #: lib/logitech_receiver/settings_templates.py:929 msgid "Mouse Gestures" msgstr "თაგუნას ჟესტები" #: lib/logitech_receiver/settings_templates.py:928 #: lib/logitech_receiver/settings_templates.py:929 #: lib/logitech_receiver/settings_templates.py:930 msgid "Regular" msgstr "ჩვეულებრივი" #: lib/logitech_receiver/settings_templates.py:928 msgid "Sliding DPI" msgstr "მცურავი DPI" #: lib/logitech_receiver/settings_templates.py:1018 #: lib/logitech_receiver/settings_templates.py:1046 msgid "Sensitivity (DPI)" msgstr "მგრძნობელობა (DPI)" #: lib/logitech_receiver/settings_templates.py:1123 msgid "Sensitivity Switching" msgstr "მგრძნობელობის გადართვა" #: lib/logitech_receiver/settings_templates.py:1125 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:1129 msgid "Off" msgstr "გამორთ" #: lib/logitech_receiver/settings_templates.py:1160 msgid "Disable keys" msgstr "ღილაკების გათიშვა" #: lib/logitech_receiver/settings_templates.py:1161 msgid "Disable specific keyboard keys." msgstr "მითითებული კლავიატურის ღილაკების გამორთვა." #: lib/logitech_receiver/settings_templates.py:1164 #, python-format msgid "Disables the %s key." msgstr "გამორთავს ღილაკს %s." #: lib/logitech_receiver/settings_templates.py:1177 #: lib/logitech_receiver/settings_templates.py:1234 msgid "Set OS" msgstr "ოპერაციული სისტემა" #: lib/logitech_receiver/settings_templates.py:1178 #: lib/logitech_receiver/settings_templates.py:1235 msgid "Change keys to match OS." msgstr "ღილაკების შეცვლა ოს-თან შესაბამისად." #: lib/logitech_receiver/settings_templates.py:1247 msgid "Change Host" msgstr "ჰოსტის შეცვლა" #: lib/logitech_receiver/settings_templates.py:1248 msgid "Switch connection to a different host" msgstr "კავშირის გადართვა სხვა ჰოსტზე" #: lib/logitech_receiver/settings_templates.py:1272 msgid "Performs a left click." msgstr "შეასრულებს მარცხენა წკაპს." #: lib/logitech_receiver/settings_templates.py:1272 msgid "Single tap" msgstr "ერთი ტყაპი" #: lib/logitech_receiver/settings_templates.py:1273 msgid "Performs a right click." msgstr "შეასრულებს მარჯვენა წკაპს." #: lib/logitech_receiver/settings_templates.py:1273 msgid "Single tap with two fingers" msgstr "ერთი ტყაპი ორი თითით" #: lib/logitech_receiver/settings_templates.py:1274 msgid "Single tap with three fingers" msgstr "ერთი ტყაპი სამი თითით" #: lib/logitech_receiver/settings_templates.py:1278 msgid "Double tap" msgstr "ორმაგი ტყაპი" #: lib/logitech_receiver/settings_templates.py:1278 msgid "Performs a double click." msgstr "შეასრულებს ორმაგ წკაპს." #: lib/logitech_receiver/settings_templates.py:1279 msgid "Double tap with two fingers" msgstr "ორმაგი ტყაპი ორი თითით" #: lib/logitech_receiver/settings_templates.py:1280 msgid "Double tap with three fingers" msgstr "ორმაგი ტყაპი სამი თითით" #: lib/logitech_receiver/settings_templates.py:1283 msgid "Drags items by dragging the finger after double tapping." msgstr "ელემენტების გადათრევა თითის გაწევით ორი ტყაპის შემდეგ." #: lib/logitech_receiver/settings_templates.py:1283 msgid "Tap and drag" msgstr "დატყაპუნება და გადათრევა" #: lib/logitech_receiver/settings_templates.py:1285 msgid "Tap and drag with two fingers" msgstr "დატყაპუნება და გადათრევა ორი თითით" #: lib/logitech_receiver/settings_templates.py:1286 msgid "Drags items by dragging the fingers after double tapping." msgstr "გადაათრიეთ ელემენტები თითების გადათრევით ორმაგი დატყაპუნების შემდეგ." #: lib/logitech_receiver/settings_templates.py:1288 msgid "Tap and drag with three fingers" msgstr "დატყაპუნება და გადათრევა სამი თითით" #: lib/logitech_receiver/settings_templates.py:1291 msgid "Suppress tap and edge gestures" msgstr "ტყაპუნის და წიბოს ჟესტების ჩახშობა" #: lib/logitech_receiver/settings_templates.py:1292 msgid "Disables tap and edge gestures (equivalent to pressing Fn+LeftClick)." msgstr "" "გამორთავს დატყაპუნების და წიბოს ჟესტებს (იგივეა, რაც Fn+მარცხენაწკაპი)." #: lib/logitech_receiver/settings_templates.py:1294 msgid "Scroll with one finger" msgstr "გადახვევა ერთი თითით" #: lib/logitech_receiver/settings_templates.py:1294 #: lib/logitech_receiver/settings_templates.py:1295 #: lib/logitech_receiver/settings_templates.py:1298 msgid "Scrolls." msgstr "ვერტიკალური გადახვევა." #: lib/logitech_receiver/settings_templates.py:1295 #: lib/logitech_receiver/settings_templates.py:1298 msgid "Scroll with two fingers" msgstr "გადახვევა ორი თითით" #: lib/logitech_receiver/settings_templates.py:1296 msgid "Scroll horizontally with two fingers" msgstr "ჰორიზონტალური გადახვევა ორი თითით" #: lib/logitech_receiver/settings_templates.py:1296 msgid "Scrolls horizontally." msgstr "ჰორიზონტალური გადახვევა." #: lib/logitech_receiver/settings_templates.py:1297 msgid "Scroll vertically with two fingers" msgstr "ვერტიკალური გადახვევა ორი თითით" #: lib/logitech_receiver/settings_templates.py:1297 msgid "Scrolls vertically." msgstr "ვერტიკალური გადახვევა." #: lib/logitech_receiver/settings_templates.py:1299 msgid "Inverts the scrolling direction." msgstr "გადახვევის მიმართულების ინვერსია." #: lib/logitech_receiver/settings_templates.py:1299 msgid "Natural scrolling" msgstr "ჩვეულებრივი გადახვევა" #: lib/logitech_receiver/settings_templates.py:1300 msgid "Enables the thumbwheel." msgstr "ჩართავს ცერა თითის ბორბალს." #: lib/logitech_receiver/settings_templates.py:1300 msgid "Thumbwheel" msgstr "ცერა თითის ბორბალი" #: lib/logitech_receiver/settings_templates.py:1311 #: lib/logitech_receiver/settings_templates.py:1315 msgid "Swipe from the top edge" msgstr "გასმა ზედა წიბოდან" #: lib/logitech_receiver/settings_templates.py:1312 msgid "Swipe from the left edge" msgstr "გასმა მარცხენა წიბოდან" #: lib/logitech_receiver/settings_templates.py:1313 msgid "Swipe from the right edge" msgstr "გასმა მარჯვენა წიბოდან" #: lib/logitech_receiver/settings_templates.py:1314 msgid "Swipe from the bottom edge" msgstr "გასმა ქვედა წიბოდან" #: lib/logitech_receiver/settings_templates.py:1316 msgid "Swipe two fingers from the left edge" msgstr "ორი თითის გასმა მარცხენა წიბოდან" #: lib/logitech_receiver/settings_templates.py:1317 msgid "Swipe two fingers from the right edge" msgstr "ორი თითის გასმა მარჯვენა წიბოდან" #: lib/logitech_receiver/settings_templates.py:1318 msgid "Swipe two fingers from the bottom edge" msgstr "ორი თითის გასმა ქვედა წიბოდან" #: lib/logitech_receiver/settings_templates.py:1319 msgid "Swipe two fingers from the top edge" msgstr "ორი თითის გასმა ზედა წიბოდან" #: lib/logitech_receiver/settings_templates.py:1320 #: lib/logitech_receiver/settings_templates.py:1324 msgid "Pinch to zoom out; spread to zoom in." msgstr "ჩქმეტა დასაპატარავებლად. გაწევა გასადიდებლად." #: lib/logitech_receiver/settings_templates.py:1320 msgid "Zoom with two fingers." msgstr "გადიდება ორი თითით." #: lib/logitech_receiver/settings_templates.py:1321 msgid "Pinch to zoom out." msgstr "ჩქმეტა დასაპატარავებლად." #: lib/logitech_receiver/settings_templates.py:1322 msgid "Spread to zoom in." msgstr "გადაშლა გასადიდებლად." #: lib/logitech_receiver/settings_templates.py:1323 msgid "Zoom with three fingers." msgstr "გადიდება სამი თითით." #: lib/logitech_receiver/settings_templates.py:1324 msgid "Zoom with two fingers" msgstr "გადიდება ორი თითით" #: lib/logitech_receiver/settings_templates.py:1342 msgid "Pixel zone" msgstr "პიქსელების ზონა" #: lib/logitech_receiver/settings_templates.py:1343 msgid "Ratio zone" msgstr "ფარდობის დონე" #: lib/logitech_receiver/settings_templates.py:1344 msgid "Scale factor" msgstr "გადიდების კოეფიციენტი" #: lib/logitech_receiver/settings_templates.py:1344 msgid "Sets the cursor speed." msgstr "აყენებს კურსორის სიჩქარეს." #: lib/logitech_receiver/settings_templates.py:1348 msgid "Left" msgstr "მარცხენა" #: lib/logitech_receiver/settings_templates.py:1348 msgid "Left-most coordinate." msgstr "ყველაზე მარცხენა კოორდინატი." #: lib/logitech_receiver/settings_templates.py:1349 msgid "Bottom" msgstr "ქვემოთ" #: lib/logitech_receiver/settings_templates.py:1349 msgid "Bottom coordinate." msgstr "ქვედა კოორდინატი." #: lib/logitech_receiver/settings_templates.py:1350 msgid "Width" msgstr "სიგანე" #: lib/logitech_receiver/settings_templates.py:1350 msgid "Width." msgstr "სიგანე." #: lib/logitech_receiver/settings_templates.py:1351 msgid "Height" msgstr "სიმაღლე" #: lib/logitech_receiver/settings_templates.py:1351 msgid "Height." msgstr "სიმაღლე." #: lib/logitech_receiver/settings_templates.py:1352 msgid "Cursor speed." msgstr "კურსორის სიჩქარე." #: lib/logitech_receiver/settings_templates.py:1352 msgid "Scale" msgstr "მასშტაბი" #: lib/logitech_receiver/settings_templates.py:1358 msgid "Gestures" msgstr "ჟესტები" #: lib/logitech_receiver/settings_templates.py:1359 msgid "Tweak the mouse/touchpad behaviour." msgstr "თაგუნას/თაჩპედის ქცევის შეცვლა." #: lib/logitech_receiver/settings_templates.py:1375 msgid "Gestures Diversion" msgstr "ჟესტების ქცევა" #: lib/logitech_receiver/settings_templates.py:1376 msgid "Divert mouse/touchpad gestures." msgstr "თაჩპედის/თაგუნას ჟესტების მორგება." #: lib/logitech_receiver/settings_templates.py:1392 msgid "Gesture params" msgstr "ჟესტის პარამეტრები" #: lib/logitech_receiver/settings_templates.py:1393 msgid "Change numerical parameters of a mouse/touchpad." msgstr "თაგუნას/თაჩპედის რიცხვითი პარამეტრების შეცვლა." #: lib/logitech_receiver/settings_templates.py:1417 msgid "M-Key LEDs" msgstr "M-ღილაკის LED-ები" #: lib/logitech_receiver/settings_templates.py:1419 msgid "Control the M-Key LEDs." msgstr "M-ღილაკის LED-ების მართვა." #: lib/logitech_receiver/settings_templates.py:1423 #: lib/logitech_receiver/settings_templates.py:1454 msgid "May need G Keys diverted to be effective." msgstr "" "შეიძლება, იმისათვის, რომ ეფექტური იყოს, G ღილაკების გადამისამართება " "დაგჭირდეთ." #: lib/logitech_receiver/settings_templates.py:1429 #, python-format msgid "Lights up the %s key." msgstr "აანთებს ღილაკს %s." #: lib/logitech_receiver/settings_templates.py:1448 msgid "MR-Key LED" msgstr "MR ღილაკის LED" #: lib/logitech_receiver/settings_templates.py:1450 msgid "Control the MR-Key LED." msgstr "MR ღილაკის LED-ის მართვა." #: lib/logitech_receiver/settings_templates.py:1471 msgid "Persistent Key/Button Mapping" msgstr "მუდმივი ღილაკის ასახვა" #: lib/logitech_receiver/settings_templates.py:1473 msgid "Permanently change the mapping for the key or button." msgstr "ღილაკის ასახვის მუდმივად შეცვლა." #: lib/logitech_receiver/settings_templates.py:1475 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:1532 msgid "Sidetone" msgstr "თვითმოსმენა" #: lib/logitech_receiver/settings_templates.py:1533 msgid "Set sidetone level." msgstr "თვითმოსმენის დონის დაყენება." #: lib/logitech_receiver/settings_templates.py:1542 msgid "Equalizer" msgstr "ეკვალაიზერი" #: lib/logitech_receiver/settings_templates.py:1543 msgid "Set equalizer levels." msgstr "ეკვალაიზერის დონეების დაყენება." #: lib/logitech_receiver/settings_templates.py:1565 msgid "Hz" msgstr "ჰც" #: lib/logitech_receiver/settings_templates.py:1571 msgid "Power Management" msgstr "კვების მართვა" #: lib/logitech_receiver/settings_templates.py:1572 msgid "Power off in minutes (0 for never)." msgstr "კვების გამორთვა მითითებულ წუთში (0 ნიშნავს 'არასდროს')." #: lib/logitech_receiver/settings_templates.py:1584 msgid "Brightness Control" msgstr "სიკაშკაშის კონტროლი" #: lib/logitech_receiver/settings_templates.py:1585 msgid "Control overall brightness" msgstr "საერთო სიკაშკაშის მართვა" #: lib/logitech_receiver/settings_templates.py:1628 #: lib/logitech_receiver/settings_templates.py:1681 msgid "LED Control" msgstr "LED-ების მართვა" #: lib/logitech_receiver/settings_templates.py:1629 #: lib/logitech_receiver/settings_templates.py:1682 msgid "Switch control of LED zones between device and Solaar" msgstr "LED-ის ზონების კონტროლის გადართვა მოწყობილობასა და Solaar-ს შორის" #: lib/logitech_receiver/settings_templates.py:1644 #: lib/logitech_receiver/settings_templates.py:1692 msgid "LED Zone Effects" msgstr "LED-ის ზონის ეფექტები" #: lib/logitech_receiver/settings_templates.py:1645 #: lib/logitech_receiver/settings_templates.py:1693 msgid "LED Control needs to be set to Solaar to be effective." msgstr "იმისათვის, რომ ეფექტური იყოს, LED-ის მართვა Solaar-ზე უნდა დააყენოთ." #: lib/logitech_receiver/settings_templates.py:1645 #: lib/logitech_receiver/settings_templates.py:1693 msgid "Set effect for LED Zone" msgstr "ეფექტის დაყენება LED ზონისთვის" #: lib/logitech_receiver/settings_templates.py:1647 msgid "Color" msgstr "ფერი" #: lib/logitech_receiver/settings_templates.py:1648 msgid "Speed" msgstr "სიჩქარე" #: lib/logitech_receiver/settings_templates.py:1649 msgid "Period" msgstr "პერიოდი" #: lib/logitech_receiver/settings_templates.py:1650 msgid "Intensity" msgstr "ინტენსივობა" #: lib/logitech_receiver/settings_templates.py:1651 msgid "Ramp" msgstr "პანდუსი" #: lib/logitech_receiver/settings_templates.py:1666 msgid "LEDs" msgstr "LED-ები" #: lib/logitech_receiver/settings_templates.py:1703 msgid "Per-key Lighting" msgstr "ინდივიდუალური განათება ღილაკებისთვის" #: lib/logitech_receiver/settings_templates.py:1704 msgid "Control per-key lighting." msgstr "ინდივიდუალური განათების კონტროლი ღილაკებისთვის." #: lib/logitech_receiver/settings_templates.py:1786 msgid "Force Sensing Buttons" msgstr "დაჭერის სენსორის მქონე ღილაკები" #: lib/logitech_receiver/settings_templates.py:1787 msgid "Change the force required to activate button." msgstr "აირჩიეთ ძალა, რომელიც საჭიროა ღილაკის გასააქტიურებლად." #: lib/logitech_receiver/settings_templates.py:1804 msgid "Force Sensing Button" msgstr "დაჭერის სენსორის მქონე ღილაკი" #: lib/logitech_receiver/settings_templates.py:1814 msgid "Haptic Feeback Level" msgstr "ტაქტილური უკუკავშირის დონე" #: lib/logitech_receiver/settings_templates.py:1815 msgid "Change power of haptic feedback. (Zero to turn off.)" msgstr "ტაქტილური უკუკავშირის სიმძლავრის შეცვლა. (0 გამოსართავად.)" #: lib/logitech_receiver/settings_templates.py:1857 msgid "Play Haptic Waveform" msgstr "სიგნალის ტაქტილური ფორმის დაკვრა" #: lib/logitech_receiver/settings_templates.py:1858 msgid "Tell device to play a haptic waveform." msgstr "მოწყობილობისთვის ტაქტილური ფორმის სიგნალის ბრძანების გადაცემა." #: lib/solaar/ui/__init__.py:120 msgid "Another Solaar process is already running so just expose its window" msgstr "" "უკვე გაშვებულია Solaar-ის სხვა პროცესი, ასე რომ, უბრალოდ ამოკეცეთ ის ფანჯარა" #: lib/solaar/ui/about/model.py:36 msgid "" "Manages Logitech receivers,\n" "keyboards, mice, and tablets." msgstr "" "მართავს Logitech-ის მიმღებებს,\n" "კლავიატურებს, თაგუნებს და ტაბლეტებს." #: lib/solaar/ui/about/model.py:63 msgid "Additional Programming" msgstr "დამატებითი პროგრამირება" #: lib/solaar/ui/about/model.py:64 msgid "GUI design" msgstr "GUI-ის დიზაინი" #: lib/solaar/ui/about/model.py:66 msgid "Testing" msgstr "ტესტირება" #: lib/solaar/ui/about/model.py:74 msgid "Logitech documentation" msgstr "Logitech-ის დოკუმენტაცია" #: lib/solaar/ui/action.py:88 lib/solaar/ui/action.py:92 #: lib/solaar/ui/window.py:208 msgid "Unpair" msgstr "დაწყვილების მოხსნა" #: lib/solaar/ui/action.py:91 lib/solaar/ui/diversion_rules.py:141 msgid "Cancel" msgstr "გაუქმება" #: lib/solaar/ui/common.py:42 msgid "Permissions error" msgstr "წვდომების შეცდომა" #: lib/solaar/ui/common.py:44 #, 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:46 msgid "" "If you've just installed Solaar, try disconnecting the receiver or device " "and then reconnecting it." msgstr "" "თუ Solaar ახლახან დააყენეთ, სცადეთ, გათიშოთ მიმღები, ან მოწყობილობა და " "შემდეგ თავიდან მიაერთეთ." #: lib/solaar/ui/common.py:49 msgid "Cannot connect to device error" msgstr "მოწყობილობასთან დაკავშირების შეცდომა" #: lib/solaar/ui/common.py:51 #, 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:53 msgid "" "Try disconnecting the device and then reconnecting it or turning it off and " "then on." msgstr "" "სცადეთ, გათიშოთ მოწყობილობა და თავიდან მიაერთოთ, ან გამორთეთ ის და თავიდან " "ჩართეთ." #: lib/solaar/ui/common.py:56 msgid "Unpairing failed" msgstr "დაწყვილების გაუქმება ჩავარდა" #: lib/solaar/ui/common.py:58 #, python-brace-format msgid "Failed to unpair %{device} from %{receiver}." msgstr "%{receiver}-დან %{device}-ის დაწყვილების გაუმების შეცდომა." #: lib/solaar/ui/common.py:63 msgid "The receiver returned an error, with no further details." msgstr "მიმღებმა დააბრუნა შეცდომა, მაგრამ დეტალების გარეშე." #: lib/solaar/ui/config_panel.py:245 msgid "Complete - ENTER to change" msgstr "დასრულდა - Enter შესაცვლელად" #: lib/solaar/ui/config_panel.py:245 msgid "Incomplete" msgstr "არასრული" #: lib/solaar/ui/config_panel.py:491 lib/solaar/ui/config_panel.py:543 #, python-format msgid "%d value" msgid_plural "%d values" msgstr[0] "%d მნიშვნელობა" msgstr[1] "%d მნიშვნელობა" #: lib/solaar/ui/config_panel.py:642 msgid "Changes allowed" msgstr "ცვლილებები დაშვებულია" #: lib/solaar/ui/config_panel.py:643 msgid "No changes allowed" msgstr "ცვლილებები დაშვებული არაა" #: lib/solaar/ui/config_panel.py:644 msgid "Ignore this setting" msgstr "ამ პარამეტრის გამოტოვება" #: lib/solaar/ui/config_panel.py:690 msgid "Working" msgstr "მუშაობს" #: lib/solaar/ui/config_panel.py:693 msgid "Read/write operation failed." msgstr "წაკითხვა/ჩაწერის ოპერაცია ჩავარდა." #: lib/solaar/ui/desktop_notifications.py:112 msgid "unspecified reason" msgstr "დაუზუსტებელი მიზეზი" #: lib/solaar/ui/diversion_rules.py:103 msgid "Built-in rules" msgstr "ჩაშენებული წესები" #: lib/solaar/ui/diversion_rules.py:103 msgid "User-defined rules" msgstr "მომხმარებლის აღწერილი წესები" #: lib/solaar/ui/diversion_rules.py:105 lib/solaar/ui/diversion_rules.py:1126 msgid "Rule" msgstr "წესი" #: lib/solaar/ui/diversion_rules.py:106 lib/solaar/ui/diversion_rules.py:382 #: lib/solaar/ui/diversion_rules.py:509 msgid "Sub-rule" msgstr "ქვეწესი" #: lib/solaar/ui/diversion_rules.py:108 msgid "[empty]" msgstr "[ცარიელი]" #: lib/solaar/ui/diversion_rules.py:132 msgid "Make changes permanent?" msgstr "გახდეს ცვლილებები მუდმივი?" #: lib/solaar/ui/diversion_rules.py:137 msgid "Yes" msgstr "დიახ" #: lib/solaar/ui/diversion_rules.py:139 msgid "No" msgstr "არა" #: lib/solaar/ui/diversion_rules.py:144 msgid "If you choose No, changes will be lost when Solaar is closed." msgstr "თუ აირჩევთ 'არას', ცვლილებები დაიკარგება, როცა Solaar-ს დახურავთ." #: lib/solaar/ui/diversion_rules.py:273 msgid "Paste here" msgstr "აქ ჩასმა" #: lib/solaar/ui/diversion_rules.py:275 msgid "Paste above" msgstr "ზემოთ ჩასმა" #: lib/solaar/ui/diversion_rules.py:277 msgid "Paste below" msgstr "ქვემოთ ჩასმა" #: lib/solaar/ui/diversion_rules.py:283 msgid "Paste rule here" msgstr "წესის ჩასმა აქ" #: lib/solaar/ui/diversion_rules.py:285 msgid "Paste rule above" msgstr "წესის ჩასმა ზემოთ" #: lib/solaar/ui/diversion_rules.py:287 msgid "Paste rule below" msgstr "წესის ჩასმა ქვემოთ" #: lib/solaar/ui/diversion_rules.py:291 msgid "Paste rule" msgstr "წესის ჩასმა" #: lib/solaar/ui/diversion_rules.py:306 msgid "Insert here" msgstr "აქ ჩასმა" #: lib/solaar/ui/diversion_rules.py:308 msgid "Insert above" msgstr "ზემოთ ჩასმა" #: lib/solaar/ui/diversion_rules.py:310 msgid "Insert below" msgstr "ქვემოთ ჩასმა" #: lib/solaar/ui/diversion_rules.py:316 msgid "Insert new rule here" msgstr "ახალი წესის ჩასმა აქ" #: lib/solaar/ui/diversion_rules.py:318 msgid "Insert new rule above" msgstr "ახალი წესის ჩასმა ზემოთ" #: lib/solaar/ui/diversion_rules.py:320 msgid "Insert new rule below" msgstr "ახალი წესის ჩასმა ქვემოთ" #: lib/solaar/ui/diversion_rules.py:347 msgid "Flatten" msgstr "გაბრტყელება" #: lib/solaar/ui/diversion_rules.py:380 msgid "Insert" msgstr "ჩასმა" #: lib/solaar/ui/diversion_rules.py:383 lib/solaar/ui/diversion_rules.py:511 #: lib/solaar/ui/diversion_rules.py:1158 msgid "Or" msgstr "ან" #: lib/solaar/ui/diversion_rules.py:384 lib/solaar/ui/diversion_rules.py:510 #: lib/solaar/ui/diversion_rules.py:1144 msgid "And" msgstr "და" #: lib/solaar/ui/diversion_rules.py:386 msgid "Condition" msgstr "პირობა" #: lib/solaar/ui/diversion_rules.py:388 lib/solaar/ui/rule_conditions.py:154 msgid "Feature" msgstr "თვისება" #: lib/solaar/ui/diversion_rules.py:389 lib/solaar/ui/rule_conditions.py:188 msgid "Report" msgstr "ანგარიში" #: lib/solaar/ui/diversion_rules.py:390 lib/solaar/ui/rule_conditions.py:69 msgid "Process" msgstr "პროცესი" #: lib/solaar/ui/diversion_rules.py:391 msgid "Mouse process" msgstr "თაგუნას ქმედება" #: lib/solaar/ui/diversion_rules.py:392 lib/solaar/ui/rule_conditions.py:225 msgid "Modifiers" msgstr "მოდიფიკატორები" #: lib/solaar/ui/diversion_rules.py:393 lib/solaar/ui/rule_conditions.py:277 msgid "Key" msgstr "ღილაკი" #: lib/solaar/ui/diversion_rules.py:394 lib/solaar/ui/rule_conditions.py:318 msgid "KeyIsDown" msgstr "ღილაკი დაჭერილია" #: lib/solaar/ui/diversion_rules.py:395 lib/solaar/ui/diversion_rules.py:1432 msgid "Active" msgstr "აქტიური" #: lib/solaar/ui/diversion_rules.py:396 lib/solaar/ui/diversion_rules.py:1390 #: lib/solaar/ui/diversion_rules.py:1441 lib/solaar/ui/diversion_rules.py:1487 msgid "Device" msgstr "მოწყობილობა" #: lib/solaar/ui/diversion_rules.py:397 lib/solaar/ui/diversion_rules.py:1467 msgid "Host" msgstr "ჰოსტი" #: lib/solaar/ui/diversion_rules.py:398 lib/solaar/ui/diversion_rules.py:1506 msgid "Setting" msgstr "პარამეტრი" #: lib/solaar/ui/diversion_rules.py:399 lib/solaar/ui/rule_conditions.py:333 #: lib/solaar/ui/rule_conditions.py:382 msgid "Test" msgstr "შემოწმება" #: lib/solaar/ui/diversion_rules.py:400 lib/solaar/ui/rule_conditions.py:507 msgid "Test bytes" msgstr "სატესტო ბაიტები" #: lib/solaar/ui/diversion_rules.py:401 lib/solaar/ui/rule_conditions.py:608 msgid "Mouse Gesture" msgstr "თაგუნას ჟესტი" #: lib/solaar/ui/diversion_rules.py:405 msgid "Action" msgstr "ქმედება" #: lib/solaar/ui/diversion_rules.py:407 lib/solaar/ui/rule_actions.py:138 msgid "Key press" msgstr "ღილაკის დაწოლა" #: lib/solaar/ui/diversion_rules.py:408 lib/solaar/ui/rule_actions.py:191 msgid "Mouse scroll" msgstr "გადახვევა თაგუნათი" #: lib/solaar/ui/diversion_rules.py:409 lib/solaar/ui/rule_actions.py:256 msgid "Mouse click" msgstr "თაგუნას წკაპი" #: lib/solaar/ui/diversion_rules.py:410 msgid "Set" msgstr "დაყენება" #: lib/solaar/ui/diversion_rules.py:411 lib/solaar/ui/rule_actions.py:328 msgid "Execute" msgstr "შესრულება" #: lib/solaar/ui/diversion_rules.py:412 lib/solaar/ui/diversion_rules.py:1189 msgid "Later" msgstr "მოგვიანებით" #: lib/solaar/ui/diversion_rules.py:441 msgid "Insert new rule" msgstr "ახალი წესის ჩასმა" #: lib/solaar/ui/diversion_rules.py:461 lib/solaar/ui/rule_actions.py:82 #: lib/solaar/ui/rule_actions.py:287 lib/solaar/ui/rule_conditions.py:556 msgid "Delete" msgstr "წაშლა" #: lib/solaar/ui/diversion_rules.py:483 msgid "Negate" msgstr "უარყოფა" #: lib/solaar/ui/diversion_rules.py:507 msgid "Wrap with" msgstr "ჩაკეცვა სად" #: lib/solaar/ui/diversion_rules.py:537 msgid "Cut" msgstr "ამოჭრა" #: lib/solaar/ui/diversion_rules.py:553 msgid "Paste" msgstr "ჩასმა" #: lib/solaar/ui/diversion_rules.py:559 msgid "Copy" msgstr "კოპირება" #: lib/solaar/ui/diversion_rules.py:568 msgid "Solaar Rule Editor" msgstr "Solaar-ის წესების რედაქტორი" #: lib/solaar/ui/diversion_rules.py:668 msgid "Save changes" msgstr "ცვლილებების შენახვა" #: lib/solaar/ui/diversion_rules.py:673 msgid "Discard changes" msgstr "ცვლილებების გაუქმება" #: lib/solaar/ui/diversion_rules.py:1104 msgid "This editor does not support the selected rule component yet." msgstr "ამ რედაქტორს ჯერ არ აქვს მონიშნული წესის კომპონენტის მხარდაჭერა." #: lib/solaar/ui/diversion_rules.py:1169 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:1207 msgid "Not" msgstr "არ" #: lib/solaar/ui/diversion_rules.py:1238 msgid "Toggle" msgstr "გადართვა" #: lib/solaar/ui/diversion_rules.py:1239 msgid "True" msgstr "ჭეშმარიტი" #: lib/solaar/ui/diversion_rules.py:1240 msgid "False" msgstr "მცდარი" #: lib/solaar/ui/diversion_rules.py:1253 msgid "Unsupported setting" msgstr "მხარდაუჭერელი პარამეტრი" #: lib/solaar/ui/diversion_rules.py:1396 lib/solaar/ui/diversion_rules.py:1415 #: lib/solaar/ui/diversion_rules.py:1493 lib/solaar/ui/diversion_rules.py:1748 #: lib/solaar/ui/diversion_rules.py:1766 msgid "Originating device" msgstr "წყარო მოწყობილობა" #: lib/solaar/ui/diversion_rules.py:1428 msgid "Device is active and its settings can be changed." msgstr "მოწყობილობა აქტიურია და შეგიძლიათ, მისი პარამეტრები შეცვალოთ." #: lib/solaar/ui/diversion_rules.py:1437 msgid "Device that originated the current notification." msgstr "მოწყობილობა, რომელიც მიმდინარე გაფრთხილებიდან დაიწყო." #: lib/solaar/ui/diversion_rules.py:1450 msgid "Name of host computer." msgstr "მთავარი კომპიუტერის სახელი." #: lib/solaar/ui/diversion_rules.py:1520 msgid "Value" msgstr "მნიშვნელობა" #: lib/solaar/ui/diversion_rules.py:1529 msgid "Item" msgstr "ელემენტი" #: lib/solaar/ui/diversion_rules.py:1808 msgid "Change setting on device" msgstr "პარამეტრის შეცვლა მოწყობილობაზე" #: lib/solaar/ui/diversion_rules.py:1824 msgid "Setting on device" msgstr "პარამეტრი მოწყობილობაზე" #: lib/solaar/ui/pair_window.py:44 lib/solaar/ui/pair_window.py:167 #, python-format msgid "%(receiver_name)s: pair new device" msgstr "%(receiver_name)s: ახალი მოწყობილობის დაწყვილება" #: lib/solaar/ui/pair_window.py:46 msgid "Bolt receivers are only compatible with Bolt devices." msgstr "ორივე მიმღები, მხოლოდ, Bolt მოწყობილობასთანაა თავსებადი." #: lib/solaar/ui/pair_window.py:48 msgid "Press a pairing button or key until the pairing light flashes quickly." msgstr "" "დააჭირეთ დაწყვილების ღილაკს, სანამ დაწყვილების ნათურა სწრაფად არ აციმციმდება." #: lib/solaar/ui/pair_window.py:51 msgid "Unifying receivers are only compatible with Unifying devices." msgstr "" "გაერთიანებული მიმღებები, მხოლოდ, გაერთიანებულ მოწყობილობებთანაა თავსებადი." #: lib/solaar/ui/pair_window.py:53 msgid "Other receivers are only compatible with a few devices." msgstr "სხვა მიმღებები, მხოლოდ, რამდენიმე მოწყობილობასთანაა თავსებადი." #: lib/solaar/ui/pair_window.py:55 msgid "For most devices, turn on the device you want to pair." msgstr "" "მოწყობილობების უმრავლესობისთვის, უბრალოდ ჩართეთ მოწყობილობა, რომლის " "დაწყვილებაც გნებავთ." #: lib/solaar/ui/pair_window.py:56 msgid "If the device is already turned on, turn it off and on again." msgstr "თუ მოწყობილობა უკვე ჩართულია, გამორთეთ და ისევ ჩართეთ." #: lib/solaar/ui/pair_window.py:58 msgid "The device must not be paired with a nearby powered-on receiver." msgstr "მოწყობილობა არ უნდა იყოს დაწყვილებული ახლომდებარე ჩართულ მიმღებთან." #: lib/solaar/ui/pair_window.py:61 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 "" "მრავალი არხის მქონე მოწყობილობებისთვის დააჭირეთ ღილაკს, გააჩერეთ და აუშვით " "ღილაკს იმ არხისთვის, რომლის დაწყვილებაც გსურთ\n" "ან გამოიყენეთ არხის გადამრთველი ღილაკი არხის ასარჩევად და შემდეგ დააჭირეთ " "ღილაკს და აუშვით არხის გადართვის ღილაკს." #: lib/solaar/ui/pair_window.py:68 msgid "The channel indicator light should be blinking rapidly." msgstr "არხის ინდიკატორის სინათლე სწრაფად უნდა ციმციმებდეს." #: lib/solaar/ui/pair_window.py:72 #, 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:78 msgid "" "\n" "Cancelling at this point will not use up a pairing." msgstr "" "\n" "ამ მომენტში გაუქმება დაწყვილებას არ გამოიყენებს." #: lib/solaar/ui/pair_window.py:168 #, python-format msgid "Enter passcode on %(name)s." msgstr "შეიყვანეთ პაროლი %(name)s-ზე." #: lib/solaar/ui/pair_window.py:171 #, python-format msgid "Type %(passcode)s and then press the enter key." msgstr "აკრიფეთ %(passcode)s და შემდეგ დააჭირეთ ღილაკს Enter." #: lib/solaar/ui/pair_window.py:176 msgid "left" msgstr "მარცხენა" #: lib/solaar/ui/pair_window.py:176 msgid "right" msgstr "მარჯვენა" #: lib/solaar/ui/pair_window.py:178 #, 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:221 msgid "The wireless link is not encrypted" msgstr "უსადენო კავშირი დაშიფრული არაა" #: lib/solaar/ui/pair_window.py:226 msgid "Found a new device:" msgstr "აღმოჩენილია ახალი მოწყობილობა:" #: lib/solaar/ui/pair_window.py:247 msgid "Pairing failed" msgstr "დაწყვილება ჩავარდა" #: lib/solaar/ui/pair_window.py:249 msgid "Make sure your device is within range, and has a decent battery charge." msgstr "" "დარწმუნდით, რომ თქვენი მოწყობილობა ხელმისაწვდომ დაშორებაზეა და აქვს ელემენტი " "საკმარისადაა დამუხტული." #: lib/solaar/ui/pair_window.py:251 msgid "A new device was detected, but it is not compatible with this receiver." msgstr "" "აღმოჩენილია ახალი მოწყობილობა, მაგრამ ის არ არის თავსებადი ამ მიმღებთან." #: lib/solaar/ui/pair_window.py:253 msgid "More paired devices than receiver can support." msgstr "უფრო მეტი დაწყვილებული მოწყობილობა, ვიდრე მიმღებს შეუძლია მხარდაჭერა." #: lib/solaar/ui/pair_window.py:255 msgid "No further details are available about the error." msgstr "შეცდომის შესახებ დამატებითი დეტალები ხელმისაწვდომი არაა." #: lib/solaar/ui/rule_actions.py:54 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:60 msgid "Add key" msgstr "გასაღების დამატება" #: lib/solaar/ui/rule_actions.py:63 msgid "Click" msgstr "წკაპი" #: lib/solaar/ui/rule_actions.py:66 msgid "Depress" msgstr "გაჭყლეტა" #: lib/solaar/ui/rule_actions.py:69 msgid "Release" msgstr "რელიზი" #: lib/solaar/ui/rule_actions.py:153 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:211 msgid "" "Simulate a mouse click.\n" "On Wayland requires write access to /dev/uinput." msgstr "" "თაგუნას დაწკაპუნების სიმულაცია.\n" "Wayland-ზე სჭირდება ჩაწერის უფლება სპეციალურ ფაილში /dev/uinput." #: lib/solaar/ui/rule_actions.py:216 msgid "Button" msgstr "ღილაკი" #: lib/solaar/ui/rule_actions.py:218 msgid "Action (and Count, if click)" msgstr "ქმედება (და რაოდენობა, თუ წკაპია)" #: lib/solaar/ui/rule_actions.py:269 msgid "Execute a command with arguments." msgstr "ბრძანების შესრულება არგუმენტებით." #: lib/solaar/ui/rule_actions.py:273 msgid "Add argument" msgstr "არგუმენტის დამატება" #: lib/solaar/ui/rule_conditions.py:52 msgid "X11 active process. For use in X11 only." msgstr "X11-ის აქტიური პროცესი. გამოიყენება, მხოლოდ, X11-ში." #: lib/solaar/ui/rule_conditions.py:82 msgid "X11 mouse process. For use in X11 only." msgstr "X11-ის თაგუნას პროცესი. გამოიყენება, მხოლოდ, X11-ში." #: lib/solaar/ui/rule_conditions.py:99 msgid "MouseProcess" msgstr "თაგუნას ქმედება" #: lib/solaar/ui/rule_conditions.py:123 msgid "Feature name of notification triggering rule processing." msgstr "შეტყობინების ფუნქციის სახელი, რომელიც იწვევს წესის დამუშავებას." #: lib/solaar/ui/rule_conditions.py:169 msgid "Report number of notification triggering rule processing." msgstr "წესის დამუშავების დამატრიგერებელი გაფრთხილებების რაოდენობის გადაცემა." #: lib/solaar/ui/rule_conditions.py:201 msgid "Active keyboard modifiers. Not always available in Wayland." msgstr "" "აქტიური კლავიატურის მოდიფიკატორების. Wayland-ზე ყოველთვის ხელმისაწვდომი არაა." #: lib/solaar/ui/rule_conditions.py:241 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:250 msgid "Key down" msgstr "ღილაკის დაჭერა" #: lib/solaar/ui/rule_conditions.py:253 msgid "Key up" msgstr "ღილაკის გაშვება" #: lib/solaar/ui/rule_conditions.py:293 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:331 msgid "Test condition on notification triggering rule processing." msgstr "სატესტო პირობა წესის დამუშავების გაფრთხილებაზე." #: lib/solaar/ui/rule_conditions.py:335 msgid "Parameter" msgstr "პარამეტრი" #: lib/solaar/ui/rule_conditions.py:408 msgid "begin (inclusive)" msgstr "დასაწყისი (ჩათვლით)" #: lib/solaar/ui/rule_conditions.py:409 msgid "end (exclusive)" msgstr "დასასრული (მხოლოდ)" #: lib/solaar/ui/rule_conditions.py:417 msgid "range" msgstr "შუალედი" #: lib/solaar/ui/rule_conditions.py:420 msgid "minimum" msgstr "მინიმუმი" #: lib/solaar/ui/rule_conditions.py:421 msgid "maximum" msgstr "მაქსიმუმი" #: lib/solaar/ui/rule_conditions.py:423 #, 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:426 lib/solaar/ui/rule_conditions.py:427 msgid "mask" msgstr "ნიღაბი" #: lib/solaar/ui/rule_conditions.py:428 #, 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:437 msgid "" "Bit or range test on bytes in notification message triggering rule " "processing." msgstr "" "ბიტის ან დიაპაზონის ტესტი ბაიტებზე შეტყობინებების შეტყობინების გამომწვევი " "წესის დამუშავებაში." #: lib/solaar/ui/rule_conditions.py:447 msgid "type" msgstr "ტიპი" #: lib/solaar/ui/rule_conditions.py:535 msgid "" "Mouse gesture with optional initiating button followed by zero or more mouse " "movements." msgstr "" "თაგუნას ჟესტი არასავალდებულო გამშვები ღილაკით, რომელსაც 0, ან მეტი თაგუნას " "მოძრაობა მოჰყვება." #: lib/solaar/ui/rule_conditions.py:541 msgid "Add movement" msgstr "მოძრაობის დამატება" #: lib/solaar/ui/tray.py:55 msgid "No supported device found" msgstr "მხარდაჭერილი მოწყობილობა აღმოჩენილი არაა" #: lib/solaar/ui/tray.py:60 lib/solaar/ui/window.py:328 #, python-format msgid "About %s" msgstr "%s-ის შესახებ" #: lib/solaar/ui/tray.py:61 lib/solaar/ui/window.py:326 #, python-format msgid "Quit %s" msgstr "%s-დან გასვლა" #: lib/solaar/ui/tray.py:273 lib/solaar/ui/tray.py:281 msgid "no receiver" msgstr "მიმღების გარეშე" #: lib/solaar/ui/tray.py:294 lib/solaar/ui/tray.py:299 #: lib/solaar/ui/window.py:728 msgid "offline" msgstr "გათიშული" #: lib/solaar/ui/tray.py:297 msgid "no status" msgstr "სტატუსის გარეშე" #: lib/solaar/ui/window.py:110 msgid "Scanning" msgstr "სკანირება" #: lib/solaar/ui/window.py:141 msgid "Battery" msgstr "ელემენტი" #: lib/solaar/ui/window.py:144 msgid "Wireless Link" msgstr "უსადენო კავშირი" #: lib/solaar/ui/window.py:148 msgid "Lighting" msgstr "განათება" #: lib/solaar/ui/window.py:182 msgid "Show Technical Details" msgstr "ტექნიკური დეტალების ჩვენება" #: lib/solaar/ui/window.py:198 msgid "Pair new device" msgstr "ახალი მოწყობილობის დაწყვილება" #: lib/solaar/ui/window.py:216 msgid "Select a device" msgstr "აირჩიეთ მოწყობილობა" #: lib/solaar/ui/window.py:331 msgid "Rule Editor" msgstr "წესების რედაქტორი" #: lib/solaar/ui/window.py:522 msgid "Path" msgstr "ბილიკი" #: lib/solaar/ui/window.py:524 msgid "USB ID" msgstr "USB ID" #: lib/solaar/ui/window.py:527 lib/solaar/ui/window.py:529 #: lib/solaar/ui/window.py:544 lib/solaar/ui/window.py:546 msgid "Serial" msgstr "სერიული" #: lib/solaar/ui/window.py:533 msgid "Index" msgstr "ინდექსი" #: lib/solaar/ui/window.py:535 msgid "Wireless PID" msgstr "უსადენო PID" #: lib/solaar/ui/window.py:537 msgid "Product ID" msgstr "პროდუქტის ID" #: lib/solaar/ui/window.py:539 msgid "Protocol" msgstr "პროტოკოლი" #: lib/solaar/ui/window.py:539 msgid "Unknown" msgstr "უცნობი" #: lib/solaar/ui/window.py:541 msgid "Polling rate" msgstr "გამოკითხვის სიხშირე" #: lib/solaar/ui/window.py:548 msgid "Unit ID" msgstr "ერთეულის ID" #: lib/solaar/ui/window.py:559 msgid "none" msgstr "არცერთი" #: lib/solaar/ui/window.py:560 msgid "Notifications" msgstr "შეტყობინებები" #: lib/solaar/ui/window.py:604 msgid "No device paired." msgstr "დაწყვილებული მოწყობილობების გარეშე." #: lib/solaar/ui/window.py:613 #, 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:620 msgid "Only one device can be paired to this receiver." msgstr "ამ მიმღებთან, მაქს., ერთი მოწყობილობის დაწყვილებაა შესაძლებელი." #: lib/solaar/ui/window.py:624 #, 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:681 msgid "Battery Voltage" msgstr "ელემენტის ვოლტაჟი" #: lib/solaar/ui/window.py:683 msgid "Voltage reported by battery" msgstr "ელემენტის მიერ მოწოდებული ძაბვა" #: lib/solaar/ui/window.py:685 msgid "Battery Level" msgstr "ელემენტის მუხტის დონე" #: lib/solaar/ui/window.py:687 msgid "Approximate level reported by battery" msgstr "დონის მიერ მუხტის მოწოდებული დაახლოებითი დონე" #: lib/solaar/ui/window.py:694 lib/solaar/ui/window.py:696 msgid "next reported " msgstr "შემდეგი ანგარიში " #: lib/solaar/ui/window.py:697 msgid " and next level to be reported." msgstr " და შემდეგი დონე გადასაცემად." #: lib/solaar/ui/window.py:702 msgid "last known" msgstr "ბოლო ცნობილი" #: lib/solaar/ui/window.py:713 msgid "encrypted" msgstr "დაშიფრულია" #: lib/solaar/ui/window.py:715 msgid "The wireless link between this device and its receiver is encrypted." msgstr "უსადენო კავშირი ამ მოწყობილობასა და მის მიმღებს შორის დაშიფრულია." #: lib/solaar/ui/window.py:717 msgid "not encrypted" msgstr "დაშიფრული არაა" #: lib/solaar/ui/window.py:721 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:737 #, python-format msgid "%(light_level)d lux" msgstr "%(light_level)d ლუქსი" ================================================ FILE: po/nb.po ================================================ # 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" ================================================ FILE: po/nl.po ================================================ # 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" ================================================ FILE: po/nn.po ================================================ # 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" ================================================ FILE: po/pl.po ================================================ # 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.17\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-12-01 19:29+0100\n" "PO-Revision-Date: 2025-12-01 19:46+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.8\n" #: lib/logitech_receiver/base_usb.py:52 msgid "Bolt Receiver" msgstr "Odbiornik Bolt" #: lib/logitech_receiver/base_usb.py:64 msgid "Unifying Receiver" msgstr "Odbiornik Unifying" #: lib/logitech_receiver/base_usb.py:75 lib/logitech_receiver/base_usb.py:87 #: lib/logitech_receiver/base_usb.py:100 lib/logitech_receiver/base_usb.py:113 msgid "Nano Receiver" msgstr "Odbiornik Nano" #: lib/logitech_receiver/base_usb.py:125 msgid "Lightspeed Receiver" msgstr "Odbiornik Lightspeed" #: lib/logitech_receiver/base_usb.py:135 msgid "EX100 Receiver 27 Mhz" msgstr "Odbiornik EX100 27 MHz" #: lib/logitech_receiver/common.py:649 #, python-format msgid "Battery: %(level)s (%(status)s)" msgstr "Akumulator: %(level)s (%(status)s)" #: lib/logitech_receiver/common.py:652 #, python-format msgid "Battery: %(percent)d%% (%(status)s)" msgstr "Akumulator: %(percent)d%% (%(status)s)" #: lib/logitech_receiver/hidpp20.py:1048 #: lib/logitech_receiver/settings_templates.py:299 msgid "Disabled" msgstr "Wyłączone" #: lib/logitech_receiver/hidpp20.py:1049 msgid "Static" msgstr "Statyczny" #: lib/logitech_receiver/hidpp20.py:1050 msgid "Pulse" msgstr "Puls" #: lib/logitech_receiver/hidpp20.py:1051 msgid "Cycle" msgstr "Cykl" #: lib/logitech_receiver/hidpp20.py:1052 msgid "Boot" msgstr "Rozruch" #: lib/logitech_receiver/hidpp20.py:1053 msgid "Demo" msgstr "Demo" #: lib/logitech_receiver/hidpp20.py:1055 msgid "Breathe" msgstr "Oddech" #: lib/logitech_receiver/hidpp20.py:1058 msgid "Ripple" msgstr "Plusk" #: lib/logitech_receiver/hidpp20.py:1059 msgid "Decomposition" msgstr "Dekompozycja" #: lib/logitech_receiver/hidpp20.py:1060 msgid "Signature1" msgstr "Podpis1" #: lib/logitech_receiver/hidpp20.py:1061 msgid "Signature2" msgstr "Podpis2" #: lib/logitech_receiver/hidpp20.py:1062 msgid "CycleS" msgstr "Cykle" #: lib/logitech_receiver/hidpp20.py:1126 msgid "Unknown Location" msgstr "Nieznana lokalizacja" #: lib/logitech_receiver/hidpp20.py:1127 msgid "Primary" msgstr "Podstawowy" #: lib/logitech_receiver/hidpp20.py:1128 msgid "Logo" msgstr "Logo" #: lib/logitech_receiver/hidpp20.py:1129 msgid "Left Side" msgstr "Lewa strona" #: lib/logitech_receiver/hidpp20.py:1130 msgid "Right Side" msgstr "Prawa strona" #: lib/logitech_receiver/hidpp20.py:1131 msgid "Combined" msgstr "Łączony" #: lib/logitech_receiver/hidpp20.py:1132 msgid "Primary 1" msgstr "Podstawowy 1" #: lib/logitech_receiver/hidpp20.py:1133 msgid "Primary 2" msgstr "Podstawowy 2" #: lib/logitech_receiver/hidpp20.py:1134 msgid "Primary 3" msgstr "Podstawowy 3" #: lib/logitech_receiver/hidpp20.py:1135 msgid "Primary 4" msgstr "Podstawowy 4" #: lib/logitech_receiver/hidpp20.py:1136 msgid "Primary 5" msgstr "Podstawowy 5" #: lib/logitech_receiver/hidpp20.py:1137 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:156 lib/solaar/listener.py:243 msgid "connected" msgstr "podłączone" #: lib/logitech_receiver/notifications.py:156 lib/solaar/listener.py:243 msgid "disconnected" msgstr "rozłączone" #: lib/logitech_receiver/notifications.py:182 msgid "unpaired" msgstr "niesparowane" #: lib/logitech_receiver/notifications.py:231 msgid "powered on" msgstr "włączone" #: lib/logitech_receiver/notifications.py:283 msgid "ADC measurement notification" msgstr "powiadomienie o pomiarze ADC" #: lib/logitech_receiver/notifications.py:428 #: lib/logitech_receiver/notifications.py:483 msgid "pairing lock is closed" msgstr "blokada sparowania jest zamknięta" #: lib/logitech_receiver/notifications.py:428 #: lib/logitech_receiver/notifications.py:483 msgid "pairing lock is open" msgstr "blokada sparowania jest otwarta" #: lib/logitech_receiver/notifications.py:446 msgid "discovery lock is closed" msgstr "blokada wykrywania jest zamknięta" #: lib/logitech_receiver/notifications.py:446 msgid "discovery lock is open" msgstr "blokada wykrywania jest otwarta" #: lib/logitech_receiver/receiver.py:437 msgid "No paired devices." msgstr "Brak sparowanych urządzeń." #: lib/logitech_receiver/receiver.py:439 lib/solaar/ui/window.py:606 #, 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:602 msgid "register" msgstr "rejestr" #: lib/logitech_receiver/settings.py:616 lib/logitech_receiver/settings.py:654 msgid "feature" msgstr "funkcja" #: lib/logitech_receiver/settings_templates.py:138 msgid "Swap Fx function" msgstr "Zamień funkcję Fx" #: lib/logitech_receiver/settings_templates.py:141 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:146 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:154 msgid "Hand Detection" msgstr "Wykrywanie dłoni" #: lib/logitech_receiver/settings_templates.py:155 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:162 msgid "Scroll Wheel Smooth Scrolling" msgstr "Płynne przewijanie kółkiem przewijania" #: lib/logitech_receiver/settings_templates.py:163 #: lib/logitech_receiver/settings_templates.py:410 #: lib/logitech_receiver/settings_templates.py:439 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:170 msgid "Side Scrolling" msgstr "Przewijanie na boki" #: lib/logitech_receiver/settings_templates.py:172 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:182 msgid "Sensitivity (DPI - older mice)" msgstr "Czułość (DPI - starsze myszy)" #: lib/logitech_receiver/settings_templates.py:183 #: lib/logitech_receiver/settings_templates.py:1019 #: lib/logitech_receiver/settings_templates.py:1047 msgid "Mouse movement sensitivity" msgstr "Czułość ruchu myszy" #: lib/logitech_receiver/settings_templates.py:256 msgid "Backlight Timed" msgstr "Podświetlenie czasowe" #: lib/logitech_receiver/settings_templates.py:257 #: lib/logitech_receiver/settings_templates.py:397 msgid "Set illumination time for keyboard." msgstr "Ustaw czas podświetlenia klawiatury." #: lib/logitech_receiver/settings_templates.py:268 msgid "Backlight" msgstr "Podświetlenie" #: lib/logitech_receiver/settings_templates.py:269 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:301 msgid "Automatic" msgstr "Automatyczne" #: lib/logitech_receiver/settings_templates.py:303 msgid "Manual" msgstr "Ręczne" #: lib/logitech_receiver/settings_templates.py:305 msgid "Enabled" msgstr "Włączone" #: lib/logitech_receiver/settings_templates.py:311 msgid "Backlight Level" msgstr "Poziom podświetlenia" #: lib/logitech_receiver/settings_templates.py:312 msgid "Illumination level on keyboard when in Manual mode." msgstr "Poziom podświetlenia klawiatury w trybie ręcznym." #: lib/logitech_receiver/settings_templates.py:369 msgid "Backlight Delay Hands Out" msgstr "Opóźnienie podświetlenia bez rąk" #: lib/logitech_receiver/settings_templates.py:370 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:378 msgid "Backlight Delay Hands In" msgstr "Opóźnienie podświetlenia z rękoma" #: lib/logitech_receiver/settings_templates.py:379 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:387 msgid "Backlight Delay Powered" msgstr "Opóźnienie podświetlenia z zasilanem" #: lib/logitech_receiver/settings_templates.py:388 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:396 msgid "Backlight (Seconds)" msgstr "Podświetlenie (sekundy)" #: lib/logitech_receiver/settings_templates.py:408 msgid "Scroll Wheel High Resolution" msgstr "Wysoka rozdzielczość kółka przewijania" #: lib/logitech_receiver/settings_templates.py:412 #: lib/logitech_receiver/settings_templates.py:441 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:419 #: lib/logitech_receiver/settings_templates.py:450 msgid "Scroll Wheel Diversion" msgstr "Przekierowanie kółka przewijania" #: lib/logitech_receiver/settings_templates.py:421 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:428 msgid "Scroll Wheel Direction" msgstr "Kierunek kółka przewijania" #: lib/logitech_receiver/settings_templates.py:429 msgid "Invert direction for vertical scroll with wheel." msgstr "Odwróć kierunek przewijania w pionie za pomocą kółka." #: lib/logitech_receiver/settings_templates.py:437 msgid "Scroll Wheel Resolution" msgstr "Rozdzielczość kółka przewijania" #: lib/logitech_receiver/settings_templates.py:452 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:461 msgid "Sensitivity (Pointer Speed)" msgstr "Czułość (prędkość wskaźnika)" #: lib/logitech_receiver/settings_templates.py:462 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:472 msgid "Thumb Wheel Diversion" msgstr "Przekierowanie kółka obsługiwanego kciukiem" #: lib/logitech_receiver/settings_templates.py:474 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:483 msgid "Thumb Wheel Direction" msgstr "Kierunek kółka obsługiwanego kciukiem" #: lib/logitech_receiver/settings_templates.py:484 msgid "Invert thumb wheel scroll direction." msgstr "Odwróć kierunek przewijania kółkiem obsługiwanym kciukiem." #: lib/logitech_receiver/settings_templates.py:504 msgid "Onboard Profiles" msgstr "Profile wbudowane" #: lib/logitech_receiver/settings_templates.py:505 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:549 #: lib/logitech_receiver/settings_templates.py:582 msgid "Report Rate" msgstr "Szybkość raportów" #: lib/logitech_receiver/settings_templates.py:551 #: lib/logitech_receiver/settings_templates.py:584 msgid "Frequency of device movement reports" msgstr "Częstotliwość raportów o ruchu urządzenia" #: lib/logitech_receiver/settings_templates.py:551 #: lib/logitech_receiver/settings_templates.py:584 #: lib/logitech_receiver/settings_templates.py:1019 #: lib/logitech_receiver/settings_templates.py:1047 #: lib/logitech_receiver/settings_templates.py:1421 #: lib/logitech_receiver/settings_templates.py:1452 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:612 msgid "Divert crown events" msgstr "Przekieruj zdarzenia korony" #: lib/logitech_receiver/settings_templates.py:613 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:621 msgid "Crown smooth scroll" msgstr "Płynne przewijanie koroną" #: lib/logitech_receiver/settings_templates.py:622 msgid "Set crown smooth scroll" msgstr "Ustaw płynne przewijanie koroną" #: lib/logitech_receiver/settings_templates.py:630 msgid "Divert G and M Keys" msgstr "Przekieruj klawisze G i M" #: lib/logitech_receiver/settings_templates.py:631 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:645 msgid "Scroll Wheel Ratcheted" msgstr "Zapadkowe kółko przewijania" #: lib/logitech_receiver/settings_templates.py:646 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:648 msgid "Freespinning" msgstr "Obrót swobodny" #: lib/logitech_receiver/settings_templates.py:648 msgid "Ratcheted" msgstr "Obrót zapadkowy" #: lib/logitech_receiver/settings_templates.py:655 msgid "Scroll Wheel Ratchet Speed" msgstr "Prędkość zapadki kółka przewijania" #: lib/logitech_receiver/settings_templates.py:657 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:707 msgid "Scroll Wheel Ratchet Torque" msgstr "Moment obrotowy zapadki kółka przewijania" #: lib/logitech_receiver/settings_templates.py:708 msgid "Change the torque needed to overcome the ratchet." msgstr "Zmień moment obrotowy potrzebny do przejścia zapadki." #: lib/logitech_receiver/settings_templates.py:743 msgid "Key/Button Actions" msgstr "Czynności klawiszy/przycisków" #: lib/logitech_receiver/settings_templates.py:745 msgid "Change the action for the key or button." msgstr "Zmień czynność klawisza lub przycisku." #: lib/logitech_receiver/settings_templates.py:747 msgid "Overridden by diversion." msgstr "Omijane przez przekierowanie." #: lib/logitech_receiver/settings_templates.py:749 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:924 msgid "Key/Button Diversion" msgstr "Przekierowanie klawiszy/przycisków" #: lib/logitech_receiver/settings_templates.py:925 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:928 #: lib/logitech_receiver/settings_templates.py:929 #: lib/logitech_receiver/settings_templates.py:930 msgid "Diverted" msgstr "Przekierowany" #: lib/logitech_receiver/settings_templates.py:928 #: lib/logitech_receiver/settings_templates.py:929 msgid "Mouse Gestures" msgstr "Gesty myszy" #: lib/logitech_receiver/settings_templates.py:928 #: lib/logitech_receiver/settings_templates.py:929 #: lib/logitech_receiver/settings_templates.py:930 msgid "Regular" msgstr "Zwykły" #: lib/logitech_receiver/settings_templates.py:928 msgid "Sliding DPI" msgstr "Przesuwnie DPI" #: lib/logitech_receiver/settings_templates.py:1018 #: lib/logitech_receiver/settings_templates.py:1046 msgid "Sensitivity (DPI)" msgstr "Czułość (DPI)" #: lib/logitech_receiver/settings_templates.py:1123 msgid "Sensitivity Switching" msgstr "Przełączanie czułości" #: lib/logitech_receiver/settings_templates.py:1125 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:1129 msgid "Off" msgstr "Wyłączone" #: lib/logitech_receiver/settings_templates.py:1160 msgid "Disable keys" msgstr "Wyłącz klawisze" #: lib/logitech_receiver/settings_templates.py:1161 msgid "Disable specific keyboard keys." msgstr "Wyłącz określone klawisze klawiatury." #: lib/logitech_receiver/settings_templates.py:1164 #, python-format msgid "Disables the %s key." msgstr "Wyłącza klawisz %s." #: lib/logitech_receiver/settings_templates.py:1177 #: lib/logitech_receiver/settings_templates.py:1234 msgid "Set OS" msgstr "Ustaw system operacyjny" #: lib/logitech_receiver/settings_templates.py:1178 #: lib/logitech_receiver/settings_templates.py:1235 msgid "Change keys to match OS." msgstr "Zmień klawisze, aby pasowały do systemu operacyjnego." #: lib/logitech_receiver/settings_templates.py:1247 msgid "Change Host" msgstr "Zmiana hosta" #: lib/logitech_receiver/settings_templates.py:1248 msgid "Switch connection to a different host" msgstr "Przełącz połączenie na innego hosta" #: lib/logitech_receiver/settings_templates.py:1272 msgid "Performs a left click." msgstr "Wykonuje kliknięcie lewym przyciskiem myszy." #: lib/logitech_receiver/settings_templates.py:1272 msgid "Single tap" msgstr "Pojedyncze stuknięcie" #: lib/logitech_receiver/settings_templates.py:1273 msgid "Performs a right click." msgstr "Wykonuje kliknięcie prawym przyciskiem myszy." #: lib/logitech_receiver/settings_templates.py:1273 msgid "Single tap with two fingers" msgstr "Pojedyncze stuknięcie dwoma palcami" #: lib/logitech_receiver/settings_templates.py:1274 msgid "Single tap with three fingers" msgstr "Pojedyncze stuknięcie trzema palcami" #: lib/logitech_receiver/settings_templates.py:1278 msgid "Double tap" msgstr "Podwójne stuknięcie" #: lib/logitech_receiver/settings_templates.py:1278 msgid "Performs a double click." msgstr "Wykonuje podwójne kliknięcie." #: lib/logitech_receiver/settings_templates.py:1279 msgid "Double tap with two fingers" msgstr "Podwójne stuknięcie dwoma palcami" #: lib/logitech_receiver/settings_templates.py:1280 msgid "Double tap with three fingers" msgstr "Podwójne stuknięcie trzema palcami" #: lib/logitech_receiver/settings_templates.py:1283 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:1283 msgid "Tap and drag" msgstr "Stuknięcie i przeciągnięcie" #: lib/logitech_receiver/settings_templates.py:1285 msgid "Tap and drag with two fingers" msgstr "Stuknięcie i przeciągnięcie dwoma palcami" #: lib/logitech_receiver/settings_templates.py:1286 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:1288 msgid "Tap and drag with three fingers" msgstr "Stuknięcie i przeciągnięcie trzema palcami" #: lib/logitech_receiver/settings_templates.py:1291 msgid "Suppress tap and edge gestures" msgstr "Stłumienie gestów stukania i krawędziowych" #: lib/logitech_receiver/settings_templates.py:1292 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:1294 msgid "Scroll with one finger" msgstr "Przewijanie jednym palcem" #: lib/logitech_receiver/settings_templates.py:1294 #: lib/logitech_receiver/settings_templates.py:1295 #: lib/logitech_receiver/settings_templates.py:1298 msgid "Scrolls." msgstr "Przewija." #: lib/logitech_receiver/settings_templates.py:1295 #: lib/logitech_receiver/settings_templates.py:1298 msgid "Scroll with two fingers" msgstr "Przewijanie dwoma palcami" #: lib/logitech_receiver/settings_templates.py:1296 msgid "Scroll horizontally with two fingers" msgstr "Przewijanie w poziomie dwoma palcami" #: lib/logitech_receiver/settings_templates.py:1296 msgid "Scrolls horizontally." msgstr "Przewija w poziomie." #: lib/logitech_receiver/settings_templates.py:1297 msgid "Scroll vertically with two fingers" msgstr "Przewijanie w pionie dwoma palcami" #: lib/logitech_receiver/settings_templates.py:1297 msgid "Scrolls vertically." msgstr "Przewija w pionie." #: lib/logitech_receiver/settings_templates.py:1299 msgid "Inverts the scrolling direction." msgstr "Odwraca kierunek przewijania." #: lib/logitech_receiver/settings_templates.py:1299 msgid "Natural scrolling" msgstr "Przewijanie naturalne" #: lib/logitech_receiver/settings_templates.py:1300 msgid "Enables the thumbwheel." msgstr "Włącza kółko obsługiwane kciukiem." #: lib/logitech_receiver/settings_templates.py:1300 msgid "Thumbwheel" msgstr "Kółko obsługiwane kciukiem" #: lib/logitech_receiver/settings_templates.py:1311 #: lib/logitech_receiver/settings_templates.py:1315 msgid "Swipe from the top edge" msgstr "Przesuwanie od górnej krawędzi" #: lib/logitech_receiver/settings_templates.py:1312 msgid "Swipe from the left edge" msgstr "Przesuwanie od lewej krawędzi" #: lib/logitech_receiver/settings_templates.py:1313 msgid "Swipe from the right edge" msgstr "Przesuwanie od prawej krawędzi" #: lib/logitech_receiver/settings_templates.py:1314 msgid "Swipe from the bottom edge" msgstr "Przesuwanie od dolnej krawędzi" #: lib/logitech_receiver/settings_templates.py:1316 msgid "Swipe two fingers from the left edge" msgstr "Przesuwanie dwoma palcami od lewej krawędzi" #: lib/logitech_receiver/settings_templates.py:1317 msgid "Swipe two fingers from the right edge" msgstr "Przesuwanie dwoma palcami od prawej krawędzi" #: lib/logitech_receiver/settings_templates.py:1318 msgid "Swipe two fingers from the bottom edge" msgstr "Przesuwanie dwoma palcami od dolnej krawędzi" #: lib/logitech_receiver/settings_templates.py:1319 msgid "Swipe two fingers from the top edge" msgstr "Przesuwanie dwoma palcami od górnej krawędzi" #: lib/logitech_receiver/settings_templates.py:1320 #: lib/logitech_receiver/settings_templates.py:1324 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:1320 msgid "Zoom with two fingers." msgstr "Zoom dwoma palcami." #: lib/logitech_receiver/settings_templates.py:1321 msgid "Pinch to zoom out." msgstr "Ściśnięcie, aby pomniejszyć." #: lib/logitech_receiver/settings_templates.py:1322 msgid "Spread to zoom in." msgstr "Rozłożenie, aby powiększyć." #: lib/logitech_receiver/settings_templates.py:1323 msgid "Zoom with three fingers." msgstr "Zoom trzema palcami." #: lib/logitech_receiver/settings_templates.py:1324 msgid "Zoom with two fingers" msgstr "Zoom dwoma palcami" #: lib/logitech_receiver/settings_templates.py:1342 msgid "Pixel zone" msgstr "Strefa pikseli" #: lib/logitech_receiver/settings_templates.py:1343 msgid "Ratio zone" msgstr "Strefa współczynnika" #: lib/logitech_receiver/settings_templates.py:1344 msgid "Scale factor" msgstr "Współczynnik skalowania" #: lib/logitech_receiver/settings_templates.py:1344 msgid "Sets the cursor speed." msgstr "Ustawia szybkość kursora." #: lib/logitech_receiver/settings_templates.py:1348 msgid "Left" msgstr "Lewo" #: lib/logitech_receiver/settings_templates.py:1348 msgid "Left-most coordinate." msgstr "Współrzędna najbardziej z lewej." #: lib/logitech_receiver/settings_templates.py:1349 msgid "Bottom" msgstr "Dół" #: lib/logitech_receiver/settings_templates.py:1349 msgid "Bottom coordinate." msgstr "Współrzędna dolna." #: lib/logitech_receiver/settings_templates.py:1350 msgid "Width" msgstr "Szerokość" #: lib/logitech_receiver/settings_templates.py:1350 msgid "Width." msgstr "Szerokość." #: lib/logitech_receiver/settings_templates.py:1351 msgid "Height" msgstr "Wysokość" #: lib/logitech_receiver/settings_templates.py:1351 msgid "Height." msgstr "Wysokość." #: lib/logitech_receiver/settings_templates.py:1352 msgid "Cursor speed." msgstr "Prędkość kursora." #: lib/logitech_receiver/settings_templates.py:1352 msgid "Scale" msgstr "Skala" #: lib/logitech_receiver/settings_templates.py:1358 msgid "Gestures" msgstr "Gesty" #: lib/logitech_receiver/settings_templates.py:1359 msgid "Tweak the mouse/touchpad behaviour." msgstr "Dostosuj zachowania myszy/panelu dotykowego." #: lib/logitech_receiver/settings_templates.py:1375 msgid "Gestures Diversion" msgstr "Przekierowanie gestów" #: lib/logitech_receiver/settings_templates.py:1376 msgid "Divert mouse/touchpad gestures." msgstr "Przekieruj gesty myszy/panelu dotykowego." #: lib/logitech_receiver/settings_templates.py:1392 msgid "Gesture params" msgstr "Parametry gestu" #: lib/logitech_receiver/settings_templates.py:1393 msgid "Change numerical parameters of a mouse/touchpad." msgstr "Zmień parametry numeryczne myszy/panelu dotykowego." #: lib/logitech_receiver/settings_templates.py:1417 msgid "M-Key LEDs" msgstr "Diody LED klawiszy M" #: lib/logitech_receiver/settings_templates.py:1419 msgid "Control the M-Key LEDs." msgstr "Kontroluj diody LED klawiszy M." #: lib/logitech_receiver/settings_templates.py:1423 #: lib/logitech_receiver/settings_templates.py:1454 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:1429 #, python-format msgid "Lights up the %s key." msgstr "Zapala klawisz %s." #: lib/logitech_receiver/settings_templates.py:1448 msgid "MR-Key LED" msgstr "Dioda LED klawisza MR" #: lib/logitech_receiver/settings_templates.py:1450 msgid "Control the MR-Key LED." msgstr "Kontroluj diody LED klawiszy MR." #: lib/logitech_receiver/settings_templates.py:1471 msgid "Persistent Key/Button Mapping" msgstr "Trwałe mapowanie klawiszy/przycisków" #: lib/logitech_receiver/settings_templates.py:1473 msgid "Permanently change the mapping for the key or button." msgstr "Trwale zmień mapowania klawisza lub przycisku." #: lib/logitech_receiver/settings_templates.py:1475 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:1532 msgid "Sidetone" msgstr "Efekt lokalny" #: lib/logitech_receiver/settings_templates.py:1533 msgid "Set sidetone level." msgstr "Ustaw poziom efektu lokalnego." #: lib/logitech_receiver/settings_templates.py:1542 msgid "Equalizer" msgstr "Korektor" #: lib/logitech_receiver/settings_templates.py:1543 msgid "Set equalizer levels." msgstr "Ustaw poziomy korektora." #: lib/logitech_receiver/settings_templates.py:1565 msgid "Hz" msgstr "Hz" #: lib/logitech_receiver/settings_templates.py:1571 msgid "Power Management" msgstr "Zarządzanie energią" #: lib/logitech_receiver/settings_templates.py:1572 msgid "Power off in minutes (0 for never)." msgstr "Wyłączenie zasilania w minutach (0 oznacza nigdy)." #: lib/logitech_receiver/settings_templates.py:1584 msgid "Brightness Control" msgstr "Sterowanie jasnością" #: lib/logitech_receiver/settings_templates.py:1585 msgid "Control overall brightness" msgstr "Steruj ogólną jasnością" #: lib/logitech_receiver/settings_templates.py:1628 #: lib/logitech_receiver/settings_templates.py:1681 msgid "LED Control" msgstr "Sterowanie diodami LED" #: lib/logitech_receiver/settings_templates.py:1629 #: lib/logitech_receiver/settings_templates.py:1682 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:1644 #: lib/logitech_receiver/settings_templates.py:1692 msgid "LED Zone Effects" msgstr "Efekty strefowe diod LED" #: lib/logitech_receiver/settings_templates.py:1645 #: lib/logitech_receiver/settings_templates.py:1693 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:1645 #: lib/logitech_receiver/settings_templates.py:1693 msgid "Set effect for LED Zone" msgstr "Ustaw efekt dla strefy diod LED" #: lib/logitech_receiver/settings_templates.py:1647 msgid "Color" msgstr "Kolor" #: lib/logitech_receiver/settings_templates.py:1648 msgid "Speed" msgstr "Szybkość" #: lib/logitech_receiver/settings_templates.py:1649 msgid "Period" msgstr "Okres" #: lib/logitech_receiver/settings_templates.py:1650 msgid "Intensity" msgstr "Intensywność" #: lib/logitech_receiver/settings_templates.py:1651 msgid "Ramp" msgstr "Narastanie" #: lib/logitech_receiver/settings_templates.py:1666 msgid "LEDs" msgstr "Diody LED" #: lib/logitech_receiver/settings_templates.py:1703 msgid "Per-key Lighting" msgstr "Podświetlenie poszczególnych klawiszy" #: lib/logitech_receiver/settings_templates.py:1704 msgid "Control per-key lighting." msgstr "Steruj podświetleniem poszczególnych klawiszy." #: lib/logitech_receiver/settings_templates.py:1786 msgid "Force Sensing Buttons" msgstr "Przyciski wykrywające siłę" #: lib/logitech_receiver/settings_templates.py:1787 msgid "Change the force required to activate button." msgstr "Zmień siłę potrzebną do aktywowania przycisku." #: lib/logitech_receiver/settings_templates.py:1804 msgid "Force Sensing Button" msgstr "Przycisk wykrywający siłę" #: lib/logitech_receiver/settings_templates.py:1814 msgid "Haptic Feeback Level" msgstr "Poziom haptycznego sprzężenia zwrotnego" #: lib/logitech_receiver/settings_templates.py:1815 msgid "Change power of haptic feedback. (Zero to turn off.)" msgstr "" "Zmień moc haptycznego sprzężenia zwrotnego. (Zero powoduje wyłączenie.)" #: lib/logitech_receiver/settings_templates.py:1857 msgid "Play Haptic Waveform" msgstr "Odtwórz falę haptyczną" #: lib/logitech_receiver/settings_templates.py:1858 msgid "Tell device to play a haptic waveform." msgstr "Poleć urządzeniu odtworzenie fali haptycznej." #: lib/solaar/ui/__init__.py:120 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:88 lib/solaar/ui/action.py:92 #: lib/solaar/ui/window.py:208 msgid "Unpair" msgstr "Usuń sparowanie" #: lib/solaar/ui/action.py:91 lib/solaar/ui/diversion_rules.py:141 msgid "Cancel" msgstr "Anuluj" #: lib/solaar/ui/common.py:42 msgid "Permissions error" msgstr "Błąd uprawnień" #: lib/solaar/ui/common.py:44 #, 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:46 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:49 msgid "Cannot connect to device error" msgstr "Błąd łączenia się z urządzeniem" #: lib/solaar/ui/common.py:51 #, 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:53 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:56 msgid "Unpairing failed" msgstr "Usunięcie sparowania nie powiodło się" #: lib/solaar/ui/common.py:58 #, python-brace-format msgid "Failed to unpair %{device} from %{receiver}." msgstr "Nie udało się sparować %{device} z %{receiver}." #: lib/solaar/ui/common.py:63 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:245 msgid "Complete - ENTER to change" msgstr "Kompletne - ENTER, aby zmienić" #: lib/solaar/ui/config_panel.py:245 msgid "Incomplete" msgstr "Niekompletne" #: lib/solaar/ui/config_panel.py:491 lib/solaar/ui/config_panel.py:543 #, 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:642 msgid "Changes allowed" msgstr "Zmiany dozwolone" #: lib/solaar/ui/config_panel.py:643 msgid "No changes allowed" msgstr "Zmiany niedozwolone" #: lib/solaar/ui/config_panel.py:644 msgid "Ignore this setting" msgstr "Ignoruj to ustawienie" #: lib/solaar/ui/config_panel.py:690 msgid "Working" msgstr "Pracuję" #: lib/solaar/ui/config_panel.py:693 msgid "Read/write operation failed." msgstr "Operacja odczytu/zapisu nie powiodła się." #: lib/solaar/ui/desktop_notifications.py:112 msgid "unspecified reason" msgstr "nieokreślony powód" #: lib/solaar/ui/diversion_rules.py:103 msgid "Built-in rules" msgstr "Wbudowane reguły" #: lib/solaar/ui/diversion_rules.py:103 msgid "User-defined rules" msgstr "Reguły zdefiniowane przez użytkownika" #: lib/solaar/ui/diversion_rules.py:105 lib/solaar/ui/diversion_rules.py:1126 msgid "Rule" msgstr "Reguła" #: lib/solaar/ui/diversion_rules.py:106 lib/solaar/ui/diversion_rules.py:382 #: lib/solaar/ui/diversion_rules.py:509 msgid "Sub-rule" msgstr "Reguła podrzędna" #: lib/solaar/ui/diversion_rules.py:108 msgid "[empty]" msgstr "[pusta]" #: lib/solaar/ui/diversion_rules.py:132 msgid "Make changes permanent?" msgstr "Wprowadzić zmiany na stałe?" #: lib/solaar/ui/diversion_rules.py:137 msgid "Yes" msgstr "Tak" #: lib/solaar/ui/diversion_rules.py:139 msgid "No" msgstr "Nie" #: lib/solaar/ui/diversion_rules.py:144 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:273 msgid "Paste here" msgstr "Wklej tutaj" #: lib/solaar/ui/diversion_rules.py:275 msgid "Paste above" msgstr "Wklej powyżej" #: lib/solaar/ui/diversion_rules.py:277 msgid "Paste below" msgstr "Wklej poniżej" #: lib/solaar/ui/diversion_rules.py:283 msgid "Paste rule here" msgstr "Wklej regułę tutaj" #: lib/solaar/ui/diversion_rules.py:285 msgid "Paste rule above" msgstr "Wklej regułę powyżej" #: lib/solaar/ui/diversion_rules.py:287 msgid "Paste rule below" msgstr "Wklej regułę poniżej" #: lib/solaar/ui/diversion_rules.py:291 msgid "Paste rule" msgstr "Wklej regułę" #: lib/solaar/ui/diversion_rules.py:306 msgid "Insert here" msgstr "Wstaw tutaj" #: lib/solaar/ui/diversion_rules.py:308 msgid "Insert above" msgstr "Wstaw powyżej" #: lib/solaar/ui/diversion_rules.py:310 msgid "Insert below" msgstr "Wstaw poniżej" #: lib/solaar/ui/diversion_rules.py:316 msgid "Insert new rule here" msgstr "Wstaw nową regułę tutaj" #: lib/solaar/ui/diversion_rules.py:318 msgid "Insert new rule above" msgstr "Wstaw nową regułę powyżej" #: lib/solaar/ui/diversion_rules.py:320 msgid "Insert new rule below" msgstr "Wstaw nową regułę poniżej" #: lib/solaar/ui/diversion_rules.py:347 msgid "Flatten" msgstr "Spłaszcz" #: lib/solaar/ui/diversion_rules.py:380 msgid "Insert" msgstr "Wstaw" #: lib/solaar/ui/diversion_rules.py:383 lib/solaar/ui/diversion_rules.py:511 #: lib/solaar/ui/diversion_rules.py:1158 msgid "Or" msgstr "Lub" #: lib/solaar/ui/diversion_rules.py:384 lib/solaar/ui/diversion_rules.py:510 #: lib/solaar/ui/diversion_rules.py:1144 msgid "And" msgstr "Oraz" #: lib/solaar/ui/diversion_rules.py:386 msgid "Condition" msgstr "Warunek" #: lib/solaar/ui/diversion_rules.py:388 lib/solaar/ui/rule_conditions.py:154 msgid "Feature" msgstr "Funkcja" #: lib/solaar/ui/diversion_rules.py:389 lib/solaar/ui/rule_conditions.py:188 msgid "Report" msgstr "Zgłoszenie" #: lib/solaar/ui/diversion_rules.py:390 lib/solaar/ui/rule_conditions.py:69 msgid "Process" msgstr "Proces" #: lib/solaar/ui/diversion_rules.py:391 msgid "Mouse process" msgstr "Proces myszy" #: lib/solaar/ui/diversion_rules.py:392 lib/solaar/ui/rule_conditions.py:225 msgid "Modifiers" msgstr "Modyfikatory" #: lib/solaar/ui/diversion_rules.py:393 lib/solaar/ui/rule_conditions.py:277 msgid "Key" msgstr "Klawisz" #: lib/solaar/ui/diversion_rules.py:394 lib/solaar/ui/rule_conditions.py:318 msgid "KeyIsDown" msgstr "KlawiszJestWciśnięty" #: lib/solaar/ui/diversion_rules.py:395 lib/solaar/ui/diversion_rules.py:1432 msgid "Active" msgstr "Aktywny" #: lib/solaar/ui/diversion_rules.py:396 lib/solaar/ui/diversion_rules.py:1390 #: lib/solaar/ui/diversion_rules.py:1441 lib/solaar/ui/diversion_rules.py:1487 msgid "Device" msgstr "Urządzenie" #: lib/solaar/ui/diversion_rules.py:397 lib/solaar/ui/diversion_rules.py:1467 msgid "Host" msgstr "Host" #: lib/solaar/ui/diversion_rules.py:398 lib/solaar/ui/diversion_rules.py:1506 msgid "Setting" msgstr "Ustawienie" #: lib/solaar/ui/diversion_rules.py:399 lib/solaar/ui/rule_conditions.py:333 #: lib/solaar/ui/rule_conditions.py:382 msgid "Test" msgstr "Test" #: lib/solaar/ui/diversion_rules.py:400 lib/solaar/ui/rule_conditions.py:507 msgid "Test bytes" msgstr "Bajty testowe" #: lib/solaar/ui/diversion_rules.py:401 lib/solaar/ui/rule_conditions.py:608 msgid "Mouse Gesture" msgstr "Gest myszy" #: lib/solaar/ui/diversion_rules.py:405 msgid "Action" msgstr "Czynność" #: lib/solaar/ui/diversion_rules.py:407 lib/solaar/ui/rule_actions.py:138 msgid "Key press" msgstr "Naciśnięcie klawisza" #: lib/solaar/ui/diversion_rules.py:408 lib/solaar/ui/rule_actions.py:191 msgid "Mouse scroll" msgstr "Przewijanie myszą" #: lib/solaar/ui/diversion_rules.py:409 lib/solaar/ui/rule_actions.py:256 msgid "Mouse click" msgstr "Kliknięcie myszą" #: lib/solaar/ui/diversion_rules.py:410 msgid "Set" msgstr "Ustaw" #: lib/solaar/ui/diversion_rules.py:411 lib/solaar/ui/rule_actions.py:328 msgid "Execute" msgstr "Wykonaj" #: lib/solaar/ui/diversion_rules.py:412 lib/solaar/ui/diversion_rules.py:1189 msgid "Later" msgstr "Później" #: lib/solaar/ui/diversion_rules.py:441 msgid "Insert new rule" msgstr "Wstaw nową regułę" #: lib/solaar/ui/diversion_rules.py:461 lib/solaar/ui/rule_actions.py:82 #: lib/solaar/ui/rule_actions.py:287 lib/solaar/ui/rule_conditions.py:556 msgid "Delete" msgstr "Usuń" #: lib/solaar/ui/diversion_rules.py:483 msgid "Negate" msgstr "Neguj" #: lib/solaar/ui/diversion_rules.py:507 msgid "Wrap with" msgstr "Obwiąż" #: lib/solaar/ui/diversion_rules.py:537 msgid "Cut" msgstr "Wytnij" #: lib/solaar/ui/diversion_rules.py:553 msgid "Paste" msgstr "Wklej" #: lib/solaar/ui/diversion_rules.py:559 msgid "Copy" msgstr "Skopiuj" #: lib/solaar/ui/diversion_rules.py:568 msgid "Solaar Rule Editor" msgstr "Edytor reguł Solaar" #: lib/solaar/ui/diversion_rules.py:668 msgid "Save changes" msgstr "Zapisz zmiany" #: lib/solaar/ui/diversion_rules.py:673 msgid "Discard changes" msgstr "Odrzuć zmiany" #: lib/solaar/ui/diversion_rules.py:1104 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:1169 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:1207 msgid "Not" msgstr "Nie" #: lib/solaar/ui/diversion_rules.py:1238 msgid "Toggle" msgstr "Przełącz" #: lib/solaar/ui/diversion_rules.py:1239 msgid "True" msgstr "Prawda" #: lib/solaar/ui/diversion_rules.py:1240 msgid "False" msgstr "Fałsz" #: lib/solaar/ui/diversion_rules.py:1253 msgid "Unsupported setting" msgstr "Nieobsługiwane ustawienie" #: lib/solaar/ui/diversion_rules.py:1396 lib/solaar/ui/diversion_rules.py:1415 #: lib/solaar/ui/diversion_rules.py:1493 lib/solaar/ui/diversion_rules.py:1748 #: lib/solaar/ui/diversion_rules.py:1766 msgid "Originating device" msgstr "Urządzenie inicjujące" #: lib/solaar/ui/diversion_rules.py:1428 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:1437 msgid "Device that originated the current notification." msgstr "Urządzenie, z którego pochodzi bieżące powiadomienie." #: lib/solaar/ui/diversion_rules.py:1450 msgid "Name of host computer." msgstr "Nazwa komputera hosta." #: lib/solaar/ui/diversion_rules.py:1520 msgid "Value" msgstr "Wartość" #: lib/solaar/ui/diversion_rules.py:1529 msgid "Item" msgstr "Pozycja" #: lib/solaar/ui/diversion_rules.py:1808 msgid "Change setting on device" msgstr "Zmień ustawienie w urządzeniu" #: lib/solaar/ui/diversion_rules.py:1824 msgid "Setting on device" msgstr "Ustawienie w urządzeniu" #: lib/solaar/ui/pair_window.py:44 lib/solaar/ui/pair_window.py:167 #, python-format msgid "%(receiver_name)s: pair new device" msgstr "%(receiver_name)s: sparuj nowe urządzenie" #: lib/solaar/ui/pair_window.py:46 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:48 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:51 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:53 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:55 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:56 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:58 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:61 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:68 msgid "The channel indicator light should be blinking rapidly." msgstr "Kontrolka kanału powinna szybko migać." #: lib/solaar/ui/pair_window.py:72 #, 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:78 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:168 #, python-format msgid "Enter passcode on %(name)s." msgstr "Wprowadź kod dostępu do %(name)s." #: lib/solaar/ui/pair_window.py:171 #, 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:176 msgid "left" msgstr "lewo" #: lib/solaar/ui/pair_window.py:176 msgid "right" msgstr "prawo" #: lib/solaar/ui/pair_window.py:178 #, 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:221 msgid "The wireless link is not encrypted" msgstr "Połączenie nie jest szyfrowane" #: lib/solaar/ui/pair_window.py:226 msgid "Found a new device:" msgstr "Znaleziono nowe urządzenie:" #: lib/solaar/ui/pair_window.py:247 msgid "Pairing failed" msgstr "Sparowanie nie powiodło się" #: lib/solaar/ui/pair_window.py:249 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:251 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:253 msgid "More paired devices than receiver can support." msgstr "Więcej sparowanych urządzeń niż obsługuje odbiornik." #: lib/solaar/ui/pair_window.py:255 msgid "No further details are available about the error." msgstr "Brak dodatkowych informacji na temat błędu." #: lib/solaar/ui/rule_actions.py:54 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:60 msgid "Add key" msgstr "Dodaj klawisz" #: lib/solaar/ui/rule_actions.py:63 msgid "Click" msgstr "Kliknij" #: lib/solaar/ui/rule_actions.py:66 msgid "Depress" msgstr "Wciśnij" #: lib/solaar/ui/rule_actions.py:69 msgid "Release" msgstr "Zwolnij" #: lib/solaar/ui/rule_actions.py:153 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:211 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:216 msgid "Button" msgstr "Przycisk" #: lib/solaar/ui/rule_actions.py:218 msgid "Action (and Count, if click)" msgstr "Czynność (i liczba, jeśli kliknięcie)" #: lib/solaar/ui/rule_actions.py:269 msgid "Execute a command with arguments." msgstr "Wykonaj polecenie z argumentami." #: lib/solaar/ui/rule_actions.py:273 msgid "Add argument" msgstr "Dodaj argument" #: lib/solaar/ui/rule_conditions.py:52 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:82 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:99 msgid "MouseProcess" msgstr "ProcesMyszy" #: lib/solaar/ui/rule_conditions.py:123 msgid "Feature name of notification triggering rule processing." msgstr "Nazwa funkcji powiadomienia wyzwalająca przetwarzanie reguły." #: lib/solaar/ui/rule_conditions.py:169 msgid "Report number of notification triggering rule processing." msgstr "Numer zgłoszenia powiadomienia wyzwalającego przetwarzanie reguły." #: lib/solaar/ui/rule_conditions.py:201 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:241 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:250 msgid "Key down" msgstr "Klawisz w dół" #: lib/solaar/ui/rule_conditions.py:253 msgid "Key up" msgstr "Klawisz w górę" #: lib/solaar/ui/rule_conditions.py:293 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:331 msgid "Test condition on notification triggering rule processing." msgstr "Warunek testowy przy powiadomieniu wyzwalający przetwarzanie reguły." #: lib/solaar/ui/rule_conditions.py:335 msgid "Parameter" msgstr "Parametr" #: lib/solaar/ui/rule_conditions.py:408 msgid "begin (inclusive)" msgstr "rozpocznij (włącznie)" #: lib/solaar/ui/rule_conditions.py:409 msgid "end (exclusive)" msgstr "zakończ (wyłącznie)" #: lib/solaar/ui/rule_conditions.py:417 msgid "range" msgstr "zakres" #: lib/solaar/ui/rule_conditions.py:420 msgid "minimum" msgstr "minimum" #: lib/solaar/ui/rule_conditions.py:421 msgid "maximum" msgstr "maksimum" #: lib/solaar/ui/rule_conditions.py:423 #, 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:426 lib/solaar/ui/rule_conditions.py:427 msgid "mask" msgstr "maska" #: lib/solaar/ui/rule_conditions.py:428 #, 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:437 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:447 msgid "type" msgstr "typ" #: lib/solaar/ui/rule_conditions.py:535 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:541 msgid "Add movement" msgstr "Dodaj ruch" #: lib/solaar/ui/tray.py:55 msgid "No supported device found" msgstr "Nie znaleziono obsługiwanego urządzenia" #: lib/solaar/ui/tray.py:60 lib/solaar/ui/window.py:328 #, python-format msgid "About %s" msgstr "O programie %s" #: lib/solaar/ui/tray.py:61 lib/solaar/ui/window.py:326 #, python-format msgid "Quit %s" msgstr "Zakończ %s" #: lib/solaar/ui/tray.py:273 lib/solaar/ui/tray.py:281 msgid "no receiver" msgstr "brak odbiornika" #: lib/solaar/ui/tray.py:294 lib/solaar/ui/tray.py:299 msgid "offline" msgstr "wyłączone" #: lib/solaar/ui/tray.py:297 msgid "no status" msgstr "brak statusu" #: lib/solaar/ui/window.py:110 msgid "Scanning" msgstr "Skanowanie" #: lib/solaar/ui/window.py:141 msgid "Battery" msgstr "Akumulator" #: lib/solaar/ui/window.py:144 msgid "Wireless Link" msgstr "Połączenie bezprzewodowe" #: lib/solaar/ui/window.py:148 msgid "Lighting" msgstr "Podświetlenie" #: lib/solaar/ui/window.py:182 msgid "Show Technical Details" msgstr "Wyświetl szczegóły techniczne" #: lib/solaar/ui/window.py:198 msgid "Pair new device" msgstr "Sparuj nowe urządzenie" #: lib/solaar/ui/window.py:216 msgid "Select a device" msgstr "Wybierz urządzenie" #: lib/solaar/ui/window.py:331 msgid "Rule Editor" msgstr "Edytor reguł" #: lib/solaar/ui/window.py:522 msgid "Path" msgstr "Ścieżka" #: lib/solaar/ui/window.py:524 msgid "USB ID" msgstr "ID USB" #: lib/solaar/ui/window.py:527 lib/solaar/ui/window.py:529 #: lib/solaar/ui/window.py:544 lib/solaar/ui/window.py:546 msgid "Serial" msgstr "Nr seryjny" #: lib/solaar/ui/window.py:533 msgid "Index" msgstr "Indeks" #: lib/solaar/ui/window.py:535 msgid "Wireless PID" msgstr "PID bezprz." #: lib/solaar/ui/window.py:537 msgid "Product ID" msgstr "ID produktu" #: lib/solaar/ui/window.py:539 msgid "Protocol" msgstr "Protokół" #: lib/solaar/ui/window.py:539 msgid "Unknown" msgstr "Nieznany" #: lib/solaar/ui/window.py:541 msgid "Polling rate" msgstr "Odpytywanie" #: lib/solaar/ui/window.py:548 msgid "Unit ID" msgstr "ID jednostki" #: lib/solaar/ui/window.py:560 msgid "Notifications" msgstr "Powiadomienia" #: lib/solaar/ui/window.py:604 msgid "No device paired." msgstr "Brak sparowanych urządzeń." #: lib/solaar/ui/window.py:613 #, 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:624 #, 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:681 msgid "Battery Voltage" msgstr "Napięcie akumulatora" #: lib/solaar/ui/window.py:683 msgid "Voltage reported by battery" msgstr "Napięcie zgłoszone przez akumulator" #: lib/solaar/ui/window.py:685 msgid "Battery Level" msgstr "Poziom akumulatora" #: lib/solaar/ui/window.py:687 msgid "Approximate level reported by battery" msgstr "Przybliżony poziom zgłoszony przez akumulator" #: lib/solaar/ui/window.py:694 lib/solaar/ui/window.py:696 msgid "next reported " msgstr "następny zgłoszony " #: lib/solaar/ui/window.py:697 msgid " and next level to be reported." msgstr " oraz następny poziom do zgłoszenia." #: lib/solaar/ui/window.py:713 msgid "encrypted" msgstr "szyfrowane" #: lib/solaar/ui/window.py:715 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:717 msgid "not encrypted" msgstr "nieszyfrowane" #: lib/solaar/ui/window.py:721 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:737 #, python-format msgid "%(light_level)d lux" msgstr "%(light_level)d lx" ================================================ FILE: po/pt.po ================================================ # 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" ================================================ FILE: po/pt_BR.po ================================================ # 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: 2026-01-08 19:37-0300\n" "PO-Revision-Date: 2026-01-08 22:37-0300\n" "Last-Translator: Mário Victor Ribeiro Silva \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.8\n" #: lib/logitech_receiver/base_usb.py:52 msgid "Bolt Receiver" msgstr "Receptor Bolt" #: lib/logitech_receiver/base_usb.py:64 msgid "Unifying Receiver" msgstr "Receptor Unifying" #: lib/logitech_receiver/base_usb.py:75 lib/logitech_receiver/base_usb.py:87 #: lib/logitech_receiver/base_usb.py:100 lib/logitech_receiver/base_usb.py:113 msgid "Nano Receiver" msgstr "Receptor Nano" #: lib/logitech_receiver/base_usb.py:125 msgid "Lightspeed Receiver" msgstr "Receptor Lightspeed" #: lib/logitech_receiver/base_usb.py:136 msgid "EX100 Receiver 27 Mhz" msgstr "Receptor EX100 27 Mhz" #: lib/logitech_receiver/common.py:649 #, python-format msgid "Battery: %(level)s (%(status)s)" msgstr "Bateria: %(level)s (%(status)s)" #: lib/logitech_receiver/common.py:652 #, python-format msgid "Battery: %(percent)d%% (%(status)s)" msgstr "Bateria: %(percent)d%% (%(status)s)" #: lib/logitech_receiver/hidpp20.py:1048 #: lib/logitech_receiver/settings_templates.py:299 msgid "Disabled" msgstr "Desativado" #: lib/logitech_receiver/hidpp20.py:1049 msgid "Static" msgstr "Estático" #: lib/logitech_receiver/hidpp20.py:1050 msgid "Pulse" msgstr "Pulso" #: lib/logitech_receiver/hidpp20.py:1051 msgid "Cycle" msgstr "Ciclo" #: lib/logitech_receiver/hidpp20.py:1052 msgid "Boot" msgstr "Inicialização" #: lib/logitech_receiver/hidpp20.py:1053 msgid "Demo" msgstr "Demonstração" #: lib/logitech_receiver/hidpp20.py:1055 msgid "Breathe" msgstr "Respirar" #: lib/logitech_receiver/hidpp20.py:1058 msgid "Ripple" msgstr "Ondulação" #: lib/logitech_receiver/hidpp20.py:1059 msgid "Decomposition" msgstr "Decomposição" #: lib/logitech_receiver/hidpp20.py:1060 msgid "Signature1" msgstr "Assinatura1" #: lib/logitech_receiver/hidpp20.py:1061 msgid "Signature2" msgstr "Assinatura2" #: lib/logitech_receiver/hidpp20.py:1062 msgid "CycleS" msgstr "CicloS" #: lib/logitech_receiver/hidpp20.py:1126 msgid "Unknown Location" msgstr "Localização Desconhecida" #: lib/logitech_receiver/hidpp20.py:1127 msgid "Primary" msgstr "Primário" #: lib/logitech_receiver/hidpp20.py:1128 msgid "Logo" msgstr "Logotipo" #: lib/logitech_receiver/hidpp20.py:1129 msgid "Left Side" msgstr "Lado esquerdo" #: lib/logitech_receiver/hidpp20.py:1130 msgid "Right Side" msgstr "Lado direito" #: lib/logitech_receiver/hidpp20.py:1131 msgid "Combined" msgstr "Combinado" #: lib/logitech_receiver/hidpp20.py:1132 msgid "Primary 1" msgstr "Primário 1" #: lib/logitech_receiver/hidpp20.py:1133 msgid "Primary 2" msgstr "Primário 2" #: lib/logitech_receiver/hidpp20.py:1134 msgid "Primary 3" msgstr "Primário 3" #: lib/logitech_receiver/hidpp20.py:1135 msgid "Primary 4" msgstr "Primário 4" #: lib/logitech_receiver/hidpp20.py:1136 msgid "Primary 5" msgstr "Primário 5" #: lib/logitech_receiver/hidpp20.py:1137 msgid "Primary 6" msgstr "Primário 6" #: lib/logitech_receiver/i18n.py:28 msgid "empty" msgstr "vazia" #: lib/logitech_receiver/i18n.py:29 msgid "critical" msgstr "crítica" #: lib/logitech_receiver/i18n.py:30 msgid "low" msgstr "fraca" #: lib/logitech_receiver/i18n.py:31 msgid "average" msgstr "média" #: lib/logitech_receiver/i18n.py:32 msgid "good" msgstr "boa" #: lib/logitech_receiver/i18n.py:33 msgid "full" msgstr "cheia" #: lib/logitech_receiver/i18n.py:35 msgid "discharging" msgstr "descarregando" #: lib/logitech_receiver/i18n.py:36 msgid "recharging" msgstr "recarregando" #: lib/logitech_receiver/i18n.py:37 lib/solaar/ui/window.py:700 msgid "charging" msgstr "carregando" #: lib/logitech_receiver/i18n.py:38 msgid "not charging" msgstr "não carregando" #: lib/logitech_receiver/i18n.py:39 msgid "almost full" msgstr "quase cheia" #: lib/logitech_receiver/i18n.py:40 msgid "charged" msgstr "carregada" #: lib/logitech_receiver/i18n.py:41 msgid "slow recharge" msgstr "recarga lenta" #: lib/logitech_receiver/i18n.py:42 msgid "invalid battery" msgstr "bateria inválida" #: lib/logitech_receiver/i18n.py:43 msgid "thermal error" msgstr "erro térmico" #: lib/logitech_receiver/i18n.py:44 msgid "error" msgstr "erro" #: lib/logitech_receiver/i18n.py:45 msgid "standard" msgstr "padrão" #: lib/logitech_receiver/i18n.py:46 msgid "fast" msgstr "rápido" #: lib/logitech_receiver/i18n.py:47 msgid "slow" msgstr "lento" #: lib/logitech_receiver/i18n.py:49 msgid "device timeout" msgstr "tempo esgotado do dispositivo" #: lib/logitech_receiver/i18n.py:50 msgid "device not supported" msgstr "dispositivo não suportado" #: lib/logitech_receiver/i18n.py:51 msgid "too many devices" msgstr "número excessivo de dispositivos" #: lib/logitech_receiver/i18n.py:52 msgid "sequence timeout" msgstr "tempo esgotado da sequência" #: lib/logitech_receiver/i18n.py:54 lib/solaar/ui/window.py:555 msgid "Firmware" msgstr "Firmware" #: lib/logitech_receiver/i18n.py:55 msgid "Bootloader" msgstr "Bootloader" #: lib/logitech_receiver/i18n.py:56 msgid "Hardware" msgstr "Hardware" #: lib/logitech_receiver/i18n.py:57 msgid "Other" msgstr "Outro" #: lib/logitech_receiver/i18n.py:59 msgid "Left Button" msgstr "Botão da esquerda" #: lib/logitech_receiver/i18n.py:60 msgid "Right Button" msgstr "Botão da direita" #: lib/logitech_receiver/i18n.py:61 msgid "Middle Button" msgstr "Botão do meio" #: lib/logitech_receiver/i18n.py:62 msgid "Back Button" msgstr "Botão Voltar" #: lib/logitech_receiver/i18n.py:63 msgid "Forward Button" msgstr "Botão Avançar" #: lib/logitech_receiver/i18n.py:64 msgid "Mouse Gesture Button" msgstr "Botão de gestos do mouse" #: lib/logitech_receiver/i18n.py:65 msgid "Smart Shift" msgstr "Smart Shift" #: lib/logitech_receiver/i18n.py:66 msgid "DPI Switch" msgstr "Mudança de DPI" #: lib/logitech_receiver/i18n.py:67 msgid "Left Tilt" msgstr "Inclinação para a esquerda" #: lib/logitech_receiver/i18n.py:68 msgid "Right Tilt" msgstr "Inclinação para a direita" #: lib/logitech_receiver/i18n.py:69 msgid "Left Click" msgstr "Clique com o botão esquerdo" #: lib/logitech_receiver/i18n.py:70 msgid "Right Click" msgstr "Clique com o botão direito" #: lib/logitech_receiver/i18n.py:71 msgid "Mouse Middle Button" msgstr "Botão do meio do mouse" #: lib/logitech_receiver/i18n.py:72 msgid "Mouse Back Button" msgstr "Botão de voltar do mouse" #: lib/logitech_receiver/i18n.py:73 msgid "Mouse Forward Button" msgstr "Botão de avançar do mouse" #: lib/logitech_receiver/i18n.py:74 msgid "Gesture Button Navigation" msgstr "Botão de navegação por gestos" #: lib/logitech_receiver/i18n.py:75 msgid "Mouse Scroll Left Button" msgstr "Botão de rolagem esquerda do mouse" #: lib/logitech_receiver/i18n.py:76 msgid "Mouse Scroll Right Button" msgstr "Botão de rolagem direita do mouse" #: lib/logitech_receiver/i18n.py:78 msgid "pressed" msgstr "pressionada" #: lib/logitech_receiver/i18n.py:79 msgid "released" msgstr "liberada" #: lib/logitech_receiver/notifications.py:156 lib/solaar/listener.py:243 msgid "connected" msgstr "conectado" #: lib/logitech_receiver/notifications.py:156 lib/solaar/listener.py:243 msgid "disconnected" msgstr "desconectado" #: lib/logitech_receiver/notifications.py:182 msgid "unpaired" msgstr "desemparelhado" #: lib/logitech_receiver/notifications.py:231 msgid "powered on" msgstr "ligado" #: lib/logitech_receiver/notifications.py:283 msgid "ADC measurement notification" msgstr "" #: lib/logitech_receiver/notifications.py:428 #: lib/logitech_receiver/notifications.py:484 msgid "pairing lock is closed" msgstr "trava de emparelhamento está fechada" #: lib/logitech_receiver/notifications.py:428 #: lib/logitech_receiver/notifications.py:484 msgid "pairing lock is open" msgstr "trava de emparelhamento está aberta" #: lib/logitech_receiver/notifications.py:447 msgid "discovery lock is closed" msgstr "bloqueio de descoberta está fechado" #: lib/logitech_receiver/notifications.py:447 msgid "discovery lock is open" msgstr "bloqueio de descoberta está aberto" #: lib/logitech_receiver/receiver.py:437 msgid "No paired devices." msgstr "Nenhum dispositivo emparelhado." #: lib/logitech_receiver/receiver.py:439 lib/solaar/ui/window.py:606 #, 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/settings.py:602 msgid "register" msgstr "register" #: lib/logitech_receiver/settings.py:616 lib/logitech_receiver/settings.py:654 msgid "feature" msgstr "feature" #: lib/logitech_receiver/settings_templates.py:138 msgid "Swap Fx function" msgstr "Trocar função de teclas Fx" #: lib/logitech_receiver/settings_templates.py:141 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:146 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:154 msgid "Hand Detection" msgstr "Detecção de Mão" #: lib/logitech_receiver/settings_templates.py:155 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:162 msgid "Scroll Wheel Smooth Scrolling" msgstr "Rolagem suave com a roda" #: lib/logitech_receiver/settings_templates.py:163 #: lib/logitech_receiver/settings_templates.py:410 #: lib/logitech_receiver/settings_templates.py:439 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:170 msgid "Side Scrolling" msgstr "Rolagem Lateral" #: lib/logitech_receiver/settings_templates.py:172 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:182 msgid "Sensitivity (DPI - older mice)" msgstr "Sensibilidade (DPI - mouses antigos)" #: lib/logitech_receiver/settings_templates.py:183 #: lib/logitech_receiver/settings_templates.py:1019 #: lib/logitech_receiver/settings_templates.py:1047 msgid "Mouse movement sensitivity" msgstr "Sensibilidade do movimento do mouse" #: lib/logitech_receiver/settings_templates.py:256 msgid "Backlight Timed" msgstr "Temporizador de luz de fundo" #: lib/logitech_receiver/settings_templates.py:257 #: lib/logitech_receiver/settings_templates.py:397 msgid "Set illumination time for keyboard." msgstr "Defina o tempo de iluminação do teclado." #: lib/logitech_receiver/settings_templates.py:268 msgid "Backlight" msgstr "Luz de fundo" #: lib/logitech_receiver/settings_templates.py:269 msgid "" "Illumination level on keyboard. Changes made are only applied in Manual " "mode." msgstr "" "Nível de iluminação do teclado. As alterações feitas só se aplicam no modo " "Manual." #: lib/logitech_receiver/settings_templates.py:301 msgid "Automatic" msgstr "Automático" #: lib/logitech_receiver/settings_templates.py:303 msgid "Manual" msgstr "Manual" #: lib/logitech_receiver/settings_templates.py:305 msgid "Enabled" msgstr "Habilitado" #: lib/logitech_receiver/settings_templates.py:311 msgid "Backlight Level" msgstr "Nível de luz de fundo" #: lib/logitech_receiver/settings_templates.py:312 msgid "Illumination level on keyboard when in Manual mode." msgstr "Nível de iluminação do teclado no modo Manual." #: lib/logitech_receiver/settings_templates.py:369 msgid "Backlight Delay Hands Out" msgstr "Atraso da luz de fundo ao retirar as mãos" #: lib/logitech_receiver/settings_templates.py:370 msgid "" "Delay in seconds until backlight fades out with hands away from keyboard." msgstr "" "Tempo de atraso, em segundos, até que a luz de fundo se apague gradualmente " "quando as mãos forem retiradas do teclado." #: lib/logitech_receiver/settings_templates.py:378 msgid "Backlight Delay Hands In" msgstr "Atraso da luz de fundo ao colocar as mãos" #: lib/logitech_receiver/settings_templates.py:379 msgid "Delay in seconds until backlight fades out with hands near keyboard." msgstr "" "Tempo de atraso, em segundos, até que a luz de fundo se apague com as mãos " "próximas ao teclado." #: lib/logitech_receiver/settings_templates.py:387 msgid "Backlight Delay Powered" msgstr "Atraso da luz de fundo Alimentado" #: lib/logitech_receiver/settings_templates.py:388 msgid "Delay in seconds until backlight fades out with external power." msgstr "" "Tempo de atraso, em segundos, até que a luz de fundo se apague com a " "alimentação externa." #: lib/logitech_receiver/settings_templates.py:396 msgid "Backlight (Seconds)" msgstr "Luz de fundo (segundos)" #: lib/logitech_receiver/settings_templates.py:408 msgid "Scroll Wheel High Resolution" msgstr "Alta resolução de rolagem com a roda" #: lib/logitech_receiver/settings_templates.py:412 #: lib/logitech_receiver/settings_templates.py:441 msgid "Set to ignore if scrolling is abnormally fast or slow" msgstr "Configure para ignorar se a rolagem for anormalmente rápida ou lenta" #: lib/logitech_receiver/settings_templates.py:419 #: lib/logitech_receiver/settings_templates.py:450 msgid "Scroll Wheel Diversion" msgstr "Desvio da roda de rolagem" #: lib/logitech_receiver/settings_templates.py:421 msgid "" "Make scroll wheel send LOWRES_WHEEL HID++ notifications (which trigger " "Solaar rules but are otherwise ignored)." msgstr "" #: lib/logitech_receiver/settings_templates.py:428 msgid "Scroll Wheel Direction" msgstr "Direção de rolagem" #: lib/logitech_receiver/settings_templates.py:429 msgid "Invert direction for vertical scroll with wheel." msgstr "Inverte a direção da rolagem vertical." #: lib/logitech_receiver/settings_templates.py:437 msgid "Scroll Wheel Resolution" msgstr "Resolução da roda de rolagem" #: lib/logitech_receiver/settings_templates.py:452 msgid "" "Make scroll wheel send HIRES_WHEEL HID++ notifications (which trigger Solaar " "rules but are otherwise ignored)." msgstr "" #: lib/logitech_receiver/settings_templates.py:461 msgid "Sensitivity (Pointer Speed)" msgstr "Sensibilidade (Velocidade do Ponteiro)" #: lib/logitech_receiver/settings_templates.py:462 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:472 msgid "Thumb Wheel Diversion" msgstr "Desvio da roda de polegar" #: lib/logitech_receiver/settings_templates.py:474 msgid "" "Make thumb wheel send THUMB_WHEEL HID++ notifications (which trigger Solaar " "rules but are otherwise ignored)." msgstr "" #: lib/logitech_receiver/settings_templates.py:483 msgid "Thumb Wheel Direction" msgstr "Direção da rolagem com a roda" #: lib/logitech_receiver/settings_templates.py:484 msgid "Invert thumb wheel scroll direction." msgstr "Inverter a direção da rolagem com o polegar." #: lib/logitech_receiver/settings_templates.py:504 msgid "Onboard Profiles" msgstr "Perfis Embarcados" #: lib/logitech_receiver/settings_templates.py:505 msgid "" "Enable an onboard profile, which controls report rate, sensitivity, and " "button actions" msgstr "" #: lib/logitech_receiver/settings_templates.py:549 #: lib/logitech_receiver/settings_templates.py:582 msgid "Report Rate" msgstr "Taxa de relatório" #: lib/logitech_receiver/settings_templates.py:551 #: lib/logitech_receiver/settings_templates.py:584 msgid "Frequency of device movement reports" msgstr "Frequência de relatórios de movimentação de dispositivos" #: lib/logitech_receiver/settings_templates.py:551 #: lib/logitech_receiver/settings_templates.py:584 #: lib/logitech_receiver/settings_templates.py:1019 #: lib/logitech_receiver/settings_templates.py:1047 #: lib/logitech_receiver/settings_templates.py:1421 #: lib/logitech_receiver/settings_templates.py:1452 msgid "May need Onboard Profiles set to Disable to be effective." msgstr "" #: lib/logitech_receiver/settings_templates.py:612 msgid "Divert crown events" msgstr "Desviar eventos da coroa" #: lib/logitech_receiver/settings_templates.py:613 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:621 msgid "Crown smooth scroll" msgstr "" #: lib/logitech_receiver/settings_templates.py:622 msgid "Set crown smooth scroll" msgstr "" #: lib/logitech_receiver/settings_templates.py:630 msgid "Divert G and M Keys" msgstr "" #: lib/logitech_receiver/settings_templates.py:631 msgid "" "Make G and M keys send HID++ notifications (which trigger Solaar rules but " "are otherwise ignored)." msgstr "" #: lib/logitech_receiver/settings_templates.py:645 msgid "Scroll Wheel Ratcheted" msgstr "" #: lib/logitech_receiver/settings_templates.py:646 msgid "" "Switch the mouse wheel between speed-controlled ratcheting and always " "freespin." msgstr "" #: lib/logitech_receiver/settings_templates.py:648 msgid "Freespinning" msgstr "Giro livre" #: lib/logitech_receiver/settings_templates.py:648 msgid "Ratcheted" msgstr "Ratcheted" #: lib/logitech_receiver/settings_templates.py:655 msgid "Scroll Wheel Ratchet Speed" msgstr "" #: lib/logitech_receiver/settings_templates.py:657 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:707 msgid "Scroll Wheel Ratchet Torque" msgstr "" #: lib/logitech_receiver/settings_templates.py:708 msgid "Change the torque needed to overcome the ratchet." msgstr "" #: lib/logitech_receiver/settings_templates.py:743 msgid "Key/Button Actions" msgstr "Ações das teclas/botões" #: lib/logitech_receiver/settings_templates.py:745 msgid "Change the action for the key or button." msgstr "Trocar a ação da tecla/botão." #: lib/logitech_receiver/settings_templates.py:747 msgid "Overridden by diversion." msgstr "" #: lib/logitech_receiver/settings_templates.py:749 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:924 msgid "Key/Button Diversion" msgstr "Desvio de Tecla/Botão" #: lib/logitech_receiver/settings_templates.py:925 msgid "" "Make the key or button send HID++ notifications (Diverted) or initiate Mouse " "Gestures or Sliding DPI" msgstr "" #: lib/logitech_receiver/settings_templates.py:928 #: lib/logitech_receiver/settings_templates.py:929 #: lib/logitech_receiver/settings_templates.py:930 msgid "Diverted" msgstr "Desviado" #: lib/logitech_receiver/settings_templates.py:928 #: lib/logitech_receiver/settings_templates.py:929 msgid "Mouse Gestures" msgstr "Gestos do mouse" #: lib/logitech_receiver/settings_templates.py:928 #: lib/logitech_receiver/settings_templates.py:929 #: lib/logitech_receiver/settings_templates.py:930 msgid "Regular" msgstr "Regular" #: lib/logitech_receiver/settings_templates.py:928 msgid "Sliding DPI" msgstr "DPI deslizante" #: lib/logitech_receiver/settings_templates.py:1018 #: lib/logitech_receiver/settings_templates.py:1046 msgid "Sensitivity (DPI)" msgstr "Sensibilidade (DPI)" #: lib/logitech_receiver/settings_templates.py:1123 msgid "Sensitivity Switching" msgstr "Alternar sensibilidade" #: lib/logitech_receiver/settings_templates.py:1125 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:1129 msgid "Off" msgstr "Desativado" #: lib/logitech_receiver/settings_templates.py:1160 msgid "Disable keys" msgstr "Desativar teclas" #: lib/logitech_receiver/settings_templates.py:1161 msgid "Disable specific keyboard keys." msgstr "Desativar teclas específicas do teclado." #: lib/logitech_receiver/settings_templates.py:1164 #, python-format msgid "Disables the %s key." msgstr "Desativa a tecla %s." #: lib/logitech_receiver/settings_templates.py:1177 #: lib/logitech_receiver/settings_templates.py:1234 msgid "Set OS" msgstr "Definir SO" #: lib/logitech_receiver/settings_templates.py:1178 #: lib/logitech_receiver/settings_templates.py:1235 msgid "Change keys to match OS." msgstr "Trocar as teclas para corresponder ao SO." #: lib/logitech_receiver/settings_templates.py:1247 msgid "Change Host" msgstr "Trocar Hospedeiro" #: lib/logitech_receiver/settings_templates.py:1248 msgid "Switch connection to a different host" msgstr "Trocar a conexão para um hospedeiro diferente" #: lib/logitech_receiver/settings_templates.py:1272 msgid "Performs a left click." msgstr "Executa um clique com botão esquerdo." #: lib/logitech_receiver/settings_templates.py:1272 msgid "Single tap" msgstr "Toque único" #: lib/logitech_receiver/settings_templates.py:1273 msgid "Performs a right click." msgstr "Executa um clique com botão direito." #: lib/logitech_receiver/settings_templates.py:1273 msgid "Single tap with two fingers" msgstr "Toque único com dois dedos" #: lib/logitech_receiver/settings_templates.py:1274 msgid "Single tap with three fingers" msgstr "Toque único com três dedos" #: lib/logitech_receiver/settings_templates.py:1278 msgid "Double tap" msgstr "Toque duplo" #: lib/logitech_receiver/settings_templates.py:1278 msgid "Performs a double click." msgstr "Executa um clique duplo." #: lib/logitech_receiver/settings_templates.py:1279 msgid "Double tap with two fingers" msgstr "Toque duplo com dois dedos" #: lib/logitech_receiver/settings_templates.py:1280 msgid "Double tap with three fingers" msgstr "Toque duplo com três dedos" #: lib/logitech_receiver/settings_templates.py:1283 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:1283 msgid "Tap and drag" msgstr "Tocar e arrastar" #: lib/logitech_receiver/settings_templates.py:1285 msgid "Tap and drag with two fingers" msgstr "Tocar e arrastar com dois dedos" #: lib/logitech_receiver/settings_templates.py:1286 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:1288 msgid "Tap and drag with three fingers" msgstr "Tocar e arrastar com três dedos" #: lib/logitech_receiver/settings_templates.py:1291 msgid "Suppress tap and edge gestures" msgstr "Impedir gestos de toque e borda" #: lib/logitech_receiver/settings_templates.py:1292 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:1294 msgid "Scroll with one finger" msgstr "Rolar com um dedo" #: lib/logitech_receiver/settings_templates.py:1294 #: lib/logitech_receiver/settings_templates.py:1295 #: lib/logitech_receiver/settings_templates.py:1298 msgid "Scrolls." msgstr "Executa a rolagem." #: lib/logitech_receiver/settings_templates.py:1295 #: lib/logitech_receiver/settings_templates.py:1298 msgid "Scroll with two fingers" msgstr "Rolar com dois dedos" #: lib/logitech_receiver/settings_templates.py:1296 msgid "Scroll horizontally with two fingers" msgstr "Rolar horizontalmente com dois dedos" #: lib/logitech_receiver/settings_templates.py:1296 msgid "Scrolls horizontally." msgstr "Faz rolagem horizontal." #: lib/logitech_receiver/settings_templates.py:1297 msgid "Scroll vertically with two fingers" msgstr "Rolar verticalmente com dois dedos" #: lib/logitech_receiver/settings_templates.py:1297 msgid "Scrolls vertically." msgstr "Faz rolagem vertical." #: lib/logitech_receiver/settings_templates.py:1299 msgid "Inverts the scrolling direction." msgstr "Inverte a direção de rolagem." #: lib/logitech_receiver/settings_templates.py:1299 msgid "Natural scrolling" msgstr "Rolagem natural" #: lib/logitech_receiver/settings_templates.py:1300 msgid "Enables the thumbwheel." msgstr "Ativa a roda do polegar." #: lib/logitech_receiver/settings_templates.py:1300 msgid "Thumbwheel" msgstr "Roda do polegar" #: lib/logitech_receiver/settings_templates.py:1311 #: lib/logitech_receiver/settings_templates.py:1315 msgid "Swipe from the top edge" msgstr "Deslizar da borda superior" #: lib/logitech_receiver/settings_templates.py:1312 msgid "Swipe from the left edge" msgstr "Deslizar da borda esquerda" #: lib/logitech_receiver/settings_templates.py:1313 msgid "Swipe from the right edge" msgstr "Deslizar da borda direita" #: lib/logitech_receiver/settings_templates.py:1314 msgid "Swipe from the bottom edge" msgstr "Deslizar da borda inferior" #: lib/logitech_receiver/settings_templates.py:1316 msgid "Swipe two fingers from the left edge" msgstr "Deslizar dois dedos da borda esquerda" #: lib/logitech_receiver/settings_templates.py:1317 msgid "Swipe two fingers from the right edge" msgstr "Deslizar dois dedos da borda direita" #: lib/logitech_receiver/settings_templates.py:1318 msgid "Swipe two fingers from the bottom edge" msgstr "Deslizar dois dedos da borda inferior" #: lib/logitech_receiver/settings_templates.py:1319 msgid "Swipe two fingers from the top edge" msgstr "Deslizar dois dedos da borda superior" #: lib/logitech_receiver/settings_templates.py:1320 #: lib/logitech_receiver/settings_templates.py:1324 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:1320 msgid "Zoom with two fingers." msgstr "Fazer zoom com dois dedos." #: lib/logitech_receiver/settings_templates.py:1321 msgid "Pinch to zoom out." msgstr "Aproximar os dedos para diminuir o zoom." #: lib/logitech_receiver/settings_templates.py:1322 msgid "Spread to zoom in." msgstr "Afastar os dedos para aumentar o zoom." #: lib/logitech_receiver/settings_templates.py:1323 msgid "Zoom with three fingers." msgstr "Fazer zoom com três dedos." #: lib/logitech_receiver/settings_templates.py:1324 msgid "Zoom with two fingers" msgstr "Fazer zoom com dois dedos" #: lib/logitech_receiver/settings_templates.py:1342 msgid "Pixel zone" msgstr "Região de pixels" #: lib/logitech_receiver/settings_templates.py:1343 msgid "Ratio zone" msgstr "Região de proporção" #: lib/logitech_receiver/settings_templates.py:1344 msgid "Scale factor" msgstr "Fator de escala" #: lib/logitech_receiver/settings_templates.py:1344 msgid "Sets the cursor speed." msgstr "Define a velocidade do cursor." #: lib/logitech_receiver/settings_templates.py:1348 msgid "Left" msgstr "Esquerda" #: lib/logitech_receiver/settings_templates.py:1348 msgid "Left-most coordinate." msgstr "Coordenada do ponto mais à esquerda." #: lib/logitech_receiver/settings_templates.py:1349 msgid "Bottom" msgstr "Fundo" #: lib/logitech_receiver/settings_templates.py:1349 msgid "Bottom coordinate." msgstr "Coordenada inferior." #: lib/logitech_receiver/settings_templates.py:1350 msgid "Width" msgstr "Largura" #: lib/logitech_receiver/settings_templates.py:1350 msgid "Width." msgstr "Largura." #: lib/logitech_receiver/settings_templates.py:1351 msgid "Height" msgstr "Altura" #: lib/logitech_receiver/settings_templates.py:1351 msgid "Height." msgstr "Altura." #: lib/logitech_receiver/settings_templates.py:1352 msgid "Cursor speed." msgstr "Velocidade do cursor." #: lib/logitech_receiver/settings_templates.py:1352 msgid "Scale" msgstr "Escala" #: lib/logitech_receiver/settings_templates.py:1358 msgid "Gestures" msgstr "Gestos" #: lib/logitech_receiver/settings_templates.py:1359 msgid "Tweak the mouse/touchpad behaviour." msgstr "Ajustar o comportamento do mouse/touchpad." #: lib/logitech_receiver/settings_templates.py:1375 msgid "Gestures Diversion" msgstr "Desvio de Gesto" #: lib/logitech_receiver/settings_templates.py:1376 msgid "Divert mouse/touchpad gestures." msgstr "" #: lib/logitech_receiver/settings_templates.py:1392 msgid "Gesture params" msgstr "Parâmetros de gestos" #: lib/logitech_receiver/settings_templates.py:1393 msgid "Change numerical parameters of a mouse/touchpad." msgstr "Alterar parâmetros numéricos de um mouse/touchpad." #: lib/logitech_receiver/settings_templates.py:1417 msgid "M-Key LEDs" msgstr "" #: lib/logitech_receiver/settings_templates.py:1419 msgid "Control the M-Key LEDs." msgstr "" #: lib/logitech_receiver/settings_templates.py:1423 #: lib/logitech_receiver/settings_templates.py:1454 msgid "May need G Keys diverted to be effective." msgstr "" #: lib/logitech_receiver/settings_templates.py:1429 #, python-format msgid "Lights up the %s key." msgstr "Ilumina a tecla %s." #: lib/logitech_receiver/settings_templates.py:1448 msgid "MR-Key LED" msgstr "" #: lib/logitech_receiver/settings_templates.py:1450 msgid "Control the MR-Key LED." msgstr "" #: lib/logitech_receiver/settings_templates.py:1471 msgid "Persistent Key/Button Mapping" msgstr "" #: lib/logitech_receiver/settings_templates.py:1473 msgid "Permanently change the mapping for the key or button." msgstr "Alterar permanentemente o mapeamento da tecla ou do botão." #: lib/logitech_receiver/settings_templates.py:1475 msgid "" "Changing important keys or buttons (such as for the left mouse button) can " "result in an unusable system." msgstr "" "Alterar teclas ou botões importantes (como o botão esquerdo do mouse) pode " "tornar o sistema inutilizável." #: lib/logitech_receiver/settings_templates.py:1532 msgid "Sidetone" msgstr "" #: lib/logitech_receiver/settings_templates.py:1533 msgid "Set sidetone level." msgstr "" #: lib/logitech_receiver/settings_templates.py:1542 msgid "Equalizer" msgstr "Equalizador" #: lib/logitech_receiver/settings_templates.py:1543 msgid "Set equalizer levels." msgstr "" #: lib/logitech_receiver/settings_templates.py:1565 msgid "Hz" msgstr "Hz" #: lib/logitech_receiver/settings_templates.py:1571 msgid "Power Management" msgstr "Gerenciamento de energia" #: lib/logitech_receiver/settings_templates.py:1572 msgid "Power off in minutes (0 for never)." msgstr "Desligamento em minutos (0 para nunca)." #: lib/logitech_receiver/settings_templates.py:1584 msgid "Brightness Control" msgstr "Controle de brilho" #: lib/logitech_receiver/settings_templates.py:1585 msgid "Control overall brightness" msgstr "Controle o brilho geral" #: lib/logitech_receiver/settings_templates.py:1628 #: lib/logitech_receiver/settings_templates.py:1681 msgid "LED Control" msgstr "LED Control" #: lib/logitech_receiver/settings_templates.py:1629 #: lib/logitech_receiver/settings_templates.py:1682 msgid "Switch control of LED zones between device and Solaar" msgstr "Controle as zonas de LED alternando entre o dispositivo e o Solaar" #: lib/logitech_receiver/settings_templates.py:1644 #: lib/logitech_receiver/settings_templates.py:1692 msgid "LED Zone Effects" msgstr "" #: lib/logitech_receiver/settings_templates.py:1645 #: lib/logitech_receiver/settings_templates.py:1693 msgid "LED Control needs to be set to Solaar to be effective." msgstr "" #: lib/logitech_receiver/settings_templates.py:1645 #: lib/logitech_receiver/settings_templates.py:1693 msgid "Set effect for LED Zone" msgstr "" #: lib/logitech_receiver/settings_templates.py:1647 msgid "Color" msgstr "Cor" #: lib/logitech_receiver/settings_templates.py:1648 msgid "Speed" msgstr "Velocidade" #: lib/logitech_receiver/settings_templates.py:1649 msgid "Period" msgstr "Período" #: lib/logitech_receiver/settings_templates.py:1650 msgid "Intensity" msgstr "Intensity" #: lib/logitech_receiver/settings_templates.py:1651 msgid "Ramp" msgstr "Rampa" #: lib/logitech_receiver/settings_templates.py:1666 msgid "LEDs" msgstr "LEDs" #: lib/logitech_receiver/settings_templates.py:1703 msgid "Per-key Lighting" msgstr "Iluminação por tecla" #: lib/logitech_receiver/settings_templates.py:1704 msgid "Control per-key lighting." msgstr "Control per-key lighting." #: lib/logitech_receiver/settings_templates.py:1786 msgid "Force Sensing Buttons" msgstr "" #: lib/logitech_receiver/settings_templates.py:1787 msgid "Change the force required to activate button." msgstr "" #: lib/logitech_receiver/settings_templates.py:1804 msgid "Force Sensing Button" msgstr "" #: lib/logitech_receiver/settings_templates.py:1814 msgid "Haptic Feeback Level" msgstr "Nível de feedback háptico" #: lib/logitech_receiver/settings_templates.py:1815 msgid "Change power of haptic feedback. (Zero to turn off.)" msgstr "Alterar a intensidade do feedback tátil. (Zero para desligar.)" #: lib/logitech_receiver/settings_templates.py:1857 msgid "Play Haptic Waveform" msgstr "Reproduzir forma de onda háptica" #: lib/logitech_receiver/settings_templates.py:1858 msgid "Tell device to play a haptic waveform." msgstr "Diga ao dispositivo para reproduzir uma forma de onda háptica." #: lib/solaar/ui/__init__.py:120 msgid "Another Solaar process is already running so just expose its window" msgstr "" #: lib/solaar/ui/about/model.py:36 msgid "" "Manages Logitech receivers,\n" "keyboards, mice, and tablets." msgstr "" "Gerencia receptores, teclados,\n" "mouses e tablets da Logitech." #: lib/solaar/ui/about/model.py:63 msgid "Additional Programming" msgstr "Programação adicional" #: lib/solaar/ui/about/model.py:64 msgid "GUI design" msgstr "Design da GUI" #: lib/solaar/ui/about/model.py:66 msgid "Testing" msgstr "Teste" #: lib/solaar/ui/about/model.py:74 msgid "Logitech documentation" msgstr "Documentação da Logitech" #: lib/solaar/ui/action.py:88 lib/solaar/ui/action.py:92 #: lib/solaar/ui/window.py:208 msgid "Unpair" msgstr "Desemparelhar" #: lib/solaar/ui/action.py:91 lib/solaar/ui/diversion_rules.py:141 msgid "Cancel" msgstr "Cancelar" #: lib/solaar/ui/common.py:42 msgid "Permissions error" msgstr "Erro de permissão" #: lib/solaar/ui/common.py:44 #, python-format msgid "" "Found a Logitech receiver or device (%s), but did not have permission to " "open it." msgstr "" "Foi encontrado um receptor ou dispositivo Logitech (%s), mas não tinha " "permissão para abri-lo." #: lib/solaar/ui/common.py:46 msgid "" "If you've just installed Solaar, try disconnecting the receiver or device " "and then reconnecting it." msgstr "" "Se você acabou de instalar o Solaar, tente desconectar o receptor ou " "dispositivo e, em seguida, reconectá-lo." #: lib/solaar/ui/common.py:49 msgid "Cannot connect to device error" msgstr "Erro - não foi possível conectar-se ao dispositivo" #: lib/solaar/ui/common.py:51 #, 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/common.py:53 msgid "" "Try disconnecting the device and then reconnecting it or turning it off and " "then on." msgstr "" "Tente desconectar o dispositivo e reconectá-lo, ou desligá-lo e ligá-lo " "novamente." #: lib/solaar/ui/common.py:56 msgid "Unpairing failed" msgstr "Desemparelhamento falhou" #: lib/solaar/ui/common.py:58 #, python-brace-format msgid "Failed to unpair %{device} from %{receiver}." msgstr "Falha ao desemparelhar %{device} do %{receiver}." #: lib/solaar/ui/common.py:63 msgid "The receiver returned an error, with no further details." msgstr "O receptor retornou um erro, sem maiores detalhes." #: lib/solaar/ui/config_panel.py:245 msgid "Complete - ENTER to change" msgstr "Completo - pressione ENTER para alterar" #: lib/solaar/ui/config_panel.py:245 msgid "Incomplete" msgstr "Incompleto" #: lib/solaar/ui/config_panel.py:491 lib/solaar/ui/config_panel.py:543 #, python-format msgid "%d value" msgid_plural "%d values" msgstr[0] "%d valor" msgstr[1] "%d valores" #: lib/solaar/ui/config_panel.py:642 msgid "Changes allowed" msgstr "Mudanças permitidas" #: lib/solaar/ui/config_panel.py:643 msgid "No changes allowed" msgstr "Mudanças não permitidas" #: lib/solaar/ui/config_panel.py:644 msgid "Ignore this setting" msgstr "Ignorar esta configuração" #: lib/solaar/ui/config_panel.py:690 msgid "Working" msgstr "Funcionando" #: lib/solaar/ui/config_panel.py:693 msgid "Read/write operation failed." msgstr "Operação de leitura/escrita falhou." #: lib/solaar/ui/desktop_notifications.py:112 msgid "unspecified reason" msgstr "razão não especificada" #: lib/solaar/ui/diversion_rules.py:103 msgid "Built-in rules" msgstr "Regras predefinidas" #: lib/solaar/ui/diversion_rules.py:103 msgid "User-defined rules" msgstr "Regras do usuário" #: lib/solaar/ui/diversion_rules.py:105 lib/solaar/ui/diversion_rules.py:1126 msgid "Rule" msgstr "Regra" #: lib/solaar/ui/diversion_rules.py:106 lib/solaar/ui/diversion_rules.py:382 #: lib/solaar/ui/diversion_rules.py:509 msgid "Sub-rule" msgstr "Sub-regra" #: lib/solaar/ui/diversion_rules.py:108 msgid "[empty]" msgstr "[vazio]" #: lib/solaar/ui/diversion_rules.py:132 msgid "Make changes permanent?" msgstr "Tornar as alterações permanentes?" #: lib/solaar/ui/diversion_rules.py:137 msgid "Yes" msgstr "Sim" #: lib/solaar/ui/diversion_rules.py:139 msgid "No" msgstr "Não" #: lib/solaar/ui/diversion_rules.py:144 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:273 msgid "Paste here" msgstr "Colar aqui" #: lib/solaar/ui/diversion_rules.py:275 msgid "Paste above" msgstr "Colar acima" #: lib/solaar/ui/diversion_rules.py:277 msgid "Paste below" msgstr "Colar abaixo" #: lib/solaar/ui/diversion_rules.py:283 msgid "Paste rule here" msgstr "Colar regra aqui" #: lib/solaar/ui/diversion_rules.py:285 msgid "Paste rule above" msgstr "Colar regra acima" #: lib/solaar/ui/diversion_rules.py:287 msgid "Paste rule below" msgstr "Colar regra abaixo" #: lib/solaar/ui/diversion_rules.py:291 msgid "Paste rule" msgstr "Colar regra" #: lib/solaar/ui/diversion_rules.py:306 msgid "Insert here" msgstr "Inserir aqui" #: lib/solaar/ui/diversion_rules.py:308 msgid "Insert above" msgstr "Inserir acima" #: lib/solaar/ui/diversion_rules.py:310 msgid "Insert below" msgstr "Inserir abaixo" #: lib/solaar/ui/diversion_rules.py:316 msgid "Insert new rule here" msgstr "Inserir nova regra aqui" #: lib/solaar/ui/diversion_rules.py:318 msgid "Insert new rule above" msgstr "Inserir nova regra acima" #: lib/solaar/ui/diversion_rules.py:320 msgid "Insert new rule below" msgstr "Inserir nova regra abaixo" #: lib/solaar/ui/diversion_rules.py:347 msgid "Flatten" msgstr "Desaninhar" #: lib/solaar/ui/diversion_rules.py:380 msgid "Insert" msgstr "Inserir" #: lib/solaar/ui/diversion_rules.py:383 lib/solaar/ui/diversion_rules.py:511 #: lib/solaar/ui/diversion_rules.py:1158 msgid "Or" msgstr "Ou" #: lib/solaar/ui/diversion_rules.py:384 lib/solaar/ui/diversion_rules.py:510 #: lib/solaar/ui/diversion_rules.py:1144 msgid "And" msgstr "E" #: lib/solaar/ui/diversion_rules.py:386 msgid "Condition" msgstr "Condição" #: lib/solaar/ui/diversion_rules.py:388 lib/solaar/ui/rule_conditions.py:154 msgid "Feature" msgstr "Feature" #: lib/solaar/ui/diversion_rules.py:389 lib/solaar/ui/rule_conditions.py:188 msgid "Report" msgstr "Report" #: lib/solaar/ui/diversion_rules.py:390 lib/solaar/ui/rule_conditions.py:69 msgid "Process" msgstr "Processo" #: lib/solaar/ui/diversion_rules.py:391 msgid "Mouse process" msgstr "Processo do mouse" #: lib/solaar/ui/diversion_rules.py:392 lib/solaar/ui/rule_conditions.py:225 msgid "Modifiers" msgstr "Teclas modificadoras" #: lib/solaar/ui/diversion_rules.py:393 lib/solaar/ui/rule_conditions.py:277 msgid "Key" msgstr "Tecla" #: lib/solaar/ui/diversion_rules.py:394 lib/solaar/ui/rule_conditions.py:318 msgid "KeyIsDown" msgstr "Tecla Pressionada" #: lib/solaar/ui/diversion_rules.py:395 lib/solaar/ui/diversion_rules.py:1432 msgid "Active" msgstr "Ativo" #: lib/solaar/ui/diversion_rules.py:396 lib/solaar/ui/diversion_rules.py:1390 #: lib/solaar/ui/diversion_rules.py:1441 lib/solaar/ui/diversion_rules.py:1487 msgid "Device" msgstr "Dispositivo" #: lib/solaar/ui/diversion_rules.py:397 lib/solaar/ui/diversion_rules.py:1467 msgid "Host" msgstr "Host" #: lib/solaar/ui/diversion_rules.py:398 lib/solaar/ui/diversion_rules.py:1506 msgid "Setting" msgstr "Configuração" #: lib/solaar/ui/diversion_rules.py:399 lib/solaar/ui/rule_conditions.py:333 #: lib/solaar/ui/rule_conditions.py:382 msgid "Test" msgstr "Teste" #: lib/solaar/ui/diversion_rules.py:400 lib/solaar/ui/rule_conditions.py:507 msgid "Test bytes" msgstr "Bytes de teste" #: lib/solaar/ui/diversion_rules.py:401 lib/solaar/ui/rule_conditions.py:608 msgid "Mouse Gesture" msgstr "Gesto do mouse" #: lib/solaar/ui/diversion_rules.py:405 msgid "Action" msgstr "Ação" #: lib/solaar/ui/diversion_rules.py:407 lib/solaar/ui/rule_actions.py:138 msgid "Key press" msgstr "Pressionamento de tecla" #: lib/solaar/ui/diversion_rules.py:408 lib/solaar/ui/rule_actions.py:191 msgid "Mouse scroll" msgstr "Rolagem do mouse" #: lib/solaar/ui/diversion_rules.py:409 lib/solaar/ui/rule_actions.py:256 msgid "Mouse click" msgstr "Clique do mouse" #: lib/solaar/ui/diversion_rules.py:410 msgid "Set" msgstr "Definir" #: lib/solaar/ui/diversion_rules.py:411 lib/solaar/ui/rule_actions.py:328 msgid "Execute" msgstr "Executar" #: lib/solaar/ui/diversion_rules.py:412 lib/solaar/ui/diversion_rules.py:1189 msgid "Later" msgstr "Mais tarde" #: lib/solaar/ui/diversion_rules.py:441 msgid "Insert new rule" msgstr "Inserir nova regra" #: lib/solaar/ui/diversion_rules.py:461 lib/solaar/ui/rule_actions.py:82 #: lib/solaar/ui/rule_actions.py:287 lib/solaar/ui/rule_conditions.py:556 msgid "Delete" msgstr "Apagar" #: lib/solaar/ui/diversion_rules.py:483 msgid "Negate" msgstr "Negar" #: lib/solaar/ui/diversion_rules.py:507 msgid "Wrap with" msgstr "Envolver com" #: lib/solaar/ui/diversion_rules.py:537 msgid "Cut" msgstr "Recortar" #: lib/solaar/ui/diversion_rules.py:553 msgid "Paste" msgstr "Colar" #: lib/solaar/ui/diversion_rules.py:559 msgid "Copy" msgstr "Copiar" #: lib/solaar/ui/diversion_rules.py:568 msgid "Solaar Rule Editor" msgstr "Editor de Regras do Solaar" #: lib/solaar/ui/diversion_rules.py:668 msgid "Save changes" msgstr "Salvar alterações" #: lib/solaar/ui/diversion_rules.py:673 msgid "Discard changes" msgstr "Descartar alterações" #: lib/solaar/ui/diversion_rules.py:1104 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:1169 msgid "" "Number of seconds to delay. Delay between 0 and 1 is done with higher " "precision." msgstr "" "Número de segundos de atraso. Atrasos entre 0 e 1 são calculados com maior " "precisão." #: lib/solaar/ui/diversion_rules.py:1207 msgid "Not" msgstr "Não" #: lib/solaar/ui/diversion_rules.py:1238 msgid "Toggle" msgstr "Alternar" #: lib/solaar/ui/diversion_rules.py:1239 msgid "True" msgstr "Verdadeiro" #: lib/solaar/ui/diversion_rules.py:1240 msgid "False" msgstr "Falso" #: lib/solaar/ui/diversion_rules.py:1253 msgid "Unsupported setting" msgstr "Configuração não suportada" #: lib/solaar/ui/diversion_rules.py:1396 lib/solaar/ui/diversion_rules.py:1415 #: lib/solaar/ui/diversion_rules.py:1493 lib/solaar/ui/diversion_rules.py:1748 #: lib/solaar/ui/diversion_rules.py:1766 msgid "Originating device" msgstr "Dispositivo de origem" #: lib/solaar/ui/diversion_rules.py:1428 msgid "Device is active and its settings can be changed." msgstr "O dispositivo está ativo e suas configurações podem ser alteradas." #: lib/solaar/ui/diversion_rules.py:1437 msgid "Device that originated the current notification." msgstr "Dispositivo que originou a notificação atual." #: lib/solaar/ui/diversion_rules.py:1450 msgid "Name of host computer." msgstr "Name of host computer." #: lib/solaar/ui/diversion_rules.py:1520 msgid "Value" msgstr "Valor" #: lib/solaar/ui/diversion_rules.py:1529 msgid "Item" msgstr "Item" #: lib/solaar/ui/diversion_rules.py:1808 msgid "Change setting on device" msgstr "Alterar configuração no dispositivo" #: lib/solaar/ui/diversion_rules.py:1824 msgid "Setting on device" msgstr "Configuração no dispositivo" #: lib/solaar/ui/pair_window.py:44 lib/solaar/ui/pair_window.py:167 #, python-format msgid "%(receiver_name)s: pair new device" msgstr "%(receiver_name)s: new dispositivo emparelhado" #: lib/solaar/ui/pair_window.py:46 msgid "Bolt receivers are only compatible with Bolt devices." msgstr "Os receptores Bolt são compatíveis apenas com dispositivos Bolt." #: lib/solaar/ui/pair_window.py:48 msgid "Press a pairing button or key until the pairing light flashes quickly." msgstr "" "Pressione o botão ou tecla de emparelhamento até que a luz de emparelhamento " "pisque rapidamente." #: lib/solaar/ui/pair_window.py:51 msgid "Unifying receivers are only compatible with Unifying devices." msgstr "" "Os receptores Unifying são compatíveis apenas com dispositivos Unifying." #: lib/solaar/ui/pair_window.py:53 msgid "Other receivers are only compatible with a few devices." msgstr "Outros receptores são compatíveis apenas com alguns dispositivos." #: lib/solaar/ui/pair_window.py:55 msgid "For most devices, turn on the device you want to pair." msgstr "" "Para a maioria dos dispositivos, ligue o dispositivo que deseja emparelhar." #: lib/solaar/ui/pair_window.py:56 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:58 msgid "The device must not be paired with a nearby powered-on receiver." msgstr "" "O dispositivo não deve estar emparelhado com um receptor ligado nas " "proximidades." #: lib/solaar/ui/pair_window.py:61 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 "" #: lib/solaar/ui/pair_window.py:68 msgid "The channel indicator light should be blinking rapidly." msgstr "A luz indicadora do canal deve estar piscando rapidamente." #: lib/solaar/ui/pair_window.py:72 #, 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:78 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:168 #, python-format msgid "Enter passcode on %(name)s." msgstr "Digite a senha em %(name)s." #: lib/solaar/ui/pair_window.py:171 #, python-format msgid "Type %(passcode)s and then press the enter key." msgstr "Digite %(passcode)s e então pressione a tecla Enter." #: lib/solaar/ui/pair_window.py:176 msgid "left" msgstr "esquerda" #: lib/solaar/ui/pair_window.py:176 msgid "right" msgstr "direita" #: lib/solaar/ui/pair_window.py:178 #, python-format msgid "" "Press %(code)s\n" "and then press left and right buttons simultaneously." msgstr "" "Pressione %(code)s\n" "e em seguida, pressione os botões esquerdo e direito simultaneamente." #: lib/solaar/ui/pair_window.py:221 msgid "The wireless link is not encrypted" msgstr "A conexão sem fio não é criptografada" #: lib/solaar/ui/pair_window.py:226 msgid "Found a new device:" msgstr "Encontrado um novo dispositivo:" #: lib/solaar/ui/pair_window.py:247 msgid "Pairing failed" msgstr "Emparelhamento falhou" #: lib/solaar/ui/pair_window.py:249 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:251 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:253 msgid "More paired devices than receiver can support." msgstr "Há mais dispositivos pareados do que o receptor suporta." #: lib/solaar/ui/pair_window.py:255 msgid "No further details are available about the error." msgstr "Sem maiores detalhes sobre o erro." #: lib/solaar/ui/rule_actions.py:54 msgid "" "Simulate a chorded key click or depress or release.\n" "On Wayland requires write access to /dev/uinput." msgstr "" "Simule um clique, pressionamento ou liberação de tecla em conjunto.\n" "No Wayland, requer acesso de gravação a /dev/uinput." #: lib/solaar/ui/rule_actions.py:60 msgid "Add key" msgstr "Adicionar tecla" #: lib/solaar/ui/rule_actions.py:63 msgid "Click" msgstr "Clique" #: lib/solaar/ui/rule_actions.py:66 msgid "Depress" msgstr "Pressionar" #: lib/solaar/ui/rule_actions.py:69 msgid "Release" msgstr "Liberar" #: lib/solaar/ui/rule_actions.py:153 msgid "" "Simulate a mouse scroll.\n" "On Wayland requires write access to /dev/uinput." msgstr "" "Simule a rolagem do mouse.\n" "Em Wayland, é necessário ter acesso de escrita a /dev/uinput." #: lib/solaar/ui/rule_actions.py:211 msgid "" "Simulate a mouse click.\n" "On Wayland requires write access to /dev/uinput." msgstr "" "Simule o clique do mouse.\r\n" "Em Wayland, é necessário ter acesso de escrita a /dev/uinput." #: lib/solaar/ui/rule_actions.py:216 msgid "Button" msgstr "Botão" #: lib/solaar/ui/rule_actions.py:218 msgid "Action (and Count, if click)" msgstr "Ação (e contagem, se houver clique)" #: lib/solaar/ui/rule_actions.py:269 msgid "Execute a command with arguments." msgstr "Executar um comando com argumentos." #: lib/solaar/ui/rule_actions.py:273 msgid "Add argument" msgstr "Adicionar argumento" #: lib/solaar/ui/rule_conditions.py:52 msgid "X11 active process. For use in X11 only." msgstr "Processo ativo do X11. Para uso exclusivo no X11." #: lib/solaar/ui/rule_conditions.py:82 msgid "X11 mouse process. For use in X11 only." msgstr "Processo de mouse X11. Para uso exclusivo no X11." #: lib/solaar/ui/rule_conditions.py:99 msgid "MouseProcess" msgstr "Processo (mouse)" #: lib/solaar/ui/rule_conditions.py:123 msgid "Feature name of notification triggering rule processing." msgstr "" #: lib/solaar/ui/rule_conditions.py:169 msgid "Report number of notification triggering rule processing." msgstr "" #: lib/solaar/ui/rule_conditions.py:201 msgid "Active keyboard modifiers. Not always available in Wayland." msgstr "" #: lib/solaar/ui/rule_conditions.py:241 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:250 msgid "Key down" msgstr "Tecla pressionada" #: lib/solaar/ui/rule_conditions.py:253 msgid "Key up" msgstr "Tecla liberada" #: lib/solaar/ui/rule_conditions.py:293 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:331 msgid "Test condition on notification triggering rule processing." msgstr "" #: lib/solaar/ui/rule_conditions.py:335 msgid "Parameter" msgstr "Parâmetro" #: lib/solaar/ui/rule_conditions.py:408 msgid "begin (inclusive)" msgstr "início (inclusivo)" #: lib/solaar/ui/rule_conditions.py:409 msgid "end (exclusive)" msgstr "fim (exclusivo)" #: lib/solaar/ui/rule_conditions.py:417 msgid "range" msgstr "faixa" #: lib/solaar/ui/rule_conditions.py:420 msgid "minimum" msgstr "mínimo" #: lib/solaar/ui/rule_conditions.py:421 msgid "maximum" msgstr "máximo" #: lib/solaar/ui/rule_conditions.py:423 #, python-format msgid "bytes %(0)d to %(1)d, ranging from %(2)d to %(3)d" msgstr "" #: lib/solaar/ui/rule_conditions.py:426 lib/solaar/ui/rule_conditions.py:427 msgid "mask" msgstr "máscara" #: lib/solaar/ui/rule_conditions.py:428 #, python-format msgid "bytes %(0)d to %(1)d, mask %(2)d" msgstr "" #: lib/solaar/ui/rule_conditions.py:437 msgid "" "Bit or range test on bytes in notification message triggering rule " "processing." msgstr "" #: lib/solaar/ui/rule_conditions.py:447 msgid "type" msgstr "tipo" #: lib/solaar/ui/rule_conditions.py:535 msgid "" "Mouse gesture with optional initiating button followed by zero or more mouse " "movements." msgstr "" "Gesto do mouse com botão iniciador opcional, seguido por zero ou mais " "movimentos do mouse." #: lib/solaar/ui/rule_conditions.py:541 msgid "Add movement" msgstr "Adicionar movimento" #: lib/solaar/ui/tray.py:55 msgid "No supported device found" msgstr "Nenhum dispositivo suportado encontrado" #: lib/solaar/ui/tray.py:60 lib/solaar/ui/window.py:328 #, python-format msgid "About %s" msgstr "Sobre o %s" #: lib/solaar/ui/tray.py:61 lib/solaar/ui/window.py:326 #, python-format msgid "Quit %s" msgstr "Sair do %s" #: lib/solaar/ui/tray.py:273 lib/solaar/ui/tray.py:281 msgid "no receiver" msgstr "nenhum receptor" #: lib/solaar/ui/tray.py:294 lib/solaar/ui/tray.py:299 #: lib/solaar/ui/window.py:728 msgid "offline" msgstr "offline" #: lib/solaar/ui/tray.py:297 msgid "no status" msgstr "sem status" #: lib/solaar/ui/window.py:110 msgid "Scanning" msgstr "Procurando" #: lib/solaar/ui/window.py:141 msgid "Battery" msgstr "Bateria" #: lib/solaar/ui/window.py:144 msgid "Wireless Link" msgstr "Conexão sem fio" #: lib/solaar/ui/window.py:148 msgid "Lighting" msgstr "Iluminação" #: lib/solaar/ui/window.py:182 msgid "Show Technical Details" msgstr "Mostrar detalhes técnicos" #: lib/solaar/ui/window.py:198 msgid "Pair new device" msgstr "Emparelhar novo dispositivo" #: lib/solaar/ui/window.py:216 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:522 msgid "Path" msgstr "Caminho" #: lib/solaar/ui/window.py:524 msgid "USB ID" msgstr "ID USB" #: lib/solaar/ui/window.py:527 lib/solaar/ui/window.py:529 #: lib/solaar/ui/window.py:544 lib/solaar/ui/window.py:546 msgid "Serial" msgstr "Número de série" #: lib/solaar/ui/window.py:533 msgid "Index" msgstr "Índice" #: lib/solaar/ui/window.py:535 msgid "Wireless PID" msgstr "PID sem fio" #: lib/solaar/ui/window.py:537 msgid "Product ID" msgstr "ID do produto" #: lib/solaar/ui/window.py:539 msgid "Protocol" msgstr "Protocolo" #: lib/solaar/ui/window.py:539 msgid "Unknown" msgstr "Desconhecido" #: lib/solaar/ui/window.py:541 msgid "Polling rate" msgstr "Taxa de sondagem" #: lib/solaar/ui/window.py:548 msgid "Unit ID" msgstr "Unit ID" #: lib/solaar/ui/window.py:559 msgid "none" msgstr "nenhum" #: lib/solaar/ui/window.py:560 msgid "Notifications" msgstr "Notificações" #: lib/solaar/ui/window.py:604 msgid "No device paired." msgstr "Nenhum dispositivo emparelhado." #: lib/solaar/ui/window.py:613 #, 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:620 msgid "Only one device can be paired to this receiver." msgstr "Apenas um dispositivo pode ser emparelhado neste receptor." #: lib/solaar/ui/window.py:624 #, 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:681 msgid "Battery Voltage" msgstr "Tensão da Bateria" #: lib/solaar/ui/window.py:683 msgid "Voltage reported by battery" msgstr "Tensão informada pela bateria" #: lib/solaar/ui/window.py:685 msgid "Battery Level" msgstr "Nível de bateria" #: lib/solaar/ui/window.py:687 msgid "Approximate level reported by battery" msgstr "Nível aproximado informado pela bateria" #: lib/solaar/ui/window.py:694 lib/solaar/ui/window.py:696 msgid "next reported " msgstr "próximo valor informado: " #: lib/solaar/ui/window.py:697 msgid " and next level to be reported." msgstr " e o próximo nível que será informado." #: lib/solaar/ui/window.py:702 msgid "last known" msgstr "último conhecido" #: lib/solaar/ui/window.py:713 msgid "encrypted" msgstr "criptografada" #: lib/solaar/ui/window.py:715 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:717 msgid "not encrypted" msgstr "não criptografada" #: lib/solaar/ui/window.py:721 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 "" "A conexão sem fio entre este dispositivo e o seu receptor não está " "criptografada.\n" "Isso representa um problema de segurança para dispositivos apontadores e um " "problema de segurança grave para dispositivos de entrada de texto." #: lib/solaar/ui/window.py:737 #, python-format msgid "%(light_level)d lux" msgstr "%(light_level)d lux" #~ msgid " paired devices." #~ msgstr " dispositivos emparelhados." #~ msgid "%(battery_level)s" #~ msgstr "%(battery_level)s" #~ msgid "%(battery_percent)d%%" #~ msgstr "%(battery_percent)d%%" #, python-format #~ msgid "%(rate)d ms (%(rate_hz)dHz)" #~ msgstr "%(rate)d ms (%(rate_hz)dHz)" #~ msgid "1 paired device." #~ msgstr "1 dispositivo emparelhado." #~ msgid "Add action" #~ msgstr "Adicionar ação" #~ 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 "" #~ "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." #~ 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 "" #~ "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" #~ msgid "Battery information unknown." #~ msgstr "A informação sobre a bateria é desconhecida." #, python-format #~ msgid "Battery: %(level)s" #~ msgstr "Bateria: %(level)s" #, python-format #~ msgid "Battery: %(percent)d%%" #~ msgstr "Bateria: %(percent)d%%" #~ msgid "Count" #~ msgstr "Número" #~ msgid "DPI Sliding Adjustment" #~ msgstr "Ajuste de DPI por deslizamento" #~ msgid "DPI Sliding Ajustment" #~ msgstr "Ajuste de DPI por deslizamento" #~ msgid "Divert G Keys" #~ msgstr "Desviar teclas G" #~ msgid "Effectively turns off thumb scrolling in Linux." #~ msgstr "Na prática, desativa a rolagem com o polegar no Linux." #~ msgid "Effectively turns off wheel scrolling in Linux." #~ msgstr "Na prática, desativa a rolagem com a roda no Linux." #~ 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" #, 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." #~ msgid "Found a new device" #~ msgstr "Novo dispositivo encontrado" #~ msgid "Frequency of device polling, in milliseconds" #~ msgstr "Frequência de sondagem, em milissegundos" #~ msgid "HID++ mode for horizontal scroll with the thumb wheel." #~ msgstr "Modo HID++ para a rolagem horizontal com o polegar." #~ msgid "HID++ mode for vertical scroll with the wheel." #~ msgstr "Modo HID++ para a rolagem vertical com a roda." #~ 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 "" #~ "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 "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 "" #~ "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." #, python-format #~ msgid "Lighting: %(level)s lux" #~ msgstr "Iluminação: %(level)s lux" #~ 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)." #~ 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)." #~ msgid "No Logitech receiver found" #~ msgstr "Nenhum receptor da 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 "Polling Rate (ms)" #~ msgstr "Taxa de Sondagem (ms)" #~ msgid "Scroll Wheel HID++ Scrolling" #~ msgstr "Rolagem com a roda usando HID++" #~ msgid "Scroll Wheel Rachet" #~ msgstr "Rolagem no modo catraca" #~ 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." #~ 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 "Smooth Scrolling" #~ msgstr "Rolagem suave" #~ msgid "Solaar depends on a udev file that is not present" #~ msgstr "Solaar depende de um arquivo udev que não está presente." #~ msgid "The receiver only supports %d paired device(s)." #~ msgstr "O receptor só suporta %d dispositivo(s) emparelhado(s)" #~ msgid "The receiver was unplugged." #~ msgstr "O receptor foi desplugado." #~ 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." #~ msgid "Thumb Wheel HID++ Scrolling" #~ msgstr "Rolagem com o polegar usando HID++" #~ msgid "Top-most coordinate." #~ msgstr "Coordenada do ponto mais acima." #~ msgid "Triple tap" #~ msgstr "Toque triplo" #~ 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." #~ msgid "Turn illumination on or off on keyboard." #~ msgstr "Ativar ou desativar a iluminação do teclado." #~ msgid "Turn on the device you want to pair." #~ msgstr "Ligue o dispositivo que você quer emparelhar." #~ msgid "USB id" #~ msgstr "ID do USB" #~ msgid "Up to %d devices can be paired to this receiver" #~ msgstr "Até %d dispositivos podem ser emparelhados com este receptor" #~ msgid "Wheel Resolution" #~ msgstr "Resolução da Roda" #~ msgid "closed" #~ msgstr "fechado" #~ msgid "height" #~ msgstr "altura" #~ msgid "lux" #~ msgstr "lux" #~ msgid "open" #~ msgstr "aberto" #~ msgid "pair new device" #~ msgstr "emparelhar novo dispositivo" #~ msgid "paired devices" #~ msgstr "dispositivos emparelhados" #~ msgid "pairing lock is " #~ msgstr "trava de emparelhamento está " #~ msgid "top" #~ msgstr "topo" #~ msgid "unknown" #~ msgstr "desconhecida" #~ msgid "width" #~ msgstr "largura" ================================================ FILE: po/ro.po ================================================ # 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" ================================================ FILE: po/ru.po ================================================ # 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: 2025-12-01 21:43+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:52 msgid "Bolt Receiver" msgstr "Приёмник «Bolt»" #: lib/logitech_receiver/base_usb.py:64 msgid "Unifying Receiver" msgstr "Приёмник «Unifying»" #: lib/logitech_receiver/base_usb.py:75 lib/logitech_receiver/base_usb.py:87 #: lib/logitech_receiver/base_usb.py:100 lib/logitech_receiver/base_usb.py:113 msgid "Nano Receiver" msgstr "Приёмник «Nano»" #: lib/logitech_receiver/base_usb.py:125 msgid "Lightspeed Receiver" msgstr "Приёмник «Lightspeed»" #: lib/logitech_receiver/base_usb.py:135 msgid "EX100 Receiver 27 Mhz" msgstr "Приёмник «EX100» 27 МГц" #: lib/logitech_receiver/common.py:649 #, python-format msgid "Battery: %(level)s (%(status)s)" msgstr "Батарея: %(level)s (%(status)s)" #: lib/logitech_receiver/common.py:652 #, python-format msgid "Battery: %(percent)d%% (%(status)s)" msgstr "Батарея: %(percent)d%% (%(status)s)" #: lib/logitech_receiver/hidpp20.py:1048 #: lib/logitech_receiver/settings_templates.py:299 msgid "Disabled" msgstr "Отключено" #: lib/logitech_receiver/hidpp20.py:1049 msgid "Static" msgstr "Статичный" #: lib/logitech_receiver/hidpp20.py:1050 msgid "Pulse" msgstr "Пульс" #: lib/logitech_receiver/hidpp20.py:1051 msgid "Cycle" msgstr "Цикл" #: lib/logitech_receiver/hidpp20.py:1052 msgid "Boot" msgstr "Запуск" #: lib/logitech_receiver/hidpp20.py:1053 msgid "Demo" msgstr "Демонстрация" #: lib/logitech_receiver/hidpp20.py:1055 msgid "Breathe" msgstr "Дышать" #: lib/logitech_receiver/hidpp20.py:1058 msgid "Ripple" msgstr "Пульсация" #: lib/logitech_receiver/hidpp20.py:1059 msgid "Decomposition" msgstr "Разложение" #: lib/logitech_receiver/hidpp20.py:1060 msgid "Signature1" msgstr "Подпись1" #: lib/logitech_receiver/hidpp20.py:1061 msgid "Signature2" msgstr "Подпись2" #: lib/logitech_receiver/hidpp20.py:1062 msgid "CycleS" msgstr "Циклы" #: lib/logitech_receiver/hidpp20.py:1126 msgid "Unknown Location" msgstr "Неизвестное местоположение" #: lib/logitech_receiver/hidpp20.py:1127 msgid "Primary" msgstr "Первичный" #: lib/logitech_receiver/hidpp20.py:1128 msgid "Logo" msgstr "Логотип" #: lib/logitech_receiver/hidpp20.py:1129 msgid "Left Side" msgstr "Левая сторона" #: lib/logitech_receiver/hidpp20.py:1130 msgid "Right Side" msgstr "Правая сторона" #: lib/logitech_receiver/hidpp20.py:1131 msgid "Combined" msgstr "Комбинированный" #: lib/logitech_receiver/hidpp20.py:1132 msgid "Primary 1" msgstr "Первичный 1" #: lib/logitech_receiver/hidpp20.py:1133 msgid "Primary 2" msgstr "Первичный 2" #: lib/logitech_receiver/hidpp20.py:1134 msgid "Primary 3" msgstr "Первичный 3" #: lib/logitech_receiver/hidpp20.py:1135 msgid "Primary 4" msgstr "Первичный 4" #: lib/logitech_receiver/hidpp20.py:1136 msgid "Primary 5" msgstr "Первичный 5" #: lib/logitech_receiver/hidpp20.py:1137 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 lib/solaar/ui/window.py:700 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 lib/solaar/ui/window.py:555 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:156 lib/solaar/listener.py:243 msgid "connected" msgstr "соединено" #: lib/logitech_receiver/notifications.py:156 lib/solaar/listener.py:243 msgid "disconnected" msgstr "разъединено" #: lib/logitech_receiver/notifications.py:182 msgid "unpaired" msgstr "не сопряжено" #: lib/logitech_receiver/notifications.py:231 msgid "powered on" msgstr "включено" #: lib/logitech_receiver/notifications.py:283 msgid "ADC measurement notification" msgstr "Уведомление об измерении ADC" #: lib/logitech_receiver/notifications.py:428 #: lib/logitech_receiver/notifications.py:483 msgid "pairing lock is closed" msgstr "сопряжение заблокировано" #: lib/logitech_receiver/notifications.py:428 #: lib/logitech_receiver/notifications.py:483 msgid "pairing lock is open" msgstr "сопряжение разблокировано" #: lib/logitech_receiver/notifications.py:446 msgid "discovery lock is closed" msgstr "обнаружение заблокировано" #: lib/logitech_receiver/notifications.py:446 msgid "discovery lock is open" msgstr "обнаружение разблокировано" #: lib/logitech_receiver/receiver.py:437 msgid "No paired devices." msgstr "Нет сопряжённых устройств." #: lib/logitech_receiver/receiver.py:439 lib/solaar/ui/window.py:606 #, 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:602 msgid "register" msgstr "зарегистрировано" #: lib/logitech_receiver/settings.py:616 lib/logitech_receiver/settings.py:654 msgid "feature" msgstr "особенность" #: lib/logitech_receiver/settings_templates.py:138 msgid "Swap Fx function" msgstr "Сменить поведение Fx" #: lib/logitech_receiver/settings_templates.py:141 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:146 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:154 msgid "Hand Detection" msgstr "Обнаружение рук" #: lib/logitech_receiver/settings_templates.py:155 msgid "Turn on illumination when the hands hover over the keyboard." msgstr "Включать подсветку, когда руки над клавиатурой." #: lib/logitech_receiver/settings_templates.py:162 msgid "Scroll Wheel Smooth Scrolling" msgstr "Плавная прокрутка колесом мыши" #: lib/logitech_receiver/settings_templates.py:163 #: lib/logitech_receiver/settings_templates.py:410 #: lib/logitech_receiver/settings_templates.py:439 msgid "High-sensitivity mode for vertical scroll with the wheel." msgstr "Режим высокой чувствительности для вертикальной прокрутки колесом." #: lib/logitech_receiver/settings_templates.py:170 msgid "Side Scrolling" msgstr "Горизонтальная прокрутка" #: lib/logitech_receiver/settings_templates.py:172 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:182 msgid "Sensitivity (DPI - older mice)" msgstr "Чувствительность (тчк/дюйм — старые мыши)" #: lib/logitech_receiver/settings_templates.py:183 #: lib/logitech_receiver/settings_templates.py:1019 #: lib/logitech_receiver/settings_templates.py:1047 msgid "Mouse movement sensitivity" msgstr "Чувствительность перемещения мыши" #: lib/logitech_receiver/settings_templates.py:256 msgid "Backlight Timed" msgstr "Подсветка по времени" #: lib/logitech_receiver/settings_templates.py:257 #: lib/logitech_receiver/settings_templates.py:397 msgid "Set illumination time for keyboard." msgstr "Настройка времени подсветки клавиатуры." #: lib/logitech_receiver/settings_templates.py:268 msgid "Backlight" msgstr "Подсветка" #: lib/logitech_receiver/settings_templates.py:269 msgid "Illumination level on keyboard. Changes made are only applied in " "Manual mode." msgstr "Уровень подсветки клавиатуры. Внесенные изменения применяются только " "в Ручном режиме." #: lib/logitech_receiver/settings_templates.py:301 msgid "Automatic" msgstr "Автоматический" #: lib/logitech_receiver/settings_templates.py:303 msgid "Manual" msgstr "Ручной" #: lib/logitech_receiver/settings_templates.py:305 msgid "Enabled" msgstr "Включенный" #: lib/logitech_receiver/settings_templates.py:311 msgid "Backlight Level" msgstr "Уровень подсветки" #: lib/logitech_receiver/settings_templates.py:312 msgid "Illumination level on keyboard when in Manual mode." msgstr "Уровень освещенности клавиатуры в ручном режиме." #: lib/logitech_receiver/settings_templates.py:369 msgid "Backlight Delay Hands Out" msgstr "Задержка подсветки" #: lib/logitech_receiver/settings_templates.py:370 msgid "Delay in seconds until backlight fades out with hands away from " "keyboard." msgstr "Задержка в секундах, пока подсветка не погаснет, если руки не будут " "лежать на клавиатуре." #: lib/logitech_receiver/settings_templates.py:378 msgid "Backlight Delay Hands In" msgstr "Задержка подсветки стрелок в" #: lib/logitech_receiver/settings_templates.py:379 msgid "Delay in seconds until backlight fades out with hands near keyboard." msgstr "Задержка в секундах, пока подсветка не погаснет, если руки находятся " "рядом с клавиатурой." #: lib/logitech_receiver/settings_templates.py:387 msgid "Backlight Delay Powered" msgstr "Включение подсветки с задержкой" #: lib/logitech_receiver/settings_templates.py:388 msgid "Delay in seconds until backlight fades out with external power." msgstr "Задержка в секундах до тех пор, пока подсветка не погаснет при " "включении внешнего питания." #: lib/logitech_receiver/settings_templates.py:396 msgid "Backlight (Seconds)" msgstr "Подсветка (секунды)" #: lib/logitech_receiver/settings_templates.py:408 msgid "Scroll Wheel High Resolution" msgstr "Высокое разрешение колеса прокрутки" #: lib/logitech_receiver/settings_templates.py:412 #: lib/logitech_receiver/settings_templates.py:441 msgid "Set to ignore if scrolling is abnormally fast or slow" msgstr "Настройка игнорирования слишком быстрой или медленной прокрутки" #: lib/logitech_receiver/settings_templates.py:419 #: lib/logitech_receiver/settings_templates.py:450 msgid "Scroll Wheel Diversion" msgstr "Поведение колеса прокрутки" #: lib/logitech_receiver/settings_templates.py:421 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:428 msgid "Scroll Wheel Direction" msgstr "Направление прокрутки" #: lib/logitech_receiver/settings_templates.py:429 msgid "Invert direction for vertical scroll with wheel." msgstr "Обратная прокрутка колесом." #: lib/logitech_receiver/settings_templates.py:437 msgid "Scroll Wheel Resolution" msgstr "Разрешение прокрутки колесом" #: lib/logitech_receiver/settings_templates.py:452 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:461 msgid "Sensitivity (Pointer Speed)" msgstr "Чувствительность (скорость указателя)" #: lib/logitech_receiver/settings_templates.py:462 msgid "Speed multiplier for mouse (256 is normal multiplier)." msgstr "Множитель скорости мыши (обычно - 256)." #: lib/logitech_receiver/settings_templates.py:472 msgid "Thumb Wheel Diversion" msgstr "Поведение бокового колеса" #: lib/logitech_receiver/settings_templates.py:474 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:483 msgid "Thumb Wheel Direction" msgstr "Направление вращения бокового колеса" #: lib/logitech_receiver/settings_templates.py:484 msgid "Invert thumb wheel scroll direction." msgstr "Изменяет направление вращения бокового колеса." #: lib/logitech_receiver/settings_templates.py:504 msgid "Onboard Profiles" msgstr "Встроенные профили" #: lib/logitech_receiver/settings_templates.py:505 msgid "Enable an onboard profile, which controls report rate, sensitivity, " "and button actions" msgstr "Включите встроенный профиль, который контролирует частоту отчетов, " "чувствительность, и действия кнопок" #: lib/logitech_receiver/settings_templates.py:549 #: lib/logitech_receiver/settings_templates.py:582 msgid "Report Rate" msgstr "Частота отчетов" #: lib/logitech_receiver/settings_templates.py:551 #: lib/logitech_receiver/settings_templates.py:584 msgid "Frequency of device movement reports" msgstr "Частота сообщений о перемещении устройства" #: lib/logitech_receiver/settings_templates.py:551 #: lib/logitech_receiver/settings_templates.py:584 #: lib/logitech_receiver/settings_templates.py:1019 #: lib/logitech_receiver/settings_templates.py:1047 #: lib/logitech_receiver/settings_templates.py:1421 #: lib/logitech_receiver/settings_templates.py:1452 msgid "May need Onboard Profiles set to Disable to be effective." msgstr "Для работы может потребоваться отключить параметр «Встроенные " "профили»." #: lib/logitech_receiver/settings_templates.py:612 msgid "Divert crown events" msgstr "Поведение верньера" #: lib/logitech_receiver/settings_templates.py:613 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:621 msgid "Crown smooth scroll" msgstr "Плавная прокрутка верньера" #: lib/logitech_receiver/settings_templates.py:622 msgid "Set crown smooth scroll" msgstr "Настройка плавной прокрутки верньера" #: lib/logitech_receiver/settings_templates.py:630 msgid "Divert G and M Keys" msgstr "Перенаправление клавиш G и M" #: lib/logitech_receiver/settings_templates.py:631 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:645 msgid "Scroll Wheel Ratcheted" msgstr "Колесо прокрутки с храповым механизмом" #: lib/logitech_receiver/settings_templates.py:646 msgid "Switch the mouse wheel between speed-controlled ratcheting and " "always freespin." msgstr "Переключение колеса мыши между вращением с храповым механизмом и " "свободным вращением." #: lib/logitech_receiver/settings_templates.py:648 msgid "Freespinning" msgstr "Свободное вращение" #: lib/logitech_receiver/settings_templates.py:648 msgid "Ratcheted" msgstr "Вращение с храповым механизмом" #: lib/logitech_receiver/settings_templates.py:655 msgid "Scroll Wheel Ratchet Speed" msgstr "Скорость вращения колеса прокрутки" #: lib/logitech_receiver/settings_templates.py:657 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:707 msgid "Scroll Wheel Ratchet Torque" msgstr "Крутящий момент храпового механизма прокручивающего колеса" #: lib/logitech_receiver/settings_templates.py:708 msgid "Change the torque needed to overcome the ratchet." msgstr "Измените крутящий момент, необходимый для преодоления храпового механизма." #: lib/logitech_receiver/settings_templates.py:743 msgid "Key/Button Actions" msgstr "Действия с клавишами/кнопками" #: lib/logitech_receiver/settings_templates.py:745 msgid "Change the action for the key or button." msgstr "Изменить действие кнопки или клавиши." #: lib/logitech_receiver/settings_templates.py:747 msgid "Overridden by diversion." msgstr "Отменено перенаправлением." #: lib/logitech_receiver/settings_templates.py:749 msgid "Changing important actions (such as for the left mouse button) can " "result in an unusable system." msgstr "(изменение важных действий — например, левой кнопки мыши - может " "вывести систему из строя)." #: lib/logitech_receiver/settings_templates.py:924 msgid "Key/Button Diversion" msgstr "Поведение клавиш/кнопок" #: lib/logitech_receiver/settings_templates.py:925 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:928 #: lib/logitech_receiver/settings_templates.py:929 #: lib/logitech_receiver/settings_templates.py:930 msgid "Diverted" msgstr "Перенаправлено" #: lib/logitech_receiver/settings_templates.py:928 #: lib/logitech_receiver/settings_templates.py:929 msgid "Mouse Gestures" msgstr "Жесты мышью" #: lib/logitech_receiver/settings_templates.py:928 #: lib/logitech_receiver/settings_templates.py:929 #: lib/logitech_receiver/settings_templates.py:930 msgid "Regular" msgstr "Обычное" #: lib/logitech_receiver/settings_templates.py:928 msgid "Sliding DPI" msgstr "Скользящий тчк/дюйм" #: lib/logitech_receiver/settings_templates.py:1018 #: lib/logitech_receiver/settings_templates.py:1046 msgid "Sensitivity (DPI)" msgstr "Чувствительность (тчк/дюйм)" #: lib/logitech_receiver/settings_templates.py:1123 msgid "Sensitivity Switching" msgstr "Переключение чувствительности" #: lib/logitech_receiver/settings_templates.py:1125 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:1129 msgid "Off" msgstr "Откл" #: lib/logitech_receiver/settings_templates.py:1160 msgid "Disable keys" msgstr "Отключить клавиши" #: lib/logitech_receiver/settings_templates.py:1161 msgid "Disable specific keyboard keys." msgstr "Отключение определённых клавиш клавиатуры." #: lib/logitech_receiver/settings_templates.py:1164 #, python-format msgid "Disables the %s key." msgstr "Отключение клавиши %s." #: lib/logitech_receiver/settings_templates.py:1177 #: lib/logitech_receiver/settings_templates.py:1234 msgid "Set OS" msgstr "Операционная система" #: lib/logitech_receiver/settings_templates.py:1178 #: lib/logitech_receiver/settings_templates.py:1235 msgid "Change keys to match OS." msgstr "Изменяет назначение клавиш соответственно ОС." #: lib/logitech_receiver/settings_templates.py:1247 msgid "Change Host" msgstr "Подключено к" #: lib/logitech_receiver/settings_templates.py:1248 msgid "Switch connection to a different host" msgstr "Переключает соединение на выбранный хост." #: lib/logitech_receiver/settings_templates.py:1272 msgid "Performs a left click." msgstr "Осуществляет щелчок левой кнопкой." #: lib/logitech_receiver/settings_templates.py:1272 msgid "Single tap" msgstr "Одиночное касание" #: lib/logitech_receiver/settings_templates.py:1273 msgid "Performs a right click." msgstr "Осуществляет щелчок правой кнопкой." #: lib/logitech_receiver/settings_templates.py:1273 msgid "Single tap with two fingers" msgstr "Одиночное касание двумя пальцами" #: lib/logitech_receiver/settings_templates.py:1274 msgid "Single tap with three fingers" msgstr "Одиночное касание тремя пальцами" #: lib/logitech_receiver/settings_templates.py:1278 msgid "Double tap" msgstr "Двойное касание" #: lib/logitech_receiver/settings_templates.py:1278 msgid "Performs a double click." msgstr "Осуществление двойного щелчка." #: lib/logitech_receiver/settings_templates.py:1279 msgid "Double tap with two fingers" msgstr "Двойное касание двумя пальцами" #: lib/logitech_receiver/settings_templates.py:1280 msgid "Double tap with three fingers" msgstr "Двойное касание тремя пальцами" #: lib/logitech_receiver/settings_templates.py:1283 msgid "Drags items by dragging the finger after double tapping." msgstr "Перетаскивать объекты перемещением пальца после двойного касания." #: lib/logitech_receiver/settings_templates.py:1283 msgid "Tap and drag" msgstr "Касание и перетаскивание" #: lib/logitech_receiver/settings_templates.py:1285 msgid "Tap and drag with two fingers" msgstr "Касание и перетаскивание двумя пальцами" #: lib/logitech_receiver/settings_templates.py:1286 msgid "Drags items by dragging the fingers after double tapping." msgstr "Перетаскивать объекты перемещением пальцев после двойного касания." #: lib/logitech_receiver/settings_templates.py:1288 msgid "Tap and drag with three fingers" msgstr "Касание и перетаскивание тремя пальцами" #: lib/logitech_receiver/settings_templates.py:1291 msgid "Suppress tap and edge gestures" msgstr "Подавление касаний и жестов по краям" #: lib/logitech_receiver/settings_templates.py:1292 msgid "Disables tap and edge gestures (equivalent to pressing Fn+LeftClick)." msgstr "Отключает касания и жесты по краям (то же что FN+ЩелчокЛевойКнопкой)." #: lib/logitech_receiver/settings_templates.py:1294 msgid "Scroll with one finger" msgstr "Вертикальная прокрутка одним пальцем" #: lib/logitech_receiver/settings_templates.py:1294 #: lib/logitech_receiver/settings_templates.py:1295 #: lib/logitech_receiver/settings_templates.py:1298 msgid "Scrolls." msgstr "Прокручивать вертикально." #: lib/logitech_receiver/settings_templates.py:1295 #: lib/logitech_receiver/settings_templates.py:1298 msgid "Scroll with two fingers" msgstr "Вертикальная прокрутка двумя пальцами" #: lib/logitech_receiver/settings_templates.py:1296 msgid "Scroll horizontally with two fingers" msgstr "Горизонтальная прокрутка двумя пальцами" #: lib/logitech_receiver/settings_templates.py:1296 msgid "Scrolls horizontally." msgstr "Прокручивать горизонтально." #: lib/logitech_receiver/settings_templates.py:1297 msgid "Scroll vertically with two fingers" msgstr "Вертикальная прокрутка двумя пальцами" #: lib/logitech_receiver/settings_templates.py:1297 msgid "Scrolls vertically." msgstr "Прокручивать вертикально." #: lib/logitech_receiver/settings_templates.py:1299 msgid "Inverts the scrolling direction." msgstr "Инвертирует направления прокрутки." #: lib/logitech_receiver/settings_templates.py:1299 msgid "Natural scrolling" msgstr "Естественная прокрутка" #: lib/logitech_receiver/settings_templates.py:1300 msgid "Enables the thumbwheel." msgstr "Включение бокового колеса прокрутки." #: lib/logitech_receiver/settings_templates.py:1300 msgid "Thumbwheel" msgstr "Боковое колесо прокрутки" #: lib/logitech_receiver/settings_templates.py:1311 #: lib/logitech_receiver/settings_templates.py:1315 msgid "Swipe from the top edge" msgstr "Листание от верхнего края" #: lib/logitech_receiver/settings_templates.py:1312 msgid "Swipe from the left edge" msgstr "Листание от левого края" #: lib/logitech_receiver/settings_templates.py:1313 msgid "Swipe from the right edge" msgstr "Листание от правого края" #: lib/logitech_receiver/settings_templates.py:1314 msgid "Swipe from the bottom edge" msgstr "Листание от нижнего края" #: lib/logitech_receiver/settings_templates.py:1316 msgid "Swipe two fingers from the left edge" msgstr "Листание двумя пальцами от левого края" #: lib/logitech_receiver/settings_templates.py:1317 msgid "Swipe two fingers from the right edge" msgstr "Листание двумя пальцами от правого края" #: lib/logitech_receiver/settings_templates.py:1318 msgid "Swipe two fingers from the bottom edge" msgstr "Листание двумя пальцами от нижнего края" #: lib/logitech_receiver/settings_templates.py:1319 msgid "Swipe two fingers from the top edge" msgstr "Листание двумя пальцами от верхнего края" #: lib/logitech_receiver/settings_templates.py:1320 #: lib/logitech_receiver/settings_templates.py:1324 msgid "Pinch to zoom out; spread to zoom in." msgstr "Щипок - уменьшить, раздвинуть - увеличить." #: lib/logitech_receiver/settings_templates.py:1320 msgid "Zoom with two fingers." msgstr "Масштабирование двумя пальцами." #: lib/logitech_receiver/settings_templates.py:1321 msgid "Pinch to zoom out." msgstr "Щипок - уменьшить." #: lib/logitech_receiver/settings_templates.py:1322 msgid "Spread to zoom in." msgstr "Раздвинуть - увеличить." #: lib/logitech_receiver/settings_templates.py:1323 msgid "Zoom with three fingers." msgstr "Масштабирование тремя пальцами." #: lib/logitech_receiver/settings_templates.py:1324 msgid "Zoom with two fingers" msgstr "Масштабирование двумя пальцами" #: lib/logitech_receiver/settings_templates.py:1342 msgid "Pixel zone" msgstr "Зона пикселей" #: lib/logitech_receiver/settings_templates.py:1343 msgid "Ratio zone" msgstr "Зона масштаба" #: lib/logitech_receiver/settings_templates.py:1344 msgid "Scale factor" msgstr "Коэффициент масштабирования" #: lib/logitech_receiver/settings_templates.py:1344 msgid "Sets the cursor speed." msgstr "Установка скорости курсора." #: lib/logitech_receiver/settings_templates.py:1348 msgid "Left" msgstr "Лево" #: lib/logitech_receiver/settings_templates.py:1348 msgid "Left-most coordinate." msgstr "Самая левая позиция." #: lib/logitech_receiver/settings_templates.py:1349 msgid "Bottom" msgstr "Низ" #: lib/logitech_receiver/settings_templates.py:1349 msgid "Bottom coordinate." msgstr "Нижняя позиция." #: lib/logitech_receiver/settings_templates.py:1350 msgid "Width" msgstr "Ширина" #: lib/logitech_receiver/settings_templates.py:1350 msgid "Width." msgstr "Ширина." #: lib/logitech_receiver/settings_templates.py:1351 msgid "Height" msgstr "Высота" #: lib/logitech_receiver/settings_templates.py:1351 msgid "Height." msgstr "Высота." #: lib/logitech_receiver/settings_templates.py:1352 msgid "Cursor speed." msgstr "Скорость курсора." #: lib/logitech_receiver/settings_templates.py:1352 msgid "Scale" msgstr "Масштаб" #: lib/logitech_receiver/settings_templates.py:1358 msgid "Gestures" msgstr "Жесты" #: lib/logitech_receiver/settings_templates.py:1359 msgid "Tweak the mouse/touchpad behaviour." msgstr "Регулировка поведения мыши/сенсорной панели." #: lib/logitech_receiver/settings_templates.py:1375 msgid "Gestures Diversion" msgstr "Поведение жестов" #: lib/logitech_receiver/settings_templates.py:1376 msgid "Divert mouse/touchpad gestures." msgstr "Регулировка поведения жестов мыши/сенсорной панели." #: lib/logitech_receiver/settings_templates.py:1392 msgid "Gesture params" msgstr "Параметры жестов" #: lib/logitech_receiver/settings_templates.py:1393 msgid "Change numerical parameters of a mouse/touchpad." msgstr "Изменение числовых параметров мыши/сенсорной панели." #: lib/logitech_receiver/settings_templates.py:1417 msgid "M-Key LEDs" msgstr "Светодиоды клавиш M" #: lib/logitech_receiver/settings_templates.py:1419 msgid "Control the M-Key LEDs." msgstr "Управление светодиодами клавиш M." #: lib/logitech_receiver/settings_templates.py:1423 #: lib/logitech_receiver/settings_templates.py:1454 msgid "May need G Keys diverted to be effective." msgstr "Возможно, для эффективности потребуется перенаправить G-клавиши." #: lib/logitech_receiver/settings_templates.py:1429 #, python-format msgid "Lights up the %s key." msgstr "Подсветка клавиши %s." #: lib/logitech_receiver/settings_templates.py:1448 msgid "MR-Key LED" msgstr "Светодиод клавиши MR" #: lib/logitech_receiver/settings_templates.py:1450 msgid "Control the MR-Key LED." msgstr "Управление светодиодом клавиши MR." #: lib/logitech_receiver/settings_templates.py:1471 msgid "Persistent Key/Button Mapping" msgstr "Постоянное сопоставление клавиши или кнопки" #: lib/logitech_receiver/settings_templates.py:1473 msgid "Permanently change the mapping for the key or button." msgstr "Навсегда изменить сопоставление для клавиши или кнопки." #: lib/logitech_receiver/settings_templates.py:1475 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:1532 msgid "Sidetone" msgstr "Самопрослушивание" #: lib/logitech_receiver/settings_templates.py:1533 msgid "Set sidetone level." msgstr "Установите уровень самопрослушивания." #: lib/logitech_receiver/settings_templates.py:1542 msgid "Equalizer" msgstr "Эквалайзер" #: lib/logitech_receiver/settings_templates.py:1543 msgid "Set equalizer levels." msgstr "Установите уровни эквалайзера." #: lib/logitech_receiver/settings_templates.py:1565 msgid "Hz" msgstr "Гц" #: lib/logitech_receiver/settings_templates.py:1571 msgid "Power Management" msgstr "Управление питанием" #: lib/logitech_receiver/settings_templates.py:1572 msgid "Power off in minutes (0 for never)." msgstr "Выключите питание через несколько минут (0 означает \"никогда\")." #: lib/logitech_receiver/settings_templates.py:1584 msgid "Brightness Control" msgstr "Регулировка яркости" #: lib/logitech_receiver/settings_templates.py:1585 msgid "Control overall brightness" msgstr "Контролируйте общую яркость" #: lib/logitech_receiver/settings_templates.py:1628 #: lib/logitech_receiver/settings_templates.py:1681 msgid "LED Control" msgstr "Светодиодное управление" #: lib/logitech_receiver/settings_templates.py:1629 #: lib/logitech_receiver/settings_templates.py:1682 msgid "Switch control of LED zones between device and Solaar" msgstr "Переключайте управление светодиодными зонами между устройством и " "Solaar" #: lib/logitech_receiver/settings_templates.py:1644 #: lib/logitech_receiver/settings_templates.py:1692 msgid "LED Zone Effects" msgstr "Эффекты светодиодной зоны" #: lib/logitech_receiver/settings_templates.py:1645 #: lib/logitech_receiver/settings_templates.py:1693 msgid "LED Control needs to be set to Solaar to be effective." msgstr "Для эффективного управления светодиодом необходимо установить режим " "Solaar." #: lib/logitech_receiver/settings_templates.py:1645 #: lib/logitech_receiver/settings_templates.py:1693 msgid "Set effect for LED Zone" msgstr "Установите эффект для светодиодной зоны" #: lib/logitech_receiver/settings_templates.py:1647 msgid "Color" msgstr "Цвет" #: lib/logitech_receiver/settings_templates.py:1648 msgid "Speed" msgstr "Скорость" #: lib/logitech_receiver/settings_templates.py:1649 msgid "Period" msgstr "Период" #: lib/logitech_receiver/settings_templates.py:1650 msgid "Intensity" msgstr "Интенсивность" #: lib/logitech_receiver/settings_templates.py:1651 msgid "Ramp" msgstr "Рампа" #: lib/logitech_receiver/settings_templates.py:1666 msgid "LEDs" msgstr "Светодиоды" #: lib/logitech_receiver/settings_templates.py:1703 msgid "Per-key Lighting" msgstr "Индивидуальное освещение" #: lib/logitech_receiver/settings_templates.py:1704 msgid "Control per-key lighting." msgstr "Управление подсветкой каждой клавиши." #: lib/logitech_receiver/settings_templates.py:1786 msgid "Force Sensing Buttons" msgstr "Кнопки с датчиками силы нажатия" #: lib/logitech_receiver/settings_templates.py:1787 msgid "Change the force required to activate button." msgstr "Измените усилие, необходимое для активации кнопки." #: lib/logitech_receiver/settings_templates.py:1804 msgid "Force Sensing Button" msgstr "Кнопка измерения усилия" #: lib/logitech_receiver/settings_templates.py:1814 msgid "Haptic Feeback Level" msgstr "Уровень тактильной обратной связи" #: lib/logitech_receiver/settings_templates.py:1815 msgid "Change power of haptic feedback. (Zero to turn off.)" msgstr "Измените мощность тактильной обратной связи. (Ноль - отключение.)" #: lib/logitech_receiver/settings_templates.py:1857 msgid "Play Haptic Waveform" msgstr "Воспроизведение тактильной формы сигнала" #: lib/logitech_receiver/settings_templates.py:1858 msgid "Tell device to play a haptic waveform." msgstr "Дайте команду устройству воспроизвести тактильный сигнал." #: lib/solaar/ui/__init__.py:120 msgid "Another Solaar process is already running so just expose its window" msgstr "Другой Solaar процесс уже запущен, так что просто откройте его окно" #: lib/solaar/ui/about/model.py:36 msgid "Manages Logitech receivers,\n" "keyboards, mice, and tablets." msgstr "Управление приёмниками, клавиатурами,\n" "мышами и другими устройствами Logitech." #: lib/solaar/ui/about/model.py:63 msgid "Additional Programming" msgstr "Дополнительные параметры" #: lib/solaar/ui/about/model.py:64 msgid "GUI design" msgstr "Дизайн интерфейса" #: lib/solaar/ui/about/model.py:66 msgid "Testing" msgstr "Тестирование" #: lib/solaar/ui/about/model.py:74 msgid "Logitech documentation" msgstr "Документация Logitech" #: lib/solaar/ui/action.py:88 lib/solaar/ui/action.py:92 #: lib/solaar/ui/window.py:208 msgid "Unpair" msgstr "Отмена сопряжения" #: lib/solaar/ui/action.py:91 lib/solaar/ui/diversion_rules.py:141 msgid "Cancel" msgstr "Отмена" #: lib/solaar/ui/common.py:42 msgid "Permissions error" msgstr "Ошибка доступа" #: lib/solaar/ui/common.py:44 #, 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:46 msgid "If you've just installed Solaar, try disconnecting the receiver or " "device and then reconnecting it." msgstr "Если вы только что установили Solaar, попробуйте отключить и снова " "подключить приёмник или устройство." #: lib/solaar/ui/common.py:49 msgid "Cannot connect to device error" msgstr "Невозможно подключиться к устройству" #: lib/solaar/ui/common.py:51 #, 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:53 msgid "Try disconnecting the device and then reconnecting it or turning it " "off and then on." msgstr "Попробуйте отключить и снова подключить устройство или выключить и " "затем снова включить его." #: lib/solaar/ui/common.py:56 msgid "Unpairing failed" msgstr "Отмена сопряжения не удалась" #: lib/solaar/ui/common.py:58 #, python-brace-format msgid "Failed to unpair %{device} from %{receiver}." msgstr "Отмена сопряжения %{device} с %{receiver} не удалась." #: lib/solaar/ui/common.py:63 msgid "The receiver returned an error, with no further details." msgstr "Приёмник вернул ошибку без дополнительных сведений." #: lib/solaar/ui/config_panel.py:245 msgid "Complete - ENTER to change" msgstr "Завершено - «ВВОД» для изменения" #: lib/solaar/ui/config_panel.py:245 msgid "Incomplete" msgstr "Незавершено" #: lib/solaar/ui/config_panel.py:491 lib/solaar/ui/config_panel.py:543 #, python-format msgid "%d value" msgid_plural "%d values" msgstr[0] "%d значение" msgstr[1] "%d значения" msgstr[2] "%d значений" #: lib/solaar/ui/config_panel.py:642 msgid "Changes allowed" msgstr "Изменения разрешены" #: lib/solaar/ui/config_panel.py:643 msgid "No changes allowed" msgstr "Изменения запрещены" #: lib/solaar/ui/config_panel.py:644 msgid "Ignore this setting" msgstr "Игнорировать данный параметр" #: lib/solaar/ui/config_panel.py:690 msgid "Working" msgstr "Работает" #: lib/solaar/ui/config_panel.py:693 msgid "Read/write operation failed." msgstr "Операция чтения/записи не удалась." #: lib/solaar/ui/desktop_notifications.py:112 msgid "unspecified reason" msgstr "неустановленная причина" #: lib/solaar/ui/diversion_rules.py:103 msgid "Built-in rules" msgstr "Встроенные правила" #: lib/solaar/ui/diversion_rules.py:103 msgid "User-defined rules" msgstr "Пользовательские правила" #: lib/solaar/ui/diversion_rules.py:105 lib/solaar/ui/diversion_rules.py:1126 msgid "Rule" msgstr "Правило" #: lib/solaar/ui/diversion_rules.py:106 lib/solaar/ui/diversion_rules.py:382 #: lib/solaar/ui/diversion_rules.py:509 msgid "Sub-rule" msgstr "Подправило" #: lib/solaar/ui/diversion_rules.py:108 msgid "[empty]" msgstr "[пусто]" #: lib/solaar/ui/diversion_rules.py:132 msgid "Make changes permanent?" msgstr "Сохранить изменения навсегда?" #: lib/solaar/ui/diversion_rules.py:137 msgid "Yes" msgstr "Да" #: lib/solaar/ui/diversion_rules.py:139 msgid "No" msgstr "Нет" #: lib/solaar/ui/diversion_rules.py:144 msgid "If you choose No, changes will be lost when Solaar is closed." msgstr "Если выбрать «Нет», при закрытии программы изменения пропадут." #: lib/solaar/ui/diversion_rules.py:273 msgid "Paste here" msgstr "Скопировать сюда" #: lib/solaar/ui/diversion_rules.py:275 msgid "Paste above" msgstr "Скопировать выше" #: lib/solaar/ui/diversion_rules.py:277 msgid "Paste below" msgstr "Скопировать ниже" #: lib/solaar/ui/diversion_rules.py:283 msgid "Paste rule here" msgstr "Скопировать правило сюда" #: lib/solaar/ui/diversion_rules.py:285 msgid "Paste rule above" msgstr "Скопировать правило выше" #: lib/solaar/ui/diversion_rules.py:287 msgid "Paste rule below" msgstr "Скопировать правило ниже" #: lib/solaar/ui/diversion_rules.py:291 msgid "Paste rule" msgstr "Скопировать правило" #: lib/solaar/ui/diversion_rules.py:306 msgid "Insert here" msgstr "Вставить сюда" #: lib/solaar/ui/diversion_rules.py:308 msgid "Insert above" msgstr "Вставить выше" #: lib/solaar/ui/diversion_rules.py:310 msgid "Insert below" msgstr "Вставить ниже" #: lib/solaar/ui/diversion_rules.py:316 msgid "Insert new rule here" msgstr "Вставить новое правило сюда" #: lib/solaar/ui/diversion_rules.py:318 msgid "Insert new rule above" msgstr "Вставить новое правило выше" #: lib/solaar/ui/diversion_rules.py:320 msgid "Insert new rule below" msgstr "Вставить новое правило ниже" #: lib/solaar/ui/diversion_rules.py:347 msgid "Flatten" msgstr "Снять обёртку" #: lib/solaar/ui/diversion_rules.py:380 msgid "Insert" msgstr "Вставка" #: lib/solaar/ui/diversion_rules.py:383 lib/solaar/ui/diversion_rules.py:511 #: lib/solaar/ui/diversion_rules.py:1158 msgid "Or" msgstr "Или" #: lib/solaar/ui/diversion_rules.py:384 lib/solaar/ui/diversion_rules.py:510 #: lib/solaar/ui/diversion_rules.py:1144 msgid "And" msgstr "И" #: lib/solaar/ui/diversion_rules.py:386 msgid "Condition" msgstr "Компонент" #: lib/solaar/ui/diversion_rules.py:388 lib/solaar/ui/rule_conditions.py:154 msgid "Feature" msgstr "Feature" #: lib/solaar/ui/diversion_rules.py:389 lib/solaar/ui/rule_conditions.py:188 msgid "Report" msgstr "Отчёт" #: lib/solaar/ui/diversion_rules.py:390 lib/solaar/ui/rule_conditions.py:69 msgid "Process" msgstr "Процесс" #: lib/solaar/ui/diversion_rules.py:391 msgid "Mouse process" msgstr "Действие мыши" #: lib/solaar/ui/diversion_rules.py:392 lib/solaar/ui/rule_conditions.py:225 msgid "Modifiers" msgstr "Модификаторы" #: lib/solaar/ui/diversion_rules.py:393 lib/solaar/ui/rule_conditions.py:277 msgid "Key" msgstr "Клавиша" #: lib/solaar/ui/diversion_rules.py:394 lib/solaar/ui/rule_conditions.py:318 msgid "KeyIsDown" msgstr "Клавиша нажата" #: lib/solaar/ui/diversion_rules.py:395 lib/solaar/ui/diversion_rules.py:1432 msgid "Active" msgstr "Активный" #: lib/solaar/ui/diversion_rules.py:396 lib/solaar/ui/diversion_rules.py:1390 #: lib/solaar/ui/diversion_rules.py:1441 lib/solaar/ui/diversion_rules.py:1487 msgid "Device" msgstr "Устройство" #: lib/solaar/ui/diversion_rules.py:397 lib/solaar/ui/diversion_rules.py:1467 msgid "Host" msgstr "Хост" #: lib/solaar/ui/diversion_rules.py:398 lib/solaar/ui/diversion_rules.py:1506 msgid "Setting" msgstr "Установка" #: lib/solaar/ui/diversion_rules.py:399 lib/solaar/ui/rule_conditions.py:333 #: lib/solaar/ui/rule_conditions.py:382 msgid "Test" msgstr "Условие" #: lib/solaar/ui/diversion_rules.py:400 lib/solaar/ui/rule_conditions.py:507 msgid "Test bytes" msgstr "Тестовые байты" #: lib/solaar/ui/diversion_rules.py:401 lib/solaar/ui/rule_conditions.py:608 msgid "Mouse Gesture" msgstr "Жест мышью" #: lib/solaar/ui/diversion_rules.py:405 msgid "Action" msgstr "Действие" #: lib/solaar/ui/diversion_rules.py:407 lib/solaar/ui/rule_actions.py:138 msgid "Key press" msgstr "Нажатие клавиши" #: lib/solaar/ui/diversion_rules.py:408 lib/solaar/ui/rule_actions.py:191 msgid "Mouse scroll" msgstr "Прокрутка мышью" #: lib/solaar/ui/diversion_rules.py:409 lib/solaar/ui/rule_actions.py:256 msgid "Mouse click" msgstr "Щелчок мышью" #: lib/solaar/ui/diversion_rules.py:410 msgid "Set" msgstr "Установить" #: lib/solaar/ui/diversion_rules.py:411 lib/solaar/ui/rule_actions.py:328 msgid "Execute" msgstr "Исполнить" #: lib/solaar/ui/diversion_rules.py:412 lib/solaar/ui/diversion_rules.py:1189 msgid "Later" msgstr "Задержка" #: lib/solaar/ui/diversion_rules.py:441 msgid "Insert new rule" msgstr "Добавить правило" #: lib/solaar/ui/diversion_rules.py:461 lib/solaar/ui/rule_actions.py:82 #: lib/solaar/ui/rule_actions.py:287 lib/solaar/ui/rule_conditions.py:556 msgid "Delete" msgstr "Удалить" #: lib/solaar/ui/diversion_rules.py:483 msgid "Negate" msgstr "Инвертировать" #: lib/solaar/ui/diversion_rules.py:507 msgid "Wrap with" msgstr "Завернуть в" #: lib/solaar/ui/diversion_rules.py:537 msgid "Cut" msgstr "Вырезать" #: lib/solaar/ui/diversion_rules.py:553 msgid "Paste" msgstr "Вставить" #: lib/solaar/ui/diversion_rules.py:559 msgid "Copy" msgstr "Копировать" #: lib/solaar/ui/diversion_rules.py:568 msgid "Solaar Rule Editor" msgstr "Редактор правил" #: lib/solaar/ui/diversion_rules.py:668 msgid "Save changes" msgstr "Сохранить изменения" #: lib/solaar/ui/diversion_rules.py:673 msgid "Discard changes" msgstr "Отменить изменения" #: lib/solaar/ui/diversion_rules.py:1104 msgid "This editor does not support the selected rule component yet." msgstr "Редактор пока не поддерживает выбранную компоненту правила." #: lib/solaar/ui/diversion_rules.py:1169 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:1207 msgid "Not" msgstr "Не" #: lib/solaar/ui/diversion_rules.py:1238 msgid "Toggle" msgstr "Переключение" #: lib/solaar/ui/diversion_rules.py:1239 msgid "True" msgstr "Истина" #: lib/solaar/ui/diversion_rules.py:1240 msgid "False" msgstr "Ложь" #: lib/solaar/ui/diversion_rules.py:1253 msgid "Unsupported setting" msgstr "Неподдерживаемый параметр" #: lib/solaar/ui/diversion_rules.py:1396 lib/solaar/ui/diversion_rules.py:1415 #: lib/solaar/ui/diversion_rules.py:1493 lib/solaar/ui/diversion_rules.py:1748 #: lib/solaar/ui/diversion_rules.py:1766 msgid "Originating device" msgstr "Исходное устройство" #: lib/solaar/ui/diversion_rules.py:1428 msgid "Device is active and its settings can be changed." msgstr "Устройство активно, и его настройки могут быть изменены." #: lib/solaar/ui/diversion_rules.py:1437 msgid "Device that originated the current notification." msgstr "Устройство, отправившее текущее уведомление." #: lib/solaar/ui/diversion_rules.py:1450 msgid "Name of host computer." msgstr "Имя главного компьютера." #: lib/solaar/ui/diversion_rules.py:1520 msgid "Value" msgstr "Значение" #: lib/solaar/ui/diversion_rules.py:1529 msgid "Item" msgstr "Элемент" #: lib/solaar/ui/diversion_rules.py:1808 msgid "Change setting on device" msgstr "Изменить настройку на устройстве" #: lib/solaar/ui/diversion_rules.py:1824 msgid "Setting on device" msgstr "Настройка на устройстве" #: lib/solaar/ui/pair_window.py:44 lib/solaar/ui/pair_window.py:167 #, python-format msgid "%(receiver_name)s: pair new device" msgstr "%(receiver_name)s: новое сопряжённое устройство" #: lib/solaar/ui/pair_window.py:46 msgid "Bolt receivers are only compatible with Bolt devices." msgstr "Приемники Bolt совместимы только с устройствами Bolt." #: lib/solaar/ui/pair_window.py:48 msgid "Press a pairing button or key until the pairing light flashes " "quickly." msgstr "Нажимайте кнопку или клавишу сопряжения, пока индикатор сопряжения " "не начнёт быстро мигать." #: lib/solaar/ui/pair_window.py:51 msgid "Unifying receivers are only compatible with Unifying devices." msgstr "Приемники Unifying совместимы только с устройствами Unifying." #: lib/solaar/ui/pair_window.py:53 msgid "Other receivers are only compatible with a few devices." msgstr "Другие ресиверы совместимы только с некоторыми устройствами." #: lib/solaar/ui/pair_window.py:55 msgid "For most devices, turn on the device you want to pair." msgstr "Обычно достаточно включить устройство, с которым вы хотите " "установить соединение." #: lib/solaar/ui/pair_window.py:56 msgid "If the device is already turned on, turn it off and on again." msgstr "Если устройство включено, выключите его и включите снова." #: lib/solaar/ui/pair_window.py:58 msgid "The device must not be paired with a nearby powered-on receiver." msgstr "Устройство не должно быть сопряжено с находящимся поблизости " "включенным приемником." #: lib/solaar/ui/pair_window.py:61 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 "Для устройств с несколькими каналами нажмите, удерживайте и " "отпустите кнопку соответствующего канала, который требуется " "подключить, или используйте кнопку переключения каналов, чтобы " "выбрать нужный канал, затем нажмите, удерживайте и отпустите кнопку " "переключения каналов." #: lib/solaar/ui/pair_window.py:68 msgid "The channel indicator light should be blinking rapidly." msgstr "Индикатор канала должен быстро мигать." #: lib/solaar/ui/pair_window.py:72 #, 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:78 msgid "\n" "Cancelling at this point will not use up a pairing." msgstr "\n" "Прерывание в данный момент отменит сопряжение." #: lib/solaar/ui/pair_window.py:168 #, python-format msgid "Enter passcode on %(name)s." msgstr "Введите код доступа на %(name)s." #: lib/solaar/ui/pair_window.py:171 #, python-format msgid "Type %(passcode)s and then press the enter key." msgstr "Введите %(passcode)s и затем нажмите клавишу «Ввод»." #: lib/solaar/ui/pair_window.py:176 msgid "left" msgstr "слева" #: lib/solaar/ui/pair_window.py:176 msgid "right" msgstr "справа" #: lib/solaar/ui/pair_window.py:178 #, 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:221 msgid "The wireless link is not encrypted" msgstr "Беспроводное соединение не зашифровано" #: lib/solaar/ui/pair_window.py:226 msgid "Found a new device:" msgstr "Найдено новое устройство:" #: lib/solaar/ui/pair_window.py:247 msgid "Pairing failed" msgstr "Сопряжение не удалось" #: lib/solaar/ui/pair_window.py:249 msgid "Make sure your device is within range, and has a decent battery " "charge." msgstr "Удостоверьтесь, что ваше устройство в радиусе приёма и батарея " "заряжена." #: lib/solaar/ui/pair_window.py:251 msgid "A new device was detected, but it is not compatible with this " "receiver." msgstr "Обнаружено новое устройство, несовместимое с данным приёмником." #: lib/solaar/ui/pair_window.py:253 msgid "More paired devices than receiver can support." msgstr "Превышено число сопрягаемых устройств, поддерживаемое приёмником." #: lib/solaar/ui/pair_window.py:255 msgid "No further details are available about the error." msgstr "Дополнительные сведения об этой ошибке отсутствуют." #: lib/solaar/ui/rule_actions.py:54 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:60 msgid "Add key" msgstr "Добавить клавишу" #: lib/solaar/ui/rule_actions.py:63 msgid "Click" msgstr "Щелчок" #: lib/solaar/ui/rule_actions.py:66 msgid "Depress" msgstr "Подавить" #: lib/solaar/ui/rule_actions.py:69 msgid "Release" msgstr "Выпускать" #: lib/solaar/ui/rule_actions.py:153 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:211 msgid "Simulate a mouse click.\n" "On Wayland requires write access to /dev/uinput." msgstr "Имитация щелчка мыши.В Wayland требуется доступ на запись в /dev/" "uinput." #: lib/solaar/ui/rule_actions.py:216 msgid "Button" msgstr "Кнопка" #: lib/solaar/ui/rule_actions.py:218 msgid "Action (and Count, if click)" msgstr "Действие (и количество, если кликнуть)" #: lib/solaar/ui/rule_actions.py:269 msgid "Execute a command with arguments." msgstr "Выполните команду с аргументами." #: lib/solaar/ui/rule_actions.py:273 msgid "Add argument" msgstr "Добавить аргумент" #: lib/solaar/ui/rule_conditions.py:52 msgid "X11 active process. For use in X11 only." msgstr "Активный процесс X11. Только для использования в X11." #: lib/solaar/ui/rule_conditions.py:82 msgid "X11 mouse process. For use in X11 only." msgstr "Процесс работы с мышью X11. Только для использования в X11." #: lib/solaar/ui/rule_conditions.py:99 msgid "MouseProcess" msgstr "Действие мыши" #: lib/solaar/ui/rule_conditions.py:123 msgid "Feature name of notification triggering rule processing." msgstr "Название функции обработки правила запуска уведомления." #: lib/solaar/ui/rule_conditions.py:169 msgid "Report number of notification triggering rule processing." msgstr "Сообщите номер уведомления, запускающего обработку правила." #: lib/solaar/ui/rule_conditions.py:201 msgid "Active keyboard modifiers. Not always available in Wayland." msgstr "Активные модификаторы клавиатуры. Не всегда доступны в Wayland." #: lib/solaar/ui/rule_conditions.py:241 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:250 msgid "Key down" msgstr "Нажать клавишу" #: lib/solaar/ui/rule_conditions.py:253 msgid "Key up" msgstr "Отпустить клавишу" #: lib/solaar/ui/rule_conditions.py:293 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:331 msgid "Test condition on notification triggering rule processing." msgstr "Тестовое условие для обработки правила запуска уведомления." #: lib/solaar/ui/rule_conditions.py:335 msgid "Parameter" msgstr "Параметр" #: lib/solaar/ui/rule_conditions.py:408 msgid "begin (inclusive)" msgstr "начало (включительно)" #: lib/solaar/ui/rule_conditions.py:409 msgid "end (exclusive)" msgstr "конец (исключительно)" #: lib/solaar/ui/rule_conditions.py:417 msgid "range" msgstr "диапазон" #: lib/solaar/ui/rule_conditions.py:420 msgid "minimum" msgstr "минимум" #: lib/solaar/ui/rule_conditions.py:421 msgid "maximum" msgstr "максимум" #: lib/solaar/ui/rule_conditions.py:423 #, 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:426 lib/solaar/ui/rule_conditions.py:427 msgid "mask" msgstr "маска" #: lib/solaar/ui/rule_conditions.py:428 #, 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:437 msgid "Bit or range test on bytes in notification message triggering rule " "processing." msgstr "Проверка битов или диапазона на байтах в уведомительном сообщении, " "запускающем обработку правила." #: lib/solaar/ui/rule_conditions.py:447 msgid "type" msgstr "тип" #: lib/solaar/ui/rule_conditions.py:535 msgid "Mouse gesture with optional initiating button followed by zero or " "more mouse movements." msgstr "Жест мыши с дополнительной кнопкой запуска, за которым следует ноль " "или более движений мыши." #: lib/solaar/ui/rule_conditions.py:541 msgid "Add movement" msgstr "Добавить движение" #: lib/solaar/ui/tray.py:55 msgid "No supported device found" msgstr "Поддерживаемое устройство не найдено" #: lib/solaar/ui/tray.py:60 lib/solaar/ui/window.py:328 #, python-format msgid "About %s" msgstr "О программе %s" #: lib/solaar/ui/tray.py:61 lib/solaar/ui/window.py:326 #, python-format msgid "Quit %s" msgstr "Выйти из %s" #: lib/solaar/ui/tray.py:273 lib/solaar/ui/tray.py:281 msgid "no receiver" msgstr "отсутствует приёмник" #: lib/solaar/ui/tray.py:294 lib/solaar/ui/tray.py:299 #: lib/solaar/ui/window.py:728 msgid "offline" msgstr "нет соединения" #: lib/solaar/ui/tray.py:297 msgid "no status" msgstr "отсутствует статус" #: lib/solaar/ui/window.py:110 msgid "Scanning" msgstr "Сканирование" #: lib/solaar/ui/window.py:141 msgid "Battery" msgstr "Батарея" #: lib/solaar/ui/window.py:144 msgid "Wireless Link" msgstr "Беспроводное соединение" #: lib/solaar/ui/window.py:148 msgid "Lighting" msgstr "Освещённость" #: lib/solaar/ui/window.py:182 msgid "Show Technical Details" msgstr "Технические сведения об устройстве" #: lib/solaar/ui/window.py:198 msgid "Pair new device" msgstr "Новое сопряженное устройство" #: lib/solaar/ui/window.py:216 msgid "Select a device" msgstr "Выбор устройства" #: lib/solaar/ui/window.py:331 msgid "Rule Editor" msgstr "Редактор правил" #: lib/solaar/ui/window.py:522 msgid "Path" msgstr "Расположение" #: lib/solaar/ui/window.py:524 msgid "USB ID" msgstr "Идент. USB" #: lib/solaar/ui/window.py:527 lib/solaar/ui/window.py:529 #: lib/solaar/ui/window.py:544 lib/solaar/ui/window.py:546 msgid "Serial" msgstr "Серийный №" #: lib/solaar/ui/window.py:533 msgid "Index" msgstr "Индекс" #: lib/solaar/ui/window.py:535 msgid "Wireless PID" msgstr "Б/проводн.PID" #: lib/solaar/ui/window.py:537 msgid "Product ID" msgstr "ID изделия" #: lib/solaar/ui/window.py:539 msgid "Protocol" msgstr "Протокол" #: lib/solaar/ui/window.py:539 msgid "Unknown" msgstr "Неизвестен" #: lib/solaar/ui/window.py:541 msgid "Polling rate" msgstr "Част. опроса" #: lib/solaar/ui/window.py:548 msgid "Unit ID" msgstr "ID устройства" #: lib/solaar/ui/window.py:559 msgid "none" msgstr "никто" #: lib/solaar/ui/window.py:560 msgid "Notifications" msgstr "Уведомления" #: lib/solaar/ui/window.py:604 msgid "No device paired." msgstr "Нет сопряжённых устройств." #: lib/solaar/ui/window.py:613 #, 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:620 msgid "Only one device can be paired to this receiver." msgstr "К этому приемнику может быть подключено только одно устройство." #: lib/solaar/ui/window.py:624 #, 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:681 msgid "Battery Voltage" msgstr "Напряжение батареи" #: lib/solaar/ui/window.py:683 msgid "Voltage reported by battery" msgstr "Напряжение батареи" #: lib/solaar/ui/window.py:685 msgid "Battery Level" msgstr "Уровень заряда" #: lib/solaar/ui/window.py:687 msgid "Approximate level reported by battery" msgstr "Приблизительный уровень заряда, cообщаемый батареей" #: lib/solaar/ui/window.py:694 lib/solaar/ui/window.py:696 msgid "next reported " msgstr "следующий доклад на " #: lib/solaar/ui/window.py:697 msgid " and next level to be reported." msgstr " \n" "(следующий уровень для отчёта)." #: lib/solaar/ui/window.py:702 msgid "last known" msgstr "последний известный" #: lib/solaar/ui/window.py:713 msgid "encrypted" msgstr "зашифровано" #: lib/solaar/ui/window.py:715 msgid "The wireless link between this device and its receiver is encrypted." msgstr "Беспроводное соединение между этим устройством и его приёмником " "зашифровано." #: lib/solaar/ui/window.py:717 msgid "not encrypted" msgstr "не зашифровано" #: lib/solaar/ui/window.py:721 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:737 #, python-format msgid "%(light_level)d lux" msgstr "%(light_level)d люкс" ================================================ FILE: po/sk.po ================================================ # 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. # # SPDX-FileCopyrightText: 2026 Miroslav Ďurian 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: 2026-01-15 13:17+0100\n" "Last-Translator: Miroslav Ďurian \n" "Language-Team: Slovak \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: Lokalize 25.12.0\n" #: lib/logitech_receiver/base_usb.py:46 msgid "Bolt Receiver" msgstr "Prijímač Bolt" #: 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 "Ľavé tlačidlo" #: lib/logitech_receiver/i18n.py:66 msgid "Right Button" msgstr "Pravé tlačidlo" #: lib/logitech_receiver/i18n.py:67 msgid "Middle Button" msgstr "Stredné tlačidlo" #: lib/logitech_receiver/i18n.py:68 msgid "Back Button" msgstr "Tlačidlo Späť" #: lib/logitech_receiver/i18n.py:69 msgid "Forward Button" msgstr "Tlačidlo Dopredu" #: 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 "Inteligentné Prepínanie" #: lib/logitech_receiver/i18n.py:72 msgid "DPI Switch" msgstr "Prepínač DPI" #: lib/logitech_receiver/i18n.py:73 msgid "Left Tilt" msgstr "Náklon Vľavo" #: lib/logitech_receiver/i18n.py:74 msgid "Right Tilt" msgstr "Náklon Vpravo" #: lib/logitech_receiver/i18n.py:75 msgid "Left Click" msgstr "Ľavý klik" #: lib/logitech_receiver/i18n.py:76 msgid "Right Click" msgstr "Pravý klik" #: lib/logitech_receiver/i18n.py:77 msgid "Mouse Middle Button" msgstr "Stredné tlačidlo myši" #: lib/logitech_receiver/i18n.py:78 msgid "Mouse Back Button" msgstr "Tlačidlo Späť na myši" #: lib/logitech_receiver/i18n.py:79 msgid "Mouse Forward Button" msgstr "Tlačidlo Dopredu na myši" #: lib/logitech_receiver/i18n.py:80 msgid "Gesture Button Navigation" msgstr "Tlačidlo navigácie gestami" #: lib/logitech_receiver/i18n.py:81 msgid "Mouse Scroll Left Button" msgstr "Ľavé tlačidlo rolovania na myši" #: lib/logitech_receiver/i18n.py:82 msgid "Mouse Scroll Right Button" msgstr "Pravé tlačidlo rolovania na myši" #: lib/logitech_receiver/i18n.py:85 msgid "pressed" msgstr "stlačnené" #: lib/logitech_receiver/i18n.py:86 msgid "released" msgstr "uvoľnené" #: 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 "zámok objavovania je uzatvorený" #: lib/logitech_receiver/notifications.py:92 msgid "discovery lock is open" msgstr "zámok objavovania je otvorený" #: 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 "Citlivosť (DPI - staršie myši)" #: 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 "Nastaviť čas podsvietenia klávesnice" #: 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 "" "Nastavte na ignorovanie ak je rolovanie neprimerane rýchle alebo pomalé" #: lib/logitech_receiver/settings_templates.py:247 #: lib/logitech_receiver/settings_templates.py:277 msgid "Scroll Wheel Diversion" msgstr "Odklonenie rolovacieho kolieska" #: 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 "" "Nech rolovacie koliesko posiela LOWRES_WHEEL HID++ notifikácie (ktoré spúšťajú" " pravidlá v Solaare ale inak sú ignorované)." #: 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 "" "Nech rolovacie koliesko posiela HIRES_WHEEL HID++ notifikácie (ktoré spúšťajú " "pravidlá v Solaare ale inak sú ignorované)." #: 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 "Odchylka palcového kolieska" #: 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 "" "Nech rolovacie koliesko posiela THUMB_WHEEL HID++ notifikácie (ktoré spúšťajú " "pravidlá v Solaare ale inak sú ignorované)." #: 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 "Hladké rolovanie korunky" #: lib/logitech_receiver/settings_templates.py:375 msgid "Set crown smooth scroll" msgstr "Nastavuje hladké rolovanie korunky" #: 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 "Umožniť aj klávesom M a MR posielať HID++ upozornenia" #: lib/logitech_receiver/settings_templates.py:402 msgid "Scroll Wheel Ratcheted" msgstr "Rapkavé rolovacie koliesko" #: lib/logitech_receiver/settings_templates.py:403 msgid "" "Switch the mouse wheel between speed-controlled ratcheting and " "always freespin." msgstr "" "Prepína koliesko myši medzi rapkaním v závislosti od rýchlosti a voľnobehom." #: lib/logitech_receiver/settings_templates.py:405 msgid "Freespinning" msgstr "Voľnobeh" #: lib/logitech_receiver/settings_templates.py:405 msgid "Ratcheted" msgstr "Rapkanie" #: lib/logitech_receiver/settings_templates.py:412 msgid "Scroll Wheel Ratchet Speed" msgstr "Rýchlosť rapkania rolovacieho kolieska" #: 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 "" "Použiť rýchlosť kolieska myši na prepínanie medzi rapkaním a voľnobehom." #: 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 "Obídené nastavením odklonenia" #: 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 "" "Nech kláves alebo tlačidlo posiela HID++ upozornenia (Odklonené) alebo spustí " "gestá myši alebo posunie 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 "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 "Posun DPI" #: lib/logitech_receiver/settings_templates.py:711 msgid "Sensitivity (DPI)" msgstr "Citlivosť (DPI)" #: lib/logitech_receiver/settings_templates.py:752 msgid "Sensitivity Switching" msgstr "Prepínanie citlivosti" #: 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 "" "Stlačením klávesu alebo tlačidla, prepínať súčasnú citlivosť a zapamätanú citl" "ivosť.\n" "Ak nie je zapamätaná citlivosť, tak si len zapamätá súčasnú citlivosť." #: 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 "Pixlová oblasť" #: lib/logitech_receiver/settings_templates.py:967 msgid "Ratio zone" msgstr "Pomerná oblasť" #: 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 "Nastavuje rýchlosť kurzora." #: 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 "Spodok" #: lib/logitech_receiver/settings_templates.py:973 msgid "Bottom coordinate." msgstr "Úplne spodná súradnica." #: 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 "Odklon gest" #: lib/logitech_receiver/settings_templates.py:1001 msgid "Divert mouse/touchpad gestures." msgstr "Odkloniť gestá myši/touchpadu." #: 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 "LEDky M-Key" #: lib/logitech_receiver/settings_templates.py:1046 msgid "Control the M-Key LEDs." msgstr "Ovládanie LED svetla klávesu M-Key" #: lib/logitech_receiver/settings_templates.py:1047 #: lib/logitech_receiver/settings_templates.py:1077 msgid "May need G Keys diverted to be effective." msgstr "Môže vyžadovať odklon G Klávesov aby to fungovalo." #: lib/logitech_receiver/settings_templates.py:1053 #, python-format msgid "Lights up the %s key." msgstr "Rozsvieti kláves %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 "Ovláda LEDky klávesu MR-Key." #: lib/logitech_receiver/settings_templates.py:1095 msgid "Persistent Key/Button Mapping" msgstr "Trvalé mapovanie Klávesov/Tlačidiel" #: lib/logitech_receiver/settings_templates.py:1097 msgid "Permanently change the mapping for the key or button." msgstr "Natrvalo zmeniť mapovanie pre kláves alebo tlačidlo." #: 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 "" "Zmena dôležitých klávesov alebo tlačidiel (ako je Ľavé tlačidlo myši) môže sko" "nčiť nepoužiteľným systémom." #: lib/logitech_receiver/settings_templates.py:1157 msgid "Sidetone" msgstr "Bočný odtieň" #: lib/logitech_receiver/settings_templates.py:1158 msgid "Set sidetone level." msgstr "Nastavenie bočného odtieňa." #: lib/logitech_receiver/settings_templates.py:1167 msgid "Equalizer" msgstr "Ekvalizér" #: lib/logitech_receiver/settings_templates.py:1168 msgid "Set equalizer levels." msgstr "Nastavenie úrovne ekvalizéru." #: lib/logitech_receiver/settings_templates.py:1191 msgid "Hz" msgstr "Hz" #: lib/logitech_receiver/settings_templates.py:1197 msgid "Power Management" msgstr "Správa napájania" #: lib/logitech_receiver/settings_templates.py:1198 msgid "Power off in minutes (0 for never)." msgstr "Minúty do vypnutia (0 znamená nikdy)" #: 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 "" "Našiel sa prijímač alebo zariadenie Logitech (%s), ale nemal oprávnenie na jeh" "o otvorenie." #: lib/solaar/ui/__init__.py:55 msgid "" "If you've just installed Solaar, try disconnecting the receiver or " "device and then reconnecting it." msgstr "" "Ak ste práve nainštalovali Solaar, skúste odpojiť prijímač alebo zariadenie a " "znova ho pripojiť." #: 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 "" "Skúste odpojiť zariadenie a potom ho opätovne pripojiť alebo ho skúste vypnúť " "a zase zapnúť." #: 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 "Iný proces Solaaru uz beží, takže len odkryte jeho okno" #: 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 "Proces myši" #: 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 "KlávesJeStlačený" #: 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 "Zariadenie" #: lib/solaar/ui/diversion_rules.py:524 lib/solaar/ui/diversion_rules.py:2297 msgid "Host" msgstr "Hostiteľ" #: lib/solaar/ui/diversion_rules.py:525 lib/solaar/ui/diversion_rules.py:2339 msgid "Setting" msgstr "Nastavenie" #: 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 "Test bajtov" #: 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 "Nastaviť" #: 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 "Neskôr" #: 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 "Počet sekúnd oneskorenia." #: 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 "Aktývny proces X11. Len pre použitie v X11." #: lib/solaar/ui/diversion_rules.py:1218 msgid "X11 mouse process. For use in X11 only." msgstr "Proces myši X11. Len pre použitie v X11." #: 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 "Názov funkcie upozornenia, ktorá spúšťa spracovanie pravidla." #: lib/solaar/ui/diversion_rules.py:1308 msgid "Report number of notification triggering rule processing." msgstr "Počet upozornení, ktoré spúšťajú spracovanie pravidla." #: lib/solaar/ui/diversion_rules.py:1342 msgid "Active keyboard modifiers. Not always available in Wayland." msgstr "Aktívne modifikátory klávesnice. Vo Waylande nie sú vždy dostupné." #: 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 "" "Odklonený kláves alebo tlačidlo stlačené alebo uvoľnené.\n" "Použiť Odklonenie Klávesu/Tlačidla a Odkloniť nastavenie G Klávesov pre odklon" "enie klávesov a tlačidiel." #: lib/solaar/ui/diversion_rules.py:1392 msgid "Key down" msgstr "Kláves stlačený" #: lib/solaar/ui/diversion_rules.py:1395 msgid "Key up" msgstr "Kláves uvoľnený" #: 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 "" "Odklonený kláves alebo tlačidlo je práve stlačené.\n" "Použiť Odklon Klávesov/Tlačidiel a Odkloniť nastavenie G Klávesov na odkloneni" "e klávesov a tlačidiel." #: 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 "" #: lib/solaar/ui/diversion_rules.py:1543 msgid "end (exclusive)" msgstr "" #: lib/solaar/ui/diversion_rules.py:1552 msgid "range" msgstr "rozsah" #: 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 "" #: 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 "" #: 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 "" #: lib/solaar/ui/diversion_rules.py:1671 msgid "Add movement" msgstr "Pridať pohyb" #: 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 "Kliknutie" #: lib/solaar/ui/diversion_rules.py:1775 msgid "Depress" msgstr "Stlačiť" #: lib/solaar/ui/diversion_rules.py:1778 msgid "Release" msgstr "Uvoľniť" #: lib/solaar/ui/diversion_rules.py:1861 msgid "" "Simulate a mouse scroll.\n" "On Wayland requires write access to /dev/uinput." msgstr "" "Simulovať rolovanie myši.\n" "Vo Waylande vyžaduje právo na zápis do /dev/uinput." #: lib/solaar/ui/diversion_rules.py:1918 msgid "" "Simulate a mouse click.\n" "On Wayland requires write access to /dev/uinput." msgstr "" "Simulovať kliknutie myši.\n" "Vo Waylande vyžaduje právo na zápis do /dev/uinput." #: lib/solaar/ui/diversion_rules.py:1921 msgid "Button" msgstr "Tlačidlo" #: lib/solaar/ui/diversion_rules.py:1922 msgid "Count and Action" msgstr "Počet a Akcia" #: lib/solaar/ui/diversion_rules.py:1972 msgid "Execute a command with arguments." msgstr "Vykonať príkaz s argumentami." #: lib/solaar/ui/diversion_rules.py:1975 msgid "Add argument" msgstr "Pridať argument" #: lib/solaar/ui/diversion_rules.py:2050 msgid "Toggle" msgstr "Prepínač" #: lib/solaar/ui/diversion_rules.py:2050 msgid "True" msgstr "Pravda" #: lib/solaar/ui/diversion_rules.py:2051 msgid "False" msgstr "Nepravda" #: lib/solaar/ui/diversion_rules.py:2065 msgid "Unsupported setting" msgstr "Nepodporované nastavenie" #: 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 "Zariadenie je aktívne a jeho nastavenia možno meniť." #: 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 "Názov hostiteľského počítača." #: lib/solaar/ui/diversion_rules.py:2347 msgid "Value" msgstr "Hodnota" #: lib/solaar/ui/diversion_rules.py:2355 msgid "Item" msgstr "Položka" #: lib/solaar/ui/diversion_rules.py:2630 msgid "Change setting on device" msgstr "Zmeniť nastavenie na zariadení" #: lib/solaar/ui/diversion_rules.py:2647 msgid "Setting on device" msgstr "Nastavenie na zariadení" #: 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 "Vložte tajný kód na %(name)s." #: lib/solaar/ui/pair_window.py:126 #, python-format msgid "Type %(passcode)s and then press the enter key." msgstr "Napíšte %(passcode)s a potom stlačte kláves Enter." #: lib/solaar/ui/pair_window.py:129 msgid "left" msgstr "vľavo" #: lib/solaar/ui/pair_window.py:129 msgid "right" msgstr "vpravo" #: lib/solaar/ui/pair_window.py:131 #, python-format msgid "" "Press %(code)s\n" "and then press left and right buttons simultaneously." msgstr "" "Stlačte %(code)s\n" "a potom stlačte ľavé a pravé tlačidlo súčasne." #: 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 "Unifying prijímače sú kompatibilné len s Unifying zariadeniami." #: lib/solaar/ui/pair_window.py:266 msgid "Bolt receivers are only compatible with Bolt devices." msgstr "Bolt prijímače sú kompatibilné len s Bolt zariadeniami." #: lib/solaar/ui/pair_window.py:268 msgid "Other receivers are only compatible with a few devices." msgstr "Ostatné prijímače sú kompatibilné len s máloktorými zariadeniami." #: lib/solaar/ui/pair_window.py:270 msgid "The device must not be paired with a nearby powered-on receiver." msgstr "Zariadenie nesmie byť spárované s nedaľekým zapnutým prijímačom." #: lib/solaar/ui/pair_window.py:274 msgid "" "Press a pairing button or key until the pairing light flashes " "quickly." msgstr "" "Stlačte párovacie tlačidlo alebo kláves kým rýchlo nezabliká párovacie svetlo." #: lib/solaar/ui/pair_window.py:276 msgid "You may have to first turn the device off and on again." msgstr "Možno bude treba najprv zariadenie vypnuť a znova zapnúť." #: 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 "Nenašlo sa podporované zariadenie" #: 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 "" "Bezdrôtové prepojenie tohto zariadenia s jeho prijímačom nie je šifrované.\n" "To je bezpečnostný problém pre ukazovacie zariadenia a závažný bezpečnostný pr" "oblém pre textové vstupné zariadenia." #: 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" ================================================ FILE: po/solaar.pot ================================================ # 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.19\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-03-05 14:00+0200\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:52 msgid "Bolt Receiver" msgstr "" #: lib/logitech_receiver/base_usb.py:64 msgid "Unifying Receiver" msgstr "" #: lib/logitech_receiver/base_usb.py:75 lib/logitech_receiver/base_usb.py:87 #: lib/logitech_receiver/base_usb.py:100 lib/logitech_receiver/base_usb.py:113 msgid "Nano Receiver" msgstr "" #: lib/logitech_receiver/base_usb.py:125 msgid "Lightspeed Receiver" msgstr "" #: lib/logitech_receiver/base_usb.py:136 msgid "EX100 Receiver 27 Mhz" msgstr "" #: lib/logitech_receiver/common.py:649 #, python-format msgid "Battery: %(level)s (%(status)s)" msgstr "" #: lib/logitech_receiver/common.py:652 #, python-format msgid "Battery: %(percent)d%% (%(status)s)" msgstr "" #: lib/logitech_receiver/hidpp20.py:1048 #: lib/logitech_receiver/settings_templates.py:299 msgid "Disabled" msgstr "" #: lib/logitech_receiver/hidpp20.py:1049 msgid "Static" msgstr "" #: lib/logitech_receiver/hidpp20.py:1050 msgid "Pulse" msgstr "" #: lib/logitech_receiver/hidpp20.py:1051 msgid "Cycle" msgstr "" #: lib/logitech_receiver/hidpp20.py:1052 msgid "Boot" msgstr "" #: lib/logitech_receiver/hidpp20.py:1053 msgid "Demo" msgstr "" #: lib/logitech_receiver/hidpp20.py:1055 msgid "Breathe" msgstr "" #: lib/logitech_receiver/hidpp20.py:1058 msgid "Ripple" msgstr "" #: lib/logitech_receiver/hidpp20.py:1059 msgid "Decomposition" msgstr "" #: lib/logitech_receiver/hidpp20.py:1060 msgid "Signature1" msgstr "" #: lib/logitech_receiver/hidpp20.py:1061 msgid "Signature2" msgstr "" #: lib/logitech_receiver/hidpp20.py:1062 msgid "CycleS" msgstr "" #: lib/logitech_receiver/hidpp20.py:1126 msgid "Unknown Location" msgstr "" #: lib/logitech_receiver/hidpp20.py:1127 msgid "Primary" msgstr "" #: lib/logitech_receiver/hidpp20.py:1128 msgid "Logo" msgstr "" #: lib/logitech_receiver/hidpp20.py:1129 msgid "Left Side" msgstr "" #: lib/logitech_receiver/hidpp20.py:1130 msgid "Right Side" msgstr "" #: lib/logitech_receiver/hidpp20.py:1131 msgid "Combined" msgstr "" #: lib/logitech_receiver/hidpp20.py:1132 msgid "Primary 1" msgstr "" #: lib/logitech_receiver/hidpp20.py:1133 msgid "Primary 2" msgstr "" #: lib/logitech_receiver/hidpp20.py:1134 msgid "Primary 3" msgstr "" #: lib/logitech_receiver/hidpp20.py:1135 msgid "Primary 4" msgstr "" #: lib/logitech_receiver/hidpp20.py:1136 msgid "Primary 5" msgstr "" #: lib/logitech_receiver/hidpp20.py:1137 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 lib/solaar/ui/window.py:700 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 lib/solaar/ui/window.py:555 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:156 lib/solaar/listener.py:243 msgid "connected" msgstr "" #: lib/logitech_receiver/notifications.py:156 lib/solaar/listener.py:243 msgid "disconnected" msgstr "" #: lib/logitech_receiver/notifications.py:182 msgid "unpaired" msgstr "" #: lib/logitech_receiver/notifications.py:231 msgid "powered on" msgstr "" #: lib/logitech_receiver/notifications.py:283 msgid "ADC measurement notification" msgstr "" #: lib/logitech_receiver/notifications.py:428 #: lib/logitech_receiver/notifications.py:484 msgid "pairing lock is closed" msgstr "" #: lib/logitech_receiver/notifications.py:428 #: lib/logitech_receiver/notifications.py:484 msgid "pairing lock is open" msgstr "" #: lib/logitech_receiver/notifications.py:447 msgid "discovery lock is closed" msgstr "" #: lib/logitech_receiver/notifications.py:447 msgid "discovery lock is open" msgstr "" #: lib/logitech_receiver/receiver.py:437 msgid "No paired devices." msgstr "" #: lib/logitech_receiver/receiver.py:439 lib/solaar/ui/window.py:606 #, python-format msgid "%(count)s paired device." msgid_plural "%(count)s paired devices." msgstr[0] "" msgstr[1] "" #: lib/logitech_receiver/settings.py:602 msgid "register" msgstr "" #: lib/logitech_receiver/settings.py:616 lib/logitech_receiver/settings.py:654 msgid "feature" msgstr "" #: lib/logitech_receiver/settings_templates.py:138 msgid "Swap Fx function" msgstr "" #: lib/logitech_receiver/settings_templates.py:141 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:146 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:154 msgid "Hand Detection" msgstr "" #: lib/logitech_receiver/settings_templates.py:155 msgid "Turn on illumination when the hands hover over the keyboard." msgstr "" #: lib/logitech_receiver/settings_templates.py:162 msgid "Scroll Wheel Smooth Scrolling" msgstr "" #: lib/logitech_receiver/settings_templates.py:163 #: lib/logitech_receiver/settings_templates.py:410 #: lib/logitech_receiver/settings_templates.py:439 msgid "High-sensitivity mode for vertical scroll with the wheel." msgstr "" #: lib/logitech_receiver/settings_templates.py:170 msgid "Side Scrolling" msgstr "" #: lib/logitech_receiver/settings_templates.py:172 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:182 msgid "Sensitivity (DPI - older mice)" msgstr "" #: lib/logitech_receiver/settings_templates.py:183 #: lib/logitech_receiver/settings_templates.py:1019 #: lib/logitech_receiver/settings_templates.py:1047 msgid "Mouse movement sensitivity" msgstr "" #: lib/logitech_receiver/settings_templates.py:256 msgid "Backlight Timed" msgstr "" #: lib/logitech_receiver/settings_templates.py:257 #: lib/logitech_receiver/settings_templates.py:397 msgid "Set illumination time for keyboard." msgstr "" #: lib/logitech_receiver/settings_templates.py:268 msgid "Backlight" msgstr "" #: lib/logitech_receiver/settings_templates.py:269 msgid "Illumination level on keyboard. Changes made are only applied in " "Manual mode." msgstr "" #: lib/logitech_receiver/settings_templates.py:301 msgid "Automatic" msgstr "" #: lib/logitech_receiver/settings_templates.py:303 msgid "Manual" msgstr "" #: lib/logitech_receiver/settings_templates.py:305 msgid "Enabled" msgstr "" #: lib/logitech_receiver/settings_templates.py:311 msgid "Backlight Level" msgstr "" #: lib/logitech_receiver/settings_templates.py:312 msgid "Illumination level on keyboard when in Manual mode." msgstr "" #: lib/logitech_receiver/settings_templates.py:369 msgid "Backlight Delay Hands Out" msgstr "" #: lib/logitech_receiver/settings_templates.py:370 msgid "Delay in seconds until backlight fades out with hands away from " "keyboard." msgstr "" #: lib/logitech_receiver/settings_templates.py:378 msgid "Backlight Delay Hands In" msgstr "" #: lib/logitech_receiver/settings_templates.py:379 msgid "Delay in seconds until backlight fades out with hands near keyboard." msgstr "" #: lib/logitech_receiver/settings_templates.py:387 msgid "Backlight Delay Powered" msgstr "" #: lib/logitech_receiver/settings_templates.py:388 msgid "Delay in seconds until backlight fades out with external power." msgstr "" #: lib/logitech_receiver/settings_templates.py:396 msgid "Backlight (Seconds)" msgstr "" #: lib/logitech_receiver/settings_templates.py:408 msgid "Scroll Wheel High Resolution" msgstr "" #: lib/logitech_receiver/settings_templates.py:412 #: lib/logitech_receiver/settings_templates.py:441 msgid "Set to ignore if scrolling is abnormally fast or slow" msgstr "" #: lib/logitech_receiver/settings_templates.py:419 #: lib/logitech_receiver/settings_templates.py:450 msgid "Scroll Wheel Diversion" msgstr "" #: lib/logitech_receiver/settings_templates.py:421 msgid "Make scroll wheel send LOWRES_WHEEL HID++ notifications (which " "trigger Solaar rules but are otherwise ignored)." msgstr "" #: lib/logitech_receiver/settings_templates.py:428 msgid "Scroll Wheel Direction" msgstr "" #: lib/logitech_receiver/settings_templates.py:429 msgid "Invert direction for vertical scroll with wheel." msgstr "" #: lib/logitech_receiver/settings_templates.py:437 msgid "Scroll Wheel Resolution" msgstr "" #: lib/logitech_receiver/settings_templates.py:452 msgid "Make scroll wheel send HIRES_WHEEL HID++ notifications (which " "trigger Solaar rules but are otherwise ignored)." msgstr "" #: lib/logitech_receiver/settings_templates.py:461 msgid "Sensitivity (Pointer Speed)" msgstr "" #: lib/logitech_receiver/settings_templates.py:462 msgid "Speed multiplier for mouse (256 is normal multiplier)." msgstr "" #: lib/logitech_receiver/settings_templates.py:472 msgid "Thumb Wheel Diversion" msgstr "" #: lib/logitech_receiver/settings_templates.py:474 msgid "Make thumb wheel send THUMB_WHEEL HID++ notifications (which trigger " "Solaar rules but are otherwise ignored)." msgstr "" #: lib/logitech_receiver/settings_templates.py:483 msgid "Thumb Wheel Direction" msgstr "" #: lib/logitech_receiver/settings_templates.py:484 msgid "Invert thumb wheel scroll direction." msgstr "" #: lib/logitech_receiver/settings_templates.py:504 msgid "Onboard Profiles" msgstr "" #: lib/logitech_receiver/settings_templates.py:505 msgid "Enable an onboard profile, which controls report rate, sensitivity, " "and button actions" msgstr "" #: lib/logitech_receiver/settings_templates.py:549 #: lib/logitech_receiver/settings_templates.py:582 msgid "Report Rate" msgstr "" #: lib/logitech_receiver/settings_templates.py:551 #: lib/logitech_receiver/settings_templates.py:584 msgid "Frequency of device movement reports" msgstr "" #: lib/logitech_receiver/settings_templates.py:551 #: lib/logitech_receiver/settings_templates.py:584 #: lib/logitech_receiver/settings_templates.py:1019 #: lib/logitech_receiver/settings_templates.py:1047 #: lib/logitech_receiver/settings_templates.py:1421 #: lib/logitech_receiver/settings_templates.py:1452 msgid "May need Onboard Profiles set to Disable to be effective." msgstr "" #: lib/logitech_receiver/settings_templates.py:612 msgid "Divert crown events" msgstr "" #: lib/logitech_receiver/settings_templates.py:613 msgid "Make crown send CROWN HID++ notifications (which trigger Solaar " "rules but are otherwise ignored)." msgstr "" #: lib/logitech_receiver/settings_templates.py:621 msgid "Crown smooth scroll" msgstr "" #: lib/logitech_receiver/settings_templates.py:622 msgid "Set crown smooth scroll" msgstr "" #: lib/logitech_receiver/settings_templates.py:630 msgid "Divert G and M Keys" msgstr "" #: lib/logitech_receiver/settings_templates.py:631 msgid "Make G and M keys send HID++ notifications (which trigger Solaar " "rules but are otherwise ignored)." msgstr "" #: lib/logitech_receiver/settings_templates.py:645 msgid "Scroll Wheel Ratcheted" msgstr "" #: lib/logitech_receiver/settings_templates.py:646 msgid "Switch the mouse wheel between speed-controlled ratcheting and " "always freespin." msgstr "" #: lib/logitech_receiver/settings_templates.py:648 msgid "Freespinning" msgstr "" #: lib/logitech_receiver/settings_templates.py:648 msgid "Ratcheted" msgstr "" #: lib/logitech_receiver/settings_templates.py:655 msgid "Scroll Wheel Ratchet Speed" msgstr "" #: lib/logitech_receiver/settings_templates.py:657 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:707 msgid "Scroll Wheel Ratchet Torque" msgstr "" #: lib/logitech_receiver/settings_templates.py:708 msgid "Change the torque needed to overcome the ratchet." msgstr "" #: lib/logitech_receiver/settings_templates.py:743 msgid "Key/Button Actions" msgstr "" #: lib/logitech_receiver/settings_templates.py:745 msgid "Change the action for the key or button." msgstr "" #: lib/logitech_receiver/settings_templates.py:747 msgid "Overridden by diversion." msgstr "" #: lib/logitech_receiver/settings_templates.py:749 msgid "Changing important actions (such as for the left mouse button) can " "result in an unusable system." msgstr "" #: lib/logitech_receiver/settings_templates.py:924 msgid "Key/Button Diversion" msgstr "" #: lib/logitech_receiver/settings_templates.py:925 msgid "Make the key or button send HID++ notifications (Diverted) or " "initiate Mouse Gestures or Sliding DPI" msgstr "" #: lib/logitech_receiver/settings_templates.py:928 #: lib/logitech_receiver/settings_templates.py:929 #: lib/logitech_receiver/settings_templates.py:930 msgid "Diverted" msgstr "" #: lib/logitech_receiver/settings_templates.py:928 #: lib/logitech_receiver/settings_templates.py:929 msgid "Mouse Gestures" msgstr "" #: lib/logitech_receiver/settings_templates.py:928 #: lib/logitech_receiver/settings_templates.py:929 #: lib/logitech_receiver/settings_templates.py:930 msgid "Regular" msgstr "" #: lib/logitech_receiver/settings_templates.py:928 msgid "Sliding DPI" msgstr "" #: lib/logitech_receiver/settings_templates.py:1018 #: lib/logitech_receiver/settings_templates.py:1046 msgid "Sensitivity (DPI)" msgstr "" #: lib/logitech_receiver/settings_templates.py:1123 msgid "Sensitivity Switching" msgstr "" #: lib/logitech_receiver/settings_templates.py:1125 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:1129 msgid "Off" msgstr "" #: lib/logitech_receiver/settings_templates.py:1160 msgid "Disable keys" msgstr "" #: lib/logitech_receiver/settings_templates.py:1161 msgid "Disable specific keyboard keys." msgstr "" #: lib/logitech_receiver/settings_templates.py:1164 #, python-format msgid "Disables the %s key." msgstr "" #: lib/logitech_receiver/settings_templates.py:1177 #: lib/logitech_receiver/settings_templates.py:1234 msgid "Set OS" msgstr "" #: lib/logitech_receiver/settings_templates.py:1178 #: lib/logitech_receiver/settings_templates.py:1235 msgid "Change keys to match OS." msgstr "" #: lib/logitech_receiver/settings_templates.py:1247 msgid "Change Host" msgstr "" #: lib/logitech_receiver/settings_templates.py:1248 msgid "Switch connection to a different host" msgstr "" #: lib/logitech_receiver/settings_templates.py:1272 msgid "Performs a left click." msgstr "" #: lib/logitech_receiver/settings_templates.py:1272 msgid "Single tap" msgstr "" #: lib/logitech_receiver/settings_templates.py:1273 msgid "Performs a right click." msgstr "" #: lib/logitech_receiver/settings_templates.py:1273 msgid "Single tap with two fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:1274 msgid "Single tap with three fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:1278 msgid "Double tap" msgstr "" #: lib/logitech_receiver/settings_templates.py:1278 msgid "Performs a double click." msgstr "" #: lib/logitech_receiver/settings_templates.py:1279 msgid "Double tap with two fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:1280 msgid "Double tap with three fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:1283 msgid "Drags items by dragging the finger after double tapping." msgstr "" #: lib/logitech_receiver/settings_templates.py:1283 msgid "Tap and drag" msgstr "" #: lib/logitech_receiver/settings_templates.py:1285 msgid "Tap and drag with two fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:1286 msgid "Drags items by dragging the fingers after double tapping." msgstr "" #: lib/logitech_receiver/settings_templates.py:1288 msgid "Tap and drag with three fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:1291 msgid "Suppress tap and edge gestures" msgstr "" #: lib/logitech_receiver/settings_templates.py:1292 msgid "Disables tap and edge gestures (equivalent to pressing Fn+LeftClick)." msgstr "" #: lib/logitech_receiver/settings_templates.py:1294 msgid "Scroll with one finger" msgstr "" #: lib/logitech_receiver/settings_templates.py:1294 #: lib/logitech_receiver/settings_templates.py:1295 #: lib/logitech_receiver/settings_templates.py:1298 msgid "Scrolls." msgstr "" #: lib/logitech_receiver/settings_templates.py:1295 #: lib/logitech_receiver/settings_templates.py:1298 msgid "Scroll with two fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:1296 msgid "Scroll horizontally with two fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:1296 msgid "Scrolls horizontally." msgstr "" #: lib/logitech_receiver/settings_templates.py:1297 msgid "Scroll vertically with two fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:1297 msgid "Scrolls vertically." msgstr "" #: lib/logitech_receiver/settings_templates.py:1299 msgid "Inverts the scrolling direction." msgstr "" #: lib/logitech_receiver/settings_templates.py:1299 msgid "Natural scrolling" msgstr "" #: lib/logitech_receiver/settings_templates.py:1300 msgid "Enables the thumbwheel." msgstr "" #: lib/logitech_receiver/settings_templates.py:1300 msgid "Thumbwheel" msgstr "" #: lib/logitech_receiver/settings_templates.py:1311 #: lib/logitech_receiver/settings_templates.py:1315 msgid "Swipe from the top edge" msgstr "" #: lib/logitech_receiver/settings_templates.py:1312 msgid "Swipe from the left edge" msgstr "" #: lib/logitech_receiver/settings_templates.py:1313 msgid "Swipe from the right edge" msgstr "" #: lib/logitech_receiver/settings_templates.py:1314 msgid "Swipe from the bottom edge" msgstr "" #: lib/logitech_receiver/settings_templates.py:1316 msgid "Swipe two fingers from the left edge" msgstr "" #: lib/logitech_receiver/settings_templates.py:1317 msgid "Swipe two fingers from the right edge" msgstr "" #: lib/logitech_receiver/settings_templates.py:1318 msgid "Swipe two fingers from the bottom edge" msgstr "" #: lib/logitech_receiver/settings_templates.py:1319 msgid "Swipe two fingers from the top edge" msgstr "" #: lib/logitech_receiver/settings_templates.py:1320 #: lib/logitech_receiver/settings_templates.py:1324 msgid "Pinch to zoom out; spread to zoom in." msgstr "" #: lib/logitech_receiver/settings_templates.py:1320 msgid "Zoom with two fingers." msgstr "" #: lib/logitech_receiver/settings_templates.py:1321 msgid "Pinch to zoom out." msgstr "" #: lib/logitech_receiver/settings_templates.py:1322 msgid "Spread to zoom in." msgstr "" #: lib/logitech_receiver/settings_templates.py:1323 msgid "Zoom with three fingers." msgstr "" #: lib/logitech_receiver/settings_templates.py:1324 msgid "Zoom with two fingers" msgstr "" #: lib/logitech_receiver/settings_templates.py:1342 msgid "Pixel zone" msgstr "" #: lib/logitech_receiver/settings_templates.py:1343 msgid "Ratio zone" msgstr "" #: lib/logitech_receiver/settings_templates.py:1344 msgid "Scale factor" msgstr "" #: lib/logitech_receiver/settings_templates.py:1344 msgid "Sets the cursor speed." msgstr "" #: lib/logitech_receiver/settings_templates.py:1348 msgid "Left" msgstr "" #: lib/logitech_receiver/settings_templates.py:1348 msgid "Left-most coordinate." msgstr "" #: lib/logitech_receiver/settings_templates.py:1349 msgid "Bottom" msgstr "" #: lib/logitech_receiver/settings_templates.py:1349 msgid "Bottom coordinate." msgstr "" #: lib/logitech_receiver/settings_templates.py:1350 msgid "Width" msgstr "" #: lib/logitech_receiver/settings_templates.py:1350 msgid "Width." msgstr "" #: lib/logitech_receiver/settings_templates.py:1351 msgid "Height" msgstr "" #: lib/logitech_receiver/settings_templates.py:1351 msgid "Height." msgstr "" #: lib/logitech_receiver/settings_templates.py:1352 msgid "Cursor speed." msgstr "" #: lib/logitech_receiver/settings_templates.py:1352 msgid "Scale" msgstr "" #: lib/logitech_receiver/settings_templates.py:1358 msgid "Gestures" msgstr "" #: lib/logitech_receiver/settings_templates.py:1359 msgid "Tweak the mouse/touchpad behaviour." msgstr "" #: lib/logitech_receiver/settings_templates.py:1375 msgid "Gestures Diversion" msgstr "" #: lib/logitech_receiver/settings_templates.py:1376 msgid "Divert mouse/touchpad gestures." msgstr "" #: lib/logitech_receiver/settings_templates.py:1392 msgid "Gesture params" msgstr "" #: lib/logitech_receiver/settings_templates.py:1393 msgid "Change numerical parameters of a mouse/touchpad." msgstr "" #: lib/logitech_receiver/settings_templates.py:1417 msgid "M-Key LEDs" msgstr "" #: lib/logitech_receiver/settings_templates.py:1419 msgid "Control the M-Key LEDs." msgstr "" #: lib/logitech_receiver/settings_templates.py:1423 #: lib/logitech_receiver/settings_templates.py:1454 msgid "May need G Keys diverted to be effective." msgstr "" #: lib/logitech_receiver/settings_templates.py:1429 #, python-format msgid "Lights up the %s key." msgstr "" #: lib/logitech_receiver/settings_templates.py:1448 msgid "MR-Key LED" msgstr "" #: lib/logitech_receiver/settings_templates.py:1450 msgid "Control the MR-Key LED." msgstr "" #: lib/logitech_receiver/settings_templates.py:1471 msgid "Persistent Key/Button Mapping" msgstr "" #: lib/logitech_receiver/settings_templates.py:1473 msgid "Permanently change the mapping for the key or button." msgstr "" #: lib/logitech_receiver/settings_templates.py:1475 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:1532 msgid "Sidetone" msgstr "" #: lib/logitech_receiver/settings_templates.py:1533 msgid "Set sidetone level." msgstr "" #: lib/logitech_receiver/settings_templates.py:1542 msgid "Equalizer" msgstr "" #: lib/logitech_receiver/settings_templates.py:1543 msgid "Set equalizer levels." msgstr "" #: lib/logitech_receiver/settings_templates.py:1565 msgid "Hz" msgstr "" #: lib/logitech_receiver/settings_templates.py:1571 msgid "Power Management" msgstr "" #: lib/logitech_receiver/settings_templates.py:1572 msgid "Power off in minutes (0 for never)." msgstr "" #: lib/logitech_receiver/settings_templates.py:1584 msgid "Brightness Control" msgstr "" #: lib/logitech_receiver/settings_templates.py:1585 msgid "Control overall brightness" msgstr "" #: lib/logitech_receiver/settings_templates.py:1628 #: lib/logitech_receiver/settings_templates.py:1681 msgid "LED Control" msgstr "" #: lib/logitech_receiver/settings_templates.py:1629 #: lib/logitech_receiver/settings_templates.py:1682 msgid "Switch control of LED zones between device and Solaar" msgstr "" #: lib/logitech_receiver/settings_templates.py:1644 #: lib/logitech_receiver/settings_templates.py:1692 msgid "LED Zone Effects" msgstr "" #: lib/logitech_receiver/settings_templates.py:1645 #: lib/logitech_receiver/settings_templates.py:1693 msgid "LED Control needs to be set to Solaar to be effective." msgstr "" #: lib/logitech_receiver/settings_templates.py:1645 #: lib/logitech_receiver/settings_templates.py:1693 msgid "Set effect for LED Zone" msgstr "" #: lib/logitech_receiver/settings_templates.py:1647 msgid "Color" msgstr "" #: lib/logitech_receiver/settings_templates.py:1648 msgid "Speed" msgstr "" #: lib/logitech_receiver/settings_templates.py:1649 msgid "Period" msgstr "" #: lib/logitech_receiver/settings_templates.py:1650 msgid "Intensity" msgstr "" #: lib/logitech_receiver/settings_templates.py:1651 msgid "Ramp" msgstr "" #: lib/logitech_receiver/settings_templates.py:1666 msgid "LEDs" msgstr "" #: lib/logitech_receiver/settings_templates.py:1703 msgid "Per-key Lighting" msgstr "" #: lib/logitech_receiver/settings_templates.py:1704 msgid "Control per-key lighting." msgstr "" #: lib/logitech_receiver/settings_templates.py:1786 msgid "Force Sensing Buttons" msgstr "" #: lib/logitech_receiver/settings_templates.py:1787 msgid "Change the force required to activate button." msgstr "" #: lib/logitech_receiver/settings_templates.py:1804 msgid "Force Sensing Button" msgstr "" #: lib/logitech_receiver/settings_templates.py:1814 msgid "Haptic Feedback Level" msgstr "" #: lib/logitech_receiver/settings_templates.py:1815 msgid "Change power of haptic feedback. (Zero to turn off.)" msgstr "" #: lib/logitech_receiver/settings_templates.py:1857 msgid "Play Haptic Waveform" msgstr "" #: lib/logitech_receiver/settings_templates.py:1858 msgid "Tell device to play a haptic waveform." msgstr "" #: lib/solaar/ui/__init__.py:120 msgid "Another Solaar process is already running so just expose its window" msgstr "" #: lib/solaar/ui/about/model.py:36 msgid "Manages Logitech receivers,\n" "keyboards, mice, and tablets." msgstr "" #: lib/solaar/ui/about/model.py:64 msgid "Additional Programming" msgstr "" #: lib/solaar/ui/about/model.py:65 msgid "GUI design" msgstr "" #: lib/solaar/ui/about/model.py:67 msgid "Testing" msgstr "" #: lib/solaar/ui/about/model.py:75 msgid "Logitech documentation" msgstr "" #: lib/solaar/ui/action.py:88 lib/solaar/ui/action.py:92 #: lib/solaar/ui/window.py:208 msgid "Unpair" msgstr "" #: lib/solaar/ui/action.py:91 lib/solaar/ui/diversion_rules.py:141 msgid "Cancel" msgstr "" #: lib/solaar/ui/common.py:42 msgid "Permissions error" msgstr "" #: lib/solaar/ui/common.py:44 #, python-format msgid "Found a Logitech receiver or device (%s), but did not have " "permission to open it." msgstr "" #: lib/solaar/ui/common.py:46 msgid "If you've just installed Solaar, try disconnecting the receiver or " "device and then reconnecting it." msgstr "" #: lib/solaar/ui/common.py:49 msgid "Cannot connect to device error" msgstr "" #: lib/solaar/ui/common.py:51 #, python-format msgid "Found a Logitech receiver or device at %s, but encountered an error " "connecting to it." msgstr "" #: lib/solaar/ui/common.py:53 msgid "Try disconnecting the device and then reconnecting it or turning it " "off and then on." msgstr "" #: lib/solaar/ui/common.py:56 msgid "Unpairing failed" msgstr "" #: lib/solaar/ui/common.py:58 #, python-brace-format msgid "Failed to unpair %{device} from %{receiver}." msgstr "" #: lib/solaar/ui/common.py:63 msgid "The receiver returned an error, with no further details." msgstr "" #: lib/solaar/ui/config_panel.py:245 msgid "Complete - ENTER to change" msgstr "" #: lib/solaar/ui/config_panel.py:245 msgid "Incomplete" msgstr "" #: lib/solaar/ui/config_panel.py:491 lib/solaar/ui/config_panel.py:543 #, python-format msgid "%d value" msgid_plural "%d values" msgstr[0] "" msgstr[1] "" #: lib/solaar/ui/config_panel.py:642 msgid "Changes allowed" msgstr "" #: lib/solaar/ui/config_panel.py:643 msgid "No changes allowed" msgstr "" #: lib/solaar/ui/config_panel.py:644 msgid "Ignore this setting" msgstr "" #: lib/solaar/ui/config_panel.py:690 msgid "Working" msgstr "" #: lib/solaar/ui/config_panel.py:693 msgid "Read/write operation failed." msgstr "" #: lib/solaar/ui/desktop_notifications.py:112 msgid "unspecified reason" msgstr "" #: lib/solaar/ui/diversion_rules.py:103 msgid "Built-in rules" msgstr "" #: lib/solaar/ui/diversion_rules.py:103 msgid "User-defined rules" msgstr "" #: lib/solaar/ui/diversion_rules.py:105 lib/solaar/ui/diversion_rules.py:1126 msgid "Rule" msgstr "" #: lib/solaar/ui/diversion_rules.py:106 lib/solaar/ui/diversion_rules.py:382 #: lib/solaar/ui/diversion_rules.py:509 msgid "Sub-rule" msgstr "" #: lib/solaar/ui/diversion_rules.py:108 msgid "[empty]" msgstr "" #: lib/solaar/ui/diversion_rules.py:132 msgid "Make changes permanent?" msgstr "" #: lib/solaar/ui/diversion_rules.py:137 msgid "Yes" msgstr "" #: lib/solaar/ui/diversion_rules.py:139 msgid "No" msgstr "" #: lib/solaar/ui/diversion_rules.py:144 msgid "If you choose No, changes will be lost when Solaar is closed." msgstr "" #: lib/solaar/ui/diversion_rules.py:273 msgid "Paste here" msgstr "" #: lib/solaar/ui/diversion_rules.py:275 msgid "Paste above" msgstr "" #: lib/solaar/ui/diversion_rules.py:277 msgid "Paste below" msgstr "" #: lib/solaar/ui/diversion_rules.py:283 msgid "Paste rule here" msgstr "" #: lib/solaar/ui/diversion_rules.py:285 msgid "Paste rule above" msgstr "" #: lib/solaar/ui/diversion_rules.py:287 msgid "Paste rule below" msgstr "" #: lib/solaar/ui/diversion_rules.py:291 msgid "Paste rule" msgstr "" #: lib/solaar/ui/diversion_rules.py:306 msgid "Insert here" msgstr "" #: lib/solaar/ui/diversion_rules.py:308 msgid "Insert above" msgstr "" #: lib/solaar/ui/diversion_rules.py:310 msgid "Insert below" msgstr "" #: lib/solaar/ui/diversion_rules.py:316 msgid "Insert new rule here" msgstr "" #: lib/solaar/ui/diversion_rules.py:318 msgid "Insert new rule above" msgstr "" #: lib/solaar/ui/diversion_rules.py:320 msgid "Insert new rule below" msgstr "" #: lib/solaar/ui/diversion_rules.py:347 msgid "Flatten" msgstr "" #: lib/solaar/ui/diversion_rules.py:380 msgid "Insert" msgstr "" #: lib/solaar/ui/diversion_rules.py:383 lib/solaar/ui/diversion_rules.py:511 #: lib/solaar/ui/diversion_rules.py:1158 msgid "Or" msgstr "" #: lib/solaar/ui/diversion_rules.py:384 lib/solaar/ui/diversion_rules.py:510 #: lib/solaar/ui/diversion_rules.py:1144 msgid "And" msgstr "" #: lib/solaar/ui/diversion_rules.py:386 msgid "Condition" msgstr "" #: lib/solaar/ui/diversion_rules.py:388 lib/solaar/ui/rule_conditions.py:154 msgid "Feature" msgstr "" #: lib/solaar/ui/diversion_rules.py:389 lib/solaar/ui/rule_conditions.py:188 msgid "Report" msgstr "" #: lib/solaar/ui/diversion_rules.py:390 lib/solaar/ui/rule_conditions.py:69 msgid "Process" msgstr "" #: lib/solaar/ui/diversion_rules.py:391 msgid "Mouse process" msgstr "" #: lib/solaar/ui/diversion_rules.py:392 lib/solaar/ui/rule_conditions.py:225 msgid "Modifiers" msgstr "" #: lib/solaar/ui/diversion_rules.py:393 lib/solaar/ui/rule_conditions.py:277 msgid "Key" msgstr "" #: lib/solaar/ui/diversion_rules.py:394 lib/solaar/ui/rule_conditions.py:318 msgid "KeyIsDown" msgstr "" #: lib/solaar/ui/diversion_rules.py:395 lib/solaar/ui/diversion_rules.py:1432 msgid "Active" msgstr "" #: lib/solaar/ui/diversion_rules.py:396 lib/solaar/ui/diversion_rules.py:1390 #: lib/solaar/ui/diversion_rules.py:1441 lib/solaar/ui/diversion_rules.py:1487 msgid "Device" msgstr "" #: lib/solaar/ui/diversion_rules.py:397 lib/solaar/ui/diversion_rules.py:1467 msgid "Host" msgstr "" #: lib/solaar/ui/diversion_rules.py:398 lib/solaar/ui/diversion_rules.py:1506 msgid "Setting" msgstr "" #: lib/solaar/ui/diversion_rules.py:399 lib/solaar/ui/rule_conditions.py:333 #: lib/solaar/ui/rule_conditions.py:382 msgid "Test" msgstr "" #: lib/solaar/ui/diversion_rules.py:400 lib/solaar/ui/rule_conditions.py:507 msgid "Test bytes" msgstr "" #: lib/solaar/ui/diversion_rules.py:401 lib/solaar/ui/rule_conditions.py:608 msgid "Mouse Gesture" msgstr "" #: lib/solaar/ui/diversion_rules.py:405 msgid "Action" msgstr "" #: lib/solaar/ui/diversion_rules.py:407 lib/solaar/ui/rule_actions.py:138 msgid "Key press" msgstr "" #: lib/solaar/ui/diversion_rules.py:408 lib/solaar/ui/rule_actions.py:191 msgid "Mouse scroll" msgstr "" #: lib/solaar/ui/diversion_rules.py:409 lib/solaar/ui/rule_actions.py:256 msgid "Mouse click" msgstr "" #: lib/solaar/ui/diversion_rules.py:410 msgid "Set" msgstr "" #: lib/solaar/ui/diversion_rules.py:411 lib/solaar/ui/rule_actions.py:328 msgid "Execute" msgstr "" #: lib/solaar/ui/diversion_rules.py:412 lib/solaar/ui/diversion_rules.py:1189 msgid "Later" msgstr "" #: lib/solaar/ui/diversion_rules.py:441 msgid "Insert new rule" msgstr "" #: lib/solaar/ui/diversion_rules.py:461 lib/solaar/ui/rule_actions.py:82 #: lib/solaar/ui/rule_actions.py:287 lib/solaar/ui/rule_conditions.py:556 msgid "Delete" msgstr "" #: lib/solaar/ui/diversion_rules.py:483 msgid "Negate" msgstr "" #: lib/solaar/ui/diversion_rules.py:507 msgid "Wrap with" msgstr "" #: lib/solaar/ui/diversion_rules.py:537 msgid "Cut" msgstr "" #: lib/solaar/ui/diversion_rules.py:553 msgid "Paste" msgstr "" #: lib/solaar/ui/diversion_rules.py:559 msgid "Copy" msgstr "" #: lib/solaar/ui/diversion_rules.py:568 msgid "Solaar Rule Editor" msgstr "" #: lib/solaar/ui/diversion_rules.py:668 msgid "Save changes" msgstr "" #: lib/solaar/ui/diversion_rules.py:673 msgid "Discard changes" msgstr "" #: lib/solaar/ui/diversion_rules.py:1104 msgid "This editor does not support the selected rule component yet." msgstr "" #: lib/solaar/ui/diversion_rules.py:1169 msgid "Number of seconds to delay. Delay between 0 and 1 is done with " "higher precision." msgstr "" #: lib/solaar/ui/diversion_rules.py:1207 msgid "Not" msgstr "" #: lib/solaar/ui/diversion_rules.py:1238 msgid "Toggle" msgstr "" #: lib/solaar/ui/diversion_rules.py:1239 msgid "True" msgstr "" #: lib/solaar/ui/diversion_rules.py:1240 msgid "False" msgstr "" #: lib/solaar/ui/diversion_rules.py:1253 msgid "Unsupported setting" msgstr "" #: lib/solaar/ui/diversion_rules.py:1396 lib/solaar/ui/diversion_rules.py:1415 #: lib/solaar/ui/diversion_rules.py:1493 lib/solaar/ui/diversion_rules.py:1748 #: lib/solaar/ui/diversion_rules.py:1766 msgid "Originating device" msgstr "" #: lib/solaar/ui/diversion_rules.py:1428 msgid "Device is active and its settings can be changed." msgstr "" #: lib/solaar/ui/diversion_rules.py:1437 msgid "Device that originated the current notification." msgstr "" #: lib/solaar/ui/diversion_rules.py:1450 msgid "Name of host computer." msgstr "" #: lib/solaar/ui/diversion_rules.py:1520 msgid "Value" msgstr "" #: lib/solaar/ui/diversion_rules.py:1529 msgid "Item" msgstr "" #: lib/solaar/ui/diversion_rules.py:1808 msgid "Change setting on device" msgstr "" #: lib/solaar/ui/diversion_rules.py:1824 msgid "Setting on device" msgstr "" #: lib/solaar/ui/pair_window.py:44 lib/solaar/ui/pair_window.py:167 #, python-format msgid "%(receiver_name)s: pair new device" msgstr "" #: lib/solaar/ui/pair_window.py:46 msgid "Bolt receivers are only compatible with Bolt devices." msgstr "" #: lib/solaar/ui/pair_window.py:48 msgid "Press a pairing button or key until the pairing light flashes " "quickly." msgstr "" #: lib/solaar/ui/pair_window.py:51 msgid "Unifying receivers are only compatible with Unifying devices." msgstr "" #: lib/solaar/ui/pair_window.py:53 msgid "Other receivers are only compatible with a few devices." msgstr "" #: lib/solaar/ui/pair_window.py:55 msgid "For most devices, turn on the device you want to pair." msgstr "" #: lib/solaar/ui/pair_window.py:56 msgid "If the device is already turned on, turn it off and on again." msgstr "" #: lib/solaar/ui/pair_window.py:58 msgid "The device must not be paired with a nearby powered-on receiver." msgstr "" #: lib/solaar/ui/pair_window.py:61 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 "" #: lib/solaar/ui/pair_window.py:68 msgid "The channel indicator light should be blinking rapidly." msgstr "" #: lib/solaar/ui/pair_window.py:72 #, 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:78 msgid "\n" "Cancelling at this point will not use up a pairing." msgstr "" #: lib/solaar/ui/pair_window.py:168 #, python-format msgid "Enter passcode on %(name)s." msgstr "" #: lib/solaar/ui/pair_window.py:171 #, python-format msgid "Type %(passcode)s and then press the enter key." msgstr "" #: lib/solaar/ui/pair_window.py:176 msgid "left" msgstr "" #: lib/solaar/ui/pair_window.py:176 msgid "right" msgstr "" #: lib/solaar/ui/pair_window.py:178 #, python-format msgid "Press %(code)s\n" "and then press left and right buttons simultaneously." msgstr "" #: lib/solaar/ui/pair_window.py:221 msgid "The wireless link is not encrypted" msgstr "" #: lib/solaar/ui/pair_window.py:226 msgid "Found a new device:" msgstr "" #: lib/solaar/ui/pair_window.py:247 msgid "Pairing failed" msgstr "" #: lib/solaar/ui/pair_window.py:249 msgid "Make sure your device is within range, and has a decent battery " "charge." msgstr "" #: lib/solaar/ui/pair_window.py:251 msgid "A new device was detected, but it is not compatible with this " "receiver." msgstr "" #: lib/solaar/ui/pair_window.py:253 msgid "More paired devices than receiver can support." msgstr "" #: lib/solaar/ui/pair_window.py:255 msgid "No further details are available about the error." msgstr "" #: lib/solaar/ui/rule_actions.py:54 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:60 msgid "Add key" msgstr "" #: lib/solaar/ui/rule_actions.py:63 msgid "Click" msgstr "" #: lib/solaar/ui/rule_actions.py:66 msgid "Depress" msgstr "" #: lib/solaar/ui/rule_actions.py:69 msgid "Release" msgstr "" #: lib/solaar/ui/rule_actions.py:153 msgid "Simulate a mouse scroll.\n" "On Wayland requires write access to /dev/uinput." msgstr "" #: lib/solaar/ui/rule_actions.py:211 msgid "Simulate a mouse click.\n" "On Wayland requires write access to /dev/uinput." msgstr "" #: lib/solaar/ui/rule_actions.py:216 msgid "Button" msgstr "" #: lib/solaar/ui/rule_actions.py:218 msgid "Action (and Count, if click)" msgstr "" #: lib/solaar/ui/rule_actions.py:269 msgid "Execute a command with arguments." msgstr "" #: lib/solaar/ui/rule_actions.py:273 msgid "Add argument" msgstr "" #: lib/solaar/ui/rule_conditions.py:52 msgid "X11 active process. For use in X11 only." msgstr "" #: lib/solaar/ui/rule_conditions.py:82 msgid "X11 mouse process. For use in X11 only." msgstr "" #: lib/solaar/ui/rule_conditions.py:99 msgid "MouseProcess" msgstr "" #: lib/solaar/ui/rule_conditions.py:123 msgid "Feature name of notification triggering rule processing." msgstr "" #: lib/solaar/ui/rule_conditions.py:169 msgid "Report number of notification triggering rule processing." msgstr "" #: lib/solaar/ui/rule_conditions.py:201 msgid "Active keyboard modifiers. Not always available in Wayland." msgstr "" #: lib/solaar/ui/rule_conditions.py:241 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:250 msgid "Key down" msgstr "" #: lib/solaar/ui/rule_conditions.py:253 msgid "Key up" msgstr "" #: lib/solaar/ui/rule_conditions.py:293 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:331 msgid "Test condition on notification triggering rule processing." msgstr "" #: lib/solaar/ui/rule_conditions.py:335 msgid "Parameter" msgstr "" #: lib/solaar/ui/rule_conditions.py:408 msgid "begin (inclusive)" msgstr "" #: lib/solaar/ui/rule_conditions.py:409 msgid "end (exclusive)" msgstr "" #: lib/solaar/ui/rule_conditions.py:417 msgid "range" msgstr "" #: lib/solaar/ui/rule_conditions.py:420 msgid "minimum" msgstr "" #: lib/solaar/ui/rule_conditions.py:421 msgid "maximum" msgstr "" #: lib/solaar/ui/rule_conditions.py:423 #, python-format msgid "bytes %(0)d to %(1)d, ranging from %(2)d to %(3)d" msgstr "" #: lib/solaar/ui/rule_conditions.py:426 lib/solaar/ui/rule_conditions.py:427 msgid "mask" msgstr "" #: lib/solaar/ui/rule_conditions.py:428 #, python-format msgid "bytes %(0)d to %(1)d, mask %(2)d" msgstr "" #: lib/solaar/ui/rule_conditions.py:437 msgid "Bit or range test on bytes in notification message triggering rule " "processing." msgstr "" #: lib/solaar/ui/rule_conditions.py:447 msgid "type" msgstr "" #: lib/solaar/ui/rule_conditions.py:535 msgid "Mouse gesture with optional initiating button followed by zero or " "more mouse movements." msgstr "" #: lib/solaar/ui/rule_conditions.py:541 msgid "Add movement" msgstr "" #: lib/solaar/ui/tray.py:55 msgid "No supported device found" msgstr "" #: lib/solaar/ui/tray.py:60 lib/solaar/ui/window.py:328 #, python-format msgid "About %s" msgstr "" #: lib/solaar/ui/tray.py:61 lib/solaar/ui/window.py:326 #, python-format msgid "Quit %s" msgstr "" #: lib/solaar/ui/tray.py:273 lib/solaar/ui/tray.py:281 msgid "no receiver" msgstr "" #: lib/solaar/ui/tray.py:294 lib/solaar/ui/tray.py:299 #: lib/solaar/ui/window.py:728 msgid "offline" msgstr "" #: lib/solaar/ui/tray.py:297 msgid "no status" msgstr "" #: lib/solaar/ui/window.py:110 msgid "Scanning" msgstr "" #: lib/solaar/ui/window.py:141 msgid "Battery" msgstr "" #: lib/solaar/ui/window.py:144 msgid "Wireless Link" msgstr "" #: lib/solaar/ui/window.py:148 msgid "Lighting" msgstr "" #: lib/solaar/ui/window.py:182 msgid "Show Technical Details" msgstr "" #: lib/solaar/ui/window.py:198 msgid "Pair new device" msgstr "" #: lib/solaar/ui/window.py:216 msgid "Select a device" msgstr "" #: lib/solaar/ui/window.py:331 msgid "Rule Editor" msgstr "" #: lib/solaar/ui/window.py:522 msgid "Path" msgstr "" #: lib/solaar/ui/window.py:524 msgid "USB ID" msgstr "" #: lib/solaar/ui/window.py:527 lib/solaar/ui/window.py:529 #: lib/solaar/ui/window.py:544 lib/solaar/ui/window.py:546 msgid "Serial" msgstr "" #: lib/solaar/ui/window.py:533 msgid "Index" msgstr "" #: lib/solaar/ui/window.py:535 msgid "Wireless PID" msgstr "" #: lib/solaar/ui/window.py:537 msgid "Product ID" msgstr "" #: lib/solaar/ui/window.py:539 msgid "Protocol" msgstr "" #: lib/solaar/ui/window.py:539 msgid "Unknown" msgstr "" #: lib/solaar/ui/window.py:541 msgid "Polling rate" msgstr "" #: lib/solaar/ui/window.py:548 msgid "Unit ID" msgstr "" #: lib/solaar/ui/window.py:559 msgid "none" msgstr "" #: lib/solaar/ui/window.py:560 msgid "Notifications" msgstr "" #: lib/solaar/ui/window.py:604 msgid "No device paired." msgstr "" #: lib/solaar/ui/window.py:613 #, 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:620 msgid "Only one device can be paired to this receiver." msgstr "" #: lib/solaar/ui/window.py:624 #, 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:681 msgid "Battery Voltage" msgstr "" #: lib/solaar/ui/window.py:683 msgid "Voltage reported by battery" msgstr "" #: lib/solaar/ui/window.py:685 msgid "Battery Level" msgstr "" #: lib/solaar/ui/window.py:687 msgid "Approximate level reported by battery" msgstr "" #: lib/solaar/ui/window.py:694 lib/solaar/ui/window.py:696 msgid "next reported " msgstr "" #: lib/solaar/ui/window.py:697 msgid " and next level to be reported." msgstr "" #: lib/solaar/ui/window.py:702 msgid "last known" msgstr "" #: lib/solaar/ui/window.py:713 msgid "encrypted" msgstr "" #: lib/solaar/ui/window.py:715 msgid "The wireless link between this device and its receiver is encrypted." msgstr "" #: lib/solaar/ui/window.py:717 msgid "not encrypted" msgstr "" #: lib/solaar/ui/window.py:721 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:737 #, python-format msgid "%(light_level)d lux" msgstr "" ================================================ FILE: po/sr.po ================================================ # 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" ================================================ FILE: po/sv.po ================================================ # 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: 2025-12-01 21:43+0300\n" "PO-Revision-Date: 2025-12-13 08:04+0100\n" "Last-Translator: Daniel Nylander \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-Poedit-Basepath: /pwr/Solaar/po\n" "X-Poedit-SearchPath-0: /pwr/Solaar/po\n" #: lib/logitech_receiver/base_usb.py:52 msgid "Bolt Receiver" msgstr "Bolt-mottagare" #: lib/logitech_receiver/base_usb.py:64 msgid "Unifying Receiver" msgstr "Unifying-mottagare" #: lib/logitech_receiver/base_usb.py:75 lib/logitech_receiver/base_usb.py:87 #: lib/logitech_receiver/base_usb.py:100 lib/logitech_receiver/base_usb.py:113 msgid "Nano Receiver" msgstr "Nano-mottagare" #: lib/logitech_receiver/base_usb.py:125 msgid "Lightspeed Receiver" msgstr "Lightspeed-mottagare" #: lib/logitech_receiver/base_usb.py:135 msgid "EX100 Receiver 27 Mhz" msgstr "EX100-mottagare 27 Mhz" #: lib/logitech_receiver/common.py:649 #, python-format msgid "Battery: %(level)s (%(status)s)" msgstr "Batteri: %(level)s (%(status)s)" #: lib/logitech_receiver/common.py:652 #, python-format msgid "Battery: %(percent)d%% (%(status)s)" msgstr "Batteri: %(percent)d%% (%(status)s)" #: lib/logitech_receiver/hidpp20.py:1048 #: lib/logitech_receiver/settings_templates.py:299 msgid "Disabled" msgstr "Inaktiverad" #: lib/logitech_receiver/hidpp20.py:1049 msgid "Static" msgstr "Statisk" #: lib/logitech_receiver/hidpp20.py:1050 msgid "Pulse" msgstr "Pulsera" #: lib/logitech_receiver/hidpp20.py:1051 msgid "Cycle" msgstr "Växla" #: lib/logitech_receiver/hidpp20.py:1052 msgid "Boot" msgstr "Boot" #: lib/logitech_receiver/hidpp20.py:1053 msgid "Demo" msgstr "Demo" #: lib/logitech_receiver/hidpp20.py:1055 msgid "Breathe" msgstr "Andas" #: lib/logitech_receiver/hidpp20.py:1058 msgid "Ripple" msgstr "Krusning" #: lib/logitech_receiver/hidpp20.py:1059 msgid "Decomposition" msgstr "Nedbrytning" #: lib/logitech_receiver/hidpp20.py:1060 msgid "Signature1" msgstr "Signatur1" #: lib/logitech_receiver/hidpp20.py:1061 msgid "Signature2" msgstr "Signatur2" #: lib/logitech_receiver/hidpp20.py:1062 msgid "CycleS" msgstr "CycleS" #: lib/logitech_receiver/hidpp20.py:1126 msgid "Unknown Location" msgstr "Okänd plats" #: lib/logitech_receiver/hidpp20.py:1127 msgid "Primary" msgstr "Primär" #: lib/logitech_receiver/hidpp20.py:1128 msgid "Logo" msgstr "Logotyp" #: lib/logitech_receiver/hidpp20.py:1129 msgid "Left Side" msgstr "Vänster sida" #: lib/logitech_receiver/hidpp20.py:1130 msgid "Right Side" msgstr "Höger sida" #: lib/logitech_receiver/hidpp20.py:1131 msgid "Combined" msgstr "Kombinerad" #: lib/logitech_receiver/hidpp20.py:1132 msgid "Primary 1" msgstr "Primär 1" #: lib/logitech_receiver/hidpp20.py:1133 msgid "Primary 2" msgstr "Primär 2" #: lib/logitech_receiver/hidpp20.py:1134 msgid "Primary 3" msgstr "Primär 3" #: lib/logitech_receiver/hidpp20.py:1135 msgid "Primary 4" msgstr "Primär 4" #: lib/logitech_receiver/hidpp20.py:1136 msgid "Primary 5" msgstr "Primär 5" #: lib/logitech_receiver/hidpp20.py:1137 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 "genomsnitt" #: 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 "urladdning" #: lib/logitech_receiver/i18n.py:36 msgid "recharging" msgstr "uppladdning" #: lib/logitech_receiver/i18n.py:37 lib/solaar/ui/window.py:700 msgid "charging" msgstr "laddar" #: lib/logitech_receiver/i18n.py:38 msgid "not charging" msgstr "laddar inte" #: lib/logitech_receiver/i18n.py:39 msgid "almost full" msgstr "nästan full" #: lib/logitech_receiver/i18n.py:40 msgid "charged" msgstr "laddad" #: lib/logitech_receiver/i18n.py:41 msgid "slow recharge" msgstr "långsam laddning" #: lib/logitech_receiver/i18n.py:42 msgid "invalid battery" msgstr "batterifel" #: lib/logitech_receiver/i18n.py:43 msgid "thermal error" msgstr "termiskt fel" #: lib/logitech_receiver/i18n.py:44 msgid "error" msgstr "fel" #: lib/logitech_receiver/i18n.py:45 msgid "standard" msgstr "standard" #: lib/logitech_receiver/i18n.py:46 msgid "fast" msgstr "snabb" #: lib/logitech_receiver/i18n.py:47 msgid "slow" msgstr "långsam" #: lib/logitech_receiver/i18n.py:49 msgid "device timeout" msgstr "enheten svarade inte" #: lib/logitech_receiver/i18n.py:50 msgid "device not supported" msgstr "enheten stöds inte" #: lib/logitech_receiver/i18n.py:51 msgid "too many devices" msgstr "för många enheter" #: lib/logitech_receiver/i18n.py:52 msgid "sequence timeout" msgstr "tidsgräns för sekvens överskreds" #: lib/logitech_receiver/i18n.py:54 lib/solaar/ui/window.py:555 msgid "Firmware" msgstr "Fast programvara" #: lib/logitech_receiver/i18n.py:55 msgid "Bootloader" msgstr "Startladdare" #: lib/logitech_receiver/i18n.py:56 msgid "Hardware" msgstr "Hårdvara" #: lib/logitech_receiver/i18n.py:57 msgid "Other" msgstr "Annan" #: lib/logitech_receiver/i18n.py:59 msgid "Left Button" msgstr "Vänster knapp" #: lib/logitech_receiver/i18n.py:60 msgid "Right Button" msgstr "Höger knapp" #: lib/logitech_receiver/i18n.py:61 msgid "Middle Button" msgstr "Mittenknapp" #: lib/logitech_receiver/i18n.py:62 msgid "Back Button" msgstr "Tillbakaknapp" #: lib/logitech_receiver/i18n.py:63 msgid "Forward Button" msgstr "Framåtknapp" #: lib/logitech_receiver/i18n.py:64 msgid "Mouse Gesture Button" msgstr "Mus gestknapp" #: lib/logitech_receiver/i18n.py:65 msgid "Smart Shift" msgstr "Smart skift" #: lib/logitech_receiver/i18n.py:66 msgid "DPI Switch" msgstr "DPI-växel" #: lib/logitech_receiver/i18n.py:67 msgid "Left Tilt" msgstr "Vänster lutning" #: lib/logitech_receiver/i18n.py:68 msgid "Right Tilt" msgstr "Höger lutning" #: lib/logitech_receiver/i18n.py:69 msgid "Left Click" msgstr "Vänsterklick" #: lib/logitech_receiver/i18n.py:70 msgid "Right Click" msgstr "Högerklick" #: lib/logitech_receiver/i18n.py:71 msgid "Mouse Middle Button" msgstr "Mus mittenknapp" #: lib/logitech_receiver/i18n.py:72 msgid "Mouse Back Button" msgstr "Mus tillbakaknapp" #: lib/logitech_receiver/i18n.py:73 msgid "Mouse Forward Button" msgstr "Mus framåtknapp" #: lib/logitech_receiver/i18n.py:74 msgid "Gesture Button Navigation" msgstr "Gestknapp navigering" #: lib/logitech_receiver/i18n.py:75 msgid "Mouse Scroll Left Button" msgstr "Vänster musrullningsknapp" #: lib/logitech_receiver/i18n.py:76 msgid "Mouse Scroll Right Button" msgstr "Höger musrullningsknapp" #: lib/logitech_receiver/i18n.py:78 msgid "pressed" msgstr "nedtryckt" #: lib/logitech_receiver/i18n.py:79 msgid "released" msgstr "släpptes" #: lib/logitech_receiver/notifications.py:156 lib/solaar/listener.py:243 msgid "connected" msgstr "ansluten" #: lib/logitech_receiver/notifications.py:156 lib/solaar/listener.py:243 msgid "disconnected" msgstr "frånkopplad" #: lib/logitech_receiver/notifications.py:182 msgid "unpaired" msgstr "inte parkopplad" #: lib/logitech_receiver/notifications.py:231 msgid "powered on" msgstr "påslagen" #: lib/logitech_receiver/notifications.py:283 msgid "ADC measurement notification" msgstr "ADC-mätningsmeddelande" #: lib/logitech_receiver/notifications.py:428 #: lib/logitech_receiver/notifications.py:483 msgid "pairing lock is closed" msgstr "parkopplingslåset är stängt" #: lib/logitech_receiver/notifications.py:428 #: lib/logitech_receiver/notifications.py:483 msgid "pairing lock is open" msgstr "parkopplingslåset är öppet" #: lib/logitech_receiver/notifications.py:446 msgid "discovery lock is closed" msgstr "upptäcktslåset är stängt" #: lib/logitech_receiver/notifications.py:446 msgid "discovery lock is open" msgstr "upptäcktslåset är öppet" #: lib/logitech_receiver/receiver.py:437 msgid "No paired devices." msgstr "Inga parkopplade enheter." #: lib/logitech_receiver/receiver.py:439 lib/solaar/ui/window.py:606 #, 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/settings.py:602 msgid "register" msgstr "registrera" #: lib/logitech_receiver/settings.py:616 lib/logitech_receiver/settings.py:654 msgid "feature" msgstr "funktion" #: lib/logitech_receiver/settings_templates.py:138 msgid "Swap Fx function" msgstr "Byt Fx-funktion" #: lib/logitech_receiver/settings_templates.py:141 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:146 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:154 msgid "Hand Detection" msgstr "Handavkänning" #: lib/logitech_receiver/settings_templates.py:155 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:162 msgid "Scroll Wheel Smooth Scrolling" msgstr "Rullningshjul mjuk rullning" #: lib/logitech_receiver/settings_templates.py:163 #: lib/logitech_receiver/settings_templates.py:410 #: lib/logitech_receiver/settings_templates.py:439 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:170 msgid "Side Scrolling" msgstr "Sidrullning" #: lib/logitech_receiver/settings_templates.py:172 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:182 msgid "Sensitivity (DPI - older mice)" msgstr "Känslighet (DPI - äldre möss)" #: lib/logitech_receiver/settings_templates.py:183 #: lib/logitech_receiver/settings_templates.py:1019 #: lib/logitech_receiver/settings_templates.py:1047 msgid "Mouse movement sensitivity" msgstr "Musrörelsekänslighet" #: lib/logitech_receiver/settings_templates.py:256 msgid "Backlight Timed" msgstr "Bakgrundsbelysning tidsinställd" #: lib/logitech_receiver/settings_templates.py:257 #: lib/logitech_receiver/settings_templates.py:397 msgid "Set illumination time for keyboard." msgstr "Ställ in belysningstid för tangentbordet." #: lib/logitech_receiver/settings_templates.py:268 msgid "Backlight" msgstr "Bakbelysning" #: lib/logitech_receiver/settings_templates.py:269 msgid "" "Illumination level on keyboard. Changes made are only applied in Manual " "mode." msgstr "" "Belysningsnivå på tangentbordet. Ändringar som görs tillämpas endast i " "manuellt läge." #: lib/logitech_receiver/settings_templates.py:301 msgid "Automatic" msgstr "Automatiskt" #: lib/logitech_receiver/settings_templates.py:303 msgid "Manual" msgstr "Manuellt" #: lib/logitech_receiver/settings_templates.py:305 msgid "Enabled" msgstr "Aktiverad" #: lib/logitech_receiver/settings_templates.py:311 msgid "Backlight Level" msgstr "Bakgrundsbelysningsnivå" #: lib/logitech_receiver/settings_templates.py:312 msgid "Illumination level on keyboard when in Manual mode." msgstr "Belysningsnivå på tangentbordet i manuellt läge." #: lib/logitech_receiver/settings_templates.py:369 msgid "Backlight Delay Hands Out" msgstr "Fördröjning av bakgrundsbelysning händer ute" #: lib/logitech_receiver/settings_templates.py:370 msgid "" "Delay in seconds until backlight fades out with hands away from keyboard." msgstr "" "Fördröjning i sekunder tills bakgrundsbelysningen tonar ut när händerna tas " "bort från tangentbordet." #: lib/logitech_receiver/settings_templates.py:378 msgid "Backlight Delay Hands In" msgstr "Fördröjning av bakgrundsbelysning händer inne" #: lib/logitech_receiver/settings_templates.py:379 msgid "Delay in seconds until backlight fades out with hands near keyboard." msgstr "" "Fördröjning i sekunder tills bakgrundsbelysningen tonar ut när händerna är " "nära tangentbordet." #: lib/logitech_receiver/settings_templates.py:387 msgid "Backlight Delay Powered" msgstr "Fördröjning av bakgrundsbelysning extern ström" #: lib/logitech_receiver/settings_templates.py:388 msgid "Delay in seconds until backlight fades out with external power." msgstr "" "Fördröjning i sekunder tills bakgrundsbelysningen tonar ut med extern " "strömförsörjning." #: lib/logitech_receiver/settings_templates.py:396 msgid "Backlight (Seconds)" msgstr "Bakgrundsbelysning (sekunder)" #: lib/logitech_receiver/settings_templates.py:408 msgid "Scroll Wheel High Resolution" msgstr "Rullningshjul hög upplösning" #: lib/logitech_receiver/settings_templates.py:412 #: lib/logitech_receiver/settings_templates.py:441 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:419 #: lib/logitech_receiver/settings_templates.py:450 msgid "Scroll Wheel Diversion" msgstr "Rullhjulsavledning" #: lib/logitech_receiver/settings_templates.py:421 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:428 msgid "Scroll Wheel Direction" msgstr "Rullhjulets riktning" #: lib/logitech_receiver/settings_templates.py:429 msgid "Invert direction for vertical scroll with wheel." msgstr "Invertera riktning för vertikal rullning med hjul." #: lib/logitech_receiver/settings_templates.py:437 msgid "Scroll Wheel Resolution" msgstr "Rullhjulsupplösning" #: lib/logitech_receiver/settings_templates.py:452 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:461 msgid "Sensitivity (Pointer Speed)" msgstr "Känslighet (pekarhastighet)" #: lib/logitech_receiver/settings_templates.py:462 msgid "Speed multiplier for mouse (256 is normal multiplier)." msgstr "Hastighetsmultiplikator för mus (256 är normal multiplikator)." #: lib/logitech_receiver/settings_templates.py:472 msgid "Thumb Wheel Diversion" msgstr "Tumhjulsavledning" #: lib/logitech_receiver/settings_templates.py:474 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:483 msgid "Thumb Wheel Direction" msgstr "Tumhjulets riktning" #: lib/logitech_receiver/settings_templates.py:484 msgid "Invert thumb wheel scroll direction." msgstr "Invertera tumhjulets rullningsriktning." #: lib/logitech_receiver/settings_templates.py:504 msgid "Onboard Profiles" msgstr "Inbyggda profiler" #: lib/logitech_receiver/settings_templates.py:505 msgid "" "Enable an onboard profile, which controls report rate, sensitivity, and " "button actions" msgstr "" "Aktivera en inbyggd profil som styr rapporteringsfrekvens, känslighet och " "knappfunktioner" #: lib/logitech_receiver/settings_templates.py:549 #: lib/logitech_receiver/settings_templates.py:582 msgid "Report Rate" msgstr "Rapporteringsfrekvens" #: lib/logitech_receiver/settings_templates.py:551 #: lib/logitech_receiver/settings_templates.py:584 msgid "Frequency of device movement reports" msgstr "Frekvens för rapporter om enhetsrörelser" #: lib/logitech_receiver/settings_templates.py:551 #: lib/logitech_receiver/settings_templates.py:584 #: lib/logitech_receiver/settings_templates.py:1019 #: lib/logitech_receiver/settings_templates.py:1047 #: lib/logitech_receiver/settings_templates.py:1421 #: lib/logitech_receiver/settings_templates.py:1452 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:612 msgid "Divert crown events" msgstr "Avled kronhändelser" #: lib/logitech_receiver/settings_templates.py:613 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:621 msgid "Crown smooth scroll" msgstr "Kronans mjuka rullning" #: lib/logitech_receiver/settings_templates.py:622 msgid "Set crown smooth scroll" msgstr "Ställ in kronans mjuka rullning" #: lib/logitech_receiver/settings_templates.py:630 msgid "Divert G and M Keys" msgstr "Omdirigera G- och M-tangenterna" #: lib/logitech_receiver/settings_templates.py:631 msgid "" "Make G and M keys send HID++ notifications (which trigger Solaar rules but " "are otherwise ignored)." msgstr "" "Gör så att tangenterna G och M skickar HID++-meddelanden (som aktiverar " "Solaar-regler men annars ignoreras)." #: lib/logitech_receiver/settings_templates.py:645 msgid "Scroll Wheel Ratcheted" msgstr "Rullningshjul spärrat" #: lib/logitech_receiver/settings_templates.py:646 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:648 msgid "Freespinning" msgstr "Fri snurrning" #: lib/logitech_receiver/settings_templates.py:648 msgid "Ratcheted" msgstr "Spärrad" #: lib/logitech_receiver/settings_templates.py:655 msgid "Scroll Wheel Ratchet Speed" msgstr "Rullningshjulets spärrhastighet" #: lib/logitech_receiver/settings_templates.py:657 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:707 msgid "Scroll Wheel Ratchet Torque" msgstr "Vridmoment för rullhjulsspärr" #: lib/logitech_receiver/settings_templates.py:708 msgid "Change the torque needed to overcome the ratchet." msgstr "Ändra vridmomentet som behövs för att övervinna spärren." #: lib/logitech_receiver/settings_templates.py:743 msgid "Key/Button Actions" msgstr "Tangent-/knappåtgärder" #: lib/logitech_receiver/settings_templates.py:745 msgid "Change the action for the key or button." msgstr "Ändra åtgärden för tangenten eller knappen." #: lib/logitech_receiver/settings_templates.py:747 msgid "Overridden by diversion." msgstr "Åsidosatt av avledning." #: lib/logitech_receiver/settings_templates.py:749 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:924 msgid "Key/Button Diversion" msgstr "Tangent/knapp avledning" #: lib/logitech_receiver/settings_templates.py:925 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:928 #: lib/logitech_receiver/settings_templates.py:929 #: lib/logitech_receiver/settings_templates.py:930 msgid "Diverted" msgstr "Avledd" #: lib/logitech_receiver/settings_templates.py:928 #: lib/logitech_receiver/settings_templates.py:929 msgid "Mouse Gestures" msgstr "Musgester" #: lib/logitech_receiver/settings_templates.py:928 #: lib/logitech_receiver/settings_templates.py:929 #: lib/logitech_receiver/settings_templates.py:930 msgid "Regular" msgstr "Regelbunden" #: lib/logitech_receiver/settings_templates.py:928 msgid "Sliding DPI" msgstr "Glidande DPI" #: lib/logitech_receiver/settings_templates.py:1018 #: lib/logitech_receiver/settings_templates.py:1046 msgid "Sensitivity (DPI)" msgstr "Känslighet (DPI)" #: lib/logitech_receiver/settings_templates.py:1123 msgid "Sensitivity Switching" msgstr "Känslighetsväxling" #: lib/logitech_receiver/settings_templates.py:1125 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:1129 msgid "Off" msgstr "Av" #: lib/logitech_receiver/settings_templates.py:1160 msgid "Disable keys" msgstr "Inaktivera tangenter" #: lib/logitech_receiver/settings_templates.py:1161 msgid "Disable specific keyboard keys." msgstr "Inaktivera specifika tangentbordstangenter." #: lib/logitech_receiver/settings_templates.py:1164 #, python-format msgid "Disables the %s key." msgstr "Inaktiverar tangenten %s." #: lib/logitech_receiver/settings_templates.py:1177 #: lib/logitech_receiver/settings_templates.py:1234 msgid "Set OS" msgstr "Ställ in OS" #: lib/logitech_receiver/settings_templates.py:1178 #: lib/logitech_receiver/settings_templates.py:1235 msgid "Change keys to match OS." msgstr "Ändra tangenter för att matcha OS." #: lib/logitech_receiver/settings_templates.py:1247 msgid "Change Host" msgstr "Byt värd" #: lib/logitech_receiver/settings_templates.py:1248 msgid "Switch connection to a different host" msgstr "Byt anslutning till en annan värd" #: lib/logitech_receiver/settings_templates.py:1272 msgid "Performs a left click." msgstr "Utför ett vänsterklick." #: lib/logitech_receiver/settings_templates.py:1272 msgid "Single tap" msgstr "Enkelt tryck" #: lib/logitech_receiver/settings_templates.py:1273 msgid "Performs a right click." msgstr "Utför ett högerklick." #: lib/logitech_receiver/settings_templates.py:1273 msgid "Single tap with two fingers" msgstr "Enkelt tryck med två fingrar" #: lib/logitech_receiver/settings_templates.py:1274 msgid "Single tap with three fingers" msgstr "Enkelt tryck med tre fingrar" #: lib/logitech_receiver/settings_templates.py:1278 msgid "Double tap" msgstr "Dubbeltryck" #: lib/logitech_receiver/settings_templates.py:1278 msgid "Performs a double click." msgstr "Utför ett dubbelklick." #: lib/logitech_receiver/settings_templates.py:1279 msgid "Double tap with two fingers" msgstr "Dubbeltryck med två fingrar" #: lib/logitech_receiver/settings_templates.py:1280 msgid "Double tap with three fingers" msgstr "Dubbeltryck med tre fingrar" #: lib/logitech_receiver/settings_templates.py:1283 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:1283 msgid "Tap and drag" msgstr "Tryck och dra" #: lib/logitech_receiver/settings_templates.py:1285 msgid "Tap and drag with two fingers" msgstr "Tryck och dra med två fingrar" #: lib/logitech_receiver/settings_templates.py:1286 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:1288 msgid "Tap and drag with three fingers" msgstr "Tryck och dra med tre fingrar" #: lib/logitech_receiver/settings_templates.py:1291 msgid "Suppress tap and edge gestures" msgstr "Inaktivera tryck- och kantgester" #: lib/logitech_receiver/settings_templates.py:1292 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:1294 msgid "Scroll with one finger" msgstr "Bläddra med ett finger" #: lib/logitech_receiver/settings_templates.py:1294 #: lib/logitech_receiver/settings_templates.py:1295 #: lib/logitech_receiver/settings_templates.py:1298 msgid "Scrolls." msgstr "Rullningar." #: lib/logitech_receiver/settings_templates.py:1295 #: lib/logitech_receiver/settings_templates.py:1298 msgid "Scroll with two fingers" msgstr "Rulla med två fingrar" #: lib/logitech_receiver/settings_templates.py:1296 msgid "Scroll horizontally with two fingers" msgstr "Rulla horisontellt med två fingrar" #: lib/logitech_receiver/settings_templates.py:1296 msgid "Scrolls horizontally." msgstr "Rullar horisontellt." #: lib/logitech_receiver/settings_templates.py:1297 msgid "Scroll vertically with two fingers" msgstr "Rulla vertikalt med två fingrar" #: lib/logitech_receiver/settings_templates.py:1297 msgid "Scrolls vertically." msgstr "Rullar vertikalt." #: lib/logitech_receiver/settings_templates.py:1299 msgid "Inverts the scrolling direction." msgstr "Inverterar rullningsriktningen." #: lib/logitech_receiver/settings_templates.py:1299 msgid "Natural scrolling" msgstr "Naturlig rullning" #: lib/logitech_receiver/settings_templates.py:1300 msgid "Enables the thumbwheel." msgstr "Aktiverar tumhjulet." #: lib/logitech_receiver/settings_templates.py:1300 msgid "Thumbwheel" msgstr "Tumhjul" #: lib/logitech_receiver/settings_templates.py:1311 #: lib/logitech_receiver/settings_templates.py:1315 msgid "Swipe from the top edge" msgstr "Svep från den övre kanten" #: lib/logitech_receiver/settings_templates.py:1312 msgid "Swipe from the left edge" msgstr "Svep från vänstra kanten" #: lib/logitech_receiver/settings_templates.py:1313 msgid "Swipe from the right edge" msgstr "Svep från den högra kanten" #: lib/logitech_receiver/settings_templates.py:1314 msgid "Swipe from the bottom edge" msgstr "Svep från den nedre kanten" #: lib/logitech_receiver/settings_templates.py:1316 msgid "Swipe two fingers from the left edge" msgstr "Svep två fingrar från den vänstra kanten" #: lib/logitech_receiver/settings_templates.py:1317 msgid "Swipe two fingers from the right edge" msgstr "Svep två fingrar från den högra kanten" #: lib/logitech_receiver/settings_templates.py:1318 msgid "Swipe two fingers from the bottom edge" msgstr "Svep två fingrar från den nedre kanten" #: lib/logitech_receiver/settings_templates.py:1319 msgid "Swipe two fingers from the top edge" msgstr "Svep två fingrar från den övre kanten" #: lib/logitech_receiver/settings_templates.py:1320 #: lib/logitech_receiver/settings_templates.py:1324 msgid "Pinch to zoom out; spread to zoom in." msgstr "Nyp för att zooma ut; sprid för att zooma in." #: lib/logitech_receiver/settings_templates.py:1320 msgid "Zoom with two fingers." msgstr "Zooma med två fingrar." #: lib/logitech_receiver/settings_templates.py:1321 msgid "Pinch to zoom out." msgstr "Nyp för att zooma ut." #: lib/logitech_receiver/settings_templates.py:1322 msgid "Spread to zoom in." msgstr "Sprid för att zooma in." #: lib/logitech_receiver/settings_templates.py:1323 msgid "Zoom with three fingers." msgstr "Zooma med tre fingrar." #: lib/logitech_receiver/settings_templates.py:1324 msgid "Zoom with two fingers" msgstr "Zooma med två fingrar" #: lib/logitech_receiver/settings_templates.py:1342 msgid "Pixel zone" msgstr "Pixelzon" #: lib/logitech_receiver/settings_templates.py:1343 msgid "Ratio zone" msgstr "Ratiozon" #: lib/logitech_receiver/settings_templates.py:1344 msgid "Scale factor" msgstr "Skalfaktor" #: lib/logitech_receiver/settings_templates.py:1344 msgid "Sets the cursor speed." msgstr "Ställer in markörhastigheten." #: lib/logitech_receiver/settings_templates.py:1348 msgid "Left" msgstr "Vänster" #: lib/logitech_receiver/settings_templates.py:1348 msgid "Left-most coordinate." msgstr "Koordinaten längst till vänster." #: lib/logitech_receiver/settings_templates.py:1349 msgid "Bottom" msgstr "Nedre" #: lib/logitech_receiver/settings_templates.py:1349 msgid "Bottom coordinate." msgstr "Nedre koordinat." #: lib/logitech_receiver/settings_templates.py:1350 msgid "Width" msgstr "Bredd" #: lib/logitech_receiver/settings_templates.py:1350 msgid "Width." msgstr "Bredd." #: lib/logitech_receiver/settings_templates.py:1351 msgid "Height" msgstr "Höjd" #: lib/logitech_receiver/settings_templates.py:1351 msgid "Height." msgstr "Höjd." #: lib/logitech_receiver/settings_templates.py:1352 msgid "Cursor speed." msgstr "Markörhastighet." #: lib/logitech_receiver/settings_templates.py:1352 msgid "Scale" msgstr "Skala" #: lib/logitech_receiver/settings_templates.py:1358 msgid "Gestures" msgstr "Gester" #: lib/logitech_receiver/settings_templates.py:1359 msgid "Tweak the mouse/touchpad behaviour." msgstr "Justera musens/pekplattans beteende." #: lib/logitech_receiver/settings_templates.py:1375 msgid "Gestures Diversion" msgstr "Avledning av gester" #: lib/logitech_receiver/settings_templates.py:1376 msgid "Divert mouse/touchpad gestures." msgstr "Avleda mus-/pekplattagester." #: lib/logitech_receiver/settings_templates.py:1392 msgid "Gesture params" msgstr "Gestparametrar" #: lib/logitech_receiver/settings_templates.py:1393 msgid "Change numerical parameters of a mouse/touchpad." msgstr "Ändra numeriska parametrar för en mus/pekplatta." #: lib/logitech_receiver/settings_templates.py:1417 msgid "M-Key LEDs" msgstr "M-Key LED:ar" #: lib/logitech_receiver/settings_templates.py:1419 msgid "Control the M-Key LEDs." msgstr "Styr M-Key LED:ar." #: lib/logitech_receiver/settings_templates.py:1423 #: lib/logitech_receiver/settings_templates.py:1454 msgid "May need G Keys diverted to be effective." msgstr "Kan behöva omdirigera G-tangenter för att vara effektiva." #: lib/logitech_receiver/settings_templates.py:1429 #, python-format msgid "Lights up the %s key." msgstr "Lyser upp tangenten %s." #: lib/logitech_receiver/settings_templates.py:1448 msgid "MR-Key LED" msgstr "MR-Key LED" #: lib/logitech_receiver/settings_templates.py:1450 msgid "Control the MR-Key LED." msgstr "Styr MR-Key LED." #: lib/logitech_receiver/settings_templates.py:1471 msgid "Persistent Key/Button Mapping" msgstr "Ihållande nyckel/knappkartläggning" #: lib/logitech_receiver/settings_templates.py:1473 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:1475 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:1532 msgid "Sidetone" msgstr "Sidoton" #: lib/logitech_receiver/settings_templates.py:1533 msgid "Set sidetone level." msgstr "Ställ in sidotonsnivå." #: lib/logitech_receiver/settings_templates.py:1542 msgid "Equalizer" msgstr "Utjämnare" #: lib/logitech_receiver/settings_templates.py:1543 msgid "Set equalizer levels." msgstr "Ställ in utjämningsnivåer." #: lib/logitech_receiver/settings_templates.py:1565 msgid "Hz" msgstr "Hz" #: lib/logitech_receiver/settings_templates.py:1571 msgid "Power Management" msgstr "Strömhantering" #: lib/logitech_receiver/settings_templates.py:1572 msgid "Power off in minutes (0 for never)." msgstr "Stäng av på några minuter (0 för aldrig)." #: lib/logitech_receiver/settings_templates.py:1584 msgid "Brightness Control" msgstr "Ljusstyrkekontroll" #: lib/logitech_receiver/settings_templates.py:1585 msgid "Control overall brightness" msgstr "Kontrollera allmän ljusstyrka" #: lib/logitech_receiver/settings_templates.py:1628 #: lib/logitech_receiver/settings_templates.py:1681 msgid "LED Control" msgstr "LED-kontroll" #: lib/logitech_receiver/settings_templates.py:1629 #: lib/logitech_receiver/settings_templates.py:1682 msgid "Switch control of LED zones between device and Solaar" msgstr "Växla styrning av LED-zoner mellan enhet och Solaar" #: lib/logitech_receiver/settings_templates.py:1644 #: lib/logitech_receiver/settings_templates.py:1692 msgid "LED Zone Effects" msgstr "LED-zoneffekter" #: lib/logitech_receiver/settings_templates.py:1645 #: lib/logitech_receiver/settings_templates.py:1693 msgid "LED Control needs to be set to Solaar to be effective." msgstr "LED-kontrollen måste ställas in på Solaar för att fungera." #: lib/logitech_receiver/settings_templates.py:1645 #: lib/logitech_receiver/settings_templates.py:1693 msgid "Set effect for LED Zone" msgstr "Ställ in effekt för LED-zon" #: lib/logitech_receiver/settings_templates.py:1647 msgid "Color" msgstr "Färg" #: lib/logitech_receiver/settings_templates.py:1648 msgid "Speed" msgstr "Hastighet" #: lib/logitech_receiver/settings_templates.py:1649 msgid "Period" msgstr "Period" #: lib/logitech_receiver/settings_templates.py:1650 msgid "Intensity" msgstr "Intensitet" #: lib/logitech_receiver/settings_templates.py:1651 msgid "Ramp" msgstr "Ramp" #: lib/logitech_receiver/settings_templates.py:1666 msgid "LEDs" msgstr "LED" #: lib/logitech_receiver/settings_templates.py:1703 msgid "Per-key Lighting" msgstr "Belysning per tangent" #: lib/logitech_receiver/settings_templates.py:1704 msgid "Control per-key lighting." msgstr "Kontrollera belysning per tangent." #: lib/logitech_receiver/settings_templates.py:1786 msgid "Force Sensing Buttons" msgstr "Kraftavkännande knappar" #: lib/logitech_receiver/settings_templates.py:1787 msgid "Change the force required to activate button." msgstr "Ändra den kraft som krävs för att aktivera knappen." #: lib/logitech_receiver/settings_templates.py:1804 msgid "Force Sensing Button" msgstr "Kraftavkännande knapp" #: lib/logitech_receiver/settings_templates.py:1814 msgid "Haptic Feeback Level" msgstr "Haptisk återkopplingsnivå" #: lib/logitech_receiver/settings_templates.py:1815 msgid "Change power of haptic feedback. (Zero to turn off.)" msgstr "" "Ändra styrkan på den haptiska återkopplingen. (Noll för att stänga av.)" #: lib/logitech_receiver/settings_templates.py:1857 msgid "Play Haptic Waveform" msgstr "Spela upp haptisk vågform" #: lib/logitech_receiver/settings_templates.py:1858 msgid "Tell device to play a haptic waveform." msgstr "Be enheten att spela upp en haptisk vågform." #: lib/solaar/ui/__init__.py:120 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/model.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/model.py:63 msgid "Additional Programming" msgstr "Ytterligare programmering" #: lib/solaar/ui/about/model.py:64 msgid "GUI design" msgstr "Grafisk design av användargränssnitt" #: lib/solaar/ui/about/model.py:66 msgid "Testing" msgstr "Testning" #: lib/solaar/ui/about/model.py:74 msgid "Logitech documentation" msgstr "Logitech-dokumentation" #: lib/solaar/ui/action.py:88 lib/solaar/ui/action.py:92 #: lib/solaar/ui/window.py:208 msgid "Unpair" msgstr "Bryt parkoppling" #: lib/solaar/ui/action.py:91 lib/solaar/ui/diversion_rules.py:141 msgid "Cancel" msgstr "Avbryt" #: lib/solaar/ui/common.py:42 msgid "Permissions error" msgstr "Behörighetsfel" #: lib/solaar/ui/common.py:44 #, 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/common.py:46 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/common.py:49 msgid "Cannot connect to device error" msgstr "Kan inte ansluta till enhet-fel" #: lib/solaar/ui/common.py:51 #, 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/common.py:53 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/common.py:56 msgid "Unpairing failed" msgstr "Kunde inte bryta parkoppling" #: lib/solaar/ui/common.py:58 #, python-brace-format msgid "Failed to unpair %{device} from %{receiver}." msgstr "Misslyckades att bryta parkoppling mellan %{device} och %{receiver}." #: lib/solaar/ui/common.py:63 msgid "The receiver returned an error, with no further details." msgstr "Mottagaren rapporterade ett fel, utan ytterligare detaljer." #: lib/solaar/ui/config_panel.py:245 msgid "Complete - ENTER to change" msgstr "Komplett - ENTER för att ändra" #: lib/solaar/ui/config_panel.py:245 msgid "Incomplete" msgstr "Ofullständig" #: lib/solaar/ui/config_panel.py:491 lib/solaar/ui/config_panel.py:543 #, 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:642 msgid "Changes allowed" msgstr "Ändringar tillåtna" #: lib/solaar/ui/config_panel.py:643 msgid "No changes allowed" msgstr "Inga ändringar tillåtna" #: lib/solaar/ui/config_panel.py:644 msgid "Ignore this setting" msgstr "Ignorera denna inställning" #: lib/solaar/ui/config_panel.py:690 msgid "Working" msgstr "Fungerar" #: lib/solaar/ui/config_panel.py:693 msgid "Read/write operation failed." msgstr "Läs-/skrivoperationen misslyckades." #: lib/solaar/ui/desktop_notifications.py:112 msgid "unspecified reason" msgstr "ospecificerad anledning" #: lib/solaar/ui/diversion_rules.py:103 msgid "Built-in rules" msgstr "Inbyggda regler" #: lib/solaar/ui/diversion_rules.py:103 msgid "User-defined rules" msgstr "Användardefinierade regler" #: lib/solaar/ui/diversion_rules.py:105 lib/solaar/ui/diversion_rules.py:1126 msgid "Rule" msgstr "Regel" #: lib/solaar/ui/diversion_rules.py:106 lib/solaar/ui/diversion_rules.py:382 #: lib/solaar/ui/diversion_rules.py:509 msgid "Sub-rule" msgstr "Underregel" #: lib/solaar/ui/diversion_rules.py:108 msgid "[empty]" msgstr "[tom]" #: lib/solaar/ui/diversion_rules.py:132 msgid "Make changes permanent?" msgstr "Göra ändringarna permanenta?" #: lib/solaar/ui/diversion_rules.py:137 msgid "Yes" msgstr "Ja" #: lib/solaar/ui/diversion_rules.py:139 msgid "No" msgstr "Nej" #: lib/solaar/ui/diversion_rules.py:144 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:273 msgid "Paste here" msgstr "Klistra in här" #: lib/solaar/ui/diversion_rules.py:275 msgid "Paste above" msgstr "Klistra in ovan" #: lib/solaar/ui/diversion_rules.py:277 msgid "Paste below" msgstr "Klistra in nedan" #: lib/solaar/ui/diversion_rules.py:283 msgid "Paste rule here" msgstr "Klistra in regeln här" #: lib/solaar/ui/diversion_rules.py:285 msgid "Paste rule above" msgstr "Klistra in regeln ovan" #: lib/solaar/ui/diversion_rules.py:287 msgid "Paste rule below" msgstr "Klistra in regeln nedan" #: lib/solaar/ui/diversion_rules.py:291 msgid "Paste rule" msgstr "Klistra in regel" #: lib/solaar/ui/diversion_rules.py:306 msgid "Insert here" msgstr "Infoga här" #: lib/solaar/ui/diversion_rules.py:308 msgid "Insert above" msgstr "Infoga ovan" #: lib/solaar/ui/diversion_rules.py:310 msgid "Insert below" msgstr "Infoga nedan" #: lib/solaar/ui/diversion_rules.py:316 msgid "Insert new rule here" msgstr "Infoga ny regel här" #: lib/solaar/ui/diversion_rules.py:318 msgid "Insert new rule above" msgstr "Infoga ny regel ovan" #: lib/solaar/ui/diversion_rules.py:320 msgid "Insert new rule below" msgstr "Infoga ny regel nedan" #: lib/solaar/ui/diversion_rules.py:347 msgid "Flatten" msgstr "Platta till" #: lib/solaar/ui/diversion_rules.py:380 msgid "Insert" msgstr "Infoga" #: lib/solaar/ui/diversion_rules.py:383 lib/solaar/ui/diversion_rules.py:511 #: lib/solaar/ui/diversion_rules.py:1158 msgid "Or" msgstr "Eller" #: lib/solaar/ui/diversion_rules.py:384 lib/solaar/ui/diversion_rules.py:510 #: lib/solaar/ui/diversion_rules.py:1144 msgid "And" msgstr "Och" #: lib/solaar/ui/diversion_rules.py:386 msgid "Condition" msgstr "Villkor" #: lib/solaar/ui/diversion_rules.py:388 lib/solaar/ui/rule_conditions.py:154 msgid "Feature" msgstr "Funktion" #: lib/solaar/ui/diversion_rules.py:389 lib/solaar/ui/rule_conditions.py:188 msgid "Report" msgstr "Rapport" #: lib/solaar/ui/diversion_rules.py:390 lib/solaar/ui/rule_conditions.py:69 msgid "Process" msgstr "Process" #: lib/solaar/ui/diversion_rules.py:391 msgid "Mouse process" msgstr "Musprocess" #: lib/solaar/ui/diversion_rules.py:392 lib/solaar/ui/rule_conditions.py:225 msgid "Modifiers" msgstr "Modifierare" #: lib/solaar/ui/diversion_rules.py:393 lib/solaar/ui/rule_conditions.py:277 msgid "Key" msgstr "Tangent" #: lib/solaar/ui/diversion_rules.py:394 lib/solaar/ui/rule_conditions.py:318 msgid "KeyIsDown" msgstr "KeyIsDown" #: lib/solaar/ui/diversion_rules.py:395 lib/solaar/ui/diversion_rules.py:1432 msgid "Active" msgstr "Aktiv" #: lib/solaar/ui/diversion_rules.py:396 lib/solaar/ui/diversion_rules.py:1390 #: lib/solaar/ui/diversion_rules.py:1441 lib/solaar/ui/diversion_rules.py:1487 msgid "Device" msgstr "Enhet" #: lib/solaar/ui/diversion_rules.py:397 lib/solaar/ui/diversion_rules.py:1467 msgid "Host" msgstr "Värd" #: lib/solaar/ui/diversion_rules.py:398 lib/solaar/ui/diversion_rules.py:1506 msgid "Setting" msgstr "Inställning" #: lib/solaar/ui/diversion_rules.py:399 lib/solaar/ui/rule_conditions.py:333 #: lib/solaar/ui/rule_conditions.py:382 msgid "Test" msgstr "Testa" #: lib/solaar/ui/diversion_rules.py:400 lib/solaar/ui/rule_conditions.py:507 msgid "Test bytes" msgstr "Testa byte" #: lib/solaar/ui/diversion_rules.py:401 lib/solaar/ui/rule_conditions.py:608 msgid "Mouse Gesture" msgstr "Musgest" #: lib/solaar/ui/diversion_rules.py:405 msgid "Action" msgstr "Åtgärd" #: lib/solaar/ui/diversion_rules.py:407 lib/solaar/ui/rule_actions.py:138 msgid "Key press" msgstr "Knapptryckning" #: lib/solaar/ui/diversion_rules.py:408 lib/solaar/ui/rule_actions.py:191 msgid "Mouse scroll" msgstr "Musrullning" #: lib/solaar/ui/diversion_rules.py:409 lib/solaar/ui/rule_actions.py:256 msgid "Mouse click" msgstr "Musklick" #: lib/solaar/ui/diversion_rules.py:410 msgid "Set" msgstr "Ställ in" #: lib/solaar/ui/diversion_rules.py:411 lib/solaar/ui/rule_actions.py:328 msgid "Execute" msgstr "Utför" #: lib/solaar/ui/diversion_rules.py:412 lib/solaar/ui/diversion_rules.py:1189 msgid "Later" msgstr "Senare" #: lib/solaar/ui/diversion_rules.py:441 msgid "Insert new rule" msgstr "Infoga ny regel" #: lib/solaar/ui/diversion_rules.py:461 lib/solaar/ui/rule_actions.py:82 #: lib/solaar/ui/rule_actions.py:287 lib/solaar/ui/rule_conditions.py:556 msgid "Delete" msgstr "Ta bort" #: lib/solaar/ui/diversion_rules.py:483 msgid "Negate" msgstr "Negera" #: lib/solaar/ui/diversion_rules.py:507 msgid "Wrap with" msgstr "Omslut med" #: lib/solaar/ui/diversion_rules.py:537 msgid "Cut" msgstr "Klipp ut" #: lib/solaar/ui/diversion_rules.py:553 msgid "Paste" msgstr "Klistra in" #: lib/solaar/ui/diversion_rules.py:559 msgid "Copy" msgstr "Kopiera" #: lib/solaar/ui/diversion_rules.py:568 msgid "Solaar Rule Editor" msgstr "Solaar regelredigerare" #: lib/solaar/ui/diversion_rules.py:668 msgid "Save changes" msgstr "Spara ändringar" #: lib/solaar/ui/diversion_rules.py:673 msgid "Discard changes" msgstr "Släng ändringar" #: lib/solaar/ui/diversion_rules.py:1104 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:1169 msgid "" "Number of seconds to delay. Delay between 0 and 1 is done with higher " "precision." msgstr "" "Antal sekunder att fördröja. Fördröjning mellan 0 och 1 görs med högre " "precision." #: lib/solaar/ui/diversion_rules.py:1207 msgid "Not" msgstr "Inte" #: lib/solaar/ui/diversion_rules.py:1238 msgid "Toggle" msgstr "Växla" #: lib/solaar/ui/diversion_rules.py:1239 msgid "True" msgstr "Sant" #: lib/solaar/ui/diversion_rules.py:1240 msgid "False" msgstr "Falskt" #: lib/solaar/ui/diversion_rules.py:1253 msgid "Unsupported setting" msgstr "Inställning som inte stöds" #: lib/solaar/ui/diversion_rules.py:1396 lib/solaar/ui/diversion_rules.py:1415 #: lib/solaar/ui/diversion_rules.py:1493 lib/solaar/ui/diversion_rules.py:1748 #: lib/solaar/ui/diversion_rules.py:1766 msgid "Originating device" msgstr "Ursprungsenhet" #: lib/solaar/ui/diversion_rules.py:1428 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:1437 msgid "Device that originated the current notification." msgstr "Enhet som skapade det aktuella meddelandet." #: lib/solaar/ui/diversion_rules.py:1450 msgid "Name of host computer." msgstr "Namn på värddator." #: lib/solaar/ui/diversion_rules.py:1520 msgid "Value" msgstr "Värde" #: lib/solaar/ui/diversion_rules.py:1529 msgid "Item" msgstr "Objekt" #: lib/solaar/ui/diversion_rules.py:1808 msgid "Change setting on device" msgstr "Ändra inställning på enheten" #: lib/solaar/ui/diversion_rules.py:1824 msgid "Setting on device" msgstr "Inställning på enheten" #: lib/solaar/ui/pair_window.py:44 lib/solaar/ui/pair_window.py:167 #, python-format msgid "%(receiver_name)s: pair new device" msgstr "%(receiver_name)s: parkoppla ny enhet" #: lib/solaar/ui/pair_window.py:46 msgid "Bolt receivers are only compatible with Bolt devices." msgstr "Bolt-mottagare är endast kompatibla med Bolt-enheter." #: lib/solaar/ui/pair_window.py:48 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:51 msgid "Unifying receivers are only compatible with Unifying devices." msgstr "Unifying-mottagare är endast kompatibla med Unifying-enheter." #: lib/solaar/ui/pair_window.py:53 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:55 msgid "For most devices, turn on the device you want to pair." msgstr "För de flesta enheter, slå på den enhet du vill para ihop." #: lib/solaar/ui/pair_window.py:56 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:58 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:61 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 "" "För enheter med flera kanaler, tryck, håll ned och släpp knappen för den " "kanal du vill para ihop eller använd\n" "kanalväxlingsknappen för att välja en kanal och tryck sedan, håll ned och " "släpp kanalväxlingsknappen." #: lib/solaar/ui/pair_window.py:68 msgid "The channel indicator light should be blinking rapidly." msgstr "Lampan för kanalindikator ska blinka snabbt." #: lib/solaar/ui/pair_window.py:72 #, 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:78 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/pair_window.py:168 #, python-format msgid "Enter passcode on %(name)s." msgstr "Ange lösenkoden på %(name)s." #: lib/solaar/ui/pair_window.py:171 #, 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:176 msgid "left" msgstr "vänster" #: lib/solaar/ui/pair_window.py:176 msgid "right" msgstr "höger" #: lib/solaar/ui/pair_window.py:178 #, 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:221 msgid "The wireless link is not encrypted" msgstr "Den trådlösa anslutningen är inte krypterad" #: lib/solaar/ui/pair_window.py:226 msgid "Found a new device:" msgstr "Hittade en ny enhet:" #: lib/solaar/ui/pair_window.py:247 msgid "Pairing failed" msgstr "Parkoppling misslyckades" #: lib/solaar/ui/pair_window.py:249 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:251 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:253 msgid "More paired devices than receiver can support." msgstr "Fler parkopplade enheter än vad mottagaren kan stöda." #: lib/solaar/ui/pair_window.py:255 msgid "No further details are available about the error." msgstr "Inga ytterligare detaljer finns tillgängliga om felet." #: lib/solaar/ui/rule_actions.py:54 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/rule_actions.py:60 msgid "Add key" msgstr "Lägg till tangent" #: lib/solaar/ui/rule_actions.py:63 msgid "Click" msgstr "Klick" #: lib/solaar/ui/rule_actions.py:66 msgid "Depress" msgstr "Tryck ned" #: lib/solaar/ui/rule_actions.py:69 msgid "Release" msgstr "Släppa" #: lib/solaar/ui/rule_actions.py:153 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/rule_actions.py:211 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/rule_actions.py:216 msgid "Button" msgstr "Knapp" #: lib/solaar/ui/rule_actions.py:218 msgid "Action (and Count, if click)" msgstr "Åtgärd (och räkna, om klick)" #: lib/solaar/ui/rule_actions.py:269 msgid "Execute a command with arguments." msgstr "Utför ett kommando med argument." #: lib/solaar/ui/rule_actions.py:273 msgid "Add argument" msgstr "Lägg till argument" #: lib/solaar/ui/rule_conditions.py:52 msgid "X11 active process. For use in X11 only." msgstr "X11 aktiv process. Endast för användning i X11." #: lib/solaar/ui/rule_conditions.py:82 msgid "X11 mouse process. For use in X11 only." msgstr "X11 musprocess. Endast för användning i X11." #: lib/solaar/ui/rule_conditions.py:99 msgid "MouseProcess" msgstr "MouseProcess" #: lib/solaar/ui/rule_conditions.py:123 msgid "Feature name of notification triggering rule processing." msgstr "Funktionsnamn för avisering som utlöser regelbearbetning." #: lib/solaar/ui/rule_conditions.py:169 msgid "Report number of notification triggering rule processing." msgstr "Rapportnummer för avisering som utlöser regelbearbetning." #: lib/solaar/ui/rule_conditions.py:201 msgid "Active keyboard modifiers. Not always available in Wayland." msgstr "Aktiva tangentbordsmodifierare. Inte alltid tillgängligt i Wayland." #: lib/solaar/ui/rule_conditions.py:241 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-tangenter för att vidarekoppla tangenter och knappar." #: lib/solaar/ui/rule_conditions.py:250 msgid "Key down" msgstr "Tangent ner" #: lib/solaar/ui/rule_conditions.py:253 msgid "Key up" msgstr "Tangent upp" #: lib/solaar/ui/rule_conditions.py:293 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-tangenter för att vidarekoppla tangenter och knappar." #: lib/solaar/ui/rule_conditions.py:331 msgid "Test condition on notification triggering rule processing." msgstr "Testvillkor för meddelande som utlöser regelbearbetning." #: lib/solaar/ui/rule_conditions.py:335 msgid "Parameter" msgstr "Parameter" #: lib/solaar/ui/rule_conditions.py:408 msgid "begin (inclusive)" msgstr "börja (inklusive)" #: lib/solaar/ui/rule_conditions.py:409 msgid "end (exclusive)" msgstr "slut (exklusivt)" #: lib/solaar/ui/rule_conditions.py:417 msgid "range" msgstr "omfång" #: lib/solaar/ui/rule_conditions.py:420 msgid "minimum" msgstr "minimum" #: lib/solaar/ui/rule_conditions.py:421 msgid "maximum" msgstr "maximum" #: lib/solaar/ui/rule_conditions.py:423 #, 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/rule_conditions.py:426 lib/solaar/ui/rule_conditions.py:427 msgid "mask" msgstr "mask" #: lib/solaar/ui/rule_conditions.py:428 #, 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/rule_conditions.py:437 msgid "" "Bit or range test on bytes in notification message triggering rule " "processing." msgstr "" "Bit- eller omfångstest på byte i aviseringsmeddelande som utlöser " "regelbearbetning." #: lib/solaar/ui/rule_conditions.py:447 msgid "type" msgstr "typ" #: lib/solaar/ui/rule_conditions.py:535 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/rule_conditions.py:541 msgid "Add movement" msgstr "Lägg till rörelse" #: lib/solaar/ui/tray.py:55 msgid "No supported device found" msgstr "Ingen enhet som stöds hittades" #: lib/solaar/ui/tray.py:60 lib/solaar/ui/window.py:328 #, python-format msgid "About %s" msgstr "Om %s" #: lib/solaar/ui/tray.py:61 lib/solaar/ui/window.py:326 #, python-format msgid "Quit %s" msgstr "Avsluta %s" #: lib/solaar/ui/tray.py:273 lib/solaar/ui/tray.py:281 msgid "no receiver" msgstr "ingen mottagare" #: lib/solaar/ui/tray.py:294 lib/solaar/ui/tray.py:299 #: lib/solaar/ui/window.py:728 msgid "offline" msgstr "frånkopplad" #: lib/solaar/ui/tray.py:297 msgid "no status" msgstr "ingen status" #: lib/solaar/ui/window.py:110 msgid "Scanning" msgstr "Skannar" #: lib/solaar/ui/window.py:141 msgid "Battery" msgstr "Batteri" #: lib/solaar/ui/window.py:144 msgid "Wireless Link" msgstr "Trådlös anslutning" #: lib/solaar/ui/window.py:148 msgid "Lighting" msgstr "Belysning" #: lib/solaar/ui/window.py:182 msgid "Show Technical Details" msgstr "Visa tekniska detaljer" #: lib/solaar/ui/window.py:198 msgid "Pair new device" msgstr "Parkoppla ny enhet" #: lib/solaar/ui/window.py:216 msgid "Select a device" msgstr "Välj en enhet" #: lib/solaar/ui/window.py:331 msgid "Rule Editor" msgstr "Regelredigerare" #: lib/solaar/ui/window.py:522 msgid "Path" msgstr "Sökväg" #: lib/solaar/ui/window.py:524 msgid "USB ID" msgstr "USB-ID" #: lib/solaar/ui/window.py:527 lib/solaar/ui/window.py:529 #: lib/solaar/ui/window.py:544 lib/solaar/ui/window.py:546 msgid "Serial" msgstr "Seriell" #: lib/solaar/ui/window.py:533 msgid "Index" msgstr "Index" #: lib/solaar/ui/window.py:535 msgid "Wireless PID" msgstr "Trådlös-PID" #: lib/solaar/ui/window.py:537 msgid "Product ID" msgstr "Produkt-ID" #: lib/solaar/ui/window.py:539 msgid "Protocol" msgstr "Protokoll" #: lib/solaar/ui/window.py:539 msgid "Unknown" msgstr "Okänd" #: lib/solaar/ui/window.py:541 msgid "Polling rate" msgstr "Uppdateringshastighet" #: lib/solaar/ui/window.py:548 msgid "Unit ID" msgstr "Enhets-ID" #: lib/solaar/ui/window.py:559 msgid "none" msgstr "ingen" #: lib/solaar/ui/window.py:560 msgid "Notifications" msgstr "Aviseringar" #: lib/solaar/ui/window.py:604 msgid "No device paired." msgstr "Ingen enhet parkopplad." #: lib/solaar/ui/window.py:613 #, 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:620 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:624 #, 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:681 msgid "Battery Voltage" msgstr "Batterispänning" #: lib/solaar/ui/window.py:683 msgid "Voltage reported by battery" msgstr "Spänning rapporterad av batteri" #: lib/solaar/ui/window.py:685 msgid "Battery Level" msgstr "Batterinivå" #: lib/solaar/ui/window.py:687 msgid "Approximate level reported by battery" msgstr "Ungefärlig nivå rapporterad av batteriet" #: lib/solaar/ui/window.py:694 lib/solaar/ui/window.py:696 msgid "next reported " msgstr "nästa rapporterade " #: lib/solaar/ui/window.py:697 msgid " and next level to be reported." msgstr " och nästa nivå som ska rapporteras." #: lib/solaar/ui/window.py:702 msgid "last known" msgstr "senast kända" #: lib/solaar/ui/window.py:713 msgid "encrypted" msgstr "krypterad" #: lib/solaar/ui/window.py:715 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:717 msgid "not encrypted" msgstr "inte krypterad" #: lib/solaar/ui/window.py:721 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:737 #, python-format msgid "%(light_level)d lux" msgstr "%(light_level)d lux" #~ msgid "Turn illumination on or off on keyboard." #~ msgstr "Slå på eller stäng av belysningen på tangentbordet." #~ msgid "" #~ "Enable onboard profiles, which often control report rate and keyboard " #~ "lighting" #~ msgstr "" #~ "Aktivera inbyggda profiler, som ofta styr rapporthastighet och " #~ "tangentbordsbelysning" #~ msgid "Polling Rate (ms)" #~ msgstr "Uppdateringsfrekvens (ms)" #~ msgid "Frequency of device polling, in milliseconds" #~ msgstr "Frekvens för enhetsavfrågning, i millisekunder" #~ msgid "Divert G Keys" #~ msgstr "Avleda G-tangenter" #~ msgid "" #~ "Make G keys send GKEY HID++ notifications (which trigger Solaar rules but " #~ "are otherwise ignored)." #~ msgstr "" #~ "Få G-tangenter att skicka GKEY HID++-aviseringar (som utlöser Solaar-" #~ "regler men som annars ignoreras)." #~ msgid "May also make M keys and MR key send HID++ notifications" #~ msgstr "" #~ "Kan också få M-tangenter och MR-tangenten att skicka HID++-aviseringar" #, python-format #~ msgid "Battery: %(level)s" #~ msgstr "Batteri: %(level)s" #, python-format #~ msgid "Battery: %(percent)d%%" #~ msgstr "Batteri: %(percent)d%%" #, python-format #~ msgid "Lighting: %(level)s lux" #~ msgstr "Belysning: %(level)s lux" #~ msgid "Number of seconds to delay." #~ msgstr "Antal sekunder att fördröja." #~ msgid "Count and Action" #~ msgstr "Antal och åtgärd" #~ 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." #~ msgid "Turn on the device you want to pair." #~ msgstr "Slå på enheten du vill parkoppla." #, python-format #~ msgid "%(rate)d ms (%(rate_hz)dHz)" #~ msgstr "%(rate)d ms (%(rate_hz)dHz)" #~ 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" ================================================ FILE: po/tr.po ================================================ # 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" ================================================ FILE: po/uk.po ================================================ # Ukrainian translation for solaar # This file is distributed under the same license as the solaar package. # Kira, 2025. # msgid "" msgstr "" "Project-Id-Version: solaar 1.1.14\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-10-16 18:53+0300\n" "PO-Revision-Date: 2025-10-16 20:11+0300\n" "Last-Translator: Олександр Афанасьєв \n" "Language-Team: Ukrainian \n" "Language: uk\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" #: lib/logitech_receiver/base_usb.py:52 msgid "Bolt Receiver" msgstr "Приймач Bolt" #: lib/logitech_receiver/base_usb.py:64 msgid "Unifying Receiver" msgstr "Приймач Unifying" #: lib/logitech_receiver/base_usb.py:75 lib/logitech_receiver/base_usb.py:87 #: lib/logitech_receiver/base_usb.py:100 lib/logitech_receiver/base_usb.py:113 msgid "Nano Receiver" msgstr "Наноприймач" #: lib/logitech_receiver/base_usb.py:125 msgid "Lightspeed Receiver" msgstr "Приймач Lightspeed" #: lib/logitech_receiver/base_usb.py:135 msgid "EX100 Receiver 27 Mhz" msgstr "Приймач EX100 27 МГц" #: lib/logitech_receiver/common.py:649 #, python-format msgid "Battery: %(level)s (%(status)s)" msgstr "Батарея: %(level)s (%(status)s)" #: lib/logitech_receiver/common.py:652 #, python-format msgid "Battery: %(percent)d%% (%(status)s)" msgstr "Батарея: %(percent)d%% (%(status)s)" #: lib/logitech_receiver/hidpp20.py:1023 #: lib/logitech_receiver/settings_templates.py:295 msgid "Disabled" msgstr "Вимкнено" #: lib/logitech_receiver/hidpp20.py:1024 msgid "Static" msgstr "Статичний" #: lib/logitech_receiver/hidpp20.py:1025 msgid "Pulse" msgstr "Пульсація" #: lib/logitech_receiver/hidpp20.py:1026 msgid "Cycle" msgstr "Цикл" #: lib/logitech_receiver/hidpp20.py:1027 msgid "Boot" msgstr "Завантаження" #: lib/logitech_receiver/hidpp20.py:1028 msgid "Demo" msgstr "Демо" #: lib/logitech_receiver/hidpp20.py:1030 msgid "Breathe" msgstr "Дихання" #: lib/logitech_receiver/hidpp20.py:1033 msgid "Ripple" msgstr "Брижі" #: lib/logitech_receiver/hidpp20.py:1034 msgid "Decomposition" msgstr "Розкладання" #: lib/logitech_receiver/hidpp20.py:1035 msgid "Signature1" msgstr "Підпис1" #: lib/logitech_receiver/hidpp20.py:1036 msgid "Signature2" msgstr "Підпис2" #: lib/logitech_receiver/hidpp20.py:1037 msgid "CycleS" msgstr "ЦиклS" #: lib/logitech_receiver/hidpp20.py:1101 msgid "Unknown Location" msgstr "Невідоме місце" #: lib/logitech_receiver/hidpp20.py:1102 msgid "Primary" msgstr "Основний" #: lib/logitech_receiver/hidpp20.py:1103 msgid "Logo" msgstr "Логотип" #: lib/logitech_receiver/hidpp20.py:1104 msgid "Left Side" msgstr "Ліва сторона" #: lib/logitech_receiver/hidpp20.py:1105 msgid "Right Side" msgstr "Права сторона" #: lib/logitech_receiver/hidpp20.py:1106 msgid "Combined" msgstr "Комбінований" #: lib/logitech_receiver/hidpp20.py:1107 msgid "Primary 1" msgstr "Основний 1" #: lib/logitech_receiver/hidpp20.py:1108 msgid "Primary 2" msgstr "Основний 2" #: lib/logitech_receiver/hidpp20.py:1109 msgid "Primary 3" msgstr "Основний 3" #: lib/logitech_receiver/hidpp20.py:1110 msgid "Primary 4" msgstr "Основний 4" #: lib/logitech_receiver/hidpp20.py:1111 msgid "Primary 5" msgstr "Основний 5" #: lib/logitech_receiver/hidpp20.py:1112 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 lib/solaar/ui/window.py:700 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 lib/solaar/ui/window.py:555 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:156 lib/solaar/listener.py:243 msgid "connected" msgstr "під'єднано" #: lib/logitech_receiver/notifications.py:156 lib/solaar/listener.py:243 msgid "disconnected" msgstr "від'єднано" #: lib/logitech_receiver/notifications.py:182 msgid "unpaired" msgstr "пару розірвано" #: lib/logitech_receiver/notifications.py:231 msgid "powered on" msgstr "увімкнено" #: lib/logitech_receiver/notifications.py:283 msgid "ADC measurement notification" msgstr "сповіщення про вимірювання АЦП" #: lib/logitech_receiver/notifications.py:428 #: lib/logitech_receiver/notifications.py:483 msgid "pairing lock is closed" msgstr "блокування створення пари закрито" #: lib/logitech_receiver/notifications.py:428 #: lib/logitech_receiver/notifications.py:483 msgid "pairing lock is open" msgstr "блокування створення пари відкрито" #: lib/logitech_receiver/notifications.py:446 msgid "discovery lock is closed" msgstr "блокування виявлення закрито" #: lib/logitech_receiver/notifications.py:446 msgid "discovery lock is open" msgstr "блокування виявлення відкрито" #: lib/logitech_receiver/receiver.py:437 msgid "No paired devices." msgstr "Немає під'єднаних пристроїв." #: lib/logitech_receiver/receiver.py:439 lib/solaar/ui/window.py:606 #, 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:598 msgid "register" msgstr "реєстр" #: lib/logitech_receiver/settings.py:612 lib/logitech_receiver/settings.py:650 msgid "feature" msgstr "функція" #: lib/logitech_receiver/settings_templates.py:134 msgid "Swap Fx function" msgstr "Поміняти функцію Fx" #: lib/logitech_receiver/settings_templates.py:137 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:150 msgid "Hand Detection" msgstr "Виявлення рук" #: lib/logitech_receiver/settings_templates.py:151 msgid "Turn on illumination when the hands hover over the keyboard." msgstr "Вмикати підсвічування, коли руки знаходяться над клавіатурою." #: lib/logitech_receiver/settings_templates.py:158 msgid "Scroll Wheel Smooth Scrolling" msgstr "Плавне прокручування коліщатка" #: lib/logitech_receiver/settings_templates.py:159 #: lib/logitech_receiver/settings_templates.py:406 #: lib/logitech_receiver/settings_templates.py:435 msgid "High-sensitivity mode for vertical scroll with the wheel." msgstr "Режим високої чутливості для вертикального прокручування коліщатком." #: lib/logitech_receiver/settings_templates.py:166 msgid "Side Scrolling" msgstr "Бічне прокручування" #: lib/logitech_receiver/settings_templates.py:168 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:178 msgid "Sensitivity (DPI - older mice)" msgstr "Чутливість (DPI - для старих мишей)" #: lib/logitech_receiver/settings_templates.py:179 #: lib/logitech_receiver/settings_templates.py:983 #: lib/logitech_receiver/settings_templates.py:1011 msgid "Mouse movement sensitivity" msgstr "Чутливість руху миші" #: lib/logitech_receiver/settings_templates.py:252 msgid "Backlight Timed" msgstr "Підсвічування за таймером" #: lib/logitech_receiver/settings_templates.py:253 #: lib/logitech_receiver/settings_templates.py:393 msgid "Set illumination time for keyboard." msgstr "Встановити час підсвічування клавіатури." #: lib/logitech_receiver/settings_templates.py:264 msgid "Backlight" msgstr "Підсвічування" #: lib/logitech_receiver/settings_templates.py:265 msgid "" "Illumination level on keyboard. Changes made are only applied in Manual " "mode." msgstr "" "Рівень підсвічування клавіатури. Зміни застосовуються лише в ручному режимі." #: lib/logitech_receiver/settings_templates.py:297 msgid "Automatic" msgstr "Автоматично" #: lib/logitech_receiver/settings_templates.py:299 msgid "Manual" msgstr "Вручну" #: lib/logitech_receiver/settings_templates.py:301 msgid "Enabled" msgstr "Увімкнено" #: lib/logitech_receiver/settings_templates.py:307 msgid "Backlight Level" msgstr "Рівень підсвічування" #: lib/logitech_receiver/settings_templates.py:308 msgid "Illumination level on keyboard when in Manual mode." msgstr "Рівень підсвічування клавіатури в ручному режимі." #: lib/logitech_receiver/settings_templates.py:365 msgid "Backlight Delay Hands Out" msgstr "Затримка підсвічування (руки прибрано)" #: lib/logitech_receiver/settings_templates.py:366 msgid "" "Delay in seconds until backlight fades out with hands away from keyboard." msgstr "" "Затримка в секундах до згасання підсвічування, коли руки прибрані з " "клавіатури." #: lib/logitech_receiver/settings_templates.py:374 msgid "Backlight Delay Hands In" msgstr "Затримка підсвічування (руки на клавіатурі)" #: lib/logitech_receiver/settings_templates.py:375 msgid "Delay in seconds until backlight fades out with hands near keyboard." msgstr "" "Затримка в секундах до згасання підсвічування, коли руки біля клавіатури." #: lib/logitech_receiver/settings_templates.py:383 msgid "Backlight Delay Powered" msgstr "Затримка підсвічування (від живлення)" #: lib/logitech_receiver/settings_templates.py:384 msgid "Delay in seconds until backlight fades out with external power." msgstr "" "Затримка в секундах до згасання підсвічування при зовнішньому живленні." #: lib/logitech_receiver/settings_templates.py:392 msgid "Backlight (Seconds)" msgstr "Підсвічування (секунди)" #: lib/logitech_receiver/settings_templates.py:404 msgid "Scroll Wheel High Resolution" msgstr "Висока роздільна здатність коліщатка" #: lib/logitech_receiver/settings_templates.py:408 #: lib/logitech_receiver/settings_templates.py:437 msgid "Set to ignore if scrolling is abnormally fast or slow" msgstr "Ігнорувати, якщо прокручування ненормально швидке або повільне" #: lib/logitech_receiver/settings_templates.py:415 #: lib/logitech_receiver/settings_templates.py:446 msgid "Scroll Wheel Diversion" msgstr "Перенаправлення коліщатка прокрутки" #: lib/logitech_receiver/settings_templates.py:417 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:424 msgid "Scroll Wheel Direction" msgstr "Напрямок прокручування коліщатка" #: lib/logitech_receiver/settings_templates.py:425 msgid "Invert direction for vertical scroll with wheel." msgstr "Інвертувати напрямок вертикального прокручування коліщатком." #: lib/logitech_receiver/settings_templates.py:433 msgid "Scroll Wheel Resolution" msgstr "Роздільна здатність коліщатка прокрутки" #: lib/logitech_receiver/settings_templates.py:448 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:457 msgid "Sensitivity (Pointer Speed)" msgstr "Чутливість (швидкість вказівника)" #: lib/logitech_receiver/settings_templates.py:458 msgid "Speed multiplier for mouse (256 is normal multiplier)." msgstr "Множник швидкості для миші (256 — звичайний множник)." #: lib/logitech_receiver/settings_templates.py:468 msgid "Thumb Wheel Diversion" msgstr "Перенаправлення коліщатка для великого пальця" #: lib/logitech_receiver/settings_templates.py:470 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:479 msgid "Thumb Wheel Direction" msgstr "Напрямок коліщатка для великого пальця" #: lib/logitech_receiver/settings_templates.py:480 msgid "Invert thumb wheel scroll direction." msgstr "Інвертувати напрямок прокручування коліщатка для великого пальця." #: lib/logitech_receiver/settings_templates.py:500 msgid "Onboard Profiles" msgstr "Вбудовані профілі" #: lib/logitech_receiver/settings_templates.py:501 msgid "" "Enable an onboard profile, which controls report rate, sensitivity, and " "button actions" msgstr "" "Увімкнути вбудований профіль, який контролює частоту опитування, чутливість " "та дії кнопок" #: lib/logitech_receiver/settings_templates.py:545 #: lib/logitech_receiver/settings_templates.py:578 msgid "Report Rate" msgstr "Частота опитування" #: lib/logitech_receiver/settings_templates.py:547 #: lib/logitech_receiver/settings_templates.py:580 msgid "Frequency of device movement reports" msgstr "Частота звітів про рух пристрою" #: lib/logitech_receiver/settings_templates.py:547 #: lib/logitech_receiver/settings_templates.py:580 #: lib/logitech_receiver/settings_templates.py:1011 #: lib/logitech_receiver/settings_templates.py:1385 #: lib/logitech_receiver/settings_templates.py:1416 msgid "May need Onboard Profiles set to Disable to be effective." msgstr "Для ефективності може знадобитися вимкнути вбудовані профілі." #: lib/logitech_receiver/settings_templates.py:608 msgid "Divert crown events" msgstr "Перенаправляти події коронки" #: lib/logitech_receiver/settings_templates.py:609 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:617 msgid "Crown smooth scroll" msgstr "Плавне прокручування коронкою" #: lib/logitech_receiver/settings_templates.py:618 msgid "Set crown smooth scroll" msgstr "Встановити плавне прокручування коронкою" #: lib/logitech_receiver/settings_templates.py:626 msgid "Divert G and M Keys" msgstr "Перенаправити клавіші G та M" #: lib/logitech_receiver/settings_templates.py:627 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:641 msgid "Scroll Wheel Ratcheted" msgstr "Тріскачка коліщатка прокрутки" #: lib/logitech_receiver/settings_templates.py:642 msgid "" "Switch the mouse wheel between speed-controlled ratcheting and always " "freespin." msgstr "" "Перемикання коліщатка миші між тріскачкою з контролем швидкості та вільним " "обертанням." #: lib/logitech_receiver/settings_templates.py:644 msgid "Freespinning" msgstr "Вільне обертання" #: lib/logitech_receiver/settings_templates.py:644 msgid "Ratcheted" msgstr "Тріскачка" #: lib/logitech_receiver/settings_templates.py:651 msgid "Scroll Wheel Ratchet Speed" msgstr "Швидкість тріскачки коліщатка прокрутки" #: lib/logitech_receiver/settings_templates.py:653 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:707 msgid "Key/Button Actions" msgstr "Дії клавіш/кнопок" #: lib/logitech_receiver/settings_templates.py:709 msgid "Change the action for the key or button." msgstr "Змінити дію для клавіші або кнопки." #: lib/logitech_receiver/settings_templates.py:711 msgid "Overridden by diversion." msgstr "Перевизначено перенаправленням." #: lib/logitech_receiver/settings_templates.py:713 msgid "" "Changing important actions (such as for the left mouse button) can result in " "an unusable system." msgstr "" "Зміна важливих дій (наприклад, для лівої кнопки миші) може призвести до " "непрацездатності системи." #: lib/solaar/ui/__init__.py:120 msgid "Another Solaar process is already running so just expose its window" msgstr "Інший процес Solaar вже запущено, тому просто показуємо його вікно" #: lib/solaar/ui/about/model.py:36 msgid "" "Manages Logitech receivers,\n" "keyboards, mice, and tablets." msgstr "" "Керує приймачами, клавіатурами,\n" "мишами та планшетами Logitech." #: lib/solaar/ui/about/model.py:63 msgid "Additional Programming" msgstr "Додаткове програмування" #: lib/solaar/ui/about/model.py:64 msgid "GUI design" msgstr "Дизайн GUI" #: lib/solaar/ui/about/model.py:66 msgid "Testing" msgstr "Тестування" #: lib/solaar/ui/about/model.py:74 msgid "Logitech documentation" msgstr "Документація Logitech" #: lib/solaar/ui/action.py:88 lib/solaar/ui/action.py:92 #: lib/solaar/ui/window.py:208 msgid "Unpair" msgstr "Розірвати пару" #: lib/solaar/ui/action.py:91 lib/solaar/ui/diversion_rules.py:141 msgid "Cancel" msgstr "Скасувати" #: lib/solaar/ui/common.py:42 msgid "Permissions error" msgstr "Помилка дозволів" #: lib/solaar/ui/common.py:44 #, 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:46 msgid "" "If you've just installed Solaar, try disconnecting the receiver or device " "and then reconnecting it." msgstr "" "Якщо ви щойно встановили Solaar, спробуйте від'єднати та знову під'єднати " "приймач або пристрій." #: lib/solaar/ui/common.py:49 msgid "Cannot connect to device error" msgstr "Помилка підключення до пристрою" #: lib/solaar/ui/common.py:51 #, 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:53 msgid "" "Try disconnecting the device and then reconnecting it or turning it off and " "then on." msgstr "" "Спробуйте від'єднати та знову під'єднати пристрій або вимкнути та увімкнути " "його." #: lib/solaar/ui/common.py:56 msgid "Unpairing failed" msgstr "Не вдалося розірвати пару" #: lib/solaar/ui/common.py:58 #, python-brace-format msgid "Failed to unpair %{device} from %{receiver}." msgstr "Не вдалося розірвати пару між %{device} та %{receiver}." #: lib/solaar/ui/common.py:63 msgid "The receiver returned an error, with no further details." msgstr "Приймач повернув помилку без додаткових деталей." #: lib/solaar/ui/config_panel.py:241 msgid "Complete - ENTER to change" msgstr "Завершено - ENTER для зміни" #: lib/solaar/ui/config_panel.py:241 msgid "Incomplete" msgstr "Не завершено" #: lib/solaar/ui/config_panel.py:487 lib/solaar/ui/config_panel.py:539 #, python-format msgid "%d value" msgid_plural "%d values" msgstr[0] "%d значення" msgstr[1] "%d значення" msgstr[2] "%d значень" #: lib/solaar/ui/config_panel.py:621 msgid "Changes allowed" msgstr "Зміни дозволено" #: lib/solaar/ui/config_panel.py:622 msgid "No changes allowed" msgstr "Зміни не дозволено" #: lib/solaar/ui/config_panel.py:623 msgid "Ignore this setting" msgstr "Ігнорувати це налаштування" #: lib/solaar/ui/config_panel.py:667 msgid "Working" msgstr "Працюю" #: lib/solaar/ui/config_panel.py:670 msgid "Read/write operation failed." msgstr "Операція читання/запису не вдалася." #: lib/solaar/ui/desktop_notifications.py:112 msgid "unspecified reason" msgstr "невідома причина" #: lib/solaar/ui/diversion_rules.py:103 msgid "Built-in rules" msgstr "Вбудовані правила" #: lib/solaar/ui/diversion_rules.py:103 msgid "User-defined rules" msgstr "Правила користувача" #: lib/solaar/ui/diversion_rules.py:105 lib/solaar/ui/diversion_rules.py:1126 msgid "Rule" msgstr "Правило" #: lib/solaar/ui/diversion_rules.py:106 lib/solaar/ui/diversion_rules.py:382 #: lib/solaar/ui/diversion_rules.py:509 msgid "Sub-rule" msgstr "Підправило" #: lib/solaar/ui/diversion_rules.py:108 msgid "[empty]" msgstr "[порожньо]" #: lib/solaar/ui/diversion_rules.py:132 msgid "Make changes permanent?" msgstr "Зробити зміни постійними?" #: lib/solaar/ui/diversion_rules.py:137 msgid "Yes" msgstr "Так" #: lib/solaar/ui/diversion_rules.py:139 msgid "No" msgstr "Ні" #: lib/solaar/ui/diversion_rules.py:144 msgid "If you choose No, changes will be lost when Solaar is closed." msgstr "Якщо ви оберете 'Ні', зміни буде втрачено при закритті Solaar." #: lib/solaar/ui/diversion_rules.py:273 msgid "Paste here" msgstr "Вставити сюди" #: lib/solaar/ui/diversion_rules.py:275 msgid "Paste above" msgstr "Вставити вище" #: lib/solaar/ui/diversion_rules.py:277 msgid "Paste below" msgstr "Вставити нижче" #: lib/solaar/ui/diversion_rules.py:283 msgid "Paste rule here" msgstr "Вставити правило сюди" #: lib/solaar/ui/diversion_rules.py:285 msgid "Paste rule above" msgstr "Вставити правило вище" #: lib/solaar/ui/diversion_rules.py:287 msgid "Paste rule below" msgstr "Вставити правило нижче" #: lib/solaar/ui/diversion_rules.py:291 msgid "Paste rule" msgstr "Вставити правило" #: lib/solaar/ui/diversion_rules.py:306 msgid "Insert here" msgstr "Вставити сюди" #: lib/solaar/ui/diversion_rules.py:308 msgid "Insert above" msgstr "Вставити вище" #: lib/solaar/ui/diversion_rules.py:310 msgid "Insert below" msgstr "Вставити нижче" #: lib/solaar/ui/diversion_rules.py:316 msgid "Insert new rule here" msgstr "Вставити нове правило сюди" #: lib/solaar/ui/diversion_rules.py:318 msgid "Insert new rule above" msgstr "Вставити нове правило вище" #: lib/solaar/ui/diversion_rules.py:320 msgid "Insert new rule below" msgstr "Вставити нове правило нижче" #: lib/solaar/ui/diversion_rules.py:347 msgid "Flatten" msgstr "Сплощити" #: lib/solaar/ui/diversion_rules.py:380 msgid "Insert" msgstr "Вставити" #: lib/solaar/ui/diversion_rules.py:383 lib/solaar/ui/diversion_rules.py:511 #: lib/solaar/ui/diversion_rules.py:1158 msgid "Or" msgstr "Або" #: lib/solaar/ui/diversion_rules.py:384 lib/solaar/ui/diversion_rules.py:510 #: lib/solaar/ui/diversion_rules.py:1144 msgid "And" msgstr "І" #: lib/solaar/ui/diversion_rules.py:386 msgid "Condition" msgstr "Умова" #: lib/solaar/ui/diversion_rules.py:388 lib/solaar/ui/rule_conditions.py:154 msgid "Feature" msgstr "Функція" #: lib/solaar/ui/diversion_rules.py:389 lib/solaar/ui/rule_conditions.py:188 msgid "Report" msgstr "Звіт" #: lib/solaar/ui/diversion_rules.py:390 lib/solaar/ui/rule_conditions.py:69 msgid "Process" msgstr "Процес" #: lib/solaar/ui/diversion_rules.py:391 msgid "Mouse process" msgstr "Процес миші" #: lib/solaar/ui/diversion_rules.py:392 lib/solaar/ui/rule_conditions.py:225 msgid "Modifiers" msgstr "Модифікатори" #: lib/solaar/ui/diversion_rules.py:393 lib/solaar/ui/rule_conditions.py:277 msgid "Key" msgstr "Клавіша" #: lib/solaar/ui/diversion_rules.py:394 lib/solaar/ui/rule_conditions.py:318 msgid "KeyIsDown" msgstr "КлавішаНатиснута" #: lib/solaar/ui/diversion_rules.py:395 lib/solaar/ui/diversion_rules.py:1432 msgid "Active" msgstr "Активний" #: lib/solaar/ui/diversion_rules.py:396 lib/solaar/ui/diversion_rules.py:1390 #: lib/solaar/ui/diversion_rules.py:1441 lib/solaar/ui/diversion_rules.py:1487 msgid "Device" msgstr "Пристрій" #: lib/solaar/ui/diversion_rules.py:397 lib/solaar/ui/diversion_rules.py:1467 msgid "Host" msgstr "Хост" #: lib/solaar/ui/diversion_rules.py:398 lib/solaar/ui/diversion_rules.py:1506 msgid "Setting" msgstr "Налаштування" #: lib/solaar/ui/diversion_rules.py:399 lib/solaar/ui/rule_conditions.py:333 #: lib/solaar/ui/rule_conditions.py:382 msgid "Test" msgstr "Тест" #: lib/solaar/ui/diversion_rules.py:400 lib/solaar/ui/rule_conditions.py:507 msgid "Test bytes" msgstr "Тестові байти" #: lib/solaar/ui/diversion_rules.py:401 lib/solaar/ui/rule_conditions.py:608 msgid "Mouse Gesture" msgstr "Жест миші" #: lib/solaar/ui/diversion_rules.py:405 msgid "Action" msgstr "Дія" #: lib/solaar/ui/diversion_rules.py:407 lib/solaar/ui/rule_actions.py:138 msgid "Key press" msgstr "Натискання клавіші" #: lib/solaar/ui/diversion_rules.py:408 lib/solaar/ui/rule_actions.py:191 msgid "Mouse scroll" msgstr "Прокручування миші" #: lib/solaar/ui/diversion_rules.py:409 lib/solaar/ui/rule_actions.py:254 msgid "Mouse click" msgstr "Клік миші" #: lib/solaar/ui/diversion_rules.py:410 msgid "Set" msgstr "Встановити" #: lib/solaar/ui/diversion_rules.py:411 lib/solaar/ui/rule_actions.py:326 msgid "Execute" msgstr "Виконати" #: lib/solaar/ui/diversion_rules.py:412 lib/solaar/ui/diversion_rules.py:1189 msgid "Later" msgstr "Пізніше" #: lib/solaar/ui/diversion_rules.py:441 msgid "Insert new rule" msgstr "Вставити нове правило" #: lib/solaar/ui/diversion_rules.py:461 lib/solaar/ui/rule_actions.py:82 #: lib/solaar/ui/rule_actions.py:285 lib/solaar/ui/rule_conditions.py:556 msgid "Delete" msgstr "Видалити" #: lib/solaar/ui/diversion_rules.py:483 msgid "Negate" msgstr "Заперечити" #: lib/solaar/ui/diversion_rules.py:507 msgid "Wrap with" msgstr "Огорнути в" #: lib/solaar/ui/diversion_rules.py:537 msgid "Cut" msgstr "Вирізати" #: lib/solaar/ui/diversion_rules.py:553 msgid "Paste" msgstr "Вставити" #: lib/solaar/ui/diversion_rules.py:559 msgid "Copy" msgstr "Копіювати" #: lib/solaar/ui/diversion_rules.py:568 msgid "Solaar Rule Editor" msgstr "Редактор правил Solaar" #: lib/solaar/ui/diversion_rules.py:668 msgid "Save changes" msgstr "Зберегти зміни" #: lib/solaar/ui/diversion_rules.py:673 msgid "Discard changes" msgstr "Скасувати зміни" #: lib/solaar/ui/diversion_rules.py:1104 msgid "This editor does not support the selected rule component yet." msgstr "Цей редактор ще не підтримує вибраний компонент правила." #: lib/solaar/ui/diversion_rules.py:1169 msgid "" "Number of seconds to delay.\n" "Delay between 0 and 1 is done with higher precision." msgstr "" "Кількість секунд для затримки.\n" "Затримка від 0 до 1 виконується з вищою точністю." #: lib/solaar/ui/diversion_rules.py:1207 msgid "Not" msgstr "Не" #: lib/solaar/ui/diversion_rules.py:1238 msgid "Toggle" msgstr "Перемкнути" #: lib/solaar/ui/diversion_rules.py:1239 msgid "True" msgstr "Істина" #: lib/solaar/ui/diversion_rules.py:1240 msgid "False" msgstr "Хиба" #: lib/solaar/ui/diversion_rules.py:1253 msgid "Unsupported setting" msgstr "Налаштування не підтримується" #: lib/solaar/ui/diversion_rules.py:1396 lib/solaar/ui/diversion_rules.py:1415 #: lib/solaar/ui/diversion_rules.py:1493 lib/solaar/ui/diversion_rules.py:1748 #: lib/solaar/ui/diversion_rules.py:1766 msgid "Originating device" msgstr "Пристрій-джерело" #: lib/solaar/ui/diversion_rules.py:1428 msgid "Device is active and its settings can be changed." msgstr "Пристрій активний, і його налаштування можна змінювати." #: lib/solaar/ui/diversion_rules.py:1437 msgid "Device that originated the current notification." msgstr "Пристрій, який надіслав поточне сповіщення." #: lib/solaar/ui/diversion_rules.py:1450 msgid "Name of host computer." msgstr "Ім'я комп'ютера-хоста." #: lib/solaar/ui/diversion_rules.py:1520 msgid "Value" msgstr "Значення" #: lib/solaar/ui/diversion_rules.py:1529 msgid "Item" msgstr "Елемент" #: lib/solaar/ui/diversion_rules.py:1808 msgid "Change setting on device" msgstr "Змінити налаштування на пристрої" #: lib/solaar/ui/diversion_rules.py:1824 msgid "Setting on device" msgstr "Налаштування на пристрої" #: lib/solaar/ui/pair_window.py:44 lib/solaar/ui/pair_window.py:167 #, python-format msgid "%(receiver_name)s: pair new device" msgstr "%(receiver_name)s: створити пару з новим пристроєм" #: lib/solaar/ui/pair_window.py:46 msgid "Bolt receivers are only compatible with Bolt devices." msgstr "Приймачі Bolt сумісні лише з пристроями Bolt." #: lib/solaar/ui/pair_window.py:48 msgid "Press a pairing button or key until the pairing light flashes quickly." msgstr "" "Натисніть кнопку або клавішу створення пари, доки індикатор не почне швидко " "блимати." #: lib/solaar/ui/pair_window.py:51 msgid "Unifying receivers are only compatible with Unifying devices." msgstr "Приймачі Unifying сумісні лише з пристроями Unifying." #: lib/solaar/ui/pair_window.py:53 msgid "Other receivers are only compatible with a few devices." msgstr "Інші приймачі сумісні лише з кількома пристроями." #: lib/solaar/ui/pair_window.py:55 msgid "For most devices, turn on the device you want to pair." msgstr "" "Для більшості пристроїв увімкніть пристрій, з яким хочете створити пару." #: lib/solaar/ui/pair_window.py:56 msgid "If the device is already turned on, turn it off and on again." msgstr "Якщо пристрій уже увімкнено, вимкніть його та увімкніть знову." #: lib/solaar/ui/pair_window.py:58 msgid "The device must not be paired with a nearby powered-on receiver." msgstr "" "Пристрій не повинен бути з'єднаний з іншим увімкненим приймачем поблизу." #: lib/solaar/ui/pair_window.py:61 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 "" "Для пристроїв з кількома каналами натисніть, утримуйте та відпустіть кнопку " "потрібного каналу\n" "або скористайтеся кнопкою перемикання каналів, щоб вибрати канал, а потім " "натисніть, утримуйте та відпустіть її." #: lib/solaar/ui/pair_window.py:68 msgid "The channel indicator light should be blinking rapidly." msgstr "Індикатор каналу повинен швидко блимати." #: lib/solaar/ui/pair_window.py:72 #, 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 спроби створення пари." msgstr[2] "" "\n" "\n" "Цей приймач має ще %d спроб створення пари." #: lib/solaar/ui/pair_window.py:78 msgid "" "\n" "Cancelling at this point will not use up a pairing." msgstr "" "\n" "Скасування на цьому етапі не використає спробу створення пари." #: lib/solaar/ui/pair_window.py:168 #, python-format msgid "Enter passcode on %(name)s." msgstr "Введіть пароль на %(name)s." #: lib/solaar/ui/pair_window.py:171 #, python-format msgid "Type %(passcode)s and then press the enter key." msgstr "Введіть %(passcode)s, а потім натисніть клавішу Enter." #: lib/solaar/ui/pair_window.py:176 msgid "left" msgstr "ліва" #: lib/solaar/ui/pair_window.py:176 msgid "right" msgstr "права" #: lib/solaar/ui/pair_window.py:178 #, 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:221 msgid "The wireless link is not encrypted" msgstr "Бездротове з'єднання не зашифроване" #: lib/solaar/ui/pair_window.py:226 msgid "Found a new device:" msgstr "Знайдено новий пристрій:" #: lib/solaar/ui/pair_window.py:247 msgid "Pairing failed" msgstr "Не вдалося створити пару" #: lib/solaar/ui/pair_window.py:249 msgid "Make sure your device is within range, and has a decent battery charge." msgstr "" "Переконайтеся, що ваш пристрій знаходиться в зоні досяжності та має " "достатній заряд батареї." #: lib/solaar/ui/pair_window.py:251 msgid "A new device was detected, but it is not compatible with this receiver." msgstr "Було виявлено новий пристрій, але він не сумісний з цим приймачем." #: lib/solaar/ui/pair_window.py:253 msgid "More paired devices than receiver can support." msgstr "Більше під'єднаних пристроїв, ніж може підтримувати приймач." #: lib/solaar/ui/pair_window.py:255 msgid "No further details are available about the error." msgstr "Додаткові відомості про помилку відсутні." #: lib/solaar/ui/rule_actions.py:54 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:60 msgid "Add key" msgstr "Додати клавішу" #: lib/solaar/ui/rule_actions.py:63 msgid "Click" msgstr "Клік" #: lib/solaar/ui/rule_actions.py:66 msgid "Depress" msgstr "Натиснути" #: lib/solaar/ui/rule_actions.py:69 msgid "Release" msgstr "Відпустити" #: lib/solaar/ui/rule_actions.py:153 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:211 msgid "" "Simulate a mouse click.\n" "On Wayland requires write access to /dev/uinput." msgstr "" "Імітувати клік миші.\n" "У Wayland потрібен дозвіл на запис до /dev/uinput." #: lib/solaar/ui/rule_actions.py:216 msgid "Button" msgstr "Кнопка" #: lib/solaar/ui/rule_actions.py:217 msgid "Count and Action" msgstr "Кількість та дія" #: lib/solaar/ui/rule_actions.py:267 msgid "Execute a command with arguments." msgstr "Виконати команду з аргументами." #: lib/solaar/ui/rule_actions.py:271 msgid "Add argument" msgstr "Додати аргумент" #: lib/solaar/ui/rule_conditions.py:52 msgid "" "X11 active process.\n" "For use in X11 only." msgstr "" "Активний процес X11.\n" "Використовувати лише в X11." #: lib/solaar/ui/rule_conditions.py:82 msgid "" "X11 mouse process.\n" "For use in X11 only." msgstr "" "Процес миші X11.\n" "Використовувати лише в X11." #: lib/solaar/ui/rule_conditions.py:99 msgid "MouseProcess" msgstr "ПроцесМиші" #: lib/solaar/ui/rule_conditions.py:123 msgid "Feature name of notification triggering rule processing." msgstr "Назва функції сповіщення, що запускає обробку правила." #: lib/solaar/ui/rule_conditions.py:169 msgid "Report number of notification triggering rule processing." msgstr "Номер звіту сповіщення, що запускає обробку правила." #: lib/solaar/ui/rule_conditions.py:201 msgid "" "Active keyboard modifiers.\n" "Not always available in Wayland." msgstr "" "Активні модифікатори клавіатури.\n" "Не завжди доступні у Wayland." #: lib/solaar/ui/rule_conditions.py:241 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:250 msgid "Key down" msgstr "Клавішу натиснуто" #: lib/solaar/ui/rule_conditions.py:253 msgid "Key up" msgstr "Клавішу відпущено" #: lib/solaar/ui/rule_conditions.py:293 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:331 msgid "Test condition on notification triggering rule processing." msgstr "Перевірка умови для сповіщення, що запускає обробку правила." #: lib/solaar/ui/rule_conditions.py:335 msgid "Parameter" msgstr "Параметр" #: lib/solaar/ui/rule_conditions.py:408 msgid "begin (inclusive)" msgstr "початок (включно)" #: lib/solaar/ui/rule_conditions.py:409 msgid "end (exclusive)" msgstr "кінець (виключно)" #: lib/solaar/ui/rule_conditions.py:417 msgid "range" msgstr "діапазон" #: lib/solaar/ui/rule_conditions.py:420 msgid "minimum" msgstr "мінімум" #: lib/solaar/ui/rule_conditions.py:421 msgid "maximum" msgstr "максимум" #: lib/solaar/ui/rule_conditions.py:423 #, 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:426 lib/solaar/ui/rule_conditions.py:427 msgid "mask" msgstr "маска" #: lib/solaar/ui/rule_conditions.py:428 #, 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:437 msgid "" "Bit or range test on bytes in notification message triggering rule " "processing." msgstr "" "Бітовий або діапазонний тест байтів у повідомленні-сповіщенні, що запускає " "обробку правила." #: lib/solaar/ui/rule_conditions.py:447 msgid "type" msgstr "тип" #: lib/solaar/ui/rule_conditions.py:535 msgid "" "Mouse gesture with optional initiating button followed by zero or more mouse " "movements." msgstr "" "Жест миші з необов'язковою кнопкою-ініціатором, за якою слідують нуль або " "більше рухів миші." #: lib/solaar/ui/rule_conditions.py:541 msgid "Add movement" msgstr "Додати рух" #: lib/solaar/ui/tray.py:55 msgid "No supported device found" msgstr "Не знайдено підтримуваних пристроїв" #: lib/solaar/ui/tray.py:60 lib/solaar/ui/window.py:328 #, python-format msgid "About %s" msgstr "Про %s" #: lib/solaar/ui/tray.py:61 lib/solaar/ui/window.py:326 #, python-format msgid "Quit %s" msgstr "Вийти з %s" #: lib/solaar/ui/tray.py:273 lib/solaar/ui/tray.py:281 msgid "no receiver" msgstr "немає приймача" #: lib/solaar/ui/tray.py:294 lib/solaar/ui/tray.py:299 #: lib/solaar/ui/window.py:728 msgid "offline" msgstr "офлайн" #: lib/solaar/ui/tray.py:297 msgid "no status" msgstr "немає статусу" #: lib/solaar/ui/window.py:110 msgid "Scanning" msgstr "Сканування" #: lib/solaar/ui/window.py:141 msgid "Battery" msgstr "Батарея" #: lib/solaar/ui/window.py:144 msgid "Wireless Link" msgstr "Бездротове з'єднання" #: lib/solaar/ui/window.py:148 msgid "Lighting" msgstr "Підсвічування" #: lib/solaar/ui/window.py:182 msgid "Show Technical Details" msgstr "Показати технічні деталі" #: lib/solaar/ui/window.py:198 msgid "Pair new device" msgstr "Створити пару з новим пристроєм" #: lib/solaar/ui/window.py:216 msgid "Select a device" msgstr "Виберіть пристрій" #: lib/solaar/ui/window.py:331 msgid "Rule Editor" msgstr "Редактор правил" #: lib/solaar/ui/window.py:522 msgid "Path" msgstr "Шлях" #: lib/solaar/ui/window.py:524 msgid "USB ID" msgstr "USB ID" #: lib/solaar/ui/window.py:527 lib/solaar/ui/window.py:529 #: lib/solaar/ui/window.py:544 lib/solaar/ui/window.py:546 msgid "Serial" msgstr "Серійний номер" #: lib/solaar/ui/window.py:533 msgid "Index" msgstr "Індекс" #: lib/solaar/ui/window.py:535 msgid "Wireless PID" msgstr "Бездротовий PID" #: lib/solaar/ui/window.py:537 msgid "Product ID" msgstr "ID продукту" #: lib/solaar/ui/window.py:539 msgid "Protocol" msgstr "Протокол" #: lib/solaar/ui/window.py:539 msgid "Unknown" msgstr "Невідомо" #: lib/solaar/ui/window.py:541 msgid "Polling rate" msgstr "Частота опитування" #: lib/solaar/ui/window.py:548 msgid "Unit ID" msgstr "ID одиниці" #: lib/solaar/ui/window.py:559 msgid "none" msgstr "немає" #: lib/solaar/ui/window.py:560 msgid "Notifications" msgstr "Сповіщення" #: lib/solaar/ui/window.py:604 msgid "No device paired." msgstr "Немає під'єднаних пристроїв." #: lib/solaar/ui/window.py:613 #, 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:620 msgid "Only one device can be paired to this receiver." msgstr "До цього приймача можна під'єднати лише один пристрій." #: lib/solaar/ui/window.py:624 #, 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:681 msgid "Battery Voltage" msgstr "Напруга батареї" #: lib/solaar/ui/window.py:683 msgid "Voltage reported by battery" msgstr "Напруга, що повідомляється батареєю" #: lib/solaar/ui/window.py:685 msgid "Battery Level" msgstr "Рівень заряду батареї" #: lib/solaar/ui/window.py:687 msgid "Approximate level reported by battery" msgstr "Приблизний рівень, що повідомляється батареєю" #: lib/solaar/ui/window.py:694 lib/solaar/ui/window.py:696 msgid "next reported " msgstr "наступний повідомлений " #: lib/solaar/ui/window.py:697 msgid " and next level to be reported." msgstr " та наступний рівень, що буде повідомлено." #: lib/solaar/ui/window.py:702 msgid "last known" msgstr "останній відомий" #: lib/solaar/ui/window.py:713 msgid "encrypted" msgstr "зашифровано" #: lib/solaar/ui/window.py:715 msgid "The wireless link between this device and its receiver is encrypted." msgstr "Бездротове з'єднання між цим пристроєм та його приймачем зашифроване." #: lib/solaar/ui/window.py:717 msgid "not encrypted" msgstr "не зашифровано" #: lib/solaar/ui/window.py:721 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:737 #, python-format msgid "%(light_level)d lux" msgstr "%(light_level)d люкс" ================================================ FILE: po/zh_CN.po ================================================ # 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 "未知的" ================================================ FILE: po/zh_TW.po ================================================ # 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: 2025-12-01 21:43+0300\n" "PO-Revision-Date: 2025-12-07 07:17+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:52 msgid "Bolt Receiver" msgstr "Bolt 接收器" #: lib/logitech_receiver/base_usb.py:64 msgid "Unifying Receiver" msgstr "Unifying 接收器" #: lib/logitech_receiver/base_usb.py:75 lib/logitech_receiver/base_usb.py:87 #: lib/logitech_receiver/base_usb.py:100 lib/logitech_receiver/base_usb.py:113 msgid "Nano Receiver" msgstr "Nano 接收器" #: lib/logitech_receiver/base_usb.py:125 msgid "Lightspeed Receiver" msgstr "Lightspeed 接收器" #: lib/logitech_receiver/base_usb.py:135 msgid "EX100 Receiver 27 Mhz" msgstr "EX100 27 Mhz 接收器" #: lib/logitech_receiver/common.py:649 #, python-format msgid "Battery: %(level)s (%(status)s)" msgstr "電量:%(level)s (%(status)s)" #: lib/logitech_receiver/common.py:652 #, python-format msgid "Battery: %(percent)d%% (%(status)s)" msgstr "電量:%(percent)d%% (%(status)s)" #: lib/logitech_receiver/hidpp20.py:1048 #: lib/logitech_receiver/settings_templates.py:299 msgid "Disabled" msgstr "已停用" #: lib/logitech_receiver/hidpp20.py:1049 msgid "Static" msgstr "靜態" #: lib/logitech_receiver/hidpp20.py:1050 msgid "Pulse" msgstr "脈衝" #: lib/logitech_receiver/hidpp20.py:1051 msgid "Cycle" msgstr "循環" #: lib/logitech_receiver/hidpp20.py:1052 msgid "Boot" msgstr "開機" #: lib/logitech_receiver/hidpp20.py:1053 msgid "Demo" msgstr "示範" #: lib/logitech_receiver/hidpp20.py:1055 msgid "Breathe" msgstr "呼吸" #: lib/logitech_receiver/hidpp20.py:1058 msgid "Ripple" msgstr "漣漪" #: lib/logitech_receiver/hidpp20.py:1059 msgid "Decomposition" msgstr "分解" #: lib/logitech_receiver/hidpp20.py:1060 msgid "Signature1" msgstr "簽名樣式 1" #: lib/logitech_receiver/hidpp20.py:1061 msgid "Signature2" msgstr "簽名樣式 2" #: lib/logitech_receiver/hidpp20.py:1062 msgid "CycleS" msgstr "循環式" #: lib/logitech_receiver/hidpp20.py:1126 msgid "Unknown Location" msgstr "未知位置" #: lib/logitech_receiver/hidpp20.py:1127 msgid "Primary" msgstr "主要" #: lib/logitech_receiver/hidpp20.py:1128 msgid "Logo" msgstr "Logo" #: lib/logitech_receiver/hidpp20.py:1129 msgid "Left Side" msgstr "左側" #: lib/logitech_receiver/hidpp20.py:1130 msgid "Right Side" msgstr "右側" #: lib/logitech_receiver/hidpp20.py:1131 msgid "Combined" msgstr "結合" #: lib/logitech_receiver/hidpp20.py:1132 msgid "Primary 1" msgstr "主要 1" #: lib/logitech_receiver/hidpp20.py:1133 msgid "Primary 2" msgstr "主要 2" #: lib/logitech_receiver/hidpp20.py:1134 msgid "Primary 3" msgstr "主要 3" #: lib/logitech_receiver/hidpp20.py:1135 msgid "Primary 4" msgstr "主要 4" #: lib/logitech_receiver/hidpp20.py:1136 msgid "Primary 5" msgstr "主要 5" #: lib/logitech_receiver/hidpp20.py:1137 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 lib/solaar/ui/window.py:700 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 lib/solaar/ui/window.py:555 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:156 lib/solaar/listener.py:243 msgid "connected" msgstr "已連線" #: lib/logitech_receiver/notifications.py:156 lib/solaar/listener.py:243 msgid "disconnected" msgstr "已斷線" #: lib/logitech_receiver/notifications.py:182 msgid "unpaired" msgstr "未配對" #: lib/logitech_receiver/notifications.py:231 msgid "powered on" msgstr "已啟動" #: lib/logitech_receiver/notifications.py:283 msgid "ADC measurement notification" msgstr "ADC 量測通知" #: lib/logitech_receiver/notifications.py:428 #: lib/logitech_receiver/notifications.py:483 msgid "pairing lock is closed" msgstr "配對鎖已關閉" #: lib/logitech_receiver/notifications.py:428 #: lib/logitech_receiver/notifications.py:483 msgid "pairing lock is open" msgstr "配對鎖已開啟" #: lib/logitech_receiver/notifications.py:446 msgid "discovery lock is closed" msgstr "探索鎖已關閉" #: lib/logitech_receiver/notifications.py:446 msgid "discovery lock is open" msgstr "探索鎖已開啟" #: lib/logitech_receiver/receiver.py:437 msgid "No paired devices." msgstr "沒有已配對的裝置。" #: lib/logitech_receiver/receiver.py:439 lib/solaar/ui/window.py:606 #, python-format msgid "%(count)s paired device." msgid_plural "%(count)s paired devices." msgstr[0] "%(count)s 個已配對裝置。" #: lib/logitech_receiver/settings.py:602 msgid "register" msgstr "暫存器" #: lib/logitech_receiver/settings.py:616 lib/logitech_receiver/settings.py:654 msgid "feature" msgstr "功能" #: lib/logitech_receiver/settings_templates.py:138 msgid "Swap Fx function" msgstr "互換 Fx 鍵功能" #: lib/logitech_receiver/settings_templates.py:141 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:146 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:154 msgid "Hand Detection" msgstr "手掌偵測" #: lib/logitech_receiver/settings_templates.py:155 msgid "Turn on illumination when the hands hover over the keyboard." msgstr "當手掌置於在鍵盤上方時開啟照明。" #: lib/logitech_receiver/settings_templates.py:162 msgid "Scroll Wheel Smooth Scrolling" msgstr "滾輪平滑捲動" #: lib/logitech_receiver/settings_templates.py:163 #: lib/logitech_receiver/settings_templates.py:410 #: lib/logitech_receiver/settings_templates.py:439 msgid "High-sensitivity mode for vertical scroll with the wheel." msgstr "滾輪垂直捲動的高靈敏度模式。" #: lib/logitech_receiver/settings_templates.py:170 msgid "Side Scrolling" msgstr "側邊捲動" #: lib/logitech_receiver/settings_templates.py:172 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:182 msgid "Sensitivity (DPI - older mice)" msgstr "靈敏度(DPI - 較舊的滑鼠)" #: lib/logitech_receiver/settings_templates.py:183 #: lib/logitech_receiver/settings_templates.py:1019 #: lib/logitech_receiver/settings_templates.py:1047 msgid "Mouse movement sensitivity" msgstr "滑鼠移動靈敏度" #: lib/logitech_receiver/settings_templates.py:256 msgid "Backlight Timed" msgstr "背光計時" #: lib/logitech_receiver/settings_templates.py:257 #: lib/logitech_receiver/settings_templates.py:397 msgid "Set illumination time for keyboard." msgstr "設定鍵盤的照明時間。" #: lib/logitech_receiver/settings_templates.py:268 msgid "Backlight" msgstr "背光" #: lib/logitech_receiver/settings_templates.py:269 msgid "" "Illumination level on keyboard. Changes made are only applied in Manual " "mode." msgstr "鍵盤上的亮度等級。僅在手動模式下適用。" #: lib/logitech_receiver/settings_templates.py:301 msgid "Automatic" msgstr "自動" #: lib/logitech_receiver/settings_templates.py:303 msgid "Manual" msgstr "手動" #: lib/logitech_receiver/settings_templates.py:305 msgid "Enabled" msgstr "已啟用" #: lib/logitech_receiver/settings_templates.py:311 msgid "Backlight Level" msgstr "背光等級" #: lib/logitech_receiver/settings_templates.py:312 msgid "Illumination level on keyboard when in Manual mode." msgstr "手動模式下的鍵盤照明等級。" #: lib/logitech_receiver/settings_templates.py:369 msgid "Backlight Delay Hands Out" msgstr "手離開後背光延遲" #: lib/logitech_receiver/settings_templates.py:370 msgid "" "Delay in seconds until backlight fades out with hands away from keyboard." msgstr "手離開鍵盤後,背光淡出的延遲秒數。" #: lib/logitech_receiver/settings_templates.py:378 msgid "Backlight Delay Hands In" msgstr "手靠近後背光延遲" #: lib/logitech_receiver/settings_templates.py:379 msgid "Delay in seconds until backlight fades out with hands near keyboard." msgstr "手靠近鍵盤後,背光淡出的延遲秒數。" #: lib/logitech_receiver/settings_templates.py:387 msgid "Backlight Delay Powered" msgstr "供電後背光延遲" #: lib/logitech_receiver/settings_templates.py:388 msgid "Delay in seconds until backlight fades out with external power." msgstr "外部供電時,背光淡出的延遲秒數。" #: lib/logitech_receiver/settings_templates.py:396 msgid "Backlight (Seconds)" msgstr "背光(秒)" #: lib/logitech_receiver/settings_templates.py:408 msgid "Scroll Wheel High Resolution" msgstr "滾輪高解析度" #: lib/logitech_receiver/settings_templates.py:412 #: lib/logitech_receiver/settings_templates.py:441 msgid "Set to ignore if scrolling is abnormally fast or slow" msgstr "若捲動速度異常快或慢,設定為忽略" #: lib/logitech_receiver/settings_templates.py:419 #: lib/logitech_receiver/settings_templates.py:450 msgid "Scroll Wheel Diversion" msgstr "滾輪轉向" #: lib/logitech_receiver/settings_templates.py:421 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:428 msgid "Scroll Wheel Direction" msgstr "滾輪方向" #: lib/logitech_receiver/settings_templates.py:429 msgid "Invert direction for vertical scroll with wheel." msgstr "反轉滾輪的垂直捲動方向。" #: lib/logitech_receiver/settings_templates.py:437 msgid "Scroll Wheel Resolution" msgstr "滾輪解析度" #: lib/logitech_receiver/settings_templates.py:452 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:461 msgid "Sensitivity (Pointer Speed)" msgstr "靈敏度(指標速度)" #: lib/logitech_receiver/settings_templates.py:462 msgid "Speed multiplier for mouse (256 is normal multiplier)." msgstr "滑鼠速度倍數(256 為正常倍數)。" #: lib/logitech_receiver/settings_templates.py:472 msgid "Thumb Wheel Diversion" msgstr "拇指滾輪轉向" #: lib/logitech_receiver/settings_templates.py:474 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:483 msgid "Thumb Wheel Direction" msgstr "拇指滾輪方向" #: lib/logitech_receiver/settings_templates.py:484 msgid "Invert thumb wheel scroll direction." msgstr "反轉拇指滾輪捲動方向。" #: lib/logitech_receiver/settings_templates.py:504 msgid "Onboard Profiles" msgstr "內建設定檔" #: lib/logitech_receiver/settings_templates.py:505 msgid "" "Enable an onboard profile, which controls report rate, sensitivity, and " "button actions" msgstr "啟用內建設定檔,可控制回報速率、靈敏度和按鈕動作" #: lib/logitech_receiver/settings_templates.py:549 #: lib/logitech_receiver/settings_templates.py:582 msgid "Report Rate" msgstr "回報速率" #: lib/logitech_receiver/settings_templates.py:551 #: lib/logitech_receiver/settings_templates.py:584 msgid "Frequency of device movement reports" msgstr "裝置移動回報的頻率" #: lib/logitech_receiver/settings_templates.py:551 #: lib/logitech_receiver/settings_templates.py:584 #: lib/logitech_receiver/settings_templates.py:1019 #: lib/logitech_receiver/settings_templates.py:1047 #: lib/logitech_receiver/settings_templates.py:1421 #: lib/logitech_receiver/settings_templates.py:1452 msgid "May need Onboard Profiles set to Disable to be effective." msgstr "可能需要將內建設定檔設為停用才能生效。" #: lib/logitech_receiver/settings_templates.py:612 msgid "Divert crown events" msgstr "轉向多功能旋鈕事件" #: lib/logitech_receiver/settings_templates.py:613 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:621 msgid "Crown smooth scroll" msgstr "多功能旋鈕平滑捲動" #: lib/logitech_receiver/settings_templates.py:622 msgid "Set crown smooth scroll" msgstr "設定多功能旋鈕平滑捲動" #: lib/logitech_receiver/settings_templates.py:630 msgid "Divert G and M Keys" msgstr "轉向 G 鍵和 M 鍵" #: lib/logitech_receiver/settings_templates.py:631 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:645 msgid "Scroll Wheel Ratcheted" msgstr "滾輪棘輪" #: lib/logitech_receiver/settings_templates.py:646 msgid "" "Switch the mouse wheel between speed-controlled ratcheting and always " "freespin." msgstr "在速度控制的棘輪模式和永遠自由捲動之間切換滑鼠滾輪。" #: lib/logitech_receiver/settings_templates.py:648 msgid "Freespinning" msgstr "自由捲動" #: lib/logitech_receiver/settings_templates.py:648 msgid "Ratcheted" msgstr "棘輪式" #: lib/logitech_receiver/settings_templates.py:655 msgid "Scroll Wheel Ratchet Speed" msgstr "滾輪棘輪速度" #: lib/logitech_receiver/settings_templates.py:657 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:707 msgid "Scroll Wheel Ratchet Torque" msgstr "滾輪棘輪扭矩" #: lib/logitech_receiver/settings_templates.py:708 msgid "Change the torque needed to overcome the ratchet." msgstr "變更克服棘輪所需的扭矩。" #: lib/logitech_receiver/settings_templates.py:743 msgid "Key/Button Actions" msgstr "鍵/按鈕動作" #: lib/logitech_receiver/settings_templates.py:745 msgid "Change the action for the key or button." msgstr "變更按鍵或按鈕的動作。" #: lib/logitech_receiver/settings_templates.py:747 msgid "Overridden by diversion." msgstr "已被轉向覆寫。" #: lib/logitech_receiver/settings_templates.py:749 msgid "" "Changing important actions (such as for the left mouse button) can result in " "an unusable system." msgstr "變更重要動作(例如滑鼠左鍵)可能會導致系統無法使用。" #: lib/logitech_receiver/settings_templates.py:924 msgid "Key/Button Diversion" msgstr "鍵/按鈕轉換" #: lib/logitech_receiver/settings_templates.py:925 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:928 #: lib/logitech_receiver/settings_templates.py:929 #: lib/logitech_receiver/settings_templates.py:930 msgid "Diverted" msgstr "已轉向" #: lib/logitech_receiver/settings_templates.py:928 #: lib/logitech_receiver/settings_templates.py:929 msgid "Mouse Gestures" msgstr "滑鼠手勢" #: lib/logitech_receiver/settings_templates.py:928 #: lib/logitech_receiver/settings_templates.py:929 #: lib/logitech_receiver/settings_templates.py:930 msgid "Regular" msgstr "一般" #: lib/logitech_receiver/settings_templates.py:928 msgid "Sliding DPI" msgstr "滑動 DPI" #: lib/logitech_receiver/settings_templates.py:1018 #: lib/logitech_receiver/settings_templates.py:1046 msgid "Sensitivity (DPI)" msgstr "靈敏度 (DPI)" #: lib/logitech_receiver/settings_templates.py:1123 msgid "Sensitivity Switching" msgstr "靈敏度切換" #: lib/logitech_receiver/settings_templates.py:1125 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:1129 msgid "Off" msgstr "關閉" #: lib/logitech_receiver/settings_templates.py:1160 msgid "Disable keys" msgstr "停用按鍵" #: lib/logitech_receiver/settings_templates.py:1161 msgid "Disable specific keyboard keys." msgstr "停用特定鍵盤按鍵。" #: lib/logitech_receiver/settings_templates.py:1164 #, python-format msgid "Disables the %s key." msgstr "停用 %s 鍵。" #: lib/logitech_receiver/settings_templates.py:1177 #: lib/logitech_receiver/settings_templates.py:1234 msgid "Set OS" msgstr "設定作業系統" #: lib/logitech_receiver/settings_templates.py:1178 #: lib/logitech_receiver/settings_templates.py:1235 msgid "Change keys to match OS." msgstr "變更按鍵以符合作業系統。" #: lib/logitech_receiver/settings_templates.py:1247 msgid "Change Host" msgstr "變更主機" #: lib/logitech_receiver/settings_templates.py:1248 msgid "Switch connection to a different host" msgstr "切換連線到不同的主機" #: lib/logitech_receiver/settings_templates.py:1272 msgid "Performs a left click." msgstr "執行左鍵點選。" #: lib/logitech_receiver/settings_templates.py:1272 msgid "Single tap" msgstr "單次點選" #: lib/logitech_receiver/settings_templates.py:1273 msgid "Performs a right click." msgstr "執行右鍵點選。" #: lib/logitech_receiver/settings_templates.py:1273 msgid "Single tap with two fingers" msgstr "用兩根手指單次點選" #: lib/logitech_receiver/settings_templates.py:1274 msgid "Single tap with three fingers" msgstr "用三根手指單次點選" #: lib/logitech_receiver/settings_templates.py:1278 msgid "Double tap" msgstr "點兩下" #: lib/logitech_receiver/settings_templates.py:1278 msgid "Performs a double click." msgstr "執行點兩下。" #: lib/logitech_receiver/settings_templates.py:1279 msgid "Double tap with two fingers" msgstr "用兩根手指點兩下" #: lib/logitech_receiver/settings_templates.py:1280 msgid "Double tap with three fingers" msgstr "用三根手指點兩下" #: lib/logitech_receiver/settings_templates.py:1283 msgid "Drags items by dragging the finger after double tapping." msgstr "點兩下後拖曳手指來拖曳項目。" #: lib/logitech_receiver/settings_templates.py:1283 msgid "Tap and drag" msgstr "點選並拖曳" #: lib/logitech_receiver/settings_templates.py:1285 msgid "Tap and drag with two fingers" msgstr "用兩根手指點兩下並拖曳" #: lib/logitech_receiver/settings_templates.py:1286 msgid "Drags items by dragging the fingers after double tapping." msgstr "點兩下後拖曳手指來拖曳項目。" #: lib/logitech_receiver/settings_templates.py:1288 msgid "Tap and drag with three fingers" msgstr "用三根手指點選並拖曳" #: lib/logitech_receiver/settings_templates.py:1291 msgid "Suppress tap and edge gestures" msgstr "停用輕觸和邊緣手勢" #: lib/logitech_receiver/settings_templates.py:1292 msgid "Disables tap and edge gestures (equivalent to pressing Fn+LeftClick)." msgstr "停用輕觸和邊緣手勢(相當於按下 Fn+左鍵點選)。" #: lib/logitech_receiver/settings_templates.py:1294 msgid "Scroll with one finger" msgstr "用一根手指捲動" #: lib/logitech_receiver/settings_templates.py:1294 #: lib/logitech_receiver/settings_templates.py:1295 #: lib/logitech_receiver/settings_templates.py:1298 msgid "Scrolls." msgstr "捲動。" #: lib/logitech_receiver/settings_templates.py:1295 #: lib/logitech_receiver/settings_templates.py:1298 msgid "Scroll with two fingers" msgstr "用兩根手指捲動" #: lib/logitech_receiver/settings_templates.py:1296 msgid "Scroll horizontally with two fingers" msgstr "用兩根手指水平捲動" #: lib/logitech_receiver/settings_templates.py:1296 msgid "Scrolls horizontally." msgstr "水平捲動。" #: lib/logitech_receiver/settings_templates.py:1297 msgid "Scroll vertically with two fingers" msgstr "用兩根手指垂直捲動" #: lib/logitech_receiver/settings_templates.py:1297 msgid "Scrolls vertically." msgstr "垂直捲動。" #: lib/logitech_receiver/settings_templates.py:1299 msgid "Inverts the scrolling direction." msgstr "反轉捲動方向。" #: lib/logitech_receiver/settings_templates.py:1299 msgid "Natural scrolling" msgstr "自然捲動" #: lib/logitech_receiver/settings_templates.py:1300 msgid "Enables the thumbwheel." msgstr "啟用拇指滾輪。" #: lib/logitech_receiver/settings_templates.py:1300 msgid "Thumbwheel" msgstr "拇指滾輪" #: lib/logitech_receiver/settings_templates.py:1311 #: lib/logitech_receiver/settings_templates.py:1315 msgid "Swipe from the top edge" msgstr "從上緣滑動" #: lib/logitech_receiver/settings_templates.py:1312 msgid "Swipe from the left edge" msgstr "從左緣滑動" #: lib/logitech_receiver/settings_templates.py:1313 msgid "Swipe from the right edge" msgstr "從右緣滑動" #: lib/logitech_receiver/settings_templates.py:1314 msgid "Swipe from the bottom edge" msgstr "從下緣滑動" #: lib/logitech_receiver/settings_templates.py:1316 msgid "Swipe two fingers from the left edge" msgstr "從左緣滑動兩根手指" #: lib/logitech_receiver/settings_templates.py:1317 msgid "Swipe two fingers from the right edge" msgstr "從右緣滑動兩根手指" #: lib/logitech_receiver/settings_templates.py:1318 msgid "Swipe two fingers from the bottom edge" msgstr "從下緣滑動兩根手指" #: lib/logitech_receiver/settings_templates.py:1319 msgid "Swipe two fingers from the top edge" msgstr "從上緣滑動兩根手指" #: lib/logitech_receiver/settings_templates.py:1320 #: lib/logitech_receiver/settings_templates.py:1324 msgid "Pinch to zoom out; spread to zoom in." msgstr "捏合以縮小;張開以放大。" #: lib/logitech_receiver/settings_templates.py:1320 msgid "Zoom with two fingers." msgstr "用兩根手指縮放。" #: lib/logitech_receiver/settings_templates.py:1321 msgid "Pinch to zoom out." msgstr "捏合以縮小。" #: lib/logitech_receiver/settings_templates.py:1322 msgid "Spread to zoom in." msgstr "張開以放大。" #: lib/logitech_receiver/settings_templates.py:1323 msgid "Zoom with three fingers." msgstr "用三根手指縮放。" #: lib/logitech_receiver/settings_templates.py:1324 msgid "Zoom with two fingers" msgstr "用兩根手指縮放" #: lib/logitech_receiver/settings_templates.py:1342 msgid "Pixel zone" msgstr "像素區域" #: lib/logitech_receiver/settings_templates.py:1343 msgid "Ratio zone" msgstr "比例區域" #: lib/logitech_receiver/settings_templates.py:1344 msgid "Scale factor" msgstr "縮放係數" #: lib/logitech_receiver/settings_templates.py:1344 msgid "Sets the cursor speed." msgstr "設定游標速度。" #: lib/logitech_receiver/settings_templates.py:1348 msgid "Left" msgstr "左" #: lib/logitech_receiver/settings_templates.py:1348 msgid "Left-most coordinate." msgstr "最左邊的座標。" #: lib/logitech_receiver/settings_templates.py:1349 msgid "Bottom" msgstr "底部" #: lib/logitech_receiver/settings_templates.py:1349 msgid "Bottom coordinate." msgstr "底部座標。" #: lib/logitech_receiver/settings_templates.py:1350 msgid "Width" msgstr "寬度" #: lib/logitech_receiver/settings_templates.py:1350 msgid "Width." msgstr "寬度。" #: lib/logitech_receiver/settings_templates.py:1351 msgid "Height" msgstr "高度" #: lib/logitech_receiver/settings_templates.py:1351 msgid "Height." msgstr "高度。" #: lib/logitech_receiver/settings_templates.py:1352 msgid "Cursor speed." msgstr "游標速度。" #: lib/logitech_receiver/settings_templates.py:1352 msgid "Scale" msgstr "縮放" #: lib/logitech_receiver/settings_templates.py:1358 msgid "Gestures" msgstr "手勢" #: lib/logitech_receiver/settings_templates.py:1359 msgid "Tweak the mouse/touchpad behaviour." msgstr "調整滑鼠/觸控板行為。" #: lib/logitech_receiver/settings_templates.py:1375 msgid "Gestures Diversion" msgstr "手勢轉換" #: lib/logitech_receiver/settings_templates.py:1376 msgid "Divert mouse/touchpad gestures." msgstr "轉換滑鼠/觸控板手勢。" #: lib/logitech_receiver/settings_templates.py:1392 msgid "Gesture params" msgstr "手勢參數" #: lib/logitech_receiver/settings_templates.py:1393 msgid "Change numerical parameters of a mouse/touchpad." msgstr "變更滑鼠/觸控板的數值參數。" #: lib/logitech_receiver/settings_templates.py:1417 msgid "M-Key LEDs" msgstr "M 鍵 LED" #: lib/logitech_receiver/settings_templates.py:1419 msgid "Control the M-Key LEDs." msgstr "控制 M 鍵 LED。" #: lib/logitech_receiver/settings_templates.py:1423 #: lib/logitech_receiver/settings_templates.py:1454 msgid "May need G Keys diverted to be effective." msgstr "可能需要轉向 G 鍵才能生效。" #: lib/logitech_receiver/settings_templates.py:1429 #, python-format msgid "Lights up the %s key." msgstr "點亮 %s 鍵。" #: lib/logitech_receiver/settings_templates.py:1448 msgid "MR-Key LED" msgstr "MR 鍵 LED" #: lib/logitech_receiver/settings_templates.py:1450 msgid "Control the MR-Key LED." msgstr "控制 MR 鍵 LED。" #: lib/logitech_receiver/settings_templates.py:1471 msgid "Persistent Key/Button Mapping" msgstr "持久按鍵/按鈕對應" #: lib/logitech_receiver/settings_templates.py:1473 msgid "Permanently change the mapping for the key or button." msgstr "永久變更按鍵或按鈕的對應。" #: lib/logitech_receiver/settings_templates.py:1475 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:1532 msgid "Sidetone" msgstr "側音" #: lib/logitech_receiver/settings_templates.py:1533 msgid "Set sidetone level." msgstr "設定側音等級。" #: lib/logitech_receiver/settings_templates.py:1542 msgid "Equalizer" msgstr "均衡器" #: lib/logitech_receiver/settings_templates.py:1543 msgid "Set equalizer levels." msgstr "設定等化器等級。" #: lib/logitech_receiver/settings_templates.py:1565 msgid "Hz" msgstr "Hz" #: lib/logitech_receiver/settings_templates.py:1571 msgid "Power Management" msgstr "電源管理" #: lib/logitech_receiver/settings_templates.py:1572 msgid "Power off in minutes (0 for never)." msgstr "幾分鐘後關機(0 表示永不關機)。" #: lib/logitech_receiver/settings_templates.py:1584 msgid "Brightness Control" msgstr "亮度控制" #: lib/logitech_receiver/settings_templates.py:1585 msgid "Control overall brightness" msgstr "控制整體亮度" #: lib/logitech_receiver/settings_templates.py:1628 #: lib/logitech_receiver/settings_templates.py:1681 msgid "LED Control" msgstr "LED 控制" #: lib/logitech_receiver/settings_templates.py:1629 #: lib/logitech_receiver/settings_templates.py:1682 msgid "Switch control of LED zones between device and Solaar" msgstr "在裝置與 Solaar 之間切換 LED 區域控制" #: lib/logitech_receiver/settings_templates.py:1644 #: lib/logitech_receiver/settings_templates.py:1692 msgid "LED Zone Effects" msgstr "LED 區域效果" #: lib/logitech_receiver/settings_templates.py:1645 #: lib/logitech_receiver/settings_templates.py:1693 msgid "LED Control needs to be set to Solaar to be effective." msgstr "需要將 LED 控制設為 Solaar 才能生效。" #: lib/logitech_receiver/settings_templates.py:1645 #: lib/logitech_receiver/settings_templates.py:1693 msgid "Set effect for LED Zone" msgstr "設定 LED 區域的效果" #: lib/logitech_receiver/settings_templates.py:1647 msgid "Color" msgstr "顏色" #: lib/logitech_receiver/settings_templates.py:1648 msgid "Speed" msgstr "速度" #: lib/logitech_receiver/settings_templates.py:1649 msgid "Period" msgstr "週期" #: lib/logitech_receiver/settings_templates.py:1650 msgid "Intensity" msgstr "強度" #: lib/logitech_receiver/settings_templates.py:1651 msgid "Ramp" msgstr "漸變" #: lib/logitech_receiver/settings_templates.py:1666 msgid "LEDs" msgstr "LED 燈" #: lib/logitech_receiver/settings_templates.py:1703 msgid "Per-key Lighting" msgstr "單鍵照明" #: lib/logitech_receiver/settings_templates.py:1704 msgid "Control per-key lighting." msgstr "控制單鍵照明。" #: lib/logitech_receiver/settings_templates.py:1786 msgid "Force Sensing Buttons" msgstr "力道感測按鈕" #: lib/logitech_receiver/settings_templates.py:1787 msgid "Change the force required to activate button." msgstr "調整觸發按鈕所需的力道。" #: lib/logitech_receiver/settings_templates.py:1804 msgid "Force Sensing Button" msgstr "力道感測按鈕" #: lib/logitech_receiver/settings_templates.py:1814 msgid "Haptic Feeback Level" msgstr "觸覺回饋等級" #: lib/logitech_receiver/settings_templates.py:1815 msgid "Change power of haptic feedback. (Zero to turn off.)" msgstr "變更觸覺回饋的力度,設為 0 可關閉。" #: lib/logitech_receiver/settings_templates.py:1857 msgid "Play Haptic Waveform" msgstr "播放觸覺波形" #: lib/logitech_receiver/settings_templates.py:1858 msgid "Tell device to play a haptic waveform." msgstr "指示裝置播放觸覺波形。" #: lib/solaar/ui/__init__.py:120 msgid "Another Solaar process is already running so just expose its window" msgstr "已有另一個 Solaar 處理程序正在執行,因此將會顯示其視窗" #: lib/solaar/ui/about/model.py:36 msgid "" "Manages Logitech receivers,\n" "keyboards, mice, and tablets." msgstr "" "管理羅技接收器、\n" "鍵盤、滑鼠和平板電腦。" #: lib/solaar/ui/about/model.py:63 msgid "Additional Programming" msgstr "額外程式設計" #: lib/solaar/ui/about/model.py:64 msgid "GUI design" msgstr "圖形介面設計" #: lib/solaar/ui/about/model.py:66 msgid "Testing" msgstr "測試" #: lib/solaar/ui/about/model.py:74 msgid "Logitech documentation" msgstr "羅技說明文件" #: lib/solaar/ui/action.py:88 lib/solaar/ui/action.py:92 #: lib/solaar/ui/window.py:208 msgid "Unpair" msgstr "取消配對" #: lib/solaar/ui/action.py:91 lib/solaar/ui/diversion_rules.py:141 msgid "Cancel" msgstr "取消" #: lib/solaar/ui/common.py:42 msgid "Permissions error" msgstr "權限錯誤" #: lib/solaar/ui/common.py:44 #, 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:46 msgid "" "If you've just installed Solaar, try disconnecting the receiver or device " "and then reconnecting it." msgstr "若剛安裝 Solaar,請嘗試中斷接收器或裝置的連線,然後重新連接。" #: lib/solaar/ui/common.py:49 msgid "Cannot connect to device error" msgstr "無法連接到裝置錯誤" #: lib/solaar/ui/common.py:51 #, 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:53 msgid "" "Try disconnecting the device and then reconnecting it or turning it off and " "then on." msgstr "請嘗試中斷裝置連線然後重新連接,或將其關閉後再開啟。" #: lib/solaar/ui/common.py:56 msgid "Unpairing failed" msgstr "取消配對失敗" #: lib/solaar/ui/common.py:58 #, python-brace-format msgid "Failed to unpair %{device} from %{receiver}." msgstr "無法取消 %{device} 與 %{receiver} 的配對。" #: lib/solaar/ui/common.py:63 msgid "The receiver returned an error, with no further details." msgstr "接收器傳回錯誤,未提供進一步詳細資訊。" #: lib/solaar/ui/config_panel.py:245 msgid "Complete - ENTER to change" msgstr "完成 - 按 ENTER 鍵變更" #: lib/solaar/ui/config_panel.py:245 msgid "Incomplete" msgstr "未完成" #: lib/solaar/ui/config_panel.py:491 lib/solaar/ui/config_panel.py:543 #, python-format msgid "%d value" msgid_plural "%d values" msgstr[0] "%d 個值" #: lib/solaar/ui/config_panel.py:642 msgid "Changes allowed" msgstr "允許變更" #: lib/solaar/ui/config_panel.py:643 msgid "No changes allowed" msgstr "不允許變更" #: lib/solaar/ui/config_panel.py:644 msgid "Ignore this setting" msgstr "忽略此設定" #: lib/solaar/ui/config_panel.py:690 msgid "Working" msgstr "工作中" #: lib/solaar/ui/config_panel.py:693 msgid "Read/write operation failed." msgstr "讀取/寫入操作失敗。" #: lib/solaar/ui/desktop_notifications.py:112 msgid "unspecified reason" msgstr "未指定原因" #: lib/solaar/ui/diversion_rules.py:103 msgid "Built-in rules" msgstr "內建規則" #: lib/solaar/ui/diversion_rules.py:103 msgid "User-defined rules" msgstr "使用者定義規則" #: lib/solaar/ui/diversion_rules.py:105 lib/solaar/ui/diversion_rules.py:1126 msgid "Rule" msgstr "規則" #: lib/solaar/ui/diversion_rules.py:106 lib/solaar/ui/diversion_rules.py:382 #: lib/solaar/ui/diversion_rules.py:509 msgid "Sub-rule" msgstr "子規則" #: lib/solaar/ui/diversion_rules.py:108 msgid "[empty]" msgstr "[空白]" #: lib/solaar/ui/diversion_rules.py:132 msgid "Make changes permanent?" msgstr "要永久套用變更嗎?" #: lib/solaar/ui/diversion_rules.py:137 msgid "Yes" msgstr "是" #: lib/solaar/ui/diversion_rules.py:139 msgid "No" msgstr "否" #: lib/solaar/ui/diversion_rules.py:144 msgid "If you choose No, changes will be lost when Solaar is closed." msgstr "若選擇否,關閉 Solaar 時將會遺失變更。" #: lib/solaar/ui/diversion_rules.py:273 msgid "Paste here" msgstr "在此貼上" #: lib/solaar/ui/diversion_rules.py:275 msgid "Paste above" msgstr "在上方貼上" #: lib/solaar/ui/diversion_rules.py:277 msgid "Paste below" msgstr "在下方貼上" #: lib/solaar/ui/diversion_rules.py:283 msgid "Paste rule here" msgstr "在此貼上規則" #: lib/solaar/ui/diversion_rules.py:285 msgid "Paste rule above" msgstr "在上方貼上規則" #: lib/solaar/ui/diversion_rules.py:287 msgid "Paste rule below" msgstr "在下方貼上規則" #: lib/solaar/ui/diversion_rules.py:291 msgid "Paste rule" msgstr "貼上規則" #: lib/solaar/ui/diversion_rules.py:306 msgid "Insert here" msgstr "在此插入" #: lib/solaar/ui/diversion_rules.py:308 msgid "Insert above" msgstr "在上方插入" #: lib/solaar/ui/diversion_rules.py:310 msgid "Insert below" msgstr "在下方插入" #: lib/solaar/ui/diversion_rules.py:316 msgid "Insert new rule here" msgstr "在此插入新規則" #: lib/solaar/ui/diversion_rules.py:318 msgid "Insert new rule above" msgstr "在上方插入新規則" #: lib/solaar/ui/diversion_rules.py:320 msgid "Insert new rule below" msgstr "在下方插入新規則" #: lib/solaar/ui/diversion_rules.py:347 msgid "Flatten" msgstr "平面化" #: lib/solaar/ui/diversion_rules.py:380 msgid "Insert" msgstr "插入" #: lib/solaar/ui/diversion_rules.py:383 lib/solaar/ui/diversion_rules.py:511 #: lib/solaar/ui/diversion_rules.py:1158 msgid "Or" msgstr "或" #: lib/solaar/ui/diversion_rules.py:384 lib/solaar/ui/diversion_rules.py:510 #: lib/solaar/ui/diversion_rules.py:1144 msgid "And" msgstr "且" #: lib/solaar/ui/diversion_rules.py:386 msgid "Condition" msgstr "條件" #: lib/solaar/ui/diversion_rules.py:388 lib/solaar/ui/rule_conditions.py:154 msgid "Feature" msgstr "功能" #: lib/solaar/ui/diversion_rules.py:389 lib/solaar/ui/rule_conditions.py:188 msgid "Report" msgstr "回報" #: lib/solaar/ui/diversion_rules.py:390 lib/solaar/ui/rule_conditions.py:69 msgid "Process" msgstr "處理程序" #: lib/solaar/ui/diversion_rules.py:391 msgid "Mouse process" msgstr "滑鼠處理程序" #: lib/solaar/ui/diversion_rules.py:392 lib/solaar/ui/rule_conditions.py:225 msgid "Modifiers" msgstr "修飾鍵" #: lib/solaar/ui/diversion_rules.py:393 lib/solaar/ui/rule_conditions.py:277 msgid "Key" msgstr "按鍵" #: lib/solaar/ui/diversion_rules.py:394 lib/solaar/ui/rule_conditions.py:318 msgid "KeyIsDown" msgstr "按鍵處於按下狀態" #: lib/solaar/ui/diversion_rules.py:395 lib/solaar/ui/diversion_rules.py:1432 msgid "Active" msgstr "使用中" #: lib/solaar/ui/diversion_rules.py:396 lib/solaar/ui/diversion_rules.py:1390 #: lib/solaar/ui/diversion_rules.py:1441 lib/solaar/ui/diversion_rules.py:1487 msgid "Device" msgstr "裝置" #: lib/solaar/ui/diversion_rules.py:397 lib/solaar/ui/diversion_rules.py:1467 msgid "Host" msgstr "主機" #: lib/solaar/ui/diversion_rules.py:398 lib/solaar/ui/diversion_rules.py:1506 msgid "Setting" msgstr "設定" #: lib/solaar/ui/diversion_rules.py:399 lib/solaar/ui/rule_conditions.py:333 #: lib/solaar/ui/rule_conditions.py:382 msgid "Test" msgstr "測試" #: lib/solaar/ui/diversion_rules.py:400 lib/solaar/ui/rule_conditions.py:507 msgid "Test bytes" msgstr "測試位元組" #: lib/solaar/ui/diversion_rules.py:401 lib/solaar/ui/rule_conditions.py:608 msgid "Mouse Gesture" msgstr "滑鼠手勢" #: lib/solaar/ui/diversion_rules.py:405 msgid "Action" msgstr "動作" #: lib/solaar/ui/diversion_rules.py:407 lib/solaar/ui/rule_actions.py:138 msgid "Key press" msgstr "按鍵按下" #: lib/solaar/ui/diversion_rules.py:408 lib/solaar/ui/rule_actions.py:191 msgid "Mouse scroll" msgstr "滑鼠捲動" #: lib/solaar/ui/diversion_rules.py:409 lib/solaar/ui/rule_actions.py:256 msgid "Mouse click" msgstr "滑鼠點選" #: lib/solaar/ui/diversion_rules.py:410 msgid "Set" msgstr "設定" #: lib/solaar/ui/diversion_rules.py:411 lib/solaar/ui/rule_actions.py:328 msgid "Execute" msgstr "執行" #: lib/solaar/ui/diversion_rules.py:412 lib/solaar/ui/diversion_rules.py:1189 msgid "Later" msgstr "稍後" #: lib/solaar/ui/diversion_rules.py:441 msgid "Insert new rule" msgstr "插入新規則" #: lib/solaar/ui/diversion_rules.py:461 lib/solaar/ui/rule_actions.py:82 #: lib/solaar/ui/rule_actions.py:287 lib/solaar/ui/rule_conditions.py:556 msgid "Delete" msgstr "刪除" #: lib/solaar/ui/diversion_rules.py:483 msgid "Negate" msgstr "否定" #: lib/solaar/ui/diversion_rules.py:507 msgid "Wrap with" msgstr "包裝為" #: lib/solaar/ui/diversion_rules.py:537 msgid "Cut" msgstr "剪下" #: lib/solaar/ui/diversion_rules.py:553 msgid "Paste" msgstr "貼上" #: lib/solaar/ui/diversion_rules.py:559 msgid "Copy" msgstr "複製" #: lib/solaar/ui/diversion_rules.py:568 msgid "Solaar Rule Editor" msgstr "Solaar 規則編輯器" #: lib/solaar/ui/diversion_rules.py:668 msgid "Save changes" msgstr "儲存變更" #: lib/solaar/ui/diversion_rules.py:673 msgid "Discard changes" msgstr "捨棄變更" #: lib/solaar/ui/diversion_rules.py:1104 msgid "This editor does not support the selected rule component yet." msgstr "此編輯器尚未支援所選的規則元件。" #: lib/solaar/ui/diversion_rules.py:1169 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:1207 msgid "Not" msgstr "非" #: lib/solaar/ui/diversion_rules.py:1238 msgid "Toggle" msgstr "切換" #: lib/solaar/ui/diversion_rules.py:1239 msgid "True" msgstr "真" #: lib/solaar/ui/diversion_rules.py:1240 msgid "False" msgstr "假" #: lib/solaar/ui/diversion_rules.py:1253 msgid "Unsupported setting" msgstr "不支援的設定" #: lib/solaar/ui/diversion_rules.py:1396 lib/solaar/ui/diversion_rules.py:1415 #: lib/solaar/ui/diversion_rules.py:1493 lib/solaar/ui/diversion_rules.py:1748 #: lib/solaar/ui/diversion_rules.py:1766 msgid "Originating device" msgstr "來源裝置" #: lib/solaar/ui/diversion_rules.py:1428 msgid "Device is active and its settings can be changed." msgstr "裝置處於使用中狀態,可以變更其設定。" #: lib/solaar/ui/diversion_rules.py:1437 msgid "Device that originated the current notification." msgstr "產生目前通知的裝置。" #: lib/solaar/ui/diversion_rules.py:1450 msgid "Name of host computer." msgstr "主機電腦名稱。" #: lib/solaar/ui/diversion_rules.py:1520 msgid "Value" msgstr "值" #: lib/solaar/ui/diversion_rules.py:1529 msgid "Item" msgstr "項目" #: lib/solaar/ui/diversion_rules.py:1808 msgid "Change setting on device" msgstr "變更裝置上的設定" #: lib/solaar/ui/diversion_rules.py:1824 msgid "Setting on device" msgstr "裝置上的設定" #: lib/solaar/ui/pair_window.py:44 lib/solaar/ui/pair_window.py:167 #, python-format msgid "%(receiver_name)s: pair new device" msgstr "%(receiver_name)s:配對新的裝置" #: lib/solaar/ui/pair_window.py:46 msgid "Bolt receivers are only compatible with Bolt devices." msgstr "Bolt 接收器僅相容於 Bolt 裝置。" #: lib/solaar/ui/pair_window.py:48 msgid "Press a pairing button or key until the pairing light flashes quickly." msgstr "請按住配對按鈕或按鍵,直到配對指示燈快速閃爍。" #: lib/solaar/ui/pair_window.py:51 msgid "Unifying receivers are only compatible with Unifying devices." msgstr "Unifying 接收器僅相容於 Unifying 裝置。" #: lib/solaar/ui/pair_window.py:53 msgid "Other receivers are only compatible with a few devices." msgstr "其他接收器僅支援少數裝置。" #: lib/solaar/ui/pair_window.py:55 msgid "For most devices, turn on the device you want to pair." msgstr "多數裝置只要開啟想要配對的裝置即可。" #: lib/solaar/ui/pair_window.py:56 msgid "If the device is already turned on, turn it off and on again." msgstr "若裝置已開啟,請將其關閉後再重新開啟。" #: lib/solaar/ui/pair_window.py:58 msgid "The device must not be paired with a nearby powered-on receiver." msgstr "裝置不得與附近已開啟的接收器配對。" #: lib/solaar/ui/pair_window.py:61 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 "" "對於有多個通道的裝置,請按住並放開想要配對通道的按鈕,\n" "或使用通道切換按鈕選擇通道後,再按住並放開通道切換按鈕。" #: lib/solaar/ui/pair_window.py:68 msgid "The channel indicator light should be blinking rapidly." msgstr "通道指示燈應會快速閃爍。" #: lib/solaar/ui/pair_window.py:72 #, 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:78 msgid "" "\n" "Cancelling at this point will not use up a pairing." msgstr "" "\n" "在此階段取消不會消耗配對次數。" #: lib/solaar/ui/pair_window.py:168 #, python-format msgid "Enter passcode on %(name)s." msgstr "請在 %(name)s 上輸入密碼。" #: lib/solaar/ui/pair_window.py:171 #, python-format msgid "Type %(passcode)s and then press the enter key." msgstr "請輸入 %(passcode)s,然後按 Enter 鍵。" #: lib/solaar/ui/pair_window.py:176 msgid "left" msgstr "左" #: lib/solaar/ui/pair_window.py:176 msgid "right" msgstr "右" #: lib/solaar/ui/pair_window.py:178 #, 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:221 msgid "The wireless link is not encrypted" msgstr "無線連線未加密" #: lib/solaar/ui/pair_window.py:226 msgid "Found a new device:" msgstr "發現一個新裝置:" #: lib/solaar/ui/pair_window.py:247 msgid "Pairing failed" msgstr "配對失敗" #: lib/solaar/ui/pair_window.py:249 msgid "Make sure your device is within range, and has a decent battery charge." msgstr "請確認裝置在範圍內,且電池電量充足。" #: lib/solaar/ui/pair_window.py:251 msgid "A new device was detected, but it is not compatible with this receiver." msgstr "偵測到新裝置,但與此接收器不相容。" #: lib/solaar/ui/pair_window.py:253 msgid "More paired devices than receiver can support." msgstr "已配對的裝置數量超過接收器可支援的數量。" #: lib/solaar/ui/pair_window.py:255 msgid "No further details are available about the error." msgstr "沒有關於此錯誤的進一步詳細資訊。" #: lib/solaar/ui/rule_actions.py:54 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:60 msgid "Add key" msgstr "新增按鍵" #: lib/solaar/ui/rule_actions.py:63 msgid "Click" msgstr "點選" #: lib/solaar/ui/rule_actions.py:66 msgid "Depress" msgstr "按下" #: lib/solaar/ui/rule_actions.py:69 msgid "Release" msgstr "放開" #: lib/solaar/ui/rule_actions.py:153 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:211 msgid "" "Simulate a mouse click.\n" "On Wayland requires write access to /dev/uinput." msgstr "" "模擬滑鼠點選。\n" "在 Wayland 上需要對 /dev/uinput 的寫入權限。" #: lib/solaar/ui/rule_actions.py:216 msgid "Button" msgstr "按鈕" #: lib/solaar/ui/rule_actions.py:218 msgid "Action (and Count, if click)" msgstr "動作(若為點選則含次數)" #: lib/solaar/ui/rule_actions.py:269 msgid "Execute a command with arguments." msgstr "執行帶有參數的命令。" #: lib/solaar/ui/rule_actions.py:273 msgid "Add argument" msgstr "新增參數" #: lib/solaar/ui/rule_conditions.py:52 msgid "X11 active process. For use in X11 only." msgstr "X11 使用中處理程序。僅適用於 X11。" #: lib/solaar/ui/rule_conditions.py:82 msgid "X11 mouse process. For use in X11 only." msgstr "X11 滑鼠處理程序。僅適用於 X11。" #: lib/solaar/ui/rule_conditions.py:99 msgid "MouseProcess" msgstr "滑鼠處理程序" #: lib/solaar/ui/rule_conditions.py:123 msgid "Feature name of notification triggering rule processing." msgstr "觸發規則處理的通知功能名稱。" #: lib/solaar/ui/rule_conditions.py:169 msgid "Report number of notification triggering rule processing." msgstr "回報觸發規則處理的通知數量。" #: lib/solaar/ui/rule_conditions.py:201 msgid "Active keyboard modifiers. Not always available in Wayland." msgstr "使用中的鍵盤修飾鍵。在 Wayland 中並非總是可用。" #: lib/solaar/ui/rule_conditions.py:241 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:250 msgid "Key down" msgstr "按鍵按下" #: lib/solaar/ui/rule_conditions.py:253 msgid "Key up" msgstr "按鍵鬆開" #: lib/solaar/ui/rule_conditions.py:293 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:331 msgid "Test condition on notification triggering rule processing." msgstr "在通知觸發規則處理上測試條件。" #: lib/solaar/ui/rule_conditions.py:335 msgid "Parameter" msgstr "參數" #: lib/solaar/ui/rule_conditions.py:408 msgid "begin (inclusive)" msgstr "開始(包含)" #: lib/solaar/ui/rule_conditions.py:409 msgid "end (exclusive)" msgstr "結束(不包含)" #: lib/solaar/ui/rule_conditions.py:417 msgid "range" msgstr "範圍" #: lib/solaar/ui/rule_conditions.py:420 msgid "minimum" msgstr "最小值" #: lib/solaar/ui/rule_conditions.py:421 msgid "maximum" msgstr "最大值" #: lib/solaar/ui/rule_conditions.py:423 #, 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:426 lib/solaar/ui/rule_conditions.py:427 msgid "mask" msgstr "遮罩" #: lib/solaar/ui/rule_conditions.py:428 #, 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:437 msgid "" "Bit or range test on bytes in notification message triggering rule " "processing." msgstr "在通知訊息觸發規則處理中對位元組進行位元或範圍測試。" #: lib/solaar/ui/rule_conditions.py:447 msgid "type" msgstr "類型" #: lib/solaar/ui/rule_conditions.py:535 msgid "" "Mouse gesture with optional initiating button followed by zero or more mouse " "movements." msgstr "滑鼠手勢,可選擇性地帶有起始按鈕,後接零個或多個滑鼠移動。" #: lib/solaar/ui/rule_conditions.py:541 msgid "Add movement" msgstr "新增移動" #: lib/solaar/ui/tray.py:55 msgid "No supported device found" msgstr "找不到支援的裝置" #: lib/solaar/ui/tray.py:60 lib/solaar/ui/window.py:328 #, python-format msgid "About %s" msgstr "關於 %s" #: lib/solaar/ui/tray.py:61 lib/solaar/ui/window.py:326 #, python-format msgid "Quit %s" msgstr "結束 %s" #: lib/solaar/ui/tray.py:273 lib/solaar/ui/tray.py:281 msgid "no receiver" msgstr "無接收器" #: lib/solaar/ui/tray.py:294 lib/solaar/ui/tray.py:299 #: lib/solaar/ui/window.py:728 msgid "offline" msgstr "離線" #: lib/solaar/ui/tray.py:297 msgid "no status" msgstr "無狀態" #: lib/solaar/ui/window.py:110 msgid "Scanning" msgstr "掃描中" #: lib/solaar/ui/window.py:141 msgid "Battery" msgstr "電池" #: lib/solaar/ui/window.py:144 msgid "Wireless Link" msgstr "無線連線" #: lib/solaar/ui/window.py:148 msgid "Lighting" msgstr "亮度" #: lib/solaar/ui/window.py:182 msgid "Show Technical Details" msgstr "顯示技術細節" #: lib/solaar/ui/window.py:198 msgid "Pair new device" msgstr "配對新裝置" #: lib/solaar/ui/window.py:216 msgid "Select a device" msgstr "請選擇裝置" #: lib/solaar/ui/window.py:331 msgid "Rule Editor" msgstr "規則編輯器" #: lib/solaar/ui/window.py:522 msgid "Path" msgstr "路徑" #: lib/solaar/ui/window.py:524 msgid "USB ID" msgstr "USB ID" #: lib/solaar/ui/window.py:527 lib/solaar/ui/window.py:529 #: lib/solaar/ui/window.py:544 lib/solaar/ui/window.py:546 msgid "Serial" msgstr "序號" #: lib/solaar/ui/window.py:533 msgid "Index" msgstr "索引" #: lib/solaar/ui/window.py:535 msgid "Wireless PID" msgstr "無線 PID" #: lib/solaar/ui/window.py:537 msgid "Product ID" msgstr "產品 ID" #: lib/solaar/ui/window.py:539 msgid "Protocol" msgstr "通訊協定" #: lib/solaar/ui/window.py:539 msgid "Unknown" msgstr "不明" #: lib/solaar/ui/window.py:541 msgid "Polling rate" msgstr "輪詢速率" #: lib/solaar/ui/window.py:548 msgid "Unit ID" msgstr "單位 ID" #: lib/solaar/ui/window.py:559 msgid "none" msgstr "無" #: lib/solaar/ui/window.py:560 msgid "Notifications" msgstr "通知" #: lib/solaar/ui/window.py:604 msgid "No device paired." msgstr "沒有已配對的裝置。" #: lib/solaar/ui/window.py:613 #, 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:620 msgid "Only one device can be paired to this receiver." msgstr "此接收器只能配對一個裝置" #: lib/solaar/ui/window.py:624 #, 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:681 msgid "Battery Voltage" msgstr "電池電壓" #: lib/solaar/ui/window.py:683 msgid "Voltage reported by battery" msgstr "電池回報的電壓" #: lib/solaar/ui/window.py:685 msgid "Battery Level" msgstr "電池電量" #: lib/solaar/ui/window.py:687 msgid "Approximate level reported by battery" msgstr "電池回報的大約電量" #: lib/solaar/ui/window.py:694 lib/solaar/ui/window.py:696 msgid "next reported " msgstr "下一次回報的 " #: lib/solaar/ui/window.py:697 msgid " and next level to be reported." msgstr " 以及下一個將回報的等級。" #: lib/solaar/ui/window.py:702 msgid "last known" msgstr "最後已知" #: lib/solaar/ui/window.py:713 msgid "encrypted" msgstr "已加密" #: lib/solaar/ui/window.py:715 msgid "The wireless link between this device and its receiver is encrypted." msgstr "此裝置與其接收器之間的無線連線已加密。" #: lib/solaar/ui/window.py:717 msgid "not encrypted" msgstr "未加密" #: lib/solaar/ui/window.py:721 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:737 #, python-format msgid "%(light_level)d lux" msgstr "%(light_level)d lux" ================================================ FILE: pyproject.toml ================================================ [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 ================================================ FILE: release.sh ================================================ #!/usr/bin/env 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 ================================================ FILE: rules.d/42-logitech-unify-permissions.rules ================================================ # 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 == "remove", 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 ================================================ FILE: rules.d-uinput/42-logitech-unify-permissions.rules ================================================ # 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 == "remove", 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 ================================================ FILE: setup.py ================================================ import 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", "typing_extensions", ], extras_require={ "report-descriptor": ["hid-parser"], "desktop-notifications": ["Notify (>= 0.7)"], "git-commit": ["python-git-info"], "test": ["pytest", "pytest-mock", "pytest-cov"], "dev": ["ruff"], }, package_dir={"": "lib"}, packages=find_packages(where="lib"), data_files=list(_data_files()), include_package_data=True, entry_points={ "console_scripts": [ "solaar = solaar.gtk:main", ], }, ) ================================================ FILE: share/README ================================================ Solaar application icon drawn by me, roughly guided by Logitech's Unifying logo. All other images are from the GNOME and Oxygen free icon themes. ================================================ FILE: share/applications/solaar.desktop ================================================ [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 ================================================ FILE: share/autostart/solaar.desktop ================================================ [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 --window=hide Icon=solaar StartupNotify=true Terminal=false Type=Application Keywords=logitech;unifying;receiver;mouse;keyboard; Categories=Utility;GTK; #Categories=Utility;GTK;Settings;HardwareSettings; ================================================ FILE: share/solaar/io.github.pwr_solaar.solaar.metainfo.xml ================================================ io.github.pwr_solaar.solaar Solaar Linux manager for Logitech keyboards, mice, and trackpads pwr-Solaar https://pwr-solaar.github.io/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*
================================================ FILE: tests/__init__.py ================================================ ================================================ FILE: tests/hid_parser/__init__.py ================================================ ================================================ FILE: tests/hid_parser/test_data.py ================================================ from 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 ================================================ FILE: tests/hidapi/__init__.py ================================================ ================================================ FILE: tests/hidapi/test_hidapi.py ================================================ import 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()) ================================================ FILE: tests/logitech_receiver/__init__.py ================================================ ================================================ FILE: tests/logitech_receiver/fake_hidpp.py ================================================ ## 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() number = "d1" present = True 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 = [] self.receiver = [] 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 handle_notification(self, handle): pass def changed(self, *args, **kwargs): pass def set_battery_info(self, *args, **kwargs): pass def status_string(self): pass 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 ================================================ FILE: tests/logitech_receiver/test_base.py ================================================ import 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.common import LOGITECH_VENDOR_ID from logitech_receiver.common import BusID 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 product_id = 0xC548 receiver_info = base.get_known_receiver_info(bus_id, LOGITECH_VENDOR_ID, product_id) assert receiver_info["name"] == "Bolt Receiver" assert receiver_info["receiver_kind"] == "bolt" def test_filter_receivers_unknown(): bus_id = 1 product_id = 0xC500 receiver_info = base.get_known_receiver_info(bus_id, LOGITECH_VENDOR_ID, product_id) assert receiver_info["bus_id"] == bus_id assert receiver_info["product_id"] == product_id @pytest.mark.parametrize( "product_id, bus, hidpp_short, hidpp_long, expected", [ (0xC548, BusID.USB, True, False, {"name": "Bolt Receiver", "usb_interface": 2}), (0xC07D, BusID.USB, True, False, {"usb_interface": 1}), (0xC07E, BusID.USB, False, True, {"usb_interface": 1}), (0xC07E, BusID.BLUETOOTH, False, True, {"bus_id": 5}), (0xA07E, BusID.USB, False, True, {"product_id": 0xA07E}), (0xA07C, BusID.USB, False, False, None), (0xC07F, BusID.USB, None, None, {"usb_interface": 2}), (0xC07F, BusID.BLUETOOTH, None, None, None), (0xB013, BusID.BLUETOOTH, None, None, {"product_id": 0xB013}), ], ) def test_filter_products_of_interest(product_id, bus, hidpp_short, hidpp_long, expected): receiver_info = base.filter_products_of_interest( bus, LOGITECH_VENDOR_ID, product_id, hidpp_short=hidpp_short, hidpp_long=hidpp_long, ) if expected is None: assert receiver_info == expected else: assert all([receiver_info[key] == expected_value for key, expected_value in expected.items()]) assert receiver_info["vendor_id"] == LOGITECH_VENDOR_ID assert receiver_info["product_id"] def test_match(): record = {"vendor_id": LOGITECH_VENDOR_ID} res = base._match_device(record, 0, LOGITECH_VENDOR_ID, 0) assert res is True @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._read_input_buffer"), 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: base.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 = base.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 ================================================ FILE: tests/logitech_receiver/test_base_usb.py ================================================ import pytest from logitech_receiver import base_usb from logitech_receiver.common import LOGITECH_VENDOR_ID def test_ensure_known_receivers_mappings_are_valid(): for key, receiver in base_usb.KNOWN_RECEIVERS.items(): assert key == receiver["product_id"] def test_get_receiver_info(): expected = { "vendor_id": LOGITECH_VENDOR_ID, "product_id": 0xC548, "usb_interface": 2, "name": "Bolt Receiver", "receiver_kind": "bolt", "max_devices": 6, "may_unpair": True, } res = base_usb.get_receiver_info(0xC548) assert res == expected def test_get_receiver_info_unknown_device_fails(): with pytest.raises(ValueError): base_usb.get_receiver_info(0xC500) ================================================ FILE: tests/logitech_receiver/test_common.py ================================================ from 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)" ================================================ FILE: tests/logitech_receiver/test_desktop_notifications.py ================================================ from unittest import mock from logitech_receiver import desktop_notifications # depends on external environment, so make some tests dependent on availability def test_init(): result = desktop_notifications.init() assert result == desktop_notifications.available def test_uninit(): assert desktop_notifications.uninit() is None class MockDevice(mock.Mock): name = "MockDevice" def close(): return True def test_show(): dev = MockDevice() reason = "unknown" result = desktop_notifications.show(dev, reason) assert result is not None if desktop_notifications.available else result is None ================================================ FILE: tests/logitech_receiver/test_device.py ================================================ ## 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 logitech_receiver.common import BatteryLevelApproximation from logitech_receiver.common import BatteryStatus from . import fake_hidpp class LowLevelInterfaceFake: def __init__(self, responses=None): self.responses = responses def open_path(self, path) -> int: 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(Exception): # noqa: B017 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, task_id, 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, task_id, 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 == task_id 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, expected_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(BatteryLevelApproximation.GOOD.value, None, BatteryStatus.DISCHARGING, 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, expected_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() == expected_battery test_device.read_battery() spy_changed.assert_called_with(**changed) ================================================ FILE: tests/logitech_receiver/test_diversion.py ================================================ import 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) ================================================ FILE: tests/logitech_receiver/test_hidpp10.py ================================================ from 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 PairingError 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 == hidpp10_constants.NotificationFlag(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.NotificationFlag.BATTERY_STATUS, hidpp10_constants.NotificationFlag.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.NotificationFlag.BATTERY_STATUS, hidpp10_constants.NotificationFlag.WIRELESS ) assert spy_request.call_count == 0 assert result is None @pytest.mark.parametrize( "flag_bits, expected_names", [ # doesn't work in Python 3.8 and 3.10 for some reason (None, ""), (hidpp10_constants.NotificationFlag(0x0), "none"), (hidpp10_constants.NotificationFlag(0x001000), "multi touch"), (hidpp10_constants.NotificationFlag(0x080000), "mouse extra buttons"), ( hidpp10_constants.NotificationFlag(0x080400), ("link quality\n mouse extra buttons"), ), ], ) def test_notification_flag_str(flag_bits, expected_names): flag_names = hidpp10_constants.flags_to_str( hidpp10_constants.NotificationFlag(flag_bits) if flag_bits is not None else None, fallback="none" ) assert flag_names == expected_names 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 def test_pairing_error(): expected_label = "device not supported" res = PairingError.DEVICE_NOT_SUPPORTED.label assert res == expected_label ================================================ FILE: tests/logitech_receiver/test_hidpp20_complex.py ================================================ ## 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 import KeyFlag from logitech_receiver.hidpp20 import MappingFlag 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, task_id, flags, default_task, expected_flags", [ (device_standard, 2, 1, 1, 0x30, "Volume Up", KeyFlag.REPROGRAMMABLE | KeyFlag.DIVERTABLE), (device_standard, 1, 2, 2, 0x20, "Volume Down", KeyFlag.DIVERTABLE), ], ) def test_reprogrammable_key_key(device, index, cid, task_id, flags, default_task, expected_flags): key = hidpp20.ReprogrammableKey(device, index, cid, task_id, flags) assert key._device == device assert key.index == index assert key._cid == cid assert key._tid == task_id assert key._flags == flags assert key.key == special_keys.CONTROL[cid] assert key.default_task == common.NamedInt(cid, default_task) assert key.flags == expected_flags @pytest.mark.parametrize( "device, index, cid, task_id, flags, pos, group, gmask, default_task, expected_flags, group_names", [ ( device_standard, 1, 0x51, 0x39, 0x60, 0, 1, 1, "Right Click", KeyFlag.DIVERTABLE | KeyFlag.PERSISTENTLY_DIVERTABLE, ["g1"], ), ( device_standard, 2, 0x52, 0x3A, 0x11, 1, 2, 3, "Mouse Middle Button", KeyFlag.MSE | KeyFlag.REPROGRAMMABLE, ["g1", "g2"], ), ( device_standard, 3, 0x53, 0x3C, 0x110, 2, 2, 7, "Mouse Back Button", KeyFlag.REPROGRAMMABLE | KeyFlag.RAW_XY, ["g1", "g2", "g3"], ), ], ) def test_reprogrammable_key_v4_key( device, index, cid, task_id, flags, pos, group, gmask, default_task, expected_flags, group_names ): key = hidpp20.ReprogrammableKeyV4(device, index, cid, task_id, flags, pos, group, gmask) assert key._device == device assert key.index == index assert key._cid == cid assert key._tid == task_id 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 key.flags == expected_flags assert list(key.group_mask) == group_names @pytest.mark.parametrize( "responses, index, mapped_to, remappable_to, expected_mapping_flags", [ (fake_hidpp.responses_key, 1, "Right Click", common.UnsortedNamedInts(Right_Click=81, Left_Click=80), MappingFlag(0)), (fake_hidpp.responses_key, 2, "Left Click", None, MappingFlag.DIVERTED), (fake_hidpp.responses_key, 3, "Mouse Back Button", None, MappingFlag.DIVERTED | MappingFlag.PERSISTENTLY_DIVERTED), (fake_hidpp.responses_key, 4, "Mouse Forward Button", None, MappingFlag.DIVERTED | MappingFlag.RAW_XY_DIVERTED), ], ) # these fields need access all the key data, so start by setting up a device and its key data def test_reprogrammable_key_v4_query(responses, index, mapped_to, remappable_to, expected_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 key.mapping_flags == expected_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_reprogrammable_key_v4_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 = key.mapping_flags if hidpp20.KeyFlag.DIVERTABLE in key.flags or not diverted: key.set_diverted(diverted) else: with pytest.raises(exceptions.FeatureNotSupported): key.set_diverted(diverted) assert (MappingFlag.DIVERTED in key.mapping_flags) == (diverted and hidpp20.KeyFlag.DIVERTABLE in key.flags) if hidpp20.KeyFlag.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 (hidpp20.MappingFlag.PERSISTENTLY_DIVERTED in key.mapping_flags) == ( persistently_diverted and hidpp20.KeyFlag.PERSISTENTLY_DIVERTABLE in key.flags ) if hidpp20.KeyFlag.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 (MappingFlag.RAW_XY_DIVERTED in key.mapping_flags) == (rawXY_reporting and hidpp20.KeyFlag.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, task_id, 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, task_id, 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 == task_id 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_old, 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_keys_array_v4_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, expected_value, expected_string", [ (fake_hidpp.responses_gestures, 1, hidpp20.SpecGesture.DVI_FIELD_WIDTH, 1, 8, "[dvi field width=8]"), (fake_hidpp.responses_gestures, 2, hidpp20.SpecGesture.FIELD_WIDTHS, 1, 8, "[field widths=8]"), (fake_hidpp.responses_gestures, 3, hidpp20.SpecGesture.PERIOD_UNIT, 2, 2048, "[period unit=2048]"), ], ) def test_spec(responses, id, s, byte_count, expected_value, expected_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 == expected_value assert repr(spec) == expected_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, expected_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, expected_behavior, sector, address, typ, val, modifiers, data, byt): button = hidpp20.Button.from_bytes(bytes.fromhex(hex)) assert getattr(button, "behavior", None) == expected_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_onboard_profile_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_onboard_profiles_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 yml_dump = yaml.dump(profiles) assert yaml.safe_load(yml_dump).to_bytes().hex() == profiles.to_bytes().hex() ================================================ FILE: tests/logitech_receiver/test_hidpp20_simple.py ================================================ ## 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 == 92 assert common.BatteryStatus.RECHARGING in battery.status 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 == 92 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 == 92 assert common.BatteryStatus.RECHARGING in battery.status 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 == 92 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 @pytest.mark.parametrize( "code, expected_name", [ (0x00, "Unknown Location"), (0x03, "Left Side"), ], ) def test_led_zone_locations(code, expected_name): assert hidpp20.LEDZoneLocations[code] == expected_name @pytest.mark.parametrize( "millivolt, expected_percentage", [ (-1234, 0), (500, 0), (2000, 0), (3500, 0), (3519, 0), (3520, 1), (3559, 1), (3579, 2), (3646, 5), (3671, 10), (3717, 20), (3751, 30), (3778, 40), (3811, 50), (3859, 60), (3922, 70), (3989, 80), (4067, 90), (4180, 99), (4181, 100), (4186, 100), (4500, 100), ], ) def test_estimate_battery_level_percentage(millivolt, expected_percentage): percentage = hidpp20.estimate_battery_level_percentage(millivolt) assert percentage == expected_percentage ================================================ FILE: tests/logitech_receiver/test_notifications.py ================================================ import 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.hidpp20_constants import SupportedFeature from logitech_receiver.receiver import Receiver from . import fake_hidpp 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 def find_paired_node(self, receiver_path: str, index: int, timeout: int): return None def close(self, device_handle) -> None: pass @pytest.mark.parametrize( "sub_id, notification_data, expected_error, expected_new_device", [ (Registers.DISCOVERY_STATUS_NOTIFICATION, b"\x01", BoltPairingError.DEVICE_TIMEOUT, None), ( Registers.DEVICE_DISCOVERY_NOTIFICATION, b"\x01\x01\x01\x01\x01\x01\x01\x01\x01", None, 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), (Registers.PASSKEY_REQUEST_NOTIFICATION, b"\x06", None, None), (Registers.PASSKEY_PRESSED_NOTIFICATION, b"\x06", None, 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, 0x02, notification_data) result = notifications.process_receiver_notification(receiver, notification) assert result assert receiver.pairing.error == (None if expected_error is None else expected_error.label) assert receiver.pairing.new_device is expected_new_device @pytest.mark.parametrize( "hidpp_notification, expected", [ (HIDPPNotification(0, 0, sub_id=Registers.BATTERY_STATUS, address=0, data=b"0x01"), False), (HIDPPNotification(0, 0, sub_id=Notification.NO_OPERATION, address=0, data=b"0x01"), False), (HIDPPNotification(0, 0, sub_id=0x40, address=0, data=b"0x01"), True), ], ) def test_process_device_notification(hidpp_notification, expected): device = fake_hidpp.Device() result = notifications.process_device_notification(device, hidpp_notification) assert result == expected @pytest.mark.parametrize( "hidpp_notification, expected", [ (HIDPPNotification(0, 0, sub_id=Notification.CONNECT_DISCONNECT, address=0, data=b"0x01"), True), (HIDPPNotification(0, 0, sub_id=Notification.DJ_PAIRING, address=0, data=b"0x01"), True), (HIDPPNotification(0, 0, sub_id=Notification.CONNECTED, address=0, data=b"0x01"), True), (HIDPPNotification(0, 0, sub_id=Notification.RAW_INPUT, address=0, data=b"0x01"), None), ], ) def test_process_dj_notification(hidpp_notification, expected): device = fake_hidpp.Device() result = notifications._process_dj_notification(device, hidpp_notification) assert result == expected @pytest.mark.parametrize( "hidpp_notification, expected", [ (HIDPPNotification(0, 0, sub_id=Registers.BATTERY_STATUS, address=0, data=b"\x01\x00"), True), (HIDPPNotification(0, 0, sub_id=Registers.BATTERY_CHARGE, address=0, data=b"0x01\x00"), True), (HIDPPNotification(0, 0, sub_id=Notification.RAW_INPUT, address=0, data=b"0x01"), None), ], ) def test_process_hidpp10_custom_notification(hidpp_notification, expected): device = fake_hidpp.Device() result = notifications._process_hidpp10_custom_notification(device, hidpp_notification) assert result == expected @pytest.mark.parametrize( "hidpp_notification, expected", [ (HIDPPNotification(0, 0, sub_id=Notification.CONNECT_DISCONNECT, address=0x02, data=b"0x01"), True), (HIDPPNotification(0, 0, sub_id=Notification.CONNECT_DISCONNECT, address=0x00, data=b"0x01"), True), (HIDPPNotification(0, 0, sub_id=Notification.DJ_PAIRING, address=0x00, data=b"0x01"), True), (HIDPPNotification(0, 0, sub_id=Notification.DJ_PAIRING, address=0x02, data=b"0x01"), True), (HIDPPNotification(0, 0, sub_id=Notification.DJ_PAIRING, address=0x03, data=b"0x01"), True), (HIDPPNotification(0, 0, sub_id=Notification.DJ_PAIRING, address=0x03, data=b"0x4040"), True), (HIDPPNotification(0, 0, sub_id=Notification.RAW_INPUT, address=0x00, data=b"0x01"), True), (HIDPPNotification(0, 0, sub_id=Notification.POWER, address=0x00, data=b"0x01"), True), (HIDPPNotification(0, 0, sub_id=Notification.POWER, address=0x01, data=b"0x01"), True), (HIDPPNotification(0, 0, sub_id=Notification.PAIRING_LOCK, address=0x01, data=b"0x01"), None), ], ) def test_process_hidpp10_notification(hidpp_notification, expected): fake_device = fake_hidpp.Device() fake_device.receiver = ["rec1", "rec2"] result = notifications._process_hidpp10_notification(fake_device, hidpp_notification) assert result == expected @pytest.mark.parametrize( "hidpp_notification, feature", [ ( HIDPPNotification(0, 0, sub_id=Notification.CONNECT_DISCONNECT, address=0x00, data=b"0x01"), SupportedFeature.BATTERY_STATUS, ), ( HIDPPNotification(0, 0, sub_id=Notification.CONNECT_DISCONNECT, address=0x02, data=b"0x01"), SupportedFeature.BATTERY_STATUS, ), ( HIDPPNotification(0, 0, sub_id=Notification.CONNECT_DISCONNECT, address=0x00, data=b"0x01"), SupportedFeature.BATTERY_VOLTAGE, ), ( HIDPPNotification(0, 0, sub_id=Notification.CONNECT_DISCONNECT, address=0x05, data=b"0x01"), SupportedFeature.BATTERY_VOLTAGE, ), ( HIDPPNotification(0, 0, sub_id=Notification.CONNECT_DISCONNECT, address=0x00, data=b"0x01"), SupportedFeature.UNIFIED_BATTERY, ), ( HIDPPNotification(0, 0, sub_id=Notification.CONNECT_DISCONNECT, address=0x02, data=b"0x01"), SupportedFeature.UNIFIED_BATTERY, ), ( HIDPPNotification(0, 0, sub_id=Notification.CONNECT_DISCONNECT, address=0x00, data=b"0x01"), SupportedFeature.ADC_MEASUREMENT, ), ( HIDPPNotification(0, 0, sub_id=Notification.CONNECT_DISCONNECT, address=0x02, data=b"0x01"), SupportedFeature.ADC_MEASUREMENT, ), ( HIDPPNotification(0, 0, sub_id=Notification.CONNECT_DISCONNECT, address=0x00, data=b"01234GOOD"), SupportedFeature.SOLAR_DASHBOARD, ), ( HIDPPNotification(0, 0, sub_id=Notification.CONNECT_DISCONNECT, address=0x10, data=b"01234GOOD"), SupportedFeature.SOLAR_DASHBOARD, ), ( HIDPPNotification(0, 0, sub_id=Notification.CONNECT_DISCONNECT, address=0x20, data=b"01234GOOD"), SupportedFeature.SOLAR_DASHBOARD, ), ( HIDPPNotification(0, 0, sub_id=Notification.CONNECT_DISCONNECT, address=0x02, data=b"01234GOOD"), SupportedFeature.SOLAR_DASHBOARD, ), ( HIDPPNotification(0, 0, sub_id=Notification.CONNECT_DISCONNECT, address=0x02, data=b"CHARGENOTGOOD"), SupportedFeature.SOLAR_DASHBOARD, ), ( HIDPPNotification(0, 0, sub_id=Notification.CONNECT_DISCONNECT, address=0x00, data=b"\x01\x01\x02"), SupportedFeature.WIRELESS_DEVICE_STATUS, ), ( HIDPPNotification(0, 0, sub_id=Notification.CONNECT_DISCONNECT, address=0x02, data=b"0x01"), SupportedFeature.WIRELESS_DEVICE_STATUS, ), ( HIDPPNotification(0, 0, sub_id=Notification.CONNECT_DISCONNECT, address=0x00, data=b"0x01"), SupportedFeature.TOUCHMOUSE_RAW_POINTS, ), ( HIDPPNotification(0, 0, sub_id=Notification.CONNECT_DISCONNECT, address=0x10, data=b"0x01"), SupportedFeature.TOUCHMOUSE_RAW_POINTS, ), ( HIDPPNotification(0, 0, sub_id=Notification.CONNECT_DISCONNECT, address=0x05, data=b"0x01"), SupportedFeature.TOUCHMOUSE_RAW_POINTS, ), ( HIDPPNotification(0, 0, sub_id=Notification.CONNECT_DISCONNECT, address=0x00, data=b"0x01"), SupportedFeature.REPROG_CONTROLS, ), ( HIDPPNotification(0, 0, sub_id=Notification.CONNECT_DISCONNECT, address=0x02, data=b"0x01"), SupportedFeature.REPROG_CONTROLS, ), ( HIDPPNotification(0, 0, sub_id=Notification.CONNECT_DISCONNECT, address=0x00, data=b"0x01"), SupportedFeature.BACKLIGHT2, ), ( HIDPPNotification( 0, 0, sub_id=Notification.CONNECT_DISCONNECT, address=0x00, data=b"\x01\x01\x01\x01\x01\x01\x01\x01" ), SupportedFeature.REPROG_CONTROLS_V4, ), ( HIDPPNotification(0, 0, sub_id=Notification.CONNECT_DISCONNECT, address=0x10, data=b"0x01"), SupportedFeature.REPROG_CONTROLS_V4, ), ( HIDPPNotification(0, 0, sub_id=Notification.CONNECT_DISCONNECT, address=0x20, data=b"0x01"), SupportedFeature.REPROG_CONTROLS_V4, ), ( HIDPPNotification(0, 0, sub_id=Notification.CONNECT_DISCONNECT, address=0x00, data=b"0x01"), SupportedFeature.HIRES_WHEEL, ), ( HIDPPNotification(0, 0, sub_id=Notification.CONNECT_DISCONNECT, address=0x10, data=b"0x01"), SupportedFeature.HIRES_WHEEL, ), ( HIDPPNotification(0, 0, sub_id=Notification.CONNECT_DISCONNECT, address=0x02, data=b"0x01"), SupportedFeature.HIRES_WHEEL, ), ( HIDPPNotification(0, 0, sub_id=Notification.CONNECT_DISCONNECT, address=0x00, data=b"0x01"), SupportedFeature.ONBOARD_PROFILES, ), ( HIDPPNotification(0, 0, sub_id=Notification.CONNECT_DISCONNECT, address=0x20, data=b"0x01"), SupportedFeature.ONBOARD_PROFILES, ), ( HIDPPNotification(0, 0, sub_id=Notification.CONNECT_DISCONNECT, address=0x00, data=b"0x01"), SupportedFeature.BRIGHTNESS_CONTROL, ), ( HIDPPNotification(0, 0, sub_id=Notification.CONNECT_DISCONNECT, address=0x10, data=b"0x01"), SupportedFeature.BRIGHTNESS_CONTROL, ), ( HIDPPNotification(0, 0, sub_id=Notification.CONNECT_DISCONNECT, address=0x20, data=b"0x01"), SupportedFeature.BRIGHTNESS_CONTROL, ), ], ) def test_process_feature_notification(mocker, hidpp_notification, feature): fake_device = fake_hidpp.Device() fake_device.receiver = ["rec1", "rec2"] result = notifications._process_feature_notification(fake_device, hidpp_notification) assert result is True def test_process_receiver_notification_invalid(mocker): invalid_sub_id = 0x30 notification_data = b"\x02" notification = HIDPPNotification(0, 0, invalid_sub_id, 0, notification_data) mock_receiver = mocker.Mock() with pytest.raises(AssertionError): notifications.process_receiver_notification(mock_receiver, notification) @pytest.mark.parametrize( "sub_id, notification_data, expected", [ (Notification.NO_OPERATION, b"\x00", False), ], ) def test_process_device_notification_extended(mocker, sub_id, notification_data, expected): device = mocker.Mock() device.handle_notification.return_value = None device.protocol = 2.0 notification = HIDPPNotification(0, 0, sub_id, 0, notification_data) result = notifications.process_device_notification(device, notification) assert result == expected def test_handle_device_discovery(): receiver: Receiver = Receiver(MockLowLevelInterface(), None, {}, True, None, None) sub_id = Registers.DISCOVERY_STATUS_NOTIFICATION data = b"\x01\x02\x03\x04\x05\x06" notification = HIDPPNotification(0, 0, sub_id, 0, data) result = notifications.handle_device_discovery(receiver, notification) assert result def test_handle_passkey_request(mocker): receiver_mock = mocker.Mock() data = b"\x01" notification = HIDPPNotification(0, 0, 0, 0, data) result = notifications.handle_passkey_request(receiver_mock, notification) assert result is True def test_handle_passkey_pressed(mocker): receiver = mocker.Mock() sub_id = Registers.DISCOVERY_STATUS_NOTIFICATION data = b"\x01\x02\x03\x04\x05\x06" notification = HIDPPNotification(0, 0, sub_id, 0, data) result = notifications.handle_passkey_pressed(receiver, notification) assert result is True ================================================ FILE: tests/logitech_receiver/test_receiver.py ================================================ from 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 @pytest.fixture def nano_recv(): device_info = DeviceInfo("12", product_id=0xC534) mock_low_level = LowLevelInterfaceFake(responses_lacking) yield receiver.create_receiver(mock_low_level, device_info, lambda x: x) 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) @pytest.mark.parametrize( "address, data, expected_online, expected_encrypted", [ (0x03, b"\x01\x02\x03", True, False), (0x10, b"\x61\x02\x03", False, True), ], ) def test_notification_information_nano_receiver(nano_recv, address, data, expected_online, expected_encrypted): _number = 0 notification = base.HIDPPNotification( report_id=0x01, devnumber=0x52C, sub_id=0, address=address, data=data, ) online, encrypted, wpid, kind = nano_recv.notification_information(_number, notification) assert online == expected_online assert encrypted == expected_encrypted assert wpid == "0302" assert kind == "keyboard" def test_extract_serial_number(): response = b'\x03\x16\xcc\x9c\xb4\x05\x06"\x00\x00\x00\x00\x00\x00\x00\x00' serial_number = receiver.extract_serial(response[1:5]) assert serial_number == "16CC9CB4" def test_extract_max_devices(): response = b'\x03\x16\xcc\x9c\xb4\x05\x06"\x00\x00\x00\x00\x00\x00\x00\x00' max_devices = receiver.extract_max_devices(response) assert max_devices == 6 @pytest.mark.parametrize( "response, expected_remaining_pairings", [ (b"\x00\x03\x00", -1), (b"\x00\x02\t", 4), ], ) def test_extract_remaining_pairings(response, expected_remaining_pairings): remaining_pairings = receiver.extract_remaining_pairings(response) assert remaining_pairings == expected_remaining_pairings def test_extract_codename(): response = b"A\x04K520" codename = receiver.extract_codename(response) assert codename == "K520" @pytest.mark.parametrize( "power_switch_byte, expected_location", [ (b"\x01", "base"), (b"\x09", "top_edge"), (b"\x0c", "bottom_edge"), (b"\x00", "unknown"), (b"\x0f", "unknown"), ], ) def test_extract_power_switch_location(power_switch_byte, expected_location): response = b"\x19\x8e>\xb8\x06\x00\x00\x00\x00" + power_switch_byte + b"\x00\x00\x00\x00\x00" ps_location = receiver.extract_power_switch_location(response) assert ps_location == expected_location def test_extract_connection_count(): response = b"\x00\x03\x00" connection_count = receiver.extract_connection_count(response) assert connection_count == 3 def test_extract_wpid(): response = b"@\x82" res = receiver.extract_wpid(response) assert res == "4082" def test_extract_polling_rate(): response = b"\x08@\x82\x04\x02\x02\x07\x00\x00\x00\x00\x00\x00\x00" polling_rate = receiver.extract_polling_rate(response) assert polling_rate == 130 @pytest.mark.parametrize( "data, expected_device_kind", [ (0x00, "unknown"), (0x03, "numpad"), ], ) def test_extract_device_kind(data, expected_device_kind): device_kind = receiver.extract_device_kind(data) assert str(device_kind) == expected_device_kind ================================================ FILE: tests/logitech_receiver/test_setting_templates.py ================================================ ## 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 readable: bool = True 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, version=0x03), 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), readable=False, ), 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), readable=False, ), 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), readable=False, ), 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, 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 if tst.readable: 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 ================================================ FILE: tests/logitech_receiver/test_settings_validator.py ================================================ import pytest from logitech_receiver import settings_validator @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_validator.bool_or_toggle(current=current, new=new) assert result == expected ================================================ FILE: tests/solaar/test_gtk.py ================================================ from solaar.gtk import create_parser def test_arg_parse(): parser = create_parser() res = parser.parse_args([]) assert res.debug == 0 assert res.hidraw_path is None assert res.restart_on_wake_up is False assert res.window is None assert res.battery_icons is None assert res.tray_icon_size is None def test_arg_parse_debug(): parser = create_parser() res = parser.parse_args(["--debug"]) assert res.debug == 1 def test_arg_parse_version(): parser = create_parser() res = parser.parse_args(["version"]) assert res ================================================ FILE: tests/solaar/ui/test_about_dialog.py ================================================ from 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 ================================================ FILE: tests/solaar/ui/test_common.py ================================================ from 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", [ ( common.ErrorReason.PERMISSIONS, "Permissions error", "not have permission to open", ), (common.ErrorReason.NO_DEVICE, "connect to device error", "error connecting"), (common.ErrorReason.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 ================================================ FILE: tests/solaar/ui/test_desktop_notifications.py ================================================ from unittest import mock from solaar.ui import desktop_notifications # depends on external environment, so make some tests dependent on availability def test_init(): result = desktop_notifications.init() assert result == desktop_notifications.available def test_uninit(): assert desktop_notifications.uninit() is None def test_alert(): reason = "unknown" assert desktop_notifications.alert(reason) is None class MockDevice(mock.Mock): name = "MockDevice" def close(): return True def test_show(): dev = MockDevice() reason = "unknown" available = desktop_notifications.init() result = desktop_notifications.show(dev, reason) if available: assert result is not None else: assert result is None ================================================ FILE: tests/solaar/ui/test_i18n.py ================================================ import 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" ================================================ FILE: tests/solaar/ui/test_pair_window.py ================================================ from dataclasses import dataclass from dataclasses import field from typing import Any from typing import List from typing import Optional 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: 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("unifying", "unifying", True), True, False, Gtk.AssistantPageType.PROGRESS), (Receiver("unifying", "unifying", False), False, False, Gtk.AssistantPageType.SUMMARY), (Receiver("nano", "nano", True, _remaining_pairings=5), True, False, Gtk.AssistantPageType.PROGRESS), (Receiver("nano", "nano", False), False, False, Gtk.AssistantPageType.SUMMARY), (Receiver("bolt", "bolt", True), False, True, Gtk.AssistantPageType.PROGRESS), (Receiver("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("unifying", "unifying", True), True, False), (Receiver("unifying", "unifying", False), False, True), (Receiver("bolt", "bolt", True), True, False), (Receiver("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("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("fail", "unifying", False, receiver.Pairing(lock_open=False)), 2, False), (Receiver("succeed", "unifying", True, receiver.Pairing(lock_open=True)), 1, True), (Receiver("error", "unifying", True, receiver.Pairing(error="error")), 0, False), (Receiver("new device", "unifying", True, receiver.Pairing(new_device=Device())), 2, False), (Receiver("closed", "unifying", True, receiver.Pairing()), 2, False), (Receiver("closed", "unifying", True, receiver.Pairing()), 1, False), (Receiver("closed", "unifying", True, receiver.Pairing()), 0, False), (Receiver("fail bolt", "bolt", False), 1, False), (Receiver("succeed bolt", "bolt", True, receiver.Pairing(lock_open=True)), 0, True), (Receiver("error bolt", "bolt", True, receiver.Pairing(error="error")), 2, False), (Receiver("new device", "bolt", True, receiver.Pairing(lock_open=True, new_device=Device())), 1, False), (Receiver("discovering", "bolt", True, receiver.Pairing(lock_open=True)), 1, True), (Receiver("closed", "bolt", True, receiver.Pairing()), 2, False), (Receiver("closed", "bolt", True, receiver.Pairing()), 1, False), (Receiver("closed", "bolt", True, receiver.Pairing()), 0, False), ( Receiver( "pass1", "bolt", True, receiver.Pairing(lock_open=True, device_passkey=50, device_authentication=0x01), ), 0, True, ), ( Receiver( "pass2", "bolt", True, receiver.Pairing(lock_open=True, device_passkey=50, device_authentication=0x02), ), 0, True, ), ( Receiver( "adt", "bolt", True, receiver.Pairing(discovering=True, device_address=2, device_name=5), pairable=True, ), 2, True, ), ( Receiver( "adf", "bolt", True, receiver.Pairing(discovering=True, device_address=2, device_name=5), pairable=False, ), 2, False, ), (Receiver("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("unifying", "unifying", pairing=receiver.Pairing(lock_open=False, error="error")), 0, 0, 0, None, ), ( Receiver("unifying", "unifying", pairing=receiver.Pairing(lock_open=True, error="error")), 0, 1, 0, "error", ), (Receiver("bolt", "bolt", pairing=receiver.Pairing(lock_open=False, error="error")), 0, 0, 0, None), (Receiver("bolt", "bolt", pairing=receiver.Pairing(lock_open=True, error="error")), 1, 0, 0, "error"), (Receiver("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("nano", "nano"), error) assert spy_create.call_count == 1 ================================================ FILE: tests/solaar/ui/test_probe.py ================================================ from 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}" ================================================ FILE: tests/test_keysyms/__init__.py ================================================ ================================================ FILE: tests/test_keysyms/test_keysymdef.py ================================================ from 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 ================================================ FILE: tools/clean.sh ================================================ #!/usr/bin/env sh cd "$(dirname "$0")/.." find . -type f -name '*.py[co]' -delete find . -type d -name '__pycache__' -delete rm --force po/*~ rm --force --recursive share/locale/ ================================================ FILE: tools/create-macos-app.sh ================================================ #!/usr/bin/env bash # Helper to build a minimal macOS .app wrapper for Solaar. set -euo pipefail APP_ROOT=${1:-/Applications/Solaar.app} SOLAAR_PATH=${SOLAAR_PATH:-solaar} SOLAAR_RESOLVED_PATH=$(command -v "${SOLAAR_PATH}" 2>/dev/null || echo "") if [ -z "${SOLAAR_RESOLVED_PATH}" ]; then echo "Error: '${SOLAAR_PATH}' not found" >&2 exit 1 fi ICON_SOURCE=${ICON_SOURCE:-share/solaar/icons/solaar.svg} case "${APP_ROOT}" in ""|"/"|".") echo "Error: Refusing to create app bundle at unsafe location: \"${APP_ROOT}\"" >&2 exit 1 ;; esac echo "Creating Solaar app bundle at ${APP_ROOT}" rm -rf "${APP_ROOT}" APP_CONTENTS="${APP_ROOT}/Contents" MACOS_DIR="${APP_CONTENTS}/MacOS" RESOURCES_DIR="${APP_CONTENTS}/Resources" mkdir -p "${MACOS_DIR}" "${RESOURCES_DIR}" WRAPPER="${MACOS_DIR}/solaar-wrapper" cat > "${WRAPPER}" <