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

File Contents

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