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