ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/SDL/video_sdl.cpp
Revision: 1.36
Committed: 2008-06-25T02:52:52Z (16 years, 5 months ago) by asvitkine
Branch: MAIN
Changes since 1.35: +18 -1 lines
Log Message:
[patch from Kelvin Delbarre]
Software cursor mode is now supported, although currently the existing hardware
cursor mode is used whenever possible. (Software mode will be used if you are
running with a recent version of SDL's Quartz video driver, since a bug in SDL
1.2.11 and later prevents the hardware cursor from working properly with that
driver.)

In hardware cursor mode, the hot-spot is now determined heuristically. Formerly
it could not be determined and was always (1,1), an annoyance for many cursors
other than the arrow.

In hardware cursor mode, the cursor will now be hidden when requested by the
emulated OS (such as when you are typing in a text field).

In hardware cursor mode, some cursor image formats that the code does not handle
correctly will now be rejected, causing the emulated OS to revert temporarily to
software cursor mode. Formerly you would just end up with random garbage for a
cursor. This typically happened for grayscale or color cursors; rejecting images
with rowBytes != 2 eliminates the worst cases.

File Contents

# Content
1 /*
2 * video_sdl.cpp - Video/graphics emulation, SDL specific stuff
3 *
4 * Basilisk II (C) 1997-2008 Christian Bauer
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 /*
22 * NOTES:
23 * The Ctrl key works like a qualifier for special actions:
24 * Ctrl-Tab = suspend DGA mode (TODO)
25 * Ctrl-Esc = emergency quit
26 * Ctrl-F1 = mount floppy
27 * Ctrl-F5 = grab mouse (in windowed mode)
28 *
29 * FIXMEs and TODOs:
30 * - Windows requires an extra mouse event to update the actual cursor image?
31 * - Ctr-Tab for suspend/resume but how? SDL does not support that for non-Linux
32 * - Ctrl-Fn doesn't generate SDL_KEYDOWN events (SDL bug?)
33 * - Mouse acceleration, there is no API in SDL yet for that
34 * - Force relative mode in Grab mode even if SDL provides absolute coordinates?
35 * - Gamma tables support is likely to be broken here
36 * - Events processing is bound to the general emulation thread as SDL requires
37 * to PumpEvents() within the same thread as the one that called SetVideoMode().
38 * Besides, there can't seem to be a way to call SetVideoMode() from a child thread.
39 * - Backport hw cursor acceleration to Basilisk II?
40 * - Factor out code
41 */
42
43 #include "sysdeps.h"
44
45 #include <SDL.h>
46 #include <SDL_mutex.h>
47 #include <SDL_thread.h>
48 #include <errno.h>
49 #include <vector>
50
51 #ifdef WIN32
52 #include <malloc.h> /* alloca() */
53 #endif
54
55 #include "cpu_emulation.h"
56 #include "main.h"
57 #include "adb.h"
58 #include "macos_util.h"
59 #include "prefs.h"
60 #include "user_strings.h"
61 #include "video.h"
62 #include "video_defs.h"
63 #include "video_blit.h"
64 #include "vm_alloc.h"
65
66 #define DEBUG 0
67 #include "debug.h"
68
69
70 // Supported video modes
71 using std::vector;
72 static vector<VIDEO_MODE> VideoModes;
73
74 // Display types
75 #ifdef SHEEPSHAVER
76 enum {
77 DISPLAY_WINDOW = DIS_WINDOW, // windowed display
78 DISPLAY_SCREEN = DIS_SCREEN // fullscreen display
79 };
80 extern int display_type; // See enum above
81 #else
82 enum {
83 DISPLAY_WINDOW, // windowed display
84 DISPLAY_SCREEN // fullscreen display
85 };
86 static int display_type = DISPLAY_WINDOW; // See enum above
87 #endif
88
89 // Constants
90 #ifdef WIN32
91 const char KEYCODE_FILE_NAME[] = "BasiliskII_keycodes";
92 #else
93 const char KEYCODE_FILE_NAME[] = DATADIR "/keycodes";
94 #endif
95
96
97 // Global variables
98 static int32 frame_skip; // Prefs items
99 static int16 mouse_wheel_mode;
100 static int16 mouse_wheel_lines;
101
102 static uint8 *the_buffer = NULL; // Mac frame buffer (where MacOS draws into)
103 static uint8 *the_buffer_copy = NULL; // Copy of Mac frame buffer (for refreshed modes)
104 static uint32 the_buffer_size; // Size of allocated the_buffer
105
106 static bool redraw_thread_active = false; // Flag: Redraw thread installed
107 #ifndef USE_CPU_EMUL_SERVICES
108 static volatile bool redraw_thread_cancel; // Flag: Cancel Redraw thread
109 static SDL_Thread *redraw_thread = NULL; // Redraw thread
110 #ifdef SHEEPSHAVER
111 static volatile bool thread_stop_req = false;
112 static volatile bool thread_stop_ack = false; // Acknowledge for thread_stop_req
113 #endif
114 #endif
115
116 #ifdef ENABLE_VOSF
117 static bool use_vosf = false; // Flag: VOSF enabled
118 #else
119 static const bool use_vosf = false; // VOSF not possible
120 #endif
121
122 static bool ctrl_down = false; // Flag: Ctrl key pressed
123 static bool caps_on = false; // Flag: Caps Lock on
124 static bool quit_full_screen = false; // Flag: DGA close requested from redraw thread
125 static bool emerg_quit = false; // Flag: Ctrl-Esc pressed, emergency quit requested from MacOS thread
126 static bool emul_suspended = false; // Flag: Emulator suspended
127
128 static bool classic_mode = false; // Flag: Classic Mac video mode
129
130 static bool use_keycodes = false; // Flag: Use keycodes rather than keysyms
131 static int keycode_table[256]; // X keycode -> Mac keycode translation table
132
133 // SDL variables
134 static int screen_depth; // Depth of current screen
135 static SDL_Cursor *sdl_cursor; // Copy of Mac cursor
136 static volatile bool cursor_changed = false; // Flag: cursor changed, redraw_func must update the cursor
137 static SDL_Color sdl_palette[256]; // Color palette to be used as CLUT and gamma table
138 static bool sdl_palette_changed = false; // Flag: Palette changed, redraw thread must set new colors
139 static const int sdl_eventmask = SDL_MOUSEEVENTMASK | SDL_KEYEVENTMASK | SDL_VIDEOEXPOSEMASK | SDL_QUITMASK | SDL_ACTIVEEVENTMASK;
140
141 // Mutex to protect SDL events
142 static SDL_mutex *sdl_events_lock = NULL;
143 #define LOCK_EVENTS SDL_LockMutex(sdl_events_lock)
144 #define UNLOCK_EVENTS SDL_UnlockMutex(sdl_events_lock)
145
146 // Mutex to protect palette
147 static SDL_mutex *sdl_palette_lock = NULL;
148 #define LOCK_PALETTE SDL_LockMutex(sdl_palette_lock)
149 #define UNLOCK_PALETTE SDL_UnlockMutex(sdl_palette_lock)
150
151 // Mutex to protect frame buffer
152 static SDL_mutex *frame_buffer_lock = NULL;
153 #define LOCK_FRAME_BUFFER SDL_LockMutex(frame_buffer_lock)
154 #define UNLOCK_FRAME_BUFFER SDL_UnlockMutex(frame_buffer_lock)
155
156 // Video refresh function
157 static void VideoRefreshInit(void);
158 static void (*video_refresh)(void);
159
160
161 // Prototypes
162 static int redraw_func(void *arg);
163
164 // From sys_unix.cpp
165 extern void SysMountFirstFloppy(void);
166
167
168 /*
169 * SDL surface locking glue
170 */
171
172 #ifdef ENABLE_VOSF
173 #define SDL_VIDEO_LOCK_VOSF_SURFACE(SURFACE) do { \
174 if ((SURFACE)->flags & (SDL_HWSURFACE | SDL_FULLSCREEN)) \
175 the_host_buffer = (uint8 *)(SURFACE)->pixels; \
176 } while (0)
177 #else
178 #define SDL_VIDEO_LOCK_VOSF_SURFACE(SURFACE)
179 #endif
180
181 #define SDL_VIDEO_LOCK_SURFACE(SURFACE) do { \
182 if (SDL_MUSTLOCK(SURFACE)) { \
183 SDL_LockSurface(SURFACE); \
184 SDL_VIDEO_LOCK_VOSF_SURFACE(SURFACE); \
185 } \
186 } while (0)
187
188 #define SDL_VIDEO_UNLOCK_SURFACE(SURFACE) do { \
189 if (SDL_MUSTLOCK(SURFACE)) \
190 SDL_UnlockSurface(SURFACE); \
191 } while (0)
192
193
194 /*
195 * Framebuffer allocation routines
196 */
197
198 static void *vm_acquire_framebuffer(uint32 size)
199 {
200 // always try to reallocate framebuffer at the same address
201 static void *fb = VM_MAP_FAILED;
202 if (fb != VM_MAP_FAILED) {
203 if (vm_acquire_fixed(fb, size) < 0) {
204 #ifndef SHEEPSHAVER
205 printf("FATAL: Could not reallocate framebuffer at previous address\n");
206 #endif
207 fb = VM_MAP_FAILED;
208 }
209 }
210 if (fb == VM_MAP_FAILED)
211 fb = vm_acquire(size, VM_MAP_DEFAULT | VM_MAP_32BIT);
212 return fb;
213 }
214
215 static inline void vm_release_framebuffer(void *fb, uint32 size)
216 {
217 vm_release(fb, size);
218 }
219
220
221 /*
222 * Windows message handler
223 */
224
225 #ifdef WIN32
226 #include <dbt.h>
227 static WNDPROC sdl_window_proc = NULL; // Window proc used by SDL
228
229 extern void SysMediaArrived(void);
230 extern void SysMediaRemoved(void);
231 extern HWND GetMainWindowHandle(void);
232
233 static LRESULT CALLBACK windows_message_handler(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
234 {
235 switch (msg) {
236 case WM_DEVICECHANGE:
237 if (wParam == DBT_DEVICEREMOVECOMPLETE) {
238 DEV_BROADCAST_HDR *p = (DEV_BROADCAST_HDR *)lParam;
239 if (p->dbch_devicetype == DBT_DEVTYP_VOLUME)
240 SysMediaRemoved();
241 }
242 else if (wParam == DBT_DEVICEARRIVAL) {
243 DEV_BROADCAST_HDR *p = (DEV_BROADCAST_HDR *)lParam;
244 if (p->dbch_devicetype == DBT_DEVTYP_VOLUME)
245 SysMediaArrived();
246 }
247 return 0;
248
249 default:
250 if (sdl_window_proc)
251 return CallWindowProc(sdl_window_proc, hwnd, msg, wParam, lParam);
252 }
253
254 return DefWindowProc(hwnd, msg, wParam, lParam);
255 }
256 #endif
257
258
259 /*
260 * SheepShaver glue
261 */
262
263 #ifdef SHEEPSHAVER
264 // Color depth modes type
265 typedef int video_depth;
266
267 // 1, 2, 4 and 8 bit depths use a color palette
268 static inline bool IsDirectMode(VIDEO_MODE const & mode)
269 {
270 return IsDirectMode(mode.viAppleMode);
271 }
272
273 // Abstract base class representing one (possibly virtual) monitor
274 // ("monitor" = rectangular display with a contiguous frame buffer)
275 class monitor_desc {
276 public:
277 monitor_desc(const vector<VIDEO_MODE> &available_modes, video_depth default_depth, uint32 default_id) {}
278 virtual ~monitor_desc() {}
279
280 // Get current Mac frame buffer base address
281 uint32 get_mac_frame_base(void) const {return screen_base;}
282
283 // Set Mac frame buffer base address (called from switch_to_mode())
284 void set_mac_frame_base(uint32 base) {screen_base = base;}
285
286 // Get current video mode
287 const VIDEO_MODE &get_current_mode(void) const {return VModes[cur_mode];}
288
289 // Called by the video driver to switch the video mode on this display
290 // (must call set_mac_frame_base())
291 virtual void switch_to_current_mode(void) = 0;
292
293 // Called by the video driver to set the color palette (in indexed modes)
294 // or the gamma table (in direct modes)
295 virtual void set_palette(uint8 *pal, int num) = 0;
296 };
297
298 // Vector of pointers to available monitor descriptions, filled by VideoInit()
299 static vector<monitor_desc *> VideoMonitors;
300
301 // Find Apple mode matching best specified dimensions
302 static int find_apple_resolution(int xsize, int ysize)
303 {
304 if (xsize == 640 && ysize == 480)
305 return APPLE_640x480;
306 if (xsize == 800 && ysize == 600)
307 return APPLE_800x600;
308 if (xsize == 1024 && ysize == 768)
309 return APPLE_1024x768;
310 if (xsize == 1152 && ysize == 768)
311 return APPLE_1152x768;
312 if (xsize == 1152 && ysize == 900)
313 return APPLE_1152x900;
314 if (xsize == 1280 && ysize == 1024)
315 return APPLE_1280x1024;
316 if (xsize == 1600 && ysize == 1200)
317 return APPLE_1600x1200;
318 return APPLE_CUSTOM;
319 }
320
321 // Display error alert
322 static void ErrorAlert(int error)
323 {
324 ErrorAlert(GetString(error));
325 }
326
327 // Display warning alert
328 static void WarningAlert(int warning)
329 {
330 WarningAlert(GetString(warning));
331 }
332 #endif
333
334
335 /*
336 * monitor_desc subclass for SDL display
337 */
338
339 class SDL_monitor_desc : public monitor_desc {
340 public:
341 SDL_monitor_desc(const vector<VIDEO_MODE> &available_modes, video_depth default_depth, uint32 default_id) : monitor_desc(available_modes, default_depth, default_id) {}
342 ~SDL_monitor_desc() {}
343
344 virtual void switch_to_current_mode(void);
345 virtual void set_palette(uint8 *pal, int num);
346
347 bool video_open(void);
348 void video_close(void);
349 };
350
351
352 /*
353 * Utility functions
354 */
355
356 // Find palette size for given color depth
357 static int palette_size(int mode)
358 {
359 switch (mode) {
360 case VIDEO_DEPTH_1BIT: return 2;
361 case VIDEO_DEPTH_2BIT: return 4;
362 case VIDEO_DEPTH_4BIT: return 16;
363 case VIDEO_DEPTH_8BIT: return 256;
364 case VIDEO_DEPTH_16BIT: return 32;
365 case VIDEO_DEPTH_32BIT: return 256;
366 default: return 0;
367 }
368 }
369
370 // Return bytes per pixel for requested depth
371 static inline int bytes_per_pixel(int depth)
372 {
373 int bpp;
374 switch (depth) {
375 case 8:
376 bpp = 1;
377 break;
378 case 15: case 16:
379 bpp = 2;
380 break;
381 case 24: case 32:
382 bpp = 4;
383 break;
384 default:
385 abort();
386 }
387 return bpp;
388 }
389
390 // Map video_mode depth ID to numerical depth value
391 static int mac_depth_of_video_depth(int video_depth)
392 {
393 int depth = -1;
394 switch (video_depth) {
395 case VIDEO_DEPTH_1BIT:
396 depth = 1;
397 break;
398 case VIDEO_DEPTH_2BIT:
399 depth = 2;
400 break;
401 case VIDEO_DEPTH_4BIT:
402 depth = 4;
403 break;
404 case VIDEO_DEPTH_8BIT:
405 depth = 8;
406 break;
407 case VIDEO_DEPTH_16BIT:
408 depth = 16;
409 break;
410 case VIDEO_DEPTH_32BIT:
411 depth = 32;
412 break;
413 default:
414 abort();
415 }
416 return depth;
417 }
418
419 // Map video_mode depth ID to SDL screen depth
420 static int sdl_depth_of_video_depth(int video_depth)
421 {
422 return (video_depth <= VIDEO_DEPTH_8BIT) ? 8 : mac_depth_of_video_depth(video_depth);
423 }
424
425 // Get screen dimensions
426 static void sdl_display_dimensions(int &width, int &height)
427 {
428 static int max_width, max_height;
429 if (max_width == 0 && max_height == 0) {
430 max_width = 640 ; max_height = 480;
431 SDL_Rect **modes = SDL_ListModes(NULL, SDL_FULLSCREEN | SDL_HWSURFACE);
432 if (modes && modes != (SDL_Rect **)-1) {
433 // It turns out that on some implementations, and contrary to the documentation,
434 // the returned list is not sorted from largest to smallest (e.g. Windows)
435 for (int i = 0; modes[i] != NULL; i++) {
436 const int w = modes[i]->w;
437 const int h = modes[i]->h;
438 if (w > max_width && h > max_height) {
439 max_width = w;
440 max_height = h;
441 }
442 }
443 }
444 }
445 width = max_width;
446 height = max_height;
447 }
448
449 static inline int sdl_display_width(void)
450 {
451 int width, height;
452 sdl_display_dimensions(width, height);
453 return width;
454 }
455
456 static inline int sdl_display_height(void)
457 {
458 int width, height;
459 sdl_display_dimensions(width, height);
460 return height;
461 }
462
463 // Check wether specified mode is available
464 static bool has_mode(int type, int width, int height, int depth)
465 {
466 #ifdef SHEEPSHAVER
467 // Filter out Classic resolutions
468 if (width == 512 && height == 384)
469 return false;
470 #endif
471
472 // Filter out out-of-bounds resolutions
473 if (width > sdl_display_width() || height > sdl_display_height())
474 return false;
475
476 // Rely on SDL capabilities
477 return SDL_VideoModeOK(width, height,
478 sdl_depth_of_video_depth(depth),
479 SDL_HWSURFACE | (type == DISPLAY_SCREEN ? SDL_FULLSCREEN : 0));
480 }
481
482 // Add mode to list of supported modes
483 static void add_mode(int type, int width, int height, int resolution_id, int bytes_per_row, int depth)
484 {
485 // Filter out unsupported modes
486 if (!has_mode(type, width, height, depth))
487 return;
488
489 // Fill in VideoMode entry
490 VIDEO_MODE mode;
491 #ifdef SHEEPSHAVER
492 resolution_id = find_apple_resolution(width, height);
493 mode.viType = type;
494 #endif
495 VIDEO_MODE_X = width;
496 VIDEO_MODE_Y = height;
497 VIDEO_MODE_RESOLUTION = resolution_id;
498 VIDEO_MODE_ROW_BYTES = bytes_per_row;
499 VIDEO_MODE_DEPTH = (video_depth)depth;
500 VideoModes.push_back(mode);
501 }
502
503 // Set Mac frame layout and base address (uses the_buffer/MacFrameBaseMac)
504 static void set_mac_frame_buffer(SDL_monitor_desc &monitor, int depth, bool native_byte_order)
505 {
506 #if !REAL_ADDRESSING && !DIRECT_ADDRESSING
507 int layout = FLAYOUT_DIRECT;
508 if (depth == VIDEO_DEPTH_16BIT)
509 layout = (screen_depth == 15) ? FLAYOUT_HOST_555 : FLAYOUT_HOST_565;
510 else if (depth == VIDEO_DEPTH_32BIT)
511 layout = (screen_depth == 24) ? FLAYOUT_HOST_888 : FLAYOUT_DIRECT;
512 if (native_byte_order)
513 MacFrameLayout = layout;
514 else
515 MacFrameLayout = FLAYOUT_DIRECT;
516 monitor.set_mac_frame_base(MacFrameBaseMac);
517
518 // Set variables used by UAE memory banking
519 const VIDEO_MODE &mode = monitor.get_current_mode();
520 MacFrameBaseHost = the_buffer;
521 MacFrameSize = VIDEO_MODE_ROW_BYTES * VIDEO_MODE_Y;
522 InitFrameBufferMapping();
523 #else
524 monitor.set_mac_frame_base(Host2MacAddr(the_buffer));
525 #endif
526 D(bug("monitor.mac_frame_base = %08x\n", monitor.get_mac_frame_base()));
527 }
528
529 // Set window name and class
530 static void set_window_name(int name)
531 {
532 const SDL_VideoInfo *vi = SDL_GetVideoInfo();
533 if (vi && vi->wm_available) {
534 const char *str = GetString(name);
535 SDL_WM_SetCaption(str, str);
536 }
537 }
538
539 // Set mouse grab mode
540 static SDL_GrabMode set_grab_mode(SDL_GrabMode mode)
541 {
542 const SDL_VideoInfo *vi = SDL_GetVideoInfo();
543 return (vi && vi->wm_available ? SDL_WM_GrabInput(mode) : SDL_GRAB_OFF);
544 }
545
546 // Migrate preferences items (XXX to be handled in MigratePrefs())
547 static void migrate_screen_prefs(void)
548 {
549 #ifdef SHEEPSHAVER
550 // Look-up priorities are: "screen", "screenmodes", "windowmodes".
551 if (PrefsFindString("screen"))
552 return;
553
554 uint32 window_modes = PrefsFindInt32("windowmodes");
555 uint32 screen_modes = PrefsFindInt32("screenmodes");
556 int width = 0, height = 0;
557 if (screen_modes) {
558 static const struct {
559 int id;
560 int width;
561 int height;
562 }
563 modes[] = {
564 { 1, 640, 480 },
565 { 2, 800, 600 },
566 { 4, 1024, 768 },
567 { 64, 1152, 768 },
568 { 8, 1152, 900 },
569 { 16, 1280, 1024 },
570 { 32, 1600, 1200 },
571 { 0, }
572 };
573 for (int i = 0; modes[i].id != 0; i++) {
574 if (screen_modes & modes[i].id) {
575 if (width < modes[i].width && height < modes[i].height) {
576 width = modes[i].width;
577 height = modes[i].height;
578 }
579 }
580 }
581 } else {
582 if (window_modes & 1)
583 width = 640, height = 480;
584 if (window_modes & 2)
585 width = 800, height = 600;
586 }
587 if (width && height) {
588 char str[32];
589 sprintf(str, "%s/%d/%d", screen_modes ? "dga" : "win", width, height);
590 PrefsReplaceString("screen", str);
591 }
592 #endif
593 }
594
595
596 /*
597 * Display "driver" classes
598 */
599
600 class driver_base {
601 public:
602 driver_base(SDL_monitor_desc &m);
603 virtual ~driver_base();
604
605 virtual void update_palette(void);
606 virtual void suspend(void) {}
607 virtual void resume(void) {}
608 virtual void toggle_mouse_grab(void) {}
609 virtual void mouse_moved(int x, int y) { ADBMouseMoved(x, y); }
610
611 void disable_mouse_accel(void);
612 void restore_mouse_accel(void);
613
614 virtual void grab_mouse(void) {}
615 virtual void ungrab_mouse(void) {}
616
617 public:
618 SDL_monitor_desc &monitor; // Associated video monitor
619 const VIDEO_MODE &mode; // Video mode handled by the driver
620
621 bool init_ok; // Initialization succeeded (we can't use exceptions because of -fomit-frame-pointer)
622 SDL_Surface *s; // The surface we draw into
623 };
624
625 class driver_window;
626 static void update_display_window_vosf(driver_window *drv);
627 static void update_display_dynamic(int ticker, driver_window *drv);
628 static void update_display_static(driver_window *drv);
629
630 class driver_window : public driver_base {
631 friend void update_display_window_vosf(driver_window *drv);
632 friend void update_display_dynamic(int ticker, driver_window *drv);
633 friend void update_display_static(driver_window *drv);
634
635 public:
636 driver_window(SDL_monitor_desc &monitor);
637 ~driver_window();
638
639 void toggle_mouse_grab(void);
640 void mouse_moved(int x, int y);
641
642 void grab_mouse(void);
643 void ungrab_mouse(void);
644
645 private:
646 bool mouse_grabbed; // Flag: mouse pointer grabbed, using relative mouse mode
647 int mouse_last_x, mouse_last_y; // Last mouse position (for relative mode)
648 };
649
650 class driver_fullscreen : public driver_base {
651 public:
652 driver_fullscreen(SDL_monitor_desc &monitor);
653 ~driver_fullscreen();
654 };
655
656 static driver_base *drv = NULL; // Pointer to currently used driver object
657
658 #ifdef ENABLE_VOSF
659 # include "video_vosf.h"
660 #endif
661
662 driver_base::driver_base(SDL_monitor_desc &m)
663 : monitor(m), mode(m.get_current_mode()), init_ok(false), s(NULL)
664 {
665 the_buffer = NULL;
666 the_buffer_copy = NULL;
667 }
668
669 driver_base::~driver_base()
670 {
671 ungrab_mouse();
672 restore_mouse_accel();
673
674 if (s)
675 SDL_FreeSurface(s);
676
677 // the_buffer shall always be mapped through vm_acquire_framebuffer()
678 if (the_buffer != VM_MAP_FAILED) {
679 D(bug(" releasing the_buffer at %p (%d bytes)\n", the_buffer, the_buffer_size));
680 vm_release_framebuffer(the_buffer, the_buffer_size);
681 the_buffer = NULL;
682 }
683
684 // Free frame buffer(s)
685 if (!use_vosf) {
686 if (the_buffer_copy) {
687 free(the_buffer_copy);
688 the_buffer_copy = NULL;
689 }
690 }
691 #ifdef ENABLE_VOSF
692 else {
693 if (the_host_buffer) {
694 D(bug(" freeing the_host_buffer at %p\n", the_host_buffer));
695 free(the_host_buffer);
696 the_host_buffer = NULL;
697 }
698 if (the_buffer_copy) {
699 D(bug(" freeing the_buffer_copy at %p\n", the_buffer_copy));
700 free(the_buffer_copy);
701 the_buffer_copy = NULL;
702 }
703
704 // Deinitialize VOSF
705 video_vosf_exit();
706 }
707 #endif
708 }
709
710 // Palette has changed
711 void driver_base::update_palette(void)
712 {
713 const VIDEO_MODE &mode = monitor.get_current_mode();
714
715 if ((int)VIDEO_MODE_DEPTH <= VIDEO_DEPTH_8BIT)
716 SDL_SetPalette(s, SDL_PHYSPAL, sdl_palette, 0, 256);
717 }
718
719 // Disable mouse acceleration
720 void driver_base::disable_mouse_accel(void)
721 {
722 }
723
724 // Restore mouse acceleration to original value
725 void driver_base::restore_mouse_accel(void)
726 {
727 }
728
729
730 /*
731 * Windowed display driver
732 */
733
734 static bool SDL_display_opened = false;
735
736 // Open display
737 driver_window::driver_window(SDL_monitor_desc &m)
738 : driver_base(m), mouse_grabbed(false)
739 {
740 int width = VIDEO_MODE_X, height = VIDEO_MODE_Y;
741 int aligned_width = (width + 15) & ~15;
742 int aligned_height = (height + 15) & ~15;
743
744 // Set absolute mouse mode
745 ADBSetRelMouseMode(mouse_grabbed);
746
747 // This is ugly:
748 // If we're switching resolutions (ie, not setting it for the first time),
749 // there's a bug in SDL where the SDL_Surface created will not be properly
750 // setup. The solution is to SDL_QuitSubSystem(SDL_INIT_VIDEO) before calling
751 // SDL_SetVideoMode for the second time (SDL_SetVideoMode will call SDL_Init()
752 // and all will be well). Without this, the video becomes corrupted (at least
753 // on Mac OS X), after the resolution switch.
754 if (SDL_display_opened)
755 SDL_QuitSubSystem(SDL_INIT_VIDEO);
756
757 // Create surface
758 int depth = sdl_depth_of_video_depth(VIDEO_MODE_DEPTH);
759 if ((s = SDL_SetVideoMode(width, height, depth, SDL_HWSURFACE)) == NULL)
760 return;
761
762 SDL_display_opened = true;
763
764 #ifdef ENABLE_VOSF
765 use_vosf = true;
766 // Allocate memory for frame buffer (SIZE is extended to page-boundary)
767 the_host_buffer = (uint8 *)s->pixels;
768 the_buffer_size = page_extend((aligned_height + 2) * s->pitch);
769 the_buffer = (uint8 *)vm_acquire_framebuffer(the_buffer_size);
770 the_buffer_copy = (uint8 *)malloc(the_buffer_size);
771 D(bug("the_buffer = %p, the_buffer_copy = %p, the_host_buffer = %p\n", the_buffer, the_buffer_copy, the_host_buffer));
772
773 // Check whether we can initialize the VOSF subsystem and it's profitable
774 if (!video_vosf_init(m)) {
775 WarningAlert(STR_VOSF_INIT_ERR);
776 use_vosf = false;
777 }
778 else if (!video_vosf_profitable()) {
779 video_vosf_exit();
780 printf("VOSF acceleration is not profitable on this platform, disabling it\n");
781 use_vosf = false;
782 }
783 if (!use_vosf) {
784 free(the_buffer_copy);
785 vm_release(the_buffer, the_buffer_size);
786 the_host_buffer = NULL;
787 }
788 #endif
789 if (!use_vosf) {
790 // Allocate memory for frame buffer
791 the_buffer_size = (aligned_height + 2) * s->pitch;
792 the_buffer_copy = (uint8 *)calloc(1, the_buffer_size);
793 the_buffer = (uint8 *)vm_acquire_framebuffer(the_buffer_size);
794 D(bug("the_buffer = %p, the_buffer_copy = %p\n", the_buffer, the_buffer_copy));
795 }
796
797 #ifdef SHEEPSHAVER
798 // Create cursor
799 if ((sdl_cursor = SDL_CreateCursor(MacCursor + 4, MacCursor + 36, 16, 16, 0, 0)) != NULL) {
800 SDL_SetCursor(sdl_cursor);
801 cursor_changed = false;
802 }
803 #else
804 // Hide cursor
805 SDL_ShowCursor(0);
806 #endif
807
808 // Set window name/class
809 set_window_name(STR_WINDOW_TITLE);
810
811 // Init blitting routines
812 SDL_PixelFormat *f = s->format;
813 VisualFormat visualFormat;
814 visualFormat.depth = depth;
815 visualFormat.Rmask = f->Rmask;
816 visualFormat.Gmask = f->Gmask;
817 visualFormat.Bmask = f->Bmask;
818 Screen_blitter_init(visualFormat, true, mac_depth_of_video_depth(VIDEO_MODE_DEPTH));
819
820 // Load gray ramp to 8->16/32 expand map
821 if (!IsDirectMode(mode))
822 for (int i=0; i<256; i++)
823 ExpandMap[i] = SDL_MapRGB(f, i, i, i);
824
825 // Set frame buffer base
826 set_mac_frame_buffer(monitor, VIDEO_MODE_DEPTH, true);
827
828 // Everything went well
829 init_ok = true;
830 }
831
832 // Close display
833 driver_window::~driver_window()
834 {
835 #ifdef ENABLE_VOSF
836 if (use_vosf)
837 the_host_buffer = NULL; // don't free() in driver_base dtor
838 #endif
839 if (s)
840 SDL_FreeSurface(s);
841 }
842
843 // Toggle mouse grab
844 void driver_window::toggle_mouse_grab(void)
845 {
846 if (mouse_grabbed)
847 ungrab_mouse();
848 else
849 grab_mouse();
850 }
851
852 // Grab mouse, switch to relative mouse mode
853 void driver_window::grab_mouse(void)
854 {
855 if (!mouse_grabbed) {
856 SDL_GrabMode new_mode = set_grab_mode(SDL_GRAB_ON);
857 if (new_mode == SDL_GRAB_ON) {
858 set_window_name(STR_WINDOW_TITLE_GRABBED);
859 disable_mouse_accel();
860 mouse_grabbed = true;
861 }
862 }
863 }
864
865 // Ungrab mouse, switch to absolute mouse mode
866 void driver_window::ungrab_mouse(void)
867 {
868 if (mouse_grabbed) {
869 SDL_GrabMode new_mode = set_grab_mode(SDL_GRAB_OFF);
870 if (new_mode == SDL_GRAB_OFF) {
871 set_window_name(STR_WINDOW_TITLE);
872 restore_mouse_accel();
873 mouse_grabbed = false;
874 }
875 }
876 }
877
878 // Mouse moved
879 void driver_window::mouse_moved(int x, int y)
880 {
881 mouse_last_x = x; mouse_last_y = y;
882 ADBMouseMoved(x, y);
883 }
884
885
886 /*
887 * Full-screen display driver
888 */
889
890 // Open display
891 driver_fullscreen::driver_fullscreen(SDL_monitor_desc &m)
892 : driver_base(m)
893 {
894 int width = VIDEO_MODE_X, height = VIDEO_MODE_Y;
895 int aligned_width = (width + 15) & ~15;
896 int aligned_height = (height + 15) & ~15;
897
898 // Set absolute mouse mode
899 ADBSetRelMouseMode(false);
900
901 // Create surface
902 int depth = sdl_depth_of_video_depth(VIDEO_MODE_DEPTH);
903 if ((s = SDL_SetVideoMode(width, height, depth, SDL_HWSURFACE | SDL_FULLSCREEN)) == NULL)
904 return;
905
906 #ifdef ENABLE_VOSF
907 use_vosf = true;
908 // Allocate memory for frame buffer (SIZE is extended to page-boundary)
909 the_host_buffer = (uint8 *)s->pixels;
910 the_buffer_size = page_extend((aligned_height + 2) * s->pitch);
911 the_buffer = (uint8 *)vm_acquire_framebuffer(the_buffer_size);
912 the_buffer_copy = (uint8 *)malloc(the_buffer_size);
913 D(bug("the_buffer = %p, the_buffer_copy = %p, the_host_buffer = %p\n", the_buffer, the_buffer_copy, the_host_buffer));
914
915 // Check whether we can initialize the VOSF subsystem and it's profitable
916 if (!video_vosf_init(m)) {
917 WarningAlert(STR_VOSF_INIT_ERR);
918 use_vosf = false;
919 }
920 else if (!video_vosf_profitable()) {
921 video_vosf_exit();
922 printf("VOSF acceleration is not profitable on this platform, disabling it\n");
923 use_vosf = false;
924 }
925 if (!use_vosf) {
926 free(the_buffer_copy);
927 vm_release(the_buffer, the_buffer_size);
928 the_host_buffer = NULL;
929 }
930 #endif
931 if (!use_vosf) {
932 // Allocate memory for frame buffer
933 the_buffer_size = (aligned_height + 2) * s->pitch;
934 the_buffer_copy = (uint8 *)calloc(1, the_buffer_size);
935 the_buffer = (uint8 *)vm_acquire_framebuffer(the_buffer_size);
936 D(bug("the_buffer = %p, the_buffer_copy = %p\n", the_buffer, the_buffer_copy));
937 }
938
939 // Hide cursor
940 SDL_ShowCursor(0);
941
942 // Init blitting routines
943 SDL_PixelFormat *f = s->format;
944 VisualFormat visualFormat;
945 visualFormat.depth = depth;
946 visualFormat.Rmask = f->Rmask;
947 visualFormat.Gmask = f->Gmask;
948 visualFormat.Bmask = f->Bmask;
949 Screen_blitter_init(visualFormat, true, mac_depth_of_video_depth(VIDEO_MODE_DEPTH));
950
951 // Load gray ramp to 8->16/32 expand map
952 if (!IsDirectMode(mode))
953 for (int i=0; i<256; i++)
954 ExpandMap[i] = SDL_MapRGB(f, i, i, i);
955
956 // Set frame buffer base
957 set_mac_frame_buffer(monitor, VIDEO_MODE_DEPTH, true);
958
959 // Everything went well
960 init_ok = true;
961 }
962
963 // Close display
964 driver_fullscreen::~driver_fullscreen()
965 {
966 #ifdef ENABLE_VOSF
967 if (use_vosf)
968 the_host_buffer = NULL; // don't free() in driver_base dtor
969 #endif
970 if (s)
971 SDL_FreeSurface(s);
972
973 // Show cursor
974 SDL_ShowCursor(1);
975 }
976
977
978 /*
979 * Initialization
980 */
981
982 // Init keycode translation table
983 static void keycode_init(void)
984 {
985 bool use_kc = PrefsFindBool("keycodes");
986 if (use_kc) {
987
988 // Get keycode file path from preferences
989 const char *kc_path = PrefsFindString("keycodefile");
990
991 // Open keycode table
992 FILE *f = fopen(kc_path ? kc_path : KEYCODE_FILE_NAME, "r");
993 if (f == NULL) {
994 char str[256];
995 sprintf(str, GetString(STR_KEYCODE_FILE_WARN), kc_path ? kc_path : KEYCODE_FILE_NAME, strerror(errno));
996 WarningAlert(str);
997 return;
998 }
999
1000 // Default translation table
1001 for (int i=0; i<256; i++)
1002 keycode_table[i] = -1;
1003
1004 // Search for server vendor string, then read keycodes
1005 char video_driver[256];
1006 SDL_VideoDriverName(video_driver, sizeof(video_driver));
1007 bool video_driver_found = false;
1008 char line[256];
1009 int n_keys = 0;
1010 while (fgets(line, sizeof(line) - 1, f)) {
1011 // Read line
1012 int len = strlen(line);
1013 if (len == 0)
1014 continue;
1015 line[len-1] = 0;
1016
1017 // Comments begin with "#" or ";"
1018 if (line[0] == '#' || line[0] == ';' || line[0] == 0)
1019 continue;
1020
1021 if (video_driver_found) {
1022 // Skip aliases as long as we have read keycodes yet
1023 // Otherwise, it's another mapping and we have to stop
1024 static const char sdl_str[] = "sdl";
1025 if (strncmp(line, sdl_str, sizeof(sdl_str) - 1) == 0 && n_keys == 0)
1026 continue;
1027
1028 // Read keycode
1029 int x_code, mac_code;
1030 if (sscanf(line, "%d %d", &x_code, &mac_code) == 2)
1031 keycode_table[x_code & 0xff] = mac_code, n_keys++;
1032 else
1033 break;
1034 } else {
1035 // Search for SDL video driver string
1036 static const char sdl_str[] = "sdl";
1037 if (strncmp(line, sdl_str, sizeof(sdl_str) - 1) == 0) {
1038 char *p = line + sizeof(sdl_str);
1039 if (strstr(video_driver, p) == video_driver)
1040 video_driver_found = true;
1041 }
1042 }
1043 }
1044
1045 // Keycode file completely read
1046 fclose(f);
1047 use_keycodes = video_driver_found;
1048
1049 // Vendor not found? Then display warning
1050 if (!video_driver_found) {
1051 char str[256];
1052 sprintf(str, GetString(STR_KEYCODE_VENDOR_WARN), video_driver, kc_path ? kc_path : KEYCODE_FILE_NAME);
1053 WarningAlert(str);
1054 return;
1055 }
1056
1057 D(bug("Using SDL/%s keycodes table, %d key mappings\n", video_driver, n_keys));
1058 }
1059 }
1060
1061 // Open display for current mode
1062 bool SDL_monitor_desc::video_open(void)
1063 {
1064 D(bug("video_open()\n"));
1065 const VIDEO_MODE &mode = get_current_mode();
1066 #if DEBUG
1067 D(bug("Current video mode:\n"));
1068 D(bug(" %dx%d (ID %02x), %d bpp\n", VIDEO_MODE_X, VIDEO_MODE_Y, VIDEO_MODE_RESOLUTION, 1 << (VIDEO_MODE_DEPTH & 0x0f)));
1069 #endif
1070
1071 // Create display driver object of requested type
1072 switch (display_type) {
1073 case DISPLAY_WINDOW:
1074 drv = new(std::nothrow) driver_window(*this);
1075 break;
1076 case DISPLAY_SCREEN:
1077 drv = new(std::nothrow) driver_fullscreen(*this);
1078 break;
1079 }
1080 if (drv == NULL)
1081 return false;
1082 if (!drv->init_ok) {
1083 delete drv;
1084 drv = NULL;
1085 return false;
1086 }
1087
1088 #ifdef WIN32
1089 // Chain in a new message handler for WM_DEVICECHANGE
1090 HWND the_window = GetMainWindowHandle();
1091 sdl_window_proc = (WNDPROC)GetWindowLongPtr(the_window, GWLP_WNDPROC);
1092 SetWindowLongPtr(the_window, GWLP_WNDPROC, (LONG_PTR)windows_message_handler);
1093 #endif
1094
1095 // Initialize VideoRefresh function
1096 VideoRefreshInit();
1097
1098 // Lock down frame buffer
1099 LOCK_FRAME_BUFFER;
1100
1101 // Start redraw/input thread
1102 #ifndef USE_CPU_EMUL_SERVICES
1103 redraw_thread_cancel = false;
1104 redraw_thread_active = ((redraw_thread = SDL_CreateThread(redraw_func, NULL)) != NULL);
1105 if (!redraw_thread_active) {
1106 printf("FATAL: cannot create redraw thread\n");
1107 return false;
1108 }
1109 #else
1110 redraw_thread_active = true;
1111 #endif
1112 return true;
1113 }
1114
1115 #ifdef SHEEPSHAVER
1116 bool VideoInit(void)
1117 {
1118 const bool classic = false;
1119 #else
1120 bool VideoInit(bool classic)
1121 {
1122 #endif
1123 classic_mode = classic;
1124
1125 #ifdef ENABLE_VOSF
1126 // Zero the mainBuffer structure
1127 mainBuffer.dirtyPages = NULL;
1128 mainBuffer.pageInfo = NULL;
1129 #endif
1130
1131 // Create Mutexes
1132 if ((sdl_events_lock = SDL_CreateMutex()) == NULL)
1133 return false;
1134 if ((sdl_palette_lock = SDL_CreateMutex()) == NULL)
1135 return false;
1136 if ((frame_buffer_lock = SDL_CreateMutex()) == NULL)
1137 return false;
1138
1139 // Init keycode translation
1140 keycode_init();
1141
1142 // Read prefs
1143 frame_skip = PrefsFindInt32("frameskip");
1144 mouse_wheel_mode = PrefsFindInt32("mousewheelmode");
1145 mouse_wheel_lines = PrefsFindInt32("mousewheellines");
1146
1147 // Get screen mode from preferences
1148 migrate_screen_prefs();
1149 const char *mode_str = NULL;
1150 if (classic_mode)
1151 mode_str = "win/512/342";
1152 else
1153 mode_str = PrefsFindString("screen");
1154
1155 // Determine display type and default dimensions
1156 int default_width, default_height;
1157 if (classic) {
1158 default_width = 512;
1159 default_height = 384;
1160 }
1161 else {
1162 default_width = 640;
1163 default_height = 480;
1164 }
1165 display_type = DISPLAY_WINDOW;
1166 if (mode_str) {
1167 if (sscanf(mode_str, "win/%d/%d", &default_width, &default_height) == 2)
1168 display_type = DISPLAY_WINDOW;
1169 else if (sscanf(mode_str, "dga/%d/%d", &default_width, &default_height) == 2)
1170 display_type = DISPLAY_SCREEN;
1171 }
1172 if (default_width <= 0)
1173 default_width = sdl_display_width();
1174 else if (default_width > sdl_display_width())
1175 default_width = sdl_display_width();
1176 if (default_height <= 0)
1177 default_height = sdl_display_height();
1178 else if (default_height > sdl_display_height())
1179 default_height = sdl_display_height();
1180
1181 // Mac screen depth follows X depth
1182 screen_depth = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
1183 int default_depth;
1184 switch (screen_depth) {
1185 case 8:
1186 default_depth = VIDEO_DEPTH_8BIT;
1187 break;
1188 case 15: case 16:
1189 default_depth = VIDEO_DEPTH_16BIT;
1190 break;
1191 case 24: case 32:
1192 default_depth = VIDEO_DEPTH_32BIT;
1193 break;
1194 default:
1195 default_depth = VIDEO_DEPTH_1BIT;
1196 break;
1197 }
1198
1199 // Initialize list of video modes to try
1200 struct {
1201 int w;
1202 int h;
1203 int resolution_id;
1204 }
1205 video_modes[] = {
1206 { -1, -1, 0x80 },
1207 { 512, 384, 0x80 },
1208 { 640, 480, 0x81 },
1209 { 800, 600, 0x82 },
1210 { 1024, 768, 0x83 },
1211 { 1152, 870, 0x84 },
1212 { 1280, 1024, 0x85 },
1213 { 1600, 1200, 0x86 },
1214 { 0, }
1215 };
1216 video_modes[0].w = default_width;
1217 video_modes[0].h = default_height;
1218
1219 // Construct list of supported modes
1220 if (display_type == DISPLAY_WINDOW) {
1221 if (classic)
1222 add_mode(display_type, 512, 342, 0x80, 64, VIDEO_DEPTH_1BIT);
1223 else {
1224 for (int i = 0; video_modes[i].w != 0; i++) {
1225 const int w = video_modes[i].w;
1226 const int h = video_modes[i].h;
1227 if (i > 0 && (w >= default_width || h >= default_height))
1228 continue;
1229 for (int d = VIDEO_DEPTH_1BIT; d <= default_depth; d++)
1230 add_mode(display_type, w, h, video_modes[i].resolution_id, TrivialBytesPerRow(w, (video_depth)d), d);
1231 }
1232 }
1233 } else if (display_type == DISPLAY_SCREEN) {
1234 for (int i = 0; video_modes[i].w != 0; i++) {
1235 const int w = video_modes[i].w;
1236 const int h = video_modes[i].h;
1237 if (i > 0 && (w >= default_width || h >= default_height))
1238 continue;
1239 if (w == 512 && h == 384)
1240 continue;
1241 #ifdef ENABLE_VOSF
1242 for (int d = VIDEO_DEPTH_1BIT; d <= default_depth; d++)
1243 add_mode(display_type, w, h, video_modes[i].resolution_id, TrivialBytesPerRow(w, (video_depth)d), d);
1244 #else
1245 add_mode(display_type, w, h, video_modes[i].resolution_id, TrivialBytesPerRow(w, (video_depth)default_depth), default_depth);
1246 #endif
1247 }
1248 }
1249
1250 if (VideoModes.empty()) {
1251 ErrorAlert(STR_NO_XVISUAL_ERR);
1252 return false;
1253 }
1254
1255 // Find requested default mode with specified dimensions
1256 uint32 default_id;
1257 std::vector<VIDEO_MODE>::const_iterator i, end = VideoModes.end();
1258 for (i = VideoModes.begin(); i != end; ++i) {
1259 const VIDEO_MODE & mode = (*i);
1260 if (VIDEO_MODE_X == default_width && VIDEO_MODE_Y == default_height && VIDEO_MODE_DEPTH == default_depth) {
1261 default_id = VIDEO_MODE_RESOLUTION;
1262 #ifdef SHEEPSHAVER
1263 std::vector<VIDEO_MODE>::const_iterator begin = VideoModes.begin();
1264 cur_mode = distance(begin, i);
1265 #endif
1266 break;
1267 }
1268 }
1269 if (i == end) { // not found, use first available mode
1270 const VIDEO_MODE & mode = VideoModes[0];
1271 default_depth = VIDEO_MODE_DEPTH;
1272 default_id = VIDEO_MODE_RESOLUTION;
1273 #ifdef SHEEPSHAVER
1274 cur_mode = 0;
1275 #endif
1276 }
1277
1278 #ifdef SHEEPSHAVER
1279 for (int i = 0; i < VideoModes.size(); i++)
1280 VModes[i] = VideoModes[i];
1281 VideoInfo *p = &VModes[VideoModes.size()];
1282 p->viType = DIS_INVALID; // End marker
1283 p->viRowBytes = 0;
1284 p->viXsize = p->viYsize = 0;
1285 p->viAppleMode = 0;
1286 p->viAppleID = 0;
1287 #endif
1288
1289 #if DEBUG
1290 D(bug("Available video modes:\n"));
1291 for (i = VideoModes.begin(); i != end; ++i) {
1292 const VIDEO_MODE & mode = (*i);
1293 int bits = 1 << VIDEO_MODE_DEPTH;
1294 if (bits == 16)
1295 bits = 15;
1296 else if (bits == 32)
1297 bits = 24;
1298 D(bug(" %dx%d (ID %02x), %d colors\n", VIDEO_MODE_X, VIDEO_MODE_Y, VIDEO_MODE_RESOLUTION, 1 << bits));
1299 }
1300 #endif
1301
1302 // Create SDL_monitor_desc for this (the only) display
1303 SDL_monitor_desc *monitor = new SDL_monitor_desc(VideoModes, (video_depth)default_depth, default_id);
1304 VideoMonitors.push_back(monitor);
1305
1306 // Open display
1307 return monitor->video_open();
1308 }
1309
1310
1311 /*
1312 * Deinitialization
1313 */
1314
1315 // Close display
1316 void SDL_monitor_desc::video_close(void)
1317 {
1318 D(bug("video_close()\n"));
1319
1320 #ifdef WIN32
1321 // Remove message handler for WM_DEVICECHANGE
1322 HWND the_window = GetMainWindowHandle();
1323 SetWindowLongPtr(the_window, GWLP_WNDPROC, (LONG_PTR)sdl_window_proc);
1324 #endif
1325
1326 // Stop redraw thread
1327 #ifndef USE_CPU_EMUL_SERVICES
1328 if (redraw_thread_active) {
1329 redraw_thread_cancel = true;
1330 SDL_WaitThread(redraw_thread, NULL);
1331 }
1332 #endif
1333 redraw_thread_active = false;
1334
1335 // Unlock frame buffer
1336 UNLOCK_FRAME_BUFFER;
1337 D(bug(" frame buffer unlocked\n"));
1338
1339 // Close display
1340 delete drv;
1341 drv = NULL;
1342 }
1343
1344 void VideoExit(void)
1345 {
1346 // Close displays
1347 vector<monitor_desc *>::iterator i, end = VideoMonitors.end();
1348 for (i = VideoMonitors.begin(); i != end; ++i)
1349 dynamic_cast<SDL_monitor_desc *>(*i)->video_close();
1350
1351 // Destroy locks
1352 if (frame_buffer_lock)
1353 SDL_DestroyMutex(frame_buffer_lock);
1354 if (sdl_palette_lock)
1355 SDL_DestroyMutex(sdl_palette_lock);
1356 if (sdl_events_lock)
1357 SDL_DestroyMutex(sdl_events_lock);
1358 }
1359
1360
1361 /*
1362 * Close down full-screen mode (if bringing up error alerts is unsafe while in full-screen mode)
1363 */
1364
1365 void VideoQuitFullScreen(void)
1366 {
1367 D(bug("VideoQuitFullScreen()\n"));
1368 quit_full_screen = true;
1369 }
1370
1371
1372 /*
1373 * Mac VBL interrupt
1374 */
1375
1376 /*
1377 * Execute video VBL routine
1378 */
1379
1380 #ifdef SHEEPSHAVER
1381 void VideoVBL(void)
1382 {
1383 // Emergency quit requested? Then quit
1384 if (emerg_quit)
1385 QuitEmulator();
1386
1387 // Temporarily give up frame buffer lock (this is the point where
1388 // we are suspended when the user presses Ctrl-Tab)
1389 UNLOCK_FRAME_BUFFER;
1390 LOCK_FRAME_BUFFER;
1391
1392 // Execute video VBL
1393 if (private_data != NULL && private_data->interruptsEnabled)
1394 VSLDoInterruptService(private_data->vslServiceID);
1395 }
1396 #else
1397 void VideoInterrupt(void)
1398 {
1399 // We must fill in the events queue in the same thread that did call SDL_SetVideoMode()
1400 SDL_PumpEvents();
1401
1402 // Emergency quit requested? Then quit
1403 if (emerg_quit)
1404 QuitEmulator();
1405
1406 // Temporarily give up frame buffer lock (this is the point where
1407 // we are suspended when the user presses Ctrl-Tab)
1408 UNLOCK_FRAME_BUFFER;
1409 LOCK_FRAME_BUFFER;
1410 }
1411 #endif
1412
1413
1414 /*
1415 * Set palette
1416 */
1417
1418 #ifdef SHEEPSHAVER
1419 void video_set_palette(void)
1420 {
1421 monitor_desc * monitor = VideoMonitors[0];
1422 int n_colors = palette_size(monitor->get_current_mode().viAppleMode);
1423 uint8 pal[256 * 3];
1424 for (int c = 0; c < n_colors; c++) {
1425 pal[c*3 + 0] = mac_pal[c].red;
1426 pal[c*3 + 1] = mac_pal[c].green;
1427 pal[c*3 + 2] = mac_pal[c].blue;
1428 }
1429 monitor->set_palette(pal, n_colors);
1430 }
1431 #endif
1432
1433 void SDL_monitor_desc::set_palette(uint8 *pal, int num_in)
1434 {
1435 const VIDEO_MODE &mode = get_current_mode();
1436
1437 // FIXME: how can we handle the gamma ramp?
1438 if ((int)VIDEO_MODE_DEPTH > VIDEO_DEPTH_8BIT)
1439 return;
1440
1441 LOCK_PALETTE;
1442
1443 // Convert colors to XColor array
1444 int num_out = 256;
1445 bool stretch = false;
1446 SDL_Color *p = sdl_palette;
1447 for (int i=0; i<num_out; i++) {
1448 int c = (stretch ? (i * num_in) / num_out : i);
1449 p->r = pal[c*3 + 0] * 0x0101;
1450 p->g = pal[c*3 + 1] * 0x0101;
1451 p->b = pal[c*3 + 2] * 0x0101;
1452 p++;
1453 }
1454
1455 // Recalculate pixel color expansion map
1456 if (!IsDirectMode(mode)) {
1457 for (int i=0; i<256; i++) {
1458 int c = i & (num_in-1); // If there are less than 256 colors, we repeat the first entries (this makes color expansion easier)
1459 ExpandMap[i] = SDL_MapRGB(drv->s->format, pal[c*3+0], pal[c*3+1], pal[c*3+2]);
1460 }
1461
1462 #ifdef ENABLE_VOSF
1463 if (use_vosf) {
1464 // We have to redraw everything because the interpretation of pixel values changed
1465 LOCK_VOSF;
1466 PFLAG_SET_ALL;
1467 UNLOCK_VOSF;
1468 memset(the_buffer_copy, 0, VIDEO_MODE_ROW_BYTES * VIDEO_MODE_Y);
1469 }
1470 #endif
1471 }
1472
1473 // Tell redraw thread to change palette
1474 sdl_palette_changed = true;
1475
1476 UNLOCK_PALETTE;
1477 }
1478
1479
1480 /*
1481 * Switch video mode
1482 */
1483
1484 #ifdef SHEEPSHAVER
1485 int16 video_mode_change(VidLocals *csSave, uint32 ParamPtr)
1486 {
1487 /* return if no mode change */
1488 if ((csSave->saveData == ReadMacInt32(ParamPtr + csData)) &&
1489 (csSave->saveMode == ReadMacInt16(ParamPtr + csMode))) return noErr;
1490
1491 /* first find video mode in table */
1492 for (int i=0; VModes[i].viType != DIS_INVALID; i++) {
1493 if ((ReadMacInt16(ParamPtr + csMode) == VModes[i].viAppleMode) &&
1494 (ReadMacInt32(ParamPtr + csData) == VModes[i].viAppleID)) {
1495 csSave->saveMode = ReadMacInt16(ParamPtr + csMode);
1496 csSave->saveData = ReadMacInt32(ParamPtr + csData);
1497 csSave->savePage = ReadMacInt16(ParamPtr + csPage);
1498
1499 // Disable interrupts and pause redraw thread
1500 DisableInterrupt();
1501 thread_stop_ack = false;
1502 thread_stop_req = true;
1503 while (!thread_stop_ack) ;
1504
1505 cur_mode = i;
1506 monitor_desc *monitor = VideoMonitors[0];
1507 monitor->switch_to_current_mode();
1508
1509 WriteMacInt32(ParamPtr + csBaseAddr, screen_base);
1510 csSave->saveBaseAddr=screen_base;
1511 csSave->saveData=VModes[cur_mode].viAppleID;/* First mode ... */
1512 csSave->saveMode=VModes[cur_mode].viAppleMode;
1513
1514 // Enable interrupts and resume redraw thread
1515 thread_stop_req = false;
1516 EnableInterrupt();
1517 return noErr;
1518 }
1519 }
1520 return paramErr;
1521 }
1522 #endif
1523
1524 void SDL_monitor_desc::switch_to_current_mode(void)
1525 {
1526 // Close and reopen display
1527 LOCK_EVENTS;
1528 video_close();
1529 video_open();
1530 UNLOCK_EVENTS;
1531
1532 if (drv == NULL) {
1533 ErrorAlert(STR_OPEN_WINDOW_ERR);
1534 QuitEmulator();
1535 }
1536 }
1537
1538
1539 /*
1540 * Can we set the MacOS cursor image into the window?
1541 */
1542
1543 #ifdef SHEEPSHAVER
1544 bool video_can_change_cursor(void)
1545 {
1546 static char driver[] = "Quartz?";
1547 static int quartzok = -1;
1548
1549 if (display_type != DISPLAY_WINDOW)
1550 return false;
1551
1552 if (quartzok < 0) {
1553 if (SDL_VideoDriverName(driver, sizeof driver) == NULL || strncmp(driver, "Quartz", sizeof driver))
1554 quartzok = true;
1555 else {
1556 // Quartz driver bug prevents cursor changing in SDL 1.2.11 and later
1557 const SDL_version *vp = SDL_Linked_Version();
1558 quartzok = SDL_VERSIONNUM(vp->major, vp->minor, vp->patch) <= SDL_VERSIONNUM(1, 2, 10);
1559 }
1560 }
1561
1562 return quartzok;
1563 }
1564 #endif
1565
1566
1567 /*
1568 * Set cursor image for window
1569 */
1570
1571 #ifdef SHEEPSHAVER
1572 void video_set_cursor(void)
1573 {
1574 cursor_changed = true;
1575 }
1576 #endif
1577
1578
1579 /*
1580 * Keyboard-related utilify functions
1581 */
1582
1583 static bool is_modifier_key(SDL_KeyboardEvent const & e)
1584 {
1585 switch (e.keysym.sym) {
1586 case SDLK_NUMLOCK:
1587 case SDLK_CAPSLOCK:
1588 case SDLK_SCROLLOCK:
1589 case SDLK_RSHIFT:
1590 case SDLK_LSHIFT:
1591 case SDLK_RCTRL:
1592 case SDLK_LCTRL:
1593 case SDLK_RALT:
1594 case SDLK_LALT:
1595 case SDLK_RMETA:
1596 case SDLK_LMETA:
1597 case SDLK_LSUPER:
1598 case SDLK_RSUPER:
1599 case SDLK_MODE:
1600 case SDLK_COMPOSE:
1601 return true;
1602 }
1603 return false;
1604 }
1605
1606 static bool is_ctrl_down(SDL_keysym const & ks)
1607 {
1608 return ctrl_down || (ks.mod & KMOD_CTRL);
1609 }
1610
1611
1612 /*
1613 * Translate key event to Mac keycode, returns -1 if no keycode was found
1614 * and -2 if the key was recognized as a hotkey
1615 */
1616
1617 static int kc_decode(SDL_keysym const & ks, bool key_down)
1618 {
1619 switch (ks.sym) {
1620 case SDLK_a: return 0x00;
1621 case SDLK_b: return 0x0b;
1622 case SDLK_c: return 0x08;
1623 case SDLK_d: return 0x02;
1624 case SDLK_e: return 0x0e;
1625 case SDLK_f: return 0x03;
1626 case SDLK_g: return 0x05;
1627 case SDLK_h: return 0x04;
1628 case SDLK_i: return 0x22;
1629 case SDLK_j: return 0x26;
1630 case SDLK_k: return 0x28;
1631 case SDLK_l: return 0x25;
1632 case SDLK_m: return 0x2e;
1633 case SDLK_n: return 0x2d;
1634 case SDLK_o: return 0x1f;
1635 case SDLK_p: return 0x23;
1636 case SDLK_q: return 0x0c;
1637 case SDLK_r: return 0x0f;
1638 case SDLK_s: return 0x01;
1639 case SDLK_t: return 0x11;
1640 case SDLK_u: return 0x20;
1641 case SDLK_v: return 0x09;
1642 case SDLK_w: return 0x0d;
1643 case SDLK_x: return 0x07;
1644 case SDLK_y: return 0x10;
1645 case SDLK_z: return 0x06;
1646
1647 case SDLK_1: case SDLK_EXCLAIM: return 0x12;
1648 case SDLK_2: case SDLK_AT: return 0x13;
1649 case SDLK_3: case SDLK_HASH: return 0x14;
1650 case SDLK_4: case SDLK_DOLLAR: return 0x15;
1651 case SDLK_5: return 0x17;
1652 case SDLK_6: return 0x16;
1653 case SDLK_7: return 0x1a;
1654 case SDLK_8: return 0x1c;
1655 case SDLK_9: return 0x19;
1656 case SDLK_0: return 0x1d;
1657
1658 case SDLK_BACKQUOTE: return 0x0a;
1659 case SDLK_MINUS: case SDLK_UNDERSCORE: return 0x1b;
1660 case SDLK_EQUALS: case SDLK_PLUS: return 0x18;
1661 case SDLK_LEFTBRACKET: return 0x21;
1662 case SDLK_RIGHTBRACKET: return 0x1e;
1663 case SDLK_BACKSLASH: return 0x2a;
1664 case SDLK_SEMICOLON: case SDLK_COLON: return 0x29;
1665 case SDLK_QUOTE: case SDLK_QUOTEDBL: return 0x27;
1666 case SDLK_COMMA: case SDLK_LESS: return 0x2b;
1667 case SDLK_PERIOD: case SDLK_GREATER: return 0x2f;
1668 case SDLK_SLASH: case SDLK_QUESTION: return 0x2c;
1669
1670 case SDLK_TAB: if (is_ctrl_down(ks)) {if (!key_down) drv->suspend(); return -2;} else return 0x30;
1671 case SDLK_RETURN: return 0x24;
1672 case SDLK_SPACE: return 0x31;
1673 case SDLK_BACKSPACE: return 0x33;
1674
1675 case SDLK_DELETE: return 0x75;
1676 case SDLK_INSERT: return 0x72;
1677 case SDLK_HOME: case SDLK_HELP: return 0x73;
1678 case SDLK_END: return 0x77;
1679 case SDLK_PAGEUP: return 0x74;
1680 case SDLK_PAGEDOWN: return 0x79;
1681
1682 case SDLK_LCTRL: return 0x36;
1683 case SDLK_RCTRL: return 0x36;
1684 case SDLK_LSHIFT: return 0x38;
1685 case SDLK_RSHIFT: return 0x38;
1686 #if (defined(__APPLE__) && defined(__MACH__))
1687 case SDLK_LALT: return 0x3a;
1688 case SDLK_RALT: return 0x3a;
1689 case SDLK_LMETA: return 0x37;
1690 case SDLK_RMETA: return 0x37;
1691 #else
1692 case SDLK_LALT: return 0x37;
1693 case SDLK_RALT: return 0x37;
1694 case SDLK_LMETA: return 0x3a;
1695 case SDLK_RMETA: return 0x3a;
1696 #endif
1697 case SDLK_LSUPER: return 0x3a; // "Windows" key
1698 case SDLK_RSUPER: return 0x3a;
1699 case SDLK_MENU: return 0x32;
1700 case SDLK_CAPSLOCK: return 0x39;
1701 case SDLK_NUMLOCK: return 0x47;
1702
1703 case SDLK_UP: return 0x3e;
1704 case SDLK_DOWN: return 0x3d;
1705 case SDLK_LEFT: return 0x3b;
1706 case SDLK_RIGHT: return 0x3c;
1707
1708 case SDLK_ESCAPE: if (is_ctrl_down(ks)) {if (!key_down) { quit_full_screen = true; emerg_quit = true; } return -2;} else return 0x35;
1709
1710 case SDLK_F1: if (is_ctrl_down(ks)) {if (!key_down) SysMountFirstFloppy(); return -2;} else return 0x7a;
1711 case SDLK_F2: return 0x78;
1712 case SDLK_F3: return 0x63;
1713 case SDLK_F4: return 0x76;
1714 case SDLK_F5: if (is_ctrl_down(ks)) {if (!key_down) drv->toggle_mouse_grab(); return -2;} else return 0x60;
1715 case SDLK_F6: return 0x61;
1716 case SDLK_F7: return 0x62;
1717 case SDLK_F8: return 0x64;
1718 case SDLK_F9: return 0x65;
1719 case SDLK_F10: return 0x6d;
1720 case SDLK_F11: return 0x67;
1721 case SDLK_F12: return 0x6f;
1722
1723 case SDLK_PRINT: return 0x69;
1724 case SDLK_SCROLLOCK: return 0x6b;
1725 case SDLK_PAUSE: return 0x71;
1726
1727 case SDLK_KP0: return 0x52;
1728 case SDLK_KP1: return 0x53;
1729 case SDLK_KP2: return 0x54;
1730 case SDLK_KP3: return 0x55;
1731 case SDLK_KP4: return 0x56;
1732 case SDLK_KP5: return 0x57;
1733 case SDLK_KP6: return 0x58;
1734 case SDLK_KP7: return 0x59;
1735 case SDLK_KP8: return 0x5b;
1736 case SDLK_KP9: return 0x5c;
1737 case SDLK_KP_PERIOD: return 0x41;
1738 case SDLK_KP_PLUS: return 0x45;
1739 case SDLK_KP_MINUS: return 0x4e;
1740 case SDLK_KP_MULTIPLY: return 0x43;
1741 case SDLK_KP_DIVIDE: return 0x4b;
1742 case SDLK_KP_ENTER: return 0x4c;
1743 case SDLK_KP_EQUALS: return 0x51;
1744 }
1745 D(bug("Unhandled SDL keysym: %d\n", ks.sym));
1746 return -1;
1747 }
1748
1749 static int event2keycode(SDL_KeyboardEvent const &ev, bool key_down)
1750 {
1751 return kc_decode(ev.keysym, key_down);
1752 }
1753
1754
1755 /*
1756 * SDL event handling
1757 */
1758
1759 static void handle_events(void)
1760 {
1761 SDL_Event events[10];
1762 const int n_max_events = sizeof(events) / sizeof(events[0]);
1763 int n_events;
1764
1765 while ((n_events = SDL_PeepEvents(events, n_max_events, SDL_GETEVENT, sdl_eventmask)) > 0) {
1766 for (int i = 0; i < n_events; i++) {
1767 SDL_Event const & event = events[i];
1768 switch (event.type) {
1769
1770 // Mouse button
1771 case SDL_MOUSEBUTTONDOWN: {
1772 unsigned int button = event.button.button;
1773 if (button < 4)
1774 ADBMouseDown(button - 1);
1775 else if (button < 6) { // Wheel mouse
1776 if (mouse_wheel_mode == 0) {
1777 int key = (button == 5) ? 0x79 : 0x74; // Page up/down
1778 ADBKeyDown(key);
1779 ADBKeyUp(key);
1780 } else {
1781 int key = (button == 5) ? 0x3d : 0x3e; // Cursor up/down
1782 for(int i=0; i<mouse_wheel_lines; i++) {
1783 ADBKeyDown(key);
1784 ADBKeyUp(key);
1785 }
1786 }
1787 }
1788 break;
1789 }
1790 case SDL_MOUSEBUTTONUP: {
1791 unsigned int button = event.button.button;
1792 if (button < 4)
1793 ADBMouseUp(button - 1);
1794 break;
1795 }
1796
1797 // Mouse moved
1798 case SDL_MOUSEMOTION:
1799 drv->mouse_moved(event.motion.x, event.motion.y);
1800 break;
1801
1802 // Keyboard
1803 case SDL_KEYDOWN: {
1804 int code = -1;
1805 if (use_keycodes && !is_modifier_key(event.key)) {
1806 if (event2keycode(event.key, true) != -2) // This is called to process the hotkeys
1807 code = keycode_table[event.key.keysym.scancode & 0xff];
1808 } else
1809 code = event2keycode(event.key, true);
1810 if (code >= 0) {
1811 if (!emul_suspended) {
1812 if (code == 0x39) { // Caps Lock pressed
1813 if (caps_on) {
1814 ADBKeyUp(code);
1815 caps_on = false;
1816 } else {
1817 ADBKeyDown(code);
1818 caps_on = true;
1819 }
1820 } else
1821 ADBKeyDown(code);
1822 if (code == 0x36)
1823 ctrl_down = true;
1824 } else {
1825 if (code == 0x31)
1826 drv->resume(); // Space wakes us up
1827 }
1828 }
1829 break;
1830 }
1831 case SDL_KEYUP: {
1832 int code = -1;
1833 if (use_keycodes && !is_modifier_key(event.key)) {
1834 if (event2keycode(event.key, false) != -2) // This is called to process the hotkeys
1835 code = keycode_table[event.key.keysym.scancode & 0xff];
1836 } else
1837 code = event2keycode(event.key, false);
1838 if (code >= 0) {
1839 if (code == 0x39) { // Caps Lock released
1840 if (caps_on) {
1841 ADBKeyUp(code);
1842 caps_on = false;
1843 } else {
1844 ADBKeyDown(code);
1845 caps_on = true;
1846 }
1847 } else
1848 ADBKeyUp(code);
1849 if (code == 0x36)
1850 ctrl_down = false;
1851 }
1852 break;
1853 }
1854
1855 // Hidden parts exposed, force complete refresh of window
1856 case SDL_VIDEOEXPOSE:
1857 if (display_type == DISPLAY_WINDOW) {
1858 const VIDEO_MODE &mode = VideoMonitors[0]->get_current_mode();
1859 #ifdef ENABLE_VOSF
1860 if (use_vosf) { // VOSF refresh
1861 LOCK_VOSF;
1862 PFLAG_SET_ALL;
1863 UNLOCK_VOSF;
1864 memset(the_buffer_copy, 0, VIDEO_MODE_ROW_BYTES * VIDEO_MODE_Y);
1865 }
1866 else
1867 #endif
1868 memset(the_buffer_copy, 0, VIDEO_MODE_ROW_BYTES * VIDEO_MODE_Y);
1869 }
1870 break;
1871
1872 // Window "close" widget clicked
1873 case SDL_QUIT:
1874 ADBKeyDown(0x7f); // Power key
1875 ADBKeyUp(0x7f);
1876 break;
1877
1878 // Application activate/deactivate; consume the event but otherwise ignore it
1879 case SDL_ACTIVEEVENT:
1880 break;
1881 }
1882 }
1883 }
1884 }
1885
1886
1887 /*
1888 * Window display update
1889 */
1890
1891 // Static display update (fixed frame rate, but incremental)
1892 static void update_display_static(driver_window *drv)
1893 {
1894 // Incremental update code
1895 int wide = 0, high = 0, x1, x2, y1, y2, i, j;
1896 const VIDEO_MODE &mode = drv->mode;
1897 int bytes_per_row = VIDEO_MODE_ROW_BYTES;
1898 uint8 *p, *p2;
1899
1900 // Check for first line from top and first line from bottom that have changed
1901 y1 = 0;
1902 for (j=0; j<VIDEO_MODE_Y; j++) {
1903 if (memcmp(&the_buffer[j * bytes_per_row], &the_buffer_copy[j * bytes_per_row], bytes_per_row)) {
1904 y1 = j;
1905 break;
1906 }
1907 }
1908 y2 = y1 - 1;
1909 for (j=VIDEO_MODE_Y-1; j>=y1; j--) {
1910 if (memcmp(&the_buffer[j * bytes_per_row], &the_buffer_copy[j * bytes_per_row], bytes_per_row)) {
1911 y2 = j;
1912 break;
1913 }
1914 }
1915 high = y2 - y1 + 1;
1916
1917 // Check for first column from left and first column from right that have changed
1918 if (high) {
1919 if ((int)VIDEO_MODE_DEPTH < VIDEO_DEPTH_8BIT) {
1920 const int src_bytes_per_row = bytes_per_row;
1921 const int dst_bytes_per_row = drv->s->pitch;
1922 const int pixels_per_byte = VIDEO_MODE_X / src_bytes_per_row;
1923
1924 x1 = VIDEO_MODE_X / pixels_per_byte;
1925 for (j = y1; j <= y2; j++) {
1926 p = &the_buffer[j * bytes_per_row];
1927 p2 = &the_buffer_copy[j * bytes_per_row];
1928 for (i = 0; i < x1; i++) {
1929 if (*p != *p2) {
1930 x1 = i;
1931 break;
1932 }
1933 p++; p2++;
1934 }
1935 }
1936 x2 = x1;
1937 for (j = y1; j <= y2; j++) {
1938 p = &the_buffer[j * bytes_per_row];
1939 p2 = &the_buffer_copy[j * bytes_per_row];
1940 p += bytes_per_row;
1941 p2 += bytes_per_row;
1942 for (i = (VIDEO_MODE_X / pixels_per_byte); i > x2; i--) {
1943 p--; p2--;
1944 if (*p != *p2) {
1945 x2 = i;
1946 break;
1947 }
1948 }
1949 }
1950 x1 *= pixels_per_byte;
1951 x2 *= pixels_per_byte;
1952 wide = (x2 - x1 + pixels_per_byte - 1) & -pixels_per_byte;
1953
1954 // Update copy of the_buffer
1955 if (high && wide) {
1956
1957 // Lock surface, if required
1958 if (SDL_MUSTLOCK(drv->s))
1959 SDL_LockSurface(drv->s);
1960
1961 // Blit to screen surface
1962 int si = y1 * src_bytes_per_row + (x1 / pixels_per_byte);
1963 int di = y1 * dst_bytes_per_row + x1;
1964 for (j = y1; j <= y2; j++) {
1965 memcpy(the_buffer_copy + si, the_buffer + si, wide / pixels_per_byte);
1966 Screen_blit((uint8 *)drv->s->pixels + di, the_buffer + si, wide / pixels_per_byte);
1967 si += src_bytes_per_row;
1968 di += dst_bytes_per_row;
1969 }
1970
1971 // Unlock surface, if required
1972 if (SDL_MUSTLOCK(drv->s))
1973 SDL_UnlockSurface(drv->s);
1974
1975 // Refresh display
1976 SDL_UpdateRect(drv->s, x1, y1, wide, high);
1977 }
1978
1979 } else {
1980 const int bytes_per_pixel = VIDEO_MODE_ROW_BYTES / VIDEO_MODE_X;
1981
1982 x1 = VIDEO_MODE_X;
1983 for (j=y1; j<=y2; j++) {
1984 p = &the_buffer[j * bytes_per_row];
1985 p2 = &the_buffer_copy[j * bytes_per_row];
1986 for (i=0; i<x1*bytes_per_pixel; i++) {
1987 if (*p != *p2) {
1988 x1 = i / bytes_per_pixel;
1989 break;
1990 }
1991 p++; p2++;
1992 }
1993 }
1994 x2 = x1;
1995 for (j=y1; j<=y2; j++) {
1996 p = &the_buffer[j * bytes_per_row];
1997 p2 = &the_buffer_copy[j * bytes_per_row];
1998 p += bytes_per_row;
1999 p2 += bytes_per_row;
2000 for (i=VIDEO_MODE_X*bytes_per_pixel; i>x2*bytes_per_pixel; i--) {
2001 p--;
2002 p2--;
2003 if (*p != *p2) {
2004 x2 = i / bytes_per_pixel;
2005 break;
2006 }
2007 }
2008 }
2009 wide = x2 - x1;
2010
2011 // Update copy of the_buffer
2012 if (high && wide) {
2013
2014 // Lock surface, if required
2015 if (SDL_MUSTLOCK(drv->s))
2016 SDL_LockSurface(drv->s);
2017
2018 // Blit to screen surface
2019 for (j=y1; j<=y2; j++) {
2020 i = j * bytes_per_row + x1 * bytes_per_pixel;
2021 memcpy(the_buffer_copy + i, the_buffer + i, bytes_per_pixel * wide);
2022 Screen_blit((uint8 *)drv->s->pixels + i, the_buffer + i, bytes_per_pixel * wide);
2023 }
2024
2025 // Unlock surface, if required
2026 if (SDL_MUSTLOCK(drv->s))
2027 SDL_UnlockSurface(drv->s);
2028
2029 // Refresh display
2030 SDL_UpdateRect(drv->s, x1, y1, wide, high);
2031 }
2032 }
2033 }
2034 }
2035
2036 // Static display update (fixed frame rate, bounding boxes based)
2037 // XXX use NQD bounding boxes to help detect dirty areas?
2038 static void update_display_static_bbox(driver_window *drv)
2039 {
2040 const VIDEO_MODE &mode = drv->mode;
2041
2042 // Allocate bounding boxes for SDL_UpdateRects()
2043 const int N_PIXELS = 64;
2044 const int n_x_boxes = (VIDEO_MODE_X + N_PIXELS - 1) / N_PIXELS;
2045 const int n_y_boxes = (VIDEO_MODE_Y + N_PIXELS - 1) / N_PIXELS;
2046 SDL_Rect *boxes = (SDL_Rect *)alloca(sizeof(SDL_Rect) * n_x_boxes * n_y_boxes);
2047 int nr_boxes = 0;
2048
2049 // Lock surface, if required
2050 if (SDL_MUSTLOCK(drv->s))
2051 SDL_LockSurface(drv->s);
2052
2053 // Update the surface from Mac screen
2054 const int bytes_per_row = VIDEO_MODE_ROW_BYTES;
2055 const int bytes_per_pixel = bytes_per_row / VIDEO_MODE_X;
2056 int x, y;
2057 for (y = 0; y < VIDEO_MODE_Y; y += N_PIXELS) {
2058 int h = N_PIXELS;
2059 if (h > VIDEO_MODE_Y - y)
2060 h = VIDEO_MODE_Y - y;
2061 for (x = 0; x < VIDEO_MODE_X; x += N_PIXELS) {
2062 int w = N_PIXELS;
2063 if (w > VIDEO_MODE_X - x)
2064 w = VIDEO_MODE_X - x;
2065 const int xs = w * bytes_per_pixel;
2066 const int xb = x * bytes_per_pixel;
2067 bool dirty = false;
2068 for (int j = y; j < (y + h); j++) {
2069 const int yb = j * bytes_per_row;
2070 if (memcmp(&the_buffer[yb + xb], &the_buffer_copy[yb + xb], xs) != 0) {
2071 memcpy(&the_buffer_copy[yb + xb], &the_buffer[yb + xb], xs);
2072 Screen_blit((uint8 *)drv->s->pixels + yb + xb, the_buffer + yb + xb, xs);
2073 dirty = true;
2074 }
2075 }
2076 if (dirty) {
2077 boxes[nr_boxes].x = x;
2078 boxes[nr_boxes].y = y;
2079 boxes[nr_boxes].w = w;
2080 boxes[nr_boxes].h = h;
2081 nr_boxes++;
2082 }
2083 }
2084 }
2085
2086 // Unlock surface, if required
2087 if (SDL_MUSTLOCK(drv->s))
2088 SDL_UnlockSurface(drv->s);
2089
2090 // Refresh display
2091 if (nr_boxes)
2092 SDL_UpdateRects(drv->s, nr_boxes, boxes);
2093 }
2094
2095
2096 // We suggest the compiler to inline the next two functions so that it
2097 // may specialise the code according to the current screen depth and
2098 // display type. A clever compiler would do that job by itself though...
2099
2100 // NOTE: update_display_vosf is inlined too
2101
2102 static inline void possibly_quit_dga_mode()
2103 {
2104 // Quit DGA mode if requested (something terrible has happened and we
2105 // want to give control back to the user)
2106 if (quit_full_screen) {
2107 quit_full_screen = false;
2108 delete drv;
2109 drv = NULL;
2110 }
2111 }
2112
2113 static inline void possibly_ungrab_mouse()
2114 {
2115 // Ungrab mouse if requested (something terrible has happened and we
2116 // want to give control back to the user)
2117 if (quit_full_screen) {
2118 quit_full_screen = false;
2119 if (drv)
2120 drv->ungrab_mouse();
2121 }
2122 }
2123
2124 static inline void handle_palette_changes(void)
2125 {
2126 LOCK_PALETTE;
2127
2128 if (sdl_palette_changed) {
2129 sdl_palette_changed = false;
2130 drv->update_palette();
2131 }
2132
2133 UNLOCK_PALETTE;
2134 }
2135
2136 static void video_refresh_dga(void)
2137 {
2138 // Quit DGA mode if requested
2139 possibly_quit_dga_mode();
2140 }
2141
2142 #ifdef ENABLE_VOSF
2143 #if REAL_ADDRESSING || DIRECT_ADDRESSING
2144 static void video_refresh_dga_vosf(void)
2145 {
2146 // Quit DGA mode if requested
2147 possibly_quit_dga_mode();
2148
2149 // Update display (VOSF variant)
2150 static int tick_counter = 0;
2151 if (++tick_counter >= frame_skip) {
2152 tick_counter = 0;
2153 if (mainBuffer.dirty) {
2154 LOCK_VOSF;
2155 update_display_dga_vosf(static_cast<driver_fullscreen *>(drv));
2156 UNLOCK_VOSF;
2157 }
2158 }
2159 }
2160 #endif
2161
2162 static void video_refresh_window_vosf(void)
2163 {
2164 // Ungrab mouse if requested
2165 possibly_ungrab_mouse();
2166
2167 // Update display (VOSF variant)
2168 static int tick_counter = 0;
2169 if (++tick_counter >= frame_skip) {
2170 tick_counter = 0;
2171 if (mainBuffer.dirty) {
2172 LOCK_VOSF;
2173 update_display_window_vosf(static_cast<driver_window *>(drv));
2174 UNLOCK_VOSF;
2175 }
2176 }
2177 }
2178 #endif // def ENABLE_VOSF
2179
2180 static void video_refresh_window_static(void)
2181 {
2182 // Ungrab mouse if requested
2183 possibly_ungrab_mouse();
2184
2185 // Update display (static variant)
2186 static int tick_counter = 0;
2187 if (++tick_counter >= frame_skip) {
2188 tick_counter = 0;
2189 const VIDEO_MODE &mode = drv->mode;
2190 if ((int)VIDEO_MODE_DEPTH >= VIDEO_DEPTH_8BIT)
2191 update_display_static_bbox(static_cast<driver_window *>(drv));
2192 else
2193 update_display_static(static_cast<driver_window *>(drv));
2194 }
2195 }
2196
2197
2198 /*
2199 * Thread for screen refresh, input handling etc.
2200 */
2201
2202 static void VideoRefreshInit(void)
2203 {
2204 // TODO: set up specialised 8bpp VideoRefresh handlers ?
2205 if (display_type == DISPLAY_SCREEN) {
2206 #if ENABLE_VOSF && (REAL_ADDRESSING || DIRECT_ADDRESSING)
2207 if (use_vosf)
2208 video_refresh = video_refresh_dga_vosf;
2209 else
2210 #endif
2211 video_refresh = video_refresh_dga;
2212 }
2213 else {
2214 #ifdef ENABLE_VOSF
2215 if (use_vosf)
2216 video_refresh = video_refresh_window_vosf;
2217 else
2218 #endif
2219 video_refresh = video_refresh_window_static;
2220 }
2221 }
2222
2223 static inline void do_video_refresh(void)
2224 {
2225 // Handle SDL events
2226 handle_events();
2227
2228 // Update display
2229 video_refresh();
2230
2231 #ifdef SHEEPSHAVER
2232 // Set new cursor image if it was changed
2233 if (cursor_changed && sdl_cursor) {
2234 cursor_changed = false;
2235 LOCK_EVENTS;
2236 SDL_FreeCursor(sdl_cursor);
2237 sdl_cursor = SDL_CreateCursor(MacCursor + 4, MacCursor + 36, 16, 16, MacCursor[2], MacCursor[3]);
2238 if (sdl_cursor) {
2239 SDL_ShowCursor(private_data == NULL || private_data->cursorVisible);
2240 SDL_SetCursor(sdl_cursor);
2241 #ifdef WIN32
2242 // XXX Windows apparently needs an extra mouse event to
2243 // make the new cursor image visible
2244 int visible = SDL_ShowCursor(-1);
2245 if (visible) {
2246 int x, y;
2247 SDL_GetMouseState(&x, &y);
2248 SDL_WarpMouse(x, y);
2249 }
2250 #endif
2251 }
2252 UNLOCK_EVENTS;
2253 }
2254 #endif
2255
2256 // Set new palette if it was changed
2257 handle_palette_changes();
2258 }
2259
2260 // This function is called on non-threaded platforms from a timer interrupt
2261 void VideoRefresh(void)
2262 {
2263 // We need to check redraw_thread_active to inhibit refreshed during
2264 // mode changes on non-threaded platforms
2265 if (!redraw_thread_active)
2266 return;
2267
2268 // Process pending events and update display
2269 do_video_refresh();
2270 }
2271
2272 const int VIDEO_REFRESH_HZ = 60;
2273 const int VIDEO_REFRESH_DELAY = 1000000 / VIDEO_REFRESH_HZ;
2274
2275 #ifndef USE_CPU_EMUL_SERVICES
2276 static int redraw_func(void *arg)
2277 {
2278 uint64 start = GetTicks_usec();
2279 int64 ticks = 0;
2280 uint64 next = GetTicks_usec() + VIDEO_REFRESH_DELAY;
2281
2282 while (!redraw_thread_cancel) {
2283
2284 // Wait
2285 next += VIDEO_REFRESH_DELAY;
2286 int64 delay = next - GetTicks_usec();
2287 if (delay > 0)
2288 Delay_usec(delay);
2289 else if (delay < -VIDEO_REFRESH_DELAY)
2290 next = GetTicks_usec();
2291 ticks++;
2292
2293 #ifdef SHEEPSHAVER
2294 // Pause if requested (during video mode switches)
2295 if (thread_stop_req) {
2296 thread_stop_ack = true;
2297 continue;
2298 }
2299 #endif
2300
2301 // Process pending events and update display
2302 do_video_refresh();
2303 }
2304
2305 uint64 end = GetTicks_usec();
2306 D(bug("%lld refreshes in %lld usec = %f refreshes/sec\n", ticks, end - start, ticks * 1000000.0 / (end - start)));
2307 return 0;
2308 }
2309 #endif
2310
2311
2312 /*
2313 * Record dirty area from NQD
2314 */
2315
2316 #ifdef SHEEPSHAVER
2317 void video_set_dirty_area(int x, int y, int w, int h)
2318 {
2319 const VIDEO_MODE &mode = drv->mode;
2320 const int screen_width = VIDEO_MODE_X;
2321 const int screen_height = VIDEO_MODE_Y;
2322 const int bytes_per_row = VIDEO_MODE_ROW_BYTES;
2323
2324 #ifdef ENABLE_VOSF
2325 if (use_vosf) {
2326 vosf_set_dirty_area(x, y, w, h, screen_width, screen_height, bytes_per_row);
2327 return;
2328 }
2329 #endif
2330
2331 // XXX handle dirty bounding boxes for non-VOSF modes
2332 }
2333 #endif