summaryrefslogtreecommitdiffstatshomepage
path: root/src/osd/windows/window.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/osd/windows/window.cpp')
-rw-r--r--src/osd/windows/window.cpp1966
1 files changed, 1966 insertions, 0 deletions
diff --git a/src/osd/windows/window.cpp b/src/osd/windows/window.cpp
new file mode 100644
index 00000000000..1b6b36b99dd
--- /dev/null
+++ b/src/osd/windows/window.cpp
@@ -0,0 +1,1966 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+//============================================================
+//
+// window.c - Win32 window handling
+//
+//============================================================
+
+#define LOG_THREADS 0
+#define LOG_TEMP_PAUSE 0
+
+// Needed for RAW Input
+#define WM_INPUT 0x00FF
+
+// standard windows headers
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <windowsx.h>
+#include <mmsystem.h>
+
+// standard C headers
+#include <process.h>
+
+// MAME headers
+#include "emu.h"
+#include "emuopts.h"
+#include "uiinput.h"
+
+// MAMEOS headers
+#include "winmain.h"
+#include "window.h"
+#include "video.h"
+#include "input.h"
+#include "strconv.h"
+#include "config.h"
+#include "winutf8.h"
+
+#include "winutil.h"
+
+extern int drawnone_init(running_machine &machine, osd_draw_callbacks *callbacks);
+extern int drawgdi_init(running_machine &machine, osd_draw_callbacks *callbacks);
+extern int drawdd_init(running_machine &machine, osd_draw_callbacks *callbacks);
+extern int drawd3d_init(running_machine &machine, osd_draw_callbacks *callbacks);
+#if defined(USE_BGFX)
+extern int drawbgfx_init(running_machine &machine, osd_draw_callbacks *callbacks);
+#endif
+#if (USE_OPENGL)
+extern int drawogl_init(running_machine &machine, osd_draw_callbacks *callbacks);
+#endif
+
+//============================================================
+// PARAMETERS
+//============================================================
+
+// window styles
+#define WINDOW_STYLE WS_OVERLAPPEDWINDOW
+#define WINDOW_STYLE_EX 0
+
+// debugger window styles
+#define DEBUG_WINDOW_STYLE WS_OVERLAPPED
+#define DEBUG_WINDOW_STYLE_EX 0
+
+// full screen window styles
+#define FULLSCREEN_STYLE WS_POPUP
+#define FULLSCREEN_STYLE_EX WS_EX_TOPMOST
+
+// minimum window dimension
+#define MIN_WINDOW_DIM 200
+
+// custom window messages
+#define WM_USER_FINISH_CREATE_WINDOW (WM_USER + 0)
+#define WM_USER_SELF_TERMINATE (WM_USER + 1)
+#define WM_USER_REDRAW (WM_USER + 2)
+#define WM_USER_SET_FULLSCREEN (WM_USER + 3)
+#define WM_USER_SET_MAXSIZE (WM_USER + 4)
+#define WM_USER_SET_MINSIZE (WM_USER + 5)
+#define WM_USER_UI_TEMP_PAUSE (WM_USER + 6)
+#define WM_USER_EXEC_FUNC (WM_USER + 7)
+
+
+
+//============================================================
+// GLOBAL VARIABLES
+//============================================================
+
+win_window_info *win_window_list;
+static win_window_info **last_window_ptr;
+static DWORD main_threadid;
+
+// actual physical resolution
+static int win_physical_width;
+static int win_physical_height;
+
+
+
+//============================================================
+// LOCAL VARIABLES
+//============================================================
+
+// event handling
+static DWORD last_event_check;
+
+// debugger
+static int in_background;
+
+static int ui_temp_pause;
+static int ui_temp_was_paused;
+
+static int multithreading_enabled;
+
+static HANDLE window_thread;
+static DWORD window_threadid;
+
+static DWORD last_update_time;
+
+static osd_draw_callbacks draw;
+
+static HANDLE ui_pause_event;
+static HANDLE window_thread_ready_event;
+
+
+
+//============================================================
+// PROTOTYPES
+//============================================================
+
+
+static void create_window_class(void);
+
+// temporary hacks
+#if LOG_THREADS
+struct mtlog
+{
+ osd_ticks_t timestamp;
+ const char *event;
+};
+
+static mtlog mtlog[100000];
+static volatile LONG mtlogindex;
+
+void mtlog_add(const char *event)
+{
+ int index = atomic_increment32((LONG *) &mtlogindex) - 1;
+ if (index < ARRAY_LENGTH(mtlog))
+ {
+ mtlog[index].timestamp = osd_ticks();
+ mtlog[index].event = event;
+ }
+}
+
+static void mtlog_dump(void)
+{
+ osd_ticks_t cps = osd_ticks_per_second();
+ osd_ticks_t last = mtlog[0].timestamp * 1000000 / cps;
+ int i;
+
+ FILE *f = fopen("mt.log", "w");
+ for (i = 0; i < mtlogindex; i++)
+ {
+ osd_ticks_t curr = mtlog[i].timestamp * 1000000 / cps;
+ fprintf(f, "%20I64d %10I64d %s\n", curr, curr - last, mtlog[i].event);
+ last = curr;
+ }
+ fclose(f);
+}
+#else
+void mtlog_add(const char *event) { }
+static void mtlog_dump(void) { }
+#endif
+
+
+
+//============================================================
+// window_init
+// (main thread)
+//============================================================
+
+bool windows_osd_interface::window_init()
+{
+ size_t temp;
+
+ // determine if we are using multithreading or not
+ multithreading_enabled = downcast<windows_options &>(machine().options()).multithreading();
+
+ // get the main thread ID before anything else
+ main_threadid = GetCurrentThreadId();
+
+ // set up window class and register it
+ create_window_class();
+
+ // create an event to signal UI pausing
+ ui_pause_event = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (!ui_pause_event)
+ fatalerror("Failed to create pause event\n");
+
+ // if multithreading, create a thread to run the windows
+ if (multithreading_enabled)
+ {
+ // create an event to signal when the window thread is ready
+ window_thread_ready_event = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (!window_thread_ready_event)
+ fatalerror("Failed to create window thread ready event\n");
+
+ // create a thread to run the windows from
+ temp = _beginthreadex(NULL, 0, win_window_info::thread_entry, NULL, 0, (unsigned *)&window_threadid);
+ window_thread = (HANDLE)temp;
+ if (window_thread == NULL)
+ fatalerror("Failed to create window thread\n");
+
+ // set the thread priority equal to the main MAME thread
+ SetThreadPriority(window_thread, GetThreadPriority(GetCurrentThread()));
+ }
+
+ // otherwise, treat the window thread as the main thread
+ else
+ {
+ window_thread = GetCurrentThread();
+ window_threadid = main_threadid;
+ }
+
+ // initialize the drawers
+ if (video_config.mode == VIDEO_MODE_D3D)
+ {
+ if (drawd3d_init(machine(), &draw))
+ video_config.mode = VIDEO_MODE_GDI;
+ }
+ if (video_config.mode == VIDEO_MODE_DDRAW)
+ {
+ if (drawdd_init(machine(), &draw))
+ video_config.mode = VIDEO_MODE_GDI;
+ }
+ if (video_config.mode == VIDEO_MODE_GDI)
+ drawgdi_init(machine(), &draw);
+#if defined(USE_BGFX)
+ if (video_config.mode == VIDEO_MODE_BGFX)
+ drawbgfx_init(machine(), &draw);
+#endif
+ if (video_config.mode == VIDEO_MODE_NONE)
+ drawnone_init(machine(), &draw);
+#if (USE_OPENGL)
+ if (video_config.mode == VIDEO_MODE_OPENGL)
+ drawogl_init(machine(), &draw);
+#endif
+ // set up the window list
+ last_window_ptr = &win_window_list;
+
+ return true;
+}
+
+
+
+//============================================================
+// winwindow_exit
+// (main thread)
+//============================================================
+
+void windows_osd_interface::window_exit()
+{
+ assert(GetCurrentThreadId() == main_threadid);
+
+ // free all the windows
+ while (win_window_list != NULL)
+ {
+ win_window_info *temp = win_window_list;
+ win_window_list = temp->m_next;
+ temp->destroy();
+ global_free(temp);
+
+ }
+
+ // kill the drawers
+ (*draw.exit)();
+
+ // if we're multithreaded, clean up the window thread
+ if (multithreading_enabled)
+ {
+ PostThreadMessage(window_threadid, WM_USER_SELF_TERMINATE, 0, 0);
+ WaitForSingleObject(window_thread, INFINITE);
+
+ mtlog_dump();
+ }
+
+ // kill the UI pause event
+ if (ui_pause_event)
+ CloseHandle(ui_pause_event);
+
+ // kill the window thread ready event
+ if (window_thread_ready_event)
+ CloseHandle(window_thread_ready_event);
+
+ // if we hid the cursor during the emulation, show it
+ while (ShowCursor(TRUE) < 0) ;
+}
+
+
+win_window_info::win_window_info(running_machine &machine)
+ : osd_window(), m_next(NULL),
+ m_init_state(0),
+ m_startmaximized(0),
+ m_isminimized(0),
+ m_ismaximized(0),
+ m_monitor(NULL),
+ m_fullscreen(0),
+ m_fullscreen_safe(0),
+ m_aspect(0),
+ m_render_lock(NULL),
+ m_target(NULL),
+ m_targetview(0),
+ m_targetorient(0),
+ m_lastclicktime(0),
+ m_lastclickx(0),
+ m_lastclicky(0),
+ m_renderer(NULL),
+ m_machine(machine)
+{
+ memset(m_title,0,sizeof(m_title));
+ m_non_fullscreen_bounds.left = 0;
+ m_non_fullscreen_bounds.top = 0;
+ m_non_fullscreen_bounds.right = 0;
+ m_non_fullscreen_bounds.bottom = 0;
+ m_prescale = video_config.prescale;
+}
+
+win_window_info::~win_window_info()
+{
+}
+
+
+//============================================================
+// winwindow_process_events_periodic
+// (main thread)
+//============================================================
+
+void winwindow_process_events_periodic(running_machine &machine)
+{
+ DWORD currticks = GetTickCount();
+
+ assert(GetCurrentThreadId() == main_threadid);
+
+ // update once every 1/8th of a second
+ if (currticks - last_event_check < 1000 / 8)
+ return;
+ winwindow_process_events(machine, TRUE, FALSE);
+}
+
+
+
+//============================================================
+// is_mame_window
+//============================================================
+
+static BOOL is_mame_window(HWND hwnd)
+{
+ win_window_info *window;
+
+ for (window = win_window_list; window != NULL; window = window->m_next)
+ if (window->m_hwnd == hwnd)
+ return TRUE;
+
+ return FALSE;
+}
+
+
+
+//============================================================
+// winwindow_process_events
+// (main thread)
+//============================================================
+
+void winwindow_process_events(running_machine &machine, int ingame, bool nodispatch)
+{
+ MSG message;
+
+ assert(GetCurrentThreadId() == main_threadid);
+
+ // remember the last time we did this
+ last_event_check = GetTickCount();
+
+ do
+ {
+ // if we are paused, lets wait for a message
+ if (ui_temp_pause > 0)
+ WaitMessage();
+
+ // loop over all messages in the queue
+ while (PeekMessage(&message, NULL, 0, 0, PM_REMOVE))
+ {
+ // prevent debugger windows from getting messages during reset
+ int dispatch = TRUE && !nodispatch;
+
+ if (message.hwnd == NULL || is_mame_window(message.hwnd))
+ {
+ dispatch = TRUE;
+ switch (message.message)
+ {
+ // ignore keyboard messages
+ case WM_SYSKEYUP:
+ case WM_SYSKEYDOWN:
+ dispatch = FALSE;
+ break;
+
+ // forward mouse button downs to the input system
+ case WM_LBUTTONDOWN:
+ dispatch = !wininput_handle_mouse_button(0, TRUE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam));
+ break;
+
+ case WM_RBUTTONDOWN:
+ dispatch = !wininput_handle_mouse_button(1, TRUE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam));
+ break;
+
+ case WM_MBUTTONDOWN:
+ dispatch = !wininput_handle_mouse_button(2, TRUE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam));
+ break;
+
+ case WM_XBUTTONDOWN:
+ dispatch = !wininput_handle_mouse_button(3, TRUE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam));
+ break;
+
+ // forward mouse button ups to the input system
+ case WM_LBUTTONUP:
+ dispatch = !wininput_handle_mouse_button(0, FALSE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam));
+ break;
+
+ case WM_RBUTTONUP:
+ dispatch = !wininput_handle_mouse_button(1, FALSE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam));
+ break;
+
+ case WM_MBUTTONUP:
+ dispatch = !wininput_handle_mouse_button(2, FALSE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam));
+ break;
+
+ case WM_XBUTTONUP:
+ dispatch = !wininput_handle_mouse_button(3, FALSE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam));
+ break;
+ }
+ }
+
+ // dispatch if necessary
+ if (dispatch)
+ winwindow_dispatch_message(machine, &message);
+ }
+ } while (ui_temp_pause > 0);
+
+ // update the cursor state after processing events
+ winwindow_update_cursor_state(machine);
+}
+
+
+
+//============================================================
+// winwindow_dispatch_message
+// (main thread)
+//============================================================
+
+void winwindow_dispatch_message(running_machine &machine, MSG *message)
+{
+ assert(GetCurrentThreadId() == main_threadid);
+
+ // dispatch our special communication messages
+ switch (message->message)
+ {
+ // special case for quit
+ case WM_QUIT:
+ machine.schedule_exit();
+ break;
+
+ // temporary pause from the window thread
+ case WM_USER_UI_TEMP_PAUSE:
+ winwindow_ui_pause_from_main_thread(machine, message->wParam);
+ break;
+
+ // execute arbitrary function
+ case WM_USER_EXEC_FUNC:
+ {
+ void (*func)(void *) = (void (*)(void *)) message->wParam;
+ void *param = (void *) message->lParam;
+ func(param);
+ }
+ break;
+
+ // everything else dispatches normally
+ default:
+ TranslateMessage(message);
+ DispatchMessage(message);
+ break;
+ }
+}
+
+
+
+//============================================================
+// winwindow_take_snap
+// (main thread)
+//============================================================
+
+void winwindow_take_snap(void)
+{
+ win_window_info *window;
+
+ assert(GetCurrentThreadId() == main_threadid);
+
+ // iterate over windows and request a snap
+ for (window = win_window_list; window != NULL; window = window->m_next)
+ {
+ window->m_renderer->save();
+ }
+}
+
+
+
+//============================================================
+// winwindow_toggle_fsfx
+// (main thread)
+//============================================================
+
+void winwindow_toggle_fsfx(void)
+{
+ win_window_info *window;
+
+ assert(GetCurrentThreadId() == main_threadid);
+
+ // iterate over windows and request a snap
+ for (window = win_window_list; window != NULL; window = window->m_next)
+ {
+ window->m_renderer->toggle_fsfx();
+ }
+}
+
+
+
+//============================================================
+// winwindow_take_video
+// (main thread)
+//============================================================
+
+void winwindow_take_video(void)
+{
+ win_window_info *window;
+
+ assert(GetCurrentThreadId() == main_threadid);
+
+ // iterate over windows and request a snap
+ for (window = win_window_list; window != NULL; window = window->m_next)
+ {
+ window->m_renderer->record();
+ }
+}
+
+
+
+//============================================================
+// winwindow_toggle_full_screen
+// (main thread)
+//============================================================
+
+void winwindow_toggle_full_screen(void)
+{
+ win_window_info *window;
+
+ assert(GetCurrentThreadId() == main_threadid);
+
+ // if we are in debug mode, never go full screen
+ for (window = win_window_list; window != NULL; window = window->m_next)
+ if (window->machine().debug_flags & DEBUG_FLAG_OSD_ENABLED)
+ return;
+
+ // toggle the window mode
+ video_config.windowed = !video_config.windowed;
+
+ // iterate over windows and toggle their fullscreen state
+ for (window = win_window_list; window != NULL; window = window->m_next)
+ SendMessage(window->m_hwnd, WM_USER_SET_FULLSCREEN, !video_config.windowed, 0);
+ SetForegroundWindow(win_window_list->m_hwnd);
+}
+
+
+
+//============================================================
+// winwindow_has_focus
+// (main or window thread)
+//============================================================
+
+BOOL winwindow_has_focus(void)
+{
+ HWND focuswnd = GetFocus();
+ win_window_info *window;
+
+ // see if one of the video windows has focus
+ for (window = win_window_list; window != NULL; window = window->m_next)
+ if (focuswnd == window->m_hwnd)
+ return TRUE;
+
+ return FALSE;
+}
+
+
+//============================================================
+// winwindow_update_cursor_state
+// (main thread)
+//============================================================
+
+void winwindow_update_cursor_state(running_machine &machine)
+{
+ static POINT saved_cursor_pos = { -1, -1 };
+
+ assert(GetCurrentThreadId() == main_threadid);
+
+ // if we should hide the mouse cursor, then do it
+ // rules are:
+ // 1. we must have focus before hiding the cursor
+ // 2. we also hide the cursor in full screen mode and when the window doesn't have a menu
+ // 3. we also hide the cursor in windowed mode if we're not paused and
+ // the input system requests it
+ if (winwindow_has_focus() && ((!video_config.windowed && !win_window_list->win_has_menu()) || (!machine.paused() && wininput_should_hide_mouse())))
+ {
+ win_window_info *window = win_window_list;
+ RECT bounds;
+
+ // hide cursor
+ while (ShowCursor(FALSE) >= -1) ;
+ ShowCursor(TRUE);
+
+ // store the cursor position
+ GetCursorPos(&saved_cursor_pos);
+
+ // clip cursor to game video window
+ GetClientRect(window->m_hwnd, &bounds);
+ ClientToScreen(window->m_hwnd, &((POINT *)&bounds)[0]);
+ ClientToScreen(window->m_hwnd, &((POINT *)&bounds)[1]);
+ ClipCursor(&bounds);
+ }
+ else
+ {
+ // show cursor
+ while (ShowCursor(TRUE) < 1) ;
+ ShowCursor(FALSE);
+
+ // allow cursor to move freely
+ ClipCursor(NULL);
+ if (saved_cursor_pos.x != -1 || saved_cursor_pos.y != -1)
+ {
+ SetCursorPos(saved_cursor_pos.x, saved_cursor_pos.y);
+ saved_cursor_pos.x = saved_cursor_pos.y = -1;
+ }
+ }
+}
+
+
+
+//============================================================
+// winwindow_video_window_create
+// (main thread)
+//============================================================
+
+void win_window_info::create(running_machine &machine, int index, osd_monitor_info *monitor, const osd_window_config *config)
+{
+ win_window_info *window, *win;
+
+ assert(GetCurrentThreadId() == main_threadid);
+
+ // allocate a new window object
+ window = global_alloc(win_window_info(machine));
+ //printf("%d, %d\n", config->width, config->height);
+ window->m_win_config = *config;
+ window->m_monitor = monitor;
+ window->m_fullscreen = !video_config.windowed;
+
+ // see if we are safe for fullscreen
+ window->m_fullscreen_safe = TRUE;
+ for (win = win_window_list; win != NULL; win = win->m_next)
+ if (win->m_monitor == monitor)
+ window->m_fullscreen_safe = FALSE;
+
+ // add us to the list
+ *last_window_ptr = window;
+ last_window_ptr = &window->m_next;
+
+ // create a lock that we can use to skip blitting
+ window->m_render_lock = osd_lock_alloc();
+
+ // load the layout
+ window->m_target = machine.render().target_alloc();
+
+ // set the specific view
+ windows_options &options = downcast<windows_options &>(machine.options());
+
+ const char *defview = options.view();
+ window->set_starting_view(index, defview, options.view(index));
+
+ // remember the current values in case they change
+ window->m_targetview = window->m_target->view();
+ window->m_targetorient = window->m_target->orientation();
+ window->m_targetlayerconfig = window->m_target->layer_config();
+
+ // make the window title
+ if (video_config.numscreens == 1)
+ sprintf(window->m_title, "%s: %s [%s]", emulator_info::get_appname(), machine.system().description, machine.system().name);
+ else
+ sprintf(window->m_title, "%s: %s [%s] - Screen %d", emulator_info::get_appname(), machine.system().description, machine.system().name, index);
+
+ // set the initial maximized state
+ window->m_startmaximized = options.maximize();
+
+ // finish the window creation on the window thread
+ if (multithreading_enabled)
+ {
+ // wait until the window thread is ready to respond to events
+ WaitForSingleObject(window_thread_ready_event, INFINITE);
+
+ PostThreadMessage(window_threadid, WM_USER_FINISH_CREATE_WINDOW, 0, (LPARAM)window);
+ while (window->m_init_state == 0)
+ {
+ winwindow_process_events(machine, 0, 1); //pump the message queue
+ Sleep(1);
+ }
+ }
+ else
+ window->m_init_state = window->complete_create() ? -1 : 1;
+
+ // handle error conditions
+ if (window->m_init_state == -1)
+ fatalerror("Unable to complete window creation\n");
+}
+
+
+
+//============================================================
+// winwindow_video_window_destroy
+// (main thread)
+//============================================================
+
+void win_window_info::destroy()
+{
+ win_window_info **prevptr;
+
+ assert(GetCurrentThreadId() == main_threadid);
+
+ // remove us from the list
+ for (prevptr = &win_window_list; *prevptr != NULL; prevptr = &(*prevptr)->m_next)
+ if (*prevptr == this)
+ {
+ *prevptr = this->m_next;
+ break;
+ }
+
+ // destroy the window
+ if (m_hwnd != NULL)
+ SendMessage(m_hwnd, WM_USER_SELF_TERMINATE, 0, 0);
+
+ // free the render target
+ machine().render().target_free(m_target);
+
+ // FIXME: move to destructor
+ // free the lock
+ osd_lock_free(m_render_lock);
+
+}
+
+
+
+//============================================================
+// winwindow_video_window_update
+// (main thread)
+//============================================================
+
+void win_window_info::update()
+{
+ int targetview, targetorient;
+ render_layer_config targetlayerconfig;
+
+ assert(GetCurrentThreadId() == main_threadid);
+
+ mtlog_add("winwindow_video_window_update: begin");
+
+ // see if the target has changed significantly in window mode
+ targetview = m_target->view();
+ targetorient = m_target->orientation();
+ targetlayerconfig = m_target->layer_config();
+ if (targetview != m_targetview || targetorient != m_targetorient || targetlayerconfig != m_targetlayerconfig)
+ {
+ m_targetview = targetview;
+ m_targetorient = targetorient;
+ m_targetlayerconfig = targetlayerconfig;
+
+ // in window mode, reminimize/maximize
+ if (!m_fullscreen)
+ {
+ if (m_isminimized)
+ SendMessage(m_hwnd, WM_USER_SET_MINSIZE, 0, 0);
+ if (m_ismaximized)
+ SendMessage(m_hwnd, WM_USER_SET_MAXSIZE, 0, 0);
+ }
+ }
+
+ // if we're visible and running and not in the middle of a resize, draw
+ if (m_hwnd != NULL && m_target != NULL && m_renderer != NULL)
+ {
+ int got_lock = TRUE;
+
+ mtlog_add("winwindow_video_window_update: try lock");
+
+ // only block if we're throttled
+ if (machine().video().throttled() || timeGetTime() - last_update_time > 250)
+ osd_lock_acquire(m_render_lock);
+ else
+ got_lock = osd_lock_try(m_render_lock);
+
+ // only render if we were able to get the lock
+ if (got_lock)
+ {
+ render_primitive_list *primlist;
+
+ mtlog_add("winwindow_video_window_update: got lock");
+
+ // don't hold the lock; we just used it to see if rendering was still happening
+ osd_lock_release(m_render_lock);
+
+ // ensure the target bounds are up-to-date, and then get the primitives
+ primlist = m_renderer->get_primitives();
+
+ // post a redraw request with the primitive list as a parameter
+ last_update_time = timeGetTime();
+ mtlog_add("winwindow_video_window_update: PostMessage start");
+ if (multithreading_enabled)
+ PostMessage(m_hwnd, WM_USER_REDRAW, 0, (LPARAM)primlist);
+ else
+ SendMessage(m_hwnd, WM_USER_REDRAW, 0, (LPARAM)primlist);
+ mtlog_add("winwindow_video_window_update: PostMessage end");
+ }
+ }
+
+ mtlog_add("winwindow_video_window_update: end");
+}
+
+
+
+//============================================================
+// winwindow_video_window_monitor
+// (window thread)
+//============================================================
+
+osd_monitor_info *win_window_info::winwindow_video_window_monitor(const osd_rect *proposed)
+{
+ osd_monitor_info *monitor;
+
+ // in window mode, find the nearest
+ if (!m_fullscreen)
+ {
+ if (proposed != NULL)
+ {
+ RECT p;
+ p.top = proposed->top();
+ p.left = proposed->left();
+ p.bottom = proposed->bottom();
+ p.right = proposed->right();
+ monitor = win_monitor_info::monitor_from_handle(MonitorFromRect(&p, MONITOR_DEFAULTTONEAREST));
+ }
+ else
+ monitor = win_monitor_info::monitor_from_handle(MonitorFromWindow(m_hwnd, MONITOR_DEFAULTTONEAREST));
+ }
+
+ // in full screen, just use the configured monitor
+ else
+ monitor = m_monitor;
+
+ // make sure we're up-to-date
+ //monitor->refresh();
+ return monitor;
+}
+
+
+
+//============================================================
+// create_window_class
+// (main thread)
+//============================================================
+
+static void create_window_class(void)
+{
+ static int classes_created = FALSE;
+
+ assert(GetCurrentThreadId() == main_threadid);
+
+ if (!classes_created)
+ {
+ WNDCLASS wc = { 0 };
+
+ // initialize the description of the window class
+ wc.lpszClassName = TEXT("MAME");
+ wc.hInstance = GetModuleHandleUni();
+ wc.lpfnWndProc = winwindow_video_window_proc_ui;
+ wc.hCursor = LoadCursor(NULL, IDC_ARROW);
+ wc.hIcon = LoadIcon(wc.hInstance, MAKEINTRESOURCE(2));
+
+ UnregisterClass(wc.lpszClassName, wc.hInstance);
+
+ // register the class; fail if we can't
+ if (!RegisterClass(&wc))
+ fatalerror("Failed to create window class\n");
+ classes_created = TRUE;
+ }
+}
+
+
+
+//============================================================
+// set_starting_view
+// (main thread)
+//============================================================
+
+void win_window_info::set_starting_view(int index, const char *defview, const char *view)
+{
+ int viewindex;
+
+ assert(GetCurrentThreadId() == main_threadid);
+
+ // choose non-auto over auto
+ if (strcmp(view, "auto") == 0 && strcmp(defview, "auto") != 0)
+ view = defview;
+
+ // query the video system to help us pick a view
+ viewindex = target()->configured_view(view, index, video_config.numscreens);
+
+ // set the view
+ target()->set_view(viewindex);
+}
+
+
+//============================================================
+// winwindow_ui_pause_from_main_thread
+// (main thread)
+//============================================================
+
+void winwindow_ui_pause_from_main_thread(running_machine &machine, int pause)
+{
+ int old_temp_pause = ui_temp_pause;
+
+ assert(GetCurrentThreadId() == main_threadid);
+
+ // if we're pausing, increment the pause counter
+ if (pause)
+ {
+ // if we're the first to pause, we have to actually initiate it
+ if (ui_temp_pause++ == 0)
+ {
+ // only call mame_pause if we weren't already paused due to some external reason
+ ui_temp_was_paused = machine.paused();
+ if (!ui_temp_was_paused)
+ machine.pause();
+
+ SetEvent(ui_pause_event);
+ }
+ }
+
+ // if we're resuming, decrement the pause counter
+ else
+ {
+ // if we're the last to resume, unpause MAME
+ if (--ui_temp_pause == 0)
+ {
+ // but only do it if we were the ones who initiated it
+ if (!ui_temp_was_paused)
+ machine.resume();
+
+ ResetEvent(ui_pause_event);
+ }
+ }
+
+ if (LOG_TEMP_PAUSE)
+ machine.logerror("winwindow_ui_pause_from_main_thread(): %d --> %d\n", old_temp_pause, ui_temp_pause);
+}
+
+
+
+//============================================================
+// winwindow_ui_pause_from_window_thread
+// (window thread)
+//============================================================
+
+void winwindow_ui_pause_from_window_thread(running_machine &machine, int pause)
+{
+ assert(GetCurrentThreadId() == window_threadid);
+
+ // if we're multithreaded, we have to request a pause on the main thread
+ if (multithreading_enabled)
+ {
+ // request a pause from the main thread
+ PostThreadMessage(main_threadid, WM_USER_UI_TEMP_PAUSE, pause, 0);
+
+ // if we're pausing, block until it happens
+ if (pause)
+ WaitForSingleObject(ui_pause_event, INFINITE);
+ }
+
+ // otherwise, we just do it directly
+ else
+ winwindow_ui_pause_from_main_thread(machine, pause);
+}
+
+
+
+//============================================================
+// winwindow_ui_exec_on_main_thread
+// (window thread)
+//============================================================
+
+void winwindow_ui_exec_on_main_thread(void (*func)(void *), void *param)
+{
+ assert(GetCurrentThreadId() == window_threadid);
+
+ // if we're multithreaded, we have to request a pause on the main thread
+ if (multithreading_enabled)
+ {
+ // request a pause from the main thread
+ PostThreadMessage(main_threadid, WM_USER_EXEC_FUNC, (WPARAM) func, (LPARAM) param);
+ }
+
+ // otherwise, we just do it directly
+ else
+ (*func)(param);
+}
+
+
+
+//============================================================
+// winwindow_ui_is_paused
+//============================================================
+
+int winwindow_ui_is_paused(running_machine &machine)
+{
+ return machine.paused() && ui_temp_was_paused;
+}
+
+
+
+//============================================================
+// wnd_extra_width
+// (window thread)
+//============================================================
+
+int win_window_info::wnd_extra_width()
+{
+ RECT temprect = { 100, 100, 200, 200 };
+ if (m_fullscreen)
+ return 0;
+ AdjustWindowRectEx(&temprect, WINDOW_STYLE, win_has_menu(), WINDOW_STYLE_EX);
+ return rect_width(&temprect) - 100;
+}
+
+
+
+//============================================================
+// wnd_extra_height
+// (window thread)
+//============================================================
+
+int win_window_info::wnd_extra_height()
+{
+ RECT temprect = { 100, 100, 200, 200 };
+ if (m_fullscreen)
+ return 0;
+ AdjustWindowRectEx(&temprect, WINDOW_STYLE, win_has_menu(), WINDOW_STYLE_EX);
+ return rect_height(&temprect) - 100;
+}
+
+
+
+//============================================================
+// thread_entry
+// (window thread)
+//============================================================
+
+unsigned __stdcall win_window_info::thread_entry(void *param)
+{
+ MSG message;
+
+ // make a bogus user call to make us a message thread
+ PeekMessage(&message, NULL, 0, 0, PM_NOREMOVE);
+
+ // attach our input to the main thread
+ AttachThreadInput(main_threadid, window_threadid, TRUE);
+
+ // signal to the main thread that we are ready to receive events
+ SetEvent(window_thread_ready_event);
+
+ // run the message pump
+ while (GetMessage(&message, NULL, 0, 0))
+ {
+ int dispatch = TRUE;
+
+ if ((message.hwnd == NULL) || is_mame_window(message.hwnd))
+ {
+ switch (message.message)
+ {
+ // ignore input messages here
+ case WM_SYSKEYUP:
+ case WM_SYSKEYDOWN:
+ dispatch = FALSE;
+ break;
+
+ // forward mouse button downs to the input system
+ case WM_LBUTTONDOWN:
+ dispatch = !wininput_handle_mouse_button(0, TRUE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam));
+ break;
+
+ case WM_RBUTTONDOWN:
+ dispatch = !wininput_handle_mouse_button(1, TRUE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam));
+ break;
+
+ case WM_MBUTTONDOWN:
+ dispatch = !wininput_handle_mouse_button(2, TRUE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam));
+ break;
+
+ case WM_XBUTTONDOWN:
+ dispatch = !wininput_handle_mouse_button(3, TRUE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam));
+ break;
+
+ // forward mouse button ups to the input system
+ case WM_LBUTTONUP:
+ dispatch = !wininput_handle_mouse_button(0, FALSE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam));
+ break;
+
+ case WM_RBUTTONUP:
+ dispatch = !wininput_handle_mouse_button(1, FALSE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam));
+ break;
+
+ case WM_MBUTTONUP:
+ dispatch = !wininput_handle_mouse_button(2, FALSE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam));
+ break;
+
+ case WM_XBUTTONUP:
+ dispatch = !wininput_handle_mouse_button(3, FALSE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam));
+ break;
+
+ // a terminate message to the thread posts a quit
+ case WM_USER_SELF_TERMINATE:
+ PostQuitMessage(0);
+ dispatch = FALSE;
+ break;
+
+ // handle the "complete create" message
+ case WM_USER_FINISH_CREATE_WINDOW:
+ {
+ win_window_info *window = (win_window_info *)message.lParam;
+ window->m_init_state = window->complete_create() ? -1 : 1;
+ dispatch = FALSE;
+ break;
+ }
+ }
+ }
+
+ // dispatch if necessary
+ if (dispatch)
+ {
+ TranslateMessage(&message);
+ DispatchMessage(&message);
+ }
+ }
+ return 0;
+}
+
+
+
+//============================================================
+// complete_create
+// (window thread)
+//============================================================
+
+int win_window_info::complete_create()
+{
+ RECT client;
+ int tempwidth, tempheight;
+ HMENU menu = NULL;
+ HDC dc;
+
+ assert(GetCurrentThreadId() == window_threadid);
+
+ // get the monitor bounds
+ osd_rect monitorbounds = m_monitor->position_size();
+
+ // create the window menu if needed
+ if (downcast<windows_options &>(machine().options()).menu())
+ {
+ if (win_create_menu(machine(), &menu))
+ return 1;
+ }
+
+ // create the window, but don't show it yet
+ m_hwnd = win_create_window_ex_utf8(
+ m_fullscreen ? FULLSCREEN_STYLE_EX : WINDOW_STYLE_EX,
+ "MAME",
+ m_title,
+ m_fullscreen ? FULLSCREEN_STYLE : WINDOW_STYLE,
+ monitorbounds.left() + 20, monitorbounds.top() + 20,
+ monitorbounds.left() + 100, monitorbounds.top() + 100,
+ NULL,//(win_window_list != NULL) ? win_window_list->m_hwnd : NULL,
+ menu,
+ GetModuleHandleUni(),
+ NULL);
+ if (m_hwnd == NULL)
+ return 1;
+
+ // set window #0 as the focus window for all windows, required for D3D & multimonitor
+ m_focus_hwnd = win_window_list->m_hwnd;
+
+ // set a pointer back to us
+ SetWindowLongPtr(m_hwnd, GWLP_USERDATA, (LONG_PTR)this);
+
+ // skip the positioning stuff for -video none */
+ if (video_config.mode == VIDEO_MODE_NONE)
+ return 0;
+
+ // adjust the window position to the initial width/height
+ tempwidth = (m_win_config.width != 0) ? m_win_config.width : 640;
+ tempheight = (m_win_config.height != 0) ? m_win_config.height : 480;
+ SetWindowPos(m_hwnd, NULL, monitorbounds.left() + 20, monitorbounds.top() + 20,
+ monitorbounds.left() + tempwidth + wnd_extra_width(),
+ monitorbounds.top() + tempheight + wnd_extra_height(),
+ SWP_NOZORDER);
+
+ // maximum or minimize as appropriate
+ if (m_startmaximized)
+ maximize_window();
+ else
+ minimize_window();
+ adjust_window_position_after_major_change();
+
+ // show the window
+ if (!m_fullscreen || m_fullscreen_safe)
+ {
+ // finish off by trying to initialize DirectX; if we fail, ignore it
+ m_renderer = draw.create(this);
+ if (m_renderer->create())
+ return 1;
+ ShowWindow(m_hwnd, SW_SHOW);
+ }
+
+ // clear the window
+ dc = GetDC(m_hwnd);
+ GetClientRect(m_hwnd, &client);
+ FillRect(dc, &client, (HBRUSH)GetStockObject(BLACK_BRUSH));
+ ReleaseDC(m_hwnd, dc);
+ return 0;
+}
+
+
+
+//============================================================
+// winwindow_video_window_proc
+// (window thread)
+//============================================================
+
+LRESULT CALLBACK win_window_info::video_window_proc(HWND wnd, UINT message, WPARAM wparam, LPARAM lparam)
+{
+ LONG_PTR ptr = GetWindowLongPtr(wnd, GWLP_USERDATA);
+ win_window_info *window = (win_window_info *)ptr;
+
+ // we may get called before SetWindowLongPtr is called
+ if (window != NULL)
+ {
+ assert(GetCurrentThreadId() == window_threadid);
+ window->update_minmax_state();
+ }
+
+ // handle a few messages
+ switch (message)
+ {
+ // paint: redraw the last bitmap
+ case WM_PAINT:
+ {
+ PAINTSTRUCT pstruct;
+ HDC hdc = BeginPaint(wnd, &pstruct);
+ window->draw_video_contents(hdc, TRUE);
+ if (window->win_has_menu())
+ DrawMenuBar(window->m_hwnd);
+ EndPaint(wnd, &pstruct);
+ break;
+ }
+
+ // non-client paint: punt if full screen
+ case WM_NCPAINT:
+ if (!window->m_fullscreen || window->win_has_menu())
+ return DefWindowProc(wnd, message, wparam, lparam);
+ break;
+
+ // input: handle the raw input
+ case WM_INPUT:
+ wininput_handle_raw((HRAWINPUT)lparam);
+ break;
+
+ // syskeys - ignore
+ case WM_SYSKEYUP:
+ case WM_SYSKEYDOWN:
+ break;
+
+ // input events
+ case WM_MOUSEMOVE:
+ ui_input_push_mouse_move_event(window->machine(), window->m_target, GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam));
+ break;
+
+ case WM_MOUSELEAVE:
+ ui_input_push_mouse_leave_event(window->machine(), window->m_target);
+ break;
+
+ case WM_LBUTTONDOWN:
+ {
+ DWORD ticks = GetTickCount();
+ ui_input_push_mouse_down_event(window->machine(), window->m_target, GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam));
+
+ // check for a double-click
+ if (ticks - window->m_lastclicktime < GetDoubleClickTime() &&
+ GET_X_LPARAM(lparam) >= window->m_lastclickx - 4 && GET_X_LPARAM(lparam) <= window->m_lastclickx + 4 &&
+ GET_Y_LPARAM(lparam) >= window->m_lastclicky - 4 && GET_Y_LPARAM(lparam) <= window->m_lastclicky + 4)
+ {
+ window->m_lastclicktime = 0;
+ ui_input_push_mouse_double_click_event(window->machine(), window->m_target, GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam));
+ }
+ else
+ {
+ window->m_lastclicktime = ticks;
+ window->m_lastclickx = GET_X_LPARAM(lparam);
+ window->m_lastclicky = GET_Y_LPARAM(lparam);
+ }
+ break;
+ }
+
+ case WM_LBUTTONUP:
+ ui_input_push_mouse_up_event(window->machine(), window->m_target, GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam));
+ break;
+
+ case WM_CHAR:
+ ui_input_push_char_event(window->machine(), window->m_target, (unicode_char) wparam);
+ break;
+
+ // pause the system when we start a menu or resize
+ case WM_ENTERSIZEMOVE:
+ window->m_resize_state = RESIZE_STATE_RESIZING;
+ case WM_ENTERMENULOOP:
+ winwindow_ui_pause_from_window_thread(window->machine(), TRUE);
+ break;
+
+ // unpause the system when we stop a menu or resize and force a redraw
+ case WM_EXITSIZEMOVE:
+ window->m_resize_state = RESIZE_STATE_PENDING;
+ case WM_EXITMENULOOP:
+ winwindow_ui_pause_from_window_thread(window->machine(), FALSE);
+ InvalidateRect(wnd, NULL, FALSE);
+ break;
+
+ // get min/max info: set the minimum window size
+ case WM_GETMINMAXINFO:
+ {
+ MINMAXINFO *minmax = (MINMAXINFO *)lparam;
+ minmax->ptMinTrackSize.x = MIN_WINDOW_DIM;
+ minmax->ptMinTrackSize.y = MIN_WINDOW_DIM;
+ break;
+ }
+
+ // sizing: constrain to the aspect ratio unless control key is held down
+ case WM_SIZING:
+ {
+ RECT *rect = (RECT *)lparam;
+ if (video_config.keepaspect && !(GetAsyncKeyState(VK_CONTROL) & 0x8000))
+ {
+ osd_rect r = window->constrain_to_aspect_ratio(RECT_to_osd_rect(*rect), wparam);
+ rect->top = r.top();
+ rect->left = r.left();
+ rect->bottom = r.bottom();
+ rect->right = r.right();
+ }
+ InvalidateRect(wnd, NULL, FALSE);
+ break;
+ }
+
+ // syscommands: catch win_start_maximized
+ case WM_SYSCOMMAND:
+ {
+ UINT16 cmd = wparam & 0xfff0;
+
+ // prevent screensaver or monitor power events
+ if (cmd == SC_MONITORPOWER || cmd == SC_SCREENSAVE)
+ return 1;
+
+ // most SYSCOMMANDs require us to invalidate the window
+ InvalidateRect(wnd, NULL, FALSE);
+
+ // handle maximize
+ if (cmd == SC_MAXIMIZE)
+ {
+ window->update_minmax_state();
+ if (window->m_ismaximized)
+ window->minimize_window();
+ else
+ window->maximize_window();
+ break;
+ }
+ return DefWindowProc(wnd, message, wparam, lparam);
+ }
+
+ // track whether we are in the foreground
+ case WM_ACTIVATEAPP:
+ in_background = !wparam;
+ break;
+
+ // close: cause MAME to exit
+ case WM_CLOSE:
+ if (multithreading_enabled)
+ PostThreadMessage(main_threadid, WM_QUIT, 0, 0);
+ else
+ window->machine().schedule_exit();
+ break;
+
+ // destroy: clean up all attached rendering bits and NULL out our hwnd
+ case WM_DESTROY:
+ if (!(window->m_renderer == NULL))
+ {
+ window->m_renderer->destroy();
+ global_free(window->m_renderer);
+ window->m_renderer = NULL;
+ }
+ window->m_hwnd = NULL;
+ return DefWindowProc(wnd, message, wparam, lparam);
+
+ // self redraw: draw ourself in a non-painty way
+ case WM_USER_REDRAW:
+ {
+ HDC hdc = GetDC(wnd);
+
+ mtlog_add("winwindow_video_window_proc: WM_USER_REDRAW begin");
+ window->m_primlist = (render_primitive_list *)lparam;
+ window->draw_video_contents(hdc, FALSE);
+ mtlog_add("winwindow_video_window_proc: WM_USER_REDRAW end");
+
+ ReleaseDC(wnd, hdc);
+ break;
+ }
+
+ // self destruct
+ case WM_USER_SELF_TERMINATE:
+ DestroyWindow(window->m_hwnd);
+ break;
+
+ // fullscreen set
+ case WM_USER_SET_FULLSCREEN:
+ window->set_fullscreen(wparam);
+ break;
+
+ // minimum size set
+ case WM_USER_SET_MINSIZE:
+ window->minimize_window();
+ break;
+
+ // maximum size set
+ case WM_USER_SET_MAXSIZE:
+ window->maximize_window();
+ break;
+
+ // maximum size set
+ case WM_DISPLAYCHANGE:
+ /* FIXME: The current codebase has an issue with setting aspect
+ * ratios correctly after display change. set_aspect should
+ * be set_forced_aspect and on a refresh this forced aspect should
+ * be preserved if set. If not, the standard aspect calculation
+ * should be used.
+ */
+ window->m_monitor->refresh();
+ break;
+
+ // set focus: if we're not the primary window, switch back
+ // commented out ATM because this prevents us from resizing secondary windows
+// case WM_SETFOCUS:
+// if (window != win_window_list && win_window_list != NULL)
+// SetFocus(win_window_list->m_hwnd);
+// break;
+
+ // everything else: defaults
+ default:
+ return DefWindowProc(wnd, message, wparam, lparam);
+ }
+
+ return 0;
+}
+
+
+
+//============================================================
+// draw_video_contents
+// (window thread)
+//============================================================
+
+void win_window_info::draw_video_contents(HDC dc, int update)
+{
+ assert(GetCurrentThreadId() == window_threadid);
+
+ mtlog_add("draw_video_contents: begin");
+
+ mtlog_add("draw_video_contents: render lock acquire");
+ osd_lock_acquire(m_render_lock);
+ mtlog_add("draw_video_contents: render lock acquired");
+
+ // if we're iconic, don't bother
+ if (m_hwnd != NULL && !IsIconic(m_hwnd))
+ {
+ // if no bitmap, just fill
+ if (m_primlist == NULL)
+ {
+ RECT fill;
+ GetClientRect(m_hwnd, &fill);
+ FillRect(dc, &fill, (HBRUSH)GetStockObject(BLACK_BRUSH));
+ }
+
+ // otherwise, render with our drawing system
+ else
+ {
+ // update DC
+ m_dc = dc;
+ m_renderer->draw(update);
+ mtlog_add("draw_video_contents: drawing finished");
+ }
+ }
+
+ osd_lock_release(m_render_lock);
+ mtlog_add("draw_video_contents: render lock released");
+
+ mtlog_add("draw_video_contents: end");
+}
+
+
+
+//============================================================
+// constrain_to_aspect_ratio
+// (window thread)
+//============================================================
+
+osd_rect win_window_info::constrain_to_aspect_ratio(const osd_rect &rect, int adjustment)
+{
+ INT32 extrawidth = wnd_extra_width();
+ INT32 extraheight = wnd_extra_height();
+ INT32 propwidth, propheight;
+ INT32 minwidth, minheight;
+ INT32 maxwidth, maxheight;
+ INT32 viswidth, visheight;
+ INT32 adjwidth, adjheight;
+ float pixel_aspect;
+ osd_monitor_info *monitor = winwindow_video_window_monitor(&rect);
+
+ assert(GetCurrentThreadId() == window_threadid);
+
+ // get the pixel aspect ratio for the target monitor
+ pixel_aspect = monitor->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:
+ m_target->compute_visible_area(10000, propheight, pixel_aspect, m_target->orientation(), propwidth, propheight);
+ break;
+
+ case WMSZ_LEFT:
+ case WMSZ_RIGHT:
+ m_target->compute_visible_area(propwidth, 10000, pixel_aspect, m_target->orientation(), propwidth, propheight);
+ break;
+
+ default:
+ m_target->compute_visible_area(propwidth, propheight, pixel_aspect, m_target->orientation(), propwidth, propheight);
+ break;
+ }
+
+ // get the minimum width/height for the current layout
+ m_target->compute_minimum_size(minwidth, minheight);
+
+ // clamp against the absolute minimum
+ propwidth = MAX(propwidth, MIN_WINDOW_DIM);
+ propheight = MAX(propheight, MIN_WINDOW_DIM);
+
+ // clamp against the minimum width and height
+ propwidth = MAX(propwidth, minwidth);
+ propheight = 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 = MIN(maxwidth, m_win_config.width + extrawidth);
+ if (m_win_config.height != 0)
+ maxheight = MIN(maxheight, m_win_config.height + extraheight);
+ }
+
+ // clamp to the maximum
+ propwidth = MIN(propwidth, maxwidth);
+ propheight = MIN(propheight, maxheight);
+
+ // compute the visible area based on the proposed rectangle
+ m_target->compute_visible_area(propwidth, propheight, pixel_aspect, m_target->orientation(), viswidth, visheight);
+
+ // 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 win_window_info::get_min_bounds(int constrain)
+{
+ INT32 minwidth, minheight;
+
+ //assert(GetCurrentThreadId() == window_threadid);
+
+ // get the minimum target size
+ m_target->compute_minimum_size(minwidth, 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();
+ }
+ }
+
+ return osd_dim(minwidth, minheight);
+}
+
+
+
+//============================================================
+// get_max_bounds
+// (window thread)
+//============================================================
+
+osd_dim win_window_info::get_max_bounds(int constrain)
+{
+ //assert(GetCurrentThreadId() == window_threadid);
+
+ // compute the maximum client area
+ //m_monitor->refresh();
+ osd_rect maximum = m_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);
+ else
+ {
+ maximum = maximum.resize(maximum.width() - wnd_extra_width(), maximum.height() - wnd_extra_height());
+ }
+ return maximum.dim();
+}
+
+
+
+//============================================================
+// update_minmax_state
+// (window thread)
+//============================================================
+
+void win_window_info::update_minmax_state()
+{
+ assert(GetCurrentThreadId() == window_threadid);
+
+ if (!m_fullscreen)
+ {
+ RECT bounds;
+
+ // compare the maximum bounds versus the current bounds
+ osd_dim minbounds = get_min_bounds(video_config.keepaspect);
+ osd_dim maxbounds = get_max_bounds(video_config.keepaspect);
+ GetWindowRect(m_hwnd, &bounds);
+
+ // if either the width or height matches, we were maximized
+ m_isminimized = (rect_width(&bounds) == minbounds.width()) ||
+ (rect_height(&bounds) == minbounds.height());
+ m_ismaximized = (rect_width(&bounds) == maxbounds.width()) ||
+ (rect_height(&bounds) == maxbounds.height());
+ }
+ else
+ {
+ m_isminimized = FALSE;
+ m_ismaximized = TRUE;
+ }
+}
+
+
+
+//============================================================
+// minimize_window
+// (window thread)
+//============================================================
+
+void win_window_info::minimize_window()
+{
+ assert(GetCurrentThreadId() == window_threadid);
+
+ osd_dim newsize = get_min_bounds(video_config.keepaspect);
+
+ // get the window rect
+ RECT bounds;
+ GetWindowRect(m_hwnd, &bounds);
+
+ osd_rect newrect(bounds.left, bounds.top, newsize );
+
+
+ SetWindowPos(m_hwnd, NULL, newrect.left(), newrect.top(), newrect.width(), newrect.height(), SWP_NOZORDER);
+}
+
+
+
+//============================================================
+// maximize_window
+// (window thread)
+//============================================================
+
+void win_window_info::maximize_window()
+{
+ assert(GetCurrentThreadId() == window_threadid);
+
+ osd_dim newsize = get_max_bounds(video_config.keepaspect);
+
+ // center within the work area
+ osd_rect work = m_monitor->usuable_position_size();
+ osd_rect newrect = osd_rect(work.left() + (work.width() - newsize.width()) / 2,
+ work.top() + (work.height() - newsize.height()) / 2,
+ newsize);
+
+ SetWindowPos(m_hwnd, NULL, newrect.left(), newrect.top(), newrect.width(), newrect.height(), SWP_NOZORDER);
+}
+
+
+
+//============================================================
+// adjust_window_position_after_major_change
+// (window thread)
+//============================================================
+
+void win_window_info::adjust_window_position_after_major_change()
+{
+ RECT oldrect;
+
+ assert(GetCurrentThreadId() == window_threadid);
+
+ // get the current size
+ GetWindowRect(m_hwnd, &oldrect);
+ osd_rect newrect = RECT_to_osd_rect(oldrect);
+
+ // adjust the window size so the client area is what we want
+ if (!m_fullscreen)
+ {
+ // constrain the existing size to the aspect ratio
+ if (video_config.keepaspect)
+ newrect = constrain_to_aspect_ratio(newrect, WMSZ_BOTTOMRIGHT);
+ }
+
+ // in full screen, make sure it covers the primary display
+ else
+ {
+ osd_monitor_info *monitor = winwindow_video_window_monitor(NULL);
+ newrect = monitor->position_size();
+ }
+
+ // adjust the position if different
+ if (oldrect.left != newrect.left() || oldrect.top != newrect.top() ||
+ oldrect.right != newrect.right() || oldrect.bottom != newrect.bottom())
+ SetWindowPos(m_hwnd, m_fullscreen ? HWND_TOPMOST : HWND_TOP,
+ newrect.left(), newrect.top(),
+ newrect.width(), newrect.height(), 0);
+
+ // take note of physical window size (used for lightgun coordinate calculation)
+ if (this == win_window_list)
+ {
+ win_physical_width = newrect.width();
+ win_physical_height = newrect.height();
+ machine().logerror("Physical width %d, height %d\n",win_physical_width,win_physical_height);
+ }
+}
+
+
+//============================================================
+// set_fullscreen
+// (window thread)
+//============================================================
+
+void win_window_info::set_fullscreen(int fullscreen)
+{
+ assert(GetCurrentThreadId() == window_threadid);
+
+ // if we're in the right state, punt
+ if (m_fullscreen == fullscreen)
+ return;
+ m_fullscreen = fullscreen;
+
+ // kill off the drawers
+ m_renderer->destroy();
+ global_free(m_renderer);
+ m_renderer = NULL;
+
+ // hide ourself
+ ShowWindow(m_hwnd, SW_HIDE);
+
+ // configure the window if non-fullscreen
+ if (!fullscreen)
+ {
+ // adjust the style
+ SetWindowLong(m_hwnd, GWL_STYLE, WINDOW_STYLE);
+ SetWindowLong(m_hwnd, GWL_EXSTYLE, WINDOW_STYLE_EX);
+ SetWindowPos(m_hwnd, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
+
+ // force to the bottom, then back on top
+ SetWindowPos(m_hwnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
+ SetWindowPos(m_hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
+
+ // if we have previous non-fullscreen bounds, use those
+ if (m_non_fullscreen_bounds.right != m_non_fullscreen_bounds.left)
+ {
+ SetWindowPos(m_hwnd, HWND_TOP, m_non_fullscreen_bounds.left, m_non_fullscreen_bounds.top,
+ rect_width(&m_non_fullscreen_bounds), rect_height(&m_non_fullscreen_bounds),
+ SWP_NOZORDER);
+ }
+
+ // otherwise, set a small size and maximize from there
+ else
+ {
+ SetWindowPos(m_hwnd, HWND_TOP, 0, 0, MIN_WINDOW_DIM, MIN_WINDOW_DIM, SWP_NOZORDER);
+ maximize_window();
+ }
+ }
+
+ // configure the window if fullscreen
+ else
+ {
+ // save the bounds
+ GetWindowRect(m_hwnd, &m_non_fullscreen_bounds);
+
+ // adjust the style
+ SetWindowLong(m_hwnd, GWL_STYLE, FULLSCREEN_STYLE);
+ SetWindowLong(m_hwnd, GWL_EXSTYLE, FULLSCREEN_STYLE_EX);
+ SetWindowPos(m_hwnd, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
+
+ // set topmost
+ SetWindowPos(m_hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
+ }
+
+ // adjust the window to compensate for the change
+ adjust_window_position_after_major_change();
+
+ // show ourself
+ if (!m_fullscreen || m_fullscreen_safe)
+ {
+ if (video_config.mode != VIDEO_MODE_NONE)
+ ShowWindow(m_hwnd, SW_SHOW);
+ m_renderer = draw.create(this);
+ if (m_renderer->create())
+ exit(1);
+ }
+
+ // ensure we're still adjusted correctly
+ adjust_window_position_after_major_change();
+}
+
+#if (USE_QTDEBUG)
+bool winwindow_qt_filter(void *message)
+{
+ MSG *msg = (MSG *)message;
+
+ if(is_mame_window(msg->hwnd) || (!msg->hwnd && (msg->message >= WM_USER)))
+ {
+ LONG_PTR ptr;
+ if(msg->hwnd) // get the machine associated with this window
+ ptr = GetWindowLongPtr(msg->hwnd, GWLP_USERDATA);
+ else // any one will have to do
+ ptr = (LONG_PTR)win_window_list;
+
+ winwindow_dispatch_message(((win_window_info *)ptr)->machine(), msg);
+ return true;
+ }
+ return false;
+}
+#endif