14749e5c039569704f157d2f1e4a2e3622fa9ede
[pcsx_rearmed.git] / frontend / common / in_sdl.c
1 /*
2  * (C) GraÅžvydas "notaz" Ignotas, 2012
3  *
4  * This work is licensed under the terms of any of these licenses
5  * (at your option):
6  *  - GNU GPL, version 2 or later.
7  *  - GNU LGPL, version 2.1 or later.
8  * See the COPYING file in the top-level directory.
9  */
10
11 #include <stdio.h>
12 #include <SDL.h>
13 #include "input.h"
14 #include "in_sdl.h"
15
16 #define IN_SDL_PREFIX "sdl:"
17 /* should be machine word for best performace */
18 typedef unsigned long keybits_t;
19 #define KEYBITS_WORD_BITS (sizeof(keybits_t) * 8)
20
21 static const char * const in_sdl_keys[SDLK_LAST] = {
22         [SDLK_BACKSPACE] = "backspace",
23         [SDLK_TAB] = "tab",
24         [SDLK_CLEAR] = "clear",
25         [SDLK_RETURN] = "return",
26         [SDLK_PAUSE] = "pause",
27         [SDLK_ESCAPE] = "escape",
28         [SDLK_SPACE] = "space",
29         [SDLK_EXCLAIM]  = "!",
30         [SDLK_QUOTEDBL]  = "\"",
31         [SDLK_HASH]  = "#",
32         [SDLK_DOLLAR]  = "$",
33         [SDLK_AMPERSAND]  = "&",
34         [SDLK_QUOTE] = "'",
35         [SDLK_LEFTPAREN] = "(",
36         [SDLK_RIGHTPAREN] = ")",
37         [SDLK_ASTERISK] = "*",
38         [SDLK_PLUS] = "+",
39         [SDLK_COMMA] = ",",
40         [SDLK_MINUS] = "-",
41         [SDLK_PERIOD] = ".",
42         [SDLK_SLASH] = "/",
43         [SDLK_0] = "0",
44         [SDLK_1] = "1",
45         [SDLK_2] = "2",
46         [SDLK_3] = "3",
47         [SDLK_4] = "4",
48         [SDLK_5] = "5",
49         [SDLK_6] = "6",
50         [SDLK_7] = "7",
51         [SDLK_8] = "8",
52         [SDLK_9] = "9",
53         [SDLK_COLON] = ":",
54         [SDLK_SEMICOLON] = ",",
55         [SDLK_LESS] = "<",
56         [SDLK_EQUALS] = "=",
57         [SDLK_GREATER] = ">",
58         [SDLK_QUESTION] = "?",
59         [SDLK_AT] = "@",
60         [SDLK_LEFTBRACKET] = "[",
61         [SDLK_BACKSLASH] = "\\",
62         [SDLK_RIGHTBRACKET] = "]",
63         [SDLK_CARET] = "^",
64         [SDLK_UNDERSCORE] = "_",
65         [SDLK_BACKQUOTE] = "`",
66         [SDLK_a] = "a",
67         [SDLK_b] = "b",
68         [SDLK_c] = "c",
69         [SDLK_d] = "d",
70         [SDLK_e] = "e",
71         [SDLK_f] = "f",
72         [SDLK_g] = "g",
73         [SDLK_h] = "h",
74         [SDLK_i] = "i",
75         [SDLK_j] = "j",
76         [SDLK_k] = "k",
77         [SDLK_l] = "l",
78         [SDLK_m] = "m",
79         [SDLK_n] = "n",
80         [SDLK_o] = "o",
81         [SDLK_p] = "p",
82         [SDLK_q] = "q",
83         [SDLK_r] = "r",
84         [SDLK_s] = "s",
85         [SDLK_t] = "t",
86         [SDLK_u] = "u",
87         [SDLK_v] = "v",
88         [SDLK_w] = "w",
89         [SDLK_x] = "x",
90         [SDLK_y] = "y",
91         [SDLK_z] = "z",
92         [SDLK_DELETE] = "delete",
93
94         [SDLK_KP0] = "[0]",
95         [SDLK_KP1] = "[1]",
96         [SDLK_KP2] = "[2]",
97         [SDLK_KP3] = "[3]",
98         [SDLK_KP4] = "[4]",
99         [SDLK_KP5] = "[5]",
100         [SDLK_KP6] = "[6]",
101         [SDLK_KP7] = "[7]",
102         [SDLK_KP8] = "[8]",
103         [SDLK_KP9] = "[9]",
104         [SDLK_KP_PERIOD] = "[.]",
105         [SDLK_KP_DIVIDE] = "[/]",
106         [SDLK_KP_MULTIPLY] = "[*]",
107         [SDLK_KP_MINUS] = "[-]",
108         [SDLK_KP_PLUS] = "[+]",
109         [SDLK_KP_ENTER] = "enter",
110         [SDLK_KP_EQUALS] = "equals",
111
112         [SDLK_UP] = "up",
113         [SDLK_DOWN] = "down",
114         [SDLK_RIGHT] = "right",
115         [SDLK_LEFT] = "left",
116         [SDLK_DOWN] = "down",
117         [SDLK_INSERT] = "insert",
118         [SDLK_HOME] = "home",
119         [SDLK_END] = "end",
120         [SDLK_PAGEUP] = "page up",
121         [SDLK_PAGEDOWN] = "page down",
122
123         [SDLK_F1] = "f1",
124         [SDLK_F2] = "f2",
125         [SDLK_F3] = "f3",
126         [SDLK_F4] = "f4",
127         [SDLK_F5] = "f5",
128         [SDLK_F6] = "f6",
129         [SDLK_F7] = "f7",
130         [SDLK_F8] = "f8",
131         [SDLK_F9] = "f9",
132         [SDLK_F10] = "f10",
133         [SDLK_F11] = "f11",
134         [SDLK_F12] = "f12",
135         [SDLK_F13] = "f13",
136         [SDLK_F14] = "f14",
137         [SDLK_F15] = "f15",
138
139         [SDLK_NUMLOCK] = "numlock",
140         [SDLK_CAPSLOCK] = "caps lock",
141         [SDLK_SCROLLOCK] = "scroll lock",
142         [SDLK_RSHIFT] = "right shift",
143         [SDLK_LSHIFT] = "left shift",
144         [SDLK_RCTRL] = "right ctrl",
145         [SDLK_LCTRL] = "left ctrl",
146         [SDLK_RALT] = "right alt",
147         [SDLK_LALT] = "left alt",
148         [SDLK_RMETA] = "right meta",
149         [SDLK_LMETA] = "left meta",
150         [SDLK_LSUPER] = "left super",   /* "Windows" keys */
151         [SDLK_RSUPER] = "right super",  
152         [SDLK_MODE] = "alt gr",
153         [SDLK_COMPOSE] = "compose",
154 };
155
156 static void in_sdl_probe(void)
157 {
158         keybits_t *keystate;
159
160         keystate = calloc(SDLK_LAST / KEYBITS_WORD_BITS + 1, sizeof(keybits_t));
161         if (keystate == NULL) {
162                 fprintf(stderr, "in_sdl: OOM\n");
163                 return;
164         }
165
166         in_register(IN_SDL_PREFIX "keys", -1, keystate, SDLK_LAST,
167                 in_sdl_keys, 0);
168 }
169
170 static void in_sdl_free(void *drv_data)
171 {
172         keybits_t *keystate = drv_data;
173
174         if (keystate != NULL)
175                 free(keystate);
176 }
177
178 static const char * const *
179 in_sdl_get_key_names(int *count)
180 {
181         *count = SDLK_LAST;
182         return in_sdl_keys;
183 }
184
185 /* could use SDL_GetKeyState, but this gives better packing */
186 static void update_keystate(keybits_t *keystate, int sym, int is_down)
187 {
188         keybits_t *ks_word, mask;
189
190         mask = 1;
191         mask <<= sym & (KEYBITS_WORD_BITS - 1);
192         ks_word = keystate + sym / KEYBITS_WORD_BITS;
193         if (is_down)
194                 *ks_word |= mask;
195         else
196                 *ks_word &= ~mask;
197 }
198
199 static int in_sdl_update(void *drv_data, const int *binds, int *result)
200 {
201         keybits_t *keystate = drv_data, mask;
202         SDL_Event event;
203         int i, sym, bit, b;
204
205         while (SDL_PollEvent(&event)) {
206                 if (event.type != SDL_KEYDOWN && event.type != SDL_KEYUP)
207                         continue;
208
209                 update_keystate(keystate, event.key.keysym.sym,
210                         event.type == SDL_KEYDOWN);
211         }
212
213         for (i = 0; i < SDLK_LAST / KEYBITS_WORD_BITS + 1; i++) {
214                 mask = keystate[i];
215                 if (mask == 0)
216                         continue;
217                 for (bit = 0; mask != 0; bit++, mask >>= 1) {
218                         if ((mask & 1) == 0)
219                                 continue;
220                         sym = i * KEYBITS_WORD_BITS + bit;
221
222                         for (b = 0; b < IN_BINDTYPE_COUNT; b++)
223                                 result[b] |= binds[IN_BIND_OFFS(sym, b)];
224                 }
225         }
226
227         return 0;
228 }
229
230 static int in_sdl_update_keycode(void *drv_data, int *is_down)
231 {
232         int ret, ret_kc = -1, ret_down = 0;
233         SDL_Event event;
234
235         ret = SDL_PollEvent(&event);
236         if (ret == 0)
237                 goto out;
238         if (event.type != SDL_KEYDOWN && event.type != SDL_KEYUP)
239                 goto out;
240
241         if (event.key.type == SDL_KEYDOWN)
242                 ret_down = 1;
243         ret_kc = event.key.keysym.sym;
244         update_keystate(drv_data, ret_kc, ret_down);
245 out:
246         if (is_down != NULL)
247                 *is_down = ret_down;
248
249         return ret_kc;
250 }
251
252 static const struct {
253         short key;
254         short pbtn;
255 } key_pbtn_map[] =
256 {
257         { SDLK_UP,      PBTN_UP },
258         { SDLK_DOWN,    PBTN_DOWN },
259         { SDLK_LEFT,    PBTN_LEFT },
260         { SDLK_RIGHT,   PBTN_RIGHT },
261         /* XXX: maybe better set this from it's plat code somehow */
262         { SDLK_RETURN,  PBTN_MOK },
263         { SDLK_ESCAPE,  PBTN_MBACK },
264         { SDLK_a,       PBTN_MA2 },
265         { SDLK_s,       PBTN_MA3 },
266         { SDLK_BACKSLASH,    PBTN_MENU },
267         { SDLK_LEFTBRACKET,  PBTN_L },
268         { SDLK_RIGHTBRACKET, PBTN_R },
269 };
270
271 #define KEY_PBTN_MAP_SIZE (sizeof(key_pbtn_map) / sizeof(key_pbtn_map[0]))
272
273 static int in_sdl_menu_translate(void *drv_data, int keycode, char *charcode)
274 {
275         int ret = 0;
276         int i;
277
278         if (keycode < 0)
279         {
280                 /* menu -> kc */
281                 keycode = -keycode;
282                 for (i = 0; i < KEY_PBTN_MAP_SIZE; i++)
283                         if (key_pbtn_map[i].pbtn == keycode)
284                                 return key_pbtn_map[i].key;
285         }
286         else
287         {
288                 for (i = 0; i < KEY_PBTN_MAP_SIZE; i++) {
289                         if (key_pbtn_map[i].key == keycode) {
290                                 ret = key_pbtn_map[i].pbtn;
291                                 break;
292                         }
293                 }
294
295                 if (charcode != NULL && (unsigned int)keycode < SDLK_LAST &&
296                     in_sdl_keys[keycode] != NULL && in_sdl_keys[keycode][1] == 0)
297                 {
298                         ret |= PBTN_CHAR;
299                         *charcode = in_sdl_keys[keycode][0];
300                 }
301         }
302
303         return ret;
304 }
305
306 static const in_drv_t in_sdl_drv = {
307         .prefix         = IN_SDL_PREFIX,
308         .probe          = in_sdl_probe,
309         .free           = in_sdl_free,
310         .get_key_names  = in_sdl_get_key_names,
311         .update         = in_sdl_update,
312         .update_keycode = in_sdl_update_keycode,
313         .menu_translate = in_sdl_menu_translate,
314 };
315
316 void in_sdl_init(const struct in_default_bind *defbinds)
317 {
318         in_register_driver(&in_sdl_drv, defbinds);
319 }
320