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