You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
450 lines
13 KiB
450 lines
13 KiB
#include <assert.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "input.h"
|
|
#include "../common/array.h"
|
|
#include "../common/log.h"
|
|
#include "gui.h"
|
|
#include "../common/string_utils.h"
|
|
#include "../common/common.h"
|
|
|
|
struct Input_Map
|
|
{
|
|
char* name;
|
|
struct Key_Combination* keys;
|
|
int state;
|
|
};
|
|
|
|
static void input_on_key(int key, int scancode, int state, int repeat, int mod_ctrl, int mod_shift, int mod_alt);
|
|
static void input_on_mousebutton(int button, int state, int x, int y, int8 num_clicks);
|
|
static void input_on_mousemotion(int x, int y, int xrel, int yrel);
|
|
static void input_on_mousewheel(int x, int y);
|
|
static int map_find(const char* name);
|
|
|
|
static struct Input_Map* input_map_list = NULL;
|
|
|
|
void input_init(void)
|
|
{
|
|
platform->keyboard_callback_set(&input_on_key);
|
|
platform->mousebutton_callback_set(&input_on_mousebutton);
|
|
platform->mousemotion_callback_set(&input_on_mousemotion);
|
|
platform->mousewheel_callback_set(&input_on_mousewheel);
|
|
|
|
input_map_list = array_new(struct Input_Map);
|
|
if(!input_keybinds_load("keybindings.cfg", DT_USER))
|
|
{
|
|
log_error("input:init", "Failed to load keybindings");
|
|
log_message("Reverting to default keybindings");
|
|
if(!input_keybinds_load("keybindings.cfg", DT_INSTALL))
|
|
{
|
|
log_error("input:init", "Failed to load default keybindings");
|
|
}
|
|
else
|
|
{
|
|
input_keybinds_save("keybindings.cfg");
|
|
}
|
|
}
|
|
|
|
/* struct Key_Combination forward_keys[2] = {{KEY_W, KMD_NONE}, {KEY_UP, KMD_ALT | KMD_SHIFT}}; */
|
|
/* struct Key_Combination backward_keys[2] = {{KEY_S, KMD_NONE}, {KEY_DOWN, KMD_NONE}}; */
|
|
/* struct Key_Combination up_keys[2] = {KEY_Q}; */
|
|
/* struct Key_Combination down_keys[2] = {KEY_E}; */
|
|
/* struct Key_Combination left_keys[2] = {KEY_A, KEY_LEFT}; */
|
|
/* struct Key_Combination right_keys[2] = {KEY_D, KEY_RIGHT}; */
|
|
/* struct Key_Combination turn_right_keys[1] = {KEY_L}; */
|
|
/* struct Key_Combination turn_left_keys[1] = {KEY_J}; */
|
|
/* struct Key_Combination turn_up_keys[1] = {KEY_I}; */
|
|
/* struct Key_Combination turn_down_keys[1] = {KEY_K}; */
|
|
/* struct Key_Combination sprint_keys[2] = {KEY_LSHIFT, KEY_RSHIFT}; */
|
|
/* struct Key_Combination recompute_keys[2] = {KEY_F5, KEY_H}; */
|
|
/* struct Key_Combination ed_toggle_keys[1] = {KEY_F1}; */
|
|
/* struct Key_Combination win_fullscr_keys[1] = {KEY_F11}; */
|
|
/* struct Key_Combination win_max_keys[1] = {KEY_F12}; */
|
|
/* input_map_create("Move_Forward", forward_keys, 2); */
|
|
/* input_map_create("Move_Backward", backward_keys, 2); */
|
|
/* input_map_create("Move_Up", up_keys, 1); */
|
|
/* input_map_create("Move_Down", down_keys, 1); */
|
|
/* input_map_create("Move_Left", left_keys, 2); */
|
|
/* input_map_create("Move_Right", right_keys, 2); */
|
|
/* input_map_create("Turn_Right", turn_right_keys, 1); */
|
|
/* input_map_create("Turn_Left", turn_left_keys, 1); */
|
|
/* input_map_create("Turn_Up", turn_up_keys, 1); */
|
|
/* input_map_create("Turn_Down", turn_down_keys, 1); */
|
|
/* input_map_create("Sprint", sprint_keys, 2); */
|
|
/* input_map_create("Recompute", recompute_keys, 2); */
|
|
/* input_map_create("Editor_Toggle", ed_toggle_keys, 1); */
|
|
/* input_map_create("Window_Fullscreen", win_fullscr_keys, 1); */
|
|
/* input_map_create("Window_Maximize", win_max_keys, 1); */
|
|
}
|
|
|
|
void input_cleanup(void)
|
|
{
|
|
for(int i = 0; i < array_len(input_map_list); i++)
|
|
{
|
|
struct Input_Map* map = &input_map_list[i];
|
|
if(map->name) free(map->name);
|
|
array_free(map->keys);
|
|
}
|
|
array_free(input_map_list);
|
|
input_map_list = NULL;
|
|
}
|
|
|
|
bool input_keybinds_load(const char* filename, int directory_type)
|
|
{
|
|
bool success = false;
|
|
const int MAX_KEYBIND_LEN = 128;
|
|
const int MAX_LINE_LEN = 512;
|
|
FILE* config_file = platform->file.open(directory_type, filename, "r");
|
|
if(!config_file)
|
|
{
|
|
log_error("input:keybinds_load", "Could not open %s", filename);
|
|
return success;
|
|
}
|
|
|
|
/* Read line by line, ignore comments */
|
|
char key_str[MAX_KEYBIND_LEN];
|
|
char line_buffer[MAX_LINE_LEN];
|
|
memset(key_str, '\0', MAX_KEYBIND_LEN);
|
|
memset(line_buffer, '\0', MAX_LINE_LEN);
|
|
int current_line = 0;
|
|
while(fgets(line_buffer, MAX_LINE_LEN - 1, config_file))
|
|
{
|
|
current_line++;
|
|
line_buffer[strcspn(line_buffer, "\r\n")] = '\0';
|
|
|
|
if(line_buffer[0] == '#' || strlen(line_buffer) == 0)
|
|
continue;
|
|
|
|
//log_message("Line : %s", line_buffer);
|
|
memset(key_str, '\0', MAX_KEYBIND_LEN);
|
|
char* value_str = strstr(line_buffer, ":");
|
|
if(!value_str)
|
|
{
|
|
log_warning("Malformed value in config file %s, line %d", filename, current_line);
|
|
continue;
|
|
}
|
|
|
|
value_str++; /* Ignore the colon(:) and set the pointer after it */
|
|
|
|
if(sscanf(line_buffer, " %1024[^: ] : %*s", key_str) != 1)
|
|
{
|
|
log_warning("Unable to read key in keybindings file %s, line %d", filename, current_line);
|
|
continue;
|
|
}
|
|
|
|
char* val = strtok(value_str, ",");
|
|
if(!val)
|
|
{
|
|
log_warning("Unable to parse keys for keybinding %s in file %s, line %d", key_str, filename, current_line);
|
|
continue;
|
|
}
|
|
|
|
while(val)
|
|
{
|
|
//log_message("Key read : %s", val);
|
|
|
|
/* Check if there are any Modifiers */
|
|
int modifiers = KMD_NONE;
|
|
char* keys = strstr(val, "-");
|
|
char* start_loc = val;
|
|
const int max_key_str_len = 20;
|
|
char key_name[max_key_str_len];
|
|
int skip_to_next = 0;
|
|
while(keys)
|
|
{
|
|
memset(key_name, '\0', max_key_str_len);
|
|
strncpy(key_name, start_loc, (keys - start_loc));
|
|
//log_message("key_name : %s", key_name);
|
|
|
|
int key_modifier = platform->key_from_name(key_name);
|
|
|
|
if(key_modifier == KEY_UNKNOWN)
|
|
{
|
|
log_warning("Unrecognized key %s in keybindings file %s, at line %d", key_name, filename, current_line);
|
|
skip_to_next = 1;
|
|
break;
|
|
}
|
|
|
|
switch(key_modifier)
|
|
{
|
|
case KEY_LSHIFT: case KEY_RSHIFT: modifiers |= KMD_SHIFT; break;
|
|
case KEY_LCTRL: case KEY_RCTRL: modifiers |= KMD_CTRL; break;
|
|
case KEY_LALT: case KEY_RALT: modifiers |= KMD_ALT; break;
|
|
};
|
|
|
|
++keys;
|
|
start_loc = keys;
|
|
keys = strstr(keys, "-");
|
|
}
|
|
if(skip_to_next)
|
|
{
|
|
val = strtok(NULL, ",");
|
|
continue;
|
|
}
|
|
|
|
/* Copy the last key after the hyphen */
|
|
strncpy(key_name, start_loc, max_key_str_len);
|
|
int key = platform->key_from_name(key_name);
|
|
if(key == KEY_UNKNOWN)
|
|
{
|
|
log_warning("Unrecognized key %s in keybindings file %s, at line %d", key_name, filename, current_line);
|
|
val = strtok(NULL, ",");
|
|
continue;
|
|
}
|
|
|
|
struct Key_Combination key_comb = { .key = key, .mods = modifiers};
|
|
input_map_create(key_str, &key_comb, 1);
|
|
|
|
val = strtok(NULL, ",");
|
|
}
|
|
}
|
|
|
|
success = true;
|
|
fclose(config_file);
|
|
return success;
|
|
}
|
|
|
|
bool input_keybinds_save(const char* filename)
|
|
{
|
|
bool success = false;
|
|
|
|
FILE* config_file = platform->file.open(DT_USER, filename, "w");
|
|
if(!config_file)
|
|
{
|
|
log_error("input:keybinds_save", "Could not open %s", filename);
|
|
return success;
|
|
}
|
|
|
|
for(int i = 0; i < array_len(input_map_list); i++)
|
|
{
|
|
struct Input_Map* map = &input_map_list[i];
|
|
fprintf(config_file, "%s : ", map->name);
|
|
for(int j = 0; j < array_len(map->keys); j++)
|
|
{
|
|
if(j != 0) fprintf(config_file, ", ");
|
|
struct Key_Combination* key_comb = &map->keys[j];
|
|
if((key_comb->mods & KMD_ALT) == KMD_ALT) fprintf(config_file, "%s-", platform->key_name_get(KEY_LALT));
|
|
if((key_comb->mods & KMD_SHIFT) == KMD_SHIFT) fprintf(config_file, "%s-", platform->key_name_get(KEY_LSHIFT));
|
|
if((key_comb->mods & KMD_CTRL) == KMD_CTRL) fprintf(config_file, "%s-", platform->key_name_get(KEY_LCTRL));
|
|
fprintf(config_file, "%s", platform->key_name_get(key_comb->key));
|
|
}
|
|
fprintf(config_file, "\n");
|
|
}
|
|
|
|
fclose(config_file);
|
|
log_message("Keybindings saved to %s", filename);
|
|
success = true;
|
|
return success;
|
|
}
|
|
|
|
void input_on_mousemotion(int x, int y, int xrel, int yrel)
|
|
{
|
|
/* TODO: This is temporary. After proper event loop is added this code should not be here */
|
|
gui_handle_mousemotion_event(x, y, xrel, yrel);
|
|
}
|
|
|
|
void input_on_mousewheel(int x, int y)
|
|
{
|
|
/* TODO: This is temporary. After proper event loop is added this code should not be here */
|
|
gui_handle_mousewheel_event(x, y);
|
|
}
|
|
|
|
void input_mouse_pos_get(int* xpos, int* ypos)
|
|
{
|
|
assert(xpos && ypos);
|
|
platform->mouse_position_get(xpos, ypos);
|
|
}
|
|
|
|
void input_mouse_pos_set(int xpos, int ypos)
|
|
{
|
|
platform->mouse_global_position_set(xpos, ypos);
|
|
}
|
|
|
|
void input_on_key(int key, int scancode, int state, int repeat, int mod_ctrl, int mod_shift, int mod_alt)
|
|
{
|
|
int mods = KMD_NONE;
|
|
if(mod_ctrl) mods |= KMD_CTRL;
|
|
if(mod_shift) mods |= KMD_SHIFT;
|
|
if(mod_alt) mods |= KMD_ALT;
|
|
for(int i = 0; i < array_len(input_map_list); i++)
|
|
{
|
|
struct Input_Map* map = &input_map_list[i];
|
|
for(int j = 0; j < array_len(map->keys); j++)
|
|
{
|
|
/* if(map->state == KS_PRESSED && */
|
|
/* state == KS_RELEASED && */
|
|
/* ((map->keys[j].mods & mods) == map->keys[j].mods)) */
|
|
/* { */
|
|
/* map->state = state; */
|
|
/* } */
|
|
if(map->keys[j].key == key && ((map->keys[j].mods & mods) == map->keys[j].mods))
|
|
{
|
|
map->state = state;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
/* TODO: This is temporary. After proper event loop is added this code should not be here */
|
|
gui_handle_keyboard_event(key, state, mod_ctrl, mod_shift);
|
|
}
|
|
|
|
void input_on_mousebutton(int button, int state, int x, int y, int8 num_clicks)
|
|
{
|
|
/* Probably add 'mouse maps', same as input maps for keyboard but with buttons
|
|
Do we even need that?
|
|
*/
|
|
/* TODO: This is temporary. After proper event loop is added this code should not be here */
|
|
gui_handle_mousebutton_event(button, state, x, y);
|
|
}
|
|
|
|
void input_mouse_mode_set(enum Mouse_Mode mode)
|
|
{
|
|
platform->mouse_relative_mode_set(mode == MM_NORMAL ? 0 : 1);
|
|
}
|
|
|
|
bool input_map_state_get(const char* map_name, int state)
|
|
{
|
|
int current_state = KS_INACTIVE;
|
|
for(int i = 0; i < array_len(input_map_list); i++)
|
|
{
|
|
struct Input_Map* map = &input_map_list[i];
|
|
if(strcmp(map->name, map_name) == 0)
|
|
{
|
|
current_state = map->state;
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool result = false;
|
|
if(current_state == state)
|
|
{
|
|
result = true;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
bool input_is_key_pressed(int key)
|
|
{
|
|
return platform->is_key_pressed(key);
|
|
}
|
|
|
|
bool input_mousebutton_state_get(uint button, int state_type)
|
|
{
|
|
int current_state = platform->mousebutton_state_get(button);
|
|
return state_type == current_state ? true : false;
|
|
}
|
|
|
|
void input_map_create(const char* name, struct Key_Combination* keys, int num_keys)
|
|
{
|
|
assert(name && keys && num_keys > 0);
|
|
int index = map_find(name);
|
|
if(index > -1)
|
|
{
|
|
struct Input_Map* map = &input_map_list[index];
|
|
for(int i = 0; i < num_keys; i++)
|
|
{
|
|
struct Key_Combination* new_comb = array_grow(map->keys, struct Key_Combination);
|
|
*new_comb = keys[i];
|
|
log_message("Added new Key combination to input map : %s", name);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
struct Input_Map* new_map = array_grow(input_map_list, struct Input_Map);
|
|
new_map->name = str_new(name);
|
|
new_map->keys = array_new_cap(struct Key_Combination, num_keys);
|
|
new_map->state = KS_INACTIVE;
|
|
for(int i = 0; i < num_keys; i++)
|
|
new_map->keys[i] = keys[i];
|
|
log_message("Created Input Map : %s", name);
|
|
}
|
|
}
|
|
|
|
void input_update(void)
|
|
{
|
|
for(int i = 0; i < array_len(input_map_list); i++)
|
|
{
|
|
struct Input_Map* map = &input_map_list[i];
|
|
if(map->state == KS_RELEASED)
|
|
map->state = KS_INACTIVE;
|
|
}
|
|
}
|
|
|
|
bool input_map_remove(const char* name)
|
|
{
|
|
assert(name);
|
|
bool success = false;
|
|
int index = map_find(name);
|
|
if(index > -1)
|
|
{
|
|
array_remove_at(input_map_list, (int)index);
|
|
success = true;
|
|
}
|
|
if(!success) log_error("input:map_remove", "Map %s not found", name);
|
|
|
|
return success;
|
|
}
|
|
|
|
bool input_map_keys_set(const char* name, struct Key_Combination* keys, int num_keys)
|
|
{
|
|
assert(name && keys && num_keys > 0);
|
|
bool success = false;
|
|
int index = map_find(name);
|
|
if(index > -1)
|
|
{
|
|
struct Input_Map* map = &input_map_list[index];
|
|
if(array_len(map->keys) != num_keys)
|
|
array_reset(map->keys, num_keys);
|
|
for(int i = 0; i < num_keys; i++)
|
|
map->keys[i] = keys[i];
|
|
|
|
map->state = KS_INACTIVE;
|
|
success = true;
|
|
}
|
|
if(!success)
|
|
log_error("input:map_keys_set", "Map %s not found", name);
|
|
return success;
|
|
}
|
|
|
|
bool input_map_name_set(const char* name, const char* new_name)
|
|
{
|
|
assert(name && new_name);
|
|
bool success = false;
|
|
int index = map_find(name);
|
|
if(index > -1)
|
|
{
|
|
struct Input_Map* map = &input_map_list[index];
|
|
map->name = str_new(new_name);
|
|
success = true;
|
|
}
|
|
if(!success) log_error("input:map_name_set", "Map %s not found", name);
|
|
return success;
|
|
}
|
|
|
|
int map_find(const char* name)
|
|
{
|
|
int index = -1;
|
|
for(int i = 0; i < array_len(input_map_list); i++)
|
|
{
|
|
struct Input_Map* map = &input_map_list[i];
|
|
if(strcmp(name, map->name) == 0)
|
|
{
|
|
index = i;
|
|
break;
|
|
}
|
|
}
|
|
return index;
|
|
}
|
|
|
|
int input_mouse_mode_get(void)
|
|
{
|
|
int mouse_mode = MM_NORMAL;
|
|
if(platform->mouse_relative_mode_get()) mouse_mode = MM_RELATIVE;
|
|
return mouse_mode;
|
|
}
|
|
|
|
void input_mouse_delta_get(int* xpos, int* ypos)
|
|
{
|
|
platform->mouse_delta_get(xpos, ypos);
|
|
}
|
|
|
|
|