ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/SheepShaver/src/Unix/video_x.cpp
Revision: 1.3
Committed: 2003-05-22T22:12:05Z (21 years, 5 months ago) by gbeauche
Branch: MAIN
Changes since 1.2: +218 -22 lines
Log Message:
Import VOSF from Basilisk II for faster and more accurate video refresh.
There may be some bugs left though. Rework sigsegv_handler() a little to
accomodate VOSF way of life.

TODO: merge video drivers infrastructure from B2.

File Contents

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