ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/SheepShaver/src/Unix/video_x.cpp
Revision: 1.1
Committed: 2002-02-04T16:58:13Z (22 years, 4 months ago) by cebix
Branch: MAIN
Branch point for: cebix
Log Message:
Initial revision

File Contents

# User Rev Content
1 cebix 1.1 /*
2     * video_x.cpp - Video/graphics emulation, X11 specific stuff
3     *
4     * SheepShaver (C) 1997-2002 Marc Hellwig and 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     #include <X11/Xlib.h>
22     #include <X11/Xutil.h>
23     #include <X11/keysym.h>
24     #include <X11/extensions/XShm.h>
25     #include <sys/ipc.h>
26     #include <sys/shm.h>
27     #include <pthread.h>
28    
29     #include "sysdeps.h"
30     #include "main.h"
31     #include "adb.h"
32     #include "prefs.h"
33     #include "user_strings.h"
34     #include "about_window.h"
35     #include "video.h"
36     #include "video_defs.h"
37    
38     #define DEBUG 0
39     #include "debug.h"
40    
41     #ifdef ENABLE_XF86_DGA
42     #include <X11/extensions/xf86dga.h>
43     #endif
44    
45     #ifdef ENABLE_XF86_VIDMODE
46     #include <X11/extensions/xf86vmode.h>
47     #endif
48    
49    
50     // Global variables
51     static int32 frame_skip;
52     static bool redraw_thread_active = false; // Flag: Redraw thread installed
53     static pthread_t redraw_thread; // Redraw thread
54    
55     static volatile bool thread_stop_req = false;
56     static volatile bool thread_stop_ack = false; // Acknowledge for thread_stop_req
57    
58     static bool has_dga = false; // Flag: Video DGA capable
59     static bool has_vidmode = false; // Flag: VidMode extension available
60    
61     static bool palette_changed = false; // Flag: Palette changed, redraw thread must update palette
62     static bool ctrl_down = false; // Flag: Ctrl key pressed
63     static bool quit_full_screen = false; // Flag: DGA close requested from redraw thread
64     static volatile bool quit_full_screen_ack = false; // Acknowledge for quit_full_screen
65     static bool emerg_quit = false; // Flag: Ctrl-Esc pressed, emergency quit requested from MacOS thread
66    
67     static bool emul_suspended = false; // Flag: emulator suspended
68     static Window suspend_win; // "Suspend" window
69     static void *fb_save = NULL; // Saved frame buffer for suspend
70    
71     // X11 variables
72     static int screen; // Screen number
73     static int xdepth; // Depth of X screen
74     static int depth; // Depth of Mac frame buffer
75     static Window rootwin, the_win; // Root window and our window
76     static XVisualInfo visualInfo;
77     static Visual *vis;
78     static Colormap cmap[2]; // Two colormaps (DGA) for 8-bit mode
79     static XColor black, white;
80     static unsigned long black_pixel, white_pixel;
81     static int eventmask;
82     static const int win_eventmask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | EnterWindowMask | ExposureMask;
83     static const int dga_eventmask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask;
84    
85     // Variables for window mode
86     static GC the_gc;
87     static XImage *img = NULL;
88     static XShmSegmentInfo shminfo;
89     static XImage *cursor_image, *cursor_mask_image;
90     static Pixmap cursor_map, cursor_mask_map;
91     static Cursor mac_cursor;
92     static GC cursor_gc, cursor_mask_gc;
93     static bool cursor_changed = false; // Flag: Cursor changed, window_func must update cursor
94     static bool have_shm = false; // Flag: SHM present and usable
95     static uint8 *the_buffer; // Pointer to Mac frame buffer
96     static uint8 *the_buffer_copy = NULL; // Copy of Mac frame buffer
97    
98     // Variables for DGA mode
99     static char *dga_screen_base;
100     static int dga_fb_width;
101     static int current_dga_cmap;
102    
103     #ifdef ENABLE_XF86_VIDMODE
104     // Variables for XF86 VidMode support
105     static XF86VidModeModeInfo **x_video_modes; // Array of all available modes
106     static int num_x_video_modes;
107     #endif
108    
109    
110     // Prototypes
111     static void *redraw_func(void *arg);
112    
113    
114     // From main_linux.cpp
115     extern Display *x_display;
116    
117     // From sys_unix.cpp
118     extern void SysMountFirstFloppy(void);
119    
120    
121     /*
122     * Open display (window or fullscreen)
123     */
124    
125     // Trap SHM errors
126     static bool shm_error = false;
127     static int (*old_error_handler)(Display *, XErrorEvent *);
128    
129     static int error_handler(Display *d, XErrorEvent *e)
130     {
131     if (e->error_code == BadAccess) {
132     shm_error = true;
133     return 0;
134     } else
135     return old_error_handler(d, e);
136     }
137    
138     // Open window
139     static bool open_window(int width, int height)
140     {
141     // Set absolute mouse mode
142     ADBSetRelMouseMode(false);
143    
144     // Read frame skip prefs
145     frame_skip = PrefsFindInt32("frameskip");
146     if (frame_skip == 0)
147     frame_skip = 1;
148    
149     // Create window
150     XSetWindowAttributes wattr;
151     wattr.event_mask = eventmask = win_eventmask;
152     wattr.background_pixel = black_pixel;
153     wattr.border_pixel = black_pixel;
154     wattr.backing_store = NotUseful;
155    
156     XSync(x_display, false);
157     the_win = XCreateWindow(x_display, rootwin, 0, 0, width, height, 0, xdepth,
158     InputOutput, vis, CWEventMask | CWBackPixel | CWBorderPixel | CWBackingStore, &wattr);
159     XSync(x_display, false);
160     XStoreName(x_display, the_win, GetString(STR_WINDOW_TITLE));
161     XMapRaised(x_display, the_win);
162     XSync(x_display, false);
163    
164     // Set colormap
165     if (depth == 8) {
166     XSetWindowColormap(x_display, the_win, cmap[0]);
167     XSetWMColormapWindows(x_display, the_win, &the_win, 1);
168     }
169    
170     // Make window unresizable
171     XSizeHints *hints;
172     if ((hints = XAllocSizeHints()) != NULL) {
173     hints->min_width = width;
174     hints->max_width = width;
175     hints->min_height = height;
176     hints->max_height = height;
177     hints->flags = PMinSize | PMaxSize;
178     XSetWMNormalHints(x_display, the_win, hints);
179     XFree((char *)hints);
180     }
181    
182     // Try to create and attach SHM image
183     have_shm = false;
184     if (depth != 1 && XShmQueryExtension(x_display)) {
185    
186     // Create SHM image ("height + 2" for safety)
187     img = XShmCreateImage(x_display, vis, depth, depth == 1 ? XYBitmap : ZPixmap, 0, &shminfo, width, height);
188     shminfo.shmid = shmget(IPC_PRIVATE, (height + 2) * img->bytes_per_line, IPC_CREAT | 0777);
189     screen_base = (uint32)shmat(shminfo.shmid, 0, 0);
190     the_buffer = (uint8 *)screen_base;
191     shminfo.shmaddr = img->data = (char *)screen_base;
192     shminfo.readOnly = False;
193    
194     // Try to attach SHM image, catching errors
195     shm_error = false;
196     old_error_handler = XSetErrorHandler(error_handler);
197     XShmAttach(x_display, &shminfo);
198     XSync(x_display, false);
199     XSetErrorHandler(old_error_handler);
200     if (shm_error) {
201     shmdt(shminfo.shmaddr);
202     XDestroyImage(img);
203     shminfo.shmid = -1;
204     } else {
205     have_shm = true;
206     shmctl(shminfo.shmid, IPC_RMID, 0);
207     }
208     }
209    
210     // Create normal X image if SHM doesn't work ("height + 2" for safety)
211     if (!have_shm) {
212     int bytes_per_row = width;
213     switch (depth) {
214     case 1:
215     bytes_per_row /= 8;
216     break;
217     case 15:
218     case 16:
219     bytes_per_row *= 2;
220     break;
221     case 24:
222     case 32:
223     bytes_per_row *= 4;
224     break;
225     }
226     screen_base = (uint32)malloc((height + 2) * bytes_per_row);
227     the_buffer = (uint8 *)screen_base;
228     img = XCreateImage(x_display, vis, depth, depth == 1 ? XYBitmap : ZPixmap, 0, (char *)screen_base, width, height, 32, bytes_per_row);
229     }
230    
231     // 1-Bit mode is big-endian
232     if (depth == 1) {
233     img->byte_order = MSBFirst;
234     img->bitmap_bit_order = MSBFirst;
235     }
236    
237     // Allocate memory for frame buffer copy
238     the_buffer_copy = (uint8 *)malloc((height + 2) * img->bytes_per_line);
239    
240     // Create GC
241     the_gc = XCreateGC(x_display, the_win, 0, 0);
242     XSetForeground(x_display, the_gc, black_pixel);
243    
244     // Create cursor
245     cursor_image = XCreateImage(x_display, vis, 1, XYPixmap, 0, (char *)MacCursor + 4, 16, 16, 16, 2);
246     cursor_image->byte_order = MSBFirst;
247     cursor_image->bitmap_bit_order = MSBFirst;
248     cursor_mask_image = XCreateImage(x_display, vis, 1, XYPixmap, 0, (char *)MacCursor + 36, 16, 16, 16, 2);
249     cursor_mask_image->byte_order = MSBFirst;
250     cursor_mask_image->bitmap_bit_order = MSBFirst;
251     cursor_map = XCreatePixmap(x_display, the_win, 16, 16, 1);
252     cursor_mask_map = XCreatePixmap(x_display, the_win, 16, 16, 1);
253     cursor_gc = XCreateGC(x_display, cursor_map, 0, 0);
254     cursor_mask_gc = XCreateGC(x_display, cursor_mask_map, 0, 0);
255     mac_cursor = XCreatePixmapCursor(x_display, cursor_map, cursor_mask_map, &black, &white, 0, 0);
256     cursor_changed = false;
257    
258     // Set bytes per row
259     VModes[cur_mode].viRowBytes = img->bytes_per_line;
260     XSync(x_display, false);
261     return true;
262     }
263    
264     // Open DGA display (!! should use X11 VidMode extensions to set mode)
265     static bool open_dga(int width, int height)
266     {
267     #ifdef ENABLE_XF86_DGA
268     // Set relative mouse mode
269     ADBSetRelMouseMode(true);
270    
271     #ifdef ENABLE_XF86_VIDMODE
272     // Switch to best mode
273     if (has_vidmode) {
274     int best = 0;
275     for (int i=1; i<num_x_video_modes; i++) {
276     if (x_video_modes[i]->hdisplay >= width && x_video_modes[i]->vdisplay >= height &&
277     x_video_modes[i]->hdisplay <= x_video_modes[best]->hdisplay && x_video_modes[i]->vdisplay <= x_video_modes[best]->vdisplay) {
278     best = i;
279     }
280     }
281     XF86VidModeSwitchToMode(x_display, screen, x_video_modes[best]);
282     XF86VidModeSetViewPort(x_display, screen, 0, 0);
283     }
284     #endif
285    
286     // Establish direct screen connection
287     XGrabKeyboard(x_display, rootwin, True, GrabModeAsync, GrabModeAsync, CurrentTime);
288     XGrabPointer(x_display, rootwin, True, PointerMotionMask | ButtonPressMask | ButtonReleaseMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
289     XF86DGADirectVideo(x_display, screen, XF86DGADirectGraphics | XF86DGADirectKeyb | XF86DGADirectMouse);
290     XF86DGASetViewPort(x_display, screen, 0, 0);
291     XF86DGASetVidPage(x_display, screen, 0);
292     screen_base = (uint32)dga_screen_base;
293    
294     // Set colormap
295     if (depth == 8)
296     XF86DGAInstallColormap(x_display, screen, cmap[current_dga_cmap]);
297    
298     // Set bytes per row
299     int bytes_per_row = (dga_fb_width + 7) & ~7;
300     switch (depth) {
301     case 15:
302     case 16:
303     bytes_per_row *= 2;
304     break;
305     case 24:
306     case 32:
307     bytes_per_row *= 4;
308     break;
309     }
310     VModes[cur_mode].viRowBytes = bytes_per_row;
311     XSync(x_display, false);
312     return true;
313     #else
314     ErrorAlert("SheepShaver has been compiled with DGA support disabled.");
315     return false;
316     #endif
317     }
318    
319     static bool open_display(void)
320     {
321     display_type = VModes[cur_mode].viType;
322     switch (VModes[cur_mode].viAppleMode) {
323     case APPLE_1_BIT:
324     depth = 1;
325     break;
326     case APPLE_2_BIT:
327     depth = 2;
328     break;
329     case APPLE_4_BIT:
330     depth = 4;
331     break;
332     case APPLE_8_BIT:
333     depth = 8;
334     break;
335     case APPLE_16_BIT:
336     depth = 16;
337     break;
338     case APPLE_32_BIT:
339     depth = 32;
340     break;
341     }
342     if (display_type == DIS_SCREEN)
343     return open_dga(VModes[cur_mode].viXsize, VModes[cur_mode].viYsize);
344     else if (display_type == DIS_WINDOW)
345     return open_window(VModes[cur_mode].viXsize, VModes[cur_mode].viYsize);
346     else
347     return false;
348     }
349    
350    
351     /*
352     * Close display
353     */
354    
355     // Close window
356     static void close_window(void)
357     {
358     // Close window
359     XDestroyWindow(x_display, the_win);
360    
361     // Close frame buffer copy
362     if (the_buffer_copy) {
363     free(the_buffer_copy);
364     the_buffer_copy = NULL;
365     }
366     }
367    
368     // Close DGA mode
369     static void close_dga(void)
370     {
371     #ifdef ENABLE_XF86_DGA
372     XF86DGADirectVideo(x_display, screen, 0);
373     XUngrabPointer(x_display, CurrentTime);
374     XUngrabKeyboard(x_display, CurrentTime);
375     #endif
376    
377     #ifdef ENABLE_XF86_VIDMODE
378     if (has_vidmode)
379     XF86VidModeSwitchToMode(x_display, screen, x_video_modes[0]);
380     #endif
381     }
382    
383     static void close_display(void)
384     {
385     if (display_type == DIS_SCREEN)
386     close_dga();
387     else if (display_type == DIS_WINDOW)
388     close_window();
389     }
390    
391    
392     /*
393     * Initialization
394     */
395    
396     static void add_mode(VideoInfo *&p, uint32 allow, uint32 test, long apple_mode, long apple_id, int type)
397     {
398     if (allow & test) {
399     p->viType = type;
400     switch (apple_id) {
401     case APPLE_W_640x480:
402     case APPLE_640x480:
403     p->viXsize = 640;
404     p->viYsize = 480;
405     break;
406     case APPLE_W_800x600:
407     case APPLE_800x600:
408     p->viXsize = 800;
409     p->viYsize = 600;
410     break;
411     case APPLE_1024x768:
412     p->viXsize = 1024;
413     p->viYsize = 768;
414     break;
415     case APPLE_1152x900:
416     p->viXsize = 1152;
417     p->viYsize = 900;
418     break;
419     case APPLE_1280x1024:
420     p->viXsize = 1280;
421     p->viYsize = 1024;
422     break;
423     case APPLE_1600x1200:
424     p->viXsize = 1600;
425     p->viYsize = 1200;
426     break;
427     }
428     switch (apple_mode) {
429     case APPLE_8_BIT:
430     p->viRowBytes = p->viXsize;
431     break;
432     case APPLE_16_BIT:
433     p->viRowBytes = p->viXsize * 2;
434     break;
435     case APPLE_32_BIT:
436     p->viRowBytes = p->viXsize * 4;
437     break;
438     }
439     p->viAppleMode = apple_mode;
440     p->viAppleID = apple_id;
441     p++;
442     }
443     }
444    
445     static bool has_mode(int x, int y)
446     {
447     #ifdef ENABLE_XF86_VIDMODE
448     for (int i=0; i<num_x_video_modes; i++)
449     if (x_video_modes[i]->hdisplay >= x && x_video_modes[i]->vdisplay >= y)
450     return true;
451     return false;
452     #else
453     return DisplayWidth(x_display, screen) >= x && DisplayHeight(x_display, screen) >= y;
454     #endif
455     }
456    
457     bool VideoInit(void)
458     {
459     // Init variables
460     private_data = NULL;
461     cur_mode = 0; // Window 640x480
462     video_activated = true;
463    
464     // Find screen and root window
465     screen = XDefaultScreen(x_display);
466     rootwin = XRootWindow(x_display, screen);
467    
468     // Get screen depth
469     xdepth = DefaultDepth(x_display, screen);
470    
471     #ifdef ENABLE_XF86_DGA
472     // DGA available?
473     int event_base, error_base;
474     if (XF86DGAQueryExtension(x_display, &event_base, &error_base)) {
475     int dga_flags = 0;
476     XF86DGAQueryDirectVideo(x_display, screen, &dga_flags);
477     has_dga = dga_flags & XF86DGADirectPresent;
478     } else
479     has_dga = false;
480     #endif
481    
482     #ifdef ENABLE_XF86_VIDMODE
483     // VidMode available?
484     int vm_event_base, vm_error_base;
485     has_vidmode = XF86VidModeQueryExtension(x_display, &vm_event_base, &vm_error_base);
486     if (has_vidmode)
487     XF86VidModeGetAllModeLines(x_display, screen, &num_x_video_modes, &x_video_modes);
488     #endif
489    
490     // Find black and white colors
491     XParseColor(x_display, DefaultColormap(x_display, screen), "rgb:00/00/00", &black);
492     XAllocColor(x_display, DefaultColormap(x_display, screen), &black);
493     XParseColor(x_display, DefaultColormap(x_display, screen), "rgb:ff/ff/ff", &white);
494     XAllocColor(x_display, DefaultColormap(x_display, screen), &white);
495     black_pixel = BlackPixel(x_display, screen);
496     white_pixel = WhitePixel(x_display, screen);
497    
498     // Get appropriate visual
499     int color_class;
500     switch (xdepth) {
501     #if 0
502     case 1:
503     color_class = StaticGray;
504     break;
505     #endif
506     case 8:
507     color_class = PseudoColor;
508     break;
509     case 15:
510     case 16:
511     case 24:
512     case 32:
513     color_class = TrueColor;
514     break;
515     default:
516     ErrorAlert(GetString(STR_UNSUPP_DEPTH_ERR));
517     return false;
518     }
519     if (!XMatchVisualInfo(x_display, screen, xdepth, color_class, &visualInfo)) {
520     ErrorAlert(GetString(STR_NO_XVISUAL_ERR));
521     return false;
522     }
523     if (visualInfo.depth != xdepth) {
524     ErrorAlert(GetString(STR_NO_XVISUAL_ERR));
525     return false;
526     }
527     vis = visualInfo.visual;
528    
529     // Mac screen depth follows X depth (for now)
530     depth = xdepth;
531    
532     // Create color maps for 8 bit mode
533     if (depth == 8) {
534     cmap[0] = XCreateColormap(x_display, rootwin, vis, AllocAll);
535     cmap[1] = XCreateColormap(x_display, rootwin, vis, AllocAll);
536     XInstallColormap(x_display, cmap[0]);
537     XInstallColormap(x_display, cmap[1]);
538     }
539    
540     // Construct video mode table
541     int mode = APPLE_8_BIT;
542     int bpr_mult = 8;
543     switch (depth) {
544     case 1:
545     mode = APPLE_1_BIT;
546     bpr_mult = 1;
547     break;
548     case 8:
549     mode = APPLE_8_BIT;
550     bpr_mult = 8;
551     break;
552     case 15:
553     case 16:
554     mode = APPLE_16_BIT;
555     bpr_mult = 16;
556     break;
557     case 24:
558     case 32:
559     mode = APPLE_32_BIT;
560     bpr_mult = 32;
561     break;
562     }
563    
564     uint32 window_modes = PrefsFindInt32("windowmodes");
565     uint32 screen_modes = PrefsFindInt32("screenmodes");
566     if (!has_dga)
567     screen_modes = 0;
568     if (window_modes == 0 && screen_modes == 0)
569     window_modes |= 3; // Allow at least 640x480 and 800x600 window modes
570    
571     VideoInfo *p = VModes;
572     add_mode(p, window_modes, 1, mode, APPLE_W_640x480, DIS_WINDOW);
573     add_mode(p, window_modes, 2, mode, APPLE_W_800x600, DIS_WINDOW);
574     if (has_vidmode) {
575     if (has_mode(640, 480))
576     add_mode(p, screen_modes, 1, mode, APPLE_640x480, DIS_SCREEN);
577     if (has_mode(800, 600))
578     add_mode(p, screen_modes, 2, mode, APPLE_800x600, DIS_SCREEN);
579     if (has_mode(1024, 768))
580     add_mode(p, screen_modes, 4, mode, APPLE_1024x768, DIS_SCREEN);
581     if (has_mode(1152, 900))
582     add_mode(p, screen_modes, 8, mode, APPLE_1152x900, DIS_SCREEN);
583     if (has_mode(1280, 1024))
584     add_mode(p, screen_modes, 16, mode, APPLE_1280x1024, DIS_SCREEN);
585     if (has_mode(1600, 1200))
586     add_mode(p, screen_modes, 32, mode, APPLE_1600x1200, DIS_SCREEN);
587     } else if (screen_modes) {
588     int xsize = DisplayWidth(x_display, screen);
589     int ysize = DisplayHeight(x_display, screen);
590     int apple_id;
591     if (xsize < 800)
592     apple_id = APPLE_640x480;
593     else if (xsize < 1024)
594     apple_id = APPLE_800x600;
595     else if (xsize < 1152)
596     apple_id = APPLE_1024x768;
597     else if (xsize < 1280)
598     apple_id = APPLE_1152x900;
599     else if (xsize < 1600)
600     apple_id = APPLE_1280x1024;
601     else
602     apple_id = APPLE_1600x1200;
603     p->viType = DIS_SCREEN;
604     p->viRowBytes = 0;
605     p->viXsize = xsize;
606     p->viYsize = ysize;
607     p->viAppleMode = mode;
608     p->viAppleID = apple_id;
609     p++;
610     }
611     p->viType = DIS_INVALID; // End marker
612     p->viRowBytes = 0;
613     p->viXsize = p->viYsize = 0;
614     p->viAppleMode = 0;
615     p->viAppleID = 0;
616    
617     #ifdef ENABLE_XF86_DGA
618     if (has_dga && screen_modes) {
619     int v_bank, v_size;
620     XF86DGAGetVideo(x_display, screen, &dga_screen_base, &dga_fb_width, &v_bank, &v_size);
621     D(bug("DGA screen_base %p, v_width %d\n", dga_screen_base, dga_fb_width));
622     }
623     #endif
624    
625     // Open window/screen
626     if (!open_display())
627     return false;
628    
629     #if 0
630     // Ignore errors from now on
631     XSetErrorHandler(ignore_errors);
632     #endif
633    
634     // Start periodic thread
635     XSync(x_display, false);
636     redraw_thread_active = (pthread_create(&redraw_thread, NULL, redraw_func, NULL) == 0);
637     D(bug("Redraw thread installed (%ld)\n", redraw_thread));
638     return true;
639     }
640    
641    
642     /*
643     * Deinitialization
644     */
645    
646     void VideoExit(void)
647     {
648     // Stop redraw thread
649     if (redraw_thread_active) {
650     pthread_cancel(redraw_thread);
651     pthread_join(redraw_thread, NULL);
652     redraw_thread_active = false;
653     }
654    
655     // Close window and server connection
656     if (x_display != NULL) {
657     XSync(x_display, false);
658     close_display();
659     XFlush(x_display);
660     XSync(x_display, false);
661     if (depth == 8) {
662     XFreeColormap(x_display, cmap[0]);
663     XFreeColormap(x_display, cmap[1]);
664     }
665     }
666     }
667    
668    
669     /*
670     * Suspend/resume emulator
671     */
672    
673     extern void PauseEmulator(void);
674     extern void ResumeEmulator(void);
675    
676     static void suspend_emul(void)
677     {
678     if (display_type == DIS_SCREEN) {
679     // Release ctrl key
680     ADBKeyUp(0x36);
681     ctrl_down = false;
682    
683     // Pause MacOS thread
684     PauseEmulator();
685     emul_suspended = true;
686    
687     // Save frame buffer
688     fb_save = malloc(VModes[cur_mode].viYsize * VModes[cur_mode].viRowBytes);
689     if (fb_save)
690     memcpy(fb_save, (void *)screen_base, VModes[cur_mode].viYsize * VModes[cur_mode].viRowBytes);
691    
692     // Close full screen display
693     #ifdef ENABLE_XF86_DGA
694     XF86DGADirectVideo(x_display, screen, 0);
695     XUngrabPointer(x_display, CurrentTime);
696     XUngrabKeyboard(x_display, CurrentTime);
697     #endif
698     XSync(x_display, false);
699    
700     // Open "suspend" window
701     XSetWindowAttributes wattr;
702     wattr.event_mask = KeyPressMask;
703     wattr.background_pixel = black_pixel;
704     wattr.border_pixel = black_pixel;
705     wattr.backing_store = Always;
706     wattr.backing_planes = xdepth;
707     wattr.colormap = DefaultColormap(x_display, screen);
708     XSync(x_display, false);
709     suspend_win = XCreateWindow(x_display, rootwin, 0, 0, 512, 1, 0, xdepth,
710     InputOutput, vis, CWEventMask | CWBackPixel | CWBorderPixel |
711     CWBackingStore | CWBackingPlanes | (xdepth == 8 ? CWColormap : 0), &wattr);
712     XSync(x_display, false);
713     XStoreName(x_display, suspend_win, GetString(STR_SUSPEND_WINDOW_TITLE));
714     XMapRaised(x_display, suspend_win);
715     XSync(x_display, false);
716     }
717     }
718    
719     static void resume_emul(void)
720     {
721     // Close "suspend" window
722     XDestroyWindow(x_display, suspend_win);
723     XSync(x_display, false);
724    
725     // Reopen full screen display
726     XGrabKeyboard(x_display, rootwin, 1, GrabModeAsync, GrabModeAsync, CurrentTime);
727     XGrabPointer(x_display, rootwin, 1, PointerMotionMask | ButtonPressMask | ButtonReleaseMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
728     XF86DGADirectVideo(x_display, screen, XF86DGADirectGraphics | XF86DGADirectKeyb | XF86DGADirectMouse);
729     XF86DGASetViewPort(x_display, screen, 0, 0);
730     XSync(x_display, false);
731    
732     // Restore frame buffer
733     if (fb_save) {
734     memcpy((void *)screen_base, fb_save, VModes[cur_mode].viYsize * VModes[cur_mode].viRowBytes);
735     free(fb_save);
736     fb_save = NULL;
737     }
738     if (depth == 8)
739     palette_changed = true;
740    
741     // Resume MacOS thread
742     emul_suspended = false;
743     ResumeEmulator();
744     }
745    
746    
747     /*
748     * Close screen in full-screen mode
749     */
750    
751     void VideoQuitFullScreen(void)
752     {
753     D(bug("VideoQuitFullScreen()\n"));
754     if (display_type == DIS_SCREEN) {
755     quit_full_screen = true;
756     while (!quit_full_screen_ack) ;
757     }
758     }
759    
760    
761     /*
762     * X11 event handling
763     */
764    
765     // Translate key event to Mac keycode
766     static int kc_decode(KeySym ks)
767     {
768     switch (ks) {
769     case XK_A: case XK_a: return 0x00;
770     case XK_B: case XK_b: return 0x0b;
771     case XK_C: case XK_c: return 0x08;
772     case XK_D: case XK_d: return 0x02;
773     case XK_E: case XK_e: return 0x0e;
774     case XK_F: case XK_f: return 0x03;
775     case XK_G: case XK_g: return 0x05;
776     case XK_H: case XK_h: return 0x04;
777     case XK_I: case XK_i: return 0x22;
778     case XK_J: case XK_j: return 0x26;
779     case XK_K: case XK_k: return 0x28;
780     case XK_L: case XK_l: return 0x25;
781     case XK_M: case XK_m: return 0x2e;
782     case XK_N: case XK_n: return 0x2d;
783     case XK_O: case XK_o: return 0x1f;
784     case XK_P: case XK_p: return 0x23;
785     case XK_Q: case XK_q: return 0x0c;
786     case XK_R: case XK_r: return 0x0f;
787     case XK_S: case XK_s: return 0x01;
788     case XK_T: case XK_t: return 0x11;
789     case XK_U: case XK_u: return 0x20;
790     case XK_V: case XK_v: return 0x09;
791     case XK_W: case XK_w: return 0x0d;
792     case XK_X: case XK_x: return 0x07;
793     case XK_Y: case XK_y: return 0x10;
794     case XK_Z: case XK_z: return 0x06;
795    
796     case XK_1: case XK_exclam: return 0x12;
797     case XK_2: case XK_at: return 0x13;
798     case XK_3: case XK_numbersign: return 0x14;
799     case XK_4: case XK_dollar: return 0x15;
800     case XK_5: case XK_percent: return 0x17;
801     case XK_6: return 0x16;
802     case XK_7: return 0x1a;
803     case XK_8: return 0x1c;
804     case XK_9: return 0x19;
805     case XK_0: return 0x1d;
806    
807     case XK_grave: case XK_asciitilde: return 0x0a;
808     case XK_minus: case XK_underscore: return 0x1b;
809     case XK_equal: case XK_plus: return 0x18;
810     case XK_bracketleft: case XK_braceleft: return 0x21;
811     case XK_bracketright: case XK_braceright: return 0x1e;
812     case XK_backslash: case XK_bar: return 0x2a;
813     case XK_semicolon: case XK_colon: return 0x29;
814     case XK_apostrophe: case XK_quotedbl: return 0x27;
815     case XK_comma: case XK_less: return 0x2b;
816     case XK_period: case XK_greater: return 0x2f;
817     case XK_slash: case XK_question: return 0x2c;
818    
819     case XK_Tab: if (ctrl_down) {suspend_emul(); return -1;} else return 0x30;
820     case XK_Return: return 0x24;
821     case XK_space: return 0x31;
822     case XK_BackSpace: return 0x33;
823    
824     case XK_Delete: return 0x75;
825     case XK_Insert: return 0x72;
826     case XK_Home: case XK_Help: return 0x73;
827     case XK_End: return 0x77;
828     #ifdef __hpux
829     case XK_Prior: return 0x74;
830     case XK_Next: return 0x79;
831     #else
832     case XK_Page_Up: return 0x74;
833     case XK_Page_Down: return 0x79;
834     #endif
835    
836     case XK_Control_L: return 0x36;
837     case XK_Control_R: return 0x36;
838     case XK_Shift_L: return 0x38;
839     case XK_Shift_R: return 0x38;
840     case XK_Alt_L: return 0x37;
841     case XK_Alt_R: return 0x37;
842     case XK_Meta_L: return 0x3a;
843     case XK_Meta_R: return 0x3a;
844     case XK_Menu: return 0x32;
845     case XK_Caps_Lock: return 0x39;
846     case XK_Num_Lock: return 0x47;
847    
848     case XK_Up: return 0x3e;
849     case XK_Down: return 0x3d;
850     case XK_Left: return 0x3b;
851     case XK_Right: return 0x3c;
852    
853     case XK_Escape: if (ctrl_down) {quit_full_screen = true; emerg_quit = true; return -1;} else return 0x35;
854    
855     case XK_F1: if (ctrl_down) {SysMountFirstFloppy(); return -1;} else return 0x7a;
856     case XK_F2: return 0x78;
857     case XK_F3: return 0x63;
858     case XK_F4: return 0x76;
859     case XK_F5: return 0x60;
860     case XK_F6: return 0x61;
861     case XK_F7: return 0x62;
862     case XK_F8: return 0x64;
863     case XK_F9: return 0x65;
864     case XK_F10: return 0x6d;
865     case XK_F11: return 0x67;
866     case XK_F12: return 0x6f;
867    
868     case XK_Print: return 0x69;
869     case XK_Scroll_Lock: return 0x6b;
870     case XK_Pause: return 0x71;
871    
872     #if defined(XK_KP_Prior) && defined(XK_KP_Left) && defined(XK_KP_Insert) && defined (XK_KP_End)
873     case XK_KP_0: case XK_KP_Insert: return 0x52;
874     case XK_KP_1: case XK_KP_End: return 0x53;
875     case XK_KP_2: case XK_KP_Down: return 0x54;
876     case XK_KP_3: case XK_KP_Next: return 0x55;
877     case XK_KP_4: case XK_KP_Left: return 0x56;
878     case XK_KP_5: case XK_KP_Begin: return 0x57;
879     case XK_KP_6: case XK_KP_Right: return 0x58;
880     case XK_KP_7: case XK_KP_Home: return 0x59;
881     case XK_KP_8: case XK_KP_Up: return 0x5b;
882     case XK_KP_9: case XK_KP_Prior: return 0x5c;
883     case XK_KP_Decimal: case XK_KP_Delete: return 0x41;
884     #else
885     case XK_KP_0: return 0x52;
886     case XK_KP_1: return 0x53;
887     case XK_KP_2: return 0x54;
888     case XK_KP_3: return 0x55;
889     case XK_KP_4: return 0x56;
890     case XK_KP_5: return 0x57;
891     case XK_KP_6: return 0x58;
892     case XK_KP_7: return 0x59;
893     case XK_KP_8: return 0x5b;
894     case XK_KP_9: return 0x5c;
895     case XK_KP_Decimal: return 0x41;
896     #endif
897     case XK_KP_Add: return 0x45;
898     case XK_KP_Subtract: return 0x4e;
899     case XK_KP_Multiply: return 0x43;
900     case XK_KP_Divide: return 0x4b;
901     case XK_KP_Enter: return 0x4c;
902     case XK_KP_Equal: return 0x51;
903     }
904     return -1;
905     }
906    
907     static int event2keycode(XKeyEvent *ev)
908     {
909     KeySym ks;
910     int as;
911     int i = 0;
912    
913     do {
914     ks = XLookupKeysym(ev, i++);
915     as = kc_decode(ks);
916     if (as != -1)
917     return as;
918     } while (ks != NoSymbol);
919    
920     return -1;
921     }
922    
923     static void handle_events(void)
924     {
925     // Handle events
926     for (;;) {
927     XEvent event;
928    
929     if (!XCheckMaskEvent(x_display, eventmask, &event))
930     break;
931    
932     switch (event.type) {
933     // Mouse button
934     case ButtonPress: {
935     unsigned int button = ((XButtonEvent *)&event)->button;
936     if (button < 4)
937     ADBMouseDown(button - 1);
938     break;
939     }
940     case ButtonRelease: {
941     unsigned int button = ((XButtonEvent *)&event)->button;
942     if (button < 4)
943     ADBMouseUp(button - 1);
944     break;
945     }
946    
947     // Mouse moved
948     case EnterNotify:
949     ADBMouseMoved(((XMotionEvent *)&event)->x, ((XMotionEvent *)&event)->y);
950     break;
951     case MotionNotify:
952     ADBMouseMoved(((XMotionEvent *)&event)->x, ((XMotionEvent *)&event)->y);
953     break;
954    
955     // Keyboard
956     case KeyPress: {
957     int code;
958     if ((code = event2keycode((XKeyEvent *)&event)) != -1) {
959     if (!emul_suspended) {
960     ADBKeyDown(code);
961     if (code == 0x36)
962     ctrl_down = true;
963     } else {
964     if (code == 0x31)
965     resume_emul(); // Space wakes us up
966     }
967     }
968     break;
969     }
970     case KeyRelease: {
971     int code;
972     if ((code = event2keycode((XKeyEvent *)&event)) != -1) {
973     ADBKeyUp(code);
974     if (code == 0x36)
975     ctrl_down = false;
976     }
977     break;
978     }
979    
980     // Hidden parts exposed, force complete refresh
981     case Expose:
982     memset(the_buffer_copy, 0, VModes[cur_mode].viRowBytes * VModes[cur_mode].viYsize);
983     break;
984     }
985     }
986     }
987    
988    
989     /*
990     * Execute video VBL routine
991     */
992    
993     void VideoVBL(void)
994     {
995     if (emerg_quit)
996     QuitEmulator();
997    
998     // Execute video VBL
999     if (private_data != NULL && private_data->interruptsEnabled)
1000     VSLDoInterruptService(private_data->vslServiceID);
1001     }
1002    
1003    
1004     /*
1005     * Install graphics acceleration
1006     */
1007    
1008     #if 0
1009     // Rectangle blitting
1010     static void accl_bitblt(accl_params *p)
1011     {
1012     D(bug("accl_bitblt\n"));
1013    
1014     // Get blitting parameters
1015     int16 src_X = p->src_rect[1] - p->src_bounds[1];
1016     int16 src_Y = p->src_rect[0] - p->src_bounds[0];
1017     int16 dest_X = p->dest_rect[1] - p->dest_bounds[1];
1018     int16 dest_Y = p->dest_rect[0] - p->dest_bounds[0];
1019     int16 width = p->dest_rect[3] - p->dest_rect[1] - 1;
1020     int16 height = p->dest_rect[2] - p->dest_rect[0] - 1;
1021     D(bug(" src X %d, src Y %d, dest X %d, dest Y %d\n", src_X, src_Y, dest_X, dest_Y));
1022     D(bug(" width %d, height %d\n", width, height));
1023    
1024     // And perform the blit
1025     bitblt_hook(src_X, src_Y, dest_X, dest_Y, width, height);
1026     }
1027    
1028     static bool accl_bitblt_hook(accl_params *p)
1029     {
1030     D(bug("accl_draw_hook %p\n", p));
1031    
1032     // Check if we can accelerate this bitblt
1033     if (p->src_base_addr == screen_base && p->dest_base_addr == screen_base &&
1034     display_type == DIS_SCREEN && bitblt_hook != NULL &&
1035     ((uint32 *)p)[0x18 >> 2] + ((uint32 *)p)[0x128 >> 2] == 0 &&
1036     ((uint32 *)p)[0x130 >> 2] == 0 &&
1037     p->transfer_mode == 0 &&
1038     p->src_row_bytes > 0 && ((uint32 *)p)[0x15c >> 2] > 0) {
1039    
1040     // Yes, set function pointer
1041     p->draw_proc = accl_bitblt;
1042     return true;
1043     }
1044     return false;
1045     }
1046    
1047     // Rectangle filling/inversion
1048     static void accl_fillrect8(accl_params *p)
1049     {
1050     D(bug("accl_fillrect8\n"));
1051    
1052     // Get filling parameters
1053     int16 dest_X = p->dest_rect[1] - p->dest_bounds[1];
1054     int16 dest_Y = p->dest_rect[0] - p->dest_bounds[0];
1055     int16 dest_X_max = p->dest_rect[3] - p->dest_bounds[1] - 1;
1056     int16 dest_Y_max = p->dest_rect[2] - p->dest_bounds[0] - 1;
1057     uint8 color = p->pen_mode == 8 ? p->fore_pen : p->back_pen;
1058     D(bug(" dest X %d, dest Y %d\n", dest_X, dest_Y));
1059     D(bug(" dest X max %d, dest Y max %d\n", dest_X_max, dest_Y_max));
1060    
1061     // And perform the fill
1062     fillrect8_hook(dest_X, dest_Y, dest_X_max, dest_Y_max, color);
1063     }
1064    
1065     static void accl_fillrect32(accl_params *p)
1066     {
1067     D(bug("accl_fillrect32\n"));
1068    
1069     // Get filling parameters
1070     int16 dest_X = p->dest_rect[1] - p->dest_bounds[1];
1071     int16 dest_Y = p->dest_rect[0] - p->dest_bounds[0];
1072     int16 dest_X_max = p->dest_rect[3] - p->dest_bounds[1] - 1;
1073     int16 dest_Y_max = p->dest_rect[2] - p->dest_bounds[0] - 1;
1074     uint32 color = p->pen_mode == 8 ? p->fore_pen : p->back_pen;
1075     D(bug(" dest X %d, dest Y %d\n", dest_X, dest_Y));
1076     D(bug(" dest X max %d, dest Y max %d\n", dest_X_max, dest_Y_max));
1077    
1078     // And perform the fill
1079     fillrect32_hook(dest_X, dest_Y, dest_X_max, dest_Y_max, color);
1080     }
1081    
1082     static void accl_invrect(accl_params *p)
1083     {
1084     D(bug("accl_invrect\n"));
1085    
1086     // Get inversion parameters
1087     int16 dest_X = p->dest_rect[1] - p->dest_bounds[1];
1088     int16 dest_Y = p->dest_rect[0] - p->dest_bounds[0];
1089     int16 dest_X_max = p->dest_rect[3] - p->dest_bounds[1] - 1;
1090     int16 dest_Y_max = p->dest_rect[2] - p->dest_bounds[0] - 1;
1091     D(bug(" dest X %d, dest Y %d\n", dest_X, dest_Y));
1092     D(bug(" dest X max %d, dest Y max %d\n", dest_X_max, dest_Y_max));
1093    
1094     //!!?? pen_mode == 14
1095    
1096     // And perform the inversion
1097     invrect_hook(dest_X, dest_Y, dest_X_max, dest_Y_max);
1098     }
1099    
1100     static bool accl_fillrect_hook(accl_params *p)
1101     {
1102     D(bug("accl_fillrect_hook %p\n", p));
1103    
1104     // Check if we can accelerate this fillrect
1105     if (p->dest_base_addr == screen_base && ((uint32 *)p)[0x284 >> 2] != 0 && display_type == DIS_SCREEN) {
1106     if (p->transfer_mode == 8) {
1107     // Fill
1108     if (p->dest_pixel_size == 8 && fillrect8_hook != NULL) {
1109     p->draw_proc = accl_fillrect8;
1110     return true;
1111     } else if (p->dest_pixel_size == 32 && fillrect32_hook != NULL) {
1112     p->draw_proc = accl_fillrect32;
1113     return true;
1114     }
1115     } else if (p->transfer_mode == 10 && invrect_hook != NULL) {
1116     // Invert
1117     p->draw_proc = accl_invrect;
1118     return true;
1119     }
1120     }
1121     return false;
1122     }
1123    
1124     // Wait for graphics operation to finish
1125     static bool accl_sync_hook(void *arg)
1126     {
1127     D(bug("accl_sync_hook %p\n", arg));
1128     if (sync_hook != NULL)
1129     sync_hook();
1130     return true;
1131     }
1132    
1133     static struct accl_hook_info bitblt_hook_info = {accl_bitblt_hook, accl_sync_hook, ACCL_BITBLT};
1134     static struct accl_hook_info fillrect_hook_info = {accl_fillrect_hook, accl_sync_hook, ACCL_FILLRECT};
1135     #endif
1136    
1137     void VideoInstallAccel(void)
1138     {
1139     // Install acceleration hooks
1140     if (PrefsFindBool("gfxaccel")) {
1141     D(bug("Video: Installing acceleration hooks\n"));
1142     //!! NQDMisc(6, &bitblt_hook_info);
1143     // NQDMisc(6, &fillrect_hook_info);
1144     }
1145     }
1146    
1147    
1148     /*
1149     * Change video mode
1150     */
1151    
1152     int16 video_mode_change(VidLocals *csSave, uint32 ParamPtr)
1153     {
1154     /* return if no mode change */
1155     if ((csSave->saveData == ReadMacInt32(ParamPtr + csData)) &&
1156     (csSave->saveMode == ReadMacInt16(ParamPtr + csMode))) return noErr;
1157    
1158     /* first find video mode in table */
1159     for (int i=0; VModes[i].viType != DIS_INVALID; i++) {
1160     if ((ReadMacInt16(ParamPtr + csMode) == VModes[i].viAppleMode) &&
1161     (ReadMacInt32(ParamPtr + csData) == VModes[i].viAppleID)) {
1162     csSave->saveMode = ReadMacInt16(ParamPtr + csMode);
1163     csSave->saveData = ReadMacInt32(ParamPtr + csData);
1164     csSave->savePage = ReadMacInt16(ParamPtr + csPage);
1165    
1166     // Disable interrupts and pause redraw thread
1167     DisableInterrupt();
1168     thread_stop_ack = false;
1169     thread_stop_req = true;
1170     while (!thread_stop_ack) ;
1171    
1172     /* close old display */
1173     close_display();
1174    
1175     /* open new display */
1176     cur_mode = i;
1177     bool ok = open_display();
1178    
1179     /* opening the screen failed? Then bail out */
1180     if (!ok) {
1181     ErrorAlert(GetString(STR_FULL_SCREEN_ERR));
1182     QuitEmulator();
1183     }
1184    
1185     WriteMacInt32(ParamPtr + csBaseAddr, screen_base);
1186     csSave->saveBaseAddr=screen_base;
1187     csSave->saveData=VModes[cur_mode].viAppleID;/* First mode ... */
1188     csSave->saveMode=VModes[cur_mode].viAppleMode;
1189    
1190     // Enable interrupts and resume redraw thread
1191     thread_stop_req = false;
1192     EnableInterrupt();
1193     return noErr;
1194     }
1195     }
1196     return paramErr;
1197     }
1198    
1199    
1200     /*
1201     * Set color palette
1202     */
1203    
1204     void video_set_palette(void)
1205     {
1206     palette_changed = true;
1207     }
1208    
1209    
1210     /*
1211     * Set cursor image for window
1212     */
1213    
1214     void video_set_cursor(void)
1215     {
1216     cursor_changed = true;
1217     }
1218    
1219    
1220     /*
1221     * Thread for window refresh, event handling and other periodic actions
1222     */
1223    
1224     static void update_display(void)
1225     {
1226     // Incremental update code
1227     int wide = 0, high = 0, x1, x2, y1, y2, i, j;
1228     int bytes_per_row = VModes[cur_mode].viRowBytes;
1229     int bytes_per_pixel = VModes[cur_mode].viRowBytes / VModes[cur_mode].viXsize;
1230     uint8 *p, *p2;
1231    
1232     // Check for first line from top and first line from bottom that have changed
1233     y1 = 0;
1234     for (j=0; j<VModes[cur_mode].viYsize; j++) {
1235     if (memcmp(&the_buffer[j * bytes_per_row], &the_buffer_copy[j * bytes_per_row], bytes_per_row)) {
1236     y1 = j;
1237     break;
1238     }
1239     }
1240     y2 = y1 - 1;
1241     for (j=VModes[cur_mode].viYsize-1; j>=y1; j--) {
1242     if (memcmp(&the_buffer[j * bytes_per_row], &the_buffer_copy[j * bytes_per_row], bytes_per_row)) {
1243     y2 = j;
1244     break;
1245     }
1246     }
1247     high = y2 - y1 + 1;
1248    
1249     // Check for first column from left and first column from right that have changed
1250     if (high) {
1251     if (depth == 1) {
1252     x1 = VModes[cur_mode].viXsize;
1253     for (j=y1; j<=y2; j++) {
1254     p = &the_buffer[j * bytes_per_row];
1255     p2 = &the_buffer_copy[j * bytes_per_row];
1256     for (i=0; i<(x1>>3); i++) {
1257     if (*p != *p2) {
1258     x1 = i << 3;
1259     break;
1260     }
1261     p++;
1262     p2++;
1263     }
1264     }
1265     x2 = x1;
1266     for (j=y1; j<=y2; j++) {
1267     p = &the_buffer[j * bytes_per_row];
1268     p2 = &the_buffer_copy[j * bytes_per_row];
1269     p += bytes_per_row;
1270     p2 += bytes_per_row;
1271     for (i=(VModes[cur_mode].viXsize>>3); i>(x2>>3); i--) {
1272     p--;
1273     p2--;
1274     if (*p != *p2) {
1275     x2 = i << 3;
1276     break;
1277     }
1278     }
1279     }
1280     wide = x2 - x1;
1281    
1282     // Update copy of the_buffer
1283     if (high && wide) {
1284     for (j=y1; j<=y2; j++) {
1285     i = j * bytes_per_row + (x1 >> 3);
1286     memcpy(&the_buffer_copy[i], &the_buffer[i], wide >> 3);
1287     }
1288     }
1289    
1290     } else {
1291     x1 = VModes[cur_mode].viXsize;
1292     for (j=y1; j<=y2; j++) {
1293     p = &the_buffer[j * bytes_per_row];
1294     p2 = &the_buffer_copy[j * bytes_per_row];
1295     for (i=0; i<x1; i++) {
1296     if (memcmp(p, p2, bytes_per_pixel)) {
1297     x1 = i;
1298     break;
1299     }
1300     p += bytes_per_pixel;
1301     p2 += bytes_per_pixel;
1302     }
1303     }
1304     x2 = x1;
1305     for (j=y1; j<=y2; j++) {
1306     p = &the_buffer[j * bytes_per_row];
1307     p2 = &the_buffer_copy[j * bytes_per_row];
1308     p += bytes_per_row;
1309     p2 += bytes_per_row;
1310     for (i=VModes[cur_mode].viXsize; i>x2; i--) {
1311     p -= bytes_per_pixel;
1312     p2 -= bytes_per_pixel;
1313     if (memcmp(p, p2, bytes_per_pixel)) {
1314     x2 = i;
1315     break;
1316     }
1317     }
1318     }
1319     wide = x2 - x1;
1320    
1321     // Update copy of the_buffer
1322     if (high && wide) {
1323     for (j=y1; j<=y2; j++) {
1324     i = j * bytes_per_row + x1 * bytes_per_pixel;
1325     memcpy(&the_buffer_copy[i], &the_buffer[i], bytes_per_pixel * wide);
1326     }
1327     }
1328     }
1329     }
1330    
1331     // Refresh display
1332     if (high && wide) {
1333     if (have_shm)
1334     XShmPutImage(x_display, the_win, the_gc, img, x1, y1, x1, y1, wide, high, 0);
1335     else
1336     XPutImage(x_display, the_win, the_gc, img, x1, y1, x1, y1, wide, high);
1337     }
1338     }
1339    
1340     static void *redraw_func(void *arg)
1341     {
1342     int tick_counter = 0;
1343     struct timespec req = {0, 16666667};
1344    
1345     for (;;) {
1346    
1347     // Wait
1348     nanosleep(&req, NULL);
1349    
1350     // Pause if requested (during video mode switches)
1351     while (thread_stop_req)
1352     thread_stop_ack = true;
1353    
1354     // Handle X11 events
1355     handle_events();
1356    
1357     // Quit DGA mode if requested
1358     if (quit_full_screen) {
1359     quit_full_screen = false;
1360     if (display_type == DIS_SCREEN) {
1361     #ifdef ENABLE_XF86_DGA
1362     XF86DGADirectVideo(x_display, screen, 0);
1363     XUngrabPointer(x_display, CurrentTime);
1364     XUngrabKeyboard(x_display, CurrentTime);
1365     #endif
1366     XSync(x_display, false);
1367     quit_full_screen_ack = true;
1368     return NULL;
1369     }
1370     }
1371    
1372     // Refresh display and set cursor image in window mode
1373     if (display_type == DIS_WINDOW) {
1374     tick_counter++;
1375     if (tick_counter >= frame_skip) {
1376     tick_counter = 0;
1377    
1378     // Update display
1379     update_display();
1380    
1381     // Set new cursor image if it was changed
1382     if (cursor_changed) {
1383     cursor_changed = false;
1384     memcpy(cursor_image->data, MacCursor + 4, 32);
1385     memcpy(cursor_mask_image->data, MacCursor + 36, 32);
1386     XFreeCursor(x_display, mac_cursor);
1387     XPutImage(x_display, cursor_map, cursor_gc, cursor_image, 0, 0, 0, 0, 16, 16);
1388     XPutImage(x_display, cursor_mask_map, cursor_mask_gc, cursor_mask_image, 0, 0, 0, 0, 16, 16);
1389     mac_cursor = XCreatePixmapCursor(x_display, cursor_map, cursor_mask_map, &black, &white, MacCursor[2], MacCursor[3]);
1390     XDefineCursor(x_display, the_win, mac_cursor);
1391     }
1392     }
1393     }
1394    
1395     // Set new palette if it was changed
1396     if (palette_changed && !emul_suspended) {
1397     palette_changed = false;
1398     XColor c[256];
1399     for (int i=0; i<256; i++) {
1400     c[i].pixel = i;
1401     c[i].red = mac_pal[i].red * 0x0101;
1402     c[i].green = mac_pal[i].green * 0x0101;
1403     c[i].blue = mac_pal[i].blue * 0x0101;
1404     c[i].flags = DoRed | DoGreen | DoBlue;
1405     }
1406     if (depth == 8) {
1407     XStoreColors(x_display, cmap[0], c, 256);
1408     XStoreColors(x_display, cmap[1], c, 256);
1409     #ifdef ENABLE_XF86_DGA
1410     if (display_type == DIS_SCREEN) {
1411     current_dga_cmap ^= 1;
1412     XF86DGAInstallColormap(x_display, screen, cmap[current_dga_cmap]);
1413     }
1414     #endif
1415     }
1416     }
1417     }
1418     return NULL;
1419     }