nestopia/source/gtkui/gtkui_input.cpp
2021-03-07 15:08:33 -05:00

389 lines
16 KiB
C++

/*
* Nestopia UE
*
* Copyright (C) 2012-2017 R. Danbrook
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*/
#include "nstcommon.h"
#include "input.h"
#include "video.h"
#include "audio.h"
#include "ini.h"
#include "sdlinput.h"
#include "gtkui.h"
#include "gtkui_input.h"
static ginputsettings_t inputconf;
static gkeys_t ui;
gpad_t pad[NUMGAMEPADS];
static char inputconfpath[256];
static bool confrunning = false;
static guint keyval = 0;
extern GtkWidget *configwindow;
extern nstpaths_t nstpaths;
extern Input::Controllers *cNstPads;
static int gtkui_input_config_match(void* user, const char* section, const char* name, const char* value) {
// Match values from input config file and populate live config
ginputsettings_t* pconfig = (ginputsettings_t*)user;
// User Interface
if (MATCH("ui", "qsave1")) { pconfig->qsave1 = strdup(value); }
else if (MATCH("ui", "qsave2")) { pconfig->qsave2 = strdup(value); }
else if (MATCH("ui", "qload1")) { pconfig->qload1 = strdup(value); }
else if (MATCH("ui", "qload2")) { pconfig->qload2 = strdup(value); }
else if (MATCH("ui", "screenshot")) { pconfig->screenshot = strdup(value); }
else if (MATCH("ui", "fdsflip")) { pconfig->fdsflip = strdup(value); }
else if (MATCH("ui", "fdsswitch")) { pconfig->fdsswitch = strdup(value); }
else if (MATCH("ui", "insertcoin1")) { pconfig->insertcoin1 = strdup(value); }
else if (MATCH("ui", "insertcoin2")) { pconfig->insertcoin2 = strdup(value); }
else if (MATCH("ui", "reset")) { pconfig->reset = strdup(value); }
else if (MATCH("ui", "ffspeed")) { pconfig->ffspeed = strdup(value); }
else if (MATCH("ui", "rwstart")) { pconfig->rwstart = strdup(value); }
else if (MATCH("ui", "rwstop")) { pconfig->rwstop = strdup(value); }
else if (MATCH("ui", "fullscreen")) { pconfig->fullscreen = strdup(value); }
else if (MATCH("ui", "filter")) { pconfig->filter = strdup(value); }
else if (MATCH("ui", "scalefactor")) { pconfig->scalefactor = strdup(value); }
// Player 1
else if (MATCH("gamepad1", "kb_u")) { pconfig->kb_p1u = strdup(value); }
else if (MATCH("gamepad1", "kb_d")) { pconfig->kb_p1d = strdup(value); }
else if (MATCH("gamepad1", "kb_l")) { pconfig->kb_p1l = strdup(value); }
else if (MATCH("gamepad1", "kb_r")) { pconfig->kb_p1r = strdup(value); }
else if (MATCH("gamepad1", "kb_select")) { pconfig->kb_p1select = strdup(value); }
else if (MATCH("gamepad1", "kb_start")) { pconfig->kb_p1start = strdup(value); }
else if (MATCH("gamepad1", "kb_a")) { pconfig->kb_p1a = strdup(value); }
else if (MATCH("gamepad1", "kb_b")) { pconfig->kb_p1b = strdup(value); }
else if (MATCH("gamepad1", "kb_ta")) { pconfig->kb_p1ta = strdup(value); }
else if (MATCH("gamepad1", "kb_tb")) { pconfig->kb_p1tb = strdup(value); }
// Player 2
else if (MATCH("gamepad2", "kb_u")) { pconfig->kb_p2u = strdup(value); }
else if (MATCH("gamepad2", "kb_d")) { pconfig->kb_p2d = strdup(value); }
else if (MATCH("gamepad2", "kb_l")) { pconfig->kb_p2l = strdup(value); }
else if (MATCH("gamepad2", "kb_r")) { pconfig->kb_p2r = strdup(value); }
else if (MATCH("gamepad2", "kb_select")) { pconfig->kb_p2select = strdup(value); }
else if (MATCH("gamepad2", "kb_start")) { pconfig->kb_p2start = strdup(value); }
else if (MATCH("gamepad2", "kb_a")) { pconfig->kb_p2a = strdup(value); }
else if (MATCH("gamepad2", "kb_b")) { pconfig->kb_p2b = strdup(value); }
else if (MATCH("gamepad2", "kb_ta")) { pconfig->kb_p2ta = strdup(value); }
else if (MATCH("gamepad2", "kb_tb")) { pconfig->kb_p2tb = strdup(value); }
else { return 0; }
return 1;
}
void gtkui_input_set_default() {
// Set the default input for GTK
// Gamepads
pad[0].u = GDK_KEY_Up;
pad[0].d = GDK_KEY_Down;
pad[0].l = GDK_KEY_Left;
pad[0].r = GDK_KEY_Right;
pad[0].select = GDK_KEY_Shift_R;
pad[0].start = GDK_KEY_Return;
pad[0].a = GDK_KEY_z;
pad[0].b = GDK_KEY_a;
pad[0].ta = GDK_KEY_x;
pad[0].tb = GDK_KEY_s;
pad[1].u = GDK_KEY_i;
pad[1].d = GDK_KEY_k;
pad[1].l = GDK_KEY_j;
pad[1].r = GDK_KEY_l;
pad[1].select = GDK_KEY_Shift_L;
pad[1].start = GDK_KEY_Control_L;
pad[1].a = GDK_KEY_m;
pad[1].b = GDK_KEY_n;
pad[1].ta = GDK_KEY_b;
pad[1].tb = GDK_KEY_v;
// User Interface
ui.qsave1 = GDK_KEY_F5;
ui.qsave2 = GDK_KEY_F6;
ui.qload1 = GDK_KEY_F7;
ui.qload2 = GDK_KEY_F8;
ui.screenshot = GDK_KEY_F9;
ui.fdsflip = GDK_KEY_F3;
ui.fdsswitch = GDK_KEY_F4;
ui.insertcoin1 = GDK_KEY_F1;
ui.insertcoin2 = GDK_KEY_F2;
ui.reset = GDK_KEY_F12;
ui.ffspeed = GDK_KEY_grave;
ui.rwstart = GDK_KEY_BackSpace;
ui.rwstop = GDK_KEY_backslash;
ui.fullscreen = GDK_KEY_f;
ui.filter = GDK_KEY_t;
ui.scalefactor = GDK_KEY_g;
}
void gtkui_input_config_read() {
// Read the input config file
snprintf(inputconfpath, sizeof(inputconfpath), "%sgtkinput.conf", nstpaths.nstconfdir);
if (ini_parse(inputconfpath, gtkui_input_config_match, &inputconf) < 0) {
fprintf(stderr, "Failed to load input config file %s: Using defaults.\n", inputconfpath);
}
else {
// Map the input settings from the config file
// User Interface
ui.qsave1 = gdk_keyval_from_name(inputconf.qsave1);
ui.qsave2 = gdk_keyval_from_name(inputconf.qsave2);
ui.qload1 = gdk_keyval_from_name(inputconf.qload1);
ui.qload2 = gdk_keyval_from_name(inputconf.qload2);
ui.screenshot = gdk_keyval_from_name(inputconf.screenshot);
ui.fdsflip = gdk_keyval_from_name(inputconf.fdsflip);
ui.fdsswitch = gdk_keyval_from_name(inputconf.fdsswitch);
ui.insertcoin1 = gdk_keyval_from_name(inputconf.insertcoin1);
ui.insertcoin2 = gdk_keyval_from_name(inputconf.insertcoin2);
ui.reset = gdk_keyval_from_name(inputconf.reset);
ui.ffspeed = gdk_keyval_from_name(inputconf.ffspeed);
ui.rwstart = gdk_keyval_from_name(inputconf.rwstart);
ui.rwstop = gdk_keyval_from_name(inputconf.rwstop);
ui.fullscreen = gdk_keyval_from_name(inputconf.fullscreen);
ui.filter = gdk_keyval_from_name(inputconf.filter);
ui.scalefactor = gdk_keyval_from_name(inputconf.scalefactor);
// Player 1
pad[0].u = gdk_keyval_from_name(inputconf.kb_p1u);
pad[0].d = gdk_keyval_from_name(inputconf.kb_p1d);
pad[0].l = gdk_keyval_from_name(inputconf.kb_p1l);
pad[0].r = gdk_keyval_from_name(inputconf.kb_p1r);
pad[0].select = gdk_keyval_from_name(inputconf.kb_p1select);
pad[0].start = gdk_keyval_from_name(inputconf.kb_p1start);
pad[0].a = gdk_keyval_from_name(inputconf.kb_p1a);
pad[0].b = gdk_keyval_from_name(inputconf.kb_p1b);
pad[0].ta = gdk_keyval_from_name(inputconf.kb_p1ta);
pad[0].tb = gdk_keyval_from_name(inputconf.kb_p1tb);
// Player 2
pad[1].u = gdk_keyval_from_name(inputconf.kb_p2u);
pad[1].d = gdk_keyval_from_name(inputconf.kb_p2d);
pad[1].l = gdk_keyval_from_name(inputconf.kb_p2l);
pad[1].r = gdk_keyval_from_name(inputconf.kb_p2r);
pad[1].select = gdk_keyval_from_name(inputconf.kb_p2select);
pad[1].start = gdk_keyval_from_name(inputconf.kb_p2start);
pad[1].a = gdk_keyval_from_name(inputconf.kb_p2a);
pad[1].b = gdk_keyval_from_name(inputconf.kb_p2b);
pad[1].ta = gdk_keyval_from_name(inputconf.kb_p2ta);
pad[1].tb = gdk_keyval_from_name(inputconf.kb_p2tb);
}
}
void gtkui_input_config_write() {
// Write out the input configuration file
FILE *fp = fopen(inputconfpath, "w");
if (fp != NULL) {
fprintf(fp, "; Nestopia UE GTK Input Configuration File\n\n");
fprintf(fp, "; Values for keyboard input are these values with the GDK_KEY_ prefix removed:\n; https://git.gnome.org/browse/gtk+/plain/gdk/gdkkeysyms.h\n\n");
fprintf(fp, "[ui]\n");
fprintf(fp, "qsave1=%s\n", gdk_keyval_name(ui.qsave1));
fprintf(fp, "qsave2=%s\n", gdk_keyval_name(ui.qsave2));
fprintf(fp, "qload1=%s\n", gdk_keyval_name(ui.qload1));
fprintf(fp, "qload2=%s\n", gdk_keyval_name(ui.qload2));
fprintf(fp, "screenshot=%s\n", gdk_keyval_name(ui.screenshot));
fprintf(fp, "fdsflip=%s\n", gdk_keyval_name(ui.fdsflip));
fprintf(fp, "fdsswitch=%s\n", gdk_keyval_name(ui.fdsswitch));
fprintf(fp, "insertcoin1=%s\n", gdk_keyval_name(ui.insertcoin1));
fprintf(fp, "insertcoin2=%s\n", gdk_keyval_name(ui.insertcoin2));
fprintf(fp, "reset=%s\n", gdk_keyval_name(ui.reset));
fprintf(fp, "ffspeed=%s\n", gdk_keyval_name(ui.ffspeed));
fprintf(fp, "rwstart=%s\n", gdk_keyval_name(ui.rwstart));
fprintf(fp, "rwstop=%s\n", gdk_keyval_name(ui.rwstop));
fprintf(fp, "fullscreen=%s\n", gdk_keyval_name(ui.fullscreen));
fprintf(fp, "filter=%s\n", gdk_keyval_name(ui.filter));
fprintf(fp, "scalefactor=%s\n", gdk_keyval_name(ui.scalefactor));
fprintf(fp, "\n"); // End of Section
fprintf(fp, "[gamepad1]\n");
fprintf(fp, "kb_u=%s\n", gdk_keyval_name(pad[0].u));
fprintf(fp, "kb_d=%s\n", gdk_keyval_name(pad[0].d));
fprintf(fp, "kb_l=%s\n", gdk_keyval_name(pad[0].l));
fprintf(fp, "kb_r=%s\n", gdk_keyval_name(pad[0].r));
fprintf(fp, "kb_select=%s\n", gdk_keyval_name(pad[0].select));
fprintf(fp, "kb_start=%s\n", gdk_keyval_name(pad[0].start));
fprintf(fp, "kb_a=%s\n", gdk_keyval_name(pad[0].a));
fprintf(fp, "kb_b=%s\n", gdk_keyval_name(pad[0].b));
fprintf(fp, "kb_ta=%s\n", gdk_keyval_name(pad[0].ta));
fprintf(fp, "kb_tb=%s\n", gdk_keyval_name(pad[0].tb));
fprintf(fp, "\n"); // End of Section
fprintf(fp, "[gamepad2]\n");
fprintf(fp, "kb_u=%s\n", gdk_keyval_name(pad[1].u));
fprintf(fp, "kb_d=%s\n", gdk_keyval_name(pad[1].d));
fprintf(fp, "kb_l=%s\n", gdk_keyval_name(pad[1].l));
fprintf(fp, "kb_r=%s\n", gdk_keyval_name(pad[1].r));
fprintf(fp, "kb_select=%s\n", gdk_keyval_name(pad[1].select));
fprintf(fp, "kb_start=%s\n", gdk_keyval_name(pad[1].start));
fprintf(fp, "kb_a=%s\n", gdk_keyval_name(pad[1].a));
fprintf(fp, "kb_b=%s\n", gdk_keyval_name(pad[1].b));
fprintf(fp, "kb_ta=%s\n", gdk_keyval_name(pad[1].ta));
fprintf(fp, "kb_tb=%s\n", gdk_keyval_name(pad[1].tb));
fclose(fp);
}
}
void gtkui_input_config_process_key(GtkWidget *widget, GdkEventKey *event, gpointer userdata) {
keyval = event->keyval;
if (keyval == GDK_KEY_Escape || keyval == GDK_KEY_space) { keyval = 0; }
confrunning = false;
}
void gtkui_input_config_signals_init() {
// Key translation
g_signal_connect(G_OBJECT(configwindow), "key-press-event",
G_CALLBACK(gtkui_input_config_process_key), NULL);
g_signal_connect(G_OBJECT(configwindow), "key-release-event",
G_CALLBACK(gtkui_input_config_process_key), NULL);
}
void gtkui_input_config_signals_deinit() {
// Key translation
g_signal_connect(G_OBJECT(configwindow), "key-press-event",
gtkui_input_null, NULL);
g_signal_connect(G_OBJECT(configwindow), "key-release-event",
gtkui_input_null, NULL);
}
void gtkui_input_config_key(int pnum, int bnum) {
// Connect signals
gtkui_input_config_signals_init();
// Wait for input
confrunning = true;
while (confrunning) { gtk_main_iteration(); }
// Set the keyval for the input item
if (keyval != 0) {
switch (bnum) {
case 0: pad[pnum].u = keyval; break;
case 1: pad[pnum].d = keyval; break;
case 2: pad[pnum].l = keyval; break;
case 3: pad[pnum].r = keyval; break;
case 4: pad[pnum].select = keyval; break;
case 5: pad[pnum].start = keyval; break;
case 6: pad[pnum].a = keyval; break;
case 7: pad[pnum].b = keyval; break;
case 8: pad[pnum].ta = keyval; break;
case 9: pad[pnum].tb = keyval; break;
default: break;
}
}
// Disconnect signals
gtkui_input_config_signals_deinit();
}
void gtkui_input_config_js(int pnum, int bnum) {
// Wait for input
nstsdl_input_conf_button(pnum, bnum);
}
void gtkui_input_null() {}
int gtkui_input_process_key(GtkWidget *widget, GdkEventKey *event, gpointer userdata) {
// Process input from GDK events
nesinput_t input;
input.nescode = input.player = input.pressed = input.turboa = input.turbob = 0;
for (int i = 0; i < NUMGAMEPADS; i++) {
if (event->keyval == pad[i].u) { input.player = i; input.nescode = Input::Controllers::Pad::UP; }
else if (event->keyval == pad[i].d) { input.player = i; input.nescode = Input::Controllers::Pad::DOWN; }
else if (event->keyval == pad[i].l) { input.player = i; input.nescode = Input::Controllers::Pad::LEFT; }
else if (event->keyval == pad[i].r) { input.player = i; input.nescode = Input::Controllers::Pad::RIGHT; }
else if (event->keyval == pad[i].select) { input.player = i; input.nescode = Input::Controllers::Pad::SELECT; }
else if (event->keyval == pad[i].start) { input.player = i; input.nescode = Input::Controllers::Pad::START; }
else if (event->keyval == pad[i].a) { input.player = i; input.nescode = Input::Controllers::Pad::A; }
else if (event->keyval == pad[i].b) { input.player = i; input.nescode = Input::Controllers::Pad::B; }
else if (event->keyval == pad[i].ta) { input.player = i; input.turboa = 1; input.nescode = Input::Controllers::Pad::A; }
else if (event->keyval == pad[i].tb) { input.player = i; input.turbob = 1; input.nescode = Input::Controllers::Pad::B; }
}
switch(event->type) {
case GDK_KEY_PRESS:
//printf("Keyval: %x\n", event->keyval);
//printf("Keyval: %s\n", gdk_keyval_name(event->keyval));
input.pressed = 1;
if (event->keyval == ui.qsave1) { nst_state_quicksave(0); }
else if (event->keyval == ui.qsave2) { nst_state_quicksave(1); }
else if (event->keyval == ui.qload1) { nst_state_quickload(0); }
else if (event->keyval == ui.qload2) { nst_state_quickload(1); }
else if (event->keyval == ui.screenshot) { video_screenshot(NULL); }
else if (event->keyval == ui.fdsflip) { nst_fds_flip(); }
else if (event->keyval == ui.fdsswitch) { nst_fds_switch(); }
else if (event->keyval == ui.insertcoin1) { cNstPads->vsSystem.insertCoin |= Input::Controllers::VsSystem::COIN_1; }
else if (event->keyval == ui.insertcoin2) { cNstPads->vsSystem.insertCoin |= Input::Controllers::VsSystem::COIN_2; }
else if (event->keyval == ui.reset) { nst_reset(0); }
else if (event->keyval == ui.ffspeed) { nst_timing_set_ffspeed(); }
else if (event->keyval == ui.rwstart) { nst_set_rewind(0); }
else if (event->keyval == ui.rwstop) { nst_set_rewind(1); }
else if (event->keyval == ui.filter) { gtkui_video_toggle_filter(); }
else if (event->keyval == ui.scalefactor) { gtkui_video_toggle_scale(); }
else if (event->keyval == gdk_keyval_from_name("space")) { cNstPads->pad[1].mic = 0x04; }
break;
case GDK_KEY_RELEASE:
input.pressed = 0;
if (event->keyval == ui.ffspeed) { nst_timing_set_default(); }
else if (event->keyval == ui.fullscreen) { gtkui_video_toggle_fullscreen(); }
else if (event->keyval == gdk_keyval_from_name("space")) { cNstPads->pad[1].mic = 0x00; }
break;
default: break;
}
nst_input_inject(cNstPads, input);
return TRUE;
}
int gtkui_input_process_key_nsf(GtkWidget *widget, GdkEventKey *event, gpointer userdata) {
if (event->type == GDK_KEY_RELEASE) {
if (event->keyval == GDK_KEY_Up) { nst_nsf_play(); }
if (event->keyval == GDK_KEY_Down) { nst_nsf_stop(); }
if (event->keyval == GDK_KEY_Left) { nst_nsf_prev(); }
if (event->keyval == GDK_KEY_Right) { nst_nsf_next(); }
}
return TRUE;
}
int gtkui_input_process_mouse(GtkWidget *widget, GdkEventButton *event, gpointer userdata) {
switch(event->type) {
case GDK_BUTTON_PRESS:
nst_input_inject_mouse(cNstPads, event->button, 1, (int)event->x, (int)event->y);
break;
case GDK_BUTTON_RELEASE:
nst_input_inject_mouse(cNstPads, event->button, 0, (int)event->x, (int)event->y);
break;
default: break;
}
return TRUE;
}