/********************************************************************* * * Copyright (C) 2009, Simon Kagstrom * * Filename: VirtualKeyboard.c * Author: Simon Kagstrom * Description: A virtual keyboard * * $Id$ * ********************************************************************/ #include #include "virtual_keyboard.hh" #include "utils.hh" #include "gui.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(Font *font) : GuiView() { this->font = font; this->sel_x = 0; this->sel_y = 0; this->shift_on = false; this->is_active = false; this->buf_head = 0; memset(this->buf, 0, sizeof(this->buf)); this->flushListeners(); } void VirtualKeyboard::draw(SDL_Surface *where, int x_base, int y_base, int w, int h) { int key_w = w / KEY_COLS; int key_h = h / KEY_ROWS; SDL_Rect bg_rect = {x_base, y_base, 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]; 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 (this->sel_x == x && this->sel_y == y) highlight_background(where, Gui::gui->small_font, Gui::gui->bg_left, Gui::gui->bg_middle, Gui::gui->bg_right, x * key_w + x_base, y * key_h + y_base, this->font->getWidth(what), h); this->font->draw(where, what, x * key_w + x_base, y * key_h + y_base, w, h); } } } 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]; } void VirtualKeyboard::registerListener(KeyboardListener *kl) { int n_listeners = sizeof(this->listeners) / sizeof(*this->listeners); int i; /* Don't register already registered listeners */ for (i = 0; i < n_listeners; i++) if (this->listeners[i] == kl) return; /* Find a free spot */ for (i = 0; i < n_listeners; i++) if (!this->listeners[i]) break; panic_if(i == n_listeners, "No free listeners!\n"); this->listeners[i] = kl; } void VirtualKeyboard::unregisterListener(KeyboardListener *kl) { int n_listeners = sizeof(this->listeners) / sizeof(*this->listeners); for (int i = 0; i < n_listeners; i++) { if (this->listeners[i] == kl) this->listeners[i] = NULL; } } void VirtualKeyboard::activate() { this->is_active = true; memset(this->buf, 0, sizeof(this->buf)); this->buf_head = 0; } void VirtualKeyboard::runLogic() { event_t ev; if (!this->is_active) return; ev = this->popEvent(); if (ev == EVENT_NONE) return; if (ev & KEY_UP) this->selectNext(0, -1); else if (ev & KEY_DOWN) this->selectNext(0, 1); else if (ev & KEY_LEFT) this->selectNext(-1, 0); else if (ev & KEY_RIGHT) this->selectNext(1, 0); else if (ev & KEY_ESCAPE) this->deactivate(); else if (ev & KEY_SELECT) { virtkey_t *key = &keys[ this->sel_y * KEY_COLS + this->sel_x ]; if (!key) return; if (key->is_shift == true) this->toggleShift(); else if (key->is_done) { int n_listeners = sizeof(this->listeners) / sizeof(*this->listeners); for (int i = 0; i < n_listeners; i++) { if (this->listeners[i]) this->listeners[i]->stringCallback(this->buf); } /* We're done! */ this->deactivate(); } else if (strcmp(key->name, "Del") == 0) { if (this->buf_head > 1) { this->buf[this->buf_head - 1] = ' '; this->buf_head -= 2; } } else { int n_listeners = sizeof(this->listeners) / sizeof(*this->listeners); char c; /* Add to buf */ c = this->keycodeToChar( this->shift_on ? key->kc | 0x80 : key->kc ); this->buf[this->buf_head] = c; this->buf_head++; if (this->buf_head >= sizeof(this->buf) - 1) this->buf_head = 0; /* OK, not good, but well... */ for (int i = 0; i < n_listeners; i++) { if (this->listeners[i]) this->listeners[i]->keyCallback(this->shift_on, key->name); } } } } void VirtualKeyboard::flushListeners() { memset(this->listeners, 0, sizeof(this->listeners)); } void VirtualKeyboard::deactivate() { this->is_active = false; this->flushListeners(); Gui::gui->popView(); } void VirtualKeyboard::draw(SDL_Surface *where) { this->draw(where, 20, 240, 600, 240); } void VirtualKeyboard::updateTheme() { this->setFont(Gui::gui->small_font); } /* The singleton */ VirtualKeyboard *VirtualKeyboard::kbd; KeyboardListener::~KeyboardListener() { VirtualKeyboard::kbd->unregisterListener(this); }