ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/SheepShaver/src/Unix/video_x.cpp
Revision: 1.8
Committed: 2003-12-27T10:37:30Z (20 years, 5 months ago) by gbeauche
Branch: MAIN
Changes since 1.7: +24 -5 lines
Log Message:
Merge in Mouse Wheel support from Basilisk II.

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