summaryrefslogtreecommitdiffstatshomepage
path: root/src/osd/sdl3/window.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/osd/sdl3/window.cpp')
-rw-r--r--src/osd/sdl3/window.cpp1336
1 files changed, 1336 insertions, 0 deletions
diff --git a/src/osd/sdl3/window.cpp b/src/osd/sdl3/window.cpp
new file mode 100644
index 00000000000..3262845fc58
--- /dev/null
+++ b/src/osd/sdl3/window.cpp
@@ -0,0 +1,1336 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert, R. Belmont
+//============================================================
+//
+// window.c - SDL window handling
+//
+// SDLMAME by Olivier Galibert and R. Belmont
+//
+//============================================================
+
+// MAME headers
+#include "emu.h"
+#include "emuopts.h"
+#include "render.h"
+#include "screen.h"
+#include "uiinput.h"
+#include "ui/uimain.h"
+
+// OSD headers
+#include "modules/monitor/monitor_common.h"
+#include "osdsdl.h"
+#include "window.h"
+
+// standard C headers
+#include <algorithm>
+#include <cassert>
+#include <cmath>
+#include <list>
+#include <memory>
+
+#ifndef _MSC_VER
+#include <unistd.h>
+#endif
+
+#ifdef SDLMAME_WIN32
+#include <windows.h>
+#endif
+
+
+//============================================================
+// PARAMETERS
+//============================================================
+
+// these are arbitrary values since AFAIK there's no way to make X/SDL tell you
+#define WINDOW_DECORATION_WIDTH (8) // should be more than plenty
+#define WINDOW_DECORATION_HEIGHT (48) // title bar + bottom drag region
+
+// minimum window dimension
+#define MIN_WINDOW_DIM 200
+
+#ifndef SDLMAME_WIN32
+#define WMSZ_TOP (0)
+#define WMSZ_BOTTOM (1)
+#define WMSZ_BOTTOMLEFT (2)
+#define WMSZ_BOTTOMRIGHT (3)
+#define WMSZ_LEFT (4)
+#define WMSZ_TOPLEFT (5)
+#define WMSZ_TOPRIGHT (6)
+#define WMSZ_RIGHT (7)
+#endif
+
+#define SDL_VERSION_EQUALS(v1, vnum2) (SDL_VERSIONNUM(v1.major, v1.minor, v1.patch) == vnum2)
+
+
+
+//============================================================
+// window_init
+// (main thread)
+//============================================================
+
+bool sdl_osd_interface::window_init()
+{
+ osd_printf_verbose("Enter sdlwindow_init\n");
+
+ // We may want to set a number of the hints SDL2 provides.
+ // The code below will document which hints were set.
+ char const *const hints[] = {
+ SDL_HINT_FRAMEBUFFER_ACCELERATION,
+ SDL_HINT_RENDER_DRIVER,
+ SDL_HINT_RENDER_VSYNC,
+ SDL_HINT_VIDEO_X11_XRANDR,
+ SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS,
+ SDL_HINT_ORIENTATIONS,
+ SDL_HINT_XINPUT_ENABLED, SDL_HINT_GAMECONTROLLERCONFIG,
+ SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, SDL_HINT_WINDOW_ALLOW_TOPMOST,
+ SDL_HINT_TIMER_RESOLUTION,
+ SDL_HINT_RENDER_DIRECT3D_THREADSAFE, SDL_HINT_VIDEO_ALLOW_SCREENSAVER,
+ SDL_HINT_MAC_CTRL_CLICK_EMULATE_RIGHT_CLICK,
+ SDL_HINT_VIDEO_WIN_D3DCOMPILER,
+ SDL_HINT_VIDEO_MAC_FULLSCREEN_SPACES,
+ SDL_HINT_RENDER_DIRECT3D11_DEBUG
+ };
+
+ osd_printf_verbose("\nHints:\n");
+ for (auto const hintname : hints)
+ {
+ char const *const hintvalue(SDL_GetHint(hintname));
+ osd_printf_verbose("\t%-40s %s\n", hintname, hintvalue ? hintvalue : "(NULL)");
+ }
+
+ // set up the window list
+ osd_printf_verbose("Leave sdlwindow_init\n");
+ return true;
+}
+
+
+//============================================================
+// sdlwindow_exit
+// (main thread)
+//============================================================
+
+void sdl_osd_interface::window_exit()
+{
+ osd_printf_verbose("Enter sdlwindow_exit\n");
+
+ // free all the windows
+ m_focus_window = nullptr;
+ while (!osd_common_t::s_window_list.empty())
+ {
+ auto window = std::move(osd_common_t::s_window_list.back());
+ s_window_list.pop_back();
+ window->destroy();
+ }
+
+ osd_printf_verbose("Leave sdlwindow_exit\n");
+}
+
+void sdl_window_info::capture_pointer()
+{
+ if (!m_mouse_captured)
+ {
+ SDL_SetWindowMouseGrab(platform_window(), true);
+ SDL_SetWindowKeyboardGrab(platform_window(), true);
+ SDL_SetWindowRelativeMouseMode(platform_window(), true);
+ m_mouse_captured = true;
+ }
+}
+
+void sdl_window_info::release_pointer()
+{
+ if (m_mouse_captured)
+ {
+ SDL_SetWindowMouseGrab(platform_window(), false);
+ SDL_SetWindowKeyboardGrab(platform_window(), false);
+ SDL_SetWindowRelativeMouseMode(platform_window(), false);
+ m_mouse_captured = false;
+ }
+}
+
+void sdl_window_info::hide_pointer()
+{
+ if (!m_mouse_hidden)
+ {
+ SDL_HideCursor();
+ m_mouse_hidden = true;
+ }
+}
+
+void sdl_window_info::show_pointer()
+{
+ if (m_mouse_hidden)
+ {
+ SDL_ShowCursor();
+ m_mouse_hidden = false;
+ }
+}
+
+
+//============================================================
+// sdlwindow_resize
+//============================================================
+
+void sdl_window_info::resize(int32_t width, int32_t height)
+{
+ osd_dim cd = get_size();
+
+ if (width != cd.width() || height != cd.height())
+ {
+ SDL_SetWindowSize(platform_window(), width, height);
+ renderer().notify_changed();
+ }
+}
+
+
+//============================================================
+// sdlwindow_clear_surface
+//============================================================
+
+void sdl_window_info::notify_changed()
+{
+ renderer().notify_changed();
+}
+
+
+//============================================================
+// sdlwindow_toggle_full_screen
+//============================================================
+
+void sdl_window_info::toggle_full_screen()
+{
+ // if we are in debug mode, never go full screen
+ if (machine().debug_flags & DEBUG_FLAG_OSD_ENABLED)
+ return;
+
+ // If we are going fullscreen (leaving windowed) remember our windowed size
+ if (!fullscreen())
+ {
+ m_windowed_dim = get_size();
+ }
+
+ // kill off the drawers
+ renderer_reset();
+ bool is_osx = false;
+#ifdef SDLMAME_MACOSX
+ // FIXME: This is weird behaviour and certainly a bug in SDL
+ is_osx = true;
+#endif
+ if (fullscreen() && (video_config.switchres || is_osx))
+ {
+ SDL_SetWindowFullscreen(platform_window(), 0);
+ SDL_SetWindowFullscreenMode(platform_window(), &m_original_mode);
+ SDL_SetWindowFullscreen(platform_window(), SDL_WINDOW_FULLSCREEN);
+ }
+ SDL_DestroyWindow(platform_window());
+ set_platform_window(nullptr);
+ downcast<sdl_osd_interface &>(machine().osd()).release_keys();
+
+ // toggle the window mode
+ set_fullscreen(!fullscreen());
+
+ complete_create();
+}
+
+void sdl_window_info::modify_prescale(int dir)
+{
+ int new_prescale = prescale();
+
+ if (dir > 0 && prescale() < 20)
+ new_prescale = prescale() + 1;
+ if (dir < 0 && prescale() > 1)
+ new_prescale = prescale() - 1;
+
+ if (new_prescale != prescale())
+ {
+ if (m_fullscreen && video_config.switchres)
+ {
+ complete_destroy();
+
+ m_prescale = new_prescale;
+
+ complete_create();
+ }
+ else
+ {
+ m_prescale = new_prescale;
+ notify_changed();
+ }
+ }
+ machine().ui().popup_time(1, "Prescale %d", prescale());
+}
+
+//============================================================
+// sdlwindow_update_cursor_state
+// (main or window thread)
+//============================================================
+
+void sdl_window_info::update_cursor_state()
+{
+#if (USE_XINPUT && USE_XINPUT_WII_LIGHTGUN_HACK)
+ // Hack for wii-lightguns:
+ // they stop working with a grabbed mouse;
+ // even a ShowCursor(SDL_DISABLE) already does this.
+ // To make the cursor disappear, we'll just set an empty cursor image.
+ unsigned char data[]={0,0,0,0,0,0,0,0};
+ SDL_Cursor *c;
+ c=SDL_CreateCursor(data, data, 8, 8, 0, 0);
+ SDL_SetCursor(c);
+#else
+ // do not do mouse capture if the debugger's enabled to avoid
+ // the possibility of losing control
+ if (!(machine().debug_flags & DEBUG_FLAG_OSD_ENABLED))
+ {
+ bool should_hide_mouse = downcast<sdl_osd_interface&>(machine().osd()).should_hide_mouse();
+
+ if (!fullscreen() && !should_hide_mouse)
+ {
+ show_pointer();
+ release_pointer();
+ }
+ else
+ {
+ hide_pointer();
+ capture_pointer();
+ }
+
+ SDL_SetCursor(nullptr); // Force an update in case the underlying driver has changed visibility
+ }
+#endif
+}
+
+int sdl_window_info::xy_to_render_target(int x, int y, int *xt, int *yt)
+{
+ return renderer().xy_to_render_target(x, y, xt, yt);
+}
+
+void sdl_window_info::mouse_entered(unsigned device)
+{
+ m_mouse_inside = true;
+}
+
+void sdl_window_info::mouse_left(unsigned device)
+{
+ m_mouse_inside = false;
+
+ auto info(std::lower_bound(m_active_pointers.begin(), m_active_pointers.end(), SDL_FingerID(-1), &sdl_pointer_info::compare));
+ if ((m_active_pointers.end() == info) || (info->finger != SDL_FingerID(-1)))
+ return;
+
+ // leaving implicitly releases buttons, so check hold/drag if necessary
+ if (BIT(info->buttons, 0) && (0 < info->clickcnt))
+ {
+ auto const now(std::chrono::steady_clock::now());
+ auto const exp(std::chrono::milliseconds(250) + info->pressed);
+ int const dx(info->x - info->pressedx);
+ int const dy(info->y - info->pressedy);
+ int const distance((dx * dx) + (dy * dy));
+ if ((exp < now) || (CLICK_DISTANCE < distance))
+ info->clickcnt = -info->clickcnt;
+ }
+
+ // push to UI manager
+ machine().ui_input().push_pointer_leave(
+ target(),
+ osd::ui_event_handler::pointer::MOUSE,
+ info->index,
+ device,
+ info->x, info->y,
+ info->buttons, info->clickcnt);
+
+ // dump pointer data
+ m_pointer_mask &= ~(decltype(m_pointer_mask)(1) << info->index);
+ if (info->index < m_next_pointer)
+ m_next_pointer = info->index;
+ m_active_pointers.erase(info);
+}
+
+void sdl_window_info::mouse_down(unsigned device, int x, int y, unsigned button)
+{
+ if (!m_mouse_inside)
+ return;
+
+ auto const info(map_pointer(SDL_FingerID(-1), device));
+ if (m_active_pointers.end() == info)
+ return;
+
+ if ((x == info->x) && (y == info->y) && BIT(info->buttons, button))
+ return;
+
+ // detect multi-click actions
+ if (0 == button)
+ {
+ info->primary_down(
+ x,
+ y,
+ std::chrono::milliseconds(250),
+ CLICK_DISTANCE,
+ false,
+ m_ptrdev_info);
+ }
+
+ // update info and push to UI manager
+ auto const pressed(decltype(info->buttons)(1) << button);
+ info->x = x;
+ info->y = y;
+ info->buttons |= pressed;
+ machine().ui_input().push_pointer_update(
+ target(),
+ osd::ui_event_handler::pointer::MOUSE,
+ info->index,
+ device,
+ x, y,
+ info->buttons, pressed, 0, info->clickcnt);
+}
+
+void sdl_window_info::mouse_up(unsigned device, int x, int y, unsigned button)
+{
+ if (!m_mouse_inside)
+ return;
+
+ auto const info(map_pointer(SDL_FingerID(-1), device));
+ if (m_active_pointers.end() == info)
+ return;
+
+ if ((x == info->x) && (y == info->y) && !BIT(info->buttons, button))
+ return;
+
+ // detect multi-click actions
+ if (0 == button)
+ {
+ info->check_primary_hold_drag(
+ x,
+ y,
+ std::chrono::milliseconds(250),
+ CLICK_DISTANCE);
+ }
+
+ // update info and push to UI manager
+ auto const released(decltype(info->buttons)(1) << button);
+ info->x = x;
+ info->y = y;
+ info->buttons &= ~released;
+ machine().ui_input().push_pointer_update(
+ target(),
+ osd::ui_event_handler::pointer::MOUSE,
+ info->index,
+ device,
+ x, y,
+ info->buttons, 0, released, info->clickcnt);
+}
+
+void sdl_window_info::mouse_moved(unsigned device, int x, int y)
+{
+ if (!m_mouse_inside)
+ return;
+
+ auto const info(map_pointer(SDL_FingerID(-1), device));
+ if (m_active_pointers.end() == info)
+ return;
+
+ // detect multi-click actions
+ if (BIT(info->buttons, 0))
+ {
+ info->check_primary_hold_drag(
+ x,
+ y,
+ std::chrono::milliseconds(250),
+ CLICK_DISTANCE);
+ }
+
+ // update info and push to UI manager
+ info->x = x;
+ info->y = y;
+ machine().ui_input().push_pointer_update(
+ target(),
+ osd::ui_event_handler::pointer::MOUSE,
+ info->index,
+ device,
+ x, y,
+ info->buttons, 0, 0, info->clickcnt);
+}
+
+void sdl_window_info::mouse_wheel(unsigned device, int y)
+{
+ if (!m_mouse_inside)
+ return;
+
+ auto const info(map_pointer(SDL_FingerID(-1), device));
+ if (m_active_pointers.end() == info)
+ return;
+
+ // push to UI manager
+ machine().ui_input().push_mouse_wheel_event(target(), info->x, info->y, y, 3);
+}
+
+void sdl_window_info::finger_down(SDL_FingerID finger, unsigned device, int x, int y)
+{
+ auto const info(map_pointer(finger, device));
+ if (m_active_pointers.end() == info)
+ return;
+
+ assert(!info->buttons);
+
+ // detect multi-click actions
+ info->primary_down(
+ x,
+ y,
+ std::chrono::milliseconds(250),
+ TAP_DISTANCE,
+ true,
+ m_ptrdev_info);
+
+ // update info and push to UI manager
+ info->x = x;
+ info->y = y;
+ info->buttons = 1;
+ machine().ui_input().push_pointer_update(
+ target(),
+ osd::ui_event_handler::pointer::TOUCH,
+ info->index,
+ device,
+ x, y,
+ 1, 1, 0, info->clickcnt);
+}
+
+void sdl_window_info::finger_up(SDL_FingerID finger, unsigned device, int x, int y)
+{
+ auto info(std::lower_bound(m_active_pointers.begin(), m_active_pointers.end(), finger, &sdl_pointer_info::compare));
+ if ((m_active_pointers.end() == info) || (info->finger != finger))
+ return;
+
+ assert(1 == info->buttons);
+
+ // check for conversion to a (multi-)click-and-hold/drag
+ info->check_primary_hold_drag(
+ x,
+ y,
+ std::chrono::milliseconds(250),
+ TAP_DISTANCE);
+
+ // need to remember touches to recognise multi-tap gestures
+ if (0 < info->clickcnt)
+ {
+ auto const now(std::chrono::steady_clock::now());
+ auto const time = std::chrono::milliseconds(250);
+ if ((time + info->pressed) >= now)
+ {
+ try
+ {
+ unsigned i(0);
+ if (m_ptrdev_info.size() > device)
+ i = m_ptrdev_info[device].clear_expired_touches(now, time);
+ else
+ m_ptrdev_info.resize(device + 1);
+
+ if (std::size(m_ptrdev_info[device].touches) > i)
+ {
+ m_ptrdev_info[device].touches[i].when = info->pressed;
+ m_ptrdev_info[device].touches[i].x = info->pressedx;
+ m_ptrdev_info[device].touches[i].y = info->pressedy;
+ m_ptrdev_info[device].touches[i].cnt = info->clickcnt;
+ }
+ }
+ catch (std::bad_alloc const &)
+ {
+ osd_printf_error("win_window_info: error allocating pointer data\n");
+ }
+ }
+ }
+
+ // push to UI manager
+ machine().ui_input().push_pointer_update(
+ target(),
+ osd::ui_event_handler::pointer::TOUCH,
+ info->index,
+ device,
+ x, y,
+ 0, 0, 1, info->clickcnt);
+ machine().ui_input().push_pointer_leave(
+ target(),
+ osd::ui_event_handler::pointer::TOUCH,
+ info->index,
+ device,
+ x, y,
+ 0, info->clickcnt);
+
+ // dump pointer data
+ m_pointer_mask &= ~(decltype(m_pointer_mask)(1) << info->index);
+ if (info->index < m_next_pointer)
+ m_next_pointer = info->index;
+ m_active_pointers.erase(info);
+}
+
+void sdl_window_info::finger_moved(SDL_FingerID finger, unsigned device, int x, int y)
+{
+ auto info(std::lower_bound(m_active_pointers.begin(), m_active_pointers.end(), finger, &sdl_pointer_info::compare));
+ if ((m_active_pointers.end() == info) || (info->finger != finger))
+
+ assert(1 == info->buttons);
+
+ if ((x != info->x) || (y != info->y))
+ {
+ info->check_primary_hold_drag(
+ x,
+ y,
+ std::chrono::milliseconds(250),
+ TAP_DISTANCE);
+
+ // update info and push to UI manager
+ info->x = x;
+ info->y = y;
+ machine().ui_input().push_pointer_update(
+ target(),
+ osd::ui_event_handler::pointer::TOUCH,
+ info->index,
+ device,
+ x, y,
+ 1, 0, 0, info->clickcnt);
+ }
+}
+
+//============================================================
+// sdlwindow_video_window_create
+// (main thread)
+//============================================================
+
+int sdl_window_info::window_init()
+{
+ // set the initial maximized state
+ // FIXME: Does not belong here
+ m_startmaximized = downcast<sdl_options &>(machine().options()).maximize();
+
+ create_target();
+
+ int result = complete_create();
+
+ // handle error conditions
+ if (result == 1)
+ goto error;
+
+ return 0;
+
+error:
+ destroy();
+ return 1;
+}
+
+
+//============================================================
+// sdlwindow_video_window_destroy
+//============================================================
+
+void sdl_window_info::complete_destroy()
+{
+ // Release pointer grab and hide if needed
+ show_pointer();
+ release_pointer();
+
+ if (fullscreen() && video_config.switchres)
+ {
+ SDL_SetWindowFullscreen(platform_window(), 0);
+ SDL_SetWindowFullscreenMode(platform_window(), &m_original_mode);
+ SDL_SetWindowFullscreen(platform_window(), SDL_WINDOW_FULLSCREEN);
+ }
+
+ renderer_reset();
+ SDL_DestroyWindow(platform_window());
+ set_platform_window(nullptr);
+ downcast<sdl_osd_interface &>(machine().osd()).release_keys();
+}
+
+
+//============================================================
+// pick_best_mode
+//============================================================
+
+osd_dim sdl_window_info::pick_best_mode()
+{
+ int minimum_width, minimum_height, target_width, target_height;
+ int i;
+ int num;
+ float size_score, best_score = 0.0f;
+ osd_dim ret(0,0);
+
+ // determine the minimum width/height for the selected target
+ target()->compute_minimum_size(minimum_width, minimum_height);
+
+ // use those as the target for now
+ target_width = minimum_width * std::max(1, prescale());
+ target_height = minimum_height * std::max(1, prescale());
+
+ // if we're not stretching, allow some slop on the minimum since we can handle it
+ {
+ minimum_width -= 4;
+ minimum_height -= 4;
+ }
+
+ SDL_DisplayMode **modes = SDL_GetFullscreenDisplayModes(monitor()->oshandle(), &num);
+ if (num == 0)
+ {
+ osd_printf_error("SDL: No modes available?!\n");
+ exit(-1);
+ }
+ else
+ {
+ for (i = 0; i < num; ++i)
+ {
+ // compute initial score based on difference between target and current
+ size_score = 1.0f / (1.0f + abs((int32_t)modes[i]->w - target_width) + abs((int32_t)modes[i]->h - target_height));
+
+ // if the mode is too small, give a big penalty
+ if (modes[i]->w < minimum_width || modes[i]->h < minimum_height)
+ size_score *= 0.01f;
+
+ // if mode is smaller than we'd like, it only scores up to 0.1
+ if (modes[i]->w < target_width || modes[i]->h < target_height)
+ size_score *= 0.1f;
+
+ // if we're looking for a particular mode, that's a winner
+ if (modes[i]->w == m_win_config.width && modes[i]->h == m_win_config.height)
+ size_score = 2.0f;
+
+ // refresh adds some points
+ if (m_win_config.refresh)
+ size_score *= 1.0f / (1.0f + abs(m_win_config.refresh - modes[i]->refresh_rate) / 10.0f);
+
+ osd_printf_verbose("%4dx%4d@%2d -> %f\n", (int)modes[i]->w, (int)modes[i]->h, (int) modes[i]->refresh_rate, (double) size_score);
+
+ // best so far?
+ if (size_score > best_score)
+ {
+ best_score = size_score;
+ ret = osd_dim(modes[i]->w, modes[i]->h);
+ }
+
+ }
+ }
+ return ret;
+}
+
+//============================================================
+// sdlwindow_video_window_update
+// (main thread)
+//============================================================
+
+void sdl_window_info::update()
+{
+ // adjust the cursor state
+ update_cursor_state();
+
+ // if we're visible and running and not in the middle of a resize, draw
+ if (target() != nullptr)
+ {
+ int tempwidth, tempheight;
+
+ // see if the games video mode has changed
+ target()->compute_minimum_size(tempwidth, tempheight);
+ if (osd_dim(tempwidth, tempheight) != m_minimum_dim)
+ {
+ m_minimum_dim = osd_dim(tempwidth, tempheight);
+
+ if (!this->m_fullscreen)
+ {
+ //Don't resize window without user interaction;
+ //window_resize(blitwidth, blitheight);
+ }
+ else if (video_config.switchres)
+ {
+ osd_dim tmp = this->pick_best_mode();
+ resize(tmp.width(), tmp.height());
+ }
+ }
+
+ osd_ticks_t event_wait_ticks;
+ if (video_config.waitvsync && video_config.syncrefresh)
+ event_wait_ticks = osd_ticks_per_second(); // block at most a second
+ else
+ event_wait_ticks = 0;
+
+ if (m_rendered_event.wait(event_wait_ticks))
+ {
+ const int update = 1;
+
+ // ensure the target bounds are up-to-date, and then get the primitives
+
+ render_primitive_list &primlist = *renderer().get_primitives();
+
+ // and redraw now
+
+ // Check whether window has vector screens
+
+ {
+ const screen_device *screen = screen_device_enumerator(machine().root_device()).byindex(index());
+ if ((screen != nullptr) && (screen->screen_type() == SCREEN_TYPE_VECTOR))
+ renderer().set_flags(osd_renderer::FLAG_HAS_VECTOR_SCREEN);
+ else
+ renderer().clear_flags(osd_renderer::FLAG_HAS_VECTOR_SCREEN);
+ }
+
+ m_primlist = &primlist;
+
+ if (m_primlist == nullptr)
+ {
+ // if no bitmap, just fill
+ }
+ else
+ {
+ // otherwise, render with our drawing system
+ if (video_config.perftest)
+ measure_fps(update);
+ else
+ renderer().draw(update);
+ }
+
+ // all done, ready for next
+ m_rendered_event.set();
+ }
+ }
+}
+
+
+//============================================================
+// complete_create
+//============================================================
+
+int sdl_window_info::complete_create()
+{
+ osd_dim temp(0,0);
+
+ // clear out original mode. Needed on OSX
+ if (fullscreen())
+ {
+ // default to the current mode exactly
+ temp = monitor()->position_size().dim();
+
+ // if we're allowed to switch resolutions, override with something better
+ if (video_config.switchres)
+ temp = pick_best_mode();
+ }
+ else if (m_windowed_dim.width() > 0)
+ {
+ // if we have a remembered size force the new window size to it
+ temp = m_windowed_dim;
+ }
+ else if (m_startmaximized)
+ {
+ temp = get_max_bounds(keepaspect());
+ }
+ else
+ {
+ temp = get_min_bounds(keepaspect());
+ }
+
+ // create the window .....
+
+ osd_printf_verbose("Enter sdl_window_info::create\n");
+ if (renderer_sdl_needs_opengl())
+ {
+ SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
+ }
+
+#if defined(SDLMAME_WIN32)
+ SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0");
+#endif
+
+ // get monitor work area for centering
+ osd_rect work = monitor()->usuable_position_size();
+
+ // create or attach to an existing window
+ SDL_Window *sdlwindow;
+#ifdef SDLMAME_X11
+ const char *attach_window = downcast<sdl_options &>(machine().options()).attach_window();
+#else
+ const char *attach_window = nullptr;
+#endif
+
+ // create the SDL window
+ SDL_PropertiesID props = SDL_CreateProperties();
+ SDL_SetStringProperty(props, SDL_PROP_WINDOW_CREATE_TITLE_STRING, title().c_str());
+ SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_X_NUMBER, work.left() + (work.width() - temp.width()) / 2);
+ SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_Y_NUMBER, work.top() + (work.height() - temp.height()) / 2);
+ SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, temp.width());
+ SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, temp.height());
+ SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_RESIZABLE_BOOLEAN, true);
+
+ if (fullscreen())
+ {
+ SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_FULLSCREEN_BOOLEAN, true);
+ }
+
+ if (renderer_sdl_needs_opengl())
+ {
+ SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_OPENGL_BOOLEAN, true);
+ }
+
+ if (attach_window && *attach_window)
+ {
+ // we're attaching to an existing window; parse the argument
+ unsigned long long attach_window_value;
+ try
+ {
+ attach_window_value = std::stoull(attach_window, nullptr, 0);
+ }
+ catch (std::invalid_argument &)
+ {
+ osd_printf_error("Invalid -attach_window value: %s\n", attach_window);
+ return 1;
+ }
+
+ // and attach to it
+ SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_X11_WINDOW_NUMBER, attach_window_value);
+ }
+
+ sdlwindow = SDL_CreateWindowWithProperties(props);
+
+ if (sdlwindow == nullptr)
+ {
+ if (renderer_sdl_needs_opengl())
+ {
+ osd_printf_error("OpenGL not supported on this driver: %s\n", SDL_GetError());
+ }
+ else
+ {
+ osd_printf_error("Window creation failed: %s\n", SDL_GetError());
+ }
+
+ osd_printf_verbose("Exit sdl_window_info::create\n");
+ return 1;
+ }
+
+ set_platform_window(sdlwindow);
+ renderer_create();
+
+ if (fullscreen() && video_config.switchres)
+ {
+ const SDL_DisplayMode *mode = SDL_GetWindowFullscreenMode(platform_window());
+ m_original_mode = *mode;
+ SDL_DisplayMode newmode;
+ newmode.w = temp.width();
+ newmode.h = temp.height();
+ if (m_win_config.refresh)
+ {
+ newmode.refresh_rate = m_win_config.refresh;
+ }
+ SDL_SetWindowFullscreenMode(platform_window(), &newmode); // Try to set mode
+#ifndef SDLMAME_WIN32
+ /* FIXME: Warp the mouse to 0,0 in case a virtual desktop resolution
+ * is in place after the mode switch - which will most likely be the case
+ * This is a hack to work around a deficiency in SDL2
+ */
+ SDL_WarpMouseInWindow(platform_window(), 1, 1);
+#endif
+ }
+ else
+ {
+ //SDL_SetWindowFullscreenMode(window().sdl_window(), nullptr); // Use desktop
+ }
+
+ // show window
+
+ SDL_ShowWindow(platform_window());
+ //SDL_SetWindowFullscreen(window->sdl_window(), 0);
+ //SDL_SetWindowFullscreen(window->sdl_window(), window->fullscreen());
+ SDL_RaiseWindow(platform_window());
+
+#ifdef SDLMAME_WIN32
+// if (fullscreen())
+// SDL_SetWindowGrab(platform_window(), true);
+#endif
+
+ // update monitor resolution after mode change to ensure proper pixel aspect
+ monitor()->refresh();
+ if (fullscreen() && video_config.switchres)
+ monitor()->update_resolution(temp.width(), temp.height());
+
+ // initialize the drawing backend
+ if (renderer().create())
+ return 1;
+
+ // Make sure we have a consistent state
+ SDL_HideCursor();
+ SDL_ShowCursor();
+
+ return 0;
+}
+
+
+//============================================================
+// draw_video_contents
+// (window thread)
+//============================================================
+
+void sdl_window_info::measure_fps(int update)
+{
+ const unsigned long frames_skip4fps = 100;
+ static int64_t lastTime=0, sumdt=0, startTime=0;
+ static unsigned long frames = 0;
+ int64_t currentTime, t0;
+ double dt;
+ double tps;
+ osd_ticks_t tps_t;
+
+ tps_t = osd_ticks_per_second();
+ tps = (double) tps_t;
+
+ t0 = osd_ticks();
+
+ renderer().draw(update);
+
+ frames++;
+ currentTime = osd_ticks();
+ if(startTime==0||frames==frames_skip4fps)
+ startTime=currentTime;
+ if( frames>=frames_skip4fps )
+ sumdt+=currentTime-t0;
+ if( (currentTime-lastTime)>1L*osd_ticks_per_second() && frames>frames_skip4fps )
+ {
+ dt = (double) (currentTime-startTime) / tps; // in decimale sec.
+ osd_printf_info("%6.2lfs, %4lu F, "
+ "avrg game: %5.2lf FPS %.2lf ms/f, "
+ "avrg video: %5.2lf FPS %.2lf ms/f, "
+ "last video: %5.2lf FPS %.2lf ms/f\n",
+ dt, frames-frames_skip4fps,
+ (double)(frames-frames_skip4fps)/dt, // avrg game fps
+ ( (currentTime-startTime) / ((frames-frames_skip4fps)) ) * 1000.0 / osd_ticks_per_second(),
+ (double)(frames-frames_skip4fps)/((double)(sumdt) / tps), // avrg vid fps
+ ( sumdt / ((frames-frames_skip4fps)) ) * 1000.0 / tps,
+ 1.0/((currentTime-t0) / osd_ticks_per_second()), // this vid fps
+ (currentTime-t0) * 1000.0 / tps
+ );
+ lastTime = currentTime;
+ }
+}
+
+int sdl_window_info::wnd_extra_width()
+{
+ return m_fullscreen ? 0 : WINDOW_DECORATION_WIDTH;
+}
+
+int sdl_window_info::wnd_extra_height()
+{
+ return m_fullscreen ? 0 : WINDOW_DECORATION_HEIGHT;
+}
+
+
+//============================================================
+// constrain_to_aspect_ratio
+// (window thread)
+//============================================================
+
+osd_rect sdl_window_info::constrain_to_aspect_ratio(const osd_rect &rect, int adjustment)
+{
+ int32_t extrawidth = wnd_extra_width();
+ int32_t extraheight = wnd_extra_height();
+ int32_t propwidth, propheight;
+ int32_t minwidth, minheight;
+ int32_t maxwidth, maxheight;
+ int32_t viswidth, visheight;
+ int32_t adjwidth, adjheight;
+ float pixel_aspect;
+
+ // get the pixel aspect ratio for the target monitor
+ pixel_aspect = monitor()->pixel_aspect();
+
+ // determine the proposed width/height
+ propwidth = rect.width() - extrawidth;
+ propheight = rect.height() - extraheight;
+
+ // based on which edge we are adjusting, take either the width, height, or both as gospel
+ // and scale to fit using that as our parameter
+ switch (adjustment)
+ {
+ case WMSZ_BOTTOM:
+ case WMSZ_TOP:
+ target()->compute_visible_area(10000, propheight, pixel_aspect, target()->orientation(), propwidth, propheight);
+ break;
+
+ case WMSZ_LEFT:
+ case WMSZ_RIGHT:
+ target()->compute_visible_area(propwidth, 10000, pixel_aspect, target()->orientation(), propwidth, propheight);
+ break;
+
+ default:
+ target()->compute_visible_area(propwidth, propheight, pixel_aspect, target()->orientation(), propwidth, propheight);
+ break;
+ }
+
+ // get the minimum width/height for the current layout
+ target()->compute_minimum_size(minwidth, minheight);
+
+ // clamp against the absolute minimum
+ propwidth = std::max(propwidth, MIN_WINDOW_DIM);
+ propheight = std::max(propheight, MIN_WINDOW_DIM);
+
+ // clamp against the minimum width and height
+ propwidth = std::max(propwidth, minwidth);
+ propheight = std::max(propheight, minheight);
+
+ // clamp against the maximum (fit on one screen for full screen mode)
+ if (m_fullscreen)
+ {
+ maxwidth = monitor()->position_size().width() - extrawidth;
+ maxheight = monitor()->position_size().height() - extraheight;
+ }
+ else
+ {
+ maxwidth = monitor()->usuable_position_size().width() - extrawidth;
+ maxheight = monitor()->usuable_position_size().height() - extraheight;
+
+ // further clamp to the maximum width/height in the window
+ if (m_win_config.width != 0)
+ maxwidth = std::min(maxwidth, m_win_config.width + extrawidth);
+ if (m_win_config.height != 0)
+ maxheight = std::min(maxheight, m_win_config.height + extraheight);
+ }
+
+ // clamp to the maximum
+ propwidth = std::min(propwidth, maxwidth);
+ propheight = std::min(propheight, maxheight);
+
+ // compute the visible area based on the proposed rectangle
+ target()->compute_visible_area(propwidth, propheight, pixel_aspect, target()->orientation(), viswidth, visheight);
+
+ // clamp visable area to the proposed rectangle
+ viswidth = std::min(viswidth, propwidth);
+ visheight = std::min(visheight, propheight);
+
+ // compute the adjustments we need to make
+ adjwidth = (viswidth + extrawidth) - rect.width();
+ adjheight = (visheight + extraheight) - rect.height();
+
+ // based on which corner we're adjusting, constrain in different ways
+ osd_rect ret(rect);
+
+ switch (adjustment)
+ {
+ case WMSZ_BOTTOM:
+ case WMSZ_BOTTOMRIGHT:
+ case WMSZ_RIGHT:
+ ret = rect.resize(rect.width() + adjwidth, rect.height() + adjheight);
+ break;
+
+ case WMSZ_BOTTOMLEFT:
+ ret = rect.move_by(-adjwidth, 0).resize(rect.width() + adjwidth, rect.height() + adjheight);
+ break;
+
+ case WMSZ_LEFT:
+ case WMSZ_TOPLEFT:
+ case WMSZ_TOP:
+ ret = rect.move_by(-adjwidth, -adjheight).resize(rect.width() + adjwidth, rect.height() + adjheight);
+ break;
+
+ case WMSZ_TOPRIGHT:
+ ret = rect.move_by(0, -adjheight).resize(rect.width() + adjwidth, rect.height() + adjheight);
+ break;
+}
+ return ret;
+}
+
+
+
+//============================================================
+// get_min_bounds
+// (window thread)
+//============================================================
+
+osd_dim sdl_window_info::get_min_bounds(int constrain)
+{
+ int32_t minwidth, minheight;
+
+ //assert(GetCurrentThreadId() == window_threadid);
+
+ // get the minimum target size
+ target()->compute_minimum_size(minwidth, minheight);
+
+ // check if visible area is bigger
+ int32_t viswidth, visheight;
+ target()->compute_visible_area(minwidth, minheight, monitor()->aspect(), target()->orientation(), viswidth, visheight);
+ minwidth = std::max(viswidth, minwidth);
+ minheight = std::max(visheight, minheight);
+
+ // expand to our minimum dimensions
+ if (minwidth < MIN_WINDOW_DIM)
+ minwidth = MIN_WINDOW_DIM;
+ if (minheight < MIN_WINDOW_DIM)
+ minheight = MIN_WINDOW_DIM;
+
+ // account for extra window stuff
+ minwidth += wnd_extra_width();
+ minheight += wnd_extra_height();
+
+ // if we want it constrained, figure out which one is larger
+ if (constrain)
+ {
+ // first constrain with no height limit
+ osd_rect test1(0,0,minwidth,10000);
+ test1 = constrain_to_aspect_ratio(test1, WMSZ_BOTTOMRIGHT);
+
+ // then constrain with no width limit
+ osd_rect test2(0,0,10000,minheight);
+ test2 = constrain_to_aspect_ratio(test2, WMSZ_BOTTOMRIGHT);
+
+ // pick the larger
+ if (test1.width() > test2.width())
+ {
+ minwidth = test1.width();
+ minheight = test1.height();
+ }
+ else
+ {
+ minwidth = test2.width();
+ minheight = test2.height();
+ }
+ }
+
+ // remove extra window stuff
+ minwidth -= wnd_extra_width();
+ minheight -= wnd_extra_height();
+
+ return osd_dim(minwidth, minheight);
+}
+
+//============================================================
+// get_size
+//============================================================
+
+osd_dim sdl_window_info::get_size()
+{
+ int w=0; int h=0;
+ SDL_GetWindowSize(platform_window(), &w, &h);
+ return osd_dim(w,h);
+}
+
+
+//============================================================
+// get_max_bounds
+// (window thread)
+//============================================================
+
+osd_dim sdl_window_info::get_max_bounds(int constrain)
+{
+ //assert(GetCurrentThreadId() == window_threadid);
+
+ // compute the maximum client area
+ // monitor()->refresh();
+ osd_rect maximum = monitor()->usuable_position_size();
+
+ // clamp to the window's max
+ int tempw = maximum.width();
+ int temph = maximum.height();
+ if (m_win_config.width != 0)
+ {
+ int temp = m_win_config.width + wnd_extra_width();
+ if (temp < maximum.width())
+ tempw = temp;
+ }
+ if (m_win_config.height != 0)
+ {
+ int temp = m_win_config.height + wnd_extra_height();
+ if (temp < maximum.height())
+ temph = temp;
+ }
+
+ maximum = maximum.resize(tempw, temph);
+
+ // constrain to fit
+ if (constrain)
+ maximum = constrain_to_aspect_ratio(maximum, WMSZ_BOTTOMRIGHT);
+
+ // remove extra window stuff
+ maximum = maximum.resize(maximum.width() - wnd_extra_width(), maximum.height() - wnd_extra_height());
+
+ return maximum.dim();
+}
+
+
+std::vector<sdl_window_info::sdl_pointer_info>::iterator sdl_window_info::map_pointer(SDL_FingerID finger, unsigned device)
+{
+ auto found(std::lower_bound(m_active_pointers.begin(), m_active_pointers.end(), finger, &sdl_pointer_info::compare));
+ if ((m_active_pointers.end() != found) && (found->finger == finger))
+ return found;
+
+ if ((sizeof(m_next_pointer) * 8) <= m_next_pointer)
+ {
+ assert(~decltype(m_pointer_mask)(0) == m_pointer_mask);
+ osd_printf_warning("sdl_window_info: exceeded maximum number of active pointers\n");
+ return m_active_pointers.end();
+ }
+ assert(!BIT(m_pointer_mask, m_next_pointer));
+
+ try
+ {
+ found = m_active_pointers.emplace(
+ found,
+ sdl_pointer_info(finger, m_next_pointer, device));
+ m_pointer_mask |= decltype(m_pointer_mask)(1) << m_next_pointer;
+ do
+ {
+ ++m_next_pointer;
+ }
+ while (((sizeof(m_next_pointer) * 8) > m_next_pointer) && BIT(m_pointer_mask, m_next_pointer));
+
+ return found;
+ }
+ catch (std::bad_alloc const &)
+ {
+ osd_printf_error("sdl_window_info: error allocating pointer data\n");
+ return m_active_pointers.end();
+ }
+}
+
+
+inline sdl_window_info::sdl_pointer_info::sdl_pointer_info(SDL_FingerID f, unsigned i, unsigned d)
+ : pointer_info(i, d)
+ , finger(f)
+{
+}
+
+
+
+
+//============================================================
+// construction and destruction
+//============================================================
+
+sdl_window_info::sdl_window_info(
+ running_machine &a_machine,
+ render_module &renderprovider,
+ int index,
+ const std::shared_ptr<osd_monitor_info> &a_monitor,
+ const osd_window_config *config)
+ : osd_window_t(a_machine, renderprovider, index, std::move(a_monitor), *config)
+ , m_startmaximized(0)
+ // Following three are used by input code to defer resizes
+ , m_minimum_dim(0, 0)
+ , m_windowed_dim(0, 0)
+ , m_rendered_event(0, 1)
+ , m_extra_flags(0)
+ , m_mouse_captured(false)
+ , m_mouse_hidden(false)
+ , m_pointer_mask(0)
+ , m_next_pointer(0)
+ , m_mouse_inside(false)
+{
+ //FIXME: these should be per_window in config-> or even better a bit set
+ m_fullscreen = !video_config.windowed;
+ m_prescale = video_config.prescale;
+
+ m_windowed_dim = osd_dim(config->width, config->height);
+
+ m_ptrdev_info.reserve(1);
+ m_active_pointers.reserve(16);
+}
+
+sdl_window_info::~sdl_window_info()
+{
+}
+
+
+//============================================================
+// osd_set_aggressive_input_focus
+//============================================================
+
+void osd_set_aggressive_input_focus(bool aggressive_focus)
+{
+ // dummy implementation for now
+}