1 /*---------------------------------------------------------------------------*
2 Project: Revolution KPR Key Processing demo
3 File: kprdemo.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: kprdemo.c,v $
14 Revision 1.9 2007/07/13 23:42:26 carlmu
15 Added instruction output for Greek layout language change.
16
17 Revision 1.8 2007/07/13 23:38:50 carlmu
18 Changed from TrySetLeds to KBDSetLedsRetry.
19
20 Revision 1.7 2007/07/12 01:37:58 dante.treglia
21 KBD: Removed unsupported languages from typedef enum _KBDCountryCode.
22
23 Revision 1.6 2007/06/21 22:47:09 carlmu
24 Updated for KBD 1.4 API (changed LED handling).
25
26 Revision 1.5 2007/05/29 21:59:19 carlmu
27 Added French Canadian layout to selection.
28 Added Keypad Equals key to output.
29
30 Revision 1.4 2007/05/11 23:13:41 carlmu
31 Added "International" map.
32 Added region initializers for KPR.
33
34 Revision 1.3 2007/05/05 01:57:21 carlmu
35 Changes for 0.8 API
36
37 Revision 1.2 2007/05/01 20:42:48 carlmu
38 Checks for control characters entered using Alt+keypad.
39
40 Revision 1.1 2007/04/25 19:00:58 carlmu
41 Initial version.
42
43
44 $NoKeywords: $
45 *---------------------------------------------------------------------------*/
46
47 /*
48 Example KPR application to show how to use Key Processing library.
49 */
50
51 #include <demo.h>
52 #include <revolution/kbd.h>
53 #include <revolution/kpr.h>
54 #include <revolution/enc.h>
55
56 // Decide if an 8-bit Unicode character is a control character
57 #define UNICODE_IS_CONTROL(uc) ((uc)<32 || ((uc)>=128 && (uc)<=159))
58
59 //-----------------------------------------------------------------------------
60
61 // List of countries useable in this demo; arbitrary order
62 KBDCountryCode countryList[] = {
63 KBD_CC_UNITED_STATES,
64 KBD_CC_JAPANESE,
65 KBD_CC_INTERNATIONAL,
66 KBD_CC_CANADIAN_BI,
67 KBD_CC_CANADIAN_FR,
68 KBD_CC_DANISH,
69 KBD_CC_FINNISH,
70 KBD_CC_FRENCH,
71 KBD_CC_GERMAN,
72 KBD_CC_GREEK,
73 KBD_CC_ITALIAN,
74 KBD_CC_LATIN_AMERICAN,
75 KBD_CC_NETHERLANDS_DUTCH,
76 KBD_CC_NORWEGIAN,
77 KBD_CC_PORTUGUESE,
78 KBD_CC_SPANISH,
79 KBD_CC_SWEDISH,
80 KBD_CC_UNITED_KINGDOM,
81 };
82
83 #define MAX_CODE (sizeof(countryList)/sizeof(KBDCountryCode))
84
85 // List of countries available in KBD driver; by code order
86 // (Subject to change!)
87 char *countryName[] = {
88 "International",
89 "Canadian_Bi",
90 "Canadian_Fr",
91 "Danish",
92 "Finnish",
93 "French",
94 "German",
95 "Greek",
96 "Italian",
97 "Japanese",
98 "Latin_American",
99 "Netherlands_Dutch",
100 "Norwegian",
101 "Portuguese",
102 "Spanish",
103 "Swedish",
104 "United_Kingdom",
105 "United_States",
106 };
107
108 typedef struct _SpTableEntry {
109 KBDUnicode code;
110 char *name;
111 } SpTableEntry;
112
113 SpTableEntry SpecialTable[] = {
114 KBK_Void, "(void key)",
115 KBK_Error, "(error!)", // should never be output
116 KBK_Backspace, "Backspace",
117 KBK_Tab, "Tab",
118 KBK_Enter, "Enter",
119 KBK_Escape, "Escape",
120 KBK_Space, "Space",
121 KBK_Mod_Shift, "Mod_Shift",
122 KBK_Mod_AltGr, "Mod_AltGr",
123 KBK_Lang_Toggle, "Lang_Toggle",
124 KBK_Num_Lock, "Num_Lock",
125 KBK_Caps_Lock, "Caps_Lock",
126 KBK_Mod_Control, "Mod_Control",
127 KBK_Mod_Alt, "Mod_Alt",
128 KBK_Mod_GUI, "Mod_GUI",
129 KBK_Mod_Extra, "Mod_Extra",
130 KBK_Print_Screen, "Print_Screen",
131 KBK_Scroll_Lock, "Scroll_Lock",
132 KBK_Pause, "Pause",
133 KBK_Application, "Application",
134 KBK_Katakana_Hiragana, "Katakana_Hiragana",
135 KBK_Henkan, "Henkan",
136 KBK_Muhenkan, "Muhenkan",
137 KBK_Kanji, "Kanji",
138 KBK_Katakana, "Katakana",
139 KBK_Hiragana, "Hiragana",
140 KBK_F1, "F1",
141 KBK_F2, "F2",
142 KBK_F3, "F3",
143 KBK_F4, "F4",
144 KBK_F5, "F5",
145 KBK_F6, "F6",
146 KBK_F7, "F7",
147 KBK_F8, "F8",
148 KBK_F9, "F9",
149 KBK_F10, "F10",
150 KBK_F11, "F11",
151 KBK_F12, "F12",
152 KBK_Delete, "Delete",
153 KBK_Insert, "Insert",
154 KBK_End, "End",
155 KBK_Down_Arrow, "Down_Arrow",
156 KBK_Page_Down, "Page_Down",
157 KBK_Left_Arrow, "Left_Arrow",
158 KBK_Right_Arrow, "Right_Arrow",
159 KBK_Home, "Home",
160 KBK_Up_Arrow, "Up_Arrow",
161 KBK_Page_Up, "Page_Up",
162 KBK_Keypad_Delete, "Keypad_Delete",
163 KBK_Keypad_Insert, "Keypad_Insert",
164 KBK_Keypad_End, "Keypad_End",
165 KBK_Keypad_Down_Arrow, "Keypad_Down_Arrow",
166 KBK_Keypad_Page_Down, "Keypad_Page_Down",
167 KBK_Keypad_Left_Arrow, "Keypad_Left_Arrow",
168 KBK_Keypad_Space, "Keypad_Space",
169 KBK_Keypad_Right_Arrow, "Keypad_Right_Arrow",
170 KBK_Keypad_Home, "Keypad_Home",
171 KBK_Keypad_Up_Arrow, "Keypad_Up_Arrow",
172 KBK_Keypad_Page_Up, "Keypad_Page_Up",
173 KBK_Keypad_Comma, "Keypad_Comma",
174 KBK_Keypad_Period, "Keypad_Period",
175 KBK_Keypad_0, "Keypad_0",
176 KBK_Keypad_1, "Keypad_1",
177 KBK_Keypad_2, "Keypad_2",
178 KBK_Keypad_3, "Keypad_3",
179 KBK_Keypad_4, "Keypad_4",
180 KBK_Keypad_5, "Keypad_5",
181 KBK_Keypad_6, "Keypad_6",
182 KBK_Keypad_7, "Keypad_7",
183 KBK_Keypad_8, "Keypad_8",
184 KBK_Keypad_9, "Keypad_9",
185 KBK_Keypad_Slash, "Keypad_Slash",
186 KBK_Keypad_Asterisk, "Keypad_Asterisk",
187 KBK_Keypad_Minus, "Keypad_Minus",
188 KBK_Keypad_Plus, "Keypad_Plus",
189 KBK_Keypad_Equals, "Keypad_Equals",
190 KBK_Keypad_Enter, "Keypad_Enter",
191 };
192
193 #define MAX_SPECIAL (sizeof(SpecialTable)/sizeof(SpTableEntry))
194
195 #define KBD_CALL(_fn_call) \
196 if ((_fn_call) != KBD_SUCCESS) { \
197 OSReport ("KBD error: calling %s @ %s:%i\n", \
198 #_fn_call, __FILE__, __LINE__); \
199 }
200
201 //-----------------------------------------------------------------------------
202
203 u8 language=0;
204 KPRQueue kprq[KBD_MAX_CHANNELS];
205
206 static void outputUTF8 (u16 utf16);
207 void outputUTF8Example(void);
208
209 //-----------------------------------------------------------------------------
210
211 static void
kbdAppAttach(KBDDevEvent * kde)212 kbdAppAttach (KBDDevEvent *kde) {
213 OSReport ("kbd app: keyboard added on channel %d\n", kde->channel);
214 // for demo purposes, enable NumLock on newly attached keyboard
215 KBDSetModState(kde->channel, KBD_MS_NUM_LOCK);
216 KBDSetLedsRetry(kde->channel, KBD_LED_NUM_LOCK);
217 }
218
219
220 static void
kbdAppDetach(KBDDevEvent * kde)221 kbdAppDetach (KBDDevEvent *kde) {
222 OSReport ("kbd app: keyboard removed on channel %d\n", kde->channel);
223 }
224
225
226 static void
kbdAppKeyEvent(KBDKeyEvent * kke)227 kbdAppKeyEvent (KBDKeyEvent *kke) {
228 KBDCountryCode country;
229 u32 i;
230 u16 utf16;
231
232 // Only report key down events (exception is Alt key going up)
233 if (KBD_KEY_MODE_UP(kke->mode) && kke->unicode != KBK_Mod_Alt) {
234 return;
235 }
236
237 // filter out unmapped keys
238 if (kke->unicode == KBK_Void) {
239 return;
240 }
241
242 // If Alt key went up, need to send a NULL to the KPR queue
243 if (KBD_KEY_MODE_UP(kke->mode) && kke->unicode == KBK_Mod_Alt) {
244 kke->unicode = 0;
245 }
246
247 // check for CapsLock, NumLock, or ScrollLock (handle LEDs)
248 if (kke->unicode == KBK_Caps_Lock ||
249 kke->unicode == KBK_Num_Lock ||
250 kke->unicode == KBK_Scroll_Lock) {
251
252 KBDModState ms;
253 KBDLedState leds;
254
255 // First compute the new LED state
256 KBDGetModState(kke->channel, &ms);
257
258 leds = (KBDLedState)
259 (((ms & KBD_MS_NUM_LOCK) == KBD_MS_NUM_LOCK) * KBD_LED_NUM_LOCK |
260 ((ms & KBD_MS_CAPS_LOCK) == KBD_MS_CAPS_LOCK) * KBD_LED_CAPS_LOCK |
261 ((ms & KBD_MS_SCROLL_LOCK) == KBD_MS_SCROLL_LOCK) * KBD_LED_SCROLL_LOCK);
262
263 KBDSetLedsRetry(kke->channel, leds);
264 }
265
266 // Don't report modifier keys by themselves
267 if (KBD_UC_IS_MODIFIER(kke->unicode)) {
268 return;
269 }
270
271 // check for special keys (KBK_F1, KBK_Home, etc.) or for
272 // Ctrl characters (enter, tab, etc.).
273 // Alt+keypad keys shouldn't be filtered, though.
274 if (KBD_UC_IS_PRIVATE(kke->unicode) &&
275 !(KBD_MS_IS_NUMLOCK(kke->modState) && KBD_UC_IS_KP_NUM_UL_KEY(kke->unicode))) {
276
277 for(i=0; i<MAX_SPECIAL; i++) {
278 if (kke->unicode == SpecialTable[i].code) {
279 OSReport("%s\n", SpecialTable[i].name);
280 break;
281 }
282 }
283
284 } else { // just a regular Unicode character (or Alt+keypad key)
285
286 // If Alt+keypad key, convert to Keypad number range
287 if (KBD_UC_IS_KP_NUM_UL_KEY(kke->unicode)) {
288 kke->unicode = KBD_KP_NUM_UL_KEY_TO_KP_NUM_NL_KEY(kke->unicode);
289 }
290
291 // Send the key to the processing queue
292 KPRPutChar( &kprq[kke->channel], kke->unicode );
293
294 // See what comes out; may be more than 1 character.
295 while ( (utf16 = KPRGetChar( &kprq[kke->channel] )) != 0 ) {
296
297 // User may have used Alt+Keypad to enter a control character
298 if (UNICODE_IS_CONTROL(utf16)) {
299 OSReport("\\%d", utf16);
300 } else {
301
302 outputUTF8(utf16);
303
304 }
305 OSReport("\n");
306 }
307 }
308
309 // For Japanese, handle language change.
310 // (This would normally be done by an IME.)
311 // Keep keyboard in Romaji mode, but now just change the processing mode.
312 if (kke->unicode == KBK_Katakana_Hiragana) {
313
314 language = (u8) ((language + 1) % 3);
315
316 switch (language) {
317 case 0:
318 OSReport("Language switched to Romaji\n");
319 KPRSetMode(&kprq[kke->channel], KPR_MODE_ALT_KEYPAD);
320 break;
321 case 1:
322 OSReport("Language switched to Hiragana\n");
323 KPRSetMode(&kprq[kke->channel], (KPRMode) (KPR_MODE_JP_ROMAJI_HIRAGANA | KPR_MODE_ALT_KEYPAD));
324 break;
325 case 2:
326 OSReport("Language switched to Katakana\n");
327 KPRSetMode(&kprq[kke->channel], (KPRMode) (KPR_MODE_JP_ROMAJI_KATAKANA | KPR_MODE_ALT_KEYPAD));
328 break;
329 }
330 }
331
332 // For Greece, handle language change.
333 KBD_CALL (KBDGetCountry (kke->channel, &country));
334
335 if (country == KBD_CC_GREEK) {
336
337 // Look for Alt-Space combination
338 if (kke->unicode == ' ' && ((kke->modState & KBD_MS_ALT)==KBD_MS_ALT)) {
339
340 language = !language;
341 if (language) {
342 KBD_CALL( KBDSetModState(kke->channel, (KBDModState) (kke->modState | KBD_MS_LANG1)) );
343 OSReport("Language switched to Greek\n");
344 } else {
345 KBD_CALL( KBDSetModState(kke->channel, (KBDModState) (kke->modState & ~KBD_MS_LANG1)) );
346 OSReport("Language switched to English\n");
347 }
348 }
349 }
350
351 }
352
353 static void
outputUTF8(u16 utf16)354 outputUTF8 (u16 utf16) {
355 s32 utf16_len = 1;
356 u8 utf8[4];
357 s32 utf8_len = 4;
358 u8 b;
359
360 ENCConvertStringUtf16ToUtf8 (&utf8[0],
361 &utf8_len,
362 &utf16,
363 &utf16_len);
364 for (b=0; b<utf8_len; b++) {
365 OSReport ("%c", utf8[b]);
366 }
367 }
368
369
outputUTF8Example(void)370 void outputUTF8Example(void) {
371 OSReport ("If your terminal program is UTF-8 compliant, you will\n");
372 OSReport ("see an Omega symbol between quotes here: \"");
373 outputUTF8(0x03a9);
374 OSReport ("\"\n");
375 OSReport ("and a Hiragana letter A between quotes here: \"");
376 outputUTF8(0x3042);
377 OSReport ("\"\n");
378 }
379
380
main(void)381 int main(void) {
382 static u32 country[KBD_MAX_CHANNELS] = { 0, 0, 0, 0}; // index into codeList above
383 KBDChannel cch = 0; // kbd channel to control
384
385 DEMOInit (NULL);
386
387
388 // Make a call to ENC lib to get its version output out of the way
389 {
390 u32 in=0;
391 u8 out=0;
392 s32 inlen=1, outlen=0;
393 ENCConvertStringUtf32ToUtf8 (&out, &outlen, &in, &inlen);
394 }
395
396 // One of these must be called before calling KPRInitQueue.
397 // The call makes sure that the correct code and maps are linked in.
398 // It's okay to call more than one.
399 KPRInitRegionUS();
400 KPRInitRegionJP();
401 KPRInitRegionEU();
402
403 // Init processing queues
404 for(cch = 0; cch < KBD_MAX_CHANNELS; cch++) {
405 KPRInitQueue(&kprq[cch]);
406 }
407 cch = 0;
408
409 OSReport ("\n\n");
410 OSReport ("************************************************\n");
411 OSReport ("kprdemo Key Processing demo\n");
412 OSReport ("************************************************\n");
413 OSReport ("Attach a USB keyboard and press some keys.\n");
414 OSReport ("Use a GameCube controller plugged into port 1.\n");
415 OSReport ("Button A : set next keyboard country (language)\n");
416 OSReport ("Button B : set previous keyboard country (language)\n");
417 OSReport ("Button X : change channel affected by Button A/B\n");
418 OSReport ("Button Y : output UTF-8 example string\n");
419 OSReport ("\n");
420 OSReport ("For the Greek layout, type Alt-Space to change languages\n");
421
422
423 OSReport ("\n");
424 OSReport ("It is recommended to use a UTF-8 compliant terminal program.\n");
425 outputUTF8Example();
426 OSReport ("\n");
427
428 OSReport ("Country set to %s.\n\n", countryName[countryList[0]]);
429
430 // One of these MUST be called before calling KBDInit.
431 // The call makes sure that the correct keyboard maps are linked in.
432 // It's okay to call more than one.
433 KBDInitRegionUS();
434 KBDInitRegionJP();
435 KBDInitRegionEU();
436
437 // It's important to set the callbacks before calling KBDInit.
438 KBDSetAttachCallback(kbdAppAttach);
439 KBDSetDetachCallback(kbdAppDetach);
440 KBDSetKeyCallback(kbdAppKeyEvent);
441
442 KBD_CALL (KBDInit());
443
444 while (1) {
445
446 DEMOPadRead();
447
448 if ( (DEMOPadGetButtonDown(0) & PAD_BUTTON_A) ||
449 (DEMOPadGetButtonDown(0) & PAD_BUTTON_B) ) {
450 if (DEMOPadGetButtonDown(0) & PAD_BUTTON_A) {
451 country[cch] = (country[cch]+1) % MAX_CODE;
452 } else {
453 country[cch] = (country[cch]+MAX_CODE-1) % MAX_CODE;
454 }
455 KBD_CALL (KBDSetCountry (cch, countryList[country[cch]]));
456 language = 0;
457 OSReport ("Country switched to %s for channel %d.\n", countryName[countryList[country[cch]]], cch);
458
459 if (countryList[country[cch]] == KBD_CC_JAPANESE) {
460 switch(language) {
461 case 0:
462 KPRSetMode(&kprq[cch], KPR_MODE_ALT_KEYPAD);
463 break;
464 case 1:
465 KPRSetMode(&kprq[cch], (KPRMode) (KPR_MODE_JP_ROMAJI_HIRAGANA | KPR_MODE_ALT_KEYPAD));
466 break;
467 case 2:
468 KPRSetMode(&kprq[cch], (KPRMode) (KPR_MODE_JP_ROMAJI_KATAKANA | KPR_MODE_ALT_KEYPAD));
469 break;
470 }
471 } else if (countryList[country[cch]] != KBD_CC_UNITED_STATES &&
472 countryList[country[cch]] != KBD_CC_ITALIAN) {
473 KPRSetMode(&kprq[cch], (KPRMode) (KPR_MODE_DEADKEY | KPR_MODE_ALT_KEYPAD));
474 } else {
475 KPRSetMode(&kprq[cch], KPR_MODE_ALT_KEYPAD);
476 }
477 }
478
479 if ( DEMOPadGetButtonDown(0) & PAD_BUTTON_Y ) {
480 outputUTF8Example();
481 }
482
483 if ( DEMOPadGetButtonDown(0) & PAD_BUTTON_X ) {
484 cch = (KBDChannel) ((cch + 1) % KBD_MAX_CHANNELS);
485 OSReport("Keyboard channel %d selected.\n", cch);
486 }
487
488 OSYieldThread();
489 };
490
491 KBD_CALL (KBDExit()); // not reached
492
493 return 0;
494 }
495