home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 6 / AACD06.ISO / AACD / Emulation / BasiliskII / src / Unix / video_x.cpp < prev    next >
C/C++ Source or Header  |  1999-11-03  |  41KB  |  1,489 lines

  1. /*
  2.  *  video_x.cpp - Video/graphics emulation, X11 specific stuff
  3.  *
  4.  *  Basilisk II (C) 1997-1999 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 <pthread.h>
  38. #include <errno.h>
  39.  
  40. #include "cpu_emulation.h"
  41. #include "main.h"
  42. #include "adb.h"
  43. #include "macos_util.h"
  44. #include "prefs.h"
  45. #include "user_strings.h"
  46. #include "video.h"
  47.  
  48. #define DEBUG 1
  49. #include "debug.h"
  50.  
  51. #if ENABLE_XF86_DGA
  52. #include <X11/extensions/xf86dga.h>
  53. #endif
  54.  
  55. #if ENABLE_XF86_VIDMODE
  56. #include <X11/extensions/xf86vmode.h>
  57. #endif
  58.  
  59. #if ENABLE_FBDEV_DGA
  60. #include <sys/mman.h>
  61. #endif
  62.  
  63.  
  64.  
  65. // Display types
  66. enum {
  67.     DISPLAY_WINDOW,    // X11 window, using MIT SHM extensions if possible
  68.     DISPLAY_DGA        // DGA fullscreen display
  69. };
  70.  
  71.  
  72. // Constants
  73. const char KEYCODE_FILE_NAME[] = DATADIR "/keycodes";
  74. const char FBDEVICES_FILE_NAME[] = DATADIR "/fbdevices";
  75.  
  76.  
  77. // Global variables
  78. static int32 frame_skip;                            // Prefs items
  79. static int16 mouse_wheel_mode = 1;
  80. static int16 mouse_wheel_lines = 3;
  81.  
  82. static int display_type = DISPLAY_WINDOW;            // See enum above
  83. static uint8 *the_buffer;                            // Mac frame buffer
  84. static bool redraw_thread_active = false;            // Flag: Redraw thread installed
  85. static volatile bool redraw_thread_cancel = false;    // Flag: Cancel Redraw thread
  86. static pthread_t redraw_thread;                        // Redraw thread
  87.  
  88. static bool has_dga = false;                        // Flag: Video DGA capable
  89. static bool has_vidmode = false;                    // Flag: VidMode extension available
  90.  
  91. static bool ctrl_down = false;                        // Flag: Ctrl key pressed
  92. static bool caps_on = false;                        // Flag: Caps Lock on
  93. static bool quit_full_screen = false;                // Flag: DGA close requested from redraw thread
  94. static bool emerg_quit = false;                        // Flag: Ctrl-Esc pressed, emergency quit requested from MacOS thread
  95. static bool emul_suspended = false;                    // Flag: Emulator suspended
  96.  
  97. static bool classic_mode = false;                    // Flag: Classic Mac video mode
  98.  
  99. static bool use_keycodes = false;                    // Flag: Use keycodes rather than keysyms
  100. static int keycode_table[256];                        // X keycode -> Mac keycode translation table
  101.  
  102. // X11 variables
  103. static int screen;                                    // Screen number
  104. static int xdepth;                                    // Depth of X screen
  105. static int depth;                                    // Depth of Mac frame buffer
  106. static Window rootwin, the_win;                        // Root window and our window
  107. static XVisualInfo visualInfo;
  108. static Visual *vis;
  109. static Colormap cmap[2];                            // Two colormaps (DGA) for 8-bit mode
  110. static XColor black, white;
  111. static unsigned long black_pixel, white_pixel;
  112. static int eventmask;
  113. static const int win_eventmask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | EnterWindowMask | ExposureMask;
  114. static const int dga_eventmask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask;
  115.  
  116. static pthread_mutex_t palette_lock = PTHREAD_MUTEX_INITIALIZER;    // Mutex to protect palette
  117. static XColor palette[256];                            // Color palette for 8-bit mode
  118. static bool palette_changed = false;                // Flag: Palette changed, redraw thread must set new colors
  119.  
  120. // Variables for window mode
  121. static GC the_gc;
  122. static XImage *img = NULL;
  123. static XShmSegmentInfo shminfo;
  124. static XImage *cursor_image, *cursor_mask_image;
  125. static Pixmap cursor_map, cursor_mask_map;
  126. static Cursor mac_cursor;
  127. static GC cursor_gc, cursor_mask_gc;
  128. static uint8 *the_buffer_copy = NULL;                // Copy of Mac frame buffer
  129. static uint8 the_cursor[64];                        // Cursor image data
  130. static bool have_shm = false;                        // Flag: SHM extensions available
  131.  
  132. // Variables for XF86 DGA mode
  133. static int current_dga_cmap;                        // Number (0 or 1) of currently installed DGA colormap
  134. static Window suspend_win;                            // "Suspend" window
  135. static void *fb_save = NULL;                        // Saved frame buffer for suspend
  136. static pthread_mutex_t frame_buffer_lock = PTHREAD_MUTEX_INITIALIZER;    // Mutex to protect frame buffer
  137.  
  138. // Variables for fbdev DGA mode
  139. const char FBDEVICE_FILE_NAME[] = "/dev/fb";
  140. static int fbdev_fd;
  141.  
  142. #if ENABLE_XF86_VIDMODE
  143. // Variables for XF86 VidMode support
  144. static XF86VidModeModeInfo **x_video_modes;            // Array of all available modes
  145. static int num_x_video_modes;
  146. #endif
  147.  
  148.  
  149. // Prototypes
  150. static void *redraw_func(void *arg);
  151. static int event2keycode(XKeyEvent *ev);
  152.  
  153.  
  154. // From main_unix.cpp
  155. extern Display *x_display;
  156.  
  157. // From sys_unix.cpp
  158. extern void SysMountFirstFloppy(void);
  159.  
  160.  
  161. /*
  162.  *  Initialization
  163.  */
  164.  
  165. // Set VideoMonitor according to video mode
  166. void set_video_monitor(int width, int height, int bytes_per_row, bool native_byte_order)
  167. {
  168.     int layout = FLAYOUT_DIRECT;
  169.     switch (depth) {
  170.         case 1:
  171.             layout = FLAYOUT_DIRECT;
  172.             VideoMonitor.mode = VMODE_1BIT;
  173.             break;
  174.         case 8:
  175.             layout = FLAYOUT_DIRECT;
  176.             VideoMonitor.mode = VMODE_8BIT;
  177.             break;
  178.         case 15:
  179.             layout = FLAYOUT_HOST_555;
  180.             VideoMonitor.mode = VMODE_16BIT;
  181.             break;
  182.         case 16:
  183.             layout = FLAYOUT_HOST_565;
  184.             VideoMonitor.mode = VMODE_16BIT;
  185.             break;
  186.         case 24:
  187.         case 32:
  188.             layout = FLAYOUT_HOST_888;
  189.             VideoMonitor.mode = VMODE_32BIT;
  190.             break;
  191.     }
  192.     VideoMonitor.x = width;
  193.     VideoMonitor.y = height;
  194.     VideoMonitor.bytes_per_row = bytes_per_row;
  195.     if (native_byte_order)
  196.         MacFrameLayout = layout;
  197.     else
  198.         MacFrameLayout = FLAYOUT_DIRECT;
  199. }
  200.  
  201. // Trap SHM errors
  202. static bool shm_error = false;
  203. static int (*old_error_handler)(Display *, XErrorEvent *);
  204.  
  205. static int error_handler(Display *d, XErrorEvent *e)
  206. {
  207.     if (e->error_code == BadAccess) {
  208.         shm_error = true;
  209.         return 0;
  210.     } else
  211.         return old_error_handler(d, e);
  212. }
  213.  
  214. // Init window mode
  215. static bool init_window(int width, int height)
  216. {
  217.     // Set absolute mouse mode
  218.     ADBSetRelMouseMode(false);
  219.  
  220.     // Read frame skip prefs
  221.     frame_skip = PrefsFindInt32("frameskip");
  222.     if (frame_skip == 0)
  223.         frame_skip = 1;
  224.  
  225.     // Create window
  226.     XSetWindowAttributes wattr;
  227.     wattr.event_mask = eventmask = win_eventmask;
  228.     wattr.background_pixel = black_pixel;
  229.     wattr.border_pixel = black_pixel;
  230.     wattr.backing_store = Always;
  231.     wattr.backing_planes = xdepth;
  232.  
  233.     XSync(x_display, false);
  234.     the_win = XCreateWindow(x_display, rootwin, 0, 0, width, height, 0, xdepth,
  235.         InputOutput, vis, CWEventMask | CWBackPixel | CWBorderPixel |
  236.         CWBackingStore | CWBackingPlanes, &wattr);
  237.     XSync(x_display, false);
  238.     XStoreName(x_display, the_win, GetString(STR_WINDOW_TITLE));
  239.     XMapRaised(x_display, the_win);
  240.     XSync(x_display, false);
  241.  
  242.     // Set colormap
  243.     if (depth == 8) {
  244.         XSetWindowColormap(x_display, the_win, cmap[0]);
  245.         XSetWMColormapWindows(x_display, the_win, &the_win, 1);
  246.     }
  247.  
  248.     // Make window unresizable
  249.     XSizeHints *hints;
  250.     if ((hints = XAllocSizeHints()) != NULL) {
  251.         hints->min_width = width;
  252.         hints->max_width = width;
  253.         hints->min_height = height;
  254.         hints->max_height = height;
  255.         hints->flags = PMinSize | PMaxSize;
  256.         XSetWMNormalHints(x_display, the_win, hints);
  257.         XFree((char *)hints);
  258.     }
  259.     
  260.     // Try to create and attach SHM image
  261.     have_shm = false;
  262.     if (depth != 1 && XShmQueryExtension(x_display)) {
  263.  
  264.         // Create SHM image ("height + 2" for safety)
  265.         img = XShmCreateImage(x_display, vis, depth, depth == 1 ? XYBitmap : ZPixmap, 0, &shminfo, width, height);
  266.         shminfo.shmid = shmget(IPC_PRIVATE, (height + 2) * img->bytes_per_line, IPC_CREAT | 0777);
  267.         the_buffer = (uint8 *)shmat(shminfo.shmid, 0, 0);
  268.         shminfo.shmaddr = img->data = (char *)the_buffer;
  269.         shminfo.readOnly = False;
  270.  
  271.         // Try to attach SHM image, catching errors
  272.         shm_error = false;
  273.         old_error_handler = XSetErrorHandler(error_handler);
  274.         XShmAttach(x_display, &shminfo);
  275.         XSync(x_display, false);
  276.         XSetErrorHandler(old_error_handler);
  277.         if (shm_error) {
  278.             shmdt(shminfo.shmaddr);
  279.             XDestroyImage(img);
  280.             shminfo.shmid = -1;
  281.         } else {
  282.             have_shm = true;
  283.             shmctl(shminfo.shmid, IPC_RMID, 0);
  284.         }
  285.     }
  286.     
  287.     // Create normal X image if SHM doesn't work ("height + 2" for safety)
  288.     if (!have_shm) {
  289.         int bytes_per_row = width;
  290.         switch (depth) {
  291.             case 1:
  292.                 bytes_per_row /= 8;
  293.                 break;
  294.             case 15:
  295.             case 16:
  296.                 bytes_per_row *= 2;
  297.                 break;
  298.             case 24:
  299.             case 32:
  300.                 bytes_per_row *= 4;
  301.                 break;
  302.         }
  303.         the_buffer = (uint8 *)malloc((height + 2) * bytes_per_row);
  304.         img = XCreateImage(x_display, vis, depth, depth == 1 ? XYBitmap : ZPixmap, 0, (char *)the_buffer, width, height, 32, bytes_per_row);
  305.     }
  306.  
  307.     // 1-Bit mode is big-endian
  308.     if (depth == 1) {
  309.         img->byte_order = MSBFirst;
  310.         img->bitmap_bit_order = MSBFirst;
  311.     }
  312.  
  313.     // Allocate memory for frame buffer copy
  314.     the_buffer_copy = (uint8 *)malloc((height + 2) * img->bytes_per_line);
  315.  
  316.     // Create GC
  317.     the_gc = XCreateGC(x_display, the_win, 0, 0);
  318.     XSetState(x_display, the_gc, black_pixel, white_pixel, GXcopy, AllPlanes);
  319.  
  320.     // Create cursor
  321.     cursor_image = XCreateImage(x_display, vis, 1, XYPixmap, 0, (char *)the_cursor, 16, 16, 16, 2);
  322.     cursor_image->byte_order = MSBFirst;
  323.     cursor_image->bitmap_bit_order = MSBFirst;
  324.     cursor_mask_image = XCreateImage(x_display, vis, 1, XYPixmap, 0, (char *)the_cursor+32, 16, 16, 16, 2);
  325.     cursor_mask_image->byte_order = MSBFirst;
  326.     cursor_mask_image->bitmap_bit_order = MSBFirst;
  327.     cursor_map = XCreatePixmap(x_display, the_win, 16, 16, 1);
  328.     cursor_mask_map = XCreatePixmap(x_display, the_win, 16, 16, 1);
  329.     cursor_gc = XCreateGC(x_display, cursor_map, 0, 0);
  330.     cursor_mask_gc = XCreateGC(x_display, cursor_mask_map, 0, 0);
  331.     mac_cursor = XCreatePixmapCursor(x_display, cursor_map, cursor_mask_map, &black, &white, 0, 0);
  332.  
  333.     // Set VideoMonitor
  334. #ifdef WORDS_BIGENDIAN
  335.     set_video_monitor(width, height, img->bytes_per_line, img->bitmap_bit_order == MSBFirst);
  336. #else
  337.     set_video_monitor(width, height, img->bytes_per_line, img->bitmap_bit_order == LSBFirst);
  338. #endif
  339.     
  340. #if REAL_ADDRESSING
  341.     VideoMonitor.mac_frame_base = (uint32)the_buffer;
  342.     MacFrameLayout = FLAYOUT_DIRECT;
  343. #else
  344.     VideoMonitor.mac_frame_base = MacFrameBaseMac;
  345. #endif
  346.     return true;
  347. }
  348.  
  349. // Init fbdev DGA display
  350. static bool init_fbdev_dga(char *in_fb_name)
  351. {
  352. #if ENABLE_FBDEV_DGA
  353.     // Find the maximum depth available
  354.     int ndepths, max_depth(0);
  355.     int *depths = XListDepths(x_display, screen, &ndepths);
  356.     if (depths == NULL) {
  357.         printf("FATAL: Could not determine the maximal depth available\n");
  358.         return false;
  359.     } else {
  360.         while (ndepths-- > 0) {
  361.             if (depths[ndepths] > max_depth)
  362.                 max_depth = depths[ndepths];
  363.         }
  364.     }
  365.     
  366.     // Get fbdevices file path from preferences
  367.     const char *fbd_path = PrefsFindString("fbdevicefile");
  368.     
  369.     // Open fbdevices file
  370.     FILE *fp = fopen(fbd_path ? fbd_path : FBDEVICES_FILE_NAME, "r");
  371.     if (fp == NULL) {
  372.         char str[256];
  373.         sprintf(str, GetString(STR_NO_FBDEVICE_FILE_ERR), fbd_path ? fbd_path : FBDEVICES_FILE_NAME, strerror(errno));
  374.         ErrorAlert(str);
  375.         return false;
  376.     }
  377.     
  378.     int fb_depth;        // supported depth
  379.     uint32 fb_offset;    // offset used for mmap(2)
  380.     char fb_name[20];
  381.     char line[256];
  382.     bool device_found = false;
  383.     while (fgets(line, 255, fp)) {
  384.         // Read line
  385.         int len = strlen(line);
  386.         if (len == 0)
  387.             continue;
  388.         line[len - 1] = '\0';
  389.         
  390.         // Comments begin with "#" or ";"
  391.         if ((line[0] == '#') || (line[0] == ';') || (line[0] == '\0'))
  392.             continue;
  393.         
  394.         if ((sscanf(line, "%19s %d %x", &fb_name, &fb_depth, &fb_offset) == 3)
  395.         && (strcmp(fb_name, in_fb_name) == 0) && (fb_depth == max_depth)) {
  396.             device_found = true;
  397.             break;
  398.         }
  399.     }
  400.     
  401.     // fbdevices file completely read
  402.     fclose(fp);
  403.     
  404.     // Frame buffer name not found ? Then, display warning
  405.     if (!device_found) {
  406.         char str[256];
  407.         sprintf(str, GetString(STR_FBDEV_NAME_ERR), in_fb_name, max_depth);
  408.         ErrorAlert(str);
  409.         return false;
  410.     }
  411.     
  412.     int width = DisplayWidth(x_display, screen);
  413.     int height = DisplayHeight(x_display, screen);
  414.     depth = fb_depth; // max_depth
  415.     
  416.     // Set relative mouse mode
  417.     ADBSetRelMouseMode(false);
  418.     
  419.     // Create window
  420.     XSetWindowAttributes wattr;
  421.     wattr.override_redirect    = True;
  422.     wattr.backing_store        = NotUseful;
  423.     wattr.background_pixel    = white_pixel;
  424.     wattr.border_pixel        = black_pixel;
  425.     wattr.event_mask        = eventmask = dga_eventmask;
  426.     
  427.     XSync(x_display, false);
  428.     the_win = XCreateWindow(x_display, rootwin,
  429.         0, 0, width, height,
  430.         0, xdepth, InputOutput, vis,
  431.         CWEventMask|CWBackPixel|CWBorderPixel|CWOverrideRedirect|CWBackingStore,
  432.         &wattr);
  433.     XSync(x_display, false);
  434.     XMapRaised(x_display, the_win);
  435.     XSync(x_display, false);
  436.     
  437.     // Grab mouse and keyboard
  438.     XGrabKeyboard(x_display, the_win, True,
  439.         GrabModeAsync, GrabModeAsync, CurrentTime);
  440.     XGrabPointer(x_display, the_win, True,
  441.         PointerMotionMask | ButtonPressMask | ButtonReleaseMask,
  442.         GrabModeAsync, GrabModeAsync, the_win, None, CurrentTime);
  443.     
  444.     // Set colormap
  445.     if (depth == 8) {
  446.         XSetWindowColormap(x_display, the_win, cmap[0]);
  447.         XSetWMColormapWindows(x_display, the_win, &the_win, 1);
  448.     }
  449.     
  450.     // Set VideoMonitor
  451.     int bytes_per_row = width;
  452.     switch (depth) {
  453.         case 1:
  454.             bytes_per_row = ((width | 7) & ~7) >> 3;
  455.             break;
  456.         case 15:
  457.         case 16:
  458.             bytes_per_row *= 2;
  459.             break;
  460.         case 24:
  461.         case 32:
  462.             bytes_per_row *= 4;
  463.             break;
  464.     }
  465.     
  466.     if ((the_buffer = (uint8 *) mmap(NULL, height * bytes_per_row, PROT_READ | PROT_WRITE, MAP_PRIVATE, fbdev_fd, fb_offset)) == MAP_FAILED) {
  467.         if ((the_buffer = (uint8 *) mmap(NULL, height * bytes_per_row, PROT_READ | PROT_WRITE, MAP_SHARED, fbdev_fd, fb_offset)) == MAP_FAILED) {
  468.             char str[256];
  469.             sprintf(str, GetString(STR_FBDEV_MMAP_ERR), strerror(errno));
  470.             ErrorAlert(str);
  471.             return false;
  472.         }
  473.     }
  474.     
  475.     set_video_monitor(width, height, bytes_per_row, true);
  476. #if REAL_ADDRESSING
  477.     VideoMonitor.mac_frame_base = (uint32)the_buffer;
  478.     MacFrameLayout = FLAYOUT_DIRECT;
  479. #else
  480.     VideoMonitor.mac_frame_base = MacFrameBaseMac;
  481. #endif
  482.     return true;
  483. #else
  484.     ErrorAlert("Basilisk II has been compiled with fbdev DGA support disabled.");
  485.     return false;
  486. #endif
  487. }
  488.  
  489. // Init XF86 DGA display
  490. static bool init_xf86_dga(int width, int height)
  491. {
  492. #if ENABLE_XF86_DGA
  493.     // Set relative mouse mode
  494.     ADBSetRelMouseMode(true);
  495.  
  496. #if ENABLE_XF86_VIDMODE
  497.     // Switch to best mode
  498.     if (has_vidmode) {
  499.         int best = 0;
  500.         for (int i=1; i<num_x_video_modes; i++) {
  501.             if (x_video_modes[i]->hdisplay >= width && x_video_modes[i]->vdisplay >= height &&
  502.                 x_video_modes[i]->hdisplay <= x_video_modes[best]->hdisplay && x_video_modes[i]->vdisplay <= x_video_modes[best]->vdisplay) {
  503.                 best = i;
  504.             }
  505.         }
  506.         XF86VidModeSwitchToMode(x_display, screen, x_video_modes[best]);
  507.         XF86VidModeSetViewPort(x_display, screen, 0, 0);
  508.     }
  509. #endif
  510.  
  511.     // Create window
  512.     XSetWindowAttributes wattr;
  513.     wattr.event_mask = eventmask = dga_eventmask;
  514.     wattr.border_pixel = black_pixel;
  515.     wattr.override_redirect = True;
  516.  
  517.     XSync(x_display, false);
  518.     the_win = XCreateWindow(x_display, rootwin, 0, 0, width, height, 0, xdepth,
  519.         InputOutput, vis, CWEventMask | CWBorderPixel | CWOverrideRedirect, &wattr);
  520.     XSync(x_display, false);
  521.     XStoreName(x_display, the_win, GetString(STR_WINDOW_TITLE));
  522.     XMapRaised(x_display, the_win);
  523.     XSync(x_display, false);
  524.  
  525.     // Establish direct screen connection
  526.     XMoveResizeWindow(x_display, the_win, 0, 0, width, height);
  527.     XWarpPointer(x_display, None, rootwin, 0, 0, 0, 0, 0, 0);
  528.     XGrabKeyboard(x_display, rootwin, True, GrabModeAsync, GrabModeAsync, CurrentTime);
  529.     XGrabPointer(x_display, rootwin, True, PointerMotionMask | ButtonPressMask | ButtonReleaseMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
  530.  
  531.     int v_width, v_bank, v_size;
  532.     XF86DGAGetVideo(x_display, screen, (char **)&the_buffer, &v_width, &v_bank, &v_size);
  533.     XF86DGADirectVideo(x_display, screen, XF86DGADirectGraphics | XF86DGADirectKeyb | XF86DGADirectMouse);
  534.     XF86DGASetViewPort(x_display, screen, 0, 0);
  535.     XF86DGASetVidPage(x_display, screen, 0);
  536.  
  537.     // Set colormap
  538.     if (depth == 8) {
  539.         XSetWindowColormap(x_display, the_win, cmap[current_dga_cmap = 0]);
  540.         XSetWMColormapWindows(x_display, the_win, &the_win, 1);
  541.         XF86DGAInstallColormap(x_display, screen, cmap[current_dga_cmap]);
  542.     }
  543.  
  544.     // Set VideoMonitor
  545.     int bytes_per_row = (v_width + 7) & ~7;
  546.     switch (depth) {
  547.         case 1:
  548.             bytes_per_row /= 8;
  549.             break;
  550.         case 15:
  551.         case 16:
  552.             bytes_per_row *= 2;
  553.             break;
  554.         case 24:
  555.         case 32:
  556.             bytes_per_row *= 4;
  557.             break;
  558.     }
  559.     set_video_monitor(width, height, bytes_per_row, true);
  560. #if REAL_ADDRESSING
  561.     VideoMonitor.mac_frame_base = (uint32)the_buffer;
  562.     MacFrameLayout = FLAYOUT_DIRECT;
  563. #else
  564.     VideoMonitor.mac_frame_base = MacFrameBaseMac;
  565. #endif
  566.     return true;
  567. #else
  568.     ErrorAlert("Basilisk II has been compiled with XF86 DGA support disabled.");
  569.     return false;
  570. #endif
  571. }
  572.  
  573. // Init keycode translation table
  574. static void keycode_init(void)
  575. {
  576.     bool use_kc = PrefsFindBool("keycodes");
  577.     if (use_kc) {
  578.  
  579.         // Get keycode file path from preferences
  580.         const char *kc_path = PrefsFindString("keycodefile");
  581.  
  582.         // Open keycode table
  583.         FILE *f = fopen(kc_path ? kc_path : KEYCODE_FILE_NAME, "r");
  584.         if (f == NULL) {
  585.             char str[256];
  586.             sprintf(str, GetString(STR_KEYCODE_FILE_WARN), kc_path ? kc_path : KEYCODE_FILE_NAME, strerror(errno));
  587.             WarningAlert(str);
  588.             return;
  589.         }
  590.  
  591.         // Default translation table
  592.         for (int i=0; i<256; i++)
  593.             keycode_table[i] = -1;
  594.  
  595.         // Search for server vendor string, then read keycodes
  596.         const char *vendor = ServerVendor(x_display);
  597.         bool vendor_found = false;
  598.         char line[256];
  599.         while (fgets(line, 255, f)) {
  600.             // Read line
  601.             int len = strlen(line);
  602.             if (len == 0)
  603.                 continue;
  604.             line[len-1] = 0;
  605.  
  606.             // Comments begin with "#" or ";"
  607.             if (line[0] == '#' || line[0] == ';' || line[0] == 0)
  608.                 continue;
  609.  
  610.             if (vendor_found) {
  611.                 // Read keycode
  612.                 int x_code, mac_code;
  613.                 if (sscanf(line, "%d %d", &x_code, &mac_code) == 2)
  614.                     keycode_table[x_code & 0xff] = mac_code;
  615.                 else
  616.                     break;
  617.             } else {
  618.                 // Search for vendor string
  619.                 if (strstr(vendor, line) == vendor)
  620.                     vendor_found = true;
  621.             }
  622.         }
  623.  
  624.         // Keycode file completely read
  625.         fclose(f);
  626.         use_keycodes = vendor_found;
  627.  
  628.         // Vendor not found? Then display warning
  629.         if (!vendor_found) {
  630.             char str[256];
  631.             sprintf(str, GetString(STR_KEYCODE_VENDOR_WARN), vendor, kc_path ? kc_path : KEYCODE_FILE_NAME);
  632.             WarningAlert(str);
  633.             return;
  634.         }
  635.     }
  636. }
  637.  
  638. bool VideoInit(bool classic)
  639. {
  640.     // Init keycode translation
  641.     keycode_init();
  642.  
  643.     // Read prefs
  644.     mouse_wheel_mode = PrefsFindInt16("mousewheelmode");
  645.     mouse_wheel_lines = PrefsFindInt16("mousewheellines");
  646.  
  647.     // Find screen and root window
  648.     screen = XDefaultScreen(x_display);
  649.     rootwin = XRootWindow(x_display, screen);
  650.     
  651.     // Get screen depth
  652.     xdepth = DefaultDepth(x_display, screen);
  653.     
  654. #if ENABLE_FBDEV_DGA
  655.     // Frame buffer name
  656.     char fb_name[20];
  657.     
  658.     // Could do fbdev dga ?
  659.     if ((fbdev_fd = open(FBDEVICE_FILE_NAME, O_RDWR)) != -1)
  660.         has_dga = true;
  661.     else
  662.         has_dga = false;
  663. #endif
  664.  
  665. #if ENABLE_XF86_DGA
  666.     // DGA available?
  667.     int dga_event_base, dga_error_base;
  668.     if (XF86DGAQueryExtension(x_display, &dga_event_base, &dga_error_base)) {
  669.         int dga_flags = 0;
  670.         XF86DGAQueryDirectVideo(x_display, screen, &dga_flags);
  671.         has_dga = dga_flags & XF86DGADirectPresent;
  672.     } else
  673.         has_dga = false;
  674. #endif
  675.  
  676. #if ENABLE_XF86_VIDMODE
  677.     // VidMode available?
  678.     int vm_event_base, vm_error_base;
  679.     has_vidmode = XF86VidModeQueryExtension(x_display, &vm_event_base, &vm_error_base);
  680.     if (has_vidmode)
  681.         XF86VidModeGetAllModeLines(x_display, screen, &num_x_video_modes, &x_video_modes);
  682. #endif
  683.     
  684.     // Find black and white colors
  685.     XParseColor(x_display, DefaultColormap(x_display, screen), "rgb:00/00/00", &black);
  686.     XAllocColor(x_display, DefaultColormap(x_display, screen), &black);
  687.     XParseColor(x_display, DefaultColormap(x_display, screen), "rgb:ff/ff/ff", &white);
  688.     XAllocColor(x_display, DefaultColormap(x_display, screen), &white);
  689.     black_pixel = BlackPixel(x_display, screen);
  690.     white_pixel = WhitePixel(x_display, screen);
  691.  
  692.     // Get appropriate visual
  693.     int color_class;
  694.     switch (xdepth) {
  695.         case 1:
  696.             color_class = StaticGray;
  697.             break;
  698.         case 8:
  699.             color_class = PseudoColor;
  700.             break;
  701.         case 15:
  702.         case 16:
  703.         case 24:
  704.         case 32:
  705.             color_class = TrueColor;
  706.             break;
  707.         default:
  708.             ErrorAlert(GetString(STR_UNSUPP_DEPTH_ERR));
  709.             return false;
  710.     }
  711.     if (!XMatchVisualInfo(x_display, screen, xdepth, color_class, &visualInfo)) {
  712.         ErrorAlert(GetString(STR_NO_XVISUAL_ERR));
  713.         return false;
  714.     }
  715.     if (visualInfo.depth != xdepth) {
  716.         ErrorAlert(GetString(STR_NO_XVISUAL_ERR));
  717.         return false;
  718.     }
  719.     vis = visualInfo.visual;
  720.  
  721.     // Mac screen depth is always 1 bit in Classic mode, but follows X depth otherwise
  722.     classic_mode = classic;
  723.     if (classic)
  724.         depth = 1;
  725.     else
  726.         depth = xdepth;
  727.  
  728.     // Create color maps for 8 bit mode
  729.     if (depth == 8) {
  730.         cmap[0] = XCreateColormap(x_display, rootwin, vis, AllocAll);
  731.         cmap[1] = XCreateColormap(x_display, rootwin, vis, AllocAll);
  732.         XInstallColormap(x_display, cmap[0]);
  733.         XInstallColormap(x_display, cmap[1]);
  734.     }
  735.  
  736.     // Get screen mode from preferences
  737.     const char *mode_str;
  738.     if (classic)
  739.         mode_str = "win/512/342";
  740.     else
  741.         mode_str = PrefsFindString("screen");
  742.  
  743.     // Determine type and mode
  744.     int width = 512, height = 384;
  745.     display_type = DISPLAY_WINDOW;
  746.     if (mode_str) {
  747.         if (sscanf(mode_str, "win/%d/%d", &width, &height) == 2)
  748.             display_type = DISPLAY_WINDOW;
  749. #if ENABLE_FBDEV_DGA
  750.         else if (has_dga && sscanf(mode_str, "dga/%19s", fb_name) == 1) {
  751. #else
  752.         else if (has_dga && sscanf(mode_str, "dga/%d/%d", &width, &height) == 2) {
  753. #endif
  754.             display_type = DISPLAY_DGA;
  755.             if (width > DisplayWidth(x_display, screen))
  756.                 width = DisplayWidth(x_display, screen);
  757.             if (height > DisplayHeight(x_display, screen))
  758.                 height = DisplayHeight(x_display, screen);
  759.         }
  760.         if (width <= 0)
  761.             width = DisplayWidth(x_display, screen);
  762.         if (height <= 0)
  763.             height = DisplayHeight(x_display, screen);
  764.     }
  765.  
  766.     // Initialize according to display type
  767.     switch (display_type) {
  768.         case DISPLAY_WINDOW:
  769.             if (!init_window(width, height))
  770.                 return false;
  771.             break;
  772.         case DISPLAY_DGA:
  773. #if ENABLE_FBDEV_DGA
  774.             if (!init_fbdev_dga(fb_name))
  775. #else
  776.             if (!init_xf86_dga(width, height))
  777. #endif
  778.                 return false;
  779.             break;
  780.     }
  781.  
  782.     // Lock down frame buffer
  783.     pthread_mutex_lock(&frame_buffer_lock);
  784.  
  785. #if !REAL_ADDRESSING
  786.     // Set variables for UAE memory mapping
  787.     MacFrameBaseHost = the_buffer;
  788.     MacFrameSize = VideoMonitor.bytes_per_row * VideoMonitor.y;
  789.  
  790.     // No special frame buffer in Classic mode (frame buffer is in Mac RAM)
  791.     if (classic)
  792.         MacFrameLayout = FLAYOUT_NONE;
  793. #endif
  794.  
  795.     // Start redraw/input thread
  796.     XSync(x_display, false);
  797.     redraw_thread_active = (pthread_create(&redraw_thread, NULL, redraw_func, NULL) == 0);
  798.     if (!redraw_thread_active)
  799.         printf("FATAL: cannot create redraw thread\n");
  800.     return redraw_thread_active;
  801. }
  802.  
  803.  
  804. /*
  805.  *  Deinitialization
  806.  */
  807.  
  808. void VideoExit(void)
  809. {
  810.     // Stop redraw thread
  811.     if (redraw_thread_active) {
  812.         redraw_thread_cancel = true;
  813. #ifdef HAVE_PTHREAD_CANCEL
  814.         pthread_cancel(redraw_thread);
  815. #endif
  816.         pthread_join(redraw_thread, NULL);
  817.         redraw_thread_active = false;
  818.     }
  819.  
  820.     // Unlock frame buffer
  821.     pthread_mutex_unlock(&frame_buffer_lock);
  822.  
  823.     // Close window and server connection
  824.     if (x_display != NULL) {
  825.         XSync(x_display, false);
  826.  
  827. #if ENABLE_XF86_DGA
  828.         if (display_type == DISPLAY_DGA) {
  829.             XF86DGADirectVideo(x_display, screen, 0);
  830.             XUngrabPointer(x_display, CurrentTime);
  831.             XUngrabKeyboard(x_display, CurrentTime);
  832.         }
  833. #endif
  834.  
  835. #if ENABLE_XF86_VIDMODE
  836.         if (has_vidmode && display_type == DISPLAY_DGA)
  837.             XF86VidModeSwitchToMode(x_display, screen, x_video_modes[0]);
  838. #endif
  839.  
  840. #if ENABLE_FBDEV_DGA
  841.         if (display_type == DISPLAY_DGA) {
  842.             XUngrabPointer(x_display, CurrentTime);
  843.             XUngrabKeyboard(x_display, CurrentTime);
  844.             close(fbdev_fd);
  845.         }
  846. #endif
  847.         
  848.         if (the_buffer_copy) {
  849.             free(the_buffer_copy);
  850.             the_buffer_copy = NULL;
  851.         }
  852.  
  853.         XFlush(x_display);
  854.         XSync(x_display, false);
  855.         if (depth == 8) {
  856.             XFreeColormap(x_display, cmap[0]);
  857.             XFreeColormap(x_display, cmap[1]);
  858.         }
  859.     }
  860. }
  861.  
  862.  
  863. /*
  864.  *  Close down full-screen mode (if bringing up error alerts is unsafe while in full-screen mode)
  865.  */
  866.  
  867. void VideoQuitFullScreen(void)
  868. {
  869.     D(bug("VideoQuitFullScreen()\n"));
  870.     if (display_type == DISPLAY_DGA)
  871.         quit_full_screen = true;
  872. }
  873.  
  874.  
  875. /*
  876.  *  Mac VBL interrupt
  877.  */
  878.  
  879. void VideoInterrupt(void)
  880. {
  881.     // Emergency quit requested? Then quit
  882.     if (emerg_quit)
  883.         QuitEmulator();
  884.  
  885.     // Temporarily give up frame buffer lock (this is the point where
  886.     // we are suspended when the user presses Ctrl-Tab)
  887.     pthread_mutex_unlock(&frame_buffer_lock);
  888.     pthread_mutex_lock(&frame_buffer_lock);
  889. }
  890.  
  891.  
  892. /*
  893.  *  Set palette
  894.  */
  895.  
  896. void video_set_palette(uint8 *pal)
  897. {
  898.     pthread_mutex_lock(&palette_lock);
  899.  
  900.     // Convert colors to XColor array
  901.     for (int i=0; i<256; i++) {
  902.         palette[i].pixel = i;
  903.         palette[i].red = pal[i*3] * 0x0101;
  904.         palette[i].green = pal[i*3+1] * 0x0101;
  905.         palette[i].blue = pal[i*3+2] * 0x0101;
  906.         palette[i].flags = DoRed | DoGreen | DoBlue;
  907.     }
  908.  
  909.     // Tell redraw thread to change palette
  910.     palette_changed = true;
  911.  
  912.     pthread_mutex_unlock(&palette_lock);
  913. }
  914.  
  915.  
  916. /*
  917.  *  Suspend/resume emulator
  918.  */
  919.  
  920. #if ENABLE_XF86_DGA || ENABLE_FBDEV_DGA
  921. static void suspend_emul(void)
  922. {
  923.     if (display_type == DISPLAY_DGA) {
  924.         // Release ctrl key
  925.         ADBKeyUp(0x36);
  926.         ctrl_down = false;
  927.  
  928.         // Lock frame buffer (this will stop the MacOS thread)
  929.         pthread_mutex_lock(&frame_buffer_lock);
  930.  
  931.         // Save frame buffer
  932.         fb_save = malloc(VideoMonitor.y * VideoMonitor.bytes_per_row);
  933.         if (fb_save)
  934.             memcpy(fb_save, the_buffer, VideoMonitor.y * VideoMonitor.bytes_per_row);
  935.  
  936.         // Close full screen display
  937. #if ENABLE_XF86_DGA
  938.         XF86DGADirectVideo(x_display, screen, 0);
  939. #endif
  940.         XUngrabPointer(x_display, CurrentTime);
  941.         XUngrabKeyboard(x_display, CurrentTime);
  942.         XUnmapWindow(x_display, the_win);
  943.         XSync(x_display, false);
  944.  
  945.         // Open "suspend" window
  946.         XSetWindowAttributes wattr;
  947.         wattr.event_mask = KeyPressMask;
  948.         wattr.background_pixel = black_pixel;
  949.         wattr.border_pixel = black_pixel;
  950.         wattr.backing_store = Always;
  951.         wattr.backing_planes = xdepth;
  952.         wattr.colormap = DefaultColormap(x_display, screen);
  953.         
  954.         XSync(x_display, false);
  955.         suspend_win = XCreateWindow(x_display, rootwin, 0, 0, 512, 1, 0, xdepth,
  956.             InputOutput, vis, CWEventMask | CWBackPixel | CWBorderPixel |
  957.             CWBackingStore | CWBackingPlanes | (xdepth == 8 ? CWColormap : 0), &wattr);
  958.         XSync(x_display, false);
  959.         XStoreName(x_display, suspend_win, GetString(STR_SUSPEND_WINDOW_TITLE));
  960.         XMapRaised(x_display, suspend_win);
  961.         XSync(x_display, false);
  962.         emul_suspended = true;
  963.     }
  964. }
  965.  
  966. static void resume_emul(void)
  967. {
  968.     // Close "suspend" window
  969.     XDestroyWindow(x_display, suspend_win);
  970.     XSync(x_display, false);
  971.  
  972.     // Reopen full screen display
  973.     XMapRaised(x_display, the_win);
  974.     XWarpPointer(x_display, None, rootwin, 0, 0, 0, 0, 0, 0);
  975.     XSync(x_display, false);
  976.     XGrabKeyboard(x_display, rootwin, 1, GrabModeAsync, GrabModeAsync, CurrentTime);
  977.     XGrabPointer(x_display, rootwin, 1, PointerMotionMask | ButtonPressMask | ButtonReleaseMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
  978. #if ENABLE_XF86_DGA
  979.     XF86DGADirectVideo(x_display, screen, XF86DGADirectGraphics | XF86DGADirectKeyb | XF86DGADirectMouse);
  980.     XF86DGASetViewPort(x_display, screen, 0, 0);
  981. #endif
  982.     XSync(x_display, false);
  983.  
  984.     // Restore frame buffer
  985.     if (fb_save) {
  986.         memcpy(the_buffer, fb_save, VideoMonitor.y * VideoMonitor.bytes_per_row);
  987.         free(fb_save);
  988.         fb_save = NULL;
  989.     }
  990.     
  991.     if (depth == 8)
  992. #if ENABLE_XF86_DGA
  993.         XF86DGAInstallColormap(x_display, screen, cmap[current_dga_cmap]);
  994. #endif
  995.  
  996.     // Unlock frame buffer (and continue MacOS thread)
  997.     pthread_mutex_unlock(&frame_buffer_lock);
  998.     emul_suspended = false;
  999. }
  1000. #endif
  1001.  
  1002.  
  1003. /*
  1004.  *  Translate key event to Mac keycode
  1005.  */
  1006.  
  1007. static int kc_decode(KeySym ks)
  1008. {
  1009.     switch (ks) {
  1010.         case XK_A: case XK_a: return 0x00;
  1011.         case XK_B: case XK_b: return 0x0b;
  1012.         case XK_C: case XK_c: return 0x08;
  1013.         case XK_D: case XK_d: return 0x02;
  1014.         case XK_E: case XK_e: return 0x0e;
  1015.         case XK_F: case XK_f: return 0x03;
  1016.         case XK_G: case XK_g: return 0x05;
  1017.         case XK_H: case XK_h: return 0x04;
  1018.         case XK_I: case XK_i: return 0x22;
  1019.         case XK_J: case XK_j: return 0x26;
  1020.         case XK_K: case XK_k: return 0x28;
  1021.         case XK_L: case XK_l: return 0x25;
  1022.         case XK_M: case XK_m: return 0x2e;
  1023.         case XK_N: case XK_n: return 0x2d;
  1024.         case XK_O: case XK_o: return 0x1f;
  1025.         case XK_P: case XK_p: return 0x23;
  1026.         case XK_Q: case XK_q: return 0x0c;
  1027.         case XK_R: case XK_r: return 0x0f;
  1028.         case XK_S: case XK_s: return 0x01;
  1029.         case XK_T: case XK_t: return 0x11;
  1030.         case XK_U: case XK_u: return 0x20;
  1031.         case XK_V: case XK_v: return 0x09;
  1032.         case XK_W: case XK_w: return 0x0d;
  1033.         case XK_X: case XK_x: return 0x07;
  1034.         case XK_Y: case XK_y: return 0x10;
  1035.         case XK_Z: case XK_z: return 0x06;
  1036.  
  1037.         case XK_1: case XK_exclam: return 0x12;
  1038.         case XK_2: case XK_at: return 0x13;
  1039.         case XK_3: case XK_numbersign: return 0x14;
  1040.         case XK_4: case XK_dollar: return 0x15;
  1041.         case XK_5: case XK_percent: return 0x17;
  1042.         case XK_6: return 0x16;
  1043.         case XK_7: return 0x1a;
  1044.         case XK_8: return 0x1c;
  1045.         case XK_9: return 0x19;
  1046.         case XK_0: return 0x1d;
  1047.  
  1048.         case XK_grave: case XK_asciitilde: return 0x0a;
  1049.         case XK_minus: case XK_underscore: return 0x1b;
  1050.         case XK_equal: case XK_plus: return 0x18;
  1051.         case XK_bracketleft: case XK_braceleft: return 0x21;
  1052.         case XK_bracketright: case XK_braceright: return 0x1e;
  1053.         case XK_backslash: case XK_bar: return 0x2a;
  1054.         case XK_semicolon: case XK_colon: return 0x29;
  1055.         case XK_apostrophe: case XK_quotedbl: return 0x27;
  1056.         case XK_comma: case XK_less: return 0x2b;
  1057.         case XK_period: case XK_greater: return 0x2f;
  1058.         case XK_slash: case XK_question: return 0x2c;
  1059.  
  1060. #if ENABLE_XF86_DGA || ENABLE_FBDEV_DGA
  1061.         case XK_Tab: if (ctrl_down) {suspend_emul(); return -1;} else return 0x30;
  1062. #else
  1063.         case XK_Tab: return 0x30;
  1064. #endif
  1065.         case XK_Return: return 0x24;
  1066.         case XK_space: return 0x31;
  1067.         case XK_BackSpace: return 0x33;
  1068.  
  1069.         case XK_Delete: return 0x75;
  1070.         case XK_Insert: return 0x72;
  1071.         case XK_Home: case XK_Help: return 0x73;
  1072.         case XK_End: return 0x77;
  1073. #ifdef __hpux
  1074.         case XK_Prior: return 0x74;
  1075.         case XK_Next: return 0x79;
  1076. #else
  1077.         case XK_Page_Up: return 0x74;
  1078.         case XK_Page_Down: return 0x79;
  1079. #endif
  1080.  
  1081.         case XK_Control_L: return 0x36;
  1082.         case XK_Control_R: return 0x36;
  1083.         case XK_Shift_L: return 0x38;
  1084.         case XK_Shift_R: return 0x38;
  1085.         case XK_Alt_L: return 0x37;
  1086.         case XK_Alt_R: return 0x37;
  1087.         case XK_Meta_L: return 0x3a;
  1088.         case XK_Meta_R: return 0x3a;
  1089.         case XK_Menu: return 0x32;
  1090.         case XK_Caps_Lock: return 0x39;
  1091.         case XK_Num_Lock: return 0x47;
  1092.  
  1093.         case XK_Up: return 0x3e;
  1094.         case XK_Down: return 0x3d;
  1095.         case XK_Left: return 0x3b;
  1096.         case XK_Right: return 0x3c;
  1097.  
  1098.         case XK_Escape: if (ctrl_down) {quit_full_screen = true; emerg_quit = true; return -1;} else return 0x35;
  1099.  
  1100.         case XK_F1: if (ctrl_down) {SysMountFirstFloppy(); return -1;} else return 0x7a;
  1101.         case XK_F2: return 0x78;
  1102.         case XK_F3: return 0x63;
  1103.         case XK_F4: return 0x76;
  1104.         case XK_F5: return 0x60;
  1105.         case XK_F6: return 0x61;
  1106.         case XK_F7: return 0x62;
  1107.         case XK_F8: return 0x64;
  1108.         case XK_F9: return 0x65;
  1109.         case XK_F10: return 0x6d;
  1110.         case XK_F11: return 0x67;
  1111.         case XK_F12: return 0x6f;
  1112.  
  1113.         case XK_Print: return 0x69;
  1114.         case XK_Scroll_Lock: return 0x6b;
  1115.         case XK_Pause: return 0x71;
  1116.  
  1117. #if defined(XK_KP_Prior) && defined(XK_KP_Left) && defined(XK_KP_Insert) && defined (XK_KP_End)
  1118.         case XK_KP_0: case XK_KP_Insert: return 0x52;
  1119.         case XK_KP_1: case XK_KP_End: return 0x53;
  1120.         case XK_KP_2: case XK_KP_Down: return 0x54;
  1121.         case XK_KP_3: case XK_KP_Next: return 0x55;
  1122.         case XK_KP_4: case XK_KP_Left: return 0x56;
  1123.         case XK_KP_5: case XK_KP_Begin: return 0x57;
  1124.         case XK_KP_6: case XK_KP_Right: return 0x58;
  1125.         case XK_KP_7: case XK_KP_Home: return 0x59;
  1126.         case XK_KP_8: case XK_KP_Up: return 0x5b;
  1127.         case XK_KP_9: case XK_KP_Prior: return 0x5c;
  1128.         case XK_KP_Decimal: case XK_KP_Delete: return 0x41;
  1129. #else
  1130.         case XK_KP_0: return 0x52;
  1131.         case XK_KP_1: return 0x53;
  1132.         case XK_KP_2: return 0x54;
  1133.         case XK_KP_3: return 0x55;
  1134.         case XK_KP_4: return 0x56;
  1135.         case XK_KP_5: return 0x57;
  1136.         case XK_KP_6: return 0x58;
  1137.         case XK_KP_7: return 0x59;
  1138.         case XK_KP_8: return 0x5b;
  1139.         case XK_KP_9: return 0x5c;
  1140.         case XK_KP_Decimal: return 0x41;
  1141. #endif
  1142.         case XK_KP_Add: return 0x45;
  1143.         case XK_KP_Subtract: return 0x4e;
  1144.         case XK_KP_Multiply: return 0x43;
  1145.         case XK_KP_Divide: return 0x4b;
  1146.         case XK_KP_Enter: return 0x4c;
  1147.         case XK_KP_Equal: return 0x51;
  1148.     }
  1149.     return -1;
  1150. }
  1151.  
  1152. static int event2keycode(XKeyEvent *ev)
  1153. {
  1154.     KeySym ks;
  1155.     int as;
  1156.     int i = 0;
  1157.  
  1158.     do {
  1159.         ks = XLookupKeysym(ev, i++);
  1160.         as = kc_decode(ks);
  1161.         if (as != -1)
  1162.             return as;
  1163.     } while (ks != NoSymbol);
  1164.  
  1165.     return -1;
  1166. }
  1167.  
  1168.  
  1169. /*
  1170.  *  X event handling
  1171.  */
  1172.  
  1173. static void handle_events(void)
  1174. {
  1175.     XEvent event;
  1176.     for (;;) {
  1177.         if (!XCheckMaskEvent(x_display, eventmask, &event))
  1178.             break;
  1179.  
  1180.         switch (event.type) {
  1181.             // Mouse button
  1182.             case ButtonPress: {
  1183.                 unsigned int button = ((XButtonEvent *)&event)->button;
  1184.                 if (button < 4)
  1185.                     ADBMouseDown(button - 1);
  1186.                 else if (button < 6) {    // Wheel mouse
  1187.                     if (mouse_wheel_mode == 0) {
  1188.                         int key = (button == 5) ? 0x79 : 0x74;    // Page up/down
  1189.                         ADBKeyDown(key);
  1190.                         ADBKeyUp(key);
  1191.                     } else {
  1192.                         int key = (button == 5) ? 0x3d : 0x3e;    // Cursor up/down
  1193.                         for(int i=0; i<mouse_wheel_lines; i++) {
  1194.                             ADBKeyDown(key);
  1195.                             ADBKeyUp(key);
  1196.                         }
  1197.                     }
  1198.                 }
  1199.                 break;
  1200.             }
  1201.             case ButtonRelease: {
  1202.                 unsigned int button = ((XButtonEvent *)&event)->button;
  1203.                 if (button < 4)
  1204.                     ADBMouseUp(button - 1);
  1205.                 break;
  1206.             }
  1207.  
  1208.             // Mouse moved
  1209.             case EnterNotify:
  1210.                 ADBMouseMoved(((XMotionEvent *)&event)->x, ((XMotionEvent *)&event)->y);
  1211.                 break;
  1212.             case MotionNotify:
  1213.                 ADBMouseMoved(((XMotionEvent *)&event)->x, ((XMotionEvent *)&event)->y);
  1214.                 break;
  1215.  
  1216.             // Keyboard
  1217.             case KeyPress: {
  1218.                 int code;
  1219.                 if (use_keycodes) {
  1220.                     event2keycode((XKeyEvent *)&event);    // This is called to process the hotkeys
  1221.                     code = keycode_table[((XKeyEvent *)&event)->keycode & 0xff];
  1222.                 } else
  1223.                     code = event2keycode((XKeyEvent *)&event);
  1224.                 if (code != -1) {
  1225.                     if (!emul_suspended) {
  1226.                         if (code == 0x39) {    // Caps Lock pressed
  1227.                             if (caps_on) {
  1228.                                 ADBKeyUp(code);
  1229.                                 caps_on = false;
  1230.                             } else {
  1231.                                 ADBKeyDown(code);
  1232.                                 caps_on = true;
  1233.                             }
  1234.                         } else
  1235.                             ADBKeyDown(code);
  1236.                         if (code == 0x36)
  1237.                             ctrl_down = true;
  1238.                     } else {
  1239. #if ENABLE_XF86_DGA || ENABLE_FBDEV_DGA
  1240.                         if (code == 0x31)
  1241.                             resume_emul();    // Space wakes us up
  1242. #endif
  1243.                     }
  1244.                 }
  1245.                 break;
  1246.             }
  1247.             case KeyRelease: {
  1248.                 int code;
  1249.                 if (use_keycodes) {
  1250.                     event2keycode((XKeyEvent *)&event);    // This is called to process the hotkeys
  1251.                     code = keycode_table[((XKeyEvent *)&event)->keycode & 0xff];
  1252.                 } else
  1253.                     code = event2keycode((XKeyEvent *)&event);
  1254.                 if (code != -1 && code != 0x39) {    // Don't propagate Caps Lock releases
  1255.                     ADBKeyUp(code);
  1256.                     if (code == 0x36)
  1257.                         ctrl_down = false;
  1258.                 }
  1259.                 break;
  1260.             }
  1261.  
  1262.             // Hidden parts exposed, force complete refresh of window
  1263.             case Expose:
  1264.                 if (display_type == DISPLAY_WINDOW)
  1265.                     memset(the_buffer_copy, 0, VideoMonitor.bytes_per_row * VideoMonitor.y);
  1266.                 break;
  1267.         }
  1268.     }
  1269. }
  1270.  
  1271.  
  1272. /*
  1273.  *  Window display update
  1274.  */
  1275.  
  1276. static void update_display(void)
  1277. {
  1278.     // In classic mode, copy the frame buffer from Mac RAM
  1279.     if (classic_mode)
  1280.         Mac2Host_memcpy(the_buffer, 0x3fa700, VideoMonitor.bytes_per_row * VideoMonitor.y);
  1281.     
  1282.     // Incremental update code
  1283.     int wide = 0, high = 0, x1, x2, y1, y2, i, j;
  1284.     int bytes_per_row = VideoMonitor.bytes_per_row;
  1285.     int bytes_per_pixel = VideoMonitor.bytes_per_row / VideoMonitor.x;
  1286.     uint8 *p, *p2;
  1287.  
  1288.     // Check for first line from top and first line from bottom that have changed
  1289.     y1 = 0;
  1290.     for (j=0; j<VideoMonitor.y; j++) {
  1291.         if (memcmp(&the_buffer[j * bytes_per_row], &the_buffer_copy[j * bytes_per_row], bytes_per_row)) {
  1292.             y1 = j;
  1293.             break;
  1294.         }
  1295.     }
  1296.     y2 = y1 - 1;
  1297.     for (j=VideoMonitor.y-1; j>=y1; j--) {
  1298.         if (memcmp(&the_buffer[j * bytes_per_row], &the_buffer_copy[j * bytes_per_row], bytes_per_row)) {
  1299.             y2 = j;
  1300.             break;
  1301.         }
  1302.     }
  1303.     high = y2 - y1 + 1;
  1304.  
  1305.     // Check for first column from left and first column from right that have changed
  1306.     if (high) {
  1307.         if (depth == 1) {
  1308.             x1 = VideoMonitor.x;
  1309.             for (j=y1; j<=y2; j++) {
  1310.                 p = &the_buffer[j * bytes_per_row];
  1311.                 p2 = &the_buffer_copy[j * bytes_per_row];
  1312.                 for (i=0; i<(x1>>3); i++) {
  1313.                     if (*p != *p2) {
  1314.                         x1 = i << 3;
  1315.                         break;
  1316.                     }
  1317.                     p++;
  1318.                     p2++;
  1319.                 }
  1320.             }
  1321.             x2 = x1;
  1322.             for (j=y1; j<=y2; j++) {
  1323.                 p = &the_buffer[j * bytes_per_row];
  1324.                 p2 = &the_buffer_copy[j * bytes_per_row];
  1325.                 p += bytes_per_row;
  1326.                 p2 += bytes_per_row;
  1327.                 for (i=(VideoMonitor.x>>3); i>(x2>>3); i--) {
  1328.                     p--;
  1329.                     p2--;
  1330.                     if (*p != *p2) {
  1331.                         x2 = i << 3;
  1332.                         break;
  1333.                     }
  1334.                 }
  1335.             }
  1336.             wide = x2 - x1;
  1337.  
  1338.             // Update copy of the_buffer
  1339.             if (high && wide) {
  1340.                 for (j=y1; j<=y2; j++) {
  1341.                     i = j * bytes_per_row + (x1 >> 3);
  1342.                     memcpy(&the_buffer_copy[i], &the_buffer[i], wide >> 3);
  1343.                 }
  1344.             }
  1345.  
  1346.         } else {
  1347.             x1 = VideoMonitor.x;
  1348.             for (j=y1; j<=y2; j++) {
  1349.                 p = &the_buffer[j * bytes_per_row];
  1350.                 p2 = &the_buffer_copy[j * bytes_per_row];
  1351.                 for (i=0; i<x1; i++) {
  1352.                     if (memcmp(p, p2, bytes_per_pixel)) {
  1353.                         x1 = i;
  1354.                         break;
  1355.                     }
  1356.                     p += bytes_per_pixel;
  1357.                     p2 += bytes_per_pixel;
  1358.                 }
  1359.             }
  1360.             x2 = x1;
  1361.             for (j=y1; j<=y2; j++) {
  1362.                 p = &the_buffer[j * bytes_per_row];
  1363.                 p2 = &the_buffer_copy[j * bytes_per_row];
  1364.                 p += bytes_per_row;
  1365.                 p2 += bytes_per_row;
  1366.                 for (i=VideoMonitor.x; i>x2; i--) {
  1367.                     p -= bytes_per_pixel;
  1368.                     p2 -= bytes_per_pixel;
  1369.                     if (memcmp(p, p2, bytes_per_pixel)) {
  1370.                         x2 = i;
  1371.                         break;
  1372.                     }
  1373.                 }
  1374.             }
  1375.             wide = x2 - x1;
  1376.  
  1377.             // Update copy of the_buffer
  1378.             if (high && wide) {
  1379.                 for (j=y1; j<=y2; j++) {
  1380.                     i = j * bytes_per_row + x1 * bytes_per_pixel;
  1381.                     memcpy(&the_buffer_copy[i], &the_buffer[i], bytes_per_pixel * wide);
  1382.                 }
  1383.             }
  1384.         }
  1385.     }
  1386.  
  1387.     // Refresh display
  1388.     if (high && wide) {
  1389.         if (have_shm)
  1390.             XShmPutImage(x_display, the_win, the_gc, img, x1, y1, x1, y1, wide, high, 0);
  1391.         else
  1392.             XPutImage(x_display, the_win, the_gc, img, x1, y1, x1, y1, wide, high);
  1393.     }
  1394.  
  1395.     // Has the Mac started? (cursor data is not valid otherwise)
  1396.     if (HasMacStarted()) {
  1397.  
  1398.         // Set new cursor image if it was changed
  1399.         if (memcmp(the_cursor, Mac2HostAddr(0x844), 64)) {
  1400.             Mac2Host_memcpy(the_cursor, 0x844, 64);
  1401.             memcpy(cursor_image->data, the_cursor, 32);
  1402.             memcpy(cursor_mask_image->data, the_cursor+32, 32);
  1403.             XFreeCursor(x_display, mac_cursor);
  1404.             XPutImage(x_display, cursor_map, cursor_gc, cursor_image, 0, 0, 0, 0, 16, 16);
  1405.             XPutImage(x_display, cursor_mask_map, cursor_mask_gc, cursor_mask_image, 0, 0, 0, 0, 16, 16);
  1406.             mac_cursor = XCreatePixmapCursor(x_display, cursor_map, cursor_mask_map, &black, &white, ReadMacInt8(0x885), ReadMacInt8(0x887));
  1407.             XDefineCursor(x_display, the_win, mac_cursor);
  1408.         }
  1409.     }
  1410. }
  1411.  
  1412.  
  1413. /*
  1414.  *  Thread for screen refresh, input handling etc.
  1415.  */
  1416.  
  1417. static void *redraw_func(void *arg)
  1418. {
  1419.     int tick_counter = 0;
  1420.  
  1421.     while (!redraw_thread_cancel) {
  1422.  
  1423.         // Wait
  1424. #ifdef HAVE_NANOSLEEP
  1425.         struct timespec req = {0, 16666667};
  1426.         nanosleep(&req, NULL);
  1427. #else
  1428.         usleep(16667);
  1429. #endif
  1430.  
  1431. #if ENABLE_XF86_DGA
  1432.         // Quit DGA mode if requested
  1433.         if (quit_full_screen) {
  1434.             quit_full_screen = false;
  1435.             if (display_type == DISPLAY_DGA) {
  1436.                 XF86DGADirectVideo(x_display, screen, 0);
  1437.                 XUngrabPointer(x_display, CurrentTime);
  1438.                 XUngrabKeyboard(x_display, CurrentTime);
  1439.                 XUnmapWindow(x_display, the_win);
  1440.                 XSync(x_display, false);
  1441.             }
  1442.         }
  1443. #endif
  1444.  
  1445. #if ENABLE_FBDEV_DGA
  1446.         // Quit DGA mode if requested
  1447.         if (quit_full_screen) {
  1448.             quit_full_screen = false;
  1449.             if (display_type == DISPLAY_DGA) {
  1450.                 XUngrabPointer(x_display, CurrentTime);
  1451.                 XUngrabKeyboard(x_display, CurrentTime);
  1452.                 XUnmapWindow(x_display, the_win);
  1453.                 XSync(x_display, false);
  1454.             }
  1455.         }
  1456. #endif
  1457.         // Handle X events
  1458.         handle_events();
  1459.  
  1460.         // Handle palette changes
  1461.         pthread_mutex_lock(&palette_lock);
  1462.         if (palette_changed) {
  1463.             palette_changed = false;
  1464.             if (depth == 8) {
  1465.                 XStoreColors(x_display, cmap[0], palette, 256);
  1466.                 XStoreColors(x_display, cmap[1], palette, 256);
  1467.                 
  1468. #if ENABLE_XF86_DGA
  1469.                 if (display_type == DISPLAY_DGA) {
  1470.                     current_dga_cmap ^= 1;
  1471.                     XF86DGAInstallColormap(x_display, screen, cmap[current_dga_cmap]);
  1472.                 }
  1473. #endif
  1474.             }
  1475.         }
  1476.         pthread_mutex_unlock(&palette_lock);
  1477.  
  1478.         // In window mode, update display and mouse pointer
  1479.         if (display_type == DISPLAY_WINDOW) {
  1480.             tick_counter++;
  1481.             if (tick_counter >= frame_skip) {
  1482.                 tick_counter = 0;
  1483.                 update_display();
  1484.             }
  1485.         }
  1486.     }
  1487.     return NULL;
  1488. }
  1489.