--- BasiliskII/src/SDL/video_sdl.cpp 2006/05/11 07:51:32 1.25 +++ BasiliskII/src/SDL/video_sdl.cpp 2011/12/29 07:38:34 1.43 @@ -1,7 +1,7 @@ /* * video_sdl.cpp - Video/graphics emulation, SDL specific stuff * - * Basilisk II (C) 1997-2005 Christian Bauer + * Basilisk II (C) 1997-2008 Christian Bauer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -27,16 +27,15 @@ * Ctrl-F5 = grab mouse (in windowed mode) * * FIXMEs and TODOs: + * - Windows requires an extra mouse event to update the actual cursor image? * - Ctr-Tab for suspend/resume but how? SDL does not support that for non-Linux * - Ctrl-Fn doesn't generate SDL_KEYDOWN events (SDL bug?) * - Mouse acceleration, there is no API in SDL yet for that * - Force relative mode in Grab mode even if SDL provides absolute coordinates? - * - Fullscreen mode * - Gamma tables support is likely to be broken here * - Events processing is bound to the general emulation thread as SDL requires * to PumpEvents() within the same thread as the one that called SetVideoMode(). * Besides, there can't seem to be a way to call SetVideoMode() from a child thread. - * - Refresh performance is still slow. Use SDL_CreateRGBSurface()? * - Backport hw cursor acceleration to Basilisk II? * - Factor out code */ @@ -49,6 +48,10 @@ #include #include +#ifdef WIN32 +#include /* alloca() */ +#endif + #include "cpu_emulation.h" #include "main.h" #include "adb.h" @@ -60,10 +63,13 @@ #include "video_blit.h" #include "vm_alloc.h" +#if (defined(__APPLE__) && defined(__MACH__)) +#include "utils_macosx.h" +#endif + #define DEBUG 0 #include "debug.h" - // Supported video modes using std::vector; static vector VideoModes; @@ -133,7 +139,7 @@ static SDL_Cursor *sdl_cursor; // C static volatile bool cursor_changed = false; // Flag: cursor changed, redraw_func must update the cursor static SDL_Color sdl_palette[256]; // Color palette to be used as CLUT and gamma table static bool sdl_palette_changed = false; // Flag: Palette changed, redraw thread must set new colors -static const int sdl_eventmask = SDL_MOUSEBUTTONDOWNMASK | SDL_MOUSEBUTTONUPMASK | SDL_MOUSEMOTIONMASK | SDL_KEYUPMASK | SDL_KEYDOWNMASK | SDL_VIDEOEXPOSEMASK | SDL_QUITMASK; +static const int sdl_eventmask = SDL_MOUSEEVENTMASK | SDL_KEYEVENTMASK | SDL_VIDEOEXPOSEMASK | SDL_QUITMASK | SDL_ACTIVEEVENTMASK; // Mutex to protect SDL events static SDL_mutex *sdl_events_lock = NULL; @@ -194,7 +200,19 @@ extern void SysMountFirstFloppy(void); static void *vm_acquire_framebuffer(uint32 size) { - return vm_acquire(size); + // always try to reallocate framebuffer at the same address + static void *fb = VM_MAP_FAILED; + if (fb != VM_MAP_FAILED) { + if (vm_acquire_fixed(fb, size) < 0) { +#ifndef SHEEPSHAVER + printf("FATAL: Could not reallocate framebuffer at previous address\n"); +#endif + fb = VM_MAP_FAILED; + } + } + if (fb == VM_MAP_FAILED) + fb = vm_acquire(size, VM_MAP_DEFAULT | VM_MAP_32BIT); + return fb; } static inline void vm_release_framebuffer(void *fb, uint32 size) @@ -610,12 +628,12 @@ public: class driver_window; static void update_display_window_vosf(driver_window *drv); static void update_display_dynamic(int ticker, driver_window *drv); -static void update_display_static(driver_window *drv); +static void update_display_static(driver_base *drv); class driver_window : public driver_base { friend void update_display_window_vosf(driver_window *drv); friend void update_display_dynamic(int ticker, driver_window *drv); - friend void update_display_static(driver_window *drv); + friend void update_display_static(driver_base *drv); public: driver_window(SDL_monitor_desc &monitor); @@ -716,6 +734,8 @@ void driver_base::restore_mouse_accel(vo * Windowed display driver */ +static bool SDL_display_opened = false; + // Open display driver_window::driver_window(SDL_monitor_desc &m) : driver_base(m), mouse_grabbed(false) @@ -727,11 +747,23 @@ driver_window::driver_window(SDL_monitor // Set absolute mouse mode ADBSetRelMouseMode(mouse_grabbed); + // This is ugly: + // If we're switching resolutions (ie, not setting it for the first time), + // there's a bug in SDL where the SDL_Surface created will not be properly + // setup. The solution is to SDL_QuitSubSystem(SDL_INIT_VIDEO) before calling + // SDL_SetVideoMode for the second time (SDL_SetVideoMode will call SDL_Init() + // and all will be well). Without this, the video becomes corrupted (at least + // on Mac OS X), after the resolution switch. + if (SDL_display_opened) + SDL_QuitSubSystem(SDL_INIT_VIDEO); + // Create surface int depth = sdl_depth_of_video_depth(VIDEO_MODE_DEPTH); if ((s = SDL_SetVideoMode(width, height, depth, SDL_HWSURFACE)) == NULL) return; + SDL_display_opened = true; + #ifdef ENABLE_VOSF use_vosf = true; // Allocate memory for frame buffer (SIZE is extended to page-boundary) @@ -1209,12 +1241,8 @@ bool VideoInit(bool classic) continue; if (w == 512 && h == 384) continue; -#ifdef ENABLE_VOSF for (int d = VIDEO_DEPTH_1BIT; d <= default_depth; d++) add_mode(display_type, w, h, video_modes[i].resolution_id, TrivialBytesPerRow(w, (video_depth)d), d); -#else - add_mode(display_type, w, h, video_modes[i].resolution_id, TrivialBytesPerRow(w, (video_depth)default_depth), default_depth); -#endif } } @@ -1514,7 +1542,23 @@ void SDL_monitor_desc::switch_to_current #ifdef SHEEPSHAVER bool video_can_change_cursor(void) { - return (display_type == DISPLAY_WINDOW); + static char driver[] = "Quartz?"; + static int quartzok = -1; + + if (display_type != DISPLAY_WINDOW) + return false; + + if (quartzok < 0) { + if (SDL_VideoDriverName(driver, sizeof driver) == NULL || strncmp(driver, "Quartz", sizeof driver)) + quartzok = true; + else { + // Quartz driver bug prevents cursor changing in SDL 1.2.11 and later + const SDL_version *vp = SDL_Linked_Version(); + quartzok = SDL_VERSIONNUM(vp->major, vp->minor, vp->patch) <= SDL_VERSIONNUM(1, 2, 10); + } + } + + return quartzok; } #endif @@ -1725,8 +1769,12 @@ static void handle_events(void) // Mouse button case SDL_MOUSEBUTTONDOWN: { unsigned int button = event.button.button; - if (button < 4) - ADBMouseDown(button - 1); + if (button == SDL_BUTTON_LEFT) + ADBMouseDown(0); + else if (button == SDL_BUTTON_RIGHT) + ADBMouseDown(1); + else if (button == SDL_BUTTON_MIDDLE) + ADBMouseDown(2); else if (button < 6) { // Wheel mouse if (mouse_wheel_mode == 0) { int key = (button == 5) ? 0x79 : 0x74; // Page up/down @@ -1744,8 +1792,12 @@ static void handle_events(void) } case SDL_MOUSEBUTTONUP: { unsigned int button = event.button.button; - if (button < 4) - ADBMouseUp(button - 1); + if (button == SDL_BUTTON_LEFT) + ADBMouseUp(0); + else if (button == SDL_BUTTON_RIGHT) + ADBMouseUp(1); + else if (button == SDL_BUTTON_MIDDLE) + ADBMouseUp(2); break; } @@ -1829,6 +1881,10 @@ static void handle_events(void) ADBKeyDown(0x7f); // Power key ADBKeyUp(0x7f); break; + + // Application activate/deactivate; consume the event but otherwise ignore it + case SDL_ACTIVEEVENT: + break; } } } @@ -1840,7 +1896,7 @@ static void handle_events(void) */ // Static display update (fixed frame rate, but incremental) -static void update_display_static(driver_window *drv) +static void update_display_static(driver_base *drv) { // Incremental update code int wide = 0, high = 0, x1, x2, y1, y2, i, j; @@ -1929,6 +1985,7 @@ static void update_display_static(driver } else { const int bytes_per_pixel = VIDEO_MODE_ROW_BYTES / VIDEO_MODE_X; + const int dst_bytes_per_row = drv->s->pitch; x1 = VIDEO_MODE_X; for (j=y1; j<=y2; j++) { @@ -1969,8 +2026,9 @@ static void update_display_static(driver // Blit to screen surface for (j=y1; j<=y2; j++) { i = j * bytes_per_row + x1 * bytes_per_pixel; + int dst_i = j * dst_bytes_per_row + x1 * bytes_per_pixel; memcpy(the_buffer_copy + i, the_buffer + i, bytes_per_pixel * wide); - Screen_blit((uint8 *)drv->s->pixels + i, the_buffer + i, bytes_per_pixel * wide); + Screen_blit((uint8 *)drv->s->pixels + dst_i, the_buffer + i, bytes_per_pixel * wide); } // Unlock surface, if required @@ -1986,7 +2044,7 @@ static void update_display_static(driver // Static display update (fixed frame rate, bounding boxes based) // XXX use NQD bounding boxes to help detect dirty areas? -static void update_display_static_bbox(driver_window *drv) +static void update_display_static_bbox(driver_base *drv) { const VIDEO_MODE &mode = drv->mode; @@ -2004,6 +2062,7 @@ static void update_display_static_bbox(d // Update the surface from Mac screen const int bytes_per_row = VIDEO_MODE_ROW_BYTES; const int bytes_per_pixel = bytes_per_row / VIDEO_MODE_X; + const int dst_bytes_per_row = drv->s->pitch; int x, y; for (y = 0; y < VIDEO_MODE_Y; y += N_PIXELS) { int h = N_PIXELS; @@ -2018,9 +2077,10 @@ static void update_display_static_bbox(d bool dirty = false; for (int j = y; j < (y + h); j++) { const int yb = j * bytes_per_row; + const int dst_yb = j * dst_bytes_per_row; if (memcmp(&the_buffer[yb + xb], &the_buffer_copy[yb + xb], xs) != 0) { memcpy(&the_buffer_copy[yb + xb], &the_buffer[yb + xb], xs); - Screen_blit((uint8 *)drv->s->pixels + yb + xb, the_buffer + yb + xb, xs); + Screen_blit((uint8 *)drv->s->pixels + dst_yb + xb, the_buffer + yb + xb, xs); dirty = true; } } @@ -2084,10 +2144,13 @@ static inline void handle_palette_change UNLOCK_PALETTE; } +static void video_refresh_window_static(void); + static void video_refresh_dga(void) { // Quit DGA mode if requested possibly_quit_dga_mode(); + video_refresh_window_static(); } #ifdef ENABLE_VOSF @@ -2139,9 +2202,9 @@ static void video_refresh_window_static( tick_counter = 0; const VIDEO_MODE &mode = drv->mode; if ((int)VIDEO_MODE_DEPTH >= VIDEO_DEPTH_8BIT) - update_display_static_bbox(static_cast(drv)); + update_display_static_bbox(drv); else - update_display_static(static_cast(drv)); + update_display_static(drv); } } @@ -2177,7 +2240,12 @@ static inline void do_video_refresh(void handle_events(); // Update display +#if (defined(__APPLE__) && defined(__MACH__)) + // SDL expects an auto-release pool to be present. + NSAutoReleasePool_wrap(video_refresh); +#else video_refresh(); +#endif #ifdef SHEEPSHAVER // Set new cursor image if it was changed @@ -2186,8 +2254,20 @@ static inline void do_video_refresh(void LOCK_EVENTS; SDL_FreeCursor(sdl_cursor); sdl_cursor = SDL_CreateCursor(MacCursor + 4, MacCursor + 36, 16, 16, MacCursor[2], MacCursor[3]); - if (sdl_cursor) + if (sdl_cursor) { + SDL_ShowCursor(private_data == NULL || private_data->cursorVisible); SDL_SetCursor(sdl_cursor); +#ifdef WIN32 + // XXX Windows apparently needs an extra mouse event to + // make the new cursor image visible + int visible = SDL_ShowCursor(-1); + if (visible) { + int x, y; + SDL_GetMouseState(&x, &y); + SDL_WarpMouse(x, y); + } +#endif + } UNLOCK_EVENTS; } #endif @@ -2246,3 +2326,27 @@ static int redraw_func(void *arg) return 0; } #endif + + +/* + * Record dirty area from NQD + */ + +#ifdef SHEEPSHAVER +void video_set_dirty_area(int x, int y, int w, int h) +{ +#ifdef ENABLE_VOSF + const VIDEO_MODE &mode = drv->mode; + const int screen_width = VIDEO_MODE_X; + const int screen_height = VIDEO_MODE_Y; + const int bytes_per_row = VIDEO_MODE_ROW_BYTES; + + if (use_vosf) { + vosf_set_dirty_area(x, y, w, h, screen_width, screen_height, bytes_per_row); + return; + } +#endif + + // XXX handle dirty bounding boxes for non-VOSF modes +} +#endif