ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/video_x.cpp
(Generate patch)

Comparing BasiliskII/src/Unix/video_x.cpp (file contents):
Revision 1.1 by cebix, 1999-10-03T14:16:25Z vs.
Revision 1.17 by cebix, 2000-07-22T16:07:21Z

# Line 1 | Line 1
1   /*
2   *  video_x.cpp - Video/graphics emulation, X11 specific stuff
3   *
4 < *  Basilisk II (C) 1997-1999 Christian Bauer
4 > *  Basilisk II (C) 1997-2000 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
# Line 34 | Line 34
34   #include <X11/extensions/XShm.h>
35   #include <sys/ipc.h>
36   #include <sys/shm.h>
37 #include <pthread.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   #include "cpu_emulation.h"
56   #include "main.h"
57   #include "adb.h"
# Line 45 | Line 60
60   #include "user_strings.h"
61   #include "video.h"
62  
63 < #define DEBUG 1
63 > #define DEBUG 0
64   #include "debug.h"
65  
51 #if ENABLE_DGA
52 #include <X11/extensions/xf86dga.h>
53 #endif
54
66  
67   // Display types
68   enum {
# Line 59 | Line 70 | enum {
70          DISPLAY_DGA             // DGA fullscreen display
71   };
72  
62
73   // Constants
74 < const char KEYCODE_FILE_NAME[] = "/usr/local/lib/basilisk_ii_keycodes";
74 > const char KEYCODE_FILE_NAME[] = DATADIR "/keycodes";
75 > const char FBDEVICES_FILE_NAME[] = DATADIR "/fbdevices";
76  
77  
78   // Global variables
79 < static int32 frame_skip;
79 > static int32 frame_skip;                                                        // Prefs items
80 > static int16 mouse_wheel_mode = 1;
81 > static int16 mouse_wheel_lines = 3;
82 >
83   static int display_type = DISPLAY_WINDOW;                       // See enum above
84 + static bool local_X11;                                                          // Flag: X server running on local machine?
85   static uint8 *the_buffer;                                                       // Mac frame buffer
86 +
87 + #ifdef HAVE_PTHREADS
88   static bool redraw_thread_active = false;                       // Flag: Redraw thread installed
89   static volatile bool redraw_thread_cancel = false;      // Flag: Cancel Redraw thread
90   static pthread_t redraw_thread;                                         // Redraw thread
91 + #endif
92  
93   static bool has_dga = false;                                            // Flag: Video DGA capable
94 + static bool has_vidmode = false;                                        // Flag: VidMode extension available
95  
96   static bool ctrl_down = false;                                          // Flag: Ctrl key pressed
97 + static bool caps_on = false;                                            // Flag: Caps Lock on
98   static bool quit_full_screen = false;                           // Flag: DGA close requested from redraw thread
99   static bool emerg_quit = false;                                         // Flag: Ctrl-Esc pressed, emergency quit requested from MacOS thread
100   static bool emul_suspended = false;                                     // Flag: Emulator suspended
# Line 98 | Line 118 | static int eventmask;
118   static const int win_eventmask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | EnterWindowMask | ExposureMask;
119   static const int dga_eventmask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask;
120  
101 static pthread_mutex_t palette_lock = PTHREAD_MUTEX_INITIALIZER;        // Mutex to protect palette
121   static XColor palette[256];                                                     // Color palette for 8-bit mode
122   static bool palette_changed = false;                            // Flag: Palette changed, redraw thread must set new colors
123 + #ifdef HAVE_PTHREADS
124 + static pthread_mutex_t palette_lock = PTHREAD_MUTEX_INITIALIZER;        // Mutex to protect palette
125 + #endif
126  
127   // Variables for window mode
128   static GC the_gc;
129   static XImage *img = NULL;
130   static XShmSegmentInfo shminfo;
109 static XImage *cursor_image, *cursor_mask_image;
110 static Pixmap cursor_map, cursor_mask_map;
131   static Cursor mac_cursor;
112 static GC cursor_gc, cursor_mask_gc;
132   static uint8 *the_buffer_copy = NULL;                           // Copy of Mac frame buffer
114 static uint8 the_cursor[64];                                            // Cursor image data
133   static bool have_shm = false;                                           // Flag: SHM extensions available
134 + static bool updt_box[17][17];                                           // Flag for Update
135 + static int nr_boxes;
136 + static const int sm_uptd[] = {4,1,6,3,0,5,2,7};
137 + static int sm_no_boxes[] = {1,8,32,64,128,300};
138  
139 < // Variables for DGA mode
139 > // Variables for XF86 DGA mode
140   static int current_dga_cmap;                                            // Number (0 or 1) of currently installed DGA colormap
141   static Window suspend_win;                                                      // "Suspend" window
142   static void *fb_save = NULL;                                            // Saved frame buffer for suspend
143 <
143 > #ifdef HAVE_PTHREADS
144   static pthread_mutex_t frame_buffer_lock = PTHREAD_MUTEX_INITIALIZER;   // Mutex to protect frame buffer
145 + #endif
146 +
147 + // Variables for fbdev DGA mode
148 + const char FBDEVICE_FILE_NAME[] = "/dev/fb";
149 + static int fbdev_fd;
150 +
151 + #ifdef ENABLE_XF86_VIDMODE
152 + // Variables for XF86 VidMode support
153 + static XF86VidModeModeInfo **x_video_modes;                     // Array of all available modes
154 + static int num_x_video_modes;
155 + #endif
156  
157  
158   // Prototypes
# Line 128 | Line 161 | static int event2keycode(XKeyEvent *ev);
161  
162  
163   // From main_unix.cpp
164 + extern char *x_display_name;
165   extern Display *x_display;
166  
167   // From sys_unix.cpp
# Line 141 | Line 175 | extern void SysMountFirstFloppy(void);
175   // Set VideoMonitor according to video mode
176   void set_video_monitor(int width, int height, int bytes_per_row, bool native_byte_order)
177   {
178 <        int layout;
178 > #if !REAL_ADDRESSING
179 >        int layout = FLAYOUT_DIRECT;
180          switch (depth) {
181                  case 1:
182                          layout = FLAYOUT_DIRECT;
148                        VideoMonitor.mode = VMODE_1BIT;
183                          break;
184                  case 8:
185                          layout = FLAYOUT_DIRECT;
152                        VideoMonitor.mode = VMODE_8BIT;
186                          break;
187                  case 15:
188                          layout = FLAYOUT_HOST_555;
156                        VideoMonitor.mode = VMODE_16BIT;
189                          break;
190                  case 16:
191                          layout = FLAYOUT_HOST_565;
160                        VideoMonitor.mode = VMODE_16BIT;
192                          break;
193                  case 24:
194                  case 32:
195                          layout = FLAYOUT_HOST_888;
165                        VideoMonitor.mode = VMODE_32BIT;
196                          break;
197          }
168        VideoMonitor.x = width;
169        VideoMonitor.y = height;
170        VideoMonitor.bytes_per_row = bytes_per_row;
198          if (native_byte_order)
199                  MacFrameLayout = layout;
200          else
201                  MacFrameLayout = FLAYOUT_DIRECT;
202 + #endif
203 +        switch (depth) {
204 +                case 1:
205 +                        VideoMonitor.mode = VMODE_1BIT;
206 +                        break;
207 +                case 8:
208 +                        VideoMonitor.mode = VMODE_8BIT;
209 +                        break;
210 +                case 15:
211 +                        VideoMonitor.mode = VMODE_16BIT;
212 +                        break;
213 +                case 16:
214 +                        VideoMonitor.mode = VMODE_16BIT;
215 +                        break;
216 +                case 24:
217 +                case 32:
218 +                        VideoMonitor.mode = VMODE_32BIT;
219 +                        break;
220 +        }
221 +        VideoMonitor.x = width;
222 +        VideoMonitor.y = height;
223 +        VideoMonitor.bytes_per_row = bytes_per_row;
224   }
225  
226   // Trap SHM errors
# Line 190 | Line 239 | static int error_handler(Display *d, XEr
239   // Init window mode
240   static bool init_window(int width, int height)
241   {
242 +        int aligned_width = (width + 15) & ~15;
243 +        int aligned_height = (height + 15) & ~15;
244 +
245          // Set absolute mouse mode
246          ADBSetRelMouseMode(false);
247  
248          // Read frame skip prefs
249          frame_skip = PrefsFindInt32("frameskip");
198        if (frame_skip == 0)
199                frame_skip = 1;
250  
251          // Create window
252          XSetWindowAttributes wattr;
253          wattr.event_mask = eventmask = win_eventmask;
254          wattr.background_pixel = black_pixel;
255          wattr.border_pixel = black_pixel;
256 <        wattr.backing_store = Always;
256 >        wattr.backing_store = NotUseful;
257 >        wattr.save_under = false;
258          wattr.backing_planes = xdepth;
259  
260          XSync(x_display, false);
# Line 232 | Line 283 | static bool init_window(int width, int h
283                  XSetWMNormalHints(x_display, the_win, hints);
284                  XFree((char *)hints);
285          }
286 <
286 >        
287          // Try to create and attach SHM image
288          have_shm = false;
289 <        if (depth != 1 && XShmQueryExtension(x_display)) {
289 >        if (depth != 1 && local_X11 && XShmQueryExtension(x_display)) {
290  
291                  // Create SHM image ("height + 2" for safety)
292                  img = XShmCreateImage(x_display, vis, depth, depth == 1 ? XYBitmap : ZPixmap, 0, &shminfo, width, height);
293 <                shminfo.shmid = shmget(IPC_PRIVATE, (height + 2) * img->bytes_per_line, IPC_CREAT | 0777);
294 <                the_buffer = (uint8 *)shmat(shminfo.shmid, 0, 0);
295 <                shminfo.shmaddr = img->data = (char *)the_buffer;
293 >                shminfo.shmid = shmget(IPC_PRIVATE, (aligned_height + 2) * img->bytes_per_line, IPC_CREAT | 0777);
294 >                the_buffer_copy = (uint8 *)shmat(shminfo.shmid, 0, 0);
295 >                shminfo.shmaddr = img->data = (char *)the_buffer_copy;
296                  shminfo.readOnly = False;
297  
298                  // Try to attach SHM image, catching errors
# Line 259 | Line 310 | static bool init_window(int width, int h
310                          shmctl(shminfo.shmid, IPC_RMID, 0);
311                  }
312          }
313 <
313 >        
314          // Create normal X image if SHM doesn't work ("height + 2" for safety)
315          if (!have_shm) {
316 <                int bytes_per_row = width;
316 >                int bytes_per_row = aligned_width;
317                  switch (depth) {
318                          case 1:
319                                  bytes_per_row /= 8;
# Line 276 | Line 327 | static bool init_window(int width, int h
327                                  bytes_per_row *= 4;
328                                  break;
329                  }
330 <                the_buffer = (uint8 *)malloc((height + 2) * bytes_per_row);
331 <                img = XCreateImage(x_display, vis, depth, depth == 1 ? XYBitmap : ZPixmap, 0, (char *)the_buffer, width, height, 32, bytes_per_row);
330 >                the_buffer_copy = (uint8 *)malloc((aligned_height + 2) * bytes_per_row);
331 >                img = XCreateImage(x_display, vis, depth, depth == 1 ? XYBitmap : ZPixmap, 0, (char *)the_buffer_copy, aligned_width, aligned_height, 32, bytes_per_row);
332          }
333  
334          // 1-Bit mode is big-endian
# Line 286 | Line 337 | static bool init_window(int width, int h
337                  img->bitmap_bit_order = MSBFirst;
338          }
339  
340 <        // Allocate memory for frame buffer copy
341 <        the_buffer_copy = (uint8 *)malloc((height + 2) * img->bytes_per_line);
340 >        // Allocate memory for frame buffer
341 >        the_buffer = (uint8 *)malloc((aligned_height + 2) * img->bytes_per_line);
342  
343          // Create GC
344          the_gc = XCreateGC(x_display, the_win, 0, 0);
345          XSetState(x_display, the_gc, black_pixel, white_pixel, GXcopy, AllPlanes);
346  
347 <        // Create cursor
348 <        cursor_image = XCreateImage(x_display, vis, 1, XYPixmap, 0, (char *)the_cursor, 16, 16, 16, 2);
349 <        cursor_image->byte_order = MSBFirst;
350 <        cursor_image->bitmap_bit_order = MSBFirst;
351 <        cursor_mask_image = XCreateImage(x_display, vis, 1, XYPixmap, 0, (char *)the_cursor+32, 16, 16, 16, 2);
352 <        cursor_mask_image->byte_order = MSBFirst;
302 <        cursor_mask_image->bitmap_bit_order = MSBFirst;
303 <        cursor_map = XCreatePixmap(x_display, the_win, 16, 16, 1);
304 <        cursor_mask_map = XCreatePixmap(x_display, the_win, 16, 16, 1);
305 <        cursor_gc = XCreateGC(x_display, cursor_map, 0, 0);
306 <        cursor_mask_gc = XCreateGC(x_display, cursor_mask_map, 0, 0);
307 <        mac_cursor = XCreatePixmapCursor(x_display, cursor_map, cursor_mask_map, &black, &white, 0, 0);
347 >        // Create no_cursor
348 >        mac_cursor = XCreatePixmapCursor(x_display,
349 >           XCreatePixmap(x_display, the_win, 1, 1, 1),
350 >           XCreatePixmap(x_display, the_win, 1, 1, 1),
351 >           &black, &white, 0, 0);
352 >        XDefineCursor(x_display, the_win, mac_cursor);
353  
354          // Set VideoMonitor
355   #ifdef WORDS_BIGENDIAN
# Line 312 | Line 357 | static bool init_window(int width, int h
357   #else
358          set_video_monitor(width, height, img->bytes_per_line, img->bitmap_bit_order == LSBFirst);
359   #endif
360 +        
361 + #if REAL_ADDRESSING
362 +        VideoMonitor.mac_frame_base = (uint32)the_buffer;
363 + #else
364 +        VideoMonitor.mac_frame_base = MacFrameBaseMac;
365 + #endif
366 +        return true;
367 + }
368 +
369 + // Init fbdev DGA display
370 + static bool init_fbdev_dga(char *in_fb_name)
371 + {
372 + #ifdef ENABLE_FBDEV_DGA
373 +        // Find the maximum depth available
374 +        int ndepths, max_depth(0);
375 +        int *depths = XListDepths(x_display, screen, &ndepths);
376 +        if (depths == NULL) {
377 +                printf("FATAL: Could not determine the maximal depth available\n");
378 +                return false;
379 +        } else {
380 +                while (ndepths-- > 0) {
381 +                        if (depths[ndepths] > max_depth)
382 +                                max_depth = depths[ndepths];
383 +                }
384 +        }
385 +        
386 +        // Get fbdevices file path from preferences
387 +        const char *fbd_path = PrefsFindString("fbdevicefile");
388 +        
389 +        // Open fbdevices file
390 +        FILE *fp = fopen(fbd_path ? fbd_path : FBDEVICES_FILE_NAME, "r");
391 +        if (fp == NULL) {
392 +                char str[256];
393 +                sprintf(str, GetString(STR_NO_FBDEVICE_FILE_ERR), fbd_path ? fbd_path : FBDEVICES_FILE_NAME, strerror(errno));
394 +                ErrorAlert(str);
395 +                return false;
396 +        }
397 +        
398 +        int fb_depth;           // supported depth
399 +        uint32 fb_offset;       // offset used for mmap(2)
400 +        char fb_name[20];
401 +        char line[256];
402 +        bool device_found = false;
403 +        while (fgets(line, 255, fp)) {
404 +                // Read line
405 +                int len = strlen(line);
406 +                if (len == 0)
407 +                        continue;
408 +                line[len - 1] = '\0';
409 +                
410 +                // Comments begin with "#" or ";"
411 +                if ((line[0] == '#') || (line[0] == ';') || (line[0] == '\0'))
412 +                        continue;
413 +                
414 +                if ((sscanf(line, "%19s %d %x", &fb_name, &fb_depth, &fb_offset) == 3)
415 +                && (strcmp(fb_name, in_fb_name) == 0) && (fb_depth == max_depth)) {
416 +                        device_found = true;
417 +                        break;
418 +                }
419 +        }
420 +        
421 +        // fbdevices file completely read
422 +        fclose(fp);
423 +        
424 +        // Frame buffer name not found ? Then, display warning
425 +        if (!device_found) {
426 +                char str[256];
427 +                sprintf(str, GetString(STR_FBDEV_NAME_ERR), in_fb_name, max_depth);
428 +                ErrorAlert(str);
429 +                return false;
430 +        }
431 +        
432 +        int width = DisplayWidth(x_display, screen);
433 +        int height = DisplayHeight(x_display, screen);
434 +        depth = fb_depth; // max_depth
435 +        
436 +        // Set relative mouse mode
437 +        ADBSetRelMouseMode(false);
438 +        
439 +        // Create window
440 +        XSetWindowAttributes wattr;
441 +        wattr.override_redirect = True;
442 +        wattr.backing_store             = NotUseful;
443 +        wattr.background_pixel  = white_pixel;
444 +        wattr.border_pixel              = black_pixel;
445 +        wattr.event_mask                = eventmask = dga_eventmask;
446 +        
447 +        XSync(x_display, false);
448 +        the_win = XCreateWindow(x_display, rootwin,
449 +                0, 0, width, height,
450 +                0, xdepth, InputOutput, vis,
451 +                CWEventMask|CWBackPixel|CWBorderPixel|CWOverrideRedirect|CWBackingStore,
452 +                &wattr);
453 +        XSync(x_display, false);
454 +        XMapRaised(x_display, the_win);
455 +        XSync(x_display, false);
456 +        
457 +        // Grab mouse and keyboard
458 +        XGrabKeyboard(x_display, the_win, True,
459 +                GrabModeAsync, GrabModeAsync, CurrentTime);
460 +        XGrabPointer(x_display, the_win, True,
461 +                PointerMotionMask | ButtonPressMask | ButtonReleaseMask,
462 +                GrabModeAsync, GrabModeAsync, the_win, None, CurrentTime);
463 +        
464 +        // Set colormap
465 +        if (depth == 8) {
466 +                XSetWindowColormap(x_display, the_win, cmap[0]);
467 +                XSetWMColormapWindows(x_display, the_win, &the_win, 1);
468 +        }
469 +        
470 +        // Set VideoMonitor
471 +        int bytes_per_row = width;
472 +        switch (depth) {
473 +                case 1:
474 +                        bytes_per_row = ((width | 7) & ~7) >> 3;
475 +                        break;
476 +                case 15:
477 +                case 16:
478 +                        bytes_per_row *= 2;
479 +                        break;
480 +                case 24:
481 +                case 32:
482 +                        bytes_per_row *= 4;
483 +                        break;
484 +        }
485 +        
486 +        if ((the_buffer = (uint8 *) mmap(NULL, height * bytes_per_row, PROT_READ | PROT_WRITE, MAP_PRIVATE, fbdev_fd, fb_offset)) == MAP_FAILED) {
487 +                if ((the_buffer = (uint8 *) mmap(NULL, height * bytes_per_row, PROT_READ | PROT_WRITE, MAP_SHARED, fbdev_fd, fb_offset)) == MAP_FAILED) {
488 +                        char str[256];
489 +                        sprintf(str, GetString(STR_FBDEV_MMAP_ERR), strerror(errno));
490 +                        ErrorAlert(str);
491 +                        return false;
492 +                }
493 +        }
494 +        
495 +        set_video_monitor(width, height, bytes_per_row, true);
496   #if REAL_ADDRESSING
497          VideoMonitor.mac_frame_base = (uint32)the_buffer;
317        MacFrameLayout = FLAYOUT_DIRECT;
498   #else
499          VideoMonitor.mac_frame_base = MacFrameBaseMac;
500   #endif
501          return true;
502 + #else
503 +        ErrorAlert("Basilisk II has been compiled with fbdev DGA support disabled.");
504 +        return false;
505 + #endif
506   }
507  
508 < // Init DGA display
509 < static bool init_dga(int width, int height)
508 > // Init XF86 DGA display
509 > static bool init_xf86_dga(int width, int height)
510   {
511 < #if ENABLE_DGA
511 > #ifdef ENABLE_XF86_DGA
512          // Set relative mouse mode
513          ADBSetRelMouseMode(true);
514  
515 + #ifdef ENABLE_XF86_VIDMODE
516 +        // Switch to best mode
517 +        if (has_vidmode) {
518 +                int best = 0;
519 +                for (int i=1; i<num_x_video_modes; i++) {
520 +                        if (x_video_modes[i]->hdisplay >= width && x_video_modes[i]->vdisplay >= height &&
521 +                                x_video_modes[i]->hdisplay <= x_video_modes[best]->hdisplay && x_video_modes[i]->vdisplay <= x_video_modes[best]->vdisplay) {
522 +                                best = i;
523 +                        }
524 +                }
525 +                XF86VidModeSwitchToMode(x_display, screen, x_video_modes[best]);
526 +                XF86VidModeSetViewPort(x_display, screen, 0, 0);
527 +        }
528 + #endif
529 +
530          // Create window
531          XSetWindowAttributes wattr;
532          wattr.event_mask = eventmask = dga_eventmask;
# Line 385 | Line 584 | static bool init_dga(int width, int heig
584   #endif
585          return true;
586   #else
587 <        ErrorAlert("Basilisk II has been compiled with DGA support disabled.");
587 >        ErrorAlert("Basilisk II has been compiled with XF86 DGA support disabled.");
588          return false;
589   #endif
590   }
# Line 457 | Line 656 | static void keycode_init(void)
656  
657   bool VideoInit(bool classic)
658   {
659 +        // Check if X server runs on local machine
660 +        local_X11 = (strncmp(XDisplayName(x_display_name), ":", 1) == 0)
661 +                 || (strncmp(XDisplayName(x_display_name), "unix:", 5) == 0);
662 +    
663          // Init keycode translation
664          keycode_init();
665  
666 +        // Read prefs
667 +        mouse_wheel_mode = PrefsFindInt16("mousewheelmode");
668 +        mouse_wheel_lines = PrefsFindInt16("mousewheellines");
669 +
670          // Find screen and root window
671          screen = XDefaultScreen(x_display);
672          rootwin = XRootWindow(x_display, screen);
673 <
673 >        
674          // Get screen depth
675          xdepth = DefaultDepth(x_display, screen);
676 +        
677 + #ifdef ENABLE_FBDEV_DGA
678 +        // Frame buffer name
679 +        char fb_name[20];
680 +        
681 +        // Could do fbdev dga ?
682 +        if ((fbdev_fd = open(FBDEVICE_FILE_NAME, O_RDWR)) != -1)
683 +                has_dga = true;
684 +        else
685 +                has_dga = false;
686 + #endif
687  
688 < #if ENABLE_DGA
688 > #ifdef ENABLE_XF86_DGA
689          // DGA available?
690 <        int dga_flags = 0;
691 <        XF86DGAQueryDirectVideo(x_display, screen, &dga_flags);
692 <        has_dga = dga_flags & XF86DGADirectPresent;
690 >        int dga_event_base, dga_error_base;
691 >        if (local_X11 && XF86DGAQueryExtension(x_display, &dga_event_base, &dga_error_base)) {
692 >                int dga_flags = 0;
693 >                XF86DGAQueryDirectVideo(x_display, screen, &dga_flags);
694 >                has_dga = dga_flags & XF86DGADirectPresent;
695 >        } else
696 >                has_dga = false;
697   #endif
698  
699 + #ifdef ENABLE_XF86_VIDMODE
700 +        // VidMode available?
701 +        int vm_event_base, vm_error_base;
702 +        has_vidmode = XF86VidModeQueryExtension(x_display, &vm_event_base, &vm_error_base);
703 +        if (has_vidmode)
704 +                XF86VidModeGetAllModeLines(x_display, screen, &num_x_video_modes, &x_video_modes);
705 + #endif
706 +        
707          // Find black and white colors
708          XParseColor(x_display, DefaultColormap(x_display, screen), "rgb:00/00/00", &black);
709          XAllocColor(x_display, DefaultColormap(x_display, screen), &black);
# Line 539 | Line 769 | bool VideoInit(bool classic)
769          if (mode_str) {
770                  if (sscanf(mode_str, "win/%d/%d", &width, &height) == 2)
771                          display_type = DISPLAY_WINDOW;
772 <                else if (has_dga && strcmp(mode_str, "dga") == 0) {
772 > #ifdef ENABLE_FBDEV_DGA
773 >                else if (has_dga && sscanf(mode_str, "dga/%19s", fb_name) == 1) {
774 > #else
775 >                else if (has_dga && sscanf(mode_str, "dga/%d/%d", &width, &height) == 2) {
776 > #endif
777                          display_type = DISPLAY_DGA;
778 +                        if (width > DisplayWidth(x_display, screen))
779 +                                width = DisplayWidth(x_display, screen);
780 +                        if (height > DisplayHeight(x_display, screen))
781 +                                height = DisplayHeight(x_display, screen);
782 +                }
783 +                if (width <= 0)
784                          width = DisplayWidth(x_display, screen);
785 +                if (height <= 0)
786                          height = DisplayHeight(x_display, screen);
546                }
787          }
788  
789          // Initialize according to display type
# Line 553 | Line 793 | bool VideoInit(bool classic)
793                                  return false;
794                          break;
795                  case DISPLAY_DGA:
796 <                        if (!init_dga(width, height))
796 > #ifdef ENABLE_FBDEV_DGA
797 >                        if (!init_fbdev_dga(fb_name))
798 > #else
799 >                        if (!init_xf86_dga(width, height))
800 > #endif
801                                  return false;
802                          break;
803          }
804  
805 + #ifdef HAVE_PTHREADS
806          // Lock down frame buffer
807          pthread_mutex_lock(&frame_buffer_lock);
808 + #endif
809  
810   #if !REAL_ADDRESSING
811          // Set variables for UAE memory mapping
# Line 571 | Line 817 | bool VideoInit(bool classic)
817                  MacFrameLayout = FLAYOUT_NONE;
818   #endif
819  
574        // Start redraw/input thread
820          XSync(x_display, false);
821 +
822 + #ifdef HAVE_PTHREADS
823 +        // Start redraw/input thread
824          redraw_thread_active = (pthread_create(&redraw_thread, NULL, redraw_func, NULL) == 0);
825 <        if (!redraw_thread_active)
825 >        if (!redraw_thread_active) {
826                  printf("FATAL: cannot create redraw thread\n");
827 <        return redraw_thread_active;
827 >                return false;
828 >        }
829 > #endif
830 >        return true;
831   }
832  
833  
# Line 586 | Line 837 | bool VideoInit(bool classic)
837  
838   void VideoExit(void)
839   {
840 + #ifdef HAVE_PTHREADS
841          // Stop redraw thread
842          if (redraw_thread_active) {
843                  redraw_thread_cancel = true;
# Line 595 | Line 847 | void VideoExit(void)
847                  pthread_join(redraw_thread, NULL);
848                  redraw_thread_active = false;
849          }
850 + #endif
851  
852 + #ifdef HAVE_PTHREADS
853          // Unlock frame buffer
854          pthread_mutex_unlock(&frame_buffer_lock);
855 + #endif
856  
857          // Close window and server connection
858          if (x_display != NULL) {
859                  XSync(x_display, false);
860  
861 < #if ENABLE_DGA
861 > #ifdef ENABLE_XF86_DGA
862                  if (display_type == DISPLAY_DGA) {
863                          XF86DGADirectVideo(x_display, screen, 0);
864                          XUngrabPointer(x_display, CurrentTime);
# Line 611 | Line 866 | void VideoExit(void)
866                  }
867   #endif
868  
869 <                if (the_buffer_copy) {
870 <                        free(the_buffer_copy);
871 <                        the_buffer_copy = NULL;
869 > #ifdef ENABLE_XF86_VIDMODE
870 >                if (has_vidmode && display_type == DISPLAY_DGA)
871 >                        XF86VidModeSwitchToMode(x_display, screen, x_video_modes[0]);
872 > #endif
873 >
874 > #ifdef ENABLE_FBDEV_DGA
875 >                if (display_type == DISPLAY_DGA) {
876 >                        XUngrabPointer(x_display, CurrentTime);
877 >                        XUngrabKeyboard(x_display, CurrentTime);
878 >                        close(fbdev_fd);
879                  }
880 + #endif
881  
882                  XFlush(x_display);
883                  XSync(x_display, false);
# Line 622 | Line 885 | void VideoExit(void)
885                          XFreeColormap(x_display, cmap[0]);
886                          XFreeColormap(x_display, cmap[1]);
887                  }
888 +
889 +                if (the_buffer) {
890 +                        free(the_buffer);
891 +                        the_buffer = NULL;
892 +                }
893 +
894 +                if (!have_shm && the_buffer_copy) {
895 +                        free(the_buffer_copy);
896 +                        the_buffer_copy = NULL;
897 +                }
898          }
899   }
900  
# Line 648 | Line 921 | void VideoInterrupt(void)
921          if (emerg_quit)
922                  QuitEmulator();
923  
924 + #ifdef HAVE_PTHREADS
925          // Temporarily give up frame buffer lock (this is the point where
926          // we are suspended when the user presses Ctrl-Tab)
927          pthread_mutex_unlock(&frame_buffer_lock);
928          pthread_mutex_lock(&frame_buffer_lock);
929 + #endif
930   }
931  
932  
# Line 661 | Line 936 | void VideoInterrupt(void)
936  
937   void video_set_palette(uint8 *pal)
938   {
939 + #ifdef HAVE_PTHREDS
940          pthread_mutex_lock(&palette_lock);
941 + #endif
942  
943          // Convert colors to XColor array
944          for (int i=0; i<256; i++) {
# Line 675 | Line 952 | void video_set_palette(uint8 *pal)
952          // Tell redraw thread to change palette
953          palette_changed = true;
954  
955 + #ifdef HAVE_PTHREADS
956          pthread_mutex_unlock(&palette_lock);
957 + #endif
958   }
959  
960  
# Line 683 | Line 962 | void video_set_palette(uint8 *pal)
962   *  Suspend/resume emulator
963   */
964  
965 < #if ENABLE_DGA
965 > #if defined(ENABLE_XF86_DGA) || defined(ENABLE_FBDEV_DGA)
966   static void suspend_emul(void)
967   {
968          if (display_type == DISPLAY_DGA) {
# Line 691 | Line 970 | static void suspend_emul(void)
970                  ADBKeyUp(0x36);
971                  ctrl_down = false;
972  
973 + #ifdef HAVE_PTHREADS
974                  // Lock frame buffer (this will stop the MacOS thread)
975                  pthread_mutex_lock(&frame_buffer_lock);
976 + #endif
977  
978                  // Save frame buffer
979                  fb_save = malloc(VideoMonitor.y * VideoMonitor.bytes_per_row);
# Line 700 | Line 981 | static void suspend_emul(void)
981                          memcpy(fb_save, the_buffer, VideoMonitor.y * VideoMonitor.bytes_per_row);
982  
983                  // Close full screen display
984 + #ifdef ENABLE_XF86_DGA
985                  XF86DGADirectVideo(x_display, screen, 0);
986 + #endif
987                  XUngrabPointer(x_display, CurrentTime);
988                  XUngrabKeyboard(x_display, CurrentTime);
989                  XUnmapWindow(x_display, the_win);
# Line 714 | Line 997 | static void suspend_emul(void)
997                  wattr.backing_store = Always;
998                  wattr.backing_planes = xdepth;
999                  wattr.colormap = DefaultColormap(x_display, screen);
1000 +                
1001                  XSync(x_display, false);
1002                  suspend_win = XCreateWindow(x_display, rootwin, 0, 0, 512, 1, 0, xdepth,
1003                          InputOutput, vis, CWEventMask | CWBackPixel | CWBorderPixel |
# Line 738 | Line 1022 | static void resume_emul(void)
1022          XSync(x_display, false);
1023          XGrabKeyboard(x_display, rootwin, 1, GrabModeAsync, GrabModeAsync, CurrentTime);
1024          XGrabPointer(x_display, rootwin, 1, PointerMotionMask | ButtonPressMask | ButtonReleaseMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
1025 + #ifdef ENABLE_XF86_DGA
1026          XF86DGADirectVideo(x_display, screen, XF86DGADirectGraphics | XF86DGADirectKeyb | XF86DGADirectMouse);
1027          XF86DGASetViewPort(x_display, screen, 0, 0);
1028 + #endif
1029          XSync(x_display, false);
1030  
1031          // Restore frame buffer
# Line 748 | Line 1034 | static void resume_emul(void)
1034                  free(fb_save);
1035                  fb_save = NULL;
1036          }
1037 +        
1038 + #ifdef ENABLE_XF86_DGA
1039          if (depth == 8)
1040                  XF86DGAInstallColormap(x_display, screen, cmap[current_dga_cmap]);
1041 + #endif
1042  
1043 + #ifdef HAVE_PTHREADS
1044          // Unlock frame buffer (and continue MacOS thread)
1045          pthread_mutex_unlock(&frame_buffer_lock);
1046          emul_suspended = false;
1047 + #endif
1048   }
1049   #endif
1050  
# Line 815 | Line 1106 | static int kc_decode(KeySym ks)
1106                  case XK_period: case XK_greater: return 0x2f;
1107                  case XK_slash: case XK_question: return 0x2c;
1108  
1109 < #if ENABLE_DGA
1109 > #if defined(ENABLE_XF86_DGA) || defined(ENABLE_FBDEV_DGA)
1110                  case XK_Tab: if (ctrl_down) {suspend_emul(); return -1;} else return 0x30;
1111   #else
1112                  case XK_Tab: return 0x30;
# Line 941 | Line 1232 | static void handle_events(void)
1232                                  unsigned int button = ((XButtonEvent *)&event)->button;
1233                                  if (button < 4)
1234                                          ADBMouseDown(button - 1);
1235 +                                else if (button < 6) {  // Wheel mouse
1236 +                                        if (mouse_wheel_mode == 0) {
1237 +                                                int key = (button == 5) ? 0x79 : 0x74;  // Page up/down
1238 +                                                ADBKeyDown(key);
1239 +                                                ADBKeyUp(key);
1240 +                                        } else {
1241 +                                                int key = (button == 5) ? 0x3d : 0x3e;  // Cursor up/down
1242 +                                                for(int i=0; i<mouse_wheel_lines; i++) {
1243 +                                                        ADBKeyDown(key);
1244 +                                                        ADBKeyUp(key);
1245 +                                                }
1246 +                                        }
1247 +                                }
1248                                  break;
1249                          }
1250                          case ButtonRelease: {
# Line 968 | Line 1272 | static void handle_events(void)
1272                                          code = event2keycode((XKeyEvent *)&event);
1273                                  if (code != -1) {
1274                                          if (!emul_suspended) {
1275 <                                                ADBKeyDown(code);
1275 >                                                if (code == 0x39) {     // Caps Lock pressed
1276 >                                                        if (caps_on) {
1277 >                                                                ADBKeyUp(code);
1278 >                                                                caps_on = false;
1279 >                                                        } else {
1280 >                                                                ADBKeyDown(code);
1281 >                                                                caps_on = true;
1282 >                                                        }
1283 >                                                } else
1284 >                                                        ADBKeyDown(code);
1285                                                  if (code == 0x36)
1286                                                          ctrl_down = true;
1287                                          } else {
1288 < #if ENABLE_DGA
1288 > #if defined(ENABLE_XF86_DGA) || defined(ENABLE_FBDEV_DGA)
1289                                                  if (code == 0x31)
1290                                                          resume_emul();  // Space wakes us up
1291   #endif
# Line 987 | Line 1300 | static void handle_events(void)
1300                                          code = keycode_table[((XKeyEvent *)&event)->keycode & 0xff];
1301                                  } else
1302                                          code = event2keycode((XKeyEvent *)&event);
1303 <                                if (code != -1) {
1303 >                                if (code != -1 && code != 0x39) {       // Don't propagate Caps Lock releases
1304                                          ADBKeyUp(code);
1305                                          if (code == 0x36)
1306                                                  ctrl_down = false;
# Line 997 | Line 1310 | static void handle_events(void)
1310  
1311                          // Hidden parts exposed, force complete refresh of window
1312                          case Expose:
1313 <                                if (display_type == DISPLAY_WINDOW)
1314 <                                        memset(the_buffer_copy, 0, VideoMonitor.bytes_per_row * VideoMonitor.y);
1313 >                                if (display_type == DISPLAY_WINDOW) {
1314 >                                        if (frame_skip == 0) {  // Dynamic refresh
1315 >                                                int x1, y1;
1316 >                                                for (y1=0; y1<16; y1++)
1317 >                                                for (x1=0; x1<16; x1++)
1318 >                                                        updt_box[x1][y1] = true;
1319 >                                                nr_boxes = 16 * 16;
1320 >                                        } else
1321 >                                                memset(the_buffer_copy, 0, VideoMonitor.bytes_per_row * VideoMonitor.y);
1322 >                                }
1323                                  break;
1324                  }
1325          }
# Line 1009 | Line 1330 | static void handle_events(void)
1330   *  Window display update
1331   */
1332  
1333 < static void update_display(void)
1333 > // Dynamic display update (variable frame rate for each box)
1334 > static void update_display_dynamic(int ticker)
1335   {
1336 <        // In classic mode, copy the frame buffer from Mac RAM
1337 <        if (classic_mode)
1338 <                memcpy(the_buffer, Mac2HostAddr(0x3fa700), VideoMonitor.bytes_per_row * VideoMonitor.y);
1336 >        int y1, y2, y2s, y2a, i, x1, xm, xmo, ymo, yo, yi, yil, xi;
1337 >        int xil = 0;
1338 >        int rxm = 0, rxmo = 0;
1339 >        int bytes_per_row = VideoMonitor.bytes_per_row;
1340 >        int bytes_per_pixel = VideoMonitor.bytes_per_row / VideoMonitor.x;
1341 >        int rx = VideoMonitor.bytes_per_row / 16;
1342 >        int ry = VideoMonitor.y / 16;
1343 >        int max_box;
1344 >
1345 >        y2s = sm_uptd[ticker % 8];
1346 >        y2a = 8;
1347 >        for (i = 0; i < 6; i++)
1348 >                if (ticker % (2 << i))
1349 >                        break;
1350 >        max_box = sm_no_boxes[i];
1351  
1352 +        if (y2a) {
1353 +                for (y1=0; y1<16; y1++) {
1354 +                        for (y2=y2s; y2 < ry; y2 += y2a) {
1355 +                                i = ((y1 * ry) + y2) * bytes_per_row;
1356 +                                for (x1=0; x1<16; x1++, i += rx) {
1357 +                                        if (updt_box[x1][y1] == false) {
1358 +                                                if (memcmp(&the_buffer_copy[i], &the_buffer[i], rx)) {
1359 +                                                        updt_box[x1][y1] = true;
1360 +                                                        nr_boxes++;
1361 +                                                }
1362 +                                        }
1363 +                                }
1364 +                        }
1365 +                }
1366 +        }
1367 +
1368 +        if ((nr_boxes <= max_box) && (nr_boxes)) {
1369 +                for (y1=0; y1<16; y1++) {
1370 +                        for (x1=0; x1<16; x1++) {
1371 +                                if (updt_box[x1][y1] == true) {
1372 +                                        if (rxm == 0)
1373 +                                                xm = x1;
1374 +                                        rxm += rx;
1375 +                                        updt_box[x1][y1] = false;
1376 +                                }
1377 +                                if (((updt_box[x1+1][y1] == false) || (x1 == 15)) && (rxm)) {
1378 +                                        if ((rxmo != rxm) || (xmo != xm) || (yo != y1 - 1)) {
1379 +                                                if (rxmo) {
1380 +                                                        xi = xmo * rx;
1381 +                                                        yi = ymo * ry;
1382 +                                                        xil = rxmo;
1383 +                                                        yil = (yo - ymo +1) * ry;
1384 +                                                }
1385 +                                                rxmo = rxm;
1386 +                                                xmo = xm;
1387 +                                                ymo = y1;
1388 +                                        }
1389 +                                        rxm = 0;
1390 +                                        yo = y1;
1391 +                                }      
1392 +                                if (xil) {
1393 +                                        i = (yi * bytes_per_row) + xi;
1394 +                                        for (y2=0; y2 < yil; y2++, i += bytes_per_row)
1395 +                                                memcpy(&the_buffer_copy[i], &the_buffer[i], xil);
1396 +                                        if (depth == 1) {
1397 +                                                if (have_shm)
1398 +                                                        XShmPutImage(x_display, the_win, the_gc, img, xi * 8, yi, xi * 8, yi, xil * 8, yil, 0);
1399 +                                                else
1400 +                                                        XPutImage(x_display, the_win, the_gc, img, xi * 8, yi, xi * 8, yi, xil * 8, yil);
1401 +                                        } else {
1402 +                                                if (have_shm)
1403 +                                                        XShmPutImage(x_display, the_win, the_gc, img, xi / bytes_per_pixel, yi, xi / bytes_per_pixel, yi, xil / bytes_per_pixel, yil, 0);
1404 +                                                else
1405 +                                                        XPutImage(x_display, the_win, the_gc, img, xi / bytes_per_pixel, yi, xi / bytes_per_pixel, yi, xil / bytes_per_pixel, yil);
1406 +                                        }
1407 +                                        xil = 0;
1408 +                                }
1409 +                                if ((x1 == 15) && (y1 == 15) && (rxmo)) {
1410 +                                        x1--;
1411 +                                        xi = xmo * rx;
1412 +                                        yi = ymo * ry;
1413 +                                        xil = rxmo;
1414 +                                        yil = (yo - ymo +1) * ry;
1415 +                                        rxmo = 0;
1416 +                                }
1417 +                        }
1418 +                }
1419 +                nr_boxes = 0;
1420 +        }
1421 + }
1422 +
1423 + // Static display update (fixed frame rate, but incremental)
1424 + static void update_display_static(void)
1425 + {
1426          // Incremental update code
1427          int wide = 0, high = 0, x1, x2, y1, y2, i, j;
1428          int bytes_per_row = VideoMonitor.bytes_per_row;
# Line 1075 | Line 1483 | static void update_display(void)
1483                          if (high && wide) {
1484                                  for (j=y1; j<=y2; j++) {
1485                                          i = j * bytes_per_row + (x1 >> 3);
1486 <                                        memcpy(&the_buffer_copy[i], &the_buffer[i], wide >> 3);
1486 >                                        memcpy(the_buffer_copy + i, the_buffer + i, wide >> 3);
1487                                  }
1488                          }
1489  
# Line 1114 | Line 1522 | static void update_display(void)
1522                          if (high && wide) {
1523                                  for (j=y1; j<=y2; j++) {
1524                                          i = j * bytes_per_row + x1 * bytes_per_pixel;
1525 <                                        memcpy(&the_buffer_copy[i], &the_buffer[i], bytes_per_pixel * wide);
1525 >                                        memcpy(the_buffer_copy + i, the_buffer + i, bytes_per_pixel * wide);
1526                                  }
1527                          }
1528                  }
# Line 1127 | Line 1535 | static void update_display(void)
1535                  else
1536                          XPutImage(x_display, the_win, the_gc, img, x1, y1, x1, y1, wide, high);
1537          }
1130
1131        // Has the Mac started? (cursor data is not valid otherwise)
1132        if (HasMacStarted()) {
1133
1134                // Set new cursor image if it was changed
1135                if (memcmp(the_cursor, Mac2HostAddr(0x844), 64)) {
1136                        memcpy(the_cursor, Mac2HostAddr(0x844), 64);
1137                        memcpy(cursor_image->data, the_cursor, 32);
1138                        memcpy(cursor_mask_image->data, the_cursor+32, 32);
1139                        XFreeCursor(x_display, mac_cursor);
1140                        XPutImage(x_display, cursor_map, cursor_gc, cursor_image, 0, 0, 0, 0, 16, 16);
1141                        XPutImage(x_display, cursor_mask_map, cursor_mask_gc, cursor_mask_image, 0, 0, 0, 0, 16, 16);
1142                        mac_cursor = XCreatePixmapCursor(x_display, cursor_map, cursor_mask_map, &black, &white, ReadMacInt8(0x885), ReadMacInt8(0x887));
1143                        XDefineCursor(x_display, the_win, mac_cursor);
1144                }
1145        }
1538   }
1539  
1540  
# Line 1150 | Line 1542 | static void update_display(void)
1542   *  Thread for screen refresh, input handling etc.
1543   */
1544  
1545 < static void *redraw_func(void *arg)
1545 > void VideoRefresh(void)
1546   {
1547 <        int tick_counter = 0;
1548 <
1549 <        while (!redraw_thread_cancel) {
1550 <
1551 <                // Wait
1552 < #ifdef HAVE_NANOSLEEP
1553 <                struct timespec req = {0, 16666667};
1162 <                nanosleep(&req, NULL);
1163 < #else
1164 <                usleep(16667);
1547 > #if defined(ENABLE_XF86_DGA) || defined(ENABLE_FBDEV_DGA)
1548 >        // Quit DGA mode if requested
1549 >        if (quit_full_screen) {
1550 >                quit_full_screen = false;
1551 >                if (display_type == DISPLAY_DGA) {
1552 > #ifdef ENABLE_XF86_DGA
1553 >                        XF86DGADirectVideo(x_display, screen, 0);
1554   #endif
1555 <
1556 < #if ENABLE_DGA
1557 <                // Quit DGA mode if requested
1558 <                if (quit_full_screen) {
1170 <                        quit_full_screen = false;
1171 <                        if (display_type == DISPLAY_DGA) {
1172 <                                XF86DGADirectVideo(x_display, screen, 0);
1173 <                                XUngrabPointer(x_display, CurrentTime);
1174 <                                XUngrabKeyboard(x_display, CurrentTime);
1175 <                                XUnmapWindow(x_display, the_win);
1176 <                                XSync(x_display, false);
1177 <                        }
1555 >                        XUngrabPointer(x_display, CurrentTime);
1556 >                        XUngrabKeyboard(x_display, CurrentTime);
1557 >                        XUnmapWindow(x_display, the_win);
1558 >                        XSync(x_display, false);
1559                  }
1560 +        }
1561   #endif
1562  
1563 <                // Handle X events
1564 <                handle_events();
1563 >        // Handle X events
1564 >        handle_events();
1565  
1566 <                // Handle palette changes
1567 <                pthread_mutex_lock(&palette_lock);
1568 <                if (palette_changed) {
1187 <                        palette_changed = false;
1188 <                        if (depth == 8) {
1189 <                                XStoreColors(x_display, cmap[0], palette, 256);
1190 <                                XStoreColors(x_display, cmap[1], palette, 256);
1191 < #if ENABLE_DGA
1192 <                                if (display_type == DISPLAY_DGA) {
1193 <                                        current_dga_cmap ^= 1;
1194 <                                        XF86DGAInstallColormap(x_display, screen, cmap[current_dga_cmap]);
1195 <                                }
1566 >        // Handle palette changes
1567 > #ifdef HAVE_PTHREADS
1568 >        pthread_mutex_lock(&palette_lock);
1569   #endif
1570 +        if (palette_changed) {
1571 +                palette_changed = false;
1572 +                if (depth == 8) {
1573 +                        XStoreColors(x_display, cmap[0], palette, 256);
1574 +                        XStoreColors(x_display, cmap[1], palette, 256);
1575 +                                
1576 + #ifdef ENABLE_XF86_DGA
1577 +                        if (display_type == DISPLAY_DGA) {
1578 +                                current_dga_cmap ^= 1;
1579 +                                XF86DGAInstallColormap(x_display, screen, cmap[current_dga_cmap]);
1580                          }
1581 + #endif
1582                  }
1583 <                pthread_mutex_unlock(&palette_lock);
1583 >        }
1584 > #ifdef HAVE_PTHREADS
1585 >        pthread_mutex_unlock(&palette_lock);
1586 > #endif
1587  
1588 <                // In window mode, update display and mouse pointer
1589 <                if (display_type == DISPLAY_WINDOW) {
1590 <                        tick_counter++;
1591 <                        if (tick_counter >= frame_skip) {
1592 <                                tick_counter = 0;
1593 <                                update_display();
1594 <                        }
1588 >        // In window mode, update display
1589 >        static int tick_counter = 0;
1590 >        if (display_type == DISPLAY_WINDOW) {
1591 >                tick_counter++;
1592 >                if (frame_skip == 0)
1593 >                        update_display_dynamic(tick_counter);
1594 >                else if (tick_counter >= frame_skip) {
1595 >                        tick_counter = 0;
1596 >                        update_display_static();
1597                  }
1598          }
1599 + }
1600 +
1601 + #ifdef HAVE_PTHREADS
1602 + static void *redraw_func(void *arg)
1603 + {
1604 +        while (!redraw_thread_cancel) {
1605 + #ifdef HAVE_NANOSLEEP
1606 +                struct timespec req = {0, 16666667};
1607 +                nanosleep(&req, NULL);
1608 + #else
1609 +                usleep(16667);
1610 + #endif
1611 +                VideoRefresh();
1612 +        }
1613          return NULL;
1614   }
1615 + #endif

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines