ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/SDL/video_sdl.cpp
Revision: 1.30
Committed: 2007-01-21T19:33:13Z (17 years, 5 months ago) by asvitkine
Branch: MAIN
Changes since 1.29: +14 -0 lines
Log Message:
Fix for resolution switching corruption under SDL (Mac OS X).

File Contents

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