ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/SheepShaver/src/Unix/video_x.cpp
Revision: 1.15
Committed: 2004-04-13T22:22:21Z (20 years, 1 month ago) by gbeauche
Branch: MAIN
Changes since 1.14: +98 -51 lines
Log Message:
Fix DGA mode for emulated PPC targets. It currently doesn't work in native
mode as the stack is corrupted and we are jumping to garbage when moving
the mouse. Also add 1152x768 resolution from PBG4, but make timing match
the 1152x870 version.

Cleanups, further merges from Basilisk II tree.

File Contents

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