1 //**************************************************************************
3 //** ## ## ## ## ## #### #### ### ###
4 //** ## ## ## ## ## ## ## ## ## ## #### ####
5 //** ## ## ## ## ## ## ## ## ## ## ## ## ## ##
6 //** ## ## ######## ## ## ## ## ## ## ## ### ##
7 //** ### ## ## ### ## ## ## ## ## ##
8 //** # ## ## # #### #### ## ##
10 //** Copyright (C) 1999-2006 Jānis Legzdiņš
11 //** Copyright (C) 2018-2023 Ketmar Dark
13 //** This program is free software: you can redistribute it and/or modify
14 //** it under the terms of the GNU General Public License as published by
15 //** the Free Software Foundation, version 3 of the License ONLY.
17 //** This program is distributed in the hope that it will be useful,
18 //** but WITHOUT ANY WARRANTY; without even the implied warranty of
19 //** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 //** GNU General Public License for more details.
22 //** You should have received a copy of the GNU General Public License
23 //** along with this program. If not, see <http://www.gnu.org/licenses/>.
25 //**************************************************************************
26 #ifdef VAVOOM_CUSTOM_SPECIAL_SDL
29 # include <SDL2/SDL.h>
35 #include "widgets/ui.h"
36 #include "psim/p_player.h"
37 #include "client/client.h"
38 #include "filesys/files.h"
40 //#define SDL_MOUSE_CAPTURE_DEBUG
43 // k8: joysticks have 16 buttons; no, really, you don't need more
45 #define VSDL_JINIT (SDL_INIT_JOYSTICK|SDL_INIT_GAMECONTROLLER)
48 static int cli_NoMouse
= 0;
49 static int cli_NoJoy
= 0;
50 static int cli_NoCotl
= 0;
52 /*static*/ bool cliRegister_input_args
=
53 VParsedArgs::RegisterFlagSet("-nomouse", "Disable mouse controls", &cli_NoMouse
) &&
54 VParsedArgs::RegisterFlagSet("-nojoystick", "Disable joysticks", &cli_NoJoy
) &&
55 VParsedArgs::RegisterFlagReset("-usejoystick", "Enable joysticks", &cli_NoJoy
) &&
56 VParsedArgs::RegisterFlagSet("-nocontroller", "Disable controllers", &cli_NoCotl
) &&
57 VParsedArgs::RegisterFlagReset("-usecontroller", "Enable controllers", &cli_NoCotl
) &&
58 VParsedArgs::RegisterAlias("-nojoy", "-nojoystick") &&
59 VParsedArgs::RegisterAlias("-joy", "-usejoystick") &&
60 VParsedArgs::RegisterAlias("-noctrl", "-nocontroller") &&
61 VParsedArgs::RegisterAlias("-ctrl", "-usecontroller");
64 // ////////////////////////////////////////////////////////////////////////// //
65 class VSdlInputDevice
: public VInputDevice
{
70 virtual void ReadInput () override
;
71 virtual void RegrabMouse () override
; // called by UI when mouse cursor is turned off
73 virtual void SetClipboardText (VStr text
) override
;
74 virtual bool HasClipboardText () override
;
75 virtual VStr
GetClipboardText () override
;
78 bool mouse
; // is mouse enabled?
79 bool winactive
; // is current window active?
80 bool firsttime
; // when we just grabbed the mouse, we should ignore the first mouse event
81 bool uiwasactive
; // was UI active last time we polled the input?
82 bool uimouselast
; // was mouse "logically grabbed" last time we polled the input?
83 bool curHidden
; // is real mouse cursor hidden?
85 bool currDoGrab
; // should we grab the mouse? (cached "m_grab" value)
86 bool currRelative
; // should we use relative mode? (cached "m_relative" value)
87 bool relativeFailed
; // did setting relative mode failed at least once?
98 bool kbdSuspended
; // if set to `true`, wait until `timeKbdAllow` to resume keyboard processing
101 SDL_Joystick
*joystick
;
102 SDL_GameController
*controller
;
105 bool joystick_started
;
106 bool joystick_controller
;
111 uint32_t joy_newb
; // bitmask
114 uint32_t joy_oldb
; // bitmask
115 uint32_t ctl_trigger
; // bitmask
116 int joynum
; // also, controller number too
118 // deletes stream; it is ok to pass `nullptr`
119 void LoadControllerMappings (VStream
*st
);
121 void StartupJoystick ();
122 void ShutdownJoystick (bool closesubsys
);
123 void PostJoystick ();
125 void HideRealMouse ();
126 void ShowRealMouse ();
129 void CtlTriggerButton (int idx
, bool down
);
131 // must be called after `StartupJoystick()`
132 void OpenJoystick (int jnum
);
138 bool bGotCloseRequest
; // used in `CheckForEscape()`
140 bool CheckForEscape ();
144 //==========================================================================
146 // GNetCheckForUserAbortCB
148 //==========================================================================
149 static bool GNetCheckForUserAbortCB (void *udata
) {
150 VSdlInputDevice
*drv
= (VSdlInputDevice
*)udata
;
151 return drv
->CheckForEscape();
155 // ////////////////////////////////////////////////////////////////////////// //
156 VCvarB
ui_freemouse("ui_freemouse", false, "Don't pass mouse movement to the camera. Used in various debug modes.", CVAR_Hidden
|CVAR_NoShadow
);
157 VCvarB
ui_want_mouse_at_zero("ui_want_mouse_at_zero", false, "Move real mouse cursor to (0,0) when UI activated?", CVAR_Archive
|CVAR_NoShadow
);
158 VCvarB
ui_mouse_forced("ui_mouse_forced", false, "Forge-grab mouse for UI?", CVAR_Hidden
|CVAR_NoShadow
);
159 static VCvarB
ui_mouse("ui_mouse", false, "Allow using mouse in UI?", CVAR_Archive
|CVAR_NoShadow
);
160 static VCvarB
ui_active("ui_active", false, "Is UI active (used to stop mouse warping if \"ui_mouse\" is false)?", CVAR_Hidden
|CVAR_NoShadow
);
161 static VCvarB
ui_control_waiting("ui_control_waiting", false, "Waiting for new control key (pass mouse buttons)?", CVAR_Hidden
|CVAR_NoShadow
);
163 static VCvarB
m_dbg_cursor("m_dbg_cursor", false, "Do not hide (true) mouse cursor on startup?", CVAR_PreInit
|CVAR_Hidden
|CVAR_NoShadow
);
164 static VCvarB
m_dbg_motion("m_dbg_motion", false, "Dump motion events?", CVAR_PreInit
|CVAR_Hidden
|CVAR_NoShadow
);
166 static VCvarB
m_grab("m_grab", true, "Grab mouse?", CVAR_Archive
|CVAR_NoShadow
);
167 static VCvarB
m_relative("m_relative", true, "Use relative mouse motion events?", CVAR_Archive
|CVAR_NoShadow
);
169 static VCvarI
ms_rel_mode("ms_rel_mode", "1", "Relative acceleration mode (0: none; 1: hl-like).", CVAR_Archive
|CVAR_NoShadow
);
171 static VCvarF
ms_rel_exponent("ms_rel_exponent", "1", "Relative acceletation distance exponent.", CVAR_Archive
|CVAR_NoShadow
);
172 static VCvarF
ms_rel_senscap("ms_rel_senscap", "2.7", "Relative acceletation sensitivity cap.", CVAR_Archive
|CVAR_NoShadow
);
173 static VCvarF
ms_rel_sensitivity("ms_rel_sensitivity", "0.8", "Relative acceletation sensitivity.", CVAR_Archive
|CVAR_NoShadow
);
174 static VCvarF
ms_rel_sensscale("ms_rel_sensscale", "0.2", "Relative acceletation sensitivity scale.", CVAR_Archive
|CVAR_NoShadow
);
176 static VCvarB
ms_rel_squaredist("ms_rel_squaredist", false, "Use squared distance for relative acceletation?", CVAR_Archive
|CVAR_NoShadow
);
177 static VCvarB
ms_rel_expweird("ms_rel_expweird", false, "Use different exponent formula for relative acceleration?", CVAR_Archive
|CVAR_NoShadow
);
179 static VCvarF
ctl_deadzone_leftstick_x("ctl_deadzone_leftstick_x", "0.08", "Dead zone for left stick (horizontal motion) -- [0..1].", CVAR_Archive
|CVAR_NoShadow
);
180 static VCvarF
ctl_deadzone_leftstick_y("ctl_deadzone_leftstick_y", "0.08", "Dead zone for left stick (vertical motion) -- [0..1].", CVAR_Archive
|CVAR_NoShadow
);
181 static VCvarF
ctl_deadzone_rightstick_x("ctl_deadzone_rightstick_x", "0.08", "Dead zone for right stick (horizontal motion) -- [0..1].", CVAR_Archive
|CVAR_NoShadow
);
182 static VCvarF
ctl_deadzone_rightstick_y("ctl_deadzone_rightstick_y", "0.08", "Dead zone for right stick (vertical motion) -- [0..1].", CVAR_Archive
|CVAR_NoShadow
);
183 static VCvarF
ctl_trigger_left_edge("ctl_trigger_left_edge", "0.8", "Minimal level for registering trigger A -- [0..1].", CVAR_Archive
|CVAR_NoShadow
);
184 static VCvarF
ctl_trigger_right_edge("ctl_trigger_right_edge", "0.8", "Minimal level for registering trigger B -- [0..1].", CVAR_Archive
|CVAR_NoShadow
);
186 static inline bool IsUIMouse () noexcept
{ return (ui_mouse
.asBool() || ui_mouse_forced
.asBool()); }
188 #ifdef VAVOOM_K8_DEVELOPER
189 # define IN_HYPER_BLOCK_DEFAULT true
190 # define IN_FOCUSGAIN_DELAY_DEFAULT "0.05"
192 # define IN_HYPER_BLOCK_DEFAULT false
193 # define IN_FOCUSGAIN_DELAY_DEFAULT "0"
195 static VCvarF
in_focusgain_delay("in_focusgain_delay", IN_FOCUSGAIN_DELAY_DEFAULT
, "Delay before resume keyboard processing after focus gain (seconds).", CVAR_Archive
|CVAR_NoShadow
);
196 static VCvarB
in_hyper_block("in_hyper_block", IN_HYPER_BLOCK_DEFAULT
, "Block keyboard input when `Hyper` is pressed.", CVAR_Archive
|CVAR_NoShadow
);
198 //extern VCvarB screen_fsmode;
199 extern VCvarB gl_current_screen_fsmode
;
202 // ////////////////////////////////////////////////////////////////////////// //
203 static int sdl2TranslateKey (SDL_Scancode scan
) {
204 if (scan
>= SDL_SCANCODE_A
&& scan
<= SDL_SCANCODE_Z
) return (int)(scan
-SDL_SCANCODE_A
+'a');
205 if (scan
>= SDL_SCANCODE_1
&& scan
<= SDL_SCANCODE_9
) return (int)(scan
-SDL_SCANCODE_1
+'1');
208 case SDL_SCANCODE_0
: return '0';
209 case SDL_SCANCODE_SPACE
: return ' ';
210 case SDL_SCANCODE_MINUS
: return '-';
211 case SDL_SCANCODE_EQUALS
: return '=';
212 case SDL_SCANCODE_LEFTBRACKET
: return '[';
213 case SDL_SCANCODE_RIGHTBRACKET
: return ']';
214 case SDL_SCANCODE_BACKSLASH
: return '\\';
215 case SDL_SCANCODE_SEMICOLON
: return ';';
216 case SDL_SCANCODE_APOSTROPHE
: return '\'';
217 case SDL_SCANCODE_COMMA
: return ',';
218 case SDL_SCANCODE_PERIOD
: return '.';
219 case SDL_SCANCODE_SLASH
: return '/';
221 case SDL_SCANCODE_UP
: return K_UPARROW
;
222 case SDL_SCANCODE_LEFT
: return K_LEFTARROW
;
223 case SDL_SCANCODE_RIGHT
: return K_RIGHTARROW
;
224 case SDL_SCANCODE_DOWN
: return K_DOWNARROW
;
225 case SDL_SCANCODE_INSERT
: return K_INSERT
;
226 case SDL_SCANCODE_DELETE
: return K_DELETE
;
227 case SDL_SCANCODE_HOME
: return K_HOME
;
228 case SDL_SCANCODE_END
: return K_END
;
229 case SDL_SCANCODE_PAGEUP
: return K_PAGEUP
;
230 case SDL_SCANCODE_PAGEDOWN
: return K_PAGEDOWN
;
232 case SDL_SCANCODE_KP_0
: return K_PAD0
;
233 case SDL_SCANCODE_KP_1
: return K_PAD1
;
234 case SDL_SCANCODE_KP_2
: return K_PAD2
;
235 case SDL_SCANCODE_KP_3
: return K_PAD3
;
236 case SDL_SCANCODE_KP_4
: return K_PAD4
;
237 case SDL_SCANCODE_KP_5
: return K_PAD5
;
238 case SDL_SCANCODE_KP_6
: return K_PAD6
;
239 case SDL_SCANCODE_KP_7
: return K_PAD7
;
240 case SDL_SCANCODE_KP_8
: return K_PAD8
;
241 case SDL_SCANCODE_KP_9
: return K_PAD9
;
243 case SDL_SCANCODE_NUMLOCKCLEAR
: return K_NUMLOCK
;
244 case SDL_SCANCODE_KP_DIVIDE
: return K_PADDIVIDE
;
245 case SDL_SCANCODE_KP_MULTIPLY
: return K_PADMULTIPLE
;
246 case SDL_SCANCODE_KP_MINUS
: return K_PADMINUS
;
247 case SDL_SCANCODE_KP_PLUS
: return K_PADPLUS
;
248 case SDL_SCANCODE_KP_ENTER
: return K_PADENTER
;
249 case SDL_SCANCODE_KP_PERIOD
: return K_PADDOT
;
251 case SDL_SCANCODE_ESCAPE
: return K_ESCAPE
;
252 case SDL_SCANCODE_RETURN
: return K_ENTER
;
253 case SDL_SCANCODE_TAB
: return K_TAB
;
254 case SDL_SCANCODE_BACKSPACE
: return K_BACKSPACE
;
256 case SDL_SCANCODE_GRAVE
: return K_BACKQUOTE
;
257 case SDL_SCANCODE_CAPSLOCK
: return K_CAPSLOCK
;
259 case SDL_SCANCODE_F1
: return K_F1
;
260 case SDL_SCANCODE_F2
: return K_F2
;
261 case SDL_SCANCODE_F3
: return K_F3
;
262 case SDL_SCANCODE_F4
: return K_F4
;
263 case SDL_SCANCODE_F5
: return K_F5
;
264 case SDL_SCANCODE_F6
: return K_F6
;
265 case SDL_SCANCODE_F7
: return K_F7
;
266 case SDL_SCANCODE_F8
: return K_F8
;
267 case SDL_SCANCODE_F9
: return K_F9
;
268 case SDL_SCANCODE_F10
: return K_F10
;
269 case SDL_SCANCODE_F11
: return K_F11
;
270 case SDL_SCANCODE_F12
: return K_F12
;
272 case SDL_SCANCODE_LSHIFT
: return K_LSHIFT
;
273 case SDL_SCANCODE_RSHIFT
: return K_RSHIFT
;
274 case SDL_SCANCODE_LCTRL
: return K_LCTRL
;
275 case SDL_SCANCODE_RCTRL
: return K_RCTRL
;
276 case SDL_SCANCODE_LALT
: return K_LALT
;
277 case SDL_SCANCODE_RALT
: return K_RALT
;
279 case SDL_SCANCODE_LGUI
: return K_LWIN
;
280 case SDL_SCANCODE_RGUI
: return K_RWIN
;
281 case SDL_SCANCODE_MENU
: return K_MENU
;
283 case SDL_SCANCODE_PRINTSCREEN
: return K_PRINTSCRN
;
284 case SDL_SCANCODE_SCROLLLOCK
: return K_SCROLLLOCK
;
285 case SDL_SCANCODE_PAUSE
: return K_PAUSE
;
288 //if (scan >= ' ' && scan < 127) return (vuint8)scan;
296 //==========================================================================
298 // VSdlInputDevice::VSdlInputDevice
300 //==========================================================================
301 VSdlInputDevice::VSdlInputDevice ()
309 , currRelative(false)
310 , relativeFailed(false)
317 , kbdSuspended(false)
320 , controller(nullptr)
323 , joystick_started(false)
324 , joystick_controller(false)
327 , bGotCloseRequest(false)
329 memset(&joy_x
[0], 0, 2*sizeof(joy_x
[0]));
330 memset(&joy_y
[0], 0, 2*sizeof(joy_y
[0]));
331 memset(&joy_oldx
[0], 0, 2*sizeof(joy_oldx
[0]));
332 memset(&joy_oldy
[0], 0, 2*sizeof(joy_oldy
[0]));
333 joy_newb
= joy_oldb
= 0;
336 // mouse and keyboard are setup using SDL's video interface
339 SDL_EventState(SDL_MOUSEMOTION
, SDL_IGNORE
);
340 SDL_EventState(SDL_MOUSEBUTTONDOWN
, SDL_IGNORE
);
341 SDL_EventState(SDL_MOUSEBUTTONUP
, SDL_IGNORE
);
348 mouseLastX
= mouseLastY
= 0;
349 currDoGrab
= !m_grab
.asBool();
350 currRelative
= !m_relative
.asBool();
357 // initialise joystick
360 CL_SetNetAbortCallback(&GNetCheckForUserAbortCB
, (void *)this);
364 //==========================================================================
366 // VSdlInputDevice::~VSdlInputDevice
368 //==========================================================================
369 VSdlInputDevice::~VSdlInputDevice () {
370 CL_SetNetAbortCallback(nullptr, nullptr);
371 //SDL_SetRelativeMouseMode(SDL_FALSE);
372 SDL_ShowCursor(1); // on
373 ShutdownJoystick(true);
377 //==========================================================================
379 // VSdlInputDevice::OwnMouse
381 //==========================================================================
382 void VSdlInputDevice::OwnMouse () {
384 currDoGrab
= m_grab
.asBool();
386 if (SDL_CaptureMouse(SDL_TRUE
) != 0) {
387 #ifdef SDL_MOUSE_CAPTURE_DEBUG
388 GCon
->Log(NAME_Debug
, "SDL: cannot capture mouse.");
391 #ifdef SDL_MOUSE_CAPTURE_DEBUG
392 GCon
->Log(NAME_Debug
, "SDL: mouse captured.");
396 currRelative
= (!relativeFailed
&& m_relative
.asBool());
398 if (SDL_SetRelativeMouseMode(SDL_TRUE
) != 0) {
399 GCon
->Log(NAME_Warning
, "SDL: cannot switch mouse to relative mode.");
400 currRelative
= false;
401 relativeFailed
= true;
403 #ifdef SDL_MOUSE_CAPTURE_DEBUG
404 GCon
->Log(NAME_Debug
, "SDL: switched mouse to relative mode.");
409 // we don't need relative mouse motion in non-relative mode
410 SDL_EventState(SDL_MOUSEMOTION
, (currRelative
? SDL_ENABLE
: SDL_IGNORE
));
412 if (!currRelative
&& Drawer
) {
413 Drawer
->WarpMouseToWindowCenter();
414 Drawer
->GetMousePosition(&mouseLastX
, &mouseLastY
);
419 //==========================================================================
421 // VSdlInputDevice::DisownMouse
423 //==========================================================================
424 void VSdlInputDevice::DisownMouse () {
425 // it is ok to release everything, why not
426 SDL_SetRelativeMouseMode(SDL_FALSE
);
427 SDL_CaptureMouse(SDL_FALSE
);
428 SDL_EventState(SDL_MOUSEMOTION
, SDL_IGNORE
);
431 currRelative
= false;
435 //==========================================================================
437 // VSdlInputDevice::DoUnpress
439 //==========================================================================
440 void VSdlInputDevice::DoUnpress () {
441 memset(&joy_x
[0], 0, 2*sizeof(joy_x
[0]));
442 memset(&joy_y
[0], 0, 2*sizeof(joy_y
[0]));
443 memset(&joy_oldx
[0], 0, 2*sizeof(joy_oldx
[0]));
444 memset(&joy_oldy
[0], 0, 2*sizeof(joy_oldy
[0]));
445 joy_newb
= joy_oldb
= 0;
447 VInputPublic::UnpressAll();
448 curmodflags
= 0; // just in case
452 //==========================================================================
454 // VSdlInputDevice::CtlTriggerButton
456 //==========================================================================
457 void VSdlInputDevice::CtlTriggerButton (int idx
, bool down
) {
458 if (idx
< 0 || idx
> 1) return;
459 const uint32_t mask
= 1u<<idx
;
462 if ((ctl_trigger
&mask
) == 0) {
464 GInput
->PostKeyEvent(K_BUTTON_TRIGGER_LEFT
+idx
, true, curmodflags
);
468 if ((ctl_trigger
&mask
) != 0) {
469 ctl_trigger
&= ~mask
;
470 GInput
->PostKeyEvent(K_BUTTON_TRIGGER_LEFT
+idx
, false, curmodflags
);
476 //==========================================================================
478 // VSdlInputDevice::HideRealMouse
480 //==========================================================================
481 void VSdlInputDevice::HideRealMouse () {
483 // real mouse cursor is visible
484 if (m_dbg_cursor
|| !mouse
) return;
488 // real mouse cursor is invisible
489 if (m_dbg_cursor
|| !mouse
) {
497 //==========================================================================
499 // VSdlInputDevice::ShowRealMouse
501 //==========================================================================
502 void VSdlInputDevice::ShowRealMouse () {
504 // real mouse cursor is invisible
511 //==========================================================================
513 // VSdlInputDevice::RegrabMouse
515 // Called by UI when mouse cursor is turned off.
517 //==========================================================================
518 void VSdlInputDevice::RegrabMouse () {
519 //FIXME: ignore winactive here, 'cause when mouse is off-window, it may be `false`
523 if (relativeFailed
|| !m_relative
.asBool()) {
524 Drawer
->WarpMouseToWindowCenter();
525 Drawer
->GetMousePosition(&mouseLastX
, &mouseLastY
);
528 SDL_GetMouseState(&mouseLastX
, &mouseLastY
);
534 //==========================================================================
536 // VSdlInputDevice::SetClipboardText
538 //==========================================================================
539 void VSdlInputDevice::SetClipboardText (VStr text
) {
540 if (text
.length() && !text
.IsValidUtf8()) {
541 VStr s2
= text
.Latin1ToUtf8();
542 SDL_SetClipboardText(s2
.getCStr());
544 SDL_SetClipboardText(text
.getCStr());
549 //==========================================================================
551 // VSdlInputDevice::HasClipboardText
553 //==========================================================================
554 bool VSdlInputDevice::HasClipboardText () {
555 return !!SDL_HasClipboardText();
559 //==========================================================================
561 // VSdlInputDevice::GetClipboardText
563 //==========================================================================
564 VStr
VSdlInputDevice::GetClipboardText () {
565 char *text
= SDL_GetClipboardText();
566 if (!text
|| !text
[0]) return VStr::EmptyString
;
567 for (char *p
= text
; *p
; ++p
) {
569 if ((unsigned)(ch
&0xff) <= 0 || (unsigned)(ch
&0xff) > 127) *p
= '?';
570 else if (ch
== '\t' || ch
== '\n') *p
= ' ';
571 else if (ch
> 0 && ch
< 32) *p
= ' ';
579 //==========================================================================
581 // calcStickTriggerValue
583 //==========================================================================
584 static int calcStickTriggerValue (const int axis
, int value
) noexcept
{
585 if (value
== 0) return 0; // just in case
586 value
= clampval(value
, -32767, 32767);
589 case SDL_CONTROLLER_AXIS_LEFTX
: dead
= ctl_deadzone_leftstick_x
.asFloat(); break;
590 case SDL_CONTROLLER_AXIS_LEFTY
: dead
= ctl_deadzone_leftstick_y
.asFloat(); break;
591 case SDL_CONTROLLER_AXIS_RIGHTX
: dead
= ctl_deadzone_rightstick_x
.asFloat(); break;
592 case SDL_CONTROLLER_AXIS_RIGHTY
: dead
= ctl_deadzone_rightstick_y
.asFloat(); break;
593 case SDL_CONTROLLER_AXIS_TRIGGERLEFT
: dead
= ctl_trigger_left_edge
.asFloat(); break;
594 case SDL_CONTROLLER_AXIS_TRIGGERRIGHT
: dead
= ctl_trigger_right_edge
.asFloat(); break;
597 if (dead
>= 1.0f
) return 0;
598 dead
= min2(0.0f
, dead
);
599 const int minval
= (int)(32767*dead
);
600 if (abs(value
) < minval
) return 0;
601 return clampval(value
*127/32767, -127, 127);
605 //==========================================================================
607 // VSdlInputDevice::ReadInput
609 // Reads input from the input devices.
611 //==========================================================================
612 void VSdlInputDevice::ReadInput () {
616 int mouseCurrX
= mouseLastX
, mouseCurrY
= mouseLastY
;
617 int rel_x
= 0, rel_y
= 0;
618 bool firstDump
= true;
619 //bool wasMouseButtons = false; // if `true`, prepend mouse movement
622 while (SDL_PollEvent(&ev
)) {
624 vev
.modflags
= curmodflags
;
629 switch (ev
.key
.keysym
.scancode
) {
630 case SDL_SCANCODE_LGUI
:
631 case SDL_SCANCODE_RGUI
:
632 hyperDown
= (ev
.key
.state
== SDL_PRESSED
);
636 // "kbd suspended" check
638 if (timeKbdAllow
> Sys_Time()) break;
639 kbdSuspended
= false;
640 if (ev
.key
.state
!= SDL_PRESSED
) break; // just in case
642 // translate key, and post keyboard event
643 if (!hyperDown
|| !in_hyper_block
) {
644 int kk
= sdl2TranslateKey(ev
.key
.keysym
.scancode
);
646 //GCon->Logf(NAME_Debug, "***KEY%s; kk=%d", (ev.key.state == SDL_PRESSED ? "DOWN" : "UP"), kk);
647 GInput
->PostKeyEvent(kk
, (ev
.key
.state
== SDL_PRESSED
? 1 : 0), vev
.modflags
);
651 switch (ev
.key
.keysym
.sym
) {
652 case SDLK_LSHIFT
: if (ev
.type
== SDL_KEYDOWN
) curmodflags
|= bShiftLeft
; else curmodflags
&= ~bShiftLeft
; break;
653 case SDLK_RSHIFT
: if (ev
.type
== SDL_KEYDOWN
) curmodflags
|= bShiftRight
; else curmodflags
&= ~bShiftRight
; break;
654 case SDLK_LCTRL
: if (ev
.type
== SDL_KEYDOWN
) curmodflags
|= bCtrlLeft
; else curmodflags
&= ~bCtrlLeft
; break;
655 case SDLK_RCTRL
: if (ev
.type
== SDL_KEYDOWN
) curmodflags
|= bCtrlRight
; else curmodflags
&= ~bCtrlRight
; break;
656 case SDLK_LALT
: if (ev
.type
== SDL_KEYDOWN
) curmodflags
|= bAltLeft
; else curmodflags
&= ~bAltLeft
; break;
657 case SDLK_RALT
: if (ev
.type
== SDL_KEYDOWN
) curmodflags
|= bAltRight
; else curmodflags
&= ~bAltRight
; break;
658 case SDLK_LGUI
: if (ev
.type
== SDL_KEYDOWN
) curmodflags
|= bHyper
; else curmodflags
&= ~bHyper
; break;
659 case SDLK_RGUI
: if (ev
.type
== SDL_KEYDOWN
) curmodflags
|= bHyper
; else curmodflags
&= ~bHyper
; break;
662 if (curmodflags
&(bShiftLeft
|bShiftRight
)) curmodflags
|= bShift
; else curmodflags
&= ~bShift
;
663 if (curmodflags
&(bCtrlLeft
|bCtrlRight
)) curmodflags
|= bCtrl
; else curmodflags
&= ~bCtrl
;
664 if (curmodflags
&(bAltLeft
|bAltRight
)) curmodflags
|= bAlt
; else curmodflags
&= ~bAlt
;
666 case SDL_MOUSEMOTION
:
667 if (currRelative
&& winactive
) {
670 int dx
= +ev
.motion
.xrel
;
671 int dy
= -ev
.motion
.yrel
;
672 if (ms_rel_mode
.asInt() > 0) {
673 float mx
= (float)dx
;
674 float my
= (float)dy
;
675 float effsens
= 1.0f
;
677 float movedist
= mx
*mx
+my
*my
;
678 if (!ms_rel_squaredist
.asBool()) movedist
= sqrtf(movedist
);
680 if (movedist
> 0.0f
) {
681 float dexp
= ms_rel_exponent
.asFloat();
682 if (!isFiniteF(dexp
)) dexp
= 0.0f
;
683 if (dexp
> 4.0f
) dexp
= 4.0f
;
685 if (ms_rel_expweird
.asBool()) {
686 const float expaccel
= max2(0.0f
, (dexp
-1.0f
)*0.5f
);
687 effsens
= powf(movedist
, expaccel
)*ms_rel_sensitivity
.asFloat();
689 effsens
= powf(movedist
, dexp
)*ms_rel_sensscale
.asFloat()+ms_rel_sensitivity
.asFloat();
694 float finsens
= effsens
;
695 if (ms_rel_senscap
.asFloat() > 0.0f
&& finsens
> ms_rel_senscap
.asFloat()) finsens
= ms_rel_senscap
.asFloat();
697 if (m_dbg_motion
.asBool()) {
698 if (firstDump
) { firstDump
= false; GCon
->Log(NAME_Debug
, "=== SDLMOTION ==="); }
699 GCon
->Logf(NAME_Debug
, "SDLMOTION: dx=%d; dy=%d; movedist=%g; finsens=%g (%g); sqdist=%d; weirdexp=%d; mx=%d; my=%d", dx
, dy
, movedist
, finsens
, effsens
, (int)ms_rel_squaredist
.asBool(), (int)ms_rel_expweird
.asBool(), (int)floorf(mx
*finsens
+hlRemX
), (int)floorf(my
*finsens
+hlRemY
));
705 // hold the remainder and pass it into the next round
709 hlRemX
= mx
-floorf(mx
);
710 hlRemY
= my
-floorf(my
);
712 rel_x
+= (int)floorf(mx
);
713 rel_y
+= (int)floorf(my
);
719 // temp, for button events
721 mouseCurrX
= clampval(mouseLastX
+rel_x
, 0, Drawer
->getWidth()-1);
722 mouseCurrY
= clampval(mouseLastY
+rel_y
, 0, Drawer
->getHeight()-1);
729 case SDL_MOUSEBUTTONDOWN
:
730 case SDL_MOUSEBUTTONUP
:
731 vev
.type
= (ev
.button
.state
== SDL_PRESSED
? ev_keydown
: ev_keyup
);
732 if (ev
.button
.button
== SDL_BUTTON_LEFT
) vev
.keycode
= K_MOUSE1
;
733 else if (ev
.button
.button
== SDL_BUTTON_RIGHT
) vev
.keycode
= K_MOUSE2
;
734 else if (ev
.button
.button
== SDL_BUTTON_MIDDLE
) vev
.keycode
= K_MOUSE3
;
735 else if (ev
.button
.button
== SDL_BUTTON_X1
) vev
.keycode
= K_MOUSE4
;
736 else if (ev
.button
.button
== SDL_BUTTON_X2
) vev
.keycode
= K_MOUSE5
;
738 if (IsUIMouse() || !ui_active
|| ui_control_waiting
|| ui_freemouse
) {
743 if (Drawer
) Drawer
->GetMousePosition(&vev
.x
, &vev
.y
);
745 VObject::PostEvent(vev
);
746 //wasMouseButtons = true;
749 if (ev
.button
.button
== SDL_BUTTON_LEFT
) { if (ev
.button
.state
== SDL_PRESSED
) curmodflags
|= bLMB
; else curmodflags
&= ~bLMB
; }
750 else if (ev
.button
.button
== SDL_BUTTON_RIGHT
) { if (ev
.button
.state
== SDL_PRESSED
) curmodflags
|= bRMB
; else curmodflags
&= ~bRMB
; }
751 else if (ev
.button
.button
== SDL_BUTTON_MIDDLE
) { if (ev
.button
.state
== SDL_PRESSED
) curmodflags
|= bMMB
; else curmodflags
&= ~bMMB
; }
754 vev
.type
= ev_keydown
;
755 if (ev
.wheel
.y
> 0) vev
.keycode
= K_MWHEELUP
;
756 else if (ev
.wheel
.y
< 0) vev
.keycode
= K_MWHEELDOWN
;
758 if (IsUIMouse() || !ui_active
|| ui_control_waiting
|| ui_freemouse
) {
763 if (Drawer
) Drawer
->GetMousePosition(&vev
.x
, &vev
.y
);
765 VObject::PostEvent(vev
);
766 //wasMouseButtons = true;
770 case SDL_JOYAXISMOTION
:
772 //GCon->Logf(NAME_Debug, "JOY AXIS %d: motion=%d", ev.jaxis.axis, ev.jaxis.value);
773 normal_value
= clampval(ev
.jaxis
.value
*127/32767, -127, 127);
774 if (ev
.jaxis
.axis
== 0) joy_x
[0] = normal_value
;
775 else if (ev
.jaxis
.axis
== 1) joy_y
[0] = normal_value
;
778 case SDL_JOYBALLMOTION
:
780 //GCon->Logf(NAME_Debug, "JOY BALL");
783 case SDL_JOYHATMOTION
:
785 //GCon->Logf(NAME_Debug, "JOY HAT");
788 case SDL_JOYBUTTONDOWN
:
790 //GCon->Logf(NAME_Debug, "JOY BUTTON %d", ev.jbutton.button);
791 if (/*ev.jbutton.button >= 0 &&*/ (unsigned)ev
.jbutton
.button
<= 15) {
792 joy_newb
|= 1u<<ev
.jbutton
.button
;
796 case SDL_JOYBUTTONUP
:
798 if (/*ev.jbutton.button >= 0 &&*/ (unsigned)ev
.jbutton
.button
<= 15) {
799 joy_newb
&= ~(1u<<ev
.jbutton
.button
);
804 case SDL_CONTROLLERAXISMOTION
:
805 if (controller
&& ev
.caxis
.which
== jid
) {
806 normal_value
= calcStickTriggerValue(ev
.caxis
.axis
, ev
.caxis
.value
);
808 const char *axisName
= "<unknown>";
809 switch (ev
.caxis
.axis
) {
810 case SDL_CONTROLLER_AXIS_LEFTX
: axisName
= "LEFTX"; break;
811 case SDL_CONTROLLER_AXIS_LEFTY
: axisName
= "LEFTY"; break;
812 case SDL_CONTROLLER_AXIS_RIGHTX
: axisName
= "RIGHTX"; break;
813 case SDL_CONTROLLER_AXIS_RIGHTY
: axisName
= "RIGHTY"; break;
814 case SDL_CONTROLLER_AXIS_TRIGGERLEFT
: axisName
= "TRIGLEFT"; break;
815 case SDL_CONTROLLER_AXIS_TRIGGERRIGHT
: axisName
= "TRIGRIGHT"; break;
818 GCon
->Logf(NAME_Debug
, "CTL AXIS '%s' (%d): motion=%d; nval=%d", axisName
, ev
.caxis
.axis
, ev
.caxis
.value
, normal_value
);
820 switch (ev
.caxis
.axis
) {
821 case SDL_CONTROLLER_AXIS_LEFTX
: joy_x
[0] = normal_value
; break;
822 case SDL_CONTROLLER_AXIS_LEFTY
: joy_y
[0] = normal_value
; break;
823 case SDL_CONTROLLER_AXIS_RIGHTX
: joy_x
[1] = normal_value
; break;
824 case SDL_CONTROLLER_AXIS_RIGHTY
: joy_y
[1] = normal_value
; break;
825 //HACK: consider both triggers as buttons for now
826 case SDL_CONTROLLER_AXIS_TRIGGERLEFT
: CtlTriggerButton(0, (normal_value
!= 0)); break;
827 case SDL_CONTROLLER_AXIS_TRIGGERRIGHT
: CtlTriggerButton(1, (normal_value
!= 0)); break;
831 case SDL_CONTROLLERBUTTONDOWN
:
832 case SDL_CONTROLLERBUTTONUP
:
833 if (controller
&& ev
.cbutton
.which
== jid
) {
835 const char *buttName
= "<unknown>";
836 switch (ev
.cbutton
.button
) {
837 case SDL_CONTROLLER_BUTTON_A
: buttName
= "K_BUTTON_A"; break;
838 case SDL_CONTROLLER_BUTTON_B
: buttName
= "K_BUTTON_B"; break;
839 case SDL_CONTROLLER_BUTTON_X
: buttName
= "K_BUTTON_X"; break;
840 case SDL_CONTROLLER_BUTTON_Y
: buttName
= "K_BUTTON_Y"; break;
841 case SDL_CONTROLLER_BUTTON_BACK
: buttName
= "K_BUTTON_BACK"; break;
842 case SDL_CONTROLLER_BUTTON_GUIDE
: buttName
= "K_BUTTON_GUIDE"; break;
843 case SDL_CONTROLLER_BUTTON_START
: buttName
= "K_BUTTON_START"; break;
844 case SDL_CONTROLLER_BUTTON_LEFTSTICK
: buttName
= "K_BUTTON_LEFTSTICK"; break;
845 case SDL_CONTROLLER_BUTTON_RIGHTSTICK
: buttName
= "K_BUTTON_RIGHTSTICK"; break;
846 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER
: buttName
= "K_BUTTON_LEFTSHOULDER"; break;
847 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER
: buttName
= "K_BUTTON_RIGHTSHOULDER"; break;
848 case SDL_CONTROLLER_BUTTON_DPAD_UP
: buttName
= "K_BUTTON_DPAD_UP"; break;
849 case SDL_CONTROLLER_BUTTON_DPAD_DOWN
: buttName
= "K_BUTTON_DPAD_DOWN"; break;
850 case SDL_CONTROLLER_BUTTON_DPAD_LEFT
: buttName
= "K_BUTTON_DPAD_LEFT"; break;
851 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT
: buttName
= "K_BUTTON_DPAD_RIGHT"; break;
854 GCon
->Logf(NAME_Debug
, "CTL %d BUTTON %s: '%s' (%d)", ev
.cbutton
.which
, (ev
.cbutton
.state
== SDL_PRESSED
? "DOWN" : "UP"), buttName
, ev
.cbutton
.button
);
856 switch (ev
.cbutton
.button
) {
857 case SDL_CONTROLLER_BUTTON_A
: GInput
->PostKeyEvent(K_BUTTON_A
, (ev
.cbutton
.state
== SDL_PRESSED
? 1 : 0), curmodflags
); break;
858 case SDL_CONTROLLER_BUTTON_B
: GInput
->PostKeyEvent(K_BUTTON_B
, (ev
.cbutton
.state
== SDL_PRESSED
? 1 : 0), curmodflags
); break;
859 case SDL_CONTROLLER_BUTTON_X
: GInput
->PostKeyEvent(K_BUTTON_X
, (ev
.cbutton
.state
== SDL_PRESSED
? 1 : 0), curmodflags
); break;
860 case SDL_CONTROLLER_BUTTON_Y
: GInput
->PostKeyEvent(K_BUTTON_Y
, (ev
.cbutton
.state
== SDL_PRESSED
? 1 : 0), curmodflags
); break;
861 case SDL_CONTROLLER_BUTTON_BACK
: GInput
->PostKeyEvent(K_BUTTON_BACK
, (ev
.cbutton
.state
== SDL_PRESSED
? 1 : 0), curmodflags
); break;
862 case SDL_CONTROLLER_BUTTON_GUIDE
: GInput
->PostKeyEvent(K_BUTTON_GUIDE
, (ev
.cbutton
.state
== SDL_PRESSED
? 1 : 0), curmodflags
); break;
863 case SDL_CONTROLLER_BUTTON_START
: GInput
->PostKeyEvent(K_BUTTON_START
, (ev
.cbutton
.state
== SDL_PRESSED
? 1 : 0), curmodflags
); break;
864 case SDL_CONTROLLER_BUTTON_LEFTSTICK
: GInput
->PostKeyEvent(K_BUTTON_LEFTSTICK
, (ev
.cbutton
.state
== SDL_PRESSED
? 1 : 0), curmodflags
); break;
865 case SDL_CONTROLLER_BUTTON_RIGHTSTICK
: GInput
->PostKeyEvent(K_BUTTON_RIGHTSTICK
, (ev
.cbutton
.state
== SDL_PRESSED
? 1 : 0), curmodflags
); break;
866 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER
: GInput
->PostKeyEvent(K_BUTTON_LEFTSHOULDER
, (ev
.cbutton
.state
== SDL_PRESSED
? 1 : 0), curmodflags
); break;
867 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER
: GInput
->PostKeyEvent(K_BUTTON_RIGHTSHOULDER
, (ev
.cbutton
.state
== SDL_PRESSED
? 1 : 0), curmodflags
); break;
868 case SDL_CONTROLLER_BUTTON_DPAD_UP
: GInput
->PostKeyEvent(K_BUTTON_DPAD_UP
, (ev
.cbutton
.state
== SDL_PRESSED
? 1 : 0), curmodflags
); break;
869 case SDL_CONTROLLER_BUTTON_DPAD_DOWN
: GInput
->PostKeyEvent(K_BUTTON_DPAD_DOWN
, (ev
.cbutton
.state
== SDL_PRESSED
? 1 : 0), curmodflags
); break;
870 case SDL_CONTROLLER_BUTTON_DPAD_LEFT
: GInput
->PostKeyEvent(K_BUTTON_DPAD_LEFT
, (ev
.cbutton
.state
== SDL_PRESSED
? 1 : 0), curmodflags
); break;
871 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT
: GInput
->PostKeyEvent(K_BUTTON_DPAD_RIGHT
, (ev
.cbutton
.state
== SDL_PRESSED
? 1 : 0), curmodflags
); break;
876 case SDL_CONTROLLERDEVICEADDED
:
877 //GCon->Logf(NAME_Debug, "CTL %d added", ev.cdevice.which);
878 if (SDL_NumJoysticks() == 1 || !(joystick
&& !controller
)) {
879 GCon
->Logf(NAME_Debug
, "controller attached");
883 case SDL_CONTROLLERDEVICEREMOVED
:
884 //GCon->Logf(NAME_Debug, "CTL %d removed", ev.cdevice.which);
885 if (joystick_controller
&& ev
.cdevice
.which
== jid
) {
886 ShutdownJoystick(false);
887 GCon
->Logf(NAME_Debug
, "controller detached");
890 /*k8: i don't know what to do here
891 case SDL_CONTROLLERDEVICEREMAPPED:
892 GCon->Logf(NAME_Debug, "CTL %d remapped", ev.cdevice.which);
893 if (joystick_controller && ev.cdevice.which == jid) {
898 case SDL_FINGERMOTION
:
899 Touch_Event(TouchMotion
, ev
.tfinger
.fingerId
, ev
.tfinger
.x
, ev
.tfinger
.y
, ev
.tfinger
.dx
, ev
.tfinger
.dy
, ev
.tfinger
.pressure
);
902 Touch_Event(TouchDown
, ev
.tfinger
.fingerId
, ev
.tfinger
.x
, ev
.tfinger
.y
, ev
.tfinger
.dx
, ev
.tfinger
.dy
, ev
.tfinger
.pressure
);
905 Touch_Event(TouchUp
, ev
.tfinger
.fingerId
, ev
.tfinger
.x
, ev
.tfinger
.y
, ev
.tfinger
.dx
, ev
.tfinger
.dy
, ev
.tfinger
.pressure
);
909 case SDL_WINDOWEVENT
:
910 switch (ev
.window
.event
) {
911 case SDL_WINDOWEVENT_FOCUS_GAINED
:
912 //GCon->Logf(NAME_Debug, "***FOCUS GAIN; wa=%d; first=%d", (int)winactive, (int)firsttime);
915 if (!winactive
&& mouse
) {
917 if (!ui_freemouse
&& (!ui_active
|| IsUIMouse())) Drawer
->WarpMouseToWindowCenter();
918 //SDL_GetMouseState(&mouseLastX, &mouseLastY);
919 if (relativeFailed
|| !m_relative
.asBool()) Drawer
->GetMousePosition(&mouseLastX
, &mouseLastY
);
924 if (cl
) cl
->ClearInput();
925 if (in_focusgain_delay
.asFloat() > 0) {
927 timeKbdAllow
= Sys_Time()+in_focusgain_delay
.asFloat();
928 //GCon->Log(NAME_Debug, "***FOCUS GAIN: KBDSUSPEND!");
931 vev
.type
= ev_winfocus
;
933 VObject::PostEvent(vev
);
935 case SDL_WINDOWEVENT_FOCUS_LOST
:
936 //fprintf(stderr, "***FOCUS LOST; first=%d; drawer=%p\n", (int)firsttime, Drawer);
941 if (cl
) cl
->ClearInput();
942 kbdSuspended
= false;
944 vev
.type
= ev_winfocus
;
946 VObject::PostEvent(vev
);
948 //case SDL_WINDOWEVENT_TAKE_FOCUS: Drawer->SDL_SetWindowInputFocus();
949 case SDL_WINDOWEVENT_RESIZED
:
950 // this seems to be called on videomode change; but this is not reliable
951 //GCon->Logf("SDL: resized to %dx%d", ev.window.data1, ev.window.data2);
953 case SDL_WINDOWEVENT_SIZE_CHANGED
:
954 //GCon->Logf("SDL: size changed to %dx%d", ev.window.data1, ev.window.data2);
959 bGotCloseRequest
= true;
967 // read mouse separately
968 if (mouse
&& winactive
&& Drawer
) {
972 mouseCurrX
= mouseLastX
= clampval(mouseLastX
+rel_x
, 0, Drawer
->getWidth()-1);
973 mouseCurrY
= mouseLastY
= clampval(mouseLastY
+rel_y
, 0, Drawer
->getHeight()-1);
977 bool currMouseInUI
= (ui_active
.asBool() || ui_freemouse
.asBool());
978 bool currMouseGrabbed
= (ui_freemouse
.asBool() ? false : IsUIMouse());
979 if (!currMouseInUI
) {
980 if ((!relativeFailed
&& currRelative
!= m_relative
.asBool()) ||
981 currDoGrab
!= m_grab
.asBool())
983 #ifdef SDL_MOUSE_CAPTURE_DEBUG
984 GCon
->Logf(NAME_Debug
, "SDL: mouse mode changed, recapturing...");
990 //SDL_GetMouseState(&mouseCurrX, &mouseCurrY);
991 if (!currRelative
) Drawer
->GetMousePosition(&mouseCurrX
, &mouseCurrY
);
992 // check for UI activity changes
993 if (currMouseInUI
!= uiwasactive
) {
994 // UI activity changed
995 uiwasactive
= currMouseInUI
;
996 uimouselast
= currMouseGrabbed
;
1005 if (curHidden
&& ui_want_mouse_at_zero
) SDL_WarpMouseGlobal(0, 0);
1010 // check for "allow mouse in UI" changes
1011 if (currMouseGrabbed
!= uimouselast
) {
1012 // "allow mouse in UI" changed
1014 if (gl_current_screen_fsmode
== 0) {
1015 if (currMouseGrabbed
) HideRealMouse(); else ShowRealMouse();
1019 if (GRoot
) GRoot
->SetMouse(currMouseGrabbed
);
1021 uimouselast
= currMouseGrabbed
;
1023 // hide real mouse in fullscreen mode, show in windowed mode (if necessary)
1024 if (gl_current_screen_fsmode
!= 0 && !curHidden
) HideRealMouse();
1025 if (gl_current_screen_fsmode
== 0 && curHidden
&& currMouseInUI
&& !currMouseGrabbed
) ShowRealMouse();
1027 if (!currMouseInUI
|| currMouseGrabbed
) {
1032 if (true/*wasMouseButtons*/) {
1033 GCon
->Log(NAME_Debug
, "=== BEFORE EVENTS ===");
1034 for (int eidx
= 0; eidx
< VObject::CountQueuedEvents(); ++eidx
) {
1035 if (!VObject::PeekEvent(eidx
, &vev
)) break;
1036 GCon
->Logf(NAME_Debug
, " %2d: type=%d; data1=%d; data2=%d; data3=%d; data4=%d", eidx
, vev
.type
, vev
.data1
, vev
.data2
, vev
.data3
, vev
.data4
);
1041 vev
.modflags
= curmodflags
;
1042 vev
.type
= ev_mouse
;
1045 if (true/*wasMouseButtons*/) VObject::InsertEvent(vev
); else VObject::PostEvent(vev
);
1047 if (true/*wasMouseButtons*/) {
1048 GCon
->Log(NAME_Debug
, "--- AFTER EVENTS ---");
1049 for (int eidx
= 0; eidx
< VObject::CountQueuedEvents(); ++eidx
) {
1050 if (!VObject::PeekEvent(eidx
, &vev
)) break;
1051 GCon
->Logf(NAME_Debug
, " %2d: type=%d; data1=%d; data2=%d; data3=%d; data4=%d", eidx
, vev
.type
, vev
.data1
, vev
.data2
, vev
.data3
, vev
.data4
);
1053 GCon
->Log(NAME_Debug
, "--------------------");
1058 vev
.modflags
= curmodflags
;
1059 vev
.type
= ev_uimouse
;
1062 VObject::PostEvent(vev
);
1066 Drawer
->WarpMouseToWindowCenter();
1067 int dx
= mouseCurrX
-mouseLastX
;
1068 int dy
= mouseLastY
-mouseCurrY
;
1072 vev
.modflags
= curmodflags
;
1073 vev
.type
= ev_mouse
;
1076 if (true/*wasMouseButtons*/) VObject::InsertEvent(vev
); else VObject::PostEvent(vev
);
1079 vev
.modflags
= curmodflags
;
1080 vev
.type
= ev_uimouse
;
1083 VObject::PostEvent(vev
);
1086 Drawer
->GetMousePosition(&mouseLastX
, &mouseLastY
);
1090 mouseLastX
= mouseCurrX
;
1091 mouseLastY
= mouseCurrY
;
1104 //**************************************************************************
1108 //**************************************************************************
1110 //==========================================================================
1112 // VSdlInputDevice::ShutdownJoystick
1114 //==========================================================================
1115 void VSdlInputDevice::ShutdownJoystick (bool closesubsys
) {
1116 if (haptic
) { SDL_HapticClose(haptic
); haptic
= nullptr; }
1117 if (joystick
) { SDL_JoystickClose(joystick
); joystick
= nullptr; }
1118 if (controller
) { SDL_GameControllerClose(controller
); controller
= nullptr; }
1120 if (has_haptic
) { SDL_QuitSubSystem(SDL_INIT_HAPTIC
); has_haptic
= false; }
1121 if (joystick_started
) { SDL_QuitSubSystem(VSDL_JINIT
); joystick_started
= false; }
1123 joy_num_buttons
= 0;
1124 joystick_controller
= false;
1127 memset(&joy_x
[0], 0, 2*sizeof(joy_x
[0]));
1128 memset(&joy_y
[0], 0, 2*sizeof(joy_y
[0]));
1129 memset(&joy_oldx
[0], 0, 2*sizeof(joy_oldx
[0]));
1130 memset(&joy_oldy
[0], 0, 2*sizeof(joy_oldy
[0]));
1132 SDL_JoystickEventState(SDL_IGNORE
);
1133 SDL_GameControllerEventState(SDL_IGNORE
);
1138 //==========================================================================
1140 // VSdlInputDevice::LoadControllerMappings
1142 // deletes stream; it is ok to pass `nullptr`
1144 //==========================================================================
1145 void VSdlInputDevice::LoadControllerMappings (VStream
*st
) {
1148 VStr stname
= st
->GetName();
1149 int size
= st
->TotalSize();
1150 if (size
<= 0 || size
> 1024*1024*32) {
1151 if (size
) GCon
->Logf(NAME_Error
, "cannot load controller mappings from '%s' (bad file size)", *stname
);
1152 VStream::Destroy(st
);
1156 char *buf
= new char[(unsigned)(size
+1)];
1157 st
->Serialise(buf
, size
);
1158 const bool wasErr
= st
->IsError();
1159 VStream::Destroy(st
);
1161 GCon
->Logf(NAME_Error
, "cannot read controller mappings from '%s'", *stname
);
1166 SDL_RWops
*rwo
= SDL_RWFromConstMem(buf
, size
);
1168 const int count
= SDL_GameControllerAddMappingsFromRW(rwo
, 1); // free rwo
1170 GCon
->Logf(NAME_Error
, "cannot read controller mappings from '%s'", *stname
);
1171 } else if (count
> 0) {
1172 GCon
->Logf(NAME_Init
, "read %d controller mapping%s from '%s'", count
, (count
!= 1 ? "s" : ""), *stname
);
1175 GCon
->Logf(NAME_Error
, "cannot read controller mappings from '%s' (rwo)", *stname
);
1182 //==========================================================================
1184 // VSdlInputDevice::StartupJoystick
1186 // Initialises joystick
1188 //==========================================================================
1189 void VSdlInputDevice::OpenJoystick (int jnum
) {
1190 if (!joystick_started
) return;
1192 ShutdownJoystick(false);
1194 int joycount
= SDL_NumJoysticks();
1196 GCon
->Log(NAME_Init
, "SDL: joystick/controller initialisation failed (cannot get number of joysticks)");
1197 ShutdownJoystick(true);
1201 if (jnum
>= joycount
) jnum
= joycount
-1;
1202 if (jnum
< 0) jnum
= 0;
1205 if (joycount
== 0) return;
1207 if (SDL_IsGameController(joynum
)) {
1209 GCon
->Log(NAME_Init
, "SDL: skipping controller initialisation due to user request");
1210 ShutdownJoystick(false);
1213 joystick_controller
= true;
1214 joy_num_buttons
= 0;
1215 controller
= SDL_GameControllerOpen(joynum
);
1217 GCon
->Logf(NAME_Init
, "SDL: cannot initialise controller #%d", joynum
);
1218 ShutdownJoystick(false);
1221 GCon
->Logf(NAME_Init
, "SDL: joystick is a controller (%s)", SDL_GameControllerNameForIndex(joynum
));
1222 SDL_Joystick
*cj
= SDL_GameControllerGetJoystick(controller
);
1224 GCon
->Log(NAME_Init
, "SDL: controller initialisation failed (cannot get joystick info)");
1225 ShutdownJoystick(true);
1228 jid
= SDL_JoystickInstanceID(cj
);
1230 haptic
= SDL_HapticOpenFromJoystick(cj
);
1232 GCon
->Logf(NAME_Init
, "SDL: found haptic support for controller");
1234 GCon
->Logf(NAME_Init
, "SDL: cannot open haptic interface");
1239 GCon
->Log(NAME_Init
, "SDL: skipping joystick initialisation due to user request");
1240 ShutdownJoystick(false);
1243 joystick
= SDL_JoystickOpen(joynum
);
1245 GCon
->Logf(NAME_Init
, "SDL: cannot initialise joystick #%d", joynum
);
1246 ShutdownJoystick(false);
1249 jid
= SDL_JoystickInstanceID(joystick
);
1250 joy_num_buttons
= SDL_JoystickNumButtons(joystick
);
1251 GCon
->Logf(NAME_Init
, "SDL: found joystick with %d buttons", joy_num_buttons
);
1254 SDL_JoystickEventState(SDL_ENABLE
);
1255 SDL_GameControllerEventState(SDL_ENABLE
);
1259 //==========================================================================
1261 // VSdlInputDevice::StartupJoystick
1263 // Initialises joystick
1265 //==========================================================================
1266 void VSdlInputDevice::StartupJoystick () {
1267 ShutdownJoystick(true);
1269 if (cli_NoJoy
&& cli_NoCotl
) {
1270 //GCon->Log(NAME_Init, "SDL: skipping joystick/controller initialisation due to user request");
1274 // load user controller mappings
1275 VStream
*rcstrm
= FL_OpenFileReadInCfgDir("gamecontrollerdb.txt");
1277 GCon
->Log(NAME_Init
, "SDL: loading user-defined controller map");
1278 LoadControllerMappings(rcstrm
);
1280 // no user mappings, load built-in mappings
1281 rcstrm
= FL_OpenFileReadBaseOnly("gamecontrollerdb.txt");
1282 GCon
->Log(NAME_Init
, "SDL: loading built-in controller map");
1283 LoadControllerMappings(rcstrm
);
1286 //FIXME: change this!
1288 for (int jparm
= GArgs
.CheckParmFrom("-joy", -1, true); jparm
; jparm
= GArgs
.CheckParmFrom("-joy", jparm
, true)) {
1289 const char *jarg
= GArgs
[jparm
];
1290 //GCon->Logf("***%d:<%s>", jparm, jarg);
1291 vassert(jarg
); // just in case
1293 if (!jarg
[0]) continue;
1295 if (!VStr::convertInt(jarg
, &n
)) continue;
1296 if (n
< 0) continue;
1299 GCon
->Logf(NAME_Init
, "SDL: will try to use joystick #%d", jnum
);
1301 if (SDL_InitSubSystem(VSDL_JINIT
) < 0) {
1302 GCon
->Log(NAME_Init
, "SDL: joystick/controller initialisation failed");
1307 joystick_started
= true;
1311 } else if (SDL_InitSubSystem(SDL_INIT_HAPTIC
) < 0) {
1313 GCon
->Log(NAME_Init
, "SDL: cannot init haptic subsystem, force feedback disabled");
1316 GCon
->Log(NAME_Init
, "SDL: force feedback enabled");
1319 int joycount
= SDL_NumJoysticks();
1321 GCon
->Log(NAME_Init
, "SDL: joystick/controller initialisation failed (cannot get number of joysticks)");
1322 ShutdownJoystick(true);
1328 SDL_JoystickEventState(SDL_ENABLE
);
1329 SDL_GameControllerEventState(SDL_ENABLE
);
1331 if (joycount
== 0) {
1332 GCon
->Log(NAME_Init
, "SDL: no joysticks found");
1336 GCon
->Logf(NAME_Init
, "SDL: %d joystick%s found", joycount
, (joycount
== 1 ? "" : "s"));
1338 if (joycount
== 1 || (jnum
>= 0 && jnum
< joycount
)) OpenJoystick(jnum
);
1342 //==========================================================================
1344 // VSdlInputDevice::PostJoystick
1346 //==========================================================================
1347 void VSdlInputDevice::PostJoystick () {
1348 if (!joystick_started
|| !(joystick
|| controller
)) return;
1350 for (unsigned jn
= 0; jn
< 2; ++jn
) {
1351 if (joy_oldx
[jn
] != joy_x
[jn
] || joy_oldy
[jn
] != joy_y
[jn
]) {
1352 //GCon->Logf(NAME_Debug, "joystick: %d (old:%d), %d (old:%d)", joy_x, joy_oldx, joy_y, joy_oldy);
1355 event
.type
= ev_joystick
;
1356 event
.dx
= joy_x
[jn
];
1357 event
.dy
= joy_y
[jn
];
1358 event
.joyidx
= (int)jn
;
1359 event
.modflags
= curmodflags
;
1360 VObject::PostEvent(event
);
1362 joy_oldx
[jn
] = joy_x
[jn
];
1363 joy_oldy
[jn
] = joy_y
[jn
];
1368 const int nb
= min2(joy_num_buttons
, 15);
1369 for (int i
= 0; i
< nb
; ++i
) {
1370 if ((joy_newb
&(1u<<i
)) != (joy_oldb
&(1u<<i
))) {
1371 const bool pressed
= !!(joy_newb
&(1u<<i
));
1372 GInput
->PostKeyEvent(K_JOY1
+i
, pressed
, curmodflags
);
1374 joy_oldb
= joy_newb
;
1380 //==========================================================================
1382 // VSdlInputDevice::CheckForEscape
1384 //==========================================================================
1385 bool VSdlInputDevice::CheckForEscape () {
1386 bool res
= bGotCloseRequest
;
1389 VInputPublic::UnpressAll();
1392 while (!bGotCloseRequest
&& SDL_PollEvent(&ev
)) {
1396 switch (ev
.key
.keysym
.sym
) {
1397 case SDLK_ESCAPE
: res
= true; break;
1402 bGotCloseRequest
= true;
1404 GCmdBuf
<< "Quit\n";
1415 //**************************************************************************
1419 //**************************************************************************
1421 //==========================================================================
1423 // VInputDevice::CreateDevice
1425 //==========================================================================
1426 VInputDevice
*VInputDevice::CreateDevice () {
1427 return new VSdlInputDevice();