#include "quakedef.h" #include #if SDL_VERSION_ATLEAST(2,0,6) && defined(VKQUAKE) #include #include "../vk/vkrenderer.h" #endif #if SDL_MAJOR_VERSION >=2 extern SDL_Window *sdlwindow; #else extern SDL_Surface *sdlsurf; #endif qboolean mouseactive; extern qboolean mouseusedforgui; extern qboolean vid_isfullscreen; #if SDL_MAJOR_VERSION > 1 || (SDL_MAJOR_VERSION == 1 && SDL_MINOR_VERSION >= 3) #define HAVE_SDL_TEXTINPUT cvar_t sys_osk = CVARD("sys_osk", "0", "Enables support for text input. This will be ignored when the console has focus, but gamecode may end up with composition boxes appearing."); #endif void IN_ActivateMouse(void) { if (mouseactive) return; mouseactive = true; SDL_ShowCursor(0); #if SDL_MAJOR_VERSION >= 2 SDL_SetRelativeMouseMode(true); SDL_SetWindowGrab(sdlwindow, true); #else SDL_WM_GrabInput(SDL_GRAB_ON); #endif } void IN_DeactivateMouse(void) { if (!mouseactive) return; mouseactive = false; SDL_ShowCursor(1); #if SDL_MAJOR_VERSION >= 2 SDL_SetRelativeMouseMode(false); SDL_SetWindowGrab(sdlwindow, false); #else SDL_WM_GrabInput(SDL_GRAB_OFF); #endif } #if SDL_MAJOR_VERSION >= 2 #define MAX_FINGERS 16 //zomg! mutant! static struct sdlfinger_s { qboolean active; SDL_JoystickID jid; SDL_TouchID tid; SDL_FingerID fid; } sdlfinger[MAX_FINGERS]; //sanitizes sdl fingers into touch events that our engine can eat. //we don't really deal with different devices, we just munge the lot into a single thing (allowing fingers to be tracked, at least if splitscreen isn't active). static uint32_t SDL_GiveFinger(SDL_JoystickID jid, SDL_TouchID tid, SDL_FingerID fid, qboolean fingerraised) { uint32_t f; for (f = 0; f < countof(sdlfinger); f++) { if (sdlfinger[f].active) { if (sdlfinger[f].jid == jid && sdlfinger[f].tid == tid && sdlfinger[f].fid == fid) { sdlfinger[f].active = !fingerraised; return f; } } } for (f = 0; f < countof(sdlfinger); f++) { if (!sdlfinger[f].active) { sdlfinger[f].active = !fingerraised; sdlfinger[f].jid = jid; sdlfinger[f].tid = tid; sdlfinger[f].fid = fid; return f; } } return f; } #endif #if SDL_MAJOR_VERSION >= 2 #define MAX_JOYSTICKS 16 #ifndef MAXJOYAXIS #define MAXJOYAXIS 6 #endif static struct { int qaxis; keynum_t pos, neg; } gpaxismap[] = { {/*SDL_CONTROLLER_AXIS_LEFTX*/ GPAXIS_LT_RIGHT, K_GP_LEFT_THUMB_RIGHT, K_GP_LEFT_THUMB_LEFT}, {/*SDL_CONTROLLER_AXIS_LEFTY*/ GPAXIS_LT_DOWN, K_GP_LEFT_THUMB_DOWN, K_GP_LEFT_THUMB_UP}, {/*SDL_CONTROLLER_AXIS_RIGHTX*/ GPAXIS_RT_RIGHT, K_GP_RIGHT_THUMB_RIGHT, K_GP_RIGHT_THUMB_LEFT}, {/*SDL_CONTROLLER_AXIS_RIGHTY*/ GPAXIS_RT_DOWN, K_GP_RIGHT_THUMB_DOWN, K_GP_RIGHT_THUMB_UP}, {/*SDL_CONTROLLER_AXIS_TRIGGERLEFT*/ GPAXIS_LT_TRIGGER, K_GP_LEFT_TRIGGER, 0}, {/*SDL_CONTROLLER_AXIS_TRIGGERRIGHT*/ GPAXIS_RT_TRIGGER, K_GP_RIGHT_TRIGGER, 0}, }; static const int gpbuttonmap[] = { K_GP_A, K_GP_B, K_GP_X, K_GP_Y, K_GP_BACK, K_GP_GUIDE, K_GP_START, K_GP_LEFT_STICK, K_GP_RIGHT_STICK, K_GP_LEFT_SHOULDER, K_GP_RIGHT_SHOULDER, K_GP_DPAD_UP, K_GP_DPAD_DOWN, K_GP_DPAD_LEFT, K_GP_DPAD_RIGHT, K_GP_MISC1, K_GP_PADDLE1, K_GP_PADDLE2, K_GP_PADDLE3, K_GP_PADDLE4, K_GP_TOUCHPAD }; static struct sdljoy_s { //fte doesn't distinguish between joysticks and controllers. //in sdl, controllers are some glorified version of joysticks apparently. char *devname; SDL_Joystick *joystick; SDL_GameController *controller; SDL_JoystickID id; unsigned int qdevid; //axis junk unsigned int axistobuttonp; unsigned int axistobuttonn; float repeattime[countof(gpaxismap)]; //button junk unsigned int buttonheld; float buttonrepeat[countof(gpbuttonmap)]; } sdljoy[MAX_JOYSTICKS]; static cvar_t joy_only = CVARD("joyonly", "0", "If true, treats \"game controllers\" as regular joysticks.\nMust be set at startup."); //the enumid is the value for the open function rather than the working id. static void J_AllocateDevID(struct sdljoy_s *joy) { extern cvar_t in_skipplayerone; unsigned int id = (in_skipplayerone.ival?1:0), j; for (j = 0; j < MAX_JOYSTICKS;) { if (sdljoy[j++].qdevid == id) { j = 0; id++; } } joy->qdevid = id; #if SDL_VERSION_ATLEAST(2,0,14) if (joy->controller) { //enable some sensors if they're there. because we can. if (SDL_GameControllerHasSensor(joy->controller, SDL_SENSOR_ACCEL)) SDL_GameControllerSetSensorEnabled(joy->controller, SDL_SENSOR_ACCEL, SDL_TRUE); if (SDL_GameControllerHasSensor(joy->controller, SDL_SENSOR_GYRO)) SDL_GameControllerSetSensorEnabled(joy->controller, SDL_SENSOR_GYRO, SDL_TRUE); } #endif } static void J_ControllerAdded(int enumid) { const char *cname; int i; if (joy_only.ival) return; for (i = 0; i < MAX_JOYSTICKS; i++) if (sdljoy[i].controller == NULL && sdljoy[i].joystick == NULL) break; if (i == MAX_JOYSTICKS) return; sdljoy[i].controller = SDL_GameControllerOpen(enumid); if (!sdljoy[i].controller) return; sdljoy[i].joystick = SDL_GameControllerGetJoystick(sdljoy[i].controller); sdljoy[i].id = SDL_JoystickInstanceID(sdljoy[i].joystick); cname = SDL_GameControllerName(sdljoy[i].controller); if (!cname) cname = "Unknown Controller"; Con_Printf("Found new controller (%i): %s\n", i, cname); sdljoy[i].devname = Z_StrDup(cname); sdljoy[i].qdevid = DEVID_UNSET; } static void J_JoystickAdded(int enumid) { const char *cname; int i; for (i = 0; i < MAX_JOYSTICKS; i++) if (sdljoy[i].joystick == NULL && sdljoy[i].controller == NULL) break; if (i == MAX_JOYSTICKS) return; if (!joy_only.ival && SDL_IsGameController(enumid)) //if its reported via the gamecontroller api then use that instead. don't open it twice. return; sdljoy[i].joystick = SDL_JoystickOpen(enumid); if (!sdljoy[i].joystick) return; sdljoy[i].id = SDL_JoystickInstanceID(sdljoy[i].joystick); cname = SDL_JoystickName(sdljoy[i].joystick); if (!cname) cname = "Unknown Joystick"; Con_Printf("Found new joystick (%i): %s\n", i, cname); sdljoy[i].qdevid = DEVID_UNSET; } static struct sdljoy_s *J_DevId(SDL_JoystickID jid) { int i; for (i = 0; i < MAX_JOYSTICKS; i++) if (sdljoy[i].joystick && sdljoy[i].id == jid) return &sdljoy[i]; return NULL; } static void J_ControllerAxis(SDL_JoystickID jid, int axis, int value) { struct sdljoy_s *joy = J_DevId(jid); if (joy->qdevid == DEVID_UNSET) { if (abs(value) < 0x4000) return; //has to be big enough to handle any generous dead zone. J_AllocateDevID(joy); } if (joy && axis < countof(gpaxismap) && joy->qdevid != DEVID_UNSET) { if (value > 0x4000 && gpaxismap[axis].pos) { if (!(joy->axistobuttonp & (1u<qdevid, true, gpaxismap[axis].pos, 0); joy->repeattime[axis] = 1.0; } joy->axistobuttonp |= 1u<axistobuttonp & (1u<qdevid, false, gpaxismap[axis].pos, 0); joy->axistobuttonp &= ~(1u<axistobuttonn & (1u<qdevid, true, gpaxismap[axis].neg, 0); joy->repeattime[axis] = 1.0; } joy->axistobuttonn |= 1u<axistobuttonn & (1u<qdevid, false, gpaxismap[axis].neg, 0); joy->axistobuttonn &= ~(1u<qdevid, gpaxismap[axis].qaxis, value / 32767.0); } } static void J_JoystickAxis(SDL_JoystickID jid, int axis, int value) { struct sdljoy_s *joy = J_DevId(jid); if (joy->qdevid == DEVID_UNSET) { if (abs(value) < 0x1000) return; J_AllocateDevID(joy); } if (joy && !joy->controller && axis < MAXJOYAXIS && joy->qdevid != DEVID_UNSET) IN_JoystickAxisEvent(joy->qdevid, axis, value / 32767.0); } //we don't do hats and balls and stuff. static void J_ControllerButton(SDL_JoystickID jid, int button, qboolean pressed) { //controllers have reliable button maps. //but that doesn't meant that fte has specific k_ names for those buttons, but the mapping should be reliable, at least until they get mapped to proper k_ values. struct sdljoy_s *joy = J_DevId(jid); if (joy && button < countof(gpbuttonmap)) { if (joy->qdevid == DEVID_UNSET) { if (!pressed) return; J_AllocateDevID(joy); } if (pressed) joy->buttonheld |= (1u<buttonheld &= ~(1u<qdevid, pressed, gpbuttonmap[button], 0); joy->buttonrepeat[button] = 1.0; } } static void J_JoystickButton(SDL_JoystickID jid, int button, qboolean pressed) { //generic joysticks have no specific mappings. they're really random like that. static const int buttonmap[] = { K_JOY1, K_JOY2, K_JOY3, K_JOY4, K_JOY5, K_JOY6, K_JOY7, K_JOY8, K_JOY9, K_JOY10, K_JOY11, K_JOY12, K_JOY13, K_JOY14, K_JOY15, K_JOY16, K_JOY17, K_JOY18, K_JOY19, K_JOY20, K_JOY21, K_JOY22, K_JOY23, K_JOY24, K_JOY25, K_JOY26, K_JOY27, K_JOY28, K_JOY29, K_JOY30, K_JOY31, K_JOY32, K_AUX1, K_AUX2, K_AUX3, K_AUX4, K_AUX5, K_AUX6, K_AUX7, K_AUX8, K_AUX9, K_AUX10, K_AUX11, K_AUX12, K_AUX13, K_AUX14, K_AUX15, K_AUX16 }; struct sdljoy_s *joy = J_DevId(jid); if (joy && !joy->controller && button < sizeof(buttonmap)/sizeof(buttonmap[0])) { if (joy->qdevid == DEVID_UNSET) { if (!pressed) return; J_AllocateDevID(joy); } IN_KeyEvent(joy->qdevid, pressed, buttonmap[button], 0); } } enum controllertype_e INS_GetControllerType(int id) { #if SDL_VERSION_ATLEAST(2,0,12) int i; for (i = 0; i < MAX_JOYSTICKS; i++) { if (sdljoy[i].qdevid == id) { switch(SDL_GameControllerTypeForIndex(sdljoy[i].id)) { default: //for the future... case SDL_CONTROLLER_TYPE_UNKNOWN: return CONTROLLER_UNKNOWN; #if SDL_VERSION_ATLEAST(2,0,14) case SDL_CONTROLLER_TYPE_VIRTUAL: //don't really know... assume steaminput and thus steamdeck and thus xbox-like. return CONTROLLER_VIRTUAL; #endif case SDL_CONTROLLER_TYPE_XBOX360: case SDL_CONTROLLER_TYPE_XBOXONE: #if SDL_VERSION_ATLEAST(2,0,16) case SDL_CONTROLLER_TYPE_GOOGLE_STADIA: //close enough case SDL_CONTROLLER_TYPE_AMAZON_LUNA: //it'll do. I guess we're starting to see a standard here. #endif #if SDL_VERSION_ATLEAST(2,0,24) case SDL_CONTROLLER_TYPE_NVIDIA_SHIELD: #endif return CONTROLLER_XBOX; //a on bottom, b('cancel') to right case SDL_CONTROLLER_TYPE_PS3: case SDL_CONTROLLER_TYPE_PS4: #if SDL_VERSION_ATLEAST(2,0,14) case SDL_CONTROLLER_TYPE_PS5: #endif return CONTROLLER_PLAYSTATION; //weird indecipherable shapes. case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO: #if SDL_VERSION_ATLEAST(2,0,24) case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_LEFT: case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT: case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_PAIR: #endif return CONTROLLER_NINTENDO; //b on bottom, a('cancel') to right } } } #endif return 0; } void INS_Rumble(int id, quint16_t amp_low, quint16_t amp_high, quint32_t duration) { #if SDL_VERSION_ATLEAST(2,0,9) int i; if (duration > 10000) duration = 10000; for (i = 0; i < MAX_JOYSTICKS; i++) { if (sdljoy[i].qdevid == id) { SDL_GameControllerRumble(SDL_GameControllerFromInstanceID(sdljoy[i].id), amp_low, amp_high, duration); return; } } #else Con_DPrintf(CON_WARNING "Rumble is requires at least SDL 2.0.9\n"); #endif } void INS_RumbleTriggers(int id, quint16_t left, quint16_t right, quint32_t duration) { #if SDL_VERSION_ATLEAST(2,0,14) int i; if (duration > 10000) duration = 10000; for (i = 0; i < MAX_JOYSTICKS; i++) { if (sdljoy[i].qdevid == id) { SDL_GameControllerRumbleTriggers(SDL_GameControllerFromInstanceID(sdljoy[i].id), left, right, duration); return; } } #else Con_DPrintf(CON_WARNING "Trigger rumble is requires at least SDL 2.0.14\n"); #endif } void INS_SetLEDColor(int id, vec3_t color) { #if SDL_VERSION_ATLEAST(2,0,14) int i; /* maybe we'll eventually get sRGB LEDs */ color[0] *= 255.0f; color[1] *= 255.0f; color[2] *= 255.0f; for (i = 0; i < MAX_JOYSTICKS; i++) { if (sdljoy[i].qdevid == id) { SDL_GameControllerSetLED(SDL_GameControllerFromInstanceID(sdljoy[i].id), (uint8_t)color[0], (uint8_t)color[1], (uint8_t)color[2]); return; } } #else Con_DPrintf(CON_WARNING "Trigger rumble is requires at least SDL 2.0.14\n"); #endif } void INS_SetTriggerFX(int id, const void *data, size_t size) { #if SDL_VERSION_ATLEAST(2,0,15) int i; for (i = 0; i < MAX_JOYSTICKS; i++) { if (sdljoy[i].qdevid == id) { SDL_GameControllerSendEffect(SDL_GameControllerFromInstanceID(sdljoy[i].id), &data, size); return; } } #else Con_DPrintf(CON_WARNING "Trigger FX is requires at least SDL 2.0.15\n"); #endif } #if SDL_VERSION_ATLEAST(2,0,14) static void J_ControllerTouchPad(SDL_JoystickID jid, int pad, int finger, int fingerstate, float x, float y, float pressure) { #if 1 //FIXME: forgets which seat its meant to be controlling. uint32_t thefinger = SDL_GiveFinger(jid, pad, finger, fingerstate<0); #else //FIXME: conflicts with regular mice (very problematic when using absolute coords) uint32_t thefinger; struct sdljoy_s *joy = J_DevId(jid); if (!joy) return; if (joy->qdevid == DEVID_UNSET) { if (fingerstate!=1) return; J_AllocateDevID(joy); } thefinger = joy->qdevid; #endif x *= vid.pixelwidth; y *= vid.pixelheight; IN_MouseMove(thefinger, true, x, y, 0, pressure); if (finger) IN_KeyEvent(thefinger, finger>0, K_MOUSE1, 0); } static void J_ControllerSensor(SDL_JoystickID jid, SDL_SensorType sensor, float *data) { struct sdljoy_s *joy = J_DevId(jid); if (!joy) return; //don't assign an id here. wait for a button. if (joy->qdevid == DEVID_UNSET) return; switch(sensor) { case SDL_SENSOR_ACCEL: IN_Accelerometer(joy->qdevid, data[0], data[1], data[2]); break; case SDL_SENSOR_GYRO: IN_Gyroscope(joy->qdevid, data[0], data[1], data[2]); break; /*#if SDL_VERSION_ATLEAST(2,25,0) case SDL_SENSOR_ACCEL_L: case SDL_SENSOR_ACCEL_R: case SDL_SENSOR_GYRO_L: case SDL_SENSOR_GYRO_R: #endif*/ case SDL_SENSOR_INVALID: case SDL_SENSOR_UNKNOWN: default: break; } } #endif static void J_Kill(SDL_JoystickID jid, qboolean verbose) { int i; struct sdljoy_s *joy = J_DevId(jid); if (!joy) return; //make sure all the axis are nulled out, to avoid surprises. if (joy->qdevid != DEVID_UNSET) { for (i = 0; i < 6; i++) IN_JoystickAxisEvent(joy->qdevid, i, 0); if (joy->controller) { for (i = 0; i < 32; i++) J_ControllerButton(jid, i, false); } else { for (i = 0; i < 32; i++) J_JoystickButton(jid, i, false); } } if (joy->controller) { Con_Printf("Controller unplugged(%i): %s\n", (int)(joy - sdljoy), joy->devname); SDL_GameControllerClose(joy->controller); } else { Con_Printf("Joystick unplugged(%i): %s\n", (int)(joy - sdljoy), joy->devname); SDL_JoystickClose(joy->joystick); } joy->controller = NULL; joy->joystick = NULL; Z_Free(joy->devname); joy->devname = NULL; joy->qdevid = DEVID_UNSET; } static void J_KillAll(void) { int i; for (i = 0; i < MAX_JOYSTICKS; i++) J_Kill(sdljoy[i].id, false); } #endif #if SDL_MAJOR_VERSION >= 2 //FIXME: switch to scancodes rather than keysyms //use SDL_GetKeyName(SDL_GetKeyFromScancode(quaketosdl[qkey])) for keybinds menu unsigned int MySDL_MapKey(SDL_Keycode sdlkey) { switch(sdlkey) { default: return 0; //any ascii chars can be mapped directly to keys, even if they're only ever accessed with shift etc... oh well. case SDLK_RETURN: return K_ENTER; case SDLK_ESCAPE: return K_ESCAPE; case SDLK_BACKSPACE: return K_BACKSPACE; case SDLK_TAB: return K_TAB; case SDLK_SPACE: return K_SPACE; case SDLK_EXCLAIM: case SDLK_QUOTEDBL: case SDLK_HASH: case SDLK_PERCENT: case SDLK_DOLLAR: case SDLK_AMPERSAND: case SDLK_QUOTE: case SDLK_LEFTPAREN: case SDLK_RIGHTPAREN: case SDLK_ASTERISK: case SDLK_PLUS: case SDLK_COMMA: case SDLK_MINUS: case SDLK_PERIOD: case SDLK_SLASH: case SDLK_0: case SDLK_1: case SDLK_2: case SDLK_3: case SDLK_4: case SDLK_5: case SDLK_6: case SDLK_7: case SDLK_8: case SDLK_9: case SDLK_COLON: case SDLK_SEMICOLON: case SDLK_LESS: case SDLK_EQUALS: case SDLK_GREATER: case SDLK_QUESTION: case SDLK_AT: case SDLK_LEFTBRACKET: case SDLK_BACKSLASH: case SDLK_RIGHTBRACKET: case SDLK_CARET: case SDLK_UNDERSCORE: case SDLK_BACKQUOTE: case SDLK_a: case SDLK_b: case SDLK_c: case SDLK_d: case SDLK_e: case SDLK_f: case SDLK_g: case SDLK_h: case SDLK_i: case SDLK_j: case SDLK_k: case SDLK_l: case SDLK_m: case SDLK_n: case SDLK_o: case SDLK_p: case SDLK_q: case SDLK_r: case SDLK_s: case SDLK_t: case SDLK_u: case SDLK_v: case SDLK_w: case SDLK_x: case SDLK_y: case SDLK_z: return sdlkey; case SDLK_CAPSLOCK: return K_CAPSLOCK; case SDLK_F1: return K_F1; case SDLK_F2: return K_F2; case SDLK_F3: return K_F3; case SDLK_F4: return K_F4; case SDLK_F5: return K_F5; case SDLK_F6: return K_F6; case SDLK_F7: return K_F7; case SDLK_F8: return K_F8; case SDLK_F9: return K_F9; case SDLK_F10: return K_F10; case SDLK_F11: return K_F11; case SDLK_F12: return K_F12; case SDLK_PRINTSCREEN: return K_PRINTSCREEN; case SDLK_SCROLLLOCK: return K_SCRLCK; case SDLK_PAUSE: return K_PAUSE; case SDLK_INSERT: return K_INS; case SDLK_HOME: return K_HOME; case SDLK_PAGEUP: return K_PGUP; case SDLK_DELETE: return K_DEL; case SDLK_END: return K_END; case SDLK_PAGEDOWN: return K_PGDN; case SDLK_RIGHT: return K_RIGHTARROW; case SDLK_LEFT: return K_LEFTARROW; case SDLK_DOWN: return K_DOWNARROW; case SDLK_UP: return K_UPARROW; case SDLK_NUMLOCKCLEAR: return K_KP_NUMLOCK; case SDLK_KP_DIVIDE: return K_KP_SLASH; case SDLK_KP_MULTIPLY: return K_KP_STAR; case SDLK_KP_MINUS: return K_KP_MINUS; case SDLK_KP_PLUS: return K_KP_PLUS; case SDLK_KP_ENTER: return K_KP_ENTER; case SDLK_KP_1: return K_KP_END; case SDLK_KP_2: return K_KP_DOWNARROW; case SDLK_KP_3: return K_KP_PGDN; case SDLK_KP_4: return K_KP_LEFTARROW; case SDLK_KP_5: return K_KP_5; case SDLK_KP_6: return K_KP_RIGHTARROW; case SDLK_KP_7: return K_KP_HOME; case SDLK_KP_8: return K_KP_UPARROW; case SDLK_KP_9: return K_KP_PGDN; case SDLK_KP_0: return K_KP_INS; case SDLK_KP_PERIOD: return K_KP_DEL; case SDLK_APPLICATION: return K_APP; case SDLK_POWER: return K_POWER; case SDLK_KP_EQUALS: return K_KP_EQUALS; case SDLK_F13: return K_F13; case SDLK_F14: return K_F14; case SDLK_F15: return K_F15; /* case SDLK_F16: return K_; case SDLK_F17: return K_; case SDLK_F18: return K_; case SDLK_F19: return K_; case SDLK_F20: return K_; case SDLK_F21: return K_; case SDLK_F22: return K_; case SDLK_F23: return K_; case SDLK_F24: return K_; case SDLK_EXECUTE: return K_; case SDLK_HELP: return K_; case SDLK_MENU: return K_; case SDLK_SELECT: return K_; case SDLK_STOP: return K_; case SDLK_AGAIN: return K_; case SDLK_UNDO: return K_; case SDLK_CUT: return K_; case SDLK_COPY: return K_; case SDLK_PASTE: return K_; case SDLK_FIND: return K_; case SDLK_MUTE: return K_; */ case SDLK_VOLUMEUP: return K_VOLUP; case SDLK_VOLUMEDOWN: return K_VOLDOWN; /* case SDLK_KP_COMMA: return K_; case SDLK_KP_EQUALSAS400: return K_; case SDLK_ALTERASE: return K_; case SDLK_SYSREQ: return K_; case SDLK_CANCEL: return K_; case SDLK_CLEAR: return K_; case SDLK_PRIOR: return K_; case SDLK_RETURN2: return K_; case SDLK_SEPARATOR: return K_; case SDLK_OUT: return K_; case SDLK_OPER: return K_; case SDLK_CLEARAGAIN: return K_; case SDLK_CRSEL: return K_; case SDLK_EXSEL: return K_; case SDLK_KP_00: return K_; case SDLK_KP_000: return K_; case SDLK_THOUSANDSSEPARATOR: return K_; case SDLK_DECIMALSEPARATOR: return K_; case SDLK_CURRENCYUNIT: return K_; case SDLK_CURRENCYSUBUNIT: return K_; case SDLK_KP_LEFTPAREN: return K_; case SDLK_KP_RIGHTPAREN: return K_; case SDLK_KP_LEFTBRACE: return K_; case SDLK_KP_RIGHTBRACE: return K_; case SDLK_KP_TAB: return K_; case SDLK_KP_BACKSPACE: return K_; case SDLK_KP_A: return K_; case SDLK_KP_B: return K_; case SDLK_KP_C: return K_; case SDLK_KP_D: return K_; case SDLK_KP_E: return K_; case SDLK_KP_F: return K_; case SDLK_KP_XOR: return K_; case SDLK_KP_POWER: return K_; case SDLK_KP_PERCENT: return K_; case SDLK_KP_LESS: return K_; case SDLK_KP_GREATER: return K_; case SDLK_KP_AMPERSAND: return K_; case SDLK_KP_DBLAMPERSAND: return K_; case SDLK_KP_VERTICALBAR: return K_; case SDLK_KP_DBLVERTICALBAR: return K_; case SDLK_KP_COLON: return K_; case SDLK_KP_HASH: return K_; case SDLK_KP_SPACE: return K_; case SDLK_KP_AT: return K_; case SDLK_KP_EXCLAM: return K_; case SDLK_KP_MEMSTORE: return K_; case SDLK_KP_MEMRECALL: return K_; case SDLK_KP_MEMCLEAR: return K_; case SDLK_KP_MEMADD: return K_; case SDLK_KP_MEMSUBTRACT: return K_; case SDLK_KP_MEMMULTIPLY: return K_; case SDLK_KP_MEMDIVIDE: return K_; case SDLK_KP_PLUSMINUS: return K_; case SDLK_KP_CLEAR: return K_; case SDLK_KP_CLEARENTRY: return K_; case SDLK_KP_BINARY: return K_; case SDLK_KP_OCTAL: return K_; case SDLK_KP_DECIMAL: return K_; case SDLK_KP_HEXADECIMAL: return K_; */ case SDLK_LCTRL: return K_LCTRL; case SDLK_LSHIFT: return K_LSHIFT; case SDLK_LALT: return K_LALT; case SDLK_LGUI: return K_APP; case SDLK_RCTRL: return K_RCTRL; case SDLK_RSHIFT: return K_RSHIFT; case SDLK_RALT: return K_RALT; /* case SDLK_RGUI: return K_; case SDLK_MODE: return K_; case SDLK_AUDIONEXT: return K_; case SDLK_AUDIOPREV: return K_; case SDLK_AUDIOSTOP: return K_; case SDLK_AUDIOPLAY: return K_; case SDLK_AUDIOMUTE: return K_; case SDLK_MEDIASELECT: return K_; case SDLK_WWW: return K_; case SDLK_MAIL: return K_; case SDLK_CALCULATOR: return K_; case SDLK_COMPUTER: return K_; case SDLK_AC_SEARCH: return K_; case SDLK_AC_HOME: return K_; case SDLK_AC_BACK: return K_; case SDLK_AC_FORWARD: return K_; case SDLK_AC_STOP: return K_; case SDLK_AC_REFRESH: return K_; case SDLK_AC_BOOKMARKS: return K_; case SDLK_BRIGHTNESSDOWN: return K_; case SDLK_BRIGHTNESSUP: return K_; case SDLK_DISPLAYSWITCH: return K_; case SDLK_KBDILLUMTOGGLE: return K_; case SDLK_KBDILLUMDOWN: return K_; case SDLK_KBDILLUMUP: return K_; case SDLK_EJECT: return K_; case SDLK_SLEEP: return K_; */ } } #else #define tenoh 0,0,0,0,0, 0,0,0,0,0 #define fiftyoh tenoh, tenoh, tenoh, tenoh, tenoh #define hundredoh fiftyoh, fiftyoh static unsigned int tbl_sdltoquake[] = { 0,0,0,0, //SDLK_UNKNOWN = 0, 0,0,0,0, //SDLK_FIRST = 0, K_BACKSPACE, //SDLK_BACKSPACE = 8, K_TAB, //SDLK_TAB = 9, 0,0, 0, //SDLK_CLEAR = 12, K_ENTER, //SDLK_RETURN = 13, 0,0,0,0,0, K_PAUSE, //SDLK_PAUSE = 19, 0,0,0,0,0,0,0, K_ESCAPE, //SDLK_ESCAPE = 27, 0,0,0,0, K_SPACE, //SDLK_SPACE = 32, '!', //SDLK_EXCLAIM = 33, '"', //SDLK_QUOTEDBL = 34, '#', //SDLK_HASH = 35, '$', //SDLK_DOLLAR = 36, 0, '&', //SDLK_AMPERSAND = 38, '\'', //SDLK_QUOTE = 39, '(', //SDLK_LEFTPAREN = 40, ')', //SDLK_RIGHTPAREN = 41, '*', //SDLK_ASTERISK = 42, '+', //SDLK_PLUS = 43, ',', //SDLK_COMMA = 44, '-', //SDLK_MINUS = 45, '.', //SDLK_PERIOD = 46, '/', //SDLK_SLASH = 47, '0', //SDLK_0 = 48, '1', //SDLK_1 = 49, '2', //SDLK_2 = 50, '3', //SDLK_3 = 51, '4', //SDLK_4 = 52, '5', //SDLK_5 = 53, '6', //SDLK_6 = 54, '7', //SDLK_7 = 55, '8', //SDLK_8 = 56, '9', //SDLK_9 = 57, ':', //SDLK_COLON = 58, ';', //SDLK_SEMICOLON = 59, '<', //SDLK_LESS = 60, '=', //SDLK_EQUALS = 61, '>', //SDLK_GREATER = 62, '?', //SDLK_QUESTION = 63, '@', //SDLK_AT = 64, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, '[', //SDLK_LEFTBRACKET = 91, '\\', //SDLK_BACKSLASH = 92, ']', //SDLK_RIGHTBRACKET = 93, '^', //SDLK_CARET = 94, '_', //SDLK_UNDERSCORE = 95, '`', //SDLK_BACKQUOTE = 96, 'a', //SDLK_a = 97, 'b', //SDLK_b = 98, 'c', //SDLK_c = 99, 'd', //SDLK_d = 100, 'e', //SDLK_e = 101, 'f', //SDLK_f = 102, 'g', //SDLK_g = 103, 'h', //SDLK_h = 104, 'i', //SDLK_i = 105, 'j', //SDLK_j = 106, 'k', //SDLK_k = 107, 'l', //SDLK_l = 108, 'm', //SDLK_m = 109, 'n', //SDLK_n = 110, 'o', //SDLK_o = 111, 'p', //SDLK_p = 112, 'q', //SDLK_q = 113, 'r', //SDLK_r = 114, 's', //SDLK_s = 115, 't', //SDLK_t = 116, 'u', //SDLK_u = 117, 'v', //SDLK_v = 118, 'w', //SDLK_w = 119, 'x', //SDLK_x = 120, 'y', //SDLK_y = 121, 'z', //SDLK_z = 122, 0,0,0,0, K_DEL, //SDLK_DELETE = 127, hundredoh /*227*/, tenoh, tenoh, 0,0,0,0,0,0,0,0, K_KP_INS, //SDLK_KP0 = 256, K_KP_END, //SDLK_KP1 = 257, K_KP_DOWNARROW, //SDLK_KP2 = 258, K_KP_PGDN, //SDLK_KP3 = 259, K_KP_LEFTARROW, //SDLK_KP4 = 260, K_KP_5, //SDLK_KP5 = 261, K_KP_RIGHTARROW, //SDLK_KP6 = 262, K_KP_HOME, //SDLK_KP7 = 263, K_KP_UPARROW, //SDLK_KP8 = 264, K_KP_PGUP, //SDLK_KP9 = 265, K_KP_DEL,//SDLK_KP_PERIOD = 266, K_KP_SLASH,//SDLK_KP_DIVIDE = 267, K_KP_STAR,//SDLK_KP_MULTIPLY= 268, K_KP_MINUS, //SDLK_KP_MINUS = 269, K_KP_PLUS, //SDLK_KP_PLUS = 270, K_KP_ENTER, //SDLK_KP_ENTER = 271, K_KP_EQUALS,//SDLK_KP_EQUALS = 272, K_UPARROW, //SDLK_UP = 273, K_DOWNARROW,//SDLK_DOWN = 274, K_RIGHTARROW,//SDLK_RIGHT = 275, K_LEFTARROW,//SDLK_LEFT = 276, K_INS, //SDLK_INSERT = 277, K_HOME, //SDLK_HOME = 278, K_END, //SDLK_END = 279, K_PGUP, //SDLK_PAGEUP = 280, K_PGDN, //SDLK_PAGEDOWN = 281, K_F1, //SDLK_F1 = 282, K_F2, //SDLK_F2 = 283, K_F3, //SDLK_F3 = 284, K_F4, //SDLK_F4 = 285, K_F5, //SDLK_F5 = 286, K_F6, //SDLK_F6 = 287, K_F7, //SDLK_F7 = 288, K_F8, //SDLK_F8 = 289, K_F9, //SDLK_F9 = 290, K_F10, //SDLK_F10 = 291, K_F11, //SDLK_F11 = 292, K_F12, //SDLK_F12 = 293, 0, //SDLK_F13 = 294, 0, //SDLK_F14 = 295, 0, //SDLK_F15 = 296, 0,0,0, 0,//K_NUMLOCK, //SDLK_NUMLOCK = 300, K_CAPSLOCK, //SDLK_CAPSLOCK = 301, 0,//K_SCROLLOCK,//SDLK_SCROLLOCK= 302, K_RSHIFT, //SDLK_RSHIFT = 303, K_LSHIFT, //SDLK_LSHIFT = 304, K_RCTRL, //SDLK_RCTRL = 305, K_LCTRL, //SDLK_LCTRL = 306, K_RALT, //SDLK_RALT = 307, K_LALT, //SDLK_LALT = 308, 0, //SDLK_RMETA = 309, 0, //SDLK_LMETA = 310, 0, //SDLK_LSUPER = 311, /* Left "Windows" key */ 0, //SDLK_RSUPER = 312, /* Right "Windows" key */ 0, //SDLK_MODE = 313, /* "Alt Gr" key */ 0, //SDLK_COMPOSE = 314, /* Multi-key compose key */ 0, //SDLK_HELP = 315, 0, //SDLK_PRINT = 316, 0, //SDLK_SYSREQ = 317, K_PAUSE, //SDLK_BREAK = 318, 0, //SDLK_MENU = 319, 0, //SDLK_POWER = 320, /* Power Macintosh power key */ 'e', //SDLK_EURO = 321, /* Some european keyboards */ 0 //SDLK_UNDO = 322, /* Atari keyboard has Undo */ }; #endif /* stubbed */ qboolean INS_KeyToLocalName(int qkey, char *buf, size_t bufsize) { const char *keyname; //too lazy to make+maintain a reverse MySDL_MapKey, so lets just make a reverse table with it instead. static qboolean stupidtabsetup; static SDL_Keycode stupidtable[K_MAX]; if (!stupidtabsetup) { int i, k; for (i = 0; i < SDL_NUM_SCANCODES; i++) { k = MySDL_MapKey(i); if (k) stupidtable[k] = i; //grr, oh well, at least we're not scanning 2 billion codes here k = MySDL_MapKey(SDL_SCANCODE_TO_KEYCODE(i)); if (k) stupidtable[k] = SDL_SCANCODE_TO_KEYCODE(i); } stupidtabsetup = true; } if (stupidtable[qkey]) keyname = SDL_GetKeyName(stupidtable[qkey]); else if (qkey >= K_GP_DIAMOND_DOWN && qkey < K_GP_TOUCHPAD) { SDL_GameControllerButton b; switch(qkey) { case K_GP_DIAMOND_DOWN: b = SDL_CONTROLLER_BUTTON_A; break; case K_GP_DIAMOND_RIGHT: b = SDL_CONTROLLER_BUTTON_B; break; case K_GP_DIAMOND_LEFT: b = SDL_CONTROLLER_BUTTON_X; break; case K_GP_DIAMOND_UP: b = SDL_CONTROLLER_BUTTON_Y; break; case K_GP_BACK: b = SDL_CONTROLLER_BUTTON_BACK; break; case K_GP_GUIDE: b = SDL_CONTROLLER_BUTTON_GUIDE; break; case K_GP_START: b = SDL_CONTROLLER_BUTTON_START; break; case K_GP_LEFT_STICK: b = SDL_CONTROLLER_BUTTON_LEFTSTICK; break; case K_GP_RIGHT_STICK: b = SDL_CONTROLLER_BUTTON_RIGHTSTICK; break; case K_GP_LEFT_SHOULDER: b = SDL_CONTROLLER_BUTTON_LEFTSHOULDER; break; case K_GP_RIGHT_SHOULDER: b = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER; break; case K_GP_DPAD_UP: b = SDL_CONTROLLER_BUTTON_DPAD_UP; break; case K_GP_DPAD_DOWN: b = SDL_CONTROLLER_BUTTON_DPAD_DOWN; break; case K_GP_DPAD_LEFT: b = SDL_CONTROLLER_BUTTON_DPAD_LEFT; break; case K_GP_DPAD_RIGHT: b = SDL_CONTROLLER_BUTTON_DPAD_RIGHT; break; #if SDL_VERSION_ATLEAST(2, 0, 14) case K_GP_MISC1: b = SDL_CONTROLLER_BUTTON_MISC1; break; case K_GP_PADDLE1: b = SDL_CONTROLLER_BUTTON_PADDLE1; break; case K_GP_PADDLE2: b = SDL_CONTROLLER_BUTTON_PADDLE2; break; case K_GP_PADDLE3: b = SDL_CONTROLLER_BUTTON_PADDLE3; break; case K_GP_PADDLE4: b = SDL_CONTROLLER_BUTTON_PADDLE4; break; case K_GP_TOUCHPAD: b = SDL_CONTROLLER_BUTTON_TOUCHPAD; break; #endif default: return false; } keyname = SDL_GameControllerGetStringForButton(b); } else return false; if (keyname) { Q_strncpyz(buf, keyname, bufsize); return true; } return false; } static unsigned int tbl_sdltoquakemouse[] = { K_MOUSE1, K_MOUSE3, K_MOUSE2, #if SDL_MAJOR_VERSION < 2 K_MWHEELUP, K_MWHEELDOWN, #endif K_MOUSE4, K_MOUSE5, K_MOUSE6, K_MOUSE7, K_MOUSE8, K_MOUSE9, K_MOUSE10 }; #ifdef HAVE_SDL_TEXTINPUT #ifdef __linux__ #include static qboolean usesteamosk; #endif #endif void Sys_SendKeyEvents(void) { SDL_Event event; int axis, j; #ifdef HAVE_SDL_TEXTINPUT static SDL_bool active = false; SDL_bool osk = Key_Dest_Has(kdm_console|kdm_cwindows|kdm_message); if (Key_Dest_Has(kdm_prompt|kdm_menu)) { j = Menu_WantOSK(); if (j < 0) osk |= sys_osk.ival; else osk |= j; } else if (Key_Dest_Has(kdm_game)) osk |= sys_osk.ival; if (osk) { SDL_Rect rect; rect.x = 0; rect.y = vid.rotpixelheight/2; rect.w = vid.rotpixelwidth; rect.h = vid.rotpixelheight - rect.y; SDL_SetTextInputRect(&rect); if (!active) { #ifdef __linux__ if (usesteamosk) SDL_OpenURL("steam://open/keyboard?Mode=1"); else #endif SDL_StartTextInput(); active = true; // Con_Printf("OSK shown...\n"); } } else { if (active) { #ifdef __linux__ if (usesteamosk) SDL_OpenURL("steam://close/keyboard?Mode=1"); else #endif SDL_StopTextInput(); active = false; // Con_Printf("OSK shown... killed\n"); } } #endif while(SDL_PollEvent(&event)) { switch(event.type) { #if SDL_MAJOR_VERSION >= 2 case SDL_WINDOWEVENT: switch(event.window.event) { default: break; case SDL_WINDOWEVENT_SIZE_CHANGED: #if SDL_VERSION_ATLEAST(2,0,6) && defined(VKQUAKE) if (qrenderer == QR_VULKAN) { unsigned window_width, window_height; SDL_Vulkan_GetDrawableSize(sdlwindow, &window_width, &window_height); //get the proper physical size. if (vid.pixelwidth != window_width || vid.pixelheight != window_height) vk.neednewswapchain = true; } else #endif { #if SDL_VERSION_ATLEAST(2,0,1) SDL_GL_GetDrawableSize(sdlwindow, &vid.pixelwidth, &vid.pixelheight); //get the proper physical size. #else SDL_GetWindowSize(sdlwindow, &vid.pixelwidth, &vid.pixelheight); #endif { extern cvar_t vid_conautoscale, vid_conwidth; //make sure the screen is updated properly. Cvar_ForceCallback(&vid_conautoscale); Cvar_ForceCallback(&vid_conwidth); } } break; case SDL_WINDOWEVENT_FOCUS_GAINED: vid.activeapp = true; break; case SDL_WINDOWEVENT_FOCUS_LOST: vid.activeapp = false; break; case SDL_WINDOWEVENT_CLOSE: Cbuf_AddText("quit prompt\n", RESTRICT_LOCAL); break; } break; #else case SDL_ACTIVEEVENT: if (event.active.state & SDL_APPINPUTFOCUS) { //follow keyboard status vid.activeapp = !!event.active.gain; break; } break; case SDL_VIDEORESIZE: #ifndef SERVERONLY vid.pixelwidth = event.resize.w; vid.pixelheight = event.resize.h; { extern cvar_t vid_conautoscale, vid_conwidth; //make sure the screen is updated properly. Cvar_ForceCallback(&vid_conautoscale); Cvar_ForceCallback(&vid_conwidth); } #endif break; #endif case SDL_KEYUP: case SDL_KEYDOWN: { int s = event.key.keysym.sym; int qs; #if SDL_MAJOR_VERSION >= 2 qs = MySDL_MapKey(s); #else if (s < sizeof(tbl_sdltoquake) / sizeof(tbl_sdltoquake[0])) qs = tbl_sdltoquake[s]; else qs = 0; #endif #ifdef FTE_TARGET_WEB if (s == 1249) qs = K_SHIFT; #endif #ifdef HAVE_SDL_TEXTINPUT IN_KeyEvent(0, event.key.state, qs, 0); #else IN_KeyEvent(0, event.key.state, qs, event.key.keysym.unicode); #endif } break; #ifdef HAVE_SDL_TEXTINPUT /*case SDL_TEXTEDITING: { event.edit; } break;*/ case SDL_TEXTINPUT: { unsigned int uc; int err; const char *text = event.text.text; while(*text) { uc = utf8_decode(&err, text, &text); if (uc && !err) { IN_KeyEvent(0, true, 0, uc); IN_KeyEvent(0, false, 0, uc); } } } break; #endif #if SDL_MAJOR_VERSION >= 2 case SDL_FINGERDOWN: case SDL_FINGERUP: { uint32_t thefinger = SDL_GiveFinger(-1, event.tfinger.touchId, event.tfinger.fingerId, event.type==SDL_FINGERUP); IN_MouseMove(thefinger, true, event.tfinger.x * vid.pixelwidth, event.tfinger.y * vid.pixelheight, 0, event.tfinger.pressure); IN_KeyEvent(thefinger, event.type==SDL_FINGERDOWN, K_TOUCH, 0); } break; case SDL_FINGERMOTION: { uint32_t thefinger = SDL_GiveFinger(-1, event.tfinger.touchId, event.tfinger.fingerId, false); IN_MouseMove(thefinger, true, event.tfinger.x * vid.pixelwidth, event.tfinger.y * vid.pixelheight, 0, event.tfinger.pressure); } break; case SDL_DROPFILE: Host_RunFile(event.drop.file, strlen(event.drop.file), NULL); SDL_free(event.drop.file); break; #endif case SDL_MOUSEMOTION: #if SDL_MAJOR_VERSION >= 2 if (event.motion.which == SDL_TOUCH_MOUSEID) break; //ignore legacy touch events. #endif if (!mouseactive) IN_MouseMove(event.motion.which, true, event.motion.x, event.motion.y, 0, 0); else IN_MouseMove(event.motion.which, false, event.motion.xrel, event.motion.yrel, 0, 0); break; #if SDL_MAJOR_VERSION >= 2 case SDL_MOUSEWHEEL: if (event.wheel.direction == SDL_MOUSEWHEEL_FLIPPED) event.wheel.y *= -1; for (; event.wheel.y > 0; event.wheel.y--) { IN_KeyEvent(event.button.which, true, K_MWHEELUP, 0); IN_KeyEvent(event.button.which, false, K_MWHEELUP, 0); } for (; event.wheel.y < 0; event.wheel.y++) { IN_KeyEvent(event.button.which, true, K_MWHEELDOWN, 0); IN_KeyEvent(event.button.which, false, K_MWHEELDOWN, 0); } /* for (; event.wheel.x > 0; event.wheel.x--) { IN_KeyEvent(event.button.which, true, K_MWHEELRIGHT, 0); IN_KeyEvent(event.button.which, false, K_MWHEELRIGHT, 0); } for (; event.wheel.x < 0; event.wheel.x++) { IN_KeyEvent(event.button.which, true, K_MWHEELLEFT, 0); IN_KeyEvent(event.button.which, false, K_MWHEELLEFT, 0); }*/ break; #endif case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONUP: #if SDL_MAJOR_VERSION >= 2 if (event.button.which == SDL_TOUCH_MOUSEID) break; //ignore legacy touch events. SDL_FINGER* events above will handle it (for multitouch) #endif //Hmm. SDL allows for 255 buttons, but only defines 5... if (event.button.button > sizeof(tbl_sdltoquakemouse)/sizeof(tbl_sdltoquakemouse[0])) event.button.button = sizeof(tbl_sdltoquakemouse)/sizeof(tbl_sdltoquakemouse[0]); IN_KeyEvent(event.button.which, event.button.state, tbl_sdltoquakemouse[event.button.button-1], 0); break; #if SDL_MAJOR_VERSION >= 2 case SDL_APP_TERMINATING: Cbuf_AddText("quit force\n", RESTRICT_LOCAL); break; #endif case SDL_QUIT: Cbuf_AddText("quit\n", RESTRICT_LOCAL); break; #if SDL_MAJOR_VERSION >= 2 //actually, joysticks *should* work with sdl1 as well, but there are some differences (like no hot plugging, I think). case SDL_JOYAXISMOTION: J_JoystickAxis(event.jaxis.which, event.jaxis.axis, event.jaxis.value); break; // case SDL_JOYBALLMOTION: // case SDL_JOYHATMOTION: // break; case SDL_JOYBUTTONDOWN: case SDL_JOYBUTTONUP: J_JoystickButton(event.jbutton.which, event.jbutton.button, event.type==SDL_JOYBUTTONDOWN); break; case SDL_JOYDEVICEADDED: J_JoystickAdded(event.jdevice.which); break; case SDL_JOYDEVICEREMOVED: J_Kill(event.jdevice.which, true); break; case SDL_CONTROLLERAXISMOTION: J_ControllerAxis(event.caxis.which, event.caxis.axis, event.caxis.value); break; case SDL_CONTROLLERBUTTONDOWN: case SDL_CONTROLLERBUTTONUP: J_ControllerButton(event.cbutton.which, event.cbutton.button, event.type==SDL_CONTROLLERBUTTONDOWN); break; case SDL_CONTROLLERDEVICEADDED: J_ControllerAdded(event.cdevice.which); break; case SDL_CONTROLLERDEVICEREMOVED: J_Kill(event.cdevice.which, true); break; // case SDL_CONTROLLERDEVICEREMAPPED: // break; #if SDL_VERSION_ATLEAST(2,0,14) case SDL_CONTROLLERTOUCHPADDOWN: J_ControllerTouchPad(event.cdevice.which, event.ctouchpad.touchpad, event.ctouchpad.finger, 1, event.ctouchpad.x, event.ctouchpad.y, event.ctouchpad.pressure); break; case SDL_CONTROLLERTOUCHPADMOTION: J_ControllerTouchPad(event.cdevice.which, event.ctouchpad.touchpad, event.ctouchpad.finger, 0, event.ctouchpad.x, event.ctouchpad.y, event.ctouchpad.pressure); break; case SDL_CONTROLLERTOUCHPADUP: J_ControllerTouchPad(event.cdevice.which, event.ctouchpad.touchpad, event.ctouchpad.finger, -1, event.ctouchpad.x, event.ctouchpad.y, event.ctouchpad.pressure); break; case SDL_CONTROLLERSENSORUPDATE: J_ControllerSensor(event.csensor.which, event.csensor.sensor, event.csensor.data); break; #endif #endif } } for (j = 0; j < MAX_JOYSTICKS; j++) { struct sdljoy_s *joy = &sdljoy[j]; if (joy->qdevid == DEVID_UNSET || !joy->controller) continue; if (joy->buttonheld) { for (axis = 0; axis < countof(gpbuttonmap); axis++) { if (joy->buttonheld & (1u<buttonrepeat[axis] -= host_frametime; if (joy->buttonrepeat[axis] < 0) { IN_KeyEvent(joy->qdevid, true, gpbuttonmap[axis], 0); joy->buttonrepeat[axis] = 0.25; } } } } for (axis = 0; axis < countof(gpaxismap); axis++) { if ((joy->axistobuttonp | joy->axistobuttonn) & (1u<repeattime[axis] -= host_frametime; if (joy->repeattime[axis] < 0) { if (joy->axistobuttonp & (1u<qdevid, true, gpaxismap[axis].pos, 0); joy->repeattime[axis] = 0.25; } if (joy->axistobuttonn & (1u<qdevid, true, gpaxismap[axis].neg, 0); joy->repeattime[axis] = 0.25; } } } } } } void INS_Shutdown (void) { IN_DeactivateMouse(); #if SDL_MAJOR_VERSION >= 2 J_KillAll(); SDL_QuitSubSystem(SDL_INIT_JOYSTICK|SDL_INIT_GAMECONTROLLER); #endif } void INS_ReInit (void) { #if SDL_MAJOR_VERSION >= 2 unsigned int i; memset(sdljoy, 0, sizeof(sdljoy)); for (i = 0; i < MAX_JOYSTICKS; i++) sdljoy[i].qdevid = DEVID_UNSET; SDL_InitSubSystem(SDL_INIT_JOYSTICK|SDL_INIT_GAMECONTROLLER); #endif IN_ActivateMouse(); #ifndef HAVE_SDL_TEXTINPUT SDL_EnableUNICODE(SDL_ENABLE); #endif #if SDL_MAJOR_VERSION >= 2 SDL_EventState(SDL_DROPFILE, SDL_ENABLE); #endif } //stubs, all the work is done in Sys_SendKeyEvents void INS_Move(void) { } void INS_Init (void) { #ifdef HAVE_SDL_TEXTINPUT Cvar_Register(&sys_osk, "input controls"); #endif Cvar_Register (&joy_only, "input controls"); #ifdef HAVE_SDL_TEXTINPUT #ifdef __linux__ usesteamosk = SDL_GetHintBoolean("SteamDeck", false); //looks like there's a 'SteamDeck=1' environment setting on the deck (when started via steam itself, at least), so we don't get valve's buggy-as-poop osk on windows etc. #endif #endif } void INS_Accumulate(void) //input polling { } void INS_Commands (void) //used to Cbuf_AddText joystick button events in windows. { } void INS_EnumerateDevices(void *ctx, void(*callback)(void *ctx, const char *type, const char *devicename, unsigned int *qdevid)) { #if SDL_MAJOR_VERSION >= 2 unsigned int i; for (i = 0; i < MAX_JOYSTICKS; i++) if (sdljoy[i].controller || sdljoy[i].joystick) callback(ctx, "joy", sdljoy[i].devname, &sdljoy[i].qdevid); #endif }