/* RetroArch - A frontend for libretro.
* Copyright (C) 2010-2014 - Hans-Kristian Arntzen
*
* RetroArch 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 Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch 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 RetroArch.
* If not, see .
*/
#include "keyboard_line.h"
#include "../general.h"
#include "../driver.h"
#include
#include
#include
#include
struct input_keyboard_line
{
char *buffer;
size_t ptr;
size_t size;
input_keyboard_line_complete_t cb;
void *userdata;
};
void input_keyboard_line_free(input_keyboard_line_t *state)
{
if (!state)
return;
free(state->buffer);
free(state);
}
input_keyboard_line_t *input_keyboard_line_new(void *userdata,
input_keyboard_line_complete_t cb)
{
input_keyboard_line_t *state = (input_keyboard_line_t*)
calloc(1, sizeof(*state));
if (!state)
return NULL;
state->cb = cb;
state->userdata = userdata;
return state;
}
bool input_keyboard_line_event(
input_keyboard_line_t *state, uint32_t character)
{
/* Treat extended chars as ? as we cannot support
* printable characters for unicode stuff. */
char c = character >= 128 ? '?' : character;
if (c == '\r' || c == '\n')
{
state->cb(state->userdata, state->buffer);
return true;
}
if (c == '\b' || c == '\x7f') /* 0x7f is ASCII for del */
{
if (state->ptr)
{
memmove(state->buffer + state->ptr - 1, state->buffer + state->ptr,
state->size - state->ptr + 1);
state->ptr--;
state->size--;
}
}
/* Handle left/right here when suitable */
else if (isprint(c))
{
char *newbuf = (char*)realloc(state->buffer, state->size + 2);
if (!newbuf)
return false;
memmove(newbuf + state->ptr + 1,
newbuf + state->ptr, state->size - state->ptr + 1);
newbuf[state->ptr] = c;
state->ptr++;
state->size++;
newbuf[state->size] = '\0';
state->buffer = newbuf;
}
return false;
}
const char **input_keyboard_line_get_buffer(
const input_keyboard_line_t *state)
{
return (const char**)&state->buffer;
}
static input_keyboard_line_t *g_keyboard_line;
static input_keyboard_press_t g_keyboard_press_cb;
static void *g_keyboard_press_data;
const char **input_keyboard_start_line(void *userdata,
input_keyboard_line_complete_t cb)
{
if (g_keyboard_line)
input_keyboard_line_free(g_keyboard_line);
g_keyboard_line = input_keyboard_line_new(userdata, cb);
/* While reading keyboard line input, we have to block all hotkeys. */
driver.block_input = true;
return input_keyboard_line_get_buffer(g_keyboard_line);
}
void input_keyboard_wait_keys(void *userdata, input_keyboard_press_t cb)
{
g_keyboard_press_cb = cb;
g_keyboard_press_data = userdata;
/* While waiting for input, we have to block all hotkeys. */
driver.block_input = true;
}
void input_keyboard_wait_keys_cancel(void)
{
g_keyboard_press_cb = NULL;
g_keyboard_press_data = NULL;
driver.block_input = false;
}
void input_keyboard_event(bool down, unsigned code,
uint32_t character, uint16_t mod)
{
static bool deferred_wait_keys;
if (deferred_wait_keys)
{
if (!down)
{
input_keyboard_wait_keys_cancel();
deferred_wait_keys = false;
}
}
else if (g_keyboard_press_cb)
{
if (down && code != RETROK_UNKNOWN
&& !g_keyboard_press_cb(g_keyboard_press_data, code))
deferred_wait_keys = true;
}
else if (g_keyboard_line)
{
if (down && input_keyboard_line_event(g_keyboard_line, character))
{
/* Line is complete, can free it now. */
input_keyboard_line_free(g_keyboard_line);
g_keyboard_line = NULL;
/* Unblock all hotkeys. */
driver.block_input = false;
}
}
else if (g_extern.system.key_event)
g_extern.system.key_event(down, code, character, mod);
}