- input code cleanup and addition of ZDoom's joystick code, which is not fully connected yet.

This commit is contained in:
Christoph Oelckers 2019-12-14 17:15:17 +01:00
parent f2dd7326d0
commit 91f83d4c55
15 changed files with 1611 additions and 1092 deletions

View file

@ -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 )

View file

@ -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.
//

View file

@ -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

View file

@ -531,7 +531,6 @@ void G_SaveConfig()
{
GameConfig->ArchiveGlobalData();
GameConfig->ArchiveGameData(GameName);
CONFIG_WriteControllerSettings();
GameConfig->WriteConfigFile();
delete GameConfig;
GameConfig = nullptr;

View file

@ -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]);
}
}
}

View file

@ -39,7 +39,6 @@ void CONFIG_SetDefaultKeys(const char *defbinds);
void CONFIG_SetupJoystick(void);
void CONFIG_WriteControllerSettings();
void CONFIG_InitMouseAndController();
void CONFIG_SetGameControllerDefaultsClear();

View file

@ -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")

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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);
}
}
}
}

View file

@ -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

View file

@ -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;
}

View file

@ -39,6 +39,8 @@ enum EMouseBits
WHEELUP_MOUSE = 16,
WHEELDOWN_MOUSE= 32,
THUMB2_MOUSE = 64,
WHEELLEFT_MOUSE = 128,
WHEELRIGHT_MOUSE = 256,
};
enum

View file

@ -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();