commit c1f98dc3a71a01d20422505d15a80a0902989d06 Author: wingsummer <1326224942@qq.com> Date: Mon Sep 26 14:39:51 2022 +0800 first commit diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..5ee5d2c --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +**/*.pro.user +push.sh diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..3140d42 --- /dev/null +++ b/LICENSE @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. httpfsf.org + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are 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. + + 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. + + Developers that use our General Public Licenses protect your rights +with two steps (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +andor modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + 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 Affero 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. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + 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 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 work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised andor new versions of +the GNU Affero 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 Affero 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 Affero 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 Affero 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 ANDOR 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 ANDOR 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. + + one line to give the program's name and a brief idea of what it does. + Copyright (C) year name of author + + This program is free software you can redistribute it andor modify + it under the terms of the GNU Affero 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 Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see httpwww.gnu.orglicenses. + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a Source link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + 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 AGPL, see +httpwww.gnu.orglicenses. diff --git a/QHotkey/LICENSE b/QHotkey/LICENSE new file mode 100644 index 0000000..55e0b00 --- /dev/null +++ b/QHotkey/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2016, Felix Barz +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of QHotkey nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/QHotkey/qhotkey.cpp b/QHotkey/qhotkey.cpp new file mode 100644 index 0000000..d514b6b --- /dev/null +++ b/QHotkey/qhotkey.cpp @@ -0,0 +1,326 @@ +#include "qhotkey.h" +#include "qhotkey_p.h" +#include +#include +#include +#include +#include + +Q_LOGGING_CATEGORY(logQHotkey, "QHotkey") + +void QHotkey::addGlobalMapping(const QKeySequence &shortcut, + QHotkey::NativeShortcut nativeShortcut) { + QMetaObject::invokeMethod( + QHotkeyPrivate::instance(), "addMappingInvoked", Qt::QueuedConnection, + Q_ARG(Qt::Key, Qt::Key(uint(shortcut[0]) & ~Qt::KeyboardModifierMask)), + Q_ARG( + Qt::KeyboardModifiers, + Qt::KeyboardModifiers(uint(shortcut[0]) & Qt::KeyboardModifierMask)), + Q_ARG(QHotkey::NativeShortcut, nativeShortcut)); +} + +bool QHotkey::isPlatformSupported() { + return QHotkeyPrivate::isPlatformSupported(); +} + +QHotkey::QHotkey(QObject *parent) + : QObject(parent), _keyCode(Qt::Key_unknown), _modifiers(Qt::NoModifier), + _registered(false) {} + +QHotkey::QHotkey(const QKeySequence &shortcut, bool autoRegister, + QObject *parent) + : QHotkey(parent) { + setShortcut(shortcut, autoRegister); +} + +QHotkey::QHotkey(Qt::Key keyCode, Qt::KeyboardModifiers modifiers, + bool autoRegister, QObject *parent) + : QHotkey(parent) { + setShortcut(keyCode, modifiers, autoRegister); +} + +QHotkey::QHotkey(QHotkey::NativeShortcut shortcut, bool autoRegister, + QObject *parent) + : QHotkey(parent) { + setNativeShortcut(shortcut, autoRegister); +} + +QHotkey::~QHotkey() { + if (_registered) + QHotkeyPrivate::instance()->removeShortcut(this); +} + +QKeySequence QHotkey::shortcut() const { + if (_keyCode == Qt::Key_unknown) + return QKeySequence(); + return QKeySequence(static_cast(_keyCode | _modifiers)); +} + +Qt::Key QHotkey::keyCode() const { return _keyCode; } + +Qt::KeyboardModifiers QHotkey::modifiers() const { return _modifiers; } + +QHotkey::NativeShortcut QHotkey::currentNativeShortcut() const { + return _nativeShortcut; +} + +bool QHotkey::isRegistered() const { return _registered; } + +bool QHotkey::setShortcut(const QKeySequence &shortcut, bool autoRegister) { + if (shortcut.isEmpty()) + return resetShortcut(); + if (shortcut.count() > 1) { + qCWarning(logQHotkey, + "Keysequences with multiple shortcuts are not allowed! " + "Only the first shortcut will be used!"); + } + + return setShortcut( + Qt::Key(uint(shortcut[0]) & ~Qt::KeyboardModifierMask), + Qt::KeyboardModifiers(uint(shortcut[0]) & Qt::KeyboardModifierMask), + autoRegister); +} + +bool QHotkey::setShortcut(Qt::Key keyCode, Qt::KeyboardModifiers modifiers, + bool autoRegister) { + if (_registered) { + if (autoRegister) { + if (!QHotkeyPrivate::instance()->removeShortcut(this)) + return false; + } else + return false; + } + + if (keyCode == Qt::Key_unknown) { + _keyCode = Qt::Key_unknown; + _modifiers = Qt::NoModifier; + _nativeShortcut = NativeShortcut(); + return true; + } + + _keyCode = keyCode; + _modifiers = modifiers; + _nativeShortcut = + QHotkeyPrivate::instance()->nativeShortcut(keyCode, modifiers); + if (_nativeShortcut.isValid()) { + if (autoRegister) + return QHotkeyPrivate::instance()->addShortcut(this); + return true; + } + + qCWarning(logQHotkey) << "Unable to map shortcut to native keys. Key:" + << keyCode << "Modifiers:" << modifiers; + _keyCode = Qt::Key_unknown; + _modifiers = Qt::NoModifier; + _nativeShortcut = NativeShortcut(); + return false; +} + +bool QHotkey::resetShortcut() { + if (_registered && !QHotkeyPrivate::instance()->removeShortcut(this)) { + return false; + } + + _keyCode = Qt::Key_unknown; + _modifiers = Qt::NoModifier; + _nativeShortcut = NativeShortcut(); + return true; +} + +bool QHotkey::setNativeShortcut(QHotkey::NativeShortcut nativeShortcut, + bool autoRegister) { + if (_registered) { + if (autoRegister) { + if (!QHotkeyPrivate::instance()->removeShortcut(this)) + return false; + } else + return false; + } + + if (nativeShortcut.isValid()) { + _keyCode = Qt::Key_unknown; + _modifiers = Qt::NoModifier; + _nativeShortcut = nativeShortcut; + if (autoRegister) + return QHotkeyPrivate::instance()->addShortcut(this); + return true; + } + + _keyCode = Qt::Key_unknown; + _modifiers = Qt::NoModifier; + _nativeShortcut = NativeShortcut(); + return true; +} + +bool QHotkey::setRegistered(bool registered) { + if (_registered && !registered) + return QHotkeyPrivate::instance()->removeShortcut(this); + if (!_registered && registered) { + if (!_nativeShortcut.isValid()) + return false; + return QHotkeyPrivate::instance()->addShortcut(this); + } + return true; +} + +// ---------- QHotkeyPrivate implementation ---------- + +QHotkeyPrivate::QHotkeyPrivate() { + Q_ASSERT_X(qApp, Q_FUNC_INFO, + "QHotkey requires QCoreApplication to be instantiated"); + qApp->eventDispatcher()->installNativeEventFilter(this); +} + +QHotkeyPrivate::~QHotkeyPrivate() { + if (!shortcuts.isEmpty()) + qCWarning(logQHotkey) + << "QHotkeyPrivate destroyed with registered shortcuts!"; + if (qApp && qApp->eventDispatcher()) + qApp->eventDispatcher()->removeNativeEventFilter(this); +} + +QHotkey::NativeShortcut +QHotkeyPrivate::nativeShortcut(Qt::Key keycode, + Qt::KeyboardModifiers modifiers) { + Qt::ConnectionType conType = + (QThread::currentThread() == thread() ? Qt::DirectConnection + : Qt::BlockingQueuedConnection); + QHotkey::NativeShortcut res; + if (!QMetaObject::invokeMethod(this, "nativeShortcutInvoked", conType, + Q_RETURN_ARG(QHotkey::NativeShortcut, res), + Q_ARG(Qt::Key, keycode), + Q_ARG(Qt::KeyboardModifiers, modifiers))) { + return QHotkey::NativeShortcut(); + } + return res; +} + +bool QHotkeyPrivate::addShortcut(QHotkey *hotkey) { + if (hotkey->_registered) + return false; + + Qt::ConnectionType conType = + (QThread::currentThread() == thread() ? Qt::DirectConnection + : Qt::BlockingQueuedConnection); + bool res = false; + if (!QMetaObject::invokeMethod(this, "addShortcutInvoked", conType, + Q_RETURN_ARG(bool, res), + Q_ARG(QHotkey *, hotkey))) { + return false; + } + + if (res) + emit hotkey->registeredChanged(true); + return res; +} + +bool QHotkeyPrivate::removeShortcut(QHotkey *hotkey) { + if (!hotkey->_registered) + return false; + + Qt::ConnectionType conType = + (QThread::currentThread() == thread() ? Qt::DirectConnection + : Qt::BlockingQueuedConnection); + bool res = false; + if (!QMetaObject::invokeMethod(this, "removeShortcutInvoked", conType, + Q_RETURN_ARG(bool, res), + Q_ARG(QHotkey *, hotkey))) { + return false; + } + + if (res) + emit hotkey->registeredChanged(false); + return res; +} + +void QHotkeyPrivate::activateShortcut(QHotkey::NativeShortcut shortcut) { + QMetaMethod signal = QMetaMethod::fromSignal(&QHotkey::activated); + for (QHotkey *hkey : shortcuts.values(shortcut)) + signal.invoke(hkey, Qt::QueuedConnection); +} + +void QHotkeyPrivate::releaseShortcut(QHotkey::NativeShortcut shortcut) { + QMetaMethod signal = QMetaMethod::fromSignal(&QHotkey::released); + for (QHotkey *hkey : shortcuts.values(shortcut)) + signal.invoke(hkey, Qt::QueuedConnection); +} + +void QHotkeyPrivate::addMappingInvoked(Qt::Key keycode, + Qt::KeyboardModifiers modifiers, + QHotkey::NativeShortcut nativeShortcut) { + mapping.insert({keycode, modifiers}, nativeShortcut); +} + +bool QHotkeyPrivate::addShortcutInvoked(QHotkey *hotkey) { + QHotkey::NativeShortcut shortcut = hotkey->_nativeShortcut; + + if (!shortcuts.contains(shortcut)) { + if (!registerShortcut(shortcut)) { + qCWarning(logQHotkey) << QHotkey::tr("Failed to register %1. Error: %2") + .arg(hotkey->shortcut().toString(), error); + return false; + } + } + + shortcuts.insert(shortcut, hotkey); + hotkey->_registered = true; + return true; +} + +bool QHotkeyPrivate::removeShortcutInvoked(QHotkey *hotkey) { + QHotkey::NativeShortcut shortcut = hotkey->_nativeShortcut; + + if (shortcuts.remove(shortcut, hotkey) == 0) + return false; + hotkey->_registered = false; + emit hotkey->registeredChanged(true); + if (shortcuts.count(shortcut) == 0) { + if (!unregisterShortcut(shortcut)) { + qCWarning(logQHotkey) << QHotkey::tr("Failed to unregister %1. Error: %2") + .arg(hotkey->shortcut().toString(), error); + return false; + } + return true; + } + return true; +} + +QHotkey::NativeShortcut +QHotkeyPrivate::nativeShortcutInvoked(Qt::Key keycode, + Qt::KeyboardModifiers modifiers) { + if (mapping.contains({keycode, modifiers})) + return mapping.value({keycode, modifiers}); + + bool ok1 = false; + auto k = nativeKeycode(keycode, ok1); + bool ok2 = false; + auto m = nativeModifiers(modifiers, ok2); + if (ok1 && ok2) + return {k, m}; + return {}; +} + +QHotkey::NativeShortcut::NativeShortcut() : key(), modifier(), valid(false) {} + +QHotkey::NativeShortcut::NativeShortcut(quint32 key, quint32 modifier) + : key(key), modifier(modifier), valid(true) {} + +bool QHotkey::NativeShortcut::isValid() const { return valid; } + +bool QHotkey::NativeShortcut::operator==(QHotkey::NativeShortcut other) const { + return (key == other.key) && (modifier == other.modifier) && + valid == other.valid; +} + +bool QHotkey::NativeShortcut::operator!=(QHotkey::NativeShortcut other) const { + return (key != other.key) || (modifier != other.modifier) || + valid != other.valid; +} + +uint qHash(QHotkey::NativeShortcut key) { + return qHash(key.key) ^ qHash(key.modifier); +} + +uint qHash(QHotkey::NativeShortcut key, uint seed) { + return qHash(key.key, seed) ^ qHash(key.modifier, seed); +} diff --git a/QHotkey/qhotkey.h b/QHotkey/qhotkey.h new file mode 100644 index 0000000..f669640 --- /dev/null +++ b/QHotkey/qhotkey.h @@ -0,0 +1,124 @@ +#ifndef QHOTKEY_H +#define QHOTKEY_H + +#include +#include +#include +#include + +#ifdef QHOTKEY_LIB + #ifdef QHOTKEY_LIB_BUILD + #define QHOTKEY_SHARED_EXPORT Q_DECL_EXPORT + #else + #define QHOTKEY_SHARED_EXPORT Q_DECL_IMPORT + #endif +#else + #define QHOTKEY_SHARED_EXPORT +#endif + +//! A class to define global, systemwide Hotkeys +class QHOTKEY_SHARED_EXPORT QHotkey : public QObject +{ + Q_OBJECT + //! @private + friend class QHotkeyPrivate; + + //! Specifies whether this hotkey is currently registered or not + Q_PROPERTY(bool registered READ isRegistered WRITE setRegistered NOTIFY registeredChanged) + //! Holds the shortcut this hotkey will be triggered on + Q_PROPERTY(QKeySequence shortcut READ shortcut WRITE setShortcut RESET resetShortcut) + +public: + //! Defines shortcut with native keycodes + class QHOTKEY_SHARED_EXPORT NativeShortcut { + public: + //! The native keycode + quint32 key; + //! The native modifiers + quint32 modifier; + + //! Creates an invalid native shortcut + NativeShortcut(); + //! Creates a valid native shortcut, with the given key and modifiers + NativeShortcut(quint32 key, quint32 modifier = 0); + + //! Checks, whether this shortcut is valid or not + bool isValid() const; + + //! Equality operator + bool operator ==(NativeShortcut other) const; + //! Inequality operator + bool operator !=(NativeShortcut other) const; + + private: + bool valid; + }; + + //! Adds a global mapping of a key sequence to a replacement native shortcut + static void addGlobalMapping(const QKeySequence &shortcut, NativeShortcut nativeShortcut); + + //! Checks if global shortcuts are supported by the current platform + static bool isPlatformSupported(); + + //! Default Constructor + explicit QHotkey(QObject *parent = nullptr); + //! Constructs a hotkey with a shortcut and optionally registers it + explicit QHotkey(const QKeySequence &shortcut, bool autoRegister = false, QObject *parent = nullptr); + //! Constructs a hotkey with a key and modifiers and optionally registers it + explicit QHotkey(Qt::Key keyCode, Qt::KeyboardModifiers modifiers, bool autoRegister = false, QObject *parent = nullptr); + //! Constructs a hotkey from a native shortcut and optionally registers it + explicit QHotkey(NativeShortcut shortcut, bool autoRegister = false, QObject *parent = nullptr); + ~QHotkey() override; + + //! @readAcFn{QHotkey::registered} + bool isRegistered() const; + //! @readAcFn{QHotkey::shortcut} + QKeySequence shortcut() const; + //! @readAcFn{QHotkey::shortcut} - the key only + Qt::Key keyCode() const; + //! @readAcFn{QHotkey::shortcut} - the modifiers only + Qt::KeyboardModifiers modifiers() const; + + //! Get the current native shortcut + NativeShortcut currentNativeShortcut() const; + +public slots: + //! @writeAcFn{QHotkey::registered} + bool setRegistered(bool registered); + + //! @writeAcFn{QHotkey::shortcut} + bool setShortcut(const QKeySequence &shortcut, bool autoRegister = false); + //! @writeAcFn{QHotkey::shortcut} + bool setShortcut(Qt::Key keyCode, Qt::KeyboardModifiers modifiers, bool autoRegister = false); + //! @resetAcFn{QHotkey::shortcut} + bool resetShortcut(); + + //! Set this hotkey to a native shortcut + bool setNativeShortcut(QHotkey::NativeShortcut nativeShortcut, bool autoRegister = false); + +signals: + //! Will be emitted if the shortcut is pressed + void activated(QPrivateSignal); + + //! Will be emitted if the shortcut press is released + void released(QPrivateSignal); + + //! @notifyAcFn{QHotkey::registered} + void registeredChanged(bool registered); + +private: + Qt::Key _keyCode; + Qt::KeyboardModifiers _modifiers; + + NativeShortcut _nativeShortcut; + bool _registered; +}; + +uint QHOTKEY_SHARED_EXPORT qHash(QHotkey::NativeShortcut key); +uint QHOTKEY_SHARED_EXPORT qHash(QHotkey::NativeShortcut key, uint seed); + +QHOTKEY_SHARED_EXPORT Q_DECLARE_LOGGING_CATEGORY(logQHotkey) + +Q_DECLARE_METATYPE(QHotkey::NativeShortcut) + +#endif // QHOTKEY_H diff --git a/QHotkey/qhotkey_p.h b/QHotkey/qhotkey_p.h new file mode 100644 index 0000000..b7c1721 --- /dev/null +++ b/QHotkey/qhotkey_p.h @@ -0,0 +1,59 @@ +#ifndef QHOTKEY_P_H +#define QHOTKEY_P_H + +#include "qhotkey.h" +#include +#include +#include +#include + +class QHOTKEY_SHARED_EXPORT QHotkeyPrivate : public QObject, public QAbstractNativeEventFilter +{ + Q_OBJECT + +public: + QHotkeyPrivate();//singleton!!! + ~QHotkeyPrivate(); + + static QHotkeyPrivate *instance(); + static bool isPlatformSupported(); + + QHotkey::NativeShortcut nativeShortcut(Qt::Key keycode, Qt::KeyboardModifiers modifiers); + + bool addShortcut(QHotkey *hotkey); + bool removeShortcut(QHotkey *hotkey); + +protected: + void activateShortcut(QHotkey::NativeShortcut shortcut); + void releaseShortcut(QHotkey::NativeShortcut shortcut); + + virtual quint32 nativeKeycode(Qt::Key keycode, bool &ok) = 0;//platform implement + virtual quint32 nativeModifiers(Qt::KeyboardModifiers modifiers, bool &ok) = 0;//platform implement + + virtual bool registerShortcut(QHotkey::NativeShortcut shortcut) = 0;//platform implement + virtual bool unregisterShortcut(QHotkey::NativeShortcut shortcut) = 0;//platform implement + + QString error; + +private: + QHash, QHotkey::NativeShortcut> mapping; + QMultiHash shortcuts; + + Q_INVOKABLE void addMappingInvoked(Qt::Key keycode, Qt::KeyboardModifiers modifiers, QHotkey::NativeShortcut nativeShortcut); + Q_INVOKABLE bool addShortcutInvoked(QHotkey *hotkey); + Q_INVOKABLE bool removeShortcutInvoked(QHotkey *hotkey); + Q_INVOKABLE QHotkey::NativeShortcut nativeShortcutInvoked(Qt::Key keycode, Qt::KeyboardModifiers modifiers); +}; + +#define NATIVE_INSTANCE(ClassName) \ + Q_GLOBAL_STATIC(ClassName, hotkeyPrivate) \ + \ + QHotkeyPrivate *QHotkeyPrivate::instance()\ + {\ + return hotkeyPrivate;\ + } + +Q_DECLARE_METATYPE(Qt::Key) +Q_DECLARE_METATYPE(Qt::KeyboardModifiers) + +#endif // QHOTKEY_P_H diff --git a/QHotkey/qhotkey_x11.cpp b/QHotkey/qhotkey_x11.cpp new file mode 100644 index 0000000..38f8772 --- /dev/null +++ b/QHotkey/qhotkey_x11.cpp @@ -0,0 +1,231 @@ +#include "qhotkey.h" +#include "qhotkey_p.h" +#include +#include +#include +#include +#include +#include + +// compability to pre Qt 5.8 +#ifndef Q_FALLTHROUGH +#define Q_FALLTHROUGH() (void)0 +#endif + +class QHotkeyPrivateX11 : public QHotkeyPrivate { +public: + // QAbstractNativeEventFilter interface + bool nativeEventFilter(const QByteArray &eventType, void *message, + long *result) Q_DECL_OVERRIDE; + +protected: + // QHotkeyPrivate interface + quint32 nativeKeycode(Qt::Key keycode, bool &ok) Q_DECL_OVERRIDE; + quint32 nativeModifiers(Qt::KeyboardModifiers modifiers, + bool &ok) Q_DECL_OVERRIDE; + static QString getX11String(Qt::Key keycode); + bool registerShortcut(QHotkey::NativeShortcut shortcut) Q_DECL_OVERRIDE; + bool unregisterShortcut(QHotkey::NativeShortcut shortcut) Q_DECL_OVERRIDE; + +private: + static const QVector specialModifiers; + static const quint32 validModsMask; + xcb_key_press_event_t prevHandledEvent; + xcb_key_press_event_t prevEvent; + + static QString formatX11Error(Display *display, int errorCode); + + class HotkeyErrorHandler { + public: + HotkeyErrorHandler(); + ~HotkeyErrorHandler(); + + static bool hasError; + static QString errorString; + + private: + XErrorHandler prevHandler; + + static int handleError(Display *display, XErrorEvent *error); + }; +}; +NATIVE_INSTANCE(QHotkeyPrivateX11) + +bool QHotkeyPrivate::isPlatformSupported() { return QX11Info::isPlatformX11(); } + +const QVector QHotkeyPrivateX11::specialModifiers = { + 0, Mod2Mask, LockMask, (Mod2Mask | LockMask)}; +const quint32 QHotkeyPrivateX11::validModsMask = + ShiftMask | ControlMask | Mod1Mask | Mod4Mask; + +bool QHotkeyPrivateX11::nativeEventFilter(const QByteArray &eventType, + void *message, long *result) { + Q_UNUSED(eventType) + Q_UNUSED(result) + + auto *genericEvent = static_cast(message); + if (genericEvent->response_type == XCB_KEY_PRESS) { + xcb_key_press_event_t keyEvent = + *static_cast(message); + this->prevEvent = keyEvent; + if (this->prevHandledEvent.response_type == XCB_KEY_RELEASE) { + if (this->prevHandledEvent.time == keyEvent.time) + return false; + } + this->activateShortcut( + {keyEvent.detail, keyEvent.state & QHotkeyPrivateX11::validModsMask}); + } else if (genericEvent->response_type == XCB_KEY_RELEASE) { + xcb_key_release_event_t keyEvent = + *static_cast(message); + this->prevEvent = keyEvent; + QTimer::singleShot(50, [this, keyEvent] { + if (this->prevEvent.time == keyEvent.time && + this->prevEvent.response_type == keyEvent.response_type && + this->prevEvent.detail == keyEvent.detail) { + this->releaseShortcut( + {keyEvent.detail, + keyEvent.state & QHotkeyPrivateX11::validModsMask}); + } + }); + this->prevHandledEvent = keyEvent; + } + + return false; +} + +QString QHotkeyPrivateX11::getX11String(Qt::Key keycode) { + switch (keycode) { + + case Qt::Key_MediaLast: + case Qt::Key_MediaPrevious: + return QStringLiteral("XF86AudioPrev"); + case Qt::Key_MediaNext: + return QStringLiteral("XF86AudioNext"); + case Qt::Key_MediaPause: + case Qt::Key_MediaPlay: + case Qt::Key_MediaTogglePlayPause: + return QStringLiteral("XF86AudioPlay"); + case Qt::Key_MediaRecord: + return QStringLiteral("XF86AudioRecord"); + case Qt::Key_MediaStop: + return QStringLiteral("XF86AudioStop"); + default: + return QKeySequence(keycode).toString(QKeySequence::NativeText); + } +} + +quint32 QHotkeyPrivateX11::nativeKeycode(Qt::Key keycode, bool &ok) { + QString keyString = getX11String(keycode); + + KeySym keysym = XStringToKeysym(keyString.toLatin1().constData()); + if (keysym == NoSymbol) { + // not found -> just use the key + if (keycode <= 0xFFFF) + keysym = keycode; + else + return 0; + } + + if (QX11Info::isPlatformX11()) { + auto res = XKeysymToKeycode(QX11Info::display(), keysym); + if (res != 0) + ok = true; + return res; + } + return 0; +} + +quint32 QHotkeyPrivateX11::nativeModifiers(Qt::KeyboardModifiers modifiers, + bool &ok) { + quint32 nMods = 0; + if (modifiers & Qt::ShiftModifier) + nMods |= ShiftMask; + if (modifiers & Qt::ControlModifier) + nMods |= ControlMask; + if (modifiers & Qt::AltModifier) + nMods |= Mod1Mask; + if (modifiers & Qt::MetaModifier) + nMods |= Mod4Mask; + ok = true; + return nMods; +} + +bool QHotkeyPrivateX11::registerShortcut(QHotkey::NativeShortcut shortcut) { + Display *display = QX11Info::display(); + if (!display || !QX11Info::isPlatformX11()) + return false; + + HotkeyErrorHandler errorHandler; + for (quint32 specialMod : QHotkeyPrivateX11::specialModifiers) { + XGrabKey(display, int(shortcut.key), shortcut.modifier | specialMod, + DefaultRootWindow(display), True, GrabModeAsync, GrabModeAsync); + } + XSync(display, False); + + if (errorHandler.hasError) { + error = errorHandler.errorString; + this->unregisterShortcut(shortcut); + return false; + } + return true; +} + +bool QHotkeyPrivateX11::unregisterShortcut(QHotkey::NativeShortcut shortcut) { + Display *display = QX11Info::display(); + if (!display) + return false; + + HotkeyErrorHandler errorHandler; + for (quint32 specialMod : QHotkeyPrivateX11::specialModifiers) { + XUngrabKey(display, int(shortcut.key), shortcut.modifier | specialMod, + XDefaultRootWindow(display)); + } + XSync(display, False); + + if (HotkeyErrorHandler::hasError) { + error = HotkeyErrorHandler::errorString; + return false; + } + return true; +} + +QString QHotkeyPrivateX11::formatX11Error(Display *display, int errorCode) { + char errStr[256]; + XGetErrorText(display, errorCode, errStr, 256); + return QString::fromLatin1(errStr); +} + +// ---------- QHotkeyPrivateX11::HotkeyErrorHandler implementation ---------- + +bool QHotkeyPrivateX11::HotkeyErrorHandler::hasError = false; +QString QHotkeyPrivateX11::HotkeyErrorHandler::errorString; + +QHotkeyPrivateX11::HotkeyErrorHandler::HotkeyErrorHandler() { + prevHandler = XSetErrorHandler(&HotkeyErrorHandler::handleError); +} + +QHotkeyPrivateX11::HotkeyErrorHandler::~HotkeyErrorHandler() { + XSetErrorHandler(prevHandler); + hasError = false; + errorString.clear(); +} + +int QHotkeyPrivateX11::HotkeyErrorHandler::handleError(Display *display, + XErrorEvent *error) { + switch (error->error_code) { + case BadAccess: + case BadValue: + case BadWindow: + if (error->request_code == 33 || // grab key + error->request_code == 34) { // ungrab key + hasError = true; + errorString = + QHotkeyPrivateX11::formatX11Error(display, error->error_code); + return 1; + } + Q_FALLTHROUGH(); + // fall through + default: + return 0; + } +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/WingTool.pro b/WingTool.pro new file mode 100644 index 0000000..666e77e --- /dev/null +++ b/WingTool.pro @@ -0,0 +1,42 @@ +QT += core gui dtkwidget + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +TARGET = WingTool +TEMPLATE = app + +QT += x11extras +LIBS += -lX11 -lXext -lXtst + +SOURCES += \ + main.cpp \ + class/eventmonitor.cpp \ + QHotkey/qhotkey.cpp \ + QHotkey/qhotkey_x11.cpp \ + class/wingapplication.cpp \ + class/appmanager.cpp \ + dialog/centerwindow.cpp \ + dialog/toolwindow.cpp \ + dialog/toolboxwindow.cpp \ + class/settings.cpp \ + plugin/pluginsystem.cpp \ + class/settingmanager.cpp \ + dialog/shortcuteditdialog.cpp + +RESOURCES += resources.qrc + +HEADERS += \ + class/eventmonitor.h \ + QHotkey/qhotkey.h \ + QHotkey/qhotkey_p.h \ + class/wingapplication.h \ + class/appmanager.h \ + dialog/centerwindow.h \ + dialog/toolwindow.h \ + dialog/toolboxwindow.h \ + class/settings.h \ + plugin/pluginsystem.h \ + plugin/iwingtoolplg.h \ + class/settingmanager.h \ + dialog/shortcuteditdialog.h \ + utilies.h diff --git a/class/appmanager.cpp b/class/appmanager.cpp new file mode 100644 index 0000000..cab7b7e --- /dev/null +++ b/class/appmanager.cpp @@ -0,0 +1,117 @@ +#include "appmanager.h" +#include +#include + +AppManager *AppManager::m_instance = nullptr; + +AppManager::AppManager(QObject *parent) : QObject(parent) { + + // 初始化选词监控 + auto clipboard = qApp->clipboard(); + connect(clipboard, &QClipboard::selectionChanged, this, [=] { + // 防止过多的消息干扰 + if (!ignoremsg) { + emit this->selectionTextChanged(clipboard->text(QClipboard::Selection)); + } + }); + + // 初始化全局鼠标监控 + +#define CONNECT(sig) connect(&monitor, SIGNAL(sig), SLOT(sig)); + CONNECT(clicked); + CONNECT(doubleClicked); + CONNECT(mouseWheel); + CONNECT(mouseMove); + + connect(&monitor, &EventMonitor::buttonPress, + [=](EventMonitor::MouseButton btn, int x, int y) { + if (btn == EventMonitor::MouseButton::MiddleButton) { + toolwin.popup(QCursor::pos()); + } + emit this->buttonPress(btn, x, y); + }); + connect(&monitor, &EventMonitor::mouseDrag, [=](int x, int y) { + ignoremsg = false; + if (this->toolwin.isVisible()) + this->toolwin.sendMousePosUpdated(); + emit this->mouseDrag(x, y); + }); + connect(&monitor, &EventMonitor::buttonRelease, + [=](EventMonitor::MouseButton btn, int x, int y) { + toolwin.hide(); + ignoremsg = true; + emit this->buttonRelease(btn, x, y); + }); + + monitor.start(); + + // 初始化热键存储 + + // 存单实例 + m_instance = this; +} + +AppManager::~AppManager() { clearHotkey(); } + +AppManager *AppManager::instance() { return m_instance; } + +QHotkey *AppManager::registerHotkey(QKeySequence &keyseq) { + auto hotkey = new QHotkey(keyseq, true); + hotkeys += hotkey; + connect(hotkey, &QHotkey::activated, this, + [=] { emit this->hotkeyTirggered(hotkey, hotkeys.indexOf(hotkey)); }); + connect(hotkey, &QHotkey::released, this, + [=] { emit this->hotkeyReleased(hotkey, hotkeys.indexOf(hotkey)); }); + connect(hotkey, &QHotkey::registeredChanged, this, [=](bool registered) { + emit this->hotkeyEnableChanged(registered, hotkey, hotkeys.indexOf(hotkey)); + }); + return hotkey; +} + +bool AppManager::enableHotKey(int index, bool enabled) { + if (index < 0 || index >= hotkeys.count()) + return false; + hotkeys[index]->setRegistered(enabled); + return true; +} + +bool AppManager::unregisterHotkey(QHotkey *hotkey) { + auto i = hotkeys.indexOf(hotkey); + if (i < 0) + return false; + hotkeys.removeAt(i); + hotkey->disconnect(); + delete hotkey; + return true; +} + +bool AppManager::unregisterHotkey(int index) { + if (index < 0 || index >= hotkeys.count()) + return false; + auto del = hotkeys[index]; + del->disconnect(); + hotkeys.removeAt(index); + delete del; + return true; +} + +bool AppManager::editHotkey(int index, QKeySequence &keyseq) { + if (index < 0 || index >= hotkeys.count()) + return false; + auto del = hotkeys[index]; + del->setShortcut(keyseq, true); + return true; +} + +QHotkey *AppManager::hotkey(int index) { + if (index < 0 || index >= hotkeys.count()) + return nullptr; + return hotkeys[index]; +} + +void AppManager::clearHotkey() { + for (auto item : hotkeys) { + delete item; + } + hotkeys.clear(); +} diff --git a/class/appmanager.h b/class/appmanager.h new file mode 100644 index 0000000..71f0b87 --- /dev/null +++ b/class/appmanager.h @@ -0,0 +1,57 @@ +#ifndef APPMANAGER_H +#define APPMANAGER_H + +#include "QHotkey/qhotkey.h" +#include "class/eventmonitor.h" +#include "dialog/toolwindow.h" +#undef Bool +#undef Unsorted +#include +#include +#include + +class AppManager : public QObject { + Q_OBJECT +public: + explicit AppManager(QObject *parent = nullptr); + ~AppManager(); + + static AppManager *instance(); + +public: + QHotkey *registerHotkey(QKeySequence &keyseq); + bool enableHotKey(int index, bool enabled = true); + bool unregisterHotkey(QHotkey *hotkey); + bool unregisterHotkey(int index); + bool editHotkey(int index, QKeySequence &keyseq); + QHotkey *hotkey(int index); + void clearHotkey(); + +signals: + void buttonPress(EventMonitor::MouseButton btn, int x, int y); + void buttonRelease(EventMonitor::MouseButton btn, int x, int y); + void clicked(int x, int y); + void doubleClicked(int x, int y); + void mouseWheel(EventMonitor::MouseWheel direction); + void mouseMove(int x, int y); + void mouseDrag(int x, int y); + + void hotkeyTirggered(const QHotkey *hotkey, int index); + void hotkeyReleased(const QHotkey *hotkey, int index); + void hotkeyEnableChanged(bool value, const QHotkey *hotkey, int index); + + void selectionTextChanged(const QString &selectedText); + +private: + EventMonitor monitor; + QList hotkeys; + QList execs; + + bool ignoremsg = false; + + static AppManager *m_instance; + + ToolWindow toolwin; +}; + +#endif // APPMANAGER_H diff --git a/class/eventmonitor.cpp b/class/eventmonitor.cpp new file mode 100644 index 0000000..2c8876c --- /dev/null +++ b/class/eventmonitor.cpp @@ -0,0 +1,184 @@ +#include "eventmonitor.h" +#include +#include + +EventMonitor::EventMonitor(QObject *parent) : QThread(parent) { + isPress = false; +} + +EventMonitor::~EventMonitor() { + Display *display = XOpenDisplay(nullptr); + XRecordDisableContext(display, context); + XSync(display, 0); + wait(); + XRecordFreeContext(display, context); + XCloseDisplay(display_datalink); +} + +void EventMonitor::run() { + // Init x11 display. + Display *display = XOpenDisplay(nullptr); + if (display == nullptr) { + fprintf(stderr, "unable to open display\n"); + return; + } + + // Receive from ALL clients, including future clients. + XRecordClientSpec clients = XRecordAllClients; + XRecordRange *range = XRecordAllocRange(); + if (range == nullptr) { + fprintf(stderr, "unable to allocate XRecordRange\n"); + return; + } + + // Receive ButtonPress, ButtonRelease and MotionNotify + // events. + memset(range, 0, sizeof(XRecordRange)); + range->device_events.first = ButtonPress; + range->device_events.last = MotionNotify; + + // And create the XRECORD context. + context = XRecordCreateContext(display, 0, &clients, 1, &range, 1); + if (context == 0) { + fprintf(stderr, "XRecordCreateContext failed\n"); + return; + } + XFree(range); + + // Sync x11 display message. + XSync(display, True); + + display_datalink = XOpenDisplay(nullptr); + if (display_datalink == nullptr) { + fprintf(stderr, "unable to open second display\n"); + return; + } + + // Enter in xrecord event loop. + if (!XRecordEnableContext(display_datalink, context, callback, + reinterpret_cast(this))) { + fprintf(stderr, "XRecordEnableContext() failed\n"); + return; + } + + // init timer + clickbefore = std::chrono::system_clock::now(); + isClicked = false; +} + +void EventMonitor::callback(XPointer ptr, XRecordInterceptData *data) { + (reinterpret_cast(ptr))->handleRecordEvent(data); +} + +void EventMonitor::handleRecordEvent(XRecordInterceptData *data) { + if (data->category == XRecordFromServer) { + auto event = reinterpret_cast(data->data); + + switch (event->u.u.type) { + case ButtonPress: + if (handleWheelEvent(event->u.u.detail)) { + isPress = true; + MouseButton btn = MouseButton::NoneButton; + switch (event->u.u.detail) { + case Button1: { + btn = MouseButton::LeftButton; + + auto clicknow = std::chrono::system_clock::now(); + double diff_ms = + std::chrono::duration(clicknow - clickbefore) + .count(); + clickbefore = clicknow; + if (isClicked && diff_ms <= QApplication::doubleClickInterval()) { + emit doubleClicked(event->u.keyButtonPointer.rootX, + event->u.keyButtonPointer.rootY); + isClicked = false; + } else { + emit clicked(event->u.keyButtonPointer.rootX, + event->u.keyButtonPointer.rootY); + isClicked = true; + } + + } break; + case Button2: + btn = MouseButton::MiddleButton; + break; + case Button3: + btn = MouseButton::RightButton; + break; + case XButton1: + btn = MouseButton::XButton_1; + break; + case XButton2: + btn = MouseButton::XButton_2; + break; + } + + emit buttonPress(btn, event->u.keyButtonPointer.rootX, + event->u.keyButtonPointer.rootY); + } + break; + case MotionNotify: + if (isPress) { + emit mouseDrag(event->u.keyButtonPointer.rootX, + event->u.keyButtonPointer.rootY); + } else { + emit mouseMove(event->u.keyButtonPointer.rootX, + event->u.keyButtonPointer.rootY); + } + break; + case ButtonRelease: + if (handleWheelEvent(event->u.u.detail)) { + isPress = false; + + MouseButton btn = MouseButton::NoneButton; + switch (event->u.u.detail) { + case Button1: + btn = MouseButton::LeftButton; + break; + case Button2: + btn = MouseButton::MiddleButton; + break; + case Button3: + btn = MouseButton::RightButton; + break; + case XButton1: + btn = MouseButton::XButton_1; + break; + case XButton2: + btn = MouseButton::XButton_2; + break; + } + emit buttonRelease(btn, event->u.keyButtonPointer.rootX, + event->u.keyButtonPointer.rootY); + } + break; + default: + break; + } + } + + fflush(stdout); + XRecordFreeData(data); +} + +bool EventMonitor::handleWheelEvent(int detail) { + MouseWheel wheel = MouseWheel::None; + switch (detail) { + case WheelUp: + wheel = MouseWheel::Up; + break; + case WheelDown: + wheel = MouseWheel::Down; + break; + case WheelLeft: + wheel = MouseWheel::Left; + break; + case WheelRight: + wheel = MouseWheel::Right; + break; + default: + return true; + } + emit mouseWheel(wheel); + return false; +} diff --git a/class/eventmonitor.h b/class/eventmonitor.h new file mode 100644 index 0000000..a2a0751 --- /dev/null +++ b/class/eventmonitor.h @@ -0,0 +1,74 @@ +/***********************************************************/ +// 此代码时基于 Deepin 的取色应用进行定制修改而成,GPL 协议 +// 原来的代码无法直接进行编译,就算配置了对应的环境,本人(寂静的羽夏) +// 对此进行了大量资料的查阅修复了该问题,并做了自己的修改 +/***********************************************************/ + +#ifndef EVENTMONITOR_H +#define EVENTMONITOR_H + +#include +#include +#include +#include +#include +#include +#undef None +#include + +// Virtual button codes that are not defined by X11. +#define Button1 1 +#define Button2 2 +#define Button3 3 +#define WheelUp 4 +#define WheelDown 5 +#define WheelLeft 6 +#define WheelRight 7 +#define XButton1 8 +#define XButton2 9 + +class EventMonitor : public QThread { + Q_OBJECT + +public: + enum class MouseButton { + NoneButton, + LeftButton, + RightButton, + MiddleButton, + XButton_1, + XButton_2 + }; + Q_ENUM(MouseButton) + + enum class MouseWheel { None, Up, Down, Left, Right }; + Q_ENUM(MouseWheel) + +public: + EventMonitor(QObject *parent = nullptr); + ~EventMonitor() override; + +signals: + void buttonPress(MouseButton btn, int x, int y); // 当鼠标按键被按下时 + void buttonRelease(MouseButton btn, int x, int y); // 当鼠标按键被释放时 + void clicked(int x, int y); // 当鼠标进行单击操作时 + void doubleClicked(int x, int y); // 当鼠标进行双击操作时 + void mouseWheel(MouseWheel direction); // 当鼠标滚轮滚动时 + void mouseMove(int x, int y); // 当鼠标移动时 + void mouseDrag(int x, int y); // 当鼠标拖拽时 + +protected: + static void callback(XPointer trash, XRecordInterceptData *data); + bool handleWheelEvent(int detail); + void handleRecordEvent(XRecordInterceptData *); + void run() override; + +private: + bool isPress, isClicked; + XRecordContext context; + Display *display_datalink; + + std::chrono::system_clock::time_point clickbefore; +}; + +#endif diff --git a/class/settingmanager.cpp b/class/settingmanager.cpp new file mode 100644 index 0000000..02efa93 --- /dev/null +++ b/class/settingmanager.cpp @@ -0,0 +1,6 @@ +#include "settingmanager.h" + +SettingManager::SettingManager(QObject *parent) : QObject(parent) +{ + +} diff --git a/class/settingmanager.h b/class/settingmanager.h new file mode 100644 index 0000000..36f9bb0 --- /dev/null +++ b/class/settingmanager.h @@ -0,0 +1,14 @@ +#ifndef SETTINGMANAGER_H +#define SETTINGMANAGER_H + +#include + +class SettingManager : public QObject { + Q_OBJECT +public: + explicit SettingManager(QObject *parent = nullptr); + +private: +}; + +#endif // SETTINGMANAGER_H diff --git a/class/settings.cpp b/class/settings.cpp new file mode 100644 index 0000000..27ddf83 --- /dev/null +++ b/class/settings.cpp @@ -0,0 +1,6 @@ +#include "settings.h" + +Settings::Settings() +{ + +} diff --git a/class/settings.h b/class/settings.h new file mode 100644 index 0000000..9cd2454 --- /dev/null +++ b/class/settings.h @@ -0,0 +1,11 @@ +#ifndef SETTINGS_H +#define SETTINGS_H + + +class Settings +{ +public: + Settings(); +}; + +#endif // SETTINGS_H diff --git a/class/wingapplication.cpp b/class/wingapplication.cpp new file mode 100644 index 0000000..cacff99 --- /dev/null +++ b/class/wingapplication.cpp @@ -0,0 +1,69 @@ +#include "wingapplication.h" +#include +#include +#include +#include +#include +#include + +WingApplication::WingApplication(int &argc, char **argv) + : DApplication(argc, argv) {} + +bool WingApplication::notify(QObject *obj, QEvent *event) { + bool done = true; + try { + done = QApplication::notify(obj, event); + } catch (const QUnhandledException &ex) { + auto p = this->applicationDirPath(); + auto errlog = "errlog"; + QDir dir(p); + dir.mkdir(errlog); + dir.cd(errlog); + auto time = QDateTime::currentDateTimeUtc(); + auto ferrname = QString::number(time.currentSecsSinceEpoch(), 16) + ".log"; + QFile log(dir.absolutePath() + "/" + ferrname); + if (log.open(QFile::WriteOnly)) { + log.write(ex.what()); + log.close(); + } else { + log.setFileName( + QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/" + + ferrname); + if (log.open(QFile::WriteOnly)) { + log.write(ex.what()); + log.close(); + } else { + QMessageBox::critical(nullptr, tr("Error"), + tr("WriteErrorLogError") + ferrname); + QMessageBox::information(nullptr, tr("Crash"), ex.what()); + } + } + } catch (const QException &ex) { + auto p = this->applicationDirPath(); + auto errlog = "errlog"; + QDir dir(p); + dir.mkdir(errlog); + dir.cd(errlog); + auto time = QDateTime::currentDateTimeUtc(); + auto ferrname = QString::number(time.currentSecsSinceEpoch(), 16) + ".log"; + QFile log(dir.absolutePath() + "/" + ferrname); + if (log.open(QFile::WriteOnly)) { + log.write(ex.what()); + log.close(); + } else { + log.setFileName( + QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/" + + ferrname); + if (log.open(QFile::WriteOnly)) { + log.write(ex.what()); + log.close(); + } else { + QMessageBox::critical(nullptr, tr("Error"), + tr("WriteErrorLogError") + ferrname); + QMessageBox::information(nullptr, tr("Crash"), ex.what()); + } + } + } catch (...) { + } + return done; +} diff --git a/class/wingapplication.h b/class/wingapplication.h new file mode 100644 index 0000000..4b87931 --- /dev/null +++ b/class/wingapplication.h @@ -0,0 +1,16 @@ +#ifndef WINGAPPLICATION_H +#define WINGAPPLICATION_H + +#include + +DWIDGET_USE_NAMESPACE + +class WingApplication : public DApplication { +public: + WingApplication(int &argc, char **argv); + +private: + bool notify(QObject *obj, QEvent *event) override; +}; + +#endif // WINGAPPLICATION_H diff --git a/dialog/centerwindow.cpp b/dialog/centerwindow.cpp new file mode 100644 index 0000000..d2cb97a --- /dev/null +++ b/dialog/centerwindow.cpp @@ -0,0 +1,301 @@ +#include "centerwindow.h" +#include "shortcuteditdialog.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +CenterWindow::CenterWindow(DMainWindow *parent) + : DMainWindow(parent), manager(AppManager::instance()) { + QIcon picon = ProgramIcon; + setWindowTitle(tr("CenterWindow")); + setMinimumSize(700, 500); + setWindowIcon(picon); + + auto title = titlebar(); + title->setTitle(tr("CenterWindow")); + title->setIcon(picon); + + tabs = new DTabWidget(this); + setCentralWidget(tabs); + + // General + auto w = new QWidget(this); + auto vlayout = new QVBoxLayout(w); + cbauto = new DCheckBox(tr("AutoStart"), this); + vlayout->setMargin(20); + vlayout->addWidget(cbauto, Qt::AlignTop); + vlayout->addStretch(); + tabs->addTab(w, tr("General")); + + // Hotkeys + w = new QWidget(this); + vlayout = new QVBoxLayout(w); + auto group = new DButtonBox(this); + vlayout->setMargin(20); + QList blist; + auto b = new DButtonBoxButton(tr("Add"), this); + connect(b, &DButtonBoxButton::clicked, this, &CenterWindow::on_addHotkey); + blist.append(b); + b = new DButtonBoxButton(tr("Remove"), this); + connect(b, &DButtonBoxButton::clicked, this, &CenterWindow::on_removeHotkey); + blist.append(b); + b = new DButtonBoxButton(tr("Edit"), this); + connect(b, &DButtonBoxButton::clicked, this, &CenterWindow::on_editHotkey); + blist.append(b); + b = new DButtonBoxButton(tr("Clear"), this); + connect(b, &DButtonBoxButton::clicked, this, &CenterWindow::on_clearHotkey); + blist.append(b); + group->setButtonList(blist, false); + vlayout->addWidget(group, Qt::AlignTop); + vlayout->addSpacing(10); + + tbhotkeys = new DTableWidget(this); + tbhotkeys->setEditTriggers(DTableWidget::EditTrigger::NoEditTriggers); + tbhotkeys->setSelectionBehavior(QAbstractItemView::SelectRows); + tbhotkeys->setSelectionMode(QAbstractItemView::ExtendedSelection); + tbhotkeys->setColumnCount(4); + tbhotkeys->setHorizontalHeaderLabels( + {tr("Enable"), tr("HotKey"), tr("Exec"), tr("Params")}); + tbhotkeys->setCornerButtonEnabled(true); + tbhotkeys->horizontalHeader()->setStretchLastSection(true); + + auto *menu = new DMenu(tbhotkeys); + QAction *a; + +#define AddMenuAction(title, slot) \ + a = new QAction(title, menu); \ + connect(a, &QAction::triggered, this, slot); \ + menu->addAction(a); + + AddMenuAction(tr("Enable"), [=] { this->enableSelectedHotkeys(true); }); + hkcmenu.append(a); + AddMenuAction(tr("Disable"), [=] { this->enableSelectedHotkeys(false); }); + hkcmenu.append(a); + menu->addSeparator(); + AddMenuAction(tr("Add"), &CenterWindow::on_addHotkey); + AddMenuAction(tr("Edit"), &CenterWindow::on_editHotkey); + hkcmenu.append(a); + AddMenuAction(tr("Remove"), &CenterWindow::on_removeHotkey); + hkcmenu.append(a); + AddMenuAction(tr("Clear"), &CenterWindow::on_clearHotkey); + + tbhotkeys->setContextMenuPolicy(Qt::CustomContextMenu); + connect(tbhotkeys, &DTabWidget::customContextMenuRequested, this, [=] { + auto flag = tbhotkeys->currentRow() >= 0; + for (auto item : hkcmenu) { + item->setEnabled(flag); + } + menu->popup(QCursor::pos()); + }); + connect(tbhotkeys, &DTableWidget::cellClicked, this, [=](int row, int) { + if (row < 0 || row >= scinfos.count()) + return; + auto b = tbhotkeys->item(row, 0)->checkState() == Qt::Checked; + scinfos[row].enabled = b; + manager->enableHotKey(row, b); + }); + connect(tbhotkeys, &DTableWidget::cellDoubleClicked, this, + [=](int row, int) { this->editTask(row); }); + + vlayout->addWidget(tbhotkeys); + tabs->addTab(w, tr("Hotkeys")); + + // ToolBox + w = new QWidget(this); + + tabs->addTab(w, tr("ToolBox")); + + // Plugins + w = new QWidget(this); + auto playout = new QHBoxLayout(w); + lwplgs = new DListWidget(w); + playout->addWidget(lwplgs); + tbplginfo = new DTextBrowser(w); + playout->addWidget(tbplginfo); + tabs->addTab(w, tr("Plugins")); + + // AboutAuthor + w = new QWidget(this); + auto alayout = new QVBoxLayout(w); + auto l = new DLabel(this); + l->setFixedSize(100, 100); + l->setScaledContents(true); + l->setPixmap(QPixmap(":/images/author.jpg")); + alayout->addWidget(l); + alayout->addSpacing(10); + alayout->addWidget(l, 0, Qt::AlignCenter); + + auto tb = new DTextBrowser(this); + tb->setSearchPaths(QStringList({":/", ":/images"})); + tb->setSource(QUrl("README.md"), QTextDocument::MarkdownResource); + tb->setOpenExternalLinks(true); + alayout->addWidget(tb); + + tabs->addTab(w, tr("About")); + + // Sponsor + w = new QWidget(this); + auto slayout = new QVBoxLayout(w); + slayout->addWidget(new DLabel(tr("ThanksForSponsor"), this), 0, + Qt::AlignCenter); + slayout->addSpacing(5); + + l = new DLabel(this); + l->setPixmap(QPixmap(":/sponsor.png")); + l->setScaledContents(true); + slayout->addWidget(l); + tabs->addTab(w, tr("Sponsor")); + + //初始化热键事件处理函数 + QObject::connect(manager, &AppManager::hotkeyTirggered, this, + [=](const QHotkey *, int index) { + auto &task = scinfos[index]; + this->runTask(task.process, task.params); + }); + QObject::connect(manager, &AppManager::hotkeyReleased, this, + [=](const QHotkey *, int) { + + }); + QObject::connect(manager, &AppManager::hotkeyEnableChanged, this, + [=](bool value, const QHotkey *, int index) { + tbhotkeys->item(index, 0)->setCheckState( + value ? Qt::Checked : Qt::Unchecked); + }); +} + +QStringList CenterWindow::parseCmdParams(QString str) { + static QRegularExpression regex("(\"[^\"]+\"|[^\\s\"]+)"); + QStringList args; + int off = 0; + while (true) { + auto match = regex.match(str, off); + if (!match.hasMatch()) { + break; + } + auto res = match.captured(); + if (res[0] == '\"') + res = res.replace("\"", ""); + if (res[0] == '\'') + res = res.replace("'", ""); + args << res; + off = match.capturedEnd(); + } + return args; +} + +bool CenterWindow::runTask(QString program, QString param) { + QMimeDatabase db; + + QFileInfo info(program); + auto absp = info.absoluteFilePath(); + + auto mt = db.mimeTypeForFile(absp); + auto n = mt.name(); + if (n == "application/x-executable") { + if (!pstart.startDetached(absp, parseCmdParams(param))) { + DMessageBox::critical(this, tr("runErr"), pstart.errorString()); + return false; + } + } else { + if (!QDesktopServices::openUrl(QUrl("file://" + absp))) { + DMessageBox::critical(this, tr("err"), tr("openErr")); + return false; + } + } + return true; +} + +void CenterWindow::editTask(int index) { + if (index < 0 || index >= scinfos.count()) + return; + auto &task = scinfos[index]; + ShortCutEditDialog d(task.enabled, task.seq, task.process, task.params); + if (d.exec()) { + auto res = d.getResult(); + auto wt = new QTableWidgetItem; + wt->setCheckState(res.enabled ? Qt::Checked : Qt::Unchecked); + tbhotkeys->setItem(index, 0, wt); + tbhotkeys->setItem(index, 1, new QTableWidgetItem(res.seq.toString())); + wt = new QTableWidgetItem(res.process); + wt->setToolTip(res.process); + tbhotkeys->setItem(index, 2, wt); + wt = new QTableWidgetItem(res.params); + wt->setToolTip(res.params); + tbhotkeys->setItem(index, 3, wt); + + task = res; + manager->editHotkey(index, res.seq); + } +} + +void CenterWindow::on_editHotkey() { this->editTask(tbhotkeys->currentRow()); } + +void CenterWindow::on_removeHotkey() { + auto selrows = tbhotkeys->selectionModel()->selectedRows(); + if (!selrows.length()) + return; + if (selrows.length() > 1) { + QVector nums; + for (auto &item : selrows) + nums.append(item.row()); + std::sort(nums.begin(), nums.end(), std::greater()); + for (auto item : nums) { + scinfos.removeAt(item); + manager->unregisterHotkey(item); + tbhotkeys->removeRow(item); + } + } else { + auto row = tbhotkeys->currentRow(); + scinfos.removeAt(row); + manager->unregisterHotkey(row); + tbhotkeys->removeRow(row); + } +} + +void CenterWindow::on_clearHotkey() { + scinfos.clear(); + manager->clearHotkey(); + tbhotkeys->setRowCount(0); + DMessageManager::instance()->sendMessage(this, ProgramIcon, + tr("ClearSuccess")); +} + +void CenterWindow::on_addHotkey() { + ShortCutEditDialog d; + if (d.exec()) { + auto res = d.getResult(); + auto index = tbhotkeys->rowCount(); + tbhotkeys->setRowCount(index + 1); + auto wt = new QTableWidgetItem; + wt->setCheckState(res.enabled ? Qt::Checked : Qt::Unchecked); + tbhotkeys->setItem(index, 0, wt); + tbhotkeys->setItem(index, 1, new QTableWidgetItem(res.seq.toString())); + wt = new QTableWidgetItem(res.process); + wt->setToolTip(res.process); + tbhotkeys->setItem(index, 2, wt); + wt = new QTableWidgetItem(res.params); + wt->setToolTip(res.params); + tbhotkeys->setItem(index, 3, wt); + + scinfos.append(res); + manager->registerHotkey(res.seq); + } +} + +void CenterWindow::enableSelectedHotkeys(bool enable) { + auto selrows = tbhotkeys->selectionModel()->selectedRows(); + for (auto &item : selrows) { + manager->enableHotKey(item.row(), enable); + } +} diff --git a/dialog/centerwindow.h b/dialog/centerwindow.h new file mode 100644 index 0000000..a6e348d --- /dev/null +++ b/dialog/centerwindow.h @@ -0,0 +1,57 @@ +#ifndef CENTERWINDOW_H +#define CENTERWINDOW_H + +#include "class/appmanager.h" +#include "utilies.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DWIDGET_USE_NAMESPACE + +class CenterWindow : public DMainWindow { + Q_OBJECT +public: + CenterWindow(DMainWindow *parent = nullptr); + +private: + QStringList parseCmdParams(QString str); + bool runTask(QString program, QString param); + void editTask(int index); + + void on_editHotkey(); + void on_removeHotkey(); + void on_clearHotkey(); + void on_addHotkey(); + void enableSelectedHotkeys(bool enable); + +private: + QList hkcmenu; + +private: + AppManager *manager; + + DTabWidget *tabs; + + DTableWidget *tbhotkeys; + DMenu *tbmenu; + + DListWidget *lwplgs; + DTextBrowser *tbplginfo; + + DCheckBox *cbauto; // 开机自启动 + + QProcess pstart; + +private: + QList scinfos; +}; + +#endif // CENTERWINDOW_H diff --git a/dialog/shortcuteditdialog.cpp b/dialog/shortcuteditdialog.cpp new file mode 100644 index 0000000..8c8b811 --- /dev/null +++ b/dialog/shortcuteditdialog.cpp @@ -0,0 +1,91 @@ +#include "shortcuteditdialog.h" +#include +#include +#include +#include + +ShortCutEditDialog::ShortCutEditDialog(bool enabled, QKeySequence seq, + QString process, QString params, + DMainWindow *parent) + : DDialog(parent), manager(AppManager::instance()) { + + // 处于编辑状态直接堵塞所有相应(屏蔽鼠标追踪和热键触发以防干扰) + manager->blockSignals(true); + + setWindowTitle(tr("HotkeyEdit")); + + cb = new DCheckBox(tr("Enabled"), this); + cb->setChecked(enabled); + addContent(cb); + addSpacing(10); + + addContent(new DLabel(tr("ShortCut"), this)); + addSpacing(5); + ksedit = new DKeySequenceEdit(this); + ksedit->setKeySequence(seq); + addContent(ksedit); + addSpacing(10); + + addContent(new DLabel(tr("FilePath"), this)); + addSpacing(5); + fcedit = new DFileChooserEdit(this); + fcedit->initDialog(); + fcedit->setText(process); + addContent(fcedit); + addSpacing(10); + + addContent(new DLabel(tr("Param"), this)); + addSpacing(5); + dledit = new DLineEdit(this); + dledit->setText(params); + addContent(dledit); + + addSpacing(20); + auto dbbox = new DDialogButtonBox( + DDialogButtonBox::Ok | DDialogButtonBox::Cancel, this); + connect(dbbox, &DDialogButtonBox::accepted, this, + &ShortCutEditDialog::on_accept); + connect(dbbox, &DDialogButtonBox::rejected, this, + &ShortCutEditDialog::on_reject); + auto key = QKeySequence(Qt::Key_Return); + auto s = new QShortcut(key, this); + connect(s, &QShortcut::activated, this, &ShortCutEditDialog::on_accept); + addContent(dbbox); +} + +ShortCutEditRes ShortCutEditDialog::getResult() { return res; } + +void ShortCutEditDialog::on_accept() { + res.enabled = cb->isChecked(); + res.seq = ksedit->keySequence(); + + if (res.seq == QKeySequence()) { + DMessageManager::instance()->sendMessage(this, ProgramIcon, + tr("NoHotkeySet")); + return; + } + + res.process = fcedit->text(); + + if (res.process.isEmpty()) { + DMessageManager::instance()->sendMessage(this, ProgramIcon, + tr("NoProcessSet")); + return; + } + + res.params = dledit->text(); + + manager->blockSignals(false); // 恢复能力 + done(1); +} + +void ShortCutEditDialog::on_reject() { + manager->blockSignals(false); // 恢复能力 + done(0); +} + +void ShortCutEditDialog::closeEvent(QCloseEvent *event) { + Q_UNUSED(event); + manager->blockSignals(false); // 恢复能力 + done(0); +} diff --git a/dialog/shortcuteditdialog.h b/dialog/shortcuteditdialog.h new file mode 100644 index 0000000..6a2f9be --- /dev/null +++ b/dialog/shortcuteditdialog.h @@ -0,0 +1,40 @@ +#ifndef SHORTCUTEDITDIALOG_H +#define SHORTCUTEDITDIALOG_H + +#include "utilies.h" + +#include "class/appmanager.h" +#include +#include +#include +#include +#include +#include + +DWIDGET_USE_NAMESPACE + +class ShortCutEditDialog : public DDialog { +public: + ShortCutEditDialog(bool enabled = true, QKeySequence seq = QKeySequence(), + QString process = QString(), QString params = QString(), + DMainWindow *parent = nullptr); + ShortCutEditRes getResult(); + +private: + void on_accept(); + void on_reject(); + +protected: + void closeEvent(QCloseEvent *event) override; + +private: + AppManager *manager; + + ShortCutEditRes res; + DCheckBox *cb; + DFileChooserEdit *fcedit; + DLineEdit *dledit; + DKeySequenceEdit *ksedit; +}; + +#endif // SHORTCUTEDITDIALOG_H diff --git a/dialog/toolboxwindow.cpp b/dialog/toolboxwindow.cpp new file mode 100644 index 0000000..46dae4c --- /dev/null +++ b/dialog/toolboxwindow.cpp @@ -0,0 +1,6 @@ +#include "toolboxwindow.h" + +ToolBoxWindow::ToolBoxWindow() +{ + +} diff --git a/dialog/toolboxwindow.h b/dialog/toolboxwindow.h new file mode 100644 index 0000000..0ff61fb --- /dev/null +++ b/dialog/toolboxwindow.h @@ -0,0 +1,11 @@ +#ifndef TOOLBOXWINDOW_H +#define TOOLBOXWINDOW_H + + +class ToolBoxWindow +{ +public: + ToolBoxWindow(); +}; + +#endif // TOOLBOXWINDOW_H \ No newline at end of file diff --git a/dialog/toolwindow.cpp b/dialog/toolwindow.cpp new file mode 100644 index 0000000..350315e --- /dev/null +++ b/dialog/toolwindow.cpp @@ -0,0 +1,66 @@ +#include "toolwindow.h" +#include "utilies.h" +#include + +#define GridSize 40 +#define GridTotal (GridSize * 3) + +ToolWindow::ToolWindow(DDialog *parent) : DDialog(parent) { + + setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint | + Qt::X11BypassWindowManagerHint); + setWindowFlag(Qt::FramelessWindowHint); + setFixedSize(GridTotal, GridTotal); + delete layout(); + + mlayout = new QGridLayout(this); + setLayout(mlayout); + mlayout->setMargin(1); + + for (int i = 0; i < 9; i++) { + auto lbl = new DIconButton(this); + lbl->setFixedSize(GridSize - 2, GridSize - 2); + lbl->setIconSize(QSize(GridSize / 2, GridSize / 2)); + auto in = std::div(i, 3); + mlayout->addWidget(lbl, in.quot, in.rem, Qt::AlignCenter); + lbls[i] = lbl; + } + + lbls[4]->setIcon(ICONRES("close")); +} + +void ToolWindow::setIcons(QVector icons) { + if (icons.count() != 8) + return; + for (int i = 0; i < 9; i++) { + if (i == 4) + continue; + if (i < 4) { + lbls[i]->setIcon(icons[i]); + } else { + lbls[i]->setIcon(icons[i - 1]); + } + } +} + +void ToolWindow::setIcon(int index, QIcon icon) { + // index 取值 0-8 ,但是索引 4 被保留不做处理,是正中间的按钮 + if (index < 0 || index == 4 || index > 8) + return; + lbls[index]->setIcon(icon); +} + +void ToolWindow::popup(QPoint pos) { + this->move(pos.x() - GridTotal / 2, pos.y() - GridTotal / 2); + show(); + setFocus(); +} + +void ToolWindow::sendMousePosUpdated() { + for (int x = 0; x < 3; x++) { + for (int y = 0; y < 3; y++) { + lbls[x * 3 + y]->setDown( + mlayout->cellRect(x, y).contains(mapFromGlobal(QCursor::pos()))); + } + } +} diff --git a/dialog/toolwindow.h b/dialog/toolwindow.h new file mode 100644 index 0000000..0f41f84 --- /dev/null +++ b/dialog/toolwindow.h @@ -0,0 +1,31 @@ +#ifndef TOOLWINDOW_H +#define TOOLWINDOW_H + +#include +#include +#include +#include + +DWIDGET_USE_NAMESPACE + +class ToolWindow : public DDialog { + Q_OBJECT +public: + ToolWindow(DDialog *parent = nullptr); + +public slots: + void setIcons(QVector icons); + void setIcon(int index, QIcon icon); + + void popup(QPoint pos); + + // 这个函数是用来提供 UI 鼠标交互和选择情况的,mouseMoveEvent 不触发就挺烦 + // 最后只能用自己封装好的鼠标 Hook 进行通知 + void sendMousePosUpdated(); + +private: + QGridLayout *mlayout; + DIconButton *lbls[9] = {nullptr}; +}; + +#endif // TOOLWINDOW_H diff --git a/images/author.jpg b/images/author.jpg new file mode 100755 index 0000000..7e66131 Binary files /dev/null and b/images/author.jpg differ diff --git a/images/close.png b/images/close.png new file mode 100755 index 0000000..f424e3f Binary files /dev/null and b/images/close.png differ diff --git a/images/logo.svg b/images/logo.svg new file mode 100644 index 0000000..d616e67 --- /dev/null +++ b/images/logo.svg @@ -0,0 +1,54 @@ + + + + + + + + diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..0235f92 --- /dev/null +++ b/main.cpp @@ -0,0 +1,96 @@ +#include "class/appmanager.h" +#include "class/wingapplication.h" +#include "dialog/centerwindow.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +DWIDGET_USE_NAMESPACE + +int main(int argc, char *argv[]) { + //解决 root/ubuntu 主题样式走形 + qputenv("XDG_CURRENT_DESKTOP", "Deepin"); + QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); + + // 程序内强制添加 -platformtheme + // deepin 参数喂给 Qt 让 Qt 正确使用 Deepin 主题修复各种奇怪样式问题 + QVector fakeArgs(argc + 2); + char fa1[] = "-platformtheme"; + char fa2[] = "deepin"; + fakeArgs[0] = argv[0]; + fakeArgs[1] = fa1; + fakeArgs[2] = fa2; + + for (int i = 1; i < argc; i++) + fakeArgs[i + 2] = argv[i]; + int fakeArgc = argc + 2; + + WingApplication a(fakeArgc, fakeArgs.data()); + QCoreApplication::setAttribute(Qt::AA_UseOpenGLES); + + // auto s = a.applicationDirPath() + "/lang/default.qm"; + // QTranslator translator; + // if (!translator.load(s)) { + // DMessageBox::critical(nullptr, "Error", "Error Loading Translation + // File!", + // DMessageBox::Ok); + // return -1; + // } + // a.installTranslator(&translator); + + a.setOrganizationName("WingCloud"); + a.setApplicationName(QObject::tr("WingTool")); + a.setApplicationVersion("1.0.0"); + + QIcon picon(":/images/logo.svg"); + a.setProductIcon(picon); + a.setProductName(QObject::tr("WingHexExplorer")); + a.setApplicationDescription("This is a dtk template application."); + + a.loadTranslator(); + a.setApplicationDisplayName("WingTool"); + + if (!a.setSingleInstance("com.Wingsummer.WingTool")) { + return -1; + } + + // 单例传参 + auto instance = DGuiApplicationHelper::instance(); + QObject::connect(instance, &DGuiApplicationHelper::newProcessInstance, + [=](qint64 pid, const QStringList &arguments) { + Q_UNUSED(pid); + Q_UNUSED(arguments); + }); + + // 保存程序的窗口主题设置 + DApplicationSettings as; + Q_UNUSED(as) + + // 初始化程序基础驱动 + AppManager manger; + + CenterWindow w; + + // 初始化托盘 + QSystemTrayIcon systray; + QMenu sysmenu; + systray.setContextMenu(&sysmenu); + systray.setIcon(picon); + systray.show(); + + QObject::connect(&systray, &QSystemTrayIcon::activated, + [&w](QSystemTrayIcon::ActivationReason reason) { + if (reason == QSystemTrayIcon::ActivationReason::Trigger) + w.show(); + }); + + Dtk::Widget::moveToCenter(&w); + + return a.exec(); +} diff --git a/plugin/iwingtoolplg.h b/plugin/iwingtoolplg.h new file mode 100644 index 0000000..7ed3a99 --- /dev/null +++ b/plugin/iwingtoolplg.h @@ -0,0 +1,106 @@ +#ifndef IWINGTOOLPLG_H +#define IWINGTOOLPLG_H + +#include +#include + +#define SDKVERSION 0 +#define GETPLUGINQM(name) \ + (QCoreApplication::applicationDirPath() + "/plglang/" + name) +#define PLUGINDIR (QCoreApplication::applicationDirPath() + "/plugin") + +#define WINGSUMMER "wingsummer" + +struct WingPluginInfo { + QString pluginName; + QString pluginAuthor; + uint pluginVersion; + QString puid; + QString pluginComment; +}; + +enum class MouseButton { + NoneButton, + LeftButton, + RightButton, + MiddleButton, + XButton_1, + XButton_2 +}; +Q_DECLARE_METATYPE(MouseButton) + +enum class MouseWheel { None, Up, Down, Left, Right }; +Q_DECLARE_METATYPE(MouseWheel) + +class IWingToolPlg : public QObject { + Q_OBJECT +public: + virtual int sdkVersion() = 0; + virtual QString signature() = 0; + QByteArray puid() { return GetPUID(this); } + virtual ~IWingToolPlg() {} + + virtual bool init(QList loadedplugin) = 0; + virtual void unload() = 0; + virtual QString pluginName() = 0; + virtual QString pluginAuthor() = 0; + virtual uint pluginVersion() = 0; + virtual QString pluginComment() = 0; + + static QByteArray GetPUID(IWingToolPlg *plugin) { + auto str = QString("%1%2%3%4") + .arg(WINGSUMMER) + .arg(plugin->pluginName()) + .arg(plugin->pluginAuthor()) + .arg(plugin->pluginVersion()); + return QCryptographicHash::hash(str.toLatin1(), QCryptographicHash::Md5); + } + + virtual QIcon pluginIcon() = 0; + +signals: + bool registerHotkey(QKeySequence &keyseq); + bool enableHotKey(int index, bool enabled = true); + bool unregisterHotkey(int index); + +public slots: + virtual void buttonPress(MouseButton btn, int x, int y) { + Q_UNUSED(btn); + Q_UNUSED(x); + Q_UNUSED(y); + } + virtual void buttonRelease(MouseButton btn, int x, int y) { + Q_UNUSED(btn); + Q_UNUSED(x); + Q_UNUSED(y); + } + virtual void clicked(int x, int y) { + Q_UNUSED(x); + Q_UNUSED(y); + } + virtual void doubleClicked(int x, int y) { + Q_UNUSED(x); + Q_UNUSED(y); + } + virtual void mouseWheel(MouseWheel direction) { Q_UNUSED(direction); } + virtual void mouseMove(int x, int y) { + Q_UNUSED(x); + Q_UNUSED(y); + } + + virtual void hotkeyTirggered(int index) { Q_UNUSED(index); } + virtual void hotkeyReleased(int index) { Q_UNUSED(index); } + virtual void hotkeyEnableChanged(bool value, int index) { + Q_UNUSED(value); + Q_UNUSED(index); + } + + virtual void selectionTextChanged(const QString &selectedText) { + Q_UNUSED(selectedText); + } +}; + +#define IWINGPLUGIN_INTERFACE_IID "com.wingsummer.iwingplugin" +Q_DECLARE_INTERFACE(WingPluginInfo, IWINGPLUGIN_INTERFACE_IID) + +#endif // IWINGTOOLPLG_H diff --git a/plugin/pluginsystem.cpp b/plugin/pluginsystem.cpp new file mode 100644 index 0000000..9270cbe --- /dev/null +++ b/plugin/pluginsystem.cpp @@ -0,0 +1,9 @@ +#include "pluginsystem.h" + +PluginSystem::PluginSystem(QObject *parent) : QObject(parent) {} + +PluginSystem::~PluginSystem() {} + +bool PluginSystem::LoadPlugin() {} + +void PluginSystem::UnloadPlugin() {} diff --git a/plugin/pluginsystem.h b/plugin/pluginsystem.h new file mode 100644 index 0000000..c4d3873 --- /dev/null +++ b/plugin/pluginsystem.h @@ -0,0 +1,35 @@ +#ifndef PLUGINSYSTEM_H +#define PLUGINSYSTEM_H + +#include "iwingtoolplg.h" +#include +#include + +class PluginSystem : public QObject { + Q_OBJECT +public: + enum class LP { + begin, + signature, + sdkVersion, + pluginName, + puid, + plugin2MessagePipe, + init, + }; + Q_ENUM(LP) + +public: + explicit PluginSystem(QObject *parent = nullptr); + + ~PluginSystem(); + bool LoadPlugin(); + void UnloadPlugin(); + + QList plugins(); + + void loadPlugin(QFileInfo filename); + IWingToolPlg *currentControlPlugin(); +}; + +#endif // PLUGINSYSTEM_H diff --git a/resources.qrc b/resources.qrc new file mode 100644 index 0000000..82b8ee8 --- /dev/null +++ b/resources.qrc @@ -0,0 +1,8 @@ + + + images/logo.svg + sponsor.png + images/author.jpg + images/close.png + + diff --git a/sponsor.png b/sponsor.png new file mode 100644 index 0000000..41829c9 Binary files /dev/null and b/sponsor.png differ diff --git a/utilies.h b/utilies.h new file mode 100644 index 0000000..bd97a36 --- /dev/null +++ b/utilies.h @@ -0,0 +1,17 @@ +#ifndef UTILIES_H +#define UTILIES_H + +#include +#include + +#define ProgramIcon QIcon(":/images/logo.svg") +#define ICONRES(name) QIcon(":/images/" name ".png") + +struct ShortCutEditRes { + bool enabled; + QKeySequence seq; + QString process; + QString params; +}; + +#endif // UTILIES_H