ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/video_x.cpp
Revision: 1.86
Committed: 2010-02-21T12:00:42Z (14 years, 9 months ago) by cebix
Branch: MAIN
CVS Tags: HEAD
Changes since 1.85: +2 -2 lines
Log Message:
fixed warnings

File Contents

# Content
1 /*
2 * video_x.cpp - Video/graphics emulation, X11 specific stuff
3 *
4 * Basilisk II (C) 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/Xxf86dga.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), "/", 1) == 0)
1617 || (strncmp(XDisplayName(x_display_name), "unix:", 5) == 0);
1618
1619 // Init keycode translation
1620 keycode_init();
1621
1622 // Read prefs
1623 frame_skip = PrefsFindInt32("frameskip");
1624 mouse_wheel_mode = PrefsFindInt32("mousewheelmode");
1625 mouse_wheel_lines = PrefsFindInt32("mousewheellines");
1626
1627 // Find screen and root window
1628 screen = XDefaultScreen(x_display);
1629 rootwin = XRootWindow(x_display, screen);
1630
1631 // Get sorted list of available depths
1632 avail_depths = XListDepths(x_display, screen, &num_depths);
1633 if (avail_depths == NULL) {
1634 ErrorAlert(STR_UNSUPP_DEPTH_ERR);
1635 return false;
1636 }
1637 std::sort(avail_depths, avail_depths + num_depths);
1638
1639 #ifdef ENABLE_FBDEV_DGA
1640 // Frame buffer name
1641 char fb_name[20];
1642
1643 // Could do fbdev DGA?
1644 if ((fbdev_fd = open(FBDEVICE_FILE_NAME, O_RDWR)) != -1)
1645 has_dga = true;
1646 else
1647 has_dga = false;
1648 #endif
1649
1650 #ifdef ENABLE_XF86_DGA
1651 // DGA available?
1652 int dga_event_base, dga_error_base;
1653 if (local_X11 && XF86DGAQueryExtension(x_display, &dga_event_base, &dga_error_base)) {
1654 int dga_flags = 0;
1655 XF86DGAQueryDirectVideo(x_display, screen, &dga_flags);
1656 has_dga = dga_flags & XF86DGADirectPresent;
1657 } else
1658 has_dga = false;
1659 #endif
1660
1661 #ifdef ENABLE_XF86_VIDMODE
1662 // VidMode available?
1663 int vm_event_base, vm_error_base;
1664 has_vidmode = XF86VidModeQueryExtension(x_display, &vm_event_base, &vm_error_base);
1665 if (has_vidmode)
1666 XF86VidModeGetAllModeLines(x_display, screen, &num_x_video_modes, &x_video_modes);
1667 #endif
1668
1669 // Find black and white colors
1670 XParseColor(x_display, DefaultColormap(x_display, screen), "rgb:00/00/00", &black);
1671 XAllocColor(x_display, DefaultColormap(x_display, screen), &black);
1672 XParseColor(x_display, DefaultColormap(x_display, screen), "rgb:ff/ff/ff", &white);
1673 XAllocColor(x_display, DefaultColormap(x_display, screen), &white);
1674 black_pixel = BlackPixel(x_display, screen);
1675 white_pixel = WhitePixel(x_display, screen);
1676
1677 // Get screen mode from preferences
1678 const char *mode_str;
1679 if (classic_mode)
1680 mode_str = "win/512/342";
1681 else
1682 mode_str = PrefsFindString("screen");
1683
1684 // Determine display type and default dimensions
1685 int default_width = 512, default_height = 384;
1686 display_type = DISPLAY_WINDOW;
1687 if (mode_str) {
1688 if (sscanf(mode_str, "win/%d/%d", &default_width, &default_height) == 2) {
1689 display_type = DISPLAY_WINDOW;
1690 #ifdef ENABLE_FBDEV_DGA
1691 } else if (has_dga && sscanf(mode_str, "dga/%19s", fb_name) == 1) {
1692 display_type = DISPLAY_DGA;
1693 default_width = -1; default_height = -1; // use entire screen
1694 #endif
1695 #ifdef ENABLE_XF86_DGA
1696 } else if (has_dga && sscanf(mode_str, "dga/%d/%d", &default_width, &default_height) == 2) {
1697 display_type = DISPLAY_DGA;
1698 #endif
1699 }
1700 }
1701 if (default_width <= 0)
1702 default_width = DisplayWidth(x_display, screen);
1703 else if (default_width > DisplayWidth(x_display, screen))
1704 default_width = DisplayWidth(x_display, screen);
1705 if (default_height <= 0)
1706 default_height = DisplayHeight(x_display, screen);
1707 else if (default_height > DisplayHeight(x_display, screen))
1708 default_height = DisplayHeight(x_display, screen);
1709
1710 // Mac screen depth follows X depth
1711 video_depth default_depth = VDEPTH_1BIT;
1712 switch (DefaultDepth(x_display, screen)) {
1713 case 8:
1714 default_depth = VDEPTH_8BIT;
1715 break;
1716 case 15: case 16:
1717 default_depth = VDEPTH_16BIT;
1718 break;
1719 case 24: case 32:
1720 default_depth = VDEPTH_32BIT;
1721 break;
1722 }
1723
1724 // Construct list of supported modes
1725 if (display_type == DISPLAY_WINDOW) {
1726 if (classic)
1727 add_mode(512, 342, 0x80, 64, VDEPTH_1BIT);
1728 else {
1729 for (unsigned d=VDEPTH_1BIT; d<=VDEPTH_32BIT; d++) {
1730 if (find_visual_for_depth(video_depth(d)))
1731 add_window_modes(video_depth(d));
1732 }
1733 }
1734 } else
1735 add_mode(default_width, default_height, 0x80, TrivialBytesPerRow(default_width, default_depth), default_depth);
1736 if (VideoModes.empty()) {
1737 ErrorAlert(STR_NO_XVISUAL_ERR);
1738 return false;
1739 }
1740
1741 // Find requested default mode with specified dimensions
1742 uint32 default_id;
1743 std::vector<video_mode>::const_iterator i, end = VideoModes.end();
1744 for (i = VideoModes.begin(); i != end; ++i) {
1745 if (i->x == default_width && i->y == default_height && i->depth == default_depth) {
1746 default_id = i->resolution_id;
1747 break;
1748 }
1749 }
1750 if (i == end) { // not found, use first available mode
1751 default_depth = VideoModes[0].depth;
1752 default_id = VideoModes[0].resolution_id;
1753 }
1754
1755 #if DEBUG
1756 D(bug("Available video modes:\n"));
1757 for (i = VideoModes.begin(); i != end; ++i) {
1758 int bits = 1 << i->depth;
1759 if (bits == 16)
1760 bits = 15;
1761 else if (bits == 32)
1762 bits = 24;
1763 D(bug(" %dx%d (ID %02x), %d colors\n", i->x, i->y, i->resolution_id, 1 << bits));
1764 }
1765 #endif
1766
1767 // Create X11_monitor_desc for this (the only) display
1768 X11_monitor_desc *monitor = new X11_monitor_desc(VideoModes, default_depth, default_id);
1769 VideoMonitors.push_back(monitor);
1770
1771 // Open display
1772 return monitor->video_open();
1773 }
1774
1775
1776 /*
1777 * Deinitialization
1778 */
1779
1780 // Close display
1781 void X11_monitor_desc::video_close(void)
1782 {
1783 D(bug("video_close()\n"));
1784
1785 // Stop redraw thread
1786 #ifdef USE_PTHREADS_SERVICES
1787 if (redraw_thread_active) {
1788 redraw_thread_cancel = true;
1789 redraw_thread_cancel_ack = false;
1790 pthread_join(redraw_thread, NULL);
1791 while (!redraw_thread_cancel_ack) ;
1792 }
1793 #endif
1794 redraw_thread_active = false;
1795
1796 // Unlock frame buffer
1797 UNLOCK_FRAME_BUFFER;
1798 XSync(x_display, false);
1799 D(bug(" frame buffer unlocked\n"));
1800
1801 #ifdef ENABLE_VOSF
1802 if (use_vosf) {
1803 // Deinitialize VOSF
1804 video_vosf_exit();
1805 }
1806 #endif
1807
1808 // Close display
1809 delete drv;
1810 drv = NULL;
1811
1812 // Free colormaps
1813 if (cmap[0]) {
1814 XFreeColormap(x_display, cmap[0]);
1815 cmap[0] = 0;
1816 }
1817 if (cmap[1]) {
1818 XFreeColormap(x_display, cmap[1]);
1819 cmap[1] = 0;
1820 }
1821 }
1822
1823 void VideoExit(void)
1824 {
1825 // Close displays
1826 vector<monitor_desc *>::iterator i, end = VideoMonitors.end();
1827 for (i = VideoMonitors.begin(); i != end; ++i)
1828 dynamic_cast<X11_monitor_desc *>(*i)->video_close();
1829
1830 #ifdef ENABLE_XF86_VIDMODE
1831 // Free video mode list
1832 if (x_video_modes) {
1833 XFree(x_video_modes);
1834 x_video_modes = NULL;
1835 }
1836 #endif
1837
1838 #ifdef ENABLE_FBDEV_DGA
1839 // Close framebuffer device
1840 if (fbdev_fd >= 0) {
1841 close(fbdev_fd);
1842 fbdev_fd = -1;
1843 }
1844 #endif
1845
1846 // Free depth list
1847 if (avail_depths) {
1848 XFree(avail_depths);
1849 avail_depths = NULL;
1850 }
1851 }
1852
1853
1854 /*
1855 * Close down full-screen mode (if bringing up error alerts is unsafe while in full-screen mode)
1856 */
1857
1858 void VideoQuitFullScreen(void)
1859 {
1860 D(bug("VideoQuitFullScreen()\n"));
1861 quit_full_screen = true;
1862 }
1863
1864
1865 /*
1866 * Mac VBL interrupt
1867 */
1868
1869 void VideoInterrupt(void)
1870 {
1871 // Emergency quit requested? Then quit
1872 if (emerg_quit)
1873 QuitEmulator();
1874
1875 // Temporarily give up frame buffer lock (this is the point where
1876 // we are suspended when the user presses Ctrl-Tab)
1877 UNLOCK_FRAME_BUFFER;
1878 LOCK_FRAME_BUFFER;
1879 }
1880
1881
1882 /*
1883 * Set palette
1884 */
1885
1886 void X11_monitor_desc::set_palette(uint8 *pal, int num_in)
1887 {
1888 const video_mode &mode = get_current_mode();
1889
1890 LOCK_PALETTE;
1891
1892 // Convert colors to XColor array
1893 int num_out = 256;
1894 bool stretch = false;
1895 if (IsDirectMode(mode)) {
1896 // If X is in 565 mode we have to stretch the gamma table from 32 to 64 entries
1897 num_out = vis->map_entries;
1898 stretch = true;
1899 }
1900 XColor *p = x_palette;
1901 for (int i=0; i<num_out; i++) {
1902 int c = (stretch ? (i * num_in) / num_out : i);
1903 p->red = pal[c*3 + 0] * 0x0101;
1904 p->green = pal[c*3 + 1] * 0x0101;
1905 p->blue = pal[c*3 + 2] * 0x0101;
1906 p++;
1907 }
1908
1909 #ifdef ENABLE_VOSF
1910 // Recalculate pixel color expansion map
1911 if (!IsDirectMode(mode) && xdepth > 8) {
1912 for (int i=0; i<256; i++) {
1913 int c = i & (num_in-1); // If there are less than 256 colors, we repeat the first entries (this makes color expansion easier)
1914 ExpandMap[i] = map_rgb(pal[c*3+0], pal[c*3+1], pal[c*3+2], true);
1915 }
1916
1917 // We have to redraw everything because the interpretation of pixel values changed
1918 LOCK_VOSF;
1919 PFLAG_SET_ALL;
1920 UNLOCK_VOSF;
1921 memset(the_buffer_copy, 0, mode.bytes_per_row * mode.y);
1922 }
1923 #endif
1924
1925 // Tell redraw thread to change palette
1926 x_palette_changed = true;
1927
1928 UNLOCK_PALETTE;
1929 }
1930
1931
1932 /*
1933 * Switch video mode
1934 */
1935
1936 void X11_monitor_desc::switch_to_current_mode(void)
1937 {
1938 // Close and reopen display
1939 video_close();
1940 video_open();
1941
1942 if (drv == NULL) {
1943 ErrorAlert(STR_OPEN_WINDOW_ERR);
1944 QuitEmulator();
1945 }
1946 }
1947
1948
1949 /*
1950 * Translate key event to Mac keycode, returns -1 if no keycode was found
1951 * and -2 if the key was recognized as a hotkey
1952 */
1953
1954 static int kc_decode(KeySym ks, bool key_down)
1955 {
1956 switch (ks) {
1957 case XK_A: case XK_a: return 0x00;
1958 case XK_B: case XK_b: return 0x0b;
1959 case XK_C: case XK_c: return 0x08;
1960 case XK_D: case XK_d: return 0x02;
1961 case XK_E: case XK_e: return 0x0e;
1962 case XK_F: case XK_f: return 0x03;
1963 case XK_G: case XK_g: return 0x05;
1964 case XK_H: case XK_h: return 0x04;
1965 case XK_I: case XK_i: return 0x22;
1966 case XK_J: case XK_j: return 0x26;
1967 case XK_K: case XK_k: return 0x28;
1968 case XK_L: case XK_l: return 0x25;
1969 case XK_M: case XK_m: return 0x2e;
1970 case XK_N: case XK_n: return 0x2d;
1971 case XK_O: case XK_o: return 0x1f;
1972 case XK_P: case XK_p: return 0x23;
1973 case XK_Q: case XK_q: return 0x0c;
1974 case XK_R: case XK_r: return 0x0f;
1975 case XK_S: case XK_s: return 0x01;
1976 case XK_T: case XK_t: return 0x11;
1977 case XK_U: case XK_u: return 0x20;
1978 case XK_V: case XK_v: return 0x09;
1979 case XK_W: case XK_w: return 0x0d;
1980 case XK_X: case XK_x: return 0x07;
1981 case XK_Y: case XK_y: return 0x10;
1982 case XK_Z: case XK_z: return 0x06;
1983
1984 case XK_1: case XK_exclam: return 0x12;
1985 case XK_2: case XK_at: return 0x13;
1986 case XK_3: case XK_numbersign: return 0x14;
1987 case XK_4: case XK_dollar: return 0x15;
1988 case XK_5: case XK_percent: return 0x17;
1989 case XK_6: return 0x16;
1990 case XK_7: return 0x1a;
1991 case XK_8: return 0x1c;
1992 case XK_9: return 0x19;
1993 case XK_0: return 0x1d;
1994
1995 case XK_grave: case XK_asciitilde: return 0x0a;
1996 case XK_minus: case XK_underscore: return 0x1b;
1997 case XK_equal: case XK_plus: return 0x18;
1998 case XK_bracketleft: case XK_braceleft: return 0x21;
1999 case XK_bracketright: case XK_braceright: return 0x1e;
2000 case XK_backslash: case XK_bar: return 0x2a;
2001 case XK_semicolon: case XK_colon: return 0x29;
2002 case XK_apostrophe: case XK_quotedbl: return 0x27;
2003 case XK_comma: case XK_less: return 0x2b;
2004 case XK_period: case XK_greater: return 0x2f;
2005 case XK_slash: case XK_question: return 0x2c;
2006
2007 case XK_Tab: if (ctrl_down) {if (key_down) drv->suspend(); return -2;} else return 0x30;
2008 case XK_Return: return 0x24;
2009 case XK_space: return 0x31;
2010 case XK_BackSpace: return 0x33;
2011
2012 case XK_Delete: return 0x75;
2013 case XK_Insert: return 0x72;
2014 case XK_Home: case XK_Help: return 0x73;
2015 case XK_End: return 0x77;
2016 #ifdef __hpux
2017 case XK_Prior: return 0x74;
2018 case XK_Next: return 0x79;
2019 #else
2020 case XK_Page_Up: return 0x74;
2021 case XK_Page_Down: return 0x79;
2022 #endif
2023
2024 case XK_Control_L: return 0x36;
2025 case XK_Control_R: return 0x36;
2026 case XK_Shift_L: return 0x38;
2027 case XK_Shift_R: return 0x38;
2028 case XK_Alt_L: return 0x37;
2029 case XK_Alt_R: return 0x37;
2030 case XK_Meta_L: return 0x3a;
2031 case XK_Meta_R: return 0x3a;
2032 case XK_Menu: return 0x32;
2033 case XK_Caps_Lock: return 0x39;
2034 case XK_Num_Lock: return 0x47;
2035
2036 case XK_Up: return 0x3e;
2037 case XK_Down: return 0x3d;
2038 case XK_Left: return 0x3b;
2039 case XK_Right: return 0x3c;
2040
2041 case XK_Escape: if (ctrl_down) {if (key_down) { quit_full_screen = true; emerg_quit = true; } return -2;} else return 0x35;
2042
2043 case XK_F1: if (ctrl_down) {if (key_down) SysMountFirstFloppy(); return -2;} else return 0x7a;
2044 case XK_F2: return 0x78;
2045 case XK_F3: return 0x63;
2046 case XK_F4: return 0x76;
2047 case XK_F5: if (ctrl_down) {if (key_down) drv->toggle_mouse_grab(); return -2;} else return 0x60;
2048 case XK_F6: return 0x61;
2049 case XK_F7: return 0x62;
2050 case XK_F8: return 0x64;
2051 case XK_F9: return 0x65;
2052 case XK_F10: return 0x6d;
2053 case XK_F11: return 0x67;
2054 case XK_F12: return 0x6f;
2055
2056 case XK_Print: return 0x69;
2057 case XK_Scroll_Lock: return 0x6b;
2058 case XK_Pause: return 0x71;
2059
2060 #if defined(XK_KP_Prior) && defined(XK_KP_Left) && defined(XK_KP_Insert) && defined (XK_KP_End)
2061 case XK_KP_0: case XK_KP_Insert: return 0x52;
2062 case XK_KP_1: case XK_KP_End: return 0x53;
2063 case XK_KP_2: case XK_KP_Down: return 0x54;
2064 case XK_KP_3: case XK_KP_Next: return 0x55;
2065 case XK_KP_4: case XK_KP_Left: return 0x56;
2066 case XK_KP_5: case XK_KP_Begin: return 0x57;
2067 case XK_KP_6: case XK_KP_Right: return 0x58;
2068 case XK_KP_7: case XK_KP_Home: return 0x59;
2069 case XK_KP_8: case XK_KP_Up: return 0x5b;
2070 case XK_KP_9: case XK_KP_Prior: return 0x5c;
2071 case XK_KP_Decimal: case XK_KP_Delete: return 0x41;
2072 #else
2073 case XK_KP_0: return 0x52;
2074 case XK_KP_1: return 0x53;
2075 case XK_KP_2: return 0x54;
2076 case XK_KP_3: return 0x55;
2077 case XK_KP_4: return 0x56;
2078 case XK_KP_5: return 0x57;
2079 case XK_KP_6: return 0x58;
2080 case XK_KP_7: return 0x59;
2081 case XK_KP_8: return 0x5b;
2082 case XK_KP_9: return 0x5c;
2083 case XK_KP_Decimal: return 0x41;
2084 #endif
2085 case XK_KP_Add: return 0x45;
2086 case XK_KP_Subtract: return 0x4e;
2087 case XK_KP_Multiply: return 0x43;
2088 case XK_KP_Divide: return 0x4b;
2089 case XK_KP_Enter: return 0x4c;
2090 case XK_KP_Equal: return 0x51;
2091 }
2092 return -1;
2093 }
2094
2095 static int event2keycode(XKeyEvent &ev, bool key_down)
2096 {
2097 KeySym ks;
2098 int i = 0;
2099
2100 do {
2101 ks = XLookupKeysym(&ev, i++);
2102 int as = kc_decode(ks, key_down);
2103 if (as >= 0)
2104 return as;
2105 if (as == -2)
2106 return as;
2107 } while (ks != NoSymbol);
2108
2109 return -1;
2110 }
2111
2112
2113 /*
2114 * X event handling
2115 */
2116
2117 static void handle_events(void)
2118 {
2119 for (;;) {
2120 XEvent event;
2121 XDisplayLock();
2122
2123 if (!XCheckMaskEvent(x_display, eventmask, &event)) {
2124 // Handle clipboard events
2125 if (XCheckTypedEvent(x_display, SelectionRequest, &event))
2126 ClipboardSelectionRequest(&event.xselectionrequest);
2127 else if (XCheckTypedEvent(x_display, SelectionClear, &event))
2128 ClipboardSelectionClear(&event.xselectionclear);
2129
2130 // Window "close" widget clicked
2131 else if (XCheckTypedEvent(x_display, ClientMessage, &event)) {
2132 if (event.xclient.format == 32 && event.xclient.data.l[0] == WM_DELETE_WINDOW) {
2133 ADBKeyDown(0x7f); // Power key
2134 ADBKeyUp(0x7f);
2135 }
2136 }
2137 XDisplayUnlock();
2138 break;
2139 }
2140
2141 switch (event.type) {
2142
2143 // Mouse button
2144 case ButtonPress: {
2145 unsigned int button = event.xbutton.button;
2146 if (button < 4)
2147 ADBMouseDown(button - 1);
2148 else if (button < 6) { // Wheel mouse
2149 if (mouse_wheel_mode == 0) {
2150 int key = (button == 5) ? 0x79 : 0x74; // Page up/down
2151 ADBKeyDown(key);
2152 ADBKeyUp(key);
2153 } else {
2154 int key = (button == 5) ? 0x3d : 0x3e; // Cursor up/down
2155 for(int i=0; i<mouse_wheel_lines; i++) {
2156 ADBKeyDown(key);
2157 ADBKeyUp(key);
2158 }
2159 }
2160 }
2161 break;
2162 }
2163 case ButtonRelease: {
2164 unsigned int button = event.xbutton.button;
2165 if (button < 4)
2166 ADBMouseUp(button - 1);
2167 break;
2168 }
2169
2170 // Mouse moved
2171 case MotionNotify:
2172 drv->mouse_moved(event.xmotion.x, event.xmotion.y);
2173 break;
2174
2175 // Mouse entered window
2176 case EnterNotify:
2177 if (event.xcrossing.mode != NotifyGrab && event.xcrossing.mode != NotifyUngrab)
2178 drv->mouse_moved(event.xmotion.x, event.xmotion.y);
2179 break;
2180
2181 // Keyboard
2182 case KeyPress: {
2183 int code = -1;
2184 if (use_keycodes) {
2185 if (event2keycode(event.xkey, true) != -2) // This is called to process the hotkeys
2186 code = keycode_table[event.xkey.keycode & 0xff];
2187 } else
2188 code = event2keycode(event.xkey, true);
2189 if (code >= 0) {
2190 if (!emul_suspended) {
2191 if (code == 0x39) { // Caps Lock pressed
2192 if (caps_on) {
2193 ADBKeyUp(code);
2194 caps_on = false;
2195 } else {
2196 ADBKeyDown(code);
2197 caps_on = true;
2198 }
2199 } else
2200 ADBKeyDown(code);
2201 if (code == 0x36)
2202 ctrl_down = true;
2203 } else {
2204 if (code == 0x31)
2205 drv->resume(); // Space wakes us up
2206 }
2207 }
2208 break;
2209 }
2210 case KeyRelease: {
2211 int code = -1;
2212 if (use_keycodes) {
2213 if (event2keycode(event.xkey, false) != -2) // This is called to process the hotkeys
2214 code = keycode_table[event.xkey.keycode & 0xff];
2215 } else
2216 code = event2keycode(event.xkey, false);
2217 if (code >= 0 && code != 0x39) { // Don't propagate Caps Lock releases
2218 ADBKeyUp(code);
2219 if (code == 0x36)
2220 ctrl_down = false;
2221 }
2222 break;
2223 }
2224
2225 // Hidden parts exposed, force complete refresh of window
2226 case Expose:
2227 if (display_type == DISPLAY_WINDOW) {
2228 const video_mode &mode = VideoMonitors[0]->get_current_mode();
2229 #ifdef ENABLE_VOSF
2230 if (use_vosf) { // VOSF refresh
2231 LOCK_VOSF;
2232 PFLAG_SET_ALL;
2233 UNLOCK_VOSF;
2234 memset(the_buffer_copy, 0, mode.bytes_per_row * mode.y);
2235 }
2236 else
2237 #endif
2238 if (frame_skip == 0) { // Dynamic refresh
2239 int x1, y1;
2240 for (y1=0; y1<16; y1++)
2241 for (x1=0; x1<16; x1++)
2242 updt_box[x1][y1] = true;
2243 nr_boxes = 16 * 16;
2244 } else // Static refresh
2245 memset(the_buffer_copy, 0, mode.bytes_per_row * mode.y);
2246 }
2247 break;
2248 }
2249
2250 XDisplayUnlock();
2251 }
2252 }
2253
2254
2255 /*
2256 * Window display update
2257 */
2258
2259 // Dynamic display update (variable frame rate for each box)
2260 static void update_display_dynamic(int ticker, driver_window *drv)
2261 {
2262 int y1, y2, y2s, y2a, i, x1, xm, xmo, ymo, yo, yi, yil, xi;
2263 int xil = 0;
2264 int rxm = 0, rxmo = 0;
2265 const video_mode &mode = drv->monitor.get_current_mode();
2266 int bytes_per_row = mode.bytes_per_row;
2267 int bytes_per_pixel = mode.bytes_per_row / mode.x;
2268 int rx = mode.bytes_per_row / 16;
2269 int ry = mode.y / 16;
2270 int max_box;
2271
2272 y2s = sm_uptd[ticker % 8];
2273 y2a = 8;
2274 for (i = 0; i < 6; i++)
2275 if (ticker % (2 << i))
2276 break;
2277 max_box = sm_no_boxes[i];
2278
2279 if (y2a) {
2280 for (y1=0; y1<16; y1++) {
2281 for (y2=y2s; y2 < ry; y2 += y2a) {
2282 i = ((y1 * ry) + y2) * bytes_per_row;
2283 for (x1=0; x1<16; x1++, i += rx) {
2284 if (updt_box[x1][y1] == false) {
2285 if (memcmp(&the_buffer_copy[i], &the_buffer[i], rx)) {
2286 updt_box[x1][y1] = true;
2287 nr_boxes++;
2288 }
2289 }
2290 }
2291 }
2292 }
2293 }
2294
2295 XDisplayLock();
2296 if ((nr_boxes <= max_box) && (nr_boxes)) {
2297 for (y1=0; y1<16; y1++) {
2298 for (x1=0; x1<16; x1++) {
2299 if (updt_box[x1][y1] == true) {
2300 if (rxm == 0)
2301 xm = x1;
2302 rxm += rx;
2303 updt_box[x1][y1] = false;
2304 }
2305 if (((updt_box[x1+1][y1] == false) || (x1 == 15)) && (rxm)) {
2306 if ((rxmo != rxm) || (xmo != xm) || (yo != y1 - 1)) {
2307 if (rxmo) {
2308 xi = xmo * rx;
2309 yi = ymo * ry;
2310 xil = rxmo;
2311 yil = (yo - ymo +1) * ry;
2312 }
2313 rxmo = rxm;
2314 xmo = xm;
2315 ymo = y1;
2316 }
2317 rxm = 0;
2318 yo = y1;
2319 }
2320 if (xil) {
2321 i = (yi * bytes_per_row) + xi;
2322 for (y2=0; y2 < yil; y2++, i += bytes_per_row)
2323 memcpy(&the_buffer_copy[i], &the_buffer[i], xil);
2324 if (mode.depth == VDEPTH_1BIT) {
2325 if (drv->have_shm)
2326 XShmPutImage(x_display, drv->w, drv->gc, drv->img, xi * 8, yi, xi * 8, yi, xil * 8, yil, 0);
2327 else
2328 XPutImage(x_display, drv->w, drv->gc, drv->img, xi * 8, yi, xi * 8, yi, xil * 8, yil);
2329 } else {
2330 if (drv->have_shm)
2331 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);
2332 else
2333 XPutImage(x_display, drv->w, drv->gc, drv->img, xi / bytes_per_pixel, yi, xi / bytes_per_pixel, yi, xil / bytes_per_pixel, yil);
2334 }
2335 xil = 0;
2336 }
2337 if ((x1 == 15) && (y1 == 15) && (rxmo)) {
2338 x1--;
2339 xi = xmo * rx;
2340 yi = ymo * ry;
2341 xil = rxmo;
2342 yil = (yo - ymo +1) * ry;
2343 rxmo = 0;
2344 }
2345 }
2346 }
2347 nr_boxes = 0;
2348 }
2349 XDisplayUnlock();
2350 }
2351
2352 // Static display update (fixed frame rate, but incremental)
2353 static void update_display_static(driver_window *drv)
2354 {
2355 // Incremental update code
2356 unsigned wide = 0, high = 0, x1, x2, y1, y2, i, j;
2357 const video_mode &mode = drv->monitor.get_current_mode();
2358 int bytes_per_row = mode.bytes_per_row;
2359 int bytes_per_pixel = mode.bytes_per_row / mode.x;
2360 uint8 *p, *p2;
2361
2362 // Check for first line from top and first line from bottom that have changed
2363 y1 = 0;
2364 for (j=0; j<mode.y; j++) {
2365 if (memcmp(&the_buffer[j * bytes_per_row], &the_buffer_copy[j * bytes_per_row], bytes_per_row)) {
2366 y1 = j;
2367 break;
2368 }
2369 }
2370 y2 = y1 - 1;
2371 for (j=mode.y-1; j>=y1; j--) {
2372 if (memcmp(&the_buffer[j * bytes_per_row], &the_buffer_copy[j * bytes_per_row], bytes_per_row)) {
2373 y2 = j;
2374 break;
2375 }
2376 }
2377 high = y2 - y1 + 1;
2378
2379 // Check for first column from left and first column from right that have changed
2380 if (high) {
2381 if (mode.depth == VDEPTH_1BIT) {
2382 x1 = mode.x - 1;
2383 for (j=y1; j<=y2; j++) {
2384 p = &the_buffer[j * bytes_per_row];
2385 p2 = &the_buffer_copy[j * bytes_per_row];
2386 for (i=0; i<(x1>>3); i++) {
2387 if (*p != *p2) {
2388 x1 = i << 3;
2389 break;
2390 }
2391 p++; p2++;
2392 }
2393 }
2394 x2 = x1;
2395 for (j=y1; j<=y2; j++) {
2396 p = &the_buffer[j * bytes_per_row];
2397 p2 = &the_buffer_copy[j * bytes_per_row];
2398 p += bytes_per_row;
2399 p2 += bytes_per_row;
2400 for (i=(mode.x>>3); i>(x2>>3); i--) {
2401 p--; p2--;
2402 if (*p != *p2) {
2403 x2 = (i << 3) + 7;
2404 break;
2405 }
2406 }
2407 }
2408 wide = x2 - x1 + 1;
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 >> 3);
2414 memcpy(the_buffer_copy + i, the_buffer + i, wide >> 3);
2415 }
2416 }
2417
2418 } else {
2419 x1 = mode.x;
2420 for (j=y1; j<=y2; j++) {
2421 p = &the_buffer[j * bytes_per_row];
2422 p2 = &the_buffer_copy[j * bytes_per_row];
2423 for (i=0; i<x1*bytes_per_pixel; i++) {
2424 if (*p != *p2) {
2425 x1 = i / bytes_per_pixel;
2426 break;
2427 }
2428 p++; p2++;
2429 }
2430 }
2431 x2 = x1;
2432 for (j=y1; j<=y2; j++) {
2433 p = &the_buffer[j * bytes_per_row];
2434 p2 = &the_buffer_copy[j * bytes_per_row];
2435 p += bytes_per_row;
2436 p2 += bytes_per_row;
2437 for (i=mode.x*bytes_per_pixel; i>x2*bytes_per_pixel; i--) {
2438 p--;
2439 p2--;
2440 if (*p != *p2) {
2441 x2 = i / bytes_per_pixel;
2442 break;
2443 }
2444 }
2445 }
2446 wide = x2 - x1;
2447
2448 // Update copy of the_buffer
2449 if (high && wide) {
2450 for (j=y1; j<=y2; j++) {
2451 i = j * bytes_per_row + x1 * bytes_per_pixel;
2452 memcpy(the_buffer_copy + i, the_buffer + i, bytes_per_pixel * wide);
2453 }
2454 }
2455 }
2456 }
2457
2458 // Refresh display
2459 XDisplayLock();
2460 if (high && wide) {
2461 if (drv->have_shm)
2462 XShmPutImage(x_display, drv->w, drv->gc, drv->img, x1, y1, x1, y1, wide, high, 0);
2463 else
2464 XPutImage(x_display, drv->w, drv->gc, drv->img, x1, y1, x1, y1, wide, high);
2465 }
2466 XDisplayUnlock();
2467 }
2468
2469
2470 /*
2471 * Screen refresh functions
2472 */
2473
2474 // We suggest the compiler to inline the next two functions so that it
2475 // may specialise the code according to the current screen depth and
2476 // display type. A clever compiler would do that job by itself though...
2477
2478 // NOTE: update_display_vosf is inlined too
2479
2480 static inline void possibly_quit_dga_mode()
2481 {
2482 // Quit DGA mode if requested (something terrible has happened and we
2483 // want to give control back to the user)
2484 if (quit_full_screen) {
2485 quit_full_screen = false;
2486 delete drv;
2487 drv = NULL;
2488 }
2489 }
2490
2491 static inline void possibly_ungrab_mouse()
2492 {
2493 // Ungrab mouse if requested (something terrible has happened and we
2494 // want to give control back to the user)
2495 if (quit_full_screen) {
2496 quit_full_screen = false;
2497 if (drv)
2498 drv->ungrab_mouse();
2499 }
2500 }
2501
2502 static inline void handle_palette_changes(void)
2503 {
2504 LOCK_PALETTE;
2505
2506 if (x_palette_changed) {
2507 x_palette_changed = false;
2508 XDisplayLock();
2509 drv->update_palette();
2510 XDisplayUnlock();
2511 }
2512
2513 UNLOCK_PALETTE;
2514 }
2515
2516 static void video_refresh_dga(void)
2517 {
2518 // Quit DGA mode if requested
2519 possibly_quit_dga_mode();
2520 }
2521
2522 #ifdef ENABLE_VOSF
2523 #if REAL_ADDRESSING || DIRECT_ADDRESSING
2524 static void video_refresh_dga_vosf(void)
2525 {
2526 // Quit DGA mode if requested
2527 possibly_quit_dga_mode();
2528
2529 // Update display (VOSF variant)
2530 static int tick_counter = 0;
2531 if (++tick_counter >= frame_skip) {
2532 tick_counter = 0;
2533 if (mainBuffer.dirty) {
2534 LOCK_VOSF;
2535 update_display_dga_vosf(static_cast<driver_dga *>(drv));
2536 UNLOCK_VOSF;
2537 }
2538 }
2539 }
2540 #endif
2541
2542 static void video_refresh_window_vosf(void)
2543 {
2544 // Ungrab mouse if requested
2545 possibly_ungrab_mouse();
2546
2547 // Update display (VOSF variant)
2548 static int tick_counter = 0;
2549 if (++tick_counter >= frame_skip) {
2550 tick_counter = 0;
2551 if (mainBuffer.dirty) {
2552 XDisplayLock();
2553 LOCK_VOSF;
2554 update_display_window_vosf(static_cast<driver_window *>(drv));
2555 UNLOCK_VOSF;
2556 XSync(x_display, false); // Let the server catch up
2557 XDisplayUnlock();
2558 }
2559 }
2560 }
2561 #endif // def ENABLE_VOSF
2562
2563 static void video_refresh_window_static(void)
2564 {
2565 // Ungrab mouse if requested
2566 possibly_ungrab_mouse();
2567
2568 // Update display (static variant)
2569 static int tick_counter = 0;
2570 if (++tick_counter >= frame_skip) {
2571 tick_counter = 0;
2572 update_display_static(static_cast<driver_window *>(drv));
2573 }
2574 }
2575
2576 static void video_refresh_window_dynamic(void)
2577 {
2578 // Ungrab mouse if requested
2579 possibly_ungrab_mouse();
2580
2581 // Update display (dynamic variant)
2582 static int tick_counter = 0;
2583 tick_counter++;
2584 update_display_dynamic(tick_counter, static_cast<driver_window *>(drv));
2585 }
2586
2587
2588 /*
2589 * Thread for screen refresh, input handling etc.
2590 */
2591
2592 static void VideoRefreshInit(void)
2593 {
2594 // TODO: set up specialised 8bpp VideoRefresh handlers ?
2595 if (display_type == DISPLAY_DGA) {
2596 #if ENABLE_VOSF && (REAL_ADDRESSING || DIRECT_ADDRESSING)
2597 if (use_vosf)
2598 video_refresh = video_refresh_dga_vosf;
2599 else
2600 #endif
2601 video_refresh = video_refresh_dga;
2602 }
2603 else {
2604 #ifdef ENABLE_VOSF
2605 if (use_vosf)
2606 video_refresh = video_refresh_window_vosf;
2607 else
2608 #endif
2609 if (frame_skip == 0)
2610 video_refresh = video_refresh_window_dynamic;
2611 else
2612 video_refresh = video_refresh_window_static;
2613 }
2614 }
2615
2616 // This function is called on non-threaded platforms from a timer interrupt
2617 void VideoRefresh(void)
2618 {
2619 // We need to check redraw_thread_active to inhibit refreshed during
2620 // mode changes on non-threaded platforms
2621 if (!redraw_thread_active)
2622 return;
2623
2624 // Handle X events
2625 handle_events();
2626
2627 // Handle palette changes
2628 handle_palette_changes();
2629
2630 // Update display
2631 video_refresh();
2632 }
2633
2634 const int VIDEO_REFRESH_HZ = 60;
2635 const int VIDEO_REFRESH_DELAY = 1000000 / VIDEO_REFRESH_HZ;
2636
2637 #ifdef USE_PTHREADS_SERVICES
2638 static void *redraw_func(void *arg)
2639 {
2640 int fd = ConnectionNumber(x_display);
2641
2642 uint64 start = GetTicks_usec();
2643 int64 ticks = 0;
2644 uint64 next = GetTicks_usec() + VIDEO_REFRESH_DELAY;
2645
2646 while (!redraw_thread_cancel) {
2647
2648 int64 delay = next - GetTicks_usec();
2649 if (delay < -VIDEO_REFRESH_DELAY) {
2650
2651 // We are lagging far behind, so we reset the delay mechanism
2652 next = GetTicks_usec();
2653
2654 } else if (delay <= 0) {
2655
2656 // Delay expired, refresh display
2657 handle_events();
2658 handle_palette_changes();
2659 video_refresh();
2660 next += VIDEO_REFRESH_DELAY;
2661 ticks++;
2662
2663 } else {
2664
2665 // No display refresh pending, check for X events
2666 fd_set readfds;
2667 FD_ZERO(&readfds);
2668 FD_SET(fd, &readfds);
2669 struct timeval timeout;
2670 timeout.tv_sec = 0;
2671 timeout.tv_usec = delay;
2672 if (select(fd+1, &readfds, NULL, NULL, &timeout) > 0)
2673 handle_events();
2674 }
2675 }
2676
2677 uint64 end = GetTicks_usec();
2678 D(bug("%lld refreshes in %lld usec = %f refreshes/sec\n", ticks, end - start, ticks * 1000000.0 / (end - start)));
2679
2680 redraw_thread_cancel_ack = true;
2681 return NULL;
2682 }
2683 #endif