ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/video_x.cpp
Revision: 1.79
Committed: 2005-05-12T11:09:31Z (19 years, 6 months ago) by gbeauche
Branch: MAIN
Changes since 1.78: +44 -21 lines
Log Message:
Fix DGA when visible screen is smaller than virtual display, aka fix
fullscreen mode when VidMode extension is enabled. Also fix SDL fullscreen
to really update the screen as this is necessary by default on Linux since
simple windowed is used (and not DGA for fullscreen).

Always prefer the 64 pixel chunks update code.

Rearrange B2 video_x.cpp to match video_vosf.h updates

File Contents

# Content
1 /*
2 * video_x.cpp - Video/graphics emulation, X11 specific stuff
3 *
4 * Basilisk II (C) 1997-2005 Christian Bauer
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 /*
22 * NOTES:
23 * The Ctrl key works like a qualifier for special actions:
24 * Ctrl-Tab = suspend DGA mode
25 * Ctrl-Esc = emergency quit
26 * Ctrl-F1 = mount floppy
27 * Ctrl-F5 = grab mouse (in windowed mode)
28 */
29
30 #include "sysdeps.h"
31
32 #include <X11/Xlib.h>
33 #include <X11/Xutil.h>
34 #include <X11/keysym.h>
35 #include <X11/extensions/XShm.h>
36 #include <sys/ipc.h>
37 #include <sys/shm.h>
38 #include <errno.h>
39
40 #include <algorithm>
41
42 #ifdef HAVE_PTHREADS
43 # include <pthread.h>
44 #endif
45
46 #ifdef ENABLE_XF86_DGA
47 # include <X11/extensions/xf86dga.h>
48 #endif
49
50 #ifdef ENABLE_XF86_VIDMODE
51 # include <X11/extensions/xf86vmode.h>
52 #endif
53
54 #ifdef ENABLE_FBDEV_DGA
55 # include <sys/mman.h>
56 #endif
57
58 #include "cpu_emulation.h"
59 #include "main.h"
60 #include "adb.h"
61 #include "macos_util.h"
62 #include "prefs.h"
63 #include "user_strings.h"
64 #include "video.h"
65 #include "video_blit.h"
66
67 #define DEBUG 0
68 #include "debug.h"
69
70
71 // Supported video modes
72 static vector<video_mode> VideoModes;
73
74 // Display types
75 enum {
76 DISPLAY_WINDOW, // X11 window, using MIT SHM extensions if possible
77 DISPLAY_DGA // DGA fullscreen display
78 };
79
80 // Constants
81 const char KEYCODE_FILE_NAME[] = DATADIR "/keycodes";
82
83 static const int win_eventmask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | EnterWindowMask | ExposureMask | StructureNotifyMask;
84 static const int dga_eventmask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | StructureNotifyMask;
85
86
87 // Global variables
88 static int32 frame_skip; // Prefs items
89 static int16 mouse_wheel_mode;
90 static int16 mouse_wheel_lines;
91
92 static int display_type = DISPLAY_WINDOW; // See enum above
93 static bool local_X11; // Flag: X server running on local machine?
94 static uint8 *the_buffer = NULL; // Mac frame buffer (where MacOS draws into)
95 static uint8 *the_buffer_copy = NULL; // Copy of Mac frame buffer (for refreshed modes)
96 static uint32 the_buffer_size; // Size of allocated the_buffer
97
98 static bool redraw_thread_active = false; // Flag: Redraw thread installed
99 #ifdef HAVE_PTHREADS
100 static pthread_attr_t redraw_thread_attr; // Redraw thread attributes
101 static volatile bool redraw_thread_cancel; // Flag: Cancel Redraw thread
102 static volatile bool redraw_thread_cancel_ack; // Flag: Acknowledge for redraw thread cancellation
103 static pthread_t redraw_thread; // Redraw thread
104 #endif
105
106 static bool has_dga = false; // Flag: Video DGA capable
107 static bool has_vidmode = false; // Flag: VidMode extension available
108
109 #ifdef ENABLE_VOSF
110 static bool use_vosf = true; // Flag: VOSF enabled
111 #else
112 static const bool use_vosf = false; // VOSF not possible
113 #endif
114
115 static bool ctrl_down = false; // Flag: Ctrl key pressed
116 static bool caps_on = false; // Flag: Caps Lock on
117 static bool quit_full_screen = false; // Flag: DGA close requested from redraw thread
118 static bool emerg_quit = false; // Flag: Ctrl-Esc pressed, emergency quit requested from MacOS thread
119 static bool emul_suspended = false; // Flag: Emulator suspended
120
121 static bool classic_mode = false; // Flag: Classic Mac video mode
122
123 static bool use_keycodes = false; // Flag: Use keycodes rather than keysyms
124 static int keycode_table[256]; // X keycode -> Mac keycode translation table
125
126 // X11 variables
127 char *x_display_name = NULL; // X11 display name
128 Display *x_display = NULL; // X11 display handle
129 static int screen; // Screen number
130 static Window rootwin; // Root window and our window
131 static int num_depths = 0; // Number of available X depths
132 static int *avail_depths = NULL; // List of available X depths
133 static XColor black, white;
134 static unsigned long black_pixel, white_pixel;
135 static int eventmask;
136
137 static int xdepth; // Depth of X screen
138 static VisualFormat visualFormat;
139 static XVisualInfo visualInfo;
140 static Visual *vis;
141 static int color_class;
142
143 static int rshift, rloss, gshift, gloss, bshift, bloss; // Pixel format of DirectColor/TrueColor modes
144
145 static Colormap cmap[2] = {0, 0}; // Colormaps for indexed modes (DGA needs two of them)
146
147 static XColor x_palette[256]; // Color palette to be used as CLUT and gamma table
148 static bool x_palette_changed = false; // Flag: Palette changed, redraw thread must set new colors
149
150 #ifdef ENABLE_FBDEV_DGA
151 static int fbdev_fd = -1;
152 #endif
153
154 #ifdef ENABLE_XF86_VIDMODE
155 static XF86VidModeModeInfo **x_video_modes = NULL; // Array of all available modes
156 static int num_x_video_modes;
157 #endif
158
159 // Mutex to protect palette
160 #ifdef HAVE_PTHREADS
161 static pthread_mutex_t x_palette_lock = PTHREAD_MUTEX_INITIALIZER;
162 #define LOCK_PALETTE pthread_mutex_lock(&x_palette_lock)
163 #define UNLOCK_PALETTE pthread_mutex_unlock(&x_palette_lock)
164 #else
165 #define LOCK_PALETTE
166 #define UNLOCK_PALETTE
167 #endif
168
169 // Mutex to protect frame buffer
170 #ifdef HAVE_PTHREADS
171 static pthread_mutex_t frame_buffer_lock = PTHREAD_MUTEX_INITIALIZER;
172 #define LOCK_FRAME_BUFFER pthread_mutex_lock(&frame_buffer_lock);
173 #define UNLOCK_FRAME_BUFFER pthread_mutex_unlock(&frame_buffer_lock);
174 #else
175 #define LOCK_FRAME_BUFFER
176 #define UNLOCK_FRAME_BUFFER
177 #endif
178
179 // Variables for non-VOSF incremental refresh
180 static const int sm_uptd[] = {4,1,6,3,0,5,2,7};
181 static int sm_no_boxes[] = {1,8,32,64,128,300};
182 static bool updt_box[17][17];
183 static int nr_boxes;
184
185 // Video refresh function
186 static void VideoRefreshInit(void);
187 static void (*video_refresh)(void);
188
189
190 // Prototypes
191 static void *redraw_func(void *arg);
192
193 // From main_unix.cpp
194 extern char *x_display_name;
195 extern Display *x_display;
196 extern void *vm_acquire_mac(size_t size);
197
198 // From sys_unix.cpp
199 extern void SysMountFirstFloppy(void);
200
201 // From clip_unix.cpp
202 extern void ClipboardSelectionClear(XSelectionClearEvent *);
203 extern void ClipboardSelectionRequest(XSelectionRequestEvent *);
204
205
206 /*
207 * monitor_desc subclass for X11 display
208 */
209
210 class X11_monitor_desc : public monitor_desc {
211 public:
212 X11_monitor_desc(const vector<video_mode> &available_modes, video_depth default_depth, uint32 default_id) : monitor_desc(available_modes, default_depth, default_id) {}
213 ~X11_monitor_desc() {}
214
215 virtual void switch_to_current_mode(void);
216 virtual void set_palette(uint8 *pal, int num);
217
218 bool video_open(void);
219 void video_close(void);
220 };
221
222
223 /*
224 * Utility functions
225 */
226
227 // Map video_mode depth ID to numerical depth value
228 static inline int depth_of_video_mode(video_mode const & mode)
229 {
230 int depth = -1;
231 switch (mode.depth) {
232 case VDEPTH_1BIT:
233 depth = 1;
234 break;
235 case VDEPTH_2BIT:
236 depth = 2;
237 break;
238 case VDEPTH_4BIT:
239 depth = 4;
240 break;
241 case VDEPTH_8BIT:
242 depth = 8;
243 break;
244 case VDEPTH_16BIT:
245 depth = 16;
246 break;
247 case VDEPTH_32BIT:
248 depth = 32;
249 break;
250 default:
251 abort();
252 }
253 return depth;
254 }
255
256 // Map RGB color to pixel value (this only works in TrueColor/DirectColor visuals)
257 static inline uint32 map_rgb(uint8 red, uint8 green, uint8 blue)
258 {
259 return ((red >> rloss) << rshift) | ((green >> gloss) << gshift) | ((blue >> bloss) << bshift);
260 }
261
262 // Do we have a visual for handling the specified Mac depth? If so, set the
263 // global variables "xdepth", "visualInfo", "vis" and "color_class".
264 static bool find_visual_for_depth(video_depth depth)
265 {
266 D(bug("have_visual_for_depth(%d)\n", 1 << depth));
267
268 // 1-bit works always and uses default visual
269 if (depth == VDEPTH_1BIT) {
270 vis = DefaultVisual(x_display, screen);
271 visualInfo.visualid = XVisualIDFromVisual(vis);
272 int num = 0;
273 XVisualInfo *vi = XGetVisualInfo(x_display, VisualIDMask, &visualInfo, &num);
274 visualInfo = vi[0];
275 XFree(vi);
276 xdepth = visualInfo.depth;
277 color_class = visualInfo.c_class;
278 D(bug(" found visual ID 0x%02x, depth %d\n", visualInfo.visualid, xdepth));
279 return true;
280 }
281
282 // Calculate minimum and maximum supported X depth
283 int min_depth = 1, max_depth = 32;
284 switch (depth) {
285 #ifdef ENABLE_VOSF
286 case VDEPTH_2BIT:
287 case VDEPTH_4BIT: // VOSF blitters can convert 2/4/8-bit -> 8/16/32-bit
288 case VDEPTH_8BIT:
289 min_depth = 8;
290 max_depth = 32;
291 break;
292 #else
293 case VDEPTH_2BIT:
294 case VDEPTH_4BIT: // 2/4-bit requires VOSF blitters
295 return false;
296 case VDEPTH_8BIT: // 8-bit without VOSF requires an 8-bit visual
297 min_depth = 8;
298 max_depth = 8;
299 break;
300 #endif
301 case VDEPTH_16BIT: // 16-bit requires a 15/16-bit visual
302 min_depth = 15;
303 max_depth = 16;
304 break;
305 case VDEPTH_32BIT: // 32-bit requires a 24/32-bit visual
306 min_depth = 24;
307 max_depth = 32;
308 break;
309 }
310 D(bug(" minimum required X depth is %d, maximum supported X depth is %d\n", min_depth, max_depth));
311
312 // Try to find a visual for one of the color depths
313 bool visual_found = false;
314 for (int i=0; i<num_depths && !visual_found; i++) {
315
316 xdepth = avail_depths[i];
317 D(bug(" trying to find visual for depth %d\n", xdepth));
318 if (xdepth < min_depth || xdepth > max_depth)
319 continue;
320
321 // Determine best color class for this depth
322 switch (xdepth) {
323 case 1: // Try StaticGray or StaticColor
324 if (XMatchVisualInfo(x_display, screen, xdepth, StaticGray, &visualInfo)
325 || XMatchVisualInfo(x_display, screen, xdepth, StaticColor, &visualInfo))
326 visual_found = true;
327 break;
328 case 8: // Need PseudoColor
329 if (XMatchVisualInfo(x_display, screen, xdepth, PseudoColor, &visualInfo))
330 visual_found = true;
331 break;
332 case 15:
333 case 16:
334 case 24:
335 case 32: // Try DirectColor first, as this will allow gamma correction
336 if (XMatchVisualInfo(x_display, screen, xdepth, DirectColor, &visualInfo)
337 || XMatchVisualInfo(x_display, screen, xdepth, TrueColor, &visualInfo))
338 visual_found = true;
339 break;
340 default:
341 D(bug(" not a supported depth\n"));
342 break;
343 }
344 }
345 if (!visual_found)
346 return false;
347
348 // Visual was found
349 vis = visualInfo.visual;
350 color_class = visualInfo.c_class;
351 D(bug(" found visual ID 0x%02x, depth %d, class ", visualInfo.visualid, xdepth));
352 #if DEBUG
353 switch (color_class) {
354 case StaticGray: D(bug("StaticGray\n")); break;
355 case GrayScale: D(bug("GrayScale\n")); break;
356 case StaticColor: D(bug("StaticColor\n")); break;
357 case PseudoColor: D(bug("PseudoColor\n")); break;
358 case TrueColor: D(bug("TrueColor\n")); break;
359 case DirectColor: D(bug("DirectColor\n")); break;
360 }
361 #endif
362 return true;
363 }
364
365 // Add mode to list of supported modes
366 static void add_mode(uint32 width, uint32 height, uint32 resolution_id, uint32 bytes_per_row, video_depth depth)
367 {
368 video_mode mode;
369 mode.x = width;
370 mode.y = height;
371 mode.resolution_id = resolution_id;
372 mode.bytes_per_row = bytes_per_row;
373 mode.depth = depth;
374 VideoModes.push_back(mode);
375 }
376
377 // Add standard list of windowed modes for given color depth
378 static void add_window_modes(video_depth depth)
379 {
380 add_mode(512, 384, 0x80, TrivialBytesPerRow(512, depth), depth);
381 add_mode(640, 480, 0x81, TrivialBytesPerRow(640, depth), depth);
382 add_mode(800, 600, 0x82, TrivialBytesPerRow(800, depth), depth);
383 add_mode(1024, 768, 0x83, TrivialBytesPerRow(1024, depth), depth);
384 add_mode(1152, 870, 0x84, TrivialBytesPerRow(1152, depth), depth);
385 add_mode(1280, 1024, 0x85, TrivialBytesPerRow(1280, depth), depth);
386 add_mode(1600, 1200, 0x86, TrivialBytesPerRow(1600, depth), depth);
387 }
388
389 // Set Mac frame layout and base address (uses the_buffer/MacFrameBaseMac)
390 static void set_mac_frame_buffer(X11_monitor_desc &monitor, video_depth depth, bool native_byte_order)
391 {
392 #if !REAL_ADDRESSING && !DIRECT_ADDRESSING
393 int layout = FLAYOUT_DIRECT;
394 if (depth == VDEPTH_16BIT)
395 layout = (xdepth == 15) ? FLAYOUT_HOST_555 : FLAYOUT_HOST_565;
396 else if (depth == VDEPTH_32BIT)
397 layout = (xdepth == 24) ? FLAYOUT_HOST_888 : FLAYOUT_DIRECT;
398 if (native_byte_order)
399 MacFrameLayout = layout;
400 else
401 MacFrameLayout = FLAYOUT_DIRECT;
402 monitor.set_mac_frame_base(MacFrameBaseMac);
403
404 // Set variables used by UAE memory banking
405 const video_mode &mode = monitor.get_current_mode();
406 MacFrameBaseHost = the_buffer;
407 MacFrameSize = mode.bytes_per_row * mode.y;
408 InitFrameBufferMapping();
409 #else
410 monitor.set_mac_frame_base(Host2MacAddr(the_buffer));
411 #endif
412 D(bug("monitor.mac_frame_base = %08x\n", monitor.get_mac_frame_base()));
413 }
414
415 // Set window name and class
416 static void set_window_name(Window w, int name)
417 {
418 const char *str = GetString(name);
419 XStoreName(x_display, w, str);
420 XSetIconName(x_display, w, str);
421
422 XClassHint *hints;
423 hints = XAllocClassHint();
424 if (hints) {
425 hints->res_name = "BasiliskII";
426 hints->res_class = "BasiliskII";
427 XSetClassHint(x_display, w, hints);
428 XFree(hints);
429 }
430 }
431
432 // Set window input focus flag
433 static void set_window_focus(Window w)
434 {
435 XWMHints *hints = XAllocWMHints();
436 if (hints) {
437 hints->input = True;
438 hints->initial_state = NormalState;
439 hints->flags = InputHint | StateHint;
440 XSetWMHints(x_display, w, hints);
441 XFree(hints);
442 }
443 }
444
445 // Set WM_DELETE_WINDOW protocol on window (preventing it from being destroyed by the WM when clicking on the "close" widget)
446 static Atom WM_DELETE_WINDOW = (Atom)0;
447 static void set_window_delete_protocol(Window w)
448 {
449 WM_DELETE_WINDOW = XInternAtom(x_display, "WM_DELETE_WINDOW", false);
450 XSetWMProtocols(x_display, w, &WM_DELETE_WINDOW, 1);
451 }
452
453 // Wait until window is mapped/unmapped
454 void wait_mapped(Window w)
455 {
456 XEvent e;
457 do {
458 XMaskEvent(x_display, StructureNotifyMask, &e);
459 } while ((e.type != MapNotify) || (e.xmap.event != w));
460 }
461
462 void wait_unmapped(Window w)
463 {
464 XEvent e;
465 do {
466 XMaskEvent(x_display, StructureNotifyMask, &e);
467 } while ((e.type != UnmapNotify) || (e.xmap.event != w));
468 }
469
470 // Trap SHM errors
471 static bool shm_error = false;
472 static int (*old_error_handler)(Display *, XErrorEvent *);
473
474 static int error_handler(Display *d, XErrorEvent *e)
475 {
476 if (e->error_code == BadAccess) {
477 shm_error = true;
478 return 0;
479 } else
480 return old_error_handler(d, e);
481 }
482
483
484 /*
485 * Display "driver" classes
486 */
487
488 class driver_base {
489 public:
490 driver_base(X11_monitor_desc &m);
491 virtual ~driver_base();
492
493 virtual void update_palette(void);
494 virtual void suspend(void) {}
495 virtual void resume(void) {}
496 virtual void toggle_mouse_grab(void) {}
497 virtual void mouse_moved(int x, int y) { ADBMouseMoved(x, y); }
498
499 void disable_mouse_accel(void);
500 void restore_mouse_accel(void);
501
502 virtual void grab_mouse(void) {}
503 virtual void ungrab_mouse(void) {}
504
505 public:
506 X11_monitor_desc &monitor; // Associated video monitor
507 const video_mode &mode; // Video mode handled by the driver
508
509 bool init_ok; // Initialization succeeded (we can't use exceptions because of -fomit-frame-pointer)
510 Window w; // The window we draw into
511
512 int orig_accel_numer, orig_accel_denom, orig_threshold; // Original mouse acceleration
513 };
514
515 class driver_window;
516 static void update_display_window_vosf(driver_window *drv);
517 static void update_display_dynamic(int ticker, driver_window *drv);
518 static void update_display_static(driver_window *drv);
519
520 class driver_window : public driver_base {
521 friend void update_display_window_vosf(driver_window *drv);
522 friend void update_display_dynamic(int ticker, driver_window *drv);
523 friend void update_display_static(driver_window *drv);
524
525 public:
526 driver_window(X11_monitor_desc &monitor);
527 ~driver_window();
528
529 void toggle_mouse_grab(void);
530 void mouse_moved(int x, int y);
531
532 void grab_mouse(void);
533 void ungrab_mouse(void);
534
535 private:
536 GC gc;
537 XImage *img;
538 bool have_shm; // Flag: SHM extensions available
539 XShmSegmentInfo shminfo;
540 Cursor mac_cursor;
541 bool mouse_grabbed; // Flag: mouse pointer grabbed, using relative mouse mode
542 int mouse_last_x, mouse_last_y; // Last mouse position (for relative mode)
543 };
544
545 class driver_dga;
546 static void update_display_dga_vosf(driver_dga *drv);
547
548 class driver_dga : public driver_base {
549 friend void update_display_dga_vosf(driver_dga *drv);
550
551 public:
552 driver_dga(X11_monitor_desc &monitor);
553 ~driver_dga();
554
555 void suspend(void);
556 void resume(void);
557
558 protected:
559 struct FakeXImage {
560 int width, height; // size of image
561 int depth; // depth of image
562 int bytes_per_line; // accelerator to next line
563
564 FakeXImage(int w, int h, int d)
565 : width(w), height(h), depth(d)
566 { bytes_per_line = TrivialBytesPerRow(width, DepthModeForPixelDepth(depth)); }
567 };
568 FakeXImage *img;
569
570 private:
571 Window suspend_win; // "Suspend" information window
572 void *fb_save; // Saved frame buffer for suspend/resume
573 };
574
575 static driver_base *drv = NULL; // Pointer to currently used driver object
576
577 #ifdef ENABLE_VOSF
578 # include "video_vosf.h"
579 #endif
580
581 driver_base::driver_base(X11_monitor_desc &m)
582 : monitor(m), mode(m.get_current_mode()), init_ok(false), w(0)
583 {
584 the_buffer = NULL;
585 the_buffer_copy = NULL;
586 XGetPointerControl(x_display, &orig_accel_numer, &orig_accel_denom, &orig_threshold);
587 }
588
589 driver_base::~driver_base()
590 {
591 ungrab_mouse();
592 restore_mouse_accel();
593
594 if (w) {
595 XUnmapWindow(x_display, w);
596 wait_unmapped(w);
597 XDestroyWindow(x_display, w);
598 }
599
600 XFlush(x_display);
601 XSync(x_display, false);
602
603 // Free frame buffer(s)
604 if (!use_vosf) {
605 if (the_buffer) {
606 free(the_buffer);
607 the_buffer = NULL;
608 }
609 if (the_buffer_copy) {
610 free(the_buffer_copy);
611 the_buffer_copy = NULL;
612 }
613 }
614 #ifdef ENABLE_VOSF
615 else {
616 // the_buffer shall always be mapped through vm_acquire() so that we can vm_protect() it at will
617 if (the_buffer != VM_MAP_FAILED) {
618 D(bug(" releasing the_buffer at %p (%d bytes)\n", the_buffer, the_buffer_size));
619 vm_release(the_buffer, the_buffer_size);
620 the_buffer = NULL;
621 }
622 if (the_host_buffer) {
623 D(bug(" freeing the_host_buffer at %p\n", the_host_buffer));
624 free(the_host_buffer);
625 the_host_buffer = NULL;
626 }
627 if (the_buffer_copy) {
628 D(bug(" freeing the_buffer_copy at %p\n", the_buffer_copy));
629 free(the_buffer_copy);
630 the_buffer_copy = NULL;
631 }
632 }
633 #endif
634 }
635
636 // Palette has changed
637 void driver_base::update_palette(void)
638 {
639 if (color_class == PseudoColor || color_class == DirectColor) {
640 int num = vis->map_entries;
641 if (!IsDirectMode(monitor.get_current_mode()) && color_class == DirectColor)
642 return; // Indexed mode on true color screen, don't set CLUT
643 XStoreColors(x_display, cmap[0], x_palette, num);
644 XStoreColors(x_display, cmap[1], x_palette, num);
645 }
646 XSync(x_display, false);
647 }
648
649 // Disable mouse acceleration
650 void driver_base::disable_mouse_accel(void)
651 {
652 XChangePointerControl(x_display, True, False, 1, 1, 0);
653 }
654
655 // Restore mouse acceleration to original value
656 void driver_base::restore_mouse_accel(void)
657 {
658 XChangePointerControl(x_display, True, True, orig_accel_numer, orig_accel_denom, orig_threshold);
659 }
660
661
662 /*
663 * Windowed display driver
664 */
665
666 // Open display
667 driver_window::driver_window(X11_monitor_desc &m)
668 : driver_base(m), gc(0), img(NULL), have_shm(false), mac_cursor(0), mouse_grabbed(false)
669 {
670 int width = mode.x, height = mode.y;
671 int aligned_width = (width + 15) & ~15;
672 int aligned_height = (height + 15) & ~15;
673
674 // Set absolute mouse mode
675 ADBSetRelMouseMode(mouse_grabbed);
676
677 // Create window (setting background_pixel, border_pixel and colormap is
678 // mandatory when using a non-default visual; in 1-bit mode we use the
679 // default visual, so we can also use the default colormap)
680 XSetWindowAttributes wattr;
681 wattr.event_mask = eventmask = win_eventmask;
682 wattr.background_pixel = (vis == DefaultVisual(x_display, screen) ? black_pixel : 0);
683 wattr.border_pixel = 0;
684 wattr.colormap = (mode.depth == VDEPTH_1BIT ? DefaultColormap(x_display, screen) : cmap[0]);
685 w = XCreateWindow(x_display, rootwin, 0, 0, width, height, 0, xdepth,
686 InputOutput, vis, CWEventMask | CWBackPixel | CWBorderPixel | CWColormap, &wattr);
687 D(bug(" window created\n"));
688
689 // Set window name/class
690 set_window_name(w, STR_WINDOW_TITLE);
691
692 // Indicate that we want keyboard input
693 set_window_focus(w);
694
695 // Set delete protocol property
696 set_window_delete_protocol(w);
697
698 // Make window unresizable
699 {
700 XSizeHints *hints = XAllocSizeHints();
701 if (hints) {
702 hints->min_width = width;
703 hints->max_width = width;
704 hints->min_height = height;
705 hints->max_height = height;
706 hints->flags = PMinSize | PMaxSize;
707 XSetWMNormalHints(x_display, w, hints);
708 XFree(hints);
709 }
710 }
711 D(bug(" window attributes set\n"));
712
713 // Show window
714 XMapWindow(x_display, w);
715 wait_mapped(w);
716 D(bug(" window mapped\n"));
717
718 // 1-bit mode is big-endian; if the X server is little-endian, we can't
719 // use SHM because that doesn't allow changing the image byte order
720 bool need_msb_image = (mode.depth == VDEPTH_1BIT && XImageByteOrder(x_display) == LSBFirst);
721
722 // Try to create and attach SHM image
723 if (local_X11 && !need_msb_image && XShmQueryExtension(x_display)) {
724
725 // Create SHM image ("height + 2" for safety)
726 img = XShmCreateImage(x_display, vis, mode.depth == VDEPTH_1BIT ? 1 : xdepth, mode.depth == VDEPTH_1BIT ? XYBitmap : ZPixmap, 0, &shminfo, width, height);
727 D(bug(" shm image created\n"));
728 shminfo.shmid = shmget(IPC_PRIVATE, (aligned_height + 2) * img->bytes_per_line, IPC_CREAT | 0777);
729 the_buffer_copy = (uint8 *)shmat(shminfo.shmid, 0, 0);
730 shminfo.shmaddr = img->data = (char *)the_buffer_copy;
731 shminfo.readOnly = False;
732
733 // Try to attach SHM image, catching errors
734 shm_error = false;
735 old_error_handler = XSetErrorHandler(error_handler);
736 XShmAttach(x_display, &shminfo);
737 XSync(x_display, false);
738 XSetErrorHandler(old_error_handler);
739 if (shm_error) {
740 shmdt(shminfo.shmaddr);
741 XDestroyImage(img);
742 img = NULL;
743 shminfo.shmid = -1;
744 } else {
745 have_shm = true;
746 shmctl(shminfo.shmid, IPC_RMID, 0);
747 }
748 D(bug(" shm image attached\n"));
749 }
750
751 // Create normal X image if SHM doesn't work ("height + 2" for safety)
752 if (!have_shm) {
753 int bytes_per_row = (mode.depth == VDEPTH_1BIT ? aligned_width/8 : TrivialBytesPerRow(aligned_width, DepthModeForPixelDepth(xdepth)));
754 the_buffer_copy = (uint8 *)malloc((aligned_height + 2) * bytes_per_row);
755 img = XCreateImage(x_display, vis, mode.depth == VDEPTH_1BIT ? 1 : xdepth, mode.depth == VDEPTH_1BIT ? XYBitmap : ZPixmap, 0, (char *)the_buffer_copy, aligned_width, aligned_height, 32, bytes_per_row);
756 D(bug(" X image created\n"));
757 }
758
759 if (need_msb_image) {
760 img->byte_order = MSBFirst;
761 img->bitmap_bit_order = MSBFirst;
762 }
763
764 #ifdef ENABLE_VOSF
765 use_vosf = true;
766 // Allocate memory for frame buffer (SIZE is extended to page-boundary)
767 the_host_buffer = the_buffer_copy;
768 the_buffer_size = page_extend((aligned_height + 2) * img->bytes_per_line);
769 the_buffer = (uint8 *)vm_acquire_mac(the_buffer_size);
770 the_buffer_copy = (uint8 *)malloc(the_buffer_size);
771 D(bug("the_buffer = %p, the_buffer_copy = %p, the_host_buffer = %p\n", the_buffer, the_buffer_copy, the_host_buffer));
772 #else
773 // Allocate memory for frame buffer
774 the_buffer = (uint8 *)malloc((aligned_height + 2) * img->bytes_per_line);
775 D(bug("the_buffer = %p, the_buffer_copy = %p\n", the_buffer, the_buffer_copy));
776 #endif
777
778 // Create GC
779 gc = XCreateGC(x_display, w, 0, 0);
780 XSetState(x_display, gc, black_pixel, white_pixel, GXcopy, AllPlanes);
781
782 // Create no_cursor
783 mac_cursor = XCreatePixmapCursor(x_display,
784 XCreatePixmap(x_display, w, 1, 1, 1),
785 XCreatePixmap(x_display, w, 1, 1, 1),
786 &black, &white, 0, 0);
787 XDefineCursor(x_display, w, mac_cursor);
788
789 // Init blitting routines
790 bool native_byte_order;
791 #ifdef WORDS_BIGENDIAN
792 native_byte_order = (XImageByteOrder(x_display) == MSBFirst);
793 #else
794 native_byte_order = (XImageByteOrder(x_display) == LSBFirst);
795 #endif
796 #ifdef ENABLE_VOSF
797 Screen_blitter_init(visualFormat, native_byte_order, depth_of_video_mode(mode));
798 #endif
799
800 // Set frame buffer base
801 set_mac_frame_buffer(monitor, mode.depth, native_byte_order);
802
803 // Everything went well
804 init_ok = true;
805 }
806
807 // Close display
808 driver_window::~driver_window()
809 {
810 if (have_shm) {
811 XShmDetach(x_display, &shminfo);
812 #ifdef ENABLE_VOSF
813 the_host_buffer = NULL; // don't free() in driver_base dtor
814 #else
815 the_buffer_copy = NULL; // don't free() in driver_base dtor
816 #endif
817 }
818 if (img) {
819 if (!have_shm)
820 img->data = NULL;
821 XDestroyImage(img);
822 }
823 if (have_shm) {
824 shmdt(shminfo.shmaddr);
825 shmctl(shminfo.shmid, IPC_RMID, 0);
826 }
827 if (gc)
828 XFreeGC(x_display, gc);
829 }
830
831 // Toggle mouse grab
832 void driver_window::toggle_mouse_grab(void)
833 {
834 if (mouse_grabbed)
835 ungrab_mouse();
836 else
837 grab_mouse();
838 }
839
840 // Grab mouse, switch to relative mouse mode
841 void driver_window::grab_mouse(void)
842 {
843 int result;
844 for (int i=0; i<10; i++) {
845 result = XGrabPointer(x_display, w, True, 0,
846 GrabModeAsync, GrabModeAsync, w, None, CurrentTime);
847 if (result != AlreadyGrabbed)
848 break;
849 Delay_usec(100000);
850 }
851 if (result == GrabSuccess) {
852 XStoreName(x_display, w, GetString(STR_WINDOW_TITLE_GRABBED));
853 ADBSetRelMouseMode(mouse_grabbed = true);
854 disable_mouse_accel();
855 }
856 }
857
858 // Ungrab mouse, switch to absolute mouse mode
859 void driver_window::ungrab_mouse(void)
860 {
861 if (mouse_grabbed) {
862 XUngrabPointer(x_display, CurrentTime);
863 XStoreName(x_display, w, GetString(STR_WINDOW_TITLE));
864 ADBSetRelMouseMode(mouse_grabbed = false);
865 restore_mouse_accel();
866 }
867 }
868
869 // Mouse moved
870 void driver_window::mouse_moved(int x, int y)
871 {
872 if (!mouse_grabbed) {
873 mouse_last_x = x; mouse_last_y = y;
874 ADBMouseMoved(x, y);
875 return;
876 }
877
878 // Warped mouse motion (this code is taken from SDL)
879
880 // Post first mouse event
881 int width = monitor.get_current_mode().x, height = monitor.get_current_mode().y;
882 int delta_x = x - mouse_last_x, delta_y = y - mouse_last_y;
883 mouse_last_x = x; mouse_last_y = y;
884 ADBMouseMoved(delta_x, delta_y);
885
886 // Only warp the pointer when it has reached the edge
887 const int MOUSE_FUDGE_FACTOR = 8;
888 if (x < MOUSE_FUDGE_FACTOR || x > (width - MOUSE_FUDGE_FACTOR)
889 || y < MOUSE_FUDGE_FACTOR || y > (height - MOUSE_FUDGE_FACTOR)) {
890 XEvent event;
891 while (XCheckTypedEvent(x_display, MotionNotify, &event)) {
892 delta_x = x - mouse_last_x; delta_y = y - mouse_last_y;
893 mouse_last_x = x; mouse_last_y = y;
894 ADBMouseMoved(delta_x, delta_y);
895 }
896 mouse_last_x = width/2;
897 mouse_last_y = height/2;
898 XWarpPointer(x_display, None, w, 0, 0, 0, 0, mouse_last_x, mouse_last_y);
899 for (int i=0; i<10; i++) {
900 XMaskEvent(x_display, PointerMotionMask, &event);
901 if (event.xmotion.x > (mouse_last_x - MOUSE_FUDGE_FACTOR)
902 && event.xmotion.x < (mouse_last_x + MOUSE_FUDGE_FACTOR)
903 && event.xmotion.y > (mouse_last_y - MOUSE_FUDGE_FACTOR)
904 && event.xmotion.y < (mouse_last_y + MOUSE_FUDGE_FACTOR))
905 break;
906 }
907 }
908 }
909
910
911 #if defined(ENABLE_XF86_DGA) || defined(ENABLE_FBDEV_DGA)
912 /*
913 * DGA display driver base class
914 */
915
916 driver_dga::driver_dga(X11_monitor_desc &m)
917 : driver_base(m), suspend_win(0), fb_save(NULL), img(NULL)
918 {
919 }
920
921 driver_dga::~driver_dga()
922 {
923 XUngrabPointer(x_display, CurrentTime);
924 XUngrabKeyboard(x_display, CurrentTime);
925
926 if (img)
927 delete img;
928 }
929
930 // Suspend emulation
931 void driver_dga::suspend(void)
932 {
933 // Release ctrl key
934 ADBKeyUp(0x36);
935 ctrl_down = false;
936
937 // Lock frame buffer (this will stop the MacOS thread)
938 LOCK_FRAME_BUFFER;
939
940 // Save frame buffer
941 fb_save = malloc(mode.y * mode.bytes_per_row);
942 if (fb_save)
943 memcpy(fb_save, the_buffer, mode.y * mode.bytes_per_row);
944
945 // Close full screen display
946 #ifdef ENABLE_XF86_DGA
947 XF86DGADirectVideo(x_display, screen, 0);
948 #endif
949 XUngrabPointer(x_display, CurrentTime);
950 XUngrabKeyboard(x_display, CurrentTime);
951 restore_mouse_accel();
952 XUnmapWindow(x_display, w);
953 wait_unmapped(w);
954
955 // Open "suspend" window
956 XSetWindowAttributes wattr;
957 wattr.event_mask = KeyPressMask;
958 wattr.background_pixel = black_pixel;
959
960 suspend_win = XCreateWindow(x_display, rootwin, 0, 0, 512, 1, 0, xdepth,
961 InputOutput, vis, CWEventMask | CWBackPixel, &wattr);
962 set_window_name(suspend_win, STR_SUSPEND_WINDOW_TITLE);
963 set_window_focus(suspend_win);
964 XMapWindow(x_display, suspend_win);
965 emul_suspended = true;
966 }
967
968 // Resume emulation
969 void driver_dga::resume(void)
970 {
971 // Close "suspend" window
972 XDestroyWindow(x_display, suspend_win);
973 XSync(x_display, false);
974
975 // Reopen full screen display
976 XMapRaised(x_display, w);
977 wait_mapped(w);
978 XWarpPointer(x_display, None, rootwin, 0, 0, 0, 0, 0, 0);
979 XGrabKeyboard(x_display, rootwin, True, GrabModeAsync, GrabModeAsync, CurrentTime);
980 XGrabPointer(x_display, rootwin, True, PointerMotionMask | ButtonPressMask | ButtonReleaseMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
981 disable_mouse_accel();
982 #ifdef ENABLE_XF86_DGA
983 XF86DGADirectVideo(x_display, screen, XF86DGADirectGraphics | XF86DGADirectKeyb | XF86DGADirectMouse);
984 XF86DGASetViewPort(x_display, screen, 0, 0);
985 #endif
986 XSync(x_display, false);
987
988 // the_buffer already contains the data to restore. i.e. since a temporary
989 // frame buffer is used when VOSF is actually used, fb_save is therefore
990 // not necessary.
991 #ifdef ENABLE_VOSF
992 if (use_vosf) {
993 LOCK_VOSF;
994 PFLAG_SET_ALL;
995 UNLOCK_VOSF;
996 memset(the_buffer_copy, 0, mode.bytes_per_row * mode.y);
997 }
998 #endif
999
1000 // Restore frame buffer
1001 if (fb_save) {
1002 #ifdef ENABLE_VOSF
1003 // Don't copy fb_save to the temporary frame buffer in VOSF mode
1004 if (!use_vosf)
1005 #endif
1006 memcpy(the_buffer, fb_save, mode.y * mode.bytes_per_row);
1007 free(fb_save);
1008 fb_save = NULL;
1009 }
1010
1011 // Unlock frame buffer (and continue MacOS thread)
1012 UNLOCK_FRAME_BUFFER;
1013 emul_suspended = false;
1014 }
1015 #endif
1016
1017
1018 #ifdef ENABLE_FBDEV_DGA
1019 /*
1020 * fbdev DGA display driver
1021 */
1022
1023 const char FBDEVICES_FILE_NAME[] = DATADIR "/fbdevices";
1024 const char FBDEVICE_FILE_NAME[] = "/dev/fb";
1025
1026 class driver_fbdev : public driver_dga {
1027 public:
1028 driver_fbdev(X11_monitor_desc &monitor);
1029 ~driver_fbdev();
1030 };
1031
1032 // Open display
1033 driver_fbdev::driver_fbdev(X11_monitor_desc &m) : driver_dga(m)
1034 {
1035 int width = mode.x, height = mode.y;
1036
1037 // Set absolute mouse mode
1038 ADBSetRelMouseMode(false);
1039
1040 // Find the maximum depth available
1041 int ndepths, max_depth(0);
1042 int *depths = XListDepths(x_display, screen, &ndepths);
1043 if (depths == NULL) {
1044 printf("FATAL: Could not determine the maximal depth available\n");
1045 return;
1046 } else {
1047 while (ndepths-- > 0) {
1048 if (depths[ndepths] > max_depth)
1049 max_depth = depths[ndepths];
1050 }
1051 }
1052
1053 // Get fbdevices file path from preferences
1054 const char *fbd_path = PrefsFindString("fbdevicefile");
1055
1056 // Open fbdevices file
1057 FILE *fp = fopen(fbd_path ? fbd_path : FBDEVICES_FILE_NAME, "r");
1058 if (fp == NULL) {
1059 char str[256];
1060 sprintf(str, GetString(STR_NO_FBDEVICE_FILE_ERR), fbd_path ? fbd_path : FBDEVICES_FILE_NAME, strerror(errno));
1061 ErrorAlert(str);
1062 return;
1063 }
1064
1065 int fb_depth; // supported depth
1066 uint32 fb_offset; // offset used for mmap(2)
1067 char fb_name[20];
1068 char line[256];
1069 bool device_found = false;
1070 while (fgets(line, 255, fp)) {
1071 // Read line
1072 int len = strlen(line);
1073 if (len == 0)
1074 continue;
1075 line[len - 1] = '\0';
1076
1077 // Comments begin with "#" or ";"
1078 if ((line[0] == '#') || (line[0] == ';') || (line[0] == '\0'))
1079 continue;
1080
1081 if ((sscanf(line, "%19s %d %x", fb_name, &fb_depth, &fb_offset) == 3)
1082 && (strcmp(fb_name, fb_name) == 0) && (fb_depth == max_depth)) {
1083 device_found = true;
1084 break;
1085 }
1086 }
1087
1088 // fbdevices file completely read
1089 fclose(fp);
1090
1091 // Frame buffer name not found ? Then, display warning
1092 if (!device_found) {
1093 char str[256];
1094 sprintf(str, GetString(STR_FBDEV_NAME_ERR), fb_name, max_depth);
1095 ErrorAlert(str);
1096 return;
1097 }
1098
1099 // Create window
1100 XSetWindowAttributes wattr;
1101 wattr.event_mask = eventmask = dga_eventmask;
1102 wattr.background_pixel = white_pixel;
1103 wattr.override_redirect = True;
1104 wattr.colormap = cmap[0];
1105
1106 w = XCreateWindow(x_display, rootwin,
1107 0, 0, width, height,
1108 0, xdepth, InputOutput, vis,
1109 CWEventMask | CWBackPixel | CWOverrideRedirect | (fb_depth <= 8 ? CWColormap : 0),
1110 &wattr);
1111
1112 // Set window name/class
1113 set_window_name(w, STR_WINDOW_TITLE);
1114
1115 // Indicate that we want keyboard input
1116 set_window_focus(w);
1117
1118 // Show window
1119 XMapRaised(x_display, w);
1120 wait_mapped(w);
1121
1122 // Grab mouse and keyboard
1123 XGrabKeyboard(x_display, w, True,
1124 GrabModeAsync, GrabModeAsync, CurrentTime);
1125 XGrabPointer(x_display, w, True,
1126 PointerMotionMask | ButtonPressMask | ButtonReleaseMask,
1127 GrabModeAsync, GrabModeAsync, w, None, CurrentTime);
1128 disable_mouse_accel();
1129
1130 // Calculate bytes per row
1131 int bytes_per_row = TrivialBytesPerRow(mode.x, mode.depth);
1132
1133 // Map frame buffer
1134 the_buffer_size = height * bytes_per_row;
1135 if ((the_buffer = (uint8 *) mmap(NULL, the_buffer_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fbdev_fd, fb_offset)) == MAP_FAILED) {
1136 if ((the_buffer = (uint8 *) mmap(NULL, the_buffer_size, PROT_READ | PROT_WRITE, MAP_SHARED, fbdev_fd, fb_offset)) == MAP_FAILED) {
1137 char str[256];
1138 sprintf(str, GetString(STR_FBDEV_MMAP_ERR), strerror(errno));
1139 ErrorAlert(str);
1140 return;
1141 }
1142 }
1143
1144 #if ENABLE_VOSF
1145 #if REAL_ADDRESSING || DIRECT_ADDRESSING
1146 // Screen_blitter_init() returns TRUE if VOSF is mandatory
1147 // i.e. the framebuffer update function is not Blit_Copy_Raw
1148 use_vosf = Screen_blitter_init(visualFormat, true, mode.depth);
1149
1150 if (use_vosf) {
1151 // Allocate memory for frame buffer (SIZE is extended to page-boundary)
1152 the_host_buffer = the_buffer;
1153 the_buffer_size = page_extend((height + 2) * bytes_per_row);
1154 the_buffer_copy = (uint8 *)malloc(the_buffer_size);
1155 the_buffer = (uint8 *)vm_acquire_mac(the_buffer_size);
1156
1157 // Fake image for DGA/VOSF mode to know about display bounds
1158 img = new FakeXImage(width, height, depth_of_video_mode(mode));
1159 }
1160 #else
1161 use_vosf = false;
1162 #endif
1163 #endif
1164
1165 // Set frame buffer base
1166 const_cast<video_mode *>(&mode)->bytes_per_row = bytes_per_row;
1167 const_cast<video_mode *>(&mode)->depth = DepthModeForPixelDepth(fb_depth);
1168 set_mac_frame_buffer(monitor, mode.depth, true);
1169
1170 // Everything went well
1171 init_ok = true;
1172 }
1173
1174 // Close display
1175 driver_fbdev::~driver_fbdev()
1176 {
1177 if (!use_vosf) {
1178 if (the_buffer != MAP_FAILED) {
1179 // don't free() the screen buffer in driver_base dtor
1180 munmap(the_buffer, the_buffer_size);
1181 the_buffer = NULL;
1182 }
1183 }
1184 #ifdef ENABLE_VOSF
1185 else {
1186 if (the_host_buffer != MAP_FAILED) {
1187 // don't free() the screen buffer in driver_base dtor
1188 munmap(the_host_buffer, the_buffer_size);
1189 the_host_buffer = NULL;
1190 }
1191 }
1192 #endif
1193 }
1194 #endif
1195
1196
1197 #ifdef ENABLE_XF86_DGA
1198 /*
1199 * XFree86 DGA display driver
1200 */
1201
1202 class driver_xf86dga : public driver_dga {
1203 public:
1204 driver_xf86dga(X11_monitor_desc &monitor);
1205 ~driver_xf86dga();
1206
1207 void update_palette(void);
1208 void resume(void);
1209
1210 private:
1211 int current_dga_cmap; // Number (0 or 1) of currently installed DGA colormap
1212 };
1213
1214 // Open display
1215 driver_xf86dga::driver_xf86dga(X11_monitor_desc &m)
1216 : driver_dga(m), current_dga_cmap(0)
1217 {
1218 int width = mode.x, height = mode.y;
1219
1220 // Set relative mouse mode
1221 ADBSetRelMouseMode(true);
1222
1223 #ifdef ENABLE_XF86_VIDMODE
1224 // Switch to best mode
1225 if (has_vidmode) {
1226 int best = 0;
1227 for (int i=1; i<num_x_video_modes; i++) {
1228 if (x_video_modes[i]->hdisplay >= width && x_video_modes[i]->vdisplay >= height &&
1229 x_video_modes[i]->hdisplay <= x_video_modes[best]->hdisplay && x_video_modes[i]->vdisplay <= x_video_modes[best]->vdisplay) {
1230 best = i;
1231 }
1232 }
1233 XF86VidModeSwitchToMode(x_display, screen, x_video_modes[best]);
1234 XF86VidModeSetViewPort(x_display, screen, 0, 0);
1235 XSync(x_display, false);
1236 }
1237 #endif
1238
1239 // Create window
1240 XSetWindowAttributes wattr;
1241 wattr.event_mask = eventmask = dga_eventmask;
1242 wattr.override_redirect = True;
1243 wattr.colormap = (mode.depth == VDEPTH_1BIT ? DefaultColormap(x_display, screen) : cmap[0]);
1244
1245 w = XCreateWindow(x_display, rootwin, 0, 0, width, height, 0, xdepth,
1246 InputOutput, vis, CWEventMask | CWOverrideRedirect |
1247 (color_class == DirectColor ? CWColormap : 0), &wattr);
1248
1249 // Set window name/class
1250 set_window_name(w, STR_WINDOW_TITLE);
1251
1252 // Indicate that we want keyboard input
1253 set_window_focus(w);
1254
1255 // Show window
1256 XMapRaised(x_display, w);
1257 wait_mapped(w);
1258
1259 // Establish direct screen connection
1260 XMoveResizeWindow(x_display, w, 0, 0, width, height);
1261 XWarpPointer(x_display, None, rootwin, 0, 0, 0, 0, 0, 0);
1262 XGrabKeyboard(x_display, rootwin, True, GrabModeAsync, GrabModeAsync, CurrentTime);
1263 XGrabPointer(x_display, rootwin, True, PointerMotionMask | ButtonPressMask | ButtonReleaseMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
1264 disable_mouse_accel();
1265
1266 int v_width, v_bank, v_size;
1267 XF86DGAGetVideo(x_display, screen, (char **)&the_buffer, &v_width, &v_bank, &v_size);
1268 XF86DGADirectVideo(x_display, screen, XF86DGADirectGraphics | XF86DGADirectKeyb | XF86DGADirectMouse);
1269 XF86DGASetViewPort(x_display, screen, 0, 0);
1270 XF86DGASetVidPage(x_display, screen, 0);
1271
1272 // Set colormap
1273 if (!IsDirectMode(mode)) {
1274 XSetWindowColormap(x_display, w, cmap[current_dga_cmap = 0]);
1275 XF86DGAInstallColormap(x_display, screen, cmap[current_dga_cmap]);
1276 }
1277 XSync(x_display, false);
1278
1279 // Init blitting routines
1280 int bytes_per_row = TrivialBytesPerRow((v_width + 7) & ~7, mode.depth);
1281 #if ENABLE_VOSF
1282 bool native_byte_order;
1283 #ifdef WORDS_BIGENDIAN
1284 native_byte_order = (XImageByteOrder(x_display) == MSBFirst);
1285 #else
1286 native_byte_order = (XImageByteOrder(x_display) == LSBFirst);
1287 #endif
1288 #if REAL_ADDRESSING || DIRECT_ADDRESSING
1289 // Screen_blitter_init() returns TRUE if VOSF is mandatory
1290 // i.e. the framebuffer update function is not Blit_Copy_Raw
1291 use_vosf = Screen_blitter_init(visualFormat, native_byte_order, depth_of_video_mode(mode));
1292
1293 if (use_vosf) {
1294 // Allocate memory for frame buffer (SIZE is extended to page-boundary)
1295 the_host_buffer = the_buffer;
1296 the_buffer_size = page_extend((height + 2) * bytes_per_row);
1297 the_buffer_copy = (uint8 *)malloc(the_buffer_size);
1298 the_buffer = (uint8 *)vm_acquire_mac(the_buffer_size);
1299
1300 // Fake image for DGA/VOSF mode to know about display bounds
1301 img = new FakeXImage((v_width + 7) & ~7, height, depth_of_video_mode(mode));
1302 }
1303 #else
1304 use_vosf = false;
1305 #endif
1306 #endif
1307
1308 // Set frame buffer base
1309 const_cast<video_mode *>(&mode)->bytes_per_row = bytes_per_row;
1310 set_mac_frame_buffer(monitor, mode.depth, true);
1311
1312 // Everything went well
1313 init_ok = true;
1314 }
1315
1316 // Close display
1317 driver_xf86dga::~driver_xf86dga()
1318 {
1319 XF86DGADirectVideo(x_display, screen, 0);
1320 if (!use_vosf) {
1321 // don't free() the screen buffer in driver_base dtor
1322 the_buffer = NULL;
1323 }
1324 #ifdef ENABLE_VOSF
1325 else {
1326 // don't free() the screen buffer in driver_base dtor
1327 the_host_buffer = NULL;
1328 }
1329 #endif
1330 #ifdef ENABLE_XF86_VIDMODE
1331 if (has_vidmode)
1332 XF86VidModeSwitchToMode(x_display, screen, x_video_modes[0]);
1333 #endif
1334 }
1335
1336 // Palette has changed
1337 void driver_xf86dga::update_palette(void)
1338 {
1339 driver_dga::update_palette();
1340 current_dga_cmap ^= 1;
1341 if (!IsDirectMode(monitor.get_current_mode()) && cmap[current_dga_cmap])
1342 XF86DGAInstallColormap(x_display, screen, cmap[current_dga_cmap]);
1343 }
1344
1345 // Resume emulation
1346 void driver_xf86dga::resume(void)
1347 {
1348 driver_dga::resume();
1349 if (!IsDirectMode(monitor.get_current_mode()))
1350 XF86DGAInstallColormap(x_display, screen, cmap[current_dga_cmap]);
1351 }
1352 #endif
1353
1354
1355 /*
1356 * Initialization
1357 */
1358
1359 // Init keycode translation table
1360 static void keycode_init(void)
1361 {
1362 bool use_kc = PrefsFindBool("keycodes");
1363 if (use_kc) {
1364
1365 // Get keycode file path from preferences
1366 const char *kc_path = PrefsFindString("keycodefile");
1367
1368 // Open keycode table
1369 FILE *f = fopen(kc_path ? kc_path : KEYCODE_FILE_NAME, "r");
1370 if (f == NULL) {
1371 char str[256];
1372 sprintf(str, GetString(STR_KEYCODE_FILE_WARN), kc_path ? kc_path : KEYCODE_FILE_NAME, strerror(errno));
1373 WarningAlert(str);
1374 return;
1375 }
1376
1377 // Default translation table
1378 for (int i=0; i<256; i++)
1379 keycode_table[i] = -1;
1380
1381 // Search for server vendor string, then read keycodes
1382 const char *vendor = ServerVendor(x_display);
1383 // Force use of MacX mappings on MacOS X with Apple's X server
1384 int dummy;
1385 if (XQueryExtension(x_display, "Apple-DRI", &dummy, &dummy, &dummy))
1386 vendor = "MacX";
1387 bool vendor_found = false;
1388 char line[256];
1389 while (fgets(line, 255, f)) {
1390 // Read line
1391 int len = strlen(line);
1392 if (len == 0)
1393 continue;
1394 line[len-1] = 0;
1395
1396 // Comments begin with "#" or ";"
1397 if (line[0] == '#' || line[0] == ';' || line[0] == 0)
1398 continue;
1399
1400 if (vendor_found) {
1401 // Read keycode
1402 int x_code, mac_code;
1403 if (sscanf(line, "%d %d", &x_code, &mac_code) == 2)
1404 keycode_table[x_code & 0xff] = mac_code;
1405 else
1406 break;
1407 } else {
1408 // Search for vendor string
1409 if (strstr(vendor, line) == vendor)
1410 vendor_found = true;
1411 }
1412 }
1413
1414 // Keycode file completely read
1415 fclose(f);
1416 use_keycodes = vendor_found;
1417
1418 // Vendor not found? Then display warning
1419 if (!vendor_found) {
1420 char str[256];
1421 sprintf(str, GetString(STR_KEYCODE_VENDOR_WARN), vendor, kc_path ? kc_path : KEYCODE_FILE_NAME);
1422 WarningAlert(str);
1423 return;
1424 }
1425 }
1426 }
1427
1428 // Open display for current mode
1429 bool X11_monitor_desc::video_open(void)
1430 {
1431 D(bug("video_open()\n"));
1432 const video_mode &mode = get_current_mode();
1433
1434 // Find best available X visual
1435 if (!find_visual_for_depth(mode.depth)) {
1436 ErrorAlert(STR_NO_XVISUAL_ERR);
1437 return false;
1438 }
1439
1440 // Build up visualFormat structure
1441 visualFormat.fullscreen = (display_type == DISPLAY_DGA);
1442 visualFormat.depth = visualInfo.depth;
1443 visualFormat.Rmask = visualInfo.red_mask;
1444 visualFormat.Gmask = visualInfo.green_mask;
1445 visualFormat.Bmask = visualInfo.blue_mask;
1446
1447 // Create color maps
1448 if (color_class == PseudoColor || color_class == DirectColor) {
1449 cmap[0] = XCreateColormap(x_display, rootwin, vis, AllocAll);
1450 cmap[1] = XCreateColormap(x_display, rootwin, vis, AllocAll);
1451 } else {
1452 cmap[0] = XCreateColormap(x_display, rootwin, vis, AllocNone);
1453 cmap[1] = XCreateColormap(x_display, rootwin, vis, AllocNone);
1454 }
1455
1456 // Find pixel format of direct modes
1457 if (color_class == DirectColor || color_class == TrueColor) {
1458 rshift = gshift = bshift = 0;
1459 rloss = gloss = bloss = 8;
1460 uint32 mask;
1461 for (mask=vis->red_mask; !(mask&1); mask>>=1)
1462 ++rshift;
1463 for (; mask&1; mask>>=1)
1464 --rloss;
1465 for (mask=vis->green_mask; !(mask&1); mask>>=1)
1466 ++gshift;
1467 for (; mask&1; mask>>=1)
1468 --gloss;
1469 for (mask=vis->blue_mask; !(mask&1); mask>>=1)
1470 ++bshift;
1471 for (; mask&1; mask>>=1)
1472 --bloss;
1473 }
1474
1475 // Preset palette pixel values for CLUT or gamma table
1476 if (color_class == DirectColor) {
1477 int num = vis->map_entries;
1478 for (int i=0; i<num; i++) {
1479 int c = (i * 256) / num;
1480 x_palette[i].pixel = map_rgb(c, c, c);
1481 x_palette[i].flags = DoRed | DoGreen | DoBlue;
1482 }
1483 } else if (color_class == PseudoColor) {
1484 for (int i=0; i<256; i++) {
1485 x_palette[i].pixel = i;
1486 x_palette[i].flags = DoRed | DoGreen | DoBlue;
1487 }
1488 }
1489
1490 // Load gray ramp to color map
1491 int num = (color_class == DirectColor ? vis->map_entries : 256);
1492 for (int i=0; i<num; i++) {
1493 int c = (i * 256) / num;
1494 x_palette[i].red = c * 0x0101;
1495 x_palette[i].green = c * 0x0101;
1496 x_palette[i].blue = c * 0x0101;
1497 }
1498 if (color_class == PseudoColor || color_class == DirectColor) {
1499 XStoreColors(x_display, cmap[0], x_palette, num);
1500 XStoreColors(x_display, cmap[1], x_palette, num);
1501 }
1502
1503 #ifdef ENABLE_VOSF
1504 // Load gray ramp to 8->16/32 expand map
1505 if (!IsDirectMode(mode) && xdepth > 8)
1506 for (int i=0; i<256; i++)
1507 ExpandMap[i] = map_rgb(i, i, i);
1508 #endif
1509
1510 // Create display driver object of requested type
1511 switch (display_type) {
1512 case DISPLAY_WINDOW:
1513 drv = new driver_window(*this);
1514 break;
1515 #ifdef ENABLE_FBDEV_DGA
1516 case DISPLAY_DGA:
1517 drv = new driver_fbdev(*this);
1518 break;
1519 #endif
1520 #ifdef ENABLE_XF86_DGA
1521 case DISPLAY_DGA:
1522 drv = new driver_xf86dga(*this);
1523 break;
1524 #endif
1525 }
1526 if (drv == NULL)
1527 return false;
1528 if (!drv->init_ok) {
1529 delete drv;
1530 drv = NULL;
1531 return false;
1532 }
1533
1534 #ifdef ENABLE_VOSF
1535 if (use_vosf) {
1536 // Initialize the VOSF system
1537 if (!video_vosf_init(*this)) {
1538 ErrorAlert(STR_VOSF_INIT_ERR);
1539 return false;
1540 }
1541 }
1542 #endif
1543
1544 // Initialize VideoRefresh function
1545 VideoRefreshInit();
1546
1547 // Lock down frame buffer
1548 XSync(x_display, false);
1549 LOCK_FRAME_BUFFER;
1550
1551 // Start redraw/input thread
1552 #ifdef HAVE_PTHREADS
1553 redraw_thread_cancel = false;
1554 Set_pthread_attr(&redraw_thread_attr, 0);
1555 redraw_thread_active = (pthread_create(&redraw_thread, &redraw_thread_attr, redraw_func, NULL) == 0);
1556 if (!redraw_thread_active) {
1557 printf("FATAL: cannot create redraw thread\n");
1558 return false;
1559 }
1560 #else
1561 redraw_thread_active = true;
1562 #endif
1563
1564 return true;
1565 }
1566
1567 bool VideoInit(bool classic)
1568 {
1569 classic_mode = classic;
1570
1571 #ifdef ENABLE_VOSF
1572 // Zero the mainBuffer structure
1573 mainBuffer.dirtyPages = NULL;
1574 mainBuffer.pageInfo = NULL;
1575 #endif
1576
1577 // Check if X server runs on local machine
1578 local_X11 = (strncmp(XDisplayName(x_display_name), ":", 1) == 0)
1579 || (strncmp(XDisplayName(x_display_name), "unix:", 5) == 0);
1580
1581 // Init keycode translation
1582 keycode_init();
1583
1584 // Read prefs
1585 frame_skip = PrefsFindInt32("frameskip");
1586 mouse_wheel_mode = PrefsFindInt32("mousewheelmode");
1587 mouse_wheel_lines = PrefsFindInt32("mousewheellines");
1588
1589 // Find screen and root window
1590 screen = XDefaultScreen(x_display);
1591 rootwin = XRootWindow(x_display, screen);
1592
1593 // Get sorted list of available depths
1594 avail_depths = XListDepths(x_display, screen, &num_depths);
1595 if (avail_depths == NULL) {
1596 ErrorAlert(STR_UNSUPP_DEPTH_ERR);
1597 return false;
1598 }
1599 std::sort(avail_depths, avail_depths + num_depths);
1600
1601 #ifdef ENABLE_FBDEV_DGA
1602 // Frame buffer name
1603 char fb_name[20];
1604
1605 // Could do fbdev DGA?
1606 if ((fbdev_fd = open(FBDEVICE_FILE_NAME, O_RDWR)) != -1)
1607 has_dga = true;
1608 else
1609 has_dga = false;
1610 #endif
1611
1612 #ifdef ENABLE_XF86_DGA
1613 // DGA available?
1614 int dga_event_base, dga_error_base;
1615 if (local_X11 && XF86DGAQueryExtension(x_display, &dga_event_base, &dga_error_base)) {
1616 int dga_flags = 0;
1617 XF86DGAQueryDirectVideo(x_display, screen, &dga_flags);
1618 has_dga = dga_flags & XF86DGADirectPresent;
1619 } else
1620 has_dga = false;
1621 #endif
1622
1623 #ifdef ENABLE_XF86_VIDMODE
1624 // VidMode available?
1625 int vm_event_base, vm_error_base;
1626 has_vidmode = XF86VidModeQueryExtension(x_display, &vm_event_base, &vm_error_base);
1627 if (has_vidmode)
1628 XF86VidModeGetAllModeLines(x_display, screen, &num_x_video_modes, &x_video_modes);
1629 #endif
1630
1631 // Find black and white colors
1632 XParseColor(x_display, DefaultColormap(x_display, screen), "rgb:00/00/00", &black);
1633 XAllocColor(x_display, DefaultColormap(x_display, screen), &black);
1634 XParseColor(x_display, DefaultColormap(x_display, screen), "rgb:ff/ff/ff", &white);
1635 XAllocColor(x_display, DefaultColormap(x_display, screen), &white);
1636 black_pixel = BlackPixel(x_display, screen);
1637 white_pixel = WhitePixel(x_display, screen);
1638
1639 // Get screen mode from preferences
1640 const char *mode_str;
1641 if (classic_mode)
1642 mode_str = "win/512/342";
1643 else
1644 mode_str = PrefsFindString("screen");
1645
1646 // Determine display type and default dimensions
1647 int default_width = 512, default_height = 384;
1648 display_type = DISPLAY_WINDOW;
1649 if (mode_str) {
1650 if (sscanf(mode_str, "win/%d/%d", &default_width, &default_height) == 2) {
1651 display_type = DISPLAY_WINDOW;
1652 #ifdef ENABLE_FBDEV_DGA
1653 } else if (has_dga && sscanf(mode_str, "dga/%19s", fb_name) == 1) {
1654 display_type = DISPLAY_DGA;
1655 default_width = -1; default_height = -1; // use entire screen
1656 #endif
1657 #ifdef ENABLE_XF86_DGA
1658 } else if (has_dga && sscanf(mode_str, "dga/%d/%d", &default_width, &default_height) == 2) {
1659 display_type = DISPLAY_DGA;
1660 #endif
1661 }
1662 }
1663 if (default_width <= 0)
1664 default_width = DisplayWidth(x_display, screen);
1665 else if (default_width > DisplayWidth(x_display, screen))
1666 default_width = DisplayWidth(x_display, screen);
1667 if (default_height <= 0)
1668 default_height = DisplayHeight(x_display, screen);
1669 else if (default_height > DisplayHeight(x_display, screen))
1670 default_height = DisplayHeight(x_display, screen);
1671
1672 // Mac screen depth follows X depth
1673 video_depth default_depth = VDEPTH_1BIT;
1674 switch (DefaultDepth(x_display, screen)) {
1675 case 8:
1676 default_depth = VDEPTH_8BIT;
1677 break;
1678 case 15: case 16:
1679 default_depth = VDEPTH_16BIT;
1680 break;
1681 case 24: case 32:
1682 default_depth = VDEPTH_32BIT;
1683 break;
1684 }
1685
1686 // Construct list of supported modes
1687 if (display_type == DISPLAY_WINDOW) {
1688 if (classic)
1689 add_mode(512, 342, 0x80, 64, VDEPTH_1BIT);
1690 else {
1691 for (unsigned d=VDEPTH_1BIT; d<=VDEPTH_32BIT; d++) {
1692 if (find_visual_for_depth(video_depth(d)))
1693 add_window_modes(video_depth(d));
1694 }
1695 }
1696 } else
1697 add_mode(default_width, default_height, 0x80, TrivialBytesPerRow(default_width, default_depth), default_depth);
1698 if (VideoModes.empty()) {
1699 ErrorAlert(STR_NO_XVISUAL_ERR);
1700 return false;
1701 }
1702
1703 // Find requested default mode with specified dimensions
1704 uint32 default_id;
1705 std::vector<video_mode>::const_iterator i, end = VideoModes.end();
1706 for (i = VideoModes.begin(); i != end; ++i) {
1707 if (i->x == default_width && i->y == default_height && i->depth == default_depth) {
1708 default_id = i->resolution_id;
1709 break;
1710 }
1711 }
1712 if (i == end) { // not found, use first available mode
1713 default_depth = VideoModes[0].depth;
1714 default_id = VideoModes[0].resolution_id;
1715 }
1716
1717 #if DEBUG
1718 D(bug("Available video modes:\n"));
1719 for (i = VideoModes.begin(); i != end; ++i) {
1720 int bits = 1 << i->depth;
1721 if (bits == 16)
1722 bits = 15;
1723 else if (bits == 32)
1724 bits = 24;
1725 D(bug(" %dx%d (ID %02x), %d colors\n", i->x, i->y, i->resolution_id, 1 << bits));
1726 }
1727 #endif
1728
1729 // Create X11_monitor_desc for this (the only) display
1730 X11_monitor_desc *monitor = new X11_monitor_desc(VideoModes, default_depth, default_id);
1731 VideoMonitors.push_back(monitor);
1732
1733 // Open display
1734 return monitor->video_open();
1735 }
1736
1737
1738 /*
1739 * Deinitialization
1740 */
1741
1742 // Close display
1743 void X11_monitor_desc::video_close(void)
1744 {
1745 D(bug("video_close()\n"));
1746
1747 // Stop redraw thread
1748 #ifdef HAVE_PTHREADS
1749 if (redraw_thread_active) {
1750 redraw_thread_cancel = true;
1751 redraw_thread_cancel_ack = false;
1752 pthread_join(redraw_thread, NULL);
1753 while (!redraw_thread_cancel_ack) ;
1754 }
1755 #endif
1756 redraw_thread_active = false;
1757
1758 // Unlock frame buffer
1759 UNLOCK_FRAME_BUFFER;
1760 XSync(x_display, false);
1761 D(bug(" frame buffer unlocked\n"));
1762
1763 #ifdef ENABLE_VOSF
1764 if (use_vosf) {
1765 // Deinitialize VOSF
1766 video_vosf_exit();
1767 }
1768 #endif
1769
1770 // Close display
1771 delete drv;
1772 drv = NULL;
1773
1774 // Free colormaps
1775 if (cmap[0]) {
1776 XFreeColormap(x_display, cmap[0]);
1777 cmap[0] = 0;
1778 }
1779 if (cmap[1]) {
1780 XFreeColormap(x_display, cmap[1]);
1781 cmap[1] = 0;
1782 }
1783 }
1784
1785 void VideoExit(void)
1786 {
1787 // Close displays
1788 vector<monitor_desc *>::iterator i, end = VideoMonitors.end();
1789 for (i = VideoMonitors.begin(); i != end; ++i)
1790 dynamic_cast<X11_monitor_desc *>(*i)->video_close();
1791
1792 #ifdef ENABLE_XF86_VIDMODE
1793 // Free video mode list
1794 if (x_video_modes) {
1795 XFree(x_video_modes);
1796 x_video_modes = NULL;
1797 }
1798 #endif
1799
1800 #ifdef ENABLE_FBDEV_DGA
1801 // Close framebuffer device
1802 if (fbdev_fd >= 0) {
1803 close(fbdev_fd);
1804 fbdev_fd = -1;
1805 }
1806 #endif
1807
1808 // Free depth list
1809 if (avail_depths) {
1810 XFree(avail_depths);
1811 avail_depths = NULL;
1812 }
1813 }
1814
1815
1816 /*
1817 * Close down full-screen mode (if bringing up error alerts is unsafe while in full-screen mode)
1818 */
1819
1820 void VideoQuitFullScreen(void)
1821 {
1822 D(bug("VideoQuitFullScreen()\n"));
1823 quit_full_screen = true;
1824 }
1825
1826
1827 /*
1828 * Mac VBL interrupt
1829 */
1830
1831 void VideoInterrupt(void)
1832 {
1833 // Emergency quit requested? Then quit
1834 if (emerg_quit)
1835 QuitEmulator();
1836
1837 // Temporarily give up frame buffer lock (this is the point where
1838 // we are suspended when the user presses Ctrl-Tab)
1839 UNLOCK_FRAME_BUFFER;
1840 LOCK_FRAME_BUFFER;
1841 }
1842
1843
1844 /*
1845 * Set palette
1846 */
1847
1848 void X11_monitor_desc::set_palette(uint8 *pal, int num_in)
1849 {
1850 const video_mode &mode = get_current_mode();
1851
1852 LOCK_PALETTE;
1853
1854 // Convert colors to XColor array
1855 int num_out = 256;
1856 bool stretch = false;
1857 if (IsDirectMode(mode)) {
1858 // If X is in 565 mode we have to stretch the gamma table from 32 to 64 entries
1859 num_out = vis->map_entries;
1860 stretch = true;
1861 }
1862 XColor *p = x_palette;
1863 for (int i=0; i<num_out; i++) {
1864 int c = (stretch ? (i * num_in) / num_out : i);
1865 p->red = pal[c*3 + 0] * 0x0101;
1866 p->green = pal[c*3 + 1] * 0x0101;
1867 p->blue = pal[c*3 + 2] * 0x0101;
1868 p++;
1869 }
1870
1871 #ifdef ENABLE_VOSF
1872 // Recalculate pixel color expansion map
1873 if (!IsDirectMode(mode) && xdepth > 8) {
1874 for (int i=0; i<256; i++) {
1875 int c = i & (num_in-1); // If there are less than 256 colors, we repeat the first entries (this makes color expansion easier)
1876 ExpandMap[i] = map_rgb(pal[c*3+0], pal[c*3+1], pal[c*3+2]);
1877 }
1878
1879 // We have to redraw everything because the interpretation of pixel values changed
1880 LOCK_VOSF;
1881 PFLAG_SET_ALL;
1882 UNLOCK_VOSF;
1883 memset(the_buffer_copy, 0, mode.bytes_per_row * mode.y);
1884 }
1885 #endif
1886
1887 // Tell redraw thread to change palette
1888 x_palette_changed = true;
1889
1890 UNLOCK_PALETTE;
1891 }
1892
1893
1894 /*
1895 * Switch video mode
1896 */
1897
1898 void X11_monitor_desc::switch_to_current_mode(void)
1899 {
1900 // Close and reopen display
1901 video_close();
1902 video_open();
1903
1904 if (drv == NULL) {
1905 ErrorAlert(STR_OPEN_WINDOW_ERR);
1906 QuitEmulator();
1907 }
1908 }
1909
1910
1911 /*
1912 * Translate key event to Mac keycode, returns -1 if no keycode was found
1913 * and -2 if the key was recognized as a hotkey
1914 */
1915
1916 static int kc_decode(KeySym ks, bool key_down)
1917 {
1918 switch (ks) {
1919 case XK_A: case XK_a: return 0x00;
1920 case XK_B: case XK_b: return 0x0b;
1921 case XK_C: case XK_c: return 0x08;
1922 case XK_D: case XK_d: return 0x02;
1923 case XK_E: case XK_e: return 0x0e;
1924 case XK_F: case XK_f: return 0x03;
1925 case XK_G: case XK_g: return 0x05;
1926 case XK_H: case XK_h: return 0x04;
1927 case XK_I: case XK_i: return 0x22;
1928 case XK_J: case XK_j: return 0x26;
1929 case XK_K: case XK_k: return 0x28;
1930 case XK_L: case XK_l: return 0x25;
1931 case XK_M: case XK_m: return 0x2e;
1932 case XK_N: case XK_n: return 0x2d;
1933 case XK_O: case XK_o: return 0x1f;
1934 case XK_P: case XK_p: return 0x23;
1935 case XK_Q: case XK_q: return 0x0c;
1936 case XK_R: case XK_r: return 0x0f;
1937 case XK_S: case XK_s: return 0x01;
1938 case XK_T: case XK_t: return 0x11;
1939 case XK_U: case XK_u: return 0x20;
1940 case XK_V: case XK_v: return 0x09;
1941 case XK_W: case XK_w: return 0x0d;
1942 case XK_X: case XK_x: return 0x07;
1943 case XK_Y: case XK_y: return 0x10;
1944 case XK_Z: case XK_z: return 0x06;
1945
1946 case XK_1: case XK_exclam: return 0x12;
1947 case XK_2: case XK_at: return 0x13;
1948 case XK_3: case XK_numbersign: return 0x14;
1949 case XK_4: case XK_dollar: return 0x15;
1950 case XK_5: case XK_percent: return 0x17;
1951 case XK_6: return 0x16;
1952 case XK_7: return 0x1a;
1953 case XK_8: return 0x1c;
1954 case XK_9: return 0x19;
1955 case XK_0: return 0x1d;
1956
1957 case XK_grave: case XK_asciitilde: return 0x0a;
1958 case XK_minus: case XK_underscore: return 0x1b;
1959 case XK_equal: case XK_plus: return 0x18;
1960 case XK_bracketleft: case XK_braceleft: return 0x21;
1961 case XK_bracketright: case XK_braceright: return 0x1e;
1962 case XK_backslash: case XK_bar: return 0x2a;
1963 case XK_semicolon: case XK_colon: return 0x29;
1964 case XK_apostrophe: case XK_quotedbl: return 0x27;
1965 case XK_comma: case XK_less: return 0x2b;
1966 case XK_period: case XK_greater: return 0x2f;
1967 case XK_slash: case XK_question: return 0x2c;
1968
1969 case XK_Tab: if (ctrl_down) {if (key_down) drv->suspend(); return -2;} else return 0x30;
1970 case XK_Return: return 0x24;
1971 case XK_space: return 0x31;
1972 case XK_BackSpace: return 0x33;
1973
1974 case XK_Delete: return 0x75;
1975 case XK_Insert: return 0x72;
1976 case XK_Home: case XK_Help: return 0x73;
1977 case XK_End: return 0x77;
1978 #ifdef __hpux
1979 case XK_Prior: return 0x74;
1980 case XK_Next: return 0x79;
1981 #else
1982 case XK_Page_Up: return 0x74;
1983 case XK_Page_Down: return 0x79;
1984 #endif
1985
1986 case XK_Control_L: return 0x36;
1987 case XK_Control_R: return 0x36;
1988 case XK_Shift_L: return 0x38;
1989 case XK_Shift_R: return 0x38;
1990 case XK_Alt_L: return 0x37;
1991 case XK_Alt_R: return 0x37;
1992 case XK_Meta_L: return 0x3a;
1993 case XK_Meta_R: return 0x3a;
1994 case XK_Menu: return 0x32;
1995 case XK_Caps_Lock: return 0x39;
1996 case XK_Num_Lock: return 0x47;
1997
1998 case XK_Up: return 0x3e;
1999 case XK_Down: return 0x3d;
2000 case XK_Left: return 0x3b;
2001 case XK_Right: return 0x3c;
2002
2003 case XK_Escape: if (ctrl_down) {if (key_down) { quit_full_screen = true; emerg_quit = true; } return -2;} else return 0x35;
2004
2005 case XK_F1: if (ctrl_down) {if (key_down) SysMountFirstFloppy(); return -2;} else return 0x7a;
2006 case XK_F2: return 0x78;
2007 case XK_F3: return 0x63;
2008 case XK_F4: return 0x76;
2009 case XK_F5: if (ctrl_down) {if (key_down) drv->toggle_mouse_grab(); return -2;} else return 0x60;
2010 case XK_F6: return 0x61;
2011 case XK_F7: return 0x62;
2012 case XK_F8: return 0x64;
2013 case XK_F9: return 0x65;
2014 case XK_F10: return 0x6d;
2015 case XK_F11: return 0x67;
2016 case XK_F12: return 0x6f;
2017
2018 case XK_Print: return 0x69;
2019 case XK_Scroll_Lock: return 0x6b;
2020 case XK_Pause: return 0x71;
2021
2022 #if defined(XK_KP_Prior) && defined(XK_KP_Left) && defined(XK_KP_Insert) && defined (XK_KP_End)
2023 case XK_KP_0: case XK_KP_Insert: return 0x52;
2024 case XK_KP_1: case XK_KP_End: return 0x53;
2025 case XK_KP_2: case XK_KP_Down: return 0x54;
2026 case XK_KP_3: case XK_KP_Next: return 0x55;
2027 case XK_KP_4: case XK_KP_Left: return 0x56;
2028 case XK_KP_5: case XK_KP_Begin: return 0x57;
2029 case XK_KP_6: case XK_KP_Right: return 0x58;
2030 case XK_KP_7: case XK_KP_Home: return 0x59;
2031 case XK_KP_8: case XK_KP_Up: return 0x5b;
2032 case XK_KP_9: case XK_KP_Prior: return 0x5c;
2033 case XK_KP_Decimal: case XK_KP_Delete: return 0x41;
2034 #else
2035 case XK_KP_0: return 0x52;
2036 case XK_KP_1: return 0x53;
2037 case XK_KP_2: return 0x54;
2038 case XK_KP_3: return 0x55;
2039 case XK_KP_4: return 0x56;
2040 case XK_KP_5: return 0x57;
2041 case XK_KP_6: return 0x58;
2042 case XK_KP_7: return 0x59;
2043 case XK_KP_8: return 0x5b;
2044 case XK_KP_9: return 0x5c;
2045 case XK_KP_Decimal: return 0x41;
2046 #endif
2047 case XK_KP_Add: return 0x45;
2048 case XK_KP_Subtract: return 0x4e;
2049 case XK_KP_Multiply: return 0x43;
2050 case XK_KP_Divide: return 0x4b;
2051 case XK_KP_Enter: return 0x4c;
2052 case XK_KP_Equal: return 0x51;
2053 }
2054 return -1;
2055 }
2056
2057 static int event2keycode(XKeyEvent &ev, bool key_down)
2058 {
2059 KeySym ks;
2060 int i = 0;
2061
2062 do {
2063 ks = XLookupKeysym(&ev, i++);
2064 int as = kc_decode(ks, key_down);
2065 if (as >= 0)
2066 return as;
2067 if (as == -2)
2068 return as;
2069 } while (ks != NoSymbol);
2070
2071 return -1;
2072 }
2073
2074
2075 /*
2076 * X event handling
2077 */
2078
2079 static void handle_events(void)
2080 {
2081 for (;;) {
2082 XEvent event;
2083 XDisplayLock();
2084
2085 if (!XCheckMaskEvent(x_display, eventmask, &event)) {
2086 // Handle clipboard events
2087 if (XCheckTypedEvent(x_display, SelectionRequest, &event))
2088 ClipboardSelectionRequest(&event.xselectionrequest);
2089 else if (XCheckTypedEvent(x_display, SelectionClear, &event))
2090 ClipboardSelectionClear(&event.xselectionclear);
2091
2092 // Window "close" widget clicked
2093 else if (XCheckTypedEvent(x_display, ClientMessage, &event)) {
2094 if (event.xclient.format == 32 && event.xclient.data.l[0] == WM_DELETE_WINDOW) {
2095 ADBKeyDown(0x7f); // Power key
2096 ADBKeyUp(0x7f);
2097 }
2098 }
2099 XDisplayUnlock();
2100 break;
2101 }
2102
2103 switch (event.type) {
2104
2105 // Mouse button
2106 case ButtonPress: {
2107 unsigned int button = event.xbutton.button;
2108 if (button < 4)
2109 ADBMouseDown(button - 1);
2110 else if (button < 6) { // Wheel mouse
2111 if (mouse_wheel_mode == 0) {
2112 int key = (button == 5) ? 0x79 : 0x74; // Page up/down
2113 ADBKeyDown(key);
2114 ADBKeyUp(key);
2115 } else {
2116 int key = (button == 5) ? 0x3d : 0x3e; // Cursor up/down
2117 for(int i=0; i<mouse_wheel_lines; i++) {
2118 ADBKeyDown(key);
2119 ADBKeyUp(key);
2120 }
2121 }
2122 }
2123 break;
2124 }
2125 case ButtonRelease: {
2126 unsigned int button = event.xbutton.button;
2127 if (button < 4)
2128 ADBMouseUp(button - 1);
2129 break;
2130 }
2131
2132 // Mouse moved
2133 case MotionNotify:
2134 drv->mouse_moved(event.xmotion.x, event.xmotion.y);
2135 break;
2136
2137 // Mouse entered window
2138 case EnterNotify:
2139 if (event.xcrossing.mode != NotifyGrab && event.xcrossing.mode != NotifyUngrab)
2140 drv->mouse_moved(event.xmotion.x, event.xmotion.y);
2141 break;
2142
2143 // Keyboard
2144 case KeyPress: {
2145 int code = -1;
2146 if (use_keycodes) {
2147 if (event2keycode(event.xkey, true) != -2) // This is called to process the hotkeys
2148 code = keycode_table[event.xkey.keycode & 0xff];
2149 } else
2150 code = event2keycode(event.xkey, true);
2151 if (code >= 0) {
2152 if (!emul_suspended) {
2153 if (code == 0x39) { // Caps Lock pressed
2154 if (caps_on) {
2155 ADBKeyUp(code);
2156 caps_on = false;
2157 } else {
2158 ADBKeyDown(code);
2159 caps_on = true;
2160 }
2161 } else
2162 ADBKeyDown(code);
2163 if (code == 0x36)
2164 ctrl_down = true;
2165 } else {
2166 if (code == 0x31)
2167 drv->resume(); // Space wakes us up
2168 }
2169 }
2170 break;
2171 }
2172 case KeyRelease: {
2173 int code = -1;
2174 if (use_keycodes) {
2175 if (event2keycode(event.xkey, false) != -2) // This is called to process the hotkeys
2176 code = keycode_table[event.xkey.keycode & 0xff];
2177 } else
2178 code = event2keycode(event.xkey, false);
2179 if (code >= 0 && code != 0x39) { // Don't propagate Caps Lock releases
2180 ADBKeyUp(code);
2181 if (code == 0x36)
2182 ctrl_down = false;
2183 }
2184 break;
2185 }
2186
2187 // Hidden parts exposed, force complete refresh of window
2188 case Expose:
2189 if (display_type == DISPLAY_WINDOW) {
2190 const video_mode &mode = VideoMonitors[0]->get_current_mode();
2191 #ifdef ENABLE_VOSF
2192 if (use_vosf) { // VOSF refresh
2193 LOCK_VOSF;
2194 PFLAG_SET_ALL;
2195 UNLOCK_VOSF;
2196 memset(the_buffer_copy, 0, mode.bytes_per_row * mode.y);
2197 }
2198 else
2199 #endif
2200 if (frame_skip == 0) { // Dynamic refresh
2201 int x1, y1;
2202 for (y1=0; y1<16; y1++)
2203 for (x1=0; x1<16; x1++)
2204 updt_box[x1][y1] = true;
2205 nr_boxes = 16 * 16;
2206 } else // Static refresh
2207 memset(the_buffer_copy, 0, mode.bytes_per_row * mode.y);
2208 }
2209 break;
2210 }
2211
2212 XDisplayUnlock();
2213 }
2214 }
2215
2216
2217 /*
2218 * Window display update
2219 */
2220
2221 // Dynamic display update (variable frame rate for each box)
2222 static void update_display_dynamic(int ticker, driver_window *drv)
2223 {
2224 int y1, y2, y2s, y2a, i, x1, xm, xmo, ymo, yo, yi, yil, xi;
2225 int xil = 0;
2226 int rxm = 0, rxmo = 0;
2227 const video_mode &mode = drv->monitor.get_current_mode();
2228 int bytes_per_row = mode.bytes_per_row;
2229 int bytes_per_pixel = mode.bytes_per_row / mode.x;
2230 int rx = mode.bytes_per_row / 16;
2231 int ry = mode.y / 16;
2232 int max_box;
2233
2234 y2s = sm_uptd[ticker % 8];
2235 y2a = 8;
2236 for (i = 0; i < 6; i++)
2237 if (ticker % (2 << i))
2238 break;
2239 max_box = sm_no_boxes[i];
2240
2241 if (y2a) {
2242 for (y1=0; y1<16; y1++) {
2243 for (y2=y2s; y2 < ry; y2 += y2a) {
2244 i = ((y1 * ry) + y2) * bytes_per_row;
2245 for (x1=0; x1<16; x1++, i += rx) {
2246 if (updt_box[x1][y1] == false) {
2247 if (memcmp(&the_buffer_copy[i], &the_buffer[i], rx)) {
2248 updt_box[x1][y1] = true;
2249 nr_boxes++;
2250 }
2251 }
2252 }
2253 }
2254 }
2255 }
2256
2257 XDisplayLock();
2258 if ((nr_boxes <= max_box) && (nr_boxes)) {
2259 for (y1=0; y1<16; y1++) {
2260 for (x1=0; x1<16; x1++) {
2261 if (updt_box[x1][y1] == true) {
2262 if (rxm == 0)
2263 xm = x1;
2264 rxm += rx;
2265 updt_box[x1][y1] = false;
2266 }
2267 if (((updt_box[x1+1][y1] == false) || (x1 == 15)) && (rxm)) {
2268 if ((rxmo != rxm) || (xmo != xm) || (yo != y1 - 1)) {
2269 if (rxmo) {
2270 xi = xmo * rx;
2271 yi = ymo * ry;
2272 xil = rxmo;
2273 yil = (yo - ymo +1) * ry;
2274 }
2275 rxmo = rxm;
2276 xmo = xm;
2277 ymo = y1;
2278 }
2279 rxm = 0;
2280 yo = y1;
2281 }
2282 if (xil) {
2283 i = (yi * bytes_per_row) + xi;
2284 for (y2=0; y2 < yil; y2++, i += bytes_per_row)
2285 memcpy(&the_buffer_copy[i], &the_buffer[i], xil);
2286 if (mode.depth == VDEPTH_1BIT) {
2287 if (drv->have_shm)
2288 XShmPutImage(x_display, drv->w, drv->gc, drv->img, xi * 8, yi, xi * 8, yi, xil * 8, yil, 0);
2289 else
2290 XPutImage(x_display, drv->w, drv->gc, drv->img, xi * 8, yi, xi * 8, yi, xil * 8, yil);
2291 } else {
2292 if (drv->have_shm)
2293 XShmPutImage(x_display, drv->w, drv->gc, drv->img, xi / bytes_per_pixel, yi, xi / bytes_per_pixel, yi, xil / bytes_per_pixel, yil, 0);
2294 else
2295 XPutImage(x_display, drv->w, drv->gc, drv->img, xi / bytes_per_pixel, yi, xi / bytes_per_pixel, yi, xil / bytes_per_pixel, yil);
2296 }
2297 xil = 0;
2298 }
2299 if ((x1 == 15) && (y1 == 15) && (rxmo)) {
2300 x1--;
2301 xi = xmo * rx;
2302 yi = ymo * ry;
2303 xil = rxmo;
2304 yil = (yo - ymo +1) * ry;
2305 rxmo = 0;
2306 }
2307 }
2308 }
2309 nr_boxes = 0;
2310 }
2311 XDisplayUnlock();
2312 }
2313
2314 // Static display update (fixed frame rate, but incremental)
2315 static void update_display_static(driver_window *drv)
2316 {
2317 // Incremental update code
2318 unsigned wide = 0, high = 0, x1, x2, y1, y2, i, j;
2319 const video_mode &mode = drv->monitor.get_current_mode();
2320 int bytes_per_row = mode.bytes_per_row;
2321 int bytes_per_pixel = mode.bytes_per_row / mode.x;
2322 uint8 *p, *p2;
2323
2324 // Check for first line from top and first line from bottom that have changed
2325 y1 = 0;
2326 for (j=0; j<mode.y; j++) {
2327 if (memcmp(&the_buffer[j * bytes_per_row], &the_buffer_copy[j * bytes_per_row], bytes_per_row)) {
2328 y1 = j;
2329 break;
2330 }
2331 }
2332 y2 = y1 - 1;
2333 for (j=mode.y-1; j>=y1; j--) {
2334 if (memcmp(&the_buffer[j * bytes_per_row], &the_buffer_copy[j * bytes_per_row], bytes_per_row)) {
2335 y2 = j;
2336 break;
2337 }
2338 }
2339 high = y2 - y1 + 1;
2340
2341 // Check for first column from left and first column from right that have changed
2342 if (high) {
2343 if (mode.depth == VDEPTH_1BIT) {
2344 x1 = mode.x - 1;
2345 for (j=y1; j<=y2; j++) {
2346 p = &the_buffer[j * bytes_per_row];
2347 p2 = &the_buffer_copy[j * bytes_per_row];
2348 for (i=0; i<(x1>>3); i++) {
2349 if (*p != *p2) {
2350 x1 = i << 3;
2351 break;
2352 }
2353 p++; p2++;
2354 }
2355 }
2356 x2 = x1;
2357 for (j=y1; j<=y2; j++) {
2358 p = &the_buffer[j * bytes_per_row];
2359 p2 = &the_buffer_copy[j * bytes_per_row];
2360 p += bytes_per_row;
2361 p2 += bytes_per_row;
2362 for (i=(mode.x>>3); i>(x2>>3); i--) {
2363 p--; p2--;
2364 if (*p != *p2) {
2365 x2 = (i << 3) + 7;
2366 break;
2367 }
2368 }
2369 }
2370 wide = x2 - x1 + 1;
2371
2372 // Update copy of the_buffer
2373 if (high && wide) {
2374 for (j=y1; j<=y2; j++) {
2375 i = j * bytes_per_row + (x1 >> 3);
2376 memcpy(the_buffer_copy + i, the_buffer + i, wide >> 3);
2377 }
2378 }
2379
2380 } else {
2381 x1 = mode.x;
2382 for (j=y1; j<=y2; j++) {
2383 p = &the_buffer[j * bytes_per_row];
2384 p2 = &the_buffer_copy[j * bytes_per_row];
2385 for (i=0; i<x1*bytes_per_pixel; i++) {
2386 if (*p != *p2) {
2387 x1 = i / bytes_per_pixel;
2388 break;
2389 }
2390 p++; p2++;
2391 }
2392 }
2393 x2 = x1;
2394 for (j=y1; j<=y2; j++) {
2395 p = &the_buffer[j * bytes_per_row];
2396 p2 = &the_buffer_copy[j * bytes_per_row];
2397 p += bytes_per_row;
2398 p2 += bytes_per_row;
2399 for (i=mode.x*bytes_per_pixel; i>x2*bytes_per_pixel; i--) {
2400 p--;
2401 p2--;
2402 if (*p != *p2) {
2403 x2 = i / bytes_per_pixel;
2404 break;
2405 }
2406 }
2407 }
2408 wide = x2 - x1;
2409
2410 // Update copy of the_buffer
2411 if (high && wide) {
2412 for (j=y1; j<=y2; j++) {
2413 i = j * bytes_per_row + x1 * bytes_per_pixel;
2414 memcpy(the_buffer_copy + i, the_buffer + i, bytes_per_pixel * wide);
2415 }
2416 }
2417 }
2418 }
2419
2420 // Refresh display
2421 XDisplayLock();
2422 if (high && wide) {
2423 if (drv->have_shm)
2424 XShmPutImage(x_display, drv->w, drv->gc, drv->img, x1, y1, x1, y1, wide, high, 0);
2425 else
2426 XPutImage(x_display, drv->w, drv->gc, drv->img, x1, y1, x1, y1, wide, high);
2427 }
2428 XDisplayUnlock();
2429 }
2430
2431
2432 /*
2433 * Screen refresh functions
2434 */
2435
2436 // We suggest the compiler to inline the next two functions so that it
2437 // may specialise the code according to the current screen depth and
2438 // display type. A clever compiler would do that job by itself though...
2439
2440 // NOTE: update_display_vosf is inlined too
2441
2442 static inline void possibly_quit_dga_mode()
2443 {
2444 // Quit DGA mode if requested (something terrible has happened and we
2445 // want to give control back to the user)
2446 if (quit_full_screen) {
2447 quit_full_screen = false;
2448 delete drv;
2449 drv = NULL;
2450 }
2451 }
2452
2453 static inline void possibly_ungrab_mouse()
2454 {
2455 // Ungrab mouse if requested (something terrible has happened and we
2456 // want to give control back to the user)
2457 if (quit_full_screen) {
2458 quit_full_screen = false;
2459 if (drv)
2460 drv->ungrab_mouse();
2461 }
2462 }
2463
2464 static inline void handle_palette_changes(void)
2465 {
2466 LOCK_PALETTE;
2467
2468 if (x_palette_changed) {
2469 x_palette_changed = false;
2470 XDisplayLock();
2471 drv->update_palette();
2472 XDisplayUnlock();
2473 }
2474
2475 UNLOCK_PALETTE;
2476 }
2477
2478 static void video_refresh_dga(void)
2479 {
2480 // Quit DGA mode if requested
2481 possibly_quit_dga_mode();
2482 }
2483
2484 #ifdef ENABLE_VOSF
2485 #if REAL_ADDRESSING || DIRECT_ADDRESSING
2486 static void video_refresh_dga_vosf(void)
2487 {
2488 // Quit DGA mode if requested
2489 possibly_quit_dga_mode();
2490
2491 // Update display (VOSF variant)
2492 static int tick_counter = 0;
2493 if (++tick_counter >= frame_skip) {
2494 tick_counter = 0;
2495 if (mainBuffer.dirty) {
2496 LOCK_VOSF;
2497 update_display_dga_vosf(static_cast<driver_dga *>(drv));
2498 UNLOCK_VOSF;
2499 }
2500 }
2501 }
2502 #endif
2503
2504 static void video_refresh_window_vosf(void)
2505 {
2506 // Ungrab mouse if requested
2507 possibly_ungrab_mouse();
2508
2509 // Update display (VOSF variant)
2510 static int tick_counter = 0;
2511 if (++tick_counter >= frame_skip) {
2512 tick_counter = 0;
2513 if (mainBuffer.dirty) {
2514 XDisplayLock();
2515 LOCK_VOSF;
2516 update_display_window_vosf(static_cast<driver_window *>(drv));
2517 UNLOCK_VOSF;
2518 XSync(x_display, false); // Let the server catch up
2519 XDisplayUnlock();
2520 }
2521 }
2522 }
2523 #endif // def ENABLE_VOSF
2524
2525 static void video_refresh_window_static(void)
2526 {
2527 // Ungrab mouse if requested
2528 possibly_ungrab_mouse();
2529
2530 // Update display (static variant)
2531 static int tick_counter = 0;
2532 if (++tick_counter >= frame_skip) {
2533 tick_counter = 0;
2534 update_display_static(static_cast<driver_window *>(drv));
2535 }
2536 }
2537
2538 static void video_refresh_window_dynamic(void)
2539 {
2540 // Ungrab mouse if requested
2541 possibly_ungrab_mouse();
2542
2543 // Update display (dynamic variant)
2544 static int tick_counter = 0;
2545 tick_counter++;
2546 update_display_dynamic(tick_counter, static_cast<driver_window *>(drv));
2547 }
2548
2549
2550 /*
2551 * Thread for screen refresh, input handling etc.
2552 */
2553
2554 static void VideoRefreshInit(void)
2555 {
2556 // TODO: set up specialised 8bpp VideoRefresh handlers ?
2557 if (display_type == DISPLAY_DGA) {
2558 #if ENABLE_VOSF && (REAL_ADDRESSING || DIRECT_ADDRESSING)
2559 if (use_vosf)
2560 video_refresh = video_refresh_dga_vosf;
2561 else
2562 #endif
2563 video_refresh = video_refresh_dga;
2564 }
2565 else {
2566 #ifdef ENABLE_VOSF
2567 if (use_vosf)
2568 video_refresh = video_refresh_window_vosf;
2569 else
2570 #endif
2571 if (frame_skip == 0)
2572 video_refresh = video_refresh_window_dynamic;
2573 else
2574 video_refresh = video_refresh_window_static;
2575 }
2576 }
2577
2578 // This function is called on non-threaded platforms from a timer interrupt
2579 void VideoRefresh(void)
2580 {
2581 // We need to check redraw_thread_active to inhibit refreshed during
2582 // mode changes on non-threaded platforms
2583 if (!redraw_thread_active)
2584 return;
2585
2586 // Handle X events
2587 handle_events();
2588
2589 // Handle palette changes
2590 handle_palette_changes();
2591
2592 // Update display
2593 video_refresh();
2594 }
2595
2596 const int VIDEO_REFRESH_HZ = 60;
2597 const int VIDEO_REFRESH_DELAY = 1000000 / VIDEO_REFRESH_HZ;
2598
2599 #ifdef HAVE_PTHREADS
2600 static void *redraw_func(void *arg)
2601 {
2602 int fd = ConnectionNumber(x_display);
2603
2604 uint64 start = GetTicks_usec();
2605 int64 ticks = 0;
2606 uint64 next = GetTicks_usec() + VIDEO_REFRESH_DELAY;
2607
2608 while (!redraw_thread_cancel) {
2609
2610 int64 delay = next - GetTicks_usec();
2611 if (delay < -VIDEO_REFRESH_DELAY) {
2612
2613 // We are lagging far behind, so we reset the delay mechanism
2614 next = GetTicks_usec();
2615
2616 } else if (delay <= 0) {
2617
2618 // Delay expired, refresh display
2619 handle_events();
2620 handle_palette_changes();
2621 video_refresh();
2622 next += VIDEO_REFRESH_DELAY;
2623 ticks++;
2624
2625 } else {
2626
2627 // No display refresh pending, check for X events
2628 fd_set readfds;
2629 FD_ZERO(&readfds);
2630 FD_SET(fd, &readfds);
2631 struct timeval timeout;
2632 timeout.tv_sec = 0;
2633 timeout.tv_usec = delay;
2634 if (select(fd+1, &readfds, NULL, NULL, &timeout) > 0)
2635 handle_events();
2636 }
2637 }
2638
2639 uint64 end = GetTicks_usec();
2640 D(bug("%Ld refreshes in %Ld usec = %f refreshes/sec\n", ticks, end - start, ticks * 1000000.0 / (end - start)));
2641
2642 redraw_thread_cancel_ack = true;
2643 return NULL;
2644 }
2645 #endif