1
0
Fork 0

Expand mouse control methods

Replace the [sdl] `autolock = true/false` configuration setting with [sdl]
`capture_mouse = ...` with a two-value setting.

The first value defines how the mouse is controlled:
- onclick:       The mouse will be captured with a click inside the window.
- onstart:       The mouse is captured immediately on start (similar to real DOS).
- seamless:      The mouse will move seamlessly in and out of DOSBox and cannot be captured.
- nomouse:       The mouse is disabled and hidden without any input sent to the game.

The second value defines how middle-clicks are handled:
- middlegame:    Middle-clicks are sent to the game (not used to uncapture the mouse).
- middlerelease: Middle-click will uncapture the mouse when windowed (not sent to the game).

Middle-clicks are sent to the game when fullscreen or when seamless control is set.

The default setting of "onclick middlegame" reproduces DOSBox's existing behavior.
This commit is contained in:
krcroft 2020-01-31 11:10:05 -08:00 committed by Patryk Obara
parent 7dbf5cece4
commit 3446db1ee9
4 changed files with 180 additions and 97 deletions

View file

@ -69,8 +69,4 @@ bool GFX_SDLUsingWinDIB(void);
void MAPPER_UpdateJoysticks(void);
#endif
/* Mouse related */
void GFX_CaptureMouse(void);
extern bool mouselocked; //true if mouse is confined to window
#endif

View file

