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