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