ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/SheepShaver/src/Unix/video_x.cpp
Revision: 1.15
Committed: 2004-04-13T22:22:21Z (20 years, 7 months ago) by gbeauche
Branch: MAIN
Changes since 1.14: +98 -51 lines
Log Message:
Fix DGA mode for emulated PPC targets. It currently doesn't work in native
mode as the stack is corrupted and we are jumping to garbage when moving
the mouse. Also add 1152x768 resolution from PBG4, but make timing match
the 1152x870 version.

Cleanups, further merges from Basilisk II tree.

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