@ -40,6 +40,10 @@
#include "support.h"
#include "video.h"
/* Mouse related */
void GFX_ToggleMouseCapture(void);
extern SDL_bool mouse_is_captured; //true if mouse is confined to window
enum {
CLR_BLACK=0,
CLR_GREY=1,
@ -2492,10 +2496,10 @@ SDL_Surface* SDL_SetVideoMode_Wrap(int width,int height,int bpp,Bit32u flags);
void MAPPER_RunInternal() {
int cursor = SDL_ShowCursor(SDL_QUERY);
SDL_ShowCursor(SDL_ENABLE);
bool mousetoggle=false;
if(mouselocked) {
mousetoggle=true;
GFX_CaptureMouse();
bool mousetoggle = false;
if (mouse_is_captured) {
mousetoggle = true;
GFX_ToggleMouseCapture();
}
/* Be sure that there is no update in progress */
@ -2545,7 +2549,8 @@ void MAPPER_RunInternal() {
#if defined (REDUCE_JOYSTICK_POLLING)
SDL_JoystickEventState(SDL_DISABLE);
#endif
if(mousetoggle) GFX_CaptureMouse();
if (mousetoggle)
GFX_ToggleMouseCapture();
SDL_ShowCursor(cursor);
GFX_ResetScreen();
}

View file

@ -23,6 +23,7 @@
#include "dosbox.h"
#include <cassert>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
@ -112,6 +113,14 @@ extern char** environ;
#define PRIO_TOTAL (PRIO_MAX-PRIO_MIN)
#endif
SDL_bool mouse_is_captured = SDL_FALSE; // global for mapper
enum MouseControlType {
CaptureOnClick = 1 << 0,
CaptureOnStart = 1 << 1,
Seamless = 1 << 2,
NoMouse = 1 << 3
};
enum SCREEN_TYPES {
SCREEN_SURFACE,
@ -192,13 +201,11 @@ struct SDL_Block {
} texture;
SDL_cond *cond;
struct {
bool autolock;
bool autoenable;
bool requestlock;
bool locked;
int xsensitivity;
int ysensitivity;
Bitu sensitivity;
MouseControlType control_choice;
bool middle_will_release;
} mouse;
SDL_Rect updateRects[1024];
Bitu num_joysticks;
@ -846,28 +853,61 @@ dosurface:
}//CASE
if (retFlags)
GFX_Start();
if (!sdl.mouse.autoenable) SDL_ShowCursor(sdl.mouse.autolock?SDL_DISABLE:SDL_ENABLE);
return retFlags;
}
void GFX_ToggleMouseCapture(void) {
assertm(sdl.mouse.control_choice != NoMouse,
"SDL: Mouse capture is invalid when NoMouse is configured [Logic Bug]");
void GFX_CaptureMouse(void) {
sdl.mouse.locked=!sdl.mouse.locked;
if (sdl.mouse.locked) {
SDL_SetRelativeMouseMode(SDL_TRUE);
SDL_ShowCursor(SDL_DISABLE);
} else {
SDL_SetRelativeMouseMode(SDL_FALSE);
if (sdl.mouse.autoenable || !sdl.mouse.autolock) SDL_ShowCursor(SDL_ENABLE);
}
mouselocked=sdl.mouse.locked;
mouse_is_captured = mouse_is_captured ? SDL_FALSE : SDL_TRUE;
if (SDL_SetRelativeMouseMode(mouse_is_captured) != 0) {
SDL_ShowCursor(SDL_ENABLE);
E_Exit("SDL: failed to %s relative-mode [SDL Bug]",
mouse_is_captured ? "put the mouse in" : "take the mouse out of");
}
LOG_MSG("SDL: %s the mouse", mouse_is_captured ? "captured" : "released");
}
bool mouselocked; //Global variable for mapper
static void CaptureMouse(bool pressed) {
if (!pressed)
static void ToggleMouseCapture(bool pressed) {
if (!pressed || sdl.desktop.fullscreen)
return;
GFX_CaptureMouse();
GFX_ToggleMouseCapture();
}
void GFX_UpdateMouseAfterExposure(void) {
// Used below
static bool has_run_once = false;
// We've switched to or started in fullscreen, so capture the mouse
// This is valid for all modes except for nomouse.
if (sdl.desktop.fullscreen
&& !mouse_is_captured
&& sdl.mouse.control_choice != NoMouse) {
GFX_ToggleMouseCapture();
// If we've switched-back from fullscreen, then released the mouse
// if it's captured and in seamless-mode.
} else if (!sdl.desktop.fullscreen
&& mouse_is_captured
&& sdl.mouse.control_choice == Seamless) {
GFX_ToggleMouseCapture();
SDL_ShowCursor(SDL_DISABLE);
// If none of the above are true /and/ has_run_once is false,
// then we're starting up the first time, so we:
// - Capture the mouse if configured onstart is set.
// - Hide the mouse if seamless or nomouse are set.
} else if (!has_run_once) {
if (sdl.mouse.control_choice == CaptureOnStart) {
SDL_RaiseWindow(sdl.window);
GFX_ToggleMouseCapture();
} else if (sdl.mouse.control_choice & (Seamless | NoMouse)) {
SDL_ShowCursor(SDL_DISABLE);
}
}
has_run_once = true;
}
#if defined (WIN32)
@ -893,19 +933,15 @@ void sticky_keys(bool restore){
#endif
void GFX_SwitchFullScreen(void) {
sdl.desktop.fullscreen=!sdl.desktop.fullscreen;
if (sdl.desktop.fullscreen) {
if (!sdl.mouse.locked) GFX_CaptureMouse();
#if defined (WIN32)
sticky_keys(false); //disable sticky keys in fullscreen mode
// We are about to switch to the opposite of our current mode
// (ie: opposite of whatever sdl.desktop.fullscreen holds).
// Sticky-keys should be set to the opposite of fullscreen,
// so we simply apply the bool of the mode we're switching out-of.
sticky_keys(sdl.desktop.fullscreen);
#endif
} else {
if (sdl.mouse.locked) GFX_CaptureMouse();
#if defined (WIN32)
sticky_keys(true); //restore sticky keys to default state in windowed mode.
#endif
}
GFX_ResetScreen();
sdl.desktop.fullscreen = !sdl.desktop.fullscreen;
GFX_ResetScreen();
}
static void SwitchFullScreen(bool pressed) {
@ -1102,8 +1138,8 @@ void GFX_UpdateDisplayDimensions(int width, int height)
static void GUI_ShutDown(Section * /*sec*/) {
GFX_Stop();
if (sdl.draw.callback) (sdl.draw.callback)( GFX_CallBackStop );
if (sdl.mouse.locked) GFX_CaptureMouse();
if (sdl.desktop.fullscreen) GFX_SwitchFullScreen();
if (mouse_is_captured) GFX_ToggleMouseCapture();
}
@ -1224,9 +1260,6 @@ static void GUI_StartUp(Section * sec) {
}
SetPriority(sdl.priority.focus); //Assume focus on startup
sdl.mouse.locked=false;
mouselocked=false; //Global for mapper
sdl.mouse.requestlock=false;
sdl.desktop.full.fixed=false;
const char* fullresolution=section->Get_string("fullresolution");
sdl.desktop.full.width = 0;
@ -1283,18 +1316,8 @@ static void GUI_StartUp(Section * sec) {
GFX_ObtainDisplayDimensions();
}
sdl.mouse.autoenable=section->Get_bool("autolock");
if (!sdl.mouse.autoenable) SDL_ShowCursor(SDL_DISABLE);
sdl.mouse.autolock=false;
Prop_multival* p3 = section->Get_multival("sensitivity");
sdl.mouse.xsensitivity = p3->GetSection()->Get_int("xsens");
sdl.mouse.ysensitivity = p3->GetSection()->Get_int("ysens");
std::string output=section->Get_string("output");
/* Setup Mouse correctly if fullscreen */
if(sdl.desktop.fullscreen) GFX_CaptureMouse();
if (output == "surface") {
sdl.desktop.want_type=SCREEN_SURFACE;
} else if (output == "texture") {
@ -1458,12 +1481,50 @@ static void GUI_StartUp(Section * sec) {
}
SDL_FreeSurface(splash_surf);
delete [] tmpbufp;
}
// Apply the user's mouse settings
Section_prop* s = section->Get_multival("capture_mouse")->GetSection();
const std::string control_choice = s->Get_string("mouse_control");
std::string mouse_control_msg;
if (control_choice == "onclick") {
sdl.mouse.control_choice = CaptureOnClick;
mouse_control_msg = "will be captured after clicking";
} else if (control_choice == "onstart") {
sdl.mouse.control_choice = CaptureOnStart;
mouse_control_msg = "will be captured immediately on start";
} else if (control_choice == "seamless") {
sdl.mouse.control_choice = Seamless;
mouse_control_msg = "will move seamlessly without being captured";
} else if (control_choice == "nomouse") {
sdl.mouse.control_choice = NoMouse;
mouse_control_msg = "will not be active";
} else {
assert(sdl.mouse.control_choice == CaptureOnClick);
}
std:: string middle_control_msg;
if (std::string(s->Get_string("middle_control")) == "middlerelease") {
sdl.mouse.middle_will_release = true;
if (sdl.mouse.control_choice & (CaptureOnClick | CaptureOnStart))
middle_control_msg = " and middle-click will uncapture the mouse";
} else {
if (sdl.mouse.control_choice & (CaptureOnClick | CaptureOnStart))
middle_control_msg = " and middle-clicks will be sent to the game";
}
LOG_MSG("SDL: Mouse %s%s.", mouse_control_msg.c_str(), middle_control_msg.c_str());
// Only setup the Ctrl+F10 handler if the mouse is capturable
if (sdl.mouse.control_choice & (CaptureOnStart | CaptureOnClick)) {
MAPPER_AddHandler(ToggleMouseCapture,MK_f10,MMOD1,"capmouse","Cap Mouse");
}
// Apply the user's mouse sensitivity settings
Prop_multival* p3 = section->Get_multival("sensitivity");
sdl.mouse.xsensitivity = p3->GetSection()->Get_int("xsens");
sdl.mouse.ysensitivity = p3->GetSection()->Get_int("ysens");
/* Get some Event handlers */
MAPPER_AddHandler(KillSwitch,MK_f9,MMOD1,"shutdown","ShutDown");
MAPPER_AddHandler(CaptureMouse,MK_f10,MMOD1,"capmouse","Cap Mouse");
MAPPER_AddHandler(SwitchFullScreen,MK_return,MMOD2,"fullscr","Fullscreen");
MAPPER_AddHandler(Restart,MK_home,MMOD1|MMOD2,"restart","Restart");
#if C_DEBUG
@ -1477,36 +1538,26 @@ static void GUI_StartUp(Section * sec) {
if(keystate&KMOD_CAPS) startup_state_capslock = true;
}
void Mouse_AutoLock(bool enable) {
sdl.mouse.autolock=enable;
if (sdl.mouse.autoenable) sdl.mouse.requestlock=enable;
else {
SDL_ShowCursor(enable?SDL_DISABLE:SDL_ENABLE);
sdl.mouse.requestlock=false;
}
}
static void HandleMouseMotion(SDL_MouseMotionEvent * motion) {
if (sdl.mouse.locked || !sdl.mouse.autoenable)
if (mouse_is_captured || sdl.mouse.control_choice == Seamless)
Mouse_CursorMoved((float)motion->xrel*sdl.mouse.xsensitivity/100.0f,
(float)motion->yrel*sdl.mouse.ysensitivity/100.0f,
(float)(motion->x-sdl.clip.x)/(sdl.clip.w-1)*sdl.mouse.xsensitivity/100.0f,
(float)(motion->y-sdl.clip.y)/(sdl.clip.h-1)*sdl.mouse.ysensitivity/100.0f,
sdl.mouse.locked);
mouse_is_captured);
}
static void HandleMouseButton(SDL_MouseButtonEvent * button) {
switch (button->state) {
case SDL_PRESSED:
if (sdl.mouse.requestlock && !sdl.mouse.locked) {
GFX_CaptureMouse();
// Don't pass click to mouse handler
break;
}
if (!sdl.mouse.autoenable && sdl.mouse.autolock && button->button == SDL_BUTTON_MIDDLE) {
GFX_CaptureMouse();
break;
}
if (!sdl.desktop.fullscreen
&& sdl.mouse.control_choice & (CaptureOnStart | CaptureOnClick)
&& ((sdl.mouse.middle_will_release && button->button == SDL_BUTTON_MIDDLE)
|| !mouse_is_captured)) {
GFX_ToggleMouseCapture();
break; // Don't pass click to mouse handler
}
switch (button->button) {
case SDL_BUTTON_LEFT:
Mouse_ButtonPressed(0);
@ -1617,23 +1668,19 @@ void GFX_Events() {
continue;
case SDL_WINDOWEVENT_EXPOSED:
if (sdl.draw.callback) sdl.draw.callback( GFX_CallBackRedraw );
GFX_UpdateMouseAfterExposure();
continue;
case SDL_WINDOWEVENT_FOCUS_GAINED:
if (sdl.desktop.fullscreen && !sdl.mouse.locked)
GFX_CaptureMouse();
SetPriority(sdl.priority.focus);
CPU_Disable_SkipAutoAdjust();
break;
case SDL_WINDOWEVENT_FOCUS_LOST:
if (sdl.mouse.locked) {
#ifdef WIN32
if (sdl.desktop.fullscreen) {
VGA_KillDrawing();
GFX_ForceFullscreenExit();
}
#endif
GFX_CaptureMouse();
if (sdl.desktop.fullscreen) {
VGA_KillDrawing();
GFX_ForceFullscreenExit();
}
#endif
SetPriority(sdl.priority.nofocus);
GFX_LosingFocus();
CPU_Enable_SkipAutoAdjust();
@ -1697,7 +1744,8 @@ void GFX_Events() {
break;
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
HandleMouseButton(&event.button);
if (sdl.mouse.control_choice != NoMouse)
HandleMouseButton(&event.button);
break;
case SDL_QUIT:
throw(0);
@ -1789,6 +1837,8 @@ void Config_Add_SDL() {
Prop_string* Pstring;
Prop_int* Pint;
Prop_multival* Pmulti;
Section_prop* Psection;
const Property::Changeable::Value always = Property::Changeable::Always;
Pbool = sdl_sec->Add_bool("fullscreen",Property::Changeable::Always,false);
Pbool->Set_help("Start dosbox directly in fullscreen. (Press ALT-Enter to go back)");
@ -1829,8 +1879,46 @@ void Config_Add_SDL() {
"Use output=auto for an automatic choice.");
Pstring->Set_values(Get_SDL_TextureRenderers());
Pbool = sdl_sec->Add_bool("autolock",Property::Changeable::Always,true);
Pbool->Set_help("Mouse will automatically lock, if you click on the screen. (Press CTRL-F10 to unlock)");
// Define mouse control settings
Pmulti = sdl_sec->Add_multi("capture_mouse", always, " ");
const char *mouse_controls[] = {
"onclick", // default
"onstart",
"seamless",
"nomouse",
0
};
const char *middle_controls[] = {
"middlegame", // default
"middlerelease",
0
};
// Generate and set the mouse control defaults from above arrays
std::string mouse_control_defaults(mouse_controls[0]);
mouse_control_defaults += " ";
mouse_control_defaults += middle_controls[0];
Pmulti->SetValue(mouse_control_defaults.c_str());
// Add the mouse and middle control as sub-sections
Psection = Pmulti->GetSection();
Psection->Add_string("mouse_control", always, mouse_controls[0])->Set_values(mouse_controls);
Psection->Add_string("middle_control", always, middle_controls[0])->Set_values(middle_controls);
// Construct and set the help block using defaults set above
std::string mouse_control_help(
"Choose a mouse control method:\n"
" onclick: The mouse will be captured after the first click inside the window.\n"
" onstart: The mouse is captured immediately on start (similar to real DOS).\n"
" seamless: The mouse can move seamlessly in and out of DOSBox and cannot be captured.\n"
" nomouse: The mouse is disabled and hidden without any input sent to the game.\n"
"Choose how middle-clicks are handled (second parameter):\n"
" middlegame: Middle-clicks are sent to the game (Ctrl-F10 uncaptures the mouse).\n"
" middlerelease: Middle-clicks are used to uncapture the mouse (not sent to the game).\n"
" However, middle-clicks /will/ be sent to the game when when fullscreen\n"
" or when seamless control is set. Ctrl-F10 will also uncapture the mouse.\n"
"Defaults (if not present or incorrect): ");
mouse_control_help += mouse_control_defaults;
Pmulti->Set_help(mouse_control_help.c_str());
Pmulti = sdl_sec->Add_multi("sensitivity",Property::Changeable::Always, ",");
Pmulti->Set_help("Mouse sensitivity. The optional second parameter specifies vertical sensitivity (e.g. 100,-50).");
@ -2254,10 +2342,10 @@ int main(int argc, char* argv[]) {
#if defined (WIN32)
sticky_keys(true); //Might not be needed if the shutdown function switches to windowed mode, but it doesn't hurt
#endif
//Force visible mouse to end user. Somehow this sometimes doesn't happen
SDL_SetRelativeMouseMode(SDL_FALSE);
SDL_ShowCursor(SDL_ENABLE);
// Release the mouse if it's still captured
if (mouse_is_captured) {
GFX_ToggleMouseCapture();
}
SDL_Quit_Wrapper(); // Let's hope sdl will quit as well when it catches an exception
return 0;
}

View file

@ -135,7 +135,6 @@ bool Mouse_SetPS2State(bool use) {
return false;
}
useps2callback = use;
Mouse_AutoLock(useps2callback);
PIC_SetIRQMask(MOUSE_IRQ,!useps2callback);
return true;
}
@ -143,13 +142,11 @@ bool Mouse_SetPS2State(bool use) {
void Mouse_ChangePS2Callback(Bit16u pseg, Bit16u pofs) {
if ((pseg==0) && (pofs==0)) {
ps2callbackinit = false;
Mouse_AutoLock(false);
} else {
ps2callbackinit = true;
ps2cbseg = pseg;
ps2cbofs = pofs;
}
Mouse_AutoLock(ps2callbackinit);
}
void DoPS2Callback(Bit16u data, Bit16s mouseX, Bit16s mouseY) {
@ -731,12 +728,10 @@ static Bitu INT33_Handler(void) {
reg_ax=0xffff;
reg_bx=MOUSE_BUTTONS;
Mouse_Reset();
Mouse_AutoLock(true);
break;
case 0x01: /* Show Mouse */
if(mouse.hidden) mouse.hidden--;
mouse.updateRegion_y[1] = -1; //offscreen
Mouse_AutoLock(true);
DrawCursor();
break;
case 0x02: /* Hide Mouse */
@ -853,7 +848,6 @@ static Bitu INT33_Handler(void) {
mouse.sub_mask=reg_cx;
mouse.sub_seg=SegValue(es);
mouse.sub_ofs=reg_dx;
Mouse_AutoLock(true); //Some games don't seem to reset the mouse before using
break;
case 0x0f: /* Define mickey/pixel rate */
Mouse_SetMickeyPixelRate(reg_cx,reg_dx);