scoutos / prex-0.9.0 / bsp / drv / arm / gba / swkbd.c @ 03e9c04a
History | View | Annotate | Download (10.5 KB)
1 |
/*-
|
---|---|
2 |
* Copyright (c) 2005, Kohsuke Ohtani
|
3 |
* All rights reserved.
|
4 |
*
|
5 |
* Redistribution and use in source and binary forms, with or without
|
6 |
* modification, are permitted provided that the following conditions
|
7 |
* are met:
|
8 |
* 1. Redistributions of source code must retain the above copyright
|
9 |
* notice, this list of conditions and the following disclaimer.
|
10 |
* 2. Redistributions in binary form must reproduce the above copyright
|
11 |
* notice, this list of conditions and the following disclaimer in the
|
12 |
* documentation and/or other materials provided with the distribution.
|
13 |
* 3. Neither the name of the author nor the names of any co-contributors
|
14 |
* may be used to endorse or promote products derived from this software
|
15 |
* without specific prior written permission.
|
16 |
*
|
17 |
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
18 |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
19 |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
20 |
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
21 |
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
22 |
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
23 |
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
24 |
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
25 |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
26 |
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
27 |
* SUCH DAMAGE.
|
28 |
*/
|
29 |
|
30 |
/*
|
31 |
* swkbd.c - GBA software keyboard driver
|
32 |
*/
|
33 |
|
34 |
/**
|
35 |
* This driver emulates a generic keyboard by using GBA keypad.
|
36 |
*
|
37 |
* <Key assign>
|
38 |
*
|
39 |
* On-screen keyboard:
|
40 |
*
|
41 |
* A : Select pointed key
|
42 |
* B : Enter key
|
43 |
* Select : Hide virtual keyboard
|
44 |
* Start :
|
45 |
* Right : Move keyboard cursor right
|
46 |
* Left : Move keyboard cursor left
|
47 |
* Up : Move keyboard cursor up
|
48 |
* Down : Move keyboard cursor down
|
49 |
* Button R : Toggle shift state
|
50 |
* Button L : Toggle shift state
|
51 |
*
|
52 |
* No on-screen keyboard:
|
53 |
*
|
54 |
* A : A key
|
55 |
* B : B key
|
56 |
* Select : Show virtual keyboard
|
57 |
* Start : Enter key
|
58 |
* Right : Right key
|
59 |
* Left : Left key
|
60 |
* Up : Up key
|
61 |
* Down : Down key
|
62 |
* Button R : R key
|
63 |
* Button L : L Key
|
64 |
*
|
65 |
*/
|
66 |
|
67 |
#include <driver.h> |
68 |
#include <sys/keycode.h> |
69 |
#include <wscons.h> |
70 |
#include "lcd.h" |
71 |
#include "kbd_img.h" |
72 |
#include "keymap.h" |
73 |
|
74 |
/*
|
75 |
* Since GBA does not kick interrupt for the button release, we have
|
76 |
* to wait for a while after button is pressed. Otherwise, many key
|
77 |
* events are queued by one button press.
|
78 |
*/
|
79 |
#define CURSOR_WAIT 100 /* 100 msec */ |
80 |
#define BUTTON_WAIT 200 /* 200 msec */ |
81 |
|
82 |
struct swkbd_softc {
|
83 |
device_t dev; /* device object */
|
84 |
irq_t irq; /* irq handle */
|
85 |
timer_t timer; /* timer to prevent chattering */
|
86 |
|
87 |
int kbd_on; /* 0: direct input, 1: virtual KBD */ |
88 |
int kbd_page; /* video page to display keyboard */ |
89 |
int ignore_key; /* ignore key input if true */ |
90 |
u_int pos_x; /* cursor x position */
|
91 |
u_int pos_y; /* cursor y position */
|
92 |
int cursor_type; /* current cursor type */ |
93 |
|
94 |
int shift; /* shift key state */ |
95 |
int alt; /* alt key state */ |
96 |
int ctrl; /* control key state */ |
97 |
int capslk; /* caps lock key staet */ |
98 |
}; |
99 |
|
100 |
static int swkbd_init(struct driver *); |
101 |
static void swkbd_move_cursor(void); |
102 |
|
103 |
static struct devops swkbd_devops = { |
104 |
/* open */ no_open,
|
105 |
/* close */ no_close,
|
106 |
/* read */ no_read,
|
107 |
/* write */ no_write,
|
108 |
/* ioctl */ no_ioctl,
|
109 |
/* devctl */ no_devctl,
|
110 |
}; |
111 |
|
112 |
struct driver swkbd_driver = {
|
113 |
/* name */ "swkbd", |
114 |
/* devops */ &swkbd_devops,
|
115 |
/* devsz */ sizeof(struct swkbd_softc), |
116 |
/* flags */ 0, |
117 |
/* probe */ NULL, |
118 |
/* init */ swkbd_init,
|
119 |
/* shutdown */ NULL, |
120 |
}; |
121 |
|
122 |
|
123 |
static struct swkbd_softc *swkbd_softc; |
124 |
|
125 |
|
126 |
/*
|
127 |
* Select keyboard page.
|
128 |
*
|
129 |
* Page0 ... Text only
|
130 |
* Page1 ... Text & Normal keyboard
|
131 |
* Page2 ... Text & Shifted keyboard
|
132 |
*/
|
133 |
static void |
134 |
swkbd_select_page(int page)
|
135 |
{ |
136 |
|
137 |
if (page == 0) |
138 |
REG_DISPCNT = 0x0840; /* only BG3 */ |
139 |
else if (page == 1) { |
140 |
REG_DISPCNT = 0x1A40; /* use BG1&3 */ |
141 |
swkbd_move_cursor(); |
142 |
} else {
|
143 |
REG_DISPCNT = 0x1C40; /* use BG2&3 */ |
144 |
swkbd_move_cursor(); |
145 |
} |
146 |
swkbd_softc->kbd_page = page; |
147 |
} |
148 |
|
149 |
/*
|
150 |
* Toggle keyboard type: normal or shift.
|
151 |
*/
|
152 |
static void |
153 |
swkbd_toggle_shift(void)
|
154 |
{ |
155 |
struct swkbd_softc *sc = swkbd_softc;
|
156 |
int page;
|
157 |
|
158 |
if (sc->kbd_page == 0) |
159 |
return;
|
160 |
if (sc->capslk)
|
161 |
page = sc->shift ? 1 : 2; |
162 |
else
|
163 |
page = sc->shift ? 2 : 1; |
164 |
swkbd_select_page(page); |
165 |
} |
166 |
|
167 |
/*
|
168 |
* Timer call back handler.
|
169 |
* Just clear ignoring flag.
|
170 |
*/
|
171 |
static void |
172 |
swkbd_timeout(void *arg)
|
173 |
{ |
174 |
struct swkbd_softc *sc = arg;
|
175 |
|
176 |
sc->ignore_key = 0;
|
177 |
} |
178 |
|
179 |
/*
|
180 |
* Move cursor to point key.
|
181 |
*/
|
182 |
static void |
183 |
swkbd_move_cursor(void)
|
184 |
{ |
185 |
struct swkbd_softc *sc = swkbd_softc;
|
186 |
uint16_t *oam = OAM; |
187 |
struct _key_info *ki;
|
188 |
int x, y;
|
189 |
int curcur; /* current cursor */ |
190 |
int newcur; /* new cursor */ |
191 |
|
192 |
curcur = sc->cursor_type; |
193 |
|
194 |
ki = (struct _key_info *)&key_info[sc->pos_y][sc->pos_x];
|
195 |
x = ki->pos_x + 108;
|
196 |
y = sc->pos_y * 8 + 11; |
197 |
|
198 |
newcur = 0;
|
199 |
switch (ki->width) {
|
200 |
case 9: newcur = 0; break; |
201 |
case 11: newcur = 1; break; |
202 |
case 12: newcur = 2; break; |
203 |
case 13: newcur = 3; break; |
204 |
case 15: newcur = 4; break; |
205 |
case 17: newcur = 5; break; |
206 |
case 19: newcur = 6; break; |
207 |
case 53: newcur = 7; break; |
208 |
} |
209 |
if (newcur != curcur) {
|
210 |
oam[curcur * 4] = (uint16_t)((oam[curcur * 4] & 0xff00) | 160); |
211 |
oam[curcur * 4 + 1] = (uint16_t)((oam[curcur * 4 + 1] & 0xfe00) | 240); |
212 |
sc->cursor_type = newcur; |
213 |
} |
214 |
oam[newcur * 4] = (uint16_t)((oam[newcur * 4] & 0xff00) | y); |
215 |
oam[newcur * 4 + 1] = (uint16_t)((oam[newcur * 4 + 1] & 0xfe00) | x); |
216 |
} |
217 |
|
218 |
/*
|
219 |
* Process key press
|
220 |
*/
|
221 |
static void |
222 |
swkbd_key_press(void)
|
223 |
{ |
224 |
struct swkbd_softc *sc = swkbd_softc;
|
225 |
struct _key_info *ki;
|
226 |
u_char ac; |
227 |
|
228 |
ki = (struct _key_info *)&key_info[sc->pos_y][sc->pos_x];
|
229 |
ac = ki->normal; |
230 |
|
231 |
/* Check meta key */
|
232 |
switch (ac) {
|
233 |
case K_SHFT:
|
234 |
sc->shift = !sc->shift; |
235 |
swkbd_toggle_shift(); |
236 |
return;
|
237 |
case K_CTRL:
|
238 |
sc->ctrl = !sc->ctrl; |
239 |
return;
|
240 |
case K_ALT:
|
241 |
sc->alt = !sc->alt; |
242 |
return;
|
243 |
case K_CAPS:
|
244 |
sc->capslk = !sc->capslk; |
245 |
swkbd_toggle_shift(); |
246 |
return;
|
247 |
} |
248 |
/* Check ctrl & shift state */
|
249 |
if (sc->ctrl) {
|
250 |
if (ac >= 'a' && ac <= 'z') |
251 |
ac = ac - 'a' + 0x01; |
252 |
else if (ac == '\\') |
253 |
ac = 0x1c;
|
254 |
else
|
255 |
ac = 0;
|
256 |
} else if (sc->kbd_page == 2) |
257 |
ac = ki->shifted; |
258 |
|
259 |
if (ac == 0) |
260 |
return;
|
261 |
|
262 |
/* Check caps lock state */
|
263 |
if (sc->capslk) {
|
264 |
if (ac >= 'A' && ac <= 'Z') |
265 |
ac += 'a' - 'A'; |
266 |
else if (ac >= 'a' && ac <= 'z') |
267 |
ac -= 'a' - 'A'; |
268 |
} |
269 |
|
270 |
/* Check alt key */
|
271 |
if (sc->alt)
|
272 |
ac |= 0x80;
|
273 |
|
274 |
wscons_kbd_input(ac); |
275 |
|
276 |
/*
|
277 |
* Reset meta key status
|
278 |
*/
|
279 |
if (sc->shift) {
|
280 |
sc->shift = 0;
|
281 |
swkbd_toggle_shift(); |
282 |
} |
283 |
if (sc->ctrl)
|
284 |
sc->ctrl = 0;
|
285 |
if (sc->alt)
|
286 |
sc->alt = 0;
|
287 |
} |
288 |
|
289 |
/*
|
290 |
* Input handler
|
291 |
* This routine will be called by gamepad ISR.
|
292 |
*/
|
293 |
void
|
294 |
swkbd_input(u_char c) |
295 |
{ |
296 |
struct swkbd_softc *sc = swkbd_softc;
|
297 |
int move = 0; |
298 |
int timeout = BUTTON_WAIT;
|
299 |
|
300 |
if (sc->ignore_key)
|
301 |
return;
|
302 |
|
303 |
/* Select key */
|
304 |
if (c == '\t') { |
305 |
sc->kbd_on = !sc->kbd_on; |
306 |
swkbd_select_page(sc->kbd_on); |
307 |
|
308 |
/* Reset meta status */
|
309 |
sc->shift = 0;
|
310 |
sc->alt = 0;
|
311 |
sc->ctrl = 0;
|
312 |
sc->capslk = 0;
|
313 |
goto out;
|
314 |
} |
315 |
|
316 |
/* Direct input */
|
317 |
if (!sc->kbd_on) {
|
318 |
wscons_kbd_input(c); |
319 |
goto out;
|
320 |
} |
321 |
|
322 |
switch (c) {
|
323 |
case K_LEFT:
|
324 |
if (sc->pos_x > 0) { |
325 |
if (sc->pos_y == 4 && sc->pos_x >=4 && sc->pos_x <= 8) |
326 |
sc->pos_x = 3;
|
327 |
sc->pos_x--; |
328 |
move = 1;
|
329 |
} |
330 |
break;
|
331 |
case K_RGHT:
|
332 |
if (sc->pos_x < max_x[sc->pos_y]) {
|
333 |
if (sc->pos_y == 4 && sc->pos_x > 3 && sc->pos_x <= 7) |
334 |
sc->pos_x = 8;
|
335 |
sc->pos_x++; |
336 |
move = 1;
|
337 |
} |
338 |
break;
|
339 |
case K_UP:
|
340 |
if (sc->pos_y > 0 ) { |
341 |
sc->pos_y--; |
342 |
move = 1;
|
343 |
if (sc->pos_x > max_x[sc->pos_y])
|
344 |
sc->pos_x = max_x[sc->pos_y]; |
345 |
} |
346 |
break;
|
347 |
case K_DOWN:
|
348 |
if (sc->pos_y < 4) { |
349 |
sc->pos_y++; |
350 |
move = 1;
|
351 |
if (sc->pos_x > max_x[sc->pos_y])
|
352 |
sc->pos_x = max_x[sc->pos_y]; |
353 |
} |
354 |
break;
|
355 |
case 'A': |
356 |
swkbd_key_press(); |
357 |
break;
|
358 |
case 'B': |
359 |
wscons_kbd_input('\n');
|
360 |
break;
|
361 |
case 'R': |
362 |
case 'L': |
363 |
sc->shift = sc->shift ? 0 : 1; |
364 |
swkbd_toggle_shift(); |
365 |
break;
|
366 |
} |
367 |
if (move) {
|
368 |
timeout = CURSOR_WAIT; |
369 |
swkbd_move_cursor(); |
370 |
} |
371 |
out:
|
372 |
sc->ignore_key = 1;
|
373 |
timer_callout(&sc->timer, timeout, &swkbd_timeout, sc); |
374 |
return;
|
375 |
} |
376 |
|
377 |
/*
|
378 |
* Init keyboard image
|
379 |
*/
|
380 |
static void |
381 |
swkbd_init_image(void)
|
382 |
{ |
383 |
uint8_t bit; |
384 |
uint16_t val1, val2; |
385 |
uint16_t *pal = BG_PALETTE; |
386 |
uint16_t *tile1 = KBD1_TILE; |
387 |
uint16_t *tile2 = KBD2_TILE; |
388 |
uint16_t *map1 = KBD1_MAP; |
389 |
uint16_t *map2 = KBD2_MAP; |
390 |
int i, j, row, col;
|
391 |
|
392 |
/* Load tiles for keyboard image */
|
393 |
for (i = 0; i < 32; i++) |
394 |
tile1[i] = 0;
|
395 |
|
396 |
for (i = 0; i < 64 * 12; i++) { |
397 |
bit = 0x01;
|
398 |
for (j = 0; j < 4; j++) { |
399 |
val1 = kbd1_bitmap[i] & bit ? 0xff : 0x03; |
400 |
val2 = kbd2_bitmap[i] & bit ? 0xff : 0x03; |
401 |
bit = bit << 1;
|
402 |
val1 |= kbd1_bitmap[i] & bit ? 0xff00 : 0x0300; |
403 |
val2 |= kbd2_bitmap[i] & bit ? 0xff00 : 0x0300; |
404 |
bit = bit << 1;
|
405 |
tile1[i * 4 + 32 + j] = val1; |
406 |
tile2[i * 4 + j] = val2;
|
407 |
} |
408 |
} |
409 |
|
410 |
|
411 |
/* Setup map */
|
412 |
i = 1;
|
413 |
for (row = 1; row < 7; row++) { |
414 |
for (col = 13; col < 29; col++) { |
415 |
map1[row * 32 + col] = (uint16_t)i;
|
416 |
map2[row * 32 + col] = (uint16_t)(i + 127); |
417 |
i++; |
418 |
} |
419 |
} |
420 |
|
421 |
pal[3] = RGB(0,0,31); /* Kbd bg color */ |
422 |
pal[255] = RGB(28,28,28); /* Kbd color */ |
423 |
|
424 |
/* Setup video */
|
425 |
REG_BG1CNT = 0x1284; /* Size0, 256color, priority0 */ |
426 |
REG_BG2CNT = 0x1484; /* Size0, 256color, priority0 */ |
427 |
|
428 |
swkbd_select_page(1);
|
429 |
} |
430 |
|
431 |
/*
|
432 |
* Initialize keyboard cursor
|
433 |
*/
|
434 |
static void |
435 |
swkbd_init_cursor(void)
|
436 |
{ |
437 |
int i, j;
|
438 |
uint8_t bit; |
439 |
uint16_t val; |
440 |
uint16_t *oam = OAM; |
441 |
uint16_t *cur = CURSOR_DATA; |
442 |
uint16_t *pal = SPL_PALETTE; |
443 |
|
444 |
/* Move out all objects */
|
445 |
for (i = 0; i < 128; i++) { |
446 |
oam[0] = 160; |
447 |
oam[1] = 240; |
448 |
oam += 4;
|
449 |
} |
450 |
/* Load cursor splite */
|
451 |
for (i = 0; i < 64 * 7 + 64 * 8; i++) { |
452 |
bit = 0x01;
|
453 |
for (j = 0; j < 4; j++) { |
454 |
val = cursor_bitmap[i] & bit ? 0xff : 0; |
455 |
bit = bit << 1;
|
456 |
val |= cursor_bitmap[i] & bit ? 0xff00 : 0; |
457 |
bit = bit << 1;
|
458 |
cur[i * 4 + j] = val;
|
459 |
} |
460 |
} |
461 |
|
462 |
/* Setup cursors */
|
463 |
oam = OAM; |
464 |
for (i = 0; i < 7; i++) { |
465 |
oam[0] = (uint16_t)(0x6000 + 160); /* 256 color, Horizontal */ |
466 |
oam[1] = (uint16_t)(0x8000 + 240); /* 32x16 */ |
467 |
oam[2] = (uint16_t)(i * 16); /* Tile number */ |
468 |
oam += 4;
|
469 |
} |
470 |
/* for space key */
|
471 |
oam[0] = 0x6000 + 160; /* 256 color, Horizontal */ |
472 |
oam[1] = 0xC000 + 240; /* 64x32 */ |
473 |
oam[2] = 112; |
474 |
|
475 |
pal[255] = RGB(31,0,0); /* cursor color */ |
476 |
} |
477 |
|
478 |
/*
|
479 |
* Initialize
|
480 |
*/
|
481 |
static int |
482 |
swkbd_init(struct driver *self)
|
483 |
{ |
484 |
struct swkbd_softc *sc;
|
485 |
device_t dev; |
486 |
|
487 |
dev = device_create(self, "swkbd", D_CHR);
|
488 |
|
489 |
sc = device_private(dev); |
490 |
sc->dev = dev; |
491 |
sc->kbd_on = 1;
|
492 |
|
493 |
swkbd_softc = sc; |
494 |
|
495 |
swkbd_init_cursor(); |
496 |
swkbd_init_image(); |
497 |
swkbd_move_cursor(); |
498 |
|
499 |
return 0; |
500 |
} |