ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/video_x.cpp
Revision: 1.57
Committed: 2001-07-09T11:22:00Z (23 years ago) by cebix
Branch: MAIN
Changes since 1.56: +90 -52 lines
Log Message:
- ADB has its own interrupt flag, INTFLAG_ADB
- ADBMouseMoved(), ADBMouseDown/Up() and ADBKeyDown/Up() trigger the ADB
  interrupt
- ADB mutex is only used for mouse movement (the only input state where it
  matters)
- adb.cpp: toggling relative mouse mode resets mouse_x/y
- PrimeTime(0) schedules a timer task with 0 delay time; this is still not
  the correct implementation, but it makes MacSyndicate work...
- Unix: pthreads are preferred to POSIX.4 timers for 60Hz ticks because the
  timers drift badly under Linux and the thread can compensate for drifting
  well enough
- Unix: moved GetTicks_usec() and Delay_usec() to timer_unix.cpp
- video_x.cpp: X mouse acceleration is disabled in relative mouse mode because
  MacOS does its own acceleration
- video_x.cpp: palette[].pixel and palette[].flags are always preset
- video_x.cpp: decoupled X event handling from 60Hz video refresh cycle by
  using select() with a timeout on the X fd

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