1 /*-------------------------------------------------------------
3 keyboard.c -- keyboard event system
5 Copyright (C) 2008, 2009
6 DAVY Guillaume davyg2@gmail.com
7 Brian Johnson brijohn@gmail.com
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
29 -------------------------------------------------------------*/
31 #include <sys/types.h>
33 #include <sys/iosupport.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
= {
75 static struct nameint kbdenc_tab
[] = {
79 static struct nameint kbdvar_tab
[] = {
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];
97 static _keyheld _held
[MAXHELD
];
101 keyboard_event event
;
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
) {
112 kbd_t encoding
, variant
;
116 if (!identifier
|| (strlen(identifier
) < 2))
120 for (i
= 0; ukbd_keydesctab
[i
].name
!= 0; ++i
) {
121 if (ukbd_keydesctab
[i
].name
& KB_HANDLEDBYWSKBD
)
124 encoding
= KB_ENCODING(ukbd_keydesctab
[i
].name
);
125 variant
= KB_VARIANT(ukbd_keydesctab
[i
].name
);
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
);
134 if (strlen(name
) < 1)
137 for (j
= 0; j
< sizeof(kbdvar_tab
) / sizeof(struct nameint
); ++j
)
138 if (variant
& kbdvar_tab
[j
].value
) {
140 strcat(name
, kbdvar_tab
[j
].name
);
143 if (!strcmp(identifier
, name
)) {
144 res
= ukbd_keydesctab
[i
].name
;
152 //Add an event to the event queue
153 static s32
_kbd_addEvent(const keyboard_event
*event
) {
154 _node
*n
= malloc(sizeof(_node
));
157 __lwp_queue_append(&_queue
, (lwp_node
*) n
);
162 void update_modifier(u_int type
, int toggle
, int mask
) {
164 if (type
== KEYBOARD_PRESSED
)
167 if (type
== KEYBOARD_RELEASED
)
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
;
184 switch (kevent
.type
) {
185 case USBKEYBOARD_DISCONNECTED
:
186 event
.type
= KEYBOARD_DISCONNECTED
;
191 _kbd_addEvent(&event
);
195 case USBKEYBOARD_PRESSED
:
196 event
.type
= KEYBOARD_PRESSED
;
199 case USBKEYBOARD_RELEASED
:
200 event
.type
= KEYBOARD_RELEASED
;
207 event
.keycode
= kevent
.keyCode
;
209 wskbd_get_mapentry(&_ukbd_keymapdata
, event
.keycode
, &kp
);
211 /* Now update modifiers */
212 switch (kp
.group1
[0]) {
214 update_modifier(event
.type
, 0, MOD_SHIFT_L
);
218 update_modifier(event
.type
, 0, MOD_SHIFT_R
);
222 update_modifier(event
.type
, 1, MOD_SHIFTLOCK
);
226 update_modifier(event
.type
, 1, MOD_CAPSLOCK
);
227 USBKeyboard_SetLed(USBKEYBOARD_LEDCAPS
,
228 MOD_ONESET(_modifiers
, MOD_CAPSLOCK
));
232 update_modifier(event
.type
, 0, MOD_CONTROL_L
);
236 update_modifier(event
.type
, 0, MOD_CONTROL_R
);
240 update_modifier(event
.type
, 0, MOD_META_L
);
244 update_modifier(event
.type
, 0, MOD_META_R
);
248 update_modifier(event
.type
, 0, MOD_MODESHIFT
);
252 update_modifier(event
.type
, 1, MOD_MODELOCK
);
256 update_modifier(event
.type
, 1, MOD_NUMLOCK
);
257 USBKeyboard_SetLed(USBKEYBOARD_LEDNUM
,
258 MOD_ONESET(_modifiers
, MOD_NUMLOCK
));
262 update_modifier(event
.type
, 1, MOD_HOLDSCREEN
);
263 USBKeyboard_SetLed(USBKEYBOARD_LEDSCROLL
,
264 MOD_ONESET(_modifiers
, MOD_HOLDSCREEN
));
269 if (_modifiers
& (MOD_MODESHIFT
|MOD_MODELOCK
) &&
270 !MOD_ONESET(_modifiers
, MOD_ANYCONTROL
))
271 group
= &kp
.group2
[0];
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
];
280 /* CAPS alone should only affect letter keys */
281 if ((_modifiers
& (MOD_CAPSLOCK
| MOD_ANYSHIFT
)) ==
284 ksym
= ksym_upcase(group
[0]);
286 gindex
= MOD_ONESET(_modifiers
, MOD_ANYSHIFT
);
287 ksym
= group
[gindex
];
291 /* Process compose sequence and dead accents */
292 switch (KS_GROUP(ksym
)) {
294 if (ksym
== KS_Multi_key
) {
295 update_modifier(KEYBOARD_PRESSED
, 0, MOD_COMPOSE
);
301 if (event
.type
!= KEYBOARD_PRESSED
)
304 if (_composelen
== 0) {
305 update_modifier(KEYBOARD_PRESSED
, 0, MOD_COMPOSE
);
307 _composebuf
[0] = ksym
;
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
])
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
);
336 // store up to MAXHELD pressed events to match the symbol for release
337 switch (KS_GROUP(ksym
)) {
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
;
351 for (i
= 0; i
< MAXHELD
; ++i
) {
352 if (_held
[i
].keycode
== event
.keycode
) {
353 ksym
= _held
[i
].symbol
;
354 _held
[i
].keycode
= 0;
366 event
.modifiers
= _modifiers
;
368 _kbd_addEvent(&event
);
373 //This function call usb function to check if a new keyboard is connected
374 static s32
_kbd_scan_for_keyboard(void)
377 keyboard_event event
;
379 ret
= USBKeyboard_Open(&_kbd_event_cb
);
386 memset(_held
, 0, sizeof(_held
));
388 USBKeyboard_SetLed(USBKEYBOARD_LEDNUM
, true);
389 USBKeyboard_SetLed(USBKEYBOARD_LEDCAPS
, true);
390 USBKeyboard_SetLed(USBKEYBOARD_LEDSCROLL
, true);
392 USBKeyboard_SetLed(USBKEYBOARD_LEDNUM
, false);
393 USBKeyboard_SetLed(USBKEYBOARD_LEDCAPS
, false);
394 USBKeyboard_SetLed(USBKEYBOARD_LEDSCROLL
, false);
396 event
.type
= KEYBOARD_CONNECTED
;
400 _kbd_addEvent(&event
);
405 static void * _kbd_thread_func(void *arg
) {
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();
419 usleep(KBD_THREAD_UDELAY
);
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
;
442 usleep(KBD_THREAD_UDELAY
);
447 static ssize_t
_keyboardRead(struct _reent
*r
, int unused
, char *ptr
, size_t len
)
450 while ( count
> 0 ) {
451 if (_keyBuffer
.head
!= _keyBuffer
.tail
) {
452 char key
= _keyBuffer
.buf
[_keyBuffer
.head
];
454 if (_readKey_cb
!= NULL
) _readKey_cb(key
);
462 static const devoptab_t std_in
=
478 //Initialize USB and USB_KEYBOARD and the event queue
479 s32
KEYBOARD_Init(keyPressCallback keypress_cb
)
486 if (USB_Initialize() != IPC_OK
)
489 if (USBKeyboard_Initialize() != IPC_OK
) {
493 if (_ukbd_keymapdata
.layout
== KB_NONE
) {
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
))) {
501 for (i
= 0; i
< 64; ++i
) {
502 if ((keymap
[i
] != '-') && (isalpha((int)keymap
[i
]) == 0)) {
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
;
521 case CONF_LANG_JAPANESE
:
522 _ukbd_keymapdata
.layout
= KB_JP
;
525 case CONF_LANG_FRENCH
:
526 _ukbd_keymapdata
.layout
= KB_FR
;
529 case CONF_LANG_SPANISH
:
530 _ukbd_keymapdata
.layout
= KB_ES
;
533 case CONF_LANG_ITALIAN
:
534 _ukbd_keymapdata
.layout
= KB_IT
;
537 case CONF_LANG_DUTCH
:
538 _ukbd_keymapdata
.layout
= KB_NL
;
541 case CONF_LANG_SIMP_CHINESE
:
542 case CONF_LANG_TRAD_CHINESE
:
543 case CONF_LANG_KOREAN
:
545 _ukbd_keymapdata
.layout
= KB_US
;
550 if (wskbd_load_keymap(&_ukbd_keymapdata
, &_sc_map
, &_sc_maplen
) < 0) {
551 _ukbd_keymapdata
.layout
= KB_NONE
;
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
,
579 res
= LWP_CreateThread(&_kbd_buf_thread
, _kbd_buf_thread_func
, NULL
,
580 _kbd_buf_stack
, KBD_THREAD_STACKSIZE
,
583 _kbd_thread_quit
= true;
585 LWP_JoinThread(_kbd_thread
, NULL
);
588 KEYBOARD_FlushEvents();
589 USBKeyboard_Deinitialize();
590 _kbd_thread_running
= false;
594 devoptab_list
[STD_IN
] = &std_in
;
595 setvbuf(stdin
, NULL
, _IONBF
, 0);
596 _readKey_cb
= keypress_cb
;
598 _kbd_thread_running
= true;
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;
618 KEYBOARD_FlushEvents();
619 USBKeyboard_Deinitialize();
630 //Get the first event of the event queue
631 s32
KEYBOARD_GetEvent(keyboard_event
*event
)
633 _node
*n
= (_node
*) __lwp_queue_get(&_queue
);
645 //Flush all pending events
646 s32
KEYBOARD_FlushEvents(void)
653 n
= (_node
*) __lwp_queue_get(&_queue
);