ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/video_x.cpp
Revision: 1.43
Committed: 2001-06-28T22:06:18Z (23 years, 5 months ago) by gbeauche
Branch: MAIN
Changes since 1.42: +0 -15 lines
Log Message:
zero_fd is not longer used since vm_alloc.cpp should handle that correctly.
However, vm_init() and vm_exit() are called in main_unix.cpp to ensure proper
initialization of the internal zero_fd descriptor, if needed. i.e. no
anonymous mapping for mmap()-based memory allocation.

File Contents

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