ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/video_x.cpp
Revision: 1.49
Committed: 2001-07-01T19:57:55Z (23 years, 1 month ago) by cebix
Branch: MAIN
Changes since 1.48: +48 -12 lines
Log Message:
added support for 8-bit windowed modes on 16 and 32-bit screens (for the
games, man, the games! :-)

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