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

File Contents

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