ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/SheepShaver/src/Unix/video_x.cpp
Revision: 1.4
Committed: 2003-10-26T07:54:02Z (20 years, 10 months ago) by gbeauche
Branch: MAIN
Changes since 1.3: +9 -3 lines
Log Message:
allow DGA & Xshm only on local displays

File Contents

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