1 /*---------------------------------------------------------------------------*
2 Project: Revolution KPR Key Processing demo
3 File: kprdemo.c
4
5 Copyright 2007 Nintendo. All rights reserved.
6
7 These coded instructions, statements, and computer programs contain
8 proprietary information of Nintendo of America Inc. and/or Nintendo
9 Company Ltd., and are protected by Federal copyright law. They may
10 not be disclosed to third parties or copied or duplicated in any form,
11 in whole or in part, without the prior written consent of Nintendo.
12
13 $Log: kprdemo.c,v $
14 Revision 1.10 2007/10/08 19:20:19 henrch
15 changed per KBD api
16
17 Revision 1.9 2007/07/13 23:42:26 carlmu
18 Added instruction output for Greek layout language change.
19
20 Revision 1.8 2007/07/13 23:38:50 carlmu
21 Changed from TrySetLeds to KBDSetLedsRetry.
22
23 Revision 1.7 2007/07/12 01:37:58 dante.treglia
24 KBD: Removed unsupported languages from typedef enum _KBDCountryCode.
25
26 Revision 1.6 2007/06/21 22:47:09 carlmu
27 Updated for KBD 1.4 API (changed LED handling).
28
29 Revision 1.5 2007/05/29 21:59:19 carlmu
30 Added French Canadian layout to selection.
31 Added Keypad Equals key to output.
32
33 Revision 1.4 2007/05/11 23:13:41 carlmu
34 Added "International" map.
35 Added region initializers for KPR.
36
37 Revision 1.3 2007/05/05 01:57:21 carlmu
38 Changes for 0.8 API
39
40 Revision 1.2 2007/05/01 20:42:48 carlmu
41 Checks for control characters entered using Alt+keypad.
42
43 Revision 1.1 2007/04/25 19:00:58 carlmu
44 Initial version.
45
46
47 $NoKeywords: $
48 *---------------------------------------------------------------------------*/
49
50 /*
51 Example KPR application to show how to use Key Processing library.
52 */
53
54 #include <demo.h>
55 #include <revolution/kbd.h>
56 #include <revolution/kpr.h>
57 #include <revolution/enc.h>
58
59 extern void hid_open_async(void);
60 extern void hid_close_async(void);
61
62
63 // Decide if an 8-bit Unicode character is a control character
64 #define UNICODE_IS_CONTROL(uc) ((uc)<32 || ((uc)>=128 && (uc)<=159))
65
66 //-----------------------------------------------------------------------------
67
68 static kbd_mem[KBD_MEM_SIZE];
69
70
71 // List of countries useable in this demo; arbitrary order
72 KBDCountryCode countryList[] = {
73 KBD_CC_UNITED_STATES,
74 KBD_CC_JAPANESE,
75 KBD_CC_INTERNATIONAL,
76 KBD_CC_CANADIAN_BI,
77 KBD_CC_CANADIAN_FR,
78 KBD_CC_DANISH,
79 KBD_CC_FINNISH,
80 KBD_CC_FRENCH,
81 KBD_CC_GERMAN,
82 KBD_CC_GREEK,
83 KBD_CC_ITALIAN,
84 KBD_CC_LATIN_AMERICAN,
85 KBD_CC_NETHERLANDS_DUTCH,
86 KBD_CC_NORWEGIAN,
87 KBD_CC_PORTUGUESE,
88 KBD_CC_SPANISH,
89 KBD_CC_SWEDISH,
90 KBD_CC_UNITED_KINGDOM,
91 };
92
93 #define MAX_CODE (sizeof(countryList)/sizeof(KBDCountryCode))
94
95 // List of countries available in KBD driver; by code order
96 // (Subject to change!)
97 char *countryName[] = {
98 "International",
99 "Canadian_Bi",
100 "Canadian_Fr",
101 "Danish",
102 "Finnish",
103 "French",
104 "German",
105 "Greek",
106 "Italian",
107 "Japanese",
108 "Latin_American",
109 "Netherlands_Dutch",
110 "Norwegian",
111 "Portuguese",
112 "Spanish",
113 "Swedish",
114 "United_Kingdom",
115 "United_States",
116 };
117
118 typedef struct _SpTableEntry {
119 KBDUnicode code;
120 char *name;
121 } SpTableEntry;
122
123 SpTableEntry SpecialTable[] = {
124 KBK_Void, "(void key)",
125 KBK_Error, "(error!)", // should never be output
126 KBK_Backspace, "Backspace",
127 KBK_Tab, "Tab",
128 KBK_Enter, "Enter",
129 KBK_Escape, "Escape",
130 KBK_Space, "Space",
131 KBK_Mod_Shift, "Mod_Shift",
132 KBK_Mod_AltGr, "Mod_AltGr",
133 KBK_Lang_Toggle, "Lang_Toggle",
134 KBK_Num_Lock, "Num_Lock",
135 KBK_Caps_Lock, "Caps_Lock",
136 KBK_Mod_Control, "Mod_Control",
137 KBK_Mod_Alt, "Mod_Alt",
138 KBK_Mod_GUI, "Mod_GUI",
139 KBK_Mod_Extra, "Mod_Extra",
140 KBK_Print_Screen, "Print_Screen",
141 KBK_Scroll_Lock, "Scroll_Lock",
142 KBK_Pause, "Pause",
143 KBK_Application, "Application",
144 KBK_Katakana_Hiragana, "Katakana_Hiragana",
145 KBK_Henkan, "Henkan",
146 KBK_Muhenkan, "Muhenkan",
147 KBK_Kanji, "Kanji",
148 KBK_Katakana, "Katakana",
149 KBK_Hiragana, "Hiragana",
150 KBK_F1, "F1",
151 KBK_F2, "F2",
152 KBK_F3, "F3",
153 KBK_F4, "F4",
154 KBK_F5, "F5",
155 KBK_F6, "F6",
156 KBK_F7, "F7",
157 KBK_F8, "F8",
158 KBK_F9, "F9",
159 KBK_F10, "F10",
160 KBK_F11, "F11",
161 KBK_F12, "F12",
162 KBK_Delete, "Delete",
163 KBK_Insert, "Insert",
164 KBK_End, "End",
165 KBK_Down_Arrow, "Down_Arrow",
166 KBK_Page_Down, "Page_Down",
167 KBK_Left_Arrow, "Left_Arrow",
168 KBK_Right_Arrow, "Right_Arrow",
169 KBK_Home, "Home",
170 KBK_Up_Arrow, "Up_Arrow",
171 KBK_Page_Up, "Page_Up",
172 KBK_Keypad_Delete, "Keypad_Delete",
173 KBK_Keypad_Insert, "Keypad_Insert",
174 KBK_Keypad_End, "Keypad_End",
175 KBK_Keypad_Down_Arrow, "Keypad_Down_Arrow",
176 KBK_Keypad_Page_Down, "Keypad_Page_Down",
177 KBK_Keypad_Left_Arrow, "Keypad_Left_Arrow",
178 KBK_Keypad_Space, "Keypad_Space",
179 KBK_Keypad_Right_Arrow, "Keypad_Right_Arrow",
180 KBK_Keypad_Home, "Keypad_Home",
181 KBK_Keypad_Up_Arrow, "Keypad_Up_Arrow",
182 KBK_Keypad_Page_Up, "Keypad_Page_Up",
183 KBK_Keypad_Comma, "Keypad_Comma",
184 KBK_Keypad_Period, "Keypad_Period",
185 KBK_Keypad_0, "Keypad_0",
186 KBK_Keypad_1, "Keypad_1",
187 KBK_Keypad_2, "Keypad_2",
188 KBK_Keypad_3, "Keypad_3",
189 KBK_Keypad_4, "Keypad_4",
190 KBK_Keypad_5, "Keypad_5",
191 KBK_Keypad_6, "Keypad_6",
192 KBK_Keypad_7, "Keypad_7",
193 KBK_Keypad_8, "Keypad_8",
194 KBK_Keypad_9, "Keypad_9",
195 KBK_Keypad_Slash, "Keypad_Slash",
196 KBK_Keypad_Asterisk, "Keypad_Asterisk",
197 KBK_Keypad_Minus, "Keypad_Minus",
198 KBK_Keypad_Plus, "Keypad_Plus",
199 KBK_Keypad_Equals, "Keypad_Equals",
200 KBK_Keypad_Enter, "Keypad_Enter",
201 };
202
203 #define MAX_SPECIAL (sizeof(SpecialTable)/sizeof(SpTableEntry))
204
205 #define KBD_CALL(_fn_call) \
206 if ((_fn_call) != KBD_SUCCESS) { \
207 OSReport ("KBD error: calling %s @ %s:%i\n", \
208 #_fn_call, __FILE__, __LINE__); \
209 }
210
211 //-----------------------------------------------------------------------------
212
213 u8 language=0;
214 KPRQueue kprq[KBD_MAX_CHANNELS];
215
216 static void outputUTF8 (u16 utf16);
217 void outputUTF8Example(void);
218
219 //-----------------------------------------------------------------------------
220
221 static void
kbdAppAttach(KBDDevEvent * kde)222 kbdAppAttach (KBDDevEvent *kde) {
223 OSReport ("kbd app: keyboard added on channel %d\n", kde->channel);
224 // for demo purposes, enable NumLock on newly attached keyboard
225 KBDSetModState(kde->channel, KBD_MS_NUM_LOCK);
226 KBDSetLedsRetry(kde->channel, KBD_LED_NUM_LOCK);
227 }
228
229
230 static void
kbdAppDetach(KBDDevEvent * kde)231 kbdAppDetach (KBDDevEvent *kde) {
232 OSReport ("kbd app: keyboard removed on channel %d\n", kde->channel);
233 }
234
235
236 static void
kbdAppKeyEvent(KBDKeyEvent * kke)237 kbdAppKeyEvent (KBDKeyEvent *kke) {
238 KBDCountryCode country;
239 u32 i;
240 u16 utf16;
241
242 // Only report key down events (exception is Alt key going up)
243 if (KBD_KEY_MODE_UP(kke->mode) && kke->unicode != KBK_Mod_Alt) {
244 return;
245 }
246
247 // filter out unmapped keys
248 if (kke->unicode == KBK_Void) {
249 return;
250 }
251
252 // If Alt key went up, need to send a NULL to the KPR queue
253 if (KBD_KEY_MODE_UP(kke->mode) && kke->unicode == KBK_Mod_Alt) {
254 kke->unicode = 0;
255 }
256
257 // check for CapsLock, NumLock, or ScrollLock (handle LEDs)
258 if (kke->unicode == KBK_Caps_Lock ||
259 kke->unicode == KBK_Num_Lock ||
260 kke->unicode == KBK_Scroll_Lock) {
261
262 KBDModState ms;
263 KBDLedState leds;
264
265 // First compute the new LED state
266 KBDGetModState(kke->channel, &ms);
267
268 leds = (KBDLedState)
269 (((ms & KBD_MS_NUM_LOCK) == KBD_MS_NUM_LOCK) * KBD_LED_NUM_LOCK |
270 ((ms & KBD_MS_CAPS_LOCK) == KBD_MS_CAPS_LOCK) * KBD_LED_CAPS_LOCK |
271 ((ms & KBD_MS_SCROLL_LOCK) == KBD_MS_SCROLL_LOCK) * KBD_LED_SCROLL_LOCK);
272
273 KBDSetLedsRetry(kke->channel, leds);
274 }
275
276 // Don't report modifier keys by themselves
277 if (KBD_UC_IS_MODIFIER(kke->unicode)) {
278 return;
279 }
280
281 // check for special keys (KBK_F1, KBK_Home, etc.) or for
282 // Ctrl characters (enter, tab, etc.).
283 // Alt+keypad keys shouldn't be filtered, though.
284 if (KBD_UC_IS_PRIVATE(kke->unicode) &&
285 !(KBD_MS_IS_NUMLOCK(kke->modState) && KBD_UC_IS_KP_NUM_UL_KEY(kke->unicode))) {
286
287 for(i=0; i<MAX_SPECIAL; i++) {
288 if (kke->unicode == SpecialTable[i].code) {
289 OSReport("%s\n", SpecialTable[i].name);
290 break;
291 }
292 }
293
294 } else { // just a regular Unicode character (or Alt+keypad key)
295
296 // If Alt+keypad key, convert to Keypad number range
297 if (KBD_UC_IS_KP_NUM_UL_KEY(kke->unicode)) {
298 kke->unicode = KBD_KP_NUM_UL_KEY_TO_KP_NUM_NL_KEY(kke->unicode);
299 }
300
301 // Send the key to the processing queue
302 KPRPutChar( &kprq[kke->channel], kke->unicode );
303
304 // See what comes out; may be more than 1 character.
305 while ( (utf16 = KPRGetChar( &kprq[kke->channel] )) != 0 ) {
306
307 // User may have used Alt+Keypad to enter a control character
308 if (UNICODE_IS_CONTROL(utf16)) {
309 OSReport("\\%d", utf16);
310 } else {
311
312 outputUTF8(utf16);
313
314 }
315 OSReport("\n");
316 }
317 }
318
319 // For Japanese, handle language change.
320 // (This would normally be done by an IME.)
321 // Keep keyboard in Romaji mode, but now just change the processing mode.
322 if (kke->unicode == KBK_Katakana_Hiragana) {
323
324 language = (u8) ((language + 1) % 3);
325
326 switch (language) {
327 case 0:
328 OSReport("Language switched to Romaji\n");
329 KPRSetMode(&kprq[kke->channel], KPR_MODE_ALT_KEYPAD);
330 break;
331 case 1:
332 OSReport("Language switched to Hiragana\n");
333 KPRSetMode(&kprq[kke->channel], (KPRMode) (KPR_MODE_JP_ROMAJI_HIRAGANA | KPR_MODE_ALT_KEYPAD));
334 break;
335 case 2:
336 OSReport("Language switched to Katakana\n");
337 KPRSetMode(&kprq[kke->channel], (KPRMode) (KPR_MODE_JP_ROMAJI_KATAKANA | KPR_MODE_ALT_KEYPAD));
338 break;
339 }
340 }
341
342 // For Greece, handle language change.
343 KBD_CALL (KBDGetCountry (kke->channel, &country));
344
345 if (country == KBD_CC_GREEK) {
346
347 // Look for Alt-Space combination
348 if (kke->unicode == ' ' && ((kke->modState & KBD_MS_ALT)==KBD_MS_ALT)) {
349
350 language = !language;
351 if (language) {
352 KBD_CALL( KBDSetModState(kke->channel, (KBDModState) (kke->modState | KBD_MS_LANG1)) );
353 OSReport("Language switched to Greek\n");
354 } else {
355 KBD_CALL( KBDSetModState(kke->channel, (KBDModState) (kke->modState & ~KBD_MS_LANG1)) );
356 OSReport("Language switched to English\n");
357 }
358 }
359 }
360
361 }
362
363 static void
outputUTF8(u16 utf16)364 outputUTF8 (u16 utf16) {
365 s32 utf16_len = 1;
366 u8 utf8[4];
367 s32 utf8_len = 4;
368 u8 b;
369
370 ENCConvertStringUtf16ToUtf8 (&utf8[0],
371 &utf8_len,
372 &utf16,
373 &utf16_len);
374 for (b=0; b<utf8_len; b++) {
375 OSReport ("%c", utf8[b]);
376 }
377 }
378
379
outputUTF8Example(void)380 void outputUTF8Example(void) {
381 OSReport ("If your terminal program is UTF-8 compliant, you will\n");
382 OSReport ("see an Omega symbol between quotes here: \"");
383 outputUTF8(0x03a9);
384 OSReport ("\"\n");
385 OSReport ("and a Hiragana letter A between quotes here: \"");
386 outputUTF8(0x3042);
387 OSReport ("\"\n");
388 }
389
390
main(void)391 int main(void) {
392 static u32 country[KBD_MAX_CHANNELS] = { 0, 0, 0, 0}; // index into codeList above
393 KBDChannel cch = 0; // kbd channel to control
394
395 DEMOInit (NULL);
396
397 hid_open_async();
398
399 // Make a call to ENC lib to get its version output out of the way
400 {
401 u32 in=0;
402 u8 out=0;
403 s32 inlen=1, outlen=0;
404 ENCConvertStringUtf32ToUtf8 (&out, &outlen, &in, &inlen);
405 }
406
407 // One of these must be called before calling KPRInitQueue.
408 // The call makes sure that the correct code and maps are linked in.
409 // It's okay to call more than one.
410 KPRInitRegionUS();
411 KPRInitRegionJP();
412 KPRInitRegionEU();
413
414 // Init processing queues
415 for(cch = 0; cch < KBD_MAX_CHANNELS; cch++) {
416 KPRInitQueue(&kprq[cch]);
417 }
418 cch = 0;
419
420 OSReport ("\n\n");
421 OSReport ("************************************************\n");
422 OSReport ("kprdemo Key Processing demo\n");
423 OSReport ("************************************************\n");
424 OSReport ("Attach a USB keyboard and press some keys.\n");
425 OSReport ("Use a GameCube controller plugged into port 1.\n");
426 OSReport ("Button A : set next keyboard country (language)\n");
427 OSReport ("Button B : set previous keyboard country (language)\n");
428 OSReport ("Button X : change channel affected by Button A/B\n");
429 OSReport ("Button Y : output UTF-8 example string\n");
430 OSReport ("\n");
431 OSReport ("For the Greek layout, type Alt-Space to change languages\n");
432
433
434 OSReport ("\n");
435 OSReport ("It is recommended to use a UTF-8 compliant terminal program.\n");
436 outputUTF8Example();
437 OSReport ("\n");
438
439 OSReport ("Country set to %s.\n\n", countryName[countryList[0]]);
440
441 KBD_CALL (KBDInit(kbd_mem, kbdAppAttach, kbdAppDetach, kbdAppKeyEvent));
442
443 // One of these MUST be called after calling KBDInit.
444 // The call makes sure that the correct keyboard maps are linked in.
445 // It's okay to call more than one.
446 KBDInitRegionUS();
447 KBDInitRegionJP();
448 KBDInitRegionEU();
449
450 while (1) {
451
452 DEMOPadRead();
453
454 if ( (DEMOPadGetButtonDown(0) & PAD_BUTTON_A) ||
455 (DEMOPadGetButtonDown(0) & PAD_BUTTON_B) ) {
456 if (DEMOPadGetButtonDown(0) & PAD_BUTTON_A) {
457 country[cch] = (country[cch]+1) % MAX_CODE;
458 } else {
459 country[cch] = (country[cch]+MAX_CODE-1) % MAX_CODE;
460 }
461 KBD_CALL (KBDSetCountry (cch, countryList[country[cch]]));
462 language = 0;
463 OSReport ("Country switched to %s for channel %d.\n", countryName[countryList[country[cch]]], cch);
464
465 if (countryList[country[cch]] == KBD_CC_JAPANESE) {
466 switch(language) {
467 case 0:
468 KPRSetMode(&kprq[cch], KPR_MODE_ALT_KEYPAD);
469 break;
470 case 1:
471 KPRSetMode(&kprq[cch], (KPRMode) (KPR_MODE_JP_ROMAJI_HIRAGANA | KPR_MODE_ALT_KEYPAD));
472 break;
473 case 2:
474 KPRSetMode(&kprq[cch], (KPRMode) (KPR_MODE_JP_ROMAJI_KATAKANA | KPR_MODE_ALT_KEYPAD));
475 break;
476 }
477 } else if (countryList[country[cch]] != KBD_CC_UNITED_STATES &&
478 countryList[country[cch]] != KBD_CC_ITALIAN) {
479 KPRSetMode(&kprq[cch], (KPRMode) (KPR_MODE_DEADKEY | KPR_MODE_ALT_KEYPAD));
480 } else {
481 KPRSetMode(&kprq[cch], KPR_MODE_ALT_KEYPAD);
482 }
483 }
484
485 if ( DEMOPadGetButtonDown(0) & PAD_BUTTON_Y ) {
486 outputUTF8Example();
487 }
488
489 if ( DEMOPadGetButtonDown(0) & PAD_BUTTON_X ) {
490 cch = (KBDChannel) ((cch + 1) % KBD_MAX_CHANNELS);
491 OSReport("Keyboard channel %d selected.\n", cch);
492 }
493
494 OSYieldThread();
495 };
496
497 hid_close_async();
498
499 KBD_CALL (KBDExit()); // not reached
500
501 return 0;
502 }
503