ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/video_x.cpp
Revision: 1.10
Committed: 1999-10-25T20:22:35Z (25 years, 1 month ago) by cebix
Branch: MAIN
CVS Tags: snapshot-02111999
Changes since 1.9: +21 -1 lines
Log Message:
- added mouse wheel support

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