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