/[cebix]/SheepShaver/src/Unix/video_x.cpp
ViewVC logotype

Contents of /SheepShaver/src/Unix/video_x.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.19 - (show annotations)
Thu Apr 22 21:45:16 2004 UTC (9 years, 1 month ago) by gbeauche
Branch: MAIN
Changes since 1.18: +58 -61 lines
NQD: use ReadMacInt*() and WriteMacInt*() accessors, i.e. code should now
be little-endian and 64-bit safe.

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

Christian Bauer">Christian Bauer
ViewVC Help
Powered by ViewVC 1.1.15