ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/video_x.cpp
Revision: 1.61
Committed: 2001-07-14T15:02:47Z (22 years, 10 months ago) by cebix
Branch: MAIN
Changes since 1.60: +34 -4 lines
Log Message:
- 1-bit mode under X11 always uses default visual
- fixed possible crash in driver_window dtor (image data would be freed twice,
  once by XDestroyImage() and once in driver_base dtor)
- fixed compilation problems with banked memory
- fixed typos

File Contents

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