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