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.2 by cebix, 1999-10-03T16:20:08Z vs.
Revision 1.16 by cebix, 2000-07-13T16:12:33Z

# 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[] = SHAREDIR "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;
# Line 113 | Line 135 | static GC cursor_gc, cursor_mask_gc;
135   static uint8 *the_buffer_copy = NULL;                           // Copy of Mac frame buffer
136   static uint8 the_cursor[64];                                            // Cursor image data
137   static bool have_shm = false;                                           // Flag: SHM extensions available
138 + static bool updt_box[17][17];                                           // Flag for Update
139 + static int nr_boxes;
140 + static const int sm_uptd[] = {4,1,6,3,0,5,2,7};
141 + static int sm_no_boxes[] = {1,8,32,64,128,300};
142  
143 < // Variables for DGA mode
143 > // Variables for XF86 DGA mode
144   static int current_dga_cmap;                                            // Number (0 or 1) of currently installed DGA colormap
145   static Window suspend_win;                                                      // "Suspend" window
146   static void *fb_save = NULL;                                            // Saved frame buffer for suspend
147 <
147 > #ifdef HAVE_PTHREADS
148   static pthread_mutex_t frame_buffer_lock = PTHREAD_MUTEX_INITIALIZER;   // Mutex to protect frame buffer
149 + #endif
150 +
151 + // Variables for fbdev DGA mode
152 + const char FBDEVICE_FILE_NAME[] = "/dev/fb";
153 + static int fbdev_fd;
154 +
155 + #ifdef ENABLE_XF86_VIDMODE
156 + // Variables for XF86 VidMode support
157 + static XF86VidModeModeInfo **x_video_modes;                     // Array of all available modes
158 + static int num_x_video_modes;
159 + #endif
160  
161  
162   // Prototypes
# Line 128 | Line 165 | static int event2keycode(XKeyEvent *ev);
165  
166  
167   // From main_unix.cpp
168 + extern char *x_display_name;
169   extern Display *x_display;
170  
171   // From sys_unix.cpp
# Line 141 | Line 179 | extern void SysMountFirstFloppy(void);
179   // Set VideoMonitor according to video mode
180   void set_video_monitor(int width, int height, int bytes_per_row, bool native_byte_order)
181   {
182 <        int layout;
182 > #if !REAL_ADDRESSING
183 >        int layout = FLAYOUT_DIRECT;
184          switch (depth) {
185                  case 1:
186                          layout = FLAYOUT_DIRECT;
148                        VideoMonitor.mode = VMODE_1BIT;
187                          break;
188                  case 8:
189                          layout = FLAYOUT_DIRECT;
152                        VideoMonitor.mode = VMODE_8BIT;
190                          break;
191                  case 15:
192                          layout = FLAYOUT_HOST_555;
156                        VideoMonitor.mode = VMODE_16BIT;
193                          break;
194                  case 16:
195                          layout = FLAYOUT_HOST_565;
160                        VideoMonitor.mode = VMODE_16BIT;
196                          break;
197                  case 24:
198                  case 32:
199                          layout = FLAYOUT_HOST_888;
165                        VideoMonitor.mode = VMODE_32BIT;
200                          break;
201          }
168        VideoMonitor.x = width;
169        VideoMonitor.y = height;
170        VideoMonitor.bytes_per_row = bytes_per_row;
202          if (native_byte_order)
203                  MacFrameLayout = layout;
204          else
205                  MacFrameLayout = FLAYOUT_DIRECT;
206 + #endif
207 +        switch (depth) {
208 +                case 1:
209 +                        VideoMonitor.mode = VMODE_1BIT;
210 +                        break;
211 +                case 8:
212 +                        VideoMonitor.mode = VMODE_8BIT;
213 +                        break;
214 +                case 15:
215 +                        VideoMonitor.mode = VMODE_16BIT;
216 +                        break;
217 +                case 16:
218 +                        VideoMonitor.mode = VMODE_16BIT;
219 +                        break;
220 +                case 24:
221 +                case 32:
222 +                        VideoMonitor.mode = VMODE_32BIT;
223 +                        break;
224 +        }
225 +        VideoMonitor.x = width;
226 +        VideoMonitor.y = height;
227 +        VideoMonitor.bytes_per_row = bytes_per_row;
228   }
229  
230   // Trap SHM errors
# Line 190 | Line 243 | static int error_handler(Display *d, XEr
243   // Init window mode
244   static bool init_window(int width, int height)
245   {
246 +        int aligned_width = (width + 15) & ~15;
247 +        int aligned_height = (height + 15) & ~15;
248 +
249          // Set absolute mouse mode
250          ADBSetRelMouseMode(false);
251  
252          // Read frame skip prefs
253          frame_skip = PrefsFindInt32("frameskip");
198        if (frame_skip == 0)
199                frame_skip = 1;
254  
255          // Create window
256          XSetWindowAttributes wattr;
257          wattr.event_mask = eventmask = win_eventmask;
258          wattr.background_pixel = black_pixel;
259          wattr.border_pixel = black_pixel;
260 <        wattr.backing_store = Always;
260 >        wattr.backing_store = NotUseful;
261 >        wattr.save_under = false;
262          wattr.backing_planes = xdepth;
263  
264          XSync(x_display, false);
# Line 232 | Line 287 | static bool init_window(int width, int h
287                  XSetWMNormalHints(x_display, the_win, hints);
288                  XFree((char *)hints);
289          }
290 <
290 >        
291          // Try to create and attach SHM image
292          have_shm = false;
293 <        if (depth != 1 && XShmQueryExtension(x_display)) {
293 >        if (depth != 1 && local_X11 && XShmQueryExtension(x_display)) {
294  
295                  // Create SHM image ("height + 2" for safety)
296                  img = XShmCreateImage(x_display, vis, depth, depth == 1 ? XYBitmap : ZPixmap, 0, &shminfo, width, height);
297 <                shminfo.shmid = shmget(IPC_PRIVATE, (height + 2) * img->bytes_per_line, IPC_CREAT | 0777);
298 <                the_buffer = (uint8 *)shmat(shminfo.shmid, 0, 0);
299 <                shminfo.shmaddr = img->data = (char *)the_buffer;
297 >                shminfo.shmid = shmget(IPC_PRIVATE, (aligned_height + 2) * img->bytes_per_line, IPC_CREAT | 0777);
298 >                the_buffer_copy = (uint8 *)shmat(shminfo.shmid, 0, 0);
299 >                shminfo.shmaddr = img->data = (char *)the_buffer_copy;
300                  shminfo.readOnly = False;
301  
302                  // Try to attach SHM image, catching errors
# Line 259 | Line 314 | static bool init_window(int width, int h
314                          shmctl(shminfo.shmid, IPC_RMID, 0);
315                  }
316          }
317 <
317 >        
318          // Create normal X image if SHM doesn't work ("height + 2" for safety)
319          if (!have_shm) {
320 <                int bytes_per_row = width;
320 >                int bytes_per_row = aligned_width;
321                  switch (depth) {
322                          case 1:
323                                  bytes_per_row /= 8;
# Line 276 | Line 331 | static bool init_window(int width, int h
331                                  bytes_per_row *= 4;
332                                  break;
333                  }
334 <                the_buffer = (uint8 *)malloc((height + 2) * bytes_per_row);
335 <                img = XCreateImage(x_display, vis, depth, depth == 1 ? XYBitmap : ZPixmap, 0, (char *)the_buffer, width, height, 32, bytes_per_row);
334 >                the_buffer_copy = (uint8 *)malloc((aligned_height + 2) * bytes_per_row);
335 >                img = XCreateImage(x_display, vis, depth, depth == 1 ? XYBitmap : ZPixmap, 0, (char *)the_buffer_copy, aligned_width, aligned_height, 32, bytes_per_row);
336          }
337  
338          // 1-Bit mode is big-endian
# Line 286 | Line 341 | static bool init_window(int width, int h
341                  img->bitmap_bit_order = MSBFirst;
342          }
343  
344 <        // Allocate memory for frame buffer copy
345 <        the_buffer_copy = (uint8 *)malloc((height + 2) * img->bytes_per_line);
344 >        // Allocate memory for frame buffer
345 >        the_buffer = (uint8 *)malloc((aligned_height + 2) * img->bytes_per_line);
346  
347          // Create GC
348          the_gc = XCreateGC(x_display, the_win, 0, 0);
349          XSetState(x_display, the_gc, black_pixel, white_pixel, GXcopy, AllPlanes);
350  
351 <        // Create cursor
352 <        cursor_image = XCreateImage(x_display, vis, 1, XYPixmap, 0, (char *)the_cursor, 16, 16, 16, 2);
353 <        cursor_image->byte_order = MSBFirst;
354 <        cursor_image->bitmap_bit_order = MSBFirst;
355 <        cursor_mask_image = XCreateImage(x_display, vis, 1, XYPixmap, 0, (char *)the_cursor+32, 16, 16, 16, 2);
356 <        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);
351 >        // Create no_cursor
352 >        mac_cursor = XCreatePixmapCursor(x_display,
353 >           XCreatePixmap(x_display, the_win, 1, 1, 1),
354 >           XCreatePixmap(x_display, the_win, 1, 1, 1),
355 >           &black, &white, 0, 0);
356 >        XDefineCursor(x_display, the_win, mac_cursor);
357  
358          // Set VideoMonitor
359   #ifdef WORDS_BIGENDIAN
# Line 312 | Line 361 | static bool init_window(int width, int h
361   #else
362          set_video_monitor(width, height, img->bytes_per_line, img->bitmap_bit_order == LSBFirst);
363   #endif
364 +        
365 + #if REAL_ADDRESSING
366 +        VideoMonitor.mac_frame_base = (uint32)the_buffer;
367 + #else
368 +        VideoMonitor.mac_frame_base = MacFrameBaseMac;
369 + #endif
370 +        return true;
371 + }
372 +
373 + // Init fbdev DGA display
374 + static bool init_fbdev_dga(char *in_fb_name)
375 + {
376 + #ifdef ENABLE_FBDEV_DGA
377 +        // Find the maximum depth available
378 +        int ndepths, max_depth(0);
379 +        int *depths = XListDepths(x_display, screen, &ndepths);
380 +        if (depths == NULL) {
381 +                printf("FATAL: Could not determine the maximal depth available\n");
382 +                return false;
383 +        } else {
384 +                while (ndepths-- > 0) {
385 +                        if (depths[ndepths] > max_depth)
386 +                                max_depth = depths[ndepths];
387 +                }
388 +        }
389 +        
390 +        // Get fbdevices file path from preferences
391 +        const char *fbd_path = PrefsFindString("fbdevicefile");
392 +        
393 +        // Open fbdevices file
394 +        FILE *fp = fopen(fbd_path ? fbd_path : FBDEVICES_FILE_NAME, "r");
395 +        if (fp == NULL) {
396 +                char str[256];
397 +                sprintf(str, GetString(STR_NO_FBDEVICE_FILE_ERR), fbd_path ? fbd_path : FBDEVICES_FILE_NAME, strerror(errno));
398 +                ErrorAlert(str);
399 +                return false;
400 +        }
401 +        
402 +        int fb_depth;           // supported depth
403 +        uint32 fb_offset;       // offset used for mmap(2)
404 +        char fb_name[20];
405 +        char line[256];
406 +        bool device_found = false;
407 +        while (fgets(line, 255, fp)) {
408 +                // Read line
409 +                int len = strlen(line);
410 +                if (len == 0)
411 +                        continue;
412 +                line[len - 1] = '\0';
413 +                
414 +                // Comments begin with "#" or ";"
415 +                if ((line[0] == '#') || (line[0] == ';') || (line[0] == '\0'))
416 +                        continue;
417 +                
418 +                if ((sscanf(line, "%19s %d %x", &fb_name, &fb_depth, &fb_offset) == 3)
419 +                && (strcmp(fb_name, in_fb_name) == 0) && (fb_depth == max_depth)) {
420 +                        device_found = true;
421 +                        break;
422 +                }
423 +        }
424 +        
425 +        // fbdevices file completely read
426 +        fclose(fp);
427 +        
428 +        // Frame buffer name not found ? Then, display warning
429 +        if (!device_found) {
430 +                char str[256];
431 +                sprintf(str, GetString(STR_FBDEV_NAME_ERR), in_fb_name, max_depth);
432 +                ErrorAlert(str);
433 +                return false;
434 +        }
435 +        
436 +        int width = DisplayWidth(x_display, screen);
437 +        int height = DisplayHeight(x_display, screen);
438 +        depth = fb_depth; // max_depth
439 +        
440 +        // Set relative mouse mode
441 +        ADBSetRelMouseMode(false);
442 +        
443 +        // Create window
444 +        XSetWindowAttributes wattr;
445 +        wattr.override_redirect = True;
446 +        wattr.backing_store             = NotUseful;
447 +        wattr.background_pixel  = white_pixel;
448 +        wattr.border_pixel              = black_pixel;
449 +        wattr.event_mask                = eventmask = dga_eventmask;
450 +        
451 +        XSync(x_display, false);
452 +        the_win = XCreateWindow(x_display, rootwin,
453 +                0, 0, width, height,
454 +                0, xdepth, InputOutput, vis,
455 +                CWEventMask|CWBackPixel|CWBorderPixel|CWOverrideRedirect|CWBackingStore,
456 +                &wattr);
457 +        XSync(x_display, false);
458 +        XMapRaised(x_display, the_win);
459 +        XSync(x_display, false);
460 +        
461 +        // Grab mouse and keyboard
462 +        XGrabKeyboard(x_display, the_win, True,
463 +                GrabModeAsync, GrabModeAsync, CurrentTime);
464 +        XGrabPointer(x_display, the_win, True,
465 +                PointerMotionMask | ButtonPressMask | ButtonReleaseMask,
466 +                GrabModeAsync, GrabModeAsync, the_win, None, CurrentTime);
467 +        
468 +        // Set colormap
469 +        if (depth == 8) {
470 +                XSetWindowColormap(x_display, the_win, cmap[0]);
471 +                XSetWMColormapWindows(x_display, the_win, &the_win, 1);
472 +        }
473 +        
474 +        // Set VideoMonitor
475 +        int bytes_per_row = width;
476 +        switch (depth) {
477 +                case 1:
478 +                        bytes_per_row = ((width | 7) & ~7) >> 3;
479 +                        break;
480 +                case 15:
481 +                case 16:
482 +                        bytes_per_row *= 2;
483 +                        break;
484 +                case 24:
485 +                case 32:
486 +                        bytes_per_row *= 4;
487 +                        break;
488 +        }
489 +        
490 +        if ((the_buffer = (uint8 *) mmap(NULL, height * bytes_per_row, PROT_READ | PROT_WRITE, MAP_PRIVATE, fbdev_fd, fb_offset)) == MAP_FAILED) {
491 +                if ((the_buffer = (uint8 *) mmap(NULL, height * bytes_per_row, PROT_READ | PROT_WRITE, MAP_SHARED, fbdev_fd, fb_offset)) == MAP_FAILED) {
492 +                        char str[256];
493 +                        sprintf(str, GetString(STR_FBDEV_MMAP_ERR), strerror(errno));
494 +                        ErrorAlert(str);
495 +                        return false;
496 +                }
497 +        }
498 +        
499 +        set_video_monitor(width, height, bytes_per_row, true);
500   #if REAL_ADDRESSING
501          VideoMonitor.mac_frame_base = (uint32)the_buffer;
317        MacFrameLayout = FLAYOUT_DIRECT;
502   #else
503          VideoMonitor.mac_frame_base = MacFrameBaseMac;
504   #endif
505          return true;
506 + #else
507 +        ErrorAlert("Basilisk II has been compiled with fbdev DGA support disabled.");
508 +        return false;
509 + #endif
510   }
511  
512 < // Init DGA display
513 < static bool init_dga(int width, int height)
512 > // Init XF86 DGA display
513 > static bool init_xf86_dga(int width, int height)
514   {
515 < #if ENABLE_DGA
515 > #ifdef ENABLE_XF86_DGA
516          // Set relative mouse mode
517          ADBSetRelMouseMode(true);
518  
519 + #ifdef ENABLE_XF86_VIDMODE
520 +        // Switch to best mode
521 +        if (has_vidmode) {
522 +                int best = 0;
523 +                for (int i=1; i<num_x_video_modes; i++) {
524 +                        if (x_video_modes[i]->hdisplay >= width && x_video_modes[i]->vdisplay >= height &&
525 +                                x_video_modes[i]->hdisplay <= x_video_modes[best]->hdisplay && x_video_modes[i]->vdisplay <= x_video_modes[best]->vdisplay) {
526 +                                best = i;
527 +                        }
528 +                }
529 +                XF86VidModeSwitchToMode(x_display, screen, x_video_modes[best]);
530 +                XF86VidModeSetViewPort(x_display, screen, 0, 0);
531 +        }
532 + #endif
533 +
534          // Create window
535          XSetWindowAttributes wattr;
536          wattr.event_mask = eventmask = dga_eventmask;
# Line 385 | Line 588 | static bool init_dga(int width, int heig
588   #endif
589          return true;
590   #else
591 <        ErrorAlert("Basilisk II has been compiled with DGA support disabled.");
591 >        ErrorAlert("Basilisk II has been compiled with XF86 DGA support disabled.");
592          return false;
593   #endif
594   }
# Line 457 | Line 660 | static void keycode_init(void)
660  
661   bool VideoInit(bool classic)
662   {
663 +        // Check if X server runs on local machine
664 +        local_X11 = (strncmp(XDisplayName(x_display_name), ":", 1) == 0)
665 +                 || (strncmp(XDisplayName(x_display_name), "unix:", 5) == 0);
666 +    
667          // Init keycode translation
668          keycode_init();
669  
670 +        // Read prefs
671 +        mouse_wheel_mode = PrefsFindInt16("mousewheelmode");
672 +        mouse_wheel_lines = PrefsFindInt16("mousewheellines");
673 +
674          // Find screen and root window
675          screen = XDefaultScreen(x_display);
676          rootwin = XRootWindow(x_display, screen);
677 <
677 >        
678          // Get screen depth
679          xdepth = DefaultDepth(x_display, screen);
680 +        
681 + #ifdef ENABLE_FBDEV_DGA
682 +        // Frame buffer name
683 +        char fb_name[20];
684 +        
685 +        // Could do fbdev dga ?
686 +        if ((fbdev_fd = open(FBDEVICE_FILE_NAME, O_RDWR)) != -1)
687 +                has_dga = true;
688 +        else
689 +                has_dga = false;
690 + #endif
691  
692 < #if ENABLE_DGA
692 > #ifdef ENABLE_XF86_DGA
693          // DGA available?
694 <        int dga_flags = 0;
695 <        XF86DGAQueryDirectVideo(x_display, screen, &dga_flags);
696 <        has_dga = dga_flags & XF86DGADirectPresent;
694 >        int dga_event_base, dga_error_base;
695 >        if (local_X11 && XF86DGAQueryExtension(x_display, &dga_event_base, &dga_error_base)) {
696 >                int dga_flags = 0;
697 >                XF86DGAQueryDirectVideo(x_display, screen, &dga_flags);
698 >                has_dga = dga_flags & XF86DGADirectPresent;
699 >        } else
700 >                has_dga = false;
701   #endif
702  
703 + #ifdef ENABLE_XF86_VIDMODE
704 +        // VidMode available?
705 +        int vm_event_base, vm_error_base;
706 +        has_vidmode = XF86VidModeQueryExtension(x_display, &vm_event_base, &vm_error_base);
707 +        if (has_vidmode)
708 +                XF86VidModeGetAllModeLines(x_display, screen, &num_x_video_modes, &x_video_modes);
709 + #endif
710 +        
711          // Find black and white colors
712          XParseColor(x_display, DefaultColormap(x_display, screen), "rgb:00/00/00", &black);
713          XAllocColor(x_display, DefaultColormap(x_display, screen), &black);
# Line 539 | Line 773 | bool VideoInit(bool classic)
773          if (mode_str) {
774                  if (sscanf(mode_str, "win/%d/%d", &width, &height) == 2)
775                          display_type = DISPLAY_WINDOW;
776 <                else if (has_dga && strcmp(mode_str, "dga") == 0) {
776 > #ifdef ENABLE_FBDEV_DGA
777 >                else if (has_dga && sscanf(mode_str, "dga/%19s", fb_name) == 1) {
778 > #else
779 >                else if (has_dga && sscanf(mode_str, "dga/%d/%d", &width, &height) == 2) {
780 > #endif
781                          display_type = DISPLAY_DGA;
782 +                        if (width > DisplayWidth(x_display, screen))
783 +                                width = DisplayWidth(x_display, screen);
784 +                        if (height > DisplayHeight(x_display, screen))
785 +                                height = DisplayHeight(x_display, screen);
786 +                }
787 +                if (width <= 0)
788                          width = DisplayWidth(x_display, screen);
789 +                if (height <= 0)
790                          height = DisplayHeight(x_display, screen);
546                }
791          }
792  
793          // Initialize according to display type
# Line 553 | Line 797 | bool VideoInit(bool classic)
797                                  return false;
798                          break;
799                  case DISPLAY_DGA:
800 <                        if (!init_dga(width, height))
800 > #ifdef ENABLE_FBDEV_DGA
801 >                        if (!init_fbdev_dga(fb_name))
802 > #else
803 >                        if (!init_xf86_dga(width, height))
804 > #endif
805                                  return false;
806                          break;
807          }
808  
809 + #ifdef HAVE_PTHREADS
810          // Lock down frame buffer
811          pthread_mutex_lock(&frame_buffer_lock);
812 + #endif
813  
814   #if !REAL_ADDRESSING
815          // Set variables for UAE memory mapping
# Line 571 | Line 821 | bool VideoInit(bool classic)
821                  MacFrameLayout = FLAYOUT_NONE;
822   #endif
823  
574        // Start redraw/input thread
824          XSync(x_display, false);
825 +
826 + #ifdef HAVE_PTHREADS
827 +        // Start redraw/input thread
828          redraw_thread_active = (pthread_create(&redraw_thread, NULL, redraw_func, NULL) == 0);
829 <        if (!redraw_thread_active)
829 >        if (!redraw_thread_active) {
830                  printf("FATAL: cannot create redraw thread\n");
831 <        return redraw_thread_active;
831 >                return false;
832 >        }
833 > #endif
834 >        return true;
835   }
836  
837  
# Line 586 | Line 841 | bool VideoInit(bool classic)
841  
842   void VideoExit(void)
843   {
844 + #ifdef HAVE_PTHREADS
845          // Stop redraw thread
846          if (redraw_thread_active) {
847                  redraw_thread_cancel = true;
# Line 595 | Line 851 | void VideoExit(void)
851                  pthread_join(redraw_thread, NULL);
852                  redraw_thread_active = false;
853          }
854 + #endif
855  
856 + #ifdef HAVE_PTHREADS
857          // Unlock frame buffer
858          pthread_mutex_unlock(&frame_buffer_lock);
859 + #endif
860  
861          // Close window and server connection
862          if (x_display != NULL) {
863                  XSync(x_display, false);
864  
865 < #if ENABLE_DGA
865 > #ifdef ENABLE_XF86_DGA
866                  if (display_type == DISPLAY_DGA) {
867                          XF86DGADirectVideo(x_display, screen, 0);
868                          XUngrabPointer(x_display, CurrentTime);
# Line 611 | Line 870 | void VideoExit(void)
870                  }
871   #endif
872  
873 <                if (the_buffer_copy) {
874 <                        free(the_buffer_copy);
875 <                        the_buffer_copy = NULL;
873 > #ifdef ENABLE_XF86_VIDMODE
874 >                if (has_vidmode && display_type == DISPLAY_DGA)
875 >                        XF86VidModeSwitchToMode(x_display, screen, x_video_modes[0]);
876 > #endif
877 >
878 > #ifdef ENABLE_FBDEV_DGA
879 >                if (display_type == DISPLAY_DGA) {
880 >                        XUngrabPointer(x_display, CurrentTime);
881 >                        XUngrabKeyboard(x_display, CurrentTime);
882 >                        close(fbdev_fd);
883                  }
884 + #endif
885  
886                  XFlush(x_display);
887                  XSync(x_display, false);
# Line 622 | Line 889 | void VideoExit(void)
889                          XFreeColormap(x_display, cmap[0]);
890                          XFreeColormap(x_display, cmap[1]);
891                  }
892 +
893 +                if (the_buffer) {
894 +                        free(the_buffer);
895 +                        the_buffer = NULL;
896 +                }
897 +
898 +                if (!have_shm && the_buffer_copy) {
899 +                        free(the_buffer_copy);
900 +                        the_buffer_copy = NULL;
901 +                }
902          }
903   }
904  
# Line 648 | Line 925 | void VideoInterrupt(void)
925          if (emerg_quit)
926                  QuitEmulator();
927  
928 + #ifdef HAVE_PTHREADS
929          // Temporarily give up frame buffer lock (this is the point where
930          // we are suspended when the user presses Ctrl-Tab)
931          pthread_mutex_unlock(&frame_buffer_lock);
932          pthread_mutex_lock(&frame_buffer_lock);
933 + #endif
934   }
935  
936  
# Line 661 | Line 940 | void VideoInterrupt(void)
940  
941   void video_set_palette(uint8 *pal)
942   {
943 + #ifdef HAVE_PTHREDS
944          pthread_mutex_lock(&palette_lock);
945 + #endif
946  
947          // Convert colors to XColor array
948          for (int i=0; i<256; i++) {
# Line 675 | Line 956 | void video_set_palette(uint8 *pal)
956          // Tell redraw thread to change palette
957          palette_changed = true;
958  
959 + #ifdef HAVE_PTHREADS
960          pthread_mutex_unlock(&palette_lock);
961 + #endif
962   }
963  
964  
# Line 683 | Line 966 | void video_set_palette(uint8 *pal)
966   *  Suspend/resume emulator
967   */
968  
969 < #if ENABLE_DGA
969 > #if defined(ENABLE_XF86_DGA) || defined(ENABLE_FBDEV_DGA)
970   static void suspend_emul(void)
971   {
972          if (display_type == DISPLAY_DGA) {
# Line 691 | Line 974 | static void suspend_emul(void)
974                  ADBKeyUp(0x36);
975                  ctrl_down = false;
976  
977 + #ifdef HAVE_PTHREADS
978                  // Lock frame buffer (this will stop the MacOS thread)
979                  pthread_mutex_lock(&frame_buffer_lock);
980 + #endif
981  
982                  // Save frame buffer
983                  fb_save = malloc(VideoMonitor.y * VideoMonitor.bytes_per_row);
# Line 700 | Line 985 | static void suspend_emul(void)
985                          memcpy(fb_save, the_buffer, VideoMonitor.y * VideoMonitor.bytes_per_row);
986  
987                  // Close full screen display
988 + #ifdef ENABLE_XF86_DGA
989                  XF86DGADirectVideo(x_display, screen, 0);
990 + #endif
991                  XUngrabPointer(x_display, CurrentTime);
992                  XUngrabKeyboard(x_display, CurrentTime);
993                  XUnmapWindow(x_display, the_win);
# Line 714 | Line 1001 | static void suspend_emul(void)
1001                  wattr.backing_store = Always;
1002                  wattr.backing_planes = xdepth;
1003                  wattr.colormap = DefaultColormap(x_display, screen);
1004 +                
1005                  XSync(x_display, false);
1006                  suspend_win = XCreateWindow(x_display, rootwin, 0, 0, 512, 1, 0, xdepth,
1007                          InputOutput, vis, CWEventMask | CWBackPixel | CWBorderPixel |
# Line 738 | Line 1026 | static void resume_emul(void)
1026          XSync(x_display, false);
1027          XGrabKeyboard(x_display, rootwin, 1, GrabModeAsync, GrabModeAsync, CurrentTime);
1028          XGrabPointer(x_display, rootwin, 1, PointerMotionMask | ButtonPressMask | ButtonReleaseMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
1029 + #ifdef ENABLE_XF86_DGA
1030          XF86DGADirectVideo(x_display, screen, XF86DGADirectGraphics | XF86DGADirectKeyb | XF86DGADirectMouse);
1031          XF86DGASetViewPort(x_display, screen, 0, 0);
1032 + #endif
1033          XSync(x_display, false);
1034  
1035          // Restore frame buffer
# Line 748 | Line 1038 | static void resume_emul(void)
1038                  free(fb_save);
1039                  fb_save = NULL;
1040          }
1041 +        
1042 + #ifdef ENABLE_XF86_DGA
1043          if (depth == 8)
1044                  XF86DGAInstallColormap(x_display, screen, cmap[current_dga_cmap]);
1045 + #endif
1046  
1047 + #ifdef HAVE_PTHREADS
1048          // Unlock frame buffer (and continue MacOS thread)
1049          pthread_mutex_unlock(&frame_buffer_lock);
1050          emul_suspended = false;
1051 + #endif
1052   }
1053   #endif
1054  
# Line 815 | Line 1110 | static int kc_decode(KeySym ks)
1110                  case XK_period: case XK_greater: return 0x2f;
1111                  case XK_slash: case XK_question: return 0x2c;
1112  
1113 < #if ENABLE_DGA
1113 > #if defined(ENABLE_XF86_DGA) || defined(ENABLE_FBDEV_DGA)
1114                  case XK_Tab: if (ctrl_down) {suspend_emul(); return -1;} else return 0x30;
1115   #else
1116                  case XK_Tab: return 0x30;
# Line 941 | Line 1236 | static void handle_events(void)
1236                                  unsigned int button = ((XButtonEvent *)&event)->button;
1237                                  if (button < 4)
1238                                          ADBMouseDown(button - 1);
1239 +                                else if (button < 6) {  // Wheel mouse
1240 +                                        if (mouse_wheel_mode == 0) {
1241 +                                                int key = (button == 5) ? 0x79 : 0x74;  // Page up/down
1242 +                                                ADBKeyDown(key);
1243 +                                                ADBKeyUp(key);
1244 +                                        } else {
1245 +                                                int key = (button == 5) ? 0x3d : 0x3e;  // Cursor up/down
1246 +                                                for(int i=0; i<mouse_wheel_lines; i++) {
1247 +                                                        ADBKeyDown(key);
1248 +                                                        ADBKeyUp(key);
1249 +                                                }
1250 +                                        }
1251 +                                }
1252                                  break;
1253                          }
1254                          case ButtonRelease: {
# Line 968 | Line 1276 | static void handle_events(void)
1276                                          code = event2keycode((XKeyEvent *)&event);
1277                                  if (code != -1) {
1278                                          if (!emul_suspended) {
1279 <                                                ADBKeyDown(code);
1279 >                                                if (code == 0x39) {     // Caps Lock pressed
1280 >                                                        if (caps_on) {
1281 >                                                                ADBKeyUp(code);
1282 >                                                                caps_on = false;
1283 >                                                        } else {
1284 >                                                                ADBKeyDown(code);
1285 >                                                                caps_on = true;
1286 >                                                        }
1287 >                                                } else
1288 >                                                        ADBKeyDown(code);
1289                                                  if (code == 0x36)
1290                                                          ctrl_down = true;
1291                                          } else {
1292 < #if ENABLE_DGA
1292 > #if defined(ENABLE_XF86_DGA) || defined(ENABLE_FBDEV_DGA)
1293                                                  if (code == 0x31)
1294                                                          resume_emul();  // Space wakes us up
1295   #endif
# Line 987 | Line 1304 | static void handle_events(void)
1304                                          code = keycode_table[((XKeyEvent *)&event)->keycode & 0xff];
1305                                  } else
1306                                          code = event2keycode((XKeyEvent *)&event);
1307 <                                if (code != -1) {
1307 >                                if (code != -1 && code != 0x39) {       // Don't propagate Caps Lock releases
1308                                          ADBKeyUp(code);
1309                                          if (code == 0x36)
1310                                                  ctrl_down = false;
# Line 997 | Line 1314 | static void handle_events(void)
1314  
1315                          // Hidden parts exposed, force complete refresh of window
1316                          case Expose:
1317 <                                if (display_type == DISPLAY_WINDOW)
1318 <                                        memset(the_buffer_copy, 0, VideoMonitor.bytes_per_row * VideoMonitor.y);
1317 >                                if (display_type == DISPLAY_WINDOW) {
1318 >                                        if (frame_skip == 0) {  // Dynamic refresh
1319 >                                                int x1, y1;
1320 >                                                for (y1=0; y1<16; y1++)
1321 >                                                for (x1=0; x1<16; x1++)
1322 >                                                        updt_box[x1][y1] = true;
1323 >                                                nr_boxes = 16 * 16;
1324 >                                        } else
1325 >                                                memset(the_buffer_copy, 0, VideoMonitor.bytes_per_row * VideoMonitor.y);
1326 >                                }
1327                                  break;
1328                  }
1329          }
# Line 1009 | Line 1334 | static void handle_events(void)
1334   *  Window display update
1335   */
1336  
1337 < static void update_display(void)
1337 > // Dynamic display update (variable frame rate for each box)
1338 > static void update_display_dynamic(int ticker)
1339   {
1340 <        // In classic mode, copy the frame buffer from Mac RAM
1341 <        if (classic_mode)
1342 <                memcpy(the_buffer, Mac2HostAddr(0x3fa700), VideoMonitor.bytes_per_row * VideoMonitor.y);
1340 >        int y1, y2, y2s, y2a, i, x1, xm, xmo, ymo, yo, yi, yil, xic, xicl, xi;
1341 >        int xil = 0;
1342 >        int rxm = 0, rxmo = 0;
1343 >        int bytes_per_row = VideoMonitor.bytes_per_row;
1344 >        int bytes_per_pixel = VideoMonitor.bytes_per_row / VideoMonitor.x;
1345 >        int rx = VideoMonitor.bytes_per_row / 16;
1346 >        int ry = VideoMonitor.y / 16;
1347 >        int max_box;
1348 >
1349 >        y2s = sm_uptd[ticker % 8];
1350 >        y2a = 8;
1351 >        for (i = 0; i < 6; i++)
1352 >                if (ticker % (2 << i))
1353 >                        break;
1354 >        max_box = sm_no_boxes[i];
1355  
1356 +        if (y2a) {
1357 +                for (y1=0; y1<16; y1++) {
1358 +                        for (y2=y2s; y2 < ry; y2 += y2a) {
1359 +                                i = ((y1 * ry) + y2) * bytes_per_row;
1360 +                                for (x1=0; x1<16; x1++, i += rx) {
1361 +                                        if (updt_box[x1][y1] == false) {
1362 +                                                if (memcmp(&the_buffer_copy[i], &the_buffer[i], rx)) {
1363 +                                                        updt_box[x1][y1] = true;
1364 +                                                        nr_boxes++;
1365 +                                                }
1366 +                                        }
1367 +                                }
1368 +                        }
1369 +                }
1370 +        }
1371 +
1372 +        if ((nr_boxes <= max_box) && (nr_boxes)) {
1373 +                for (y1=0; y1<16; y1++) {
1374 +                        for (x1=0; x1<16; x1++) {
1375 +                                if (updt_box[x1][y1] == true) {
1376 +                                        if (rxm == 0)
1377 +                                                xm = x1;
1378 +                                        rxm += rx;
1379 +                                        updt_box[x1][y1] = false;
1380 +                                }
1381 +                                if (((updt_box[x1+1][y1] == false) || (x1 == 15)) && (rxm)) {
1382 +                                        if ((rxmo != rxm) || (xmo != xm) || (yo != y1 - 1)) {
1383 +                                                if (rxmo) {
1384 +                                                        xi = xmo * rx;
1385 +                                                        yi = ymo * ry;
1386 +                                                        xil = rxmo;
1387 +                                                        yil = (yo - ymo +1) * ry;
1388 +                                                }
1389 +                                                rxmo = rxm;
1390 +                                                xmo = xm;
1391 +                                                ymo = y1;
1392 +                                        }
1393 +                                        rxm = 0;
1394 +                                        yo = y1;
1395 +                                }      
1396 +                                if (xil) {
1397 +                                        i = (yi * bytes_per_row) + xi;
1398 +                                        for (y2=0; y2 < yil; y2++, i += bytes_per_row)
1399 +                                                memcpy(&the_buffer_copy[i], &the_buffer[i], xil);
1400 +                                        if (depth == 1) {
1401 +                                                if (have_shm)
1402 +                                                        XShmPutImage(x_display, the_win, the_gc, img, xi * 8, yi, xi * 8, yi, xil * 8, yil, 0);
1403 +                                                else
1404 +                                                        XPutImage(x_display, the_win, the_gc, img, xi * 8, yi, xi * 8, yi, xil * 8, yil);
1405 +                                        } else {
1406 +                                                if (have_shm)
1407 +                                                        XShmPutImage(x_display, the_win, the_gc, img, xi / bytes_per_pixel, yi, xi / bytes_per_pixel, yi, xil / bytes_per_pixel, yil, 0);
1408 +                                                else
1409 +                                                        XPutImage(x_display, the_win, the_gc, img, xi / bytes_per_pixel, yi, xi / bytes_per_pixel, yi, xil / bytes_per_pixel, yil);
1410 +                                        }
1411 +                                        xil = 0;
1412 +                                }
1413 +                                if ((x1 == 15) && (y1 == 15) && (rxmo)) {
1414 +                                        x1--;
1415 +                                        xi = xmo * rx;
1416 +                                        yi = ymo * ry;
1417 +                                        xil = rxmo;
1418 +                                        yil = (yo - ymo +1) * ry;
1419 +                                        rxmo = 0;
1420 +                                }
1421 +                        }
1422 +                }
1423 +                nr_boxes = 0;
1424 +        }
1425 + }
1426 +
1427 + // Static display update (fixed frame rate, but incremental)
1428 + static void update_display_static(void)
1429 + {
1430          // Incremental update code
1431          int wide = 0, high = 0, x1, x2, y1, y2, i, j;
1432          int bytes_per_row = VideoMonitor.bytes_per_row;
# Line 1075 | Line 1487 | static void update_display(void)
1487                          if (high && wide) {
1488                                  for (j=y1; j<=y2; j++) {
1489                                          i = j * bytes_per_row + (x1 >> 3);
1490 <                                        memcpy(&the_buffer_copy[i], &the_buffer[i], wide >> 3);
1490 >                                        memcpy(the_buffer_copy + i, the_buffer + i, wide >> 3);
1491                                  }
1492                          }
1493  
# Line 1114 | Line 1526 | static void update_display(void)
1526                          if (high && wide) {
1527                                  for (j=y1; j<=y2; j++) {
1528                                          i = j * bytes_per_row + x1 * bytes_per_pixel;
1529 <                                        memcpy(&the_buffer_copy[i], &the_buffer[i], bytes_per_pixel * wide);
1529 >                                        memcpy(the_buffer_copy + i, the_buffer + i, bytes_per_pixel * wide);
1530                                  }
1531                          }
1532                  }
# Line 1127 | Line 1539 | static void update_display(void)
1539                  else
1540                          XPutImage(x_display, the_win, the_gc, img, x1, y1, x1, y1, wide, high);
1541          }
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        }
1542   }
1543  
1544  
# Line 1150 | Line 1546 | static void update_display(void)
1546   *  Thread for screen refresh, input handling etc.
1547   */
1548  
1549 < static void *redraw_func(void *arg)
1549 > void VideoRefresh(void)
1550   {
1551 <        int tick_counter = 0;
1552 <
1553 <        while (!redraw_thread_cancel) {
1554 <
1555 <                // Wait
1556 < #ifdef HAVE_NANOSLEEP
1557 <                struct timespec req = {0, 16666667};
1162 <                nanosleep(&req, NULL);
1163 < #else
1164 <                usleep(16667);
1551 > #if defined(ENABLE_XF86_DGA) || defined(ENABLE_FBDEV_DGA)
1552 >        // Quit DGA mode if requested
1553 >        if (quit_full_screen) {
1554 >                quit_full_screen = false;
1555 >                if (display_type == DISPLAY_DGA) {
1556 > #ifdef ENABLE_XF86_DGA
1557 >                        XF86DGADirectVideo(x_display, screen, 0);
1558   #endif
1559 <
1560 < #if ENABLE_DGA
1561 <                // Quit DGA mode if requested
1562 <                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 <                        }
1559 >                        XUngrabPointer(x_display, CurrentTime);
1560 >                        XUngrabKeyboard(x_display, CurrentTime);
1561 >                        XUnmapWindow(x_display, the_win);
1562 >                        XSync(x_display, false);
1563                  }
1564 +        }
1565   #endif
1566  
1567 <                // Handle X events
1568 <                handle_events();
1567 >        // Handle X events
1568 >        handle_events();
1569  
1570 <                // Handle palette changes
1571 <                pthread_mutex_lock(&palette_lock);
1572 <                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 <                                }
1570 >        // Handle palette changes
1571 > #ifdef HAVE_PTHREADS
1572 >        pthread_mutex_lock(&palette_lock);
1573   #endif
1574 +        if (palette_changed) {
1575 +                palette_changed = false;
1576 +                if (depth == 8) {
1577 +                        XStoreColors(x_display, cmap[0], palette, 256);
1578 +                        XStoreColors(x_display, cmap[1], palette, 256);
1579 +                                
1580 + #ifdef ENABLE_XF86_DGA
1581 +                        if (display_type == DISPLAY_DGA) {
1582 +                                current_dga_cmap ^= 1;
1583 +                                XF86DGAInstallColormap(x_display, screen, cmap[current_dga_cmap]);
1584                          }
1585 + #endif
1586                  }
1587 <                pthread_mutex_unlock(&palette_lock);
1587 >        }
1588 > #ifdef HAVE_PTHREADS
1589 >        pthread_mutex_unlock(&palette_lock);
1590 > #endif
1591  
1592 <                // In window mode, update display and mouse pointer
1593 <                if (display_type == DISPLAY_WINDOW) {
1594 <                        tick_counter++;
1595 <                        if (tick_counter >= frame_skip) {
1596 <                                tick_counter = 0;
1597 <                                update_display();
1598 <                        }
1592 >        // In window mode, update display
1593 >        static int tick_counter = 0;
1594 >        if (display_type == DISPLAY_WINDOW) {
1595 >                tick_counter++;
1596 >                if (frame_skip == 0)
1597 >                        update_display_dynamic(tick_counter);
1598 >                else if (tick_counter >= frame_skip) {
1599 >                        tick_counter = 0;
1600 >                        update_display_static();
1601                  }
1602          }
1603 + }
1604 +
1605 + #ifdef HAVE_PTHREADS
1606 + static void *redraw_func(void *arg)
1607 + {
1608 +        while (!redraw_thread_cancel) {
1609 + #ifdef HAVE_NANOSLEEP
1610 +                struct timespec req = {0, 16666667};
1611 +                nanosleep(&req, NULL);
1612 + #else
1613 +                usleep(16667);
1614 + #endif
1615 +                VideoRefresh();
1616 +        }
1617          return NULL;
1618   }
1619 + #endif

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines