ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/SheepShaver/src/Unix/video_x.cpp
Revision: 1.34
Committed: 2004-12-18T18:34:56Z (19 years, 5 months ago) by gbeauche
Branch: MAIN
Changes since 1.33: +0 -2 lines
Log Message:
Don't restrict MacOS X Xserver detection on native versions since SheepShaver
can run remotely and drawing to an X window on MacOS X.

File Contents

# Content
1 /*
2 * video_x.cpp - Video/graphics emulation, X11 specific stuff
3 *
4 * SheepShaver (C) 1997-2004 Marc Hellwig and Christian Bauer
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 #include "sysdeps.h"
22
23 #include <X11/Xlib.h>
24 #include <X11/Xutil.h>
25 #include <X11/keysym.h>
26 #include <X11/extensions/XShm.h>
27 #include <sys/ipc.h>
28 #include <sys/shm.h>
29 #include <errno.h>
30 #include <pthread.h>
31
32 #include <algorithm>
33
34 #ifdef ENABLE_XF86_DGA
35 # include <X11/extensions/xf86dga.h>
36 #endif
37
38 #ifdef ENABLE_XF86_VIDMODE
39 # include <X11/extensions/xf86vmode.h>
40 #endif
41
42 #include "main.h"
43 #include "adb.h"
44 #include "prefs.h"
45 #include "user_strings.h"
46 #include "about_window.h"
47 #include "video.h"
48 #include "video_defs.h"
49 #include "video_blit.h"
50
51 #define DEBUG 0
52 #include "debug.h"
53
54 #ifndef NO_STD_NAMESPACE
55 using std::sort;
56 #endif
57
58
59 // Constants
60 const char KEYCODE_FILE_NAME[] = DATADIR "/keycodes";
61 static const bool hw_mac_cursor_accl = true; // Flag: Enable MacOS to X11 copy of cursor?
62
63 // Global variables
64 static int32 frame_skip;
65 static int16 mouse_wheel_mode;
66 static int16 mouse_wheel_lines;
67 static bool redraw_thread_active = false; // Flag: Redraw thread installed
68 static pthread_attr_t redraw_thread_attr; // Redraw thread attributes
69 static volatile bool redraw_thread_cancel; // Flag: Cancel Redraw thread
70 static pthread_t redraw_thread; // Redraw thread
71
72 static bool local_X11; // Flag: X server running on local machine?
73 static volatile bool thread_stop_req = false;
74 static volatile bool thread_stop_ack = false; // Acknowledge for thread_stop_req
75
76 static bool has_dga = false; // Flag: Video DGA capable
77 static bool has_vidmode = false; // Flag: VidMode extension available
78
79 #ifdef ENABLE_VOSF
80 static bool use_vosf = true; // Flag: VOSF enabled
81 #else
82 static const bool use_vosf = false; // VOSF not possible
83 #endif
84
85 static bool palette_changed = false; // Flag: Palette changed, redraw thread must update palette
86 static bool ctrl_down = false; // Flag: Ctrl key pressed
87 static bool caps_on = false; // Flag: Caps Lock on
88 static bool quit_full_screen = false; // Flag: DGA close requested from redraw thread
89 static volatile bool quit_full_screen_ack = false; // Acknowledge for quit_full_screen
90 static bool emerg_quit = false; // Flag: Ctrl-Esc pressed, emergency quit requested from MacOS thread
91
92 static bool emul_suspended = false; // Flag: emulator suspended
93 static Window suspend_win; // "Suspend" window
94 static void *fb_save = NULL; // Saved frame buffer for suspend
95 static bool use_keycodes = false; // Flag: Use keycodes rather than keysyms
96 static int keycode_table[256]; // X keycode -> Mac keycode translation table
97
98 // X11 variables
99 static int screen; // Screen number
100 static int xdepth; // Depth of X screen
101 static int depth; // Depth of Mac frame buffer
102 static Window rootwin, the_win; // Root window and our window
103 static int num_depths = 0; // Number of available X depths
104 static int *avail_depths = NULL; // List of available X depths
105 static VisualFormat visualFormat;
106 static XVisualInfo visualInfo;
107 static Visual *vis;
108 static int color_class;
109 static int rshift, rloss, gshift, gloss, bshift, bloss; // Pixel format of DirectColor/TrueColor modes
110 static Colormap cmap[2]; // Two colormaps (DGA) for 8-bit mode
111 static XColor x_palette[256]; // Color palette to be used as CLUT and gamma table
112
113 static XColor black, white;
114 static unsigned long black_pixel, white_pixel;
115 static int eventmask;
116 static const int win_eventmask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | EnterWindowMask | ExposureMask | StructureNotifyMask;
117 static const int dga_eventmask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | StructureNotifyMask;
118
119 // Variables for window mode
120 static GC the_gc;
121 static XImage *img = NULL;
122 static XShmSegmentInfo shminfo;
123 static XImage *cursor_image, *cursor_mask_image;
124 static Pixmap cursor_map, cursor_mask_map;
125 static Cursor mac_cursor;
126 static GC cursor_gc, cursor_mask_gc;
127 static bool cursor_changed = false; // Flag: Cursor changed, window_func must update cursor
128 static bool have_shm = false; // Flag: SHM present and usable
129 static uint8 *the_buffer = NULL; // Pointer to Mac frame buffer
130 static uint8 *the_buffer_copy = NULL; // Copy of Mac frame buffer
131 static uint32 the_buffer_size; // Size of allocated the_buffer
132
133 // Variables for DGA mode
134 static int current_dga_cmap;
135
136 #ifdef ENABLE_XF86_VIDMODE
137 // Variables for XF86 VidMode support
138 static XF86VidModeModeInfo **x_video_modes; // Array of all available modes
139 static int num_x_video_modes;
140 #endif
141
142 // Mutex to protect palette
143 #ifdef HAVE_SPINLOCKS
144 static spinlock_t x_palette_lock = SPIN_LOCK_UNLOCKED;
145 #define LOCK_PALETTE spin_lock(&x_palette_lock)
146 #define UNLOCK_PALETTE spin_unlock(&x_palette_lock)
147 #elif defined(HAVE_PTHREADS)
148 static pthread_mutex_t x_palette_lock = PTHREAD_MUTEX_INITIALIZER;
149 #define LOCK_PALETTE pthread_mutex_lock(&x_palette_lock)
150 #define UNLOCK_PALETTE pthread_mutex_unlock(&x_palette_lock)
151 #else
152 #define LOCK_PALETTE
153 #define UNLOCK_PALETTE
154 #endif
155
156
157 // Prototypes
158 static void *redraw_func(void *arg);
159
160
161 // From main_unix.cpp
162 extern char *x_display_name;
163 extern Display *x_display;
164
165 // From sys_unix.cpp
166 extern void SysMountFirstFloppy(void);
167
168 // From clip_unix.cpp
169 extern void ClipboardSelectionClear(XSelectionClearEvent *);
170 extern void ClipboardSelectionRequest(XSelectionRequestEvent *);
171
172
173 // Video acceleration through SIGSEGV
174 #ifdef ENABLE_VOSF
175 # include "video_vosf.h"
176 #endif
177
178
179 /*
180 * Utility functions
181 */
182
183 // Get current video mode
184 static inline int get_current_mode(void)
185 {
186 return VModes[cur_mode].viAppleMode;
187 }
188
189 // Find palette size for given color depth
190 static int palette_size(int mode)
191 {
192 switch (mode) {
193 case APPLE_1_BIT: return 2;
194 case APPLE_2_BIT: return 4;
195 case APPLE_4_BIT: return 16;
196 case APPLE_8_BIT: return 256;
197 case APPLE_16_BIT: return 32;
198 case APPLE_32_BIT: return 256;
199 default: return 0;
200 }
201 }
202
203 // Return bits per pixel for requested depth
204 static inline int bytes_per_pixel(int depth)
205 {
206 int bpp;
207 switch (depth) {
208 case 8:
209 bpp = 1;
210 break;
211 case 15: case 16:
212 bpp = 2;
213 break;
214 case 24: case 32:
215 bpp = 4;
216 break;
217 default:
218 abort();
219 }
220 return bpp;
221 }
222
223 // Map video_mode depth ID to numerical depth value
224 static inline int depth_of_video_mode(int mode)
225 {
226 int depth;
227 switch (mode) {
228 case APPLE_1_BIT:
229 depth = 1;
230 break;
231 case APPLE_2_BIT:
232 depth = 2;
233 break;
234 case APPLE_4_BIT:
235 depth = 4;
236 break;
237 case APPLE_8_BIT:
238 depth = 8;
239 break;
240 case APPLE_16_BIT:
241 depth = 16;
242 break;
243 case APPLE_32_BIT:
244 depth = 32;
245 break;
246 default:
247 abort();
248 }
249 return depth;
250 }
251
252 // Map RGB color to pixel value (this only works in TrueColor/DirectColor visuals)
253 static inline uint32 map_rgb(uint8 red, uint8 green, uint8 blue)
254 {
255 return ((red >> rloss) << rshift) | ((green >> gloss) << gshift) | ((blue >> bloss) << bshift);
256 }
257
258
259 // Do we have a visual for handling the specified Mac depth? If so, set the
260 // global variables "xdepth", "visualInfo", "vis" and "color_class".
261 static bool find_visual_for_depth(int depth)
262 {
263 D(bug("have_visual_for_depth(%d)\n", depth_of_video_mode(depth)));
264
265 // 1-bit works always and uses default visual
266 if (depth == APPLE_1_BIT) {
267 vis = DefaultVisual(x_display, screen);
268 visualInfo.visualid = XVisualIDFromVisual(vis);
269 int num = 0;
270 XVisualInfo *vi = XGetVisualInfo(x_display, VisualIDMask, &visualInfo, &num);
271 visualInfo = vi[0];
272 XFree(vi);
273 xdepth = visualInfo.depth;
274 color_class = visualInfo.c_class;
275 D(bug(" found visual ID 0x%02x, depth %d\n", visualInfo.visualid, xdepth));
276 return true;
277 }
278
279 // Calculate minimum and maximum supported X depth
280 int min_depth = 1, max_depth = 32;
281 switch (depth) {
282 #ifdef ENABLE_VOSF
283 case APPLE_2_BIT:
284 case APPLE_4_BIT: // VOSF blitters can convert 2/4/8-bit -> 8/16/32-bit
285 case APPLE_8_BIT:
286 min_depth = 8;
287 max_depth = 32;
288 break;
289 #else
290 case APPLE_2_BIT:
291 case APPLE_4_BIT: // 2/4-bit requires VOSF blitters
292 return false;
293 case APPLE_8_BIT: // 8-bit without VOSF requires an 8-bit visual
294 min_depth = 8;
295 max_depth = 8;
296 break;
297 #endif
298 case APPLE_16_BIT: // 16-bit requires a 15/16-bit visual
299 min_depth = 15;
300 max_depth = 16;
301 break;
302 case APPLE_32_BIT: // 32-bit requires a 24/32-bit visual
303 min_depth = 24;
304 max_depth = 32;
305 break;
306 }
307 D(bug(" minimum required X depth is %d, maximum supported X depth is %d\n", min_depth, max_depth));
308
309 // Try to find a visual for one of the color depths
310 bool visual_found = false;
311 for (int i=0; i<num_depths && !visual_found; i++) {
312
313 xdepth = avail_depths[i];
314 D(bug(" trying to find visual for depth %d\n", xdepth));
315 if (xdepth < min_depth || xdepth > max_depth)
316 continue;
317
318 // Determine best color class for this depth
319 switch (xdepth) {
320 case 1: // Try StaticGray or StaticColor
321 if (XMatchVisualInfo(x_display, screen, xdepth, StaticGray, &visualInfo)
322 || XMatchVisualInfo(x_display, screen, xdepth, StaticColor, &visualInfo))
323 visual_found = true;
324 break;
325 case 8: // Need PseudoColor
326 if (XMatchVisualInfo(x_display, screen, xdepth, PseudoColor, &visualInfo))
327 visual_found = true;
328 break;
329 case 15:
330 case 16:
331 case 24:
332 case 32: // Try DirectColor first, as this will allow gamma correction
333 if (XMatchVisualInfo(x_display, screen, xdepth, DirectColor, &visualInfo)
334 || XMatchVisualInfo(x_display, screen, xdepth, TrueColor, &visualInfo))
335 visual_found = true;
336 break;
337 default:
338 D(bug(" not a supported depth\n"));
339 break;
340 }
341 }
342 if (!visual_found)
343 return false;
344
345 // Visual was found
346 vis = visualInfo.visual;
347 color_class = visualInfo.c_class;
348 D(bug(" found visual ID 0x%02x, depth %d, class ", visualInfo.visualid, xdepth));
349 #if DEBUG
350 switch (color_class) {
351 case StaticGray: D(bug("StaticGray\n")); break;
352 case GrayScale: D(bug("GrayScale\n")); break;
353 case StaticColor: D(bug("StaticColor\n")); break;
354 case PseudoColor: D(bug("PseudoColor\n")); break;
355 case TrueColor: D(bug("TrueColor\n")); break;
356 case DirectColor: D(bug("DirectColor\n")); break;
357 }
358 #endif
359 return true;
360 }
361
362
363 /*
364 * Open display (window or fullscreen)
365 */
366
367 // Set WM_DELETE_WINDOW protocol on window (preventing it from being destroyed by the WM when clicking on the "close" widget)
368 static Atom WM_DELETE_WINDOW = (Atom)0;
369 static void set_window_delete_protocol(Window w)
370 {
371 WM_DELETE_WINDOW = XInternAtom(x_display, "WM_DELETE_WINDOW", false);
372 XSetWMProtocols(x_display, w, &WM_DELETE_WINDOW, 1);
373 }
374
375 // Wait until window is mapped/unmapped
376 static void wait_mapped(Window w)
377 {
378 XEvent e;
379 do {
380 XMaskEvent(x_display, StructureNotifyMask, &e);
381 } while ((e.type != MapNotify) || (e.xmap.event != w));
382 }
383
384 static void wait_unmapped(Window w)
385 {
386 XEvent e;
387 do {
388 XMaskEvent(x_display, StructureNotifyMask, &e);
389 } while ((e.type != UnmapNotify) || (e.xmap.event != w));
390 }
391
392 // Trap SHM errors
393 static bool shm_error = false;
394 static int (*old_error_handler)(Display *, XErrorEvent *);
395
396 static int error_handler(Display *d, XErrorEvent *e)
397 {
398 if (e->error_code == BadAccess) {
399 shm_error = true;
400 return 0;
401 } else
402 return old_error_handler(d, e);
403 }
404
405 // Open window
406 static bool open_window(int width, int height)
407 {
408 int aligned_width = (width + 15) & ~15;
409 int aligned_height = (height + 15) & ~15;
410
411 // Set absolute mouse mode
412 ADBSetRelMouseMode(false);
413
414 // Create window
415 XSetWindowAttributes wattr;
416 wattr.event_mask = eventmask = win_eventmask;
417 wattr.background_pixel = (vis == DefaultVisual(x_display, screen) ? black_pixel : 0);
418 wattr.border_pixel = 0;
419 wattr.backing_store = NotUseful;
420 wattr.colormap = (depth == 1 ? DefaultColormap(x_display, screen) : cmap[0]);
421 the_win = XCreateWindow(x_display, rootwin, 0, 0, width, height, 0, xdepth,
422 InputOutput, vis, CWEventMask | CWBackPixel | CWBorderPixel | CWBackingStore | CWColormap, &wattr);
423
424 // Set window name
425 XStoreName(x_display, the_win, GetString(STR_WINDOW_TITLE));
426
427 // Set delete protocol property
428 set_window_delete_protocol(the_win);
429
430 // Make window unresizable
431 XSizeHints *hints;
432 if ((hints = XAllocSizeHints()) != NULL) {
433 hints->min_width = width;
434 hints->max_width = width;
435 hints->min_height = height;
436 hints->max_height = height;
437 hints->flags = PMinSize | PMaxSize;
438 XSetWMNormalHints(x_display, the_win, hints);
439 XFree((char *)hints);
440 }
441
442 // Show window
443 XMapWindow(x_display, the_win);
444 wait_mapped(the_win);
445
446 // 1-bit mode is big-endian; if the X server is little-endian, we can't
447 // use SHM because that doesn't allow changing the image byte order
448 bool need_msb_image = (depth == 1 && XImageByteOrder(x_display) == LSBFirst);
449
450 // Try to create and attach SHM image
451 have_shm = false;
452 if (local_X11 && !need_msb_image && XShmQueryExtension(x_display)) {
453
454 // Create SHM image ("height + 2" for safety)
455 img = XShmCreateImage(x_display, vis, depth == 1 ? 1 : xdepth, depth == 1 ? XYBitmap : ZPixmap, 0, &shminfo, width, height);
456 shminfo.shmid = shmget(IPC_PRIVATE, (aligned_height + 2) * img->bytes_per_line, IPC_CREAT | 0777);
457 D(bug(" shm image created\n"));
458 the_buffer_copy = (uint8 *)shmat(shminfo.shmid, 0, 0);
459 shminfo.shmaddr = img->data = (char *)the_buffer_copy;
460 shminfo.readOnly = False;
461
462 // Try to attach SHM image, catching errors
463 shm_error = false;
464 old_error_handler = XSetErrorHandler(error_handler);
465 XShmAttach(x_display, &shminfo);
466 XSync(x_display, false);
467 XSetErrorHandler(old_error_handler);
468 if (shm_error) {
469 shmdt(shminfo.shmaddr);
470 XDestroyImage(img);
471 shminfo.shmid = -1;
472 } else {
473 have_shm = true;
474 shmctl(shminfo.shmid, IPC_RMID, 0);
475 }
476 D(bug(" shm image attached\n"));
477 }
478
479 // Create normal X image if SHM doesn't work ("height + 2" for safety)
480 if (!have_shm) {
481 int bytes_per_row = depth == 1 ? aligned_width/8 : TrivialBytesPerRow(aligned_width, DepthModeForPixelDepth(xdepth));
482 the_buffer_copy = (uint8 *)malloc((aligned_height + 2) * bytes_per_row);
483 img = XCreateImage(x_display, vis, depth == 1 ? 1 : xdepth, depth == 1 ? XYBitmap : ZPixmap, 0, (char *)the_buffer_copy, aligned_width, aligned_height, 32, bytes_per_row);
484 D(bug(" X image created\n"));
485 }
486
487 // 1-Bit mode is big-endian
488 if (need_msb_image) {
489 img->byte_order = MSBFirst;
490 img->bitmap_bit_order = MSBFirst;
491 }
492
493 #ifdef ENABLE_VOSF
494 use_vosf = true;
495 // Allocate memory for frame buffer (SIZE is extended to page-boundary)
496 the_host_buffer = the_buffer_copy;
497 the_buffer_size = page_extend((aligned_height + 2) * img->bytes_per_line);
498 the_buffer = (uint8 *)vm_acquire(the_buffer_size);
499 the_buffer_copy = (uint8 *)malloc(the_buffer_size);
500 D(bug("the_buffer = %p, the_buffer_copy = %p, the_host_buffer = %p\n", the_buffer, the_buffer_copy, the_host_buffer));
501 #else
502 // Allocate memory for frame buffer
503 the_buffer = (uint8 *)malloc((aligned_height + 2) * img->bytes_per_line);
504 D(bug("the_buffer = %p, the_buffer_copy = %p\n", the_buffer, the_buffer_copy));
505 #endif
506 screen_base = Host2MacAddr(the_buffer);
507
508 // Create GC
509 the_gc = XCreateGC(x_display, the_win, 0, 0);
510 XSetState(x_display, the_gc, black_pixel, white_pixel, GXcopy, AllPlanes);
511
512 // Create cursor
513 if (hw_mac_cursor_accl) {
514 cursor_image = XCreateImage(x_display, vis, 1, XYPixmap, 0, (char *)MacCursor + 4, 16, 16, 16, 2);
515 cursor_image->byte_order = MSBFirst;
516 cursor_image->bitmap_bit_order = MSBFirst;
517 cursor_mask_image = XCreateImage(x_display, vis, 1, XYPixmap, 0, (char *)MacCursor + 36, 16, 16, 16, 2);
518 cursor_mask_image->byte_order = MSBFirst;
519 cursor_mask_image->bitmap_bit_order = MSBFirst;
520 cursor_map = XCreatePixmap(x_display, the_win, 16, 16, 1);
521 cursor_mask_map = XCreatePixmap(x_display, the_win, 16, 16, 1);
522 cursor_gc = XCreateGC(x_display, cursor_map, 0, 0);
523 cursor_mask_gc = XCreateGC(x_display, cursor_mask_map, 0, 0);
524 mac_cursor = XCreatePixmapCursor(x_display, cursor_map, cursor_mask_map, &black, &white, 0, 0);
525 cursor_changed = false;
526 }
527
528 // Create no_cursor
529 else {
530 mac_cursor = XCreatePixmapCursor(x_display,
531 XCreatePixmap(x_display, the_win, 1, 1, 1),
532 XCreatePixmap(x_display, the_win, 1, 1, 1),
533 &black, &white, 0, 0);
534 XDefineCursor(x_display, the_win, mac_cursor);
535 }
536
537 // Init blitting routines
538 bool native_byte_order;
539 #ifdef WORDS_BIGENDIAN
540 native_byte_order = (XImageByteOrder(x_display) == MSBFirst);
541 #else
542 native_byte_order = (XImageByteOrder(x_display) == LSBFirst);
543 #endif
544 #ifdef ENABLE_VOSF
545 Screen_blitter_init(visualFormat, native_byte_order, depth);
546 #endif
547
548 // Set bytes per row
549 XSync(x_display, false);
550 return true;
551 }
552
553 // Open DGA display (!! should use X11 VidMode extensions to set mode)
554 static bool open_dga(int width, int height)
555 {
556 #ifdef ENABLE_XF86_DGA
557 // Set relative mouse mode
558 ADBSetRelMouseMode(true);
559
560 // Create window
561 XSetWindowAttributes wattr;
562 wattr.event_mask = eventmask = dga_eventmask;
563 wattr.override_redirect = True;
564 wattr.colormap = (depth == 1 ? DefaultColormap(x_display, screen) : cmap[0]);
565 the_win = XCreateWindow(x_display, rootwin, 0, 0, width, height, 0, xdepth,
566 InputOutput, vis, CWEventMask | CWOverrideRedirect |
567 (color_class == DirectColor ? CWColormap : 0), &wattr);
568
569 // Show window
570 XMapRaised(x_display, the_win);
571 wait_mapped(the_win);
572
573 #ifdef ENABLE_XF86_VIDMODE
574 // Switch to best mode
575 if (has_vidmode) {
576 int best = 0;
577 for (int i=1; i<num_x_video_modes; i++) {
578 if (x_video_modes[i]->hdisplay >= width && x_video_modes[i]->vdisplay >= height &&
579 x_video_modes[i]->hdisplay <= x_video_modes[best]->hdisplay && x_video_modes[i]->vdisplay <= x_video_modes[best]->vdisplay) {
580 best = i;
581 }
582 }
583 XF86VidModeSwitchToMode(x_display, screen, x_video_modes[best]);
584 XF86VidModeSetViewPort(x_display, screen, 0, 0);
585 }
586 #endif
587
588 // Establish direct screen connection
589 XMoveResizeWindow(x_display, the_win, 0, 0, width, height);
590 XWarpPointer(x_display, None, rootwin, 0, 0, 0, 0, 0, 0);
591 XGrabKeyboard(x_display, rootwin, True, GrabModeAsync, GrabModeAsync, CurrentTime);
592 XGrabPointer(x_display, rootwin, True, PointerMotionMask | ButtonPressMask | ButtonReleaseMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
593
594 int v_width, v_bank, v_size;
595 XF86DGAGetVideo(x_display, screen, (char **)&the_buffer, &v_width, &v_bank, &v_size);
596 XF86DGADirectVideo(x_display, screen, XF86DGADirectGraphics | XF86DGADirectKeyb | XF86DGADirectMouse);
597 XF86DGASetViewPort(x_display, screen, 0, 0);
598 XF86DGASetVidPage(x_display, screen, 0);
599
600 // Set colormap
601 if (!IsDirectMode(get_current_mode())) {
602 XSetWindowColormap(x_display, the_win, cmap[current_dga_cmap = 0]);
603 XF86DGAInstallColormap(x_display, screen, cmap[current_dga_cmap]);
604 }
605 XSync(x_display, false);
606
607 // Init blitting routines
608 int bytes_per_row = TrivialBytesPerRow((v_width + 7) & ~7, DepthModeForPixelDepth(depth));
609 #if ENABLE_VOSF
610 bool native_byte_order;
611 #ifdef WORDS_BIGENDIAN
612 native_byte_order = (XImageByteOrder(x_display) == MSBFirst);
613 #else
614 native_byte_order = (XImageByteOrder(x_display) == LSBFirst);
615 #endif
616 #if REAL_ADDRESSING || DIRECT_ADDRESSING
617 // Screen_blitter_init() returns TRUE if VOSF is mandatory
618 // i.e. the framebuffer update function is not Blit_Copy_Raw
619 use_vosf = Screen_blitter_init(visualFormat, native_byte_order, depth);
620
621 if (use_vosf) {
622 // Allocate memory for frame buffer (SIZE is extended to page-boundary)
623 the_host_buffer = the_buffer;
624 the_buffer_size = page_extend((height + 2) * bytes_per_row);
625 the_buffer_copy = (uint8 *)malloc(the_buffer_size);
626 the_buffer = (uint8 *)vm_acquire(the_buffer_size);
627 D(bug("the_buffer = %p, the_buffer_copy = %p, the_host_buffer = %p\n", the_buffer, the_buffer_copy, the_host_buffer));
628 }
629 #else
630 use_vosf = false;
631 #endif
632 #endif
633
634 // Set frame buffer base
635 D(bug("the_buffer = %p, use_vosf = %d\n", the_buffer, use_vosf));
636 screen_base = Host2MacAddr(the_buffer);
637 VModes[cur_mode].viRowBytes = bytes_per_row;
638 return true;
639 #else
640 ErrorAlert("SheepShaver has been compiled with DGA support disabled.");
641 return false;
642 #endif
643 }
644
645 static bool open_display(void)
646 {
647 D(bug("open_display()\n"));
648 const VideoInfo &mode = VModes[cur_mode];
649
650 // Find best available X visual
651 if (!find_visual_for_depth(mode.viAppleMode)) {
652 ErrorAlert(GetString(STR_NO_XVISUAL_ERR));
653 return false;
654 }
655
656 // Build up visualFormat structure
657 visualFormat.depth = visualInfo.depth;
658 visualFormat.Rmask = visualInfo.red_mask;
659 visualFormat.Gmask = visualInfo.green_mask;
660 visualFormat.Bmask = visualInfo.blue_mask;
661
662 // Create color maps
663 if (color_class == PseudoColor || color_class == DirectColor) {
664 cmap[0] = XCreateColormap(x_display, rootwin, vis, AllocAll);
665 cmap[1] = XCreateColormap(x_display, rootwin, vis, AllocAll);
666 } else {
667 cmap[0] = XCreateColormap(x_display, rootwin, vis, AllocNone);
668 cmap[1] = XCreateColormap(x_display, rootwin, vis, AllocNone);
669 }
670
671 // Find pixel format of direct modes
672 if (color_class == DirectColor || color_class == TrueColor) {
673 rshift = gshift = bshift = 0;
674 rloss = gloss = bloss = 8;
675 uint32 mask;
676 for (mask=vis->red_mask; !(mask&1); mask>>=1)
677 ++rshift;
678 for (; mask&1; mask>>=1)
679 --rloss;
680 for (mask=vis->green_mask; !(mask&1); mask>>=1)
681 ++gshift;
682 for (; mask&1; mask>>=1)
683 --gloss;
684 for (mask=vis->blue_mask; !(mask&1); mask>>=1)
685 ++bshift;
686 for (; mask&1; mask>>=1)
687 --bloss;
688 }
689
690 // Preset palette pixel values for CLUT or gamma table
691 if (color_class == DirectColor) {
692 int num = vis->map_entries;
693 for (int i=0; i<num; i++) {
694 int c = (i * 256) / num;
695 x_palette[i].pixel = map_rgb(c, c, c);
696 x_palette[i].flags = DoRed | DoGreen | DoBlue;
697 }
698 } else if (color_class == PseudoColor) {
699 for (int i=0; i<256; i++) {
700 x_palette[i].pixel = i;
701 x_palette[i].flags = DoRed | DoGreen | DoBlue;
702 }
703 }
704
705 // Load gray ramp to color map
706 int num = (color_class == DirectColor ? vis->map_entries : 256);
707 for (int i=0; i<num; i++) {
708 int c = (i * 256) / num;
709 x_palette[i].red = c * 0x0101;
710 x_palette[i].green = c * 0x0101;
711 x_palette[i].blue = c * 0x0101;
712 }
713 if (color_class == PseudoColor || color_class == DirectColor) {
714 XStoreColors(x_display, cmap[0], x_palette, num);
715 XStoreColors(x_display, cmap[1], x_palette, num);
716 }
717
718 #ifdef ENABLE_VOSF
719 // Load gray ramp to 8->16/32 expand map
720 if (!IsDirectMode(get_current_mode()) && xdepth > 8)
721 for (int i=0; i<256; i++)
722 ExpandMap[i] = map_rgb(i, i, i);
723 #endif
724
725 // Create display of requested type
726 display_type = mode.viType;
727 depth = depth_of_video_mode(mode.viAppleMode);
728
729 bool display_open = false;
730 if (display_type == DIS_SCREEN)
731 display_open = open_dga(VModes[cur_mode].viXsize, VModes[cur_mode].viYsize);
732 else if (display_type == DIS_WINDOW)
733 display_open = open_window(VModes[cur_mode].viXsize, VModes[cur_mode].viYsize);
734
735 #ifdef ENABLE_VOSF
736 if (use_vosf) {
737 // Initialize the VOSF system
738 if (!video_vosf_init()) {
739 ErrorAlert(GetString(STR_VOSF_INIT_ERR));
740 return false;
741 }
742 }
743 #endif
744
745 return display_open;
746 }
747
748
749 /*
750 * Close display
751 */
752
753 // Close window
754 static void close_window(void)
755 {
756 if (have_shm) {
757 XShmDetach(x_display, &shminfo);
758 #ifdef ENABLE_VOSF
759 the_host_buffer = NULL; // don't free() in driver_base dtor
760 #else
761 the_buffer_copy = NULL; // don't free() in driver_base dtor
762 #endif
763 }
764 if (img) {
765 if (!have_shm)
766 img->data = NULL;
767 XDestroyImage(img);
768 }
769 if (have_shm) {
770 shmdt(shminfo.shmaddr);
771 shmctl(shminfo.shmid, IPC_RMID, 0);
772 }
773 if (the_gc)
774 XFreeGC(x_display, the_gc);
775
776 XFlush(x_display);
777 XSync(x_display, false);
778 }
779
780 // Close DGA mode
781 static void close_dga(void)
782 {
783 #ifdef ENABLE_XF86_DGA
784 XF86DGADirectVideo(x_display, screen, 0);
785 XUngrabPointer(x_display, CurrentTime);
786 XUngrabKeyboard(x_display, CurrentTime);
787 #endif
788
789 #ifdef ENABLE_XF86_VIDMODE
790 if (has_vidmode)
791 XF86VidModeSwitchToMode(x_display, screen, x_video_modes[0]);
792 #endif
793
794 if (!use_vosf) {
795 // don't free() the screen buffer in driver_base dtor
796 the_buffer = NULL;
797 }
798 #ifdef ENABLE_VOSF
799 else {
800 // don't free() the screen buffer in driver_base dtor
801 the_host_buffer = NULL;
802 }
803 #endif
804 }
805
806 static void close_display(void)
807 {
808 if (display_type == DIS_SCREEN)
809 close_dga();
810 else if (display_type == DIS_WINDOW)
811 close_window();
812
813 // Close window
814 if (the_win) {
815 XUnmapWindow(x_display, the_win);
816 wait_unmapped(the_win);
817 XDestroyWindow(x_display, the_win);
818 }
819
820 // Free colormaps
821 if (cmap[0]) {
822 XFreeColormap(x_display, cmap[0]);
823 cmap[0] = 0;
824 }
825 if (cmap[1]) {
826 XFreeColormap(x_display, cmap[1]);
827 cmap[1] = 0;
828 }
829
830 #ifdef ENABLE_VOSF
831 if (use_vosf) {
832 // Deinitialize VOSF
833 video_vosf_exit();
834 }
835 #endif
836
837 // Free frame buffer(s)
838 if (!use_vosf) {
839 if (the_buffer_copy) {
840 free(the_buffer_copy);
841 the_buffer_copy = NULL;
842 }
843 }
844 #ifdef ENABLE_VOSF
845 else {
846 // the_buffer shall always be mapped through vm_acquire() so that we can vm_protect() it at will
847 if (the_buffer != VM_MAP_FAILED) {
848 D(bug(" releasing the_buffer at %p (%d bytes)\n", the_buffer, the_buffer_size));
849 vm_release(the_buffer, the_buffer_size);
850 the_buffer = NULL;
851 }
852 if (the_host_buffer) {
853 D(bug(" freeing the_host_buffer at %p\n", the_host_buffer));
854 free(the_host_buffer);
855 the_host_buffer = NULL;
856 }
857 if (the_buffer_copy) {
858 D(bug(" freeing the_buffer_copy at %p\n", the_buffer_copy));
859 free(the_buffer_copy);
860 the_buffer_copy = NULL;
861 }
862 }
863 #endif
864 }
865
866
867 /*
868 * Initialization
869 */
870
871 // Init keycode translation table
872 static void keycode_init(void)
873 {
874 bool use_kc = PrefsFindBool("keycodes");
875 if (use_kc) {
876
877 // Get keycode file path from preferences
878 const char *kc_path = PrefsFindString("keycodefile");
879
880 // Open keycode table
881 FILE *f = fopen(kc_path ? kc_path : KEYCODE_FILE_NAME, "r");
882 if (f == NULL) {
883 char str[256];
884 sprintf(str, GetString(STR_KEYCODE_FILE_WARN), kc_path ? kc_path : KEYCODE_FILE_NAME, strerror(errno));
885 WarningAlert(str);
886 return;
887 }
888
889 // Default translation table
890 for (int i=0; i<256; i++)
891 keycode_table[i] = -1;
892
893 // Search for server vendor string, then read keycodes
894 const char *vendor = ServerVendor(x_display);
895 // Force use of MacX mappings on MacOS X with Apple's X server
896 int dummy;
897 if (XQueryExtension(x_display, "Apple-DRI", &dummy, &dummy, &dummy))
898 vendor = "MacX";
899 bool vendor_found = false;
900 char line[256];
901 while (fgets(line, 255, f)) {
902 // Read line
903 int len = strlen(line);
904 if (len == 0)
905 continue;
906 line[len-1] = 0;
907
908 // Comments begin with "#" or ";"
909 if (line[0] == '#' || line[0] == ';' || line[0] == 0)
910 continue;
911
912 if (vendor_found) {
913 // Read keycode
914 int x_code, mac_code;
915 if (sscanf(line, "%d %d", &x_code, &mac_code) == 2)
916 keycode_table[x_code & 0xff] = mac_code;
917 else
918 break;
919 } else {
920 // Search for vendor string
921 if (strstr(vendor, line) == vendor)
922 vendor_found = true;
923 }
924 }
925
926 // Keycode file completely read
927 fclose(f);
928 use_keycodes = vendor_found;
929
930 // Vendor not found? Then display warning
931 if (!vendor_found) {
932 char str[256];
933 sprintf(str, GetString(STR_KEYCODE_VENDOR_WARN), vendor, kc_path ? kc_path : KEYCODE_FILE_NAME);
934 WarningAlert(str);
935 return;
936 }
937 }
938 }
939
940 // Find Apple mode matching best specified dimensions
941 static int find_apple_resolution(int xsize, int ysize)
942 {
943 int apple_id;
944 if (xsize < 800)
945 apple_id = APPLE_640x480;
946 else if (xsize < 1024)
947 apple_id = APPLE_800x600;
948 else if (xsize < 1152)
949 apple_id = APPLE_1024x768;
950 else if (xsize < 1280) {
951 if (ysize < 900)
952 apple_id = APPLE_1152x768;
953 else
954 apple_id = APPLE_1152x900;
955 }
956 else if (xsize < 1600)
957 apple_id = APPLE_1280x1024;
958 else
959 apple_id = APPLE_1600x1200;
960 return apple_id;
961 }
962
963 // Find mode in list of supported modes
964 static int find_mode(int apple_mode, int apple_id, int type)
965 {
966 for (VideoInfo *p = VModes; p->viType != DIS_INVALID; p++) {
967 if (p->viType == type && p->viAppleID == apple_id && p->viAppleMode == apple_mode)
968 return p - VModes;
969 }
970 return -1;
971 }
972
973 // Add mode to list of supported modes
974 static void add_mode(VideoInfo *&p, uint32 allow, uint32 test, int apple_mode, int apple_id, int type)
975 {
976 if (allow & test) {
977 p->viType = type;
978 switch (apple_id) {
979 case APPLE_W_640x480:
980 case APPLE_640x480:
981 p->viXsize = 640;
982 p->viYsize = 480;
983 break;
984 case APPLE_W_800x600:
985 case APPLE_800x600:
986 p->viXsize = 800;
987 p->viYsize = 600;
988 break;
989 case APPLE_1024x768:
990 p->viXsize = 1024;
991 p->viYsize = 768;
992 break;
993 case APPLE_1152x768:
994 p->viXsize = 1152;
995 p->viYsize = 768;
996 break;
997 case APPLE_1152x900:
998 p->viXsize = 1152;
999 p->viYsize = 900;
1000 break;
1001 case APPLE_1280x1024:
1002 p->viXsize = 1280;
1003 p->viYsize = 1024;
1004 break;
1005 case APPLE_1600x1200:
1006 p->viXsize = 1600;
1007 p->viYsize = 1200;
1008 break;
1009 }
1010 p->viRowBytes = TrivialBytesPerRow(p->viXsize, apple_mode);
1011 p->viAppleMode = apple_mode;
1012 p->viAppleID = apple_id;
1013 p++;
1014 }
1015 }
1016
1017 // Add standard list of windowed modes for given color depth
1018 static void add_window_modes(VideoInfo *&p, int window_modes, int mode)
1019 {
1020 add_mode(p, window_modes, 1, mode, APPLE_W_640x480, DIS_WINDOW);
1021 add_mode(p, window_modes, 2, mode, APPLE_W_800x600, DIS_WINDOW);
1022 }
1023
1024 static bool has_mode(int x, int y)
1025 {
1026 #ifdef ENABLE_XF86_VIDMODE
1027 for (int i=0; i<num_x_video_modes; i++)
1028 if (x_video_modes[i]->hdisplay >= x && x_video_modes[i]->vdisplay >= y)
1029 return true;
1030 return false;
1031 #else
1032 return DisplayWidth(x_display, screen) >= x && DisplayHeight(x_display, screen) >= y;
1033 #endif
1034 }
1035
1036 bool VideoInit(void)
1037 {
1038 #ifdef ENABLE_VOSF
1039 // Zero the mainBuffer structure
1040 mainBuffer.dirtyPages = NULL;
1041 mainBuffer.pageInfo = NULL;
1042 #endif
1043
1044 // Check if X server runs on local machine
1045 local_X11 = (strncmp(XDisplayName(x_display_name), ":", 1) == 0)
1046 || (strncmp(XDisplayName(x_display_name), "unix:", 5) == 0);
1047
1048 // Init keycode translation
1049 keycode_init();
1050
1051 // Read frame skip prefs
1052 frame_skip = PrefsFindInt32("frameskip");
1053 if (frame_skip == 0)
1054 frame_skip = 1;
1055
1056 // Read mouse wheel prefs
1057 mouse_wheel_mode = PrefsFindInt32("mousewheelmode");
1058 mouse_wheel_lines = PrefsFindInt32("mousewheellines");
1059
1060 // Init variables
1061 private_data = NULL;
1062 video_activated = true;
1063
1064 // Find screen and root window
1065 screen = XDefaultScreen(x_display);
1066 rootwin = XRootWindow(x_display, screen);
1067
1068 // Get sorted list of available depths
1069 avail_depths = XListDepths(x_display, screen, &num_depths);
1070 if (avail_depths == NULL) {
1071 ErrorAlert(GetString(STR_UNSUPP_DEPTH_ERR));
1072 return false;
1073 }
1074 sort(avail_depths, avail_depths + num_depths);
1075
1076 // Get screen depth
1077 xdepth = DefaultDepth(x_display, screen);
1078
1079 #ifdef ENABLE_XF86_DGA
1080 // DGA available?
1081 int event_base, error_base;
1082 if (local_X11 && XF86DGAQueryExtension(x_display, &event_base, &error_base)) {
1083 int dga_flags = 0;
1084 XF86DGAQueryDirectVideo(x_display, screen, &dga_flags);
1085 has_dga = dga_flags & XF86DGADirectPresent;
1086 } else
1087 has_dga = false;
1088 #endif
1089
1090 #ifdef ENABLE_XF86_VIDMODE
1091 // VidMode available?
1092 int vm_event_base, vm_error_base;
1093 has_vidmode = XF86VidModeQueryExtension(x_display, &vm_event_base, &vm_error_base);
1094 if (has_vidmode)
1095 XF86VidModeGetAllModeLines(x_display, screen, &num_x_video_modes, &x_video_modes);
1096 #endif
1097
1098 // Find black and white colors
1099 XParseColor(x_display, DefaultColormap(x_display, screen), "rgb:00/00/00", &black);
1100 XAllocColor(x_display, DefaultColormap(x_display, screen), &black);
1101 XParseColor(x_display, DefaultColormap(x_display, screen), "rgb:ff/ff/ff", &white);
1102 XAllocColor(x_display, DefaultColormap(x_display, screen), &white);
1103 black_pixel = BlackPixel(x_display, screen);
1104 white_pixel = WhitePixel(x_display, screen);
1105
1106 // Mac screen depth follows X depth (for now)
1107 int default_mode = APPLE_8_BIT;
1108 switch (DefaultDepth(x_display, screen)) {
1109 case 1:
1110 default_mode = APPLE_1_BIT;
1111 break;
1112 case 8:
1113 default_mode = APPLE_8_BIT;
1114 break;
1115 case 15: case 16:
1116 default_mode = APPLE_16_BIT;
1117 break;
1118 case 24: case 32:
1119 default_mode = APPLE_32_BIT;
1120 break;
1121 }
1122
1123 // Construct video mode table
1124 uint32 window_modes = PrefsFindInt32("windowmodes");
1125 uint32 screen_modes = PrefsFindInt32("screenmodes");
1126 if (!has_dga)
1127 screen_modes = 0;
1128 if (window_modes == 0 && screen_modes == 0)
1129 window_modes |= 3; // Allow at least 640x480 and 800x600 window modes
1130
1131 VideoInfo *p = VModes;
1132 for (unsigned int d = APPLE_1_BIT; d <= APPLE_32_BIT; d++)
1133 if (find_visual_for_depth(d))
1134 add_window_modes(p, window_modes, d);
1135
1136 if (has_vidmode) {
1137 if (has_mode(640, 480))
1138 add_mode(p, screen_modes, 1, default_mode, APPLE_640x480, DIS_SCREEN);
1139 if (has_mode(800, 600))
1140 add_mode(p, screen_modes, 2, default_mode, APPLE_800x600, DIS_SCREEN);
1141 if (has_mode(1024, 768))
1142 add_mode(p, screen_modes, 4, default_mode, APPLE_1024x768, DIS_SCREEN);
1143 if (has_mode(1152, 768))
1144 add_mode(p, screen_modes, 64, default_mode, APPLE_1152x768, DIS_SCREEN);
1145 if (has_mode(1152, 900))
1146 add_mode(p, screen_modes, 8, default_mode, APPLE_1152x900, DIS_SCREEN);
1147 if (has_mode(1280, 1024))
1148 add_mode(p, screen_modes, 16, default_mode, APPLE_1280x1024, DIS_SCREEN);
1149 if (has_mode(1600, 1200))
1150 add_mode(p, screen_modes, 32, default_mode, APPLE_1600x1200, DIS_SCREEN);
1151 } else if (screen_modes) {
1152 int xsize = DisplayWidth(x_display, screen);
1153 int ysize = DisplayHeight(x_display, screen);
1154 int apple_id = find_apple_resolution(xsize, ysize);
1155 p->viType = DIS_SCREEN;
1156 p->viRowBytes = 0;
1157 p->viXsize = xsize;
1158 p->viYsize = ysize;
1159 p->viAppleMode = default_mode;
1160 p->viAppleID = apple_id;
1161 p++;
1162 }
1163 p->viType = DIS_INVALID; // End marker
1164 p->viRowBytes = 0;
1165 p->viXsize = p->viYsize = 0;
1166 p->viAppleMode = 0;
1167 p->viAppleID = 0;
1168
1169 // Find default mode (window 640x480)
1170 cur_mode = -1;
1171 if (has_dga && screen_modes) {
1172 int screen_width = DisplayWidth(x_display, screen);
1173 int screen_height = DisplayHeight(x_display, screen);
1174 int apple_id = find_apple_resolution(screen_width, screen_height);
1175 if (apple_id != -1)
1176 cur_mode = find_mode(default_mode, apple_id, DIS_SCREEN);
1177 }
1178 if (cur_mode == -1) {
1179 // pick up first windowed mode available
1180 for (VideoInfo *p = VModes; p->viType != DIS_INVALID; p++) {
1181 if (p->viType == DIS_WINDOW && p->viAppleMode == default_mode) {
1182 cur_mode = p - VModes;
1183 break;
1184 }
1185 }
1186 }
1187 assert(cur_mode != -1);
1188
1189 #if DEBUG
1190 D(bug("Available video modes:\n"));
1191 for (p = VModes; p->viType != DIS_INVALID; p++) {
1192 int bits = depth_of_video_mode(p->viAppleMode);
1193 D(bug(" %dx%d (ID %02x), %d colors\n", p->viXsize, p->viYsize, p->viAppleID, 1 << bits));
1194 }
1195 #endif
1196
1197 // Open window/screen
1198 if (!open_display())
1199 return false;
1200
1201 #if 0
1202 // Ignore errors from now on
1203 XSetErrorHandler(ignore_errors);
1204 #endif
1205
1206 // Start periodic thread
1207 XSync(x_display, false);
1208 Set_pthread_attr(&redraw_thread_attr, 0);
1209 redraw_thread_cancel = false;
1210 redraw_thread_active = (pthread_create(&redraw_thread, &redraw_thread_attr, redraw_func, NULL) == 0);
1211 D(bug("Redraw thread installed (%ld)\n", redraw_thread));
1212 return true;
1213 }
1214
1215
1216 /*
1217 * Deinitialization
1218 */
1219
1220 void VideoExit(void)
1221 {
1222 // Stop redraw thread
1223 if (redraw_thread_active) {
1224 redraw_thread_cancel = true;
1225 pthread_cancel(redraw_thread);
1226 pthread_join(redraw_thread, NULL);
1227 redraw_thread_active = false;
1228 }
1229
1230 #ifdef ENABLE_VOSF
1231 if (use_vosf) {
1232 // Deinitialize VOSF
1233 video_vosf_exit();
1234 }
1235 #endif
1236
1237 // Close window and server connection
1238 if (x_display != NULL) {
1239 XSync(x_display, false);
1240 close_display();
1241 XFlush(x_display);
1242 XSync(x_display, false);
1243 }
1244 }
1245
1246
1247 /*
1248 * Suspend/resume emulator
1249 */
1250
1251 extern void PauseEmulator(void);
1252 extern void ResumeEmulator(void);
1253
1254 static void suspend_emul(void)
1255 {
1256 if (display_type == DIS_SCREEN) {
1257 // Release ctrl key
1258 ADBKeyUp(0x36);
1259 ctrl_down = false;
1260
1261 // Pause MacOS thread
1262 PauseEmulator();
1263 emul_suspended = true;
1264
1265 // Save frame buffer
1266 fb_save = malloc(VModes[cur_mode].viYsize * VModes[cur_mode].viRowBytes);
1267 if (fb_save)
1268 Mac2Host_memcpy(fb_save, screen_base, VModes[cur_mode].viYsize * VModes[cur_mode].viRowBytes);
1269
1270 // Close full screen display
1271 #ifdef ENABLE_XF86_DGA
1272 XF86DGADirectVideo(x_display, screen, 0);
1273 XUngrabPointer(x_display, CurrentTime);
1274 XUngrabKeyboard(x_display, CurrentTime);
1275 #endif
1276 XSync(x_display, false);
1277
1278 // Open "suspend" window
1279 XSetWindowAttributes wattr;
1280 wattr.event_mask = KeyPressMask;
1281 wattr.background_pixel = black_pixel;
1282 wattr.border_pixel = black_pixel;
1283 wattr.backing_store = Always;
1284 wattr.backing_planes = xdepth;
1285 wattr.colormap = DefaultColormap(x_display, screen);
1286 XSync(x_display, false);
1287 suspend_win = XCreateWindow(x_display, rootwin, 0, 0, 512, 1, 0, xdepth,
1288 InputOutput, vis, CWEventMask | CWBackPixel | CWBorderPixel |
1289 CWBackingStore | CWBackingPlanes | (xdepth == 8 ? CWColormap : 0), &wattr);
1290 XSync(x_display, false);
1291 XStoreName(x_display, suspend_win, GetString(STR_SUSPEND_WINDOW_TITLE));
1292 XMapRaised(x_display, suspend_win);
1293 XSync(x_display, false);
1294 }
1295 }
1296
1297 static void resume_emul(void)
1298 {
1299 // Close "suspend" window
1300 XDestroyWindow(x_display, suspend_win);
1301 XSync(x_display, false);
1302
1303 // Reopen full screen display
1304 XGrabKeyboard(x_display, rootwin, 1, GrabModeAsync, GrabModeAsync, CurrentTime);
1305 XGrabPointer(x_display, rootwin, 1, PointerMotionMask | ButtonPressMask | ButtonReleaseMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
1306 #ifdef ENABLE_XF86_DGA
1307 XF86DGADirectVideo(x_display, screen, XF86DGADirectGraphics | XF86DGADirectKeyb | XF86DGADirectMouse);
1308 XF86DGASetViewPort(x_display, screen, 0, 0);
1309 #endif
1310 XSync(x_display, false);
1311
1312 // the_buffer already contains the data to restore. i.e. since a temporary
1313 // frame buffer is used when VOSF is actually used, fb_save is therefore
1314 // not necessary.
1315 #ifdef ENABLE_VOSF
1316 if (use_vosf) {
1317 LOCK_VOSF;
1318 PFLAG_SET_ALL;
1319 UNLOCK_VOSF;
1320 memset(the_buffer_copy, 0, VModes[cur_mode].viRowBytes * VModes[cur_mode].viYsize);
1321 }
1322 #endif
1323
1324 // Restore frame buffer
1325 if (fb_save) {
1326 #ifdef ENABLE_VOSF
1327 // Don't copy fb_save to the temporary frame buffer in VOSF mode
1328 if (!use_vosf)
1329 #endif
1330 Host2Mac_memcpy(screen_base, fb_save, VModes[cur_mode].viYsize * VModes[cur_mode].viRowBytes);
1331 free(fb_save);
1332 fb_save = NULL;
1333 }
1334 if (depth == 8)
1335 palette_changed = true;
1336
1337 // Resume MacOS thread
1338 emul_suspended = false;
1339 ResumeEmulator();
1340 }
1341
1342
1343 /*
1344 * Close screen in full-screen mode
1345 */
1346
1347 void VideoQuitFullScreen(void)
1348 {
1349 D(bug("VideoQuitFullScreen()\n"));
1350 if (display_type == DIS_SCREEN) {
1351 quit_full_screen = true;
1352 while (!quit_full_screen_ack) ;
1353 }
1354 }
1355
1356
1357 /*
1358 * X11 event handling
1359 */
1360
1361 // Translate key event to Mac keycode
1362 static int kc_decode(KeySym ks)
1363 {
1364 switch (ks) {
1365 case XK_A: case XK_a: return 0x00;
1366 case XK_B: case XK_b: return 0x0b;
1367 case XK_C: case XK_c: return 0x08;
1368 case XK_D: case XK_d: return 0x02;
1369 case XK_E: case XK_e: return 0x0e;
1370 case XK_F: case XK_f: return 0x03;
1371 case XK_G: case XK_g: return 0x05;
1372 case XK_H: case XK_h: return 0x04;
1373 case XK_I: case XK_i: return 0x22;
1374 case XK_J: case XK_j: return 0x26;
1375 case XK_K: case XK_k: return 0x28;
1376 case XK_L: case XK_l: return 0x25;
1377 case XK_M: case XK_m: return 0x2e;
1378 case XK_N: case XK_n: return 0x2d;
1379 case XK_O: case XK_o: return 0x1f;
1380 case XK_P: case XK_p: return 0x23;
1381 case XK_Q: case XK_q: return 0x0c;
1382 case XK_R: case XK_r: return 0x0f;
1383 case XK_S: case XK_s: return 0x01;
1384 case XK_T: case XK_t: return 0x11;
1385 case XK_U: case XK_u: return 0x20;
1386 case XK_V: case XK_v: return 0x09;
1387 case XK_W: case XK_w: return 0x0d;
1388 case XK_X: case XK_x: return 0x07;
1389 case XK_Y: case XK_y: return 0x10;
1390 case XK_Z: case XK_z: return 0x06;
1391
1392 case XK_1: case XK_exclam: return 0x12;
1393 case XK_2: case XK_at: return 0x13;
1394 case XK_3: case XK_numbersign: return 0x14;
1395 case XK_4: case XK_dollar: return 0x15;
1396 case XK_5: case XK_percent: return 0x17;
1397 case XK_6: return 0x16;
1398 case XK_7: return 0x1a;
1399 case XK_8: return 0x1c;
1400 case XK_9: return 0x19;
1401 case XK_0: return 0x1d;
1402
1403 case XK_grave: case XK_asciitilde: return 0x0a;
1404 case XK_minus: case XK_underscore: return 0x1b;
1405 case XK_equal: case XK_plus: return 0x18;
1406 case XK_bracketleft: case XK_braceleft: return 0x21;
1407 case XK_bracketright: case XK_braceright: return 0x1e;
1408 case XK_backslash: case XK_bar: return 0x2a;
1409 case XK_semicolon: case XK_colon: return 0x29;
1410 case XK_apostrophe: case XK_quotedbl: return 0x27;
1411 case XK_comma: case XK_less: return 0x2b;
1412 case XK_period: case XK_greater: return 0x2f;
1413 case XK_slash: case XK_question: return 0x2c;
1414
1415 case XK_Tab: if (ctrl_down) {suspend_emul(); return -1;} else return 0x30;
1416 case XK_Return: return 0x24;
1417 case XK_space: return 0x31;
1418 case XK_BackSpace: return 0x33;
1419
1420 case XK_Delete: return 0x75;
1421 case XK_Insert: return 0x72;
1422 case XK_Home: case XK_Help: return 0x73;
1423 case XK_End: return 0x77;
1424 #ifdef __hpux
1425 case XK_Prior: return 0x74;
1426 case XK_Next: return 0x79;
1427 #else
1428 case XK_Page_Up: return 0x74;
1429 case XK_Page_Down: return 0x79;
1430 #endif
1431
1432 case XK_Control_L: return 0x36;
1433 case XK_Control_R: return 0x36;
1434 case XK_Shift_L: return 0x38;
1435 case XK_Shift_R: return 0x38;
1436 case XK_Alt_L: return 0x37;
1437 case XK_Alt_R: return 0x37;
1438 case XK_Meta_L: return 0x3a;
1439 case XK_Meta_R: return 0x3a;
1440 case XK_Menu: return 0x32;
1441 case XK_Caps_Lock: return 0x39;
1442 case XK_Num_Lock: return 0x47;
1443
1444 case XK_Up: return 0x3e;
1445 case XK_Down: return 0x3d;
1446 case XK_Left: return 0x3b;
1447 case XK_Right: return 0x3c;
1448
1449 case XK_Escape: if (ctrl_down) {quit_full_screen = true; emerg_quit = true; return -1;} else return 0x35;
1450
1451 case XK_F1: if (ctrl_down) {SysMountFirstFloppy(); return -1;} else return 0x7a;
1452 case XK_F2: return 0x78;
1453 case XK_F3: return 0x63;
1454 case XK_F4: return 0x76;
1455 case XK_F5: return 0x60;
1456 case XK_F6: return 0x61;
1457 case XK_F7: return 0x62;
1458 case XK_F8: return 0x64;
1459 case XK_F9: return 0x65;
1460 case XK_F10: return 0x6d;
1461 case XK_F11: return 0x67;
1462 case XK_F12: return 0x6f;
1463
1464 case XK_Print: return 0x69;
1465 case XK_Scroll_Lock: return 0x6b;
1466 case XK_Pause: return 0x71;
1467
1468 #if defined(XK_KP_Prior) && defined(XK_KP_Left) && defined(XK_KP_Insert) && defined (XK_KP_End)
1469 case XK_KP_0: case XK_KP_Insert: return 0x52;
1470 case XK_KP_1: case XK_KP_End: return 0x53;
1471 case XK_KP_2: case XK_KP_Down: return 0x54;
1472 case XK_KP_3: case XK_KP_Next: return 0x55;
1473 case XK_KP_4: case XK_KP_Left: return 0x56;
1474 case XK_KP_5: case XK_KP_Begin: return 0x57;
1475 case XK_KP_6: case XK_KP_Right: return 0x58;
1476 case XK_KP_7: case XK_KP_Home: return 0x59;
1477 case XK_KP_8: case XK_KP_Up: return 0x5b;
1478 case XK_KP_9: case XK_KP_Prior: return 0x5c;
1479 case XK_KP_Decimal: case XK_KP_Delete: return 0x41;
1480 #else
1481 case XK_KP_0: return 0x52;
1482 case XK_KP_1: return 0x53;
1483 case XK_KP_2: return 0x54;
1484 case XK_KP_3: return 0x55;
1485 case XK_KP_4: return 0x56;
1486 case XK_KP_5: return 0x57;
1487 case XK_KP_6: return 0x58;
1488 case XK_KP_7: return 0x59;
1489 case XK_KP_8: return 0x5b;
1490 case XK_KP_9: return 0x5c;
1491 case XK_KP_Decimal: return 0x41;
1492 #endif
1493 case XK_KP_Add: return 0x45;
1494 case XK_KP_Subtract: return 0x4e;
1495 case XK_KP_Multiply: return 0x43;
1496 case XK_KP_Divide: return 0x4b;
1497 case XK_KP_Enter: return 0x4c;
1498 case XK_KP_Equal: return 0x51;
1499 }
1500 return -1;
1501 }
1502
1503 static int event2keycode(XKeyEvent &ev, bool key_down)
1504 {
1505 KeySym ks;
1506 int i = 0;
1507
1508 do {
1509 ks = XLookupKeysym(&ev, i++);
1510 int as = kc_decode(ks);
1511 if (as >= 0)
1512 return as;
1513 if (as == -2)
1514 return as;
1515 } while (ks != NoSymbol);
1516
1517 return -1;
1518 }
1519
1520 static void handle_events(void)
1521 {
1522 // Handle events
1523 for (;;) {
1524 XEvent event;
1525
1526 XDisplayLock();
1527 if (!XCheckMaskEvent(x_display, eventmask, &event)) {
1528 // Handle clipboard events
1529 if (XCheckTypedEvent(x_display, SelectionRequest, &event))
1530 ClipboardSelectionRequest(&event.xselectionrequest);
1531 else if (XCheckTypedEvent(x_display, SelectionClear, &event))
1532 ClipboardSelectionClear(&event.xselectionclear);
1533
1534 // Window "close" widget clicked
1535 else if (XCheckTypedEvent(x_display, ClientMessage, &event)) {
1536 if (event.xclient.format == 32 && event.xclient.data.l[0] == WM_DELETE_WINDOW) {
1537 ADBKeyDown(0x7f); // Power key
1538 ADBKeyUp(0x7f);
1539 }
1540 }
1541
1542 XDisplayUnlock();
1543 break;
1544 }
1545 XDisplayUnlock();
1546
1547 switch (event.type) {
1548 // Mouse button
1549 case ButtonPress: {
1550 unsigned int button = ((XButtonEvent *)&event)->button;
1551 if (button < 4)
1552 ADBMouseDown(button - 1);
1553 else if (button < 6) { // Wheel mouse
1554 if (mouse_wheel_mode == 0) {
1555 int key = (button == 5) ? 0x79 : 0x74; // Page up/down
1556 ADBKeyDown(key);
1557 ADBKeyUp(key);
1558 } else {
1559 int key = (button == 5) ? 0x3d : 0x3e; // Cursor up/down
1560 for(int i=0; i<mouse_wheel_lines; i++) {
1561 ADBKeyDown(key);
1562 ADBKeyUp(key);
1563 }
1564 }
1565 }
1566 break;
1567 }
1568 case ButtonRelease: {
1569 unsigned int button = ((XButtonEvent *)&event)->button;
1570 if (button < 4)
1571 ADBMouseUp(button - 1);
1572 break;
1573 }
1574
1575 // Mouse entered window
1576 case EnterNotify:
1577 if (event.xcrossing.mode != NotifyGrab && event.xcrossing.mode != NotifyUngrab)
1578 ADBMouseMoved(event.xmotion.x, event.xmotion.y);
1579 break;
1580
1581 // Mouse moved
1582 case MotionNotify:
1583 ADBMouseMoved(event.xmotion.x, event.xmotion.y);
1584 break;
1585
1586 // Keyboard
1587 case KeyPress: {
1588 int code = -1;
1589 if (use_keycodes) {
1590 if (event2keycode(event.xkey, true) != -2) // This is called to process the hotkeys
1591 code = keycode_table[event.xkey.keycode & 0xff];
1592 } else
1593 code = event2keycode(event.xkey, true);
1594 if (code >= 0) {
1595 if (!emul_suspended) {
1596 if (code == 0x39) { // Caps Lock pressed
1597 if (caps_on) {
1598 ADBKeyUp(code);
1599 caps_on = false;
1600 } else {
1601 ADBKeyDown(code);
1602 caps_on = true;
1603 }
1604 } else
1605 ADBKeyDown(code);
1606 if (code == 0x36)
1607 ctrl_down = true;
1608 } else {
1609 if (code == 0x31)
1610 resume_emul(); // Space wakes us up
1611 }
1612 }
1613 break;
1614 }
1615 case KeyRelease: {
1616 int code = -1;
1617 if (use_keycodes) {
1618 if (event2keycode(event.xkey, false) != -2) // This is called to process the hotkeys
1619 code = keycode_table[event.xkey.keycode & 0xff];
1620 } else
1621 code = event2keycode(event.xkey, false);
1622 if (code >= 0 && code != 0x39) { // Don't propagate Caps Lock releases
1623 ADBKeyUp(code);
1624 if (code == 0x36)
1625 ctrl_down = false;
1626 }
1627 break;
1628 }
1629
1630 // Hidden parts exposed, force complete refresh
1631 case Expose:
1632 #ifdef ENABLE_VOSF
1633 if (use_vosf) { // VOSF refresh
1634 LOCK_VOSF;
1635 PFLAG_SET_ALL;
1636 UNLOCK_VOSF;
1637 }
1638 #endif
1639 memset(the_buffer_copy, 0, VModes[cur_mode].viRowBytes * VModes[cur_mode].viYsize);
1640 break;
1641 }
1642 }
1643 }
1644
1645
1646 /*
1647 * Execute video VBL routine
1648 */
1649
1650 void VideoVBL(void)
1651 {
1652 if (emerg_quit)
1653 QuitEmulator();
1654
1655 // Execute video VBL
1656 if (private_data != NULL && private_data->interruptsEnabled)
1657 VSLDoInterruptService(private_data->vslServiceID);
1658 }
1659
1660
1661 /*
1662 * Change video mode
1663 */
1664
1665 int16 video_mode_change(VidLocals *csSave, uint32 ParamPtr)
1666 {
1667 /* return if no mode change */
1668 if ((csSave->saveData == ReadMacInt32(ParamPtr + csData)) &&
1669 (csSave->saveMode == ReadMacInt16(ParamPtr + csMode))) return noErr;
1670
1671 /* first find video mode in table */
1672 for (int i=0; VModes[i].viType != DIS_INVALID; i++) {
1673 if ((ReadMacInt16(ParamPtr + csMode) == VModes[i].viAppleMode) &&
1674 (ReadMacInt32(ParamPtr + csData) == VModes[i].viAppleID)) {
1675 csSave->saveMode = ReadMacInt16(ParamPtr + csMode);
1676 csSave->saveData = ReadMacInt32(ParamPtr + csData);
1677 csSave->savePage = ReadMacInt16(ParamPtr + csPage);
1678
1679 // Disable interrupts and pause redraw thread
1680 DisableInterrupt();
1681 thread_stop_ack = false;
1682 thread_stop_req = true;
1683 while (!thread_stop_ack) ;
1684
1685 /* close old display */
1686 close_display();
1687
1688 /* open new display */
1689 cur_mode = i;
1690 bool ok = open_display();
1691
1692 /* opening the screen failed? Then bail out */
1693 if (!ok) {
1694 ErrorAlert(GetString(STR_FULL_SCREEN_ERR));
1695 QuitEmulator();
1696 }
1697
1698 WriteMacInt32(ParamPtr + csBaseAddr, screen_base);
1699 csSave->saveBaseAddr=screen_base;
1700 csSave->saveData=VModes[cur_mode].viAppleID;/* First mode ... */
1701 csSave->saveMode=VModes[cur_mode].viAppleMode;
1702
1703 // Enable interrupts and resume redraw thread
1704 thread_stop_req = false;
1705 EnableInterrupt();
1706 return noErr;
1707 }
1708 }
1709 return paramErr;
1710 }
1711
1712
1713 /*
1714 * Set color palette
1715 */
1716
1717 void video_set_palette(void)
1718 {
1719 LOCK_PALETTE;
1720
1721 // Convert colors to XColor array
1722 int mode = get_current_mode();
1723 int num_in = palette_size(mode);
1724 int num_out = 256;
1725 bool stretch = false;
1726 if (IsDirectMode(mode)) {
1727 // If X is in 565 mode we have to stretch the gamma table from 32 to 64 entries
1728 num_out = vis->map_entries;
1729 stretch = true;
1730 }
1731 XColor *p = x_palette;
1732 for (int i=0; i<num_out; i++) {
1733 int c = (stretch ? (i * num_in) / num_out : i);
1734 p->red = mac_pal[c].red * 0x0101;
1735 p->green = mac_pal[c].green * 0x0101;
1736 p->blue = mac_pal[c].blue * 0x0101;
1737 p++;
1738 }
1739
1740 #ifdef ENABLE_VOSF
1741 // Recalculate pixel color expansion map
1742 if (!IsDirectMode(mode) && xdepth > 8) {
1743 for (int i=0; i<256; i++) {
1744 int c = i & (num_in-1); // If there are less than 256 colors, we repeat the first entries (this makes color expansion easier)
1745 ExpandMap[i] = map_rgb(mac_pal[c].red, mac_pal[c].green, mac_pal[c].blue);
1746 }
1747
1748 // We have to redraw everything because the interpretation of pixel values changed
1749 LOCK_VOSF;
1750 PFLAG_SET_ALL;
1751 UNLOCK_VOSF;
1752 memset(the_buffer_copy, 0, VModes[cur_mode].viRowBytes * VModes[cur_mode].viYsize);
1753 }
1754 #endif
1755
1756 // Tell redraw thread to change palette
1757 palette_changed = true;
1758
1759 UNLOCK_PALETTE;
1760 }
1761
1762
1763 /*
1764 * Can we set the MacOS cursor image into the window?
1765 */
1766
1767 bool video_can_change_cursor(void)
1768 {
1769 return hw_mac_cursor_accl && (display_type != DIS_SCREEN);
1770 }
1771
1772
1773 /*
1774 * Set cursor image for window
1775 */
1776
1777 void video_set_cursor(void)
1778 {
1779 cursor_changed = true;
1780 }
1781
1782
1783 /*
1784 * Thread for window refresh, event handling and other periodic actions
1785 */
1786
1787 static void update_display(void)
1788 {
1789 // Incremental update code
1790 int wide = 0, high = 0, x1, x2, y1, y2, i, j;
1791 int bytes_per_row = VModes[cur_mode].viRowBytes;
1792 int bytes_per_pixel = VModes[cur_mode].viRowBytes / VModes[cur_mode].viXsize;
1793 uint8 *p, *p2;
1794
1795 // Check for first line from top and first line from bottom that have changed
1796 y1 = 0;
1797 for (j=0; j<VModes[cur_mode].viYsize; j++) {
1798 if (memcmp(&the_buffer[j * bytes_per_row], &the_buffer_copy[j * bytes_per_row], bytes_per_row)) {
1799 y1 = j;
1800 break;
1801 }
1802 }
1803 y2 = y1 - 1;
1804 for (j=VModes[cur_mode].viYsize-1; j>=y1; j--) {
1805 if (memcmp(&the_buffer[j * bytes_per_row], &the_buffer_copy[j * bytes_per_row], bytes_per_row)) {
1806 y2 = j;
1807 break;
1808 }
1809 }
1810 high = y2 - y1 + 1;
1811
1812 // Check for first column from left and first column from right that have changed
1813 if (high) {
1814 if (depth == 1) {
1815 x1 = VModes[cur_mode].viXsize;
1816 for (j=y1; j<=y2; j++) {
1817 p = &the_buffer[j * bytes_per_row];
1818 p2 = &the_buffer_copy[j * bytes_per_row];
1819 for (i=0; i<(x1>>3); i++) {
1820 if (*p != *p2) {
1821 x1 = i << 3;
1822 break;
1823 }
1824 p++;
1825 p2++;
1826 }
1827 }
1828 x2 = x1;
1829 for (j=y1; j<=y2; j++) {
1830 p = &the_buffer[j * bytes_per_row];
1831 p2 = &the_buffer_copy[j * bytes_per_row];
1832 p += bytes_per_row;
1833 p2 += bytes_per_row;
1834 for (i=(VModes[cur_mode].viXsize>>3); i>(x2>>3); i--) {
1835 p--;
1836 p2--;
1837 if (*p != *p2) {
1838 x2 = i << 3;
1839 break;
1840 }
1841 }
1842 }
1843 wide = x2 - x1;
1844
1845 // Update copy of the_buffer
1846 if (high && wide) {
1847 for (j=y1; j<=y2; j++) {
1848 i = j * bytes_per_row + (x1 >> 3);
1849 memcpy(&the_buffer_copy[i], &the_buffer[i], wide >> 3);
1850 }
1851 }
1852
1853 } else {
1854 x1 = VModes[cur_mode].viXsize;
1855 for (j=y1; j<=y2; j++) {
1856 p = &the_buffer[j * bytes_per_row];
1857 p2 = &the_buffer_copy[j * bytes_per_row];
1858 for (i=0; i<x1; i++) {
1859 if (memcmp(p, p2, bytes_per_pixel)) {
1860 x1 = i;
1861 break;
1862 }
1863 p += bytes_per_pixel;
1864 p2 += bytes_per_pixel;
1865 }
1866 }
1867 x2 = x1;
1868 for (j=y1; j<=y2; j++) {
1869 p = &the_buffer[j * bytes_per_row];
1870 p2 = &the_buffer_copy[j * bytes_per_row];
1871 p += bytes_per_row;
1872 p2 += bytes_per_row;
1873 for (i=VModes[cur_mode].viXsize; i>x2; i--) {
1874 p -= bytes_per_pixel;
1875 p2 -= bytes_per_pixel;
1876 if (memcmp(p, p2, bytes_per_pixel)) {
1877 x2 = i;
1878 break;
1879 }
1880 }
1881 }
1882 wide = x2 - x1;
1883
1884 // Update copy of the_buffer
1885 if (high && wide) {
1886 for (j=y1; j<=y2; j++) {
1887 i = j * bytes_per_row + x1 * bytes_per_pixel;
1888 memcpy(&the_buffer_copy[i], &the_buffer[i], bytes_per_pixel * wide);
1889 }
1890 }
1891 }
1892 }
1893
1894 // Refresh display
1895 if (high && wide) {
1896 XDisplayLock();
1897 if (have_shm)
1898 XShmPutImage(x_display, the_win, the_gc, img, x1, y1, x1, y1, wide, high, 0);
1899 else
1900 XPutImage(x_display, the_win, the_gc, img, x1, y1, x1, y1, wide, high);
1901 XDisplayUnlock();
1902 }
1903 }
1904
1905 const int VIDEO_REFRESH_HZ = 60;
1906 const int VIDEO_REFRESH_DELAY = 1000000 / VIDEO_REFRESH_HZ;
1907
1908 static void handle_palette_changes(void)
1909 {
1910 LOCK_PALETTE;
1911
1912 if (palette_changed && !emul_suspended) {
1913 palette_changed = false;
1914
1915 int mode = get_current_mode();
1916 if (color_class == PseudoColor || color_class == DirectColor) {
1917 int num = vis->map_entries;
1918 bool set_clut = true;
1919 if (!IsDirectMode(mode) && color_class == DirectColor) {
1920 if (display_type == DIS_WINDOW)
1921 set_clut = false; // Indexed mode on true color screen, don't set CLUT
1922 }
1923
1924 if (set_clut) {
1925 XDisplayLock();
1926 XStoreColors(x_display, cmap[0], x_palette, num);
1927 XStoreColors(x_display, cmap[1], x_palette, num);
1928 XSync(x_display, false);
1929 XDisplayUnlock();
1930 }
1931 }
1932
1933 #ifdef ENABLE_XF86_DGA
1934 if (display_type == DIS_SCREEN) {
1935 current_dga_cmap ^= 1;
1936 if (!IsDirectMode(mode) && cmap[current_dga_cmap])
1937 XF86DGAInstallColormap(x_display, screen, cmap[current_dga_cmap]);
1938 }
1939 #endif
1940 }
1941
1942 UNLOCK_PALETTE;
1943 }
1944
1945 static void *redraw_func(void *arg)
1946 {
1947 int fd = ConnectionNumber(x_display);
1948
1949 uint64 start = GetTicks_usec();
1950 int64 ticks = 0;
1951 uint64 next = GetTicks_usec() + VIDEO_REFRESH_DELAY;
1952
1953 while (!redraw_thread_cancel) {
1954
1955 // Pause if requested (during video mode switches)
1956 while (thread_stop_req)
1957 thread_stop_ack = true;
1958
1959 int64 delay = next - GetTicks_usec();
1960 if (delay < -VIDEO_REFRESH_DELAY) {
1961
1962 // We are lagging far behind, so we reset the delay mechanism
1963 next = GetTicks_usec();
1964
1965 } else if (delay <= 0) {
1966
1967 // Delay expired, refresh display
1968 next += VIDEO_REFRESH_DELAY;
1969 ticks++;
1970
1971 // Handle X11 events
1972 handle_events();
1973
1974 // Quit DGA mode if requested
1975 if (quit_full_screen) {
1976 quit_full_screen = false;
1977 if (display_type == DIS_SCREEN) {
1978 XDisplayLock();
1979 #ifdef ENABLE_XF86_DGA
1980 XF86DGADirectVideo(x_display, screen, 0);
1981 XUngrabPointer(x_display, CurrentTime);
1982 XUngrabKeyboard(x_display, CurrentTime);
1983 XUnmapWindow(x_display, the_win);
1984 wait_unmapped(the_win);
1985 XDestroyWindow(x_display, the_win);
1986 #endif
1987 XSync(x_display, false);
1988 XDisplayUnlock();
1989 quit_full_screen_ack = true;
1990 return NULL;
1991 }
1992 }
1993
1994 // Refresh display and set cursor image in window mode
1995 static int tick_counter = 0;
1996 if (display_type == DIS_WINDOW) {
1997 tick_counter++;
1998 if (tick_counter >= frame_skip) {
1999 tick_counter = 0;
2000
2001 // Update display
2002 #ifdef ENABLE_VOSF
2003 if (use_vosf) {
2004 XDisplayLock();
2005 if (mainBuffer.dirty) {
2006 LOCK_VOSF;
2007 update_display_window_vosf();
2008 UNLOCK_VOSF;
2009 XSync(x_display, false); // Let the server catch up
2010 }
2011 XDisplayUnlock();
2012 }
2013 else
2014 #endif
2015 update_display();
2016
2017 // Set new cursor image if it was changed
2018 if (hw_mac_cursor_accl && cursor_changed) {
2019 cursor_changed = false;
2020 uint8 *x_data = (uint8 *)cursor_image->data;
2021 uint8 *x_mask = (uint8 *)cursor_mask_image->data;
2022 for (int i = 0; i < 32; i++) {
2023 x_mask[i] = MacCursor[4 + i] | MacCursor[36 + i];
2024 x_data[i] = MacCursor[4 + i];
2025 }
2026 XDisplayLock();
2027 XFreeCursor(x_display, mac_cursor);
2028 XPutImage(x_display, cursor_map, cursor_gc, cursor_image, 0, 0, 0, 0, 16, 16);
2029 XPutImage(x_display, cursor_mask_map, cursor_mask_gc, cursor_mask_image, 0, 0, 0, 0, 16, 16);
2030 mac_cursor = XCreatePixmapCursor(x_display, cursor_map, cursor_mask_map, &black, &white, MacCursor[2], MacCursor[3]);
2031 XDefineCursor(x_display, the_win, mac_cursor);
2032 XDisplayUnlock();
2033 }
2034 }
2035 }
2036 #ifdef ENABLE_VOSF
2037 else if (use_vosf) {
2038 // Update display (VOSF variant)
2039 if (++tick_counter >= frame_skip) {
2040 tick_counter = 0;
2041 if (mainBuffer.dirty) {
2042 LOCK_VOSF;
2043 update_display_dga_vosf();
2044 UNLOCK_VOSF;
2045 }
2046 }
2047 }
2048 #endif
2049
2050 // Set new palette if it was changed
2051 handle_palette_changes();
2052
2053 } else {
2054
2055 // No display refresh pending, check for X events
2056 fd_set readfds;
2057 FD_ZERO(&readfds);
2058 FD_SET(fd, &readfds);
2059 struct timeval timeout;
2060 timeout.tv_sec = 0;
2061 timeout.tv_usec = delay;
2062 if (select(fd+1, &readfds, NULL, NULL, &timeout) > 0)
2063 handle_events();
2064 }
2065 }
2066 return NULL;
2067 }