--- BasiliskII/src/SDL/video_sdl.cpp 2004/06/24 21:46:55 1.5 +++ BasiliskII/src/SDL/video_sdl.cpp 2008/01/01 09:40:32 1.34 @@ -1,7 +1,7 @@ /* * video_sdl.cpp - Video/graphics emulation, SDL specific stuff * - * Basilisk II (C) 1997-2004 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 @@ -21,20 +21,23 @@ /* * NOTES: * The Ctrl key works like a qualifier for special actions: - * Ctrl-Tab = suspend DGA mode + * Ctrl-Tab = suspend DGA mode (TODO) * Ctrl-Esc = emergency quit * Ctrl-F1 = mount floppy * 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. + * - Backport hw cursor acceleration to Basilisk II? + * - Factor out code */ #include "sysdeps.h" @@ -45,6 +48,10 @@ #include #include +#ifdef WIN32 +#include /* alloca() */ +#endif + #include "cpu_emulation.h" #include "main.h" #include "adb.h" @@ -54,6 +61,7 @@ #include "video.h" #include "video_defs.h" #include "video_blit.h" +#include "vm_alloc.h" #define DEBUG 0 #include "debug.h" @@ -79,7 +87,11 @@ static int display_type = DISPLAY_WINDOW #endif // Constants +#ifdef WIN32 +const char KEYCODE_FILE_NAME[] = "BasiliskII_keycodes"; +#else const char KEYCODE_FILE_NAME[] = DATADIR "/keycodes"; +#endif // Global variables @@ -92,11 +104,17 @@ static uint8 *the_buffer_copy = NULL; static uint32 the_buffer_size; // Size of allocated the_buffer static bool redraw_thread_active = false; // Flag: Redraw thread installed +#ifndef USE_CPU_EMUL_SERVICES static volatile bool redraw_thread_cancel; // Flag: Cancel Redraw thread static SDL_Thread *redraw_thread = NULL; // Redraw thread +#ifdef SHEEPSHAVER +static volatile bool thread_stop_req = false; +static volatile bool thread_stop_ack = false; // Acknowledge for thread_stop_req +#endif +#endif #ifdef ENABLE_VOSF -static bool use_vosf = true; // Flag: VOSF enabled +static bool use_vosf = false; // Flag: VOSF enabled #else static const bool use_vosf = false; // VOSF not possible #endif @@ -114,10 +132,17 @@ static int keycode_table[256]; // X // SDL variables static int screen_depth; // Depth of current screen +static SDL_Cursor *sdl_cursor; // Copy of Mac cursor +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; +// Mutex to protect SDL events +static SDL_mutex *sdl_events_lock = NULL; +#define LOCK_EVENTS SDL_LockMutex(sdl_events_lock) +#define UNLOCK_EVENTS SDL_UnlockMutex(sdl_events_lock) + // Mutex to protect palette static SDL_mutex *sdl_palette_lock = NULL; #define LOCK_PALETTE SDL_LockMutex(sdl_palette_lock) @@ -141,6 +166,97 @@ extern void SysMountFirstFloppy(void); /* + * SDL surface locking glue + */ + +#ifdef ENABLE_VOSF +#define SDL_VIDEO_LOCK_VOSF_SURFACE(SURFACE) do { \ + if ((SURFACE)->flags & (SDL_HWSURFACE | SDL_FULLSCREEN)) \ + the_host_buffer = (uint8 *)(SURFACE)->pixels; \ +} while (0) +#else +#define SDL_VIDEO_LOCK_VOSF_SURFACE(SURFACE) +#endif + +#define SDL_VIDEO_LOCK_SURFACE(SURFACE) do { \ + if (SDL_MUSTLOCK(SURFACE)) { \ + SDL_LockSurface(SURFACE); \ + SDL_VIDEO_LOCK_VOSF_SURFACE(SURFACE); \ + } \ +} while (0) + +#define SDL_VIDEO_UNLOCK_SURFACE(SURFACE) do { \ + if (SDL_MUSTLOCK(SURFACE)) \ + SDL_UnlockSurface(SURFACE); \ +} while (0) + + +/* + * Framebuffer allocation routines + */ + +static void *vm_acquire_framebuffer(uint32 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) +{ + vm_release(fb, size); +} + + +/* + * Windows message handler + */ + +#ifdef WIN32 +#include +static WNDPROC sdl_window_proc = NULL; // Window proc used by SDL + +extern void SysMediaArrived(void); +extern void SysMediaRemoved(void); +extern HWND GetMainWindowHandle(void); + +static LRESULT CALLBACK windows_message_handler(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) { + case WM_DEVICECHANGE: + if (wParam == DBT_DEVICEREMOVECOMPLETE) { + DEV_BROADCAST_HDR *p = (DEV_BROADCAST_HDR *)lParam; + if (p->dbch_devicetype == DBT_DEVTYP_VOLUME) + SysMediaRemoved(); + } + else if (wParam == DBT_DEVICEARRIVAL) { + DEV_BROADCAST_HDR *p = (DEV_BROADCAST_HDR *)lParam; + if (p->dbch_devicetype == DBT_DEVTYP_VOLUME) + SysMediaArrived(); + } + return 0; + + default: + if (sdl_window_proc) + return CallWindowProc(sdl_window_proc, hwnd, msg, wParam, lParam); + } + + return DefWindowProc(hwnd, msg, wParam, lParam); +} +#endif + + +/* * SheepShaver glue */ @@ -185,69 +301,21 @@ static vector VideoMonit // Find Apple mode matching best specified dimensions static int find_apple_resolution(int xsize, int ysize) { - int apple_id; - if (xsize < 800) - apple_id = APPLE_640x480; - else if (xsize < 1024) - apple_id = APPLE_800x600; - else if (xsize < 1152) - apple_id = APPLE_1024x768; - else if (xsize < 1280) { - if (ysize < 900) - apple_id = APPLE_1152x768; - else - apple_id = APPLE_1152x900; - } - else if (xsize < 1600) - apple_id = APPLE_1280x1024; - else - apple_id = APPLE_1600x1200; - return apple_id; -} - -// Set parameters to specified Apple mode -static void set_apple_resolution(int apple_id, int &xsize, int &ysize) -{ - switch (apple_id) { - case APPLE_640x480: - xsize = 640; - ysize = 480; - break; - case APPLE_800x600: - xsize = 800; - ysize = 600; - break; - case APPLE_1024x768: - xsize = 1024; - ysize = 768; - break; - case APPLE_1152x768: - xsize = 1152; - ysize = 768; - break; - case APPLE_1152x900: - xsize = 1152; - ysize = 900; - break; - case APPLE_1280x1024: - xsize = 1280; - ysize = 1024; - break; - case APPLE_1600x1200: - xsize = 1600; - ysize = 1200; - break; - default: - abort(); - } -} - -// Match Apple mode matching best specified dimensions -static int match_apple_resolution(int &xsize, int &ysize) -{ - int apple_id = find_apple_resolution(xsize, ysize); - set_apple_resolution(apple_id, xsize, ysize); - return apple_id; + if (xsize == 640 && ysize == 480) + return APPLE_640x480; + if (xsize == 800 && ysize == 600) + return APPLE_800x600; + if (xsize == 1024 && ysize == 768) + return APPLE_1024x768; + if (xsize == 1152 && ysize == 768) + return APPLE_1152x768; + if (xsize == 1152 && ysize == 900) + return APPLE_1152x900; + if (xsize == 1280 && ysize == 1024) + return APPLE_1280x1024; + if (xsize == 1600 && ysize == 1200) + return APPLE_1600x1200; + return APPLE_CUSTOM; } // Display error alert @@ -255,6 +323,12 @@ static void ErrorAlert(int error) { ErrorAlert(GetString(error)); } + +// Display warning alert +static void WarningAlert(int warning) +{ + WarningAlert(GetString(warning)); +} #endif @@ -314,7 +388,7 @@ static inline int bytes_per_pixel(int de } // Map video_mode depth ID to numerical depth value -static int sdl_depth_of_video_depth(int video_depth) +static int mac_depth_of_video_depth(int video_depth) { int depth = -1; switch (video_depth) { @@ -342,83 +416,90 @@ static int sdl_depth_of_video_depth(int return depth; } -// Check wether specified mode is available -static bool has_mode(int type, int width, int height) +// Map video_mode depth ID to SDL screen depth +static int sdl_depth_of_video_depth(int video_depth) { - // FIXME: no fullscreen support yet - if (type == DISPLAY_SCREEN) - return false; + return (video_depth <= VIDEO_DEPTH_8BIT) ? 8 : mac_depth_of_video_depth(video_depth); +} + +// Get screen dimensions +static void sdl_display_dimensions(int &width, int &height) +{ + static int max_width, max_height; + if (max_width == 0 && max_height == 0) { + max_width = 640 ; max_height = 480; + SDL_Rect **modes = SDL_ListModes(NULL, SDL_FULLSCREEN | SDL_HWSURFACE); + if (modes && modes != (SDL_Rect **)-1) { + // It turns out that on some implementations, and contrary to the documentation, + // the returned list is not sorted from largest to smallest (e.g. Windows) + for (int i = 0; modes[i] != NULL; i++) { + const int w = modes[i]->w; + const int h = modes[i]->h; + if (w > max_width && h > max_height) { + max_width = w; + max_height = h; + } + } + } + } + width = max_width; + height = max_height; +} + +static inline int sdl_display_width(void) +{ + int width, height; + sdl_display_dimensions(width, height); + return width; +} + +static inline int sdl_display_height(void) +{ + int width, height; + sdl_display_dimensions(width, height); + return height; +} +// Check wether specified mode is available +static bool has_mode(int type, int width, int height, int depth) +{ #ifdef SHEEPSHAVER - // Filter out Classic resolutiosn + // Filter out Classic resolutions if (width == 512 && height == 384) return false; - - // Read window modes prefs - static uint32 window_modes = 0; - static uint32 screen_modes = 0; - if (window_modes == 0 || screen_modes == 0) { - window_modes = PrefsFindInt32("windowmodes"); - screen_modes = PrefsFindInt32("screenmodes"); - if (window_modes == 0 || screen_modes == 0) - window_modes |= 3; // Allow at least 640x480 and 800x600 window modes - } - - if (type == DISPLAY_WINDOW) { - int apple_mask, apple_id = find_apple_resolution(width, height); - switch (apple_id) { - case APPLE_640x480: apple_mask = 0x01; break; - case APPLE_800x600: apple_mask = 0x02; break; - case APPLE_1024x768: apple_mask = 0x04; break; - case APPLE_1152x768: apple_mask = 0x40; break; - case APPLE_1152x900: apple_mask = 0x08; break; - case APPLE_1280x1024: apple_mask = 0x10; break; - case APPLE_1600x1200: apple_mask = 0x20; break; - default: apple_mask = 0x00; break; - } - return (window_modes & apple_mask); - } -#else - return true; #endif - return false; + + // Filter out out-of-bounds resolutions + if (width > sdl_display_width() || height > sdl_display_height()) + return false; + + // Rely on SDL capabilities + return SDL_VideoModeOK(width, height, + sdl_depth_of_video_depth(depth), + SDL_HWSURFACE | (type == DISPLAY_SCREEN ? SDL_FULLSCREEN : 0)); } // Add mode to list of supported modes static void add_mode(int type, int width, int height, int resolution_id, int bytes_per_row, int depth) { // Filter out unsupported modes - if (!has_mode(type, width, height)) + if (!has_mode(type, width, height, depth)) return; // Fill in VideoMode entry VIDEO_MODE mode; #ifdef SHEEPSHAVER - // Recalculate dimensions to fit Apple modes - resolution_id = match_apple_resolution(width, height); + resolution_id = find_apple_resolution(width, height); mode.viType = type; #endif VIDEO_MODE_X = width; VIDEO_MODE_Y = height; VIDEO_MODE_RESOLUTION = resolution_id; VIDEO_MODE_ROW_BYTES = bytes_per_row; - VIDEO_MODE_DEPTH = depth; + VIDEO_MODE_DEPTH = (video_depth)depth; VideoModes.push_back(mode); } -// Add standard list of windowed modes for given color depth -static void add_window_modes(int depth) -{ - video_depth vdepth = (video_depth)depth; - add_mode(DISPLAY_WINDOW, 512, 384, 0x80, TrivialBytesPerRow(512, vdepth), depth); - add_mode(DISPLAY_WINDOW, 640, 480, 0x81, TrivialBytesPerRow(640, vdepth), depth); - add_mode(DISPLAY_WINDOW, 800, 600, 0x82, TrivialBytesPerRow(800, vdepth), depth); - add_mode(DISPLAY_WINDOW, 1024, 768, 0x83, TrivialBytesPerRow(1024, vdepth), depth); - add_mode(DISPLAY_WINDOW, 1152, 870, 0x84, TrivialBytesPerRow(1152, vdepth), depth); - add_mode(DISPLAY_WINDOW, 1280, 1024, 0x85, TrivialBytesPerRow(1280, vdepth), depth); - add_mode(DISPLAY_WINDOW, 1600, 1200, 0x86, TrivialBytesPerRow(1600, vdepth), depth); -} - // Set Mac frame layout and base address (uses the_buffer/MacFrameBaseMac) static void set_mac_frame_buffer(SDL_monitor_desc &monitor, int depth, bool native_byte_order) { @@ -458,10 +539,59 @@ static void set_window_name(int name) // Set mouse grab mode static SDL_GrabMode set_grab_mode(SDL_GrabMode mode) { - const SDL_VideoInfo *vi =SDL_GetVideoInfo(); + const SDL_VideoInfo *vi = SDL_GetVideoInfo(); return (vi && vi->wm_available ? SDL_WM_GrabInput(mode) : SDL_GRAB_OFF); } +// Migrate preferences items (XXX to be handled in MigratePrefs()) +static void migrate_screen_prefs(void) +{ +#ifdef SHEEPSHAVER + // Look-up priorities are: "screen", "screenmodes", "windowmodes". + if (PrefsFindString("screen")) + return; + + uint32 window_modes = PrefsFindInt32("windowmodes"); + uint32 screen_modes = PrefsFindInt32("screenmodes"); + int width = 0, height = 0; + if (screen_modes) { + static const struct { + int id; + int width; + int height; + } + modes[] = { + { 1, 640, 480 }, + { 2, 800, 600 }, + { 4, 1024, 768 }, + { 64, 1152, 768 }, + { 8, 1152, 900 }, + { 16, 1280, 1024 }, + { 32, 1600, 1200 }, + { 0, } + }; + for (int i = 0; modes[i].id != 0; i++) { + if (screen_modes & modes[i].id) { + if (width < modes[i].width && height < modes[i].height) { + width = modes[i].width; + height = modes[i].height; + } + } + } + } else { + if (window_modes & 1) + width = 640, height = 480; + if (window_modes & 2) + width = 800, height = 600; + } + if (width && height) { + char str[32]; + sprintf(str, "%s/%d/%d", screen_modes ? "dga" : "win", width, height); + PrefsReplaceString("screen", str); + } +#endif +} + /* * Display "driver" classes @@ -517,6 +647,12 @@ private: int mouse_last_x, mouse_last_y; // Last mouse position (for relative mode) }; +class driver_fullscreen : public driver_base { +public: + driver_fullscreen(SDL_monitor_desc &monitor); + ~driver_fullscreen(); +}; + static driver_base *drv = NULL; // Pointer to currently used driver object #ifdef ENABLE_VOSF @@ -538,12 +674,15 @@ driver_base::~driver_base() if (s) SDL_FreeSurface(s); + // the_buffer shall always be mapped through vm_acquire_framebuffer() + if (the_buffer != VM_MAP_FAILED) { + D(bug(" releasing the_buffer at %p (%d bytes)\n", the_buffer, the_buffer_size)); + vm_release_framebuffer(the_buffer, the_buffer_size); + the_buffer = NULL; + } + // Free frame buffer(s) if (!use_vosf) { - if (the_buffer) { - free(the_buffer); - the_buffer = NULL; - } if (the_buffer_copy) { free(the_buffer_copy); the_buffer_copy = NULL; @@ -551,12 +690,6 @@ driver_base::~driver_base() } #ifdef ENABLE_VOSF else { - // the_buffer shall always be mapped through vm_acquire() so that we can vm_protect() it at will - if (the_buffer != VM_MAP_FAILED) { - D(bug(" releasing the_buffer at %p (%d bytes)\n", the_buffer, the_buffer_size)); - vm_release(the_buffer, the_buffer_size); - the_buffer = NULL; - } if (the_host_buffer) { D(bug(" freeing the_host_buffer at %p\n", the_host_buffer)); free(the_host_buffer); @@ -567,6 +700,9 @@ driver_base::~driver_base() free(the_buffer_copy); the_buffer_copy = NULL; } + + // Deinitialize VOSF + video_vosf_exit(); } #endif } @@ -576,7 +712,7 @@ void driver_base::update_palette(void) { const VIDEO_MODE &mode = monitor.get_current_mode(); - if (VIDEO_MODE_DEPTH <= VIDEO_DEPTH_8BIT) + if ((int)VIDEO_MODE_DEPTH <= VIDEO_DEPTH_8BIT) SDL_SetPalette(s, SDL_PHYSPAL, sdl_palette, 0, 256); } @@ -595,6 +731,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) @@ -606,33 +744,70 @@ 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 = (VIDEO_MODE_DEPTH <= VIDEO_DEPTH_8BIT ? 8 : screen_depth); + 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) the_host_buffer = (uint8 *)s->pixels; the_buffer_size = page_extend((aligned_height + 2) * s->pitch); - the_buffer = (uint8 *)vm_acquire(the_buffer_size); + the_buffer = (uint8 *)vm_acquire_framebuffer(the_buffer_size); the_buffer_copy = (uint8 *)malloc(the_buffer_size); D(bug("the_buffer = %p, the_buffer_copy = %p, the_host_buffer = %p\n", the_buffer, the_buffer_copy, the_host_buffer)); + + // Check whether we can initialize the VOSF subsystem and it's profitable + if (!video_vosf_init(m)) { + WarningAlert(STR_VOSF_INIT_ERR); + use_vosf = false; + } + else if (!video_vosf_profitable()) { + video_vosf_exit(); + printf("VOSF acceleration is not profitable on this platform, disabling it\n"); + use_vosf = false; + } + if (!use_vosf) { + free(the_buffer_copy); + vm_release(the_buffer, the_buffer_size); + the_host_buffer = NULL; + } +#endif + if (!use_vosf) { + // Allocate memory for frame buffer + the_buffer_size = (aligned_height + 2) * s->pitch; + the_buffer_copy = (uint8 *)calloc(1, the_buffer_size); + the_buffer = (uint8 *)vm_acquire_framebuffer(the_buffer_size); + D(bug("the_buffer = %p, the_buffer_copy = %p\n", the_buffer, the_buffer_copy)); + } + +#ifdef SHEEPSHAVER + // Create cursor + if ((sdl_cursor = SDL_CreateCursor(MacCursor + 4, MacCursor + 36, 16, 16, 0, 0)) != NULL) { + SDL_SetCursor(sdl_cursor); + cursor_changed = false; + } #else - // Allocate memory for frame buffer - the_buffer_size = (aligned_height + 2) * s->pitch; - the_buffer_copy = (uint8 *)calloc(1, the_buffer_size); - the_buffer = (uint8 *)calloc(1, the_buffer_size); - D(bug("the_buffer = %p, the_buffer_copy = %p\n", the_buffer, the_buffer_copy)); + // Hide cursor + SDL_ShowCursor(0); #endif // Set window name/class set_window_name(STR_WINDOW_TITLE); - // Hide cursor - SDL_ShowCursor(0); - // Init blitting routines SDL_PixelFormat *f = s->format; VisualFormat visualFormat; @@ -640,7 +815,7 @@ driver_window::driver_window(SDL_monitor visualFormat.Rmask = f->Rmask; visualFormat.Gmask = f->Gmask; visualFormat.Bmask = f->Bmask; - Screen_blitter_init(visualFormat, true, sdl_depth_of_video_depth(VIDEO_MODE_DEPTH)); + Screen_blitter_init(visualFormat, true, mac_depth_of_video_depth(VIDEO_MODE_DEPTH)); // Load gray ramp to 8->16/32 expand map if (!IsDirectMode(mode)) @@ -707,6 +882,99 @@ void driver_window::mouse_moved(int x, i ADBMouseMoved(x, y); } + +/* + * Full-screen display driver + */ + +// Open display +driver_fullscreen::driver_fullscreen(SDL_monitor_desc &m) + : driver_base(m) +{ + int width = VIDEO_MODE_X, height = VIDEO_MODE_Y; + int aligned_width = (width + 15) & ~15; + int aligned_height = (height + 15) & ~15; + + // Set absolute mouse mode + ADBSetRelMouseMode(false); + + // Create surface + int depth = sdl_depth_of_video_depth(VIDEO_MODE_DEPTH); + if ((s = SDL_SetVideoMode(width, height, depth, SDL_HWSURFACE | SDL_FULLSCREEN)) == NULL) + return; + +#ifdef ENABLE_VOSF + use_vosf = true; + // Allocate memory for frame buffer (SIZE is extended to page-boundary) + the_host_buffer = (uint8 *)s->pixels; + the_buffer_size = page_extend((aligned_height + 2) * s->pitch); + the_buffer = (uint8 *)vm_acquire_framebuffer(the_buffer_size); + the_buffer_copy = (uint8 *)malloc(the_buffer_size); + D(bug("the_buffer = %p, the_buffer_copy = %p, the_host_buffer = %p\n", the_buffer, the_buffer_copy, the_host_buffer)); + + // Check whether we can initialize the VOSF subsystem and it's profitable + if (!video_vosf_init(m)) { + WarningAlert(STR_VOSF_INIT_ERR); + use_vosf = false; + } + else if (!video_vosf_profitable()) { + video_vosf_exit(); + printf("VOSF acceleration is not profitable on this platform, disabling it\n"); + use_vosf = false; + } + if (!use_vosf) { + free(the_buffer_copy); + vm_release(the_buffer, the_buffer_size); + the_host_buffer = NULL; + } +#endif + if (!use_vosf) { + // Allocate memory for frame buffer + the_buffer_size = (aligned_height + 2) * s->pitch; + the_buffer_copy = (uint8 *)calloc(1, the_buffer_size); + the_buffer = (uint8 *)vm_acquire_framebuffer(the_buffer_size); + D(bug("the_buffer = %p, the_buffer_copy = %p\n", the_buffer, the_buffer_copy)); + } + + // Hide cursor + SDL_ShowCursor(0); + + // Init blitting routines + SDL_PixelFormat *f = s->format; + VisualFormat visualFormat; + visualFormat.depth = depth; + visualFormat.Rmask = f->Rmask; + visualFormat.Gmask = f->Gmask; + visualFormat.Bmask = f->Bmask; + Screen_blitter_init(visualFormat, true, mac_depth_of_video_depth(VIDEO_MODE_DEPTH)); + + // Load gray ramp to 8->16/32 expand map + if (!IsDirectMode(mode)) + for (int i=0; i<256; i++) + ExpandMap[i] = SDL_MapRGB(f, i, i, i); + + // Set frame buffer base + set_mac_frame_buffer(monitor, VIDEO_MODE_DEPTH, true); + + // Everything went well + init_ok = true; +} + +// Close display +driver_fullscreen::~driver_fullscreen() +{ +#ifdef ENABLE_VOSF + if (use_vosf) + the_host_buffer = NULL; // don't free() in driver_base dtor +#endif + if (s) + SDL_FreeSurface(s); + + // Show cursor + SDL_ShowCursor(1); +} + + /* * Initialization */ @@ -738,6 +1006,7 @@ static void keycode_init(void) SDL_VideoDriverName(video_driver, sizeof(video_driver)); bool video_driver_found = false; char line[256]; + int n_keys = 0; while (fgets(line, sizeof(line) - 1, f)) { // Read line int len = strlen(line); @@ -750,22 +1019,23 @@ static void keycode_init(void) continue; if (video_driver_found) { - // Skip aliases - static const char alias_str[] = "alias"; - if (strncmp(line, alias_str, sizeof(alias_str) - 1) == 0) + // Skip aliases as long as we have read keycodes yet + // Otherwise, it's another mapping and we have to stop + static const char sdl_str[] = "sdl"; + if (strncmp(line, sdl_str, sizeof(sdl_str) - 1) == 0 && n_keys == 0) continue; // Read keycode int x_code, mac_code; if (sscanf(line, "%d %d", &x_code, &mac_code) == 2) - keycode_table[x_code & 0xff] = mac_code; + keycode_table[x_code & 0xff] = mac_code, n_keys++; else break; } else { // Search for SDL video driver string - static const char alias_sdl_str[] = "alias SDL"; - if (strncmp(line, alias_sdl_str, sizeof(alias_sdl_str) - 1) == 0) { - char *p = line + sizeof(alias_sdl_str); + static const char sdl_str[] = "sdl"; + if (strncmp(line, sdl_str, sizeof(sdl_str) - 1) == 0) { + char *p = line + sizeof(sdl_str); if (strstr(video_driver, p) == video_driver) video_driver_found = true; } @@ -783,6 +1053,8 @@ static void keycode_init(void) WarningAlert(str); return; } + + D(bug("Using SDL/%s keycodes table, %d key mappings\n", video_driver, n_keys)); } } @@ -801,6 +1073,9 @@ bool SDL_monitor_desc::video_open(void) case DISPLAY_WINDOW: drv = new(std::nothrow) driver_window(*this); break; + case DISPLAY_SCREEN: + drv = new(std::nothrow) driver_fullscreen(*this); + break; } if (drv == NULL) return false; @@ -810,16 +1085,13 @@ bool SDL_monitor_desc::video_open(void) return false; } -#ifdef ENABLE_VOSF - if (use_vosf) { - // Initialize the VOSF system - if (!video_vosf_init(*this)) { - ErrorAlert(STR_VOSF_INIT_ERR); - return false; - } - } +#ifdef WIN32 + // Chain in a new message handler for WM_DEVICECHANGE + HWND the_window = GetMainWindowHandle(); + sdl_window_proc = (WNDPROC)GetWindowLongPtr(the_window, GWLP_WNDPROC); + SetWindowLongPtr(the_window, GWLP_WNDPROC, (LONG_PTR)windows_message_handler); #endif - + // Initialize VideoRefresh function VideoRefreshInit(); @@ -827,12 +1099,16 @@ bool SDL_monitor_desc::video_open(void) LOCK_FRAME_BUFFER; // Start redraw/input thread +#ifndef USE_CPU_EMUL_SERVICES redraw_thread_cancel = false; redraw_thread_active = ((redraw_thread = SDL_CreateThread(redraw_func, NULL)) != NULL); if (!redraw_thread_active) { printf("FATAL: cannot create redraw thread\n"); return false; } +#else + redraw_thread_active = true; +#endif return true; } @@ -853,6 +1129,8 @@ bool VideoInit(bool classic) #endif // Create Mutexes + if ((sdl_events_lock = SDL_CreateMutex()) == NULL) + return false; if ((sdl_palette_lock = SDL_CreateMutex()) == NULL) return false; if ((frame_buffer_lock = SDL_CreateMutex()) == NULL) @@ -867,13 +1145,12 @@ bool VideoInit(bool classic) mouse_wheel_lines = PrefsFindInt32("mousewheellines"); // Get screen mode from preferences + migrate_screen_prefs(); const char *mode_str = NULL; -#ifndef SHEEPSHAVER if (classic_mode) mode_str = "win/512/342"; else mode_str = PrefsFindString("screen"); -#endif // Determine display type and default dimensions int default_width, default_height; @@ -889,21 +1166,17 @@ bool VideoInit(bool classic) if (mode_str) { if (sscanf(mode_str, "win/%d/%d", &default_width, &default_height) == 2) display_type = DISPLAY_WINDOW; - } - int max_width = 640, max_height = 480; - SDL_Rect **modes = SDL_ListModes(NULL, SDL_FULLSCREEN | SDL_HWSURFACE); - if (modes && modes != (SDL_Rect **)-1) { - max_width = modes[0]->w; - max_height = modes[0]->h; - if (default_width > max_width) - default_width = max_width; - if (default_height > max_height) - default_height = max_height; + else if (sscanf(mode_str, "dga/%d/%d", &default_width, &default_height) == 2) + display_type = DISPLAY_SCREEN; } if (default_width <= 0) - default_width = max_width; + default_width = sdl_display_width(); + else if (default_width > sdl_display_width()) + default_width = sdl_display_width(); if (default_height <= 0) - default_height = max_height; + default_height = sdl_display_height(); + else if (default_height > sdl_display_height()) + default_height = sdl_display_height(); // Mac screen depth follows X depth screen_depth = SDL_GetVideoInfo()->vfmt->BitsPerPixel; @@ -923,19 +1196,57 @@ bool VideoInit(bool classic) break; } + // Initialize list of video modes to try + struct { + int w; + int h; + int resolution_id; + } + video_modes[] = { + { -1, -1, 0x80 }, + { 512, 384, 0x80 }, + { 640, 480, 0x81 }, + { 800, 600, 0x82 }, + { 1024, 768, 0x83 }, + { 1152, 870, 0x84 }, + { 1280, 1024, 0x85 }, + { 1600, 1200, 0x86 }, + { 0, } + }; + video_modes[0].w = default_width; + video_modes[0].h = default_height; + // Construct list of supported modes if (display_type == DISPLAY_WINDOW) { if (classic) add_mode(display_type, 512, 342, 0x80, 64, VIDEO_DEPTH_1BIT); else { - for (int d = VIDEO_DEPTH_1BIT; d <= default_depth; d++) { - int bpp = (d <= VIDEO_DEPTH_8BIT ? 8 : sdl_depth_of_video_depth(d)); - if (SDL_VideoModeOK(max_width, max_height, bpp, SDL_HWSURFACE)) - add_window_modes(video_depth(d)); + for (int i = 0; video_modes[i].w != 0; i++) { + const int w = video_modes[i].w; + const int h = video_modes[i].h; + if (i > 0 && (w >= default_width || h >= default_height)) + continue; + 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, default_width, default_height, 0x80, TrivialBytesPerRow(default_width, (video_depth)default_depth), default_depth); + } else if (display_type == DISPLAY_SCREEN) { + for (int i = 0; video_modes[i].w != 0; i++) { + const int w = video_modes[i].w; + const int h = video_modes[i].h; + if (i > 0 && (w >= default_width || h >= default_height)) + 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 + } + } + if (VideoModes.empty()) { ErrorAlert(STR_NO_XVISUAL_ERR); return false; @@ -1006,24 +1317,25 @@ void SDL_monitor_desc::video_close(void) { D(bug("video_close()\n")); +#ifdef WIN32 + // Remove message handler for WM_DEVICECHANGE + HWND the_window = GetMainWindowHandle(); + SetWindowLongPtr(the_window, GWLP_WNDPROC, (LONG_PTR)sdl_window_proc); +#endif + // Stop redraw thread +#ifndef USE_CPU_EMUL_SERVICES if (redraw_thread_active) { redraw_thread_cancel = true; SDL_WaitThread(redraw_thread, NULL); } +#endif redraw_thread_active = false; // Unlock frame buffer UNLOCK_FRAME_BUFFER; D(bug(" frame buffer unlocked\n")); -#ifdef ENABLE_VOSF - if (use_vosf) { - // Deinitialize VOSF - video_vosf_exit(); - } -#endif - // Close display delete drv; drv = NULL; @@ -1041,6 +1353,8 @@ void VideoExit(void) SDL_DestroyMutex(frame_buffer_lock); if (sdl_palette_lock) SDL_DestroyMutex(sdl_palette_lock); + if (sdl_events_lock) + SDL_DestroyMutex(sdl_events_lock); } @@ -1121,7 +1435,7 @@ void SDL_monitor_desc::set_palette(uint8 const VIDEO_MODE &mode = get_current_mode(); // FIXME: how can we handle the gamma ramp? - if (VIDEO_MODE_DEPTH > VIDEO_DEPTH_8BIT) + if ((int)VIDEO_MODE_DEPTH > VIDEO_DEPTH_8BIT) return; LOCK_PALETTE; @@ -1146,11 +1460,13 @@ void SDL_monitor_desc::set_palette(uint8 } #ifdef ENABLE_VOSF - // We have to redraw everything because the interpretation of pixel values changed - LOCK_VOSF; - PFLAG_SET_ALL; - UNLOCK_VOSF; - memset(the_buffer_copy, 0, VIDEO_MODE_ROW_BYTES * VIDEO_MODE_Y); + if (use_vosf) { + // We have to redraw everything because the interpretation of pixel values changed + LOCK_VOSF; + PFLAG_SET_ALL; + UNLOCK_VOSF; + memset(the_buffer_copy, 0, VIDEO_MODE_ROW_BYTES * VIDEO_MODE_Y); + } #endif } @@ -1180,8 +1496,11 @@ int16 video_mode_change(VidLocals *csSav csSave->saveData = ReadMacInt32(ParamPtr + csData); csSave->savePage = ReadMacInt16(ParamPtr + csPage); - // Disable interrupts + // Disable interrupts and pause redraw thread DisableInterrupt(); + thread_stop_ack = false; + thread_stop_req = true; + while (!thread_stop_ack) ; cur_mode = i; monitor_desc *monitor = VideoMonitors[0]; @@ -1192,7 +1511,8 @@ int16 video_mode_change(VidLocals *csSav csSave->saveData=VModes[cur_mode].viAppleID;/* First mode ... */ csSave->saveMode=VModes[cur_mode].viAppleMode; - // Enable interrupts + // Enable interrupts and resume redraw thread + thread_stop_req = false; EnableInterrupt(); return noErr; } @@ -1204,8 +1524,10 @@ int16 video_mode_change(VidLocals *csSav void SDL_monitor_desc::switch_to_current_mode(void) { // Close and reopen display + LOCK_EVENTS; video_close(); video_open(); + UNLOCK_EVENTS; if (drv == NULL) { ErrorAlert(STR_OPEN_WINDOW_ERR); @@ -1221,8 +1543,7 @@ void SDL_monitor_desc::switch_to_current #ifdef SHEEPSHAVER bool video_can_change_cursor(void) { -// return hw_mac_cursor_accl && (display_type != DISPLAY_SCREEN); - return false; + return (display_type == DISPLAY_WINDOW); } #endif @@ -1234,371 +1555,42 @@ bool video_can_change_cursor(void) #ifdef SHEEPSHAVER void video_set_cursor(void) { -// cursor_changed = true; + cursor_changed = true; } #endif /* - * Install graphics acceleration + * Keyboard-related utilify functions */ -#ifdef SHEEPSHAVER -// Rectangle inversion -template< int bpp > -static inline void do_invrect(uint8 *dest, uint32 length) -{ -#define INVERT_1(PTR, OFS) ((uint8 *)(PTR))[OFS] = ~((uint8 *)(PTR))[OFS] -#define INVERT_2(PTR, OFS) ((uint16 *)(PTR))[OFS] = ~((uint16 *)(PTR))[OFS] -#define INVERT_4(PTR, OFS) ((uint32 *)(PTR))[OFS] = ~((uint32 *)(PTR))[OFS] -#define INVERT_8(PTR, OFS) ((uint64 *)(PTR))[OFS] = ~((uint64 *)(PTR))[OFS] - -#ifndef UNALIGNED_PROFITABLE - // Align on 16-bit boundaries - if (bpp < 16 && (((uintptr)dest) & 1)) { - INVERT_1(dest, 0); - dest += 1; length -= 1; - } - - // Align on 32-bit boundaries - if (bpp < 32 && (((uintptr)dest) & 2)) { - INVERT_2(dest, 0); - dest += 2; length -= 2; - } -#endif - - // Invert 8-byte words - if (length >= 8) { - const int r = (length / 8) % 8; - dest += r * 8; - - int n = ((length / 8) + 7) / 8; - switch (r) { - case 0: do { - dest += 64; - INVERT_8(dest, -8); - case 7: INVERT_8(dest, -7); - case 6: INVERT_8(dest, -6); - case 5: INVERT_8(dest, -5); - case 4: INVERT_8(dest, -4); - case 3: INVERT_8(dest, -3); - case 2: INVERT_8(dest, -2); - case 1: INVERT_8(dest, -1); - } while (--n > 0); - } - } - - // 32-bit cell to invert? - if (length & 4) { - INVERT_4(dest, 0); - if (bpp <= 16) - dest += 4; - } - - // 16-bit cell to invert? - if (bpp <= 16 && (length & 2)) { - INVERT_2(dest, 0); - if (bpp <= 8) - dest += 2; - } - - // 8-bit cell to invert? - if (bpp <= 8 && (length & 1)) - INVERT_1(dest, 0); - -#undef INVERT_1 -#undef INVERT_2 -#undef INVERT_4 -#undef INVERT_8 -} - -void NQD_invrect(uint32 p) -{ - D(bug("accl_invrect %08x\n", p)); - - // Get inversion parameters - int16 dest_X = (int16)ReadMacInt16(p + acclDestRect + 2) - (int16)ReadMacInt16(p + acclDestBoundsRect + 2); - int16 dest_Y = (int16)ReadMacInt16(p + acclDestRect + 0) - (int16)ReadMacInt16(p + acclDestBoundsRect + 0); - int16 width = (int16)ReadMacInt16(p + acclDestRect + 6) - (int16)ReadMacInt16(p + acclDestRect + 2); - int16 height = (int16)ReadMacInt16(p + acclDestRect + 4) - (int16)ReadMacInt16(p + acclDestRect + 0); - D(bug(" dest X %d, dest Y %d\n", dest_X, dest_Y)); - D(bug(" width %d, height %d, bytes_per_row %d\n", width, height, (int32)ReadMacInt32(p + acclDestRowBytes))); - - //!!?? pen_mode == 14 - - // And perform the inversion - const int bpp = bytes_per_pixel(ReadMacInt32(p + acclDestPixelSize)); - const int dest_row_bytes = (int32)ReadMacInt32(p + acclDestRowBytes); - uint8 *dest = Mac2HostAddr(ReadMacInt32(p + acclDestBaseAddr) + (dest_Y * dest_row_bytes) + (dest_X * bpp)); - width *= bpp; - switch (bpp) { - case 1: - for (int i = 0; i < height; i++) { - do_invrect<8>(dest, width); - dest += dest_row_bytes; - } - break; - case 2: - for (int i = 0; i < height; i++) { - do_invrect<16>(dest, width); - dest += dest_row_bytes; - } - break; - case 4: - for (int i = 0; i < height; i++) { - do_invrect<32>(dest, width); - dest += dest_row_bytes; - } - break; - } -} - -// Rectangle filling -template< int bpp > -static inline void do_fillrect(uint8 *dest, uint32 color, uint32 length) -{ -#define FILL_1(PTR, OFS, VAL) ((uint8 *)(PTR))[OFS] = (VAL) -#define FILL_2(PTR, OFS, VAL) ((uint16 *)(PTR))[OFS] = (VAL) -#define FILL_4(PTR, OFS, VAL) ((uint32 *)(PTR))[OFS] = (VAL) -#define FILL_8(PTR, OFS, VAL) ((uint64 *)(PTR))[OFS] = (VAL) - -#ifndef UNALIGNED_PROFITABLE - // Align on 16-bit boundaries - if (bpp < 16 && (((uintptr)dest) & 1)) { - FILL_1(dest, 0, color); - dest += 1; length -= 1; - } - - // Align on 32-bit boundaries - if (bpp < 32 && (((uintptr)dest) & 2)) { - FILL_2(dest, 0, color); - dest += 2; length -= 2; - } -#endif - - // Fill 8-byte words - if (length >= 8) { - const uint64 c = (((uint64)color) << 32) | color; - const int r = (length / 8) % 8; - dest += r * 8; - - int n = ((length / 8) + 7) / 8; - switch (r) { - case 0: do { - dest += 64; - FILL_8(dest, -8, c); - case 7: FILL_8(dest, -7, c); - case 6: FILL_8(dest, -6, c); - case 5: FILL_8(dest, -5, c); - case 4: FILL_8(dest, -4, c); - case 3: FILL_8(dest, -3, c); - case 2: FILL_8(dest, -2, c); - case 1: FILL_8(dest, -1, c); - } while (--n > 0); - } - } - - // 32-bit cell to fill? - if (length & 4) { - FILL_4(dest, 0, color); - if (bpp <= 16) - dest += 4; - } - - // 16-bit cell to fill? - if (bpp <= 16 && (length & 2)) { - FILL_2(dest, 0, color); - if (bpp <= 8) - dest += 2; - } - - // 8-bit cell to fill? - if (bpp <= 8 && (length & 1)) - FILL_1(dest, 0, color); - -#undef FILL_1 -#undef FILL_2 -#undef FILL_4 -#undef FILL_8 -} - -void NQD_fillrect(uint32 p) -{ - D(bug("accl_fillrect %08x\n", p)); - - // Get filling parameters - int16 dest_X = (int16)ReadMacInt16(p + acclDestRect + 2) - (int16)ReadMacInt16(p + acclDestBoundsRect + 2); - int16 dest_Y = (int16)ReadMacInt16(p + acclDestRect + 0) - (int16)ReadMacInt16(p + acclDestBoundsRect + 0); - int16 width = (int16)ReadMacInt16(p + acclDestRect + 6) - (int16)ReadMacInt16(p + acclDestRect + 2); - int16 height = (int16)ReadMacInt16(p + acclDestRect + 4) - (int16)ReadMacInt16(p + acclDestRect + 0); - uint32 color = htonl(ReadMacInt32(p + acclPenMode) == 8 ? ReadMacInt32(p + acclForePen) : ReadMacInt32(p + acclBackPen)); - D(bug(" dest X %d, dest Y %d\n", dest_X, dest_Y)); - D(bug(" width %d, height %d\n", width, height)); - D(bug(" bytes_per_row %d color %08x\n", (int32)ReadMacInt32(p + acclDestRowBytes), color)); - - // And perform the fill - const int bpp = bytes_per_pixel(ReadMacInt32(p + acclDestPixelSize)); - const int dest_row_bytes = (int32)ReadMacInt32(p + acclDestRowBytes); - uint8 *dest = Mac2HostAddr(ReadMacInt32(p + acclDestBaseAddr) + (dest_Y * dest_row_bytes) + (dest_X * bpp)); - width *= bpp; - switch (bpp) { - case 1: - for (int i = 0; i < height; i++) { - memset(dest, color, width); - dest += dest_row_bytes; - } - break; - case 2: - for (int i = 0; i < height; i++) { - do_fillrect<16>(dest, color, width); - dest += dest_row_bytes; - } - break; - case 4: - for (int i = 0; i < height; i++) { - do_fillrect<32>(dest, color, width); - dest += dest_row_bytes; - } - break; - } -} - -bool NQD_fillrect_hook(uint32 p) +static bool is_modifier_key(SDL_KeyboardEvent const & e) { - D(bug("accl_fillrect_hook %08x\n", p)); - - // Check if we can accelerate this fillrect - if (ReadMacInt32(p + 0x284) != 0 && ReadMacInt32(p + acclDestPixelSize) >= 8) { - const int transfer_mode = ReadMacInt32(p + acclTransferMode); - if (transfer_mode == 8) { - // Fill - WriteMacInt32(p + acclDrawProc, NativeTVECT(NATIVE_FILLRECT)); - return true; - } - else if (transfer_mode == 10) { - // Invert - WriteMacInt32(p + acclDrawProc, NativeTVECT(NATIVE_INVRECT)); - return true; - } - } - return false; -} - -// Rectangle blitting -// TODO: optimize for VOSF and target pixmap == screen -void NQD_bitblt(uint32 p) -{ - D(bug("accl_bitblt %08x\n", p)); - - // Get blitting parameters - int16 src_X = (int16)ReadMacInt16(p + acclSrcRect + 2) - (int16)ReadMacInt16(p + acclSrcBoundsRect + 2); - int16 src_Y = (int16)ReadMacInt16(p + acclSrcRect + 0) - (int16)ReadMacInt16(p + acclSrcBoundsRect + 0); - int16 dest_X = (int16)ReadMacInt16(p + acclDestRect + 2) - (int16)ReadMacInt16(p + acclDestBoundsRect + 2); - int16 dest_Y = (int16)ReadMacInt16(p + acclDestRect + 0) - (int16)ReadMacInt16(p + acclDestBoundsRect + 0); - int16 width = (int16)ReadMacInt16(p + acclDestRect + 6) - (int16)ReadMacInt16(p + acclDestRect + 2); - int16 height = (int16)ReadMacInt16(p + acclDestRect + 4) - (int16)ReadMacInt16(p + acclDestRect + 0); - D(bug(" src addr %08x, dest addr %08x\n", ReadMacInt32(p + acclSrcBaseAddr), ReadMacInt32(p + acclDestBaseAddr))); - D(bug(" src X %d, src Y %d, dest X %d, dest Y %d\n", src_X, src_Y, dest_X, dest_Y)); - D(bug(" width %d, height %d\n", width, height)); - - // And perform the blit - const int bpp = bytes_per_pixel(ReadMacInt32(p + acclSrcPixelSize)); - width *= bpp; - if ((int32)ReadMacInt32(p + acclSrcRowBytes) > 0) { - const int src_row_bytes = (int32)ReadMacInt32(p + acclSrcRowBytes); - const int dst_row_bytes = (int32)ReadMacInt32(p + acclDestRowBytes); - uint8 *src = Mac2HostAddr(ReadMacInt32(p + acclSrcBaseAddr) + (src_Y * src_row_bytes) + (src_X * bpp)); - uint8 *dst = Mac2HostAddr(ReadMacInt32(p + acclDestBaseAddr) + (dest_Y * dst_row_bytes) + (dest_X * bpp)); - for (int i = 0; i < height; i++) { - memmove(dst, src, width); - src += src_row_bytes; - dst += dst_row_bytes; - } - } - else { - const int src_row_bytes = -(int32)ReadMacInt32(p + acclSrcRowBytes); - const int dst_row_bytes = -(int32)ReadMacInt32(p + acclDestRowBytes); - uint8 *src = Mac2HostAddr(ReadMacInt32(p + acclSrcBaseAddr) + ((src_Y + height - 1) * src_row_bytes) + (src_X * bpp)); - uint8 *dst = Mac2HostAddr(ReadMacInt32(p + acclDestBaseAddr) + ((dest_Y + height - 1) * dst_row_bytes) + (dest_X * bpp)); - for (int i = height - 1; i >= 0; i--) { - memmove(dst, src, width); - src -= src_row_bytes; - dst -= dst_row_bytes; - } - } -} - -/* - BitBlt transfer modes: - 0 : srcCopy - 1 : srcOr - 2 : srcXor - 3 : srcBic - 4 : notSrcCopy - 5 : notSrcOr - 6 : notSrcXor - 7 : notSrcBic - 32 : blend - 33 : addPin - 34 : addOver - 35 : subPin - 36 : transparent - 37 : adMax - 38 : subOver - 39 : adMin - 50 : hilite -*/ - -bool NQD_bitblt_hook(uint32 p) -{ - D(bug("accl_draw_hook %08x\n", p)); - - // Check if we can accelerate this bitblt - if (ReadMacInt32(p + 0x018) + ReadMacInt32(p + 0x128) == 0 && - ReadMacInt32(p + 0x130) == 0 && - ReadMacInt32(p + acclSrcPixelSize) >= 8 && - ReadMacInt32(p + acclSrcPixelSize) == ReadMacInt32(p + acclDestPixelSize) && - (ReadMacInt32(p + acclSrcRowBytes) ^ ReadMacInt32(p + acclDestRowBytes)) >= 0 && // same sign? - ReadMacInt32(p + acclTransferMode) == 0 && // srcCopy? - ReadMacInt32(p + 0x15c) > 0) { - - // Yes, set function pointer - WriteMacInt32(p + acclDrawProc, NativeTVECT(NATIVE_BITBLT)); + switch (e.keysym.sym) { + case SDLK_NUMLOCK: + case SDLK_CAPSLOCK: + case SDLK_SCROLLOCK: + case SDLK_RSHIFT: + case SDLK_LSHIFT: + case SDLK_RCTRL: + case SDLK_LCTRL: + case SDLK_RALT: + case SDLK_LALT: + case SDLK_RMETA: + case SDLK_LMETA: + case SDLK_LSUPER: + case SDLK_RSUPER: + case SDLK_MODE: + case SDLK_COMPOSE: return true; } return false; } -// Wait for graphics operation to finish -bool NQD_sync_hook(uint32 arg) -{ - D(bug("accl_sync_hook %08x\n", arg)); - return true; -} - -void VideoInstallAccel(void) +static bool is_ctrl_down(SDL_keysym const & ks) { - // Install acceleration hooks - if (PrefsFindBool("gfxaccel")) { - D(bug("Video: Installing acceleration hooks\n")); - uint32 base; - - SheepVar bitblt_hook_info(sizeof(accl_hook_info)); - base = bitblt_hook_info.addr(); - WriteMacInt32(base + 0, NativeTVECT(NATIVE_BITBLT_HOOK)); - WriteMacInt32(base + 4, NativeTVECT(NATIVE_SYNC_HOOK)); - WriteMacInt32(base + 8, ACCL_BITBLT); - NQDMisc(6, bitblt_hook_info.ptr()); - - SheepVar fillrect_hook_info(sizeof(accl_hook_info)); - base = fillrect_hook_info.addr(); - WriteMacInt32(base + 0, NativeTVECT(NATIVE_FILLRECT_HOOK)); - WriteMacInt32(base + 4, NativeTVECT(NATIVE_SYNC_HOOK)); - WriteMacInt32(base + 8, ACCL_FILLRECT); - NQDMisc(6, fillrect_hook_info.ptr()); - } + return ctrl_down || (ks.mod & KMOD_CTRL); } -#endif /* @@ -1606,11 +1598,6 @@ void VideoInstallAccel(void) * and -2 if the key was recognized as a hotkey */ -static bool is_ctrl_down(SDL_keysym const & ks) -{ - return ctrl_down || (ks.mod & KMOD_CTRL); -} - static int kc_decode(SDL_keysym const & ks, bool key_down) { switch (ks.sym) { @@ -1643,23 +1630,23 @@ static int kc_decode(SDL_keysym const & case SDLK_1: case SDLK_EXCLAIM: return 0x12; case SDLK_2: case SDLK_AT: return 0x13; -// case SDLK_3: case SDLK_numbersign: return 0x14; + case SDLK_3: case SDLK_HASH: return 0x14; case SDLK_4: case SDLK_DOLLAR: return 0x15; -// case SDLK_5: case SDLK_percent: return 0x17; + case SDLK_5: return 0x17; case SDLK_6: return 0x16; case SDLK_7: return 0x1a; case SDLK_8: return 0x1c; case SDLK_9: return 0x19; case SDLK_0: return 0x1d; -// case SDLK_BACKQUOTE: case SDLK_asciitilde: return 0x0a; + case SDLK_BACKQUOTE: return 0x0a; case SDLK_MINUS: case SDLK_UNDERSCORE: return 0x1b; case SDLK_EQUALS: case SDLK_PLUS: return 0x18; -// case SDLK_bracketleft: case SDLK_braceleft: return 0x21; -// case SDLK_bracketright: case SDLK_braceright: return 0x1e; -// case SDLK_BACKSLASH: case SDLK_bar: return 0x2a; + case SDLK_LEFTBRACKET: return 0x21; + case SDLK_RIGHTBRACKET: return 0x1e; + case SDLK_BACKSLASH: return 0x2a; case SDLK_SEMICOLON: case SDLK_COLON: return 0x29; -// case SDLK_apostrophe: case SDLK_QUOTEDBL: return 0x27; + case SDLK_QUOTE: case SDLK_QUOTEDBL: return 0x27; case SDLK_COMMA: case SDLK_LESS: return 0x2b; case SDLK_PERIOD: case SDLK_GREATER: return 0x2f; case SDLK_SLASH: case SDLK_QUESTION: return 0x2c; @@ -1680,10 +1667,19 @@ static int kc_decode(SDL_keysym const & case SDLK_RCTRL: return 0x36; case SDLK_LSHIFT: return 0x38; case SDLK_RSHIFT: return 0x38; +#if (defined(__APPLE__) && defined(__MACH__)) + case SDLK_LALT: return 0x3a; + case SDLK_RALT: return 0x3a; + case SDLK_LMETA: return 0x37; + case SDLK_RMETA: return 0x37; +#else case SDLK_LALT: return 0x37; case SDLK_RALT: return 0x37; case SDLK_LMETA: return 0x3a; case SDLK_RMETA: return 0x3a; +#endif + case SDLK_LSUPER: return 0x3a; // "Windows" key + case SDLK_RSUPER: return 0x3a; case SDLK_MENU: return 0x32; case SDLK_CAPSLOCK: return 0x39; case SDLK_NUMLOCK: return 0x47; @@ -1790,7 +1786,7 @@ static void handle_events(void) // Keyboard case SDL_KEYDOWN: { int code = -1; - if (use_keycodes) { + if (use_keycodes && !is_modifier_key(event.key)) { if (event2keycode(event.key, true) != -2) // This is called to process the hotkeys code = keycode_table[event.key.keysym.scancode & 0xff]; } else @@ -1818,13 +1814,22 @@ static void handle_events(void) } case SDL_KEYUP: { int code = -1; - if (use_keycodes) { + if (use_keycodes && !is_modifier_key(event.key)) { if (event2keycode(event.key, false) != -2) // This is called to process the hotkeys code = keycode_table[event.key.keysym.scancode & 0xff]; } else code = event2keycode(event.key, false); - if (code >= 0 && code != 0x39) { // Don't propagate Caps Lock releases - ADBKeyUp(code); + if (code >= 0) { + if (code == 0x39) { // Caps Lock released + if (caps_on) { + ADBKeyUp(code); + caps_on = false; + } else { + ADBKeyDown(code); + caps_on = true; + } + } else + ADBKeyUp(code); if (code == 0x36) ctrl_down = false; } @@ -1891,7 +1896,7 @@ static void update_display_static(driver // Check for first column from left and first column from right that have changed if (high) { - if (VIDEO_MODE_DEPTH < VIDEO_DEPTH_8BIT) { + if ((int)VIDEO_MODE_DEPTH < VIDEO_DEPTH_8BIT) { const int src_bytes_per_row = bytes_per_row; const int dst_bytes_per_row = drv->s->pitch; const int pixels_per_byte = VIDEO_MODE_X / src_bytes_per_row; @@ -2008,6 +2013,65 @@ 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) +{ + const VIDEO_MODE &mode = drv->mode; + + // Allocate bounding boxes for SDL_UpdateRects() + const int N_PIXELS = 64; + const int n_x_boxes = (VIDEO_MODE_X + N_PIXELS - 1) / N_PIXELS; + const int n_y_boxes = (VIDEO_MODE_Y + N_PIXELS - 1) / N_PIXELS; + SDL_Rect *boxes = (SDL_Rect *)alloca(sizeof(SDL_Rect) * n_x_boxes * n_y_boxes); + int nr_boxes = 0; + + // Lock surface, if required + if (SDL_MUSTLOCK(drv->s)) + SDL_LockSurface(drv->s); + + // 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; + int x, y; + for (y = 0; y < VIDEO_MODE_Y; y += N_PIXELS) { + int h = N_PIXELS; + if (h > VIDEO_MODE_Y - y) + h = VIDEO_MODE_Y - y; + for (x = 0; x < VIDEO_MODE_X; x += N_PIXELS) { + int w = N_PIXELS; + if (w > VIDEO_MODE_X - x) + w = VIDEO_MODE_X - x; + const int xs = w * bytes_per_pixel; + const int xb = x * bytes_per_pixel; + bool dirty = false; + for (int j = y; j < (y + h); j++) { + const int yb = j * 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); + dirty = true; + } + } + if (dirty) { + boxes[nr_boxes].x = x; + boxes[nr_boxes].y = y; + boxes[nr_boxes].w = w; + boxes[nr_boxes].h = h; + nr_boxes++; + } + } + } + + // Unlock surface, if required + if (SDL_MUSTLOCK(drv->s)) + SDL_UnlockSurface(drv->s); + + // Refresh display + if (nr_boxes) + SDL_UpdateRects(drv->s, nr_boxes, boxes); +} + // We suggest the compiler to inline the next two functions so that it // may specialise the code according to the current screen depth and @@ -2068,7 +2132,7 @@ static void video_refresh_dga_vosf(void) tick_counter = 0; if (mainBuffer.dirty) { LOCK_VOSF; - update_display_dga_vosf(); + update_display_dga_vosf(static_cast(drv)); UNLOCK_VOSF; } } @@ -2102,7 +2166,11 @@ static void video_refresh_window_static( static int tick_counter = 0; if (++tick_counter >= frame_skip) { tick_counter = 0; - update_display_static(static_cast(drv)); + const VIDEO_MODE &mode = drv->mode; + if ((int)VIDEO_MODE_DEPTH >= VIDEO_DEPTH_8BIT) + update_display_static_bbox(static_cast(drv)); + else + update_display_static(static_cast(drv)); } } @@ -2132,9 +2200,58 @@ static void VideoRefreshInit(void) } } +static inline void do_video_refresh(void) +{ + // Handle SDL events + handle_events(); + + // Update display + video_refresh(); + +#ifdef SHEEPSHAVER + // Set new cursor image if it was changed + if (cursor_changed && sdl_cursor) { + cursor_changed = false; + LOCK_EVENTS; + SDL_FreeCursor(sdl_cursor); + sdl_cursor = SDL_CreateCursor(MacCursor + 4, MacCursor + 36, 16, 16, MacCursor[2], MacCursor[3]); + if (sdl_cursor) { + 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 + + // Set new palette if it was changed + handle_palette_changes(); +} + +// This function is called on non-threaded platforms from a timer interrupt +void VideoRefresh(void) +{ + // We need to check redraw_thread_active to inhibit refreshed during + // mode changes on non-threaded platforms + if (!redraw_thread_active) + return; + + // Process pending events and update display + do_video_refresh(); +} + const int VIDEO_REFRESH_HZ = 60; const int VIDEO_REFRESH_DELAY = 1000000 / VIDEO_REFRESH_HZ; +#ifndef USE_CPU_EMUL_SERVICES static int redraw_func(void *arg) { uint64 start = GetTicks_usec(); @@ -2152,17 +2269,44 @@ static int redraw_func(void *arg) next = GetTicks_usec(); ticks++; - // Handle SDL events - handle_events(); - - // Refresh display - video_refresh(); +#ifdef SHEEPSHAVER + // Pause if requested (during video mode switches) + if (thread_stop_req) { + thread_stop_ack = true; + continue; + } +#endif - // Set new palette if it was changed - handle_palette_changes(); + // Process pending events and update display + do_video_refresh(); } uint64 end = GetTicks_usec(); D(bug("%lld refreshes in %lld usec = %f refreshes/sec\n", ticks, end - start, ticks * 1000000.0 / (end - start))); return 0; } +#endif + + +/* + * Record dirty area from NQD + */ + +#ifdef SHEEPSHAVER +void video_set_dirty_area(int x, int y, int w, int h) +{ + 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; + +#ifdef ENABLE_VOSF + 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