/* 3dsspec - Nintendo 3DS Spectrum emulator. Copyright (C) 2021 Ian Cowburn This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see $Id: keyboard.c 64 2008-12-05 00:37:26Z ianc $ */ #include <3ds.h> #include "keyboard.h" #include "framebuffer.h" #include "stream.h" /* ---------------------------------------- STATIC DATA */ #ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE 0 #endif static int selection_on = COL_WHITE; static int selection_off = COL_BLACK; static struct { int state; int new_state; int handled; int is_sticky; } key_state[NUM_SOFT_KEYS]; static SoftKey pad_left_key = SK_5; static SoftKey pad_right_key = SK_8; static SoftKey pad_up_key = SK_7; static SoftKey pad_down_key = SK_6; static SoftKey pad_A_key = SK_0; static SoftKey pad_B_key = SK_NEWLINE; static SoftKey pad_X_key = NUM_SOFT_KEYS; static SoftKey pad_Y_key = NUM_SOFT_KEYS; static SoftKey pad_R_key = NUM_SOFT_KEYS; static SoftKey pad_L_key = NUM_SOFT_KEYS; static SoftKey pad_start_key = NUM_SOFT_KEYS; static SoftKey pad_select_key = NUM_SOFT_KEYS; #define CLEAR_STATE(SHORTCUT) \ do \ { \ if (SHORTCUT != NUM_SOFT_KEYS && \ !key_state[SHORTCUT].handled) \ { \ key_state[SHORTCUT].new_state = FALSE; \ } \ } while(0) #define CHECK_STATE(KEYS,BIT,CODE,SHORTCUT,USE_SHORTCUT) \ do \ { \ key_state[CODE].new_state = (KEYS & BIT); \ if (USE_SHORTCUT && SHORTCUT != NUM_SOFT_KEYS && \ !key_state[SHORTCUT].handled && (KEYS & BIT)) \ { \ key_state[SHORTCUT].new_state = TRUE; \ } \ } while(0) #define CELL_WIDTH 32 #define CELL_HEIGHT 48 static const char *keynames[]= { "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "A", "S", "D", "F", "G", "H", "J", "K", "L", "NEWLINE", "SHIFT", "Z", "X", "C", "V", "B", "N", "M", "SYMBOL SHIFT", "SPACE", "ABOUT", "CONFIG", "JOYPAD UP", "JOYPAD DOWN", "JOYPAD LEFT", "JOYPAD RIGHT", "A BUTTON", "B BUTTON", "X BUTTON", "Y BUTTON", "RIGHT SHOULDER BUTTON", "LEFT SHOULDER BUTTON", "START BUTTON", "SELECT BUTTON" }; /* ---------------------------------------- PRIVATE INTERFACES */ static SoftKey LocatePress(const touchPosition *p) { int kx=0,ky=0; int key = NUM_SOFT_KEYS; kx = p->px / CELL_WIDTH; ky = p->py / CELL_HEIGHT; key = kx + ky * 10; if (key>SK_SPACE) { key = NUM_SOFT_KEYS; } return key; } static void DrawSelect(Framebuffer *fb, int key, int selected) { int x,y; x = (key % 10) * CELL_WIDTH; y = (key / 10) * CELL_HEIGHT; FB_Box(fb, x, fb->height - 1 - y, CELL_WIDTH, CELL_HEIGHT, selected ? selection_on : selection_off); } static int GetEvent(SoftKeyEvent *ev, int map) { static SoftKey last = NUM_SOFT_KEYS; static int poll_index = -1; Framebuffer lower; FB_StartFrame(NULL, &lower); /* Read the keys if this is a new loop */ if (poll_index == -1) { int f; u32 keys; keys = keysHeld(); /* Clear the non-sticky keys */ for(f=SK_1; f<=SK_CONFIG; f++) { key_state[f].handled = FALSE; if (key_state[f].is_sticky) { key_state[f].new_state = key_state[f].state; } else { key_state[f].new_state = FALSE; } } /* Check the soft keyboard */ if (keys & KEY_TOUCH) { touchPosition tp; hidTouchRead(&tp); if (tp.py>192) { key_state[SK_CONFIG].new_state = TRUE; } else { SoftKey press; press = LocatePress(&tp); if (press != NUM_SOFT_KEYS) { key_state[press].handled = TRUE; if (key_state[press].is_sticky) { if (last != press) { key_state[press].new_state = !key_state[press].state; } } else { key_state[press].new_state = TRUE; } last = press; } } } else { last = NUM_SOFT_KEYS; } /* Check non soft-keyboard controls */ CHECK_STATE(keys, KEY_A, SK_PAD_A, pad_A_key, map); CHECK_STATE(keys, KEY_B, SK_PAD_B, pad_B_key, map); CHECK_STATE(keys, KEY_X, SK_PAD_X, pad_X_key, map); CHECK_STATE(keys, KEY_Y, SK_PAD_Y, pad_Y_key, map); CHECK_STATE(keys, KEY_R, SK_PAD_R, pad_R_key, map); CHECK_STATE(keys, KEY_L, SK_PAD_L, pad_L_key, map); CHECK_STATE(keys, KEY_START, SK_PAD_START, pad_start_key, map); CHECK_STATE(keys, KEY_SELECT, SK_PAD_SELECT, pad_select_key, map); CHECK_STATE(keys, KEY_UP, SK_PAD_UP, pad_up_key, map); CHECK_STATE(keys, KEY_DOWN, SK_PAD_DOWN, pad_down_key, map); CHECK_STATE(keys, KEY_LEFT, SK_PAD_LEFT, pad_left_key, map); CHECK_STATE(keys, KEY_RIGHT, SK_PAD_RIGHT, pad_right_key, map); /* Reset key event poll index */ poll_index = 0; /* Update any on-screen indicators */ for(f=SK_1; fkey = poll_index; ev->pressed = key_state[poll_index].state; return TRUE; } else { poll_index = -1; return FALSE; } } /* ---------------------------------------- PUBLIC INTERFACES */ void SK_DisplayKeyboard(void) { int f; Framebuffer lower; FB_StartFrame(NULL, &lower); FB_Blit(&lower,IMG_KEYBOARD,0,0); /* Update any on-screen indicators */ for(f=SK_1; f