ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/video_x.cpp
Revision: 1.80
Committed: 2005-06-11T06:43:24Z (19 years, 5 months ago) by gbeauche
Branch: MAIN
Changes since 1.79: +3 -3 lines
Log Message:
Much improved responsiveness on NetBSD systems.

On those systems, it's really hard to get high resolution timings and the
system oftens fails to honour a timeout in less than 20 ms. The idea here
is to have an average m68k instruction count (countdown quantum) that
triggers real interrupt checks. The quantum is calibrated every 10 ticks
and has a 1000 Hz resolution on average.

File Contents

# User Rev Content
1 cebix 1.1 /*
2     * video_x.cpp - Video/graphics emulation, X11 specific stuff
3     *
4 gbeauche 1.76 * Basilisk II (C) 1997-2005 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 cebix 1.51 * Ctrl-F5 = grab mouse (in windowed mode)
28 cebix 1.1 */
29    
30     #include "sysdeps.h"
31    
32     #include <X11/Xlib.h>
33     #include <X11/Xutil.h>
34     #include <X11/keysym.h>
35     #include <X11/extensions/XShm.h>
36     #include <sys/ipc.h>
37     #include <sys/shm.h>
38     #include <errno.h>
39    
40 cebix 1.53 #include <algorithm>
41    
42 cebix 1.15 #ifdef HAVE_PTHREADS
43     # include <pthread.h>
44     #endif
45    
46     #ifdef ENABLE_XF86_DGA
47     # include <X11/extensions/xf86dga.h>
48     #endif
49    
50     #ifdef ENABLE_XF86_VIDMODE
51     # include <X11/extensions/xf86vmode.h>
52     #endif
53    
54     #ifdef ENABLE_FBDEV_DGA
55     # include <sys/mman.h>
56     #endif
57    
58 cebix 1.1 #include "cpu_emulation.h"
59     #include "main.h"
60     #include "adb.h"
61     #include "macos_util.h"
62     #include "prefs.h"
63     #include "user_strings.h"
64     #include "video.h"
65 gbeauche 1.71 #include "video_blit.h"
66 cebix 1.1
67 cebix 1.55 #define DEBUG 0
68 cebix 1.1 #include "debug.h"
69    
70    
71 cebix 1.66 // Supported video modes
72     static vector<video_mode> VideoModes;
73    
74 cebix 1.1 // Display types
75     enum {
76     DISPLAY_WINDOW, // X11 window, using MIT SHM extensions if possible
77     DISPLAY_DGA // DGA fullscreen display
78     };
79    
80     // Constants
81 cebix 1.4 const char KEYCODE_FILE_NAME[] = DATADIR "/keycodes";
82 cebix 1.44
83 cebix 1.57 static const int win_eventmask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | EnterWindowMask | ExposureMask | StructureNotifyMask;
84 cebix 1.44 static const int dga_eventmask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | StructureNotifyMask;
85 cebix 1.1
86    
87     // Global variables
88 cebix 1.10 static int32 frame_skip; // Prefs items
89 cebix 1.44 static int16 mouse_wheel_mode;
90     static int16 mouse_wheel_lines;
91 cebix 1.10
92 cebix 1.1 static int display_type = DISPLAY_WINDOW; // See enum above
93 cebix 1.16 static bool local_X11; // Flag: X server running on local machine?
94 cebix 1.44 static uint8 *the_buffer = NULL; // Mac frame buffer (where MacOS draws into)
95     static uint8 *the_buffer_copy = NULL; // Copy of Mac frame buffer (for refreshed modes)
96 gbeauche 1.56 static uint32 the_buffer_size; // Size of allocated the_buffer
97 cebix 1.15
98 cebix 1.45 static bool redraw_thread_active = false; // Flag: Redraw thread installed
99 cebix 1.15 #ifdef HAVE_PTHREADS
100 cebix 1.64 static pthread_attr_t redraw_thread_attr; // Redraw thread attributes
101 cebix 1.42 static volatile bool redraw_thread_cancel; // Flag: Cancel Redraw thread
102 gbeauche 1.77 static volatile bool redraw_thread_cancel_ack; // Flag: Acknowledge for redraw thread cancellation
103 cebix 1.1 static pthread_t redraw_thread; // Redraw thread
104 cebix 1.15 #endif
105 cebix 1.1
106     static bool has_dga = false; // Flag: Video DGA capable
107 cebix 1.12 static bool has_vidmode = false; // Flag: VidMode extension available
108 cebix 1.1
109 cebix 1.44 #ifdef ENABLE_VOSF
110     static bool use_vosf = true; // Flag: VOSF enabled
111     #else
112     static const bool use_vosf = false; // VOSF not possible
113     #endif
114    
115 cebix 1.1 static bool ctrl_down = false; // Flag: Ctrl key pressed
116 cebix 1.3 static bool caps_on = false; // Flag: Caps Lock on
117 cebix 1.1 static bool quit_full_screen = false; // Flag: DGA close requested from redraw thread
118     static bool emerg_quit = false; // Flag: Ctrl-Esc pressed, emergency quit requested from MacOS thread
119     static bool emul_suspended = false; // Flag: Emulator suspended
120    
121     static bool classic_mode = false; // Flag: Classic Mac video mode
122    
123     static bool use_keycodes = false; // Flag: Use keycodes rather than keysyms
124     static int keycode_table[256]; // X keycode -> Mac keycode translation table
125    
126     // X11 variables
127 gbeauche 1.71 char *x_display_name = NULL; // X11 display name
128     Display *x_display = NULL; // X11 display handle
129 cebix 1.1 static int screen; // Screen number
130 cebix 1.44 static Window rootwin; // Root window and our window
131 cebix 1.53 static int num_depths = 0; // Number of available X depths
132     static int *avail_depths = NULL; // List of available X depths
133 cebix 1.1 static XColor black, white;
134     static unsigned long black_pixel, white_pixel;
135     static int eventmask;
136    
137 cebix 1.53 static int xdepth; // Depth of X screen
138 gbeauche 1.71 static VisualFormat visualFormat;
139 cebix 1.53 static XVisualInfo visualInfo;
140     static Visual *vis;
141     static int color_class;
142    
143 cebix 1.48 static int rshift, rloss, gshift, gloss, bshift, bloss; // Pixel format of DirectColor/TrueColor modes
144    
145 cebix 1.53 static Colormap cmap[2] = {0, 0}; // Colormaps for indexed modes (DGA needs two of them)
146    
147 cebix 1.66 static XColor x_palette[256]; // Color palette to be used as CLUT and gamma table
148     static bool x_palette_changed = false; // Flag: Palette changed, redraw thread must set new colors
149 cebix 1.44
150     #ifdef ENABLE_FBDEV_DGA
151     static int fbdev_fd = -1;
152     #endif
153    
154     #ifdef ENABLE_XF86_VIDMODE
155     static XF86VidModeModeInfo **x_video_modes = NULL; // Array of all available modes
156     static int num_x_video_modes;
157     #endif
158    
159     // Mutex to protect palette
160 cebix 1.15 #ifdef HAVE_PTHREADS
161 cebix 1.66 static pthread_mutex_t x_palette_lock = PTHREAD_MUTEX_INITIALIZER;
162     #define LOCK_PALETTE pthread_mutex_lock(&x_palette_lock)
163     #define UNLOCK_PALETTE pthread_mutex_unlock(&x_palette_lock)
164 cebix 1.29 #else
165     #define LOCK_PALETTE
166     #define UNLOCK_PALETTE
167 cebix 1.15 #endif
168 cebix 1.1
169 cebix 1.44 // Mutex to protect frame buffer
170 cebix 1.15 #ifdef HAVE_PTHREADS
171 cebix 1.44 static pthread_mutex_t frame_buffer_lock = PTHREAD_MUTEX_INITIALIZER;
172 cebix 1.29 #define LOCK_FRAME_BUFFER pthread_mutex_lock(&frame_buffer_lock);
173     #define UNLOCK_FRAME_BUFFER pthread_mutex_unlock(&frame_buffer_lock);
174     #else
175     #define LOCK_FRAME_BUFFER
176     #define UNLOCK_FRAME_BUFFER
177 cebix 1.15 #endif
178 cebix 1.1
179 cebix 1.44 // Variables for non-VOSF incremental refresh
180     static const int sm_uptd[] = {4,1,6,3,0,5,2,7};
181     static int sm_no_boxes[] = {1,8,32,64,128,300};
182     static bool updt_box[17][17];
183     static int nr_boxes;
184 cebix 1.12
185 cebix 1.44 // Video refresh function
186 cebix 1.42 static void VideoRefreshInit(void);
187 gbeauche 1.19 static void (*video_refresh)(void);
188 cebix 1.1
189 cebix 1.44
190 cebix 1.1 // Prototypes
191     static void *redraw_func(void *arg);
192    
193     // From main_unix.cpp
194 cebix 1.16 extern char *x_display_name;
195 cebix 1.1 extern Display *x_display;
196 gbeauche 1.72 extern void *vm_acquire_mac(size_t size);
197 cebix 1.1
198     // From sys_unix.cpp
199     extern void SysMountFirstFloppy(void);
200    
201 gbeauche 1.73 // From clip_unix.cpp
202     extern void ClipboardSelectionClear(XSelectionClearEvent *);
203     extern void ClipboardSelectionRequest(XSelectionRequestEvent *);
204    
205 cebix 1.42
206 cebix 1.1 /*
207 cebix 1.66 * monitor_desc subclass for X11 display
208     */
209    
210     class X11_monitor_desc : public monitor_desc {
211     public:
212     X11_monitor_desc(const vector<video_mode> &available_modes, video_depth default_depth, uint32 default_id) : monitor_desc(available_modes, default_depth, default_id) {}
213     ~X11_monitor_desc() {}
214    
215     virtual void switch_to_current_mode(void);
216     virtual void set_palette(uint8 *pal, int num);
217    
218     bool video_open(void);
219     void video_close(void);
220     };
221    
222    
223     /*
224 cebix 1.44 * Utility functions
225 cebix 1.1 */
226    
227 gbeauche 1.69 // Map video_mode depth ID to numerical depth value
228     static inline int depth_of_video_mode(video_mode const & mode)
229     {
230     int depth = -1;
231     switch (mode.depth) {
232     case VDEPTH_1BIT:
233     depth = 1;
234     break;
235     case VDEPTH_2BIT:
236     depth = 2;
237     break;
238     case VDEPTH_4BIT:
239     depth = 4;
240     break;
241     case VDEPTH_8BIT:
242     depth = 8;
243     break;
244     case VDEPTH_16BIT:
245     depth = 16;
246     break;
247     case VDEPTH_32BIT:
248     depth = 32;
249     break;
250     default:
251     abort();
252     }
253     return depth;
254     }
255    
256 cebix 1.49 // Map RGB color to pixel value (this only works in TrueColor/DirectColor visuals)
257     static inline uint32 map_rgb(uint8 red, uint8 green, uint8 blue)
258     {
259     return ((red >> rloss) << rshift) | ((green >> gloss) << gshift) | ((blue >> bloss) << bshift);
260     }
261    
262 cebix 1.53 // Do we have a visual for handling the specified Mac depth? If so, set the
263     // global variables "xdepth", "visualInfo", "vis" and "color_class".
264     static bool find_visual_for_depth(video_depth depth)
265     {
266     D(bug("have_visual_for_depth(%d)\n", 1 << depth));
267    
268 cebix 1.61 // 1-bit works always and uses default visual
269     if (depth == VDEPTH_1BIT) {
270     vis = DefaultVisual(x_display, screen);
271     visualInfo.visualid = XVisualIDFromVisual(vis);
272     int num = 0;
273     XVisualInfo *vi = XGetVisualInfo(x_display, VisualIDMask, &visualInfo, &num);
274     visualInfo = vi[0];
275     XFree(vi);
276     xdepth = visualInfo.depth;
277     color_class = visualInfo.c_class;
278     D(bug(" found visual ID 0x%02x, depth %d\n", visualInfo.visualid, xdepth));
279     return true;
280     }
281    
282 cebix 1.53 // Calculate minimum and maximum supported X depth
283     int min_depth = 1, max_depth = 32;
284     switch (depth) {
285     #ifdef ENABLE_VOSF
286     case VDEPTH_2BIT:
287 cebix 1.54 case VDEPTH_4BIT: // VOSF blitters can convert 2/4/8-bit -> 8/16/32-bit
288     case VDEPTH_8BIT:
289 cebix 1.53 min_depth = 8;
290     max_depth = 32;
291     break;
292     #else
293     case VDEPTH_2BIT:
294     case VDEPTH_4BIT: // 2/4-bit requires VOSF blitters
295     return false;
296     case VDEPTH_8BIT: // 8-bit without VOSF requires an 8-bit visual
297     min_depth = 8;
298     max_depth = 8;
299     break;
300     #endif
301     case VDEPTH_16BIT: // 16-bit requires a 15/16-bit visual
302     min_depth = 15;
303     max_depth = 16;
304     break;
305     case VDEPTH_32BIT: // 32-bit requires a 24/32-bit visual
306     min_depth = 24;
307     max_depth = 32;
308     break;
309     }
310     D(bug(" minimum required X depth is %d, maximum supported X depth is %d\n", min_depth, max_depth));
311    
312     // Try to find a visual for one of the color depths
313     bool visual_found = false;
314     for (int i=0; i<num_depths && !visual_found; i++) {
315    
316     xdepth = avail_depths[i];
317     D(bug(" trying to find visual for depth %d\n", xdepth));
318     if (xdepth < min_depth || xdepth > max_depth)
319     continue;
320    
321     // Determine best color class for this depth
322     switch (xdepth) {
323     case 1: // Try StaticGray or StaticColor
324     if (XMatchVisualInfo(x_display, screen, xdepth, StaticGray, &visualInfo)
325     || XMatchVisualInfo(x_display, screen, xdepth, StaticColor, &visualInfo))
326     visual_found = true;
327     break;
328     case 8: // Need PseudoColor
329     if (XMatchVisualInfo(x_display, screen, xdepth, PseudoColor, &visualInfo))
330     visual_found = true;
331     break;
332     case 15:
333     case 16:
334     case 24:
335     case 32: // Try DirectColor first, as this will allow gamma correction
336     if (XMatchVisualInfo(x_display, screen, xdepth, DirectColor, &visualInfo)
337     || XMatchVisualInfo(x_display, screen, xdepth, TrueColor, &visualInfo))
338     visual_found = true;
339     break;
340     default:
341     D(bug(" not a supported depth\n"));
342     break;
343     }
344     }
345     if (!visual_found)
346     return false;
347    
348     // Visual was found
349     vis = visualInfo.visual;
350     color_class = visualInfo.c_class;
351     D(bug(" found visual ID 0x%02x, depth %d, class ", visualInfo.visualid, xdepth));
352     #if DEBUG
353     switch (color_class) {
354     case StaticGray: D(bug("StaticGray\n")); break;
355     case GrayScale: D(bug("GrayScale\n")); break;
356     case StaticColor: D(bug("StaticColor\n")); break;
357     case PseudoColor: D(bug("PseudoColor\n")); break;
358     case TrueColor: D(bug("TrueColor\n")); break;
359     case DirectColor: D(bug("DirectColor\n")); break;
360     }
361     #endif
362 cebix 1.59 return true;
363 cebix 1.53 }
364    
365 cebix 1.42 // Add mode to list of supported modes
366     static void add_mode(uint32 width, uint32 height, uint32 resolution_id, uint32 bytes_per_row, video_depth depth)
367 cebix 1.1 {
368 cebix 1.40 video_mode mode;
369 cebix 1.42 mode.x = width;
370     mode.y = height;
371     mode.resolution_id = resolution_id;
372     mode.bytes_per_row = bytes_per_row;
373     mode.depth = depth;
374     VideoModes.push_back(mode);
375     }
376    
377 cebix 1.50 // Add standard list of windowed modes for given color depth
378     static void add_window_modes(video_depth depth)
379     {
380     add_mode(512, 384, 0x80, TrivialBytesPerRow(512, depth), depth);
381     add_mode(640, 480, 0x81, TrivialBytesPerRow(640, depth), depth);
382     add_mode(800, 600, 0x82, TrivialBytesPerRow(800, depth), depth);
383     add_mode(1024, 768, 0x83, TrivialBytesPerRow(1024, depth), depth);
384     add_mode(1152, 870, 0x84, TrivialBytesPerRow(1152, depth), depth);
385     add_mode(1280, 1024, 0x85, TrivialBytesPerRow(1280, depth), depth);
386     add_mode(1600, 1200, 0x86, TrivialBytesPerRow(1600, depth), depth);
387     }
388    
389 cebix 1.42 // Set Mac frame layout and base address (uses the_buffer/MacFrameBaseMac)
390 cebix 1.66 static void set_mac_frame_buffer(X11_monitor_desc &monitor, video_depth depth, bool native_byte_order)
391 cebix 1.42 {
392 gbeauche 1.19 #if !REAL_ADDRESSING && !DIRECT_ADDRESSING
393 cebix 1.5 int layout = FLAYOUT_DIRECT;
394 cebix 1.42 if (depth == VDEPTH_16BIT)
395     layout = (xdepth == 15) ? FLAYOUT_HOST_555 : FLAYOUT_HOST_565;
396     else if (depth == VDEPTH_32BIT)
397 cebix 1.48 layout = (xdepth == 24) ? FLAYOUT_HOST_888 : FLAYOUT_DIRECT;
398 cebix 1.15 if (native_byte_order)
399     MacFrameLayout = layout;
400     else
401     MacFrameLayout = FLAYOUT_DIRECT;
402 cebix 1.66 monitor.set_mac_frame_base(MacFrameBaseMac);
403 cebix 1.44
404     // Set variables used by UAE memory banking
405 cebix 1.66 const video_mode &mode = monitor.get_current_mode();
406 cebix 1.44 MacFrameBaseHost = the_buffer;
407 cebix 1.66 MacFrameSize = mode.bytes_per_row * mode.y;
408 cebix 1.44 InitFrameBufferMapping();
409 cebix 1.42 #else
410 cebix 1.66 monitor.set_mac_frame_base(Host2MacAddr(the_buffer));
411 cebix 1.15 #endif
412 cebix 1.66 D(bug("monitor.mac_frame_base = %08x\n", monitor.get_mac_frame_base()));
413 cebix 1.1 }
414    
415 cebix 1.29 // Set window name and class
416     static void set_window_name(Window w, int name)
417     {
418     const char *str = GetString(name);
419     XStoreName(x_display, w, str);
420     XSetIconName(x_display, w, str);
421    
422     XClassHint *hints;
423     hints = XAllocClassHint();
424     if (hints) {
425     hints->res_name = "BasiliskII";
426     hints->res_class = "BasiliskII";
427     XSetClassHint(x_display, w, hints);
428 cebix 1.33 XFree(hints);
429 cebix 1.29 }
430     }
431    
432     // Set window input focus flag
433     static void set_window_focus(Window w)
434     {
435     XWMHints *hints = XAllocWMHints();
436     if (hints) {
437     hints->input = True;
438     hints->initial_state = NormalState;
439     hints->flags = InputHint | StateHint;
440     XSetWMHints(x_display, w, hints);
441 cebix 1.33 XFree(hints);
442 cebix 1.29 }
443     }
444    
445     // Set WM_DELETE_WINDOW protocol on window (preventing it from being destroyed by the WM when clicking on the "close" widget)
446 cebix 1.44 static Atom WM_DELETE_WINDOW = (Atom)0;
447 cebix 1.29 static void set_window_delete_protocol(Window w)
448     {
449     WM_DELETE_WINDOW = XInternAtom(x_display, "WM_DELETE_WINDOW", false);
450     XSetWMProtocols(x_display, w, &WM_DELETE_WINDOW, 1);
451     }
452    
453     // Wait until window is mapped/unmapped
454     void wait_mapped(Window w)
455     {
456     XEvent e;
457     do {
458     XMaskEvent(x_display, StructureNotifyMask, &e);
459     } while ((e.type != MapNotify) || (e.xmap.event != w));
460     }
461    
462     void wait_unmapped(Window w)
463     {
464     XEvent e;
465     do {
466     XMaskEvent(x_display, StructureNotifyMask, &e);
467     } while ((e.type != UnmapNotify) || (e.xmap.event != w));
468     }
469    
470 cebix 1.1 // Trap SHM errors
471     static bool shm_error = false;
472     static int (*old_error_handler)(Display *, XErrorEvent *);
473    
474     static int error_handler(Display *d, XErrorEvent *e)
475     {
476     if (e->error_code == BadAccess) {
477     shm_error = true;
478     return 0;
479     } else
480     return old_error_handler(d, e);
481     }
482    
483 cebix 1.44
484     /*
485     * Display "driver" classes
486     */
487    
488     class driver_base {
489     public:
490 cebix 1.66 driver_base(X11_monitor_desc &m);
491 cebix 1.44 virtual ~driver_base();
492    
493     virtual void update_palette(void);
494     virtual void suspend(void) {}
495     virtual void resume(void) {}
496 cebix 1.51 virtual void toggle_mouse_grab(void) {}
497     virtual void mouse_moved(int x, int y) { ADBMouseMoved(x, y); }
498    
499 cebix 1.57 void disable_mouse_accel(void);
500     void restore_mouse_accel(void);
501    
502 cebix 1.51 virtual void grab_mouse(void) {}
503     virtual void ungrab_mouse(void) {}
504 cebix 1.44
505     public:
506 cebix 1.66 X11_monitor_desc &monitor; // Associated video monitor
507     const video_mode &mode; // Video mode handled by the driver
508    
509 cebix 1.44 bool init_ok; // Initialization succeeded (we can't use exceptions because of -fomit-frame-pointer)
510     Window w; // The window we draw into
511 cebix 1.57
512     int orig_accel_numer, orig_accel_denom, orig_threshold; // Original mouse acceleration
513 cebix 1.44 };
514    
515     class driver_window;
516     static void update_display_window_vosf(driver_window *drv);
517     static void update_display_dynamic(int ticker, driver_window *drv);
518     static void update_display_static(driver_window *drv);
519    
520     class driver_window : public driver_base {
521     friend void update_display_window_vosf(driver_window *drv);
522     friend void update_display_dynamic(int ticker, driver_window *drv);
523     friend void update_display_static(driver_window *drv);
524    
525     public:
526 cebix 1.66 driver_window(X11_monitor_desc &monitor);
527 cebix 1.44 ~driver_window();
528    
529 cebix 1.51 void toggle_mouse_grab(void);
530     void mouse_moved(int x, int y);
531    
532     void grab_mouse(void);
533     void ungrab_mouse(void);
534    
535 cebix 1.44 private:
536     GC gc;
537     XImage *img;
538 cebix 1.51 bool have_shm; // Flag: SHM extensions available
539 cebix 1.44 XShmSegmentInfo shminfo;
540     Cursor mac_cursor;
541 cebix 1.51 bool mouse_grabbed; // Flag: mouse pointer grabbed, using relative mouse mode
542     int mouse_last_x, mouse_last_y; // Last mouse position (for relative mode)
543 cebix 1.44 };
544    
545 gbeauche 1.79 class driver_dga;
546     static void update_display_dga_vosf(driver_dga *drv);
547    
548     class driver_dga : public driver_base {
549     friend void update_display_dga_vosf(driver_dga *drv);
550    
551     public:
552     driver_dga(X11_monitor_desc &monitor);
553     ~driver_dga();
554    
555     void suspend(void);
556     void resume(void);
557    
558     protected:
559     struct FakeXImage {
560     int width, height; // size of image
561     int depth; // depth of image
562     int bytes_per_line; // accelerator to next line
563    
564     FakeXImage(int w, int h, int d)
565     : width(w), height(h), depth(d)
566     { bytes_per_line = TrivialBytesPerRow(width, DepthModeForPixelDepth(depth)); }
567     };
568     FakeXImage *img;
569    
570     private:
571     Window suspend_win; // "Suspend" information window
572     void *fb_save; // Saved frame buffer for suspend/resume
573     };
574    
575 cebix 1.44 static driver_base *drv = NULL; // Pointer to currently used driver object
576    
577     #ifdef ENABLE_VOSF
578     # include "video_vosf.h"
579     #endif
580    
581 cebix 1.66 driver_base::driver_base(X11_monitor_desc &m)
582     : monitor(m), mode(m.get_current_mode()), init_ok(false), w(0)
583 cebix 1.44 {
584     the_buffer = NULL;
585     the_buffer_copy = NULL;
586 cebix 1.57 XGetPointerControl(x_display, &orig_accel_numer, &orig_accel_denom, &orig_threshold);
587 cebix 1.44 }
588    
589     driver_base::~driver_base()
590     {
591 cebix 1.51 ungrab_mouse();
592 cebix 1.57 restore_mouse_accel();
593 cebix 1.51
594 cebix 1.44 if (w) {
595     XUnmapWindow(x_display, w);
596     wait_unmapped(w);
597     XDestroyWindow(x_display, w);
598     }
599    
600 cebix 1.45 XFlush(x_display);
601     XSync(x_display, false);
602    
603 cebix 1.44 // Free frame buffer(s)
604     if (!use_vosf) {
605     if (the_buffer) {
606     free(the_buffer);
607     the_buffer = NULL;
608     }
609     if (the_buffer_copy) {
610     free(the_buffer_copy);
611     the_buffer_copy = NULL;
612     }
613     }
614     #ifdef ENABLE_VOSF
615     else {
616 gbeauche 1.62 // the_buffer shall always be mapped through vm_acquire() so that we can vm_protect() it at will
617     if (the_buffer != VM_MAP_FAILED) {
618     D(bug(" releasing the_buffer at %p (%d bytes)\n", the_buffer, the_buffer_size));
619     vm_release(the_buffer, the_buffer_size);
620     the_buffer = NULL;
621     }
622 cebix 1.55 if (the_host_buffer) {
623 cebix 1.61 D(bug(" freeing the_host_buffer at %p\n", the_host_buffer));
624 cebix 1.55 free(the_host_buffer);
625     the_host_buffer = NULL;
626     }
627 gbeauche 1.56 if (the_buffer_copy) {
628 cebix 1.61 D(bug(" freeing the_buffer_copy at %p\n", the_buffer_copy));
629 gbeauche 1.56 free(the_buffer_copy);
630 cebix 1.44 the_buffer_copy = NULL;
631     }
632     }
633     #endif
634     }
635    
636     // Palette has changed
637     void driver_base::update_palette(void)
638     {
639 cebix 1.58 if (color_class == PseudoColor || color_class == DirectColor) {
640 cebix 1.54 int num = vis->map_entries;
641 cebix 1.66 if (!IsDirectMode(monitor.get_current_mode()) && color_class == DirectColor)
642 cebix 1.47 return; // Indexed mode on true color screen, don't set CLUT
643 cebix 1.66 XStoreColors(x_display, cmap[0], x_palette, num);
644     XStoreColors(x_display, cmap[1], x_palette, num);
645 cebix 1.44 }
646     XSync(x_display, false);
647     }
648    
649 cebix 1.57 // Disable mouse acceleration
650     void driver_base::disable_mouse_accel(void)
651     {
652     XChangePointerControl(x_display, True, False, 1, 1, 0);
653     }
654    
655     // Restore mouse acceleration to original value
656     void driver_base::restore_mouse_accel(void)
657     {
658     XChangePointerControl(x_display, True, True, orig_accel_numer, orig_accel_denom, orig_threshold);
659     }
660    
661 cebix 1.44
662     /*
663     * Windowed display driver
664     */
665    
666     // Open display
667 cebix 1.66 driver_window::driver_window(X11_monitor_desc &m)
668     : driver_base(m), gc(0), img(NULL), have_shm(false), mac_cursor(0), mouse_grabbed(false)
669 cebix 1.1 {
670 cebix 1.42 int width = mode.x, height = mode.y;
671 cebix 1.13 int aligned_width = (width + 15) & ~15;
672     int aligned_height = (height + 15) & ~15;
673    
674 cebix 1.1 // Set absolute mouse mode
675 cebix 1.51 ADBSetRelMouseMode(mouse_grabbed);
676 cebix 1.1
677 gbeauche 1.62 // Create window (setting background_pixel, border_pixel and colormap is
678 cebix 1.58 // mandatory when using a non-default visual; in 1-bit mode we use the
679     // default visual, so we can also use the default colormap)
680 cebix 1.1 XSetWindowAttributes wattr;
681     wattr.event_mask = eventmask = win_eventmask;
682 cebix 1.58 wattr.background_pixel = (vis == DefaultVisual(x_display, screen) ? black_pixel : 0);
683     wattr.border_pixel = 0;
684     wattr.colormap = (mode.depth == VDEPTH_1BIT ? DefaultColormap(x_display, screen) : cmap[0]);
685 cebix 1.44 w = XCreateWindow(x_display, rootwin, 0, 0, width, height, 0, xdepth,
686 cebix 1.58 InputOutput, vis, CWEventMask | CWBackPixel | CWBorderPixel | CWColormap, &wattr);
687 cebix 1.61 D(bug(" window created\n"));
688 cebix 1.29
689     // Set window name/class
690 cebix 1.44 set_window_name(w, STR_WINDOW_TITLE);
691 cebix 1.26
692     // Indicate that we want keyboard input
693 cebix 1.44 set_window_focus(w);
694 cebix 1.29
695     // Set delete protocol property
696 cebix 1.44 set_window_delete_protocol(w);
697 cebix 1.26
698     // Make window unresizable
699     {
700     XSizeHints *hints = XAllocSizeHints();
701     if (hints) {
702     hints->min_width = width;
703     hints->max_width = width;
704     hints->min_height = height;
705     hints->max_height = height;
706     hints->flags = PMinSize | PMaxSize;
707 cebix 1.44 XSetWMNormalHints(x_display, w, hints);
708 cebix 1.33 XFree(hints);
709 cebix 1.26 }
710     }
711 cebix 1.61 D(bug(" window attributes set\n"));
712 cebix 1.26
713     // Show window
714 cebix 1.44 XMapWindow(x_display, w);
715     wait_mapped(w);
716 cebix 1.61 D(bug(" window mapped\n"));
717 cebix 1.1
718 cebix 1.48 // 1-bit mode is big-endian; if the X server is little-endian, we can't
719     // use SHM because that doesn't allow changing the image byte order
720     bool need_msb_image = (mode.depth == VDEPTH_1BIT && XImageByteOrder(x_display) == LSBFirst);
721    
722 cebix 1.1 // Try to create and attach SHM image
723 cebix 1.48 if (local_X11 && !need_msb_image && XShmQueryExtension(x_display)) {
724 cebix 1.1
725     // Create SHM image ("height + 2" for safety)
726 cebix 1.42 img = XShmCreateImage(x_display, vis, mode.depth == VDEPTH_1BIT ? 1 : xdepth, mode.depth == VDEPTH_1BIT ? XYBitmap : ZPixmap, 0, &shminfo, width, height);
727 cebix 1.61 D(bug(" shm image created\n"));
728 cebix 1.13 shminfo.shmid = shmget(IPC_PRIVATE, (aligned_height + 2) * img->bytes_per_line, IPC_CREAT | 0777);
729     the_buffer_copy = (uint8 *)shmat(shminfo.shmid, 0, 0);
730     shminfo.shmaddr = img->data = (char *)the_buffer_copy;
731 cebix 1.1 shminfo.readOnly = False;
732    
733     // Try to attach SHM image, catching errors
734     shm_error = false;
735     old_error_handler = XSetErrorHandler(error_handler);
736     XShmAttach(x_display, &shminfo);
737     XSync(x_display, false);
738     XSetErrorHandler(old_error_handler);
739     if (shm_error) {
740     shmdt(shminfo.shmaddr);
741     XDestroyImage(img);
742 cebix 1.55 img = NULL;
743 cebix 1.1 shminfo.shmid = -1;
744     } else {
745     have_shm = true;
746     shmctl(shminfo.shmid, IPC_RMID, 0);
747     }
748 cebix 1.61 D(bug(" shm image attached\n"));
749 cebix 1.1 }
750 cebix 1.7
751 cebix 1.1 // Create normal X image if SHM doesn't work ("height + 2" for safety)
752     if (!have_shm) {
753 cebix 1.49 int bytes_per_row = (mode.depth == VDEPTH_1BIT ? aligned_width/8 : TrivialBytesPerRow(aligned_width, DepthModeForPixelDepth(xdepth)));
754 cebix 1.13 the_buffer_copy = (uint8 *)malloc((aligned_height + 2) * bytes_per_row);
755 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);
756 cebix 1.61 D(bug(" X image created\n"));
757 cebix 1.1 }
758    
759 cebix 1.48 if (need_msb_image) {
760 cebix 1.1 img->byte_order = MSBFirst;
761     img->bitmap_bit_order = MSBFirst;
762     }
763    
764 gbeauche 1.19 #ifdef ENABLE_VOSF
765 gbeauche 1.56 use_vosf = true;
766 gbeauche 1.39 // Allocate memory for frame buffer (SIZE is extended to page-boundary)
767 gbeauche 1.19 the_host_buffer = the_buffer_copy;
768 gbeauche 1.79 the_buffer_size = page_extend((aligned_height + 2) * img->bytes_per_line);
769 gbeauche 1.72 the_buffer = (uint8 *)vm_acquire_mac(the_buffer_size);
770 gbeauche 1.62 the_buffer_copy = (uint8 *)malloc(the_buffer_size);
771 cebix 1.55 D(bug("the_buffer = %p, the_buffer_copy = %p, the_host_buffer = %p\n", the_buffer, the_buffer_copy, the_host_buffer));
772 gbeauche 1.19 #else
773 cebix 1.16 // Allocate memory for frame buffer
774 cebix 1.13 the_buffer = (uint8 *)malloc((aligned_height + 2) * img->bytes_per_line);
775 cebix 1.55 D(bug("the_buffer = %p, the_buffer_copy = %p\n", the_buffer, the_buffer_copy));
776 gbeauche 1.19 #endif
777 cebix 1.1
778     // Create GC
779 cebix 1.44 gc = XCreateGC(x_display, w, 0, 0);
780     XSetState(x_display, gc, black_pixel, white_pixel, GXcopy, AllPlanes);
781 cebix 1.1
782 cebix 1.13 // Create no_cursor
783 cebix 1.16 mac_cursor = XCreatePixmapCursor(x_display,
784 cebix 1.44 XCreatePixmap(x_display, w, 1, 1, 1),
785     XCreatePixmap(x_display, w, 1, 1, 1),
786 cebix 1.16 &black, &white, 0, 0);
787 cebix 1.44 XDefineCursor(x_display, w, mac_cursor);
788 cebix 1.1
789 cebix 1.44 // Init blitting routines
790 gbeauche 1.19 bool native_byte_order;
791 cebix 1.1 #ifdef WORDS_BIGENDIAN
792 cebix 1.31 native_byte_order = (XImageByteOrder(x_display) == MSBFirst);
793 cebix 1.1 #else
794 cebix 1.31 native_byte_order = (XImageByteOrder(x_display) == LSBFirst);
795 cebix 1.1 #endif
796 gbeauche 1.19 #ifdef ENABLE_VOSF
797 gbeauche 1.71 Screen_blitter_init(visualFormat, native_byte_order, depth_of_video_mode(mode));
798 gbeauche 1.19 #endif
799 cebix 1.44
800 cebix 1.66 // Set frame buffer base
801     set_mac_frame_buffer(monitor, mode.depth, native_byte_order);
802 cebix 1.44
803     // Everything went well
804     init_ok = true;
805     }
806    
807     // Close display
808     driver_window::~driver_window()
809     {
810 cebix 1.55 if (have_shm) {
811     XShmDetach(x_display, &shminfo);
812     #ifdef ENABLE_VOSF
813     the_host_buffer = NULL; // don't free() in driver_base dtor
814     #else
815     the_buffer_copy = NULL; // don't free() in driver_base dtor
816     #endif
817     }
818 cebix 1.61 if (img) {
819     if (!have_shm)
820     img->data = NULL;
821 cebix 1.44 XDestroyImage(img);
822 cebix 1.61 }
823 cebix 1.44 if (have_shm) {
824 cebix 1.55 shmdt(shminfo.shmaddr);
825     shmctl(shminfo.shmid, IPC_RMID, 0);
826 cebix 1.44 }
827     if (gc)
828     XFreeGC(x_display, gc);
829     }
830    
831 cebix 1.51 // Toggle mouse grab
832     void driver_window::toggle_mouse_grab(void)
833     {
834     if (mouse_grabbed)
835     ungrab_mouse();
836     else
837     grab_mouse();
838     }
839    
840     // Grab mouse, switch to relative mouse mode
841     void driver_window::grab_mouse(void)
842     {
843     int result;
844     for (int i=0; i<10; i++) {
845     result = XGrabPointer(x_display, w, True, 0,
846     GrabModeAsync, GrabModeAsync, w, None, CurrentTime);
847     if (result != AlreadyGrabbed)
848     break;
849     Delay_usec(100000);
850     }
851     if (result == GrabSuccess) {
852 cebix 1.57 XStoreName(x_display, w, GetString(STR_WINDOW_TITLE_GRABBED));
853 cebix 1.51 ADBSetRelMouseMode(mouse_grabbed = true);
854 cebix 1.57 disable_mouse_accel();
855 cebix 1.51 }
856     }
857    
858     // Ungrab mouse, switch to absolute mouse mode
859     void driver_window::ungrab_mouse(void)
860     {
861     if (mouse_grabbed) {
862     XUngrabPointer(x_display, CurrentTime);
863     XStoreName(x_display, w, GetString(STR_WINDOW_TITLE));
864     ADBSetRelMouseMode(mouse_grabbed = false);
865 cebix 1.57 restore_mouse_accel();
866 cebix 1.51 }
867     }
868    
869     // Mouse moved
870     void driver_window::mouse_moved(int x, int y)
871     {
872     if (!mouse_grabbed) {
873     mouse_last_x = x; mouse_last_y = y;
874     ADBMouseMoved(x, y);
875     return;
876     }
877    
878     // Warped mouse motion (this code is taken from SDL)
879    
880     // Post first mouse event
881 cebix 1.66 int width = monitor.get_current_mode().x, height = monitor.get_current_mode().y;
882 cebix 1.51 int delta_x = x - mouse_last_x, delta_y = y - mouse_last_y;
883     mouse_last_x = x; mouse_last_y = y;
884     ADBMouseMoved(delta_x, delta_y);
885    
886     // Only warp the pointer when it has reached the edge
887     const int MOUSE_FUDGE_FACTOR = 8;
888     if (x < MOUSE_FUDGE_FACTOR || x > (width - MOUSE_FUDGE_FACTOR)
889     || y < MOUSE_FUDGE_FACTOR || y > (height - MOUSE_FUDGE_FACTOR)) {
890     XEvent event;
891     while (XCheckTypedEvent(x_display, MotionNotify, &event)) {
892     delta_x = x - mouse_last_x; delta_y = y - mouse_last_y;
893     mouse_last_x = x; mouse_last_y = y;
894     ADBMouseMoved(delta_x, delta_y);
895     }
896     mouse_last_x = width/2;
897     mouse_last_y = height/2;
898     XWarpPointer(x_display, None, w, 0, 0, 0, 0, mouse_last_x, mouse_last_y);
899     for (int i=0; i<10; i++) {
900     XMaskEvent(x_display, PointerMotionMask, &event);
901     if (event.xmotion.x > (mouse_last_x - MOUSE_FUDGE_FACTOR)
902     && event.xmotion.x < (mouse_last_x + MOUSE_FUDGE_FACTOR)
903     && event.xmotion.y > (mouse_last_y - MOUSE_FUDGE_FACTOR)
904     && event.xmotion.y < (mouse_last_y + MOUSE_FUDGE_FACTOR))
905     break;
906     }
907     }
908     }
909    
910 cebix 1.44
911     #if defined(ENABLE_XF86_DGA) || defined(ENABLE_FBDEV_DGA)
912     /*
913     * DGA display driver base class
914     */
915    
916 cebix 1.66 driver_dga::driver_dga(X11_monitor_desc &m)
917 gbeauche 1.79 : driver_base(m), suspend_win(0), fb_save(NULL), img(NULL)
918 cebix 1.44 {
919     }
920    
921     driver_dga::~driver_dga()
922     {
923     XUngrabPointer(x_display, CurrentTime);
924     XUngrabKeyboard(x_display, CurrentTime);
925 gbeauche 1.79
926     if (img)
927     delete img;
928 cebix 1.44 }
929    
930     // Suspend emulation
931     void driver_dga::suspend(void)
932     {
933     // Release ctrl key
934     ADBKeyUp(0x36);
935     ctrl_down = false;
936    
937     // Lock frame buffer (this will stop the MacOS thread)
938     LOCK_FRAME_BUFFER;
939    
940     // Save frame buffer
941 cebix 1.66 fb_save = malloc(mode.y * mode.bytes_per_row);
942 cebix 1.44 if (fb_save)
943 cebix 1.66 memcpy(fb_save, the_buffer, mode.y * mode.bytes_per_row);
944 cebix 1.44
945     // Close full screen display
946     #ifdef ENABLE_XF86_DGA
947     XF86DGADirectVideo(x_display, screen, 0);
948     #endif
949     XUngrabPointer(x_display, CurrentTime);
950     XUngrabKeyboard(x_display, CurrentTime);
951 cebix 1.57 restore_mouse_accel();
952 cebix 1.44 XUnmapWindow(x_display, w);
953     wait_unmapped(w);
954    
955     // Open "suspend" window
956     XSetWindowAttributes wattr;
957     wattr.event_mask = KeyPressMask;
958     wattr.background_pixel = black_pixel;
959    
960     suspend_win = XCreateWindow(x_display, rootwin, 0, 0, 512, 1, 0, xdepth,
961     InputOutput, vis, CWEventMask | CWBackPixel, &wattr);
962     set_window_name(suspend_win, STR_SUSPEND_WINDOW_TITLE);
963     set_window_focus(suspend_win);
964     XMapWindow(x_display, suspend_win);
965     emul_suspended = true;
966 cebix 1.7 }
967    
968 cebix 1.44 // Resume emulation
969     void driver_dga::resume(void)
970 cebix 1.7 {
971 cebix 1.44 // Close "suspend" window
972     XDestroyWindow(x_display, suspend_win);
973     XSync(x_display, false);
974    
975     // Reopen full screen display
976     XMapRaised(x_display, w);
977     wait_mapped(w);
978     XWarpPointer(x_display, None, rootwin, 0, 0, 0, 0, 0, 0);
979 cebix 1.51 XGrabKeyboard(x_display, rootwin, True, GrabModeAsync, GrabModeAsync, CurrentTime);
980     XGrabPointer(x_display, rootwin, True, PointerMotionMask | ButtonPressMask | ButtonReleaseMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
981 cebix 1.57 disable_mouse_accel();
982 cebix 1.44 #ifdef ENABLE_XF86_DGA
983     XF86DGADirectVideo(x_display, screen, XF86DGADirectGraphics | XF86DGADirectKeyb | XF86DGADirectMouse);
984     XF86DGASetViewPort(x_display, screen, 0, 0);
985     #endif
986     XSync(x_display, false);
987    
988     // the_buffer already contains the data to restore. i.e. since a temporary
989     // frame buffer is used when VOSF is actually used, fb_save is therefore
990     // not necessary.
991     #ifdef ENABLE_VOSF
992     if (use_vosf) {
993     LOCK_VOSF;
994     PFLAG_SET_ALL;
995     UNLOCK_VOSF;
996 cebix 1.66 memset(the_buffer_copy, 0, mode.bytes_per_row * mode.y);
997 cebix 1.44 }
998     #endif
999    
1000     // Restore frame buffer
1001     if (fb_save) {
1002     #ifdef ENABLE_VOSF
1003     // Don't copy fb_save to the temporary frame buffer in VOSF mode
1004     if (!use_vosf)
1005     #endif
1006 cebix 1.66 memcpy(the_buffer, fb_save, mode.y * mode.bytes_per_row);
1007 cebix 1.44 free(fb_save);
1008     fb_save = NULL;
1009     }
1010    
1011     // Unlock frame buffer (and continue MacOS thread)
1012     UNLOCK_FRAME_BUFFER;
1013     emul_suspended = false;
1014     }
1015     #endif
1016    
1017    
1018 cebix 1.15 #ifdef ENABLE_FBDEV_DGA
1019 cebix 1.44 /*
1020     * fbdev DGA display driver
1021     */
1022    
1023 cebix 1.45 const char FBDEVICES_FILE_NAME[] = DATADIR "/fbdevices";
1024     const char FBDEVICE_FILE_NAME[] = "/dev/fb";
1025    
1026 cebix 1.44 class driver_fbdev : public driver_dga {
1027     public:
1028 cebix 1.66 driver_fbdev(X11_monitor_desc &monitor);
1029 cebix 1.44 ~driver_fbdev();
1030     };
1031    
1032     // Open display
1033 cebix 1.66 driver_fbdev::driver_fbdev(X11_monitor_desc &m) : driver_dga(m)
1034 cebix 1.44 {
1035 cebix 1.42 int width = mode.x, height = mode.y;
1036    
1037 cebix 1.44 // Set absolute mouse mode
1038     ADBSetRelMouseMode(false);
1039    
1040 cebix 1.7 // Find the maximum depth available
1041     int ndepths, max_depth(0);
1042     int *depths = XListDepths(x_display, screen, &ndepths);
1043     if (depths == NULL) {
1044 cebix 1.9 printf("FATAL: Could not determine the maximal depth available\n");
1045 cebix 1.44 return;
1046 cebix 1.7 } else {
1047     while (ndepths-- > 0) {
1048     if (depths[ndepths] > max_depth)
1049     max_depth = depths[ndepths];
1050     }
1051     }
1052    
1053     // Get fbdevices file path from preferences
1054 cebix 1.8 const char *fbd_path = PrefsFindString("fbdevicefile");
1055 cebix 1.7
1056     // Open fbdevices file
1057     FILE *fp = fopen(fbd_path ? fbd_path : FBDEVICES_FILE_NAME, "r");
1058     if (fp == NULL) {
1059     char str[256];
1060     sprintf(str, GetString(STR_NO_FBDEVICE_FILE_ERR), fbd_path ? fbd_path : FBDEVICES_FILE_NAME, strerror(errno));
1061     ErrorAlert(str);
1062 cebix 1.44 return;
1063 cebix 1.7 }
1064    
1065     int fb_depth; // supported depth
1066     uint32 fb_offset; // offset used for mmap(2)
1067     char fb_name[20];
1068     char line[256];
1069     bool device_found = false;
1070     while (fgets(line, 255, fp)) {
1071     // Read line
1072     int len = strlen(line);
1073     if (len == 0)
1074     continue;
1075     line[len - 1] = '\0';
1076    
1077     // Comments begin with "#" or ";"
1078     if ((line[0] == '#') || (line[0] == ';') || (line[0] == '\0'))
1079     continue;
1080    
1081 cebix 1.65 if ((sscanf(line, "%19s %d %x", fb_name, &fb_depth, &fb_offset) == 3)
1082 cebix 1.44 && (strcmp(fb_name, fb_name) == 0) && (fb_depth == max_depth)) {
1083 cebix 1.7 device_found = true;
1084     break;
1085     }
1086     }
1087    
1088     // fbdevices file completely read
1089     fclose(fp);
1090    
1091     // Frame buffer name not found ? Then, display warning
1092     if (!device_found) {
1093     char str[256];
1094 cebix 1.42 sprintf(str, GetString(STR_FBDEV_NAME_ERR), fb_name, max_depth);
1095 cebix 1.7 ErrorAlert(str);
1096 cebix 1.44 return;
1097 cebix 1.7 }
1098    
1099     // Create window
1100     XSetWindowAttributes wattr;
1101 cebix 1.29 wattr.event_mask = eventmask = dga_eventmask;
1102     wattr.background_pixel = white_pixel;
1103     wattr.override_redirect = True;
1104     wattr.colormap = cmap[0];
1105 cebix 1.7
1106 cebix 1.44 w = XCreateWindow(x_display, rootwin,
1107 cebix 1.7 0, 0, width, height,
1108     0, xdepth, InputOutput, vis,
1109 cebix 1.44 CWEventMask | CWBackPixel | CWOverrideRedirect | (fb_depth <= 8 ? CWColormap : 0),
1110 cebix 1.7 &wattr);
1111 cebix 1.29
1112     // Set window name/class
1113 cebix 1.44 set_window_name(w, STR_WINDOW_TITLE);
1114 cebix 1.29
1115     // Indicate that we want keyboard input
1116 cebix 1.44 set_window_focus(w);
1117 cebix 1.29
1118     // Show window
1119 cebix 1.44 XMapRaised(x_display, w);
1120     wait_mapped(w);
1121 cebix 1.7
1122     // Grab mouse and keyboard
1123 cebix 1.44 XGrabKeyboard(x_display, w, True,
1124 cebix 1.7 GrabModeAsync, GrabModeAsync, CurrentTime);
1125 cebix 1.44 XGrabPointer(x_display, w, True,
1126 cebix 1.7 PointerMotionMask | ButtonPressMask | ButtonReleaseMask,
1127 cebix 1.44 GrabModeAsync, GrabModeAsync, w, None, CurrentTime);
1128 cebix 1.57 disable_mouse_accel();
1129 cebix 1.7
1130 cebix 1.42 // Calculate bytes per row
1131     int bytes_per_row = TrivialBytesPerRow(mode.x, mode.depth);
1132 cebix 1.7
1133 cebix 1.42 // Map frame buffer
1134 gbeauche 1.56 the_buffer_size = height * bytes_per_row;
1135     if ((the_buffer = (uint8 *) mmap(NULL, the_buffer_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fbdev_fd, fb_offset)) == MAP_FAILED) {
1136     if ((the_buffer = (uint8 *) mmap(NULL, the_buffer_size, PROT_READ | PROT_WRITE, MAP_SHARED, fbdev_fd, fb_offset)) == MAP_FAILED) {
1137 cebix 1.7 char str[256];
1138     sprintf(str, GetString(STR_FBDEV_MMAP_ERR), strerror(errno));
1139     ErrorAlert(str);
1140 cebix 1.44 return;
1141 cebix 1.7 }
1142     }
1143    
1144 cebix 1.23 #if ENABLE_VOSF
1145 gbeauche 1.19 #if REAL_ADDRESSING || DIRECT_ADDRESSING
1146 gbeauche 1.36 // Screen_blitter_init() returns TRUE if VOSF is mandatory
1147     // i.e. the framebuffer update function is not Blit_Copy_Raw
1148 gbeauche 1.71 use_vosf = Screen_blitter_init(visualFormat, true, mode.depth);
1149 gbeauche 1.19
1150     if (use_vosf) {
1151 gbeauche 1.39 // Allocate memory for frame buffer (SIZE is extended to page-boundary)
1152     the_host_buffer = the_buffer;
1153 gbeauche 1.79 the_buffer_size = page_extend((height + 2) * bytes_per_row);
1154 gbeauche 1.62 the_buffer_copy = (uint8 *)malloc(the_buffer_size);
1155 gbeauche 1.72 the_buffer = (uint8 *)vm_acquire_mac(the_buffer_size);
1156 gbeauche 1.79
1157     // Fake image for DGA/VOSF mode to know about display bounds
1158     img = new FakeXImage(width, height, depth_of_video_mode(mode));
1159 gbeauche 1.19 }
1160 cebix 1.23 #else
1161 gbeauche 1.19 use_vosf = false;
1162     #endif
1163 cebix 1.23 #endif
1164 gbeauche 1.19
1165 cebix 1.66 // Set frame buffer base
1166     const_cast<video_mode *>(&mode)->bytes_per_row = bytes_per_row;
1167     const_cast<video_mode *>(&mode)->depth = DepthModeForPixelDepth(fb_depth);
1168 cebix 1.67 set_mac_frame_buffer(monitor, mode.depth, true);
1169 cebix 1.44
1170     // Everything went well
1171     init_ok = true;
1172     }
1173    
1174     // Close display
1175     driver_fbdev::~driver_fbdev()
1176     {
1177 gbeauche 1.56 if (!use_vosf) {
1178     if (the_buffer != MAP_FAILED) {
1179     // don't free() the screen buffer in driver_base dtor
1180     munmap(the_buffer, the_buffer_size);
1181     the_buffer = NULL;
1182     }
1183     }
1184     #ifdef ENABLE_VOSF
1185     else {
1186     if (the_host_buffer != MAP_FAILED) {
1187     // don't free() the screen buffer in driver_base dtor
1188     munmap(the_host_buffer, the_buffer_size);
1189     the_host_buffer = NULL;
1190     }
1191     }
1192     #endif
1193 cebix 1.44 }
1194 cebix 1.7 #endif
1195 cebix 1.1
1196 cebix 1.44
1197     #ifdef ENABLE_XF86_DGA
1198     /*
1199     * XFree86 DGA display driver
1200     */
1201    
1202     class driver_xf86dga : public driver_dga {
1203     public:
1204 cebix 1.66 driver_xf86dga(X11_monitor_desc &monitor);
1205 cebix 1.44 ~driver_xf86dga();
1206    
1207     void update_palette(void);
1208     void resume(void);
1209    
1210     private:
1211     int current_dga_cmap; // Number (0 or 1) of currently installed DGA colormap
1212     };
1213    
1214     // Open display
1215 cebix 1.66 driver_xf86dga::driver_xf86dga(X11_monitor_desc &m)
1216     : driver_dga(m), current_dga_cmap(0)
1217 cebix 1.1 {
1218 cebix 1.42 int width = mode.x, height = mode.y;
1219    
1220 cebix 1.1 // Set relative mouse mode
1221     ADBSetRelMouseMode(true);
1222    
1223 cebix 1.15 #ifdef ENABLE_XF86_VIDMODE
1224 cebix 1.12 // Switch to best mode
1225     if (has_vidmode) {
1226     int best = 0;
1227     for (int i=1; i<num_x_video_modes; i++) {
1228     if (x_video_modes[i]->hdisplay >= width && x_video_modes[i]->vdisplay >= height &&
1229     x_video_modes[i]->hdisplay <= x_video_modes[best]->hdisplay && x_video_modes[i]->vdisplay <= x_video_modes[best]->vdisplay) {
1230     best = i;
1231     }
1232     }
1233     XF86VidModeSwitchToMode(x_display, screen, x_video_modes[best]);
1234     XF86VidModeSetViewPort(x_display, screen, 0, 0);
1235 cebix 1.29 XSync(x_display, false);
1236 cebix 1.12 }
1237     #endif
1238    
1239 cebix 1.1 // Create window
1240     XSetWindowAttributes wattr;
1241     wattr.event_mask = eventmask = dga_eventmask;
1242     wattr.override_redirect = True;
1243 gbeauche 1.68 wattr.colormap = (mode.depth == VDEPTH_1BIT ? DefaultColormap(x_display, screen) : cmap[0]);
1244 cebix 1.1
1245 cebix 1.44 w = XCreateWindow(x_display, rootwin, 0, 0, width, height, 0, xdepth,
1246 gbeauche 1.68 InputOutput, vis, CWEventMask | CWOverrideRedirect |
1247     (color_class == DirectColor ? CWColormap : 0), &wattr);
1248 cebix 1.29
1249     // Set window name/class
1250 cebix 1.44 set_window_name(w, STR_WINDOW_TITLE);
1251 cebix 1.29
1252     // Indicate that we want keyboard input
1253 cebix 1.44 set_window_focus(w);
1254 cebix 1.29
1255     // Show window
1256 cebix 1.44 XMapRaised(x_display, w);
1257     wait_mapped(w);
1258 cebix 1.1
1259     // Establish direct screen connection
1260 cebix 1.44 XMoveResizeWindow(x_display, w, 0, 0, width, height);
1261 cebix 1.1 XWarpPointer(x_display, None, rootwin, 0, 0, 0, 0, 0, 0);
1262     XGrabKeyboard(x_display, rootwin, True, GrabModeAsync, GrabModeAsync, CurrentTime);
1263     XGrabPointer(x_display, rootwin, True, PointerMotionMask | ButtonPressMask | ButtonReleaseMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
1264 cebix 1.57 disable_mouse_accel();
1265 cebix 1.1
1266     int v_width, v_bank, v_size;
1267     XF86DGAGetVideo(x_display, screen, (char **)&the_buffer, &v_width, &v_bank, &v_size);
1268     XF86DGADirectVideo(x_display, screen, XF86DGADirectGraphics | XF86DGADirectKeyb | XF86DGADirectMouse);
1269     XF86DGASetViewPort(x_display, screen, 0, 0);
1270     XF86DGASetVidPage(x_display, screen, 0);
1271    
1272     // Set colormap
1273 cebix 1.42 if (!IsDirectMode(mode)) {
1274 cebix 1.44 XSetWindowColormap(x_display, w, cmap[current_dga_cmap = 0]);
1275 cebix 1.1 XF86DGAInstallColormap(x_display, screen, cmap[current_dga_cmap]);
1276     }
1277 cebix 1.29 XSync(x_display, false);
1278 cebix 1.1
1279 cebix 1.44 // Init blitting routines
1280 cebix 1.42 int bytes_per_row = TrivialBytesPerRow((v_width + 7) & ~7, mode.depth);
1281 gbeauche 1.68 #if ENABLE_VOSF
1282     bool native_byte_order;
1283     #ifdef WORDS_BIGENDIAN
1284     native_byte_order = (XImageByteOrder(x_display) == MSBFirst);
1285     #else
1286     native_byte_order = (XImageByteOrder(x_display) == LSBFirst);
1287     #endif
1288 gbeauche 1.19 #if REAL_ADDRESSING || DIRECT_ADDRESSING
1289 gbeauche 1.36 // Screen_blitter_init() returns TRUE if VOSF is mandatory
1290     // i.e. the framebuffer update function is not Blit_Copy_Raw
1291 gbeauche 1.71 use_vosf = Screen_blitter_init(visualFormat, native_byte_order, depth_of_video_mode(mode));
1292 gbeauche 1.19
1293     if (use_vosf) {
1294 gbeauche 1.39 // Allocate memory for frame buffer (SIZE is extended to page-boundary)
1295     the_host_buffer = the_buffer;
1296 gbeauche 1.79 the_buffer_size = page_extend((height + 2) * bytes_per_row);
1297 gbeauche 1.62 the_buffer_copy = (uint8 *)malloc(the_buffer_size);
1298 gbeauche 1.72 the_buffer = (uint8 *)vm_acquire_mac(the_buffer_size);
1299 gbeauche 1.79
1300     // Fake image for DGA/VOSF mode to know about display bounds
1301     img = new FakeXImage((v_width + 7) & ~7, height, depth_of_video_mode(mode));
1302 gbeauche 1.19 }
1303 gbeauche 1.39 #else
1304 gbeauche 1.19 use_vosf = false;
1305     #endif
1306 gbeauche 1.39 #endif
1307 gbeauche 1.19
1308 cebix 1.66 // Set frame buffer base
1309 cebix 1.42 const_cast<video_mode *>(&mode)->bytes_per_row = bytes_per_row;
1310 cebix 1.66 set_mac_frame_buffer(monitor, mode.depth, true);
1311 cebix 1.44
1312     // Everything went well
1313     init_ok = true;
1314     }
1315    
1316     // Close display
1317     driver_xf86dga::~driver_xf86dga()
1318     {
1319     XF86DGADirectVideo(x_display, screen, 0);
1320 gbeauche 1.56 if (!use_vosf) {
1321     // don't free() the screen buffer in driver_base dtor
1322     the_buffer = NULL;
1323     }
1324     #ifdef ENABLE_VOSF
1325     else {
1326     // don't free() the screen buffer in driver_base dtor
1327     the_host_buffer = NULL;
1328     }
1329     #endif
1330 cebix 1.44 #ifdef ENABLE_XF86_VIDMODE
1331     if (has_vidmode)
1332     XF86VidModeSwitchToMode(x_display, screen, x_video_modes[0]);
1333 cebix 1.1 #endif
1334     }
1335    
1336 cebix 1.44 // Palette has changed
1337     void driver_xf86dga::update_palette(void)
1338     {
1339     driver_dga::update_palette();
1340     current_dga_cmap ^= 1;
1341 cebix 1.66 if (!IsDirectMode(monitor.get_current_mode()) && cmap[current_dga_cmap])
1342 cebix 1.44 XF86DGAInstallColormap(x_display, screen, cmap[current_dga_cmap]);
1343     }
1344    
1345     // Resume emulation
1346     void driver_xf86dga::resume(void)
1347     {
1348     driver_dga::resume();
1349 cebix 1.66 if (!IsDirectMode(monitor.get_current_mode()))
1350 cebix 1.44 XF86DGAInstallColormap(x_display, screen, cmap[current_dga_cmap]);
1351     }
1352     #endif
1353    
1354    
1355     /*
1356     * Initialization
1357     */
1358    
1359 cebix 1.1 // Init keycode translation table
1360     static void keycode_init(void)
1361     {
1362     bool use_kc = PrefsFindBool("keycodes");
1363     if (use_kc) {
1364    
1365     // Get keycode file path from preferences
1366     const char *kc_path = PrefsFindString("keycodefile");
1367    
1368     // Open keycode table
1369     FILE *f = fopen(kc_path ? kc_path : KEYCODE_FILE_NAME, "r");
1370     if (f == NULL) {
1371     char str[256];
1372     sprintf(str, GetString(STR_KEYCODE_FILE_WARN), kc_path ? kc_path : KEYCODE_FILE_NAME, strerror(errno));
1373     WarningAlert(str);
1374     return;
1375     }
1376    
1377     // Default translation table
1378     for (int i=0; i<256; i++)
1379     keycode_table[i] = -1;
1380    
1381     // Search for server vendor string, then read keycodes
1382     const char *vendor = ServerVendor(x_display);
1383 gbeauche 1.75 // Force use of MacX mappings on MacOS X with Apple's X server
1384     int dummy;
1385     if (XQueryExtension(x_display, "Apple-DRI", &dummy, &dummy, &dummy))
1386     vendor = "MacX";
1387 cebix 1.1 bool vendor_found = false;
1388     char line[256];
1389     while (fgets(line, 255, f)) {
1390     // Read line
1391     int len = strlen(line);
1392     if (len == 0)
1393     continue;
1394     line[len-1] = 0;
1395    
1396     // Comments begin with "#" or ";"
1397     if (line[0] == '#' || line[0] == ';' || line[0] == 0)
1398     continue;
1399    
1400     if (vendor_found) {
1401     // Read keycode
1402     int x_code, mac_code;
1403     if (sscanf(line, "%d %d", &x_code, &mac_code) == 2)
1404     keycode_table[x_code & 0xff] = mac_code;
1405     else
1406     break;
1407     } else {
1408     // Search for vendor string
1409     if (strstr(vendor, line) == vendor)
1410     vendor_found = true;
1411     }
1412     }
1413    
1414     // Keycode file completely read
1415     fclose(f);
1416     use_keycodes = vendor_found;
1417    
1418     // Vendor not found? Then display warning
1419     if (!vendor_found) {
1420     char str[256];
1421     sprintf(str, GetString(STR_KEYCODE_VENDOR_WARN), vendor, kc_path ? kc_path : KEYCODE_FILE_NAME);
1422     WarningAlert(str);
1423     return;
1424     }
1425     }
1426     }
1427    
1428 cebix 1.66 // Open display for current mode
1429     bool X11_monitor_desc::video_open(void)
1430 gbeauche 1.19 {
1431 cebix 1.61 D(bug("video_open()\n"));
1432 cebix 1.66 const video_mode &mode = get_current_mode();
1433 cebix 1.61
1434 cebix 1.53 // Find best available X visual
1435     if (!find_visual_for_depth(mode.depth)) {
1436     ErrorAlert(STR_NO_XVISUAL_ERR);
1437     return false;
1438     }
1439    
1440 gbeauche 1.71 // Build up visualFormat structure
1441 gbeauche 1.78 visualFormat.fullscreen = (display_type == DISPLAY_DGA);
1442 gbeauche 1.71 visualFormat.depth = visualInfo.depth;
1443     visualFormat.Rmask = visualInfo.red_mask;
1444     visualFormat.Gmask = visualInfo.green_mask;
1445     visualFormat.Bmask = visualInfo.blue_mask;
1446    
1447 cebix 1.53 // Create color maps
1448     if (color_class == PseudoColor || color_class == DirectColor) {
1449     cmap[0] = XCreateColormap(x_display, rootwin, vis, AllocAll);
1450     cmap[1] = XCreateColormap(x_display, rootwin, vis, AllocAll);
1451 cebix 1.58 } else {
1452     cmap[0] = XCreateColormap(x_display, rootwin, vis, AllocNone);
1453     cmap[1] = XCreateColormap(x_display, rootwin, vis, AllocNone);
1454 cebix 1.53 }
1455    
1456     // Find pixel format of direct modes
1457     if (color_class == DirectColor || color_class == TrueColor) {
1458     rshift = gshift = bshift = 0;
1459     rloss = gloss = bloss = 8;
1460     uint32 mask;
1461     for (mask=vis->red_mask; !(mask&1); mask>>=1)
1462     ++rshift;
1463     for (; mask&1; mask>>=1)
1464     --rloss;
1465     for (mask=vis->green_mask; !(mask&1); mask>>=1)
1466     ++gshift;
1467     for (; mask&1; mask>>=1)
1468     --gloss;
1469     for (mask=vis->blue_mask; !(mask&1); mask>>=1)
1470     ++bshift;
1471     for (; mask&1; mask>>=1)
1472     --bloss;
1473     }
1474    
1475 cebix 1.57 // Preset palette pixel values for CLUT or gamma table
1476 cebix 1.53 if (color_class == DirectColor) {
1477     int num = vis->map_entries;
1478     for (int i=0; i<num; i++) {
1479     int c = (i * 256) / num;
1480 cebix 1.66 x_palette[i].pixel = map_rgb(c, c, c);
1481     x_palette[i].flags = DoRed | DoGreen | DoBlue;
1482 cebix 1.57 }
1483     } else if (color_class == PseudoColor) {
1484     for (int i=0; i<256; i++) {
1485 cebix 1.66 x_palette[i].pixel = i;
1486     x_palette[i].flags = DoRed | DoGreen | DoBlue;
1487 cebix 1.53 }
1488     }
1489    
1490 cebix 1.48 // Load gray ramp to color map
1491 cebix 1.53 int num = (color_class == DirectColor ? vis->map_entries : 256);
1492 cebix 1.48 for (int i=0; i<num; i++) {
1493     int c = (i * 256) / num;
1494 cebix 1.66 x_palette[i].red = c * 0x0101;
1495     x_palette[i].green = c * 0x0101;
1496     x_palette[i].blue = c * 0x0101;
1497 cebix 1.48 }
1498 cebix 1.58 if (color_class == PseudoColor || color_class == DirectColor) {
1499 cebix 1.66 XStoreColors(x_display, cmap[0], x_palette, num);
1500     XStoreColors(x_display, cmap[1], x_palette, num);
1501 cebix 1.48 }
1502    
1503 cebix 1.49 #ifdef ENABLE_VOSF
1504     // Load gray ramp to 8->16/32 expand map
1505 cebix 1.57 if (!IsDirectMode(mode) && xdepth > 8)
1506 cebix 1.49 for (int i=0; i<256; i++)
1507     ExpandMap[i] = map_rgb(i, i, i);
1508     #endif
1509    
1510 cebix 1.44 // Create display driver object of requested type
1511 cebix 1.42 switch (display_type) {
1512     case DISPLAY_WINDOW:
1513 cebix 1.66 drv = new driver_window(*this);
1514 cebix 1.42 break;
1515 cebix 1.44 #ifdef ENABLE_FBDEV_DGA
1516 cebix 1.42 case DISPLAY_DGA:
1517 cebix 1.66 drv = new driver_fbdev(*this);
1518 cebix 1.44 break;
1519 cebix 1.42 #endif
1520 cebix 1.44 #ifdef ENABLE_XF86_DGA
1521     case DISPLAY_DGA:
1522 cebix 1.66 drv = new driver_xf86dga(*this);
1523 cebix 1.42 break;
1524 cebix 1.44 #endif
1525     }
1526     if (drv == NULL)
1527     return false;
1528     if (!drv->init_ok) {
1529     delete drv;
1530     drv = NULL;
1531     return false;
1532 cebix 1.42 }
1533 gbeauche 1.19
1534 cebix 1.42 #ifdef ENABLE_VOSF
1535     if (use_vosf) {
1536 gbeauche 1.56 // Initialize the VOSF system
1537 cebix 1.66 if (!video_vosf_init(*this)) {
1538 cebix 1.44 ErrorAlert(STR_VOSF_INIT_ERR);
1539 cebix 1.42 return false;
1540 gbeauche 1.19 }
1541     }
1542     #endif
1543 cebix 1.42
1544     // Initialize VideoRefresh function
1545     VideoRefreshInit();
1546 cebix 1.44
1547     // Lock down frame buffer
1548 cebix 1.42 XSync(x_display, false);
1549 cebix 1.44 LOCK_FRAME_BUFFER;
1550 cebix 1.42
1551 cebix 1.45 // Start redraw/input thread
1552 gbeauche 1.80 #ifdef USE_PTHREADS_SERVICES
1553 cebix 1.42 redraw_thread_cancel = false;
1554 cebix 1.64 Set_pthread_attr(&redraw_thread_attr, 0);
1555     redraw_thread_active = (pthread_create(&redraw_thread, &redraw_thread_attr, redraw_func, NULL) == 0);
1556 cebix 1.42 if (!redraw_thread_active) {
1557     printf("FATAL: cannot create redraw thread\n");
1558     return false;
1559     }
1560 cebix 1.45 #else
1561     redraw_thread_active = true;
1562 cebix 1.42 #endif
1563    
1564 gbeauche 1.19 return true;
1565     }
1566    
1567 cebix 1.1 bool VideoInit(bool classic)
1568     {
1569 cebix 1.42 classic_mode = classic;
1570    
1571 gbeauche 1.19 #ifdef ENABLE_VOSF
1572     // Zero the mainBuffer structure
1573 cebix 1.44 mainBuffer.dirtyPages = NULL;
1574     mainBuffer.pageInfo = NULL;
1575 gbeauche 1.19 #endif
1576    
1577 cebix 1.16 // Check if X server runs on local machine
1578     local_X11 = (strncmp(XDisplayName(x_display_name), ":", 1) == 0)
1579     || (strncmp(XDisplayName(x_display_name), "unix:", 5) == 0);
1580    
1581 cebix 1.1 // Init keycode translation
1582     keycode_init();
1583    
1584 cebix 1.10 // Read prefs
1585 cebix 1.44 frame_skip = PrefsFindInt32("frameskip");
1586 cebix 1.32 mouse_wheel_mode = PrefsFindInt32("mousewheelmode");
1587     mouse_wheel_lines = PrefsFindInt32("mousewheellines");
1588 cebix 1.10
1589 cebix 1.1 // Find screen and root window
1590     screen = XDefaultScreen(x_display);
1591     rootwin = XRootWindow(x_display, screen);
1592 cebix 1.53
1593     // Get sorted list of available depths
1594     avail_depths = XListDepths(x_display, screen, &num_depths);
1595     if (avail_depths == NULL) {
1596     ErrorAlert(STR_UNSUPP_DEPTH_ERR);
1597     return false;
1598     }
1599 cebix 1.66 std::sort(avail_depths, avail_depths + num_depths);
1600 cebix 1.7
1601 cebix 1.15 #ifdef ENABLE_FBDEV_DGA
1602 cebix 1.7 // Frame buffer name
1603     char fb_name[20];
1604    
1605 cebix 1.44 // Could do fbdev DGA?
1606 cebix 1.7 if ((fbdev_fd = open(FBDEVICE_FILE_NAME, O_RDWR)) != -1)
1607     has_dga = true;
1608     else
1609     has_dga = false;
1610     #endif
1611 cebix 1.12
1612 cebix 1.15 #ifdef ENABLE_XF86_DGA
1613 cebix 1.1 // DGA available?
1614 cebix 1.12 int dga_event_base, dga_error_base;
1615 cebix 1.16 if (local_X11 && XF86DGAQueryExtension(x_display, &dga_event_base, &dga_error_base)) {
1616 cebix 1.5 int dga_flags = 0;
1617     XF86DGAQueryDirectVideo(x_display, screen, &dga_flags);
1618     has_dga = dga_flags & XF86DGADirectPresent;
1619     } else
1620     has_dga = false;
1621 cebix 1.1 #endif
1622    
1623 cebix 1.15 #ifdef ENABLE_XF86_VIDMODE
1624 cebix 1.12 // VidMode available?
1625     int vm_event_base, vm_error_base;
1626     has_vidmode = XF86VidModeQueryExtension(x_display, &vm_event_base, &vm_error_base);
1627     if (has_vidmode)
1628     XF86VidModeGetAllModeLines(x_display, screen, &num_x_video_modes, &x_video_modes);
1629     #endif
1630    
1631 cebix 1.1 // Find black and white colors
1632     XParseColor(x_display, DefaultColormap(x_display, screen), "rgb:00/00/00", &black);
1633     XAllocColor(x_display, DefaultColormap(x_display, screen), &black);
1634     XParseColor(x_display, DefaultColormap(x_display, screen), "rgb:ff/ff/ff", &white);
1635     XAllocColor(x_display, DefaultColormap(x_display, screen), &white);
1636     black_pixel = BlackPixel(x_display, screen);
1637     white_pixel = WhitePixel(x_display, screen);
1638    
1639     // Get screen mode from preferences
1640     const char *mode_str;
1641 cebix 1.42 if (classic_mode)
1642 cebix 1.1 mode_str = "win/512/342";
1643     else
1644     mode_str = PrefsFindString("screen");
1645    
1646 cebix 1.42 // Determine display type and default dimensions
1647     int default_width = 512, default_height = 384;
1648 cebix 1.1 display_type = DISPLAY_WINDOW;
1649     if (mode_str) {
1650 cebix 1.42 if (sscanf(mode_str, "win/%d/%d", &default_width, &default_height) == 2) {
1651 cebix 1.1 display_type = DISPLAY_WINDOW;
1652 cebix 1.15 #ifdef ENABLE_FBDEV_DGA
1653 cebix 1.42 } else if (has_dga && sscanf(mode_str, "dga/%19s", fb_name) == 1) {
1654     display_type = DISPLAY_DGA;
1655 cebix 1.44 default_width = -1; default_height = -1; // use entire screen
1656     #endif
1657     #ifdef ENABLE_XF86_DGA
1658 cebix 1.42 } else if (has_dga && sscanf(mode_str, "dga/%d/%d", &default_width, &default_height) == 2) {
1659 cebix 1.1 display_type = DISPLAY_DGA;
1660 cebix 1.7 #endif
1661 cebix 1.42 }
1662 cebix 1.1 }
1663 cebix 1.42 if (default_width <= 0)
1664     default_width = DisplayWidth(x_display, screen);
1665     else if (default_width > DisplayWidth(x_display, screen))
1666     default_width = DisplayWidth(x_display, screen);
1667     if (default_height <= 0)
1668     default_height = DisplayHeight(x_display, screen);
1669     else if (default_height > DisplayHeight(x_display, screen))
1670     default_height = DisplayHeight(x_display, screen);
1671 cebix 1.1
1672 cebix 1.48 // Mac screen depth follows X depth
1673 cebix 1.53 video_depth default_depth = VDEPTH_1BIT;
1674     switch (DefaultDepth(x_display, screen)) {
1675     case 8:
1676     default_depth = VDEPTH_8BIT;
1677     break;
1678     case 15: case 16:
1679     default_depth = VDEPTH_16BIT;
1680     break;
1681     case 24: case 32:
1682     default_depth = VDEPTH_32BIT;
1683     break;
1684     }
1685 cebix 1.1
1686 cebix 1.42 // Construct list of supported modes
1687     if (display_type == DISPLAY_WINDOW) {
1688     if (classic)
1689 cebix 1.48 add_mode(512, 342, 0x80, 64, VDEPTH_1BIT);
1690 cebix 1.42 else {
1691 cebix 1.53 for (unsigned d=VDEPTH_1BIT; d<=VDEPTH_32BIT; d++) {
1692     if (find_visual_for_depth(video_depth(d)))
1693     add_window_modes(video_depth(d));
1694 cebix 1.49 }
1695 gbeauche 1.19 }
1696 cebix 1.42 } else
1697 cebix 1.48 add_mode(default_width, default_height, 0x80, TrivialBytesPerRow(default_width, default_depth), default_depth);
1698 cebix 1.53 if (VideoModes.empty()) {
1699     ErrorAlert(STR_NO_XVISUAL_ERR);
1700     return false;
1701     }
1702 cebix 1.66
1703     // Find requested default mode with specified dimensions
1704     uint32 default_id;
1705     std::vector<video_mode>::const_iterator i, end = VideoModes.end();
1706     for (i = VideoModes.begin(); i != end; ++i) {
1707     if (i->x == default_width && i->y == default_height && i->depth == default_depth) {
1708     default_id = i->resolution_id;
1709     break;
1710     }
1711     }
1712     if (i == end) { // not found, use first available mode
1713     default_depth = VideoModes[0].depth;
1714     default_id = VideoModes[0].resolution_id;
1715     }
1716 gbeauche 1.19
1717 cebix 1.53 #if DEBUG
1718     D(bug("Available video modes:\n"));
1719 cebix 1.66 for (i = VideoModes.begin(); i != end; ++i) {
1720 cebix 1.53 int bits = 1 << i->depth;
1721     if (bits == 16)
1722     bits = 15;
1723     else if (bits == 32)
1724     bits = 24;
1725     D(bug(" %dx%d (ID %02x), %d colors\n", i->x, i->y, i->resolution_id, 1 << bits));
1726     }
1727     #endif
1728    
1729 cebix 1.66 // Create X11_monitor_desc for this (the only) display
1730     X11_monitor_desc *monitor = new X11_monitor_desc(VideoModes, default_depth, default_id);
1731     VideoMonitors.push_back(monitor);
1732    
1733     // Open display
1734     return monitor->video_open();
1735 cebix 1.1 }
1736    
1737    
1738     /*
1739     * Deinitialization
1740     */
1741    
1742 cebix 1.42 // Close display
1743 cebix 1.66 void X11_monitor_desc::video_close(void)
1744 cebix 1.1 {
1745 cebix 1.61 D(bug("video_close()\n"));
1746    
1747 cebix 1.45 // Stop redraw thread
1748 gbeauche 1.80 #ifdef USE_PTHREADS_SERVICES
1749 cebix 1.1 if (redraw_thread_active) {
1750     redraw_thread_cancel = true;
1751 gbeauche 1.77 redraw_thread_cancel_ack = false;
1752 cebix 1.1 pthread_join(redraw_thread, NULL);
1753 gbeauche 1.77 while (!redraw_thread_cancel_ack) ;
1754 cebix 1.1 }
1755 cebix 1.15 #endif
1756 cebix 1.45 redraw_thread_active = false;
1757 cebix 1.1
1758     // Unlock frame buffer
1759 cebix 1.29 UNLOCK_FRAME_BUFFER;
1760 cebix 1.44 XSync(x_display, false);
1761 cebix 1.61 D(bug(" frame buffer unlocked\n"));
1762 cebix 1.1
1763 gbeauche 1.19 #ifdef ENABLE_VOSF
1764     if (use_vosf) {
1765 gbeauche 1.56 // Deinitialize VOSF
1766     video_vosf_exit();
1767 cebix 1.1 }
1768 cebix 1.42 #endif
1769 cebix 1.44
1770     // Close display
1771     delete drv;
1772     drv = NULL;
1773    
1774     // Free colormaps
1775     if (cmap[0]) {
1776     XFreeColormap(x_display, cmap[0]);
1777     cmap[0] = 0;
1778     }
1779     if (cmap[1]) {
1780     XFreeColormap(x_display, cmap[1]);
1781     cmap[1] = 0;
1782     }
1783 cebix 1.53 }
1784    
1785     void VideoExit(void)
1786     {
1787 cebix 1.66 // Close displays
1788     vector<monitor_desc *>::iterator i, end = VideoMonitors.end();
1789     for (i = VideoMonitors.begin(); i != end; ++i)
1790     dynamic_cast<X11_monitor_desc *>(*i)->video_close();
1791 cebix 1.44
1792     #ifdef ENABLE_XF86_VIDMODE
1793     // Free video mode list
1794     if (x_video_modes) {
1795     XFree(x_video_modes);
1796     x_video_modes = NULL;
1797     }
1798     #endif
1799    
1800     #ifdef ENABLE_FBDEV_DGA
1801     // Close framebuffer device
1802     if (fbdev_fd >= 0) {
1803     close(fbdev_fd);
1804     fbdev_fd = -1;
1805     }
1806     #endif
1807 cebix 1.53
1808     // Free depth list
1809     if (avail_depths) {
1810     XFree(avail_depths);
1811     avail_depths = NULL;
1812     }
1813 cebix 1.1 }
1814    
1815    
1816     /*
1817     * Close down full-screen mode (if bringing up error alerts is unsafe while in full-screen mode)
1818     */
1819    
1820     void VideoQuitFullScreen(void)
1821     {
1822     D(bug("VideoQuitFullScreen()\n"));
1823 cebix 1.44 quit_full_screen = true;
1824 cebix 1.1 }
1825    
1826    
1827     /*
1828     * Mac VBL interrupt
1829     */
1830    
1831     void VideoInterrupt(void)
1832     {
1833     // Emergency quit requested? Then quit
1834     if (emerg_quit)
1835     QuitEmulator();
1836    
1837     // Temporarily give up frame buffer lock (this is the point where
1838     // we are suspended when the user presses Ctrl-Tab)
1839 cebix 1.29 UNLOCK_FRAME_BUFFER;
1840     LOCK_FRAME_BUFFER;
1841 cebix 1.1 }
1842    
1843    
1844     /*
1845     * Set palette
1846     */
1847    
1848 cebix 1.66 void X11_monitor_desc::set_palette(uint8 *pal, int num_in)
1849 cebix 1.1 {
1850 cebix 1.66 const video_mode &mode = get_current_mode();
1851    
1852 cebix 1.29 LOCK_PALETTE;
1853 cebix 1.1
1854     // Convert colors to XColor array
1855 cebix 1.50 int num_out = 256;
1856 cebix 1.54 bool stretch = false;
1857 cebix 1.66 if (IsDirectMode(mode)) {
1858 cebix 1.50 // If X is in 565 mode we have to stretch the gamma table from 32 to 64 entries
1859 cebix 1.47 num_out = vis->map_entries;
1860 cebix 1.54 stretch = true;
1861 cebix 1.46 }
1862 cebix 1.66 XColor *p = x_palette;
1863 cebix 1.46 for (int i=0; i<num_out; i++) {
1864 cebix 1.54 int c = (stretch ? (i * num_in) / num_out : i);
1865 cebix 1.46 p->red = pal[c*3 + 0] * 0x0101;
1866     p->green = pal[c*3 + 1] * 0x0101;
1867     p->blue = pal[c*3 + 2] * 0x0101;
1868     p++;
1869 cebix 1.1 }
1870 cebix 1.49
1871     #ifdef ENABLE_VOSF
1872     // Recalculate pixel color expansion map
1873 cebix 1.66 if (!IsDirectMode(mode) && xdepth > 8) {
1874 cebix 1.50 for (int i=0; i<256; i++) {
1875 cebix 1.51 int c = i & (num_in-1); // If there are less than 256 colors, we repeat the first entries (this makes color expansion easier)
1876 cebix 1.50 ExpandMap[i] = map_rgb(pal[c*3+0], pal[c*3+1], pal[c*3+2]);
1877     }
1878 cebix 1.49
1879     // We have to redraw everything because the interpretation of pixel values changed
1880     LOCK_VOSF;
1881     PFLAG_SET_ALL;
1882     UNLOCK_VOSF;
1883 cebix 1.66 memset(the_buffer_copy, 0, mode.bytes_per_row * mode.y);
1884 cebix 1.49 }
1885     #endif
1886 cebix 1.1
1887     // Tell redraw thread to change palette
1888 cebix 1.66 x_palette_changed = true;
1889 cebix 1.1
1890 cebix 1.29 UNLOCK_PALETTE;
1891 cebix 1.1 }
1892    
1893    
1894     /*
1895 cebix 1.41 * Switch video mode
1896     */
1897    
1898 cebix 1.66 void X11_monitor_desc::switch_to_current_mode(void)
1899 cebix 1.41 {
1900 cebix 1.44 // Close and reopen display
1901 cebix 1.42 video_close();
1902 cebix 1.66 video_open();
1903 cebix 1.41
1904 cebix 1.44 if (drv == NULL) {
1905     ErrorAlert(STR_OPEN_WINDOW_ERR);
1906     QuitEmulator();
1907 cebix 1.1 }
1908     }
1909    
1910    
1911     /*
1912 cebix 1.51 * Translate key event to Mac keycode, returns -1 if no keycode was found
1913     * and -2 if the key was recognized as a hotkey
1914 cebix 1.1 */
1915    
1916 cebix 1.51 static int kc_decode(KeySym ks, bool key_down)
1917 cebix 1.1 {
1918     switch (ks) {
1919     case XK_A: case XK_a: return 0x00;
1920     case XK_B: case XK_b: return 0x0b;
1921     case XK_C: case XK_c: return 0x08;
1922     case XK_D: case XK_d: return 0x02;
1923     case XK_E: case XK_e: return 0x0e;
1924     case XK_F: case XK_f: return 0x03;
1925     case XK_G: case XK_g: return 0x05;
1926     case XK_H: case XK_h: return 0x04;
1927     case XK_I: case XK_i: return 0x22;
1928     case XK_J: case XK_j: return 0x26;
1929     case XK_K: case XK_k: return 0x28;
1930     case XK_L: case XK_l: return 0x25;
1931     case XK_M: case XK_m: return 0x2e;
1932     case XK_N: case XK_n: return 0x2d;
1933     case XK_O: case XK_o: return 0x1f;
1934     case XK_P: case XK_p: return 0x23;
1935     case XK_Q: case XK_q: return 0x0c;
1936     case XK_R: case XK_r: return 0x0f;
1937     case XK_S: case XK_s: return 0x01;
1938     case XK_T: case XK_t: return 0x11;
1939     case XK_U: case XK_u: return 0x20;
1940     case XK_V: case XK_v: return 0x09;
1941     case XK_W: case XK_w: return 0x0d;
1942     case XK_X: case XK_x: return 0x07;
1943     case XK_Y: case XK_y: return 0x10;
1944     case XK_Z: case XK_z: return 0x06;
1945    
1946     case XK_1: case XK_exclam: return 0x12;
1947     case XK_2: case XK_at: return 0x13;
1948     case XK_3: case XK_numbersign: return 0x14;
1949     case XK_4: case XK_dollar: return 0x15;
1950     case XK_5: case XK_percent: return 0x17;
1951     case XK_6: return 0x16;
1952     case XK_7: return 0x1a;
1953     case XK_8: return 0x1c;
1954     case XK_9: return 0x19;
1955     case XK_0: return 0x1d;
1956    
1957     case XK_grave: case XK_asciitilde: return 0x0a;
1958     case XK_minus: case XK_underscore: return 0x1b;
1959     case XK_equal: case XK_plus: return 0x18;
1960     case XK_bracketleft: case XK_braceleft: return 0x21;
1961     case XK_bracketright: case XK_braceright: return 0x1e;
1962     case XK_backslash: case XK_bar: return 0x2a;
1963     case XK_semicolon: case XK_colon: return 0x29;
1964     case XK_apostrophe: case XK_quotedbl: return 0x27;
1965     case XK_comma: case XK_less: return 0x2b;
1966     case XK_period: case XK_greater: return 0x2f;
1967     case XK_slash: case XK_question: return 0x2c;
1968    
1969 cebix 1.51 case XK_Tab: if (ctrl_down) {if (key_down) drv->suspend(); return -2;} else return 0x30;
1970 cebix 1.1 case XK_Return: return 0x24;
1971     case XK_space: return 0x31;
1972     case XK_BackSpace: return 0x33;
1973    
1974     case XK_Delete: return 0x75;
1975     case XK_Insert: return 0x72;
1976     case XK_Home: case XK_Help: return 0x73;
1977     case XK_End: return 0x77;
1978     #ifdef __hpux
1979     case XK_Prior: return 0x74;
1980     case XK_Next: return 0x79;
1981     #else
1982     case XK_Page_Up: return 0x74;
1983     case XK_Page_Down: return 0x79;
1984     #endif
1985    
1986     case XK_Control_L: return 0x36;
1987     case XK_Control_R: return 0x36;
1988     case XK_Shift_L: return 0x38;
1989     case XK_Shift_R: return 0x38;
1990     case XK_Alt_L: return 0x37;
1991     case XK_Alt_R: return 0x37;
1992     case XK_Meta_L: return 0x3a;
1993     case XK_Meta_R: return 0x3a;
1994     case XK_Menu: return 0x32;
1995     case XK_Caps_Lock: return 0x39;
1996     case XK_Num_Lock: return 0x47;
1997    
1998     case XK_Up: return 0x3e;
1999     case XK_Down: return 0x3d;
2000     case XK_Left: return 0x3b;
2001     case XK_Right: return 0x3c;
2002    
2003 cebix 1.51 case XK_Escape: if (ctrl_down) {if (key_down) { quit_full_screen = true; emerg_quit = true; } return -2;} else return 0x35;
2004 cebix 1.1
2005 cebix 1.51 case XK_F1: if (ctrl_down) {if (key_down) SysMountFirstFloppy(); return -2;} else return 0x7a;
2006 cebix 1.1 case XK_F2: return 0x78;
2007     case XK_F3: return 0x63;
2008     case XK_F4: return 0x76;
2009 cebix 1.51 case XK_F5: if (ctrl_down) {if (key_down) drv->toggle_mouse_grab(); return -2;} else return 0x60;
2010 cebix 1.1 case XK_F6: return 0x61;
2011     case XK_F7: return 0x62;
2012     case XK_F8: return 0x64;
2013     case XK_F9: return 0x65;
2014     case XK_F10: return 0x6d;
2015     case XK_F11: return 0x67;
2016     case XK_F12: return 0x6f;
2017    
2018     case XK_Print: return 0x69;
2019     case XK_Scroll_Lock: return 0x6b;
2020     case XK_Pause: return 0x71;
2021    
2022     #if defined(XK_KP_Prior) && defined(XK_KP_Left) && defined(XK_KP_Insert) && defined (XK_KP_End)
2023     case XK_KP_0: case XK_KP_Insert: return 0x52;
2024     case XK_KP_1: case XK_KP_End: return 0x53;
2025     case XK_KP_2: case XK_KP_Down: return 0x54;
2026     case XK_KP_3: case XK_KP_Next: return 0x55;
2027     case XK_KP_4: case XK_KP_Left: return 0x56;
2028     case XK_KP_5: case XK_KP_Begin: return 0x57;
2029     case XK_KP_6: case XK_KP_Right: return 0x58;
2030     case XK_KP_7: case XK_KP_Home: return 0x59;
2031     case XK_KP_8: case XK_KP_Up: return 0x5b;
2032     case XK_KP_9: case XK_KP_Prior: return 0x5c;
2033     case XK_KP_Decimal: case XK_KP_Delete: return 0x41;
2034     #else
2035     case XK_KP_0: return 0x52;
2036     case XK_KP_1: return 0x53;
2037     case XK_KP_2: return 0x54;
2038     case XK_KP_3: return 0x55;
2039     case XK_KP_4: return 0x56;
2040     case XK_KP_5: return 0x57;
2041     case XK_KP_6: return 0x58;
2042     case XK_KP_7: return 0x59;
2043     case XK_KP_8: return 0x5b;
2044     case XK_KP_9: return 0x5c;
2045     case XK_KP_Decimal: return 0x41;
2046     #endif
2047     case XK_KP_Add: return 0x45;
2048     case XK_KP_Subtract: return 0x4e;
2049     case XK_KP_Multiply: return 0x43;
2050     case XK_KP_Divide: return 0x4b;
2051     case XK_KP_Enter: return 0x4c;
2052     case XK_KP_Equal: return 0x51;
2053     }
2054     return -1;
2055     }
2056    
2057 cebix 1.51 static int event2keycode(XKeyEvent &ev, bool key_down)
2058 cebix 1.1 {
2059     KeySym ks;
2060     int i = 0;
2061    
2062     do {
2063 cebix 1.29 ks = XLookupKeysym(&ev, i++);
2064 cebix 1.51 int as = kc_decode(ks, key_down);
2065     if (as >= 0)
2066     return as;
2067     if (as == -2)
2068 cebix 1.1 return as;
2069     } while (ks != NoSymbol);
2070    
2071     return -1;
2072     }
2073    
2074    
2075     /*
2076     * X event handling
2077     */
2078    
2079     static void handle_events(void)
2080     {
2081 gbeauche 1.73 for (;;) {
2082 cebix 1.29 XEvent event;
2083 gbeauche 1.73 XDisplayLock();
2084    
2085     if (!XCheckMaskEvent(x_display, eventmask, &event)) {
2086     // Handle clipboard events
2087     if (XCheckTypedEvent(x_display, SelectionRequest, &event))
2088     ClipboardSelectionRequest(&event.xselectionrequest);
2089     else if (XCheckTypedEvent(x_display, SelectionClear, &event))
2090     ClipboardSelectionClear(&event.xselectionclear);
2091 cebix 1.1
2092 gbeauche 1.73 // Window "close" widget clicked
2093     else if (XCheckTypedEvent(x_display, ClientMessage, &event)) {
2094     if (event.xclient.format == 32 && event.xclient.data.l[0] == WM_DELETE_WINDOW) {
2095     ADBKeyDown(0x7f); // Power key
2096     ADBKeyUp(0x7f);
2097     }
2098     }
2099     XDisplayUnlock();
2100     break;
2101     }
2102    
2103 cebix 1.1 switch (event.type) {
2104 cebix 1.57
2105 cebix 1.1 // Mouse button
2106     case ButtonPress: {
2107 cebix 1.29 unsigned int button = event.xbutton.button;
2108 cebix 1.1 if (button < 4)
2109     ADBMouseDown(button - 1);
2110 cebix 1.10 else if (button < 6) { // Wheel mouse
2111     if (mouse_wheel_mode == 0) {
2112     int key = (button == 5) ? 0x79 : 0x74; // Page up/down
2113     ADBKeyDown(key);
2114     ADBKeyUp(key);
2115     } else {
2116     int key = (button == 5) ? 0x3d : 0x3e; // Cursor up/down
2117     for(int i=0; i<mouse_wheel_lines; i++) {
2118     ADBKeyDown(key);
2119     ADBKeyUp(key);
2120     }
2121     }
2122     }
2123 cebix 1.1 break;
2124     }
2125     case ButtonRelease: {
2126 cebix 1.29 unsigned int button = event.xbutton.button;
2127 cebix 1.1 if (button < 4)
2128     ADBMouseUp(button - 1);
2129     break;
2130     }
2131    
2132     // Mouse moved
2133     case MotionNotify:
2134 cebix 1.51 drv->mouse_moved(event.xmotion.x, event.xmotion.y);
2135 cebix 1.1 break;
2136    
2137 cebix 1.57 // Mouse entered window
2138     case EnterNotify:
2139     if (event.xcrossing.mode != NotifyGrab && event.xcrossing.mode != NotifyUngrab)
2140     drv->mouse_moved(event.xmotion.x, event.xmotion.y);
2141     break;
2142    
2143 cebix 1.1 // Keyboard
2144     case KeyPress: {
2145 cebix 1.51 int code = -1;
2146 cebix 1.1 if (use_keycodes) {
2147 cebix 1.51 if (event2keycode(event.xkey, true) != -2) // This is called to process the hotkeys
2148     code = keycode_table[event.xkey.keycode & 0xff];
2149 cebix 1.1 } else
2150 cebix 1.51 code = event2keycode(event.xkey, true);
2151     if (code >= 0) {
2152 cebix 1.1 if (!emul_suspended) {
2153 cebix 1.3 if (code == 0x39) { // Caps Lock pressed
2154     if (caps_on) {
2155     ADBKeyUp(code);
2156     caps_on = false;
2157     } else {
2158     ADBKeyDown(code);
2159     caps_on = true;
2160     }
2161     } else
2162     ADBKeyDown(code);
2163 cebix 1.1 if (code == 0x36)
2164     ctrl_down = true;
2165     } else {
2166     if (code == 0x31)
2167 cebix 1.44 drv->resume(); // Space wakes us up
2168 cebix 1.1 }
2169     }
2170     break;
2171     }
2172     case KeyRelease: {
2173 cebix 1.51 int code = -1;
2174 cebix 1.1 if (use_keycodes) {
2175 cebix 1.51 if (event2keycode(event.xkey, false) != -2) // This is called to process the hotkeys
2176     code = keycode_table[event.xkey.keycode & 0xff];
2177 cebix 1.1 } else
2178 cebix 1.51 code = event2keycode(event.xkey, false);
2179     if (code >= 0 && code != 0x39) { // Don't propagate Caps Lock releases
2180 cebix 1.1 ADBKeyUp(code);
2181     if (code == 0x36)
2182     ctrl_down = false;
2183     }
2184     break;
2185     }
2186    
2187     // Hidden parts exposed, force complete refresh of window
2188     case Expose:
2189 cebix 1.13 if (display_type == DISPLAY_WINDOW) {
2190 cebix 1.66 const video_mode &mode = VideoMonitors[0]->get_current_mode();
2191 gbeauche 1.19 #ifdef ENABLE_VOSF
2192 gbeauche 1.21 if (use_vosf) { // VOSF refresh
2193 cebix 1.29 LOCK_VOSF;
2194 gbeauche 1.20 PFLAG_SET_ALL;
2195 cebix 1.29 UNLOCK_VOSF;
2196 cebix 1.66 memset(the_buffer_copy, 0, mode.bytes_per_row * mode.y);
2197 gbeauche 1.21 }
2198     else
2199 gbeauche 1.20 #endif
2200 gbeauche 1.21 if (frame_skip == 0) { // Dynamic refresh
2201     int x1, y1;
2202     for (y1=0; y1<16; y1++)
2203     for (x1=0; x1<16; x1++)
2204     updt_box[x1][y1] = true;
2205     nr_boxes = 16 * 16;
2206     } else // Static refresh
2207 cebix 1.66 memset(the_buffer_copy, 0, mode.bytes_per_row * mode.y);
2208 cebix 1.13 }
2209 cebix 1.26 break;
2210 gbeauche 1.73 }
2211 cebix 1.26
2212 gbeauche 1.73 XDisplayUnlock();
2213 cebix 1.1 }
2214     }
2215    
2216    
2217     /*
2218     * Window display update
2219     */
2220    
2221 cebix 1.16 // Dynamic display update (variable frame rate for each box)
2222 cebix 1.44 static void update_display_dynamic(int ticker, driver_window *drv)
2223 cebix 1.1 {
2224 cebix 1.17 int y1, y2, y2s, y2a, i, x1, xm, xmo, ymo, yo, yi, yil, xi;
2225 cebix 1.13 int xil = 0;
2226     int rxm = 0, rxmo = 0;
2227 cebix 1.66 const video_mode &mode = drv->monitor.get_current_mode();
2228     int bytes_per_row = mode.bytes_per_row;
2229     int bytes_per_pixel = mode.bytes_per_row / mode.x;
2230     int rx = mode.bytes_per_row / 16;
2231     int ry = mode.y / 16;
2232 cebix 1.13 int max_box;
2233    
2234     y2s = sm_uptd[ticker % 8];
2235     y2a = 8;
2236     for (i = 0; i < 6; i++)
2237     if (ticker % (2 << i))
2238 cebix 1.1 break;
2239 cebix 1.13 max_box = sm_no_boxes[i];
2240 cebix 1.1
2241 cebix 1.13 if (y2a) {
2242     for (y1=0; y1<16; y1++) {
2243     for (y2=y2s; y2 < ry; y2 += y2a) {
2244     i = ((y1 * ry) + y2) * bytes_per_row;
2245     for (x1=0; x1<16; x1++, i += rx) {
2246     if (updt_box[x1][y1] == false) {
2247     if (memcmp(&the_buffer_copy[i], &the_buffer[i], rx)) {
2248     updt_box[x1][y1] = true;
2249     nr_boxes++;
2250     }
2251 cebix 1.1 }
2252     }
2253     }
2254 cebix 1.13 }
2255     }
2256 cebix 1.1
2257 gbeauche 1.73 XDisplayLock();
2258 cebix 1.13 if ((nr_boxes <= max_box) && (nr_boxes)) {
2259     for (y1=0; y1<16; y1++) {
2260     for (x1=0; x1<16; x1++) {
2261     if (updt_box[x1][y1] == true) {
2262     if (rxm == 0)
2263     xm = x1;
2264     rxm += rx;
2265     updt_box[x1][y1] = false;
2266 cebix 1.1 }
2267 cebix 1.13 if (((updt_box[x1+1][y1] == false) || (x1 == 15)) && (rxm)) {
2268     if ((rxmo != rxm) || (xmo != xm) || (yo != y1 - 1)) {
2269     if (rxmo) {
2270     xi = xmo * rx;
2271     yi = ymo * ry;
2272     xil = rxmo;
2273     yil = (yo - ymo +1) * ry;
2274     }
2275     rxmo = rxm;
2276     xmo = xm;
2277     ymo = y1;
2278 cebix 1.1 }
2279 cebix 1.13 rxm = 0;
2280     yo = y1;
2281     }
2282     if (xil) {
2283     i = (yi * bytes_per_row) + xi;
2284     for (y2=0; y2 < yil; y2++, i += bytes_per_row)
2285     memcpy(&the_buffer_copy[i], &the_buffer[i], xil);
2286 cebix 1.66 if (mode.depth == VDEPTH_1BIT) {
2287 cebix 1.44 if (drv->have_shm)
2288     XShmPutImage(x_display, drv->w, drv->gc, drv->img, xi * 8, yi, xi * 8, yi, xil * 8, yil, 0);
2289 cebix 1.13 else
2290 cebix 1.44 XPutImage(x_display, drv->w, drv->gc, drv->img, xi * 8, yi, xi * 8, yi, xil * 8, yil);
2291 cebix 1.13 } else {
2292 cebix 1.44 if (drv->have_shm)
2293     XShmPutImage(x_display, drv->w, drv->gc, drv->img, xi / bytes_per_pixel, yi, xi / bytes_per_pixel, yi, xil / bytes_per_pixel, yil, 0);
2294 cebix 1.13 else
2295 cebix 1.44 XPutImage(x_display, drv->w, drv->gc, drv->img, xi / bytes_per_pixel, yi, xi / bytes_per_pixel, yi, xil / bytes_per_pixel, yil);
2296 cebix 1.1 }
2297 cebix 1.13 xil = 0;
2298 cebix 1.1 }
2299 cebix 1.13 if ((x1 == 15) && (y1 == 15) && (rxmo)) {
2300     x1--;
2301     xi = xmo * rx;
2302     yi = ymo * ry;
2303     xil = rxmo;
2304     yil = (yo - ymo +1) * ry;
2305     rxmo = 0;
2306 cebix 1.1 }
2307     }
2308     }
2309 cebix 1.13 nr_boxes = 0;
2310 cebix 1.1 }
2311 gbeauche 1.73 XDisplayUnlock();
2312 cebix 1.1 }
2313    
2314 cebix 1.16 // Static display update (fixed frame rate, but incremental)
2315 cebix 1.44 static void update_display_static(driver_window *drv)
2316 cebix 1.16 {
2317     // Incremental update code
2318 cebix 1.60 unsigned wide = 0, high = 0, x1, x2, y1, y2, i, j;
2319 cebix 1.66 const video_mode &mode = drv->monitor.get_current_mode();
2320     int bytes_per_row = mode.bytes_per_row;
2321     int bytes_per_pixel = mode.bytes_per_row / mode.x;
2322 cebix 1.16 uint8 *p, *p2;
2323    
2324     // Check for first line from top and first line from bottom that have changed
2325     y1 = 0;
2326 cebix 1.66 for (j=0; j<mode.y; j++) {
2327 cebix 1.16 if (memcmp(&the_buffer[j * bytes_per_row], &the_buffer_copy[j * bytes_per_row], bytes_per_row)) {
2328     y1 = j;
2329     break;
2330     }
2331     }
2332     y2 = y1 - 1;
2333 cebix 1.66 for (j=mode.y-1; j>=y1; j--) {
2334 cebix 1.16 if (memcmp(&the_buffer[j * bytes_per_row], &the_buffer_copy[j * bytes_per_row], bytes_per_row)) {
2335     y2 = j;
2336     break;
2337     }
2338     }
2339     high = y2 - y1 + 1;
2340    
2341     // Check for first column from left and first column from right that have changed
2342     if (high) {
2343 cebix 1.66 if (mode.depth == VDEPTH_1BIT) {
2344     x1 = mode.x - 1;
2345 cebix 1.16 for (j=y1; j<=y2; j++) {
2346     p = &the_buffer[j * bytes_per_row];
2347     p2 = &the_buffer_copy[j * bytes_per_row];
2348     for (i=0; i<(x1>>3); i++) {
2349     if (*p != *p2) {
2350     x1 = i << 3;
2351     break;
2352     }
2353 cebix 1.18 p++; p2++;
2354 cebix 1.16 }
2355     }
2356     x2 = x1;
2357     for (j=y1; j<=y2; j++) {
2358     p = &the_buffer[j * bytes_per_row];
2359     p2 = &the_buffer_copy[j * bytes_per_row];
2360     p += bytes_per_row;
2361     p2 += bytes_per_row;
2362 cebix 1.66 for (i=(mode.x>>3); i>(x2>>3); i--) {
2363 cebix 1.18 p--; p2--;
2364 cebix 1.16 if (*p != *p2) {
2365 cebix 1.25 x2 = (i << 3) + 7;
2366 cebix 1.16 break;
2367     }
2368     }
2369     }
2370 cebix 1.25 wide = x2 - x1 + 1;
2371 cebix 1.16
2372     // Update copy of the_buffer
2373     if (high && wide) {
2374     for (j=y1; j<=y2; j++) {
2375     i = j * bytes_per_row + (x1 >> 3);
2376     memcpy(the_buffer_copy + i, the_buffer + i, wide >> 3);
2377     }
2378     }
2379    
2380     } else {
2381 cebix 1.66 x1 = mode.x;
2382 cebix 1.16 for (j=y1; j<=y2; j++) {
2383     p = &the_buffer[j * bytes_per_row];
2384     p2 = &the_buffer_copy[j * bytes_per_row];
2385 cebix 1.18 for (i=0; i<x1*bytes_per_pixel; i++) {
2386     if (*p != *p2) {
2387     x1 = i / bytes_per_pixel;
2388 cebix 1.16 break;
2389     }
2390 cebix 1.18 p++; p2++;
2391 cebix 1.16 }
2392     }
2393     x2 = x1;
2394     for (j=y1; j<=y2; j++) {
2395     p = &the_buffer[j * bytes_per_row];
2396     p2 = &the_buffer_copy[j * bytes_per_row];
2397     p += bytes_per_row;
2398     p2 += bytes_per_row;
2399 cebix 1.66 for (i=mode.x*bytes_per_pixel; i>x2*bytes_per_pixel; i--) {
2400 cebix 1.18 p--;
2401     p2--;
2402     if (*p != *p2) {
2403     x2 = i / bytes_per_pixel;
2404 cebix 1.16 break;
2405     }
2406     }
2407     }
2408     wide = x2 - x1;
2409    
2410     // Update copy of the_buffer
2411     if (high && wide) {
2412     for (j=y1; j<=y2; j++) {
2413     i = j * bytes_per_row + x1 * bytes_per_pixel;
2414     memcpy(the_buffer_copy + i, the_buffer + i, bytes_per_pixel * wide);
2415     }
2416     }
2417     }
2418     }
2419    
2420     // Refresh display
2421 gbeauche 1.73 XDisplayLock();
2422 cebix 1.16 if (high && wide) {
2423 cebix 1.44 if (drv->have_shm)
2424     XShmPutImage(x_display, drv->w, drv->gc, drv->img, x1, y1, x1, y1, wide, high, 0);
2425 cebix 1.16 else
2426 cebix 1.44 XPutImage(x_display, drv->w, drv->gc, drv->img, x1, y1, x1, y1, wide, high);
2427 cebix 1.16 }
2428 gbeauche 1.73 XDisplayUnlock();
2429 cebix 1.16 }
2430    
2431 cebix 1.1
2432     /*
2433 gbeauche 1.19 * Screen refresh functions
2434     */
2435    
2436 gbeauche 1.34 // We suggest the compiler to inline the next two functions so that it
2437     // may specialise the code according to the current screen depth and
2438 gbeauche 1.35 // display type. A clever compiler would do that job by itself though...
2439 gbeauche 1.34
2440     // NOTE: update_display_vosf is inlined too
2441 gbeauche 1.19
2442     static inline void possibly_quit_dga_mode()
2443     {
2444 cebix 1.51 // Quit DGA mode if requested (something terrible has happened and we
2445     // want to give control back to the user)
2446 gbeauche 1.19 if (quit_full_screen) {
2447     quit_full_screen = false;
2448 cebix 1.44 delete drv;
2449     drv = NULL;
2450 gbeauche 1.19 }
2451     }
2452    
2453 cebix 1.51 static inline void possibly_ungrab_mouse()
2454     {
2455     // Ungrab mouse if requested (something terrible has happened and we
2456     // want to give control back to the user)
2457     if (quit_full_screen) {
2458     quit_full_screen = false;
2459     if (drv)
2460     drv->ungrab_mouse();
2461     }
2462     }
2463    
2464 cebix 1.44 static inline void handle_palette_changes(void)
2465 gbeauche 1.19 {
2466 cebix 1.29 LOCK_PALETTE;
2467    
2468 cebix 1.66 if (x_palette_changed) {
2469     x_palette_changed = false;
2470 gbeauche 1.73 XDisplayLock();
2471 cebix 1.44 drv->update_palette();
2472 gbeauche 1.73 XDisplayUnlock();
2473 gbeauche 1.19 }
2474 cebix 1.29
2475     UNLOCK_PALETTE;
2476 gbeauche 1.19 }
2477    
2478     static void video_refresh_dga(void)
2479     {
2480     // Quit DGA mode if requested
2481     possibly_quit_dga_mode();
2482     }
2483    
2484 cebix 1.23 #ifdef ENABLE_VOSF
2485 gbeauche 1.19 #if REAL_ADDRESSING || DIRECT_ADDRESSING
2486     static void video_refresh_dga_vosf(void)
2487     {
2488     // Quit DGA mode if requested
2489     possibly_quit_dga_mode();
2490    
2491     // Update display (VOSF variant)
2492     static int tick_counter = 0;
2493     if (++tick_counter >= frame_skip) {
2494     tick_counter = 0;
2495 gbeauche 1.36 if (mainBuffer.dirty) {
2496     LOCK_VOSF;
2497 gbeauche 1.79 update_display_dga_vosf(static_cast<driver_dga *>(drv));
2498 gbeauche 1.36 UNLOCK_VOSF;
2499     }
2500 gbeauche 1.19 }
2501     }
2502     #endif
2503    
2504     static void video_refresh_window_vosf(void)
2505     {
2506 cebix 1.51 // Ungrab mouse if requested
2507     possibly_ungrab_mouse();
2508 gbeauche 1.19
2509     // Update display (VOSF variant)
2510     static int tick_counter = 0;
2511     if (++tick_counter >= frame_skip) {
2512     tick_counter = 0;
2513 gbeauche 1.36 if (mainBuffer.dirty) {
2514 gbeauche 1.74 XDisplayLock();
2515 gbeauche 1.36 LOCK_VOSF;
2516 cebix 1.44 update_display_window_vosf(static_cast<driver_window *>(drv));
2517 gbeauche 1.36 UNLOCK_VOSF;
2518 cebix 1.37 XSync(x_display, false); // Let the server catch up
2519 gbeauche 1.74 XDisplayUnlock();
2520 gbeauche 1.36 }
2521 gbeauche 1.19 }
2522     }
2523 cebix 1.23 #endif // def ENABLE_VOSF
2524 gbeauche 1.19
2525     static void video_refresh_window_static(void)
2526     {
2527 cebix 1.51 // Ungrab mouse if requested
2528     possibly_ungrab_mouse();
2529    
2530 gbeauche 1.19 // Update display (static variant)
2531     static int tick_counter = 0;
2532     if (++tick_counter >= frame_skip) {
2533     tick_counter = 0;
2534 cebix 1.44 update_display_static(static_cast<driver_window *>(drv));
2535 gbeauche 1.19 }
2536     }
2537    
2538     static void video_refresh_window_dynamic(void)
2539     {
2540 cebix 1.51 // Ungrab mouse if requested
2541     possibly_ungrab_mouse();
2542    
2543 gbeauche 1.19 // Update display (dynamic variant)
2544     static int tick_counter = 0;
2545     tick_counter++;
2546 cebix 1.44 update_display_dynamic(tick_counter, static_cast<driver_window *>(drv));
2547 gbeauche 1.19 }
2548    
2549    
2550     /*
2551 cebix 1.1 * Thread for screen refresh, input handling etc.
2552     */
2553    
2554 cebix 1.42 static void VideoRefreshInit(void)
2555 gbeauche 1.19 {
2556     // TODO: set up specialised 8bpp VideoRefresh handlers ?
2557     if (display_type == DISPLAY_DGA) {
2558 cebix 1.23 #if ENABLE_VOSF && (REAL_ADDRESSING || DIRECT_ADDRESSING)
2559 gbeauche 1.19 if (use_vosf)
2560     video_refresh = video_refresh_dga_vosf;
2561     else
2562     #endif
2563     video_refresh = video_refresh_dga;
2564     }
2565     else {
2566     #ifdef ENABLE_VOSF
2567     if (use_vosf)
2568     video_refresh = video_refresh_window_vosf;
2569     else
2570     #endif
2571     if (frame_skip == 0)
2572     video_refresh = video_refresh_window_dynamic;
2573     else
2574     video_refresh = video_refresh_window_static;
2575     }
2576     }
2577    
2578 cebix 1.57 // This function is called on non-threaded platforms from a timer interrupt
2579 gbeauche 1.19 void VideoRefresh(void)
2580     {
2581 cebix 1.45 // We need to check redraw_thread_active to inhibit refreshed during
2582     // mode changes on non-threaded platforms
2583 cebix 1.57 if (!redraw_thread_active)
2584     return;
2585    
2586     // Handle X events
2587     handle_events();
2588    
2589     // Handle palette changes
2590     handle_palette_changes();
2591    
2592     // Update display
2593     video_refresh();
2594 gbeauche 1.19 }
2595    
2596 cebix 1.57 const int VIDEO_REFRESH_HZ = 60;
2597     const int VIDEO_REFRESH_DELAY = 1000000 / VIDEO_REFRESH_HZ;
2598    
2599 gbeauche 1.80 #ifdef USE_PTHREADS_SERVICES
2600 cebix 1.15 static void *redraw_func(void *arg)
2601     {
2602 cebix 1.57 int fd = ConnectionNumber(x_display);
2603    
2604 gbeauche 1.19 uint64 start = GetTicks_usec();
2605     int64 ticks = 0;
2606 cebix 1.57 uint64 next = GetTicks_usec() + VIDEO_REFRESH_DELAY;
2607    
2608 cebix 1.15 while (!redraw_thread_cancel) {
2609 cebix 1.57
2610 cebix 1.18 int64 delay = next - GetTicks_usec();
2611 cebix 1.57 if (delay < -VIDEO_REFRESH_DELAY) {
2612    
2613     // We are lagging far behind, so we reset the delay mechanism
2614 cebix 1.18 next = GetTicks_usec();
2615 cebix 1.57
2616     } else if (delay <= 0) {
2617    
2618     // Delay expired, refresh display
2619     handle_events();
2620     handle_palette_changes();
2621     video_refresh();
2622     next += VIDEO_REFRESH_DELAY;
2623     ticks++;
2624    
2625     } else {
2626    
2627     // No display refresh pending, check for X events
2628     fd_set readfds;
2629     FD_ZERO(&readfds);
2630     FD_SET(fd, &readfds);
2631     struct timeval timeout;
2632     timeout.tv_sec = 0;
2633     timeout.tv_usec = delay;
2634     if (select(fd+1, &readfds, NULL, NULL, &timeout) > 0)
2635     handle_events();
2636     }
2637 cebix 1.1 }
2638 cebix 1.57
2639 gbeauche 1.19 uint64 end = GetTicks_usec();
2640 cebix 1.57 D(bug("%Ld refreshes in %Ld usec = %f refreshes/sec\n", ticks, end - start, ticks * 1000000.0 / (end - start)));
2641 gbeauche 1.77
2642     redraw_thread_cancel_ack = true;
2643 cebix 1.1 return NULL;
2644     }
2645 cebix 1.15 #endif