ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/video_x.cpp
Revision: 1.63
Committed: 2002-01-15T14:58:37Z (22 years, 10 months ago) by cebix
Branch: MAIN
CVS Tags: snapshot-15012002
Changes since 1.62: +1 -1 lines
Log Message:
- documentation updates
- 2001 -> 2002
- version 0.9 -> 1.0

File Contents

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