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