ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/SheepShaver/src/Unix/video_x.cpp
Revision: 1.32
Committed: 2004-07-02T06:06:34Z (20 years, 5 months ago) by gbeauche
Branch: MAIN
Changes since 1.31: +0 -360 lines
Log Message:
Move NQD to gfxaccel.cpp, since it does not depend on a specific system
implementation.

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 = (uint32)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 = (uint32)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 #if (defined(__APPLE__) && defined(__MACH__))
896 // Force use of MacX mappings on MacOS X with Apple's X server
897 int dummy;
898 if (XQueryExtension(x_display, "Apple-DRI", &dummy, &dummy, &dummy))
899 vendor = "MacX";
900 #endif
901 bool vendor_found = false;
902 char line[256];
903 while (fgets(line, 255, f)) {
904 // Read line
905 int len = strlen(line);
906 if (len == 0)
907 continue;
908 line[len-1] = 0;
909
910 // Comments begin with "#" or ";"
911 if (line[0] == '#' || line[0] == ';' || line[0] == 0)
912 continue;
913
914 if (vendor_found) {
915 // Read keycode
916 int x_code, mac_code;
917 if (sscanf(line, "%d %d", &x_code, &mac_code) == 2)
918 keycode_table[x_code & 0xff] = mac_code;
919 else
920 break;
921 } else {
922 // Search for vendor string
923 if (strstr(vendor, line) == vendor)
924 vendor_found = true;
925 }
926 }
927
928 // Keycode file completely read
929 fclose(f);
930 use_keycodes = vendor_found;
931
932 // Vendor not found? Then display warning
933 if (!vendor_found) {
934 char str[256];
935 sprintf(str, GetString(STR_KEYCODE_VENDOR_WARN), vendor, kc_path ? kc_path : KEYCODE_FILE_NAME);
936 WarningAlert(str);
937 return;
938 }
939 }
940 }
941
942 // Find Apple mode matching best specified dimensions
943 static int find_apple_resolution(int xsize, int ysize)
944 {
945 int apple_id;
946 if (xsize < 800)
947 apple_id = APPLE_640x480;
948 else if (xsize < 1024)
949 apple_id = APPLE_800x600;
950 else if (xsize < 1152)
951 apple_id = APPLE_1024x768;
952 else if (xsize < 1280) {
953 if (ysize < 900)
954 apple_id = APPLE_1152x768;
955 else
956 apple_id = APPLE_1152x900;
957 }
958 else if (xsize < 1600)
959 apple_id = APPLE_1280x1024;
960 else
961 apple_id = APPLE_1600x1200;
962 return apple_id;
963 }
964
965 // Find mode in list of supported modes
966 static int find_mode(int apple_mode, int apple_id, int type)
967 {
968 for (VideoInfo *p = VModes; p->viType != DIS_INVALID; p++) {
969 if (p->viType == type && p->viAppleID == apple_id && p->viAppleMode == apple_mode)
970 return p - VModes;
971 }
972 return -1;
973 }
974
975 // Add mode to list of supported modes
976 static void add_mode(VideoInfo *&p, uint32 allow, uint32 test, int apple_mode, int apple_id, int type)
977 {
978 if (allow & test) {
979 p->viType = type;
980 switch (apple_id) {
981 case APPLE_W_640x480:
982 case APPLE_640x480:
983 p->viXsize = 640;
984 p->viYsize = 480;
985 break;
986 case APPLE_W_800x600:
987 case APPLE_800x600:
988 p->viXsize = 800;
989 p->viYsize = 600;
990 break;
991 case APPLE_1024x768:
992 p->viXsize = 1024;
993 p->viYsize = 768;
994 break;
995 case APPLE_1152x768:
996 p->viXsize = 1152;
997 p->viYsize = 768;
998 break;
999 case APPLE_1152x900:
1000 p->viXsize = 1152;
1001 p->viYsize = 900;
1002 break;
1003 case APPLE_1280x1024:
1004 p->viXsize = 1280;
1005 p->viYsize = 1024;
1006 break;
1007 case APPLE_1600x1200:
1008 p->viXsize = 1600;
1009 p->viYsize = 1200;
1010 break;
1011 }
1012 p->viRowBytes = TrivialBytesPerRow(p->viXsize, apple_mode);
1013 p->viAppleMode = apple_mode;
1014 p->viAppleID = apple_id;
1015 p++;
1016 }
1017 }
1018
1019 // Add standard list of windowed modes for given color depth
1020 static void add_window_modes(VideoInfo *&p, int window_modes, int mode)
1021 {
1022 add_mode(p, window_modes, 1, mode, APPLE_W_640x480, DIS_WINDOW);
1023 add_mode(p, window_modes, 2, mode, APPLE_W_800x600, DIS_WINDOW);
1024 }
1025
1026 static bool has_mode(int x, int y)
1027 {
1028 #ifdef ENABLE_XF86_VIDMODE
1029 for (int i=0; i<num_x_video_modes; i++)
1030 if (x_video_modes[i]->hdisplay >= x && x_video_modes[i]->vdisplay >= y)
1031 return true;
1032 return false;
1033 #else
1034 return DisplayWidth(x_display, screen) >= x && DisplayHeight(x_display, screen) >= y;
1035 #endif
1036 }
1037
1038 bool VideoInit(void)
1039 {
1040 #ifdef ENABLE_VOSF
1041 // Zero the mainBuffer structure
1042 mainBuffer.dirtyPages = NULL;
1043 mainBuffer.pageInfo = NULL;
1044 #endif
1045
1046 // Check if X server runs on local machine
1047 local_X11 = (strncmp(XDisplayName(x_display_name), ":", 1) == 0)
1048 || (strncmp(XDisplayName(x_display_name), "unix:", 5) == 0);
1049
1050 // Init keycode translation
1051 keycode_init();
1052
1053 // Read frame skip prefs
1054 frame_skip = PrefsFindInt32("frameskip");
1055 if (frame_skip == 0)
1056 frame_skip = 1;
1057
1058 // Read mouse wheel prefs
1059 mouse_wheel_mode = PrefsFindInt32("mousewheelmode");
1060 mouse_wheel_lines = PrefsFindInt32("mousewheellines");
1061
1062 // Init variables
1063 private_data = NULL;
1064 video_activated = true;
1065
1066 // Find screen and root window
1067 screen = XDefaultScreen(x_display);
1068 rootwin = XRootWindow(x_display, screen);
1069
1070 // Get sorted list of available depths
1071 avail_depths = XListDepths(x_display, screen, &num_depths);
1072 if (avail_depths == NULL) {
1073 ErrorAlert(GetString(STR_UNSUPP_DEPTH_ERR));
1074 return false;
1075 }
1076 sort(avail_depths, avail_depths + num_depths);
1077
1078 // Get screen depth
1079 xdepth = DefaultDepth(x_display, screen);
1080
1081 #ifdef ENABLE_XF86_DGA
1082 // DGA available?
1083 int event_base, error_base;
1084 if (local_X11 && XF86DGAQueryExtension(x_display, &event_base, &error_base)) {
1085 int dga_flags = 0;
1086 XF86DGAQueryDirectVideo(x_display, screen, &dga_flags);
1087 has_dga = dga_flags & XF86DGADirectPresent;
1088 } else
1089 has_dga = false;
1090 #endif
1091
1092 #ifdef ENABLE_XF86_VIDMODE
1093 // VidMode available?
1094 int vm_event_base, vm_error_base;
1095 has_vidmode = XF86VidModeQueryExtension(x_display, &vm_event_base, &vm_error_base);
1096 if (has_vidmode)
1097 XF86VidModeGetAllModeLines(x_display, screen, &num_x_video_modes, &x_video_modes);
1098 #endif
1099
1100 // Find black and white colors
1101 XParseColor(x_display, DefaultColormap(x_display, screen), "rgb:00/00/00", &black);
1102 XAllocColor(x_display, DefaultColormap(x_display, screen), &black);
1103 XParseColor(x_display, DefaultColormap(x_display, screen), "rgb:ff/ff/ff", &white);
1104 XAllocColor(x_display, DefaultColormap(x_display, screen), &white);
1105 black_pixel = BlackPixel(x_display, screen);
1106 white_pixel = WhitePixel(x_display, screen);
1107
1108 // Mac screen depth follows X depth (for now)
1109 int default_mode = APPLE_8_BIT;
1110 switch (DefaultDepth(x_display, screen)) {
1111 case 1:
1112 default_mode = APPLE_1_BIT;
1113 break;
1114 case 8:
1115 default_mode = APPLE_8_BIT;
1116 break;
1117 case 15: case 16:
1118 default_mode = APPLE_16_BIT;
1119 break;
1120 case 24: case 32:
1121 default_mode = APPLE_32_BIT;
1122 break;
1123 }
1124
1125 // Construct video mode table
1126 uint32 window_modes = PrefsFindInt32("windowmodes");
1127 uint32 screen_modes = PrefsFindInt32("screenmodes");
1128 if (!has_dga)
1129 screen_modes = 0;
1130 if (window_modes == 0 && screen_modes == 0)
1131 window_modes |= 3; // Allow at least 640x480 and 800x600 window modes
1132
1133 VideoInfo *p = VModes;
1134 for (unsigned int d = APPLE_1_BIT; d <= APPLE_32_BIT; d++)
1135 if (find_visual_for_depth(d))
1136 add_window_modes(p, window_modes, d);
1137
1138 if (has_vidmode) {
1139 if (has_mode(640, 480))
1140 add_mode(p, screen_modes, 1, default_mode, APPLE_640x480, DIS_SCREEN);
1141 if (has_mode(800, 600))
1142 add_mode(p, screen_modes, 2, default_mode, APPLE_800x600, DIS_SCREEN);
1143 if (has_mode(1024, 768))
1144 add_mode(p, screen_modes, 4, default_mode, APPLE_1024x768, DIS_SCREEN);
1145 if (has_mode(1152, 768))
1146 add_mode(p, screen_modes, 64, default_mode, APPLE_1152x768, DIS_SCREEN);
1147 if (has_mode(1152, 900))
1148 add_mode(p, screen_modes, 8, default_mode, APPLE_1152x900, DIS_SCREEN);
1149 if (has_mode(1280, 1024))
1150 add_mode(p, screen_modes, 16, default_mode, APPLE_1280x1024, DIS_SCREEN);
1151 if (has_mode(1600, 1200))
1152 add_mode(p, screen_modes, 32, default_mode, APPLE_1600x1200, DIS_SCREEN);
1153 } else if (screen_modes) {
1154 int xsize = DisplayWidth(x_display, screen);
1155 int ysize = DisplayHeight(x_display, screen);
1156 int apple_id = find_apple_resolution(xsize, ysize);
1157 p->viType = DIS_SCREEN;
1158 p->viRowBytes = 0;
1159 p->viXsize = xsize;
1160 p->viYsize = ysize;
1161 p->viAppleMode = default_mode;
1162 p->viAppleID = apple_id;
1163 p++;
1164 }
1165 p->viType = DIS_INVALID; // End marker
1166 p->viRowBytes = 0;
1167 p->viXsize = p->viYsize = 0;
1168 p->viAppleMode = 0;
1169 p->viAppleID = 0;
1170
1171 // Find default mode (window 640x480)
1172 cur_mode = -1;
1173 if (has_dga && screen_modes) {
1174 int screen_width = DisplayWidth(x_display, screen);
1175 int screen_height = DisplayHeight(x_display, screen);
1176 int apple_id = find_apple_resolution(screen_width, screen_height);
1177 if (apple_id != -1)
1178 cur_mode = find_mode(default_mode, apple_id, DIS_SCREEN);
1179 }
1180 if (cur_mode == -1) {
1181 // pick up first windowed mode available
1182 for (VideoInfo *p = VModes; p->viType != DIS_INVALID; p++) {
1183 if (p->viType == DIS_WINDOW && p->viAppleMode == default_mode) {
1184 cur_mode = p - VModes;
1185 break;
1186 }
1187 }
1188 }
1189 assert(cur_mode != -1);
1190
1191 #if DEBUG
1192 D(bug("Available video modes:\n"));
1193 for (p = VModes; p->viType != DIS_INVALID; p++) {
1194 int bits = depth_of_video_mode(p->viAppleMode);
1195 D(bug(" %dx%d (ID %02x), %d colors\n", p->viXsize, p->viYsize, p->viAppleID, 1 << bits));
1196 }
1197 #endif
1198
1199 // Open window/screen
1200 if (!open_display())
1201 return false;
1202
1203 #if 0
1204 // Ignore errors from now on
1205 XSetErrorHandler(ignore_errors);
1206 #endif
1207
1208 // Start periodic thread
1209 XSync(x_display, false);
1210 Set_pthread_attr(&redraw_thread_attr, 0);
1211 redraw_thread_cancel = false;
1212 redraw_thread_active = (pthread_create(&redraw_thread, &redraw_thread_attr, redraw_func, NULL) == 0);
1213 D(bug("Redraw thread installed (%ld)\n", redraw_thread));
1214 return true;
1215 }
1216
1217
1218 /*
1219 * Deinitialization
1220 */
1221
1222 void VideoExit(void)
1223 {
1224 // Stop redraw thread
1225 if (redraw_thread_active) {
1226 redraw_thread_cancel = true;
1227 pthread_cancel(redraw_thread);
1228 pthread_join(redraw_thread, NULL);
1229 redraw_thread_active = false;
1230 }
1231
1232 #ifdef ENABLE_VOSF
1233 if (use_vosf) {
1234 // Deinitialize VOSF
1235 video_vosf_exit();
1236 }
1237 #endif
1238
1239 // Close window and server connection
1240 if (x_display != NULL) {
1241 XSync(x_display, false);
1242 close_display();
1243 XFlush(x_display);
1244 XSync(x_display, false);
1245 }
1246 }
1247
1248
1249 /*
1250 * Suspend/resume emulator
1251 */
1252
1253 extern void PauseEmulator(void);
1254 extern void ResumeEmulator(void);
1255
1256 static void suspend_emul(void)
1257 {
1258 if (display_type == DIS_SCREEN) {
1259 // Release ctrl key
1260 ADBKeyUp(0x36);
1261 ctrl_down = false;
1262
1263 // Pause MacOS thread
1264 PauseEmulator();
1265 emul_suspended = true;
1266
1267 // Save frame buffer
1268 fb_save = malloc(VModes[cur_mode].viYsize * VModes[cur_mode].viRowBytes);
1269 if (fb_save)
1270 memcpy(fb_save, (void *)screen_base, VModes[cur_mode].viYsize * VModes[cur_mode].viRowBytes);
1271
1272 // Close full screen display
1273 #ifdef ENABLE_XF86_DGA
1274 XF86DGADirectVideo(x_display, screen, 0);
1275 XUngrabPointer(x_display, CurrentTime);
1276 XUngrabKeyboard(x_display, CurrentTime);
1277 #endif
1278 XSync(x_display, false);
1279
1280 // Open "suspend" window
1281 XSetWindowAttributes wattr;
1282 wattr.event_mask = KeyPressMask;
1283 wattr.background_pixel = black_pixel;
1284 wattr.border_pixel = black_pixel;
1285 wattr.backing_store = Always;
1286 wattr.backing_planes = xdepth;
1287 wattr.colormap = DefaultColormap(x_display, screen);
1288 XSync(x_display, false);
1289 suspend_win = XCreateWindow(x_display, rootwin, 0, 0, 512, 1, 0, xdepth,
1290 InputOutput, vis, CWEventMask | CWBackPixel | CWBorderPixel |
1291 CWBackingStore | CWBackingPlanes | (xdepth == 8 ? CWColormap : 0), &wattr);
1292 XSync(x_display, false);
1293 XStoreName(x_display, suspend_win, GetString(STR_SUSPEND_WINDOW_TITLE));
1294 XMapRaised(x_display, suspend_win);
1295 XSync(x_display, false);
1296 }
1297 }
1298
1299 static void resume_emul(void)
1300 {
1301 // Close "suspend" window
1302 XDestroyWindow(x_display, suspend_win);
1303 XSync(x_display, false);
1304
1305 // Reopen full screen display
1306 XGrabKeyboard(x_display, rootwin, 1, GrabModeAsync, GrabModeAsync, CurrentTime);
1307 XGrabPointer(x_display, rootwin, 1, PointerMotionMask | ButtonPressMask | ButtonReleaseMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
1308 #ifdef ENABLE_XF86_DGA
1309 XF86DGADirectVideo(x_display, screen, XF86DGADirectGraphics | XF86DGADirectKeyb | XF86DGADirectMouse);
1310 XF86DGASetViewPort(x_display, screen, 0, 0);
1311 #endif
1312 XSync(x_display, false);
1313
1314 // the_buffer already contains the data to restore. i.e. since a temporary
1315 // frame buffer is used when VOSF is actually used, fb_save is therefore
1316 // not necessary.
1317 #ifdef ENABLE_VOSF
1318 if (use_vosf) {
1319 LOCK_VOSF;
1320 PFLAG_SET_ALL;
1321 UNLOCK_VOSF;
1322 memset(the_buffer_copy, 0, VModes[cur_mode].viRowBytes * VModes[cur_mode].viYsize);
1323 }
1324 #endif
1325
1326 // Restore frame buffer
1327 if (fb_save) {
1328 #ifdef ENABLE_VOSF
1329 // Don't copy fb_save to the temporary frame buffer in VOSF mode
1330 if (!use_vosf)
1331 #endif
1332 memcpy((void *)screen_base, fb_save, VModes[cur_mode].viYsize * VModes[cur_mode].viRowBytes);
1333 free(fb_save);
1334 fb_save = NULL;
1335 }
1336 if (depth == 8)
1337 palette_changed = true;
1338
1339 // Resume MacOS thread
1340 emul_suspended = false;
1341 ResumeEmulator();
1342 }
1343
1344
1345 /*
1346 * Close screen in full-screen mode
1347 */
1348
1349 void VideoQuitFullScreen(void)
1350 {
1351 D(bug("VideoQuitFullScreen()\n"));
1352 if (display_type == DIS_SCREEN) {
1353 quit_full_screen = true;
1354 while (!quit_full_screen_ack) ;
1355 }
1356 }
1357
1358
1359 /*
1360 * X11 event handling
1361 */
1362
1363 // Translate key event to Mac keycode
1364 static int kc_decode(KeySym ks)
1365 {
1366 switch (ks) {
1367 case XK_A: case XK_a: return 0x00;
1368 case XK_B: case XK_b: return 0x0b;
1369 case XK_C: case XK_c: return 0x08;
1370 case XK_D: case XK_d: return 0x02;
1371 case XK_E: case XK_e: return 0x0e;
1372 case XK_F: case XK_f: return 0x03;
1373 case XK_G: case XK_g: return 0x05;
1374 case XK_H: case XK_h: return 0x04;
1375 case XK_I: case XK_i: return 0x22;
1376 case XK_J: case XK_j: return 0x26;
1377 case XK_K: case XK_k: return 0x28;
1378 case XK_L: case XK_l: return 0x25;
1379 case XK_M: case XK_m: return 0x2e;
1380 case XK_N: case XK_n: return 0x2d;
1381 case XK_O: case XK_o: return 0x1f;
1382 case XK_P: case XK_p: return 0x23;
1383 case XK_Q: case XK_q: return 0x0c;
1384 case XK_R: case XK_r: return 0x0f;
1385 case XK_S: case XK_s: return 0x01;
1386 case XK_T: case XK_t: return 0x11;
1387 case XK_U: case XK_u: return 0x20;
1388 case XK_V: case XK_v: return 0x09;
1389 case XK_W: case XK_w: return 0x0d;
1390 case XK_X: case XK_x: return 0x07;
1391 case XK_Y: case XK_y: return 0x10;
1392 case XK_Z: case XK_z: return 0x06;
1393
1394 case XK_1: case XK_exclam: return 0x12;
1395 case XK_2: case XK_at: return 0x13;
1396 case XK_3: case XK_numbersign: return 0x14;
1397 case XK_4: case XK_dollar: return 0x15;
1398 case XK_5: case XK_percent: return 0x17;
1399 case XK_6: return 0x16;
1400 case XK_7: return 0x1a;
1401 case XK_8: return 0x1c;
1402 case XK_9: return 0x19;
1403 case XK_0: return 0x1d;
1404
1405 case XK_grave: case XK_asciitilde: return 0x0a;
1406 case XK_minus: case XK_underscore: return 0x1b;
1407 case XK_equal: case XK_plus: return 0x18;
1408 case XK_bracketleft: case XK_braceleft: return 0x21;
1409 case XK_bracketright: case XK_braceright: return 0x1e;
1410 case XK_backslash: case XK_bar: return 0x2a;
1411 case XK_semicolon: case XK_colon: return 0x29;
1412 case XK_apostrophe: case XK_quotedbl: return 0x27;
1413 case XK_comma: case XK_less: return 0x2b;
1414 case XK_period: case XK_greater: return 0x2f;
1415 case XK_slash: case XK_question: return 0x2c;
1416
1417 case XK_Tab: if (ctrl_down) {suspend_emul(); return -1;} else return 0x30;
1418 case XK_Return: return 0x24;
1419 case XK_space: return 0x31;
1420 case XK_BackSpace: return 0x33;
1421
1422 case XK_Delete: return 0x75;
1423 case XK_Insert: return 0x72;
1424 case XK_Home: case XK_Help: return 0x73;
1425 case XK_End: return 0x77;
1426 #ifdef __hpux
1427 case XK_Prior: return 0x74;
1428 case XK_Next: return 0x79;
1429 #else
1430 case XK_Page_Up: return 0x74;
1431 case XK_Page_Down: return 0x79;
1432 #endif
1433
1434 case XK_Control_L: return 0x36;
1435 case XK_Control_R: return 0x36;
1436 case XK_Shift_L: return 0x38;
1437 case XK_Shift_R: return 0x38;
1438 case XK_Alt_L: return 0x37;
1439 case XK_Alt_R: return 0x37;
1440 case XK_Meta_L: return 0x3a;
1441 case XK_Meta_R: return 0x3a;
1442 case XK_Menu: return 0x32;
1443 case XK_Caps_Lock: return 0x39;
1444 case XK_Num_Lock: return 0x47;
1445
1446 case XK_Up: return 0x3e;
1447 case XK_Down: return 0x3d;
1448 case XK_Left: return 0x3b;
1449 case XK_Right: return 0x3c;
1450
1451 case XK_Escape: if (ctrl_down) {quit_full_screen = true; emerg_quit = true; return -1;} else return 0x35;
1452
1453 case XK_F1: if (ctrl_down) {SysMountFirstFloppy(); return -1;} else return 0x7a;
1454 case XK_F2: return 0x78;
1455 case XK_F3: return 0x63;
1456 case XK_F4: return 0x76;
1457 case XK_F5: return 0x60;
1458 case XK_F6: return 0x61;
1459 case XK_F7: return 0x62;
1460 case XK_F8: return 0x64;
1461 case XK_F9: return 0x65;
1462 case XK_F10: return 0x6d;
1463 case XK_F11: return 0x67;
1464 case XK_F12: return 0x6f;
1465
1466 case XK_Print: return 0x69;
1467 case XK_Scroll_Lock: return 0x6b;
1468 case XK_Pause: return 0x71;
1469
1470 #if defined(XK_KP_Prior) && defined(XK_KP_Left) && defined(XK_KP_Insert) && defined (XK_KP_End)
1471 case XK_KP_0: case XK_KP_Insert: return 0x52;
1472 case XK_KP_1: case XK_KP_End: return 0x53;
1473 case XK_KP_2: case XK_KP_Down: return 0x54;
1474 case XK_KP_3: case XK_KP_Next: return 0x55;
1475 case XK_KP_4: case XK_KP_Left: return 0x56;
1476 case XK_KP_5: case XK_KP_Begin: return 0x57;
1477 case XK_KP_6: case XK_KP_Right: return 0x58;
1478 case XK_KP_7: case XK_KP_Home: return 0x59;
1479 case XK_KP_8: case XK_KP_Up: return 0x5b;
1480 case XK_KP_9: case XK_KP_Prior: return 0x5c;
1481 case XK_KP_Decimal: case XK_KP_Delete: return 0x41;
1482 #else
1483 case XK_KP_0: return 0x52;
1484 case XK_KP_1: return 0x53;
1485 case XK_KP_2: return 0x54;
1486 case XK_KP_3: return 0x55;
1487 case XK_KP_4: return 0x56;
1488 case XK_KP_5: return 0x57;
1489 case XK_KP_6: return 0x58;
1490 case XK_KP_7: return 0x59;
1491 case XK_KP_8: return 0x5b;
1492 case XK_KP_9: return 0x5c;
1493 case XK_KP_Decimal: return 0x41;
1494 #endif
1495 case XK_KP_Add: return 0x45;
1496 case XK_KP_Subtract: return 0x4e;
1497 case XK_KP_Multiply: return 0x43;
1498 case XK_KP_Divide: return 0x4b;
1499 case XK_KP_Enter: return 0x4c;
1500 case XK_KP_Equal: return 0x51;
1501 }
1502 return -1;
1503 }
1504
1505 static int event2keycode(XKeyEvent &ev, bool key_down)
1506 {
1507 KeySym ks;
1508 int i = 0;
1509
1510 do {
1511 ks = XLookupKeysym(&ev, i++);
1512 int as = kc_decode(ks);
1513 if (as >= 0)
1514 return as;
1515 if (as == -2)
1516 return as;
1517 } while (ks != NoSymbol);
1518
1519 return -1;
1520 }
1521
1522 static void handle_events(void)
1523 {
1524 // Handle events
1525 for (;;) {
1526 XEvent event;
1527
1528 XDisplayLock();
1529 if (!XCheckMaskEvent(x_display, eventmask, &event)) {
1530 // Handle clipboard events
1531 if (XCheckTypedEvent(x_display, SelectionRequest, &event))
1532 ClipboardSelectionRequest(&event.xselectionrequest);
1533 else if (XCheckTypedEvent(x_display, SelectionClear, &event))
1534 ClipboardSelectionClear(&event.xselectionclear);
1535
1536 // Window "close" widget clicked
1537 else if (XCheckTypedEvent(x_display, ClientMessage, &event)) {
1538 if (event.xclient.format == 32 && event.xclient.data.l[0] == WM_DELETE_WINDOW) {
1539 ADBKeyDown(0x7f); // Power key
1540 ADBKeyUp(0x7f);
1541 }
1542 }
1543
1544 XDisplayUnlock();
1545 break;
1546 }
1547 XDisplayUnlock();
1548
1549 switch (event.type) {
1550 // Mouse button
1551 case ButtonPress: {
1552 unsigned int button = ((XButtonEvent *)&event)->button;
1553 if (button < 4)
1554 ADBMouseDown(button - 1);
1555 else if (button < 6) { // Wheel mouse
1556 if (mouse_wheel_mode == 0) {
1557 int key = (button == 5) ? 0x79 : 0x74; // Page up/down
1558 ADBKeyDown(key);
1559 ADBKeyUp(key);
1560 } else {
1561 int key = (button == 5) ? 0x3d : 0x3e; // Cursor up/down
1562 for(int i=0; i<mouse_wheel_lines; i++) {
1563 ADBKeyDown(key);
1564 ADBKeyUp(key);
1565 }
1566 }
1567 }
1568 break;
1569 }
1570 case ButtonRelease: {
1571 unsigned int button = ((XButtonEvent *)&event)->button;
1572 if (button < 4)
1573 ADBMouseUp(button - 1);
1574 break;
1575 }
1576
1577 // Mouse entered window
1578 case EnterNotify:
1579 if (event.xcrossing.mode != NotifyGrab && event.xcrossing.mode != NotifyUngrab)
1580 ADBMouseMoved(event.xmotion.x, event.xmotion.y);
1581 break;
1582
1583 // Mouse moved
1584 case MotionNotify:
1585 ADBMouseMoved(event.xmotion.x, event.xmotion.y);
1586 break;
1587
1588 // Keyboard
1589 case KeyPress: {
1590 int code = -1;
1591 if (use_keycodes) {
1592 if (event2keycode(event.xkey, true) != -2) // This is called to process the hotkeys
1593 code = keycode_table[event.xkey.keycode & 0xff];
1594 } else
1595 code = event2keycode(event.xkey, true);
1596 if (code >= 0) {
1597 if (!emul_suspended) {
1598 if (code == 0x39) { // Caps Lock pressed
1599 if (caps_on) {
1600 ADBKeyUp(code);
1601 caps_on = false;
1602 } else {
1603 ADBKeyDown(code);
1604 caps_on = true;
1605 }
1606 } else
1607 ADBKeyDown(code);
1608 if (code == 0x36)
1609 ctrl_down = true;
1610 } else {
1611 if (code == 0x31)
1612 resume_emul(); // Space wakes us up
1613 }
1614 }
1615 break;
1616 }
1617 case KeyRelease: {
1618 int code = -1;
1619 if (use_keycodes) {
1620 if (event2keycode(event.xkey, false) != -2) // This is called to process the hotkeys
1621 code = keycode_table[event.xkey.keycode & 0xff];
1622 } else
1623 code = event2keycode(event.xkey, false);
1624 if (code >= 0 && code != 0x39) { // Don't propagate Caps Lock releases
1625 ADBKeyUp(code);
1626 if (code == 0x36)
1627 ctrl_down = false;
1628 }
1629 break;
1630 }
1631
1632 // Hidden parts exposed, force complete refresh
1633 case Expose:
1634 #ifdef ENABLE_VOSF
1635 if (use_vosf) { // VOSF refresh
1636 LOCK_VOSF;
1637 PFLAG_SET_ALL;
1638 UNLOCK_VOSF;
1639 }
1640 #endif
1641 memset(the_buffer_copy, 0, VModes[cur_mode].viRowBytes * VModes[cur_mode].viYsize);
1642 break;
1643 }
1644 }
1645 }
1646
1647
1648 /*
1649 * Execute video VBL routine
1650 */
1651
1652 void VideoVBL(void)
1653 {
1654 if (emerg_quit)
1655 QuitEmulator();
1656
1657 // Execute video VBL
1658 if (private_data != NULL && private_data->interruptsEnabled)
1659 VSLDoInterruptService(private_data->vslServiceID);
1660 }
1661
1662
1663 /*
1664 * Change video mode
1665 */
1666
1667 int16 video_mode_change(VidLocals *csSave, uint32 ParamPtr)
1668 {
1669 /* return if no mode change */
1670 if ((csSave->saveData == ReadMacInt32(ParamPtr + csData)) &&
1671 (csSave->saveMode == ReadMacInt16(ParamPtr + csMode))) return noErr;
1672
1673 /* first find video mode in table */
1674 for (int i=0; VModes[i].viType != DIS_INVALID; i++) {
1675 if ((ReadMacInt16(ParamPtr + csMode) == VModes[i].viAppleMode) &&
1676 (ReadMacInt32(ParamPtr + csData) == VModes[i].viAppleID)) {
1677 csSave->saveMode = ReadMacInt16(ParamPtr + csMode);
1678 csSave->saveData = ReadMacInt32(ParamPtr + csData);
1679 csSave->savePage = ReadMacInt16(ParamPtr + csPage);
1680
1681 // Disable interrupts and pause redraw thread
1682 DisableInterrupt();
1683 thread_stop_ack = false;
1684 thread_stop_req = true;
1685 while (!thread_stop_ack) ;
1686
1687 /* close old display */
1688 close_display();
1689
1690 /* open new display */
1691 cur_mode = i;
1692 bool ok = open_display();
1693
1694 /* opening the screen failed? Then bail out */
1695 if (!ok) {
1696 ErrorAlert(GetString(STR_FULL_SCREEN_ERR));
1697 QuitEmulator();
1698 }
1699
1700 WriteMacInt32(ParamPtr + csBaseAddr, screen_base);
1701 csSave->saveBaseAddr=screen_base;
1702 csSave->saveData=VModes[cur_mode].viAppleID;/* First mode ... */
1703 csSave->saveMode=VModes[cur_mode].viAppleMode;
1704
1705 // Enable interrupts and resume redraw thread
1706 thread_stop_req = false;
1707 EnableInterrupt();
1708 return noErr;
1709 }
1710 }
1711 return paramErr;
1712 }
1713
1714
1715 /*
1716 * Set color palette
1717 */
1718
1719 void video_set_palette(void)
1720 {
1721 LOCK_PALETTE;
1722
1723 // Convert colors to XColor array
1724 int mode = get_current_mode();
1725 int num_in = palette_size(mode);
1726 int num_out = 256;
1727 bool stretch = false;
1728 if (IsDirectMode(mode)) {
1729 // If X is in 565 mode we have to stretch the gamma table from 32 to 64 entries
1730 num_out = vis->map_entries;
1731 stretch = true;
1732 }
1733 XColor *p = x_palette;
1734 for (int i=0; i<num_out; i++) {
1735 int c = (stretch ? (i * num_in) / num_out : i);
1736 p->red = mac_pal[c].red * 0x0101;
1737 p->green = mac_pal[c].green * 0x0101;
1738 p->blue = mac_pal[c].blue * 0x0101;
1739 p++;
1740 }
1741
1742 #ifdef ENABLE_VOSF
1743 // Recalculate pixel color expansion map
1744 if (!IsDirectMode(mode) && xdepth > 8) {
1745 for (int i=0; i<256; i++) {
1746 int c = i & (num_in-1); // If there are less than 256 colors, we repeat the first entries (this makes color expansion easier)
1747 ExpandMap[i] = map_rgb(mac_pal[c].red, mac_pal[c].green, mac_pal[c].blue);
1748 }
1749
1750 // We have to redraw everything because the interpretation of pixel values changed
1751 LOCK_VOSF;
1752 PFLAG_SET_ALL;
1753 UNLOCK_VOSF;
1754 memset(the_buffer_copy, 0, VModes[cur_mode].viRowBytes * VModes[cur_mode].viYsize);
1755 }
1756 #endif
1757
1758 // Tell redraw thread to change palette
1759 palette_changed = true;
1760
1761 UNLOCK_PALETTE;
1762 }
1763
1764
1765 /*
1766 * Can we set the MacOS cursor image into the window?
1767 */
1768
1769 bool video_can_change_cursor(void)
1770 {
1771 return hw_mac_cursor_accl && (display_type != DIS_SCREEN);
1772 }
1773
1774
1775 /*
1776 * Set cursor image for window
1777 */
1778
1779 void video_set_cursor(void)
1780 {
1781 cursor_changed = true;
1782 }
1783
1784
1785 /*
1786 * Thread for window refresh, event handling and other periodic actions
1787 */
1788
1789 static void update_display(void)
1790 {
1791 // Incremental update code
1792 int wide = 0, high = 0, x1, x2, y1, y2, i, j;
1793 int bytes_per_row = VModes[cur_mode].viRowBytes;
1794 int bytes_per_pixel = VModes[cur_mode].viRowBytes / VModes[cur_mode].viXsize;
1795 uint8 *p, *p2;
1796
1797 // Check for first line from top and first line from bottom that have changed
1798 y1 = 0;
1799 for (j=0; j<VModes[cur_mode].viYsize; j++) {
1800 if (memcmp(&the_buffer[j * bytes_per_row], &the_buffer_copy[j * bytes_per_row], bytes_per_row)) {
1801 y1 = j;
1802 break;
1803 }
1804 }
1805 y2 = y1 - 1;
1806 for (j=VModes[cur_mode].viYsize-1; j>=y1; j--) {
1807 if (memcmp(&the_buffer[j * bytes_per_row], &the_buffer_copy[j * bytes_per_row], bytes_per_row)) {
1808 y2 = j;
1809 break;
1810 }
1811 }
1812 high = y2 - y1 + 1;
1813
1814 // Check for first column from left and first column from right that have changed
1815 if (high) {
1816 if (depth == 1) {
1817 x1 = VModes[cur_mode].viXsize;
1818 for (j=y1; j<=y2; j++) {
1819 p = &the_buffer[j * bytes_per_row];
1820 p2 = &the_buffer_copy[j * bytes_per_row];
1821 for (i=0; i<(x1>>3); i++) {
1822 if (*p != *p2) {
1823 x1 = i << 3;
1824 break;
1825 }
1826 p++;
1827 p2++;
1828 }
1829 }
1830 x2 = x1;
1831 for (j=y1; j<=y2; j++) {
1832 p = &the_buffer[j * bytes_per_row];
1833 p2 = &the_buffer_copy[j * bytes_per_row];
1834 p += bytes_per_row;
1835 p2 += bytes_per_row;
1836 for (i=(VModes[cur_mode].viXsize>>3); i>(x2>>3); i--) {
1837 p--;
1838 p2--;
1839 if (*p != *p2) {
1840 x2 = i << 3;
1841 break;
1842 }
1843 }
1844 }
1845 wide = x2 - x1;
1846
1847 // Update copy of the_buffer
1848 if (high && wide) {
1849 for (j=y1; j<=y2; j++) {
1850 i = j * bytes_per_row + (x1 >> 3);
1851 memcpy(&the_buffer_copy[i], &the_buffer[i], wide >> 3);
1852 }
1853 }
1854
1855 } else {
1856 x1 = VModes[cur_mode].viXsize;
1857 for (j=y1; j<=y2; j++) {
1858 p = &the_buffer[j * bytes_per_row];
1859 p2 = &the_buffer_copy[j * bytes_per_row];
1860 for (i=0; i<x1; i++) {
1861 if (memcmp(p, p2, bytes_per_pixel)) {
1862 x1 = i;
1863 break;
1864 }
1865 p += bytes_per_pixel;
1866 p2 += bytes_per_pixel;
1867 }
1868 }
1869 x2 = x1;
1870 for (j=y1; j<=y2; j++) {
1871 p = &the_buffer[j * bytes_per_row];
1872 p2 = &the_buffer_copy[j * bytes_per_row];
1873 p += bytes_per_row;
1874 p2 += bytes_per_row;
1875 for (i=VModes[cur_mode].viXsize; i>x2; i--) {
1876 p -= bytes_per_pixel;
1877 p2 -= bytes_per_pixel;
1878 if (memcmp(p, p2, bytes_per_pixel)) {
1879 x2 = i;
1880 break;
1881 }
1882 }
1883 }
1884 wide = x2 - x1;
1885
1886 // Update copy of the_buffer
1887 if (high && wide) {
1888 for (j=y1; j<=y2; j++) {
1889 i = j * bytes_per_row + x1 * bytes_per_pixel;
1890 memcpy(&the_buffer_copy[i], &the_buffer[i], bytes_per_pixel * wide);
1891 }
1892 }
1893 }
1894 }
1895
1896 // Refresh display
1897 if (high && wide) {
1898 XDisplayLock();
1899 if (have_shm)
1900 XShmPutImage(x_display, the_win, the_gc, img, x1, y1, x1, y1, wide, high, 0);
1901 else
1902 XPutImage(x_display, the_win, the_gc, img, x1, y1, x1, y1, wide, high);
1903 XDisplayUnlock();
1904 }
1905 }
1906
1907 const int VIDEO_REFRESH_HZ = 60;
1908 const int VIDEO_REFRESH_DELAY = 1000000 / VIDEO_REFRESH_HZ;
1909
1910 static void handle_palette_changes(void)
1911 {
1912 LOCK_PALETTE;
1913
1914 if (palette_changed && !emul_suspended) {
1915 palette_changed = false;
1916
1917 int mode = get_current_mode();
1918 if (color_class == PseudoColor || color_class == DirectColor) {
1919 int num = vis->map_entries;
1920 bool set_clut = true;
1921 if (!IsDirectMode(mode) && color_class == DirectColor) {
1922 if (display_type == DIS_WINDOW)
1923 set_clut = false; // Indexed mode on true color screen, don't set CLUT
1924 }
1925
1926 if (set_clut) {
1927 XDisplayLock();
1928 XStoreColors(x_display, cmap[0], x_palette, num);
1929 XStoreColors(x_display, cmap[1], x_palette, num);
1930 XSync(x_display, false);
1931 XDisplayUnlock();
1932 }
1933 }
1934
1935 #ifdef ENABLE_XF86_DGA
1936 if (display_type == DIS_SCREEN) {
1937 current_dga_cmap ^= 1;
1938 if (!IsDirectMode(mode) && cmap[current_dga_cmap])
1939 XF86DGAInstallColormap(x_display, screen, cmap[current_dga_cmap]);
1940 }
1941 #endif
1942 }
1943
1944 UNLOCK_PALETTE;
1945 }
1946
1947 static void *redraw_func(void *arg)
1948 {
1949 int fd = ConnectionNumber(x_display);
1950
1951 uint64 start = GetTicks_usec();
1952 int64 ticks = 0;
1953 uint64 next = GetTicks_usec() + VIDEO_REFRESH_DELAY;
1954
1955 while (!redraw_thread_cancel) {
1956
1957 // Pause if requested (during video mode switches)
1958 while (thread_stop_req)
1959 thread_stop_ack = true;
1960
1961 int64 delay = next - GetTicks_usec();
1962 if (delay < -VIDEO_REFRESH_DELAY) {
1963
1964 // We are lagging far behind, so we reset the delay mechanism
1965 next = GetTicks_usec();
1966
1967 } else if (delay <= 0) {
1968
1969 // Delay expired, refresh display
1970 next += VIDEO_REFRESH_DELAY;
1971 ticks++;
1972
1973 // Handle X11 events
1974 handle_events();
1975
1976 // Quit DGA mode if requested
1977 if (quit_full_screen) {
1978 quit_full_screen = false;
1979 if (display_type == DIS_SCREEN) {
1980 XDisplayLock();
1981 #ifdef ENABLE_XF86_DGA
1982 XF86DGADirectVideo(x_display, screen, 0);
1983 XUngrabPointer(x_display, CurrentTime);
1984 XUngrabKeyboard(x_display, CurrentTime);
1985 XUnmapWindow(x_display, the_win);
1986 wait_unmapped(the_win);
1987 XDestroyWindow(x_display, the_win);
1988 #endif
1989 XSync(x_display, false);
1990 XDisplayUnlock();
1991 quit_full_screen_ack = true;
1992 return NULL;
1993 }
1994 }
1995
1996 // Refresh display and set cursor image in window mode
1997 static int tick_counter = 0;
1998 if (display_type == DIS_WINDOW) {
1999 tick_counter++;
2000 if (tick_counter >= frame_skip) {
2001 tick_counter = 0;
2002
2003 // Update display
2004 #ifdef ENABLE_VOSF
2005 if (use_vosf) {
2006 XDisplayLock();
2007 if (mainBuffer.dirty) {
2008 LOCK_VOSF;
2009 update_display_window_vosf();
2010 UNLOCK_VOSF;
2011 XSync(x_display, false); // Let the server catch up
2012 }
2013 XDisplayUnlock();
2014 }
2015 else
2016 #endif
2017 update_display();
2018
2019 // Set new cursor image if it was changed
2020 if (hw_mac_cursor_accl && cursor_changed) {
2021 cursor_changed = false;
2022 uint8 *x_data = (uint8 *)cursor_image->data;
2023 uint8 *x_mask = (uint8 *)cursor_mask_image->data;
2024 for (int i = 0; i < 32; i++) {
2025 x_mask[i] = MacCursor[4 + i] | MacCursor[36 + i];
2026 x_data[i] = MacCursor[4 + i];
2027 }
2028 XDisplayLock();
2029 XFreeCursor(x_display, mac_cursor);
2030 XPutImage(x_display, cursor_map, cursor_gc, cursor_image, 0, 0, 0, 0, 16, 16);
2031 XPutImage(x_display, cursor_mask_map, cursor_mask_gc, cursor_mask_image, 0, 0, 0, 0, 16, 16);
2032 mac_cursor = XCreatePixmapCursor(x_display, cursor_map, cursor_mask_map, &black, &white, MacCursor[2], MacCursor[3]);
2033 XDefineCursor(x_display, the_win, mac_cursor);
2034 XDisplayUnlock();
2035 }
2036 }
2037 }
2038 #ifdef ENABLE_VOSF
2039 else if (use_vosf) {
2040 // Update display (VOSF variant)
2041 if (++tick_counter >= frame_skip) {
2042 tick_counter = 0;
2043 if (mainBuffer.dirty) {
2044 LOCK_VOSF;
2045 update_display_dga_vosf();
2046 UNLOCK_VOSF;
2047 }
2048 }
2049 }
2050 #endif
2051
2052 // Set new palette if it was changed
2053 handle_palette_changes();
2054
2055 } else {
2056
2057 // No display refresh pending, check for X events
2058 fd_set readfds;
2059 FD_ZERO(&readfds);
2060 FD_SET(fd, &readfds);
2061 struct timeval timeout;
2062 timeout.tv_sec = 0;
2063 timeout.tv_usec = delay;
2064 if (select(fd+1, &readfds, NULL, NULL, &timeout) > 0)
2065 handle_events();
2066 }
2067 }
2068 return NULL;
2069 }