foxygit / doom Log in
commit c1ff9bfd5b0effd4377c0a9f669ca96093547d98
Author:     Simon Howard <fraggle@soulsphere.org>
AuthorDate: Sat Dec 17 21:14:13 2016 -0500
Commit:     Simon Howard <fraggle@soulsphere.org>
CommitDate: Sat Dec 17 21:14:13 2016 -0500

    textscreen: Add input mode switching.

    This replaces the old control over whether key mapping is enabled. We
    have three different modes used in different situations: "normal" (used
    when navigating windows, etc.); "raw" (used when configuring a key and
    we want something derived from the scancode) and "text" (used when typing
    something into an input box widget).
---
 src/setup/txt_keyinput.c  | 12 ++++-----
 textscreen/txt_inputbox.c | 17 ++++++++++--
 textscreen/txt_main.h     | 24 ++++++++++++++---
 textscreen/txt_sdl.c      | 69 +++++++++++++++++++++++++++++++++++++++++------
 4 files changed, 102 insertions(+), 20 deletions(-)

diff --git a/src/setup/txt_keyinput.c b/src/setup/txt_keyinput.c
index 240c4f47..bf76f07b 100644
--- a/src/setup/txt_keyinput.c
+++ b/src/setup/txt_keyinput.c
@@ -33,7 +33,7 @@ static int KeyPressCallback(txt_window_t *window, int key,

     if (key != KEY_ESCAPE)
     {
-        // Got the key press.  Save to the variable and close the window.
+        // Got the key press. Save to the variable and close the window.

         *key_input->variable = key;

@@ -44,9 +44,8 @@ static int KeyPressCallback(txt_window_t *window, int key,

         TXT_CloseWindow(window);

-        // Re-enable key mappings now that we have the key
-
-        TXT_EnableKeyMapping(1);
+        // Return to normal input mode now that we have the key.
+        TXT_SetInputMode(TXT_INPUT_NORMAL);

         return 1;
     }
@@ -74,9 +73,8 @@ static void OpenPromptWindow(txt_key_input_t *key_input)

     TXT_SetKeyListener(window, KeyPressCallback, key_input);

-    // Disable key mappings while we prompt for the key press
-
-    TXT_EnableKeyMapping(0);
+    // Switch to raw input mode while we're grabbing the key.
+    TXT_SetInputMode(TXT_INPUT_RAW);

     // Grab input while reading the key.  On Windows Mobile
     // handheld devices, the hardware keypresses are only
diff --git a/textscreen/txt_inputbox.c b/textscreen/txt_inputbox.c
index 60512845..2e3a5d0c 100644
--- a/textscreen/txt_inputbox.c
+++ b/textscreen/txt_inputbox.c
@@ -64,9 +64,21 @@ static void StartEditing(txt_inputbox_t *inputbox)
         SetBufferFromValue(inputbox);
     }

+    // Switch to text input mode so we get shifted input.
+    TXT_SetInputMode(TXT_INPUT_TEXT);
     inputbox->editing = 1;
 }

+static void StopEditing(txt_inputbox_t *inputbox)
+{
+    if (inputbox->editing)
+    {
+        // Switch back to normal input mode.
+        TXT_SetInputMode(TXT_INPUT_NORMAL);
+        inputbox->editing = 0;
+    }
+}
+
 static void FinishEditing(txt_inputbox_t *inputbox)
 {
     if (!inputbox->editing)
@@ -88,7 +100,7 @@ static void FinishEditing(txt_inputbox_t *inputbox)

     TXT_EmitSignal(&inputbox->widget, "changed");

-    inputbox->editing = 0;
+    StopEditing(inputbox);
 }

 static void TXT_InputBoxSizeCalc(TXT_UNCAST_ARG(inputbox))
@@ -164,6 +176,7 @@ static void TXT_InputBoxDestructor(TXT_UNCAST_ARG(inputbox))
 {
     TXT_CAST_ARG(txt_inputbox_t, inputbox);

+    StopEditing(inputbox);
     free(inputbox->buffer);
 }

@@ -227,7 +240,7 @@ static int TXT_InputBoxKeyPress(TXT_UNCAST_ARG(inputbox), int key)

     if (key == KEY_ESCAPE)
     {
-        inputbox->editing = 0;
+        StopEditing(inputbox);
     }

     if (key == KEY_BACKSPACE)
diff --git a/textscreen/txt_main.h b/textscreen/txt_main.h
index 02a7cdda..e5eb3a09 100644
--- a/textscreen/txt_main.h
+++ b/textscreen/txt_main.h
@@ -98,6 +98,25 @@ typedef enum
     TXT_NUM_MODIFIERS
 } txt_modifier_t;

+// Due to the way the SDL API works, we provide different ways of configuring
+// how we read input events, each of which is useful in different scenarios.
+typedef enum
+{
+    // "Localized" output that takes software keyboard layout into account,
+    // but key shifting has no effect.
+    TXT_INPUT_NORMAL,
+
+    // "Raw" input; the keys correspond to physical keyboard layout and
+    // software keyboard layout has no effect.
+    TXT_INPUT_RAW,
+
+    // Used for full text input. Events are fully shifted and localized.
+    // However, not all keyboard keys will generate input.
+    // Setting this mode may activate the on-screen keyboard, depending on
+    // device and OS.
+    TXT_INPUT_TEXT,
+} txt_input_mode_t;
+
 // Initialize the screen
 // Returns 1 if successful, 0 if failed.
 int TXT_Init(void);
