ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/SheepShaver/src/Unix/video_x.cpp
Revision: 1.18
Committed: 2004-04-22T20:57:30Z (20 years, 4 months ago) by gbeauche
Branch: MAIN
Changes since 1.17: +243 -53 lines
Log Message:
Basic fillrect/invrect NQD. Code may need to be factored out somehow.
Verify that bitblt NQD transfer modes are really CopyBits() ones [MB5].

File Contents

# User Rev Content
1 cebix 1.1 /*
2     * video_x.cpp - Video/graphics emulation, X11 specific stuff
3     *
4 cebix 1.11 * SheepShaver (C) 1997-2004 Marc Hellwig and Christian Bauer
5 cebix 1.1 *
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 gbeauche 1.6 #include "sysdeps.h"
22    
23 cebix 1.1 #include <X11/Xlib.h>
24     #include <X11/Xutil.h>
25     #include <X11/keysym.h>
26     #include <X11/extensions/XShm.h>
27     #include <sys/ipc.h>
28     #include <sys/shm.h>
29 gbeauche 1.6 #include <errno.h>
30 gbeauche 1.7 #include <pthread.h>
31 gbeauche 1.6
32 gbeauche 1.13 #include <algorithm>
33    
34 gbeauche 1.6 #ifdef ENABLE_XF86_DGA
35 gbeauche 1.15 # include <X11/extensions/xf86dga.h>
36 gbeauche 1.6 #endif
37    
38     #ifdef ENABLE_XF86_VIDMODE
39     # include <X11/extensions/xf86vmode.h>
40     #endif
41 cebix 1.1
42     #include "main.h"
43     #include "adb.h"
44     #include "prefs.h"
45     #include "user_strings.h"
46     #include "about_window.h"
47     #include "video.h"
48     #include "video_defs.h"
49    
50     #define DEBUG 0
51     #include "debug.h"
52    
53 gbeauche 1.13 #ifndef NO_STD_NAMESPACE
54     using std::sort;
55     #endif
56    
57 cebix 1.1
58 gbeauche 1.6 // Constants
59     const char KEYCODE_FILE_NAME[] = DATADIR "/keycodes";
60 cebix 1.1
61     // Global variables
62     static int32 frame_skip;
63 gbeauche 1.8 static int16 mouse_wheel_mode;
64     static int16 mouse_wheel_lines;
65 cebix 1.1 static bool redraw_thread_active = false; // Flag: Redraw thread installed
66 gbeauche 1.15 static pthread_attr_t redraw_thread_attr; // Redraw thread attributes
67 cebix 1.1 static pthread_t redraw_thread; // Redraw thread
68    
69 gbeauche 1.4 static bool local_X11; // Flag: X server running on local machine?
70 cebix 1.1 static volatile bool thread_stop_req = false;
71     static volatile bool thread_stop_ack = false; // Acknowledge for thread_stop_req
72    
73     static bool has_dga = false; // Flag: Video DGA capable
74     static bool has_vidmode = false; // Flag: VidMode extension available
75    
76 gbeauche 1.3 #ifdef ENABLE_VOSF
77     static bool use_vosf = true; // Flag: VOSF enabled
78     #else
79     static const bool use_vosf = false; // VOSF not possible
80     #endif
81    
82 cebix 1.1 static bool palette_changed = false; // Flag: Palette changed, redraw thread must update palette
83     static bool ctrl_down = false; // Flag: Ctrl key pressed
84     static bool quit_full_screen = false; // Flag: DGA close requested from redraw thread
85     static volatile bool quit_full_screen_ack = false; // Acknowledge for quit_full_screen
86     static bool emerg_quit = false; // Flag: Ctrl-Esc pressed, emergency quit requested from MacOS thread
87    
88     static bool emul_suspended = false; // Flag: emulator suspended
89     static Window suspend_win; // "Suspend" window
90     static void *fb_save = NULL; // Saved frame buffer for suspend
91 gbeauche 1.6 static bool use_keycodes = false; // Flag: Use keycodes rather than keysyms
92     static int keycode_table[256]; // X keycode -> Mac keycode translation table
93 cebix 1.1
94     // X11 variables
95     static int screen; // Screen number
96     static int xdepth; // Depth of X screen
97     static int depth; // Depth of Mac frame buffer
98     static Window rootwin, the_win; // Root window and our window
99 gbeauche 1.13 static int num_depths = 0; // Number of available X depths
100     static int *avail_depths = NULL; // List of available X depths
101 cebix 1.1 static XVisualInfo visualInfo;
102     static Visual *vis;
103 gbeauche 1.13 static int color_class;
104     static int rshift, rloss, gshift, gloss, bshift, bloss; // Pixel format of DirectColor/TrueColor modes
105 cebix 1.1 static Colormap cmap[2]; // Two colormaps (DGA) for 8-bit mode
106 gbeauche 1.13 static XColor x_palette[256]; // Color palette to be used as CLUT and gamma table
107    
108 cebix 1.1 static XColor black, white;
109     static unsigned long black_pixel, white_pixel;
110     static int eventmask;
111 gbeauche 1.13 static const int win_eventmask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | EnterWindowMask | ExposureMask | StructureNotifyMask;
112     static const int dga_eventmask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | StructureNotifyMask;
113 cebix 1.1
114     // Variables for window mode
115     static GC the_gc;
116     static XImage *img = NULL;
117     static XShmSegmentInfo shminfo;
118     static XImage *cursor_image, *cursor_mask_image;
119     static Pixmap cursor_map, cursor_mask_map;
120     static Cursor mac_cursor;
121     static GC cursor_gc, cursor_mask_gc;
122     static bool cursor_changed = false; // Flag: Cursor changed, window_func must update cursor
123     static bool have_shm = false; // Flag: SHM present and usable
124 gbeauche 1.3 static uint8 *the_buffer = NULL; // Pointer to Mac frame buffer
125 cebix 1.1 static uint8 *the_buffer_copy = NULL; // Copy of Mac frame buffer
126 gbeauche 1.3 static uint32 the_buffer_size; // Size of allocated the_buffer
127 cebix 1.1
128     // Variables for DGA mode
129     static int current_dga_cmap;
130    
131     #ifdef ENABLE_XF86_VIDMODE
132     // Variables for XF86 VidMode support
133     static XF86VidModeModeInfo **x_video_modes; // Array of all available modes
134     static int num_x_video_modes;
135     #endif
136    
137 gbeauche 1.13 // Mutex to protect palette
138     #ifdef HAVE_SPINLOCKS
139     static spinlock_t x_palette_lock = SPIN_LOCK_UNLOCKED;
140     #define LOCK_PALETTE spin_lock(&x_palette_lock)
141     #define UNLOCK_PALETTE spin_unlock(&x_palette_lock)
142     #elif defined(HAVE_PTHREADS)
143     static pthread_mutex_t x_palette_lock = PTHREAD_MUTEX_INITIALIZER;
144     #define LOCK_PALETTE pthread_mutex_lock(&x_palette_lock)
145     #define UNLOCK_PALETTE pthread_mutex_unlock(&x_palette_lock)
146     #else
147     #define LOCK_PALETTE
148     #define UNLOCK_PALETTE
149     #endif
150    
151 cebix 1.1
152     // Prototypes
153     static void *redraw_func(void *arg);
154    
155    
156 gbeauche 1.4 // From main_unix.cpp
157     extern char *x_display_name;
158 cebix 1.1 extern Display *x_display;
159    
160     // From sys_unix.cpp
161     extern void SysMountFirstFloppy(void);
162    
163 gbeauche 1.9 // From clip_unix.cpp
164     extern void ClipboardSelectionClear(XSelectionClearEvent *);
165     extern void ClipboardSelectionRequest(XSelectionRequestEvent *);
166    
167 cebix 1.1
168 gbeauche 1.3 // Video acceleration through SIGSEGV
169     #ifdef ENABLE_VOSF
170     # include "video_vosf.h"
171     #endif
172    
173    
174 cebix 1.1 /*
175 gbeauche 1.13 * Utility functions
176     */
177    
178     // Get current video mode
179     static inline int get_current_mode(void)
180     {
181     return VModes[cur_mode].viAppleMode;
182     }
183    
184     // Find palette size for given color depth
185     static int palette_size(int mode)
186     {
187     switch (mode) {
188     case APPLE_1_BIT: return 2;
189     case APPLE_2_BIT: return 4;
190     case APPLE_4_BIT: return 16;
191     case APPLE_8_BIT: return 256;
192     case APPLE_16_BIT: return 32;
193     case APPLE_32_BIT: return 256;
194     default: return 0;
195     }
196     }
197    
198 gbeauche 1.16 // Return bits per pixel for requested depth
199     static inline int bytes_per_pixel(int depth)
200     {
201     int bpp;
202     switch (depth) {
203     case 8:
204     bpp = 1;
205     break;
206     case 15: case 16:
207     bpp = 2;
208     break;
209     case 24: case 32:
210     bpp = 4;
211     break;
212     default:
213     abort();
214     }
215     return bpp;
216     }
217    
218 gbeauche 1.13 // Map video_mode depth ID to numerical depth value
219     static inline int depth_of_video_mode(int mode)
220     {
221 gbeauche 1.16 int depth;
222 gbeauche 1.13 switch (mode) {
223     case APPLE_1_BIT:
224     depth = 1;
225     break;
226     case APPLE_2_BIT:
227     depth = 2;
228     break;
229     case APPLE_4_BIT:
230     depth = 4;
231     break;
232     case APPLE_8_BIT:
233     depth = 8;
234     break;
235     case APPLE_16_BIT:
236     depth = 16;
237     break;
238     case APPLE_32_BIT:
239     depth = 32;
240     break;
241     default:
242     abort();
243     }
244     return depth;
245     }
246    
247     // Map RGB color to pixel value (this only works in TrueColor/DirectColor visuals)
248     static inline uint32 map_rgb(uint8 red, uint8 green, uint8 blue)
249     {
250     return ((red >> rloss) << rshift) | ((green >> gloss) << gshift) | ((blue >> bloss) << bshift);
251     }
252    
253    
254     // Do we have a visual for handling the specified Mac depth? If so, set the
255     // global variables "xdepth", "visualInfo", "vis" and "color_class".
256     static bool find_visual_for_depth(int depth)
257     {
258     D(bug("have_visual_for_depth(%d)\n", depth_of_video_mode(depth)));
259    
260     // 1-bit works always and uses default visual
261     if (depth == APPLE_1_BIT) {
262     vis = DefaultVisual(x_display, screen);
263     visualInfo.visualid = XVisualIDFromVisual(vis);
264     int num = 0;
265     XVisualInfo *vi = XGetVisualInfo(x_display, VisualIDMask, &visualInfo, &num);
266     visualInfo = vi[0];
267     XFree(vi);
268     xdepth = visualInfo.depth;
269     color_class = visualInfo.c_class;
270     D(bug(" found visual ID 0x%02x, depth %d\n", visualInfo.visualid, xdepth));
271     return true;
272     }
273    
274     // Calculate minimum and maximum supported X depth
275     int min_depth = 1, max_depth = 32;
276     switch (depth) {
277     #ifdef ENABLE_VOSF
278     case APPLE_2_BIT:
279     case APPLE_4_BIT: // VOSF blitters can convert 2/4/8-bit -> 8/16/32-bit
280     case APPLE_8_BIT:
281     min_depth = 8;
282     max_depth = 32;
283     break;
284     #else
285     case APPLE_2_BIT:
286     case APPLE_4_BIT: // 2/4-bit requires VOSF blitters
287     return false;
288     case APPLE_8_BIT: // 8-bit without VOSF requires an 8-bit visual
289     min_depth = 8;
290     max_depth = 8;
291     break;
292     #endif
293     case APPLE_16_BIT: // 16-bit requires a 15/16-bit visual
294     min_depth = 15;
295     max_depth = 16;
296     break;
297     case APPLE_32_BIT: // 32-bit requires a 24/32-bit visual
298     min_depth = 24;
299     max_depth = 32;
300     break;
301     }
302     D(bug(" minimum required X depth is %d, maximum supported X depth is %d\n", min_depth, max_depth));
303    
304     // Try to find a visual for one of the color depths
305     bool visual_found = false;
306     for (int i=0; i<num_depths && !visual_found; i++) {
307    
308     xdepth = avail_depths[i];
309     D(bug(" trying to find visual for depth %d\n", xdepth));
310     if (xdepth < min_depth || xdepth > max_depth)
311     continue;
312    
313     // Determine best color class for this depth
314     switch (xdepth) {
315     case 1: // Try StaticGray or StaticColor
316     if (XMatchVisualInfo(x_display, screen, xdepth, StaticGray, &visualInfo)
317     || XMatchVisualInfo(x_display, screen, xdepth, StaticColor, &visualInfo))
318     visual_found = true;
319     break;
320     case 8: // Need PseudoColor
321     if (XMatchVisualInfo(x_display, screen, xdepth, PseudoColor, &visualInfo))
322     visual_found = true;
323     break;
324     case 15:
325     case 16:
326     case 24:
327     case 32: // Try DirectColor first, as this will allow gamma correction
328     if (XMatchVisualInfo(x_display, screen, xdepth, DirectColor, &visualInfo)
329     || XMatchVisualInfo(x_display, screen, xdepth, TrueColor, &visualInfo))
330     visual_found = true;
331     break;
332     default:
333     D(bug(" not a supported depth\n"));
334     break;
335     }
336     }
337     if (!visual_found)
338     return false;
339    
340     // Visual was found
341     vis = visualInfo.visual;
342     color_class = visualInfo.c_class;
343     D(bug(" found visual ID 0x%02x, depth %d, class ", visualInfo.visualid, xdepth));
344     #if DEBUG
345     switch (color_class) {
346     case StaticGray: D(bug("StaticGray\n")); break;
347     case GrayScale: D(bug("GrayScale\n")); break;
348     case StaticColor: D(bug("StaticColor\n")); break;
349     case PseudoColor: D(bug("PseudoColor\n")); break;
350     case TrueColor: D(bug("TrueColor\n")); break;
351     case DirectColor: D(bug("DirectColor\n")); break;
352     }
353     #endif
354     return true;
355     }
356    
357    
358     /*
359 cebix 1.1 * Open display (window or fullscreen)
360     */
361    
362 gbeauche 1.14 // Set WM_DELETE_WINDOW protocol on window (preventing it from being destroyed by the WM when clicking on the "close" widget)
363     static Atom WM_DELETE_WINDOW = (Atom)0;
364     static void set_window_delete_protocol(Window w)
365     {
366     WM_DELETE_WINDOW = XInternAtom(x_display, "WM_DELETE_WINDOW", false);
367     XSetWMProtocols(x_display, w, &WM_DELETE_WINDOW, 1);
368     }
369    
370 gbeauche 1.13 // Wait until window is mapped/unmapped
371 gbeauche 1.15 static void wait_mapped(Window w)
372 gbeauche 1.13 {
373     XEvent e;
374     do {
375     XMaskEvent(x_display, StructureNotifyMask, &e);
376     } while ((e.type != MapNotify) || (e.xmap.event != w));
377     }
378    
379 gbeauche 1.15 static void wait_unmapped(Window w)
380 gbeauche 1.13 {
381     XEvent e;
382     do {
383     XMaskEvent(x_display, StructureNotifyMask, &e);
384     } while ((e.type != UnmapNotify) || (e.xmap.event != w));
385     }
386    
387 cebix 1.1 // Trap SHM errors
388     static bool shm_error = false;
389     static int (*old_error_handler)(Display *, XErrorEvent *);
390    
391     static int error_handler(Display *d, XErrorEvent *e)
392     {
393     if (e->error_code == BadAccess) {
394     shm_error = true;
395     return 0;
396     } else
397     return old_error_handler(d, e);
398     }
399    
400     // Open window
401     static bool open_window(int width, int height)
402     {
403 gbeauche 1.3 int aligned_width = (width + 15) & ~15;
404     int aligned_height = (height + 15) & ~15;
405    
406 cebix 1.1 // Set absolute mouse mode
407     ADBSetRelMouseMode(false);
408    
409     // Create window
410     XSetWindowAttributes wattr;
411     wattr.event_mask = eventmask = win_eventmask;
412 gbeauche 1.13 wattr.background_pixel = (vis == DefaultVisual(x_display, screen) ? black_pixel : 0);
413     wattr.border_pixel = 0;
414 cebix 1.1 wattr.backing_store = NotUseful;
415 gbeauche 1.13 wattr.colormap = (depth == 1 ? DefaultColormap(x_display, screen) : cmap[0]);
416     the_win = XCreateWindow(x_display, rootwin, 0, 0, width, height, 0, xdepth,
417     InputOutput, vis, CWEventMask | CWBackPixel | CWBorderPixel | CWBackingStore | CWColormap, &wattr);
418 cebix 1.1
419 gbeauche 1.13 // Set window name
420 cebix 1.1 XStoreName(x_display, the_win, GetString(STR_WINDOW_TITLE));
421    
422 gbeauche 1.14 // Set delete protocol property
423     set_window_delete_protocol(the_win);
424    
425 cebix 1.1 // Make window unresizable
426     XSizeHints *hints;
427     if ((hints = XAllocSizeHints()) != NULL) {
428     hints->min_width = width;
429     hints->max_width = width;
430     hints->min_height = height;
431     hints->max_height = height;
432     hints->flags = PMinSize | PMaxSize;
433     XSetWMNormalHints(x_display, the_win, hints);
434     XFree((char *)hints);
435     }
436    
437 gbeauche 1.13 // Show window
438     XMapWindow(x_display, the_win);
439     wait_mapped(the_win);
440    
441 gbeauche 1.5 // 1-bit mode is big-endian; if the X server is little-endian, we can't
442     // use SHM because that doesn't allow changing the image byte order
443     bool need_msb_image = (depth == 1 && XImageByteOrder(x_display) == LSBFirst);
444    
445 cebix 1.1 // Try to create and attach SHM image
446     have_shm = false;
447 gbeauche 1.5 if (local_X11 && !need_msb_image && XShmQueryExtension(x_display)) {
448 cebix 1.1
449     // Create SHM image ("height + 2" for safety)
450 gbeauche 1.5 img = XShmCreateImage(x_display, vis, depth == 1 ? 1 : xdepth, depth == 1 ? XYBitmap : ZPixmap, 0, &shminfo, width, height);
451 gbeauche 1.13 shminfo.shmid = shmget(IPC_PRIVATE, (aligned_height + 2) * img->bytes_per_line, IPC_CREAT | 0777);
452     D(bug(" shm image created\n"));
453 gbeauche 1.3 the_buffer_copy = (uint8 *)shmat(shminfo.shmid, 0, 0);
454     shminfo.shmaddr = img->data = (char *)the_buffer_copy;
455 cebix 1.1 shminfo.readOnly = False;
456    
457     // Try to attach SHM image, catching errors
458     shm_error = false;
459     old_error_handler = XSetErrorHandler(error_handler);
460     XShmAttach(x_display, &shminfo);
461     XSync(x_display, false);
462     XSetErrorHandler(old_error_handler);
463     if (shm_error) {
464     shmdt(shminfo.shmaddr);
465     XDestroyImage(img);
466     shminfo.shmid = -1;
467     } else {
468     have_shm = true;
469     shmctl(shminfo.shmid, IPC_RMID, 0);
470     }
471 gbeauche 1.13 D(bug(" shm image attached\n"));
472 cebix 1.1 }
473    
474     // Create normal X image if SHM doesn't work ("height + 2" for safety)
475     if (!have_shm) {
476 gbeauche 1.13 int bytes_per_row = depth == 1 ? aligned_width/8 : TrivialBytesPerRow(aligned_width, DepthModeForPixelDepth(xdepth));
477 gbeauche 1.3 the_buffer_copy = (uint8 *)malloc((aligned_height + 2) * bytes_per_row);
478     img = XCreateImage(x_display, vis, depth == 1 ? 1 : xdepth, depth == 1 ? XYBitmap : ZPixmap, 0, (char *)the_buffer_copy, aligned_width, aligned_height, 32, bytes_per_row);
479 gbeauche 1.13 D(bug(" X image created\n"));
480 cebix 1.1 }
481    
482     // 1-Bit mode is big-endian
483 gbeauche 1.13 if (need_msb_image) {
484 cebix 1.1 img->byte_order = MSBFirst;
485     img->bitmap_bit_order = MSBFirst;
486     }
487    
488 gbeauche 1.3 #ifdef ENABLE_VOSF
489     use_vosf = true;
490     // Allocate memory for frame buffer (SIZE is extended to page-boundary)
491     the_host_buffer = the_buffer_copy;
492     the_buffer_size = page_extend((aligned_height + 2) * img->bytes_per_line);
493     the_buffer = (uint8 *)vm_acquire(the_buffer_size);
494     the_buffer_copy = (uint8 *)malloc(the_buffer_size);
495     D(bug("the_buffer = %p, the_buffer_copy = %p, the_host_buffer = %p\n", the_buffer, the_buffer_copy, the_host_buffer));
496     #else
497     // Allocate memory for frame buffer
498     the_buffer = (uint8 *)malloc((aligned_height + 2) * img->bytes_per_line);
499     D(bug("the_buffer = %p, the_buffer_copy = %p\n", the_buffer, the_buffer_copy));
500     #endif
501     screen_base = (uint32)the_buffer;
502 cebix 1.1
503     // Create GC
504     the_gc = XCreateGC(x_display, the_win, 0, 0);
505 gbeauche 1.13 XSetState(x_display, the_gc, black_pixel, white_pixel, GXcopy, AllPlanes);
506 cebix 1.1
507     // Create cursor
508     cursor_image = XCreateImage(x_display, vis, 1, XYPixmap, 0, (char *)MacCursor + 4, 16, 16, 16, 2);
509     cursor_image->byte_order = MSBFirst;
510     cursor_image->bitmap_bit_order = MSBFirst;
511     cursor_mask_image = XCreateImage(x_display, vis, 1, XYPixmap, 0, (char *)MacCursor + 36, 16, 16, 16, 2);
512     cursor_mask_image->byte_order = MSBFirst;
513     cursor_mask_image->bitmap_bit_order = MSBFirst;
514     cursor_map = XCreatePixmap(x_display, the_win, 16, 16, 1);
515     cursor_mask_map = XCreatePixmap(x_display, the_win, 16, 16, 1);
516     cursor_gc = XCreateGC(x_display, cursor_map, 0, 0);
517     cursor_mask_gc = XCreateGC(x_display, cursor_mask_map, 0, 0);
518     mac_cursor = XCreatePixmapCursor(x_display, cursor_map, cursor_mask_map, &black, &white, 0, 0);
519     cursor_changed = false;
520    
521 gbeauche 1.3 // Init blitting routines
522     bool native_byte_order;
523     #ifdef WORDS_BIGENDIAN
524     native_byte_order = (XImageByteOrder(x_display) == MSBFirst);
525     #else
526     native_byte_order = (XImageByteOrder(x_display) == LSBFirst);
527     #endif
528     #ifdef ENABLE_VOSF
529     Screen_blitter_init(&visualInfo, native_byte_order, depth);
530     #endif
531    
532 cebix 1.1 // Set bytes per row
533     XSync(x_display, false);
534     return true;
535     }
536    
537     // Open DGA display (!! should use X11 VidMode extensions to set mode)
538     static bool open_dga(int width, int height)
539     {
540     #ifdef ENABLE_XF86_DGA
541     // Set relative mouse mode
542     ADBSetRelMouseMode(true);
543    
544 gbeauche 1.15 // Create window
545     XSetWindowAttributes wattr;
546     wattr.event_mask = eventmask = dga_eventmask;
547     wattr.override_redirect = True;
548     wattr.colormap = (depth == 1 ? DefaultColormap(x_display, screen) : cmap[0]);
549     the_win = XCreateWindow(x_display, rootwin, 0, 0, width, height, 0, xdepth,
550     InputOutput, vis, CWEventMask | CWOverrideRedirect |
551     (color_class == DirectColor ? CWColormap : 0), &wattr);
552    
553     // Show window
554     XMapRaised(x_display, the_win);
555     wait_mapped(the_win);
556    
557 cebix 1.1 #ifdef ENABLE_XF86_VIDMODE
558     // Switch to best mode
559     if (has_vidmode) {
560     int best = 0;
561     for (int i=1; i<num_x_video_modes; i++) {
562     if (x_video_modes[i]->hdisplay >= width && x_video_modes[i]->vdisplay >= height &&
563     x_video_modes[i]->hdisplay <= x_video_modes[best]->hdisplay && x_video_modes[i]->vdisplay <= x_video_modes[best]->vdisplay) {
564     best = i;
565     }
566     }
567     XF86VidModeSwitchToMode(x_display, screen, x_video_modes[best]);
568     XF86VidModeSetViewPort(x_display, screen, 0, 0);
569     }
570     #endif
571    
572     // Establish direct screen connection
573 gbeauche 1.15 XMoveResizeWindow(x_display, the_win, 0, 0, width, height);
574     XWarpPointer(x_display, None, rootwin, 0, 0, 0, 0, 0, 0);
575 cebix 1.1 XGrabKeyboard(x_display, rootwin, True, GrabModeAsync, GrabModeAsync, CurrentTime);
576     XGrabPointer(x_display, rootwin, True, PointerMotionMask | ButtonPressMask | ButtonReleaseMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
577 gbeauche 1.15
578     int v_width, v_bank, v_size;
579     XF86DGAGetVideo(x_display, screen, (char **)&the_buffer, &v_width, &v_bank, &v_size);
580 cebix 1.1 XF86DGADirectVideo(x_display, screen, XF86DGADirectGraphics | XF86DGADirectKeyb | XF86DGADirectMouse);
581     XF86DGASetViewPort(x_display, screen, 0, 0);
582     XF86DGASetVidPage(x_display, screen, 0);
583    
584     // Set colormap
585 gbeauche 1.15 if (!IsDirectMode(get_current_mode())) {
586     XSetWindowColormap(x_display, the_win, cmap[current_dga_cmap = 0]);
587 cebix 1.1 XF86DGAInstallColormap(x_display, screen, cmap[current_dga_cmap]);
588 gbeauche 1.15 }
589     XSync(x_display, false);
590 cebix 1.1
591 gbeauche 1.15 // Init blitting routines
592     int bytes_per_row = TrivialBytesPerRow((v_width + 7) & ~7, DepthModeForPixelDepth(depth));
593 gbeauche 1.3 #if ENABLE_VOSF
594     bool native_byte_order;
595     #ifdef WORDS_BIGENDIAN
596     native_byte_order = (XImageByteOrder(x_display) == MSBFirst);
597     #else
598     native_byte_order = (XImageByteOrder(x_display) == LSBFirst);
599     #endif
600     #if REAL_ADDRESSING || DIRECT_ADDRESSING
601     // Screen_blitter_init() returns TRUE if VOSF is mandatory
602     // i.e. the framebuffer update function is not Blit_Copy_Raw
603     use_vosf = Screen_blitter_init(&visualInfo, native_byte_order, depth);
604    
605     if (use_vosf) {
606     // Allocate memory for frame buffer (SIZE is extended to page-boundary)
607     the_host_buffer = the_buffer;
608     the_buffer_size = page_extend((height + 2) * bytes_per_row);
609     the_buffer_copy = (uint8 *)malloc(the_buffer_size);
610     the_buffer = (uint8 *)vm_acquire(the_buffer_size);
611 gbeauche 1.15 D(bug("the_buffer = %p, the_buffer_copy = %p, the_host_buffer = %p\n", the_buffer, the_buffer_copy, the_host_buffer));
612 gbeauche 1.3 }
613     #else
614     use_vosf = false;
615     #endif
616     #endif
617 gbeauche 1.15
618     // Set frame buffer base
619     D(bug("the_buffer = %p, use_vosf = %d\n", the_buffer, use_vosf));
620 gbeauche 1.3 screen_base = (uint32)the_buffer;
621 cebix 1.1 VModes[cur_mode].viRowBytes = bytes_per_row;
622     return true;
623     #else
624     ErrorAlert("SheepShaver has been compiled with DGA support disabled.");
625     return false;
626     #endif
627     }
628    
629     static bool open_display(void)
630     {
631 gbeauche 1.13 D(bug("open_display()\n"));
632     const VideoInfo &mode = VModes[cur_mode];
633    
634     // Find best available X visual
635     if (!find_visual_for_depth(mode.viAppleMode)) {
636     ErrorAlert(GetString(STR_NO_XVISUAL_ERR));
637     return false;
638     }
639    
640     // Create color maps
641     if (color_class == PseudoColor || color_class == DirectColor) {
642     cmap[0] = XCreateColormap(x_display, rootwin, vis, AllocAll);
643     cmap[1] = XCreateColormap(x_display, rootwin, vis, AllocAll);
644     } else {
645     cmap[0] = XCreateColormap(x_display, rootwin, vis, AllocNone);
646     cmap[1] = XCreateColormap(x_display, rootwin, vis, AllocNone);
647     }
648    
649     // Find pixel format of direct modes
650     if (color_class == DirectColor || color_class == TrueColor) {
651     rshift = gshift = bshift = 0;
652     rloss = gloss = bloss = 8;
653     uint32 mask;
654     for (mask=vis->red_mask; !(mask&1); mask>>=1)
655     ++rshift;
656     for (; mask&1; mask>>=1)
657     --rloss;
658     for (mask=vis->green_mask; !(mask&1); mask>>=1)
659     ++gshift;
660     for (; mask&1; mask>>=1)
661     --gloss;
662     for (mask=vis->blue_mask; !(mask&1); mask>>=1)
663     ++bshift;
664     for (; mask&1; mask>>=1)
665     --bloss;
666     }
667    
668     // Preset palette pixel values for CLUT or gamma table
669     if (color_class == DirectColor) {
670     int num = vis->map_entries;
671     for (int i=0; i<num; i++) {
672     int c = (i * 256) / num;
673     x_palette[i].pixel = map_rgb(c, c, c);
674     x_palette[i].flags = DoRed | DoGreen | DoBlue;
675     }
676     } else if (color_class == PseudoColor) {
677     for (int i=0; i<256; i++) {
678     x_palette[i].pixel = i;
679     x_palette[i].flags = DoRed | DoGreen | DoBlue;
680     }
681     }
682    
683     // Load gray ramp to color map
684     int num = (color_class == DirectColor ? vis->map_entries : 256);
685     for (int i=0; i<num; i++) {
686     int c = (i * 256) / num;
687     x_palette[i].red = c * 0x0101;
688     x_palette[i].green = c * 0x0101;
689     x_palette[i].blue = c * 0x0101;
690     }
691     if (color_class == PseudoColor || color_class == DirectColor) {
692     XStoreColors(x_display, cmap[0], x_palette, num);
693     XStoreColors(x_display, cmap[1], x_palette, num);
694 cebix 1.1 }
695 gbeauche 1.3
696 gbeauche 1.13 #ifdef ENABLE_VOSF
697     // Load gray ramp to 8->16/32 expand map
698     if (!IsDirectMode(get_current_mode()) && xdepth > 8)
699     for (int i=0; i<256; i++)
700     ExpandMap[i] = map_rgb(i, i, i);
701     #endif
702    
703     // Create display of requested type
704     display_type = mode.viType;
705     depth = depth_of_video_mode(mode.viAppleMode);
706    
707 gbeauche 1.3 bool display_open = false;
708 cebix 1.1 if (display_type == DIS_SCREEN)
709 gbeauche 1.3 display_open = open_dga(VModes[cur_mode].viXsize, VModes[cur_mode].viYsize);
710 cebix 1.1 else if (display_type == DIS_WINDOW)
711 gbeauche 1.3 display_open = open_window(VModes[cur_mode].viXsize, VModes[cur_mode].viYsize);
712    
713     #ifdef ENABLE_VOSF
714     if (use_vosf) {
715     // Initialize the VOSF system
716     if (!video_vosf_init()) {
717     ErrorAlert(GetString(STR_VOSF_INIT_ERR));
718     return false;
719     }
720     }
721     #endif
722    
723     return display_open;
724 cebix 1.1 }
725    
726    
727     /*
728     * Close display
729     */
730    
731     // Close window
732     static void close_window(void)
733     {
734 gbeauche 1.3 if (have_shm) {
735     XShmDetach(x_display, &shminfo);
736     #ifdef ENABLE_VOSF
737     the_host_buffer = NULL; // don't free() in driver_base dtor
738     #else
739     the_buffer_copy = NULL; // don't free() in driver_base dtor
740     #endif
741     }
742     if (img) {
743     if (!have_shm)
744     img->data = NULL;
745     XDestroyImage(img);
746     }
747     if (have_shm) {
748     shmdt(shminfo.shmaddr);
749     shmctl(shminfo.shmid, IPC_RMID, 0);
750     }
751     if (the_gc)
752     XFreeGC(x_display, the_gc);
753    
754 gbeauche 1.13 XFlush(x_display);
755     XSync(x_display, false);
756 cebix 1.1 }
757    
758     // Close DGA mode
759     static void close_dga(void)
760     {
761     #ifdef ENABLE_XF86_DGA
762     XF86DGADirectVideo(x_display, screen, 0);
763     XUngrabPointer(x_display, CurrentTime);
764     XUngrabKeyboard(x_display, CurrentTime);
765     #endif
766    
767     #ifdef ENABLE_XF86_VIDMODE
768     if (has_vidmode)
769     XF86VidModeSwitchToMode(x_display, screen, x_video_modes[0]);
770     #endif
771 gbeauche 1.3
772     if (!use_vosf) {
773     // don't free() the screen buffer in driver_base dtor
774     the_buffer = NULL;
775     }
776     #ifdef ENABLE_VOSF
777     else {
778     // don't free() the screen buffer in driver_base dtor
779     the_host_buffer = NULL;
780     }
781     #endif
782 cebix 1.1 }
783    
784     static void close_display(void)
785     {
786     if (display_type == DIS_SCREEN)
787     close_dga();
788     else if (display_type == DIS_WINDOW)
789     close_window();
790 gbeauche 1.3
791 gbeauche 1.15 // Close window
792     if (the_win) {
793     XUnmapWindow(x_display, the_win);
794     wait_unmapped(the_win);
795     XDestroyWindow(x_display, the_win);
796     }
797    
798 gbeauche 1.13 // Free colormaps
799     if (cmap[0]) {
800     XFreeColormap(x_display, cmap[0]);
801     cmap[0] = 0;
802     }
803     if (cmap[1]) {
804     XFreeColormap(x_display, cmap[1]);
805     cmap[1] = 0;
806     }
807    
808 gbeauche 1.3 #ifdef ENABLE_VOSF
809     if (use_vosf) {
810     // Deinitialize VOSF
811     video_vosf_exit();
812     }
813     #endif
814    
815     // Free frame buffer(s)
816     if (!use_vosf) {
817     if (the_buffer_copy) {
818     free(the_buffer_copy);
819     the_buffer_copy = NULL;
820     }
821     }
822     #ifdef ENABLE_VOSF
823     else {
824     // the_buffer shall always be mapped through vm_acquire() so that we can vm_protect() it at will
825     if (the_buffer != VM_MAP_FAILED) {
826     D(bug(" releasing the_buffer at %p (%d bytes)\n", the_buffer, the_buffer_size));
827     vm_release(the_buffer, the_buffer_size);
828     the_buffer = NULL;
829     }
830     if (the_host_buffer) {
831     D(bug(" freeing the_host_buffer at %p\n", the_host_buffer));
832     free(the_host_buffer);
833     the_host_buffer = NULL;
834     }
835     if (the_buffer_copy) {
836     D(bug(" freeing the_buffer_copy at %p\n", the_buffer_copy));
837     free(the_buffer_copy);
838     the_buffer_copy = NULL;
839     }
840     }
841     #endif
842 cebix 1.1 }
843    
844    
845     /*
846     * Initialization
847     */
848    
849 gbeauche 1.6 // Init keycode translation table
850     static void keycode_init(void)
851     {
852     bool use_kc = PrefsFindBool("keycodes");
853     if (use_kc) {
854    
855     // Get keycode file path from preferences
856     const char *kc_path = PrefsFindString("keycodefile");
857    
858     // Open keycode table
859     FILE *f = fopen(kc_path ? kc_path : KEYCODE_FILE_NAME, "r");
860     if (f == NULL) {
861     char str[256];
862     sprintf(str, GetString(STR_KEYCODE_FILE_WARN), kc_path ? kc_path : KEYCODE_FILE_NAME, strerror(errno));
863     WarningAlert(str);
864     return;
865     }
866    
867     // Default translation table
868     for (int i=0; i<256; i++)
869     keycode_table[i] = -1;
870    
871     // Search for server vendor string, then read keycodes
872     const char *vendor = ServerVendor(x_display);
873     bool vendor_found = false;
874     char line[256];
875     while (fgets(line, 255, f)) {
876     // Read line
877     int len = strlen(line);
878     if (len == 0)
879     continue;
880     line[len-1] = 0;
881    
882     // Comments begin with "#" or ";"
883     if (line[0] == '#' || line[0] == ';' || line[0] == 0)
884     continue;
885    
886     if (vendor_found) {
887     // Read keycode
888     int x_code, mac_code;
889     if (sscanf(line, "%d %d", &x_code, &mac_code) == 2)
890     keycode_table[x_code & 0xff] = mac_code;
891     else
892     break;
893     } else {
894     // Search for vendor string
895     if (strstr(vendor, line) == vendor)
896     vendor_found = true;
897     }
898     }
899    
900     // Keycode file completely read
901     fclose(f);
902     use_keycodes = vendor_found;
903    
904     // Vendor not found? Then display warning
905     if (!vendor_found) {
906     char str[256];
907     sprintf(str, GetString(STR_KEYCODE_VENDOR_WARN), vendor, kc_path ? kc_path : KEYCODE_FILE_NAME);
908     WarningAlert(str);
909     return;
910     }
911     }
912     }
913    
914 gbeauche 1.15 // Find Apple mode matching best specified dimensions
915     static int find_apple_resolution(int xsize, int ysize)
916     {
917     int apple_id;
918     if (xsize < 800)
919     apple_id = APPLE_640x480;
920     else if (xsize < 1024)
921     apple_id = APPLE_800x600;
922     else if (xsize < 1152)
923     apple_id = APPLE_1024x768;
924     else if (xsize < 1280) {
925     if (ysize < 900)
926     apple_id = APPLE_1152x768;
927     else
928     apple_id = APPLE_1152x900;
929     }
930     else if (xsize < 1600)
931     apple_id = APPLE_1280x1024;
932     else
933     apple_id = APPLE_1600x1200;
934     return apple_id;
935     }
936    
937     // Find mode in list of supported modes
938     static int find_mode(int apple_mode, int apple_id, int type)
939     {
940     for (VideoInfo *p = VModes; p->viType != DIS_INVALID; p++) {
941     if (p->viType == type && p->viAppleID == apple_id && p->viAppleMode == apple_mode)
942     return p - VModes;
943     }
944     return -1;
945     }
946    
947 gbeauche 1.13 // Add mode to list of supported modes
948     static void add_mode(VideoInfo *&p, uint32 allow, uint32 test, int apple_mode, int apple_id, int type)
949 cebix 1.1 {
950     if (allow & test) {
951     p->viType = type;
952     switch (apple_id) {
953     case APPLE_W_640x480:
954     case APPLE_640x480:
955     p->viXsize = 640;
956     p->viYsize = 480;
957     break;
958     case APPLE_W_800x600:
959     case APPLE_800x600:
960     p->viXsize = 800;
961     p->viYsize = 600;
962     break;
963     case APPLE_1024x768:
964     p->viXsize = 1024;
965     p->viYsize = 768;
966     break;
967 gbeauche 1.15 case APPLE_1152x768:
968     p->viXsize = 1152;
969     p->viYsize = 768;
970     break;
971 cebix 1.1 case APPLE_1152x900:
972     p->viXsize = 1152;
973     p->viYsize = 900;
974     break;
975     case APPLE_1280x1024:
976     p->viXsize = 1280;
977     p->viYsize = 1024;
978     break;
979     case APPLE_1600x1200:
980     p->viXsize = 1600;
981     p->viYsize = 1200;
982     break;
983     }
984 gbeauche 1.13 p->viRowBytes = TrivialBytesPerRow(p->viXsize, apple_mode);
985 cebix 1.1 p->viAppleMode = apple_mode;
986     p->viAppleID = apple_id;
987     p++;
988     }
989     }
990    
991 gbeauche 1.13 // Add standard list of windowed modes for given color depth
992     static void add_window_modes(VideoInfo *&p, int window_modes, int mode)
993     {
994     add_mode(p, window_modes, 1, mode, APPLE_W_640x480, DIS_WINDOW);
995     add_mode(p, window_modes, 2, mode, APPLE_W_800x600, DIS_WINDOW);
996     }
997    
998 cebix 1.1 static bool has_mode(int x, int y)
999     {
1000     #ifdef ENABLE_XF86_VIDMODE
1001     for (int i=0; i<num_x_video_modes; i++)
1002     if (x_video_modes[i]->hdisplay >= x && x_video_modes[i]->vdisplay >= y)
1003     return true;
1004     return false;
1005     #else
1006     return DisplayWidth(x_display, screen) >= x && DisplayHeight(x_display, screen) >= y;
1007     #endif
1008     }
1009    
1010     bool VideoInit(void)
1011     {
1012 gbeauche 1.3 #ifdef ENABLE_VOSF
1013     // Zero the mainBuffer structure
1014     mainBuffer.dirtyPages = NULL;
1015     mainBuffer.pageInfo = NULL;
1016     #endif
1017    
1018 gbeauche 1.4 // Check if X server runs on local machine
1019     local_X11 = (strncmp(XDisplayName(x_display_name), ":", 1) == 0)
1020     || (strncmp(XDisplayName(x_display_name), "unix:", 5) == 0);
1021    
1022 gbeauche 1.6 // Init keycode translation
1023     keycode_init();
1024    
1025 gbeauche 1.8 // Read frame skip prefs
1026     frame_skip = PrefsFindInt32("frameskip");
1027     if (frame_skip == 0)
1028     frame_skip = 1;
1029    
1030     // Read mouse wheel prefs
1031     mouse_wheel_mode = PrefsFindInt32("mousewheelmode");
1032     mouse_wheel_lines = PrefsFindInt32("mousewheellines");
1033    
1034 cebix 1.1 // Init variables
1035     private_data = NULL;
1036     video_activated = true;
1037    
1038     // Find screen and root window
1039     screen = XDefaultScreen(x_display);
1040     rootwin = XRootWindow(x_display, screen);
1041    
1042 gbeauche 1.13 // Get sorted list of available depths
1043     avail_depths = XListDepths(x_display, screen, &num_depths);
1044     if (avail_depths == NULL) {
1045     ErrorAlert(GetString(STR_UNSUPP_DEPTH_ERR));
1046     return false;
1047     }
1048     sort(avail_depths, avail_depths + num_depths);
1049    
1050 cebix 1.1 // Get screen depth
1051     xdepth = DefaultDepth(x_display, screen);
1052    
1053     #ifdef ENABLE_XF86_DGA
1054     // DGA available?
1055     int event_base, error_base;
1056 gbeauche 1.4 if (local_X11 && XF86DGAQueryExtension(x_display, &event_base, &error_base)) {
1057 cebix 1.1 int dga_flags = 0;
1058     XF86DGAQueryDirectVideo(x_display, screen, &dga_flags);
1059     has_dga = dga_flags & XF86DGADirectPresent;
1060     } else
1061     has_dga = false;
1062     #endif
1063    
1064     #ifdef ENABLE_XF86_VIDMODE
1065     // VidMode available?
1066     int vm_event_base, vm_error_base;
1067     has_vidmode = XF86VidModeQueryExtension(x_display, &vm_event_base, &vm_error_base);
1068     if (has_vidmode)
1069     XF86VidModeGetAllModeLines(x_display, screen, &num_x_video_modes, &x_video_modes);
1070     #endif
1071    
1072     // Find black and white colors
1073     XParseColor(x_display, DefaultColormap(x_display, screen), "rgb:00/00/00", &black);
1074     XAllocColor(x_display, DefaultColormap(x_display, screen), &black);
1075     XParseColor(x_display, DefaultColormap(x_display, screen), "rgb:ff/ff/ff", &white);
1076     XAllocColor(x_display, DefaultColormap(x_display, screen), &white);
1077     black_pixel = BlackPixel(x_display, screen);
1078     white_pixel = WhitePixel(x_display, screen);
1079    
1080     // Mac screen depth follows X depth (for now)
1081 gbeauche 1.13 int default_mode = APPLE_8_BIT;
1082     switch (DefaultDepth(x_display, screen)) {
1083     case 1:
1084     default_mode = APPLE_1_BIT;
1085     break;
1086     case 8:
1087     default_mode = APPLE_8_BIT;
1088     break;
1089     case 15: case 16:
1090     default_mode = APPLE_16_BIT;
1091     break;
1092     case 24: case 32:
1093     default_mode = APPLE_32_BIT;
1094     break;
1095 cebix 1.1 }
1096    
1097     // Construct video mode table
1098     uint32 window_modes = PrefsFindInt32("windowmodes");
1099     uint32 screen_modes = PrefsFindInt32("screenmodes");
1100     if (!has_dga)
1101     screen_modes = 0;
1102     if (window_modes == 0 && screen_modes == 0)
1103     window_modes |= 3; // Allow at least 640x480 and 800x600 window modes
1104    
1105     VideoInfo *p = VModes;
1106 gbeauche 1.13 for (unsigned int d = APPLE_1_BIT; d <= APPLE_32_BIT; d++)
1107     if (find_visual_for_depth(d))
1108     add_window_modes(p, window_modes, d);
1109    
1110 cebix 1.1 if (has_vidmode) {
1111     if (has_mode(640, 480))
1112 gbeauche 1.13 add_mode(p, screen_modes, 1, default_mode, APPLE_640x480, DIS_SCREEN);
1113 cebix 1.1 if (has_mode(800, 600))
1114 gbeauche 1.13 add_mode(p, screen_modes, 2, default_mode, APPLE_800x600, DIS_SCREEN);
1115 cebix 1.1 if (has_mode(1024, 768))
1116 gbeauche 1.13 add_mode(p, screen_modes, 4, default_mode, APPLE_1024x768, DIS_SCREEN);
1117 gbeauche 1.15 if (has_mode(1152, 768))
1118     add_mode(p, screen_modes, 64, default_mode, APPLE_1152x768, DIS_SCREEN);
1119 cebix 1.1 if (has_mode(1152, 900))
1120 gbeauche 1.13 add_mode(p, screen_modes, 8, default_mode, APPLE_1152x900, DIS_SCREEN);
1121 cebix 1.1 if (has_mode(1280, 1024))
1122 gbeauche 1.13 add_mode(p, screen_modes, 16, default_mode, APPLE_1280x1024, DIS_SCREEN);
1123 cebix 1.1 if (has_mode(1600, 1200))
1124 gbeauche 1.13 add_mode(p, screen_modes, 32, default_mode, APPLE_1600x1200, DIS_SCREEN);
1125 cebix 1.1 } else if (screen_modes) {
1126     int xsize = DisplayWidth(x_display, screen);
1127     int ysize = DisplayHeight(x_display, screen);
1128 gbeauche 1.15 int apple_id = find_apple_resolution(xsize, ysize);
1129 cebix 1.1 p->viType = DIS_SCREEN;
1130     p->viRowBytes = 0;
1131     p->viXsize = xsize;
1132     p->viYsize = ysize;
1133 gbeauche 1.13 p->viAppleMode = default_mode;
1134 cebix 1.1 p->viAppleID = apple_id;
1135     p++;
1136     }
1137     p->viType = DIS_INVALID; // End marker
1138     p->viRowBytes = 0;
1139     p->viXsize = p->viYsize = 0;
1140     p->viAppleMode = 0;
1141     p->viAppleID = 0;
1142    
1143 gbeauche 1.13 // Find default mode (window 640x480)
1144     cur_mode = -1;
1145 gbeauche 1.15 if (has_dga && screen_modes) {
1146     int screen_width = DisplayWidth(x_display, screen);
1147     int screen_height = DisplayHeight(x_display, screen);
1148     int apple_id = find_apple_resolution(screen_width, screen_height);
1149     if (apple_id != -1)
1150     cur_mode = find_mode(default_mode, apple_id, DIS_SCREEN);
1151 gbeauche 1.13 }
1152 gbeauche 1.15 if (cur_mode == -1)
1153     cur_mode = find_mode(default_mode, APPLE_W_640x480, DIS_WINDOW);
1154 gbeauche 1.13 assert(cur_mode != -1);
1155    
1156     #if DEBUG
1157     D(bug("Available video modes:\n"));
1158     for (p = VModes; p->viType != DIS_INVALID; p++) {
1159     int bits = depth_of_video_mode(p->viAppleMode);
1160     D(bug(" %dx%d (ID %02x), %d colors\n", p->viXsize, p->viYsize, p->viAppleID, 1 << bits));
1161     }
1162     #endif
1163    
1164 cebix 1.1 // Open window/screen
1165     if (!open_display())
1166     return false;
1167    
1168     #if 0
1169     // Ignore errors from now on
1170     XSetErrorHandler(ignore_errors);
1171     #endif
1172    
1173     // Start periodic thread
1174     XSync(x_display, false);
1175 gbeauche 1.15 Set_pthread_attr(&redraw_thread_attr, 0);
1176     redraw_thread_active = (pthread_create(&redraw_thread, &redraw_thread_attr, redraw_func, NULL) == 0);
1177 cebix 1.1 D(bug("Redraw thread installed (%ld)\n", redraw_thread));
1178     return true;
1179     }
1180    
1181    
1182     /*
1183     * Deinitialization
1184     */
1185    
1186     void VideoExit(void)
1187     {
1188     // Stop redraw thread
1189     if (redraw_thread_active) {
1190     pthread_cancel(redraw_thread);
1191     pthread_join(redraw_thread, NULL);
1192     redraw_thread_active = false;
1193     }
1194    
1195 gbeauche 1.3 #ifdef ENABLE_VOSF
1196     if (use_vosf) {
1197     // Deinitialize VOSF
1198     video_vosf_exit();
1199     }
1200     #endif
1201    
1202 cebix 1.1 // Close window and server connection
1203     if (x_display != NULL) {
1204     XSync(x_display, false);
1205     close_display();
1206     XFlush(x_display);
1207     XSync(x_display, false);
1208     }
1209     }
1210    
1211    
1212     /*
1213     * Suspend/resume emulator
1214     */
1215    
1216     extern void PauseEmulator(void);
1217     extern void ResumeEmulator(void);
1218    
1219     static void suspend_emul(void)
1220     {
1221     if (display_type == DIS_SCREEN) {
1222     // Release ctrl key
1223     ADBKeyUp(0x36);
1224     ctrl_down = false;
1225    
1226     // Pause MacOS thread
1227     PauseEmulator();
1228     emul_suspended = true;
1229    
1230     // Save frame buffer
1231     fb_save = malloc(VModes[cur_mode].viYsize * VModes[cur_mode].viRowBytes);
1232     if (fb_save)
1233     memcpy(fb_save, (void *)screen_base, VModes[cur_mode].viYsize * VModes[cur_mode].viRowBytes);
1234    
1235     // Close full screen display
1236     #ifdef ENABLE_XF86_DGA
1237     XF86DGADirectVideo(x_display, screen, 0);
1238     XUngrabPointer(x_display, CurrentTime);
1239     XUngrabKeyboard(x_display, CurrentTime);
1240     #endif
1241     XSync(x_display, false);
1242    
1243     // Open "suspend" window
1244     XSetWindowAttributes wattr;
1245     wattr.event_mask = KeyPressMask;
1246     wattr.background_pixel = black_pixel;
1247     wattr.border_pixel = black_pixel;
1248     wattr.backing_store = Always;
1249     wattr.backing_planes = xdepth;
1250     wattr.colormap = DefaultColormap(x_display, screen);
1251     XSync(x_display, false);
1252     suspend_win = XCreateWindow(x_display, rootwin, 0, 0, 512, 1, 0, xdepth,
1253     InputOutput, vis, CWEventMask | CWBackPixel | CWBorderPixel |
1254     CWBackingStore | CWBackingPlanes | (xdepth == 8 ? CWColormap : 0), &wattr);
1255     XSync(x_display, false);
1256     XStoreName(x_display, suspend_win, GetString(STR_SUSPEND_WINDOW_TITLE));
1257     XMapRaised(x_display, suspend_win);
1258     XSync(x_display, false);
1259     }
1260     }
1261    
1262     static void resume_emul(void)
1263     {
1264     // Close "suspend" window
1265     XDestroyWindow(x_display, suspend_win);
1266     XSync(x_display, false);
1267    
1268     // Reopen full screen display
1269     XGrabKeyboard(x_display, rootwin, 1, GrabModeAsync, GrabModeAsync, CurrentTime);
1270     XGrabPointer(x_display, rootwin, 1, PointerMotionMask | ButtonPressMask | ButtonReleaseMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
1271 gbeauche 1.12 #ifdef ENABLE_XF86_DGA
1272 cebix 1.1 XF86DGADirectVideo(x_display, screen, XF86DGADirectGraphics | XF86DGADirectKeyb | XF86DGADirectMouse);
1273     XF86DGASetViewPort(x_display, screen, 0, 0);
1274 gbeauche 1.12 #endif
1275 cebix 1.1 XSync(x_display, false);
1276    
1277 gbeauche 1.3 // the_buffer already contains the data to restore. i.e. since a temporary
1278     // frame buffer is used when VOSF is actually used, fb_save is therefore
1279     // not necessary.
1280     #ifdef ENABLE_VOSF
1281     if (use_vosf) {
1282     LOCK_VOSF;
1283     PFLAG_SET_ALL;
1284     UNLOCK_VOSF;
1285     memset(the_buffer_copy, 0, VModes[cur_mode].viRowBytes * VModes[cur_mode].viYsize);
1286     }
1287     #endif
1288    
1289 cebix 1.1 // Restore frame buffer
1290     if (fb_save) {
1291 gbeauche 1.3 #ifdef ENABLE_VOSF
1292     // Don't copy fb_save to the temporary frame buffer in VOSF mode
1293     if (!use_vosf)
1294     #endif
1295 cebix 1.1 memcpy((void *)screen_base, fb_save, VModes[cur_mode].viYsize * VModes[cur_mode].viRowBytes);
1296     free(fb_save);
1297     fb_save = NULL;
1298     }
1299     if (depth == 8)
1300     palette_changed = true;
1301    
1302     // Resume MacOS thread
1303     emul_suspended = false;
1304     ResumeEmulator();
1305     }
1306    
1307    
1308     /*
1309     * Close screen in full-screen mode
1310     */
1311    
1312     void VideoQuitFullScreen(void)
1313     {
1314     D(bug("VideoQuitFullScreen()\n"));
1315     if (display_type == DIS_SCREEN) {
1316     quit_full_screen = true;
1317     while (!quit_full_screen_ack) ;
1318     }
1319     }
1320    
1321    
1322     /*
1323     * X11 event handling
1324     */
1325    
1326     // Translate key event to Mac keycode
1327     static int kc_decode(KeySym ks)
1328     {
1329     switch (ks) {
1330     case XK_A: case XK_a: return 0x00;
1331     case XK_B: case XK_b: return 0x0b;
1332     case XK_C: case XK_c: return 0x08;
1333     case XK_D: case XK_d: return 0x02;
1334     case XK_E: case XK_e: return 0x0e;
1335     case XK_F: case XK_f: return 0x03;
1336     case XK_G: case XK_g: return 0x05;
1337     case XK_H: case XK_h: return 0x04;
1338     case XK_I: case XK_i: return 0x22;
1339     case XK_J: case XK_j: return 0x26;
1340     case XK_K: case XK_k: return 0x28;
1341     case XK_L: case XK_l: return 0x25;
1342     case XK_M: case XK_m: return 0x2e;
1343     case XK_N: case XK_n: return 0x2d;
1344     case XK_O: case XK_o: return 0x1f;
1345     case XK_P: case XK_p: return 0x23;
1346     case XK_Q: case XK_q: return 0x0c;
1347     case XK_R: case XK_r: return 0x0f;
1348     case XK_S: case XK_s: return 0x01;
1349     case XK_T: case XK_t: return 0x11;
1350     case XK_U: case XK_u: return 0x20;
1351     case XK_V: case XK_v: return 0x09;
1352     case XK_W: case XK_w: return 0x0d;
1353     case XK_X: case XK_x: return 0x07;
1354     case XK_Y: case XK_y: return 0x10;
1355     case XK_Z: case XK_z: return 0x06;
1356    
1357     case XK_1: case XK_exclam: return 0x12;
1358     case XK_2: case XK_at: return 0x13;
1359     case XK_3: case XK_numbersign: return 0x14;
1360     case XK_4: case XK_dollar: return 0x15;
1361     case XK_5: case XK_percent: return 0x17;
1362     case XK_6: return 0x16;
1363     case XK_7: return 0x1a;
1364     case XK_8: return 0x1c;
1365     case XK_9: return 0x19;
1366     case XK_0: return 0x1d;
1367    
1368     case XK_grave: case XK_asciitilde: return 0x0a;
1369     case XK_minus: case XK_underscore: return 0x1b;
1370     case XK_equal: case XK_plus: return 0x18;
1371     case XK_bracketleft: case XK_braceleft: return 0x21;
1372     case XK_bracketright: case XK_braceright: return 0x1e;
1373     case XK_backslash: case XK_bar: return 0x2a;
1374     case XK_semicolon: case XK_colon: return 0x29;
1375     case XK_apostrophe: case XK_quotedbl: return 0x27;
1376     case XK_comma: case XK_less: return 0x2b;
1377     case XK_period: case XK_greater: return 0x2f;
1378     case XK_slash: case XK_question: return 0x2c;
1379    
1380     case XK_Tab: if (ctrl_down) {suspend_emul(); return -1;} else return 0x30;
1381     case XK_Return: return 0x24;
1382     case XK_space: return 0x31;
1383     case XK_BackSpace: return 0x33;
1384    
1385     case XK_Delete: return 0x75;
1386     case XK_Insert: return 0x72;
1387     case XK_Home: case XK_Help: return 0x73;
1388     case XK_End: return 0x77;
1389     #ifdef __hpux
1390     case XK_Prior: return 0x74;
1391     case XK_Next: return 0x79;
1392     #else
1393     case XK_Page_Up: return 0x74;
1394     case XK_Page_Down: return 0x79;
1395     #endif
1396    
1397     case XK_Control_L: return 0x36;
1398     case XK_Control_R: return 0x36;
1399     case XK_Shift_L: return 0x38;
1400     case XK_Shift_R: return 0x38;
1401     case XK_Alt_L: return 0x37;
1402     case XK_Alt_R: return 0x37;
1403     case XK_Meta_L: return 0x3a;
1404     case XK_Meta_R: return 0x3a;
1405     case XK_Menu: return 0x32;
1406     case XK_Caps_Lock: return 0x39;
1407     case XK_Num_Lock: return 0x47;
1408    
1409     case XK_Up: return 0x3e;
1410     case XK_Down: return 0x3d;
1411     case XK_Left: return 0x3b;
1412     case XK_Right: return 0x3c;
1413    
1414     case XK_Escape: if (ctrl_down) {quit_full_screen = true; emerg_quit = true; return -1;} else return 0x35;
1415    
1416     case XK_F1: if (ctrl_down) {SysMountFirstFloppy(); return -1;} else return 0x7a;
1417     case XK_F2: return 0x78;
1418     case XK_F3: return 0x63;
1419     case XK_F4: return 0x76;
1420     case XK_F5: return 0x60;
1421     case XK_F6: return 0x61;
1422     case XK_F7: return 0x62;
1423     case XK_F8: return 0x64;
1424     case XK_F9: return 0x65;
1425     case XK_F10: return 0x6d;
1426     case XK_F11: return 0x67;
1427     case XK_F12: return 0x6f;
1428    
1429     case XK_Print: return 0x69;
1430     case XK_Scroll_Lock: return 0x6b;
1431     case XK_Pause: return 0x71;
1432    
1433     #if defined(XK_KP_Prior) && defined(XK_KP_Left) && defined(XK_KP_Insert) && defined (XK_KP_End)
1434     case XK_KP_0: case XK_KP_Insert: return 0x52;
1435     case XK_KP_1: case XK_KP_End: return 0x53;
1436     case XK_KP_2: case XK_KP_Down: return 0x54;
1437     case XK_KP_3: case XK_KP_Next: return 0x55;
1438     case XK_KP_4: case XK_KP_Left: return 0x56;
1439     case XK_KP_5: case XK_KP_Begin: return 0x57;
1440     case XK_KP_6: case XK_KP_Right: return 0x58;
1441     case XK_KP_7: case XK_KP_Home: return 0x59;
1442     case XK_KP_8: case XK_KP_Up: return 0x5b;
1443     case XK_KP_9: case XK_KP_Prior: return 0x5c;
1444     case XK_KP_Decimal: case XK_KP_Delete: return 0x41;
1445     #else
1446     case XK_KP_0: return 0x52;
1447     case XK_KP_1: return 0x53;
1448     case XK_KP_2: return 0x54;
1449     case XK_KP_3: return 0x55;
1450     case XK_KP_4: return 0x56;
1451     case XK_KP_5: return 0x57;
1452     case XK_KP_6: return 0x58;
1453     case XK_KP_7: return 0x59;
1454     case XK_KP_8: return 0x5b;
1455     case XK_KP_9: return 0x5c;
1456     case XK_KP_Decimal: return 0x41;
1457     #endif
1458     case XK_KP_Add: return 0x45;
1459     case XK_KP_Subtract: return 0x4e;
1460     case XK_KP_Multiply: return 0x43;
1461     case XK_KP_Divide: return 0x4b;
1462     case XK_KP_Enter: return 0x4c;
1463     case XK_KP_Equal: return 0x51;
1464     }
1465     return -1;
1466     }
1467    
1468 gbeauche 1.6 static int event2keycode(XKeyEvent &ev)
1469 cebix 1.1 {
1470     KeySym ks;
1471     int as;
1472     int i = 0;
1473    
1474     do {
1475 gbeauche 1.6 ks = XLookupKeysym(&ev, i++);
1476 cebix 1.1 as = kc_decode(ks);
1477     if (as != -1)
1478     return as;
1479     } while (ks != NoSymbol);
1480    
1481     return -1;
1482     }
1483    
1484     static void handle_events(void)
1485     {
1486     // Handle events
1487     for (;;) {
1488     XEvent event;
1489    
1490 gbeauche 1.10 XDisplayLock();
1491 gbeauche 1.9 if (!XCheckMaskEvent(x_display, eventmask, &event)) {
1492     // Handle clipboard events
1493     if (XCheckTypedEvent(x_display, SelectionRequest, &event))
1494     ClipboardSelectionRequest(&event.xselectionrequest);
1495     else if (XCheckTypedEvent(x_display, SelectionClear, &event))
1496     ClipboardSelectionClear(&event.xselectionclear);
1497 gbeauche 1.14
1498     // Window "close" widget clicked
1499     else if (XCheckTypedEvent(x_display, ClientMessage, &event)) {
1500     if (event.xclient.format == 32 && event.xclient.data.l[0] == WM_DELETE_WINDOW) {
1501     ADBKeyDown(0x7f); // Power key
1502     ADBKeyUp(0x7f);
1503     }
1504     }
1505 gbeauche 1.10
1506     XDisplayUnlock();
1507 cebix 1.1 break;
1508 gbeauche 1.9 }
1509 gbeauche 1.10 XDisplayUnlock();
1510 cebix 1.1
1511     switch (event.type) {
1512     // Mouse button
1513     case ButtonPress: {
1514     unsigned int button = ((XButtonEvent *)&event)->button;
1515     if (button < 4)
1516     ADBMouseDown(button - 1);
1517 gbeauche 1.8 else if (button < 6) { // Wheel mouse
1518     if (mouse_wheel_mode == 0) {
1519     int key = (button == 5) ? 0x79 : 0x74; // Page up/down
1520     ADBKeyDown(key);
1521     ADBKeyUp(key);
1522     } else {
1523     int key = (button == 5) ? 0x3d : 0x3e; // Cursor up/down
1524     for(int i=0; i<mouse_wheel_lines; i++) {
1525     ADBKeyDown(key);
1526     ADBKeyUp(key);
1527     }
1528     }
1529     }
1530 cebix 1.1 break;
1531     }
1532     case ButtonRelease: {
1533     unsigned int button = ((XButtonEvent *)&event)->button;
1534     if (button < 4)
1535     ADBMouseUp(button - 1);
1536     break;
1537     }
1538    
1539 gbeauche 1.15 // Mouse entered window
1540 cebix 1.1 case EnterNotify:
1541 gbeauche 1.15 if (event.xcrossing.mode != NotifyGrab && event.xcrossing.mode != NotifyUngrab)
1542     ADBMouseMoved(event.xmotion.x, event.xmotion.y);
1543 cebix 1.1 break;
1544 gbeauche 1.15
1545     // Mouse moved
1546 cebix 1.1 case MotionNotify:
1547 gbeauche 1.15 ADBMouseMoved(event.xmotion.x, event.xmotion.y);
1548 cebix 1.1 break;
1549    
1550     // Keyboard
1551     case KeyPress: {
1552 gbeauche 1.6 int code = event2keycode(event.xkey);
1553     if (use_keycodes && code != -1)
1554     code = keycode_table[event.xkey.keycode & 0xff];
1555     if (code != -1) {
1556 cebix 1.1 if (!emul_suspended) {
1557     ADBKeyDown(code);
1558     if (code == 0x36)
1559     ctrl_down = true;
1560     } else {
1561     if (code == 0x31)
1562     resume_emul(); // Space wakes us up
1563     }
1564     }
1565     break;
1566     }
1567     case KeyRelease: {
1568 gbeauche 1.6 int code = event2keycode(event.xkey);
1569     if (use_keycodes && code != 1)
1570     code = keycode_table[event.xkey.keycode & 0xff];
1571     if (code != -1) {
1572 cebix 1.1 ADBKeyUp(code);
1573     if (code == 0x36)
1574     ctrl_down = false;
1575     }
1576     break;
1577     }
1578    
1579     // Hidden parts exposed, force complete refresh
1580     case Expose:
1581 gbeauche 1.3 #ifdef ENABLE_VOSF
1582     if (use_vosf) { // VOSF refresh
1583     LOCK_VOSF;
1584     PFLAG_SET_ALL;
1585     UNLOCK_VOSF;
1586     }
1587     #endif
1588 cebix 1.1 memset(the_buffer_copy, 0, VModes[cur_mode].viRowBytes * VModes[cur_mode].viYsize);
1589     break;
1590     }
1591     }
1592     }
1593    
1594    
1595     /*
1596     * Execute video VBL routine
1597     */
1598    
1599     void VideoVBL(void)
1600     {
1601     if (emerg_quit)
1602     QuitEmulator();
1603    
1604     // Execute video VBL
1605     if (private_data != NULL && private_data->interruptsEnabled)
1606     VSLDoInterruptService(private_data->vslServiceID);
1607     }
1608    
1609    
1610     /*
1611     * Install graphics acceleration
1612     */
1613    
1614 gbeauche 1.18 // Rectangle inversion
1615     template< int bpp >
1616     static inline void do_invrect(uint8 *dest, uint32 length)
1617 cebix 1.1 {
1618 gbeauche 1.18 #define INVERT_1(PTR, OFS) ((uint8 *)(PTR))[OFS] = ~((uint8 *)(PTR))[OFS]
1619     #define INVERT_2(PTR, OFS) ((uint16 *)(PTR))[OFS] = ~((uint16 *)(PTR))[OFS]
1620     #define INVERT_4(PTR, OFS) ((uint32 *)(PTR))[OFS] = ~((uint32 *)(PTR))[OFS]
1621     #define INVERT_8(PTR, OFS) ((uint64 *)(PTR))[OFS] = ~((uint64 *)(PTR))[OFS]
1622    
1623     #ifndef UNALIGNED_PROFITABLE
1624     // Align on 16-bit boundaries
1625     if (bpp < 16 && (((uintptr)dest) & 1)) {
1626     INVERT_1(dest, 0);
1627     dest += 1; length -= 1;
1628     }
1629    
1630     // Align on 32-bit boundaries
1631     if (bpp < 32 && (((uintptr)dest) & 2)) {
1632     INVERT_2(dest, 0);
1633     dest += 2; length -= 2;
1634     }
1635     #endif
1636    
1637     // Invert 8-byte words
1638     if (length >= 8) {
1639     const int r = (length / 8) % 8;
1640     dest += r * 8;
1641    
1642     int n = ((length / 8) + 7) / 8;
1643     switch (r) {
1644     case 0: do {
1645     dest += 64;
1646     INVERT_8(dest, -8);
1647     case 7: INVERT_8(dest, -7);
1648     case 6: INVERT_8(dest, -6);
1649     case 5: INVERT_8(dest, -5);
1650     case 4: INVERT_8(dest, -4);
1651     case 3: INVERT_8(dest, -3);
1652     case 2: INVERT_8(dest, -2);
1653     case 1: INVERT_8(dest, -1);
1654     } while (--n > 0);
1655     }
1656     }
1657 cebix 1.1
1658 gbeauche 1.18 // 32-bit cell to invert?
1659     if (length & 4) {
1660     INVERT_4(dest, 0);
1661     if (bpp <= 16)
1662     dest += 4;
1663     }
1664    
1665     // 16-bit cell to invert?
1666     if (bpp <= 16 && (length & 2)) {
1667     INVERT_2(dest, 0);
1668     if (bpp <= 8)
1669     dest += 2;
1670     }
1671    
1672     // 8-bit cell to invert?
1673     if (bpp <= 8 && (length & 1))
1674     INVERT_1(dest, 0);
1675    
1676     #undef INVERT_1
1677     #undef INVERT_2
1678     #undef INVERT_4
1679     #undef INVERT_8
1680     }
1681    
1682     void NQD_invrect(uint32 arg)
1683     {
1684     D(bug("accl_invrect %08x\n", arg));
1685     accl_params *p = (accl_params *)arg;
1686    
1687     // Get inversion parameters
1688 cebix 1.1 int16 dest_X = p->dest_rect[1] - p->dest_bounds[1];
1689     int16 dest_Y = p->dest_rect[0] - p->dest_bounds[0];
1690 gbeauche 1.18 int16 width = p->dest_rect[3] - p->dest_rect[1];
1691     int16 height = p->dest_rect[2] - p->dest_rect[0];
1692 cebix 1.1 D(bug(" dest X %d, dest Y %d\n", dest_X, dest_Y));
1693 gbeauche 1.18 D(bug(" width %d, height %d, bytes_per_row %d\n", width, height, p->dest_row_bytes));
1694 cebix 1.1
1695 gbeauche 1.18 //!!?? pen_mode == 14
1696    
1697     // And perform the inversion
1698     const int bpp = bytes_per_pixel(p->dest_pixel_size);
1699     const int dest_row_bytes = p->dest_row_bytes;
1700     uint8 *dest = (uint8 *)(p->dest_base_addr + (dest_Y * dest_row_bytes) + (dest_X * bpp));
1701     width *= bpp;
1702     switch (bpp) {
1703     case 1:
1704     for (int i = 0; i < height; i++) {
1705     do_invrect<8>(dest, width);
1706     dest += dest_row_bytes;
1707     }
1708     break;
1709     case 2:
1710     for (int i = 0; i < height; i++) {
1711     do_invrect<16>(dest, width);
1712     dest += dest_row_bytes;
1713     }
1714     break;
1715     case 4:
1716     for (int i = 0; i < height; i++) {
1717     do_invrect<32>(dest, width);
1718     dest += dest_row_bytes;
1719     }
1720     break;
1721     }
1722 cebix 1.1 }
1723    
1724 gbeauche 1.18 // Rectangle filling
1725     template< int bpp >
1726     static inline void do_fillrect(uint8 *dest, uint32 color, uint32 length)
1727 cebix 1.1 {
1728 gbeauche 1.18 #define FILL_1(PTR, OFS, VAL) ((uint8 *)(PTR))[OFS] = (VAL)
1729     #define FILL_2(PTR, OFS, VAL) ((uint16 *)(PTR))[OFS] = (VAL)
1730     #define FILL_4(PTR, OFS, VAL) ((uint32 *)(PTR))[OFS] = (VAL)
1731     #define FILL_8(PTR, OFS, VAL) ((uint64 *)(PTR))[OFS] = (VAL)
1732    
1733     #ifndef UNALIGNED_PROFITABLE
1734     // Align on 16-bit boundaries
1735     if (bpp < 16 && (((uintptr)dest) & 1)) {
1736     FILL_1(dest, 0, color);
1737     dest += 1; length -= 1;
1738     }
1739    
1740     // Align on 32-bit boundaries
1741     if (bpp < 32 && (((uintptr)dest) & 2)) {
1742     FILL_2(dest, 0, color);
1743     dest += 2; length -= 2;
1744     }
1745     #endif
1746    
1747     // Fill 8-byte words
1748     if (length >= 8) {
1749     const uint64 c = (((uint64)color) << 32) | color;
1750     const int r = (length / 8) % 8;
1751     dest += r * 8;
1752    
1753     int n = ((length / 8) + 7) / 8;
1754     switch (r) {
1755     case 0: do {
1756     dest += 64;
1757     FILL_8(dest, -8, c);
1758     case 7: FILL_8(dest, -7, c);
1759     case 6: FILL_8(dest, -6, c);
1760     case 5: FILL_8(dest, -5, c);
1761     case 4: FILL_8(dest, -4, c);
1762     case 3: FILL_8(dest, -3, c);
1763     case 2: FILL_8(dest, -2, c);
1764     case 1: FILL_8(dest, -1, c);
1765     } while (--n > 0);
1766     }
1767     }
1768 cebix 1.1
1769 gbeauche 1.18 // 32-bit cell to fill?
1770     if (length & 4) {
1771     FILL_4(dest, 0, color);
1772     if (bpp <= 16)
1773     dest += 4;
1774     }
1775    
1776     // 16-bit cell to fill?
1777     if (bpp <= 16 && (length & 2)) {
1778     FILL_2(dest, 0, color);
1779     if (bpp <= 8)
1780     dest += 2;
1781     }
1782 cebix 1.1
1783 gbeauche 1.18 // 8-bit cell to fill?
1784     if (bpp <= 8 && (length & 1))
1785     FILL_1(dest, 0, color);
1786    
1787     #undef FILL_1
1788     #undef FILL_2
1789     #undef FILL_4
1790     #undef FILL_8
1791 cebix 1.1 }
1792    
1793 gbeauche 1.18 void NQD_fillrect(uint32 arg)
1794 cebix 1.1 {
1795 gbeauche 1.18 D(bug("accl_fillrect %08x\n", arg));
1796     accl_params *p = (accl_params *)arg;
1797 cebix 1.1
1798 gbeauche 1.18 // Get filling parameters
1799 cebix 1.1 int16 dest_X = p->dest_rect[1] - p->dest_bounds[1];
1800     int16 dest_Y = p->dest_rect[0] - p->dest_bounds[0];
1801 gbeauche 1.18 int16 width = p->dest_rect[3] - p->dest_rect[1];
1802     int16 height = p->dest_rect[2] - p->dest_rect[0];
1803     uint32 color = p->pen_mode == 8 ? p->fore_pen : p->back_pen;
1804 cebix 1.1 D(bug(" dest X %d, dest Y %d\n", dest_X, dest_Y));
1805 gbeauche 1.18 D(bug(" width %d, height %d\n", width, height));
1806     D(bug(" bytes_per_row %d color %08x\n", p->dest_row_bytes, color));
1807 cebix 1.1
1808 gbeauche 1.18 // And perform the fill
1809     const int bpp = bytes_per_pixel(p->dest_pixel_size);
1810     const int dest_row_bytes = p->dest_row_bytes;
1811     uint8 *dest = (uint8 *)(p->dest_base_addr + (dest_Y * dest_row_bytes) + (dest_X * bpp));
1812     width *= bpp;
1813     switch (bpp) {
1814     case 1:
1815     for (int i = 0; i < height; i++) {
1816     memset(dest, color, width);
1817     dest += dest_row_bytes;
1818     }
1819     break;
1820     case 2:
1821     for (int i = 0; i < height; i++) {
1822     do_fillrect<16>(dest, color, width);
1823     dest += dest_row_bytes;
1824     }
1825     break;
1826     case 4:
1827     for (int i = 0; i < height; i++) {
1828     do_fillrect<32>(dest, color, width);
1829     dest += dest_row_bytes;
1830     }
1831     break;
1832     }
1833 cebix 1.1 }
1834    
1835 gbeauche 1.18 bool NQD_fillrect_hook(uint32 arg)
1836 cebix 1.1 {
1837 gbeauche 1.18 D(bug("accl_fillrect_hook %08x\n", arg));
1838     accl_params *p = (accl_params *)arg;
1839 cebix 1.1
1840     // Check if we can accelerate this fillrect
1841 gbeauche 1.18 if (((uint32 *)p)[0x284 >> 2] != 0 && p->dest_pixel_size >= 8) {
1842 cebix 1.1 if (p->transfer_mode == 8) {
1843     // Fill
1844 gbeauche 1.18 p->draw_proc = NativeTVECT(NATIVE_FILLRECT);
1845     return true;
1846     }
1847     else if (p->transfer_mode == 10) {
1848 cebix 1.1 // Invert
1849 gbeauche 1.18 p->draw_proc = NativeTVECT(NATIVE_INVRECT);
1850 cebix 1.1 return true;
1851     }
1852     }
1853     return false;
1854     }
1855    
1856 gbeauche 1.16 // Rectangle blitting
1857     // TODO: optimize for VOSF and target pixmap == screen
1858     void NQD_bitblt(uint32 arg)
1859     {
1860     D(bug("accl_bitblt %08x\n", arg));
1861     accl_params *p = (accl_params *)arg;
1862    
1863     // Get blitting parameters
1864     int16 src_X = p->src_rect[1] - p->src_bounds[1];
1865     int16 src_Y = p->src_rect[0] - p->src_bounds[0];
1866     int16 dest_X = p->dest_rect[1] - p->dest_bounds[1];
1867     int16 dest_Y = p->dest_rect[0] - p->dest_bounds[0];
1868     int16 width = p->dest_rect[3] - p->dest_rect[1];
1869     int16 height = p->dest_rect[2] - p->dest_rect[0];
1870     D(bug(" src addr %08x, dest addr %08x\n", p->src_base_addr, p->dest_base_addr));
1871     D(bug(" src X %d, src Y %d, dest X %d, dest Y %d\n", src_X, src_Y, dest_X, dest_Y));
1872     D(bug(" width %d, height %d\n", width, height));
1873    
1874     // And perform the blit
1875     const int bpp = bytes_per_pixel(p->src_pixel_size);
1876     width *= bpp;
1877     if (p->src_row_bytes > 0) {
1878     const int src_row_bytes = p->src_row_bytes;
1879     const int dst_row_bytes = p->dest_row_bytes;
1880     uint8 *src = (uint8 *)p->src_base_addr + (src_Y * src_row_bytes) + (src_X * bpp);
1881     uint8 *dst = (uint8 *)p->dest_base_addr + (dest_Y * dst_row_bytes) + (dest_X * bpp);
1882     for (int i = 0; i < height; i++) {
1883     memcpy(dst, src, width);
1884     src += src_row_bytes;
1885     dst += dst_row_bytes;
1886     }
1887     }
1888     else {
1889     const int src_row_bytes = -p->src_row_bytes;
1890     const int dst_row_bytes = -p->dest_row_bytes;
1891     uint8 *src = (uint8 *)p->src_base_addr + ((src_Y + height - 1) * src_row_bytes) + (src_X * bpp);
1892     uint8 *dst = (uint8 *)p->dest_base_addr + ((dest_Y + height - 1) * dst_row_bytes) + (dest_X * bpp);
1893     for (int i = height - 1; i >= 0; i--) {
1894     memcpy(dst, src, width);
1895     src -= src_row_bytes;
1896     dst -= dst_row_bytes;
1897     }
1898     }
1899     }
1900    
1901 gbeauche 1.18 /*
1902     BitBlt transfer modes:
1903     0 : srcCopy
1904     1 : srcOr
1905     2 : srcXor
1906     3 : srcBic
1907     4 : notSrcCopy
1908     5 : notSrcOr
1909     6 : notSrcXor
1910     7 : notSrcBic
1911     32 : blend
1912     33 : addPin
1913     34 : addOver
1914     35 : subPin
1915     36 : transparent
1916     37 : adMax
1917     38 : subOver
1918     39 : adMin
1919     50 : hilite
1920     */
1921    
1922 gbeauche 1.16 bool NQD_bitblt_hook(uint32 arg)
1923     {
1924     D(bug("accl_draw_hook %08x\n", arg));
1925     accl_params *p = (accl_params *)arg;
1926    
1927     // Check if we can accelerate this bitblt
1928     if (((uint32 *)p)[0x18 >> 2] + ((uint32 *)p)[0x128 >> 2] == 0 &&
1929     ((uint32 *)p)[0x130 >> 2] == 0 &&
1930     p->src_pixel_size >= 8 && p->src_pixel_size == p->dest_pixel_size &&
1931 gbeauche 1.18 (p->src_row_bytes ^ p->dest_row_bytes) >= 0 && // same sign?
1932     p->transfer_mode == 0 && // srcCopy?
1933 gbeauche 1.16 ((uint32 *)p)[0x15c >> 2] > 0) {
1934    
1935     // Yes, set function pointer
1936     p->draw_proc = NativeTVECT(NATIVE_BITBLT);
1937     return true;
1938     }
1939     return false;
1940     }
1941    
1942 cebix 1.1 // Wait for graphics operation to finish
1943 gbeauche 1.16 bool NQD_sync_hook(uint32 arg)
1944 cebix 1.1 {
1945 gbeauche 1.16 D(bug("accl_sync_hook %08x\n", arg));
1946 cebix 1.1 return true;
1947     }
1948    
1949     void VideoInstallAccel(void)
1950     {
1951 gbeauche 1.18 // Temporary hack until it's fixed for e.g. little-endian & 64-bit platforms
1952     #ifndef __powerpc__
1953     return;
1954     #endif
1955    
1956 cebix 1.1 // Install acceleration hooks
1957     if (PrefsFindBool("gfxaccel")) {
1958     D(bug("Video: Installing acceleration hooks\n"));
1959 gbeauche 1.16 uint32 base;
1960    
1961     SheepVar bitblt_hook_info(sizeof(accl_hook_info));
1962     base = bitblt_hook_info.addr();
1963     WriteMacInt32(base + 0, NativeTVECT(NATIVE_BITBLT_HOOK));
1964     WriteMacInt32(base + 4, NativeTVECT(NATIVE_SYNC_HOOK));
1965     WriteMacInt32(base + 8, ACCL_BITBLT);
1966     NQDMisc(6, bitblt_hook_info.ptr());
1967    
1968 gbeauche 1.18 SheepVar fillrect_hook_info(sizeof(accl_hook_info));
1969     base = fillrect_hook_info.addr();
1970     WriteMacInt32(base + 0, NativeTVECT(NATIVE_FILLRECT_HOOK));
1971     WriteMacInt32(base + 4, NativeTVECT(NATIVE_SYNC_HOOK));
1972     WriteMacInt32(base + 8, ACCL_FILLRECT);
1973     NQDMisc(6, fillrect_hook_info.ptr());
1974 cebix 1.1 }
1975     }
1976    
1977    
1978     /*
1979     * Change video mode
1980     */
1981    
1982     int16 video_mode_change(VidLocals *csSave, uint32 ParamPtr)
1983     {
1984     /* return if no mode change */
1985     if ((csSave->saveData == ReadMacInt32(ParamPtr + csData)) &&
1986     (csSave->saveMode == ReadMacInt16(ParamPtr + csMode))) return noErr;
1987    
1988     /* first find video mode in table */
1989     for (int i=0; VModes[i].viType != DIS_INVALID; i++) {
1990     if ((ReadMacInt16(ParamPtr + csMode) == VModes[i].viAppleMode) &&
1991     (ReadMacInt32(ParamPtr + csData) == VModes[i].viAppleID)) {
1992     csSave->saveMode = ReadMacInt16(ParamPtr + csMode);
1993     csSave->saveData = ReadMacInt32(ParamPtr + csData);
1994     csSave->savePage = ReadMacInt16(ParamPtr + csPage);
1995    
1996     // Disable interrupts and pause redraw thread
1997     DisableInterrupt();
1998     thread_stop_ack = false;
1999     thread_stop_req = true;
2000     while (!thread_stop_ack) ;
2001    
2002     /* close old display */
2003     close_display();
2004    
2005     /* open new display */
2006     cur_mode = i;
2007     bool ok = open_display();
2008    
2009     /* opening the screen failed? Then bail out */
2010     if (!ok) {
2011     ErrorAlert(GetString(STR_FULL_SCREEN_ERR));
2012     QuitEmulator();
2013     }
2014    
2015     WriteMacInt32(ParamPtr + csBaseAddr, screen_base);
2016     csSave->saveBaseAddr=screen_base;
2017     csSave->saveData=VModes[cur_mode].viAppleID;/* First mode ... */
2018     csSave->saveMode=VModes[cur_mode].viAppleMode;
2019    
2020     // Enable interrupts and resume redraw thread
2021     thread_stop_req = false;
2022     EnableInterrupt();
2023     return noErr;
2024     }
2025     }
2026     return paramErr;
2027     }
2028    
2029    
2030     /*
2031     * Set color palette
2032     */
2033    
2034     void video_set_palette(void)
2035     {
2036 gbeauche 1.13 LOCK_PALETTE;
2037    
2038     // Convert colors to XColor array
2039     int mode = get_current_mode();
2040     int num_in = palette_size(mode);
2041     int num_out = 256;
2042     bool stretch = false;
2043     if (IsDirectMode(mode)) {
2044     // If X is in 565 mode we have to stretch the gamma table from 32 to 64 entries
2045     num_out = vis->map_entries;
2046     stretch = true;
2047     }
2048     XColor *p = x_palette;
2049     for (int i=0; i<num_out; i++) {
2050     int c = (stretch ? (i * num_in) / num_out : i);
2051     p->red = mac_pal[c].red * 0x0101;
2052     p->green = mac_pal[c].green * 0x0101;
2053     p->blue = mac_pal[c].blue * 0x0101;
2054     p++;
2055     }
2056    
2057     #ifdef ENABLE_VOSF
2058     // Recalculate pixel color expansion map
2059     if (!IsDirectMode(mode) && xdepth > 8) {
2060     for (int i=0; i<256; i++) {
2061     int c = i & (num_in-1); // If there are less than 256 colors, we repeat the first entries (this makes color expansion easier)
2062     ExpandMap[i] = map_rgb(mac_pal[c].red, mac_pal[c].green, mac_pal[c].blue);
2063     }
2064    
2065     // We have to redraw everything because the interpretation of pixel values changed
2066     LOCK_VOSF;
2067     PFLAG_SET_ALL;
2068     UNLOCK_VOSF;
2069     memset(the_buffer_copy, 0, VModes[cur_mode].viRowBytes * VModes[cur_mode].viYsize);
2070     }
2071     #endif
2072    
2073     // Tell redraw thread to change palette
2074 cebix 1.1 palette_changed = true;
2075 gbeauche 1.13
2076     UNLOCK_PALETTE;
2077 cebix 1.1 }
2078    
2079    
2080     /*
2081     * Set cursor image for window
2082     */
2083    
2084     void video_set_cursor(void)
2085     {
2086     cursor_changed = true;
2087     }
2088    
2089    
2090     /*
2091     * Thread for window refresh, event handling and other periodic actions
2092     */
2093    
2094     static void update_display(void)
2095     {
2096     // Incremental update code
2097     int wide = 0, high = 0, x1, x2, y1, y2, i, j;
2098     int bytes_per_row = VModes[cur_mode].viRowBytes;
2099     int bytes_per_pixel = VModes[cur_mode].viRowBytes / VModes[cur_mode].viXsize;
2100     uint8 *p, *p2;
2101    
2102     // Check for first line from top and first line from bottom that have changed
2103     y1 = 0;
2104     for (j=0; j<VModes[cur_mode].viYsize; j++) {
2105     if (memcmp(&the_buffer[j * bytes_per_row], &the_buffer_copy[j * bytes_per_row], bytes_per_row)) {
2106     y1 = j;
2107     break;
2108     }
2109     }
2110     y2 = y1 - 1;
2111     for (j=VModes[cur_mode].viYsize-1; j>=y1; j--) {
2112     if (memcmp(&the_buffer[j * bytes_per_row], &the_buffer_copy[j * bytes_per_row], bytes_per_row)) {
2113     y2 = j;
2114     break;
2115     }
2116     }
2117     high = y2 - y1 + 1;
2118    
2119     // Check for first column from left and first column from right that have changed
2120     if (high) {
2121     if (depth == 1) {
2122     x1 = VModes[cur_mode].viXsize;
2123     for (j=y1; j<=y2; j++) {
2124     p = &the_buffer[j * bytes_per_row];
2125     p2 = &the_buffer_copy[j * bytes_per_row];
2126     for (i=0; i<(x1>>3); i++) {
2127     if (*p != *p2) {
2128     x1 = i << 3;
2129     break;
2130     }
2131     p++;
2132     p2++;
2133     }
2134     }
2135     x2 = x1;
2136     for (j=y1; j<=y2; j++) {
2137     p = &the_buffer[j * bytes_per_row];
2138     p2 = &the_buffer_copy[j * bytes_per_row];
2139     p += bytes_per_row;
2140     p2 += bytes_per_row;
2141     for (i=(VModes[cur_mode].viXsize>>3); i>(x2>>3); i--) {
2142     p--;
2143     p2--;
2144     if (*p != *p2) {
2145     x2 = i << 3;
2146     break;
2147     }
2148     }
2149     }
2150     wide = x2 - x1;
2151    
2152     // Update copy of the_buffer
2153     if (high && wide) {
2154     for (j=y1; j<=y2; j++) {
2155     i = j * bytes_per_row + (x1 >> 3);
2156     memcpy(&the_buffer_copy[i], &the_buffer[i], wide >> 3);
2157     }
2158     }
2159    
2160     } else {
2161     x1 = VModes[cur_mode].viXsize;
2162     for (j=y1; j<=y2; j++) {
2163     p = &the_buffer[j * bytes_per_row];
2164     p2 = &the_buffer_copy[j * bytes_per_row];
2165     for (i=0; i<x1; i++) {
2166     if (memcmp(p, p2, bytes_per_pixel)) {
2167     x1 = i;
2168     break;
2169     }
2170     p += bytes_per_pixel;
2171     p2 += bytes_per_pixel;
2172     }
2173     }
2174     x2 = x1;
2175     for (j=y1; j<=y2; j++) {
2176     p = &the_buffer[j * bytes_per_row];
2177     p2 = &the_buffer_copy[j * bytes_per_row];
2178     p += bytes_per_row;
2179     p2 += bytes_per_row;
2180     for (i=VModes[cur_mode].viXsize; i>x2; i--) {
2181     p -= bytes_per_pixel;
2182     p2 -= bytes_per_pixel;
2183     if (memcmp(p, p2, bytes_per_pixel)) {
2184     x2 = i;
2185     break;
2186     }
2187     }
2188     }
2189     wide = x2 - x1;
2190    
2191     // Update copy of the_buffer
2192     if (high && wide) {
2193     for (j=y1; j<=y2; j++) {
2194     i = j * bytes_per_row + x1 * bytes_per_pixel;
2195     memcpy(&the_buffer_copy[i], &the_buffer[i], bytes_per_pixel * wide);
2196     }
2197     }
2198     }
2199     }
2200    
2201     // Refresh display
2202     if (high && wide) {
2203 gbeauche 1.10 XDisplayLock();
2204 cebix 1.1 if (have_shm)
2205     XShmPutImage(x_display, the_win, the_gc, img, x1, y1, x1, y1, wide, high, 0);
2206     else
2207     XPutImage(x_display, the_win, the_gc, img, x1, y1, x1, y1, wide, high);
2208 gbeauche 1.10 XDisplayUnlock();
2209 cebix 1.1 }
2210     }
2211    
2212 gbeauche 1.10 const int VIDEO_REFRESH_HZ = 60;
2213     const int VIDEO_REFRESH_DELAY = 1000000 / VIDEO_REFRESH_HZ;
2214    
2215 gbeauche 1.13 static void handle_palette_changes(void)
2216     {
2217     LOCK_PALETTE;
2218    
2219     if (palette_changed && !emul_suspended) {
2220     palette_changed = false;
2221    
2222     int mode = get_current_mode();
2223     if (color_class == PseudoColor || color_class == DirectColor) {
2224     int num = vis->map_entries;
2225     bool set_clut = true;
2226     if (!IsDirectMode(mode) && color_class == DirectColor) {
2227     if (display_type == DIS_WINDOW)
2228     set_clut = false; // Indexed mode on true color screen, don't set CLUT
2229     }
2230    
2231     if (set_clut) {
2232     XDisplayLock();
2233     XStoreColors(x_display, cmap[0], x_palette, num);
2234     XStoreColors(x_display, cmap[1], x_palette, num);
2235     XSync(x_display, false);
2236     XDisplayUnlock();
2237     }
2238     }
2239    
2240     #ifdef ENABLE_XF86_DGA
2241     if (display_type == DIS_SCREEN) {
2242     current_dga_cmap ^= 1;
2243     if (!IsDirectMode(mode) && cmap[current_dga_cmap])
2244     XF86DGAInstallColormap(x_display, screen, cmap[current_dga_cmap]);
2245     }
2246     #endif
2247     }
2248    
2249     UNLOCK_PALETTE;
2250     }
2251    
2252 cebix 1.1 static void *redraw_func(void *arg)
2253     {
2254 gbeauche 1.10 int fd = ConnectionNumber(x_display);
2255    
2256     uint64 start = GetTicks_usec();
2257     int64 ticks = 0;
2258     uint64 next = GetTicks_usec() + VIDEO_REFRESH_DELAY;
2259 cebix 1.1
2260     for (;;) {
2261    
2262     // Pause if requested (during video mode switches)
2263     while (thread_stop_req)
2264     thread_stop_ack = true;
2265    
2266 gbeauche 1.10 int64 delay = next - GetTicks_usec();
2267     if (delay < -VIDEO_REFRESH_DELAY) {
2268    
2269     // We are lagging far behind, so we reset the delay mechanism
2270     next = GetTicks_usec();
2271 cebix 1.1
2272 gbeauche 1.10 } else if (delay <= 0) {
2273    
2274     // Delay expired, refresh display
2275     next += VIDEO_REFRESH_DELAY;
2276     ticks++;
2277    
2278     // Handle X11 events
2279     handle_events();
2280    
2281     // Quit DGA mode if requested
2282     if (quit_full_screen) {
2283     quit_full_screen = false;
2284     if (display_type == DIS_SCREEN) {
2285     XDisplayLock();
2286 cebix 1.1 #ifdef ENABLE_XF86_DGA
2287 gbeauche 1.10 XF86DGADirectVideo(x_display, screen, 0);
2288     XUngrabPointer(x_display, CurrentTime);
2289     XUngrabKeyboard(x_display, CurrentTime);
2290 gbeauche 1.15 XUnmapWindow(x_display, the_win);
2291     wait_unmapped(the_win);
2292     XDestroyWindow(x_display, the_win);
2293 gbeauche 1.10 #endif
2294     XSync(x_display, false);
2295     XDisplayUnlock();
2296     quit_full_screen_ack = true;
2297     return NULL;
2298     }
2299 cebix 1.1 }
2300    
2301 gbeauche 1.10 // Refresh display and set cursor image in window mode
2302     static int tick_counter = 0;
2303     if (display_type == DIS_WINDOW) {
2304     tick_counter++;
2305     if (tick_counter >= frame_skip) {
2306     tick_counter = 0;
2307 cebix 1.1
2308 gbeauche 1.10 // Update display
2309 gbeauche 1.3 #ifdef ENABLE_VOSF
2310 gbeauche 1.10 if (use_vosf) {
2311     XDisplayLock();
2312     if (mainBuffer.dirty) {
2313     LOCK_VOSF;
2314     update_display_window_vosf();
2315     UNLOCK_VOSF;
2316     XSync(x_display, false); // Let the server catch up
2317     }
2318     XDisplayUnlock();
2319 gbeauche 1.3 }
2320 gbeauche 1.10 else
2321 gbeauche 1.3 #endif
2322 gbeauche 1.10 update_display();
2323 cebix 1.1
2324 gbeauche 1.10 // Set new cursor image if it was changed
2325     if (cursor_changed) {
2326     cursor_changed = false;
2327     memcpy(cursor_image->data, MacCursor + 4, 32);
2328     memcpy(cursor_mask_image->data, MacCursor + 36, 32);
2329     XDisplayLock();
2330     XFreeCursor(x_display, mac_cursor);
2331     XPutImage(x_display, cursor_map, cursor_gc, cursor_image, 0, 0, 0, 0, 16, 16);
2332     XPutImage(x_display, cursor_mask_map, cursor_mask_gc, cursor_mask_image, 0, 0, 0, 0, 16, 16);
2333     mac_cursor = XCreatePixmapCursor(x_display, cursor_map, cursor_mask_map, &black, &white, MacCursor[2], MacCursor[3]);
2334     XDefineCursor(x_display, the_win, mac_cursor);
2335     XDisplayUnlock();
2336     }
2337 cebix 1.1 }
2338     }
2339 gbeauche 1.3 #ifdef ENABLE_VOSF
2340 gbeauche 1.10 else if (use_vosf) {
2341     // Update display (VOSF variant)
2342     if (++tick_counter >= frame_skip) {
2343     tick_counter = 0;
2344     if (mainBuffer.dirty) {
2345     LOCK_VOSF;
2346     update_display_dga_vosf();
2347     UNLOCK_VOSF;
2348     }
2349 gbeauche 1.3 }
2350     }
2351     #endif
2352 cebix 1.1
2353 gbeauche 1.10 // Set new palette if it was changed
2354 gbeauche 1.13 handle_palette_changes();
2355 gbeauche 1.10
2356     } else {
2357    
2358     // No display refresh pending, check for X events
2359     fd_set readfds;
2360     FD_ZERO(&readfds);
2361     FD_SET(fd, &readfds);
2362     struct timeval timeout;
2363     timeout.tv_sec = 0;
2364     timeout.tv_usec = delay;
2365     if (select(fd+1, &readfds, NULL, NULL, &timeout) > 0)
2366     handle_events();
2367 cebix 1.1 }
2368     }
2369     return NULL;
2370     }