From 5f0619caa596b90eb004b59257cc4e4235fafba2 Mon Sep 17 00:00:00 2001 From: Michael Richters Date: Fri, 24 Nov 2017 11:30:04 -0600 Subject: [PATCH 01/72] Update .gitignore --- .gitignore/.gitignore | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 .gitignore/.gitignore diff --git a/.gitignore/.gitignore b/.gitignore/.gitignore new file mode 100644 index 00000000..259148fa --- /dev/null +++ b/.gitignore/.gitignore @@ -0,0 +1,32 @@ +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app From ff89e4ec8762fa43a7e61cad4184d86c91111c27 Mon Sep 17 00:00:00 2001 From: Michael Richters Date: Fri, 24 Nov 2017 11:30:58 -0600 Subject: [PATCH 02/72] Create LICENSE --- LICENSE | 674 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 674 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..94a9ed02 --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. From cf48742d7cfc11aeefdedfa0bbd01c30fc2392a7 Mon Sep 17 00:00:00 2001 From: Michael Richters Date: Fri, 24 Nov 2017 11:31:20 -0600 Subject: [PATCH 03/72] Create README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 00000000..5cbdd39e --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# Kaleidoscope-Qukey +Kaleidoscope "quantum key" plugin – two keycodes on a single key From d6d56cbd81eac0c0a5227a4dbea8e2ea611ab38f Mon Sep 17 00:00:00 2001 From: Michael Richters Date: Fri, 24 Nov 2017 14:55:37 -0600 Subject: [PATCH 04/72] Basic design overview --- README.md | 169 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 168 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 5cbdd39e..c7ac31df 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,169 @@ # Kaleidoscope-Qukey -Kaleidoscope "quantum key" plugin – two keycodes on a single key + +![status][st:experimental] [![Build Status][travis:image]][travis:status] + + [travis:image]: https://travis-ci.org/gedankenlab/Kaleidoscope-Qukey.svg?branch=master + [travis:status]: https://travis-ci.org/gedankenlab/Kaleidoscope-Qukey + + [st:stable]: https://img.shields.io/badge/stable-✔-black.svg?style=flat&colorA=44cc11&colorB=494e52 + [st:broken]: https://img.shields.io/badge/broken-X-black.svg?style=flat&colorA=e05d44&colorB=494e52 + [st:experimental]: https://img.shields.io/badge/experimental----black.svg?style=flat&colorA=dfb317&colorB=494e52 + +## Concept + +This Kaleidoscope plugin will allow you to overload keys on your +keyboard so that they produce one keycode (i.e. symbol) when tapped, +and a different keycode -- most likely a modifier (e.g. `shift` or +`alt`) when held. There are already two Kaleidoscop plugins that +provide this same functionality +([DualUse](https://github.com/keyboardio/Kaleidoscope-DualUse) and +[SpaceCadet](https://github.com/keyboardio/Kaleidoscope-SpaceCadet)), +but those plugins were designed primarily to allow you to overload +keys whose primary function is as a modifier, and use them to produce +printable keycodes when tapped. The `Qukey` design is different; it's +meant to overload letter keys that are usually tapped and let you use +them as alternate modifier keys (though the design is flexible enough +that any keycode can be the secondary one -- I just haven't thought of +a realistic use for that yet). + +## Design goals + +* I want users to be able to type at fast pace on `Qukey`s, without + accidentally triggering the secondary function, and preserving the + order of input keystrokes. + +* I want users to be able to rapidly invoke the secondary function of + a `Qukey` without having to wait for a timeout. + +* I want `Qukey`s to be useful as modifiers with other input devices + (e.g. a mouse), without having to wait a long time for a + timeout. I'm guessing that 200ms is acceptable, but not anything + longer than that. + +* I want physical *keys* on the keyboard to be defined independently + of each other. I don't want the plugin to act on *keycodes*; simply + translating one keycode to another. + +## Schrödinger's Key + +The idea is that a `Qukey` will work just like a particle in a +superposition of quantum states until some event causes the "waveform" +to "collapse" into just one of the two states. (The name "qukey" is a +reference to "qubit", a term used in quantum computing.) + +When a `Qukey` is pressed, its state will be indeterminate (like a +superposition of quantum states); no keycode for that key will be +transmitted to the host until some other event (or sequence of events) +causes the state to be decided. Once the state is decided, the `Qukey` +will stay in that state until the key is released. The possible +triggers for "collapsing" the state of a `Qukey` are: + +* Timeout + +* Release of the `qukey` + +* Press and subsequent release of another key + +### Timeout + +When a `qukey` times out, it falls into its secondary state (probably +a modifier).The timeout should be fairly short ­ just a bit longer +than the longest "reasonable" time of a tap, such that you very rarely +(ideally never) hold a `qukey` long enough to trigger the secondary +function when intending a tap, but short enough that you don't feel a +need to wait a while to use it with a mouse. + +### Release of `qukey` + +If the `qukey` is released before it times out, and before any other +keys are pressed, it falls into its primary state. This is slightly +different from the timeout case, because we then add the `qukey`'s +primary keycode to the HID report when the key toggles off (rather +than when it toggles on, which is the behaviour of a normal key). It +will get cleared at the end of the cycle, but there's still no need to +send any extra HID reports. + +### Interactions with subsequent keys + +This is where things get tricky. Because fast typists might have +multiple keys pressed simultaneously, we need to keep track of a +sequence of subsequent keypresses, starting with the first `qukey` to +toggle on. Basically, if any subsequent key is released before a +`qukey` that was pressed before it, that `qukey` should fall into its +secondary state (e.g. a modifier). + +In order to do this, we need to suppress the output of any subsequent +keys, as well as the `qukey`. Basically, this means storing a list of +keys that have been pressed, but which have not affected the report +yet. This also means that when a key in that list toggles off, every +`qukey` preceding it in the list has its state decided. + +When a subsequent key is released, we now need to send a series of HID +reports to preserve the order that the keypresses were detected by the +keyboard. To do that, we add the one keycode at a time to the report +and send it, until we reach the next `qukey` in the list that has an +indeterminate state. + +The really tricky part is that the key *releases* might come in any +order, so if we have a sequence that goes like this, we could get into +trouble: + +1. press `qk(A,shift)` +2. press `B` +3. press `C` +4. press `qk(D,ctrl)` +5. press `space` +6. release `B` + +In this case, `B` is released after the second `qukey` was pressed, +but I don't think it's actually a problem. Maybe. + +We might also send a series of reports when a `qukey` times out, of +course, but this is much more straightforward. + +### Another way to address overlap + +An alternative method is to only allow the `qukey` to fall into its +secondary (modifier) state if there is only one non-`qukey` press +following. So, in the previous example, once step 3 was reached, the +`qukey` would become `A`. I don't think this works as well, but it +would err more on the side of defaulting to the primary key, which +would mean fewer unintended modifiers while typing, and enforce +(slightly) more deliberate usage to get the secondary keycode. This +would make for another interesting possibility, though divergent from +the idea that I started with ­ the state could change while the key is +pressed: + +1. press `qk(A,shift)` +2. press `B` +3. press `C` -> output: `abc` +4. release `B` -> output: `shift` held? + +It wouldn't require any additional data structures, so the algorithm +to use could be user-configurable. + +## Data structures + +I want to store a table, indexed by key id (layer, row & column). Each +entry would be an object containing: + +* primary keycode (or `Key` object) +* secondary keycode (or `Key` object) +* time limit (set when key toggles on, based on timeout) +* flags (bitfield ­ maybe useful) + +I think I need the flags byte in order to signal which state the +`qukey` has once its state has collapsed, but maybe I can get away +with just using the time limit value. I could set that to zero (or +maybe some other constant?) to signal that the `qukey` is in its +primary state, and if the state collapses to secondary before the +timeout, just reset the time limit to `millis()`. Or maybe I never +need that, because it should only send the primary keycode as a "tap" +in a single report. + +In addition, I need to store a list of keypresses, ideally in +something like `std::queue`, though I think that's not available to +Arduino (it looks like someone has written a `QueueArray` library, +though). This would just be a FIFO buffer of key ids (row & column ­ +maybe layer, too, but that might even be undesirable, because we might +try to expand `Qukey` to allow layer switching, too). \ No newline at end of file From ecc52c86de15fd532c53d8aa6b8d5d63d6573e30 Mon Sep 17 00:00:00 2001 From: Michael Richters Date: Fri, 24 Nov 2017 15:01:40 -0600 Subject: [PATCH 05/72] Pluralized library name --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c7ac31df..229b76bd 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Kaleidoscope-Qukey +# Kaleidoscope-Qukeys ![status][st:experimental] [![Build Status][travis:image]][travis:status] From ac354318d363d09f8df5bfe96b503377aa96eef6 Mon Sep 17 00:00:00 2001 From: Michael Richters Date: Fri, 24 Nov 2017 18:38:20 -0600 Subject: [PATCH 06/72] Basic skeleton code --- Makefile | 14 ++++++++++++++ library.properties | 10 ++++++++++ src/Kaleidoscope-Qukeys.h | 21 +++++++++++++++++++++ src/Kaleidoscope/Qukeys.cpp | 35 +++++++++++++++++++++++++++++++++++ src/Kaleidoscope/Qukeys.h | 37 +++++++++++++++++++++++++++++++++++++ 5 files changed, 117 insertions(+) create mode 100644 Makefile create mode 100644 library.properties create mode 100644 src/Kaleidoscope-Qukeys.h create mode 100644 src/Kaleidoscope/Qukeys.cpp create mode 100644 src/Kaleidoscope/Qukeys.h diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..8f830f44 --- /dev/null +++ b/Makefile @@ -0,0 +1,14 @@ +# This stub makefile for a Kaleidoscope plugin pulls in +# all targets from the Kaleidoscope-Plugin library + +UNAME_S := $(shell uname -s) + +ifeq ($(UNAME_S),Darwin) +SKETCHBOOK_DIR ?= $(HOME)/Documents/Arduino/ +else +SKETCHBOOK_DIR ?= $(HOME)/Arduino +endif + +BOARD_HARDWARE_PATH ?= $(SKETCHBOOK_DIR)/hardware +KALEIDOSCOPE_PLUGIN_MAKEFILE_DIR ?= keyboardio/avr/build-tools/makefiles/ +include $(BOARD_HARDWARE_PATH)/$(KALEIDOSCOPE_PLUGIN_MAKEFILE_DIR)/rules.mk diff --git a/library.properties b/library.properties new file mode 100644 index 00000000..6b2bf9ab --- /dev/null +++ b/library.properties @@ -0,0 +1,10 @@ +name=Kaleidoscope-Qukeys +version=0.0.0 +author=Michael Richters +maintainer=Michael Richters +sentence=Assign two keycodes to a single key. +paragraph=See README for more information. +category=Communication +url=https://github.com/gedankenlab/Kaleidoscope-Qukeys +architectures=avr +dot_a_linkage=true diff --git a/src/Kaleidoscope-Qukeys.h b/src/Kaleidoscope-Qukeys.h new file mode 100644 index 00000000..3a0e70f1 --- /dev/null +++ b/src/Kaleidoscope-Qukeys.h @@ -0,0 +1,21 @@ +/* -*- mode: c++ -*- + * Kaleidoscope-Qukeys -- Assign two keycodes to a single key + * Copyright (C) 2017 Michael Richters + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include diff --git a/src/Kaleidoscope/Qukeys.cpp b/src/Kaleidoscope/Qukeys.cpp new file mode 100644 index 00000000..9d6353c1 --- /dev/null +++ b/src/Kaleidoscope/Qukeys.cpp @@ -0,0 +1,35 @@ +/* -*- mode: c++ -*- + * Kaleidoscope-Qukeys -- Assign two keycodes to a single key + * Copyright (C) 2017 Michael Richters + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#ifdef ARDUINO_VIRTUAL +#define debug_print(...) printf(__VA_ARGS__) +#else +#define debug_print(...) +#endif + + +namespace kaleidoscope { + +// constructor +Qukeys::Qukeys(void) { +} + +} diff --git a/src/Kaleidoscope/Qukeys.h b/src/Kaleidoscope/Qukeys.h new file mode 100644 index 00000000..92da0b98 --- /dev/null +++ b/src/Kaleidoscope/Qukeys.h @@ -0,0 +1,37 @@ +/* -*- mode: c++ -*- + * Kaleidoscope-Qukeys -- Assign two keycodes to a single key + * Copyright (C) 2017 Michael Richters + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include + + +namespace kaleidoscope { + +class Qukeys : public KaleidoscopePlugin { + + public: + Qukeys(void); + + void begin(void) final; + + private: + +} + +} From bba6cf878f124d3f9844d5df2becfd4ecc156754 Mon Sep 17 00:00:00 2001 From: Michael Richters Date: Sun, 26 Nov 2017 22:25:52 -0600 Subject: [PATCH 07/72] Empty example sketch --- examples/Qukeys/Qukeys.ino | 1 + 1 file changed, 1 insertion(+) create mode 100644 examples/Qukeys/Qukeys.ino diff --git a/examples/Qukeys/Qukeys.ino b/examples/Qukeys/Qukeys.ino new file mode 100644 index 00000000..3f53c8e1 --- /dev/null +++ b/examples/Qukeys/Qukeys.ino @@ -0,0 +1 @@ +// -*- mode: c++ -*- \ No newline at end of file From 29c243eda55646cf5ce01b84fa0b8177e00affe1 Mon Sep 17 00:00:00 2001 From: Michael Richters Date: Tue, 28 Nov 2017 13:47:22 -0600 Subject: [PATCH 08/72] Added addr.h with helper functions This contains helper functions for converting (row,col) coordinates to single-byte addresses, assuming the keyboard has fewer than 256 keys. --- src/addr.h | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 src/addr.h diff --git a/src/addr.h b/src/addr.h new file mode 100644 index 00000000..048b4de9 --- /dev/null +++ b/src/addr.h @@ -0,0 +1,38 @@ +/* -*- mode: c++ -*- + * Kaleidoscope-Qukeys -- Assign two keycodes to a single key + * Copyright (C) 2017 Michael Richters + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include + +// Helper functions for converting between separate (row,col) +// coordinates and a single-byte key number (addr). This works as long +// as the keyboard has fewer than 256 keys. +namespace kaleidoscope { + namespace addr { + inline uint8_t row(uint8_t key_addr) { + return (key_addr / COLS); + } + inline uint8_t col(uint8_t key_addr) { + return (key_addr % COLS); + } + inline uint8_t addr(uint8_t row, uint8_t col) { + return ((row * COLS) + col); + } + } // namespace addr { +} // namespace kaleidoscope { From 5bfe5c07eb004ba77ae281f28c5c83a4fa69d9ed Mon Sep 17 00:00:00 2001 From: Michael Richters Date: Tue, 28 Nov 2017 13:49:10 -0600 Subject: [PATCH 09/72] Version 1 of the Qukeys implementation I think everything is there. This probably doesn't even compile yet, and it certainly hasn't been debugged. --- examples/Qukeys/Qukeys.ino | 46 ++++++++- src/Kaleidoscope/Qukeys.cpp | 187 +++++++++++++++++++++++++++++++++++- src/Kaleidoscope/Qukeys.h | 80 ++++++++++++++- 3 files changed, 305 insertions(+), 8 deletions(-) diff --git a/examples/Qukeys/Qukeys.ino b/examples/Qukeys/Qukeys.ino index 3f53c8e1..b1caf1c0 100644 --- a/examples/Qukeys/Qukeys.ino +++ b/examples/Qukeys/Qukeys.ino @@ -1 +1,45 @@ -// -*- mode: c++ -*- \ No newline at end of file +// -*- mode: c++ -*- + +#include +#include + +const Key keymaps[][ROWS][COLS] PROGMEM = { + [0] = KEYMAP_STACKED + ( + Key_NoKey, Key_1, Key_2, Key_3, Key_4, Key_5, Key_NoKey, + Key_Backtick, Key_Q, Key_W, Key_E, Key_R, Key_T, Key_Tab, + Key_PageUp, Key_A, Key_S, Key_D, Key_F, Key_G, + Key_PageDown, Key_Z, Key_X, Key_C, Key_V, Key_B, Key_Escape, + + Key_LeftControl, Key_Backspace, Key_LeftGui, Key_LeftShift, + ___, + + Key_skip, Key_6, Key_7, Key_8, Key_9, Key_0, Key_skip, + Key_Enter, Key_Y, Key_U, Key_I, Key_O, Key_P, Key_Equals, + Key_H, Key_J, Key_K, Key_L, Key_Semicolon, Key_Quote, + Key_skip, Key_N, Key_M, Key_Comma, Key_Period, Key_Slash, Key_Minus, + + Key_RightShift, Key_RightAlt, Key_Spacebar, Key_RightControl, + ___), +}; + + +// Define the Qukeys map +QUKEYS( + Qukey(QWERTY, 2, 1, Key_LeftShift), + Qukey(QWERTY, 2, 2, Key_LeftControl), + Qukey(QWERTY, 2, 3, Key_LeftAlt), + Qukey(QWERTY, 2, 4, Key_LeftGui) + ) + +void setup() { + // Use Qukeys + Kaleidoscope.use(&Qukeys); + Qukeys.init(qukey_list, qukey_count); + + Kaleidoscope.setup(); +} + +void loop() { + Kaleidoscope.loop(); +} diff --git a/src/Kaleidoscope/Qukeys.cpp b/src/Kaleidoscope/Qukeys.cpp index 9d6353c1..48f9942d 100644 --- a/src/Kaleidoscope/Qukeys.cpp +++ b/src/Kaleidoscope/Qukeys.cpp @@ -28,8 +28,191 @@ namespace kaleidoscope { -// constructor -Qukeys::Qukeys(void) { +Qukey::Qukey(int8_t layer, byte row, byte col, Key alt_keycode) { + this->layer = layer; + this->addr = addr::addr(row, col); + this->alt_keycode = alt_keycode; + this->state = QUKEY_STATE_UNDETERMINED; } +Qukeys::Qukeys() {} + +Qukeys::init(Qukey *qukeys, uint8_t qukeys_count) { + qukeys_ = qukeys; + qukeys_count_ = qukeys_count; +} + +int8_t Qukeys::lookupQukey(uint8_t key_addr) { + if (key_addr == QUKEY_UNKNOWN_ADDR) + return QUKEY_NOT_FOUND; + for (int8_t i; i < qukeys_count_; i++) { + Qukey qukey = qukeys_[i]; + if (qukey.addr == key_addr) { + byte row = addr::row(key_addr); + byte col = addr::col(key_addr); + if ((qukey.layer == QUKEY_ALL_LAYERS) || + (qukey.layer == Layer.lookupActiveLayer(row, col))) { + return i; + } + } + } + return QUKEY_NOT_FOUND; +} + +void Qukeys::enqueue(uint8_t key_addr) { + if (key_queue_length_ == QUKEYS_QUEUE_MAX) { + flushKey(QUKEY_STATE_PRIMARY); + } + key_queue_[key_queue_length_].addr = key_addr; + key_queue_[key_queue_length_].flush_time = millis() + time_limit_; + key_queue_length_++; } + +int8_t Qukeys::searchQueue(uint8_t key_addr) { + for (int8_t i = 0; i < key_queue_length_; i++) { + if (key_queue_[i].addr == key_addr) + return i; + } + return -1; +} + +// flush a single entry from the head of the queue +void Qukeys::flushKey(int8_t state) { + int8_t qukey_index = Qukeys.lookupQukey(key_addr); + if (qukey_index != QUKEY_NOT_FOUND) { + qukeys_[qukey_index].state = state; + } + byte row = addr::row(key_queue[0].addr); + byte col = addr::col(key_queue[0].addr); + Key keycode = Key_NoKey; + if (state == QUKEY_STATE_ALTERNATE && qukey_index != QUKEY_NOT_FOUND) { + keycode = qukeys_[qukey_index].alt_keycode; + } else { + keycode = Layer.lookup(row, col); + } + // Since we're in the middle of the key scan, we don't necessarily + // have a full HID report, and we don't want to accidentally turn + // off keys that the scan hasn't reached yet, so we force the + // current report to be the same as the previous one, then proceed + hid::copyPrevKeyboardReport(); + // Instead of just calling pressKey here, we start processing the + // key again, as if it was just pressed, and mark it as injected, so + // we can ignore it and don't start an infinite loop. It would be + // nice if we could use key_state to also indicate which plugin + // injected the key. + handleKeySwitchEvent(keycode, row, col, IS_PRESSED | INJECTED); + // Now we send the report (if there were any changes) + hid::sendKeyboardReport(); + + // Shift the queue, so key_queue[0] is always the first key that gets processed + for (byte i = 0; i < key_queue_length_; i++) { + key_queue_[i] = key_queue_[i + 1]; + } + key_queue_length_--; +} + +void Qukeys::flushQueue(int8_t state, int8_t index) { + for (int8_t i = 0; i <= index; i++) { + if (key_queue_length_ == 0) + break; + flushKey(state); + } +} + +Key Qukeys::keyScanHook(Key mapped_key, byte row, byte col, uint8_t key_state) { + + // If Qukeys is turned off, continue to next plugin + if (!active) + return mapped_key; + + // If the key was injected (from the queue being flushed), continue to next plugin + if (key_state & INJECTED) + return mapped_key; + + // If the key isn't active, and didn't just toggle off, continue to next plugin + if (!keyIsPressed(key_state) && !keyWasPressed(key_state)) + return mapped_key; + + // get key addr & qukey (if any) + uint8_t key_addr = addr::addr(row, col); + int8_t qukey_index = Qukeys.lookupQukey(key_addr); + + // If the key was just pressed: + if (keyToggledOn(key_state)) { + // I think I may need to call maskKey() somewhere here, but I'm not sure + if (key_queue_length) { + enqueue(key_addr); + } else { + if (qukey_index == QUKEY_NOT_FOUND) + return mapped_key; + enqueue(key_addr); + } + } + + // In all other cases, we need to know if the key is queued already + int8_t queue_index = searchQueue(key_addr); + + // If the key was just released: + if (keyToggledOff(key_state)) { + if (queue_index == QUKEY_NOT_FOUND) + return mapped_key; + flushQueue(QUKEY_STATE_ALTERNATE, queue_index); + return Key_NoKey; + } + + // Otherwise, the key is still pressed + + // If the key is not a qukey: + if (qukey_index == QUKEY_NOT_FOUND) { + // If the key was pressed before the keys in the queue: + if (queue_index == QUKEY_NOT_FOUND) { + return mapped_key; + } else { + // suppress this keypress; it's still in the queue + return Key_NoKey; + } + } + + // qukeys that have already decided their keycode + if (qukeys_[qukey_index].state == QUKEY_STATE_PRIMARY) + return mapped_key; + if (qukeys_[qukey_index].state == QUKEY_STATE_ALTERNATE) + return qukeys_[qukey_index].alt_keycode; + // else state is undetermined; block. I could check timeouts here, + // but I'd rather do that in the pre-report hook + return Key_NoKey; +} + +void preReportHook(void) { + // If the qukey has been held longer than the time limit, set its + // state to the alternate keycode and add it to the report + uint32_t current_time = millis(); + for (int8_t i = 0; i < key_queue_length_; i++) { + if (current_time > key_queue_[i].flush_time) { + flushKey(QUKEY_STATE_ALTERNATE); + } else { + break; + } + } +} + +void loopHook(bool post_clear) { + if (!post_clear) + return preReportHook(); +} + +void Qukeys::begin() { + // initializing the key_queue seems unnecessary, actually + for (int8_t i = 0; i < QUKEYS_QUEUE_MAX; i++) { + key_queue_[i].addr = QUKEY_UNKNOWN_ADDR; + key_queue_[i].flush_time = 0; + } + key_queue_length_ = 0; + + Kaleidoscope.useEventHandlerHook(keyScanHook); + Kaleidoscope.useLoopHook(loopHook); +} + +} // namespace kaleidoscope { + +kaleidoscope::Qukeys Qukeys; diff --git a/src/Kaleidoscope/Qukeys.h b/src/Kaleidoscope/Qukeys.h index 92da0b98..ccb27898 100644 --- a/src/Kaleidoscope/Qukeys.h +++ b/src/Kaleidoscope/Qukeys.h @@ -19,19 +19,89 @@ #pragma once #include +#include +// Maximum number of qukeys allowed ­ not actually used +#define QUKEYS_MAX 64 +// Maximum length of the pending queue +#define QUKEYS_QUEUE_MAX 8 + +// Maybe it's better to use an enum for these state values? +#define QUKEY_STATE_UNDETERMINED 0 +#define QUKEY_STATE_PRIMARY 1 +#define QUKEY_STATE_ALTERNATE -1 + +// Initialization addr value for empty key_queue. This seems to be +// unnecessary, because we rely on keeping track of the lenght of the +// queue, anyway. +#define QUKEY_UNKNOWN_ADDR 0xFF +// Value to return when no match is found in Qukeys.dict. A successful +// match returns an index in the array, so this must be negative. Also +// used for failed search of the key_queue. +#define QUKEY_NOT_FOUND -1 +// Wildcard value; this matches any layer +#define QUKEY_ALL_LAYERS -1 namespace kaleidoscope { -class Qukeys : public KaleidoscopePlugin { +// Data structure for an individual qukey +struct Qukey { + public: + Qukey(void) {} + Qukey(int8_t layer, byte row, byte col, Key alt_keycode); + + int8_t layer; + uint8_t addr; + Key alt_keycode; + int8_t state; +}; + +// Data structure for an entry in the key_queue +struct QueueItem { + uint8_t addr; // keyswitch coordinates + uint32_t flush_time; // time past which a qukey gets flushed +}; +// The plugin itself +class Qukeys : public KaleidoscopePlugin { + // I could use a bitfield to get the state values, but then we'd + // have to check the key_queue (there are three states). Or use a + // second bitfield for the indeterminite state. Using a bitfield + // would enable storing the qukey list in PROGMEM, but I don't know + // if the added complexity is worth it. public: Qukeys(void); - void begin(void) final; + static void begin(void) final; + static void activate(void) { + active = true; + } + static void deactivate(void) { + active = false; + } + static int8_t lookupQukey(uint8_t key_addr); private: - -} + static Qukey * qukeys_; + static uint8_t qukeys_count_; + + static bool active_; + static uint16_t time_limit_; + static queue_item_t key_queue_[QUKEYS_QUEUE_MAX]; + static uint8_t key_queue_length_; + + static Key keyScanHook(Key mapped_key, byte row, byte col, uint8_t key_state); + static void preReportHook(void); + static void postReportHook(void) {} + static void loopHook(bool post_clear); +}; + +} // namespace kaleidoscope { + +extern kaleidoscope::Qukeys Qukeys; -} +// macro for use in sketch file to simplify definition of qukeys +#define QUKEYS(qukey_defs...) \ + static kaleidoscope::Qukey qukeys[] = { qukey_defs... }; \ + uint8_t qukeys_count = sizeof(qukeys) / sizeof(kaleidoscope::Qukey); \ + Qukeys.init(qukeys, qukeys_count); From 721baeca78a9979994257aab2c05f26954a1ed53 Mon Sep 17 00:00:00 2001 From: Michael Richters Date: Tue, 28 Nov 2017 14:45:31 -0600 Subject: [PATCH 10/72] Fixed lots of compilation errors --- examples/Qukeys/Qukeys.ino | 23 ++++++++++++++-------- src/Kaleidoscope/Qukeys.cpp | 39 +++++++++++++++++++++++-------------- src/Kaleidoscope/Qukeys.h | 23 ++++++++++++++-------- src/addr.h | 22 ++++++++++----------- 4 files changed, 65 insertions(+), 42 deletions(-) diff --git a/examples/Qukeys/Qukeys.ino b/examples/Qukeys/Qukeys.ino index b1caf1c0..f819a875 100644 --- a/examples/Qukeys/Qukeys.ino +++ b/examples/Qukeys/Qukeys.ino @@ -23,19 +23,26 @@ const Key keymaps[][ROWS][COLS] PROGMEM = { ___), }; - // Define the Qukeys map -QUKEYS( - Qukey(QWERTY, 2, 1, Key_LeftShift), - Qukey(QWERTY, 2, 2, Key_LeftControl), - Qukey(QWERTY, 2, 3, Key_LeftAlt), - Qukey(QWERTY, 2, 4, Key_LeftGui) - ) +// QUKEYS( +// Qukey(0, 2, 1, Key_LeftShift), +// Qukey(0, 2, 2, Key_LeftControl), +// Qukey(0, 2, 3, Key_LeftAlt), +// Qukey(0, 2, 4, Key_LeftGui) +// ) void setup() { // Use Qukeys Kaleidoscope.use(&Qukeys); - Qukeys.init(qukey_list, qukey_count); + //kaleidoscope::Qukeys.init(qukeys, count); + + kaleidoscope::Qukey qukeys[] = { + kaleidoscope::Qukey(0, 2, 1, Key_LeftShift), + kaleidoscope::Qukey(0, 2, 2, Key_LeftControl) + }; + uint8_t count = sizeof(qukeys) / sizeof(kaleidoscope::Qukey); + Qukeys.qukeys_ = qukeys; + Qukeys.qukeys_count_ = count; Kaleidoscope.setup(); } diff --git a/src/Kaleidoscope/Qukeys.cpp b/src/Kaleidoscope/Qukeys.cpp index 48f9942d..278d6996 100644 --- a/src/Kaleidoscope/Qukeys.cpp +++ b/src/Kaleidoscope/Qukeys.cpp @@ -16,6 +16,7 @@ * along with this program. If not, see . */ +#include #include #include @@ -35,17 +36,25 @@ Qukey::Qukey(int8_t layer, byte row, byte col, Key alt_keycode) { this->state = QUKEY_STATE_UNDETERMINED; } -Qukeys::Qukeys() {} +Qukey * Qukeys::qukeys_; +uint8_t Qukeys::qukeys_count_ = 0; +bool Qukeys::active_ = true; +uint16_t Qukeys::time_limit_ = 200; +QueueItem Qukeys::key_queue_[QUKEYS_QUEUE_MAX] = {}; +uint8_t Qukeys::key_queue_length_ = 0; -Qukeys::init(Qukey *qukeys, uint8_t qukeys_count) { - qukeys_ = qukeys; - qukeys_count_ = qukeys_count; -} +// Empty constructor; nothing is stored at the instance level +Qukeys::Qukeys(void) {} + +// Qukeys::init(Qukey *qukeys, uint8_t qukeys_count) { +// qukeys_ = qukeys; +// qukeys_count_ = qukeys_count; +// } int8_t Qukeys::lookupQukey(uint8_t key_addr) { if (key_addr == QUKEY_UNKNOWN_ADDR) return QUKEY_NOT_FOUND; - for (int8_t i; i < qukeys_count_; i++) { + for (int8_t i = 0; i < qukeys_count_; i++) { Qukey qukey = qukeys_[i]; if (qukey.addr == key_addr) { byte row = addr::row(key_addr); @@ -78,12 +87,12 @@ int8_t Qukeys::searchQueue(uint8_t key_addr) { // flush a single entry from the head of the queue void Qukeys::flushKey(int8_t state) { - int8_t qukey_index = Qukeys.lookupQukey(key_addr); + int8_t qukey_index = lookupQukey(key_queue_[0].addr); if (qukey_index != QUKEY_NOT_FOUND) { qukeys_[qukey_index].state = state; } - byte row = addr::row(key_queue[0].addr); - byte col = addr::col(key_queue[0].addr); + byte row = addr::row(key_queue_[0].addr); + byte col = addr::col(key_queue_[0].addr); Key keycode = Key_NoKey; if (state == QUKEY_STATE_ALTERNATE && qukey_index != QUKEY_NOT_FOUND) { keycode = qukeys_[qukey_index].alt_keycode; @@ -100,7 +109,7 @@ void Qukeys::flushKey(int8_t state) { // we can ignore it and don't start an infinite loop. It would be // nice if we could use key_state to also indicate which plugin // injected the key. - handleKeySwitchEvent(keycode, row, col, IS_PRESSED | INJECTED); + handleKeyswitchEvent(keycode, row, col, IS_PRESSED | INJECTED); // Now we send the report (if there were any changes) hid::sendKeyboardReport(); @@ -122,7 +131,7 @@ void Qukeys::flushQueue(int8_t state, int8_t index) { Key Qukeys::keyScanHook(Key mapped_key, byte row, byte col, uint8_t key_state) { // If Qukeys is turned off, continue to next plugin - if (!active) + if (!active_) return mapped_key; // If the key was injected (from the queue being flushed), continue to next plugin @@ -135,12 +144,12 @@ Key Qukeys::keyScanHook(Key mapped_key, byte row, byte col, uint8_t key_state) { // get key addr & qukey (if any) uint8_t key_addr = addr::addr(row, col); - int8_t qukey_index = Qukeys.lookupQukey(key_addr); + int8_t qukey_index = lookupQukey(key_addr); // If the key was just pressed: if (keyToggledOn(key_state)) { // I think I may need to call maskKey() somewhere here, but I'm not sure - if (key_queue_length) { + if (key_queue_length_) { enqueue(key_addr); } else { if (qukey_index == QUKEY_NOT_FOUND) @@ -183,7 +192,7 @@ Key Qukeys::keyScanHook(Key mapped_key, byte row, byte col, uint8_t key_state) { return Key_NoKey; } -void preReportHook(void) { +void Qukeys::preReportHook(void) { // If the qukey has been held longer than the time limit, set its // state to the alternate keycode and add it to the report uint32_t current_time = millis(); @@ -196,7 +205,7 @@ void preReportHook(void) { } } -void loopHook(bool post_clear) { +void Qukeys::loopHook(bool post_clear) { if (!post_clear) return preReportHook(); } diff --git a/src/Kaleidoscope/Qukeys.h b/src/Kaleidoscope/Qukeys.h index ccb27898..958ae542 100644 --- a/src/Kaleidoscope/Qukeys.h +++ b/src/Kaleidoscope/Qukeys.h @@ -72,22 +72,27 @@ class Qukeys : public KaleidoscopePlugin { public: Qukeys(void); - static void begin(void) final; + //static void init(Qukey *qukeys, uint8_t qukeys_count); + void begin(void) final; static void activate(void) { - active = true; + active_ = true; } static void deactivate(void) { - active = false; + active_ = false; } static int8_t lookupQukey(uint8_t key_addr); + static void enqueue(uint8_t key_addr); + static int8_t searchQueue(uint8_t key_addr); + static void flushKey(int8_t state); + static void flushQueue(int8_t state, int8_t index); - private: static Qukey * qukeys_; static uint8_t qukeys_count_; + private: static bool active_; static uint16_t time_limit_; - static queue_item_t key_queue_[QUKEYS_QUEUE_MAX]; + static QueueItem key_queue_[QUKEYS_QUEUE_MAX]; static uint8_t key_queue_length_; static Key keyScanHook(Key mapped_key, byte row, byte col, uint8_t key_state); @@ -102,6 +107,8 @@ extern kaleidoscope::Qukeys Qukeys; // macro for use in sketch file to simplify definition of qukeys #define QUKEYS(qukey_defs...) \ - static kaleidoscope::Qukey qukeys[] = { qukey_defs... }; \ - uint8_t qukeys_count = sizeof(qukeys) / sizeof(kaleidoscope::Qukey); \ - Qukeys.init(qukeys, qukeys_count); + namespace kaleidoscope { \ + Qukey qukeys[] = { qukey_defs... }; \ + uint8_t qukeys_count = sizeof(qukeys) / sizeof(Qukey); \ + Qukeys.init(qukeys, qukeys_count); \ + } diff --git a/src/addr.h b/src/addr.h index 048b4de9..156ce8e8 100644 --- a/src/addr.h +++ b/src/addr.h @@ -24,15 +24,15 @@ // coordinates and a single-byte key number (addr). This works as long // as the keyboard has fewer than 256 keys. namespace kaleidoscope { - namespace addr { - inline uint8_t row(uint8_t key_addr) { - return (key_addr / COLS); - } - inline uint8_t col(uint8_t key_addr) { - return (key_addr % COLS); - } - inline uint8_t addr(uint8_t row, uint8_t col) { - return ((row * COLS) + col); - } - } // namespace addr { +namespace addr { +inline uint8_t row(uint8_t key_addr) { + return (key_addr / COLS); +} +inline uint8_t col(uint8_t key_addr) { + return (key_addr % COLS); +} +inline uint8_t addr(uint8_t row, uint8_t col) { + return ((row * COLS) + col); +} +} // namespace addr { } // namespace kaleidoscope { From eff16ded81154f07f09017ce6d5ed7c9c87831b2 Mon Sep 17 00:00:00 2001 From: Michael Richters Date: Tue, 28 Nov 2017 22:41:36 -0600 Subject: [PATCH 11/72] Not functional yet, but doesn't seem to break anything else --- src/Kaleidoscope/Qukeys.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Kaleidoscope/Qukeys.cpp b/src/Kaleidoscope/Qukeys.cpp index 278d6996..3a79ee08 100644 --- a/src/Kaleidoscope/Qukeys.cpp +++ b/src/Kaleidoscope/Qukeys.cpp @@ -152,8 +152,10 @@ Key Qukeys::keyScanHook(Key mapped_key, byte row, byte col, uint8_t key_state) { if (key_queue_length_) { enqueue(key_addr); } else { + // If it's not a qukey, proceed: if (qukey_index == QUKEY_NOT_FOUND) return mapped_key; + // Otherwise, queue the qukey: enqueue(key_addr); } } @@ -173,7 +175,7 @@ Key Qukeys::keyScanHook(Key mapped_key, byte row, byte col, uint8_t key_state) { // If the key is not a qukey: if (qukey_index == QUKEY_NOT_FOUND) { - // If the key was pressed before the keys in the queue: + // If the key was pressed before the keys in the queue, proceed: if (queue_index == QUKEY_NOT_FOUND) { return mapped_key; } else { From 2fba5c4541c0861b29a99cfccdc4027014b82e06 Mon Sep 17 00:00:00 2001 From: Michael Richters Date: Wed, 29 Nov 2017 11:41:00 -0600 Subject: [PATCH 12/72] First working version Too many bug fixes to list them all. Luckily, at this early stage, that doesn't matter. --- examples/Qukeys/Qukeys.ino | 20 ++++--------- src/Kaleidoscope/Qukeys.cpp | 57 ++++++++++++++++++++++++++----------- src/Kaleidoscope/Qukeys.h | 5 ++-- 3 files changed, 50 insertions(+), 32 deletions(-) diff --git a/examples/Qukeys/Qukeys.ino b/examples/Qukeys/Qukeys.ino index f819a875..b3588088 100644 --- a/examples/Qukeys/Qukeys.ino +++ b/examples/Qukeys/Qukeys.ino @@ -23,26 +23,18 @@ const Key keymaps[][ROWS][COLS] PROGMEM = { ___), }; -// Define the Qukeys map -// QUKEYS( -// Qukey(0, 2, 1, Key_LeftShift), -// Qukey(0, 2, 2, Key_LeftControl), -// Qukey(0, 2, 3, Key_LeftAlt), -// Qukey(0, 2, 4, Key_LeftGui) -// ) - void setup() { // Use Qukeys Kaleidoscope.use(&Qukeys); - //kaleidoscope::Qukeys.init(qukeys, count); - kaleidoscope::Qukey qukeys[] = { - kaleidoscope::Qukey(0, 2, 1, Key_LeftShift), - kaleidoscope::Qukey(0, 2, 2, Key_LeftControl) + static kaleidoscope::Qukey qukeys[] = { + kaleidoscope::Qukey(0, 2, 1, Key_LeftGui), // A/cmd + kaleidoscope::Qukey(0, 2, 2, Key_LeftAlt), // S/alt + kaleidoscope::Qukey(0, 2, 3, Key_LeftControl), // D/ctrl + kaleidoscope::Qukey(0, 2, 4, Key_LeftShift) // F/shift }; - uint8_t count = sizeof(qukeys) / sizeof(kaleidoscope::Qukey); Qukeys.qukeys_ = qukeys; - Qukeys.qukeys_count_ = count; + Qukeys.qukeys_count_ = sizeof(qukeys) / sizeof(kaleidoscope::Qukey); Kaleidoscope.setup(); } diff --git a/src/Kaleidoscope/Qukeys.cpp b/src/Kaleidoscope/Qukeys.cpp index 3a79ee08..1f2ba5e1 100644 --- a/src/Kaleidoscope/Qukeys.cpp +++ b/src/Kaleidoscope/Qukeys.cpp @@ -39,7 +39,7 @@ Qukey::Qukey(int8_t layer, byte row, byte col, Key alt_keycode) { Qukey * Qukeys::qukeys_; uint8_t Qukeys::qukeys_count_ = 0; bool Qukeys::active_ = true; -uint16_t Qukeys::time_limit_ = 200; +uint16_t Qukeys::time_limit_ = 500; QueueItem Qukeys::key_queue_[QUKEYS_QUEUE_MAX] = {}; uint8_t Qukeys::key_queue_length_ = 0; @@ -52,15 +52,15 @@ Qukeys::Qukeys(void) {} // } int8_t Qukeys::lookupQukey(uint8_t key_addr) { - if (key_addr == QUKEY_UNKNOWN_ADDR) + if (key_addr == QUKEY_UNKNOWN_ADDR) { return QUKEY_NOT_FOUND; + } for (int8_t i = 0; i < qukeys_count_; i++) { - Qukey qukey = qukeys_[i]; - if (qukey.addr == key_addr) { + if (qukeys_[i].addr == key_addr) { byte row = addr::row(key_addr); byte col = addr::col(key_addr); - if ((qukey.layer == QUKEY_ALL_LAYERS) || - (qukey.layer == Layer.lookupActiveLayer(row, col))) { + if ((qukeys_[i].layer == QUKEY_ALL_LAYERS) || + (qukeys_[i].layer == Layer.lookupActiveLayer(row, col))) { return i; } } @@ -70,7 +70,7 @@ int8_t Qukeys::lookupQukey(uint8_t key_addr) { void Qukeys::enqueue(uint8_t key_addr) { if (key_queue_length_ == QUKEYS_QUEUE_MAX) { - flushKey(QUKEY_STATE_PRIMARY); + flushKey(QUKEY_STATE_PRIMARY, IS_PRESSED | WAS_PRESSED); } key_queue_[key_queue_length_].addr = key_addr; key_queue_[key_queue_length_].flush_time = millis() + time_limit_; @@ -82,11 +82,11 @@ int8_t Qukeys::searchQueue(uint8_t key_addr) { if (key_queue_[i].addr == key_addr) return i; } - return -1; + return QUKEY_NOT_FOUND; } // flush a single entry from the head of the queue -void Qukeys::flushKey(int8_t state) { +void Qukeys::flushKey(int8_t state, uint8_t keyswitch_state) { int8_t qukey_index = lookupQukey(key_queue_[0].addr); if (qukey_index != QUKEY_NOT_FOUND) { qukeys_[qukey_index].state = state; @@ -112,23 +112,43 @@ void Qukeys::flushKey(int8_t state) { handleKeyswitchEvent(keycode, row, col, IS_PRESSED | INJECTED); // Now we send the report (if there were any changes) hid::sendKeyboardReport(); + // Now for the tricky bit; we need to know if the key was actually + // released, or if it's still being held. Otherwise, we'll screw up + // the next call to flushKey(). + handleKeyswitchEvent(keycode, row, col, keyswitch_state | INJECTED); + hid::sendKeyboardReport(); // Shift the queue, so key_queue[0] is always the first key that gets processed for (byte i = 0; i < key_queue_length_; i++) { key_queue_[i] = key_queue_[i + 1]; } key_queue_length_--; + // If a qukey was released, reset its state to undetermined. This + // probably doesn't hurt, but it's also probably useless: + if (!(keyswitch_state & IS_PRESSED) && + (qukey_index != QUKEY_NOT_FOUND)) { + qukeys_[qukey_index].state = QUKEY_STATE_UNDETERMINED; + } } -void Qukeys::flushQueue(int8_t state, int8_t index) { - for (int8_t i = 0; i <= index; i++) { +// flushQueue() is called when a key that's in the key_queue is +// released. This means that all the keys ahead of it in the queue are +// still being held, so first we flush them, then we flush the +// released key (with different parameters). +void Qukeys::flushQueue(int8_t index) { + if (index == QUKEY_NOT_FOUND) + return; + for (int8_t i = 0; i < index; i++) { if (key_queue_length_ == 0) break; - flushKey(state); + flushKey(QUKEY_STATE_ALTERNATE, IS_PRESSED | WAS_PRESSED); } + flushKey(QUKEY_STATE_PRIMARY, WAS_PRESSED); } Key Qukeys::keyScanHook(Key mapped_key, byte row, byte col, uint8_t key_state) { + // Uncomment this for debugging, so as not to make flashing difficult + //if (row == 0 && col == 0) return mapped_key; // If Qukeys is turned off, continue to next plugin if (!active_) @@ -165,10 +185,15 @@ Key Qukeys::keyScanHook(Key mapped_key, byte row, byte col, uint8_t key_state) { // If the key was just released: if (keyToggledOff(key_state)) { - if (queue_index == QUKEY_NOT_FOUND) + // If the key isn't in the key_queue, proceed + if (queue_index == QUKEY_NOT_FOUND) { + // If a qukey that was not in the queue toggles off, reset its state + if (qukey_index != QUKEY_NOT_FOUND) + qukeys_[qukey_index].state = QUKEY_STATE_UNDETERMINED; return mapped_key; - flushQueue(QUKEY_STATE_ALTERNATE, queue_index); - return Key_NoKey; + } + flushQueue(queue_index); + return mapped_key; } // Otherwise, the key is still pressed @@ -200,7 +225,7 @@ void Qukeys::preReportHook(void) { uint32_t current_time = millis(); for (int8_t i = 0; i < key_queue_length_; i++) { if (current_time > key_queue_[i].flush_time) { - flushKey(QUKEY_STATE_ALTERNATE); + flushKey(QUKEY_STATE_ALTERNATE, IS_PRESSED | WAS_PRESSED); } else { break; } diff --git a/src/Kaleidoscope/Qukeys.h b/src/Kaleidoscope/Qukeys.h index 958ae542..ddd0694f 100644 --- a/src/Kaleidoscope/Qukeys.h +++ b/src/Kaleidoscope/Qukeys.h @@ -83,8 +83,8 @@ class Qukeys : public KaleidoscopePlugin { static int8_t lookupQukey(uint8_t key_addr); static void enqueue(uint8_t key_addr); static int8_t searchQueue(uint8_t key_addr); - static void flushKey(int8_t state); - static void flushQueue(int8_t state, int8_t index); + static void flushKey(int8_t state, uint8_t keyswitch_state); + static void flushQueue(int8_t index); static Qukey * qukeys_; static uint8_t qukeys_count_; @@ -94,6 +94,7 @@ class Qukeys : public KaleidoscopePlugin { static uint16_t time_limit_; static QueueItem key_queue_[QUKEYS_QUEUE_MAX]; static uint8_t key_queue_length_; + //static uint8_t keyswitch_state[]; static Key keyScanHook(Key mapped_key, byte row, byte col, uint8_t key_state); static void preReportHook(void); From 7fb0c96931336666c0b5fa7d61021a5a6c9c797f Mon Sep 17 00:00:00 2001 From: Michael Richters Date: Thu, 30 Nov 2017 10:10:58 -0600 Subject: [PATCH 13/72] Why was .gitignore in a .gitignore/ directory? GitHub screwed that up, I think. --- .gitignore/.gitignore => .gitignore | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .gitignore/.gitignore => .gitignore (100%) diff --git a/.gitignore/.gitignore b/.gitignore similarity index 100% rename from .gitignore/.gitignore rename to .gitignore From 87c98b15af778729aec8ed75c4c849cbe77e70e3 Mon Sep 17 00:00:00 2001 From: Michael Richters Date: Thu, 30 Nov 2017 10:12:20 -0600 Subject: [PATCH 14/72] Direct manipulation of `Keyboard.keyReport` & `.lastKeyReport` KeyboardioHID isn't going to get a function to copy the previous report to the current one, but it is now making the current and previous HID reports public. This is a much better solution all around, as it allows us to save and restore the current report in the midst of a scan, while still sending a modified version of the old report. --- src/Kaleidoscope/Qukeys.cpp | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/Kaleidoscope/Qukeys.cpp b/src/Kaleidoscope/Qukeys.cpp index 1f2ba5e1..389166cd 100644 --- a/src/Kaleidoscope/Qukeys.cpp +++ b/src/Kaleidoscope/Qukeys.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #ifdef ARDUINO_VIRTUAL #define debug_print(...) printf(__VA_ARGS__) @@ -99,11 +100,15 @@ void Qukeys::flushKey(int8_t state, uint8_t keyswitch_state) { } else { keycode = Layer.lookup(row, col); } + // Since we're in the middle of the key scan, we don't necessarily // have a full HID report, and we don't want to accidentally turn // off keys that the scan hasn't reached yet, so we force the // current report to be the same as the previous one, then proceed - hid::copyPrevKeyboardReport(); + HID_KeyboardReport_Data_t hid_report; + // First, save the current report + memcpy(hid_report.allkeys, Keyboard.keyReport.allkeys, sizeof(hid_report)); + memcpy(Keyboard.keyReport.allkeys, Keyboard.lastKeyReport.allkeys, sizeof(Keyboard.keyReport)); // Instead of just calling pressKey here, we start processing the // key again, as if it was just pressed, and mark it as injected, so // we can ignore it and don't start an infinite loop. It would be @@ -112,11 +117,19 @@ void Qukeys::flushKey(int8_t state, uint8_t keyswitch_state) { handleKeyswitchEvent(keycode, row, col, IS_PRESSED | INJECTED); // Now we send the report (if there were any changes) hid::sendKeyboardReport(); + + /* I think this is now unnecessary // Now for the tricky bit; we need to know if the key was actually // released, or if it's still being held. Otherwise, we'll screw up // the next call to flushKey(). - handleKeyswitchEvent(keycode, row, col, keyswitch_state | INJECTED); - hid::sendKeyboardReport(); + if (keyToggledOff(keyswitch_state)) { + handleKeyswitchEvent(keycode, row, col, keyswitch_state | INJECTED); + hid::sendKeyboardReport(); + } + */ + + // Last, we restore the current state of the report + memcpy(Keyboard.keyReport.allkeys, hid_report.allkeys, sizeof(hid_report)); // Shift the queue, so key_queue[0] is always the first key that gets processed for (byte i = 0; i < key_queue_length_; i++) { From b51d679939cdf7d3509528108c6dd20c4d120a91 Mon Sep 17 00:00:00 2001 From: Michael Richters Date: Thu, 30 Nov 2017 10:16:50 -0600 Subject: [PATCH 15/72] Ignore output/ directory --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 259148fa..729af14c 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,4 @@ *.exe *.out *.app +output/ From cb16b322158ae0302846face8323e6701c57def2 Mon Sep 17 00:00:00 2001 From: Michael Richters Date: Thu, 30 Nov 2017 23:45:15 -0600 Subject: [PATCH 16/72] Fixed `QUKEYS()` macro for defining qukeys in sketch --- examples/Qukeys/Qukeys.ino | 6 ++---- src/Kaleidoscope/Qukeys.h | 9 ++++----- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/examples/Qukeys/Qukeys.ino b/examples/Qukeys/Qukeys.ino index b3588088..21a45b58 100644 --- a/examples/Qukeys/Qukeys.ino +++ b/examples/Qukeys/Qukeys.ino @@ -27,14 +27,12 @@ void setup() { // Use Qukeys Kaleidoscope.use(&Qukeys); - static kaleidoscope::Qukey qukeys[] = { + QUKEYS( kaleidoscope::Qukey(0, 2, 1, Key_LeftGui), // A/cmd kaleidoscope::Qukey(0, 2, 2, Key_LeftAlt), // S/alt kaleidoscope::Qukey(0, 2, 3, Key_LeftControl), // D/ctrl kaleidoscope::Qukey(0, 2, 4, Key_LeftShift) // F/shift - }; - Qukeys.qukeys_ = qukeys; - Qukeys.qukeys_count_ = sizeof(qukeys) / sizeof(kaleidoscope::Qukey); + ) Kaleidoscope.setup(); } diff --git a/src/Kaleidoscope/Qukeys.h b/src/Kaleidoscope/Qukeys.h index ddd0694f..77aef365 100644 --- a/src/Kaleidoscope/Qukeys.h +++ b/src/Kaleidoscope/Qukeys.h @@ -80,6 +80,7 @@ class Qukeys : public KaleidoscopePlugin { static void deactivate(void) { active_ = false; } + static int8_t lookupQukey(uint8_t key_addr); static void enqueue(uint8_t key_addr); static int8_t searchQueue(uint8_t key_addr); @@ -108,8 +109,6 @@ extern kaleidoscope::Qukeys Qukeys; // macro for use in sketch file to simplify definition of qukeys #define QUKEYS(qukey_defs...) \ - namespace kaleidoscope { \ - Qukey qukeys[] = { qukey_defs... }; \ - uint8_t qukeys_count = sizeof(qukeys) / sizeof(Qukey); \ - Qukeys.init(qukeys, qukeys_count); \ - } + static kaleidoscope::Qukey qukeys[] = { qukey_defs }; \ + Qukeys.qukeys_ = qukeys; \ + Qukeys.qukeys_count_ = sizeof(qukeys) / sizeof(kaleidoscope::Qukey); From d12a4870fe19056cec94e877a4162f4ac9880fff Mon Sep 17 00:00:00 2001 From: Michael Richters Date: Thu, 30 Nov 2017 23:46:57 -0600 Subject: [PATCH 17/72] Added `setTimeout()` function to set the time limit in sketch --- examples/Qukeys/Qukeys.ino | 1 + src/Kaleidoscope/Qukeys.h | 3 +++ 2 files changed, 4 insertions(+) diff --git a/examples/Qukeys/Qukeys.ino b/examples/Qukeys/Qukeys.ino index 21a45b58..0ab51d14 100644 --- a/examples/Qukeys/Qukeys.ino +++ b/examples/Qukeys/Qukeys.ino @@ -33,6 +33,7 @@ void setup() { kaleidoscope::Qukey(0, 2, 3, Key_LeftControl), // D/ctrl kaleidoscope::Qukey(0, 2, 4, Key_LeftShift) // F/shift ) + Qukeys.setTimeout(200); Kaleidoscope.setup(); } diff --git a/src/Kaleidoscope/Qukeys.h b/src/Kaleidoscope/Qukeys.h index 77aef365..dccd84aa 100644 --- a/src/Kaleidoscope/Qukeys.h +++ b/src/Kaleidoscope/Qukeys.h @@ -80,6 +80,9 @@ class Qukeys : public KaleidoscopePlugin { static void deactivate(void) { active_ = false; } + static void setTimeout(uint16_t time_limit) { + time_limit_ = time_limit; + } static int8_t lookupQukey(uint8_t key_addr); static void enqueue(uint8_t key_addr); From f603e6d8d3e70cffac826c98316257912f095412 Mon Sep 17 00:00:00 2001 From: Michael Richters Date: Fri, 1 Dec 2017 23:34:48 -0600 Subject: [PATCH 18/72] astyle --- examples/Qukeys/Qukeys.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/Qukeys/Qukeys.ino b/examples/Qukeys/Qukeys.ino index 0ab51d14..4a5b6f8e 100644 --- a/examples/Qukeys/Qukeys.ino +++ b/examples/Qukeys/Qukeys.ino @@ -32,7 +32,7 @@ void setup() { kaleidoscope::Qukey(0, 2, 2, Key_LeftAlt), // S/alt kaleidoscope::Qukey(0, 2, 3, Key_LeftControl), // D/ctrl kaleidoscope::Qukey(0, 2, 4, Key_LeftShift) // F/shift - ) + ) Qukeys.setTimeout(200); Kaleidoscope.setup(); From 6ca34d7f99b77d7d06d38df59d1bf28beef928f9 Mon Sep 17 00:00:00 2001 From: Michael Richters Date: Sat, 2 Dec 2017 00:10:23 -0600 Subject: [PATCH 19/72] Added a function to toggle Qukeys on/off --- src/Kaleidoscope/Qukeys.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Kaleidoscope/Qukeys.h b/src/Kaleidoscope/Qukeys.h index dccd84aa..aacc75d9 100644 --- a/src/Kaleidoscope/Qukeys.h +++ b/src/Kaleidoscope/Qukeys.h @@ -80,6 +80,9 @@ class Qukeys : public KaleidoscopePlugin { static void deactivate(void) { active_ = false; } + static void toggle(void) { + active_ = !active_; + } static void setTimeout(uint16_t time_limit) { time_limit_ = time_limit; } From 6e57b9868faa8e492a817a73af779f93111b2755 Mon Sep 17 00:00:00 2001 From: Michael Richters Date: Sat, 2 Dec 2017 00:11:03 -0600 Subject: [PATCH 20/72] Added a macro to toggle Qukeys in the example - also updated the keymaps definition --- examples/Qukeys/Qukeys.ino | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/examples/Qukeys/Qukeys.ino b/examples/Qukeys/Qukeys.ino index 4a5b6f8e..8715cf34 100644 --- a/examples/Qukeys/Qukeys.ino +++ b/examples/Qukeys/Qukeys.ino @@ -2,8 +2,9 @@ #include #include +#include -const Key keymaps[][ROWS][COLS] PROGMEM = { +KEYMAPS( [0] = KEYMAP_STACKED ( Key_NoKey, Key_1, Key_2, Key_3, Key_4, Key_5, Key_NoKey, @@ -14,14 +15,24 @@ const Key keymaps[][ROWS][COLS] PROGMEM = { Key_LeftControl, Key_Backspace, Key_LeftGui, Key_LeftShift, ___, - Key_skip, Key_6, Key_7, Key_8, Key_9, Key_0, Key_skip, + M(MACRO_TOGGLE_QUKEYS), Key_6, Key_7, Key_8, Key_9, Key_0, Key_skip, Key_Enter, Key_Y, Key_U, Key_I, Key_O, Key_P, Key_Equals, Key_H, Key_J, Key_K, Key_L, Key_Semicolon, Key_Quote, Key_skip, Key_N, Key_M, Key_Comma, Key_Period, Key_Slash, Key_Minus, Key_RightShift, Key_RightAlt, Key_Spacebar, Key_RightControl, ___), -}; +) + +// Defining a macro (on the "any" key: see above) to toggle Qukeys on and off +const macro_t *macroAction(uint8_t macro_index, uint8_t key_state) { + switch (macro_index) { + case MACRO_TOGGLE_QUKEYS: + Qukeys.toggle(); + break; + } + return MACRO_NONE; +} void setup() { // Use Qukeys @@ -35,6 +46,9 @@ void setup() { ) Qukeys.setTimeout(200); + // To toggle Qukeys off and on, we use a macro + Kaleidoscope.use(&Macros); + Kaleidoscope.setup(); } From 8fcff0109fd0af7435df5b26c1c9912df506efae Mon Sep 17 00:00:00 2001 From: Michael Richters Date: Sat, 2 Dec 2017 00:19:54 -0600 Subject: [PATCH 21/72] Small improvement to QUKEYS() macro Using braces really makes it much clearer. I stole the idea from @algernon. --- src/Kaleidoscope/Qukeys.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Kaleidoscope/Qukeys.h b/src/Kaleidoscope/Qukeys.h index aacc75d9..3e227e70 100644 --- a/src/Kaleidoscope/Qukeys.h +++ b/src/Kaleidoscope/Qukeys.h @@ -114,7 +114,8 @@ class Qukeys : public KaleidoscopePlugin { extern kaleidoscope::Qukeys Qukeys; // macro for use in sketch file to simplify definition of qukeys -#define QUKEYS(qukey_defs...) \ +#define QUKEYS(qukey_defs...) { \ static kaleidoscope::Qukey qukeys[] = { qukey_defs }; \ Qukeys.qukeys_ = qukeys; \ - Qukeys.qukeys_count_ = sizeof(qukeys) / sizeof(kaleidoscope::Qukey); + Qukeys.qukeys_count_ = sizeof(qukeys) / sizeof(kaleidoscope::Qukey); \ +} From 17d61518c24d033157f203b29902ceb0d12f4dba Mon Sep 17 00:00:00 2001 From: Michael Richters Date: Sat, 2 Dec 2017 00:20:50 -0600 Subject: [PATCH 22/72] Fix example toggle-qukeys macro --- examples/Qukeys/Qukeys.ino | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/Qukeys/Qukeys.ino b/examples/Qukeys/Qukeys.ino index 8715cf34..8f8ed62b 100644 --- a/examples/Qukeys/Qukeys.ino +++ b/examples/Qukeys/Qukeys.ino @@ -4,6 +4,8 @@ #include #include +enum { MACRO_TOGGLE_QUKEYS }; + KEYMAPS( [0] = KEYMAP_STACKED ( From 99988e4c76d6b18dbb1b5102db056a7df15128bd Mon Sep 17 00:00:00 2001 From: Michael Richters Date: Sun, 3 Dec 2017 00:00:20 -0600 Subject: [PATCH 23/72] Call toggle() only when the macro key toggles on (fixes #11) --- examples/Qukeys/Qukeys.ino | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/Qukeys/Qukeys.ino b/examples/Qukeys/Qukeys.ino index 8f8ed62b..e821e194 100644 --- a/examples/Qukeys/Qukeys.ino +++ b/examples/Qukeys/Qukeys.ino @@ -30,7 +30,8 @@ KEYMAPS( const macro_t *macroAction(uint8_t macro_index, uint8_t key_state) { switch (macro_index) { case MACRO_TOGGLE_QUKEYS: - Qukeys.toggle(); + if (keyToggledOn(key_state)) + Qukeys.toggle(); break; } return MACRO_NONE; From 6102388813c391c7ed9188cb02863e014c75d190 Mon Sep 17 00:00:00 2001 From: Michael Richters Date: Sun, 3 Dec 2017 10:27:14 -0600 Subject: [PATCH 24/72] Call handleKeyswitchEvent() again after restoring the current report After restoring the current report, if we don't add the keycode for the current key back in (by calling `handleKeyswitchEvent()` with the "held" state flags if the flushed key is still held), we'll accidentally leave that keycode out of the next report. fixes #13 --- src/Kaleidoscope/Qukeys.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Kaleidoscope/Qukeys.cpp b/src/Kaleidoscope/Qukeys.cpp index 389166cd..fc6b2238 100644 --- a/src/Kaleidoscope/Qukeys.cpp +++ b/src/Kaleidoscope/Qukeys.cpp @@ -108,6 +108,7 @@ void Qukeys::flushKey(int8_t state, uint8_t keyswitch_state) { HID_KeyboardReport_Data_t hid_report; // First, save the current report memcpy(hid_report.allkeys, Keyboard.keyReport.allkeys, sizeof(hid_report)); + // Next, copy the old report memcpy(Keyboard.keyReport.allkeys, Keyboard.lastKeyReport.allkeys, sizeof(Keyboard.keyReport)); // Instead of just calling pressKey here, we start processing the // key again, as if it was just pressed, and mark it as injected, so @@ -128,9 +129,13 @@ void Qukeys::flushKey(int8_t state, uint8_t keyswitch_state) { } */ - // Last, we restore the current state of the report + // Next, we restore the current state of the report memcpy(Keyboard.keyReport.allkeys, hid_report.allkeys, sizeof(hid_report)); + // Last, if the key is still down, add its code back in + if ( ! keyToggledOn(keyswitch_state) ) + handleKeyswitchEvent(keycode, row, col, IS_PRESSED | WAS_PRESSED | INJECTED); + // Shift the queue, so key_queue[0] is always the first key that gets processed for (byte i = 0; i < key_queue_length_; i++) { key_queue_[i] = key_queue_[i + 1]; @@ -142,6 +147,8 @@ void Qukeys::flushKey(int8_t state, uint8_t keyswitch_state) { (qukey_index != QUKEY_NOT_FOUND)) { qukeys_[qukey_index].state = QUKEY_STATE_UNDETERMINED; } + // After flushing the first key in the queue, maybe the next key should be checked to + // see if it should also be flushed? } // flushQueue() is called when a key that's in the key_queue is From 2db772aeca3d74bd69eddc209cde2b924b264749 Mon Sep 17 00:00:00 2001 From: Michael Richters Date: Sun, 3 Dec 2017 11:18:39 -0600 Subject: [PATCH 25/72] Better pos-scan flushing of the key queue --- src/Kaleidoscope/Qukeys.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/Kaleidoscope/Qukeys.cpp b/src/Kaleidoscope/Qukeys.cpp index fc6b2238..e15b2bd1 100644 --- a/src/Kaleidoscope/Qukeys.cpp +++ b/src/Kaleidoscope/Qukeys.cpp @@ -149,6 +149,13 @@ void Qukeys::flushKey(int8_t state, uint8_t keyswitch_state) { } // After flushing the first key in the queue, maybe the next key should be checked to // see if it should also be flushed? + // while (key_queue_length_ > 0) { + // // If it's a qukey, stop: + // if ( lookupQukey(key_queue_[0].addr) != QUKEY_NOT_FOUND ) + // break; + // // Otherwise, flush the next key from the queue + // flushKey(QUKEY_STATE_PRIMARY, IS_PRESSED | WAS_PRESSED); + // } } // flushQueue() is called when a key that's in the key_queue is @@ -243,8 +250,10 @@ void Qukeys::preReportHook(void) { // If the qukey has been held longer than the time limit, set its // state to the alternate keycode and add it to the report uint32_t current_time = millis(); - for (int8_t i = 0; i < key_queue_length_; i++) { - if (current_time > key_queue_[i].flush_time) { + while (key_queue_length_ > 0) { + if ( lookupQukey(key_queue_[0].addr) == QUKEY_NOT_FOUND ) { + flushKey(QUKEY_STATE_PRIMARY, IS_PRESSED | WAS_PRESSED); + } else if (current_time > key_queue_[0].flush_time) { flushKey(QUKEY_STATE_ALTERNATE, IS_PRESSED | WAS_PRESSED); } else { break; From f2dc52ba0b734c2f54e32dbf494bf83540f03249 Mon Sep 17 00:00:00 2001 From: Michael Richters Date: Fri, 1 Dec 2017 23:32:55 -0600 Subject: [PATCH 26/72] Added mask() & unmask() functions --- src/addr.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/addr.h b/src/addr.h index 156ce8e8..24e8054a 100644 --- a/src/addr.h +++ b/src/addr.h @@ -34,5 +34,11 @@ inline uint8_t col(uint8_t key_addr) { inline uint8_t addr(uint8_t row, uint8_t col) { return ((row * COLS) + col); } +inline void mask(uint8_t key_addr) { + KeyboardHardware.maskKey(row(key_addr), col(key_addr)); +} +inline void unmask(uint8_t key_addr) { + KeyboardHardware.maskKey(row(key_addr), col(key_addr)); +} } // namespace addr { } // namespace kaleidoscope { From 0e951d8b4608e131cd7e1365eb6020788de24cf1 Mon Sep 17 00:00:00 2001 From: Michael Richters Date: Sun, 3 Dec 2017 12:09:47 -0600 Subject: [PATCH 27/72] Changed parameter name In `flushKey()`, I changed the ambiguous parameter name `state` to the more descriptive `qukey_state`. --- src/Kaleidoscope/Qukeys.cpp | 6 +++--- src/Kaleidoscope/Qukeys.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Kaleidoscope/Qukeys.cpp b/src/Kaleidoscope/Qukeys.cpp index e15b2bd1..c2f0bd50 100644 --- a/src/Kaleidoscope/Qukeys.cpp +++ b/src/Kaleidoscope/Qukeys.cpp @@ -87,15 +87,15 @@ int8_t Qukeys::searchQueue(uint8_t key_addr) { } // flush a single entry from the head of the queue -void Qukeys::flushKey(int8_t state, uint8_t keyswitch_state) { +void Qukeys::flushKey(int8_t qukey_state, uint8_t keyswitch_state) { int8_t qukey_index = lookupQukey(key_queue_[0].addr); if (qukey_index != QUKEY_NOT_FOUND) { - qukeys_[qukey_index].state = state; + qukeys_[qukey_index].state = qukey_state; } byte row = addr::row(key_queue_[0].addr); byte col = addr::col(key_queue_[0].addr); Key keycode = Key_NoKey; - if (state == QUKEY_STATE_ALTERNATE && qukey_index != QUKEY_NOT_FOUND) { + if (qukey_state == QUKEY_STATE_ALTERNATE && qukey_index != QUKEY_NOT_FOUND) { keycode = qukeys_[qukey_index].alt_keycode; } else { keycode = Layer.lookup(row, col); diff --git a/src/Kaleidoscope/Qukeys.h b/src/Kaleidoscope/Qukeys.h index 3e227e70..4e9be630 100644 --- a/src/Kaleidoscope/Qukeys.h +++ b/src/Kaleidoscope/Qukeys.h @@ -90,7 +90,7 @@ class Qukeys : public KaleidoscopePlugin { static int8_t lookupQukey(uint8_t key_addr); static void enqueue(uint8_t key_addr); static int8_t searchQueue(uint8_t key_addr); - static void flushKey(int8_t state, uint8_t keyswitch_state); + static void flushKey(int8_t qukey_state, uint8_t keyswitch_state); static void flushQueue(int8_t index); static Qukey * qukeys_; From ba67fe2ee63dde9ad21d8b27cbd9c9975a78b01b Mon Sep 17 00:00:00 2001 From: Michael Richters Date: Sun, 3 Dec 2017 16:04:19 -0600 Subject: [PATCH 28/72] Fixed addr::unmask() It was calling `maskKey()` instead of `unMaskKey()` --- src/addr.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/addr.h b/src/addr.h index 24e8054a..6b44358f 100644 --- a/src/addr.h +++ b/src/addr.h @@ -38,7 +38,7 @@ inline void mask(uint8_t key_addr) { KeyboardHardware.maskKey(row(key_addr), col(key_addr)); } inline void unmask(uint8_t key_addr) { - KeyboardHardware.maskKey(row(key_addr), col(key_addr)); + KeyboardHardware.unMaskKey(row(key_addr), col(key_addr)); } } // namespace addr { } // namespace kaleidoscope { From 632e876084271e8dfb5ab71365da0eaa98e90bea Mon Sep 17 00:00:00 2001 From: Michael Richters Date: Sun, 3 Dec 2017 16:06:43 -0600 Subject: [PATCH 29/72] Mask keys when they're in the queue; unmask when flushed --- src/Kaleidoscope/Qukeys.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Kaleidoscope/Qukeys.cpp b/src/Kaleidoscope/Qukeys.cpp index c2f0bd50..68455e63 100644 --- a/src/Kaleidoscope/Qukeys.cpp +++ b/src/Kaleidoscope/Qukeys.cpp @@ -76,6 +76,7 @@ void Qukeys::enqueue(uint8_t key_addr) { key_queue_[key_queue_length_].addr = key_addr; key_queue_[key_queue_length_].flush_time = millis() + time_limit_; key_queue_length_++; + addr::mask(key_addr); } int8_t Qukeys::searchQueue(uint8_t key_addr) { @@ -88,6 +89,7 @@ int8_t Qukeys::searchQueue(uint8_t key_addr) { // flush a single entry from the head of the queue void Qukeys::flushKey(int8_t qukey_state, uint8_t keyswitch_state) { + addr::unmask(key_queue_[0].addr); int8_t qukey_index = lookupQukey(key_queue_[0].addr); if (qukey_index != QUKEY_NOT_FOUND) { qukeys_[qukey_index].state = qukey_state; From 65163be9c7c5f64da083be0b4f0d38c3cd645067 Mon Sep 17 00:00:00 2001 From: Michael Richters Date: Mon, 4 Dec 2017 17:39:49 +0000 Subject: [PATCH 30/72] Store qukey state as a bitfield instead of a struct member (#15) * Defined bitfield structure for storing qukey state * Implement storage of qukey states in bitfield --- src/Kaleidoscope/Qukeys.cpp | 29 ++++++++++++----------------- src/Kaleidoscope/Qukeys.h | 21 +++++++++++++++------ 2 files changed, 27 insertions(+), 23 deletions(-) diff --git a/src/Kaleidoscope/Qukeys.cpp b/src/Kaleidoscope/Qukeys.cpp index 68455e63..3d921c19 100644 --- a/src/Kaleidoscope/Qukeys.cpp +++ b/src/Kaleidoscope/Qukeys.cpp @@ -34,7 +34,6 @@ Qukey::Qukey(int8_t layer, byte row, byte col, Key alt_keycode) { this->layer = layer; this->addr = addr::addr(row, col); this->alt_keycode = alt_keycode; - this->state = QUKEY_STATE_UNDETERMINED; } Qukey * Qukeys::qukeys_; @@ -43,6 +42,7 @@ bool Qukeys::active_ = true; uint16_t Qukeys::time_limit_ = 500; QueueItem Qukeys::key_queue_[QUKEYS_QUEUE_MAX] = {}; uint8_t Qukeys::key_queue_length_ = 0; +byte Qukeys::qukey_state_[] = {}; // Empty constructor; nothing is stored at the instance level Qukeys::Qukeys(void) {} @@ -88,11 +88,11 @@ int8_t Qukeys::searchQueue(uint8_t key_addr) { } // flush a single entry from the head of the queue -void Qukeys::flushKey(int8_t qukey_state, uint8_t keyswitch_state) { +void Qukeys::flushKey(bool qukey_state, uint8_t keyswitch_state) { addr::unmask(key_queue_[0].addr); int8_t qukey_index = lookupQukey(key_queue_[0].addr); if (qukey_index != QUKEY_NOT_FOUND) { - qukeys_[qukey_index].state = qukey_state; + setQukeyState(key_queue_[0].addr, qukey_state); } byte row = addr::row(key_queue_[0].addr); byte col = addr::col(key_queue_[0].addr); @@ -143,12 +143,6 @@ void Qukeys::flushKey(int8_t qukey_state, uint8_t keyswitch_state) { key_queue_[i] = key_queue_[i + 1]; } key_queue_length_--; - // If a qukey was released, reset its state to undetermined. This - // probably doesn't hurt, but it's also probably useless: - if (!(keyswitch_state & IS_PRESSED) && - (qukey_index != QUKEY_NOT_FOUND)) { - qukeys_[qukey_index].state = QUKEY_STATE_UNDETERMINED; - } // After flushing the first key in the queue, maybe the next key should be checked to // see if it should also be flushed? // while (key_queue_length_ > 0) { @@ -206,6 +200,7 @@ Key Qukeys::keyScanHook(Key mapped_key, byte row, byte col, uint8_t key_state) { return mapped_key; // Otherwise, queue the qukey: enqueue(key_addr); + return Key_NoKey; // is this right? } } @@ -216,9 +211,6 @@ Key Qukeys::keyScanHook(Key mapped_key, byte row, byte col, uint8_t key_state) { if (keyToggledOff(key_state)) { // If the key isn't in the key_queue, proceed if (queue_index == QUKEY_NOT_FOUND) { - // If a qukey that was not in the queue toggles off, reset its state - if (qukey_index != QUKEY_NOT_FOUND) - qukeys_[qukey_index].state = QUKEY_STATE_UNDETERMINED; return mapped_key; } flushQueue(queue_index); @@ -238,11 +230,14 @@ Key Qukeys::keyScanHook(Key mapped_key, byte row, byte col, uint8_t key_state) { } } - // qukeys that have already decided their keycode - if (qukeys_[qukey_index].state == QUKEY_STATE_PRIMARY) - return mapped_key; - if (qukeys_[qukey_index].state == QUKEY_STATE_ALTERNATE) - return qukeys_[qukey_index].alt_keycode; + // If the qukey is not in the queue, check its state + if (queue_index == QUKEY_NOT_FOUND) { + if (getQukeyState(key_addr) == QUKEY_STATE_ALTERNATE) { + return qukeys_[qukey_index].alt_keycode; + } else { // qukey_state == QUKEY_STATE_PRIMARY + return mapped_key; + } + } // else state is undetermined; block. I could check timeouts here, // but I'd rather do that in the pre-report hook return Key_NoKey; diff --git a/src/Kaleidoscope/Qukeys.h b/src/Kaleidoscope/Qukeys.h index 4e9be630..3d48b996 100644 --- a/src/Kaleidoscope/Qukeys.h +++ b/src/Kaleidoscope/Qukeys.h @@ -25,11 +25,12 @@ #define QUKEYS_MAX 64 // Maximum length of the pending queue #define QUKEYS_QUEUE_MAX 8 +// Total number of keys on the keyboard (assuming full grid) +#define TOTAL_KEYS ROWS * COLS -// Maybe it's better to use an enum for these state values? -#define QUKEY_STATE_UNDETERMINED 0 -#define QUKEY_STATE_PRIMARY 1 -#define QUKEY_STATE_ALTERNATE -1 +// Boolean values for storing qukey state +#define QUKEY_STATE_PRIMARY false +#define QUKEY_STATE_ALTERNATE true // Initialization addr value for empty key_queue. This seems to be // unnecessary, because we rely on keeping track of the lenght of the @@ -53,7 +54,6 @@ struct Qukey { int8_t layer; uint8_t addr; Key alt_keycode; - int8_t state; }; // Data structure for an entry in the key_queue @@ -90,7 +90,7 @@ class Qukeys : public KaleidoscopePlugin { static int8_t lookupQukey(uint8_t key_addr); static void enqueue(uint8_t key_addr); static int8_t searchQueue(uint8_t key_addr); - static void flushKey(int8_t qukey_state, uint8_t keyswitch_state); + static void flushKey(bool qukey_state, uint8_t keyswitch_state); static void flushQueue(int8_t index); static Qukey * qukeys_; @@ -103,6 +103,15 @@ class Qukeys : public KaleidoscopePlugin { static uint8_t key_queue_length_; //static uint8_t keyswitch_state[]; + // Qukey state bitfield + static uint8_t qukey_state_[(TOTAL_KEYS)/8 + ((TOTAL_KEYS)%8 ? 1 : 0)]; + static bool getQukeyState(uint8_t addr) { + return bitRead(qukey_state_[addr / 8], addr % 8); + } + static void setQukeyState(uint8_t addr, boolean qukey_state) { + bitWrite(qukey_state_[addr / 8], addr % 8, qukey_state); + } + static Key keyScanHook(Key mapped_key, byte row, byte col, uint8_t key_state); static void preReportHook(void); static void postReportHook(void) {} From abea4c281cd75f8301682f99e7502aa6a45d0cb9 Mon Sep 17 00:00:00 2001 From: Michael Richters Date: Mon, 4 Dec 2017 13:13:07 -0600 Subject: [PATCH 31/72] Simplify if(keyToggledOn) --- src/Kaleidoscope/Qukeys.cpp | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/Kaleidoscope/Qukeys.cpp b/src/Kaleidoscope/Qukeys.cpp index 3d921c19..602c12ce 100644 --- a/src/Kaleidoscope/Qukeys.cpp +++ b/src/Kaleidoscope/Qukeys.cpp @@ -191,17 +191,13 @@ Key Qukeys::keyScanHook(Key mapped_key, byte row, byte col, uint8_t key_state) { // If the key was just pressed: if (keyToggledOn(key_state)) { - // I think I may need to call maskKey() somewhere here, but I'm not sure - if (key_queue_length_) { - enqueue(key_addr); - } else { - // If it's not a qukey, proceed: - if (qukey_index == QUKEY_NOT_FOUND) - return mapped_key; - // Otherwise, queue the qukey: - enqueue(key_addr); - return Key_NoKey; // is this right? - } + // If the queue is empty and the key isn't a qukey, proceed: + if (key_queue_length_ == 0 && + qukey_index == QUKEY_NOT_FOUND) + return mapped_key; + // Otherwise, queue the key and stop processing: + enqueue(key_addr); + return Key_NoKey; } // In all other cases, we need to know if the key is queued already From c112cc615d9c1f87fecc531f621cecd170223198 Mon Sep 17 00:00:00 2001 From: Michael Richters Date: Mon, 4 Dec 2017 14:29:25 -0600 Subject: [PATCH 32/72] Flush non-qukeys from the head of the queue --- src/Kaleidoscope/Qukeys.cpp | 19 ++++++++++--------- src/Kaleidoscope/Qukeys.h | 1 + 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/Kaleidoscope/Qukeys.cpp b/src/Kaleidoscope/Qukeys.cpp index 602c12ce..a8177543 100644 --- a/src/Kaleidoscope/Qukeys.cpp +++ b/src/Kaleidoscope/Qukeys.cpp @@ -72,6 +72,7 @@ int8_t Qukeys::lookupQukey(uint8_t key_addr) { void Qukeys::enqueue(uint8_t key_addr) { if (key_queue_length_ == QUKEYS_QUEUE_MAX) { flushKey(QUKEY_STATE_PRIMARY, IS_PRESSED | WAS_PRESSED); + flushQueue(); } key_queue_[key_queue_length_].addr = key_addr; key_queue_[key_queue_length_].flush_time = millis() + time_limit_; @@ -143,15 +144,6 @@ void Qukeys::flushKey(bool qukey_state, uint8_t keyswitch_state) { key_queue_[i] = key_queue_[i + 1]; } key_queue_length_--; - // After flushing the first key in the queue, maybe the next key should be checked to - // see if it should also be flushed? - // while (key_queue_length_ > 0) { - // // If it's a qukey, stop: - // if ( lookupQukey(key_queue_[0].addr) != QUKEY_NOT_FOUND ) - // break; - // // Otherwise, flush the next key from the queue - // flushKey(QUKEY_STATE_PRIMARY, IS_PRESSED | WAS_PRESSED); - // } } // flushQueue() is called when a key that's in the key_queue is @@ -169,6 +161,15 @@ void Qukeys::flushQueue(int8_t index) { flushKey(QUKEY_STATE_PRIMARY, WAS_PRESSED); } +// Flush all the non-qukey keys from the front of the queue +void Qukeys::flushQueue(void) { + // flush keys until we find a qukey: + while (key_queue_length_ > 0 && + lookupQukey(key_queue_[0].addr) == QUKEY_NOT_FOUND) { + flushKey(QUKEY_STATE_PRIMARY, IS_PRESSED | WAS_PRESSED); + } +} + Key Qukeys::keyScanHook(Key mapped_key, byte row, byte col, uint8_t key_state) { // Uncomment this for debugging, so as not to make flashing difficult //if (row == 0 && col == 0) return mapped_key; diff --git a/src/Kaleidoscope/Qukeys.h b/src/Kaleidoscope/Qukeys.h index 3d48b996..403f6b81 100644 --- a/src/Kaleidoscope/Qukeys.h +++ b/src/Kaleidoscope/Qukeys.h @@ -92,6 +92,7 @@ class Qukeys : public KaleidoscopePlugin { static int8_t searchQueue(uint8_t key_addr); static void flushKey(bool qukey_state, uint8_t keyswitch_state); static void flushQueue(int8_t index); + static void flushQueue(void); static Qukey * qukeys_; static uint8_t qukeys_count_; From d33deda114a8ff99b3a9499b2e13b506b75b6f4e Mon Sep 17 00:00:00 2001 From: Michael Richters Date: Mon, 4 Dec 2017 14:34:12 -0600 Subject: [PATCH 33/72] astyle --- examples/Qukeys/Qukeys.ino | 32 ++++++++++++++++---------------- src/Kaleidoscope/Qukeys.cpp | 12 ++++++------ src/Kaleidoscope/Qukeys.h | 2 +- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/examples/Qukeys/Qukeys.ino b/examples/Qukeys/Qukeys.ino index e821e194..27d61a20 100644 --- a/examples/Qukeys/Qukeys.ino +++ b/examples/Qukeys/Qukeys.ino @@ -8,22 +8,22 @@ enum { MACRO_TOGGLE_QUKEYS }; KEYMAPS( [0] = KEYMAP_STACKED - ( - Key_NoKey, Key_1, Key_2, Key_3, Key_4, Key_5, Key_NoKey, - Key_Backtick, Key_Q, Key_W, Key_E, Key_R, Key_T, Key_Tab, - Key_PageUp, Key_A, Key_S, Key_D, Key_F, Key_G, - Key_PageDown, Key_Z, Key_X, Key_C, Key_V, Key_B, Key_Escape, - - Key_LeftControl, Key_Backspace, Key_LeftGui, Key_LeftShift, - ___, - - M(MACRO_TOGGLE_QUKEYS), Key_6, Key_7, Key_8, Key_9, Key_0, Key_skip, - Key_Enter, Key_Y, Key_U, Key_I, Key_O, Key_P, Key_Equals, - Key_H, Key_J, Key_K, Key_L, Key_Semicolon, Key_Quote, - Key_skip, Key_N, Key_M, Key_Comma, Key_Period, Key_Slash, Key_Minus, - - Key_RightShift, Key_RightAlt, Key_Spacebar, Key_RightControl, - ___), + ( + Key_NoKey, Key_1, Key_2, Key_3, Key_4, Key_5, Key_NoKey, + Key_Backtick, Key_Q, Key_W, Key_E, Key_R, Key_T, Key_Tab, + Key_PageUp, Key_A, Key_S, Key_D, Key_F, Key_G, + Key_PageDown, Key_Z, Key_X, Key_C, Key_V, Key_B, Key_Escape, + + Key_LeftControl, Key_Backspace, Key_LeftGui, Key_LeftShift, + ___, + + M(MACRO_TOGGLE_QUKEYS), Key_6, Key_7, Key_8, Key_9, Key_0, Key_skip, + Key_Enter, Key_Y, Key_U, Key_I, Key_O, Key_P, Key_Equals, + Key_H, Key_J, Key_K, Key_L, Key_Semicolon, Key_Quote, + Key_skip, Key_N, Key_M, Key_Comma, Key_Period, Key_Slash, Key_Minus, + + Key_RightShift, Key_RightAlt, Key_Spacebar, Key_RightControl, + ___), ) // Defining a macro (on the "any" key: see above) to toggle Qukeys on and off diff --git a/src/Kaleidoscope/Qukeys.cpp b/src/Kaleidoscope/Qukeys.cpp index a8177543..3c2f9c54 100644 --- a/src/Kaleidoscope/Qukeys.cpp +++ b/src/Kaleidoscope/Qukeys.cpp @@ -136,7 +136,7 @@ void Qukeys::flushKey(bool qukey_state, uint8_t keyswitch_state) { memcpy(Keyboard.keyReport.allkeys, hid_report.allkeys, sizeof(hid_report)); // Last, if the key is still down, add its code back in - if ( ! keyToggledOn(keyswitch_state) ) + if (! keyToggledOn(keyswitch_state)) handleKeyswitchEvent(keycode, row, col, IS_PRESSED | WAS_PRESSED | INJECTED); // Shift the queue, so key_queue[0] is always the first key that gets processed @@ -164,11 +164,11 @@ void Qukeys::flushQueue(int8_t index) { // Flush all the non-qukey keys from the front of the queue void Qukeys::flushQueue(void) { // flush keys until we find a qukey: - while (key_queue_length_ > 0 && - lookupQukey(key_queue_[0].addr) == QUKEY_NOT_FOUND) { + while (key_queue_length_ > 0 && + lookupQukey(key_queue_[0].addr) == QUKEY_NOT_FOUND) { flushKey(QUKEY_STATE_PRIMARY, IS_PRESSED | WAS_PRESSED); } -} +} Key Qukeys::keyScanHook(Key mapped_key, byte row, byte col, uint8_t key_state) { // Uncomment this for debugging, so as not to make flashing difficult @@ -194,7 +194,7 @@ Key Qukeys::keyScanHook(Key mapped_key, byte row, byte col, uint8_t key_state) { if (keyToggledOn(key_state)) { // If the queue is empty and the key isn't a qukey, proceed: if (key_queue_length_ == 0 && - qukey_index == QUKEY_NOT_FOUND) + qukey_index == QUKEY_NOT_FOUND) return mapped_key; // Otherwise, queue the key and stop processing: enqueue(key_addr); @@ -245,7 +245,7 @@ void Qukeys::preReportHook(void) { // state to the alternate keycode and add it to the report uint32_t current_time = millis(); while (key_queue_length_ > 0) { - if ( lookupQukey(key_queue_[0].addr) == QUKEY_NOT_FOUND ) { + if (lookupQukey(key_queue_[0].addr) == QUKEY_NOT_FOUND) { flushKey(QUKEY_STATE_PRIMARY, IS_PRESSED | WAS_PRESSED); } else if (current_time > key_queue_[0].flush_time) { flushKey(QUKEY_STATE_ALTERNATE, IS_PRESSED | WAS_PRESSED); diff --git a/src/Kaleidoscope/Qukeys.h b/src/Kaleidoscope/Qukeys.h index 403f6b81..4c733919 100644 --- a/src/Kaleidoscope/Qukeys.h +++ b/src/Kaleidoscope/Qukeys.h @@ -105,7 +105,7 @@ class Qukeys : public KaleidoscopePlugin { //static uint8_t keyswitch_state[]; // Qukey state bitfield - static uint8_t qukey_state_[(TOTAL_KEYS)/8 + ((TOTAL_KEYS)%8 ? 1 : 0)]; + static uint8_t qukey_state_[(TOTAL_KEYS) / 8 + ((TOTAL_KEYS) % 8 ? 1 : 0)]; static bool getQukeyState(uint8_t addr) { return bitRead(qukey_state_[addr / 8], addr % 8); } From f2894fa455f2518801653533143db680c1be76fd Mon Sep 17 00:00:00 2001 From: Michael Richters Date: Mon, 4 Dec 2017 14:35:26 -0600 Subject: [PATCH 34/72] Removed unused & commented init() function --- src/Kaleidoscope/Qukeys.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/Kaleidoscope/Qukeys.cpp b/src/Kaleidoscope/Qukeys.cpp index 3c2f9c54..76934a8f 100644 --- a/src/Kaleidoscope/Qukeys.cpp +++ b/src/Kaleidoscope/Qukeys.cpp @@ -47,11 +47,6 @@ byte Qukeys::qukey_state_[] = {}; // Empty constructor; nothing is stored at the instance level Qukeys::Qukeys(void) {} -// Qukeys::init(Qukey *qukeys, uint8_t qukeys_count) { -// qukeys_ = qukeys; -// qukeys_count_ = qukeys_count; -// } - int8_t Qukeys::lookupQukey(uint8_t key_addr) { if (key_addr == QUKEY_UNKNOWN_ADDR) { return QUKEY_NOT_FOUND; From e5813aba559448ff232d50090001fb17020b5690 Mon Sep 17 00:00:00 2001 From: Michael Richters Date: Mon, 4 Dec 2017 14:37:11 -0600 Subject: [PATCH 35/72] Removed unnecessary comments --- src/Kaleidoscope/Qukeys.cpp | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/Kaleidoscope/Qukeys.cpp b/src/Kaleidoscope/Qukeys.cpp index 76934a8f..29d0236d 100644 --- a/src/Kaleidoscope/Qukeys.cpp +++ b/src/Kaleidoscope/Qukeys.cpp @@ -117,16 +117,6 @@ void Qukeys::flushKey(bool qukey_state, uint8_t keyswitch_state) { // Now we send the report (if there were any changes) hid::sendKeyboardReport(); - /* I think this is now unnecessary - // Now for the tricky bit; we need to know if the key was actually - // released, or if it's still being held. Otherwise, we'll screw up - // the next call to flushKey(). - if (keyToggledOff(keyswitch_state)) { - handleKeyswitchEvent(keycode, row, col, keyswitch_state | INJECTED); - hid::sendKeyboardReport(); - } - */ - // Next, we restore the current state of the report memcpy(Keyboard.keyReport.allkeys, hid_report.allkeys, sizeof(hid_report)); @@ -166,8 +156,6 @@ void Qukeys::flushQueue(void) { } Key Qukeys::keyScanHook(Key mapped_key, byte row, byte col, uint8_t key_state) { - // Uncomment this for debugging, so as not to make flashing difficult - //if (row == 0 && col == 0) return mapped_key; // If Qukeys is turned off, continue to next plugin if (!active_) From 50a074bafc8ac8a273426c325339018b2b5ed954 Mon Sep 17 00:00:00 2001 From: Michael Richters Date: Mon, 4 Dec 2017 14:38:55 -0600 Subject: [PATCH 36/72] Make internal functions private --- src/Kaleidoscope/Qukeys.h | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/Kaleidoscope/Qukeys.h b/src/Kaleidoscope/Qukeys.h index 4c733919..8a466264 100644 --- a/src/Kaleidoscope/Qukeys.h +++ b/src/Kaleidoscope/Qukeys.h @@ -21,8 +21,6 @@ #include #include -// Maximum number of qukeys allowed ­ not actually used -#define QUKEYS_MAX 64 // Maximum length of the pending queue #define QUKEYS_QUEUE_MAX 8 // Total number of keys on the keyboard (assuming full grid) @@ -87,13 +85,6 @@ class Qukeys : public KaleidoscopePlugin { time_limit_ = time_limit; } - static int8_t lookupQukey(uint8_t key_addr); - static void enqueue(uint8_t key_addr); - static int8_t searchQueue(uint8_t key_addr); - static void flushKey(bool qukey_state, uint8_t keyswitch_state); - static void flushQueue(int8_t index); - static void flushQueue(void); - static Qukey * qukeys_; static uint8_t qukeys_count_; @@ -113,6 +104,13 @@ class Qukeys : public KaleidoscopePlugin { bitWrite(qukey_state_[addr / 8], addr % 8, qukey_state); } + static int8_t lookupQukey(uint8_t key_addr); + static void enqueue(uint8_t key_addr); + static int8_t searchQueue(uint8_t key_addr); + static void flushKey(bool qukey_state, uint8_t keyswitch_state); + static void flushQueue(int8_t index); + static void flushQueue(void); + static Key keyScanHook(Key mapped_key, byte row, byte col, uint8_t key_state); static void preReportHook(void); static void postReportHook(void) {} From e842a7cef5a99216270390446290774497bfe18e Mon Sep 17 00:00:00 2001 From: Michael Richters Date: Mon, 4 Dec 2017 14:45:40 -0600 Subject: [PATCH 37/72] Removed trailing underscore from public member variable names --- src/Kaleidoscope/Qukeys.cpp | 17 +++++++++-------- src/Kaleidoscope/Qukeys.h | 10 +++++----- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/Kaleidoscope/Qukeys.cpp b/src/Kaleidoscope/Qukeys.cpp index 29d0236d..aa3a12df 100644 --- a/src/Kaleidoscope/Qukeys.cpp +++ b/src/Kaleidoscope/Qukeys.cpp @@ -36,8 +36,9 @@ Qukey::Qukey(int8_t layer, byte row, byte col, Key alt_keycode) { this->alt_keycode = alt_keycode; } -Qukey * Qukeys::qukeys_; -uint8_t Qukeys::qukeys_count_ = 0; +Qukey * Qukeys::qukeys; +uint8_t Qukeys::qukeys_count = 0; + bool Qukeys::active_ = true; uint16_t Qukeys::time_limit_ = 500; QueueItem Qukeys::key_queue_[QUKEYS_QUEUE_MAX] = {}; @@ -51,12 +52,12 @@ int8_t Qukeys::lookupQukey(uint8_t key_addr) { if (key_addr == QUKEY_UNKNOWN_ADDR) { return QUKEY_NOT_FOUND; } - for (int8_t i = 0; i < qukeys_count_; i++) { - if (qukeys_[i].addr == key_addr) { + for (int8_t i = 0; i < qukeys_count; i++) { + if (qukeys[i].addr == key_addr) { byte row = addr::row(key_addr); byte col = addr::col(key_addr); - if ((qukeys_[i].layer == QUKEY_ALL_LAYERS) || - (qukeys_[i].layer == Layer.lookupActiveLayer(row, col))) { + if ((qukeys[i].layer == QUKEY_ALL_LAYERS) || + (qukeys[i].layer == Layer.lookupActiveLayer(row, col))) { return i; } } @@ -94,7 +95,7 @@ void Qukeys::flushKey(bool qukey_state, uint8_t keyswitch_state) { byte col = addr::col(key_queue_[0].addr); Key keycode = Key_NoKey; if (qukey_state == QUKEY_STATE_ALTERNATE && qukey_index != QUKEY_NOT_FOUND) { - keycode = qukeys_[qukey_index].alt_keycode; + keycode = qukeys[qukey_index].alt_keycode; } else { keycode = Layer.lookup(row, col); } @@ -213,7 +214,7 @@ Key Qukeys::keyScanHook(Key mapped_key, byte row, byte col, uint8_t key_state) { // If the qukey is not in the queue, check its state if (queue_index == QUKEY_NOT_FOUND) { if (getQukeyState(key_addr) == QUKEY_STATE_ALTERNATE) { - return qukeys_[qukey_index].alt_keycode; + return qukeys[qukey_index].alt_keycode; } else { // qukey_state == QUKEY_STATE_PRIMARY return mapped_key; } diff --git a/src/Kaleidoscope/Qukeys.h b/src/Kaleidoscope/Qukeys.h index 8a466264..ef34b09d 100644 --- a/src/Kaleidoscope/Qukeys.h +++ b/src/Kaleidoscope/Qukeys.h @@ -85,8 +85,8 @@ class Qukeys : public KaleidoscopePlugin { time_limit_ = time_limit; } - static Qukey * qukeys_; - static uint8_t qukeys_count_; + static Qukey * qukeys; + static uint8_t qukeys_count; private: static bool active_; @@ -123,7 +123,7 @@ extern kaleidoscope::Qukeys Qukeys; // macro for use in sketch file to simplify definition of qukeys #define QUKEYS(qukey_defs...) { \ - static kaleidoscope::Qukey qukeys[] = { qukey_defs }; \ - Qukeys.qukeys_ = qukeys; \ - Qukeys.qukeys_count_ = sizeof(qukeys) / sizeof(kaleidoscope::Qukey); \ + static kaleidoscope::Qukey qk_table[] = { qukey_defs }; \ + Qukeys.qukeys = qk_table; \ + Qukeys.qukeys_count = sizeof(qk_table) / sizeof(kaleidoscope::Qukey); \ } From 41437c789a66a311407caaa413b4c25addfc4f16 Mon Sep 17 00:00:00 2001 From: Michael Richters Date: Mon, 4 Dec 2017 14:47:27 -0600 Subject: [PATCH 38/72] Spurious comments removed --- src/Kaleidoscope/Qukeys.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Kaleidoscope/Qukeys.h b/src/Kaleidoscope/Qukeys.h index ef34b09d..5a8d9989 100644 --- a/src/Kaleidoscope/Qukeys.h +++ b/src/Kaleidoscope/Qukeys.h @@ -70,7 +70,6 @@ class Qukeys : public KaleidoscopePlugin { public: Qukeys(void); - //static void init(Qukey *qukeys, uint8_t qukeys_count); void begin(void) final; static void activate(void) { active_ = true; @@ -93,7 +92,6 @@ class Qukeys : public KaleidoscopePlugin { static uint16_t time_limit_; static QueueItem key_queue_[QUKEYS_QUEUE_MAX]; static uint8_t key_queue_length_; - //static uint8_t keyswitch_state[]; // Qukey state bitfield static uint8_t qukey_state_[(TOTAL_KEYS) / 8 + ((TOTAL_KEYS) % 8 ? 1 : 0)]; From cd0073f0ae3bc75556c67471108effec3571ebe1 Mon Sep 17 00:00:00 2001 From: Michael Richters Date: Mon, 4 Dec 2017 14:53:02 -0600 Subject: [PATCH 39/72] Prettier formatting of keymap; astyle off for that section --- examples/Qukeys/Qukeys.ino | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/examples/Qukeys/Qukeys.ino b/examples/Qukeys/Qukeys.ino index 27d61a20..862fabb0 100644 --- a/examples/Qukeys/Qukeys.ino +++ b/examples/Qukeys/Qukeys.ino @@ -6,25 +6,27 @@ enum { MACRO_TOGGLE_QUKEYS }; +// *INDENT-OFF* KEYMAPS( [0] = KEYMAP_STACKED - ( - Key_NoKey, Key_1, Key_2, Key_3, Key_4, Key_5, Key_NoKey, - Key_Backtick, Key_Q, Key_W, Key_E, Key_R, Key_T, Key_Tab, - Key_PageUp, Key_A, Key_S, Key_D, Key_F, Key_G, - Key_PageDown, Key_Z, Key_X, Key_C, Key_V, Key_B, Key_Escape, - - Key_LeftControl, Key_Backspace, Key_LeftGui, Key_LeftShift, - ___, - - M(MACRO_TOGGLE_QUKEYS), Key_6, Key_7, Key_8, Key_9, Key_0, Key_skip, - Key_Enter, Key_Y, Key_U, Key_I, Key_O, Key_P, Key_Equals, - Key_H, Key_J, Key_K, Key_L, Key_Semicolon, Key_Quote, - Key_skip, Key_N, Key_M, Key_Comma, Key_Period, Key_Slash, Key_Minus, - - Key_RightShift, Key_RightAlt, Key_Spacebar, Key_RightControl, - ___), + ( + Key_NoKey, Key_1, Key_2, Key_3, Key_4, Key_5, Key_NoKey, + Key_Backtick, Key_Q, Key_W, Key_E, Key_R, Key_T, Key_Tab, + Key_PageUp, Key_A, Key_S, Key_D, Key_F, Key_G, + Key_PageDown, Key_Z, Key_X, Key_C, Key_V, Key_B, Key_Escape, + + Key_LeftControl, Key_Backspace, Key_LeftGui, Key_LeftShift, + ___, + + M(MACRO_TOGGLE_QUKEYS), Key_6, Key_7, Key_8, Key_9, Key_0, Key_skip, + Key_Enter, Key_Y, Key_U, Key_I, Key_O, Key_P, Key_Equals, + Key_H, Key_J, Key_K, Key_L, Key_Semicolon, Key_Quote, + Key_skip, Key_N, Key_M, Key_Comma, Key_Period, Key_Slash, Key_Minus, + + Key_RightShift, Key_RightAlt, Key_Spacebar, Key_RightControl, + ___), ) +// *INDENT-ON* // Defining a macro (on the "any" key: see above) to toggle Qukeys on and off const macro_t *macroAction(uint8_t macro_index, uint8_t key_state) { From 40d2e5bc350addce49bfeec446dbd2e3596c87ab Mon Sep 17 00:00:00 2001 From: Michael Richters Date: Mon, 4 Dec 2017 22:48:30 -0600 Subject: [PATCH 40/72] Re-wrote README --- README.md | 230 ++++++++++++++++++------------------------------------ 1 file changed, 74 insertions(+), 156 deletions(-) diff --git a/README.md b/README.md index 229b76bd..c9be484b 100644 --- a/README.md +++ b/README.md @@ -11,159 +11,77 @@ ## Concept -This Kaleidoscope plugin will allow you to overload keys on your -keyboard so that they produce one keycode (i.e. symbol) when tapped, -and a different keycode -- most likely a modifier (e.g. `shift` or -`alt`) when held. There are already two Kaleidoscop plugins that -provide this same functionality -([DualUse](https://github.com/keyboardio/Kaleidoscope-DualUse) and -[SpaceCadet](https://github.com/keyboardio/Kaleidoscope-SpaceCadet)), -but those plugins were designed primarily to allow you to overload -keys whose primary function is as a modifier, and use them to produce -printable keycodes when tapped. The `Qukey` design is different; it's -meant to overload letter keys that are usually tapped and let you use -them as alternate modifier keys (though the design is flexible enough -that any keycode can be the secondary one -- I just haven't thought of -a realistic use for that yet). - -## Design goals - -* I want users to be able to type at fast pace on `Qukey`s, without - accidentally triggering the secondary function, and preserving the - order of input keystrokes. - -* I want users to be able to rapidly invoke the secondary function of - a `Qukey` without having to wait for a timeout. - -* I want `Qukey`s to be useful as modifiers with other input devices - (e.g. a mouse), without having to wait a long time for a - timeout. I'm guessing that 200ms is acceptable, but not anything - longer than that. - -* I want physical *keys* on the keyboard to be defined independently - of each other. I don't want the plugin to act on *keycodes*; simply - translating one keycode to another. - -## Schrödinger's Key - -The idea is that a `Qukey` will work just like a particle in a -superposition of quantum states until some event causes the "waveform" -to "collapse" into just one of the two states. (The name "qukey" is a -reference to "qubit", a term used in quantum computing.) - -When a `Qukey` is pressed, its state will be indeterminate (like a -superposition of quantum states); no keycode for that key will be -transmitted to the host until some other event (or sequence of events) -causes the state to be decided. Once the state is decided, the `Qukey` -will stay in that state until the key is released. The possible -triggers for "collapsing" the state of a `Qukey` are: - -* Timeout - -* Release of the `qukey` - -* Press and subsequent release of another key - -### Timeout - -When a `qukey` times out, it falls into its secondary state (probably -a modifier).The timeout should be fairly short ­ just a bit longer -than the longest "reasonable" time of a tap, such that you very rarely -(ideally never) hold a `qukey` long enough to trigger the secondary -function when intending a tap, but short enough that you don't feel a -need to wait a while to use it with a mouse. - -### Release of `qukey` - -If the `qukey` is released before it times out, and before any other -keys are pressed, it falls into its primary state. This is slightly -different from the timeout case, because we then add the `qukey`'s -primary keycode to the HID report when the key toggles off (rather -than when it toggles on, which is the behaviour of a normal key). It -will get cleared at the end of the cycle, but there's still no need to -send any extra HID reports. - -### Interactions with subsequent keys - -This is where things get tricky. Because fast typists might have -multiple keys pressed simultaneously, we need to keep track of a -sequence of subsequent keypresses, starting with the first `qukey` to -toggle on. Basically, if any subsequent key is released before a -`qukey` that was pressed before it, that `qukey` should fall into its -secondary state (e.g. a modifier). - -In order to do this, we need to suppress the output of any subsequent -keys, as well as the `qukey`. Basically, this means storing a list of -keys that have been pressed, but which have not affected the report -yet. This also means that when a key in that list toggles off, every -`qukey` preceding it in the list has its state decided. - -When a subsequent key is released, we now need to send a series of HID -reports to preserve the order that the keypresses were detected by the -keyboard. To do that, we add the one keycode at a time to the report -and send it, until we reach the next `qukey` in the list that has an -indeterminate state. - -The really tricky part is that the key *releases* might come in any -order, so if we have a sequence that goes like this, we could get into -trouble: - -1. press `qk(A,shift)` -2. press `B` -3. press `C` -4. press `qk(D,ctrl)` -5. press `space` -6. release `B` - -In this case, `B` is released after the second `qukey` was pressed, -but I don't think it's actually a problem. Maybe. - -We might also send a series of reports when a `qukey` times out, of -course, but this is much more straightforward. - -### Another way to address overlap - -An alternative method is to only allow the `qukey` to fall into its -secondary (modifier) state if there is only one non-`qukey` press -following. So, in the previous example, once step 3 was reached, the -`qukey` would become `A`. I don't think this works as well, but it -would err more on the side of defaulting to the primary key, which -would mean fewer unintended modifiers while typing, and enforce -(slightly) more deliberate usage to get the secondary keycode. This -would make for another interesting possibility, though divergent from -the idea that I started with ­ the state could change while the key is -pressed: - -1. press `qk(A,shift)` -2. press `B` -3. press `C` -> output: `abc` -4. release `B` -> output: `shift` held? - -It wouldn't require any additional data structures, so the algorithm -to use could be user-configurable. - -## Data structures - -I want to store a table, indexed by key id (layer, row & column). Each -entry would be an object containing: - -* primary keycode (or `Key` object) -* secondary keycode (or `Key` object) -* time limit (set when key toggles on, based on timeout) -* flags (bitfield ­ maybe useful) - -I think I need the flags byte in order to signal which state the -`qukey` has once its state has collapsed, but maybe I can get away -with just using the time limit value. I could set that to zero (or -maybe some other constant?) to signal that the `qukey` is in its -primary state, and if the state collapses to secondary before the -timeout, just reset the time limit to `millis()`. Or maybe I never -need that, because it should only send the primary keycode as a "tap" -in a single report. - -In addition, I need to store a list of keypresses, ideally in -something like `std::queue`, though I think that's not available to -Arduino (it looks like someone has written a `QueueArray` library, -though). This would just be a FIFO buffer of key ids (row & column ­ -maybe layer, too, but that might even be undesirable, because we might -try to expand `Qukey` to allow layer switching, too). \ No newline at end of file +This Kaleidoscope plugin allows you to overload keys on your keyboard so that they produce +one keycode (i.e. symbol) when tapped, and a different keycode -- most likely a modifier +(e.g. `shift` or `alt`) -- when held. + + +## Setup + +- Clone the module -- In your sketch directory (e.g. `Model01-Firmware/`: +``` +git submodule add Kaleidoscope-Qukeys https://github.com/gedankenlab/Kaleidoscope-Qukeys.git +``` +- Include the header file: +``` +#include +``` +- Use the plugin in the `setup()` function: +``` +Kaleidoscope.use(&Qukeys); +``` +- Define some `Qukeys`: +``` +QUKEYS( + kaleidoscope::Qukey(0, 2, 1, Key_LeftGui), // A/cmd + kaleidoscope::Qukey(0, 2, 2, Key_LeftAlt), // S/alt + kaleidoscope::Qukey(0, 2, 3, Key_LeftControl), // D/ctrl + kaleidoscope::Qukey(0, 2, 4, Key_LeftShift) // F/shift +) +``` + +`Qukeys` will work best if it's the first plugin in the `use()` list, because when typing +overlap occurs, it will (temporarily) mask keys and block them from being processed by +other plugins. If those other plugins handle the keypress events first, it may not work as +expected. It doesn't _need_ to be first, but if it's `use()`'d after another plugin that +handles typing events, especially one that sends extra keyboard HID reports, it is more +likely to generate errors and out-of-order events. + + +## Configuration + +- set timeout + +- activate/deactivate `Qukeys` + +- see the + [example](https://github.com/gedankenlab/Kaleidoscope-Qukeys/blob/master/examples/Qukeys/Qukeys.ino) + for a way to turn `Qukeys` on and off, using Kaleidoscope-Macros + + +## Design & Implementation + +When a `Qukey` is pressed, it doesn't immediately add a corresponding keycode to the HID +report; it adds that key to a queue, and waits until one of three things happens: + +1. a time limit is reached + +2. the `Qukey` is released + +3. a subsequently-pressed key is released + +Until one of those conditions is met, all subsequent keypresses are simply added to the +queue, and no new reports are sent to the host. Once a condition is met, the `Qukey` is +flushed from the queue, and so are any subsequent keypresses (up to, but not including, +the next `Qukey` that is still pressed). + +Basically, if you hold the `Qukey`, then press and release some other key, you'll get the +alternate keycode (probably a modifier) for the `Qukey`, even if you don't wait for a +timeout. If you're typing quickly, and there's some overlap between two keypresses, you +won't get the alternate keycode, and the keys will be reported in the order that they were +pressed -- as long as the keys are released in the same order they were pressed. + +The time limit is mainly there so that a `Qukey` can be used as a modifier (in its +alternate state) with a second input device (e.g. a mouse). It can be quite short (200ms +is probably short enough) -- as long as your "taps" while typing are shorter than the time +limit, you won't get any unintended alternate keycodes. From 421556f7cc5cce95464b1876ecc85886328a6f92 Mon Sep 17 00:00:00 2001 From: Michael Richters Date: Mon, 4 Dec 2017 23:22:03 -0600 Subject: [PATCH 41/72] Correct array initialization --- src/Kaleidoscope/Qukeys.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Kaleidoscope/Qukeys.cpp b/src/Kaleidoscope/Qukeys.cpp index aa3a12df..311feb93 100644 --- a/src/Kaleidoscope/Qukeys.cpp +++ b/src/Kaleidoscope/Qukeys.cpp @@ -41,7 +41,7 @@ uint8_t Qukeys::qukeys_count = 0; bool Qukeys::active_ = true; uint16_t Qukeys::time_limit_ = 500; -QueueItem Qukeys::key_queue_[QUKEYS_QUEUE_MAX] = {}; +QueueItem Qukeys::key_queue_[] = {}; uint8_t Qukeys::key_queue_length_ = 0; byte Qukeys::qukey_state_[] = {}; From 94db15ad0862fcaade3e94a114c030d9d9abd798 Mon Sep 17 00:00:00 2001 From: Michael Richters Date: Tue, 5 Dec 2017 11:19:35 -0600 Subject: [PATCH 42/72] Use a static variable instead of INJECTED keyswitch state We need to prevent infinite loops, and also stop handling keyswitch events when flushing the queue, but if we do this by setting the `INJECTED` bit in `keyswitch_state` when calling `handleKeyswitchEvent()`, then other plugins will ignore those events, which is not what we want; we need them to process those events as if they were real keypresses. The solution is to use a static boolean to let us know if the queue is being flushed. --- src/Kaleidoscope/Qukeys.cpp | 13 ++++++++++--- src/Kaleidoscope/Qukeys.h | 1 + 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/Kaleidoscope/Qukeys.cpp b/src/Kaleidoscope/Qukeys.cpp index 311feb93..a48fa752 100644 --- a/src/Kaleidoscope/Qukeys.cpp +++ b/src/Kaleidoscope/Qukeys.cpp @@ -100,6 +100,10 @@ void Qukeys::flushKey(bool qukey_state, uint8_t keyswitch_state) { keycode = Layer.lookup(row, col); } + // Before calling handleKeyswitchEvent() below, make sure Qukeys knows not to handle + // these events: + flushing_queue_ = true; + // Since we're in the middle of the key scan, we don't necessarily // have a full HID report, and we don't want to accidentally turn // off keys that the scan hasn't reached yet, so we force the @@ -114,7 +118,7 @@ void Qukeys::flushKey(bool qukey_state, uint8_t keyswitch_state) { // we can ignore it and don't start an infinite loop. It would be // nice if we could use key_state to also indicate which plugin // injected the key. - handleKeyswitchEvent(keycode, row, col, IS_PRESSED | INJECTED); + handleKeyswitchEvent(keycode, row, col, IS_PRESSED); // Now we send the report (if there were any changes) hid::sendKeyboardReport(); @@ -123,7 +127,10 @@ void Qukeys::flushKey(bool qukey_state, uint8_t keyswitch_state) { // Last, if the key is still down, add its code back in if (! keyToggledOn(keyswitch_state)) - handleKeyswitchEvent(keycode, row, col, IS_PRESSED | WAS_PRESSED | INJECTED); + handleKeyswitchEvent(keycode, row, col, IS_PRESSED | WAS_PRESSED); + + // Now that we're done sending the report(s), Qukeys can process events again: + flushing_queue_ = true; // Shift the queue, so key_queue[0] is always the first key that gets processed for (byte i = 0; i < key_queue_length_; i++) { @@ -163,7 +170,7 @@ Key Qukeys::keyScanHook(Key mapped_key, byte row, byte col, uint8_t key_state) { return mapped_key; // If the key was injected (from the queue being flushed), continue to next plugin - if (key_state & INJECTED) + if (flushing_queue_) return mapped_key; // If the key isn't active, and didn't just toggle off, continue to next plugin diff --git a/src/Kaleidoscope/Qukeys.h b/src/Kaleidoscope/Qukeys.h index 5a8d9989..2e920e69 100644 --- a/src/Kaleidoscope/Qukeys.h +++ b/src/Kaleidoscope/Qukeys.h @@ -92,6 +92,7 @@ class Qukeys : public KaleidoscopePlugin { static uint16_t time_limit_; static QueueItem key_queue_[QUKEYS_QUEUE_MAX]; static uint8_t key_queue_length_; + static bool flushing_queue_; // Qukey state bitfield static uint8_t qukey_state_[(TOTAL_KEYS) / 8 + ((TOTAL_KEYS) % 8 ? 1 : 0)]; From 71d2be126457a4d8f2f87ffdabeecf59924c1841 Mon Sep 17 00:00:00 2001 From: Michael Richters Date: Tue, 5 Dec 2017 12:32:13 -0600 Subject: [PATCH 43/72] Define flushing_queue_ & fix logic error --- src/Kaleidoscope/Qukeys.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Kaleidoscope/Qukeys.cpp b/src/Kaleidoscope/Qukeys.cpp index a48fa752..ecabebad 100644 --- a/src/Kaleidoscope/Qukeys.cpp +++ b/src/Kaleidoscope/Qukeys.cpp @@ -44,6 +44,7 @@ uint16_t Qukeys::time_limit_ = 500; QueueItem Qukeys::key_queue_[] = {}; uint8_t Qukeys::key_queue_length_ = 0; byte Qukeys::qukey_state_[] = {}; +bool Qukeys::flushing_queue_ = false; // Empty constructor; nothing is stored at the instance level Qukeys::Qukeys(void) {} @@ -130,7 +131,7 @@ void Qukeys::flushKey(bool qukey_state, uint8_t keyswitch_state) { handleKeyswitchEvent(keycode, row, col, IS_PRESSED | WAS_PRESSED); // Now that we're done sending the report(s), Qukeys can process events again: - flushing_queue_ = true; + flushing_queue_ = false; // Shift the queue, so key_queue[0] is always the first key that gets processed for (byte i = 0; i < key_queue_length_; i++) { From 778cb705bcd33514e1238aa8db13d3059436703d Mon Sep 17 00:00:00 2001 From: Michael Richters Date: Mon, 18 Dec 2017 22:36:49 -0600 Subject: [PATCH 44/72] Correct for timer overflow When the value returned by `millis()` overflows (after ~two months of runtime), a straight comparison to the end time would fail. This wasn't a really big deal, but it is possible to do it correctly, and in the process, reduce the size of the time values stored from 32 bits to 16 bits (~one minute), since the largest conceivable useful timeout is measured in seconds (at most). --- src/Kaleidoscope/Qukeys.cpp | 8 ++++---- src/Kaleidoscope/Qukeys.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Kaleidoscope/Qukeys.cpp b/src/Kaleidoscope/Qukeys.cpp index ecabebad..0eb95ddd 100644 --- a/src/Kaleidoscope/Qukeys.cpp +++ b/src/Kaleidoscope/Qukeys.cpp @@ -72,7 +72,7 @@ void Qukeys::enqueue(uint8_t key_addr) { flushQueue(); } key_queue_[key_queue_length_].addr = key_addr; - key_queue_[key_queue_length_].flush_time = millis() + time_limit_; + key_queue_[key_queue_length_].start_time = (uint16_t)millis(); key_queue_length_++; addr::mask(key_addr); } @@ -235,11 +235,11 @@ Key Qukeys::keyScanHook(Key mapped_key, byte row, byte col, uint8_t key_state) { void Qukeys::preReportHook(void) { // If the qukey has been held longer than the time limit, set its // state to the alternate keycode and add it to the report - uint32_t current_time = millis(); + uint16_t current_time = (uint16_t)millis(); while (key_queue_length_ > 0) { if (lookupQukey(key_queue_[0].addr) == QUKEY_NOT_FOUND) { flushKey(QUKEY_STATE_PRIMARY, IS_PRESSED | WAS_PRESSED); - } else if (current_time > key_queue_[0].flush_time) { + } else if ((current_time - key_queue_[0].start_time) > time_limit_) { flushKey(QUKEY_STATE_ALTERNATE, IS_PRESSED | WAS_PRESSED); } else { break; @@ -256,7 +256,7 @@ void Qukeys::begin() { // initializing the key_queue seems unnecessary, actually for (int8_t i = 0; i < QUKEYS_QUEUE_MAX; i++) { key_queue_[i].addr = QUKEY_UNKNOWN_ADDR; - key_queue_[i].flush_time = 0; + key_queue_[i].start_time = 0; } key_queue_length_ = 0; diff --git a/src/Kaleidoscope/Qukeys.h b/src/Kaleidoscope/Qukeys.h index 2e920e69..2994bdff 100644 --- a/src/Kaleidoscope/Qukeys.h +++ b/src/Kaleidoscope/Qukeys.h @@ -57,7 +57,7 @@ struct Qukey { // Data structure for an entry in the key_queue struct QueueItem { uint8_t addr; // keyswitch coordinates - uint32_t flush_time; // time past which a qukey gets flushed + uint16_t start_time; // time a queued key was pressed }; // The plugin itself From a06447211f7d67c02b8933eb24fe0b84a2453a5d Mon Sep 17 00:00:00 2001 From: Michael Richters Date: Mon, 18 Dec 2017 22:44:34 -0600 Subject: [PATCH 45/72] Changed default timeout from 500ms to 250ms --- src/Kaleidoscope/Qukeys.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Kaleidoscope/Qukeys.cpp b/src/Kaleidoscope/Qukeys.cpp index 0eb95ddd..6d566abf 100644 --- a/src/Kaleidoscope/Qukeys.cpp +++ b/src/Kaleidoscope/Qukeys.cpp @@ -40,7 +40,7 @@ Qukey * Qukeys::qukeys; uint8_t Qukeys::qukeys_count = 0; bool Qukeys::active_ = true; -uint16_t Qukeys::time_limit_ = 500; +uint16_t Qukeys::time_limit_ = 250; QueueItem Qukeys::key_queue_[] = {}; uint8_t Qukeys::key_queue_length_ = 0; byte Qukeys::qukey_state_[] = {}; From 238f1faaff189089e7ed19e3d1f3ddb3eca19e61 Mon Sep 17 00:00:00 2001 From: Michael Richters Date: Mon, 15 Jan 2018 13:18:15 -0600 Subject: [PATCH 46/72] Added support for Travis-CI automated build testing --- .travis.yml | 14 ++++++++++++++ README.md | 4 ++-- 2 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..1fbeb196 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,14 @@ +dist: trusty +sudo: false +os: + - linux +install: + - git clone --recursive https://github.com/keyboardio/Arduino-Boards hardware/keyboardio/avr +script: + - make travis-test BOARD_HARDWARE_PATH=$(pwd)/hardware +notifications: + email: + on_success: change + on_failure: change +cache: + ccache: true diff --git a/README.md b/README.md index c9be484b..bac8b772 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,8 @@ ![status][st:experimental] [![Build Status][travis:image]][travis:status] - [travis:image]: https://travis-ci.org/gedankenlab/Kaleidoscope-Qukey.svg?branch=master - [travis:status]: https://travis-ci.org/gedankenlab/Kaleidoscope-Qukey + [travis:image]: https://travis-ci.org/gedankenlab/Kaleidoscope-Qukeys.svg?branch=master + [travis:status]: https://travis-ci.org/gedankenlab/Kaleidoscope-Qukeys [st:stable]: https://img.shields.io/badge/stable-✔-black.svg?style=flat&colorA=44cc11&colorB=494e52 [st:broken]: https://img.shields.io/badge/broken-X-black.svg?style=flat&colorA=e05d44&colorB=494e52 From f6f5f9ae3b972cf5bf8e2fc14511efd6dcc4c6d8 Mon Sep 17 00:00:00 2001 From: Michael Richters Date: Tue, 30 Jan 2018 23:18:32 -0600 Subject: [PATCH 47/72] Fixed layer-shift bugs Layer changes didn't take effect immediately, and a ShiftToLayer(n) alternate keycode was never released properly, so it was effectively a LockLayer(n) key. This patch fixes both problems. Fixes #28 --- src/Kaleidoscope/Qukeys.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Kaleidoscope/Qukeys.cpp b/src/Kaleidoscope/Qukeys.cpp index 6d566abf..60a5dde8 100644 --- a/src/Kaleidoscope/Qukeys.cpp +++ b/src/Kaleidoscope/Qukeys.cpp @@ -97,8 +97,6 @@ void Qukeys::flushKey(bool qukey_state, uint8_t keyswitch_state) { Key keycode = Key_NoKey; if (qukey_state == QUKEY_STATE_ALTERNATE && qukey_index != QUKEY_NOT_FOUND) { keycode = qukeys[qukey_index].alt_keycode; - } else { - keycode = Layer.lookup(row, col); } // Before calling handleKeyswitchEvent() below, make sure Qukeys knows not to handle @@ -200,10 +198,16 @@ Key Qukeys::keyScanHook(Key mapped_key, byte row, byte col, uint8_t key_state) { if (keyToggledOff(key_state)) { // If the key isn't in the key_queue, proceed if (queue_index == QUKEY_NOT_FOUND) { + // If a qukey was released while in its alternate state, change its keycode + if (qukey_index != QUKEY_NOT_FOUND && + getQukeyState(key_addr) == QUKEY_STATE_ALTERNATE) { + return qukeys[qukey_index].alt_keycode; + } return mapped_key; } flushQueue(queue_index); - return mapped_key; + // flushQueue() has already handled this key release + return Key_NoKey; } // Otherwise, the key is still pressed From 0fa876f912294dcb031b4b2002fc1f009488d45e Mon Sep 17 00:00:00 2001 From: Michael Richters Date: Wed, 31 Jan 2018 22:56:11 -0600 Subject: [PATCH 48/72] Added support for DualUse key definitions in keymap (#27) With this change, Qukeys reads DualUse key defs from the keymap, and treats them as Qukeys, within the limitations of normal DualUse keys. Primary keycodes can only be unmodified, basic keys, and alternate keycodes can only be modifiers or layer-switch keys. --- .astylerc | 6 ++ README.md | 9 +++ examples/Qukeys/Qukeys.ino | 47 ++++++++++---- src/Kaleidoscope/Qukeys.cpp | 121 ++++++++++++++++++++++++++++-------- src/Kaleidoscope/Qukeys.h | 11 ++++ 5 files changed, 154 insertions(+), 40 deletions(-) create mode 100644 .astylerc diff --git a/.astylerc b/.astylerc new file mode 100644 index 00000000..7ced720e --- /dev/null +++ b/.astylerc @@ -0,0 +1,6 @@ +--style=google +--unpad-paren +--pad-header +--pad-oper +--indent-classes +--indent=spaces=2 diff --git a/README.md b/README.md index bac8b772..7b0d90be 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,15 @@ likely to generate errors and out-of-order events. [example](https://github.com/gedankenlab/Kaleidoscope-Qukeys/blob/master/examples/Qukeys/Qukeys.ino) for a way to turn `Qukeys` on and off, using Kaleidoscope-Macros +### DualUse key definitions + +In addition to normal `Qukeys` described above, Kaleidoscope-Qukeys also treats +DualUse keys in the keymap as `Qukeys`. See [the Kaleidoscope-DualUse +documentation](https://github.com/keyboardio/Kaleidoscope-DualUse#keymap-markup) +for a thorough description of how to define DualUse keys. This makes `Qukeys` a +drop-in replacement for the `DualUse` plugin, without the need to edit the +keymap. + ## Design & Implementation diff --git a/examples/Qukeys/Qukeys.ino b/examples/Qukeys/Qukeys.ino index 862fabb0..cfc33638 100644 --- a/examples/Qukeys/Qukeys.ino +++ b/examples/Qukeys/Qukeys.ino @@ -10,21 +10,41 @@ enum { MACRO_TOGGLE_QUKEYS }; KEYMAPS( [0] = KEYMAP_STACKED ( - Key_NoKey, Key_1, Key_2, Key_3, Key_4, Key_5, Key_NoKey, - Key_Backtick, Key_Q, Key_W, Key_E, Key_R, Key_T, Key_Tab, - Key_PageUp, Key_A, Key_S, Key_D, Key_F, Key_G, - Key_PageDown, Key_Z, Key_X, Key_C, Key_V, Key_B, Key_Escape, + Key_NoKey, Key_1, Key_2, Key_3, Key_4, Key_5, Key_NoKey, + Key_Backtick, Key_Q, Key_W, Key_E, Key_R, Key_T, Key_Tab, + Key_PageUp, Key_A, Key_S, Key_D, Key_F, Key_G, + Key_PageDown, Key_Z, Key_X, Key_C, Key_V, Key_B, Key_Escape, - Key_LeftControl, Key_Backspace, Key_LeftGui, Key_LeftShift, - ___, + Key_LeftControl, Key_Backspace, Key_LeftGui, Key_LeftShift, + Key_Q, - M(MACRO_TOGGLE_QUKEYS), Key_6, Key_7, Key_8, Key_9, Key_0, Key_skip, - Key_Enter, Key_Y, Key_U, Key_I, Key_O, Key_P, Key_Equals, - Key_H, Key_J, Key_K, Key_L, Key_Semicolon, Key_Quote, - Key_skip, Key_N, Key_M, Key_Comma, Key_Period, Key_Slash, Key_Minus, + M(MACRO_TOGGLE_QUKEYS), Key_6, Key_7, Key_8, Key_9, Key_0, Key_skip, + Key_Enter, Key_Y, Key_U, Key_I, Key_O, Key_P, Key_Equals, + Key_H, SFT_T(J), CTL_T(K), ALT_T(L), GUI_T(Semicolon), Key_Quote, + Key_skip, Key_N, Key_M, Key_Comma, Key_Period, Key_Slash, Key_Minus, - Key_RightShift, Key_RightAlt, Key_Spacebar, Key_RightControl, - ___), + Key_RightShift, Key_RightAlt, Key_Spacebar, Key_RightControl, + LT(1,E) + ), + [1] = KEYMAP_STACKED + ( + ___, Key_B, Key_C, Key_D, Key_E, Key_F, Key_G, + Key_A, Key_B, Key_C, Key_D, Key_E, Key_F, Key_G, + Key_A, Key_B, Key_C, Key_D, Key_E, Key_F, + Key_A, Key_B, Key_C, Key_D, Key_E, Key_F, Key_G, + + Key_1, Key_2, Key_3, Key_4, + ___, + + + ___, Key_B, Key_C, Key_D, Key_E, Key_F, Key_G, + Key_A, Key_B, Key_C, Key_D, Key_E, Key_F, Key_G, + Key_A, Key_B, Key_C, Key_D, Key_E, Key_F, + Key_A, Key_B, Key_C, Key_D, Key_E, Key_F, Key_G, + + Key_1, Key_2, Key_3, Key_4, + ___ + ), ) // *INDENT-ON* @@ -47,7 +67,8 @@ void setup() { kaleidoscope::Qukey(0, 2, 1, Key_LeftGui), // A/cmd kaleidoscope::Qukey(0, 2, 2, Key_LeftAlt), // S/alt kaleidoscope::Qukey(0, 2, 3, Key_LeftControl), // D/ctrl - kaleidoscope::Qukey(0, 2, 4, Key_LeftShift) // F/shift + kaleidoscope::Qukey(0, 2, 4, Key_LeftShift), // F/shift + kaleidoscope::Qukey(0, 3, 6, ShiftToLayer(1)) // Q/layer-shift (on `fn`) ) Qukeys.setTimeout(200); diff --git a/src/Kaleidoscope/Qukeys.cpp b/src/Kaleidoscope/Qukeys.cpp index 60a5dde8..1c652b74 100644 --- a/src/Kaleidoscope/Qukeys.cpp +++ b/src/Kaleidoscope/Qukeys.cpp @@ -20,6 +20,8 @@ #include #include #include +#include +#include #ifdef ARDUINO_VIRTUAL #define debug_print(...) printf(__VA_ARGS__) @@ -30,6 +32,39 @@ namespace kaleidoscope { +bool isDualUse(Key k) { + if (k.raw < ranges::DU_FIRST || k.raw > ranges::DU_LAST) + return false; + return true; +} + +Key getDualUsePrimaryKey(Key k) { + if (k.raw >= ranges::DUM_FIRST && k.raw <= ranges::DUM_LAST) { + k.raw -= ranges::DUM_FIRST; + k.flags = 0; + } else if (k.raw >= ranges::DUL_FIRST && k.raw <= ranges::DUL_LAST) { + k.raw -= ranges::DUL_FIRST; + k.flags = 0; + } + return k; +} + +Key getDualUseAlternateKey(Key k) { + if (k.raw >= ranges::DUM_FIRST && k.raw <= ranges::DUM_LAST) { + k.raw -= ranges::DUM_FIRST; + k.raw = (k.raw >> 8) + Key_LeftControl.keyCode; + } else if (k.raw >= ranges::DUL_FIRST && k.raw <= ranges::DUL_LAST) { + k.raw -= ranges::DUL_FIRST; + byte layer = k.flags; + // Should be `ShiftToLayer(layer)`, but that gives "narrowing conversion" + // warnings that I can't figure out how to resolve + k.keyCode = layer + LAYER_SHIFT_OFFSET; + k.flags = KEY_FLAGS | SYNTHETIC | SWITCH_TO_KEYMAP; + } + return k; +} + + Qukey::Qukey(int8_t layer, byte row, byte col, Key alt_keycode) { this->layer = layer; this->addr = addr::addr(row, col); @@ -89,14 +124,20 @@ int8_t Qukeys::searchQueue(uint8_t key_addr) { void Qukeys::flushKey(bool qukey_state, uint8_t keyswitch_state) { addr::unmask(key_queue_[0].addr); int8_t qukey_index = lookupQukey(key_queue_[0].addr); - if (qukey_index != QUKEY_NOT_FOUND) { - setQukeyState(key_queue_[0].addr, qukey_state); - } + bool is_qukey = (qukey_index != QUKEY_NOT_FOUND); byte row = addr::row(key_queue_[0].addr); byte col = addr::col(key_queue_[0].addr); + bool is_dual_use = isDualUse(Layer.lookup(row, col)); Key keycode = Key_NoKey; - if (qukey_state == QUKEY_STATE_ALTERNATE && qukey_index != QUKEY_NOT_FOUND) { - keycode = qukeys[qukey_index].alt_keycode; + if (is_qukey || is_dual_use) { + setQukeyState(key_queue_[0].addr, qukey_state); + if (qukey_state == QUKEY_STATE_ALTERNATE) { + if (is_dual_use) { + keycode = getDualUseAlternateKey(keycode); + } else { // is_qukey + keycode = qukeys[qukey_index].alt_keycode; + } + } } // Before calling handleKeyswitchEvent() below, make sure Qukeys knows not to handle @@ -125,7 +166,7 @@ void Qukeys::flushKey(bool qukey_state, uint8_t keyswitch_state) { memcpy(Keyboard.keyReport.allkeys, hid_report.allkeys, sizeof(hid_report)); // Last, if the key is still down, add its code back in - if (! keyToggledOn(keyswitch_state)) + if (keyswitch_state | IS_PRESSED) handleKeyswitchEvent(keycode, row, col, IS_PRESSED | WAS_PRESSED); // Now that we're done sending the report(s), Qukeys can process events again: @@ -166,28 +207,42 @@ Key Qukeys::keyScanHook(Key mapped_key, byte row, byte col, uint8_t key_state) { // If Qukeys is turned off, continue to next plugin if (!active_) - return mapped_key; + return getDualUsePrimaryKey(mapped_key); + + // get key addr & qukey (if any) + uint8_t key_addr = addr::addr(row, col); + int8_t qukey_index = lookupQukey(key_addr); - // If the key was injected (from the queue being flushed), continue to next plugin - if (flushing_queue_) + // If the key was injected (from the queue being flushed) + if (flushing_queue_) { + // If it's a DualUse key, we still need to update its keycode + if (isDualUse(mapped_key)) { + if (getQukeyState(key_addr) == QUKEY_STATE_ALTERNATE) { + return getDualUseAlternateKey(mapped_key); + } else { + return getDualUsePrimaryKey(mapped_key); + } + } + // ...otherwise, just continue to the next plugin return mapped_key; + } // If the key isn't active, and didn't just toggle off, continue to next plugin if (!keyIsPressed(key_state) && !keyWasPressed(key_state)) - return mapped_key; - - // get key addr & qukey (if any) - uint8_t key_addr = addr::addr(row, col); - int8_t qukey_index = lookupQukey(key_addr); + return getDualUsePrimaryKey(mapped_key); // If the key was just pressed: if (keyToggledOn(key_state)) { // If the queue is empty and the key isn't a qukey, proceed: if (key_queue_length_ == 0 && - qukey_index == QUKEY_NOT_FOUND) + ! isDualUse(mapped_key) && + qukey_index == QUKEY_NOT_FOUND) { return mapped_key; + } + // Otherwise, queue the key and stop processing: enqueue(key_addr); + // flushQueue() has already handled this key release return Key_NoKey; } @@ -199,21 +254,25 @@ Key Qukeys::keyScanHook(Key mapped_key, byte row, byte col, uint8_t key_state) { // If the key isn't in the key_queue, proceed if (queue_index == QUKEY_NOT_FOUND) { // If a qukey was released while in its alternate state, change its keycode - if (qukey_index != QUKEY_NOT_FOUND && - getQukeyState(key_addr) == QUKEY_STATE_ALTERNATE) { - return qukeys[qukey_index].alt_keycode; + if (isDualUse(mapped_key)) { + if (getQukeyState(key_addr) == QUKEY_STATE_ALTERNATE) + return getDualUseAlternateKey(mapped_key); + return getDualUsePrimaryKey(mapped_key); + } else if (qukey_index != QUKEY_NOT_FOUND) { + if (getQukeyState(key_addr) == QUKEY_STATE_ALTERNATE) + return qukeys[qukey_index].alt_keycode; + return mapped_key; } - return mapped_key; } flushQueue(queue_index); - // flushQueue() has already handled this key release return Key_NoKey; } // Otherwise, the key is still pressed // If the key is not a qukey: - if (qukey_index == QUKEY_NOT_FOUND) { + if (qukey_index == QUKEY_NOT_FOUND && + ! isDualUse(mapped_key)) { // If the key was pressed before the keys in the queue, proceed: if (queue_index == QUKEY_NOT_FOUND) { return mapped_key; @@ -226,9 +285,11 @@ Key Qukeys::keyScanHook(Key mapped_key, byte row, byte col, uint8_t key_state) { // If the qukey is not in the queue, check its state if (queue_index == QUKEY_NOT_FOUND) { if (getQukeyState(key_addr) == QUKEY_STATE_ALTERNATE) { + if (isDualUse(mapped_key)) + return getDualUseAlternateKey(mapped_key); return qukeys[qukey_index].alt_keycode; } else { // qukey_state == QUKEY_STATE_PRIMARY - return mapped_key; + return getDualUsePrimaryKey(mapped_key); } } // else state is undetermined; block. I could check timeouts here, @@ -241,12 +302,18 @@ void Qukeys::preReportHook(void) { // state to the alternate keycode and add it to the report uint16_t current_time = (uint16_t)millis(); while (key_queue_length_ > 0) { - if (lookupQukey(key_queue_[0].addr) == QUKEY_NOT_FOUND) { - flushKey(QUKEY_STATE_PRIMARY, IS_PRESSED | WAS_PRESSED); - } else if ((current_time - key_queue_[0].start_time) > time_limit_) { - flushKey(QUKEY_STATE_ALTERNATE, IS_PRESSED | WAS_PRESSED); + byte row = addr::row(key_queue_[0].addr); + byte col = addr::col(key_queue_[0].addr); + Key keycode = Layer.lookup(row, col); + bool is_dual_use = isDualUse(keycode); + if (lookupQukey(key_queue_[0].addr) != QUKEY_NOT_FOUND || is_dual_use) { + if ((current_time - key_queue_[0].start_time) > time_limit_) { + flushKey(QUKEY_STATE_ALTERNATE, IS_PRESSED | WAS_PRESSED); + } else { + break; + } } else { - break; + flushKey(QUKEY_STATE_PRIMARY, IS_PRESSED | WAS_PRESSED); } } } diff --git a/src/Kaleidoscope/Qukeys.h b/src/Kaleidoscope/Qukeys.h index 2994bdff..4d718d97 100644 --- a/src/Kaleidoscope/Qukeys.h +++ b/src/Kaleidoscope/Qukeys.h @@ -20,6 +20,7 @@ #include #include +#include // Maximum length of the pending queue #define QUKEYS_QUEUE_MAX 8 @@ -41,6 +42,16 @@ // Wildcard value; this matches any layer #define QUKEY_ALL_LAYERS -1 +#define MT(mod, key) (Key) { \ + .raw = kaleidoscope::ranges::DUM_FIRST + \ + (((Key_ ## mod).keyCode - Key_LeftControl.keyCode) << 8) + (Key_ ## key).keyCode } +#define SFT_T(key) MT(LeftShift, key) +#define CTL_T(key) MT(LeftControl, key) +#define ALT_T(key) MT(LeftAlt, key) +#define GUI_T(key) MT(LeftGui, key) + +#define LT(layer, key) (Key) { .raw = kaleidoscope::ranges::DUL_FIRST + (layer << 8) + (Key_ ## key).keyCode } + namespace kaleidoscope { // Data structure for an individual qukey From e902cb99a73cfcf2308bec569b317178909a49d2 Mon Sep 17 00:00:00 2001 From: Harry Mills Date: Sun, 11 Feb 2018 11:34:32 -0500 Subject: [PATCH 49/72] Fix readme instructions (#30) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7b0d90be..06d4a0ad 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ one keycode (i.e. symbol) when tapped, and a different keycode -- most likely a - Clone the module -- In your sketch directory (e.g. `Model01-Firmware/`: ``` -git submodule add Kaleidoscope-Qukeys https://github.com/gedankenlab/Kaleidoscope-Qukeys.git +git submodule add https://github.com/gedankenlab/Kaleidoscope-Qukeys.git Kaleidoscope-Qukeys ``` - Include the header file: ``` From 8adc1d94a9d4ac539f0199fe2ae79cbdf6034235 Mon Sep 17 00:00:00 2001 From: Michael Richters Date: Mon, 12 Feb 2018 15:15:13 -0600 Subject: [PATCH 50/72] Fixed qukeys causing layer shifts to get stuck I wasn't properly letting key release events through, causing layer shift keys to become LockLayer keys --- src/Kaleidoscope/Qukeys.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Kaleidoscope/Qukeys.cpp b/src/Kaleidoscope/Qukeys.cpp index 1c652b74..3d7ccf28 100644 --- a/src/Kaleidoscope/Qukeys.cpp +++ b/src/Kaleidoscope/Qukeys.cpp @@ -263,6 +263,7 @@ Key Qukeys::keyScanHook(Key mapped_key, byte row, byte col, uint8_t key_state) { return qukeys[qukey_index].alt_keycode; return mapped_key; } + return mapped_key; } flushQueue(queue_index); return Key_NoKey; From 1828c9fa2d5ebeb3be32853fd62013a66e3be809 Mon Sep 17 00:00:00 2001 From: Michael Richters Date: Sat, 17 Mar 2018 13:02:34 -0500 Subject: [PATCH 51/72] Guarantee that the first key in the queue is an undetermined qukey (#34) This will make a few tests simpler because we can assume that if the queue isn't empty, the head of the queue is a valid Qukey (or DualUse Key). --- src/Kaleidoscope/Qukeys.cpp | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/Kaleidoscope/Qukeys.cpp b/src/Kaleidoscope/Qukeys.cpp index 3d7ccf28..8f97ee63 100644 --- a/src/Kaleidoscope/Qukeys.cpp +++ b/src/Kaleidoscope/Qukeys.cpp @@ -32,12 +32,21 @@ namespace kaleidoscope { +inline bool isDualUse(Key k) { if (k.raw < ranges::DU_FIRST || k.raw > ranges::DU_LAST) return false; return true; } +inline +bool isDualUse(byte key_addr) { + byte row = addr::row(key_addr); + byte col = addr::col(key_addr); + Key k = Layer.lookup(row, col); + return isDualUse(k); +} + Key getDualUsePrimaryKey(Key k) { if (k.raw >= ranges::DUM_FIRST && k.raw <= ranges::DUM_LAST) { k.raw -= ranges::DUM_FIRST; @@ -195,9 +204,10 @@ void Qukeys::flushQueue(int8_t index) { } // Flush all the non-qukey keys from the front of the queue -void Qukeys::flushQueue(void) { +void Qukeys::flushQueue() { // flush keys until we find a qukey: while (key_queue_length_ > 0 && + ! isDualUse(key_queue_[0].addr) && lookupQukey(key_queue_[0].addr) == QUKEY_NOT_FOUND) { flushKey(QUKEY_STATE_PRIMARY, IS_PRESSED | WAS_PRESSED); } @@ -266,6 +276,7 @@ Key Qukeys::keyScanHook(Key mapped_key, byte row, byte col, uint8_t key_state) { return mapped_key; } flushQueue(queue_index); + flushQueue(); return Key_NoKey; } @@ -301,20 +312,13 @@ Key Qukeys::keyScanHook(Key mapped_key, byte row, byte col, uint8_t key_state) { void Qukeys::preReportHook(void) { // If the qukey has been held longer than the time limit, set its // state to the alternate keycode and add it to the report - uint16_t current_time = (uint16_t)millis(); while (key_queue_length_ > 0) { - byte row = addr::row(key_queue_[0].addr); - byte col = addr::col(key_queue_[0].addr); - Key keycode = Layer.lookup(row, col); - bool is_dual_use = isDualUse(keycode); - if (lookupQukey(key_queue_[0].addr) != QUKEY_NOT_FOUND || is_dual_use) { - if ((current_time - key_queue_[0].start_time) > time_limit_) { - flushKey(QUKEY_STATE_ALTERNATE, IS_PRESSED | WAS_PRESSED); - } else { - break; - } + uint16_t current_time = millis(); + if ((current_time - key_queue_[0].start_time) > time_limit_) { + flushKey(QUKEY_STATE_ALTERNATE, IS_PRESSED | WAS_PRESSED); + flushQueue(); } else { - flushKey(QUKEY_STATE_PRIMARY, IS_PRESSED | WAS_PRESSED); + break; } } } From 26c5bd7162c6c894467ec0ab2bd0adaab4dbde39 Mon Sep 17 00:00:00 2001 From: Michael Richters Date: Tue, 20 Mar 2018 10:28:58 -0500 Subject: [PATCH 52/72] Ignore keyswitch events with out-of-bounds addresses (#36) Any keyswitch events without real physical addresses were injected by other plugins and should not be processed by Qukeys. There was an interaction with OneShot that prevented the two plugins from working together because OneShot sends events with a (row, col) address of `UNKNOWN_KEYSWITCH_LOCATION` (i.e. 255, 255). This meant that if a OneShot modifier was on when a Qukey was pressed, it would fill up the queue with bogus-address versions of the Qukey, which would then get flushed in the primary state, cancelling the OneShot and producing an un-modified, repeating primary keycode, causing both plugins to fail. --- src/Kaleidoscope/Qukeys.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Kaleidoscope/Qukeys.cpp b/src/Kaleidoscope/Qukeys.cpp index 8f97ee63..b2d04c3b 100644 --- a/src/Kaleidoscope/Qukeys.cpp +++ b/src/Kaleidoscope/Qukeys.cpp @@ -215,6 +215,10 @@ void Qukeys::flushQueue() { Key Qukeys::keyScanHook(Key mapped_key, byte row, byte col, uint8_t key_state) { + // If key_addr is not a physical key, ignore it; some other plugin injected it + if (row >= ROWS || col >= COLS) + return mapped_key; + // If Qukeys is turned off, continue to next plugin if (!active_) return getDualUsePrimaryKey(mapped_key); From 32a8537c282e253891ace1637dbc967f53181018 Mon Sep 17 00:00:00 2001 From: Michael Richters Date: Sat, 17 Mar 2018 23:39:26 -0500 Subject: [PATCH 53/72] Added a configurable release delay on qukey release When releasing a qukey, allow a short timeout in case a subsequent key was released effectively simultaneously, treating that near-simultaneous release as intended to use the alternate (i.e. modifier) keycode. --- src/Kaleidoscope/Qukeys.cpp | 47 ++++++++++++++++++++++++++++++------- src/Kaleidoscope/Qukeys.h | 5 ++++ 2 files changed, 44 insertions(+), 8 deletions(-) diff --git a/src/Kaleidoscope/Qukeys.cpp b/src/Kaleidoscope/Qukeys.cpp index b2d04c3b..517c802c 100644 --- a/src/Kaleidoscope/Qukeys.cpp +++ b/src/Kaleidoscope/Qukeys.cpp @@ -85,11 +85,14 @@ uint8_t Qukeys::qukeys_count = 0; bool Qukeys::active_ = true; uint16_t Qukeys::time_limit_ = 250; +uint8_t Qukeys::release_delay_ = 0; QueueItem Qukeys::key_queue_[] = {}; uint8_t Qukeys::key_queue_length_ = 0; byte Qukeys::qukey_state_[] = {}; bool Qukeys::flushing_queue_ = false; +constexpr uint16_t QUKEYS_RELEASE_DELAY_OFFSET = 4096; + // Empty constructor; nothing is stored at the instance level Qukeys::Qukeys(void) {} @@ -116,7 +119,7 @@ void Qukeys::enqueue(uint8_t key_addr) { flushQueue(); } key_queue_[key_queue_length_].addr = key_addr; - key_queue_[key_queue_length_].start_time = (uint16_t)millis(); + key_queue_[key_queue_length_].start_time = millis(); key_queue_length_++; addr::mask(key_addr); } @@ -175,7 +178,7 @@ void Qukeys::flushKey(bool qukey_state, uint8_t keyswitch_state) { memcpy(Keyboard.keyReport.allkeys, hid_report.allkeys, sizeof(hid_report)); // Last, if the key is still down, add its code back in - if (keyswitch_state | IS_PRESSED) + if (keyswitch_state & IS_PRESSED) handleKeyswitchEvent(keycode, row, col, IS_PRESSED | WAS_PRESSED); // Now that we're done sending the report(s), Qukeys can process events again: @@ -197,22 +200,29 @@ void Qukeys::flushQueue(int8_t index) { return; for (int8_t i = 0; i < index; i++) { if (key_queue_length_ == 0) - break; + return; flushKey(QUKEY_STATE_ALTERNATE, IS_PRESSED | WAS_PRESSED); } - flushKey(QUKEY_STATE_PRIMARY, WAS_PRESSED); + if (isQukey(key_queue_[0].addr)) { + flushKey(QUKEY_STATE_PRIMARY, IS_PRESSED | WAS_PRESSED); + } else { + flushKey(QUKEY_STATE_PRIMARY, WAS_PRESSED); + } } // Flush all the non-qukey keys from the front of the queue void Qukeys::flushQueue() { // flush keys until we find a qukey: - while (key_queue_length_ > 0 && - ! isDualUse(key_queue_[0].addr) && - lookupQukey(key_queue_[0].addr) == QUKEY_NOT_FOUND) { + while (key_queue_length_ > 0 && !isQukey(key_queue_[0].addr)) { flushKey(QUKEY_STATE_PRIMARY, IS_PRESSED | WAS_PRESSED); } } +inline +bool Qukeys::isQukey(uint8_t addr) { + return (isDualUse(addr) || lookupQukey(addr) != QUKEY_NOT_FOUND); +} + Key Qukeys::keyScanHook(Key mapped_key, byte row, byte col, uint8_t key_state) { // If key_addr is not a physical key, ignore it; some other plugin injected it @@ -279,6 +289,14 @@ Key Qukeys::keyScanHook(Key mapped_key, byte row, byte col, uint8_t key_state) { } return mapped_key; } + // If there's a release delay in effect, the released key is a qukey, and there's at + // least one key after it in the queue, delay this key's release event: + if (release_delay_ > 0 && + isQukey(key_addr) && + queue_index < (key_queue_length_ - 1)) { + key_queue_[queue_index].start_time = millis() + QUKEYS_RELEASE_DELAY_OFFSET; + return Key_NoKey; + } flushQueue(queue_index); flushQueue(); return Key_NoKey; @@ -314,10 +332,23 @@ Key Qukeys::keyScanHook(Key mapped_key, byte row, byte col, uint8_t key_state) { } void Qukeys::preReportHook(void) { + + uint16_t current_time = millis(); + + if (release_delay_ > 0) { + int16_t diff_time = key_queue_[0].start_time - current_time; + if (diff_time > 0) { + int16_t delay_window = QUKEYS_RELEASE_DELAY_OFFSET - release_delay_; + if (diff_time < delay_window) { + flushKey(QUKEY_STATE_ALTERNATE, WAS_PRESSED); + flushQueue(); + } + } + } + // If the qukey has been held longer than the time limit, set its // state to the alternate keycode and add it to the report while (key_queue_length_ > 0) { - uint16_t current_time = millis(); if ((current_time - key_queue_[0].start_time) > time_limit_) { flushKey(QUKEY_STATE_ALTERNATE, IS_PRESSED | WAS_PRESSED); flushQueue(); diff --git a/src/Kaleidoscope/Qukeys.h b/src/Kaleidoscope/Qukeys.h index 4d718d97..6e654cd5 100644 --- a/src/Kaleidoscope/Qukeys.h +++ b/src/Kaleidoscope/Qukeys.h @@ -94,6 +94,9 @@ class Qukeys : public KaleidoscopePlugin { static void setTimeout(uint16_t time_limit) { time_limit_ = time_limit; } + static void setReleaseDelay(uint8_t release_delay) { + release_delay_ = release_delay; + } static Qukey * qukeys; static uint8_t qukeys_count; @@ -101,6 +104,7 @@ class Qukeys : public KaleidoscopePlugin { private: static bool active_; static uint16_t time_limit_; + static uint8_t release_delay_; static QueueItem key_queue_[QUKEYS_QUEUE_MAX]; static uint8_t key_queue_length_; static bool flushing_queue_; @@ -120,6 +124,7 @@ class Qukeys : public KaleidoscopePlugin { static void flushKey(bool qukey_state, uint8_t keyswitch_state); static void flushQueue(int8_t index); static void flushQueue(void); + static bool isQukey(uint8_t addr); static Key keyScanHook(Key mapped_key, byte row, byte col, uint8_t key_state); static void preReportHook(void); From 639f16984a6a6e0872a1809f605e6eb8974bea80 Mon Sep 17 00:00:00 2001 From: Michael Richters Date: Sat, 17 Mar 2018 23:52:50 -0500 Subject: [PATCH 54/72] Set the release delay in the example --- examples/Qukeys/Qukeys.ino | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/Qukeys/Qukeys.ino b/examples/Qukeys/Qukeys.ino index cfc33638..0732383b 100644 --- a/examples/Qukeys/Qukeys.ino +++ b/examples/Qukeys/Qukeys.ino @@ -71,6 +71,7 @@ void setup() { kaleidoscope::Qukey(0, 3, 6, ShiftToLayer(1)) // Q/layer-shift (on `fn`) ) Qukeys.setTimeout(200); + Qukeys.setReleaseDelay(20); // To toggle Qukeys off and on, we use a macro Kaleidoscope.use(&Macros); From 4e624afb338021f9782cb0e33daebd1e062a0962 Mon Sep 17 00:00:00 2001 From: Michael Richters Date: Tue, 20 Mar 2018 10:17:00 -0500 Subject: [PATCH 55/72] Fixed random input bug & restructured queue flushing I had failed to check that the queue length was non-zero before checking release delay timeouts, causing reading past the end of the `key_queue_` array an repeatedly sending essentially random input to the host. In the process of fixing that bug, I realized that I was also assuming that layer changes weren't happening earlier in the queue and checking whether or not a key is a qukey when it wasn't the head of the queue. Now we only enact a release delay when `flushKey()` is called, and always call `setQukeyState()` when enqueuing new keys so that we can distinguish between keys that should be immediately flushed in the primary state, and ones that should have keypresses delayed. --- src/Kaleidoscope/Qukeys.cpp | 32 ++++++++++++++++++++------------ src/Kaleidoscope/Qukeys.h | 2 +- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/src/Kaleidoscope/Qukeys.cpp b/src/Kaleidoscope/Qukeys.cpp index 517c802c..c3213fd4 100644 --- a/src/Kaleidoscope/Qukeys.cpp +++ b/src/Kaleidoscope/Qukeys.cpp @@ -115,9 +115,13 @@ int8_t Qukeys::lookupQukey(uint8_t key_addr) { void Qukeys::enqueue(uint8_t key_addr) { if (key_queue_length_ == QUKEYS_QUEUE_MAX) { + setQukeyState(key_queue_[0].addr, QUKEY_STATE_PRIMARY); flushKey(QUKEY_STATE_PRIMARY, IS_PRESSED | WAS_PRESSED); flushQueue(); } + // default to alternate state to stop keys being flushed from the queue before the grace + // period timeout + setQukeyState(key_addr, QUKEY_STATE_ALTERNATE); key_queue_[key_queue_length_].addr = key_addr; key_queue_[key_queue_length_].start_time = millis(); key_queue_length_++; @@ -133,7 +137,7 @@ int8_t Qukeys::searchQueue(uint8_t key_addr) { } // flush a single entry from the head of the queue -void Qukeys::flushKey(bool qukey_state, uint8_t keyswitch_state) { +bool Qukeys::flushKey(bool qukey_state, uint8_t keyswitch_state) { addr::unmask(key_queue_[0].addr); int8_t qukey_index = lookupQukey(key_queue_[0].addr); bool is_qukey = (qukey_index != QUKEY_NOT_FOUND); @@ -142,6 +146,15 @@ void Qukeys::flushKey(bool qukey_state, uint8_t keyswitch_state) { bool is_dual_use = isDualUse(Layer.lookup(row, col)); Key keycode = Key_NoKey; if (is_qukey || is_dual_use) { + if (qukey_state == QUKEY_STATE_PRIMARY && + getQukeyState(key_queue_[0].addr) == QUKEY_STATE_ALTERNATE) { + // If there's a release delay in effect, and there's at least one key after it in + // the queue, delay this key's release event: + if (release_delay_ > 0 && key_queue_length_ > 1) { + key_queue_[0].start_time = millis() + QUKEYS_RELEASE_DELAY_OFFSET; + return false; + } + } setQukeyState(key_queue_[0].addr, qukey_state); if (qukey_state == QUKEY_STATE_ALTERNATE) { if (is_dual_use) { @@ -189,6 +202,7 @@ void Qukeys::flushKey(bool qukey_state, uint8_t keyswitch_state) { key_queue_[i] = key_queue_[i + 1]; } key_queue_length_--; + return true; } // flushQueue() is called when a key that's in the key_queue is @@ -214,7 +228,8 @@ void Qukeys::flushQueue(int8_t index) { void Qukeys::flushQueue() { // flush keys until we find a qukey: while (key_queue_length_ > 0 && !isQukey(key_queue_[0].addr)) { - flushKey(QUKEY_STATE_PRIMARY, IS_PRESSED | WAS_PRESSED); + if (flushKey(QUKEY_STATE_PRIMARY, IS_PRESSED | WAS_PRESSED) == false) + break; } } @@ -289,14 +304,6 @@ Key Qukeys::keyScanHook(Key mapped_key, byte row, byte col, uint8_t key_state) { } return mapped_key; } - // If there's a release delay in effect, the released key is a qukey, and there's at - // least one key after it in the queue, delay this key's release event: - if (release_delay_ > 0 && - isQukey(key_addr) && - queue_index < (key_queue_length_ - 1)) { - key_queue_[queue_index].start_time = millis() + QUKEYS_RELEASE_DELAY_OFFSET; - return Key_NoKey; - } flushQueue(queue_index); flushQueue(); return Key_NoKey; @@ -335,12 +342,13 @@ void Qukeys::preReportHook(void) { uint16_t current_time = millis(); - if (release_delay_ > 0) { + if (release_delay_ > 0 && key_queue_length_ > 0) { int16_t diff_time = key_queue_[0].start_time - current_time; if (diff_time > 0) { int16_t delay_window = QUKEYS_RELEASE_DELAY_OFFSET - release_delay_; if (diff_time < delay_window) { - flushKey(QUKEY_STATE_ALTERNATE, WAS_PRESSED); + setQukeyState(key_queue_[0].addr, QUKEY_STATE_PRIMARY); + flushKey(QUKEY_STATE_PRIMARY, WAS_PRESSED); flushQueue(); } } diff --git a/src/Kaleidoscope/Qukeys.h b/src/Kaleidoscope/Qukeys.h index 6e654cd5..e85d93c6 100644 --- a/src/Kaleidoscope/Qukeys.h +++ b/src/Kaleidoscope/Qukeys.h @@ -121,7 +121,7 @@ class Qukeys : public KaleidoscopePlugin { static int8_t lookupQukey(uint8_t key_addr); static void enqueue(uint8_t key_addr); static int8_t searchQueue(uint8_t key_addr); - static void flushKey(bool qukey_state, uint8_t keyswitch_state); + static bool flushKey(bool qukey_state, uint8_t keyswitch_state); static void flushQueue(int8_t index); static void flushQueue(void); static bool isQukey(uint8_t addr); From e2519b440b483e730a628cbd1fa15662877d2820 Mon Sep 17 00:00:00 2001 From: Michael Richters Date: Wed, 21 Mar 2018 11:16:37 -0500 Subject: [PATCH 56/72] Don't test for timeouts if the first queue entry is a delayed qukeys release --- src/Kaleidoscope/Qukeys.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Kaleidoscope/Qukeys.cpp b/src/Kaleidoscope/Qukeys.cpp index c3213fd4..7ecb7a2c 100644 --- a/src/Kaleidoscope/Qukeys.cpp +++ b/src/Kaleidoscope/Qukeys.cpp @@ -351,6 +351,7 @@ void Qukeys::preReportHook(void) { flushKey(QUKEY_STATE_PRIMARY, WAS_PRESSED); flushQueue(); } + return; } } From 6036e908f50098e86619b25ecc818b5ee2906a06 Mon Sep 17 00:00:00 2001 From: Michael Richters Date: Sat, 21 Apr 2018 22:48:54 -0500 Subject: [PATCH 57/72] Ignore any event with INJECTED flag Any event with the `INJECTED` flag set is now ignored. This is necessary because OneShot now sends events with valid `row` & `col` values when it calls `handleKeyswitchEvent()`, and we need to make sure those events don't get enqueued in the case of a qukey whose primary keycode value is a OSM key, followed by a key that is meant to be modified by that key. Fixes #40. --- src/Kaleidoscope/Qukeys.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Kaleidoscope/Qukeys.cpp b/src/Kaleidoscope/Qukeys.cpp index 7ecb7a2c..e6b94523 100644 --- a/src/Kaleidoscope/Qukeys.cpp +++ b/src/Kaleidoscope/Qukeys.cpp @@ -241,7 +241,7 @@ bool Qukeys::isQukey(uint8_t addr) { Key Qukeys::keyScanHook(Key mapped_key, byte row, byte col, uint8_t key_state) { // If key_addr is not a physical key, ignore it; some other plugin injected it - if (row >= ROWS || col >= COLS) + if (row >= ROWS || col >= COLS || (key_state & INJECTED) != 0) return mapped_key; // If Qukeys is turned off, continue to next plugin From 445655a666336e4e266b9400651475aac10f31a2 Mon Sep 17 00:00:00 2001 From: Gergely Nagy Date: Sun, 13 May 2018 10:36:38 +0200 Subject: [PATCH 58/72] Updated to use the new plugin APIs Signed-off-by: Gergely Nagy --- examples/Qukeys/Qukeys.ino | 9 ++-- src/Kaleidoscope/Qukeys.cpp | 97 ++++++++++++++++++++++++------------- src/Kaleidoscope/Qukeys.h | 18 ++++--- 3 files changed, 76 insertions(+), 48 deletions(-) diff --git a/examples/Qukeys/Qukeys.ino b/examples/Qukeys/Qukeys.ino index 0732383b..71042774 100644 --- a/examples/Qukeys/Qukeys.ino +++ b/examples/Qukeys/Qukeys.ino @@ -59,10 +59,10 @@ const macro_t *macroAction(uint8_t macro_index, uint8_t key_state) { return MACRO_NONE; } -void setup() { - // Use Qukeys - Kaleidoscope.use(&Qukeys); +// Use Qukeys +KALEIDOSCOPE_INIT_PLUGINS(Qukeys, Macros); +void setup() { QUKEYS( kaleidoscope::Qukey(0, 2, 1, Key_LeftGui), // A/cmd kaleidoscope::Qukey(0, 2, 2, Key_LeftAlt), // S/alt @@ -73,9 +73,6 @@ void setup() { Qukeys.setTimeout(200); Qukeys.setReleaseDelay(20); - // To toggle Qukeys off and on, we use a macro - Kaleidoscope.use(&Macros); - Kaleidoscope.setup(); } diff --git a/src/Kaleidoscope/Qukeys.cpp b/src/Kaleidoscope/Qukeys.cpp index e6b94523..fce07fd7 100644 --- a/src/Kaleidoscope/Qukeys.cpp +++ b/src/Kaleidoscope/Qukeys.cpp @@ -238,15 +238,17 @@ bool Qukeys::isQukey(uint8_t addr) { return (isDualUse(addr) || lookupQukey(addr) != QUKEY_NOT_FOUND); } -Key Qukeys::keyScanHook(Key mapped_key, byte row, byte col, uint8_t key_state) { +EventHandlerResult Qukeys::onKeyswitchEvent(Key &mapped_key, byte row, byte col, uint8_t key_state) { // If key_addr is not a physical key, ignore it; some other plugin injected it if (row >= ROWS || col >= COLS || (key_state & INJECTED) != 0) - return mapped_key; + return EventHandlerResult::OK; // If Qukeys is turned off, continue to next plugin - if (!active_) - return getDualUsePrimaryKey(mapped_key); + if (!active_) { + mapped_key = getDualUsePrimaryKey(mapped_key); + return EventHandlerResult::OK; + } // get key addr & qukey (if any) uint8_t key_addr = addr::addr(row, col); @@ -257,18 +259,20 @@ Key Qukeys::keyScanHook(Key mapped_key, byte row, byte col, uint8_t key_state) { // If it's a DualUse key, we still need to update its keycode if (isDualUse(mapped_key)) { if (getQukeyState(key_addr) == QUKEY_STATE_ALTERNATE) { - return getDualUseAlternateKey(mapped_key); + mapped_key = getDualUseAlternateKey(mapped_key); } else { - return getDualUsePrimaryKey(mapped_key); + mapped_key = getDualUsePrimaryKey(mapped_key); } } // ...otherwise, just continue to the next plugin - return mapped_key; + return EventHandlerResult::OK; } // If the key isn't active, and didn't just toggle off, continue to next plugin - if (!keyIsPressed(key_state) && !keyWasPressed(key_state)) - return getDualUsePrimaryKey(mapped_key); + if (!keyIsPressed(key_state) && !keyWasPressed(key_state)) { + mapped_key = getDualUsePrimaryKey(mapped_key); + return EventHandlerResult::OK; + } // If the key was just pressed: if (keyToggledOn(key_state)) { @@ -276,13 +280,13 @@ Key Qukeys::keyScanHook(Key mapped_key, byte row, byte col, uint8_t key_state) { if (key_queue_length_ == 0 && ! isDualUse(mapped_key) && qukey_index == QUKEY_NOT_FOUND) { - return mapped_key; + return EventHandlerResult::OK; } // Otherwise, queue the key and stop processing: enqueue(key_addr); // flushQueue() has already handled this key release - return Key_NoKey; + return EventHandlerResult::EVENT_CONSUMED; } // In all other cases, we need to know if the key is queued already @@ -294,19 +298,21 @@ Key Qukeys::keyScanHook(Key mapped_key, byte row, byte col, uint8_t key_state) { if (queue_index == QUKEY_NOT_FOUND) { // If a qukey was released while in its alternate state, change its keycode if (isDualUse(mapped_key)) { - if (getQukeyState(key_addr) == QUKEY_STATE_ALTERNATE) - return getDualUseAlternateKey(mapped_key); - return getDualUsePrimaryKey(mapped_key); + if (getQukeyState(key_addr) == QUKEY_STATE_ALTERNATE) { + mapped_key = getDualUseAlternateKey(mapped_key); + } else { + mapped_key = getDualUsePrimaryKey(mapped_key); + } } else if (qukey_index != QUKEY_NOT_FOUND) { - if (getQukeyState(key_addr) == QUKEY_STATE_ALTERNATE) - return qukeys[qukey_index].alt_keycode; - return mapped_key; + if (getQukeyState(key_addr) == QUKEY_STATE_ALTERNATE) { + mapped_key = qukeys[qukey_index].alt_keycode; + } } - return mapped_key; + return EventHandlerResult::OK; } flushQueue(queue_index); flushQueue(); - return Key_NoKey; + return EventHandlerResult::EVENT_CONSUMED; } // Otherwise, the key is still pressed @@ -316,29 +322,32 @@ Key Qukeys::keyScanHook(Key mapped_key, byte row, byte col, uint8_t key_state) { ! isDualUse(mapped_key)) { // If the key was pressed before the keys in the queue, proceed: if (queue_index == QUKEY_NOT_FOUND) { - return mapped_key; + return EventHandlerResult::OK; } else { // suppress this keypress; it's still in the queue - return Key_NoKey; + return EventHandlerResult::EVENT_CONSUMED; } } // If the qukey is not in the queue, check its state if (queue_index == QUKEY_NOT_FOUND) { if (getQukeyState(key_addr) == QUKEY_STATE_ALTERNATE) { - if (isDualUse(mapped_key)) - return getDualUseAlternateKey(mapped_key); - return qukeys[qukey_index].alt_keycode; + if (isDualUse(mapped_key)) { + mapped_key = getDualUseAlternateKey(mapped_key); + } else { + mapped_key = qukeys[qukey_index].alt_keycode; + } } else { // qukey_state == QUKEY_STATE_PRIMARY - return getDualUsePrimaryKey(mapped_key); + mapped_key = getDualUsePrimaryKey(mapped_key); } + return EventHandlerResult::OK; } // else state is undetermined; block. I could check timeouts here, // but I'd rather do that in the pre-report hook - return Key_NoKey; + return EventHandlerResult::EVENT_CONSUMED; } -void Qukeys::preReportHook(void) { +EventHandlerResult Qukeys::beforeReportingState() { uint16_t current_time = millis(); @@ -351,7 +360,7 @@ void Qukeys::preReportHook(void) { flushKey(QUKEY_STATE_PRIMARY, WAS_PRESSED); flushQueue(); } - return; + return EventHandlerResult::OK; } } @@ -365,14 +374,11 @@ void Qukeys::preReportHook(void) { break; } } -} -void Qukeys::loopHook(bool post_clear) { - if (!post_clear) - return preReportHook(); + return EventHandlerResult::OK; } -void Qukeys::begin() { +EventHandlerResult Qukeys::onSetup() { // initializing the key_queue seems unnecessary, actually for (int8_t i = 0; i < QUKEYS_QUEUE_MAX; i++) { key_queue_[i].addr = QUKEY_UNKNOWN_ADDR; @@ -380,10 +386,31 @@ void Qukeys::begin() { } key_queue_length_ = 0; - Kaleidoscope.useEventHandlerHook(keyScanHook); - Kaleidoscope.useLoopHook(loopHook); + return EventHandlerResult::OK; } +// Legacy V1 API +#if KALEIDOSCOPE_ENABLE_V1_PLUGIN_API +void Qukeys::begin() { + onSetup(); + Kaleidoscope.useEventHandlerHook(legacyEventHandler); + Kaleidoscope.useLoopHook(legacyLoopHook); +} + +Key Qukeys::legacyEventHandler(Key mapped_key, byte row, byte col, uint8_t key_state) { + EventHandlerResult r = ::Qukeys.onKeyswitchEvent(mapped_key, row, col, key_state); + if (r == EventHandlerResult::OK) + return mapped_key; + return Key_NoKey; +} + +void Qukeys::legacyLoopHook(bool is_post_clear) { + if (is_post_clear) + return; + ::Qukeys.beforeReportingState(); +} +#endif + } // namespace kaleidoscope { kaleidoscope::Qukeys Qukeys; diff --git a/src/Kaleidoscope/Qukeys.h b/src/Kaleidoscope/Qukeys.h index e85d93c6..959bc7f3 100644 --- a/src/Kaleidoscope/Qukeys.h +++ b/src/Kaleidoscope/Qukeys.h @@ -72,7 +72,7 @@ struct QueueItem { }; // The plugin itself -class Qukeys : public KaleidoscopePlugin { +class Qukeys : public kaleidoscope::Plugin { // I could use a bitfield to get the state values, but then we'd // have to check the key_queue (there are three states). Or use a // second bitfield for the indeterminite state. Using a bitfield @@ -81,7 +81,6 @@ class Qukeys : public KaleidoscopePlugin { public: Qukeys(void); - void begin(void) final; static void activate(void) { active_ = true; } @@ -101,6 +100,16 @@ class Qukeys : public KaleidoscopePlugin { static Qukey * qukeys; static uint8_t qukeys_count; + EventHandlerResult onSetup(); + EventHandlerResult onKeyswitchEvent(Key &mapped_key, byte row, byte col, uint8_t key_state); + EventHandlerResult beforeReportingState(); + +#if KALEIDOSCOPE_ENABLE_V1_PLUGIN_API + void begin(); + static Key legacyEventHandler(Key mapped_key, byte row, byte col, uint8_t key_state); + static void legacyLoopHook(bool is_post_clear); +#endif + private: static bool active_; static uint16_t time_limit_; @@ -125,11 +134,6 @@ class Qukeys : public KaleidoscopePlugin { static void flushQueue(int8_t index); static void flushQueue(void); static bool isQukey(uint8_t addr); - - static Key keyScanHook(Key mapped_key, byte row, byte col, uint8_t key_state); - static void preReportHook(void); - static void postReportHook(void) {} - static void loopHook(bool post_clear); }; } // namespace kaleidoscope { From c8e48c625fd4933ac255b9fdf4081e8aa52d4f87 Mon Sep 17 00:00:00 2001 From: Shriramana Sharma Date: Tue, 19 Jun 2018 11:34:50 +0530 Subject: [PATCH 59/72] Update README.md for repo change and Qukeys syntax The repo is now under the keyboardio organization; also the Qukeys syntax had not at all been explained --- README.md | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 06d4a0ad..ae182c68 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,8 @@ ![status][st:experimental] [![Build Status][travis:image]][travis:status] - [travis:image]: https://travis-ci.org/gedankenlab/Kaleidoscope-Qukeys.svg?branch=master - [travis:status]: https://travis-ci.org/gedankenlab/Kaleidoscope-Qukeys + [travis:image]: https://travis-ci.org/keyboardio/Kaleidoscope-Qukeys.svg?branch=master + [travis:status]: https://travis-ci.org/keyboardio/Kaleidoscope-Qukeys [st:stable]: https://img.shields.io/badge/stable-✔-black.svg?style=flat&colorA=44cc11&colorB=494e52 [st:broken]: https://img.shields.io/badge/broken-X-black.svg?style=flat&colorA=e05d44&colorB=494e52 @@ -18,19 +18,15 @@ one keycode (i.e. symbol) when tapped, and a different keycode -- most likely a ## Setup -- Clone the module -- In your sketch directory (e.g. `Model01-Firmware/`: -``` -git submodule add https://github.com/gedankenlab/Kaleidoscope-Qukeys.git Kaleidoscope-Qukeys -``` - Include the header file: ``` #include ``` -- Use the plugin in the `setup()` function: +- Use the plugin in the `KALEIDOSCOPE_INIT_PLUGINS` macro: ``` -Kaleidoscope.use(&Qukeys); +KALEIDOSCOPE_INIT_PLUGINS(Qukeys); ``` -- Define some `Qukeys`: +- Define some `Qukeys` of the format `Qukey(layer, row, col, alt_keycode)` (layers, rows and columns are all zero-indexed, rows are top to bottom and columns are left to right): ``` QUKEYS( kaleidoscope::Qukey(0, 2, 1, Key_LeftGui), // A/cmd @@ -55,7 +51,7 @@ likely to generate errors and out-of-order events. - activate/deactivate `Qukeys` - see the - [example](https://github.com/gedankenlab/Kaleidoscope-Qukeys/blob/master/examples/Qukeys/Qukeys.ino) + [example](https://github.com/keyboardio/Kaleidoscope-Qukeys/blob/master/examples/Qukeys/Qukeys.ino) for a way to turn `Qukeys` on and off, using Kaleidoscope-Macros ### DualUse key definitions From 4f5413c9e523cc04f54fadb6b01913b4297aecb5 Mon Sep 17 00:00:00 2001 From: Michael Richters Date: Thu, 28 Jun 2018 13:51:31 -0500 Subject: [PATCH 60/72] Fix release of primary keycodes When a key was released, we were failing to send the release event explicitly. For most plugins, this didn't matter, but it was causing a problem for Leader, which acts on release events. By returning `EVENT_CONSUMED` instead of `OK`, we were stopping the release event from getting through to Leader, and thus a qukey with a Leader key as its primary keycode would fail. --- src/Kaleidoscope/Qukeys.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Kaleidoscope/Qukeys.cpp b/src/Kaleidoscope/Qukeys.cpp index fce07fd7..18292dff 100644 --- a/src/Kaleidoscope/Qukeys.cpp +++ b/src/Kaleidoscope/Qukeys.cpp @@ -312,7 +312,8 @@ EventHandlerResult Qukeys::onKeyswitchEvent(Key &mapped_key, byte row, byte col, } flushQueue(queue_index); flushQueue(); - return EventHandlerResult::EVENT_CONSUMED; + mapped_key = getDualUsePrimaryKey(mapped_key); + return EventHandlerResult::OK; } // Otherwise, the key is still pressed From 65fe8b64f987c764d0b55fdbccce6e1c6726bd97 Mon Sep 17 00:00:00 2001 From: Mark Skipper Date: Sat, 30 Jun 2018 20:49:50 +0100 Subject: [PATCH 61/72] Add qukeys docs removed from qukeys readme the link to qukeys readme was broken; the relevant section removed by a [recent commit](https://github.com/keyboardio/Kaleidoscope-DualUse/commit/fe752f474fa54d6b5ebc1f14361ceca40c7a5f82#diff-04c6e90faac2675aa89e2176d2eec7d8). --- README.md | 49 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 43 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index ae182c68..fba93073 100644 --- a/README.md +++ b/README.md @@ -57,12 +57,49 @@ likely to generate errors and out-of-order events. ### DualUse key definitions In addition to normal `Qukeys` described above, Kaleidoscope-Qukeys also treats -DualUse keys in the keymap as `Qukeys`. See [the Kaleidoscope-DualUse -documentation](https://github.com/keyboardio/Kaleidoscope-DualUse#keymap-markup) -for a thorough description of how to define DualUse keys. This makes `Qukeys` a -drop-in replacement for the `DualUse` plugin, without the need to edit the -keymap. - +DualUse keys in the keymap as `Qukeys`. This makes `Qukeys` a drop-in replacement +for the `DualUse` plugin, without the need to edit the keymap. + + +The plugin provides a number of macros one can use in keymap definitions: + +#### `CTL_T(key)` + +> A key that acts as the *left* `Control` when held, or used in conjunction with +> other keys, but as `key` when tapped in isolation. The `key` argument must be +> a plain old key, and can't have any modifiers or anything else applied. + +#### `ALT_T(key)` + +> A key that acts as the *left* `Alt` when held, or used in conjunction with +> other keys, but as `key` when tapped in isolation. The `key` argument must be +> a plain old key, and can't have any modifiers or anything else applied. + +#### `SFT_T(key)` + +> A key that acts as the *left* `Shift` when held, or used in conjunction with +> other keys, but as `key` when tapped in isolation. The `key` argument must be +> a plain old key, and can't have any modifiers or anything else applied. + +#### `GUI_T(key)` + +> A key that acts as the *left* `GUI` when held, or used in conjunction with +> other keys, but as `key` when tapped in isolation. The `key` argument must be +> a plain old key, and can't have any modifiers or anything else applied. + +#### `MT(mod, key)` + +> A key that acts as `mod` when held, or used in conjunction with other keys, +> but as `key` when tapped in isolation. The `key` argument must be a plain old +> key, and can't have any modifiers or anything else applied. The `mod` argument +> can be any of the modifiers, *left* or *right* alike. + +#### `LT(layer, key)` + +> A key that momentarily switches to `layer` when held, or used in conjunction +> with other keys, but as `key` when tapped in isolation. The `key` argument +> must be a plain old key, and can't have any modifiers or anything else +> applied. ## Design & Implementation From 1f4b3c7d844e82710f75225ef3f0a4fff1e13eaf Mon Sep 17 00:00:00 2001 From: Shriramana Sharma Date: Mon, 16 Jul 2018 20:49:23 +0530 Subject: [PATCH 62/72] Document the methods provided by the plugin --- README.md | 114 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 68 insertions(+), 46 deletions(-) diff --git a/README.md b/README.md index fba93073..7dd61016 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Kaleidoscope-Qukeys -![status][st:experimental] [![Build Status][travis:image]][travis:status] +[![Build Status][travis:image]][travis:status] [travis:image]: https://travis-ci.org/keyboardio/Kaleidoscope-Qukeys.svg?branch=master [travis:status]: https://travis-ci.org/keyboardio/Kaleidoscope-Qukeys @@ -46,59 +46,75 @@ likely to generate errors and out-of-order events. ## Configuration -- set timeout +### `.setTimeout(time_limit)` -- activate/deactivate `Qukeys` +> Sets the time length in milliseconds which determines if a key has been tapped or held. +> +> Defaults to 250. -- see the - [example](https://github.com/keyboardio/Kaleidoscope-Qukeys/blob/master/examples/Qukeys/Qukeys.ino) - for a way to turn `Qukeys` on and off, using Kaleidoscope-Macros +### `.setReleaseDelay(release_delay)` + +> Sets the time length in milliseconds to artificially delay the release of the Qukey. +> +> This is to accommodate users who are in the habit of releasing modifiers and the keys +> they modify (almost) simultaneously, since the Qukey may be detected as released +> *slightly* before the other key, which would not trigger the desired alternate keycode. +> +> It is best to keep this a very small value such as 20 to avoid over-extending the +> modifier to further keystrokes. +> +> Defaults to 0. + +### `.activate()` +### `.deactivate()` +### `.toggle()` + +> activate/deactivate `Qukeys` ### DualUse key definitions In addition to normal `Qukeys` described above, Kaleidoscope-Qukeys also treats -DualUse keys in the keymap as `Qukeys`. This makes `Qukeys` a drop-in replacement +DualUse keys in the keymap as `Qukeys`. This makes `Qukeys` a drop-in replacement for the `DualUse` plugin, without the need to edit the keymap. - -The plugin provides a number of macros one can use in keymap definitions: - -#### `CTL_T(key)` - -> A key that acts as the *left* `Control` when held, or used in conjunction with -> other keys, but as `key` when tapped in isolation. The `key` argument must be -> a plain old key, and can't have any modifiers or anything else applied. - -#### `ALT_T(key)` - -> A key that acts as the *left* `Alt` when held, or used in conjunction with -> other keys, but as `key` when tapped in isolation. The `key` argument must be -> a plain old key, and can't have any modifiers or anything else applied. - -#### `SFT_T(key)` - -> A key that acts as the *left* `Shift` when held, or used in conjunction with -> other keys, but as `key` when tapped in isolation. The `key` argument must be -> a plain old key, and can't have any modifiers or anything else applied. - -#### `GUI_T(key)` - -> A key that acts as the *left* `GUI` when held, or used in conjunction with -> other keys, but as `key` when tapped in isolation. The `key` argument must be -> a plain old key, and can't have any modifiers or anything else applied. - -#### `MT(mod, key)` - -> A key that acts as `mod` when held, or used in conjunction with other keys, -> but as `key` when tapped in isolation. The `key` argument must be a plain old -> key, and can't have any modifiers or anything else applied. The `mod` argument -> can be any of the modifiers, *left* or *right* alike. - -#### `LT(layer, key)` - -> A key that momentarily switches to `layer` when held, or used in conjunction -> with other keys, but as `key` when tapped in isolation. The `key` argument -> must be a plain old key, and can't have any modifiers or anything else +The plugin provides a number of macros one can use in keymap definitions: + +#### `CTL_T(key)` + +> A key that acts as the *left* `Control` when held, or used in conjunction with +> other keys, but as `key` when tapped in isolation. The `key` argument must be +> a plain old key, and can't have any modifiers or anything else applied. + +#### `ALT_T(key)` + +> A key that acts as the *left* `Alt` when held, or used in conjunction with +> other keys, but as `key` when tapped in isolation. The `key` argument must be +> a plain old key, and can't have any modifiers or anything else applied. + +#### `SFT_T(key)` + +> A key that acts as the *left* `Shift` when held, or used in conjunction with +> other keys, but as `key` when tapped in isolation. The `key` argument must be +> a plain old key, and can't have any modifiers or anything else applied. + +#### `GUI_T(key)` + +> A key that acts as the *left* `GUI` when held, or used in conjunction with +> other keys, but as `key` when tapped in isolation. The `key` argument must be +> a plain old key, and can't have any modifiers or anything else applied. + +#### `MT(mod, key)` + +> A key that acts as `mod` when held, or used in conjunction with other keys, +> but as `key` when tapped in isolation. The `key` argument must be a plain old +> key, and can't have any modifiers or anything else applied. The `mod` argument +> can be any of the modifiers, *left* or *right* alike. + +#### `LT(layer, key)` + +> A key that momentarily switches to `layer` when held, or used in conjunction +> with other keys, but as `key` when tapped in isolation. The `key` argument +> must be a plain old key, and can't have any modifiers or anything else > applied. ## Design & Implementation @@ -127,3 +143,9 @@ The time limit is mainly there so that a `Qukey` can be used as a modifier (in i alternate state) with a second input device (e.g. a mouse). It can be quite short (200ms is probably short enough) -- as long as your "taps" while typing are shorter than the time limit, you won't get any unintended alternate keycodes. + +## Further reading + +The [example][plugin:example] can help to learn how to use this plugin. + + [plugin:example]: https://github.com/keyboardio/Kaleidoscope-Qukeys/blob/master/examples/Qukeys/Qukeys.ino From d39b258cd23eb0f13f78e9119ca0b4347ed69264 Mon Sep 17 00:00:00 2001 From: Jean-Martin Archer Date: Fri, 10 Aug 2018 20:03:41 -0700 Subject: [PATCH 63/72] Add some additional documentation for how to define Qukeys and what the Model 01's keymap looks like. (Commit edited by @obra) --- README.md | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index fba93073..60d0c9ef 100644 --- a/README.md +++ b/README.md @@ -26,13 +26,23 @@ one keycode (i.e. symbol) when tapped, and a different keycode -- most likely a ``` KALEIDOSCOPE_INIT_PLUGINS(Qukeys); ``` -- Define some `Qukeys` of the format `Qukey(layer, row, col, alt_keycode)` (layers, rows and columns are all zero-indexed, rows are top to bottom and columns are left to right): + +- Define some `Qukeys` of the format `Qukey(layer, row, col, alt_keycode)` + (layers, rows and columns are all zero-indexed, rows are top to bottom and + columns are left to right): + +- For the Keyboardio Model 01, key coordinates refer to [this header + file](https://github.com/keyboardio/Kaleidoscope-Hardware-Model01/blob/f469015346535cb864a340bf8eb317d268943248/src/Kaleidoscope-Hardware-Model01.h#L267-L279). + ``` QUKEYS( + // l, r, c, alt_keycode kaleidoscope::Qukey(0, 2, 1, Key_LeftGui), // A/cmd kaleidoscope::Qukey(0, 2, 2, Key_LeftAlt), // S/alt kaleidoscope::Qukey(0, 2, 3, Key_LeftControl), // D/ctrl - kaleidoscope::Qukey(0, 2, 4, Key_LeftShift) // F/shift + kaleidoscope::Qukey(0, 2, 4, Key_LeftShift), // F/shift + kaleidoscope::Qukey(0, 1, 14, Key_LeftShift), // P/shift + kaleidoscope::Qukey(0, 3, 15, Key_LeftShift) // Minus/shift ) ``` From 23f5e60a5aba1c481c0d622f1c42659cdd6537ad Mon Sep 17 00:00:00 2001 From: Jesse Vincent Date: Fri, 10 Aug 2018 20:04:52 -0700 Subject: [PATCH 64/72] Whitespace only changes --- README.md | 80 +++++++++++++++++++++++++++---------------------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index 60d0c9ef..22fd87f2 100644 --- a/README.md +++ b/README.md @@ -67,48 +67,48 @@ likely to generate errors and out-of-order events. ### DualUse key definitions In addition to normal `Qukeys` described above, Kaleidoscope-Qukeys also treats -DualUse keys in the keymap as `Qukeys`. This makes `Qukeys` a drop-in replacement +DualUse keys in the keymap as `Qukeys`. This makes `Qukeys` a drop-in replacement for the `DualUse` plugin, without the need to edit the keymap. - -The plugin provides a number of macros one can use in keymap definitions: - -#### `CTL_T(key)` - -> A key that acts as the *left* `Control` when held, or used in conjunction with -> other keys, but as `key` when tapped in isolation. The `key` argument must be -> a plain old key, and can't have any modifiers or anything else applied. - -#### `ALT_T(key)` - -> A key that acts as the *left* `Alt` when held, or used in conjunction with -> other keys, but as `key` when tapped in isolation. The `key` argument must be -> a plain old key, and can't have any modifiers or anything else applied. - -#### `SFT_T(key)` - -> A key that acts as the *left* `Shift` when held, or used in conjunction with -> other keys, but as `key` when tapped in isolation. The `key` argument must be -> a plain old key, and can't have any modifiers or anything else applied. - -#### `GUI_T(key)` - -> A key that acts as the *left* `GUI` when held, or used in conjunction with -> other keys, but as `key` when tapped in isolation. The `key` argument must be -> a plain old key, and can't have any modifiers or anything else applied. - -#### `MT(mod, key)` - -> A key that acts as `mod` when held, or used in conjunction with other keys, -> but as `key` when tapped in isolation. The `key` argument must be a plain old -> key, and can't have any modifiers or anything else applied. The `mod` argument -> can be any of the modifiers, *left* or *right* alike. - -#### `LT(layer, key)` - -> A key that momentarily switches to `layer` when held, or used in conjunction -> with other keys, but as `key` when tapped in isolation. The `key` argument -> must be a plain old key, and can't have any modifiers or anything else + +The plugin provides a number of macros one can use in keymap definitions: + +#### `CTL_T(key)` + +> A key that acts as the *left* `Control` when held, or used in conjunction with +> other keys, but as `key` when tapped in isolation. The `key` argument must be +> a plain old key, and can't have any modifiers or anything else applied. + +#### `ALT_T(key)` + +> A key that acts as the *left* `Alt` when held, or used in conjunction with +> other keys, but as `key` when tapped in isolation. The `key` argument must be +> a plain old key, and can't have any modifiers or anything else applied. + +#### `SFT_T(key)` + +> A key that acts as the *left* `Shift` when held, or used in conjunction with +> other keys, but as `key` when tapped in isolation. The `key` argument must be +> a plain old key, and can't have any modifiers or anything else applied. + +#### `GUI_T(key)` + +> A key that acts as the *left* `GUI` when held, or used in conjunction with +> other keys, but as `key` when tapped in isolation. The `key` argument must be +> a plain old key, and can't have any modifiers or anything else applied. + +#### `MT(mod, key)` + +> A key that acts as `mod` when held, or used in conjunction with other keys, +> but as `key` when tapped in isolation. The `key` argument must be a plain old +> key, and can't have any modifiers or anything else applied. The `mod` argument +> can be any of the modifiers, *left* or *right* alike. + +#### `LT(layer, key)` + +> A key that momentarily switches to `layer` when held, or used in conjunction +> with other keys, but as `key` when tapped in isolation. The `key` argument +> must be a plain old key, and can't have any modifiers or anything else > applied. ## Design & Implementation From 4d8fa5d5dad9ceae2f5efee5ef233f57ccd0c829 Mon Sep 17 00:00:00 2001 From: Gergely Nagy Date: Mon, 20 Aug 2018 23:23:26 +0200 Subject: [PATCH 65/72] Drop the V1 plugin API compatibility code Signed-off-by: Gergely Nagy --- src/Kaleidoscope/Qukeys.cpp | 22 ---------------------- src/Kaleidoscope/Qukeys.h | 6 ------ 2 files changed, 28 deletions(-) diff --git a/src/Kaleidoscope/Qukeys.cpp b/src/Kaleidoscope/Qukeys.cpp index 18292dff..366f9a96 100644 --- a/src/Kaleidoscope/Qukeys.cpp +++ b/src/Kaleidoscope/Qukeys.cpp @@ -390,28 +390,6 @@ EventHandlerResult Qukeys::onSetup() { return EventHandlerResult::OK; } -// Legacy V1 API -#if KALEIDOSCOPE_ENABLE_V1_PLUGIN_API -void Qukeys::begin() { - onSetup(); - Kaleidoscope.useEventHandlerHook(legacyEventHandler); - Kaleidoscope.useLoopHook(legacyLoopHook); -} - -Key Qukeys::legacyEventHandler(Key mapped_key, byte row, byte col, uint8_t key_state) { - EventHandlerResult r = ::Qukeys.onKeyswitchEvent(mapped_key, row, col, key_state); - if (r == EventHandlerResult::OK) - return mapped_key; - return Key_NoKey; -} - -void Qukeys::legacyLoopHook(bool is_post_clear) { - if (is_post_clear) - return; - ::Qukeys.beforeReportingState(); -} -#endif - } // namespace kaleidoscope { kaleidoscope::Qukeys Qukeys; diff --git a/src/Kaleidoscope/Qukeys.h b/src/Kaleidoscope/Qukeys.h index 959bc7f3..5b3508a3 100644 --- a/src/Kaleidoscope/Qukeys.h +++ b/src/Kaleidoscope/Qukeys.h @@ -104,12 +104,6 @@ class Qukeys : public kaleidoscope::Plugin { EventHandlerResult onKeyswitchEvent(Key &mapped_key, byte row, byte col, uint8_t key_state); EventHandlerResult beforeReportingState(); -#if KALEIDOSCOPE_ENABLE_V1_PLUGIN_API - void begin(); - static Key legacyEventHandler(Key mapped_key, byte row, byte col, uint8_t key_state); - static void legacyLoopHook(bool is_post_clear); -#endif - private: static bool active_; static uint16_t time_limit_; From c5b3b2fe753ec9869276cd6e41f0df4c8e2fa284 Mon Sep 17 00:00:00 2001 From: Jesse Vincent Date: Tue, 4 Sep 2018 18:24:02 -0700 Subject: [PATCH 66/72] Update travis.yml to point to new bundle repo --- .travis.yml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 1fbeb196..e3e64f77 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,11 +2,23 @@ dist: trusty sudo: false os: - linux +addons: + apt: + packages: + - shellcheck install: - - git clone --recursive https://github.com/keyboardio/Arduino-Boards hardware/keyboardio/avr + - git clone --depth 1 --recurse-submodules https://github.com/keyboardio/Kaleidoscope-Bundle-Keyboardio hardware/keyboardio script: - make travis-test BOARD_HARDWARE_PATH=$(pwd)/hardware + - shellcheck bin/kaleidoscope-builder notifications: + irc: + channels: + - "chat.freenode.net#keyboardio" + use_notice: true + skip_join: true + template: + - "%{repository_name}/%{branch} %{commit} by %{author}: %{commit_subject} %{build_url} %{message}" email: on_success: change on_failure: change From f79fc343401c53efec6b52a8b6b45bff14965679 Mon Sep 17 00:00:00 2001 From: Jesse Vincent Date: Tue, 4 Sep 2018 18:42:30 -0700 Subject: [PATCH 67/72] shellcheck should only be run in the Kaleidoscope repo --- .travis.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index e3e64f77..23d30ccc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,15 +2,10 @@ dist: trusty sudo: false os: - linux -addons: - apt: - packages: - - shellcheck install: - git clone --depth 1 --recurse-submodules https://github.com/keyboardio/Kaleidoscope-Bundle-Keyboardio hardware/keyboardio script: - make travis-test BOARD_HARDWARE_PATH=$(pwd)/hardware - - shellcheck bin/kaleidoscope-builder notifications: irc: channels: From 151efb438cdff666f35a44ae7f2c2083025a678d Mon Sep 17 00:00:00 2001 From: Gergely Nagy Date: Wed, 10 Oct 2018 06:53:40 +0200 Subject: [PATCH 68/72] README.md & Travis cleanup Remove the IRC notifications from Travis (we're not using IRC anymore), and the stable/experimental badge from README.md (because they aren't being kept up to date). Signed-off-by: Gergely Nagy --- .travis.yml | 7 ------- README.md | 4 ---- 2 files changed, 11 deletions(-) diff --git a/.travis.yml b/.travis.yml index 23d30ccc..49b8c498 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,13 +7,6 @@ install: script: - make travis-test BOARD_HARDWARE_PATH=$(pwd)/hardware notifications: - irc: - channels: - - "chat.freenode.net#keyboardio" - use_notice: true - skip_join: true - template: - - "%{repository_name}/%{branch} %{commit} by %{author}: %{commit_subject} %{build_url} %{message}" email: on_success: change on_failure: change diff --git a/README.md b/README.md index e6b5dbfe..46cca114 100644 --- a/README.md +++ b/README.md @@ -5,10 +5,6 @@ [travis:image]: https://travis-ci.org/keyboardio/Kaleidoscope-Qukeys.svg?branch=master [travis:status]: https://travis-ci.org/keyboardio/Kaleidoscope-Qukeys - [st:stable]: https://img.shields.io/badge/stable-✔-black.svg?style=flat&colorA=44cc11&colorB=494e52 - [st:broken]: https://img.shields.io/badge/broken-X-black.svg?style=flat&colorA=e05d44&colorB=494e52 - [st:experimental]: https://img.shields.io/badge/experimental----black.svg?style=flat&colorA=dfb317&colorB=494e52 - ## Concept This Kaleidoscope plugin allows you to overload keys on your keyboard so that they produce From 36ae6282ba42a65c45f2b65d28b3b39fcf171780 Mon Sep 17 00:00:00 2001 From: Gergely Nagy Date: Sun, 14 Oct 2018 10:20:23 +0200 Subject: [PATCH 69/72] Qukeys.cpp: Don't include `key_defs_keymaps.h` It's pulled in by anyway, no need to include it explicitly. Signed-off-by: Gergely Nagy --- src/Kaleidoscope/Qukeys.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Kaleidoscope/Qukeys.cpp b/src/Kaleidoscope/Qukeys.cpp index 366f9a96..343ac37c 100644 --- a/src/Kaleidoscope/Qukeys.cpp +++ b/src/Kaleidoscope/Qukeys.cpp @@ -21,7 +21,6 @@ #include #include #include -#include #ifdef ARDUINO_VIRTUAL #define debug_print(...) printf(__VA_ARGS__) From 7e4bcc49d5804b1dcdc18f0a4508a48e28e35d33 Mon Sep 17 00:00:00 2001 From: Gergely Nagy Date: Sat, 13 Oct 2018 15:07:44 +0200 Subject: [PATCH 70/72] Rearrange the file layout in preparation of becoming a monorepo Move the documentation to `doc/plugin/Qukeys.md`, sources under `src/kaleidoscope/plugin/` (appropriately namespaced). This is in preparation of merging plugins into a single monorepo. Signed-off-by: Gergely Nagy --- README.md | 151 +---------------- doc/plugin/Qukeys.md | 158 ++++++++++++++++++ examples/Qukeys/Qukeys.ino | 10 +- src/Kaleidoscope-Qukeys.h | 2 +- src/{ => kaleidoscope}/addr.h | 0 .../plugin}/Qukeys.cpp | 4 +- .../plugin}/Qukeys.h | 14 +- 7 files changed, 178 insertions(+), 161 deletions(-) create mode 100644 doc/plugin/Qukeys.md rename src/{ => kaleidoscope}/addr.h (100%) rename src/{Kaleidoscope => kaleidoscope/plugin}/Qukeys.cpp (99%) rename src/{Kaleidoscope => kaleidoscope/plugin}/Qukeys.h (94%) diff --git a/README.md b/README.md index 46cca114..62f4518b 100644 --- a/README.md +++ b/README.md @@ -5,153 +5,4 @@ [travis:image]: https://travis-ci.org/keyboardio/Kaleidoscope-Qukeys.svg?branch=master [travis:status]: https://travis-ci.org/keyboardio/Kaleidoscope-Qukeys -## Concept - -This Kaleidoscope plugin allows you to overload keys on your keyboard so that they produce -one keycode (i.e. symbol) when tapped, and a different keycode -- most likely a modifier -(e.g. `shift` or `alt`) -- when held. - - -## Setup - -- Include the header file: -``` -#include -``` -- Use the plugin in the `KALEIDOSCOPE_INIT_PLUGINS` macro: -``` -KALEIDOSCOPE_INIT_PLUGINS(Qukeys); -``` - -- Define some `Qukeys` of the format `Qukey(layer, row, col, alt_keycode)` - (layers, rows and columns are all zero-indexed, rows are top to bottom and - columns are left to right): - -- For the Keyboardio Model 01, key coordinates refer to [this header - file](https://github.com/keyboardio/Kaleidoscope-Hardware-Model01/blob/f469015346535cb864a340bf8eb317d268943248/src/Kaleidoscope-Hardware-Model01.h#L267-L279). - -``` -QUKEYS( - // l, r, c, alt_keycode - kaleidoscope::Qukey(0, 2, 1, Key_LeftGui), // A/cmd - kaleidoscope::Qukey(0, 2, 2, Key_LeftAlt), // S/alt - kaleidoscope::Qukey(0, 2, 3, Key_LeftControl), // D/ctrl - kaleidoscope::Qukey(0, 2, 4, Key_LeftShift), // F/shift - kaleidoscope::Qukey(0, 1, 14, Key_LeftShift), // P/shift - kaleidoscope::Qukey(0, 3, 15, Key_LeftShift) // Minus/shift -) -``` - -`Qukeys` will work best if it's the first plugin in the `use()` list, because when typing -overlap occurs, it will (temporarily) mask keys and block them from being processed by -other plugins. If those other plugins handle the keypress events first, it may not work as -expected. It doesn't _need_ to be first, but if it's `use()`'d after another plugin that -handles typing events, especially one that sends extra keyboard HID reports, it is more -likely to generate errors and out-of-order events. - - -## Configuration - -### `.setTimeout(time_limit)` - -> Sets the time length in milliseconds which determines if a key has been tapped or held. -> -> Defaults to 250. - -### `.setReleaseDelay(release_delay)` - -> Sets the time length in milliseconds to artificially delay the release of the Qukey. -> -> This is to accommodate users who are in the habit of releasing modifiers and the keys -> they modify (almost) simultaneously, since the Qukey may be detected as released -> *slightly* before the other key, which would not trigger the desired alternate keycode. -> -> It is best to keep this a very small value such as 20 to avoid over-extending the -> modifier to further keystrokes. -> -> Defaults to 0. - -### `.activate()` -### `.deactivate()` -### `.toggle()` - -> activate/deactivate `Qukeys` - -### DualUse key definitions - -In addition to normal `Qukeys` described above, Kaleidoscope-Qukeys also treats -DualUse keys in the keymap as `Qukeys`. This makes `Qukeys` a drop-in replacement -for the `DualUse` plugin, without the need to edit the keymap. - -The plugin provides a number of macros one can use in keymap definitions: - -#### `CTL_T(key)` - -> A key that acts as the *left* `Control` when held, or used in conjunction with -> other keys, but as `key` when tapped in isolation. The `key` argument must be -> a plain old key, and can't have any modifiers or anything else applied. - -#### `ALT_T(key)` - -> A key that acts as the *left* `Alt` when held, or used in conjunction with -> other keys, but as `key` when tapped in isolation. The `key` argument must be -> a plain old key, and can't have any modifiers or anything else applied. - -#### `SFT_T(key)` - -> A key that acts as the *left* `Shift` when held, or used in conjunction with -> other keys, but as `key` when tapped in isolation. The `key` argument must be -> a plain old key, and can't have any modifiers or anything else applied. - -#### `GUI_T(key)` - -> A key that acts as the *left* `GUI` when held, or used in conjunction with -> other keys, but as `key` when tapped in isolation. The `key` argument must be -> a plain old key, and can't have any modifiers or anything else applied. - -#### `MT(mod, key)` - -> A key that acts as `mod` when held, or used in conjunction with other keys, -> but as `key` when tapped in isolation. The `key` argument must be a plain old -> key, and can't have any modifiers or anything else applied. The `mod` argument -> can be any of the modifiers, *left* or *right* alike. - -#### `LT(layer, key)` - -> A key that momentarily switches to `layer` when held, or used in conjunction -> with other keys, but as `key` when tapped in isolation. The `key` argument -> must be a plain old key, and can't have any modifiers or anything else -> applied. - -## Design & Implementation - -When a `Qukey` is pressed, it doesn't immediately add a corresponding keycode to the HID -report; it adds that key to a queue, and waits until one of three things happens: - -1. a time limit is reached - -2. the `Qukey` is released - -3. a subsequently-pressed key is released - -Until one of those conditions is met, all subsequent keypresses are simply added to the -queue, and no new reports are sent to the host. Once a condition is met, the `Qukey` is -flushed from the queue, and so are any subsequent keypresses (up to, but not including, -the next `Qukey` that is still pressed). - -Basically, if you hold the `Qukey`, then press and release some other key, you'll get the -alternate keycode (probably a modifier) for the `Qukey`, even if you don't wait for a -timeout. If you're typing quickly, and there's some overlap between two keypresses, you -won't get the alternate keycode, and the keys will be reported in the order that they were -pressed -- as long as the keys are released in the same order they were pressed. - -The time limit is mainly there so that a `Qukey` can be used as a modifier (in its -alternate state) with a second input device (e.g. a mouse). It can be quite short (200ms -is probably short enough) -- as long as your "taps" while typing are shorter than the time -limit, you won't get any unintended alternate keycodes. - -## Further reading - -The [example][plugin:example] can help to learn how to use this plugin. - - [plugin:example]: https://github.com/keyboardio/Kaleidoscope-Qukeys/blob/master/examples/Qukeys/Qukeys.ino +See [doc/plugin/Qukeys.md](doc/plugin/Qukeys.md) for documentation. diff --git a/doc/plugin/Qukeys.md b/doc/plugin/Qukeys.md new file mode 100644 index 00000000..5fad43d9 --- /dev/null +++ b/doc/plugin/Qukeys.md @@ -0,0 +1,158 @@ +# Kaleidoscope-Qukeys + +## Concept + +This Kaleidoscope plugin allows you to overload keys on your keyboard so that they produce +one keycode (i.e. symbol) when tapped, and a different keycode -- most likely a modifier +(e.g. `shift` or `alt`) -- when held. + + +## Setup + +- Include the header file: +``` +#include +``` +- Use the plugin in the `KALEIDOSCOPE_INIT_PLUGINS` macro: +``` +KALEIDOSCOPE_INIT_PLUGINS(Qukeys); +``` + +- Define some `Qukeys` of the format `Qukey(layer, row, col, alt_keycode)` + (layers, rows and columns are all zero-indexed, rows are top to bottom and + columns are left to right): + +- For the Keyboardio Model 01, key coordinates refer to [this header + file](https://github.com/keyboardio/Kaleidoscope-Hardware-Model01/blob/f469015346535cb864a340bf8eb317d268943248/src/Kaleidoscope-Hardware-Model01.h#L267-L279). + +``` +QUKEYS( + // l, r, c, alt_keycode + kaleidoscope::plugin::Qukey(0, 2, 1, Key_LeftGui), // A/cmd + kaleidoscope::plugin::Qukey(0, 2, 2, Key_LeftAlt), // S/alt + kaleidoscope::plugin::Qukey(0, 2, 3, Key_LeftControl), // D/ctrl + kaleidoscope::plugin::Qukey(0, 2, 4, Key_LeftShift), // F/shift + kaleidoscope::plugin::Qukey(0, 1, 14, Key_LeftShift), // P/shift + kaleidoscope::plugin::Qukey(0, 3, 15, Key_LeftShift) // Minus/shift +) +``` + +`Qukeys` will work best if it's the first plugin in the `INIT()` list, because when typing +overlap occurs, it will (temporarily) mask keys and block them from being processed by +other plugins. If those other plugins handle the keypress events first, it may not work as +expected. It doesn't _need_ to be first, but if it's `INIT()`'d after another plugin that +handles typing events, especially one that sends extra keyboard HID reports, it is more +likely to generate errors and out-of-order events. + + +## Configuration + +### `.setTimeout(time_limit)` + +> Sets the time length in milliseconds which determines if a key has been tapped or held. +> +> Defaults to 250. + +### `.setReleaseDelay(release_delay)` + +> Sets the time length in milliseconds to artificially delay the release of the Qukey. +> +> This is to accommodate users who are in the habit of releasing modifiers and the keys +> they modify (almost) simultaneously, since the Qukey may be detected as released +> *slightly* before the other key, which would not trigger the desired alternate keycode. +> +> It is best to keep this a very small value such as 20 to avoid over-extending the +> modifier to further keystrokes. +> +> Defaults to 0. + +### `.activate()` +### `.deactivate()` +### `.toggle()` + +> activate/deactivate `Qukeys` + +### DualUse key definitions + +In addition to normal `Qukeys` described above, Kaleidoscope-Qukeys also treats +DualUse keys in the keymap as `Qukeys`. This makes `Qukeys` a drop-in replacement +for the `DualUse` plugin, without the need to edit the keymap. + +The plugin provides a number of macros one can use in keymap definitions: + +#### `CTL_T(key)` + +> A key that acts as the *left* `Control` when held, or used in conjunction with +> other keys, but as `key` when tapped in isolation. The `key` argument must be +> a plain old key, and can't have any modifiers or anything else applied. + +#### `ALT_T(key)` + +> A key that acts as the *left* `Alt` when held, or used in conjunction with +> other keys, but as `key` when tapped in isolation. The `key` argument must be +> a plain old key, and can't have any modifiers or anything else applied. + +#### `SFT_T(key)` + +> A key that acts as the *left* `Shift` when held, or used in conjunction with +> other keys, but as `key` when tapped in isolation. The `key` argument must be +> a plain old key, and can't have any modifiers or anything else applied. + +#### `GUI_T(key)` + +> A key that acts as the *left* `GUI` when held, or used in conjunction with +> other keys, but as `key` when tapped in isolation. The `key` argument must be +> a plain old key, and can't have any modifiers or anything else applied. + +#### `MT(mod, key)` + +> A key that acts as `mod` when held, or used in conjunction with other keys, +> but as `key` when tapped in isolation. The `key` argument must be a plain old +> key, and can't have any modifiers or anything else applied. The `mod` argument +> can be any of the modifiers, *left* or *right* alike. + +#### `LT(layer, key)` + +> A key that momentarily switches to `layer` when held, or used in conjunction +> with other keys, but as `key` when tapped in isolation. The `key` argument +> must be a plain old key, and can't have any modifiers or anything else +> applied. + +## Design & Implementation + +When a `Qukey` is pressed, it doesn't immediately add a corresponding keycode to the HID +report; it adds that key to a queue, and waits until one of three things happens: + +1. a time limit is reached + +2. the `Qukey` is released + +3. a subsequently-pressed key is released + +Until one of those conditions is met, all subsequent keypresses are simply added to the +queue, and no new reports are sent to the host. Once a condition is met, the `Qukey` is +flushed from the queue, and so are any subsequent keypresses (up to, but not including, +the next `Qukey` that is still pressed). + +Basically, if you hold the `Qukey`, then press and release some other key, you'll get the +alternate keycode (probably a modifier) for the `Qukey`, even if you don't wait for a +timeout. If you're typing quickly, and there's some overlap between two keypresses, you +won't get the alternate keycode, and the keys will be reported in the order that they were +pressed -- as long as the keys are released in the same order they were pressed. + +The time limit is mainly there so that a `Qukey` can be used as a modifier (in its +alternate state) with a second input device (e.g. a mouse). It can be quite short (200ms +is probably short enough) -- as long as your "taps" while typing are shorter than the time +limit, you won't get any unintended alternate keycodes. + +## Further reading + +The [example][plugin:example] can help to learn how to use this plugin. + + [plugin:example]: https://github.com/keyboardio/Kaleidoscope-Qukeys/blob/master/examples/Qukeys/Qukeys.ino + +## Upgrading + +Previous versions of `Qukeys` used `kaleidoscope::Qukey` objects within the +`QUKEYS` macro. In newer versions, this is `kaleidoscope::plugin::Qukey` +instead. The old name still works, but will be removed by 2019-01-14. diff --git a/examples/Qukeys/Qukeys.ino b/examples/Qukeys/Qukeys.ino index 71042774..c16558c8 100644 --- a/examples/Qukeys/Qukeys.ino +++ b/examples/Qukeys/Qukeys.ino @@ -64,11 +64,11 @@ KALEIDOSCOPE_INIT_PLUGINS(Qukeys, Macros); void setup() { QUKEYS( - kaleidoscope::Qukey(0, 2, 1, Key_LeftGui), // A/cmd - kaleidoscope::Qukey(0, 2, 2, Key_LeftAlt), // S/alt - kaleidoscope::Qukey(0, 2, 3, Key_LeftControl), // D/ctrl - kaleidoscope::Qukey(0, 2, 4, Key_LeftShift), // F/shift - kaleidoscope::Qukey(0, 3, 6, ShiftToLayer(1)) // Q/layer-shift (on `fn`) + kaleidoscope::plugin::Qukey(0, 2, 1, Key_LeftGui), // A/cmd + kaleidoscope::plugin::Qukey(0, 2, 2, Key_LeftAlt), // S/alt + kaleidoscope::plugin::Qukey(0, 2, 3, Key_LeftControl), // D/ctrl + kaleidoscope::plugin::Qukey(0, 2, 4, Key_LeftShift), // F/shift + kaleidoscope::plugin::Qukey(0, 3, 6, ShiftToLayer(1)) // Q/layer-shift (on `fn`) ) Qukeys.setTimeout(200); Qukeys.setReleaseDelay(20); diff --git a/src/Kaleidoscope-Qukeys.h b/src/Kaleidoscope-Qukeys.h index 3a0e70f1..7d93cde9 100644 --- a/src/Kaleidoscope-Qukeys.h +++ b/src/Kaleidoscope-Qukeys.h @@ -18,4 +18,4 @@ #pragma once -#include +#include diff --git a/src/addr.h b/src/kaleidoscope/addr.h similarity index 100% rename from src/addr.h rename to src/kaleidoscope/addr.h diff --git a/src/Kaleidoscope/Qukeys.cpp b/src/kaleidoscope/plugin/Qukeys.cpp similarity index 99% rename from src/Kaleidoscope/Qukeys.cpp rename to src/kaleidoscope/plugin/Qukeys.cpp index 343ac37c..d356224c 100644 --- a/src/Kaleidoscope/Qukeys.cpp +++ b/src/kaleidoscope/plugin/Qukeys.cpp @@ -30,6 +30,7 @@ namespace kaleidoscope { +namespace plugin { inline bool isDualUse(Key k) { @@ -389,6 +390,7 @@ EventHandlerResult Qukeys::onSetup() { return EventHandlerResult::OK; } +} // namespace plugin { } // namespace kaleidoscope { -kaleidoscope::Qukeys Qukeys; +kaleidoscope::plugin::Qukeys Qukeys; diff --git a/src/Kaleidoscope/Qukeys.h b/src/kaleidoscope/plugin/Qukeys.h similarity index 94% rename from src/Kaleidoscope/Qukeys.h rename to src/kaleidoscope/plugin/Qukeys.h index 5b3508a3..a25217c0 100644 --- a/src/Kaleidoscope/Qukeys.h +++ b/src/kaleidoscope/plugin/Qukeys.h @@ -19,7 +19,7 @@ #pragma once #include -#include +#include #include // Maximum length of the pending queue @@ -53,6 +53,7 @@ #define LT(layer, key) (Key) { .raw = kaleidoscope::ranges::DUL_FIRST + (layer << 8) + (Key_ ## key).keyCode } namespace kaleidoscope { +namespace plugin { // Data structure for an individual qukey struct Qukey { @@ -130,13 +131,18 @@ class Qukeys : public kaleidoscope::Plugin { static bool isQukey(uint8_t addr); }; +} // namespace plugin { + +// Backwards compatibility +typedef plugin::Qukey Qukey; + } // namespace kaleidoscope { -extern kaleidoscope::Qukeys Qukeys; +extern kaleidoscope::plugin::Qukeys Qukeys; // macro for use in sketch file to simplify definition of qukeys #define QUKEYS(qukey_defs...) { \ - static kaleidoscope::Qukey qk_table[] = { qukey_defs }; \ + static kaleidoscope::plugin::Qukey qk_table[] = { qukey_defs }; \ Qukeys.qukeys = qk_table; \ - Qukeys.qukeys_count = sizeof(qk_table) / sizeof(kaleidoscope::Qukey); \ + Qukeys.qukeys_count = sizeof(qk_table) / sizeof(kaleidoscope::plugin::Qukey); \ } From 7f6abbce52f32774429e946121d651a7a8086ecb Mon Sep 17 00:00:00 2001 From: Gergely Nagy Date: Sat, 13 Oct 2018 15:15:30 +0200 Subject: [PATCH 71/72] Remove files we do not need in the monorepo Signed-off-by: Gergely Nagy --- .astylerc | 6 - .gitignore | 33 --- .travis.yml | 14 - LICENSE | 674 --------------------------------------------- Makefile | 14 - README.md | 8 - library.properties | 10 - 7 files changed, 759 deletions(-) delete mode 100644 .astylerc delete mode 100644 .gitignore delete mode 100644 .travis.yml delete mode 100644 LICENSE delete mode 100644 Makefile delete mode 100644 README.md delete mode 100644 library.properties diff --git a/.astylerc b/.astylerc deleted file mode 100644 index 7ced720e..00000000 --- a/.astylerc +++ /dev/null @@ -1,6 +0,0 @@ ---style=google ---unpad-paren ---pad-header ---pad-oper ---indent-classes ---indent=spaces=2 diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 729af14c..00000000 --- a/.gitignore +++ /dev/null @@ -1,33 +0,0 @@ -# Prerequisites -*.d - -# Compiled Object files -*.slo -*.lo -*.o -*.obj - -# Precompiled Headers -*.gch -*.pch - -# Compiled Dynamic libraries -*.so -*.dylib -*.dll - -# Fortran module files -*.mod -*.smod - -# Compiled Static libraries -*.lai -*.la -*.a -*.lib - -# Executables -*.exe -*.out -*.app -output/ diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 49b8c498..00000000 --- a/.travis.yml +++ /dev/null @@ -1,14 +0,0 @@ -dist: trusty -sudo: false -os: - - linux -install: - - git clone --depth 1 --recurse-submodules https://github.com/keyboardio/Kaleidoscope-Bundle-Keyboardio hardware/keyboardio -script: - - make travis-test BOARD_HARDWARE_PATH=$(pwd)/hardware -notifications: - email: - on_success: change - on_failure: change -cache: - ccache: true diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 94a9ed02..00000000 --- a/LICENSE +++ /dev/null @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. diff --git a/Makefile b/Makefile deleted file mode 100644 index 8f830f44..00000000 --- a/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -# This stub makefile for a Kaleidoscope plugin pulls in -# all targets from the Kaleidoscope-Plugin library - -UNAME_S := $(shell uname -s) - -ifeq ($(UNAME_S),Darwin) -SKETCHBOOK_DIR ?= $(HOME)/Documents/Arduino/ -else -SKETCHBOOK_DIR ?= $(HOME)/Arduino -endif - -BOARD_HARDWARE_PATH ?= $(SKETCHBOOK_DIR)/hardware -KALEIDOSCOPE_PLUGIN_MAKEFILE_DIR ?= keyboardio/avr/build-tools/makefiles/ -include $(BOARD_HARDWARE_PATH)/$(KALEIDOSCOPE_PLUGIN_MAKEFILE_DIR)/rules.mk diff --git a/README.md b/README.md deleted file mode 100644 index 62f4518b..00000000 --- a/README.md +++ /dev/null @@ -1,8 +0,0 @@ -# Kaleidoscope-Qukeys - -[![Build Status][travis:image]][travis:status] - - [travis:image]: https://travis-ci.org/keyboardio/Kaleidoscope-Qukeys.svg?branch=master - [travis:status]: https://travis-ci.org/keyboardio/Kaleidoscope-Qukeys - -See [doc/plugin/Qukeys.md](doc/plugin/Qukeys.md) for documentation. diff --git a/library.properties b/library.properties deleted file mode 100644 index 6b2bf9ab..00000000 --- a/library.properties +++ /dev/null @@ -1,10 +0,0 @@ -name=Kaleidoscope-Qukeys -version=0.0.0 -author=Michael Richters -maintainer=Michael Richters -sentence=Assign two keycodes to a single key. -paragraph=See README for more information. -category=Communication -url=https://github.com/gedankenlab/Kaleidoscope-Qukeys -architectures=avr -dot_a_linkage=true From 3d21853dad3982239600b2842b168e1d60f0d2f5 Mon Sep 17 00:00:00 2001 From: Gergely Nagy Date: Thu, 18 Oct 2018 00:07:29 +0200 Subject: [PATCH 72/72] Update the documentation links Updates the example and dependency links in the documentation, to use URLs that are valid within the monorepo. Signed-off-by: Gergely Nagy --- doc/plugin/Qukeys.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/plugin/Qukeys.md b/doc/plugin/Qukeys.md index 5fad43d9..160efbf1 100644 --- a/doc/plugin/Qukeys.md +++ b/doc/plugin/Qukeys.md @@ -23,7 +23,7 @@ KALEIDOSCOPE_INIT_PLUGINS(Qukeys); columns are left to right): - For the Keyboardio Model 01, key coordinates refer to [this header - file](https://github.com/keyboardio/Kaleidoscope-Hardware-Model01/blob/f469015346535cb864a340bf8eb317d268943248/src/Kaleidoscope-Hardware-Model01.h#L267-L279). + file](Hardware-Model01/blob/f469015346535cb864a340bf8eb317d268943248/src/Kaleidoscope-Hardware-Model01.h#L267-L279)..md ``` QUKEYS( @@ -149,7 +149,7 @@ limit, you won't get any unintended alternate keycodes. The [example][plugin:example] can help to learn how to use this plugin. - [plugin:example]: https://github.com/keyboardio/Kaleidoscope-Qukeys/blob/master/examples/Qukeys/Qukeys.ino + [plugin:example]: ../../examples/Qukeys/Qukeys.ino ## Upgrading