ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/video_x.cpp
Revision: 1.83
Committed: 2007-06-14T14:45:54Z (17 years, 5 months ago) by gbeauche
Branch: MAIN
Changes since 1.82: +31 -5 lines
Log Message:
Workaround video mode switch problems. IOW, make sure to always allocate
the frame buffer at the same base address.

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