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 ago) by cebix
Branch: MAIN
CVS Tags: snapshot-02111999
Changes since 1.9: +21 -1 lines
Log Message:
- added mouse wheel support

File Contents

# User Rev Content
1 cebix 1.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 cebix 1.7 #if ENABLE_XF86_DGA
52 cebix 1.1 #include <X11/extensions/xf86dga.h>
53     #endif
54    
55 cebix 1.7 #if ENABLE_FBDEV_DGA
56     #include <sys/mman.h>
57     #endif
58    
59    
60 cebix 1.1
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 cebix 1.4 const char KEYCODE_FILE_NAME[] = DATADIR "/keycodes";
70 cebix 1.7 const char FBDEVICES_FILE_NAME[] = DATADIR "/fbdevices";
71 cebix 1.1
72    
73     // Global variables
74 cebix 1.10 static int32 frame_skip; // Prefs items
75     static int16 mouse_wheel_mode = 1;
76     static int16 mouse_wheel_lines = 3;
77    
78 cebix 1.1 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 cebix 1.3 static bool caps_on = false; // Flag: Caps Lock on
88 cebix 1.1 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 cebix 1.7 // Variables for XF86 DGA mode
128 cebix 1.1 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 cebix 1.7 static pthread_mutex_t frame_buffer_lock = PTHREAD_MUTEX_INITIALIZER; // Mutex to protect frame buffer
132 cebix 1.1
133 cebix 1.7 // Variables for fbdev DGA mode
134     const char FBDEVICE_FILE_NAME[] = "/dev/fb";
135     static int fbdev_fd;
136 cebix 1.1
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 cebix 1.5 int layout = FLAYOUT_DIRECT;
158 cebix 1.1 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 cebix 1.7 wattr.backing_store = Always;
220     wattr.backing_planes = xdepth;
221 cebix 1.1
222     XSync(x_display, false);
223     the_win = XCreateWindow(x_display, rootwin, 0, 0, width, height, 0, xdepth,
224 cebix 1.7 InputOutput, vis, CWEventMask | CWBackPixel | CWBorderPixel |
225     CWBackingStore | CWBackingPlanes, &wattr);
226 cebix 1.1 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 cebix 1.7
249 cebix 1.1 // 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 cebix 1.7
276 cebix 1.1 // 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 cebix 1.7
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 cebix 1.9 printf("FATAL: Could not determine the maximal depth available\n");
347 cebix 1.7 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 cebix 1.8 const char *fbd_path = PrefsFindString("fbdevicefile");
357 cebix 1.7
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 cebix 1.8 if ((sscanf(line, "%19s %d %x", &fb_name, &fb_depth, &fb_offset) == 3)
384 cebix 1.7 && (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 cebix 1.9 XSetWindowColormap(x_display, the_win, cmap[0]);
436 cebix 1.7 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 cebix 1.1 #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 cebix 1.7 #else
473     ErrorAlert("Basilisk II has been compiled with fbdev DGA support disabled.");
474     return false;
475     #endif
476 cebix 1.1 }
477    
478 cebix 1.7 // Init XF86 DGA display
479     static bool init_xf86_dga(int width, int height)
480 cebix 1.1 {
481 cebix 1.7 #if ENABLE_XF86_DGA
482 cebix 1.1 // 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 cebix 1.7 ErrorAlert("Basilisk II has been compiled with XF86 DGA support disabled.");
543 cebix 1.1 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 cebix 1.10 // Read prefs
618     mouse_wheel_mode = PrefsFindInt16("mousewheelmode");
619     mouse_wheel_lines = PrefsFindInt16("mousewheellines");
620    
621 cebix 1.1 // Find screen and root window
622     screen = XDefaultScreen(x_display);
623     rootwin = XRootWindow(x_display, screen);
624 cebix 1.7
625 cebix 1.1 // Get screen depth
626     xdepth = DefaultDepth(x_display, screen);
627 cebix 1.7
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 cebix 1.1 // DGA available?
641 cebix 1.5 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 cebix 1.1 #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 cebix 1.7 #if ENABLE_FBDEV_DGA
716 cebix 1.8 else if (has_dga && sscanf(mode_str, "dga/%19s", fb_name) == 1) {
717 cebix 1.7 #else
718 cebix 1.3 else if (has_dga && sscanf(mode_str, "dga/%d/%d", &width, &height) == 2) {
719 cebix 1.7 #endif
720 cebix 1.1 display_type = DISPLAY_DGA;
721 cebix 1.3 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 cebix 1.1 width = DisplayWidth(x_display, screen);
728 cebix 1.3 if (height <= 0)
729 cebix 1.1 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 cebix 1.7 #if ENABLE_FBDEV_DGA
740     if (!init_fbdev_dga(fb_name))
741     #else
742     if (!init_xf86_dga(width, height))
743     #endif
744 cebix 1.1 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 cebix 1.7 #if ENABLE_XF86_DGA
794 cebix 1.1 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 cebix 1.7 #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 cebix 1.1 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 cebix 1.7 #if ENABLE_XF86_DGA || ENABLE_FBDEV_DGA
882 cebix 1.1 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 cebix 1.7 #if ENABLE_XF86_DGA
899 cebix 1.1 XF86DGADirectVideo(x_display, screen, 0);
900 cebix 1.7 #endif
901 cebix 1.1 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 cebix 1.7
915 cebix 1.1 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 cebix 1.7 #if ENABLE_XF86_DGA
940 cebix 1.1 XF86DGADirectVideo(x_display, screen, XF86DGADirectGraphics | XF86DGADirectKeyb | XF86DGADirectMouse);
941     XF86DGASetViewPort(x_display, screen, 0, 0);
942 cebix 1.7 #endif
943 cebix 1.1 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 cebix 1.7
952 cebix 1.1 if (depth == 8)
953 cebix 1.7 #if ENABLE_XF86_DGA
954 cebix 1.1 XF86DGAInstallColormap(x_display, screen, cmap[current_dga_cmap]);
955 cebix 1.7 #endif
956 cebix 1.1
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 cebix 1.7 #if ENABLE_XF86_DGA || ENABLE_FBDEV_DGA
1022 cebix 1.1 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 cebix 1.10 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 cebix 1.1 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 cebix 1.3 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 cebix 1.1 if (code == 0x36)
1198     ctrl_down = true;
1199     } else {
1200 cebix 1.7 #if ENABLE_XF86_DGA || ENABLE_FBDEV_DGA
1201 cebix 1.1 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 cebix 1.3 if (code != -1 && code != 0x39) { // Don't propagate Caps Lock releases
1216 cebix 1.1 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 cebix 1.7
1243 cebix 1.1 // 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 cebix 1.7 #if ENABLE_XF86_DGA
1393 cebix 1.1 // 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 cebix 1.7 #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 cebix 1.1 // 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 cebix 1.7
1429     #if ENABLE_XF86_DGA
1430 cebix 1.1 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     }