From 91f83d4c555f28fe3c16c967b97e9c5aa9e4f67c Mon Sep 17 00:00:00 2001 From: Christoph Oelckers <coelckers@users.noreply.github.com> Date: Sat, 14 Dec 2019 17:15:17 +0100 Subject: [PATCH] - input code cleanup and addition of ZDoom's joystick code, which is not fully connected yet. --- source/CMakeLists.txt | 6 + source/build/src/sdlayer.cpp | 557 +-------------------- source/build/src/sdlkeytrans.cpp | 263 ---------- source/common/gameconfigfile.cpp | 1 - source/common/gamecontrol.cpp | 285 +---------- source/common/gamecontrol.h | 1 - source/common/gamecvars.cpp | 5 +- source/common/input/i_gui.cpp | 87 ++++ source/common/input/i_input.cpp | 750 +++++++++++++++++++++++++++++ source/common/input/i_joystick.cpp | 342 +++++++++++++ source/common/input/m_joy.cpp | 331 +++++++++++++ source/common/input/m_joy.h | 65 +++ source/common/inputstate.cpp | 2 + source/common/inputstate.h | 2 + source/mact/src/control.cpp | 6 +- 15 files changed, 1611 insertions(+), 1092 deletions(-) delete mode 100644 source/build/src/sdlkeytrans.cpp create mode 100644 source/common/input/i_gui.cpp create mode 100644 source/common/input/i_input.cpp create mode 100644 source/common/input/i_joystick.cpp create mode 100644 source/common/input/m_joy.cpp create mode 100644 source/common/input/m_joy.h diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index 676b71140..e0a045216 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -624,6 +624,7 @@ file( GLOB HEADER_FILES common/filesystem/*.h common/music/*.h common/menu/*.h + common/input/*.h build/src/*.h thirdparty/include/*.h @@ -837,6 +838,11 @@ set (PCH_SOURCES common/menu/menuinput.cpp common/menu/messagebox.cpp common/menu/optionmenu.cpp + + common/input/i_gui.cpp + common/input/i_joystick.cpp + common/input/i_input.cpp + common/input/m_joy.cpp ) if( MSVC ) diff --git a/source/build/src/sdlayer.cpp b/source/build/src/sdlayer.cpp index 9cc9992d9..16f27e654 100644 --- a/source/build/src/sdlayer.cpp +++ b/source/build/src/sdlayer.cpp @@ -78,6 +78,8 @@ double g_beforeSwapTime; GameInterface* gi; FArgs* Args; +void buildkeytranslationtable();; + #if !defined STARTUP_SETUP_WINDOW int32_t startwin_open(void) { return 0; } int32_t startwin_close(void) { return 0; } @@ -143,7 +145,6 @@ static int32_t vsync_renderlayer; //#define KEY_PRINT_DEBUG -#include "sdlkeytrans.cpp" static SDL_Surface *appicon = NULL; #if !defined __APPLE__ && !defined EDUKE32_TOUCH_DEVICES @@ -377,7 +378,7 @@ void wm_setapptitle(const char *name) # include <execinfo.h> #endif -static inline char grabmouse_low(char a); +char grabmouse_low(char a); #ifndef __ANDROID__ static void attach_debugger_here(void) {} @@ -924,57 +925,6 @@ const char *joyGetName(int32_t what, int32_t num) } -// -// initmouse() -- init mouse input -// -void mouseInit(void) -{ - mouseGrabInput(g_mouseEnabled = g_mouseLockedToWindow); // FIXME - SA -} - -// -// uninitmouse() -- uninit mouse input -// -void mouseUninit(void) -{ - mouseGrabInput(0); - g_mouseEnabled = 0; -} - - -// -// grabmouse_low() -- show/hide mouse cursor, lower level (doesn't check state). -// furthermore return 0 if successful. -// - -static inline char grabmouse_low(char a) -{ - /* FIXME: Maybe it's better to make sure that grabmouse_low - is called only when a window is ready? */ - if (sdl_window) - SDL_SetWindowGrab(sdl_window, a ? SDL_TRUE : SDL_FALSE); - return SDL_SetRelativeMouseMode(a ? SDL_TRUE : SDL_FALSE); -} - -// -// grabmouse() -- show/hide mouse cursor -// -void mouseGrabInput(bool grab) -{ - if (appactive && g_mouseEnabled) - { - if ((grab != g_mouseGrabbed) && !grabmouse_low(grab)) - g_mouseGrabbed = grab; - } - else - g_mouseGrabbed = grab; - - inputState.MouseSetPos(0, 0); - SDL_ShowCursor(!grab ? SDL_ENABLE : SDL_DISABLE); - if (grab) GUICapture &= ~1; - else GUICapture |= 1; -} - // // setjoydeadzone() -- sets the dead and saturation zones for the joystick // @@ -1026,6 +976,20 @@ static int sortmodes(const void *a_, const void *b_) static char modeschecked=0; +void WindowMoved(int x, int y) +{ + if (windowpos) + { + windowx = x; + windowy = y; + } + + r_displayindex = SDL_GetWindowDisplayIndex(sdl_window); + modeschecked = 0; + videoGetModes(); +} + + #if SDL_MAJOR_VERSION != 1 void videoGetModes(void) { @@ -1606,493 +1570,6 @@ static inline SDL_Surface *loadappicon(void) // // -int32_t handleevents_peekkeys(void) -{ - SDL_PumpEvents(); - - return SDL_PeepEvents(NULL, 1, SDL_PEEKEVENT, SDL_KEYDOWN, SDL_KEYDOWN); -} - -static void PostMouseMove(int x, int y) -{ - static int lastx = 0, lasty = 0; - event_t ev = { 0,0,0,0,0,0,0 }; - - ev.x = x; - ev.y = y; - lastx = x; - lasty = y; - if (ev.x | ev.y) - { - ev.type = EV_Mouse; - D_PostEvent(&ev); - } -} - -CVAR(Bool, m_noprescale, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) - -static void MouseRead() -{ - int x, y; - -#if 0 - if (NativeMouse) - { - return; - } -#endif - - SDL_GetRelativeMouseState(&x, &y); - if (!m_noprescale) - { - x *= 3; - y *= 2; - } - if (x | y) - { - PostMouseMove(x, -y); - } -} - - -// -// handleevents() -- process the SDL message queue -// returns !0 if there was an important event worth checking (like quitting) -// - -int32_t handleevents_sdlcommon(SDL_Event *ev) -{ - switch (ev->type) - { - case SDL_MOUSEMOTION: - //case SDL_JOYBALLMOTION: - { - // The menus need this, even in non GUI-capture mode - event_t event; - event.data1 = ev->motion.x; - event.data2 = ev->motion.y; - - //screen->ScaleCoordsFromWindow(event.data1, event.data2); - - event.type = EV_GUI_Event; - event.subtype = EV_GUI_MouseMove; - - SDL_Keymod kmod = SDL_GetModState(); - event.data3 = ((kmod & KMOD_SHIFT) ? GKM_SHIFT : 0) | - ((kmod & KMOD_CTRL) ? GKM_CTRL : 0) | - ((kmod & KMOD_ALT) ? GKM_ALT : 0); - - D_PostEvent(&event); - break; - } - - case SDL_MOUSEBUTTONDOWN: - case SDL_MOUSEBUTTONUP: - { - int32_t j; - - // some of these get reordered to match winlayer - switch (ev->button.button) - { - default: j = -1; break; - case SDL_BUTTON_LEFT: j = KEY_MOUSE1; break; - case SDL_BUTTON_RIGHT: j = KEY_MOUSE2; break; - case SDL_BUTTON_MIDDLE: j = KEY_MOUSE3; break; - - /* Thumb buttons. */ - // On SDL2/Windows and SDL >= 2.0.?/Linux, everything is as it should be. - // If anyone cares about old versions of SDL2 on Linux, patches welcome. - case SDL_BUTTON_X1: j = KEY_MOUSE4; break; - case SDL_BUTTON_X2: j = KEY_MOUSE5; break; - } - - if (j < 0) - break; - - if (!(GUICapture & 1)) - { - event_t evt = { uint8_t((ev->button.state == SDL_PRESSED) ? EV_KeyDown : EV_KeyUp), 0, (int16_t)j }; - D_PostEvent(&evt); - } - else - { - event_t evt; - evt.type = EV_GUI_Event; - evt.subtype = uint8_t((ev->button.state == SDL_PRESSED) ? EV_GUI_LButtonDown : EV_GUI_LButtonUp); - evt.data1 = ev->motion.x; - evt.data2 = ev->motion.y; - - SDL_Keymod kmod = SDL_GetModState(); - evt.data3 = ((kmod & KMOD_SHIFT) ? GKM_SHIFT : 0) | - ((kmod & KMOD_CTRL) ? GKM_CTRL : 0) | - ((kmod & KMOD_ALT) ? GKM_ALT : 0); - - D_PostEvent(&evt); - - } - break; - } - - case SDL_JOYAXISMOTION: -#if SDL_MAJOR_VERSION >= 2 - if (joystick.isGameController) - break; - fallthrough__; - case SDL_CONTROLLERAXISMOTION: -#endif - if (appactive && ev->jaxis.axis < joystick.numAxes) - { - joystick.pAxis[ev->jaxis.axis] = ev->jaxis.value; - int32_t const scaledValue = ev->jaxis.value * 10000 / 32767; - if ((scaledValue < joydead[ev->jaxis.axis]) && - (scaledValue > -joydead[ev->jaxis.axis])) - joystick.pAxis[ev->jaxis.axis] = 0; - else if (scaledValue >= joysatur[ev->jaxis.axis]) - joystick.pAxis[ev->jaxis.axis] = 32767; - else if (scaledValue <= -joysatur[ev->jaxis.axis]) - joystick.pAxis[ev->jaxis.axis] = -32767; - else - joystick.pAxis[ev->jaxis.axis] = joystick.pAxis[ev->jaxis.axis] * 10000 / joysatur[ev->jaxis.axis]; - } - break; - - case SDL_JOYHATMOTION: - { - int32_t hatvals[16] = { - -1, // centre - 0, // up 1 - 9000, // right 2 - 4500, // up+right 3 - 18000, // down 4 - -1, // down+up!! 5 - 13500, // down+right 6 - -1, // down+right+up!! 7 - 27000, // left 8 - 27500, // left+up 9 - -1, // left+right!! 10 - -1, // left+right+up!! 11 - 22500, // left+down 12 - -1, // left+down+up!! 13 - -1, // left+down+right!! 14 - -1, // left+down+right+up!! 15 - }; - if (appactive && ev->jhat.hat < joystick.numHats) - joystick.pHat[ev->jhat.hat] = hatvals[ev->jhat.value & 15]; - break; - } - - case SDL_JOYBUTTONDOWN: - case SDL_JOYBUTTONUP: -#if SDL_MAJOR_VERSION >= 2 - if (joystick.isGameController) - break; - fallthrough__; - case SDL_CONTROLLERBUTTONDOWN: - case SDL_CONTROLLERBUTTONUP: -#endif - if (appactive && ev->jbutton.button < joystick.numButtons) - { - if (ev->jbutton.state == SDL_PRESSED) - joystick.bits |= 1 << ev->jbutton.button; - else - joystick.bits &= ~(1 << ev->jbutton.button); - - } - break; - - case SDL_QUIT: - throw ExitEvent(0); // completely bypass the hackery in the games to block Alt-F4. - return -1; - } - - return 0; -} - -// So this is how the engine handles text input? -// Argh. This is just gross. -int scancodetoasciihack(SDL_Event &ev) -{ - int sc = ev.key.keysym.scancode; - SDL_Keycode keyvalue = ev.key.keysym.sym; - int code = keytranslation[sc]; - // Modifiers that have to be held down to be effective - // (excludes KMOD_NUM, for example). - static const int MODIFIERS = - KMOD_LSHIFT|KMOD_RSHIFT|KMOD_LCTRL|KMOD_RCTRL| - KMOD_LALT|KMOD_RALT|KMOD_LGUI|KMOD_RGUI; - - // XXX: see osd.c, OSD_HandleChar(), there are more... - if ( - (sc == SDL_SCANCODE_RETURN || sc == SDL_SCANCODE_KP_ENTER || - sc == SDL_SCANCODE_ESCAPE || - sc == SDL_SCANCODE_BACKSPACE || - sc == SDL_SCANCODE_TAB || - (((ev.key.keysym.mod) & MODIFIERS) == KMOD_LCTRL && - (sc >= SDL_SCANCODE_A && sc <= SDL_SCANCODE_Z)))) - { - char keyvalue; - switch (sc) - { - case SDL_SCANCODE_RETURN: case SDL_SCANCODE_KP_ENTER: keyvalue = '\r'; break; - case SDL_SCANCODE_ESCAPE: keyvalue = 27; break; - case SDL_SCANCODE_BACKSPACE: keyvalue = '\b'; break; - case SDL_SCANCODE_TAB: keyvalue = '\t'; break; - default: keyvalue = sc - SDL_SCANCODE_A + 1; break; // Ctrl+A --> 1, etc. - } - } - else - { - /* - Necessary for Duke 3D's method of entering cheats to work without showing IMEs. - SDL_TEXTINPUT is preferable overall, but with bitmap fonts it has no advantage. - */ - // Note that this is not how text input is supposed to be handled! - - if ('a' <= keyvalue && keyvalue <= 'z') - { - if (!!(ev.key.keysym.mod & KMOD_SHIFT) ^ !!(ev.key.keysym.mod & KMOD_CAPS)) - keyvalue -= 'a'-'A'; - } - else if (ev.key.keysym.mod & KMOD_SHIFT) - { - keyvalue = g_keyAsciiTableShift[code]; - } - else if (ev.key.keysym.mod & KMOD_NUM) // && !(ev.key.keysym.mod & KMOD_SHIFT) - { - switch (keyvalue) - { - case SDLK_KP_1: keyvalue = '1'; break; - case SDLK_KP_2: keyvalue = '2'; break; - case SDLK_KP_3: keyvalue = '3'; break; - case SDLK_KP_4: keyvalue = '4'; break; - case SDLK_KP_5: keyvalue = '5'; break; - case SDLK_KP_6: keyvalue = '6'; break; - case SDLK_KP_7: keyvalue = '7'; break; - case SDLK_KP_8: keyvalue = '8'; break; - case SDLK_KP_9: keyvalue = '9'; break; - case SDLK_KP_0: keyvalue = '0'; break; - case SDLK_KP_PERIOD: keyvalue = '.'; break; - case SDLK_KP_COMMA: keyvalue = ','; break; - } - } - - switch (keyvalue) - { - case SDLK_KP_DIVIDE: keyvalue = '/'; break; - case SDLK_KP_MULTIPLY: keyvalue = '*'; break; - case SDLK_KP_MINUS: keyvalue = '-'; break; - case SDLK_KP_PLUS: keyvalue = '+'; break; - } - } - if (keyvalue >= 0x80) keyvalue = 0; // Sadly ASCII only... - return keyvalue; -} - -int32_t handleevents_pollsdl(void); -#if SDL_MAJOR_VERSION != 1 -// SDL 2.0 specific event handling -int32_t handleevents_pollsdl(void) -{ - int32_t code, rv=0, j; - SDL_Event ev; - - while (SDL_PollEvent(&ev)) - { - if ((GUICapture & 10) == 2) - { - if (ImGui_ImplSDL2_ProcessEvent(&ev)) return 0; - } - switch (ev.type) - { - case SDL_TEXTINPUT: - { - j = 0; - const uint8_t* text = (uint8_t*)ev.text.text; - while ((j = GetCharFromString(text))) - { - code = ev.text.text[j]; - // Fixme: Send an EV_GUI_Event instead and properly deal with Unicode. - if ((GUICapture & 1) && menuactive != MENU_WaitKey) - { - event_t ev = { EV_GUI_Event, EV_GUI_Char, int16_t(j), !!(SDL_GetModState() & KMOD_ALT) }; - D_PostEvent(&ev); - } - } - break; - } - - case SDL_KEYDOWN: - case SDL_KEYUP: - { - if ((GUICapture & 1) && menuactive != MENU_WaitKey) - { - event_t event = {}; - event.type = EV_GUI_Event; - event.subtype = ev.type == SDL_KEYDOWN ? EV_GUI_KeyDown : EV_GUI_KeyUp; - SDL_Keymod kmod = SDL_GetModState(); - event.data3 = ((kmod & KMOD_SHIFT) ? GKM_SHIFT : 0) | - ((kmod & KMOD_CTRL) ? GKM_CTRL : 0) | - ((kmod & KMOD_ALT) ? GKM_ALT : 0); - - if (event.subtype == EV_GUI_KeyDown && ev.key.repeat) - { - event.subtype = EV_GUI_KeyRepeat; - } - - switch (ev.key.keysym.sym) - { - case SDLK_KP_ENTER: event.data1 = GK_RETURN; break; - case SDLK_PAGEUP: event.data1 = GK_PGUP; break; - case SDLK_PAGEDOWN: event.data1 = GK_PGDN; break; - case SDLK_END: event.data1 = GK_END; break; - case SDLK_HOME: event.data1 = GK_HOME; break; - case SDLK_LEFT: event.data1 = GK_LEFT; break; - case SDLK_RIGHT: event.data1 = GK_RIGHT; break; - case SDLK_UP: event.data1 = GK_UP; break; - case SDLK_DOWN: event.data1 = GK_DOWN; break; - case SDLK_DELETE: event.data1 = GK_DEL; break; - case SDLK_ESCAPE: event.data1 = GK_ESCAPE; break; - case SDLK_F1: event.data1 = GK_F1; break; - case SDLK_F2: event.data1 = GK_F2; break; - case SDLK_F3: event.data1 = GK_F3; break; - case SDLK_F4: event.data1 = GK_F4; break; - case SDLK_F5: event.data1 = GK_F5; break; - case SDLK_F6: event.data1 = GK_F6; break; - case SDLK_F7: event.data1 = GK_F7; break; - case SDLK_F8: event.data1 = GK_F8; break; - case SDLK_F9: event.data1 = GK_F9; break; - case SDLK_F10: event.data1 = GK_F10; break; - case SDLK_F11: event.data1 = GK_F11; break; - case SDLK_F12: event.data1 = GK_F12; break; - default: - if (ev.key.keysym.sym < 256) - { - event.data1 = ev.key.keysym.sym; - } - break; - } - if (event.data1 < 128) - { - event.data1 = toupper(event.data1); - D_PostEvent(&event); - } - } - else - { - auto const& sc = ev.key.keysym.scancode; - code = keytranslation[sc]; - - // The pause key generates a release event right after - // the pressing one. As a result, it gets unseen - // by the game most of the time. - if (code == 0x59 && ev.type == SDL_KEYUP) // pause - break; - - int keyvalue = ev.type == SDL_KEYDOWN ? scancodetoasciihack(ev) : 0; - event_t evt = { (uint8_t)(ev.type == SDL_KEYUP ? EV_KeyUp : EV_KeyDown), 0, (int16_t)code, (int16_t)keyvalue }; - D_PostEvent(&evt); - } - - break; - } - - case SDL_MOUSEWHEEL: - // initprintf("wheel y %d\n",ev.wheel.y); - - // This never sends keyup events. For the current code that should suffice - if (ev.wheel.y > 0) - { - event_t evt = { EV_KeyDown, 0, (int16_t)KEY_MWHEELUP }; - D_PostEvent(&evt); - } - if (ev.wheel.y < 0) - { - event_t evt = { EV_KeyDown, 0, (int16_t)KEY_MWHEELDOWN }; - D_PostEvent(&evt); - } - break; - - case SDL_WINDOWEVENT: - switch (ev.window.event) - { - case SDL_WINDOWEVENT_FOCUS_GAINED: - case SDL_WINDOWEVENT_FOCUS_LOST: - appactive = (ev.window.event == SDL_WINDOWEVENT_FOCUS_GAINED); - if (g_mouseGrabbed && g_mouseEnabled) - grabmouse_low(appactive); - break; - - case SDL_WINDOWEVENT_MOVED: - { - if (windowpos) - { - windowx = ev.window.data1; - windowy = ev.window.data2; - } - - r_displayindex = SDL_GetWindowDisplayIndex(sdl_window); - modeschecked = 0; - videoGetModes(); - break; - } - case SDL_WINDOWEVENT_ENTER: - g_mouseInsideWindow = 1; - break; - case SDL_WINDOWEVENT_LEAVE: - g_mouseInsideWindow = 0; - break; - } - - break; - - default: - rv = handleevents_sdlcommon(&ev); - break; - } - } - MouseRead(); - - return rv; -} -#endif - -int32_t handleevents(void) -{ - int32_t rv; - - if (inputchecked && g_mouseEnabled) - { - // This is a horrible crutch - if (inputState.mouseReadButtons() & WHEELUP_MOUSE) - { - event_t ev = { EV_KeyUp, 0, (int16_t)KEY_MWHEELUP }; - D_PostEvent(&ev); - } - if (inputState.mouseReadButtons() & WHEELDOWN_MOUSE) - { - event_t ev = { EV_KeyUp, 0, (int16_t)KEY_MWHEELDOWN }; - D_PostEvent(&ev); - } - } - - rv = handleevents_pollsdl(); - - inputchecked = 0; - timerUpdateClock(); - return rv; -} - -void I_SetMouseCapture() -{ - // Clear out any mouse movement. - SDL_CaptureMouse(SDL_TRUE); -} - -void I_ReleaseMouseCapture() -{ - SDL_CaptureMouse(SDL_FALSE); -} - auto vsnprintfptr = vsnprintf; // This is an inline in Visual Studio but we need an address for it to satisfy the MinGW compiled libraries. // diff --git a/source/build/src/sdlkeytrans.cpp b/source/build/src/sdlkeytrans.cpp deleted file mode 100644 index 9cdd18ce7..000000000 --- a/source/build/src/sdlkeytrans.cpp +++ /dev/null @@ -1,263 +0,0 @@ - -#if (SDL_MAJOR_VERSION == 1 && SDL_MINOR_VERSION < 3) -static uint8_t keytranslation[SDLK_LAST]; -#else -static uint8_t keytranslation[SDL_NUM_SCANCODES]; -#endif -static int32_t buildkeytranslationtable(void); - -#if (SDL_MAJOR_VERSION == 1 && SDL_MINOR_VERSION < 3) // SDL 1.2 -static int32_t buildkeytranslationtable(void) -{ - memset(keytranslation,0,sizeof(keytranslation)); - -#define MAP(x,y) keytranslation[x] = y - MAP(SDLK_BACKSPACE, 0xe); - MAP(SDLK_TAB, 0xf); - MAP(SDLK_RETURN, 0x1c); - MAP(SDLK_PAUSE, 0x59); // 0x1d + 0x45 + 0x9d + 0xc5 - MAP(SDLK_ESCAPE, 0x1); - MAP(SDLK_SPACE, 0x39); - MAP(SDLK_EXCLAIM, 0x2); // '1' - MAP(SDLK_QUOTEDBL, 0x28); // ''' - MAP(SDLK_HASH, 0x4); // '3' - MAP(SDLK_DOLLAR, 0x5); // '4' - MAP(37, 0x6); // '5' <-- where's the keysym SDL guys? - MAP(SDLK_AMPERSAND, 0x8); // '7' - MAP(SDLK_QUOTE, 0x28); // ''' - MAP(SDLK_LEFTPAREN, 0xa); // '9' - MAP(SDLK_RIGHTPAREN, 0xb); // '0' - MAP(SDLK_ASTERISK, 0x9); // '8' - MAP(SDLK_PLUS, 0xd); // '=' - MAP(SDLK_COMMA, 0x33); - MAP(SDLK_MINUS, 0xc); - MAP(SDLK_PERIOD, 0x34); - MAP(SDLK_SLASH, 0x35); - MAP(SDLK_0, 0xb); - MAP(SDLK_1, 0x2); - MAP(SDLK_2, 0x3); - MAP(SDLK_3, 0x4); - MAP(SDLK_4, 0x5); - MAP(SDLK_5, 0x6); - MAP(SDLK_6, 0x7); - MAP(SDLK_7, 0x8); - MAP(SDLK_8, 0x9); - MAP(SDLK_9, 0xa); - MAP(SDLK_COLON, 0x27); - MAP(SDLK_SEMICOLON, 0x27); - MAP(SDLK_LESS, 0x56); - MAP(SDLK_EQUALS, 0xd); - MAP(SDLK_GREATER, 0x34); - MAP(SDLK_QUESTION, 0x35); - MAP(SDLK_AT, 0x3); // '2' - MAP(SDLK_LEFTBRACKET, 0x1a); - MAP(SDLK_BACKSLASH, 0x2b); - MAP(SDLK_RIGHTBRACKET, 0x1b); - MAP(SDLK_CARET, 0x7); // '7' - MAP(SDLK_UNDERSCORE, 0xc); - MAP(SDLK_BACKQUOTE, 0x29); - MAP(SDLK_a, 0x1e); - MAP(SDLK_b, 0x30); - MAP(SDLK_c, 0x2e); - MAP(SDLK_d, 0x20); - MAP(SDLK_e, 0x12); - MAP(SDLK_f, 0x21); - MAP(SDLK_g, 0x22); - MAP(SDLK_h, 0x23); - MAP(SDLK_i, 0x17); - MAP(SDLK_j, 0x24); - MAP(SDLK_k, 0x25); - MAP(SDLK_l, 0x26); - MAP(SDLK_m, 0x32); - MAP(SDLK_n, 0x31); - MAP(SDLK_o, 0x18); - MAP(SDLK_p, 0x19); - MAP(SDLK_q, 0x10); - MAP(SDLK_r, 0x13); - MAP(SDLK_s, 0x1f); - MAP(SDLK_t, 0x14); - MAP(SDLK_u, 0x16); - MAP(SDLK_v, 0x2f); - MAP(SDLK_w, 0x11); - MAP(SDLK_x, 0x2d); - MAP(SDLK_y, 0x15); - MAP(SDLK_z, 0x2c); - MAP(SDLK_DELETE, 0xd3); - MAP(SDLK_KP0, 0x52); - MAP(SDLK_KP1, 0x4f); - MAP(SDLK_KP2, 0x50); - MAP(SDLK_KP3, 0x51); - MAP(SDLK_KP4, 0x4b); - MAP(SDLK_KP5, 0x4c); - MAP(SDLK_KP6, 0x4d); - MAP(SDLK_KP7, 0x47); - MAP(SDLK_KP8, 0x48); - MAP(SDLK_KP9, 0x49); - MAP(SDLK_KP_PERIOD, 0x53); - MAP(SDLK_KP_DIVIDE, 0xb5); - MAP(SDLK_KP_MULTIPLY, 0x37); - MAP(SDLK_KP_MINUS, 0x4a); - MAP(SDLK_KP_PLUS, 0x4e); - MAP(SDLK_KP_ENTER, 0x9c); - //MAP(SDLK_KP_EQUALS, ); - MAP(SDLK_UP, 0xc8); - MAP(SDLK_DOWN, 0xd0); - MAP(SDLK_RIGHT, 0xcd); - MAP(SDLK_LEFT, 0xcb); - MAP(SDLK_INSERT, 0xd2); - MAP(SDLK_HOME, 0xc7); - MAP(SDLK_END, 0xcf); - MAP(SDLK_PAGEUP, 0xc9); - MAP(SDLK_PAGEDOWN, 0xd1); - MAP(SDLK_F1, 0x3b); - MAP(SDLK_F2, 0x3c); - MAP(SDLK_F3, 0x3d); - MAP(SDLK_F4, 0x3e); - MAP(SDLK_F5, 0x3f); - MAP(SDLK_F6, 0x40); - MAP(SDLK_F7, 0x41); - MAP(SDLK_F8, 0x42); - MAP(SDLK_F9, 0x43); - MAP(SDLK_F10, 0x44); - MAP(SDLK_F11, 0x57); - MAP(SDLK_F12, 0x58); - MAP(SDLK_NUMLOCK, 0x45); - MAP(SDLK_CAPSLOCK, 0x3a); - MAP(SDLK_SCROLLOCK, 0x46); - MAP(SDLK_RSHIFT, 0x36); - MAP(SDLK_LSHIFT, 0x2a); - MAP(SDLK_RCTRL, 0x9d); - MAP(SDLK_LCTRL, 0x1d); - MAP(SDLK_RALT, 0xb8); - MAP(SDLK_LALT, 0x38); - MAP(SDLK_LSUPER, 0xdb); // win l - MAP(SDLK_RSUPER, 0xdc); // win r - MAP(SDLK_PRINT, -2); // 0xaa + 0xb7 - MAP(SDLK_SYSREQ, 0x54); // alt+printscr - MAP(SDLK_BREAK, 0xb7); // ctrl+pause - MAP(SDLK_MENU, 0xdd); // win menu? -#undef MAP - - return 0; -} -#else // if SDL 1.3+ -static int32_t buildkeytranslationtable(void) -{ - memset(keytranslation,0,sizeof(keytranslation)); - -#define MAP(x,y) keytranslation[x] = y - MAP(SDL_SCANCODE_BACKSPACE, 0xe); - MAP(SDL_SCANCODE_TAB, 0xf); - MAP(SDL_SCANCODE_RETURN, 0x1c); - MAP(SDL_SCANCODE_PAUSE, 0x59); // 0x1d + 0x45 + 0x9d + 0xc5 - MAP(SDL_SCANCODE_ESCAPE, 0x1); - MAP(SDL_SCANCODE_SPACE, 0x39); - MAP(SDL_SCANCODE_COMMA, 0x33); - MAP(SDL_SCANCODE_NONUSBACKSLASH, 0x56); - MAP(SDL_SCANCODE_MINUS, 0xc); - MAP(SDL_SCANCODE_PERIOD, 0x34); - MAP(SDL_SCANCODE_SLASH, 0x35); - MAP(SDL_SCANCODE_0, 0xb); - MAP(SDL_SCANCODE_1, 0x2); - MAP(SDL_SCANCODE_2, 0x3); - MAP(SDL_SCANCODE_3, 0x4); - MAP(SDL_SCANCODE_4, 0x5); - MAP(SDL_SCANCODE_5, 0x6); - MAP(SDL_SCANCODE_6, 0x7); - MAP(SDL_SCANCODE_7, 0x8); - MAP(SDL_SCANCODE_8, 0x9); - MAP(SDL_SCANCODE_9, 0xa); - MAP(SDL_SCANCODE_SEMICOLON, 0x27); - MAP(SDL_SCANCODE_APOSTROPHE, 0x28); - MAP(SDL_SCANCODE_EQUALS, 0xd); - MAP(SDL_SCANCODE_LEFTBRACKET, 0x1a); - MAP(SDL_SCANCODE_BACKSLASH, 0x2b); - MAP(SDL_SCANCODE_RIGHTBRACKET, 0x1b); - MAP(SDL_SCANCODE_A, 0x1e); - MAP(SDL_SCANCODE_B, 0x30); - MAP(SDL_SCANCODE_C, 0x2e); - MAP(SDL_SCANCODE_D, 0x20); - MAP(SDL_SCANCODE_E, 0x12); - MAP(SDL_SCANCODE_F, 0x21); - MAP(SDL_SCANCODE_G, 0x22); - MAP(SDL_SCANCODE_H, 0x23); - MAP(SDL_SCANCODE_I, 0x17); - MAP(SDL_SCANCODE_J, 0x24); - MAP(SDL_SCANCODE_K, 0x25); - MAP(SDL_SCANCODE_L, 0x26); - MAP(SDL_SCANCODE_M, 0x32); - MAP(SDL_SCANCODE_N, 0x31); - MAP(SDL_SCANCODE_O, 0x18); - MAP(SDL_SCANCODE_P, 0x19); - MAP(SDL_SCANCODE_Q, 0x10); - MAP(SDL_SCANCODE_R, 0x13); - MAP(SDL_SCANCODE_S, 0x1f); - MAP(SDL_SCANCODE_T, 0x14); - MAP(SDL_SCANCODE_U, 0x16); - MAP(SDL_SCANCODE_V, 0x2f); - MAP(SDL_SCANCODE_W, 0x11); - MAP(SDL_SCANCODE_X, 0x2d); - MAP(SDL_SCANCODE_Y, 0x15); - MAP(SDL_SCANCODE_Z, 0x2c); - MAP(SDL_SCANCODE_DELETE, 0xd3); - MAP(SDL_SCANCODE_KP_0, 0x52); - MAP(SDL_SCANCODE_KP_1, 0x4f); - MAP(SDL_SCANCODE_KP_2, 0x50); - MAP(SDL_SCANCODE_KP_3, 0x51); - MAP(SDL_SCANCODE_KP_4, 0x4b); - MAP(SDL_SCANCODE_KP_5, 0x4c); - MAP(SDL_SCANCODE_KP_CLEAR, 0x4c); - MAP(SDL_SCANCODE_CLEAR, 0x4c); - MAP(SDL_SCANCODE_KP_6, 0x4d); - MAP(SDL_SCANCODE_KP_7, 0x47); - MAP(SDL_SCANCODE_KP_8, 0x48); - MAP(SDL_SCANCODE_KP_9, 0x49); - MAP(SDL_SCANCODE_KP_PERIOD, 0x53); - MAP(SDL_SCANCODE_KP_DIVIDE, 0xb5); - MAP(SDL_SCANCODE_KP_MULTIPLY, 0x37); - MAP(SDL_SCANCODE_KP_MINUS, 0x4a); - MAP(SDL_SCANCODE_KP_PLUS, 0x4e); - MAP(SDL_SCANCODE_KP_ENTER, 0x9c); - //MAP(SDL_SCANCODE_KP_EQUALS, ); - MAP(SDL_SCANCODE_UP, 0xc8); - MAP(SDL_SCANCODE_DOWN, 0xd0); - MAP(SDL_SCANCODE_RIGHT, 0xcd); - MAP(SDL_SCANCODE_LEFT, 0xcb); - MAP(SDL_SCANCODE_INSERT, 0xd2); - MAP(SDL_SCANCODE_HOME, 0xc7); - MAP(SDL_SCANCODE_END, 0xcf); - MAP(SDL_SCANCODE_PAGEUP, 0xc9); - MAP(SDL_SCANCODE_PAGEDOWN, 0xd1); - MAP(SDL_SCANCODE_F1, 0x3b); - MAP(SDL_SCANCODE_F2, 0x3c); - MAP(SDL_SCANCODE_F3, 0x3d); - MAP(SDL_SCANCODE_F4, 0x3e); - MAP(SDL_SCANCODE_F5, 0x3f); - MAP(SDL_SCANCODE_F6, 0x40); - MAP(SDL_SCANCODE_F7, 0x41); - MAP(SDL_SCANCODE_F8, 0x42); - MAP(SDL_SCANCODE_F9, 0x43); - MAP(SDL_SCANCODE_F10, 0x44); - MAP(SDL_SCANCODE_F11, 0x57); - MAP(SDL_SCANCODE_F12, 0x58); - MAP(SDL_SCANCODE_NUMLOCKCLEAR, 0x45); - MAP(SDL_SCANCODE_CAPSLOCK, 0x3a); - MAP(SDL_SCANCODE_SCROLLLOCK, 0x46); - MAP(SDL_SCANCODE_RSHIFT, 0x36); - MAP(SDL_SCANCODE_LSHIFT, 0x2a); - MAP(SDL_SCANCODE_RCTRL, 0x9d); - MAP(SDL_SCANCODE_LCTRL, 0x1d); - MAP(SDL_SCANCODE_RALT, 0xb8); - MAP(SDL_SCANCODE_LALT, 0x38); - MAP(SDL_SCANCODE_LGUI, 0xdb); // win l - MAP(SDL_SCANCODE_RGUI, 0xdc); // win r -// MAP(SDL_SCANCODE_PRINTSCREEN, -2); // 0xaa + 0xb7 - MAP(SDL_SCANCODE_SYSREQ, 0x54); // alt+printscr -// MAP(SDL_SCANCODE_PAUSE, 0xb7); // ctrl+pause - MAP(SDL_SCANCODE_MENU, 0xdd); // win menu? - MAP(SDL_SCANCODE_GRAVE, 0x29); // tilde -#undef MAP - - return 0; -} -#endif diff --git a/source/common/gameconfigfile.cpp b/source/common/gameconfigfile.cpp index e045c2645..c97a5765a 100644 --- a/source/common/gameconfigfile.cpp +++ b/source/common/gameconfigfile.cpp @@ -531,7 +531,6 @@ void G_SaveConfig() { GameConfig->ArchiveGlobalData(); GameConfig->ArchiveGameData(GameName); - CONFIG_WriteControllerSettings(); GameConfig->WriteConfigFile(); delete GameConfig; GameConfig = nullptr; diff --git a/source/common/gamecontrol.cpp b/source/common/gamecontrol.cpp index 5164b3aea..6a959e78b 100644 --- a/source/common/gamecontrol.cpp +++ b/source/common/gamecontrol.cpp @@ -250,6 +250,9 @@ void CheckFrontend(int flags) } } +void I_StartupJoysticks(); +void I_ShutdownInput(); +int RunGame(); int GameMain() { @@ -257,6 +260,7 @@ int GameMain() C_InitConsole(1024, 768, true); FStringf logpath("logfile %sdemolition.log", M_GetDocumentsPath().GetChars()); C_DoCommand(logpath); + I_StartupJoysticks(); #ifndef NETCODE_DISABLE gHaveNetworking = !enet_initialize(); @@ -267,7 +271,7 @@ int GameMain() int r; try { - r = CONFIG_Init(); + r = RunGame(); } catch (const std::runtime_error & err) { @@ -283,6 +287,7 @@ int GameMain() #ifndef NETCODE_DISABLE if (gHaveNetworking) enet_deinitialize(); #endif + I_ShutdownInput(); return r; } @@ -328,7 +333,7 @@ void SetDefaultStrings() // //========================================================================== -int CONFIG_Init() +int RunGame() { SetClipshapes(); @@ -526,288 +531,12 @@ int CONFIG_SetMapBestTime(uint8_t const* const mapmd4, int32_t tm) } return 0; } -//========================================================================== -// -// -// -//========================================================================== - -int32_t MouseAnalogueAxes[MAXMOUSEAXES]; -int32_t JoystickFunctions[MAXJOYBUTTONSANDHATS][2]; -int32_t JoystickDigitalFunctions[MAXJOYAXES][2]; -int32_t JoystickAnalogueAxes[MAXJOYAXES]; -int32_t JoystickAnalogueScale[MAXJOYAXES]; -int32_t JoystickAnalogueDead[MAXJOYAXES]; -int32_t JoystickAnalogueSaturate[MAXJOYAXES]; -int32_t JoystickAnalogueInvert[MAXJOYAXES]; - -static const char* mouseanalogdefaults[MAXMOUSEAXES] = -{ -"analog_turning", -"analog_moving", -}; - - -static const char* joystickclickeddefaults[MAXJOYBUTTONSANDHATS] = -{ -"", -"Inventory", -"Jump", -"Crouch", -}; - - -static const char* joystickanalogdefaults[MAXJOYAXES] = -{ -"analog_turning", -"analog_moving", -"analog_strafing", -}; - - -//========================================================================== -// -// -// -//========================================================================== - -int32_t CONFIG_AnalogNameToNum(const char* func) -{ - if (!func) - return -1; - - if (!Bstrcasecmp(func, "analog_turning")) - { - return analog_turning; - } - if (!Bstrcasecmp(func, "analog_strafing")) - { - return analog_strafing; - } - if (!Bstrcasecmp(func, "analog_moving")) - { - return analog_moving; - } - if (!Bstrcasecmp(func, "analog_lookingupanddown")) - { - return analog_lookingupanddown; - } - - return -1; -} - - -//========================================================================== -// -// -// -//========================================================================== - -const char* CONFIG_AnalogNumToName(int32_t func) -{ - switch (func) - { - case analog_turning: - return "analog_turning"; - case analog_strafing: - return "analog_strafing"; - case analog_moving: - return "analog_moving"; - case analog_lookingupanddown: - return "analog_lookingupanddown"; - } - - return NULL; -} - -void CONFIG_SetupMouse(void) -{ - CONTROL_MouseEnabled = (in_mouse && CONTROL_MousePresent); -} - - -void CONFIG_SetupJoystick(void) -{ - for (int i = 0; i < MAXJOYAXES; i++) - { - CONTROL_MapAnalogAxis(i, JoystickAnalogueAxes[i], controldevice_joystick); - CONTROL_MapDigitalAxis(i, JoystickDigitalFunctions[i][0], 0, controldevice_joystick); - CONTROL_MapDigitalAxis(i, JoystickDigitalFunctions[i][1], 1, controldevice_joystick); - CONTROL_SetAnalogAxisScale(i, JoystickAnalogueScale[i], controldevice_joystick); - CONTROL_SetAnalogAxisInvert(i, JoystickAnalogueInvert[i], controldevice_joystick); - } - - CONTROL_JoystickEnabled = (in_joystick && CONTROL_JoyPresent); - - // JBF 20040215: evil and nasty place to do this, but joysticks are evil and nasty too - for (int i=0; i<joystick.numAxes; i++) - joySetDeadZone(i,JoystickAnalogueDead[i],JoystickAnalogueSaturate[i]); - -} - -static void CONFIG_SetJoystickButtonFunction(int i, int j, int function) -{ - JoystickFunctions[i][j] = function; - //CONTROL_MapButton(function, i, j, controldevice_joystick); -} -static void CONFIG_SetJoystickAnalogAxisScale(int i, int scale) -{ - JoystickAnalogueScale[i] = scale; - CONTROL_SetAnalogAxisScale(i, scale, controldevice_joystick); -} -static void CONFIG_SetJoystickAnalogAxisInvert(int i, int invert) -{ - JoystickAnalogueInvert[i] = invert; - CONTROL_SetAnalogAxisInvert(i, invert, controldevice_joystick); -} -static void CONFIG_SetJoystickAnalogAxisDeadSaturate(int i, int dead, int saturate) -{ - JoystickAnalogueDead[i] = dead; - JoystickAnalogueSaturate[i] = saturate; - joySetDeadZone(i, dead, saturate); -} -static void CONFIG_SetJoystickDigitalAxisFunction(int i, int j, int function) -{ - JoystickDigitalFunctions[i][j] = function; - CONTROL_MapDigitalAxis(i, function, j, controldevice_joystick); -} -static void CONFIG_SetJoystickAnalogAxisFunction(int i, int function) -{ - JoystickAnalogueAxes[i] = function; - CONTROL_MapAnalogAxis(i, function, controldevice_joystick); -} - -struct GameControllerButtonSetting -{ - GameControllerButton button; - int function; - - void apply() const - { - CONFIG_SetJoystickButtonFunction(button, 0, function); - } -}; -struct GameControllerAnalogAxisSetting -{ - GameControllerAxis axis; - int function; - - void apply() const - { - CONFIG_SetJoystickAnalogAxisFunction(axis, function); - } -}; -struct GameControllerDigitalAxisSetting -{ - GameControllerAxis axis; - int polarity; - int function; - - void apply() const - { - CONFIG_SetJoystickDigitalAxisFunction(axis, polarity, function); - } -}; - - -void CONFIG_SetGameControllerDefaultsClear() -{ - for (int i = 0; i < MAXJOYBUTTONSANDHATS; i++) - { - CONFIG_SetJoystickButtonFunction(i, 0, -1); - CONFIG_SetJoystickButtonFunction(i, 1, -1); - } - - for (int i = 0; i < MAXJOYAXES; i++) - { - CONFIG_SetJoystickAnalogAxisScale(i, DEFAULTJOYSTICKANALOGUESCALE); - CONFIG_SetJoystickAnalogAxisInvert(i, 0); - CONFIG_SetJoystickAnalogAxisDeadSaturate(i, DEFAULTJOYSTICKANALOGUEDEAD, DEFAULTJOYSTICKANALOGUESATURATE); - - CONFIG_SetJoystickDigitalAxisFunction(i, 0, -1); - CONFIG_SetJoystickDigitalAxisFunction(i, 1, -1); - - CONFIG_SetJoystickAnalogAxisFunction(i, -1); - } -} - -static void CONFIG_SetGameControllerAxesModern() -{ - static GameControllerAnalogAxisSetting const analogAxes[] = - { - { GAMECONTROLLER_AXIS_LEFTX, analog_strafing }, - { GAMECONTROLLER_AXIS_LEFTY, analog_moving }, - { GAMECONTROLLER_AXIS_RIGHTX, analog_turning }, - { GAMECONTROLLER_AXIS_RIGHTY, analog_lookingupanddown }, - }; - - CONFIG_SetJoystickAnalogAxisScale(GAMECONTROLLER_AXIS_RIGHTX, 32768 + 16384); - CONFIG_SetJoystickAnalogAxisScale(GAMECONTROLLER_AXIS_RIGHTY, 32768 + 16384); - - for (auto const& analogAxis : analogAxes) - analogAxis.apply(); -} - void CONFIG_InitMouseAndController() { - memset(JoystickFunctions, -1, sizeof(JoystickFunctions)); - memset(JoystickDigitalFunctions, -1, sizeof(JoystickDigitalFunctions)); - - for (int i = 0; i < MAXMOUSEAXES; i++) - { - MouseAnalogueAxes[i] = CONFIG_AnalogNameToNum(mouseanalogdefaults[i]); - CONTROL_MapAnalogAxis(i, MouseAnalogueAxes[i], controldevice_mouse); - } - CONFIG_SetupMouse(); - CONFIG_SetupJoystick(); inputState.ClearKeysDown(); inputState.keyFlushChars(); inputState.keyFlushScans(); } - -void CONFIG_PutNumber(const char* key, int number) -{ - FStringf str("%d", number); - GameConfig->SetValueForKey(key, str); -} - -void CONFIG_WriteControllerSettings() -{ - FString buf; - - - if (in_joystick) - { - FString section = currentGame + ".ControllerSettings"; - GameConfig->SetSection(section); - for (int dummy = 0; dummy < MAXJOYAXES; dummy++) - { - if (CONFIG_AnalogNumToName(JoystickAnalogueAxes[dummy])) - { - buf.Format("ControllerAnalogAxes%d", dummy); - GameConfig->SetValueForKey(buf, CONFIG_AnalogNumToName(JoystickAnalogueAxes[dummy])); - } - - if (buttonMap.GetButtonName(JoystickDigitalFunctions[dummy][1])) - { - buf.Format("ControllerDigitalAxes%d_1", dummy); - GameConfig->SetValueForKey(buf, buttonMap.GetButtonName(JoystickDigitalFunctions[dummy][1])); - } - - buf.Format("ControllerAnalogScale%d", dummy); - CONFIG_PutNumber(buf, JoystickAnalogueScale[dummy]); - - buf.Format("ControllerAnalogInvert%d", dummy); - CONFIG_PutNumber(buf, JoystickAnalogueInvert[dummy]); - - buf.Format("ControllerAnalogDead%d", dummy); - CONFIG_PutNumber(buf, JoystickAnalogueDead[dummy]); - - buf.Format("ControllerAnalogSaturate%d", dummy); - CONFIG_PutNumber(buf, JoystickAnalogueSaturate[dummy]); - } - } -} diff --git a/source/common/gamecontrol.h b/source/common/gamecontrol.h index b699693d8..155d86338 100644 --- a/source/common/gamecontrol.h +++ b/source/common/gamecontrol.h @@ -39,7 +39,6 @@ void CONFIG_SetDefaultKeys(const char *defbinds); void CONFIG_SetupJoystick(void); -void CONFIG_WriteControllerSettings(); void CONFIG_InitMouseAndController(); void CONFIG_SetGameControllerDefaultsClear(); diff --git a/source/common/gamecvars.cpp b/source/common/gamecvars.cpp index 5a4674b13..e6d7d2e93 100644 --- a/source/common/gamecvars.cpp +++ b/source/common/gamecvars.cpp @@ -297,10 +297,7 @@ CUSTOM_CVARD(Bool, in_joystick, false, CVAR_ARCHIVE||CVAR_GLOBALCONFIG|CVAR_NOIN CONTROL_JoystickEnabled = (self && CONTROL_JoyPresent); } -CUSTOM_CVARD(Bool, in_mouse, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG|CVAR_NOINITCALL, "enables input from the mouse if it is present") -{ - CONTROL_MouseEnabled = (self && CONTROL_MousePresent); -} +CVARD(Bool, in_mouse, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG|CVAR_NOINITCALL, "enables input from the mouse if it is present") CVARD(Bool, in_mousemode, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG, "toggles vertical mouse view") diff --git a/source/common/input/i_gui.cpp b/source/common/input/i_gui.cpp new file mode 100644 index 000000000..01a2362ca --- /dev/null +++ b/source/common/input/i_gui.cpp @@ -0,0 +1,87 @@ +/* +** i_gui.cpp +** +**--------------------------------------------------------------------------- +** Copyright 2008 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include <string.h> + +#include <SDL.h> + +#include "bitmap.h" +//#include "v_palette.h" +#include "textures.h" + +bool I_SetCursor(FTexture *cursorpic) +{ + static SDL_Cursor *cursor; + static SDL_Surface *cursorSurface; + + if (cursorpic != NULL) + { + auto src = cursorpic->GetBgraBitmap(nullptr); + // Must be no larger than 32x32. + if (src.GetWidth() > 32 || src.GetHeight() > 32) + { + return false; + } + + if (cursorSurface == NULL) + cursorSurface = SDL_CreateRGBSurface (0, 32, 32, 32, MAKEARGB(0,255,0,0), MAKEARGB(0,0,255,0), MAKEARGB(0,0,0,255), MAKEARGB(255,0,0,0)); + + SDL_LockSurface(cursorSurface); + uint8_t buffer[32*32*4]; + memset(buffer, 0, 32*32*4); + FBitmap bmp(buffer, 32*4, 32, 32); + bmp.Blit(0, 0, src); // expand to 32*32 + memcpy(cursorSurface->pixels, bmp.GetPixels(), 32*32*4); + SDL_UnlockSurface(cursorSurface); + + if (cursor) + SDL_FreeCursor (cursor); + cursor = SDL_CreateColorCursor (cursorSurface, 0, 0); + SDL_SetCursor (cursor); + } + else + { + if (cursor) + { + SDL_SetCursor (NULL); + SDL_FreeCursor (cursor); + cursor = NULL; + } + if (cursorSurface != NULL) + { + SDL_FreeSurface(cursorSurface); + cursorSurface = NULL; + } + } + return true; +} diff --git a/source/common/input/i_input.cpp b/source/common/input/i_input.cpp new file mode 100644 index 000000000..fa3a94b87 --- /dev/null +++ b/source/common/input/i_input.cpp @@ -0,0 +1,750 @@ +/* +** This code is partially original EDuke32 code, partially taken from ZDoom +** +** For the portions taken from ZDoom the following applies: +** +**--------------------------------------------------------------------------- +** Copyright 2005-2016 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include <SDL.h> +#include "compat.h" + +//#include "doomtype.h" +//#include "doomdef.h" +//#include "doomstat.h" +#include "m_argv.h" +//#include "v_video.h" + +#include "c_buttons.h" +#include "d_event.h" +#include "d_gui.h" +#include "c_console.h" +#include "c_dispatch.h" +#include "c_cvars.h" +#include "keydef.h" +//#include "dikeys.h" +//#include "events.h" +//#include "g_game.h" +//#include "g_levellocals.h" +#include "utf8.h" +#include "menu/menu.h" + + +char grabmouse_low(char a); +void WindowMoved(int x, int y); +extern SDL_Window* sdl_window; + + +static uint8_t keytranslation[SDL_NUM_SCANCODES]; + +void buildkeytranslationtable(void) +{ + memset(keytranslation, 0, sizeof(keytranslation)); + +#define MAP(x,y) keytranslation[x] = y + MAP(SDL_SCANCODE_BACKSPACE, 0xe); + MAP(SDL_SCANCODE_TAB, 0xf); + MAP(SDL_SCANCODE_RETURN, 0x1c); + MAP(SDL_SCANCODE_PAUSE, 0x59); // 0x1d + 0x45 + 0x9d + 0xc5 + MAP(SDL_SCANCODE_ESCAPE, 0x1); + MAP(SDL_SCANCODE_SPACE, 0x39); + MAP(SDL_SCANCODE_COMMA, 0x33); + MAP(SDL_SCANCODE_NONUSBACKSLASH, 0x56); + MAP(SDL_SCANCODE_MINUS, 0xc); + MAP(SDL_SCANCODE_PERIOD, 0x34); + MAP(SDL_SCANCODE_SLASH, 0x35); + MAP(SDL_SCANCODE_0, 0xb); + MAP(SDL_SCANCODE_1, 0x2); + MAP(SDL_SCANCODE_2, 0x3); + MAP(SDL_SCANCODE_3, 0x4); + MAP(SDL_SCANCODE_4, 0x5); + MAP(SDL_SCANCODE_5, 0x6); + MAP(SDL_SCANCODE_6, 0x7); + MAP(SDL_SCANCODE_7, 0x8); + MAP(SDL_SCANCODE_8, 0x9); + MAP(SDL_SCANCODE_9, 0xa); + MAP(SDL_SCANCODE_SEMICOLON, 0x27); + MAP(SDL_SCANCODE_APOSTROPHE, 0x28); + MAP(SDL_SCANCODE_EQUALS, 0xd); + MAP(SDL_SCANCODE_LEFTBRACKET, 0x1a); + MAP(SDL_SCANCODE_BACKSLASH, 0x2b); + MAP(SDL_SCANCODE_RIGHTBRACKET, 0x1b); + MAP(SDL_SCANCODE_A, 0x1e); + MAP(SDL_SCANCODE_B, 0x30); + MAP(SDL_SCANCODE_C, 0x2e); + MAP(SDL_SCANCODE_D, 0x20); + MAP(SDL_SCANCODE_E, 0x12); + MAP(SDL_SCANCODE_F, 0x21); + MAP(SDL_SCANCODE_G, 0x22); + MAP(SDL_SCANCODE_H, 0x23); + MAP(SDL_SCANCODE_I, 0x17); + MAP(SDL_SCANCODE_J, 0x24); + MAP(SDL_SCANCODE_K, 0x25); + MAP(SDL_SCANCODE_L, 0x26); + MAP(SDL_SCANCODE_M, 0x32); + MAP(SDL_SCANCODE_N, 0x31); + MAP(SDL_SCANCODE_O, 0x18); + MAP(SDL_SCANCODE_P, 0x19); + MAP(SDL_SCANCODE_Q, 0x10); + MAP(SDL_SCANCODE_R, 0x13); + MAP(SDL_SCANCODE_S, 0x1f); + MAP(SDL_SCANCODE_T, 0x14); + MAP(SDL_SCANCODE_U, 0x16); + MAP(SDL_SCANCODE_V, 0x2f); + MAP(SDL_SCANCODE_W, 0x11); + MAP(SDL_SCANCODE_X, 0x2d); + MAP(SDL_SCANCODE_Y, 0x15); + MAP(SDL_SCANCODE_Z, 0x2c); + MAP(SDL_SCANCODE_DELETE, 0xd3); + MAP(SDL_SCANCODE_KP_0, 0x52); + MAP(SDL_SCANCODE_KP_1, 0x4f); + MAP(SDL_SCANCODE_KP_2, 0x50); + MAP(SDL_SCANCODE_KP_3, 0x51); + MAP(SDL_SCANCODE_KP_4, 0x4b); + MAP(SDL_SCANCODE_KP_5, 0x4c); + MAP(SDL_SCANCODE_KP_CLEAR, 0x4c); + MAP(SDL_SCANCODE_CLEAR, 0x4c); + MAP(SDL_SCANCODE_KP_6, 0x4d); + MAP(SDL_SCANCODE_KP_7, 0x47); + MAP(SDL_SCANCODE_KP_8, 0x48); + MAP(SDL_SCANCODE_KP_9, 0x49); + MAP(SDL_SCANCODE_KP_PERIOD, 0x53); + MAP(SDL_SCANCODE_KP_DIVIDE, 0xb5); + MAP(SDL_SCANCODE_KP_MULTIPLY, 0x37); + MAP(SDL_SCANCODE_KP_MINUS, 0x4a); + MAP(SDL_SCANCODE_KP_PLUS, 0x4e); + MAP(SDL_SCANCODE_KP_ENTER, 0x9c); + //MAP(SDL_SCANCODE_KP_EQUALS, ); + MAP(SDL_SCANCODE_UP, 0xc8); + MAP(SDL_SCANCODE_DOWN, 0xd0); + MAP(SDL_SCANCODE_RIGHT, 0xcd); + MAP(SDL_SCANCODE_LEFT, 0xcb); + MAP(SDL_SCANCODE_INSERT, 0xd2); + MAP(SDL_SCANCODE_HOME, 0xc7); + MAP(SDL_SCANCODE_END, 0xcf); + MAP(SDL_SCANCODE_PAGEUP, 0xc9); + MAP(SDL_SCANCODE_PAGEDOWN, 0xd1); + MAP(SDL_SCANCODE_F1, 0x3b); + MAP(SDL_SCANCODE_F2, 0x3c); + MAP(SDL_SCANCODE_F3, 0x3d); + MAP(SDL_SCANCODE_F4, 0x3e); + MAP(SDL_SCANCODE_F5, 0x3f); + MAP(SDL_SCANCODE_F6, 0x40); + MAP(SDL_SCANCODE_F7, 0x41); + MAP(SDL_SCANCODE_F8, 0x42); + MAP(SDL_SCANCODE_F9, 0x43); + MAP(SDL_SCANCODE_F10, 0x44); + MAP(SDL_SCANCODE_F11, 0x57); + MAP(SDL_SCANCODE_F12, 0x58); + MAP(SDL_SCANCODE_NUMLOCKCLEAR, 0x45); + MAP(SDL_SCANCODE_CAPSLOCK, 0x3a); + MAP(SDL_SCANCODE_SCROLLLOCK, 0x46); + MAP(SDL_SCANCODE_RSHIFT, 0x36); + MAP(SDL_SCANCODE_LSHIFT, 0x2a); + MAP(SDL_SCANCODE_RCTRL, 0x9d); + MAP(SDL_SCANCODE_LCTRL, 0x1d); + MAP(SDL_SCANCODE_RALT, 0xb8); + MAP(SDL_SCANCODE_LALT, 0x38); + MAP(SDL_SCANCODE_LGUI, 0xdb); // win l + MAP(SDL_SCANCODE_RGUI, 0xdc); // win r +// MAP(SDL_SCANCODE_PRINTSCREEN, -2); // 0xaa + 0xb7 + MAP(SDL_SCANCODE_SYSREQ, 0x54); // alt+printscr +// MAP(SDL_SCANCODE_PAUSE, 0xb7); // ctrl+pause + MAP(SDL_SCANCODE_MENU, 0xdd); // win menu? + MAP(SDL_SCANCODE_GRAVE, 0x29); // tilde +#undef MAP +} + + + +// +// initmouse() -- init mouse input +// +void mouseInit(void) +{ + mouseGrabInput(g_mouseEnabled = g_mouseLockedToWindow); // FIXME - SA +} + +// +// uninitmouse() -- uninit mouse input +// +void mouseUninit(void) +{ + mouseGrabInput(0); + g_mouseEnabled = 0; +} + + +// +// grabmouse_low() -- show/hide mouse cursor, lower level (doesn't check state). +// furthermore return 0 if successful. +// + +char grabmouse_low(char a) +{ + /* FIXME: Maybe it's better to make sure that grabmouse_low + is called only when a window is ready? */ + if (sdl_window) + SDL_SetWindowGrab(sdl_window, a ? SDL_TRUE : SDL_FALSE); + return SDL_SetRelativeMouseMode(a ? SDL_TRUE : SDL_FALSE); +} + +// +// grabmouse() -- show/hide mouse cursor +// +void mouseGrabInput(bool grab) +{ + if (appactive && g_mouseEnabled) + { + if ((grab != g_mouseGrabbed) && !grabmouse_low(grab)) + g_mouseGrabbed = grab; + } + else + g_mouseGrabbed = grab; + + inputState.MouseSetPos(0, 0); + SDL_ShowCursor(!grab ? SDL_ENABLE : SDL_DISABLE); + if (grab) GUICapture &= ~1; + else GUICapture |= 1; +} + + +// So this is how the engine handles text input? +// Argh. This is just gross. +int scancodetoasciihack(SDL_Event& ev) +{ + int sc = ev.key.keysym.scancode; + SDL_Keycode keyvalue = ev.key.keysym.sym; + int code = keytranslation[sc]; + // Modifiers that have to be held down to be effective + // (excludes KMOD_NUM, for example). + static const int MODIFIERS = + KMOD_LSHIFT | KMOD_RSHIFT | KMOD_LCTRL | KMOD_RCTRL | + KMOD_LALT | KMOD_RALT | KMOD_LGUI | KMOD_RGUI; + + // XXX: see osd.c, OSD_HandleChar(), there are more... + if ( + (sc == SDL_SCANCODE_RETURN || sc == SDL_SCANCODE_KP_ENTER || + sc == SDL_SCANCODE_ESCAPE || + sc == SDL_SCANCODE_BACKSPACE || + sc == SDL_SCANCODE_TAB || + (((ev.key.keysym.mod) & MODIFIERS) == KMOD_LCTRL && + (sc >= SDL_SCANCODE_A && sc <= SDL_SCANCODE_Z)))) + { + char keyvalue; + switch (sc) + { + case SDL_SCANCODE_RETURN: case SDL_SCANCODE_KP_ENTER: keyvalue = '\r'; break; + case SDL_SCANCODE_ESCAPE: keyvalue = 27; break; + case SDL_SCANCODE_BACKSPACE: keyvalue = '\b'; break; + case SDL_SCANCODE_TAB: keyvalue = '\t'; break; + default: keyvalue = sc - SDL_SCANCODE_A + 1; break; // Ctrl+A --> 1, etc. + } + } + else + { + /* + Necessary for Duke 3D's method of entering cheats to work without showing IMEs. + SDL_TEXTINPUT is preferable overall, but with bitmap fonts it has no advantage. + */ + // Note that this is not how text input is supposed to be handled! + + if ('a' <= keyvalue && keyvalue <= 'z') + { + if (!!(ev.key.keysym.mod & KMOD_SHIFT) ^ !!(ev.key.keysym.mod & KMOD_CAPS)) + keyvalue -= 'a' - 'A'; + } + else if (ev.key.keysym.mod & KMOD_SHIFT) + { + keyvalue = g_keyAsciiTableShift[code]; + } + else if (ev.key.keysym.mod & KMOD_NUM) // && !(ev.key.keysym.mod & KMOD_SHIFT) + { + switch (keyvalue) + { + case SDLK_KP_1: keyvalue = '1'; break; + case SDLK_KP_2: keyvalue = '2'; break; + case SDLK_KP_3: keyvalue = '3'; break; + case SDLK_KP_4: keyvalue = '4'; break; + case SDLK_KP_5: keyvalue = '5'; break; + case SDLK_KP_6: keyvalue = '6'; break; + case SDLK_KP_7: keyvalue = '7'; break; + case SDLK_KP_8: keyvalue = '8'; break; + case SDLK_KP_9: keyvalue = '9'; break; + case SDLK_KP_0: keyvalue = '0'; break; + case SDLK_KP_PERIOD: keyvalue = '.'; break; + case SDLK_KP_COMMA: keyvalue = ','; break; + } + } + + switch (keyvalue) + { + case SDLK_KP_DIVIDE: keyvalue = '/'; break; + case SDLK_KP_MULTIPLY: keyvalue = '*'; break; + case SDLK_KP_MINUS: keyvalue = '-'; break; + case SDLK_KP_PLUS: keyvalue = '+'; break; + } + } + if (keyvalue >= 0x80) keyvalue = 0; // Sadly ASCII only... + return keyvalue; +} + +static void PostMouseMove(int x, int y) +{ + static int lastx = 0, lasty = 0; + event_t ev = { 0,0,0,0,0,0,0 }; + + ev.x = x; + ev.y = y; + lastx = x; + lasty = y; + if (ev.x | ev.y) + { + ev.type = EV_Mouse; + D_PostEvent(&ev); + } +} + +CVAR(Bool, m_noprescale, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) + +static void MouseRead() +{ + int x, y; + +#if 0 + if (NativeMouse) + { + return; + } +#endif + + SDL_GetRelativeMouseState(&x, &y); + if (!m_noprescale) + { + x *= 3; + y *= 2; + } + if (x | y) + { + PostMouseMove(x, -y); + } +} + +int32_t handleevents_pollsdl(void) +{ + int32_t code, rv = 0, j; + SDL_Event ev; + event_t evt; + + while (SDL_PollEvent(&ev)) + { + switch (ev.type) + { + case SDL_TEXTINPUT: + { + j = 0; + const uint8_t* text = (uint8_t*)ev.text.text; + while ((j = GetCharFromString(text))) + { + code = ev.text.text[j]; + // Fixme: Send an EV_GUI_Event instead and properly deal with Unicode. + if ((GUICapture & 1) && menuactive != MENU_WaitKey) + { + evt = { EV_GUI_Event, EV_GUI_Char, int16_t(j), !!(SDL_GetModState() & KMOD_ALT) }; + D_PostEvent(&evt); + } + } + break; + } + + case SDL_KEYDOWN: + case SDL_KEYUP: + { + if ((GUICapture & 1) && menuactive != MENU_WaitKey) + { + evt = {}; + evt.type = EV_GUI_Event; + evt.subtype = ev.type == SDL_KEYDOWN ? EV_GUI_KeyDown : EV_GUI_KeyUp; + SDL_Keymod kmod = SDL_GetModState(); + evt.data3 = ((kmod & KMOD_SHIFT) ? GKM_SHIFT : 0) | + ((kmod & KMOD_CTRL) ? GKM_CTRL : 0) | + ((kmod & KMOD_ALT) ? GKM_ALT : 0); + + if (evt.subtype == EV_GUI_KeyDown && ev.key.repeat) + { + evt.subtype = EV_GUI_KeyRepeat; + } + + switch (ev.key.keysym.sym) + { + case SDLK_KP_ENTER: evt.data1 = GK_RETURN; break; + case SDLK_PAGEUP: evt.data1 = GK_PGUP; break; + case SDLK_PAGEDOWN: evt.data1 = GK_PGDN; break; + case SDLK_END: evt.data1 = GK_END; break; + case SDLK_HOME: evt.data1 = GK_HOME; break; + case SDLK_LEFT: evt.data1 = GK_LEFT; break; + case SDLK_RIGHT: evt.data1 = GK_RIGHT; break; + case SDLK_UP: evt.data1 = GK_UP; break; + case SDLK_DOWN: evt.data1 = GK_DOWN; break; + case SDLK_DELETE: evt.data1 = GK_DEL; break; + case SDLK_ESCAPE: evt.data1 = GK_ESCAPE; break; + case SDLK_F1: evt.data1 = GK_F1; break; + case SDLK_F2: evt.data1 = GK_F2; break; + case SDLK_F3: evt.data1 = GK_F3; break; + case SDLK_F4: evt.data1 = GK_F4; break; + case SDLK_F5: evt.data1 = GK_F5; break; + case SDLK_F6: evt.data1 = GK_F6; break; + case SDLK_F7: evt.data1 = GK_F7; break; + case SDLK_F8: evt.data1 = GK_F8; break; + case SDLK_F9: evt.data1 = GK_F9; break; + case SDLK_F10: evt.data1 = GK_F10; break; + case SDLK_F11: evt.data1 = GK_F11; break; + case SDLK_F12: evt.data1 = GK_F12; break; + default: + if (ev.key.keysym.sym < 256) + { + evt.data1 = ev.key.keysym.sym; + } + break; + } + if (evt.data1 < 128) + { + evt.data1 = toupper(evt.data1); + D_PostEvent(&evt); + } + } + else + { + auto const& sc = ev.key.keysym.scancode; + code = keytranslation[sc]; + + // The pause key generates a release event right after + // the pressing one. As a result, it gets unseen + // by the game most of the time. + if (code == 0x59 && ev.type == SDL_KEYUP) // pause + break; + + int keyvalue = ev.type == SDL_KEYDOWN ? scancodetoasciihack(ev) : 0; + event_t evt = { (uint8_t)(ev.type == SDL_KEYUP ? EV_KeyUp : EV_KeyDown), 0, (int16_t)code, (int16_t)keyvalue }; + D_PostEvent(&evt); + } + + break; + } + + case SDL_MOUSEWHEEL: + if (GUICapture) + { + evt.type = EV_GUI_Event; + + if (ev.wheel.y == 0) + evt.subtype = ev.wheel.x > 0 ? EV_GUI_WheelRight : EV_GUI_WheelLeft; + else + evt.subtype = ev.wheel.y > 0 ? EV_GUI_WheelUp : EV_GUI_WheelDown; + + SDL_Keymod kmod = SDL_GetModState(); + evt.data3 = ((kmod & KMOD_SHIFT) ? GKM_SHIFT : 0) | + ((kmod & KMOD_CTRL) ? GKM_CTRL : 0) | + ((kmod & KMOD_ALT) ? GKM_ALT : 0); + + D_PostEvent(&evt); + } + else + { + // This never sends keyup events. They must be delayed for this to work with game buttons. + if (ev.wheel.y > 0) + { + evt = { EV_KeyDown, 0, (int16_t)KEY_MWHEELUP }; + D_PostEvent(&evt); + } + if (ev.wheel.y < 0) + { + evt = { EV_KeyDown, 0, (int16_t)KEY_MWHEELDOWN }; + D_PostEvent(&evt); + } + if (ev.wheel.x > 0) + { + evt = { EV_KeyDown, 0, (int16_t)KEY_MWHEELRIGHT }; + D_PostEvent(&evt); + } + if (ev.wheel.y < 0) + { + evt = { EV_KeyDown, 0, (int16_t)KEY_MWHEELLEFT }; + D_PostEvent(&evt); + } + } + break; + + case SDL_WINDOWEVENT: + switch (ev.window.event) + { + case SDL_WINDOWEVENT_FOCUS_GAINED: + case SDL_WINDOWEVENT_FOCUS_LOST: + appactive = (ev.window.event == SDL_WINDOWEVENT_FOCUS_GAINED); + if (g_mouseGrabbed && g_mouseEnabled) + grabmouse_low(appactive); + break; + + case SDL_WINDOWEVENT_MOVED: + { + WindowMoved(ev.window.data1, ev.window.data2); + break; + } + case SDL_WINDOWEVENT_ENTER: + g_mouseInsideWindow = 1; + break; + case SDL_WINDOWEVENT_LEAVE: + g_mouseInsideWindow = 0; + break; + } + + break; + + case SDL_MOUSEMOTION: + //case SDL_JOYBALLMOTION: + { + // The menus need this, even in non GUI-capture mode + event_t event; + event.data1 = ev.motion.x; + event.data2 = ev.motion.y; + + //screen->ScaleCoordsFromWindow(event.data1, event.data2); + + event.type = EV_GUI_Event; + event.subtype = EV_GUI_MouseMove; + + SDL_Keymod kmod = SDL_GetModState(); + event.data3 = ((kmod & KMOD_SHIFT) ? GKM_SHIFT : 0) | + ((kmod & KMOD_CTRL) ? GKM_CTRL : 0) | + ((kmod & KMOD_ALT) ? GKM_ALT : 0); + + D_PostEvent(&event); + break; + } + + case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEBUTTONUP: + + if (!GUICapture) + { + evt.type = ev.type == SDL_MOUSEBUTTONDOWN ? EV_KeyDown : EV_KeyUp; + + switch (ev.button.button) + { + case SDL_BUTTON_LEFT: evt.data1 = KEY_MOUSE1; break; + case SDL_BUTTON_MIDDLE: evt.data1 = KEY_MOUSE3; break; + case SDL_BUTTON_RIGHT: evt.data1 = KEY_MOUSE2; break; + case SDL_BUTTON_X1: evt.data1 = KEY_MOUSE4; break; + case SDL_BUTTON_X2: evt.data1 = KEY_MOUSE5; break; + case 6: evt.data1 = KEY_MOUSE6; break; + case 7: evt.data1 = KEY_MOUSE7; break; + case 8: evt.data1 = KEY_MOUSE8; break; + //default: printf("SDL mouse button %s %d\n", sev.type == SDL_MOUSEBUTTONDOWN ? "down" : "up", sev.button.button); break; + } + + if (evt.data1 != 0) + { + D_PostEvent(&evt); + } + } + else if ((ev.button.button >= SDL_BUTTON_LEFT && ev.button.button <= SDL_BUTTON_X2)) + { + int x, y; + SDL_GetMouseState(&x, &y); + + evt.type = EV_GUI_Event; + evt.data1 = x; + evt.data2 = y; + + //screen->ScaleCoordsFromWindow(event.data1, event.data2); + + if (ev.type == SDL_MOUSEBUTTONDOWN) + { + switch (ev.button.button) + { + case SDL_BUTTON_LEFT: evt.subtype = EV_GUI_LButtonDown; break; + case SDL_BUTTON_MIDDLE: evt.subtype = EV_GUI_MButtonDown; break; + case SDL_BUTTON_RIGHT: evt.subtype = EV_GUI_RButtonDown; break; + case SDL_BUTTON_X1: evt.subtype = EV_GUI_BackButtonDown; break; + case SDL_BUTTON_X2: evt.subtype = EV_GUI_FwdButtonDown; break; + default: assert(false); evt.subtype = EV_GUI_None; break; + } + } + else + { + switch (ev.button.button) + { + case SDL_BUTTON_LEFT: evt.subtype = EV_GUI_LButtonUp; break; + case SDL_BUTTON_MIDDLE: evt.subtype = EV_GUI_MButtonUp; break; + case SDL_BUTTON_RIGHT: evt.subtype = EV_GUI_RButtonUp; break; + case SDL_BUTTON_X1: evt.subtype = EV_GUI_BackButtonUp; break; + case SDL_BUTTON_X2: evt.subtype = EV_GUI_FwdButtonUp; break; + default: assert(false); evt.subtype = EV_GUI_None; break; + } + } + + SDL_Keymod kmod = SDL_GetModState(); + evt.data3 = ((kmod & KMOD_SHIFT) ? GKM_SHIFT : 0) | + ((kmod & KMOD_CTRL) ? GKM_CTRL : 0) | + ((kmod & KMOD_ALT) ? GKM_ALT : 0); + + D_PostEvent(&evt); + } + break; + +#if 0 + case SDL_JOYAXISMOTION: +#if SDL_MAJOR_VERSION >= 2 + if (joystick.isGameController) + break; + fallthrough__; + case SDL_CONTROLLERAXISMOTION: +#endif + if (appactive && ev.jaxis.axis < joystick.numAxes) + { + joystick.pAxis[ev.jaxis.axis] = ev.jaxis.value; + int32_t const scaledValue = ev.jaxis.value * 10000 / 32767; + if ((scaledValue < joydead[ev.jaxis.axis]) && + (scaledValue > -joydead[ev.jaxis.axis])) + joystick.pAxis[ev.jaxis.axis] = 0; + else if (scaledValue >= joysatur[ev.jaxis.axis]) + joystick.pAxis[ev.jaxis.axis] = 32767; + else if (scaledValue <= -joysatur[ev.jaxis.axis]) + joystick.pAxis[ev.jaxis.axis] = -32767; + else + joystick.pAxis[ev.jaxis.axis] = joystick.pAxis[ev.jaxis.axis] * 10000 / joysatur[ev.jaxis.axis]; + } + break; +#endif + + case SDL_JOYHATMOTION: + { + int32_t hatvals[16] = { + -1, // centre + 0, // up 1 + 9000, // right 2 + 4500, // up+right 3 + 18000, // down 4 + -1, // down+up!! 5 + 13500, // down+right 6 + -1, // down+right+up!! 7 + 27000, // left 8 + 27500, // left+up 9 + -1, // left+right!! 10 + -1, // left+right+up!! 11 + 22500, // left+down 12 + -1, // left+down+up!! 13 + -1, // left+down+right!! 14 + -1, // left+down+right+up!! 15 + }; + if (appactive && ev.jhat.hat < joystick.numHats) + joystick.pHat[ev.jhat.hat] = hatvals[ev.jhat.value & 15]; + break; + } + + case SDL_JOYBUTTONDOWN: + case SDL_JOYBUTTONUP: + if (!GUICapture) + { + evt.type = ev.type == SDL_JOYBUTTONDOWN ? EV_KeyDown : EV_KeyUp; + evt.data1 = KEY_FIRSTJOYBUTTON + ev.jbutton.button; + if (evt.data1 != 0) + D_PostEvent(&evt); + } + break; + + case SDL_QUIT: + throw ExitEvent(0); // completely bypass the hackery in the games to block Alt-F4. + return -1; + + default: + break; + } + } + MouseRead(); + + return rv; +} + +int32_t handleevents(void) +{ + int32_t rv; + + if (inputchecked && g_mouseEnabled) + { + // This is a horrible crutch + if (inputState.mouseReadButtons() & WHEELUP_MOUSE) + { + event_t ev = { EV_KeyUp, 0, (int16_t)KEY_MWHEELUP }; + D_PostEvent(&ev); + } + if (inputState.mouseReadButtons() & WHEELDOWN_MOUSE) + { + event_t ev = { EV_KeyUp, 0, (int16_t)KEY_MWHEELDOWN }; + D_PostEvent(&ev); + } + if (inputState.mouseReadButtons() & WHEELLEFT_MOUSE) + { + event_t ev = { EV_KeyUp, 0, (int16_t)KEY_MWHEELLEFT }; + D_PostEvent(&ev); + } + if (inputState.mouseReadButtons() & WHEELRIGHT_MOUSE) + { + event_t ev = { EV_KeyUp, 0, (int16_t)KEY_MWHEELRIGHT }; + D_PostEvent(&ev); + } + } + + rv = handleevents_pollsdl(); + + inputchecked = 0; + timerUpdateClock(); + //I_ProcessJoysticks(); + return rv; +} + + +int32_t handleevents_peekkeys(void) +{ + SDL_PumpEvents(); + + return SDL_PeepEvents(NULL, 1, SDL_PEEKEVENT, SDL_KEYDOWN, SDL_KEYDOWN); +} + +void I_SetMouseCapture() +{ + // Clear out any mouse movement. + SDL_CaptureMouse(SDL_TRUE); +} + +void I_ReleaseMouseCapture() +{ + SDL_CaptureMouse(SDL_FALSE); +} diff --git a/source/common/input/i_joystick.cpp b/source/common/input/i_joystick.cpp new file mode 100644 index 000000000..695a44686 --- /dev/null +++ b/source/common/input/i_joystick.cpp @@ -0,0 +1,342 @@ +/* +** i_joystick.cpp +** +**--------------------------------------------------------------------------- +** Copyright 2005-2016 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ +#include <SDL.h> + +#include "basics.h" +#include "templates.h" +#include "m_joy.h" +#include "keydef.h" + +// Very small deadzone so that floating point magic doesn't happen +#define MIN_DEADZONE 0.000001f + +class SDLInputJoystick: public IJoystickConfig +{ +public: + SDLInputJoystick(int DeviceIndex) : DeviceIndex(DeviceIndex), Multiplier(1.0f) + { + Device = SDL_JoystickOpen(DeviceIndex); + if(Device != NULL) + { + NumAxes = SDL_JoystickNumAxes(Device); + NumHats = SDL_JoystickNumHats(Device); + + SetDefaultConfig(); + } + } + ~SDLInputJoystick() + { + if(Device != NULL) + M_SaveJoystickConfig(this); + SDL_JoystickClose(Device); + } + + bool IsValid() const + { + return Device != NULL; + } + + FString GetName() + { + return SDL_JoystickName(Device); + } + float GetSensitivity() + { + return Multiplier; + } + void SetSensitivity(float scale) + { + Multiplier = scale; + } + + int GetNumAxes() + { + return NumAxes + NumHats*2; + } + float GetAxisDeadZone(int axis) + { + return Axes[axis].DeadZone; + } + EJoyAxis GetAxisMap(int axis) + { + return Axes[axis].GameAxis; + } + const char *GetAxisName(int axis) + { + return Axes[axis].Name.GetChars(); + } + float GetAxisScale(int axis) + { + return Axes[axis].Multiplier; + } + + void SetAxisDeadZone(int axis, float zone) + { + Axes[axis].DeadZone = clamp(zone, MIN_DEADZONE, 1.f); + } + void SetAxisMap(int axis, EJoyAxis gameaxis) + { + Axes[axis].GameAxis = gameaxis; + } + void SetAxisScale(int axis, float scale) + { + Axes[axis].Multiplier = scale; + } + + // Used by the saver to not save properties that are at their defaults. + bool IsSensitivityDefault() + { + return Multiplier == 1.0f; + } + bool IsAxisDeadZoneDefault(int axis) + { + return Axes[axis].DeadZone <= MIN_DEADZONE; + } + bool IsAxisMapDefault(int axis) + { + if(axis >= 5) + return Axes[axis].GameAxis == JOYAXIS_None; + return Axes[axis].GameAxis == DefaultAxes[axis]; + } + bool IsAxisScaleDefault(int axis) + { + return Axes[axis].Multiplier == 1.0f; + } + + void SetDefaultConfig() + { + for(int i = 0;i < GetNumAxes();i++) + { + AxisInfo info; + if(i < NumAxes) + info.Name.Format("Axis %d", i+1); + else + info.Name.Format("Hat %d (%c)", (i-NumAxes)/2 + 1, (i-NumAxes)%2 == 0 ? 'x' : 'y'); + info.DeadZone = MIN_DEADZONE; + info.Multiplier = 1.0f; + info.Value = 0.0; + info.ButtonValue = 0; + if(i >= 5) + info.GameAxis = JOYAXIS_None; + else + info.GameAxis = DefaultAxes[i]; + Axes.Push(info); + } + } + FString GetIdentifier() + { + char id[16]; + snprintf(id, countof(id), "JS:%d", DeviceIndex); + return id; + } + + void AddAxes(float axes[NUM_JOYAXIS]) + { + // Add to game axes. + for (int i = 0; i < GetNumAxes(); ++i) + { + if(Axes[i].GameAxis != JOYAXIS_None) + axes[Axes[i].GameAxis] -= float(Axes[i].Value * Multiplier * Axes[i].Multiplier); + } + } + + void ProcessInput() + { + uint8_t buttonstate; + + for (int i = 0; i < NumAxes; ++i) + { + buttonstate = 0; + + Axes[i].Value = SDL_JoystickGetAxis(Device, i)/32767.0; + Axes[i].Value = Joy_RemoveDeadZone(Axes[i].Value, Axes[i].DeadZone, &buttonstate); + + // Map button to axis + // X and Y are handled differently so if we have 2 or more axes then we'll use that code instead. + if (NumAxes == 1 || (i >= 2 && i < NUM_JOYAXISBUTTONS)) + { + Joy_GenerateButtonEvents(Axes[i].ButtonValue, buttonstate, 2, KEY_JOYAXIS1PLUS + i*2); + Axes[i].ButtonValue = buttonstate; + } + } + + if(NumAxes > 1) + { + buttonstate = Joy_XYAxesToButtons(Axes[0].Value, Axes[1].Value); + Joy_GenerateButtonEvents(Axes[0].ButtonValue, buttonstate, 4, KEY_JOYAXIS1PLUS); + Axes[0].ButtonValue = buttonstate; + } + + // Map POV hats to buttons and axes. Why axes? Well apparently I have + // a gamepad where the left control stick is a POV hat (instead of the + // d-pad like you would expect, no that's pressure sensitive). Also + // KDE's joystick dialog maps them to axes as well. + for (int i = 0; i < NumHats; ++i) + { + AxisInfo &x = Axes[NumAxes + i*2]; + AxisInfo &y = Axes[NumAxes + i*2 + 1]; + + buttonstate = SDL_JoystickGetHat(Device, i); + + // If we're going to assume that we can pass SDL's value into + // Joy_GenerateButtonEvents then we might as well assume the format here. + if(buttonstate & 0x1) // Up + y.Value = -1.0; + else if(buttonstate & 0x4) // Down + y.Value = 1.0; + else + y.Value = 0.0; + if(buttonstate & 0x2) // Left + x.Value = 1.0; + else if(buttonstate & 0x8) // Right + x.Value = -1.0; + else + x.Value = 0.0; + + if(i < 4) + { + Joy_GenerateButtonEvents(x.ButtonValue, buttonstate, 4, KEY_JOYPOV1_UP + i*4); + x.ButtonValue = buttonstate; + } + } + } + +protected: + struct AxisInfo + { + FString Name; + float DeadZone; + float Multiplier; + EJoyAxis GameAxis; + double Value; + uint8_t ButtonValue; + }; + static const EJoyAxis DefaultAxes[5]; + + int DeviceIndex; + SDL_Joystick *Device; + + float Multiplier; + TArray<AxisInfo> Axes; + int NumAxes; + int NumHats; +}; +const EJoyAxis SDLInputJoystick::DefaultAxes[5] = {JOYAXIS_Side, JOYAXIS_Forward, JOYAXIS_Pitch, JOYAXIS_Yaw, JOYAXIS_Up}; + +class SDLInputJoystickManager +{ +public: + SDLInputJoystickManager() + { + for(int i = 0;i < SDL_NumJoysticks();i++) + { + SDLInputJoystick *device = new SDLInputJoystick(i); + if(device->IsValid()) + Joysticks.Push(device); + else + delete device; + } + } + ~SDLInputJoystickManager() + { + for(unsigned int i = 0;i < Joysticks.Size();i++) + delete Joysticks[i]; + } + + void AddAxes(float axes[NUM_JOYAXIS]) + { + for(unsigned int i = 0;i < Joysticks.Size();i++) + Joysticks[i]->AddAxes(axes); + } + void GetDevices(TArray<IJoystickConfig *> &sticks) + { + for(unsigned int i = 0;i < Joysticks.Size();i++) + { + M_LoadJoystickConfig(Joysticks[i]); + sticks.Push(Joysticks[i]); + } + } + + void ProcessInput() const + { + for(unsigned int i = 0;i < Joysticks.Size();++i) + Joysticks[i]->ProcessInput(); + } +protected: + TArray<SDLInputJoystick *> Joysticks; +}; +static SDLInputJoystickManager *JoystickManager; + +void I_StartupJoysticks() +{ + if(SDL_InitSubSystem(SDL_INIT_JOYSTICK) >= 0) + JoystickManager = new SDLInputJoystickManager(); +} +void I_ShutdownInput() +{ + if(JoystickManager) + { + delete JoystickManager; + SDL_QuitSubSystem(SDL_INIT_JOYSTICK); + } +} + +void I_GetJoysticks(TArray<IJoystickConfig *> &sticks) +{ + sticks.Clear(); + + JoystickManager->GetDevices(sticks); +} + +void I_GetAxes(float axes[NUM_JOYAXIS]) +{ + for (int i = 0; i < NUM_JOYAXIS; ++i) + { + axes[i] = 0; + } + if (use_joystick) + { + JoystickManager->AddAxes(axes); + } +} + +void I_ProcessJoysticks() +{ + if (use_joystick && JoystickManager) + JoystickManager->ProcessInput(); +} + +IJoystickConfig *I_UpdateDeviceList() +{ + return NULL; +} diff --git a/source/common/input/m_joy.cpp b/source/common/input/m_joy.cpp new file mode 100644 index 000000000..bcdb429d0 --- /dev/null +++ b/source/common/input/m_joy.cpp @@ -0,0 +1,331 @@ +/* +** +** +**--------------------------------------------------------------------------- +** Copyright 2005-2016 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ +// HEADER FILES ------------------------------------------------------------ + +#include <math.h> +#include "vectors.h" +#include "m_joy.h" +#include "gameconfigfile.h" +#include "d_event.h" + +// MACROS ------------------------------------------------------------------ + +// TYPES ------------------------------------------------------------------- + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +#if 0 +EXTERN_CVAR(Bool, joy_ps2raw) +EXTERN_CVAR(Bool, joy_dinput) +EXTERN_CVAR(Bool, joy_xinput) +#endif + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +CUSTOM_CVAR(Bool, use_joystick, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG|CVAR_NOINITCALL) +{ +#if 0//def _WIN32 + joy_ps2raw.Callback(); + joy_dinput.Callback(); + joy_xinput.Callback(); +#endif +} + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +// Bits 0 is X+, 1 is X-, 2 is Y+, and 3 is Y-. +static uint8_t JoyAngleButtons[8] = { 1, 1+4, 4, 2+4, 2, 2+8, 8, 1+8 }; + +// CODE -------------------------------------------------------------------- + +//========================================================================== +// +// IJoystickConfig - Virtual Destructor +// +//========================================================================== + +IJoystickConfig::~IJoystickConfig() +{ +} + +//========================================================================== +// +// M_SetJoystickConfigSection +// +// Sets up the config for reading or writing this controller's axis config. +// +//========================================================================== + +static bool M_SetJoystickConfigSection(IJoystickConfig *joy, bool create) +{ + FString id = "Joy:"; + id += joy->GetIdentifier(); + return GameConfig->SetSection(id, create); +} + +//========================================================================== +// +// M_LoadJoystickConfig +// +//========================================================================== + +bool M_LoadJoystickConfig(IJoystickConfig *joy) +{ + char key[32]; + const char *value; + int axislen; + int numaxes; + + joy->SetDefaultConfig(); + if (!M_SetJoystickConfigSection(joy, false)) + { + return false; + } + value = GameConfig->GetValueForKey("Sensitivity"); + if (value != NULL) + { + joy->SetSensitivity((float)atof(value)); + } + numaxes = joy->GetNumAxes(); + for (int i = 0; i < numaxes; ++i) + { + axislen = snprintf(key, countof(key), "Axis%u", i); + + snprintf(key + axislen, countof(key) - axislen, "deadzone"); + value = GameConfig->GetValueForKey(key); + if (value != NULL) + { + joy->SetAxisDeadZone(i, (float)atof(value)); + } + + snprintf(key + axislen, countof(key) - axislen, "scale"); + value = GameConfig->GetValueForKey(key); + if (value != NULL) + { + joy->SetAxisScale(i, (float)atof(value)); + } + + snprintf(key + axislen, countof(key) - axislen, "map"); + value = GameConfig->GetValueForKey(key); + if (value != NULL) + { + EJoyAxis gameaxis = (EJoyAxis)atoi(value); + if (gameaxis < JOYAXIS_None || gameaxis >= NUM_JOYAXIS) + { + gameaxis = JOYAXIS_None; + } + joy->SetAxisMap(i, gameaxis); + } + } + return true; +} + +//========================================================================== +// +// M_SaveJoystickConfig +// +// Only saves settings that are not at their defaults. +// +//========================================================================== + +void M_SaveJoystickConfig(IJoystickConfig *joy) +{ + char key[32], value[32]; + int axislen, numaxes; + + if (M_SetJoystickConfigSection(joy, true)) + { + GameConfig->ClearCurrentSection(); + if (!joy->IsSensitivityDefault()) + { + snprintf(value, countof(value), "%g", joy->GetSensitivity()); + GameConfig->SetValueForKey("Sensitivity", value); + } + numaxes = joy->GetNumAxes(); + for (int i = 0; i < numaxes; ++i) + { + axislen = snprintf(key, countof(key), "Axis%u", i); + + if (!joy->IsAxisDeadZoneDefault(i)) + { + snprintf(key + axislen, countof(key) - axislen, "deadzone"); + snprintf(value, countof(value), "%g", joy->GetAxisDeadZone(i)); + GameConfig->SetValueForKey(key, value); + } + if (!joy->IsAxisScaleDefault(i)) + { + snprintf(key + axislen, countof(key) - axislen, "scale"); + snprintf(value, countof(value), "%g", joy->GetAxisScale(i)); + GameConfig->SetValueForKey(key, value); + } + if (!joy->IsAxisMapDefault(i)) + { + snprintf(key + axislen, countof(key) - axislen, "map"); + snprintf(value, countof(value), "%d", joy->GetAxisMap(i)); + GameConfig->SetValueForKey(key, value); + } + } + // If the joystick is entirely at its defaults, delete this section + // so that we don't write out a lone section header. + if (GameConfig->SectionIsEmpty()) + { + GameConfig->DeleteCurrentSection(); + } + } +} + + +//=========================================================================== +// +// Joy_RemoveDeadZone +// +//=========================================================================== + +double Joy_RemoveDeadZone(double axisval, double deadzone, uint8_t *buttons) +{ + uint8_t butt; + + // Cancel out deadzone. + if (fabs(axisval) < deadzone) + { + axisval = 0; + butt = 0; + } + // Make the dead zone the new 0. + else if (axisval < 0) + { + axisval = (axisval + deadzone) / (1.0 - deadzone); + butt = 2; // button minus + } + else + { + axisval = (axisval - deadzone) / (1.0 - deadzone); + butt = 1; // button plus + } + if (buttons != NULL) + { + *buttons = butt; + } + return axisval; +} + +//=========================================================================== +// +// Joy_XYAxesToButtons +// +// Given two axes, returns a button set for them. They should have already +// been sent through Joy_RemoveDeadZone. For axes that share the same +// physical stick, the angle the stick forms should determine whether or +// not the four component buttons are present. Going by deadzone alone gives +// you huge areas where you have to buttons pressed and thin strips where +// you only have one button pressed. For DirectInput gamepads, there is +// not much standard for how the right stick is presented, so we can only +// do this for the left stick for those, since X and Y axes are pretty +// standard. For XInput and Raw PS2 controllers, both sticks are processed +// through here. +// +//=========================================================================== + +int Joy_XYAxesToButtons(double x, double y) +{ + if (x == 0 && y == 0) + { + return 0; + } + double rad = atan2(y, x); + if (rad < 0) + { + rad += 2*pi::pi(); + } + // The circle is divided into eight segments for corresponding + // button combinations. Each segment is pi/4 radians wide. We offset + // by half this so that the segments are centered around the ideal lines + // their buttons represent instead of being right on the lines. + rad += pi::pi()/8; // Offset + rad *= 4/pi::pi(); // Convert range from [0,2pi) to [0,8) + return JoyAngleButtons[int(rad) & 7]; +} + +//=========================================================================== +// +// Joy_GenerateButtonEvents +// +// Provided two bitmasks for a set of buttons, generates events to reflect +// any changes from the old to new set, where base is the key for bit 0, +// base+1 is the key for bit 1, etc. +// +//=========================================================================== + +void Joy_GenerateButtonEvents(int oldbuttons, int newbuttons, int numbuttons, int base) +{ + int changed = oldbuttons ^ newbuttons; + if (changed != 0) + { + event_t ev = { 0, 0, 0, 0, 0, 0, 0 }; + int mask = 1; + for (int j = 0; j < numbuttons; mask <<= 1, ++j) + { + if (changed & mask) + { + ev.data1 = base + j; + ev.type = (newbuttons & mask) ? EV_KeyDown : EV_KeyUp; + D_PostEvent(&ev); + } + } + } +} + +void Joy_GenerateButtonEvents(int oldbuttons, int newbuttons, int numbuttons, const int *keys) +{ + int changed = oldbuttons ^ newbuttons; + if (changed != 0) + { + event_t ev = { 0, 0, 0, 0, 0, 0, 0 }; + int mask = 1; + for (int j = 0; j < numbuttons; mask <<= 1, ++j) + { + if (changed & mask) + { + ev.data1 = keys[j]; + ev.type = (newbuttons & mask) ? EV_KeyDown : EV_KeyUp; + D_PostEvent(&ev); + } + } + } +} diff --git a/source/common/input/m_joy.h b/source/common/input/m_joy.h new file mode 100644 index 000000000..90cea3e14 --- /dev/null +++ b/source/common/input/m_joy.h @@ -0,0 +1,65 @@ +#ifndef M_JOY_H +#define M_JOY_H + +#include <stdint.h> +#include "tarray.h" +#include "c_cvars.h" + +enum EJoyAxis +{ + JOYAXIS_None = -1, + JOYAXIS_Yaw, + JOYAXIS_Pitch, + JOYAXIS_Forward, + JOYAXIS_Side, + JOYAXIS_Up, +// JOYAXIS_Roll, // Ha ha. No roll for you. + NUM_JOYAXIS, +}; + +// Generic configuration interface for a controller. +struct IJoystickConfig +{ + virtual ~IJoystickConfig() = 0; + + virtual FString GetName() = 0; + virtual float GetSensitivity() = 0; + virtual void SetSensitivity(float scale) = 0; + + virtual int GetNumAxes() = 0; + virtual float GetAxisDeadZone(int axis) = 0; + virtual EJoyAxis GetAxisMap(int axis) = 0; + virtual const char *GetAxisName(int axis) = 0; + virtual float GetAxisScale(int axis) = 0; + + virtual void SetAxisDeadZone(int axis, float zone) = 0; + virtual void SetAxisMap(int axis, EJoyAxis gameaxis) = 0; + virtual void SetAxisScale(int axis, float scale) = 0; + + // Used by the saver to not save properties that are at their defaults. + virtual bool IsSensitivityDefault() = 0; + virtual bool IsAxisDeadZoneDefault(int axis) = 0; + virtual bool IsAxisMapDefault(int axis) = 0; + virtual bool IsAxisScaleDefault(int axis) = 0; + + virtual void SetDefaultConfig() = 0; + virtual FString GetIdentifier() = 0; +}; + +EXTERN_CVAR(Bool, use_joystick); + +bool M_LoadJoystickConfig(IJoystickConfig *joy); +void M_SaveJoystickConfig(IJoystickConfig *joy); + +void Joy_GenerateButtonEvents(int oldbuttons, int newbuttons, int numbuttons, int base); +void Joy_GenerateButtonEvents(int oldbuttons, int newbuttons, int numbuttons, const int *keys); +int Joy_XYAxesToButtons(double x, double y); +double Joy_RemoveDeadZone(double axisval, double deadzone, uint8_t *buttons); + +// These ought to be provided by a system-specific i_input.cpp. +void I_GetAxes(float axes[NUM_JOYAXIS]); +void I_GetJoysticks(TArray<IJoystickConfig *> &sticks); +IJoystickConfig *I_UpdateDeviceList(); +extern void UpdateJoystickMenu(IJoystickConfig *); + +#endif diff --git a/source/common/inputstate.cpp b/source/common/inputstate.cpp index f5a3e6ced..ef7c45452 100644 --- a/source/common/inputstate.cpp +++ b/source/common/inputstate.cpp @@ -83,6 +83,8 @@ void InputState::AddEvent(const event_t *ev) case KEY_MOUSE4 : mouseSetBit(THUMB_MOUSE, ev->type == EV_KeyDown); break; case KEY_MWHEELUP: mouseSetBit(WHEELUP_MOUSE, ev->type == EV_KeyDown); break; case KEY_MWHEELDOWN: mouseSetBit(WHEELDOWN_MOUSE, ev->type == EV_KeyDown); break; + case KEY_MWHEELLEFT: mouseSetBit(WHEELLEFT_MOUSE, ev->type == EV_KeyDown); break; + case KEY_MWHEELRIGHT: mouseSetBit(WHEELRIGHT_MOUSE, ev->type == EV_KeyDown); break; case KEY_MOUSE5: mouseSetBit(THUMB2_MOUSE, ev->type == EV_KeyDown); break; default: break; } diff --git a/source/common/inputstate.h b/source/common/inputstate.h index 12b973ca3..6325c305f 100644 --- a/source/common/inputstate.h +++ b/source/common/inputstate.h @@ -39,6 +39,8 @@ enum EMouseBits WHEELUP_MOUSE = 16, WHEELDOWN_MOUSE= 32, THUMB2_MOUSE = 64, + WHEELLEFT_MOUSE = 128, + WHEELRIGHT_MOUSE = 256, }; enum diff --git a/source/mact/src/control.cpp b/source/mact/src/control.cpp index 6eb85ce87..05aae41ac 100644 --- a/source/mact/src/control.cpp +++ b/source/mact/src/control.cpp @@ -18,8 +18,6 @@ #include "pragmas.h" bool CONTROL_Started = false; -bool CONTROL_MouseEnabled = false; -bool CONTROL_MousePresent = false; bool CONTROL_JoyPresent = false; bool CONTROL_JoystickEnabled = false; @@ -368,7 +366,7 @@ static void CONTROL_PollDevices(ControlInfo *info) { memset(info, 0, sizeof(ControlInfo)); - if (CONTROL_MouseEnabled) + if (in_mouse) inputState.GetMouseDelta(info); if (CONTROL_JoystickEnabled) @@ -531,8 +529,6 @@ bool CONTROL_Startup(controltype which, int32_t(*TimeFunction)(void), int32_t ti CONTROL_NumMouseButtons = MAXMOUSEBUTTONS; mouseInit(); - CONTROL_MousePresent = ((inputdevices & 2) == 2); - CONTROL_MouseEnabled = CONTROL_MousePresent; CONTROL_ResetJoystickValues();