ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/video_x.cpp
Revision: 1.50
Committed: 2001-07-01T21:09:29Z (23 years, 5 months ago) by cebix
Branch: MAIN
Changes since 1.49: +26 -31 lines
Log Message:
- video_set_palette() gets passed the number of used palette entries
- video_x.cpp supports 2- and 4-bit modes on truecolor screens

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