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