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