├── .gitignore
├── LICENSE.txt
├── MANIFEST.in
├── README.md
├── examples
├── clickonacci.py
├── filetranscriber.py
├── mouse.py
└── mouselogger
├── pykeyboard
├── __init__.py
├── base.py
├── java_.py
├── mac.py
├── mir.py
├── wayland.py
├── windows.py
├── x11.py
└── x11_keysyms.py
├── pymouse
├── __init__.py
├── base.py
├── java_.py
├── license_notice.txt
├── mac.py
├── mir.py
├── wayland.py
├── windows.py
└── x11.py
├── reference_materials
├── virtual_keystroke_example.py
├── windows_VK.txt
├── winex.py
├── xlib-keysyms-to-python.py
└── xlib-keysyms.txt
├── setup.py
└── tests
├── basic.py
├── test_import_times.py
└── test_unix.py
/.gitignore:
--------------------------------------------------------------------------------
1 | *.py[cod]
2 | *~
3 |
4 | # C extensions
5 | *.so
6 |
7 | # Packages
8 | *.egg
9 | *.egg-info
10 | dist
11 | build
12 | eggs
13 | parts
14 | bin
15 | var
16 | sdist
17 | develop-eggs
18 | .installed.cfg
19 | lib
20 | lib64
21 | MANIFEST
22 |
23 | # Installer logs
24 | pip-log.txt
25 |
26 | # Unit test / coverage reports
27 | .coverage
28 | .tox
29 | nosetests.xml
30 |
31 | # Translations
32 | *.mo
33 |
34 | # Mr Developer
35 | .mr.developer.cfg
36 | .project
37 | .pydevproject
38 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU General Public License is a free, copyleft license for
11 | software and other kinds of works.
12 |
13 | The licenses for most software and other practical works are designed
14 | to take away your freedom to share and change the works. By contrast,
15 | the GNU General Public License is intended to guarantee your freedom to
16 | share and change all versions of a program--to make sure it remains free
17 | software for all its users. We, the Free Software Foundation, use the
18 | GNU General Public License for most of our software; it applies also to
19 | any other work released this way by its authors. You can apply it to
20 | your programs, too.
21 |
22 | When we speak of free software, we are referring to freedom, not
23 | price. Our General Public Licenses are designed to make sure that you
24 | have the freedom to distribute copies of free software (and charge for
25 | them if you wish), that you receive source code or can get it if you
26 | want it, that you can change the software or use pieces of it in new
27 | free programs, and that you know you can do these things.
28 |
29 | To protect your rights, we need to prevent others from denying you
30 | these rights or asking you to surrender the rights. Therefore, you have
31 | certain responsibilities if you distribute copies of the software, or if
32 | you modify it: responsibilities to respect the freedom of others.
33 |
34 | For example, if you distribute copies of such a program, whether
35 | gratis or for a fee, you must pass on to the recipients the same
36 | freedoms that you received. You must make sure that they, too, receive
37 | or can get the source code. And you must show them these terms so they
38 | know their rights.
39 |
40 | Developers that use the GNU GPL protect your rights with two steps:
41 | (1) assert copyright on the software, and (2) offer you this License
42 | giving you legal permission to copy, distribute and/or modify it.
43 |
44 | For the developers' and authors' protection, the GPL clearly explains
45 | that there is no warranty for this free software. For both users' and
46 | authors' sake, the GPL requires that modified versions be marked as
47 | changed, so that their problems will not be attributed erroneously to
48 | authors of previous versions.
49 |
50 | Some devices are designed to deny users access to install or run
51 | modified versions of the software inside them, although the manufacturer
52 | can do so. This is fundamentally incompatible with the aim of
53 | protecting users' freedom to change the software. The systematic
54 | pattern of such abuse occurs in the area of products for individuals to
55 | use, which is precisely where it is most unacceptable. Therefore, we
56 | have designed this version of the GPL to prohibit the practice for those
57 | products. If such problems arise substantially in other domains, we
58 | stand ready to extend this provision to those domains in future versions
59 | of the GPL, as needed to protect the freedom of users.
60 |
61 | Finally, every program is threatened constantly by software patents.
62 | States should not allow patents to restrict development and use of
63 | software on general-purpose computers, but in those that do, we wish to
64 | avoid the special danger that patents applied to a free program could
65 | make it effectively proprietary. To prevent this, the GPL assures that
66 | patents cannot be used to render the program non-free.
67 |
68 | The precise terms and conditions for copying, distribution and
69 | modification follow.
70 |
71 | TERMS AND CONDITIONS
72 |
73 | 0. Definitions.
74 |
75 | "This License" refers to version 3 of the GNU General Public License.
76 |
77 | "Copyright" also means copyright-like laws that apply to other kinds of
78 | works, such as semiconductor masks.
79 |
80 | "The Program" refers to any copyrightable work licensed under this
81 | License. Each licensee is addressed as "you". "Licensees" and
82 | "recipients" may be individuals or organizations.
83 |
84 | To "modify" a work means to copy from or adapt all or part of the work
85 | in a fashion requiring copyright permission, other than the making of an
86 | exact copy. The resulting work is called a "modified version" of the
87 | earlier work or a work "based on" the earlier work.
88 |
89 | A "covered work" means either the unmodified Program or a work based
90 | on the Program.
91 |
92 | To "propagate" a work means to do anything with it that, without
93 | permission, would make you directly or secondarily liable for
94 | infringement under applicable copyright law, except executing it on a
95 | computer or modifying a private copy. Propagation includes copying,
96 | distribution (with or without modification), making available to the
97 | public, and in some countries other activities as well.
98 |
99 | To "convey" a work means any kind of propagation that enables other
100 | parties to make or receive copies. Mere interaction with a user through
101 | a computer network, with no transfer of a copy, is not conveying.
102 |
103 | An interactive user interface displays "Appropriate Legal Notices"
104 | to the extent that it includes a convenient and prominently visible
105 | feature that (1) displays an appropriate copyright notice, and (2)
106 | tells the user that there is no warranty for the work (except to the
107 | extent that warranties are provided), that licensees may convey the
108 | work under this License, and how to view a copy of this License. If
109 | the interface presents a list of user commands or options, such as a
110 | menu, a prominent item in the list meets this criterion.
111 |
112 | 1. Source Code.
113 |
114 | The "source code" for a work means the preferred form of the work
115 | for making modifications to it. "Object code" means any non-source
116 | form of a work.
117 |
118 | A "Standard Interface" means an interface that either is an official
119 | standard defined by a recognized standards body, or, in the case of
120 | interfaces specified for a particular programming language, one that
121 | is widely used among developers working in that language.
122 |
123 | The "System Libraries" of an executable work include anything, other
124 | than the work as a whole, that (a) is included in the normal form of
125 | packaging a Major Component, but which is not part of that Major
126 | Component, and (b) serves only to enable use of the work with that
127 | Major Component, or to implement a Standard Interface for which an
128 | implementation is available to the public in source code form. A
129 | "Major Component", in this context, means a major essential component
130 | (kernel, window system, and so on) of the specific operating system
131 | (if any) on which the executable work runs, or a compiler used to
132 | produce the work, or an object code interpreter used to run it.
133 |
134 | The "Corresponding Source" for a work in object code form means all
135 | the source code needed to generate, install, and (for an executable
136 | work) run the object code and to modify the work, including scripts to
137 | control those activities. However, it does not include the work's
138 | System Libraries, or general-purpose tools or generally available free
139 | programs which are used unmodified in performing those activities but
140 | which are not part of the work. For example, Corresponding Source
141 | includes interface definition files associated with source files for
142 | the work, and the source code for shared libraries and dynamically
143 | linked subprograms that the work is specifically designed to require,
144 | such as by intimate data communication or control flow between those
145 | subprograms and other parts of the work.
146 |
147 | The Corresponding Source need not include anything that users
148 | can regenerate automatically from other parts of the Corresponding
149 | Source.
150 |
151 | The Corresponding Source for a work in source code form is that
152 | same work.
153 |
154 | 2. Basic Permissions.
155 |
156 | All rights granted under this License are granted for the term of
157 | copyright on the Program, and are irrevocable provided the stated
158 | conditions are met. This License explicitly affirms your unlimited
159 | permission to run the unmodified Program. The output from running a
160 | covered work is covered by this License only if the output, given its
161 | content, constitutes a covered work. This License acknowledges your
162 | rights of fair use or other equivalent, as provided by copyright law.
163 |
164 | You may make, run and propagate covered works that you do not
165 | convey, without conditions so long as your license otherwise remains
166 | in force. You may convey covered works to others for the sole purpose
167 | of having them make modifications exclusively for you, or provide you
168 | with facilities for running those works, provided that you comply with
169 | the terms of this License in conveying all material for which you do
170 | not control copyright. Those thus making or running the covered works
171 | for you must do so exclusively on your behalf, under your direction
172 | and control, on terms that prohibit them from making any copies of
173 | your copyrighted material outside their relationship with you.
174 |
175 | Conveying under any other circumstances is permitted solely under
176 | the conditions stated below. Sublicensing is not allowed; section 10
177 | makes it unnecessary.
178 |
179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180 |
181 | No covered work shall be deemed part of an effective technological
182 | measure under any applicable law fulfilling obligations under article
183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184 | similar laws prohibiting or restricting circumvention of such
185 | measures.
186 |
187 | When you convey a covered work, you waive any legal power to forbid
188 | circumvention of technological measures to the extent such circumvention
189 | is effected by exercising rights under this License with respect to
190 | the covered work, and you disclaim any intention to limit operation or
191 | modification of the work as a means of enforcing, against the work's
192 | users, your or third parties' legal rights to forbid circumvention of
193 | technological measures.
194 |
195 | 4. Conveying Verbatim Copies.
196 |
197 | You may convey verbatim copies of the Program's source code as you
198 | receive it, in any medium, provided that you conspicuously and
199 | appropriately publish on each copy an appropriate copyright notice;
200 | keep intact all notices stating that this License and any
201 | non-permissive terms added in accord with section 7 apply to the code;
202 | keep intact all notices of the absence of any warranty; and give all
203 | recipients a copy of this License along with the Program.
204 |
205 | You may charge any price or no price for each copy that you convey,
206 | and you may offer support or warranty protection for a fee.
207 |
208 | 5. Conveying Modified Source Versions.
209 |
210 | You may convey a work based on the Program, or the modifications to
211 | produce it from the Program, in the form of source code under the
212 | terms of section 4, provided that you also meet all of these conditions:
213 |
214 | a) The work must carry prominent notices stating that you modified
215 | it, and giving a relevant date.
216 |
217 | b) The work must carry prominent notices stating that it is
218 | released under this License and any conditions added under section
219 | 7. This requirement modifies the requirement in section 4 to
220 | "keep intact all notices".
221 |
222 | c) You must license the entire work, as a whole, under this
223 | License to anyone who comes into possession of a copy. This
224 | License will therefore apply, along with any applicable section 7
225 | additional terms, to the whole of the work, and all its parts,
226 | regardless of how they are packaged. This License gives no
227 | permission to license the work in any other way, but it does not
228 | invalidate such permission if you have separately received it.
229 |
230 | d) If the work has interactive user interfaces, each must display
231 | Appropriate Legal Notices; however, if the Program has interactive
232 | interfaces that do not display Appropriate Legal Notices, your
233 | work need not make them do so.
234 |
235 | A compilation of a covered work with other separate and independent
236 | works, which are not by their nature extensions of the covered work,
237 | and which are not combined with it such as to form a larger program,
238 | in or on a volume of a storage or distribution medium, is called an
239 | "aggregate" if the compilation and its resulting copyright are not
240 | used to limit the access or legal rights of the compilation's users
241 | beyond what the individual works permit. Inclusion of a covered work
242 | in an aggregate does not cause this License to apply to the other
243 | parts of the aggregate.
244 |
245 | 6. Conveying Non-Source Forms.
246 |
247 | You may convey a covered work in object code form under the terms
248 | of sections 4 and 5, provided that you also convey the
249 | machine-readable Corresponding Source under the terms of this License,
250 | in one of these ways:
251 |
252 | a) Convey the object code in, or embodied in, a physical product
253 | (including a physical distribution medium), accompanied by the
254 | Corresponding Source fixed on a durable physical medium
255 | customarily used for software interchange.
256 |
257 | b) Convey the object code in, or embodied in, a physical product
258 | (including a physical distribution medium), accompanied by a
259 | written offer, valid for at least three years and valid for as
260 | long as you offer spare parts or customer support for that product
261 | model, to give anyone who possesses the object code either (1) a
262 | copy of the Corresponding Source for all the software in the
263 | product that is covered by this License, on a durable physical
264 | medium customarily used for software interchange, for a price no
265 | more than your reasonable cost of physically performing this
266 | conveying of source, or (2) access to copy the
267 | Corresponding Source from a network server at no charge.
268 |
269 | c) Convey individual copies of the object code with a copy of the
270 | written offer to provide the Corresponding Source. This
271 | alternative is allowed only occasionally and noncommercially, and
272 | only if you received the object code with such an offer, in accord
273 | with subsection 6b.
274 |
275 | d) Convey the object code by offering access from a designated
276 | place (gratis or for a charge), and offer equivalent access to the
277 | Corresponding Source in the same way through the same place at no
278 | further charge. You need not require recipients to copy the
279 | Corresponding Source along with the object code. If the place to
280 | copy the object code is a network server, the Corresponding Source
281 | may be on a different server (operated by you or a third party)
282 | that supports equivalent copying facilities, provided you maintain
283 | clear directions next to the object code saying where to find the
284 | Corresponding Source. Regardless of what server hosts the
285 | Corresponding Source, you remain obligated to ensure that it is
286 | available for as long as needed to satisfy these requirements.
287 |
288 | e) Convey the object code using peer-to-peer transmission, provided
289 | you inform other peers where the object code and Corresponding
290 | Source of the work are being offered to the general public at no
291 | charge under subsection 6d.
292 |
293 | A separable portion of the object code, whose source code is excluded
294 | from the Corresponding Source as a System Library, need not be
295 | included in conveying the object code work.
296 |
297 | A "User Product" is either (1) a "consumer product", which means any
298 | tangible personal property which is normally used for personal, family,
299 | or household purposes, or (2) anything designed or sold for incorporation
300 | into a dwelling. In determining whether a product is a consumer product,
301 | doubtful cases shall be resolved in favor of coverage. For a particular
302 | product received by a particular user, "normally used" refers to a
303 | typical or common use of that class of product, regardless of the status
304 | of the particular user or of the way in which the particular user
305 | actually uses, or expects or is expected to use, the product. A product
306 | is a consumer product regardless of whether the product has substantial
307 | commercial, industrial or non-consumer uses, unless such uses represent
308 | the only significant mode of use of the product.
309 |
310 | "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to install
312 | and execute modified versions of a covered work in that User Product from
313 | a modified version of its Corresponding Source. The information must
314 | suffice to ensure that the continued functioning of the modified object
315 | code is in no case prevented or interfered with solely because
316 | modification has been made.
317 |
318 | If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information. But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 |
329 | The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or updates
331 | for a work that has been modified or installed by the recipient, or for
332 | the User Product in which it has been modified or installed. Access to a
333 | network may be denied when the modification itself materially and
334 | adversely affects the operation of the network or violates the rules and
335 | protocols for communication across the network.
336 |
337 | Corresponding Source conveyed, and Installation Information provided,
338 | in accord with this section must be in a format that is publicly
339 | documented (and with an implementation available to the public in
340 | source code form), and must require no special password or key for
341 | unpacking, reading or copying.
342 |
343 | 7. Additional Terms.
344 |
345 | "Additional permissions" are terms that supplement the terms of this
346 | License by making exceptions from one or more of its conditions.
347 | Additional permissions that are applicable to the entire Program shall
348 | be treated as though they were included in this License, to the extent
349 | that they are valid under applicable law. If additional permissions
350 | apply only to part of the Program, that part may be used separately
351 | under those permissions, but the entire Program remains governed by
352 | this License without regard to the additional permissions.
353 |
354 | When you convey a copy of a covered work, you may at your option
355 | remove any additional permissions from that copy, or from any part of
356 | it. (Additional permissions may be written to require their own
357 | removal in certain cases when you modify the work.) You may place
358 | additional permissions on material, added by you to a covered work,
359 | for which you have or can give appropriate copyright permission.
360 |
361 | Notwithstanding any other provision of this License, for material you
362 | add to a covered work, you may (if authorized by the copyright holders of
363 | that material) supplement the terms of this License with terms:
364 |
365 | a) Disclaiming warranty or limiting liability differently from the
366 | terms of sections 15 and 16 of this License; or
367 |
368 | b) Requiring preservation of specified reasonable legal notices or
369 | author attributions in that material or in the Appropriate Legal
370 | Notices displayed by works containing it; or
371 |
372 | c) Prohibiting misrepresentation of the origin of that material, or
373 | requiring that modified versions of such material be marked in
374 | reasonable ways as different from the original version; or
375 |
376 | d) Limiting the use for publicity purposes of names of licensors or
377 | authors of the material; or
378 |
379 | e) Declining to grant rights under trademark law for use of some
380 | trade names, trademarks, or service marks; or
381 |
382 | f) Requiring indemnification of licensors and authors of that
383 | material by anyone who conveys the material (or modified versions of
384 | it) with contractual assumptions of liability to the recipient, for
385 | any liability that these contractual assumptions directly impose on
386 | those licensors and authors.
387 |
388 | All other non-permissive additional terms are considered "further
389 | restrictions" within the meaning of section 10. If the Program as you
390 | received it, or any part of it, contains a notice stating that it is
391 | governed by this License along with a term that is a further
392 | restriction, you may remove that term. If a license document contains
393 | a further restriction but permits relicensing or conveying under this
394 | License, you may add to a covered work material governed by the terms
395 | of that license document, provided that the further restriction does
396 | not survive such relicensing or conveying.
397 |
398 | If you add terms to a covered work in accord with this section, you
399 | must place, in the relevant source files, a statement of the
400 | additional terms that apply to those files, or a notice indicating
401 | where to find the applicable terms.
402 |
403 | Additional terms, permissive or non-permissive, may be stated in the
404 | form of a separately written license, or stated as exceptions;
405 | the above requirements apply either way.
406 |
407 | 8. Termination.
408 |
409 | You may not propagate or modify a covered work except as expressly
410 | provided under this License. Any attempt otherwise to propagate or
411 | modify it is void, and will automatically terminate your rights under
412 | this License (including any patent licenses granted under the third
413 | paragraph of section 11).
414 |
415 | However, if you cease all violation of this License, then your
416 | license from a particular copyright holder is reinstated (a)
417 | provisionally, unless and until the copyright holder explicitly and
418 | finally terminates your license, and (b) permanently, if the copyright
419 | holder fails to notify you of the violation by some reasonable means
420 | prior to 60 days after the cessation.
421 |
422 | Moreover, your license from a particular copyright holder is
423 | reinstated permanently if the copyright holder notifies you of the
424 | violation by some reasonable means, this is the first time you have
425 | received notice of violation of this License (for any work) from that
426 | copyright holder, and you cure the violation prior to 30 days after
427 | your receipt of the notice.
428 |
429 | Termination of your rights under this section does not terminate the
430 | licenses of parties who have received copies or rights from you under
431 | this License. If your rights have been terminated and not permanently
432 | reinstated, you do not qualify to receive new licenses for the same
433 | material under section 10.
434 |
435 | 9. Acceptance Not Required for Having Copies.
436 |
437 | You are not required to accept this License in order to receive or
438 | run a copy of the Program. Ancillary propagation of a covered work
439 | occurring solely as a consequence of using peer-to-peer transmission
440 | to receive a copy likewise does not require acceptance. However,
441 | nothing other than this License grants you permission to propagate or
442 | modify any covered work. These actions infringe copyright if you do
443 | not accept this License. Therefore, by modifying or propagating a
444 | covered work, you indicate your acceptance of this License to do so.
445 |
446 | 10. Automatic Licensing of Downstream Recipients.
447 |
448 | Each time you convey a covered work, the recipient automatically
449 | receives a license from the original licensors, to run, modify and
450 | propagate that work, subject to this License. You are not responsible
451 | for enforcing compliance by third parties with this License.
452 |
453 | An "entity transaction" is a transaction transferring control of an
454 | organization, or substantially all assets of one, or subdividing an
455 | organization, or merging organizations. If propagation of a covered
456 | work results from an entity transaction, each party to that
457 | transaction who receives a copy of the work also receives whatever
458 | licenses to the work the party's predecessor in interest had or could
459 | give under the previous paragraph, plus a right to possession of the
460 | Corresponding Source of the work from the predecessor in interest, if
461 | the predecessor has it or can get it with reasonable efforts.
462 |
463 | You may not impose any further restrictions on the exercise of the
464 | rights granted or affirmed under this License. For example, you may
465 | not impose a license fee, royalty, or other charge for exercise of
466 | rights granted under this License, and you may not initiate litigation
467 | (including a cross-claim or counterclaim in a lawsuit) alleging that
468 | any patent claim is infringed by making, using, selling, offering for
469 | sale, or importing the Program or any portion of it.
470 |
471 | 11. Patents.
472 |
473 | A "contributor" is a copyright holder who authorizes use under this
474 | License of the Program or a work on which the Program is based. The
475 | work thus licensed is called the contributor's "contributor version".
476 |
477 | A contributor's "essential patent claims" are all patent claims
478 | owned or controlled by the contributor, whether already acquired or
479 | hereafter acquired, that would be infringed by some manner, permitted
480 | by this License, of making, using, or selling its contributor version,
481 | but do not include claims that would be infringed only as a
482 | consequence of further modification of the contributor version. For
483 | purposes of this definition, "control" includes the right to grant
484 | patent sublicenses in a manner consistent with the requirements of
485 | this License.
486 |
487 | Each contributor grants you a non-exclusive, worldwide, royalty-free
488 | patent license under the contributor's essential patent claims, to
489 | make, use, sell, offer for sale, import and otherwise run, modify and
490 | propagate the contents of its contributor version.
491 |
492 | In the following three paragraphs, a "patent license" is any express
493 | agreement or commitment, however denominated, not to enforce a patent
494 | (such as an express permission to practice a patent or covenant not to
495 | sue for patent infringement). To "grant" such a patent license to a
496 | party means to make such an agreement or commitment not to enforce a
497 | patent against the party.
498 |
499 | If you convey a covered work, knowingly relying on a patent license,
500 | and the Corresponding Source of the work is not available for anyone
501 | to copy, free of charge and under the terms of this License, through a
502 | publicly available network server or other readily accessible means,
503 | then you must either (1) cause the Corresponding Source to be so
504 | available, or (2) arrange to deprive yourself of the benefit of the
505 | patent license for this particular work, or (3) arrange, in a manner
506 | consistent with the requirements of this License, to extend the patent
507 | license to downstream recipients. "Knowingly relying" means you have
508 | actual knowledge that, but for the patent license, your conveying the
509 | covered work in a country, or your recipient's use of the covered work
510 | in a country, would infringe one or more identifiable patents in that
511 | country that you have reason to believe are valid.
512 |
513 | If, pursuant to or in connection with a single transaction or
514 | arrangement, you convey, or propagate by procuring conveyance of, a
515 | covered work, and grant a patent license to some of the parties
516 | receiving the covered work authorizing them to use, propagate, modify
517 | or convey a specific copy of the covered work, then the patent license
518 | you grant is automatically extended to all recipients of the covered
519 | work and works based on it.
520 |
521 | A patent license is "discriminatory" if it does not include within
522 | the scope of its coverage, prohibits the exercise of, or is
523 | conditioned on the non-exercise of one or more of the rights that are
524 | specifically granted under this License. You may not convey a covered
525 | work if you are a party to an arrangement with a third party that is
526 | in the business of distributing software, under which you make payment
527 | to the third party based on the extent of your activity of conveying
528 | the work, and under which the third party grants, to any of the
529 | parties who would receive the covered work from you, a discriminatory
530 | patent license (a) in connection with copies of the covered work
531 | conveyed by you (or copies made from those copies), or (b) primarily
532 | for and in connection with specific products or compilations that
533 | contain the covered work, unless you entered into that arrangement,
534 | or that patent license was granted, prior to 28 March 2007.
535 |
536 | Nothing in this License shall be construed as excluding or limiting
537 | any implied license or other defenses to infringement that may
538 | otherwise be available to you under applicable patent law.
539 |
540 | 12. No Surrender of Others' Freedom.
541 |
542 | If conditions are imposed on you (whether by court order, agreement or
543 | otherwise) that contradict the conditions of this License, they do not
544 | excuse you from the conditions of this License. If you cannot convey a
545 | covered work so as to satisfy simultaneously your obligations under this
546 | License and any other pertinent obligations, then as a consequence you may
547 | not convey it at all. For example, if you agree to terms that obligate you
548 | to collect a royalty for further conveying from those to whom you convey
549 | the Program, the only way you could satisfy both those terms and this
550 | License would be to refrain entirely from conveying the Program.
551 |
552 | 13. Use with the GNU Affero General Public License.
553 |
554 | Notwithstanding any other provision of this License, you have
555 | permission to link or combine any covered work with a work licensed
556 | under version 3 of the GNU Affero General Public License into a single
557 | combined work, and to convey the resulting work. The terms of this
558 | License will continue to apply to the part which is the covered work,
559 | but the special requirements of the GNU Affero General Public License,
560 | section 13, concerning interaction through a network will apply to the
561 | combination as such.
562 |
563 | 14. Revised Versions of this License.
564 |
565 | The Free Software Foundation may publish revised and/or new versions of
566 | the GNU General Public License from time to time. Such new versions will
567 | be similar in spirit to the present version, but may differ in detail to
568 | address new problems or concerns.
569 |
570 | Each version is given a distinguishing version number. If the
571 | Program specifies that a certain numbered version of the GNU General
572 | Public License "or any later version" applies to it, you have the
573 | option of following the terms and conditions either of that numbered
574 | version or of any later version published by the Free Software
575 | Foundation. If the Program does not specify a version number of the
576 | GNU General Public License, you may choose any version ever published
577 | by the Free Software Foundation.
578 |
579 | If the Program specifies that a proxy can decide which future
580 | versions of the GNU General Public License can be used, that proxy's
581 | public statement of acceptance of a version permanently authorizes you
582 | to choose that version for the Program.
583 |
584 | Later license versions may give you additional or different
585 | permissions. However, no additional obligations are imposed on any
586 | author or copyright holder as a result of your choosing to follow a
587 | later version.
588 |
589 | 15. Disclaimer of Warranty.
590 |
591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599 |
600 | 16. Limitation of Liability.
601 |
602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610 | SUCH DAMAGES.
611 |
612 | 17. Interpretation of Sections 15 and 16.
613 |
614 | If the disclaimer of warranty and limitation of liability provided
615 | above cannot be given local legal effect according to their terms,
616 | reviewing courts shall apply local law that most closely approximates
617 | an absolute waiver of all civil liability in connection with the
618 | Program, unless a warranty or assumption of liability accompanies a
619 | copy of the Program in return for a fee.
620 |
621 | END OF TERMS AND CONDITIONS
622 |
623 | How to Apply These Terms to Your New Programs
624 |
625 | If you develop a new program, and you want it to be of the greatest
626 | possible use to the public, the best way to achieve this is to make it
627 | free software which everyone can redistribute and change under these terms.
628 |
629 | To do so, attach the following notices to the program. It is safest
630 | to attach them to the start of each source file to most effectively
631 | state the exclusion of warranty; and each file should have at least
632 | the "copyright" line and a pointer to where the full notice is found.
633 |
634 |
635 | Copyright (C)
636 |
637 | This program is free software: you can redistribute it and/or modify
638 | it under the terms of the GNU General Public License as published by
639 | the Free Software Foundation, either version 3 of the License, or
640 | (at your option) any later version.
641 |
642 | This program is distributed in the hope that it will be useful,
643 | but WITHOUT ANY WARRANTY; without even the implied warranty of
644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 | GNU General Public License for more details.
646 |
647 | You should have received a copy of the GNU General Public License
648 | along with this program. If not, see .
649 |
650 | Also add information on how to contact you by electronic and paper mail.
651 |
652 | If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 |
655 | Copyright (C)
656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 | This is free software, and you are welcome to redistribute it
658 | under certain conditions; type `show c' for details.
659 |
660 | The hypothetical commands `show w' and `show c' should show the appropriate
661 | parts of the General Public License. Of course, your program's commands
662 | might be different; for a GUI interface, you would use an "about box".
663 |
664 | You should also get your employer (if you work as a programmer) or school,
665 | if any, to sign a "copyright disclaimer" for the program, if necessary.
666 | For more information on this, and how to apply and follow the GNU GPL, see
667 | .
668 |
669 | The GNU General Public License does not permit incorporating your program
670 | into proprietary programs. If your program is a subroutine library, you
671 | may consider it more useful to permit linking proprietary applications with
672 | the library. If this is what you want to do, use the GNU Lesser General
673 | Public License instead of this License. But first, please read
674 | .
675 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include *.md
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | PyUserInput
2 | ===========
3 |
4 | **PyUserInput is a group project so we've moved the project over to a group
5 | organization: https://github.com/PyUserInput/PyUserInput . That is now the
6 | active development repository and I'll be phasing this one out, so please go
7 | there for the latest code and to post new issues. This should be corrected on
8 | PyPI in the next version update of PyUserInput.**
9 |
10 | A module for cross-platform control of the mouse and keyboard in python that is
11 | simple to use.
12 |
13 | Mouse control should work on Windows, Mac, and X11 (most Linux systems).
14 | Scrolling is implemented, but users should be aware that variations may
15 | exist between platforms and applications.
16 |
17 | Keyboard control works on X11(linux) and Windows systems. Mac control is a work
18 | in progress.
19 |
20 | Dependencies
21 | ------------
22 |
23 | Depending on your platform, you will need the following python modules for
24 | PyUserInput to function:
25 |
26 | * Linux - Xlib
27 | * Mac - Quartz, AppKit
28 | * Windows - pywin32, pyHook
29 |
30 | How to get started
31 | ------------------
32 |
33 | After installing PyUserInput, you should have pymouse and pykeyboard modules in
34 | your python path. Let's make a mouse and keyboard object:
35 |
36 | from pymouse import PyMouse
37 | from pykeyboard import PyKeyboard
38 |
39 | m = PyMouse()
40 | k = PyKeyboard()
41 |
42 | Here's an example of clicking the center of the screen and typing "Hello, World!":
43 |
44 | x_dim, y_dim = m.screen_size()
45 | m.click(x_dim/2, y_dim/2, 1)
46 | k.type_string('Hello, World!')
47 |
48 | PyKeyboard allows for a range of ways for sending keystrokes:
49 |
50 | # pressing a key
51 | k.press_key('H')
52 | # which you then follow with a release of the key
53 | k.release_key('H')
54 | # or you can 'tap' a key which does both
55 | k.tap_key('e')
56 | # note that that tap_key does support a way of repeating keystrokes with a interval time between each
57 | k.tap_key('l',n=2,interval=5)
58 | # and you can send a string if needed too
59 | k.type_string('o World!')
60 |
61 |
62 | and it supports a wide range of special keys:
63 |
64 | #Create an Alt+Tab combo
65 | k.press_key(k.alt_key)
66 | k.tap_key(k.tab_key)
67 | k.release_key(k.alt_key)
68 |
69 | k.tap_key(k.function_keys[5]) # Tap F5
70 | k.tap_key(k.numpad_keys['Home']) # Tap 'Home' on the numpad
71 | k.tap_key(k.numpad_keys[5], n=3) # Tap 5 on the numpad, thrice
72 |
73 | Note you can also send multiple keystrokes together (e.g. when accessing a keyboard shortcut) using the press_keys method:
74 |
75 | # Mac example
76 | k.press_keys(['Command','shift','3'])
77 | # Windows example
78 | k.press_keys([k.windows_l_key,'d'])
79 |
80 | Consistency between platforms is a big challenge; Please look at the source for the operating system that you are using to help understand the format of the keys that you would need to send. For example:
81 |
82 | # Windows
83 | k.tap_key(k.alt_key)
84 | # Mac
85 | k.tap_key('Alternate')
86 |
87 | I'd like to make a special note about using PyMouseEvent and PyKeyboardEvent.
88 | These objects are a framework for listening for mouse and keyboard input; they
89 | don't do anything besides listen until you subclass them. I'm still formalizing
90 | PyKeyboardEvent, so here's an example of subclassing PyMouseEvent:
91 |
92 | from pymouse import PyMouseEvent
93 |
94 | def fibo():
95 | a = 0
96 | yield a
97 | b = 1
98 | yield b
99 | while True:
100 | a, b = b, a+b
101 | yield b
102 |
103 | class Clickonacci(PyMouseEvent):
104 | def __init__(self):
105 | PyMouseEvent.__init__(self)
106 | self.fibo = fibo()
107 |
108 | def click(self, x, y, button, press):
109 | '''Print Fibonacci numbers when the left click is pressed.'''
110 | if button == 1:
111 | if press:
112 | print(self.fibo.next())
113 | else: # Exit if any other mouse button used
114 | self.stop()
115 |
116 | C = Clickonacci()
117 | C.run()
118 |
119 | Intended Functionality of Capturing in PyUserInput
120 | --------------------------------------------------
121 |
122 | For PyMouseEvent classes, the variables "capture" and "capture_move" may be
123 | passed during instantiation. If `capture=True` is passed, the intended result
124 | is that all mouse button input will go to your program and nowhere else. The
125 | same is true for `capture_move=True` except it deals with mouse pointer motion
126 | instead of the buttons. Both may be set simultaneously, and serve to prevent
127 | events from propagating further. If you notice any bugs with this behavior,
128 | please bring it to our attention.
129 |
130 | A Short Todo List
131 | -----------------
132 |
133 | These are a few things I am considering for future development in
134 | PyUserInput:
135 |
136 | * Ensuring that PyMouse capturing works for all platforms
137 | * Implement PyKeyboard capturing (add PyKeyboardEvent for Mac as well)
138 | * PyMouse dynamic delta scrolling (available in Mac and Windows, hard to standardize)
139 | * Make friends with more Mac developers, testing help is needed...
140 |
141 |
142 | Many thanks to
143 | --------------
144 |
145 | [Pepijn de Vos](https://github.com/pepijndevos) - For making
146 | [PyMouse](https://github.com/pepijndevos/PyMouse) and allowing me to modify
147 | and distribute it along with PyKeyboard.
148 |
149 | [Jack Grigg](https://github.com/pythonian4000) - For contributions to
150 | cross-platform scrolling in PyMouse.
151 |
--------------------------------------------------------------------------------
/examples/clickonacci.py:
--------------------------------------------------------------------------------
1 | from pymouse import PyMouseEvent
2 |
3 | def fibo():
4 | a = 0
5 | yield a
6 | b = 1
7 | yield b
8 | while True:
9 | a, b = b, a+b
10 | yield b
11 |
12 | class Clickonacci(PyMouseEvent):
13 | def __init__(self):
14 | PyMouseEvent.__init__(self)
15 | self.fibo = fibo()
16 |
17 | def click(self, x, y, button, press):
18 | '''Print Fibonacci numbers when the left click is pressed.'''
19 | if button == 1:
20 | if press:
21 | print(self.fibo.next())
22 | else: # Exit if any other mouse button used
23 | self.stop()
24 |
25 | C = Clickonacci()
26 | C.run()
--------------------------------------------------------------------------------
/examples/filetranscriber.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 |
4 | """
5 | FileTranscriber
6 |
7 | A small utility that simulates user typing to aid plaintext file transcription
8 | in limited environments
9 |
10 | Usage:
11 | transcribe [--interval=] [--pause=]
12 | transcribe (--help | --version)
13 |
14 | Options:
15 | -v --version show program's version number and exit
16 | -h --help show this help message and exit
17 | -i --interval= Interval between keystrokes (in seconds). Typing too
18 | quickly may break applications processing the
19 | keystrokes. [default: 0.1]
20 | -p --pause= How long the script should wait before starting (in
21 | seconds). Increase this if you need more time to enter
22 | the typing field. [default: 5]
23 | """
24 |
25 | #This script copied from https://github.com/SavinaRoja/FileTranscriber
26 | #Check there for updates
27 |
28 | from docopt import docopt
29 | from pykeyboard import PyKeyboard
30 | from pymouse import PyMouseEvent
31 | import time
32 | import sys
33 |
34 |
35 | #Hack to make input work for both Python 2 and Python 3
36 | try:
37 | input = raw_input
38 | except NameError:
39 | pass
40 |
41 |
42 | class AbortMouse(PyMouseEvent):
43 | def click(self, x, y, button, press):
44 | if press:
45 | self.stop()
46 |
47 |
48 | def main():
49 | #Get an instance of PyKeyboard, and our custom PyMouseEvent
50 | keyboard = PyKeyboard()
51 | mouse = AbortMouse()
52 |
53 | input('Press Enter when ready.')
54 | print('Typing will begin in {0} seconds...'.format(opts['--pause']))
55 | time.sleep(opts['--pause'])
56 |
57 | mouse.start()
58 | with open(opts[''], 'r') as readfile:
59 | for line in readfile:
60 | if not mouse.state:
61 | print('Typing aborted!')
62 | break
63 | keyboard.type_string(line, opts['--interval'])
64 |
65 | if __name__ == '__main__':
66 | opts = docopt(__doc__, version='0.2.1')
67 | try:
68 | opts['--interval'] = float(opts['--interval'])
69 | except ValueError:
70 | print('The value of --interval must be a number')
71 | sys.exit(1)
72 | try:
73 | opts['--pause'] = float(opts['--pause'])
74 | except ValueError:
75 | print('The value of --pause must be a number')
76 | sys.exit(1)
77 | main()
--------------------------------------------------------------------------------
/examples/mouse.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 |
3 | import logging
4 | import daemon
5 | from socket import gethostname;
6 | from pymouse import PyMouse
7 | import random, time
8 | from signal import signal, SIGINT
9 |
10 | with daemon.DaemonContext():
11 |
12 | def stop(signum, frame):
13 | cleanup_stop_thread();
14 | sys.exit()
15 | signal(SIGINT, stop)
16 |
17 | try:
18 | from pymouse import PyMouseEvent
19 |
20 | class event(PyMouseEvent):
21 | def __init__(self):
22 | super(event, self).__init__()
23 | FORMAT = '%(asctime)-15s ' + gethostname() + ' touchlogger %(levelname)s %(message)s'
24 | logging.basicConfig(filename='/var/log/mouse.log', level=logging.DEBUG, format=FORMAT)
25 |
26 | def move(self, x, y):
27 | pass
28 |
29 | def click(self, x, y, button, press):
30 | if press:
31 | logging.info('{ "event": "click", "type": "press", "x": "' + str(x) + '", "y": "' + str(y) + '"}')
32 | else:
33 | logging.info('{ "event": "click", "type": "release", "x": "' + str(x) + '", "y": "' + str(y) + '"}')
34 |
35 | e = event()
36 | e.capture = False
37 | e.daemon = False
38 | e.start()
39 |
40 | except ImportError:
41 | logging.info('{ "event": "exception", "type": "ImportError", "value": "Mouse events unsupported"}')
42 | sys.exit()
43 |
44 | m = PyMouse()
45 | try:
46 | size = m.screen_size()
47 | logging.info('{ "event": "start", "type": "size", "value": "' + str(size) + '"}')
48 | except:
49 | logging.info('{ "event": "exception", "type": "size", "value": "undetermined problem"}')
50 | sys.exit()
51 |
52 | try:
53 | e.join()
54 | except KeyboardInterrupt:
55 | e.stop()
56 | sys.exit()
57 |
58 |
--------------------------------------------------------------------------------
/examples/mouselogger:
--------------------------------------------------------------------------------
1 | #! /bin/sh
2 | ### BEGIN INIT INFO
3 | # Provides: mouselogger
4 | # Default-Start: 2 3 4 5
5 | # Default-Stop: 0 1 6
6 | # Short-Description: Mouse Logger daemon
7 | # Description: This file should be used to construct scripts to be
8 | # placed in /etc/init.d.
9 | ### END INIT INFO
10 |
11 | # Author: David Suarez
12 | #
13 |
14 | # Do NOT "set -e"
15 |
16 | # PATH should only include /usr/* if it runs after the mountnfs.sh script
17 | PATH=/sbin:/usr/sbin:/bin:/usr/bin:/opt/wantudu/bin
18 | DESC="Mouse logger for X"
19 | NAME=mouselogger
20 | DAEMON=/opt/wantudu/bin/mouse.py
21 | DAEMON_ARGS=""
22 | PIDFILE=/var/run/$NAME.pid
23 | SCRIPTNAME=/etc/init.d/$NAME
24 |
25 | # Exit if the package is not installed
26 | [ -x "$DAEMON" ] || exit 0
27 |
28 | # Read configuration variable file if it is present
29 | [ -r /etc/default/$NAME ] && . /etc/default/$NAME
30 |
31 | # Load the VERBOSE setting and other rcS variables
32 | . /lib/init/vars.sh
33 |
34 | # Define LSB log_* functions.
35 | # Depend on lsb-base (>= 3.0-6) to ensure that this file is present.
36 | . /lib/lsb/init-functions
37 |
38 | #
39 | # Function that starts the daemon/service
40 | #
41 | do_start()
42 | {
43 | # Return
44 | # 0 if daemon has been started
45 | # 1 if daemon was already running
46 | # 2 if daemon could not be started
47 | start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \
48 | || return 1
49 | start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \
50 | $DAEMON_ARGS \
51 | || return 2
52 | # Add code here, if necessary, that waits for the process to be ready
53 | # to handle requests from services started subsequently which depend
54 | # on this one. As a last resort, sleep for some time.
55 | }
56 |
57 | #
58 | # Function that stops the daemon/service
59 | #
60 | do_stop()
61 | {
62 | # Return
63 | # 0 if daemon has been stopped
64 | # 1 if daemon was already stopped
65 | # 2 if daemon could not be stopped
66 | # other if a failure occurred
67 | start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME
68 | RETVAL="$?"
69 | [ "$RETVAL" = 2 ] && return 2
70 | # Wait for children to finish too if this is a daemon that forks
71 | # and if the daemon is only ever run from this initscript.
72 | # If the above conditions are not satisfied then add some other code
73 | # that waits for the process to drop all resources that could be
74 | # needed by services started subsequently. A last resort is to
75 | # sleep for some time.
76 | start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON
77 | [ "$?" = 2 ] && return 2
78 | # Many daemons don't delete their pidfiles when they exit.
79 | rm -f $PIDFILE
80 | return "$RETVAL"
81 | }
82 |
83 | #
84 | # Function that sends a SIGHUP to the daemon/service
85 | #
86 | do_reload() {
87 | #
88 | # If the daemon can reload its configuration without
89 | # restarting (for example, when it is sent a SIGHUP),
90 | # then implement that here.
91 | #
92 | start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME
93 | return 0
94 | }
95 |
96 | case "$1" in
97 | start)
98 | [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
99 | do_start
100 | case "$?" in
101 | 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
102 | 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
103 | esac
104 | ;;
105 | stop)
106 | [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
107 | do_stop
108 | case "$?" in
109 | 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
110 | 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
111 | esac
112 | ;;
113 | status)
114 | status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $?
115 | ;;
116 | #reload|force-reload)
117 | #
118 | # If do_reload() is not implemented then leave this commented out
119 | # and leave 'force-reload' as an alias for 'restart'.
120 | #
121 | #log_daemon_msg "Reloading $DESC" "$NAME"
122 | #do_reload
123 | #log_end_msg $?
124 | #;;
125 | restart|force-reload)
126 | #
127 | # If the "reload" option is implemented then remove the
128 | # 'force-reload' alias
129 | #
130 | log_daemon_msg "Restarting $DESC" "$NAME"
131 | do_stop
132 | case "$?" in
133 | 0|1)
134 | do_start
135 | case "$?" in
136 | 0) log_end_msg 0 ;;
137 | 1) log_end_msg 1 ;; # Old process is still running
138 | *) log_end_msg 1 ;; # Failed to start
139 | esac
140 | ;;
141 | *)
142 | # Failed to stop
143 | log_end_msg 1
144 | ;;
145 | esac
146 | ;;
147 | *)
148 | #echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2
149 | echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2
150 | exit 3
151 | ;;
152 | esac
153 |
154 | :
155 |
--------------------------------------------------------------------------------
/pykeyboard/__init__.py:
--------------------------------------------------------------------------------
1 | #Copyright 2013 Paul Barton
2 | #
3 | #This program is free software: you can redistribute it and/or modify
4 | #it under the terms of the GNU General Public License as published by
5 | #the Free Software Foundation, either version 3 of the License, or
6 | #(at your option) any later version.
7 | #
8 | #This program is distributed in the hope that it will be useful,
9 | #but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | #GNU General Public License for more details.
12 | #
13 | #You should have received a copy of the GNU General Public License
14 | #along with this program. If not, see .
15 |
16 | """
17 | The goal of PyMouse is to have a cross-platform way to control the mouse.
18 | PyMouse should work on Windows, Mac and any Unix that has xlib.
19 |
20 | PyKeyboard is a part of PyUserInput, along with PyMouse, for more information
21 | about this project, see:
22 | http://github.com/SavinaRoja/PyUserInput
23 | """
24 |
25 | import sys
26 |
27 | if sys.platform.startswith('java'):
28 | from .java_ import PyKeyboard
29 |
30 | elif sys.platform == 'darwin':
31 | from .mac import PyKeyboard, PyKeyboardEvent
32 |
33 | elif sys.platform == 'win32':
34 | from .windows import PyKeyboard, PyKeyboardEvent
35 |
36 | else:
37 | from .x11 import PyKeyboard, PyKeyboardEvent
38 |
--------------------------------------------------------------------------------
/pykeyboard/base.py:
--------------------------------------------------------------------------------
1 | #Copyright 2013 Paul Barton
2 | #
3 | #This program is free software: you can redistribute it and/or modify
4 | #it under the terms of the GNU General Public License as published by
5 | #the Free Software Foundation, either version 3 of the License, or
6 | #(at your option) any later version.
7 | #
8 | #This program is distributed in the hope that it will be useful,
9 | #but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | #GNU General Public License for more details.
12 | #
13 | #You should have received a copy of the GNU General Public License
14 | #along with this program. If not, see .
15 |
16 | """
17 | As the base file, this provides a rough operational model along with the
18 | framework to be extended by each platform.
19 | """
20 |
21 | import time
22 | from threading import Thread
23 |
24 |
25 | class PyKeyboardMeta(object):
26 | """
27 | The base class for PyKeyboard. Represents basic operational model.
28 | """
29 | #: We add this named character for convenience
30 | space = ' '
31 |
32 | def press_key(self, character=''):
33 | """Press a given character key."""
34 | raise NotImplementedError
35 |
36 | def release_key(self, character=''):
37 | """Release a given character key."""
38 | raise NotImplementedError
39 |
40 | def tap_key(self, character='', n=1, interval=0):
41 | """Press and release a given character key n times."""
42 | for i in range(n):
43 | self.press_key(character)
44 | self.release_key(character)
45 | time.sleep(interval)
46 |
47 | def press_keys(self,characters=[]):
48 | """Press a given character key."""
49 | for character in characters:
50 | self.press_key(character)
51 | for character in characters:
52 | self.release_key(character)
53 |
54 | def type_string(self, char_string, interval=0):
55 | """
56 | A convenience method for typing longer strings of characters. Generates
57 | as few Shift events as possible."""
58 | shift = False
59 | for char in char_string:
60 | if self.is_char_shifted(char):
61 | if not shift: # Only press Shift as needed
62 | time.sleep(interval)
63 | self.press_key(self.shift_key)
64 | shift = True
65 | #In order to avoid tap_key pressing Shift, we need to pass the
66 | #unshifted form of the character
67 | if char in '<>?:"{}|~!@#$%^&*()_+':
68 | ch_index = '<>?:"{}|~!@#$%^&*()_+'.index(char)
69 | unshifted_char = ",./;'[]\\`1234567890-="[ch_index]
70 | else:
71 | unshifted_char = char.lower()
72 | time.sleep(interval)
73 | self.tap_key(unshifted_char)
74 | else: # Unshifted already
75 | if shift and char != ' ': # Only release Shift as needed
76 | self.release_key(self.shift_key)
77 | shift = False
78 | time.sleep(interval)
79 | self.tap_key(char)
80 |
81 | if shift: # Turn off Shift if it's still ON
82 | self.release_key(self.shift_key)
83 |
84 | def special_key_assignment(self):
85 | """Makes special keys more accessible."""
86 | raise NotImplementedError
87 |
88 | def lookup_character_value(self, character):
89 | """
90 | If necessary, lookup a valid API value for the key press from the
91 | character.
92 | """
93 | raise NotImplementedError
94 |
95 | def is_char_shifted(self, character):
96 | """Returns True if the key character is uppercase or shifted."""
97 | if character.isupper():
98 | return True
99 | if character in '<>?:"{}|~!@#$%^&*()_+':
100 | return True
101 | return False
102 |
103 |
104 | class PyKeyboardEventMeta(Thread):
105 | """
106 | The base class for PyKeyboard. Represents basic operational model.
107 | """
108 |
109 | #One of the most variable components of keyboards throughout history and
110 | #across manufacturers is the Modifier Key...
111 | #I am attempting to cover a lot of bases to make using PyKeyboardEvent
112 | #simpler, without digging a bunch of traps for incompatibilities between
113 | #platforms.
114 |
115 | #Keeping track of the keyboard's state is not only necessary at times to
116 | #correctly interpret character identities in keyboard events, but should
117 | #also enable a user to easily query modifier states without worrying about
118 | #chaining event triggers for mod-combinations
119 |
120 | #The keyboard's state will be represented by an integer, the individual
121 | #mod keys by a bit mask of that integer
122 | state = 0
123 |
124 | #Each platform should assign, where applicable/possible, the bit masks for
125 | #modifier keys initially set to 0 here. Not all modifiers are recommended
126 | #for cross-platform use
127 | modifier_bits = {'Shift': 1,
128 | 'Lock': 2,
129 | 'Control': 4,
130 | 'Mod1': 8, # X11 dynamic assignment
131 | 'Mod2': 16, # X11 dynamic assignment
132 | 'Mod3': 32, # X11 dynamic assignment
133 | 'Mod4': 64, # X11 dynamic assignment
134 | 'Mod5': 128, # X11 dynamic assignment
135 | 'Alt': 0,
136 | 'AltGr': 0, # Uncommon
137 | 'Caps_Lock': 0,
138 | 'Command': 0, # Mac key without generic equivalent
139 | 'Function': 0, # Not advised; typically undetectable
140 | 'Hyper': 0, # Uncommon?
141 | 'Meta': 0, # Uncommon?
142 | 'Num_Lock': 0,
143 | 'Mode_switch': 0, # Uncommon
144 | 'Shift_Lock': 0, # Uncommon
145 | 'Super': 0, # X11 key, sometimes equivalent to Windows
146 | 'Windows': 0} # Windows key, sometimes equivalent to Super
147 |
148 | #Make the modifiers dictionary for individual states, setting all to off
149 | modifiers = {}
150 | for key in modifier_bits.keys():
151 | modifiers[key] = False
152 |
153 | def __init__(self, capture=False):
154 | Thread.__init__(self)
155 | self.daemon = True
156 | self.capture = capture
157 | self.state = True
158 | self.configure_keys()
159 |
160 | def run(self):
161 | self.state = True
162 |
163 | def stop(self):
164 | self.state = False
165 |
166 | def handler(self):
167 | raise NotImplementedError
168 |
169 | def tap(self, keycode, character, press):
170 | """
171 | Subclass this method with your key event handler. It will receive
172 | the keycode associated with the key event, as well as string name for
173 | the key if one can be assigned (keyboard mask states will apply). The
174 | argument 'press' will be True if the key was depressed and False if the
175 | key was released.
176 | """
177 | pass
178 |
179 | def escape(self, event):
180 | """
181 | A function that defines when to stop listening; subclass this with your
182 | escape behavior. If the program is meant to stop, this method should
183 | return True. Every key event will go through this method before going to
184 | tap(), allowing this method to check for exit conditions.
185 |
186 | The default behavior is to stop when the 'Esc' key is pressed.
187 |
188 | If one wishes to use key combinations, or key series, one might be
189 | interested in reading about Finite State Machines.
190 | http://en.wikipedia.org/wiki/Deterministic_finite_automaton
191 | """
192 | condition = None
193 | return event == condition
194 |
195 | def configure_keys(self):
196 | """
197 | Does per-platform work of configuring the modifier keys as well as data
198 | structures for simplified key access. Does nothing in this base
199 | implementation.
200 | """
201 | pass
202 |
--------------------------------------------------------------------------------
/pykeyboard/java_.py:
--------------------------------------------------------------------------------
1 | #Copyright 2013 Paul Barton
2 | #
3 | #This program is free software: you can redistribute it and/or modify
4 | #it under the terms of the GNU General Public License as published by
5 | #the Free Software Foundation, either version 3 of the License, or
6 | #(at your option) any later version.
7 | #
8 | #This program is distributed in the hope that it will be useful,
9 | #but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | #GNU General Public License for more details.
12 | #
13 | #You should have received a copy of the GNU General Public License
14 | #along with this program. If not, see .
15 |
--------------------------------------------------------------------------------
/pykeyboard/mac.py:
--------------------------------------------------------------------------------
1 | #Copyright 2013 Paul Barton
2 | #
3 | #This program is free software: you can redistribute it and/or modify
4 | #it under the terms of the GNU General Public License as published by
5 | #the Free Software Foundation, either version 3 of the License, or
6 | #(at your option) any later version.
7 | #
8 | #This program is distributed in the hope that it will be useful,
9 | #but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | #GNU General Public License for more details.
12 | #
13 | #You should have received a copy of the GNU General Public License
14 | #along with this program. If not, see .
15 |
16 | import time
17 | import Quartz
18 | from AppKit import NSEvent
19 | from .base import PyKeyboardMeta, PyKeyboardEventMeta
20 |
21 | # Taken from events.h
22 | # /System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/Headers/Events.h
23 | character_translate_table = {
24 | 'a': 0x00,
25 | 's': 0x01,
26 | 'd': 0x02,
27 | 'f': 0x03,
28 | 'h': 0x04,
29 | 'g': 0x05,
30 | 'z': 0x06,
31 | 'x': 0x07,
32 | 'c': 0x08,
33 | 'v': 0x09,
34 | 'b': 0x0b,
35 | 'q': 0x0c,
36 | 'w': 0x0d,
37 | 'e': 0x0e,
38 | 'r': 0x0f,
39 | 'y': 0x10,
40 | 't': 0x11,
41 | '1': 0x12,
42 | '2': 0x13,
43 | '3': 0x14,
44 | '4': 0x15,
45 | '6': 0x16,
46 | '5': 0x17,
47 | '=': 0x18,
48 | '9': 0x19,
49 | '7': 0x1a,
50 | '-': 0x1b,
51 | '8': 0x1c,
52 | '0': 0x1d,
53 | ']': 0x1e,
54 | 'o': 0x1f,
55 | 'u': 0x20,
56 | '[': 0x21,
57 | 'i': 0x22,
58 | 'p': 0x23,
59 | 'l': 0x25,
60 | 'j': 0x26,
61 | '\'': 0x27,
62 | 'k': 0x28,
63 | ';': 0x29,
64 | '\\': 0x2a,
65 | ',': 0x2b,
66 | '/': 0x2c,
67 | 'n': 0x2d,
68 | 'm': 0x2e,
69 | '.': 0x2f,
70 | '`': 0x32,
71 | ' ': 0x31,
72 | '\r': 0x24,
73 | '\t': 0x30,
74 | '\n': 0x24,
75 | 'return' : 0x24,
76 | 'tab' : 0x30,
77 | 'space' : 0x31,
78 | 'delete' : 0x33,
79 | 'escape' : 0x35,
80 | 'command' : 0x37,
81 | 'shift' : 0x38,
82 | 'capslock' : 0x39,
83 | 'option' : 0x3A,
84 | 'alternate' : 0x3A,
85 | 'control' : 0x3B,
86 | 'rightshift' : 0x3C,
87 | 'rightoption' : 0x3D,
88 | 'rightcontrol' : 0x3E,
89 | 'function' : 0x3F,
90 | }
91 |
92 |
93 | # Taken from ev_keymap.h
94 | # http://www.opensource.apple.com/source/IOHIDFamily/IOHIDFamily-86.1/IOHIDSystem/IOKit/hidsystem/ev_keymap.h
95 | special_key_translate_table = {
96 | 'KEYTYPE_SOUND_UP': 0,
97 | 'KEYTYPE_SOUND_DOWN': 1,
98 | 'KEYTYPE_BRIGHTNESS_UP': 2,
99 | 'KEYTYPE_BRIGHTNESS_DOWN': 3,
100 | 'KEYTYPE_CAPS_LOCK': 4,
101 | 'KEYTYPE_HELP': 5,
102 | 'POWER_KEY': 6,
103 | 'KEYTYPE_MUTE': 7,
104 | 'UP_ARROW_KEY': 8,
105 | 'DOWN_ARROW_KEY': 9,
106 | 'KEYTYPE_NUM_LOCK': 10,
107 | 'KEYTYPE_CONTRAST_UP': 11,
108 | 'KEYTYPE_CONTRAST_DOWN': 12,
109 | 'KEYTYPE_LAUNCH_PANEL': 13,
110 | 'KEYTYPE_EJECT': 14,
111 | 'KEYTYPE_VIDMIRROR': 15,
112 | 'KEYTYPE_PLAY': 16,
113 | 'KEYTYPE_NEXT': 17,
114 | 'KEYTYPE_PREVIOUS': 18,
115 | 'KEYTYPE_FAST': 19,
116 | 'KEYTYPE_REWIND': 20,
117 | 'KEYTYPE_ILLUMINATION_UP': 21,
118 | 'KEYTYPE_ILLUMINATION_DOWN': 22,
119 | 'KEYTYPE_ILLUMINATION_TOGGLE': 23
120 | }
121 |
122 | class PyKeyboard(PyKeyboardMeta):
123 |
124 | def __init__(self):
125 | self.shift_key = 'shift'
126 | self.modifier_table = {'Shift':False,'Command':False,'Control':False,'Alternate':False}
127 |
128 | def press_key(self, key):
129 | if key.title() in self.modifier_table:
130 | self.modifier_table.update({key.title():True})
131 |
132 | if key in special_key_translate_table:
133 | self._press_special_key(key, True)
134 | else:
135 | self._press_normal_key(key, True)
136 |
137 | def release_key(self, key):
138 | # remove the key
139 | if key.title() in self.modifier_table: self.modifier_table.update({key.title():False})
140 |
141 | if key in special_key_translate_table:
142 | self._press_special_key(key, False)
143 | else:
144 | self._press_normal_key(key, False)
145 |
146 | def special_key_assignment(self):
147 | self.volume_mute_key = 'KEYTYPE_MUTE'
148 | self.volume_down_key = 'KEYTYPE_SOUND_DOWN'
149 | self.volume_up_key = 'KEYTYPE_SOUND_UP'
150 | self.media_play_pause_key = 'KEYTYPE_PLAY'
151 |
152 | # Doesn't work :(
153 | # self.media_next_track_key = 'KEYTYPE_NEXT'
154 | # self.media_prev_track_key = 'KEYTYPE_PREVIOUS'
155 |
156 | def _press_normal_key(self, key, down):
157 | try:
158 | key_code = character_translate_table[key.lower()]
159 | # kCGEventFlagMaskAlternate | kCGEventFlagMaskCommand | kCGEventFlagMaskControl | kCGEventFlagMaskShift
160 | event = Quartz.CGEventCreateKeyboardEvent(None, key_code, down)
161 | mkeyStr = ''
162 | for mkey in self.modifier_table:
163 | if self.modifier_table[mkey]:
164 | if len(mkeyStr)>1: mkeyStr = mkeyStr+' ^ '
165 | mkeyStr = mkeyStr+'Quartz.kCGEventFlagMask'+mkey
166 | if len(mkeyStr)>1: eval('Quartz.CGEventSetFlags(event, '+mkeyStr+')')
167 | Quartz.CGEventPost(Quartz.kCGHIDEventTap, event)
168 | if key.lower() == "shift":
169 | time.sleep(.1)
170 |
171 | except KeyError:
172 | raise RuntimeError("Key {} not implemented.".format(key))
173 |
174 | def _press_special_key(self, key, down):
175 | """ Helper method for special keys.
176 |
177 | Source: http://stackoverflow.com/questions/11045814/emulate-media-key-press-on-mac
178 | """
179 | key_code = special_key_translate_table[key]
180 |
181 | ev = NSEvent.otherEventWithType_location_modifierFlags_timestamp_windowNumber_context_subtype_data1_data2_(
182 | NSSystemDefined, # type
183 | (0,0), # location
184 | 0xa00 if down else 0xb00, # flags
185 | 0, # timestamp
186 | 0, # window
187 | 0, # ctx
188 | 8, # subtype
189 | (key_code << 16) | ((0xa if down else 0xb) << 8), # data1
190 | -1 # data2
191 | )
192 |
193 | Quartz.CGEventPost(0, ev.Quartz.CGEvent())
194 |
195 | class PyKeyboardEvent(PyKeyboardEventMeta):
196 | def run(self):
197 | tap = Quartz.CGEventTapCreate(
198 | Quartz.kCGSessionEventTap,
199 | Quartz.kCGHeadInsertEventTap,
200 | Quartz.kCGEventTapOptionDefault,
201 | Quartz.CGEventMaskBit(Quartz.kCGEventKeyDown) |
202 | Quartz.CGEventMaskBit(Quartz.kCGEventKeyUp),
203 | self.handler,
204 | None)
205 |
206 | loopsource = Quartz.CFMachPortCreateRunLoopSource(None, tap, 0)
207 | loop = Quartz.CFRunLoopGetCurrent()
208 | Quartz.CFRunLoopAddSource(loop, loopsource, Quartz.kCFRunLoopDefaultMode)
209 | Quartz.CGEventTapEnable(tap, True)
210 |
211 | while self.state:
212 | Quartz.CFRunLoopRunInMode(Quartz.kCFRunLoopDefaultMode, 5, False)
213 |
214 | def handler(self, proxy, type, event, refcon):
215 | key = Quartz.CGEventGetIntegerValueField(event, Quartz.kCGKeyboardEventKeycode)
216 | if type == Quartz.kCGEventKeyDown:
217 | self.key_press(key)
218 | elif type == Quartz.kCGEventKeyUp:
219 | self.key_release(key)
220 |
221 | if self.capture:
222 | Quartz.CGEventSetType(event, Quartz.kCGEventNull)
223 |
224 | return event
225 |
--------------------------------------------------------------------------------
/pykeyboard/mir.py:
--------------------------------------------------------------------------------
1 | #Copyright 2013 Paul Barton
2 | #
3 | #This program is free software: you can redistribute it and/or modify
4 | #it under the terms of the GNU General Public License as published by
5 | #the Free Software Foundation, either version 3 of the License, or
6 | #(at your option) any later version.
7 | #
8 | #This program is distributed in the hope that it will be useful,
9 | #but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | #GNU General Public License for more details.
12 | #
13 | #You should have received a copy of the GNU General Public License
14 | #along with this program. If not, see .
15 |
--------------------------------------------------------------------------------
/pykeyboard/wayland.py:
--------------------------------------------------------------------------------
1 | #Copyright 2013 Paul Barton
2 | #
3 | #This program is free software: you can redistribute it and/or modify
4 | #it under the terms of the GNU General Public License as published by
5 | #the Free Software Foundation, either version 3 of the License, or
6 | #(at your option) any later version.
7 | #
8 | #This program is distributed in the hope that it will be useful,
9 | #but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | #GNU General Public License for more details.
12 | #
13 | #You should have received a copy of the GNU General Public License
14 | #along with this program. If not, see .
15 |
--------------------------------------------------------------------------------
/pykeyboard/windows.py:
--------------------------------------------------------------------------------
1 | #Copyright 2013 Paul Barton
2 | #
3 | #This program is free software: you can redistribute it and/or modify
4 | #it under the terms of the GNU General Public License as published by
5 | #the Free Software Foundation, either version 3 of the License, or
6 | #(at your option) any later version.
7 | #
8 | #This program is distributed in the hope that it will be useful,
9 | #but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | #GNU General Public License for more details.
12 | #
13 | #You should have received a copy of the GNU General Public License
14 | #along with this program. If not, see .
15 |
16 | from ctypes import *
17 | import win32api
18 | from win32con import *
19 | import pythoncom
20 |
21 | from .base import PyKeyboardMeta, PyKeyboardEventMeta
22 |
23 | import time
24 |
25 |
26 | class SupportError(Exception):
27 | """For keys not supported on this system"""
28 | def __init__(self, value):
29 | self.value = value
30 |
31 | def __str__(self):
32 | return('The {0} key is not supported in Windows'.format(self.value))
33 |
34 |
35 | class PyKeyboard(PyKeyboardMeta):
36 | """
37 | The PyKeyboard implementation for Windows systems. This allows one to
38 | simulate keyboard input.
39 | """
40 | def __init__(self):
41 | PyKeyboardMeta.__init__(self)
42 | self.special_key_assignment()
43 |
44 | def press_key(self, character=''):
45 | """
46 | Press a given character key.
47 | """
48 | try:
49 | shifted = self.is_char_shifted(character)
50 | except AttributeError:
51 | win32api.keybd_event(character, 0, 0, 0)
52 | else:
53 | if shifted:
54 | win32api.keybd_event(self.shift_key, 0, 0, 0)
55 | char_vk = win32api.VkKeyScan(character)
56 | win32api.keybd_event(char_vk, 0, 0, 0)
57 |
58 | def release_key(self, character=''):
59 | """
60 | Release a given character key.
61 | """
62 | try:
63 | shifted = self.is_char_shifted(character)
64 | except AttributeError:
65 | win32api.keybd_event(character, 0, KEYEVENTF_KEYUP, 0)
66 | else:
67 | if shifted:
68 | win32api.keybd_event(self.shift_key, 0, KEYEVENTF_KEYUP, 0)
69 | char_vk = win32api.VkKeyScan(character)
70 | win32api.keybd_event(char_vk, 0, KEYEVENTF_KEYUP, 0)
71 |
72 | def special_key_assignment(self):
73 | """
74 | Special Key assignment for windows
75 | """
76 | #As defined by Microsoft, refer to:
77 | #http://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx
78 | self.backspace_key = VK_BACK
79 | self.tab_key = VK_TAB
80 | self.clear_key = VK_CLEAR
81 | self.return_key = VK_RETURN
82 | self.enter_key = self.return_key # Because many keyboards call it "Enter"
83 | self.shift_key = VK_SHIFT
84 | self.shift_l_key = VK_LSHIFT
85 | self.shift_r_key = VK_RSHIFT
86 | self.control_key = VK_CONTROL
87 | self.control_l_key = VK_LCONTROL
88 | self.control_r_key = VK_RCONTROL
89 | #Windows uses "menu" to refer to Alt...
90 | self.menu_key = VK_MENU
91 | self.alt_l_key = VK_LMENU
92 | self.alt_r_key = VK_RMENU
93 | self.altgr_key = VK_RMENU
94 | self.alt_key = self.alt_l_key
95 | self.pause_key = VK_PAUSE
96 | self.caps_lock_key = VK_CAPITAL
97 | self.capital_key = self.caps_lock_key
98 | self.num_lock_key = VK_NUMLOCK
99 | self.scroll_lock_key = VK_SCROLL
100 | #Windows Language Keys,
101 | self.kana_key = VK_KANA
102 | self.hangeul_key = VK_HANGEUL # old name - should be here for compatibility
103 | self.hangul_key = VK_HANGUL
104 | self.junjua_key = VK_JUNJA
105 | self.final_key = VK_FINAL
106 | self.hanja_key = VK_HANJA
107 | self.kanji_key = VK_KANJI
108 | self.convert_key = VK_CONVERT
109 | self.nonconvert_key = VK_NONCONVERT
110 | self.accept_key = VK_ACCEPT
111 | self.modechange_key = VK_MODECHANGE
112 | #More Keys
113 | self.escape_key = VK_ESCAPE
114 | self.space_key = VK_SPACE
115 | self.prior_key = VK_PRIOR
116 | self.next_key = VK_NEXT
117 | self.page_up_key = self.prior_key
118 | self.page_down_key = self.next_key
119 | self.home_key = VK_HOME
120 | self.up_key = VK_UP
121 | self.down_key = VK_DOWN
122 | self.left_key = VK_LEFT
123 | self.right_key = VK_RIGHT
124 | self.end_key = VK_END
125 | self.select_key = VK_SELECT
126 | self.print_key = VK_PRINT
127 | self.snapshot_key = VK_SNAPSHOT
128 | self.print_screen_key = self.snapshot_key
129 | self.execute_key = VK_EXECUTE
130 | self.insert_key = VK_INSERT
131 | self.delete_key = VK_DELETE
132 | self.help_key = VK_HELP
133 | self.windows_l_key = VK_LWIN
134 | self.super_l_key = self.windows_l_key
135 | self.windows_r_key = VK_RWIN
136 | self.super_r_key = self.windows_r_key
137 | self.apps_key = VK_APPS
138 | #Numpad
139 | self.keypad_keys = {'Space': None,
140 | 'Tab': None,
141 | 'Enter': None, # Needs Fixing
142 | 'F1': None,
143 | 'F2': None,
144 | 'F3': None,
145 | 'F4': None,
146 | 'Home': VK_NUMPAD7,
147 | 'Left': VK_NUMPAD4,
148 | 'Up': VK_NUMPAD8,
149 | 'Right': VK_NUMPAD6,
150 | 'Down': VK_NUMPAD2,
151 | 'Prior': None,
152 | 'Page_Up': VK_NUMPAD9,
153 | 'Next': None,
154 | 'Page_Down': VK_NUMPAD3,
155 | 'End': VK_NUMPAD1,
156 | 'Begin': None,
157 | 'Insert': VK_NUMPAD0,
158 | 'Delete': VK_DECIMAL,
159 | 'Equal': None, # Needs Fixing
160 | 'Multiply': VK_MULTIPLY,
161 | 'Add': VK_ADD,
162 | 'Separator': VK_SEPARATOR,
163 | 'Subtract': VK_SUBTRACT,
164 | 'Decimal': VK_DECIMAL,
165 | 'Divide': VK_DIVIDE,
166 | 0: VK_NUMPAD0,
167 | 1: VK_NUMPAD1,
168 | 2: VK_NUMPAD2,
169 | 3: VK_NUMPAD3,
170 | 4: VK_NUMPAD4,
171 | 5: VK_NUMPAD5,
172 | 6: VK_NUMPAD6,
173 | 7: VK_NUMPAD7,
174 | 8: VK_NUMPAD8,
175 | 9: VK_NUMPAD9}
176 | self.numpad_keys = self.keypad_keys
177 | #FKeys
178 | self.function_keys = [None, VK_F1, VK_F2, VK_F3, VK_F4, VK_F5, VK_F6,
179 | VK_F7, VK_F8, VK_F9, VK_F10, VK_F11, VK_F12,
180 | VK_F13, VK_F14, VK_F15, VK_F16, VK_F17, VK_F18,
181 | VK_F19, VK_F20, VK_F21, VK_F22, VK_F23, VK_F24,
182 | None, None, None, None, None, None, None, None,
183 | None, None, None] # Up to 36 as in x11
184 | #Miscellaneous
185 | self.cancel_key = VK_CANCEL
186 | self.break_key = self.cancel_key
187 | self.mode_switch_key = VK_MODECHANGE
188 | self.browser_back_key = VK_BROWSER_BACK
189 | self.browser_forward_key = VK_BROWSER_FORWARD
190 | self.processkey_key = VK_PROCESSKEY
191 | self.attn_key = VK_ATTN
192 | self.crsel_key = VK_CRSEL
193 | self.exsel_key = VK_EXSEL
194 | self.ereof_key = VK_EREOF
195 | self.play_key = VK_PLAY
196 | self.zoom_key = VK_ZOOM
197 | self.noname_key = VK_NONAME
198 | self.pa1_key = VK_PA1
199 | self.oem_clear_key = VK_OEM_CLEAR
200 | self.volume_mute_key = VK_VOLUME_MUTE
201 | self.volume_down_key = VK_VOLUME_DOWN
202 | self.volume_up_key = VK_VOLUME_UP
203 | self.media_next_track_key = VK_MEDIA_NEXT_TRACK
204 | self.media_prev_track_key = VK_MEDIA_PREV_TRACK
205 | self.media_play_pause_key = VK_MEDIA_PLAY_PAUSE
206 | self.begin_key = self.home_key
207 | #LKeys - Unsupported
208 | self.l_keys = [None] * 11
209 | #RKeys - Unsupported
210 | self.r_keys = [None] * 16
211 |
212 | #Other unsupported Keys from X11
213 | self.linefeed_key = None
214 | self.find_key = None
215 | self.meta_l_key = None
216 | self.meta_r_key = None
217 | self.sys_req_key = None
218 | self.hyper_l_key = None
219 | self.hyper_r_key = None
220 | self.undo_key = None
221 | self.redo_key = None
222 | self.script_switch_key = None
223 |
224 | class PyKeyboardEvent(PyKeyboardEventMeta):
225 | """
226 | The PyKeyboardEvent implementation for Windows Systems. This allows one
227 | to listen for keyboard input.
228 | """
229 | def __init__(self, diagnostic=False):
230 | self.diagnostic = diagnostic
231 |
232 | import pyWinhook as pyHook
233 |
234 | PyKeyboardEventMeta.__init__(self)
235 | self.hm = pyHook.HookManager()
236 | self.hc = pyHook.HookConstants()
237 |
238 | self.lock_meaning = None
239 |
240 | def run(self):
241 | """Begin listening for keyboard input events."""
242 | self.state = True
243 | self.hm.KeyAll = self.handler
244 | self.hm.HookKeyboard()
245 | while self.state:
246 | time.sleep(0.01)
247 | pythoncom.PumpWaitingMessages()
248 |
249 | def stop(self):
250 | """Stop listening for keyboard input events."""
251 | self.hm.UnhookKeyboard()
252 | self.state = False
253 |
254 | def handler(self, event):
255 | """Upper level handler of keyboard events."""
256 |
257 | if self.escape(event): # A chance to escape
258 | self.stop()
259 | elif self.diagnostic:
260 | self._diagnostic(event)
261 | else:
262 | self._tap(event)
263 | #This is needed according to the pyHook tutorials 'http://sourceforge.net/apps/mediawiki/pyhook/index.php?title=PyHook_Tutorial'
264 | return True
265 |
266 | def _tap(self, event):
267 | keycode = event.KeyID
268 | press_bool = (event.Message in [self.hc.WM_KEYDOWN, self.hc.WM_SYSKEYDOWN])
269 |
270 | #Not using event.GeyKey() because we want to differentiate between
271 | #KeyID and Ascii attributes of the event
272 | if event.Ascii != 0:
273 | character = chr(event.Ascii)
274 | else:
275 | character = self.hc.id_to_vk[keycode][3:]
276 |
277 | #TODO: Need to universalize keys between platforms. ie. 'Menu' -> 'Alt'
278 |
279 | self.tap(keycode, character, press_bool)
280 |
281 | def _diagnostic(self, event):
282 | """
283 | This method is employed instead of _tap() if the PyKeyboardEvent is
284 | initialized with diagnostic=True. This makes some basic testing quickly
285 | and easily available. It will print out information regarding the event
286 | instead of passing information along to self.tap()
287 | """
288 | print('\n---Keyboard Event Diagnostic---')
289 | print('MessageName:', event.MessageName)
290 | print('Message:', event.Message)
291 | print('Time:', event.Time)
292 | print('Window:', event.Window)
293 | print('WindowName:', event.WindowName)
294 | print('Ascii:', event.Ascii, ',', chr(event.Ascii))
295 | print('Key:', event.Key)
296 | print('KeyID:', event.KeyID)
297 | print('ScanCode:', event.ScanCode)
298 | print('Extended:', event.Extended)
299 | print('Injected:', event.Injected)
300 | print('Alt', event.Alt)
301 | print('Transition', event.Transition)
302 | print('---')
303 |
304 | def escape(self, event):
305 | return event.KeyID == VK_ESCAPE
306 |
307 | def toggle_shift_state(self): # This will be removed later
308 | '''Does toggling for the shift state.'''
309 | states = [1, 0]
310 | self.shift_state = states[self.shift_state]
311 |
312 | def toggle_alt_state(self): # This will be removed later
313 | '''Does toggling for the alt state.'''
314 | states = [2, None, 0]
315 | self.alt_state = states[self.alt_state]
316 |
317 | def configure_keys(self):
318 | """
319 | This does initial configuration for keyboard modifier state tracking
320 | including alias setting and keycode list construction.
321 | """
322 | pass
323 |
--------------------------------------------------------------------------------
/pykeyboard/x11.py:
--------------------------------------------------------------------------------
1 | #Copyright 2013 Paul Barton
2 | #
3 | #This program is free software: you can redistribute it and/or modify
4 | #it under the terms of the GNU General Public License as published by
5 | #the Free Software Foundation, either version 3 of the License, or
6 | #(at your option) any later version.
7 | #
8 | #This program is distributed in the hope that it will be useful,
9 | #but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | #GNU General Public License for more details.
12 | #
13 | #You should have received a copy of the GNU General Public License
14 | #along with this program. If not, see .
15 |
16 | from Xlib.display import Display
17 | from Xlib import X
18 | from Xlib.ext.xtest import fake_input
19 | from Xlib.ext import record
20 | from Xlib.protocol import rq
21 | import Xlib.XK
22 | import Xlib.keysymdef.xkb
23 |
24 | from .base import PyKeyboardMeta, PyKeyboardEventMeta
25 |
26 | import time
27 | import string
28 |
29 | from pymouse.x11 import display_manager
30 |
31 | from .x11_keysyms import KEYSYMS
32 |
33 |
34 | class PyKeyboard(PyKeyboardMeta):
35 | """
36 | The PyKeyboard implementation for X11 systems (mostly linux). This
37 | allows one to simulate keyboard input.
38 | """
39 | def __init__(self, display=None):
40 | PyKeyboardMeta.__init__(self)
41 | self.display = Display(display)
42 | self.display2 = Display(display)
43 | self.special_key_assignment()
44 |
45 | def _handle_key(self, character, event):
46 | """Handles either a key press or release, depending on ``event``.
47 |
48 | :param character: The key to handle. See :meth:`press_key` and
49 | :meth:`release_key` for information about this parameter.
50 |
51 | :param event: The *Xlib* event. This should be either
52 | :attr:`Xlib.X.KeyPress` or :attr:`Xlib.X.KeyRelease`
53 | """
54 | try:
55 | # Detect uppercase or shifted character
56 | shifted = self.is_char_shifted(character)
57 | except AttributeError:
58 | # Handle the case of integer keycode argument
59 | with display_manager(self.display) as d:
60 | fake_input(d, event, character)
61 | else:
62 | with display_manager(self.display) as d:
63 | if shifted:
64 | fake_input(d, event, self.shift_key)
65 | keycode = self.lookup_character_keycode(character)
66 | fake_input(d, event, keycode)
67 |
68 | def press_key(self, character=''):
69 | """
70 | Press a given character key. Also works with character keycodes as
71 | integers, but not keysyms.
72 | """
73 | self._handle_key(character, X.KeyPress)
74 |
75 | def release_key(self, character=''):
76 | """
77 | Release a given character key. Also works with character keycodes as
78 | integers, but not keysyms.
79 | """
80 | self._handle_key(character, X.KeyRelease)
81 |
82 | def special_key_assignment(self):
83 | """
84 | Determines the keycodes for common special keys on the keyboard. These
85 | are integer values and can be passed to the other key methods.
86 | Generally speaking, these are non-printable codes.
87 | """
88 | #This set of keys compiled using the X11 keysymdef.h file as reference
89 | #They comprise a relatively universal set of keys, though there may be
90 | #exceptions which may come up for other OSes and vendors. Countless
91 | #special cases exist which are not handled here, but may be extended.
92 | #TTY Function Keys
93 | self.backspace_key = self.lookup_character_keycode('BackSpace')
94 | self.tab_key = self.lookup_character_keycode('Tab')
95 | self.linefeed_key = self.lookup_character_keycode('Linefeed')
96 | self.clear_key = self.lookup_character_keycode('Clear')
97 | self.return_key = self.lookup_character_keycode('Return')
98 | self.enter_key = self.return_key # Because many keyboards call it "Enter"
99 | self.pause_key = self.lookup_character_keycode('Pause')
100 | self.scroll_lock_key = self.lookup_character_keycode('Scroll_Lock')
101 | self.sys_req_key = self.lookup_character_keycode('Sys_Req')
102 | self.escape_key = self.lookup_character_keycode('Escape')
103 | self.delete_key = self.lookup_character_keycode('Delete')
104 | #Modifier Keys
105 | self.shift_l_key = self.lookup_character_keycode('Shift_L')
106 | self.shift_r_key = self.lookup_character_keycode('Shift_R')
107 | self.shift_key = self.shift_l_key # Default Shift is left Shift
108 | self.alt_l_key = self.lookup_character_keycode('Alt_L')
109 | self.alt_r_key = self.lookup_character_keycode('Alt_R')
110 | self.altgr_key = self.lookup_character_keycode('ISO_Level3_Shift')
111 | self.alt_key = self.alt_l_key # Default Alt is left Alt
112 | self.control_l_key = self.lookup_character_keycode('Control_L')
113 | self.control_r_key = self.lookup_character_keycode('Control_R')
114 | self.control_key = self.control_l_key # Default Ctrl is left Ctrl
115 | self.caps_lock_key = self.lookup_character_keycode('Caps_Lock')
116 | self.capital_key = self.caps_lock_key # Some may know it as Capital
117 | self.shift_lock_key = self.lookup_character_keycode('Shift_Lock')
118 | self.meta_l_key = self.lookup_character_keycode('Meta_L')
119 | self.meta_r_key = self.lookup_character_keycode('Meta_R')
120 | self.super_l_key = self.lookup_character_keycode('Super_L')
121 | self.windows_l_key = self.super_l_key # Cross-support; also it's printed there
122 | self.super_r_key = self.lookup_character_keycode('Super_R')
123 | self.windows_r_key = self.super_r_key # Cross-support; also it's printed there
124 | self.hyper_l_key = self.lookup_character_keycode('Hyper_L')
125 | self.hyper_r_key = self.lookup_character_keycode('Hyper_R')
126 | #Cursor Control and Motion
127 | self.home_key = self.lookup_character_keycode('Home')
128 | self.up_key = self.lookup_character_keycode('Up')
129 | self.down_key = self.lookup_character_keycode('Down')
130 | self.left_key = self.lookup_character_keycode('Left')
131 | self.right_key = self.lookup_character_keycode('Right')
132 | self.end_key = self.lookup_character_keycode('End')
133 | self.begin_key = self.lookup_character_keycode('Begin')
134 | self.page_up_key = self.lookup_character_keycode('Page_Up')
135 | self.page_down_key = self.lookup_character_keycode('Page_Down')
136 | self.prior_key = self.lookup_character_keycode('Prior')
137 | self.next_key = self.lookup_character_keycode('Next')
138 | #Misc Functions
139 | self.select_key = self.lookup_character_keycode('Select')
140 | self.print_key = self.lookup_character_keycode('Print')
141 | self.print_screen_key = self.print_key # Seems to be the same thing
142 | self.snapshot_key = self.print_key # Another name for printscreen
143 | self.execute_key = self.lookup_character_keycode('Execute')
144 | self.insert_key = self.lookup_character_keycode('Insert')
145 | self.undo_key = self.lookup_character_keycode('Undo')
146 | self.redo_key = self.lookup_character_keycode('Redo')
147 | self.menu_key = self.lookup_character_keycode('Menu')
148 | self.apps_key = self.menu_key # Windows...
149 | self.find_key = self.lookup_character_keycode('Find')
150 | self.cancel_key = self.lookup_character_keycode('Cancel')
151 | self.help_key = self.lookup_character_keycode('Help')
152 | self.break_key = self.lookup_character_keycode('Break')
153 | self.mode_switch_key = self.lookup_character_keycode('Mode_switch')
154 | self.script_switch_key = self.lookup_character_keycode('script_switch')
155 | self.num_lock_key = self.lookup_character_keycode('Num_Lock')
156 | #Keypad Keys: Dictionary structure
157 | keypad = ['Space', 'Tab', 'Enter', 'F1', 'F2', 'F3', 'F4', 'Home',
158 | 'Left', 'Up', 'Right', 'Down', 'Prior', 'Page_Up', 'Next',
159 | 'Page_Down', 'End', 'Begin', 'Insert', 'Delete', 'Equal',
160 | 'Multiply', 'Add', 'Separator', 'Subtract', 'Decimal',
161 | 'Divide', 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
162 | self.keypad_keys = dict((k, self.lookup_character_keycode('KP_'+str(k))) for k in keypad)
163 | self.numpad_keys = self.keypad_keys
164 | #Function Keys/ Auxilliary Keys
165 | #FKeys
166 | self.function_keys = [None] + [self.lookup_character_keycode('F'+str(i)) for i in range(1,36)]
167 | #LKeys
168 | self.l_keys = [None] + [self.lookup_character_keycode('L'+str(i)) for i in range(1,11)]
169 | #RKeys
170 | self.r_keys = [None] + [self.lookup_character_keycode('R'+str(i)) for i in range(1,16)]
171 |
172 | #Unsupported keys from windows
173 | self.kana_key = None
174 | self.hangeul_key = None # old name - should be here for compatibility
175 | self.hangul_key = None
176 | self.junjua_key = None
177 | self.final_key = None
178 | self.hanja_key = None
179 | self.kanji_key = None
180 | self.convert_key = None
181 | self.nonconvert_key = None
182 | self.accept_key = None
183 | self.modechange_key = None
184 | self.sleep_key = None
185 |
186 | def lookup_character_keycode(self, character):
187 | """
188 | Looks up the keysym for the character then returns the keycode mapping
189 | for that keysym.
190 | """
191 | keysym = Xlib.XK.string_to_keysym(character)
192 | if not keysym:
193 | try:
194 | keysym = getattr(Xlib.keysymdef.xkb, 'XK_' + character, 0)
195 | except:
196 | keysym = 0
197 | if not keysym:
198 | keysym = Xlib.XK.string_to_keysym(KEYSYMS[character])
199 | return self.display.keysym_to_keycode(keysym)
200 |
201 |
202 | class PyKeyboardEvent(PyKeyboardEventMeta):
203 | """
204 | The PyKeyboardEvent implementation for X11 systems (mostly linux). This
205 | allows one to listen for keyboard input.
206 | """
207 | def __init__(self, capture=False, display=None):
208 | self.display = Display(display)
209 | self.display2 = Display(display)
210 | self.ctx = self.display2.record_create_context(
211 | 0,
212 | [record.AllClients],
213 | [{
214 | 'core_requests': (0, 0),
215 | 'core_replies': (0, 0),
216 | 'ext_requests': (0, 0, 0, 0),
217 | 'ext_replies': (0, 0, 0, 0),
218 | 'delivered_events': (0, 0),
219 | 'device_events': (X.KeyPress, X.KeyRelease),
220 | 'errors': (0, 0),
221 | 'client_started': False,
222 | 'client_died': False,
223 | }])
224 |
225 | self.lock_meaning = None
226 |
227 | #Get these dictionaries for converting keysyms and strings
228 | self.keysym_to_string, self.string_to_keysym = self.get_translation_dicts()
229 |
230 | #Identify and register special groups of keys
231 | self.modifier_keycodes = {}
232 | self.all_mod_keycodes = []
233 | self.keypad_keycodes = []
234 | #self.configure_keys()
235 |
236 | #Direct access to the display's keycode-to-keysym array
237 | #print('Keycode to Keysym map')
238 | #for i in range(len(self.display._keymap_codes)):
239 | # print('{0}: {1}'.format(i, self.display._keymap_codes[i]))
240 |
241 | PyKeyboardEventMeta.__init__(self, capture)
242 |
243 | def run(self):
244 | """Begin listening for keyboard input events."""
245 | self.state = True
246 | if self.capture:
247 | self.display2.screen().root.grab_keyboard(X.KeyPressMask | X.KeyReleaseMask, X.GrabModeAsync, X.GrabModeAsync, X.CurrentTime)
248 |
249 | self.display2.record_enable_context(self.ctx, self.handler)
250 | self.display2.record_free_context(self.ctx)
251 |
252 | def stop(self):
253 | """Stop listening for keyboard input events."""
254 | self.state = False
255 | with display_manager(self.display) as d:
256 | d.record_disable_context(self.ctx)
257 | d.ungrab_keyboard(X.CurrentTime)
258 | with display_manager(self.display2):
259 | d.record_disable_context(self.ctx)
260 | d.ungrab_keyboard(X.CurrentTime)
261 |
262 | def handler(self, reply):
263 | """Upper level handler of keyboard events."""
264 | data = reply.data
265 | while len(data):
266 | event, data = rq.EventField(None).parse_binary_value(data, self.display.display, None, None)
267 | if self.escape(event): # Quit if this returns True
268 | self.stop()
269 | else:
270 | self._tap(event)
271 |
272 | def _tap(self, event):
273 | keycode = event.detail
274 | press_bool = (event.type == X.KeyPress)
275 |
276 | #Detect modifier states from event.state
277 | for mod, bit in self.modifier_bits.items():
278 | self.modifiers[mod] = event.state & bit
279 |
280 | if keycode in self.all_mod_keycodes:
281 | keysym = self.display.keycode_to_keysym(keycode, 0)
282 | character = self.keysym_to_string[keysym]
283 | else:
284 | character = self.lookup_char_from_keycode(keycode)
285 |
286 | #All key events get passed to self.tap()
287 | self.tap(keycode,
288 | character,
289 | press=press_bool)
290 |
291 | def lookup_char_from_keycode(self, keycode):
292 | """
293 | This will conduct a lookup of the character or string associated with a
294 | given keycode.
295 | """
296 |
297 | #TODO: Logic should be strictly adapted from X11's src/KeyBind.c
298 | #Right now the logic is based off of
299 | #http://tronche.com/gui/x/xlib/input/keyboard-encoding.html
300 | #Which I suspect is not the whole story and may likely cause bugs
301 |
302 | keysym_index = 0
303 | #TODO: Display's Keysyms per keycode count? Do I need this?
304 | #If the Num_Lock is on, and the keycode corresponds to the keypad
305 | if self.modifiers['Num_Lock'] and keycode in self.keypad_keycodes:
306 | if self.modifiers['Shift'] or self.modifiers['Shift_Lock']:
307 | keysym_index = 0
308 | else:
309 | keysym_index = 1
310 |
311 | elif not self.modifiers['Shift'] and self.modifiers['Caps_Lock']:
312 | #Use the first keysym if uppercase or uncased
313 | #Use the uppercase keysym if the first is lowercase (second)
314 | keysym_index = 0
315 | keysym = self.display.keycode_to_keysym(keycode, keysym_index)
316 | #TODO: Support Unicode, Greek, and special latin characters
317 | if keysym & 0x7f == keysym and chr(keysym) in 'abcdefghijklmnopqrstuvwxyz':
318 | keysym_index = 1
319 |
320 | elif self.modifiers['Shift'] and self.modifiers['Caps_Lock']:
321 | keysym_index = 1
322 | keysym = self.display.keycode_to_keysym(keycode, keysym_index)
323 | #TODO: Support Unicode, Greek, and special latin characters
324 | if keysym & 0x7f == keysym and chr(keysym) in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ':
325 | keysym_index = 0
326 |
327 | elif self.modifiers['Shift'] or self.modifiers['Shift_Lock']:
328 | keysym_index = 1
329 |
330 | if self.modifiers['Mode_switch']:
331 | keysym_index += 2
332 |
333 | #Finally! Get the keysym
334 | keysym = self.display.keycode_to_keysym(keycode, keysym_index)
335 |
336 | #If the character is ascii printable, return that character
337 | if keysym & 0x7f == keysym and self.ascii_printable(keysym):
338 | return chr(keysym)
339 |
340 | #If the character was not printable, look for its name
341 | try:
342 | char = self.keysym_to_string[keysym]
343 | except KeyError:
344 | print('Unable to determine character.')
345 | print('Keycode: {0} KeySym {1}'.format(keycode, keysym))
346 | return None
347 | else:
348 | return char
349 |
350 | def escape(self, event):
351 | if event.detail == self.lookup_character_keycode('Escape'):
352 | return True
353 | return False
354 |
355 | def configure_keys(self):
356 | """
357 | This function locates the keycodes corresponding to special groups of
358 | keys and creates data structures of them for use by the PyKeyboardEvent
359 | instance; including the keypad keys and the modifiers.
360 |
361 | The keycodes pertaining to the keyboard modifiers are assigned by the
362 | modifier name in a dictionary. This dictionary can be accessed in the
363 | following manner:
364 | self.modifier_keycodes['Shift'] # All keycodes for Shift Masking
365 |
366 | It also assigns certain named modifiers (Alt, Num_Lock, Super), which
367 | may be dynamically assigned to Mod1 - Mod5 on different platforms. This
368 | should generally allow the user to do the following lookups on any
369 | system:
370 | self.modifier_keycodes['Alt'] # All keycodes for Alt Masking
371 | self.modifiers['Alt'] # State of Alt mask, non-zero if "ON"
372 | """
373 | modifier_mapping = self.display.get_modifier_mapping()
374 | all_mod_keycodes = []
375 | mod_keycodes = {}
376 | mod_index = [('Shift', X.ShiftMapIndex), ('Lock', X.LockMapIndex),
377 | ('Control', X.ControlMapIndex), ('Mod1', X.Mod1MapIndex),
378 | ('Mod2', X.Mod2MapIndex), ('Mod3', X.Mod3MapIndex),
379 | ('Mod4', X.Mod4MapIndex), ('Mod5', X.Mod5MapIndex)]
380 | #This gets the list of all keycodes per Modifier, assigns to name
381 | for name, index in mod_index:
382 | codes = [v for v in list(modifier_mapping[index]) if v]
383 | mod_keycodes[name] = codes
384 | all_mod_keycodes += codes
385 |
386 | def lookup_keycode(string):
387 | keysym = self.string_to_keysym[string]
388 | return self.display.keysym_to_keycode(keysym)
389 |
390 | #Dynamically assign Lock to Caps_Lock, Shift_Lock, Alt, Num_Lock, Super,
391 | #and mode switch. Set in both mod_keycodes and self.modifier_bits
392 |
393 | #Try to assign Lock to Caps_Lock or Shift_Lock
394 | shift_lock_keycode = lookup_keycode('Shift_Lock')
395 | caps_lock_keycode = lookup_keycode('Caps_Lock')
396 |
397 | if shift_lock_keycode in mod_keycodes['Lock']:
398 | mod_keycodes['Shift_Lock'] = [shift_lock_keycode]
399 | self.modifier_bits['Shift_Lock'] = self.modifier_bits['Lock']
400 | self.lock_meaning = 'Shift_Lock'
401 | elif caps_lock_keycode in mod_keycodes['Lock']:
402 | mod_keycodes['Caps_Lock'] = [caps_lock_keycode]
403 | self.modifier_bits['Caps_Lock'] = self.modifier_bits['Lock']
404 | self.lock_meaning = 'Caps_Lock'
405 | else:
406 | self.lock_meaning = None
407 | #print('Lock is bound to {0}'.format(self.lock_meaning))
408 |
409 | #Need to find out which Mod# to use for Alt, Num_Lock, Super, and
410 | #Mode_switch
411 | num_lock_keycodes = [lookup_keycode('Num_Lock')]
412 | alt_keycodes = [lookup_keycode(i) for i in ['Alt_L', 'Alt_R']]
413 | super_keycodes = [lookup_keycode(i) for i in ['Super_L', 'Super_R']]
414 | mode_switch_keycodes = [lookup_keycode('Mode_switch')]
415 |
416 | #Detect Mod number for Alt, Num_Lock, and Super
417 | for name, keycodes in list(mod_keycodes.items()):
418 | for alt_key in alt_keycodes:
419 | if alt_key in keycodes:
420 | mod_keycodes['Alt'] = keycodes
421 | self.modifier_bits['Alt'] = self.modifier_bits[name]
422 | for num_lock_key in num_lock_keycodes:
423 | if num_lock_key in keycodes:
424 | mod_keycodes['Num_Lock'] = keycodes
425 | self.modifier_bits['Num_Lock'] = self.modifier_bits[name]
426 | for super_key in super_keycodes:
427 | if super_key in keycodes:
428 | mod_keycodes['Super'] = keycodes
429 | self.modifier_bits['Super'] = self.modifier_bits[name]
430 | for mode_switch_key in mode_switch_keycodes:
431 | if mode_switch_key in keycodes:
432 | mod_keycodes['Mode_switch'] = keycodes
433 | self.modifier_bits['Mode_switch'] = self.modifier_bits[name]
434 |
435 | #Assign the mod_keycodes to a local variable for access
436 | self.modifier_keycodes = mod_keycodes
437 | self.all_mod_keycodes = all_mod_keycodes
438 |
439 | #TODO: Determine if this might fail, perhaps iterate through the mapping
440 | #and identify all keycodes with registered keypad keysyms?
441 |
442 | #Acquire the full list of keypad keycodes
443 | self.keypad_keycodes = []
444 | keypad = ['Space', 'Tab', 'Enter', 'F1', 'F2', 'F3', 'F4', 'Home',
445 | 'Left', 'Up', 'Right', 'Down', 'Prior', 'Page_Up', 'Next',
446 | 'Page_Down', 'End', 'Begin', 'Insert', 'Delete', 'Equal',
447 | 'Multiply', 'Add', 'Separator', 'Subtract', 'Decimal',
448 | 'Divide', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
449 | for keyname in keypad:
450 | keypad_keycode = self.lookup_character_keycode('KP_' + keyname)
451 | self.keypad_keycodes.append(keypad_keycode)
452 |
453 | def lookup_character_keycode(self, character):
454 | """
455 | Looks up the keysym for the character then returns the keycode mapping
456 | for that keysym.
457 | """
458 | keysym = self.string_to_keysym.get(character, 0)
459 | if keysym == 0:
460 | keysym = self.string_to_keysym.get(KEYSYMS[character], 0)
461 | return self.display.keysym_to_keycode(keysym)
462 |
463 | def get_translation_dicts(self):
464 | """
465 | Returns dictionaries for the translation of keysyms to strings and from
466 | strings to keysyms.
467 | """
468 | keysym_to_string_dict = {}
469 | string_to_keysym_dict = {}
470 | #XK loads latin1 and miscellany on its own; load latin2-4 and greek
471 | Xlib.XK.load_keysym_group('latin2')
472 | Xlib.XK.load_keysym_group('latin3')
473 | Xlib.XK.load_keysym_group('latin4')
474 | Xlib.XK.load_keysym_group('greek')
475 |
476 | #Make a standard dict and the inverted dict
477 | for string, keysym in Xlib.XK.__dict__.items():
478 | if string.startswith('XK_'):
479 | string_to_keysym_dict[string[3:]] = keysym
480 | keysym_to_string_dict[keysym] = string[3:]
481 | return keysym_to_string_dict, string_to_keysym_dict
482 |
483 | def ascii_printable(self, keysym):
484 | """
485 | If the keysym corresponds to a non-printable ascii character this will
486 | return False. If it is printable, then True will be returned.
487 |
488 | ascii 11 (vertical tab) and ascii 12 are printable, chr(11) and chr(12)
489 | will return '\x0b' and '\x0c' respectively.
490 | """
491 | if 0 <= keysym < 9:
492 | return False
493 | elif 13 < keysym < 32:
494 | return False
495 | elif keysym > 126:
496 | return False
497 | else:
498 | return True
499 |
--------------------------------------------------------------------------------
/pykeyboard/x11_keysyms.py:
--------------------------------------------------------------------------------
1 | # coding: utf-8
2 | # Copyright 2015 Moses Palmér
3 | #
4 | # This program is free software: you can redistribute it and/or modify it
5 | # under the terms of the GNU General Public License as published by the Free
6 | # Software Foundation, either version 3 of the License, or (at your option)
7 | # any later version.
8 | #
9 | # This program is distributed in the hope that it will be useful, but WITHOUT
10 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11 | # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12 | # details.
13 | #
14 | # You should have received a copy of the GNU General Public License along with
15 | # this program. If not, see .
16 |
17 | KEYSYMS = {
18 | u'\u0020': 'space',
19 | u'\u0021': 'exclam',
20 | u'\u0022': 'quotedbl',
21 | u'\u0023': 'numbersign',
22 | u'\u0024': 'dollar',
23 | u'\u0025': 'percent',
24 | u'\u0026': 'ampersand',
25 | u'\u0027': 'apostrophe',
26 | u'\u0027': 'quoteright',
27 | u'\u0028': 'parenleft',
28 | u'\u0029': 'parenright',
29 | u'\u002a': 'asterisk',
30 | u'\u002b': 'plus',
31 | u'\u002c': 'comma',
32 | u'\u002d': 'minus',
33 | u'\u002e': 'period',
34 | u'\u002f': 'slash',
35 | u'\u0030': '0',
36 | u'\u0031': '1',
37 | u'\u0032': '2',
38 | u'\u0033': '3',
39 | u'\u0034': '4',
40 | u'\u0035': '5',
41 | u'\u0036': '6',
42 | u'\u0037': '7',
43 | u'\u0038': '8',
44 | u'\u0039': '9',
45 | u'\u003a': 'colon',
46 | u'\u003b': 'semicolon',
47 | u'\u003c': 'less',
48 | u'\u003d': 'equal',
49 | u'\u003e': 'greater',
50 | u'\u003f': 'question',
51 | u'\u0040': 'at',
52 | u'\u0041': 'A',
53 | u'\u0042': 'B',
54 | u'\u0043': 'C',
55 | u'\u0044': 'D',
56 | u'\u0045': 'E',
57 | u'\u0046': 'F',
58 | u'\u0047': 'G',
59 | u'\u0048': 'H',
60 | u'\u0049': 'I',
61 | u'\u004a': 'J',
62 | u'\u004b': 'K',
63 | u'\u004c': 'L',
64 | u'\u004d': 'M',
65 | u'\u004e': 'N',
66 | u'\u004f': 'O',
67 | u'\u0050': 'P',
68 | u'\u0051': 'Q',
69 | u'\u0052': 'R',
70 | u'\u0053': 'S',
71 | u'\u0054': 'T',
72 | u'\u0055': 'U',
73 | u'\u0056': 'V',
74 | u'\u0057': 'W',
75 | u'\u0058': 'X',
76 | u'\u0059': 'Y',
77 | u'\u005a': 'Z',
78 | u'\u005b': 'bracketleft',
79 | u'\u005c': 'backslash',
80 | u'\u005d': 'bracketright',
81 | u'\u005e': 'asciicircum',
82 | u'\u005f': 'underscore',
83 | u'\u0060': 'grave',
84 | u'\u0060': 'quoteleft',
85 | u'\u0061': 'a',
86 | u'\u0062': 'b',
87 | u'\u0063': 'c',
88 | u'\u0064': 'd',
89 | u'\u0065': 'e',
90 | u'\u0066': 'f',
91 | u'\u0067': 'g',
92 | u'\u0068': 'h',
93 | u'\u0069': 'i',
94 | u'\u006a': 'j',
95 | u'\u006b': 'k',
96 | u'\u006c': 'l',
97 | u'\u006d': 'm',
98 | u'\u006e': 'n',
99 | u'\u006f': 'o',
100 | u'\u0070': 'p',
101 | u'\u0071': 'q',
102 | u'\u0072': 'r',
103 | u'\u0073': 's',
104 | u'\u0074': 't',
105 | u'\u0075': 'u',
106 | u'\u0076': 'v',
107 | u'\u0077': 'w',
108 | u'\u0078': 'x',
109 | u'\u0079': 'y',
110 | u'\u007a': 'z',
111 | u'\u007b': 'braceleft',
112 | u'\u007c': 'bar',
113 | u'\u007d': 'braceright',
114 | u'\u007e': 'asciitilde',
115 | u'\u00a0': 'nobreakspace',
116 | u'\u00a1': 'exclamdown',
117 | u'\u00a2': 'cent',
118 | u'\u00a3': 'sterling',
119 | u'\u00a4': 'currency',
120 | u'\u00a5': 'yen',
121 | u'\u00a6': 'brokenbar',
122 | u'\u00a7': 'section',
123 | u'\u00a8': 'diaeresis',
124 | u'\u00a9': 'copyright',
125 | u'\u00aa': 'ordfeminine',
126 | u'\u00ab': 'guillemotleft',
127 | u'\u00ac': 'notsign',
128 | u'\u00ad': 'hyphen',
129 | u'\u00ae': 'registered',
130 | u'\u00af': 'macron',
131 | u'\u00b0': 'degree',
132 | u'\u00b1': 'plusminus',
133 | u'\u00b2': 'twosuperior',
134 | u'\u00b3': 'threesuperior',
135 | u'\u00b4': 'acute',
136 | u'\u00b5': 'mu',
137 | u'\u00b6': 'paragraph',
138 | u'\u00b7': 'periodcentered',
139 | u'\u00b8': 'cedilla',
140 | u'\u00b9': 'onesuperior',
141 | u'\u00ba': 'masculine',
142 | u'\u00bb': 'guillemotright',
143 | u'\u00bc': 'onequarter',
144 | u'\u00bd': 'onehalf',
145 | u'\u00be': 'threequarters',
146 | u'\u00bf': 'questiondown',
147 | u'\u00c0': 'Agrave',
148 | u'\u00c1': 'Aacute',
149 | u'\u00c2': 'Acircumflex',
150 | u'\u00c3': 'Atilde',
151 | u'\u00c4': 'Adiaeresis',
152 | u'\u00c5': 'Aring',
153 | u'\u00c6': 'AE',
154 | u'\u00c7': 'Ccedilla',
155 | u'\u00c8': 'Egrave',
156 | u'\u00c9': 'Eacute',
157 | u'\u00ca': 'Ecircumflex',
158 | u'\u00cb': 'Ediaeresis',
159 | u'\u00cc': 'Igrave',
160 | u'\u00cd': 'Iacute',
161 | u'\u00ce': 'Icircumflex',
162 | u'\u00cf': 'Idiaeresis',
163 | u'\u00d0': 'ETH',
164 | u'\u00d0': 'Eth',
165 | u'\u00d1': 'Ntilde',
166 | u'\u00d2': 'Ograve',
167 | u'\u00d3': 'Oacute',
168 | u'\u00d4': 'Ocircumflex',
169 | u'\u00d5': 'Otilde',
170 | u'\u00d6': 'Odiaeresis',
171 | u'\u00d7': 'multiply',
172 | u'\u00d8': 'Ooblique',
173 | u'\u00d9': 'Ugrave',
174 | u'\u00da': 'Uacute',
175 | u'\u00db': 'Ucircumflex',
176 | u'\u00dc': 'Udiaeresis',
177 | u'\u00dd': 'Yacute',
178 | u'\u00de': 'THORN',
179 | u'\u00de': 'Thorn',
180 | u'\u00df': 'ssharp',
181 | u'\u00e0': 'agrave',
182 | u'\u00e1': 'aacute',
183 | u'\u00e2': 'acircumflex',
184 | u'\u00e3': 'atilde',
185 | u'\u00e4': 'adiaeresis',
186 | u'\u00e5': 'aring',
187 | u'\u00e6': 'ae',
188 | u'\u00e7': 'ccedilla',
189 | u'\u00e8': 'egrave',
190 | u'\u00e9': 'eacute',
191 | u'\u00ea': 'ecircumflex',
192 | u'\u00eb': 'ediaeresis',
193 | u'\u00ec': 'igrave',
194 | u'\u00ed': 'iacute',
195 | u'\u00ee': 'icircumflex',
196 | u'\u00ef': 'idiaeresis',
197 | u'\u00f0': 'eth',
198 | u'\u00f1': 'ntilde',
199 | u'\u00f2': 'ograve',
200 | u'\u00f3': 'oacute',
201 | u'\u00f4': 'ocircumflex',
202 | u'\u00f5': 'otilde',
203 | u'\u00f6': 'odiaeresis',
204 | u'\u00f7': 'division',
205 | u'\u00f8': 'oslash',
206 | u'\u00f9': 'ugrave',
207 | u'\u00fa': 'uacute',
208 | u'\u00fb': 'ucircumflex',
209 | u'\u00fc': 'udiaeresis',
210 | u'\u00fd': 'yacute',
211 | u'\u00fe': 'thorn',
212 | u'\u00ff': 'ydiaeresis',
213 | u'\u0104': 'Aogonek',
214 | u'\u02d8': 'breve',
215 | u'\u0141': 'Lstroke',
216 | u'\u013d': 'Lcaron',
217 | u'\u015a': 'Sacute',
218 | u'\u0160': 'Scaron',
219 | u'\u015e': 'Scedilla',
220 | u'\u0164': 'Tcaron',
221 | u'\u0179': 'Zacute',
222 | u'\u017d': 'Zcaron',
223 | u'\u017b': 'Zabovedot',
224 | u'\u0105': 'aogonek',
225 | u'\u02db': 'ogonek',
226 | u'\u0142': 'lstroke',
227 | u'\u013e': 'lcaron',
228 | u'\u015b': 'sacute',
229 | u'\u02c7': 'caron',
230 | u'\u0161': 'scaron',
231 | u'\u015f': 'scedilla',
232 | u'\u0165': 'tcaron',
233 | u'\u017a': 'zacute',
234 | u'\u02dd': 'doubleacute',
235 | u'\u017e': 'zcaron',
236 | u'\u017c': 'zabovedot',
237 | u'\u0154': 'Racute',
238 | u'\u0102': 'Abreve',
239 | u'\u0139': 'Lacute',
240 | u'\u0106': 'Cacute',
241 | u'\u010c': 'Ccaron',
242 | u'\u0118': 'Eogonek',
243 | u'\u011a': 'Ecaron',
244 | u'\u010e': 'Dcaron',
245 | u'\u0110': 'Dstroke',
246 | u'\u0143': 'Nacute',
247 | u'\u0147': 'Ncaron',
248 | u'\u0150': 'Odoubleacute',
249 | u'\u0158': 'Rcaron',
250 | u'\u016e': 'Uring',
251 | u'\u0170': 'Udoubleacute',
252 | u'\u0162': 'Tcedilla',
253 | u'\u0155': 'racute',
254 | u'\u0103': 'abreve',
255 | u'\u013a': 'lacute',
256 | u'\u0107': 'cacute',
257 | u'\u010d': 'ccaron',
258 | u'\u0119': 'eogonek',
259 | u'\u011b': 'ecaron',
260 | u'\u010f': 'dcaron',
261 | u'\u0111': 'dstroke',
262 | u'\u0144': 'nacute',
263 | u'\u0148': 'ncaron',
264 | u'\u0151': 'odoubleacute',
265 | u'\u0159': 'rcaron',
266 | u'\u016f': 'uring',
267 | u'\u0171': 'udoubleacute',
268 | u'\u0163': 'tcedilla',
269 | u'\u02d9': 'abovedot',
270 | u'\u0126': 'Hstroke',
271 | u'\u0124': 'Hcircumflex',
272 | u'\u0130': 'Iabovedot',
273 | u'\u011e': 'Gbreve',
274 | u'\u0134': 'Jcircumflex',
275 | u'\u0127': 'hstroke',
276 | u'\u0125': 'hcircumflex',
277 | u'\u0131': 'idotless',
278 | u'\u011f': 'gbreve',
279 | u'\u0135': 'jcircumflex',
280 | u'\u010a': 'Cabovedot',
281 | u'\u0108': 'Ccircumflex',
282 | u'\u0120': 'Gabovedot',
283 | u'\u011c': 'Gcircumflex',
284 | u'\u016c': 'Ubreve',
285 | u'\u015c': 'Scircumflex',
286 | u'\u010b': 'cabovedot',
287 | u'\u0109': 'ccircumflex',
288 | u'\u0121': 'gabovedot',
289 | u'\u011d': 'gcircumflex',
290 | u'\u016d': 'ubreve',
291 | u'\u015d': 'scircumflex',
292 | u'\u0138': 'kra',
293 | u'\u0156': 'Rcedilla',
294 | u'\u0128': 'Itilde',
295 | u'\u013b': 'Lcedilla',
296 | u'\u0112': 'Emacron',
297 | u'\u0122': 'Gcedilla',
298 | u'\u0166': 'Tslash',
299 | u'\u0157': 'rcedilla',
300 | u'\u0129': 'itilde',
301 | u'\u013c': 'lcedilla',
302 | u'\u0113': 'emacron',
303 | u'\u0123': 'gcedilla',
304 | u'\u0167': 'tslash',
305 | u'\u014a': 'ENG',
306 | u'\u014b': 'eng',
307 | u'\u0100': 'Amacron',
308 | u'\u012e': 'Iogonek',
309 | u'\u0116': 'Eabovedot',
310 | u'\u012a': 'Imacron',
311 | u'\u0145': 'Ncedilla',
312 | u'\u014c': 'Omacron',
313 | u'\u0136': 'Kcedilla',
314 | u'\u0172': 'Uogonek',
315 | u'\u0168': 'Utilde',
316 | u'\u016a': 'Umacron',
317 | u'\u0101': 'amacron',
318 | u'\u012f': 'iogonek',
319 | u'\u0117': 'eabovedot',
320 | u'\u012b': 'imacron',
321 | u'\u0146': 'ncedilla',
322 | u'\u014d': 'omacron',
323 | u'\u0137': 'kcedilla',
324 | u'\u0173': 'uogonek',
325 | u'\u0169': 'utilde',
326 | u'\u016b': 'umacron',
327 | u'\u203e': 'overline',
328 | u'\u3002': 'kana_fullstop',
329 | u'\u300c': 'kana_openingbracket',
330 | u'\u300d': 'kana_closingbracket',
331 | u'\u3001': 'kana_comma',
332 | u'\u30fb': 'kana_conjunctive',
333 | u'\u30f2': 'kana_WO',
334 | u'\u30a1': 'kana_a',
335 | u'\u30a3': 'kana_i',
336 | u'\u30a5': 'kana_u',
337 | u'\u30a7': 'kana_e',
338 | u'\u30a9': 'kana_o',
339 | u'\u30e3': 'kana_ya',
340 | u'\u30e5': 'kana_yu',
341 | u'\u30e7': 'kana_yo',
342 | u'\u30c3': 'kana_tsu',
343 | u'\u30fc': 'prolongedsound',
344 | u'\u30a2': 'kana_A',
345 | u'\u30a4': 'kana_I',
346 | u'\u30a6': 'kana_U',
347 | u'\u30a8': 'kana_E',
348 | u'\u30aa': 'kana_O',
349 | u'\u30ab': 'kana_KA',
350 | u'\u30ad': 'kana_KI',
351 | u'\u30af': 'kana_KU',
352 | u'\u30b1': 'kana_KE',
353 | u'\u30b3': 'kana_KO',
354 | u'\u30b5': 'kana_SA',
355 | u'\u30b7': 'kana_SHI',
356 | u'\u30b9': 'kana_SU',
357 | u'\u30bb': 'kana_SE',
358 | u'\u30bd': 'kana_SO',
359 | u'\u30bf': 'kana_TA',
360 | u'\u30c1': 'kana_CHI',
361 | u'\u30c4': 'kana_TSU',
362 | u'\u30c6': 'kana_TE',
363 | u'\u30c8': 'kana_TO',
364 | u'\u30ca': 'kana_NA',
365 | u'\u30cb': 'kana_NI',
366 | u'\u30cc': 'kana_NU',
367 | u'\u30cd': 'kana_NE',
368 | u'\u30ce': 'kana_NO',
369 | u'\u30cf': 'kana_HA',
370 | u'\u30d2': 'kana_HI',
371 | u'\u30d5': 'kana_FU',
372 | u'\u30d8': 'kana_HE',
373 | u'\u30db': 'kana_HO',
374 | u'\u30de': 'kana_MA',
375 | u'\u30df': 'kana_MI',
376 | u'\u30e0': 'kana_MU',
377 | u'\u30e1': 'kana_ME',
378 | u'\u30e2': 'kana_MO',
379 | u'\u30e4': 'kana_YA',
380 | u'\u30e6': 'kana_YU',
381 | u'\u30e8': 'kana_YO',
382 | u'\u30e9': 'kana_RA',
383 | u'\u30ea': 'kana_RI',
384 | u'\u30eb': 'kana_RU',
385 | u'\u30ec': 'kana_RE',
386 | u'\u30ed': 'kana_RO',
387 | u'\u30ef': 'kana_WA',
388 | u'\u30f3': 'kana_N',
389 | u'\u309b': 'voicedsound',
390 | u'\u309c': 'semivoicedsound',
391 | u'\u060c': 'Arabic_comma',
392 | u'\u061b': 'Arabic_semicolon',
393 | u'\u061f': 'Arabic_question_mark',
394 | u'\u0621': 'Arabic_hamza',
395 | u'\u0622': 'Arabic_maddaonalef',
396 | u'\u0623': 'Arabic_hamzaonalef',
397 | u'\u0624': 'Arabic_hamzaonwaw',
398 | u'\u0625': 'Arabic_hamzaunderalef',
399 | u'\u0626': 'Arabic_hamzaonyeh',
400 | u'\u0627': 'Arabic_alef',
401 | u'\u0628': 'Arabic_beh',
402 | u'\u0629': 'Arabic_tehmarbuta',
403 | u'\u062a': 'Arabic_teh',
404 | u'\u062b': 'Arabic_theh',
405 | u'\u062c': 'Arabic_jeem',
406 | u'\u062d': 'Arabic_hah',
407 | u'\u062e': 'Arabic_khah',
408 | u'\u062f': 'Arabic_dal',
409 | u'\u0630': 'Arabic_thal',
410 | u'\u0631': 'Arabic_ra',
411 | u'\u0632': 'Arabic_zain',
412 | u'\u0633': 'Arabic_seen',
413 | u'\u0634': 'Arabic_sheen',
414 | u'\u0635': 'Arabic_sad',
415 | u'\u0636': 'Arabic_dad',
416 | u'\u0637': 'Arabic_tah',
417 | u'\u0638': 'Arabic_zah',
418 | u'\u0639': 'Arabic_ain',
419 | u'\u063a': 'Arabic_ghain',
420 | u'\u0640': 'Arabic_tatweel',
421 | u'\u0641': 'Arabic_feh',
422 | u'\u0642': 'Arabic_qaf',
423 | u'\u0643': 'Arabic_kaf',
424 | u'\u0644': 'Arabic_lam',
425 | u'\u0645': 'Arabic_meem',
426 | u'\u0646': 'Arabic_noon',
427 | u'\u0647': 'Arabic_ha',
428 | u'\u0648': 'Arabic_waw',
429 | u'\u0649': 'Arabic_alefmaksura',
430 | u'\u064a': 'Arabic_yeh',
431 | u'\u064b': 'Arabic_fathatan',
432 | u'\u064c': 'Arabic_dammatan',
433 | u'\u064d': 'Arabic_kasratan',
434 | u'\u064e': 'Arabic_fatha',
435 | u'\u064f': 'Arabic_damma',
436 | u'\u0650': 'Arabic_kasra',
437 | u'\u0651': 'Arabic_shadda',
438 | u'\u0652': 'Arabic_sukun',
439 | u'\u0452': 'Serbian_dje',
440 | u'\u0453': 'Macedonia_gje',
441 | u'\u0451': 'Cyrillic_io',
442 | u'\u0454': 'Ukrainian_ie',
443 | u'\u0455': 'Macedonia_dse',
444 | u'\u0456': 'Ukrainian_i',
445 | u'\u0457': 'Ukrainian_yi',
446 | u'\u0458': 'Cyrillic_je',
447 | u'\u0459': 'Cyrillic_lje',
448 | u'\u045a': 'Cyrillic_nje',
449 | u'\u045b': 'Serbian_tshe',
450 | u'\u045c': 'Macedonia_kje',
451 | u'\u045e': 'Byelorussian_shortu',
452 | u'\u045f': 'Cyrillic_dzhe',
453 | u'\u2116': 'numerosign',
454 | u'\u0402': 'Serbian_DJE',
455 | u'\u0403': 'Macedonia_GJE',
456 | u'\u0401': 'Cyrillic_IO',
457 | u'\u0404': 'Ukrainian_IE',
458 | u'\u0405': 'Macedonia_DSE',
459 | u'\u0406': 'Ukrainian_I',
460 | u'\u0407': 'Ukrainian_YI',
461 | u'\u0408': 'Cyrillic_JE',
462 | u'\u0409': 'Cyrillic_LJE',
463 | u'\u040a': 'Cyrillic_NJE',
464 | u'\u040b': 'Serbian_TSHE',
465 | u'\u040c': 'Macedonia_KJE',
466 | u'\u040e': 'Byelorussian_SHORTU',
467 | u'\u040f': 'Cyrillic_DZHE',
468 | u'\u044e': 'Cyrillic_yu',
469 | u'\u0430': 'Cyrillic_a',
470 | u'\u0431': 'Cyrillic_be',
471 | u'\u0446': 'Cyrillic_tse',
472 | u'\u0434': 'Cyrillic_de',
473 | u'\u0435': 'Cyrillic_ie',
474 | u'\u0444': 'Cyrillic_ef',
475 | u'\u0433': 'Cyrillic_ghe',
476 | u'\u0445': 'Cyrillic_ha',
477 | u'\u0438': 'Cyrillic_i',
478 | u'\u0439': 'Cyrillic_shorti',
479 | u'\u043a': 'Cyrillic_ka',
480 | u'\u043b': 'Cyrillic_el',
481 | u'\u043c': 'Cyrillic_em',
482 | u'\u043d': 'Cyrillic_en',
483 | u'\u043e': 'Cyrillic_o',
484 | u'\u043f': 'Cyrillic_pe',
485 | u'\u044f': 'Cyrillic_ya',
486 | u'\u0440': 'Cyrillic_er',
487 | u'\u0441': 'Cyrillic_es',
488 | u'\u0442': 'Cyrillic_te',
489 | u'\u0443': 'Cyrillic_u',
490 | u'\u0436': 'Cyrillic_zhe',
491 | u'\u0432': 'Cyrillic_ve',
492 | u'\u044c': 'Cyrillic_softsign',
493 | u'\u044b': 'Cyrillic_yeru',
494 | u'\u0437': 'Cyrillic_ze',
495 | u'\u0448': 'Cyrillic_sha',
496 | u'\u044d': 'Cyrillic_e',
497 | u'\u0449': 'Cyrillic_shcha',
498 | u'\u0447': 'Cyrillic_che',
499 | u'\u044a': 'Cyrillic_hardsign',
500 | u'\u042e': 'Cyrillic_YU',
501 | u'\u0410': 'Cyrillic_A',
502 | u'\u0411': 'Cyrillic_BE',
503 | u'\u0426': 'Cyrillic_TSE',
504 | u'\u0414': 'Cyrillic_DE',
505 | u'\u0415': 'Cyrillic_IE',
506 | u'\u0424': 'Cyrillic_EF',
507 | u'\u0413': 'Cyrillic_GHE',
508 | u'\u0425': 'Cyrillic_HA',
509 | u'\u0418': 'Cyrillic_I',
510 | u'\u0419': 'Cyrillic_SHORTI',
511 | u'\u041a': 'Cyrillic_KA',
512 | u'\u041b': 'Cyrillic_EL',
513 | u'\u041c': 'Cyrillic_EM',
514 | u'\u041d': 'Cyrillic_EN',
515 | u'\u041e': 'Cyrillic_O',
516 | u'\u041f': 'Cyrillic_PE',
517 | u'\u042f': 'Cyrillic_YA',
518 | u'\u0420': 'Cyrillic_ER',
519 | u'\u0421': 'Cyrillic_ES',
520 | u'\u0422': 'Cyrillic_TE',
521 | u'\u0423': 'Cyrillic_U',
522 | u'\u0416': 'Cyrillic_ZHE',
523 | u'\u0412': 'Cyrillic_VE',
524 | u'\u042c': 'Cyrillic_SOFTSIGN',
525 | u'\u042b': 'Cyrillic_YERU',
526 | u'\u0417': 'Cyrillic_ZE',
527 | u'\u0428': 'Cyrillic_SHA',
528 | u'\u042d': 'Cyrillic_E',
529 | u'\u0429': 'Cyrillic_SHCHA',
530 | u'\u0427': 'Cyrillic_CHE',
531 | u'\u042a': 'Cyrillic_HARDSIGN',
532 | u'\u0386': 'Greek_ALPHAaccent',
533 | u'\u0388': 'Greek_EPSILONaccent',
534 | u'\u0389': 'Greek_ETAaccent',
535 | u'\u038a': 'Greek_IOTAaccent',
536 | u'\u03aa': 'Greek_IOTAdiaeresis',
537 | u'\u038c': 'Greek_OMICRONaccent',
538 | u'\u038e': 'Greek_UPSILONaccent',
539 | u'\u03ab': 'Greek_UPSILONdieresis',
540 | u'\u038f': 'Greek_OMEGAaccent',
541 | u'\u0385': 'Greek_accentdieresis',
542 | u'\u2015': 'Greek_horizbar',
543 | u'\u03ac': 'Greek_alphaaccent',
544 | u'\u03ad': 'Greek_epsilonaccent',
545 | u'\u03ae': 'Greek_etaaccent',
546 | u'\u03af': 'Greek_iotaaccent',
547 | u'\u03ca': 'Greek_iotadieresis',
548 | u'\u0390': 'Greek_iotaaccentdieresis',
549 | u'\u03cc': 'Greek_omicronaccent',
550 | u'\u03cd': 'Greek_upsilonaccent',
551 | u'\u03cb': 'Greek_upsilondieresis',
552 | u'\u03b0': 'Greek_upsilonaccentdieresis',
553 | u'\u03ce': 'Greek_omegaaccent',
554 | u'\u0391': 'Greek_ALPHA',
555 | u'\u0392': 'Greek_BETA',
556 | u'\u0393': 'Greek_GAMMA',
557 | u'\u0394': 'Greek_DELTA',
558 | u'\u0395': 'Greek_EPSILON',
559 | u'\u0396': 'Greek_ZETA',
560 | u'\u0397': 'Greek_ETA',
561 | u'\u0398': 'Greek_THETA',
562 | u'\u0399': 'Greek_IOTA',
563 | u'\u039a': 'Greek_KAPPA',
564 | u'\u039b': 'Greek_LAMBDA',
565 | u'\u039b': 'Greek_LAMDA',
566 | u'\u039c': 'Greek_MU',
567 | u'\u039d': 'Greek_NU',
568 | u'\u039e': 'Greek_XI',
569 | u'\u039f': 'Greek_OMICRON',
570 | u'\u03a0': 'Greek_PI',
571 | u'\u03a1': 'Greek_RHO',
572 | u'\u03a3': 'Greek_SIGMA',
573 | u'\u03a4': 'Greek_TAU',
574 | u'\u03a5': 'Greek_UPSILON',
575 | u'\u03a6': 'Greek_PHI',
576 | u'\u03a7': 'Greek_CHI',
577 | u'\u03a8': 'Greek_PSI',
578 | u'\u03a9': 'Greek_OMEGA',
579 | u'\u03b1': 'Greek_alpha',
580 | u'\u03b2': 'Greek_beta',
581 | u'\u03b3': 'Greek_gamma',
582 | u'\u03b4': 'Greek_delta',
583 | u'\u03b5': 'Greek_epsilon',
584 | u'\u03b6': 'Greek_zeta',
585 | u'\u03b7': 'Greek_eta',
586 | u'\u03b8': 'Greek_theta',
587 | u'\u03b9': 'Greek_iota',
588 | u'\u03ba': 'Greek_kappa',
589 | u'\u03bb': 'Greek_lambda',
590 | u'\u03bc': 'Greek_mu',
591 | u'\u03bd': 'Greek_nu',
592 | u'\u03be': 'Greek_xi',
593 | u'\u03bf': 'Greek_omicron',
594 | u'\u03c0': 'Greek_pi',
595 | u'\u03c1': 'Greek_rho',
596 | u'\u03c3': 'Greek_sigma',
597 | u'\u03c2': 'Greek_finalsmallsigma',
598 | u'\u03c4': 'Greek_tau',
599 | u'\u03c5': 'Greek_upsilon',
600 | u'\u03c6': 'Greek_phi',
601 | u'\u03c7': 'Greek_chi',
602 | u'\u03c8': 'Greek_psi',
603 | u'\u03c9': 'Greek_omega',
604 | u'\u23b7': 'leftradical',
605 | u'\u2320': 'topintegral',
606 | u'\u2321': 'botintegral',
607 | u'\u23a1': 'topleftsqbracket',
608 | u'\u23a3': 'botleftsqbracket',
609 | u'\u23a4': 'toprightsqbracket',
610 | u'\u23a6': 'botrightsqbracket',
611 | u'\u239b': 'topleftparens',
612 | u'\u239d': 'botleftparens',
613 | u'\u239e': 'toprightparens',
614 | u'\u23a0': 'botrightparens',
615 | u'\u23a8': 'leftmiddlecurlybrace',
616 | u'\u23ac': 'rightmiddlecurlybrace',
617 | u'\u2264': 'lessthanequal',
618 | u'\u2260': 'notequal',
619 | u'\u2265': 'greaterthanequal',
620 | u'\u222b': 'integral',
621 | u'\u2234': 'therefore',
622 | u'\u221d': 'variation',
623 | u'\u221e': 'infinity',
624 | u'\u2207': 'nabla',
625 | u'\u223c': 'approximate',
626 | u'\u2243': 'similarequal',
627 | u'\u21d4': 'ifonlyif',
628 | u'\u21d2': 'implies',
629 | u'\u2261': 'identical',
630 | u'\u221a': 'radical',
631 | u'\u2282': 'includedin',
632 | u'\u2283': 'includes',
633 | u'\u2229': 'intersection',
634 | u'\u222a': 'union',
635 | u'\u2227': 'logicaland',
636 | u'\u2228': 'logicalor',
637 | u'\u2202': 'partialderivative',
638 | u'\u0192': 'function',
639 | u'\u2190': 'leftarrow',
640 | u'\u2191': 'uparrow',
641 | u'\u2192': 'rightarrow',
642 | u'\u2193': 'downarrow',
643 | u'\u25c6': 'soliddiamond',
644 | u'\u2592': 'checkerboard',
645 | u'\u2409': 'ht',
646 | u'\u240c': 'ff',
647 | u'\u240d': 'cr',
648 | u'\u240a': 'lf',
649 | u'\u2424': 'nl',
650 | u'\u240b': 'vt',
651 | u'\u2518': 'lowrightcorner',
652 | u'\u2510': 'uprightcorner',
653 | u'\u250c': 'upleftcorner',
654 | u'\u2514': 'lowleftcorner',
655 | u'\u253c': 'crossinglines',
656 | u'\u23ba': 'horizlinescan1',
657 | u'\u23bb': 'horizlinescan3',
658 | u'\u2500': 'horizlinescan5',
659 | u'\u23bc': 'horizlinescan7',
660 | u'\u23bd': 'horizlinescan9',
661 | u'\u251c': 'leftt',
662 | u'\u2524': 'rightt',
663 | u'\u2534': 'bott',
664 | u'\u252c': 'topt',
665 | u'\u2502': 'vertbar',
666 | u'\u2003': 'emspace',
667 | u'\u2002': 'enspace',
668 | u'\u2004': 'em3space',
669 | u'\u2005': 'em4space',
670 | u'\u2007': 'digitspace',
671 | u'\u2008': 'punctspace',
672 | u'\u2009': 'thinspace',
673 | u'\u200a': 'hairspace',
674 | u'\u2014': 'emdash',
675 | u'\u2013': 'endash',
676 | u'\u2026': 'ellipsis',
677 | u'\u2025': 'doubbaselinedot',
678 | u'\u2153': 'onethird',
679 | u'\u2154': 'twothirds',
680 | u'\u2155': 'onefifth',
681 | u'\u2156': 'twofifths',
682 | u'\u2157': 'threefifths',
683 | u'\u2158': 'fourfifths',
684 | u'\u2159': 'onesixth',
685 | u'\u215a': 'fivesixths',
686 | u'\u2105': 'careof',
687 | u'\u2012': 'figdash',
688 | u'\u215b': 'oneeighth',
689 | u'\u215c': 'threeeighths',
690 | u'\u215d': 'fiveeighths',
691 | u'\u215e': 'seveneighths',
692 | u'\u2122': 'trademark',
693 | u'\u2018': 'leftsinglequotemark',
694 | u'\u2019': 'rightsinglequotemark',
695 | u'\u201c': 'leftdoublequotemark',
696 | u'\u201d': 'rightdoublequotemark',
697 | u'\u211e': 'prescription',
698 | u'\u2032': 'minutes',
699 | u'\u2033': 'seconds',
700 | u'\u271d': 'latincross',
701 | u'\u2663': 'club',
702 | u'\u2666': 'diamond',
703 | u'\u2665': 'heart',
704 | u'\u2720': 'maltesecross',
705 | u'\u2020': 'dagger',
706 | u'\u2021': 'doubledagger',
707 | u'\u2713': 'checkmark',
708 | u'\u2717': 'ballotcross',
709 | u'\u266f': 'musicalsharp',
710 | u'\u266d': 'musicalflat',
711 | u'\u2642': 'malesymbol',
712 | u'\u2640': 'femalesymbol',
713 | u'\u260e': 'telephone',
714 | u'\u2315': 'telephonerecorder',
715 | u'\u2117': 'phonographcopyright',
716 | u'\u2038': 'caret',
717 | u'\u201a': 'singlelowquotemark',
718 | u'\u201e': 'doublelowquotemark',
719 | u'\u22a5': 'downtack',
720 | u'\u230a': 'downstile',
721 | u'\u2218': 'jot',
722 | u'\u2395': 'quad',
723 | u'\u22a4': 'uptack',
724 | u'\u25cb': 'circle',
725 | u'\u2308': 'upstile',
726 | u'\u22a2': 'lefttack',
727 | u'\u22a3': 'righttack',
728 | u'\u2017': 'hebrew_doublelowline',
729 | u'\u05d0': 'hebrew_aleph',
730 | u'\u05d1': 'hebrew_bet',
731 | u'\u05d1': 'hebrew_beth',
732 | u'\u05d2': 'hebrew_gimel',
733 | u'\u05d2': 'hebrew_gimmel',
734 | u'\u05d3': 'hebrew_dalet',
735 | u'\u05d3': 'hebrew_daleth',
736 | u'\u05d4': 'hebrew_he',
737 | u'\u05d5': 'hebrew_waw',
738 | u'\u05d6': 'hebrew_zain',
739 | u'\u05d6': 'hebrew_zayin',
740 | u'\u05d7': 'hebrew_chet',
741 | u'\u05d7': 'hebrew_het',
742 | u'\u05d8': 'hebrew_tet',
743 | u'\u05d8': 'hebrew_teth',
744 | u'\u05d9': 'hebrew_yod',
745 | u'\u05da': 'hebrew_finalkaph',
746 | u'\u05db': 'hebrew_kaph',
747 | u'\u05dc': 'hebrew_lamed',
748 | u'\u05dd': 'hebrew_finalmem',
749 | u'\u05de': 'hebrew_mem',
750 | u'\u05df': 'hebrew_finalnun',
751 | u'\u05e0': 'hebrew_nun',
752 | u'\u05e1': 'hebrew_samech',
753 | u'\u05e1': 'hebrew_samekh',
754 | u'\u05e2': 'hebrew_ayin',
755 | u'\u05e3': 'hebrew_finalpe',
756 | u'\u05e4': 'hebrew_pe',
757 | u'\u05e5': 'hebrew_finalzade',
758 | u'\u05e5': 'hebrew_finalzadi',
759 | u'\u05e6': 'hebrew_zade',
760 | u'\u05e6': 'hebrew_zadi',
761 | u'\u05e7': 'hebrew_kuf',
762 | u'\u05e7': 'hebrew_qoph',
763 | u'\u05e8': 'hebrew_resh',
764 | u'\u05e9': 'hebrew_shin',
765 | u'\u05ea': 'hebrew_taf',
766 | u'\u05ea': 'hebrew_taw',
767 | u'\u0e01': 'Thai_kokai',
768 | u'\u0e02': 'Thai_khokhai',
769 | u'\u0e03': 'Thai_khokhuat',
770 | u'\u0e04': 'Thai_khokhwai',
771 | u'\u0e05': 'Thai_khokhon',
772 | u'\u0e06': 'Thai_khorakhang',
773 | u'\u0e07': 'Thai_ngongu',
774 | u'\u0e08': 'Thai_chochan',
775 | u'\u0e09': 'Thai_choching',
776 | u'\u0e0a': 'Thai_chochang',
777 | u'\u0e0b': 'Thai_soso',
778 | u'\u0e0c': 'Thai_chochoe',
779 | u'\u0e0d': 'Thai_yoying',
780 | u'\u0e0e': 'Thai_dochada',
781 | u'\u0e0f': 'Thai_topatak',
782 | u'\u0e10': 'Thai_thothan',
783 | u'\u0e11': 'Thai_thonangmontho',
784 | u'\u0e12': 'Thai_thophuthao',
785 | u'\u0e13': 'Thai_nonen',
786 | u'\u0e14': 'Thai_dodek',
787 | u'\u0e15': 'Thai_totao',
788 | u'\u0e16': 'Thai_thothung',
789 | u'\u0e17': 'Thai_thothahan',
790 | u'\u0e18': 'Thai_thothong',
791 | u'\u0e19': 'Thai_nonu',
792 | u'\u0e1a': 'Thai_bobaimai',
793 | u'\u0e1b': 'Thai_popla',
794 | u'\u0e1c': 'Thai_phophung',
795 | u'\u0e1d': 'Thai_fofa',
796 | u'\u0e1e': 'Thai_phophan',
797 | u'\u0e1f': 'Thai_fofan',
798 | u'\u0e20': 'Thai_phosamphao',
799 | u'\u0e21': 'Thai_moma',
800 | u'\u0e22': 'Thai_yoyak',
801 | u'\u0e23': 'Thai_rorua',
802 | u'\u0e24': 'Thai_ru',
803 | u'\u0e25': 'Thai_loling',
804 | u'\u0e26': 'Thai_lu',
805 | u'\u0e27': 'Thai_wowaen',
806 | u'\u0e28': 'Thai_sosala',
807 | u'\u0e29': 'Thai_sorusi',
808 | u'\u0e2a': 'Thai_sosua',
809 | u'\u0e2b': 'Thai_hohip',
810 | u'\u0e2c': 'Thai_lochula',
811 | u'\u0e2d': 'Thai_oang',
812 | u'\u0e2e': 'Thai_honokhuk',
813 | u'\u0e2f': 'Thai_paiyannoi',
814 | u'\u0e30': 'Thai_saraa',
815 | u'\u0e31': 'Thai_maihanakat',
816 | u'\u0e32': 'Thai_saraaa',
817 | u'\u0e33': 'Thai_saraam',
818 | u'\u0e34': 'Thai_sarai',
819 | u'\u0e35': 'Thai_saraii',
820 | u'\u0e36': 'Thai_saraue',
821 | u'\u0e37': 'Thai_sarauee',
822 | u'\u0e38': 'Thai_sarau',
823 | u'\u0e39': 'Thai_sarauu',
824 | u'\u0e3a': 'Thai_phinthu',
825 | u'\u0e3f': 'Thai_baht',
826 | u'\u0e40': 'Thai_sarae',
827 | u'\u0e41': 'Thai_saraae',
828 | u'\u0e42': 'Thai_sarao',
829 | u'\u0e43': 'Thai_saraaimaimuan',
830 | u'\u0e44': 'Thai_saraaimaimalai',
831 | u'\u0e45': 'Thai_lakkhangyao',
832 | u'\u0e46': 'Thai_maiyamok',
833 | u'\u0e47': 'Thai_maitaikhu',
834 | u'\u0e48': 'Thai_maiek',
835 | u'\u0e49': 'Thai_maitho',
836 | u'\u0e4a': 'Thai_maitri',
837 | u'\u0e4b': 'Thai_maichattawa',
838 | u'\u0e4c': 'Thai_thanthakhat',
839 | u'\u0e4d': 'Thai_nikhahit',
840 | u'\u0e50': 'Thai_leksun',
841 | u'\u0e51': 'Thai_leknung',
842 | u'\u0e52': 'Thai_leksong',
843 | u'\u0e53': 'Thai_leksam',
844 | u'\u0e54': 'Thai_leksi',
845 | u'\u0e55': 'Thai_lekha',
846 | u'\u0e56': 'Thai_lekhok',
847 | u'\u0e57': 'Thai_lekchet',
848 | u'\u0e58': 'Thai_lekpaet',
849 | u'\u0e59': 'Thai_lekkao',
850 | u'\u0152': 'OE',
851 | u'\u0153': 'oe',
852 | u'\u0178': 'Ydiaeresis',
853 | u'\u20ac': 'EuroSign',
854 | u'\u0491': 'Ukrainian_ghe_with_upturn',
855 | u'\u0490': 'Ukrainian_GHE_WITH_UPTURN'}
856 |
--------------------------------------------------------------------------------
/pymouse/__init__.py:
--------------------------------------------------------------------------------
1 | #Copyright 2013 Paul Barton
2 | #
3 | #This program is free software: you can redistribute it and/or modify
4 | #it under the terms of the GNU General Public License as published by
5 | #the Free Software Foundation, either version 3 of the License, or
6 | #(at your option) any later version.
7 | #
8 | #This program is distributed in the hope that it will be useful,
9 | #but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | #GNU General Public License for more details.
12 | #
13 | #You should have received a copy of the GNU General Public License
14 | #along with this program. If not, see .
15 |
16 | """
17 | The goal of PyMouse is to have a cross-platform way to control the mouse.
18 | PyMouse should work on Windows, Mac and any Unix that has xlib.
19 |
20 | PyMouse is a part of PyUserInput, along with PyKeyboard, for more information
21 | about this project, see:
22 | http://github.com/SavinaRoja/PyUserInput
23 |
24 | PyMouse was originally developed by Pepijn de Vos. For the original repository,
25 | see:
26 | https://github.com/pepijndevos/PyMouse
27 | """
28 |
29 | import sys
30 |
31 | if sys.platform.startswith('java'):
32 | from .java_ import PyMouse
33 |
34 | elif sys.platform == 'darwin':
35 | from .mac import PyMouse, PyMouseEvent
36 |
37 | elif sys.platform == 'win32':
38 | from .windows import PyMouse, PyMouseEvent
39 |
40 | else:
41 | from .x11 import PyMouse, PyMouseEvent
42 |
43 |
--------------------------------------------------------------------------------
/pymouse/base.py:
--------------------------------------------------------------------------------
1 | #Copyright 2013 Paul Barton
2 | #
3 | #This program is free software: you can redistribute it and/or modify
4 | #it under the terms of the GNU General Public License as published by
5 | #the Free Software Foundation, either version 3 of the License, or
6 | #(at your option) any later version.
7 | #
8 | #This program is distributed in the hope that it will be useful,
9 | #but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | #GNU General Public License for more details.
12 | #
13 | #You should have received a copy of the GNU General Public License
14 | #along with this program. If not, see .
15 |
16 | """
17 | The goal of PyMouse is to have a cross-platform way to control the mouse.
18 | PyMouse should work on Windows, Mac and any Unix that has xlib.
19 |
20 | As the base file, this provides a rough operational model along with the
21 | framework to be extended by each platform.
22 | """
23 |
24 | from threading import Thread
25 |
26 |
27 | class ScrollSupportError(Exception):
28 | pass
29 |
30 |
31 | class PyMouseMeta(object):
32 |
33 | def press(self, x, y, button=1):
34 | """
35 | Press the mouse on a given x, y and button.
36 | Button is defined as 1 = left, 2 = right, 3 = middle.
37 | """
38 |
39 | raise NotImplementedError
40 |
41 | def release(self, x, y, button=1):
42 | """
43 | Release the mouse on a given x, y and button.
44 | Button is defined as 1 = left, 2 = right, 3 = middle.
45 | """
46 |
47 | raise NotImplementedError
48 |
49 | def click(self, x, y, button=1, n=1):
50 | """
51 | Click a mouse button n times on a given x, y.
52 | Button is defined as 1 = left, 2 = right, 3 = middle.
53 | """
54 |
55 | for i in range(n):
56 | self.press(x, y, button)
57 | self.release(x, y, button)
58 |
59 | def scroll(self, vertical=None, horizontal=None, depth=None):
60 | """
61 | Generates mouse scrolling events in up to three dimensions: vertical,
62 | horizontal, and depth (Mac-only). Values for these arguments may be
63 | positive or negative numbers (float or int). Refer to the following:
64 | Vertical: + Up, - Down
65 | Horizontal: + Right, - Left
66 | Depth: + Rise (out of display), - Dive (towards display)
67 |
68 | Dynamic scrolling, which is used Windows and Mac platforms, is not
69 | implemented at this time due to an inability to test Mac code. The
70 | events generated by this code will thus be discrete units of scrolling
71 | "lines". The user is advised to take care at all times with scrolling
72 | automation as scrolling event consumption is relatively un-standardized.
73 |
74 | Float values will be coerced to integers.
75 | """
76 |
77 | raise NotImplementedError
78 |
79 | def move(self, x, y):
80 | """Move the mouse to a given x and y"""
81 |
82 | raise NotImplementedError
83 |
84 | def drag(self, x, y):
85 | """Drag the mouse to a given x and y.
86 | A Drag is a Move where the mouse key is held down."""
87 |
88 | raise NotImplementedError
89 |
90 | def position(self):
91 | """
92 | Get the current mouse position in pixels.
93 | Returns a tuple of 2 integers
94 | """
95 |
96 | raise NotImplementedError
97 |
98 | def screen_size(self):
99 | """
100 | Get the current screen size in pixels.
101 | Returns a tuple of 2 integers
102 | """
103 |
104 | raise NotImplementedError
105 |
106 |
107 | class PyMouseEventMeta(Thread):
108 | def __init__(self, capture=False, capture_move=False):
109 | Thread.__init__(self)
110 | self.daemon = True
111 | self.capture = capture
112 | self.capture_move = capture_move
113 | self.state = True
114 |
115 | def stop(self):
116 | self.state = False
117 |
118 | def click(self, x, y, button, press):
119 | """Subclass this method with your click event handler"""
120 | pass
121 |
122 | def move(self, x, y):
123 | """Subclass this method with your move event handler"""
124 | pass
125 |
126 | def scroll(self, x, y, vertical, horizontal):
127 | """
128 | Subclass this method with your scroll event handler
129 | Vertical: + Up, - Down
130 | Horizontal: + Right, - Left
131 | """
132 | pass
133 |
--------------------------------------------------------------------------------
/pymouse/java_.py:
--------------------------------------------------------------------------------
1 | #Copyright 2013 Paul Barton
2 | #
3 | #This program is free software: you can redistribute it and/or modify
4 | #it under the terms of the GNU General Public License as published by
5 | #the Free Software Foundation, either version 3 of the License, or
6 | #(at your option) any later version.
7 | #
8 | #This program is distributed in the hope that it will be useful,
9 | #but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | #GNU General Public License for more details.
12 | #
13 | #You should have received a copy of the GNU General Public License
14 | #along with this program. If not, see .
15 |
16 | from java.awt import Robot, Toolkit
17 | from java.awt.event import InputEvent
18 | from java.awt.MouseInfo import getPointerInfo
19 | from .base import PyMouseMeta
20 |
21 | r = Robot()
22 |
23 | class PyMouse(PyMouseMeta):
24 | def press(self, x, y, button = 1):
25 | button_list = [None, InputEvent.BUTTON1_MASK, InputEvent.BUTTON3_MASK, InputEvent.BUTTON2_MASK]
26 | self.move(x, y)
27 | r.mousePress(button_list[button])
28 |
29 | def release(self, x, y, button = 1):
30 | button_list = [None, InputEvent.BUTTON1_MASK, InputEvent.BUTTON3_MASK, InputEvent.BUTTON2_MASK]
31 | self.move(x, y)
32 | r.mouseRelease(button_list[button])
33 |
34 | def move(self, x, y):
35 | r.mouseMove(x, y)
36 |
37 | def position(self):
38 | loc = getPointerInfo().getLocation()
39 | return loc.getX, loc.getY
40 |
41 | def screen_size(self):
42 | dim = Toolkit.getDefaultToolkit().getScreenSize()
43 | return dim.getWidth(), dim.getHeight()
44 |
--------------------------------------------------------------------------------
/pymouse/license_notice.txt:
--------------------------------------------------------------------------------
1 | The primary developer of PyMouse is Pepijn de Vos.
2 | https://github.com/pepijndevos/PyMouse
3 |
4 | It was originally copyrighted under the Apache License version 2.0.
5 | It is with permission, on the condition that the result remain open source,
6 | that I, Paul Barton (https://github.com/SavinaRoja), transferred the source
7 | code of PyMouse into PyUserInput and adjusted the license to GPL3.
8 |
9 | The original license headers of the PyMouse source files was:
10 |
11 | # Copyright 2010 Pepijn de Vos
12 | #
13 | # Licensed under the Apache License, Version 2.0 (the "License");
14 | # you may not use this file except in compliance with the License.
15 | # You may obtain a copy of the License at
16 | #
17 | # http://www.apache.org/licenses/LICENSE-2.0
18 | #
19 | # Unless required by applicable law or agreed to in writing, software
20 | # distributed under the License is distributed on an "AS IS" BASIS,
21 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22 | # See the License for the specific language governing permissions and
23 | # limitations under the License.
24 |
--------------------------------------------------------------------------------
/pymouse/mac.py:
--------------------------------------------------------------------------------
1 | #Copyright 2013 Paul Barton
2 | #
3 | #This program is free software: you can redistribute it and/or modify
4 | #it under the terms of the GNU General Public License as published by
5 | #the Free Software Foundation, either version 3 of the License, or
6 | #(at your option) any later version.
7 | #
8 | #This program is distributed in the hope that it will be useful,
9 | #but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | #GNU General Public License for more details.
12 | #
13 | #You should have received a copy of the GNU General Public License
14 | #along with this program. If not, see .
15 |
16 | import Quartz
17 | from AppKit import NSEvent, NSScreen
18 | from .base import PyMouseMeta, PyMouseEventMeta
19 |
20 | pressID = [None, Quartz.kCGEventLeftMouseDown,
21 | Quartz.kCGEventRightMouseDown, Quartz.kCGEventOtherMouseDown]
22 | releaseID = [None, Quartz.kCGEventLeftMouseUp,
23 | Quartz.kCGEventRightMouseUp, Quartz.kCGEventOtherMouseUp]
24 |
25 |
26 | class PyMouse(PyMouseMeta):
27 |
28 | def press(self, x, y, button=1):
29 | event = Quartz.CGEventCreateMouseEvent(None,
30 | pressID[button],
31 | (x, y),
32 | button - 1)
33 | Quartz.CGEventPost(Quartz.kCGHIDEventTap, event)
34 |
35 | def release(self, x, y, button=1):
36 | event = Quartz.CGEventCreateMouseEvent(None,
37 | releaseID[button],
38 | (x, y),
39 | button - 1)
40 | Quartz.CGEventPost(Quartz.kCGHIDEventTap, event)
41 |
42 | def move(self, x, y):
43 | move = Quartz.CGEventCreateMouseEvent(None, Quartz.kCGEventMouseMoved, (x, y), 0)
44 | Quartz.CGEventPost(Quartz.kCGHIDEventTap, move)
45 |
46 | def drag(self, x, y):
47 | drag = Quartz.CGEventCreateMouseEvent(None, Quartz.kCGEventLeftMouseDragged, (x, y), 0)
48 | Quartz.CGEventPost(Quartz.kCGHIDEventTap, drag)
49 |
50 | def position(self):
51 | loc = NSEvent.mouseLocation()
52 | return loc.x, Quartz.CGDisplayPixelsHigh(0) - loc.y
53 |
54 | def screen_size(self):
55 | return NSScreen.mainScreen().frame().size.width, NSScreen.mainScreen().frame().size.height
56 |
57 | def scroll(self, vertical=None, horizontal=None, depth=None):
58 | #Local submethod for generating Mac scroll events in one axis at a time
59 | def scroll_event(y_move=0, x_move=0, z_move=0, n=1):
60 | for _ in range(abs(n)):
61 | scrollWheelEvent = Quartz.CGEventCreateScrollWheelEvent(
62 | None, # No source
63 | Quartz.kCGScrollEventUnitLine, # Unit of measurement is lines
64 | 3, # Number of wheels(dimensions)
65 | y_move,
66 | x_move,
67 | z_move)
68 | Quartz.CGEventPost(Quartz.kCGHIDEventTap, scrollWheelEvent)
69 |
70 | #Execute vertical then horizontal then depth scrolling events
71 | if vertical is not None:
72 | vertical = int(vertical)
73 | if vertical == 0: # Do nothing with 0 distance
74 | pass
75 | elif vertical > 0: # Scroll up if positive
76 | scroll_event(y_move=1, n=vertical)
77 | else: # Scroll down if negative
78 | scroll_event(y_move=-1, n=abs(vertical))
79 | if horizontal is not None:
80 | horizontal = int(horizontal)
81 | if horizontal == 0: # Do nothing with 0 distance
82 | pass
83 | elif horizontal > 0: # Scroll right if positive
84 | scroll_event(x_move=1, n=horizontal)
85 | else: # Scroll left if negative
86 | scroll_event(x_move=-1, n=abs(horizontal))
87 | if depth is not None:
88 | depth = int(depth)
89 | if depth == 0: # Do nothing with 0 distance
90 | pass
91 | elif vertical > 0: # Scroll "out" if positive
92 | scroll_event(z_move=1, n=depth)
93 | else: # Scroll "in" if negative
94 | scroll_event(z_move=-1, n=abs(depth))
95 |
96 |
97 | class PyMouseEvent(PyMouseEventMeta):
98 | def run(self):
99 | tap = Quartz.CGEventTapCreate(
100 | Quartz.kCGSessionEventTap,
101 | Quartz.kCGHeadInsertEventTap,
102 | Quartz.kCGEventTapOptionDefault,
103 | Quartz.CGEventMaskBit(Quartz.kCGEventMouseMoved) |
104 | Quartz.CGEventMaskBit(Quartz.kCGEventLeftMouseDown) |
105 | Quartz.CGEventMaskBit(Quartz.kCGEventLeftMouseUp) |
106 | Quartz.CGEventMaskBit(Quartz.kCGEventRightMouseDown) |
107 | Quartz.CGEventMaskBit(Quartz.kCGEventRightMouseUp) |
108 | Quartz.CGEventMaskBit(Quartz.kCGEventOtherMouseDown) |
109 | Quartz.CGEventMaskBit(Quartz.kCGEventOtherMouseUp),
110 | self.handler,
111 | None)
112 |
113 | loopsource = Quartz.CFMachPortCreateRunLoopSource(None, tap, 0)
114 | loop = Quartz.CFRunLoopGetCurrent()
115 | Quartz.CFRunLoopAddSource(loop, loopsource, Quartz.kCFRunLoopDefaultMode)
116 | Quartz.CGEventTapEnable(tap, True)
117 |
118 | while self.state:
119 | Quartz.CFRunLoopRunInMode(Quartz.kCFRunLoopDefaultMode, 5, False)
120 |
121 | def handler(self, proxy, type, event, refcon):
122 | (x, y) = Quartz.CGEventGetLocation(event)
123 | if type in pressID:
124 | self.click(x, y, pressID.index(type), True)
125 | elif type in releaseID:
126 | self.click(x, y, releaseID.index(type), False)
127 | else:
128 | self.move(x, y)
129 |
130 | if self.capture:
131 | Quartz.CGEventSetType(event, Quartz.kCGEventNull)
132 |
133 | return event
134 |
--------------------------------------------------------------------------------
/pymouse/mir.py:
--------------------------------------------------------------------------------
1 | #Copyright 2013 Paul Barton
2 | #
3 | #This program is free software: you can redistribute it and/or modify
4 | #it under the terms of the GNU General Public License as published by
5 | #the Free Software Foundation, either version 3 of the License, or
6 | #(at your option) any later version.
7 | #
8 | #This program is distributed in the hope that it will be useful,
9 | #but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | #GNU General Public License for more details.
12 | #
13 | #You should have received a copy of the GNU General Public License
14 | #along with this program. If not, see .
15 |
--------------------------------------------------------------------------------
/pymouse/wayland.py:
--------------------------------------------------------------------------------
1 | #Copyright 2013 Paul Barton
2 | #
3 | #This program is free software: you can redistribute it and/or modify
4 | #it under the terms of the GNU General Public License as published by
5 | #the Free Software Foundation, either version 3 of the License, or
6 | #(at your option) any later version.
7 | #
8 | #This program is distributed in the hope that it will be useful,
9 | #but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | #GNU General Public License for more details.
12 | #
13 | #You should have received a copy of the GNU General Public License
14 | #along with this program. If not, see .
15 |
--------------------------------------------------------------------------------
/pymouse/windows.py:
--------------------------------------------------------------------------------
1 | #Copyright 2013 Paul Barton
2 | #
3 | #This program is free software: you can redistribute it and/or modify
4 | #it under the terms of the GNU General Public License as published by
5 | #the Free Software Foundation, either version 3 of the License, or
6 | #(at your option) any later version.
7 | #
8 | #This program is distributed in the hope that it will be useful,
9 | #but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | #GNU General Public License for more details.
12 | #
13 | #You should have received a copy of the GNU General Public License
14 | #along with this program. If not, see .
15 |
16 | from ctypes import *
17 | import win32api, win32con
18 | from .base import PyMouseMeta, PyMouseEventMeta, ScrollSupportError
19 | import pythoncom
20 | from time import sleep
21 |
22 | class POINT(Structure):
23 | _fields_ = [("x", c_ulong),
24 | ("y", c_ulong)]
25 |
26 | class PyMouse(PyMouseMeta):
27 | """MOUSEEVENTF_(button and action) constants
28 | are defined at win32con, buttonAction is that value"""
29 |
30 | def press(self, x, y, button=1):
31 | buttonAction = 2 ** ((2 * button) - 1)
32 | self.move(x, y)
33 | win32api.mouse_event(buttonAction, x, y)
34 |
35 | def release(self, x, y, button=1):
36 | buttonAction = 2 ** ((2 * button))
37 | self.move(x, y)
38 | win32api.mouse_event(buttonAction, x, y)
39 |
40 | def scroll(self, vertical=None, horizontal=None, depth=None):
41 |
42 | #Windows supports only vertical and horizontal scrolling
43 | if depth is not None:
44 | raise ScrollSupportError('PyMouse cannot support depth-scrolling \
45 | in Windows. This feature is only available on Mac.')
46 |
47 | #Execute vertical then horizontal scrolling events
48 | if vertical is not None:
49 | vertical = int(vertical)
50 | if vertical == 0: # Do nothing with 0 distance
51 | pass
52 | elif vertical > 0: # Scroll up if positive
53 | for _ in range(vertical):
54 | win32api.mouse_event(0x0800, 0, 0, 120, 0)
55 | else: # Scroll down if negative
56 | for _ in range(abs(vertical)):
57 | win32api.mouse_event(0x0800, 0, 0, -120, 0)
58 | if horizontal is not None:
59 | horizontal = int(horizontal)
60 | if horizontal == 0: # Do nothing with 0 distance
61 | pass
62 | elif horizontal > 0: # Scroll right if positive
63 | for _ in range(horizontal):
64 | win32api.mouse_event(0x01000, 0, 0, 120, 0)
65 | else: # Scroll left if negative
66 | for _ in range(abs(horizontal)):
67 | win32api.mouse_event(0x01000, 0, 0, -120, 0)
68 |
69 | def move(self, x, y):
70 | windll.user32.SetCursorPos(x, y)
71 |
72 | def drag(self, x, y):
73 | self.press(*m.position())
74 | #self.move(x, y)
75 | self.release(x, y)
76 |
77 | def position(self):
78 | pt = POINT()
79 | windll.user32.GetCursorPos(byref(pt))
80 | return pt.x, pt.y
81 |
82 | def screen_size(self):
83 | if windll.user32.GetSystemMetrics(80) == 1:
84 | width = windll.user32.GetSystemMetrics(0)
85 | height = windll.user32.GetSystemMetrics(1)
86 | else:
87 | width = windll.user32.GetSystemMetrics(78)
88 | height = windll.user32.GetSystemMetrics(79)
89 | return width, height
90 |
91 | class PyMouseEvent(PyMouseEventMeta):
92 | def __init__(self, capture=False, capture_move=False):
93 | import pyWinhook as pyHook
94 |
95 | PyMouseEventMeta.__init__(self, capture=capture, capture_move=capture_move)
96 | self.hm = pyHook.HookManager()
97 |
98 | def run(self):
99 | self.hm.MouseAll = self._action
100 | self.hm.HookMouse()
101 | while self.state:
102 | sleep(0.01)
103 | pythoncom.PumpWaitingMessages()
104 |
105 | def stop(self):
106 | self.hm.UnhookMouse()
107 | self.state = False
108 |
109 | def _action(self, event):
110 | import pyWinhook as pyHook
111 | x, y = event.Position
112 |
113 | if event.Message == pyHook.HookConstants.WM_MOUSEMOVE:
114 | self.move(x,y)
115 | return not self.capture_move
116 |
117 | elif event.Message == pyHook.HookConstants.WM_LBUTTONDOWN:
118 | self.click(x, y, 1, True)
119 | elif event.Message == pyHook.HookConstants.WM_LBUTTONUP:
120 | self.click(x, y, 1, False)
121 | elif event.Message == pyHook.HookConstants.WM_RBUTTONDOWN:
122 | self.click(x, y, 2, True)
123 | elif event.Message == pyHook.HookConstants.WM_RBUTTONUP:
124 | self.click(x, y, 2, False)
125 | elif event.Message == pyHook.HookConstants.WM_MBUTTONDOWN:
126 | self.click(x, y, 3, True)
127 | elif event.Message == pyHook.HookConstants.WM_MBUTTONUP:
128 | self.click(x, y, 3, False)
129 |
130 | elif event.Message == pyHook.HookConstants.WM_MOUSEWHEEL:
131 | # event.Wheel is -1 when scrolling down, 1 when scrolling up
132 | self.scroll(x, y, event.Wheel, 0)
133 |
134 | return not self.capture
135 |
--------------------------------------------------------------------------------
/pymouse/x11.py:
--------------------------------------------------------------------------------
1 | #Copyright 2013 Paul Barton
2 | #
3 | #This program is free software: you can redistribute it and/or modify
4 | #it under the terms of the GNU General Public License as published by
5 | #the Free Software Foundation, either version 3 of the License, or
6 | #(at your option) any later version.
7 | #
8 | #This program is distributed in the hope that it will be useful,
9 | #but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | #GNU General Public License for more details.
12 | #
13 | #You should have received a copy of the GNU General Public License
14 | #along with this program. If not, see .
15 |
16 | from Xlib.display import Display
17 | from Xlib import X
18 | from Xlib.ext.xtest import fake_input
19 | from Xlib.ext import record
20 | from Xlib.protocol import rq
21 |
22 | from .base import PyMouseMeta, PyMouseEventMeta, ScrollSupportError
23 |
24 |
25 | class X11Error(Exception):
26 | """An error that is thrown at the end of a code block managed by a
27 | :func:`display_manager` if an *X11* error occurred.
28 | """
29 | pass
30 |
31 |
32 | def display_manager(display):
33 | """Traps *X* errors and raises an :class:``X11Error`` at the end if any
34 | error occurred.
35 |
36 | This handler also ensures that the :class:`Xlib.display.Display` being
37 | managed is sync'd.
38 |
39 | :param Xlib.display.Display display: The *X* display.
40 |
41 | :return: the display
42 | :rtype: Xlib.display.Display
43 | """
44 | from contextlib import contextmanager
45 |
46 | @contextmanager
47 | def manager():
48 | errors = []
49 |
50 | def handler(*args):
51 | errors.append(args)
52 |
53 | old_handler = display.set_error_handler(handler)
54 | yield display
55 | display.sync()
56 | display.set_error_handler(old_handler)
57 | if errors:
58 | raise X11Error(errors)
59 |
60 | return manager()
61 |
62 |
63 | def translate_button_code(button):
64 | # In X11, the button numbers are:
65 | # leftclick=1, middleclick=2, rightclick=3
66 | # For the purposes of the cross-platform interface of PyMouse, we
67 | # invert the button number values of the right and middle buttons
68 | if button in [1, 2, 3]:
69 | return (None, 1, 3, 2)[button]
70 | else:
71 | return button
72 |
73 | def button_code_to_scroll_direction(button):
74 | # scrollup=4, scrolldown=5, scrollleft=6, scrollright=7
75 | return {
76 | 4: (1, 0),
77 | 5: (-1, 0),
78 | 6: (0, 1),
79 | 7: (0, -1),
80 | }[button]
81 |
82 |
83 | class PyMouse(PyMouseMeta):
84 | def __init__(self, display=None):
85 | PyMouseMeta.__init__(self)
86 | self.display = Display(display)
87 | self.display2 = Display(display)
88 |
89 | def press(self, x, y, button=1):
90 | self.move(x, y)
91 |
92 | with display_manager(self.display) as d:
93 | fake_input(d, X.ButtonPress, translate_button_code(button))
94 |
95 | def release(self, x, y, button=1):
96 | self.move(x, y)
97 |
98 | with display_manager(self.display) as d:
99 | fake_input(d, X.ButtonRelease, translate_button_code(button))
100 |
101 | def scroll(self, vertical=None, horizontal=None, depth=None):
102 | #Xlib supports only vertical and horizontal scrolling
103 | if depth is not None:
104 | raise ScrollSupportError('PyMouse cannot support depth-scrolling \
105 | in X11. This feature is only available on Mac.')
106 |
107 | #Execute vertical then horizontal scrolling events
108 | if vertical is not None:
109 | vertical = int(vertical)
110 | if vertical == 0: # Do nothing with 0 distance
111 | pass
112 | elif vertical > 0: # Scroll up if positive
113 | self.click(*self.position(), button=4, n=vertical)
114 | else: # Scroll down if negative
115 | self.click(*self.position(), button=5, n=abs(vertical))
116 | if horizontal is not None:
117 | horizontal = int(horizontal)
118 | if horizontal == 0: # Do nothing with 0 distance
119 | pass
120 | elif horizontal > 0: # Scroll right if positive
121 | self.click(*self.position(), button=7, n=horizontal)
122 | else: # Scroll left if negative
123 | self.click(*self.position(), button=6, n=abs(horizontal))
124 |
125 | def move(self, x, y):
126 | if (x, y) != self.position():
127 | with display_manager(self.display) as d:
128 | fake_input(d, X.MotionNotify, x=x, y=y)
129 |
130 | def drag(self, x, y):
131 | with display_manager(self.display) as d:
132 | fake_input(d, X.ButtonPress, 1)
133 | fake_input(d, X.MotionNotify, x=x, y=y)
134 | fake_input(d, X.ButtonRelease, 1)
135 |
136 | def position(self):
137 | coord = self.display.screen().root.query_pointer()._data
138 | return coord["root_x"], coord["root_y"]
139 |
140 | def screen_size(self):
141 | width = self.display.screen().width_in_pixels
142 | height = self.display.screen().height_in_pixels
143 | return width, height
144 |
145 |
146 | class PyMouseEvent(PyMouseEventMeta):
147 | def __init__(self, capture=False, capture_move=False, display=None):
148 | PyMouseEventMeta.__init__(self,
149 | capture=capture,
150 | capture_move=capture_move)
151 | self.display = Display(display)
152 | self.display2 = Display(display)
153 | self.ctx = self.display2.record_create_context(
154 | 0,
155 | [record.AllClients],
156 | [{
157 | 'core_requests': (0, 0),
158 | 'core_replies': (0, 0),
159 | 'ext_requests': (0, 0, 0, 0),
160 | 'ext_replies': (0, 0, 0, 0),
161 | 'delivered_events': (0, 0),
162 | 'device_events': (X.ButtonPressMask, X.ButtonReleaseMask),
163 | 'errors': (0, 0),
164 | 'client_started': False,
165 | 'client_died': False,
166 | }])
167 |
168 | def run(self):
169 | try:
170 | if self.capture and self.capture_move:
171 | capturing = X.ButtonPressMask | X.ButtonReleaseMask | X.PointerMotionMask
172 | elif self.capture:
173 | capturing = X.ButtonPressMask | X.ButtonReleaseMask
174 | elif self.capture_move:
175 | capturing = X.PointerMotionMask
176 | else:
177 | capturing = False
178 |
179 | if capturing:
180 | self.display2.screen().root.grab_pointer(True,
181 | capturing,
182 | X.GrabModeAsync,
183 | X.GrabModeAsync,
184 | 0, 0, X.CurrentTime)
185 | self.display.screen().root.grab_pointer(True,
186 | capturing,
187 | X.GrabModeAsync,
188 | X.GrabModeAsync,
189 | 0, 0, X.CurrentTime)
190 |
191 | self.display2.record_enable_context(self.ctx, self.handler)
192 | self.display2.record_free_context(self.ctx)
193 | except KeyboardInterrupt:
194 | self.stop()
195 |
196 | def stop(self):
197 | self.state = False
198 | with display_manager(self.display) as d:
199 | d.ungrab_pointer(X.CurrentTime)
200 | d.record_disable_context(self.ctx)
201 | with display_manager(self.display2) as d:
202 | d.ungrab_pointer(X.CurrentTime)
203 | d.record_disable_context(self.ctx)
204 |
205 | def handler(self, reply):
206 | data = reply.data
207 | while len(data):
208 | event, data = rq.EventField(None).parse_binary_value(data, self.display.display, None, None)
209 |
210 | if event.detail in [4, 5, 6, 7]:
211 | if event.type == X.ButtonPress:
212 | self.scroll(event.root_x, event.root_y, *button_code_to_scroll_direction(event.detail))
213 | elif event.type == X.ButtonPress:
214 | self.click(event.root_x, event.root_y, translate_button_code(event.detail), True)
215 | elif event.type == X.ButtonRelease:
216 | self.click(event.root_x, event.root_y, translate_button_code(event.detail), False)
217 | else:
218 | self.move(event.root_x, event.root_y)
219 |
--------------------------------------------------------------------------------
/reference_materials/virtual_keystroke_example.py:
--------------------------------------------------------------------------------
1 | #Giant dictionary to hold key name and VK value
2 | VK_CODE = {'backspace':0x08,
3 | 'tab':0x09,
4 | 'clear':0x0C,
5 | 'enter':0x0D,
6 | 'shift':0x10,
7 | 'ctrl':0x11,
8 | 'alt':0x12,
9 | 'pause':0x13,
10 | 'caps_lock':0x14,
11 | 'esc':0x1B,
12 | 'spacebar':0x20,
13 | 'page_up':0x21,
14 | 'page_down':0x22,
15 | 'end':0x23,
16 | 'home':0x24,
17 | 'left_arrow':0x25,
18 | 'up_arrow':0x26,
19 | 'right_arrow':0x27,
20 | 'down_arrow':0x28,
21 | 'select':0x29,
22 | 'print':0x2A,
23 | 'execute':0x2B,
24 | 'print_screen':0x2C,
25 | 'ins':0x2D,
26 | 'del':0x2E,
27 | 'help':0x2F,
28 | '0':0x30,
29 | '1':0x31,
30 | '2':0x32,
31 | '3':0x33,
32 | '4':0x34,
33 | '5':0x35,
34 | '6':0x36,
35 | '7':0x37,
36 | '8':0x38,
37 | '9':0x39,
38 | 'a':0x41,
39 | 'b':0x42,
40 | 'c':0x43,
41 | 'd':0x44,
42 | 'e':0x45,
43 | 'f':0x46,
44 | 'g':0x47,
45 | 'h':0x48,
46 | 'i':0x49,
47 | 'j':0x4A,
48 | 'k':0x4B,
49 | 'l':0x4C,
50 | 'm':0x4D,
51 | 'n':0x4E,
52 | 'o':0x4F,
53 | 'p':0x50,
54 | 'q':0x51,
55 | 'r':0x52,
56 | 's':0x53,
57 | 't':0x54,
58 | 'u':0x55,
59 | 'v':0x56,
60 | 'w':0x57,
61 | 'x':0x58,
62 | 'y':0x59,
63 | 'z':0x5A,
64 | 'numpad_0':0x60,
65 | 'numpad_1':0x61,
66 | 'numpad_2':0x62,
67 | 'numpad_3':0x63,
68 | 'numpad_4':0x64,
69 | 'numpad_5':0x65,
70 | 'numpad_6':0x66,
71 | 'numpad_7':0x67,
72 | 'numpad_8':0x68,
73 | 'numpad_9':0x69,
74 | 'multiply_key':0x6A,
75 | 'add_key':0x6B,
76 | 'separator_key':0x6C,
77 | 'subtract_key':0x6D,
78 | 'decimal_key':0x6E,
79 | 'divide_key':0x6F,
80 | 'F1':0x70,
81 | 'F2':0x71,
82 | 'F3':0x72,
83 | 'F4':0x73,
84 | 'F5':0x74,
85 | 'F6':0x75,
86 | 'F7':0x76,
87 | 'F8':0x77,
88 | 'F9':0x78,
89 | 'F10':0x79,
90 | 'F11':0x7A,
91 | 'F12':0x7B,
92 | 'F13':0x7C,
93 | 'F14':0x7D,
94 | 'F15':0x7E,
95 | 'F16':0x7F,
96 | 'F17':0x80,
97 | 'F18':0x81,
98 | 'F19':0x82,
99 | 'F20':0x83,
100 | 'F21':0x84,
101 | 'F22':0x85,
102 | 'F23':0x86,
103 | 'F24':0x87,
104 | 'num_lock':0x90,
105 | 'scroll_lock':0x91,
106 | 'left_shift':0xA0,
107 | 'right_shift ':0xA1,
108 | 'left_control':0xA2,
109 | 'right_control':0xA3,
110 | 'left_menu':0xA4,
111 | 'right_menu':0xA5,
112 | 'browser_back':0xA6,
113 | 'browser_forward':0xA7,
114 | 'browser_refresh':0xA8,
115 | 'browser_stop':0xA9,
116 | 'browser_search':0xAA,
117 | 'browser_favorites':0xAB,
118 | 'browser_start_and_home':0xAC,
119 | 'volume_mute':0xAD,
120 | 'volume_Down':0xAE,
121 | 'volume_up':0xAF,
122 | 'next_track':0xB0,
123 | 'previous_track':0xB1,
124 | 'stop_media':0xB2,
125 | 'play/pause_media':0xB3,
126 | 'start_mail':0xB4,
127 | 'select_media':0xB5,
128 | 'start_application_1':0xB6,
129 | 'start_application_2':0xB7,
130 | 'attn_key':0xF6,
131 | 'crsel_key':0xF7,
132 | 'exsel_key':0xF8,
133 | 'play_key':0xFA,
134 | 'zoom_key':0xFB,
135 | 'clear_key':0xFE,
136 | '+':0xBB,
137 | ',':0xBC,
138 | '-':0xBD,
139 | '.':0xBE,
140 | '/':0xBF,
141 | '`':0xC0,
142 | ';':0xBA,
143 | '[':0xDB,
144 | '\\':0xDC,
145 | ']':0xDD,
146 | "'":0xDE,
147 | '`':0xC0}
148 |
149 | def press(*args):
150 | '''
151 | one press, one release.
152 | accepts as many arguments as you want. e.g. press('left_arrow', 'a','b').
153 | '''
154 | for i in args:
155 | win32api.keybd_event(VK_CODE[i], 0,0,0)
156 | time.sleep(.05)
157 | win32api.keybd_event(VK_CODE[i],0 ,win32con.KEYEVENTF_KEYUP ,0)
158 |
159 | def pressAndHold(*args):
160 | '''
161 | press and hold. Do NOT release.
162 | accepts as many arguments as you want.
163 | e.g. pressAndHold('left_arrow', 'a','b').
164 | '''
165 | for i in args:
166 | win32api.keybd_event(VK_CODE[i], 0,0,0)
167 | time.sleep(.05)
168 |
169 | def pressHoldRelease(*args):
170 | '''
171 | press and hold passed in strings. Once held, release
172 | accepts as many arguments as you want.
173 | e.g. pressAndHold('left_arrow', 'a','b').
174 |
175 | this is useful for issuing shortcut command or shift commands.
176 | e.g. pressHoldRelease('ctrl', 'alt', 'del'), pressHoldRelease('shift','a')
177 | '''
178 | for i in args:
179 | win32api.keybd_event(VK_CODE[i], 0,0,0)
180 | time.sleep(.05)
181 |
182 | for i in args:
183 | win32api.keybd_event(VK_CODE[i],0 ,win32con.KEYEVENTF_KEYUP ,0)
184 | time.sleep(.1)
185 |
186 |
187 |
188 | def release(*args):
189 | '''
190 | release depressed keys
191 | accepts as many arguments as you want.
192 | e.g. release('left_arrow', 'a','b').
193 | '''
194 | for i in args:
195 | win32api.keybd_event(VK_CODE[i],0 ,win32con.KEYEVENTF_KEYUP ,0)
196 |
197 |
198 | def typer(string=None,*args):
199 | ## time.sleep(4)
200 | for i in string:
201 | if i == ' ':
202 | win32api.keybd_event(VK_CODE['spacebar'], 0,0,0)
203 | time.sleep(.05)
204 | win32api.keybd_event(VK_CODE['spacebar'],0 ,win32con.KEYEVENTF_KEYUP ,0)
205 |
206 | elif i == '!':
207 | win32api.keybd_event(VK_CODE['left_shift'], 0,0,0)
208 | win32api.keybd_event(VK_CODE['1'], 0,0,0)
209 | time.sleep(.05)
210 | win32api.keybd_event(VK_CODE['left_shift'],0 ,win32con.KEYEVENTF_KEYUP ,0)
211 | win32api.keybd_event(VK_CODE['1'],0 ,win32con.KEYEVENTF_KEYUP ,0)
212 |
213 | elif i == '@':
214 | win32api.keybd_event(VK_CODE['left_shift'], 0,0,0)
215 | win32api.keybd_event(VK_CODE['2'], 0,0,0)
216 | time.sleep(.05)
217 | win32api.keybd_event(VK_CODE['left_shift'],0 ,win32con.KEYEVENTF_KEYUP ,0)
218 | win32api.keybd_event(VK_CODE['2'],0 ,win32con.KEYEVENTF_KEYUP ,0)
219 |
220 | elif i == '{':
221 | win32api.keybd_event(VK_CODE['left_shift'], 0,0,0)
222 | win32api.keybd_event(VK_CODE['['], 0,0,0)
223 | time.sleep(.05)
224 | win32api.keybd_event(VK_CODE['left_shift'],0 ,win32con.KEYEVENTF_KEYUP ,0)
225 | win32api.keybd_event(VK_CODE['['],0 ,win32con.KEYEVENTF_KEYUP ,0)
226 |
227 | elif i == '?':
228 | win32api.keybd_event(VK_CODE['left_shift'], 0,0,0)
229 | win32api.keybd_event(VK_CODE['/'], 0,0,0)
230 | time.sleep(.05)
231 | win32api.keybd_event(VK_CODE['left_shift'],0 ,win32con.KEYEVENTF_KEYUP ,0)
232 | win32api.keybd_event(VK_CODE['/'],0 ,win32con.KEYEVENTF_KEYUP ,0)
233 |
234 | elif i == ':':
235 | win32api.keybd_event(VK_CODE['left_shift'], 0,0,0)
236 | win32api.keybd_event(VK_CODE[';'], 0,0,0)
237 | time.sleep(.05)
238 | win32api.keybd_event(VK_CODE['left_shift'],0 ,win32con.KEYEVENTF_KEYUP ,0)
239 | win32api.keybd_event(VK_CODE[';'],0 ,win32con.KEYEVENTF_KEYUP ,0)
240 |
241 | elif i == '"':
242 | win32api.keybd_event(VK_CODE['left_shift'], 0,0,0)
243 | win32api.keybd_event(VK_CODE['\''], 0,0,0)
244 | time.sleep(.05)
245 | win32api.keybd_event(VK_CODE['left_shift'],0 ,win32con.KEYEVENTF_KEYUP ,0)
246 | win32api.keybd_event(VK_CODE['\''],0 ,win32con.KEYEVENTF_KEYUP ,0)
247 |
248 | elif i == '}':
249 | win32api.keybd_event(VK_CODE['left_shift'], 0,0,0)
250 | win32api.keybd_event(VK_CODE[']'], 0,0,0)
251 | time.sleep(.05)
252 | win32api.keybd_event(VK_CODE['left_shift'],0 ,win32con.KEYEVENTF_KEYUP ,0)
253 | win32api.keybd_event(VK_CODE[']'],0 ,win32con.KEYEVENTF_KEYUP ,0)
254 |
255 | elif i == '#':
256 | win32api.keybd_event(VK_CODE['left_shift'], 0,0,0)
257 | win32api.keybd_event(VK_CODE['3'], 0,0,0)
258 | time.sleep(.05)
259 | win32api.keybd_event(VK_CODE['left_shift'],0 ,win32con.KEYEVENTF_KEYUP ,0)
260 | win32api.keybd_event(VK_CODE['3'],0 ,win32con.KEYEVENTF_KEYUP ,0)
261 |
262 | elif i == '$':
263 | win32api.keybd_event(VK_CODE['left_shift'], 0,0,0)
264 | win32api.keybd_event(VK_CODE['4'], 0,0,0)
265 | time.sleep(.05)
266 | win32api.keybd_event(VK_CODE['left_shift'],0 ,win32con.KEYEVENTF_KEYUP ,0)
267 | win32api.keybd_event(VK_CODE['4'],0 ,win32con.KEYEVENTF_KEYUP ,0)
268 |
269 | elif i == '%':
270 | win32api.keybd_event(VK_CODE['left_shift'], 0,0,0)
271 | win32api.keybd_event(VK_CODE['5'], 0,0,0)
272 | time.sleep(.05)
273 | win32api.keybd_event(VK_CODE['left_shift'],0 ,win32con.KEYEVENTF_KEYUP ,0)
274 | win32api.keybd_event(VK_CODE['5'],0 ,win32con.KEYEVENTF_KEYUP ,0)
275 |
276 | elif i == '^':
277 | win32api.keybd_event(VK_CODE['left_shift'], 0,0,0)
278 | win32api.keybd_event(VK_CODE['6'], 0,0,0)
279 | time.sleep(.05)
280 | win32api.keybd_event(VK_CODE['left_shift'],0 ,win32con.KEYEVENTF_KEYUP ,0)
281 | win32api.keybd_event(VK_CODE['6'],0 ,win32con.KEYEVENTF_KEYUP ,0)
282 |
283 | elif i == '&':
284 | win32api.keybd_event(VK_CODE['left_shift'], 0,0,0)
285 | win32api.keybd_event(VK_CODE['7'], 0,0,0)
286 | time.sleep(.05)
287 | win32api.keybd_event(VK_CODE['left_shift'],0 ,win32con.KEYEVENTF_KEYUP ,0)
288 | win32api.keybd_event(VK_CODE['7'],0 ,win32con.KEYEVENTF_KEYUP ,0)
289 |
290 | elif i == '*':
291 | win32api.keybd_event(VK_CODE['left_shift'], 0,0,0)
292 | win32api.keybd_event(VK_CODE['8'], 0,0,0)
293 | time.sleep(.05)
294 | win32api.keybd_event(VK_CODE['left_shift'],0 ,win32con.KEYEVENTF_KEYUP ,0)
295 | win32api.keybd_event(VK_CODE['8'],0 ,win32con.KEYEVENTF_KEYUP ,0)
296 |
297 | elif i == '(':
298 | win32api.keybd_event(VK_CODE['left_shift'], 0,0,0)
299 | win32api.keybd_event(VK_CODE['9'], 0,0,0)
300 | time.sleep(.05)
301 | win32api.keybd_event(VK_CODE['left_shift'],0 ,win32con.KEYEVENTF_KEYUP ,0)
302 | win32api.keybd_event(VK_CODE['9'],0 ,win32con.KEYEVENTF_KEYUP ,0)
303 |
304 | elif i == ')':
305 | win32api.keybd_event(VK_CODE['left_shift'], 0,0,0)
306 | win32api.keybd_event(VK_CODE['0'], 0,0,0)
307 | time.sleep(.05)
308 | win32api.keybd_event(VK_CODE['left_shift'],0 ,win32con.KEYEVENTF_KEYUP ,0)
309 | win32api.keybd_event(VK_CODE['0'],0 ,win32con.KEYEVENTF_KEYUP ,0)
310 |
311 | elif i == '_':
312 | win32api.keybd_event(VK_CODE['left_shift'], 0,0,0)
313 | win32api.keybd_event(VK_CODE['-'], 0,0,0)
314 | time.sleep(.05)
315 | win32api.keybd_event(VK_CODE['left_shift'],0 ,win32con.KEYEVENTF_KEYUP ,0)
316 | win32api.keybd_event(VK_CODE['-'],0 ,win32con.KEYEVENTF_KEYUP ,0)
317 |
318 |
319 | elif i == '=':
320 | win32api.keybd_event(VK_CODE['left_shift'], 0,0,0)
321 | win32api.keybd_event(VK_CODE['+'], 0,0,0)
322 | time.sleep(.05)
323 | win32api.keybd_event(VK_CODE['left_shift'],0 ,win32con.KEYEVENTF_KEYUP ,0)
324 | win32api.keybd_event(VK_CODE['+'],0 ,win32con.KEYEVENTF_KEYUP ,0)
325 |
326 | elif i == '~':
327 | win32api.keybd_event(VK_CODE['left_shift'], 0,0,0)
328 | win32api.keybd_event(VK_CODE['`'], 0,0,0)
329 | time.sleep(.05)
330 | win32api.keybd_event(VK_CODE['left_shift'],0 ,win32con.KEYEVENTF_KEYUP ,0)
331 | win32api.keybd_event(VK_CODE['`'],0 ,win32con.KEYEVENTF_KEYUP ,0)
332 |
333 | elif i == '<':
334 | win32api.keybd_event(VK_CODE['left_shift'], 0,0,0)
335 | win32api.keybd_event(VK_CODE[','], 0,0,0)
336 | time.sleep(.05)
337 | win32api.keybd_event(VK_CODE['left_shift'],0 ,win32con.KEYEVENTF_KEYUP ,0)
338 | win32api.keybd_event(VK_CODE[','],0 ,win32con.KEYEVENTF_KEYUP ,0)
339 |
340 | elif i == '>':
341 | win32api.keybd_event(VK_CODE['left_shift'], 0,0,0)
342 | win32api.keybd_event(VK_CODE['.'], 0,0,0)
343 | time.sleep(.05)
344 | win32api.keybd_event(VK_CODE['left_shift'],0 ,win32con.KEYEVENTF_KEYUP ,0)
345 | win32api.keybd_event(VK_CODE['.'],0 ,win32con.KEYEVENTF_KEYUP ,0)
346 |
347 | elif i == 'A':
348 | win32api.keybd_event(VK_CODE['left_shift'], 0,0,0)
349 | win32api.keybd_event(VK_CODE['a'], 0,0,0)
350 | time.sleep(.05)
351 | win32api.keybd_event(VK_CODE['left_shift'],0 ,win32con.KEYEVENTF_KEYUP ,0)
352 | win32api.keybd_event(VK_CODE['a'],0 ,win32con.KEYEVENTF_KEYUP ,0)
353 |
354 | elif i == 'B':
355 | win32api.keybd_event(VK_CODE['left_shift'], 0,0,0)
356 | win32api.keybd_event(VK_CODE['b'], 0,0,0)
357 | time.sleep(.05)
358 | win32api.keybd_event(VK_CODE['left_shift'],0 ,win32con.KEYEVENTF_KEYUP ,0)
359 | win32api.keybd_event(VK_CODE['b'],0 ,win32con.KEYEVENTF_KEYUP ,0)
360 |
361 | elif i == 'C':
362 | win32api.keybd_event(VK_CODE['left_shift'], 0,0,0)
363 | win32api.keybd_event(VK_CODE['c'], 0,0,0)
364 | time.sleep(.05)
365 | win32api.keybd_event(VK_CODE['left_shift'],0 ,win32con.KEYEVENTF_KEYUP ,0)
366 | win32api.keybd_event(VK_CODE['c'],0 ,win32con.KEYEVENTF_KEYUP ,0)
367 |
368 | elif i == 'D':
369 | win32api.keybd_event(VK_CODE['left_shift'], 0,0,0)
370 | win32api.keybd_event(VK_CODE['d'], 0,0,0)
371 | time.sleep(.05)
372 | win32api.keybd_event(VK_CODE['left_shift'],0 ,win32con.KEYEVENTF_KEYUP ,0)
373 | win32api.keybd_event(VK_CODE['d'],0 ,win32con.KEYEVENTF_KEYUP ,0)
374 |
375 | elif i == 'E':
376 | win32api.keybd_event(VK_CODE['left_shift'], 0,0,0)
377 | win32api.keybd_event(VK_CODE['e'], 0,0,0)
378 | time.sleep(.05)
379 | win32api.keybd_event(VK_CODE['left_shift'],0 ,win32con.KEYEVENTF_KEYUP ,0)
380 | win32api.keybd_event(VK_CODE['e'],0 ,win32con.KEYEVENTF_KEYUP ,0)
381 |
382 | elif i == 'F':
383 | win32api.keybd_event(VK_CODE['left_shift'], 0,0,0)
384 | win32api.keybd_event(VK_CODE['f'], 0,0,0)
385 | time.sleep(.05)
386 | win32api.keybd_event(VK_CODE['left_shift'],0 ,win32con.KEYEVENTF_KEYUP ,0)
387 | win32api.keybd_event(VK_CODE['f'],0 ,win32con.KEYEVENTF_KEYUP ,0)
388 |
389 | elif i == 'G':
390 | win32api.keybd_event(VK_CODE['left_shift'], 0,0,0)
391 | win32api.keybd_event(VK_CODE['g'], 0,0,0)
392 | time.sleep(.05)
393 | win32api.keybd_event(VK_CODE['left_shift'],0 ,win32con.KEYEVENTF_KEYUP ,0)
394 | win32api.keybd_event(VK_CODE['g'],0 ,win32con.KEYEVENTF_KEYUP ,0)
395 |
396 | elif i == 'H':
397 | win32api.keybd_event(VK_CODE['left_shift'], 0,0,0)
398 | win32api.keybd_event(VK_CODE['h'], 0,0,0)
399 | time.sleep(.05)
400 | win32api.keybd_event(VK_CODE['left_shift'],0 ,win32con.KEYEVENTF_KEYUP ,0)
401 | win32api.keybd_event(VK_CODE['h'],0 ,win32con.KEYEVENTF_KEYUP ,0)
402 |
403 | elif i == 'I':
404 | win32api.keybd_event(VK_CODE['left_shift'], 0,0,0)
405 | win32api.keybd_event(VK_CODE['i'], 0,0,0)
406 | time.sleep(.05)
407 | win32api.keybd_event(VK_CODE['left_shift'],0 ,win32con.KEYEVENTF_KEYUP ,0)
408 | win32api.keybd_event(VK_CODE['i'],0 ,win32con.KEYEVENTF_KEYUP ,0)
409 |
410 | elif i == 'J':
411 | win32api.keybd_event(VK_CODE['left_shift'], 0,0,0)
412 | win32api.keybd_event(VK_CODE['j'], 0,0,0)
413 | time.sleep(.05)
414 | win32api.keybd_event(VK_CODE['left_shift'],0 ,win32con.KEYEVENTF_KEYUP ,0)
415 | win32api.keybd_event(VK_CODE['j'],0 ,win32con.KEYEVENTF_KEYUP ,0)
416 |
417 | elif i == 'K':
418 | win32api.keybd_event(VK_CODE['left_shift'], 0,0,0)
419 | win32api.keybd_event(VK_CODE['k'], 0,0,0)
420 | time.sleep(.05)
421 | win32api.keybd_event(VK_CODE['left_shift'],0 ,win32con.KEYEVENTF_KEYUP ,0)
422 | win32api.keybd_event(VK_CODE['k'],0 ,win32con.KEYEVENTF_KEYUP ,0)
423 |
424 | elif i == 'L':
425 | win32api.keybd_event(VK_CODE['left_shift'], 0,0,0)
426 | win32api.keybd_event(VK_CODE['l'], 0,0,0)
427 | time.sleep(.05)
428 | win32api.keybd_event(VK_CODE['left_shift'],0 ,win32con.KEYEVENTF_KEYUP ,0)
429 | win32api.keybd_event(VK_CODE['l'],0 ,win32con.KEYEVENTF_KEYUP ,0)
430 |
431 | elif i == 'M':
432 | win32api.keybd_event(VK_CODE['left_shift'], 0,0,0)
433 | win32api.keybd_event(VK_CODE['m'], 0,0,0)
434 | time.sleep(.05)
435 | win32api.keybd_event(VK_CODE['left_shift'],0 ,win32con.KEYEVENTF_KEYUP ,0)
436 | win32api.keybd_event(VK_CODE['m'],0 ,win32con.KEYEVENTF_KEYUP ,0)
437 |
438 | elif i == 'N':
439 | win32api.keybd_event(VK_CODE['left_shift'], 0,0,0)
440 | win32api.keybd_event(VK_CODE['n'], 0,0,0)
441 | time.sleep(.05)
442 | win32api.keybd_event(VK_CODE['left_shift'],0 ,win32con.KEYEVENTF_KEYUP ,0)
443 | win32api.keybd_event(VK_CODE['n'],0 ,win32con.KEYEVENTF_KEYUP ,0)
444 |
445 | elif i == 'O':
446 | win32api.keybd_event(VK_CODE['left_shift'], 0,0,0)
447 | win32api.keybd_event(VK_CODE['o'], 0,0,0)
448 | time.sleep(.05)
449 | win32api.keybd_event(VK_CODE['left_shift'],0 ,win32con.KEYEVENTF_KEYUP ,0)
450 | win32api.keybd_event(VK_CODE['o'],0 ,win32con.KEYEVENTF_KEYUP ,0)
451 |
452 | elif i == 'P':
453 | win32api.keybd_event(VK_CODE['left_shift'], 0,0,0)
454 | win32api.keybd_event(VK_CODE['p'], 0,0,0)
455 | time.sleep(.05)
456 | win32api.keybd_event(VK_CODE['left_shift'],0 ,win32con.KEYEVENTF_KEYUP ,0)
457 | win32api.keybd_event(VK_CODE['p'],0 ,win32con.KEYEVENTF_KEYUP ,0)
458 |
459 | elif i == 'Q':
460 | win32api.keybd_event(VK_CODE['left_shift'], 0,0,0)
461 | win32api.keybd_event(VK_CODE['q'], 0,0,0)
462 | time.sleep(.05)
463 | win32api.keybd_event(VK_CODE['left_shift'],0 ,win32con.KEYEVENTF_KEYUP ,0)
464 | win32api.keybd_event(VK_CODE['q'],0 ,win32con.KEYEVENTF_KEYUP ,0)
465 |
466 | elif i == 'R':
467 | win32api.keybd_event(VK_CODE['left_shift'], 0,0,0)
468 | win32api.keybd_event(VK_CODE['r'], 0,0,0)
469 | time.sleep(.05)
470 | win32api.keybd_event(VK_CODE['left_shift'],0 ,win32con.KEYEVENTF_KEYUP ,0)
471 | win32api.keybd_event(VK_CODE['r'],0 ,win32con.KEYEVENTF_KEYUP ,0)
472 |
473 | elif i == 'S':
474 | win32api.keybd_event(VK_CODE['left_shift'], 0,0,0)
475 | win32api.keybd_event(VK_CODE['s'], 0,0,0)
476 | time.sleep(.05)
477 | win32api.keybd_event(VK_CODE['left_shift'],0 ,win32con.KEYEVENTF_KEYUP ,0)
478 | win32api.keybd_event(VK_CODE['s'],0 ,win32con.KEYEVENTF_KEYUP ,0)
479 |
480 | elif i == 'T':
481 | win32api.keybd_event(VK_CODE['left_shift'], 0,0,0)
482 | win32api.keybd_event(VK_CODE['t'], 0,0,0)
483 | time.sleep(.05)
484 | win32api.keybd_event(VK_CODE['left_shift'],0 ,win32con.KEYEVENTF_KEYUP ,0)
485 | win32api.keybd_event(VK_CODE['t'],0 ,win32con.KEYEVENTF_KEYUP ,0)
486 |
487 | elif i == 'U':
488 | win32api.keybd_event(VK_CODE['left_shift'], 0,0,0)
489 | win32api.keybd_event(VK_CODE['u'], 0,0,0)
490 | time.sleep(.05)
491 | win32api.keybd_event(VK_CODE['left_shift'],0 ,win32con.KEYEVENTF_KEYUP ,0)
492 | win32api.keybd_event(VK_CODE['u'],0 ,win32con.KEYEVENTF_KEYUP ,0)
493 |
494 | elif i == 'V':
495 | win32api.keybd_event(VK_CODE['left_shift'], 0,0,0)
496 | win32api.keybd_event(VK_CODE['v'], 0,0,0)
497 | time.sleep(.05)
498 | win32api.keybd_event(VK_CODE['left_shift'],0 ,win32con.KEYEVENTF_KEYUP ,0)
499 | win32api.keybd_event(VK_CODE['v'],0 ,win32con.KEYEVENTF_KEYUP ,0)
500 |
501 | elif i == 'W':
502 | win32api.keybd_event(VK_CODE['left_shift'], 0,0,0)
503 | win32api.keybd_event(VK_CODE['w'], 0,0,0)
504 | time.sleep(.05)
505 | win32api.keybd_event(VK_CODE['left_shift'],0 ,win32con.KEYEVENTF_KEYUP ,0)
506 | win32api.keybd_event(VK_CODE['w'],0 ,win32con.KEYEVENTF_KEYUP ,0)
507 |
508 | elif i == 'X':
509 | win32api.keybd_event(VK_CODE['left_shift'], 0,0,0)
510 | win32api.keybd_event(VK_CODE['x'], 0,0,0)
511 | time.sleep(.05)
512 | win32api.keybd_event(VK_CODE['left_shift'],0 ,win32con.KEYEVENTF_KEYUP ,0)
513 | win32api.keybd_event(VK_CODE['x'],0 ,win32con.KEYEVENTF_KEYUP ,0)
514 |
515 | elif i == 'Y':
516 | win32api.keybd_event(VK_CODE['left_shift'], 0,0,0)
517 | win32api.keybd_event(VK_CODE['y'], 0,0,0)
518 | time.sleep(.05)
519 | win32api.keybd_event(VK_CODE['left_shift'],0 ,win32con.KEYEVENTF_KEYUP ,0)
520 | win32api.keybd_event(VK_CODE['y'],0 ,win32con.KEYEVENTF_KEYUP ,0)
521 |
522 | elif i == 'Z':
523 | win32api.keybd_event(VK_CODE['left_shift'], 0,0,0)
524 | win32api.keybd_event(VK_CODE['z'], 0,0,0)
525 | time.sleep(.05)
526 | win32api.keybd_event(VK_CODE['left_shift'],0 ,win32con.KEYEVENTF_KEYUP ,0)
527 | win32api.keybd_event(VK_CODE['z'],0 ,win32con.KEYEVENTF_KEYUP ,0)
528 |
529 |
530 | else:
531 | win32api.keybd_event(VK_CODE[i], 0,0,0)
532 | time.sleep(.05)
533 | win32api.keybd_event(VK_CODE[i],0 ,win32con.KEYEVENTF_KEYUP ,0)
534 |
535 |
536 |
537 |
538 |
--------------------------------------------------------------------------------
/reference_materials/windows_VK.txt:
--------------------------------------------------------------------------------
1 | VK_LBUTTON = 1
2 | VK_RBUTTON = 2
3 | VK_CANCEL = 3
4 | VK_MBUTTON = 4
5 | VK_BACK = 8
6 | VK_TAB = 9
7 | VK_CLEAR = 12
8 | VK_RETURN = 13
9 | VK_SHIFT = 16
10 | VK_CONTROL = 17
11 | VK_MENU = 18
12 | VK_PAUSE = 19
13 | VK_CAPITAL = 20
14 | VK_KANA = 21
15 | VK_HANGEUL = 21 # old name - should be here for compatibility
16 | VK_HANGUL = 21
17 | VK_JUNJA = 23
18 | VK_FINAL = 24
19 | VK_HANJA = 25
20 | VK_KANJI = 25
21 | VK_ESCAPE = 27
22 | VK_CONVERT = 28
23 | VK_NONCONVERT = 29
24 | VK_ACCEPT = 30
25 | VK_MODECHANGE = 31
26 | VK_SPACE = 32
27 | VK_PRIOR = 33
28 | VK_NEXT = 34
29 | VK_END = 35
30 | VK_HOME = 36
31 | VK_LEFT = 37
32 | VK_UP = 38
33 | VK_RIGHT = 39
34 | VK_DOWN = 40
35 | VK_SELECT = 41
36 | VK_PRINT = 42
37 | VK_EXECUTE = 43
38 | VK_SNAPSHOT = 44
39 | VK_INSERT = 45
40 | VK_DELETE = 46
41 | VK_HELP = 47
42 | VK_LWIN = 91
43 | VK_RWIN = 92
44 | VK_APPS = 93
45 | VK_NUMPAD0 = 96
46 | VK_NUMPAD1 = 97
47 | VK_NUMPAD2 = 98
48 | VK_NUMPAD3 = 99
49 | VK_NUMPAD4 = 100
50 | VK_NUMPAD5 = 101
51 | VK_NUMPAD6 = 102
52 | VK_NUMPAD7 = 103
53 | VK_NUMPAD8 = 104
54 | VK_NUMPAD9 = 105
55 | VK_MULTIPLY = 106
56 | VK_ADD = 107
57 | VK_SEPARATOR = 108
58 | VK_SUBTRACT = 109
59 | VK_DECIMAL = 110
60 | VK_DIVIDE = 111
61 | VK_F1 = 112
62 | VK_F2 = 113
63 | VK_F3 = 114
64 | VK_F4 = 115
65 | VK_F5 = 116
66 | VK_F6 = 117
67 | VK_F7 = 118
68 | VK_F8 = 119
69 | VK_F9 = 120
70 | VK_F10 = 121
71 | VK_F11 = 122
72 | VK_F12 = 123
73 | VK_F13 = 124
74 | VK_F14 = 125
75 | VK_F15 = 126
76 | VK_F16 = 127
77 | VK_F17 = 128
78 | VK_F18 = 129
79 | VK_F19 = 130
80 | VK_F20 = 131
81 | VK_F21 = 132
82 | VK_F22 = 133
83 | VK_F23 = 134
84 | VK_F24 = 135
85 | VK_NUMLOCK = 144
86 | VK_SCROLL = 145
87 | VK_LSHIFT = 160
88 | VK_RSHIFT = 161
89 | VK_LCONTROL = 162
90 | VK_RCONTROL = 163
91 | VK_LMENU = 164
92 | VK_RMENU = 165
93 | VK_PROCESSKEY = 229
94 | VK_ATTN = 246
95 | VK_CRSEL = 247
96 | VK_EXSEL = 248
97 | VK_EREOF = 249
98 | VK_PLAY = 250
99 | VK_ZOOM = 251
100 | VK_NONAME = 252
101 | VK_PA1 = 253
102 | VK_OEM_CLEAR = 254
103 |
104 | VK_XBUTTON1 = 0x05
105 | VK_XBUTTON2 = 0x06
106 | VK_VOLUME_MUTE = 0xAD
107 | VK_VOLUME_DOWN = 0xAE
108 | VK_VOLUME_UP = 0xAF
109 | VK_MEDIA_NEXT_TRACK = 0xB0
110 | VK_MEDIA_PREV_TRACK = 0xB1
111 | VK_MEDIA_PLAY_PAUSE = 0xB3
112 | VK_BROWSER_BACK = 0xA6
113 | VK_BROWSER_FORWARD = 0xA7
114 |
--------------------------------------------------------------------------------
/reference_materials/winex.py:
--------------------------------------------------------------------------------
1 | import ctypes
2 |
3 | LONG = ctypes.c_long
4 | DWORD = ctypes.c_ulong
5 | ULONG_PTR = ctypes.POINTER(DWORD)
6 | WORD = ctypes.c_ushort
7 |
8 | class MOUSEINPUT(ctypes.Structure):
9 | _fields_ = (('dx', LONG),
10 | ('dy', LONG),
11 | ('mouseData', DWORD),
12 | ('dwFlags', DWORD),
13 | ('time', DWORD),
14 | ('dwExtraInfo', ULONG_PTR))
15 |
16 | class KEYBDINPUT(ctypes.Structure):
17 | _fields_ = (('wVk', WORD),
18 | ('wScan', WORD),
19 | ('dwFlags', DWORD),
20 | ('time', DWORD),
21 | ('dwExtraInfo', ULONG_PTR))
22 |
23 | class HARDWAREINPUT(ctypes.Structure):
24 | _fields_ = (('uMsg', DWORD),
25 | ('wParamL', WORD),
26 | ('wParamH', WORD))
27 |
28 | class _INPUTunion(ctypes.Union):
29 | _fields_ = (('mi', MOUSEINPUT),
30 | ('ki', KEYBDINPUT),
31 | ('hi', HARDWAREINPUT))
32 |
33 | class INPUT(ctypes.Structure):
34 | _fields_ = (('type', DWORD),
35 | ('union', _INPUTunion))
36 |
37 | def SendInput(*inputs):
38 | nInputs = len(inputs)
39 | LPINPUT = INPUT * nInputs
40 | pInputs = LPINPUT(*inputs)
41 | cbSize = ctypes.c_int(ctypes.sizeof(INPUT))
42 | return ctypes.windll.user32.SendInput(nInputs, pInputs, cbSize)
43 |
44 | INPUT_MOUSE = 0
45 | INPUT_KEYBOARD = 1
46 | INPUT_HARDWARD = 2
47 |
48 | def Input(structure):
49 | if isinstance(structure, MOUSEINPUT):
50 | return INPUT(INPUT_MOUSE, _INPUTunion(mi=structure))
51 | if isinstance(structure, KEYBDINPUT):
52 | return INPUT(INPUT_KEYBOARD, _INPUTunion(ki=structure))
53 | if isinstance(structure, HARDWAREINPUT):
54 | return INPUT(INPUT_HARDWARE, _INPUTunion(hi=structure))
55 | raise TypeError('Cannot create INPUT structure!')
56 |
57 | WHEEL_DELTA = 120
58 | XBUTTON1 = 0x0001
59 | XBUTTON2 = 0x0002
60 | MOUSEEVENTF_ABSOLUTE = 0x8000
61 | MOUSEEVENTF_HWHEEL = 0x01000
62 | MOUSEEVENTF_MOVE = 0x0001
63 | MOUSEEVENTF_MOVE_NOCOALESCE = 0x2000
64 | MOUSEEVENTF_LEFTDOWN = 0x0002
65 | MOUSEEVENTF_LEFTUP = 0x0004
66 | MOUSEEVENTF_RIGHTDOWN = 0x0008
67 | MOUSEEVENTF_RIGHTUP = 0x0010
68 | MOUSEEVENTF_MIDDLEDOWN = 0x0020
69 | MOUSEEVENTF_MIDDLEUP = 0x0040
70 | MOUSEEVENTF_VIRTUALDESK = 0x4000
71 | MOUSEEVENTF_WHEEL = 0x0800
72 | MOUSEEVENTF_XDOWN = 0x0080
73 | MOUSEEVENTF_XUP = 0x0100
74 |
75 | def MouseInput(flags, x, y, data):
76 | return MOUSEINPUT(x, y, data, flags, 0, None)
77 |
78 | VK_LBUTTON = 0x01 # Left mouse button
79 | VK_RBUTTON = 0x02 # Right mouse button
80 | VK_CANCEL = 0x03 # Control-break processing
81 | VK_MBUTTON = 0x04 # Middle mouse button (three-button mouse)
82 | VK_XBUTTON1 = 0x05 # X1 mouse button
83 | VK_XBUTTON2 = 0x06 # X2 mouse button
84 | VK_BACK = 0x08 # BACKSPACE key
85 | VK_TAB = 0x09 # TAB key
86 | VK_CLEAR = 0x0C # CLEAR key
87 | VK_RETURN = 0x0D # ENTER key
88 | VK_SHIFT = 0x10 # SHIFT key
89 | VK_CONTROL = 0x11 # CTRL key
90 | VK_MENU = 0x12 # ALT key
91 | VK_PAUSE = 0x13 # PAUSE key
92 | VK_CAPITAL = 0x14 # CAPS LOCK key
93 | VK_KANA = 0x15 # IME Kana mode
94 | VK_HANGUL = 0x15 # IME Hangul mode
95 | VK_JUNJA = 0x17 # IME Junja mode
96 | VK_FINAL = 0x18 # IME final mode
97 | VK_HANJA = 0x19 # IME Hanja mode
98 | VK_KANJI = 0x19 # IME Kanji mode
99 | VK_ESCAPE = 0x1B # ESC key
100 | VK_CONVERT = 0x1C # IME convert
101 | VK_NONCONVERT = 0x1D # IME nonconvert
102 | VK_ACCEPT = 0x1E # IME accept
103 | VK_MODECHANGE = 0x1F # IME mode change request
104 | VK_SPACE = 0x20 # SPACEBAR
105 | VK_PRIOR = 0x21 # PAGE UP key
106 | VK_NEXT = 0x22 # PAGE DOWN key
107 | VK_END = 0x23 # END key
108 | VK_HOME = 0x24 # HOME key
109 | VK_LEFT = 0x25 # LEFT ARROW key
110 | VK_UP = 0x26 # UP ARROW key
111 | VK_RIGHT = 0x27 # RIGHT ARROW key
112 | VK_DOWN = 0x28 # DOWN ARROW key
113 | VK_SELECT = 0x29 # SELECT key
114 | VK_PRINT = 0x2A # PRINT key
115 | VK_EXECUTE = 0x2B # EXECUTE key
116 | VK_SNAPSHOT = 0x2C # PRINT SCREEN key
117 | VK_INSERT = 0x2D # INS key
118 | VK_DELETE = 0x2E # DEL key
119 | VK_HELP = 0x2F # HELP key
120 | VK_LWIN = 0x5B # Left Windows key (Natural keyboard)
121 | VK_RWIN = 0x5C # Right Windows key (Natural keyboard)
122 | VK_APPS = 0x5D # Applications key (Natural keyboard)
123 | VK_SLEEP = 0x5F # Computer Sleep key
124 | VK_NUMPAD0 = 0x60 # Numeric keypad 0 key
125 | VK_NUMPAD1 = 0x61 # Numeric keypad 1 key
126 | VK_NUMPAD2 = 0x62 # Numeric keypad 2 key
127 | VK_NUMPAD3 = 0x63 # Numeric keypad 3 key
128 | VK_NUMPAD4 = 0x64 # Numeric keypad 4 key
129 | VK_NUMPAD5 = 0x65 # Numeric keypad 5 key
130 | VK_NUMPAD6 = 0x66 # Numeric keypad 6 key
131 | VK_NUMPAD7 = 0x67 # Numeric keypad 7 key
132 | VK_NUMPAD8 = 0x68 # Numeric keypad 8 key
133 | VK_NUMPAD9 = 0x69 # Numeric keypad 9 key
134 | VK_MULTIPLY = 0x6A # Multiply key
135 | VK_ADD = 0x6B # Add key
136 | VK_SEPARATOR = 0x6C # Separator key
137 | VK_SUBTRACT = 0x6D # Subtract key
138 | VK_DECIMAL = 0x6E # Decimal key
139 | VK_DIVIDE = 0x6F # Divide key
140 | VK_F1 = 0x70 # F1 key
141 | VK_F2 = 0x71 # F2 key
142 | VK_F3 = 0x72 # F3 key
143 | VK_F4 = 0x73 # F4 key
144 | VK_F5 = 0x74 # F5 key
145 | VK_F6 = 0x75 # F6 key
146 | VK_F7 = 0x76 # F7 key
147 | VK_F8 = 0x77 # F8 key
148 | VK_F9 = 0x78 # F9 key
149 | VK_F10 = 0x79 # F10 key
150 | VK_F11 = 0x7A # F11 key
151 | VK_F12 = 0x7B # F12 key
152 | VK_F13 = 0x7C # F13 key
153 | VK_F14 = 0x7D # F14 key
154 | VK_F15 = 0x7E # F15 key
155 | VK_F16 = 0x7F # F16 key
156 | VK_F17 = 0x80 # F17 key
157 | VK_F18 = 0x81 # F18 key
158 | VK_F19 = 0x82 # F19 key
159 | VK_F20 = 0x83 # F20 key
160 | VK_F21 = 0x84 # F21 key
161 | VK_F22 = 0x85 # F22 key
162 | VK_F23 = 0x86 # F23 key
163 | VK_F24 = 0x87 # F24 key
164 | VK_NUMLOCK = 0x90 # NUM LOCK key
165 | VK_SCROLL = 0x91 # SCROLL LOCK key
166 | VK_LSHIFT = 0xA0 # Left SHIFT key
167 | VK_RSHIFT = 0xA1 # Right SHIFT key
168 | VK_LCONTROL = 0xA2 # Left CONTROL key
169 | VK_RCONTROL = 0xA3 # Right CONTROL key
170 | VK_LMENU = 0xA4 # Left MENU key
171 | VK_RMENU = 0xA5 # Right MENU key
172 | VK_BROWSER_BACK = 0xA6 # Browser Back key
173 | VK_BROWSER_FORWARD = 0xA7 # Browser Forward key
174 | VK_BROWSER_REFRESH = 0xA8 # Browser Refresh key
175 | VK_BROWSER_STOP = 0xA9 # Browser Stop key
176 | VK_BROWSER_SEARCH = 0xAA # Browser Search key
177 | VK_BROWSER_FAVORITES = 0xAB # Browser Favorites key
178 | VK_BROWSER_HOME = 0xAC # Browser Start and Home key
179 | VK_VOLUME_MUTE = 0xAD # Volume Mute key
180 | VK_VOLUME_DOWN = 0xAE # Volume Down key
181 | VK_VOLUME_UP = 0xAF # Volume Up key
182 | VK_MEDIA_NEXT_TRACK = 0xB0 # Next Track key
183 | VK_MEDIA_PREV_TRACK = 0xB1 # Previous Track key
184 | VK_MEDIA_STOP = 0xB2 # Stop Media key
185 | VK_MEDIA_PLAY_PAUSE = 0xB3 # Play/Pause Media key
186 | VK_LAUNCH_MAIL = 0xB4 # Start Mail key
187 | VK_LAUNCH_MEDIA_SELECT = 0xB5 # Select Media key
188 | VK_LAUNCH_APP1 = 0xB6 # Start Application 1 key
189 | VK_LAUNCH_APP2 = 0xB7 # Start Application 2 key
190 | VK_OEM_1 = 0xBA # Used for miscellaneous characters; it can vary by keyboard.
191 | # For the US standard keyboard, the ';:' key
192 | VK_OEM_PLUS = 0xBB # For any country/region, the '+' key
193 | VK_OEM_COMMA = 0xBC # For any country/region, the ',' key
194 | VK_OEM_MINUS = 0xBD # For any country/region, the '-' key
195 | VK_OEM_PERIOD = 0xBE # For any country/region, the '.' key
196 | VK_OEM_2 = 0xBF # Used for miscellaneous characters; it can vary by keyboard.
197 | # For the US standard keyboard, the '/?' key
198 | VK_OEM_3 = 0xC0 # Used for miscellaneous characters; it can vary by keyboard.
199 | # For the US standard keyboard, the '`~' key
200 | VK_OEM_4 = 0xDB # Used for miscellaneous characters; it can vary by keyboard.
201 | # For the US standard keyboard, the '[{' key
202 | VK_OEM_5 = 0xDC # Used for miscellaneous characters; it can vary by keyboard.
203 | # For the US standard keyboard, the '\|' key
204 | VK_OEM_6 = 0xDD # Used for miscellaneous characters; it can vary by keyboard.
205 | # For the US standard keyboard, the ']}' key
206 | VK_OEM_7 = 0xDE # Used for miscellaneous characters; it can vary by keyboard.
207 | # For the US standard keyboard, the 'single-quote/double-quote' key
208 | VK_OEM_8 = 0xDF # Used for miscellaneous characters; it can vary by keyboard.
209 | VK_OEM_102 = 0xE2 # Either the angle bracket key or the backslash key on the RT 102-key keyboard
210 | VK_PROCESSKEY = 0xE5 # IME PROCESS key
211 | VK_PACKET = 0xE7 # 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. For more information, see Remark in KEYBDINPUT, SendInput, WM_KEYDOWN, and WM_KEYUP
212 | VK_ATTN = 0xF6 # Attn key
213 | VK_CRSEL = 0xF7 # CrSel key
214 | VK_EXSEL = 0xF8 # ExSel key
215 | VK_EREOF = 0xF9 # Erase EOF key
216 | VK_PLAY = 0xFA # Play key
217 | VK_ZOOM = 0xFB # Zoom key
218 | VK_PA1 = 0xFD # PA1 key
219 | VK_OEM_CLEAR = 0xFE # Clear key
220 |
221 | KEYEVENTF_EXTENDEDKEY = 0x0001
222 | KEYEVENTF_KEYUP = 0x0002
223 | KEYEVENTF_SCANCODE = 0x0008
224 | KEYEVENTF_UNICODE = 0x0004
225 |
226 | KEY_0 = 0x30
227 | KEY_1 = 0x31
228 | KEY_2 = 0x32
229 | KEY_3 = 0x33
230 | KEY_4 = 0x34
231 | KEY_5 = 0x35
232 | KEY_6 = 0x36
233 | KEY_7 = 0x37
234 | KEY_8 = 0x38
235 | KEY_9 = 0x39
236 | KEY_A = 0x41
237 | KEY_B = 0x42
238 | KEY_C = 0x43
239 | KEY_D = 0x44
240 | KEY_E = 0x45
241 | KEY_F = 0x46
242 | KEY_G = 0x47
243 | KEY_H = 0x48
244 | KEY_I = 0x49
245 | KEY_J = 0x4A
246 | KEY_K = 0x4B
247 | KEY_L = 0x4C
248 | KEY_M = 0x4D
249 | KEY_N = 0x4E
250 | KEY_O = 0x4F
251 | KEY_P = 0x50
252 | KEY_Q = 0x51
253 | KEY_R = 0x52
254 | KEY_S = 0x53
255 | KEY_T = 0x54
256 | KEY_U = 0x55
257 | KEY_V = 0x56
258 | KEY_W = 0x57
259 | KEY_X = 0x58
260 | KEY_Y = 0x59
261 | KEY_Z = 0x5A
262 |
263 | def KeybdInput(code, flags):
264 | return KEYBDINPUT(code, code, flags, 0, None)
265 |
266 | def HardwareInput(message, parameter):
267 | return HARDWAREINPUT(message & 0xFFFFFFFF,
268 | parameter & 0xFFFF,
269 | parameter >> 16 & 0xFFFF)
270 |
271 | def Mouse(flags, x=0, y=0, data=0):
272 | return Input(MouseInput(flags, x, y, data))
273 |
274 | def Keyboard(code, flags=0):
275 | return Input(KeybdInput(code, flags))
276 |
277 | def Hardware(message, parameter=0):
278 | return Input(HardwareInput(message, parameter))
279 |
280 | ################################################################################
281 |
282 | import string
283 |
284 | UPPER = frozenset('~!@#$%^&*()_+QWERTYUIOP{}|ASDFGHJKL:"ZXCVBNM<>?')
285 | LOWER = frozenset("`1234567890-=qwertyuiop[]\\asdfghjkl;'zxcvbnm,./")
286 | ORDER = string.ascii_letters + string.digits + ' \b\r\t'
287 | ALTER = dict(zip('!@#$%^&*()', '1234567890'))
288 | OTHER = {'`': VK_OEM_3,
289 | '~': VK_OEM_3,
290 | '-': VK_OEM_MINUS,
291 | '_': VK_OEM_MINUS,
292 | '=': VK_OEM_PLUS,
293 | '+': VK_OEM_PLUS,
294 | '[': VK_OEM_4,
295 | '{': VK_OEM_4,
296 | ']': VK_OEM_6,
297 | '}': VK_OEM_6,
298 | '\\': VK_OEM_5,
299 | '|': VK_OEM_5,
300 | ';': VK_OEM_1,
301 | ':': VK_OEM_1,
302 | "'": VK_OEM_7,
303 | '"': VK_OEM_7,
304 | ',': VK_OEM_COMMA,
305 | '<': VK_OEM_COMMA,
306 | '.': VK_OEM_PERIOD,
307 | '>': VK_OEM_PERIOD,
308 | '/': VK_OEM_2,
309 | '?': VK_OEM_2}
310 |
311 | def keyboard_stream(string):
312 | mode = False
313 | for character in string.replace('\r\n', '\r').replace('\n', '\r'):
314 | if mode and character in LOWER or not mode and character in UPPER:
315 | yield Keyboard(VK_SHIFT, mode and KEYEVENTF_KEYUP)
316 | mode = not mode
317 | character = ALTER.get(character, character)
318 | if character in ORDER:
319 | code = ord(character.upper())
320 | elif character in OTHER:
321 | code = OTHER[character]
322 | else:
323 | continue
324 | raise ValueError('String is not understood!')
325 | yield Keyboard(code)
326 | yield Keyboard(code, KEYEVENTF_KEYUP)
327 | if mode:
328 | yield Keyboard(VK_SHIFT, KEYEVENTF_KEYUP)
329 |
330 | ################################################################################
331 |
332 | import time, sys
333 |
334 | def main():
335 | time.sleep(5)
336 | for event in keyboard_stream('o2E^uXh#:SHn&HQ+t]YF'):
337 | SendInput(event)
338 | time.sleep(0.1)
339 |
340 | ##if __name__ == '__main__':
341 | ## main()
342 |
343 | def switch_program():
344 | SendInput(Keyboard(VK_MENU), Keyboard(VK_TAB))
345 | time.sleep(0.2)
346 | SendInput(Keyboard(VK_TAB, KEYEVENTF_KEYUP),
347 | Keyboard(VK_MENU, KEYEVENTF_KEYUP))
348 | time.sleep(0.2)
349 |
350 | def select_line():
351 | SendInput(Keyboard(VK_SHIFT, KEYEVENTF_EXTENDEDKEY),
352 | Keyboard(VK_END, KEYEVENTF_EXTENDEDKEY))
353 | time.sleep(0.2)
354 | SendInput(Keyboard(VK_SHIFT, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP),
355 | Keyboard(VK_END, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP))
356 | time.sleep(0.2)
357 |
358 | def copy_line():
359 | SendInput(Keyboard(VK_CONTROL), Keyboard(KEY_C))
360 | time.sleep(0.2)
361 | SendInput(Keyboard(VK_CONTROL, KEYEVENTF_KEYUP),
362 | Keyboard(KEY_C, KEYEVENTF_KEYUP))
363 | time.sleep(0.2)
364 |
365 | def next_line():
366 | SendInput(Keyboard(VK_HOME), Keyboard(VK_DOWN))
367 | time.sleep(0.2)
368 | SendInput(Keyboard(VK_HOME, KEYEVENTF_KEYUP),
369 | Keyboard(VK_DOWN, KEYEVENTF_KEYUP))
370 | time.sleep(0.2)
371 |
372 | def prepare_text():
373 | # Open Text
374 | SendInput(Keyboard(KEY_M))
375 | time.sleep(0.2)
376 | SendInput(Keyboard(KEY_M, KEYEVENTF_KEYUP))
377 | time.sleep(0.2)
378 | # Goto Area
379 | SendInput(Keyboard(VK_TAB))
380 | time.sleep(0.2)
381 | SendInput(Keyboard(VK_TAB, KEYEVENTF_KEYUP))
382 | time.sleep(0.2)
383 | # Paste Message
384 | SendInput(Keyboard(VK_CONTROL), Keyboard(KEY_V))
385 | time.sleep(0.2)
386 | SendInput(Keyboard(VK_CONTROL, KEYEVENTF_KEYUP),
387 | Keyboard(KEY_V, KEYEVENTF_KEYUP))
388 | time.sleep(0.2)
389 | # Goto Button
390 | SendInput(Keyboard(VK_TAB))
391 | time.sleep(0.2)
392 | SendInput(Keyboard(VK_TAB, KEYEVENTF_KEYUP))
393 | time.sleep(0.2)
394 |
395 | def send_one_message():
396 | select_line()
397 | copy_line()
398 | next_line()
399 | switch_program()
400 | prepare_text()
401 | # Send Message
402 | SendInput(Keyboard(VK_RETURN))
403 | time.sleep(0.2)
404 | SendInput(Keyboard(VK_RETURN, KEYEVENTF_KEYUP))
405 | time.sleep(10)
406 | switch_program()
407 |
408 | def send_messages(total):
409 | time.sleep(10)
410 | for _ in range(total):
411 | send_one_message()
--------------------------------------------------------------------------------
/reference_materials/xlib-keysyms-to-python.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # coding: utf-8
3 | # Copyright 2015 Moses Palmér
4 | #
5 | # This program is free software: you can redistribute it and/or modify it
6 | # under the terms of the GNU General Public License as published by the Free
7 | # Software Foundation, either version 3 of the License, or (at your option)
8 | # any later version.
9 | #
10 | # This program is distributed in the hope that it will be useful, but WITHOUT
11 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 | # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
13 | # details.
14 | #
15 | # You should have received a copy of the GNU General Public License along with
16 | # this program. If not, see .
17 | """
18 | Converts the file xlib-keysyms.txt to a Python mapping from character to
19 | symbol name.
20 | """
21 |
22 | import os
23 |
24 | #: The path to the input file
25 | INPUT_PATH = os.path.join(
26 | os.path.dirname(__file__),
27 | 'xlib-keysyms.txt')
28 |
29 | #: The path to the output file
30 | OUTPUT_PATH = os.path.join(
31 | os.path.dirname(__file__),
32 | os.path.pardir,
33 | 'pykeyboard',
34 | 'x11_keysyms.py')
35 |
36 |
37 | def lines():
38 | """Yields all lines in the input file.
39 | """
40 | with open(INPUT_PATH) as f:
41 | for line in f:
42 | yield line.rstrip()
43 |
44 |
45 | def keysym_lines():
46 | """Yields all lines in the input file containing a keysym definition.
47 | """
48 | for line in lines():
49 | if not line:
50 | # Ignore empty lines
51 | continue
52 | elif line[0] == '#':
53 | # Ignore lines starting with '#'; it is also used to separate
54 | # keysym information from its name, but in the first position it is
55 | # used to mark comments
56 | continue
57 | else:
58 | yield line
59 |
60 |
61 | def keysym_definitions():
62 | """Yields all keysym definitions parsed as tuples.
63 | """
64 | for keysym_line in keysym_lines():
65 | # As described in the input text, the format of a line is:
66 | # 0x20 U0020 . # space /* optional comment */
67 | keysym_number, codepoint, status, _, name_part = [
68 | p.strip() for p in keysym_line.split(None, 4)]
69 | name = name_part.split()[0]
70 |
71 | yield (int(keysym_number, 16), codepoint[1:], status, name)
72 |
73 |
74 | def keysyms_from_strings():
75 | """Yields the tuple ``(character, symbol name)`` for all keysyms.
76 | """
77 | for number, codepoint, status, name in keysym_definitions():
78 | # Ignore keysyms that do not map to unicode characters
79 | if all(c == '0' for c in codepoint):
80 | continue
81 |
82 | # Ignore keysyms that are not well established
83 | if status != '.':
84 | continue
85 |
86 | yield (codepoint, name)
87 |
88 |
89 | if __name__ == '__main__':
90 | with open(OUTPUT_PATH, 'w') as f:
91 | f.write('''# coding: utf-8
92 | # Copyright 2015 Moses Palmér
93 | #
94 | # This program is free software: you can redistribute it and/or modify it
95 | # under the terms of the GNU General Public License as published by the Free
96 | # Software Foundation, either version 3 of the License, or (at your option)
97 | # any later version.
98 | #
99 | # This program is distributed in the hope that it will be useful, but WITHOUT
100 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
101 | # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
102 | # details.
103 | #
104 | # You should have received a copy of the GNU General Public License along with
105 | # this program. If not, see .
106 |
107 | KEYSYMS = {
108 | %s}
109 | ''' % ',\n'.join(
110 | ' u\'\\u%s\': \'%s\'' % (codepoint, name)
111 | for codepoint, name in keysyms_from_strings()))
112 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | try:
2 | from setuptools import setup
3 | except ImportError:
4 | from distutils.core import setup
5 | import os
6 | import sys
7 |
8 |
9 | def long_description():
10 | readme = os.path.join(os.path.dirname(__file__), 'README.md')
11 | with open(readme, 'r') as inf:
12 | readme_text = inf.read()
13 | return(readme_text)
14 |
15 | setup(name='PyUserInput',
16 | version='0.1.10',
17 | description='A simple, cross-platform module for mouse and keyboard control',
18 | long_description=long_description(),
19 | author='Paul Barton',
20 | #Original author of PyMouse: Pepijn de Vos
21 | author_email='pablo.barton@gmail.com',
22 | url='https://github.com/SavinaRoja/PyUserInput',
23 | package_dir = {'': '.'},
24 | packages = ['pykeyboard', 'pymouse'],
25 | license='http://www.gnu.org/licenses/gpl-3.0.html',
26 | keywords='mouse,keyboard user input event',
27 | )
28 |
29 | def dependency_check(dep_list):
30 | for dep in dep_list:
31 | try:
32 | __import__(dep)
33 | except ImportError:
34 | print('Missing dependency, could not import this module: {0}'.format(dep))
35 |
36 | #Check for dependencies
37 | if sys.platform == 'darwin': # Mac
38 | dependency_check(['Quartz', 'AppKit'])
39 | elif sys.platform == 'win32': # Windows
40 | dependency_check(['win32api', 'win32con', 'pythoncom', 'pyWinhook'])
41 | else: # X11 (LInux)
42 | dependency_check(['Xlib'])
43 |
--------------------------------------------------------------------------------
/tests/basic.py:
--------------------------------------------------------------------------------
1 | #Copyright 2013 Paul Barton
2 | #
3 | #This program is free software: you can redistribute it and/or modify
4 | #it under the terms of the GNU General Public License as published by
5 | #the Free Software Foundation, either version 3 of the License, or
6 | #(at your option) any later version.
7 | #
8 | #This program is distributed in the hope that it will be useful,
9 | #but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | #GNU General Public License for more details.
12 | #
13 | #You should have received a copy of the GNU General Public License
14 | #along with this program. If not, see .
15 |
16 | from pymouse import PyMouse
17 | import random, time
18 | try:
19 | from pymouse import PyMouseEvent
20 |
21 | class event(PyMouseEvent):
22 | def move(self, x, y):
23 | print("Mouse moved to", x, y)
24 |
25 | def click(self, x, y, button, press):
26 | if press:
27 | print("Mouse pressed at", x, y, "with button", button)
28 | else:
29 | print("Mouse released at", x, y, "with button", button)
30 |
31 | e = event()
32 | #e.capture = True
33 | e.start()
34 |
35 | except ImportError:
36 | print("Mouse events are not yet supported on your platform")
37 |
38 | m = PyMouse()
39 | try:
40 | size = m.screen_size()
41 | print("size: %s" % (str(size)))
42 |
43 | pos = (random.randint(0, size[0]), random.randint(0, size[1]))
44 | except:
45 | pos = (random.randint(0, 250), random.randint(0, 250))
46 |
47 | print("Position: %s" % (str(pos)))
48 |
49 | m.move(pos[0], pos[1])
50 |
51 | time.sleep(2)
52 |
53 | m.click(pos[0], pos[1], 1)
54 |
55 | time.sleep(2)
56 |
57 | m.click(pos[0], pos[1], 2)
58 |
59 | time.sleep(2)
60 |
61 | m.click(pos[0], pos[1], 3)
62 |
63 | try:
64 | e.stop()
65 | except:
66 | pass
67 |
--------------------------------------------------------------------------------
/tests/test_import_times.py:
--------------------------------------------------------------------------------
1 | # coding=utf8
2 |
3 | '''
4 | Tests that import times for pymouse and pykeyboard are not unreasonably long.
5 |
6 | This catches the slow-import-of-Quartz on Mac OS X due to pyobjc's slow
7 | behavior when import via from ... import *.
8 |
9 | Note that since this ultimately tests the import speed of pyobjc's Quartz
10 | package, only the first test can ever fail; after that, Quartz will have been
11 | imported and so subsequent from Quartz import * calls in later tests should
12 | take negligible time, even if they would, on their own, be very slow.
13 | '''
14 |
15 | import contextlib
16 | import time
17 |
18 | from nose import SkipTest
19 |
20 | @contextlib.contextmanager
21 | def check_execution_time(description, max_seconds):
22 | start_time = time.time()
23 | yield
24 | end_time = time.time()
25 | execution_time = end_time - start_time
26 | if execution_time > max_seconds:
27 | raise Exception("Took too long to complete %s" % description)
28 |
29 | def test_pymouse_import_time():
30 | # skip this test by default, call nosetests with --no-skip to enable
31 | raise SkipTest()
32 | with check_execution_time("importing pymouse", max_seconds=3):
33 | import pymouse
34 |
35 | def test_pykeyboard_import_time():
36 | # skip this test by default, call nosetests with --no-skip to enable
37 | raise SkipTest()
38 | with check_execution_time("importing pykeyboard", max_seconds=3):
39 | import pykeyboard
40 |
41 |
--------------------------------------------------------------------------------
/tests/test_unix.py:
--------------------------------------------------------------------------------
1 | '''
2 | Tested on linux.
3 |
4 | install: Xvfb, Xephyr, PyVirtualDisplay, nose
5 |
6 | on Ubuntu:
7 |
8 | sudo apt-get install python-nose
9 | sudo apt-get install xvfb
10 | sudo apt-get install xserver-xephyr
11 | sudo apt-get install python-setuptools
12 | sudo easy_install PyVirtualDisplay
13 |
14 | to start:
15 |
16 | nosetests -v
17 | '''
18 |
19 | from nose.tools import eq_
20 | from pymouse import PyMouse, PyMouseEvent
21 | from pyvirtualdisplay import Display
22 | from unittest import TestCase
23 | import time
24 |
25 | # 0 -> Xvfb
26 | # 1 -> Xephyr
27 | VISIBLE = 0
28 |
29 | screen_sizes = [
30 | (10, 20),
31 | (100, 200),
32 | (765, 666),
33 | ]
34 | positions = [
35 | (0, 5),
36 | (0, 0),
37 | (10, 20),
38 | (-10, -20),
39 | (2222, 2222),
40 | (5, 0),
41 | (9, 19),
42 | ]
43 |
44 | buttons = [1, 2, 3, 10] # 10 = mouse btn6
45 |
46 | class Event(PyMouseEvent):
47 | def reset(self):
48 | self.pos = None
49 | self.button = None
50 | self.press = None
51 | self.scroll_vertical = None
52 | self.scroll_horizontal = None
53 |
54 | def move(self, x, y):
55 | print("Mouse moved to", x, y)
56 | self.pos = (x, y)
57 |
58 | def click(self, x, y, button, press):
59 | if press:
60 | print("Mouse pressed at", x, y, "with button", button)
61 | else:
62 | print("Mouse released at", x, y, "with button", button)
63 | self.button = button
64 | self.press = press
65 |
66 | def scroll(self, x, y, vertical, horizontal):
67 | self.scroll_vertical = vertical
68 | self.scroll_horizontal = horizontal
69 |
70 | def expect_pos(pos, size):
71 | def expect(x, m):
72 | x = max(0, x)
73 | x = min(m - 1, x)
74 | return x
75 | expected_pos = (expect(pos[0], size[0]), expect(pos[1], size[1]))
76 | return expected_pos
77 |
78 | class Test(TestCase):
79 | def test_size(self):
80 | for size in screen_sizes:
81 | with Display(visible=VISIBLE, size=size):
82 | mouse = PyMouse()
83 | eq_(size, mouse.screen_size())
84 |
85 | def test_move(self):
86 | for size in screen_sizes:
87 | with Display(visible=VISIBLE, size=size):
88 | mouse = PyMouse()
89 | for p in positions:
90 | mouse.move(*p)
91 | eq_(expect_pos(p, size), mouse.position())
92 |
93 | def test_event(self):
94 | for size in screen_sizes:
95 | with Display(visible=VISIBLE, size=size):
96 | time.sleep(1.0) # TODO: how long should we wait?
97 | mouse = PyMouse()
98 | event = Event()
99 | event.start()
100 | # check move
101 | for p in positions:
102 | event.reset()
103 | mouse.move(*p)
104 | time.sleep(0.01)
105 | print('check ', expect_pos(p, size), '=', event.pos)
106 | eq_(expect_pos(p, size), event.pos)
107 | # check buttons
108 | for btn in buttons:
109 | # press
110 | event.reset()
111 | mouse.press(0, 0, btn)
112 | time.sleep(0.01)
113 | print("check button", btn, "pressed")
114 | eq_(btn, event.button)
115 | eq_(True, event.press)
116 | # release
117 | event.reset()
118 | mouse.release(0, 0, btn)
119 | time.sleep(0.01)
120 | print("check button", btn, "released")
121 | eq_(btn, event.button)
122 | eq_(False, event.press)
123 | # check scroll
124 | def check_scroll(btn, vertical=None, horizontal=None):
125 | event.reset()
126 | mouse.press(0, 0, btn)
127 | time.sleep(0.01)
128 | if vertical:
129 | eq_(vertical, event.scroll_vertical)
130 | elif horizontal:
131 | eq_(horizontal, event.scroll_horizontal)
132 | print("check scroll up")
133 | check_scroll(4, 1, 0)
134 | print("check scroll down")
135 | check_scroll(5, -1, 0)
136 | print("check scroll left")
137 | check_scroll(6, 0, 1)
138 | print("check scroll right")
139 | check_scroll(7, 0, -1)
140 | event.stop()
141 |
--------------------------------------------------------------------------------