remove const from TPL_OpenTPLFromMemory since the memory is altered
[libogc.git] / libwiikeyboard / keyboard.c
blobdd4f13b0a8d06a42b16caee782c10cb9162faab0
1 /*-------------------------------------------------------------
3 keyboard.c -- keyboard event system
5 Copyright (C) 2008, 2009
6 DAVY Guillaume davyg2@gmail.com
7 Brian Johnson brijohn@gmail.com
8 dhewg
10 This software is provided 'as-is', without any express or implied
11 warranty. In no event will the authors be held liable for any
12 damages arising from the use of this software.
14 Permission is granted to anyone to use this software for any
15 purpose, including commercial applications, and to alter it and
16 redistribute it freely, subject to the following restrictions:
18 1. The origin of this software must not be misrepresented; you
19 must not claim that you wrote the original software. If you use
20 this software in a product, an acknowledgment in the product
21 documentation would be appreciated but is not required.
23 2. Altered source versions must be plainly marked as such, and
24 must not be misrepresented as being the original software.
26 3. This notice may not be removed or altered from any source
27 distribution.
29 -------------------------------------------------------------*/
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <sys/iosupport.h>
34 #include <stdio.h>
35 #include <malloc.h>
36 #include <ctype.h>
37 #include <string.h>
38 #include <fcntl.h>
39 #include <unistd.h>
41 #include <gccore.h>
42 #include <ogc/usb.h>
43 #include <ogc/lwp_queue.h>
45 #include <wiikeyboard/usbkeyboard.h>
46 #include <wiikeyboard/keyboard.h>
48 #include "wsksymvar.h"
50 #define KBD_THREAD_STACKSIZE (1024 * 4)
51 #define KBD_THREAD_PRIO 64
52 #define KBD_THREAD_UDELAY (1000 * 10)
53 #define KBD_THREAD_KBD_SCAN_INTERVAL (3 * 100)
55 static lwp_queue _queue;
56 static lwp_t _kbd_thread = LWP_THREAD_NULL;
57 static lwp_t _kbd_buf_thread = LWP_THREAD_NULL;
58 static bool _kbd_thread_running = false;
59 static bool _kbd_thread_quit = false;
61 keysym_t ksym_upcase(keysym_t);
63 extern const struct wscons_keydesc ukbd_keydesctab[];
65 static struct wskbd_mapdata _ukbd_keymapdata = {
66 ukbd_keydesctab,
67 KB_NONE
70 struct nameint {
71 int value;
72 char *name;
75 static struct nameint kbdenc_tab[] = {
76 KB_ENCTAB
79 static struct nameint kbdvar_tab[] = {
80 KB_VARTAB
83 static int _sc_maplen = 0; /* number of entries in sc_map */
84 static struct wscons_keymap *_sc_map = 0; /* current translation map */
86 static u16 _modifiers;
88 static int _composelen; /* remaining entries in _composebuf */
89 static keysym_t _composebuf[2];
91 typedef struct {
92 u8 keycode;
93 u16 symbol;
94 } _keyheld;
96 #define MAXHELD 8
97 static _keyheld _held[MAXHELD];
99 typedef struct {
100 lwp_node node;
101 keyboard_event event;
102 } _node;
104 static keyPressCallback _readKey_cb = NULL;
106 static u8 *_kbd_stack[KBD_THREAD_STACKSIZE] ATTRIBUTE_ALIGN(8);
107 static u8 *_kbd_buf_stack[KBD_THREAD_STACKSIZE] ATTRIBUTE_ALIGN(8);
109 static kbd_t _get_keymap_by_name(const char *identifier) {
110 char name[64];
111 u8 i, j;
112 kbd_t encoding, variant;
114 kbd_t res = KB_NONE;
116 if (!identifier || (strlen(identifier) < 2))
117 return res;
119 i = 0;
120 for (i = 0; ukbd_keydesctab[i].name != 0; ++i) {
121 if (ukbd_keydesctab[i].name & KB_HANDLEDBYWSKBD)
122 continue;
124 encoding = KB_ENCODING(ukbd_keydesctab[i].name);
125 variant = KB_VARIANT(ukbd_keydesctab[i].name);
127 name[0] = 0;
128 for (j = 0; j < sizeof(kbdenc_tab) / sizeof(struct nameint); ++j)
129 if (encoding == kbdenc_tab[j].value) {
130 strcpy(name, kbdenc_tab[j].name);
131 break;
134 if (strlen(name) < 1)
135 continue;
137 for (j = 0; j < sizeof(kbdvar_tab) / sizeof(struct nameint); ++j)
138 if (variant & kbdvar_tab[j].value) {
139 strcat(name, "-");
140 strcat(name, kbdvar_tab[j].name);
143 if (!strcmp(identifier, name)) {
144 res = ukbd_keydesctab[i].name;
145 break;
149 return res;
152 //Add an event to the event queue
153 static s32 _kbd_addEvent(const keyboard_event *event) {
154 _node *n = malloc(sizeof(_node));
155 n->event = *event;
157 __lwp_queue_append(&_queue, (lwp_node*) n);
159 return 1;
162 void update_modifier(u_int type, int toggle, int mask) {
163 if (toggle) {
164 if (type == KEYBOARD_PRESSED)
165 _modifiers ^= mask;
166 } else {
167 if (type == KEYBOARD_RELEASED)
168 _modifiers &= ~mask;
169 else
170 _modifiers |= mask;
174 //Event callback, gets called when an event occurs in usbkeyboard
175 static void _kbd_event_cb(USBKeyboard_event kevent)
177 keyboard_event event;
178 struct wscons_keymap kp;
179 keysym_t *group;
180 int gindex;
181 keysym_t ksym;
182 int i;
184 switch (kevent.type) {
185 case USBKEYBOARD_DISCONNECTED:
186 event.type = KEYBOARD_DISCONNECTED;
187 event.modifiers = 0;
188 event.keycode = 0;
189 event.symbol = 0;
191 _kbd_addEvent(&event);
193 return;
195 case USBKEYBOARD_PRESSED:
196 event.type = KEYBOARD_PRESSED;
197 break;
199 case USBKEYBOARD_RELEASED:
200 event.type = KEYBOARD_RELEASED;
201 break;
203 default:
204 return;
207 event.keycode = kevent.keyCode;
209 wskbd_get_mapentry(&_ukbd_keymapdata, event.keycode, &kp);
211 /* Now update modifiers */
212 switch (kp.group1[0]) {
213 case KS_Shift_L:
214 update_modifier(event.type, 0, MOD_SHIFT_L);
215 break;
217 case KS_Shift_R:
218 update_modifier(event.type, 0, MOD_SHIFT_R);
219 break;
221 case KS_Shift_Lock:
222 update_modifier(event.type, 1, MOD_SHIFTLOCK);
223 break;
225 case KS_Caps_Lock:
226 update_modifier(event.type, 1, MOD_CAPSLOCK);
227 USBKeyboard_SetLed(USBKEYBOARD_LEDCAPS,
228 MOD_ONESET(_modifiers, MOD_CAPSLOCK));
229 break;
231 case KS_Control_L:
232 update_modifier(event.type, 0, MOD_CONTROL_L);
233 break;
235 case KS_Control_R:
236 update_modifier(event.type, 0, MOD_CONTROL_R);
237 break;
239 case KS_Alt_L:
240 update_modifier(event.type, 0, MOD_META_L);
241 break;
243 case KS_Alt_R:
244 update_modifier(event.type, 0, MOD_META_R);
245 break;
247 case KS_Mode_switch:
248 update_modifier(event.type, 0, MOD_MODESHIFT);
249 break;
251 case KS_Mode_Lock:
252 update_modifier(event.type, 1, MOD_MODELOCK);
253 break;
255 case KS_Num_Lock:
256 update_modifier(event.type, 1, MOD_NUMLOCK);
257 USBKeyboard_SetLed(USBKEYBOARD_LEDNUM,
258 MOD_ONESET(_modifiers, MOD_NUMLOCK));
259 break;
261 case KS_Hold_Screen:
262 update_modifier(event.type, 1, MOD_HOLDSCREEN);
263 USBKeyboard_SetLed(USBKEYBOARD_LEDSCROLL,
264 MOD_ONESET(_modifiers, MOD_HOLDSCREEN));
265 break;
268 /* Get the keysym */
269 if (_modifiers & (MOD_MODESHIFT|MOD_MODELOCK) &&
270 !MOD_ONESET(_modifiers, MOD_ANYCONTROL))
271 group = &kp.group2[0];
272 else
273 group = &kp.group1[0];
275 if ((_modifiers & MOD_NUMLOCK) &&
276 KS_GROUP(group[1]) == KS_GROUP_Keypad) {
277 gindex = !MOD_ONESET(_modifiers, MOD_ANYSHIFT);
278 ksym = group[gindex];
279 } else {
280 /* CAPS alone should only affect letter keys */
281 if ((_modifiers & (MOD_CAPSLOCK | MOD_ANYSHIFT)) ==
282 MOD_CAPSLOCK) {
283 gindex = 0;
284 ksym = ksym_upcase(group[0]);
285 } else {
286 gindex = MOD_ONESET(_modifiers, MOD_ANYSHIFT);
287 ksym = group[gindex];
291 /* Process compose sequence and dead accents */
292 switch (KS_GROUP(ksym)) {
293 case KS_GROUP_Mod:
294 if (ksym == KS_Multi_key) {
295 update_modifier(KEYBOARD_PRESSED, 0, MOD_COMPOSE);
296 _composelen = 2;
298 break;
300 case KS_GROUP_Dead:
301 if (event.type != KEYBOARD_PRESSED)
302 return;
304 if (_composelen == 0) {
305 update_modifier(KEYBOARD_PRESSED, 0, MOD_COMPOSE);
306 _composelen = 1;
307 _composebuf[0] = ksym;
309 return;
311 break;
314 if ((event.type == KEYBOARD_PRESSED) && (_composelen > 0)) {
316 * If the compose key also serves as AltGr (i.e. set to both
317 * KS_Multi_key and KS_Mode_switch), and would provide a valid,
318 * distinct combination as AltGr, leave compose mode.
320 if (_composelen == 2 && group == &kp.group2[0]) {
321 if (kp.group1[gindex] != kp.group2[gindex])
322 _composelen = 0;
325 if (_composelen != 0) {
326 _composebuf[2 - _composelen] = ksym;
327 if (--_composelen == 0) {
328 ksym = wskbd_compose_value(_composebuf);
329 update_modifier(KEYBOARD_RELEASED, 0, MOD_COMPOSE);
330 } else {
331 return;
336 // store up to MAXHELD pressed events to match the symbol for release
337 switch (KS_GROUP(ksym)) {
338 case KS_GROUP_Ascii:
339 case KS_GROUP_Keypad:
340 case KS_GROUP_Function:
341 if (event.type == KEYBOARD_PRESSED) {
342 for (i = 0; i < MAXHELD; ++i) {
343 if (_held[i].keycode == 0) {
344 _held[i].keycode = event.keycode;
345 _held[i].symbol = ksym;
347 break;
350 } else {
351 for (i = 0; i < MAXHELD; ++i) {
352 if (_held[i].keycode == event.keycode) {
353 ksym = _held[i].symbol;
354 _held[i].keycode = 0;
355 _held[i].symbol = 0;
357 break;
362 break;
365 event.symbol = ksym;
366 event.modifiers = _modifiers;
368 _kbd_addEvent(&event);
370 return;
373 //This function call usb function to check if a new keyboard is connected
374 static s32 _kbd_scan_for_keyboard(void)
376 s32 ret;
377 keyboard_event event;
379 ret = USBKeyboard_Open(&_kbd_event_cb);
381 if (ret < 0)
382 return ret;
384 _modifiers = 0;
385 _composelen = 0;
386 memset(_held, 0, sizeof(_held));
388 USBKeyboard_SetLed(USBKEYBOARD_LEDNUM, true);
389 USBKeyboard_SetLed(USBKEYBOARD_LEDCAPS, true);
390 USBKeyboard_SetLed(USBKEYBOARD_LEDSCROLL, true);
391 usleep(200 * 1000);
392 USBKeyboard_SetLed(USBKEYBOARD_LEDNUM, false);
393 USBKeyboard_SetLed(USBKEYBOARD_LEDCAPS, false);
394 USBKeyboard_SetLed(USBKEYBOARD_LEDSCROLL, false);
396 event.type = KEYBOARD_CONNECTED;
397 event.modifiers = 0;
398 event.keycode = 0;
400 _kbd_addEvent(&event);
402 return ret;
405 static void * _kbd_thread_func(void *arg) {
406 u32 turns = 0;
408 while (!_kbd_thread_quit) {
409 // scan for new attached keyboards
410 if ((turns % KBD_THREAD_KBD_SCAN_INTERVAL) == 0) {
411 if (!USBKeyboard_IsConnected())
412 _kbd_scan_for_keyboard();
414 turns = 0;
416 turns++;
418 USBKeyboard_Scan();
419 usleep(KBD_THREAD_UDELAY);
422 return NULL;
425 struct {
426 vu8 head;
427 vu8 tail;
428 char buf[256];
429 } _keyBuffer;
431 static void * _kbd_buf_thread_func(void *arg) {
432 keyboard_event event;
433 while (!_kbd_thread_quit) {
434 if (((_keyBuffer.tail+1)&255) != _keyBuffer.head) {
435 if ( KEYBOARD_GetEvent(&event)) {
436 if (event.type == KEYBOARD_PRESSED) {
437 _keyBuffer.buf[_keyBuffer.tail] = event.symbol;
438 _keyBuffer.tail++;
442 usleep(KBD_THREAD_UDELAY);
444 return NULL;
447 static ssize_t _keyboardRead(struct _reent *r, int unused, char *ptr, size_t len)
449 ssize_t count = len;
450 while ( count > 0 ) {
451 if (_keyBuffer.head != _keyBuffer.tail) {
452 char key = _keyBuffer.buf[_keyBuffer.head];
453 *ptr++ = key;
454 if (_readKey_cb != NULL) _readKey_cb(key);
455 _keyBuffer.head++;
456 count--;
459 return len;
462 static const devoptab_t std_in =
464 "stdin",
466 NULL,
467 NULL,
468 NULL,
469 _keyboardRead,
470 NULL,
471 NULL,
472 NULL,
473 NULL,
474 NULL,
475 NULL
478 //Initialize USB and USB_KEYBOARD and the event queue
479 s32 KEYBOARD_Init(keyPressCallback keypress_cb)
481 int fd;
482 struct stat st;
483 char keymap[64];
484 size_t i;
486 if (USB_Initialize() != IPC_OK)
487 return -1;
489 if (USBKeyboard_Initialize() != IPC_OK) {
490 return -2;
493 if (_ukbd_keymapdata.layout == KB_NONE) {
494 keymap[0] = 0;
495 fd = open("/wiikbd.map", O_RDONLY);
497 if ((fd > 0) && !fstat(fd, &st)) {
498 if ((st.st_size > 0) && (st.st_size < 64) &&
499 (st.st_size == read(fd, keymap, st.st_size))) {
500 keymap[63] = 0;
501 for (i = 0; i < 64; ++i) {
502 if ((keymap[i] != '-') && (isalpha((int)keymap[i]) == 0)) {
503 keymap[i] = 0;
504 break;
509 close(fd);
512 _ukbd_keymapdata.layout = _get_keymap_by_name(keymap);
515 if (_ukbd_keymapdata.layout == KB_NONE) {
516 switch (CONF_GetLanguage()) {
517 case CONF_LANG_GERMAN:
518 _ukbd_keymapdata.layout = KB_DE;
519 break;
521 case CONF_LANG_JAPANESE:
522 _ukbd_keymapdata.layout = KB_JP;
523 break;
525 case CONF_LANG_FRENCH:
526 _ukbd_keymapdata.layout = KB_FR;
527 break;
529 case CONF_LANG_SPANISH:
530 _ukbd_keymapdata.layout = KB_ES;
531 break;
533 case CONF_LANG_ITALIAN:
534 _ukbd_keymapdata.layout = KB_IT;
535 break;
537 case CONF_LANG_DUTCH:
538 _ukbd_keymapdata.layout = KB_NL;
539 break;
541 case CONF_LANG_SIMP_CHINESE:
542 case CONF_LANG_TRAD_CHINESE:
543 case CONF_LANG_KOREAN:
544 default:
545 _ukbd_keymapdata.layout = KB_US;
546 break;
550 if (wskbd_load_keymap(&_ukbd_keymapdata, &_sc_map, &_sc_maplen) < 0) {
551 _ukbd_keymapdata.layout = KB_NONE;
553 return -4;
556 __lwp_queue_init_empty(&_queue);
558 if (!_kbd_thread_running) {
559 // start the keyboard thread
560 _kbd_thread_quit = false;
562 memset(_kbd_stack, 0, KBD_THREAD_STACKSIZE);
564 s32 res = LWP_CreateThread(&_kbd_thread, _kbd_thread_func, NULL,
565 _kbd_stack, KBD_THREAD_STACKSIZE,
566 KBD_THREAD_PRIO);
568 if (res) {
569 USBKeyboard_Close();
571 return -6;
574 if(keypress_cb)
576 _keyBuffer.head = 0;
577 _keyBuffer.tail = 0;
579 res = LWP_CreateThread(&_kbd_buf_thread, _kbd_buf_thread_func, NULL,
580 _kbd_buf_stack, KBD_THREAD_STACKSIZE,
581 KBD_THREAD_PRIO);
582 if(res) {
583 _kbd_thread_quit = true;
585 LWP_JoinThread(_kbd_thread, NULL);
587 USBKeyboard_Close();
588 KEYBOARD_FlushEvents();
589 USBKeyboard_Deinitialize();
590 _kbd_thread_running = false;
591 return -6;
594 devoptab_list[STD_IN] = &std_in;
595 setvbuf(stdin, NULL , _IONBF, 0);
596 _readKey_cb = keypress_cb;
598 _kbd_thread_running = true;
600 return 0;
603 //Deinitialize USB and USB_KEYBOARD and the event queue
604 s32 KEYBOARD_Deinit(void)
606 if (_kbd_thread_running) {
607 _kbd_thread_quit = true;
609 if(_kbd_thread != LWP_THREAD_NULL)
610 LWP_JoinThread(_kbd_thread, NULL);
611 if(_kbd_buf_thread != LWP_THREAD_NULL)
612 LWP_JoinThread(_kbd_buf_thread, NULL);
614 _kbd_thread_running = false;
617 USBKeyboard_Close();
618 KEYBOARD_FlushEvents();
619 USBKeyboard_Deinitialize();
621 if (_sc_map) {
622 free(_sc_map);
623 _sc_map = NULL;
624 _sc_maplen = 0;
627 return 1;
630 //Get the first event of the event queue
631 s32 KEYBOARD_GetEvent(keyboard_event *event)
633 _node *n = (_node *) __lwp_queue_get(&_queue);
635 if (!n)
636 return 0;
638 *event = n->event;
640 free(n);
642 return 1;
645 //Flush all pending events
646 s32 KEYBOARD_FlushEvents(void)
648 s32 res;
649 _node *n;
651 res = 0;
652 while (true) {
653 n = (_node *) __lwp_queue_get(&_queue);
655 if (!n)
656 break;
658 free(n);
659 res++;
662 return res;