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