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