From 48371bd9ee18df6330d0959cc1389e5e761cfe08 Mon Sep 17 00:00:00 2001 From: craft Date: Mon, 23 Feb 2026 00:02:30 +0100 Subject: [PATCH] initial commit --- LICENSE | 674 ++++++++++++++++++++++ README.md | 16 + docs/screenshot.png | Bin 0 -> 131340 bytes tabletmapper.py | 1347 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 2037 insertions(+) create mode 100644 LICENSE create mode 100644 README.md create mode 100644 docs/screenshot.png create mode 100644 tabletmapper.py diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f288702 --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. 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 +them 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 prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. 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. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey 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; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If 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 convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU 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 that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + 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. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +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. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + 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 +state 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program 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, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/README.md b/README.md new file mode 100644 index 0000000..5bb53c2 --- /dev/null +++ b/README.md @@ -0,0 +1,16 @@ +# Tablet Mapper - PyQt6 application for mapping graphics tablet layouts + +
+ + + +
+ +This application makes it easy to configure keybindings for multi-monitor layouts with xsetwacom-compatible tablets. + +Integrates with xrandr for screen detection and xbindkeys for keybindings + +
+ +Created with Claude Sonnet 4.6 + diff --git a/docs/screenshot.png b/docs/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..a38f5af7c9385f376f29faa6d1c1b2fa136f4fd1 GIT binary patch literal 131340 zcma&OWmFu&+Neti2@+(22Y2`2?(Q%UWFQ20cbDK2+#$HT1rGs&ySux)!)da=ea>Cy z{bfDK|vu&Ns1~%LB0C{1@+be4jT9*Pd+6V3hK>w zQxOqGDG?DO8#`+wQwu{VD5_}B=ysV-LA-8Vl{^v**pgsQ(L#AlQ87Goj8Mg_NrW=) zB<=eTDXe7F43U239UX*WU|1|;2)_0^p;OpkZy!$TCdrT$83g)oyqw3K&F)dn&OVBa zpQjFkp^J?^6}iX`8mM>wriDTLNl2RXBdKVFJQm(1vcmoL)(vY-V|nKpk578oz(;{B zl4Yqfm?PMjoj(17-!>S7{QGU1%i_-ur~-Bnr@ILI4n1Gvz;Fk%3{Fux%=s@?`f9AI z>!$0K*=BK!2HI+z*0yQ`;OQ1hZ#|}hGCKS8ZKm}vU8pf2;+#;_{z_05n6z6o^je?b znYbsDEK#-`6`KoF(HEib%>Sk1#A@hqGB*`H{7pJz{Sz|+jFYeUPJOJo&`ai4){43mrS1ta+p5#4`R5;tOc!7umn+B%GyMjAw)Q<4MJH|#VTxtI4P&; zuR}rYcGb0g7GpBArdLQXo|+;lLKmKx{)$b3j$jKH)w6Nup_Ai0Rm5u2tXs7vXs@88A>Q68sqtGK|yyq^B=HIF0t<-h-j`Z?qq@a})Vo5%aV58c*K zl@wmu>?7IUzFEO{@o0Y|vPF#mdeu(gB`9))7(PhP^Zc?v?4@D~;d@fjr##rLD=8^+ zeLR&{{^k+ejpxSgKB7V9KD2XI&7xeceOmD~j;%oh51C`kQ+#CkX+kA75`Xb-3AV;! zs6MH%GEq-KlWJmU*>twhmb>X5nxAn^&SEtF0uOmdfm0tH14AM*-9m3pi3+vF3*(ON zk|2=nY*m*gzW!nK)0}ygzw(zJXjrtE@GlFPRr@BujRht*#!Hg*4@)C$% zREprUv1_DNIh0f+uU^bd*?})F!Aa1`(_ptdcheC__*>PlTao82pV;V+3U#%PBRXEE zhHKGpo2AHJJb2Gw{si=Wevj;wXKMDDwx3V{vCV5v_x`T)_nAve59^9hr#LkgRXznJ z9$#f^ZEm678yq>>Rkz^7I>Fxq(h7X84$jLDhlK7)Wg%H|1y%!U)~VcjOxk#_i@n;A zm0QX(Y*`F3c6UX{tKIBu)tW(D?s~-KvD;s={tZ&c$LGT=VW2)6T1r*l-#2U5=Xozq zF=0G3Gd;G{W*XlrqCk(|I(QkEnOIqQFut^4JQn+-d2K|5wa%f%=sr-u+hb=tp5jAd ze1Zj}0h>7YXCbeV-@_xh`E*6n%3{LRCDH>5UYqF^^!uDgT}wNAnS(w4575JSa&q#j z%E}qL{7de~>;B%>cSj?RI1u~pCj3vPeM0JQ`_9ir0$&XEXRcOPw8&6xW4F{9uJvZ7 zer#^KadM4lwHRH+a7}>+x7m-knJ>2Me$N)3o(!wiF7K1`JJzwGzavkThZH=}!8M_R z&ej*W3tuHiB4s0}=HOeqTW9&t65k$_<3Ag=p7%)0wHK@TQy&1S_KJ@!&TkqYuYJ^9eK*k4PP#aQ!{MWnWxUOyN4H*?zQ|W~$#_+KK2q zGrun#>X&`6E2ZUxf8j*fw{zi@h5c%xplap#FA_sU|14>#&rGQ~*B)?BIFGF?OmDT0 z+*i4O;`&>5;*&MQm_F1msR9NQvYQ>ofKwYRzc8R9v^lDj=k#Dq(}sUOWrHWtRUXHxPv3+&wN=O zj-YRjqN1U;pQY1Rxd2pSeh-%gd&}8M#|sy0gL$c^@dXqxx-Zw98Mxf;`Ltfmws3w? zZOP-!aXtaew~Kdk$q=y1ze6-HPRN1KqviuJ^iN&dS?} zdFS4tzR-wBLAWXn#~pcs@2`J{>VI{fX!GRM@lF_dB>OL-YDP3d&ih{rsE)T_qh& z-DIz~K~0EuU^S&a?@WA`YZvm^GqmBWkmHFJ@-7Ikv#?Vx0zxQ*f8fnA&#L7j?ZWU9 zX=f%o4I?_-tp|VRh&PU%^=z;0-+cV#79+1Y5VHI*!RPHCedj6#BesWoE#?FJ)1rqD z^YVcsFJG#Hce3i(z{~t$*`%FaBx{L0EEX$z-ScH(9<46g48n!2A#>+!T9_lLMqf=1 z`HaU{+CRY&t9%X%QOQ5R#b!<({PECWU`qrF)~9IVx!RASTwQ66Xm;A83mUp#L*nqf z_A9}9vR?T`^>DE-_R`d(`T0YMIQ$34;=hNkGL}3GVF7;<^`hQBMuqTu+9Kj{_v@#i z_zEI=6WAS3>P1TigK*fUEqjBjRB6hHUY;6Nf;1()foEB^$GkrLEJ%f(!Q)uz#pNq_ znRGlcV~FkR6BQQ0?Ys{vDR)|$_H^aaz#7T?5wFqUsB0y^op(WL9V9xE&YuNXwzdr6 zH;jiGz29pn4H}seg-HL(ovW%8Y#Zc1Wb*#VO!Wuha5*U}2|X{|s;{6|T~0b<2PSLH z=TcCDLda6(m#nq<%}-{v)Jo(b_$=eGbB9e2LRY=-nuFk!ED{S((xwoBwZy2&fVNN2Ekjd;GtzXgR!pqOj{n!=VE~fj~ zK#U3R%1a>J5{wn~fP}qVa`L-$P*tNxhiH{J0PuvdPagIk4veEt3MeRv0>)LAEj8LD zm6RIKX83(wo-c*wf#$>-fMXipWD zkOo0+cAv`*5{JKH8%-1~Jc^l%5H34CnUjbKojo6wU=8D2*VaYWLt1v|#w-hTb=2JM zXEVmvvjdsK!(1CZM?(7aAqJV|pTi=XQPPrGoW6F_A1-=tcTd1(p;(ay(3M(nobYn% z8JsXSq{V??6NO$al*zz+ssm@GdrhepIxfDFx>o0g9Z2{by`Pg~gkcH4*Wm7QGB<2T zzH;ucRbn`u`@F%zgf~QV-u?11M8vxwkBogzy{sU+nE%`5Eq)vRzJ{BEBVP?|QN(_(SWVXcGQjRTZK&`-xPP>i&P-C|x zS*2Wu^RxXh381VWj1??ae))A8-3{9`dp_yE4E-%X7<*cnDNCB!q=C!o=oH_>t=Db0 zd)_Cvj{~cganJXu9WFU5j>h|tU*v<;nG0NotW=Y8yIe#_W0}zw>m08@!j6t?);jIN zaBDW4?d0a)p~phseXcLlwX*oeZdR(5ld}5nb2D@EgyD<%R!7GX{-^gSg#MuFqeah0 zvJn-D>$@R41vz=`YP%ZV?<86cK`NPOLC%b68%oOBoTmGLmJ}s#?b;p(-B@X`_K-4i zG$+;!C~%J1y0k<<8hTLt1@etlr+Vbh4Ql3)`O*_h=Piu%mi{!cJ3->(!O~%2DHg#= zyEZGItVp6wI;GnoT51g}-&SUvbfu+(dvcRPiZo(*`rR|ZIy%!sZ3U&3DivO56}o-g zTvv@g-v>Cl#3cqZkJh}`e<#7Klv;8V2LN&4?pLEm*Q0EV55~$=aT@kmFQ3Gd63T%|d|hc`9AA`Nfv{j0qP|!z<31E-+M_vxJ!WuDW4za;nUddnlRRtE!^F zvcO8E#fUgykghcS0cEaPzxCmkIQruVX2s&HFuu_(9TPj-O)8(^vTOr^{O}cJS-l_q!M0i!iP+=m(bZ8-6SCBYG9GmQ^5qYrY^_Q{#fu=WR11j}$) zfMJqPUu(ABd4E&okGA~RYSAk=`=<}c?r=|gWw(qnS`u~TuhZUfFsQnEughpEfWKv_ zV^LE|>K!o7avE264nt`Sa-v`EN;_{-cb6g+}0HQL-DrZfWk%<9I6R~&t zSAY57h>=pZx6^z6WCqD%PdT3q*G(8P*c=PoPH-(g+5MJcY#@#w3|)eKeEW!^kj}I# zr#(rxvPG|6EyEG?j$fr^F>t@`Gai?b^t&7fKXbx>9}mBL0i%|pRnnLe{^>;Tn;F0H z{cJJ;DR^p<$NA{24io|pBTTz-$MP6*po;|eYKE0ZspF^Qh5h;gUCH6A})uMuKX383-%)-4@>bk}6EeGcFWIPI_x?+F~ z1n|8FyKp@^PX@H}j>p3rb}Zw|b))L>K(?W!sk~@2`94qz5G-=sMj+x10xZuFt~y=1 zKzjE#dd;dgK`%*ymDN=X)mD3<@0RDen;85dB{Z}i#=r6?$hDDqw|F|`h*oE;e@N%u z(L)|s4y#ryZbo4{I<b3oLsQ5ko$8=H%t6h ze@X^C0rw+^Rr$^lv|)(enqPhltl4Dnd5y}iRE`PZ(OWJJyrI1F;IHwsy_p6FWeme* zwK4b;E;lD9rjkbo6D~DVui6_v=>&|H^j4dbz zS59FnT2jZV3Uzt4rtEBGh#Xr)IlTl0$5R#M>cb_W$MfY!BQdU2u=91-8(xAa(4UPn z|DXt7M+aT<8o!>c3@)#q_6-Cy#l;o+BwL$Q)Ai{eg*_qf4L$D5EvhrLW*8go)>ws2 zdQaP}bWjF|Wn(3~8poCpZOYp6@oe|0~(mO49TmB-&?O|U(N3| zXm|2(R0Ib=vo`}8uhMy)U&dR$ZAKCu4AdhuHQhY;y$|lmD|E$bD?fgqU66<1X);WV z){MF@aM0CNSMlt7KlSb%bc#n>XT7za8nc?WXcuwkXt?x-ClIjMbm5Ks@pv3jl$=+d z(Qzii z_%4nrj&hhv4M(`Fd-QhFlGRRVNK93>#310z^roOIch`YF+~$ zyTh)zi^SFu>2_E8%1WY>75*_X5~Z^sXkg)UblZN=_x1I2wm56;SRv0aWW;})O)Rm1 zwAYsB8~p~&lwU_yTTD9FLhL_29M_w}1a%e_7FT_#8#>wrlDs5%0vuMkx1HD83=Lxt z8C9IJ+RaQIj-XC)I3+5C^Yb`It!s9?u&mEeE_S1;bF34V$MtNWa!H*l4alL zl%f-h{^m%JR#j9aiQPIt$?P-eJ!Vr>RE#U2I^^<5Solhz$>tc*G(TTy9ChD>DCK%H zJ!fcg8ZkgWS;Oz}Ui>fQD4jw+ z#%4PqtHWf$=hMtMCW?-IGy~2>b(R*3=>q#NWF)9s4@dLba(>o2`BYTpib-TqPSBb+GpABGHp=VL7^dr3KY;P3aE4d!&c{gaxR*CY_e-Ar^U~lT*dTY@Q(u2cP0S_gR8Pvk}BUf&H(GZd7Lq zOBV2GBBoD!hR0(_XyUIo&@dex>()jH9~iV<*H2dr^SVPrL*E8PBD#VGt&IYhQiqC* zzmWtmz^3Wc+1}(dbA*KsE_dNAy&#ReF}1>|l5}~v?_QW;EG{k@Io05F%9i+@ zoTIy>$>C1PkVu-E7p0`*v?2X_C1{w6ifURFT%=NDXJ@chV3n#!W_F1ll2S~AoLXm7 zZk$8lC3O}=8FkEW$t2un7`>(d6dPjt&Pl};PS3Qe+(JeaIA{F*%Uy<2dWuVAylw&o ziLMD*>FBx6q*wR0`_;i}(^G0Ijjr8U>MKA3(B^W#%QIiz80@LL;MrGh@63z*!cVEC zyHePP4-{5TP}dLf-9KUDc;7un?-v)#L!R%Dw8g~Yhsrn3egP(YW;>pczrFFGhaE4r z?^ZEMz^1nsSoq%k5D-g4ZC~H;V+#fitBC`Ph7NnKw8Dxc9p=>Zw8`Es4K+0_bxE(9 z#msPJeZDx!$EegzDUui?Ieb#gu*>jSe?OJj$hiy zQ9W9%vy=iAXZ{2k-cgFW4JD1hjuVT2L|Y<6TX`G*U>2=PR9jsuf-T;|VptcAS4ScU3kyR!Z~QXOxxrc+ zhiB|!AmF+P7G`I3m(Ph3^q66Nc2`3A^_5!);?&yk zuMLKumdaoi=l!J<)7`hxibR<3y6tYJQ)A^hki<=A>wLG7h_Db37Y;b$$%2=!J6`?< zB`t{V-+;o3d}LaDhB8=VETzp%Q+oVz&g&>*=-f_V8N;tDoBg7jl-vo3=H?H{Fe1CEiEbBj>muE zHz?d#H8}(ro2z5?*4VT%gq2iP)7S^BH?cVa6uZJG0Zn3JtNFbFznw6h>Wr`UG>Kx$ zqF-%vVTA)xp~KWq%54mCc(j4H{x+P+&0<-<(h-{&nbEj7;0hIec~y@FcT0R@{aSw^ zlX#A-H)nWFa9Ql^$VjB*<LPA*-kUn#HQ3zW(s$&kOwkeT`!D@g_v#xTofgFDx}TsrRvS5CJ1J+mJW z`8-AVJUfd%Z(rN&dmgy`EN#xqE%l>ZwXYuk@ZsEOL`ynLu@Hw9n4>=#-+KdvgGK)U zx$=MtI^Zq{CUye&+=D@X)@(#cLdYbP3(RDb)}It8HRu3QG3Q(*jQ#p?M1;?Hc;e?6 z21Y!P2gl)m4AdJTT*+*9jZ#%`Wg1nYkq}^Uw z1tm45y*Xn+4~twnXihvKV4W0IAI<%OEP%QnpDQ}=k}-2czF3xGOUqOJ310{Z!5#CP z!8i6nM_wSh{c6SL6{Ft!q$Ti}LkT)%4iQz6kzj8Y1p-*0fFa#aqlSOv#^s$xQ{9*{ zcn;ZaFldd52~{K#RW9wTDQ_zF>!YhEDu2wkcqiQZxBdXA(gTtaEP zK0M>&hBVjLH`V7i#nZlrg=jW7db@8R1KJFGW0A~hI8S!7XTeU(%_7SlX8NJHBuTFP z{8s4Hph9EeCLJ{_AbF4as>ZSYurH?>2HH0J!J+tl#K=Y-8TtDRu0T+Xbe_buRq8sM z`$+V~w`e8?ihZEv^EZewo?yAzv3h5aFK8rGEW0+&Sx z=C$p2;JDeFnwmm6UY2x03j-lEp`OmT+|+4Z_x449xUjF#xlEMK6KGjmChscK-P&O{ z{9DKzYr`2Aolx!8m*&@U1DEw{wzs2G;$+d;><;?m=pb<@rD5{r+_Op&`_uE`V{#N) zq$&$-Oe|_-TAOedm1qE88@de+vBS~0#>#iv5$#_p>gx6vW?wVCUM_=-?-;R59;^2ZItM6u`8|cCSLQy zCzZ}UO`rI~rmdGMO0(6#eQ@3cZf9YzPJ<~8vKnHJ%j;|xm=)3V8jUVScHbUnUTS_w zbCjQ_Hh(o-t?MC)W?GXw?BprKG3hi&(B<{^IP0h1SJgD_3*uW)S7)IG2_xwTgF?OT zEc%M+=aNmk#lBZ#tQZ?(U~KEQqOlv64orm^BO*p9yjy0Veg=7LWLFi5eb54y0jQ0q z1_*a#93Av1Tx&MFGWtpy#Kmj3+K6BG1_Nv7Hkia?GF!Ng^3JwO=}M)>zA?=i{){ip z?PRX)TA#2I!*j~Lp#seE2?$NoxIK|mhon7lv39*>*&VodrGJ0n7`a-wj^5k^dip4b z%K>^Tyk7OCwVPdQOB;*4O9gkE26+M>T8+P%E#FVszJ*mVQe#>TFxfD17y=r=;va_S zd4w09LXluo^sVNY@9=poP(7BiViSQ<54F3+QAPLPavl%L*T?X_#W=~F4(VL8xf1Y0 zcEs1WF!OSFiBxmSKehP_9KQuq(ailQtV%EaR zP>d_!-}8@#n0p%U>;L)RlL7Vf$fqFSbz1(t{=Yzz|9%hZ|6)*xh2elhJ_4bQhCTN) zfMtIzMwAQXZNPi)jsV@6;7+6X#CTTC^8X-0y!OZK6#s(=q2uhTo8kZxqC}+kI(3g@ zvOHfv!QPiwP$8nqDmg&*WNer_&B)5m*27fVl&=}8XA|?-qh2D4`dyg0rZVvTJ$VW+ zL0&|D#l@JS|7PEQbT{RyB2Th7w-lOLh%*2rPZNLcq8;#qJD%ddT+p|R%uXg*D)e;_ zK~&Gh9k0pH`bAb?n)N&IFfH-LG?q#qzP_%%kGKE(cx0g;9cw?{AL+B2uyThD`6bm- zd+;~#&%8&PG)jHFc~%iCSfaPbia<;j|1Ay2NOSV& zR^nNC?*>I}(#m>+)2-v7iz!4BYKIoMF~WnW^oHy`lhrd9-Z#Y50BnSdG>?PW2?qns zs;Fg!gBU9aJdglpRo_cl`=IC~IH3ieD;57GiHfqq^y?O@k#D~5)`q>gw;)om-BQqq zw5#jEi|m(2gL}0|QIflXfOo=FeD}ixxbC)`c;Fs`)mnOzHymekc_+(CI?AFt$}nj~ zz=Hi&*mDz#`hWu`H~|GUAL@;YIp5SI>W8PHlQH{sn|^L{4UU*PFa$<>uHA34H#8gZ zCZeHkVIg6Qd<|Z-N2zU_Pki&IL?!o7K5=akyV?<-o!hupyGO z#RhNHg<#a^=aRql0zsgZ`p;zin-+&l`Uu6W#trfJnAbpzz{5bseOoC?HhMH#*Z$Cy zTUnC+-USq52Na${T3iK5FzdJBcp7E*W{q32aayZ6DfhRZ$8SQ)FQS;vRrF8r6ESSA z_#HCmRbjhF+%{&(z=Gw9!Vm`>28}3BO?ul|rI%KYWbOW3(!qC+syXI+ zPi$Z$aJkqoMABh=bEtaq(p`=M^ zR8)Am7C(yMp@Km+3P?7C?IxVQ2WDuHe+=6X%A+bAO`|NEY^cPtnJbEJSPn=7Pp~*> z*-v>lhW+T79%ZxDaz7d?GHeWCwBbyfiIf;&v|x~o5YJSALjf3|C_wFDJotl#jH=9t z0o?+$s~Z*b904IP@Nc&mVeUBhn@mQ`u=XaLhJ-s}(&^v*S#*qjwwE6G`U?j<6d&%i zu}0+H7Faeq8?;4AQNpTc{#?qa!|1*yHPYaYaPxdIc-mLvlW-sVUoV$CJ7D@Afh=z!|+{EcRw>qkZ?c|>gWYQhc zCGOiPH?xKl6Dxtkv$?5w@)OJZa6(p3^WP~SlbLVa!Y|{=>8Pa5j_BN45a$bW#R<7y z6c3lsoRJ6f_q2Wu^N_7h8}-QFIp+w&6 zVNcs1W;6OH;A4GT*8c?nJq8n6y&LF;Ez`_LbXxd9ok*EAQA$6YthPX|CrB8VlQMW8AGQ~S(%evV$<(h^f0lhWj- zVOk|g=9lz3=c|c65^$ezaE$W{DmCix*qhnrLhsYQx5RKG2Q?hCaX-#mImvzhF%r`9 z+~6BeDkdct8gd4cjj#k2MA@~>GsdK)H|<%tvFqxLGE}6G$uUo{^jleEgzoU1{Y^t8 z7>sG_+34myPHpu8P{-%l^erzf=0eQ~j)jxC;UK&G)?A0dY*LRM+Jsw8f*E`-d^ z>RVDvc5!_bzHo2u!Oze?mnrAa6ILR{(snDYzeq6Q`|Vc9dEsl1`L16O%VThs`b`aV z&URHOz1NqnlqRbjXBRRT#-j(fP^uTNyl}UEIIGq0rw?0L9{+^740)nOc&wq z(qHzq&wSh=^_G@0;73-xik_w{Eo|1SOh%mDwalmNkH@Uf&!+&< z8|a_gexwNo3#8@t43V?jP0*_sm8@It%p^hyrX}{3L%5xn6X&JginPI&Uw*7W6ENz)_c&gX6bm zD!zcvm&a{4Z@MR&Zm?q+o@_aJmE`$p88+p5x#rIA?>Vqow}S>X#j~UeyxtWl-6!z< zsW>)6$Kwi(xyjhM!u@#q1ukpRdCfoHn({|)%KHQq1-d#tisFg;<1t$~TT z+BY-LRhE^}LJ@v-XFj^5dO~EqQhAOmO)1$lax2+A3q5ay3AD&omlro68fjC7viBv; zdlknqy>V6|#)!#{2kU|DAYs&KqOWBKUr+Ggwy+1KX;M5r?#-pmBZ#qaE6MB7ZIT8| z){WPD%J^qNChf+o&UHrGb zG3N(4jhx(aIimi#YkT{2Z=lvWXC(k~>7YXyvcm*f`dTNm*BJTlL1__F6 z?OBHxl@lpp@aI*@0T>$^8M{mRBfx_y$4wmtjQ|y#YWms+6q zw3jria3K?0fBg!XxDZa}+t?Cd&ToPGUTk8)qkz^I7Nef}qJsF%cwB}7#LG03z5FjeSeQ&<T%^NqZRhEz#LPMKR|LhE0q!&}~mUqQzA zjTKjm*FHVvu4rzw71SK>o^n38MM5Uc7r$9SrViXGt91l~ z;kGEn47wHx_nbw$cK$B%QMf}P2vgD1xa=PC)m#VZ&U4n6S8r-pG zz5144Hzi$-v9Bj#xz9wM$jRwu(3Ol^(v|*&hCpQoozBtqL73N!+H9Mx*h1-SFSC^=EMlwI zo`srtN^$XhrIJ2?0<~Cn+GQ!8mZjR^^R-nHS^Q2&69KLcN4)sEBX;4Lh;IoQazJ==mxDcb>Ext^@ykiHGlU+v zL7$a{m%E<_m{*E+njEsa;4D;QVzVf^b*WIFp^(nW27~ zu8wD&zs&E#EdoXdi3e)JR0c0Cc8j^`tcF@=)X)1re-vKed#Y&VJ^HpIO-zgN`NB#J zcG9-QaNuTH6WsT05U-lIgtI$4Jv~3g3XH!Y9w(5K4?}EFYRRAdK$cSM^oEqOfUK@H zvipwm;iE7z=2JQy1ii1Hkmll86jsfirhN~}5K9bT0h2Mp)ylJr@>;zXo&j3Db!7Ob z*(k>`r;W)-LRx9~E00P}`NgUB^WIk_HBFDJKYN3hssW<_IQuR8k4_+;M!k`inVlcN zE^)ivV{GL;QzFMDpc4m}q|9f0-cE61sxV+7Tx#cw9qxP1F8^rWKo}vE|ETkpYw1qwnv10Vz)~J1_gTfabWA-U}i|u6|lhHx#PD1 zig&gvyk~Krfas>dgk zQR(R{uTPdI@29iI*xGY^LBd`#Guzt+D=2Lg4=M^Pib6unJfDXat8;GB#5#l$0cZ#m zlDiXi0^l-0Fb&tmjJA~ScGZJ+db0c%((I7XX~bsX7p=dzkFx&~z6MOk8{^0(M4a+s z@m8-pI$uFNUXNO!t~G!axcKqYboUd_|I?Jb(v48pJ+DjK15^_aux#_|PJ=-~j z0g&rK`B=w5V#G;eh8E`fpKCb|QgxDu?k}}zJezDO(RxZgQZ{Ix$qak*W!;qCW4LRg zAbUCLE!2IFHjUzgy`@&+M~-){-Ss}~sK=kdnz1Y9yB~V6i%Os8(S{djyW00VPl@?> z9SahsLD*M;V0Ia{FWPmV7@8H<3^8d)kq#lh<$1w4Jsh1b`4F0X2jRKAhkv6Nm-r3> zf11LD5=AMS&P)lE{^ivI56gr)C01G;yRGXVN_w#_5=+5KVCz~dlmdQA;%%EFBCaSrN&rdX@{W4}URab`*@OzJ#e+{9ZegBM=4~em_BMn9L0r@GSs% zEqIuhMQ2&S7bKK>e!8Buu6N({U)3w$@S2yxCB$9w9<6L+5K&c~1PON&B2<5#Ft9FC zVPiG9tdsGK!k^_b=$b{|R)fm|Kq~Ge4J(~ylkPvs=m>_>i``{&1I|5)Dl*0w^J4Dx z73u3-3%UMxf$<;M%QR=3SBNwW4d7EqAL`3A*YyqcIrOS$d?C1r_pU(G1{RSZ1Y~CY zZF_X$ZZn$i!q~m5TXg9Bsi0ns-AQYj9#ZvNOG{$Lu(E^>pWS<`*f{HEXC&pEY%{~+ z!z(R5Ft)LXa^akO3d4}1O4->Y27Kd~<04KGy88pavWy%Gv4OFLt{UW$ zW+PdWWRON)o*_6fK2DO{1y4sr3@wV1SL)Xj>lq~<6YZgfBzbI=!4!of&C&1>?bU#; zSfHU{Zb*P%56THszfAKy53A-G*x-wtV?26iHc3Xq1cxBpr_`jex>|;JY17yGAhW+V3rn0^ zQc#hT^T163O-vRY3;-lklSQ)PbM;pPB_;g0?%Ac;np!GTm)#@;GX*a{flU(3pA2~! zZq^$&RFRtI3fe9cgGSSiky}F%E;0qYs8A7tmzg zN9qA+HUJV`ZC}wM#;Ssu!vxUHOb@&s;ktTG?>q<+Q&VG;)02nSY`RGFjNGad>XC)1 zPCTc#*}^XD?a|TEH5}_yk+`KI>9o!?df!$__O971ceRe28~cE&op<9CQ_;@WtG21hrp@VkMovIv=45=3$Jz46-xHc7Ja%4 zQy6TBta5XU^B&?(l(A6|TXNT}Xg*%VI$3kBsi{MR0H6RTff@iRwzP2;XA3LJ{z>5u z8MvG6K?9;%0vR!(2ql$88|M~<5gx2CF&N&D#JRPnp zwxTEnAu;*g)Su(UC<2re3L?jh+HKF$tESPrnbZxmIJ^kCEw!R@keo5iYFs3cFi@8i zBq9j_R<)RhwmUnDRIPtN8aLqv@~e7H`2WCE-pc6c>)(K#i(?-d10(SsR;?7HGKQ@; z^j(3?8C0u-RqcNjbeavLd`7%6?$&iq9ZmgrBCGbzz*B{C1UTQbi5L|C?4i8E>|IWH zv5@&OC`BZ1tY7-YbnZW}$%Op;;b86IAS)iS@py+v^$r~X?+%&!!Vx2*xa%iU!V$mUT}ab!c{TghY62!l4ET5B2JG}A7oGFI#@ zr#{n1|H5gfBLc3ey?-k#>04STs#LV0>Rkto$1kG=X@6F5BzJo{;FycHt|r|I-t=-R zk9`{CcZzTuLY5t8YSwB zHcbCwiN6$hLh$GwR@AulC{|Kms@1<%`Cs&=y=F{&`ESoJg_~D8?PT;|y)o({PFZFe# z(KuTj4$`Ozk{SfUKHQKL(v#tf@-bZEZXom+=jGn~20R4{xZLJm+gxSi-d zEikBlO>sOP)k<~N->x`9`Mlr}2*0j^k2OZ@S$4Aqp=XoSpNHL%l@6eCLE@V$tK_)H zTD^wr^cVT4ZV=u@*!n<7m0VME4R`MRnZ4XhT9qsNk*cStd(dOfl9XO%IEv#ZuJh68 zhbT&HQ^(5B&zx;JIZ88)oioMMV`NF#on)vXw>zR66=%nFbyMol3hg7qNxWB?raX9Q z_emOGfRG9Zc}&er+e5PT1#3f>^z&ELCYVx{2v)~aB z;^OP+nJxf03AAq^5a-%GA>%fe!x3jGf(UWhTy9OEeF1J8nK+Nwa_YrcNR)8OBNK>y<+A~Sg-r1PSI-Bi5B)2ix# zEATXrTmOHxs~>A8rWUg|5Hr6Y$qlNi0y`?4?4AbX*x#7N8uai?xif(MHo-$#6@ z{gz`<=`Tv6rltgRuu!YMC@B?rp@wE+c2ca_0K11!rO3c+jx+5Pb=sT^?EQtURrW8$ zFwkT3`6@eBk=0vEhw0N6%W^s9q1^&kHGuJ>_y36bx_q}rbL7-8yv$rLb&7-5zaHXK^{Jt-J2`zq| zcbZc+$5T^&Xm=Yz4EY=BblS@}5-IOqTwxjx>c9PAkUOV0Tsb8QT~u3qx<8;IknuBk3I~DbZ8f}Ev$~cCsM3$ZD97&sPGxLzFgv^Du($2Vu}+te*H3R;#J$1A zZG^{#0osa?HpOOCg%rD@>ph@-QJ>!oyxh= zt_EA*=N^L^qxTSa?1Sw_uLb^?G;KiBeV0=Atz}JGtH)!2T_{oZ7Y>=*v9a;bbe9<+ zMRy}ykS7H^Y=JrR=a?yJ>3+5;aD{HGg==KsPue%$5ze}R5l+KvWD>a97xSLm*df*7 z)o5}XIP-5tMO(i|#;$hT7`8k}d>O=M-#acd*I91!CcHGRIUrbWo$D-emdV5ypBsDI z{8!;Rg>{POGvC(O647TF^MKLj08SPA5-_gQM=3i(1Um)%xwk8JP(JmmGcKjnuj}lk zdU7u?cLF4pXe&Kkh7zorIxn~5)@^QcaV|&K{P}96Cc# zW$gIde2q^ul3r0kX*P>PS5kOwE_-~LqO#gt%RyzG93`N6WjPBq4KngZ+^m2&`e6R1 zC;lgu-8L|XSbZpgP`$P5|78Ij@D)_1*TY;w8Y@UtG6laFz`?9fx4-5k1(A52o8F0ic+PBK-P4Cw8>VO7Na&j|n17;NOU zV2=^T1_3)#Kc)`Fv~JqXo}U}QNogHm)O@ip`N_WgFMksH*WJ9ek59yqiB?76`amtr z*+unm3sMw!rPj#E{`oVYr!!tTbr)qIwlB1wMpm5qzgOy5`3!n(jjk#r#79d{{WKrl z^|n}1AVaO1E4{Z6^ZYV7S;X3e=_}|HyV*{TjxbB-bhd+%<@4UGMh?(=0nk6!=_8O1 z6D!G#_*4Y{0bm8o<-zI&f09Sp`}(4ba14qA-v>Yg4a-x?)eBc`rM*9H2G@R>e5 z3^YT~5EbK-rQgc~{#ZLY4AgJ$ibQrokN#24Ba)%yU_bSzw_Ze&+qwi#t#mg zna$p7t-aRukLz05_s&mIqI;$?%Y~vKHu&AfX1wx8(;WjGKlSCri6Z|+{%gUC+0!(+W%e^3sGpl zZuv`06g6L}M26Y5bPEa@w*K(J?OS`PgP*eDp>lp46|5Q}_YrauBCApC!YGS-6u8s zPsIk{*es)nz=`=_BuiHIl?#$V_vK&xho8!z(1VIYMR2IcWDE^Z+Q5v9HS;4>bNrx8 zSJ>rw5*QKuJ|IumExyM!8pfCwH75Dci|M?eAfQ{Nmw5N-M+hF%Z26_rZrz0r7Yh1D zRVJFNBb#+a2;EUrzqrOr@Dn8sH&Mj2ygHSJ2lazz0nu)RRFlN+>L1z&f4v8*wP;at zWyPja*^inSU14Qn^J`S~qyGeqhYR~YAUR+Z4lh>a%Bf}Ym(iT7w}X0EN}I*(PB?i9 zk7zJiscC9jAb)|?h|i;^HPv5(!)?)DVY+QpcMCGWZd0loQBvfqAS-K9IIUn{02o~R zozYhjqN5sOAk7FQ9J6ZJ?#~347`+M&I;U5Qj*2I}ct3Frdkoe#GX92)q5>oM(9Zdi zKmYa*$v)bkCunExT1XtTMCpyZ-S=DNA?VTK)6n&hE!Nad8rt7(SMNFYw-NYN=9ZH~ zZvE*79?S`i9F$!cb|i;qI`2~l{IO_nc^XfB3}IwwE^>)0X6+2Th{eDRH_P+%V6P;9 zDSe{WKIvl)K{h3i@%0p=dBP^LG|h1E;iq!JjyzFM-jJa>e9feH(|9}cY7csQtlpU4 zPLVtt^J({&N@ERYqKZ)?p_jDW{_4|gB(#!;!Q}t1pFJ2Ilk1~#38)Kl0p+o(pN0Bn zQ8u}y1qDgU#0t%AlLn%#SM4YUaPcwqEvPh|j1C>bjul(r^8u-tPwH%&e3OJ!O}fS$ zf)Oa(Cqi`QH>^h)0T@1DyBI>QCuK$U|Q&;h)grf}wr&r;W8?kz|0{s$oQP zSUIoW!>--*+Q&Af| zmQW3HtP5r9Z%BBVzc7kbxsVk-&eJ3OXeh`2l^l>5ajJeeui52&`Z%nlRK>nv@ZMP! z4;`g){0e{hL?KU9jv%6Epy;*9J1}M=*TuERR6nU`o-!#@|H3V-_+Cf+L+0^wXKWdNn42f9$wuxj!D^Lu5;T zP*IVSH$KA=MIx~oR&k%=?e^@tHN^@Tb8}ld^2a9|l*A=S6|rn4tZE1M)L%sg z_>^wssaqz7n6?Zr$EYdMN3LoHKg+>4W~s>iU4nb>23Re4{xF)KFMg0hvnCF!RQLCn zb*ij%(42OZdq7*GQ91AD!tP{?9o!D={z&@`72yjb(wXLw?BvU6iR+-$c`3<0)3Gu) zXH+qhx7&EiHkaej$cT)zhtt7tkM^}4PMi{>%MQ!NhsGu*Z=0Y{CF$5n`pE zzFoYUoM_uxIn2=xR{bq8%e|xNR_C;lmzL+k%o5jf=Df+Jm!iF#{JvCld7IleplX}9 zRBt39E+^dM1i#CmObR3($2Gfe7Pb>BF*w>FIIQ=6BRwtNv8o9`+S}i>|9!Cw!dBN- zoG&Eo9Q@;u(X-+Y#_Pz~ImnNN6ZeviOJ2QB|%6beEddbrC3`+BZIRb-~swiv_h` zQ!&f^?hw3(%&Ilj$E9Advrr%OPwc055L6&W+pUH|@|Hwa^^MM@ zQ;=}`th!@jdiD3vvDa42cEOmWvw^>V{#*~=IJEM;k7Bg^4gdM2c=i%$WcxAGayRi$BD6>FaNv*Sn#g0*zjN`=pPS0QUnBM08|HJ4~1JMtwcD1KF2Xhg?c z=v=J}pgvYCE}h#;k4>!{0@zFdo5&1Mg3Q#(MtI3&ko{g}npSg!j5;k0PB~%%E;pXl z1wFiz7{Ps9-m7i+8zi9U6%t@ek?^lPm^DWq+9IpMsDlHA7i6-uYf^g&pB)#TaOi)VDTDi=CZ zR7jFNee1YvzJ4MM+5oDhpU|O89s_Ks%tP`>Jc%b1I4k5PfBJmF13kmN7t}WLripN5 zr@{hM26*2ge%W(RDjp*8{}{(qZA;r|P)aDz_EuLK3Zlh-y7Evh(Rc$`P=>1#E(Q81 zL{)*o=62g>Ml{Q&E)3TQf-7Y%Ok!}w1crjtou2!$#> z=7bhmUaAdWb-zgQ`*Kq*>PkYbwgUPL#l-%WW@C1`_oxEDNFy+yMZd~?+i`Q^>r7H% zI{9@Mg+~zkZ;MO5v)5sbQ!~6qv5$&}cT}P+g1O+DH~7Jkb8qZVwn#{c%;kHE0|u;ChKomgv$Izd9Z8k0H`dp2 zBFfD!&6F7$pwHgoM{7Lc0Cc1#o~wkk^jKqnHHBW(QN^f;Yy=+do7f(-gTsnShd4>$ z#FHHnteyV8E_(bY;UrV?FabR}i)5L%E!sUFAaDzFsXkgz$4XH{BPlxG=Fe7OOwsHf z+4BX0H&Hzjua8bk9+5{%7@WU?1&9_XVvrCJXA>m`C_H@kG~yhh92G$)*^nF{nxsS} zj&!b17myK=w|a{Z+JQi9se`V6(57pRd|Uf04mnTT_jP)P_X-DT=@9*#P*S)r8HsVp zu8-Jn+wcWrY}Zgja*vmSK%yL(>Vf{9(9E>DSOAiK>e9X+-(BYGgtLP49NUDgT&Y0k z=fe9yD%1tSE72+-z${H2D&++JAZ11p?VA0~aDYjWkS}t^5uI3kI(lC_T6QJU1!-d) z&ns;b0TWp*eqHp$X3d+MvSJF49;-*wP1r-=Mg0ECuOknuO`+sh?R4I3NA)Kpl3(cUZ0yixl+h4c#>A))(0 z1iGbh?Yhq*zDCHZZRU*hjegg^dpEz8WUc7atv6>Ui$H zqR!X+CE)1jNRjM*WJ-)29+zTg|17WD5x;|Zv-)L!lo9=vU7sABlcVR?=lAyZ$4}4> zU3f4Y9Ok?X5WaW5+U6DS-xu2!ZFO#1f5182pE`f3MV_}<3C!8&`!|4 z^61LRbH|OosuA7WPh>IJ&e1W88bx zRq&hdawa39uRhbm0N+v*XY_`KZru6xVR*g zEXykLVMP~9`AtLj(wV4$X@Dk&>Yb2l?L zKVjJQ`W>(r({K6aO}pOP-ic;2`Cz4DbV_;pC}@lnbu@MM<|ps~>zNv+P0^Mz%AnSU zo~$Ill#Qr1>OCy@+?%emv?}@Fun*xJub`o!(crqVbt)E^|9p$&1s;PBxZLL~EG+z$ zlG4)H&DLeRdkpG0w>+>-S8nKbAF4IyWY}V8m_h4OA&}QmJy}e=;aqvdR^sPxg1quuX##RzzdLPBAcnjq z5UWhiFHl4g_I%SP$Qx&(_DF7L8!VX|l}KtC;KD94EP`o`>@AJP$<^vE%8-}EZDzyM z)y4viO>_dQ>@Yxp;idlg*BNa~usweGGyY{NY*hwCVH{kPKBC)4_c*~XD~2v?fINMX zT~%m^EI{C6^lcJ;%)dm>&*ql)qE=*m^{|}$lLI@n13P{Ys+|o%397?v<7p41otx){ z*NF*Djg7C_M%5XU(h85teEc*VO{Tj##;$j^-^1-6cmqtF*g6Cs*`2#&hYO`NJ|H9{ zDkwPg3%lTD{NvE=f4Y(ryVTM+&1yDTU*n+9q*}R+sIs;^+^x3Ohn)2hw^aY~N=}+) zv78#tHryVzWt@0Xd=TEEk0aZ*O-#a5H#sp?I=N-p_`n$9!B># zSyrt%NM`D#;w$4?K8t4I6erXrB)F>P-9qFZcAnz+xT$3y<1l%8dM6pq+xsQQE?`RF(<&_} zb>N=N^Qdq%BMysa!_F#1bAzK6E`^i>1M*siOq6@<%_Ybqmmg_`b3||;@9=N(q6BcJ zKY3eZib0;c?c#MB9BfGiP#Fk1Uk&PU-ex=>UwTfPt%LHTwP39kJ#d{U1wlx+_-jcz z)*{0(4GB$h>ac+zo7sFry-%j9-YH?|`116TP0#H%Lr?Q$u+)pt3IAH5y!?n7lXmXRhh`Emks5nbqDNBd}xP1-Ah-QCW&~&e8-kc?y59VfiH1 z&g{jO$QdY06t+j&>^P#u*Zgwbt2wH?z;+1!(=N&o6ux1Ek`UBGM>b*Eu@rQy25T{_ z$Ojl?Ov7sq8e7K&1QuqKsLSot_y|XeWhU%oZfHn>Quh~J5E&+4%DkJuNab-qPe(Nd zBwk$jJV`4nhfPHZGXAR3b)Dw^R^x!;|8ReU`}$F>!|SO!1LQ$VYiHqfnG898bAZdie_CvyWb30Jk{F*?50LI z@Fh<9WKFZ(pB2eY=MlTFIq$?&B?wcXm6sOPta54CC2tng8@vdh7W-|8Fg9i@XG&e~ z{>(_1T_vSTOf61_YeWE_nPYc2ECTZi(^t{Hj){qaz$PP2^0Ha(_s0W?LgDg+=Glf6 zXorEtv?LB0^F(Oc274KE%oCDgRX+;L;NuQ5)3)m0)qYG)$aOPd^GFp{K!>{Y_6jBl z?Zp9Yr2W;Fr$LT0=k^I^m}>lU6g+zZ0LC2_GI%X2rHu7#`t>z!Lyu%^plq$Euwvq_7ANy?ymP~yRf|1L#HPY9X2Przmp zSr%dDkE4cd$9K(brfKy8;jRXF1X;^y+J+5@E`b5&RVPE1s>tR#Gs(5ASWi-~e<}*U zxP-dVm=_*!khq+acTBWpR+{;%>%20?2SS;4ue-Wkp98gxFVokWkRzs?h?t(`eZep> zw4lHM<#C1SybD-^$kV^H^O@|`FR!0pMv$JwI=Y7RxQ}+`9-}qc-tN0|YY!!{B1{t? zN=eK6URLR)cR<^_b9Bt$ycW2*z78vlg3)7x*Xv(Y9n|1|bdh}o3krD@9`Z<}70|N- z{WUG#@3QFg30BoTREwjvW()L&{GR;7X<)U!rNH^;PC~^UA1=b{eo5!a#10RHb-f+L-Z>bgw3JTIlJWir5bY7vF zN6%1=bop(<;P(5*Rr$1$O}Dl`tzbsK!}0LYId}PcvtNb{GIR3t4p~?#;v|#aUwt=I zb6{navPdor8AS;%p-!uEhly|Bx@Oll3hZj2N?XO_EB_EQmivUGOZWKl&i3Jb)xa)o z&fN4;a0NeTZ5UD!8^naq6U$U+D}+~Y0>#gKudodGJ+D!kHz!*+Vgdbc)zq)6cN8QU zLLQUL;2-JLI}`OehL5T5Q2+*c^C zJsj|Xo7aCD>*no;Xz#y`{y!f6*UaBVlx5~8Ccd|KaNgJt6z-&bdPzmy-7SeSl}>WM zIaj>7xfzgbm?> zpFgSk{38G3PoAfcT~H94nyM@<-TQa`&0=O_L+>mmY8{-5SAoiLwy zeA9jW-YT?)<(w;P0)KlG9J!VM;x89;^Y%yge-1az&$J`@HR{T3E_GM{d{dzR_n9Wk ziUC~IkARHp@j={F)l%n+?l;VnFX$7%xv2^6+_3zg9=jfq;t~?gRtXvzh$DsjFq7-@ zowr%tXF^;&v}5v3%$n_w$C$rjUkhMMC|ea-$`^E7z6@?}a^CS#o)4S1YiidG-Ai#k z+B%jeu{;Xkw@4u;An8Lq@%Wq=48}DxA+M?!Vk+4I3{h z?NhG;V83GE_Z&~kYOf1TPnWr=DJgPKi(kg5eg4(0ik_-&jxO8YGP_UW;JR) zx?sr^Z+^AR8JbWVH|Sq)j9_z`%zJ3?DnI5HxKnrhlHu~!f>)oPY47aJka#Ne5p zEDu+pR7bGqc)NwK(rI$M=uaU*0LqLq-B6l-3~mAHF81m()gKd8=g22Zx2~D-}(ll^^rc+Y}`kASmP5fuJUra6VT;e2GzpF~f zyqI0u91~M5@fJDlAwaekKg1pagr@@uG2m)uD-DjF?BmG!JWswe_Kfc=Ti51WAE^RUTc5=~Bv>@<3v>y}?txnSeO}e6mi8rIXz- zUn~|`eI_IzE_b_}s6az4 zd!1k1dEj08+e+TXrYqoUa?TlMxl`^g>y?vGM0TJ0Wh&Q9y+lFMRe_jiSSoQ~G>tUhPvSWVQbr@A>=#VA%glJFLc zm(V#xq(yWMD^9lx!qC?z9g*GVt-|3}GlkJ$5V#&a?|s>E&MczXW6PltFPZO76D^*v z2s@vJQqmo_Ry=M^sI=ZE;@c^RPfnT*3Och|)C1>nwJ%zyN&(%iO7|mRiZ+pd(nz07 z2D{Sxlp)>QbLUXP(e~kd*Kj^9h{Aa3(4g#6 zY;W?J`mUo5ET!IQ}9AhAT*%i(GrySTP~ z5GeFU-re6G!D$_`YmZi73ZN{g%7+A0M@+JJXc2Pj-Q35tZvzY}JWje|Hl^vmqt@Mq zuy$}{EiUWuW8m3gWYkezJ|F*-de>iI5P4io@Kv8Ar}Bql*4m~7kn-1&;k#x@)zdTR zraYB37X9p~M2YRI(fHo%TAVzx$ABrXx2^Z1yaYlE0xH+<1z9Pdw|UwQpfDk zJu~?2CAljhURrTKGYgZngcx6mQ80OqU$&zdK=R~DF)N2?EUXRzRDYyqHlAG?GH7V$ zu(bZy3t*vd&Sf`D)h-(KJHYy`U`Fn5^%rd8P6sm^c2_a|sgjU2X%ej}!|4d-SCAw6 z{k~K)e9z8)TeJCd%|!$tB?8tJx0q8Spy$5#A(@4A*s5xZ#OtiCQ$9MW+ikzO|A1Dk z@i>`c`I+8Y#X ze47rgsXZO1H23x57;C!%tz6#s_Qg1Lm7JU`(z$Aqhd_4yr_Icdb!R2VRP}EOnTgUH zm2;|1afK#Uxe_|$kQ?~kY1vr1lp&jk0ape1Q7X>EQ>)~3_!GmVY2*C>8xMaKMM8yLI{AV;QuH<2 z8pwFm?_Ee+lH+UOwp&~%Dp`19=^!Sy&tKe_g#&tf1=4o4_8vn(HZ386D6vnrgSMi= zJ}8J!>$Ub14o>%@HUh@*Xyt|RVvSER$LcJ|pMfudO^)0nZN5%ZCVh7xb%Q3h=Msac zR2RAPc&3B*mmF6^6Xn&xu7zHg|qQ?-vSh=�RJj+U**tVTxn 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�&b_WGK@>N}zA(S;KkEq(k&^)?Y+g2Y)=Pg6E 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 iBP;*_00000bo@WZXJJJlkjI<=0000 +""" +Tablet Mapper - PyQt6 application for mapping graphics tablet layouts +Supports Wacom and other xsetwacom-compatible tablets +Integrates with xrandr for screen detection and xbindkeys for keybindings +""" + +import sys +import subprocess +import re +import json +import os +from dataclasses import dataclass, field, asdict +from typing import Optional +from PyQt6.QtWidgets import ( + QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, + QPushButton, QLabel, QLineEdit, QComboBox, QGroupBox, QScrollArea, + QSplitter, QTextEdit, QMessageBox, QDialog, QFormLayout, QSpinBox, + QDialogButtonBox, QFrame, QSizePolicy, QTabWidget, QListWidget, + QListWidgetItem, QCheckBox, QToolButton, QMenu +) +from PyQt6.QtCore import Qt, QRect, QPoint, QSize, pyqtSignal, QTimer +from PyQt6.QtGui import QPainter, QColor, QFont, QPen, QBrush, QFontMetrics, QAction, QKeySequence + + +# ───────────────────────────────────────────── +# Data classes +# ───────────────────────────────────────────── + +@dataclass +class Monitor: + name: str # e.g. "HDMI-1" + x: int + y: int + width: int + height: int + primary: bool = False + + @property + def rect(self) -> QRect: + return QRect(self.x, self.y, self.width, self.height) + + def __str__(self): + return f"{self.name} {self.width}x{self.height}+{self.x}+{self.y}" + + +TABLET_RATIO_W = 16 +TABLET_RATIO_H = 10 + + +@dataclass +class TabletMapping: + name: str # user label, e.g. "All Screens" + monitor_names: list[str] # empty = full desktop, else specific monitors + keybinding: str = "" # xbindkeys keycode string + tablet_device: str = "" # xsetwacom device name + + # Aspect ratio correction + aspect_correct: bool = False # whether to apply correction + aspect_expand: str = "width" # "width" or "height" — which dimension to expand + aspect_anchor: str = "top-left" # "top-left" or "center" + + def _bounding_box(self, monitors: list[Monitor]) -> tuple[int, int, int, int]: + """Return (x0, y0, w, h) bounding box for this mapping's target monitors.""" + if not self.monitor_names: + if not monitors: + return (0, 0, 0, 0) + x0 = min(m.x for m in monitors) + y0 = min(m.y for m in monitors) + x1 = max(m.x + m.width for m in monitors) + y1 = max(m.y + m.height for m in monitors) + return (x0, y0, x1 - x0, y1 - y0) + targets = [m for m in monitors if m.name in self.monitor_names] + if not targets: + return (0, 0, 0, 0) + x0 = min(m.x for m in targets) + y0 = min(m.y for m in targets) + x1 = max(m.x + m.width for m in targets) + y1 = max(m.y + m.height for m in targets) + return (x0, y0, x1 - x0, y1 - y0) + + def _apply_aspect_correction(self, x: int, y: int, w: int, h: int) -> tuple[int, int, int, int]: + """Expand one dimension so the geometry matches the tablet's 16:10 ratio. + + The screen pixels stay the same; the geometry rectangle grows so that + the tablet's full physical area corresponds to the expanded region — + meaning the stylus can reach pixels outside the mapped screen. + """ + if self.aspect_expand == "width": + # Derive width from height: w = h * 16/10 + new_w = round(h * TABLET_RATIO_W / TABLET_RATIO_H) + delta = new_w - w + if self.aspect_anchor == "center": + x = x - delta // 2 + # anchor top-left: x stays, expansion goes rightward + w = new_w + else: + # Derive height from width: h = w * 10/16 + new_h = round(w * TABLET_RATIO_H / TABLET_RATIO_W) + delta = new_h - h + if self.aspect_anchor == "center": + y = y - delta // 2 + h = new_h + return (x, y, w, h) + + def area_string(self, monitors: list[Monitor]) -> str: + """Return xsetwacom MapToOutput geometry string (WIDTHxHEIGHT+X+Y).""" + x, y, w, h = self._bounding_box(monitors) + if w == 0 or h == 0: + return "" + if self.aspect_correct: + x, y, w, h = self._apply_aspect_correction(x, y, w, h) + return f"{w}x{h}+{x}+{y}" + + def corrected_rect(self, monitors: list[Monitor]) -> Optional[QRect]: + """Return the aspect-corrected geometry as a QRect for preview drawing.""" + x, y, w, h = self._bounding_box(monitors) + if w == 0 or h == 0: + return None + if self.aspect_correct: + x, y, w, h = self._apply_aspect_correction(x, y, w, h) + return QRect(x, y, w, h) + + +@dataclass +class AppConfig: + tablet_device: str = "" + mappings: list[TabletMapping] = field(default_factory=list) + + def to_dict(self): + return { + "tablet_device": self.tablet_device, + "mappings": [asdict(m) for m in self.mappings], + } + + @classmethod + def from_dict(cls, d: dict) -> "AppConfig": + cfg = cls(tablet_device=d.get("tablet_device", "")) + for md in d.get("mappings", []): + md.setdefault("aspect_correct", False) + md.setdefault("aspect_expand", "width") + md.setdefault("aspect_anchor", "top-left") + cfg.mappings.append(TabletMapping(**md)) + return cfg + + +CONFIG_PATH = os.path.expanduser("~/.config/tablet_mapper.json") + + +# ───────────────────────────────────────────── +# xrandr / xsetwacom helpers +# ───────────────────────────────────────────── + +def parse_xrandr() -> list[Monitor]: + """Parse connected monitors from xrandr output.""" + monitors: list[Monitor] = [] + try: + out = subprocess.check_output(["xrandr", "--query"], text=True) + except Exception: + return monitors + + # Match lines like: HDMI-1 connected primary 1920x1080+0+0 ... + pattern = re.compile( + r"^(\S+) connected (primary )?(\d+)x(\d+)\+(\d+)\+(\d+)", + re.MULTILINE, + ) + for m in pattern.finditer(out): + monitors.append(Monitor( + name=m.group(1), + primary=bool(m.group(2)), + width=int(m.group(3)), + height=int(m.group(4)), + x=int(m.group(5)), + y=int(m.group(6)), + )) + return monitors + + +def list_wacom_devices() -> list[str]: + """Return list of xsetwacom device names.""" + devices = [] + try: + out = subprocess.check_output(["xsetwacom", "--list", "devices"], text=True) + for line in out.splitlines(): + # Format: "Wacom Intuos BT S Pen id: 12 type: STYLUS" + match = re.match(r"^(.+?)\s+id:\s+\d+", line) + if match: + devices.append(match.group(1).strip()) + except Exception: + pass + return devices + + +def apply_mapping(device: str, area: str) -> tuple[bool, str]: + """Apply xsetwacom MapToOutput. Returns (success, message).""" + if not device or not area: + return False, "No device or area specified." + try: + subprocess.run( + ["xsetwacom", "set", device, "MapToOutput", area], + check=True, capture_output=True, text=True + ) + return True, f"Mapped '{device}' → {area}" + except subprocess.CalledProcessError as e: + return False, e.stderr.strip() + except FileNotFoundError: + return False, "xsetwacom not found." + + +SENTINEL_BEGIN = "# >>> tablet-mapper begin <<<" +SENTINEL_END = "# >>> tablet-mapper end <<<" + + +def generate_xbindkeys_config(config: AppConfig, monitors: list[Monitor]) -> str: + """Generate xbindkeys config snippet wrapped in sentinel comments.""" + lines = [ + SENTINEL_BEGIN, + "# Generated by tablet-mapper — do not edit this block manually.", + "", + ] + for mapping in config.mappings: + if not mapping.keybinding: + continue + area = mapping.area_string(monitors) + if not area: + continue + device = mapping.tablet_device or config.tablet_device + cmd = f'xsetwacom set "{device}" MapToOutput {area}' + lines.append(f"# {mapping.name}") + lines.append(f'"{cmd}"') + lines.append(f"\t{mapping.keybinding}") + lines.append("") + lines.append(SENTINEL_END) + return "\n".join(lines) + + +def upsert_xbindkeysrc(new_block: str, rc_path: str) -> str: + """Insert or replace the tablet-mapper sentinel block in rc_path. + + - If the file doesn't exist it is created with just the block. + - If a previous sentinel block exists it is replaced in-place. + - Otherwise the block is appended with a blank line separator. + + Returns a short status string. + """ + if os.path.exists(rc_path): + with open(rc_path, "r") as f: + original = f.read() + else: + original = "" + + if SENTINEL_BEGIN in original and SENTINEL_END in original: + # Replace the existing block (including both sentinel lines) + before = original[:original.index(SENTINEL_BEGIN)] + after = original[original.index(SENTINEL_END) + len(SENTINEL_END):] + # Normalise surrounding whitespace: keep one blank line either side + updated = before.rstrip("\n") + "\n\n" + new_block + "\n" + after.lstrip("\n") + action = "updated" + else: + # Append — add blank line separator if file has content + sep = "\n\n" if original.strip() else "" + updated = original + sep + new_block + "\n" + action = "written" + + with open(rc_path, "w") as f: + f.write(updated) + + return action + + +# ───────────────────────────────────────────── +# Desktop preview widget +# ───────────────────────────────────────────── + +class DesktopPreview(QWidget): + """Visual representation of monitor layout with tablet mapping overlay. + + Clicking on a monitor selects the first mapping that covers it. + Clicking the desktop background (bounding box but outside all monitors) + selects the first all-screens mapping. + """ + + mapping_changed = pyqtSignal() + mapping_clicked = pyqtSignal(int) # emits mapping index, or -1 for none + + def __init__(self, parent=None): + super().__init__(parent) + self.monitors: list[Monitor] = [] + self.mappings: list[TabletMapping] = [] # kept in sync by MainWindow + self.active_mapping: Optional[TabletMapping] = None + self.setMinimumSize(400, 220) + self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding) + self.setCursor(Qt.CursorShape.ArrowCursor) + self.setMouseTracking(True) + + self._colors = [ + QColor("#3a7bd5"), QColor("#e74c3c"), QColor("#2ecc71"), + QColor("#f39c12"), QColor("#9b59b6"), QColor("#1abc9c"), + ] + + # Cached layout geometry, recomputed in paintEvent + self._last_desktop: QRect = QRect() + self._last_target: QRect = QRect() + + def set_monitors(self, monitors: list[Monitor]): + self.monitors = monitors + self.update() + + def set_mappings(self, mappings: list[TabletMapping]): + self.mappings = mappings + + def set_active_mapping(self, mapping: Optional[TabletMapping]): + self.active_mapping = mapping + self.update() + + # ── Coordinate helpers ─────────────────── + + def _desktop_rect(self) -> QRect: + if not self.monitors: + return QRect(0, 0, 1920, 1080) + x0 = min(m.x for m in self.monitors) + y0 = min(m.y for m in self.monitors) + x1 = max(m.x + m.width for m in self.monitors) + y1 = max(m.y + m.height for m in self.monitors) + return QRect(x0, y0, x1 - x0, y1 - y0) + + def _compute_target(self) -> QRect: + """Return the screen-space rect the desktop is drawn into (aspect-corrected).""" + desktop = self._desktop_rect() + margin = 16 + target = QRect(margin, margin, self.width() - 2 * margin, self.height() - 2 * margin) + if desktop.width() > 0 and desktop.height() > 0: + asp = desktop.width() / desktop.height() + if target.width() / target.height() > asp: + new_w = int(target.height() * asp) + target = QRect(target.x() + (target.width() - new_w) // 2, + target.y(), new_w, target.height()) + else: + new_h = int(target.width() / asp) + target = QRect(target.x(), target.y() + (target.height() - new_h) // 2, + target.width(), new_h) + return target + + def _scale_rect(self, rect: QRect, desktop: QRect, target: QRect) -> QRect: + sx = target.width() / desktop.width() + sy = target.height() / desktop.height() + scale = min(sx, sy) + x = target.x() + (rect.x() - desktop.x()) * scale + y = target.y() + (rect.y() - desktop.y()) * scale + return QRect(int(x), int(y), int(rect.width() * scale), int(rect.height() * scale)) + + def _widget_to_desktop(self, px: int, py: int) -> QPoint: + """Convert widget pixel coordinates to desktop coordinate space.""" + desktop = self._last_desktop + target = self._last_target + if target.width() == 0 or target.height() == 0: + return QPoint(0, 0) + scale_x = desktop.width() / target.width() + scale_y = desktop.height() / target.height() + scale = max(scale_x, scale_y) # inverse of min(sx,sy) used in _scale_rect + dx = desktop.x() + (px - target.x()) * scale + dy = desktop.y() + (py - target.y()) * scale + return QPoint(int(dx), int(dy)) + + # ── Hit testing ────────────────────────── + + def _monitor_at(self, dp: QPoint) -> Optional[Monitor]: + """Return the monitor containing desktop point dp, or None.""" + for m in self.monitors: + if m.rect.contains(dp): + return m + return None + + def _in_desktop_bounding_box(self, dp: QPoint) -> bool: + return self._desktop_rect().contains(dp) + + def _best_mapping_for_click(self, dp: QPoint) -> int: + """Return index of the best matching mapping for a click at desktop point dp. + + Priority: + 1. Click inside a monitor → first mapping that covers exactly that monitor + (single-monitor mapping preferred over multi-monitor) + 2. Click in bounding box but outside all monitors → first all-screens mapping + 3. Nothing matched → -1 + """ + clicked_mon = self._monitor_at(dp) + + if clicked_mon: + # Prefer single-monitor mappings for this monitor + for i, m in enumerate(self.mappings): + if m.monitor_names == [clicked_mon.name]: + return i + # Fall back to any mapping that includes this monitor + for i, m in enumerate(self.mappings): + if clicked_mon.name in m.monitor_names: + return i + # Fall back to all-screens + for i, m in enumerate(self.mappings): + if not m.monitor_names: + return i + elif self._in_desktop_bounding_box(dp): + # Outside monitors but inside bounding box → all-screens + for i, m in enumerate(self.mappings): + if not m.monitor_names: + return i + + return -1 + + # ── Mouse events ───────────────────────── + + def mouseMoveEvent(self, event): + if not self.monitors or not self.mappings: + return + dp = self._widget_to_desktop(event.pos().x(), event.pos().y()) + if self._in_desktop_bounding_box(dp): + self.setCursor(Qt.CursorShape.PointingHandCursor) + else: + self.setCursor(Qt.CursorShape.ArrowCursor) + + def mousePressEvent(self, event): + if event.button() != Qt.MouseButton.LeftButton: + return + if not self.monitors or not self.mappings: + return + dp = self._widget_to_desktop(event.pos().x(), event.pos().y()) + idx = self._best_mapping_for_click(dp) + self.mapping_clicked.emit(idx) + + # ── Paint ──────────────────────────────── + + def paintEvent(self, event): + painter = QPainter(self) + painter.setRenderHint(QPainter.RenderHint.Antialiasing) + + desktop = self._desktop_rect() + self._last_desktop = desktop + target = self._compute_target() + self._last_target = target + + # Background + painter.fillRect(target, QColor("#1a1a2e")) + + # Draw monitors + for i, mon in enumerate(self.monitors): + r = self._scale_rect(mon.rect, desktop, target) + color = self._colors[i % len(self._colors)] + painter.fillRect(r, color.darker(150)) + painter.setPen(QPen(color, 2)) + painter.drawRect(r) + + # Label + painter.setPen(QColor("white")) + font = painter.font() + font.setPointSize(8) + painter.setFont(font) + label = f"{mon.name}\n{mon.width}×{mon.height}" + painter.drawText(r, Qt.AlignmentFlag.AlignCenter, label) + + # Draw mapping overlay + if self.active_mapping and self.monitors: + targets = [m for m in self.monitors if m.name in self.active_mapping.monitor_names] + if not targets and not self.active_mapping.monitor_names: + targets = self.monitors # all screens + + if targets: + x0 = min(m.x for m in targets) + y0 = min(m.y for m in targets) + x1 = max(m.x + m.width for m in targets) + y1 = max(m.y + m.height for m in targets) + mapping_rect = QRect(x0, y0, x1 - x0, y1 - y0) + sr = self._scale_rect(mapping_rect, desktop, target) + + # Raw mapping rect — yellow dashed + overlay = QColor("#f1c40f") + overlay.setAlpha(40) + painter.fillRect(sr, overlay) + painter.setPen(QPen(QColor("#f1c40f"), 2, Qt.PenStyle.DashLine)) + painter.drawRect(sr) + + # Aspect-corrected rect — cyan solid (only if correction enabled) + if self.active_mapping.aspect_correct: + corr = self.active_mapping.corrected_rect(self.monitors) + if corr: + cr = self._scale_rect(corr, desktop, target) + corr_overlay = QColor("#00d4ff") + corr_overlay.setAlpha(35) + painter.fillRect(cr, corr_overlay) + painter.setPen(QPen(QColor("#00d4ff"), 2)) + painter.drawRect(cr) + # Label on corrected rect + painter.setPen(QColor("#00d4ff")) + font = painter.font() + font.setPointSize(7) + font.setBold(True) + painter.setFont(font) + from math import gcd + g = gcd(corr.width(), corr.height()) if corr.height() > 0 else 1 + label = f"Tablet: {corr.width() // g}:{corr.height() // g} ({corr.width()}×{corr.height()})" + painter.drawText(cr.adjusted(4, 4, -4, -4), + Qt.AlignmentFlag.AlignBottom | Qt.AlignmentFlag.AlignRight, + label) + else: + # Just show raw aspect ratio + painter.setPen(QColor("#f1c40f")) + font = painter.font() + font.setPointSize(7) + font.setBold(True) + painter.setFont(font) + from math import gcd + rw, rh = x1 - x0, y1 - y0 + g = gcd(rw, rh) if rh > 0 else 1 + label = f"{rw // g}:{rh // g} ({rw}×{rh})" + painter.drawText(sr.adjusted(4, 4, -4, -4), + Qt.AlignmentFlag.AlignBottom | Qt.AlignmentFlag.AlignRight, + label) + + painter.end() + + +# ───────────────────────────────────────────── +# Key capture dialog (keycode-based) +# ───────────────────────────────────────────── + +# Keys that are modifiers only — not the primary key +_MODIFIER_KEYS = { + Qt.Key.Key_Control, Qt.Key.Key_Shift, Qt.Key.Key_Alt, + Qt.Key.Key_Meta, Qt.Key.Key_Super_L, Qt.Key.Key_Super_R, + Qt.Key.Key_AltGr, Qt.Key.Key_CapsLock, Qt.Key.Key_NumLock, + Qt.Key.Key_ScrollLock, Qt.Key.Key_Hyper_L, Qt.Key.Key_Hyper_R, +} + +CAPTURE_TIMEOUT_MS = 1500 + + +class KeyCaptureDialog(QDialog): + """Captures a key combination and stores it as an xbindkeys keycode string. + + Output format: m:0x58 + c:10 + m: hex modifier mask (as reported by the native X11 event) + c: X11 keycode (nativeScanCode from the Qt key event) + + This is the only format used — no human-readable conversion needed. + The display label shows a friendly representation while capturing, + but the stored string is always the keycode form. + """ + + def __init__(self, parent=None): + super().__init__(parent) + self.setWindowTitle("Capture Keybinding") + self.setFixedSize(400, 200) + self.setWindowFlags(self.windowFlags() | Qt.WindowType.WindowStaysOnTopHint) + + self._native_mods: int = 0 # raw X11 modifier mask + self._native_keycode: int = 0 # raw X11 keycode (scan code) + self._display_str: str = "" # human-friendly label (display only) + self._captured: str = "" # the actual xbindkeys keycode string + + self._timer = QTimer(self) + self._timer.setSingleShot(True) + self._timer.setInterval(CAPTURE_TIMEOUT_MS) + self._timer.timeout.connect(self._on_timeout) + + layout = QVBoxLayout(self) + layout.setSpacing(12) + + instr = QLabel("Hold down your key combination, then release…") + instr.setAlignment(Qt.AlignmentFlag.AlignCenter) + instr.setWordWrap(True) + layout.addWidget(instr) + + self._combo_label = QLabel("—") + self._combo_label.setAlignment(Qt.AlignmentFlag.AlignCenter) + f = self._combo_label.font() + f.setPointSize(15) + f.setBold(True) + self._combo_label.setFont(f) + self._combo_label.setStyleSheet("color: #cba6f7;") + layout.addWidget(self._combo_label) + + self._code_label = QLabel("") + self._code_label.setAlignment(Qt.AlignmentFlag.AlignCenter) + self._code_label.setStyleSheet("color: #6c7086; font-size: 11px; font-family: monospace;") + layout.addWidget(self._code_label) + + self._countdown_label = QLabel("") + self._countdown_label.setAlignment(Qt.AlignmentFlag.AlignCenter) + self._countdown_label.setStyleSheet("color: #6c7086; font-size: 11px;") + layout.addWidget(self._countdown_label) + + btn_row = QHBoxLayout() + cancel_btn = QPushButton("Cancel") + cancel_btn.clicked.connect(self.reject) + btn_row.addWidget(cancel_btn) + self._accept_btn = QPushButton("Use this binding") + self._accept_btn.setEnabled(False) + self._accept_btn.setStyleSheet("background:#2ecc71;color:#000;font-weight:bold;") + self._accept_btn.clicked.connect(self.accept) + btn_row.addWidget(self._accept_btn) + layout.addLayout(btn_row) + + self._tick = QTimer(self) + self._tick.setInterval(50) + self._tick.timeout.connect(self._update_countdown) + self._tick.start() + + def result_string(self) -> str: + return self._captured + + # ── Qt key event → X11 keycode ─────────── + + def keyPressEvent(self, event): + if event.isAutoRepeat(): + return + + key = Qt.Key(event.key()) + if key in _MODIFIER_KEYS: + # Update modifier display but don't set a keycode yet + self._native_mods = event.nativeModifiers() + self._update_display() + return + + # Capture both the X11 modifier mask and keycode + self._native_mods = event.nativeModifiers() + self._native_keycode = event.nativeScanCode() + self._display_str = self._build_display(event) + self._captured = self._build_keycode_string() + self._timer.stop() + self._update_display() + + def keyReleaseEvent(self, event): + if event.isAutoRepeat(): + return + if self._captured: + self._timer.start() + + def _build_keycode_string(self) -> str: + """Build the xbindkeys keycode string: m:0xMM + c:CC""" + if not self._native_keycode: + return "" + return f"m:0x{self._native_mods:x} + c:{self._native_keycode}" + + def _build_display(self, event) -> str: + """Human-readable label shown during capture (not stored).""" + _MOD_DISPLAY = [ + (Qt.KeyboardModifier.MetaModifier, "Super"), + (Qt.KeyboardModifier.ControlModifier, "Ctrl"), + (Qt.KeyboardModifier.AltModifier, "Alt"), + (Qt.KeyboardModifier.ShiftModifier, "Shift"), + ] + parts = [label for mod, label in _MOD_DISPLAY if event.modifiers() & mod] + # Append key name + key = Qt.Key(event.key()) + if Qt.Key.Key_0 <= key <= Qt.Key.Key_9: + parts.append(str(key - Qt.Key.Key_0)) + elif Qt.Key.Key_A <= key <= Qt.Key.Key_Z: + parts.append(chr(key - Qt.Key.Key_A + ord('A'))) + elif Qt.Key.Key_F1 <= key <= Qt.Key.Key_F35: + parts.append(f"F{key - Qt.Key.Key_F1 + 1}") + else: + text = event.text() + parts.append(text.upper() if text.strip() else f"key:{self._native_keycode}") + return " + ".join(parts) + + def _update_display(self): + self._combo_label.setText(self._display_str or "—") + if self._captured: + self._code_label.setText(self._captured) + self._accept_btn.setEnabled(bool(self._captured)) + + def _update_countdown(self): + if self._timer.isActive(): + remaining = self._timer.remainingTime() + self._countdown_label.setText(f"Accepting in {remaining / 1000:.1f}s…") + else: + self._countdown_label.setText( + "Press keys, then release to confirm." if not self._captured else "" + ) + + def _on_timeout(self): + if self._captured: + self.accept() + + +# ───────────────────────────────────────────── +# Mapping editor dialog +# ───────────────────────────────────────────── + +class MappingDialog(QDialog): + def __init__(self, monitors: list[Monitor], mapping: Optional[TabletMapping] = None, + devices: list[str] = None, parent=None): + super().__init__(parent) + self.setWindowTitle("Edit Mapping" if mapping else "Add Mapping") + self.monitors = monitors + self.devices = devices or [] + self.resize(420, 380) + + layout = QVBoxLayout(self) + form = QFormLayout() + + self.name_edit = QLineEdit(mapping.name if mapping else "") + form.addRow("Name:", self.name_edit) + + self.key_edit = QLineEdit(mapping.keybinding if mapping else "") + self.key_edit.setPlaceholderText("e.g. m:0x58 + c:10 (use Capture button)") + key_widget = QWidget() + key_row = QHBoxLayout(key_widget) + key_row.setContentsMargins(0, 0, 0, 0) + key_row.addWidget(self.key_edit) + capture_btn = QPushButton("⌨ Capture keybinding") + capture_btn.setFixedWidth(165) + capture_btn.clicked.connect(self._capture_keybinding) + key_row.addWidget(capture_btn) + form.addRow("Keybinding:", key_widget) + + self.device_combo = QComboBox() + self.device_combo.addItem("(use global device)") + self.device_combo.addItems(self.devices) + if mapping and mapping.tablet_device: + idx = self.device_combo.findText(mapping.tablet_device) + if idx >= 0: + self.device_combo.setCurrentIndex(idx) + form.addRow("Device override:", self.device_combo) + + layout.addLayout(form) + + mon_group = QGroupBox("Monitor coverage") + mon_layout = QVBoxLayout(mon_group) + + self.all_check = QCheckBox("All screens (full desktop)") + mon_layout.addWidget(self.all_check) + + self.mon_checks: dict[str, QCheckBox] = {} + for mon in monitors: + cb = QCheckBox(f"{mon.name} ({mon.width}×{mon.height}+{mon.x}+{mon.y})") + if mapping and mon.name in mapping.monitor_names: + cb.setChecked(True) + self.mon_checks[mon.name] = cb + mon_layout.addWidget(cb) + + if mapping and not mapping.monitor_names: + self.all_check.setChecked(True) + + self.all_check.toggled.connect(self._all_toggled) + layout.addWidget(mon_group) + + # ── Aspect ratio correction ────────────── + asp_group = QGroupBox("Aspect ratio correction (tablet is 16:10)") + asp_layout = QVBoxLayout(asp_group) + + self.aspect_check = QCheckBox("Correct geometry to match tablet 16:10 aspect ratio") + self.aspect_check.setChecked(mapping.aspect_correct if mapping else False) + asp_layout.addWidget(self.aspect_check) + + asp_controls = QWidget() + asp_ctrl_layout = QHBoxLayout(asp_controls) + asp_ctrl_layout.setContentsMargins(0, 0, 0, 0) + + # Expand dimension toggle button + asp_ctrl_layout.addWidget(QLabel("Expand:")) + self._expand_val = (mapping.aspect_expand if mapping else "width") + self.expand_btn = QPushButton() + self.expand_btn.setFixedWidth(90) + self.expand_btn.setCheckable(False) + self._update_expand_btn() + self.expand_btn.clicked.connect(self._toggle_expand) + asp_ctrl_layout.addWidget(self.expand_btn) + + asp_ctrl_layout.addSpacing(16) + + # Anchor combo + asp_ctrl_layout.addWidget(QLabel("Anchor:")) + self.anchor_combo = QComboBox() + self.anchor_combo.addItem("Top-left (expand right/down)", "top-left") + self.anchor_combo.addItem("Center (expand both sides)", "center") + anchor_val = mapping.aspect_anchor if mapping else "top-left" + idx = self.anchor_combo.findData(anchor_val) + if idx >= 0: + self.anchor_combo.setCurrentIndex(idx) + asp_ctrl_layout.addWidget(self.anchor_combo) + asp_ctrl_layout.addStretch() + + asp_layout.addWidget(asp_controls) + + # Preview label showing computed geometry + self._asp_preview = QLabel("") + self._asp_preview.setStyleSheet("color: #89dceb; font-family: monospace; font-size: 11px;") + asp_layout.addWidget(self._asp_preview) + + self.aspect_check.toggled.connect(self._update_asp_preview) + self.expand_btn.clicked.connect(self._update_asp_preview) + self.anchor_combo.currentIndexChanged.connect(self._update_asp_preview) + for cb in self.mon_checks.values(): + cb.toggled.connect(self._update_asp_preview) + self.all_check.toggled.connect(self._update_asp_preview) + + layout.addWidget(asp_group) + self._update_asp_preview() + self._update_asp_controls(self.aspect_check.isChecked()) + self.aspect_check.toggled.connect(self._update_asp_controls) + + btns = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok | + QDialogButtonBox.StandardButton.Cancel) + btns.accepted.connect(self.accept) + btns.rejected.connect(self.reject) + layout.addWidget(btns) + + self._all_toggled(self.all_check.isChecked()) + + def _all_toggled(self, checked: bool): + for cb in self.mon_checks.values(): + cb.setEnabled(not checked) + + def _toggle_expand(self): + self._expand_val = "height" if self._expand_val == "width" else "width" + self._update_expand_btn() + self._update_asp_preview() + + def _update_expand_btn(self): + if self._expand_val == "width": + self.expand_btn.setText("↔ Width") + self.expand_btn.setStyleSheet("background:#313244;") + else: + self.expand_btn.setText("↕ Height") + self.expand_btn.setStyleSheet("background:#313244;") + + def _update_asp_controls(self, enabled: bool): + self.expand_btn.setEnabled(enabled) + self.anchor_combo.setEnabled(enabled) + self._update_asp_preview() + + def _current_bounding_box(self) -> tuple[int, int, int, int]: + """Return (x, y, w, h) bounding box based on current dialog selections.""" + if self.all_check.isChecked(): + if not self.monitors: + return (0, 0, 0, 0) + x0 = min(m.x for m in self.monitors) + y0 = min(m.y for m in self.monitors) + x1 = max(m.x + m.width for m in self.monitors) + y1 = max(m.y + m.height for m in self.monitors) + return (x0, y0, x1 - x0, y1 - y0) + selected = [m for m in self.monitors if self.mon_checks.get(m.name, QCheckBox()).isChecked()] + if not selected: + return (0, 0, 0, 0) + x0 = min(m.x for m in selected) + y0 = min(m.y for m in selected) + x1 = max(m.x + m.width for m in selected) + y1 = max(m.y + m.height for m in selected) + return (x0, y0, x1 - x0, y1 - y0) + + def _update_asp_preview(self): + if not self.aspect_check.isChecked(): + self._asp_preview.setText("") + return + x, y, w, h = self._current_bounding_box() + if w == 0 or h == 0: + self._asp_preview.setText("No monitors selected.") + return + anchor = self.anchor_combo.currentData() + if self._expand_val == "width": + new_w = round(h * TABLET_RATIO_W / TABLET_RATIO_H) + delta = new_w - w + adj_x = x - delta // 2 if anchor == "center" else x + self._asp_preview.setText( + f"Raw: {w}×{h}+{x}+{y} → Corrected: {new_w}×{h}+{adj_x}+{y}" + f" (width +{delta}px)" + ) + else: + new_h = round(w * TABLET_RATIO_H / TABLET_RATIO_W) + delta = new_h - h + adj_y = y - delta // 2 if anchor == "center" else y + self._asp_preview.setText( + f"Raw: {w}×{h}+{x}+{y} → Corrected: {w}×{new_h}+{x}+{adj_y}" + f" (height +{delta}px)" + ) + + def _capture_keybinding(self): + dlg = KeyCaptureDialog(self) + if dlg.exec() and dlg.result_string(): + self.key_edit.setText(dlg.result_string()) + + def get_mapping(self) -> TabletMapping: + if self.all_check.isChecked(): + selected = [] + else: + selected = [n for n, cb in self.mon_checks.items() if cb.isChecked()] + + device = "" + if self.device_combo.currentIndex() > 0: + device = self.device_combo.currentText() + + return TabletMapping( + name=self.name_edit.text() or "Unnamed", + monitor_names=selected, + keybinding=self.key_edit.text().strip(), + tablet_device=device, + aspect_correct=self.aspect_check.isChecked(), + aspect_expand=self._expand_val, + aspect_anchor=self.anchor_combo.currentData(), + ) + + +# ───────────────────────────────────────────── +# Main window +# ───────────────────────────────────────────── + +class MainWindow(QMainWindow): + def __init__(self): + super().__init__() + self.setWindowTitle("Tablet Mapper") + self.resize(1000, 680) + + self.config = AppConfig() + self.monitors: list[Monitor] = [] + self.wacom_devices: list[str] = [] + + self._load_config() + self._build_ui() + self._refresh_monitors() + self._refresh_devices() + self._refresh_mapping_list() + + # ── UI construction ────────────────────── + + def _build_ui(self): + central = QWidget() + self.setCentralWidget(central) + root = QVBoxLayout(central) + root.setContentsMargins(8, 8, 8, 8) + + # Toolbar row + toolbar = QHBoxLayout() + + refresh_btn = QPushButton("⟳ Refresh Screens") + refresh_btn.clicked.connect(self._refresh_monitors) + toolbar.addWidget(refresh_btn) + + refresh_dev_btn = QPushButton("⟳ Refresh Devices") + refresh_dev_btn.clicked.connect(self._refresh_devices) + toolbar.addWidget(refresh_dev_btn) + + toolbar.addWidget(QLabel("Global device:")) + self.device_combo = QComboBox() + self.device_combo.setMinimumWidth(220) + self.device_combo.currentTextChanged.connect(self._on_global_device_changed) + toolbar.addWidget(self.device_combo) + + toolbar.addStretch() + + save_btn = QPushButton("Save Config") + save_btn.clicked.connect(self._save_config) + toolbar.addWidget(save_btn) + + root.addLayout(toolbar) + + # Splitter: left = preview+monitors, right = mappings+output + splitter = QSplitter(Qt.Orientation.Horizontal) + root.addWidget(splitter) + + # Left panel + left = QWidget() + left_layout = QVBoxLayout(left) + left_layout.setContentsMargins(0, 0, 0, 0) + + preview_group = QGroupBox("Desktop Layout Preview") + pg = QVBoxLayout(preview_group) + self.preview = DesktopPreview() + self.preview.mapping_clicked.connect(self._on_preview_clicked) + pg.addWidget(self.preview) + left_layout.addWidget(preview_group, 3) + + mon_group = QGroupBox("Detected Monitors (from xrandr)") + mg = QVBoxLayout(mon_group) + self.monitor_list = QListWidget() + mg.addWidget(self.monitor_list) + manual_row = QHBoxLayout() + manual_row.addWidget(QLabel("Manual input:")) + self.manual_xrandr = QLineEdit() + self.manual_xrandr.setPlaceholderText("Paste xrandr output or geometry, e.g. 1920x1080+0+0") + manual_row.addWidget(self.manual_xrandr) + parse_btn = QPushButton("Parse") + parse_btn.clicked.connect(self._parse_manual_xrandr) + manual_row.addWidget(parse_btn) + mg.addLayout(manual_row) + left_layout.addWidget(mon_group, 2) + + splitter.addWidget(left) + + # Right panel + right = QWidget() + right_layout = QVBoxLayout(right) + right_layout.setContentsMargins(0, 0, 0, 0) + + tabs = QTabWidget() + right_layout.addWidget(tabs) + + # ── Tab 1: Mappings ── + map_widget = QWidget() + map_layout = QVBoxLayout(map_widget) + + map_btn_row = QHBoxLayout() + add_btn = QPushButton("+ Add Mapping") + add_btn.clicked.connect(self._add_mapping) + map_btn_row.addWidget(add_btn) + edit_btn = QPushButton("✎ Edit") + edit_btn.clicked.connect(self._edit_mapping) + map_btn_row.addWidget(edit_btn) + del_btn = QPushButton("✕ Delete") + del_btn.clicked.connect(self._delete_mapping) + map_btn_row.addWidget(del_btn) + map_btn_row.addStretch() + apply_btn = QPushButton("▶ Apply Selected") + apply_btn.setStyleSheet("background:#2ecc71;color:#000;font-weight:bold;") + apply_btn.clicked.connect(self._apply_selected) + map_btn_row.addWidget(apply_btn) + map_layout.addLayout(map_btn_row) + + self.mapping_list = QListWidget() + self.mapping_list.currentRowChanged.connect(self._on_mapping_selected) + map_layout.addWidget(self.mapping_list) + + quick_group = QGroupBox("Quick-add per-monitor mappings") + qg = QHBoxLayout(quick_group) + quick_add_btn = QPushButton("Auto-generate monitor mappings + All-screens") + quick_add_btn.clicked.connect(self._quick_add_monitor_mappings) + qg.addWidget(quick_add_btn) + map_layout.addWidget(quick_group) + + tabs.addTab(map_widget, "Mappings") + + # ── Tab 2: xbindkeys output ── + bind_widget = QWidget() + bind_layout = QVBoxLayout(bind_widget) + bind_btn_row = QHBoxLayout() + gen_btn = QPushButton("Generate xbindkeys config") + gen_btn.clicked.connect(self._generate_xbindkeys) + bind_btn_row.addWidget(gen_btn) + + copy_btn = QPushButton("Copy to clipboard") + copy_btn.clicked.connect(self._copy_xbindkeys) + bind_btn_row.addWidget(copy_btn) + + write_btn = QPushButton("+ Apply to xbindkeys") + write_btn.setStyleSheet("background:#2ecc71;color:#000;font-weight:bold;") + write_btn.setToolTip( + "Writes the generated block to ~/.xbindkeysrc (replacing any previous\n" + "tablet-mapper block) then restarts xbindkeys." + ) + write_btn.clicked.connect(self._write_and_reload_xbindkeys) + bind_btn_row.addWidget(write_btn) + bind_layout.addLayout(bind_btn_row) + self.xbindkeys_output = QTextEdit() + self.xbindkeys_output.setFont(QFont("Monospace", 10)) + self.xbindkeys_output.setReadOnly(False) + bind_layout.addWidget(self.xbindkeys_output) + tabs.addTab(bind_widget, "xbindkeys Config") + + # ── Tab 3: xsetwacom commands ── + cmd_widget = QWidget() + cmd_layout = QVBoxLayout(cmd_widget) + gen_cmd_btn = QPushButton("Generate shell script") + gen_cmd_btn.clicked.connect(self._generate_shell_script) + cmd_layout.addWidget(gen_cmd_btn) + self.cmd_output = QTextEdit() + self.cmd_output.setFont(QFont("Monospace", 10)) + self.cmd_output.setReadOnly(False) + cmd_layout.addWidget(self.cmd_output) + tabs.addTab(cmd_widget, "Shell Commands") + + # Status bar + self.status_label = QLabel("Ready.") + self.status_label.setStyleSheet("color: #aaa; font-size: 11px;") + right_layout.addWidget(self.status_label) + + splitter.addWidget(right) + splitter.setSizes([480, 520]) + + # ── Refresh ────────────────────────────── + + def _refresh_monitors(self): + self.monitors = parse_xrandr() + self.monitor_list.clear() + for m in self.monitors: + self.monitor_list.addItem( + f"{'★ ' if m.primary else ' '}{m.name} {m.width}×{m.height} +{m.x}+{m.y}" + ) + self.preview.set_monitors(self.monitors) + n = len(self.monitors) + self._set_status(f"Found {n} monitor{'s' if n != 1 else ''} via xrandr." if n + else "No monitors detected. Try manual input.") + + def _refresh_devices(self): + self.wacom_devices = list_wacom_devices() + self.device_combo.blockSignals(True) + self.device_combo.clear() + self.device_combo.addItem("(none)") + self.device_combo.addItems(self.wacom_devices) + if self.config.tablet_device: + idx = self.device_combo.findText(self.config.tablet_device) + if idx >= 0: + self.device_combo.setCurrentIndex(idx) + self.device_combo.blockSignals(False) + self._set_status(f"Devices: {self.wacom_devices or ['none found']}") + + def _on_global_device_changed(self, text: str): + self.config.tablet_device = text if text != "(none)" else "" + + # ── Manual xrandr input ────────────────── + + def _parse_manual_xrandr(self): + text = self.manual_xrandr.text().strip() + if not text: + return + # Try full xrandr output + monitors = parse_xrandr() if not text else [] + # Also try simple geometry list + geo_pattern = re.compile(r"(\w[\w-]*)\s+(\d+)x(\d+)\+(\d+)\+(\d+)") + found = [] + for m in geo_pattern.finditer(text): + found.append(Monitor( + name=m.group(1), width=int(m.group(2)), height=int(m.group(3)), + x=int(m.group(4)), y=int(m.group(5)) + )) + if found: + self.monitors = found + self.monitor_list.clear() + for mon in self.monitors: + self.monitor_list.addItem(f" {mon.name} {mon.width}×{mon.height} +{mon.x}+{mon.y}") + self.preview.set_monitors(self.monitors) + self._set_status(f"Parsed {len(found)} monitors from input.") + else: + self._set_status("Could not parse monitor geometry from input.") + + # ── Mappings ───────────────────────────── + + def _refresh_mapping_list(self): + self.mapping_list.clear() + for i, m in enumerate(self.config.mappings): + kb = f" [{m.keybinding}]" if m.keybinding else "" + scr = ", ".join(m.monitor_names) if m.monitor_names else "All screens" + self.mapping_list.addItem(f"{m.name}{kb} → {scr}") + self.preview.set_mappings(self.config.mappings) + + def _on_mapping_selected(self, row: int): + if 0 <= row < len(self.config.mappings): + self.preview.set_active_mapping(self.config.mappings[row]) + else: + self.preview.set_active_mapping(None) + + def _on_preview_clicked(self, idx: int): + """Handle a click on the desktop preview — select the mapping in the list.""" + if idx < 0: + self.mapping_list.clearSelection() + self.preview.set_active_mapping(None) + return + # Block the list's signal briefly to avoid double-firing set_active_mapping + self.mapping_list.blockSignals(True) + self.mapping_list.setCurrentRow(idx) + self.mapping_list.blockSignals(False) + self.preview.set_active_mapping(self.config.mappings[idx]) + + def _add_mapping(self): + dlg = MappingDialog(self.monitors, devices=self.wacom_devices, parent=self) + if dlg.exec(): + self.config.mappings.append(dlg.get_mapping()) + self._refresh_mapping_list() + + def _edit_mapping(self): + row = self.mapping_list.currentRow() + if row < 0 or row >= len(self.config.mappings): + return + dlg = MappingDialog(self.monitors, self.config.mappings[row], + self.wacom_devices, self) + if dlg.exec(): + self.config.mappings[row] = dlg.get_mapping() + self._refresh_mapping_list() + + def _delete_mapping(self): + row = self.mapping_list.currentRow() + if row < 0 or row >= len(self.config.mappings): + return + name = self.config.mappings[row].name + if QMessageBox.question(self, "Delete", f"Delete mapping '{name}'?", + QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No + ) == QMessageBox.StandardButton.Yes: + self.config.mappings.pop(row) + self._refresh_mapping_list() + self.preview.set_active_mapping(None) + + def _apply_selected(self): + row = self.mapping_list.currentRow() + if row < 0 or row >= len(self.config.mappings): + self._set_status("No mapping selected.") + return + m = self.config.mappings[row] + device = m.tablet_device or self.config.tablet_device + area = m.area_string(self.monitors) + if not device: + self._set_status("No tablet device set.") + return + ok, msg = apply_mapping(device, area) + self._set_status(("✓ " if ok else "✗ ") + msg) + + def _quick_add_monitor_mappings(self): + if not self.monitors: + self._set_status("No monitors detected.") + return + existing_names = {m.name for m in self.config.mappings} + + # All-screens mapping + all_name = "All Screens" + if all_name not in existing_names: + self.config.mappings.append(TabletMapping( + name=all_name, monitor_names=[], keybinding="" + )) + + # Per-monitor mappings + for mon in self.monitors: + name = f"Monitor: {mon.name}" + if name not in existing_names: + self.config.mappings.append(TabletMapping( + name=name, monitor_names=[mon.name], keybinding="" + )) + + self._refresh_mapping_list() + self._set_status( + f"Generated mappings for {len(self.monitors)} monitors + all-screens. " + "Use the Capture button to set keybindings." + ) + + # ── Output generation ──────────────────── + + def _generate_xbindkeys(self): + text = generate_xbindkeys_config(self.config, self.monitors) + self.xbindkeys_output.setPlainText(text) + + def _copy_xbindkeys(self): + self._generate_xbindkeys() + QApplication.clipboard().setText(self.xbindkeys_output.toPlainText()) + self._set_status("Copied xbindkeys config to clipboard.") + + def _write_and_reload_xbindkeys(self): + self._generate_xbindkeys() + block = self.xbindkeys_output.toPlainText() + rc_path = os.path.expanduser("~/.xbindkeysrc") + try: + action = upsert_xbindkeysrc(block, rc_path) + self._set_status(f"✓ tablet-mapper block {action} in {rc_path} — reloading xbindkeys…") + except Exception as e: + self._set_status(f"✗ Write failed: {e}") + return + try: + subprocess.run(["pkill", "xbindkeys"], capture_output=True) + subprocess.Popen(["xbindkeys"]) + self._set_status( + f"✓ tablet-mapper block {action} in {rc_path} and xbindkeys reloaded." + ) + except FileNotFoundError: + self._set_status("✓ File written — but xbindkeys not found, is it installed?") + except Exception as e: + self._set_status(f"✓ File written — xbindkeys reload failed: {e}") + + def _generate_shell_script(self): + lines = ["#!/bin/bash", "# Tablet mapping commands", ""] + for m in self.config.mappings: + device = m.tablet_device or self.config.tablet_device + area = m.area_string(self.monitors) + if device and area: + lines.append(f"# {m.name}") + lines.append(f'xsetwacom --set "{device}" MapToOutput {area}') + lines.append("") + self.cmd_output.setPlainText("\n".join(lines)) + + # ── Config persistence ─────────────────── + + def _save_config(self): + self.config.tablet_device = ( + self.device_combo.currentText() + if self.device_combo.currentIndex() > 0 else "" + ) + try: + os.makedirs(os.path.dirname(CONFIG_PATH), exist_ok=True) + with open(CONFIG_PATH, "w") as f: + json.dump(self.config.to_dict(), f, indent=2) + self._set_status(f"Config saved to {CONFIG_PATH}") + except Exception as e: + self._set_status(f"Save error: {e}") + + def _load_config(self): + if os.path.exists(CONFIG_PATH): + try: + with open(CONFIG_PATH) as f: + self.config = AppConfig.from_dict(json.load(f)) + except Exception: + pass + + def _set_status(self, msg: str): + self.status_label.setText(msg) + + +# ───────────────────────────────────────────── +# Dark stylesheet +# ───────────────────────────────────────────── + +DARK_STYLE = """ +QWidget { background-color: #1e1e2e; color: #cdd6f4; font-family: 'Segoe UI', sans-serif; font-size: 13px; } +QMainWindow { background-color: #1e1e2e; } +QGroupBox { border: 1px solid #45475a; border-radius: 6px; margin-top: 6px; padding-top: 8px; } +QGroupBox::title { subcontrol-origin: margin; left: 10px; color: #89b4fa; } +QPushButton { background-color: #313244; border: 1px solid #45475a; border-radius: 4px; padding: 5px 12px; } +QPushButton:hover { background-color: #45475a; } +QPushButton:pressed { background-color: #585b70; } +QLineEdit, QComboBox, QSpinBox { background-color: #181825; border: 1px solid #45475a; border-radius: 4px; padding: 4px 8px; } +QComboBox::drop-down { border: none; } +QComboBox::down-arrow { image: none; border: none; } +QListWidget { background-color: #181825; border: 1px solid #45475a; border-radius: 4px; } +QListWidget::item { padding: 4px 8px; } +QListWidget::item:selected { background-color: #45475a; color: #cba6f7; } +QListWidget::item:hover { background-color: #313244; } +QTabWidget::pane { border: 1px solid #45475a; border-radius: 4px; } +QTabBar::tab { background-color: #313244; border: 1px solid #45475a; padding: 6px 14px; margin-right: 2px; border-radius: 4px 4px 0 0; } +QTabBar::tab:selected { background-color: #45475a; color: #cba6f7; } +QTextEdit { background-color: #181825; border: 1px solid #45475a; border-radius: 4px; } +QCheckBox::indicator { width: 14px; height: 14px; border: 1px solid #45475a; border-radius: 3px; background: #181825; } +QCheckBox::indicator:checked { background: #89b4fa; } +QSplitter::handle { background: #45475a; } +QScrollBar:vertical { background: #181825; width: 8px; } +QScrollBar::handle:vertical { background: #45475a; border-radius: 4px; min-height: 20px; } +""" + + +# ───────────────────────────────────────────── +# Entry point +# ───────────────────────────────────────────── + +def main(): + app = QApplication(sys.argv) + app.setStyleSheet(DARK_STYLE) + app.setApplicationName("Tablet Mapper") + win = MainWindow() + win.show() + sys.exit(app.exec()) + + +if __name__ == "__main__": + main()