From d1d8f515df0ca0bfbc031cfdc2853bb2daf83aa9 Mon Sep 17 00:00:00 2001 From: FeralChild64 Date: Fri, 28 Oct 2022 20:21:12 +0200 Subject: Move toolkit-independent mouse code out of sdlmain.cpp --- include/mouse.h | 49 ++- include/sdlmain.h | 12 - include/video.h | 9 +- src/dos/program_mousectl.cpp | 3 - src/gui/sdl_mapper.cpp | 26 +- src/gui/sdlmain.cpp | 287 +++----------- src/hardware/mouse/mouse.cpp | 503 +++++++++++++++++++----- src/hardware/mouse/mouse_common.cpp | 3 +- src/hardware/mouse/mouse_common.h | 28 +- src/hardware/mouse/mouse_config.cpp | 154 +++++--- src/hardware/mouse/mouse_config.h | 18 +- src/hardware/mouse/mouse_interfaces.cpp | 47 ++- src/hardware/mouse/mouse_interfaces.h | 24 +- src/hardware/mouse/mouse_manymouse.cpp | 12 +- src/hardware/mouse/mouseif_dos_driver.cpp | 71 ++-- src/hardware/mouse/mouseif_ps2_bios.cpp | 4 +- src/hardware/mouse/mouseif_virtual_machines.cpp | 88 ++--- 17 files changed, 737 insertions(+), 601 deletions(-) diff --git a/include/mouse.h b/include/mouse.h index 98ef12871..11df6328c 100644 --- a/include/mouse.h +++ b/include/mouse.h @@ -50,8 +50,7 @@ enum class MouseInterfaceId : uint8_t { None = UINT8_MAX }; -constexpr uint8_t num_mouse_interfaces = static_cast(MouseInterfaceId::Last) + - 1; +constexpr uint8_t num_mouse_interfaces = static_cast(MouseInterfaceId::Last) + 1; enum class MouseMapStatus : uint8_t { HostPointer, @@ -65,7 +64,7 @@ enum class MouseMapStatus : uint8_t { // *************************************************************************** void MOUSE_EventMoved(const float x_rel, const float y_rel, - const uint16_t x_abs, const uint16_t y_abs); + const int32_t x_abs, const int32_t y_abs); void MOUSE_EventMoved(const float x_rel, const float y_rel, const MouseInterfaceId device_id); @@ -76,23 +75,37 @@ void MOUSE_EventButton(const uint8_t idx, const bool pressed, void MOUSE_EventWheel(const int16_t w_rel); void MOUSE_EventWheel(const int16_t w_rel, const MouseInterfaceId device_id); +// Notify that guest OS is being booted, so that certain +// parts of the emulation (like DOS driver) should be disabled void MOUSE_NotifyBooting(); -void MOUSE_SetConfigSeamless(const bool seamless); -void MOUSE_SetConfigNoMouse(); - -void MOUSE_NewScreenParams(const uint16_t clip_x, const uint16_t clip_y, - const uint16_t res_x, const uint16_t res_y, - const bool fullscreen, const uint16_t x_abs, - const uint16_t y_abs); - -// *************************************************************************** -// Information for the GFX subsystem -// *************************************************************************** - -bool MOUSE_IsUsingSeamlessDriver(); // if driver with seamless pointer support - // is running -bool MOUSE_IsUsingSeamlessSetting(); // if user selected seamless mode is in effect +// Notify that GFX subsystem (currently SDL) is started +// and can accept requests from mouse emulation module +void MOUSE_NotifyReadyGFX(); + +// Notify that window has lost or gained focus, this tells the mouse +// emulation code if it should process mouse events or ignore them +void MOUSE_NotifyHasFocus(const bool has_focus); + +// A GUI has to use this function to tell when it takes over or releases +// the mouse; this will change various settings like raw input (we don't +// want it for the GUI) or cursor visibility (we want the host cursor +// visible while a GUI is running) +void MOUSE_NotifyTakeOver(const bool gui_has_taken_over); + +// To be called when screen mode changes, emulator window gets resized, etc. +// clip_x / clip_y - size of the black bars around screen area +// res_x / res_y - size of drawing area (in hot OS pixels) +// x_abs / y_abs - new absolute mouse cursor position +// is_fullscreen - whether the new mode is fullscreen or windowed +void MOUSE_NewScreenParams(const uint32_t clip_x, const uint32_t clip_y, + const uint32_t res_x, const uint32_t res_y, + const int32_t x_abs, const int32_t y_abs, + const bool is_fullscreen); + +// Notification that user pressed/released the hotkey combination +// to capture/release the mouse +void MOUSE_ToggleUserCapture(const bool pressed); // *************************************************************************** // BIOS mouse interface for PS/2 mouse diff --git a/include/sdlmain.h b/include/sdlmain.h index cbf17c200..5dbc4e4be 100644 --- a/include/sdlmain.h +++ b/include/sdlmain.h @@ -48,13 +48,6 @@ static void update_frame_surface(const uint16_t *changedLines); constexpr void update_frame_noop([[maybe_unused]] const uint16_t *) { /* no-op */ } static inline bool present_frame_noop() { return true; } -enum MouseControlType { - CaptureOnClick = 1 << 0, - CaptureOnStart = 1 << 1, - Seamless = 1 << 2, - NoMouse = 1 << 3 -}; - enum SCREEN_TYPES { SCREEN_SURFACE, SCREEN_TEXTURE, @@ -232,11 +225,6 @@ struct SDL_Block { int period_us_early = 0; int period_us_late = 0; } frame = {}; - struct { - MouseControlType control_choice = Seamless; - bool middle_will_release = true; - bool has_focus = false; - } mouse = {}; PPScale pp_scale = {}; SDL_Rect updateRects[1024] = {}; bool use_exact_window_resolution = false; diff --git a/include/video.h b/include/video.h index 456574acd..1b3b741dd 100644 --- a/include/video.h +++ b/include/video.h @@ -1,4 +1,5 @@ /* + * Copyright (C) 2020-2022 The DOSBox Staging Team * Copyright (C) 2002-2021 The DOSBox Team * * This program is free software; you can redistribute it and/or modify @@ -78,12 +79,12 @@ void GFX_SwitchFullScreen(void); bool GFX_StartUpdate(uint8_t * &pixels, int &pitch); void GFX_EndUpdate( const uint16_t *changedLines ); void GFX_GetSize(int &width, int &height, bool &fullscreen); -void GFX_UpdateMouseState(); void GFX_LosingFocus(); void GFX_RegenerateWindow(Section *sec); -void GFX_SetMouseRawInput(const bool raw_input); -void GFX_MouseCaptureAfterMapping(); -bool GFX_MouseIsAvailable(); + +void GFX_SetMouseCapture(const bool requested_capture); +void GFX_SetMouseVisibility(const bool requested_visible); +void GFX_SetMouseRawInput(const bool requested_raw_input); #if defined (REDUCE_JOYSTICK_POLLING) void MAPPER_UpdateJoysticks(void); diff --git a/src/dos/program_mousectl.cpp b/src/dos/program_mousectl.cpp index 64709fe55..66f90611f 100644 --- a/src/dos/program_mousectl.cpp +++ b/src/dos/program_mousectl.cpp @@ -23,7 +23,6 @@ #include "ansi_code_markup.h" #include "checks.h" #include "string_utils.h" -#include "video.h" #include @@ -323,8 +322,6 @@ void MOUSECTL::FinalizeMapping() WriteOut("\n"); WriteOut(MSG_Get("SHELL_CMD_MOUSECTL_MAP_HINT")); WriteOut("\n\n"); - - GFX_MouseCaptureAfterMapping(); } bool MOUSECTL::CmdMap(const MouseInterfaceId interface_id, const std::string &pattern) diff --git a/src/gui/sdl_mapper.cpp b/src/gui/sdl_mapper.cpp index 284f9e8fc..188bd38ac 100644 --- a/src/gui/sdl_mapper.cpp +++ b/src/gui/sdl_mapper.cpp @@ -42,6 +42,7 @@ #include "keyboard.h" #include "mapper.h" #include "math_utils.h" +#include "mouse.h" #include "pic.h" #include "rgb24.h" #include "setup.h" @@ -59,10 +60,6 @@ constexpr rgb24 marginal_color(255, 103, 0); // Amber for marginal conditions constexpr rgb24 on_color(0, 1, 0); // Green for on/ready/in-use constexpr rgb24 off_color(0, 0, 0); // Black for off/stopped/not-in-use -/* Mouse related */ -void GFX_ToggleMouseCapture(); -extern bool mouse_is_captured; //true if mouse is confined to window - enum { CLR_BLACK=0, CLR_GREY=1, @@ -2921,11 +2918,6 @@ void MAPPER_LosingFocus() { void MAPPER_RunEvent(uint32_t /*val*/) { - if (!GFX_MouseIsAvailable()) { - LOG_ERR("MAPPER: The mapper requires a mouse, but no mouse is available"); - LOG_WARNING("MAPPER: Set your conf 'capture_mouse' setting to something other than 'nomouse'"); - return; - } KEYBOARD_ClrBuffer(); // Clear buffer GFX_LosingFocus(); //Release any keys pressed (buffer gets filled again). MAPPER_DisplayUI(); @@ -2940,20 +2932,14 @@ void MAPPER_Run(bool pressed) { SDL_Surface* SDL_SetVideoMode_Wrap(int width,int height,int bpp,uint32_t flags); void MAPPER_DisplayUI() { + MOUSE_NotifyTakeOver(true); + // The mapper is about to take-over SDL's surface and rendering // functions, so disengage the main ones. When the mapper closes, SDL // main will recreate its rendering pipeline. GFX_DisengageRendering(); - int cursor = SDL_ShowCursor(SDL_QUERY); - SDL_ShowCursor(SDL_ENABLE); - bool mousetoggle = false; - if (mouse_is_captured) { - mousetoggle = true; - GFX_ToggleMouseCapture(); - } - - /* Be sure that there is no update in progress */ + // Be sure that there is no update in progress GFX_EndUpdate( 0 ); mapper.window = GFX_SetSDLSurfaceWindow(640, 480); if (mapper.window == nullptr) @@ -3000,10 +2986,8 @@ void MAPPER_DisplayUI() { #if defined (REDUCE_JOYSTICK_POLLING) SDL_JoystickEventState(SDL_DISABLE); #endif - if (mousetoggle) - GFX_ToggleMouseCapture(); - SDL_ShowCursor(cursor); GFX_ResetScreen(); + MOUSE_NotifyTakeOver(false); } static void MAPPER_Destroy(Section *sec) { diff --git a/src/gui/sdlmain.cpp b/src/gui/sdlmain.cpp index b85932fd0..4eb0d7e94 100644 --- a/src/gui/sdlmain.cpp +++ b/src/gui/sdlmain.cpp @@ -198,9 +198,6 @@ PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer = NULL; SDL_Block sdl; -bool mouse_is_captured = false; // the actual state of the mouse -bool mouse_capture_requested = false; // if the user manually requested the capture - // Masks to be passed when creating SDL_Surface. // Remove ifndef if they'll be needed for MacOS X builds. #ifndef MACOSX @@ -1131,10 +1128,9 @@ static void setup_presentation_mode(FRAME_MODE &previous_mode) static void NewMouseScreenParams() { - int abs_x, abs_y; + int abs_x = 0; + int abs_y = 0; SDL_GetMouseState(&abs_x, &abs_y); - abs_x = std::clamp(abs_x, 0, static_cast(UINT16_MAX)); - abs_y = std::clamp(abs_y, 0, static_cast(UINT16_MAX)); #ifdef __APPLE__ // macOS moves mouse cursor on "client points" grid, not physical pixels; @@ -1143,15 +1139,17 @@ static void NewMouseScreenParams() sdl.clip.y / sdl.desktop.dpi_scale, sdl.clip.w / sdl.desktop.dpi_scale, sdl.clip.h / sdl.desktop.dpi_scale, - sdl.desktop.fullscreen, - check_cast(abs_x), - check_cast(abs_y)); + check_cast(abs_x), + check_cast(abs_y), + sdl.desktop.fullscreen); #else - MOUSE_NewScreenParams(sdl.clip.x, sdl.clip.y, - sdl.clip.w, sdl.clip.h, - sdl.desktop.fullscreen, - check_cast(abs_x), - check_cast(abs_y)); + MOUSE_NewScreenParams(check_cast(sdl.clip.x), + check_cast(sdl.clip.y), + check_cast(sdl.clip.w), + check_cast(sdl.clip.h), + check_cast(abs_x), + check_cast(abs_y), + sdl.desktop.fullscreen); #endif } @@ -2169,61 +2167,34 @@ void GFX_SetShader([[maybe_unused]] const std::string &source) #endif } -bool GFX_MouseIsAvailable() { - return sdl.mouse.control_choice != NoMouse; -} - -void GFX_SetMouseRawInput(const bool raw_input) +void GFX_SetMouseRawInput(const bool requested_raw_input) { - if (!SDL_SetHintWithPriority(SDL_HINT_MOUSE_RELATIVE_MODE_WARP, - raw_input ? "0" : "1", - SDL_HINT_OVERRIDE)) - LOG_WARNING("DISPLAY: Mouse raw input set failed"); + if (SDL_SetHintWithPriority(SDL_HINT_MOUSE_RELATIVE_MODE_WARP, + requested_raw_input ? "0" : "1", + SDL_HINT_OVERRIDE) != SDL_TRUE) + LOG_WARNING("SDL: Mouse raw input %s failed", + requested_raw_input ? "enable" : "disable"); } -void GFX_ToggleMouseCapture() +void GFX_SetMouseCapture(const bool requested_capture) { - /* - * Only process mouse events when we have focus. - * This protects against out-of-order event issues such - * as acting on clicks before the window is drawn. - */ - if (!sdl.mouse.has_focus) - return; - - assertm(sdl.mouse.control_choice != NoMouse, - "SDL: Mouse capture is invalid when NoMouse is configured [Logic Bug]"); - - mouse_is_captured = !mouse_is_captured; // flip state - if (SDL_SetRelativeMouseMode(mouse_is_captured ? SDL_TRUE : SDL_FALSE) != 0) { + const auto param = requested_capture ? SDL_TRUE : SDL_FALSE; + if (SDL_SetRelativeMouseMode(param) != 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"); + E_Exit("SDL: Failed to %s relative-mode [SDL Bug]", + requested_capture ? "put the mouse in" : + "take the mouse out of"); } - LOG_MSG("SDL: %s the mouse", mouse_is_captured ? "captured" : "released"); } -static void toggle_mouse_capture_from_user(bool pressed) +void GFX_SetMouseVisibility(const bool requested_visible) { - if (!pressed || sdl.desktop.fullscreen) - return; - mouse_capture_requested = !mouse_capture_requested; - GFX_ToggleMouseCapture(); -} - -void GFX_MouseCaptureAfterMapping() -{ - if (sdl.desktop.fullscreen || - !sdl.mouse.has_focus || - mouse_is_captured) - return; - - mouse_capture_requested = true; - GFX_ToggleMouseCapture(); + const auto param = requested_visible ? SDL_ENABLE : SDL_DISABLE; + if (SDL_ShowCursor(param) < 0) + E_Exit("SDL: Failed to make mouse cursor %s [SDL Bug]", + requested_visible ? "visible" : "invisible"); } - static void FocusInput() { // Ensure auto-cycles are enabled @@ -2246,60 +2217,6 @@ static void FocusInput() SDL_SetWindowInputFocus(sdl.window); } -/* - * Assesses the following: - * - current window size (full or not), - * - mouse capture state (yes or no), - * - whether VMware type mouse driver is running, - * - desired capture type (start, click, seamless), and - * - if we're starting up for the first time, - * to determine if the mouse-capture state should be toggled. - * Note that this also acts a filter: we don't want to repeatedly - * re-apply the same mouse capture state over and over again, so most - * of the time this function will (or should) decide to do nothing. - */ -void GFX_UpdateMouseState() -{ - // Don't change anything if we do not have focus - if (!sdl.mouse.has_focus) - return; - - // 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 release the - // mouse if it is controlled by a VMware type driver or - // it's auto-captured (but not manually requested) and - // in seamless-mode - } else if (!sdl.desktop.fullscreen && mouse_is_captured && - (MOUSE_IsUsingSeamlessDriver() || - (!mouse_capture_requested && MOUSE_IsUsingSeamlessSetting()))) { - GFX_ToggleMouseCapture(); - SDL_ShowCursor(SDL_DISABLE); - - // If none of the above are true /and/ we're starting - // up the first time, then: - // - Capture the mouse if configured onstart is set - // - Hide the mouse if seamless or nomouse are set - // - Also hide if it is handled by a VMware type driver - } else if (!has_run_once) { - if (sdl.mouse.control_choice == CaptureOnStart) { - SDL_RaiseWindow(sdl.window); - toggle_mouse_capture_from_user(true); - } else if (MOUSE_IsUsingSeamlessDriver() || - (sdl.mouse.control_choice & (Seamless | NoMouse))) { - SDL_ShowCursor(SDL_DISABLE); - } - } - has_run_once = true; -} - #if defined (WIN32) STICKYKEYS stick_keys = {sizeof(STICKYKEYS), 0}; void sticky_keys(bool restore){ @@ -2637,8 +2554,9 @@ static void GUI_ShutDown(Section *) (sdl.draw.callback)( GFX_CallBackStop ); if (sdl.desktop.fullscreen) GFX_SwitchFullScreen(); - if (mouse_is_captured) - GFX_ToggleMouseCapture(); + + GFX_SetMouseCapture(false); + GFX_SetMouseVisibility(true); CleanupSDLResources(); if (sdl.renderer) { @@ -3557,56 +3475,16 @@ static void GUI_StartUp(Section *sec) SDL_SetWindowTitle(sdl.window, "DOSBox Staging"); SetIcon(); - // Apply the user's mouse settings - Section_prop* s = section->GetMultiVal("capture_mouse")->GetSection(); - const std::string control_choice = s->Get_string("capture_mouse_first_value"); - std::string mouse_control_msg; - if (control_choice == "onclick") { - sdl.mouse.control_choice = CaptureOnClick; - mouse_control_msg = "will be captured after the first left or right button click"; - } 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: left and right button clicks won't capture the mouse"; - } else if (control_choice == "nomouse") { - sdl.mouse.control_choice = NoMouse; - mouse_control_msg = "is disabled"; - MOUSE_SetConfigNoMouse(); - } else { - assert(sdl.mouse.control_choice == CaptureOnClick); - } - - LOG_MSG("SDL: Mouse %s", mouse_control_msg.c_str()); - - if (sdl.mouse.control_choice != NoMouse) { - const std::string mclick_choice = s->Get_string("capture_mouse_second_value"); - - // release the mouse is the default; this logic handles an empty 2nd value - sdl.mouse.middle_will_release = (mclick_choice != "middlegame"); - - - const auto middle_control_msg = sdl.mouse.middle_will_release - ? "will capture/release the mouse (clicks not sent to the game/program)" - : "will be sent to the game/program (clicks not used to capture/release)"; - LOG_MSG("SDL: Middle mouse button %s", middle_control_msg); - - // Only setup the Ctrl/Cmd+F10 handler if the mouse is capturable - MAPPER_AddHandler(toggle_mouse_capture_from_user, SDL_SCANCODE_F10, - PRIMARY_MOD, "capmouse", "Cap Mouse"); - - // Notify mouse emulation routines about the configuration - MOUSE_SetConfigSeamless(sdl.mouse.control_choice == Seamless); - } - /* Get some Event handlers */ MAPPER_AddHandler(GFX_RequestExit, SDL_SCANCODE_F9, PRIMARY_MOD, "shutdown", "Shutdown"); MAPPER_AddHandler(SwitchFullScreen, SDL_SCANCODE_RETURN, MMOD2, "fullscr", "Fullscreen"); - MAPPER_AddHandler(Restart, SDL_SCANCODE_HOME, MMOD1 | MMOD2, "restart", - "Restart"); + MAPPER_AddHandler(Restart, SDL_SCANCODE_HOME, MMOD1 | MMOD2, + "restart", "Restart"); + MAPPER_AddHandler(MOUSE_ToggleUserCapture, SDL_SCANCODE_F10, PRIMARY_MOD, + "capmouse", "Cap Mouse"); + #if C_DEBUG /* Pause binds with activate-debugger */ #else @@ -3621,15 +3499,17 @@ static void GUI_StartUp(Section *sec) // be toggled by the user /after/ starting DOSBox. startup_state_numlock = keystate & KMOD_NUM; startup_state_capslock = keystate & KMOD_CAPS; -} + // Notify MOUSE subsystem that it can start now + MOUSE_NotifyReadyGFX(); +} static void HandleMouseMotion(SDL_MouseMotionEvent *motion) { MOUSE_EventMoved(static_cast(motion->xrel), static_cast(motion->yrel), - std::clamp(motion->x, 0, static_cast(UINT16_MAX)), - std::clamp(motion->y, 0, static_cast(UINT16_MAX))); + check_cast(motion->x), + check_cast(motion->y)); } static void HandleMouseWheel(SDL_MouseWheelEvent *wheel) @@ -3640,10 +3520,7 @@ static void HandleMouseWheel(SDL_MouseWheelEvent *wheel) static void HandleMouseButton(SDL_MouseButtonEvent * button) { - constexpr auto state_released = false; - constexpr auto state_pressed = true; - - auto notify_button = [](const uint8_t button, const bool pressed) { + auto notify_button = [](const uint8_t button, const bool pressed) { switch (button) { case SDL_BUTTON_LEFT: MOUSE_EventButton(0, pressed); break; case SDL_BUTTON_RIGHT: MOUSE_EventButton(1, pressed); break; @@ -3653,29 +3530,7 @@ static void HandleMouseButton(SDL_MouseButtonEvent * button) } }; - if (button->state == SDL_RELEASED) { - notify_button(button->button, state_released); - return; - } - - assert(button->state == SDL_PRESSED); - - if (sdl.desktop.fullscreen || MOUSE_IsUsingSeamlessDriver()) { - notify_button(button->button, state_pressed); - return; - } - - if (!mouse_is_captured && (sdl.mouse.control_choice & (CaptureOnStart | CaptureOnClick))) { - toggle_mouse_capture_from_user(true); - return; // Don't pass click to mouse handler - } - - if (button->button == SDL_BUTTON_MIDDLE && sdl.mouse.control_choice != NoMouse) { - toggle_mouse_capture_from_user(true); - return; // Don't pass click to mouse handler - } - - notify_button(button->button, true); + notify_button(button->button, button->state == SDL_PRESSED); } void GFX_LosingFocus() @@ -3872,9 +3727,7 @@ bool GFX_Events() // keyboard focus"); if (sdl.draw.callback) sdl.draw.callback(GFX_CallBackRedraw); - sdl.mouse.has_focus = true; - GFX_UpdateMouseState(); - + MOUSE_NotifyHasFocus(true); ApplyActiveSettings(); FocusInput(); continue; @@ -3890,7 +3743,7 @@ bool GFX_Events() ApplyInactiveSettings(); GFX_LosingFocus(); CPU_Enable_SkipAutoAdjust(); - sdl.mouse.has_focus = false; + MOUSE_NotifyHasFocus(false); break; case SDL_WINDOWEVENT_ENTER: @@ -4063,10 +3916,7 @@ bool GFX_Events() case SDL_MOUSEMOTION: HandleMouseMotion(&event.motion); break; case SDL_MOUSEWHEEL: HandleMouseWheel(&event.wheel); break; case SDL_MOUSEBUTTONDOWN: - case SDL_MOUSEBUTTONUP: - if (sdl.mouse.control_choice != NoMouse) - HandleMouseButton(&event.button); - break; + case SDL_MOUSEBUTTONUP: HandleMouseButton(&event.button); break; case SDL_QUIT: GFX_RequestExit(true); break; #ifdef WIN32 @@ -4273,47 +4123,8 @@ void Config_Add_SDL() { "Use texture_renderer=auto for an automatic choice."); pstring->Set_values(Get_SDL_TextureRenderers()); - // Define mouse control settings - Pmulti = sdl_sec->AddMultiVal("capture_mouse", always, " "); - const char *mouse_controls[] = { - "seamless", // default - "onclick", "onstart", "nomouse", 0, - }; - const char *middle_controls[] = { - "middlerelease", // default - "middlegame", - "", // allow empty second value for 'nomouse' - 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); - - // Add the mouse and middle control as sub-sections - psection = Pmulti->GetSection(); - psection->Add_string("capture_mouse_first_value", always, mouse_controls[0]) - ->Set_values(mouse_controls); - psection->Add_string("capture_mouse_second_value", 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: Capture the mouse when clicking any button in the window.\n" - " onstart: Capture the mouse immediately on start.\n" - " seamless: Let the mouse move seamlessly; captures only with\n" - " middle-click or hotkey.\n" - " nomouse: Hide the mouse and don't send input to the game.\n" - "Choose how middle-clicks are handled (second parameter):\n" - " middlegame: Middle-clicks are sent to the game.\n" - " middlerelease: Middle-click will release the captured mouse, and also\n" - " capture when seamless.\n" - "Defaults (if not present or incorrect): "); - mouse_control_help += mouse_control_defaults; - Pmulti->Set_help(mouse_control_help); - + Pmulti = sdl_sec->AddMultiVal("capture_mouse", deprecated, ","); + Pmulti->Set_help("Moved to [mouse] section."); Pmulti = sdl_sec->AddMultiVal("sensitivity", deprecated, ","); Pmulti->Set_help("Moved to [mouse] section."); pbool = sdl_sec->Add_bool("raw_mouse_input", deprecated, false); diff --git a/src/hardware/mouse/mouse.cpp b/src/hardware/mouse/mouse.cpp index 6f99a6e1e..891c7a194 100644 --- a/src/hardware/mouse/mouse.cpp +++ b/src/hardware/mouse/mouse.cpp @@ -37,14 +37,262 @@ CHECK_NARROWING(); -bool seamless_driver = false; -bool seamless_setting = false; - static Bitu int74_ret_callback = 0; static MouseQueue &mouse_queue = MouseQueue::GetInstance(); static ManyMouseGlue &manymouse = ManyMouseGlue::GetInstance(); +// *************************************************************************** +// GFX-related decision making +// *************************************************************************** + +static struct { + bool is_fullscreen = false; // if full screen mode is active + uint32_t clip_x = 0; // clipping = size of black border (one side) + uint32_t clip_y = 0; + + uint32_t cursor_x_abs = 0; // absolute position from start of drawing area + uint32_t cursor_y_abs = 0; + bool cursor_is_outside = true; // if mouse cursor is outside of drawing area + + bool has_focus = false; // if our window has focus + bool gui_has_taken_over = false; // if a GUI requested to take over the mouse + bool capture_was_requested = false; // if user requested mouse to be captured + + bool is_captured = false; // if GFX was requested to capture mouse + bool is_visible = false; // if GFX was requested to make cursor visible + bool is_input_raw = false; // if GFX was requested to provide raw movements + bool is_seamless = false; // if seamless mouse integration is in effect + + bool should_drop_events = true; // if we should drop mouse events + bool should_capture_on_click = false; // if any button click should capture the mouse + bool should_capture_on_middle = false; // if middle button press should capture the mouse + bool should_release_on_middle = false; // if middle button press should release the mouse + bool should_toggle_on_hotkey = false; // if hotkey should toggle mouse capture + +} state; + +static void update_cursor_absolute_position(const int32_t x_abs, const int32_t y_abs) +{ + state.cursor_is_outside = false; + + auto calculate = [&](const int32_t absolute, + const uint32_t clipping, + const uint32_t resolution) -> uint32_t { + assert(resolution > 1u); + assert(clipping * 2 < resolution); + + if (absolute < 0 || static_cast(absolute) < clipping) { + // cursor is over the top or left black bar + state.cursor_is_outside = true; + return 0; + } else if (static_cast(absolute) >= resolution + clipping) { + // cursor is over the bottom or right black bar + state.cursor_is_outside = true; + return check_cast(resolution - 1); + } + + const auto result = static_cast(absolute) - clipping; + return static_cast(result); + }; + + auto &x = state.cursor_x_abs; + auto &y = state.cursor_y_abs; + + x = calculate(x_abs, state.clip_x, mouse_shared.resolution_x); + y = calculate(y_abs, state.clip_y, mouse_shared.resolution_y); +} + +static void update_cursor_visibility() +{ + // If mouse subsystem not started yet, do nothing + if (!mouse_shared.started) + return; + + static bool first_time = true; + + // Store internally old settings, to avoid unnecessary GFX calls + const auto old_is_visible = state.is_visible; + + if (!state.has_focus) { + + // No change to cursor visibility + + } else if (state.gui_has_taken_over) { + + state.is_visible = true; + + } else { // Window has focus, no GUI running + + // Host cursor should be hidden if any of: + // - mouse cursor is captured, for any reason + // - seamless integration is in effect + // But show it nevertheless if: + // - seamless integration is in effect and + // - cursor is outside of drawing area + state.is_visible = !(state.is_captured || state.is_seamless) || + (state.is_seamless && state.cursor_is_outside); + } + + // Apply calculated settings if changed or if this is the first run + if (first_time || old_is_visible != state.is_visible) + GFX_SetMouseVisibility(state.is_visible); + + // And take a note that this is no longer the first run + first_time = false; +} + +static void update_state() // updates whole 'state' structure, except cursor visibility +{ + // If mouse subsystem not started yet, do nothing + if (!mouse_shared.started) + return; + + const bool is_config_on_start = (mouse_config.capture == MouseCapture::OnStart); + const bool is_config_on_click = (mouse_config.capture == MouseCapture::OnClick); + const bool is_config_no_mouse = (mouse_config.capture == MouseCapture::NoMouse); + + // If running for the first time, capture the mouse if this was configured + static bool first_time = true; + if (first_time && is_config_on_start) + state.capture_was_requested = true; + + // We are running in seamless mode: + // - we are not in windowed mode, and + // - NoMouse is not configured, and + // - seamless driver is running or Seamless capture is configured + const bool is_seamless_config = (mouse_config.capture == MouseCapture::Seamless); + const bool is_seamless_driver = mouse_shared.active_vmm; + state.is_seamless = !state.is_fullscreen && + !is_config_no_mouse && + (is_seamless_driver || is_seamless_config); + + // Due to ManyMouse API limitation, we are unable to support seamless + // integration if mapping is in effect + const bool is_mapping = manymouse.IsMappingInEffect(); + if (state.is_seamless && is_mapping) { + state.is_seamless = false; + static bool already_warned = false; + if (!already_warned) { + LOG_WARNING("MOUSE: Mapping disables seamless pointer integration"); + already_warned = true; + } + } + + // Store internally old settings, to avoid unnecessary GFX calls + const auto old_is_captured = state.is_captured; + const auto old_is_input_raw = state.is_input_raw; + + // Raw input depends on the user configuration + state.is_input_raw = mouse_config.raw_input; + + if (!state.has_focus) { + + state.should_drop_events = true; + + // No change to: + // - state.is_captured + + } else if (state.gui_has_taken_over) { + + state.is_captured = false; + state.should_drop_events = true; + + // Override user configuration, for the GUI we want + // host OS mouse acceleration applied + state.is_input_raw = false; + + } else { // Window has focus, no GUI running + + // Capture mouse cursor if any of: + // - we are in fullscreen mode + // - user asked to capture the mouse + state.is_captured = state.is_fullscreen || + state.capture_was_requested; + + // Drop mouse events if NoMouse is configured + state.should_drop_events = is_config_no_mouse; + // Also drop events if: + // - mouse not captured, and + // - mouse not in seamless mode (due to user setting or seamless driver) + if (!state.is_captured && !state.is_seamless) + state.should_drop_events = true; + } + + // Use a hotkey to toggle mouse capture if: + // - windowed mode, and + // - capture type is different than NoMouse + state.should_toggle_on_hotkey = !state.is_fullscreen && + !is_config_no_mouse; + + // Use any mouse click to capture the mouse if: + // - windowed mode, and + // - mouse is not captured, and + // - we are not in seamless mode, and + // - no GUI has taken over the mouse, and + // - no NoMouse mode is in effect, and + // - capture on start/click was configured or mapping is in effect + state.should_capture_on_click = !state.is_fullscreen && + !state.is_captured && + !state.is_seamless && + !state.gui_has_taken_over && + !is_config_no_mouse && + (is_config_on_start || is_config_on_click || is_mapping); + + // Use a middle click to capture the mouse if: + // - windowed mode, and + // - mouse is not captured, and + // - no GUI has taken over the mouse, and + // - no NoMouse mode is in effect, and + // - seamless mode is in effect, and + // - middle release was configured + state.should_capture_on_middle = !state.is_fullscreen && + !state.is_captured && + !state.gui_has_taken_over && + !is_config_no_mouse && + state.is_seamless && + mouse_config.middle_release; + + // Use a middle click to release the mouse if: + // - windowed mode, and + // - mouse is captured, and + // - release by middle button was configured + state.should_release_on_middle = !state.is_fullscreen && + state.is_captured && + mouse_config.middle_release; + + // Apply calculated settings if changed or if this is the first run + if (first_time || old_is_captured != state.is_captured) + GFX_SetMouseCapture(state.is_captured); + if (first_time || old_is_input_raw != state.is_input_raw) + GFX_SetMouseRawInput(state.is_input_raw); + + for (auto &interface : mouse_interfaces) + interface->UpdateInputType(); + + // And take a note that this is no longer the first run + first_time = false; +} + +static bool should_drop_event() +{ + // Decide whether to drop mouse events, depending on both + // mouse cursor position and general event dropping policy + return (state.is_seamless && state.cursor_is_outside) || + state.should_drop_events; +} + +void MOUSE_UpdateGFX() +{ + update_state(); + update_cursor_visibility(); +} + +bool MOUSE_IsCaptured() +{ + return state.is_captured; +} + // *************************************************************************** // Interrupt 74 implementation // *************************************************************************** @@ -117,72 +365,66 @@ Bitu int74_ret_handler() return CBRET_NONE; } -// *************************************************************************** -// Information for the GFX subsystem -// *************************************************************************** - -bool MOUSE_IsUsingSeamlessDriver() -{ - return seamless_driver; -} - -bool MOUSE_IsUsingSeamlessSetting() -{ - return seamless_setting; -} - // *************************************************************************** // External notifications // *************************************************************************** -void MOUSE_NewScreenParams(const uint16_t clip_x, const uint16_t clip_y, - const uint16_t res_x, const uint16_t res_y, - const bool fullscreen, const uint16_t x_abs, - const uint16_t y_abs) +void MOUSE_NewScreenParams(const uint32_t clip_x, const uint32_t clip_y, + const uint32_t res_x, const uint32_t res_y, + const int32_t x_abs, const int32_t y_abs, + const bool is_fullscreen) { - mouse_video.clip_x = clip_x; - mouse_video.clip_y = clip_y; + assert(clip_x <= INT32_MAX); + assert(clip_y <= INT32_MAX); + assert(res_x <= INT32_MAX); + assert(res_y <= INT32_MAX); + + state.clip_x = clip_x; + state.clip_y = clip_y; // Protection against strange window sizes, // to prevent division by 0 in some places - mouse_video.res_x = std::max(res_x, static_cast(2)); - mouse_video.res_y = std::max(res_y, static_cast(2)); + constexpr uint32_t min = 2; + mouse_shared.resolution_x = std::max(res_x, min); + mouse_shared.resolution_y = std::max(res_y, min); - mouse_video.fullscreen = fullscreen; + // If we are switching back from fullscreen, + // clear the user capture request + if (state.is_fullscreen && !is_fullscreen) + state.capture_was_requested = false; - MOUSEVMM_NewScreenParams(x_abs, y_abs); - MOUSE_NotifyStateChanged(); -} + state.is_fullscreen = is_fullscreen; -void MOUSE_NotifyResetDOS() -{ - mouse_queue.ClearEventsDOS(); + update_cursor_absolute_position(x_abs, y_abs); + + MOUSE_UpdateGFX(); + MOUSEVMM_NewScreenParams(state.cursor_x_abs, state.cursor_y_abs); } -void MOUSE_NotifyStateChanged() +void MOUSE_ToggleUserCapture(const bool pressed) { - const auto old_seamless_driver = seamless_driver; - const auto old_seamless_setting = seamless_setting; + if (!pressed || !state.should_toggle_on_hotkey) + return; - const auto is_mapping_in_effect = manymouse.IsMappingInEffect(); + state.capture_was_requested = !state.capture_was_requested; + MOUSE_UpdateGFX(); +} - static bool already_warned = false; - if (!already_warned && is_mapping_in_effect && - (mouse_shared.active_vmm || mouse_config.seamless)) { - LOG_WARNING("MOUSE: Mapping disables seamless pointer integration"); - already_warned = true; - } +void MOUSE_NotifyTakeOver(const bool gui_has_taken_over) +{ + state.gui_has_taken_over = gui_has_taken_over; + MOUSE_UpdateGFX(); +} - // Prepare suggestions to the GFX subsystem - seamless_driver = mouse_shared.active_vmm && !mouse_video.fullscreen && - !is_mapping_in_effect; - seamless_setting = mouse_config.seamless && !mouse_video.fullscreen && - !is_mapping_in_effect; +void MOUSE_NotifyHasFocus(const bool has_focus) +{ + state.has_focus = has_focus; + MOUSE_UpdateGFX(); +} - // If state has really changed, update GFX subsystem - if (seamless_driver != old_seamless_driver || - seamless_setting != old_seamless_setting) - GFX_UpdateMouseState(); +void MOUSE_NotifyResetDOS() +{ + mouse_queue.ClearEventsDOS(); } void MOUSE_NotifyDisconnect(const MouseInterfaceId interface_id) @@ -210,10 +452,16 @@ void MOUSE_NotifyBooting() } void MOUSE_EventMoved(const float x_rel, const float y_rel, - const uint16_t x_abs, const uint16_t y_abs) + const int32_t x_abs, const int32_t y_abs) { + // Event from GFX + + // Update cursor position and visibility + update_cursor_absolute_position(x_abs, y_abs); + update_cursor_visibility(); + // Drop unneeded events - if (!mouse_is_captured && !seamless_driver && !seamless_setting) + if (should_drop_event()) return; // From the GUI we are getting mouse movement data in two @@ -237,13 +485,21 @@ void MOUSE_EventMoved(const float x_rel, const float y_rel, MouseEvent ev; for (auto &interface : mouse_interfaces) if (interface->IsUsingHostPointer()) - interface->NotifyMoved(ev, x_rel, y_rel, x_abs, y_abs); + interface->NotifyMoved(ev, x_rel, y_rel, + state.cursor_x_abs, + state.cursor_y_abs); mouse_queue.AddEvent(ev); } void MOUSE_EventMoved(const float x_rel, const float y_rel, const MouseInterfaceId interface_id) { + // Event from ManyMouse + + // Drop unneeded events + if (should_drop_event()) + return; + auto interface = MouseInterface::Get(interface_id); if (interface && interface->IsUsingEvents()) { MouseEvent ev; @@ -254,6 +510,37 @@ void MOUSE_EventMoved(const float x_rel, const float y_rel, void MOUSE_EventButton(const uint8_t idx, const bool pressed) { + // Event from GFX + + // Never ignore any button releases - always pass them + // to concrete interfaces, they will decide whether to + // ignore them or not. + if (pressed) { + // Handle mouse capture by button click + if (state.should_capture_on_click) { + state.capture_was_requested = true; + MOUSE_UpdateGFX(); + return; + } + + // Handle mouse capture toggle by middle click + constexpr uint8_t idx_middle = 2; + if (idx == idx_middle && state.should_capture_on_middle) { + state.capture_was_requested = true; + MOUSE_UpdateGFX(); + return; + } + if (idx == idx_middle && state.should_release_on_middle) { + state.capture_was_requested = false; + MOUSE_UpdateGFX(); + return; + } + + /// Drop unneeded events + if (should_drop_event()) + return; + } + MouseEvent ev; for (auto &interface : mouse_interfaces) if (interface->IsUsingHostPointer()) @@ -264,6 +551,14 @@ void MOUSE_EventButton(const uint8_t idx, const bool pressed) void MOUSE_EventButton(const uint8_t idx, const bool pressed, const MouseInterfaceId interface_id) { + // Event from ManyMouse + + // Drop unneeded events - but never drop any button + // releases events; pass them to concrete interfaces, + // they will decide whether to ignore them or not. + if (pressed && should_drop_event()) + return; + auto interface = MouseInterface::Get(interface_id); if (interface && interface->IsUsingEvents()) { MouseEvent ev; @@ -274,6 +569,12 @@ void MOUSE_EventButton(const uint8_t idx, const bool pressed, void MOUSE_EventWheel(const int16_t w_rel) { + // Event from GFX + + // Drop unneeded events + if (should_drop_event()) + return; + MouseEvent ev; for (auto &interface : mouse_interfaces) if (interface->IsUsingHostPointer()) @@ -283,6 +584,12 @@ void MOUSE_EventWheel(const int16_t w_rel) void MOUSE_EventWheel(const int16_t w_rel, const MouseInterfaceId interface_id) { + // Event from ManyMouse + + // Drop unneeded events + if (state.should_drop_events) + return; + auto interface = MouseInterface::Get(interface_id); if (interface && interface->IsUsingEvents()) { MouseEvent ev; @@ -328,12 +635,12 @@ MouseControlAPI::MouseControlAPI() MouseControlAPI::~MouseControlAPI() { manymouse.StopConfigAPI(); - MOUSE_NotifyStateChanged(); + MOUSE_UpdateGFX(); } bool MouseControlAPI::IsNoMouseMode() { - return mouse_config.no_mouse; + return mouse_config.capture == MouseCapture::NoMouse; } const std::vector &MouseControlAPI::GetInfoInterfaces() const @@ -387,7 +694,7 @@ bool MouseControlAPI::PatternToRegex(const std::string &pattern, std::regex ® bool MouseControlAPI::ProbeForMapping(uint8_t &device_id) { - if (mouse_config.no_mouse) + if (IsNoMouseMode()) return false; manymouse.RescanIfSafe(); @@ -396,7 +703,7 @@ bool MouseControlAPI::ProbeForMapping(uint8_t &device_id) bool MouseControlAPI::Map(const MouseInterfaceId interface_id, const uint8_t device_idx) { - if (mouse_config.no_mouse) + if (IsNoMouseMode()) return false; auto mouse_interface = MouseInterface::Get(interface_id); @@ -408,15 +715,17 @@ bool MouseControlAPI::Map(const MouseInterfaceId interface_id, const uint8_t dev bool MouseControlAPI::Map(const MouseInterfaceId interface_id, const std::regex ®ex) { - if (mouse_config.no_mouse) + if (IsNoMouseMode()) return false; manymouse.RescanIfSafe(); const auto idx = manymouse.GetIdx(regex); if (idx >= mouse_info.physical.size()) return false; + const auto result = Map(interface_id, idx); - return Map(interface_id, idx); + MOUSE_UpdateGFX(); + return result; } bool MouseControlAPI::UnMap(const MouseControlAPI::ListIDs &list_ids) @@ -425,6 +734,7 @@ bool MouseControlAPI::UnMap(const MouseControlAPI::ListIDs &list_ids) for (auto &interface : list) interface->ConfigUnMap(); + MOUSE_UpdateGFX(); return !list.empty(); } @@ -443,6 +753,7 @@ bool MouseControlAPI::Reset(const MouseControlAPI::ListIDs &list_ids) for (auto &interface : list) interface->ConfigReset(); + MOUSE_UpdateGFX(); return !list.empty(); } @@ -546,8 +857,8 @@ const std::string &MouseControlAPI::GetValidMinRateStr() std::string MouseControlAPI::GetInterfaceNameStr(const MouseInterfaceId interface_id) { switch (interface_id) { - case MouseInterfaceId::DOS: return "DOS"; - case MouseInterfaceId::PS2: return "PS/2"; + case MouseInterfaceId::DOS: return "DOS"; + case MouseInterfaceId::PS2: return "PS/2"; case MouseInterfaceId::COM1: return "COM1"; case MouseInterfaceId::COM2: return "COM2"; case MouseInterfaceId::COM3: return "COM3"; @@ -586,38 +897,36 @@ bool MouseControlAPI::ResetMinRate(const MouseControlAPI::ListIDs &list_ids) // Initialization // *************************************************************************** -void MOUSE_SetConfigSeamless(const bool seamless) +void MOUSE_StartupIfReady() { - // Called during SDL initialization - mouse_config.seamless = seamless; - MOUSE_NotifyStateChanged(); - - // Just in case it is also called later - for (auto &interface : mouse_interfaces) - interface->UpdateConfig(); - - // Start mouse emulation if ready - mouse_shared.ready_config_sdl = true; - MOUSE_Startup(); -} - -void MOUSE_SetConfigNoMouse() -{ - // NOTE: if it is decided to not allow enabling/disabling - // this during runtime, add button click releases for all - // the mouse buttons - mouse_config.no_mouse = true; + if (mouse_shared.started || + !mouse_shared.ready_init || + !mouse_shared.ready_config || + !mouse_shared.ready_gfx) + return; - // Start mouse emulation if ready - mouse_shared.ready_config_sdl = true; - MOUSE_Startup(); -} + switch (mouse_config.capture) { + case MouseCapture::Seamless: + LOG_MSG("MOUSE: Will move seamlessly: left and right button clicks won't capture the mouse"); + break; + case MouseCapture::OnClick: + LOG_MSG("MOUSE: Will be captured after the first left or right button click"); + break; + case MouseCapture::OnStart: + LOG_MSG("MOUSE: Will be captured immediately on start"); + break; + case MouseCapture::NoMouse: + LOG_MSG("MOUSE: Control is disabled"); + break; + default: assert(false); break; + } -void MOUSE_Startup() -{ - if (mouse_shared.started || !mouse_shared.ready_startup_sequence || - !mouse_shared.ready_config_mouse || !mouse_shared.ready_config_sdl) - return; + if (mouse_config.capture != MouseCapture::NoMouse) { + LOG_MSG("MOUSE: Middle button will %s", + mouse_config.middle_release + ? "capture/release the mouse (clicks not sent to the game/program)" + : "be sent to the game/program (clicks not used to capture/release)"); + } // Callback for ps2 irq auto call_int74 = CALLBACK_Allocate(); @@ -656,11 +965,19 @@ void MOUSE_Startup() MouseInterface::InitAllInstances(); mouse_shared.started = true; + + MOUSE_UpdateGFX(); +} + +void MOUSE_NotifyReadyGFX() +{ + mouse_shared.ready_gfx = true; + MOUSE_StartupIfReady(); } void MOUSE_Init(Section * /*sec*/) { // Start mouse emulation if ready - mouse_shared.ready_startup_sequence = true; - MOUSE_Startup(); + mouse_shared.ready_init = true; + MOUSE_StartupIfReady(); } diff --git a/src/hardware/mouse/mouse_common.cpp b/src/hardware/mouse/mouse_common.cpp index eb526ce9b..e63de4a7d 100644 --- a/src/hardware/mouse/mouse_common.cpp +++ b/src/hardware/mouse/mouse_common.cpp @@ -29,9 +29,8 @@ CHECK_NARROWING(); // Common variables // *************************************************************************** -MouseInfo mouse_info; +MouseInfo mouse_info; MouseShared mouse_shared; -MouseVideo mouse_video; // *************************************************************************** // Common helper calculations diff --git a/src/hardware/mouse/mouse_common.h b/src/hardware/mouse/mouse_common.h index a44779c84..8dedd9216 100644 --- a/src/hardware/mouse/mouse_common.h +++ b/src/hardware/mouse/mouse_common.h @@ -30,28 +30,21 @@ class MouseShared { public: bool active_bios = false; // true = BIOS has a registered callback - bool active_dos = false; // true = DOS driver has a functioning callback - bool active_vmm = false; // true = VMware-compatible driver is active + bool active_dos = false; // true = DOS driver has a functioning callback + bool active_vmm = false; // true = VMware-compatible driver is active bool dos_cb_running = false; // true = DOS callback is running // Readiness for initialization - bool ready_startup_sequence = false; - bool ready_config_mouse = false; - bool ready_config_sdl = false; + bool ready_init = false; // if allowed to init in the main startup sequence + bool ready_config = false; // if configuration was read + bool ready_gfx = false; // if GFX subsystem is ready bool started = false; -}; - -class MouseVideo { -public: - bool fullscreen = true; - uint16_t res_x = 640; // resolution to which guest image is scaled, - uint16_t res_y = 400; // excluding black borders - - uint16_t clip_x = 0; // clipping = size of black border (one side) - uint16_t clip_y = 0; + // Screen size + uint32_t resolution_x = 640; // resolution to which guest image is scaled, + uint32_t resolution_y = 400; // excluding black borders }; class MouseInfo { @@ -60,11 +53,8 @@ public: std::vector physical = {}; }; -extern MouseInfo mouse_info; // information which can be shared externally +extern MouseInfo mouse_info; // information which can be shared externally extern MouseShared mouse_shared; // shared internal information -extern MouseVideo mouse_video; // video information - resolution, clipping, etc. - -extern bool mouse_is_captured; // *************************************************************************** // Common helper calculations diff --git a/src/hardware/mouse/mouse_config.cpp b/src/hardware/mouse/mouse_config.cpp index f81735790..6474e54f0 100644 --- a/src/hardware/mouse/mouse_config.cpp +++ b/src/hardware/mouse/mouse_config.cpp @@ -26,7 +26,6 @@ #include "setup.h" #include "string_utils.h" #include "support.h" -#include "video.h" #include @@ -39,44 +38,53 @@ CHECK_NARROWING(); // #define ENABLE_EXPLORER_MOUSE -MouseConfig mouse_config; +MouseConfig mouse_config; MousePredefined mouse_predefined; -static struct { - const std::string param_standard = "standard"; - const std::string param_intellimouse = "intellimouse"; +constexpr auto capture_type_seamless_str = "seamless"; +constexpr auto capture_type_onclick_str = "onclick"; +constexpr auto capture_type_onstart_str = "onstart"; +constexpr auto capture_type_nomouse_str = "nomouse"; + +constexpr auto model_ps2_standard_str = "standard"; +constexpr auto model_ps2_intellimouse_str = "intellimouse"; #ifdef ENABLE_EXPLORER_MOUSE - const std::string param_explorer = "explorer"; +constexpr auto model_ps2_explorer_str = "explorer"; #endif -} models_ps2; - -static struct { - const std::string param_2button = "2button"; - const std::string param_3button = "3button"; - const std::string param_wheel = "wheel"; - const std::string param_msm = "msm"; - const std::string param_2button_msm = "2button+msm"; - const std::string param_3button_msm = "3button+msm"; - const std::string param_wheel_msm = "wheel+msm"; -} models_com; + +constexpr auto model_com_2button_str = "2button"; +constexpr auto model_com_3button_str = "3button"; +constexpr auto model_com_wheel_str = "wheel"; +constexpr auto model_com_msm_str = "msm"; +constexpr auto model_com_2button_msm_str = "2button+msm"; +constexpr auto model_com_3button_msm_str = "3button+msm"; +constexpr auto model_com_wheel_msm_str = "wheel+msm"; + +static const char *list_capture_types[] = { + capture_type_seamless_str, + capture_type_onclick_str, + capture_type_onstart_str, + capture_type_nomouse_str, + nullptr +}; static const char *list_models_ps2[] = { - models_ps2.param_standard.c_str(), - models_ps2.param_intellimouse.c_str(), + model_ps2_standard_str, + model_ps2_intellimouse_str, #ifdef ENABLE_EXPLORER_MOUSE - models_ps2.param_explorer.c_str(), + model_ps2_explorer_str, #endif nullptr }; static const char *list_models_com[] = { - models_com.param_2button.c_str(), - models_com.param_3button.c_str(), - models_com.param_wheel.c_str(), - models_com.param_msm.c_str(), - models_com.param_2button_msm.c_str(), - models_com.param_3button_msm.c_str(), - models_com.param_wheel_msm.c_str(), + model_com_2button_str, + model_com_3button_str, + model_com_wheel_str, + model_com_msm_str, + model_com_2button_msm_str, + model_com_3button_msm_str, + model_com_wheel_msm_str, nullptr }; @@ -105,34 +113,49 @@ static const std::vector list_rates = { // issues. }; +bool MouseConfig::ParseCaptureType(const std::string &capture_str, MouseCapture &capture) +{ + if (capture_str == capture_type_seamless_str) + capture = MouseCapture::Seamless; + else if (capture_str == capture_type_onclick_str) + capture = MouseCapture::OnClick; + else if (capture_str == capture_type_onstart_str) + capture = MouseCapture::OnStart; + else if (capture_str == capture_type_nomouse_str) + capture = MouseCapture::NoMouse; + else + return false; + return true; +} + bool MouseConfig::ParseCOMModel(const std::string &model_str, MouseModelCOM &model, bool &auto_msm) { - if (model_str == models_com.param_2button) { + if (model_str == model_com_2button_str) { model = MouseModelCOM::Microsoft; auto_msm = false; return true; - } else if (model_str == models_com.param_3button) { + } else if (model_str == model_com_3button_str) { model = MouseModelCOM::Logitech; auto_msm = false; return true; - } else if (model_str == models_com.param_wheel) { + } else if (model_str == model_com_wheel_str) { model = MouseModelCOM::Wheel; auto_msm = false; return true; - } else if (model_str == models_com.param_msm) { + } else if (model_str == model_com_msm_str) { model = MouseModelCOM::MouseSystems; auto_msm = false; return true; - } else if (model_str == models_com.param_2button_msm) { + } else if (model_str == model_com_2button_msm_str) { model = MouseModelCOM::Microsoft; auto_msm = true; return true; - } else if (model_str == models_com.param_3button_msm) { + } else if (model_str == model_com_3button_msm_str) { model = MouseModelCOM::Logitech; auto_msm = true; return true; - } else if (model_str == models_com.param_wheel_msm) { + } else if (model_str == model_com_wheel_msm_str) { model = MouseModelCOM::Wheel; auto_msm = true; return true; @@ -143,12 +166,12 @@ bool MouseConfig::ParseCOMModel(const std::string &model_str, bool MouseConfig::ParsePS2Model(const std::string &model_str, MouseModelPS2 &model) { - if (model_str == models_ps2.param_standard) + if (model_str == model_ps2_standard_str) model = MouseModelPS2::Standard; - else if (model_str == models_ps2.param_intellimouse) + else if (model_str == model_ps2_intellimouse_str) model = MouseModelPS2::IntelliMouse; #ifdef ENABLE_EXPLORER_MOUSE - else if (model_str == models_ps2.param_explorer) + else if (model_str == model_ps2_explorer_str) model = MouseModelPS2::Explorer; #endif else @@ -171,14 +194,25 @@ static void config_read(Section *section) // Settings changeable during runtime - mouse_config.dos_immediate = conf->Get_bool("dos_mouse_immediate"); - mouse_config.raw_input = conf->Get_bool("mouse_raw_input"); - GFX_SetMouseRawInput(mouse_config.raw_input); + std::string prop_str = conf->Get_string("mouse_capture"); + MouseConfig::ParseCaptureType(prop_str, mouse_config.capture); + + mouse_config.middle_release = conf->Get_bool("mouse_middle_release"); + mouse_config.raw_input = conf->Get_bool("mouse_raw_input"); + mouse_config.dos_immediate = conf->Get_bool("dos_mouse_immediate"); // Settings below should be read only once - if (mouse_shared.ready_config_mouse) { - MOUSE_NotifyStateChanged(); + if (mouse_shared.ready_config) { + + if (mouse_config.capture == MouseCapture::NoMouse) { + // If NoMouse got configured in runtime, + // immediately clear all the mapping + MouseControlAPI mouse_config_api; + mouse_config_api.UnMap(MouseControlAPI::ListIDs()); + } + + MOUSE_UpdateGFX(); return; } @@ -196,7 +230,7 @@ static void config_read(Section *section) // PS/2 AUX port mouse configuration - std::string prop_str = conf->Get_string("ps2_mouse_model"); + prop_str = conf->Get_string("ps2_mouse_model"); MouseConfig::ParsePS2Model(prop_str, mouse_config.model_ps2); // COM port mouse configuration @@ -206,9 +240,9 @@ static void config_read(Section *section) mouse_config.model_com, mouse_config.model_com_auto_msm); - // Start mouse emulation if ready - mouse_shared.ready_config_mouse = true; - MOUSE_Startup(); + // Start mouse emulation if everything is ready + mouse_shared.ready_config = true; + MOUSE_StartupIfReady(); } static void config_init(Section_prop &secprop) @@ -216,13 +250,29 @@ static void config_init(Section_prop &secprop) constexpr auto always = Property::Changeable::Always; constexpr auto only_at_start = Property::Changeable::OnlyAtStart; - Prop_bool *prop_bool = nullptr; - Prop_int *prop_int = nullptr; - Prop_string *prop_str = nullptr; + Prop_bool *prop_bool = nullptr; + Prop_int *prop_int = nullptr; + Prop_string *prop_str = nullptr; PropMultiVal *prop_multi = nullptr; // General configuration + prop_str = secprop.Add_string("mouse_capture", always, + capture_type_onstart_str); + assert(prop_str); + prop_str->Set_values(list_capture_types); + prop_str->Set_help( + "Choose a mouse control method:\n" + " onclick: Capture the mouse when clicking any button in the window.\n" + " onstart: Capture the mouse immediately on start.\n" + " seamless: Let the mouse move seamlessly; captures only with middle-click or\n" + " hotkey.\n" + " nomouse: Hide the mouse and don't send input to the game."); + + prop_bool = secprop.Add_bool("mouse_middle_release", always, true); + prop_bool->Set_help("If true, middle-click will release the captured mouse, and also\n" + "capture when seamless."); + prop_multi = secprop.AddMultiVal("mouse_sensitivity", only_at_start, ","); prop_multi->Set_help( "Default mouse sensitivity. 100 is a base value, 150 is 150% sensitivity, etc.\n" @@ -269,7 +319,8 @@ static void config_init(Section_prop &secprop) // Physical mice configuration - prop_str = secprop.Add_string("ps2_mouse_model", only_at_start, "intellimouse"); + prop_str = secprop.Add_string("ps2_mouse_model", only_at_start, + model_ps2_intellimouse_str); assert(prop_str); prop_str->Set_values(list_models_ps2); prop_str->Set_help( @@ -282,7 +333,8 @@ static void config_init(Section_prop &secprop) #endif "Default: intellimouse"); - prop_str = secprop.Add_string("com_mouse_model", only_at_start, "wheel+msm"); + prop_str = secprop.Add_string("com_mouse_model", only_at_start, + model_com_wheel_msm_str); assert(prop_str); prop_str->Set_values(list_models_com); prop_str->Set_help( diff --git a/src/hardware/mouse/mouse_config.h b/src/hardware/mouse/mouse_config.h index b8e757342..caa52c2a9 100644 --- a/src/hardware/mouse/mouse_config.h +++ b/src/hardware/mouse/mouse_config.h @@ -59,6 +59,8 @@ extern MousePredefined mouse_predefined; // Configuration file content // *************************************************************************** +enum class MouseCapture : uint8_t { Seamless, OnClick, OnStart, NoMouse }; + enum class MouseModelPS2 : uint8_t { // Values must match PS/2 protocol IDs Standard = 0x00, @@ -74,19 +76,9 @@ enum class MouseModelCOM : uint8_t { MouseSystems }; -enum class MouseModelBus : uint8_t { - NoMouse, - Bus, - InPort, -}; - struct MouseConfig { - // From [sdl] section - - bool no_mouse = false; // true = NoMouse selected in GUI - bool seamless = false; // true = seamless mouse integration - - // From [mouse] section + MouseCapture capture = MouseCapture::OnStart; + bool middle_release = true; int16_t sensitivity_x = 50; // default sensitivity values int16_t sensitivity_y = 50; @@ -103,6 +95,8 @@ struct MouseConfig { // Helper functions for external modules static const std::vector &GetValidMinRateList(); + static bool ParseCaptureType(const std::string &capture_str, + MouseCapture &capture); static bool ParseCOMModel(const std::string &model_str, MouseModelCOM &model, bool &auto_msm); static bool ParsePS2Model(const std::string &model_str, MouseModelPS2 &model); diff --git a/src/hardware/mouse/mouse_interfaces.cpp b/src/hardware/mouse/mouse_interfaces.cpp index f2a3620a4..4015256c2 100644 --- a/src/hardware/mouse/mouse_interfaces.cpp +++ b/src/hardware/mouse/mouse_interfaces.cpp @@ -148,13 +148,14 @@ public: ~InterfaceDos() = default; void NotifyMoved(MouseEvent &ev, const float x_rel, const float y_rel, - const uint16_t x_abs, const uint16_t y_abs) override; + const uint32_t x_abs, const uint32_t y_abs) override; void NotifyButton(MouseEvent &ev, const uint8_t idx, const bool pressed) override; void NotifyWheel(MouseEvent &ev, const int16_t w_rel) override; - void NotifyBooting() override; + void UpdateInputType() override; + private: friend class MouseInterface; @@ -163,7 +164,6 @@ private: void Init() override; - void UpdateRawMapped() override; void UpdateMinRate() override; void UpdateRate() override; }; @@ -174,11 +174,13 @@ public: ~InterfacePS2() = default; void NotifyMoved(MouseEvent &ev, const float x_rel, const float y_rel, - const uint16_t x_abs, const uint16_t y_abs) override; + const uint32_t x_abs, const uint32_t y_abs) override; void NotifyButton(MouseEvent &ev, const uint8_t idx, const bool pressed) override; void NotifyWheel(MouseEvent &ev, const int16_t w_rel) override; + void UpdateInputType() override; + private: friend class MouseInterface; @@ -187,7 +189,6 @@ private: void Init() override; - void UpdateRawMapped() override; void UpdateSensitivity() override; void UpdateRate() override; @@ -201,7 +202,7 @@ public: ~InterfaceCOM() = default; void NotifyMoved(MouseEvent &ev, const float x_rel, const float y_rel, - const uint16_t x_abs, const uint16_t y_abs) override; + const uint32_t x_abs, const uint32_t y_abs) override; void NotifyButton(MouseEvent &ev, const uint8_t idx, const bool pressed) override; void NotifyWheel(MouseEvent &ev, const int16_t w_rel) override; @@ -263,8 +264,10 @@ void MouseInterface::InitAllInstances() default: assert(false); break; } - for (auto interface : mouse_interfaces) + for (auto interface : mouse_interfaces) { interface->Init(); + interface->UpdateConfig(); + } } MouseInterface *MouseInterface::Get(const MouseInterfaceId interface_id) @@ -415,7 +418,7 @@ void MouseInterface::SetMapStatus(const MouseMapStatus status, const uint8_t dev if (map_status != new_map_status || mapped_idx != new_mapped_idx) ResetButtons(); if (map_status != new_map_status) - UpdateRawMapped(); + UpdateInputType(); if (mapped_idx != new_mapped_idx) ManyMouseGlue::GetInstance().Map(new_mapped_idx, interface_id); @@ -514,11 +517,11 @@ void MouseInterface::UnRegisterListener() void MouseInterface::UpdateConfig() { - UpdateRawMapped(); + UpdateInputType(); UpdateSensitivity(); } -void MouseInterface::UpdateRawMapped() {} +void MouseInterface::UpdateInputType() {} void MouseInterface::UpdateSensitivity() { @@ -630,7 +633,7 @@ void InterfaceDos::Init() } void InterfaceDos::NotifyMoved(MouseEvent &ev, const float x_rel, const float y_rel, - const uint16_t x_abs, const uint16_t y_abs) + const uint32_t x_abs, const uint32_t y_abs) { ev.dos_moved = MOUSEDOS_NotifyMoved(x_rel * sensitivity_coeff_x, y_rel * sensitivity_coeff_y, @@ -668,10 +671,12 @@ void InterfaceDos::NotifyBooting() ManyMouseGlue::GetInstance().ShutdownIfSafe(); } -void InterfaceDos::UpdateRawMapped() +void InterfaceDos::UpdateInputType() { - MOUSEDOS_NotifyMapped(IsMapped()); - MOUSEDOS_NotifyRawInput(mouse_config.raw_input || IsMapped()); + const bool use_relative = IsMapped() || MOUSE_IsCaptured(); + const bool is_input_raw = IsMapped() || mouse_config.raw_input; + + MOUSEDOS_NotifyInputType(use_relative, is_input_raw); } void InterfaceDos::UpdateMinRate() @@ -698,7 +703,7 @@ void InterfacePS2::Init() } void InterfacePS2::NotifyMoved(MouseEvent &ev, const float x_rel, const float y_rel, - const uint16_t x_abs, const uint16_t y_abs) + const uint32_t x_abs, const uint32_t y_abs) { const bool request_ps2 = MOUSEPS2_NotifyMoved(x_rel * sensitivity_coeff_x, y_rel * sensitivity_coeff_y); @@ -731,10 +736,12 @@ void InterfacePS2::NotifyWheel(MouseEvent &ev, const int16_t w_rel) ev.request_ps2 = request_ps2 || request_vmm; } -void InterfacePS2::UpdateRawMapped() +void InterfacePS2::UpdateInputType() { - MOUSEVMM_NotifyMapped(IsMapped()); - MOUSEVMM_NotifyRawInput(mouse_config.raw_input || IsMapped()); + const bool use_relative = IsMapped() || MOUSE_IsCaptured(); + const bool is_input_raw = IsMapped() || mouse_config.raw_input; + + MOUSEVMM_NotifyInputType(use_relative, is_input_raw); } void InterfacePS2::UpdateSensitivity() @@ -759,8 +766,8 @@ InterfaceCOM::InterfaceCOM(const uint8_t port_id) mouse_predefined.sensitivity_com) {} -void InterfaceCOM::NotifyMoved(MouseEvent &, const float x_rel, - const float y_rel, const uint16_t, const uint16_t) +void InterfaceCOM::NotifyMoved(MouseEvent &, const float x_rel, const float y_rel, + const uint32_t, const uint32_t) { assert(listener); diff --git a/src/hardware/mouse/mouse_interfaces.h b/src/hardware/mouse/mouse_interfaces.h index 8974add8a..063ce34f6 100644 --- a/src/hardware/mouse/mouse_interfaces.h +++ b/src/hardware/mouse/mouse_interfaces.h @@ -27,20 +27,22 @@ // Main mouse module // *************************************************************************** -void MOUSE_Startup(); +void MOUSE_StartupIfReady(); void MOUSE_NotifyDisconnect(const MouseInterfaceId interface_id); void MOUSE_NotifyFakePS2(); // fake PS/2 event, for VMware protocol support void MOUSE_NotifyResetDOS(); -void MOUSE_NotifyStateChanged(); + +bool MOUSE_IsCaptured(); +void MOUSE_UpdateGFX(); // *************************************************************************** // DOS mouse driver // *************************************************************************** void MOUSEDOS_Init(); -void MOUSEDOS_NotifyMapped(const bool enabled); -void MOUSEDOS_NotifyRawInput(const bool enabled); +void MOUSEDOS_NotifyInputType(const bool new_use_relative, + const bool new_is_input_raw); void MOUSEDOS_NotifyMinRate(const uint16_t value_hz); void MOUSEDOS_DrawCursor(); @@ -51,7 +53,7 @@ Bitu MOUSEDOS_DoCallback(const uint8_t mask, const MouseButtons12S buttons_12S); // - understands up to 3 buttons bool MOUSEDOS_NotifyMoved(const float x_rel, const float y_rel, - const uint16_t x_abs, const uint16_t y_abs); + const uint32_t x_abs, const uint32_t y_abs); bool MOUSEDOS_NotifyWheel(const int16_t w_rel); uint8_t MOUSEDOS_UpdateMoved(); @@ -88,16 +90,16 @@ Bitu MOUSEBIOS_DoCallback(); // *************************************************************************** void MOUSEVMM_Init(); -void MOUSEVMM_NotifyMapped(const bool enabled); -void MOUSEVMM_NotifyRawInput(const bool enabled); -void MOUSEVMM_NewScreenParams(const uint16_t x_abs, const uint16_t y_abs); +void MOUSEVMM_NotifyInputType(const bool new_use_relative, + const bool new_is_input_raw); +void MOUSEVMM_NewScreenParams(const uint32_t x_abs, const uint32_t y_abs); void MOUSEVMM_Deactivate(); // - needs absolute mouse position // - understands up to 3 buttons bool MOUSEVMM_NotifyMoved(const float x_rel, const float y_rel, - const uint16_t x_abs, const uint16_t y_abs); + const uint32_t x_abs, const uint32_t y_abs); bool MOUSEVMM_NotifyButton(const MouseButtons12S buttons_12S); bool MOUSEVMM_NotifyWheel(const int16_t w_rel); @@ -128,7 +130,7 @@ public: static MouseInterface *GetSerial(const uint8_t port_id); virtual void NotifyMoved(MouseEvent &ev, const float x_rel, const float y_rel, - const uint16_t x_abs, const uint16_t y_abs) = 0; + const uint32_t x_abs, const uint32_t y_abs) = 0; virtual void NotifyButton(MouseEvent &ev, const uint8_t idx, const bool pressed) = 0; virtual void NotifyWheel(MouseEvent &ev, const int16_t w_rel) = 0; @@ -166,6 +168,7 @@ public: void ConfigResetMinRate(); virtual void UpdateConfig(); + virtual void UpdateInputType(); virtual void RegisterListener(CSerialMouse &listener_object); virtual void UnRegisterListener(); @@ -182,7 +185,6 @@ protected: void SetMapStatus(const MouseMapStatus status, const uint8_t device_idx = idx_host_pointer); - virtual void UpdateRawMapped(); virtual void UpdateSensitivity(); virtual void UpdateMinRate(); virtual void UpdateRate(); diff --git a/src/hardware/mouse/mouse_manymouse.cpp b/src/hardware/mouse/mouse_manymouse.cpp index b4c736b3e..a2cd51023 100644 --- a/src/hardware/mouse/mouse_manymouse.cpp +++ b/src/hardware/mouse/mouse_manymouse.cpp @@ -229,6 +229,10 @@ void ManyMouseGlue::RescanIfSafe() bool ManyMouseGlue::ProbeForMapping(uint8_t &device_id) { + // Do not even try if NoMouse is configured + if (mouse_config.capture == MouseCapture::NoMouse) + return false; + // Wait a little to speedup screen update constexpr uint32_t ticks_threshold = 50; // time to wait idle in PIC ticks const auto pic_ticks_start = PIC_Ticks; @@ -281,7 +285,7 @@ bool ManyMouseGlue::ProbeForMapping(uint8_t &device_id) break; } - if (is_mapping_in_effect && !mouse_config.no_mouse) + if (is_mapping_in_effect) PIC_AddEvent(manymouse_tick, tick_interval); return success; } @@ -346,7 +350,7 @@ void ManyMouseGlue::MapFinalize() continue; is_mapping_in_effect = true; - if (!mouse_config.no_mouse) + if (mouse_config.capture != MouseCapture::NoMouse) PIC_AddEvent(manymouse_tick, tick_interval); break; } @@ -361,7 +365,7 @@ void ManyMouseGlue::HandleEvent(const ManyMouseEvent &event, const bool critical { if (GCC_UNLIKELY(event.device >= mouse_info.physical.size())) return; // device ID out of supported range - if (GCC_UNLIKELY(mouse_config.no_mouse && + if (GCC_UNLIKELY(mouse_config.capture == MouseCapture::NoMouse && event.type != MANYMOUSE_EVENT_DISCONNECT)) return; // mouse control disabled in GUI @@ -435,7 +439,7 @@ void ManyMouseGlue::HandleEvent(const ManyMouseEvent &event, const bool critical void ManyMouseGlue::Tick() { - assert(!mouse_config.no_mouse); + assert(mouse_config.capture != MouseCapture::NoMouse); // Handle all the events from the queue ManyMouseEvent event; diff --git a/src/hardware/mouse/mouseif_dos_driver.cpp b/src/hardware/mouse/mouseif_dos_driver.cpp index 81587c2e5..a1a5be573 100644 --- a/src/hardware/mouse/mouseif_dos_driver.cpp +++ b/src/hardware/mouse/mouseif_dos_driver.cpp @@ -76,8 +76,9 @@ static MouseButtons12S buttons = 0; static float pos_x = 0.0f; static float pos_y = 0.0f; static int8_t counter_w = 0; // wheel counter -static bool is_mapped = false; // true = physical mouse is mapped to this interface -static bool raw_input = true; // true = no host mouse acceleration pre-applied + +static bool use_relative = true; // true = ignore absolute mouse position, use relative +static bool is_input_raw = true; // true = no host mouse acceleration pre-applied static bool rate_is_set = false; // true = rate was set by DOS application static uint16_t rate_hz = 0; @@ -90,8 +91,8 @@ static struct { // Mouse movement float x_rel = 0.0f; float y_rel = 0.0f; - uint16_t x_abs = 0; - uint16_t y_abs = 0; + uint32_t x_abs = 0; + uint32_t y_abs = 0; // Wheel movement int16_t w_rel = 0; @@ -569,7 +570,7 @@ void MOUSEDOS_DrawCursor() static void update_driver_active() { mouse_shared.active_dos = (state.user_callback_mask != 0); - MOUSE_NotifyStateChanged(); + MOUSE_UpdateGFX(); } static uint8_t get_reset_wheel_8bit() @@ -895,22 +896,21 @@ static void move_cursor_captured(const float x_rel, const float y_rel) } static void move_cursor_seamless(const float x_rel, const float y_rel, - const uint16_t x_abs, const uint16_t y_abs) + const uint32_t x_abs, const uint32_t y_abs) { // Update mickey counters update_mickeys_on_move(x_rel, y_rel); - auto calculate = [](const uint16_t absolute, - const uint16_t res, - const uint16_t clip) { - assert(res > 1u); - return (static_cast(absolute) - clip) / - static_cast(res - 1); + auto calculate = [](const uint32_t absolute, + const uint32_t resolution) { + assert(resolution > 1u); + return static_cast(absolute) / + static_cast(resolution - 1); }; // Apply mouse movement to mimic host OS - const float x = calculate(x_abs, mouse_video.res_x, mouse_video.clip_x); - const float y = calculate(y_abs, mouse_video.res_y, mouse_video.clip_y); + const float x = calculate(x_abs, mouse_shared.resolution_x); + const float y = calculate(y_abs, mouse_shared.resolution_y); // TODO: this is probably overcomplicated, especially // the usage of relative movement - to be investigated @@ -938,14 +938,6 @@ static void move_cursor_seamless(const float x_rel, const float y_rel, } } -static bool is_captured() -{ - // If DOS driver uses a mapped physical mouse, always consider it - // captured, as we have no absolute mouse position from the host OS - - return mouse_is_captured || is_mapped; -} - static uint8_t move_cursor() { const auto old_pos_x = get_pos_x(); @@ -954,14 +946,14 @@ static uint8_t move_cursor() const auto old_mickey_x = static_cast(state.mickey_counter_x); const auto old_mickey_y = static_cast(state.mickey_counter_y); - if (is_captured()) { + if (use_relative) { // For raw mouse input use our built-in pointer acceleration model const float acceleration_coeff = - raw_input ? MOUSE_GetBallisticsCoeff( + is_input_raw ? MOUSE_GetBallisticsCoeff( speed_mickeys.Get() / state.double_speed_threshold) * 2.0f - : 2.0f; + : 2.0f; const float tmp_x = pending.x_rel * acceleration_coeff * state.sensitivity_coeff_x; @@ -1076,16 +1068,15 @@ uint8_t MOUSEDOS_UpdateWheel() } bool MOUSEDOS_NotifyMoved(const float x_rel, const float y_rel, - const uint16_t x_abs, const uint16_t y_abs) + const uint32_t x_abs, const uint32_t y_abs) { // Check if an event is needed bool event_needed = false; - if (is_captured()) { + if (use_relative) { // Uses relative mouse movements - processing is too complicated // to easily predict whether the event can be safely omitted event_needed = true; - // TODO: it actually can be done - but it will require some - // refactoring + // TODO: this can be done, but requyires refactoring } else { // Uses absolute mouse position (seamless mode), relative // movements can wait to be reported - they are completely @@ -1635,13 +1626,10 @@ Bitu MOUSEDOS_DoCallback(const uint8_t mask, const MouseButtons12S buttons_12S) // which allows seamless mouse integration. It is also included in // DOSBox-X and Dosemu2: // - https://github.com/joncampbell123/dosbox-x/pull/3424 - // - - // https://github.com/dosemu2/dosemu2/issues/1552#issuecomment-1100777880 - // - - // https://github.com/dosemu2/dosemu2/commit/cd9d2dbc8e3d58dc7cbc92f172c0d447881526be - // - - // https://github.com/joncampbell123/dosbox-x/commit/aec29ce28eb4b520f21ead5b2debf370183b9f28 - reg_ah = (!is_captured() && mouse_moved) ? 1 : 0; + // - https://github.com/dosemu2/dosemu2/issues/1552#issuecomment-1100777880 + // - https://github.com/dosemu2/dosemu2/commit/cd9d2dbc8e3d58dc7cbc92f172c0d447881526be + // - https://github.com/joncampbell123/dosbox-x/commit/aec29ce28eb4b520f21ead5b2debf370183b9f28 + reg_ah = (!use_relative && mouse_moved) ? 1 : 0; reg_al = mask; reg_bl = buttons_12S.data; @@ -1659,14 +1647,11 @@ Bitu MOUSEDOS_DoCallback(const uint8_t mask, const MouseButtons12S buttons_12S) return CBRET_NONE; } -void MOUSEDOS_NotifyMapped(const bool enabled) -{ - is_mapped = enabled; -} - -void MOUSEDOS_NotifyRawInput(const bool enabled) +void MOUSEDOS_NotifyInputType(const bool new_use_relative, + const bool new_is_input_raw) { - raw_input = enabled; + use_relative = new_use_relative; + is_input_raw = new_is_input_raw; } void MOUSEDOS_Init() diff --git a/src/hardware/mouse/mouseif_ps2_bios.cpp b/src/hardware/mouse/mouseif_ps2_bios.cpp index 5ea009bb6..b4ad95c63 100644 --- a/src/hardware/mouse/mouseif_ps2_bios.cpp +++ b/src/hardware/mouse/mouseif_ps2_bios.cpp @@ -438,14 +438,14 @@ void MOUSEBIOS_SetScaling21(const bool enable) bool MOUSEBIOS_Enable() { mouse_shared.active_bios = callback_init; - MOUSE_NotifyStateChanged(); + MOUSE_UpdateGFX(); return callback_init; } bool MOUSEBIOS_Disable() { mouse_shared.active_bios = false; - MOUSE_NotifyStateChanged(); + MOUSE_UpdateGFX(); return true; } diff --git a/src/hardware/mouse/mouseif_virtual_machines.cpp b/src/hardware/mouse/mouseif_virtual_machines.cpp index b2133bab6..f17b8455f 100644 --- a/src/hardware/mouse/mouseif_virtual_machines.cpp +++ b/src/hardware/mouse/mouseif_virtual_machines.cpp @@ -62,19 +62,17 @@ union VMwareButtons { bit_view<3, 1> middle; }; -static constexpr io_port_t VMWARE_PORT = 0x5658u; // communication port -// static constexpr io_port_t VMWARE_PORTHB = 0x5659u; // communication port, -// high bandwidth -static constexpr uint32_t VMWARE_MAGIC = 0x564D5868u; // magic number for all - // VMware calls -static constexpr uint32_t ABS_UPDATED = 4; // tells that new pointer - // position is available +static constexpr io_port_t VMWARE_PORT = 0x5658u; // communication port +// static constexpr io_port_t VMWARE_PORTHB = 0x5659u; // communication port, high bandwidth +static constexpr uint32_t VMWARE_MAGIC = 0x564D5868u; // magic number for all VMware calls +static constexpr uint32_t ABS_UPDATED = 4; // tells about new pointer position static constexpr uint32_t ABS_NOT_UPDATED = 0; -static bool raw_input = true; // true = no host mouse acceleration pre-applied -static bool is_mapped = false; // true = physical mouse is mapped to this interface -static bool updated = false; // true = mouse state update waits to be picked up -static VMwareButtons buttons; // state of mouse buttons, in VMware format +static bool use_relative = true; // true = ignore absolute mouse position, use relative +static bool is_input_raw = true; // true = no host mouse acceleration pre-applied + +static bool updated = false; // true = mouse state update waits to be picked up +static VMwareButtons buttons; // state of mouse buttons, in VMware format static uint16_t scaled_x = 0x7fff; // absolute position scaled from 0 to 0xffff static uint16_t scaled_y = 0x7fff; // 0x7fff is a center position static int8_t counter_w = 0; // wheel movement counter @@ -96,16 +94,17 @@ static void MOUSEVMM_Activate() if (!mouse_shared.active_vmm) { mouse_shared.active_vmm = true; LOG_MSG("MOUSE (PS/2): VMware protocol enabled"); - if (mouse_is_captured) { - // If mouse is captured, prepare sane start settings - // (center of the screen, will trigger mouse move event) - pos_x = mouse_video.res_x / 2.0f; - pos_y = mouse_video.res_y / 2.0f; + MOUSEPS2_UpdateButtonSquish(); + MOUSE_UpdateGFX(); + if (use_relative) { + // If no seamless integration, prepare sane + // cursor star position + pos_x = static_cast(mouse_shared.resolution_x) / 2.0f; + pos_y = static_cast(mouse_shared.resolution_y) / 2.0f; scaled_x = 0; scaled_y = 0; + MOUSE_NotifyFakePS2(); } - MOUSEPS2_UpdateButtonSquish(); - MOUSE_NotifyStateChanged(); } buttons.data = 0; counter_w = 0; @@ -117,20 +116,17 @@ void MOUSEVMM_Deactivate() mouse_shared.active_vmm = false; LOG_MSG("MOUSE (PS/2): VMware protocol disabled"); MOUSEPS2_UpdateButtonSquish(); - MOUSE_NotifyStateChanged(); + MOUSE_UpdateGFX(); } buttons.data = 0; counter_w = 0; } -void MOUSEVMM_NotifyMapped(const bool enabled) +void MOUSEVMM_NotifyInputType(const bool new_use_relative, + const bool new_is_input_raw) { - is_mapped = enabled; -} - -void MOUSEVMM_NotifyRawInput(const bool enabled) -{ - raw_input = enabled; + use_relative = new_use_relative; + is_input_raw = new_is_input_raw; } static void cmd_get_version() @@ -189,7 +185,7 @@ static uint32_t port_read_vmware(const io_port_t, const io_width_t) } bool MOUSEVMM_NotifyMoved(const float x_rel, const float y_rel, - const uint16_t x_abs, const uint16_t y_abs) + const uint32_t x_abs, const uint32_t y_abs) { if (!mouse_shared.active_vmm) return false; @@ -201,42 +197,38 @@ bool MOUSEVMM_NotifyMoved(const float x_rel, const float y_rel, auto calculate = [](float &position, const float relative, - const uint16_t absolute, - const uint16_t resolution, - const uint16_t clip) { + const uint32_t absolute, + const uint32_t resolution) { assert(resolution > 1u); - if (mouse_is_captured || is_mapped) { - // Mouse is captured, there is no need for pointer - // integration with host OS - we can use relative - // movement with configured sensitivity and (for - // raw mouse input) our built-in pointer acceleration - // model - - if (raw_input) { - const auto coeff = MOUSE_GetBallisticsCoeff( - speed_xy.Get()); - position += MOUSE_ClampRelativeMovement( - relative * coeff); + if (use_relative) { + // Mouse is captured or mapped, there is no need for + // pointer integration with host OS - we can use + // relative movement with configured sensitivity and + // (for raw mouse input) our built-in pointer + // acceleration model + + if (is_input_raw) { + const auto coeff = MOUSE_GetBallisticsCoeff(speed_xy.Get()); + position += MOUSE_ClampRelativeMovement(relative * coeff); } else position += MOUSE_ClampRelativeMovement(relative); } else // Cursor position controlled by the host OS - position = static_cast(std::max(absolute - clip, 0)); + position = static_cast(absolute); position = std::clamp(position, 0.0f, static_cast(resolution)); const auto scale = static_cast(UINT16_MAX) / static_cast(resolution - 1); const auto tmp = std::min(static_cast(UINT16_MAX), - static_cast( - std::lround(position * scale))); + static_cast(std::lround(position * scale))); return static_cast(tmp); }; - scaled_x = calculate(pos_x, x_rel, x_abs, mouse_video.res_x, mouse_video.clip_x); - scaled_y = calculate(pos_y, y_rel, y_abs, mouse_video.res_y, mouse_video.clip_y); + scaled_x = calculate(pos_x, x_rel, x_abs, mouse_shared.resolution_x); + scaled_y = calculate(pos_y, y_rel, y_abs, mouse_shared.resolution_y); // Filter out unneeded events (like sub-pixel mouse movements, // which won't change guest side mouse state) @@ -282,7 +274,7 @@ bool MOUSEVMM_NotifyWheel(const int16_t w_rel) return true; } -void MOUSEVMM_NewScreenParams(const uint16_t x_abs, const uint16_t y_abs) +void MOUSEVMM_NewScreenParams(const uint32_t x_abs, const uint32_t y_abs) { // Report a fake mouse movement if (MOUSEVMM_NotifyMoved(0.0f, 0.0f, x_abs, y_abs) && mouse_shared.active_vmm) -- cgit v1.2.3