/********************************************************************* * * Copyright (C) 2009, Simon Kagstrom * * Filename: VirtualKeyboard.c * Author: Simon Kagstrom * Description: A virtual keyboard * * $Id$ * ********************************************************************/ #include #include #include "menu.h" #include "VirtualKeyboard.h" 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 KEY_COLS 15 #define KEY_ROWS 5 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"), }; 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(SDL_Surface *screen, TTF_Font *font) { this->screen = screen; this->font = font; this->sel_x = 0; this->sel_y = 0; this->shift_on = false; memset(this->buf, 0, sizeof(this->buf)); } void VirtualKeyboard::draw() { int screen_w = this->screen->w; int screen_h = this->screen->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(this->screen, &bg_rect, SDL_MapRGB(screen->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(this->screen, this->font, r, g, b, x * key_w + border_x, y * key_h + border_y, what); } } } void VirtualKeyboard::select_next(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->select_next(-1, 0); else this->select_next(dx, dy); } } void VirtualKeyboard::toggle_shift() { this->shift_on = !this->shift_on; } const char *VirtualKeyboard::keycode_to_string(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::char_to_keycode(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::get_char(int kc) { /* NULL is never, ever returned */ return this->keycode_to_string(kc)[0]; } struct virtkey *VirtualKeyboard::get_key_internal() { while(1) { uint32_t k; this->draw(); SDL_Flip(this->screen); k = menu_wait_key_press(); if (k & KEY_UP) this->select_next(0, -1); else if (k & KEY_DOWN) this->select_next(0, 1); else if (k & KEY_LEFT) this->select_next(-1, 0); else if (k & KEY_RIGHT) this->select_next(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->toggle_shift(); else return key; } } return NULL; } int VirtualKeyboard::get_key() { virtkey_t *key; SDL_FillRect(this->screen, 0, SDL_MapRGB(screen->format, 0x00, 0x80, 0x80)); key = this->get_key_internal(); if (key == NULL) return -2; if (key->is_shift) return key->kc | 0x80; return key->kc; } const char *VirtualKeyboard::get_string() { 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->get_key_internal(); 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->get_char( this->shift_on ? key->kc | 0x80 : key->kc ); if (strcmp(key->name, "space") == 0) c = ' '; else if (strcmp(key->name, "Ret") == 0) c = '\n'; 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, this->font, 255, 255, 0, 40, screen->h - 50, this->buf); } /* Not reachable */ return NULL; }