From d0f0dc3b50a73c40ef7789946ffadba832354f73 Mon Sep 17 00:00:00 2001 From: "simon.kagstrom" Date: Sat, 19 Dec 2009 15:40:53 +0000 Subject: [PATCH] Add code for the virtual keyboard. Won't compile just yet, --- virtual_keyboard.cpp | 378 +++++++++++++++++++++++++++++++++++++++++++ virtual_keyboard.hh | 64 ++++++++ 2 files changed, 442 insertions(+) create mode 100644 virtual_keyboard.cpp create mode 100644 virtual_keyboard.hh diff --git a/virtual_keyboard.cpp b/virtual_keyboard.cpp new file mode 100644 index 0000000..b1fdea4 --- /dev/null +++ b/virtual_keyboard.cpp @@ -0,0 +1,378 @@ +/********************************************************************* + * + * Copyright (C) 2009, Simon Kagstrom + * + * Filename: VirtualKeyboard.c + * Author: Simon Kagstrom + * Description: A virtual keyboard + * + * $Id$ + * + ********************************************************************/ +#include +#include + +#include "menu.h" +#include "virtual_keyboard.hh" + +typedef struct virtkey +{ + const char *name; + int kc; + bool is_shift; + bool is_done; +} virtkey_t; + +/* + C64 keyboard matrix: + + Bit 7 6 5 4 3 2 1 0 + 0 CUD F5 F3 F1 F7 CLR RET DEL + 1 SHL E S Z 4 A W 3 + 2 X T F C 6 D R 5 + 3 V U H B 8 G Y 7 + 4 N O K M 0 J I 9 + 5 , @ : . - L P + + 6 / ^ = SHR HOM ; * � + 7 R/S Q C= SPC 2 CTL <- 1 +*/ +#define MATRIX(a,b) (((a) << 3) | (b)) + +#define K(name, a,b) \ + { name, MATRIX(a,b), false, false } +#define S(name, a,b) \ + { name, MATRIX(a,b), true, false } +#define N(name) \ + { name, -1, false, false } +#define D(name) \ + { name, -1, false, true } +#define J(name, v) \ + { name, 0x40 | (v), false, false } + +#define KEY_COLS 15 +#define KEY_ROWS 8 + +static virtkey_t keys[KEY_COLS * KEY_ROWS] = { + K("<-",7,1), K("1", 7,0), K("2", 7,3), K("3", 1,0), K("4", 1,3), K("5", 2,0), K("6", 2,3), K("7", 3,0), K("8", 3,3), K("9", 4,0), K("0", 4,3), K("+", 5,0), K("-", 5,3), K("£", 6,0), K("Hom", 6,3), + K("Cr", 7,2), K("Q", 7,6), K("W", 1,1), K("E", 1,6), K("R", 2,1), K("T", 2,6), K("Y", 3,1), K("U", 3,6), K("I", 4,1), K("O", 4,6), K("P", 5,1), K("@", 5,6), K("*", 6,1), K("Au", 6,6),K("Rstr",4,0), + K("R/Stp", 7,7), K(NULL,0,0), K("A", 1,2), K("S", 1,5), K("D", 2,2), K("F", 2,5), K("G", 3,2), K("H", 3,5), K("J", 4,2), K("K", 4,5), K("L", 5,2), K(":", 5,5), K(";", 6,2), K("=", 6,5), K("Ret", 0,1), + K("C=", 7,5), S("Shft",1,7),K(NULL,0,0),K("Z", 1,4), K("X", 2,7), K("C", 2,4), K("V", 3,7), K("B", 3,4), K("N", 4,7), K("M", 4,4), K(",", 5,7), K(".", 5,4), K("/", 6,7), K("Dwn",0,7),K("Rgt", 0,2), + N("None"), K(NULL,0,0), K(NULL,0,0), K("space", 7,4),K(0, 0,0),K(NULL,0,0), K("f1",0,4), K("f3",0,5), K("f5",0,6), K("f7",0,3), K("Del",0,0),K(NULL,0,0), K(NULL,0,0), K(NULL,0,0), D("DONE"), + K(NULL,0,0), K(NULL,0,0), K(NULL,0,0), K(NULL,0,0), K(0, 0,0),J("Joystick up",1),K(0, 0,0),K(NULL,0,0), K(NULL,0,0), K(NULL,0,0), K(NULL, 0,0),K(NULL,0,0), K(NULL,0,0), K(NULL,0,0), K(NULL, 0,0), + J("Joystick left",4),K(0, 0,0), K(NULL,0,0), K(NULL,0,0), K(0,0,0),J("Joystick fire",0x10),K(0,0,0),K(0,0,0), K(NULL,0,0), K(0,0,0),J("Joystick right",8),K(0, 0,0),K(0, 0,0), K(NULL,0,0), K(NULL,0,0), + K(NULL,0,0), K(0, 0,0), K(NULL,0,0), K(NULL,0,0), K(0,0,0),J("Joystick down",2),K(0,0,0),K(NULL,0,0),K(NULL,0,0), K(NULL,0,0), K(NULL,0,0), K(NULL,0,0), K(NULL, 0,0),K(NULL,0,0), K(NULL, 0,0), +}; + +static const char *shifted_names[KEY_COLS * KEY_ROWS] = { + NULL, "!", "\"", "#", "$", "%", "&", "'", "(", ")", NULL, NULL, NULL, NULL, "Clr", + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "[", "]", NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "<", ">", "?", "Up", "Lft", + NULL, NULL, NULL, NULL, NULL, NULL, "f2", "f4", "f6", "f8", "Ins", NULL, NULL, NULL, NULL, +}; + +VirtualKeyboard::VirtualKeyboard(TTF_Font *font) +{ + this->font = font; + this->sel_x = 0; + this->sel_y = 0; + this->shift_on = false; + + memset(this->buf, 0, sizeof(this->buf)); + memset(this->stringListeners, 0, sizeof(this->stringListeners)); + memset(this->keyListeners, 0, sizeof(this->keyListeners)); +} + +void VirtualKeyboard::draw(SDL_Surface *where, int x, int y, int w, int h) +{ + int screen_w = where->w; + int screen_h = where->h; + int key_w = 36; + int key_h = 36; + int border_x = (screen_w - (key_w * KEY_COLS)) / 2; + int border_y = (screen_h - (key_h * KEY_ROWS)) / 2; + SDL_Rect bg_rect = {border_x, border_y, + key_w * KEY_COLS, key_h * KEY_ROWS}; + + SDL_FillRect(where, &bg_rect, + SDL_MapRGB(where->format, 0x00, 0x80, 0x80)); + + for (int y = 0; y < KEY_ROWS; y++ ) + { + for (int x = 0; x < KEY_COLS; x++ ) + { + int which = y * KEY_COLS + x; + virtkey_t key = keys[which]; + int r = 255, g = 255, b = 255; + const char *what = key.name; + + /* Skip empty positions */ + if (key.name == NULL) + continue; + if (this->shift_on && shifted_names[which]) + what = shifted_names[which]; + + if ( key.is_done ) + r = 0; + if ( (x == this->sel_x && y == this->sel_y) || + (this->shift_on && key.is_shift)) + b = 0; + + menu_print_font(where, r, g, b, + x * key_w + border_x, y * key_h + border_y, + what); + } + } +} + +void VirtualKeyboard::selectNext(int dx, int dy) +{ + int next_x = (this->sel_x + dx) % KEY_COLS; + int next_y = (this->sel_y + dy) % KEY_ROWS; + virtkey_t key; + + if (next_x < 0) + next_x = KEY_COLS + next_x; + if (next_y < 0) + next_y = KEY_ROWS + next_y; + this->sel_x = next_x; + this->sel_y = next_y; + + key = keys[ next_y * KEY_COLS + next_x ]; + + /* Skip the empty spots */ + if (key.name == NULL) + { + if (dy != 0) /* Look left */ + this->selectNext(-1, 0); + else + this->selectNext(dx, dy); + } +} + +void VirtualKeyboard::toggleShift() +{ + this->shift_on = !this->shift_on; +} + +const char *VirtualKeyboard::keycodeToString(int kc) +{ + bool shifted = kc & 0x80; + int kc_raw = kc & ~0x80; + const char *out = "Unknown"; + + if (kc < 0) + return "None"; + + /* Just loop through all of them */ + for (int i = 0; i < KEY_COLS * KEY_ROWS; i++) + { + virtkey_t key = keys[i]; + + if (key.kc == kc_raw && key.name != NULL) + { + out = key.name; + + if (shifted && shifted_names[i]) + out = shifted_names[i]; + break; + } + } + + return out; +} + +int VirtualKeyboard::charToKeycode(char c) +{ + for (int i = 0; i < KEY_COLS * KEY_ROWS; i++) + { + virtkey_t key = keys[i]; + + if (key.name != NULL) + { + if (strlen(key.name) == 1) + { + if (key.name[0] == c) + return key.kc; + if (shifted_names[i] && strlen(shifted_names[i]) == 1 && + shifted_names[i][0] == c) + return key.kc | 0x80; + } + + /* OK, ugly special cases, but these are pretty important */ + if (c == ' ' && strcmp(key.name, "space") == 0) + return key.kc; + if (c == '\n' && strcmp(key.name, "Ret") == 0) + return key.kc; + } + } + + return -1; +} + +const char VirtualKeyboard::keycodeToChar(int kc) +{ + const char *s = this->keycodeToString(kc); + + if (strcmp(s, "space") == 0) + return ' '; + if (strcmp(s, "Ret") == 0) + return '\n'; + if (strcmp(s, "Del") == 0) + return '\b'; + + /* NULL is never, ever returned */ + return s[0]; +} + +struct virtkey *VirtualKeyboard::getKeyInternal() +{ + while(1) + { + uint32_t k; + + this->draw(); + SDL_Flip(this->screen); + + k = menu_wait_key_press(); + + if (k & KEY_UP) + this->selectNext(0, -1); + else if (k & KEY_DOWN) + this->selectNext(0, 1); + else if (k & KEY_LEFT) + this->selectNext(-1, 0); + else if (k & KEY_RIGHT) + this->selectNext(1, 0); + else if (k & KEY_ESCAPE) + return NULL; + else if (k & KEY_SELECT) + { + virtkey_t *key = &keys[ this->sel_y * KEY_COLS + this->sel_x ]; + + if (key->is_shift == true) + this->toggleShift(); + else + return key; + } + } + + return NULL; +} + +int VirtualKeyboard::getKey() +{ + virtkey_t *key; + + SDL_FillRect(this->screen, 0, SDL_MapRGB(screen->format, 0x00, 0x80, 0x80)); + + key = this->getKeyInternal(); + if (key == NULL) + return -2; + + if (this->shift_on) + return key->kc | 0x80; + return key->kc; +} + +const char *VirtualKeyboard::getString() +{ + int cnt = 0; + + SDL_FillRect(this->screen, 0, SDL_MapRGB(screen->format, 0x00, 0x80, 0x80)); + memset(this->buf, 0, sizeof(this->buf)); + + while (true) + { + virtkey_t *key = this->getKeyInternal(); + char c; + + /* Abort */ + if (key == NULL) + return NULL; + + /* Done */ + if (key->is_done) + return this->buf; + /* Skip None */ + if (key->kc == -1) + continue; + + /* Special-case for delete */ + if (strcmp(key->name, "Del") == 0) + { + if (cnt < 1) + continue; + this->buf[cnt - 1] = ' '; + cnt -= 2; + } + else + { + c = this->keycodeToChar( this->shift_on ? key->kc | 0x80 : key->kc ); + + this->buf[cnt] = c; + } + + cnt++; + if (cnt >= sizeof(this->buf) - 1) + return this->buf; + + /* SDL_Flip is done in get_key_internal() */ + SDL_FillRect(this->screen, 0, SDL_MapRGB(screen->format, 0x00, 0x80, 0x80)); + menu_print_font(this->screen, 255, 255, 0, + 40, screen->h - 50, + this->buf); + } + + /* Not reachable */ + return NULL; +} + +void VirtualKeyboard::registerListener(KeyListener *kl) +{ + int n_listeners = sizeof(this->keyListeners) / sizeof(*this->keyListeners); + int i; + + for (i = 0; i < n_listeners; i++) + if (!this->keyListeners[i]) + break; + panic_if(i == n_listeners, + "No free listeners!\n"); + this->keyListeners[i] = kl; +} + +/* Templates FTW! */ +void VirtualKeyboard::registerListener(StringListener *sl) +{ + int n_listeners = sizeof(this->stringListeners) / sizeof(*this->stringListeners); + int i; + + for (i = 0; i < n_listeners; i++) + if (!this->stringListeners[i]) + break; + panic_if(i == n_listeners, + "No free listeners!\n"); + this->stringListeners[i] = sl; +} + +void VirtualKeyboard::unregisterListener(KeyListener *kl) +{ + int n_listeners = sizeof(this->keyListeners) / sizeof(*this->keyListeners); + + for (int i = 0; i < n_listeners; i++) + { + if (this->keyListeners[i] == kl) + this->keyListeners[i] = NULL; + } +} + +void VirtualKeyboard::unregisterListener(StringListener *sl) +{ + int n_listeners = sizeof(this->stringListeners) / sizeof(*this->stringListeners); + + for (int i = 0; i < n_listeners; i++) + { + if (this->stringListeners[i] == sl) + this->stringListeners[i] = NULL; + } +} diff --git a/virtual_keyboard.hh b/virtual_keyboard.hh new file mode 100644 index 0000000..74242e7 --- /dev/null +++ b/virtual_keyboard.hh @@ -0,0 +1,64 @@ +/********************************************************************* + * + * Copyright (C) 2009, Simon Kagstrom + * + * Filename: VirtualKeyboard.c + * Author: Simon Kagstrom + * Description: A virtual keyboard + * + * $Id$ + * + ********************************************************************/ +#include +#include + +struct virtkey; + +class KeyListener +{ + void keyCallback(char c); +}; + +class StringListener +{ + void stringCallback(const char *str); +}; + +class VirtualKeyboard +{ +public: + VirtualKeyboard(TTF_Font *font); + + void registerListener(KeyListener *kl); + void registerListener(StringListener *sl); + void unregisterListener(KeyListener *kl); + void unregisterListener(StringListener *sl); + + /* Conversions */ + int getKey(); + const char *getString(); + const char *keycodeToString(int kc); + const char keycodeToChar(int kc); + int charToKeycode(char c); + + void pushEvent(SDL_Event *ev); + + void draw(SDL_Surface *where, int x, int y, int w, int h); + + /* Singleton object */ + static VirtualKeyboard *kbd; +private: + KeyListener *keyListeners[8]; + StringListener *stringListeners[8]; + + struct virtkey *getKeyInternal(); + void selectNext(int dx, int dy); + void toggleShift(); + + TTF_Font *font; + int sel_x; + int sel_y; + bool shift_on; + + char buf[255]; +};