ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/SDL/video_sdl.cpp
Revision: 1.26
Committed: 2006-05-13T16:58:44Z (18 years, 2 months ago) by gbeauche
Branch: MAIN
Changes since 1.25: +24 -0 lines
Log Message:
NQD dirty boxes, SDL backend.

This reduces the number of Screen_fault_handler() calls by 80%. i.e. VOSF
is now viable on this turtle MacOS X. Besides, since there is no buffer
comparison, idle sleep can really be effective. SheepShaver in idle mode
on my PBG4 now goes below 8% of CPU resources instead of 70-80% with
bounding boxes based video refreshes.

Caveat: if your program doesn't use standard MacOS routines that call NQD,
then you can expect slower (visual) performance. However, I do think the
new default behavior (VOSF+NQD) is the most common.

File Contents

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