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