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