ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/video_x.cpp
Revision: 1.12
Committed: 1999-11-03T21:04:23Z (24 years, 7 months ago) by cebix
Branch: MAIN
CVS Tags: snapshot-22121999, release-0_8-1
Changes since 1.11: +42 -3 lines
Log Message:
*** empty log message ***

File Contents

# Content
1 /*
2 * video_x.cpp - Video/graphics emulation, X11 specific stuff
3 *
4 * Basilisk II (C) 1997-1999 Christian Bauer
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 /*
22 * NOTES:
23 * The Ctrl key works like a qualifier for special actions:
24 * Ctrl-Tab = suspend DGA mode
25 * Ctrl-Esc = emergency quit
26 * Ctrl-F1 = mount floppy
27 */
28
29 #include "sysdeps.h"
30
31 #include <X11/Xlib.h>
32 #include <X11/Xutil.h>
33 #include <X11/keysym.h>
34 #include <X11/extensions/XShm.h>
35 #include <sys/ipc.h>
36 #include <sys/shm.h>
37 #include <pthread.h>
38 #include <errno.h>
39
40 #include "cpu_emulation.h"
41 #include "main.h"
42 #include "adb.h"
43 #include "macos_util.h"
44 #include "prefs.h"
45 #include "user_strings.h"
46 #include "video.h"
47
48 #define DEBUG 1
49 #include "debug.h"
50
51 #if ENABLE_XF86_DGA
52 #include <X11/extensions/xf86dga.h>
53 #endif
54
55 #if ENABLE_XF86_VIDMODE
56 #include <X11/extensions/xf86vmode.h>
57 #endif
58
59 #if ENABLE_FBDEV_DGA
60 #include <sys/mman.h>
61 #endif
62
63
64
65 // Display types
66 enum {
67 DISPLAY_WINDOW, // X11 window, using MIT SHM extensions if possible
68 DISPLAY_DGA // DGA fullscreen display
69 };
70
71
72 // Constants
73 const char KEYCODE_FILE_NAME[] = DATADIR "/keycodes";
74 const char FBDEVICES_FILE_NAME[] = DATADIR "/fbdevices";
75
76
77 // Global variables
78 static int32 frame_skip; // Prefs items
79 static int16 mouse_wheel_mode = 1;
80 static int16 mouse_wheel_lines = 3;
81
82 static int display_type = DISPLAY_WINDOW; // See enum above
83 static uint8 *the_buffer; // Mac frame buffer
84 static bool redraw_thread_active = false; // Flag: Redraw thread installed
85 static volatile bool redraw_thread_cancel = false; // Flag: Cancel Redraw thread
86 static pthread_t redraw_thread; // Redraw thread
87
88 static bool has_dga = false; // Flag: Video DGA capable
89 static bool has_vidmode = false; // Flag: VidMode extension available
90
91 static bool ctrl_down = false; // Flag: Ctrl key pressed
92 static bool caps_on = false; // Flag: Caps Lock on
93 static bool quit_full_screen = false; // Flag: DGA close requested from redraw thread
94 static bool emerg_quit = false; // Flag: Ctrl-Esc pressed, emergency quit requested from MacOS thread
95 static bool emul_suspended = false; // Flag: Emulator suspended
96
97 static bool classic_mode = false; // Flag: Classic Mac video mode
98
99 static bool use_keycodes = false; // Flag: Use keycodes rather than keysyms
100 static int keycode_table[256]; // X keycode -> Mac keycode translation table
101
102 // X11 variables
103 static int screen; // Screen number
104 static int xdepth; // Depth of X screen
105 static int depth; // Depth of Mac frame buffer
106 static Window rootwin, the_win; // Root window and our window
107 static XVisualInfo visualInfo;
108 static Visual *vis;
109 static Colormap cmap[2]; // Two colormaps (DGA) for 8-bit mode
110 static XColor black, white;
111 static unsigned long black_pixel, white_pixel;
112 static int eventmask;
113 static const int win_eventmask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | EnterWindowMask | ExposureMask;
114 static const int dga_eventmask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask;
115
116 static pthread_mutex_t palette_lock = PTHREAD_MUTEX_INITIALIZER; // Mutex to protect palette
117 static XColor palette[256]; // Color palette for 8-bit mode
118 static bool palette_changed = false; // Flag: Palette changed, redraw thread must set new colors
119
120 // Variables for window mode
121 static GC the_gc;
122 static XImage *img = NULL;
123 static XShmSegmentInfo shminfo;
124 static XImage *cursor_image, *cursor_mask_image;
125 static Pixmap cursor_map, cursor_mask_map;
126 static Cursor mac_cursor;
127 static GC cursor_gc, cursor_mask_gc;
128 static uint8 *the_buffer_copy = NULL; // Copy of Mac frame buffer
129 static uint8 the_cursor[64]; // Cursor image data
130 static bool have_shm = false; // Flag: SHM extensions available
131
132 // Variables for XF86 DGA mode
133 static int current_dga_cmap; // Number (0 or 1) of currently installed DGA colormap
134 static Window suspend_win; // "Suspend" window
135 static void *fb_save = NULL; // Saved frame buffer for suspend
136 static pthread_mutex_t frame_buffer_lock = PTHREAD_MUTEX_INITIALIZER; // Mutex to protect frame buffer
137
138 // Variables for fbdev DGA mode
139 const char FBDEVICE_FILE_NAME[] = "/dev/fb";
140 static int fbdev_fd;
141
142 #if ENABLE_XF86_VIDMODE
143 // Variables for XF86 VidMode support
144 static XF86VidModeModeInfo **x_video_modes; // Array of all available modes
145 static int num_x_video_modes;
146 #endif
147
148
149 // Prototypes
150 static void *redraw_func(void *arg);
151 static int event2keycode(XKeyEvent *ev);
152
153
154 // From main_unix.cpp
155 extern Display *x_display;
156
157 // From sys_unix.cpp
158 extern void SysMountFirstFloppy(void);
159
160
161 /*
162 * Initialization
163 */
164
165 // Set VideoMonitor according to video mode
166 void set_video_monitor(int width, int height, int bytes_per_row, bool native_byte_order)
167 {
168 int layout = FLAYOUT_DIRECT;
169 switch (depth) {
170 case 1:
171 layout = FLAYOUT_DIRECT;
172 VideoMonitor.mode = VMODE_1BIT;
173 break;
174 case 8:
175 layout = FLAYOUT_DIRECT;
176 VideoMonitor.mode = VMODE_8BIT;
177 break;
178 case 15:
179 layout = FLAYOUT_HOST_555;
180 VideoMonitor.mode = VMODE_16BIT;
181 break;
182 case 16:
183 layout = FLAYOUT_HOST_565;
184 VideoMonitor.mode = VMODE_16BIT;
185 break;
186 case 24:
187 case 32:
188 layout = FLAYOUT_HOST_888;
189 VideoMonitor.mode = VMODE_32BIT;
190 break;
191 }
192 VideoMonitor.x = width;
193 VideoMonitor.y = height;
194 VideoMonitor.bytes_per_row = bytes_per_row;
195 if (native_byte_order)
196 MacFrameLayout = layout;
197 else
198 MacFrameLayout = FLAYOUT_DIRECT;
199 }
200
201 // Trap SHM errors
202 static bool shm_error = false;
203 static int (*old_error_handler)(Display *, XErrorEvent *);
204
205 static int error_handler(Display *d, XErrorEvent *e)
206 {
207 if (e->error_code == BadAccess) {
208 shm_error = true;
209 return 0;
210 } else
211 return old_error_handler(d, e);
212 }
213
214 // Init window mode
215 static bool init_window(int width, int height)
216 {
217 // Set absolute mouse mode
218 ADBSetRelMouseMode(false);
219
220 // Read frame skip prefs
221 frame_skip = PrefsFindInt32("frameskip");
222 if (frame_skip == 0)
223 frame_skip = 1;
224
225 // Create window
226 XSetWindowAttributes wattr;
227 wattr.event_mask = eventmask = win_eventmask;
228 wattr.background_pixel = black_pixel;
229 wattr.border_pixel = black_pixel;
230 wattr.backing_store = Always;
231 wattr.backing_planes = xdepth;
232
233 XSync(x_display, false);
234 the_win = XCreateWindow(x_display, rootwin, 0, 0, width, height, 0, xdepth,
235 InputOutput, vis, CWEventMask | CWBackPixel | CWBorderPixel |
236 CWBackingStore | CWBackingPlanes, &wattr);
237 XSync(x_display, false);
238 XStoreName(x_display, the_win, GetString(STR_WINDOW_TITLE));
239 XMapRaised(x_display, the_win);
240 XSync(x_display, false);
241
242 // Set colormap
243 if (depth == 8) {
244 XSetWindowColormap(x_display, the_win, cmap[0]);
245 XSetWMColormapWindows(x_display, the_win, &the_win, 1);
246 }
247
248 // Make window unresizable
249 XSizeHints *hints;
250 if ((hints = XAllocSizeHints()) != NULL) {
251 hints->min_width = width;
252 hints->max_width = width;
253 hints->min_height = height;
254 hints->max_height = height;
255 hints->flags = PMinSize | PMaxSize;
256 XSetWMNormalHints(x_display, the_win, hints);
257 XFree((char *)hints);
258 }
259
260 // Try to create and attach SHM image
261 have_shm = false;
262 if (depth != 1 && XShmQueryExtension(x_display)) {
263
264 // Create SHM image ("height + 2" for safety)
265 img = XShmCreateImage(x_display, vis, depth, depth == 1 ? XYBitmap : ZPixmap, 0, &shminfo, width, height);
266 shminfo.shmid = shmget(IPC_PRIVATE, (height + 2) * img->bytes_per_line, IPC_CREAT | 0777);
267 the_buffer = (uint8 *)shmat(shminfo.shmid, 0, 0);
268 shminfo.shmaddr = img->data = (char *)the_buffer;
269 shminfo.readOnly = False;
270
271 // Try to attach SHM image, catching errors
272 shm_error = false;
273 old_error_handler = XSetErrorHandler(error_handler);
274 XShmAttach(x_display, &shminfo);
275 XSync(x_display, false);
276 XSetErrorHandler(old_error_handler);
277 if (shm_error) {
278 shmdt(shminfo.shmaddr);
279 XDestroyImage(img);
280 shminfo.shmid = -1;
281 } else {
282 have_shm = true;
283 shmctl(shminfo.shmid, IPC_RMID, 0);
284 }
285 }
286
287 // Create normal X image if SHM doesn't work ("height + 2" for safety)
288 if (!have_shm) {
289 int bytes_per_row = width;
290 switch (depth) {
291 case 1:
292 bytes_per_row /= 8;
293 break;
294 case 15:
295 case 16:
296 bytes_per_row *= 2;
297 break;
298 case 24:
299 case 32:
300 bytes_per_row *= 4;
301 break;
302 }
303 the_buffer = (uint8 *)malloc((height + 2) * bytes_per_row);
304 img = XCreateImage(x_display, vis, depth, depth == 1 ? XYBitmap : ZPixmap, 0, (char *)the_buffer, width, height, 32, bytes_per_row);
305 }
306
307 // 1-Bit mode is big-endian
308 if (depth == 1) {
309 img->byte_order = MSBFirst;
310 img->bitmap_bit_order = MSBFirst;
311 }
312
313 // Allocate memory for frame buffer copy
314 the_buffer_copy = (uint8 *)malloc((height + 2) * img->bytes_per_line);
315
316 // Create GC
317 the_gc = XCreateGC(x_display, the_win, 0, 0);
318 XSetState(x_display, the_gc, black_pixel, white_pixel, GXcopy, AllPlanes);
319
320 // Create cursor
321 cursor_image = XCreateImage(x_display, vis, 1, XYPixmap, 0, (char *)the_cursor, 16, 16, 16, 2);
322 cursor_image->byte_order = MSBFirst;
323 cursor_image->bitmap_bit_order = MSBFirst;
324 cursor_mask_image = XCreateImage(x_display, vis, 1, XYPixmap, 0, (char *)the_cursor+32, 16, 16, 16, 2);
325 cursor_mask_image->byte_order = MSBFirst;
326 cursor_mask_image->bitmap_bit_order = MSBFirst;
327 cursor_map = XCreatePixmap(x_display, the_win, 16, 16, 1);
328 cursor_mask_map = XCreatePixmap(x_display, the_win, 16, 16, 1);
329 cursor_gc = XCreateGC(x_display, cursor_map, 0, 0);
330 cursor_mask_gc = XCreateGC(x_display, cursor_mask_map, 0, 0);
331 mac_cursor = XCreatePixmapCursor(x_display, cursor_map, cursor_mask_map, &black, &white, 0, 0);
332
333 // Set VideoMonitor
334 #ifdef WORDS_BIGENDIAN
335 set_video_monitor(width, height, img->bytes_per_line, img->bitmap_bit_order == MSBFirst);
336 #else
337 set_video_monitor(width, height, img->bytes_per_line, img->bitmap_bit_order == LSBFirst);
338 #endif
339
340 #if REAL_ADDRESSING
341 VideoMonitor.mac_frame_base = (uint32)the_buffer;
342 MacFrameLayout = FLAYOUT_DIRECT;
343 #else
344 VideoMonitor.mac_frame_base = MacFrameBaseMac;
345 #endif
346 return true;
347 }
348
349 // Init fbdev DGA display
350 static bool init_fbdev_dga(char *in_fb_name)
351 {
352 #if ENABLE_FBDEV_DGA
353 // Find the maximum depth available
354 int ndepths, max_depth(0);
355 int *depths = XListDepths(x_display, screen, &ndepths);
356 if (depths == NULL) {
357 printf("FATAL: Could not determine the maximal depth available\n");
358 return false;
359 } else {
360 while (ndepths-- > 0) {
361 if (depths[ndepths] > max_depth)
362 max_depth = depths[ndepths];
363 }
364 }
365
366 // Get fbdevices file path from preferences
367 const char *fbd_path = PrefsFindString("fbdevicefile");
368
369 // Open fbdevices file
370 FILE *fp = fopen(fbd_path ? fbd_path : FBDEVICES_FILE_NAME, "r");
371 if (fp == NULL) {
372 char str[256];
373 sprintf(str, GetString(STR_NO_FBDEVICE_FILE_ERR), fbd_path ? fbd_path : FBDEVICES_FILE_NAME, strerror(errno));
374 ErrorAlert(str);
375 return false;
376 }
377
378 int fb_depth; // supported depth
379 uint32 fb_offset; // offset used for mmap(2)
380 char fb_name[20];
381 char line[256];
382 bool device_found = false;
383 while (fgets(line, 255, fp)) {
384 // Read line
385 int len = strlen(line);
386 if (len == 0)
387 continue;
388 line[len - 1] = '\0';
389
390 // Comments begin with "#" or ";"
391 if ((line[0] == '#') || (line[0] == ';') || (line[0] == '\0'))
392 continue;
393
394 if ((sscanf(line, "%19s %d %x", &fb_name, &fb_depth, &fb_offset) == 3)
395 && (strcmp(fb_name, in_fb_name) == 0) && (fb_depth == max_depth)) {
396 device_found = true;
397 break;
398 }
399 }
400
401 // fbdevices file completely read
402 fclose(fp);
403
404 // Frame buffer name not found ? Then, display warning
405 if (!device_found) {
406 char str[256];
407 sprintf(str, GetString(STR_FBDEV_NAME_ERR), in_fb_name, max_depth);
408 ErrorAlert(str);
409 return false;
410 }
411
412 int width = DisplayWidth(x_display, screen);
413 int height = DisplayHeight(x_display, screen);
414 depth = fb_depth; // max_depth
415
416 // Set relative mouse mode
417 ADBSetRelMouseMode(false);
418
419 // Create window
420 XSetWindowAttributes wattr;
421 wattr.override_redirect = True;
422 wattr.backing_store = NotUseful;
423 wattr.background_pixel = white_pixel;
424 wattr.border_pixel = black_pixel;
425 wattr.event_mask = eventmask = dga_eventmask;
426
427 XSync(x_display, false);
428 the_win = XCreateWindow(x_display, rootwin,
429 0, 0, width, height,
430 0, xdepth, InputOutput, vis,
431 CWEventMask|CWBackPixel|CWBorderPixel|CWOverrideRedirect|CWBackingStore,
432 &wattr);
433 XSync(x_display, false);
434 XMapRaised(x_display, the_win);
435 XSync(x_display, false);
436
437 // Grab mouse and keyboard
438 XGrabKeyboard(x_display, the_win, True,
439 GrabModeAsync, GrabModeAsync, CurrentTime);
440 XGrabPointer(x_display, the_win, True,
441 PointerMotionMask | ButtonPressMask | ButtonReleaseMask,
442 GrabModeAsync, GrabModeAsync, the_win, None, CurrentTime);
443
444 // Set colormap
445 if (depth == 8) {
446 XSetWindowColormap(x_display, the_win, cmap[0]);
447 XSetWMColormapWindows(x_display, the_win, &the_win, 1);
448 }
449
450 // Set VideoMonitor
451 int bytes_per_row = width;
452 switch (depth) {
453 case 1:
454 bytes_per_row = ((width | 7) & ~7) >> 3;
455 break;
456 case 15:
457 case 16:
458 bytes_per_row *= 2;
459 break;
460 case 24:
461 case 32:
462 bytes_per_row *= 4;
463 break;
464 }
465
466 if ((the_buffer = (uint8 *) mmap(NULL, height * bytes_per_row, PROT_READ | PROT_WRITE, MAP_PRIVATE, fbdev_fd, fb_offset)) == MAP_FAILED) {
467 if ((the_buffer = (uint8 *) mmap(NULL, height * bytes_per_row, PROT_READ | PROT_WRITE, MAP_SHARED, fbdev_fd, fb_offset)) == MAP_FAILED) {
468 char str[256];
469 sprintf(str, GetString(STR_FBDEV_MMAP_ERR), strerror(errno));
470 ErrorAlert(str);
471 return false;
472 }
473 }
474
475 set_video_monitor(width, height, bytes_per_row, true);
476 #if REAL_ADDRESSING
477 VideoMonitor.mac_frame_base = (uint32)the_buffer;
478 MacFrameLayout = FLAYOUT_DIRECT;
479 #else
480 VideoMonitor.mac_frame_base = MacFrameBaseMac;
481 #endif
482 return true;
483 #else
484 ErrorAlert("Basilisk II has been compiled with fbdev DGA support disabled.");
485 return false;
486 #endif
487 }
488
489 // Init XF86 DGA display
490 static bool init_xf86_dga(int width, int height)
491 {
492 #if ENABLE_XF86_DGA
493 // Set relative mouse mode
494 ADBSetRelMouseMode(true);
495
496 #if ENABLE_XF86_VIDMODE
497 // Switch to best mode
498 if (has_vidmode) {
499 int best = 0;
500 for (int i=1; i<num_x_video_modes; i++) {
501 if (x_video_modes[i]->hdisplay >= width && x_video_modes[i]->vdisplay >= height &&
502 x_video_modes[i]->hdisplay <= x_video_modes[best]->hdisplay && x_video_modes[i]->vdisplay <= x_video_modes[best]->vdisplay) {
503 best = i;
504 }
505 }
506 XF86VidModeSwitchToMode(x_display, screen, x_video_modes[best]);
507 XF86VidModeSetViewPort(x_display, screen, 0, 0);
508 }
509 #endif
510
511 // Create window
512 XSetWindowAttributes wattr;
513 wattr.event_mask = eventmask = dga_eventmask;
514 wattr.border_pixel = black_pixel;
515 wattr.override_redirect = True;
516
517 XSync(x_display, false);
518 the_win = XCreateWindow(x_display, rootwin, 0, 0, width, height, 0, xdepth,
519 InputOutput, vis, CWEventMask | CWBorderPixel | CWOverrideRedirect, &wattr);
520 XSync(x_display, false);
521 XStoreName(x_display, the_win, GetString(STR_WINDOW_TITLE));
522 XMapRaised(x_display, the_win);
523 XSync(x_display, false);
524
525 // Establish direct screen connection
526 XMoveResizeWindow(x_display, the_win, 0, 0, width, height);
527 XWarpPointer(x_display, None, rootwin, 0, 0, 0, 0, 0, 0);
528 XGrabKeyboard(x_display, rootwin, True, GrabModeAsync, GrabModeAsync, CurrentTime);
529 XGrabPointer(x_display, rootwin, True, PointerMotionMask | ButtonPressMask | ButtonReleaseMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
530
531 int v_width, v_bank, v_size;
532 XF86DGAGetVideo(x_display, screen, (char **)&the_buffer, &v_width, &v_bank, &v_size);
533 XF86DGADirectVideo(x_display, screen, XF86DGADirectGraphics | XF86DGADirectKeyb | XF86DGADirectMouse);
534 XF86DGASetViewPort(x_display, screen, 0, 0);
535 XF86DGASetVidPage(x_display, screen, 0);
536
537 // Set colormap
538 if (depth == 8) {
539 XSetWindowColormap(x_display, the_win, cmap[current_dga_cmap = 0]);
540 XSetWMColormapWindows(x_display, the_win, &the_win, 1);
541 XF86DGAInstallColormap(x_display, screen, cmap[current_dga_cmap]);
542 }
543
544 // Set VideoMonitor
545 int bytes_per_row = (v_width + 7) & ~7;
546 switch (depth) {
547 case 1:
548 bytes_per_row /= 8;
549 break;
550 case 15:
551 case 16:
552 bytes_per_row *= 2;
553 break;
554 case 24:
555 case 32:
556 bytes_per_row *= 4;
557 break;
558 }
559 set_video_monitor(width, height, bytes_per_row, true);
560 #if REAL_ADDRESSING
561 VideoMonitor.mac_frame_base = (uint32)the_buffer;
562 MacFrameLayout = FLAYOUT_DIRECT;
563 #else
564 VideoMonitor.mac_frame_base = MacFrameBaseMac;
565 #endif
566 return true;
567 #else
568 ErrorAlert("Basilisk II has been compiled with XF86 DGA support disabled.");
569 return false;
570 #endif
571 }
572
573 // Init keycode translation table
574 static void keycode_init(void)
575 {
576 bool use_kc = PrefsFindBool("keycodes");
577 if (use_kc) {
578
579 // Get keycode file path from preferences
580 const char *kc_path = PrefsFindString("keycodefile");
581
582 // Open keycode table
583 FILE *f = fopen(kc_path ? kc_path : KEYCODE_FILE_NAME, "r");
584 if (f == NULL) {
585 char str[256];
586 sprintf(str, GetString(STR_KEYCODE_FILE_WARN), kc_path ? kc_path : KEYCODE_FILE_NAME, strerror(errno));
587 WarningAlert(str);
588 return;
589 }
590
591 // Default translation table
592 for (int i=0; i<256; i++)
593 keycode_table[i] = -1;
594
595 // Search for server vendor string, then read keycodes
596 const char *vendor = ServerVendor(x_display);
597 bool vendor_found = false;
598 char line[256];
599 while (fgets(line, 255, f)) {
600 // Read line
601 int len = strlen(line);
602 if (len == 0)
603 continue;
604 line[len-1] = 0;
605
606 // Comments begin with "#" or ";"
607 if (line[0] == '#' || line[0] == ';' || line[0] == 0)
608 continue;
609
610 if (vendor_found) {
611 // Read keycode
612 int x_code, mac_code;
613 if (sscanf(line, "%d %d", &x_code, &mac_code) == 2)
614 keycode_table[x_code & 0xff] = mac_code;
615 else
616 break;
617 } else {
618 // Search for vendor string
619 if (strstr(vendor, line) == vendor)
620 vendor_found = true;
621 }
622 }
623
624 // Keycode file completely read
625 fclose(f);
626 use_keycodes = vendor_found;
627
628 // Vendor not found? Then display warning
629 if (!vendor_found) {
630 char str[256];
631 sprintf(str, GetString(STR_KEYCODE_VENDOR_WARN), vendor, kc_path ? kc_path : KEYCODE_FILE_NAME);
632 WarningAlert(str);
633 return;
634 }
635 }
636 }
637
638 bool VideoInit(bool classic)
639 {
640 // Init keycode translation
641 keycode_init();
642
643 // Read prefs
644 mouse_wheel_mode = PrefsFindInt16("mousewheelmode");
645 mouse_wheel_lines = PrefsFindInt16("mousewheellines");
646
647 // Find screen and root window
648 screen = XDefaultScreen(x_display);
649 rootwin = XRootWindow(x_display, screen);
650
651 // Get screen depth
652 xdepth = DefaultDepth(x_display, screen);
653
654 #if ENABLE_FBDEV_DGA
655 // Frame buffer name
656 char fb_name[20];
657
658 // Could do fbdev dga ?
659 if ((fbdev_fd = open(FBDEVICE_FILE_NAME, O_RDWR)) != -1)
660 has_dga = true;
661 else
662 has_dga = false;
663 #endif
664
665 #if ENABLE_XF86_DGA
666 // DGA available?
667 int dga_event_base, dga_error_base;
668 if (XF86DGAQueryExtension(x_display, &dga_event_base, &dga_error_base)) {
669 int dga_flags = 0;
670 XF86DGAQueryDirectVideo(x_display, screen, &dga_flags);
671 has_dga = dga_flags & XF86DGADirectPresent;
672 } else
673 has_dga = false;
674 #endif
675
676 #if ENABLE_XF86_VIDMODE
677 // VidMode available?
678 int vm_event_base, vm_error_base;
679 has_vidmode = XF86VidModeQueryExtension(x_display, &vm_event_base, &vm_error_base);
680 if (has_vidmode)
681 XF86VidModeGetAllModeLines(x_display, screen, &num_x_video_modes, &x_video_modes);
682 #endif
683
684 // Find black and white colors
685 XParseColor(x_display, DefaultColormap(x_display, screen), "rgb:00/00/00", &black);
686 XAllocColor(x_display, DefaultColormap(x_display, screen), &black);
687 XParseColor(x_display, DefaultColormap(x_display, screen), "rgb:ff/ff/ff", &white);
688 XAllocColor(x_display, DefaultColormap(x_display, screen), &white);
689 black_pixel = BlackPixel(x_display, screen);
690 white_pixel = WhitePixel(x_display, screen);
691
692 // Get appropriate visual
693 int color_class;
694 switch (xdepth) {
695 case 1:
696 color_class = StaticGray;
697 break;
698 case 8:
699 color_class = PseudoColor;
700 break;
701 case 15:
702 case 16:
703 case 24:
704 case 32:
705 color_class = TrueColor;
706 break;
707 default:
708 ErrorAlert(GetString(STR_UNSUPP_DEPTH_ERR));
709 return false;
710 }
711 if (!XMatchVisualInfo(x_display, screen, xdepth, color_class, &visualInfo)) {
712 ErrorAlert(GetString(STR_NO_XVISUAL_ERR));
713 return false;
714 }
715 if (visualInfo.depth != xdepth) {
716 ErrorAlert(GetString(STR_NO_XVISUAL_ERR));
717 return false;
718 }
719 vis = visualInfo.visual;
720
721 // Mac screen depth is always 1 bit in Classic mode, but follows X depth otherwise
722 classic_mode = classic;
723 if (classic)
724 depth = 1;
725 else
726 depth = xdepth;
727
728 // Create color maps for 8 bit mode
729 if (depth == 8) {
730 cmap[0] = XCreateColormap(x_display, rootwin, vis, AllocAll);
731 cmap[1] = XCreateColormap(x_display, rootwin, vis, AllocAll);
732 XInstallColormap(x_display, cmap[0]);
733 XInstallColormap(x_display, cmap[1]);
734 }
735
736 // Get screen mode from preferences
737 const char *mode_str;
738 if (classic)
739 mode_str = "win/512/342";
740 else
741 mode_str = PrefsFindString("screen");
742
743 // Determine type and mode
744 int width = 512, height = 384;
745 display_type = DISPLAY_WINDOW;
746 if (mode_str) {
747 if (sscanf(mode_str, "win/%d/%d", &width, &height) == 2)
748 display_type = DISPLAY_WINDOW;
749 #if ENABLE_FBDEV_DGA
750 else if (has_dga && sscanf(mode_str, "dga/%19s", fb_name) == 1) {
751 #else
752 else if (has_dga && sscanf(mode_str, "dga/%d/%d", &width, &height) == 2) {
753 #endif
754 display_type = DISPLAY_DGA;
755 if (width > DisplayWidth(x_display, screen))
756 width = DisplayWidth(x_display, screen);
757 if (height > DisplayHeight(x_display, screen))
758 height = DisplayHeight(x_display, screen);
759 }
760 if (width <= 0)
761 width = DisplayWidth(x_display, screen);
762 if (height <= 0)
763 height = DisplayHeight(x_display, screen);
764 }
765
766 // Initialize according to display type
767 switch (display_type) {
768 case DISPLAY_WINDOW:
769 if (!init_window(width, height))
770 return false;
771 break;
772 case DISPLAY_DGA:
773 #if ENABLE_FBDEV_DGA
774 if (!init_fbdev_dga(fb_name))
775 #else
776 if (!init_xf86_dga(width, height))
777 #endif
778 return false;
779 break;
780 }
781
782 // Lock down frame buffer
783 pthread_mutex_lock(&frame_buffer_lock);
784
785 #if !REAL_ADDRESSING
786 // Set variables for UAE memory mapping
787 MacFrameBaseHost = the_buffer;
788 MacFrameSize = VideoMonitor.bytes_per_row * VideoMonitor.y;
789
790 // No special frame buffer in Classic mode (frame buffer is in Mac RAM)
791 if (classic)
792 MacFrameLayout = FLAYOUT_NONE;
793 #endif
794
795 // Start redraw/input thread
796 XSync(x_display, false);
797 redraw_thread_active = (pthread_create(&redraw_thread, NULL, redraw_func, NULL) == 0);
798 if (!redraw_thread_active)
799 printf("FATAL: cannot create redraw thread\n");
800 return redraw_thread_active;
801 }
802
803
804 /*
805 * Deinitialization
806 */
807
808 void VideoExit(void)
809 {
810 // Stop redraw thread
811 if (redraw_thread_active) {
812 redraw_thread_cancel = true;
813 #ifdef HAVE_PTHREAD_CANCEL
814 pthread_cancel(redraw_thread);
815 #endif
816 pthread_join(redraw_thread, NULL);
817 redraw_thread_active = false;
818 }
819
820 // Unlock frame buffer
821 pthread_mutex_unlock(&frame_buffer_lock);
822
823 // Close window and server connection
824 if (x_display != NULL) {
825 XSync(x_display, false);
826
827 #if ENABLE_XF86_DGA
828 if (display_type == DISPLAY_DGA) {
829 XF86DGADirectVideo(x_display, screen, 0);
830 XUngrabPointer(x_display, CurrentTime);
831 XUngrabKeyboard(x_display, CurrentTime);
832 }
833 #endif
834
835 #if ENABLE_XF86_VIDMODE
836 if (has_vidmode && display_type == DISPLAY_DGA)
837 XF86VidModeSwitchToMode(x_display, screen, x_video_modes[0]);
838 #endif
839
840 #if ENABLE_FBDEV_DGA
841 if (display_type == DISPLAY_DGA) {
842 XUngrabPointer(x_display, CurrentTime);
843 XUngrabKeyboard(x_display, CurrentTime);
844 close(fbdev_fd);
845 }
846 #endif
847
848 if (the_buffer_copy) {
849 free(the_buffer_copy);
850 the_buffer_copy = NULL;
851 }
852
853 XFlush(x_display);
854 XSync(x_display, false);
855 if (depth == 8) {
856 XFreeColormap(x_display, cmap[0]);
857 XFreeColormap(x_display, cmap[1]);
858 }
859 }
860 }
861
862
863 /*
864 * Close down full-screen mode (if bringing up error alerts is unsafe while in full-screen mode)
865 */
866
867 void VideoQuitFullScreen(void)
868 {
869 D(bug("VideoQuitFullScreen()\n"));
870 if (display_type == DISPLAY_DGA)
871 quit_full_screen = true;
872 }
873
874
875 /*
876 * Mac VBL interrupt
877 */
878
879 void VideoInterrupt(void)
880 {
881 // Emergency quit requested? Then quit
882 if (emerg_quit)
883 QuitEmulator();
884
885 // Temporarily give up frame buffer lock (this is the point where
886 // we are suspended when the user presses Ctrl-Tab)
887 pthread_mutex_unlock(&frame_buffer_lock);
888 pthread_mutex_lock(&frame_buffer_lock);
889 }
890
891
892 /*
893 * Set palette
894 */
895
896 void video_set_palette(uint8 *pal)
897 {
898 pthread_mutex_lock(&palette_lock);
899
900 // Convert colors to XColor array
901 for (int i=0; i<256; i++) {
902 palette[i].pixel = i;
903 palette[i].red = pal[i*3] * 0x0101;
904 palette[i].green = pal[i*3+1] * 0x0101;
905 palette[i].blue = pal[i*3+2] * 0x0101;
906 palette[i].flags = DoRed | DoGreen | DoBlue;
907 }
908
909 // Tell redraw thread to change palette
910 palette_changed = true;
911
912 pthread_mutex_unlock(&palette_lock);
913 }
914
915
916 /*
917 * Suspend/resume emulator
918 */
919
920 #if ENABLE_XF86_DGA || ENABLE_FBDEV_DGA
921 static void suspend_emul(void)
922 {
923 if (display_type == DISPLAY_DGA) {
924 // Release ctrl key
925 ADBKeyUp(0x36);
926 ctrl_down = false;
927
928 // Lock frame buffer (this will stop the MacOS thread)
929 pthread_mutex_lock(&frame_buffer_lock);
930
931 // Save frame buffer
932 fb_save = malloc(VideoMonitor.y * VideoMonitor.bytes_per_row);
933 if (fb_save)
934 memcpy(fb_save, the_buffer, VideoMonitor.y * VideoMonitor.bytes_per_row);
935
936 // Close full screen display
937 #if ENABLE_XF86_DGA
938 XF86DGADirectVideo(x_display, screen, 0);
939 #endif
940 XUngrabPointer(x_display, CurrentTime);
941 XUngrabKeyboard(x_display, CurrentTime);
942 XUnmapWindow(x_display, the_win);
943 XSync(x_display, false);
944
945 // Open "suspend" window
946 XSetWindowAttributes wattr;
947 wattr.event_mask = KeyPressMask;
948 wattr.background_pixel = black_pixel;
949 wattr.border_pixel = black_pixel;
950 wattr.backing_store = Always;
951 wattr.backing_planes = xdepth;
952 wattr.colormap = DefaultColormap(x_display, screen);
953
954 XSync(x_display, false);
955 suspend_win = XCreateWindow(x_display, rootwin, 0, 0, 512, 1, 0, xdepth,
956 InputOutput, vis, CWEventMask | CWBackPixel | CWBorderPixel |
957 CWBackingStore | CWBackingPlanes | (xdepth == 8 ? CWColormap : 0), &wattr);
958 XSync(x_display, false);
959 XStoreName(x_display, suspend_win, GetString(STR_SUSPEND_WINDOW_TITLE));
960 XMapRaised(x_display, suspend_win);
961 XSync(x_display, false);
962 emul_suspended = true;
963 }
964 }
965
966 static void resume_emul(void)
967 {
968 // Close "suspend" window
969 XDestroyWindow(x_display, suspend_win);
970 XSync(x_display, false);
971
972 // Reopen full screen display
973 XMapRaised(x_display, the_win);
974 XWarpPointer(x_display, None, rootwin, 0, 0, 0, 0, 0, 0);
975 XSync(x_display, false);
976 XGrabKeyboard(x_display, rootwin, 1, GrabModeAsync, GrabModeAsync, CurrentTime);
977 XGrabPointer(x_display, rootwin, 1, PointerMotionMask | ButtonPressMask | ButtonReleaseMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
978 #if ENABLE_XF86_DGA
979 XF86DGADirectVideo(x_display, screen, XF86DGADirectGraphics | XF86DGADirectKeyb | XF86DGADirectMouse);
980 XF86DGASetViewPort(x_display, screen, 0, 0);
981 #endif
982 XSync(x_display, false);
983
984 // Restore frame buffer
985 if (fb_save) {
986 memcpy(the_buffer, fb_save, VideoMonitor.y * VideoMonitor.bytes_per_row);
987 free(fb_save);
988 fb_save = NULL;
989 }
990
991 if (depth == 8)
992 #if ENABLE_XF86_DGA
993 XF86DGAInstallColormap(x_display, screen, cmap[current_dga_cmap]);
994 #endif
995
996 // Unlock frame buffer (and continue MacOS thread)
997 pthread_mutex_unlock(&frame_buffer_lock);
998 emul_suspended = false;
999 }
1000 #endif
1001
1002
1003 /*
1004 * Translate key event to Mac keycode
1005 */
1006
1007 static int kc_decode(KeySym ks)
1008 {
1009 switch (ks) {
1010 case XK_A: case XK_a: return 0x00;
1011 case XK_B: case XK_b: return 0x0b;
1012 case XK_C: case XK_c: return 0x08;
1013 case XK_D: case XK_d: return 0x02;
1014 case XK_E: case XK_e: return 0x0e;
1015 case XK_F: case XK_f: return 0x03;
1016 case XK_G: case XK_g: return 0x05;
1017 case XK_H: case XK_h: return 0x04;
1018 case XK_I: case XK_i: return 0x22;
1019 case XK_J: case XK_j: return 0x26;
1020 case XK_K: case XK_k: return 0x28;
1021 case XK_L: case XK_l: return 0x25;
1022 case XK_M: case XK_m: return 0x2e;
1023 case XK_N: case XK_n: return 0x2d;
1024 case XK_O: case XK_o: return 0x1f;
1025 case XK_P: case XK_p: return 0x23;
1026 case XK_Q: case XK_q: return 0x0c;
1027 case XK_R: case XK_r: return 0x0f;
1028 case XK_S: case XK_s: return 0x01;
1029 case XK_T: case XK_t: return 0x11;
1030 case XK_U: case XK_u: return 0x20;
1031 case XK_V: case XK_v: return 0x09;
1032 case XK_W: case XK_w: return 0x0d;
1033 case XK_X: case XK_x: return 0x07;
1034 case XK_Y: case XK_y: return 0x10;
1035 case XK_Z: case XK_z: return 0x06;
1036
1037 case XK_1: case XK_exclam: return 0x12;
1038 case XK_2: case XK_at: return 0x13;
1039 case XK_3: case XK_numbersign: return 0x14;
1040 case XK_4: case XK_dollar: return 0x15;
1041 case XK_5: case XK_percent: return 0x17;
1042 case XK_6: return 0x16;
1043 case XK_7: return 0x1a;
1044 case XK_8: return 0x1c;
1045 case XK_9: return 0x19;
1046 case XK_0: return 0x1d;
1047
1048 case XK_grave: case XK_asciitilde: return 0x0a;
1049 case XK_minus: case XK_underscore: return 0x1b;
1050 case XK_equal: case XK_plus: return 0x18;
1051 case XK_bracketleft: case XK_braceleft: return 0x21;
1052 case XK_bracketright: case XK_braceright: return 0x1e;
1053 case XK_backslash: case XK_bar: return 0x2a;
1054 case XK_semicolon: case XK_colon: return 0x29;
1055 case XK_apostrophe: case XK_quotedbl: return 0x27;
1056 case XK_comma: case XK_less: return 0x2b;
1057 case XK_period: case XK_greater: return 0x2f;
1058 case XK_slash: case XK_question: return 0x2c;
1059
1060 #if ENABLE_XF86_DGA || ENABLE_FBDEV_DGA
1061 case XK_Tab: if (ctrl_down) {suspend_emul(); return -1;} else return 0x30;
1062 #else
1063 case XK_Tab: return 0x30;
1064 #endif
1065 case XK_Return: return 0x24;
1066 case XK_space: return 0x31;
1067 case XK_BackSpace: return 0x33;
1068
1069 case XK_Delete: return 0x75;
1070 case XK_Insert: return 0x72;
1071 case XK_Home: case XK_Help: return 0x73;
1072 case XK_End: return 0x77;
1073 #ifdef __hpux
1074 case XK_Prior: return 0x74;
1075 case XK_Next: return 0x79;
1076 #else
1077 case XK_Page_Up: return 0x74;
1078 case XK_Page_Down: return 0x79;
1079 #endif
1080
1081 case XK_Control_L: return 0x36;
1082 case XK_Control_R: return 0x36;
1083 case XK_Shift_L: return 0x38;
1084 case XK_Shift_R: return 0x38;
1085 case XK_Alt_L: return 0x37;
1086 case XK_Alt_R: return 0x37;
1087 case XK_Meta_L: return 0x3a;
1088 case XK_Meta_R: return 0x3a;
1089 case XK_Menu: return 0x32;
1090 case XK_Caps_Lock: return 0x39;
1091 case XK_Num_Lock: return 0x47;
1092
1093 case XK_Up: return 0x3e;
1094 case XK_Down: return 0x3d;
1095 case XK_Left: return 0x3b;
1096 case XK_Right: return 0x3c;
1097
1098 case XK_Escape: if (ctrl_down) {quit_full_screen = true; emerg_quit = true; return -1;} else return 0x35;
1099
1100 case XK_F1: if (ctrl_down) {SysMountFirstFloppy(); return -1;} else return 0x7a;
1101 case XK_F2: return 0x78;
1102 case XK_F3: return 0x63;
1103 case XK_F4: return 0x76;
1104 case XK_F5: return 0x60;
1105 case XK_F6: return 0x61;
1106 case XK_F7: return 0x62;
1107 case XK_F8: return 0x64;
1108 case XK_F9: return 0x65;
1109 case XK_F10: return 0x6d;
1110 case XK_F11: return 0x67;
1111 case XK_F12: return 0x6f;
1112
1113 case XK_Print: return 0x69;
1114 case XK_Scroll_Lock: return 0x6b;
1115 case XK_Pause: return 0x71;
1116
1117 #if defined(XK_KP_Prior) && defined(XK_KP_Left) && defined(XK_KP_Insert) && defined (XK_KP_End)
1118 case XK_KP_0: case XK_KP_Insert: return 0x52;
1119 case XK_KP_1: case XK_KP_End: return 0x53;
1120 case XK_KP_2: case XK_KP_Down: return 0x54;
1121 case XK_KP_3: case XK_KP_Next: return 0x55;
1122 case XK_KP_4: case XK_KP_Left: return 0x56;
1123 case XK_KP_5: case XK_KP_Begin: return 0x57;
1124 case XK_KP_6: case XK_KP_Right: return 0x58;
1125 case XK_KP_7: case XK_KP_Home: return 0x59;
1126 case XK_KP_8: case XK_KP_Up: return 0x5b;
1127 case XK_KP_9: case XK_KP_Prior: return 0x5c;
1128 case XK_KP_Decimal: case XK_KP_Delete: return 0x41;
1129 #else
1130 case XK_KP_0: return 0x52;
1131 case XK_KP_1: return 0x53;
1132 case XK_KP_2: return 0x54;
1133 case XK_KP_3: return 0x55;
1134 case XK_KP_4: return 0x56;
1135 case XK_KP_5: return 0x57;
1136 case XK_KP_6: return 0x58;
1137 case XK_KP_7: return 0x59;
1138 case XK_KP_8: return 0x5b;
1139 case XK_KP_9: return 0x5c;
1140 case XK_KP_Decimal: return 0x41;
1141 #endif
1142 case XK_KP_Add: return 0x45;
1143 case XK_KP_Subtract: return 0x4e;
1144 case XK_KP_Multiply: return 0x43;
1145 case XK_KP_Divide: return 0x4b;
1146 case XK_KP_Enter: return 0x4c;
1147 case XK_KP_Equal: return 0x51;
1148 }
1149 return -1;
1150 }
1151
1152 static int event2keycode(XKeyEvent *ev)
1153 {
1154 KeySym ks;
1155 int as;
1156 int i = 0;
1157
1158 do {
1159 ks = XLookupKeysym(ev, i++);
1160 as = kc_decode(ks);
1161 if (as != -1)
1162 return as;
1163 } while (ks != NoSymbol);
1164
1165 return -1;
1166 }
1167
1168
1169 /*
1170 * X event handling
1171 */
1172
1173 static void handle_events(void)
1174 {
1175 XEvent event;
1176 for (;;) {
1177 if (!XCheckMaskEvent(x_display, eventmask, &event))
1178 break;
1179
1180 switch (event.type) {
1181 // Mouse button
1182 case ButtonPress: {
1183 unsigned int button = ((XButtonEvent *)&event)->button;
1184 if (button < 4)
1185 ADBMouseDown(button - 1);
1186 else if (button < 6) { // Wheel mouse
1187 if (mouse_wheel_mode == 0) {
1188 int key = (button == 5) ? 0x79 : 0x74; // Page up/down
1189 ADBKeyDown(key);
1190 ADBKeyUp(key);
1191 } else {
1192 int key = (button == 5) ? 0x3d : 0x3e; // Cursor up/down
1193 for(int i=0; i<mouse_wheel_lines; i++) {
1194 ADBKeyDown(key);
1195 ADBKeyUp(key);
1196 }
1197 }
1198 }
1199 break;
1200 }
1201 case ButtonRelease: {
1202 unsigned int button = ((XButtonEvent *)&event)->button;
1203 if (button < 4)
1204 ADBMouseUp(button - 1);
1205 break;
1206 }
1207
1208 // Mouse moved
1209 case EnterNotify:
1210 ADBMouseMoved(((XMotionEvent *)&event)->x, ((XMotionEvent *)&event)->y);
1211 break;
1212 case MotionNotify:
1213 ADBMouseMoved(((XMotionEvent *)&event)->x, ((XMotionEvent *)&event)->y);
1214 break;
1215
1216 // Keyboard
1217 case KeyPress: {
1218 int code;
1219 if (use_keycodes) {
1220 event2keycode((XKeyEvent *)&event); // This is called to process the hotkeys
1221 code = keycode_table[((XKeyEvent *)&event)->keycode & 0xff];
1222 } else
1223 code = event2keycode((XKeyEvent *)&event);
1224 if (code != -1) {
1225 if (!emul_suspended) {
1226 if (code == 0x39) { // Caps Lock pressed
1227 if (caps_on) {
1228 ADBKeyUp(code);
1229 caps_on = false;
1230 } else {
1231 ADBKeyDown(code);
1232 caps_on = true;
1233 }
1234 } else
1235 ADBKeyDown(code);
1236 if (code == 0x36)
1237 ctrl_down = true;
1238 } else {
1239 #if ENABLE_XF86_DGA || ENABLE_FBDEV_DGA
1240 if (code == 0x31)
1241 resume_emul(); // Space wakes us up
1242 #endif
1243 }
1244 }
1245 break;
1246 }
1247 case KeyRelease: {
1248 int code;
1249 if (use_keycodes) {
1250 event2keycode((XKeyEvent *)&event); // This is called to process the hotkeys
1251 code = keycode_table[((XKeyEvent *)&event)->keycode & 0xff];
1252 } else
1253 code = event2keycode((XKeyEvent *)&event);
1254 if (code != -1 && code != 0x39) { // Don't propagate Caps Lock releases
1255 ADBKeyUp(code);
1256 if (code == 0x36)
1257 ctrl_down = false;
1258 }
1259 break;
1260 }
1261
1262 // Hidden parts exposed, force complete refresh of window
1263 case Expose:
1264 if (display_type == DISPLAY_WINDOW)
1265 memset(the_buffer_copy, 0, VideoMonitor.bytes_per_row * VideoMonitor.y);
1266 break;
1267 }
1268 }
1269 }
1270
1271
1272 /*
1273 * Window display update
1274 */
1275
1276 static void update_display(void)
1277 {
1278 // In classic mode, copy the frame buffer from Mac RAM
1279 if (classic_mode)
1280 Mac2Host_memcpy(the_buffer, 0x3fa700, VideoMonitor.bytes_per_row * VideoMonitor.y);
1281
1282 // Incremental update code
1283 int wide = 0, high = 0, x1, x2, y1, y2, i, j;
1284 int bytes_per_row = VideoMonitor.bytes_per_row;
1285 int bytes_per_pixel = VideoMonitor.bytes_per_row / VideoMonitor.x;
1286 uint8 *p, *p2;
1287
1288 // Check for first line from top and first line from bottom that have changed
1289 y1 = 0;
1290 for (j=0; j<VideoMonitor.y; j++) {
1291 if (memcmp(&the_buffer[j * bytes_per_row], &the_buffer_copy[j * bytes_per_row], bytes_per_row)) {
1292 y1 = j;
1293 break;
1294 }
1295 }
1296 y2 = y1 - 1;
1297 for (j=VideoMonitor.y-1; j>=y1; j--) {
1298 if (memcmp(&the_buffer[j * bytes_per_row], &the_buffer_copy[j * bytes_per_row], bytes_per_row)) {
1299 y2 = j;
1300 break;
1301 }
1302 }
1303 high = y2 - y1 + 1;
1304
1305 // Check for first column from left and first column from right that have changed
1306 if (high) {
1307 if (depth == 1) {
1308 x1 = VideoMonitor.x;
1309 for (j=y1; j<=y2; j++) {
1310 p = &the_buffer[j * bytes_per_row];
1311 p2 = &the_buffer_copy[j * bytes_per_row];
1312 for (i=0; i<(x1>>3); i++) {
1313 if (*p != *p2) {
1314 x1 = i << 3;
1315 break;
1316 }
1317 p++;
1318 p2++;
1319 }
1320 }
1321 x2 = x1;
1322 for (j=y1; j<=y2; j++) {
1323 p = &the_buffer[j * bytes_per_row];
1324 p2 = &the_buffer_copy[j * bytes_per_row];
1325 p += bytes_per_row;
1326 p2 += bytes_per_row;
1327 for (i=(VideoMonitor.x>>3); i>(x2>>3); i--) {
1328 p--;
1329 p2--;
1330 if (*p != *p2) {
1331 x2 = i << 3;
1332 break;
1333 }
1334 }
1335 }
1336 wide = x2 - x1;
1337
1338 // Update copy of the_buffer
1339 if (high && wide) {
1340 for (j=y1; j<=y2; j++) {
1341 i = j * bytes_per_row + (x1 >> 3);
1342 memcpy(&the_buffer_copy[i], &the_buffer[i], wide >> 3);
1343 }
1344 }
1345
1346 } else {
1347 x1 = VideoMonitor.x;
1348 for (j=y1; j<=y2; j++) {
1349 p = &the_buffer[j * bytes_per_row];
1350 p2 = &the_buffer_copy[j * bytes_per_row];
1351 for (i=0; i<x1; i++) {
1352 if (memcmp(p, p2, bytes_per_pixel)) {
1353 x1 = i;
1354 break;
1355 }
1356 p += bytes_per_pixel;
1357 p2 += bytes_per_pixel;
1358 }
1359 }
1360 x2 = x1;
1361 for (j=y1; j<=y2; j++) {
1362 p = &the_buffer[j * bytes_per_row];
1363 p2 = &the_buffer_copy[j * bytes_per_row];
1364 p += bytes_per_row;
1365 p2 += bytes_per_row;
1366 for (i=VideoMonitor.x; i>x2; i--) {
1367 p -= bytes_per_pixel;
1368 p2 -= bytes_per_pixel;
1369 if (memcmp(p, p2, bytes_per_pixel)) {
1370 x2 = i;
1371 break;
1372 }
1373 }
1374 }
1375 wide = x2 - x1;
1376
1377 // Update copy of the_buffer
1378 if (high && wide) {
1379 for (j=y1; j<=y2; j++) {
1380 i = j * bytes_per_row + x1 * bytes_per_pixel;
1381 memcpy(&the_buffer_copy[i], &the_buffer[i], bytes_per_pixel * wide);
1382 }
1383 }
1384 }
1385 }
1386
1387 // Refresh display
1388 if (high && wide) {
1389 if (have_shm)
1390 XShmPutImage(x_display, the_win, the_gc, img, x1, y1, x1, y1, wide, high, 0);
1391 else
1392 XPutImage(x_display, the_win, the_gc, img, x1, y1, x1, y1, wide, high);
1393 }
1394
1395 // Has the Mac started? (cursor data is not valid otherwise)
1396 if (HasMacStarted()) {
1397
1398 // Set new cursor image if it was changed
1399 if (memcmp(the_cursor, Mac2HostAddr(0x844), 64)) {
1400 Mac2Host_memcpy(the_cursor, 0x844, 64);
1401 memcpy(cursor_image->data, the_cursor, 32);
1402 memcpy(cursor_mask_image->data, the_cursor+32, 32);
1403 XFreeCursor(x_display, mac_cursor);
1404 XPutImage(x_display, cursor_map, cursor_gc, cursor_image, 0, 0, 0, 0, 16, 16);
1405 XPutImage(x_display, cursor_mask_map, cursor_mask_gc, cursor_mask_image, 0, 0, 0, 0, 16, 16);
1406 mac_cursor = XCreatePixmapCursor(x_display, cursor_map, cursor_mask_map, &black, &white, ReadMacInt8(0x885), ReadMacInt8(0x887));
1407 XDefineCursor(x_display, the_win, mac_cursor);
1408 }
1409 }
1410 }
1411
1412
1413 /*
1414 * Thread for screen refresh, input handling etc.
1415 */
1416
1417 static void *redraw_func(void *arg)
1418 {
1419 int tick_counter = 0;
1420
1421 while (!redraw_thread_cancel) {
1422
1423 // Wait
1424 #ifdef HAVE_NANOSLEEP
1425 struct timespec req = {0, 16666667};
1426 nanosleep(&req, NULL);
1427 #else
1428 usleep(16667);
1429 #endif
1430
1431 #if ENABLE_XF86_DGA
1432 // Quit DGA mode if requested
1433 if (quit_full_screen) {
1434 quit_full_screen = false;
1435 if (display_type == DISPLAY_DGA) {
1436 XF86DGADirectVideo(x_display, screen, 0);
1437 XUngrabPointer(x_display, CurrentTime);
1438 XUngrabKeyboard(x_display, CurrentTime);
1439 XUnmapWindow(x_display, the_win);
1440 XSync(x_display, false);
1441 }
1442 }
1443 #endif
1444
1445 #if ENABLE_FBDEV_DGA
1446 // Quit DGA mode if requested
1447 if (quit_full_screen) {
1448 quit_full_screen = false;
1449 if (display_type == DISPLAY_DGA) {
1450 XUngrabPointer(x_display, CurrentTime);
1451 XUngrabKeyboard(x_display, CurrentTime);
1452 XUnmapWindow(x_display, the_win);
1453 XSync(x_display, false);
1454 }
1455 }
1456 #endif
1457 // Handle X events
1458 handle_events();
1459
1460 // Handle palette changes
1461 pthread_mutex_lock(&palette_lock);
1462 if (palette_changed) {
1463 palette_changed = false;
1464 if (depth == 8) {
1465 XStoreColors(x_display, cmap[0], palette, 256);
1466 XStoreColors(x_display, cmap[1], palette, 256);
1467
1468 #if ENABLE_XF86_DGA
1469 if (display_type == DISPLAY_DGA) {
1470 current_dga_cmap ^= 1;
1471 XF86DGAInstallColormap(x_display, screen, cmap[current_dga_cmap]);
1472 }
1473 #endif
1474 }
1475 }
1476 pthread_mutex_unlock(&palette_lock);
1477
1478 // In window mode, update display and mouse pointer
1479 if (display_type == DISPLAY_WINDOW) {
1480 tick_counter++;
1481 if (tick_counter >= frame_skip) {
1482 tick_counter = 0;
1483 update_display();
1484 }
1485 }
1486 }
1487 return NULL;
1488 }