ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/SheepShaver/src/Unix/video_x.cpp
Revision: 1.10
Committed: 2003-12-31T18:16:55Z (20 years, 10 months ago) by gbeauche
Branch: MAIN
Changes since 1.9: +115 -76 lines
Log Message:
Add fast X11 display locking routines based on spinlocks, or on pthreads
in the worst case. Optimize out GetScrap() case when we already own the
selection. i.e. make it smoother. Use our own XDisplay{Un,}Lock() routines.

File Contents

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