pax_global_header00006660000000000000000000000064136201167100014507gustar00rootroot0000000000000052 comment=0ab5c63ed404f619dcd6c380d8881d3f9eb3d406 piper-0.4/000077500000000000000000000000001362011671000124715ustar00rootroot00000000000000piper-0.4/.circleci/000077500000000000000000000000001362011671000143245ustar00rootroot00000000000000piper-0.4/.circleci/config.yml000066400000000000000000000050631362011671000163200ustar00rootroot00000000000000# vim: set expandtab shiftwidth=2 tabstop=8: piper_references: build_dependencies: &build_dependencies FEDORA_RPMS: meson gettext python3 pygobject3-devel python3-lxml libratbag-ratbagd python3-cairo python3-evdev python3-flake8 gtk-update-icon-cache UBUNTU_DEBS: meson pkg-config gettext python3 python-gi-dev python3-lxml python3-evdev gir1.2-rsvg-2.0 python3-gi-cairo python3-flake8 ratbagd gtk-update-icon-cache default_settings: &default_settings working_directory: ~/piper environment: LANG: C.UTF-8 build_default: &build_default name: Build command: | rm -rf build meson build ${MESON_PARAMS} meson configure build ninja -v -C build ${NINJA_ARGS} environment: MESON_PARAMS: --prefix=/usr build_buildtype_plain: &build_buildtype_plain run: <<: *build_default name: Build with buildtype plain environment: MESON_PARAMS: --prefix=/usr -Dbuildtype=plain build_buildtype_release: &build_buildtype_release run: <<: *build_default name: Build with buildtype release environment: MESON_PARAMS: --prefix=/usr -Dbuildtype=release build_and_test: &build_and_test run: <<: *build_default name: Build and test environment: NINJA_ARGS: test install: &install run: name: Installing command: ninja -C build install export_logs: &export_logs store_artifacts: path: ~/piper/build/meson-logs fedora_settings: &fedora_settings <<: *default_settings steps: - run: name: Install prerequisites command: | dnf install -y tree git diffutils ${FEDORA_RPMS} - checkout - *build_and_test - *install - *build_buildtype_plain - *build_buildtype_release - *export_logs environment: *build_dependencies ubuntu_settings: &ubuntu_settings <<: *default_settings steps: - run: name: Install prerequisites command: | apt-get update apt-get install -y software-properties-common add-apt-repository universe apt-get update apt-get install -y tree git ${UBUNTU_DEBS} - checkout - *build_and_test - *install - *build_buildtype_plain - *build_buildtype_release - *export_logs environment: *build_dependencies version: 2 jobs: fedora_31: <<: *fedora_settings docker: - image: fedora:31 ubuntu_19_04: <<: *ubuntu_settings docker: - image: ubuntu:19.04 workflows: version: 2 compile_and_test: jobs: - ubuntu_19_04 - fedora_31 piper-0.4/.flake8000066400000000000000000000001321362011671000136400ustar00rootroot00000000000000[flake8] ignore = E402,E501 exclude = .git,__pycache__,build,data,piper/piper.py,piper.in piper-0.4/.github/000077500000000000000000000000001362011671000140315ustar00rootroot00000000000000piper-0.4/.github/ISSUE_TEMPLATE/000077500000000000000000000000001362011671000162145ustar00rootroot00000000000000piper-0.4/.github/ISSUE_TEMPLATE/bug_report.md000066400000000000000000000016341362011671000207120ustar00rootroot00000000000000--- name: Bug report about: Create a report to help us improve title: '' labels: bug assignees: '' --- **Information** - `ratbagd` version (`ratbagd --version`): - Piper version: - Distribution: - Kernel version (ex. `uname -srmo`): `KERNEL VERSION HERE` **Describe the bug** A clear and concise description of what the bug is. **To Reproduce** Please make sure your bug is reproducible with the CLI client, `ratbagctl`. If it is not open a bug report in libratbag instead. Steps to reproduce the behavior: 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' 4. See error **Logs** Please start the daemon with verbosity and reproduce the issue. First make sure it isn't running already. ``` $ ratbagd --verbose=raw OUTPUT HERE ``` **Additional context** Add any other context about the problem here. piper-0.4/.github/ISSUE_TEMPLATE/feature_request.md000066400000000000000000000011341362011671000217400ustar00rootroot00000000000000--- name: Feature request about: Suggest an idea for this project title: '' labels: enhancement assignees: '' --- **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. piper-0.4/COPYING000066400000000000000000000431331362011671000135300ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin St, 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 Library 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 St, 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 Library General Public License instead of this License. piper-0.4/README.md000066400000000000000000000066611362011671000137610ustar00rootroot00000000000000Piper [![CircleCI](https://circleci.com/gh/libratbag/piper.svg?style=svg&circle-token=7082ad7a7fea706fff66f1547649dca32e446cb0)](https://circleci.com/gh/libratbag/piper) ===== Piper is a GTK+ application to configure gaming mice. Piper is merely a graphical frontend to the ratbagd DBus daemon, see [the libratbag README](https://github.com/libratbag/libratbag/blob/master/README.md#running-ratbagd-as-dbus-activated-systemd-service) for instructions on how to run ratbagd. If you are running piper from git, we recommend using libratbag from git as well to make sure the latest bugfixes are applied. Supported Devices ================= Piper is merely a frontend, the list of supported devices depends on libratbag. See [the libratbag device files](https://github.com/libratbag/libratbag/tree/master/data/devices) for a list of all known devices. The device-specific protocols usually have to be reverse-engineered and the features available may vary to the manufacturer's advertized features. Screenshots =========== ![resolution configuration screenshot](https://github.com/libratbag/piper/blob/wiki/screenshots/piper-resolutionpage.png) ![button configuration screenshot](https://github.com/libratbag/piper/blob/wiki/screenshots/piper-buttonpage.png) ![LED configuration screenshot](https://github.com/libratbag/piper/blob/wiki/screenshots/piper-ledpage.png) And if you see the mousetrap, something isn't right. Usually this means that either ratbagd is not running (like in this screenshot), ratbagd needs to be updated to a newer version, or some other unexpected error occured. ![The error page](https://github.com/libratbag/piper/blob/wiki/screenshots/piper-errorpage.png) Installing Piper ================ See [our Wiki](https://github.com/libratbag/piper/wiki/Installation) for how to install Piper. Building Piper from git ======================= Piper uses the [meson build system](http://mesonbuild.com/). Run the following commands to clone Piper and initialize the build: ``` $ git clone https://github.com/libratbag/piper.git $ cd piper $ meson builddir --prefix=/usr/ ``` To build or re-build after code-changes and install, run: ``` $ ninja -C builddir $ sudo ninja -C builddir install ``` Note: `builddir` is the build output directory and can be changed to any other directory name. See [our Wiki](https://github.com/libratbag/piper/wiki/Installation) for what to do when you encounter missing dependencies. Contributing ============ Yes please. It's best to contact us first to see what you could do. Note that the devices displayed by Piper come from libratbag. For quicker development iteration, there is a special binary `piper.devel` that uses data files from the git directory. This removes the need to install piper after every code change. ``` $ ninja -C builddir $ ./builddir/piper.devel ``` Note that this still requires ratbagd to run on the system bus. Piper tries to conform to Python's PEP8 style guide. To verify your code before opening a PR, please install `flake8` and run the following commands to install its pre-commit hook: ``` $ flake8 --install-hook git $ git config --bool flake8.strict true ``` Source ====== `git clone https://github.com/libratbag/piper.git` Bugs ==== Bugs can be reported in the issue tracker on our GitHub repo: https://github.com/libratbag/piper/issues License ======= Licensed under the GPLv2. See the [COPYING](https://github.com/libratbag/piper/blob/master/COPYING) file for the full license information. piper-0.4/data/000077500000000000000000000000001362011671000134025ustar00rootroot00000000000000piper-0.4/data/404.svg000066400000000000000000000255621362011671000144440ustar00rootroot00000000000000 image/svg+xml piper-0.4/data/check-files-in-git.sh000066400000000000000000000010661362011671000173030ustar00rootroot00000000000000#!/bin/bash # # Usage: check-files-in-git.sh /path/to/piper/ if [ -z "$top_srcdir" ]; then top_srcdir="$1" fi if [ -z "$top_srcdir" ]; then echo "Usage: `basename $0` /path/to/piper" exit 1 fi export GIT_DIR="$top_srcdir/.git"; if ! git ls-files >& /dev/null; then echo "Not a git tree. Skipping" exit 77 fi pushd "$top_srcdir" > /dev/null for file in data/svgs/*.svg; do git ls-files --error-unmatch "$file" &> /dev/null || ( echo "ERROR: File $file is not in git" && test); rc="$(($rc + $?))"; done popd > /dev/null exit $rc piper-0.4/data/check-svg-in-meson.build.sh000077500000000000000000000002111362011671000204260ustar00rootroot00000000000000#!/bin/bash -e pushd "$1" > /dev/null diff -u1 <(grep -o 'svgs/.*\.svg' data/meson.build) <(cd data; ls -v svgs/*.svg) popd > /dev/null piper-0.4/data/enter-keyboard-shortcut.svg000066400000000000000000000433331362011671000207150ustar00rootroot00000000000000 image/svg+xml piper-0.4/data/generate-piper-gresource.xml.py000066400000000000000000000010171362011671000214550ustar00rootroot00000000000000#!/usr/bin/env python3 import sys infile = sys.argv[1] outfile = sys.argv[2] svgs = sys.argv[3:] print("Using input file: {}".format(infile)) print("Writing to output file: {}".format(outfile)) print("SVG files: {}".format(",".join(svgs))) with open(infile) as f_in: with open(outfile, 'w') as f_out: for line in f_in: if '@SVG_FILES@' in line: for svg in svgs: f_out.write(line.replace('@SVG_FILES@', svg)) continue f_out.write(line) piper-0.4/data/led-off.svg000066400000000000000000001324211362011671000154420ustar00rootroot00000000000000 image/svg+xml piper-0.4/data/meson.build000066400000000000000000000070701362011671000155500ustar00rootroot00000000000000gnome = import('gnome') desktopdir = join_paths(datadir, 'applications') icondir = join_paths(datadir, 'icons', 'hicolor', 'scalable', 'apps') metainfodir = join_paths(datadir, 'metainfo') conf = configuration_data() conf.set('version', meson.project_version()) conf.set('version_date', version_date) about_dialog = configure_file(input: 'ui/AboutDialog.ui.in', output: 'AboutDialog.ui', configuration: conf) install_data('org.freedesktop.Piper.svg', install_dir: icondir) i18n.merge_file(input: 'org.freedesktop.Piper.desktop.in', output: 'org.freedesktop.Piper.desktop', type: 'desktop', po_dir: podir, install: true, install_dir: desktopdir) appdata = configure_file(input: 'org.freedesktop.Piper.appdata.xml.in.in', output: 'org.freedesktop.Piper.appdata.xml.in', configuration: conf) i18n.merge_file(input: appdata, output: 'org.freedesktop.Piper.appdata.xml', type: 'xml', po_dir: podir, install: true, install_dir: metainfodir) # SVG files svgs = [ 'svgs/fallback.svg', 'svgs/logitech-g9.svg', 'svgs/logitech-g102-g203.svg', 'svgs/logitech-g300.svg', 'svgs/logitech-g303.svg', 'svgs/logitech-g402.svg', 'svgs/logitech-g403.svg', 'svgs/logitech-g500.svg', 'svgs/logitech-g500s.svg', 'svgs/logitech-g502.svg', 'svgs/logitech-g600.svg', 'svgs/logitech-g602.svg', 'svgs/logitech-g603.svg', 'svgs/logitech-g604.svg', 'svgs/logitech-g700.svg', 'svgs/logitech-g703.svg', 'svgs/logitech-g900.svg', 'svgs/logitech-g-pro.svg', 'svgs/logitech-g-pro-wireless.svg', 'svgs/logitech-mx-anywhere2.svg', 'svgs/logitech-mx-anywhere2s.svg', 'svgs/logitech-mx-ergo.svg', 'svgs/logitech-mx-master.svg', 'svgs/logitech-mx-master-2s.svg', 'svgs/logitech-mx-master-3.svg', 'svgs/roccat-kone-pure.svg', 'svgs/roccat-kone-xtd.svg', 'svgs/steelseries-kinzu-v2.svg', 'svgs/steelseries-kinzu-v3.svg', 'svgs/steelseries-rival.svg', 'svgs/steelseries-rival310.svg', 'svgs/steelseries-rival600.svg', 'svgs/steelseries-sensei310.svg', 'svgs/steelseries-senseiraw.svg', ] svg_mapping = files('svgs/svg-lookup.ini') test_svg_files = find_program('svgs/check-svg.py') test('check-svg', test_svg_files, args: svgs, env: ['BASEDIR=@0@'.format(meson.current_source_dir())]) test('svg-lookup-check', find_program('svg-lookup-ini-test.py'), args: [ svg_mapping, join_paths(meson.source_root(), 'data/svgs/') ], ) # meson requires that we specify all files one-by-one, so let's add a test # that we can't forget about that. test('svg-files-in-meson.build', find_program('check-svg-in-meson.build.sh'), args: [meson.source_root()], suite: ['all']) test('files-in-git', find_program('check-files-in-git.sh'), args: [meson.source_root()], suite: ['all']) gresource = configure_file(input: 'piper.gresource.xml.in', output: 'piper.gresource.xml', command: ['generate-piper-gresource.xml.py', join_paths(meson.current_source_dir(), 'piper.gresource.xml.in'), join_paths(meson.current_build_dir(), 'piper.gresource.xml'), svgs ] ) gnome.compile_resources('piper', gresource, source_dir: '.', dependencies: [about_dialog], gresource_bundle: true, install: true, install_dir: pkgdatadir) piper-0.4/data/org.freedesktop.Piper.appdata.xml.in.in000066400000000000000000000040651362011671000227330ustar00rootroot00000000000000 org.freedesktop.Piper FSFAP GPL-2.0+ Piper Configurable mouse configuration utility

Piper is a graphical user interface to configure gaming mice. Configuration options include changing the resolution (DPI) of the mouse, adding and removing profiles, setting LED colors and changing button behaviors.

Piper requires libratbag’s ratbagd, the daemon to actually communicate with the mice. Piper is merely a front end to ratbagd, ratbagd must be installed and running when Piper is launched.

AppMenu HiDpiIcon ModernToolkit org.freedesktop.Piper.desktop The button configuraton page https://github.com/libratbag/piper/raw/wiki/screenshots/flathub/piper-buttonpage.png The LED configuraton page https://github.com/libratbag/piper/raw/wiki/screenshots/flathub/piper-ledpage.png The resolution configuraton page https://github.com/libratbag/piper/raw/wiki/screenshots/flathub/piper-resolutionpage.png https://github.com/libratbag/piper/ https://github.com/libratbag/piper/issues https://github.com/libratbag/piper/wiki GNOME piper piper
piper-0.4/data/org.freedesktop.Piper.desktop.in000066400000000000000000000007021362011671000215600ustar00rootroot00000000000000[Desktop Entry] Name=Piper Comment=Configurable mouse configuration utility Exec=piper # Translators: Do NOT translate or transliterate this text (this is an icon file name)! Icon=org.freedesktop.Piper Type=Application StartupNotify=true Categories=GTK;GNOME;Utility; # Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! Keywords=gaming;configuration;mouse;mice; piper-0.4/data/org.freedesktop.Piper.svg000066400000000000000000000266741362011671000203210ustar00rootroot00000000000000 image/svg+xml piper-0.4/data/piper.gresource.xml.in000066400000000000000000000022751362011671000176530ustar00rootroot00000000000000 404.svg enter-keyboard-shortcut.svg led-off.svg svgs/svg-lookup.ini AboutDialog.ui ui/ButtonDialog.ui ui/ButtonRow.ui ui/DeviceRow.ui ui/ErrorPerspective.ui ui/MousePerspective.ui ui/LedDialog.ui ui/OptionButton.ui ui/ProfileRow.ui ui/ResolutionRow.ui ui/ResolutionsPage.ui ui/WelcomePerspective.ui ui/Window.ui @SVG_FILES@ piper-0.4/data/svg-lookup-ini-test.py000077500000000000000000000030261362011671000176200ustar00rootroot00000000000000#!/usr/bin/env python3 import argparse import configparser import unittest import sys from pathlib import Path path = None svgdir = None config = None class TestSVGLookup(unittest.TestCase): def test_has_sections(self): self.assertTrue(config.sections()) def test_required(self): for name in config.sections(): s = config[name] self.assertIn('DeviceMatch', s) self.assertIn('Svg', s) def test_svg_filename(self): svgs = [config[s]['Svg'] for s in config.sections()] for svg in svgs: self.assertTrue(Path(svgdir, svg).exists(), msg=svg) def test_uniq_match(self): matches = [config[s]['DeviceMatch'] for s in config.sections()] d = {} for match in matches: self.assertNotIn(match, d, msg='Duplicate match "{}"'.format(match)) d[match] = True def setUpModule(): global config, path config = configparser.ConfigParser(strict=True) config.optionxform = lambda option: option config.read(path) assert(config.sections()) def main(): global path, svgdir parser = argparse.ArgumentParser(description="Device data-file checker") parser.add_argument('file', nargs=1, help='Absolute path to svg-lookup.ini') parser.add_argument('svgdir', nargs=1, help='Directory containing svg files') args, remainder = parser.parse_known_args() path = args.file[0] svgdir = args.svgdir[0] unittest.main(argv=[sys.argv[0], *remainder]) if __name__ == "__main__": main() piper-0.4/data/svgs/000077500000000000000000000000001362011671000143645ustar00rootroot00000000000000piper-0.4/data/svgs/README.md000066400000000000000000000065731362011671000156560ustar00rootroot00000000000000libratbag gnome theme ==================== Requirements ------------ See the Logitech G403 for an example of a device with a small amount of buttons, and the Logitech G700 for an example of a device with a large amount of buttons. - Canvas size should be between 400x400 and 500x500 pixels. - Three layers in the final SVG: 1. a lower layer named "Device" with the device itself. Each button on the device should have an id `buttonX`, with `X` being the number of the button (so for button 0 the id would be `button0`). Similarly, each LED on the device should have an id `ledX`. 2. a middle layer named "Buttons" with the button leaders (see below for leaders). 3. an upper layer named "LEDs" with the LED leaders. - A leader line is a path that extends from the button or LED to the left or the right of the device (see below). Each leader line requires the following: - It should start with a 7x7 square placed on or close to the button or LED that it maps with. - From this square, a path should extend left or right (see below). - Each path should end with a 1x1 pixel with identifier `buttonX-leader` (or `ledX-leader`), where `X` is the number of the button (or LED) with which the leader maps. For button 0, this would be `button0-leader`. - All these elements should be grouped and given the identifier `buttonX-path` (or `ledX-path` for LEDs) - Leader lines should have a vertical spacing of at least 40 pixels. When there are several leader lines above and below each other, make the spacing between them equal. - If the device's scroll wheel supports horizontal tilting, add two small arrows left and right of the scroll wheel with the respective button identifiers (see the Logitech G700 for an example). Do not cut the scroll wheel in half vertically to map these buttons. - If there aren't too many buttons, preferably make the leaders point to the right with the device itself placed on the left. If the buttons would extend below or above the device, make some point to the left instead with the device itself centered in the middle. In this case, half of the leaders should extend to the left and the other half to the right. - When a leader points to the right, its 1x1 pixel should have a style property `text-align:start`. When a leader points to the left, its 1x1 pixel should have a style property `text-align:end`. - The canvas should be resized so that there is a 20px gap between the device and the edge of the canvas and no gap between the 1x1 pixels and the canvas. Please note that due to the way the SVG is drawn, you cannot rely on the z-ordering of elements to line up or cover elements (as noted in [this issue](https://github.com/libratbag/piper/issues/48), which includes links to examples). As such, please make sure that you align the elements appropriately; [this comment](https://github.com/libratbag/piper/issues/48#issuecomment-315979109) includes some helpful tips. Technique --------- The simplest approach is to find a photo of the device and import it into inkscape. Put it on the lowest layer, create a new layer "Device" above it and start tracing the outlines and edges of the device. Fill in the shapes and your device should resemble the underlying photo. Delete the photo layer, add leaders in their respective layers and you're done. Make sure the image looks ''toned-down'' and not realistic. Do not use dark or bright colors. piper-0.4/data/svgs/check-svg.py000077500000000000000000000075201362011671000166170ustar00rootroot00000000000000#!/usr/bin/env python3 # vim: set expandtab shiftwidth=4 tabstop=4: import os import sys from lxml import etree import logging ns = {'svg': 'http://www.w3.org/2000/svg'} style_query = '//svg:rect[@id=\"{}\"][contains(@style, \"{}\")]' logger = None class SVGLogger(logging.Logger): def __init__(self, name, level=logging.NOTSET): self.success = True return super().__init__(name, level) def error(self, msg, *args, **kwargs): self.success = False return super().error(msg, *args, **kwargs) @classmethod def get_logger(cls, path): logging.setLoggerClass(SVGLogger) logging.basicConfig(level=logging.DEBUG) return logging.getLogger(path) def check_size(root): width = float(root.attrib['width']) height = float(root.attrib['height']) if not 400 <= width <= 500: logger.error("Width is outside of range: {}".format(width)) if not 400 <= height <= 500: logger.error("Height is outside of range: {}".format(height)) def check_layers(root): """ Check there are layers (well, groups) for the components we require. """ layer_ids = [g.attrib['id'] for g in root.iterfind('svg:g', ns)] for layer in ["Device", "Buttons", "LEDs"]: if layer not in layer_ids: logger.error("Missing layer: {}".format(layer)) def check_elements(root, prefix, required=0): """ Checks for elements of the form 'prefixN' in the root tag. Any elements found must be consecutive or an warning is printed, i.e. if there's a 'button8' there has to be a 'button7'. If required is nonzero, an error is logged for any missing element with an index less than required. """ # elements can be paths and rects # This includes leaders and lines element_ids = [] for element in ['path', 'rect', 'g', 'circle']: element_ids += [p.attrib['id'] for p in root.xpath('//svg:{}'.format(element), namespaces=ns) if p.attrib['id'].startswith(prefix)] idx = 0 highest = -1 for idx in range(0, 20): e = '{}{}'.format(prefix, idx) previous = '{}{}'.format(prefix, idx - 1) leader = '{}{}-leader'.format(prefix, idx) path = '{}{}-path'.format(prefix, idx) if e in element_ids: highest = idx if idx > 0 and previous not in element_ids: logger.warning("Non-consecutive {}: {}".format(prefix, e)) if leader not in element_ids: logger.error("Missing {} for {}".format(leader, e)) else: element = root.xpath(style_query.format(leader, 'text-align'), namespaces=ns) if element is None or len(element) != 1 or element[0] is None: logger.error("Missing style property for {}".format(leader)) if path not in element_ids: logger.error("Missing {} for {}".format(path, e)) elif leader in element_ids: logger.error("Have {} but not {}".format(leader, e)) elif path in element_ids: logger.error("Have {} but not {}".format(path, e)) elif idx < required: logger.error("Missing {}: {}".format(prefix, e)) logger.info("Found {} {}s".format(highest + 1, prefix)) def check_leds(root): check_elements(root, "led") def check_buttons(root): check_elements(root, "button", 3) def check_svg(path): path = os.path.join(os.environ.get('BASEDIR', '.'), path) svg = etree.parse(path) root = svg.getroot() check_size(root) check_layers(root) check_buttons(root) check_leds(root) if __name__ == "__main__": success = True for path in sys.argv[1:]: logger = SVGLogger.get_logger(path) print("checking {}...".format(path)) check_svg(path) if not logger.success: success = False if not success: sys.exit(1) piper-0.4/data/svgs/fallback.svg000066400000000000000000000762631362011671000166620ustar00rootroot00000000000000 image/svg+xml 404 piper-0.4/data/svgs/logitech-g-pro-wireless.svg000066400000000000000000000614721362011671000215720ustar00rootroot00000000000000 image/svg+xml piper-0.4/data/svgs/logitech-g-pro.svg000066400000000000000000000545741362011671000177440ustar00rootroot00000000000000 image/svg+xml piper-0.4/data/svgs/logitech-g102-g203.svg000077700000000000000000000000001362011671000233572logitech-g-pro.svgustar00rootroot00000000000000piper-0.4/data/svgs/logitech-g300.svg000066400000000000000000000661121362011671000173600ustar00rootroot00000000000000 image/svg+xml piper-0.4/data/svgs/logitech-g303.svg000066400000000000000000000547021362011671000173650ustar00rootroot00000000000000 image/svg+xml piper-0.4/data/svgs/logitech-g402.svg000066400000000000000000000705021362011671000173610ustar00rootroot00000000000000 image/svg+xml piper-0.4/data/svgs/logitech-g403.svg000066400000000000000000000517421362011671000173670ustar00rootroot00000000000000 image/svg+xml piper-0.4/data/svgs/logitech-g500.svg000066400000000000000000000514341362011671000173630ustar00rootroot00000000000000 image/svg+xml piper-0.4/data/svgs/logitech-g500s.svg000077700000000000000000000000001362011671000225222logitech-g500.svgustar00rootroot00000000000000piper-0.4/data/svgs/logitech-g502.svg000066400000000000000000001030111362011671000173520ustar00rootroot00000000000000 image/svg+xml piper-0.4/data/svgs/logitech-g600.svg000066400000000000000000002762701362011671000173730ustar00rootroot00000000000000 image/svg+xml piper-0.4/data/svgs/logitech-g602.svg000066400000000000000000001166001362011671000173630ustar00rootroot00000000000000 image/svg+xml G7 G8 G9 G4 G5 G6 G10 G11 piper-0.4/data/svgs/logitech-g603.svg000066400000000000000000000436711362011671000173730ustar00rootroot00000000000000 image/svg+xml piper-0.4/data/svgs/logitech-g604.svg000066400000000000000000001222301362011671000173610ustar00rootroot00000000000000 image/svg+xml G9 G8 G7 G6 G5 G4 piper-0.4/data/svgs/logitech-g700.svg000066400000000000000000001116071362011671000173640ustar00rootroot00000000000000 image/svg+xml piper-0.4/data/svgs/logitech-g703.svg000077700000000000000000000000001362011671000223462logitech-g403.svgustar00rootroot00000000000000piper-0.4/data/svgs/logitech-g9.svg000066400000000000000000000642221362011671000172260ustar00rootroot00000000000000 image/svg+xml piper-0.4/data/svgs/logitech-g900.svg000066400000000000000000000674361362011671000174000ustar00rootroot00000000000000 image/svg+xml piper-0.4/data/svgs/logitech-mx-anywhere2.svg000066400000000000000000000442351362011671000212370ustar00rootroot00000000000000 image/svg+xml piper-0.4/data/svgs/logitech-mx-anywhere2s.svg000077700000000000000000000000001362011671000262502logitech-mx-anywhere2.svgustar00rootroot00000000000000piper-0.4/data/svgs/logitech-mx-ergo.svg000066400000000000000000000624521362011671000202700ustar00rootroot00000000000000 image/svg+xml piper-0.4/data/svgs/logitech-mx-master-2s.svg000066400000000000000000000513011362011671000211400ustar00rootroot00000000000000 image/svg+xml piper-0.4/data/svgs/logitech-mx-master-3.svg000077700000000000000000000000001362011671000255252logitech-mx-master-2s.svgustar00rootroot00000000000000piper-0.4/data/svgs/logitech-mx-master.svg000077700000000000000000000000001362011671000253652logitech-mx-master-2s.svgustar00rootroot00000000000000piper-0.4/data/svgs/roccat-kone-pure.svg000066400000000000000000000503161362011671000202700ustar00rootroot00000000000000 image/svg+xml piper-0.4/data/svgs/roccat-kone-xtd.svg000066400000000000000000000775311362011671000201240ustar00rootroot00000000000000 image/svg+xml piper-0.4/data/svgs/steelseries-kinzu-v2.svg000066400000000000000000000366471362011671000211370ustar00rootroot00000000000000 image/svg+xml piper-0.4/data/svgs/steelseries-kinzu-v3.svg000066400000000000000000000366271362011671000211360ustar00rootroot00000000000000 image/svg+xml piper-0.4/data/svgs/steelseries-rival.svg000066400000000000000000000544001362011671000205520ustar00rootroot00000000000000 image/svg+xml piper-0.4/data/svgs/steelseries-rival310.svg000066400000000000000000000564101362011671000210010ustar00rootroot00000000000000 image/svg+xml piper-0.4/data/svgs/steelseries-rival600.svg000066400000000000000000001026751362011671000210100ustar00rootroot00000000000000 image/svg+xml piper-0.4/data/svgs/steelseries-sensei310.svg000066400000000000000000000664361362011671000211630ustar00rootroot00000000000000 image/svg+xml piper-0.4/data/svgs/steelseries-senseiraw.svg000066400000000000000000000622221362011671000214360ustar00rootroot00000000000000 image/svg+xml piper-0.4/data/svgs/svg-lookup.ini000066400000000000000000000066341362011671000172040ustar00rootroot00000000000000[Etekcity Scroll Alpha] DeviceMatch=usb:1ea7:4011 Svg=fallback.svg [Logitech G102/G103] DeviceMatch=usb:046d:c084 Svg=logitech-g102-g203.svg [Logitech G300] DeviceMatch=usb:046d:c246 Svg=logitech-g300.svg [Logitech G302] DeviceMatch=usb:046d:c07f Svg=logitech-g303.svg [Logitech G303] DeviceMatch=usb:046d:c080 Svg=logitech-g303.svg [Logitech G305] DeviceMatch=usb:046d:4074 Svg=logitech-g-pro.svg [Logitech G402] DeviceMatch=usb:046d:c07e Svg=logitech-g402.svg [Logitech G403] DeviceMatch=usb:046d:c083 Svg=logitech-g403.svg [Logitech G403 Wireless] DeviceMatch=usb:046d:c082;usb:046d:405d Svg=logitech-g403.svg [Logitech G500] DeviceMatch=usb:046d:c068 Svg=logitech-g500.svg [Logitech G500s] DeviceMatch=usb:046d:c24e Svg=logitech-g500s.svg [Logitech G502 Hero Wireless] DeviceMatch=usb:046d:407f;usb:046d:c08d Svg=logitech-g502.svg [Logitech G502 Hero] DeviceMatch=usb:046d:c08b Svg=logitech-g502.svg [Logitech G502 Proteus Core] DeviceMatch=usb:046d:c07d Svg=logitech-g502.svg [Logitech G502 Proteus Spectrum] DeviceMatch=usb:046d:c332 Svg=logitech-g502.svg [Logitech G600] DeviceMatch=usb:046d:c24a Svg=logitech-g600.svg [Logitech G602] DeviceMatch=usb:046d:402c Svg=logitech-g602.svg [Logitech G603] DeviceMatch=bluetooth:046d:b01c;usb:046d:406c Svg=logitech-g603.svg [Logitech G604] DeviceMatch=usb:046d:4085;bluetooth:046d:b024 Svg=logitech-g604.svg [Logitech G700] DeviceMatch=usb:046d:c06b;usb:046d:c07c Svg=logitech-g700.svg [Logitech G700s] DeviceMatch=usb:046d:c531 Svg=logitech-g700.svg [Logitech G703] DeviceMatch=usb:046d:c087;usb:046d:4070 Svg=logitech-g703.svg [Logitech G703 Hero] DeviceMatch=usb:046d:c090;usb:046d:4086 Svg=logitech-g703.svg [Logitech G900] DeviceMatch=usb:046d:c081;usb:046d:4053 Svg=logitech-g900.svg [Logitech G903] DeviceMatch=usb:046d:c086;usb:046d:4067 Svg=logitech-g900.svg [Logitech G903 Hero] DeviceMatch=usb:046d:c091;usb:046d:4087 Svg=logitech-g900.svg [Logitech G9] DeviceMatch=usb:046d:c048 Svg=logitech-g9.svg [Logitech G9x] DeviceMatch=usb:046d:c066 Svg=logitech-g9.svg [Logitech G Pro] DeviceMatch=usb:046d:c085;usb:046d:c08c Svg=logitech-g-pro.svg [Logitech G Pro Wireless] DeviceMatch=usb:046d:c088;usb:046d:4079 Svg=logitech-g-pro-wireless.svg [Logitech MX Anywhere 2] DeviceMatch=usb:046d:404a;bluetooth:046d:b013;bluetooth:046d:b018 Svg=logitech-mx-anywhere2.svg [Logitech MX Anywhere 2S] DeviceMatch=bluetooth:046d:b01a;usb:046d:406a Svg=logitech-mx-anywhere2.svg [Logitech MX Ergo] DeviceMatch=bluetooth:046d:b01d;usb:046d:406f Svg=logitech-mx-ergo.svg [Logitech MX Master 2S] DeviceMatch=bluetooth:046d:b019;usb:046d:4069 Svg=logitech-mx-master-2s.svg [Logitech MX Master] DeviceMatch=usb:046d:4041;bluetooth:046d:b012;usb:046d:4060;bluetooth:046d:b017;usb:046d:4071 Svg=logitech-mx-master.svg [Roccat Kone Pure] DeviceMatch=usb:1e7d:2dc2 Svg=roccat-kone-pure.svg [Roccat Kone XTD] DeviceMatch=usb:1e7d:2e22 Svg=roccat-kone-xtd.svg [SteelSeries Kinzu V2] DeviceMatch=usb:1038:1378 Svg=steelseries-kinzu-v2.svg [SteelSeries Kinzu V3] DeviceMatch=usb:1038:1388 Svg=steelseries-kinzu-v3.svg [SteelSeries Rival 310] DeviceMatch=usb:1038:1720 Svg=steelseries-rival310.svg [SteelSeries Rival 600] DeviceMatch=usb:1038:1724 Svg=steelseries-rival600.svg [SteelSeries Rival] DeviceMatch=usb:1038:1384 Svg=steelseries-rival.svg [SteelSeries Sensei 310] DeviceMatch=usb:1038:1722 Svg=steelseries-sensei310.svg [SteelSeries Sensei Raw] DeviceMatch=usb:1038:1369 Svg=steelseries-senseiraw.svg piper-0.4/data/ui/000077500000000000000000000000001362011671000140175ustar00rootroot00000000000000piper-0.4/data/ui/AboutDialog.ui.in000066400000000000000000000026221362011671000171570ustar00rootroot00000000000000 False True normal Piper Version: @version@ https://github.com/libratbag/piper Visit Piper on GitHub translator-credits org.freedesktop.Piper gpl-2-0 False False False False 0 piper-0.4/data/ui/ButtonDialog.ui000066400000000000000000000526631362011671000167650ustar00rootroot00000000000000 True False True True 18 vertical 6 True False 72 edit-find-symbolic True True 0 True False No button mapping found False True 1 True False Try a different search False True 2 piper-0.4/data/ui/ButtonRow.ui000066400000000000000000000026341362011671000163260ustar00rootroot00000000000000 piper-0.4/data/ui/DeviceRow.ui000066400000000000000000000031671362011671000162540ustar00rootroot00000000000000 piper-0.4/data/ui/ErrorPerspective.ui000066400000000000000000000062051362011671000176640ustar00rootroot00000000000000 True False Uh Oh! Something went wrong… False Quit True True True piper-0.4/data/ui/LedDialog.ui000066400000000000000000000546201362011671000162110ustar00rootroot00000000000000 255 1 10 0 10000 100 500 piper-0.4/data/ui/MousePerspective.ui000066400000000000000000000207231362011671000176640ustar00rootroot00000000000000 True False True True True True Select another profile popover_profiles True False Profile 1 center False True False stack True True True Commit the changes to the device True False Apply end 1 False 2 True False vertical 6 True True in True True True False 180 True False Click to switch to another profile True True 0 Add profile True True True Add a new profile to the device False True 1 piper-0.4/data/ui/OptionButton.ui000066400000000000000000000046421362011671000170300ustar00rootroot00000000000000 piper-0.4/data/ui/ProfileRow.ui000066400000000000000000000043101362011671000164440ustar00rootroot00000000000000 piper-0.4/data/ui/ResolutionRow.ui000066400000000000000000000111051362011671000172070ustar00rootroot00000000000000 piper-0.4/data/ui/ResolutionsPage.ui000066400000000000000000000267121362011671000175110ustar00rootroot00000000000000 piper-0.4/data/ui/WelcomePerspective.ui000066400000000000000000000131401362011671000201620ustar00rootroot00000000000000 True False Select a Device False Quit True True True piper-0.4/data/ui/Window.ui000066400000000000000000000031631362011671000156300ustar00rootroot00000000000000 app.about _About Piper piper-0.4/meson.build000066400000000000000000000103721362011671000146360ustar00rootroot00000000000000project('piper', version: '0.4', license: 'GPLv2', meson_version: '>= 0.42.0') # The tag date of the project_version(), update when the version bumps. version_date='2020-02-09' # The DBus API version of ratbagd that we are compatible with. Anything # higher or lower will not be compatible so make sure you bumpt that to the # latest released ratbagd version whenever a piper release is made. ratbagd_api_version = 1 # Dependencies dependency('pygobject-3.0', required: true) ratbagd = find_program('ratbagd', required: false) ratbagd_required_version='0.13' if not ratbagd.found() message(''' *********************** WARNING ********************** * _???_ Dependency libratbag not found in $PATH! * *(_)_(_) ratbagd must be installed and running * * (o o) for Piper to work. * *==\o/== * ****************************************************** ''') else r = run_command(ratbagd, '--version') if r.returncode() != 0 or r.stdout().version_compare('<' + ratbagd_required_version) message(''' *********************** WARNING **************************** * ( )( * * (\-. ) ( ) ratbagd found in $PATH is too * * / _`> .---------. old and will not work with * * _) / _)= |'-------'| this version of Piper. Please * *( / _/ |O O o| update libratbag/ratbagd. * * `-.__(___)_ | o O . o | * ************************************************************ ''') endif endif # Gtk version required gtk_major_version = 3 gtk_minor_version = 22 prefix = get_option('prefix') datadir = join_paths(prefix, get_option('datadir')) localedir = join_paths(prefix, get_option('localedir')) pkgdatadir = join_paths(datadir, meson.project_name()) bindir = join_paths(prefix, get_option('bindir')) podir = join_paths(meson.source_root(), 'po') i18n = import('i18n') subdir('data') subdir('po') # Find the directory to install our Python code if meson.version().version_compare('<0.48.0') python = import('python3') py3 = python.find_python() python_dir = python.sysconfig_path('purelib') message('Getting Python3 install dir') if not python_dir.endswith('site-packages') error('Python3 purelib path seems invalid') endif else pymod = import('python') # external python modules that are required for running piper python_modules = [ 'lxml', 'evdev', 'cairo', 'gi', ] if meson.version().version_compare('>=0.51') py3 = pymod.find_installation(modules: python_modules) else py3 = pymod.find_installation() foreach module: python_modules if run_command(py3, '-c', 'import @0@'.format(module)).returncode() != 0 error('Failed to find required python module \'@0@\'.'.format(module)) endif endforeach endif python_dir = py3.get_install_dir() endif install_subdir('piper', install_dir: python_dir, exclude_directories: '__pycache__') config_piper = configuration_data() config_piper.set('pkgdatadir', pkgdatadir) config_piper.set('localedir', localedir) config_piper.set('gtk_major_version', gtk_major_version) config_piper.set('gtk_minor_version', gtk_minor_version) config_piper.set('RATBAGD_API_VERSION', ratbagd_api_version) config_piper.set('devel', '') config_piper_devel = config_piper config_piper_devel.set('pkgdatadir', join_paths(meson.build_root(), 'data')) config_piper_devel.set('localedir', join_paths(meson.build_root(), 'po')) config_piper_devel.set('devel', ''' sys.path.insert(1, '@0@') print('Running from source tree, using local files') '''.format(meson.source_root())) configure_file(input: 'piper.in', output: 'piper', configuration: config_piper, install_dir: bindir) configure_file(input: 'piper.in', output: 'piper.devel', configuration: config_piper_devel) meson.add_install_script('meson_install.sh') flake8 = find_program('flake8', required: false) if flake8.found() test('flake8', flake8, args: ['--ignore=E501,W504', join_paths(meson.source_root(), 'piper'), join_paths(meson.source_root(), 'piper.in')]) endif piper-0.4/meson_install.sh000066400000000000000000000003771362011671000157030ustar00rootroot00000000000000#!/usr/bin/env sh if [ -z $DESTDIR ]; then PREFIX=${MESON_INSTALL_PREFIX:-/usr} # Update icon cache gtk-update-icon-cache -f -t $PREFIX/share/icons/hicolor # Install new schemas #glib-compile-schemas $PREFIX/share/glib-2.0/schemas/ fi piper-0.4/org.freedesktop.Piper.json000066400000000000000000000041051362011671000175430ustar00rootroot00000000000000{ "app-id": "org.freedesktop.Piper", "runtime": "org.gnome.Platform", "runtime-version": "3.32", "sdk": "org.gnome.Sdk", "command": "piper", "finish-args": [ "--share=ipc", "--socket=x11", "--socket=wayland", "--filesystem=xdg-run/dconf", "--filesystem=~/.config/dconf:ro", "--talk-name=ca.desrt.dconf", "--env=DCONF_USER_CONFIG_DIR=.config/dconf", "--system-talk-name=org.freedesktop.ratbag1" ], "modules": [ { "name": "python-evdev", "buildsystem": "simple", "build-commands": [ "pip3 install --prefix=/app --no-deps ." ], "sources": [ { "type": "archive", "url": "https://files.pythonhosted.org/packages/7e/53/374b82dd2ccec240b7388c65075391147524255466651a14340615aabb5f/evdev-1.1.2.tar.gz", "sha256": "2dd67291be20e70643e8ef6f2381efc10e0c6e44a32abb3c1db74996ea3b0351" } ] }, { "name": "python-lxml", "buildsystem": "simple", "build-commands": [ "pip3 install --prefix=/app --no-deps ." ], "build-options": { "env": { "XSLT_CONFIG": "pkg-config libxslt" } }, "sources": [ { "type": "archive", "url": "https://files.pythonhosted.org/packages/65/6f/d070609b415f4a24bf36b7deb94bfd791e8d023c0dbf4233659af53fe0ab/lxml-4.3.2.tar.gz", "sha256": "3a9d8521c89bf6f2a929c3d12ad3ad7392c774c327ea809fd08a13be6b3bc05f" } ] }, { "name": "Piper", "buildsystem": "meson", "config-opts": [ "-Dignore-ratbagd-dependency=true" ], "sources": [ { "type": "git", "url": "https://github.com/libratbag/piper" } ] } ] } piper-0.4/piper.in000077500000000000000000000024161362011671000141460ustar00rootroot00000000000000#!/usr/bin/env python3 import gi import os import sys gi.require_version('Gio', '2.0') gi.require_version('Gtk', '3.0') from gi.repository import Gio, Gtk gtk_version = Gtk.get_major_version(), Gtk.get_minor_version() if gtk_version < (@gtk_major_version@, @gtk_minor_version@): print("Version of GTK is too old, @gtk_major_version@.@gtk_minor_version@ required", file=sys.stderr) sys.exit(1) @devel@ resource = Gio.resource_load(os.path.join('@pkgdatadir@', 'piper.gresource')) Gio.Resource._register(resource) def install_excepthook(): """Make sure we exit when an unhandled exception occurs.""" old_hook = sys.excepthook def new_hook(etype, evalue, etb): old_hook(etype, evalue, etb) while Gtk.main_level(): Gtk.main_quit() sys.exit() sys.excepthook = new_hook if __name__ == "__main__": import gettext import locale import signal from piper.application import Application install_excepthook() locale.bindtextdomain('piper', '@localedir@') locale.textdomain('piper') gettext.bindtextdomain('piper', '@localedir@') gettext.textdomain('piper') signal.signal(signal.SIGINT, signal.SIG_DFL) exit_status = Application(@RATBAGD_API_VERSION@).run(sys.argv) sys.exit(exit_status) piper-0.4/piper/000077500000000000000000000000001362011671000136105ustar00rootroot00000000000000piper-0.4/piper/__init__.py000066400000000000000000000000001362011671000157070ustar00rootroot00000000000000piper-0.4/piper/application.py000066400000000000000000000060631362011671000164720ustar00rootroot00000000000000# Copyright (C) 2017 Jente Hidskes # # 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 .ratbagd import Ratbagd from .window import Window import gi gi.require_version("Gio", "2.0") gi.require_version("Gtk", "3.0") from gi.repository import Gio, GLib, Gtk # noqa class Application(Gtk.Application): """A Gtk.Application subclass to handle the application's initialization and integration with the GNOME stack. It implements the do_startup and do_activate methods and is responsible for the application's menus, icons, title and lifetime.""" def __init__(self, ratbagd_api_version): """Instantiates a new Application.""" Gtk.Application.__init__(self, application_id="org.freedesktop.Piper", flags=Gio.ApplicationFlags.FLAGS_NONE) GLib.set_application_name("Piper") self._required_ratbagd_version = ratbagd_api_version def do_startup(self): """This function is called when the application is first started. All initialization should be done here, to prevent doing duplicate work in case another window is opened.""" Gtk.Application.do_startup(self) self._build_app_menu() self._ratbagd = None def init_ratbagd(self): if self._ratbagd is None: self._ratbag = Ratbagd(self._required_ratbagd_version) return self._ratbag def do_activate(self): """This function is called when the user requests a new window to be opened.""" window = Window(self.init_ratbagd, application=self) window.present() def _build_app_menu(self): # Set up the app menu actions = [("about", self._about), ("quit", self._quit)] for (name, callback) in actions: action = Gio.SimpleAction.new(name, None) action.connect("activate", callback) self.add_action(action) def _about(self, action, param): # Set up the about dialog. builder = Gtk.Builder().new_from_resource("/org/freedesktop/Piper/AboutDialog.ui") about = builder.get_object("about_dialog") about.set_transient_for(self.get_active_window()) about.connect('response', lambda about, param: about.destroy()) about.show() def _quit(self, action, param): # Quit the application. windows = self.get_windows() for window in windows: window.destroy() piper-0.4/piper/buttondialog.py000066400000000000000000000353641362011671000166700ustar00rootroot00000000000000# Copyright (C) 2017 Jente Hidskes # # 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 sys from gettext import gettext as _ from .gi_composites import GtkTemplate from .ratbagd import RatbagdButton, RatbagdMacro import gi gi.require_version("Gtk", "3.0") from gi.repository import Gdk, GObject, Gtk # noqa @GtkTemplate(ui="/org/freedesktop/Piper/ui/ButtonRow.ui") class ButtonRow(Gtk.ListBoxRow): """A Gtk.ListBoxRow subclass to implement the rows that show up in the ButtonDialog's Gtk.ListBox. It doesn't do much besides moving UI code into a .ui file.""" __gtype_name__ = "ButtonRow" description_label = GtkTemplate.Child() def __init__(self, description, section, action_type, value, *args, **kwargs): """Instantiates a new ButtonRow. @param description The text to display in the row, as str. @param section The section this row belongs to, as str. @param action_type The type of this row's mapping, as one of RatbagdButton.ActionType.*. @param value The value to set when this row is activated. The type needs to match the action_type, e.g. for it should be int for RatbagdButton.ActionType.BUTTON. """ Gtk.ListBoxRow.__init__(self, *args, **kwargs) self._section = section self._action_type = action_type self._value = value self.init_template() self.description_label.set_text(description) @GObject.Property def description(self): return self.description_label.get_text() @GtkTemplate(ui="/org/freedesktop/Piper/ui/ButtonDialog.ui") class ButtonDialog(Gtk.Dialog): """A Gtk.Dialog subclass to implement the dialog that shows the configuration options for button mappings.""" __gtype_name__ = "ButtonDialog" LEFT_HANDED_MODE = -1000 RIGHT_HANDED_MODE = -1001 # Gdk uses an offset of 8 from the keycodes defined in linux/input.h. _XORG_KEYCODE_OFFSET = 8 stack = GtkTemplate.Child() listbox = GtkTemplate.Child() label_keystroke = GtkTemplate.Child() label_preview = GtkTemplate.Child() row_keystroke = GtkTemplate.Child() row_keystroke_label = GtkTemplate.Child() radio_right_handed = GtkTemplate.Child() radio_left_handed = GtkTemplate.Child() empty_search_placeholder = GtkTemplate.Child() search_entry = GtkTemplate.Child() search_bar = GtkTemplate.Child() def __init__(self, ratbagd_button, buttons, *args, **kwargs): """Instantiates a new ButtonDialog. @param ratbagd_button The button to configure, as ratbagd.RatbagdButton. @param buttons The buttons on this device, as [ratbagd.RatbagdButton]. """ Gtk.Dialog.__init__(self, *args, **kwargs) self.init_template() self._grab_pointer = None self._current_macro = None self._button = ratbagd_button self._action_type = self._button.action_type if self._action_type == RatbagdButton.ActionType.BUTTON: self._mapping = self._button.mapping elif self._action_type == RatbagdButton.ActionType.MACRO: self._mapping = self._button.macro elif self._action_type == RatbagdButton.ActionType.SPECIAL: self._mapping = self._button.special else: self._mapping = -1 self._init_ui(buttons) def _init_ui(self, buttons): if self._button.index in [0, 1]: self._init_primary_buttons_ui() else: self._init_other_buttons_ui(buttons) def _init_primary_buttons_ui(self): # Shows the listbox to swap the primary buttons. self.stack.set_visible_child_name("handedness") # Left mouse button (index 0) is mapped to right mouse button, where # mappings are 1-indexed and thus right mouse click has value 2. # Or, right mouse button (index 1) is mapped to left mouse button, # which has value 1. if self._button.index == 0 and self._mapping == 2 or \ self._button.index == 1 and self._mapping == 1: self.radio_left_handed.set_active(True) else: self.radio_right_handed.set_active(True) def _init_other_buttons_ui(self, buttons): # Shows the listbox to map non-primary buttons. self.listbox.set_header_func(self._listbox_header_func) self.listbox.set_filter_func(self._listbox_filter_func) self.listbox.set_placeholder(self.empty_search_placeholder) self.search_entry.connect("notify::text", lambda o, p: self.listbox.invalidate_filter()) i = 0 for button in buttons: key, name = self._get_button_name_and_description(button) # Translators: section header for mapping one button's click to another. row = ButtonRow(name, _("Button mapping"), RatbagdButton.ActionType.BUTTON, button.index + 1) self.listbox.insert(row, i) if self._action_type == RatbagdButton.ActionType.BUTTON and button.index + 1 == self._button.mapping: self.listbox.select_row(row) i += 1 for key, name in RatbagdButton.SPECIAL_DESCRIPTION.items(): if name == "Unknown" or name == "Invalid": continue # Translators: section header for assigning special functions to buttons. row = ButtonRow(_(name), _("Special mapping"), RatbagdButton.ActionType.SPECIAL, key) self.listbox.insert(row, i) if self._action_type == RatbagdButton.ActionType.SPECIAL and key == self._mapping: self.listbox.select_row(row) i += 1 if self._action_type == RatbagdButton.ActionType.MACRO: self._create_current_macro(macro=self._mapping) else: self._create_current_macro() def _create_current_macro(self, macro=None): if macro is not None: self._current_macro = macro self._on_macro_updated(macro, None) else: self._current_macro = RatbagdMacro() self._current_macro.connect("macro-set", self._on_macro_set) self._current_macro.connect("notify::keys", self._on_macro_updated) def _listbox_header_func(self, row, before): # Adds headers to those rows where a new category starts, to separate # different kinds of mappings. if row == self.row_keystroke: separator = Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL, margin_top=18) row.set_header(separator) return if before is not None: add_header = (row._section != before._section) else: add_header = True if not add_header: row.set_header(None) return box = Gtk.Box.new(orientation=Gtk.Orientation.VERTICAL, spacing=6) if before is not None: box.set_margin_top(18) else: box.set_margin_top(6) markup = "{}".format(row._section) label = Gtk.Label(label=markup, use_markup=True, xalign=0.0, margin_start=6) label.get_style_context().add_class("dim-label") box.add(label) box.add(Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL)) row.set_header(box) box.show_all() def _listbox_filter_func(self, row): # Filters the list box with the text from the search entry. if self.search_entry.get_text_length() == 0: return True if row is not self.row_keystroke: description = row.description.casefold() else: description = self.row_keystroke_label.get_label().casefold() search = self.search_entry.get_text().casefold() for term in search.split(" "): if term not in description: return False return True def _get_button_name_and_description(self, button): # Translators: the {} will be replaced with the button index, e.g. # "Button 1 click". name = _("Button {} click").format(button.index) if button.index in RatbagdButton.BUTTON_DESCRIPTION: description = _(RatbagdButton.BUTTON_DESCRIPTION[button.index]) else: description = name return name, description def _grab_seat(self): # Grabs the keyboard seat. Returns True on success, False on failure. # Gratefully copied from GNOME Control Center's keyboard panel. window = self.get_window() if window is None: return False display = window.get_display() seats = display.list_seats() if len(seats) == 0: return False device = seats[0].get_keyboard() if device is None: return False if device.get_source == Gdk.InputSource.KEYBOARD: pointer = device.get_associated_device() if pointer is None: return False else: pointer = device status = pointer.get_seat().grab(window, Gdk.SeatCapabilities.KEYBOARD, False, None, None, None, None) if status != Gdk.GrabStatus.SUCCESS: return False self._grab_pointer = pointer self.grab_add() return True def _release_grab(self): # Releases a previously grabbed keyboard seat, if any. if self._grab_pointer is None: return self._grab_pointer.get_seat().ungrab() self._grab_pointer = None self.grab_remove() def do_key_press_event(self, event): # Overrides Gtk.Window's standard key press event callback, so we can # capture the pressed buttons in capture mode. If we're not in capture # mode, we trigger the search bar on any key press. if self.stack.get_visible_child_name() != "capture": if self.search_bar.handle_event(event) == Gdk.EVENT_STOP: return Gdk.EVENT_STOP return Gtk.Window.do_key_press_event(self, event) return self._do_key_event(event) def do_key_release_event(self, event): # Overrides Gtk.Window's standard key release event callback, so we can # capture the released buttons in capture mode. If we're not in capture # mode, we pass the event on to other widgets in the dialog. if self.stack.get_visible_child_name() != "capture": return Gtk.Window.do_key_release_event(self, event) return self._do_key_event(event) def _do_key_event(self, event): # Normalize tab. if event.keyval == Gdk.KEY_ISO_Left_Tab: event.keyval = Gdk.KEY_Tab # HACK: we don't want to use SysRq as a keybinding, but we do want # Al+Print, so we avoid translating Alt+Print to SysRq. if event.keyval == Gdk.KEY_Sys_Req and (event.state & Gdk.ModifierType.MOD1_MASK): event.keyval = Gdk.KEY_Print if event.type == Gdk.EventType.KEY_PRESS: type = RatbagdButton.Macro.KEY_PRESS # Return accepts the current keystroke. if event.keyval == Gdk.KEY_Return: self._current_macro.accept() return # Escape cancels the editing. elif event.keyval == Gdk.KEY_Escape: if self._action_type == RatbagdButton.ActionType.MACRO: self._create_current_macro(macro=self._mapping) else: self._create_current_macro() self.stack.set_visible_child_name("overview") return elif event.type == Gdk.EventType.KEY_RELEASE: type = RatbagdButton.Macro.KEY_RELEASE # TODO: this needs to be checked for its Wayland support. if not self._XORG_KEYCODE_OFFSET <= event.hardware_keycode <= 255: print("Keycode is not within the valid range.", file=sys.stderr) else: self._current_macro.append(type, event.hardware_keycode - self._XORG_KEYCODE_OFFSET) return Gdk.EVENT_STOP def _on_macro_updated(self, macro, pspec): current_macro = str(self._current_macro) self.label_keystroke.set_label(current_macro) self.label_preview.set_label(current_macro) def _on_macro_set(self, macro): # A macro has been set; update accordingly. self._action_type = RatbagdButton.ActionType.MACRO self._mapping = macro self.stack.set_visible_child_name("overview") self._release_grab() @GtkTemplate.Callback def _on_row_activated(self, listbox, row): if row == self.row_keystroke: if self._grab_seat() is not True: # TODO: display this somewhere in the UI instead. print("Unable to grab keyboard, can't set keystroke", file=sys.stderr) else: self._create_current_macro() self.stack.set_visible_child_name("capture") else: self._action_type = row._action_type self._mapping = row._value @GtkTemplate.Callback def _on_apply_button_clicked(self, button): if self.stack.get_visible_child_name() == "capture": self._current_macro.accept() return Gdk.EVENT_PROPAGATE @GtkTemplate.Callback def _on_primary_mode_toggled(self, toggle): if not toggle.get_active(): return self._action_type = RatbagdButton.ActionType.BUTTON if toggle is self.radio_left_handed: self._mapping = ButtonDialog.LEFT_HANDED_MODE elif toggle is self.radio_right_handed: self._mapping = ButtonDialog.RIGHT_HANDED_MODE @GObject.Property def action_type(self): """The action type as last set in the dialog, one of RatbagdButton.ActionType.*.""" return self._action_type @GObject.Property def mapping(self): """The mapping as last set in the dialog. Note that the type depends on action_type, and as such you should check that before using this property. A custom value of ButtonDialog.LEFT_HANDED_MODE means that the left- and right mouse buttons should be swapped, whereas ButtonDialog.RIGHT_HANDED_MODE means that the buttons should be as usual.""" return self._mapping piper-0.4/piper/buttonspage.py000066400000000000000000000164531362011671000165260ustar00rootroot00000000000000# Copyright (C) 2017 Jente Hidskes # # 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 gettext import gettext as _ from .buttondialog import ButtonDialog from .mousemap import MouseMap from .optionbutton import OptionButton from .ratbagd import RatbagdButton import gi gi.require_version("Gtk", "3.0") from gi.repository import Gtk # noqa class ButtonsPage(Gtk.Box): """The second stack page, exposing the button configuration.""" __gtype_name__ = "ButtonsPage" def __init__(self, ratbagd_device, *args, **kwargs): """Instantiates a new ButtonsPage. @param ratbag_device The ratbag device to configure, as ratbagd.RatbagdDevice """ Gtk.Box.__init__(self, *args, **kwargs) self._device = ratbagd_device self._device.connect("active-profile-changed", self._on_active_profile_changed) self._profile = None self._mousemap = MouseMap("#Buttons", self._device, spacing=20, border_width=20) self.pack_start(self._mousemap, True, True, 0) self._sizegroup = Gtk.SizeGroup(mode=Gtk.SizeGroupMode.HORIZONTAL) self._set_profile(self._device.active_profile) self.show_all() def _set_profile(self, profile): self._profile = profile for ratbagd_button in profile.buttons: button = OptionButton() # Set the correct label in the option button. self._on_button_mapping_changed(ratbagd_button, None, button) button.connect("clicked", self._on_button_clicked, ratbagd_button) ratbagd_button.connect("notify::mapping", self._on_button_mapping_changed, button) ratbagd_button.connect("notify::special", self._on_button_mapping_changed, button) ratbagd_button.connect("notify::macro", self._on_button_mapping_changed, button) ratbagd_button.connect("notify::action-type", self._on_button_mapping_changed, button) self._mousemap.add(button, "#button{}".format(ratbagd_button.index)) self._sizegroup.add_widget(button) def _on_active_profile_changed(self, device, profile): # Disconnect the notify::action_type signal on the old profile's buttons. for button in self._profile.buttons: button.disconnect_by_func(self._on_button_mapping_changed) # Clear the MouseMap of any children. self._mousemap.foreach(Gtk.Widget.destroy) # Repopulate the MouseMap. self._set_profile(profile) def _on_button_mapping_changed(self, ratbagd_button, pspec, optionbutton): # Called when the button's action type changed, which means its # corresponding optionbutton has to be updated. action_type = ratbagd_button.action_type if action_type == RatbagdButton.ActionType.BUTTON: if ratbagd_button.mapping - 1 in RatbagdButton.BUTTON_DESCRIPTION: label = _(RatbagdButton.BUTTON_DESCRIPTION[ratbagd_button.mapping - 1]) else: # Translators: the {} will be replaced with the button index, e.g. # "Button 1 click". label = _("Button {} click").format(ratbagd_button.mapping - 1) elif action_type == RatbagdButton.ActionType.SPECIAL: label = _(RatbagdButton.SPECIAL_DESCRIPTION[ratbagd_button.special]) elif action_type == RatbagdButton.ActionType.MACRO: label = _("Macro: {}").format(str(ratbagd_button.macro)) elif action_type == RatbagdButton.ActionType.NONE: # Translators: the button is turned disabled, e.g. off. label = _("Disabled") else: # Translators: the button has an unknown function. label = _("Unknown") optionbutton.set_label(label) def _on_button_clicked(self, button, ratbagd_button): # Presents the ButtonDialog to configure the mouse button corresponding # to the clicked button. buttons = self._find_active_profile().buttons dialog = ButtonDialog(ratbagd_button, buttons, title=_("Configure button {}").format(ratbagd_button.index), use_header_bar=True, transient_for=self.get_toplevel()) dialog.connect("response", self._on_dialog_response, ratbagd_button) dialog.present() def _on_dialog_response(self, dialog, response, ratbagd_button): # The user either pressed cancel or apply. If it's apply, apply the # changes before closing the dialog, otherwise just close the dialog. if response == Gtk.ResponseType.APPLY: if dialog.action_type == RatbagdButton.ActionType.BUTTON: if dialog.mapping in [ButtonDialog.LEFT_HANDED_MODE, ButtonDialog.RIGHT_HANDED_MODE]: left = self._find_button_type(0) right = self._find_button_type(1) if left is None or right is None: return # Mappings are 1-indexed, so 1 is left mouse click and 2 is # right mouse click. if dialog.mapping == ButtonDialog.LEFT_HANDED_MODE: left.mapping, right.mapping = 2, 1 elif dialog.mapping == ButtonDialog.RIGHT_HANDED_MODE: left.mapping, right.mapping = 1, 2 else: ratbagd_button.mapping = dialog.mapping elif dialog.action_type == RatbagdButton.ActionType.MACRO: ratbagd_button.macro = dialog.mapping elif dialog.action_type == RatbagdButton.ActionType.SPECIAL: ratbagd_button.special = dialog.mapping lower = RatbagdButton.ActionSpecial.PROFILE_CYCLE_UP upper = RatbagdButton.ActionSpecial.PROFILE_DOWN if lower <= dialog.mapping <= upper: index = ratbagd_button.index for profile in self._device.profiles: if profile is self._profile: continue profile.buttons[index].special = dialog.mapping dialog.destroy() def _find_active_profile(self): # Finds the active profile, which is guaranteed to exist. for profile in self._device.profiles: if profile.is_active: return profile def _find_button_type(self, button_type): for button in self._profile.buttons: if button.index == button_type: return button return None piper-0.4/piper/devicerow.py000066400000000000000000000043311362011671000161520ustar00rootroot00000000000000# Copyright (C) 2017 Jente Hidskes # # 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 piper.svg import get_svg import sys from .gi_composites import GtkTemplate import gi gi.require_version("Gtk", "3.0") from gi.repository import GdkPixbuf, GObject, Gtk, Rsvg # noqa @GtkTemplate(ui="/org/freedesktop/Piper/ui/DeviceRow.ui") class DeviceRow(Gtk.ListBoxRow): """A Gtk.ListBoxRow subclass to present devices in the welcome perspective.""" __gtype_name__ = "DeviceRow" title = GtkTemplate.Child() image = GtkTemplate.Child() def __init__(self, device, *args, **kwargs): Gtk.ListBoxRow.__init__(self, *args, **kwargs) self.init_template() self._device = device self.title.set_text(device.name) try: svg_bytes = get_svg(device.model) handle = Rsvg.Handle.new_from_data(svg_bytes) svg = handle.get_pixbuf_sub("#Device") handle.close() if svg is None: print("Device {}'s SVG is incompatible".format(device.name), file=sys.stderr) else: svg = svg.scale_simple(50, 50, GdkPixbuf.InterpType.BILINEAR) if svg is None: print("Cannot resize device SVG", file=sys.stderr) else: self.image.set_from_pixbuf(svg) except FileNotFoundError: print("Device {} has no image or its path is invalid".format(device.name), file=sys.stderr) self.show_all() @GObject.Property def device(self): return self._device piper-0.4/piper/errorperspective.py000066400000000000000000000051361362011671000175720ustar00rootroot00000000000000# Copyright (C) 2017 Jente Hidskes # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. from .gi_composites import GtkTemplate import gi gi.require_version("Gtk", "3.0") from gi.repository import GObject, Gtk # noqa @GtkTemplate(ui="/org/freedesktop/Piper/ui/ErrorPerspective.ui") class ErrorPerspective(Gtk.Box): """A perspective to present an error condition in a user-friendly manner.""" __gtype_name__ = "ErrorPerspective" label_error = GtkTemplate.Child() label_detail = GtkTemplate.Child() _titlebar = GtkTemplate.Child() def __init__(self, message=None, *args, **kwargs): """Instantiates a new ErrorPerspective. @param message The error message to display, as str. """ Gtk.Box.__init__(self, *args, **kwargs) self.init_template() if message is not None: self.set_message(message) @GObject.Property def name(self): """The name of this perspective.""" return "error_perspective" @GObject.Property def titlebar(self): """The titlebar to this perspective.""" return self._titlebar @GObject.Property def can_go_back(self): """Whether this perspective wants a back button to be displayed in case there is more than one connected device.""" return False @GObject.Property def can_shutdown(self): """Whether this perspective can safely shutdown.""" return True def set_message(self, message): """Sets the error message. @param message The error message to display, as str. """ self.label_error.set_label(message) def set_detail(self, detail): """Sets the detail message. @param message The detail message to display, as str. """ self.label_detail.set_label(detail) @GtkTemplate.Callback def _on_quit_button_clicked(self, button): window = button.get_toplevel() window.destroy() piper-0.4/piper/gi_composites.py000066400000000000000000000216021362011671000170270ustar00rootroot00000000000000# # Copyright (C) 2015 Dustin Spicuzza # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library 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 # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 # USA from os.path import abspath, join import inspect import warnings from gi.repository import Gio, GLib, GObject, Gtk # noqa __all__ = ['GtkTemplate'] class GtkTemplateWarning(UserWarning): pass def _connect_func(builder, obj, signal_name, handler_name, connect_object, flags, cls): '''Handles GtkBuilder signal connect events''' if connect_object is None: extra = () else: extra = (connect_object,) # The handler name refers to an attribute on the template instance, # so ask GtkBuilder for the template instance template_inst = builder.get_object(cls.__gtype_name__) if template_inst is None: # This should never happen errmsg = "Internal error: cannot find template instance! obj: %s; " \ "signal: %s; handler: %s; connect_obj: %s; class: %s" % \ (obj, signal_name, handler_name, connect_object, cls) warnings.warn(errmsg, GtkTemplateWarning) return handler = getattr(template_inst, handler_name) if flags == GObject.ConnectFlags.AFTER: obj.connect_after(signal_name, handler, *extra) else: obj.connect(signal_name, handler, *extra) template_inst.__connected_template_signals__.add(handler_name) def _register_template(cls, template_bytes): '''Registers the template for the widget and hooks init_template''' # This implementation won't work if there are nested templates, but # we can't do that anyways due to PyGObject limitations so it's ok if not hasattr(cls, 'set_template'): raise TypeError("Requires PyGObject 3.13.2 or greater") cls.set_template(template_bytes) bound_methods = set() bound_widgets = set() # Walk the class, find marked callbacks and child attributes for name in dir(cls): o = getattr(cls, name, None) if inspect.ismethod(o): if hasattr(o, '_gtk_callback'): bound_methods.add(name) # Don't need to call this, as connect_func always gets called # cls.bind_template_callback_full(name, o) elif isinstance(o, _Child): cls.bind_template_child_full(name, True, 0) bound_widgets.add(name) # Have to setup a special connect function to connect at template init # because the methods are not bound yet cls.set_connect_func(_connect_func, cls) cls.__gtemplate_methods__ = bound_methods cls.__gtemplate_widgets__ = bound_widgets base_init_template = cls.init_template cls.init_template = lambda s: _init_template(s, cls, base_init_template) def _init_template(self, cls, base_init_template): '''This would be better as an override for Gtk.Widget''' # TODO: could disallow using a metaclass.. but this is good enough # .. if you disagree, feel free to fix it and issue a PR :) if self.__class__ is not cls: raise TypeError("Inheritance from classes with @GtkTemplate decorators " "is not allowed at this time") connected_signals = set() self.__connected_template_signals__ = connected_signals base_init_template(self) for name in self.__gtemplate_widgets__: widget = self.get_template_child(cls, name) self.__dict__[name] = widget if widget is None: # Bug: if you bind a template child, and one of them was # not present, then the whole template is broken (and # it's not currently possible for us to know which # one is broken either -- but the stderr should show # something useful with a Gtk-CRITICAL message) raise AttributeError("A missing child widget was set using " "GtkTemplate.Child and the entire " "template is now broken (widgets: %s)" % ', '.join(self.__gtemplate_widgets__)) for name in self.__gtemplate_methods__.difference(connected_signals): errmsg = ("Signal '%s' was declared with @GtkTemplate.Callback " "but was not present in template") % name warnings.warn(errmsg, GtkTemplateWarning) # TODO: Make it easier for IDE to introspect this class _Child(object): ''' Assign this to an attribute in your class definition and it will be replaced with a widget defined in the UI file when init_template is called ''' __slots__ = [] @staticmethod def widgets(count): ''' Allows declaring multiple widgets with less typing:: button \ label1 \ label2 = GtkTemplate.Child.widgets(3) ''' return [_Child() for _ in range(count)] class _GtkTemplate(object): ''' Use this class decorator to signify that a class is a composite widget which will receive widgets and connect to signals as defined in a UI template. You must call init_template to cause the widgets/signals to be initialized from the template:: @GtkTemplate(ui='foo.ui') class Foo(Gtk.Box): def __init__(self): super(Foo, self).__init__() self.init_template() The 'ui' parameter can either be a file path or a GResource resource path:: @GtkTemplate(ui='/org/example/foo.ui') class Foo(Gtk.Box): pass To connect a signal to a method on your instance, do:: @GtkTemplate.Callback def on_thing_happened(self, widget): pass To create a child attribute that is retrieved from your template, add this to your class definition:: @GtkTemplate(ui='foo.ui') class Foo(Gtk.Box): widget = GtkTemplate.Child() Note: This is implemented as a class decorator, but if it were included with PyGI I suspect it might be better to do this in the GObject metaclass (or similar) so that init_template can be called automatically instead of forcing the user to do it. .. note:: Due to limitations in PyGObject, you may not inherit from python objects that use the GtkTemplate decorator. ''' __ui_path__ = None @staticmethod def Callback(f): ''' Decorator that designates a method to be attached to a signal from the template ''' f._gtk_callback = True return f Child = _Child @staticmethod def set_ui_path(*path): ''' If using file paths instead of resources, call this *before* loading anything that uses GtkTemplate, or it will fail to load your template file :param path: one or more path elements, will be joined together to create the final path TODO: Alternatively, could wait until first class instantiation before registering templates? Would need a metaclass... ''' _GtkTemplate.__ui_path__ = abspath(join(*path)) def __init__(self, ui): self.ui = ui def __call__(self, cls): if not issubclass(cls, Gtk.Widget): raise TypeError("Can only use @GtkTemplate on Widgets") # Nested templates don't work if hasattr(cls, '__gtemplate_methods__'): raise TypeError("Cannot nest template classes") # Load the template either from a resource path or a file # - Prefer the resource path first try: template_bytes = Gio.resources_lookup_data(self.ui, Gio.ResourceLookupFlags.NONE) except GLib.GError: ui = self.ui if isinstance(ui, (list, tuple)): ui = join(ui) if _GtkTemplate.__ui_path__ is not None: ui = join(_GtkTemplate.__ui_path__, ui) with open(ui, 'rb') as fp: template_bytes = GLib.Bytes.new(fp.read()) _register_template(cls, template_bytes) return cls # Future shim support if this makes it into PyGI? # if hasattr(Gtk, 'GtkTemplate'): # GtkTemplate = lambda c: c # else: GtkTemplate = _GtkTemplate piper-0.4/piper/leddialog.py000066400000000000000000000075511362011671000161160ustar00rootroot00000000000000# Copyright (C) 2017 Jente Hidskes # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. from .gi_composites import GtkTemplate from .ratbagd import RatbagdLed import gi gi.require_version("Gtk", "3.0") from gi.repository import Gdk, GObject, Gtk # noqa @GtkTemplate(ui="/org/freedesktop/Piper/ui/LedDialog.ui") class LedDialog(Gtk.Dialog): """A Gtk.Dialog subclass to implement the dialog that shows the configuration options for the LED effects.""" __gtype_name__ = "LedDialog" stack = GtkTemplate.Child() colorchooser = GtkTemplate.Child() colorbutton = GtkTemplate.Child() adjustment_brightness = GtkTemplate.Child() adjustment_effect_duration = GtkTemplate.Child() led_off_image = GtkTemplate.Child() def __init__(self, ratbagd_led, *args, **kwargs): """Instantiates a new LedDialog. @param ratbagd_led The LED to configure, as ratbagd.RatbagdLed. """ Gtk.Dialog.__init__(self, *args, **kwargs) self.init_template() self._led = ratbagd_led self._modes = { "solid": RatbagdLed.Mode.ON, "cycle": RatbagdLed.Mode.CYCLE, "breathing": RatbagdLed.Mode.BREATHING, "off": RatbagdLed.Mode.OFF } mode = self._led.mode for k, v in self._modes.items(): if mode == v: self.stack.set_visible_child_name(k) if v not in self._led.modes: self.stack.get_child_by_name(k).set_visible(False) rgba = self._get_led_color_as_rgba() self.colorchooser.set_rgba(rgba) self.colorbutton.set_rgba(rgba) self.adjustment_brightness.set_value(self._led.brightness) self.adjustment_effect_duration.set_value(self._led.effect_duration) sp = Gtk.CssProvider() sp.load_from_data("* { background: #565854}".encode()) Gtk.StyleContext.add_provider(self.led_off_image.get_style_context(), sp, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) @GtkTemplate.Callback def _on_change_value(self, scale, scroll, value): # Round the value resulting from a scroll event to the nearest multiple # of 500. This is to work around the Gtk.Scale not snapping to its # Gtk.Adjustment's step_increment. scale.set_value(int(value - (value % 500))) return True def _get_led_color_as_rgba(self): # Helper function to convert ratbagd's 0-255 color range to a Gdk.RGBA # with a 0.0-1.0 color range. r, g, b = self._led.color return Gdk.RGBA(r / 255.0, g / 255.0, b / 255.0, 1.0) @GObject.Property def mode(self): visible_child = self.stack.get_visible_child_name() return self._modes[visible_child] @GObject.Property def color(self): if self.mode == RatbagdLed.Mode.ON: rgba = self.colorchooser.get_rgba() else: rgba = self.colorbutton.get_rgba() return (rgba.red * 255.0, rgba.green * 255.0, rgba.blue * 255.0) @GObject.Property def brightness(self): return self.adjustment_brightness.get_value() @GObject.Property def effect_duration(self): return self.adjustment_effect_duration.get_value() piper-0.4/piper/ledspage.py000066400000000000000000000071201362011671000157460ustar00rootroot00000000000000# Copyright (C) 2017 Jente Hidskes # # 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 gettext import gettext as _ from .leddialog import LedDialog from .mousemap import MouseMap from .optionbutton import OptionButton from .ratbagd import RatbagdLed import gi gi.require_version("Gtk", "3.0") from gi.repository import Gtk # noqa class LedsPage(Gtk.Box): """The third stack page, exposing the LED configuration.""" __gtype_name__ = "LedsPage" def __init__(self, ratbagd_device, *args, **kwargs): """Instantiates a new LedsPage. @param ratbag_device The ratbag device to configure, as ratbagd.RatbagdDevice """ Gtk.Box.__init__(self, *args, **kwargs) self._device = ratbagd_device self._device.connect("active-profile-changed", self._on_active_profile_changed) self._profile = None self._mousemap = MouseMap("#Leds", self._device, spacing=20, border_width=20) self.pack_start(self._mousemap, True, True, 0) self._sizegroup = Gtk.SizeGroup(mode=Gtk.SizeGroupMode.HORIZONTAL) self._set_profile(self._device.active_profile) self.show_all() def _set_profile(self, profile): self._profile = profile for led in profile.leds: mode = _(RatbagdLed.LED_DESCRIPTION[led.mode]) button = OptionButton(mode) button.connect("clicked", self._on_button_clicked, led) led.connect("notify::mode", self._on_led_mode_changed, button) self._mousemap.add(button, "#led{}".format(led.index)) self._sizegroup.add_widget(button) def _on_active_profile_changed(self, device, profile): # Disconnect the notify::mode signal on the old profile's LEDs. for led in self._profile.leds: led.disconnect_by_func(self._on_led_mode_changed) # Clear the MouseMap of any children. self._mousemap.foreach(Gtk.Widget.destroy) # Repopulate the MouseMap. self._set_profile(profile) def _on_led_mode_changed(self, led, pspec, button): mode = _(RatbagdLed.LED_DESCRIPTION[led.mode]) button.set_label(mode) def _on_button_clicked(self, button, led): # Presents the LedDialog to configure the LED corresponding to the # clicked button. dialog = LedDialog(led, transient_for=self.get_toplevel()) dialog.connect("response", self._on_dialog_response, led) dialog.present() def _on_dialog_response(self, dialog, response, led): # The user either pressed cancel or apply. If it's apply, apply the # changes before closing the dialog, otherwise just close the dialog. if response == Gtk.ResponseType.APPLY: led.mode = dialog.mode led.color = dialog.color led.brightness = dialog.brightness led.effect_duration = dialog.effect_duration dialog.destroy() piper-0.4/piper/mousemap.py000066400000000000000000000425521362011671000160200ustar00rootroot00000000000000# Copyright (C) 2017 Jente Hidskes # # 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 cairo import gi import sys from lxml import etree from piper.svg import get_svg gi.require_version("Gdk", "3.0") gi.require_version("Gtk", "3.0") gi.require_version("Rsvg", "2.0") from gi.repository import Gdk, GLib, Gtk, GObject, Rsvg # noqa """This module contains the MouseMap widget (and its helper class _MouseMapChild), which is central to the button and LED configuration stack pages. The MouseMap widget draws the device SVG in the center and lays out a bunch of child widgets relative to the leaders in the device SVG.""" class _MouseMapChild: # A helper class to manage children and their properties. def __init__(self, widget, is_left, svg_id): self._widget = widget self._is_left = is_left self._svg_id = svg_id self._svg_leader = svg_id + "-leader" self._svg_path = svg_id + "-path" @property def widget(self): # The widget belonging to this child. return self._widget @property def svg_id(self): # The identifier of the SVG element with which this child's widget is # paired. return self._svg_id @property def svg_leader(self): # The identifier of the leader SVG element with which this child's # widget is paired. return self._svg_leader @property def svg_path(self): # The identifier of the SVG element's path with which this child's # widget is paired. return self._svg_path @property def is_left(self): # True iff this child's widget is allocated to the left of the SVG. return self._is_left class MouseMap(Gtk.Container): """A Gtk.Container subclass to draw a device SVG with child widgets that map to the SVG. The SVG should have objects with identifiers, whose value should also be given to the `add` method. See `do_size_allocate` and https://github.com/libratbag/libratbag/blob/master/data/README.md for more information. """ __gtype_name__ = "MouseMap" __gproperties__ = { "spacing": (int, "spacing", "The amount of space between children and the SVG leaders", 0, GLib.MAXINT, 0, GObject.ParamFlags.READABLE), } def __init__(self, layer, ratbagd_device, spacing=10, *args, **kwargs): """Instantiates a new MouseMap. @param layer The SVG layer whose leaders to draw, according to librsvg so e.g. `#layer1`. @param ratbagd_device The device that should be mapped, as ratbagd.RatbagdDevice @param spacing The spacing to place between the SVG's leaders and the widgets, as int @raises ValueError when an argument is invalid, or when the given device has no image registered. @raises GLib.Error when the SVG cannot be loaded. """ if layer is None: raise ValueError("Layer cannot be None") if ratbagd_device is None: raise ValueError("Device cannot be None") try: svg_bytes = get_svg(ratbagd_device.model) self._handle = Rsvg.Handle.new_from_data(svg_bytes) self._svg_data = etree.fromstring(svg_bytes) except FileNotFoundError: raise ValueError("Device has no image or its path is invalid") Gtk.Container.__init__(self, *args, **kwargs) self.set_has_window(False) self.spacing = spacing self._layer = layer self._device = ratbagd_device self._children = [] self._highlight_element = None # TODO: remove this when we're out of the transition to toned down SVGs device = self._handle.has_sub("#Device") buttons = self._handle.has_sub("#Buttons") leds = self._handle.has_sub("#LEDs") if not device or not buttons or not leds: print("Device SVG is incompatible", file=sys.stderr) def add(self, widget, svg_id): """Adds the given widget to the map, bound to the given SVG element identifier. If the element identifier or its leader is not found in the SVG, the widget is not added. @param widget The widget to add, as Gtk.Widget @param svg_id The identifier of the SVG element with which this widget is to be paired, as str """ svg_leader = svg_id + "-leader" if widget is None or svg_id is None or not \ self._handle.has_sub(svg_id) or not \ self._handle.has_sub(svg_leader): return is_left = self._xpath_has_style(svg_leader[1:], "text-align:end") child = _MouseMapChild(widget, is_left, svg_id) self._children.append(child) widget.connect("enter-notify-event", self._on_enter, child) widget.connect("leave-notify-event", self._on_leave) widget.set_parent(self) def do_remove(self, widget): """Removes the given widget from the map. @param widget The widget to remove, as Gtk.Widget """ if widget is not None: for child in self._children: if child.widget == widget: self._children.remove(child) child.widget.unparent() break def do_child_type(self): """Indicates that this container accepts any GTK+ widget.""" return Gtk.Widget.get_type() def do_forall(self, include_internals, callback, *parameters): """Invokes the given callback on each child, with the given parameters. @param include_internals Whether to run on internal children as well, as boolean. Ignored, as there are no internal children. @param callback The callback to call on each child, as Gtk.Callback @param parameters The parameters to pass to the callback, as object or None. """ try: if callback is not None: for child in self._children: callback(child.widget, *parameters) except AttributeError: # See https://bugzilla.gnome.org/show_bug.cgi?id=722562. pass def do_get_request_mode(self): """Gets whether the container prefers a height-for-width or a width-for-height layout. We don't want to trade width for height or height for width so we return CONSTANT_SIZE.""" return Gtk.SizeRequestMode.CONSTANT_SIZE def do_get_preferred_height(self): """Calculates the container's initial minimum and natural height. While this call is specific to width-for-height requests (that we requested not to get) we cannot be certain that our wishes are granted and hence we must implement this method as well. We just return the SVG's height plus the border widths. """ # TODO: account for children sticking out under or above the SVG, if # they exist. At the moment we cannot reliably do so because the # y-coordinates of the leaders can have any arbitrary value. For now, # we assume that the SVG is high enough to fit all children and do not # worry about setups beyond the default GNOME Adwaita. height = self._handle.props.height + 2 * self.props.border_width return height, height def do_get_preferred_width(self): """Calculates the container's initial minimum and natural width. While this call is specific to height-for-width requests (that we requested not to get) we cannot be certain that our wishes are granted and hence we must implement this method as well. We return the sum of the SVG's width, the natural child widths (left and right), spacing and border width. """ width = 2 * self.props.border_width width_svg = self._handle.props.width width_left = [child.widget.get_preferred_width()[1] for child in self._children if child.is_left] width_right = [child.widget.get_preferred_width()[1] for child in self._children if not child.is_left] width_left = max(width_left, default=0) width_right = max(width_right, default=0) width += width_left + width_svg + width_right + self.spacing if width_left > 0: width += self.spacing return width, width def do_get_preferred_height_for_width(self, width): """Returns this container's minimum and natural height if it would be given the specified width. While this call is specific to height-for-width requests (that we requested not to get) we cannot be certain that our wishes are granted and hence we must implement this method as well. Since we really want to be the same size always, we simply return do_get_preferred_height. @param width The given width, as int. Ignored. """ return self.do_get_preferred_height() def do_get_preferred_width_for_height(self, height): """Returns this container's minimum and natural width if it would be given the specified height. While this call is specific to width-for-height requests (that we requested not to get) we cannot be certain that our wishes are granted and hence we must implement this method as well. Since we really want to be the same size always, we simply return do_get_preferred_width. @param height The given height, as int. Ignored. """ return self.do_get_preferred_width() def do_size_allocate(self, allocation): """Assigns a size and position to the child widgets. Children may adjust the given allocation in their adjust_size_allocation virtual method implementation. @param allocation The position and size allocated to this container, as Gdk.Rectangle """ self.set_allocation(allocation) x, y = self._translate_to_origin() child_allocation = Gdk.Rectangle() for child in self._children: if not child.widget.get_visible(): continue svg_geom = self._get_svg_sub_geometry(child.svg_leader)[1] nat_size = child.widget.get_preferred_size()[1] if child.is_left: child_allocation.x = x + svg_geom.x - self.spacing - nat_size.width else: child_allocation.x = x + svg_geom.x + self.spacing child_allocation.y = y + svg_geom.y + 0.5 * svg_geom.height - 0.5 * nat_size.height child_allocation.width = nat_size.width child_allocation.height = nat_size.height if not child.widget.get_has_window(): child_allocation.x += allocation.x child_allocation.y += allocation.y child.widget.size_allocate(child_allocation) def do_draw(self, cr): """Draws the container to the given Cairo context. The top left corner of the widget will be drawn to the currently set origin point of the context. The container needs to propagate the draw signal to its children. @param cr The Cairo context to draw into, as cairo.Context """ scale_factor = self.get_scale_factor() target = cr.get_target() target.set_device_scale(scale_factor, scale_factor) cr.save() x, y = self._translate_to_origin() cr.translate(x, y) self._draw_device(cr) cr.restore() for child in self._children: self.propagate_draw(child.widget, cr) def do_get_property(self, prop): """Gets a property value. @param prop The property to get, as GObject.ParamSpec """ if prop.name == "spacing": return self.spacing else: raise AttributeError("Unknown property %s" % prop.name) def _on_enter(self, widget, event, child): # Highlights the element in the SVG to which the given widget belongs. self._highlight_element = child.svg_id self._redraw_svg_element(child.svg_id) def _on_leave(self, widget, event): # Restores the device SVG to its original state. old_highlight = self._highlight_element if old_highlight is None: return self._highlight_element = None self._redraw_svg_element(old_highlight) def _xpath_has_style(self, svg_id, style): # Checks if the SVG element with the given identifier has the given # style attribute set. namespaces = { 'sodipodi': 'http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd', 'cc': 'http://web.resource.org/cc/', 'svg': 'http://www.w3.org/2000/svg', 'dc': 'http://purl.org/dc/elements/1.1/', 'xlink': 'http://www.w3.org/1999/xlink', 'rdf': 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', 'inkscape': 'http://www.inkscape.org/namespaces/inkscape' } query = "//svg:rect[@id=\"{}\"][contains(@style, \"{}\")]".format(svg_id, style) element = self._svg_data.xpath(query, namespaces=namespaces) return element is not None and len(element) == 1 and element[0] is not None def _get_svg_sub_geometry(self, svg_id): # Helper method to get an SVG element's x- and y-coordinates, width and # height. ret = Gdk.Rectangle() ok, svg_pos = self._handle.get_position_sub(svg_id) if not ok: print("Warning: cannot retrieve element's position:", svg_id, file=sys.stderr) return ok, ret ret.x = svg_pos.x ret.y = svg_pos.y ok, svg_dim = self._handle.get_dimensions_sub(svg_id) if not ok: print("Warning: cannot retrieve element's dimensions:", svg_id, file=sys.stderr) return ok, ret ret.width = svg_dim.width ret.height = svg_dim.height return ok, ret def _redraw_svg_element(self, svg_id): # Helper method to redraw an element of the SVG image. Attempts to # redraw only the element (plus an offset), but will fall back to # redrawing the complete SVG. x, y = self._translate_to_origin() ok, svg_geom = self._get_svg_sub_geometry(svg_id) if not ok: svg_width = self._handle.props.width svg_height = self._handle.props.height self.queue_draw_area(x, y, svg_width, svg_height) else: self.queue_draw_area(x + svg_geom.x - 10, y + svg_geom.y - 10, svg_geom.width + 20, svg_geom.height + 20) def _translate_to_origin(self): # Translates the coordinate system such that the SVG and its buttons # will be drawn in the center of the allocated space. The returned x- # and y-coordinates will be the top left corner of the centered SVG. allocation = self.get_allocation() width = self.get_preferred_width()[1] height = self.get_preferred_height()[1] width_left = [child.widget.get_preferred_width()[1] for child in self._children if child.is_left] width_left = max(width_left, default=0) if width_left > 0: width_left += self.spacing x = (allocation.width - width) / 2 + self.props.border_width + width_left y = (allocation.height - height) / 2 + self.props.border_width return round(x), round(y) def _draw_device(self, cr): # Draws the SVG into the Cairo context. If there is an element to be # highlighted, it will do as such in a separate surface which will be # used as a mask over the device surface. style_context = self.get_style_context() style_context.save() color = style_context.get_color(Gtk.StateFlags.LINK) style_context.restore() cr.set_source_rgba(color.red, color.green, color.blue, 0.5) self._handle.render_cairo_sub(cr, id="#Device") if self._highlight_element is not None: svg_surface = cr.get_target() highlight_surface = svg_surface.create_similar(cairo.CONTENT_COLOR_ALPHA, self._handle.props.width, self._handle.props.height) highlight_context = cairo.Context(highlight_surface) self._handle.render_cairo_sub(highlight_context, self._highlight_element) cr.mask_surface(highlight_surface, 0, 0) for child in self._children: self._handle.render_cairo_sub(cr, id=child.svg_path) self._handle.render_cairo_sub(cr, id=child.svg_leader) piper-0.4/piper/mouseperspective.py000066400000000000000000000147031362011671000175710ustar00rootroot00000000000000# Copyright (C) 2017 Jente Hidskes # # 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 gettext import gettext as _ from .buttonspage import ButtonsPage from .gi_composites import GtkTemplate from .profilerow import ProfileRow from .resolutionspage import ResolutionsPage from .ledspage import LedsPage import gi gi.require_version("Gtk", "3.0") from gi.repository import GLib, GObject, Gtk # noqa @GtkTemplate(ui="/org/freedesktop/Piper/ui/MousePerspective.ui") class MousePerspective(Gtk.Overlay): """The perspective to configure a mouse.""" __gtype_name__ = "MousePerspective" _titlebar = GtkTemplate.Child() stack = GtkTemplate.Child() notification_error = GtkTemplate.Child() listbox_profiles = GtkTemplate.Child() label_profile = GtkTemplate.Child() add_profile_button = GtkTemplate.Child() button_commit = GtkTemplate.Child() button_profile = GtkTemplate.Child() def __init__(self, *args, **kwargs): """Instantiates a new MousePerspective.""" Gtk.Overlay.__init__(self, *args, **kwargs) self.init_template() self._device = None self._notification_error_timeout_id = 0 @GObject.Property def name(self): """The name of this perspective.""" return "mouse_perspective" @GObject.Property def titlebar(self): """The titlebar to this perspective.""" return self._titlebar @GObject.Property def can_go_back(self): """Whether this perspective wants a back button to be displayed in case there is more than one connected device.""" return True @GObject.Property def can_shutdown(self): if self._device is None: return True """Whether this perspective can safely shutdown.""" for profile in self._device.profiles: if profile.dirty: return False return True @GObject.Property def device(self): return self._device def set_device(self, device): self._device = device device.connect("resync", lambda _: self._show_notification_error()) self.stack.foreach(Gtk.Widget.destroy) active_profile = device.active_profile if active_profile.resolutions: self.stack.add_titled(ResolutionsPage(device), "resolutions", _("Resolutions")) if active_profile.buttons: self.stack.add_titled(ButtonsPage(device), "buttons", _("Buttons")) if active_profile.leds: self.stack.add_titled(LedsPage(device), "leds", _("LEDs")) self.button_profile.set_visible(len(device.profiles) > 1) name = active_profile.name if not name: name = 'Profile {}'.format(active_profile.index) self.label_profile.set_label(name) self._on_profile_notify_dirty(active_profile, None) # Find the first profile that is enabled. If there is none, disable the # add button. left = next((p for p in device.profiles if not p.enabled), None) self.add_profile_button.set_visible(left is not None) self.listbox_profiles.foreach(Gtk.Widget.destroy) for profile in device.profiles: profile.connect("notify::enabled", self._on_profile_notify_enabled) profile.connect("notify::dirty", self._on_profile_notify_dirty) row = ProfileRow(profile) self.listbox_profiles.insert(row, profile.index) if profile is active_profile: self.listbox_profiles.select_row(row) def _hide_notification_error(self): if self._notification_error_timeout_id != 0: GLib.Source.remove(self._notification_error_timeout_id) self._notification_error_timeout_id = 0 self.notification_error.set_reveal_child(False) def _show_notification_error(self): self.notification_error.set_reveal_child(True) self._notification_error_timeout_id = GLib.timeout_add_seconds(5, self._on_notification_error_timeout) def _on_notification_error_timeout(self): self._hide_notification_error() return False @GtkTemplate.Callback def _on_save_button_clicked(self, button): self._device.commit() @GtkTemplate.Callback def _on_notification_error_close_clicked(self, button): self._hide_notification_error() @GtkTemplate.Callback def _on_profile_row_activated(self, listbox, row): row.set_active() self.label_profile.set_label(row.name) @GtkTemplate.Callback def _on_add_profile_button_clicked(self, button): # Enable the first disabled profile we find. for profile in self._device.profiles: if profile.enabled: continue profile.enabled = True if profile == self._device.profiles[-1]: self.add_profile_button.set_sensitive(False) break def _on_profile_notify_enabled(self, profile, pspec): # We're only interested in the case where the last profile is disabled, # so that we can reset the sensitivity of the add button. if not profile.enabled and profile == self._device.profiles[-1]: self.add_profile_button.set_sensitive(True) def _on_profile_notify_dirty(self, profile, pspec): style_context = self.button_commit.get_style_context() if profile.dirty: style_context.add_class("suggested-action") self.button_commit.set_sensitive(True) else: # There is no way to make a single profile non-dirty, so this works # for now. Ideally, this should however check if there are any other # profiles on the device that are dirty. style_context.remove_class("suggested-action") self.button_commit.set_sensitive(False) piper-0.4/piper/optionbutton.py000066400000000000000000000030741362011671000167320ustar00rootroot00000000000000# Copyright (C) 2017 Jente Hidskes # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. from .gi_composites import GtkTemplate import gi gi.require_version("Gtk", "3.0") from gi.repository import Gtk # noqa @GtkTemplate(ui="/org/freedesktop/Piper/ui/OptionButton.ui") class OptionButton(Gtk.Button): """A Gtk.Button subclass that displays a label, a separator and a cog.""" __gtype_name__ = "OptionButton" label = GtkTemplate.Child() def __init__(self, label=None, *args, **kwargs): """Instantiates a new OptionButton. @param label The text to display. """ Gtk.Button.__init__(self, *args, **kwargs) self.init_template() if label is not None: self.set_label(label) def set_label(self, label): """Set the text to display. @param label The new text to display, as str. """ self.label.set_text(label) piper-0.4/piper/profilerow.py000066400000000000000000000037601362011671000163600ustar00rootroot00000000000000# Copyright (C) 2017 Jente Hidskes # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. from .gi_composites import GtkTemplate import gi gi.require_version("Gtk", "3.0") from gi.repository import GObject, Gtk # noqa @GtkTemplate(ui="/org/freedesktop/Piper/ui/ProfileRow.ui") class ProfileRow(Gtk.ListBoxRow): """A Gtk.ListBoxRow subclass containing the widgets to display a profile in the profile poper.""" __gtype_name__ = "ProfileRow" title = GtkTemplate.Child() def __init__(self, profile, *args, **kwargs): Gtk.ListBoxRow.__init__(self, *args, **kwargs) self.init_template() self._profile = profile self._profile.connect("notify::enabled", self._on_profile_notify_enabled) name = profile.name if not name: name = 'Profile {}'.format(profile.index) self.title.set_text(name) self.show_all() self.set_visible(profile.enabled) def _on_profile_notify_enabled(self, profile, pspec): self.set_visible(profile.enabled) @GtkTemplate.Callback def _on_delete_button_clicked(self, button): self._profile.enabled = False def set_active(self): """Activates the profile paired with this row.""" self._profile.set_active() @GObject.Property def name(self): return self.title.get_text() piper-0.4/piper/ratbagd.py000066400000000000000000001027701362011671000155750ustar00rootroot00000000000000# vim: set expandtab shiftwidth=4 tabstop=4: # # Copyright 2016 Red Hat, Inc. # # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), # to deal in the Software without restriction, including without limitation # the rights to use, copy, modify, merge, publish, distribute, sublicense, # and/or sell copies of the Software, and to permit persons to whom the # Software is furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice (including the next # paragraph) shall be included in all copies or substantial portions of the # Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. import os import sys import hashlib from enum import IntEnum from evdev import ecodes from gettext import gettext as _ from gi.repository import Gio, GLib, GObject # noqa # Deferred translations, see https://docs.python.org/3/library/gettext.html#deferred-translations def N_(x): return x class RatbagErrorCode(IntEnum): SUCCESS = 0 """An error occured on the device. Either the device is not a libratbag device or communication with the device failed.""" DEVICE = -1000 """Insufficient capabilities. This error occurs when a requested change is beyond the device's capabilities.""" CAPABILITY = -1001 """Invalid value or value range. The provided value or value range is outside of the legal or supported range.""" VALUE = -1002 """A low-level system error has occured, e.g. a failure to access files that should be there. This error is usually unrecoverable and libratbag will print a log message with details about the error.""" SYSTEM = -1003 """Implementation bug, either in libratbag or in the caller. This error is usually unrecoverable and libratbag will print a log message with details about the error.""" IMPLEMENTATION = -1004 class RatbagdIncompatible(Exception): """ratbagd is incompatible with this client""" def __init__(self, ratbagd_version, required_version): super().__init__() self.ratbagd_version = ratbagd_version self.required_version = required_version self.message = "ratbagd API version is {} but we require {}".format(ratbagd_version, required_version) def __str__(self): return self.message class RatbagdUnavailable(Exception): """Signals DBus is unavailable or the ratbagd daemon is not available.""" pass class RatbagdDBusTimeout(Exception): """Signals that a timeout occurred during a DBus method call.""" pass class RatbagError(Exception): """A common base exception to catch any ratbag exception.""" pass class RatbagErrorDevice(RatbagError): """An exception corresponding to RatbagErrorCode.DEVICE.""" pass class RatbagErrorCapability(RatbagError): """An exception corresponding to RatbagErrorCode.CAPABILITY.""" pass class RatbagErrorValue(RatbagError): """An exception corresponding to RatbagErrorCode.VALUE.""" pass class RatbagErrorSystem(RatbagError): """An exception corresponding to RatbagErrorCode.SYSTEM.""" pass class RatbagErrorImplementation(RatbagError): """An exception corresponding to RatbagErrorCode.IMPLEMENTATION.""" pass """A table mapping RatbagErrorCode values to RatbagError* exceptions.""" EXCEPTION_TABLE = { RatbagErrorCode.DEVICE: RatbagErrorDevice, RatbagErrorCode.CAPABILITY: RatbagErrorCapability, RatbagErrorCode.VALUE: RatbagErrorValue, RatbagErrorCode.SYSTEM: RatbagErrorSystem, RatbagErrorCode.IMPLEMENTATION: RatbagErrorImplementation } class _RatbagdDBus(GObject.GObject): _dbus = None def __init__(self, interface, object_path): super().__init__() if _RatbagdDBus._dbus is None: try: _RatbagdDBus._dbus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None) except GLib.Error as e: raise RatbagdUnavailable(e.message) ratbag1 = "org.freedesktop.ratbag1" if os.environ.get('RATBAG_TEST'): ratbag1 = "org.freedesktop.ratbag_devel1" if object_path is None: object_path = "/" + ratbag1.replace('.', '/') self._object_path = object_path self._interface = "{}.{}".format(ratbag1, interface) try: self._proxy = Gio.DBusProxy.new_sync(_RatbagdDBus._dbus, Gio.DBusProxyFlags.NONE, None, ratbag1, object_path, self._interface, None) except GLib.Error as e: raise RatbagdUnavailable(e.message) if self._proxy.get_name_owner() is None: raise RatbagdUnavailable("No one currently owns {}".format(ratbag1)) self._proxy.connect("g-properties-changed", self._on_properties_changed) self._proxy.connect("g-signal", self._on_signal_received) def _on_properties_changed(self, proxy, changed_props, invalidated_props): # Implement this in derived classes to respond to property changes. pass def _on_signal_received(self, proxy, sender_name, signal_name, parameters): # Implement this in derived classes to respond to signals. pass def _find_object_with_path(self, iterable, object_path): # Find the index of an object in an iterable that whose object path # matches the given object path. for index, obj in enumerate(iterable): if obj._object_path == object_path: return index return -1 def _get_dbus_property(self, property): # Retrieves a cached property from the bus, or None. p = self._proxy.get_cached_property(property) if p is not None: return p.unpack() return p def _set_dbus_property(self, property, type, value, readwrite=True): # Sets a cached property on the bus. # Take our real value and wrap it into a variant. To call # org.freedesktop.DBus.Properties.Set we need to wrap that again # into a (ssv), where v is our value's variant. # args to .Set are "interface name", "function name", value-variant val = GLib.Variant("{}".format(type), value) if readwrite: pval = GLib.Variant("(ssv)".format(type), (self._interface, property, val)) self._proxy.call_sync("org.freedesktop.DBus.Properties.Set", pval, Gio.DBusCallFlags.NO_AUTO_START, 2000, None) # This is our local copy, so we don't have to wait for the async # update self._proxy.set_cached_property(property, val) def _dbus_call(self, method, type, *value): # Calls a method synchronously on the bus, using the given method name, # type signature and values. # # If the result is valid, it is returned. Invalid results raise the # appropriate RatbagError* or RatbagdDBus* exception, or GLib.Error if # it is an unexpected exception that probably shouldn't be passed up to # the UI. val = GLib.Variant("({})".format(type), value) try: res = self._proxy.call_sync(method, val, Gio.DBusCallFlags.NO_AUTO_START, 2000, None) if res in EXCEPTION_TABLE: raise EXCEPTION_TABLE[res] return res.unpack()[0] # Result is always a tuple except GLib.Error as e: if e.code == Gio.IOErrorEnum.TIMED_OUT: raise RatbagdDBusTimeout(e.message) else: # Unrecognized error code; print the message to stderr and raise # the GLib.Error. print(e.message, file=sys.stderr) raise def __eq__(self, other): return other and self._object_path == other._object_path class Ratbagd(_RatbagdDBus): """The ratbagd top-level object. Provides a list of devices available through ratbagd; actual interaction with the devices is via the RatbagdDevice, RatbagdProfile, RatbagdResolution and RatbagdButton objects. Throws RatbagdUnavailable when the DBus service is not available. """ __gsignals__ = { "device-added": (GObject.SignalFlags.RUN_FIRST, None, (GObject.TYPE_PYOBJECT,)), "device-removed": (GObject.SignalFlags.RUN_FIRST, None, (GObject.TYPE_PYOBJECT,)), "daemon-disappeared": (GObject.SignalFlags.RUN_FIRST, None, ()), } def __init__(self, api_version): super().__init__("Manager", None) result = self._get_dbus_property("Devices") if result is None and not self._proxy.get_cached_property_names(): raise RatbagdUnavailable("Make sure it is running and your user is in the required groups.") if self.api_version != api_version: raise RatbagdIncompatible(self.api_version or -1, api_version) self._devices = [RatbagdDevice(objpath) for objpath in result or []] self._proxy.connect("notify::g-name-owner", self._on_name_owner_changed) def _on_name_owner_changed(self, *kwargs): self.emit("daemon-disappeared") def _on_properties_changed(self, proxy, changed_props, invalidated_props): if "Devices" in changed_props.keys(): object_paths = [d._object_path for d in self._devices] for object_path in changed_props["Devices"]: if object_path not in object_paths: device = RatbagdDevice(object_path) self._devices.append(device) self.emit("device-added", device) for device in self.devices: if device._object_path not in changed_props["Devices"]: self._devices.remove(device) self.emit("device-removed", device) self.notify("devices") @GObject.Property def api_version(self): return self._get_dbus_property("APIVersion") @GObject.Property def devices(self): """A list of RatbagdDevice objects supported by ratbagd.""" return self._devices def __getitem__(self, id): """Returns the requested device, or None.""" for d in self.devices: if d.id == id: return d return None def __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): pass class RatbagdDevice(_RatbagdDBus): """Represents a ratbagd device.""" __gsignals__ = { "active-profile-changed": (GObject.SignalFlags.RUN_FIRST, None, (GObject.TYPE_PYOBJECT,)), "resync": (GObject.SignalFlags.RUN_FIRST, None, ()), } def __init__(self, object_path): super().__init__("Device", object_path) # FIXME: if we start adding and removing objects from this list, # things will break! result = self._get_dbus_property("Profiles") or [] self._profiles = [RatbagdProfile(objpath) for objpath in result] for profile in self._profiles: profile.connect("notify::is-active", self._on_active_profile_changed) # Use a SHA1 of our object path as our device's ID self._id = hashlib.sha1(object_path.encode('utf-8')).hexdigest() def _on_signal_received(self, proxy, sender_name, signal_name, parameters): if signal_name == "Resync": self.emit("resync") def _on_active_profile_changed(self, profile, pspec): if profile.is_active: self.emit("active-profile-changed", self._profiles[profile.index]) @GObject.Property def id(self): return self._id @id.setter def id(self, id): self._id = id @GObject.Property def model(self): """The unique identifier for this device model.""" return self._get_dbus_property("Model") @GObject.Property def name(self): """The device name, usually provided by the kernel.""" return self._get_dbus_property("Name") @GObject.Property def profiles(self): """A list of RatbagdProfile objects provided by this device.""" return self._profiles @GObject.Property def active_profile(self): """The currently active profile. This is a non-DBus property computed over the cached list of profiles. In the unlikely case that your device driver is misconfigured and there is no active profile, this returns the first profile.""" for profile in self._profiles: if profile.is_active: return profile print("No active profile. Please report this bug to the libratbag developers", file=sys.stderr) return self._profiles[0] def commit(self): """Commits all changes made to the device. This is implemented asynchronously inside ratbagd. Hence, we just call this method and always succeed. Any failure is handled inside ratbagd by emitting the Resync signal, which automatically resynchronizes the device. No further interaction is required by the client. """ self._dbus_call("Commit", "") for profile in self._profiles: if profile.dirty: profile._dirty = False profile.notify("dirty") class RatbagdProfile(_RatbagdDBus): """Represents a ratbagd profile.""" CAP_WRITABLE_NAME = 100 CAP_SET_DEFAULT = 101 CAP_DISABLE = 102 CAP_WRITE_ONLY = 103 def __init__(self, object_path): super().__init__("Profile", object_path) self._dirty = False self._active = self._get_dbus_property("IsActive") # FIXME: if we start adding and removing objects from any of these # lists, things will break! result = self._get_dbus_property("Resolutions") or [] self._resolutions = [RatbagdResolution(objpath) for objpath in result] self._subscribe_dirty(self._resolutions) result = self._get_dbus_property("Buttons") or [] self._buttons = [RatbagdButton(objpath) for objpath in result] self._subscribe_dirty(self._buttons) result = self._get_dbus_property("Leds") or [] self._leds = [RatbagdLed(objpath) for objpath in result] self._subscribe_dirty(self._leds) def _subscribe_dirty(self, objects): for obj in objects: obj.connect("notify", self._on_obj_notify) def _on_obj_notify(self, obj, pspec): if not self._dirty: self._dirty = True self.notify("dirty") def _on_properties_changed(self, proxy, changed_props, invalidated_props): if "IsActive" in changed_props.keys(): active = changed_props["IsActive"] if active != self._active: self._active = active self.notify("is-active") self._on_obj_notify(None, None) @GObject.Property def capabilities(self): """The capabilities of this profile as an array. Capabilities not present on the profile are not in the list. Thus use e.g. if RatbagdProfile.CAP_WRITABLE_NAME is in profile.capabilities: do something """ return self._get_dbus_property("Capabilities") or [] @GObject.Property def name(self): """The name of the profile""" return self._get_dbus_property("Name") @name.setter def name(self, name): """Set the name of this profile. @param name The new name, as str""" self._set_dbus_property("Name", "s", name) @GObject.Property def index(self): """The index of this profile.""" return self._get_dbus_property("Index") @GObject.Property def dirty(self): """Whether this profile is dirty.""" return self._dirty @GObject.Property def enabled(self): """tells if the profile is enabled.""" return self._get_dbus_property("Enabled") @enabled.setter def enabled(self, enabled): """Enable/Disable this profile. @param enabled The new state, as boolean""" self._set_dbus_property("Enabled", "b", enabled) @GObject.Property def report_rate(self): """The report rate in Hz.""" return self._get_dbus_property("ReportRate") @report_rate.setter def report_rate(self, rate): """Set the report rate in Hz. @param rate The new report rate, as int """ self._set_dbus_property("ReportRate", "u", rate) @GObject.Property def report_rates(self): """The list of supported report rates""" return self._get_dbus_property("ReportRates") or [] @GObject.Property def resolutions(self): """A list of RatbagdResolution objects with this profile's resolutions. Note that the list of resolutions differs between profiles but the number of resolutions is identical across profiles.""" return self._resolutions @GObject.Property def active_resolution(self): """The currently active resolution of this profile. This is a non-DBus property computed over the cached list of resolutions. In the unlikely case that your device driver is misconfigured and there is no active resolution, this returns the first resolution.""" for resolution in self._resolutions: if resolution.is_active: return resolution print("No active resolution. Please report this bug to the libratbag developers", file=sys.stderr) return self._resolutions[0] @GObject.Property def buttons(self): """A list of RatbagdButton objects with this profile's button mappings. Note that the list of buttons differs between profiles but the number of buttons is identical across profiles.""" return self._buttons @GObject.Property def leds(self): """A list of RatbagdLed objects with this profile's leds. Note that the list of leds differs between profiles but the number of leds is identical across profiles.""" return self._leds @GObject.Property def is_active(self): """Returns True if the profile is currently active, false otherwise.""" return self._active def set_active(self): """Set this profile to be the active profile.""" ret = self._dbus_call("SetActive", "") self._set_dbus_property("IsActive", "b", True, readwrite=False) return ret class RatbagdResolution(_RatbagdDBus): """Represents a ratbagd resolution.""" def __init__(self, object_path): super().__init__("Resolution", object_path) self._active = self._get_dbus_property("IsActive") self._default = self._get_dbus_property("IsDefault") def _on_properties_changed(self, proxy, changed_props, invalidated_props): if "IsActive" in changed_props.keys(): active = changed_props["IsActive"] if active != self._active: self._active = active self.notify("is-active") elif "IsDefault" in changed_props.keys(): default = changed_props["IsDefault"] if default != self._default: self._default = default self.notify("is-default") @GObject.Property def index(self): """The index of this resolution.""" return self._get_dbus_property("Index") @GObject.Property def resolution(self): """The resolution in DPI, either as single value tuple ``(res, )`` or as tuple ``(xres, yres)``. """ res = self._get_dbus_property("Resolution") if isinstance(res, int): res = tuple([res]) return res @resolution.setter def resolution(self, resolution): """Set the x- and y-resolution using the given (xres, yres) tuple. @param res The new resolution, as (int, int) """ res = self.resolution if len(res) != len(resolution) or len(res) > 2: raise ValueError('invalid resolution precision') if len(res) == 1: variant = GLib.Variant('u', resolution[0]) else: variant = GLib.Variant('(uu)', resolution) self._set_dbus_property("Resolution", "v", variant) @GObject.Property def resolutions(self): """The list of supported DPI values""" return self._get_dbus_property("Resolutions") or [] @GObject.Property def is_active(self): """True if this is the currently active resolution, False otherwise""" return self._active @GObject.Property def is_default(self): """True if this is the currently default resolution, False otherwise""" return self._default def set_default(self): """Set this resolution to be the default.""" ret = self._dbus_call("SetDefault", "") self._set_dbus_property("IsDefault", "b", True, readwrite=False) return ret def set_active(self): """Set this resolution to be the active one.""" ret = self._dbus_call("SetActive", "") self._set_dbus_property("IsActive", "b", True, readwrite=False) return ret class RatbagdButton(_RatbagdDBus): """Represents a ratbagd button.""" class ActionType(IntEnum): NONE = 0 BUTTON = 1 SPECIAL = 2 MACRO = 4 class ActionSpecial(IntEnum): INVALID = -1 UNKNOWN = (1 << 30) DOUBLECLICK = (1 << 30) + 1 WHEEL_LEFT = (1 << 30) + 2 WHEEL_RIGHT = (1 << 30) + 3 WHEEL_UP = (1 << 30) + 4 WHEEL_DOWN = (1 << 30) + 5 RATCHET_MODE_SWITCH = (1 << 30) + 6 RESOLUTION_CYCLE_UP = (1 << 30) + 7 RESOLUTION_CYCLE_DOWN = (1 << 30) + 8 RESOLUTION_UP = (1 << 30) + 9 RESOLUTION_DOWN = (1 << 30) + 10 RESOLUTION_ALTERNATE = (1 << 30) + 11 RESOLUTION_DEFAULT = (1 << 30) + 12 PROFILE_CYCLE_UP = (1 << 30) + 13 PROFILE_CYCLE_DOWN = (1 << 30) + 14 PROFILE_UP = (1 << 30) + 15 PROFILE_DOWN = (1 << 30) + 16 SECOND_MODE = (1 << 30) + 17 BATTERY_LEVEL = (1 << 30) + 18 class Macro(IntEnum): NONE = 0 KEY_PRESS = 1 KEY_RELEASE = 2 WAIT = 3 """A table mapping a button's index to its usual function as defined by X and the common desktop environments.""" BUTTON_DESCRIPTION = { 0: N_("Left mouse button click"), 1: N_("Right mouse button click"), 2: N_("Middle mouse button click"), 3: N_("Backward"), 4: N_("Forward"), } """A table mapping a special function to its human-readable description.""" SPECIAL_DESCRIPTION = { ActionSpecial.INVALID: N_("Invalid"), ActionSpecial.UNKNOWN: N_("Unknown"), ActionSpecial.DOUBLECLICK: N_("Doubleclick"), ActionSpecial.WHEEL_LEFT: N_("Wheel Left"), ActionSpecial.WHEEL_RIGHT: N_("Wheel Right"), ActionSpecial.WHEEL_UP: N_("Wheel Up"), ActionSpecial.WHEEL_DOWN: N_("Wheel Down"), ActionSpecial.RATCHET_MODE_SWITCH: N_("Ratchet Mode"), ActionSpecial.RESOLUTION_CYCLE_UP: N_("Cycle Resolution Up"), ActionSpecial.RESOLUTION_CYCLE_DOWN: N_("Cycle Resolution Down"), ActionSpecial.RESOLUTION_UP: N_("Resolution Up"), ActionSpecial.RESOLUTION_DOWN: N_("Resolution Down"), ActionSpecial.RESOLUTION_ALTERNATE: N_("Resolution Switch"), ActionSpecial.RESOLUTION_DEFAULT: N_("Default Resolution"), ActionSpecial.PROFILE_CYCLE_UP: N_("Cycle Profile Up"), ActionSpecial.PROFILE_CYCLE_DOWN: N_("Cycle Profile Down"), ActionSpecial.PROFILE_UP: N_("Profile Up"), ActionSpecial.PROFILE_DOWN: N_("Profile Down"), ActionSpecial.SECOND_MODE: N_("Second Mode"), ActionSpecial.BATTERY_LEVEL: N_("Battery Level"), } def __init__(self, object_path): super().__init__("Button", object_path) def _on_properties_changed(self, proxy, changed_props, invalidated_props): if "Mapping" in changed_props.keys(): self.notify("action-type") def _mapping(self): return self._get_dbus_property("Mapping") @GObject.Property def index(self): """The index of this button.""" return self._get_dbus_property("Index") @GObject.Property def mapping(self): """An integer of the current button mapping, if mapping to a button or None otherwise.""" type, button = self._mapping() if type != RatbagdButton.ActionType.BUTTON: return None return button @mapping.setter def mapping(self, button): """Set the button mapping to the given button. @param button The button to map to, as int """ button = GLib.Variant("u", button) self._set_dbus_property("Mapping", "(uv)", (RatbagdButton.ActionType.BUTTON, button)) @GObject.Property def macro(self): """A RatbagdMacro object representing the currently set macro or None otherwise.""" type, macro = self._mapping() if type != RatbagdButton.ActionType.MACRO: return None return RatbagdMacro.from_ratbag(macro) @macro.setter def macro(self, macro): """Set the macro to the macro represented by the given RatbagdMacro object. @param macro A RatbagdMacro object representing the macro to apply to the button, as RatbagdMacro. """ macro = GLib.Variant("a(uu)", macro.keys) self._set_dbus_property("Mapping", "(uv)", (RatbagdButton.ActionType.MACRO, macro)) @GObject.Property def special(self): """An enum describing the current special mapping, if mapped to special or None otherwise.""" type, special = self._mapping() if type != RatbagdButton.ActionType.SPECIAL: return None return special @special.setter def special(self, special): """Set the button mapping to the given special entry. @param special The special entry, as one of RatbagdButton.ActionSpecial """ special = GLib.Variant("u", special) self._set_dbus_property("Mapping", "(uv)", (RatbagdButton.ActionType.SPECIAL, special)) @GObject.Property def action_type(self): """An enum describing the action type of the button. One of ActionType.NONE, ActionType.BUTTON, ActionType.SPECIAL, ActionType.MACRO. This decides which *Mapping property has a value. """ type, mapping = self._mapping() return type @GObject.Property def action_types(self): """An array of possible values for ActionType.""" return self._get_dbus_property("ActionTypes") def disable(self): """Disables this button.""" return self._dbus_call("Disable", "") class RatbagdMacro(GObject.Object): """Represents a button macro. Note that it uses keycodes as defined by linux/input.h and not those used by X.Org or any other higher layer such as Gdk.""" # All keys from ecodes.KEY have a KEY_ prefix. We strip it. _PREFIX_LEN = len("KEY_") # Both a key press and release. _MACRO_KEY = 1000 _MACRO_DESCRIPTION = { RatbagdButton.Macro.KEY_PRESS: lambda key: "↓{}".format(ecodes.KEY[key][RatbagdMacro._PREFIX_LEN:]), RatbagdButton.Macro.KEY_RELEASE: lambda key: "↑{}".format(ecodes.KEY[key][RatbagdMacro._PREFIX_LEN:]), RatbagdButton.Macro.WAIT: lambda val: "{}ms".format(val), _MACRO_KEY: lambda key: "↕{}".format(ecodes.KEY[key][RatbagdMacro._PREFIX_LEN:]), } __gsignals__ = { 'macro-set': (GObject.SignalFlags.RUN_FIRST, None, ()), } def __init__(self, **kwargs): super().__init__(**kwargs) self._macro = [] def __str__(self): if not self._macro: # Translators: this is used when there is no macro to preview. return _("None") keys = [] idx = 0 while idx < len(self._macro): t, v = self._macro[idx] try: if t == RatbagdButton.Macro.KEY_PRESS: # Check for a paired press/release event t2, v2 = self._macro[idx + 1] if t2 == RatbagdButton.Macro.KEY_RELEASE and v == v2: t = self._MACRO_KEY idx += 1 except IndexError: pass keys.append(self._MACRO_DESCRIPTION[t](v)) idx += 1 return " ".join(keys) @GObject.Property def keys(self): """A list of (RatbagdButton.Macro.*, value) tuples representing the current macro.""" return self._macro @staticmethod def from_ratbag(macro): """Instantiates a new RatbagdMacro instance from the given macro in libratbag format. @param macro The macro in libratbag format, as [(RatbagdButton.Macro.*, value)]. """ ratbagd_macro = RatbagdMacro() # Do not emit notify::keys for every key that we add. with ratbagd_macro.freeze_notify(): for (type, value) in macro: ratbagd_macro.append(type, value) return ratbagd_macro def accept(self): """Applies the currently cached macro.""" self.emit("macro-set") def append(self, type, value): """Appends the given event to the current macro. @param type The type of event, as one of RatbagdButton.Macro.*. @param value If the type denotes a key event, the X.Org or Gdk keycode of the event, as int. Otherwise, the value of the timeout in milliseconds, as int. """ # Only append if the entry isn't identical to the last one, as we cannot # e.g. have two identical key presses in a row. if len(self._macro) == 0 or (type, value) != self._macro[-1]: self._macro.append((type, value)) self.notify("keys") class RatbagdLed(_RatbagdDBus): """Represents a ratbagd led.""" TYPE_LOGO = 1 TYPE_SIDE = 2 TYPE_BATTERY = 3 TYPE_DPI = 4 TYPE_WHEEL = 5 class Mode(IntEnum): OFF = 0 ON = 1 CYCLE = 2 BREATHING = 3 class ColorDepth(IntEnum): MONOCHROME = 0 RGB_888 = 1 RGB_111 = 2 LED_DESCRIPTION = { # Translators: the LED is off. Mode.OFF: N_("Off"), # Translators: the LED has a single, solid color. Mode.ON: N_("Solid"), # Translators: the LED is cycling between red, green and blue. Mode.CYCLE: N_("Cycle"), # Translators: the LED's is pulsating a single color on different # brightnesses. Mode.BREATHING: N_("Breathing"), } def __init__(self, object_path): super().__init__("Led", object_path) @GObject.Property def index(self): """The index of this led.""" return self._get_dbus_property("Index") @GObject.Property def mode(self): """This led's mode, one of Mode.OFF, Mode.ON, Mode.CYCLE and Mode.BREATHING.""" return self._get_dbus_property("Mode") @mode.setter def mode(self, mode): """Set the led's mode to the given mode. @param mode The new mode, as one of Mode.OFF, Mode.ON, Mode.CYCLE and Mode.BREATHING. """ self._set_dbus_property("Mode", "u", mode) @GObject.Property def modes(self): """The supported modes as a list""" return self._get_dbus_property("Modes") @GObject.Property def color(self): """An integer triple of the current LED color.""" return self._get_dbus_property("Color") @color.setter def color(self, color): """Set the led color to the given color. @param color An RGB color, as an integer triplet with values 0-255. """ self._set_dbus_property("Color", "(uuu)", color) @GObject.Property def colordepth(self): """An enum describing this led's colordepth, one of RatbagdLed.ColorDepth.MONOCHROME, RatbagdLed.ColorDepth.RGB""" return self._get_dbus_property("ColorDepth") @GObject.Property def effect_duration(self): """The LED's effect duration in ms, values range from 0 to 10000.""" return self._get_dbus_property("EffectDuration") @effect_duration.setter def effect_duration(self, effect_duration): """Set the effect duration in ms. Allowed values range from 0 to 10000. @param effect_duration The new effect duration, as int """ self._set_dbus_property("EffectDuration", "u", effect_duration) @GObject.Property def brightness(self): """The LED's brightness, values range from 0 to 255.""" return self._get_dbus_property("Brightness") @brightness.setter def brightness(self, brightness): """Set the brightness. Allowed values range from 0 to 255. @param brightness The new brightness, as int """ self._set_dbus_property("Brightness", "u", brightness) piper-0.4/piper/resolutionrow.py000066400000000000000000000130011362011671000171100ustar00rootroot00000000000000# Copyright (C) 2017 Jente Hidskes # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. from .gi_composites import GtkTemplate import gi gi.require_version("Gtk", "3.0") from gi.repository import GObject, Gtk # noqa @GtkTemplate(ui="/org/freedesktop/Piper/ui/ResolutionRow.ui") class ResolutionRow(Gtk.ListBoxRow): """A Gtk.ListBoxRow subclass containing the widgets to configure a resolution.""" __gtype_name__ = "ResolutionRow" dpi_label = GtkTemplate.Child() active_label = GtkTemplate.Child() revealer = GtkTemplate.Child() scale = GtkTemplate.Child() def __init__(self, device, resolution, *args, **kwargs): Gtk.ListBoxRow.__init__(self, *args, **kwargs) self.init_template() self._resolution = None self._resolution_handler = 0 self._active_handler = 0 self._scale_handler = self.scale.connect("value-changed", self._on_scale_value_changed) device.connect("active-profile-changed", self._on_active_profile_changed, resolution.index) self._init_values(resolution) def _init_values(self, resolution): if self._resolution_handler > 0: self._resolution.disconnect(self._resolution_handler) if self._active_handler > 0: self._resolution.disconnect(self._active_handler) self._resolution = resolution self._resolution_handler = resolution.connect("notify::resolution", self._on_resolution_changed) self._active_handler = resolution.connect("notify::is-active", self._on_is_active_changed) xres = resolution.resolution[0] minres = resolution.resolutions[0] maxres = resolution.resolutions[-1] self.resolutions = resolution.resolutions self.dpi_label.set_text("{} DPI".format(xres)) self.active_label.set_visible(resolution.is_active) with self.scale.handler_block(self._scale_handler): self.scale.props.adjustment.configure(xres, minres, maxres, 50, 50, 0) self.scale.set_value(xres) def _on_active_profile_changed(self, device, profile, index): resolution = profile.resolutions[index] self._init_values(resolution) @GtkTemplate.Callback def _on_change_value(self, scale, scroll, value): # Cursor-controlled slider may get out of the GtkAdjustment's range value = min(max(self.resolutions[0], value), self.resolutions[-1]) # Find the nearest permitted value to our Gtk.Scale value lo = max([r for r in self.resolutions if r <= value]) hi = min([r for r in self.resolutions if r >= value]) if value - lo < hi - value: value = lo else: value = hi scale.set_value(value) # libratbag provides a fake-exponential range with the deltas # increasing as the resolution goes up. Make sure we set our # steps to the next available value. idx = self.resolutions.index(value) if idx < len(self.resolutions) - 1: delta = self.resolutions[idx + 1] - self.resolutions[idx] self.scale.props.adjustment.set_step_increment(delta) self.scale.props.adjustment.set_page_increment(delta) return True @GtkTemplate.Callback def _on_scroll_event(self, widget, event): # Prevent a scroll in the list to get caught by the scale GObject.signal_stop_emission_by_name(widget, "scroll-event") return False def _on_scale_value_changed(self, scale): # The scale has been moved, update RatbagdResolution's resolution and # the title label. xres = int(self.scale.get_value()) # Freeze the notify::resolution signal from firing to prevent Piper from # ending up in an infinite update loop. with self._resolution.handler_block(self._resolution_handler): if len(self._resolution.resolution) == 1: self._resolution.resolution = (xres, ) else: self._resolution.resolution = (xres, xres) self.dpi_label.set_text("{} DPI".format(xres)) def _on_resolution_changed(self, resolution, pspec): # RatbagdResolution's resolution has changed, update the scales. xres = resolution.resolution[0] self.scale.set_value(xres) def _on_is_active_changed(self, resolution, pspec): # The active resolution changed; update the visibility of the label. self.active_label.set_visible(resolution.is_active) def toggle_revealer(self): """Toggles the revealer to show or hide the configuration widgets.""" reveal = not self.revealer.get_reveal_child() self.revealer.set_reveal_child(reveal) if reveal: self._resolution.set_active() piper-0.4/piper/resolutionspage.py000066400000000000000000000110351362011671000174050ustar00rootroot00000000000000# Copyright (C) 2017 Jente Hidskes # # 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 gettext import gettext as _ from .gi_composites import GtkTemplate from .mousemap import MouseMap from .ratbagd import RatbagdButton from .resolutionrow import ResolutionRow import gi gi.require_version("Gtk", "3.0") from gi.repository import Gtk # noqa @GtkTemplate(ui="/org/freedesktop/Piper/ui/ResolutionsPage.ui") class ResolutionsPage(Gtk.Box): """The first stack page, exposing the resolution configuration with its report rate buttons and resolutions list.""" __gtype_name__ = "ResolutionsPage" _resolution_labels = [ RatbagdButton.ActionSpecial.RESOLUTION_CYCLE_UP, RatbagdButton.ActionSpecial.RESOLUTION_CYCLE_DOWN, RatbagdButton.ActionSpecial.RESOLUTION_UP, RatbagdButton.ActionSpecial.RESOLUTION_DOWN, RatbagdButton.ActionSpecial.RESOLUTION_ALTERNATE, RatbagdButton.ActionSpecial.RESOLUTION_DEFAULT, ] rate_500 = GtkTemplate.Child() rate_1000 = GtkTemplate.Child() listbox = GtkTemplate.Child() add_resolution_row = GtkTemplate.Child() def __init__(self, ratbagd_device, *args, **kwargs): """Instantiates a new ResolutionsPage. @param ratbag_device The ratbag device to configure, as ratbagd.RatbagdDevice """ Gtk.Box.__init__(self, *args, **kwargs) self.init_template() self._device = ratbagd_device self._last_activated_row = None self._device.connect("active-profile-changed", self._on_active_profile_changed) self._handler_500 = self.rate_500.connect("toggled", self._on_report_rate_toggled, 500) self._handler_1000 = self.rate_1000.connect("toggled", self._on_report_rate_toggled, 1000) self._init_ui() def _init_ui(self): profile = self._device.active_profile mousemap = MouseMap("#Buttons", self._device, spacing=20, border_width=20) self.pack_start(mousemap, True, True, 0) # Place the MouseMap on the left self.reorder_child(mousemap, 0) for button in profile.buttons: if button.action_type == RatbagdButton.ActionType.SPECIAL and \ button.special in self._resolution_labels: label = Gtk.Label(label=_(RatbagdButton.SPECIAL_DESCRIPTION[button.special])) mousemap.add(label, "#button{}".format(button.index)) mousemap.show_all() for resolution in profile.resolutions: row = ResolutionRow(self._device, resolution) self.listbox.insert(row, resolution.index) # Set the report rate through a manual callback invocation. self._on_active_profile_changed(self._device, profile) def _on_active_profile_changed(self, device, profile): # Updates report rate to reflect the new active profile's report rate. with self.rate_500.handler_block(self._handler_500): self.rate_500.set_active(profile.report_rate == 500) with self.rate_1000.handler_block(self._handler_1000): self.rate_1000.set_active(profile.report_rate == 1000) def _on_report_rate_toggled(self, button, rate): if not button.get_active(): return profile = self._device.active_profile profile.report_rate = rate @GtkTemplate.Callback def _on_row_activated(self, listbox, row): if row is self._last_activated_row: self._last_activated_row = None row.toggle_revealer() else: if self._last_activated_row is not None: self._last_activated_row.toggle_revealer() if row is self.add_resolution_row: print("TODO: RatbagdProfile needs a way to add resolutions") self._last_activated_row = None else: self._last_activated_row = row row.toggle_revealer() piper-0.4/piper/svg.py000066400000000000000000000035521362011671000147660ustar00rootroot00000000000000# Copyright (C) 2019 Red Hat, Inc # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. from gi.repository import Gio # noqa import configparser def get_svg(model): resource = Gio.resources_lookup_data('/org/freedesktop/Piper/svgs/svg-lookup.ini', Gio.ResourceLookupFlags.NONE) data = resource.get_data() config = configparser.ConfigParser() config.read_string(data.decode('utf-8'), source='svg-lookup.ini') assert config.sections() filename = 'fallback.svg' if model.startswith('usb:') or model.startswith('bluetooth:'): bus, vid, pid, version = model.split(':') # Where the version is 0 (virtually all devices) we drop it. This # way the DeviceMatch lines are less confusing. if int(version) == 0: usbid = ':'.join([bus, vid, pid]) else: usbid = model for s in config.sections(): matches = config[s]['DeviceMatch'].split(';') if usbid in matches: filename = config[s]['Svg'] break resource = Gio.resources_lookup_data('/org/freedesktop/Piper/svgs/{}'.format(filename), Gio.ResourceLookupFlags.NONE) return resource.get_data() piper-0.4/piper/welcomeperspective.py000066400000000000000000000073771362011671000201050ustar00rootroot00000000000000# Copyright (C) 2017 Jente Hidskes # # 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 .devicerow import DeviceRow from .gi_composites import GtkTemplate import gi gi.require_version("Gtk", "3.0") from gi.repository import GObject, Gtk, Gdk # noqa @GtkTemplate(ui="/org/freedesktop/Piper/ui/WelcomePerspective.ui") class WelcomePerspective(Gtk.Box): """A perspective to present a list of devices for the user to pick one to configure.""" __gtype_name__ = "WelcomePerspective" __gsignals__ = { "device-selected": (GObject.SignalFlags.RUN_FIRST, None, (GObject.TYPE_PYOBJECT,)), } listbox = GtkTemplate.Child() _titlebar = GtkTemplate.Child() def __init__(self, *args, **kwargs): """Instantiates a new WelcomePerspective.""" Gtk.Box.__init__(self, *args, **kwargs) self.init_template() self.listbox.set_sort_func(self._listbox_sort_func) self.listbox.set_header_func(self._listbox_header_func) def set_devices(self, devices): """Sets the devices to present to the user. @param devices The devices to present, as [ratbagd.RatbagdDevice] """ self.listbox.foreach(Gtk.Widget.destroy) for device in devices: self.add_device(device) def add_device(self, device): """Add a device to the list. @param device The device to add, as ratbagd.RatbagdDevice """ self.listbox.add(DeviceRow(device)) def remove_device(self, device): """Remove a device from the list. @param device The device to remove, as ratbagd.RatbagdDevice """ for child in self.listbox.get_children(): if child._device is device: child.destroy() break @GObject.Property def name(self): """The name of this perspective.""" return "welcome_perspective" @GObject.Property def titlebar(self): """The titlebar to this perspective.""" return self._titlebar @GObject.Property def can_go_back(self): """Whether this perspective wants a back button to be displayed in case there is more than one connected device.""" return False @GObject.Property def can_shutdown(self): """Whether this perspective can safely shutdown.""" return True @GtkTemplate.Callback def _on_quit_button_clicked(self, button): window = button.get_toplevel() if not window.emit("delete-event", Gdk.Event.new(Gdk.EventType.DELETE)): window.destroy() @GtkTemplate.Callback def _on_device_row_activated(self, listbox, row): self.emit("device-selected", row.device) def _listbox_sort_func(self, row1, row2): name1 = row1.device.name.casefold() name2 = row2.device.name.casefold() if name1 < name2: return -1 elif name1 == name2: return 0 return 1 def _listbox_header_func(self, row, before): if before is not None: separator = Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL) row.set_header(separator) piper-0.4/piper/window.py000066400000000000000000000241621362011671000154760ustar00rootroot00000000000000# Copyright (C) 2017 Jente Hidskes # # 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 gettext import gettext as _ from .ratbagd import RatbagdIncompatible, RatbagdUnavailable from .errorperspective import ErrorPerspective from .gi_composites import GtkTemplate from .mouseperspective import MousePerspective from .welcomeperspective import WelcomePerspective import gi gi.require_version("Gtk", "3.0") from gi.repository import Gdk, GLib, Gtk, Gio # noqa @GtkTemplate(ui="/org/freedesktop/Piper/ui/Window.ui") class Window(Gtk.ApplicationWindow): """A Gtk.ApplicationWindow subclass to implement the main application window. This window displays the different perspectives (error, mouse and welcome) that each present their own behavior.""" __gtype_name__ = "Window" stack_titlebar = GtkTemplate.Child() stack_perspectives = GtkTemplate.Child() primary_menu = GtkTemplate.Child() def __init__(self, init_ratbagd_cb, *args, **kwargs): """Instantiates a new Window. @param ratbag The ratbag instance to connect to, as ratbagd.Ratbag """ Gtk.ApplicationWindow.__init__(self, *args, **kwargs) self.init_template() self._add_perspective(ErrorPerspective(), None) try: ratbag = init_ratbagd_cb() except RatbagdUnavailable: self._present_error_perspective(_("Cannot connect to ratbagd"), _("Please make sure ratbagd is running and your user is in the required group")) return except RatbagdIncompatible as e: self._present_error_perspective(_("Incompatible ratbagd API version (required: {}, provided: {})".format(e.required_version, e.ratbagd_version)), _("Please update both piper and libratbag to the latest versions")) return for perspective in [MousePerspective(), WelcomePerspective()]: self._add_perspective(perspective, ratbag) welcome_perspective = self._get_child("welcome_perspective") welcome_perspective.connect("device-selected", self._on_device_selected) ratbag.connect("device-added", self._on_device_added) ratbag.connect("device-removed", self._on_device_removed) ratbag.connect("daemon-disappeared", self._on_daemon_disappeared) if len(ratbag.devices) == 0: self._present_error_perspective(_("Cannot find any devices"), _("Please make sure your device is supported and plugged in")) elif len(ratbag.devices) == 1: self._present_mouse_perspective(ratbag.devices[0]) else: self._present_welcome_perspective(ratbag.devices) def do_delete_event(self, event): for perspective in self.stack_perspectives.get_children(): if not perspective.can_shutdown: dialog = Gtk.MessageDialog(self, Gtk.DialogFlags.MODAL, Gtk.MessageType.QUESTION, Gtk.ButtonsType.YES_NO, _("There are unapplied changes. Are you sure you want to quit?")) response = dialog.run() dialog.destroy() if response == Gtk.ResponseType.NO or response == Gtk.ResponseType.DELETE_EVENT: return Gdk.EVENT_STOP return Gdk.EVENT_PROPAGATE def _on_daemon_disappeared(self, ratbag): self._present_error_perspective(_("Ooops. ratbagd has disappeared"), _("Please restart Piper")) def _on_device_added(self, ratbag, device): if len(ratbag.devices) == 1: # We went from 0 devices to 1 device; immediately configure it. self._present_mouse_perspective(device) elif self.stack_perspectives.get_visible_child_name() == "welcome_perspective": # We're in the welcome perspective; just add it to the list. welcome_perspective = self._get_child("welcome_perspective") welcome_perspective.add_device(device) else: # We're configuring another device; just notify the user. # TODO: show in-app notification? print("Device connected") def _on_device_removed(self, ratbag, device): mouse_perspective = self._get_child("mouse_perspective") if device is mouse_perspective.device: # The current device disconnected, which can only happen from the # mouse perspective as we'd otherwise be in the welcome screen with # more than one device remaining. Hence, we display the error # perspective. self._present_error_perspective(_("Your device disconnected!"), _("Please make sure your device is plugged in")) elif self.stack_perspectives.get_visible_child_name() == "welcome_perspective": # We're in the welcome screen; just remove it from the list. If # there is nothing left, display the error perspective. welcome_perspective = self._get_child("welcome_perspective") welcome_perspective.remove_device(device) if len(ratbag.devices) == 0: self._present_error_perspective(_("Cannot find any devices"), _("Please make sure your device is supported and plugged in")) else: # We're configuring another device; just notify the user. # TODO: show in-app notification? print("Device disconnected") def _add_perspective(self, perspective, ratbag): self.stack_perspectives.add_named(perspective, perspective.name) self.stack_titlebar.add_named(perspective.titlebar, perspective.name) if perspective.can_go_back: self._perspective_add_back_button(perspective, ratbag) self._perspective_add_primary_menu(perspective) def _present_welcome_perspective(self, devices): # Present the welcome perspective for the user to select one of their # devices. welcome_perspective = self._get_child("welcome_perspective") welcome_perspective.set_devices(devices) self.stack_titlebar.set_visible_child_name(welcome_perspective.name) self.stack_perspectives.set_visible_child_name(welcome_perspective.name) def _present_mouse_perspective(self, device): # Present the mouse configuration perspective for the given device. try: mouse_perspective = self._get_child("mouse_perspective") mouse_perspective.set_device(device) self.stack_titlebar.set_visible_child_name(mouse_perspective.name) self.stack_perspectives.set_visible_child_name(mouse_perspective.name) except ValueError as e: self._present_error_perspective(_("Cannot display device SVG"), str(e)) except GLib.Error as e: # Happens with the GetSvgFd() call when running against older # python. This can be removed when we've had the newer call out # for a while. The full error is printed to stderr by # ratbagd.py. if e.code == Gio.DBusError.UNKNOWN_METHOD: self._present_error_perspective(_("Newer version of ratbagd required"), _('Please update to the latest available version')) else: self._present_error_perspective(_("Unknown exception occurred"), e.message) def _present_error_perspective(self, message, detail): # Present the error perspective informing the user of any errors. error_perspective = self._get_child("error_perspective") error_perspective.set_message(message) error_perspective.set_detail(detail) self.stack_titlebar.set_visible_child_name(error_perspective.name) self.stack_perspectives.set_visible_child_name(error_perspective.name) def _on_device_selected(self, perspective, device): self._present_mouse_perspective(device) def _get_child(self, name): return self.stack_perspectives.get_child_by_name(name) def _perspective_add_back_button(self, perspective, ratbag): button_back = Gtk.Button.new_from_icon_name("go-previous-symbolic", Gtk.IconSize.BUTTON) button_back.set_visible(len(ratbag.devices) > 1) button_back.connect("clicked", lambda _, ratbag: self._present_welcome_perspective(ratbag.devices), ratbag) ratbag.connect("notify::devices", lambda ratbag, _: button_back.set_visible(len(ratbag.devices) > 1)) perspective.titlebar.add(button_back) # Place the button first in the titlebar. perspective.titlebar.child_set_property(button_back, "position", 0) def _perspective_add_primary_menu(self, perspective): hamburger = Gtk.Image.new_from_icon_name("open-menu-symbolic", Gtk.IconSize.BUTTON) hamburger.set_visible(True) button_primary_menu = Gtk.MenuButton.new() button_primary_menu.add(hamburger) button_primary_menu.set_visible(True) button_primary_menu.set_menu_model(self.primary_menu) perspective.titlebar.pack_end(button_primary_menu) # Place the button last in the titlebar. perspective.titlebar.child_set_property(button_primary_menu, "position", 0) piper-0.4/po/000077500000000000000000000000001362011671000131075ustar00rootroot00000000000000piper-0.4/po/LINGUAS000066400000000000000000000001301362011671000141260ustar00rootroot00000000000000# Language list must be in alphabetical order be bg de es fr hr it nl pl pt_BR sv zh_TW piper-0.4/po/POTFILES000066400000000000000000000014161362011671000142610ustar00rootroot00000000000000data/org.freedesktop.Piper.desktop.in data/org.freedesktop.Piper.appdata.xml.in.in piper/__init__.py piper/application.py piper/buttondialog.py piper/buttonspage.py piper/devicerow.py piper/errorperspective.py piper/gi_composites.py piper/leddialog.py piper/ledspage.py piper/mousemap.py piper/mouseperspective.py piper/optionbutton.py piper/profilerow.py piper/ratbagd.py piper/resolutionrow.py piper/resolutionspage.py piper/welcomeperspective.py piper/window.py data/ui/AboutDialog.ui.in data/ui/ButtonDialog.ui data/ui/ButtonRow.ui data/ui/DeviceRow.ui data/ui/ErrorPerspective.ui data/ui/LedDialog.ui data/ui/MousePerspective.ui data/ui/OptionButton.ui data/ui/ProfileRow.ui data/ui/ResolutionRow.ui data/ui/ResolutionsPage.ui data/ui/WelcomePerspective.ui data/ui/Window.ui piper-0.4/po/README.md000066400000000000000000000024271362011671000143730ustar00rootroot00000000000000i18n ==== This directory contains the translations of Piper. For errors in translations, please [file an issue](https://github.com/libratbag/piper/issues/new). New or updated translations are always welcome. To start a new translation, run: $ meson translation-build $ ninja -C translation-build piper-pot # Now you can optionally remove the build directory $ rm -rf translation-build $ cp po/piper.pot po/$lang.po where `$lang` is the language code of your target language, e.g. `nl` for Dutch or `en_GB` for British English. Edit the [LINGUAS](https://github.com/libratbag/piper/blob/master/po/LINGUAS) file and add your language code, keeping the list sorted alphabetically. Finally, open the `.po` file you just created and translate all the strings. Don't forget to fill in the information in the header! To update an existing translation, run: $ meson translation-build $ ninja -C translation-build piper-update-po # Now you can optionally remove the build directory $ rm -rf translation-build and update the `po/$lang.po` file of your target language. When you are done translating, file a pull request on [GitHub](https://github.com/libratbag/piper) or, if you don't know how to, [open an issue](https://github.com/libratbag/piper/issues/new) and attach the `.po` file there. piper-0.4/po/be.po000066400000000000000000000405671362011671000140510ustar00rootroot00000000000000# Belarusian translation for Piper. # Copyright (C) 2019 Piper authors # This file is distributed under the same license as the piper package. # Aliaksei Piatrouski >, 2019. # msgid "" msgstr "" "Project-Id-Version: piper\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2019-10-26 19:46+0300\n" "PO-Revision-Date: 2019-10-26 22:23+0300\n" "Language: be\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Last-Translator: Aliaksei Piatrouski \n" "Language-Team: \n" "X-Generator: Poedit 2.2.1\n" #: data/org.freedesktop.Piper.desktop.in:3 #: data/org.freedesktop.Piper.appdata.xml.in.in:7 msgid "Piper" msgstr "Piper" #: data/org.freedesktop.Piper.desktop.in:4 #: data/org.freedesktop.Piper.appdata.xml.in.in:8 msgid "Configurable mouse configuration utility" msgstr "Прыстасаванне для наладкі мышы" #. Translators: Do NOT translate or transliterate this text (this is an icon file name)! #: data/org.freedesktop.Piper.desktop.in:7 msgid "org.freedesktop.Piper" msgstr "org.freedesktop.Piper" #. Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! #: data/org.freedesktop.Piper.desktop.in:12 msgid "gaming;configuration;mouse;mice;" msgstr "гульні;наладкі;настройкі;мыш;" #: data/org.freedesktop.Piper.appdata.xml.in.in:10 msgid "" "Piper is a graphical user interface to configure gaming mice. Configuration " "options include changing the resolution (DPI) of the mouse, adding and " "removing profiles, setting LED colors and changing button behaviors." msgstr "" "Piper - гэта графічны інтэрфэйс карыстальніка для наладкі гульнявых мышэй. " "Варыянты канфігурацыі ўключаюць змену адрознівальнай здольнасці (DPI) мышы, " "даданне і выдаленне профіляў, наладкі колеру святлодыёда і змяненне паводзін " "кнопкі." #: data/org.freedesktop.Piper.appdata.xml.in.in:16 msgid "" "Piper requires libratbag’s ratbagd, the daemon to actually communicate with " "the mice. Piper is merely a front end to ratbagd, ratbagd must be installed " "and running when Piper is launched." msgstr "" "Piper патрабуе бібліятэку libratbagd ад сэрвісу ratbagd, каб камунікаваць з " "мышшу. Piper - гэта проста графічны інтэрфэйс, ratbagd павінен быць " "усталяваны і запушчаны перад запускам Piper." #: data/org.freedesktop.Piper.appdata.xml.in.in:33 msgid "The button configuraton page" msgstr "Старонка наладкі кнопак" #: data/org.freedesktop.Piper.appdata.xml.in.in:37 msgid "The LED configuraton page" msgstr "Старонка наладкі LED" #: data/org.freedesktop.Piper.appdata.xml.in.in:41 msgid "The resolution configuraton page" msgstr "Старонка наладкі разрознасці (DPI)" #. Translators: section header for mapping one button's click to another. #: piper/buttondialog.py:141 msgid "Button mapping" msgstr "Прывязка кнопак" #. Translators: section header for assigning special functions to buttons. #: piper/buttondialog.py:150 msgid "Special mapping" msgstr "Асобная прывязка" #. Translators: the {} will be replaced with the button index, e.g. #. "Button 1 click". #: piper/buttondialog.py:222 piper/buttonspage.py:90 msgid "Button {} click" msgstr "Клік кнопкі {}" #: piper/buttonspage.py:94 msgid "Macro: {}" msgstr "Макра: {}" #. Translators: the button is turned disabled, e.g. off. #: piper/buttonspage.py:97 msgid "Disabled" msgstr "Выключана" #. Translators: the button has an unknown function. #: piper/buttonspage.py:100 piper/ratbagd.py:663 msgid "Unknown" msgstr "Невядома" #: piper/buttonspage.py:108 msgid "Configure button {}" msgstr "Наладкі кнопкі {}" #: piper/mouseperspective.py:90 data/ui/ResolutionsPage.ui:209 msgid "Resolutions" msgstr "Разрознасць" #: piper/mouseperspective.py:92 msgid "Buttons" msgstr "Кнопкі" #: piper/mouseperspective.py:94 msgid "LEDs" msgstr "LEDs" #: piper/ratbagd.py:653 msgid "Left mouse button click" msgstr "Клік левай кнопкі мышы" #: piper/ratbagd.py:654 msgid "Right mouse button click" msgstr "Клік правай кнопкі мышы" #: piper/ratbagd.py:655 msgid "Middle mouse button click" msgstr "Клік сярэдняй кнопкі мышы" #: piper/ratbagd.py:656 msgid "Backward" msgstr "Назад" #: piper/ratbagd.py:657 msgid "Forward" msgstr "Далей" #: piper/ratbagd.py:662 msgid "Invalid" msgstr "Несапраўдны" #: piper/ratbagd.py:664 msgid "Doubleclick" msgstr "Двайны клік" #: piper/ratbagd.py:665 msgid "Wheel Left" msgstr "Кола ўлева" #: piper/ratbagd.py:666 msgid "Wheel Right" msgstr "Кола ўправа" #: piper/ratbagd.py:667 msgid "Wheel Up" msgstr "Кола ўверх" #: piper/ratbagd.py:668 msgid "Wheel Down" msgstr "Кола ўніз" #: piper/ratbagd.py:669 msgid "Ratchet Mode" msgstr "Рэжым трашчоткі" #: piper/ratbagd.py:670 msgid "Cycle Resolution Up" msgstr "Цыклічна павялічыць разрознасць" #: piper/ratbagd.py:671 msgid "Cycle Resolution Down" msgstr "Цыклічна зменшыць разрознасць" #: piper/ratbagd.py:672 msgid "Resolution Up" msgstr "Павялічыць разрознасць" #: piper/ratbagd.py:673 msgid "Resolution Down" msgstr "Зменшыць разрознасць" #: piper/ratbagd.py:674 msgid "Resolution Switch" msgstr "Змяніць разрознасць" #: piper/ratbagd.py:675 msgid "Default Resolution" msgstr "Разрознасць па змоўчанні" #: piper/ratbagd.py:676 msgid "Cycle Profile Up" msgstr "Наступны профіль (цыклічна)" #: piper/ratbagd.py:677 msgid "Cycle Profile Down" msgstr "Папярэдні профіль (цыклічна)" #: piper/ratbagd.py:678 msgid "Profile Up" msgstr "Наступны профіль" #: piper/ratbagd.py:679 msgid "Profile Down" msgstr "Папярэдні профіль" #: piper/ratbagd.py:680 msgid "Second Mode" msgstr "Іншы Рэжым" #: piper/ratbagd.py:681 msgid "Battery Level" msgstr "Узровень батарэі" #. Translators: this is used when there is no macro to preview. #: piper/ratbagd.py:811 data/ui/ButtonDialog.ui:106 data/ui/ButtonDialog.ui:187 msgid "None" msgstr "Нічога" #. Translators: the LED is off. #: piper/ratbagd.py:893 data/ui/LedDialog.ui:395 msgid "Off" msgstr "Выключыць" #. Translators: the LED has a single, solid color. #: piper/ratbagd.py:895 data/ui/LedDialog.ui:66 msgid "Solid" msgstr "Адзін колер" #. Translators: the LED is cycling between red, green and blue. #: piper/ratbagd.py:897 data/ui/LedDialog.ui:189 msgid "Cycle" msgstr "Зменны колер" #. Translators: the LED's is pulsating a single color on different #. brightnesses. #: piper/ratbagd.py:900 data/ui/LedDialog.ui:355 msgid "Breathing" msgstr "Пульсацыя" #: piper/window.py:54 msgid "Cannot connect to ratbagd" msgstr "Немагчыма злучыцца з ratbagd" #: piper/window.py:55 msgid "Please make sure ratbagd is running" msgstr "Упэўніцеся, што ratbagd запушчаны" #: piper/window.py:58 msgid "Incompatible ratbagd API version (required: {}, provided: {})" msgstr "Няправільная версія ratbagdAPI (трэба: {}, знойдзена: {})" #: piper/window.py:59 msgid "Please update both piper and libratbag to the latest versions" msgstr "Калі ласка, абнавіце Piper і libratbag да апошніх весій" #: piper/window.py:73 piper/window.py:127 msgid "Cannot find any devices" msgstr "Немагчыма знайсці ніякія прылады" #: piper/window.py:74 piper/window.py:128 msgid "Please make sure your device is supported and plugged in" msgstr "Упэўніцеся, што прылада падтрымліваецца і падключана" #: piper/window.py:86 msgid "There are unapplied changes. Are you sure you want to quit?" msgstr "Ёсць непрымянёныя змены. Усё роўна выйсці?" #: piper/window.py:95 msgid "Ooops. ratbagd has disappeared" msgstr "Ой! ratbagd прапаў" #: piper/window.py:96 msgid "Please restart Piper" msgstr "Калі ласка, перазапусціце Piper" #. The current device disconnected, which can only happen from the #. mouse perspective as we'd otherwise be in the welcome screen with #. more than one device remaining. Hence, we display the error #. perspective. #: piper/window.py:119 msgid "Your device disconnected!" msgstr "Ваша прылада адключана!" #: piper/window.py:120 msgid "Please make sure your device is plugged in" msgstr "Упэўніцеся, то ваша прылада падключана" #: piper/window.py:159 msgid "Cannot display device SVG" msgstr "Немагчыма паказаць выяву прылады" #: piper/window.py:166 msgid "Newer version of ratbagd required" msgstr "Неабходна больш новая версія ratbagd" #: piper/window.py:167 msgid "Please update to the latest available version" msgstr "Калі ласка абнавіцеся да апошняй версіі" #: piper/window.py:169 msgid "Unknown exception occurred" msgstr "Адбылася невядомая памылка" #: data/ui/AboutDialog.ui.in:13 msgid "Visit Piper’s website" msgstr "Перайсці на старонку Piper" #: data/ui/AboutDialog.ui.in:14 msgid "translator-credits" msgstr "Aliaksei Piatrouski " #: data/ui/ButtonDialog.ui:65 msgid "Search for a button mapping" msgstr "Пошук прывязак кнопак" #: data/ui/ButtonDialog.ui:91 msgid "Capture a macro for this button" msgstr "Запісаць макра для гэтай кнопкі" #: data/ui/ButtonDialog.ui:104 msgid "The currently set macro" msgstr "Бягучы ўсталяваны макра" #: data/ui/ButtonDialog.ui:129 msgid "Send keystroke" msgstr "Адправіць камбінацыю клавіш" #: data/ui/ButtonDialog.ui:169 msgid "Enter a new key sequence for the macro." msgstr "Увядзіце новую паслядоўнасць клавіш для макраса." #: data/ui/ButtonDialog.ui:221 msgid "Press Escape to cancel or Return to accept." msgstr "Націсніце Escape каб адмяніць або Return каб прыняць." #: data/ui/ButtonDialog.ui:252 msgid "Set the device’s handedness." msgstr "Левая/правая рука." #: data/ui/ButtonDialog.ui:270 msgid "" "This allows you to swap the order of the primary mouse buttons. In left-" "handed mode, the physical left mouse button generates a right mouse click " "and vice versa." msgstr "" "Гэта дазваляе змяніць парадак асноўных кнопак мышы. У рэжыме левай рукі " "фізічная левая кнопка мышы працуе як правая і наадварот." #: data/ui/ButtonDialog.ui:290 msgid "Left Handed" msgstr "Левая рука" #: data/ui/ButtonDialog.ui:295 msgid "The left mouse button generates a left mouse click" msgstr "Левая кнопка мышы генеруе левы клік" #: data/ui/ButtonDialog.ui:309 msgid "Right Handed" msgstr "Правая рука" #: data/ui/ButtonDialog.ui:314 msgid "The left mouse button generates a right mouse click" msgstr "Левая кнопка мышы генеруе правы клік" #: data/ui/ButtonDialog.ui:354 data/ui/LedDialog.ui:414 msgid "Cancel" msgstr "Адмена" #: data/ui/ButtonDialog.ui:356 data/ui/LedDialog.ui:418 msgid "Ignore any changes made" msgstr "Ігнараваць усе зробленыя змены" #: data/ui/ButtonDialog.ui:362 data/ui/LedDialog.ui:430 #: data/ui/MousePerspective.ui:132 msgid "Apply" msgstr "Ужывіць" #: data/ui/ButtonDialog.ui:365 data/ui/LedDialog.ui:435 msgid "Apply any changes made" msgstr "Ужывіць усе зробленыя змены" #: data/ui/ButtonDialog.ui:405 msgid "No button mapping found" msgstr "Не знойдзена ніякіх прывязак" #: data/ui/ButtonDialog.ui:421 msgid "Try a different search" msgstr "Паспрабуйце іншае слова" #: data/ui/ErrorPerspective.ui:16 msgid "Here, rodent, rodent, rodent…" msgstr "Тут мыш, мыш, мыш…" #: data/ui/ErrorPerspective.ui:69 msgid "Uh Oh! Something went wrong…" msgstr "Ах! Нешта пайшло не так…" #: data/ui/ErrorPerspective.ui:73 data/ui/WelcomePerspective.ui:123 msgid "Quit" msgstr "Выйсці" #: data/ui/LedDialog.ui:58 data/ui/LedDialog.ui:209 msgid "Choose a color for the LED" msgstr "Абярыце колер для LED" #: data/ui/LedDialog.ui:90 data/ui/LedDialog.ui:257 msgid "Choose a brightness for the LED" msgstr "Абярыце яркасць для LED" #: data/ui/LedDialog.ui:104 data/ui/LedDialog.ui:270 msgid "Brightness" msgstr "Яркасць" #: data/ui/LedDialog.ui:135 data/ui/LedDialog.ui:301 msgid "Choose an effect duration for the LED" msgstr "Абярыце эфект для LED" #: data/ui/LedDialog.ui:153 data/ui/LedDialog.ui:319 msgid "ms" msgstr "мс" #: data/ui/LedDialog.ui:175 data/ui/LedDialog.ui:341 msgid "Effect duration" msgstr "Працягласць эфекту" #: data/ui/LedDialog.ui:212 msgid "Pick a Color for the LED" msgstr "Вызначце колер для LED" #: data/ui/LedDialog.ui:226 msgid "Color" msgstr "Колер" #: data/ui/LedDialog.ui:380 msgid "This LED is off" msgstr "Гэты LED адключаны" #: data/ui/MousePerspective.ui:44 msgid "Something went wrong. The device has been reset to a previous state." msgstr "Нешта пайшло не так. Прылада была адноўлена ў папярэдні стан." #: data/ui/MousePerspective.ui:101 msgid "Select another profile" msgstr "Абярыце іншы профіль" #: data/ui/MousePerspective.ui:126 msgid "Commit the changes to the device" msgstr "Адправіць змены ў прыладу" #: data/ui/MousePerspective.ui:170 msgid "Click to switch to another profile" msgstr "Клікніце каб абраць іншы профіль" #: data/ui/MousePerspective.ui:185 msgid "Add profile" msgstr "Дадаць профіль" #: data/ui/MousePerspective.ui:189 msgid "Add a new profile to the device" msgstr "Дадаць новы профіль да прылады" #: data/ui/OptionButton.ui:9 msgid "Open the configuration dialog" msgstr "Адкрыць дыялог наладкаў" #: data/ui/ProfileRow.ui:16 msgid "Remove this profile from the device" msgstr "Выдаліць гэты профіль з прылады" #. Translators: this is used to indicate the active resolution. #: data/ui/ResolutionRow.ui:37 msgid "Active" msgstr "Актыўны" #: data/ui/ResolutionRow.ui:77 msgid "Set this resolution’s DPI" msgstr "Вызначыць разрознасць DPI" #. Translators: the rate at which the device reports movement. #: data/ui/ResolutionsPage.ui:43 msgid "Report rate" msgstr "Хуткасць водгуку" #: data/ui/ResolutionsPage.ui:60 msgid "Change the profile’s report rate" msgstr "Змяніць профіль хуткасці водгуку" #: data/ui/ResolutionsPage.ui:105 msgid "Hz" msgstr "Гц" #. Translators: the sensitivity of the device's sensor. #: data/ui/ResolutionsPage.ui:125 msgid "Sensitivity" msgstr "Адчувальнасць" #: data/ui/ResolutionsPage.ui:174 msgid "Add a new resolution to the profile" msgstr "Дадаць новую разрознасць да профілю" #: data/ui/WelcomePerspective.ui:20 msgid "Welcome" msgstr "Вітаем" #: data/ui/WelcomePerspective.ui:38 msgctxt "A subtitle under the \"Welcome\" title." msgid "Select a device to configure it" msgstr "Абярыце прыстасаванне для наладкі" #: data/ui/WelcomePerspective.ui:89 msgctxt "A tooltip over the list of devices." msgid "Select a device to configure it" msgstr "Абярыце прыстасаванне для наладкі" #: data/ui/WelcomePerspective.ui:119 msgid "Select a Device" msgstr "Абраць прыладу" #: data/ui/Window.ui:8 msgid "_About Piper" msgstr "_Пра Piper" piper-0.4/po/bg.po000077500000000000000000000446631362011671000140570ustar00rootroot00000000000000# Bulgarian translation for Piper # Copyright (C) 2017-2019 Piper developers # This file is distributed under the same license as the piper package. # Lyubomir Vasilev , 2019. # msgid "" msgstr "" "Project-Id-Version: piper\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2020-01-27 23:39+0200\n" "PO-Revision-Date: 2020-01-27 23:40+0200\n" "Last-Translator: Lyubomir Vasilev \n" "Language-Team: Bulgarian <>\n" "Language: bg\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.0.6\n" #: data/org.freedesktop.Piper.desktop.in:3 #: data/org.freedesktop.Piper.appdata.xml.in.in:7 msgid "Piper" msgstr "Piper" #: data/org.freedesktop.Piper.desktop.in:4 #: data/org.freedesktop.Piper.appdata.xml.in.in:8 msgid "Configurable mouse configuration utility" msgstr "Инструмент за управление на настройките на мишката" #. Translators: Do NOT translate or transliterate this text (this is an icon file name)! #: data/org.freedesktop.Piper.desktop.in:7 msgid "org.freedesktop.Piper" msgstr "org.freedesktop.Piper" #. Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! #: data/org.freedesktop.Piper.desktop.in:12 msgid "gaming;configuration;mouse;mice;" msgstr "игра;игри;гейминг;настройка;настройки;конфигурация;мишка;мишки;" #: data/org.freedesktop.Piper.appdata.xml.in.in:10 msgid "" "Piper is a graphical user interface to configure gaming mice. Configuration " "options include changing the resolution (DPI) of the mouse, adding and " "removing profiles, setting LED colors and changing button behaviors." msgstr "" "Piper е програма за управление на настройките на мишки, предназначени за " "ползване в игри. С Piper можете да променяте разделителната способност на " "мишката (DPI), да добавяте и премахвате профили, да задавате цветовете на " "светодиодите и да променяте това какво правят бутоните." #: data/org.freedesktop.Piper.appdata.xml.in.in:16 msgid "" "Piper requires libratbag’s ratbagd, the daemon to actually communicate with " "the mice. Piper is merely a front end to ratbagd, ratbagd must be installed " "and running when Piper is launched." msgstr "" "Piper изисква ratbagd – демон, който всъщност осъществява връзката с " "мишката. Piper е само потребителски интерфейс за ratbagd, и за да " "функционира, трябва ratbagd да е инсталиран и да работи при стартирането на " "Piper." #: data/org.freedesktop.Piper.appdata.xml.in.in:33 msgid "The button configuraton page" msgstr "Страницата за настройка на бутоните" #: data/org.freedesktop.Piper.appdata.xml.in.in:37 msgid "The LED configuraton page" msgstr "Страницата за настройка на светодиодите" #: data/org.freedesktop.Piper.appdata.xml.in.in:41 msgid "The resolution configuraton page" msgstr "Страницата за настройка на разделителната способност" #. Translators: section header for mapping one button's click to another. #: piper/buttondialog.py:141 msgid "Button mapping" msgstr "Съответствие на бутона" #. Translators: section header for assigning special functions to buttons. #: piper/buttondialog.py:150 msgid "Special mapping" msgstr "Специално съответствие" #. Translators: the {} will be replaced with the button index, e.g. #. "Button 1 click". #: piper/buttondialog.py:222 piper/buttonspage.py:90 msgid "Button {} click" msgstr "Щракване на бутон {}" #: piper/buttonspage.py:94 msgid "Macro: {}" msgstr "Комбинация: {}" #. Translators: the button is turned disabled, e.g. off. #: piper/buttonspage.py:97 msgid "Disabled" msgstr "Изключено" #. Translators: the button has an unknown function. #: piper/buttonspage.py:100 piper/ratbagd.py:665 msgid "Unknown" msgstr "Неизвестно" #: piper/buttonspage.py:108 msgid "Configure button {}" msgstr "Настройване на бутон {}" #: piper/mouseperspective.py:90 data/ui/ResolutionsPage.ui:209 msgid "Resolutions" msgstr "Разделителни способности" #: piper/mouseperspective.py:92 msgid "Buttons" msgstr "Бутони" #: piper/mouseperspective.py:94 msgid "LEDs" msgstr "Светодиоди" #: piper/ratbagd.py:655 msgid "Left mouse button click" msgstr "Натискане на левия бутон" #: piper/ratbagd.py:656 msgid "Right mouse button click" msgstr "Натискане на десния бутон" #: piper/ratbagd.py:657 msgid "Middle mouse button click" msgstr "Натискане на средния бутон" #: piper/ratbagd.py:658 msgid "Backward" msgstr "Назад" #: piper/ratbagd.py:659 msgid "Forward" msgstr "Напред" #: piper/ratbagd.py:664 msgid "Invalid" msgstr "Грешно" #: piper/ratbagd.py:666 msgid "Doubleclick" msgstr "Двукратно щракване" #: piper/ratbagd.py:667 msgid "Wheel Left" msgstr "Колелце наляво" #: piper/ratbagd.py:668 msgid "Wheel Right" msgstr "Колелце надясно" #: piper/ratbagd.py:669 msgid "Wheel Up" msgstr "Колелце нагоре" #: piper/ratbagd.py:670 msgid "Wheel Down" msgstr "Колелце надолу" #: piper/ratbagd.py:671 msgid "Ratchet Mode" msgstr "Постъпково въртене" #: piper/ratbagd.py:672 msgid "Cycle Resolution Up" msgstr "Разделителна способност при превъртане нагоре" #: piper/ratbagd.py:673 msgid "Cycle Resolution Down" msgstr "Разделителна способност при превъртане надолу" #: piper/ratbagd.py:674 msgid "Resolution Up" msgstr "Разделителна способност нагоре" #: piper/ratbagd.py:675 msgid "Resolution Down" msgstr "Разделителна способност надолу" #: piper/ratbagd.py:676 msgid "Resolution Switch" msgstr "Превключване на разделителната способност" #: piper/ratbagd.py:677 msgid "Default Resolution" msgstr "Разделителна способност по подразбиране" #: piper/ratbagd.py:678 msgid "Cycle Profile Up" msgstr "Превъртане на профила нагоре" #: piper/ratbagd.py:679 msgid "Cycle Profile Down" msgstr "Превъртане на профила надолу" #: piper/ratbagd.py:680 msgid "Profile Up" msgstr "Профил нагоре" #: piper/ratbagd.py:681 msgid "Profile Down" msgstr "Профил надолу" #: piper/ratbagd.py:682 msgid "Second Mode" msgstr "Втори режим" #: piper/ratbagd.py:683 msgid "Battery Level" msgstr "Заряд на батерията" #. Translators: this is used when there is no macro to preview. #: piper/ratbagd.py:813 data/ui/ButtonDialog.ui:106 data/ui/ButtonDialog.ui:187 msgid "None" msgstr "Няма" #. Translators: the LED is off. #: piper/ratbagd.py:895 data/ui/LedDialog.ui:395 msgid "Off" msgstr "Изключено" #. Translators: the LED has a single, solid color. #: piper/ratbagd.py:897 data/ui/LedDialog.ui:66 msgid "Solid" msgstr "Постоянен" #. Translators: the LED is cycling between red, green and blue. #: piper/ratbagd.py:899 data/ui/LedDialog.ui:189 msgid "Cycle" msgstr "Цикъл" #. Translators: the LED's is pulsating a single color on different #. brightnesses. #: piper/ratbagd.py:902 data/ui/LedDialog.ui:355 msgid "Breathing" msgstr "Пулсиране" #: piper/window.py:54 msgid "Cannot connect to ratbagd" msgstr "Не може да се установи връзка с ratbagd" #: piper/window.py:55 msgid "" "Please make sure ratbagd is running and your user is in the required group" msgstr "" "Моля, уверете се, че ratbagd работи и че потребителят Ви е в нужната група" #: piper/window.py:58 msgid "Incompatible ratbagd API version (required: {}, provided: {})" msgstr "Несъвместима версия на ППИ на ratbagd (необходима: {}, налична: {})" #: piper/window.py:59 msgid "Please update both piper and libratbag to the latest versions" msgstr "Моля, обновете piper и libratbag до последните им версии" #: piper/window.py:73 piper/window.py:127 msgid "Cannot find any devices" msgstr "Няма открити усройства" #: piper/window.py:74 piper/window.py:128 msgid "Please make sure your device is supported and plugged in" msgstr "Уверете се, че устройството се поддържа и че е свързано" #: piper/window.py:86 msgid "There are unapplied changes. Are you sure you want to quit?" msgstr "Няма неприложени промени. Наистина ли искате да излезете?" #: piper/window.py:95 msgid "Ooops. ratbagd has disappeared" msgstr "Опа, ratbagd изчезна" #: piper/window.py:96 msgid "Please restart Piper" msgstr "Моля, рестартирайте Piper" #. The current device disconnected, which can only happen from the #. mouse perspective as we'd otherwise be in the welcome screen with #. more than one device remaining. Hence, we display the error #. perspective. #: piper/window.py:119 msgid "Your device disconnected!" msgstr "Устройството се разкачи!" #: piper/window.py:120 msgid "Please make sure your device is plugged in" msgstr "Уверете се, че устройството е свързано" #: piper/window.py:159 msgid "Cannot display device SVG" msgstr "Не може да бъде показана графика на устройството" #: piper/window.py:166 msgid "Newer version of ratbagd required" msgstr "Изисква се по-нова версия на ratbagd" #: piper/window.py:167 msgid "Please update to the latest available version" msgstr "Моля, обновете до най-новата версия" #: piper/window.py:169 msgid "Unknown exception occurred" msgstr "Възникна неизвестно изключение" #: data/ui/AboutDialog.ui.in:10 msgid "Version: @version@" msgstr "Версия: @version@" #: data/ui/AboutDialog.ui.in:12 msgid "Visit Piper on GitHub" msgstr "Посетете Piper в GitHub" #: data/ui/AboutDialog.ui.in:13 msgid "translator-credits" msgstr "Любомир Василев" #: data/ui/ButtonDialog.ui:65 msgid "Search for a button mapping" msgstr "Търсене за съответствие на бутон" #: data/ui/ButtonDialog.ui:91 msgid "Capture a macro for this button" msgstr "Запис на комбинация за бутона" #: data/ui/ButtonDialog.ui:104 msgid "The currently set macro" msgstr "Текущата комбинация" #: data/ui/ButtonDialog.ui:129 msgid "Send keystroke" msgstr "Натискане на клавиш" #: data/ui/ButtonDialog.ui:169 msgid "Enter a new key sequence for the macro." msgstr "Въведете нова клавишна последователност за комбинацията." #: data/ui/ButtonDialog.ui:221 msgid "Press Escape to cancel or Return to accept." msgstr "Натиснете Escape за отказ или Enter за потвърждение." #: data/ui/ButtonDialog.ui:252 msgid "Set the device’s handedness." msgstr "Изберете дали устройството се ползва с лява или дясна ръка." #: data/ui/ButtonDialog.ui:270 msgid "" "This allows you to swap the order of the primary mouse buttons. In left-" "handed mode, the physical left mouse button generates a right mouse click " "and vice versa." msgstr "" "Това позволява да размените действията на основните бутони на мишката. При " "режим за лява ръка, реалният ляв бутон на мишката действа като десен в " "програмите, и обратното." #: data/ui/ButtonDialog.ui:290 msgid "Left Handed" msgstr "За лява ръка" #: data/ui/ButtonDialog.ui:295 msgid "The left mouse button generates a left mouse click" msgstr "Левият бутон на мишката действа като щракване с ляв бутон" #: data/ui/ButtonDialog.ui:309 msgid "Right Handed" msgstr "За дясна ръка" #: data/ui/ButtonDialog.ui:314 msgid "The left mouse button generates a right mouse click" msgstr "Левият бутон на мишката действа като щракване с десен бутон" #: data/ui/ButtonDialog.ui:354 data/ui/LedDialog.ui:414 msgid "Cancel" msgstr "Отказ" #: data/ui/ButtonDialog.ui:356 data/ui/LedDialog.ui:418 msgid "Ignore any changes made" msgstr "Отхвърляне на направените промени" #: data/ui/ButtonDialog.ui:362 data/ui/LedDialog.ui:430 #: data/ui/MousePerspective.ui:132 msgid "Apply" msgstr "Прилагане" #: data/ui/ButtonDialog.ui:365 data/ui/LedDialog.ui:435 msgid "Apply any changes made" msgstr "Прилагане на направените промени" #: data/ui/ButtonDialog.ui:405 msgid "No button mapping found" msgstr "Няма намерени съответствия на бутони" #: data/ui/ButtonDialog.ui:421 msgid "Try a different search" msgstr "Опитайте друго търсене" #: data/ui/ErrorPerspective.ui:16 msgid "Here, rodent, rodent, rodent…" msgstr "Тук, миши, миши…" #: data/ui/ErrorPerspective.ui:69 msgid "Uh Oh! Something went wrong…" msgstr "Опа! нещо се обърка…" #: data/ui/ErrorPerspective.ui:73 data/ui/WelcomePerspective.ui:123 msgid "Quit" msgstr "Изход" #: data/ui/LedDialog.ui:58 data/ui/LedDialog.ui:209 msgid "Choose a color for the LED" msgstr "Изберете цвят за светодиода" #: data/ui/LedDialog.ui:90 data/ui/LedDialog.ui:257 msgid "Choose a brightness for the LED" msgstr "Изберете яркост за светодиода" #: data/ui/LedDialog.ui:104 data/ui/LedDialog.ui:270 msgid "Brightness" msgstr "Яркост" #: data/ui/LedDialog.ui:135 data/ui/LedDialog.ui:301 msgid "Choose an effect duration for the LED" msgstr "Изберете продължителността на ефекта за светодиода" #: data/ui/LedDialog.ui:153 data/ui/LedDialog.ui:319 msgid "ms" msgstr "мсек" #: data/ui/LedDialog.ui:175 data/ui/LedDialog.ui:341 msgid "Effect duration" msgstr "Продължителност на ефекта" #: data/ui/LedDialog.ui:212 msgid "Pick a Color for the LED" msgstr "Изберете цвят за светодиода" #: data/ui/LedDialog.ui:226 msgid "Color" msgstr "Цвят" #: data/ui/LedDialog.ui:380 msgid "This LED is off" msgstr "Светодиодът е изключен" #: data/ui/MousePerspective.ui:44 msgid "Something went wrong. The device has been reset to a previous state." msgstr "Нещо се обърка. Устройството беше върнато към предишните настройки." #: data/ui/MousePerspective.ui:101 msgid "Select another profile" msgstr "Избор на друг профил" #: data/ui/MousePerspective.ui:126 msgid "Commit the changes to the device" msgstr "Изпращане на промените към устройството" #: data/ui/MousePerspective.ui:170 msgid "Click to switch to another profile" msgstr "Щракнете за превключване към друг профил" #: data/ui/MousePerspective.ui:185 msgid "Add profile" msgstr "Добавяне на профил" #: data/ui/MousePerspective.ui:189 msgid "Add a new profile to the device" msgstr "Добавяне на нов профил за устройството" #: data/ui/OptionButton.ui:9 msgid "Open the configuration dialog" msgstr "Отваряне на прозореца с настройките" #: data/ui/ProfileRow.ui:16 msgid "Remove this profile from the device" msgstr "Премахване на този профил от устройството" #. Translators: this is used to indicate the active resolution. #: data/ui/ResolutionRow.ui:37 msgid "Active" msgstr "Избрано" #: data/ui/ResolutionRow.ui:77 msgid "Set this resolution’s DPI" msgstr "Задаване на тази разделителна способност" #. Translators: the rate at which the device reports movement. #: data/ui/ResolutionsPage.ui:43 msgid "Report rate" msgstr "Честота на опресняване" #: data/ui/ResolutionsPage.ui:60 msgid "Change the profile’s report rate" msgstr "Промяна на честотата на опресняване за профила" #: data/ui/ResolutionsPage.ui:105 msgid "Hz" msgstr "Hz" #. Translators: the sensitivity of the device's sensor. #: data/ui/ResolutionsPage.ui:125 msgid "Sensitivity" msgstr "Чувствителност" #: data/ui/ResolutionsPage.ui:174 msgid "Add a new resolution to the profile" msgstr "Добавяне на нова разделителна способност в профила" #: data/ui/WelcomePerspective.ui:20 msgid "Welcome" msgstr "Добре дошли" #: data/ui/WelcomePerspective.ui:38 msgctxt "A subtitle under the \"Welcome\" title." msgid "Select a device to configure it" msgstr "Изберете устройство, за да го настроите" #: data/ui/WelcomePerspective.ui:89 msgctxt "A tooltip over the list of devices." msgid "Select a device to configure it" msgstr "Изберете устройство, за да го настроите" #: data/ui/WelcomePerspective.ui:119 msgid "Select a Device" msgstr "Изберете устройство" #: data/ui/Window.ui:8 msgid "_About Piper" msgstr "_Относно Piper" piper-0.4/po/de.po000066400000000000000000000363431362011671000140500ustar00rootroot00000000000000# GermanPiper translation # Copyright (C) 2017 Red Hat, Inc. # This file is distributed under the same license as the piper package. # Peter Hutterer , 2017. # msgid "" msgstr "" "Project-Id-Version: piper\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2020-01-28 20:40+0100\n" "PO-Revision-Date: 2017-08-23 13:02+1000\n" "Last-Translator: Peter Hutterer \n" "Language-Team: German <>\n" "Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: data/org.freedesktop.Piper.desktop.in:3 #: data/org.freedesktop.Piper.appdata.xml.in.in:7 msgid "Piper" msgstr "Piper" #: data/org.freedesktop.Piper.desktop.in:4 #: data/org.freedesktop.Piper.appdata.xml.in.in:8 msgid "Configurable mouse configuration utility" msgstr "Konfigurationsprogramm für konfigurierbare Mäuse" #. Translators: Do NOT translate or transliterate this text (this is an icon file name)! #: data/org.freedesktop.Piper.desktop.in:7 msgid "org.freedesktop.Piper" msgstr "org.freedesktop.Piper" #. Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! #: data/org.freedesktop.Piper.desktop.in:12 msgid "gaming;configuration;mouse;mice;" msgstr "Spiele;Games;Konfiguration;Maus;Mäuse;" #: data/org.freedesktop.Piper.appdata.xml.in.in:10 msgid "" "Piper is a graphical user interface to configure gaming mice. Configuration " "options include changing the resolution (DPI) of the mouse, adding and " "removing profiles, setting LED colors and changing button behaviors." msgstr "" "Piper ist eine Grafische Benutzerschnittstelle zur Konfiguration von Gaming " "Mäuse. Mögliche Konfigurationsoptionen sind die Auflösung der Maus (DPI), " "das Editieren von Profilen, LED Farbeinstellungen und der Tastenzuweisung." #: data/org.freedesktop.Piper.appdata.xml.in.in:16 msgid "" "Piper requires libratbag’s ratbagd, the daemon to actually communicate with " "the mice. Piper is merely a front end to ratbagd, ratbagd must be installed " "and running when Piper is launched." msgstr "" "Piper benötigt libratbags ratbagd, der Daemon der die eigentliche " "Kommunikation mit der Maus verwaltet. Piper ist nur eine Graphische " "Benutzeroberfläche für ratbagd, ratbagd muss installiert sein und laufen " "wenn Piper gestartet wird." #: data/org.freedesktop.Piper.appdata.xml.in.in:33 #, fuzzy msgid "The button configuraton page" msgstr "Das Konfigurationsfenster öffnen" #: data/org.freedesktop.Piper.appdata.xml.in.in:37 #, fuzzy msgid "The LED configuraton page" msgstr "Das Konfigurationsfenster öffnen" #: data/org.freedesktop.Piper.appdata.xml.in.in:41 #, fuzzy msgid "The resolution configuraton page" msgstr "Das Konfigurationsfenster öffnen" #. Translators: section header for mapping one button's click to another. #: piper/buttondialog.py:141 msgid "Button mapping" msgstr "Tastenfunktion" #. Translators: section header for assigning special functions to buttons. #: piper/buttondialog.py:150 msgid "Special mapping" msgstr "Spezialfunktion" #. Translators: the {} will be replaced with the button index, e.g. #. "Button 1 click". #: piper/buttondialog.py:222 piper/buttonspage.py:90 msgid "Button {} click" msgstr "Klick mit Taste {}" #: piper/buttonspage.py:94 msgid "Macro: {}" msgstr "Makro: {}" #. Translators: the button is turned disabled, e.g. off. #: piper/buttonspage.py:97 msgid "Disabled" msgstr "Abgeschalted" #. Translators: the button has an unknown function. #: piper/buttonspage.py:100 piper/ratbagd.py:665 msgid "Unknown" msgstr "Unbekannt" #: piper/buttonspage.py:108 msgid "Configure button {}" msgstr "Taste {} konfigurieren" #: piper/mouseperspective.py:90 data/ui/ResolutionsPage.ui:209 msgid "Resolutions" msgstr "Auflösungen" #: piper/mouseperspective.py:92 msgid "Buttons" msgstr "Tasten" #: piper/mouseperspective.py:94 msgid "LEDs" msgstr "LEDs" #: piper/ratbagd.py:655 msgid "Left mouse button click" msgstr "Klick mit linker Maustaste" #: piper/ratbagd.py:656 msgid "Right mouse button click" msgstr "Klick mit rechter Maustaste" #: piper/ratbagd.py:657 msgid "Middle mouse button click" msgstr "Klick mit mittlerer Maustaste" #: piper/ratbagd.py:658 msgid "Backward" msgstr "Zurück" #: piper/ratbagd.py:659 msgid "Forward" msgstr "Weiter" #: piper/ratbagd.py:664 msgid "Invalid" msgstr "Ung̈́ültig" #: piper/ratbagd.py:666 msgid "Doubleclick" msgstr "Doppelklick" #: piper/ratbagd.py:667 msgid "Wheel Left" msgstr "Mausrad links" #: piper/ratbagd.py:668 msgid "Wheel Right" msgstr "Mausrad rechts" #: piper/ratbagd.py:669 msgid "Wheel Up" msgstr "Mausrad hinauf" #: piper/ratbagd.py:670 msgid "Wheel Down" msgstr "Mausrad hinunter" #: piper/ratbagd.py:671 msgid "Ratchet Mode" msgstr "Präzisionsmodus" #: piper/ratbagd.py:672 msgid "Cycle Resolution Up" msgstr "Nächste Auflösung (zyklisch)" #: piper/ratbagd.py:673 msgid "Cycle Resolution Down" msgstr "Vorherige Auflösung (zyklisch)" #: piper/ratbagd.py:674 msgid "Resolution Up" msgstr "Nächste Auflösung" #: piper/ratbagd.py:675 msgid "Resolution Down" msgstr "Vorherige Auflösung" #: piper/ratbagd.py:676 msgid "Resolution Switch" msgstr "Auflösung wechseln" #: piper/ratbagd.py:677 msgid "Default Resolution" msgstr "Standardauflösung" #: piper/ratbagd.py:678 msgid "Cycle Profile Up" msgstr "Nächstes Profil (zyklisch)" #: piper/ratbagd.py:679 msgid "Cycle Profile Down" msgstr "Vorheriges Profil (zyklisch)" #: piper/ratbagd.py:680 msgid "Profile Up" msgstr "Nächstes Profil" #: piper/ratbagd.py:681 msgid "Profile Down" msgstr "Vorheriges Profil" #: piper/ratbagd.py:682 msgid "Second Mode" msgstr "Zweiter Modus" #: piper/ratbagd.py:683 msgid "Battery Level" msgstr "Batterieladestand" #. Translators: this is used when there is no macro to preview. #: piper/ratbagd.py:813 data/ui/ButtonDialog.ui:106 data/ui/ButtonDialog.ui:187 msgid "None" msgstr "Nicht zugewiesen" #. Translators: the LED is off. #: piper/ratbagd.py:895 data/ui/LedDialog.ui:395 msgid "Off" msgstr "Aus" #. Translators: the LED has a single, solid color. #: piper/ratbagd.py:897 data/ui/LedDialog.ui:66 msgid "Solid" msgstr "Solide" #. Translators: the LED is cycling between red, green and blue. #: piper/ratbagd.py:899 data/ui/LedDialog.ui:189 msgid "Cycle" msgstr "Zyklus" #. Translators: the LED's is pulsating a single color on different #. brightnesses. #: piper/ratbagd.py:902 data/ui/LedDialog.ui:355 msgid "Breathing" msgstr "Pulsierend" #: piper/window.py:54 msgid "Cannot connect to ratbagd" msgstr "Verbindung mit ratbagd fehlgeschlagen" #: piper/window.py:55 #, fuzzy msgid "" "Please make sure ratbagd is running and your user is in the required group" msgstr "" "Bitte vergewissern Sie sich dass ratbagd läuft und der Benutzer in der " "benötigten Gruppe ist" #: piper/window.py:58 msgid "Incompatible ratbagd API version (required: {}, provided: {})" msgstr "Inkompatible API version zu ratbagd (notwendig: {}, aktuell: {})" #: piper/window.py:59 #, fuzzy msgid "Please update both piper and libratbag to the latest versions" msgstr "Bitte sowohl piper als auch libratbag auf die neueste Version updaten." #: piper/window.py:73 piper/window.py:127 msgid "Cannot find any devices" msgstr "Es wurden keine Geräte gefunden" #: piper/window.py:74 piper/window.py:128 msgid "Please make sure your device is supported and plugged in" msgstr "" "Bitte vergewissern Sie sich dass Ihr Gerät angeschlossen ist und unterstützt " "wird" #: piper/window.py:86 msgid "There are unapplied changes. Are you sure you want to quit?" msgstr "Es gibt ungespeicherte Änderungen. Wollen Sie wirklich aussteigen?" #: piper/window.py:95 msgid "Ooops. ratbagd has disappeared" msgstr "Ups. ratbagd hat sich in Luft aufgelöst" #: piper/window.py:96 msgid "Please restart Piper" msgstr "Bitte starten Sie Piper von neuem" #. The current device disconnected, which can only happen from the #. mouse perspective as we'd otherwise be in the welcome screen with #. more than one device remaining. Hence, we display the error #. perspective. #: piper/window.py:119 msgid "Your device disconnected!" msgstr "Das Gerät wurde entfernt!" #: piper/window.py:120 msgid "Please make sure your device is plugged in" msgstr "Bitte vergewissern Sie sich dass Ihr Gerät angeschlossen ist" #: piper/window.py:159 msgid "Cannot display device SVG" msgstr "Das SVG des Gerätes kann nicht angezeigt werden" #: piper/window.py:166 msgid "Newer version of ratbagd required" msgstr "Eine neuere Version von ratbagd ist notwendig" #: piper/window.py:167 msgid "Please update to the latest available version" msgstr "Bitte auf die neueste Version updaten." #: piper/window.py:169 msgid "Unknown exception occurred" msgstr "Ein unbekannter Fehler ist aufgetreten" #: data/ui/AboutDialog.ui.in:10 msgid "Version: @version@" msgstr "Version: @version@" #: data/ui/AboutDialog.ui.in:12 #, fuzzy msgid "Visit Piper on GitHub" msgstr "Besuche Piper auf GitHub" #: data/ui/AboutDialog.ui.in:13 msgid "translator-credits" msgstr "Peter Hutterer , 2017-2019" #: data/ui/ButtonDialog.ui:65 msgid "Search for a button mapping" msgstr "Eine Tastenfunktion suchen" #: data/ui/ButtonDialog.ui:91 msgid "Capture a macro for this button" msgstr "Ein Marko fuer diese Taste aufnehmen" #: data/ui/ButtonDialog.ui:104 msgid "The currently set macro" msgstr "Das aktuelle Makro" #: data/ui/ButtonDialog.ui:129 msgid "Send keystroke" msgstr "Tastenkombination senden" #: data/ui/ButtonDialog.ui:169 msgid "Enter a new key sequence for the macro." msgstr "Bitte geben sie eine neue Tastenkombination für das Makro ein" #: data/ui/ButtonDialog.ui:221 msgid "Press Escape to cancel or Return to accept." msgstr "Drücken Sie Escape um abzubrechen oder Enter um zu akzeptieren" #: data/ui/ButtonDialog.ui:252 #, fuzzy msgid "Set the device’s handedness." msgstr "Gerät für Links- order Rechtshänder einstellen" #: data/ui/ButtonDialog.ui:270 msgid "" "This allows you to swap the order of the primary mouse buttons. In left-" "handed mode, the physical left mouse button generates a right mouse click " "and vice versa." msgstr "" "Ändern Sie die Funktion der primären Maustasten. Im Linkshändermodus sendet " "die linke Maustaste einen Klick für die rechte Maustaste und umgekehrt." #: data/ui/ButtonDialog.ui:290 msgid "Left Handed" msgstr "Linkshändig" #: data/ui/ButtonDialog.ui:295 msgid "The left mouse button generates a left mouse click" msgstr "Die linke Maustaste sendet einen Klick der linken Taste" #: data/ui/ButtonDialog.ui:309 msgid "Right Handed" msgstr "Rechtshändig" #: data/ui/ButtonDialog.ui:314 msgid "The left mouse button generates a right mouse click" msgstr "Die linke Maustaste sendet einen Klick der rechten Taste" #: data/ui/ButtonDialog.ui:354 data/ui/LedDialog.ui:414 msgid "Cancel" msgstr "Abbrechen" #: data/ui/ButtonDialog.ui:356 data/ui/LedDialog.ui:418 msgid "Ignore any changes made" msgstr "Alle Änderungen verwerfen" #: data/ui/ButtonDialog.ui:362 data/ui/LedDialog.ui:430 #: data/ui/MousePerspective.ui:132 msgid "Apply" msgstr "Zuweisen" #: data/ui/ButtonDialog.ui:365 data/ui/LedDialog.ui:435 msgid "Apply any changes made" msgstr "Alle Änderungen zuweisen" #: data/ui/ButtonDialog.ui:405 msgid "No button mapping found" msgstr "Keine Tastenfunktion gefunden" #: data/ui/ButtonDialog.ui:421 msgid "Try a different search" msgstr "Versuchen sie eine andere Suche" #: data/ui/ErrorPerspective.ui:16 #, fuzzy msgid "Here, rodent, rodent, rodent…" msgstr "Ja wo bist du denn, kleines Nagetier?" #: data/ui/ErrorPerspective.ui:69 #, fuzzy msgid "Uh Oh! Something went wrong…" msgstr "Ups. Da ist etwas falschgelaufen..." #: data/ui/ErrorPerspective.ui:73 data/ui/WelcomePerspective.ui:123 msgid "Quit" msgstr "Beenden" #: data/ui/LedDialog.ui:58 data/ui/LedDialog.ui:209 msgid "Choose a color for the LED" msgstr "Eine Farbe fuer die LED auswählen" #: data/ui/LedDialog.ui:90 data/ui/LedDialog.ui:257 msgid "Choose a brightness for the LED" msgstr "Helligkeit für die LED auswählen" #: data/ui/LedDialog.ui:104 data/ui/LedDialog.ui:270 msgid "Brightness" msgstr "Helligkeit" #: data/ui/LedDialog.ui:135 data/ui/LedDialog.ui:301 #, fuzzy msgid "Choose an effect duration for the LED" msgstr "Effektrate für die LED auswählen" #: data/ui/LedDialog.ui:153 data/ui/LedDialog.ui:319 msgid "ms" msgstr "ms" #: data/ui/LedDialog.ui:175 data/ui/LedDialog.ui:341 #, fuzzy msgid "Effect duration" msgstr "Effektrate" #: data/ui/LedDialog.ui:212 msgid "Pick a Color for the LED" msgstr "Eine Farbe fuer die LED auswählen" #: data/ui/LedDialog.ui:226 msgid "Color" msgstr "Farbe" #: data/ui/LedDialog.ui:380 msgid "This LED is off" msgstr "Diese LED ist ausgeschalten" #: data/ui/MousePerspective.ui:44 msgid "Something went wrong. The device has been reset to a previous state." msgstr "" "Irgendetwas ist falschgelaufen. Das Gerät wurde auf einen vorherigen Stand " "zurückgesetzt." #: data/ui/MousePerspective.ui:101 msgid "Select another profile" msgstr "Anderes Profile auswählen" #: data/ui/MousePerspective.ui:126 msgid "Commit the changes to the device" msgstr "Änderungen am Gerät speichern" #: data/ui/MousePerspective.ui:170 msgid "Click to switch to another profile" msgstr "Klicken Sie hier um auf ein anderes Profil zu wechseln" #: data/ui/MousePerspective.ui:185 msgid "Add profile" msgstr "Profil hinzufügen" #: data/ui/MousePerspective.ui:189 msgid "Add a new profile to the device" msgstr "Profil hinzufügen" #: data/ui/OptionButton.ui:9 msgid "Open the configuration dialog" msgstr "Das Konfigurationsfenster öffnen" #: data/ui/ProfileRow.ui:16 msgid "Remove this profile from the device" msgstr "Dieses Profil vom Gerät löschen" #. Translators: this is used to indicate the active resolution. #: data/ui/ResolutionRow.ui:37 msgid "Active" msgstr "Aktiv" #: data/ui/ResolutionRow.ui:77 #, fuzzy msgid "Set this resolution’s DPI" msgstr "Die Auflösung einstellen (in DPI)" #. Translators: the rate at which the device reports movement. #: data/ui/ResolutionsPage.ui:43 msgid "Report rate" msgstr "Abtastrate" #: data/ui/ResolutionsPage.ui:60 #, fuzzy msgid "Change the profile’s report rate" msgstr "Die Abtastrate für dieses Profil einstellen" #: data/ui/ResolutionsPage.ui:105 msgid "Hz" msgstr "Hz" #. Translators: the sensitivity of the device's sensor. #: data/ui/ResolutionsPage.ui:125 msgid "Sensitivity" msgstr "Auflösung" #: data/ui/ResolutionsPage.ui:174 msgid "Add a new resolution to the profile" msgstr "Eine neue Auflösung zum Profil hinzufügen" #: data/ui/WelcomePerspective.ui:20 msgid "Welcome" msgstr "Willkommen" #: data/ui/WelcomePerspective.ui:38 msgctxt "A subtitle under the \"Welcome\" title." msgid "Select a device to configure it" msgstr "Bitte ein Gerät zur Konfiguration auswählen" #: data/ui/WelcomePerspective.ui:89 msgctxt "A tooltip over the list of devices." msgid "Select a device to configure it" msgstr "Bitte ein Gerät zur Konfiguration auswählen" #: data/ui/WelcomePerspective.ui:119 msgid "Select a Device" msgstr "Bitte ein Gerät auswählen" #: data/ui/Window.ui:8 #, fuzzy msgid "_About Piper" msgstr "_Info" #~ msgid "Remove this resolution from the profile" #~ msgstr "Diese Auflösung vom Gerät löschen" #~ msgid "_Quit" #~ msgstr "_Beenden" #~ msgid "Failed to commit changes to the device" #~ msgstr "Das Speichern der Änderungen ist fehlgeschlagen" #~ msgid "Profile {}" #~ msgstr "Profil {}" piper-0.4/po/es.po000066400000000000000000000354771362011671000140760ustar00rootroot00000000000000# Spanish translation of Piper # Copyright (C) 2019 Piper Developers # This file is distributed under the same license as the piper package. # Amaro Martínez , 2019. # msgid "" msgstr "" "Project-Id-Version: piper\n" "Report-Msgid-Bugs-To: https://github.com/libratbag/piper/issues\n" "POT-Creation-Date: 2019-10-27 17:55-0500\n" "PO-Revision-Date: 2019-10-31 23:48-0500\n" "Last-Translator: Amaro Martínez \n" "Language-Team: Español; Castellano <>\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: Gtranslator 2.91.7\n" #: data/org.freedesktop.Piper.desktop.in:3 #: data/org.freedesktop.Piper.appdata.xml.in.in:7 msgid "Piper" msgstr "Piper" #: data/org.freedesktop.Piper.desktop.in:4 #: data/org.freedesktop.Piper.appdata.xml.in.in:8 msgid "Configurable mouse configuration utility" msgstr "Utilidad de configuración de ratón configurable" #. Translators: Do NOT translate or transliterate this text (this is an icon file name)! #: data/org.freedesktop.Piper.desktop.in:7 msgid "org.freedesktop.Piper" msgstr "org.freedesktop.Piper" #. Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! #: data/org.freedesktop.Piper.desktop.in:12 msgid "gaming;configuration;mouse;mice;" msgstr "gaming;configuración;ratón;ratones;" #: data/org.freedesktop.Piper.appdata.xml.in.in:10 msgid "" "Piper is a graphical user interface to configure gaming mice. Configuration " "options include changing the resolution (DPI) of the mouse, adding and " "removing profiles, setting LED colors and changing button behaviors." msgstr "" "Piper es una interfaz gráfica de usuario para configurar ratones «gaming». " "Las opciones de configuración incluyen cambiar la resolución PPP (DPI) del " "ratón, añadir y eliminar perfiles, configurar los colores de los LED y " "cambiar el comportamiento de los botones." #: data/org.freedesktop.Piper.appdata.xml.in.in:16 msgid "" "Piper requires libratbag’s ratbagd, the daemon to actually communicate with " "the mice. Piper is merely a front end to ratbagd, ratbagd must be installed " "and running when Piper is launched." msgstr "" "Piper necesita ratbagd de libratbag, el demonio, para poder comunicarse con " "los ratones. Piper es simplemente un frontal para ratbagd, ratbagd debe " "estar instalado y funcionando cuando inicie Piper." #: data/org.freedesktop.Piper.appdata.xml.in.in:33 msgid "The button configuraton page" msgstr "La página de configuración de botones" #: data/org.freedesktop.Piper.appdata.xml.in.in:37 msgid "The LED configuraton page" msgstr "La página de configuración de LEDs" #: data/org.freedesktop.Piper.appdata.xml.in.in:41 msgid "The resolution configuraton page" msgstr "La página de configuración de resolución" #. Translators: section header for mapping one button's click to another. #: piper/buttondialog.py:141 msgid "Button mapping" msgstr "Mapeado de botones" #. Translators: section header for assigning special functions to buttons. #: piper/buttondialog.py:150 msgid "Special mapping" msgstr "Mapeado especial" #. Translators: the {} will be replaced with the button index, e.g. #. "Button 1 click". #: piper/buttondialog.py:222 piper/buttonspage.py:90 msgid "Button {} click" msgstr "Pulsación del botón {}" #: piper/buttonspage.py:94 msgid "Macro: {}" msgstr "Macro: {}" #. Translators: the button is turned disabled, e.g. off. #: piper/buttonspage.py:97 msgid "Disabled" msgstr "Desactivado" #. Translators: the button has an unknown function. #: piper/buttonspage.py:100 piper/ratbagd.py:663 msgid "Unknown" msgstr "Desconocido" #: piper/buttonspage.py:108 msgid "Configure button {}" msgstr "Configurar botón {}" #: piper/mouseperspective.py:90 data/ui/ResolutionsPage.ui:209 msgid "Resolutions" msgstr "Resoluciones" #: piper/mouseperspective.py:92 msgid "Buttons" msgstr "Botones" #: piper/mouseperspective.py:94 msgid "LEDs" msgstr "LEDs" #: piper/ratbagd.py:653 msgid "Left mouse button click" msgstr "Pulsación del botón primario del ratón" #: piper/ratbagd.py:654 msgid "Right mouse button click" msgstr "Pulsación del botón secundario del ratón" #: piper/ratbagd.py:655 msgid "Middle mouse button click" msgstr "Pulsación del botón central del ratón" #: piper/ratbagd.py:656 msgid "Backward" msgstr "Atrás" #: piper/ratbagd.py:657 msgid "Forward" msgstr "Adelante" #: piper/ratbagd.py:662 msgid "Invalid" msgstr "No válida" #: piper/ratbagd.py:664 msgid "Doubleclick" msgstr "Doble pulsación" #: piper/ratbagd.py:665 msgid "Wheel Left" msgstr "Rueda a la izquierda" #: piper/ratbagd.py:666 msgid "Wheel Right" msgstr "Rueda a la derecha" #: piper/ratbagd.py:667 msgid "Wheel Up" msgstr "Rueda hacia arriba" #: piper/ratbagd.py:668 msgid "Wheel Down" msgstr "Rueda hacia abajo" #: piper/ratbagd.py:669 msgid "Ratchet Mode" msgstr "Modo de giro gradual" #: piper/ratbagd.py:670 msgid "Cycle Resolution Up" msgstr "Ciclo de resolución siguiente" #: piper/ratbagd.py:671 msgid "Cycle Resolution Down" msgstr "Ciclo de resolución anterior" #: piper/ratbagd.py:672 msgid "Resolution Up" msgstr "Aumentar resolución" #: piper/ratbagd.py:673 msgid "Resolution Down" msgstr "Reducir resolución" #: piper/ratbagd.py:674 msgid "Resolution Switch" msgstr "Cambiar resolución" #: piper/ratbagd.py:675 msgid "Default Resolution" msgstr "Resolución predeterminada" #: piper/ratbagd.py:676 msgid "Cycle Profile Up" msgstr "Ciclo de perfil siguiente" #: piper/ratbagd.py:677 msgid "Cycle Profile Down" msgstr "Ciclo de perfil anterior" #: piper/ratbagd.py:678 msgid "Profile Up" msgstr "Perfil siguiente" #: piper/ratbagd.py:679 msgid "Profile Down" msgstr "Perfil anterior" #: piper/ratbagd.py:680 msgid "Second Mode" msgstr "Segundo modo" #: piper/ratbagd.py:681 msgid "Battery Level" msgstr "Nivel de la batería" #. Translators: this is used when there is no macro to preview. #: piper/ratbagd.py:811 data/ui/ButtonDialog.ui:106 data/ui/ButtonDialog.ui:187 msgid "None" msgstr "Ninguno" #. Translators: the LED is off. #: piper/ratbagd.py:893 data/ui/LedDialog.ui:395 msgid "Off" msgstr "Apagado" #. Translators: the LED has a single, solid color. #: piper/ratbagd.py:895 data/ui/LedDialog.ui:66 msgid "Solid" msgstr "Sólido" #. Translators: the LED is cycling between red, green and blue. #: piper/ratbagd.py:897 data/ui/LedDialog.ui:189 msgid "Cycle" msgstr "Ciclo" #. Translators: the LED's is pulsating a single color on different #. brightnesses. #: piper/ratbagd.py:900 data/ui/LedDialog.ui:355 msgid "Breathing" msgstr "Respiración" #: piper/window.py:54 msgid "Cannot connect to ratbagd" msgstr "No se puede conectar a ratbagd" #: piper/window.py:55 msgid "Please make sure ratbagd is running" msgstr "Asegúrese de que ratbagd se esté ejecutando" #: piper/window.py:58 msgid "Incompatible ratbagd API version (required: {}, provided: {})" msgstr "" "Versión de la API ratbagd incompatible (necesaria: {}, proporcionada: {})" #: piper/window.py:59 msgid "Please update both piper and libratbag to the latest versions" msgstr "Actualice tanto piper como libratbag a las últimas versiones" #: piper/window.py:73 piper/window.py:127 msgid "Cannot find any devices" msgstr "No se puede encontrar ningún dispositivo" #: piper/window.py:74 piper/window.py:128 msgid "Please make sure your device is supported and plugged in" msgstr "Asegúrese de que su dispositivo es compatible y está conectado" #: piper/window.py:86 msgid "There are unapplied changes. Are you sure you want to quit?" msgstr "Hay cambios no aplicados. ¿Seguro que quiere salir?" #: piper/window.py:95 msgid "Ooops. ratbagd has disappeared" msgstr "Vaya. ratbagd ha desaparecido" #: piper/window.py:96 msgid "Please restart Piper" msgstr "Reinicie Piper" #. The current device disconnected, which can only happen from the #. mouse perspective as we'd otherwise be in the welcome screen with #. more than one device remaining. Hence, we display the error #. perspective. #: piper/window.py:119 msgid "Your device disconnected!" msgstr "¡Su dispositivo está desconectado!" #: piper/window.py:120 msgid "Please make sure your device is plugged in" msgstr "Asegúrese de que su dispositivo está conectado" #: piper/window.py:159 msgid "Cannot display device SVG" msgstr "No se puede mostrar el SVG del dispositivo" #: piper/window.py:166 msgid "Newer version of ratbagd required" msgstr "Se requiere una versión más reciente de ratbagd" #: piper/window.py:167 msgid "Please update to the latest available version" msgstr "Actualice a la última versión disponible" #: piper/window.py:169 msgid "Unknown exception occurred" msgstr "Ha ocurrido una excepción desconocida" #: data/ui/AboutDialog.ui.in:13 msgid "Visit Piper’s website" msgstr "Visitar la página web de Piper" #: data/ui/AboutDialog.ui.in:14 msgid "translator-credits" msgstr "Amaro Martínez , 2019" #: data/ui/ButtonDialog.ui:65 msgid "Search for a button mapping" msgstr "Buscar un mapeado de botón" #: data/ui/ButtonDialog.ui:91 msgid "Capture a macro for this button" msgstr "Capturar una macro para este botón" #: data/ui/ButtonDialog.ui:104 msgid "The currently set macro" msgstr "La macro actualmente establecida" #: data/ui/ButtonDialog.ui:129 msgid "Send keystroke" msgstr "Enviar pulsación de teclas" #: data/ui/ButtonDialog.ui:169 msgid "Enter a new key sequence for the macro." msgstr "Introduzca una combinación de teclas nueva para la macro." #: data/ui/ButtonDialog.ui:221 msgid "Press Escape to cancel or Return to accept." msgstr "Pulse Esc para cancelar o Intro para aceptar." #: data/ui/ButtonDialog.ui:252 msgid "Set the device’s handedness." msgstr "Establezca la mano dominante del dispositivo." #: data/ui/ButtonDialog.ui:270 msgid "" "This allows you to swap the order of the primary mouse buttons. In left-" "handed mode, the physical left mouse button generates a right mouse click " "and vice versa." msgstr "" "Esto le permite cambiar el orden de los botones principales del ratón. En " "modo zurdo, el botón primario físico del ratón genera una pulsación " "secundaria del ratón y viceversa." #: data/ui/ButtonDialog.ui:290 msgid "Left Handed" msgstr "Zurdo" #: data/ui/ButtonDialog.ui:295 msgid "The left mouse button generates a left mouse click" msgstr "El botón primario del ratón genera una pulsación primaria del ratón" #: data/ui/ButtonDialog.ui:309 msgid "Right Handed" msgstr "Diestro" #: data/ui/ButtonDialog.ui:314 msgid "The left mouse button generates a right mouse click" msgstr "El botón primario del ratón genera una pulsación secundaria del ratón" #: data/ui/ButtonDialog.ui:354 data/ui/LedDialog.ui:414 msgid "Cancel" msgstr "Cancelar" #: data/ui/ButtonDialog.ui:356 data/ui/LedDialog.ui:418 msgid "Ignore any changes made" msgstr "Ignorar los cambios realizados" #: data/ui/ButtonDialog.ui:362 data/ui/LedDialog.ui:430 #: data/ui/MousePerspective.ui:132 msgid "Apply" msgstr "Aplicar" #: data/ui/ButtonDialog.ui:365 data/ui/LedDialog.ui:435 msgid "Apply any changes made" msgstr "Aplicar los cambios realizados" #: data/ui/ButtonDialog.ui:405 msgid "No button mapping found" msgstr "No se ha encontrado ningún mapeado de botón" #: data/ui/ButtonDialog.ui:421 msgid "Try a different search" msgstr "Pruebe a hacer una búsqueda diferente" #: data/ui/ErrorPerspective.ui:16 msgid "Here, rodent, rodent, rodent…" msgstr "Aquí, roedor, roedor, roedor…" #: data/ui/ErrorPerspective.ui:69 msgid "Uh Oh! Something went wrong…" msgstr "Algo ha fallado…" #: data/ui/ErrorPerspective.ui:73 data/ui/WelcomePerspective.ui:123 msgid "Quit" msgstr "Salir" #: data/ui/LedDialog.ui:58 data/ui/LedDialog.ui:209 msgid "Choose a color for the LED" msgstr "Elija un color para el LED" #: data/ui/LedDialog.ui:90 data/ui/LedDialog.ui:257 msgid "Choose a brightness for the LED" msgstr "Elija un brillo para el LED" #: data/ui/LedDialog.ui:104 data/ui/LedDialog.ui:270 msgid "Brightness" msgstr "Brillo" #: data/ui/LedDialog.ui:135 data/ui/LedDialog.ui:301 msgid "Choose an effect duration for the LED" msgstr "Elija una duración de efecto para el LED" #: data/ui/LedDialog.ui:153 data/ui/LedDialog.ui:319 msgid "ms" msgstr "ms" #: data/ui/LedDialog.ui:175 data/ui/LedDialog.ui:341 msgid "Effect duration" msgstr "Duración del efecto" #: data/ui/LedDialog.ui:212 msgid "Pick a Color for the LED" msgstr "Elegir un color para el LED" #: data/ui/LedDialog.ui:226 msgid "Color" msgstr "Color" #: data/ui/LedDialog.ui:380 msgid "This LED is off" msgstr "Este LED está apagado" #: data/ui/MousePerspective.ui:44 msgid "Something went wrong. The device has been reset to a previous state." msgstr "" "Algo salió mal. El dispositivo se ha restablecido a un estado anterior." #: data/ui/MousePerspective.ui:101 msgid "Select another profile" msgstr "Seleccione otro perfil" #: data/ui/MousePerspective.ui:126 msgid "Commit the changes to the device" msgstr "Efectuar los cambios en el dispositivo" #: data/ui/MousePerspective.ui:170 msgid "Click to switch to another profile" msgstr "Pulse para cambiar a otro perfil" #: data/ui/MousePerspective.ui:185 msgid "Add profile" msgstr "Añadir perfil" #: data/ui/MousePerspective.ui:189 msgid "Add a new profile to the device" msgstr "Añadir un nuevo perfil al dispositivo" #: data/ui/OptionButton.ui:9 msgid "Open the configuration dialog" msgstr "Abre el diálogo de configuración" #: data/ui/ProfileRow.ui:16 msgid "Remove this profile from the device" msgstr "Quite este perfil del dispositivo" #. Translators: this is used to indicate the active resolution. #: data/ui/ResolutionRow.ui:37 msgid "Active" msgstr "Activo" #: data/ui/ResolutionRow.ui:77 msgid "Set this resolution’s DPI" msgstr "Establezca el DPI de esta resolución" #. Translators: the rate at which the device reports movement. #: data/ui/ResolutionsPage.ui:43 msgid "Report rate" msgstr "Velocidad de respuesta" #: data/ui/ResolutionsPage.ui:60 msgid "Change the profile’s report rate" msgstr "Cambia la velocidad de respuesta del perfil" #: data/ui/ResolutionsPage.ui:105 msgid "Hz" msgstr "Hz" #. Translators: the sensitivity of the device's sensor. #: data/ui/ResolutionsPage.ui:125 msgid "Sensitivity" msgstr "Sensibilidad" #: data/ui/ResolutionsPage.ui:174 msgid "Add a new resolution to the profile" msgstr "Añadir una nueva resolución al perfil" #: data/ui/WelcomePerspective.ui:20 msgid "Welcome" msgstr "Bienvenido" #: data/ui/WelcomePerspective.ui:38 msgctxt "A subtitle under the \"Welcome\" title." msgid "Select a device to configure it" msgstr "Seleccionar un dispositivo para configurarlo" #: data/ui/WelcomePerspective.ui:89 msgctxt "A tooltip over the list of devices." msgid "Select a device to configure it" msgstr "Seleccione un dispositivo para configurarlo" #: data/ui/WelcomePerspective.ui:119 msgid "Select a Device" msgstr "Seleccione un dispositivo" #: data/ui/Window.ui:8 msgid "_About Piper" msgstr "_Acerca de Piper" piper-0.4/po/fr.po000066400000000000000000000351061362011671000140630ustar00rootroot00000000000000# This file is distributed under the same license as the piper package. # msgid "" msgstr "" "Project-Id-Version: piper\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2019-07-12 22:45+0200\n" "PO-Revision-Date: 2020-01-20 20:30+1000\n" "Last-Translator: Launay Théo \n" "Language-Team: French <>\n" "Language: fr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: data/org.freedesktop.Piper.desktop.in:3 #: data/org.freedesktop.Piper.appdata.xml.in.in:7 msgid "Piper" msgstr "Piper" #: data/org.freedesktop.Piper.desktop.in:4 #: data/org.freedesktop.Piper.appdata.xml.in.in:8 msgid "Configurable mouse configuration utility" msgstr "Outil de configuration de souris programmable" #. Translators: Do NOT translate or transliterate this text (this is an icon file name)! #: data/org.freedesktop.Piper.desktop.in:7 msgid "org.freedesktop.Piper" msgstr "org.freedesktop.Piper" #. Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! #: data/org.freedesktop.Piper.desktop.in:12 msgid "gaming;configuration;mouse;mice;" msgstr "Gaming;Games;Configuration;Souris;Paramètres;" #: data/org.freedesktop.Piper.appdata.xml.in.in:10 msgid "" "Piper is a graphical user interface to configure gaming mice. Configuration " "options include changing the resolution (DPI) of the mouse, adding and " "removing profiles, setting LED colors and changing button behaviors." msgstr "" "Piper est une interface graphique pour configurer votre souris. Les " "options de configuration comprennent changer la resolution (DPI) de la " "souris, ajouter et supprimer des profils, définir la couleurs des LEDs et " "changer le comportement des boutons." #: data/org.freedesktop.Piper.appdata.xml.in.in:16 msgid "" "Piper requires libratbag’s ratbagd, the daemon to actually communicate with " "the mice. Piper is merely a front end to ratbagd, ratbagd must be installed " "and running when Piper is launched." msgstr "" "Piper a besoin de ratbagd, le démon de libratbag qui permet de communiquer" "avec la souris. Piper est seulement une interface à ratbagd. ratbagd doit donc " "être installé, et doit être exécuté quand Piper est lancé." #: data/org.freedesktop.Piper.appdata.xml.in.in:33 #, fuzzy msgid "The button configuraton page" msgstr "La page de configuration des boutons" #: data/org.freedesktop.Piper.appdata.xml.in.in:37 #, fuzzy msgid "The LED configuraton page" msgstr "La page de configuration des LEDs" #: data/org.freedesktop.Piper.appdata.xml.in.in:41 #, fuzzy msgid "The resolution configuraton page" msgstr "La page de configuration de la résolution" #. Translators: section header for mapping one button's click to another. #: piper/buttondialog.py:141 msgid "Button mapping" msgstr "Mappage des boutons" #. Translators: section header for assigning special functions to buttons. #: piper/buttondialog.py:150 msgid "Special mapping" msgstr "Mappage spécial" #. Translators: the {} will be replaced with the button index, e.g. #. "Button 1 click". #: piper/buttondialog.py:222 piper/buttonspage.py:90 msgid "Button {} click" msgstr "clic du bouton {}" #: piper/buttonspage.py:94 msgid "Macro: {}" msgstr "Macro: {}" #. Translators: the button is turned disabled, e.g. off. #: piper/buttonspage.py:97 msgid "Disabled" msgstr "Désactivé" #. Translators: the button has an unknown function. #: piper/buttonspage.py:100 piper/ratbagd.py:641 msgid "Unknown" msgstr "Inconnue" #: piper/buttonspage.py:108 msgid "Configure button {}" msgstr "configurer le bouton {}" #: piper/mouseperspective.py:90 data/ui/ResolutionsPage.ui:209 msgid "Resolutions" msgstr "Résolutions" #: piper/mouseperspective.py:92 msgid "Buttons" msgstr "Boutons" #: piper/mouseperspective.py:94 msgid "LEDs" msgstr "LEDs" #: piper/ratbagd.py:631 msgid "Left mouse button click" msgstr "Clic bouton gauche" #: piper/ratbagd.py:632 msgid "Right mouse button click" msgstr "Clic bouton droit" #: piper/ratbagd.py:633 msgid "Middle mouse button click" msgstr "Clic bouton du milieu" #: piper/ratbagd.py:634 msgid "Backward" msgstr "En arrière" #: piper/ratbagd.py:635 msgid "Forward" msgstr "En avant" #: piper/ratbagd.py:640 msgid "Invalid" msgstr "Invalide" #: piper/ratbagd.py:642 msgid "Doubleclick" msgstr "Double clic" #: piper/ratbagd.py:643 msgid "Wheel Left" msgstr "Molette gauche" #: piper/ratbagd.py:644 msgid "Wheel Right" msgstr "Molette droite" #: piper/ratbagd.py:645 msgid "Wheel Up" msgstr "Molette Haut" #: piper/ratbagd.py:646 msgid "Wheel Down" msgstr "Molette Bas" #: piper/ratbagd.py:647 msgid "Ratchet Mode" msgstr "Mode Précision" #: piper/ratbagd.py:648 msgid "Cycle Resolution Up" msgstr "Cycle la résolution vers le haut" #: piper/ratbagd.py:649 msgid "Cycle Resolution Down" msgstr "Cycle la résolution vers le bas" #: piper/ratbagd.py:650 msgid "Resolution Up" msgstr "Augmente la résolution" #: piper/ratbagd.py:651 msgid "Resolution Down" msgstr "Diminue la résolution" #: piper/ratbagd.py:652 msgid "Resolution Switch" msgstr "Change la résolution" #: piper/ratbagd.py:653 msgid "Default Resolution" msgstr "Résolution par défaut" #: piper/ratbagd.py:654 msgid "Cycle Profile Up" msgstr "Cycle les profils vers le haut" #: piper/ratbagd.py:655 msgid "Cycle Profile Down" msgstr "Cycle les profils vers le bas" #: piper/ratbagd.py:656 msgid "Profile Up" msgstr "Profil suivant" #: piper/ratbagd.py:657 msgid "Profile Down" msgstr "Profil précédent" #: piper/ratbagd.py:658 msgid "Second Mode" msgstr "Second mode" #: piper/ratbagd.py:659 msgid "Battery Level" msgstr "Niveau de batterie" #. Translators: this is used when there is no macro to preview. #: piper/ratbagd.py:789 data/ui/ButtonDialog.ui:106 data/ui/ButtonDialog.ui:187 msgid "None" msgstr "Rien" #. Translators: the LED is off. #: piper/ratbagd.py:869 data/ui/LedDialog.ui:395 msgid "Off" msgstr "Désactivé" #. Translators: the LED has a single, solid color. #: piper/ratbagd.py:871 data/ui/LedDialog.ui:66 msgid "Solid" msgstr "Solide" #. Translators: the LED is cycling between red, green and blue. #: piper/ratbagd.py:873 data/ui/LedDialog.ui:189 msgid "Cycle" msgstr "Cycle" #. Translators: the LED's is pulsating a single color on different #. brightnesses. #: piper/ratbagd.py:876 data/ui/LedDialog.ui:355 msgid "Breathing" msgstr "Pulsations" #: piper/window.py:51 msgid "Cannot connect to ratbagd" msgstr "Impossible de se connecter à ratbagd" #: piper/window.py:52 msgid "Please make sure ratbagd is running" msgstr "Merci de vérifier que ratbagd est lancé" #: piper/window.py:66 piper/window.py:120 msgid "Cannot find any devices" msgstr "Impossible de trouver un périphérique" #: piper/window.py:67 piper/window.py:121 msgid "Please make sure your device is supported and plugged in" msgstr "Merci de vérifier que votre périphérique est supporté et branché" #: piper/window.py:79 msgid "There are unapplied changes. Are you sure you want to quit?" msgstr "Les changements ne sont pas appliqués. Voulez-vous quitter quand même?" #: piper/window.py:88 msgid "Ooops. ratbagd has disappeared" msgstr "Oups. ratbagd a disparu" #: piper/window.py:89 msgid "Please restart Piper" msgstr "Veuillez redémarrer Piper" #. The current device disconnected, which can only happen from the #. mouse perspective as we'd otherwise be in the welcome screen with #. more than one device remaining. Hence, we display the error #. perspective. #: piper/window.py:112 msgid "Your device disconnected!" msgstr "Votre périphérique s'est déconnecté!" #: piper/window.py:113 msgid "Please make sure your device is plugged in" msgstr "Veuillez vérifier que votre périphérique est branché" #: piper/window.py:152 msgid "Cannot display device SVG" msgstr "Le SVG de l'appareil ne peut être affiché" #: piper/window.py:159 msgid "Newer version of ratbagd required" msgstr "Une version plus récente de ratbagd est requise" #: piper/window.py:160 msgid "Please update to the latest available version" msgstr "Veuillez mettre à jour à la version la plus récente." #: piper/window.py:162 msgid "Unknown exception occurred" msgstr "Une erreur inconnue s'est produite" #: data/ui/AboutDialog.ui.in:13 #, fuzzy msgid "Visit Piper’s website" msgstr "Visiter le site de Piper" #: data/ui/AboutDialog.ui.in:36 msgid "translator-credits" msgstr "Mallory Macrez , 2019" #: data/ui/ButtonDialog.ui:65 msgid "Search for a button mapping" msgstr "Chercher une fonction" #: data/ui/ButtonDialog.ui:91 msgid "Capture a macro for this button" msgstr "Enregistrer une macro pour ce bouton" #: data/ui/ButtonDialog.ui:104 msgid "The currently set macro" msgstr "la macro actuellement définie" #: data/ui/ButtonDialog.ui:129 msgid "Send keystroke" msgstr "Combinaison envoyée" #: data/ui/ButtonDialog.ui:169 msgid "Enter a new key sequence for the macro." msgstr "Entrez une nouvelle séquence de touches pour la macro." #: data/ui/ButtonDialog.ui:221 msgid "Press Escape to cancel or Return to accept." msgstr "Appuyez sur Echap pour annuler ou Entrée pour accepter." #: data/ui/ButtonDialog.ui:252 #, fuzzy msgid "Set the device’s handedness." msgstr "Définir pour les droitiers ou gauchers " #: data/ui/ButtonDialog.ui:270 msgid "" "This allows you to swap the order of the primary mouse buttons. In left-" "handed mode, the physical left mouse button generates a right mouse click " "and vice versa." msgstr "" "Ceci vous permet de changer l'ordre des boutons primaires de la souris. En " "mode gaucher, le bouton physique de gauche génère un clique droit, et " "inversement." #: data/ui/ButtonDialog.ui:290 msgid "Left Handed" msgstr "Gaucher" #: data/ui/ButtonDialog.ui:295 msgid "The left mouse button generates a left mouse click" msgstr "Le bouton gauche génère un clique gauche" #: data/ui/ButtonDialog.ui:309 msgid "Right Handed" msgstr "Droitier" #: data/ui/ButtonDialog.ui:314 msgid "The left mouse button generates a right mouse click" msgstr "Le bouton gauche génère un clique droit" #: data/ui/ButtonDialog.ui:354 data/ui/LedDialog.ui:414 msgid "Cancel" msgstr "Annuler" #: data/ui/ButtonDialog.ui:356 data/ui/LedDialog.ui:418 msgid "Ignore any changes made" msgstr "Ignorer les modifications" #: data/ui/ButtonDialog.ui:362 data/ui/LedDialog.ui:430 #: data/ui/MousePerspective.ui:132 msgid "Apply" msgstr "Appliquer" #: data/ui/ButtonDialog.ui:365 data/ui/LedDialog.ui:435 msgid "Apply any changes made" msgstr "Appliquer les changements" #: data/ui/ButtonDialog.ui:405 msgid "No button mapping found" msgstr "Aucune fonction trouvée" #: data/ui/ButtonDialog.ui:421 msgid "Try a different search" msgstr "Essayez une recherche différente" #: data/ui/ErrorPerspective.ui:16 #, fuzzy msgid "Here, rodent, rodent, rodent…" msgstr "Où es-tu petit rongeur?" #: data/ui/ErrorPerspective.ui:69 #, fuzzy msgid "Uh Oh! Something went wrong…" msgstr "Oups, quelquechose s'est mal passé" #: data/ui/ErrorPerspective.ui:73 data/ui/WelcomePerspective.ui:123 msgid "Quit" msgstr "Quitter" #: data/ui/LedDialog.ui:58 data/ui/LedDialog.ui:209 msgid "Choose a color for the LED" msgstr "Choisissez une couleur pour la LED" #: data/ui/LedDialog.ui:90 data/ui/LedDialog.ui:257 msgid "Choose a brightness for the LED" msgstr "Choisissez une luminosité pour la LED" #: data/ui/LedDialog.ui:104 data/ui/LedDialog.ui:270 msgid "Brightness" msgstr "Luminosité" #: data/ui/LedDialog.ui:135 data/ui/LedDialog.ui:301 #, fuzzy msgid "Choose an effect duration for the LED" msgstr "Choisissez une durée d'effet pour la LED" #: data/ui/LedDialog.ui:153 data/ui/LedDialog.ui:319 msgid "ms" msgstr "ms" #: data/ui/LedDialog.ui:175 data/ui/LedDialog.ui:341 #, fuzzy msgid "Effect duration" msgstr "Durée de l'effet" #: data/ui/LedDialog.ui:212 msgid "Pick a Color for the LED" msgstr "Sélectionnez une couleur pour la LED" #: data/ui/LedDialog.ui:226 msgid "Color" msgstr "Couleur" #: data/ui/LedDialog.ui:380 msgid "This LED is off" msgstr "Cette led est éteinte" #: data/ui/MousePerspective.ui:44 msgid "Something went wrong. The device has been reset to a previous state." msgstr "" "Quelque chose s'est mal passé. Le périphérique a été remis à un état " "antérieur." #: data/ui/MousePerspective.ui:101 msgid "Select another profile" msgstr "Choisir un autre profil" #: data/ui/MousePerspective.ui:126 msgid "Commit the changes to the device" msgstr "Appliquer les changement au périphérique" #: data/ui/MousePerspective.ui:170 msgid "Click to switch to another profile" msgstr "Cliquer pour changer vers un autre profil" #: data/ui/MousePerspective.ui:185 msgid "Add profile" msgstr "Ajouter un profile" #: data/ui/MousePerspective.ui:189 msgid "Add a new profile to the device" msgstr "Ajouter un profil au périphérique" #: data/ui/OptionButton.ui:9 msgid "Open the configuration dialog" msgstr "Ouvrir le panneau de configuration" #: data/ui/ProfileRow.ui:16 msgid "Remove this profile from the device" msgstr "Enlever ce profil du périphérique" #. Translators: this is used to indicate the active resolution. #: data/ui/ResolutionRow.ui:37 msgid "Active" msgstr "Active" #: data/ui/ResolutionRow.ui:77 #, fuzzy msgid "Set this resolution’s DPI" msgstr "Définir le DPI de cette résolution " #. Translators: the rate at which the device reports movement. #: data/ui/ResolutionsPage.ui:43 msgid "Report rate" msgstr "Fréquence de rapport" #: data/ui/ResolutionsPage.ui:60 #, fuzzy msgid "Change the profile’s report rate" msgstr "Changer la fréquence de rapport de ce profil" #: data/ui/ResolutionsPage.ui:105 msgid "Hz" msgstr "Hz" #. Translators: the sensitivity of the device's sensor. #: data/ui/ResolutionsPage.ui:125 msgid "Sensitivity" msgstr "Sensibilité" #: data/ui/ResolutionsPage.ui:174 msgid "Add a new resolution to the profile" msgstr "Ajouter une nouvelle résolution au profil" #: data/ui/WelcomePerspective.ui:20 msgid "Welcome" msgstr "Bienvenue" #: data/ui/WelcomePerspective.ui:38 msgctxt "A subtitle under the \"Welcome\" title." msgid "Select a device to configure it" msgstr "Sélectionnez un périphérique pour le configurer" #: data/ui/WelcomePerspective.ui:89 msgctxt "A tooltip over the list of devices." msgid "Select a device to configure it" msgstr "Sélectionnez un périphérique pour le configurer" #: data/ui/WelcomePerspective.ui:119 msgid "Select a Device" msgstr "Sélectionnez un périphérique" #: data/ui/Window.ui:8 #, fuzzy msgid "_About Piper" msgstr "_Info" #~ msgid "Remove this resolution from the profile" #~ msgstr "Enlever cette résolution du périphérique" #~ msgid "_Quit" #~ msgstr "_Beenden" #~ msgid "Failed to commit changes to the device" #~ msgstr "Das Speichern der Änderungen ist fehlgeschlagen" #~ msgid "Profile {}" #~ msgstr "Profil {}" piper-0.4/po/hr.po000066400000000000000000000366351362011671000140750ustar00rootroot00000000000000# Croatian Piper translation # Copyright (C) 2017 gogo # This file is distributed under the same license as the piper package. # gogo , 2017. # msgid "" msgstr "" "Project-Id-Version: piper\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2020-01-24 12:46+0100\n" "PO-Revision-Date: 2020-01-24 13:03+0100\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" "X-Generator: Poedit 2.0.6\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" #: data/org.freedesktop.Piper.desktop.in:3 #: data/org.freedesktop.Piper.appdata.xml.in.in:7 msgid "Piper" msgstr "Piper" #: data/org.freedesktop.Piper.desktop.in:4 #: data/org.freedesktop.Piper.appdata.xml.in.in:8 msgid "Configurable mouse configuration utility" msgstr "Pomagalo za podešavanje miša" #. Translators: Do NOT translate or transliterate this text (this is an icon file name)! #: data/org.freedesktop.Piper.desktop.in:7 msgid "org.freedesktop.Piper" msgstr "org.freedesktop.Piper" #. Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! #: data/org.freedesktop.Piper.desktop.in:12 msgid "gaming;configuration;mouse;mice;" msgstr "igranje;prilagodba;podešavanje;miš;" #: data/org.freedesktop.Piper.appdata.xml.in.in:10 msgid "" "Piper is a graphical user interface to configure gaming mice. Configuration " "options include changing the resolution (DPI) of the mouse, adding and " "removing profiles, setting LED colors and changing button behaviors." msgstr "" "Piper je korisničko grafičko sučelje za podešavanje miševa namijenjenih " "igranju. Mogućnosti podešavanja uključuju promjenu razlučivosti (DPI) miša, " "dodavanje i uklanjanje profila, prilagodba LED boja i ponašanja tipka." #: data/org.freedesktop.Piper.appdata.xml.in.in:16 msgid "" "Piper requires libratbag’s ratbagd, the daemon to actually communicate with " "the mice. Piper is merely a front end to ratbagd, ratbagd must be installed " "and running when Piper is launched." msgstr "" "Piper zahtijeva libratbagov ratbag, pozadinski program za komunikaciju s " "mišem. Piper je prednji program za ratbagd, ratbagd mora biti instaliran i " "pokrenut kada se Piper pokrene." #: data/org.freedesktop.Piper.appdata.xml.in.in:33 msgid "The button configuraton page" msgstr "Stranica podešavanja tipki" #: data/org.freedesktop.Piper.appdata.xml.in.in:37 msgid "The LED configuraton page" msgstr "Stranica LED podešavanja" #: data/org.freedesktop.Piper.appdata.xml.in.in:41 msgid "The resolution configuraton page" msgstr "Stranica podešavanja razlučivosti" #. Translators: section header for mapping one button's click to another. #: piper/buttondialog.py:141 msgid "Button mapping" msgstr "Mapiranje tipki" #. Translators: section header for assigning special functions to buttons. #: piper/buttondialog.py:150 msgid "Special mapping" msgstr "Posebno mapiranje" #. Translators: the {} will be replaced with the button index, e.g. #. "Button 1 click". #: piper/buttondialog.py:222 piper/buttonspage.py:90 msgid "Button {} click" msgstr "Tipka {} klik" #: piper/buttonspage.py:94 msgid "Macro: {}" msgstr "Makro: {}" #. Translators: the button is turned disabled, e.g. off. #: piper/buttonspage.py:97 msgid "Disabled" msgstr "Onemogućeno" #. Translators: the button has an unknown function. #: piper/buttonspage.py:100 piper/ratbagd.py:665 msgid "Unknown" msgstr "Nepoznato" #: piper/buttonspage.py:108 msgid "Configure button {}" msgstr "Prilagodi tipku {}" #: piper/mouseperspective.py:90 data/ui/ResolutionsPage.ui:209 msgid "Resolutions" msgstr "Razlučivost" #: piper/mouseperspective.py:92 msgid "Buttons" msgstr "Tipke" #: piper/mouseperspective.py:94 msgid "LEDs" msgstr "LED" #: piper/ratbagd.py:655 msgid "Left mouse button click" msgstr "Lijeva tipka klika miša" #: piper/ratbagd.py:656 msgid "Right mouse button click" msgstr "Desna tipka klika miša" #: piper/ratbagd.py:657 msgid "Middle mouse button click" msgstr "Srednja tipka klika miša" #: piper/ratbagd.py:658 msgid "Backward" msgstr "Natrag" #: piper/ratbagd.py:659 msgid "Forward" msgstr "Naprijed" #: piper/ratbagd.py:664 msgid "Invalid" msgstr "Neispravno" #: piper/ratbagd.py:666 msgid "Doubleclick" msgstr "Dvostruki klik" #: piper/ratbagd.py:667 msgid "Wheel Left" msgstr "Kotačić lijevo" #: piper/ratbagd.py:668 msgid "Wheel Right" msgstr "Kotačić desno" #: piper/ratbagd.py:669 msgid "Wheel Up" msgstr "Kotačić gore" #: piper/ratbagd.py:670 msgid "Wheel Down" msgstr "Kotačić dolje" #: piper/ratbagd.py:671 msgid "Ratchet Mode" msgstr "Zaustavljački način" #: piper/ratbagd.py:672 msgid "Cycle Resolution Up" msgstr "Povećaj kružnu razlučivost" #: piper/ratbagd.py:673 msgid "Cycle Resolution Down" msgstr "Smanji kružnu razlučivost" #: piper/ratbagd.py:674 msgid "Resolution Up" msgstr "Povećaj razlučivost" #: piper/ratbagd.py:675 msgid "Resolution Down" msgstr "Smanji razlučivost" #: piper/ratbagd.py:676 msgid "Resolution Switch" msgstr "Prebaci razlučivosti" #: piper/ratbagd.py:677 msgid "Default Resolution" msgstr "Zadana razlučivost" #: piper/ratbagd.py:678 msgid "Cycle Profile Up" msgstr "Veći kružni profil" #: piper/ratbagd.py:679 msgid "Cycle Profile Down" msgstr "Manji kružni profil" #: piper/ratbagd.py:680 msgid "Profile Up" msgstr "Veći profil" #: piper/ratbagd.py:681 msgid "Profile Down" msgstr "Manji profil" #: piper/ratbagd.py:682 msgid "Second Mode" msgstr "Pomoćni način" #: piper/ratbagd.py:683 msgid "Battery Level" msgstr "Razina baterije" #. Translators: this is used when there is no macro to preview. #: piper/ratbagd.py:813 data/ui/ButtonDialog.ui:106 data/ui/ButtonDialog.ui:187 msgid "None" msgstr "Nedodjeljeno" #. Translators: the LED is off. #: piper/ratbagd.py:895 data/ui/LedDialog.ui:395 msgid "Off" msgstr "Isključeno" #. Translators: the LED has a single, solid color. #: piper/ratbagd.py:897 data/ui/LedDialog.ui:66 msgid "Solid" msgstr "Nepromjenjivo" #. Translators: the LED is cycling between red, green and blue. #: piper/ratbagd.py:899 data/ui/LedDialog.ui:189 msgid "Cycle" msgstr "Kružno" #. Translators: the LED's is pulsating a single color on different #. brightnesses. #: piper/ratbagd.py:902 data/ui/LedDialog.ui:355 msgid "Breathing" msgstr "Pulsirajuće" #: piper/window.py:54 msgid "Cannot connect to ratbagd" msgstr "Nemoguće povezivanje s ratbagdom" #: piper/window.py:55 msgid "" "Please make sure ratbagd is running and your user is in the required group" msgstr "" "Pobrinite se da je ratbagd pokrenuti i da je vaše korisničko ime dodano " "zahtijevanoj grupi" #: piper/window.py:58 msgid "Incompatible ratbagd API version (required: {}, provided: {})" msgstr "Nekompatibilna ratbagd API inačica (zahtijevana: {}, omogućena: {})" #: piper/window.py:59 msgid "Please update both piper and libratbag to the latest versions" msgstr "Nadopunite piper i libratbag na najnoviju dostupnu inačicu" #: piper/window.py:73 piper/window.py:127 msgid "Cannot find any devices" msgstr "Nemoguć pronalazak prikladnih uređaja" #: piper/window.py:74 piper/window.py:128 msgid "Please make sure your device is supported and plugged in" msgstr "Pobrinite se da je vaš uređaj podržan i priključen" #: piper/window.py:86 msgid "There are unapplied changes. Are you sure you want to quit?" msgstr "Postoje neprimijenjene promjene. Sigurno želite zatvoriti?" #: piper/window.py:95 msgid "Ooops. ratbagd has disappeared" msgstr "Ups, ratbagd je nestao" #: piper/window.py:96 msgid "Please restart Piper" msgstr "Ponovno pokrenite Piper" #. The current device disconnected, which can only happen from the #. mouse perspective as we'd otherwise be in the welcome screen with #. more than one device remaining. Hence, we display the error #. perspective. #: piper/window.py:119 msgid "Your device disconnected!" msgstr "Vaš uređaj nije priključen!" #: piper/window.py:120 msgid "Please make sure your device is plugged in" msgstr "Pobrinite se da je vaš uređaj priključen" #: piper/window.py:159 msgid "Cannot display device SVG" msgstr "Nemoguć prikaz SVG uređaja" #: piper/window.py:166 msgid "Newer version of ratbagd required" msgstr "Potrebna je nova inačica ratbagda" #: piper/window.py:167 msgid "Please update to the latest available version" msgstr "Nadopunite na najnoviju dostupnu inačicu" #: piper/window.py:169 msgid "Unknown exception occurred" msgstr "Dogodila se nepoznata iznimka" #: data/ui/AboutDialog.ui.in:12 msgid "Visit Piper’s website" msgstr "Posjetite Piper web stranicu" #: data/ui/AboutDialog.ui.in:13 msgid "translator-credits" msgstr "" "Launchpad Contributions:\n" " gogo https://launchpad.net/~trebelnik-stefina" #: data/ui/ButtonDialog.ui:65 msgid "Search for a button mapping" msgstr "Pretraži mapiranje tipki" #: data/ui/ButtonDialog.ui:91 msgid "Capture a macro for this button" msgstr "Uhvati makro za ovu tipku" #: data/ui/ButtonDialog.ui:104 msgid "The currently set macro" msgstr "Trenutno postavljeni makro" #: data/ui/ButtonDialog.ui:129 msgid "Send keystroke" msgstr "Pošalji pritisak tipke" #: data/ui/ButtonDialog.ui:169 msgid "Enter a new key sequence for the macro." msgstr "Unesi novi slijed tipke za makro." #: data/ui/ButtonDialog.ui:221 msgid "Press Escape to cancel or Return to accept." msgstr "Pritisni Escape za prekidanje ili Return za prihvaćanje." #: data/ui/ButtonDialog.ui:252 msgid "Set the device’s handedness." msgstr "Postavite za ljevu/desnu ruku uređaja." #: data/ui/ButtonDialog.ui:270 msgid "" "This allows you to swap the order of the primary mouse buttons. In left-" "handed mode, the physical left mouse button generates a right mouse click " "and vice versa." msgstr "" "Ovo vam omogućuje zamjenu poredka glavnih tipka miša. U lijevorukom načinu " "rada, fizička lijeva tipka miša generira desni klik miša i obrnuto." #: data/ui/ButtonDialog.ui:290 msgid "Left Handed" msgstr "Lijevoruko" #: data/ui/ButtonDialog.ui:295 msgid "The left mouse button generates a left mouse click" msgstr "Lijeva tipka miša generira lijevi klik miša" #: data/ui/ButtonDialog.ui:309 msgid "Right Handed" msgstr "Desnoruko" #: data/ui/ButtonDialog.ui:314 msgid "The left mouse button generates a right mouse click" msgstr "Lijeva tipka miša generira desni klik miša" #: data/ui/ButtonDialog.ui:354 data/ui/LedDialog.ui:414 msgid "Cancel" msgstr "Odustani" #: data/ui/ButtonDialog.ui:356 data/ui/LedDialog.ui:418 msgid "Ignore any changes made" msgstr "Zanemari sve napravljene promjene" #: data/ui/ButtonDialog.ui:362 data/ui/LedDialog.ui:430 #: data/ui/MousePerspective.ui:132 msgid "Apply" msgstr "Primjeni" #: data/ui/ButtonDialog.ui:365 data/ui/LedDialog.ui:435 msgid "Apply any changes made" msgstr "Primijeni sve napravljene promjene" #: data/ui/ButtonDialog.ui:405 msgid "No button mapping found" msgstr "Nema pronađenih mapiranja tipki" #: data/ui/ButtonDialog.ui:421 msgid "Try a different search" msgstr "Pokušajte drugačiju pretragu" #: data/ui/ErrorPerspective.ui:16 msgid "Here, rodent, rodent, rodent…" msgstr "Ovdje, glodavac, glodavac, glodavac…" #: data/ui/ErrorPerspective.ui:69 msgid "Uh Oh! Something went wrong…" msgstr "Ups! Nešto je pošlo po krivu…" #: data/ui/ErrorPerspective.ui:73 data/ui/WelcomePerspective.ui:123 msgid "Quit" msgstr "Zatvori" #: data/ui/LedDialog.ui:58 data/ui/LedDialog.ui:209 msgid "Choose a color for the LED" msgstr "Odaberi boju za LED" #: data/ui/LedDialog.ui:90 data/ui/LedDialog.ui:257 msgid "Choose a brightness for the LED" msgstr "Odaberi svjetlinu za LED" #: data/ui/LedDialog.ui:104 data/ui/LedDialog.ui:270 msgid "Brightness" msgstr "Svjetlina" #: data/ui/LedDialog.ui:135 data/ui/LedDialog.ui:301 msgid "Choose an effect duration for the LED" msgstr "Odaberi trajanje efekta za LED" #: data/ui/LedDialog.ui:153 data/ui/LedDialog.ui:319 msgid "ms" msgstr "ms" #: data/ui/LedDialog.ui:175 data/ui/LedDialog.ui:341 msgid "Effect duration" msgstr "Trajanje efekta" #: data/ui/LedDialog.ui:212 msgid "Pick a Color for the LED" msgstr "Odaberi boju za LED" #: data/ui/LedDialog.ui:226 msgid "Color" msgstr "Boja" #: data/ui/LedDialog.ui:380 msgid "This LED is off" msgstr "Ovaj LED je isključen" #: data/ui/MousePerspective.ui:44 msgid "Something went wrong. The device has been reset to a previous state." msgstr "Nešto je pošlo po krivu. Uređaj je postavljen na prijašnje stanje." #: data/ui/MousePerspective.ui:101 msgid "Select another profile" msgstr "Odaberi drugi profil" #: data/ui/MousePerspective.ui:126 msgid "Commit the changes to the device" msgstr "Primijeni promjene na uređaju" #: data/ui/MousePerspective.ui:170 msgid "Click to switch to another profile" msgstr "Klikni za prebacivanje na drugi profil" #: data/ui/MousePerspective.ui:185 msgid "Add profile" msgstr "Dodaj profil" #: data/ui/MousePerspective.ui:189 msgid "Add a new profile to the device" msgstr "Dodaj novi profil na uređaj" #: data/ui/OptionButton.ui:9 msgid "Open the configuration dialog" msgstr "Otvori dijalog prilagodbe" #: data/ui/ProfileRow.ui:16 msgid "Remove this profile from the device" msgstr "Ukloni ovaj profil sa uređaja" #. Translators: this is used to indicate the active resolution. #: data/ui/ResolutionRow.ui:37 msgid "Active" msgstr "Aktivno" #: data/ui/ResolutionRow.ui:77 msgid "Set this resolution’s DPI" msgstr "Postavi ovu DPI razlučivosti" #. Translators: the rate at which the device reports movement. #: data/ui/ResolutionsPage.ui:43 msgid "Report rate" msgstr "Frekvencija" #: data/ui/ResolutionsPage.ui:60 msgid "Change the profile’s report rate" msgstr "Promijeni frekvenciju profila" #: data/ui/ResolutionsPage.ui:105 msgid "Hz" msgstr "Hz" #. Translators: the sensitivity of the device's sensor. #: data/ui/ResolutionsPage.ui:125 msgid "Sensitivity" msgstr "Osjetljivost" #: data/ui/ResolutionsPage.ui:174 msgid "Add a new resolution to the profile" msgstr "Dodaj novu razlučivost u profil" #: data/ui/WelcomePerspective.ui:20 msgid "Welcome" msgstr "Dobrodošli" #: data/ui/WelcomePerspective.ui:38 msgctxt "A subtitle under the \"Welcome\" title." msgid "Select a device to configure it" msgstr "Odaberite uređaj za prilagodbu" #: data/ui/WelcomePerspective.ui:89 msgctxt "A tooltip over the list of devices." msgid "Select a device to configure it" msgstr "Odaberite uređaj za prilagodbu" #: data/ui/WelcomePerspective.ui:119 msgid "Select a Device" msgstr "Odaberi uređaj" #: data/ui/Window.ui:8 msgid "_About Piper" msgstr "_O Piperu" #~ msgid "Remove this resolution from the profile" #~ msgstr "Ukloni ovu razlučivost sa uređaja" #~ msgid "Profile {}" #~ msgstr "Profil {}" #~ msgid "Visit Piper's website" #~ msgstr "Posjetite Piper web stranicu" #~ msgid "Set the device's handedness." #~ msgstr "Postavite za ljevu/desnu ruku uređaja." #~ msgid "Here, rodent, rodent, rodent..." #~ msgstr "Ovdje, glodavac, glodavac, glodavac..." #~ msgid "Uh Oh! Something went wrong..." #~ msgstr "Ups! Nešto je pošlo po krivu..." #~ msgid "Choose an effect rate for the LED" #~ msgstr "Odaberi frekvenciju efekta za LED" #~ msgid "Effect rate" #~ msgstr "Frekvencija efekta" #~ msgid "_About" #~ msgstr "_O programu" #~ msgid "_Quit" #~ msgstr "_Zatvori" #~ msgid "Failed to commit changes to the device" #~ msgstr "Neuspješno primijenjivanje promjena na uređaju" #~ msgid "Set this resolution's DPI" #~ msgstr "Postavi ovu DPI razlučivost" #~ msgid "Change the profile's report rate" #~ msgstr "Promijeni frekvenciju profila" piper-0.4/po/it.po000066400000000000000000000353571362011671000141000ustar00rootroot00000000000000# Italian translations for piper # Copyright (C) 2017-2020 the piper authors # This file is distributed under the same license as the piper package. # Francesco Venturoli , 2020. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: piper\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2020-01-28 22:04+0100\n" "PO-Revision-Date: 2020-01-28 22:10+0100\n" "Last-Translator: Francesco Venturoli \n" "Language-Team: Italian <>\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" #: data/org.freedesktop.Piper.desktop.in:3 #: data/org.freedesktop.Piper.appdata.xml.in.in:7 msgid "Piper" msgstr "Piper" #: data/org.freedesktop.Piper.desktop.in:4 #: data/org.freedesktop.Piper.appdata.xml.in.in:8 msgid "Configurable mouse configuration utility" msgstr "Utility di configurazione per mouse configurabili" #. Translators: Do NOT translate or transliterate this text (this is an icon file name)! #: data/org.freedesktop.Piper.desktop.in:7 msgid "org.freedesktop.Piper" msgstr "org.freedesktop.Piper" #. Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! #: data/org.freedesktop.Piper.desktop.in:12 msgid "gaming;configuration;mouse;mice;" msgstr "gaming;gioco;configurazione;mouse;" #: data/org.freedesktop.Piper.appdata.xml.in.in:10 msgid "" "Piper is a graphical user interface to configure gaming mice. Configuration " "options include changing the resolution (DPI) of the mouse, adding and " "removing profiles, setting LED colors and changing button behaviors." msgstr "" "Piper è un'interfaccia grafica per configurare mouse da gioco. Le opzioni " "di configurazione includono il cambiamento della risoluzione (DPI) del mouse, " "l'aggiunta e la rimozione di profili, l'impostazione dei colori dei LED e il " "cambiamento del comportamento dei pulsanti." #: data/org.freedesktop.Piper.appdata.xml.in.in:16 msgid "" "Piper requires libratbag’s ratbagd, the daemon to actually communicate with " "the mice. Piper is merely a front end to ratbagd, ratbagd must be installed " "and running when Piper is launched." msgstr "" "Piper necessita di ratbagd (incluso in libratbag), il demone che comunica " "effettivamente con il mouse. Piper è una semplice interfaccia per ratbagd, " "ratbagd deve essere installato e in esecuzione quando Piper viene lanciato." #: data/org.freedesktop.Piper.appdata.xml.in.in:33 msgid "The button configuraton page" msgstr "La pagina di configurazione dei pulsanti" #: data/org.freedesktop.Piper.appdata.xml.in.in:37 msgid "The LED configuraton page" msgstr "La pagina di configurazione dei LED" #: data/org.freedesktop.Piper.appdata.xml.in.in:41 msgid "The resolution configuraton page" msgstr "La pagina di configurazione della risoluzione" #. Translators: section header for mapping one button's click to another. #: piper/buttondialog.py:141 msgid "Button mapping" msgstr "Mappatura dei pulsanti" #. Translators: section header for assigning special functions to buttons. #: piper/buttondialog.py:150 msgid "Special mapping" msgstr "Mappatura speciale" #. Translators: the {} will be replaced with the button index, e.g. #. "Button 1 click". #: piper/buttondialog.py:222 piper/buttonspage.py:90 msgid "Button {} click" msgstr "Click pulsante {}" #: piper/buttonspage.py:94 msgid "Macro: {}" msgstr "Macro: {}" #. Translators: the button is turned disabled, e.g. off. #: piper/buttonspage.py:97 msgid "Disabled" msgstr "Disabilitato" #. Translators: the button has an unknown function. #: piper/buttonspage.py:100 piper/ratbagd.py:665 msgid "Unknown" msgstr "Sconosciuto" #: piper/buttonspage.py:108 msgid "Configure button {}" msgstr "Configura pulsante {}" #: piper/mouseperspective.py:90 data/ui/ResolutionsPage.ui:209 msgid "Resolutions" msgstr "Risoluzioni" #: piper/mouseperspective.py:92 msgid "Buttons" msgstr "Pulsanti" #: piper/mouseperspective.py:94 msgid "LEDs" msgstr "LED" #: piper/ratbagd.py:655 msgid "Left mouse button click" msgstr "Click pulsante sinistro" #: piper/ratbagd.py:656 msgid "Right mouse button click" msgstr "Click pulsante destro" #: piper/ratbagd.py:657 msgid "Middle mouse button click" msgstr "Click pulsante centrale" #: piper/ratbagd.py:658 msgid "Backward" msgstr "Indietro" #: piper/ratbagd.py:659 msgid "Forward" msgstr "Avanti" #: piper/ratbagd.py:664 msgid "Invalid" msgstr "Invalido" #: piper/ratbagd.py:666 msgid "Doubleclick" msgstr "Doppio click" #: piper/ratbagd.py:667 msgid "Wheel Left" msgstr "Rotella a sinistra" #: piper/ratbagd.py:668 msgid "Wheel Right" msgstr "Rotella a destra" #: piper/ratbagd.py:669 msgid "Wheel Up" msgstr "Rotella su" #: piper/ratbagd.py:670 msgid "Wheel Down" msgstr "Rotella giù" #: piper/ratbagd.py:671 msgid "Ratchet Mode" msgstr "Rotazione graduale" #: piper/ratbagd.py:672 msgid "Cycle Resolution Up" msgstr "Cicla risoluzione su" #: piper/ratbagd.py:673 msgid "Cycle Resolution Down" msgstr "Cicla risoluzione giù" #: piper/ratbagd.py:674 msgid "Resolution Up" msgstr "Risoluzione su" #: piper/ratbagd.py:675 msgid "Resolution Down" msgstr "Risoluzione giù" #: piper/ratbagd.py:676 msgid "Resolution Switch" msgstr "Cambio risoluzione" #: piper/ratbagd.py:677 msgid "Default Resolution" msgstr "Risoluzione predefinita" #: piper/ratbagd.py:678 msgid "Cycle Profile Up" msgstr "Cicla profilo su" #: piper/ratbagd.py:679 msgid "Cycle Profile Down" msgstr "Cicla profilo giù" #: piper/ratbagd.py:680 msgid "Profile Up" msgstr "Profilo su" #: piper/ratbagd.py:681 msgid "Profile Down" msgstr "Profilo giù" #: piper/ratbagd.py:682 msgid "Second Mode" msgstr "Seconda modalità" #: piper/ratbagd.py:683 msgid "Battery Level" msgstr "Livello batteria" #. Translators: this is used when there is no macro to preview. #: piper/ratbagd.py:813 data/ui/ButtonDialog.ui:106 data/ui/ButtonDialog.ui:187 msgid "None" msgstr "Nessuno" #. Translators: the LED is off. #: piper/ratbagd.py:895 data/ui/LedDialog.ui:395 msgid "Off" msgstr "Spento" #. Translators: the LED has a single, solid color. #: piper/ratbagd.py:897 data/ui/LedDialog.ui:66 msgid "Solid" msgstr "Fisso" #. Translators: the LED is cycling between red, green and blue. #: piper/ratbagd.py:899 data/ui/LedDialog.ui:189 msgid "Cycle" msgstr "Ciclo" #. Translators: the LED's is pulsating a single color on different #. brightnesses. #: piper/ratbagd.py:902 data/ui/LedDialog.ui:355 msgid "Breathing" msgstr "Respiro" #: piper/window.py:54 msgid "Cannot connect to ratbagd" msgstr "Impossibile connetersi a ratbagd" #: piper/window.py:55 msgid "" "Please make sure ratbagd is running and your user is in the required group" msgstr "" "Assicurati che ratbagd sia in esecuzione e che il tuo utente sia nel gruppo corretto" #: piper/window.py:58 msgid "Incompatible ratbagd API version (required: {}, provided: {})" msgstr "Versione delle API di ratbagd incompatibile (richiesta: {}, fornita: {})" #: piper/window.py:59 msgid "Please update both piper and libratbag to the latest versions" msgstr "Aggiorna piper e ratbagd alle ultime versioni" #: piper/window.py:73 piper/window.py:127 msgid "Cannot find any devices" msgstr "Impossibile trovare dispositivi" #: piper/window.py:74 piper/window.py:128 msgid "Please make sure your device is supported and plugged in" msgstr "Assicurati che il tuo dispositivo sia supportato e collegato" #: piper/window.py:86 msgid "There are unapplied changes. Are you sure you want to quit?" msgstr "Ci sono cambiamenti non applicati. Sei sicuro di voler uscire?" #: piper/window.py:95 msgid "Ooops. ratbagd has disappeared" msgstr "Ooops. ratbagd è scomparso" #: piper/window.py:96 msgid "Please restart Piper" msgstr "Riavvia Piper" #. The current device disconnected, which can only happen from the #. mouse perspective as we'd otherwise be in the welcome screen with #. more than one device remaining. Hence, we display the error #. perspective. #: piper/window.py:119 msgid "Your device disconnected!" msgstr "Il tuo dispositivo si è disconnesso!" #: piper/window.py:120 msgid "Please make sure your device is plugged in" msgstr "Assicurati che il tuo dispositivo sia collegato" #: piper/window.py:159 msgid "Cannot display device SVG" msgstr "Impossibile mostrare SVG dispositivo" #: piper/window.py:166 msgid "Newer version of ratbagd required" msgstr "Versione più recente di ratbagd richiesta" #: piper/window.py:167 msgid "Please update to the latest available version" msgstr "Aggiorna all'ultima versione disponibile" #: piper/window.py:169 msgid "Unknown exception occurred" msgstr "Eccezione sconosciuta" #: data/ui/AboutDialog.ui.in:10 msgid "Version: @version@" msgstr "Versione: @version@" #: data/ui/AboutDialog.ui.in:12 msgid "Visit Piper on GitHub" msgstr "Visita Piper su GitHub" #: data/ui/AboutDialog.ui.in:13 msgid "translator-credits" msgstr "Francesco Venturoli , 2020" #: data/ui/ButtonDialog.ui:65 msgid "Search for a button mapping" msgstr "Cerca mappatura di un pulsante" #: data/ui/ButtonDialog.ui:91 msgid "Capture a macro for this button" msgstr "Registra una macro per questo pulsante" #: data/ui/ButtonDialog.ui:104 msgid "The currently set macro" msgstr "La macro correntemente impostata" #: data/ui/ButtonDialog.ui:129 msgid "Send keystroke" msgstr "Invia pressione di un tasto" #: data/ui/ButtonDialog.ui:169 msgid "Enter a new key sequence for the macro." msgstr "Inserisci una nuova sequenza per la macro." #: data/ui/ButtonDialog.ui:221 msgid "Press Escape to cancel or Return to accept." msgstr "Premi Esc per annullare o Invio per accettare." #: data/ui/ButtonDialog.ui:252 msgid "Set the device’s handedness." msgstr "Imposta la mano dominante per il dispositivo" #: data/ui/ButtonDialog.ui:270 msgid "" "This allows you to swap the order of the primary mouse buttons. In left-" "handed mode, the physical left mouse button generates a right mouse click " "and vice versa." msgstr "" "Questo permette di scambiare l'ordine dei pulsanti principali del mouse. In " "modalità mancina, il pulsante fisico sinisto del mouse genera un click destro " "e viceversa." #: data/ui/ButtonDialog.ui:290 msgid "Left Handed" msgstr "Mancino" #: data/ui/ButtonDialog.ui:295 msgid "The left mouse button generates a left mouse click" msgstr "Il pulsante sinistro del mouse genera un click sinistro" #: data/ui/ButtonDialog.ui:309 msgid "Right Handed" msgstr "Destrorso" #: data/ui/ButtonDialog.ui:314 msgid "The left mouse button generates a right mouse click" msgstr "Il pulsante sinistro del mouse genera un click destro" #: data/ui/ButtonDialog.ui:354 data/ui/LedDialog.ui:414 msgid "Cancel" msgstr "Annulla" #: data/ui/ButtonDialog.ui:356 data/ui/LedDialog.ui:418 msgid "Ignore any changes made" msgstr "Ignora i cambiamenti effettuati" #: data/ui/ButtonDialog.ui:362 data/ui/LedDialog.ui:430 #: data/ui/MousePerspective.ui:132 msgid "Apply" msgstr "Applica" #: data/ui/ButtonDialog.ui:365 data/ui/LedDialog.ui:435 msgid "Apply any changes made" msgstr "Applica i cambiamenti effettuati" #: data/ui/ButtonDialog.ui:405 msgid "No button mapping found" msgstr "Nessuna mappatura pulsante trovata" #: data/ui/ButtonDialog.ui:421 msgid "Try a different search" msgstr "Prova ad effettuare un'altra ricerca" #: data/ui/ErrorPerspective.ui:16 msgid "Here, rodent, rodent, rodent…" msgstr "Qui, roditore, roditore, roditore…" #: data/ui/ErrorPerspective.ui:69 msgid "Uh Oh! Something went wrong…" msgstr "Oh oh! Qualcosa è andato storto…" #: data/ui/ErrorPerspective.ui:73 data/ui/WelcomePerspective.ui:123 msgid "Quit" msgstr "Esci" #: data/ui/LedDialog.ui:58 data/ui/LedDialog.ui:209 msgid "Choose a color for the LED" msgstr "Scegli un colore per il LED" #: data/ui/LedDialog.ui:90 data/ui/LedDialog.ui:257 msgid "Choose a brightness for the LED" msgstr "Scegli una luminosità per il LED" #: data/ui/LedDialog.ui:104 data/ui/LedDialog.ui:270 msgid "Brightness" msgstr "Luminosità" #: data/ui/LedDialog.ui:135 data/ui/LedDialog.ui:301 msgid "Choose an effect duration for the LED" msgstr "Scegli la durata dell'effetto del LED" #: data/ui/LedDialog.ui:153 data/ui/LedDialog.ui:319 msgid "ms" msgstr "ms" #: data/ui/LedDialog.ui:175 data/ui/LedDialog.ui:341 msgid "Effect duration" msgstr "Durata effetto" #: data/ui/LedDialog.ui:212 msgid "Pick a Color for the LED" msgstr "Scegli un colore per il LED" #: data/ui/LedDialog.ui:226 msgid "Color" msgstr "Colore" #: data/ui/LedDialog.ui:380 msgid "This LED is off" msgstr "Questo LED è spento" #: data/ui/MousePerspective.ui:44 msgid "Something went wrong. The device has been reset to a previous state." msgstr "Qualcosa è andato storto. Lo stato precedente del dispositivo è stato ripristinato." #: data/ui/MousePerspective.ui:101 msgid "Select another profile" msgstr "Scegli un altro profilo" #: data/ui/MousePerspective.ui:126 msgid "Commit the changes to the device" msgstr "Applica i cambiamenti al dispositivo" #: data/ui/MousePerspective.ui:170 msgid "Click to switch to another profile" msgstr "Clicca per passare ad un altro profilo" #: data/ui/MousePerspective.ui:185 msgid "Add profile" msgstr "Aggiungi profilo" #: data/ui/MousePerspective.ui:189 msgid "Add a new profile to the device" msgstr "Aggiungi un nuovo profilo al dispositivo" #: data/ui/OptionButton.ui:9 msgid "Open the configuration dialog" msgstr "Apri il dialogo di configurazione" #: data/ui/ProfileRow.ui:16 msgid "Remove this profile from the device" msgstr "Rimuovi questo profilo dal dispositivo" #. Translators: this is used to indicate the active resolution. #: data/ui/ResolutionRow.ui:37 msgid "Active" msgstr "Attiva" #: data/ui/ResolutionRow.ui:77 msgid "Set this resolution’s DPI" msgstr "Imposta i DPI di questa risoluzione" #. Translators: the rate at which the device reports movement. #: data/ui/ResolutionsPage.ui:43 msgid "Report rate" msgstr "Frequenza di aggiornamento" #: data/ui/ResolutionsPage.ui:60 msgid "Change the profile’s report rate" msgstr "Cambia la frequenza di aggiornamento del profilo" #: data/ui/ResolutionsPage.ui:105 msgid "Hz" msgstr "Hz" #. Translators: the sensitivity of the device's sensor. #: data/ui/ResolutionsPage.ui:125 msgid "Sensitivity" msgstr "Sensibilità" #: data/ui/ResolutionsPage.ui:174 msgid "Add a new resolution to the profile" msgstr "Aggiungi una nuova risoluzione al profilo" #: data/ui/WelcomePerspective.ui:20 msgid "Welcome" msgstr "Benvenuto" #: data/ui/WelcomePerspective.ui:38 msgctxt "A subtitle under the \"Welcome\" title." msgid "Select a device to configure it" msgstr "Scegli un dispositivo per configurarlo" #: data/ui/WelcomePerspective.ui:89 msgctxt "A tooltip over the list of devices." msgid "Select a device to configure it" msgstr "Scegli un dispositivo per configurarlo" #: data/ui/WelcomePerspective.ui:119 msgid "Select a Device" msgstr "Scegli un dispositivo" #: data/ui/Window.ui:8 msgid "_About Piper" msgstr "_A proposito di Piper" piper-0.4/po/meson.build000066400000000000000000000000461362011671000152510ustar00rootroot00000000000000i18n.gettext('piper', preset: 'glib') piper-0.4/po/nl.po000066400000000000000000000271201362011671000140620ustar00rootroot00000000000000# Dutch Piper translation # Copyright (C) 2017 Jente Hidskes # This file is distributed under the same license as the piper package. # Jente Hidskes , 2017. # msgid "" msgstr "" "Project-Id-Version: piper\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-08-23 20:37+0200\n" "PO-Revision-Date: 2017-08-17 17:49+0200\n" "Last-Translator: Jente Hidskes \n" "Language-Team: Dutch <>\n" "Language: nl_NL\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #. Translators: section header for mapping one button's click to another. #: piper/buttondialog.py:144 msgid "Button mapping" msgstr "Knopindeling" #. Translators: section header for assigning special functions to buttons. #: piper/buttondialog.py:153 msgid "Special mapping" msgstr "Speciale functie indeling" #. Translators: the {} will be replaced with the button index, e.g. #. "Button 1 click". #: piper/buttondialog.py:225 piper/buttonspage.py:84 msgid "Button {} click" msgstr "Klik met knop {}" #: piper/buttonspage.py:88 msgid "Macro: {}" msgstr "Makro: {}" #. Translators: the button is turned disabled, e.g. off. #: piper/buttonspage.py:91 msgid "Disabled" msgstr "Uitgeschakeld" #. Translators: the button has an unknown function. #: piper/buttonspage.py:94 piper/ratbagd.py:613 msgid "Unknown" msgstr "Onbekend" #: piper/buttonspage.py:102 msgid "Configure button {}" msgstr "Knop {} instellen" #: piper/mouseperspective.py:86 data/ui/ResolutionsPage.ui:209 msgid "Resolutions" msgstr "Resoluties" #: piper/mouseperspective.py:88 msgid "Buttons" msgstr "Knoppen" #: piper/mouseperspective.py:90 msgid "LEDs" msgstr "LEDs" #. Translators: the {} will be replaced with the profile index. #: piper/mouseperspective.py:94 piper/profilerow.py:42 msgid "Profile {}" msgstr "Profiel {}" #: piper/ratbagd.py:604 msgid "Left mouse button click" msgstr "Klik met linkermuisknop" #: piper/ratbagd.py:605 msgid "Right mouse button click" msgstr "Klik met rechtermuisknop" #: piper/ratbagd.py:606 msgid "Middle mouse button click" msgstr "Klik met middelmuisknop" #: piper/ratbagd.py:607 msgid "Backward" msgstr "Vorige" #: piper/ratbagd.py:608 msgid "Forward" msgstr "Volgende" #: piper/ratbagd.py:614 msgid "Doubleclick" msgstr "Dubbelklik" #: piper/ratbagd.py:615 msgid "Wheel Left" msgstr "Muiswiel links" #: piper/ratbagd.py:616 msgid "Wheel Right" msgstr "Muiswiel rechts" #: piper/ratbagd.py:617 msgid "Wheel Up" msgstr "Muiswiel omhoog" #: piper/ratbagd.py:618 msgid "Wheel Down" msgstr "Muiswiel omlaag" #: piper/ratbagd.py:619 msgid "Ratchet Mode" msgstr "" #: piper/ratbagd.py:620 msgid "Cycle Resolution Up" msgstr "Volgende resolutie (cyclisch)" #: piper/ratbagd.py:621 msgid "Cycle Resolution Down" msgstr "Vorige resolutie (cyclisch)" #: piper/ratbagd.py:622 msgid "Resolution Up" msgstr "Volgende resolutie" #: piper/ratbagd.py:623 msgid "Resolution Down" msgstr "Vorige resolutie" #: piper/ratbagd.py:624 msgid "Resolution Switch" msgstr "Resolutie wisselen" #: piper/ratbagd.py:625 msgid "Default Resolution" msgstr "Standaardresolutie" #: piper/ratbagd.py:626 msgid "Cycle Profile Up" msgstr "Volgende profiel (cyclisch)" #: piper/ratbagd.py:627 msgid "Cycle Profile Down" msgstr "Vorige profiel (cyclisch)" #: piper/ratbagd.py:628 msgid "Profile Up" msgstr "Volgende profiel" #: piper/ratbagd.py:629 msgid "Profile Down" msgstr "Vorige profiel" #: piper/ratbagd.py:630 msgid "Second Mode" msgstr "Tweede modus" #: piper/ratbagd.py:631 msgid "Battery Level" msgstr "Batterijniveau" #. Translators: this is used when there is no macro to preview. #: piper/ratbagd.py:759 data/ui/ButtonDialog.ui:106 data/ui/ButtonDialog.ui:187 msgid "None" msgstr "Geen" #. Translators: the LED is off. #: piper/ratbagd.py:833 data/ui/LedDialog.ui:391 msgid "Off" msgstr "Uit" #. Translators: the LED has a single, solid color. #: piper/ratbagd.py:835 data/ui/LedDialog.ui:66 msgid "Solid" msgstr "Solide" #. Translators: the LED is cycling between red, green and blue. #: piper/ratbagd.py:837 data/ui/LedDialog.ui:188 msgid "Cycle" msgstr "Cyclus" #. Translators: the LED's is pulsating a single color on different #. brightnesses. #: piper/ratbagd.py:840 data/ui/LedDialog.ui:353 msgid "Breathing" msgstr "Pulserend" #: piper/window.py:50 msgid "Cannot connect to ratbagd" msgstr "Kan niet verbinden met ratbagd" #: piper/window.py:51 msgid "Please make sure ratbagd is running" msgstr "Maakt u zich er zeker van dat ratbagd uitgevoerd wordt" #: piper/window.py:64 piper/window.py:114 msgid "Cannot find any devices" msgstr "Kan geen apparaten vinden" #: piper/window.py:65 piper/window.py:115 msgid "Please make sure your device is supported and plugged in" msgstr "Maakt u zich er zeker van dat uw apparat ondersteund en verbonden is" #: piper/window.py:77 msgid "There are unapplied changes. Are you sure you want to quit?" msgstr "" "Er zijn niet-toegepaste wijzigingen. Weet u zeker dat u wilt afsluiten?" #. The current device disconnected, which can only happen from the #. mouse perspective as we'd otherwise be in the welcome screen with #. more than one device remaining. Hence, we display the error #. perspective. #: piper/window.py:106 msgid "Your device disconnected!" msgstr "Uw apparaat is losgekoppeld" #: piper/window.py:107 msgid "Please make sure your device is plugged in" msgstr "Maakt u zich er zeker van dat uw apparaat is aangesloten" #: piper/window.py:155 msgid "Cannot display device SVG" msgstr "De SVG van dit apparaat kan niet worden weergegeven" #: piper/window.py:157 msgid "Unknown exception occurred" msgstr "Er is een onbekende fout opgetreden" #: data/ui/AboutDialog.ui.in:13 msgid "Visit Piper's website" msgstr "Bezoek Piper's website" #: data/ui/ButtonDialog.ui:65 msgid "Search for a button mapping" msgstr "Zoek voor een knop aanwijzing" #: data/ui/ButtonDialog.ui:91 msgid "Capture a macro for this button" msgstr "Leg een makro vast voor deze knop" #: data/ui/ButtonDialog.ui:104 msgid "The currently set macro" msgstr "De huidig ingestelde makro" #: data/ui/ButtonDialog.ui:129 msgid "Send keystroke" msgstr "Zend een makro" #: data/ui/ButtonDialog.ui:169 msgid "Enter a new key sequence for the macro." msgstr "Voer een nieuwe toetsencombinatie in voor de makro." #: data/ui/ButtonDialog.ui:221 msgid "Press Escape to cancel or Return to accept." msgstr "Druk op Escape om af te breken of op Return om te accepteren." #: data/ui/ButtonDialog.ui:252 msgid "Set the device's handedness." msgstr "Stel links- of rechthandigheid in." #: data/ui/ButtonDialog.ui:270 msgid "" "This allows you to swap the order of the primary mouse buttons. In left-" "handed mode, the physical left mouse button generates a right mouse click " "and vice versa." msgstr "" "Dit stelt u in staat om de volgorde van de primaire muisknoppen om " "tewisselen. In linkshandige modus genereert de fysieke linkermuisknop " "eenrechter muisklik en vice versa." #: data/ui/ButtonDialog.ui:290 msgid "Left Handed" msgstr "Linkshandig" #: data/ui/ButtonDialog.ui:295 msgid "The left mouse button generates a left mouse click" msgstr "De linkermuisknop genereert een linker muisklik" #: data/ui/ButtonDialog.ui:309 msgid "Right Handed" msgstr "Rechtshandig" #: data/ui/ButtonDialog.ui:314 msgid "The left mouse button generates a right mouse click" msgstr "De linkermuisknop genereert een rechter muisklik" #: data/ui/ButtonDialog.ui:354 data/ui/LedDialog.ui:410 msgid "Cancel" msgstr "Afbreken" #: data/ui/ButtonDialog.ui:356 data/ui/LedDialog.ui:414 msgid "Ignore any changes made" msgstr "Alle veranderingen negeren" #: data/ui/ButtonDialog.ui:362 data/ui/LedDialog.ui:426 #: data/ui/MousePerspective.ui:133 msgid "Apply" msgstr "Toepassen" #: data/ui/ButtonDialog.ui:365 data/ui/LedDialog.ui:431 msgid "Apply any changes made" msgstr "Alle veranderingen toepassen" #: data/ui/ButtonDialog.ui:405 msgid "No button mapping found" msgstr "Geen knop aanwijzingen gevonden" #: data/ui/ButtonDialog.ui:421 msgid "Try a different search" msgstr "Probeer een andere zoekterm" #: data/ui/ErrorPerspective.ui:16 msgid "Here, rodent, rodent, rodent..." msgstr "Hier, knaagdier, knaagdier, knaagdier..." #: data/ui/ErrorPerspective.ui:69 msgid "Uh Oh! Something went wrong..." msgstr "Uh oh! Er is iets fout gegaan..." #: data/ui/ErrorPerspective.ui:73 data/ui/WelcomePerspective.ui:123 msgid "Quit" msgstr "Afsluiten" #: data/ui/LedDialog.ui:58 data/ui/LedDialog.ui:208 msgid "Choose a color for the LED" msgstr "Kies een kleur voor de LED" #: data/ui/LedDialog.ui:90 data/ui/LedDialog.ui:256 msgid "Choose a brightness for the LED" msgstr "Kies een helderheid voor de LED" #: data/ui/LedDialog.ui:104 data/ui/LedDialog.ui:269 msgid "Brightness" msgstr "Helderheid" #: data/ui/LedDialog.ui:135 data/ui/LedDialog.ui:300 msgid "Choose an effect rate for the LED" msgstr "Kies een effectsnelheid voor de LED" #: data/ui/LedDialog.ui:152 data/ui/LedDialog.ui:317 #: data/ui/ResolutionsPage.ui:105 msgid "Hz" msgstr "Hz" #: data/ui/LedDialog.ui:174 data/ui/LedDialog.ui:339 msgid "Effect rate" msgstr "Effectsnelheid" #: data/ui/LedDialog.ui:211 msgid "Pick a Color for the LED" msgstr "Kies een kleur voor de LED" #: data/ui/LedDialog.ui:225 msgid "Color" msgstr "Kleur" #: data/ui/LedDialog.ui:378 msgid "This LED is off" msgstr "Deze LED is uit" #: data/ui/Menus.ui:7 msgid "_About" msgstr "_Over" #: data/ui/Menus.ui:11 msgid "_Quit" msgstr "_Afsluiten" #: data/ui/MousePerspective.ui:44 msgid "Failed to commit changes to the device" msgstr "Fout bij het toepassen van de veranderingen op het apparaat" #: data/ui/MousePerspective.ui:102 msgid "Select another profile" msgstr "Kies een ander profiel" #: data/ui/MousePerspective.ui:127 msgid "Commit the changes to the device" msgstr "Pas de veranderingen toe op het apparaat" #: data/ui/MousePerspective.ui:171 msgid "Click to switch to another profile" msgstr "Klik om van profiel te veranderen" #: data/ui/MousePerspective.ui:186 msgid "Add profile" msgstr "Voeg profiel toe" #: data/ui/MousePerspective.ui:190 msgid "Add a new profile to the device" msgstr "Voeg een nieuw profiel toe aan het apparaat" #: data/ui/OptionButton.ui:9 msgid "Open the configuration dialog" msgstr "Open de instellingen" #: data/ui/ProfileRow.ui:16 msgid "Remove this profile from the device" msgstr "Verwijder dit profiel van het apparaat" #: data/ui/ResolutionRow.ui:23 msgid "Remove this resolution from the profile" msgstr "Verwijder deze resolutie uit het profiel" #. Translators: this is used to indicate the active resolution. #: data/ui/ResolutionRow.ui:65 msgid "Active" msgstr "Actief" #: data/ui/ResolutionRow.ui:105 msgid "Set this resolution's DPI" msgstr "Stel de DPI van deze resolutie in" #. Translators: the rate at which the device reports movement. #: data/ui/ResolutionsPage.ui:43 msgid "Report rate" msgstr "Rapportagesnelheid" #: data/ui/ResolutionsPage.ui:60 msgid "Change the profile's report rate" msgstr "Verander de rapportagesnelheid van dit profiel" #. Translators: the sensitivity of the device's sensor. #: data/ui/ResolutionsPage.ui:125 msgid "Sensitivity" msgstr "Gevoeligheid" #: data/ui/ResolutionsPage.ui:174 msgid "Add a new resolution to the profile" msgstr "Voeg een nieuwe resolutie toe aan het profiel" #: data/ui/WelcomePerspective.ui:20 msgid "Welcome" msgstr "Welkom" #: data/ui/WelcomePerspective.ui:38 msgctxt "A subtitle under the \"Welcome\" title." msgid "Select a device to configure it" msgstr "Kies een apparaat om het in te stellen" #: data/ui/WelcomePerspective.ui:89 msgctxt "A tooltip over the list of devices." msgid "Select a device to configure it" msgstr "Klik op een apparaat om het in te stellen" #: data/ui/WelcomePerspective.ui:119 msgid "Select a Device" msgstr "Kies een apparaat om het in te stellen" piper-0.4/po/pl.po000066400000000000000000000362331362011671000140710ustar00rootroot00000000000000# Polish translation for piper. # Copyright © 2017-2020 the piper authors. # This file is distributed under the same license as the piper package. # Piotr Drąg , 2017-2020. # Aviary.pl , 2017-2020. # msgid "" msgstr "" "Project-Id-Version: piper\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2020-01-27 21:28+0100\n" "PO-Revision-Date: 2020-01-24 15:22+0100\n" "Last-Translator: Piotr Drąg \n" "Language-Team: Polish \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" #: data/org.freedesktop.Piper.desktop.in:3 #: data/org.freedesktop.Piper.appdata.xml.in.in:7 msgid "Piper" msgstr "Piper" #: data/org.freedesktop.Piper.desktop.in:4 #: data/org.freedesktop.Piper.appdata.xml.in.in:8 msgid "Configurable mouse configuration utility" msgstr "Konfigurowalne narzędzie do konfiguracji myszy" #. Translators: Do NOT translate or transliterate this text (this is an icon file name)! #: data/org.freedesktop.Piper.desktop.in:7 msgid "org.freedesktop.Piper" msgstr "org.freedesktop.Piper" #. Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! #: data/org.freedesktop.Piper.desktop.in:12 msgid "gaming;configuration;mouse;mice;" msgstr "" "gry;gra;granie;konfiguracja;ustawienia;opcje;preferencje;mysz;myszy;myszka;" "myszki;gryzoń;" #: data/org.freedesktop.Piper.appdata.xml.in.in:10 msgid "" "Piper is a graphical user interface to configure gaming mice. Configuration " "options include changing the resolution (DPI) of the mouse, adding and " "removing profiles, setting LED colors and changing button behaviors." msgstr "" "Piper to graficzny interfejs użytkownika do konfiguracji myszy do gier " "komputerowych. Można między innymi zmieniać rozdzielczość (DPI) myszy, " "dodawać i usuwać profile, ustawiać kolory diod LED i zmieniać zachowanie " "przycisków." #: data/org.freedesktop.Piper.appdata.xml.in.in:16 msgid "" "Piper requires libratbag’s ratbagd, the daemon to actually communicate with " "the mice. Piper is merely a front end to ratbagd, ratbagd must be installed " "and running when Piper is launched." msgstr "" "Piper wymaga ratbagd biblioteki libratbag, usługi do komunikacji z myszą. " "Piper to tylko interfejs dla usługi ratbagd, która musi być zainstalowana " "i uruchomiona przed włączeniem programu Piper." #: data/org.freedesktop.Piper.appdata.xml.in.in:33 msgid "The button configuraton page" msgstr "Strona konfiguracji przycisków" #: data/org.freedesktop.Piper.appdata.xml.in.in:37 msgid "The LED configuraton page" msgstr "Strona konfiguracji diod LED" #: data/org.freedesktop.Piper.appdata.xml.in.in:41 msgid "The resolution configuraton page" msgstr "Strona konfiguracji rozdzielczości" #. Translators: section header for mapping one button's click to another. #: piper/buttondialog.py:141 msgid "Button mapping" msgstr "Mapowanie przycisku" #. Translators: section header for assigning special functions to buttons. #: piper/buttondialog.py:150 msgid "Special mapping" msgstr "Mapowanie specjalne" #. Translators: the {} will be replaced with the button index, e.g. #. "Button 1 click". #: piper/buttondialog.py:222 piper/buttonspage.py:90 msgid "Button {} click" msgstr "Kliknięcie {}. przyciskiem" #: piper/buttonspage.py:94 msgid "Macro: {}" msgstr "Makro: {}" #. Translators: the button is turned disabled, e.g. off. #: piper/buttonspage.py:97 msgid "Disabled" msgstr "Wyłączone" #. Translators: the button has an unknown function. #: piper/buttonspage.py:100 piper/ratbagd.py:665 msgid "Unknown" msgstr "Nieznane" #: piper/buttonspage.py:108 msgid "Configure button {}" msgstr "Konfiguracja {}. przycisku" #: piper/mouseperspective.py:90 data/ui/ResolutionsPage.ui:209 msgid "Resolutions" msgstr "Rozdzielczości" #: piper/mouseperspective.py:92 msgid "Buttons" msgstr "Przyciski" #: piper/mouseperspective.py:94 msgid "LEDs" msgstr "Diody LED" #: piper/ratbagd.py:655 msgid "Left mouse button click" msgstr "Kliknięcie lewym przyciskiem myszy" #: piper/ratbagd.py:656 msgid "Right mouse button click" msgstr "Kliknięcie prawym przyciskiem myszy" #: piper/ratbagd.py:657 msgid "Middle mouse button click" msgstr "Kliknięcie środkowym przyciskiem myszy" #: piper/ratbagd.py:658 msgid "Backward" msgstr "Wstecz" #: piper/ratbagd.py:659 msgid "Forward" msgstr "Dalej" #: piper/ratbagd.py:664 msgid "Invalid" msgstr "Nieprawidłowe" #: piper/ratbagd.py:666 msgid "Doubleclick" msgstr "Podwójne kliknięcie" #: piper/ratbagd.py:667 msgid "Wheel Left" msgstr "Kółko w lewo" #: piper/ratbagd.py:668 msgid "Wheel Right" msgstr "Kółko w prawo" #: piper/ratbagd.py:669 msgid "Wheel Up" msgstr "Kółko w górę" #: piper/ratbagd.py:670 msgid "Wheel Down" msgstr "Kółko w dół" #: piper/ratbagd.py:671 msgid "Ratchet Mode" msgstr "Tryb zapadkowy" #: piper/ratbagd.py:672 msgid "Cycle Resolution Up" msgstr "Przewija rozdzielczość w górę" #: piper/ratbagd.py:673 msgid "Cycle Resolution Down" msgstr "Przewija rozdzielczość w dół" #: piper/ratbagd.py:674 msgid "Resolution Up" msgstr "Rozdzielczość w górę" #: piper/ratbagd.py:675 msgid "Resolution Down" msgstr "Rozdzielczość w dół" #: piper/ratbagd.py:676 msgid "Resolution Switch" msgstr "Przełączenie rozdzielczości" #: piper/ratbagd.py:677 msgid "Default Resolution" msgstr "Domyślna rozdzielczość" #: piper/ratbagd.py:678 msgid "Cycle Profile Up" msgstr "Przewija profil w górę" #: piper/ratbagd.py:679 msgid "Cycle Profile Down" msgstr "Przewija profil w dół" #: piper/ratbagd.py:680 msgid "Profile Up" msgstr "Profil w górę" #: piper/ratbagd.py:681 msgid "Profile Down" msgstr "Profil w dół" #: piper/ratbagd.py:682 msgid "Second Mode" msgstr "Drugi poziom" #: piper/ratbagd.py:683 msgid "Battery Level" msgstr "Poziom naładowania akumulatora" #. Translators: this is used when there is no macro to preview. #: piper/ratbagd.py:813 data/ui/ButtonDialog.ui:106 data/ui/ButtonDialog.ui:187 msgid "None" msgstr "Brak" #. Translators: the LED is off. #: piper/ratbagd.py:895 data/ui/LedDialog.ui:395 msgid "Off" msgstr "Wyłączone" #. Translators: the LED has a single, solid color. #: piper/ratbagd.py:897 data/ui/LedDialog.ui:66 msgid "Solid" msgstr "Jeden kolor" #. Translators: the LED is cycling between red, green and blue. #: piper/ratbagd.py:899 data/ui/LedDialog.ui:189 msgid "Cycle" msgstr "Zmienny kolor" #. Translators: the LED's is pulsating a single color on different #. brightnesses. #: piper/ratbagd.py:902 data/ui/LedDialog.ui:355 msgid "Breathing" msgstr "Pulsujący kolor" #: piper/window.py:54 msgid "Cannot connect to ratbagd" msgstr "Nie można połączyć z usługą ratbagd" #: piper/window.py:55 msgid "" "Please make sure ratbagd is running and your user is in the required group" msgstr "" "Proszę się upewnić, że usługa ratbagd jest uruchomiona, a obecny użytkownik " "jest w wymaganej grupie" #: piper/window.py:58 msgid "Incompatible ratbagd API version (required: {}, provided: {})" msgstr "Niezgodna wersja API usługi ratbagd (wymagana: {}, dostarczona: {})" #: piper/window.py:59 msgid "Please update both piper and libratbag to the latest versions" msgstr "" "Proszę zaktualizować program Piper i usługę libratbag do najnowszych wersji" #: piper/window.py:73 piper/window.py:127 msgid "Cannot find any devices" msgstr "Nie można odnaleźć żadnego urządzenia" #: piper/window.py:74 piper/window.py:128 msgid "Please make sure your device is supported and plugged in" msgstr "Proszę się upewnić, że urządzenie jest obsługiwane i podłączone" #: piper/window.py:86 msgid "There are unapplied changes. Are you sure you want to quit?" msgstr "Są niezastosowane zmiany. Na pewno zakończyć?" #: piper/window.py:95 msgid "Ooops. ratbagd has disappeared" msgstr "Usługa ratbagd zniknęła" #: piper/window.py:96 msgid "Please restart Piper" msgstr "Proszę ponownie uruchomić program Piper" #. The current device disconnected, which can only happen from the #. mouse perspective as we'd otherwise be in the welcome screen with #. more than one device remaining. Hence, we display the error #. perspective. #: piper/window.py:119 msgid "Your device disconnected!" msgstr "Urządzenie zostało rozłączone." #: piper/window.py:120 msgid "Please make sure your device is plugged in" msgstr "Proszę się upewnić, że urządzenie jest podłączone" #: piper/window.py:159 msgid "Cannot display device SVG" msgstr "Nie można wyświetlić obrazu SVG urządzenia" #: piper/window.py:166 msgid "Newer version of ratbagd required" msgstr "Wymagana jest nowsza wersja usługi ratbagd" #: piper/window.py:167 msgid "Please update to the latest available version" msgstr "Proszę zaktualizować do najnowszej dostępnej wersji" #: piper/window.py:169 msgid "Unknown exception occurred" msgstr "Wystąpił nieznany wyjątek" #: data/ui/AboutDialog.ui.in:10 msgid "Version: @version@" msgstr "Wersja: @version@" #: data/ui/AboutDialog.ui.in:12 msgid "Visit Piper on GitHub" msgstr "Program Piper w serwisie GitHub" #: data/ui/AboutDialog.ui.in:13 msgid "translator-credits" msgstr "" "Piotr Drąg , 2017-2020\n" "Aviary.pl , 2017-2020" #: data/ui/ButtonDialog.ui:65 msgid "Search for a button mapping" msgstr "Wyszukuje mapowanie przycisku" #: data/ui/ButtonDialog.ui:91 msgid "Capture a macro for this button" msgstr "Przechwytuje makro dla tego przycisku" #: data/ui/ButtonDialog.ui:104 msgid "The currently set macro" msgstr "Obecnie ustawione makro" #: data/ui/ButtonDialog.ui:129 msgid "Send keystroke" msgstr "Wyślij kombinację klawiszy" #: data/ui/ButtonDialog.ui:169 msgid "Enter a new key sequence for the macro." msgstr "Proszę wprowadzić nową sekwencję klawiszy dla makra." #: data/ui/ButtonDialog.ui:221 msgid "Press Escape to cancel or Return to accept." msgstr "Klawisz Esc anuluje, a Enter przyjmie." #: data/ui/ButtonDialog.ui:252 msgid "Set the device’s handedness." msgstr "Lewo- lub praworęczność urządzenia." #: data/ui/ButtonDialog.ui:270 msgid "" "This allows you to swap the order of the primary mouse buttons. In left-" "handed mode, the physical left mouse button generates a right mouse click " "and vice versa." msgstr "" "Umożliwia zamianę głównych przycisków myszy. W trybie leworęcznym fizyczny " "lewy przycisk myszy generuje kliknięcie prawym przyciskiem myszy, " "i odwrotnie w trybie praworęcznym." #: data/ui/ButtonDialog.ui:290 msgid "Left Handed" msgstr "Leworęczna" #: data/ui/ButtonDialog.ui:295 msgid "The left mouse button generates a left mouse click" msgstr "Lewy przycisk myszy generuje kliknięcie lewym przyciskiem myszy" #: data/ui/ButtonDialog.ui:309 msgid "Right Handed" msgstr "Praworęczna" #: data/ui/ButtonDialog.ui:314 msgid "The left mouse button generates a right mouse click" msgstr "Lewy przycisk myszy generuje kliknięcie prawym przyciskiem myszy" #: data/ui/ButtonDialog.ui:354 data/ui/LedDialog.ui:414 msgid "Cancel" msgstr "Anuluj" #: data/ui/ButtonDialog.ui:356 data/ui/LedDialog.ui:418 msgid "Ignore any changes made" msgstr "Ignoruje wprowadzone zmiany" #: data/ui/ButtonDialog.ui:362 data/ui/LedDialog.ui:430 #: data/ui/MousePerspective.ui:132 msgid "Apply" msgstr "Zastosuj" #: data/ui/ButtonDialog.ui:365 data/ui/LedDialog.ui:435 msgid "Apply any changes made" msgstr "Zastosowuje wprowadzone zmiany" #: data/ui/ButtonDialog.ui:405 msgid "No button mapping found" msgstr "Nie odnaleziono żadnego mapowania przycisku" #: data/ui/ButtonDialog.ui:421 msgid "Try a different search" msgstr "Proszę spróbować innych słów" #: data/ui/ErrorPerspective.ui:16 msgid "Here, rodent, rodent, rodent…" msgstr "Myszko, myszko, myszko…" #: data/ui/ErrorPerspective.ui:69 msgid "Uh Oh! Something went wrong…" msgstr "Coś się nie powiodło." #: data/ui/ErrorPerspective.ui:73 data/ui/WelcomePerspective.ui:123 msgid "Quit" msgstr "Zakończ" #: data/ui/LedDialog.ui:58 data/ui/LedDialog.ui:209 msgid "Choose a color for the LED" msgstr "Wybiera kolor diody LED" #: data/ui/LedDialog.ui:90 data/ui/LedDialog.ui:257 msgid "Choose a brightness for the LED" msgstr "Wybiera jasność diody LED" #: data/ui/LedDialog.ui:104 data/ui/LedDialog.ui:270 msgid "Brightness" msgstr "Jasność" #: data/ui/LedDialog.ui:135 data/ui/LedDialog.ui:301 msgid "Choose an effect duration for the LED" msgstr "Wybiera czas trwania efektu diody LED" #: data/ui/LedDialog.ui:153 data/ui/LedDialog.ui:319 msgid "ms" msgstr "ms" #: data/ui/LedDialog.ui:175 data/ui/LedDialog.ui:341 msgid "Effect duration" msgstr "Czas trwania efektu" #: data/ui/LedDialog.ui:212 msgid "Pick a Color for the LED" msgstr "Kolor diody LED" #: data/ui/LedDialog.ui:226 msgid "Color" msgstr "Kolor" #: data/ui/LedDialog.ui:380 msgid "This LED is off" msgstr "Ta dioda LED jest wyłączona" #: data/ui/MousePerspective.ui:44 msgid "Something went wrong. The device has been reset to a previous state." msgstr "" "Coś się nie powiodło. Urządzenie zostało przywrócone do poprzedniego stanu." #: data/ui/MousePerspective.ui:101 msgid "Select another profile" msgstr "Wybiera inny profil" #: data/ui/MousePerspective.ui:126 msgid "Commit the changes to the device" msgstr "Zatwierdza zmiany na urządzeniu" #: data/ui/MousePerspective.ui:170 msgid "Click to switch to another profile" msgstr "Przełącza na inny profil" #: data/ui/MousePerspective.ui:185 msgid "Add profile" msgstr "Dodaj profil" #: data/ui/MousePerspective.ui:189 msgid "Add a new profile to the device" msgstr "Dodaje nowy profil do urządzenia" #: data/ui/OptionButton.ui:9 msgid "Open the configuration dialog" msgstr "Otwiera okno konfiguracji" #: data/ui/ProfileRow.ui:16 msgid "Remove this profile from the device" msgstr "Usuwa ten profil z urządzenia" #. Translators: this is used to indicate the active resolution. #: data/ui/ResolutionRow.ui:37 msgid "Active" msgstr "Aktywna" #: data/ui/ResolutionRow.ui:77 msgid "Set this resolution’s DPI" msgstr "Ustawia DPI tej rozdzielczości" #. Translators: the rate at which the device reports movement. #: data/ui/ResolutionsPage.ui:43 msgid "Report rate" msgstr "Tempo zgłaszania" #: data/ui/ResolutionsPage.ui:60 msgid "Change the profile’s report rate" msgstr "Zmienia tempo zgłaszania profilu" #: data/ui/ResolutionsPage.ui:105 msgid "Hz" msgstr "Hz" #. Translators: the sensitivity of the device's sensor. #: data/ui/ResolutionsPage.ui:125 msgid "Sensitivity" msgstr "Czułość" #: data/ui/ResolutionsPage.ui:174 msgid "Add a new resolution to the profile" msgstr "Dodaje nową rozdzielczość do profilu" #: data/ui/WelcomePerspective.ui:20 msgid "Welcome" msgstr "Witamy" #: data/ui/WelcomePerspective.ui:38 msgctxt "A subtitle under the \"Welcome\" title." msgid "Select a device to configure it" msgstr "Proszę wybrać urządzenie do skonfigurowania" #: data/ui/WelcomePerspective.ui:89 msgctxt "A tooltip over the list of devices." msgid "Select a device to configure it" msgstr "Wybiera urządzenie do skonfigurowania" #: data/ui/WelcomePerspective.ui:119 msgid "Select a Device" msgstr "Wybór urządzenia" #: data/ui/Window.ui:8 msgid "_About Piper" msgstr "_O programie" piper-0.4/po/pt_BR.po000066400000000000000000000354011362011671000144600ustar00rootroot00000000000000# Brazilian Portuguese translation for Piper # Copyright (C) 2017-2019 Piper developers # This file is distributed under the same license as the piper package. # Alexandre Oliveira , 2019. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: piper\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2019-08-18 20:27-0300\n" "PO-Revision-Date: 2019-08-18 21:18-0300\n" "Last-Translator: Alexandre Oliveira \n" "Language-Team: Brazilian Portuguese <>\n" "Language: pt_BR\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: data/org.freedesktop.Piper.desktop.in:3 #: data/org.freedesktop.Piper.appdata.xml.in.in:7 msgid "Piper" msgstr "Piper" #: data/org.freedesktop.Piper.desktop.in:4 #: data/org.freedesktop.Piper.appdata.xml.in.in:8 msgid "Configurable mouse configuration utility" msgstr "Ferramenta de configuração de mouses programáveis" #. Translators: Do NOT translate or transliterate this text (this is an icon file name)! #: data/org.freedesktop.Piper.desktop.in:7 msgid "org.freedesktop.Piper" msgstr "org.freedesktop.Piper" #. Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! #: data/org.freedesktop.Piper.desktop.in:12 msgid "gaming;configuration;mouse;mice;" msgstr "configuração;mouse;gaming;gamer;" #: data/org.freedesktop.Piper.appdata.xml.in.in:10 msgid "" "Piper is a graphical user interface to configure gaming mice. Configuration " "options include changing the resolution (DPI) of the mouse, adding and " "removing profiles, setting LED colors and changing button behaviors." msgstr "" "Piper é uma interface gráfica para configurar mouses para jogos. As opções " "de configuração incluem alterar a resolução (DPI) do mouse, adicionar e " "remover perfis, alterar as cores dos LEDs e alterar o comportamento dos botões." #: data/org.freedesktop.Piper.appdata.xml.in.in:16 msgid "" "Piper requires libratbag’s ratbagd, the daemon to actually communicate with " "the mice. Piper is merely a front end to ratbagd, ratbagd must be installed " "and running when Piper is launched." msgstr "" "O Piper requer o programa ratbagd, um daemon que realiza a comunicação " " com o mouse. O aplicativo é apenas uma interface para o ratbagd, ou seja, o daemon " " precisa estar instalado e executando ao iniciar o Piper." #: data/org.freedesktop.Piper.appdata.xml.in.in:33 msgid "The button configuraton page" msgstr "A página de configuração dos botões" #: data/org.freedesktop.Piper.appdata.xml.in.in:37 msgid "The LED configuraton page" msgstr "A página de configuração do(s) LED(s)" #: data/org.freedesktop.Piper.appdata.xml.in.in:41 msgid "The resolution configuraton page" msgstr "A página de configuração das resoluções" #. Translators: section header for mapping one button's click to another. #: piper/buttondialog.py:141 msgid "Button mapping" msgstr "Mapeamento de botões" #. Translators: section header for assigning special functions to buttons. #: piper/buttondialog.py:150 msgid "Special mapping" msgstr "Mapeamento especial" #. Translators: the {} will be replaced with the button index, e.g. #. "Button 1 click". #: piper/buttondialog.py:222 piper/buttonspage.py:90 msgid "Button {} click" msgstr "Clique com o botão {}" #: piper/buttonspage.py:94 msgid "Macro: {}" msgstr "Macro: {}" #. Translators: the button is turned disabled, e.g. off. #: piper/buttonspage.py:97 msgid "Disabled" msgstr "Desabilitado" #. Translators: the button has an unknown function. #: piper/buttonspage.py:100 piper/ratbagd.py:663 msgid "Unknown" msgstr "Desconhecida" #: piper/buttonspage.py:108 msgid "Configure button {}" msgstr "Configurar botão {}" #: piper/mouseperspective.py:90 data/ui/ResolutionsPage.ui:209 msgid "Resolutions" msgstr "Resoluções" #: piper/mouseperspective.py:92 msgid "Buttons" msgstr "Botões" #: piper/mouseperspective.py:94 msgid "LEDs" msgstr "LEDs" #: piper/ratbagd.py:653 msgid "Left mouse button click" msgstr "Clique com o botão esquerdo" #: piper/ratbagd.py:654 msgid "Right mouse button click" msgstr "Clique com o botão direito" #: piper/ratbagd.py:655 msgid "Middle mouse button click" msgstr "Clique com o botão do meio" #: piper/ratbagd.py:656 msgid "Backward" msgstr "Voltar" #: piper/ratbagd.py:657 msgid "Forward" msgstr "Avançar" #: piper/ratbagd.py:662 msgid "Invalid" msgstr "Inválido" #: piper/ratbagd.py:664 msgid "Doubleclick" msgstr "Clique duplo" #: piper/ratbagd.py:665 msgid "Wheel Left" msgstr "Rolar para a esquerda" #: piper/ratbagd.py:666 msgid "Wheel Right" msgstr "Rolar para a direita" #: piper/ratbagd.py:667 msgid "Wheel Up" msgstr "Rolar para cima" #: piper/ratbagd.py:668 msgid "Wheel Down" msgstr "Rolar para baixo" #: piper/ratbagd.py:669 msgid "Ratchet Mode" msgstr "Modo catraca (clique-a-clique)" #: piper/ratbagd.py:670 msgid "Cycle Resolution Up" msgstr "Alternar resolução (para cima)" #: piper/ratbagd.py:671 msgid "Cycle Resolution Down" msgstr "Alternar resolução (para baixo)" #: piper/ratbagd.py:672 msgid "Resolution Up" msgstr "Próxima resolução" #: piper/ratbagd.py:673 msgid "Resolution Down" msgstr "Resolução anterior" #: piper/ratbagd.py:674 msgid "Resolution Switch" msgstr "Trocar resolução" #: piper/ratbagd.py:675 msgid "Default Resolution" msgstr "Resolução padrão" #: piper/ratbagd.py:676 msgid "Cycle Profile Up" msgstr "Alternar perfil (para cima)" #: piper/ratbagd.py:677 msgid "Cycle Profile Down" msgstr "Alternar perfil (para baixo)" #: piper/ratbagd.py:678 msgid "Profile Up" msgstr "Próximo perfil" #: piper/ratbagd.py:679 msgid "Profile Down" msgstr "Perfil anterior" #: piper/ratbagd.py:680 msgid "Second Mode" msgstr "Modo alternativo" #: piper/ratbagd.py:681 msgid "Battery Level" msgstr "Nível da bateria" #. Translators: this is used when there is no macro to preview. #: piper/ratbagd.py:811 data/ui/ButtonDialog.ui:106 data/ui/ButtonDialog.ui:187 msgid "None" msgstr "Nenhum" #. Translators: the LED is off. #: piper/ratbagd.py:893 data/ui/LedDialog.ui:395 msgid "Off" msgstr "Desligado" #. Translators: the LED has a single, solid color. #: piper/ratbagd.py:895 data/ui/LedDialog.ui:66 msgid "Solid" msgstr "Sólido" #. Translators: the LED is cycling between red, green and blue. #: piper/ratbagd.py:897 data/ui/LedDialog.ui:189 msgid "Cycle" msgstr "Alternar" #. Translators: the LED's is pulsating a single color on different #. brightnesses. #: piper/ratbagd.py:900 data/ui/LedDialog.ui:355 msgid "Breathing" msgstr "Pulsar" #: piper/window.py:54 msgid "Cannot connect to ratbagd" msgstr "Não foi possível conectar-se ao ratbagd" #: piper/window.py:55 msgid "Please make sure ratbagd is running" msgstr "Por favor, certifique-se de que o ratbagd está executando" #: piper/window.py:58 msgid "Incompatible ratbagd API version (required: {}, provided: {})" msgstr "Versão de API do ratbagd incompatível (requer: {}, atual: {})" #: piper/window.py:59 msgid "Please update both piper and libratbag to the latest versions" msgstr "Por favor, atualize o piper e a biblioteca libratbag para a versão mais recente" #: piper/window.py:73 piper/window.py:127 msgid "Cannot find any devices" msgstr "Não foi possível encontrar quaisquer dispositivos" #: piper/window.py:74 piper/window.py:128 msgid "Please make sure your device is supported and plugged in" msgstr "Por favor, certifique-se de que o seu dispositivo é suportado e está conectado" #: piper/window.py:86 msgid "There are unapplied changes. Are you sure you want to quit?" msgstr "Há alterações não salvas. Você tem certeza de que deseja sair?" #: piper/window.py:95 msgid "Ooops. ratbagd has disappeared" msgstr "Oops. O ratbagd desapareceu" #: piper/window.py:96 msgid "Please restart Piper" msgstr "Por favor, reinicie o aplicativo" #. The current device disconnected, which can only happen from the #. mouse perspective as we'd otherwise be in the welcome screen with #. more than one device remaining. Hence, we display the error #. perspective. #: piper/window.py:119 msgid "Your device disconnected!" msgstr "O seu dispositivo foi desconectado!" #: piper/window.py:120 msgid "Please make sure your device is plugged in" msgstr "Por favor, certifique-se de que seu dispositivo está conectado" #: piper/window.py:159 msgid "Cannot display device SVG" msgstr "Não foi possível exibir a imagem do dispositivo" #: piper/window.py:166 msgid "Newer version of ratbagd required" msgstr "Requer uma versão mais recente do ratbagd" #: piper/window.py:167 msgid "Please update to the latest available version" msgstr "Por favor, atualize para a versão mais recente" #: piper/window.py:169 msgid "Unknown exception occurred" msgstr "Ocorreu uma exceção desconhecida" #: data/ui/AboutDialog.ui.in:13 msgid "Visit Piper’s website" msgstr "Visite o site do aplicativo" #: data/ui/AboutDialog.ui.in:14 msgid "translator-credits" msgstr "Alexandre Oliveira , 2019" #: data/ui/ButtonDialog.ui:65 msgid "Search for a button mapping" msgstr "Pesquisar por mapeamento de botão" #: data/ui/ButtonDialog.ui:91 msgid "Capture a macro for this button" msgstr "Capturar macro para o botão" #: data/ui/ButtonDialog.ui:104 msgid "The currently set macro" msgstr "O macro atualmente definido" #: data/ui/ButtonDialog.ui:129 msgid "Send keystroke" msgstr "Enviar sequência de teclas" #: data/ui/ButtonDialog.ui:169 msgid "Enter a new key sequence for the macro." msgstr "Insira uma nova sequência de teclas para o macro." #: data/ui/ButtonDialog.ui:221 msgid "Press Escape to cancel or Return to accept." msgstr "Aperte Esc para cancelar ou Enter para aceitar." #: data/ui/ButtonDialog.ui:252 msgid "Set the device’s handedness." msgstr "Selecionar a mão que usa o mouse" #: data/ui/ButtonDialog.ui:270 msgid "" "This allows you to swap the order of the primary mouse buttons. In left-" "handed mode, the physical left mouse button generates a right mouse click " "and vice versa." msgstr "" "Isto permite que você troque a ordem dos botões primários do mouse. No modo" "canhoto, o botão esquerdo físico do mouse gera um clique com o botão direito " "e vice-versa." #: data/ui/ButtonDialog.ui:290 msgid "Left Handed" msgstr "Canhoto" #: data/ui/ButtonDialog.ui:295 msgid "The left mouse button generates a left mouse click" msgstr "O botão esquerdo do mouse gera um clique com o botão direito" #: data/ui/ButtonDialog.ui:309 msgid "Right Handed" msgstr "Destro" #: data/ui/ButtonDialog.ui:314 msgid "The left mouse button generates a right mouse click" msgstr "O botão esquerdo do mouse gera um clique com o botão esquerdo" #: data/ui/ButtonDialog.ui:354 data/ui/LedDialog.ui:414 msgid "Cancel" msgstr "Cancelar" #: data/ui/ButtonDialog.ui:356 data/ui/LedDialog.ui:418 msgid "Ignore any changes made" msgstr "Ignorar alterações" #: data/ui/ButtonDialog.ui:362 data/ui/LedDialog.ui:430 #: data/ui/MousePerspective.ui:132 msgid "Apply" msgstr "Aplicar" #: data/ui/ButtonDialog.ui:365 data/ui/LedDialog.ui:435 msgid "Apply any changes made" msgstr "Aplicar alterações" #: data/ui/ButtonDialog.ui:405 msgid "No button mapping found" msgstr "Nenhum mapeamento de botão encontrado" #: data/ui/ButtonDialog.ui:421 msgid "Try a different search" msgstr "Tente uma nova pesquisa" #: data/ui/ErrorPerspective.ui:16 msgid "Here, rodent, rodent, rodent…" msgstr "Aqui, ratinho, ratinho, ratinho..." #: data/ui/ErrorPerspective.ui:69 msgid "Uh Oh! Something went wrong…" msgstr "Uh oh! Algo de errado, não está certo..." #: data/ui/ErrorPerspective.ui:73 data/ui/WelcomePerspective.ui:123 msgid "Quit" msgstr "Sair" #: data/ui/LedDialog.ui:58 data/ui/LedDialog.ui:209 msgid "Choose a color for the LED" msgstr "Escolha a cor do LED" #: data/ui/LedDialog.ui:90 data/ui/LedDialog.ui:257 msgid "Choose a brightness for the LED" msgstr "Escolha a intensidade do LED" #: data/ui/LedDialog.ui:104 data/ui/LedDialog.ui:270 msgid "Brightness" msgstr "Brilho" #: data/ui/LedDialog.ui:135 data/ui/LedDialog.ui:301 msgid "Choose an effect duration for the LED" msgstr "Escolha a duração do efeito do LED" #: data/ui/LedDialog.ui:153 data/ui/LedDialog.ui:319 msgid "ms" msgstr "ms" #: data/ui/LedDialog.ui:175 data/ui/LedDialog.ui:341 msgid "Effect duration" msgstr "Duração do efeito" #: data/ui/LedDialog.ui:212 msgid "Pick a Color for the LED" msgstr "Escolha a cor do LED" #: data/ui/LedDialog.ui:226 msgid "Color" msgstr "Cor" #: data/ui/LedDialog.ui:380 msgid "This LED is off" msgstr "Este LED está desligado" #: data/ui/MousePerspective.ui:44 msgid "Something went wrong. The device has been reset to a previous state." msgstr "Aconteceu algo inesperado. O dispositivo foi restaurado para o estado anterior." #: data/ui/MousePerspective.ui:101 msgid "Select another profile" msgstr "Escolher outro perfil" #: data/ui/MousePerspective.ui:126 msgid "Commit the changes to the device" msgstr "Salvar alterações no dispositivo" #: data/ui/MousePerspective.ui:170 msgid "Click to switch to another profile" msgstr "Clique para trocar para outro perfil" #: data/ui/MousePerspective.ui:185 msgid "Add profile" msgstr "Adicionar perfil" #: data/ui/MousePerspective.ui:189 msgid "Add a new profile to the device" msgstr "Adicionar um novo perfil ao dispositivo" #: data/ui/OptionButton.ui:9 msgid "Open the configuration dialog" msgstr "Abrir a janela de configurações" #: data/ui/ProfileRow.ui:16 msgid "Remove this profile from the device" msgstr "Remover este perfil do dispositivo" #. Translators: this is used to indicate the active resolution. #: data/ui/ResolutionRow.ui:37 msgid "Active" msgstr "Ativo" #: data/ui/ResolutionRow.ui:77 msgid "Set this resolution’s DPI" msgstr "Definir a resolução em DPI" #. Translators: the rate at which the device reports movement. #: data/ui/ResolutionsPage.ui:43 msgid "Report rate" msgstr "Frequência de atualização" #: data/ui/ResolutionsPage.ui:60 msgid "Change the profile’s report rate" msgstr "Alterar a frequência de atualização do perfil" #: data/ui/ResolutionsPage.ui:105 msgid "Hz" msgstr "Hz" #. Translators: the sensitivity of the device's sensor. #: data/ui/ResolutionsPage.ui:125 msgid "Sensitivity" msgstr "Sensibilidade" #: data/ui/ResolutionsPage.ui:174 msgid "Add a new resolution to the profile" msgstr "Adicionar uma nova resolução ao perfil" #: data/ui/WelcomePerspective.ui:20 msgid "Welcome" msgstr "Bem-vindo(a)" #: data/ui/WelcomePerspective.ui:38 msgctxt "A subtitle under the \"Welcome\" title." msgid "Select a device to configure it" msgstr "Selecione um dispositivo para configurá-lo" #: data/ui/WelcomePerspective.ui:89 msgctxt "A tooltip over the list of devices." msgid "Select a device to configure it" msgstr "Selecione um dispositivo para configurá-lo" #: data/ui/WelcomePerspective.ui:119 msgid "Select a Device" msgstr "Selecione um dispositivo" #: data/ui/Window.ui:8 msgid "_About Piper" msgstr "Sobre o aplicativo" piper-0.4/po/sv.po000066400000000000000000000353501362011671000141050ustar00rootroot00000000000000# Swedish localization of Piper # Copyright (C) 2017 the piper developers # This file is distributed under the same license as the piper package. # Patrik Nilsson , 2017. # msgid "" msgstr "" "Project-Id-Version: piper\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2020-01-26 11:18+0100\n" "PO-Revision-Date: 2017-09-14 12:14+0200\n" "Last-Translator: Stephan Lachnit \n" "Language-Team: \n" "Language: sv\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 2.0.3\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: data/org.freedesktop.Piper.desktop.in:3 #: data/org.freedesktop.Piper.appdata.xml.in.in:7 msgid "Piper" msgstr "Piper" #: data/org.freedesktop.Piper.desktop.in:4 #: data/org.freedesktop.Piper.appdata.xml.in.in:8 msgid "Configurable mouse configuration utility" msgstr "Konfigurationsprogram för programmerbara möss" #. Translators: Do NOT translate or transliterate this text (this is an icon file name)! #: data/org.freedesktop.Piper.desktop.in:7 msgid "org.freedesktop.Piper" msgstr "org.freedesktop.Piper" #. Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! #: data/org.freedesktop.Piper.desktop.in:12 msgid "gaming;configuration;mouse;mice;" msgstr "gaming;spel;konfiguration;mus;möss;" #: data/org.freedesktop.Piper.appdata.xml.in.in:10 msgid "" "Piper is a graphical user interface to configure gaming mice. Configuration " "options include changing the resolution (DPI) of the mouse, adding and " "removing profiles, setting LED colors and changing button behaviors." msgstr "" "Piper är ett användargränssnitt för att konfigurera möss. Det finns optioner " "för att ändra musens upplösning (DPI), lägga till och ta bort profiler, " "ställa in LED färger och ändra knappbindningar." #: data/org.freedesktop.Piper.appdata.xml.in.in:16 msgid "" "Piper requires libratbag’s ratbagd, the daemon to actually communicate with " "the mice. Piper is merely a front end to ratbagd, ratbagd must be installed " "and running when Piper is launched." msgstr "" "Piper behöver libratbags ratbagd, demonen som kommunicerar faktiskt med musen." "Piper är bara ett användargränssnitt för ratbagd, ratbagd måste vara " "installerad och körs." #: data/org.freedesktop.Piper.appdata.xml.in.in:33 #, fuzzy msgid "The button configuraton page" msgstr "Öppna konfigurationsdialogen" #: data/org.freedesktop.Piper.appdata.xml.in.in:37 #, fuzzy msgid "The LED configuraton page" msgstr "Öppna konfigurationsdialogen" #: data/org.freedesktop.Piper.appdata.xml.in.in:41 #, fuzzy msgid "The resolution configuraton page" msgstr "Öppna konfigurationsdialogen" #. Translators: section header for mapping one button's click to another. #: piper/buttondialog.py:141 msgid "Button mapping" msgstr "Knappbindning" #. Translators: section header for assigning special functions to buttons. #: piper/buttondialog.py:150 msgid "Special mapping" msgstr "Speciell bindning" #. Translators: the {} will be replaced with the button index, e.g. #. "Button 1 click". #: piper/buttondialog.py:222 piper/buttonspage.py:90 msgid "Button {} click" msgstr "Knapp {} klick" #: piper/buttonspage.py:94 msgid "Macro: {}" msgstr "Makro: {}" #. Translators: the button is turned disabled, e.g. off. #: piper/buttonspage.py:97 msgid "Disabled" msgstr "Inaktiverad" #. Translators: the button has an unknown function. #: piper/buttonspage.py:100 piper/ratbagd.py:663 msgid "Unknown" msgstr "Okänd" #: piper/buttonspage.py:108 msgid "Configure button {}" msgstr "Konfigurera knapp {}" #: piper/mouseperspective.py:90 data/ui/ResolutionsPage.ui:209 msgid "Resolutions" msgstr "Upplösningar" #: piper/mouseperspective.py:92 msgid "Buttons" msgstr "Knappar" #: piper/mouseperspective.py:94 msgid "LEDs" msgstr "Lysdioder" #: piper/ratbagd.py:653 msgid "Left mouse button click" msgstr "Vänster musklick" #: piper/ratbagd.py:654 msgid "Right mouse button click" msgstr "Höger musklick" #: piper/ratbagd.py:655 msgid "Middle mouse button click" msgstr "Mellersta musklick" #: piper/ratbagd.py:656 msgid "Backward" msgstr "Bakåt" #: piper/ratbagd.py:657 msgid "Forward" msgstr "Framåt" #: piper/ratbagd.py:662 msgid "Invalid" msgstr "Ogiltig" #: piper/ratbagd.py:664 msgid "Doubleclick" msgstr "Dubbelklick" #: piper/ratbagd.py:665 msgid "Wheel Left" msgstr "Mushjul vänster" #: piper/ratbagd.py:666 msgid "Wheel Right" msgstr "Mushjul höger" #: piper/ratbagd.py:667 msgid "Wheel Up" msgstr "Mushjul Upp" #: piper/ratbagd.py:668 msgid "Wheel Down" msgstr "Mushjul Ner" #: piper/ratbagd.py:669 msgid "Ratchet Mode" msgstr "Spärrläge" #: piper/ratbagd.py:670 msgid "Cycle Resolution Up" msgstr "Cykla upplösning upp" #: piper/ratbagd.py:671 msgid "Cycle Resolution Down" msgstr "Cykla upplösning ner" #: piper/ratbagd.py:672 msgid "Resolution Up" msgstr "Upplösning upp" #: piper/ratbagd.py:673 msgid "Resolution Down" msgstr "Upplösning ner" #: piper/ratbagd.py:674 msgid "Resolution Switch" msgstr "Växla upplösning" #: piper/ratbagd.py:675 msgid "Default Resolution" msgstr "Standardupplösning" #: piper/ratbagd.py:676 msgid "Cycle Profile Up" msgstr "Cykla profil upp" #: piper/ratbagd.py:677 msgid "Cycle Profile Down" msgstr "Cykla profil ner" #: piper/ratbagd.py:678 msgid "Profile Up" msgstr "Profil upp" #: piper/ratbagd.py:679 msgid "Profile Down" msgstr "Profil ner" #: piper/ratbagd.py:680 msgid "Second Mode" msgstr "Andra läget" #: piper/ratbagd.py:681 msgid "Battery Level" msgstr "Batterinivå" #. Translators: this is used when there is no macro to preview. #: piper/ratbagd.py:811 data/ui/ButtonDialog.ui:106 data/ui/ButtonDialog.ui:187 msgid "None" msgstr "Ingen" #. Translators: the LED is off. #: piper/ratbagd.py:893 data/ui/LedDialog.ui:395 msgid "Off" msgstr "Av" #. Translators: the LED has a single, solid color. #: piper/ratbagd.py:895 data/ui/LedDialog.ui:66 msgid "Solid" msgstr "Solid" #. Translators: the LED is cycling between red, green and blue. #: piper/ratbagd.py:897 data/ui/LedDialog.ui:189 msgid "Cycle" msgstr "Cykla" #. Translators: the LED's is pulsating a single color on different #. brightnesses. #: piper/ratbagd.py:900 data/ui/LedDialog.ui:355 msgid "Breathing" msgstr "Pulserande" #: piper/window.py:54 msgid "Cannot connect to ratbagd" msgstr "Kan inte ansluta till ratbagd" #: piper/window.py:55 #, fuzzy msgid "" "Please make sure ratbagd is running and your user is in the required group" msgstr "Se till att ratbagd körs" #: piper/window.py:58 msgid "Incompatible ratbagd API version (required: {}, provided: {})" msgstr "Inkompatibel ratbagd API version (behövs: {}, försedd: {})" #: piper/window.py:59 msgid "Please update both piper and libratbag to the latest versions" msgstr "Uppdatera både Piper och libratbag till den senaste versionen" #: piper/window.py:73 piper/window.py:127 msgid "Cannot find any devices" msgstr "Kan inte hitta några enheter" #: piper/window.py:74 piper/window.py:128 msgid "Please make sure your device is supported and plugged in" msgstr "Se till att din enhet stöds och är ansluten" #: piper/window.py:86 msgid "There are unapplied changes. Are you sure you want to quit?" msgstr "Det finns ej utförda förändringar. Är du säker på att du vill avsluta?" #: piper/window.py:95 msgid "Ooops. ratbagd has disappeared" msgstr "Aj då. ratbagd är försvunnen" #: piper/window.py:96 msgid "Please restart Piper" msgstr "Starta om Piper" #. The current device disconnected, which can only happen from the #. mouse perspective as we'd otherwise be in the welcome screen with #. more than one device remaining. Hence, we display the error #. perspective. #: piper/window.py:119 msgid "Your device disconnected!" msgstr "Anslutningen till din enhet bröts!" #: piper/window.py:120 msgid "Please make sure your device is plugged in" msgstr "Se till att din enhet är ansluten" #: piper/window.py:159 msgid "Cannot display device SVG" msgstr "Kan inte visa enhetens SVG" #: piper/window.py:166 msgid "Newer version of ratbagd required" msgstr "Nyare version av ratbagd behövs" #: piper/window.py:167 msgid "Please update to the latest available version" msgstr "Uppdatera till senaste versionen" #: piper/window.py:169 msgid "Unknown exception occurred" msgstr "Okänt fel av typ \"exception\" inträffade" #: data/ui/AboutDialog.ui.in:10 msgid "Version: @version@" msgstr "Version: @version@" #: data/ui/AboutDialog.ui.in:12 #, fuzzy msgid "Visit Piper on GitHub" msgstr "Besök Piper på GitHub" #: data/ui/AboutDialog.ui.in:13 msgid "translator-credits" msgstr "" #: data/ui/ButtonDialog.ui:65 msgid "Search for a button mapping" msgstr "Sök efter en knappbindning" #: data/ui/ButtonDialog.ui:91 msgid "Capture a macro for this button" msgstr "Spela in makro för den här knappen" #: data/ui/ButtonDialog.ui:104 msgid "The currently set macro" msgstr "Det nuvarande inställda makrot" #: data/ui/ButtonDialog.ui:129 msgid "Send keystroke" msgstr "Skicka knapptryckning" #: data/ui/ButtonDialog.ui:169 msgid "Enter a new key sequence for the macro." msgstr "Ange en ny nyckelsekvens för makrot." #: data/ui/ButtonDialog.ui:221 msgid "Press Escape to cancel or Return to accept." msgstr "Tryck på Escape för att avbryta eller Retur för att acceptera." #: data/ui/ButtonDialog.ui:252 #, fuzzy msgid "Set the device’s handedness." msgstr "Ställ in enheten för vänster eller höger-hänthet." #: data/ui/ButtonDialog.ui:270 msgid "" "This allows you to swap the order of the primary mouse buttons. In left-" "handed mode, the physical left mouse button generates a right mouse click " "and vice versa." msgstr "" "Detta låter dig skifta ordningen på de primära musknapparna. I vänsterhänt " "läge genererar den vänstra musknappen ett högerklick och vice versa." #: data/ui/ButtonDialog.ui:290 msgid "Left Handed" msgstr "Vänsterhänt" #: data/ui/ButtonDialog.ui:295 msgid "The left mouse button generates a left mouse click" msgstr "Den vänstra musknappen genererar ett vänsterklick" #: data/ui/ButtonDialog.ui:309 msgid "Right Handed" msgstr "Högerhänt" #: data/ui/ButtonDialog.ui:314 msgid "The left mouse button generates a right mouse click" msgstr "Den vänstra musknappen genererar ett högerklick" #: data/ui/ButtonDialog.ui:354 data/ui/LedDialog.ui:414 msgid "Cancel" msgstr "Avbryt" #: data/ui/ButtonDialog.ui:356 data/ui/LedDialog.ui:418 msgid "Ignore any changes made" msgstr "Ignorera eventuella ändringar" #: data/ui/ButtonDialog.ui:362 data/ui/LedDialog.ui:430 #: data/ui/MousePerspective.ui:132 msgid "Apply" msgstr "Tillämpa" #: data/ui/ButtonDialog.ui:365 data/ui/LedDialog.ui:435 msgid "Apply any changes made" msgstr "Tillämpa eventuella ändringar" #: data/ui/ButtonDialog.ui:405 msgid "No button mapping found" msgstr "Ingen knappbindning hittades" #: data/ui/ButtonDialog.ui:421 msgid "Try a different search" msgstr "Prova en annan sökning" #: data/ui/ErrorPerspective.ui:16 #, fuzzy msgid "Here, rodent, rodent, rodent…" msgstr "Hit, gnagare, gnagare, gnagare..." #: data/ui/ErrorPerspective.ui:69 #, fuzzy msgid "Uh Oh! Something went wrong…" msgstr "Oj då! Något gick fel..." #: data/ui/ErrorPerspective.ui:73 data/ui/WelcomePerspective.ui:123 msgid "Quit" msgstr "Avsluta" #: data/ui/LedDialog.ui:58 data/ui/LedDialog.ui:209 msgid "Choose a color for the LED" msgstr "Välj en färg för lysdioden" #: data/ui/LedDialog.ui:90 data/ui/LedDialog.ui:257 msgid "Choose a brightness for the LED" msgstr "Välj en ljusstyrka för lysdioden" #: data/ui/LedDialog.ui:104 data/ui/LedDialog.ui:270 msgid "Brightness" msgstr "Ljusstyrka" #: data/ui/LedDialog.ui:135 data/ui/LedDialog.ui:301 #, fuzzy msgid "Choose an effect duration for the LED" msgstr "Välj en effektfrekvens för lysdioden" #: data/ui/LedDialog.ui:153 data/ui/LedDialog.ui:319 msgid "ms" msgstr "ms" #: data/ui/LedDialog.ui:175 data/ui/LedDialog.ui:341 #, fuzzy msgid "Effect duration" msgstr "Effektfrekvens" #: data/ui/LedDialog.ui:212 msgid "Pick a Color for the LED" msgstr "Välj en färg för lysdioden" #: data/ui/LedDialog.ui:226 msgid "Color" msgstr "Färg" #: data/ui/LedDialog.ui:380 msgid "This LED is off" msgstr "Den här lysdioden är av" #: data/ui/MousePerspective.ui:44 msgid "Something went wrong. The device has been reset to a previous state." msgstr "Något gick fel. Enheten återställdes till ett tidigare tillstand." #: data/ui/MousePerspective.ui:101 msgid "Select another profile" msgstr "Välj en annan profil" #: data/ui/MousePerspective.ui:126 msgid "Commit the changes to the device" msgstr "Utför förändringarna i enheten" #: data/ui/MousePerspective.ui:170 msgid "Click to switch to another profile" msgstr "Klicka för att växla till en annan profil" #: data/ui/MousePerspective.ui:185 msgid "Add profile" msgstr "Lägg till profil" #: data/ui/MousePerspective.ui:189 msgid "Add a new profile to the device" msgstr "Lägg till en ny profil till enheten" #: data/ui/OptionButton.ui:9 msgid "Open the configuration dialog" msgstr "Öppna konfigurationsdialogen" #: data/ui/ProfileRow.ui:16 msgid "Remove this profile from the device" msgstr "Ta bort den här profilen från enheten" #. Translators: this is used to indicate the active resolution. #: data/ui/ResolutionRow.ui:37 msgid "Active" msgstr "Aktiv" #: data/ui/ResolutionRow.ui:77 #, fuzzy msgid "Set this resolution’s DPI" msgstr "Ange den här upplösningens DPI" #. Translators: the rate at which the device reports movement. #: data/ui/ResolutionsPage.ui:43 msgid "Report rate" msgstr "Poll frekvens" #: data/ui/ResolutionsPage.ui:60 #, fuzzy msgid "Change the profile’s report rate" msgstr "Ändra profilens poll frekvens" #: data/ui/ResolutionsPage.ui:105 msgid "Hz" msgstr "Hz" #. Translators: the sensitivity of the device's sensor. #: data/ui/ResolutionsPage.ui:125 msgid "Sensitivity" msgstr "Känslighet" #: data/ui/ResolutionsPage.ui:174 msgid "Add a new resolution to the profile" msgstr "Lägg till en ny upplösning för profilen" #: data/ui/WelcomePerspective.ui:20 msgid "Welcome" msgstr "Välkommen" #: data/ui/WelcomePerspective.ui:38 msgctxt "A subtitle under the \"Welcome\" title." msgid "Select a device to configure it" msgstr "Välj en enhet för att konfigurera den" #: data/ui/WelcomePerspective.ui:89 msgctxt "A tooltip over the list of devices." msgid "Select a device to configure it" msgstr "Välj en enhet för att konfigurera den" #: data/ui/WelcomePerspective.ui:119 msgid "Select a Device" msgstr "Välj en enhet" #: data/ui/Window.ui:8 #, fuzzy msgid "_About Piper" msgstr "_Om" #~ msgid "Profile {}" #~ msgstr "Profil {}" #~ msgid "_Quit" #~ msgstr "_Avsluta" #~ msgid "Failed to commit changes to the device" #~ msgstr "Misslyckades med att utföra förändringarna i enheten" #~ msgid "Remove this resolution from the profile" #~ msgstr "Ta bort den här upplösningen från profilen" piper-0.4/po/zh_TW.po000066400000000000000000000337411362011671000145120ustar00rootroot00000000000000# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the piper package. # # Jeff Huang , 2019, 2020. msgid "" msgstr "" "Project-Id-Version: piper\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2020-01-30 11:46+0800\n" "PO-Revision-Date: 2020-01-30 11:51+0800\n" "Last-Translator: Jeff Huang \n" "Language-Team: Chinese \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-Generator: Lokalize 19.08.3\n" #: data/org.freedesktop.Piper.desktop.in:3 #: data/org.freedesktop.Piper.appdata.xml.in.in:7 msgid "Piper" msgstr "Piper" #: data/org.freedesktop.Piper.desktop.in:4 #: data/org.freedesktop.Piper.appdata.xml.in.in:8 msgid "Configurable mouse configuration utility" msgstr "可設定的滑鼠組態實用工具" #. Translators: Do NOT translate or transliterate this text (this is an icon file name)! #: data/org.freedesktop.Piper.desktop.in:7 msgid "org.freedesktop.Piper" msgstr "org.freedesktop.Piper" #. Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! #: data/org.freedesktop.Piper.desktop.in:12 msgid "gaming;configuration;mouse;mice;" msgstr "gaming;configuration;mouse;mice;" #: data/org.freedesktop.Piper.appdata.xml.in.in:10 msgid "" "Piper is a graphical user interface to configure gaming mice. Configuration " "options include changing the resolution (DPI) of the mouse, adding and " "removing profiles, setting LED colors and changing button behaviors." msgstr "" "Piper 是用來設定遊戲滑鼠的圖形化使用者介面。設定選項包含了變更滑鼠解析度 " "(DPI)、新增與移除設定檔、設定 LED 色彩並變更按鈕行為等等。" #: data/org.freedesktop.Piper.appdata.xml.in.in:16 msgid "" "Piper requires libratbag’s ratbagd, the daemon to actually communicate with " "the mice. Piper is merely a front end to ratbagd, ratbagd must be installed " "and running when Piper is launched." msgstr "" "Piper 依賴於 libratbag 的 ratbagd,這是實際與滑鼠溝通的幕後程式。Piper 只是 " "ratbagd 的前端,ratbagd 必須在 Piper 啟動前安裝並執行。" #: data/org.freedesktop.Piper.appdata.xml.in.in:33 msgid "The button configuraton page" msgstr "按鈕設定頁面" #: data/org.freedesktop.Piper.appdata.xml.in.in:37 msgid "The LED configuraton page" msgstr "LED 設定頁面" #: data/org.freedesktop.Piper.appdata.xml.in.in:41 msgid "The resolution configuraton page" msgstr "解析度設定頁面" #. Translators: section header for mapping one button's click to another. #: piper/buttondialog.py:141 msgid "Button mapping" msgstr "按鈕對映" #. Translators: section header for assigning special functions to buttons. #: piper/buttondialog.py:150 msgid "Special mapping" msgstr "特別對映" #. Translators: the {} will be replaced with the button index, e.g. #. "Button 1 click". #: piper/buttondialog.py:222 piper/buttonspage.py:90 msgid "Button {} click" msgstr "按鈕 {} 點擊" #: piper/buttonspage.py:94 msgid "Macro: {}" msgstr "巨集: {}" #. Translators: the button is turned disabled, e.g. off. #: piper/buttonspage.py:97 msgid "Disabled" msgstr "已停用" #. Translators: the button has an unknown function. #: piper/buttonspage.py:100 piper/ratbagd.py:665 msgid "Unknown" msgstr "未知" #: piper/buttonspage.py:108 msgid "Configure button {}" msgstr "設定按鈕 {}" #: piper/mouseperspective.py:90 data/ui/ResolutionsPage.ui:209 msgid "Resolutions" msgstr "解析度" #: piper/mouseperspective.py:92 msgid "Buttons" msgstr "按鈕" #: piper/mouseperspective.py:94 msgid "LEDs" msgstr "LED" #: piper/ratbagd.py:655 msgid "Left mouse button click" msgstr "點擊滑鼠左鍵" #: piper/ratbagd.py:656 msgid "Right mouse button click" msgstr "點擊滑鼠右鍵" #: piper/ratbagd.py:657 msgid "Middle mouse button click" msgstr "點擊滑鼠中鍵" #: piper/ratbagd.py:658 msgid "Backward" msgstr "往後" #: piper/ratbagd.py:659 msgid "Forward" msgstr "往前" #: piper/ratbagd.py:664 msgid "Invalid" msgstr "無效" #: piper/ratbagd.py:666 msgid "Doubleclick" msgstr "雙擊" #: piper/ratbagd.py:667 msgid "Wheel Left" msgstr "滾輪向左" #: piper/ratbagd.py:668 msgid "Wheel Right" msgstr "滾輪向右" #: piper/ratbagd.py:669 msgid "Wheel Up" msgstr "滾輪向上" #: piper/ratbagd.py:670 msgid "Wheel Down" msgstr "滾輪向下" #: piper/ratbagd.py:671 msgid "Ratchet Mode" msgstr "棘輪模式" #: piper/ratbagd.py:672 msgid "Cycle Resolution Up" msgstr "循環解析度提高" #: piper/ratbagd.py:673 msgid "Cycle Resolution Down" msgstr "循環解析度降低" #: piper/ratbagd.py:674 msgid "Resolution Up" msgstr "解析度提高" #: piper/ratbagd.py:675 msgid "Resolution Down" msgstr "解析度降低" #: piper/ratbagd.py:676 msgid "Resolution Switch" msgstr "解析度切換" #: piper/ratbagd.py:677 msgid "Default Resolution" msgstr "預設解析度" #: piper/ratbagd.py:678 msgid "Cycle Profile Up" msgstr "循環設定檔提高" #: piper/ratbagd.py:679 msgid "Cycle Profile Down" msgstr "循環設定檔降低" #: piper/ratbagd.py:680 msgid "Profile Up" msgstr "設定檔提高" #: piper/ratbagd.py:681 msgid "Profile Down" msgstr "設定檔降低" #: piper/ratbagd.py:682 msgid "Second Mode" msgstr "第二模式" #: piper/ratbagd.py:683 msgid "Battery Level" msgstr "電池電量" #. Translators: this is used when there is no macro to preview. #: piper/ratbagd.py:813 data/ui/ButtonDialog.ui:106 data/ui/ButtonDialog.ui:187 msgid "None" msgstr "無" #. Translators: the LED is off. #: piper/ratbagd.py:895 data/ui/LedDialog.ui:395 msgid "Off" msgstr "關閉" #. Translators: the LED has a single, solid color. #: piper/ratbagd.py:897 data/ui/LedDialog.ui:66 msgid "Solid" msgstr "單色" #. Translators: the LED is cycling between red, green and blue. #: piper/ratbagd.py:899 data/ui/LedDialog.ui:189 msgid "Cycle" msgstr "循環" #. Translators: the LED's is pulsating a single color on different #. brightnesses. #: piper/ratbagd.py:902 data/ui/LedDialog.ui:355 msgid "Breathing" msgstr "呼吸" #: piper/window.py:54 msgid "Cannot connect to ratbagd" msgstr "無法連線至 ratbagd" #: piper/window.py:55 msgid "" "Please make sure ratbagd is running and your user is in the required group" msgstr "請確定 ratbagd 正在執行,且您的使用者位在必要的群組中" #: piper/window.py:58 msgid "Incompatible ratbagd API version (required: {}, provided: {})" msgstr "不相容的 ratbagd API 版本(需要: {},提供: {})" #: piper/window.py:59 msgid "Please update both piper and libratbag to the latest versions" msgstr "請將 piper 與 libratbag 同時更新到最新版本" #: piper/window.py:73 piper/window.py:127 msgid "Cannot find any devices" msgstr "找不到任何裝置" #: piper/window.py:74 piper/window.py:128 msgid "Please make sure your device is supported and plugged in" msgstr "請確定您的裝置已被支援並已插入" #: piper/window.py:86 msgid "There are unapplied changes. Are you sure you want to quit?" msgstr "有尚未套用的變更。您確定您想要離開嗎?" #: piper/window.py:95 msgid "Ooops. ratbagd has disappeared" msgstr "喔喔。ratbagd 消失了" #: piper/window.py:96 msgid "Please restart Piper" msgstr "請重新啟動 Piper" #. The current device disconnected, which can only happen from the #. mouse perspective as we'd otherwise be in the welcome screen with #. more than one device remaining. Hence, we display the error #. perspective. #: piper/window.py:119 msgid "Your device disconnected!" msgstr "您的裝置已斷線!" #: piper/window.py:120 msgid "Please make sure your device is plugged in" msgstr "請確定您的裝置已插入" #: piper/window.py:159 msgid "Cannot display device SVG" msgstr "無法顯示裝置 SVG" #: piper/window.py:166 msgid "Newer version of ratbagd required" msgstr "需要較新的 ratbagd 版本" #: piper/window.py:167 msgid "Please update to the latest available version" msgstr "請更新到可用的最新版本" #: piper/window.py:169 msgid "Unknown exception occurred" msgstr "遇到未知的例外" #: data/ui/AboutDialog.ui.in:10 msgid "Version: @version@" msgstr "版本:@version@" #: data/ui/AboutDialog.ui.in:12 msgid "Visit Piper on GitHub" msgstr "在 GitHub 上造訪 Piper" #: data/ui/AboutDialog.ui.in:13 msgid "translator-credits" msgstr "Jeff Huang , 2019" #: data/ui/ButtonDialog.ui:65 msgid "Search for a button mapping" msgstr "搜尋按鈕對映" #: data/ui/ButtonDialog.ui:91 msgid "Capture a macro for this button" msgstr "捕獲此按鈕的巨集" #: data/ui/ButtonDialog.ui:104 msgid "The currently set macro" msgstr "目前設定的巨集" #: data/ui/ButtonDialog.ui:129 msgid "Send keystroke" msgstr "傳送按鍵" #: data/ui/ButtonDialog.ui:169 msgid "Enter a new key sequence for the macro." msgstr "為巨集輸入新的按鍵序列。" #: data/ui/ButtonDialog.ui:221 msgid "Press Escape to cancel or Return to accept." msgstr "按下 Esc 鍵以取消,或 Enter/Return 鍵以接受。" #: data/ui/ButtonDialog.ui:252 msgid "Set the device’s handedness." msgstr "設定裝置慣用手。" #: data/ui/ButtonDialog.ui:270 msgid "" "This allows you to swap the order of the primary mouse buttons. In left-" "handed mode, the physical left mouse button generates a right mouse click " "and vice versa." msgstr "" "這讓您可以切換滑鼠主要按鍵的順序。在左手模式中,實際上的滑鼠左鍵會生成滑鼠右" "鍵的點擊,反之亦然。" #: data/ui/ButtonDialog.ui:290 msgid "Left Handed" msgstr "左手" #: data/ui/ButtonDialog.ui:295 msgid "The left mouse button generates a left mouse click" msgstr "滑鼠左鍵生成滑鼠左鍵點擊" #: data/ui/ButtonDialog.ui:309 msgid "Right Handed" msgstr "右手" #: data/ui/ButtonDialog.ui:314 msgid "The left mouse button generates a right mouse click" msgstr "滑鼠左鍵生成滑鼠右鍵點擊" #: data/ui/ButtonDialog.ui:354 data/ui/LedDialog.ui:414 msgid "Cancel" msgstr "取消" #: data/ui/ButtonDialog.ui:356 data/ui/LedDialog.ui:418 msgid "Ignore any changes made" msgstr "忽略已作出的任何變更" #: data/ui/ButtonDialog.ui:362 data/ui/LedDialog.ui:430 #: data/ui/MousePerspective.ui:132 msgid "Apply" msgstr "套用" #: data/ui/ButtonDialog.ui:365 data/ui/LedDialog.ui:435 msgid "Apply any changes made" msgstr "套用已作出的任何變更" #: data/ui/ButtonDialog.ui:405 msgid "No button mapping found" msgstr "找不到按鈕對映" #: data/ui/ButtonDialog.ui:421 msgid "Try a different search" msgstr "嘗試不同的搜尋" #: data/ui/ErrorPerspective.ui:16 msgid "Here, rodent, rodent, rodent…" msgstr "這裡,嚼、嚼、嚼……" #: data/ui/ErrorPerspective.ui:69 msgid "Uh Oh! Something went wrong…" msgstr "喔喔!發生了一點問題……" #: data/ui/ErrorPerspective.ui:73 data/ui/WelcomePerspective.ui:123 msgid "Quit" msgstr "離開" #: data/ui/LedDialog.ui:58 data/ui/LedDialog.ui:209 msgid "Choose a color for the LED" msgstr "選擇 LED 的顏色" #: data/ui/LedDialog.ui:90 data/ui/LedDialog.ui:257 msgid "Choose a brightness for the LED" msgstr "選擇 LED 的亮度" #: data/ui/LedDialog.ui:104 data/ui/LedDialog.ui:270 msgid "Brightness" msgstr "亮度" #: data/ui/LedDialog.ui:135 data/ui/LedDialog.ui:301 msgid "Choose an effect duration for the LED" msgstr "選擇 LED 效果的持續時間" #: data/ui/LedDialog.ui:153 data/ui/LedDialog.ui:319 msgid "ms" msgstr "毫秒" #: data/ui/LedDialog.ui:175 data/ui/LedDialog.ui:341 msgid "Effect duration" msgstr "效果持續時間" #: data/ui/LedDialog.ui:212 msgid "Pick a Color for the LED" msgstr "挑選 LED 的顏色" #: data/ui/LedDialog.ui:226 msgid "Color" msgstr "顏色" #: data/ui/LedDialog.ui:380 msgid "This LED is off" msgstr "這個 LED 關掉了" #: data/ui/MousePerspective.ui:44 msgid "Something went wrong. The device has been reset to a previous state." msgstr "發生了一點問題。裝置已被重設回先前的狀態。" #: data/ui/MousePerspective.ui:101 msgid "Select another profile" msgstr "選取其他設定檔" #: data/ui/MousePerspective.ui:126 msgid "Commit the changes to the device" msgstr "遞交變更到裝置" #: data/ui/MousePerspective.ui:170 msgid "Click to switch to another profile" msgstr "點擊以切換到其他設定檔" #: data/ui/MousePerspective.ui:185 msgid "Add profile" msgstr "新增設定檔" #: data/ui/MousePerspective.ui:189 msgid "Add a new profile to the device" msgstr "將新的設定檔新增到裝置" #: data/ui/OptionButton.ui:9 msgid "Open the configuration dialog" msgstr "開啟設定對話框" #: data/ui/ProfileRow.ui:16 msgid "Remove this profile from the device" msgstr "從裝置移除此設定檔" #. Translators: this is used to indicate the active resolution. #: data/ui/ResolutionRow.ui:37 msgid "Active" msgstr "作用中" #: data/ui/ResolutionRow.ui:77 msgid "Set this resolution’s DPI" msgstr "設定此解析度的 DPI" #. Translators: the rate at which the device reports movement. #: data/ui/ResolutionsPage.ui:43 msgid "Report rate" msgstr "回報頻率" #: data/ui/ResolutionsPage.ui:60 msgid "Change the profile’s report rate" msgstr "變更設定檔的回報頻率" #: data/ui/ResolutionsPage.ui:105 msgid "Hz" msgstr "Hz" #. Translators: the sensitivity of the device's sensor. #: data/ui/ResolutionsPage.ui:125 msgid "Sensitivity" msgstr "靈敏度" #: data/ui/ResolutionsPage.ui:174 msgid "Add a new resolution to the profile" msgstr "將新的解析度新增到設定檔中" #: data/ui/WelcomePerspective.ui:20 msgid "Welcome" msgstr "歡迎" #: data/ui/WelcomePerspective.ui:38 msgctxt "A subtitle under the \"Welcome\" title." msgid "Select a device to configure it" msgstr "選取要設定的裝置" #: data/ui/WelcomePerspective.ui:89 msgctxt "A tooltip over the list of devices." msgid "Select a device to configure it" msgstr "選取要設定的裝置" #: data/ui/WelcomePerspective.ui:119 msgid "Select a Device" msgstr "選取裝置" #: data/ui/Window.ui:8 msgid "_About Piper" msgstr "關於 Piper(_A)"