ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/SheepShaver/src/Unix/video_x.cpp
Revision: 1.27
Committed: 2004-06-11T22:09:27Z (20 years, 4 months ago) by gbeauche
Branch: MAIN
Changes since 1.26: +30 -13 lines
Log Message:
Merge event2keycode() from Basilisk II, aka. make Option key mapped to
Mode_Switch work as real option key. Merge caps_on handling from B2 too.

File Contents

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