ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/video_x.cpp
Revision: 1.51
Committed: 2001-07-03T15:59:47Z (23 years, 4 months ago) by cebix
Branch: MAIN
Changes since 1.50: +145 -36 lines
Log Message:
- added support for platform-independant mutexes, currently only properly
  implemented under Unix
- adb.cpp uses mutexes for thread-safe mouse handling
- video_x.cpp: pressing Ctrl-F5 in windowed mode switches to a "grabbed"
  relative mouse mode, useful for some games
- video_x.cpp: fixed some bugs relating to the hotkeys (key releases are no
  longer treated as hotkeys)

File Contents

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