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