ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/video_x.cpp
Revision: 1.53
Committed: 2001-07-06T20:49:53Z (23 years ago) by cebix
Branch: MAIN
Changes since 1.52: +211 -96 lines
Log Message:
- an appropriate visual is selected for the requested Mac color depth
- the list of supported Mac color depths is constructed from the list of
  depth and visuals supported by the X server

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