ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/video_x.cpp
Revision: 1.42
Committed: 2001-06-28T21:20:00Z (23 years ago) by cebix
Branch: MAIN
Changes since 1.41: +204 -413 lines
Log Message:
video_x.cpp supports resolution switching in windowed mode: the available
resolutions are 512x384, 640x480, 800x600, 1024x768 and 1280x1024 (the prefs
editor has to be updated to reflect this). The resolution selected in the
prefs editor is used as the default, but it can be changed in the Monitors
control panel. So far only tested with direct addressing.

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 // Open /dev/zero
807 zero_fd = open("/dev/zero", O_RDWR);
808 if (zero_fd < 0) {
809 char str[256];
810 sprintf(str, GetString(STR_NO_DEV_ZERO_ERR), strerror(errno));
811 ErrorAlert(str);
812 return false;
813 }
814
815 // Zero the mainBuffer structure
816 mainBuffer.dirtyPages = 0;
817 mainBuffer.pageInfo = 0;
818 #endif
819
820 // Check if X server runs on local machine
821 local_X11 = (strncmp(XDisplayName(x_display_name), ":", 1) == 0)
822 || (strncmp(XDisplayName(x_display_name), "unix:", 5) == 0);
823
824 // Init keycode translation
825 keycode_init();
826
827 // Read prefs
828 mouse_wheel_mode = PrefsFindInt32("mousewheelmode");
829 mouse_wheel_lines = PrefsFindInt32("mousewheellines");
830
831 // Find screen and root window
832 screen = XDefaultScreen(x_display);
833 rootwin = XRootWindow(x_display, screen);
834
835 // Get screen depth
836 xdepth = DefaultDepth(x_display, screen);
837
838 #ifdef ENABLE_FBDEV_DGA
839 // Frame buffer name
840 char fb_name[20];
841
842 // Could do fbdev dga ?
843 if ((fbdev_fd = open(FBDEVICE_FILE_NAME, O_RDWR)) != -1)
844 has_dga = true;
845 else
846 has_dga = false;
847 #endif
848
849 #ifdef ENABLE_XF86_DGA
850 // DGA available?
851 int dga_event_base, dga_error_base;
852 if (local_X11 && XF86DGAQueryExtension(x_display, &dga_event_base, &dga_error_base)) {
853 int dga_flags = 0;
854 XF86DGAQueryDirectVideo(x_display, screen, &dga_flags);
855 has_dga = dga_flags & XF86DGADirectPresent;
856 } else
857 has_dga = false;
858 #endif
859
860 #ifdef ENABLE_XF86_VIDMODE
861 // VidMode available?
862 int vm_event_base, vm_error_base;
863 has_vidmode = XF86VidModeQueryExtension(x_display, &vm_event_base, &vm_error_base);
864 if (has_vidmode)
865 XF86VidModeGetAllModeLines(x_display, screen, &num_x_video_modes, &x_video_modes);
866 #endif
867
868 // Find black and white colors
869 XParseColor(x_display, DefaultColormap(x_display, screen), "rgb:00/00/00", &black);
870 XAllocColor(x_display, DefaultColormap(x_display, screen), &black);
871 XParseColor(x_display, DefaultColormap(x_display, screen), "rgb:ff/ff/ff", &white);
872 XAllocColor(x_display, DefaultColormap(x_display, screen), &white);
873 black_pixel = BlackPixel(x_display, screen);
874 white_pixel = WhitePixel(x_display, screen);
875
876 // Get appropriate visual
877 int color_class;
878 switch (xdepth) {
879 case 1:
880 color_class = StaticGray;
881 break;
882 case 8:
883 color_class = PseudoColor;
884 break;
885 case 15:
886 case 16:
887 case 24:
888 case 32:
889 color_class = TrueColor;
890 break;
891 default:
892 ErrorAlert(GetString(STR_UNSUPP_DEPTH_ERR));
893 return false;
894 }
895 if (!XMatchVisualInfo(x_display, screen, xdepth, color_class, &visualInfo)) {
896 ErrorAlert(GetString(STR_NO_XVISUAL_ERR));
897 return false;
898 }
899 if (visualInfo.depth != xdepth) {
900 ErrorAlert(GetString(STR_NO_XVISUAL_ERR));
901 return false;
902 }
903 vis = visualInfo.visual;
904
905 // Get screen mode from preferences
906 const char *mode_str;
907 if (classic_mode)
908 mode_str = "win/512/342";
909 else
910 mode_str = PrefsFindString("screen");
911
912 // Determine display type and default dimensions
913 int default_width = 512, default_height = 384;
914 display_type = DISPLAY_WINDOW;
915 if (mode_str) {
916 if (sscanf(mode_str, "win/%d/%d", &default_width, &default_height) == 2) {
917 display_type = DISPLAY_WINDOW;
918 #ifdef ENABLE_FBDEV_DGA
919 } else if (has_dga && sscanf(mode_str, "dga/%19s", fb_name) == 1) {
920 display_type = DISPLAY_DGA;
921 default_width = -1; default_height = -1;
922 #else
923 } else if (has_dga && sscanf(mode_str, "dga/%d/%d", &default_width, &default_height) == 2) {
924 display_type = DISPLAY_DGA;
925 #endif
926 }
927 }
928 if (default_width <= 0)
929 default_width = DisplayWidth(x_display, screen);
930 else if (default_width > DisplayWidth(x_display, screen))
931 default_width = DisplayWidth(x_display, screen);
932 if (default_height <= 0)
933 default_height = DisplayHeight(x_display, screen);
934 else if (default_height > DisplayHeight(x_display, screen))
935 default_height = DisplayHeight(x_display, screen);
936
937 // Mac screen depth is always 1 bit in Classic mode, but follows X depth otherwise
938 int depth = (classic_mode ? 1 : xdepth);
939 video_depth depth_mode = DepthModeForPixelDepth(depth);
940
941 // Construct list of supported modes
942 if (display_type == DISPLAY_WINDOW) {
943 if (classic)
944 add_mode(512, 342, 0x80, 64, depth_mode);
945 else {
946 add_mode(512, 384, 0x80, TrivialBytesPerRow(512, depth_mode), depth_mode);
947 add_mode(640, 480, 0x81, TrivialBytesPerRow(640, depth_mode), depth_mode);
948 add_mode(800, 600, 0x82, TrivialBytesPerRow(800, depth_mode), depth_mode);
949 add_mode(1024, 768, 0x83, TrivialBytesPerRow(1024, depth_mode), depth_mode);
950 add_mode(1280, 1024, 0x84, TrivialBytesPerRow(1280, depth_mode), depth_mode);
951 }
952 } else
953 add_mode(default_width, default_height, 0x80, TrivialBytesPerRow(default_width, depth_mode), depth_mode);
954
955 // Find requested default mode and open display
956 if (VideoModes.size() == 1)
957 return video_open(VideoModes[0]);
958 else {
959 // Find mode with specified dimensions
960 std::vector<video_mode>::const_iterator i = VideoModes.begin(), end = VideoModes.end();
961 while (i != end) {
962 if (i->x == default_width && i->y == default_height)
963 return video_open(*i);
964 ++i;
965 }
966 return video_open(VideoModes[0]);
967 }
968 }
969
970
971 /*
972 * Deinitialization
973 */
974
975 // Close display
976 static void video_close(void)
977 {
978 #ifdef HAVE_PTHREADS
979 // Stop redraw thread
980 if (redraw_thread_active) {
981 redraw_thread_cancel = true;
982 #ifdef HAVE_PTHREAD_CANCEL
983 pthread_cancel(redraw_thread);
984 #endif
985 pthread_join(redraw_thread, NULL);
986 redraw_thread_active = false;
987 }
988 #endif
989
990 // Unlock frame buffer
991 UNLOCK_FRAME_BUFFER;
992
993 // Close window and server connection
994 if (x_display != NULL) {
995 XSync(x_display, false);
996
997 #ifdef ENABLE_XF86_DGA
998 if (display_type == DISPLAY_DGA) {
999 XF86DGADirectVideo(x_display, screen, 0);
1000 XUngrabPointer(x_display, CurrentTime);
1001 XUngrabKeyboard(x_display, CurrentTime);
1002 }
1003 #endif
1004
1005 #ifdef ENABLE_XF86_VIDMODE
1006 if (has_vidmode && display_type == DISPLAY_DGA)
1007 XF86VidModeSwitchToMode(x_display, screen, x_video_modes[0]);
1008 #endif
1009
1010 #ifdef ENABLE_FBDEV_DGA
1011 if (display_type == DISPLAY_DGA) {
1012 XUngrabPointer(x_display, CurrentTime);
1013 XUngrabKeyboard(x_display, CurrentTime);
1014 close(fbdev_fd);
1015 }
1016 #endif
1017
1018 XFlush(x_display);
1019 XSync(x_display, false);
1020 XUnmapWindow(x_display, the_win);
1021 wait_unmapped(the_win);
1022 XDestroyWindow(x_display, the_win);
1023
1024 if (have_shm) {
1025 XDestroyImage(img);
1026 XShmDetach(x_display, &shminfo);
1027 have_shm = false;
1028 } else {
1029 //!! free img
1030 }
1031 //!! free the_gc
1032
1033 if (cmap_allocated) {
1034 XFreeColormap(x_display, cmap[0]);
1035 XFreeColormap(x_display, cmap[1]);
1036 cmap_allocated = false;
1037 }
1038
1039 if (!use_vosf) {
1040 if (the_buffer) {
1041 free(the_buffer);
1042 the_buffer = NULL;
1043 }
1044
1045 if (!have_shm && the_buffer_copy) {
1046 free(the_buffer_copy);
1047 the_buffer_copy = NULL;
1048 }
1049 }
1050 #ifdef ENABLE_VOSF
1051 else {
1052 //!! uninstall SEGV handler?
1053
1054 if (the_buffer != (uint8 *)VM_MAP_FAILED) {
1055 vm_release(the_buffer, the_buffer_size);
1056 the_buffer = 0;
1057 }
1058
1059 if (the_buffer_copy != (uint8 *)VM_MAP_FAILED) {
1060 vm_release(the_buffer_copy, the_buffer_size);
1061 the_buffer_copy = 0;
1062 }
1063 }
1064 #endif
1065 }
1066
1067 #ifdef ENABLE_VOSF
1068 if (use_vosf) {
1069 // Clear mainBuffer data
1070 if (mainBuffer.pageInfo) {
1071 free(mainBuffer.pageInfo);
1072 mainBuffer.pageInfo = 0;
1073 }
1074
1075 if (mainBuffer.dirtyPages) {
1076 free(mainBuffer.dirtyPages);
1077 mainBuffer.dirtyPages = 0;
1078 }
1079 }
1080 #endif
1081 }
1082
1083 void VideoExit(void)
1084 {
1085 video_close();
1086
1087 #ifdef ENABLE_VOSF
1088 // Close /dev/zero
1089 if (zero_fd > 0)
1090 close(zero_fd);
1091 #endif
1092 }
1093
1094
1095 /*
1096 * Close down full-screen mode (if bringing up error alerts is unsafe while in full-screen mode)
1097 */
1098
1099 void VideoQuitFullScreen(void)
1100 {
1101 D(bug("VideoQuitFullScreen()\n"));
1102 if (display_type == DISPLAY_DGA)
1103 quit_full_screen = true;
1104 }
1105
1106
1107 /*
1108 * Mac VBL interrupt
1109 */
1110
1111 void VideoInterrupt(void)
1112 {
1113 // Emergency quit requested? Then quit
1114 if (emerg_quit)
1115 QuitEmulator();
1116
1117 // Temporarily give up frame buffer lock (this is the point where
1118 // we are suspended when the user presses Ctrl-Tab)
1119 UNLOCK_FRAME_BUFFER;
1120 LOCK_FRAME_BUFFER;
1121 }
1122
1123
1124 /*
1125 * Set palette
1126 */
1127
1128 void video_set_palette(uint8 *pal)
1129 {
1130 LOCK_PALETTE;
1131
1132 // Convert colors to XColor array
1133 for (int i=0; i<256; i++) {
1134 palette[i].pixel = i;
1135 palette[i].red = pal[i*3] * 0x0101;
1136 palette[i].green = pal[i*3+1] * 0x0101;
1137 palette[i].blue = pal[i*3+2] * 0x0101;
1138 palette[i].flags = DoRed | DoGreen | DoBlue;
1139 }
1140
1141 // Tell redraw thread to change palette
1142 palette_changed = true;
1143
1144 UNLOCK_PALETTE;
1145 }
1146
1147
1148 /*
1149 * Switch video mode
1150 */
1151
1152 void video_switch_to_mode(const video_mode &mode)
1153 {
1154 video_close();
1155 video_open(mode);
1156 }
1157
1158
1159 /*
1160 * Suspend/resume emulator
1161 */
1162
1163 #if defined(ENABLE_XF86_DGA) || defined(ENABLE_FBDEV_DGA)
1164 static void suspend_emul(void)
1165 {
1166 if (display_type == DISPLAY_DGA) {
1167 // Release ctrl key
1168 ADBKeyUp(0x36);
1169 ctrl_down = false;
1170
1171 // Lock frame buffer (this will stop the MacOS thread)
1172 LOCK_FRAME_BUFFER;
1173
1174 // Save frame buffer
1175 fb_save = malloc(VideoMonitor.mode.y * VideoMonitor.mode.bytes_per_row);
1176 if (fb_save)
1177 memcpy(fb_save, the_buffer, VideoMonitor.mode.y * VideoMonitor.mode.bytes_per_row);
1178
1179 // Close full screen display
1180 #ifdef ENABLE_XF86_DGA
1181 XF86DGADirectVideo(x_display, screen, 0);
1182 #endif
1183 XUngrabPointer(x_display, CurrentTime);
1184 XUngrabKeyboard(x_display, CurrentTime);
1185 XUnmapWindow(x_display, the_win);
1186 wait_unmapped(the_win);
1187
1188 // Open "suspend" window
1189 XSetWindowAttributes wattr;
1190 wattr.event_mask = KeyPressMask;
1191 wattr.background_pixel = black_pixel;
1192
1193 suspend_win = XCreateWindow(x_display, rootwin, 0, 0, 512, 1, 0, xdepth,
1194 InputOutput, vis, CWEventMask | CWBackPixel, &wattr);
1195 set_window_name(suspend_win, STR_SUSPEND_WINDOW_TITLE);
1196 set_window_focus(suspend_win);
1197 XMapWindow(x_display, suspend_win);
1198 emul_suspended = true;
1199 }
1200 }
1201
1202 static void resume_emul(void)
1203 {
1204 // Close "suspend" window
1205 XDestroyWindow(x_display, suspend_win);
1206 XSync(x_display, false);
1207
1208 // Reopen full screen display
1209 XMapRaised(x_display, the_win);
1210 wait_mapped(the_win);
1211 XWarpPointer(x_display, None, rootwin, 0, 0, 0, 0, 0, 0);
1212 XGrabKeyboard(x_display, rootwin, 1, GrabModeAsync, GrabModeAsync, CurrentTime);
1213 XGrabPointer(x_display, rootwin, 1, PointerMotionMask | ButtonPressMask | ButtonReleaseMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
1214 #ifdef ENABLE_XF86_DGA
1215 XF86DGADirectVideo(x_display, screen, XF86DGADirectGraphics | XF86DGADirectKeyb | XF86DGADirectMouse);
1216 XF86DGASetViewPort(x_display, screen, 0, 0);
1217 #endif
1218 XSync(x_display, false);
1219
1220 // the_buffer already contains the data to restore. i.e. since a temporary
1221 // frame buffer is used when VOSF is actually used, fb_save is therefore
1222 // not necessary.
1223 #ifdef ENABLE_VOSF
1224 if (use_vosf) {
1225 LOCK_VOSF;
1226 PFLAG_SET_ALL;
1227 UNLOCK_VOSF;
1228 memset(the_buffer_copy, 0, VideoMonitor.mode.bytes_per_row * VideoMonitor.mode.y);
1229 }
1230 #endif
1231
1232 // Restore frame buffer
1233 if (fb_save) {
1234 #ifdef ENABLE_VOSF
1235 // Don't copy fb_save to the temporary frame buffer in VOSF mode
1236 if (!use_vosf)
1237 #endif
1238 memcpy(the_buffer, fb_save, VideoMonitor.mode.y * VideoMonitor.mode.bytes_per_row);
1239 free(fb_save);
1240 fb_save = NULL;
1241 }
1242
1243 #ifdef ENABLE_XF86_DGA
1244 if (!IsDirectMode(VideoMonitor.mode))
1245 XF86DGAInstallColormap(x_display, screen, cmap[current_dga_cmap]);
1246 #endif
1247
1248 // Unlock frame buffer (and continue MacOS thread)
1249 UNLOCK_FRAME_BUFFER;
1250 emul_suspended = false;
1251 }
1252 #endif
1253
1254
1255 /*
1256 * Translate key event to Mac keycode
1257 */
1258
1259 static int kc_decode(KeySym ks)
1260 {
1261 switch (ks) {
1262 case XK_A: case XK_a: return 0x00;
1263 case XK_B: case XK_b: return 0x0b;
1264 case XK_C: case XK_c: return 0x08;
1265 case XK_D: case XK_d: return 0x02;
1266 case XK_E: case XK_e: return 0x0e;
1267 case XK_F: case XK_f: return 0x03;
1268 case XK_G: case XK_g: return 0x05;
1269 case XK_H: case XK_h: return 0x04;
1270 case XK_I: case XK_i: return 0x22;
1271 case XK_J: case XK_j: return 0x26;
1272 case XK_K: case XK_k: return 0x28;
1273 case XK_L: case XK_l: return 0x25;
1274 case XK_M: case XK_m: return 0x2e;
1275 case XK_N: case XK_n: return 0x2d;
1276 case XK_O: case XK_o: return 0x1f;
1277 case XK_P: case XK_p: return 0x23;
1278 case XK_Q: case XK_q: return 0x0c;
1279 case XK_R: case XK_r: return 0x0f;
1280 case XK_S: case XK_s: return 0x01;
1281 case XK_T: case XK_t: return 0x11;
1282 case XK_U: case XK_u: return 0x20;
1283 case XK_V: case XK_v: return 0x09;
1284 case XK_W: case XK_w: return 0x0d;
1285 case XK_X: case XK_x: return 0x07;
1286 case XK_Y: case XK_y: return 0x10;
1287 case XK_Z: case XK_z: return 0x06;
1288
1289 case XK_1: case XK_exclam: return 0x12;
1290 case XK_2: case XK_at: return 0x13;
1291 case XK_3: case XK_numbersign: return 0x14;
1292 case XK_4: case XK_dollar: return 0x15;
1293 case XK_5: case XK_percent: return 0x17;
1294 case XK_6: return 0x16;
1295 case XK_7: return 0x1a;
1296 case XK_8: return 0x1c;
1297 case XK_9: return 0x19;
1298 case XK_0: return 0x1d;
1299
1300 case XK_grave: case XK_asciitilde: return 0x0a;
1301 case XK_minus: case XK_underscore: return 0x1b;
1302 case XK_equal: case XK_plus: return 0x18;
1303 case XK_bracketleft: case XK_braceleft: return 0x21;
1304 case XK_bracketright: case XK_braceright: return 0x1e;
1305 case XK_backslash: case XK_bar: return 0x2a;
1306 case XK_semicolon: case XK_colon: return 0x29;
1307 case XK_apostrophe: case XK_quotedbl: return 0x27;
1308 case XK_comma: case XK_less: return 0x2b;
1309 case XK_period: case XK_greater: return 0x2f;
1310 case XK_slash: case XK_question: return 0x2c;
1311
1312 #if defined(ENABLE_XF86_DGA) || defined(ENABLE_FBDEV_DGA)
1313 case XK_Tab: if (ctrl_down) {suspend_emul(); return -1;} else return 0x30;
1314 #else
1315 case XK_Tab: return 0x30;
1316 #endif
1317 case XK_Return: return 0x24;
1318 case XK_space: return 0x31;
1319 case XK_BackSpace: return 0x33;
1320
1321 case XK_Delete: return 0x75;
1322 case XK_Insert: return 0x72;
1323 case XK_Home: case XK_Help: return 0x73;
1324 case XK_End: return 0x77;
1325 #ifdef __hpux
1326 case XK_Prior: return 0x74;
1327 case XK_Next: return 0x79;
1328 #else
1329 case XK_Page_Up: return 0x74;
1330 case XK_Page_Down: return 0x79;
1331 #endif
1332
1333 case XK_Control_L: return 0x36;
1334 case XK_Control_R: return 0x36;
1335 case XK_Shift_L: return 0x38;
1336 case XK_Shift_R: return 0x38;
1337 case XK_Alt_L: return 0x37;
1338 case XK_Alt_R: return 0x37;
1339 case XK_Meta_L: return 0x3a;
1340 case XK_Meta_R: return 0x3a;
1341 case XK_Menu: return 0x32;
1342 case XK_Caps_Lock: return 0x39;
1343 case XK_Num_Lock: return 0x47;
1344
1345 case XK_Up: return 0x3e;
1346 case XK_Down: return 0x3d;
1347 case XK_Left: return 0x3b;
1348 case XK_Right: return 0x3c;
1349
1350 case XK_Escape: if (ctrl_down) {quit_full_screen = true; emerg_quit = true; return -1;} else return 0x35;
1351
1352 case XK_F1: if (ctrl_down) {SysMountFirstFloppy(); return -1;} else return 0x7a;
1353 case XK_F2: return 0x78;
1354 case XK_F3: return 0x63;
1355 case XK_F4: return 0x76;
1356 case XK_F5: return 0x60;
1357 case XK_F6: return 0x61;
1358 case XK_F7: return 0x62;
1359 case XK_F8: return 0x64;
1360 case XK_F9: return 0x65;
1361 case XK_F10: return 0x6d;
1362 case XK_F11: return 0x67;
1363 case XK_F12: return 0x6f;
1364
1365 case XK_Print: return 0x69;
1366 case XK_Scroll_Lock: return 0x6b;
1367 case XK_Pause: return 0x71;
1368
1369 #if defined(XK_KP_Prior) && defined(XK_KP_Left) && defined(XK_KP_Insert) && defined (XK_KP_End)
1370 case XK_KP_0: case XK_KP_Insert: return 0x52;
1371 case XK_KP_1: case XK_KP_End: return 0x53;
1372 case XK_KP_2: case XK_KP_Down: return 0x54;
1373 case XK_KP_3: case XK_KP_Next: return 0x55;
1374 case XK_KP_4: case XK_KP_Left: return 0x56;
1375 case XK_KP_5: case XK_KP_Begin: return 0x57;
1376 case XK_KP_6: case XK_KP_Right: return 0x58;
1377 case XK_KP_7: case XK_KP_Home: return 0x59;
1378 case XK_KP_8: case XK_KP_Up: return 0x5b;
1379 case XK_KP_9: case XK_KP_Prior: return 0x5c;
1380 case XK_KP_Decimal: case XK_KP_Delete: return 0x41;
1381 #else
1382 case XK_KP_0: return 0x52;
1383 case XK_KP_1: return 0x53;
1384 case XK_KP_2: return 0x54;
1385 case XK_KP_3: return 0x55;
1386 case XK_KP_4: return 0x56;
1387 case XK_KP_5: return 0x57;
1388 case XK_KP_6: return 0x58;
1389 case XK_KP_7: return 0x59;
1390 case XK_KP_8: return 0x5b;
1391 case XK_KP_9: return 0x5c;
1392 case XK_KP_Decimal: return 0x41;
1393 #endif
1394 case XK_KP_Add: return 0x45;
1395 case XK_KP_Subtract: return 0x4e;
1396 case XK_KP_Multiply: return 0x43;
1397 case XK_KP_Divide: return 0x4b;
1398 case XK_KP_Enter: return 0x4c;
1399 case XK_KP_Equal: return 0x51;
1400 }
1401 return -1;
1402 }
1403
1404 static int event2keycode(XKeyEvent &ev)
1405 {
1406 KeySym ks;
1407 int as;
1408 int i = 0;
1409
1410 do {
1411 ks = XLookupKeysym(&ev, i++);
1412 as = kc_decode(ks);
1413 if (as != -1)
1414 return as;
1415 } while (ks != NoSymbol);
1416
1417 return -1;
1418 }
1419
1420
1421 /*
1422 * X event handling
1423 */
1424
1425 static void handle_events(void)
1426 {
1427 while (XPending(x_display)) {
1428 XEvent event;
1429 XNextEvent(x_display, &event);
1430
1431 switch (event.type) {
1432 // Mouse button
1433 case ButtonPress: {
1434 unsigned int button = event.xbutton.button;
1435 if (button < 4)
1436 ADBMouseDown(button - 1);
1437 else if (button < 6) { // Wheel mouse
1438 if (mouse_wheel_mode == 0) {
1439 int key = (button == 5) ? 0x79 : 0x74; // Page up/down
1440 ADBKeyDown(key);
1441 ADBKeyUp(key);
1442 } else {
1443 int key = (button == 5) ? 0x3d : 0x3e; // Cursor up/down
1444 for(int i=0; i<mouse_wheel_lines; i++) {
1445 ADBKeyDown(key);
1446 ADBKeyUp(key);
1447 }
1448 }
1449 }
1450 break;
1451 }
1452 case ButtonRelease: {
1453 unsigned int button = event.xbutton.button;
1454 if (button < 4)
1455 ADBMouseUp(button - 1);
1456 break;
1457 }
1458
1459 // Mouse moved
1460 case EnterNotify:
1461 case MotionNotify:
1462 ADBMouseMoved(event.xmotion.x, event.xmotion.y);
1463 break;
1464
1465 // Keyboard
1466 case KeyPress: {
1467 int code;
1468 if (use_keycodes) {
1469 event2keycode(event.xkey); // This is called to process the hotkeys
1470 code = keycode_table[event.xkey.keycode & 0xff];
1471 } else
1472 code = event2keycode(event.xkey);
1473 if (code != -1) {
1474 if (!emul_suspended) {
1475 if (code == 0x39) { // Caps Lock pressed
1476 if (caps_on) {
1477 ADBKeyUp(code);
1478 caps_on = false;
1479 } else {
1480 ADBKeyDown(code);
1481 caps_on = true;
1482 }
1483 } else
1484 ADBKeyDown(code);
1485 if (code == 0x36)
1486 ctrl_down = true;
1487 } else {
1488 #if defined(ENABLE_XF86_DGA) || defined(ENABLE_FBDEV_DGA)
1489 if (code == 0x31)
1490 resume_emul(); // Space wakes us up
1491 #endif
1492 }
1493 }
1494 break;
1495 }
1496 case KeyRelease: {
1497 int code;
1498 if (use_keycodes) {
1499 event2keycode(event.xkey); // This is called to process the hotkeys
1500 code = keycode_table[event.xkey.keycode & 0xff];
1501 } else
1502 code = event2keycode(event.xkey);
1503 if (code != -1 && code != 0x39) { // Don't propagate Caps Lock releases
1504 ADBKeyUp(code);
1505 if (code == 0x36)
1506 ctrl_down = false;
1507 }
1508 break;
1509 }
1510
1511 // Hidden parts exposed, force complete refresh of window
1512 case Expose:
1513 if (display_type == DISPLAY_WINDOW) {
1514 #ifdef ENABLE_VOSF
1515 if (use_vosf) { // VOSF refresh
1516 LOCK_VOSF;
1517 PFLAG_SET_ALL;
1518 UNLOCK_VOSF;
1519 memset(the_buffer_copy, 0, VideoMonitor.mode.bytes_per_row * VideoMonitor.mode.y);
1520 }
1521 else
1522 #endif
1523 if (frame_skip == 0) { // Dynamic refresh
1524 int x1, y1;
1525 for (y1=0; y1<16; y1++)
1526 for (x1=0; x1<16; x1++)
1527 updt_box[x1][y1] = true;
1528 nr_boxes = 16 * 16;
1529 } else // Static refresh
1530 memset(the_buffer_copy, 0, VideoMonitor.mode.bytes_per_row * VideoMonitor.mode.y);
1531 }
1532 break;
1533
1534 // Window "close" widget clicked
1535 case ClientMessage:
1536 if (event.xclient.format == 32 && event.xclient.data.l[0] == WM_DELETE_WINDOW) {
1537 ADBKeyDown(0x7f); // Power key
1538 ADBKeyUp(0x7f);
1539 }
1540 break;
1541 }
1542 }
1543 }
1544
1545
1546 /*
1547 * Window display update
1548 */
1549
1550 // Dynamic display update (variable frame rate for each box)
1551 static void update_display_dynamic(int ticker)
1552 {
1553 int y1, y2, y2s, y2a, i, x1, xm, xmo, ymo, yo, yi, yil, xi;
1554 int xil = 0;
1555 int rxm = 0, rxmo = 0;
1556 int bytes_per_row = VideoMonitor.mode.bytes_per_row;
1557 int bytes_per_pixel = VideoMonitor.mode.bytes_per_row / VideoMonitor.mode.x;
1558 int rx = VideoMonitor.mode.bytes_per_row / 16;
1559 int ry = VideoMonitor.mode.y / 16;
1560 int max_box;
1561
1562 y2s = sm_uptd[ticker % 8];
1563 y2a = 8;
1564 for (i = 0; i < 6; i++)
1565 if (ticker % (2 << i))
1566 break;
1567 max_box = sm_no_boxes[i];
1568
1569 if (y2a) {
1570 for (y1=0; y1<16; y1++) {
1571 for (y2=y2s; y2 < ry; y2 += y2a) {
1572 i = ((y1 * ry) + y2) * bytes_per_row;
1573 for (x1=0; x1<16; x1++, i += rx) {
1574 if (updt_box[x1][y1] == false) {
1575 if (memcmp(&the_buffer_copy[i], &the_buffer[i], rx)) {
1576 updt_box[x1][y1] = true;
1577 nr_boxes++;
1578 }
1579 }
1580 }
1581 }
1582 }
1583 }
1584
1585 if ((nr_boxes <= max_box) && (nr_boxes)) {
1586 for (y1=0; y1<16; y1++) {
1587 for (x1=0; x1<16; x1++) {
1588 if (updt_box[x1][y1] == true) {
1589 if (rxm == 0)
1590 xm = x1;
1591 rxm += rx;
1592 updt_box[x1][y1] = false;
1593 }
1594 if (((updt_box[x1+1][y1] == false) || (x1 == 15)) && (rxm)) {
1595 if ((rxmo != rxm) || (xmo != xm) || (yo != y1 - 1)) {
1596 if (rxmo) {
1597 xi = xmo * rx;
1598 yi = ymo * ry;
1599 xil = rxmo;
1600 yil = (yo - ymo +1) * ry;
1601 }
1602 rxmo = rxm;
1603 xmo = xm;
1604 ymo = y1;
1605 }
1606 rxm = 0;
1607 yo = y1;
1608 }
1609 if (xil) {
1610 i = (yi * bytes_per_row) + xi;
1611 for (y2=0; y2 < yil; y2++, i += bytes_per_row)
1612 memcpy(&the_buffer_copy[i], &the_buffer[i], xil);
1613 if (VideoMonitor.mode.depth == VDEPTH_1BIT) {
1614 if (have_shm)
1615 XShmPutImage(x_display, the_win, the_gc, img, xi * 8, yi, xi * 8, yi, xil * 8, yil, 0);
1616 else
1617 XPutImage(x_display, the_win, the_gc, img, xi * 8, yi, xi * 8, yi, xil * 8, yil);
1618 } else {
1619 if (have_shm)
1620 XShmPutImage(x_display, the_win, the_gc, img, xi / bytes_per_pixel, yi, xi / bytes_per_pixel, yi, xil / bytes_per_pixel, yil, 0);
1621 else
1622 XPutImage(x_display, the_win, the_gc, img, xi / bytes_per_pixel, yi, xi / bytes_per_pixel, yi, xil / bytes_per_pixel, yil);
1623 }
1624 xil = 0;
1625 }
1626 if ((x1 == 15) && (y1 == 15) && (rxmo)) {
1627 x1--;
1628 xi = xmo * rx;
1629 yi = ymo * ry;
1630 xil = rxmo;
1631 yil = (yo - ymo +1) * ry;
1632 rxmo = 0;
1633 }
1634 }
1635 }
1636 nr_boxes = 0;
1637 }
1638 }
1639
1640 // Static display update (fixed frame rate, but incremental)
1641 static void update_display_static(void)
1642 {
1643 // Incremental update code
1644 int wide = 0, high = 0, x1, x2, y1, y2, i, j;
1645 int bytes_per_row = VideoMonitor.mode.bytes_per_row;
1646 int bytes_per_pixel = VideoMonitor.mode.bytes_per_row / VideoMonitor.mode.x;
1647 uint8 *p, *p2;
1648
1649 // Check for first line from top and first line from bottom that have changed
1650 y1 = 0;
1651 for (j=0; j<VideoMonitor.mode.y; j++) {
1652 if (memcmp(&the_buffer[j * bytes_per_row], &the_buffer_copy[j * bytes_per_row], bytes_per_row)) {
1653 y1 = j;
1654 break;
1655 }
1656 }
1657 y2 = y1 - 1;
1658 for (j=VideoMonitor.mode.y-1; j>=y1; j--) {
1659 if (memcmp(&the_buffer[j * bytes_per_row], &the_buffer_copy[j * bytes_per_row], bytes_per_row)) {
1660 y2 = j;
1661 break;
1662 }
1663 }
1664 high = y2 - y1 + 1;
1665
1666 // Check for first column from left and first column from right that have changed
1667 if (high) {
1668 if (VideoMonitor.mode.depth == VDEPTH_1BIT) {
1669 x1 = VideoMonitor.mode.x - 1;
1670 for (j=y1; j<=y2; j++) {
1671 p = &the_buffer[j * bytes_per_row];
1672 p2 = &the_buffer_copy[j * bytes_per_row];
1673 for (i=0; i<(x1>>3); i++) {
1674 if (*p != *p2) {
1675 x1 = i << 3;
1676 break;
1677 }
1678 p++; p2++;
1679 }
1680 }
1681 x2 = x1;
1682 for (j=y1; j<=y2; j++) {
1683 p = &the_buffer[j * bytes_per_row];
1684 p2 = &the_buffer_copy[j * bytes_per_row];
1685 p += bytes_per_row;
1686 p2 += bytes_per_row;
1687 for (i=(VideoMonitor.mode.x>>3); i>(x2>>3); i--) {
1688 p--; p2--;
1689 if (*p != *p2) {
1690 x2 = (i << 3) + 7;
1691 break;
1692 }
1693 }
1694 }
1695 wide = x2 - x1 + 1;
1696
1697 // Update copy of the_buffer
1698 if (high && wide) {
1699 for (j=y1; j<=y2; j++) {
1700 i = j * bytes_per_row + (x1 >> 3);
1701 memcpy(the_buffer_copy + i, the_buffer + i, wide >> 3);
1702 }
1703 }
1704
1705 } else {
1706 x1 = VideoMonitor.mode.x;
1707 for (j=y1; j<=y2; j++) {
1708 p = &the_buffer[j * bytes_per_row];
1709 p2 = &the_buffer_copy[j * bytes_per_row];
1710 for (i=0; i<x1*bytes_per_pixel; i++) {
1711 if (*p != *p2) {
1712 x1 = i / bytes_per_pixel;
1713 break;
1714 }
1715 p++; p2++;
1716 }
1717 }
1718 x2 = x1;
1719 for (j=y1; j<=y2; j++) {
1720 p = &the_buffer[j * bytes_per_row];
1721 p2 = &the_buffer_copy[j * bytes_per_row];
1722 p += bytes_per_row;
1723 p2 += bytes_per_row;
1724 for (i=VideoMonitor.mode.x*bytes_per_pixel; i>x2*bytes_per_pixel; i--) {
1725 p--;
1726 p2--;
1727 if (*p != *p2) {
1728 x2 = i / bytes_per_pixel;
1729 break;
1730 }
1731 }
1732 }
1733 wide = x2 - x1;
1734
1735 // Update copy of the_buffer
1736 if (high && wide) {
1737 for (j=y1; j<=y2; j++) {
1738 i = j * bytes_per_row + x1 * bytes_per_pixel;
1739 memcpy(the_buffer_copy + i, the_buffer + i, bytes_per_pixel * wide);
1740 }
1741 }
1742 }
1743 }
1744
1745 // Refresh display
1746 if (high && wide) {
1747 if (have_shm)
1748 XShmPutImage(x_display, the_win, the_gc, img, x1, y1, x1, y1, wide, high, 0);
1749 else
1750 XPutImage(x_display, the_win, the_gc, img, x1, y1, x1, y1, wide, high);
1751 }
1752 }
1753
1754
1755 /*
1756 * Screen refresh functions
1757 */
1758
1759 // We suggest the compiler to inline the next two functions so that it
1760 // may specialise the code according to the current screen depth and
1761 // display type. A clever compiler would do that job by itself though...
1762
1763 // NOTE: update_display_vosf is inlined too
1764
1765 static inline void possibly_quit_dga_mode()
1766 {
1767 #if defined(ENABLE_XF86_DGA) || defined(ENABLE_FBDEV_DGA)
1768 // Quit DGA mode if requested
1769 if (quit_full_screen) {
1770 quit_full_screen = false;
1771 #ifdef ENABLE_XF86_DGA
1772 XF86DGADirectVideo(x_display, screen, 0);
1773 #endif
1774 XUngrabPointer(x_display, CurrentTime);
1775 XUngrabKeyboard(x_display, CurrentTime);
1776 XUnmapWindow(x_display, the_win);
1777 wait_unmapped(the_win);
1778 }
1779 #endif
1780 }
1781
1782 static inline void handle_palette_changes(int display_type)
1783 {
1784 LOCK_PALETTE;
1785
1786 if (palette_changed) {
1787 palette_changed = false;
1788 if (!IsDirectMode(VideoMonitor.mode)) {
1789 XStoreColors(x_display, cmap[0], palette, 256);
1790 XStoreColors(x_display, cmap[1], palette, 256);
1791 XSync(x_display, false);
1792
1793 #ifdef ENABLE_XF86_DGA
1794 if (display_type == DISPLAY_DGA) {
1795 current_dga_cmap ^= 1;
1796 XF86DGAInstallColormap(x_display, screen, cmap[current_dga_cmap]);
1797 }
1798 #endif
1799 }
1800 }
1801
1802 UNLOCK_PALETTE;
1803 }
1804
1805 static void video_refresh_dga(void)
1806 {
1807 // Quit DGA mode if requested
1808 possibly_quit_dga_mode();
1809
1810 // Handle X events
1811 handle_events();
1812
1813 // Handle palette changes
1814 handle_palette_changes(DISPLAY_DGA);
1815 }
1816
1817 #ifdef ENABLE_VOSF
1818 #if REAL_ADDRESSING || DIRECT_ADDRESSING
1819 static void video_refresh_dga_vosf(void)
1820 {
1821 // Quit DGA mode if requested
1822 possibly_quit_dga_mode();
1823
1824 // Handle X events
1825 handle_events();
1826
1827 // Handle palette changes
1828 handle_palette_changes(DISPLAY_DGA);
1829
1830 // Update display (VOSF variant)
1831 static int tick_counter = 0;
1832 if (++tick_counter >= frame_skip) {
1833 tick_counter = 0;
1834 if (mainBuffer.dirty) {
1835 LOCK_VOSF;
1836 update_display_dga_vosf();
1837 UNLOCK_VOSF;
1838 }
1839 }
1840 }
1841 #endif
1842
1843 static void video_refresh_window_vosf(void)
1844 {
1845 // Quit DGA mode if requested
1846 possibly_quit_dga_mode();
1847
1848 // Handle X events
1849 handle_events();
1850
1851 // Handle palette changes
1852 handle_palette_changes(DISPLAY_WINDOW);
1853
1854 // Update display (VOSF variant)
1855 static int tick_counter = 0;
1856 if (++tick_counter >= frame_skip) {
1857 tick_counter = 0;
1858 if (mainBuffer.dirty) {
1859 LOCK_VOSF;
1860 update_display_window_vosf();
1861 UNLOCK_VOSF;
1862 XSync(x_display, false); // Let the server catch up
1863 }
1864 }
1865 }
1866 #endif // def ENABLE_VOSF
1867
1868 static void video_refresh_window_static(void)
1869 {
1870 // Handle X events
1871 handle_events();
1872
1873 // Handle_palette changes
1874 handle_palette_changes(DISPLAY_WINDOW);
1875
1876 // Update display (static variant)
1877 static int tick_counter = 0;
1878 if (++tick_counter >= frame_skip) {
1879 tick_counter = 0;
1880 update_display_static();
1881 }
1882 }
1883
1884 static void video_refresh_window_dynamic(void)
1885 {
1886 // Handle X events
1887 handle_events();
1888
1889 // Handle_palette changes
1890 handle_palette_changes(DISPLAY_WINDOW);
1891
1892 // Update display (dynamic variant)
1893 static int tick_counter = 0;
1894 tick_counter++;
1895 update_display_dynamic(tick_counter);
1896 }
1897
1898
1899 /*
1900 * Thread for screen refresh, input handling etc.
1901 */
1902
1903 static void VideoRefreshInit(void)
1904 {
1905 // TODO: set up specialised 8bpp VideoRefresh handlers ?
1906 if (display_type == DISPLAY_DGA) {
1907 #if ENABLE_VOSF && (REAL_ADDRESSING || DIRECT_ADDRESSING)
1908 if (use_vosf)
1909 video_refresh = video_refresh_dga_vosf;
1910 else
1911 #endif
1912 video_refresh = video_refresh_dga;
1913 }
1914 else {
1915 #ifdef ENABLE_VOSF
1916 if (use_vosf)
1917 video_refresh = video_refresh_window_vosf;
1918 else
1919 #endif
1920 if (frame_skip == 0)
1921 video_refresh = video_refresh_window_dynamic;
1922 else
1923 video_refresh = video_refresh_window_static;
1924 }
1925 }
1926
1927 void VideoRefresh(void)
1928 {
1929 // TODO: make main_unix/VideoRefresh call directly video_refresh() ?
1930 video_refresh();
1931 }
1932
1933 #ifdef HAVE_PTHREADS
1934 static void *redraw_func(void *arg)
1935 {
1936 uint64 start = GetTicks_usec();
1937 int64 ticks = 0;
1938 uint64 next = GetTicks_usec();
1939 while (!redraw_thread_cancel) {
1940 video_refresh();
1941 next += 16667;
1942 int64 delay = next - GetTicks_usec();
1943 if (delay > 0)
1944 Delay_usec(delay);
1945 else if (delay < -16667)
1946 next = GetTicks_usec();
1947 ticks++;
1948 }
1949 uint64 end = GetTicks_usec();
1950 // printf("%Ld ticks in %Ld usec = %Ld ticks/sec\n", ticks, end - start, ticks * 1000000 / (end - start));
1951 return NULL;
1952 }
1953 #endif