mame/src/osd/sdl/window.c
Couriersud 10bee5ce78 The return of the "misc"
- Added sdlmisc_<targetos>.c again. This was necessary since
  certain tools create stubs for e.g. osd_break_into_debugger.
  If we do not have this in a separate file, the link stage may
  break.
- Applied OS/2 patch [Credit: KO Myung-Hun]
- Cleaned up #includes. Removed stdlib.h were possible.
- More malloc to osd_malloc rename.
- SDL monitor modes are read now when they are needed. This is now consistent across platforms.
2010-01-21 22:54:49 +00:00

1396 lines
37 KiB
C

//============================================================
//
// window.c - SDL window handling
//
// Copyright (c) 1996-2010, Nicola Salmoria and the MAME Team.
// Visit http://mamedev.org for licensing and usage restrictions.
//
// SDLMAME by Olivier Galibert and R. Belmont
//
//============================================================
// standard SDL headers
#include <SDL/SDL.h>
#include <SDL/SDL_syswm.h>
#include <SDL/SDL_thread.h>
// standard C headers
#include <math.h>
#include <unistd.h>
// MAME headers
#include "emu.h"
#include "emuopts.h"
#include "ui.h"
// OSD headers
#include "window.h"
#include "input.h"
#include "osdsdl.h"
#ifdef MESS
#include "menu.h"
#endif /* MESS */
//============================================================
// 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
#ifdef MAME_DEBUG
//#define ASSERT_USE(x) do { printf("%x %x\n", (int) SDL_ThreadID(), x); assert_always(SDL_ThreadID() == x, "Wrong Thread"); } while (0)
#define ASSERT_USE(x) do { SDL_threadID _thid = SDL_ThreadID(); assert_always( _thid == x, "Wrong Thread"); } while (0)
#else
#define ASSERT_USE(x) do {} while (0)
//#define ASSERT_USE(x) assert(SDL_ThreadID() == x)
#endif
#define ASSERT_REDRAW_THREAD() ASSERT_USE(window_threadid)
#define ASSERT_WINDOW_THREAD() ASSERT_USE(window_threadid)
#define ASSERT_MAIN_THREAD() ASSERT_USE(main_threadid)
#define OSDWORK_CALLBACK(name) void *name(void *param, ATTR_UNUSED int threadid)
// 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
//============================================================
// GLOBAL VARIABLES
//============================================================
sdl_window_info *sdl_window_list;
//============================================================
// LOCAL VARIABLES
//============================================================
static sdl_window_info **last_window_ptr;
// event handling
static int multithreading_enabled;
static osd_work_queue *work_queue;
#if !(SDL_VERSION_ATLEAST(1,3,0))
typedef int SDL_threadID;
#endif
static SDL_threadID main_threadid;
static SDL_threadID window_threadid;
// debugger
//static int in_background;
static sdl_draw_info draw;
typedef struct _worker_param worker_param;
struct _worker_param {
sdl_window_info *window;
const render_primitive_list *list;
running_machine *machine;
int resize_new_width;
int resize_new_height;
};
//============================================================
// PROTOTYPES
//============================================================
static void sdlwindow_exit(running_machine *machine);
static void sdlwindow_video_window_destroy(running_machine *machine, sdl_window_info *window);
static OSDWORK_CALLBACK( draw_video_contents_wt );
static OSDWORK_CALLBACK( sdlwindow_video_window_destroy_wt );
static OSDWORK_CALLBACK( sdlwindow_resize_wt );
static OSDWORK_CALLBACK( sdlwindow_toggle_full_screen_wt );
static void sdlwindow_update_cursor_state(running_machine *machine, sdl_window_info *window);
static void sdlwindow_sync(void);
static void get_min_bounds(sdl_window_info *window, int *window_width, int *window_height, int constrain);
static void get_max_bounds(sdl_window_info *window, int *window_width, int *window_height, int constrain);
static void *complete_create_wt(void *param, int threadid);
static void set_starting_view(running_machine *machine, int index, sdl_window_info *window, const char *view);
//============================================================
// clear the worker_param structure, inline - faster than memset
//============================================================
INLINE void clear_worker_param(worker_param *wp)
{
wp->window=NULL;
wp->list=NULL;
wp->machine=NULL;
wp->resize_new_width=0;
wp->resize_new_height=0;
}
//============================================================
// execute_async
//============================================================
INLINE void execute_async(osd_work_callback callback, worker_param *wp)
{
worker_param *wp_temp = NULL;
if (wp)
{
wp_temp = (worker_param *) osd_malloc(sizeof(worker_param));
*wp_temp = *wp;
}
if (multithreading_enabled)
{
osd_work_item_queue(work_queue, callback, (void *) wp_temp, WORK_ITEM_FLAG_AUTO_RELEASE);
} else
callback((void *) wp_temp, 0);
}
//============================================================
// execute_async_wait
//============================================================
INLINE void execute_async_wait(osd_work_callback callback, worker_param *wp)
{
execute_async(callback, wp);
sdlwindow_sync();
}
//============================================================
// sdlwindow_thread_id
// (window thread)
//============================================================
static OSDWORK_CALLBACK(sdlwindow_thread_id)
{
window_threadid = SDL_ThreadID();
if (SDLMAME_INIT_IN_WORKER_THREAD)
{
#if (SDL_VERSION_ATLEAST(1,3,0))
if (SDL_InitSubSystem(SDL_INIT_TIMER|SDL_INIT_AUDIO| SDL_INIT_VIDEO| SDL_INIT_JOYSTICK|SDL_INIT_NOPARACHUTE))
#else
if (SDL_Init(SDL_INIT_TIMER|SDL_INIT_AUDIO| SDL_INIT_VIDEO| SDL_INIT_JOYSTICK|SDL_INIT_NOPARACHUTE))
#endif
{
mame_printf_error("Could not initialize SDL: %s.\n", SDL_GetError());
exit(-1);
}
}
return NULL;
}
//============================================================
// win_init_window
// (main thread)
//============================================================
int sdlwindow_init(running_machine *machine)
{
mame_printf_verbose("Enter sdlwindow_init\n");
// determine if we are using multithreading or not
multithreading_enabled = options_get_bool(mame_options(), SDLOPTION_MULTITHREADING);
// get the main thread ID before anything else
main_threadid = SDL_ThreadID();
// ensure we get called on the way out
add_exit_callback(machine, sdlwindow_exit);
// if multithreading, create a thread to run the windows
if (multithreading_enabled)
{
// create a thread to run the windows from
work_queue = osd_work_queue_alloc(WORK_QUEUE_FLAG_IO);
if (work_queue == NULL)
return 1;
osd_work_item_queue(work_queue, &sdlwindow_thread_id, NULL, WORK_ITEM_FLAG_AUTO_RELEASE);
}
else
{
// otherwise, treat the window thread as the main thread
//window_threadid = main_threadid;
sdlwindow_thread_id(NULL, 0);
}
// initialize the drawers
#if USE_OPENGL
if (video_config.mode == VIDEO_MODE_OPENGL)
{
if (drawogl_init(&draw))
video_config.mode = VIDEO_MODE_SOFT;
}
#endif
#if SDL_VERSION_ATLEAST(1,3,0)
if (video_config.mode == VIDEO_MODE_SDL13)
{
if (draw13_init(&draw))
video_config.mode = VIDEO_MODE_SOFT;
}
#endif
if (video_config.mode == VIDEO_MODE_SOFT)
{
if (drawsdl_init(&draw))
return 1;
}
// set up the window list
last_window_ptr = &sdl_window_list;
mame_printf_verbose("Leave sdlwindow_init\n");
return 0;
}
//============================================================
// sdlwindow_sync
//============================================================
static void sdlwindow_sync(void)
{
if (multithreading_enabled)
{
// Fallback
while (!osd_work_queue_wait(work_queue, osd_ticks_per_second()*10))
{
mame_printf_warning("sdlwindow_sync: Sleeping...\n");
osd_sleep(100000);
}
}
}
//============================================================
// sdlwindow_exit
// (main thread)
//============================================================
static OSDWORK_CALLBACK( sdlwindow_exit_wt )
{
if (SDLMAME_INIT_IN_WORKER_THREAD)
SDL_Quit();
if (param)
osd_free(param);
return NULL;
}
static void sdlwindow_exit(running_machine *machine)
{
ASSERT_MAIN_THREAD();
mame_printf_verbose("Enter sdlwindow_exit\n");
// free all the windows
while (sdl_window_list != NULL)
{
sdl_window_info *temp = sdl_window_list;
sdl_window_list = temp->next;
sdlwindow_video_window_destroy(machine, temp);
}
// if we're multithreaded, clean up the window thread
if (multithreading_enabled)
{
sdlwindow_sync();
}
// kill the drawers
(*draw.exit)();
execute_async_wait(&sdlwindow_exit_wt, NULL);
if (multithreading_enabled)
{
osd_work_queue_wait(work_queue, 1000000);
osd_work_queue_free(work_queue);
}
mame_printf_verbose("Leave sdlwindow_exit\n");
}
//============================================================
// sdlwindow_blit_surface_size
//============================================================
INLINE int better_mode(int width0, int height0, int width1, int height1, float desired_aspect)
{
float aspect0 = (float)width0 / (float)height0;
float aspect1 = (float)width1 / (float)height1;
return (fabs(desired_aspect - aspect0) < fabs(desired_aspect - aspect1)) ? 0 : 1;
}
void sdlwindow_blit_surface_size(sdl_window_info *window, int window_width, int window_height)
{
INT32 newwidth, newheight;
int xscale = 1, yscale = 1;
float desired_aspect = 1.0f;
INT32 target_width = window_width;
INT32 target_height = window_height;
// start with the minimum size
render_target_get_minimum_size(window->target, &newwidth, &newheight);
// compute the appropriate visible area if we're trying to keepaspect
if (video_config.keepaspect)
{
// make sure the monitor is up-to-date
sdlvideo_monitor_refresh(window->monitor);
render_target_compute_visible_area(window->target, target_width, target_height, sdlvideo_monitor_get_aspect(window->monitor), render_target_get_orientation(window->target), &target_width, &target_height);
desired_aspect = (float)target_width / (float)target_height;
}
// non-integer scaling - often gives more pleasing results in full screen
if (!video_config.fullstretch)
{
// compute maximum integral scaling to fit the window
xscale = (target_width + 2) / newwidth;
yscale = (target_height + 2) / newheight;
// try a little harder to keep the aspect ratio if desired
if (video_config.keepaspect)
{
// if we could stretch more in the X direction, and that makes a better fit, bump the xscale
while (newwidth * (xscale + 1) <= window_width &&
better_mode(newwidth * xscale, newheight * yscale, newwidth * (xscale + 1), newheight * yscale, desired_aspect))
xscale++;
// if we could stretch more in the Y direction, and that makes a better fit, bump the yscale
while (newheight * (yscale + 1) <= window_height &&
better_mode(newwidth * xscale, newheight * yscale, newwidth * xscale, newheight * (yscale + 1), desired_aspect))
yscale++;
// now that we've maxed out, see if backing off the maximally stretched one makes a better fit
if (window_width - newwidth * xscale < window_height - newheight * yscale)
{
while (better_mode(newwidth * xscale, newheight * yscale, newwidth * (xscale - 1), newheight * yscale, desired_aspect) && (xscale >= 0))
xscale--;
}
else
{
while (better_mode(newwidth * xscale, newheight * yscale, newwidth * xscale, newheight * (yscale - 1), desired_aspect) && (yscale >= 0))
yscale--;
}
}
// ensure at least a scale factor of 1
if (xscale <= 0) xscale = 1;
if (yscale <= 0) yscale = 1;
// apply the final scale
newwidth *= xscale;
newheight *= yscale;
}
else
{
newwidth = target_width;
newheight = target_height;
}
//FIXME: really necessary to distinguish for yuv_modes ?
if ((render_target_get_layer_config(window->target) & LAYER_CONFIG_ZOOM_TO_SCREEN)
&& (window->scale_mode == VIDEO_SCALE_MODE_NONE ))
newwidth = window_width;
if ((window->blitwidth != newwidth) || (window->blitheight != newheight))
sdlwindow_clear(window);
window->blitwidth = newwidth;
window->blitheight = newheight;
}
//============================================================
// sdlwindow_resize
// (main thread)
//============================================================
static OSDWORK_CALLBACK( sdlwindow_resize_wt )
{
worker_param * wp = (worker_param *) param;
sdl_window_info * window = wp->window;
ASSERT_WINDOW_THREAD();
window->destroy_all_textures(window);
window->resize(window, wp->resize_new_width, wp->resize_new_height);
sdlwindow_blit_surface_size(window, wp->resize_new_width, wp->resize_new_height);
sdlwindow_clear(window);
osd_free(wp);
return NULL;
}
void sdlwindow_resize(sdl_window_info *window, INT32 width, INT32 height)
{
worker_param wp;
ASSERT_MAIN_THREAD();
if (width == window->width && height == window->height)
return;
clear_worker_param(&wp);
wp.resize_new_width = width;
wp.resize_new_height = height;
wp.window = window;
execute_async_wait(&sdlwindow_resize_wt, &wp);
}
//============================================================
// sdlwindow_clear_surface
// (window thread)
//============================================================
static OSDWORK_CALLBACK( sdlwindow_clear_surface_wt )
{
worker_param *wp = (worker_param *) param;
sdl_window_info *window = wp->window;
ASSERT_WINDOW_THREAD();
window->clear(window);
osd_free(wp);
return NULL;
}
void sdlwindow_clear(sdl_window_info *window)
{
worker_param *wp = (worker_param *) osd_malloc(sizeof(worker_param));
clear_worker_param(wp);
wp->window = window;
if (SDL_ThreadID() == main_threadid)
{
execute_async_wait(&sdlwindow_clear_surface_wt, wp);
osd_free(wp);
}
else
sdlwindow_clear_surface_wt( (void *) wp, 0);
}
//============================================================
// sdlwindow_toggle_full_screen
// (main thread)
//============================================================
static OSDWORK_CALLBACK( sdlwindow_toggle_full_screen_wt )
{
worker_param *wp = (worker_param *) param;
sdl_window_info *window = wp->window;
ASSERT_WINDOW_THREAD();
// if we are in debug mode, never go full screen
if (options_get_bool(mame_options(), OPTION_DEBUG))
return NULL;
// If we are going fullscreen (leaving windowed) remember our windowed size
if (!window->fullscreen)
{
window->windowed_width = window->width;
window->windowed_height = window->height;
}
window->destroy(window);
sdlinput_release_keys(wp->machine);
// toggle the window mode
window->fullscreen = !window->fullscreen;
complete_create_wt(param, 0);
return NULL;
}
void sdlwindow_toggle_full_screen(running_machine *machine, sdl_window_info *window)
{
worker_param wp;
ASSERT_MAIN_THREAD();
clear_worker_param(&wp);
wp.window = window;
wp.machine = machine;
execute_async_wait(&sdlwindow_toggle_full_screen_wt, &wp);
}
static OSDWORK_CALLBACK( destroy_all_textures_wt )
{
worker_param *wp = (worker_param *) param;
sdl_window_info *window = wp->window;
window->destroy_all_textures(window);
osd_free(wp);
return NULL;
}
void sdlwindow_modify_prescale(running_machine *machine, sdl_window_info *window, int dir)
{
worker_param wp;
int new_prescale = window->prescale;
clear_worker_param(&wp);
wp.window = window;
wp.machine = machine;
if (dir > 0 && window->prescale < 3)
new_prescale = window->prescale + 1;
if (dir < 0 && window->prescale > 1)
new_prescale = window->prescale - 1;
if (new_prescale != window->prescale)
{
if (window->fullscreen && video_config.switchres)
{
execute_async_wait(&sdlwindow_video_window_destroy_wt, &wp);
window->prescale = new_prescale;
execute_async_wait(&complete_create_wt, &wp);
}
else
{
execute_async_wait(destroy_all_textures_wt, &wp);
window->prescale = new_prescale;
}
ui_popup_time(1, "Prescale %d", window->prescale);
}
}
void sdlwindow_toggle_draw(running_machine *machine, sdl_window_info *window)
{
#if USE_OPENGL
//FIXME: Not yet working in 1.3
#if (!SDL_VERSION_ATLEAST(1,3,0))
worker_param wp;
// If we are not fullscreen (windowed) remember our windowed size
if (!window->fullscreen)
{
window->windowed_width = window->width;
window->windowed_height = window->height;
}
clear_worker_param(&wp);
wp.window = window;
wp.machine = machine;
execute_async_wait(&sdlwindow_video_window_destroy_wt, &wp);
window->scale_mode = VIDEO_SCALE_MODE_NONE;
if (video_config.mode == VIDEO_MODE_OPENGL)
{
video_config.mode = VIDEO_MODE_SOFT;
drawsdl_init(&draw);
ui_popup_time(1, "Using software rendering");
}
else
{
video_config.mode = VIDEO_MODE_OPENGL;
drawogl_init(&draw);
ui_popup_time(1, "Using OpenGL rendering");
}
execute_async_wait(&complete_create_wt, &wp);
#endif
#endif
}
//============================================================
// sdlwindow_update_cursor_state
// (main or window thread)
//============================================================
static void sdlwindow_update_cursor_state(running_machine *machine, sdl_window_info *window)
{
#if (SDL_VERSION_ATLEAST(1,3,0))
// do not do mouse capture if the debugger's enabled to avoid
// the possibility of losing control
if (!options_get_bool(mame_options(), OPTION_DEBUG))
{
//FIXME: SDL1.3: really broken: the whole SDL code
// will only work correct with relative mouse movements ...
//SDL_SetRelativeMouseMode
if (!window->fullscreen && !sdlinput_should_hide_mouse(machine))
{
SDL_ShowCursor(SDL_ENABLE);
if (SDL_GetWindowGrab(window->window_id ))
SDL_SetWindowGrab(window->window_id, 0);
}
else
{
SDL_ShowCursor(SDL_DISABLE);
if (!SDL_GetWindowGrab(window->window_id))
SDL_SetWindowGrab(window->window_id, 1);
}
SDL_SetCursor(NULL); // Force an update in case the underlying driver has changed visibility
}
#else
// do not do mouse capture if the debugger's enabled to avoid
// the possibility of losing control
if (!options_get_bool(mame_options(), OPTION_DEBUG))
{
if ( window->fullscreen || sdlinput_should_hide_mouse(machine) )
{
SDL_ShowCursor(SDL_DISABLE);
if (!SDL_WM_GrabInput(SDL_GRAB_QUERY))
{
SDL_WM_GrabInput(SDL_GRAB_ON);
}
}
else
{
SDL_ShowCursor(SDL_ENABLE);
if (SDL_WM_GrabInput(SDL_GRAB_QUERY))
{
SDL_WM_GrabInput(SDL_GRAB_OFF);
}
}
}
#endif
}
//============================================================
// sdlwindow_video_window_create
// (main thread)
//============================================================
int sdlwindow_video_window_create(running_machine *machine, int index, sdl_monitor_info *monitor, const sdl_window_config *config)
{
sdl_window_info *window;
worker_param *wp = (worker_param *) osd_malloc(sizeof(worker_param));
char option[20];
int result;
ASSERT_MAIN_THREAD();
clear_worker_param(wp);
// allocate a new window object
window = global_alloc_clear(sdl_window_info);
window->maxwidth = config->width;
window->maxheight = config->height;
window->depth = config->depth;
window->refresh = config->refresh;
window->monitor = monitor;
window->machine = machine;
window->index = index;
//FIXME: these should be per_window in config-> or even better a bit set
window->fullscreen = !video_config.windowed;
window->prescale = video_config.prescale;
window->scale_mode = video_config.scale_mode;
// set the initial maximized state
// FIXME: Does not belong here
window->startmaximized = options_get_bool(mame_options(), SDLOPTION_MAXIMIZE);
if (!window->fullscreen)
{
window->windowed_width = config->width;
window->windowed_height = config->height;
}
window->totalColors = config->totalColors;
// add us to the list
*last_window_ptr = window;
last_window_ptr = &window->next;
draw.attach(&draw, window);
// create an event that we can use to skip blitting
window->rendered_event = osd_event_alloc(FALSE, TRUE);
// load the layout
window->target = render_target_alloc(machine, NULL, FALSE);
if (window->target == NULL)
goto error;
// set the specific view
sprintf(option, SDLOPTION_VIEW("%d"), index);
set_starting_view(machine, index, window, options_get_string(mame_options(), option));
// make the window title
if (video_config.numscreens == 1)
sprintf(window->title, APPNAME ": %s [%s]", machine->gamedrv->description, machine->gamedrv->name);
else
sprintf(window->title, APPNAME ": %s [%s] - Screen %d", machine->gamedrv->description, machine->gamedrv->name, index);
wp->window = window;
if (multithreading_enabled)
{
osd_work_item *wi;
wi = osd_work_item_queue(work_queue, &complete_create_wt, (void *) wp, 0);
sdlwindow_sync();
result = *((int *) (osd_work_item_result)(wi));
osd_work_item_release(wi);
}
else
result = *((int *) complete_create_wt((void *) wp, 0));
// handle error conditions
if (result == 1)
goto error;
return 0;
error:
sdlwindow_video_window_destroy(machine, window);
return 1;
}
//============================================================
// sdlwindow_video_window_destroy
// (main thread)
//============================================================
static OSDWORK_CALLBACK( sdlwindow_video_window_destroy_wt )
{
worker_param * wp = (worker_param *) param;
sdl_window_info * window = wp->window;
ASSERT_WINDOW_THREAD();
// free the textures etc
window->destroy(window);
// release all keys ...
sdlinput_release_keys(wp->machine);
osd_free(wp);
return NULL;
}
static void sdlwindow_video_window_destroy(running_machine *machine, sdl_window_info *window)
{
sdl_window_info **prevptr;
worker_param wp;
ASSERT_MAIN_THREAD();
if (multithreading_enabled)
{
sdlwindow_sync();
}
//osd_event_wait(window->rendered_event, osd_ticks_per_second()*10);
// remove us from the list
for (prevptr = &sdl_window_list; *prevptr != NULL; prevptr = &(*prevptr)->next)
if (*prevptr == window)
{
*prevptr = window->next;
break;
}
// free the textures etc
clear_worker_param(&wp);
wp.window = window;
wp.machine = machine;
execute_async_wait(&sdlwindow_video_window_destroy_wt, &wp);
// free the render target, after the textures!
if (window->target != NULL)
render_target_free(window->target);
// free the event
osd_event_free(window->rendered_event);
// free the window itself
global_free(window);
}
//============================================================
// pick_best_mode
//============================================================
#if SDL_VERSION_ATLEAST(1,3,0)
static void pick_best_mode(sdl_window_info *window, int *fswidth, int *fsheight)
{
int minimum_width, minimum_height, target_width, target_height;
int i;
int num;
float size_score, best_score = 0.0f;
// determine the minimum width/height for the selected target
render_target_get_minimum_size(window->target, &minimum_width, &minimum_height);
// use those as the target for now
target_width = minimum_width * MAX(1, window->prescale);
target_height = minimum_height * MAX(1, window->prescale);
// if we're not stretching, allow some slop on the minimum since we can handle it
{
minimum_width -= 4;
minimum_height -= 4;
}
num = SDL_GetNumDisplayModes();
if (num == 0)
{
mame_printf_error("SDL: No modes available?!\n");
exit(-1);
}
else
{
for (i = 0; i < num; ++i)
{
SDL_DisplayMode mode;
SDL_GetDisplayMode(i, &mode);
// compute initial score based on difference between target and current
size_score = 1.0f / (1.0f + fabsf((INT32)mode.w - target_width) + fabsf((INT32)mode.h - target_height));
// if the mode is too small, give a big penalty
if (mode.w < minimum_width || mode.h < minimum_height)
size_score *= 0.01f;
// if mode is smaller than we'd like, it only scores up to 0.1
if (mode.w < target_width || mode.h < target_height)
size_score *= 0.1f;
// if we're looking for a particular mode, that's a winner
if (mode.w == window->maxwidth && mode.h == window->maxheight)
size_score = 2.0f;
// refresh adds some points
if (window->refresh)
size_score *= 1.0f / (1.0f + fabsf(window->refresh - mode.refresh_rate) / 10.0f);
mame_printf_verbose("%4dx%4d@%2d -> %f\n", (int)mode.w, (int)mode.h, (int) mode.refresh_rate, size_score);
// best so far?
if (size_score > best_score)
{
best_score = size_score;
*fswidth = mode.w;
*fsheight = mode.h;
}
}
}
}
#else
static void pick_best_mode(sdl_window_info *window, int *fswidth, int *fsheight)
{
int minimum_width, minimum_height, target_width, target_height;
int i;
float size_score, best_score = 0.0f;
SDL_Rect **modes;
// determine the minimum width/height for the selected target
render_target_get_minimum_size(window->target, &minimum_width, &minimum_height);
// use those as the target for now
target_width = minimum_width * MAX(1, window->prescale);
target_height = minimum_height * MAX(1, window->prescale);
// if we're not stretching, allow some slop on the minimum since we can handle it
{
minimum_width -= 4;
minimum_height -= 4;
}
#if 1 // defined(SDLMAME_WIN32)
/*
* We need to do this here. If SDL_ListModes is
* called in init_monitors, the call will crash
* on win32
*/
modes = SDL_ListModes(NULL, SDL_FULLSCREEN | SDL_DOUBLEBUF);
#else
modes = window->monitor->modes;
#endif
if (modes == (SDL_Rect **)0)
{
mame_printf_error("SDL: No modes available?!\n");
exit(-1);
}
else if (modes == (SDL_Rect **)-1) // all modes are possible
{
*fswidth = window->maxwidth;
*fsheight = window->maxheight;
}
else
{
for (i = 0; modes[i]; ++i)
{
// compute initial score based on difference between target and current
size_score = 1.0f / (1.0f + fabsf((INT32)modes[i]->w - target_width) + fabsf((INT32)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 == window->maxwidth && modes[i]->h == window->maxheight)
size_score = 2.0f;
mame_printf_verbose("%4dx%4d -> %f\n", (int)modes[i]->w, (int)modes[i]->h, size_score);
// best so far?
if (size_score > best_score)
{
best_score = size_score;
*fswidth = modes[i]->w;
*fsheight = modes[i]->h;
}
}
}
}
#endif
//============================================================
// sdlwindow_video_window_update
// (main thread)
//============================================================
void sdlwindow_video_window_update(running_machine *machine, sdl_window_info *window)
{
ASSERT_MAIN_THREAD();
// adjust the cursor state
sdlwindow_update_cursor_state(machine, window);
// if we're visible and running and not in the middle of a resize, draw
if (window->target != NULL)
{
int tempwidth, tempheight;
// see if the games video mode has changed
render_target_get_minimum_size(window->target, &tempwidth, &tempheight);
if (tempwidth != window->minwidth || tempheight != window->minheight)
{
window->minwidth = tempwidth;
window->minheight = tempheight;
if (!window->fullscreen)
{
sdlwindow_blit_surface_size(window, window->width, window->height);
sdlwindow_resize(window, window->blitwidth, window->blitheight);
}
else if (video_config.switchres)
{
pick_best_mode(window, &tempwidth, &tempheight);
sdlwindow_resize(window, tempwidth, tempheight);
}
}
// only render if we have been signalled
if (osd_event_wait(window->rendered_event, 0))
{
worker_param wp;
const render_primitive_list *primlist;
clear_worker_param(&wp);
// ensure the target bounds are up-to-date, and then get the primitives
primlist = window->get_primitives(window);
// and redraw now
wp.list = primlist;
wp.window = window;
wp.machine = machine;
execute_async(&draw_video_contents_wt, &wp);
}
}
}
//============================================================
// set_starting_view
// (main thread)
//============================================================
static void set_starting_view(running_machine *machine, int index, sdl_window_info *window, const char *view)
{
const char *defview = options_get_string(mame_options(), SDLOPTION_VIEW( ));
int viewindex;
ASSERT_MAIN_THREAD();
// 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 = video_get_view_for_target(machine, window->target, view, index, video_config.numscreens);
// set the view
render_target_set_view(window->target, viewindex);
window->start_viewscreen=viewindex;
}
//============================================================
// complete_create
// (window thread)
//============================================================
static OSDWORK_CALLBACK( complete_create_wt )
{
worker_param * wp = (worker_param *) param;
sdl_window_info * window = wp->window;
int tempwidth, tempheight;
static int result[2] = {0,1};
ASSERT_WINDOW_THREAD();
osd_free(wp);
if (window->fullscreen)
{
// default to the current mode exactly
tempwidth = window->monitor->monitor_width;
tempheight = window->monitor->monitor_height;
// if we're allowed to switch resolutions, override with something better
if (video_config.switchres)
pick_best_mode(window, &tempwidth, &tempheight);
}
else if (window->windowed_width)
{
// if we have a remembered size force the new window size to it
tempwidth = window->windowed_width;
tempheight = window->windowed_height;
}
else
{
if (window->startmaximized)
{
tempwidth = tempheight = 0;
get_max_bounds(window, &tempwidth, &tempheight, video_config.keepaspect );
}
else
{
/* Create the window directly with the correct aspect
instead of letting sdlwindow_blit_surface_size() resize it
this stops the window from "flashing" from the wrong aspect
size to the right one at startup. */
tempwidth = (window->maxwidth != 0) ? window->maxwidth : 640;
tempheight = (window->maxheight != 0) ? window->maxheight : 480;
get_min_bounds(window, &tempwidth, &tempheight, video_config.keepaspect );
}
}
// initialize the drawing backend
if (window->create(window, tempwidth, tempheight))
return (void *) &result[1];
// Make sure we have a consistent state
SDL_ShowCursor(0);
SDL_ShowCursor(1);
return (void *) &result[0];
}
//============================================================
// draw_video_contents
// (window thread)
//============================================================
static void measure_fps(sdl_window_info *window, UINT32 dc, 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();
window->draw(window, dc, 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.
mame_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;
}
}
static OSDWORK_CALLBACK( draw_video_contents_wt )
{
UINT32 dc = 0;
int update = 1;
worker_param *wp = (worker_param *) param;
sdl_window_info *window = wp->window;
ASSERT_REDRAW_THREAD();
// Some configurations require events to be polled in the worker thread
sdlinput_process_events_buf(wp->machine);
window->primlist = wp->list;
// if no bitmap, just fill
if (window->primlist == NULL)
{
}
// otherwise, render with our drawing system
else
{
if( video_config.perftest )
measure_fps(window, dc, update);
else
window->draw(window, dc, update);
}
/* all done, ready for next */
osd_event_set(window->rendered_event);
osd_free(wp);
return NULL;
}
//============================================================
// constrain_to_aspect_ratio
// (window thread)
//============================================================
static void constrain_to_aspect_ratio(sdl_window_info *window, int *window_width, int *window_height, int adjustment)
{
INT32 extrawidth = 0;
INT32 extraheight = 0;
INT32 propwidth, propheight;
INT32 minwidth, minheight;
INT32 maxwidth, maxheight;
INT32 viswidth, visheight;
float pixel_aspect;
// make sure the monitor is up-to-date
sdlvideo_monitor_refresh(window->monitor);
// get the pixel aspect ratio for the target monitor
pixel_aspect = sdlvideo_monitor_get_aspect(window->monitor);
// determine the proposed width/height
propwidth = *window_width - extrawidth;
propheight = *window_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:
render_target_compute_visible_area(window->target, 10000, propheight, pixel_aspect, render_target_get_orientation(window->target), &propwidth, &propheight);
break;
case WMSZ_LEFT:
case WMSZ_RIGHT:
render_target_compute_visible_area(window->target, propwidth, 10000, pixel_aspect, render_target_get_orientation(window->target), &propwidth, &propheight);
break;
default:
render_target_compute_visible_area(window->target, propwidth, propheight, pixel_aspect, render_target_get_orientation(window->target), &propwidth, &propheight);
break;
}
// get the minimum width/height for the current layout
render_target_get_minimum_size(window->target, &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 (window->fullscreen)
{
maxwidth = window->monitor->center_width - extrawidth;
maxheight = window->monitor->center_height - extraheight;
}
else
{
maxwidth = window->monitor->center_width - extrawidth;
maxheight = window->monitor->center_height - extraheight;
// further clamp to the maximum width/height in the window
if (window->maxwidth != 0)
maxwidth = MIN(maxwidth, window->maxwidth + extrawidth);
if (window->maxheight != 0)
maxheight = MIN(maxheight, window->maxheight + extraheight);
}
// clamp to the maximum
propwidth = MIN(propwidth, maxwidth);
propheight = MIN(propheight, maxheight);
// compute the visible area based on the proposed rectangle
render_target_compute_visible_area(window->target, propwidth, propheight, pixel_aspect, render_target_get_orientation(window->target), &viswidth, &visheight);
*window_width = viswidth;
*window_height = visheight;
}
//============================================================
// get_min_bounds
// (window thread)
//============================================================
static void get_min_bounds(sdl_window_info *window, int *window_width, int *window_height, int constrain)
{
INT32 minwidth, minheight;
// get the minimum target size
render_target_get_minimum_size(window->target, &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;
// if we want it constrained, figure out which one is larger
if (constrain)
{
int test1w, test1h;
int test2w, test2h;
// first constrain with no height limit
test1w = minwidth; test1h = 10000;
constrain_to_aspect_ratio(window, &test1w, &test1h, WMSZ_BOTTOMRIGHT);
// then constrain with no width limit
test2w = 10000; test2h = minheight;
constrain_to_aspect_ratio(window, &test2w, &test2h, WMSZ_BOTTOMRIGHT);
// pick the larger
if ( test1w > test2w )
{
minwidth = test1w;
minheight = test1h;
}
else
{
minwidth = test2w;
minheight = test2h;
}
}
*window_width = minwidth;
*window_height = minheight;
}
//============================================================
// get_max_bounds
// (window thread)
//============================================================
static void get_max_bounds(sdl_window_info *window, int *window_width, int *window_height, int constrain)
{
INT32 maxwidth, maxheight;
// compute the maximum client area
maxwidth = window->monitor->center_width;
maxheight = window->monitor->center_height;
// clamp to the window's max
if (window->maxwidth != 0)
{
int temp = window->maxwidth + WINDOW_DECORATION_WIDTH;
if (temp < maxwidth)
maxwidth = temp;
}
if (window->maxheight != 0)
{
int temp = window->maxheight + WINDOW_DECORATION_HEIGHT;
if (temp < maxheight)
maxheight = temp;
}
// constrain to fit
if (constrain)
constrain_to_aspect_ratio(window, &maxwidth, &maxheight, WMSZ_BOTTOMRIGHT);
//else
{
maxwidth -= WINDOW_DECORATION_WIDTH;
maxheight -= WINDOW_DECORATION_HEIGHT;
*window_width = maxwidth;
*window_height = maxheight;
}
}