ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/video_x.cpp
Revision: 1.42
Committed: 2001-06-28T21:20:00Z (23 years, 5 months ago) by cebix
Branch: MAIN
Changes since 1.41: +204 -413 lines
Log Message:
video_x.cpp supports resolution switching in windowed mode: the available
resolutions are 512x384, 640x480, 800x600, 1024x768 and 1280x1024 (the prefs
editor has to be updated to reflect this). The resolution selected in the
prefs editor is used as the default, but it can be changed in the Monitors
control panel. So far only tested with direct addressing.

File Contents

# User Rev Content
1 cebix 1.1 /*
2     * video_x.cpp - Video/graphics emulation, X11 specific stuff
3     *
4 gbeauche 1.36 * Basilisk II (C) 1997-2001 Christian Bauer
5 cebix 1.1 *
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     /*
22     * NOTES:
23     * The Ctrl key works like a qualifier for special actions:
24     * Ctrl-Tab = suspend DGA mode
25     * Ctrl-Esc = emergency quit
26     * Ctrl-F1 = mount floppy
27     */
28    
29     #include "sysdeps.h"
30    
31     #include <X11/Xlib.h>
32     #include <X11/Xutil.h>
33     #include <X11/keysym.h>
34     #include <X11/extensions/XShm.h>
35     #include <sys/ipc.h>
36     #include <sys/shm.h>
37     #include <errno.h>
38    
39 cebix 1.15 #ifdef HAVE_PTHREADS
40     # include <pthread.h>
41     #endif
42    
43     #ifdef ENABLE_XF86_DGA
44     # include <X11/extensions/xf86dga.h>
45     #endif
46    
47     #ifdef ENABLE_XF86_VIDMODE
48     # include <X11/extensions/xf86vmode.h>
49     #endif
50    
51     #ifdef ENABLE_FBDEV_DGA
52     # include <sys/mman.h>
53     #endif
54    
55 cebix 1.1 #include "cpu_emulation.h"
56     #include "main.h"
57     #include "adb.h"
58     #include "macos_util.h"
59     #include "prefs.h"
60     #include "user_strings.h"
61     #include "video.h"
62    
63 cebix 1.16 #define DEBUG 0
64 cebix 1.1 #include "debug.h"
65    
66    
67     // Display types
68     enum {
69     DISPLAY_WINDOW, // X11 window, using MIT SHM extensions if possible
70     DISPLAY_DGA // DGA fullscreen display
71     };
72    
73     // Constants
74 cebix 1.4 const char KEYCODE_FILE_NAME[] = DATADIR "/keycodes";
75 cebix 1.7 const char FBDEVICES_FILE_NAME[] = DATADIR "/fbdevices";
76 cebix 1.1
77    
78     // Global variables
79 cebix 1.10 static int32 frame_skip; // Prefs items
80     static int16 mouse_wheel_mode = 1;
81     static int16 mouse_wheel_lines = 3;
82    
83 cebix 1.1 static int display_type = DISPLAY_WINDOW; // See enum above
84 cebix 1.16 static bool local_X11; // Flag: X server running on local machine?
85 cebix 1.1 static uint8 *the_buffer; // Mac frame buffer
86 cebix 1.15
87     #ifdef HAVE_PTHREADS
88 cebix 1.1 static bool redraw_thread_active = false; // Flag: Redraw thread installed
89 cebix 1.42 static volatile bool redraw_thread_cancel; // Flag: Cancel Redraw thread
90 cebix 1.1 static pthread_t redraw_thread; // Redraw thread
91 cebix 1.15 #endif
92 cebix 1.1
93     static bool has_dga = false; // Flag: Video DGA capable
94 cebix 1.12 static bool has_vidmode = false; // Flag: VidMode extension available
95 cebix 1.1
96     static bool ctrl_down = false; // Flag: Ctrl key pressed
97 cebix 1.3 static bool caps_on = false; // Flag: Caps Lock on
98 cebix 1.1 static bool quit_full_screen = false; // Flag: DGA close requested from redraw thread
99     static bool emerg_quit = false; // Flag: Ctrl-Esc pressed, emergency quit requested from MacOS thread
100     static bool emul_suspended = false; // Flag: Emulator suspended
101    
102     static bool classic_mode = false; // Flag: Classic Mac video mode
103    
104     static bool use_keycodes = false; // Flag: Use keycodes rather than keysyms
105     static int keycode_table[256]; // X keycode -> Mac keycode translation table
106    
107     // X11 variables
108     static int screen; // Screen number
109     static int xdepth; // Depth of X screen
110     static Window rootwin, the_win; // Root window and our window
111     static XVisualInfo visualInfo;
112     static Visual *vis;
113     static Colormap cmap[2]; // Two colormaps (DGA) for 8-bit mode
114 cebix 1.42 static bool cmap_allocated = false;
115 cebix 1.1 static XColor black, white;
116     static unsigned long black_pixel, white_pixel;
117     static int eventmask;
118 cebix 1.29 static const int win_eventmask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | EnterWindowMask | ExposureMask | StructureNotifyMask;
119     static const int dga_eventmask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | StructureNotifyMask;
120     static Atom WM_DELETE_WINDOW = (Atom)0;
121 cebix 1.1
122     static XColor palette[256]; // Color palette for 8-bit mode
123     static bool palette_changed = false; // Flag: Palette changed, redraw thread must set new colors
124 cebix 1.15 #ifdef HAVE_PTHREADS
125     static pthread_mutex_t palette_lock = PTHREAD_MUTEX_INITIALIZER; // Mutex to protect palette
126 cebix 1.29 #define LOCK_PALETTE pthread_mutex_lock(&palette_lock)
127     #define UNLOCK_PALETTE pthread_mutex_unlock(&palette_lock)
128     #else
129     #define LOCK_PALETTE
130     #define UNLOCK_PALETTE
131 cebix 1.15 #endif
132 cebix 1.1
133     // Variables for window mode
134     static GC the_gc;
135     static XImage *img = NULL;
136     static XShmSegmentInfo shminfo;
137     static Cursor mac_cursor;
138     static uint8 *the_buffer_copy = NULL; // Copy of Mac frame buffer
139     static bool have_shm = false; // Flag: SHM extensions available
140 cebix 1.13 static bool updt_box[17][17]; // Flag for Update
141     static int nr_boxes;
142     static const int sm_uptd[] = {4,1,6,3,0,5,2,7};
143     static int sm_no_boxes[] = {1,8,32,64,128,300};
144 cebix 1.1
145 cebix 1.7 // Variables for XF86 DGA mode
146 cebix 1.1 static int current_dga_cmap; // Number (0 or 1) of currently installed DGA colormap
147     static Window suspend_win; // "Suspend" window
148     static void *fb_save = NULL; // Saved frame buffer for suspend
149 cebix 1.15 #ifdef HAVE_PTHREADS
150 cebix 1.7 static pthread_mutex_t frame_buffer_lock = PTHREAD_MUTEX_INITIALIZER; // Mutex to protect frame buffer
151 cebix 1.29 #define LOCK_FRAME_BUFFER pthread_mutex_lock(&frame_buffer_lock);
152     #define UNLOCK_FRAME_BUFFER pthread_mutex_unlock(&frame_buffer_lock);
153     #else
154     #define LOCK_FRAME_BUFFER
155     #define UNLOCK_FRAME_BUFFER
156 cebix 1.15 #endif
157 cebix 1.1
158 cebix 1.7 // Variables for fbdev DGA mode
159     const char FBDEVICE_FILE_NAME[] = "/dev/fb";
160     static int fbdev_fd;
161 cebix 1.1
162 cebix 1.15 #ifdef ENABLE_XF86_VIDMODE
163 cebix 1.12 // Variables for XF86 VidMode support
164     static XF86VidModeModeInfo **x_video_modes; // Array of all available modes
165     static int num_x_video_modes;
166     #endif
167    
168 gbeauche 1.19 #ifdef ENABLE_VOSF
169     static bool use_vosf = true; // Flag: VOSF enabled
170     #else
171     static const bool use_vosf = false; // Flag: VOSF enabled
172     #endif
173    
174     // VideoRefresh function
175 cebix 1.42 static void VideoRefreshInit(void);
176 gbeauche 1.19 static void (*video_refresh)(void);
177 cebix 1.1
178     // Prototypes
179     static void *redraw_func(void *arg);
180 cebix 1.29 static int event2keycode(XKeyEvent &ev);
181 cebix 1.1
182     // From main_unix.cpp
183 cebix 1.16 extern char *x_display_name;
184 cebix 1.1 extern Display *x_display;
185    
186     // From sys_unix.cpp
187     extern void SysMountFirstFloppy(void);
188    
189 cebix 1.42
190 gbeauche 1.19 #ifdef ENABLE_VOSF
191     # include "video_vosf.h"
192     #endif
193    
194 cebix 1.1
195     /*
196     * Initialization
197     */
198    
199 cebix 1.42 // Add mode to list of supported modes
200     static void add_mode(uint32 width, uint32 height, uint32 resolution_id, uint32 bytes_per_row, video_depth depth)
201 cebix 1.1 {
202 cebix 1.40 video_mode mode;
203 cebix 1.42 mode.x = width;
204     mode.y = height;
205     mode.resolution_id = resolution_id;
206     mode.bytes_per_row = bytes_per_row;
207     mode.depth = depth;
208     VideoModes.push_back(mode);
209     }
210    
211     // Set Mac frame layout and base address (uses the_buffer/MacFrameBaseMac)
212     static void set_mac_frame_buffer(video_depth depth, bool native_byte_order)
213     {
214 gbeauche 1.19 #if !REAL_ADDRESSING && !DIRECT_ADDRESSING
215 cebix 1.5 int layout = FLAYOUT_DIRECT;
216 cebix 1.42 if (depth == VDEPTH_16BIT)
217     layout = (xdepth == 15) ? FLAYOUT_HOST_555 : FLAYOUT_HOST_565;
218     else if (depth == VDEPTH_32BIT)
219     layour = (xdepth == 24) ? FLAYOUT_HOST_888 : FLAYOUT_DIRECT;
220 cebix 1.15 if (native_byte_order)
221     MacFrameLayout = layout;
222     else
223     MacFrameLayout = FLAYOUT_DIRECT;
224 cebix 1.42 VideoMonitor.mac_frame_base = MacFrameBaseMac;
225     #else
226     VideoMonitor.mac_frame_base = Host2MacAddr(the_buffer);
227     D(bug("Host frame buffer = %p, ", the_buffer));
228 cebix 1.15 #endif
229 cebix 1.42 D(bug("VideoMonitor.mac_frame_base = %08x\n", VideoMonitor.mac_frame_base));
230 cebix 1.1 }
231    
232 cebix 1.29 // Set window name and class
233     static void set_window_name(Window w, int name)
234     {
235     const char *str = GetString(name);
236     XStoreName(x_display, w, str);
237     XSetIconName(x_display, w, str);
238    
239     XClassHint *hints;
240     hints = XAllocClassHint();
241     if (hints) {
242     hints->res_name = "BasiliskII";
243     hints->res_class = "BasiliskII";
244     XSetClassHint(x_display, w, hints);
245 cebix 1.33 XFree(hints);
246 cebix 1.29 }
247     }
248    
249     // Set window input focus flag
250     static void set_window_focus(Window w)
251     {
252     XWMHints *hints = XAllocWMHints();
253     if (hints) {
254     hints->input = True;
255     hints->initial_state = NormalState;
256     hints->flags = InputHint | StateHint;
257     XSetWMHints(x_display, w, hints);
258 cebix 1.33 XFree(hints);
259 cebix 1.29 }
260     }
261    
262     // Set WM_DELETE_WINDOW protocol on window (preventing it from being destroyed by the WM when clicking on the "close" widget)
263     static void set_window_delete_protocol(Window w)
264     {
265     WM_DELETE_WINDOW = XInternAtom(x_display, "WM_DELETE_WINDOW", false);
266     XSetWMProtocols(x_display, w, &WM_DELETE_WINDOW, 1);
267     }
268    
269     // Wait until window is mapped/unmapped
270     void wait_mapped(Window w)
271     {
272     XEvent e;
273     do {
274     XMaskEvent(x_display, StructureNotifyMask, &e);
275     } while ((e.type != MapNotify) || (e.xmap.event != w));
276     }
277    
278     void wait_unmapped(Window w)
279     {
280     XEvent e;
281     do {
282     XMaskEvent(x_display, StructureNotifyMask, &e);
283     } while ((e.type != UnmapNotify) || (e.xmap.event != w));
284     }
285    
286 cebix 1.1 // Trap SHM errors
287     static bool shm_error = false;
288     static int (*old_error_handler)(Display *, XErrorEvent *);
289    
290     static int error_handler(Display *d, XErrorEvent *e)
291     {
292     if (e->error_code == BadAccess) {
293     shm_error = true;
294     return 0;
295     } else
296     return old_error_handler(d, e);
297     }
298    
299     // Init window mode
300 cebix 1.42 static bool init_window(const video_mode &mode)
301 cebix 1.1 {
302 cebix 1.42 int width = mode.x, height = mode.y;
303 cebix 1.13 int aligned_width = (width + 15) & ~15;
304     int aligned_height = (height + 15) & ~15;
305    
306 cebix 1.1 // Set absolute mouse mode
307     ADBSetRelMouseMode(false);
308    
309     // Read frame skip prefs
310     frame_skip = PrefsFindInt32("frameskip");
311    
312     // Create window
313     XSetWindowAttributes wattr;
314     wattr.event_mask = eventmask = win_eventmask;
315     wattr.background_pixel = black_pixel;
316 cebix 1.29 wattr.colormap = cmap[0];
317 cebix 1.1
318     the_win = XCreateWindow(x_display, rootwin, 0, 0, width, height, 0, xdepth,
319 cebix 1.42 InputOutput, vis, CWEventMask | CWBackPixel | ((IsDirectMode(mode) || mode.depth == VDEPTH_1BIT) ? 0 : CWColormap), &wattr);
320 cebix 1.29
321     // Set window name/class
322     set_window_name(the_win, STR_WINDOW_TITLE);
323 cebix 1.26
324     // Indicate that we want keyboard input
325 cebix 1.29 set_window_focus(the_win);
326    
327     // Set delete protocol property
328     set_window_delete_protocol(the_win);
329 cebix 1.26
330     // Make window unresizable
331     {
332     XSizeHints *hints = XAllocSizeHints();
333     if (hints) {
334     hints->min_width = width;
335     hints->max_width = width;
336     hints->min_height = height;
337     hints->max_height = height;
338     hints->flags = PMinSize | PMaxSize;
339     XSetWMNormalHints(x_display, the_win, hints);
340 cebix 1.33 XFree(hints);
341 cebix 1.26 }
342     }
343    
344     // Show window
345 cebix 1.29 XMapWindow(x_display, the_win);
346     wait_mapped(the_win);
347 cebix 1.1
348     // Try to create and attach SHM image
349     have_shm = false;
350 cebix 1.42 if (mode.depth != VDEPTH_1BIT && local_X11 && XShmQueryExtension(x_display)) {
351 cebix 1.1
352     // Create SHM image ("height + 2" for safety)
353 cebix 1.42 img = XShmCreateImage(x_display, vis, mode.depth == VDEPTH_1BIT ? 1 : xdepth, mode.depth == VDEPTH_1BIT ? XYBitmap : ZPixmap, 0, &shminfo, width, height);
354 cebix 1.13 shminfo.shmid = shmget(IPC_PRIVATE, (aligned_height + 2) * img->bytes_per_line, IPC_CREAT | 0777);
355     the_buffer_copy = (uint8 *)shmat(shminfo.shmid, 0, 0);
356     shminfo.shmaddr = img->data = (char *)the_buffer_copy;
357 cebix 1.1 shminfo.readOnly = False;
358    
359     // Try to attach SHM image, catching errors
360     shm_error = false;
361     old_error_handler = XSetErrorHandler(error_handler);
362     XShmAttach(x_display, &shminfo);
363     XSync(x_display, false);
364     XSetErrorHandler(old_error_handler);
365     if (shm_error) {
366     shmdt(shminfo.shmaddr);
367     XDestroyImage(img);
368     shminfo.shmid = -1;
369     } else {
370     have_shm = true;
371     shmctl(shminfo.shmid, IPC_RMID, 0);
372     }
373     }
374 cebix 1.7
375 cebix 1.1 // Create normal X image if SHM doesn't work ("height + 2" for safety)
376     if (!have_shm) {
377 cebix 1.42 int bytes_per_row = TrivialBytesPerRow(aligned_width, mode.depth);
378 cebix 1.13 the_buffer_copy = (uint8 *)malloc((aligned_height + 2) * bytes_per_row);
379 cebix 1.42 img = XCreateImage(x_display, vis, mode.depth == VDEPTH_1BIT ? 1 : xdepth, mode.depth == VDEPTH_1BIT ? XYBitmap : ZPixmap, 0, (char *)the_buffer_copy, aligned_width, aligned_height, 32, bytes_per_row);
380 cebix 1.1 }
381    
382     // 1-Bit mode is big-endian
383 cebix 1.42 if (mode.depth == VDEPTH_1BIT) {
384 cebix 1.1 img->byte_order = MSBFirst;
385     img->bitmap_bit_order = MSBFirst;
386     }
387    
388 gbeauche 1.19 #ifdef ENABLE_VOSF
389 gbeauche 1.39 // Allocate memory for frame buffer (SIZE is extended to page-boundary)
390 gbeauche 1.19 the_host_buffer = the_buffer_copy;
391 gbeauche 1.39 the_buffer_size = page_extend((aligned_height + 2) * img->bytes_per_line);
392     the_buffer_copy = (uint8 *)vm_acquire(the_buffer_size);
393     the_buffer = (uint8 *)vm_acquire(the_buffer_size);
394 gbeauche 1.19 #else
395 cebix 1.16 // Allocate memory for frame buffer
396 cebix 1.13 the_buffer = (uint8 *)malloc((aligned_height + 2) * img->bytes_per_line);
397 gbeauche 1.19 #endif
398 cebix 1.1
399     // Create GC
400     the_gc = XCreateGC(x_display, the_win, 0, 0);
401     XSetState(x_display, the_gc, black_pixel, white_pixel, GXcopy, AllPlanes);
402    
403 cebix 1.13 // Create no_cursor
404 cebix 1.16 mac_cursor = XCreatePixmapCursor(x_display,
405     XCreatePixmap(x_display, the_win, 1, 1, 1),
406     XCreatePixmap(x_display, the_win, 1, 1, 1),
407     &black, &white, 0, 0);
408     XDefineCursor(x_display, the_win, mac_cursor);
409 cebix 1.1
410 cebix 1.40 // Add resolution and set VideoMonitor
411 gbeauche 1.19 bool native_byte_order;
412 cebix 1.1 #ifdef WORDS_BIGENDIAN
413 cebix 1.31 native_byte_order = (XImageByteOrder(x_display) == MSBFirst);
414 cebix 1.1 #else
415 cebix 1.31 native_byte_order = (XImageByteOrder(x_display) == LSBFirst);
416 cebix 1.1 #endif
417 gbeauche 1.19 #ifdef ENABLE_VOSF
418 gbeauche 1.36 Screen_blitter_init(&visualInfo, native_byte_order);
419 gbeauche 1.19 #endif
420 cebix 1.42 VideoMonitor.mode = mode;
421     set_mac_frame_buffer(mode.depth, native_byte_order);
422 cebix 1.7 return true;
423     }
424    
425     // Init fbdev DGA display
426 cebix 1.42 static bool init_fbdev_dga(const video_mode &mode)
427 cebix 1.7 {
428 cebix 1.15 #ifdef ENABLE_FBDEV_DGA
429 cebix 1.42 int width = mode.x, height = mode.y;
430    
431 cebix 1.7 // Find the maximum depth available
432     int ndepths, max_depth(0);
433     int *depths = XListDepths(x_display, screen, &ndepths);
434     if (depths == NULL) {
435 cebix 1.9 printf("FATAL: Could not determine the maximal depth available\n");
436 cebix 1.7 return false;
437     } else {
438     while (ndepths-- > 0) {
439     if (depths[ndepths] > max_depth)
440     max_depth = depths[ndepths];
441     }
442     }
443    
444     // Get fbdevices file path from preferences
445 cebix 1.8 const char *fbd_path = PrefsFindString("fbdevicefile");
446 cebix 1.7
447     // Open fbdevices file
448     FILE *fp = fopen(fbd_path ? fbd_path : FBDEVICES_FILE_NAME, "r");
449     if (fp == NULL) {
450     char str[256];
451     sprintf(str, GetString(STR_NO_FBDEVICE_FILE_ERR), fbd_path ? fbd_path : FBDEVICES_FILE_NAME, strerror(errno));
452     ErrorAlert(str);
453     return false;
454     }
455    
456     int fb_depth; // supported depth
457     uint32 fb_offset; // offset used for mmap(2)
458     char fb_name[20];
459     char line[256];
460     bool device_found = false;
461     while (fgets(line, 255, fp)) {
462     // Read line
463     int len = strlen(line);
464     if (len == 0)
465     continue;
466     line[len - 1] = '\0';
467    
468     // Comments begin with "#" or ";"
469     if ((line[0] == '#') || (line[0] == ';') || (line[0] == '\0'))
470     continue;
471    
472 cebix 1.8 if ((sscanf(line, "%19s %d %x", &fb_name, &fb_depth, &fb_offset) == 3)
473 cebix 1.42 && (strcmp(fb_name, fb_name) == 0) && (fb_depth == max_depth)) {
474 cebix 1.7 device_found = true;
475     break;
476     }
477     }
478    
479     // fbdevices file completely read
480     fclose(fp);
481    
482     // Frame buffer name not found ? Then, display warning
483     if (!device_found) {
484     char str[256];
485 cebix 1.42 sprintf(str, GetString(STR_FBDEV_NAME_ERR), fb_name, max_depth);
486 cebix 1.7 ErrorAlert(str);
487     return false;
488     }
489    
490 cebix 1.42 depth = fb_depth;
491 cebix 1.7
492     // Set relative mouse mode
493     ADBSetRelMouseMode(false);
494    
495     // Create window
496     XSetWindowAttributes wattr;
497 cebix 1.29 wattr.event_mask = eventmask = dga_eventmask;
498     wattr.background_pixel = white_pixel;
499     wattr.override_redirect = True;
500     wattr.colormap = cmap[0];
501 cebix 1.7
502     the_win = XCreateWindow(x_display, rootwin,
503     0, 0, width, height,
504     0, xdepth, InputOutput, vis,
505 cebix 1.29 CWEventMask | CWBackPixel | CWOverrideRedirect | (depth == 8 ? CWColormap : 0),
506 cebix 1.7 &wattr);
507 cebix 1.29
508     // Set window name/class
509     set_window_name(the_win, STR_WINDOW_TITLE);
510    
511     // Indicate that we want keyboard input
512     set_window_focus(the_win);
513    
514     // Show window
515 cebix 1.7 XMapRaised(x_display, the_win);
516 cebix 1.29 wait_mapped(the_win);
517 cebix 1.7
518     // Grab mouse and keyboard
519     XGrabKeyboard(x_display, the_win, True,
520     GrabModeAsync, GrabModeAsync, CurrentTime);
521     XGrabPointer(x_display, the_win, True,
522     PointerMotionMask | ButtonPressMask | ButtonReleaseMask,
523     GrabModeAsync, GrabModeAsync, the_win, None, CurrentTime);
524    
525 cebix 1.42 // Calculate bytes per row
526     int bytes_per_row = TrivialBytesPerRow(mode.x, mode.depth);
527 cebix 1.7
528 cebix 1.42 // Map frame buffer
529 cebix 1.7 if ((the_buffer = (uint8 *) mmap(NULL, height * bytes_per_row, PROT_READ | PROT_WRITE, MAP_PRIVATE, fbdev_fd, fb_offset)) == MAP_FAILED) {
530     if ((the_buffer = (uint8 *) mmap(NULL, height * bytes_per_row, PROT_READ | PROT_WRITE, MAP_SHARED, fbdev_fd, fb_offset)) == MAP_FAILED) {
531     char str[256];
532     sprintf(str, GetString(STR_FBDEV_MMAP_ERR), strerror(errno));
533     ErrorAlert(str);
534     return false;
535     }
536     }
537    
538 cebix 1.23 #if ENABLE_VOSF
539 gbeauche 1.19 #if REAL_ADDRESSING || DIRECT_ADDRESSING
540 gbeauche 1.36 // Screen_blitter_init() returns TRUE if VOSF is mandatory
541     // i.e. the framebuffer update function is not Blit_Copy_Raw
542     use_vosf = Screen_blitter_init(&visualInfo, true);
543 gbeauche 1.19
544     if (use_vosf) {
545 gbeauche 1.39 // Allocate memory for frame buffer (SIZE is extended to page-boundary)
546     the_host_buffer = the_buffer;
547     the_buffer_size = page_extend((height + 2) * bytes_per_row);
548     the_buffer_copy = (uint8 *)vm_acquire(the_buffer_size);
549     the_buffer = (uint8 *)vm_acquire(the_buffer_size);
550 gbeauche 1.19 }
551 cebix 1.23 #else
552 gbeauche 1.19 use_vosf = false;
553     #endif
554 cebix 1.23 #endif
555 gbeauche 1.19
556 cebix 1.42 // Set VideoMonitor
557     VideoMonitor.mode = mode;
558     set_mac_frame_buffer(mode.depth, true);
559 cebix 1.1 return true;
560 cebix 1.7 #else
561     ErrorAlert("Basilisk II has been compiled with fbdev DGA support disabled.");
562     return false;
563     #endif
564 cebix 1.1 }
565    
566 cebix 1.7 // Init XF86 DGA display
567 cebix 1.42 static bool init_xf86_dga(const video_mode &mode)
568 cebix 1.1 {
569 cebix 1.42 int width = mode.x, height = mode.y;
570    
571 cebix 1.15 #ifdef ENABLE_XF86_DGA
572 cebix 1.1 // Set relative mouse mode
573     ADBSetRelMouseMode(true);
574    
575 cebix 1.15 #ifdef ENABLE_XF86_VIDMODE
576 cebix 1.12 // Switch to best mode
577     if (has_vidmode) {
578     int best = 0;
579     for (int i=1; i<num_x_video_modes; i++) {
580     if (x_video_modes[i]->hdisplay >= width && x_video_modes[i]->vdisplay >= height &&
581     x_video_modes[i]->hdisplay <= x_video_modes[best]->hdisplay && x_video_modes[i]->vdisplay <= x_video_modes[best]->vdisplay) {
582     best = i;
583     }
584     }
585     XF86VidModeSwitchToMode(x_display, screen, x_video_modes[best]);
586     XF86VidModeSetViewPort(x_display, screen, 0, 0);
587 cebix 1.29 XSync(x_display, false);
588 cebix 1.12 }
589     #endif
590    
591 cebix 1.1 // Create window
592     XSetWindowAttributes wattr;
593     wattr.event_mask = eventmask = dga_eventmask;
594     wattr.override_redirect = True;
595    
596     the_win = XCreateWindow(x_display, rootwin, 0, 0, width, height, 0, xdepth,
597 cebix 1.28 InputOutput, vis, CWEventMask | CWOverrideRedirect, &wattr);
598 cebix 1.29
599     // Set window name/class
600     set_window_name(the_win, STR_WINDOW_TITLE);
601    
602     // Indicate that we want keyboard input
603     set_window_focus(the_win);
604    
605     // Show window
606 cebix 1.1 XMapRaised(x_display, the_win);
607 cebix 1.29 wait_mapped(the_win);
608 cebix 1.1
609     // Establish direct screen connection
610     XMoveResizeWindow(x_display, the_win, 0, 0, width, height);
611     XWarpPointer(x_display, None, rootwin, 0, 0, 0, 0, 0, 0);
612     XGrabKeyboard(x_display, rootwin, True, GrabModeAsync, GrabModeAsync, CurrentTime);
613     XGrabPointer(x_display, rootwin, True, PointerMotionMask | ButtonPressMask | ButtonReleaseMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
614    
615     int v_width, v_bank, v_size;
616     XF86DGAGetVideo(x_display, screen, (char **)&the_buffer, &v_width, &v_bank, &v_size);
617     XF86DGADirectVideo(x_display, screen, XF86DGADirectGraphics | XF86DGADirectKeyb | XF86DGADirectMouse);
618     XF86DGASetViewPort(x_display, screen, 0, 0);
619     XF86DGASetVidPage(x_display, screen, 0);
620    
621     // Set colormap
622 cebix 1.42 if (!IsDirectMode(mode)) {
623 cebix 1.1 XSetWindowColormap(x_display, the_win, cmap[current_dga_cmap = 0]);
624     XF86DGAInstallColormap(x_display, screen, cmap[current_dga_cmap]);
625     }
626 cebix 1.29 XSync(x_display, false);
627 cebix 1.1
628     // Set VideoMonitor
629 cebix 1.42 int bytes_per_row = TrivialBytesPerRow((v_width + 7) & ~7, mode.depth);
630 gbeauche 1.39
631     #ifdef VIDEO_VOSF
632 gbeauche 1.19 #if REAL_ADDRESSING || DIRECT_ADDRESSING
633 gbeauche 1.36 // Screen_blitter_init() returns TRUE if VOSF is mandatory
634     // i.e. the framebuffer update function is not Blit_Copy_Raw
635     use_vosf = Screen_blitter_init(&visualInfo, true);
636 gbeauche 1.19
637     if (use_vosf) {
638 gbeauche 1.39 // Allocate memory for frame buffer (SIZE is extended to page-boundary)
639     the_host_buffer = the_buffer;
640     the_buffer_size = page_extend((height + 2) * bytes_per_row);
641     the_buffer_copy = (uint8 *)vm_acquire(the_buffer_size);
642     the_buffer = (uint8 *)vm_acquire(the_buffer_size);
643 gbeauche 1.19 }
644 gbeauche 1.39 #else
645 gbeauche 1.19 use_vosf = false;
646     #endif
647 gbeauche 1.39 #endif
648 gbeauche 1.19
649 cebix 1.42 const_cast<video_mode *>(&mode)->bytes_per_row = bytes_per_row;
650     VideoMonitor.mode = mode;
651     set_mac_frame_buffer(mode.depth, true);
652 cebix 1.1 return true;
653     #else
654 cebix 1.7 ErrorAlert("Basilisk II has been compiled with XF86 DGA support disabled.");
655 cebix 1.1 return false;
656     #endif
657     }
658    
659     // Init keycode translation table
660     static void keycode_init(void)
661     {
662     bool use_kc = PrefsFindBool("keycodes");
663     if (use_kc) {
664    
665     // Get keycode file path from preferences
666     const char *kc_path = PrefsFindString("keycodefile");
667    
668     // Open keycode table
669     FILE *f = fopen(kc_path ? kc_path : KEYCODE_FILE_NAME, "r");
670     if (f == NULL) {
671     char str[256];
672     sprintf(str, GetString(STR_KEYCODE_FILE_WARN), kc_path ? kc_path : KEYCODE_FILE_NAME, strerror(errno));
673     WarningAlert(str);
674     return;
675     }
676    
677     // Default translation table
678     for (int i=0; i<256; i++)
679     keycode_table[i] = -1;
680    
681     // Search for server vendor string, then read keycodes
682     const char *vendor = ServerVendor(x_display);
683     bool vendor_found = false;
684     char line[256];
685     while (fgets(line, 255, f)) {
686     // Read line
687     int len = strlen(line);
688     if (len == 0)
689     continue;
690     line[len-1] = 0;
691    
692     // Comments begin with "#" or ";"
693     if (line[0] == '#' || line[0] == ';' || line[0] == 0)
694     continue;
695    
696     if (vendor_found) {
697     // Read keycode
698     int x_code, mac_code;
699     if (sscanf(line, "%d %d", &x_code, &mac_code) == 2)
700     keycode_table[x_code & 0xff] = mac_code;
701     else
702     break;
703     } else {
704     // Search for vendor string
705     if (strstr(vendor, line) == vendor)
706     vendor_found = true;
707     }
708     }
709    
710     // Keycode file completely read
711     fclose(f);
712     use_keycodes = vendor_found;
713    
714     // Vendor not found? Then display warning
715     if (!vendor_found) {
716     char str[256];
717     sprintf(str, GetString(STR_KEYCODE_VENDOR_WARN), vendor, kc_path ? kc_path : KEYCODE_FILE_NAME);
718     WarningAlert(str);
719     return;
720     }
721     }
722     }
723    
724 cebix 1.42 // Open display for specified mode
725     static bool video_open(const video_mode &mode)
726 gbeauche 1.19 {
727 cebix 1.42 // Create color maps for 8 bit mode
728     if (!IsDirectMode(mode)) {
729     cmap[0] = XCreateColormap(x_display, rootwin, vis, AllocAll);
730     cmap[1] = XCreateColormap(x_display, rootwin, vis, AllocAll);
731     cmap_allocated = true;
732     XInstallColormap(x_display, cmap[0]);
733     XInstallColormap(x_display, cmap[1]);
734     }
735 gbeauche 1.19
736 cebix 1.42 // Initialize according to display type
737     switch (display_type) {
738     case DISPLAY_WINDOW:
739     if (!init_window(mode))
740     return false;
741     break;
742     case DISPLAY_DGA:
743     #ifdef ENABLE_FBDEV_DGA
744     if (!init_fbdev_dga(mode))
745     #else
746     if (!init_xf86_dga(mode))
747     #endif
748     return false;
749     break;
750     }
751 gbeauche 1.19
752 cebix 1.42 // Lock down frame buffer
753     LOCK_FRAME_BUFFER;
754 gbeauche 1.19
755 cebix 1.42 #if !REAL_ADDRESSING && !DIRECT_ADDRESSING
756     // Set variables for UAE memory mapping
757     MacFrameBaseHost = the_buffer;
758     MacFrameSize = VideoMonitor.mode.bytes_per_row * VideoMonitor.mode.y;
759 gbeauche 1.19
760 cebix 1.42 // No special frame buffer in Classic mode (frame buffer is in Mac RAM)
761     if (classic)
762     MacFrameLayout = FLAYOUT_NONE;
763     #endif
764 gbeauche 1.19
765 cebix 1.42 InitFrameBufferMapping();
766 gbeauche 1.19
767 cebix 1.42 #ifdef ENABLE_VOSF
768     if (use_vosf) {
769     // Initialize the mainBuffer structure
770     if (!video_init_buffer()) {
771     ErrorAlert(GetString(STR_VOSF_INIT_ERR));
772     return false;
773 gbeauche 1.19 }
774 cebix 1.42
775     // Initialize the handler for SIGSEGV
776     if (!sigsegv_install_handler(screen_fault_handler)) {
777     ErrorAlert("Could not initialize Video on SEGV signals");
778 gbeauche 1.19 return false;
779 cebix 1.42 }
780 gbeauche 1.19 }
781     #endif
782 cebix 1.42
783     // Initialize VideoRefresh function
784     VideoRefreshInit();
785    
786     XSync(x_display, false);
787    
788     #ifdef HAVE_PTHREADS
789     // Start redraw/input thread
790     redraw_thread_cancel = false;
791     redraw_thread_active = (pthread_create(&redraw_thread, NULL, redraw_func, NULL) == 0);
792     if (!redraw_thread_active) {
793     printf("FATAL: cannot create redraw thread\n");
794     return false;
795     }
796     #endif
797    
798 gbeauche 1.19 return true;
799     }
800    
801 cebix 1.1 bool VideoInit(bool classic)
802     {
803 cebix 1.42 classic_mode = classic;
804    
805 gbeauche 1.19 #ifdef ENABLE_VOSF
806     // Open /dev/zero
807     zero_fd = open("/dev/zero", O_RDWR);
808     if (zero_fd < 0) {
809     char str[256];
810     sprintf(str, GetString(STR_NO_DEV_ZERO_ERR), strerror(errno));
811     ErrorAlert(str);
812     return false;
813     }
814    
815     // Zero the mainBuffer structure
816     mainBuffer.dirtyPages = 0;
817     mainBuffer.pageInfo = 0;
818     #endif
819    
820 cebix 1.16 // Check if X server runs on local machine
821     local_X11 = (strncmp(XDisplayName(x_display_name), ":", 1) == 0)
822     || (strncmp(XDisplayName(x_display_name), "unix:", 5) == 0);
823    
824 cebix 1.1 // Init keycode translation
825     keycode_init();
826    
827 cebix 1.10 // Read prefs
828 cebix 1.32 mouse_wheel_mode = PrefsFindInt32("mousewheelmode");
829     mouse_wheel_lines = PrefsFindInt32("mousewheellines");
830 cebix 1.10
831 cebix 1.1 // Find screen and root window
832     screen = XDefaultScreen(x_display);
833     rootwin = XRootWindow(x_display, screen);
834 cebix 1.7
835 cebix 1.1 // Get screen depth
836     xdepth = DefaultDepth(x_display, screen);
837 cebix 1.7
838 cebix 1.15 #ifdef ENABLE_FBDEV_DGA
839 cebix 1.7 // Frame buffer name
840     char fb_name[20];
841    
842     // Could do fbdev dga ?
843     if ((fbdev_fd = open(FBDEVICE_FILE_NAME, O_RDWR)) != -1)
844     has_dga = true;
845     else
846     has_dga = false;
847     #endif
848 cebix 1.12
849 cebix 1.15 #ifdef ENABLE_XF86_DGA
850 cebix 1.1 // DGA available?
851 cebix 1.12 int dga_event_base, dga_error_base;
852 cebix 1.16 if (local_X11 && XF86DGAQueryExtension(x_display, &dga_event_base, &dga_error_base)) {
853 cebix 1.5 int dga_flags = 0;
854     XF86DGAQueryDirectVideo(x_display, screen, &dga_flags);
855     has_dga = dga_flags & XF86DGADirectPresent;
856     } else
857     has_dga = false;
858 cebix 1.1 #endif
859    
860 cebix 1.15 #ifdef ENABLE_XF86_VIDMODE
861 cebix 1.12 // VidMode available?
862     int vm_event_base, vm_error_base;
863     has_vidmode = XF86VidModeQueryExtension(x_display, &vm_event_base, &vm_error_base);
864     if (has_vidmode)
865     XF86VidModeGetAllModeLines(x_display, screen, &num_x_video_modes, &x_video_modes);
866     #endif
867    
868 cebix 1.1 // Find black and white colors
869     XParseColor(x_display, DefaultColormap(x_display, screen), "rgb:00/00/00", &black);
870     XAllocColor(x_display, DefaultColormap(x_display, screen), &black);
871     XParseColor(x_display, DefaultColormap(x_display, screen), "rgb:ff/ff/ff", &white);
872     XAllocColor(x_display, DefaultColormap(x_display, screen), &white);
873     black_pixel = BlackPixel(x_display, screen);
874     white_pixel = WhitePixel(x_display, screen);
875    
876     // Get appropriate visual
877     int color_class;
878     switch (xdepth) {
879     case 1:
880     color_class = StaticGray;
881     break;
882     case 8:
883     color_class = PseudoColor;
884     break;
885     case 15:
886     case 16:
887     case 24:
888     case 32:
889     color_class = TrueColor;
890     break;
891     default:
892     ErrorAlert(GetString(STR_UNSUPP_DEPTH_ERR));
893     return false;
894     }
895     if (!XMatchVisualInfo(x_display, screen, xdepth, color_class, &visualInfo)) {
896     ErrorAlert(GetString(STR_NO_XVISUAL_ERR));
897     return false;
898     }
899     if (visualInfo.depth != xdepth) {
900     ErrorAlert(GetString(STR_NO_XVISUAL_ERR));
901     return false;
902     }
903     vis = visualInfo.visual;
904    
905     // Get screen mode from preferences
906     const char *mode_str;
907 cebix 1.42 if (classic_mode)
908 cebix 1.1 mode_str = "win/512/342";
909     else
910     mode_str = PrefsFindString("screen");
911    
912 cebix 1.42 // Determine display type and default dimensions
913     int default_width = 512, default_height = 384;
914 cebix 1.1 display_type = DISPLAY_WINDOW;
915     if (mode_str) {
916 cebix 1.42 if (sscanf(mode_str, "win/%d/%d", &default_width, &default_height) == 2) {
917 cebix 1.1 display_type = DISPLAY_WINDOW;
918 cebix 1.15 #ifdef ENABLE_FBDEV_DGA
919 cebix 1.42 } else if (has_dga && sscanf(mode_str, "dga/%19s", fb_name) == 1) {
920     display_type = DISPLAY_DGA;
921     default_width = -1; default_height = -1;
922 cebix 1.7 #else
923 cebix 1.42 } else if (has_dga && sscanf(mode_str, "dga/%d/%d", &default_width, &default_height) == 2) {
924 cebix 1.1 display_type = DISPLAY_DGA;
925 cebix 1.7 #endif
926 cebix 1.42 }
927 cebix 1.1 }
928 cebix 1.42 if (default_width <= 0)
929     default_width = DisplayWidth(x_display, screen);
930     else if (default_width > DisplayWidth(x_display, screen))
931     default_width = DisplayWidth(x_display, screen);
932     if (default_height <= 0)
933     default_height = DisplayHeight(x_display, screen);
934     else if (default_height > DisplayHeight(x_display, screen))
935     default_height = DisplayHeight(x_display, screen);
936 cebix 1.1
937 cebix 1.42 // Mac screen depth is always 1 bit in Classic mode, but follows X depth otherwise
938     int depth = (classic_mode ? 1 : xdepth);
939     video_depth depth_mode = DepthModeForPixelDepth(depth);
940 cebix 1.1
941 cebix 1.42 // Construct list of supported modes
942     if (display_type == DISPLAY_WINDOW) {
943     if (classic)
944     add_mode(512, 342, 0x80, 64, depth_mode);
945     else {
946     add_mode(512, 384, 0x80, TrivialBytesPerRow(512, depth_mode), depth_mode);
947     add_mode(640, 480, 0x81, TrivialBytesPerRow(640, depth_mode), depth_mode);
948     add_mode(800, 600, 0x82, TrivialBytesPerRow(800, depth_mode), depth_mode);
949     add_mode(1024, 768, 0x83, TrivialBytesPerRow(1024, depth_mode), depth_mode);
950     add_mode(1280, 1024, 0x84, TrivialBytesPerRow(1280, depth_mode), depth_mode);
951 gbeauche 1.19 }
952 cebix 1.42 } else
953     add_mode(default_width, default_height, 0x80, TrivialBytesPerRow(default_width, depth_mode), depth_mode);
954 gbeauche 1.19
955 cebix 1.42 // Find requested default mode and open display
956     if (VideoModes.size() == 1)
957     return video_open(VideoModes[0]);
958     else {
959     // Find mode with specified dimensions
960     std::vector<video_mode>::const_iterator i = VideoModes.begin(), end = VideoModes.end();
961     while (i != end) {
962     if (i->x == default_width && i->y == default_height)
963     return video_open(*i);
964     ++i;
965 gbeauche 1.19 }
966 cebix 1.42 return video_open(VideoModes[0]);
967 gbeauche 1.19 }
968 cebix 1.1 }
969    
970    
971     /*
972     * Deinitialization
973     */
974    
975 cebix 1.42 // Close display
976     static void video_close(void)
977 cebix 1.1 {
978 cebix 1.15 #ifdef HAVE_PTHREADS
979 cebix 1.1 // Stop redraw thread
980     if (redraw_thread_active) {
981     redraw_thread_cancel = true;
982     #ifdef HAVE_PTHREAD_CANCEL
983     pthread_cancel(redraw_thread);
984     #endif
985     pthread_join(redraw_thread, NULL);
986     redraw_thread_active = false;
987     }
988 cebix 1.15 #endif
989 cebix 1.1
990     // Unlock frame buffer
991 cebix 1.29 UNLOCK_FRAME_BUFFER;
992 cebix 1.1
993     // Close window and server connection
994     if (x_display != NULL) {
995     XSync(x_display, false);
996    
997 cebix 1.15 #ifdef ENABLE_XF86_DGA
998 cebix 1.1 if (display_type == DISPLAY_DGA) {
999     XF86DGADirectVideo(x_display, screen, 0);
1000     XUngrabPointer(x_display, CurrentTime);
1001     XUngrabKeyboard(x_display, CurrentTime);
1002     }
1003 cebix 1.12 #endif
1004    
1005 cebix 1.15 #ifdef ENABLE_XF86_VIDMODE
1006 cebix 1.12 if (has_vidmode && display_type == DISPLAY_DGA)
1007     XF86VidModeSwitchToMode(x_display, screen, x_video_modes[0]);
1008 cebix 1.1 #endif
1009    
1010 cebix 1.15 #ifdef ENABLE_FBDEV_DGA
1011 cebix 1.7 if (display_type == DISPLAY_DGA) {
1012     XUngrabPointer(x_display, CurrentTime);
1013     XUngrabKeyboard(x_display, CurrentTime);
1014     close(fbdev_fd);
1015     }
1016     #endif
1017 cebix 1.1
1018     XFlush(x_display);
1019     XSync(x_display, false);
1020 cebix 1.42 XUnmapWindow(x_display, the_win);
1021     wait_unmapped(the_win);
1022     XDestroyWindow(x_display, the_win);
1023    
1024     if (have_shm) {
1025     XDestroyImage(img);
1026     XShmDetach(x_display, &shminfo);
1027     have_shm = false;
1028     } else {
1029     //!! free img
1030     }
1031     //!! free the_gc
1032    
1033     if (cmap_allocated) {
1034 cebix 1.1 XFreeColormap(x_display, cmap[0]);
1035     XFreeColormap(x_display, cmap[1]);
1036 cebix 1.42 cmap_allocated = false;
1037 cebix 1.1 }
1038 gbeauche 1.19
1039     if (!use_vosf) {
1040     if (the_buffer) {
1041     free(the_buffer);
1042     the_buffer = NULL;
1043     }
1044 cebix 1.13
1045 gbeauche 1.19 if (!have_shm && the_buffer_copy) {
1046     free(the_buffer_copy);
1047     the_buffer_copy = NULL;
1048     }
1049     }
1050     #ifdef ENABLE_VOSF
1051     else {
1052 cebix 1.42 //!! uninstall SEGV handler?
1053    
1054 gbeauche 1.39 if (the_buffer != (uint8 *)VM_MAP_FAILED) {
1055     vm_release(the_buffer, the_buffer_size);
1056 gbeauche 1.19 the_buffer = 0;
1057     }
1058    
1059 gbeauche 1.39 if (the_buffer_copy != (uint8 *)VM_MAP_FAILED) {
1060     vm_release(the_buffer_copy, the_buffer_size);
1061 gbeauche 1.19 the_buffer_copy = 0;
1062     }
1063     }
1064     #endif
1065     }
1066    
1067     #ifdef ENABLE_VOSF
1068     if (use_vosf) {
1069     // Clear mainBuffer data
1070     if (mainBuffer.pageInfo) {
1071     free(mainBuffer.pageInfo);
1072     mainBuffer.pageInfo = 0;
1073 cebix 1.13 }
1074    
1075 gbeauche 1.19 if (mainBuffer.dirtyPages) {
1076     free(mainBuffer.dirtyPages);
1077     mainBuffer.dirtyPages = 0;
1078 cebix 1.13 }
1079 cebix 1.1 }
1080 cebix 1.42 #endif
1081     }
1082    
1083     void VideoExit(void)
1084     {
1085     video_close();
1086 gbeauche 1.19
1087 cebix 1.42 #ifdef ENABLE_VOSF
1088 gbeauche 1.19 // Close /dev/zero
1089     if (zero_fd > 0)
1090     close(zero_fd);
1091     #endif
1092 cebix 1.1 }
1093    
1094    
1095     /*
1096     * Close down full-screen mode (if bringing up error alerts is unsafe while in full-screen mode)
1097     */
1098    
1099     void VideoQuitFullScreen(void)
1100     {
1101     D(bug("VideoQuitFullScreen()\n"));
1102     if (display_type == DISPLAY_DGA)
1103     quit_full_screen = true;
1104     }
1105    
1106    
1107     /*
1108     * Mac VBL interrupt
1109     */
1110    
1111     void VideoInterrupt(void)
1112     {
1113     // Emergency quit requested? Then quit
1114     if (emerg_quit)
1115     QuitEmulator();
1116    
1117     // Temporarily give up frame buffer lock (this is the point where
1118     // we are suspended when the user presses Ctrl-Tab)
1119 cebix 1.29 UNLOCK_FRAME_BUFFER;
1120     LOCK_FRAME_BUFFER;
1121 cebix 1.1 }
1122    
1123    
1124     /*
1125     * Set palette
1126     */
1127    
1128     void video_set_palette(uint8 *pal)
1129     {
1130 cebix 1.29 LOCK_PALETTE;
1131 cebix 1.1
1132     // Convert colors to XColor array
1133     for (int i=0; i<256; i++) {
1134     palette[i].pixel = i;
1135     palette[i].red = pal[i*3] * 0x0101;
1136     palette[i].green = pal[i*3+1] * 0x0101;
1137     palette[i].blue = pal[i*3+2] * 0x0101;
1138     palette[i].flags = DoRed | DoGreen | DoBlue;
1139     }
1140    
1141     // Tell redraw thread to change palette
1142     palette_changed = true;
1143    
1144 cebix 1.29 UNLOCK_PALETTE;
1145 cebix 1.1 }
1146    
1147    
1148     /*
1149 cebix 1.41 * Switch video mode
1150     */
1151    
1152     void video_switch_to_mode(const video_mode &mode)
1153     {
1154 cebix 1.42 video_close();
1155     video_open(mode);
1156 cebix 1.41 }
1157    
1158    
1159     /*
1160 cebix 1.1 * Suspend/resume emulator
1161     */
1162    
1163 cebix 1.15 #if defined(ENABLE_XF86_DGA) || defined(ENABLE_FBDEV_DGA)
1164 cebix 1.1 static void suspend_emul(void)
1165     {
1166     if (display_type == DISPLAY_DGA) {
1167     // Release ctrl key
1168     ADBKeyUp(0x36);
1169     ctrl_down = false;
1170    
1171     // Lock frame buffer (this will stop the MacOS thread)
1172 cebix 1.29 LOCK_FRAME_BUFFER;
1173 cebix 1.1
1174     // Save frame buffer
1175 cebix 1.40 fb_save = malloc(VideoMonitor.mode.y * VideoMonitor.mode.bytes_per_row);
1176 cebix 1.1 if (fb_save)
1177 cebix 1.40 memcpy(fb_save, the_buffer, VideoMonitor.mode.y * VideoMonitor.mode.bytes_per_row);
1178 cebix 1.1
1179     // Close full screen display
1180 cebix 1.15 #ifdef ENABLE_XF86_DGA
1181 cebix 1.1 XF86DGADirectVideo(x_display, screen, 0);
1182 cebix 1.7 #endif
1183 cebix 1.1 XUngrabPointer(x_display, CurrentTime);
1184     XUngrabKeyboard(x_display, CurrentTime);
1185     XUnmapWindow(x_display, the_win);
1186 cebix 1.29 wait_unmapped(the_win);
1187 cebix 1.1
1188     // Open "suspend" window
1189     XSetWindowAttributes wattr;
1190     wattr.event_mask = KeyPressMask;
1191     wattr.background_pixel = black_pixel;
1192 cebix 1.7
1193 cebix 1.1 suspend_win = XCreateWindow(x_display, rootwin, 0, 0, 512, 1, 0, xdepth,
1194 cebix 1.29 InputOutput, vis, CWEventMask | CWBackPixel, &wattr);
1195     set_window_name(suspend_win, STR_SUSPEND_WINDOW_TITLE);
1196     set_window_focus(suspend_win);
1197     XMapWindow(x_display, suspend_win);
1198 cebix 1.1 emul_suspended = true;
1199     }
1200     }
1201    
1202     static void resume_emul(void)
1203     {
1204     // Close "suspend" window
1205     XDestroyWindow(x_display, suspend_win);
1206     XSync(x_display, false);
1207    
1208     // Reopen full screen display
1209     XMapRaised(x_display, the_win);
1210 cebix 1.29 wait_mapped(the_win);
1211 cebix 1.1 XWarpPointer(x_display, None, rootwin, 0, 0, 0, 0, 0, 0);
1212     XGrabKeyboard(x_display, rootwin, 1, GrabModeAsync, GrabModeAsync, CurrentTime);
1213     XGrabPointer(x_display, rootwin, 1, PointerMotionMask | ButtonPressMask | ButtonReleaseMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
1214 cebix 1.15 #ifdef ENABLE_XF86_DGA
1215 cebix 1.1 XF86DGADirectVideo(x_display, screen, XF86DGADirectGraphics | XF86DGADirectKeyb | XF86DGADirectMouse);
1216     XF86DGASetViewPort(x_display, screen, 0, 0);
1217 cebix 1.7 #endif
1218 cebix 1.1 XSync(x_display, false);
1219 gbeauche 1.21
1220     // the_buffer already contains the data to restore. i.e. since a temporary
1221     // frame buffer is used when VOSF is actually used, fb_save is therefore
1222     // not necessary.
1223     #ifdef ENABLE_VOSF
1224     if (use_vosf) {
1225 cebix 1.29 LOCK_VOSF;
1226 gbeauche 1.21 PFLAG_SET_ALL;
1227 cebix 1.29 UNLOCK_VOSF;
1228 cebix 1.40 memset(the_buffer_copy, 0, VideoMonitor.mode.bytes_per_row * VideoMonitor.mode.y);
1229 gbeauche 1.21 }
1230 gbeauche 1.19 #endif
1231 gbeauche 1.21
1232     // Restore frame buffer
1233     if (fb_save) {
1234     #ifdef ENABLE_VOSF
1235     // Don't copy fb_save to the temporary frame buffer in VOSF mode
1236     if (!use_vosf)
1237 gbeauche 1.19 #endif
1238 cebix 1.40 memcpy(the_buffer, fb_save, VideoMonitor.mode.y * VideoMonitor.mode.bytes_per_row);
1239 cebix 1.1 free(fb_save);
1240     fb_save = NULL;
1241     }
1242 cebix 1.7
1243 cebix 1.15 #ifdef ENABLE_XF86_DGA
1244 cebix 1.42 if (!IsDirectMode(VideoMonitor.mode))
1245 cebix 1.1 XF86DGAInstallColormap(x_display, screen, cmap[current_dga_cmap]);
1246 cebix 1.7 #endif
1247 cebix 1.1
1248     // Unlock frame buffer (and continue MacOS thread)
1249 cebix 1.29 UNLOCK_FRAME_BUFFER;
1250 cebix 1.1 emul_suspended = false;
1251     }
1252     #endif
1253    
1254    
1255     /*
1256     * Translate key event to Mac keycode
1257     */
1258    
1259     static int kc_decode(KeySym ks)
1260     {
1261     switch (ks) {
1262     case XK_A: case XK_a: return 0x00;
1263     case XK_B: case XK_b: return 0x0b;
1264     case XK_C: case XK_c: return 0x08;
1265     case XK_D: case XK_d: return 0x02;
1266     case XK_E: case XK_e: return 0x0e;
1267     case XK_F: case XK_f: return 0x03;
1268     case XK_G: case XK_g: return 0x05;
1269     case XK_H: case XK_h: return 0x04;
1270     case XK_I: case XK_i: return 0x22;
1271     case XK_J: case XK_j: return 0x26;
1272     case XK_K: case XK_k: return 0x28;
1273     case XK_L: case XK_l: return 0x25;
1274     case XK_M: case XK_m: return 0x2e;
1275     case XK_N: case XK_n: return 0x2d;
1276     case XK_O: case XK_o: return 0x1f;
1277     case XK_P: case XK_p: return 0x23;
1278     case XK_Q: case XK_q: return 0x0c;
1279     case XK_R: case XK_r: return 0x0f;
1280     case XK_S: case XK_s: return 0x01;
1281     case XK_T: case XK_t: return 0x11;
1282     case XK_U: case XK_u: return 0x20;
1283     case XK_V: case XK_v: return 0x09;
1284     case XK_W: case XK_w: return 0x0d;
1285     case XK_X: case XK_x: return 0x07;
1286     case XK_Y: case XK_y: return 0x10;
1287     case XK_Z: case XK_z: return 0x06;
1288    
1289     case XK_1: case XK_exclam: return 0x12;
1290     case XK_2: case XK_at: return 0x13;
1291     case XK_3: case XK_numbersign: return 0x14;
1292     case XK_4: case XK_dollar: return 0x15;
1293     case XK_5: case XK_percent: return 0x17;
1294     case XK_6: return 0x16;
1295     case XK_7: return 0x1a;
1296     case XK_8: return 0x1c;
1297     case XK_9: return 0x19;
1298     case XK_0: return 0x1d;
1299    
1300     case XK_grave: case XK_asciitilde: return 0x0a;
1301     case XK_minus: case XK_underscore: return 0x1b;
1302     case XK_equal: case XK_plus: return 0x18;
1303     case XK_bracketleft: case XK_braceleft: return 0x21;
1304     case XK_bracketright: case XK_braceright: return 0x1e;
1305     case XK_backslash: case XK_bar: return 0x2a;
1306     case XK_semicolon: case XK_colon: return 0x29;
1307     case XK_apostrophe: case XK_quotedbl: return 0x27;
1308     case XK_comma: case XK_less: return 0x2b;
1309     case XK_period: case XK_greater: return 0x2f;
1310     case XK_slash: case XK_question: return 0x2c;
1311    
1312 cebix 1.15 #if defined(ENABLE_XF86_DGA) || defined(ENABLE_FBDEV_DGA)
1313 cebix 1.1 case XK_Tab: if (ctrl_down) {suspend_emul(); return -1;} else return 0x30;
1314     #else
1315     case XK_Tab: return 0x30;
1316     #endif
1317     case XK_Return: return 0x24;
1318     case XK_space: return 0x31;
1319     case XK_BackSpace: return 0x33;
1320    
1321     case XK_Delete: return 0x75;
1322     case XK_Insert: return 0x72;
1323     case XK_Home: case XK_Help: return 0x73;
1324     case XK_End: return 0x77;
1325     #ifdef __hpux
1326     case XK_Prior: return 0x74;
1327     case XK_Next: return 0x79;
1328     #else
1329     case XK_Page_Up: return 0x74;
1330     case XK_Page_Down: return 0x79;
1331     #endif
1332    
1333     case XK_Control_L: return 0x36;
1334     case XK_Control_R: return 0x36;
1335     case XK_Shift_L: return 0x38;
1336     case XK_Shift_R: return 0x38;
1337     case XK_Alt_L: return 0x37;
1338     case XK_Alt_R: return 0x37;
1339     case XK_Meta_L: return 0x3a;
1340     case XK_Meta_R: return 0x3a;
1341     case XK_Menu: return 0x32;
1342     case XK_Caps_Lock: return 0x39;
1343     case XK_Num_Lock: return 0x47;
1344    
1345     case XK_Up: return 0x3e;
1346     case XK_Down: return 0x3d;
1347     case XK_Left: return 0x3b;
1348     case XK_Right: return 0x3c;
1349    
1350     case XK_Escape: if (ctrl_down) {quit_full_screen = true; emerg_quit = true; return -1;} else return 0x35;
1351    
1352     case XK_F1: if (ctrl_down) {SysMountFirstFloppy(); return -1;} else return 0x7a;
1353     case XK_F2: return 0x78;
1354     case XK_F3: return 0x63;
1355     case XK_F4: return 0x76;
1356     case XK_F5: return 0x60;
1357     case XK_F6: return 0x61;
1358     case XK_F7: return 0x62;
1359     case XK_F8: return 0x64;
1360     case XK_F9: return 0x65;
1361     case XK_F10: return 0x6d;
1362     case XK_F11: return 0x67;
1363     case XK_F12: return 0x6f;
1364    
1365     case XK_Print: return 0x69;
1366     case XK_Scroll_Lock: return 0x6b;
1367     case XK_Pause: return 0x71;
1368    
1369     #if defined(XK_KP_Prior) && defined(XK_KP_Left) && defined(XK_KP_Insert) && defined (XK_KP_End)
1370     case XK_KP_0: case XK_KP_Insert: return 0x52;
1371     case XK_KP_1: case XK_KP_End: return 0x53;
1372     case XK_KP_2: case XK_KP_Down: return 0x54;
1373     case XK_KP_3: case XK_KP_Next: return 0x55;
1374     case XK_KP_4: case XK_KP_Left: return 0x56;
1375     case XK_KP_5: case XK_KP_Begin: return 0x57;
1376     case XK_KP_6: case XK_KP_Right: return 0x58;
1377     case XK_KP_7: case XK_KP_Home: return 0x59;
1378     case XK_KP_8: case XK_KP_Up: return 0x5b;
1379     case XK_KP_9: case XK_KP_Prior: return 0x5c;
1380     case XK_KP_Decimal: case XK_KP_Delete: return 0x41;
1381     #else
1382     case XK_KP_0: return 0x52;
1383     case XK_KP_1: return 0x53;
1384     case XK_KP_2: return 0x54;
1385     case XK_KP_3: return 0x55;
1386     case XK_KP_4: return 0x56;
1387     case XK_KP_5: return 0x57;
1388     case XK_KP_6: return 0x58;
1389     case XK_KP_7: return 0x59;
1390     case XK_KP_8: return 0x5b;
1391     case XK_KP_9: return 0x5c;
1392     case XK_KP_Decimal: return 0x41;
1393     #endif
1394     case XK_KP_Add: return 0x45;
1395     case XK_KP_Subtract: return 0x4e;
1396     case XK_KP_Multiply: return 0x43;
1397     case XK_KP_Divide: return 0x4b;
1398     case XK_KP_Enter: return 0x4c;
1399     case XK_KP_Equal: return 0x51;
1400     }
1401     return -1;
1402     }
1403    
1404 cebix 1.29 static int event2keycode(XKeyEvent &ev)
1405 cebix 1.1 {
1406     KeySym ks;
1407     int as;
1408     int i = 0;
1409    
1410     do {
1411 cebix 1.29 ks = XLookupKeysym(&ev, i++);
1412 cebix 1.1 as = kc_decode(ks);
1413     if (as != -1)
1414     return as;
1415     } while (ks != NoSymbol);
1416    
1417     return -1;
1418     }
1419    
1420    
1421     /*
1422     * X event handling
1423     */
1424    
1425     static void handle_events(void)
1426     {
1427 cebix 1.29 while (XPending(x_display)) {
1428     XEvent event;
1429     XNextEvent(x_display, &event);
1430 cebix 1.1
1431     switch (event.type) {
1432     // Mouse button
1433     case ButtonPress: {
1434 cebix 1.29 unsigned int button = event.xbutton.button;
1435 cebix 1.1 if (button < 4)
1436     ADBMouseDown(button - 1);
1437 cebix 1.10 else if (button < 6) { // Wheel mouse
1438     if (mouse_wheel_mode == 0) {
1439     int key = (button == 5) ? 0x79 : 0x74; // Page up/down
1440     ADBKeyDown(key);
1441     ADBKeyUp(key);
1442     } else {
1443     int key = (button == 5) ? 0x3d : 0x3e; // Cursor up/down
1444     for(int i=0; i<mouse_wheel_lines; i++) {
1445     ADBKeyDown(key);
1446     ADBKeyUp(key);
1447     }
1448     }
1449     }
1450 cebix 1.1 break;
1451     }
1452     case ButtonRelease: {
1453 cebix 1.29 unsigned int button = event.xbutton.button;
1454 cebix 1.1 if (button < 4)
1455     ADBMouseUp(button - 1);
1456     break;
1457     }
1458    
1459     // Mouse moved
1460     case EnterNotify:
1461     case MotionNotify:
1462 cebix 1.29 ADBMouseMoved(event.xmotion.x, event.xmotion.y);
1463 cebix 1.1 break;
1464    
1465     // Keyboard
1466     case KeyPress: {
1467     int code;
1468     if (use_keycodes) {
1469 cebix 1.29 event2keycode(event.xkey); // This is called to process the hotkeys
1470     code = keycode_table[event.xkey.keycode & 0xff];
1471 cebix 1.1 } else
1472 cebix 1.29 code = event2keycode(event.xkey);
1473 cebix 1.1 if (code != -1) {
1474     if (!emul_suspended) {
1475 cebix 1.3 if (code == 0x39) { // Caps Lock pressed
1476     if (caps_on) {
1477     ADBKeyUp(code);
1478     caps_on = false;
1479     } else {
1480     ADBKeyDown(code);
1481     caps_on = true;
1482     }
1483     } else
1484     ADBKeyDown(code);
1485 cebix 1.1 if (code == 0x36)
1486     ctrl_down = true;
1487     } else {
1488 cebix 1.15 #if defined(ENABLE_XF86_DGA) || defined(ENABLE_FBDEV_DGA)
1489 cebix 1.1 if (code == 0x31)
1490     resume_emul(); // Space wakes us up
1491     #endif
1492     }
1493     }
1494     break;
1495     }
1496     case KeyRelease: {
1497     int code;
1498     if (use_keycodes) {
1499 cebix 1.29 event2keycode(event.xkey); // This is called to process the hotkeys
1500     code = keycode_table[event.xkey.keycode & 0xff];
1501 cebix 1.1 } else
1502 cebix 1.29 code = event2keycode(event.xkey);
1503 cebix 1.3 if (code != -1 && code != 0x39) { // Don't propagate Caps Lock releases
1504 cebix 1.1 ADBKeyUp(code);
1505     if (code == 0x36)
1506     ctrl_down = false;
1507     }
1508     break;
1509     }
1510    
1511     // Hidden parts exposed, force complete refresh of window
1512     case Expose:
1513 cebix 1.13 if (display_type == DISPLAY_WINDOW) {
1514 gbeauche 1.19 #ifdef ENABLE_VOSF
1515 gbeauche 1.21 if (use_vosf) { // VOSF refresh
1516 cebix 1.29 LOCK_VOSF;
1517 gbeauche 1.20 PFLAG_SET_ALL;
1518 cebix 1.29 UNLOCK_VOSF;
1519 cebix 1.40 memset(the_buffer_copy, 0, VideoMonitor.mode.bytes_per_row * VideoMonitor.mode.y);
1520 gbeauche 1.21 }
1521     else
1522 gbeauche 1.20 #endif
1523 gbeauche 1.21 if (frame_skip == 0) { // Dynamic refresh
1524     int x1, y1;
1525     for (y1=0; y1<16; y1++)
1526     for (x1=0; x1<16; x1++)
1527     updt_box[x1][y1] = true;
1528     nr_boxes = 16 * 16;
1529     } else // Static refresh
1530 cebix 1.40 memset(the_buffer_copy, 0, VideoMonitor.mode.bytes_per_row * VideoMonitor.mode.y);
1531 cebix 1.13 }
1532 cebix 1.26 break;
1533    
1534 cebix 1.29 // Window "close" widget clicked
1535     case ClientMessage:
1536 cebix 1.30 if (event.xclient.format == 32 && event.xclient.data.l[0] == WM_DELETE_WINDOW) {
1537     ADBKeyDown(0x7f); // Power key
1538     ADBKeyUp(0x7f);
1539     }
1540 cebix 1.1 break;
1541     }
1542     }
1543     }
1544    
1545    
1546     /*
1547     * Window display update
1548     */
1549    
1550 cebix 1.16 // Dynamic display update (variable frame rate for each box)
1551     static void update_display_dynamic(int ticker)
1552 cebix 1.1 {
1553 cebix 1.17 int y1, y2, y2s, y2a, i, x1, xm, xmo, ymo, yo, yi, yil, xi;
1554 cebix 1.13 int xil = 0;
1555     int rxm = 0, rxmo = 0;
1556 cebix 1.40 int bytes_per_row = VideoMonitor.mode.bytes_per_row;
1557     int bytes_per_pixel = VideoMonitor.mode.bytes_per_row / VideoMonitor.mode.x;
1558     int rx = VideoMonitor.mode.bytes_per_row / 16;
1559     int ry = VideoMonitor.mode.y / 16;
1560 cebix 1.13 int max_box;
1561    
1562     y2s = sm_uptd[ticker % 8];
1563     y2a = 8;
1564     for (i = 0; i < 6; i++)
1565     if (ticker % (2 << i))
1566 cebix 1.1 break;
1567 cebix 1.13 max_box = sm_no_boxes[i];
1568 cebix 1.1
1569 cebix 1.13 if (y2a) {
1570     for (y1=0; y1<16; y1++) {
1571     for (y2=y2s; y2 < ry; y2 += y2a) {
1572     i = ((y1 * ry) + y2) * bytes_per_row;
1573     for (x1=0; x1<16; x1++, i += rx) {
1574     if (updt_box[x1][y1] == false) {
1575     if (memcmp(&the_buffer_copy[i], &the_buffer[i], rx)) {
1576     updt_box[x1][y1] = true;
1577     nr_boxes++;
1578     }
1579 cebix 1.1 }
1580     }
1581     }
1582 cebix 1.13 }
1583     }
1584 cebix 1.1
1585 cebix 1.13 if ((nr_boxes <= max_box) && (nr_boxes)) {
1586     for (y1=0; y1<16; y1++) {
1587     for (x1=0; x1<16; x1++) {
1588     if (updt_box[x1][y1] == true) {
1589     if (rxm == 0)
1590     xm = x1;
1591     rxm += rx;
1592     updt_box[x1][y1] = false;
1593 cebix 1.1 }
1594 cebix 1.13 if (((updt_box[x1+1][y1] == false) || (x1 == 15)) && (rxm)) {
1595     if ((rxmo != rxm) || (xmo != xm) || (yo != y1 - 1)) {
1596     if (rxmo) {
1597     xi = xmo * rx;
1598     yi = ymo * ry;
1599     xil = rxmo;
1600     yil = (yo - ymo +1) * ry;
1601     }
1602     rxmo = rxm;
1603     xmo = xm;
1604     ymo = y1;
1605 cebix 1.1 }
1606 cebix 1.13 rxm = 0;
1607     yo = y1;
1608     }
1609     if (xil) {
1610     i = (yi * bytes_per_row) + xi;
1611     for (y2=0; y2 < yil; y2++, i += bytes_per_row)
1612     memcpy(&the_buffer_copy[i], &the_buffer[i], xil);
1613 cebix 1.42 if (VideoMonitor.mode.depth == VDEPTH_1BIT) {
1614 cebix 1.13 if (have_shm)
1615     XShmPutImage(x_display, the_win, the_gc, img, xi * 8, yi, xi * 8, yi, xil * 8, yil, 0);
1616     else
1617     XPutImage(x_display, the_win, the_gc, img, xi * 8, yi, xi * 8, yi, xil * 8, yil);
1618     } else {
1619     if (have_shm)
1620     XShmPutImage(x_display, the_win, the_gc, img, xi / bytes_per_pixel, yi, xi / bytes_per_pixel, yi, xil / bytes_per_pixel, yil, 0);
1621     else
1622     XPutImage(x_display, the_win, the_gc, img, xi / bytes_per_pixel, yi, xi / bytes_per_pixel, yi, xil / bytes_per_pixel, yil);
1623 cebix 1.1 }
1624 cebix 1.13 xil = 0;
1625 cebix 1.1 }
1626 cebix 1.13 if ((x1 == 15) && (y1 == 15) && (rxmo)) {
1627     x1--;
1628     xi = xmo * rx;
1629     yi = ymo * ry;
1630     xil = rxmo;
1631     yil = (yo - ymo +1) * ry;
1632     rxmo = 0;
1633 cebix 1.1 }
1634     }
1635     }
1636 cebix 1.13 nr_boxes = 0;
1637 cebix 1.1 }
1638     }
1639    
1640 cebix 1.16 // Static display update (fixed frame rate, but incremental)
1641     static void update_display_static(void)
1642     {
1643     // Incremental update code
1644     int wide = 0, high = 0, x1, x2, y1, y2, i, j;
1645 cebix 1.40 int bytes_per_row = VideoMonitor.mode.bytes_per_row;
1646     int bytes_per_pixel = VideoMonitor.mode.bytes_per_row / VideoMonitor.mode.x;
1647 cebix 1.16 uint8 *p, *p2;
1648    
1649     // Check for first line from top and first line from bottom that have changed
1650     y1 = 0;
1651 cebix 1.40 for (j=0; j<VideoMonitor.mode.y; j++) {
1652 cebix 1.16 if (memcmp(&the_buffer[j * bytes_per_row], &the_buffer_copy[j * bytes_per_row], bytes_per_row)) {
1653     y1 = j;
1654     break;
1655     }
1656     }
1657     y2 = y1 - 1;
1658 cebix 1.40 for (j=VideoMonitor.mode.y-1; j>=y1; j--) {
1659 cebix 1.16 if (memcmp(&the_buffer[j * bytes_per_row], &the_buffer_copy[j * bytes_per_row], bytes_per_row)) {
1660     y2 = j;
1661     break;
1662     }
1663     }
1664     high = y2 - y1 + 1;
1665    
1666     // Check for first column from left and first column from right that have changed
1667     if (high) {
1668 cebix 1.42 if (VideoMonitor.mode.depth == VDEPTH_1BIT) {
1669 cebix 1.40 x1 = VideoMonitor.mode.x - 1;
1670 cebix 1.16 for (j=y1; j<=y2; j++) {
1671     p = &the_buffer[j * bytes_per_row];
1672     p2 = &the_buffer_copy[j * bytes_per_row];
1673     for (i=0; i<(x1>>3); i++) {
1674     if (*p != *p2) {
1675     x1 = i << 3;
1676     break;
1677     }
1678 cebix 1.18 p++; p2++;
1679 cebix 1.16 }
1680     }
1681     x2 = x1;
1682     for (j=y1; j<=y2; j++) {
1683     p = &the_buffer[j * bytes_per_row];
1684     p2 = &the_buffer_copy[j * bytes_per_row];
1685     p += bytes_per_row;
1686     p2 += bytes_per_row;
1687 cebix 1.40 for (i=(VideoMonitor.mode.x>>3); i>(x2>>3); i--) {
1688 cebix 1.18 p--; p2--;
1689 cebix 1.16 if (*p != *p2) {
1690 cebix 1.25 x2 = (i << 3) + 7;
1691 cebix 1.16 break;
1692     }
1693     }
1694     }
1695 cebix 1.25 wide = x2 - x1 + 1;
1696 cebix 1.16
1697     // Update copy of the_buffer
1698     if (high && wide) {
1699     for (j=y1; j<=y2; j++) {
1700     i = j * bytes_per_row + (x1 >> 3);
1701     memcpy(the_buffer_copy + i, the_buffer + i, wide >> 3);
1702     }
1703     }
1704    
1705     } else {
1706 cebix 1.40 x1 = VideoMonitor.mode.x;
1707 cebix 1.16 for (j=y1; j<=y2; j++) {
1708     p = &the_buffer[j * bytes_per_row];
1709     p2 = &the_buffer_copy[j * bytes_per_row];
1710 cebix 1.18 for (i=0; i<x1*bytes_per_pixel; i++) {
1711     if (*p != *p2) {
1712     x1 = i / bytes_per_pixel;
1713 cebix 1.16 break;
1714     }
1715 cebix 1.18 p++; p2++;
1716 cebix 1.16 }
1717     }
1718     x2 = x1;
1719     for (j=y1; j<=y2; j++) {
1720     p = &the_buffer[j * bytes_per_row];
1721     p2 = &the_buffer_copy[j * bytes_per_row];
1722     p += bytes_per_row;
1723     p2 += bytes_per_row;
1724 cebix 1.40 for (i=VideoMonitor.mode.x*bytes_per_pixel; i>x2*bytes_per_pixel; i--) {
1725 cebix 1.18 p--;
1726     p2--;
1727     if (*p != *p2) {
1728     x2 = i / bytes_per_pixel;
1729 cebix 1.16 break;
1730     }
1731     }
1732     }
1733     wide = x2 - x1;
1734    
1735     // Update copy of the_buffer
1736     if (high && wide) {
1737     for (j=y1; j<=y2; j++) {
1738     i = j * bytes_per_row + x1 * bytes_per_pixel;
1739     memcpy(the_buffer_copy + i, the_buffer + i, bytes_per_pixel * wide);
1740     }
1741     }
1742     }
1743     }
1744    
1745     // Refresh display
1746     if (high && wide) {
1747     if (have_shm)
1748     XShmPutImage(x_display, the_win, the_gc, img, x1, y1, x1, y1, wide, high, 0);
1749     else
1750     XPutImage(x_display, the_win, the_gc, img, x1, y1, x1, y1, wide, high);
1751     }
1752     }
1753    
1754 cebix 1.1
1755     /*
1756 gbeauche 1.19 * Screen refresh functions
1757     */
1758    
1759 gbeauche 1.34 // We suggest the compiler to inline the next two functions so that it
1760     // may specialise the code according to the current screen depth and
1761 gbeauche 1.35 // display type. A clever compiler would do that job by itself though...
1762 gbeauche 1.34
1763     // NOTE: update_display_vosf is inlined too
1764 gbeauche 1.19
1765     static inline void possibly_quit_dga_mode()
1766     {
1767     #if defined(ENABLE_XF86_DGA) || defined(ENABLE_FBDEV_DGA)
1768     // Quit DGA mode if requested
1769     if (quit_full_screen) {
1770     quit_full_screen = false;
1771     #ifdef ENABLE_XF86_DGA
1772     XF86DGADirectVideo(x_display, screen, 0);
1773     #endif
1774     XUngrabPointer(x_display, CurrentTime);
1775     XUngrabKeyboard(x_display, CurrentTime);
1776     XUnmapWindow(x_display, the_win);
1777 cebix 1.42 wait_unmapped(the_win);
1778 gbeauche 1.19 }
1779     #endif
1780     }
1781    
1782 cebix 1.42 static inline void handle_palette_changes(int display_type)
1783 gbeauche 1.19 {
1784 cebix 1.29 LOCK_PALETTE;
1785    
1786 gbeauche 1.19 if (palette_changed) {
1787     palette_changed = false;
1788 cebix 1.42 if (!IsDirectMode(VideoMonitor.mode)) {
1789 gbeauche 1.19 XStoreColors(x_display, cmap[0], palette, 256);
1790     XStoreColors(x_display, cmap[1], palette, 256);
1791 cebix 1.29 XSync(x_display, false);
1792 gbeauche 1.19
1793     #ifdef ENABLE_XF86_DGA
1794     if (display_type == DISPLAY_DGA) {
1795     current_dga_cmap ^= 1;
1796     XF86DGAInstallColormap(x_display, screen, cmap[current_dga_cmap]);
1797     }
1798     #endif
1799     }
1800     }
1801 cebix 1.29
1802     UNLOCK_PALETTE;
1803 gbeauche 1.19 }
1804    
1805     static void video_refresh_dga(void)
1806     {
1807     // Quit DGA mode if requested
1808     possibly_quit_dga_mode();
1809    
1810     // Handle X events
1811     handle_events();
1812    
1813     // Handle palette changes
1814 cebix 1.42 handle_palette_changes(DISPLAY_DGA);
1815 gbeauche 1.19 }
1816    
1817 cebix 1.23 #ifdef ENABLE_VOSF
1818 gbeauche 1.19 #if REAL_ADDRESSING || DIRECT_ADDRESSING
1819     static void video_refresh_dga_vosf(void)
1820     {
1821     // Quit DGA mode if requested
1822     possibly_quit_dga_mode();
1823    
1824     // Handle X events
1825     handle_events();
1826    
1827     // Handle palette changes
1828 cebix 1.42 handle_palette_changes(DISPLAY_DGA);
1829 gbeauche 1.19
1830     // Update display (VOSF variant)
1831     static int tick_counter = 0;
1832     if (++tick_counter >= frame_skip) {
1833     tick_counter = 0;
1834 gbeauche 1.36 if (mainBuffer.dirty) {
1835     LOCK_VOSF;
1836     update_display_dga_vosf();
1837     UNLOCK_VOSF;
1838     }
1839 gbeauche 1.19 }
1840     }
1841     #endif
1842    
1843     static void video_refresh_window_vosf(void)
1844     {
1845     // Quit DGA mode if requested
1846     possibly_quit_dga_mode();
1847    
1848     // Handle X events
1849     handle_events();
1850    
1851     // Handle palette changes
1852 cebix 1.42 handle_palette_changes(DISPLAY_WINDOW);
1853 gbeauche 1.19
1854     // Update display (VOSF variant)
1855     static int tick_counter = 0;
1856     if (++tick_counter >= frame_skip) {
1857     tick_counter = 0;
1858 gbeauche 1.36 if (mainBuffer.dirty) {
1859     LOCK_VOSF;
1860     update_display_window_vosf();
1861     UNLOCK_VOSF;
1862 cebix 1.37 XSync(x_display, false); // Let the server catch up
1863 gbeauche 1.36 }
1864 gbeauche 1.19 }
1865     }
1866 cebix 1.23 #endif // def ENABLE_VOSF
1867 gbeauche 1.19
1868     static void video_refresh_window_static(void)
1869     {
1870     // Handle X events
1871     handle_events();
1872    
1873     // Handle_palette changes
1874 cebix 1.42 handle_palette_changes(DISPLAY_WINDOW);
1875 gbeauche 1.19
1876     // Update display (static variant)
1877     static int tick_counter = 0;
1878     if (++tick_counter >= frame_skip) {
1879     tick_counter = 0;
1880     update_display_static();
1881     }
1882     }
1883    
1884     static void video_refresh_window_dynamic(void)
1885     {
1886     // Handle X events
1887     handle_events();
1888    
1889     // Handle_palette changes
1890 cebix 1.42 handle_palette_changes(DISPLAY_WINDOW);
1891 gbeauche 1.19
1892     // Update display (dynamic variant)
1893     static int tick_counter = 0;
1894     tick_counter++;
1895     update_display_dynamic(tick_counter);
1896     }
1897    
1898    
1899     /*
1900 cebix 1.1 * Thread for screen refresh, input handling etc.
1901     */
1902    
1903 cebix 1.42 static void VideoRefreshInit(void)
1904 gbeauche 1.19 {
1905     // TODO: set up specialised 8bpp VideoRefresh handlers ?
1906     if (display_type == DISPLAY_DGA) {
1907 cebix 1.23 #if ENABLE_VOSF && (REAL_ADDRESSING || DIRECT_ADDRESSING)
1908 gbeauche 1.19 if (use_vosf)
1909     video_refresh = video_refresh_dga_vosf;
1910     else
1911     #endif
1912     video_refresh = video_refresh_dga;
1913     }
1914     else {
1915     #ifdef ENABLE_VOSF
1916     if (use_vosf)
1917     video_refresh = video_refresh_window_vosf;
1918     else
1919     #endif
1920     if (frame_skip == 0)
1921     video_refresh = video_refresh_window_dynamic;
1922     else
1923     video_refresh = video_refresh_window_static;
1924     }
1925     }
1926    
1927     void VideoRefresh(void)
1928     {
1929     // TODO: make main_unix/VideoRefresh call directly video_refresh() ?
1930     video_refresh();
1931     }
1932    
1933 cebix 1.15 #ifdef HAVE_PTHREADS
1934     static void *redraw_func(void *arg)
1935     {
1936 gbeauche 1.19 uint64 start = GetTicks_usec();
1937     int64 ticks = 0;
1938 cebix 1.18 uint64 next = GetTicks_usec();
1939 cebix 1.15 while (!redraw_thread_cancel) {
1940 gbeauche 1.19 video_refresh();
1941 cebix 1.18 next += 16667;
1942     int64 delay = next - GetTicks_usec();
1943     if (delay > 0)
1944     Delay_usec(delay);
1945     else if (delay < -16667)
1946     next = GetTicks_usec();
1947 gbeauche 1.19 ticks++;
1948 cebix 1.1 }
1949 gbeauche 1.19 uint64 end = GetTicks_usec();
1950 cebix 1.37 // printf("%Ld ticks in %Ld usec = %Ld ticks/sec\n", ticks, end - start, ticks * 1000000 / (end - start));
1951 cebix 1.1 return NULL;
1952     }
1953 cebix 1.15 #endif