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

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

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

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

File Contents

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