diff --git a/Gopkg.lock b/Gopkg.lock
index 2a1e0c3..4320b14 100644
--- a/Gopkg.lock
+++ b/Gopkg.lock
@@ -50,6 +50,12 @@
revision = "b026a6fd291f00736db60738f4e83a79d26359cb"
version = "v2.2.0"
+[[projects]]
+ branch = "master"
+ name = "github.com/robotn/gohook"
+ packages = ["."]
+ revision = "3dccbf0083168096c576bf62abb3d5d4c916e2ea"
+
[[projects]]
name = "github.com/shirou/gopsutil"
packages = [
@@ -88,11 +94,11 @@
"unix",
"windows"
]
- revision = "49385e6e15226593f68b26af201feec29d5bba22"
+ revision = "d0be0721c37eeb5299f245a996a483160fc36940"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
- inputs-digest = "0fd06dbf29cbad04968ef5a7c12e61c18ab8595bcb2d30c9f35698f0873916d1"
+ inputs-digest = "b1299bcf48c5b674a219acd0d53d6e1dc9cd44f60a9448f74d2fb623eaf9b00a"
solver-name = "gps-cdcl"
solver-version = 1
diff --git a/cdeps/README.md b/cdeps/README.md
index b6354dd..3303cf7 100644
--- a/cdeps/README.md
+++ b/cdeps/README.md
@@ -1 +1 @@
-C language dependent package, better to compilation. You need to follow the relevant agreement and LICENSE.
\ No newline at end of file
+C language dependent package, better to compilation.
\ No newline at end of file
diff --git a/vendor/github.com/robotn/gohook/.gitignore b/vendor/github.com/robotn/gohook/.gitignore
new file mode 100644
index 0000000..daf913b
--- /dev/null
+++ b/vendor/github.com/robotn/gohook/.gitignore
@@ -0,0 +1,24 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+*.test
+*.prof
diff --git a/vendor/github.com/robotn/gohook/LICENSE b/vendor/github.com/robotn/gohook/LICENSE
new file mode 100644
index 0000000..9cecc1d
--- /dev/null
+++ b/vendor/github.com/robotn/gohook/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.
+
+ {one line to give the program's name and a brief idea of what it does.}
+ Copyright (C) {year} {name of author}
+
+ This program is free software: you can redistribute it 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:
+
+ {project} Copyright (C) {year} {fullname}
+ 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/vendor/github.com/robotn/gohook/README.md b/vendor/github.com/robotn/gohook/README.md
new file mode 100644
index 0000000..be006df
--- /dev/null
+++ b/vendor/github.com/robotn/gohook/README.md
@@ -0,0 +1,21 @@
+# gohook
+
+This is a work in progress.
+
+```Go
+package main
+
+import (
+ "fmt"
+
+ "go-vgo/robotn/gohook"
+)
+
+func main() {
+ // hook.AsyncHook()
+ veve := hook.AddEvent("v")
+ if veve == 0 {
+ fmt.Println("v...")
+ }
+}
+```
\ No newline at end of file
diff --git a/vendor/github.com/robotn/gohook/event/goEvent.h b/vendor/github.com/robotn/gohook/event/goEvent.h
new file mode 100644
index 0000000..b3467fb
--- /dev/null
+++ b/vendor/github.com/robotn/gohook/event/goEvent.h
@@ -0,0 +1,252 @@
+// Copyright 2016 The go-vgo Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// https://github.com/go-vgo/robotgo/blob/master/LICENSE
+//
+// Licensed under the Apache License, Version 2.0 or the MIT license
+// , at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#ifdef HAVE_CONFIG_H
+ #include
+#endif
+
+#include "pub.h"
+
+
+void dispatch_proc(iohook_event * const event) {
+ char buffer[256] = { 0 };
+ size_t length = snprintf(buffer, sizeof(buffer),
+ "id=%i,when=%" PRIu64 ",mask=0x%X",
+ event->type, event->time, event->mask);
+
+ switch (event->type) {
+ case EVENT_KEY_PRESSED:
+ // If the escape key is pressed, naturally terminate the program.
+ if (event->data.keyboard.keycode == VC_ESCAPE) {
+ // int status = hook_stop();
+ // switch (status) {
+ // // System level errors.
+ // case IOHOOK_ERROR_OUT_OF_MEMORY:
+ // loggerProc(LOG_LEVEL_ERROR, "Failed to allocate memory. (%#X)", status);
+ // break;
+
+ // case IOHOOK_ERROR_X_RECORD_GET_CONTEXT:
+ // // NOTE This is the only platform specific error that occurs on hook_stop().
+ // loggerProc(LOG_LEVEL_ERROR, "Failed to get XRecord context. (%#X)", status);
+ // break;
+
+ // // Default error.
+ // case IOHOOK_FAILURE:
+ // default:
+ // loggerProc(LOG_LEVEL_ERROR, "An unknown hook error occurred. (%#X)", status);
+ // break;
+ // }
+ }
+ case EVENT_KEY_RELEASED:
+ snprintf(buffer + length, sizeof(buffer) - length,
+ ",keycode=%u,rawcode=0x%X",
+ event->data.keyboard.keycode, event->data.keyboard.rawcode);
+ int akeyCode = (uint16_t) event->data.keyboard.keycode;
+
+ if (event->data.keyboard.keycode == VC_ESCAPE
+ && atoi(cevent) == 11) {
+ int stopEvent = stop_event();
+ // printf("stop_event%d\n", stopEvent);
+ cstatus = 0;
+ }
+
+ // printf("atoi(str)---%d\n", atoi(cevent));
+ if (akeyCode == atoi(cevent)) {
+ int stopEvent = stop_event();
+ // printf("%d\n", stopEvent);
+ cstatus = 0;
+ }
+ break;
+
+ case EVENT_KEY_TYPED:
+ snprintf(buffer + length, sizeof(buffer) - length,
+ ",keychar=%lc,rawcode=%u",
+ (uint16_t) event->data.keyboard.keychar,
+ event->data.keyboard.rawcode);
+
+ #ifdef WE_REALLY_WANT_A_POINTER
+ char *buf = malloc (6);
+ #else
+ char buf[6];
+ #endif
+
+ sprintf(buf, "%lc", (uint16_t) event->data.keyboard.keychar);
+
+ #ifdef WE_REALLY_WANT_A_POINTER
+ free (buf);
+ #endif
+
+ if (strcmp(buf, cevent) == 0) {
+ int stopEvent = stop_event();
+ // printf("%d\n", stopEvent);
+ cstatus = 0;
+ }
+ // return (char*) event->data.keyboard.keychar;
+ break;
+
+ case EVENT_MOUSE_PRESSED:
+ case EVENT_MOUSE_RELEASED:
+ case EVENT_MOUSE_CLICKED:
+ case EVENT_MOUSE_MOVED:
+ case EVENT_MOUSE_DRAGGED:
+ snprintf(buffer + length, sizeof(buffer) - length,
+ ",x=%i,y=%i,button=%i,clicks=%i",
+ event->data.mouse.x, event->data.mouse.y,
+ event->data.mouse.button, event->data.mouse.clicks);
+
+ int abutton = event->data.mouse.button;
+ int aclicks = event->data.mouse.clicks;
+ int amouse = -1;
+
+ if (strcmp(cevent, "mleft") == 0) {
+ amouse = 1;
+ }
+ if (strcmp(cevent, "mright") == 0) {
+ amouse = 2;
+ }
+ if (strcmp(cevent, "wheelDown") == 0) {
+ amouse = 4;
+ }
+ if (strcmp(cevent, "wheelUp") == 0) {
+ amouse = 5;
+ }
+ if (strcmp(cevent, "wheelLeft") == 0) {
+ amouse = 6;
+ }
+ if (strcmp(cevent, "wheelRight") == 0) {
+ amouse = 7;
+ }
+ if (abutton == amouse && aclicks == 1) {
+ int stopEvent = stop_event();
+ cstatus = 0;
+ }
+
+ break;
+
+ case EVENT_MOUSE_WHEEL:
+ snprintf(buffer + length, sizeof(buffer) - length,
+ ",type=%i,amount=%i,rotation=%i",
+ event->data.wheel.type, event->data.wheel.amount,
+ event->data.wheel.rotation);
+ break;
+
+ default:
+ break;
+ }
+
+ // fprintf(stdout, "----%s\n", buffer);
+}
+
+int add_event(char *key_event) {
+ // (uint16_t *)
+ cevent = key_event;
+ // Set the logger callback for library output.
+ hookSetlogger(&loggerProc);
+
+ // Set the event callback for IOhook events.
+ hook_set_dispatch_proc(&dispatch_proc);
+ // Start the hook and block.
+ // NOTE If EVENT_HOOK_ENABLED was delivered, the status will always succeed.
+ int status = hook_run();
+
+ switch (status) {
+ case IOHOOK_SUCCESS:
+ // Everything is ok.
+ break;
+
+ // System level errors.
+ case IOHOOK_ERROR_OUT_OF_MEMORY:
+ loggerProc(LOG_LEVEL_ERROR, "Failed to allocate memory. (%#X)", status);
+ break;
+
+
+ // X11 specific errors.
+ case IOHOOK_ERROR_X_OPEN_DISPLAY:
+ loggerProc(LOG_LEVEL_ERROR, "Failed to open X11 display. (%#X)", status);
+ break;
+
+ case IOHOOK_ERROR_X_RECORD_NOT_FOUND:
+ loggerProc(LOG_LEVEL_ERROR, "Unable to locate XRecord extension. (%#X)", status);
+ break;
+
+ case IOHOOK_ERROR_X_RECORD_ALLOC_RANGE:
+ loggerProc(LOG_LEVEL_ERROR, "Unable to allocate XRecord range. (%#X)", status);
+ break;
+
+ case IOHOOK_ERROR_X_RECORD_CREATE_CONTEXT:
+ loggerProc(LOG_LEVEL_ERROR, "Unable to allocate XRecord context. (%#X)", status);
+ break;
+
+ case IOHOOK_ERROR_X_RECORD_ENABLE_CONTEXT:
+ loggerProc(LOG_LEVEL_ERROR, "Failed to enable XRecord context. (%#X)", status);
+ break;
+
+
+ // Windows specific errors.
+ case IOHOOK_ERROR_SET_WINDOWS_HOOK_EX:
+ loggerProc(LOG_LEVEL_ERROR, "Failed to register low level windows hook. (%#X)", status);
+ break;
+
+
+ // Darwin specific errors.
+ case IOHOOK_ERROR_AXAPI_DISABLED:
+ loggerProc(LOG_LEVEL_ERROR, "Failed to enable access for assistive devices. (%#X)", status);
+ break;
+
+ case IOHOOK_ERROR_CREATE_EVENT_PORT:
+ loggerProc(LOG_LEVEL_ERROR, "Failed to create apple event port. (%#X)", status);
+ break;
+
+ case IOHOOK_ERROR_CREATE_RUN_LOOP_SOURCE:
+ loggerProc(LOG_LEVEL_ERROR, "Failed to create apple run loop source. (%#X)", status);
+ break;
+
+ case IOHOOK_ERROR_GET_RUNLOOP:
+ loggerProc(LOG_LEVEL_ERROR, "Failed to acquire apple run loop. (%#X)", status);
+ break;
+
+ case IOHOOK_ERROR_CREATE_OBSERVER:
+ loggerProc(LOG_LEVEL_ERROR, "Failed to create apple run loop observer. (%#X)", status);
+ break;
+
+ // Default error.
+ case IOHOOK_FAILURE:
+ default:
+ loggerProc(LOG_LEVEL_ERROR, "An unknown hook error occurred. (%#X)", status);
+ break;
+ }
+
+ // return status;
+ // printf("%d\n", status);
+ return cstatus;
+}
+
+int stop_event(){
+ int status = hook_stop();
+ switch (status) {
+ // System level errors.
+ case IOHOOK_ERROR_OUT_OF_MEMORY:
+ loggerProc(LOG_LEVEL_ERROR, "Failed to allocate memory. (%#X)", status);
+ break;
+
+ case IOHOOK_ERROR_X_RECORD_GET_CONTEXT:
+ // NOTE This is the only platform specific error that occurs on hook_stop().
+ loggerProc(LOG_LEVEL_ERROR, "Failed to get XRecord context. (%#X)", status);
+ break;
+
+ // Default error.
+ case IOHOOK_FAILURE:
+ default:
+ // loggerProc(LOG_LEVEL_ERROR, "An unknown hook error occurred. (%#X)", status);
+ break;
+ }
+
+ return status;
+}
\ No newline at end of file
diff --git a/vendor/github.com/robotn/gohook/event/os.h b/vendor/github.com/robotn/gohook/event/os.h
new file mode 100644
index 0000000..ab46989
--- /dev/null
+++ b/vendor/github.com/robotn/gohook/event/os.h
@@ -0,0 +1,49 @@
+#pragma once
+#ifndef OS_H
+#define OS_H
+
+/* Python versions under 2.5 don't support this macro, but it's not
+ * terribly difficult to replicate: */
+#ifndef PyModule_AddIntMacro
+ #define PyModule_AddIntMacro(module, macro) \
+ PyModule_AddIntConstant(module, #macro, macro)
+#endif /* PyModule_AddIntMacro */
+
+#if !defined(IS_MACOSX) && defined(__APPLE__) && defined(__MACH__)
+ #define IS_MACOSX
+#endif /* IS_MACOSX */
+
+#if !defined(IS_WINDOWS) && (defined(WIN32) || defined(_WIN32) || \
+ defined(__WIN32__) || defined(__WINDOWS__) || defined(__CYGWIN__))
+ #define IS_WINDOWS
+#endif /* IS_WINDOWS */
+
+#if !defined(USE_X11) && !defined(NUSE_X11) && !defined(IS_MACOSX) && !defined(IS_WINDOWS)
+ #define USE_X11
+#endif /* USE_X11 */
+
+#if defined(IS_WINDOWS)
+ #define STRICT /* Require use of exact types. */
+ #define WIN32_LEAN_AND_MEAN 1 /* Speed up compilation. */
+ #include
+#elif !defined(IS_MACOSX) && !defined(USE_X11)
+ #error "Sorry, this platform isn't supported yet!"
+#endif
+
+/* Interval to align by for large buffers (e.g. bitmaps). */
+/* Must be a power of 2. */
+#ifndef BYTE_ALIGN
+ #define BYTE_ALIGN 4 /* Bytes to align pixel buffers to. */
+ /* #include */
+ /* #define BYTE_ALIGN (sizeof(size_t)) */
+#endif /* BYTE_ALIGN */
+
+#if BYTE_ALIGN == 0
+ /* No alignment needed. */
+ #define ADD_PADDING(width) (width)
+#else
+ /* Aligns given width to padding. */
+ #define ADD_PADDING(width) (BYTE_ALIGN + (((width) - 1) & ~(BYTE_ALIGN - 1)))
+#endif
+
+#endif /* OS_H */
diff --git a/vendor/github.com/robotn/gohook/event/pub.h b/vendor/github.com/robotn/gohook/event/pub.h
new file mode 100644
index 0000000..408f113
--- /dev/null
+++ b/vendor/github.com/robotn/gohook/event/pub.h
@@ -0,0 +1,95 @@
+// Copyright 2016 The go-vgo Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// https://github.com/go-vgo/robotgo/blob/master/LICENSE
+//
+// Licensed under the Apache License, Version 2.0 or the MIT license
+// , at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#include "os.h"
+
+#if defined(IS_MACOSX)
+ #include "../hook/darwin/input_c.h"
+ #include "../hook/darwin/hook_c.h"
+ #include "../hook/darwin/event_c.h"
+ #include "../hook/darwin/properties_c.h"
+#elif defined(USE_X11)
+ //#define USE_XKBCOMMON 0
+ #include "../hook/x11/input_c.h"
+ #include "../hook/x11/hook_c.h"
+ #include "../hook/x11/event_c.h"
+ #include "../hook/x11/properties_c.h"
+#elif defined(IS_WINDOWS)
+ #include "../hook/windows/input_c.h"
+ #include "../hook/windows/hook_c.h"
+ #include "../hook/windows/event_c.h"
+ #include "../hook/windows/properties_c.h"
+#endif
+
+#include
+#include
+#include
+#include
+#include
+#include "../hook/iohook.h"
+
+
+int vccode[100];
+int codesz;
+
+char *cevent;
+int rrevent;
+// uint16_t *cevent;
+int cstatus = 1;
+
+
+int stop_event();
+int add_event(char *key_event);
+// int allEvent(char *key_event);
+int allEvent(char *key_event, int vcode[], int size);
+
+// NOTE: The following callback executes on the same thread that hook_run() is called
+// from.
+
+struct _MEvent {
+ uint8_t id;
+ size_t mask;
+ uint16_t keychar;
+ // char *keychar;
+ size_t x;
+ uint8_t y;
+ uint8_t bytesPerPixel;
+};
+
+typedef struct _MEvent MEvent;
+// typedef MMBitmap *MMBitmapRef;
+
+MEvent mEvent;
+
+
+bool loggerProc(unsigned int level, const char *format, ...) {
+ bool status = false;
+
+ va_list args;
+ switch (level) {
+ #ifdef USE_DEBUG
+ case LOG_LEVEL_DEBUG:
+ case LOG_LEVEL_INFO:
+ va_start(args, format);
+ status = vfprintf(stdout, format, args) >= 0;
+ va_end(args);
+ break;
+ #endif
+
+ case LOG_LEVEL_WARN:
+ case LOG_LEVEL_ERROR:
+ va_start(args, format);
+ status = vfprintf(stderr, format, args) >= 0;
+ va_end(args);
+ break;
+ }
+
+ return status;
+}
\ No newline at end of file
diff --git a/vendor/github.com/robotn/gohook/hook.go b/vendor/github.com/robotn/gohook/hook.go
new file mode 100644
index 0000000..a5b3b91
--- /dev/null
+++ b/vendor/github.com/robotn/gohook/hook.go
@@ -0,0 +1,35 @@
+package hook
+
+/*
+#cgo darwin CFLAGS: -x objective-c -Wno-deprecated-declarations
+#cgo darwin LDFLAGS: -framework Cocoa
+#cgo linux CFLAGS:-I/usr/src
+#cgo linux LDFLAGS: -L/usr/src -lX11 -lXtst
+#cgo linux LDFLAGS: -lX11-xcb -lxcb -lxcb-xkb -lxkbcommon -lxkbcommon-x11
+#cgo windows LDFLAGS: -lgdi32 -luser32
+
+#include "event/goEvent.h"
+// #include "event/hook_async.h"
+*/
+import "C"
+
+import(
+ // "fmt"
+ "unsafe"
+)
+
+// AddEvent add event listener
+func AddEvent(key string) int {
+ cs := C.CString(key)
+
+ eve := C.add_event(cs)
+ geve := int(eve)
+
+ defer C.free(unsafe.Pointer(cs))
+ return geve
+}
+
+// StopEvent stop event listener
+func StopEvent() {
+ C.stop_event()
+}
\ No newline at end of file
diff --git a/vendor/github.com/robotn/gohook/hook/darwin/event_c.h b/vendor/github.com/robotn/gohook/hook/darwin/event_c.h
new file mode 100644
index 0000000..c621b71
--- /dev/null
+++ b/vendor/github.com/robotn/gohook/hook/darwin/event_c.h
@@ -0,0 +1,235 @@
+
+#ifdef HAVE_CONFIG_H
+ #include
+#endif
+
+#include
+#include
+#include
+#include
+#include "../iohook.h"
+
+// #include "../logger_c.h"
+#include "input.h"
+
+// TODO Possibly relocate to input helper.
+static inline CGEventFlags get_key_event_mask(iohook_event * const event) {
+ CGEventFlags native_mask = 0x00;
+
+ if (event->mask & (MASK_SHIFT)) { native_mask |= kCGEventFlagMaskShift; }
+ if (event->mask & (MASK_CTRL)) { native_mask |= kCGEventFlagMaskControl; }
+ if (event->mask & (MASK_META)) { native_mask |= kCGEventFlagMaskControl; }
+ if (event->mask & (MASK_ALT)) { native_mask |= kCGEventFlagMaskAlternate; }
+
+ if (event->type == EVENT_KEY_PRESSED || event->type == EVENT_KEY_RELEASED || event->type == EVENT_KEY_TYPED) {
+ switch (event->data.keyboard.keycode) {
+ case VC_KP_0:
+ case VC_KP_1:
+ case VC_KP_2:
+ case VC_KP_3:
+ case VC_KP_4:
+ case VC_KP_5:
+ case VC_KP_6:
+ case VC_KP_7:
+ case VC_KP_8:
+ case VC_KP_9:
+
+ case VC_NUM_LOCK:
+ case VC_KP_ENTER:
+ case VC_KP_MULTIPLY:
+ case VC_KP_ADD:
+ case VC_KP_SEPARATOR:
+ case VC_KP_SUBTRACT:
+ case VC_KP_DIVIDE:
+ case VC_KP_COMMA:
+ native_mask |= kCGEventFlagMaskNumericPad;
+ break;
+ }
+ }
+
+ return native_mask;
+}
+
+static inline void post_key_event(iohook_event * const event) {
+ bool is_pressed = event->type == EVENT_KEY_PRESSED;
+
+ CGEventSourceRef src = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
+ CGEventRef cg_event = CGEventCreateKeyboardEvent(src,
+ (CGKeyCode) scancode_to_keycode(event->data.keyboard.keycode),
+ is_pressed);
+
+ CGEventSetFlags(cg_event, get_key_event_mask(event));
+ CGEventPost(kCGHIDEventTap, cg_event); // kCGSessionEventTap also works.
+ CFRelease(cg_event);
+ CFRelease(src);
+}
+
+static inline void post_mouse_button_event(iohook_event * const event, bool is_pressed) {
+ CGMouseButton mouse_button;
+ CGEventType mouse_type;
+ if (event->data.mouse.button == MOUSE_BUTTON1) {
+ if (is_pressed) {
+ mouse_type = kCGEventLeftMouseDown;
+ }
+ else {
+ mouse_type = kCGEventLeftMouseUp;
+ }
+ mouse_button = kCGMouseButtonLeft;
+ }
+ else if (event->data.mouse.button == MOUSE_BUTTON2) {
+ if (is_pressed) {
+ mouse_type = kCGEventRightMouseDown;
+ }
+ else {
+ mouse_type = kCGEventRightMouseUp;
+ }
+ mouse_button = kCGMouseButtonRight;
+ }
+ else {
+ if (is_pressed) {
+ mouse_type = kCGEventOtherMouseDown;
+ }
+ else {
+ mouse_type = kCGEventOtherMouseUp;
+ }
+ mouse_button = event->data.mouse.button - 1;
+ }
+
+ CGEventSourceRef src = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
+ CGEventRef cg_event = CGEventCreateMouseEvent(src,
+ mouse_type,
+ CGPointMake(
+ (CGFloat) event->data.mouse.x,
+ (CGFloat) event->data.mouse.y
+ ),
+ mouse_button
+ );
+ CGEventPost(kCGHIDEventTap, cg_event); // kCGSessionEventTap also works.
+ CFRelease(cg_event);
+ CFRelease(src);
+}
+
+static inline void post_mouse_wheel_event(iohook_event * const event) {
+ // FIXME Should I create a source event with the coords?
+ // It seems to automagically use the current location of the cursor.
+ // Two options: Query the mouse, move it to x/y, scroll, then move back
+ // OR disable x/y for scroll events on Windows & X11.
+ CGScrollEventUnit scroll_unit;
+ if (event->data.wheel.type == WHEEL_BLOCK_SCROLL) {
+ // Scrolling data is line-based.
+ scroll_unit = kCGScrollEventUnitLine;
+ }
+ else {
+ // Scrolling data is pixel-based.
+ scroll_unit = kCGScrollEventUnitPixel;
+ }
+
+ CGEventSourceRef src = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
+ CGEventRef cg_event = CGEventCreateScrollWheelEvent(src,
+ kCGScrollEventUnitLine,
+ // TODO Currently only support 1 wheel axis.
+ (CGWheelCount) 1, // 1 for Y-only, 2 for Y-X, 3 for Y-X-Z
+ event->data.wheel.amount * event->data.wheel.rotation);
+
+ CGEventPost(kCGHIDEventTap, cg_event); // kCGSessionEventTap also works.
+ CFRelease(cg_event);
+ CFRelease(src);
+}
+
+static inline void post_mouse_motion_event(iohook_event * const event) {
+ CGEventSourceRef src = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
+ CGEventRef cg_event;
+ if (event->mask >> 8 == 0x00) {
+ // No mouse flags.
+ cg_event = CGEventCreateMouseEvent(src,
+ kCGEventMouseMoved,
+ CGPointMake(
+ (CGFloat) event->data.mouse.x,
+ (CGFloat) event->data.mouse.y
+ ),
+ 0
+ );
+ }
+ else if (event->mask & MASK_BUTTON1) {
+ cg_event = CGEventCreateMouseEvent(src,
+ kCGEventLeftMouseDragged,
+ CGPointMake(
+ (CGFloat) event->data.mouse.x,
+ (CGFloat) event->data.mouse.y
+ ),
+ kCGMouseButtonLeft
+ );
+ }
+ else if (event->mask & MASK_BUTTON2) {
+ cg_event = CGEventCreateMouseEvent(src,
+ kCGEventRightMouseDragged,
+ CGPointMake(
+ (CGFloat) event->data.mouse.x,
+ (CGFloat) event->data.mouse.y
+ ),
+ kCGMouseButtonRight
+ );
+ }
+ else {
+ cg_event = CGEventCreateMouseEvent(src,
+ kCGEventOtherMouseDragged,
+ CGPointMake(
+ (CGFloat) event->data.mouse.x,
+ (CGFloat) event->data.mouse.y
+ ),
+ (event->mask >> 8) - 1
+ );
+ }
+
+ // kCGSessionEventTap also works.
+ CGEventPost(kCGHIDEventTap, cg_event);
+ CFRelease(cg_event);
+ CFRelease(src);
+}
+
+IOHOOK_API void hook_post_event(iohook_event * const event) {
+ switch (event->type) {
+ case EVENT_KEY_PRESSED:
+ case EVENT_KEY_RELEASED:
+ post_key_event(event);
+ break;
+
+
+ case EVENT_MOUSE_PRESSED:
+ post_mouse_button_event(event, true);
+ break;
+
+ case EVENT_MOUSE_RELEASED:
+ post_mouse_button_event(event, false);
+ break;
+
+ case EVENT_MOUSE_CLICKED:
+ post_mouse_button_event(event, true);
+ post_mouse_button_event(event, false);
+ break;
+
+ case EVENT_MOUSE_WHEEL:
+ post_mouse_wheel_event(event);
+ break;
+
+
+ case EVENT_MOUSE_MOVED:
+ case EVENT_MOUSE_DRAGGED:
+ post_mouse_motion_event(event);
+ break;
+
+
+ case EVENT_KEY_TYPED:
+ // FIXME Ignoreing EVENT_KEY_TYPED events.
+
+ case EVENT_HOOK_ENABLED:
+ case EVENT_HOOK_DISABLED:
+ // Ignore hook enabled / disabled events.
+
+ default:
+ // Ignore any other garbage.
+ logger(LOG_LEVEL_WARN, "%s [%u]: Ignoring post event type %#X\n",
+ __FUNCTION__, __LINE__, event->type);
+ break;
+ }
+}
diff --git a/vendor/github.com/robotn/gohook/hook/darwin/hook_c.h b/vendor/github.com/robotn/gohook/hook/darwin/hook_c.h
new file mode 100644
index 0000000..cb8b609
--- /dev/null
+++ b/vendor/github.com/robotn/gohook/hook/darwin/hook_c.h
@@ -0,0 +1,1362 @@
+
+#ifdef HAVE_CONFIG_H
+#include
+#endif
+
+#ifndef USE_WEAK_IMPORT
+ #include
+#endif
+#include
+#ifdef USE_OBJC
+ #include
+ #include
+#endif
+
+#include
+#include
+#include
+#include "../iohook.h"
+// #include "../logger_c.h"
+#include "input.h"
+
+typedef struct _hook_info {
+ CFMachPortRef port;
+ CFRunLoopSourceRef source;
+ CFRunLoopObserverRef observer;
+} hook_info;
+
+#ifdef USE_OBJC
+static id auto_release_pool;
+#endif
+
+// Event runloop reference.
+CFRunLoopRef event_loop;
+
+// Flag to restart the event tap incase of timeout.
+static Boolean restart_tap = false;
+
+// Modifiers for tracking key masks.
+static uint16_t current_modifiers = 0x0000;
+
+// Required to transport messages between the main runloop and our thread for
+// Unicode lookups.
+#define KEY_BUFFER_SIZE 4
+typedef struct {
+ CGEventRef event;
+ UniChar buffer[KEY_BUFFER_SIZE];
+ UniCharCount length;
+} TISMessage;
+TISMessage *tis_message;
+
+#ifdef USE_WEAK_IMPORT
+// Required to dynamically check for AXIsProcessTrustedWithOptions availability.
+extern void dispatch_get_main_queue() __attribute__((weak_import));
+extern void dispatch_sync_f(dispatch_queue_t queue, void *context, void (*function)(void *)) __attribute__((weak_import));
+#else
+#if __MAC_OS_X_VERSION_MAX_ALLOWED <= 1050
+typedef void* dispatch_queue_t;
+#endif
+static dispatch_queue_t (*dispatch_get_main_queue_f)();
+static void (*dispatch_sync_f_f)(dispatch_queue_t, void *, void (*function)(void *));
+#endif
+
+#if ! defined(USE_CARBON_LEGACY) && defined(USE_COREFOUNDATION)
+static CFRunLoopSourceRef src_msg_port;
+static CFRunLoopObserverRef observer;
+
+static pthread_cond_t msg_port_cond = PTHREAD_COND_INITIALIZER;
+static pthread_mutex_t msg_port_mutex = PTHREAD_MUTEX_INITIALIZER;
+#endif
+
+// Click count globals.
+static unsigned short click_count = 0;
+static CGEventTimestamp click_time = 0;
+static unsigned short int click_button = MOUSE_NOBUTTON;
+static bool mouse_dragged = false;
+
+// Structure for the current Unix epoch in milliseconds.
+static struct timeval system_time;
+
+// Virtual event pointer.
+static iohook_event event;
+
+// Event dispatch callback.
+static dispatcher_t dispatcher = NULL;
+
+IOHOOK_API void hook_set_dispatch_proc(dispatcher_t dispatch_proc) {
+ logger(LOG_LEVEL_DEBUG, "%s [%u]: Setting new dispatch callback to %#p.\n",
+ __FUNCTION__, __LINE__, dispatch_proc);
+
+ dispatcher = dispatch_proc;
+}
+
+// Send out an event if a dispatcher was set.
+static inline void dispatch_event(iohook_event *const event) {
+ if (dispatcher != NULL) {
+ logger(LOG_LEVEL_DEBUG, "%s [%u]: Dispatching event type %u.\n",
+ __FUNCTION__, __LINE__, event->type);
+
+ dispatcher(event);
+ }
+ else {
+ logger(LOG_LEVEL_WARN, "%s [%u]: No dispatch callback set!\n",
+ __FUNCTION__, __LINE__);
+ }
+}
+
+
+// Set the native modifier mask for future events.
+static inline void set_modifier_mask(uint16_t mask) {
+ current_modifiers |= mask;
+}
+
+// Unset the native modifier mask for future events.
+static inline void unset_modifier_mask(uint16_t mask) {
+ current_modifiers ^= mask;
+}
+
+// Get the current native modifier mask state.
+static inline uint16_t get_modifiers() {
+ return current_modifiers;
+}
+
+// Initialize the modifier mask to the current modifiers.
+static void initialize_modifiers() {
+ current_modifiers = 0x0000;
+
+ if (CGEventSourceKeyState(kCGEventSourceStateCombinedSessionState, kVK_Shift)) {
+ set_modifier_mask(MASK_SHIFT_L);
+ }
+ if (CGEventSourceKeyState(kCGEventSourceStateCombinedSessionState, kVK_RightShift)) {
+ set_modifier_mask(MASK_SHIFT_R);
+ }
+ if (CGEventSourceKeyState(kCGEventSourceStateCombinedSessionState, kVK_Control)) {
+ set_modifier_mask(MASK_CTRL_L);
+ }
+ if (CGEventSourceKeyState(kCGEventSourceStateCombinedSessionState, kVK_RightControl)) {
+ set_modifier_mask(MASK_CTRL_R);
+ }
+ if (CGEventSourceKeyState(kCGEventSourceStateCombinedSessionState, kVK_Option)) {
+ set_modifier_mask(MASK_ALT_L);
+ }
+ if (CGEventSourceKeyState(kCGEventSourceStateCombinedSessionState, kVK_RightOption)) {
+ set_modifier_mask(MASK_ALT_R);
+ }
+ if (CGEventSourceKeyState(kCGEventSourceStateCombinedSessionState, kVK_Command)) {
+ set_modifier_mask(MASK_META_L);
+ }
+ if (CGEventSourceKeyState(kCGEventSourceStateCombinedSessionState, kVK_RightCommand)) {
+ set_modifier_mask(MASK_META_R);
+ }
+
+ if (CGEventSourceButtonState(kCGEventSourceStateCombinedSessionState, kVK_LBUTTON)) {
+ set_modifier_mask(MASK_BUTTON1);
+ }
+ if (CGEventSourceButtonState(kCGEventSourceStateCombinedSessionState, kVK_RBUTTON)) {
+ set_modifier_mask(MASK_BUTTON2);
+ }
+ if (CGEventSourceButtonState(kCGEventSourceStateCombinedSessionState, kVK_MBUTTON)) {
+ set_modifier_mask(MASK_BUTTON3);
+ }
+ if (CGEventSourceButtonState(kCGEventSourceStateCombinedSessionState, kVK_XBUTTON1)) {
+ set_modifier_mask(MASK_BUTTON4);
+ }
+ if (CGEventSourceButtonState(kCGEventSourceStateCombinedSessionState, kVK_XBUTTON2)) {
+ set_modifier_mask(MASK_BUTTON5);
+ }
+
+ if (CGEventSourceFlagsState(kCGEventSourceStateCombinedSessionState) & kCGEventFlagMaskAlphaShift) {
+ set_modifier_mask(MASK_CAPS_LOCK);
+ }
+ // Best I can tell, OS X does not support Num or Scroll lock.
+ unset_modifier_mask(MASK_NUM_LOCK);
+ unset_modifier_mask(MASK_SCROLL_LOCK);
+}
+
+
+// Wrap keycode_to_unicode with some null checks.
+static void keycode_to_lookup(void *info) {
+ TISMessage *data = (TISMessage *) info;
+
+ if (data != NULL && data->event != NULL) {
+ // Preform Unicode lookup.
+ data->length = keycode_to_unicode(data->event, data->buffer, KEY_BUFFER_SIZE);
+ }
+}
+
+#if ! defined(USE_CARBON_LEGACY) && defined(USE_COREFOUNDATION)
+void message_port_status_proc(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info) {
+ switch (activity) {
+ case kCFRunLoopExit:
+ // Acquire a lock on the msg_port and signal that anyone waiting
+ // should continue.
+ pthread_mutex_lock(&msg_port_mutex);
+ pthread_cond_broadcast(&msg_port_cond);
+ pthread_mutex_unlock(&msg_port_mutex);
+ break;
+
+ default:
+ logger(LOG_LEVEL_WARN, "%s [%u]: Unhandled RunLoop activity! (%#X)\n",
+ __FUNCTION__, __LINE__, (unsigned int) activity);
+ break;
+ }
+}
+
+// Runloop to execute KeyCodeToString on the "Main" runloop due to an
+// undocumented thread safety requirement.
+static void message_port_proc(void *info) {
+ // Lock the msg_port mutex as we enter the main runloop.
+ pthread_mutex_lock(&msg_port_mutex);
+
+ keycode_to_lookup(info);
+
+ // Unlock the msg_port mutex to signal to the hook_thread that we have
+ // finished on the main runloop.
+ pthread_cond_broadcast(&msg_port_cond);
+ pthread_mutex_unlock(&msg_port_mutex);
+}
+
+static int start_message_port_runloop() {
+ int status = IOHOOK_FAILURE;
+
+ if (tis_message != NULL) {
+ // Create a runloop observer for the main runloop.
+ observer = CFRunLoopObserverCreate(
+ kCFAllocatorDefault,
+ kCFRunLoopExit, //kCFRunLoopEntry | kCFRunLoopExit, //kCFRunLoopAllActivities,
+ true,
+ 0,
+ message_port_status_proc,
+ NULL
+ );
+
+ if (observer != NULL) {
+ pthread_mutex_lock(&msg_port_mutex);
+
+ CFRunLoopSourceContext context = {
+ .version = 0,
+ .info = tis_message,
+ .retain = NULL,
+ .release = NULL,
+ .copyDescription = NULL,
+ .equal = NULL,
+ .hash = NULL,
+ .schedule = NULL,
+ .cancel = NULL,
+ .perform = message_port_proc
+ };
+
+ CFRunLoopRef main_loop = CFRunLoopGetMain();
+
+ src_msg_port = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);
+ if (src_msg_port != NULL) {
+ CFRunLoopAddSource(main_loop, src_msg_port, kCFRunLoopDefaultMode);
+ CFRunLoopAddObserver(main_loop, observer, kCFRunLoopDefaultMode);
+
+ logger(LOG_LEVEL_DEBUG, "%s [%u]: Successful.\n",
+ __FUNCTION__, __LINE__);
+
+ status = IOHOOK_SUCCESS;
+ }
+ else {
+ logger(LOG_LEVEL_ERROR, "%s [%u]: CFRunLoopSourceCreate failure!\n",
+ __FUNCTION__, __LINE__);
+
+ status = IOHOOK_ERROR_CREATE_RUN_LOOP_SOURCE;
+ }
+
+ pthread_mutex_unlock(&msg_port_mutex);
+ }
+ else {
+ logger(LOG_LEVEL_ERROR, "%s [%u]: CFRunLoopObserverCreate failure!\n",
+ __FUNCTION__, __LINE__);
+
+ status = IOHOOK_ERROR_CREATE_OBSERVER;
+ }
+ }
+ else {
+ logger(LOG_LEVEL_ERROR, "%s [%u]: No available TIS Message pointer.\n",
+ __FUNCTION__, __LINE__);
+ }
+
+ return status;
+}
+
+static void stop_message_port_runloop() {
+ CFRunLoopRef main_loop = CFRunLoopGetMain();
+
+ if (CFRunLoopContainsObserver(main_loop, observer, kCFRunLoopDefaultMode)) {
+ CFRunLoopRemoveObserver(main_loop, observer, kCFRunLoopDefaultMode);
+ CFRunLoopObserverInvalidate(observer);
+ }
+
+ if (CFRunLoopContainsSource(main_loop, src_msg_port, kCFRunLoopDefaultMode)) {
+ CFRunLoopRemoveSource(main_loop, src_msg_port, kCFRunLoopDefaultMode);
+ CFRelease(src_msg_port);
+ }
+
+ observer = NULL;
+ src_msg_port = NULL;
+
+ logger(LOG_LEVEL_DEBUG, "%s [%u]: Successful.\n",
+ __FUNCTION__, __LINE__);
+}
+#endif
+
+static void hook_status_proc(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info) {
+ uint64_t timestamp = mach_absolute_time();
+
+ switch (activity) {
+ case kCFRunLoopEntry:
+ // Populate the hook start event.
+ event.time = timestamp;
+ event.reserved = 0x00;
+
+ event.type = EVENT_HOOK_ENABLED;
+ event.mask = 0x00;
+
+ // Fire the hook start event.
+ dispatch_event(&event);
+ break;
+
+ case kCFRunLoopExit:
+ // Populate the hook stop event.
+ event.time = timestamp;
+ event.reserved = 0x00;
+
+ event.type = EVENT_HOOK_DISABLED;
+ event.mask = 0x00;
+
+ // Fire the hook stop event.
+ dispatch_event(&event);
+ break;
+
+ default:
+ logger(LOG_LEVEL_WARN, "%s [%u]: Unhandled RunLoop activity! (%#X)\n",
+ __FUNCTION__, __LINE__, (unsigned int) activity);
+ break;
+ }
+}
+
+static inline void process_key_pressed(uint64_t timestamp, CGEventRef event_ref) {
+ UInt64 keycode = CGEventGetIntegerValueField(event_ref, kCGKeyboardEventKeycode);
+
+ // Populate key pressed event.
+ event.time = timestamp;
+ event.reserved = 0x00;
+
+ event.type = EVENT_KEY_PRESSED;
+ event.mask = get_modifiers();
+
+ event.data.keyboard.keycode = keycode_to_scancode(keycode);
+ event.data.keyboard.rawcode = keycode;
+ event.data.keyboard.keychar = CHAR_UNDEFINED;
+
+ logger(LOG_LEVEL_INFO, "%s [%u]: Key %#X pressed. (%#X)\n",
+ __FUNCTION__, __LINE__, event.data.keyboard.keycode, event.data.keyboard.rawcode);
+
+ // Fire key pressed event.
+ dispatch_event(&event);
+
+ // If the pressed event was not consumed...
+ if (event.reserved ^ 0x01) {
+ tis_message->event = event_ref;
+ tis_message->length = 0;
+ bool is_runloop_main = CFEqual(event_loop, CFRunLoopGetMain());
+
+ #ifdef USE_WEAK_IMPORT
+ if (dispatch_sync_f != NULL && dispatch_get_main_queue != NULL && !is_runloop_main) {
+ logger(LOG_LEVEL_DEBUG, "%s [%u]: Using dispatch_sync_f for key typed events.\n", __FUNCTION__, __LINE__);
+ dispatch_sync_f(dispatch_get_main_queue(), tis_message, &keycode_to_lookup);
+ }
+ #else
+ if (dispatch_sync_f_f != NULL && dispatch_get_main_queue_f != NULL && !is_runloop_main) {
+ logger(LOG_LEVEL_DEBUG, "%s [%u]: Using *dispatch_sync_f_f for key typed events.\n", __FUNCTION__, __LINE__);
+ (*dispatch_sync_f_f)((*dispatch_get_main_queue_f)(), tis_message, &keycode_to_lookup);
+ }
+ #endif
+ #if ! defined(USE_CARBON_LEGACY) && defined(USE_COREFOUNDATION)
+ else if (!is_runloop_main) {
+ // Lock for code dealing with the main runloop.
+ pthread_mutex_lock(&msg_port_mutex);
+
+ // Check to see if the main runloop is still running.
+ // TOOD I would rather this be a check on hook_enable(),
+ // but it makes the usage complicated by requiring a separate
+ // thread for the main runloop and hook registration.
+ CFStringRef mode = CFRunLoopCopyCurrentMode(CFRunLoopGetMain());
+ if (mode != NULL) {
+ CFRelease(mode);
+
+ // Lookup the Unicode representation for this event.
+ //CFRunLoopSourceContext context = { .version = 0 };
+ //CFRunLoopSourceGetContext(src_msg_port, &context);
+
+ // Get the run loop context info pointer.
+ //TISMessage *info = (TISMessage *) context.info;
+
+ // Set the event pointer.
+ //info->event = event_ref;
+
+ // Signal the custom source and wakeup the main runloop.
+ CFRunLoopSourceSignal(src_msg_port);
+ CFRunLoopWakeUp(CFRunLoopGetMain());
+
+ // Wait for a lock while the main runloop processes they key typed event.
+ pthread_cond_wait(&msg_port_cond, &msg_port_mutex);
+ }
+ else {
+ logger(LOG_LEVEL_WARN, "%s [%u]: Failed to signal RunLoop main!\n",
+ __FUNCTION__, __LINE__);
+ }
+
+ // Unlock for code dealing with the main runloop.
+ pthread_mutex_unlock(&msg_port_mutex);
+ }
+ #endif
+ else {
+ keycode_to_lookup(tis_message);
+ }
+ unsigned int i;
+ for (i= 0; i < tis_message->length; i++) {
+ // Populate key typed event.
+ event.time = timestamp;
+ event.reserved = 0x00;
+
+ event.type = EVENT_KEY_TYPED;
+ event.mask = get_modifiers();
+
+ event.data.keyboard.keycode = VC_UNDEFINED;
+ event.data.keyboard.rawcode = keycode;
+ event.data.keyboard.keychar = tis_message->buffer[i];
+
+ logger(LOG_LEVEL_INFO, "%s [%u]: Key %#X typed. (%lc)\n",
+ __FUNCTION__, __LINE__, event.data.keyboard.keycode,
+ (wint_t) event.data.keyboard.keychar);
+
+ // Populate key typed event.
+ dispatch_event(&event);
+ }
+ }
+}
+
+static inline void process_key_released(uint64_t timestamp, CGEventRef event_ref) {
+ UInt64 keycode = CGEventGetIntegerValueField(event_ref, kCGKeyboardEventKeycode);
+
+ // Populate key released event.
+ event.time = timestamp;
+ event.reserved = 0x00;
+
+ event.type = EVENT_KEY_RELEASED;
+ event.mask = get_modifiers();
+
+ event.data.keyboard.keycode = keycode_to_scancode(keycode);
+ event.data.keyboard.rawcode = keycode;
+ event.data.keyboard.keychar = CHAR_UNDEFINED;
+
+ logger(LOG_LEVEL_INFO, "%s [%u]: Key %#X released. (%#X)\n",
+ __FUNCTION__, __LINE__, event.data.keyboard.keycode, event.data.keyboard.rawcode);
+
+ // Fire key released event.
+ dispatch_event(&event);
+}
+
+static inline void process_modifier_changed(uint64_t timestamp, CGEventRef event_ref) {
+ CGEventFlags event_mask = CGEventGetFlags(event_ref);
+ UInt64 keycode = CGEventGetIntegerValueField(event_ref, kCGKeyboardEventKeycode);
+
+ logger(LOG_LEVEL_INFO, "%s [%u]: Modifiers Changed for key %#X. (%#X)\n",
+ __FUNCTION__, __LINE__, (unsigned long) keycode, (unsigned int) event_mask);
+
+ /* Because Apple treats modifier keys differently than normal key
+ * events, any changes to the modifier keys will require a key state
+ * change to be fired manually.
+ *
+ * NOTE Left and right keyboard masks like NX_NEXTLSHIFTKEYMASK exist and
+ * appear to be in use on Darwin, however they are removed by comment or
+ * preprocessor with a note that reads "device-dependent (really?)." To
+ * ensure compatability, we will do this the verbose way.
+ *
+ * NOTE The masks for scroll and number lock are set in the key event.
+ */
+ if (keycode == kVK_Shift) {
+ if (event_mask & kCGEventFlagMaskShift) {
+ // Process as a key pressed event.
+ set_modifier_mask(MASK_SHIFT_L);
+ process_key_pressed(timestamp, event_ref);
+ }
+ else {
+ // Process as a key released event.
+ unset_modifier_mask(MASK_SHIFT_L);
+ process_key_released(timestamp, event_ref);
+ }
+ }
+ else if (keycode == kVK_Control) {
+ if (event_mask & kCGEventFlagMaskControl) {
+ // Process as a key pressed event.
+ set_modifier_mask(MASK_CTRL_L);
+ process_key_pressed(timestamp, event_ref);
+ }
+ else {
+ // Process as a key released event.
+ unset_modifier_mask(MASK_CTRL_L);
+ process_key_released(timestamp, event_ref);
+ }
+ }
+ else if (keycode == kVK_Command) {
+ if (event_mask & kCGEventFlagMaskCommand) {
+ // Process as a key pressed event.
+ set_modifier_mask(MASK_META_L);
+ process_key_pressed(timestamp, event_ref);
+ }
+ else {
+ // Process as a key released event.
+ unset_modifier_mask(MASK_META_L);
+ process_key_released(timestamp, event_ref);
+ }
+ }
+ else if (keycode == kVK_Option) {
+ if (event_mask & kCGEventFlagMaskAlternate) {
+ // Process as a key pressed event.
+ set_modifier_mask(MASK_ALT_L);
+ process_key_pressed(timestamp, event_ref);
+ }
+ else {
+ // Process as a key released event.
+ unset_modifier_mask(MASK_ALT_L);
+ process_key_released(timestamp, event_ref);
+ }
+ }
+ else if (keycode == kVK_RightShift) {
+ if (event_mask & kCGEventFlagMaskShift) {
+ // Process as a key pressed event.
+ set_modifier_mask(MASK_SHIFT_R);
+ process_key_pressed(timestamp, event_ref);
+ }
+ else {
+ // Process as a key released event.
+ unset_modifier_mask(MASK_SHIFT_R);
+ process_key_released(timestamp, event_ref);
+ }
+ }
+ else if (keycode == kVK_RightControl) {
+ if (event_mask & kCGEventFlagMaskControl) {
+ // Process as a key pressed event.
+ set_modifier_mask(MASK_CTRL_R);
+ process_key_pressed(timestamp, event_ref);
+ }
+ else {
+ // Process as a key released event.
+ unset_modifier_mask(MASK_CTRL_R);
+ process_key_released(timestamp, event_ref);
+ }
+ }
+ else if (keycode == kVK_RightCommand) {
+ if (event_mask & kCGEventFlagMaskCommand) {
+ // Process as a key pressed event.
+ set_modifier_mask(MASK_META_R);
+ process_key_pressed(timestamp, event_ref);
+ }
+ else {
+ // Process as a key released event.
+ unset_modifier_mask(MASK_META_R);
+ process_key_released(timestamp, event_ref);
+ }
+ }
+ else if (keycode == kVK_RightOption) {
+ if (event_mask & kCGEventFlagMaskAlternate) {
+ // Process as a key pressed event.
+ set_modifier_mask(MASK_ALT_R);
+ process_key_pressed(timestamp, event_ref);
+ }
+ else {
+ // Process as a key released event.
+ unset_modifier_mask(MASK_ALT_R);
+ process_key_released(timestamp, event_ref);
+ }
+ }
+ /* FIXME This should produce a modifier mask for the caps lock key!
+ else if (keycode == kVK_CapsLock) {
+ // Process as a key pressed event.
+ process_key_pressed(timestamp, event_ref);
+
+ // Set the caps-lock flag for release.
+ caps_down = true;
+ }
+ */
+}
+
+/* These events are totally undocumented for the CGEvent type, but are required to grab media and caps-lock keys.
+ */
+static inline void process_system_key(uint64_t timestamp, CGEventRef event_ref) {
+ if( CGEventGetType(event_ref) == NX_SYSDEFINED) {
+ #ifdef USE_OBJC
+ // Contributed by Iván Munsuri Ibáñez
+ id event_data = objc_msgSend((id) objc_getClass("NSEvent"), sel_registerName("eventWithCGEvent:"), event_ref);
+ int subtype = (int) objc_msgSend(event_data, sel_registerName("subtype"));
+ #else
+ CFDataRef data = CGEventCreateData(kCFAllocatorDefault, event_ref);
+ //CFIndex len = CFDataGetLength(data);
+ UInt8 *buffer = malloc(12);
+ // CFDataRef cf_data = CFDataCreate(NULL, [nsData bytes], [nsData length]);
+ CFDataGetBytes(data, CFRangeMake(108, 12), buffer);
+ UInt32 subtype = CFSwapInt32BigToHost(*((UInt32 *) buffer));
+ #endif
+ if (subtype == 8) {
+ #ifdef USE_OBJC
+ // int data = (int) objc_msgSend(event_data, sel_registerName("data1"));
+ uint16_t data = (uint16_t) objc_msgSend(event_data, sel_registerName("data1"))
+ #endif
+
+ // int
+ uint16_t key_code = ((uint16_t)data & 0xFFFF0000) >> 16;
+ uint16_t key_flags = ((uint16_t)data & 0xFFFF);
+ //int key_state = (key_flags & 0xFF00) >> 8;
+ bool key_down = (key_flags & 0x1) > 0;
+
+ if (key_code == NX_KEYTYPE_CAPS_LOCK) {
+ // It doesn't appear like we can modify the event coming in, so we will fabricate a new event.
+ CGEventSourceRef src = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
+ CGEventRef ns_event = CGEventCreateKeyboardEvent(src, kVK_CapsLock, key_down);
+ CGEventSetFlags(ns_event, CGEventGetFlags(event_ref));
+
+ if (key_down) {
+ process_key_pressed(timestamp, ns_event);
+ }
+ else {
+ process_key_released(timestamp, ns_event);
+ }
+
+ CFRelease(ns_event);
+ CFRelease(src);
+ }
+ else if (key_code == NX_KEYTYPE_SOUND_UP) {
+ // It doesn't appear like we can modify the event coming in, so we will fabricate a new event.
+ CGEventSourceRef src = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
+ CGEventRef ns_event = CGEventCreateKeyboardEvent(src, kVK_VolumeUp, key_down);
+ CGEventSetFlags(ns_event, CGEventGetFlags(event_ref));
+
+ if (key_down) {
+ process_key_pressed(timestamp, ns_event);
+ }
+ else {
+ process_key_released(timestamp, ns_event);
+ }
+
+ CFRelease(ns_event);
+ CFRelease(src);
+ }
+ else if (key_code == NX_KEYTYPE_SOUND_DOWN) {
+ // It doesn't appear like we can modify the event coming in, so we will fabricate a new event.
+ CGEventSourceRef src = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
+ CGEventRef ns_event = CGEventCreateKeyboardEvent(src, kVK_VolumeDown, key_down);
+ CGEventSetFlags(ns_event, CGEventGetFlags(event_ref));
+
+ if (key_down) {
+ process_key_pressed(timestamp, ns_event);
+ }
+ else {
+ process_key_released(timestamp, ns_event);
+ }
+
+ CFRelease(ns_event);
+ CFRelease(src);
+ }
+ else if (key_code == NX_KEYTYPE_MUTE) {
+ // It doesn't appear like we can modify the event coming in, so we will fabricate a new event.
+ CGEventSourceRef src = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
+ CGEventRef ns_event = CGEventCreateKeyboardEvent(src, kVK_Mute, key_down);
+ CGEventSetFlags(ns_event, CGEventGetFlags(event_ref));
+
+ if (key_down) {
+ process_key_pressed(timestamp, ns_event);
+ }
+ else {
+ process_key_released(timestamp, ns_event);
+ }
+
+ CFRelease(ns_event);
+ CFRelease(src);
+ }
+
+ else if (key_code == NX_KEYTYPE_EJECT) {
+ // It doesn't appear like we can modify the event coming in, so we will fabricate a new event.
+ CGEventSourceRef src = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
+ CGEventRef ns_event = CGEventCreateKeyboardEvent(src, kVK_NX_Eject, key_down);
+ CGEventSetFlags(ns_event, CGEventGetFlags(event_ref));
+
+ if (key_down) {
+ process_key_pressed(timestamp, ns_event);
+ }
+ else {
+ process_key_released(timestamp, ns_event);
+ }
+
+ CFRelease(ns_event);
+ CFRelease(src);
+ }
+ else if (key_code == NX_KEYTYPE_PLAY) {
+ // It doesn't appear like we can modify the event coming in, so we will fabricate a new event.
+ CGEventSourceRef src = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
+ CGEventRef ns_event = CGEventCreateKeyboardEvent(src, kVK_MEDIA_Play, key_down);
+ CGEventSetFlags(ns_event, CGEventGetFlags(event_ref));
+
+ if (key_down) {
+ process_key_pressed(timestamp, ns_event);
+ }
+ else {
+ process_key_released(timestamp, ns_event);
+ }
+
+ CFRelease(ns_event);
+ CFRelease(src);
+ }
+ else if (key_code == NX_KEYTYPE_FAST) {
+ // It doesn't appear like we can modify the event coming in, so we will fabricate a new event.
+ CGEventSourceRef src = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
+ CGEventRef ns_event = CGEventCreateKeyboardEvent(src, kVK_MEDIA_Next, key_down);
+ CGEventSetFlags(ns_event, CGEventGetFlags(event_ref));
+
+ if (key_down) {
+ process_key_pressed(timestamp, ns_event);
+ }
+ else {
+ process_key_released(timestamp, ns_event);
+ }
+
+ CFRelease(ns_event);
+ CFRelease(src);
+ }
+ else if (key_code == NX_KEYTYPE_REWIND) {
+ // It doesn't appear like we can modify the event coming in, so we will fabricate a new event.
+ CGEventSourceRef src = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
+ CGEventRef ns_event = CGEventCreateKeyboardEvent(src, kVK_MEDIA_Previous, key_down);
+ CGEventSetFlags(ns_event, CGEventGetFlags(event_ref));
+
+ if (key_down) {
+ process_key_pressed(timestamp, ns_event);
+ }
+ else {
+ process_key_released(timestamp, ns_event);
+ }
+
+ CFRelease(ns_event);
+ CFRelease(src);
+ }
+ }
+
+ #ifndef USE_OBJC
+ free(buffer);
+ CFRelease(data);
+ #endif
+ }
+}
+
+
+static inline void process_button_pressed(uint64_t timestamp, CGEventRef event_ref, uint16_t button) {
+ // Track the number of clicks.
+ if (button == click_button && (long int) (timestamp - click_time) <= hook_get_multi_click_time()) {
+ if (click_count < USHRT_MAX) {
+ click_count++;
+ }
+ else {
+ logger(LOG_LEVEL_WARN, "%s [%u]: Click count overflow detected!\n",
+ __FUNCTION__, __LINE__);
+ }
+ }
+ else {
+ // Reset the click count.
+ click_count = 1;
+
+ // Set the previous button.
+ click_button = button;
+ }
+
+ // Save this events time to calculate the click_count.
+ click_time = timestamp;
+
+ CGPoint event_point = CGEventGetLocation(event_ref);
+
+ // Populate mouse pressed event.
+ event.time = timestamp;
+ event.reserved = 0x00;
+
+ event.type = EVENT_MOUSE_PRESSED;
+ event.mask = get_modifiers();
+
+ event.data.mouse.button = button;
+ event.data.mouse.clicks = click_count;
+ event.data.mouse.x = event_point.x;
+ event.data.mouse.y = event_point.y;
+
+ logger(LOG_LEVEL_INFO, "%s [%u]: Button %u pressed %u time(s). (%u, %u)\n",
+ __FUNCTION__, __LINE__, event.data.mouse.button, event.data.mouse.clicks,
+ event.data.mouse.x, event.data.mouse.y);
+
+ // Fire mouse pressed event.
+ dispatch_event(&event);
+}
+
+static inline void process_button_released(uint64_t timestamp, CGEventRef event_ref, uint16_t button) {
+ CGPoint event_point = CGEventGetLocation(event_ref);
+
+ // Populate mouse released event.
+ event.time = timestamp;
+ event.reserved = 0x00;
+
+ event.type = EVENT_MOUSE_RELEASED;
+ event.mask = get_modifiers();
+
+ event.data.mouse.button = button;
+ event.data.mouse.clicks = click_count;
+ event.data.mouse.x = event_point.x;
+ event.data.mouse.y = event_point.y;
+
+ logger(LOG_LEVEL_INFO, "%s [%u]: Button %u released %u time(s). (%u, %u)\n",
+ __FUNCTION__, __LINE__, event.data.mouse.button, event.data.mouse.clicks,
+ event.data.mouse.x, event.data.mouse.y);
+
+ // Fire mouse released event.
+ dispatch_event(&event);
+
+ // If the pressed event was not consumed...
+ if (event.reserved ^ 0x01 && mouse_dragged != true) {
+ // Populate mouse clicked event.
+ event.time = timestamp;
+ event.reserved = 0x00;
+
+ event.type = EVENT_MOUSE_CLICKED;
+ event.mask = get_modifiers();
+
+ event.data.mouse.button = button;
+ event.data.mouse.clicks = click_count;
+ event.data.mouse.x = event_point.x;
+ event.data.mouse.y = event_point.y;
+
+ logger(LOG_LEVEL_INFO, "%s [%u]: Button %u clicked %u time(s). (%u, %u)\n",
+ __FUNCTION__, __LINE__, event.data.mouse.button, event.data.mouse.clicks,
+ event.data.mouse.x, event.data.mouse.y);
+
+ // Fire mouse clicked event.
+ dispatch_event(&event);
+ }
+
+ // Reset the number of clicks.
+ if (button == click_button && (long int) (event.time - click_time) > hook_get_multi_click_time()) {
+ // Reset the click count.
+ click_count = 0;
+ }
+}
+
+static inline void process_mouse_moved(uint64_t timestamp, CGEventRef event_ref) {
+ // Reset the click count.
+ if (click_count != 0 && (long int) (timestamp - click_time) > hook_get_multi_click_time()) {
+ click_count = 0;
+ }
+
+ CGPoint event_point = CGEventGetLocation(event_ref);
+
+ // Populate mouse motion event.
+ event.time = timestamp;
+ event.reserved = 0x00;
+
+ if (mouse_dragged) {
+ event.type = EVENT_MOUSE_DRAGGED;
+ }
+ else {
+ event.type = EVENT_MOUSE_MOVED;
+ }
+ event.mask = get_modifiers();
+
+ event.data.mouse.button = MOUSE_NOBUTTON;
+ event.data.mouse.clicks = click_count;
+ event.data.mouse.x = event_point.x;
+ event.data.mouse.y = event_point.y;
+
+ logger(LOG_LEVEL_INFO, "%s [%u]: Mouse %s to %u, %u.\n",
+ __FUNCTION__, __LINE__, mouse_dragged ? "dragged" : "moved",
+ event.data.mouse.x, event.data.mouse.y);
+
+ // Fire mouse motion event.
+ dispatch_event(&event);
+}
+
+static inline void process_mouse_wheel(uint64_t timestamp, CGEventRef event_ref) {
+ // Reset the click count and previous button.
+ click_count = 1;
+ click_button = MOUSE_NOBUTTON;
+
+ // Check to see what axis was rotated, we only care about axis 1 for vertical rotation.
+ // TODO Implement horizontal scrolling by examining axis 2.
+ // NOTE kCGScrollWheelEventDeltaAxis3 is currently unused.
+ if (CGEventGetIntegerValueField(event_ref, kCGScrollWheelEventDeltaAxis1) != 0
+ || CGEventGetIntegerValueField(event_ref, kCGScrollWheelEventDeltaAxis2) != 0) {
+ CGPoint event_point = CGEventGetLocation(event_ref);
+
+ // Populate mouse wheel event.
+ event.time = timestamp;
+ event.reserved = 0x00;
+
+ event.type = EVENT_MOUSE_WHEEL;
+ event.mask = get_modifiers();
+
+ event.data.wheel.clicks = click_count;
+ event.data.wheel.x = event_point.x;
+ event.data.wheel.y = event_point.y;
+
+ // TODO Figure out if kCGScrollWheelEventDeltaAxis2 causes mouse events with zero rotation.
+ if (CGEventGetIntegerValueField(event_ref, kCGScrollWheelEventIsContinuous) == 0) {
+ // Scrolling data is line-based.
+ event.data.wheel.type = WHEEL_BLOCK_SCROLL;
+ }
+ else {
+ // Scrolling data is pixel-based.
+ event.data.wheel.type = WHEEL_UNIT_SCROLL;
+ }
+
+ // TODO The result of kCGScrollWheelEventIsContinuous may effect this value.
+ // Calculate the amount based on the Point Delta / Event Delta. Integer sign should always be homogeneous resulting in a positive result.
+ // NOTE kCGScrollWheelEventFixedPtDeltaAxis1 a floating point value (+0.1/-0.1) that takes acceleration into account.
+ // NOTE kCGScrollWheelEventPointDeltaAxis1 will not build on OS X < 10.5
+
+ if(CGEventGetIntegerValueField(event_ref, kCGScrollWheelEventDeltaAxis1) != 0) {
+ event.data.wheel.amount = CGEventGetIntegerValueField(event_ref, kCGScrollWheelEventPointDeltaAxis1) / CGEventGetIntegerValueField(event_ref, kCGScrollWheelEventDeltaAxis1);
+
+ // Scrolling data uses a fixed-point 16.16 signed integer format (Ex: 1.0 = 0x00010000).
+ event.data.wheel.rotation = CGEventGetIntegerValueField(event_ref, kCGScrollWheelEventDeltaAxis1) * -1;
+
+ }
+ else if(CGEventGetIntegerValueField(event_ref, kCGScrollWheelEventDeltaAxis2) != 0) {
+ event.data.wheel.amount = CGEventGetIntegerValueField(event_ref, kCGScrollWheelEventPointDeltaAxis2) / CGEventGetIntegerValueField(event_ref, kCGScrollWheelEventDeltaAxis2);
+
+ // Scrolling data uses a fixed-point 16.16 signed integer format (Ex: 1.0 = 0x00010000).
+ event.data.wheel.rotation = CGEventGetIntegerValueField(event_ref, kCGScrollWheelEventDeltaAxis2) * -1;
+ }
+ else {
+ //Fail Silently if a 3rd axis gets added without changing this section of code.
+ event.data.wheel.amount = 0;
+ event.data.wheel.rotation = 0;
+ }
+
+
+
+ if (CGEventGetIntegerValueField(event_ref, kCGScrollWheelEventDeltaAxis1) != 0) {
+ // Wheel Rotated Up or Down.
+ event.data.wheel.direction = WHEEL_VERTICAL_DIRECTION;
+ }
+ else { // data->event.u.u.detail == WheelLeft || data->event.u.u.detail == WheelRight
+ // Wheel Rotated Left or Right.
+ event.data.wheel.direction = WHEEL_HORIZONTAL_DIRECTION;
+ }
+
+ logger(LOG_LEVEL_INFO, "%s [%u]: Mouse wheel type %u, rotated %i units in the %u direction at %u, %u.\n",
+ __FUNCTION__, __LINE__, event.data.wheel.type,
+ event.data.wheel.amount * event.data.wheel.rotation,
+ event.data.wheel.direction,
+ event.data.wheel.x, event.data.wheel.y);
+
+ // Fire mouse wheel event.
+ dispatch_event(&event);
+ }
+}
+
+CGEventRef hook_event_proc(CGEventTapProxy tap_proxy, CGEventType type, CGEventRef event_ref, void *refcon) {
+ // Get the local system time in UTC.
+ gettimeofday(&system_time, NULL);
+
+ // Grab the native event timestap for use later..
+ uint64_t timestamp = (uint64_t) CGEventGetTimestamp(event_ref);
+
+ // Get the event class.
+ switch (type) {
+ case kCGEventKeyDown:
+ process_key_pressed(timestamp, event_ref);
+ break;
+
+ case kCGEventKeyUp:
+ process_key_released(timestamp, event_ref);
+ break;
+
+ case kCGEventFlagsChanged:
+ process_modifier_changed(timestamp, event_ref);
+ break;
+
+ //b
+ // case NX_SYSDEFINED:
+ // process_system_key(timestamp, event_ref);
+ // break;
+
+ case kCGEventLeftMouseDown:
+ set_modifier_mask(MASK_BUTTON1);
+ process_button_pressed(timestamp, event_ref, MOUSE_BUTTON1);
+ break;
+
+ case kCGEventRightMouseDown:
+ set_modifier_mask(MASK_BUTTON2);
+ process_button_pressed(timestamp, event_ref, MOUSE_BUTTON2);
+ break;
+
+ case kCGEventOtherMouseDown:
+ // Extra mouse buttons.
+ if (CGEventGetIntegerValueField(event_ref, kCGMouseEventButtonNumber) < UINT16_MAX) {
+ uint16_t button = (uint16_t) CGEventGetIntegerValueField(event_ref, kCGMouseEventButtonNumber) + 1;
+
+ // Add support for mouse 4 & 5.
+ if (button == 4) {
+ set_modifier_mask(MOUSE_BUTTON4);
+ }
+ else if (button == 5) {
+ set_modifier_mask(MOUSE_BUTTON5);
+ }
+
+ process_button_pressed(timestamp, event_ref, button);
+ }
+ break;
+
+ case kCGEventLeftMouseUp:
+ unset_modifier_mask(MASK_BUTTON1);
+ process_button_released(timestamp, event_ref, MOUSE_BUTTON1);
+ break;
+
+ case kCGEventRightMouseUp:
+ unset_modifier_mask(MASK_BUTTON2);
+ process_button_released(timestamp, event_ref, MOUSE_BUTTON2);
+ break;
+
+ case kCGEventOtherMouseUp:
+ // Extra mouse buttons.
+ if (CGEventGetIntegerValueField(event_ref, kCGMouseEventButtonNumber) < UINT16_MAX) {
+ uint16_t button = (uint16_t) CGEventGetIntegerValueField(event_ref, kCGMouseEventButtonNumber) + 1;
+
+ // Add support for mouse 4 & 5.
+ if (button == 4) {
+ unset_modifier_mask(MOUSE_BUTTON4);
+ }
+ else if (button == 5) {
+ unset_modifier_mask(MOUSE_BUTTON5);
+ }
+
+ process_button_pressed(timestamp, event_ref, button);
+ }
+ break;
+
+
+ case kCGEventLeftMouseDragged:
+ case kCGEventRightMouseDragged:
+ case kCGEventOtherMouseDragged:
+ // FIXME The drag flag is confusing. Use prev x,y to determine click.
+ // Set the mouse dragged flag.
+ mouse_dragged = true;
+ process_mouse_moved(timestamp, event_ref);
+ break;
+
+ case kCGEventMouseMoved:
+ // Set the mouse dragged flag.
+ mouse_dragged = false;
+ process_mouse_moved(timestamp, event_ref);
+ break;
+
+
+ case kCGEventScrollWheel:
+ process_mouse_wheel(timestamp, event_ref);
+ break;
+
+
+ #ifdef USE_DEBUG
+ case kCGEventNull:
+ logger(LOG_LEVEL_DEBUG, "%s [%u]: Ignoring kCGEventNull.\n",
+ __FUNCTION__, __LINE__);
+ break;
+ #endif
+
+ default:
+ // Check for an old OS X bug where the tap seems to timeout for no reason.
+ // See: http://stackoverflow.com/questions/2969110/cgeventtapcreate-breaks-down-mysteriously-with-key-down-events#2971217
+ if (type == (CGEventType) kCGEventTapDisabledByTimeout) {
+ logger(LOG_LEVEL_WARN, "%s [%u]: CGEventTap timeout!\n",
+ __FUNCTION__, __LINE__);
+
+ // We need to restart the tap!
+ restart_tap = true;
+ CFRunLoopStop(CFRunLoopGetCurrent());
+ }
+ else {
+ // In theory this *should* never execute.
+ logger(LOG_LEVEL_DEBUG, "%s [%u]: Unhandled Darwin event: %#X.\n",
+ __FUNCTION__, __LINE__, (unsigned int) type);
+ }
+ break;
+ }
+
+ CGEventRef result_ref = NULL;
+ if (event.reserved ^ 0x01) {
+ result_ref = event_ref;
+ }
+ else {
+ logger(LOG_LEVEL_DEBUG, "%s [%u]: Consuming the current event. (%#X) (%#p)\n",
+ __FUNCTION__, __LINE__, type, event_ref);
+ }
+
+ return result_ref;
+}
+
+IOHOOK_API int hook_run() {
+ int status = IOHOOK_SUCCESS;
+
+ do {
+ // Reset the restart flag...
+ restart_tap = false;
+
+ // Check for accessibility each time we start the loop.
+ if (is_accessibility_enabled()) {
+ logger(LOG_LEVEL_DEBUG, "%s [%u]: Accessibility API is enabled.\n",
+ __FUNCTION__, __LINE__);
+
+ // Initialize starting modifiers.
+ initialize_modifiers();
+
+ // Try and allocate memory for hook_info.
+ hook_info *hook = malloc(sizeof(hook_info));
+ if (hook != NULL) {
+ // Setup the event mask to listen for.
+ #ifdef USE_DEBUG
+ CGEventMask event_mask = kCGEventMaskForAllEvents;
+ #else
+ CGEventMask event_mask = CGEventMaskBit(kCGEventKeyDown) |
+ CGEventMaskBit(kCGEventKeyUp) |
+ CGEventMaskBit(kCGEventFlagsChanged) |
+
+ CGEventMaskBit(kCGEventLeftMouseDown) |
+ CGEventMaskBit(kCGEventLeftMouseUp) |
+ CGEventMaskBit(kCGEventLeftMouseDragged) |
+
+ CGEventMaskBit(kCGEventRightMouseDown) |
+ CGEventMaskBit(kCGEventRightMouseUp) |
+ CGEventMaskBit(kCGEventRightMouseDragged) |
+
+ CGEventMaskBit(kCGEventOtherMouseDown) |
+ CGEventMaskBit(kCGEventOtherMouseUp) |
+ CGEventMaskBit(kCGEventOtherMouseDragged) |
+
+ CGEventMaskBit(kCGEventMouseMoved) |
+ CGEventMaskBit(kCGEventScrollWheel) |
+
+ // NOTE This event is undocumented and used
+ // for caps-lock release and multi-media keys.
+ CGEventMaskBit(NX_SYSDEFINED);
+ #endif
+
+ // Create the event tap.
+ hook->port = CGEventTapCreate(
+ kCGSessionEventTap, // kCGHIDEventTap
+ kCGHeadInsertEventTap, // kCGTailAppendEventTap
+ kCGEventTapOptionDefault, // kCGEventTapOptionListenOnly See Bug #22
+ event_mask,
+ hook_event_proc,
+ NULL);
+
+ if (hook->port != NULL) {
+ logger(LOG_LEVEL_DEBUG, "%s [%u]: CGEventTapCreate Successful.\n",
+ __FUNCTION__, __LINE__);
+
+ // Create the runloop event source from the event tap.
+ hook->source = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, hook->port, 0);
+ if (hook->source != NULL) {
+ logger(LOG_LEVEL_DEBUG, "%s [%u]: CFMachPortCreateRunLoopSource successful.\n",
+ __FUNCTION__, __LINE__);
+
+ event_loop = CFRunLoopGetCurrent();
+ if (event_loop != NULL) {
+ logger(LOG_LEVEL_DEBUG, "%s [%u]: CFRunLoopGetCurrent successful.\n",
+ __FUNCTION__, __LINE__);
+
+ // Create run loop observers.
+ hook->observer = CFRunLoopObserverCreate(
+ kCFAllocatorDefault,
+ kCFRunLoopEntry | kCFRunLoopExit, //kCFRunLoopAllActivities,
+ true,
+ 0,
+ hook_status_proc,
+ NULL);
+
+ if (hook->observer != NULL) {
+ logger(LOG_LEVEL_DEBUG, "%s [%u]: CFRunLoopObserverCreate successful.\n",
+ __FUNCTION__, __LINE__);
+
+ tis_message = (TISMessage *) calloc(1, sizeof(TISMessage));
+ if (tis_message != NULL) {
+ if (! CFEqual(event_loop, CFRunLoopGetMain())) {
+ #ifdef USE_WEAK_IMPORT
+ if (dispatch_sync_f == NULL || dispatch_get_main_queue == NULL) {
+ #else
+ *(void **) (&dispatch_sync_f_f) = dlsym(RTLD_DEFAULT, "dispatch_sync_f");
+ const char *dlError = dlerror();
+ if (dlError != NULL) {
+ logger(LOG_LEVEL_DEBUG, "%s [%u]: %s.\n",
+ __FUNCTION__, __LINE__, dlError);
+ }
+
+ *(void **) (&dispatch_get_main_queue_f) = dlsym(RTLD_DEFAULT, "dispatch_get_main_queue");
+ dlError = dlerror();
+ if (dlError != NULL) {
+ logger(LOG_LEVEL_DEBUG, "%s [%u]: %s.\n",
+ __FUNCTION__, __LINE__, dlError);
+ }
+
+ if (dispatch_sync_f_f == NULL || dispatch_get_main_queue_f == NULL) {
+ #endif
+ logger(LOG_LEVEL_DEBUG, "%s [%u]: Failed to locate dispatch_sync_f() or dispatch_get_main_queue()!\n",
+ __FUNCTION__, __LINE__);
+
+ #if ! defined(USE_CARBON_LEGACY) && defined(USE_COREFOUNDATION)
+ logger(LOG_LEVEL_DEBUG, "%s [%u]: Falling back to runloop signaling.\n",
+ __FUNCTION__, __LINE__);
+
+ int runloop_status = start_message_port_runloop();
+ if (runloop_status != IOHOOK_SUCCESS) {
+ return runloop_status;
+ }
+ #endif
+ }
+ }
+
+ // Add the event source and observer to the runloop mode.
+ CFRunLoopAddSource(event_loop, hook->source, kCFRunLoopDefaultMode);
+ CFRunLoopAddObserver(event_loop, hook->observer, kCFRunLoopDefaultMode);
+
+ #ifdef USE_OBJC
+ // Create a garbage collector to handle Cocoa events correctly.
+ Class NSAutoreleasePool_class = (Class) objc_getClass("NSAutoreleasePool");
+ id pool = class_createInstance(NSAutoreleasePool_class, 0);
+ auto_release_pool = objc_msgSend(pool, sel_registerName("init"));
+ #endif
+
+ // Start the hook thread runloop.
+ CFRunLoopRun();
+
+
+ #ifdef USE_OBJC
+ //objc_msgSend(auto_release_pool, sel_registerName("drain"));
+ objc_msgSend(auto_release_pool, sel_registerName("release"));
+ #endif
+
+ // Lock back up until we are done processing the exit.
+ if (CFRunLoopContainsObserver(event_loop, hook->observer, kCFRunLoopDefaultMode)) {
+ CFRunLoopRemoveObserver(event_loop, hook->observer, kCFRunLoopDefaultMode);
+ }
+
+ if (CFRunLoopContainsSource(event_loop, hook->source, kCFRunLoopDefaultMode)) {
+ CFRunLoopRemoveSource(event_loop, hook->source, kCFRunLoopDefaultMode);
+ }
+
+ #if ! defined(USE_CARBON_LEGACY) && defined(USE_COREFOUNDATION)
+ if (! CFEqual(event_loop, CFRunLoopGetMain())) {
+ #ifdef USE_WEAK_IMPORT
+ if (dispatch_sync_f == NULL || dispatch_get_main_queue == NULL) {
+ #else
+ if (dispatch_sync_f_f == NULL || dispatch_get_main_queue_f == NULL) {
+ #endif
+ stop_message_port_runloop();
+ }
+ }
+ #endif
+
+ // Free the TIS Message.
+ free(tis_message);
+ }
+ else {
+ logger(LOG_LEVEL_ERROR, "%s [%u]: Failed to allocate memory for TIS message structure!\n",
+ __FUNCTION__, __LINE__);
+
+ // Set the exit status.
+ status = IOHOOK_ERROR_OUT_OF_MEMORY;
+ }
+
+ // Invalidate and free hook observer.
+ CFRunLoopObserverInvalidate(hook->observer);
+ CFRelease(hook->observer);
+ }
+ else {
+ // We cant do a whole lot of anything if we cant
+ // create run loop observer.
+ logger(LOG_LEVEL_ERROR, "%s [%u]: CFRunLoopObserverCreate failure!\n",
+ __FUNCTION__, __LINE__);
+
+ // Set the exit status.
+ status = IOHOOK_ERROR_CREATE_OBSERVER;
+ }
+ }
+ else {
+ logger(LOG_LEVEL_ERROR, "%s [%u]: CFRunLoopGetCurrent failure!\n",
+ __FUNCTION__, __LINE__);
+
+ // Set the exit status.
+ status = IOHOOK_ERROR_GET_RUNLOOP;
+ }
+
+ // Clean up the event source.
+ CFRelease(hook->source);
+ }
+ else {
+ logger(LOG_LEVEL_ERROR, "%s [%u]: CFMachPortCreateRunLoopSource failure!\n",
+ __FUNCTION__, __LINE__);
+
+ // Set the exit status.
+ status = IOHOOK_ERROR_CREATE_RUN_LOOP_SOURCE;
+ }
+
+ // Stop the CFMachPort from receiving any more messages.
+ CFMachPortInvalidate(hook->port);
+ CFRelease(hook->port);
+ }
+ else {
+ logger(LOG_LEVEL_ERROR, "%s [%u]: Failed to create event port!\n",
+ __FUNCTION__, __LINE__);
+
+ // Set the exit status.
+ status = IOHOOK_ERROR_CREATE_EVENT_PORT;
+ }
+
+ // Free the hook structure.
+ free(hook);
+ }
+ else {
+ status = IOHOOK_ERROR_OUT_OF_MEMORY;
+ }
+ }
+ else {
+ logger(LOG_LEVEL_ERROR, "%s [%u]: Accessibility API is disabled!\n",
+ __FUNCTION__, __LINE__);
+
+ // Set the exit status.
+ status = IOHOOK_ERROR_AXAPI_DISABLED;
+ }
+ } while (restart_tap);
+
+ logger(LOG_LEVEL_DEBUG, "%s [%u]: Something, something, something, complete.\n",
+ __FUNCTION__, __LINE__);
+
+ return status;
+}
+
+IOHOOK_API int hook_stop() {
+ int status = IOHOOK_FAILURE;
+
+ CFStringRef mode = CFRunLoopCopyCurrentMode(event_loop);
+ if (mode != NULL) {
+ CFRelease(mode);
+
+ // Make sure the tap doesn't restart.
+ restart_tap = false;
+
+ // Stop the run loop.
+ CFRunLoopStop(event_loop);
+
+ status = IOHOOK_SUCCESS;
+ }
+
+ logger(LOG_LEVEL_DEBUG, "%s [%u]: Status: %#X.\n",
+ __FUNCTION__, __LINE__, status);
+
+ return status;
+}
diff --git a/vendor/github.com/robotn/gohook/hook/darwin/input.h b/vendor/github.com/robotn/gohook/hook/darwin/input.h
new file mode 100644
index 0000000..a841070
--- /dev/null
+++ b/vendor/github.com/robotn/gohook/hook/darwin/input.h
@@ -0,0 +1,103 @@
+
+#ifndef _included_input_helper
+#define _included_input_helper
+
+#include
+#include // For HIToolbox kVK_ keycodes and TIS funcitons.
+#ifdef USE_IOKIT
+#include
+#endif
+#include
+
+
+#ifndef USE_IOKIT
+// Some of the system key codes that are needed from IOKit.
+#define NX_KEYTYPE_SOUND_UP 0x00
+#define NX_KEYTYPE_SOUND_DOWN 0x01
+#define NX_KEYTYPE_MUTE 0x07
+
+/* Display controls...
+#define NX_KEYTYPE_BRIGHTNESS_UP 0x02
+#define NX_KEYTYPE_BRIGHTNESS_DOWN 0x03
+#define NX_KEYTYPE_CONTRAST_UP 0x0B
+#define NX_KEYTYPE_CONTRAST_DOWN 0x0C
+#define NX_KEYTYPE_ILLUMINATION_UP 0x15
+#define NX_KEYTYPE_ILLUMINATION_DOWN 0x16
+#define NX_KEYTYPE_ILLUMINATION_TOGGLE 0x17
+*/
+
+#define NX_KEYTYPE_CAPS_LOCK 0x04
+//#define NX_KEYTYPE_HELP 0x05
+#define NX_POWER_KEY 0x06
+
+#define NX_KEYTYPE_EJECT 0x0E
+#define NX_KEYTYPE_PLAY 0x10
+#define NX_KEYTYPE_NEXT 0x12
+#define NX_KEYTYPE_PREVIOUS 0x13
+
+/* There is no official fast-forward or rewind scan code support.*/
+#define NX_KEYTYPE_FAST 0x14
+#define NX_KEYTYPE_REWIND 0x15
+
+#endif
+
+// These virtual key codes do not appear to be defined anywhere by Apple.
+#define kVK_NX_Power 0xE0 | NX_POWER_KEY /* 0xE6 */
+#define kVK_NX_Eject 0xE0 | NX_KEYTYPE_EJECT /* 0xEE */
+
+#define kVK_MEDIA_Play 0xE0 | NX_KEYTYPE_PLAY /* 0xF0 */
+#define kVK_MEDIA_Next 0xE0 | NX_KEYTYPE_NEXT /* 0xF1 */
+#define kVK_MEDIA_Previous 0xE0 | NX_KEYTYPE_PREVIOUS /* 0xF2 */
+
+#define kVK_RightCommand 0x36
+#define kVK_ContextMenu 0x6E // AKA kMenuPowerGlyph
+#define kVK_Undefined 0xFF
+
+// These button codes do not appear to be defined anywhere by Apple.
+#define kVK_LBUTTON kCGMouseButtonLeft
+#define kVK_RBUTTON kCGMouseButtonRight
+#define kVK_MBUTTON kCGMouseButtonCenter
+#define kVK_XBUTTON1 3
+#define kVK_XBUTTON2 4
+
+// These button masks do not appear to be defined anywhere by Apple.
+#define kCGEventFlagMaskButtonLeft 1 << 0
+#define kCGEventFlagMaskButtonRight 1 << 1
+#define kCGEventFlagMaskButtonCenter 1 << 2
+#define kCGEventFlagMaskXButton1 1 << 3
+#define kCGEventFlagMaskXButton2 1 << 4
+
+
+/* Check for access to Apples accessibility API.
+ */
+extern bool is_accessibility_enabled();
+
+/* Converts an OSX key code and event mask to the appropriate Unicode character
+ * representation.
+ */
+extern UniCharCount keycode_to_unicode(CGEventRef event_ref, UniChar *buffer, UniCharCount size);
+
+/* Converts an OSX keycode to the appropriate IOHook scancode constant.
+ */
+extern uint16_t keycode_to_scancode(UInt64 keycode);
+
+/* Converts a IOHook scancode constant to the appropriate OSX keycode.
+ */
+extern UInt64 scancode_to_keycode(uint16_t keycode);
+
+
+/* Initialize items required for KeyCodeToKeySym() and KeySymToUnicode()
+ * functionality. This method is called by OnLibraryLoad() and may need to be
+ * called in combination with UnloadInputHelper() if the native keyboard layout
+ * is changed.
+ */
+extern void load_input_helper();
+
+/* De-initialize items required for KeyCodeToKeySym() and KeySymToUnicode()
+ * functionality. This method is called by OnLibraryUnload() and may need to be
+ * called in combination with LoadInputHelper() if the native keyboard layout
+ * is changed.
+ */
+extern void unload_input_helper();
+
+#endif
diff --git a/vendor/github.com/robotn/gohook/hook/darwin/input_c.h b/vendor/github.com/robotn/gohook/hook/darwin/input_c.h
new file mode 100644
index 0000000..298f67f
--- /dev/null
+++ b/vendor/github.com/robotn/gohook/hook/darwin/input_c.h
@@ -0,0 +1,542 @@
+
+#ifdef HAVE_CONFIG_H
+#include
+#endif
+
+#ifdef USE_COREFOUNDATION
+ #include
+#endif
+#ifndef USE_WEAK_IMPORT
+ #include
+#endif
+
+#include
+#include "input.h"
+#include "../iohook.h"
+#include "../logger_c.h"
+
+// Current dead key state.
+#if defined(USE_CARBON_LEGACY) || defined(USE_COREFOUNDATION)
+static UInt32 deadkey_state;
+#endif
+
+// Input source data for the keyboard.
+#if defined(USE_CARBON_LEGACY)
+static KeyboardLayoutRef prev_keyboard_layout = NULL;
+#elif defined(USE_COREFOUNDATION)
+static TISInputSourceRef prev_keyboard_layout = NULL;
+#endif
+
+#ifdef USE_WEAK_IMPORT
+// Required to dynamically check for AXIsProcessTrustedWithOptions availability.
+extern Boolean AXIsProcessTrustedWithOptions(CFDictionaryRef options) __attribute__((weak_import));
+extern CFStringRef kAXTrustedCheckOptionPrompt __attribute__((weak_import));
+#else
+static Boolean (*AXIsProcessTrustedWithOptions_t)(CFDictionaryRef);
+#endif
+
+bool is_accessibility_enabled() {
+ bool is_enabled = false;
+
+ #ifdef USE_WEAK_IMPORT
+ // Check and make sure assistive devices is enabled.
+ if (AXIsProcessTrustedWithOptions != NULL) {
+ // New accessibility API 10.9 and later.
+ const void * keys[] = { kAXTrustedCheckOptionPrompt };
+ const void * values[] = { kCFBooleanTrue };
+
+ CFDictionaryRef options = CFDictionaryCreate(
+ kCFAllocatorDefault,
+ keys,
+ values,
+ sizeof(keys) / sizeof(*keys),
+ &kCFCopyStringDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+
+ is_enabled = AXIsProcessTrustedWithOptions(options);
+ }
+ #else
+ // Dynamically load the application services framework for examination.
+ *(void **) (&AXIsProcessTrustedWithOptions_t) = dlsym(RTLD_DEFAULT, "AXIsProcessTrustedWithOptions");
+ const char *dlError = dlerror();
+ if (AXIsProcessTrustedWithOptions_t != NULL && dlError == NULL) {
+ // Check for property CFStringRef kAXTrustedCheckOptionPrompt
+ void ** kAXTrustedCheckOptionPrompt_t = dlsym(RTLD_DEFAULT, "kAXTrustedCheckOptionPrompt");
+
+ dlError = dlerror();
+ if (kAXTrustedCheckOptionPrompt_t != NULL && dlError == NULL) {
+ // New accessibility API 10.9 and later.
+ const void * keys[] = { *kAXTrustedCheckOptionPrompt_t };
+ const void * values[] = { kCFBooleanTrue };
+
+ CFDictionaryRef options = CFDictionaryCreate(
+ kCFAllocatorDefault,
+ keys,
+ values,
+ sizeof(keys) / sizeof(*keys),
+ &kCFCopyStringDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+
+ is_enabled = (*AXIsProcessTrustedWithOptions_t)(options);
+ }
+ }
+ #endif
+ else {
+ #ifndef USE_WEAK_IMPORT
+ if (dlError != NULL) {
+ // Could not load the AXIsProcessTrustedWithOptions function!
+ logger(LOG_LEVEL_DEBUG, "%s [%u]: %s.\n",
+ __FUNCTION__, __LINE__, dlError);
+ }
+ #endif
+
+ logger(LOG_LEVEL_DEBUG, "%s [%u]: Weak import AXIsProcessTrustedWithOptions not found.\n",
+ __FUNCTION__, __LINE__, dlError);
+
+ logger(LOG_LEVEL_DEBUG, "%s [%u]: Falling back to AXAPIEnabled().\n",
+ __FUNCTION__, __LINE__, dlError);
+
+ // Old accessibility check 10.8 and older.
+ is_enabled = AXAPIEnabled();
+ }
+
+ return is_enabled;
+}
+
+
+UniCharCount keycode_to_unicode(CGEventRef event_ref, UniChar *buffer, UniCharCount size) {
+ UniCharCount count = 0;
+
+ #if defined(USE_CARBON_LEGACY)
+ KeyboardLayoutRef curr_keyboard_layout;
+ void *inputData = NULL;
+ if (KLGetCurrentKeyboardLayout(&curr_keyboard_layout) == noErr) {
+ if (KLGetKeyboardLayoutProperty(curr_keyboard_layout, kKLuchrData, (const void **) &inputData) != noErr) {
+ inputData = NULL;
+ }
+ }
+ #elif defined(USE_COREFOUNDATION)
+ CFDataRef inputData = NULL;
+ if (CFEqual(CFRunLoopGetCurrent(), CFRunLoopGetMain())) {
+ // NOTE The following block must execute on the main runloop,
+ // Ex: CFEqual(CFRunLoopGetCurrent(), CFRunLoopGetMain()) to avoid
+ // Exception detected while handling key input and TSMProcessRawKeyCode failed
+ // (-192) errors.
+ TISInputSourceRef curr_keyboard_layout = TISCopyCurrentKeyboardLayoutInputSource();
+ if (curr_keyboard_layout != NULL && CFGetTypeID(curr_keyboard_layout) == TISInputSourceGetTypeID()) {
+ CFDataRef data = (CFDataRef) TISGetInputSourceProperty(curr_keyboard_layout, kTISPropertyUnicodeKeyLayoutData);
+ if (data != NULL && CFGetTypeID(data) == CFDataGetTypeID() && CFDataGetLength(data) > 0) {
+ inputData = (CFDataRef) data;
+ }
+ }
+
+ // Check if the keyboard layout has changed to see if the dead key state needs to be discarded.
+ if (prev_keyboard_layout != NULL && curr_keyboard_layout != NULL && CFEqual(curr_keyboard_layout, prev_keyboard_layout) == false) {
+ deadkey_state = 0x00;
+ }
+
+ // Release the previous keyboard layout.
+ if (prev_keyboard_layout != NULL) {
+ CFRelease(prev_keyboard_layout);
+ prev_keyboard_layout = NULL;
+ }
+
+ // Set the previous keyboard layout to the current layout.
+ if (curr_keyboard_layout != NULL) {
+ prev_keyboard_layout = curr_keyboard_layout;
+ }
+ }
+ #endif
+
+ #if defined(USE_CARBON_LEGACY) || defined(USE_COREFOUNDATION)
+ if (inputData != NULL) {
+ #ifdef USE_CARBON_LEGACY
+ const UCKeyboardLayout *keyboard_layout = (const UCKeyboardLayout *) inputData;
+ #else
+ const UCKeyboardLayout *keyboard_layout = (const UCKeyboardLayout*) CFDataGetBytePtr(inputData);
+ #endif
+
+ if (keyboard_layout != NULL) {
+ //Extract keycode and modifier information.
+ CGKeyCode keycode = CGEventGetIntegerValueField(event_ref, kCGKeyboardEventKeycode);
+ CGEventFlags modifiers = CGEventGetFlags(event_ref);
+
+ // Disable all command modifiers for translation. This is required
+ // so UCKeyTranslate will provide a keysym for the separate event.
+ static const CGEventFlags cmd_modifiers = kCGEventFlagMaskCommand |
+ kCGEventFlagMaskControl | kCGEventFlagMaskAlternate;
+ modifiers &= ~cmd_modifiers;
+
+ // I don't know why but UCKeyTranslate does not process the
+ // kCGEventFlagMaskAlphaShift (A.K.A. Caps Lock Mask) correctly.
+ // We need to basically turn off the mask and process the capital
+ // letters after UCKeyTranslate().
+ bool is_caps_lock = modifiers & kCGEventFlagMaskAlphaShift;
+ modifiers &= ~kCGEventFlagMaskAlphaShift;
+
+ // Run the translation with the saved deadkey_state.
+ OSStatus status = UCKeyTranslate(
+ keyboard_layout,
+ keycode,
+ kUCKeyActionDown, //kUCKeyActionDisplay,
+ (modifiers >> 16) & 0xFF, //(modifiers >> 16) & 0xFF, || (modifiers >> 8) & 0xFF,
+ LMGetKbdType(),
+ kNilOptions, //kNilOptions, //kUCKeyTranslateNoDeadKeysMask
+ &deadkey_state,
+ size,
+ &count,
+ buffer);
+
+ if (status == noErr && count > 0) {
+ if (is_caps_lock) {
+ // We *had* a caps lock mask so we need to convert to uppercase.
+ CFMutableStringRef keytxt = CFStringCreateMutableWithExternalCharactersNoCopy(kCFAllocatorDefault, buffer, count, size, kCFAllocatorNull);
+ if (keytxt != NULL) {
+ CFLocaleRef locale = CFLocaleCopyCurrent();
+ CFStringUppercase(keytxt, locale);
+ CFRelease(locale);
+ CFRelease(keytxt);
+ }
+ else {
+ // There was an problem creating the CFMutableStringRef.
+ count = 0;
+ }
+ }
+ }
+ else {
+ // Make sure the buffer count is zero if an error occurred.
+ count = 0;
+ }
+ }
+ }
+ #else
+ CGEventKeyboardGetUnicodeString(event_ref, size, &count, buffer);
+ #endif
+
+ // The following codes should not be processed because they are invalid.
+ if (count == 1) {
+ switch (buffer[0]) {
+ case 0x01: // Home
+ case 0x04: // End
+ case 0x05: // Help Key
+ case 0x10: // Function Keys
+ case 0x0B: // Page Up
+ case 0x0C: // Page Down
+ case 0x1F: // Volume Up
+ count = 0;
+ }
+ }
+
+ return count;
+}
+
+
+static const uint16_t keycode_scancode_table[][2] = {
+ /* idx { keycode, scancode }, */
+ /* 0 */ { VC_A, kVK_Undefined }, // 0x00
+ /* 1 */ { VC_S, kVK_Escape }, // 0x01
+ /* 2 */ { VC_D, kVK_ANSI_1 }, // 0x02
+ /* 3 */ { VC_F, kVK_ANSI_2 }, // 0x03
+ /* 4 */ { VC_H, kVK_ANSI_3 }, // 0x04
+ /* 5 */ { VC_G, kVK_ANSI_4 }, // 0x05
+ /* 6 */ { VC_Z, kVK_ANSI_5 }, // 0x07
+ /* 7 */ { VC_X, kVK_ANSI_6 }, // 0x08
+ /* 8 */ { VC_C, kVK_ANSI_7 }, // 0x09
+ /* 9 */ { VC_V, kVK_ANSI_8 }, // 0x0A
+ /* 10 */ { VC_UNDEFINED, kVK_ANSI_9 }, // 0x0B
+ /* 11 */ { VC_B, kVK_ANSI_0 }, // 0x0C
+ /* 12 */ { VC_Q, kVK_ANSI_Minus }, // 0x0D
+ /* 13 */ { VC_W, kVK_ANSI_Equal }, // 0x0E
+ /* 14 */ { VC_E, kVK_Delete }, // 0x0F
+ /* 15 */ { VC_R, kVK_Tab }, // 0x10
+ /* 16 */ { VC_Y, kVK_ANSI_Q }, // 0x11
+ /* 17 */ { VC_T, kVK_ANSI_W }, // 0x12
+ /* 18 */ { VC_1, kVK_ANSI_E }, // 0x13
+ /* 19 */ { VC_2, kVK_ANSI_R }, // 0x14
+ /* 20 */ { VC_3, kVK_ANSI_T }, // 0x15
+ /* 21 */ { VC_4, kVK_ANSI_Y }, // 0x16
+ /* 22 */ { VC_6, kVK_ANSI_U }, // 0x17
+ /* 23 */ { VC_5, kVK_ANSI_I }, // 0x18
+ /* 24 */ { VC_EQUALS, kVK_ANSI_O }, // 0x19
+ /* 25 */ { VC_9, kVK_ANSI_P }, // 0x19
+ /* 26 */ { VC_7, kVK_ANSI_LeftBracket }, // 0x1A
+ /* 27 */ { VC_MINUS, kVK_ANSI_RightBracket }, // 0x1B
+ /* 28 */ { VC_8, kVK_Return }, // 0x1C
+ /* 29 */ { VC_0, kVK_Control }, // 0x1D
+ /* 30 */ { VC_CLOSE_BRACKET, kVK_ANSI_A }, // 0x1E
+ /* 31 */ { VC_O, kVK_ANSI_S }, // 0x1F
+ /* 32 */ { VC_U, kVK_ANSI_D }, // 0x20
+ /* 33 */ { VC_OPEN_BRACKET, kVK_ANSI_F }, // 0x21
+ /* 34 */ { VC_I, kVK_ANSI_G }, // 0x22
+ /* 35 */ { VC_P, kVK_ANSI_H }, // 0x23
+ /* 36 */ { VC_ENTER, kVK_ANSI_J }, // 0x24
+ /* 37 */ { VC_L, kVK_ANSI_K }, // 0x25
+ /* 38 */ { VC_J, kVK_ANSI_L }, // 0x26
+ /* 39 */ { VC_QUOTE, kVK_ANSI_Semicolon }, // 0x27
+ /* 40 */ { VC_K, kVK_ANSI_Quote }, // 0x28
+ /* 41 */ { VC_SEMICOLON, kVK_ANSI_Grave }, // 0x29
+ /* 42 */ { VC_BACK_SLASH, kVK_Shift }, // 0x2A
+ /* 43 */ { VC_COMMA, kVK_ANSI_Backslash }, // 0x2B
+ /* 44 */ { VC_SLASH, kVK_ANSI_Z }, // 0x2C
+ /* 45 */ { VC_N, kVK_ANSI_X }, // 0x2D
+ /* 46 */ { VC_M, kVK_ANSI_C }, // 0x2E
+ /* 47 */ { VC_PERIOD, kVK_ANSI_V }, // 0x2F
+ /* 48 */ { VC_TAB, kVK_ANSI_B }, // 0x30
+ /* 49 */ { VC_SPACE, kVK_ANSI_N }, // 0x31
+ /* 50 */ { VC_BACKQUOTE, kVK_ANSI_M }, // 0x32
+ /* 51 */ { VC_BACKSPACE, kVK_ANSI_Comma }, // 0x33
+ /* 52 */ { VC_UNDEFINED, kVK_ANSI_Period }, // 0x34
+ /* 53 */ { VC_ESCAPE, kVK_ANSI_Slash }, // 0x35
+ /* 54 */ { VC_META_R, kVK_RightShift }, // 0x36
+ /* 55 */ { VC_META_L, kVK_ANSI_KeypadMultiply }, // 0x37
+ /* 56 */ { VC_SHIFT_L, kVK_Option }, // 0x38
+ /* 57 */ { VC_CAPS_LOCK, kVK_Space }, // 0x39
+ /* 58 */ { VC_ALT_L, kVK_CapsLock }, // 0x3A
+ /* 59 */ { VC_CONTROL_L, kVK_F1 }, // 0x3B
+ /* 60 */ { VC_SHIFT_R, kVK_F2 }, // 0x3C
+ /* 61 */ { VC_ALT_R, kVK_F3 }, // 0x3D
+ /* 62 */ { VC_CONTROL_R, kVK_F4 }, // 0x3E
+ /* 63 */ { VC_UNDEFINED, kVK_F5 }, // 0x3F
+ /* 64 */ { VC_F17, kVK_F6 }, // 0x40
+ /* 65 */ { VC_KP_SEPARATOR, kVK_F7 }, // 0x41
+ /* 66 */ { VC_UNDEFINED, kVK_F8 }, // 0x42
+ /* 67 */ { VC_KP_MULTIPLY, kVK_F9 }, // 0x43
+ /* 68 */ { VC_UNDEFINED, kVK_F10 }, // 0x44
+ /* 69 */ { VC_KP_ADD, kVK_ANSI_KeypadClear }, // 0x45
+ /* 70 */ { VC_UNDEFINED, kVK_Undefined }, // 0x46
+ /* 71 */ { VC_NUM_LOCK, kVK_ANSI_Keypad7 }, // 0x47
+ /* 72 */ { VC_VOLUME_UP, kVK_ANSI_Keypad8 }, // 0x48
+ /* 73 */ { VC_VOLUME_DOWN, kVK_ANSI_Keypad9 }, // 0x49
+ /* 74 */ { VC_VOLUME_MUTE, kVK_ANSI_KeypadMinus }, // 0x4A
+ /* 75 */ { VC_KP_DIVIDE, kVK_ANSI_Keypad4 }, // 0x4B
+ /* 76 */ { VC_KP_ENTER, kVK_ANSI_Keypad5 }, // 0x4C
+ /* 77 */ { VC_UNDEFINED, kVK_ANSI_Keypad6 }, // 0x4D
+ /* 78 */ { VC_KP_SUBTRACT, kVK_ANSI_KeypadPlus }, // 0x4E
+ /* 79 */ { VC_F18, kVK_ANSI_Keypad1 }, // 0x4F
+ /* 80 */ { VC_F19, kVK_ANSI_Keypad2 }, // 0x50
+ /* 81 */ { VC_KP_EQUALS, kVK_ANSI_Keypad3 }, // 0x51
+ /* 82 */ { VC_KP_0, kVK_ANSI_Keypad0 }, // 0x52
+ /* 83 */ { VC_KP_1, kVK_ANSI_KeypadDecimal }, // 0x53
+ /* 84 */ { VC_KP_2, kVK_Undefined }, // 0x54
+ /* 85 */ { VC_KP_3, kVK_Undefined }, // 0x55
+ /* 86 */ { VC_KP_4, kVK_Undefined }, // 0x56
+ /* 87 */ { VC_KP_5, kVK_F11 }, // 0x57
+ /* 88 */ { VC_KP_6, kVK_F12 }, // 0x58
+ /* 89 */ { VC_KP_7, kVK_Undefined }, // 0x59
+ /* 90 */ { VC_F20, kVK_Undefined }, // 0x5A
+ /* 91 */ { VC_KP_8, kVK_F13 }, // 0x5B
+ /* 92 */ { VC_KP_9, kVK_F14 }, // 0x5C
+ /* 93 */ { VC_YEN, kVK_F15 }, // 0x5D
+ /* 94 */ { VC_UNDERSCORE, kVK_Undefined }, // 0x5E
+ /* 95 */ { VC_KP_COMMA, kVK_Undefined }, // 0x5F
+ /* 96 */ { VC_F5, kVK_Undefined }, // 0x60
+ /* 97 */ { VC_F6, kVK_Undefined }, // 0x61
+ /* 98 */ { VC_F7, kVK_Undefined }, // 0x62
+ /* 99 */ { VC_F3, kVK_F16 }, // 0x63
+ /* 100 */ { VC_F8, kVK_F17 }, // 0x64
+ /* 101 */ { VC_F9, kVK_F18 }, // 0x65
+ /* 102 */ { VC_UNDEFINED, kVK_F19 }, // 0x66
+ /* 103 */ { VC_F11, kVK_F20 }, // 0x67
+ /* 104 */ { VC_KATAKANA, kVK_Undefined }, // 0x68
+ /* 105 */ { VC_F13, kVK_Undefined }, // 0x69
+ /* 106 */ { VC_F16, kVK_Undefined }, // 0x6A
+ /* 107 */ { VC_F14, kVK_Undefined }, // 0x6B
+ /* 108 */ { VC_UNDEFINED, kVK_Undefined }, // 0x6C FIXME kVK_JIS_Eisu same as Caps Lock ?
+ /* 109 */ { VC_F10, kVK_Undefined }, // 0x6D
+ /* 110 */ { VC_UNDEFINED, kVK_Undefined }, // 0x6E
+ /* 111 */ { VC_F12, kVK_Undefined }, // 0x6F
+ /* 112 */ { VC_UNDEFINED, kVK_JIS_Kana }, // 0x70
+ /* 113 */ { VC_F15, kVK_Undefined }, // 0x71
+ /* 114 */ { VC_INSERT, kVK_Undefined }, // 0x72
+ /* 115 */ { VC_HOME, kVK_JIS_Underscore }, // 0x73
+ /* 116 */ { VC_PAGE_UP, kVK_Undefined }, // 0x74
+ /* 117 */ { VC_DELETE, kVK_Undefined }, // 0x75
+ /* 118 */ { VC_F4, kVK_Undefined }, // 0x76
+ /* 119 */ { VC_END, kVK_Undefined }, // 0x77
+ /* 120 */ { VC_F2, kVK_Undefined }, // 0x78
+ /* 121 */ { VC_PAGE_DOWN, kVK_Undefined }, // 0x79
+ /* 122 */ { VC_F1, kVK_Undefined }, // 0x7A
+ /* 123 */ { VC_LEFT, kVK_Undefined }, // 0x7B
+ /* 124 */ { VC_RIGHT, kVK_Undefined }, // 0x7C
+ /* 125 */ { VC_DOWN, kVK_JIS_Yen }, // 0x7D
+ /* 126 */ { VC_UP, kVK_JIS_KeypadComma }, // 0x7E
+ /* 127 */ { VC_UNDEFINED, kVK_Undefined }, // 0x7F
+
+ // No Offset Offset (i & 0x007F) + 128
+
+ /* 128 */ { VC_UNDEFINED, kVK_Undefined }, // 0x80
+ /* 129 */ { VC_UNDEFINED, kVK_Undefined }, // 0x81
+ /* 130 */ { VC_UNDEFINED, kVK_Undefined }, // 0x82
+ /* 131 */ { VC_UNDEFINED, kVK_Undefined }, // 0x83
+ /* 132 */ { VC_UNDEFINED, kVK_Undefined }, // 0x84
+ /* 133 */ { VC_UNDEFINED, kVK_Undefined }, // 0x85
+ /* 134 */ { VC_UNDEFINED, kVK_Undefined }, // 0x86
+ /* 135 */ { VC_UNDEFINED, kVK_Undefined }, // 0x87
+ /* 136 */ { VC_UNDEFINED, kVK_Undefined }, // 0x88
+ /* 137 */ { VC_UNDEFINED, kVK_Undefined }, // 0x89
+ /* 138 */ { VC_UNDEFINED, kVK_Undefined }, // 0x8A
+ /* 139 */ { VC_UNDEFINED, kVK_Undefined }, // 0x8B
+ /* 140 */ { VC_UNDEFINED, kVK_Undefined }, // 0x8C
+ /* 141 */ { VC_UNDEFINED, kVK_ANSI_KeypadEquals }, // 0x8D
+ /* 142 */ { VC_UNDEFINED, kVK_Undefined }, // 0x8E
+ /* 143 */ { VC_UNDEFINED, kVK_Undefined }, // 0x8F
+ /* 144 */ { VC_UNDEFINED, kVK_MEDIA_Previous }, // 0x90
+ /* 145 */ { VC_UNDEFINED, kVK_Undefined }, // 0x91
+ /* 146 */ { VC_UNDEFINED, kVK_Undefined }, // 0x92
+ /* 147 */ { VC_UNDEFINED, kVK_Undefined }, // 0x93
+ /* 148 */ { VC_UNDEFINED, kVK_Undefined }, // 0x94
+ /* 149 */ { VC_UNDEFINED, kVK_Undefined }, // 0x95
+ /* 150 */ { VC_UNDEFINED, kVK_Undefined }, // 0x96
+ /* 151 */ { VC_UNDEFINED, kVK_Undefined }, // 0x97
+ /* 152 */ { VC_UNDEFINED, kVK_Undefined }, // 0x98
+ /* 153 */ { VC_UNDEFINED, kVK_MEDIA_Next }, // 0x99
+ /* 154 */ { VC_UNDEFINED, kVK_Undefined }, // 0x9A
+ /* 155 */ { VC_UNDEFINED, kVK_Undefined }, // 0x9B
+ /* 156 */ { VC_UNDEFINED, kVK_ANSI_KeypadEnter }, // 0x9C
+ /* 157 */ { VC_UNDEFINED, kVK_RightControl }, // 0x9D
+ /* 158 */ { VC_UNDEFINED, kVK_Undefined }, // 0x9E
+ /* 159 */ { VC_UNDEFINED, kVK_Undefined }, // 0x9F
+ /* 160 */ { VC_UNDEFINED, kVK_Mute }, // 0xA0
+ /* 161 */ { VC_UNDEFINED, kVK_Undefined }, // 0xA1
+ /* 162 */ { VC_UNDEFINED, kVK_MEDIA_Play }, // 0xA2
+ /* 163 */ { VC_UNDEFINED, kVK_Undefined }, // 0xA3
+ /* 164 */ { VC_UNDEFINED, kVK_Undefined }, // 0xA4
+ /* 165 */ { VC_UNDEFINED, kVK_Undefined }, // 0xA5
+ /* 166 */ { VC_UNDEFINED, kVK_Undefined }, // 0xA6
+ /* 167 */ { VC_UNDEFINED, kVK_Undefined }, // 0xA7
+ /* 168 */ { VC_UNDEFINED, kVK_Undefined }, // 0xA8
+ /* 169 */ { VC_UNDEFINED, kVK_Undefined }, // 0xA9
+ /* 170 */ { VC_UNDEFINED, kVK_Undefined }, // 0xAA
+ /* 171 */ { VC_UNDEFINED, kVK_Undefined }, // 0xAB
+ /* 172 */ { VC_UNDEFINED, kVK_NX_Eject }, // 0xAC
+ /* 173 */ { VC_UNDEFINED, kVK_Undefined }, // 0xAD
+ /* 174 */ { VC_UNDEFINED, kVK_VolumeDown }, // 0xAE
+ /* 175 */ { VC_UNDEFINED, kVK_Undefined }, // 0xAF
+ /* 176 */ { VC_UNDEFINED, kVK_VolumeUp }, // 0xB0
+ /* 177 */ { VC_UNDEFINED, kVK_Undefined }, // 0xB1
+ /* 178 */ { VC_UNDEFINED, kVK_Undefined }, // 0xB2
+ /* 179 */ { VC_UNDEFINED, kVK_Undefined }, // 0xB3
+ /* 180 */ { VC_UNDEFINED, kVK_Undefined }, // 0xB4
+ /* 181 */ { VC_UNDEFINED, kVK_ANSI_KeypadDivide }, // 0xB5
+ /* 182 */ { VC_UNDEFINED, kVK_Undefined }, // 0xB6
+ /* 183 */ { VC_UNDEFINED, kVK_Undefined }, // 0xB7
+ /* 184 */ { VC_UNDEFINED, kVK_RightOption }, // 0xB8
+ /* 185 */ { VC_UNDEFINED, kVK_Undefined }, // 0xB9
+ /* 186 */ { VC_UNDEFINED, kVK_Undefined }, // 0xBA
+ /* 187 */ { VC_UNDEFINED, kVK_Undefined }, // 0xBB
+ /* 188 */ { VC_UNDEFINED, kVK_Undefined }, // 0xBC
+ /* 189 */ { VC_UNDEFINED, kVK_Undefined }, // 0xBD
+ /* 190 */ { VC_UNDEFINED, kVK_Undefined }, // 0xBE
+ /* 191 */ { VC_UNDEFINED, kVK_Undefined }, // 0xBF
+ /* 192 */ { VC_UNDEFINED, kVK_Undefined }, // 0xC0
+ /* 193 */ { VC_UNDEFINED, kVK_Undefined }, // 0xC1
+ /* 194 */ { VC_UNDEFINED, kVK_Undefined }, // 0xC2
+ /* 195 */ { VC_UNDEFINED, kVK_Undefined }, // 0xC3
+ /* 196 */ { VC_UNDEFINED, kVK_Undefined }, // 0xC4
+ /* 197 */ { VC_UNDEFINED, kVK_Undefined }, // 0xC5
+ /* 198 */ { VC_UNDEFINED, kVK_Undefined }, // 0xC6
+ /* 199 */ { VC_UNDEFINED, kVK_Home }, // 0xC7
+ /* 200 */ { VC_UNDEFINED, kVK_UpArrow }, // 0xC8
+ /* 201 */ { VC_UNDEFINED, kVK_PageUp }, // 0xC9
+ /* 202 */ { VC_UNDEFINED, kVK_Undefined }, // 0xCA
+ /* 203 */ { VC_UNDEFINED, kVK_LeftArrow }, // 0xCB
+ /* 204 */ { VC_UNDEFINED, kVK_Undefined }, // 0xCC
+ /* 205 */ { VC_UNDEFINED, kVK_RightArrow }, // 0xCD
+ /* 206 */ { VC_UNDEFINED, kVK_Undefined }, // 0xCE
+ /* 207 */ { VC_UNDEFINED, kVK_End }, // 0xCF
+ /* 208 */ { VC_UNDEFINED, kVK_DownArrow }, // 0xD0
+ /* 209 */ { VC_UNDEFINED, kVK_PageDown }, // 0xD1
+ /* 210 */ { VC_UNDEFINED, kVK_Help }, // 0xD2
+ /* 211 */ { VC_UNDEFINED, kVK_ForwardDelete }, // 0xD3
+ /* 212 */ { VC_UNDEFINED, kVK_Undefined }, // 0xD4
+ /* 213 */ { VC_UNDEFINED, kVK_Undefined }, // 0xD5
+ /* 214 */ { VC_UNDEFINED, kVK_Undefined }, // 0xD6
+ /* 215 */ { VC_UNDEFINED, kVK_Undefined }, // 0xD7
+ /* 216 */ { VC_UNDEFINED, kVK_Undefined }, // 0xD8
+ /* 217 */ { VC_UNDEFINED, kVK_Undefined }, // 0xD9
+ /* 218 */ { VC_UNDEFINED, kVK_Undefined }, // 0xDA
+ /* 219 */ { VC_UNDEFINED, kVK_Command }, // 0xDB
+ /* 220 */ { VC_UNDEFINED, kVK_RightCommand }, // 0xDC
+ /* 221 */ { VC_UNDEFINED, kVK_Undefined }, // 0xDD
+ /* 222 */ { VC_UNDEFINED, kVK_NX_Power }, // 0xDE
+ /* 223 */ { VC_UNDEFINED, kVK_Undefined }, // 0xDF
+ /* 224 */ { VC_UNDEFINED, kVK_Undefined }, // 0xE0
+ /* 225 */ { VC_UNDEFINED, kVK_Undefined }, // 0xE1
+ /* 226 */ { VC_UNDEFINED, kVK_Undefined }, // 0xE2
+ /* 227 */ { VC_UNDEFINED, kVK_Undefined }, // 0xE3
+ /* 228 */ { VC_UNDEFINED, kVK_Undefined }, // 0xE4
+ /* 229 */ { VC_UNDEFINED, kVK_Undefined }, // 0xE5
+ /* 230 */ { VC_POWER, kVK_Undefined }, // 0xE6
+ /* 231 */ { VC_UNDEFINED, kVK_Undefined }, // 0xE7
+ /* 232 */ { VC_UNDEFINED, kVK_Undefined }, // 0xE8
+ /* 233 */ { VC_UNDEFINED, kVK_Undefined }, // 0xE9
+ /* 234 */ { VC_UNDEFINED, kVK_Undefined }, // 0xEA
+ /* 235 */ { VC_UNDEFINED, kVK_Undefined }, // 0xEB
+ /* 236 */ { VC_UNDEFINED, kVK_Undefined }, // 0xEC
+ /* 237 */ { VC_UNDEFINED, kVK_Undefined }, // 0xED
+ /* 238 */ { VC_MEDIA_EJECT, kVK_Undefined }, // 0xEE
+ /* 239 */ { VC_UNDEFINED, kVK_Undefined }, // 0xEF
+ /* 240 */ { VC_MEDIA_PLAY, kVK_Undefined }, // 0xF0
+ /* 241 */ { VC_MEDIA_NEXT, kVK_Undefined }, // 0xF1
+ /* 242 */ { VC_MEDIA_PREVIOUS, kVK_Undefined }, // 0xF2
+ /* 243 */ { VC_UNDEFINED, kVK_Undefined }, // 0xF3
+ /* 244 */ { VC_UNDEFINED, kVK_Undefined }, // 0xF4
+ /* 245 */ { VC_UNDEFINED, kVK_Undefined }, // 0xF5
+ /* 246 */ { VC_UNDEFINED, kVK_Undefined }, // 0xF6
+ /* 247 */ { VC_UNDEFINED, kVK_Undefined }, // 0xF7
+ /* 248 */ { VC_UNDEFINED, kVK_Undefined }, // 0xF8
+ /* 249 */ { VC_UNDEFINED, kVK_Undefined }, // 0xF9
+ /* 250 */ { VC_UNDEFINED, kVK_Undefined }, // 0xFA
+ /* 251 */ { VC_UNDEFINED, kVK_Undefined }, // 0xFB
+ /* 252 */ { VC_UNDEFINED, kVK_Undefined }, // 0xFC
+ /* 253 */ { VC_UNDEFINED, kVK_Undefined }, // 0xFD
+ /* 254 */ { VC_UNDEFINED, kVK_Undefined }, // 0xFE
+ /* 255 */ { VC_UNDEFINED, kVK_Undefined }, // 0xFF
+};
+
+uint16_t keycode_to_scancode(UInt64 keycode) {
+ uint16_t scancode = VC_UNDEFINED;
+
+ // Bound check 0 <= keycode < 256
+ if (keycode < sizeof(keycode_scancode_table) / sizeof(keycode_scancode_table[0])) {
+ scancode = keycode_scancode_table[keycode][0];
+ }
+
+ return scancode;
+}
+
+UInt64 scancode_to_keycode(uint16_t scancode) {
+ UInt64 keycode = kVK_Undefined;
+
+ // Bound check 0 <= keycode < 128
+ if (scancode < 128) {
+ keycode = keycode_scancode_table[scancode][1];
+ }
+ else {
+ // Calculate the upper offset.
+ unsigned short i = (scancode & 0x007F) | 0x80;
+
+ if (i < sizeof(keycode_scancode_table) / sizeof(keycode_scancode_table[1])) {
+ keycode = keycode_scancode_table[i][1];
+ }
+ }
+
+ return keycode;
+}
+
+void load_input_helper() {
+ #if defined(USE_CARBON_LEGACY) || defined(USE_COREFOUNDATION)
+ // Start with a fresh dead key state.
+ //curr_deadkey_state = 0;
+ #endif
+}
+
+void unload_input_helper() {
+ #if defined(USE_CARBON_LEGACY) || defined(USE_COREFOUNDATION)
+ if (prev_keyboard_layout != NULL) {
+ // Cleanup tracking of the previous layout.
+ CFRelease(prev_keyboard_layout);
+ prev_keyboard_layout = NULL;
+ }
+ #endif
+}
diff --git a/vendor/github.com/robotn/gohook/hook/darwin/properties_c.h b/vendor/github.com/robotn/gohook/hook/darwin/properties_c.h
new file mode 100644
index 0000000..0a7679c
--- /dev/null
+++ b/vendor/github.com/robotn/gohook/hook/darwin/properties_c.h
@@ -0,0 +1,522 @@
+
+#ifdef HAVE_CONFIG_H
+ #include
+#endif
+
+#ifdef USE_CARBON_LEGACY
+ #include
+#endif
+#ifdef USE_COREFOUNDATION
+ #include
+#endif
+#ifdef USE_IOKIT
+ #include
+ #include
+#endif
+
+#include
+#include "../iohook.h"
+#include "input.h"
+// #include "../logger_c.h"
+
+IOHOOK_API screen_data* hook_create_screen_info(unsigned char *count) {
+ CGError status = kCGErrorFailure;
+ screen_data* screens = NULL;
+
+ // Initialize count to zero.
+ *count = 0;
+
+ // Allocate memory to hold each display id. We will just allocate our MAX
+ // because its only about 1K of memory.
+ // TODO This can probably be realistically cut to something like 16 or 32....
+ // If you have more than 32 monitors, send me a picture and make a donation ;)
+ CGDirectDisplayID *display_ids = malloc(sizeof(CGDirectDisplayID) * UCHAR_MAX);
+ if (display_ids != NULL) {
+ // NOTE Pass UCHAR_MAX to make sure uint32_t doesn't overflow uint8_t.
+ // TOOD Test/Check whether CGGetOnlineDisplayList is more suitable...
+ status = CGGetActiveDisplayList(UCHAR_MAX, display_ids, (uint32_t *) count);
+
+ // If there is no error and at least one monitor.
+ if (status == kCGErrorSuccess && *count > 0) {
+ logger(LOG_LEVEL_INFO, "%s [%u]: CGGetActiveDisplayList: %li.\n",
+ __FUNCTION__, __LINE__, *count);
+
+ // Allocate memory for the number of screens found.
+ screens = malloc(sizeof(screen_data) * (*count));
+ if (screens != NULL) {
+ uint8_t i;
+ for (i = 0; i < *count; i++) {
+ //size_t width = CGDisplayPixelsWide(display_ids[i]);
+ //size_t height = CGDisplayPixelsHigh(display_ids[i]);
+ CGRect boundsDisp = CGDisplayBounds(display_ids[i]);
+ if (boundsDisp.size.width > 0 && boundsDisp.size.height > 0) {
+ screens[i] = (screen_data) {
+ .number = i + 1,
+ //TODO: make sure we follow the same convention for the origin
+ //in all other platform implementations (upper-left)
+ //TODO: document the approach with examples in order to show different
+ //cases -> different resolutions (secondary monitors origin might be
+ //negative)
+ .x = boundsDisp.origin.x,
+ .y = boundsDisp.origin.y,
+ .width = boundsDisp.size.width,
+ .height = boundsDisp.size.height
+ };
+ }
+ }
+ }
+ }
+ else {
+ logger(LOG_LEVEL_INFO, "%s [%u]: multiple_get_screen_info failed: %ld. Fallback.\n",
+ __FUNCTION__, __LINE__, status);
+
+ size_t width = CGDisplayPixelsWide(CGMainDisplayID());
+ size_t height = CGDisplayPixelsHigh(CGMainDisplayID());
+
+ if (width > 0 && height > 0) {
+ screens = malloc(sizeof(screen_data));
+
+ if (screens != NULL) {
+ *count = 1;
+ screens[0] = (screen_data) {
+ .number = 1,
+ .x = 0,
+ .y = 0,
+ .width = width,
+ .height = height
+ };
+ }
+ }
+ }
+
+ // Free the id's after we are done.
+ free(display_ids);
+ }
+
+ return screens;
+}
+
+/*
+ * Apple's documentation is not very good. I was finally able to find this
+ * information after many hours of googling. Value is the slider value in the
+ * system preferences. That value * 15 is the rate in MS. 66 / the value is the
+ * chars per second rate.
+ *
+ * Value MS Char/Sec
+ *
+ * 1 15 66 * Out of standard range *
+ *
+ * 2 30 33
+ * 6 90 11
+ * 12 180 5.5
+ * 30 450 2.2
+ * 60 900 1.1
+ * 90 1350 0.73
+ * 120 1800 0.55
+ *
+ * V = MS / 15
+ * V = 66 / CharSec
+ *
+ * MS = V * 15
+ * MS = (66 / CharSec) * 15
+ *
+ * CharSec = 66 / V
+ * CharSec = 66 / (MS / 15)
+ */
+
+IOHOOK_API long int hook_get_auto_repeat_rate() {
+ #if defined USE_IOKIT || defined USE_COREFOUNDATION || defined USE_CARBON_LEGACY
+ bool successful = false;
+ SInt64 rate;
+ #endif
+
+ long int value = -1;
+
+ #ifdef USE_IOKIT
+ if (!successful) {
+ io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching(kIOHIDSystemClass));
+ if (service) {
+ kern_return_t kren_ret = kIOReturnError;
+ io_connect_t connection;
+
+ kren_ret = IOServiceOpen(service, mach_task_self(), kIOHIDParamConnectType, &connection);
+ if (kren_ret == kIOReturnSuccess) {
+ IOByteCount size = sizeof(rate);
+
+ kren_ret = IOHIDGetParameter(connection, CFSTR(kIOHIDKeyRepeatKey), (IOByteCount) sizeof(rate), &rate, &size);
+ if (kren_ret == kIOReturnSuccess) {
+ /* This is in some undefined unit of time that if we happen
+ * to multiply by 900 gives us the time in milliseconds. We
+ * add 0.5 to the result so that when we cast to long we
+ * actually get a rounded result. Saves the math.h depend.
+ *
+ * 33,333,333.0 / 1000.0 / 1000.0 / 1000.0 == 0.033333333 * Fast *
+ * 100,000,000.0 / 1000.0 / 1000.0 / 1000.0 == 0.1
+ * 200,000,000.0 / 1000.0 / 1000.0 / 1000.0 == 0.2
+ * 500,000,000.0 / 1000.0 / 1000.0 / 1000.0 == 0.5
+ * 1,000,000,000.0 / 1000.0 / 1000.0 / 1000.0 == 1
+ * 1,500,000,000.0 / 1000.0 / 1000.0 / 1000.0 == 1.5
+ * 2,000,000,000.0 / 1000.0 / 1000.0 / 1000.0 == 2 * Slow *
+ */
+ value = (long) (900.0 * ((double) rate) / 1000.0 / 1000.0 / 1000.0 + 0.5);
+ successful = true;
+
+ logger(LOG_LEVEL_INFO, "%s [%u]: IOHIDGetParameter: %li.\n",
+ __FUNCTION__, __LINE__, value);
+ }
+ }
+ }
+ }
+ #endif
+
+ #ifdef USE_COREFOUNDATION
+ if (!successful) {
+ CFTypeRef pref_val = CFPreferencesCopyValue(CFSTR("KeyRepeat"), kCFPreferencesAnyApplication, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
+ if (pref_val != NULL && CFGetTypeID(pref_val) == CFNumberGetTypeID()) {
+ if (CFNumberGetValue((CFNumberRef) pref_val, kCFNumberSInt32Type, &rate)) {
+ // This is the slider value, we must multiply by 15 to convert to milliseconds.
+ value = (long) rate * 15;
+ successful = true;
+
+ logger(LOG_LEVEL_INFO, "%s [%u]: CFPreferencesCopyValue: %li.\n",
+ __FUNCTION__, __LINE__, value);
+ }
+ }
+ }
+ #endif
+
+ #ifdef USE_CARBON_LEGACY
+ if (!successful) {
+ // Apple documentation states that value is in 'ticks'. I am not sure
+ // what that means, but it looks a lot like the arbitrary slider value.
+ rate = LMGetKeyRepThresh();
+ if (rate > -1) {
+ /* This is the slider value, we must multiply by 15 to convert to
+ * milliseconds.
+ */
+ value = (long) rate * 15;
+ successful = true;
+
+ logger(LOG_LEVEL_INFO, "%s [%u]: LMGetKeyRepThresh: %li.\n",
+ __FUNCTION__, __LINE__, value);
+ }
+ }
+ #endif
+
+ return value;
+}
+
+IOHOOK_API long int hook_get_auto_repeat_delay() {
+ #if defined USE_IOKIT || defined USE_COREFOUNDATION || defined USE_CARBON_LEGACY
+ bool successful = false;
+ SInt64 delay;
+ #endif
+
+ long int value = -1;
+
+ #ifdef USE_IOKIT
+ if (!successful) {
+ io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching(kIOHIDSystemClass));
+ if (service) {
+ kern_return_t kren_ret = kIOReturnError;
+ io_connect_t connection;
+
+ kren_ret = IOServiceOpen(service, mach_task_self(), kIOHIDParamConnectType, &connection);
+ if (kren_ret == kIOReturnSuccess) {
+ IOByteCount size = sizeof(delay);
+
+ kren_ret = IOHIDGetParameter(connection, CFSTR(kIOHIDInitialKeyRepeatKey), (IOByteCount) sizeof(delay), &delay, &size);
+ if (kren_ret == kIOReturnSuccess) {
+ /* This is in some undefined unit of time that if we happen
+ * to multiply by 900 gives us the time in milliseconds. We
+ * add 0.5 to the result so that when we cast to long we
+ * actually get a rounded result. Saves the math.h depend.
+ *
+ * 33,333,333.0 / 1000.0 / 1000.0 / 1000.0 == 0.033333333 * Fast *
+ * 100,000,000.0 / 1000.0 / 1000.0 / 1000.0 == 0.1
+ * 200,000,000.0 / 1000.0 / 1000.0 / 1000.0 == 0.2
+ * 500,000,000.0 / 1000.0 / 1000.0 / 1000.0 == 0.5
+ * 1,000,000,000.0 / 1000.0 / 1000.0 / 1000.0 == 1
+ * 1,500,000,000.0 / 1000.0 / 1000.0 / 1000.0 == 1.5
+ * 2,000,000,000.0 / 1000.0 / 1000.0 / 1000.0 == 2 * Slow *
+ */
+ value = (long) (900.0 * ((double) delay) / 1000.0 / 1000.0 / 1000.0 + 0.5);
+ successful = true;
+
+ logger(LOG_LEVEL_INFO, "%s [%u]: IOHIDGetParameter: %li.\n",
+ __FUNCTION__, __LINE__, value);
+ }
+ }
+ }
+ }
+ #endif
+
+ #ifdef USE_COREFOUNDATION
+ if (!successful) {
+ CFTypeRef pref_val = CFPreferencesCopyValue(CFSTR("InitialKeyRepeat"), kCFPreferencesAnyApplication, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
+ if (pref_val != NULL && CFGetTypeID(pref_val) == CFNumberGetTypeID()) {
+ if (CFNumberGetValue((CFNumberRef) pref_val, kCFNumberSInt32Type, &delay)) {
+ // This is the slider value, we must multiply by 15 to convert to
+ // milliseconds.
+ value = (long) delay * 15;
+ successful = true;
+
+ logger(LOG_LEVEL_INFO, "%s [%u]: CFPreferencesCopyValue: %li.\n",
+ __FUNCTION__, __LINE__, value);
+ }
+ }
+ }
+ #endif
+
+ #ifdef USE_CARBON_LEGACY
+ if (!successful) {
+ // Apple documentation states that value is in 'ticks'. I am not sure
+ // what that means, but it looks a lot like the arbitrary slider value.
+ delay = LMGetKeyThresh();
+ if (delay > -1) {
+ // This is the slider value, we must multiply by 15 to convert to
+ // milliseconds.
+ value = (long) delay * 15;
+ successful = true;
+
+ logger(LOG_LEVEL_INFO, "%s [%u]: LMGetKeyThresh: %li.\n",
+ __FUNCTION__, __LINE__, value);
+ }
+ }
+ #endif
+
+ return value;
+}
+
+IOHOOK_API long int hook_get_pointer_acceleration_multiplier() {
+ #if defined USE_IOKIT || defined USE_COREFOUNDATION
+ bool successful = false;
+ double multiplier;
+ #endif
+
+ long int value = -1;
+
+ #ifdef USE_IOKIT
+ if (!successful) {
+ io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching(kIOHIDSystemClass));
+
+ if (service) {
+ kern_return_t kren_ret = kIOReturnError;
+ io_connect_t connection;
+
+ kren_ret = IOServiceOpen(service, mach_task_self(), kIOHIDParamConnectType, &connection);
+ if (kren_ret == kIOReturnSuccess) {
+ // IOByteCount size = sizeof(multiplier);
+
+ kren_ret = IOHIDGetAccelerationWithKey(connection, CFSTR(kIOHIDMouseAccelerationType), &multiplier);
+ if (kren_ret == kIOReturnSuccess) {
+ // Calculate the greatest common factor.
+
+ unsigned long denominator = 1000000, d = denominator;
+ unsigned long numerator = multiplier * denominator, gcf = numerator;
+
+ while (d != 0) {
+ unsigned long i = gcf % d;
+ gcf = d;
+ d = i;
+ }
+
+ value = denominator / gcf;
+ successful = true;
+
+ logger(LOG_LEVEL_INFO, "%s [%u]: IOHIDGetAccelerationWithKey: %li.\n",
+ __FUNCTION__, __LINE__, value);
+ }
+ }
+ }
+ }
+ #endif
+
+ #ifdef USE_COREFOUNDATION
+ if (!successful) {
+ CFTypeRef pref_val = CFPreferencesCopyValue(CFSTR("com.apple.mouse.scaling"), kCFPreferencesAnyApplication, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
+ if (pref_val != NULL && CFGetTypeID(pref_val) == CFNumberGetTypeID()) {
+ if (CFNumberGetValue((CFNumberRef) pref_val, kCFNumberSInt32Type, &multiplier)) {
+ value = (long) multiplier;
+
+ logger(LOG_LEVEL_INFO, "%s [%u]: CFPreferencesCopyValue: %li.\n",
+ __FUNCTION__, __LINE__, value);
+ }
+ }
+ }
+ #endif
+
+ return value;
+}
+
+IOHOOK_API long int hook_get_pointer_acceleration_threshold() {
+ #if defined USE_COREFOUNDATION
+ bool successful = false;
+ SInt32 threshold;
+ #endif
+
+ long int value = -1;
+
+ #ifdef USE_COREFOUNDATION
+ if (!successful) {
+ CFTypeRef pref_val = CFPreferencesCopyValue(CFSTR("mouseDriverMaxSpeed"), CFSTR("com.apple.universalaccess"), kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
+ if (pref_val != NULL && CFGetTypeID(pref_val) == CFNumberGetTypeID()) {
+ if (CFNumberGetValue((CFNumberRef) pref_val, kCFNumberSInt32Type, &threshold)) {
+ value = (long) threshold;
+
+ logger(LOG_LEVEL_INFO, "%s [%u]: CFPreferencesCopyValue: %li.\n",
+ __FUNCTION__, __LINE__, value);
+ }
+ }
+ }
+ #endif
+
+ return value;
+}
+
+IOHOOK_API long int hook_get_pointer_sensitivity() {
+ #ifdef USE_IOKIT
+ bool successful = false;
+ double sensitivity;
+ #endif
+
+ long int value = -1;
+
+ #ifdef USE_IOKIT
+ if (!successful) {
+ io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching(kIOHIDSystemClass));
+
+ if (service) {
+ kern_return_t kren_ret = kIOReturnError;
+ io_connect_t connection;
+
+ kren_ret = IOServiceOpen(service, mach_task_self(), kIOHIDParamConnectType, &connection);
+ if (kren_ret == kIOReturnSuccess) {
+ // IOByteCount size = sizeof(multiplier);
+
+ kren_ret = IOHIDGetAccelerationWithKey(connection, CFSTR(kIOHIDMouseAccelerationType), &sensitivity);
+ if (kren_ret == kIOReturnSuccess) {
+ // Calculate the greatest common factor.
+
+ unsigned long denominator = 1000000, d = denominator;
+ unsigned long numerator = sensitivity * denominator, gcf = numerator;
+
+ while (d != 0) {
+ unsigned long i = gcf % d;
+ gcf = d;
+ d = i;
+ }
+
+ value = numerator / gcf;
+ successful = true;
+
+ logger(LOG_LEVEL_INFO, "%s [%u]: IOHIDGetAccelerationWithKey: %li.\n",
+ __FUNCTION__, __LINE__, value);
+ }
+ }
+ }
+ }
+ #endif
+
+ return value;
+}
+
+IOHOOK_API long int hook_get_multi_click_time() {
+ #if defined USE_IOKIT || defined USE_COREFOUNDATION || defined USE_CARBON_LEGACY
+ bool successful = false;
+ #if defined USE_IOKIT || defined USE_CARBON_LEGACY
+ // This needs to be defined only if we have USE_IOKIT or USE_CARBON_LEGACY.
+ SInt64 time;
+ #endif
+ #endif
+
+ long int value = -1;
+
+ #ifdef USE_IOKIT
+ if (!successful) {
+ io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching(kIOHIDSystemClass));
+ if (service) {
+ kern_return_t kren_ret = kIOReturnError;
+ io_connect_t connection;
+
+ kren_ret = IOServiceOpen(service, mach_task_self(), kIOHIDParamConnectType, &connection);
+ if (kren_ret == kIOReturnSuccess) {
+ IOByteCount size = sizeof(time);
+
+ kren_ret = IOHIDGetParameter(connection, CFSTR(kIOHIDClickTimeKey), (IOByteCount) sizeof(time), &time, &size);
+ if (kren_ret == kIOReturnSuccess) {
+ /* This is in some undefined unit of time that if we happen
+ * to multiply by 900 gives us the time in milliseconds. We
+ * add 0.5 to the result so that when we cast to long we
+ * actually get a rounded result. Saves the math.h depend.
+ */
+ value = (long) (900.0 * ((double) time) / 1000.0 / 1000.0 / 1000.0 + 0.5);
+ successful = true;
+
+ logger(LOG_LEVEL_INFO, "%s [%u]: IOHIDGetParameter: %li.\n",
+ __FUNCTION__, __LINE__, value);
+ }
+ }
+ }
+ }
+ #endif
+
+ #ifdef USE_COREFOUNDATION
+ if (!successful) {
+ Float32 clicktime;
+ CFTypeRef pref_val = CFPreferencesCopyValue(CFSTR("com.apple.mouse.doubleClickThreshold"), kCFPreferencesAnyApplication, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
+ if (pref_val != NULL && CFGetTypeID(pref_val) == CFNumberGetTypeID()) {
+ if (CFNumberGetValue((CFNumberRef) pref_val, kCFNumberFloat32Type, &clicktime)) {
+ /* This is in some undefined unit of time that if we happen
+ * to multiply by 900 gives us the time in milliseconds. It is
+ * completely possible that this value is in seconds and should be
+ * multiplied by 1000 but because IOKit values are undocumented and
+ * I have no idea what a Carbon 'tick' is so there really is no way
+ * to confirm this.
+ */
+ value = (long) (clicktime * 900);
+
+ logger(LOG_LEVEL_INFO, "%s [%u]: CFPreferencesCopyValue: %li.\n",
+ __FUNCTION__, __LINE__, value);
+ }
+ }
+ }
+ #endif
+
+ #ifdef USE_CARBON_LEGACY
+ if (!successful) {
+ // Apple documentation states that value is in 'ticks'. I am not sure
+ // what that means, but it looks a lot like the arbitrary slider value.
+ time = GetDblTime();
+ if (time > -1) {
+ // This is the slider value, we must multiply by 15 to convert to
+ // milliseconds.
+ value = (long) time * 15;
+ successful = true;
+
+ logger(LOG_LEVEL_INFO, "%s [%u]: GetDblTime: %li.\n",
+ __FUNCTION__, __LINE__, value);
+ }
+ }
+ #endif
+
+ return value;
+}
+
+
+// Create a shared object constructor.
+__attribute__ ((constructor))
+void on_library_load() {
+ // Initialize Native Input Functions.
+ load_input_helper();
+}
+
+// Create a shared object destructor.
+__attribute__ ((destructor))
+void on_library_unload() {
+ // Disable the event hook.
+ //hook_stop();
+
+ // Cleanup native input functions.
+ unload_input_helper();
+}
diff --git a/vendor/github.com/robotn/gohook/hook/iohook.h b/vendor/github.com/robotn/gohook/hook/iohook.h
new file mode 100644
index 0000000..db0ba8b
--- /dev/null
+++ b/vendor/github.com/robotn/gohook/hook/iohook.h
@@ -0,0 +1,441 @@
+
+#ifndef __IOHOOK_H
+#define __IOHOOK_H
+
+// #include "../../base/os.h"
+#include
+#include
+#include
+
+/* Begin Error Codes */
+#define IOHOOK_SUCCESS 0x00
+#define IOHOOK_FAILURE 0x01
+
+// System level errors.
+#define IOHOOK_ERROR_OUT_OF_MEMORY 0x02
+
+// Unix specific errors.
+#define IOHOOK_ERROR_X_OPEN_DISPLAY 0x20
+#define IOHOOK_ERROR_X_RECORD_NOT_FOUND 0x21
+#define IOHOOK_ERROR_X_RECORD_ALLOC_RANGE 0x22
+#define IOHOOK_ERROR_X_RECORD_CREATE_CONTEXT 0x23
+#define IOHOOK_ERROR_X_RECORD_ENABLE_CONTEXT 0x24
+#define IOHOOK_ERROR_X_RECORD_GET_CONTEXT 0x25
+
+// Windows specific errors.
+#define IOHOOK_ERROR_SET_WINDOWS_HOOK_EX 0x30
+#define IOHOOK_ERROR_GET_MODULE_HANDLE 0x31
+
+// Darwin specific errors.
+#define IOHOOK_ERROR_AXAPI_DISABLED 0x40
+#define IOHOOK_ERROR_CREATE_EVENT_PORT 0x41
+#define IOHOOK_ERROR_CREATE_RUN_LOOP_SOURCE 0x42
+#define IOHOOK_ERROR_GET_RUNLOOP 0x43
+#define IOHOOK_ERROR_CREATE_OBSERVER 0x44
+/* End Error Codes */
+
+/* Begin Log Levels and Function Prototype */
+typedef enum _log_level {
+ LOG_LEVEL_DEBUG = 1,
+ LOG_LEVEL_INFO,
+ LOG_LEVEL_WARN,
+ LOG_LEVEL_ERROR
+} log_level;
+
+// Logger callback function prototype.
+typedef bool (*logger_t)(unsigned int, const char *, ...);
+/* End Log Levels and Function Prototype */
+
+/* Begin Virtual Event Types and Data Structures */
+typedef enum _event_type {
+ EVENT_HOOK_ENABLED = 1,
+ EVENT_HOOK_DISABLED,
+ EVENT_KEY_TYPED,
+ EVENT_KEY_PRESSED,
+ EVENT_KEY_RELEASED,
+ EVENT_MOUSE_CLICKED,
+ EVENT_MOUSE_PRESSED,
+ EVENT_MOUSE_RELEASED,
+ EVENT_MOUSE_MOVED,
+ EVENT_MOUSE_DRAGGED,
+ EVENT_MOUSE_WHEEL
+} event_type;
+
+typedef struct _screen_data {
+ uint8_t number;
+ int16_t x;
+ int16_t y;
+ uint16_t width;
+ uint16_t height;
+} screen_data;
+
+typedef struct _keyboard_event_data {
+ uint16_t keycode;
+ uint16_t rawcode;
+ uint16_t keychar;
+ // char *keychar;
+} keyboard_event_data,
+ key_pressed_event_data,
+ key_released_event_data,
+ key_typed_event_data;
+
+typedef struct _mouse_event_data {
+ uint16_t button;
+ uint16_t clicks;
+ int16_t x;
+ int16_t y;
+} mouse_event_data,
+ mouse_pressed_event_data,
+ mouse_released_event_data,
+ mouse_clicked_event_data;
+
+typedef struct _mouse_wheel_event_data {
+ uint16_t clicks;
+ int16_t x;
+ int16_t y;
+ uint8_t type;
+ uint16_t amount;
+ int16_t rotation;
+ uint8_t direction;
+} mouse_wheel_event_data;
+
+typedef struct _iohook_event {
+ event_type type;
+ uint64_t time;
+ uint16_t mask;
+ uint16_t reserved;
+ union {
+ keyboard_event_data keyboard;
+ mouse_event_data mouse;
+ mouse_wheel_event_data wheel;
+ } data;
+} iohook_event;
+
+typedef void (*dispatcher_t)(iohook_event *const);
+/* End Virtual Event Types and Data Structures */
+
+
+/* Begin Virtual Key Codes */
+#define VC_ESCAPE 0x0001
+
+// Begin Function Keys
+#define VC_F1 0x003B
+#define VC_F2 0x003C
+#define VC_F3 0x003D
+#define VC_F4 0x003E
+#define VC_F5 0x003F
+#define VC_F6 0x0040
+#define VC_F7 0x0041
+#define VC_F8 0x0042
+#define VC_F9 0x0043
+#define VC_F10 0x0044
+#define VC_F11 0x0057
+#define VC_F12 0x0058
+
+#define VC_F13 0x005B
+#define VC_F14 0x005C
+#define VC_F15 0x005D
+#define VC_F16 0x0063
+#define VC_F17 0x0064
+#define VC_F18 0x0065
+#define VC_F19 0x0066
+#define VC_F20 0x0067
+#define VC_F21 0x0068
+#define VC_F22 0x0069
+#define VC_F23 0x006A
+#define VC_F24 0x006B
+// End Function Keys
+
+
+// Begin Alphanumeric Zone
+#define VC_BACKQUOTE 0x0029
+
+#define VC_1 0x0002
+#define VC_2 0x0003
+#define VC_3 0x0004
+#define VC_4 0x0005
+#define VC_5 0x0006
+#define VC_6 0x0007
+#define VC_7 0x0008
+#define VC_8 0x0009
+#define VC_9 0x000A
+#define VC_0 0x000B
+
+#define VC_MINUS 0x000C // '-'
+#define VC_EQUALS 0x000D // '='
+#define VC_BACKSPACE 0x000E
+
+#define VC_TAB 0x000F
+#define VC_CAPS_LOCK 0x003A
+
+#define VC_A 0x001E
+#define VC_B 0x0030
+#define VC_C 0x002E
+#define VC_D 0x0020
+#define VC_E 0x0012
+#define VC_F 0x0021
+#define VC_G 0x0022
+#define VC_H 0x0023
+#define VC_I 0x0017
+#define VC_J 0x0024
+#define VC_K 0x0025
+#define VC_L 0x0026
+#define VC_M 0x0032
+#define VC_N 0x0031
+#define VC_O 0x0018
+#define VC_P 0x0019
+#define VC_Q 0x0010
+#define VC_R 0x0013
+#define VC_S 0x001F
+#define VC_T 0x0014
+#define VC_U 0x0016
+#define VC_V 0x002F
+#define VC_W 0x0011
+#define VC_X 0x002D
+#define VC_Y 0x0015
+#define VC_Z 0x002C
+
+#define VC_OPEN_BRACKET 0x001A // '['
+#define VC_CLOSE_BRACKET 0x001B // ']'
+#define VC_BACK_SLASH 0x002B // '\'
+
+#define VC_SEMICOLON 0x0027 // ';'
+#define VC_QUOTE 0x0028
+#define VC_ENTER 0x001C
+
+#define VC_COMMA 0x0033 // ','
+#define VC_PERIOD 0x0034 // '.'
+#define VC_SLASH 0x0035 // '/'
+
+#define VC_SPACE 0x0039
+// End Alphanumeric Zone
+
+
+#define VC_PRINTSCREEN 0x0E37
+#define VC_SCROLL_LOCK 0x0046
+#define VC_PAUSE 0x0E45
+
+
+// Begin Edit Key Zone
+#define VC_INSERT 0x0E52
+#define VC_DELETE 0x0E53
+#define VC_HOME 0x0E47
+#define VC_END 0x0E4F
+#define VC_PAGE_UP 0x0E49
+#define VC_PAGE_DOWN 0x0E51
+// End Edit Key Zone
+
+
+// Begin Cursor Key Zone
+#define VC_UP 0xE048
+#define VC_LEFT 0xE04B
+#define VC_CLEAR 0xE04C
+#define VC_RIGHT 0xE04D
+#define VC_DOWN 0xE050
+// End Cursor Key Zone
+
+
+// Begin Numeric Zone
+#define VC_NUM_LOCK 0x0045
+#define VC_KP_DIVIDE 0x0E35
+#define VC_KP_MULTIPLY 0x0037
+#define VC_KP_SUBTRACT 0x004A
+#define VC_KP_EQUALS 0x0E0D
+#define VC_KP_ADD 0x004E
+#define VC_KP_ENTER 0x0E1C
+#define VC_KP_SEPARATOR 0x0053
+
+#define VC_KP_1 0x004F
+#define VC_KP_2 0x0050
+#define VC_KP_3 0x0051
+#define VC_KP_4 0x004B
+#define VC_KP_5 0x004C
+#define VC_KP_6 0x004D
+#define VC_KP_7 0x0047
+#define VC_KP_8 0x0048
+#define VC_KP_9 0x0049
+#define VC_KP_0 0x0052
+
+#define VC_KP_END 0xEE00 | VC_KP_1
+#define VC_KP_DOWN 0xEE00 | VC_KP_2
+#define VC_KP_PAGE_DOWN 0xEE00 | VC_KP_3
+#define VC_KP_LEFT 0xEE00 | VC_KP_4
+#define VC_KP_CLEAR 0xEE00 | VC_KP_5
+#define VC_KP_RIGHT 0xEE00 | VC_KP_6
+#define VC_KP_HOME 0xEE00 | VC_KP_7
+#define VC_KP_UP 0xEE00 | VC_KP_8
+#define VC_KP_PAGE_UP 0xEE00 | VC_KP_9
+#define VC_KP_INSERT 0xEE00 | VC_KP_0
+#define VC_KP_DELETE 0xEE00 | VC_KP_SEPARATOR
+// End Numeric Zone
+
+
+// Begin Modifier and Control Keys
+#define VC_SHIFT_L 0x002A
+#define VC_SHIFT_R 0x0036
+#define VC_CONTROL_L 0x001D
+#define VC_CONTROL_R 0x0E1D
+#define VC_ALT_L 0x0038 // Option or Alt Key
+#define VC_ALT_R 0x0E38 // Option or Alt Key
+#define VC_META_L 0x0E5B // Windows or Command Key
+#define VC_META_R 0x0E5C // Windows or Command Key
+#define VC_CONTEXT_MENU 0x0E5D
+// End Modifier and Control Keys
+
+
+// Begin Media Control Keys
+#define VC_POWER 0xE05E
+#define VC_SLEEP 0xE05F
+#define VC_WAKE 0xE063
+
+#define VC_MEDIA_PLAY 0xE022
+#define VC_MEDIA_STOP 0xE024
+#define VC_MEDIA_PREVIOUS 0xE010
+#define VC_MEDIA_NEXT 0xE019
+#define VC_MEDIA_SELECT 0xE06D
+#define VC_MEDIA_EJECT 0xE02C
+
+#define VC_VOLUME_MUTE 0xE020
+#define VC_VOLUME_UP 0xE030
+#define VC_VOLUME_DOWN 0xE02E
+
+#define VC_APP_MAIL 0xE06C
+#define VC_APP_CALCULATOR 0xE021
+#define VC_APP_MUSIC 0xE03C
+#define VC_APP_PICTURES 0xE064
+
+#define VC_BROWSER_SEARCH 0xE065
+#define VC_BROWSER_HOME 0xE032
+#define VC_BROWSER_BACK 0xE06A
+#define VC_BROWSER_FORWARD 0xE069
+#define VC_BROWSER_STOP 0xE068
+#define VC_BROWSER_REFRESH 0xE067
+#define VC_BROWSER_FAVORITES 0xE066
+// End Media Control Keys
+
+// Begin Japanese Language Keys
+#define VC_KATAKANA 0x0070
+#define VC_UNDERSCORE 0x0073
+#define VC_FURIGANA 0x0077
+#define VC_KANJI 0x0079
+#define VC_HIRAGANA 0x007B
+#define VC_YEN 0x007D
+#define VC_KP_COMMA 0x007E
+// End Japanese Language Keys
+
+// Begin Sun keyboards
+#define VC_SUN_HELP 0xFF75
+
+#define VC_SUN_STOP 0xFF78
+#define VC_SUN_PROPS 0xFF76
+#define VC_SUN_FRONT 0xFF77
+#define VC_SUN_OPEN 0xFF74
+#define VC_SUN_FIND 0xFF7E
+#define VC_SUN_AGAIN 0xFF79
+#define VC_SUN_UNDO 0xFF7A
+#define VC_SUN_COPY 0xFF7C
+#define VC_SUN_INSERT 0xFF7D
+#define VC_SUN_CUT 0xFF7B
+// End Sun keyboards
+
+#define VC_UNDEFINED 0x0000 // KeyCode Unknown
+
+#define CHAR_UNDEFINED 0xFFFF // CharCode Unknown
+/* End Virtual Key Codes */
+
+
+/* Begin Virtual Modifier Masks */
+#define MASK_SHIFT_L 1 << 0
+#define MASK_CTRL_L 1 << 1
+#define MASK_META_L 1 << 2
+#define MASK_ALT_L 1 << 3
+
+#define MASK_SHIFT_R 1 << 4
+#define MASK_CTRL_R 1 << 5
+#define MASK_META_R 1 << 6
+#define MASK_ALT_R 1 << 7
+
+#define MASK_SHIFT MASK_SHIFT_L | MASK_SHIFT_R
+#define MASK_CTRL MASK_CTRL_L | MASK_CTRL_R
+#define MASK_META MASK_META_L | MASK_META_R
+#define MASK_ALT MASK_ALT_L | MASK_ALT_R
+
+#define MASK_BUTTON1 1 << 8
+#define MASK_BUTTON2 1 << 9
+#define MASK_BUTTON3 1 << 10
+#define MASK_BUTTON4 1 << 11
+#define MASK_BUTTON5 1 << 12
+
+#define MASK_NUM_LOCK 1 << 13
+#define MASK_CAPS_LOCK 1 << 14
+#define MASK_SCROLL_LOCK 1 << 15
+/* End Virtual Modifier Masks */
+
+
+/* Begin Virtual Mouse Buttons */
+#define MOUSE_NOBUTTON 0 // Any Button
+#define MOUSE_BUTTON1 1 // Left Button
+#define MOUSE_BUTTON2 2 // Right Button
+#define MOUSE_BUTTON3 3 // Middle Button
+#define MOUSE_BUTTON4 4 // Extra Mouse Button
+#define MOUSE_BUTTON5 5 // Extra Mouse Button
+
+#define WHEEL_UNIT_SCROLL 1
+#define WHEEL_BLOCK_SCROLL 2
+
+#define WHEEL_VERTICAL_DIRECTION 3
+#define WHEEL_HORIZONTAL_DIRECTION 4
+/* End Virtual Mouse Buttons */
+
+
+#ifdef _WIN32
+#define IOHOOK_API __declspec(dllexport)
+#else
+#define IOHOOK_API
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ // Set the logger callback functions.
+ IOHOOK_API void hook_set_logger_proc(logger_t logger_proc);
+
+ // Send a virtual event back to the system.
+ IOHOOK_API void hook_post_event(iohook_event * const event);
+
+ // Set the event callback function.
+ IOHOOK_API void hook_set_dispatch_proc(dispatcher_t dispatch_proc);
+
+ // Insert the event hook.
+ IOHOOK_API int hook_run();
+
+ // Withdraw the event hook.
+ IOHOOK_API int hook_stop();
+
+ // Retrieves an array of screen data for each available monitor.
+ IOHOOK_API screen_data* hook_create_screen_info(unsigned char *count);
+
+ // Retrieves the keyboard auto repeat rate.
+ IOHOOK_API long int hook_get_auto_repeat_rate();
+
+ // Retrieves the keyboard auto repeat delay.
+ IOHOOK_API long int hook_get_auto_repeat_delay();
+
+ // Retrieves the mouse acceleration multiplier.
+ IOHOOK_API long int hook_get_pointer_acceleration_multiplier();
+
+ // Retrieves the mouse acceleration threshold.
+ IOHOOK_API long int hook_get_pointer_acceleration_threshold();
+
+ // Retrieves the mouse sensitivity.
+ IOHOOK_API long int hook_get_pointer_sensitivity();
+
+ // Retrieves the double/triple click interval.
+ IOHOOK_API long int hook_get_multi_click_time();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/vendor/github.com/robotn/gohook/hook/logger.h b/vendor/github.com/robotn/gohook/hook/logger.h
new file mode 100644
index 0000000..0314e0f
--- /dev/null
+++ b/vendor/github.com/robotn/gohook/hook/logger.h
@@ -0,0 +1,15 @@
+
+#ifndef _included_logger
+#define _included_logger
+
+#include "iohook.h"
+#include
+
+#ifndef __FUNCTION__
+#define __FUNCTION__ __func__
+#endif
+
+// logger(level, message)
+extern logger_t logger;
+
+#endif
diff --git a/vendor/github.com/robotn/gohook/hook/logger_c.h b/vendor/github.com/robotn/gohook/hook/logger_c.h
new file mode 100644
index 0000000..8752245
--- /dev/null
+++ b/vendor/github.com/robotn/gohook/hook/logger_c.h
@@ -0,0 +1,53 @@
+
+#ifdef HAVE_CONFIG_H
+ #include
+#endif
+
+#include
+#include
+#include
+#include
+
+#include "iohook.h"
+#include "logger.h"
+
+static bool default_logger(unsigned int level, const char *format, ...) {
+ bool status = false;
+
+ #ifndef USE_QUIET
+ va_list args;
+ switch (level) {
+ #ifdef USE_DEBUG
+ case LOG_LEVEL_DEBUG:
+ #endif
+ case LOG_LEVEL_INFO:
+ va_start(args, format);
+ status = vfprintf(stdout, format, args) >= 0;
+ va_end(args);
+ break;
+
+ case LOG_LEVEL_WARN:
+ case LOG_LEVEL_ERROR:
+ va_start(args, format);
+ status = vfprintf(stderr, format, args) >= 0;
+ va_end(args);
+ break;
+ }
+ #endif
+
+ return status;
+}
+
+// Current logger function pointer, this should never be null.
+// FIXME This should be static and wrapped with a public facing function.
+logger_t logger = &default_logger;
+
+
+IOHOOK_API void hookSetlogger(logger_t logger_proc) {
+ if (logger_proc == NULL) {
+ logger = &default_logger;
+ }
+ else {
+ logger = logger_proc;
+ }
+}
diff --git a/vendor/github.com/robotn/gohook/hook/windows/event_c.h b/vendor/github.com/robotn/gohook/hook/windows/event_c.h
new file mode 100644
index 0000000..2eb51f6
--- /dev/null
+++ b/vendor/github.com/robotn/gohook/hook/windows/event_c.h
@@ -0,0 +1,328 @@
+
+#ifdef HAVE_CONFIG_H
+#include
+#endif
+
+#include
+#include "../iohook.h"
+#include
+
+// #include "logger.h"
+#include "input.h"
+
+// Some buggy versions of MinGW and MSys do not include these constants in winuser.h.
+#ifndef MAPVK_VK_TO_VSC
+#define MAPVK_VK_TO_VSC 0
+#define MAPVK_VSC_TO_VK 1
+#define MAPVK_VK_TO_CHAR 2
+#define MAPVK_VSC_TO_VK_EX 3
+#endif
+// Some buggy versions of MinGW and MSys only define this value for Windows
+// versions >= 0x0600 (Windows Vista) when it should be 0x0500 (Windows 2000).
+#ifndef MAPVK_VK_TO_VSC_EX
+#define MAPVK_VK_TO_VSC_EX 4
+#endif
+
+#ifndef KEYEVENTF_SCANCODE
+#define KEYEVENTF_EXTENDEDKEY 0x0001
+#define KEYEVENTF_KEYUP 0x0002
+#define KEYEVENTF_UNICODE 0x0004
+#define KEYEVENTF_SCANCODE 0x0008
+#endif
+
+#ifndef KEYEVENTF_KEYDOWN
+#define KEYEVENTF_KEYDOWN 0x0000
+#endif
+
+#define MAX_WINDOWS_COORD_VALUE 65535
+
+static UINT keymask_lookup[8] = {
+ VK_LSHIFT,
+ VK_LCONTROL,
+ VK_LWIN,
+ VK_LMENU,
+
+ VK_RSHIFT,
+ VK_RCONTROL,
+ VK_RWIN,
+ VK_RMENU
+};
+
+IOHOOK_API void hook_post_event(iohook_event * const event) {
+ //FIXME implement multiple monitor support
+ uint16_t screen_width = GetSystemMetrics( SM_CXSCREEN );
+ uint16_t screen_height = GetSystemMetrics( SM_CYSCREEN );
+
+ unsigned char events_size = 0, events_max = 28;
+ INPUT *events = malloc(sizeof(INPUT) * events_max);
+
+ if (event->mask & (MASK_SHIFT | MASK_CTRL | MASK_META | MASK_ALT)) {
+ unsigned int i;
+ for (i = 0; i < sizeof(keymask_lookup) / sizeof(UINT); i++) {
+ if (event->mask & 1 << i) {
+ events[events_size].type = INPUT_KEYBOARD;
+ events[events_size].ki.wVk = keymask_lookup[i];
+ events[events_size].ki.dwFlags = KEYEVENTF_KEYDOWN;
+ events[events_size].ki.time = 0; // Use current system time.
+ events_size++;
+ }
+ }
+ }
+
+ if (event->mask & (MASK_BUTTON1 | MASK_BUTTON2 | MASK_BUTTON3 | MASK_BUTTON4 | MASK_BUTTON5)) {
+ events[events_size].type = INPUT_MOUSE;
+ events[events_size].mi.dx = 0; // Relative mouse movement due to
+ events[events_size].mi.dy = 0; // MOUSEEVENTF_ABSOLUTE not being set.
+ events[events_size].mi.mouseData = 0x00;
+ events[events_size].mi.time = 0; // Use current system time.
+
+ if (event->mask & MASK_BUTTON1) {
+ events[events_size].mi.mouseData |= MOUSEEVENTF_LEFTDOWN;
+ }
+
+ if (event->mask & MASK_BUTTON2) {
+ events[events_size].mi.mouseData |= MOUSEEVENTF_RIGHTDOWN;
+ }
+
+ if (event->mask & MASK_BUTTON3) {
+ events[events_size].mi.mouseData |= MOUSEEVENTF_MIDDLEDOWN;
+ }
+
+ if (event->mask & MASK_BUTTON4) {
+ events[events_size].mi.mouseData = XBUTTON1;
+ events[events_size].mi.mouseData |= MOUSEEVENTF_XDOWN;
+ }
+
+ if (event->mask & MASK_BUTTON5) {
+ events[events_size].mi.mouseData = XBUTTON2;
+ events[events_size].mi.dwFlags |= MOUSEEVENTF_XDOWN;
+ }
+
+ events_size++;
+ }
+
+
+ switch (event->type) {
+ case EVENT_KEY_PRESSED:
+ events[events_size].ki.wVk = scancode_to_keycode(event->data.keyboard.keycode);
+ if (events[events_size].ki.wVk != 0x0000) {
+ events[events_size].type = INPUT_KEYBOARD;
+ events[events_size].ki.dwFlags = KEYEVENTF_KEYDOWN; // |= KEYEVENTF_SCANCODE;
+ events[events_size].ki.wScan = 0; // event->data.keyboard.keycode;
+ events[events_size].ki.time = 0; // GetSystemTime()
+ events_size++;
+ }
+ else {
+ logger(LOG_LEVEL_INFO, "%s [%u]: Unable to lookup scancode: %li\n",
+ __FUNCTION__, __LINE__,
+ event->data.keyboard.keycode);
+ }
+ break;
+
+ case EVENT_KEY_RELEASED:
+ events[events_size].ki.wVk = scancode_to_keycode(event->data.keyboard.keycode);
+ if (events[events_size].ki.wVk != 0x0000) {
+ events[events_size].type = INPUT_KEYBOARD;
+ events[events_size].ki.dwFlags = KEYEVENTF_KEYUP; // |= KEYEVENTF_SCANCODE;
+ events[events_size].ki.wVk = scancode_to_keycode(event->data.keyboard.keycode);
+ events[events_size].ki.wScan = 0; // event->data.keyboard.keycode;
+ events[events_size].ki.time = 0; // GetSystemTime()
+ events_size++;
+ }
+ else {
+ logger(LOG_LEVEL_INFO, "%s [%u]: Unable to lookup scancode: %li\n",
+ __FUNCTION__, __LINE__,
+ event->data.keyboard.keycode);
+ }
+ break;
+
+
+ case EVENT_MOUSE_PRESSED:
+ events[events_size].type = INPUT_MOUSE;
+ events[events_size].mi.dwFlags = MOUSEEVENTF_XDOWN;
+
+ switch (event->data.mouse.button) {
+ case MOUSE_BUTTON1:
+ events[events_size].mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
+ break;
+
+ case MOUSE_BUTTON2:
+ events[events_size].mi.dwFlags = MOUSEEVENTF_RIGHTDOWN;
+ break;
+
+ case MOUSE_BUTTON3:
+ events[events_size].mi.dwFlags = MOUSEEVENTF_MIDDLEDOWN;
+ break;
+
+ case MOUSE_BUTTON4:
+ events[events_size].mi.mouseData = XBUTTON1;
+ break;
+
+ case MOUSE_BUTTON5:
+ events[events_size].mi.mouseData = XBUTTON2;
+ break;
+
+ default:
+ // Extra buttons.
+ if (event->data.mouse.button > 3) {
+ events[events_size].mi.mouseData = event->data.mouse.button - 3;
+ }
+ }
+
+ events[events_size].mi.dx = event->data.mouse.x * (MAX_WINDOWS_COORD_VALUE / screen_width) + 1;
+ events[events_size].mi.dy = event->data.mouse.y * (MAX_WINDOWS_COORD_VALUE / screen_height) + 1;
+
+ events[events_size].mi.dwFlags |= MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE;
+ events[events_size].mi.time = 0; // GetSystemTime()
+
+ events_size++;
+ break;
+
+ case EVENT_MOUSE_RELEASED:
+ events[events_size].type = INPUT_MOUSE;
+ events[events_size].mi.dwFlags = MOUSEEVENTF_XUP;
+
+ switch (event->data.mouse.button) {
+ case MOUSE_BUTTON1:
+ events[events_size].mi.dwFlags = MOUSEEVENTF_LEFTUP;
+ break;
+
+ case MOUSE_BUTTON2:
+ events[events_size].mi.dwFlags = MOUSEEVENTF_RIGHTUP;
+ break;
+
+ case MOUSE_BUTTON3:
+ events[events_size].mi.dwFlags = MOUSEEVENTF_MIDDLEUP;
+ break;
+
+ case MOUSE_BUTTON4:
+ events[events_size].mi.mouseData = XBUTTON1;
+ break;
+
+ case MOUSE_BUTTON5:
+ events[events_size].mi.mouseData = XBUTTON2;
+ break;
+
+ default:
+ // Extra buttons.
+ if (event->data.mouse.button > 3) {
+ events[events_size].mi.mouseData = event->data.mouse.button - 3;
+ }
+ }
+
+ events[events_size].mi.dx = event->data.mouse.x * (MAX_WINDOWS_COORD_VALUE / screen_width) + 1;
+ events[events_size].mi.dy = event->data.mouse.y * (MAX_WINDOWS_COORD_VALUE / screen_height) + 1;
+
+ events[events_size].mi.dwFlags |= MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE;
+ events[events_size].mi.time = 0; // GetSystemTime()
+ events_size++;
+ break;
+
+
+ case EVENT_MOUSE_WHEEL:
+ events[events_size].type = INPUT_MOUSE;
+ events[events_size].mi.dwFlags = MOUSEEVENTF_WHEEL;
+
+ // type, amount and rotation?
+ events[events_size].mi.mouseData = event->data.wheel.amount * event->data.wheel.rotation * WHEEL_DELTA;
+
+ events[events_size].mi.dx = event->data.wheel.x * (MAX_WINDOWS_COORD_VALUE / screen_width) + 1;
+ events[events_size].mi.dy = event->data.wheel.y * (MAX_WINDOWS_COORD_VALUE / screen_height) + 1;
+
+ events[events_size].mi.dwFlags |= MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE;
+ events[events_size].mi.time = 0; // GetSystemTime()
+ events_size++;
+ break;
+
+
+ case EVENT_MOUSE_DRAGGED:
+ // The button masks are all applied with the modifier masks.
+
+ case EVENT_MOUSE_MOVED:
+ events[events_size].type = INPUT_MOUSE;
+ events[events_size].mi.dwFlags = MOUSEEVENTF_MOVE;
+
+ events[events_size].mi.dx = event->data.mouse.x * (MAX_WINDOWS_COORD_VALUE / screen_width) + 1;
+ events[events_size].mi.dy = event->data.mouse.y * (MAX_WINDOWS_COORD_VALUE / screen_height) + 1;
+
+ events[events_size].mi.dwFlags |= MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE;
+ events[events_size].mi.time = 0; // GetSystemTime()
+ events_size++;
+ break;
+
+
+ case EVENT_MOUSE_CLICKED:
+ case EVENT_KEY_TYPED:
+ // Ignore clicked and typed events.
+
+ case EVENT_HOOK_ENABLED:
+ case EVENT_HOOK_DISABLED:
+ // Ignore hook enabled / disabled events.
+
+ default:
+ // Ignore any other garbage.
+ logger(LOG_LEVEL_WARN, "%s [%u]: Ignoring post event type %#X\n",
+ __FUNCTION__, __LINE__, event->type);
+ break;
+ }
+
+ // Release the previously held modifier keys used to fake the event mask.
+ if (event->mask & (MASK_SHIFT | MASK_CTRL | MASK_META | MASK_ALT)) {
+ unsigned int i;
+ for (i = 0; i < sizeof(keymask_lookup) / sizeof(UINT); i++) {
+ if (event->mask & 1 << i) {
+ events[events_size].type = INPUT_KEYBOARD;
+ events[events_size].ki.wVk = keymask_lookup[i];
+ events[events_size].ki.dwFlags = KEYEVENTF_KEYUP;
+ events[events_size].ki.time = 0; // Use current system time.
+ events_size++;
+ }
+ }
+ }
+
+ if (event->mask & (MASK_BUTTON1 | MASK_BUTTON2 | MASK_BUTTON3 | MASK_BUTTON4 | MASK_BUTTON5)) {
+ events[events_size].type = INPUT_MOUSE;
+ events[events_size].mi.dx = 0; // Relative mouse movement due to
+ events[events_size].mi.dy = 0; // MOUSEEVENTF_ABSOLUTE not being set.
+ events[events_size].mi.mouseData = 0x00;
+ events[events_size].mi.time = 0; // Use current system time.
+
+ // If dwFlags does not contain MOUSEEVENTF_WHEEL, MOUSEEVENTF_XDOWN, or MOUSEEVENTF_XUP,
+ // then mouseData should be zero.
+ // http://msdn.microsoft.com/en-us/library/windows/desktop/ms646273%28v=vs.85%29.aspx
+ if (event->mask & MASK_BUTTON1) {
+ events[events_size].mi.dwFlags |= MOUSEEVENTF_LEFTUP;
+ }
+
+ if (event->mask & MASK_BUTTON2) {
+ events[events_size].mi.dwFlags |= MOUSEEVENTF_RIGHTUP;
+ }
+
+ if (event->mask & MASK_BUTTON3) {
+ events[events_size].mi.dwFlags |= MOUSEEVENTF_MIDDLEUP;
+ }
+
+ if (event->mask & MASK_BUTTON4) {
+ events[events_size].mi.mouseData = XBUTTON1;
+ events[events_size].mi.dwFlags |= MOUSEEVENTF_XUP;
+ }
+
+ if (event->mask & MASK_BUTTON5) {
+ events[events_size].mi.mouseData = XBUTTON2;
+ events[events_size].mi.dwFlags |= MOUSEEVENTF_XUP;
+ }
+
+ events_size++;
+ }
+
+ // Create the key release input
+ // memcpy(key_events + 1, key_events, sizeof(INPUT));
+ // key_events[1].ki.dwFlags |= KEYEVENTF_KEYUP;
+
+ if (! SendInput(events_size, events, sizeof(INPUT)) ) {
+ logger(LOG_LEVEL_ERROR, "%s [%u]: SendInput() failed! (%#lX)\n",
+ __FUNCTION__, __LINE__, (unsigned long) GetLastError());
+ }
+
+ free(events);
+}
diff --git a/vendor/github.com/robotn/gohook/hook/windows/hook_c.h b/vendor/github.com/robotn/gohook/hook/windows/hook_c.h
new file mode 100644
index 0000000..ed15515
--- /dev/null
+++ b/vendor/github.com/robotn/gohook/hook/windows/hook_c.h
@@ -0,0 +1,747 @@
+
+#ifdef HAVE_CONFIG_H
+ #include
+#endif
+
+#include
+#include
+
+#include "../iohook.h"
+#include "input.h"
+// #include "logger.h"
+
+// Thread and hook handles.
+static DWORD hook_thread_id = 0;
+static HHOOK keyboard_event_hhook = NULL, mouse_event_hhook = NULL;
+static HWINEVENTHOOK win_event_hhook = NULL;
+
+// The handle to the DLL module pulled in DllMain on DLL_PROCESS_ATTACH.
+extern HINSTANCE hInst;
+
+// Modifiers for tracking key masks.
+static unsigned short int current_modifiers = 0x0000;
+
+// Click count globals.
+static unsigned short click_count = 0;
+static DWORD click_time = 0;
+static unsigned short int click_button = MOUSE_NOBUTTON;
+static POINT last_click;
+
+// Static event memory.
+static iohook_event event;
+
+// Event dispatch callback.
+static dispatcher_t dispatcher = NULL;
+
+IOHOOK_API void hook_set_dispatch_proc(dispatcher_t dispatch_proc) {
+ logger(LOG_LEVEL_DEBUG, "%s [%u]: Setting new dispatch callback to %#p.\n",
+ __FUNCTION__, __LINE__, dispatch_proc);
+
+ dispatcher = dispatch_proc;
+}
+
+// Send out an event if a dispatcher was set.
+static inline void dispatch_event(iohook_event *const event) {
+ if (dispatcher != NULL) {
+ logger(LOG_LEVEL_DEBUG, "%s [%u]: Dispatching event type %u.\n",
+ __FUNCTION__, __LINE__, event->type);
+
+ dispatcher(event);
+ }
+ else {
+ logger(LOG_LEVEL_WARN, "%s [%u]: No dispatch callback set!\n",
+ __FUNCTION__, __LINE__);
+ }
+}
+
+
+// Set the native modifier mask for future events.
+static inline void set_modifier_mask(unsigned short int mask) {
+ current_modifiers |= mask;
+}
+
+// Unset the native modifier mask for future events.
+static inline void unset_modifier_mask(unsigned short int mask) {
+ current_modifiers ^= mask;
+}
+
+// Get the current native modifier mask state.
+static inline unsigned short int get_modifiers() {
+ return current_modifiers;
+}
+
+// Initialize the modifier mask to the current modifiers.
+static void initialize_modifiers() {
+ current_modifiers = 0x0000;
+
+ // NOTE We are checking the high order bit, so it will be < 0 for a singed short.
+ if (GetKeyState(VK_LSHIFT) < 0) { set_modifier_mask(MASK_SHIFT_L); }
+ if (GetKeyState(VK_RSHIFT) < 0) { set_modifier_mask(MASK_SHIFT_R); }
+ if (GetKeyState(VK_LCONTROL) < 0) { set_modifier_mask(MASK_CTRL_L); }
+ if (GetKeyState(VK_RCONTROL) < 0) { set_modifier_mask(MASK_CTRL_R); }
+ if (GetKeyState(VK_LMENU) < 0) { set_modifier_mask(MASK_ALT_L); }
+ if (GetKeyState(VK_RMENU) < 0) { set_modifier_mask(MASK_ALT_R); }
+ if (GetKeyState(VK_LWIN) < 0) { set_modifier_mask(MASK_META_L); }
+ if (GetKeyState(VK_RWIN) < 0) { set_modifier_mask(MASK_META_R); }
+
+ if (GetKeyState(VK_LBUTTON) < 0) { set_modifier_mask(MASK_BUTTON1); }
+ if (GetKeyState(VK_RBUTTON) < 0) { set_modifier_mask(MASK_BUTTON2); }
+ if (GetKeyState(VK_MBUTTON) < 0) { set_modifier_mask(MASK_BUTTON3); }
+ if (GetKeyState(VK_XBUTTON1) < 0) { set_modifier_mask(MASK_BUTTON4); }
+ if (GetKeyState(VK_XBUTTON2) < 0) { set_modifier_mask(MASK_BUTTON5); }
+
+ if (GetKeyState(VK_NUMLOCK) < 0) { set_modifier_mask(MASK_NUM_LOCK); }
+ if (GetKeyState(VK_CAPITAL) < 0) { set_modifier_mask(MASK_CAPS_LOCK); }
+ if (GetKeyState(VK_SCROLL) < 0) { set_modifier_mask(MASK_SCROLL_LOCK); }
+}
+
+
+/* Retrieves the mouse wheel scroll type. This function cannot be included as
+ * part of the input.h due to platform specific calling restrictions.
+ */
+static unsigned short int get_scroll_wheel_type() {
+ unsigned short int value;
+ UINT wheel_type;
+
+ SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &wheel_type, 0);
+ if (wheel_type == WHEEL_PAGESCROLL) {
+ value = WHEEL_BLOCK_SCROLL;
+ }
+ else {
+ value = WHEEL_UNIT_SCROLL;
+ }
+
+ return value;
+}
+
+/* Retrieves the mouse wheel scroll amount. This function cannot be included as
+ * part of the input.h due to platform specific calling restrictions.
+ */
+static unsigned short int get_scroll_wheel_amount() {
+ unsigned short int value;
+ UINT wheel_amount;
+
+ SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &wheel_amount, 0);
+ if (wheel_amount == WHEEL_PAGESCROLL) {
+ value = 1;
+ }
+ else {
+ value = (unsigned short int) wheel_amount;
+ }
+
+ return value;
+}
+
+void unregister_running_hooks() {
+ // Stop the event hook and any timer still running.
+ if (win_event_hhook != NULL) {
+ UnhookWinEvent(win_event_hhook);
+ win_event_hhook = NULL;
+ }
+
+ // Destroy the native hooks.
+ if (keyboard_event_hhook != NULL) {
+ UnhookWindowsHookEx(keyboard_event_hhook);
+ keyboard_event_hhook = NULL;
+ }
+
+ if (mouse_event_hhook != NULL) {
+ UnhookWindowsHookEx(mouse_event_hhook);
+ mouse_event_hhook = NULL;
+ }
+}
+
+void hook_start_proc() {
+ // Get the local system time in UNIX epoch form.
+ uint64_t timestamp = GetMessageTime();
+
+ // Populate the hook start event.
+ event.time = timestamp;
+ event.reserved = 0x00;
+
+ event.type = EVENT_HOOK_ENABLED;
+ event.mask = 0x00;
+
+ // Fire the hook start event.
+ dispatch_event(&event);
+}
+
+void hook_stop_proc() {
+ // Get the local system time in UNIX epoch form.
+ uint64_t timestamp = GetMessageTime();
+
+ // Populate the hook stop event.
+ event.time = timestamp;
+ event.reserved = 0x00;
+
+ event.type = EVENT_HOOK_DISABLED;
+ event.mask = 0x00;
+
+ // Fire the hook stop event.
+ dispatch_event(&event);
+}
+
+static void process_key_pressed(KBDLLHOOKSTRUCT *kbhook) {
+ // Check and setup modifiers.
+ if (kbhook->vkCode == VK_LSHIFT) { set_modifier_mask(MASK_SHIFT_L); }
+ else if (kbhook->vkCode == VK_RSHIFT) { set_modifier_mask(MASK_SHIFT_R); }
+ else if (kbhook->vkCode == VK_LCONTROL) { set_modifier_mask(MASK_CTRL_L); }
+ else if (kbhook->vkCode == VK_RCONTROL) { set_modifier_mask(MASK_CTRL_R); }
+ else if (kbhook->vkCode == VK_LMENU) { set_modifier_mask(MASK_ALT_L); }
+ else if (kbhook->vkCode == VK_RMENU) { set_modifier_mask(MASK_ALT_R); }
+ else if (kbhook->vkCode == VK_LWIN) { set_modifier_mask(MASK_META_L); }
+ else if (kbhook->vkCode == VK_RWIN) { set_modifier_mask(MASK_META_R); }
+ else if (kbhook->vkCode == VK_NUMLOCK) { set_modifier_mask(MASK_NUM_LOCK); }
+ else if (kbhook->vkCode == VK_CAPITAL) { set_modifier_mask(MASK_CAPS_LOCK); }
+ else if (kbhook->vkCode == VK_SCROLL) { set_modifier_mask(MASK_SCROLL_LOCK); }
+
+ // Populate key pressed event.
+ event.time = kbhook->time;
+ event.reserved = 0x00;
+
+ event.type = EVENT_KEY_PRESSED;
+ event.mask = get_modifiers();
+
+ event.data.keyboard.keycode = keycode_to_scancode(kbhook->vkCode, kbhook->flags);
+ event.data.keyboard.rawcode = kbhook->vkCode;
+ event.data.keyboard.keychar = CHAR_UNDEFINED;
+
+ logger(LOG_LEVEL_INFO, "%s [%u]: Key %#X pressed. (%#X)\n",
+ __FUNCTION__, __LINE__, event.data.keyboard.keycode, event.data.keyboard.rawcode);
+
+ // Populate key pressed event.
+ dispatch_event(&event);
+
+ // If the pressed event was not consumed...
+ if (event.reserved ^ 0x01) {
+ // Buffer for unicode typed chars. No more than 2 needed.
+ WCHAR buffer[2]; // = { WCH_NONE };
+
+ // If the pressed event was not consumed and a unicode char exists...
+ SIZE_T count = keycode_to_unicode(kbhook->vkCode, buffer, sizeof(buffer));
+ unsigned int i;
+ for (i = 0; i < count; i++) {
+ // Populate key typed event.
+ event.time = kbhook->time;
+ event.reserved = 0x00;
+
+ event.type = EVENT_KEY_TYPED;
+ event.mask = get_modifiers();
+
+ event.data.keyboard.keycode = VC_UNDEFINED;
+ event.data.keyboard.rawcode = kbhook->vkCode;
+ event.data.keyboard.keychar = buffer[i];
+
+ logger(LOG_LEVEL_INFO, "%s [%u]: Key %#X typed. (%lc)\n",
+ __FUNCTION__, __LINE__, event.data.keyboard.keycode, (wint_t) event.data.keyboard.keychar);
+
+ // Fire key typed event.
+ dispatch_event(&event);
+ }
+ }
+}
+
+static void process_key_released(KBDLLHOOKSTRUCT *kbhook) {
+ // Check and setup modifiers.
+ if (kbhook->vkCode == VK_LSHIFT) { unset_modifier_mask(MASK_SHIFT_L); }
+ else if (kbhook->vkCode == VK_RSHIFT) { unset_modifier_mask(MASK_SHIFT_R); }
+ else if (kbhook->vkCode == VK_LCONTROL) { unset_modifier_mask(MASK_CTRL_L); }
+ else if (kbhook->vkCode == VK_RCONTROL) { unset_modifier_mask(MASK_CTRL_R); }
+ else if (kbhook->vkCode == VK_LMENU) { unset_modifier_mask(MASK_ALT_L); }
+ else if (kbhook->vkCode == VK_RMENU) { unset_modifier_mask(MASK_ALT_R); }
+ else if (kbhook->vkCode == VK_LWIN) { unset_modifier_mask(MASK_META_L); }
+ else if (kbhook->vkCode == VK_RWIN) { unset_modifier_mask(MASK_META_R); }
+ else if (kbhook->vkCode == VK_NUMLOCK) { unset_modifier_mask(MASK_NUM_LOCK); }
+ else if (kbhook->vkCode == VK_CAPITAL) { unset_modifier_mask(MASK_CAPS_LOCK); }
+ else if (kbhook->vkCode == VK_SCROLL) { unset_modifier_mask(MASK_SCROLL_LOCK); }
+
+ // Populate key pressed event.
+ event.time = kbhook->time;
+ event.reserved = 0x00;
+
+ event.type = EVENT_KEY_RELEASED;
+ event.mask = get_modifiers();
+
+ event.data.keyboard.keycode = keycode_to_scancode(kbhook->vkCode, kbhook->flags);
+ event.data.keyboard.rawcode = kbhook->vkCode;
+ event.data.keyboard.keychar = CHAR_UNDEFINED;
+
+ logger(LOG_LEVEL_INFO, "%s [%u]: Key %#X released. (%#X)\n",
+ __FUNCTION__, __LINE__, event.data.keyboard.keycode, event.data.keyboard.rawcode);
+
+ // Fire key released event.
+ dispatch_event(&event);
+}
+
+LRESULT CALLBACK keyboard_hook_event_proc(int nCode, WPARAM wParam, LPARAM lParam) {
+ KBDLLHOOKSTRUCT *kbhook = (KBDLLHOOKSTRUCT *) lParam;
+ switch (wParam) {
+ case WM_KEYDOWN:
+ case WM_SYSKEYDOWN:
+ process_key_pressed(kbhook);
+ break;
+
+ case WM_KEYUP:
+ case WM_SYSKEYUP:
+ process_key_released(kbhook);
+ break;
+
+ default:
+ // In theory this *should* never execute.
+ logger(LOG_LEVEL_DEBUG, "%s [%u]: Unhandled Windows keyboard event: %#X.\n",
+ __FUNCTION__, __LINE__, (unsigned int) wParam);
+ break;
+ }
+
+ LRESULT hook_result = -1;
+ if (nCode < 0 || event.reserved ^ 0x01) {
+ hook_result = CallNextHookEx(keyboard_event_hhook, nCode, wParam, lParam);
+ }
+ else {
+ logger(LOG_LEVEL_DEBUG, "%s [%u]: Consuming the current event. (%li)\n",
+ __FUNCTION__, __LINE__, (long) hook_result);
+ }
+
+ return hook_result;
+}
+
+
+static void process_button_pressed(MSLLHOOKSTRUCT *mshook, uint16_t button) {
+ uint64_t timestamp = GetMessageTime();
+
+ // Track the number of clicks, the button must match the previous button.
+ if (button == click_button && (long int) (timestamp - click_time) <= hook_get_multi_click_time()) {
+ if (click_count < USHRT_MAX) {
+ click_count++;
+ }
+ else {
+ logger(LOG_LEVEL_WARN, "%s [%u]: Click count overflow detected!\n",
+ __FUNCTION__, __LINE__);
+ }
+ }
+ else {
+ // Reset the click count.
+ click_count = 1;
+
+ // Set the previous button.
+ click_button = button;
+ }
+
+ // Save this events time to calculate the click_count.
+ click_time = timestamp;
+
+ // Store the last click point.
+ last_click.x = mshook->pt.x;
+ last_click.y = mshook->pt.y;
+
+ // Populate mouse pressed event.
+ event.time = timestamp;
+ event.reserved = 0x00;
+
+ event.type = EVENT_MOUSE_PRESSED;
+ event.mask = get_modifiers();
+
+ event.data.mouse.button = button;
+ event.data.mouse.clicks = click_count;
+
+ event.data.mouse.x = mshook->pt.x;
+ event.data.mouse.y = mshook->pt.y;
+
+ logger(LOG_LEVEL_INFO, "%s [%u]: Button %u pressed %u time(s). (%u, %u)\n",
+ __FUNCTION__, __LINE__, event.data.mouse.button, event.data.mouse.clicks,
+ event.data.mouse.x, event.data.mouse.y);
+
+ // Fire mouse pressed event.
+ dispatch_event(&event);
+}
+
+static void process_button_released(MSLLHOOKSTRUCT *mshook, uint16_t button) {
+ // Populate mouse released event.
+ event.time = GetMessageTime();
+ event.reserved = 0x00;
+
+ event.type = EVENT_MOUSE_RELEASED;
+ event.mask = get_modifiers();
+
+ event.data.mouse.button = button;
+ event.data.mouse.clicks = click_count;
+
+ event.data.mouse.x = mshook->pt.x;
+ event.data.mouse.y = mshook->pt.y;
+
+ logger(LOG_LEVEL_INFO, "%s [%u]: Button %u released %u time(s). (%u, %u)\n",
+ __FUNCTION__, __LINE__, event.data.mouse.button,
+ event.data.mouse.clicks,
+ event.data.mouse.x, event.data.mouse.y);
+
+ // Fire mouse released event.
+ dispatch_event(&event);
+
+ // If the pressed event was not consumed...
+ if (event.reserved ^ 0x01 && last_click.x == mshook->pt.x && last_click.y == mshook->pt.y) {
+ // Populate mouse clicked event.
+ event.time = GetMessageTime();
+ event.reserved = 0x00;
+
+ event.type = EVENT_MOUSE_CLICKED;
+ event.mask = get_modifiers();
+
+ event.data.mouse.button = button;
+ event.data.mouse.clicks = click_count;
+ event.data.mouse.x = mshook->pt.x;
+ event.data.mouse.y = mshook->pt.y;
+
+ logger(LOG_LEVEL_INFO, "%s [%u]: Button %u clicked %u time(s). (%u, %u)\n",
+ __FUNCTION__, __LINE__, event.data.mouse.button, event.data.mouse.clicks,
+ event.data.mouse.x, event.data.mouse.y);
+
+ // Fire mouse clicked event.
+ dispatch_event(&event);
+ }
+
+ // Reset the number of clicks.
+ if (button == click_button && (long int) (event.time - click_time) > hook_get_multi_click_time()) {
+ // Reset the click count.
+ click_count = 0;
+ }
+}
+
+static void process_mouse_moved(MSLLHOOKSTRUCT *mshook) {
+ uint64_t timestamp = GetMessageTime();
+
+ // We received a mouse move event with the mouse actually moving.
+ // This verifies that the mouse was moved after being depressed.
+ if (last_click.x != mshook->pt.x || last_click.y != mshook->pt.y) {
+ // Reset the click count.
+ if (click_count != 0 && (long) (timestamp - click_time) > hook_get_multi_click_time()) {
+ click_count = 0;
+ }
+
+ // Populate mouse move event.
+ event.time = timestamp;
+ event.reserved = 0x00;
+
+ event.mask = get_modifiers();
+
+ // Check the modifier mask range for MASK_BUTTON1 - 5.
+ bool mouse_dragged = event.mask & (MASK_BUTTON1 | MASK_BUTTON2 | MASK_BUTTON3 | MASK_BUTTON4 | MASK_BUTTON5);
+ if (mouse_dragged) {
+ // Create Mouse Dragged event.
+ event.type = EVENT_MOUSE_DRAGGED;
+ }
+ else {
+ // Create a Mouse Moved event.
+ event.type = EVENT_MOUSE_MOVED;
+ }
+
+ event.data.mouse.button = MOUSE_NOBUTTON;
+ event.data.mouse.clicks = click_count;
+ event.data.mouse.x = mshook->pt.x;
+ event.data.mouse.y = mshook->pt.y;
+
+ logger(LOG_LEVEL_INFO, "%s [%u]: Mouse %s to %u, %u.\n",
+ __FUNCTION__, __LINE__, mouse_dragged ? "dragged" : "moved",
+ event.data.mouse.x, event.data.mouse.y);
+
+ // Fire mouse move event.
+ dispatch_event(&event);
+ }
+}
+
+static void process_mouse_wheel(MSLLHOOKSTRUCT *mshook, uint8_t direction) {
+ // Track the number of clicks.
+ // Reset the click count and previous button.
+ click_count = 1;
+ click_button = MOUSE_NOBUTTON;
+
+ // Populate mouse wheel event.
+ event.time = GetMessageTime();
+ event.reserved = 0x00;
+
+ event.type = EVENT_MOUSE_WHEEL;
+ event.mask = get_modifiers();
+
+ event.data.wheel.clicks = click_count;
+ event.data.wheel.x = mshook->pt.x;
+ event.data.wheel.y = mshook->pt.y;
+
+ event.data.wheel.type = get_scroll_wheel_type();
+ event.data.wheel.amount = get_scroll_wheel_amount();
+
+ /* Delta HIWORD(mshook->mouseData)
+ * A positive value indicates that the wheel was rotated
+ * forward, away from the user; a negative value indicates that
+ * the wheel was rotated backward, toward the user. One wheel
+ * click is defined as WHEEL_DELTA, which is 120. */
+ event.data.wheel.rotation = ((int16_t) HIWORD(mshook->mouseData) / WHEEL_DELTA) * -1;
+
+ // Set the direction based on what event was received.
+ event.data.wheel.direction = direction;
+
+ logger(LOG_LEVEL_INFO, "%s [%u]: Mouse wheel type %u, rotated %i units in the %u direction at %u, %u.\n",
+ __FUNCTION__, __LINE__, event.data.wheel.type,
+ event.data.wheel.amount * event.data.wheel.rotation,
+ event.data.wheel.direction,
+ event.data.wheel.x, event.data.wheel.y);
+
+ // Fire mouse wheel event.
+ dispatch_event(&event);
+}
+
+LRESULT CALLBACK mouse_hook_event_proc(int nCode, WPARAM wParam, LPARAM lParam) {
+ MSLLHOOKSTRUCT *mshook = (MSLLHOOKSTRUCT *) lParam;
+ switch (wParam) {
+ case WM_LBUTTONDOWN:
+ set_modifier_mask(MASK_BUTTON1);
+ process_button_pressed(mshook, MOUSE_BUTTON1);
+ break;
+
+ case WM_RBUTTONDOWN:
+ set_modifier_mask(MASK_BUTTON2);
+ process_button_pressed(mshook, MOUSE_BUTTON2);
+ break;
+
+ case WM_MBUTTONDOWN:
+ set_modifier_mask(MASK_BUTTON3);
+ process_button_pressed(mshook, MOUSE_BUTTON3);
+ break;
+
+ case WM_XBUTTONDOWN:
+ case WM_NCXBUTTONDOWN:
+ if (HIWORD(mshook->mouseData) == XBUTTON1) {
+ set_modifier_mask(MASK_BUTTON4);
+ process_button_pressed(mshook, MOUSE_BUTTON4);
+ }
+ else if (HIWORD(mshook->mouseData) == XBUTTON2) {
+ set_modifier_mask(MASK_BUTTON5);
+ process_button_pressed(mshook, MOUSE_BUTTON5);
+ }
+ else {
+ // Extra mouse buttons.
+ uint16_t button = HIWORD(mshook->mouseData);
+
+ // Add support for mouse 4 & 5.
+ if (button == 4) {
+ set_modifier_mask(MOUSE_BUTTON4);
+ }
+ else if (button == 5) {
+ set_modifier_mask(MOUSE_BUTTON5);
+ }
+
+ process_button_pressed(mshook, button);
+ }
+ break;
+
+
+ case WM_LBUTTONUP:
+ unset_modifier_mask(MASK_BUTTON1);
+ process_button_released(mshook, MOUSE_BUTTON1);
+ break;
+
+ case WM_RBUTTONUP:
+ unset_modifier_mask(MASK_BUTTON2);
+ process_button_released(mshook, MOUSE_BUTTON2);
+ break;
+
+ case WM_MBUTTONUP:
+ unset_modifier_mask(MASK_BUTTON3);
+ process_button_released(mshook, MOUSE_BUTTON3);
+ break;
+
+ case WM_XBUTTONUP:
+ case WM_NCXBUTTONUP:
+ if (HIWORD(mshook->mouseData) == XBUTTON1) {
+ unset_modifier_mask(MASK_BUTTON4);
+ process_button_released(mshook, MOUSE_BUTTON4);
+ }
+ else if (HIWORD(mshook->mouseData) == XBUTTON2) {
+ unset_modifier_mask(MASK_BUTTON5);
+ process_button_released(mshook, MOUSE_BUTTON5);
+ }
+ else {
+ // Extra mouse buttons.
+ uint16_t button = HIWORD(mshook->mouseData);
+
+ // Add support for mouse 4 & 5.
+ if (button == 4) {
+ unset_modifier_mask(MOUSE_BUTTON4);
+ }
+ else if (button == 5) {
+ unset_modifier_mask(MOUSE_BUTTON5);
+ }
+
+ process_button_released(mshook, MOUSE_BUTTON5);
+ }
+ break;
+
+ case WM_MOUSEMOVE:
+ process_mouse_moved(mshook);
+ break;
+
+ case WM_MOUSEWHEEL:
+ process_mouse_wheel(mshook, WHEEL_VERTICAL_DIRECTION);
+ break;
+
+ /* For horizontal scroll wheel support.
+ * NOTE Windows >= Vista
+ * case 0x020E:
+ */
+ case WM_MOUSEHWHEEL:
+ process_mouse_wheel(mshook, WHEEL_HORIZONTAL_DIRECTION);
+ break;
+
+ default:
+ // In theory this *should* never execute.
+ logger(LOG_LEVEL_DEBUG, "%s [%u]: Unhandled Windows mouse event: %#X.\n",
+ __FUNCTION__, __LINE__, (unsigned int) wParam);
+ break;
+ }
+
+ LRESULT hook_result = -1;
+ if (nCode < 0 || event.reserved ^ 0x01) {
+ hook_result = CallNextHookEx(mouse_event_hhook, nCode, wParam, lParam);
+ }
+ else {
+ logger(LOG_LEVEL_DEBUG, "%s [%u]: Consuming the current event. (%li)\n",
+ __FUNCTION__, __LINE__, (long) hook_result);
+ }
+
+ return hook_result;
+}
+
+
+// Callback function that handles events.
+void CALLBACK win_hook_event_proc(HWINEVENTHOOK hook, DWORD event, HWND hWnd, LONG idObject, LONG idChild, DWORD dwEventThread, DWORD dwmsEventTime) {
+ switch (event) {
+ case EVENT_OBJECT_NAMECHANGE:
+ logger(LOG_LEVEL_INFO, "%s [%u]: Restarting Windows input hook on window event: %#X.\n",
+ __FUNCTION__, __LINE__, event);
+
+ // Remove any keyboard or mouse hooks that are still running.
+ if (keyboard_event_hhook != NULL) {
+ UnhookWindowsHookEx(keyboard_event_hhook);
+ }
+
+ if (mouse_event_hhook != NULL) {
+ UnhookWindowsHookEx(mouse_event_hhook);
+ }
+
+ // Restart the event hooks.
+ keyboard_event_hhook = SetWindowsHookEx(WH_KEYBOARD_LL, keyboard_hook_event_proc, hInst, 0);
+ mouse_event_hhook = SetWindowsHookEx(WH_MOUSE_LL, mouse_hook_event_proc, hInst, 0);
+
+ // Re-initialize modifier masks.
+ initialize_modifiers();
+
+ // FIXME We should compare the modifier mask before and after the restart
+ // to determine if we should synthesize missing events.
+
+ // Check for event hook error.
+ if (keyboard_event_hhook == NULL || mouse_event_hhook == NULL) {
+ logger(LOG_LEVEL_ERROR, "%s [%u]: SetWindowsHookEx() failed! (%#lX)\n",
+ __FUNCTION__, __LINE__, (unsigned long) GetLastError());
+ }
+ break;
+
+ default:
+ logger(LOG_LEVEL_INFO, "%s [%u]: Unhandled Windows window event: %#X.\n",
+ __FUNCTION__, __LINE__, event);
+ }
+}
+
+
+IOHOOK_API int hook_run() {
+ int status = IOHOOK_FAILURE;
+
+ // Set the thread id we want to signal later.
+ hook_thread_id = GetCurrentThreadId();
+
+ // Spot check the hInst incase the library was statically linked and DllMain
+ // did not receive a pointer on load.
+ if (hInst == NULL) {
+ logger(LOG_LEVEL_INFO, "%s [%u]: hInst was not set by DllMain().\n",
+ __FUNCTION__, __LINE__);
+
+ hInst = GetModuleHandle(NULL);
+ if (hInst != NULL) {
+ // Initialize native input helper functions.
+ load_input_helper();
+ }
+ else {
+ logger(LOG_LEVEL_ERROR, "%s [%u]: Could not determine hInst for SetWindowsHookEx()! (%#lX)\n",
+ __FUNCTION__, __LINE__, (unsigned long) GetLastError());
+
+ status = IOHOOK_ERROR_GET_MODULE_HANDLE;
+ }
+ }
+
+ // Create the native hooks.
+ keyboard_event_hhook = SetWindowsHookEx(WH_KEYBOARD_LL, keyboard_hook_event_proc, hInst, 0);
+ mouse_event_hhook = SetWindowsHookEx(WH_MOUSE_LL, mouse_hook_event_proc, hInst, 0);
+
+ // Create a window event hook to listen for capture change.
+ win_event_hhook = SetWinEventHook(
+ EVENT_OBJECT_NAMECHANGE, EVENT_OBJECT_NAMECHANGE,
+ NULL,
+ win_hook_event_proc,
+ 0, 0,
+ WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS);
+
+ // If we did not encounter a problem, start processing events.
+ if (keyboard_event_hhook != NULL && mouse_event_hhook != NULL) {
+ if (win_event_hhook == NULL) {
+ logger(LOG_LEVEL_WARN, "%s [%u]: SetWinEventHook() failed!\n",
+ __FUNCTION__, __LINE__);
+ }
+
+ logger(LOG_LEVEL_DEBUG, "%s [%u]: SetWindowsHookEx() successful.\n",
+ __FUNCTION__, __LINE__);
+
+ // Check and setup modifiers.
+ initialize_modifiers();
+
+ // Set the exit status.
+ status = IOHOOK_SUCCESS;
+
+ // Windows does not have a hook start event or callback so we need to
+ // manually fake it.
+ hook_start_proc();
+
+ // Block until the thread receives an WM_QUIT request.
+ MSG message;
+ while (GetMessage(&message, (HWND) NULL, 0, 0) > 0) {
+ TranslateMessage(&message);
+ DispatchMessage(&message);
+ }
+ }
+ else {
+ logger(LOG_LEVEL_ERROR, "%s [%u]: SetWindowsHookEx() failed! (%#lX)\n",
+ __FUNCTION__, __LINE__, (unsigned long) GetLastError());
+
+ status = IOHOOK_ERROR_SET_WINDOWS_HOOK_EX;
+ }
+
+
+ // Unregister any hooks that may still be installed.
+ unregister_running_hooks();
+
+ // We must explicitly call the cleanup handler because Windows does not
+ // provide a thread cleanup method like POSIX pthread_cleanup_push/pop.
+ hook_stop_proc();
+
+ return status;
+}
+
+IOHOOK_API int hook_stop() {
+ int status = IOHOOK_FAILURE;
+
+ // Try to exit the thread naturally.
+ if (PostThreadMessage(hook_thread_id, WM_QUIT, (WPARAM) NULL, (LPARAM) NULL)) {
+ status = IOHOOK_SUCCESS;
+ }
+
+ logger(LOG_LEVEL_DEBUG, "%s [%u]: Status: %#X.\n",
+ __FUNCTION__, __LINE__, status);
+
+ return status;
+}
diff --git a/vendor/github.com/robotn/gohook/hook/windows/input.h b/vendor/github.com/robotn/gohook/hook/windows/input.h
new file mode 100644
index 0000000..6ba9817
--- /dev/null
+++ b/vendor/github.com/robotn/gohook/hook/windows/input.h
@@ -0,0 +1,104 @@
+/***********************************************************************
+ Input
+ ***********************************************************************/
+
+#ifndef _included_input_helper
+#define _included_input_helper
+
+#include
+#include
+
+#ifndef LPFN_ISWOW64PROCESS
+typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
+#endif
+
+typedef void* (CALLBACK *KbdLayerDescriptor) (VOID);
+
+#define CAPLOK 0x01
+#define WCH_NONE 0xF000
+#define WCH_DEAD 0xF001
+
+#ifndef WM_MOUSEHWHEEL
+#define WM_MOUSEHWHEEL 0x020E
+#endif
+
+typedef struct _VK_TO_WCHARS {
+ BYTE VirtualKey;
+ BYTE Attributes;
+ WCHAR wch[];
+} VK_TO_WCHARS, *PVK_TO_WCHARS;
+
+typedef struct _LIGATURE {
+ BYTE VirtualKey;
+ WORD ModificationNumber;
+ WCHAR wch[];
+} LIGATURE, *PLIGATURE;
+
+typedef struct _VK_TO_BIT {
+ BYTE Vk;
+ BYTE ModBits;
+} VK_TO_BIT, *PVK_TO_BIT;
+
+typedef struct _MODIFIERS {
+ PVK_TO_BIT pVkToBit; // __ptr64
+ WORD wMaxModBits;
+ BYTE ModNumber[];
+} MODIFIERS, *PMODIFIERS;
+
+typedef struct _VSC_VK {
+ BYTE Vsc;
+ USHORT Vk;
+} VSC_VK, *PVSC_VK;
+
+typedef struct _VK_TO_WCHAR_TABLE {
+ PVK_TO_WCHARS pVkToWchars; // __ptr64
+ BYTE nModifications;
+ BYTE cbSize;
+} VK_TO_WCHAR_TABLE, *PVK_TO_WCHAR_TABLE;
+
+typedef struct _DEADKEY {
+ DWORD dwBoth;
+ WCHAR wchComposed;
+ USHORT uFlags;
+} DEADKEY, *PDEADKEY;
+
+typedef struct _VSC_LPWSTR {
+ BYTE vsc;
+ WCHAR *pwsz; // __ptr64
+} VSC_LPWSTR, *PVSC_LPWSTR;
+
+typedef struct tagKbdLayer {
+ PMODIFIERS pCharModifiers; // __ptr64
+ PVK_TO_WCHAR_TABLE pVkToWcharTable; // __ptr64
+ PDEADKEY pDeadKey; // __ptr64
+ PVSC_LPWSTR pKeyNames; // __ptr64
+ PVSC_LPWSTR pKeyNamesExt; // __ptr64
+ WCHAR **pKeyNamesDead; // __ptr64
+ USHORT *pusVSCtoVK; // __ptr64
+ BYTE bMaxVSCtoVK;
+ PVSC_VK pVSCtoVK_E0; // __ptr64
+ PVSC_VK pVSCtoVK_E1; // __ptr64
+ DWORD fLocaleFlags;
+ BYTE nLgMax;
+ BYTE cbLgEntry;
+ PLIGATURE pLigature; // __ptr64
+ DWORD dwType;
+ DWORD dwSubType;
+} KBDTABLES, *PKBDTABLES; // __ptr64
+
+
+extern SIZE_T keycode_to_unicode(DWORD keycode, PWCHAR buffer, SIZE_T size);
+
+//extern DWORD unicode_to_keycode(wchar_t unicode);
+
+extern unsigned short keycode_to_scancode(DWORD vk_code, DWORD flags);
+
+extern DWORD scancode_to_keycode(unsigned short scancode);
+
+// Initialize the locale list and wow64 pointer size.
+extern int load_input_helper();
+
+// Cleanup the initialized locales.
+extern int unload_input_helper();
+
+#endif
diff --git a/vendor/github.com/robotn/gohook/hook/windows/input_c.h b/vendor/github.com/robotn/gohook/hook/windows/input_c.h
new file mode 100644
index 0000000..f061b1b
--- /dev/null
+++ b/vendor/github.com/robotn/gohook/hook/windows/input_c.h
@@ -0,0 +1,832 @@
+
+#ifdef HAVE_CONFIG_H
+ #include
+#endif
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "../iohook.h"
+#include "../logger_c.h"
+#include "input.h"
+
+static const uint16_t keycode_scancode_table[][2] = {
+ /* idx { vk_code, scancode }, */
+ /* 0 */ { VC_UNDEFINED, 0x0000 }, // 0x00
+ /* 1 */ { MOUSE_BUTTON1, VK_ESCAPE }, // 0x01
+ /* 2 */ { MOUSE_BUTTON2, 0x0031 }, // 0x02
+ /* 3 */ { VC_UNDEFINED, 0x0032 }, // 0x03 VK_CANCEL
+ /* 4 */ { MOUSE_BUTTON3, 0x0033 }, // 0x04
+ /* 5 */ { MOUSE_BUTTON4, 0x0034 }, // 0x05
+ /* 6 */ { MOUSE_BUTTON5, 0x0035 }, // 0x06
+ /* 7 */ { VC_UNDEFINED, 0x0036 }, // 0x07 Undefined
+ /* 8 */ { VC_BACKSPACE, 0x0037 }, // 0x08 VK_BACK
+ /* 9 */ { VC_TAB, 0x0038 }, // 0x09 VK_TAB
+ /* 10 */ { VC_UNDEFINED, 0x0039 }, // 0x0A Reserved
+ /* 11 */ { VC_UNDEFINED, 0x0030 }, // 0x0B Reserved
+ /* 12 */ { VC_CLEAR, VK_OEM_MINUS }, // 0x0C VK_CLEAR
+ /* 13 */ { VC_ENTER, VK_OEM_PLUS }, // 0x0D VK_RETURN
+ /* 14 */ { VC_UNDEFINED, VK_BACK }, // 0x0E Undefined
+ /* 15 */ { VC_UNDEFINED, VK_TAB }, // 0x0F Undefined
+ /* 16 */ { VC_SHIFT_L, 0x0051 }, // 0x10 VK_SHIFT
+ /* 17 */ { VC_CONTROL_L, 0x0057 }, // 0x11 VK_CONTROL
+ /* 18 */ { VC_ALT_L, 0x0045 }, // 0x12 VK_MENU ALT key
+ /* 19 */ { VC_PAUSE, 0x0052 }, // 0x13 VK_PAUSE
+ /* 20 */ { VC_CAPS_LOCK, 0x0054 }, // 0x14 VK_CAPITAL CAPS LOCK key
+ /* 21 */ { VC_KATAKANA, 0x0059 }, // 0x15 VK_KANA IME Kana mode
+ /* 22 */ { VC_UNDEFINED, 0x0055 }, // 0x16 Undefined
+ /* 23 */ { VC_UNDEFINED, 0x0049 }, // 0x17 VK_JUNJA IME Junja mode
+ /* 24 */ { VC_UNDEFINED, 0x004F }, // 0x18 VK_FINAL
+ /* 25 */ { VC_KANJI, 0x0050 }, // 0x19 VK_KANJI / VK_HANJA IME Kanji / Hanja mode
+ /* 26 */ { VC_UNDEFINED, 0x00DB }, // 0x1A Undefined
+ /* 27 */ { VC_ESCAPE, 0x00DD }, // 0x1B VK_ESCAPE ESC key
+ /* 28 */ { VC_UNDEFINED, VK_RETURN }, // 0x1C VK_CONVERT IME convert// 0x1C
+ /* 29 */ { VC_UNDEFINED, VK_LCONTROL }, // 0x1D VK_NONCONVERT IME nonconvert
+ /* 30 */ { VC_UNDEFINED, 0x0041 }, // 0x1E VK_ACCEPT IME accept
+ /* 31 */ { VC_UNDEFINED, 0x0053 }, // 0x1F VK_MODECHANGE IME mode change request
+ /* 32 */ { VC_SPACE, 0x0044 }, // 0x20 VK_SPACE SPACEBAR
+ /* 33 */ { VC_PAGE_UP, 0x0046 }, // 0x21 VK_PRIOR PAGE UP key
+ /* 34 */ { VC_PAGE_DOWN, 0x0047 }, // 0x22 VK_NEXT PAGE DOWN key
+ /* 35 */ { VC_END, 0x0048 }, // 0x23 VK_END END key
+ /* 36 */ { VC_HOME, 0x004A }, // 0x24 VK_HOME HOME key
+ /* 37 */ { VC_LEFT, 0x004B }, // 0x25 VK_LEFT LEFT ARROW key
+ /* 38 */ { VC_UP, 0x004C }, // 0x26 VK_UP UP ARROW key
+ /* 39 */ { VC_RIGHT, VK_OEM_1 }, // 0x27 VK_RIGHT RIGHT ARROW key
+ /* 40 */ { VC_DOWN, VK_OEM_7 }, // 0x28 VK_DOWN DOWN ARROW key
+ /* 41 */ { VC_UNDEFINED, VK_OEM_3 }, // 0x29 VK_SELECT SELECT key
+ /* 42 */ { VC_UNDEFINED, VK_LSHIFT }, // 0x2A VK_PRINT PRINT key
+ /* 43 */ { VC_UNDEFINED, VK_OEM_5 }, // 0x2B VK_EXECUTE EXECUTE key
+ /* 44 */ { VC_PRINTSCREEN, 0x005A }, // 0x2C VK_SNAPSHOT PRINT SCREEN key
+ /* 45 */ { VC_INSERT, 0x0058 }, // 0x2D VK_INSERT INS key
+ /* 46 */ { VC_DELETE, 0x0043 }, // 0x2E VK_DELETE DEL key
+ /* 47 */ { VC_UNDEFINED, 0x0056 }, // 0x2F VK_HELP HELP key
+ /* 48 */ { VC_0, 0x0042 }, // 0x30 0 key
+ /* 49 */ { VC_1, 0x004E }, // 0x31 1 key
+ /* 50 */ { VC_2, 0x004D }, // 0x32 2 key
+ /* 51 */ { VC_3, VK_OEM_COMMA }, // 0x33 3 key
+ /* 52 */ { VC_4, VK_OEM_PERIOD }, // 0x34 4 key
+ /* 53 */ { VC_5, VK_OEM_2 }, // 0x35 5 key
+ /* 54 */ { VC_6, VK_RSHIFT }, // 0x36 6 key
+ /* 55 */ { VC_7, VK_MULTIPLY }, // 0x37 7 key
+ /* 56 */ { VC_8, VK_LMENU }, // 0x38 8 key
+ /* 57 */ { VC_9, VK_SPACE }, // 0x39 9 key
+ /* 58 */ { VC_UNDEFINED, VK_CAPITAL }, // 0x3A Undefined
+ /* 59 */ { VC_UNDEFINED, VK_F1 }, // 0x3B Undefined
+ /* 60 */ { VC_UNDEFINED, VK_F2 }, // 0x3C Undefined
+ /* 61 */ { VC_UNDEFINED, VK_F3 }, // 0x3D Undefined
+ /* 62 */ { VC_UNDEFINED, VK_F4 }, // 0x3E Undefined
+ /* 63 */ { VC_UNDEFINED, VK_F5 }, // 0x3F Undefined
+ /* 64 */ { VC_UNDEFINED, VK_F6 }, // 0x40 Undefined
+ /* 65 */ { VC_A, VK_F7 }, // 0x41 A key
+ /* 66 */ { VC_B, VK_F8 }, // 0x42 B key
+ /* 67 */ { VC_C, VK_F9 }, // 0x43 C key
+ /* 68 */ { VC_D, VK_F10 }, // 0x44 D key
+ /* 69 */ { VC_E, VK_NUMLOCK }, // 0x45 E key
+ /* 70 */ { VC_F, VK_SCROLL }, // 0x46 F key
+ /* 71 */ { VC_G, VK_NUMPAD7 }, // 0x47 G key
+ /* 72 */ { VC_H, VK_NUMPAD8 }, // 0x48 H key
+ /* 73 */ { VC_I, VK_NUMPAD9 }, // 0x49 I key
+ /* 74 */ { VC_J, VK_SUBTRACT }, // 0x4A J key
+ /* 75 */ { VC_K, VK_NUMPAD4 }, // 0x4B K key
+ /* 76 */ { VC_L, VK_NUMPAD5 }, // 0x4C L key
+ /* 77 */ { VC_M, VK_NUMPAD6 }, // 0x4D M key
+ /* 78 */ { VC_N, VK_ADD }, // 0x4E N key
+ /* 79 */ { VC_O, VK_NUMPAD1 }, // 0x4F O key
+ /* 80 */ { VC_P, VK_NUMPAD2 }, // 0x50 P key
+ /* 81 */ { VC_Q, VK_NUMPAD3 }, // 0x51 Q key
+ /* 82 */ { VC_R, VK_NUMPAD0 }, // 0x52 R key
+ /* 83 */ { VC_S, VK_DECIMAL }, // 0x53 S key
+ /* 84 */ { VC_T, 0x0000 }, // 0x54 T key
+ /* 85 */ { VC_U, 0x0000 }, // 0x55 U key
+ /* 86 */ { VC_V, 0x0000 }, // 0x56 V key
+ /* 87 */ { VC_W, VK_F11 }, // 0x57 W key
+ /* 88 */ { VC_X, VK_F12 }, // 0x58 X key
+ /* 89 */ { VC_Y, 0x0000 }, // 0x59 Y key
+ /* 90 */ { VC_Z, 0x0000 }, // 0x5A Z key
+ /* 91 */ { VC_META_L, VK_F13 }, // 0x5B VK_LWIN Left Windows key (Natural keyboard)
+ /* 92 */ { VC_META_R, VK_F14 }, // 0x5C VK_RWIN Right Windows key (Natural keyboard)
+ /* 93 */ { VC_CONTEXT_MENU, VK_F15 }, // 0x5D VK_APPS Applications key (Natural keyboard)
+ /* 94 */ { VC_UNDEFINED, 0x0000 }, // 0x5E Reserved
+ /* 95 */ { VC_SLEEP, 0x0000 }, // 0x5F VK_SLEEP Computer Sleep key
+ /* 96 */ { VC_KP_0, 0x0000 }, // 0x60 VK_NUMPAD0 Numeric keypad 0 key
+ /* 97 */ { VC_KP_1, 0x0000 }, // 0x61 VK_NUMPAD1 Numeric keypad 1 key
+ /* 98 */ { VC_KP_2, 0x0000 }, // 0x62 VK_NUMPAD2 Numeric keypad 2 key
+ /* 99 */ { VC_KP_3, VK_F16 }, // 0x63 VK_NUMPAD3 Numeric keypad 3 key
+ /* 100 */ { VC_KP_4, VK_F17 }, // 0x64 VK_NUMPAD4 Numeric keypad 4 key
+ /* 101 */ { VC_KP_5, VK_F18 }, // 0x65 VK_NUMPAD5 Numeric keypad 5 key
+ /* 102 */ { VC_KP_6, VK_F19 }, // 0x66 VK_NUMPAD6 Numeric keypad 6 key
+ /* 103 */ { VC_KP_7, VK_F20 }, // 0x67 VK_NUMPAD7 Numeric keypad 7 key
+ /* 104 */ { VC_KP_8, VK_F21 }, // 0x68 VK_NUMPAD8 Numeric keypad 8 key
+ /* 105 */ { VC_KP_9, VK_F22 }, // 0x69 VK_NUMPAD9 Numeric keypad 9 key
+ /* 106 */ { VC_KP_MULTIPLY, VK_F23 }, // 0x6A VK_MULTIPLY Multiply key
+ /* 107 */ { VC_KP_ADD, VK_F24 }, // 0x6B VK_ADD Add key
+ /* 108 */ { VC_UNDEFINED, 0x0000 }, // 0x6C VK_SEPARATOR Separator key
+ /* 109 */ { VC_KP_SUBTRACT, 0x0000 }, // 0x6D VK_SUBTRACT Subtract key
+ /* 110 */ { VC_KP_SEPARATOR, 0x0000 }, // 0x6E VK_DECIMAL Decimal key
+ /* 111 */ { VC_KP_DIVIDE, 0x0000 }, // 0x6F VK_DIVIDE Divide key
+ /* 112 */ { VC_F1, VK_KANA }, // 0x70 VK_F1 F1 key
+ /* 113 */ { VC_F2, 0x0000 }, // 0x71 VK_F2 F2 key
+ /* 114 */ { VC_F3, 0x0000 }, // 0x72 VK_F3 F3 key
+ /* 115 */ { VC_F4, 0x0000 }, // 0x73 VK_F4 F4 key
+ /* 116 */ { VC_F5, 0x0000 }, // 0x74 VK_F5 F5 key
+ /* 117 */ { VC_F6, 0x0000 }, // 0x75 VK_F6 F6 key
+ /* 118 */ { VC_F7, 0x0000 }, // 0x76 VK_F7 F7 key
+ /* 119 */ { VC_F8, 0x0000 }, // 0x77 VK_F8 F8 key
+ /* 120 */ { VC_F9, 0x0000 }, // 0x78 VK_F9 F9 key
+ /* 121 */ { VC_F10, VK_KANJI }, // 0x79 VK_F10 F10 key
+ /* 122 */ { VC_F11, 0x0000 }, // 0x7A VK_F11 F11 key
+ /* 123 */ { VC_F12, 0x0000 }, // 0x7B VK_F12 F12 key
+ /* 124 */ { VC_F13, 0x0000 }, // 0x7C VK_F13 F13 key
+ /* 125 */ { VC_F14, VK_OEM_8 }, // 0x7D VK_F14 F14 key
+ /* 126 */ { VC_F15, 0x0000 }, // 0x7E VK_F15 F15 key
+ /* 127 */ { VC_F16, 0x0000 }, // 0x7F VK_F16 F16 key
+
+ // No Offset Offset (i & 0x007F) | 0x80
+
+ /* 128 */ { VC_F17, 0x0000 }, // 0x80 VK_F17 F17 key
+ /* 129 */ { VC_F18, 0x0000 }, // 0x81 VK_F18 F18 key
+ /* 130 */ { VC_F19, 0x0000 }, // 0x82 VK_F19 F19 key
+ /* 131 */ { VC_F20, 0x0000 }, // 0x83 VK_F20 F20 key
+ /* 132 */ { VC_F21, 0x0000 }, // 0x84 VK_F21 F21 key
+ /* 133 */ { VC_F22, 0x0000 }, // 0x85 VK_F22 F22 key
+ /* 134 */ { VC_F23, 0x0000 }, // 0x86 VK_F23 F23 key
+ /* 135 */ { VC_F24, 0x0000 }, // 0x87 VK_F24 F24 key
+ /* 136 */ { VC_UNDEFINED, 0x0000 }, // 0x88 Unassigned
+ /* 137 */ { VC_UNDEFINED, 0x0000 }, // 0x89 Unassigned
+ /* 138 */ { VC_UNDEFINED, 0x0000 }, // 0x8A Unassigned
+ /* 139 */ { VC_UNDEFINED, 0x0000 }, // 0x8B Unassigned
+ /* 140 */ { VC_UNDEFINED, 0x0000 }, // 0x8C Unassigned
+ /* 141 */ { VC_UNDEFINED, 0x0000 }, // 0x8D Unassigned
+ /* 142 */ { VC_UNDEFINED, 0x0000 }, // 0x8E Unassigned
+ /* 143 */ { VC_UNDEFINED, 0x0000 }, // 0x8F Unassigned
+ /* 144 */ { VC_NUM_LOCK, VK_MEDIA_PREV_TRACK }, // 0x90 VK_NUMLOCK NUM LOCK key
+ /* 145 */ { VC_SCROLL_LOCK, 0x0000 }, // 0x91 VK_SCROLL SCROLL LOCK key
+ /* 146 */ { VC_UNDEFINED, 0x0000 }, // 0x92 OEM specific
+ /* 147 */ { VC_UNDEFINED, 0x0000 }, // 0x93 OEM specific
+ /* 148 */ { VC_UNDEFINED, 0x0000 }, // 0x94 OEM specific
+ /* 149 */ { VC_UNDEFINED, 0x0000 }, // 0x95 OEM specific
+ /* 150 */ { VC_UNDEFINED, 0x0000 }, // 0x96 OEM specific
+ /* 151 */ { VC_UNDEFINED, 0x0000 }, // 0x97 Unassigned
+ /* 152 */ { VC_UNDEFINED, 0x0000 }, // 0x98 Unassigned
+ /* 153 */ { VC_UNDEFINED, VK_MEDIA_NEXT_TRACK }, // 0x99 Unassigned
+ /* 154 */ { VC_UNDEFINED, 0x0000 }, // 0x9A Unassigned
+ /* 155 */ { VC_UNDEFINED, 0x0000 }, // 0x9B Unassigned
+ /* 156 */ { VC_UNDEFINED, 0x0000 }, // 0x9C Unassigned
+ /* 157 */ { VC_UNDEFINED, VK_RCONTROL }, // 0x9D Unassigned
+ /* 158 */ { VC_UNDEFINED, 0x0000 }, // 0x9E Unassigned
+ /* 159 */ { VC_UNDEFINED, 0x0000 }, // 0x9F Unassigned
+ /* 160 */ { VC_SHIFT_L, VK_VOLUME_MUTE }, // 0xA0 VK_LSHIFT Left SHIFT key
+ /* 161 */ { VC_SHIFT_R, VK_LAUNCH_APP2 }, // 0xA1 VK_RSHIFT Right SHIFT key
+ /* 162 */ { VC_CONTROL_L, VK_MEDIA_PLAY_PAUSE }, // 0xA2 VK_LCONTROL Left CONTROL key
+ /* 163 */ { VC_CONTROL_R, 0x0000 }, // 0xA3 VK_RCONTROL Right CONTROL key
+ /* 164 */ { VC_ALT_L, VK_MEDIA_STOP }, // 0xA4 VK_LMENU Left MENU key
+ /* 165 */ { VC_ALT_R, 0x0000 }, // 0xA5 VK_RMENU Right MENU key
+ /* 166 */ { VC_BROWSER_BACK, 0x0000 }, // 0xA6 VK_BROWSER_BACK Browser Back key
+ /* 167 */ { VC_BROWSER_FORWARD, 0x0000 }, // 0xA7 VK_BROWSER_FORWARD Browser Forward key
+ /* 168 */ { VC_BROWSER_REFRESH, 0x0000 }, // 0xA8 VK_BROWSER_REFRESH Browser Refresh key
+ /* 169 */ { VC_BROWSER_STOP, 0x0000 }, // 0xA9 VK_BROWSER_STOP Browser Stop key
+ /* 170 */ { VC_BROWSER_SEARCH, 0x0000 }, // 0xAA VK_BROWSER_SEARCH Browser Search key
+ /* 171 */ { VC_BROWSER_FAVORITES, 0x0000 }, // 0xAB VK_BROWSER_FAVORITES Browser Favorites key
+ /* 172 */ { VC_BROWSER_HOME, 0x0000 }, // 0xAC VK_BROWSER_HOME Browser Start and Home key
+ /* 173 */ { VC_VOLUME_MUTE, 0x0000 }, // 0xAD VK_VOLUME_MUTE Volume Mute key
+ /* 174 */ { VC_VOLUME_DOWN, VK_VOLUME_DOWN }, // 0xAE VK_VOLUME_DOWN Volume Down key
+ /* 175 */ { VC_VOLUME_UP, 0x0000 }, // 0xAF VK_VOLUME_UP Volume Up key
+ /* 176 */ { VC_MEDIA_NEXT, VK_VOLUME_UP }, // 0xB0 VK_MEDIA_NEXT_TRACK Next Track key
+ /* 177 */ { VC_MEDIA_PREVIOUS, 0x0000 }, // 0xB1 VK_MEDIA_PREV_TRACK Previous Track key
+ /* 178 */ { VC_MEDIA_STOP, VK_BROWSER_HOME }, // 0xB2 VK_MEDIA_STOP Stop Media key
+ /* 179 */ { VC_MEDIA_PLAY, 0x0000 }, // 0xB3 VK_MEDIA_PLAY_PAUSE Play/Pause Media key
+ /* 180 */ { VC_UNDEFINED, 0x0000 }, // 0xB4 VK_LAUNCH_MAIL Start Mail key
+ /* 181 */ { VC_MEDIA_SELECT, VK_DIVIDE }, // 0xB5 VK_LAUNCH_MEDIA_SELECT Select Media key
+ /* 182 */ { VC_APP_MAIL, 0x0000 }, // 0xB6 VK_LAUNCH_APP1 Start Application 1 key
+ /* 183 */ { VC_APP_CALCULATOR, VK_SNAPSHOT }, // 0xB7 VK_LAUNCH_APP2 Start Application 2 key
+ /* 184 */ { VC_UNDEFINED, VK_RMENU }, // 0xB8 Reserved
+ /* 185 */ { VC_UNDEFINED, 0x0000 }, // 0xB9 Reserved
+ /* 186 */ { VC_SEMICOLON, 0x0000 }, // 0xBA VK_OEM_1 Varies by keyboard. For the US standard keyboard, the ';:' key
+ /* 187 */ { VC_EQUALS, 0x0000 }, // 0xBB VK_OEM_PLUS For any country/region, the '+' key
+ /* 188 */ { VC_COMMA, 0x00E6 }, // 0xBC VK_OEM_COMMA For any country/region, the ',' key
+ /* 189 */ { VC_MINUS, 0x0000 }, // 0xBD VK_OEM_MINUS For any country/region, the '-' key
+ /* 190 */ { VC_PERIOD, 0x0000 }, // 0xBE VK_OEM_PERIOD For any country/region, the '.' key
+ /* 191 */ { VC_SLASH, 0x0000 }, // 0xBF VK_OEM_2 Varies by keyboard. For the US standard keyboard, the '/?' key
+ /* 192 */ { VC_BACKQUOTE, 0x0000 }, // 0xC0 VK_OEM_3 Varies by keyboard. For the US standard keyboard, the '`~' key
+ /* 193 */ { VC_UNDEFINED, 0x0000 }, // 0xC1 Reserved
+ /* 194 */ { VC_UNDEFINED, 0x0000 }, // 0xC2 Reserved
+ /* 195 */ { VC_UNDEFINED, 0x0000 }, // 0xC3 Reserved
+ /* 196 */ { VC_UNDEFINED, 0x0000 }, // 0xC4 Reserved
+ /* 197 */ { VC_UNDEFINED, VK_PAUSE }, // 0xC5 Reserved
+ /* 198 */ { VC_UNDEFINED, 0x0000 }, // 0xC6 Reserved
+ /* 199 */ { VC_UNDEFINED, VK_HOME }, // 0xC7 Reserved
+ /* 200 */ { VC_UNDEFINED, VK_UP }, // 0xC8 Reserved
+ /* 201 */ { VC_UNDEFINED, VK_PRIOR }, // 0xC9 Reserved
+ /* 202 */ { VC_UNDEFINED, 0x0000 }, // 0xCA Reserved
+ /* 203 */ { VC_UNDEFINED, VK_LEFT }, // 0xCB Reserved
+ /* 204 */ { VC_UNDEFINED, VK_CLEAR }, // 0xCC Reserved
+ /* 205 */ { VC_UNDEFINED, VK_RIGHT }, // 0xCD Reserved
+ /* 206 */ { VC_UNDEFINED, 0x0000 }, // 0xCE Reserved
+ /* 207 */ { VC_UNDEFINED, VK_END }, // 0xCF Reserved
+ /* 208 */ { VC_UNDEFINED, VK_DOWN }, // 0xD0 Reserved
+ /* 209 */ { VC_UNDEFINED, VK_NEXT }, // 0xD1 Reserved
+ /* 210 */ { VC_UNDEFINED, VK_INSERT }, // 0xD2 Reserved
+ /* 211 */ { VC_UNDEFINED, VK_DELETE }, // 0xD3 Reserved
+ /* 212 */ { VC_UNDEFINED, 0x0000 }, // 0xD4 Reserved
+ /* 213 */ { VC_UNDEFINED, 0x0000 }, // 0xD5 Reserved
+ /* 214 */ { VC_UNDEFINED, 0x0000 }, // 0xD6 Reserved
+ /* 215 */ { VC_UNDEFINED, 0x0000 }, // 0xD7 Reserved
+ /* 216 */ { VC_UNDEFINED, 0x0000 }, // 0xD8 Unassigned
+ /* 217 */ { VC_UNDEFINED, 0x0000 }, // 0xD9 Unassigned
+ /* 218 */ { VC_UNDEFINED, 0x0000 }, // 0xDA Unassigned
+ /* 219 */ { VC_OPEN_BRACKET, VK_LWIN }, // 0xDB VK_OEM_4 Varies by keyboard. For the US standard keyboard, the '[{' key
+ /* 220 */ { VC_BACK_SLASH, VK_RWIN }, // 0xDC VK_OEM_5 Varies by keyboard. For the US standard keyboard, the '\|' key
+ /* 221 */ { VC_CLOSE_BRACKET, VK_APPS }, // 0xDD VK_OEM_6 Varies by keyboard. For the US standard keyboard, the ']}' key
+ /* 222 */ { VC_QUOTE, 0x0000 }, // 0xDE VK_OEM_7 Varies by keyboard. For the US standard keyboard, the 'single-quote/double-quote' key
+ /* 223 */ { VC_YEN, VK_SLEEP }, // 0xDF VK_OEM_8 Varies by keyboard.
+ /* 224 */ { VC_UNDEFINED, 0x0000 }, // 0xE0 Reserved
+ /* 225 */ { VC_UNDEFINED, 0x0000 }, // 0xE1 OEM specific
+ /* 226 */ { VC_UNDEFINED, 0x0000 }, // 0xE2 VK_OEM_102 Either the angle bracket key or the backslash key on the RT 102-key keyboard
+ /* 227 */ { VC_UNDEFINED, 0x0000 }, // 0xE3 OEM specific
+ /* 228 */ { VC_UNDEFINED, 0x00E5 }, // 0xE4 VC_APP_PICTURES OEM specific
+ /* 229 */ { VC_APP_PICTURES, VK_BROWSER_SEARCH }, // 0xE5 VK_PROCESSKEY IME PROCESS key
+ /* 230 */ { VC_APP_MUSIC, VK_BROWSER_FAVORITES }, // 0xE6 OEM specific
+ /* 231 */ { VC_UNDEFINED, VK_BROWSER_REFRESH }, // 0xE7 VK_PACKET Used to pass Unicode characters as if they were keystrokes. The VK_PACKET key is the low word of a 32-bit Virtual Key value used for non-keyboard input methods.
+ /* 232 */ { VC_UNDEFINED, VK_BROWSER_STOP }, // 0xE8 Unassigned
+ /* 233 */ { VC_UNDEFINED, VK_BROWSER_FORWARD }, // 0xE9 OEM specific
+ /* 234 */ { VC_UNDEFINED, VK_BROWSER_BACK }, // 0xEA OEM specific
+ /* 235 */ { VC_UNDEFINED, 0x0000 }, // 0xEB OEM specific
+ /* 236 */ { VC_UNDEFINED, VK_LAUNCH_APP1 }, // 0xEC OEM specific
+ /* 237 */ { VC_UNDEFINED, VK_LAUNCH_MEDIA_SELECT }, // 0xED OEM specific
+ /* 238 */ { VC_UNDEFINED, 0x0000 }, // 0xEE OEM specific
+ /* 239 */ { VC_UNDEFINED, 0x0000 }, // 0xEF OEM specific
+ /* 240 */ { VC_UNDEFINED, 0x0000 }, // 0xF0 OEM specific
+ /* 241 */ { VC_UNDEFINED, 0x0000 }, // 0xF1 OEM specific
+ /* 242 */ { VC_UNDEFINED, 0x0000 }, // 0xF2 OEM specific
+ /* 243 */ { VC_UNDEFINED, 0x0000 }, // 0xF3 OEM specific
+ /* 244 */ { VC_UNDEFINED, 0x0000 }, // 0xF4 OEM specific
+ /* 245 */ { VC_UNDEFINED, 0x0000 }, // 0xF5 OEM specific
+ /* 246 */ { VC_UNDEFINED, 0x0000 }, // 0xF6 VK_ATTN Attn key
+ /* 247 */ { VC_UNDEFINED, 0x0000 }, // 0xF7 VK_CRSEL CrSel key
+ /* 248 */ { VC_UNDEFINED, 0x0000 }, // 0xF8 VK_EXSEL ExSel key
+ /* 249 */ { VC_UNDEFINED, 0x0000 }, // 0xF9 VK_EREOF Erase EOF key
+ /* 250 */ { VC_UNDEFINED, 0x0000 }, // 0xFA VK_PLAY Play key
+ /* 251 */ { VC_UNDEFINED, 0x0000 }, // 0xFB VK_ZOOM Zoom key
+ /* 252 */ { VC_UNDEFINED, 0x0000 }, // 0xFC VK_NONAME Reserved
+ /* 253 */ { VC_UNDEFINED, 0x0000 }, // 0xFD
+ /* 254 */ { VC_CLEAR, 0x0000 }, // 0xFE VK_OEM_CLEAR Clear key
+ /* 255 */ { VC_UNDEFINED, 0x0000 } // 0xFE Unassigned
+};
+
+unsigned short keycode_to_scancode(DWORD vk_code, DWORD flags) {
+ unsigned short scancode = VC_UNDEFINED;
+
+ // Check the vk_code is in range.
+ // NOTE vk_code >= 0 is assumed because DWORD is unsigned.
+ if (vk_code < sizeof(keycode_scancode_table) / sizeof(keycode_scancode_table[0])) {
+ scancode = keycode_scancode_table[vk_code][0];
+
+ if (flags & LLKHF_EXTENDED) {
+ logger(LOG_LEVEL_WARN, "%s [%u]: EXTD2, vk_code %li\n",
+ __FUNCTION__, __LINE__, vk_code);
+
+ switch (vk_code) {
+ case VK_PRIOR:
+ case VK_NEXT:
+ case VK_END:
+ case VK_HOME:
+ case VK_LEFT:
+ case VK_UP:
+ case VK_RIGHT:
+ case VK_DOWN:
+
+ case VK_INSERT:
+ case VK_DELETE:
+ scancode |= 0xEE00;
+ break;
+
+ case VK_RETURN:
+ scancode |= 0x0E00;
+ break;
+ }
+ }
+ else {
+ // logger(LOG_LEVEL_WARN, "%s [%u]: Test2, vk_code %li\n",
+ // __FUNCTION__, __LINE__, vk_code);
+ }
+ }
+
+ return scancode;
+}
+
+DWORD scancode_to_keycode(unsigned short scancode) {
+ unsigned short keycode = 0x0000;
+
+ // Check the vk_code is in range.
+ // NOTE vk_code >= 0 is assumed because the scancode is unsigned.
+ if (scancode < 128) {
+ keycode = keycode_scancode_table[scancode][1];
+ }
+ else {
+ // Calculate the upper offset based on the lower half of the scancode + 128.
+ unsigned short int i = (scancode & 0x007F) | 0x80;
+
+ if (i < sizeof(keycode_scancode_table) / sizeof(keycode_scancode_table[1])) {
+ keycode = keycode_scancode_table[i][1];
+ }
+ }
+
+ return keycode;
+}
+
+
+/************************************************************************/
+
+// Structure and pointers for the keyboard locale cache.
+typedef struct _KeyboardLocale {
+ HKL id; // Locale ID
+ HINSTANCE library; // Keyboard DLL instance.
+ PVK_TO_BIT pVkToBit; // Pointers struct arrays.
+ PVK_TO_WCHAR_TABLE pVkToWcharTable;
+ PDEADKEY pDeadKey;
+ struct _KeyboardLocale* next;
+} KeyboardLocale;
+
+static KeyboardLocale* locale_first = NULL;
+static KeyboardLocale* locale_current = NULL;
+static WCHAR deadChar = WCH_NONE;
+
+// Amount of pointer padding to apply for Wow64 instances.
+static unsigned short int ptr_padding = 0;
+
+#if defined(_WIN32) && !defined(_WIN64)
+// Small function to check and see if we are executing under Wow64.
+static BOOL is_wow64() {
+ BOOL status = FALSE;
+
+ LPFN_ISWOW64PROCESS pIsWow64Process = (LPFN_ISWOW64PROCESS)
+ GetProcAddress(GetModuleHandle("kernel32"), "IsWow64Process");
+
+ if (pIsWow64Process != NULL) {
+ HANDLE current_proc = GetCurrentProcess();
+
+ if (!pIsWow64Process(current_proc, &status)) {
+ status = FALSE;
+
+ logger(LOG_LEVEL_DEBUG, "%s [%u]: pIsWow64Process(%#p, %#p) failed!\n",
+ __FUNCTION__, __LINE__, current_proc, &status);
+ }
+ }
+
+ return status;
+}
+#endif
+
+// Locate the DLL that contains the current keyboard layout.
+static int get_keyboard_layout_file(char *layoutFile, DWORD bufferSize) {
+ int status = IOHOOK_FAILURE;
+ HKEY hKey;
+ DWORD varType = REG_SZ;
+
+ char kbdName[KL_NAMELENGTH];
+ if (GetKeyboardLayoutName(kbdName)) {
+ char kbdKeyPath[51 + KL_NAMELENGTH];
+ snprintf(kbdKeyPath, 51 + KL_NAMELENGTH, "SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts\\%s", kbdName);
+
+ if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, (LPCTSTR) kbdKeyPath, 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS) {
+ if (RegQueryValueEx(hKey, "Layout File", NULL, &varType, (LPBYTE) layoutFile, &bufferSize) == ERROR_SUCCESS) {
+ RegCloseKey(hKey);
+ status = IOHOOK_SUCCESS;
+ }
+ }
+ }
+
+ return status;
+}
+
+static int refresh_locale_list() {
+ int count = 0;
+
+ // Get the number of layouts the user has activated.
+ int hkl_size = GetKeyboardLayoutList(0, NULL);
+ if (hkl_size > 0) {
+ logger(LOG_LEVEL_INFO, "%s [%u]: GetKeyboardLayoutList(0, NULL) found %i layouts.\n",
+ __FUNCTION__, __LINE__, hkl_size);
+
+ // Get the thread id that currently has focus for our default.
+ DWORD focus_pid = GetWindowThreadProcessId(GetForegroundWindow(), NULL);
+ HKL hlk_focus = GetKeyboardLayout(focus_pid);
+ HKL hlk_default = GetKeyboardLayout(0);
+ HKL *hkl_list = malloc(sizeof(HKL) * hkl_size);
+
+ int new_size = GetKeyboardLayoutList(hkl_size, hkl_list);
+ if (new_size > 0) {
+ if (new_size != hkl_size) {
+ logger(LOG_LEVEL_WARN, "%s [%u]: Locale size mismatch! "
+ "Expected %i, received %i!\n",
+ __FUNCTION__, __LINE__, hkl_size, new_size);
+ }
+ else {
+ logger(LOG_LEVEL_INFO, "%s [%u]: Received %i locales.\n",
+ __FUNCTION__, __LINE__, new_size);
+ }
+
+ KeyboardLocale* locale_previous = NULL;
+ KeyboardLocale* locale_item = locale_first;
+
+ // Go though the linked list and remove KeyboardLocale's that are
+ // no longer loaded.
+ while (locale_item != NULL) {
+ // Check to see if the old HKL is in the new list.
+ bool is_loaded = false;
+ int i;
+ for (i = 0; i < new_size && !is_loaded; i++) {
+ if (locale_item->id == hkl_list[i]) {
+ // Flag and jump out of the loop.
+ hkl_list[i] = NULL;
+ is_loaded = true;
+ }
+ }
+
+
+ if (is_loaded) {
+ logger(LOG_LEVEL_DEBUG, "%s [%u]: Found locale ID %#p in the cache.\n",
+ __FUNCTION__, __LINE__, locale_item->id);
+
+ // Set the previous local to the current locale.
+ locale_previous = locale_item;
+
+ // Check and see if the locale is our current active locale.
+ if (locale_item->id == hlk_focus) {
+ locale_current = locale_item;
+ }
+
+ count++;
+ }
+ else {
+ logger(LOG_LEVEL_DEBUG, "%s [%u]: Removing locale ID %#p from the cache.\n",
+ __FUNCTION__, __LINE__, locale_item->id);
+
+ // If the old id is not in the new list, remove it.
+ locale_previous->next = locale_item->next;
+
+ // Make sure the locale_current points NULL or something valid.
+ if (locale_item == locale_current) {
+ locale_current = NULL;
+ }
+
+ // Free the memory used by locale_item;
+ free(locale_item);
+
+ // Set the item to the pervious item to guarantee a next.
+ locale_item = locale_previous;
+ }
+
+ // Iterate to the next linked list item.
+ locale_item = locale_item->next;
+ }
+
+
+ // Insert anything new into the linked list.
+ int i;
+ for (i = 0; i < new_size; i++) {
+ // Check to see if the item was already in the list.
+ if (hkl_list[i] != NULL) {
+ // Set the active keyboard layout for this thread to the HKL.
+ ActivateKeyboardLayout(hkl_list[i], 0x00);
+
+ // Try to pull the current keyboard layout DLL from the registry.
+ char layoutFile[MAX_PATH];
+ if (get_keyboard_layout_file(layoutFile, sizeof(layoutFile)) == IOHOOK_SUCCESS) {
+ // You can't trust the %SYSPATH%, look it up manually.
+ char systemDirectory[MAX_PATH];
+ if (GetSystemDirectory(systemDirectory, MAX_PATH) != 0) {
+ char kbdLayoutFilePath[MAX_PATH];
+ snprintf(kbdLayoutFilePath, MAX_PATH, "%s\\%s", systemDirectory, layoutFile);
+
+ logger(LOG_LEVEL_DEBUG, "%s [%u]: Loading layout for %#p: %s.\n",
+ __FUNCTION__, __LINE__, hkl_list[i], layoutFile);
+
+ // Create the new locale item.
+ locale_item = malloc(sizeof(KeyboardLocale));
+ locale_item->id = hkl_list[i];
+ locale_item->library = LoadLibrary(kbdLayoutFilePath);
+
+ // Get the function pointer from the library to get the keyboard layer descriptor.
+ KbdLayerDescriptor pKbdLayerDescriptor = (KbdLayerDescriptor) GetProcAddress(locale_item->library, "KbdLayerDescriptor");
+ if (pKbdLayerDescriptor != NULL) {
+ PKBDTABLES pKbd = pKbdLayerDescriptor();
+
+ // Store the memory address of the following 3 structures.
+ BYTE *base = (BYTE *) pKbd;
+
+ // First element of each structure, no offset adjustment needed.
+ locale_item->pVkToBit = pKbd->pCharModifiers->pVkToBit;
+
+ // Second element of pKbd, +4 byte offset on wow64.
+ locale_item->pVkToWcharTable = *((PVK_TO_WCHAR_TABLE *) (base + offsetof(KBDTABLES, pVkToWcharTable) + ptr_padding));
+
+ // Third element of pKbd, +8 byte offset on wow64.
+ locale_item->pDeadKey = *((PDEADKEY *) (base + offsetof(KBDTABLES, pDeadKey) + (ptr_padding * 2)));
+
+ // This will always be added to the end of the list.
+ locale_item->next = NULL;
+
+ // Insert the item into the linked list.
+ if (locale_previous == NULL) {
+ // If nothing came before, the list is empty.
+ locale_first = locale_item;
+ }
+ else {
+ // Append the new locale to the end of the list.
+ locale_previous->next = locale_item;
+ }
+
+ // Check and see if the locale is our current active locale.
+ if (locale_item->id == hlk_focus) {
+ locale_current = locale_item;
+ }
+
+ // Set the pervious locale item to the new one.
+ locale_previous = locale_item;
+
+ count++;
+ }
+ else {
+ logger(LOG_LEVEL_ERROR,
+ "%s [%u]: GetProcAddress() failed for KbdLayerDescriptor!\n",
+ __FUNCTION__, __LINE__);
+
+ FreeLibrary(locale_item->library);
+ free(locale_item);
+ locale_item = NULL;
+ }
+ }
+ else {
+ logger(LOG_LEVEL_ERROR,
+ "%s [%u]: GetSystemDirectory() failed!\n",
+ __FUNCTION__, __LINE__);
+ }
+ }
+ else {
+ logger(LOG_LEVEL_ERROR,
+ "%s [%u]: Could not find keyboard map for locale %#p!\n",
+ __FUNCTION__, __LINE__, hkl_list[i]);
+ }
+ }
+ }
+ }
+ else {
+ logger(LOG_LEVEL_ERROR,
+ "%s [%u]: GetKeyboardLayoutList() failed!\n",
+ __FUNCTION__, __LINE__);
+
+ // TODO Try and recover by using the current layout.
+ // Hint: Use locale_id instead of hkl_list[i] in the loop above.
+ }
+
+ free(hkl_list);
+ ActivateKeyboardLayout(hlk_default, 0x00);
+ }
+
+ return count;
+}
+
+SIZE_T keycode_to_unicode(DWORD keycode, PWCHAR buffer, SIZE_T size) {
+ // Get the thread id that currently has focus and ask for its current
+ // locale.
+ DWORD focus_pid = GetWindowThreadProcessId(GetForegroundWindow(), NULL);
+ HKL locale_id = GetKeyboardLayout(focus_pid);
+
+ // If the current Locale is not the new locale, search the linked list.
+ if (locale_current == NULL || locale_current->id != locale_id) {
+ locale_current = NULL;
+ KeyboardLocale* locale_item = locale_first;
+
+ // Search the linked list...
+ while (locale_item != NULL && locale_item->id != locale_id) {
+ locale_item = locale_item->next;
+ }
+
+ // You may already be a winner!
+ if (locale_item != NULL && locale_item->id != locale_id) {
+ logger(LOG_LEVEL_INFO,
+ "%s [%u]: Activating keyboard layout %#p.\n",
+ __FUNCTION__, __LINE__, locale_item->id);
+
+ // Switch the current locale.
+ locale_current = locale_item;
+ locale_item = NULL;
+
+ // If they layout changes the dead key state needs to be reset.
+ // This is consistent with the way Windows handles locale changes.
+ deadChar = WCH_NONE;
+ }
+ else {
+ logger(LOG_LEVEL_DEBUG,
+ "%s [%u]: Refreshing locale cache.\n",
+ __FUNCTION__, __LINE__);
+
+ refresh_locale_list();
+ }
+ }
+
+ // Initialize to empty.
+ SIZE_T charCount = 0;
+ // buffer[i] = WCH_NONE;
+
+ // Check and make sure the Unicode helper was loaded.
+ if (locale_current != NULL) {
+ logger(LOG_LEVEL_INFO,
+ "%s [%u]: Using keyboard layout %#p.\n",
+ __FUNCTION__, __LINE__, locale_current->id);
+
+ int mod = 0;
+
+ int capsLock = (GetKeyState(VK_CAPITAL) & 0x01);
+
+ PVK_TO_BIT pVkToBit = locale_current->pVkToBit;
+ PVK_TO_WCHAR_TABLE pVkToWcharTable = locale_current->pVkToWcharTable;
+ PDEADKEY pDeadKey = locale_current->pDeadKey;
+
+ /* Loop over the modifier keys for this locale and determine what is
+ * currently depressed. Because this is only a structure of two
+ * bytes, we don't need to worry about the structure padding of __ptr64
+ * offsets on Wow64.
+ */
+ bool is_shift = false, is_ctrl = false, is_alt = false;
+ int i;
+ for (i = 0; pVkToBit[i].Vk != 0; i++) {
+ short state = GetAsyncKeyState(pVkToBit[i].Vk);
+
+ // Check to see if the most significant bit is active.
+ if (state & ~SHRT_MAX) {
+ if (pVkToBit[i].Vk == VK_SHIFT) {
+ is_shift = true;
+ }
+ else if (pVkToBit[i].Vk == VK_CONTROL) {
+ is_ctrl = true;
+ }
+ else if (pVkToBit[i].Vk == VK_MENU) {
+ is_alt = true;
+ }
+ }
+ }
+
+ // Check the Shift modifier.
+ if (is_shift) {
+ mod = 1;
+ }
+
+ // Check for the AltGr modifier.
+ if (is_ctrl && is_alt) {
+ mod += 3;
+ }
+
+ // Default 32 bit structure size should be 6 bytes (4 for the pointer and 2
+ // additional byte fields) that are padded out to 8 bytes by the compiler.
+ unsigned short sizeVkToWcharTable = sizeof(VK_TO_WCHAR_TABLE);
+ #if defined(_WIN32) && !defined(_WIN64)
+ if (is_wow64()) {
+ // If we are running under Wow64 the size of the first pointer will be
+ // 8 bringing the total size to 10 bytes padded out to 16.
+ sizeVkToWcharTable = (sizeVkToWcharTable + ptr_padding + 7) & -8;
+ }
+ #endif
+
+ BYTE *ptrCurrentVkToWcharTable = (BYTE *) pVkToWcharTable;
+
+ int cbSize, n;
+ do {
+ // cbSize is used to calculate n, and n is used for the size of pVkToWchars[j].wch[n]
+ cbSize = *(ptrCurrentVkToWcharTable + offsetof(VK_TO_WCHAR_TABLE, cbSize) + ptr_padding);
+ n = (cbSize - 2) / 2;
+
+ // Same as VK_TO_WCHARS pVkToWchars[] = pVkToWcharTable[i].pVkToWchars
+ PVK_TO_WCHARS pVkToWchars = (PVK_TO_WCHARS) ((PVK_TO_WCHAR_TABLE) ptrCurrentVkToWcharTable)->pVkToWchars;
+
+ if (pVkToWchars != NULL && mod < n) {
+ // pVkToWchars[j].VirtualKey
+ BYTE *pCurrentVkToWchars = (BYTE *) pVkToWchars;
+
+ do {
+ if (((PVK_TO_WCHARS) pCurrentVkToWchars)->VirtualKey == keycode) {
+ if ((((PVK_TO_WCHARS) pCurrentVkToWchars)->Attributes == CAPLOK) && capsLock) {
+ if (is_shift && mod > 0) {
+ mod -= 1;
+ }
+ else {
+ mod += 1;
+ }
+ }
+
+ // Set the initial unicode char.
+ WCHAR unicode = ((PVK_TO_WCHARS) pCurrentVkToWchars)->wch[mod];
+
+ // Increment the pCurrentVkToWchars by the size of wch[n].
+ pCurrentVkToWchars += sizeof(VK_TO_WCHARS) + (sizeof(WCHAR) * n);
+
+
+ if (unicode == WCH_DEAD) {
+ // The current unicode char is a dead key...
+ if (deadChar == WCH_NONE) {
+ // No previous dead key was set so cache the next
+ // wchar so we know what to do next time its pressed.
+ deadChar = ((PVK_TO_WCHARS) pCurrentVkToWchars)->wch[mod];
+ }
+ else {
+ if (size >= 2) {
+ // Received a second dead key.
+ memset(buffer, deadChar, 2);
+ //buffer[0] = deadChar;
+ //buffer[1] = deadChar;
+
+ deadChar = WCH_NONE;
+ charCount = 2;
+ }
+ }
+ }
+ else if (unicode != WCH_NONE) {
+ // We are not WCH_NONE or WCH_DEAD
+ if (size >= 1) {
+ buffer[0] = unicode;
+ charCount = 1;
+ }
+ }
+
+ break;
+ }
+ else {
+ // Add sizeof WCHAR because we are really an array of WCHAR[n] not WCHAR[]
+ pCurrentVkToWchars += sizeof(VK_TO_WCHARS) + (sizeof(WCHAR) * n);
+ }
+ } while ( ((PVK_TO_WCHARS) pCurrentVkToWchars)->VirtualKey != 0 );
+ }
+
+ // This is effectively the same as: ptrCurrentVkToWcharTable = pVkToWcharTable[++i];
+ ptrCurrentVkToWcharTable += sizeVkToWcharTable;
+ } while (cbSize != 0);
+
+
+ // If the current local has a dead key set.
+ if (deadChar != WCH_NONE) {
+ // Loop over the pDeadKey lookup table for the locale.
+ int i;
+ for (i = 0; pDeadKey[i].dwBoth != 0; i++) {
+ WCHAR baseChar = (WCHAR) pDeadKey[i].dwBoth;
+ WCHAR diacritic = (WCHAR) (pDeadKey[i].dwBoth >> 16);
+
+ // If we locate an extended dead char, set it.
+ if (size >= 1 && baseChar == buffer[0] && diacritic == deadChar) {
+ deadChar = WCH_NONE;
+
+ if (charCount <= size) {
+ memset(buffer, (WCHAR) pDeadKey[i].wchComposed, charCount);
+ //buffer[i] = (WCHAR) pDeadKey[i].wchComposed;
+ }
+ }
+ }
+ }
+ }
+
+ return charCount;
+}
+
+int load_input_helper() {
+ int count = 0;
+
+ #if defined(_WIN32) && !defined(_WIN64)
+ if (is_wow64()) {
+ ptr_padding = sizeof(void *);
+ }
+ #endif
+
+ count = refresh_locale_list();
+
+ logger(LOG_LEVEL_INFO,
+ "%s [%u]: refresh_locale_list() found %i locale(s).\n",
+ __FUNCTION__, __LINE__, count);
+
+ return count;
+}
+
+// This returns the number of locales that were removed.
+int unload_input_helper() {
+ int count = 0;
+
+ // Cleanup and free memory from the old list.
+ KeyboardLocale* locale_item = locale_first;
+ while (locale_item != NULL) {
+ // Remove the first item from the linked list.
+ FreeLibrary(locale_item->library);
+ locale_first = locale_item->next;
+ free(locale_item);
+ locale_item = locale_first;
+
+ count++;
+ }
+
+ // Reset the current local.
+ locale_current = NULL;
+
+ return count;
+}
diff --git a/vendor/github.com/robotn/gohook/hook/windows/properties_c.h b/vendor/github.com/robotn/gohook/hook/windows/properties_c.h
new file mode 100644
index 0000000..3c8ea2b
--- /dev/null
+++ b/vendor/github.com/robotn/gohook/hook/windows/properties_c.h
@@ -0,0 +1,211 @@
+
+#ifdef HAVE_CONFIG_H
+ #include
+#endif
+
+#include
+#include "../iohook.h"
+#include "input.h"
+// #include "logger.h"
+
+// The handle to the DLL module pulled in DllMain on DLL_PROCESS_ATTACH.
+HINSTANCE hInst;
+
+// input_hook.c
+extern void unregister_running_hooks();
+
+
+// Structure for the monitor_enum_proc() callback so we can track the count.
+typedef struct _screen_info {
+ uint8_t count;
+ screen_data *data;
+} screen_info;
+
+
+static BOOL CALLBACK monitor_enum_proc(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
+ int width = lprcMonitor->right - lprcMonitor->left;
+ int height = lprcMonitor->bottom - lprcMonitor->top;
+ int origin_x = lprcMonitor->left;
+ int origin_y = lprcMonitor->top;
+
+ if (width > 0 && height > 0) {
+ screen_info *screens = (screen_info *) dwData;
+
+ if (screens->data == NULL) {
+ screens->data = (screen_data *) malloc(sizeof(screen_data));
+ }
+ else {
+ screens->data = (screen_data *) realloc(screens, sizeof(screen_data) * screens->count);
+ }
+
+ screens->data[screens->count++] = (screen_data) {
+ // Should monitor count start @ zero? Currently it starts at 1.
+ .number = screens->count,
+ .x = origin_x,
+ .y = origin_y,
+ .width = width,
+ .height = height
+ };
+
+ logger(LOG_LEVEL_INFO, "%s [%u]: Monitor %d: %ldx%ld (%ld, %ld)\n",
+ __FUNCTION__, __LINE__, screens->count, width, height, origin_x, origin_y);
+ }
+
+ return TRUE;
+}
+
+IOHOOK_API screen_data* hook_create_screen_info(unsigned char *count) {
+ // Initialize count to zero.
+ *count = 0;
+
+ // Create a simple structure to make working with monitor_enum_proc easier.
+ screen_info screens = {
+ .count = 0,
+ .data = NULL
+ };
+
+ BOOL status = EnumDisplayMonitors(NULL, NULL, monitor_enum_proc, (LPARAM) &screens);
+
+ if (!status || screens.count == 0) {
+ // Fallback in case EnumDisplayMonitors fails.
+ logger(LOG_LEVEL_INFO, "%s [%u]: EnumDisplayMonitors failed. Fallback.\n",
+ __FUNCTION__, __LINE__);
+
+ int width = GetSystemMetrics(SM_CXSCREEN);
+ int height = GetSystemMetrics(SM_CYSCREEN);
+
+ if (width > 0 && height > 0) {
+ screens.data = (screen_data *) malloc(sizeof(screen_data));
+
+ if (screens.data != NULL) {
+ *count = 1;
+ screens.data[0] = (screen_data) {
+ .number = 1,
+ .x = 0,
+ .y = 0,
+ .width = width,
+ .height = height
+ };
+ }
+ }
+ } else {
+ // Populate the count.
+ *count = screens.count;
+ }
+
+ return screens.data;
+}
+
+IOHOOK_API long int hook_get_auto_repeat_rate() {
+ long int value = -1;
+ long int rate;
+
+ if (SystemParametersInfo(SPI_GETKEYBOARDSPEED, 0, &rate, 0)) {
+ logger(LOG_LEVEL_INFO, "%s [%u]: SPI_GETKEYBOARDSPEED: %li.\n",
+ __FUNCTION__, __LINE__, rate);
+
+ value = rate;
+ }
+
+ return value;
+}
+
+IOHOOK_API long int hook_get_auto_repeat_delay() {
+ long int value = -1;
+ long int delay;
+
+ if (SystemParametersInfo(SPI_GETKEYBOARDDELAY, 0, &delay, 0)) {
+ logger(LOG_LEVEL_INFO, "%s [%u]: SPI_GETKEYBOARDDELAY: %li.\n",
+ __FUNCTION__, __LINE__, delay);
+
+ value = delay;
+ }
+
+ return value;
+}
+
+IOHOOK_API long int hook_get_pointer_acceleration_multiplier() {
+ long int value = -1;
+ int mouse[3]; // 0-Threshold X, 1-Threshold Y and 2-Speed.
+
+ if (SystemParametersInfo(SPI_GETMOUSE, 0, &mouse, 0)) {
+ logger(LOG_LEVEL_INFO, "%s [%u]: SPI_GETMOUSE[2]: %i.\n",
+ __FUNCTION__, __LINE__, mouse[2]);
+
+ value = mouse[2];
+ }
+
+ return value;
+}
+
+IOHOOK_API long int hook_get_pointer_acceleration_threshold() {
+ long int value = -1;
+ int mouse[3]; // 0-Threshold X, 1-Threshold Y and 2-Speed.
+
+ if (SystemParametersInfo(SPI_GETMOUSE, 0, &mouse, 0)) {
+ logger(LOG_LEVEL_INFO, "%s [%u]: SPI_GETMOUSE[0]: %i.\n",
+ __FUNCTION__, __LINE__, mouse[0]);
+ logger(LOG_LEVEL_INFO, "%s [%u]: SPI_GETMOUSE[1]: %i.\n",
+ __FUNCTION__, __LINE__, mouse[1]);
+
+ // Average the x and y thresholds.
+ value = (mouse[0] + mouse[1]) / 2;
+ }
+
+ return value;
+}
+
+IOHOOK_API long int hook_get_pointer_sensitivity() {
+ long int value = -1;
+ int sensitivity;
+
+ if (SystemParametersInfo(SPI_GETMOUSESPEED, 0, &sensitivity, 0)) {
+ logger(LOG_LEVEL_INFO, "%s [%u]: SPI_GETMOUSESPEED: %i.\n",
+ __FUNCTION__, __LINE__, sensitivity);
+
+ value = sensitivity;
+ }
+
+ return value;
+}
+
+IOHOOK_API long int hook_get_multi_click_time() {
+ long int value = -1;
+ UINT clicktime;
+
+ clicktime = GetDoubleClickTime();
+ logger(LOG_LEVEL_INFO, "%s [%u]: GetDoubleClickTime: %u.\n",
+ __FUNCTION__, __LINE__, (unsigned int) clicktime);
+
+ value = (long int) clicktime;
+
+ return value;
+}
+
+// DLL Entry point.
+BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID lpReserved) {
+ switch (fdwReason) {
+ case DLL_PROCESS_ATTACH:
+ // Save the DLL address.
+ hInst = hInstDLL;
+
+ // Initialize native input helper functions.
+ load_input_helper();
+ break;
+
+ case DLL_PROCESS_DETACH:
+ // Unregister any hooks that may still be installed.
+ unregister_running_hooks();
+
+ // Deinitialize native input helper functions.
+ unload_input_helper();
+ break;
+
+ case DLL_THREAD_ATTACH:
+ case DLL_THREAD_DETACH:
+ // Do Nothing.
+ break;
+ }
+
+ return TRUE;
+}
diff --git a/vendor/github.com/robotn/gohook/hook/x11/event_c.h b/vendor/github.com/robotn/gohook/hook/x11/event_c.h
new file mode 100644
index 0000000..50e5ede
--- /dev/null
+++ b/vendor/github.com/robotn/gohook/hook/x11/event_c.h
@@ -0,0 +1,386 @@
+
+#ifdef HAVE_CONFIG_H
+ #include
+#endif
+
+#include
+#include
+#include
+#include
+#include
+#ifdef USE_XTEST
+ #include
+#endif
+
+#include "../iohook.h"
+#include "input.h"
+// #include "../logger.h"
+
+extern Display *properties_disp;
+
+// This lookup table must be in the same order the masks are defined.
+#ifdef USE_XTEST
+static KeySym keymask_lookup[8] = {
+ XK_Shift_L,
+ XK_Control_L,
+ XK_Meta_L,
+ XK_Alt_L,
+
+ XK_Shift_R,
+ XK_Control_R,
+ XK_Meta_R,
+ XK_Alt_R
+};
+
+static unsigned int btnmask_lookup[5] = {
+ MASK_BUTTON1,
+ MASK_BUTTON2,
+ MASK_BUTTON3,
+ MASK_BUTTON4,
+ MASK_BUTTON5
+};
+#else
+// TODO Possibly relocate to input helper.
+static unsigned int convert_to_native_mask(unsigned int mask) {
+ unsigned int native_mask = 0x00;
+
+ if (mask & (MASK_SHIFT)) { native_mask |= ShiftMask; }
+ if (mask & (MASK_CTRL)) { native_mask |= ControlMask; }
+ if (mask & (MASK_META)) { native_mask |= Mod4Mask; }
+ if (mask & (MASK_ALT)) { native_mask |= Mod1Mask; }
+
+ if (mask & MASK_BUTTON1) { native_mask |= Button1Mask; }
+ if (mask & MASK_BUTTON2) { native_mask |= Button2Mask; }
+ if (mask & MASK_BUTTON3) { native_mask |= Button3Mask; }
+ if (mask & MASK_BUTTON4) { native_mask |= Button4Mask; }
+ if (mask & MASK_BUTTON5) { native_mask |= Button5Mask; }
+
+ return native_mask;
+}
+#endif
+
+static inline void post_key_event(iohook_event * const event) {
+ #ifdef USE_XTEST
+ // FIXME Currently ignoring EVENT_KEY_TYPED.
+ if (event->type == EVENT_KEY_PRESSED) {
+ XTestFakeKeyEvent(
+ properties_disp,
+ scancode_to_keycode(event->data.keyboard.keycode),
+ True,
+ 0);
+ }
+ else if (event->type == EVENT_KEY_RELEASED) {
+ XTestFakeKeyEvent(
+ properties_disp,
+ scancode_to_keycode(event->data.keyboard.keycode),
+ False,
+ 0);
+ }
+ #else
+ XKeyEvent key_event;
+
+ key_event.serial = 0x00;
+ key_event.send_event = False;
+ key_event.display = properties_disp;
+ key_event.time = CurrentTime;
+ key_event.same_screen = True;
+
+ unsigned int mask;
+ if (!XQueryPointer(properties_disp, DefaultRootWindow(properties_disp), &(key_event.root), &(key_event.subwindow), &(key_event.x_root), &(key_event.y_root), &(key_event.x), &(key_event.y), &mask)) {
+ key_event.root = DefaultRootWindow(properties_disp);
+ key_event.window = key_event.root;
+ key_event.subwindow = None;
+
+ key_event.x_root = 0;
+ key_event.y_root = 0;
+ key_event.x = 0;
+ key_event.y = 0;
+ }
+
+ key_event.state = convert_to_native_mask(event->mask);
+ key_event.keycode = XKeysymToKeycode(properties_disp, scancode_to_keycode(event->data.keyboard.keycode));
+
+ // FIXME Currently ignoring typed events.
+ if (event->type == EVENT_KEY_PRESSED) {
+ key_event.type = KeyPress;
+ XSendEvent(properties_disp, InputFocus, False, KeyPressMask, (XEvent *) &key_event);
+ }
+ else if (event->type == EVENT_KEY_RELEASED) {
+ key_event.type = KeyRelease;
+ XSendEvent(properties_disp, InputFocus, False, KeyReleaseMask, (XEvent *) &key_event);
+ }
+ #endif
+}
+
+static inline void post_mouse_button_event(iohook_event * const event) {
+ #ifdef USE_XTEST
+ Window ret_root;
+ Window ret_child;
+ int root_x;
+ int root_y;
+ int win_x;
+ int win_y;
+ unsigned int mask;
+
+ Window win_root = XDefaultRootWindow(properties_disp);
+ Bool query_status = XQueryPointer(properties_disp, win_root, &ret_root, &ret_child, &root_x, &root_y, &win_x, &win_y, &mask);
+ if (query_status) {
+ if (event->data.mouse.x != root_x || event->data.mouse.y != root_y) {
+ // Move the pointer to the specified position.
+ XTestFakeMotionEvent(properties_disp, -1, event->data.mouse.x, event->data.mouse.y, 0);
+ }
+ else {
+ query_status = False;
+ }
+ }
+
+ if (event->type == EVENT_MOUSE_WHEEL) {
+ // Wheel events should be the same as click events on X11.
+ // type, amount and rotation
+ if (event->data.wheel.rotation < 0) {
+ XTestFakeButtonEvent(properties_disp, WheelUp, True, 0);
+ XTestFakeButtonEvent(properties_disp, WheelUp, False, 0);
+ }
+ else {
+ XTestFakeButtonEvent(properties_disp, WheelDown, True, 0);
+ XTestFakeButtonEvent(properties_disp, WheelDown, False, 0);
+ }
+ }
+ else if (event->type == EVENT_MOUSE_PRESSED) {
+ XTestFakeButtonEvent(properties_disp, event->data.mouse.button, True, 0);
+ }
+ else if (event->type == EVENT_MOUSE_RELEASED) {
+ XTestFakeButtonEvent(properties_disp, event->data.mouse.button, False, 0);
+ }
+ else if (event->type == EVENT_MOUSE_CLICKED) {
+ XTestFakeButtonEvent(properties_disp, event->data.mouse.button, True, 0);
+ XTestFakeButtonEvent(properties_disp, event->data.mouse.button, False, 0);
+ }
+
+ if (query_status) {
+ // Move the pointer back to the original position.
+ XTestFakeMotionEvent(properties_disp, -1, root_x, root_y, 0);
+ }
+ #else
+ XButtonEvent btn_event;
+
+ btn_event.serial = 0x00;
+ btn_event.send_event = False;
+ btn_event.display = properties_disp;
+ btn_event.time = CurrentTime;
+ btn_event.same_screen = True;
+
+ btn_event.root = DefaultRootWindow(properties_disp);
+ btn_event.window = btn_event.root;
+ btn_event.subwindow = None;
+
+ btn_event.type = 0x00;
+ btn_event.state = 0x00;
+ btn_event.x_root = 0;
+ btn_event.y_root = 0;
+ btn_event.x = 0;
+ btn_event.y = 0;
+ btn_event.button = 0x00;
+
+ btn_event.state = convert_to_native_mask(event->mask);
+
+ btn_event.x = event->data.mouse.x;
+ btn_event.y = event->data.mouse.y;
+
+ #if defined(USE_XINERAMA) || defined(USE_XRANDR)
+ uint8_t screen_count;
+ screen_data *screens = hook_create_screen_info(&screen_count);
+ if (screen_count > 1) {
+ btn_event.x += screens[0].x;
+ btn_event.y += screens[0].y;
+ }
+
+ if (screens != NULL) {
+ free(screens);
+ }
+ #endif
+
+ // These are the same because Window == Root Window.
+ btn_event.x_root = btn_event.x;
+ btn_event.y_root = btn_event.y;
+
+ if (event->type == EVENT_MOUSE_WHEEL) {
+ // type, amount and rotation
+ if (event->data.wheel.rotation < 0) {
+ btn_event.button = WheelUp;
+ }
+ else {
+ btn_event.button = WheelDown;
+ }
+ }
+
+ if (event->type != EVENT_MOUSE_RELEASED) {
+ // FIXME Where do we set event->button?
+ btn_event.type = ButtonPress;
+ XSendEvent(properties_disp, InputFocus, False, ButtonPressMask, (XEvent *) &btn_event);
+ }
+
+ if (event->type != EVENT_MOUSE_PRESSED) {
+ btn_event.type = ButtonRelease;
+ XSendEvent(properties_disp, InputFocus, False, ButtonReleaseMask, (XEvent *) &btn_event);
+ }
+ #endif
+}
+
+static inline void post_mouse_motion_event(iohook_event * const event) {
+ #ifdef USE_XTEST
+ XTestFakeMotionEvent(properties_disp, -1, event->data.mouse.x, event->data.mouse.y, 0);
+ #else
+ XMotionEvent mov_event;
+
+ mov_event.serial = MotionNotify;
+ mov_event.send_event = False;
+ mov_event.display = properties_disp;
+ mov_event.time = CurrentTime;
+ mov_event.same_screen = True;
+ mov_event.is_hint = NotifyNormal,
+ mov_event.root = DefaultRootWindow(properties_disp);
+ mov_event.window = mov_event.root;
+ mov_event.subwindow = None;
+
+ mov_event.type = 0x00;
+ mov_event.state = 0x00;
+ mov_event.x_root = 0;
+ mov_event.y_root = 0;
+ mov_event.x = 0;
+ mov_event.y = 0;
+
+ mov_event.state = convert_to_native_mask(event->mask);
+
+ mov_event.x = event->data.mouse.x;
+ mov_event.y = event->data.mouse.y;
+
+ #if defined(USE_XINERAMA) || defined(USE_XRANDR)
+ uint8_t screen_count;
+ screen_data *screens = hook_create_screen_info(&screen_count);
+ if (screen_count > 1) {
+ mov_event.x += screens[0].x;
+ mov_event.y += screens[0].y;
+ }
+
+ if (screens != NULL) {
+ free(screens);
+ }
+ #endif
+
+ // These are the same because Window == Root Window.
+ mov_event.x_root = mov_event.x;
+ mov_event.y_root = mov_event.y;
+
+ long int event_mask = NoEventMask;
+ if (event->type == EVENT_MOUSE_DRAGGED) {
+ #if Button1Mask == Button1MotionMask && \
+ Button2Mask == Button2MotionMask && \
+ Button3Mask == Button3MotionMask && \
+ Button4Mask == Button4MotionMask && \
+ Button5Mask == Button5MotionMask
+ // This little trick only works if Button#MotionMasks align with
+ // the Button#Masks.
+ event_mask = mov_event.state &
+ (Button1MotionMask | Button2MotionMask |
+ Button2MotionMask | Button3MotionMask | Button5MotionMask);
+ #else
+ // Fallback to some slightly larger...
+ if (event->state & Button1Mask) {
+ event_mask |= Button1MotionMask;
+ }
+
+ if (event->state & Button2Mask) {
+ event_mask |= Button2MotionMask;
+ }
+
+ if (event->state & Button3Mask) {
+ event_mask |= Button3MotionMask;
+ }
+
+ if (event->state & Button4Mask) {
+ event_mask |= Button4MotionMask;
+ }
+
+ if (event->state & Button5Mask) {
+ event_mask |= Button5MotionMask;
+ }
+ #endif
+ }
+
+ // NOTE x_mask = NoEventMask.
+ XSendEvent(properties_disp, InputFocus, False, event_mask, (XEvent *) &mov_event);
+ #endif
+}
+
+IOHOOK_API void hook_post_event(iohook_event * const event) {
+ XLockDisplay(properties_disp);
+
+ #ifdef USE_XTEST
+ // XTest does not have modifier support, so we fake it by depressing the
+ // appropriate modifier keys.
+ unsigned int i;
+ for (i = 0; i < sizeof(keymask_lookup) / sizeof(KeySym); i++) {
+ if (event->mask & 1 << i) {
+ XTestFakeKeyEvent(properties_disp, XKeysymToKeycode(properties_disp, keymask_lookup[i]), True, 0);
+ }
+ }
+
+ unsigned int i;
+ for (i = 0; i < sizeof(btnmask_lookup) / sizeof(unsigned int); i++) {
+ if (event->mask & btnmask_lookup[i]) {
+ XTestFakeButtonEvent(properties_disp, i + 1, True, 0);
+ }
+ }
+ #endif
+
+ switch (event->type) {
+ case EVENT_KEY_PRESSED:
+ case EVENT_KEY_RELEASED:
+ case EVENT_KEY_TYPED:
+ post_key_event(event);
+ break;
+
+ case EVENT_MOUSE_PRESSED:
+ case EVENT_MOUSE_RELEASED:
+ case EVENT_MOUSE_WHEEL:
+ case EVENT_MOUSE_CLICKED:
+ post_mouse_button_event(event);
+ break;
+
+ case EVENT_MOUSE_DRAGGED:
+ case EVENT_MOUSE_MOVED:
+ post_mouse_motion_event(event);
+ break;
+
+ case EVENT_HOOK_ENABLED:
+ case EVENT_HOOK_DISABLED:
+ // Ignore hook enabled / disabled events.
+
+ default:
+ // Ignore any other garbage.
+ logger(LOG_LEVEL_WARN, "%s [%u]: Ignoring post event type %#X\n",
+ __FUNCTION__, __LINE__, event->type);
+ break;
+ }
+
+ #ifdef USE_XTEST
+ // Release the previously held modifier keys used to fake the event mask.
+ unsigned int i ;
+ for (i= 0; i < sizeof(keymask_lookup) / sizeof(KeySym); i++) {
+ if (event->mask & 1 << i) {
+ XTestFakeKeyEvent(properties_disp, XKeysymToKeycode(properties_disp, keymask_lookup[i]), False, 0);
+ }
+ }
+ unsigned int i;
+ for (i = 0; i < sizeof(btnmask_lookup) / sizeof(unsigned int); i++) {
+ if (event->mask & btnmask_lookup[i]) {
+ XTestFakeButtonEvent(properties_disp, i + 1, False, 0);
+ }
+ }
+ #endif
+
+ // Don't forget to flush!
+ XSync(properties_disp, True);
+ XUnlockDisplay(properties_disp);
+}
diff --git a/vendor/github.com/robotn/gohook/hook/x11/hook_c.h b/vendor/github.com/robotn/gohook/hook/x11/hook_c.h
new file mode 100644
index 0000000..4be4b60
--- /dev/null
+++ b/vendor/github.com/robotn/gohook/hook/x11/hook_c.h
@@ -0,0 +1,1132 @@
+
+#ifdef HAVE_CONFIG_H
+ #include
+#endif
+
+#define USE_XKB 0
+#define USE_XKBCOMMON 0
+#include
+#include
+#ifdef USE_XRECORD_ASYNC
+ #include
+#endif
+#include
+
+#include
+#include
+#include
+#include
+// #ifdef USE_XKB
+#include
+#include
+// #endif
+#if defined(USE_XINERAMA) && !defined(USE_XRANDR)
+ #include
+#elif defined(USE_XRANDR)
+ #include
+#else
+// TODO We may need to fallback to the xf86vm extension for things like TwinView.
+// #pragma message("*** Warning: Xinerama or XRandR support is required to produce cross-platform mouse coordinates for multi-head configurations!")
+// #pragma message("... Assuming single-head display.")
+#endif
+
+#include "../iohook.h"
+// #include "../logger.h"
+#include "input.h"
+
+// Thread and hook handles.
+#ifdef USE_XRECORD_ASYNC
+static bool running;
+
+static pthread_cond_t hook_xrecord_cond = PTHREAD_COND_INITIALIZER;
+static pthread_mutex_t hook_xrecord_mutex = PTHREAD_MUTEX_INITIALIZER;
+#endif
+
+typedef struct _hook_info {
+ struct _data {
+ Display *display;
+ XRecordRange *range;
+ } data;
+ struct _ctrl {
+ Display *display;
+ XRecordContext context;
+ } ctrl;
+ struct _input {
+ #ifdef USE_XKBCOMMON
+ xcb_connection_t *connection;
+ struct xkb_context *context;
+ #endif
+ uint16_t mask;
+ struct _mouse {
+ bool is_dragged;
+ struct _click {
+ unsigned short int count;
+ long int time;
+ unsigned short int button;
+ } click;
+ } mouse;
+ } input;
+} hook_info;
+static hook_info *hook;
+
+// For this struct, refer to libxnee, requires Xlibint.h
+typedef union {
+ unsigned char type;
+ xEvent event;
+ xResourceReq req;
+ xGenericReply reply;
+ xError error;
+ xConnSetupPrefix setup;
+} XRecordDatum;
+
+#if defined(USE_XKBCOMMON)
+//struct xkb_keymap *keymap;
+//struct xkb_state *state = xkb_state_new(keymap);
+static struct xkb_state *state = NULL;
+#endif
+
+// Virtual event pointer.
+static iohook_event event;
+
+// Event dispatch callback.
+static dispatcher_t dispatcher = NULL;
+
+IOHOOK_API void hook_set_dispatch_proc(dispatcher_t dispatch_proc) {
+ logger(LOG_LEVEL_DEBUG, "%s [%u]: Setting new dispatch callback to %#p.\n",
+ __FUNCTION__, __LINE__, dispatch_proc);
+
+ dispatcher = dispatch_proc;
+}
+
+// Send out an event if a dispatcher was set.
+static inline void dispatch_event(iohook_event *const event) {
+ if (dispatcher != NULL) {
+ logger(LOG_LEVEL_DEBUG, "%s [%u]: Dispatching event type %u.\n",
+ __FUNCTION__, __LINE__, event->type);
+
+ dispatcher(event);
+ }
+ else {
+ logger(LOG_LEVEL_WARN, "%s [%u]: No dispatch callback set!\n",
+ __FUNCTION__, __LINE__);
+ }
+}
+
+// Set the native modifier mask for future events.
+static inline void set_modifier_mask(uint16_t mask) {
+ hook->input.mask |= mask;
+}
+
+// Unset the native modifier mask for future events.
+static inline void unset_modifier_mask(uint16_t mask) {
+ hook->input.mask &= ~mask;
+}
+
+// Get the current native modifier mask state.
+static inline uint16_t get_modifiers() {
+ return hook->input.mask;
+}
+
+// Initialize the modifier lock masks.
+static void initialize_locks() {
+ #ifdef USE_XKBCOMMON
+
+ if (xkb_state_led_name_is_active(state, XKB_LED_NAME_CAPS)) {
+ set_modifier_mask(MASK_CAPS_LOCK);
+ }
+ else {
+ unset_modifier_mask(MASK_CAPS_LOCK);
+ }
+
+ if (xkb_state_led_name_is_active(state, XKB_LED_NAME_NUM)) {
+ set_modifier_mask(MASK_NUM_LOCK);
+ }
+ else {
+ unset_modifier_mask(MASK_NUM_LOCK);
+ }
+
+ if (xkb_state_led_name_is_active(state, XKB_LED_NAME_SCROLL)) {
+ set_modifier_mask(MASK_SCROLL_LOCK);
+ }
+ else {
+ unset_modifier_mask(MASK_SCROLL_LOCK);
+ }
+ #else
+ unsigned int led_mask = 0x00;
+ if (XkbGetIndicatorState(hook->ctrl.display, XkbUseCoreKbd, &led_mask) == Success) {
+ if (led_mask & 0x01) {
+ set_modifier_mask(MASK_CAPS_LOCK);
+ }
+ else {
+ unset_modifier_mask(MASK_CAPS_LOCK);
+ }
+
+ if (led_mask & 0x02) {
+ set_modifier_mask(MASK_NUM_LOCK);
+ }
+ else {
+ unset_modifier_mask(MASK_NUM_LOCK);
+ }
+
+ if (led_mask & 0x04) {
+ set_modifier_mask(MASK_SCROLL_LOCK);
+ }
+ else {
+ unset_modifier_mask(MASK_SCROLL_LOCK);
+ }
+ }
+ else {
+ logger(LOG_LEVEL_WARN, "%s [%u]: XkbGetIndicatorState failed to get current led mask!\n",
+ __FUNCTION__, __LINE__);
+ }
+ #endif
+}
+
+// Initialize the modifier mask to the current modifiers.
+static void initialize_modifiers() {
+ hook->input.mask = 0x0000;
+
+ KeyCode keycode;
+ char keymap[32];
+ XQueryKeymap(hook->ctrl.display, keymap);
+
+ Window unused_win;
+ int unused_int;
+ unsigned int mask;
+ if (XQueryPointer(hook->ctrl.display, DefaultRootWindow(hook->ctrl.display), &unused_win, &unused_win, &unused_int, &unused_int, &unused_int, &unused_int, &mask)) {
+ if (mask & ShiftMask) {
+ keycode = XKeysymToKeycode(hook->ctrl.display, XK_Shift_L);
+ if (keymap[keycode / 8] & (1 << (keycode % 8))) { set_modifier_mask(MASK_SHIFT_L); }
+ keycode = XKeysymToKeycode(hook->ctrl.display, XK_Shift_R);
+ if (keymap[keycode / 8] & (1 << (keycode % 8))) { set_modifier_mask(MASK_SHIFT_R); }
+ }
+ if (mask & ControlMask) {
+ keycode = XKeysymToKeycode(hook->ctrl.display, XK_Control_L);
+ if (keymap[keycode / 8] & (1 << (keycode % 8))) { set_modifier_mask(MASK_CTRL_L); }
+ keycode = XKeysymToKeycode(hook->ctrl.display, XK_Control_R);
+ if (keymap[keycode / 8] & (1 << (keycode % 8))) { set_modifier_mask(MASK_CTRL_R); }
+ }
+ if (mask & Mod1Mask) {
+ keycode = XKeysymToKeycode(hook->ctrl.display, XK_Alt_L);
+ if (keymap[keycode / 8] & (1 << (keycode % 8))) { set_modifier_mask(MASK_ALT_L); }
+ keycode = XKeysymToKeycode(hook->ctrl.display, XK_Alt_R);
+ if (keymap[keycode / 8] & (1 << (keycode % 8))) { set_modifier_mask(MASK_ALT_R); }
+ }
+ if (mask & Mod4Mask) {
+ keycode = XKeysymToKeycode(hook->ctrl.display, XK_Super_L);
+ if (keymap[keycode / 8] & (1 << (keycode % 8))) { set_modifier_mask(MASK_META_L); }
+ keycode = XKeysymToKeycode(hook->ctrl.display, XK_Super_R);
+ if (keymap[keycode / 8] & (1 << (keycode % 8))) { set_modifier_mask(MASK_META_R); }
+ }
+
+ if (mask & Button1Mask) { set_modifier_mask(MASK_BUTTON1); }
+ if (mask & Button2Mask) { set_modifier_mask(MASK_BUTTON2); }
+ if (mask & Button3Mask) { set_modifier_mask(MASK_BUTTON3); }
+ if (mask & Button4Mask) { set_modifier_mask(MASK_BUTTON4); }
+ if (mask & Button5Mask) { set_modifier_mask(MASK_BUTTON5); }
+ }
+ else {
+ logger(LOG_LEVEL_WARN, "%s [%u]: XQueryPointer failed to get current modifiers!\n",
+ __FUNCTION__, __LINE__);
+
+ keycode = XKeysymToKeycode(hook->ctrl.display, XK_Shift_L);
+ if (keymap[keycode / 8] & (1 << (keycode % 8))) { set_modifier_mask(MASK_SHIFT_L); }
+ keycode = XKeysymToKeycode(hook->ctrl.display, XK_Shift_R);
+ if (keymap[keycode / 8] & (1 << (keycode % 8))) { set_modifier_mask(MASK_SHIFT_R); }
+ keycode = XKeysymToKeycode(hook->ctrl.display, XK_Control_L);
+ if (keymap[keycode / 8] & (1 << (keycode % 8))) { set_modifier_mask(MASK_CTRL_L); }
+ keycode = XKeysymToKeycode(hook->ctrl.display, XK_Control_R);
+ if (keymap[keycode / 8] & (1 << (keycode % 8))) { set_modifier_mask(MASK_CTRL_R); }
+ keycode = XKeysymToKeycode(hook->ctrl.display, XK_Alt_L);
+ if (keymap[keycode / 8] & (1 << (keycode % 8))) { set_modifier_mask(MASK_ALT_L); }
+ keycode = XKeysymToKeycode(hook->ctrl.display, XK_Alt_R);
+ if (keymap[keycode / 8] & (1 << (keycode % 8))) { set_modifier_mask(MASK_ALT_R); }
+ keycode = XKeysymToKeycode(hook->ctrl.display, XK_Super_L);
+ if (keymap[keycode / 8] & (1 << (keycode % 8))) { set_modifier_mask(MASK_META_L); }
+ keycode = XKeysymToKeycode(hook->ctrl.display, XK_Super_R);
+ if (keymap[keycode / 8] & (1 << (keycode % 8))) { set_modifier_mask(MASK_META_R); }
+ }
+
+ initialize_locks();
+}
+
+void hook_event_proc(XPointer closeure, XRecordInterceptData *recorded_data) {
+ uint64_t timestamp = (uint64_t) recorded_data->server_time;
+
+ if (recorded_data->category == XRecordStartOfData) {
+ // Populate the hook start event.
+ event.time = timestamp;
+ event.reserved = 0x00;
+
+ event.type = EVENT_HOOK_ENABLED;
+ event.mask = 0x00;
+
+ // Fire the hook start event.
+ dispatch_event(&event);
+ }
+ else if (recorded_data->category == XRecordEndOfData) {
+ // Populate the hook stop event.
+ event.time = timestamp;
+ event.reserved = 0x00;
+
+ event.type = EVENT_HOOK_DISABLED;
+ event.mask = 0x00;
+
+ // Fire the hook stop event.
+ dispatch_event(&event);
+ }
+ else if (recorded_data->category == XRecordFromServer || recorded_data->category == XRecordFromClient) {
+ // Get XRecord data.
+ XRecordDatum *data = (XRecordDatum *) recorded_data->data;
+
+ if (data->type == KeyPress) {
+ // The X11 KeyCode associated with this event.
+ KeyCode keycode = (KeyCode) data->event.u.u.detail;
+ KeySym keysym = 0x00;
+ #if defined(USE_XKBCOMMON)
+ if (state != NULL) {
+ keysym = xkb_state_key_get_one_sym(state, keycode);
+ }
+ #else
+ keysym = keycode_to_keysym(keycode, data->event.u.keyButtonPointer.state);
+ #endif
+
+ unsigned short int scancode = keycode_to_scancode(keycode);
+
+ // TODO If you have a better suggestion for this ugly, let me know.
+ if (scancode == VC_SHIFT_L) { set_modifier_mask(MASK_SHIFT_L); }
+ else if (scancode == VC_SHIFT_R) { set_modifier_mask(MASK_SHIFT_R); }
+ else if (scancode == VC_CONTROL_L) { set_modifier_mask(MASK_CTRL_L); }
+ else if (scancode == VC_CONTROL_R) { set_modifier_mask(MASK_CTRL_R); }
+ else if (scancode == VC_ALT_L) { set_modifier_mask(MASK_ALT_L); }
+ else if (scancode == VC_ALT_R) { set_modifier_mask(MASK_ALT_R); }
+ else if (scancode == VC_META_L) { set_modifier_mask(MASK_META_L); }
+ else if (scancode == VC_META_R) { set_modifier_mask(MASK_META_R); }
+ xkb_state_update_key(state, keycode, XKB_KEY_DOWN);
+ initialize_locks();
+
+ if ((get_modifiers() & MASK_NUM_LOCK) == 0) {
+ switch (scancode) {
+ case VC_KP_SEPARATOR:
+ case VC_KP_1:
+ case VC_KP_2:
+ case VC_KP_3:
+ case VC_KP_4:
+ case VC_KP_5:
+ case VC_KP_6:
+ case VC_KP_7:
+ case VC_KP_8:
+ case VC_KP_0:
+ case VC_KP_9:
+ scancode |= 0xEE00;
+ break;
+ }
+ }
+
+ // Populate key pressed event.
+ event.time = timestamp;
+ event.reserved = 0x00;
+
+ event.type = EVENT_KEY_PRESSED;
+ event.mask = get_modifiers();
+
+ event.data.keyboard.keycode = scancode;
+ event.data.keyboard.rawcode = keysym;
+ event.data.keyboard.keychar = CHAR_UNDEFINED;
+
+ logger(LOG_LEVEL_INFO, "%s [%u]: Key %#X pressed. (%#X)\n",
+ __FUNCTION__, __LINE__, event.data.keyboard.keycode, event.data.keyboard.rawcode);
+
+ // Fire key pressed event.
+ dispatch_event(&event);
+
+ // If the pressed event was not consumed...
+ if (event.reserved ^ 0x01) {
+ uint16_t buffer[2];
+ size_t count = 0;
+
+ // Check to make sure the key is printable.
+ #ifdef USE_XKBCOMMON
+ if (state != NULL) {
+ count = keycode_to_unicode(state, keycode, buffer, sizeof(buffer) / sizeof(uint16_t));
+ }
+ #else
+ count = keysym_to_unicode(keysym, buffer, sizeof(buffer) / sizeof(uint16_t));
+ #endif
+
+ unsigned int i;
+ for (i = 0; i < count; i++) {
+ // Populate key typed event.
+ event.time = timestamp;
+ event.reserved = 0x00;
+
+ event.type = EVENT_KEY_TYPED;
+ event.mask = get_modifiers();
+
+ event.data.keyboard.keycode = VC_UNDEFINED;
+ event.data.keyboard.rawcode = keysym;
+ event.data.keyboard.keychar = buffer[i];
+
+ logger(LOG_LEVEL_INFO, "%s [%u]: Key %#X typed. (%lc)\n",
+ __FUNCTION__, __LINE__, event.data.keyboard.keycode, (uint16_t) event.data.keyboard.keychar);
+
+ // Fire key typed event.
+ dispatch_event(&event);
+ }
+ }
+ }
+ else if (data->type == KeyRelease) {
+ // The X11 KeyCode associated with this event.
+ KeyCode keycode = (KeyCode) data->event.u.u.detail;
+ KeySym keysym = 0x00;
+ #ifdef USE_XKBCOMMON
+ if (state != NULL) {
+ keysym = xkb_state_key_get_one_sym(state, keycode);
+ }
+ #else
+ keysym = keycode_to_keysym(keycode, data->event.u.keyButtonPointer.state);
+ #endif
+
+ unsigned short int scancode = keycode_to_scancode(keycode);
+
+ // TODO If you have a better suggestion for this ugly, let me know.
+ if (scancode == VC_SHIFT_L) { unset_modifier_mask(MASK_SHIFT_L); }
+ else if (scancode == VC_SHIFT_R) { unset_modifier_mask(MASK_SHIFT_R); }
+ else if (scancode == VC_CONTROL_L) { unset_modifier_mask(MASK_CTRL_L); }
+ else if (scancode == VC_CONTROL_R) { unset_modifier_mask(MASK_CTRL_R); }
+ else if (scancode == VC_ALT_L) { unset_modifier_mask(MASK_ALT_L); }
+ else if (scancode == VC_ALT_R) { unset_modifier_mask(MASK_ALT_R); }
+ else if (scancode == VC_META_L) { unset_modifier_mask(MASK_META_L); }
+ else if (scancode == VC_META_R) { unset_modifier_mask(MASK_META_R); }
+ xkb_state_update_key(state, keycode, XKB_KEY_UP);
+ initialize_locks();
+
+ if ((get_modifiers() & MASK_NUM_LOCK) == 0) {
+ switch (scancode) {
+ case VC_KP_SEPARATOR:
+ case VC_KP_1:
+ case VC_KP_2:
+ case VC_KP_3:
+ case VC_KP_4:
+ case VC_KP_5:
+ case VC_KP_6:
+ case VC_KP_7:
+ case VC_KP_8:
+ case VC_KP_0:
+ case VC_KP_9:
+ scancode |= 0xEE00;
+ break;
+ }
+ }
+
+ // Populate key released event.
+ event.time = timestamp;
+ event.reserved = 0x00;
+
+ event.type = EVENT_KEY_RELEASED;
+ event.mask = get_modifiers();
+
+ event.data.keyboard.keycode = scancode;
+ event.data.keyboard.rawcode = keysym;
+ event.data.keyboard.keychar = CHAR_UNDEFINED;
+
+ logger(LOG_LEVEL_INFO, "%s [%u]: Key %#X released. (%#X)\n",
+ __FUNCTION__, __LINE__, event.data.keyboard.keycode, event.data.keyboard.rawcode);
+
+ // Fire key released event.
+ dispatch_event(&event);
+ }
+ else if (data->type == ButtonPress) {
+ // X11 handles wheel events as button events.
+ if (data->event.u.u.detail == WheelUp || data->event.u.u.detail == WheelDown
+ || data->event.u.u.detail == WheelLeft || data->event.u.u.detail == WheelRight) {
+
+ // Reset the click count and previous button.
+ hook->input.mouse.click.count = 1;
+ hook->input.mouse.click.button = MOUSE_NOBUTTON;
+
+ /* Scroll wheel release events.
+ * Scroll type: WHEEL_UNIT_SCROLL
+ * Scroll amount: 3 unit increments per notch
+ * Units to scroll: 3 unit increments
+ * Vertical unit increment: 15 pixels
+ */
+
+ // Populate mouse wheel event.
+ event.time = timestamp;
+ event.reserved = 0x00;
+
+ event.type = EVENT_MOUSE_WHEEL;
+ event.mask = get_modifiers();
+
+ event.data.wheel.clicks = hook->input.mouse.click.count;
+ event.data.wheel.x = data->event.u.keyButtonPointer.rootX;
+ event.data.wheel.y = data->event.u.keyButtonPointer.rootY;
+
+ #if defined(USE_XINERAMA) || defined(USE_XRANDR)
+ uint8_t count;
+ screen_data *screens = hook_create_screen_info(&count);
+ if (count > 1) {
+ event.data.wheel.x -= screens[0].x;
+ event.data.wheel.y -= screens[0].y;
+ }
+
+ if (screens != NULL) {
+ free(screens);
+ }
+ #endif
+
+ /* X11 does not have an API call for acquiring the mouse scroll type. This
+ * maybe part of the XInput2 (XI2) extention but I will wont know until it
+ * is available on my platform. For the time being we will just use the
+ * unit scroll value.
+ */
+ event.data.wheel.type = WHEEL_UNIT_SCROLL;
+
+ /* Some scroll wheel properties are available via the new XInput2 (XI2)
+ * extension. Unfortunately the extension is not available on my
+ * development platform at this time. For the time being we will just
+ * use the Windows default value of 3.
+ */
+ event.data.wheel.amount = 3;
+
+ if (data->event.u.u.detail == WheelUp || data->event.u.u.detail == WheelLeft) {
+ // Wheel Rotated Up and Away.
+ event.data.wheel.rotation = -1;
+ }
+ else { // data->event.u.u.detail == WheelDown
+ // Wheel Rotated Down and Towards.
+ event.data.wheel.rotation = 1;
+ }
+
+ if (data->event.u.u.detail == WheelUp || data->event.u.u.detail == WheelDown) {
+ // Wheel Rotated Up or Down.
+ event.data.wheel.direction = WHEEL_VERTICAL_DIRECTION;
+ }
+ else { // data->event.u.u.detail == WheelLeft || data->event.u.u.detail == WheelRight
+ // Wheel Rotated Left or Right.
+ event.data.wheel.direction = WHEEL_HORIZONTAL_DIRECTION;
+ }
+
+ logger(LOG_LEVEL_INFO, "%s [%u]: Mouse wheel type %u, rotated %i units in the %u direction at %u, %u.\n",
+ __FUNCTION__, __LINE__, event.data.wheel.type,
+ event.data.wheel.amount * event.data.wheel.rotation,
+ event.data.wheel.direction,
+ event.data.wheel.x, event.data.wheel.y);
+
+ // Fire mouse wheel event.
+ dispatch_event(&event);
+ }
+ else {
+ /* This information is all static for X11, its up to the WM to
+ * decide how to interpret the wheel events.
+ */
+ uint16_t button = MOUSE_NOBUTTON;
+ switch (data->event.u.u.detail) {
+ // FIXME This should use a lookup table to handle button remapping.
+ case Button1:
+ button = MOUSE_BUTTON1;
+ set_modifier_mask(MASK_BUTTON1);
+ break;
+
+ case Button2:
+ button = MOUSE_BUTTON2;
+ set_modifier_mask(MASK_BUTTON2);
+ break;
+
+ case Button3:
+ button = MOUSE_BUTTON3;
+ set_modifier_mask(MASK_BUTTON3);
+ break;
+
+ case XButton1:
+ button = MOUSE_BUTTON4;
+ set_modifier_mask(MASK_BUTTON5);
+ break;
+
+ case XButton2:
+ button = MOUSE_BUTTON5;
+ set_modifier_mask(MASK_BUTTON5);
+ break;
+
+ default:
+ // Do not set modifier masks past button MASK_BUTTON5.
+ break;
+ }
+
+
+ // Track the number of clicks, the button must match the previous button.
+ if (button == hook->input.mouse.click.button && (long int) (timestamp - hook->input.mouse.click.time) <= hook_get_multi_click_time()) {
+ if (hook->input.mouse.click.count < USHRT_MAX) {
+ hook->input.mouse.click.count++;
+ }
+ else {
+ logger(LOG_LEVEL_WARN, "%s [%u]: Click count overflow detected!\n",
+ __FUNCTION__, __LINE__);
+ }
+ }
+ else {
+ // Reset the click count.
+ hook->input.mouse.click.count = 1;
+
+ // Set the previous button.
+ hook->input.mouse.click.button = button;
+ }
+
+ // Save this events time to calculate the hook->input.mouse.click.count.
+ hook->input.mouse.click.time = timestamp;
+
+
+ // Populate mouse pressed event.
+ event.time = timestamp;
+ event.reserved = 0x00;
+
+ event.type = EVENT_MOUSE_PRESSED;
+ event.mask = get_modifiers();
+
+ event.data.mouse.button = button;
+ event.data.mouse.clicks = hook->input.mouse.click.count;
+ event.data.mouse.x = data->event.u.keyButtonPointer.rootX;
+ event.data.mouse.y = data->event.u.keyButtonPointer.rootY;
+
+ #if defined(USE_XINERAMA) || defined(USE_XRANDR)
+ uint8_t count;
+ screen_data *screens = hook_create_screen_info(&count);
+ if (count > 1) {
+ event.data.mouse.x -= screens[0].x;
+ event.data.mouse.y -= screens[0].y;
+ }
+
+ if (screens != NULL) {
+ free(screens);
+ }
+ #endif
+
+ logger(LOG_LEVEL_INFO, "%s [%u]: Button %u pressed %u time(s). (%u, %u)\n",
+ __FUNCTION__, __LINE__, event.data.mouse.button, event.data.mouse.clicks,
+ event.data.mouse.x, event.data.mouse.y);
+
+ // Fire mouse pressed event.
+ dispatch_event(&event);
+ }
+ }
+ else if (data->type == ButtonRelease) {
+ // X11 handles wheel events as button events.
+ if (data->event.u.u.detail != WheelUp && data->event.u.u.detail != WheelDown) {
+ /* This information is all static for X11, its up to the WM to
+ * decide how to interpret the wheel events.
+ */
+ uint16_t button = MOUSE_NOBUTTON;
+ switch (data->event.u.u.detail) {
+ // FIXME This should use a lookup table to handle button remapping.
+ case Button1:
+ button = MOUSE_BUTTON1;
+ unset_modifier_mask(MASK_BUTTON1);
+ break;
+
+ case Button2:
+ button = MOUSE_BUTTON2;
+ unset_modifier_mask(MASK_BUTTON2);
+ break;
+
+ case Button3:
+ button = MOUSE_BUTTON3;
+ unset_modifier_mask(MASK_BUTTON3);
+ break;
+
+ case XButton1:
+ button = MOUSE_BUTTON4;
+ unset_modifier_mask(MASK_BUTTON5);
+ break;
+
+ case XButton2:
+ button = MOUSE_BUTTON5;
+ unset_modifier_mask(MASK_BUTTON5);
+ break;
+
+ default:
+ // Do not set modifier masks past button MASK_BUTTON5.
+ break;
+ }
+
+ // Populate mouse released event.
+ event.time = timestamp;
+ event.reserved = 0x00;
+
+ event.type = EVENT_MOUSE_RELEASED;
+ event.mask = get_modifiers();
+
+ event.data.mouse.button = button;
+ event.data.mouse.clicks = hook->input.mouse.click.count;
+ event.data.mouse.x = data->event.u.keyButtonPointer.rootX;
+ event.data.mouse.y = data->event.u.keyButtonPointer.rootY;
+
+ #if defined(USE_XINERAMA) || defined(USE_XRANDR)
+ uint8_t count;
+ screen_data *screens = hook_create_screen_info(&count);
+ if (count > 1) {
+ event.data.mouse.x -= screens[0].x;
+ event.data.mouse.y -= screens[0].y;
+ }
+
+ if (screens != NULL) {
+ free(screens);
+ }
+ #endif
+
+ logger(LOG_LEVEL_INFO, "%s [%u]: Button %u released %u time(s). (%u, %u)\n",
+ __FUNCTION__, __LINE__, event.data.mouse.button,
+ event.data.mouse.clicks,
+ event.data.mouse.x, event.data.mouse.y);
+
+ // Fire mouse released event.
+ dispatch_event(&event);
+
+ // If the pressed event was not consumed...
+ if (event.reserved ^ 0x01 && hook->input.mouse.is_dragged != true) {
+ // Populate mouse clicked event.
+ event.time = timestamp;
+ event.reserved = 0x00;
+
+ event.type = EVENT_MOUSE_CLICKED;
+ event.mask = get_modifiers();
+
+ event.data.mouse.button = button;
+ event.data.mouse.clicks = hook->input.mouse.click.count;
+ event.data.mouse.x = data->event.u.keyButtonPointer.rootX;
+ event.data.mouse.y = data->event.u.keyButtonPointer.rootY;
+
+ #if defined(USE_XINERAMA) || defined(USE_XRANDR)
+ uint8_t count;
+ screen_data *screens = hook_create_screen_info(&count);
+ if (count > 1) {
+ event.data.mouse.x -= screens[0].x;
+ event.data.mouse.y -= screens[0].y;
+ }
+
+ if (screens != NULL) {
+ free(screens);
+ }
+ #endif
+
+ logger(LOG_LEVEL_INFO, "%s [%u]: Button %u clicked %u time(s). (%u, %u)\n",
+ __FUNCTION__, __LINE__, event.data.mouse.button,
+ event.data.mouse.clicks,
+ event.data.mouse.x, event.data.mouse.y);
+
+ // Fire mouse clicked event.
+ dispatch_event(&event);
+ }
+
+ // Reset the number of clicks.
+ if (button == hook->input.mouse.click.button && (long int) (event.time - hook->input.mouse.click.time) > hook_get_multi_click_time()) {
+ // Reset the click count.
+ hook->input.mouse.click.count = 0;
+ }
+ }
+ }
+ else if (data->type == MotionNotify) {
+ // Reset the click count.
+ if (hook->input.mouse.click.count != 0 && (long int) (timestamp - hook->input.mouse.click.time) > hook_get_multi_click_time()) {
+ hook->input.mouse.click.count = 0;
+ }
+
+ // Populate mouse move event.
+ event.time = timestamp;
+ event.reserved = 0x00;
+
+ event.mask = get_modifiers();
+
+ // Check the upper half of virtual modifiers for non-zero
+ // values and set the mouse dragged flag.
+ hook->input.mouse.is_dragged = (event.mask >> 8 > 0);
+ if (hook->input.mouse.is_dragged) {
+ // Create Mouse Dragged event.
+ event.type = EVENT_MOUSE_DRAGGED;
+ }
+ else {
+ // Create a Mouse Moved event.
+ event.type = EVENT_MOUSE_MOVED;
+ }
+
+ event.data.mouse.button = MOUSE_NOBUTTON;
+ event.data.mouse.clicks = hook->input.mouse.click.count;
+ event.data.mouse.x = data->event.u.keyButtonPointer.rootX;
+ event.data.mouse.y = data->event.u.keyButtonPointer.rootY;
+
+ #if defined(USE_XINERAMA) || defined(USE_XRANDR)
+ uint8_t count;
+ screen_data *screens = hook_create_screen_info(&count);
+ if (count > 1) {
+ event.data.mouse.x -= screens[0].x;
+ event.data.mouse.y -= screens[0].y;
+ }
+
+ if (screens != NULL) {
+ free(screens);
+ }
+ #endif
+
+ logger(LOG_LEVEL_INFO, "%s [%u]: Mouse %s to %i, %i. (%#X)\n",
+ __FUNCTION__, __LINE__, hook->input.mouse.is_dragged ? "dragged" : "moved",
+ event.data.mouse.x, event.data.mouse.y, event.mask);
+
+ // Fire mouse move event.
+ dispatch_event(&event);
+ }
+ else {
+ // In theory this *should* never execute.
+ logger(LOG_LEVEL_DEBUG, "%s [%u]: Unhandled X11 event: %#X.\n",
+ __FUNCTION__, __LINE__, (unsigned int) data->type);
+ }
+ }
+ else {
+ logger(LOG_LEVEL_WARN, "%s [%u]: Unhandled X11 hook category! (%#X)\n",
+ __FUNCTION__, __LINE__, recorded_data->category);
+ }
+
+ // TODO There is no way to consume the XRecord event.
+
+ XRecordFreeData(recorded_data);
+}
+
+
+static inline bool enable_key_repeate() {
+ // Attempt to setup detectable autorepeat.
+ // NOTE: is_auto_repeat is NOT stdbool!
+ Bool is_auto_repeat = False;
+ #ifdef USE_XKB
+ // Enable detectable auto-repeat.
+ XkbSetDetectableAutoRepeat(hook->ctrl.display, True, &is_auto_repeat);
+ #else
+ XAutoRepeatOn(hook->ctrl.display);
+
+ XKeyboardState kb_state;
+ XGetKeyboardControl(hook->ctrl.display, &kb_state);
+
+ is_auto_repeat = (kb_state.global_auto_repeat == AutoRepeatModeOn);
+ #endif
+
+ return is_auto_repeat;
+}
+
+
+static inline int xrecord_block() {
+ int status = IOHOOK_FAILURE;
+
+ // Save the data display associated with this hook so it is passed to each event.
+ //XPointer closeure = (XPointer) (ctrl_display);
+ XPointer closeure = NULL;
+
+ #ifdef USE_XRECORD_ASYNC
+ // Async requires that we loop so that our thread does not return.
+ if (XRecordEnableContextAsync(hook->data.display, context, hook_event_proc, closeure) != 0) {
+ // Time in MS to sleep the runloop.
+ int timesleep = 100;
+
+ // Allow the thread loop to block.
+ pthread_mutex_lock(&hook_xrecord_mutex);
+ running = true;
+
+ do {
+ // Unlock the mutex from the previous iteration.
+ pthread_mutex_unlock(&hook_xrecord_mutex);
+
+ XRecordProcessReplies(hook->data.display);
+
+ // Prevent 100% CPU utilization.
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+
+ struct timespec ts;
+ ts.tv_sec = time(NULL) + timesleep / 1000;
+ ts.tv_nsec = tv.tv_usec * 1000 + 1000 * 1000 * (timesleep % 1000);
+ ts.tv_sec += ts.tv_nsec / (1000 * 1000 * 1000);
+ ts.tv_nsec %= (1000 * 1000 * 1000);
+
+ pthread_mutex_lock(&hook_xrecord_mutex);
+ pthread_cond_timedwait(&hook_xrecord_cond, &hook_xrecord_mutex, &ts);
+ } while (running);
+
+ // Unlock after loop exit.
+ pthread_mutex_unlock(&hook_xrecord_mutex);
+
+ // Set the exit status.
+ status = NULL;
+ }
+ #else
+ // Sync blocks until XRecordDisableContext() is called.
+ if (XRecordEnableContext(hook->data.display, hook->ctrl.context, hook_event_proc, closeure) != 0) {
+ status = IOHOOK_SUCCESS;
+ }
+ #endif
+ else {
+ logger(LOG_LEVEL_ERROR, "%s [%u]: XRecordEnableContext failure!\n",
+ __FUNCTION__, __LINE__);
+
+ #ifdef USE_XRECORD_ASYNC
+ // Reset the running state.
+ pthread_mutex_lock(&hook_xrecord_mutex);
+ running = false;
+ pthread_mutex_unlock(&hook_xrecord_mutex);
+ #endif
+
+ // Set the exit status.
+ status = IOHOOK_ERROR_X_RECORD_ENABLE_CONTEXT;
+ }
+
+ return status;
+}
+
+static int xrecord_alloc() {
+ int status = IOHOOK_FAILURE;
+
+ // Make sure the data display is synchronized to prevent late event delivery!
+ // See Bug 42356 for more information.
+ // https://bugs.freedesktop.org/show_bug.cgi?id=42356#c4
+ XSynchronize(hook->data.display, True);
+
+ // Setup XRecord range.
+ XRecordClientSpec clients = XRecordAllClients;
+
+ hook->data.range = XRecordAllocRange();
+ if (hook->data.range != NULL) {
+ logger(LOG_LEVEL_DEBUG, "%s [%u]: XRecordAllocRange successful.\n",
+ __FUNCTION__, __LINE__);
+
+ hook->data.range->device_events.first = KeyPress;
+ hook->data.range->device_events.last = MotionNotify;
+
+ // Note that the documentation for this function is incorrect,
+ // hook->data.display should be used!
+ // See: http://www.x.org/releases/X11R7.6/doc/libXtst/recordlib.txt
+ hook->ctrl.context = XRecordCreateContext(hook->data.display, XRecordFromServerTime, &clients, 1, &hook->data.range, 1);
+ if (hook->ctrl.context != 0) {
+ logger(LOG_LEVEL_DEBUG, "%s [%u]: XRecordCreateContext successful.\n",
+ __FUNCTION__, __LINE__);
+
+ // Block until hook_stop() is called.
+ status = xrecord_block();
+
+ // Free up the context if it was set.
+ XRecordFreeContext(hook->data.display, hook->ctrl.context);
+ }
+ else {
+ logger(LOG_LEVEL_ERROR, "%s [%u]: XRecordCreateContext failure!\n",
+ __FUNCTION__, __LINE__);
+
+ // Set the exit status.
+ status = IOHOOK_ERROR_X_RECORD_CREATE_CONTEXT;
+ }
+
+ // Free the XRecord range.
+ XFree(hook->data.range);
+ }
+ else {
+ logger(LOG_LEVEL_ERROR, "%s [%u]: XRecordAllocRange failure!\n",
+ __FUNCTION__, __LINE__);
+
+ // Set the exit status.
+ status = IOHOOK_ERROR_X_RECORD_ALLOC_RANGE;
+ }
+
+ return status;
+}
+
+static int xrecord_query() {
+ int status = IOHOOK_FAILURE;
+
+ // Check to make sure XRecord is installed and enabled.
+ int major, minor;
+ if (XRecordQueryVersion(hook->ctrl.display, &major, &minor) != 0) {
+ logger(LOG_LEVEL_INFO, "%s [%u]: XRecord version: %i.%i.\n",
+ __FUNCTION__, __LINE__, major, minor);
+
+ status = xrecord_alloc();
+ }
+ else {
+ logger(LOG_LEVEL_ERROR, "%s [%u]: XRecord is not currently available!\n",
+ __FUNCTION__, __LINE__);
+
+ status = IOHOOK_ERROR_X_RECORD_NOT_FOUND;
+ }
+
+ return status;
+}
+
+static int xrecord_start() {
+ int status = IOHOOK_FAILURE;
+
+ // Open the control display for XRecord.
+ hook->ctrl.display = XOpenDisplay(NULL);
+
+ // Open a data display for XRecord.
+ // NOTE This display must be opened on the same thread as XRecord.
+ hook->data.display = XOpenDisplay(NULL);
+ if (hook->ctrl.display != NULL && hook->data.display != NULL) {
+ logger(LOG_LEVEL_DEBUG, "%s [%u]: XOpenDisplay successful.\n",
+ __FUNCTION__, __LINE__);
+
+ bool is_auto_repeat = enable_key_repeate();
+ if (is_auto_repeat) {
+ logger(LOG_LEVEL_DEBUG, "%s [%u]: Successfully enabled detectable autorepeat.\n",
+ __FUNCTION__, __LINE__);
+ }
+ else {
+ logger(LOG_LEVEL_WARN, "%s [%u]: Could not enable detectable auto-repeat!\n",
+ __FUNCTION__, __LINE__);
+ }
+
+ #if defined(USE_XKBCOMMON)
+ // Open XCB Connection
+ hook->input.connection = XGetXCBConnection(hook->ctrl.display);
+ int xcb_status = xcb_connection_has_error(hook->input.connection);
+ if (xcb_status <= 0) {
+ // Initialize xkbcommon context.
+ struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
+
+ if (context != NULL) {
+ hook->input.context = xkb_context_ref(context);
+ }
+ else {
+ logger(LOG_LEVEL_ERROR, "%s [%u]: xkb_context_new failure!\n",
+ __FUNCTION__, __LINE__);
+ }
+ }
+ else {
+ logger(LOG_LEVEL_ERROR, "%s [%u]: xcb_connect failure! (%d)\n",
+ __FUNCTION__, __LINE__, xcb_status);
+ }
+ #endif
+
+ #ifdef USE_XKBCOMMON
+ state = create_xkb_state(hook->input.context, hook->input.connection);
+ #endif
+
+ // Initialize starting modifiers.
+ initialize_modifiers();
+
+ status = xrecord_query();
+
+ #ifdef USE_XKBCOMMON
+ if (state != NULL) {
+ destroy_xkb_state(state);
+ }
+
+ if (hook->input.context != NULL) {
+ xkb_context_unref(hook->input.context);
+ hook->input.context = NULL;
+ }
+
+ if (hook->input.connection != NULL) {
+ // xcb_disconnect(hook->input.connection);
+ hook->input.connection = NULL;
+ }
+ #endif
+ }
+ else {
+ logger(LOG_LEVEL_ERROR, "%s [%u]: XOpenDisplay failure!\n",
+ __FUNCTION__, __LINE__);
+
+ status = IOHOOK_ERROR_X_OPEN_DISPLAY;
+ }
+
+ // Close down the XRecord data display.
+ if (hook->data.display != NULL) {
+ XCloseDisplay(hook->data.display);
+ hook->data.display = NULL;
+ }
+
+ // Close down the XRecord control display.
+ if (hook->ctrl.display) {
+ XCloseDisplay(hook->ctrl.display);
+ hook->ctrl.display = NULL;
+ }
+
+ return status;
+}
+
+IOHOOK_API int hook_run() {
+ int status = IOHOOK_FAILURE;
+
+ // Hook data for future cleanup.
+ hook = malloc(sizeof(hook_info));
+ if (hook != NULL) {
+ hook->input.mask = 0x0000;
+ hook->input.mouse.is_dragged = false;
+ hook->input.mouse.click.count = 0;
+ hook->input.mouse.click.time = 0;
+ hook->input.mouse.click.button = MOUSE_NOBUTTON;
+
+ status = xrecord_start();
+
+ // Free data associated with this hook.
+ free(hook);
+ hook = NULL;
+ }
+ else {
+ logger(LOG_LEVEL_ERROR, "%s [%u]: Failed to allocate memory for hook structure!\n",
+ __FUNCTION__, __LINE__);
+
+ status = IOHOOK_ERROR_OUT_OF_MEMORY;
+ }
+
+ logger(LOG_LEVEL_DEBUG, "%s [%u]: Something, something, something, complete.\n",
+ __FUNCTION__, __LINE__);
+
+ return status;
+}
+
+IOHOOK_API int hook_stop() {
+ int status = IOHOOK_FAILURE;
+
+ if (hook != NULL && hook->ctrl.display != NULL && hook->ctrl.context != 0) {
+ // We need to make sure the context is still valid.
+ XRecordState *state = malloc(sizeof(XRecordState));
+ if (state != NULL) {
+ if (XRecordGetContext(hook->ctrl.display, hook->ctrl.context, &state) != 0) {
+ // Try to exit the thread naturally.
+ if (state->enabled && XRecordDisableContext(hook->ctrl.display, hook->ctrl.context) != 0) {
+ #ifdef USE_XRECORD_ASYNC
+ pthread_mutex_lock(&hook_xrecord_mutex);
+ running = false;
+ pthread_cond_signal(&hook_xrecord_cond);
+ pthread_mutex_unlock(&hook_xrecord_mutex);
+ #endif
+
+ // See Bug 42356 for more information.
+ // https://bugs.freedesktop.org/show_bug.cgi?id=42356#c4
+ //XFlush(hook->ctrl.display);
+ XSync(hook->ctrl.display, False);
+ if (hook->ctrl.display) {
+ XCloseDisplay(hook->ctrl.display);
+ hook->ctrl.display = NULL;
+ }
+
+ status = IOHOOK_SUCCESS;
+ }
+ }
+ else {
+ logger(LOG_LEVEL_ERROR, "%s [%u]: XRecordGetContext failure!\n",
+ __FUNCTION__, __LINE__);
+
+ status = IOHOOK_ERROR_X_RECORD_GET_CONTEXT;
+ }
+
+ free(state);
+ }
+ else {
+ logger(LOG_LEVEL_ERROR, "%s [%u]: Failed to allocate memory for XRecordState!\n",
+ __FUNCTION__, __LINE__);
+
+ status = IOHOOK_ERROR_OUT_OF_MEMORY;
+ }
+
+ return status;
+ }
+
+ logger(LOG_LEVEL_DEBUG, "%s [%u]: Status: %#X.\n",
+ __FUNCTION__, __LINE__, status);
+
+ return status;
+}
diff --git a/vendor/github.com/robotn/gohook/hook/x11/input.h b/vendor/github.com/robotn/gohook/hook/x11/input.h
new file mode 100644
index 0000000..905983f
--- /dev/null
+++ b/vendor/github.com/robotn/gohook/hook/x11/input.h
@@ -0,0 +1,86 @@
+
+#define USE_XKBCOMMON 0
+//#define _included_input_helper 0
+#ifndef _included_input_helper
+#define _included_input_helper
+
+#include
+#include
+
+#ifdef USE_XKBCOMMON
+ #include
+ #include
+ #include
+#endif
+
+
+// Virtual button codes that are not defined by X11.
+#define Button1 1
+#define Button2 2
+#define Button3 3
+#define WheelUp 4
+#define WheelDown 5
+#define WheelLeft 6
+#define WheelRight 7
+#define XButton1 8
+#define XButton2 9
+
+/* Converts an X11 key symbol to a single Unicode character. No direct X11
+ * functionality exists to provide this information.
+ */
+extern size_t keysym_to_unicode(KeySym keysym, uint16_t *buffer, size_t size);
+
+/* Convert a single Unicode character to an X11 key symbol. This function
+ * provides a better translation than XStringToKeysym() for Unicode characters.
+ */
+extern KeySym unicode_to_keysym(uint16_t unicode);
+
+/* Converts an X11 key code to the appropriate keyboard scan code.
+ */
+extern uint16_t keycode_to_scancode(KeyCode keycode);
+
+/* Converts a keyboard scan code to the appropriate X11 key code.
+ */
+extern KeyCode scancode_to_keycode(uint16_t scancode);
+
+
+#ifdef USE_XKBCOMMON
+
+/* Converts an X11 key code to a Unicode character sequence. libXKBCommon support
+ * is required for this method.
+ */
+extern size_t keycode_to_unicode(struct xkb_state* state, KeyCode keycode, uint16_t *buffer, size_t size);
+
+/* Create a xkb_state structure and return a pointer to it.
+ */
+extern struct xkb_state * create_xkb_state(struct xkb_context *context, xcb_connection_t *connection);
+
+/* Release xkb_state structure created by create_xkb_state().
+ */
+extern void destroy_xkb_state(struct xkb_state* state);
+
+#else
+
+/* Converts an X11 key code and event mask to the appropriate X11 key symbol.
+ * This functions in much the same way as XKeycodeToKeysym() but allows for a
+ * faster and more flexible lookup.
+ */
+extern KeySym keycode_to_keysym(KeyCode keycode, unsigned int modifier_mask);
+
+#endif
+
+/* Initialize items required for KeyCodeToKeySym() and KeySymToUnicode()
+ * functionality. This method is called by OnLibraryLoad() and may need to be
+ * called in combination with UnloadInputHelper() if the native keyboard layout
+ * is changed.
+ */
+extern void load_input_helper();
+
+/* De-initialize items required for KeyCodeToKeySym() and KeySymToUnicode()
+ * functionality. This method is called by OnLibraryUnload() and may need to be
+ * called in combination with LoadInputHelper() if the native keyboard layout
+ * is changed.
+ */
+extern void unload_input_helper();
+
+#endif
diff --git a/vendor/github.com/robotn/gohook/hook/x11/input_c.h b/vendor/github.com/robotn/gohook/hook/x11/input_c.h
new file mode 100644
index 0000000..8d7f39d
--- /dev/null
+++ b/vendor/github.com/robotn/gohook/hook/x11/input_c.h
@@ -0,0 +1,1970 @@
+
+#ifdef HAVE_CONFIG_H
+#include
+#endif
+
+#define USE_XKB 0
+#define USE_XKBCOMMON 0
+#include
+#include
+#include
+#include
+#include
+#include
+
+#ifdef USE_XKB
+#ifdef USE_EVDEV
+ #include
+ static bool is_evdev = false;
+#endif
+
+#include
+static XkbDescPtr keyboard_map;
+#else
+ #include
+ static KeySym *keyboard_map;
+ static int keysym_per_keycode;
+ static bool is_caps_lock = false, is_shift_lock = false;
+#endif
+
+#ifdef USE_XKBCOMMON
+#include
+#include
+#include
+
+#ifdef USE_XKBFILE
+#include
+
+static struct xkb_rule_names xkb_names = {
+ .rules = "base",
+ .model = "us",
+ .layout = "pc105",
+ .variant = NULL,
+ .options = NULL
+};
+#endif
+
+#endif
+
+#include "../logger_c.h"
+
+/* The follwoing two tables are based on QEMU's x_keymap.c, under the following
+ * terms:
+ *
+ * Copyright (C) 2003 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#if defined(USE_EVDEV) && defined(USE_XKB)
+/* This table is generated based off the evdev -> scancode mapping above
+ * and the keycode mappings in the following files:
+ * /usr/include/linux/input.h
+ * /usr/share/X11/xkb/keycodes/evdev
+ *
+ * NOTE This table only works for Linux.
+ */
+static const uint16_t evdev_scancode_table[][2] = {
+ /* idx { keycode, scancode }, idx evdev code */
+ /* 0 */ { VC_UNDEFINED, 0x00 }, /* 0x00 KEY_RESERVED */
+ /* 1 */ { VC_UNDEFINED, 0x09 }, /* 0x01 KEY_ESC */
+ /* 2 */ { VC_UNDEFINED, 0x0A }, /* 0x02 KEY_1 */
+ /* 3 */ { VC_UNDEFINED, 0x0B }, /* 0x03 KEY_2 */
+ /* 4 */ { VC_UNDEFINED, 0x0C }, /* 0x04 KEY_3 */
+ /* 5 */ { VC_UNDEFINED, 0x0D }, /* 0x05 KEY_4 */
+ /* 6 */ { VC_UNDEFINED, 0x0E }, /* 0x06 KEY_5 */
+ /* 7 */ { VC_UNDEFINED, 0x0F }, /* 0x07 KEY_6 */
+ /* 8 */ { VC_UNDEFINED, 0x10 }, /* 0x08 KEY_7 */
+ /* 9 */ { VC_ESCAPE, 0x11 }, /* 0x09 KEY_8 */
+ /* 10 */ { VC_1, 0x12 }, /* 0x0A KEY_9 */
+ /* 11 */ { VC_2, 0x13 }, /* 0x0B KEY_0 */
+ /* 12 */ { VC_3, 0x14 }, /* 0x0C KEY_MINUS */
+ /* 13 */ { VC_4, 0x15 }, /* 0x0D KEY_EQUAL */
+ /* 14 */ { VC_5, 0x16 }, /* 0x0E KEY_BACKSPACE */
+ /* 15 */ { VC_6, 0x17 }, /* 0x0F KEY_TAB */
+ /* 16 */ { VC_7, 0x18 }, /* 0x10 KEY_Q */
+ /* 17 */ { VC_8, 0x19 }, /* 0x11 KEY_W */
+ /* 18 */ { VC_9, 0x1A }, /* 0x12 KEY_E */
+ /* 19 */ { VC_0, 0x1B }, /* 0x13 KEY_T */
+ /* 20 */ { VC_MINUS, 0x1C }, /* 0x14 KEY_R */
+ /* 21 */ { VC_EQUALS, 0x1D }, /* 0x15 KEY_Y */
+ /* 22 */ { VC_BACKSPACE, 0x1E }, /* 0x16 KEY_U */
+ /* 23 */ { VC_TAB, 0x1F }, /* 0x17 KEY_I */
+ /* 24 */ { VC_Q, 0x20 }, /* 0x18 KEY_O */
+ /* 25 */ { VC_W, 0x21 }, /* 0x19 KEY_P */
+ /* 26 */ { VC_E, 0x22 }, /* 0x1A KEY_LEFTBRACE */
+ /* 27 */ { VC_R, 0x23 }, /* 0x1B KEY_RIGHTBRACE */
+ /* 28 */ { VC_T, 0x24 }, /* 0x1C KEY_ENTER */
+ /* 29 */ { VC_Y, 0x25 }, /* 0x1D KEY_LEFTCTRL */
+ /* 30 */ { VC_U, 0x26 }, /* 0x1E KEY_A */
+ /* 31 */ { VC_I, 0x27 }, /* 0x1F KEY_S */
+ /* 32 */ { VC_O, 0x28 }, /* 0x20 KEY_D */
+ /* 33 */ { VC_P, 0x29 }, /* 0x21 KEY_F */
+ /* 34 */ { VC_OPEN_BRACKET, 0x2A }, /* 0x22 KEY_G */
+ /* 35 */ { VC_CLOSE_BRACKET, 0x2B }, /* 0x23 KEY_H */
+ /* 36 */ { VC_ENTER, 0x2C }, /* 0x24 KEY_J */
+ /* 37 */ { VC_CONTROL_L, 0x2D }, /* 0x25 KEY_K */
+ /* 38 */ { VC_A, 0x2E }, /* 0x26 KEY_L */
+ /* 39 */ { VC_S, 0x2F }, /* 0x27 KEY_SEMICOLON */
+ /* 40 */ { VC_D, 0x30 }, /* 0x28 KEY_APOSTROPHE */
+ /* 41 */ { VC_F, 0x31 }, /* 0x29 KEY_GRAVE */
+ /* 42 */ { VC_G, 0x32 }, /* 0x2A KEY_LEFTSHIFT */
+ /* 43 */ { VC_H, 0x33 }, /* 0x2B KEY_BACKSLASH */
+ /* 44 */ { VC_J, 0x34 }, /* 0x2C KEY_Z */
+ /* 45 */ { VC_K, 0x35 }, /* 0x2D KEY_X */
+ /* 46 */ { VC_L, 0x36 }, /* 0x2E KEY_C */
+ /* 47 */ { VC_SEMICOLON, 0x37 }, /* 0x2F KEY_V */
+ /* 48 */ { VC_QUOTE, 0x38 }, /* 0x30 KEY_B */
+ /* 49 */ { VC_BACKQUOTE, 0x39 }, /* 0x31 KEY_N */
+ /* 50 */ { VC_SHIFT_L, 0x3A }, /* 0x32 KEY_M */
+ /* 51 */ { VC_BACK_SLASH, 0x3B }, /* 0x33 KEY_COMMA */
+ /* 52 */ { VC_Z, 0x3C }, /* 0x34 KEY_DOT */
+ /* 53 */ { VC_X, 0x3D }, /* 0x35 KEY_SLASH */
+ /* 54 */ { VC_C, 0x3E }, /* 0x36 KEY_RIGHTSHIFT */
+ /* 55 */ { VC_V, 0x3F }, /* 0x37 KEY_KPASTERISK */
+ /* 56 */ { VC_B, 0x40 }, /* 0x38 KEY_LEFTALT */
+ /* 57 */ { VC_N, 0x41 }, /* 0x39 KEY_SPACE */
+ /* 58 */ { VC_M, 0x42 }, /* 0x3A KEY_CAPSLOCK */
+ /* 59 */ { VC_COMMA, 0x43 }, /* 0x3B KEY_F1 */
+ /* 60 */ { VC_PERIOD, 0x44 }, /* 0x3C KEY_F2 */
+ /* 61 */ { VC_SLASH, 0x45 }, /* 0x3D KEY_F3 */
+ /* 62 */ { VC_SHIFT_R, 0x46 }, /* 0x3E KEY_F4 */
+ /* 63 */ { VC_KP_MULTIPLY, 0x47 }, /* 0x3F KEY_F5 */
+ /* 64 */ { VC_ALT_L, 0x48 }, /* 0x40 KEY_F6 */
+ /* 65 */ { VC_SPACE, 0x49 }, /* 0x41 KEY_F7 */
+ /* 66 */ { VC_CAPS_LOCK, 0x4A }, /* 0x42 KEY_F8 */
+ /* 67 */ { VC_F1, 0x4B }, /* 0x43 KEY_F9 */
+ /* 68 */ { VC_F2, 0x4C }, /* 0x44 KEY_F10 */
+ /* 69 */ { VC_F3, 0x4D }, /* 0x45 KEY_NUMLOCK */
+ /* 70 */ { VC_F4, 0x4E }, /* 0x46 KEY_SCROLLLOCK */
+ /* 71 */ { VC_F5, 0x4F }, /* 0x47 KEY_KP7 */
+ /* 72 */ { VC_F6, 0x50 }, /* 0x48 KEY_KP8 */
+ /* 73 */ { VC_F7, 0x51 }, /* 0x49 KEY_KP9 */
+ /* 74 */ { VC_F8, 0x52 }, /* 0x4A KEY_KPMINUS */
+ /* 75 */ { VC_F9, 0x53 }, /* 0x4B KEY_KP4 */
+ /* 76 */ { VC_F10, 0x54 }, /* 0x4C KEY_KP5 */
+ /* 77 */ { VC_NUM_LOCK, 0x55 }, /* 0x4D KEY_KP6 */
+ /* 78 */ { VC_SCROLL_LOCK, 0x56 }, /* 0x4E KEY_KPPLUS */
+ /* 79 */ { VC_KP_7, 0x57 }, /* 0x4F KEY_KP1 */
+ /* 80 */ { VC_KP_8, 0x58 }, /* 0x50 KEY_KP2 */
+ /* 81 */ { VC_KP_9, 0x59 }, /* 0x51 KEY_KP3 */
+ /* 82 */ { VC_KP_SUBTRACT, 0x5A }, /* 0x52 KEY_KP0 */
+ /* 83 */ { VC_KP_4, 0x5B }, /* 0x53 KEY_KPDOT */
+ /* 84 */ { VC_KP_5, 0x00 }, /* 0x54 */
+ /* 85 */ { VC_KP_6, 0x00 }, /* 0x55 TODO [KEY_ZENKAKUHANKAKU][0] == [VC_?][1] */
+ /* 86 */ { VC_KP_ADD, 0x00 }, /* 0x56 TODO [KEY_102ND][0] == [VC_?][1] */
+ /* 87 */ { VC_KP_1, 0x5F }, /* 0x57 KEY_F11 */
+ /* 88 */ { VC_KP_2, 0x60 }, /* 0x58 KEY_F12 */
+ /* 89 */ { VC_KP_3, 0x00 }, /* 0x59 TODO [KEY_RO][0] == [VC_?][1] */
+ /* 90 */ { VC_KP_0, 0x00 }, /* 0x5A */
+ /* 91 */ { VC_KP_SEPARATOR, 0xBF }, /* 0x5B KEY_F13 */
+ /* 92 */ { VC_UNDEFINED, 0xC0 }, /* 0x5C KEY_F14 */
+ /* 93 */ { VC_UNDEFINED, 0xC1 }, /* 0x5D KEY_F15 */
+ /* 94 */ { VC_UNDEFINED, 0x00 }, /* 0x5E TODO [KEY_MUHENKAN][0] == [VC_?][1] */
+ /* 95 */ { VC_F11, 0x00 }, /* 0x5F */
+ /* 96 */ { VC_F12, 0x00 }, /* 0x60 */
+
+ /* First 97 chars are identical to XFree86! */
+
+ /* 97 */ { VC_UNDEFINED, 0x00 }, /* 0x61 */
+ /* 98 */ { VC_KATAKANA, 0x00 }, /* 0x62 */
+ /* 99 */ { VC_HIRAGANA, 0xC2 }, /* 0x63 KEY_F16 */
+ /* 100 */ { VC_KANJI, 0xC3 }, /* 0x64 KEY_F17 */
+ /* 101 */ { VC_UNDEFINED, 0xC4 }, /* 0x65 KEY_F18 */
+ /* 102 */ { VC_UNDEFINED, 0xC5 }, /* 0x66 KEY_F19 */
+ /* 103 */ { VC_KP_COMMA, 0xC6 }, /* 0x67 KEY_F20 */
+ /* 104 */ { VC_KP_ENTER, 0xC7 }, /* 0x68 KEY_F21 */
+ /* 105 */ { VC_CONTROL_R, 0xC8 }, /* 0x69 KEY_F22 */
+ /* 106 */ { VC_KP_DIVIDE, 0xC9 }, /* 0x6A KEY_F23 */
+ /* 107 */ { VC_PRINTSCREEN, 0xCA }, /* 0x6B KEY_F24 */
+ /* 108 */ { VC_ALT_R, 0x00 }, /* 0x6C */
+ /* 109 */ { VC_UNDEFINED, 0x00 }, /* 0x6D */
+ /* 110 */ { VC_HOME, 0x00 }, /* 0x6E */
+ /* 111 */ { VC_UP, 0x00 }, /* 0x6F */
+ /* 112 */ { VC_PAGE_UP, 0x62 }, /* 0x70 KEY_KATAKANA */
+ /* 113 */ { VC_LEFT, 0x00 }, /* 0x71 */
+ /* 114 */ { VC_RIGHT, 0x00 }, /* 0x72 */
+ /* 115 */ { VC_END, 0x00 }, /* 0x73 TODO KEY_? = [VC_UNDERSCORE][1] */
+ /* 116 */ { VC_DOWN, 0x00 }, /* 0x74 TODO KEY_? = [VC_FURIGANA][1] */
+ /* 117 */ { VC_PAGE_DOWN, 0x00 }, /* 0x75 */
+ /* 118 */ { VC_INSERT, 0x00 }, /* 0x76 TODO [KEY_KPPLUSMINUS][0] = [VC_?][1] */
+ /* 119 */ { VC_DELETE, 0x00 }, /* 0x77 */
+ /* 120 */ { VC_UNDEFINED, 0x00 }, /* 0x78 TODO [KEY_SCALE][0] = [VC_?][1] */
+ /* 121 */ { VC_VOLUME_MUTE, 0x64 }, /* 0x79 KEY_HENKAN */
+ /* 122 */ { VC_VOLUME_DOWN, 0x00 }, /* 0x7A */
+ /* 123 */ { VC_VOLUME_UP, 0x63 }, /* 0x7B KEY_HIRAGANA */
+ /* 124 */ { VC_POWER, 0x00 }, /* 0x7C */
+ /* 125 */ { VC_KP_EQUALS, 0x84 }, /* 0x7D KEY_YEN */
+ /* 126 */ { VC_UNDEFINED, 0x67 }, /* 0x7E KEY_KPJPCOMMA */
+ /* 127 */ { VC_PAUSE, 0x00 }, /* 0x7F */
+
+ /* No Offset Offset (i & 0x007F) + 128 */
+
+ /* 128 */ { VC_UNDEFINED, 0 }, /* 0x80 */
+ /* 129 */ { VC_UNDEFINED, 0 }, /* 0x81 */
+ /* 130 */ { VC_UNDEFINED, 0 }, /* 0x82 */
+ /* 131 */ { VC_UNDEFINED, 0 }, /* 0x83 */
+ /* 132 */ { VC_YEN, 0 }, /* 0x84 */
+ /* 133 */ { VC_META_L, 0 }, /* 0x85 */
+ /* 134 */ { VC_META_R, 0 }, /* 0x86 */
+ /* 135 */ { VC_CONTEXT_MENU, 0 }, /* 0x87 */
+ /* 136 */ { VC_SUN_STOP, 0 }, /* 0x88 */
+ /* 137 */ { VC_SUN_AGAIN, 0 }, /* 0x89 */
+ /* 138 */ { VC_SUN_PROPS, 0 }, /* 0x8A */
+ /* 139 */ { VC_SUN_UNDO, 0 }, /* 0x8B */
+ /* 140 */ { VC_SUN_FRONT, 0 }, /* 0x8C */
+ /* 141 */ { VC_SUN_COPY, 0x7D }, /* 0x8D KEY_KPEQUAL */
+ /* 142 */ { VC_SUN_OPEN, 0 }, /* 0x8E */
+ /* 143 */ { VC_SUN_INSERT, 0 }, /* 0x8F */
+ /* 144 */ { VC_SUN_FIND, 0 }, /* 0x90 */
+ /* 145 */ { VC_SUN_CUT, 0 }, /* 0x91 */
+ /* 146 */ { VC_SUN_HELP, 0 }, /* 0x92 */
+ /* 147 */ { VC_UNDEFINED, 0 }, /* 0x93 */
+ /* 148 */ { VC_APP_CALCULATOR, 0 }, /* 0x94 */
+ /* 149 */ { VC_UNDEFINED, 0 }, /* 0x95 */
+ /* 150 */ { VC_SLEEP, 0 }, /* 0x96 */
+ /* 151 */ { VC_UNDEFINED, 0 }, /* 0x97 */
+ /* 152 */ { VC_UNDEFINED, 0 }, /* 0x98 */
+ /* 153 */ { VC_UNDEFINED, 0 }, /* 0x99 */
+ /* 154 */ { VC_UNDEFINED, 0 }, /* 0x9A */
+ /* 155 */ { VC_UNDEFINED, 0 }, /* 0x9B */
+ /* 156 */ { VC_UNDEFINED, 0x68 }, /* 0x9C KEY_KPENTER */
+ /* 157 */ { VC_UNDEFINED, 0x69 }, /* 0x9D KEY_RIGHTCTRL */
+ /* 158 */ { VC_UNDEFINED, 0 }, /* 0x9E */
+ /* 159 */ { VC_UNDEFINED, 0 }, /* 0x9F */
+ /* 160 */ { VC_UNDEFINED, 0x79 }, /* 0xA0 KEY_MUTE */
+ /* 161 */ { VC_UNDEFINED, 0x94 }, /* 0xA1 KEY_CALC */
+ /* 162 */ { VC_UNDEFINED, 0xA7 }, /* 0xA2 KEY_FORWARD */
+ /* 163 */ { VC_UNDEFINED, 0 }, /* 0xA3 */
+ /* 164 */ { VC_UNDEFINED, 0 }, /* 0xA4 */
+ /* 165 */ { VC_UNDEFINED, 0 }, /* 0xA5 */
+ /* 166 */ { VC_APP_MAIL, 0 }, /* 0xA6 */
+ /* 167 */ { VC_MEDIA_PLAY, 0 }, /* 0xA7 */
+ /* 168 */ { VC_UNDEFINED, 0 }, /* 0xA8 */
+ /* 169 */ { VC_UNDEFINED, 0 }, /* 0xA9 */
+ /* 170 */ { VC_UNDEFINED, 0 }, /* 0xAA */
+ /* 171 */ { VC_UNDEFINED, 0 }, /* 0xAB */
+ /* 172 */ { VC_UNDEFINED, 0 }, /* 0xAC */
+ /* 173 */ { VC_UNDEFINED, 0 }, /* 0xAD */
+ /* 174 */ { VC_UNDEFINED, 0x7A }, /* 0xAE KEY_VOLUMEDOWN */
+ /* 175 */ { VC_UNDEFINED, 0 }, /* 0xAF */
+ /* 176 */ { VC_UNDEFINED, 0x7B }, /* 0xB0 KEY_VOLUMEUP */
+ /* 177 */ { VC_UNDEFINED, 0x00 }, /* 0xB1 */
+ /* 178 */ { VC_UNDEFINED, 0xBA }, /* 0xB2 KEY_SCROLLUP */
+ /* 179 */ { VC_UNDEFINED, 0x00 }, /* 0xB3 */
+ /* 180 */ { VC_UNDEFINED, 0x00 }, /* 0xB4 */
+ /* 181 */ { VC_UNDEFINED, 0x6A }, /* 0xB5 KEY_KPSLASH */
+ /* 182 */ { VC_UNDEFINED, 0x00 }, /* 0xB6 */
+ /* 183 */ { VC_UNDEFINED, 0x6B }, /* 0xB7 KEY_SYSRQ */
+ /* 184 */ { VC_UNDEFINED, 0x6C }, /* 0xB8 KEY_RIGHTALT */
+ /* 185 */ { VC_UNDEFINED, 0x00 }, /* 0xB9 */
+ /* 186 */ { VC_BROWSER_HOME, 0x00 }, /* 0xBA */
+ /* 187 */ { VC_UNDEFINED, 0x00 }, /* 0xBB */
+ /* 188 */ { VC_UNDEFINED, 0x00 }, /* 0xBC */
+ /* 189 */ { VC_UNDEFINED, 0x00 }, /* 0xBD */
+ /* 190 */ { VC_UNDEFINED, 0x00 }, /* 0xBE */
+ /* 191 */ { VC_F13, 0x00 }, /* 0xBF */
+ /* 192 */ { VC_F14, 0x00 }, /* 0xC0 */
+ /* 193 */ { VC_F15, 0x00 }, /* 0xC1 */
+ /* 194 */ { VC_F16, 0x00 }, /* 0xC2 */
+ /* 195 */ { VC_F17, 0x00 }, /* 0xC3 */
+ /* 196 */ { VC_F18, 0x00 }, /* 0xC4 */
+ /* 197 */ { VC_F19, 0x7F }, /* 0xC5 KEY_PAUSE */
+ /* 198 */ { VC_F20, 0x00 }, /* 0xC6 */
+ /* 199 */ { VC_F21, 0x6E }, /* 0xC7 KEY_HOME */
+ /* 200 */ { VC_F22, 0x6F }, /* 0xC8 KEY_UP */
+ /* 201 */ { VC_F23, 0x70 }, /* 0xC9 KEY_PAGEUP */
+ /* 202 */ { VC_F24, 0x00 }, /* 0xCA */
+ /* 203 */ { VC_UNDEFINED, 0x71 }, /* 0xCB KEY_LEFT */
+ /* 204 */ { VC_UNDEFINED, 0x00 }, /* 0xCC */
+ /* 205 */ { VC_UNDEFINED, 0x72 }, /* 0xCD KEY_RIGHT */
+ /* 206 */ { VC_UNDEFINED, 0x00 }, /* 0xCE */
+ /* 207 */ { VC_UNDEFINED, 0x73 }, /* 0xCF KEY_END */
+ /* 208 */ { VC_UNDEFINED, 0x74 }, /* 0xD0 KEY_DOWN */
+ /* 209 */ { VC_UNDEFINED, 0x75 }, /* 0xD1 KEY_PAGEDOWN */
+ /* 210 */ { VC_UNDEFINED, 0x76 }, /* 0xD2 KEY_INSERT */
+ /* 211 */ { VC_UNDEFINED, 0x77 }, /* 0xD3 KEY_DELETE */
+ /* 212 */ { VC_UNDEFINED, 0x00 }, /* 0xD4 */
+ /* 213 */ { VC_UNDEFINED, 0x00 }, /* 0xD5 */
+ /* 214 */ { VC_UNDEFINED, 0x00 }, /* 0xD6 */
+ /* 215 */ { VC_UNDEFINED, 0x00 }, /* 0xD7 */
+ /* 216 */ { VC_UNDEFINED, 0x00 }, /* 0xD8 */
+ /* 217 */ { VC_UNDEFINED, 0x00 }, /* 0xD9 */
+ /* 218 */ { VC_UNDEFINED, 0x00 }, /* 0xDA */
+ /* 219 */ { VC_UNDEFINED, 0x85 }, /* 0xDB KEY_LEFTMETA */
+ /* 220 */ { VC_UNDEFINED, 0x86 }, /* 0xDC KEY_RIGHTMETA */
+ /* 221 */ { VC_UNDEFINED, 0x87 }, /* 0xDD KEY_COMPOSE */
+ /* 222 */ { VC_UNDEFINED, 0x7C }, /* 0xDE KEY_POWER */
+ /* 223 */ { VC_UNDEFINED, 0x96 }, /* 0xDF KEY_SLEEP */
+ /* 224 */ { VC_UNDEFINED, 0x00 }, /* 0xE0 */
+ /* 225 */ { VC_BROWSER_SEARCH, 0x00 }, /* 0xE1 */
+ /* 226 */ { VC_UNDEFINED, 0x00 }, /* 0xE2 */
+ /* 227 */ { VC_UNDEFINED, 0x00 }, /* 0xE3 */
+ /* 228 */ { VC_UNDEFINED, 0x00 }, /* 0xE4 */
+ /* 229 */ { VC_UNDEFINED, 0xE1 }, /* 0xE5 KEY_SEARCH */
+ /* 230 */ { VC_UNDEFINED, 0x00 }, /* 0xE6 */
+ /* 231 */ { VC_UNDEFINED, 0x00 }, /* 0xE7 */
+ /* 232 */ { VC_UNDEFINED, 0x00 }, /* 0xE8 */
+ /* 233 */ { VC_UNDEFINED, 0x00 }, /* 0xE9 */
+ /* 234 */ { VC_UNDEFINED, 0x00 }, /* 0xEA */
+ /* 235 */ { VC_UNDEFINED, 0x00 }, /* 0xEB */
+ /* 236 */ { VC_UNDEFINED, 0xA6 }, /* 0xEC KEY_BACK */
+ /* 237 */ { VC_UNDEFINED, 0x00 }, /* 0xED */
+ /* 238 */ { VC_UNDEFINED, 0x00 }, /* 0xEE */
+ /* 239 */ { VC_UNDEFINED, 0x00 }, /* 0xEF */
+ /* 240 */ { VC_UNDEFINED, 0x00 }, /* 0xF0 */
+ /* 241 */ { VC_UNDEFINED, 0x00 }, /* 0xF1 */
+ /* 242 */ { VC_UNDEFINED, 0x00 }, /* 0xF2 */
+ /* 243 */ { VC_UNDEFINED, 0x00 }, /* 0xF3 */
+ /* 244 */ { VC_UNDEFINED, 0x8E }, /* 0xF4 KEY_OPEN */
+ /* 245 */ { VC_UNDEFINED, 0x92 }, /* 0xF5 KEY_HELP */
+ /* 246 */ { VC_UNDEFINED, 0x8A }, /* 0xF6 KEY_PROPS */
+ /* 247 */ { VC_UNDEFINED, 0x8C }, /* 0xF7 KEY_FRONT */
+ /* 248 */ { VC_UNDEFINED, 0x88 }, /* 0xF8 KEY_STOP */
+ /* 249 */ { VC_UNDEFINED, 0x89 }, /* 0xF9 KEY_AGAIN */
+ /* 250 */ { VC_UNDEFINED, 0x8B }, /* 0xFA KEY_UNDO */
+ /* 251 */ { VC_UNDEFINED, 0x91 }, /* 0xFB KEY_CUT */
+ /* 252 */ { VC_UNDEFINED, 0x8D }, /* 0xFC KEY_COPY */
+ /* 253 */ { VC_UNDEFINED, 0x8F }, /* 0xFD KEY_PASTE */
+ /* 254 */ { VC_UNDEFINED, 0x90 }, /* 0xFE KEY_FIND */
+ /* 255 */ { VC_UNDEFINED, 0x00 }, /* 0xFF */
+};
+#endif
+
+
+/* This table is generated based off the xfree86 -> scancode mapping above
+ * and the keycode mappings in the following files:
+ * /usr/share/X11/xkb/keycodes/xfree86
+ *
+ * TODO Everything after 157 needs to be populated with scancodes for media
+ * controls and internet keyboards.
+ */
+static const uint16_t xfree86_scancode_table[][2] = {
+ /* idx { keycode, scancode }, */
+ /* 0 */ { VC_UNDEFINED, 0 /* */ }, // Unused
+ /* 1 */ { VC_UNDEFINED, 9 /* */ }, //
+ /* 2 */ { VC_UNDEFINED, 10 /* */ }, //
+ /* 3 */ { VC_UNDEFINED, 11 /* */ }, //
+ /* 4 */ { VC_UNDEFINED, 12 /* */ }, //
+ /* 5 */ { VC_UNDEFINED, 13 /* */ }, //
+ /* 6 */ { VC_UNDEFINED, 14 /* */ }, //
+ /* 7 */ { VC_UNDEFINED, 15 /* */ }, //
+ /* 8 */ { VC_UNDEFINED, 16 /* */ }, //
+ /* 9 */ { VC_ESCAPE, 17 /* */ }, //
+ /* 10 */ { VC_1, 18 /* */ }, //
+ /* 11 */ { VC_2, 19 /* */ }, //
+ /* 12 */ { VC_3, 20 /* */ }, //
+ /* 13 */ { VC_4, 21 /* */ }, //
+ /* 14 */ { VC_5, 22 /* */ }, //
+ /* 15 */ { VC_6, 23 /* */ }, //
+ /* 16 */ { VC_7, 24 /* */ }, //
+ /* 17 */ { VC_8, 25 /* */ }, //
+ /* 18 */ { VC_9, 26 /* */ }, //
+ /* 19 */ { VC_0, 27 /* */ }, //
+ /* 20 */ { VC_MINUS, 28 /* */ }, //
+ /* 21 */ { VC_EQUALS, 29 /* */ }, //
+ /* 22 */ { VC_BACKSPACE, 30 /* */ }, //
+ /* 23 */ { VC_TAB, 31 /* */ }, //
+ /* 24 */ { VC_Q, 32 /* */ }, //
+ /* 25 */ { VC_W, 33 /* */ }, //
+ /* 26 */ { VC_E, 34 /* */ }, //
+ /* 27 */ { VC_R, 35 /* */ }, //
+ /* 28 */ { VC_T, 36 /* */ }, //
+ /* 29 */ { VC_Y, 37 /* */ }, //
+ /* 30 */ { VC_U, 38 /* */ }, //
+ /* 31 */ { VC_I, 39 /* */ }, //
+ /* 32 */ { VC_O, 40 /* */ }, //
+ /* 33 */ { VC_P, 41 /* */ }, //
+ /* 34 */ { VC_OPEN_BRACKET, 42 /* */ }, //
+ /* 35 */ { VC_CLOSE_BRACKET, 43 /* */ }, //
+ /* 36 */ { VC_ENTER, 44 /* */ }, //
+ /* 37 */ { VC_CONTROL_L, 45 /* */ }, //
+ /* 38 */ { VC_A, 46 /* */ }, //
+ /* 39 */ { VC_S, 47 /* */ }, //
+ /* 40 */ { VC_D, 48 /* */ }, //
+ /* 41 */ { VC_F, 49 /* */ }, //
+ /* 42 */ { VC_G, 50 /* */ }, //
+ /* 43 */ { VC_H, 51 /* */ }, //
+ /* 44 */ { VC_J, 52 /* */ }, //
+ /* 45 */ { VC_K, 53 /* */ }, //
+ /* 46 */ { VC_L, 54 /* */ }, //
+ /* 47 */ { VC_SEMICOLON, 55 /* */ }, //
+ /* 48 */ { VC_QUOTE, 56 /* */ }, //
+ /* 49 */ { VC_BACKQUOTE, 57 /* */ }, //
+ /* 50 */ { VC_SHIFT_L, 58 /* */ }, //
+ /* 51 */ { VC_BACK_SLASH, 59 /* */ }, //
+ /* 52 */ { VC_Z, 60 /* */ }, //
+ /* 53 */ { VC_X, 61 /* */ }, //
+ /* 54 */ { VC_C, 62 /* */ }, //
+ /* 55 */ { VC_V, 63 /* */ }, //
+ /* 56 */ { VC_B, 64 /* */ }, //
+ /* 57 */ { VC_N, 65 /* */ }, //
+ /* 58 */ { VC_M, 66 /* */ }, //
+ /* 59 */ { VC_COMMA, 67 /* */ }, //
+ /* 60 */ { VC_PERIOD, 68 /* */ }, //
+ /* 61 */ { VC_SLASH, 69 /* */ }, //
+ /* 62 */ { VC_SHIFT_R, 70 /* */ }, //
+ /* 63 */ { VC_KP_MULTIPLY, 71 /* */ }, //
+ /* 64 */ { VC_ALT_L, 72 /* */ }, //
+ /* 65 */ { VC_SPACE, 73 /* */ }, //
+ /* 66 */ { VC_CAPS_LOCK, 74 /* */ }, //
+ /* 67 */ { VC_F1, 75 /* */ }, //
+ /* 68 */ { VC_F2, 76 /* */ }, //
+ /* 69 */ { VC_F3, 77 /* */ }, //
+ /* 70 */ { VC_F4, 78 /* */ }, //
+ /* 71 */ { VC_F5, 79 /* */ }, //
+ /* 72 */ { VC_F6, 80 /* */ }, //
+ /* 73 */ { VC_F7, 81 /* */ }, //
+ /* 74 */ { VC_F8, 82 /* */ }, //
+ /* 75 */ { VC_F9, 83 /* */ }, //
+ /* 76 */ { VC_F10, 84 /* */ }, //
+ /* 77 */ { VC_NUM_LOCK, 85 /* */ }, //
+ /* 78 */ { VC_SCROLL_LOCK, 86 /* */ }, //
+ /* 79 */ { VC_KP_7, 87 /* */ }, //
+ /* 80 */ { VC_KP_8, 88 /* */ }, //
+ /* 81 */ { VC_KP_9, 89 /* */ }, //
+ /* 82 */ { VC_KP_SUBTRACT, 90 /* */ }, //
+ /* 83 */ { VC_KP_4, 91 /* */ }, //
+ /* 84 */ { VC_KP_5, 0 }, //
+ /* 85 */ { VC_KP_6, 0 }, //
+ /* 86 */ { VC_KP_ADD, 0 }, //
+ /* 87 */ { VC_KP_1, 95 /* */ }, //
+ /* 88 */ { VC_KP_2, 96 /* */ },
+ /* 89 */ { VC_KP_3, 0 },
+ /* 90 */ { VC_KP_0, 0 },
+ /* 91 */ { VC_KP_SEPARATOR, 118 /* */ },
+ /* 92 */ { VC_UNDEFINED, 119 /* */ },
+ /* 93 */ { VC_UNDEFINED, 120 /* */ },
+ /* 94 */ { VC_UNDEFINED, 0 },
+ /* 95 */ { VC_F11, 0 },
+ /* 96 */ { VC_F12, 0 },
+
+ /* First 97 chars are identical to XFree86! */
+
+ /* 97 */ { VC_HOME, 0 },
+ /* 98 */ { VC_UP, 0 },
+ /* 99 */ { VC_PAGE_UP, 121 /* */ },
+ /* 100 */ { VC_LEFT, 122 /* */ },
+ /* 101 */ { VC_UNDEFINED, 0 }, // TODO lower brightness key?
+ /* 102 */ { VC_RIGHT, 0 },
+ /* 103 */ { VC_END, 0 },
+ /* 104 */ { VC_DOWN, 0 },
+ /* 105 */ { VC_PAGE_DOWN, 0 },
+ /* 106 */ { VC_INSERT, 0 },
+ /* 107 */ { VC_DELETE, 0 },
+ /* 108 */ { VC_KP_ENTER, 0 },
+ /* 109 */ { VC_CONTROL_R, 0 },
+ /* 110 */ { VC_PAUSE, 0 },
+ /* 111 */ { VC_PRINTSCREEN, 0 },
+ /* 112 */ { VC_KP_DIVIDE, 0 },
+ /* 113 */ { VC_ALT_R, 0 },
+ /* 114 */ { VC_UNDEFINED, 0 }, // VC_BREAK?
+ /* 115 */ { VC_META_L, 0 },
+ /* 116 */ { VC_META_R, 0 },
+ /* 117 */ { VC_CONTEXT_MENU, 0 },
+ /* 118 */ { VC_F13, 0 },
+ /* 119 */ { VC_F14, 0 },
+ /* 120 */ { VC_F15, 0 },
+ /* 121 */ { VC_F16, 0 },
+ /* 122 */ { VC_F17, 0 },
+ /* 123 */ { VC_UNDEFINED, 0 }, // FIXME What is this key?
+ /* 124 */ { VC_UNDEFINED, 0 }, // Never Generated
+ /* 125 */ { VC_UNDEFINED, 133 /* */ }, // Never Generated
+ /* 126 */ { VC_KP_EQUALS, 0 },
+ /* 127 */ { VC_UNDEFINED, 0 }, // Never Generated
+ /* 128 */ { VC_UNDEFINED, 0 }, // Never Generated
+ /* 129 */ { VC_UNDEFINED, 0 }, // Henkan
+ /* 130 */ { VC_UNDEFINED, 0 }, // Some extended Internet key
+ /* 131 */ { VC_UNDEFINED, 0 }, // Muhenkan
+ /* 132 */ { VC_UNDEFINED, 0 }, //
+ /* 133 */ { VC_YEN, 0 }, //
+ /* 134 */ { VC_UNDEFINED, 0 }, //
+ /* 135 */ { VC_UNDEFINED, 0 }, //
+ /* 136 */ { VC_UNDEFINED, 0 }, //
+ /* 137 */ { VC_UNDEFINED, 0 }, //
+ /* 138 */ { VC_UNDEFINED, 0 }, //
+ /* 139 */ { VC_UNDEFINED, 0 }, //
+ /* 140 */ { VC_UNDEFINED, 0 }, //
+ /* 141 */ { VC_UNDEFINED, 126 }, //
+ /* 142 */ { VC_UNDEFINED, 0 }, //
+ /* 143 */ { VC_UNDEFINED, 0 }, //
+ /* 144 */ { VC_UNDEFINED, 0 }, //
+ /* 145 */ { VC_UNDEFINED, 0 }, //
+ /* 146 */ { VC_UNDEFINED, 0 }, //
+ /* 147 */ { VC_UNDEFINED, 0 }, //
+ /* 148 */ { VC_UNDEFINED, 0 }, //
+ /* 149 */ { VC_UNDEFINED, 0 }, //
+ /* 150 */ { VC_UNDEFINED, 0 }, //
+ /* 151 */ { VC_UNDEFINED, 0 }, //
+ /* 152 */ { VC_UNDEFINED, 0 }, //
+ /* 153 */ { VC_UNDEFINED, 0 }, //
+ /* 154 */ { VC_UNDEFINED, 0 }, //
+ /* 155 */ { VC_UNDEFINED, 0 }, //
+ /* 156 */ { VC_UNDEFINED, 108 /* */ }, // Never Generated
+ /* 157 */ { VC_UNDEFINED, 109 /* */ }, //
+ /* 158 */ { VC_UNDEFINED, 0 }, //
+ /* 159 */ { VC_UNDEFINED, 0 }, //
+ /* 160 */ { VC_UNDEFINED, 0 }, //
+ /* 161 */ { VC_UNDEFINED, 0 }, //
+ /* 162 */ { VC_UNDEFINED, 0 }, //
+ /* 163 */ { VC_UNDEFINED, 0 }, //
+ /* 164 */ { VC_UNDEFINED, 0 }, //
+ /* 165 */ { VC_UNDEFINED, 0 }, //
+ /* 166 */ { VC_UNDEFINED, 0 }, //
+ /* 167 */ { VC_UNDEFINED, 0 }, //
+ /* 168 */ { VC_UNDEFINED, 0 }, //
+ /* 169 */ { VC_UNDEFINED, 0 }, //
+ /* 170 */ { VC_UNDEFINED, 0 }, //
+ /* 171 */ { VC_UNDEFINED, 0 }, //
+ /* 172 */ { VC_UNDEFINED, 0 }, //
+ /* 173 */ { VC_UNDEFINED, 0 }, //
+ /* 174 */ { VC_UNDEFINED, 0 }, //
+ /* 175 */ { VC_UNDEFINED, 0 }, //
+ /* 176 */ { VC_UNDEFINED, 0 }, //