@@ -131,9 +150,8 @@ void TXT_GetMousePosition(int *x, int *y);
 // Optional timeout in ms (timeout == 0 : sleep forever)
 void TXT_Sleep(int timeout);

-// Controls whether keys are returned from TXT_GetChar based on keyboard
-// mapping, or raw key code.
-void TXT_EnableKeyMapping(int enable);
+// Change mode for text input.
+void TXT_SetInputMode(txt_input_mode_t mode);

 // Set the window title of the window containing the text mode screen
 void TXT_SetWindowTitle(char *title);
diff --git a/textscreen/txt_sdl.c b/textscreen/txt_sdl.c
index 57d90644..20830254 100644
--- a/textscreen/txt_sdl.c
+++ b/textscreen/txt_sdl.c
@@ -53,7 +53,9 @@ SDL_Window *TXT_SDLWindow;
 static SDL_Surface *screenbuffer;
 static unsigned char *screendata;
 static SDL_Renderer *renderer;
-static int key_mapping = 1;
+
+// Current input mode.
+static txt_input_mode_t input_mode = TXT_INPUT_NORMAL;

 // Dimensions of the screen image in screen coordinates (not pixels); this
 // is the value that was passed to SDL_CreateWindow().
@@ -514,10 +516,8 @@ void TXT_GetMousePosition(int *x, int *y)
 // XXX: duplicate from doomtype.h
 #define arrlen(array) (sizeof(array) / sizeof(*array))

-static int TranslateKey(SDL_Keysym *sym)
+static int TranslateScancode(SDL_Scancode scancode)
 {
-    int scancode = sym->scancode;
-
     switch (scancode)
     {
         case SDL_SCANCODE_LCTRL:
@@ -535,7 +535,7 @@ static int TranslateKey(SDL_Keysym *sym)
             return KEY_RALT;

         default:
-            if (scancode >= 0 && scancode < arrlen(scancode_translate_table))
+            if (scancode < arrlen(scancode_translate_table))
             {
                 return scancode_translate_table[scancode];
             }
@@ -546,6 +546,25 @@ static int TranslateKey(SDL_Keysym *sym)
     }
 }

+static int TranslateKeysym(SDL_Keysym *sym)
+{
+    int translated;
+
+    // We cheat here and make use of TranslateScancode. The range of keys
+    // associated with printable characters is pretty contiguous, so if it's
+    // inside that range we want the localized version of the key instead.
+    translated = TranslateScancode(sym->scancode);
+
+    if (translated >= 0x20 && translated < 0x7f)
+    {
+        return sym->sym;
+    }
+    else
+    {
+        return translated;
+    }
+}
+
 // Convert an SDL button index to textscreen button index.
 //
 // Note special cases because 2 == mid in SDL, 3 == mid in textscreen/setup
@@ -669,12 +688,37 @@ signed int TXT_GetChar(void)
             case SDL_KEYDOWN:
                 UpdateModifierState(&ev.key.keysym, 1);

-                return TranslateKey(&ev.key.keysym);
+                switch (input_mode)
+                {
+                    case TXT_INPUT_RAW:
+                        return TranslateScancode(ev.key.keysym.scancode);
+                    case TXT_INPUT_NORMAL:
+                        return TranslateKeysym(&ev.key.keysym);
+                    case TXT_INPUT_TEXT:
+                        // We ignore key inputs in this mode, except for a
+                        // few special cases needed during text input:
+                        if (ev.key.keysym.sym == SDLK_ESCAPE
+                         || ev.key.keysym.sym == SDLK_BACKSPACE
+                         || ev.key.keysym.sym == SDLK_RETURN)
+                        {
+                            return TranslateKeysym(&ev.key.keysym);
+                        }
+                        break;
+                }
+                break;

             case SDL_KEYUP:
                 UpdateModifierState(&ev.key.keysym, 0);
                 break;

+            case SDL_TEXTINPUT:
+                if (input_mode == TXT_INPUT_TEXT)
+                {
+                    // TODO: Support input of more than just the first char.
+                    return ev.text.text[0];
+                }
+                break;
+
             case SDL_QUIT:
                 // Quit = escape
                 return 27;
@@ -871,9 +915,18 @@ void TXT_Sleep(int timeout)
     }
 }

-void TXT_EnableKeyMapping(int enable)
+void TXT_SetInputMode(txt_input_mode_t mode)
 {
-    key_mapping = enable;
+    if (mode == TXT_INPUT_TEXT && !SDL_IsTextInputActive())
+    {
+        SDL_StartTextInput();
+    }
+    else if (SDL_IsTextInputActive() && mode != TXT_INPUT_TEXT)
+    {
+        SDL_StopTextInput();
+    }
+
+    input_mode = mode;
 }

 void TXT_SetWindowTitle(char *title)