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