home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 25 / AACD 25.iso / AACD / Utilities / BasiliskII / src / Unix / video_x.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2001-05-20  |  55.7 KB  |  2,145 lines

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