ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/video_x.cpp
Revision: 1.52
Committed: 2001-07-03T19:20:46Z (23 years ago) by cebix
Branch: MAIN
Changes since 1.51: +1 -0 lines
Log Message:
- improved compatibility of multiple depth/resolution handling with versions
  of MacOS before 7.6:
   - Apple mode IDs are now allocated contiguously from 0x80 (the video_*.cpp
     module must call video_init_depth_list() after adding all modes)
   - if the video driver didn't receive a GetVideoParameters call, it patches
     ScrnBase and the main GDevice upon a video mode switch (otherwise MacOS
     will continue to use the old frame buffer base)
   - the rowBytes values in the video parameters slot resources are correct
     for all bit depths

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 video_init_depth_list();
1414
1415 // Find requested default mode and open display
1416 if (VideoModes.size() == 1)
1417 return video_open(VideoModes[0]);
1418 else {
1419 // Find mode with specified dimensions
1420 std::vector<video_mode>::const_iterator i, end = VideoModes.end();
1421 for (i = VideoModes.begin(); i != end; ++i) {
1422 if (i->x == default_width && i->y == default_height && i->depth == default_depth)
1423 return video_open(*i);
1424 }
1425 return video_open(VideoModes[0]);
1426 }
1427 }
1428
1429
1430 /*
1431 * Deinitialization
1432 */
1433
1434 // Close display
1435 static void video_close(void)
1436 {
1437 // Stop redraw thread
1438 #ifdef HAVE_PTHREADS
1439 if (redraw_thread_active) {
1440 redraw_thread_cancel = true;
1441 #ifdef HAVE_PTHREAD_CANCEL
1442 pthread_cancel(redraw_thread);
1443 #endif
1444 pthread_join(redraw_thread, NULL);
1445 }
1446 #endif
1447 redraw_thread_active = false;
1448
1449 // Unlock frame buffer
1450 UNLOCK_FRAME_BUFFER;
1451 XSync(x_display, false);
1452
1453 #ifdef ENABLE_VOSF
1454 // Deinitialize VOSF
1455 if (use_vosf) {
1456 if (mainBuffer.pageInfo) {
1457 free(mainBuffer.pageInfo);
1458 mainBuffer.pageInfo = NULL;
1459 }
1460 if (mainBuffer.dirtyPages) {
1461 free(mainBuffer.dirtyPages);
1462 mainBuffer.dirtyPages = NULL;
1463 }
1464 }
1465 #endif
1466
1467 // Close display
1468 delete drv;
1469 drv = NULL;
1470 }
1471
1472 void VideoExit(void)
1473 {
1474 // Close display
1475 video_close();
1476
1477 // Free colormaps
1478 if (cmap[0]) {
1479 XFreeColormap(x_display, cmap[0]);
1480 cmap[0] = 0;
1481 }
1482 if (cmap[1]) {
1483 XFreeColormap(x_display, cmap[1]);
1484 cmap[1] = 0;
1485 }
1486
1487 #ifdef ENABLE_XF86_VIDMODE
1488 // Free video mode list
1489 if (x_video_modes) {
1490 XFree(x_video_modes);
1491 x_video_modes = NULL;
1492 }
1493 #endif
1494
1495 #ifdef ENABLE_FBDEV_DGA
1496 // Close framebuffer device
1497 if (fbdev_fd >= 0) {
1498 close(fbdev_fd);
1499 fbdev_fd = -1;
1500 }
1501 #endif
1502 }
1503
1504
1505 /*
1506 * Close down full-screen mode (if bringing up error alerts is unsafe while in full-screen mode)
1507 */
1508
1509 void VideoQuitFullScreen(void)
1510 {
1511 D(bug("VideoQuitFullScreen()\n"));
1512 quit_full_screen = true;
1513 }
1514
1515
1516 /*
1517 * Mac VBL interrupt
1518 */
1519
1520 void VideoInterrupt(void)
1521 {
1522 // Emergency quit requested? Then quit
1523 if (emerg_quit)
1524 QuitEmulator();
1525
1526 // Temporarily give up frame buffer lock (this is the point where
1527 // we are suspended when the user presses Ctrl-Tab)
1528 UNLOCK_FRAME_BUFFER;
1529 LOCK_FRAME_BUFFER;
1530 }
1531
1532
1533 /*
1534 * Set palette
1535 */
1536
1537 void video_set_palette(uint8 *pal, int num_in)
1538 {
1539 LOCK_PALETTE;
1540
1541 // Convert colors to XColor array
1542 int num_out = 256;
1543 if (IsDirectMode(VideoMonitor.mode)) {
1544 // If X is in 565 mode we have to stretch the gamma table from 32 to 64 entries
1545 num_out = vis->map_entries;
1546 }
1547 XColor *p = palette;
1548 for (int i=0; i<num_out; i++) {
1549 int c = (i * num_in) / num_out;
1550 p->red = pal[c*3 + 0] * 0x0101;
1551 p->green = pal[c*3 + 1] * 0x0101;
1552 p->blue = pal[c*3 + 2] * 0x0101;
1553 if (vis->c_class == PseudoColor)
1554 p->pixel = i;
1555 p->flags = DoRed | DoGreen | DoBlue;
1556 p++;
1557 }
1558
1559 #ifdef ENABLE_VOSF
1560 // Recalculate pixel color expansion map
1561 if (!IsDirectMode(VideoMonitor.mode) && (vis->c_class == TrueColor || vis->c_class == DirectColor)) {
1562 for (int i=0; i<256; i++) {
1563 int c = i & (num_in-1); // If there are less than 256 colors, we repeat the first entries (this makes color expansion easier)
1564 ExpandMap[i] = map_rgb(pal[c*3+0], pal[c*3+1], pal[c*3+2]);
1565 }
1566
1567 // We have to redraw everything because the interpretation of pixel values changed
1568 LOCK_VOSF;
1569 PFLAG_SET_ALL;
1570 UNLOCK_VOSF;
1571 memset(the_buffer_copy, 0, VideoMonitor.mode.bytes_per_row * VideoMonitor.mode.y);
1572 }
1573 #endif
1574
1575 // Tell redraw thread to change palette
1576 palette_changed = true;
1577
1578 UNLOCK_PALETTE;
1579 }
1580
1581
1582 /*
1583 * Switch video mode
1584 */
1585
1586 void video_switch_to_mode(const video_mode &mode)
1587 {
1588 // Close and reopen display
1589 video_close();
1590 video_open(mode);
1591
1592 if (drv == NULL) {
1593 ErrorAlert(STR_OPEN_WINDOW_ERR);
1594 QuitEmulator();
1595 }
1596 }
1597
1598
1599 /*
1600 * Translate key event to Mac keycode, returns -1 if no keycode was found
1601 * and -2 if the key was recognized as a hotkey
1602 */
1603
1604 static int kc_decode(KeySym ks, bool key_down)
1605 {
1606 switch (ks) {
1607 case XK_A: case XK_a: return 0x00;
1608 case XK_B: case XK_b: return 0x0b;
1609 case XK_C: case XK_c: return 0x08;
1610 case XK_D: case XK_d: return 0x02;
1611 case XK_E: case XK_e: return 0x0e;
1612 case XK_F: case XK_f: return 0x03;
1613 case XK_G: case XK_g: return 0x05;
1614 case XK_H: case XK_h: return 0x04;
1615 case XK_I: case XK_i: return 0x22;
1616 case XK_J: case XK_j: return 0x26;
1617 case XK_K: case XK_k: return 0x28;
1618 case XK_L: case XK_l: return 0x25;
1619 case XK_M: case XK_m: return 0x2e;
1620 case XK_N: case XK_n: return 0x2d;
1621 case XK_O: case XK_o: return 0x1f;
1622 case XK_P: case XK_p: return 0x23;
1623 case XK_Q: case XK_q: return 0x0c;
1624 case XK_R: case XK_r: return 0x0f;
1625 case XK_S: case XK_s: return 0x01;
1626 case XK_T: case XK_t: return 0x11;
1627 case XK_U: case XK_u: return 0x20;
1628 case XK_V: case XK_v: return 0x09;
1629 case XK_W: case XK_w: return 0x0d;
1630 case XK_X: case XK_x: return 0x07;
1631 case XK_Y: case XK_y: return 0x10;
1632 case XK_Z: case XK_z: return 0x06;
1633
1634 case XK_1: case XK_exclam: return 0x12;
1635 case XK_2: case XK_at: return 0x13;
1636 case XK_3: case XK_numbersign: return 0x14;
1637 case XK_4: case XK_dollar: return 0x15;
1638 case XK_5: case XK_percent: return 0x17;
1639 case XK_6: return 0x16;
1640 case XK_7: return 0x1a;
1641 case XK_8: return 0x1c;
1642 case XK_9: return 0x19;
1643 case XK_0: return 0x1d;
1644
1645 case XK_grave: case XK_asciitilde: return 0x0a;
1646 case XK_minus: case XK_underscore: return 0x1b;
1647 case XK_equal: case XK_plus: return 0x18;
1648 case XK_bracketleft: case XK_braceleft: return 0x21;
1649 case XK_bracketright: case XK_braceright: return 0x1e;
1650 case XK_backslash: case XK_bar: return 0x2a;
1651 case XK_semicolon: case XK_colon: return 0x29;
1652 case XK_apostrophe: case XK_quotedbl: return 0x27;
1653 case XK_comma: case XK_less: return 0x2b;
1654 case XK_period: case XK_greater: return 0x2f;
1655 case XK_slash: case XK_question: return 0x2c;
1656
1657 case XK_Tab: if (ctrl_down) {if (key_down) drv->suspend(); return -2;} else return 0x30;
1658 case XK_Return: return 0x24;
1659 case XK_space: return 0x31;
1660 case XK_BackSpace: return 0x33;
1661
1662 case XK_Delete: return 0x75;
1663 case XK_Insert: return 0x72;
1664 case XK_Home: case XK_Help: return 0x73;
1665 case XK_End: return 0x77;
1666 #ifdef __hpux
1667 case XK_Prior: return 0x74;
1668 case XK_Next: return 0x79;
1669 #else
1670 case XK_Page_Up: return 0x74;
1671 case XK_Page_Down: return 0x79;
1672 #endif
1673
1674 case XK_Control_L: return 0x36;
1675 case XK_Control_R: return 0x36;
1676 case XK_Shift_L: return 0x38;
1677 case XK_Shift_R: return 0x38;
1678 case XK_Alt_L: return 0x37;
1679 case XK_Alt_R: return 0x37;
1680 case XK_Meta_L: return 0x3a;
1681 case XK_Meta_R: return 0x3a;
1682 case XK_Menu: return 0x32;
1683 case XK_Caps_Lock: return 0x39;
1684 case XK_Num_Lock: return 0x47;
1685
1686 case XK_Up: return 0x3e;
1687 case XK_Down: return 0x3d;
1688 case XK_Left: return 0x3b;
1689 case XK_Right: return 0x3c;
1690
1691 case XK_Escape: if (ctrl_down) {if (key_down) { quit_full_screen = true; emerg_quit = true; } return -2;} else return 0x35;
1692
1693 case XK_F1: if (ctrl_down) {if (key_down) SysMountFirstFloppy(); return -2;} else return 0x7a;
1694 case XK_F2: return 0x78;
1695 case XK_F3: return 0x63;
1696 case XK_F4: return 0x76;
1697 case XK_F5: if (ctrl_down) {if (key_down) drv->toggle_mouse_grab(); return -2;} else return 0x60;
1698 case XK_F6: return 0x61;
1699 case XK_F7: return 0x62;
1700 case XK_F8: return 0x64;
1701 case XK_F9: return 0x65;
1702 case XK_F10: return 0x6d;
1703 case XK_F11: return 0x67;
1704 case XK_F12: return 0x6f;
1705
1706 case XK_Print: return 0x69;
1707 case XK_Scroll_Lock: return 0x6b;
1708 case XK_Pause: return 0x71;
1709
1710 #if defined(XK_KP_Prior) && defined(XK_KP_Left) && defined(XK_KP_Insert) && defined (XK_KP_End)
1711 case XK_KP_0: case XK_KP_Insert: return 0x52;
1712 case XK_KP_1: case XK_KP_End: return 0x53;
1713 case XK_KP_2: case XK_KP_Down: return 0x54;
1714 case XK_KP_3: case XK_KP_Next: return 0x55;
1715 case XK_KP_4: case XK_KP_Left: return 0x56;
1716 case XK_KP_5: case XK_KP_Begin: return 0x57;
1717 case XK_KP_6: case XK_KP_Right: return 0x58;
1718 case XK_KP_7: case XK_KP_Home: return 0x59;
1719 case XK_KP_8: case XK_KP_Up: return 0x5b;
1720 case XK_KP_9: case XK_KP_Prior: return 0x5c;
1721 case XK_KP_Decimal: case XK_KP_Delete: return 0x41;
1722 #else
1723 case XK_KP_0: return 0x52;
1724 case XK_KP_1: return 0x53;
1725 case XK_KP_2: return 0x54;
1726 case XK_KP_3: return 0x55;
1727 case XK_KP_4: return 0x56;
1728 case XK_KP_5: return 0x57;
1729 case XK_KP_6: return 0x58;
1730 case XK_KP_7: return 0x59;
1731 case XK_KP_8: return 0x5b;
1732 case XK_KP_9: return 0x5c;
1733 case XK_KP_Decimal: return 0x41;
1734 #endif
1735 case XK_KP_Add: return 0x45;
1736 case XK_KP_Subtract: return 0x4e;
1737 case XK_KP_Multiply: return 0x43;
1738 case XK_KP_Divide: return 0x4b;
1739 case XK_KP_Enter: return 0x4c;
1740 case XK_KP_Equal: return 0x51;
1741 }
1742 return -1;
1743 }
1744
1745 static int event2keycode(XKeyEvent &ev, bool key_down)
1746 {
1747 KeySym ks;
1748 int i = 0;
1749
1750 do {
1751 ks = XLookupKeysym(&ev, i++);
1752 int as = kc_decode(ks, key_down);
1753 if (as >= 0)
1754 return as;
1755 if (as == -2)
1756 return as;
1757 } while (ks != NoSymbol);
1758
1759 return -1;
1760 }
1761
1762
1763 /*
1764 * X event handling
1765 */
1766
1767 static void handle_events(void)
1768 {
1769 while (XPending(x_display)) {
1770 XEvent event;
1771 XNextEvent(x_display, &event);
1772
1773 switch (event.type) {
1774 // Mouse button
1775 case ButtonPress: {
1776 unsigned int button = event.xbutton.button;
1777 if (button < 4)
1778 ADBMouseDown(button - 1);
1779 else if (button < 6) { // Wheel mouse
1780 if (mouse_wheel_mode == 0) {
1781 int key = (button == 5) ? 0x79 : 0x74; // Page up/down
1782 ADBKeyDown(key);
1783 ADBKeyUp(key);
1784 } else {
1785 int key = (button == 5) ? 0x3d : 0x3e; // Cursor up/down
1786 for(int i=0; i<mouse_wheel_lines; i++) {
1787 ADBKeyDown(key);
1788 ADBKeyUp(key);
1789 }
1790 }
1791 }
1792 break;
1793 }
1794 case ButtonRelease: {
1795 unsigned int button = event.xbutton.button;
1796 if (button < 4)
1797 ADBMouseUp(button - 1);
1798 break;
1799 }
1800
1801 // Mouse moved
1802 case MotionNotify:
1803 drv->mouse_moved(event.xmotion.x, event.xmotion.y);
1804 break;
1805
1806 // Keyboard
1807 case KeyPress: {
1808 int code = -1;
1809 if (use_keycodes) {
1810 if (event2keycode(event.xkey, true) != -2) // This is called to process the hotkeys
1811 code = keycode_table[event.xkey.keycode & 0xff];
1812 } else
1813 code = event2keycode(event.xkey, true);
1814 if (code >= 0) {
1815 if (!emul_suspended) {
1816 if (code == 0x39) { // Caps Lock pressed
1817 if (caps_on) {
1818 ADBKeyUp(code);
1819 caps_on = false;
1820 } else {
1821 ADBKeyDown(code);
1822 caps_on = true;
1823 }
1824 } else
1825 ADBKeyDown(code);
1826 if (code == 0x36)
1827 ctrl_down = true;
1828 } else {
1829 if (code == 0x31)
1830 drv->resume(); // Space wakes us up
1831 }
1832 }
1833 break;
1834 }
1835 case KeyRelease: {
1836 int code = -1;
1837 if (use_keycodes) {
1838 if (event2keycode(event.xkey, false) != -2) // This is called to process the hotkeys
1839 code = keycode_table[event.xkey.keycode & 0xff];
1840 } else
1841 code = event2keycode(event.xkey, false);
1842 if (code >= 0 && code != 0x39) { // Don't propagate Caps Lock releases
1843 ADBKeyUp(code);
1844 if (code == 0x36)
1845 ctrl_down = false;
1846 }
1847 break;
1848 }
1849
1850 // Hidden parts exposed, force complete refresh of window
1851 case Expose:
1852 if (display_type == DISPLAY_WINDOW) {
1853 #ifdef ENABLE_VOSF
1854 if (use_vosf) { // VOSF refresh
1855 LOCK_VOSF;
1856 PFLAG_SET_ALL;
1857 UNLOCK_VOSF;
1858 memset(the_buffer_copy, 0, VideoMonitor.mode.bytes_per_row * VideoMonitor.mode.y);
1859 }
1860 else
1861 #endif
1862 if (frame_skip == 0) { // Dynamic refresh
1863 int x1, y1;
1864 for (y1=0; y1<16; y1++)
1865 for (x1=0; x1<16; x1++)
1866 updt_box[x1][y1] = true;
1867 nr_boxes = 16 * 16;
1868 } else // Static refresh
1869 memset(the_buffer_copy, 0, VideoMonitor.mode.bytes_per_row * VideoMonitor.mode.y);
1870 }
1871 break;
1872
1873 // Window "close" widget clicked
1874 case ClientMessage:
1875 if (event.xclient.format == 32 && event.xclient.data.l[0] == WM_DELETE_WINDOW) {
1876 ADBKeyDown(0x7f); // Power key
1877 ADBKeyUp(0x7f);
1878 }
1879 break;
1880 }
1881 }
1882 }
1883
1884
1885 /*
1886 * Window display update
1887 */
1888
1889 // Dynamic display update (variable frame rate for each box)
1890 static void update_display_dynamic(int ticker, driver_window *drv)
1891 {
1892 int y1, y2, y2s, y2a, i, x1, xm, xmo, ymo, yo, yi, yil, xi;
1893 int xil = 0;
1894 int rxm = 0, rxmo = 0;
1895 int bytes_per_row = VideoMonitor.mode.bytes_per_row;
1896 int bytes_per_pixel = VideoMonitor.mode.bytes_per_row / VideoMonitor.mode.x;
1897 int rx = VideoMonitor.mode.bytes_per_row / 16;
1898 int ry = VideoMonitor.mode.y / 16;
1899 int max_box;
1900
1901 y2s = sm_uptd[ticker % 8];
1902 y2a = 8;
1903 for (i = 0; i < 6; i++)
1904 if (ticker % (2 << i))
1905 break;
1906 max_box = sm_no_boxes[i];
1907
1908 if (y2a) {
1909 for (y1=0; y1<16; y1++) {
1910 for (y2=y2s; y2 < ry; y2 += y2a) {
1911 i = ((y1 * ry) + y2) * bytes_per_row;
1912 for (x1=0; x1<16; x1++, i += rx) {
1913 if (updt_box[x1][y1] == false) {
1914 if (memcmp(&the_buffer_copy[i], &the_buffer[i], rx)) {
1915 updt_box[x1][y1] = true;
1916 nr_boxes++;
1917 }
1918 }
1919 }
1920 }
1921 }
1922 }
1923
1924 if ((nr_boxes <= max_box) && (nr_boxes)) {
1925 for (y1=0; y1<16; y1++) {
1926 for (x1=0; x1<16; x1++) {
1927 if (updt_box[x1][y1] == true) {
1928 if (rxm == 0)
1929 xm = x1;
1930 rxm += rx;
1931 updt_box[x1][y1] = false;
1932 }
1933 if (((updt_box[x1+1][y1] == false) || (x1 == 15)) && (rxm)) {
1934 if ((rxmo != rxm) || (xmo != xm) || (yo != y1 - 1)) {
1935 if (rxmo) {
1936 xi = xmo * rx;
1937 yi = ymo * ry;
1938 xil = rxmo;
1939 yil = (yo - ymo +1) * ry;
1940 }
1941 rxmo = rxm;
1942 xmo = xm;
1943 ymo = y1;
1944 }
1945 rxm = 0;
1946 yo = y1;
1947 }
1948 if (xil) {
1949 i = (yi * bytes_per_row) + xi;
1950 for (y2=0; y2 < yil; y2++, i += bytes_per_row)
1951 memcpy(&the_buffer_copy[i], &the_buffer[i], xil);
1952 if (VideoMonitor.mode.depth == VDEPTH_1BIT) {
1953 if (drv->have_shm)
1954 XShmPutImage(x_display, drv->w, drv->gc, drv->img, xi * 8, yi, xi * 8, yi, xil * 8, yil, 0);
1955 else
1956 XPutImage(x_display, drv->w, drv->gc, drv->img, xi * 8, yi, xi * 8, yi, xil * 8, yil);
1957 } else {
1958 if (drv->have_shm)
1959 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);
1960 else
1961 XPutImage(x_display, drv->w, drv->gc, drv->img, xi / bytes_per_pixel, yi, xi / bytes_per_pixel, yi, xil / bytes_per_pixel, yil);
1962 }
1963 xil = 0;
1964 }
1965 if ((x1 == 15) && (y1 == 15) && (rxmo)) {
1966 x1--;
1967 xi = xmo * rx;
1968 yi = ymo * ry;
1969 xil = rxmo;
1970 yil = (yo - ymo +1) * ry;
1971 rxmo = 0;
1972 }
1973 }
1974 }
1975 nr_boxes = 0;
1976 }
1977 }
1978
1979 // Static display update (fixed frame rate, but incremental)
1980 static void update_display_static(driver_window *drv)
1981 {
1982 // Incremental update code
1983 int wide = 0, high = 0, x1, x2, y1, y2, i, j;
1984 int bytes_per_row = VideoMonitor.mode.bytes_per_row;
1985 int bytes_per_pixel = VideoMonitor.mode.bytes_per_row / VideoMonitor.mode.x;
1986 uint8 *p, *p2;
1987
1988 // Check for first line from top and first line from bottom that have changed
1989 y1 = 0;
1990 for (j=0; j<VideoMonitor.mode.y; j++) {
1991 if (memcmp(&the_buffer[j * bytes_per_row], &the_buffer_copy[j * bytes_per_row], bytes_per_row)) {
1992 y1 = j;
1993 break;
1994 }
1995 }
1996 y2 = y1 - 1;
1997 for (j=VideoMonitor.mode.y-1; j>=y1; j--) {
1998 if (memcmp(&the_buffer[j * bytes_per_row], &the_buffer_copy[j * bytes_per_row], bytes_per_row)) {
1999 y2 = j;
2000 break;
2001 }
2002 }
2003 high = y2 - y1 + 1;
2004
2005 // Check for first column from left and first column from right that have changed
2006 if (high) {
2007 if (VideoMonitor.mode.depth == VDEPTH_1BIT) {
2008 x1 = VideoMonitor.mode.x - 1;
2009 for (j=y1; j<=y2; j++) {
2010 p = &the_buffer[j * bytes_per_row];
2011 p2 = &the_buffer_copy[j * bytes_per_row];
2012 for (i=0; i<(x1>>3); i++) {
2013 if (*p != *p2) {
2014 x1 = i << 3;
2015 break;
2016 }
2017 p++; p2++;
2018 }
2019 }
2020 x2 = x1;
2021 for (j=y1; j<=y2; j++) {
2022 p = &the_buffer[j * bytes_per_row];
2023 p2 = &the_buffer_copy[j * bytes_per_row];
2024 p += bytes_per_row;
2025 p2 += bytes_per_row;
2026 for (i=(VideoMonitor.mode.x>>3); i>(x2>>3); i--) {
2027 p--; p2--;
2028 if (*p != *p2) {
2029 x2 = (i << 3) + 7;
2030 break;
2031 }
2032 }
2033 }
2034 wide = x2 - x1 + 1;
2035
2036 // Update copy of the_buffer
2037 if (high && wide) {
2038 for (j=y1; j<=y2; j++) {
2039 i = j * bytes_per_row + (x1 >> 3);
2040 memcpy(the_buffer_copy + i, the_buffer + i, wide >> 3);
2041 }
2042 }
2043
2044 } else {
2045 x1 = VideoMonitor.mode.x;
2046 for (j=y1; j<=y2; j++) {
2047 p = &the_buffer[j * bytes_per_row];
2048 p2 = &the_buffer_copy[j * bytes_per_row];
2049 for (i=0; i<x1*bytes_per_pixel; i++) {
2050 if (*p != *p2) {
2051 x1 = i / bytes_per_pixel;
2052 break;
2053 }
2054 p++; p2++;
2055 }
2056 }
2057 x2 = x1;
2058 for (j=y1; j<=y2; j++) {
2059 p = &the_buffer[j * bytes_per_row];
2060 p2 = &the_buffer_copy[j * bytes_per_row];
2061 p += bytes_per_row;
2062 p2 += bytes_per_row;
2063 for (i=VideoMonitor.mode.x*bytes_per_pixel; i>x2*bytes_per_pixel; i--) {
2064 p--;
2065 p2--;
2066 if (*p != *p2) {
2067 x2 = i / bytes_per_pixel;
2068 break;
2069 }
2070 }
2071 }
2072 wide = x2 - x1;
2073
2074 // Update copy of the_buffer
2075 if (high && wide) {
2076 for (j=y1; j<=y2; j++) {
2077 i = j * bytes_per_row + x1 * bytes_per_pixel;
2078 memcpy(the_buffer_copy + i, the_buffer + i, bytes_per_pixel * wide);
2079 }
2080 }
2081 }
2082 }
2083
2084 // Refresh display
2085 if (high && wide) {
2086 if (drv->have_shm)
2087 XShmPutImage(x_display, drv->w, drv->gc, drv->img, x1, y1, x1, y1, wide, high, 0);
2088 else
2089 XPutImage(x_display, drv->w, drv->gc, drv->img, x1, y1, x1, y1, wide, high);
2090 }
2091 }
2092
2093
2094 /*
2095 * Screen refresh functions
2096 */
2097
2098 // We suggest the compiler to inline the next two functions so that it
2099 // may specialise the code according to the current screen depth and
2100 // display type. A clever compiler would do that job by itself though...
2101
2102 // NOTE: update_display_vosf is inlined too
2103
2104 static inline void possibly_quit_dga_mode()
2105 {
2106 // Quit DGA mode if requested (something terrible has happened and we
2107 // want to give control back to the user)
2108 if (quit_full_screen) {
2109 quit_full_screen = false;
2110 delete drv;
2111 drv = NULL;
2112 }
2113 }
2114
2115 static inline void possibly_ungrab_mouse()
2116 {
2117 // Ungrab mouse if requested (something terrible has happened and we
2118 // want to give control back to the user)
2119 if (quit_full_screen) {
2120 quit_full_screen = false;
2121 if (drv)
2122 drv->ungrab_mouse();
2123 }
2124 }
2125
2126 static inline void handle_palette_changes(void)
2127 {
2128 LOCK_PALETTE;
2129
2130 if (palette_changed) {
2131 palette_changed = false;
2132 drv->update_palette();
2133 }
2134
2135 UNLOCK_PALETTE;
2136 }
2137
2138 static void video_refresh_dga(void)
2139 {
2140 // Quit DGA mode if requested
2141 possibly_quit_dga_mode();
2142
2143 // Handle X events
2144 handle_events();
2145
2146 // Handle palette changes
2147 handle_palette_changes();
2148 }
2149
2150 #ifdef ENABLE_VOSF
2151 #if REAL_ADDRESSING || DIRECT_ADDRESSING
2152 static void video_refresh_dga_vosf(void)
2153 {
2154 // Quit DGA mode if requested
2155 possibly_quit_dga_mode();
2156
2157 // Handle X events
2158 handle_events();
2159
2160 // Handle palette changes
2161 handle_palette_changes();
2162
2163 // Update display (VOSF variant)
2164 static int tick_counter = 0;
2165 if (++tick_counter >= frame_skip) {
2166 tick_counter = 0;
2167 if (mainBuffer.dirty) {
2168 LOCK_VOSF;
2169 update_display_dga_vosf();
2170 UNLOCK_VOSF;
2171 }
2172 }
2173 }
2174 #endif
2175
2176 static void video_refresh_window_vosf(void)
2177 {
2178 // Ungrab mouse if requested
2179 possibly_ungrab_mouse();
2180
2181 // Handle X events
2182 handle_events();
2183
2184 // Handle palette changes
2185 handle_palette_changes();
2186
2187 // Update display (VOSF variant)
2188 static int tick_counter = 0;
2189 if (++tick_counter >= frame_skip) {
2190 tick_counter = 0;
2191 if (mainBuffer.dirty) {
2192 LOCK_VOSF;
2193 update_display_window_vosf(static_cast<driver_window *>(drv));
2194 UNLOCK_VOSF;
2195 XSync(x_display, false); // Let the server catch up
2196 }
2197 }
2198 }
2199 #endif // def ENABLE_VOSF
2200
2201 static void video_refresh_window_static(void)
2202 {
2203 // Ungrab mouse if requested
2204 possibly_ungrab_mouse();
2205
2206 // Handle X events
2207 handle_events();
2208
2209 // Handle_palette changes
2210 handle_palette_changes();
2211
2212 // Update display (static variant)
2213 static int tick_counter = 0;
2214 if (++tick_counter >= frame_skip) {
2215 tick_counter = 0;
2216 update_display_static(static_cast<driver_window *>(drv));
2217 }
2218 }
2219
2220 static void video_refresh_window_dynamic(void)
2221 {
2222 // Ungrab mouse if requested
2223 possibly_ungrab_mouse();
2224
2225 // Handle X events
2226 handle_events();
2227
2228 // Handle_palette changes
2229 handle_palette_changes();
2230
2231 // Update display (dynamic variant)
2232 static int tick_counter = 0;
2233 tick_counter++;
2234 update_display_dynamic(tick_counter, static_cast<driver_window *>(drv));
2235 }
2236
2237
2238 /*
2239 * Thread for screen refresh, input handling etc.
2240 */
2241
2242 static void VideoRefreshInit(void)
2243 {
2244 // TODO: set up specialised 8bpp VideoRefresh handlers ?
2245 if (display_type == DISPLAY_DGA) {
2246 #if ENABLE_VOSF && (REAL_ADDRESSING || DIRECT_ADDRESSING)
2247 if (use_vosf)
2248 video_refresh = video_refresh_dga_vosf;
2249 else
2250 #endif
2251 video_refresh = video_refresh_dga;
2252 }
2253 else {
2254 #ifdef ENABLE_VOSF
2255 if (use_vosf)
2256 video_refresh = video_refresh_window_vosf;
2257 else
2258 #endif
2259 if (frame_skip == 0)
2260 video_refresh = video_refresh_window_dynamic;
2261 else
2262 video_refresh = video_refresh_window_static;
2263 }
2264 }
2265
2266 void VideoRefresh(void)
2267 {
2268 // We need to check redraw_thread_active to inhibit refreshed during
2269 // mode changes on non-threaded platforms
2270 if (redraw_thread_active)
2271 video_refresh();
2272 }
2273
2274 #ifdef HAVE_PTHREADS
2275 static void *redraw_func(void *arg)
2276 {
2277 uint64 start = GetTicks_usec();
2278 int64 ticks = 0;
2279 uint64 next = GetTicks_usec();
2280 while (!redraw_thread_cancel) {
2281 video_refresh();
2282 next += 16667;
2283 int64 delay = next - GetTicks_usec();
2284 if (delay > 0)
2285 Delay_usec(delay);
2286 else if (delay < -16667)
2287 next = GetTicks_usec();
2288 ticks++;
2289 }
2290 uint64 end = GetTicks_usec();
2291 // printf("%Ld ticks in %Ld usec = %Ld ticks/sec\n", ticks, end - start, ticks * 1000000 / (end - start));
2292 return NULL;
2293 }
2294 #endif