1# Copyright 2004-2021 Tom Rothamel <pytom@bishoujo.us>
2#
3# Permission is hereby granted, free of charge, to any person
4# obtaining a copy of this software and associated documentation files
5# (the "Software"), to deal in the Software without restriction,
6# including without limitation the rights to use, copy, modify, merge,
7# publish, distribute, sublicense, and/or sell copies of the Software,
8# and to permit persons to whom the Software is furnished to do so,
9# subject to the following conditions:
10#
11# The above copyright notice and this permission notice shall be
12# included in all copies or substantial portions of the Software.
13#
14# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22from __future__ import division, absolute_import, with_statement, print_function, unicode_literals
23from renpy.compat import *
24
25import pygame_sdl2
26
27code_to_unicode = {
28    pygame_sdl2.K_UNKNOWN : "",
29    pygame_sdl2.K_RETURN : "\n",
30    pygame_sdl2.K_ESCAPE : "\e",
31    pygame_sdl2.K_BACKSPACE : "\b",
32    pygame_sdl2.K_TAB : "\t",
33    pygame_sdl2.K_SPACE : " ",
34    pygame_sdl2.K_EXCLAIM : "!",
35    pygame_sdl2.K_QUOTEDBL : "\"",
36    pygame_sdl2.K_HASH : "#",
37    pygame_sdl2.K_PERCENT : "%",
38    pygame_sdl2.K_DOLLAR : "$",
39    pygame_sdl2.K_AMPERSAND : "&",
40    pygame_sdl2.K_QUOTE : "'",
41    pygame_sdl2.K_LEFTPAREN : "(",
42    pygame_sdl2.K_RIGHTPAREN : ")",
43    pygame_sdl2.K_ASTERISK : "*",
44    pygame_sdl2.K_PLUS : "+",
45    pygame_sdl2.K_COMMA : ",",
46    pygame_sdl2.K_MINUS : "-",
47    pygame_sdl2.K_PERIOD : ".",
48    pygame_sdl2.K_SLASH : "/",
49    pygame_sdl2.K_0 : "0",
50    pygame_sdl2.K_1 : "1",
51    pygame_sdl2.K_2 : "2",
52    pygame_sdl2.K_3 : "3",
53    pygame_sdl2.K_4 : "4",
54    pygame_sdl2.K_5 : "5",
55    pygame_sdl2.K_6 : "6",
56    pygame_sdl2.K_7 : "7",
57    pygame_sdl2.K_8 : "8",
58    pygame_sdl2.K_9 : "9",
59    pygame_sdl2.K_COLON : ":",
60    pygame_sdl2.K_SEMICOLON : ";",
61    pygame_sdl2.K_LESS : "<",
62    pygame_sdl2.K_EQUALS : ":",
63    pygame_sdl2.K_GREATER : ">",
64    pygame_sdl2.K_QUESTION : "?",
65    pygame_sdl2.K_AT : "@",
66    pygame_sdl2.K_LEFTBRACKET : "[",
67    pygame_sdl2.K_BACKSLASH : "\\",
68    pygame_sdl2.K_RIGHTBRACKET : "]",
69    pygame_sdl2.K_CARET : "^",
70    pygame_sdl2.K_UNDERSCORE : "_",
71    pygame_sdl2.K_BACKQUOTE : "`",
72    pygame_sdl2.K_a : "a",
73    pygame_sdl2.K_b : "b",
74    pygame_sdl2.K_c : "c",
75    pygame_sdl2.K_d : "d",
76    pygame_sdl2.K_e : "e",
77    pygame_sdl2.K_f : "f",
78    pygame_sdl2.K_g : "g",
79    pygame_sdl2.K_h : "h",
80    pygame_sdl2.K_i : "i",
81    pygame_sdl2.K_j : "j",
82    pygame_sdl2.K_k : "k",
83    pygame_sdl2.K_l : "l",
84    pygame_sdl2.K_m : "m",
85    pygame_sdl2.K_n : "n",
86    pygame_sdl2.K_o : "o",
87    pygame_sdl2.K_p : "p",
88    pygame_sdl2.K_q : "q",
89    pygame_sdl2.K_r : "r",
90    pygame_sdl2.K_s : "s",
91    pygame_sdl2.K_t : "t",
92    pygame_sdl2.K_u : "u",
93    pygame_sdl2.K_v : "v",
94    pygame_sdl2.K_w : "w",
95    pygame_sdl2.K_x : "x",
96    pygame_sdl2.K_y : "y",
97    pygame_sdl2.K_z : "z",
98    pygame_sdl2.K_CAPSLOCK : "",
99    pygame_sdl2.K_F1 : "",
100    pygame_sdl2.K_F2 : "",
101    pygame_sdl2.K_F3 : "",
102    pygame_sdl2.K_F4 : "",
103    pygame_sdl2.K_F5 : "",
104    pygame_sdl2.K_F6 : "",
105    pygame_sdl2.K_F7 : "",
106    pygame_sdl2.K_F8 : "",
107    pygame_sdl2.K_F9 : "",
108    pygame_sdl2.K_F10 : "",
109    pygame_sdl2.K_F11 : "",
110    pygame_sdl2.K_F12 : "",
111    pygame_sdl2.K_PRINTSCREEN : "",
112    pygame_sdl2.K_SCROLLLOCK : "",
113    pygame_sdl2.K_PAUSE : "",
114    pygame_sdl2.K_INSERT : "",
115    pygame_sdl2.K_HOME : "",
116    pygame_sdl2.K_PAGEUP : "",
117    pygame_sdl2.K_DELETE : "",
118    pygame_sdl2.K_END : "",
119    pygame_sdl2.K_PAGEDOWN : "",
120    pygame_sdl2.K_RIGHT : "",
121    pygame_sdl2.K_LEFT : "",
122    pygame_sdl2.K_DOWN : "",
123    pygame_sdl2.K_UP : "",
124    pygame_sdl2.K_NUMLOCKCLEAR : "",
125    pygame_sdl2.K_KP_DIVIDE : "/",
126    pygame_sdl2.K_KP_MULTIPLY : "*",
127    pygame_sdl2.K_KP_MINUS : "-",
128    pygame_sdl2.K_KP_PLUS : "+",
129    pygame_sdl2.K_KP_ENTER : "\n",
130    pygame_sdl2.K_KP_1 : "1",
131    pygame_sdl2.K_KP_2 : "2",
132    pygame_sdl2.K_KP_3 : "3",
133    pygame_sdl2.K_KP_4 : "4",
134    pygame_sdl2.K_KP_5 : "5",
135    pygame_sdl2.K_KP_6 : "6",
136    pygame_sdl2.K_KP_7 : "7",
137    pygame_sdl2.K_KP_8 : "8",
138    pygame_sdl2.K_KP_9 : "9",
139    pygame_sdl2.K_KP_0 : "0",
140    pygame_sdl2.K_KP_PERIOD : ".",
141    pygame_sdl2.K_APPLICATION : "",
142    pygame_sdl2.K_POWER : "",
143    pygame_sdl2.K_KP_EQUALS : ":",
144    pygame_sdl2.K_F13 : "",
145    pygame_sdl2.K_F14 : "",
146    pygame_sdl2.K_F15 : "",
147    pygame_sdl2.K_F16 : "",
148    pygame_sdl2.K_F17 : "",
149    pygame_sdl2.K_F18 : "",
150    pygame_sdl2.K_F19 : "",
151    pygame_sdl2.K_F20 : "",
152    pygame_sdl2.K_F21 : "",
153    pygame_sdl2.K_F22 : "",
154    pygame_sdl2.K_F23 : "",
155    pygame_sdl2.K_F24 : "",
156    pygame_sdl2.K_EXECUTE : "",
157    pygame_sdl2.K_HELP : "",
158    pygame_sdl2.K_MENU : "",
159    pygame_sdl2.K_SELECT : "",
160    pygame_sdl2.K_STOP : "",
161    pygame_sdl2.K_AGAIN : "",
162    pygame_sdl2.K_UNDO : "",
163    pygame_sdl2.K_CUT : "",
164    pygame_sdl2.K_COPY : "",
165    pygame_sdl2.K_PASTE : "",
166    pygame_sdl2.K_FIND : "",
167    pygame_sdl2.K_MUTE : "",
168    pygame_sdl2.K_VOLUMEUP : "",
169    pygame_sdl2.K_VOLUMEDOWN : "",
170    pygame_sdl2.K_KP_COMMA : "",
171    pygame_sdl2.K_KP_EQUALSAS400 : "",
172    pygame_sdl2.K_ALTERASE : "",
173    pygame_sdl2.K_SYSREQ : "",
174    pygame_sdl2.K_CANCEL : "",
175    pygame_sdl2.K_CLEAR : "",
176    pygame_sdl2.K_PRIOR : "",
177    pygame_sdl2.K_RETURN2 : "",
178    pygame_sdl2.K_SEPARATOR : "",
179    pygame_sdl2.K_OUT : "",
180    pygame_sdl2.K_OPER : "",
181    pygame_sdl2.K_CLEARAGAIN : "",
182    pygame_sdl2.K_CRSEL : "",
183    pygame_sdl2.K_EXSEL : "",
184    pygame_sdl2.K_KP_00 : "",
185    pygame_sdl2.K_KP_000 : "",
186    pygame_sdl2.K_THOUSANDSSEPARATOR : "",
187    pygame_sdl2.K_DECIMALSEPARATOR : "",
188    pygame_sdl2.K_CURRENCYUNIT : "",
189    pygame_sdl2.K_CURRENCYSUBUNIT : "",
190    pygame_sdl2.K_KP_LEFTPAREN : "",
191    pygame_sdl2.K_KP_RIGHTPAREN : "",
192    pygame_sdl2.K_KP_LEFTBRACE : "",
193    pygame_sdl2.K_KP_RIGHTBRACE : "",
194    pygame_sdl2.K_KP_TAB : "",
195    pygame_sdl2.K_KP_BACKSPACE : "",
196    pygame_sdl2.K_KP_A : "",
197    pygame_sdl2.K_KP_B : "",
198    pygame_sdl2.K_KP_C : "",
199    pygame_sdl2.K_KP_D : "",
200    pygame_sdl2.K_KP_E : "",
201    pygame_sdl2.K_KP_F : "",
202    pygame_sdl2.K_KP_XOR : "",
203    pygame_sdl2.K_KP_POWER : "",
204    pygame_sdl2.K_KP_PERCENT : "",
205    pygame_sdl2.K_KP_LESS : "",
206    pygame_sdl2.K_KP_GREATER : "",
207    pygame_sdl2.K_KP_AMPERSAND : "",
208    pygame_sdl2.K_KP_DBLAMPERSAND : "",
209    pygame_sdl2.K_KP_VERTICALBAR : "",
210    pygame_sdl2.K_KP_DBLVERTICALBAR : "",
211    pygame_sdl2.K_KP_COLON : "",
212    pygame_sdl2.K_KP_HASH : "",
213    pygame_sdl2.K_KP_SPACE : "",
214    pygame_sdl2.K_KP_AT : "",
215    pygame_sdl2.K_KP_EXCLAM : "",
216    pygame_sdl2.K_KP_MEMSTORE : "",
217    pygame_sdl2.K_KP_MEMRECALL : "",
218    pygame_sdl2.K_KP_MEMCLEAR : "",
219    pygame_sdl2.K_KP_MEMADD : "",
220    pygame_sdl2.K_KP_MEMSUBTRACT : "",
221    pygame_sdl2.K_KP_MEMMULTIPLY : "",
222    pygame_sdl2.K_KP_MEMDIVIDE : "",
223    pygame_sdl2.K_KP_PLUSMINUS : "",
224    pygame_sdl2.K_KP_CLEAR : "",
225    pygame_sdl2.K_KP_CLEARENTRY : "",
226    pygame_sdl2.K_KP_BINARY : "",
227    pygame_sdl2.K_KP_OCTAL : "",
228    pygame_sdl2.K_KP_DECIMAL : "",
229    pygame_sdl2.K_KP_HEXADECIMAL : "",
230    pygame_sdl2.K_LCTRL : "",
231    pygame_sdl2.K_LSHIFT : "",
232    pygame_sdl2.K_LALT : "",
233    pygame_sdl2.K_LGUI : "",
234    pygame_sdl2.K_RCTRL : "",
235    pygame_sdl2.K_RSHIFT : "",
236    pygame_sdl2.K_RALT : "",
237    pygame_sdl2.K_RGUI : "",
238    pygame_sdl2.K_MODE : "",
239    pygame_sdl2.K_AUDIONEXT : "",
240    pygame_sdl2.K_AUDIOPREV : "",
241    pygame_sdl2.K_AUDIOSTOP : "",
242    pygame_sdl2.K_AUDIOPLAY : "",
243    pygame_sdl2.K_AUDIOMUTE : "",
244    pygame_sdl2.K_MEDIASELECT : "",
245    pygame_sdl2.K_WWW : "",
246    pygame_sdl2.K_MAIL : "",
247    pygame_sdl2.K_CALCULATOR : "",
248    pygame_sdl2.K_COMPUTER : "",
249    pygame_sdl2.K_AC_SEARCH : "",
250    pygame_sdl2.K_AC_HOME : "",
251    pygame_sdl2.K_AC_BACK : "",
252    pygame_sdl2.K_AC_FORWARD : "",
253    pygame_sdl2.K_AC_STOP : "",
254    pygame_sdl2.K_AC_REFRESH : "",
255    pygame_sdl2.K_AC_BOOKMARKS : "",
256    pygame_sdl2.K_BRIGHTNESSDOWN : "",
257    pygame_sdl2.K_BRIGHTNESSUP : "",
258    pygame_sdl2.K_DISPLAYSWITCH : "",
259    pygame_sdl2.K_KBDILLUMTOGGLE : "",
260    pygame_sdl2.K_KBDILLUMDOWN : "",
261    pygame_sdl2.K_KBDILLUMUP : "",
262    pygame_sdl2.K_EJECT : "",
263    pygame_sdl2.K_SLEEP : "",
264}
265
266unicode_to_code = { }
267for k, v in sorted(code_to_unicode.items()):
268    if v and (v not in unicode_to_code):
269        unicode_to_code[v] = k
270
271
272def get_keycode(node, keysym):
273
274    c = keysym.split("_")
275
276    mods = 0
277
278    while c:
279        if c[0] == "shift":
280            mods |= pygame_sdl2.KMOD_LSHIFT
281            c.pop(0)
282        elif c[0] == "ctrl":
283            mods |= pygame_sdl2.KMOD_LCTRL
284            c.pop(0)
285        elif c[0] == "alt":
286            mods |= pygame_sdl2.KMOD_LALT
287            c.pop(0)
288        elif c[0] == "meta":
289            mods |= pygame_sdl2.KMOD_LMETA
290            c.pop(0)
291        else:
292            break
293
294    key = "_".join(c)
295
296    if key in unicode_to_code:
297        if ord(key) >= 32:
298            u = key
299        else:
300            u = None
301
302        code = unicode_to_code[key]
303
304    elif key.lower() in unicode_to_code:
305        u = key
306        code = unicode_to_code[key.lower()]
307        mods |= pygame_sdl2.KMOD_LSHIFT
308
309    else:
310        code = getattr(pygame_sdl2, "K_" + key, None)
311
312        if code is None:
313            raise Exception("Could not find keysym {!r} at {}:{}.".format(keysym, node.filename, node.linenumber))
314
315        u = code_to_unicode.get(code, "")
316
317        if not u:
318            u = None
319        elif ord(u) < 32:
320            u = None
321
322    return code, u, mods
323
324
325def down(node, keysym):
326    code, u, mods = get_keycode(node, keysym)
327
328    if pygame_sdl2.key.text_input:
329        pygame_sdl2.event.post(pygame_sdl2.event.Event(
330            pygame_sdl2.KEYDOWN,
331            unicode='',
332            key=code,
333            scancode=code,
334            mod=mods,
335            repeat=False,
336            test=True))
337
338        pygame_sdl2.event.post(pygame_sdl2.event.Event(
339            pygame_sdl2.TEXTINPUT,
340            text=u,
341            test=True))
342
343    else:
344
345        pygame_sdl2.event.post(pygame_sdl2.event.Event(
346            pygame_sdl2.KEYDOWN,
347            unicode=u,
348            key=code,
349            scancode=code,
350            mod=mods,
351            repeat=False,
352            test=True))
353
354
355def up(node, keysym):
356    code, _, mods = get_keycode(node, keysym)
357
358    pygame_sdl2.event.post(pygame_sdl2.event.Event(
359        pygame_sdl2.KEYUP,
360        key=code,
361        scancode=code,
362        mod=mods,
363        repeat=False,
364        test=True))
365