[Dosemu-devel] [PATCH] Add XRandR support; rework input device grabbing.
Brought to you by:
bartoldeman
From: Ryan C. U. <nem...@ic...> - 2015-01-18 16:37:11
|
From: "Ryan C. Underwood" <ne...@ic...> XRandR support is fully added and works properly in a multihead configuration if the user's version is at least 1.2. It works basically like the existing XVidMode code and is preferred whenever it is available. It will also properly restore the dosemu window's position when exiting fullscreen mode. Keyboard and mouse grabbing had to be reworked to account for multihead (e.g., fullscreen DOS window on one output but with the ability to use applications on a different output). Signed-off-by: Ryan C. Underwood <ne...@ic...> --- configure.ac | 17 +++ src/include/config.h.in | 3 + src/plugin/X/X.c | 275 ++++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 252 insertions(+), 43 deletions(-) diff --git a/configure.ac b/configure.ac index a5b4df9..f444b31 100644 --- a/configure.ac +++ b/configure.ac @@ -376,6 +376,20 @@ if test "$have_x" = "yes" ; then [#include <X11/Xlib.h>]) fi + AC_ARG_ENABLE(xrandr, + { --disable-xrandr do NOT use the XRandR extension]) + if test "$enable_xrandr" != "no" ; then + AC_CHECK_LIB(Xrandr, XRRQueryVersion, enable_xrandr="yes", enable_xrandr="no") + fi + if test "$enable_xrandr" = "no" ; then + AC_MSG_NOTICE(Compiling without the XRandR extension) + else + AC_CHECK_HEADER(X11/extensions/Xrandr.h, + [AC_DEFINE(HAVE_XRANDR,1) X_LIBS="-lXrandr $X_LIBS"], + AC_MSG_NOTICE(Compiling without the XRandR extension), + [#include <X11/Xlib.h>]) + fi + LIBS=$SLIBS unset SLIBS @@ -768,6 +782,9 @@ AH_TEMPLATE([HAVE_MITSHM], AH_TEMPLATE([HAVE_XVIDMODE], [ Define this if you want to use the XF86 video mode extension ]) +AH_TEMPLATE([HAVE_XRANDR], +[ Define this if you want to use the XRandR extension ]) + AH_TEMPLATE([ASPI_SUPPORT], [ Define this for ASPI (generic SCSI) support ]) diff --git a/src/include/config.h.in b/src/include/config.h.in index 6ef8763..7cf2248 100644 --- a/src/include/config.h.in +++ b/src/include/config.h.in @@ -134,6 +134,9 @@ EXTERN char *dosemu_midi_in_path INIT("~/" LOCALDIR_BASE_NAME "/run/" DOSEMU_MID /* Define to 1 if you have the <unistd.h> header file. */ #undef HAVE_UNISTD_H +/* Define this if you want to use the XRandR extension */ +#undef HAVE_XRANDR + /* Define this if you want to use the XF86 video mode extension */ #undef HAVE_XVIDMODE diff --git a/src/plugin/X/X.c b/src/plugin/X/X.c index 8e188c8..4a22b0f 100644 --- a/src/plugin/X/X.c +++ b/src/plugin/X/X.c @@ -235,6 +235,10 @@ #include <X11/extensions/xf86vmode.h> #endif +#ifdef HAVE_XRANDR +#include <X11/extensions/Xrandr.h> +#endif + #include "emu.h" #include "timers.h" #include "bios.h" @@ -320,6 +324,13 @@ static int modecount; static XF86VidModeModeInfo **vidmode_modes; #endif +#ifdef HAVE_XRANDR +static int xrandr_ok = 0; +static RRMode xrandr_win_mode = None; +static RRCrtc xrandr_win_crtc = None; +static int xrandr_win_xpos, xrandr_win_ypos; +#endif + Display *display; /* used in plugin/?/keyb_X_keycode.c */ static int screen; static Visual *visual; @@ -369,7 +380,7 @@ static unsigned ximage_bits_per_pixel; static unsigned ximage_mode; static vga_emu_update_type veut; -static int grab_active = 0, kbd_grab_active = 0; +static int grab_active = 0, kbd_grab_active = 0, force_kbd_grab = 0; #if CONFIG_X_MOUSE static char *grab_keystring = "Home"; static KeySym grab_keysym = NoSymbol; @@ -402,6 +413,10 @@ static void X_dga_done(void); static void X_xf86vm_init(void); static void X_xf86vm_done(void); #endif +#ifdef HAVE_XRANDR +static void X_randr_init(void); +static void X_randr_done(void); +#endif static void X_keymap_init(void); @@ -432,7 +447,7 @@ static void resize_ximage(unsigned, unsigned); static int X_set_videomode(int, int, int); static void X_resize_text_screen(void); static void toggle_fullscreen_mode(int); -static void X_vidmode(int w, int h, int *new_width, int *new_height); +static void X_vidmode(int w, int h, int *new_x, int *new_y, int *new_width, int *new_height); static void lock_window_size(unsigned wx_res, unsigned wy_res); /* screen update/redraw functions */ @@ -447,6 +462,7 @@ static Cursor create_invisible_cursor(void); /* text mode cursor manipulation stuff */ static void X_update_cursor(void); +static void toggle_kbd_grab(void); #if CONFIG_X_MOUSE /* mouse related code */ static void set_mouse_position(int, int); @@ -593,6 +609,10 @@ int X_init() X_xf86vm_init(); #endif +#ifdef HAVE_XRANDR + X_randr_init(); +#endif + /* see if we find out something useful about our X server... -- sw */ X_keymap_init(); @@ -806,6 +826,10 @@ void X_close() X_xf86vm_done(); #endif +#ifdef HAVE_XRANDR + X_randr_done(); +#endif + X_load_text_font(display, 0, drawwindow, NULL, NULL, NULL); if(our_window) { XDestroyWindow(display, drawwindow); @@ -1026,12 +1050,51 @@ static void X_xf86vm_init(void) static void X_xf86vm_done(void) { if (mainwindow == fullscreenwindow) - X_vidmode(-1, -1, &w_x_res, &w_y_res); + X_vidmode(-1, -1, NULL, NULL, &w_x_res, &w_y_res); xf86vm_ok = 0; } #endif +#ifdef HAVE_XRANDR +static void X_randr_init(void) +{ + int param1, param2; + if (XRRQueryExtension(display, ¶m1, ¶m2) && + XRRQueryVersion(display, ¶m1, ¶m2)) + { + X_printf("X: RandR Extension version %d.%d\n", param1, param2); + if (param1 <= 1 && param2 < 2) { + X_printf("X: RandR version doesn't support stretched desktop or multihead!\n"); + /* continue anyway, as it should work for single head */ + } + xrandr_ok = 1; + } +} + +static void X_randr_exit_fullscreen(void) +{ + if (xrandr_win_crtc != None && xrandr_win_mode != None) { + X_printf("X: RandR restoring old mode %d crtc %d\n", (int)xrandr_win_mode, (int)xrandr_win_crtc); + XRRScreenResources *sr = XRRGetScreenResourcesCurrent(display, mainwindow); + XRRCrtcInfo *ci = XRRGetCrtcInfo(display, sr, xrandr_win_crtc); + XRRSetCrtcConfig(display, sr, xrandr_win_crtc, CurrentTime, ci->x, ci->y, xrandr_win_mode, ci->rotation, ci->outputs, ci->noutput); + XRRFreeCrtcInfo(ci); + XRRFreeScreenResources(sr); + xrandr_win_crtc = xrandr_win_mode = None; + } +} + +static void X_randr_done(void) +{ + if (mainwindow == fullscreenwindow) { + X_printf("X: RandR leaving fullscreen mode\n"); + X_randr_exit_fullscreen(); + } + xrandr_ok = 0; +} +#endif /* HAVE_XRANDR */ + /* * Handle 'auto'-entries in dosemu.conf, namely * $_X_keycode & $_layout @@ -1229,15 +1292,10 @@ static void toggle_kbd_grab(void) { if(kbd_grab_active ^= 1) { X_printf("X: keyboard grab activated\n"); - if (mainwindow != fullscreenwindow) { - XGrabKeyboard(display, drawwindow, True, GrabModeAsync, GrabModeAsync, CurrentTime); - } - } - else { + XGrabKeyboard(display, drawwindow, True, GrabModeAsync, GrabModeAsync, CurrentTime); + } else { X_printf("X: keyboard grab released\n"); - if (mainwindow != fullscreenwindow) { - XUngrabKeyboard(display, CurrentTime); - } + XUngrabKeyboard(display, CurrentTime); } X_change_config(CHG_TITLE, NULL); } @@ -1247,19 +1305,15 @@ static void toggle_mouse_grab(void) if(grab_active ^= 1) { config.mouse.use_absolute = 0; X_printf("X: mouse grab activated\n"); - if (mainwindow != fullscreenwindow) { - XGrabPointer(display, drawwindow, True, PointerMotionMask | ButtonPressMask | ButtonReleaseMask, - GrabModeAsync, GrabModeAsync, drawwindow, None, CurrentTime); - } + XGrabPointer(display, drawwindow, True, PointerMotionMask | ButtonPressMask | ButtonReleaseMask, + GrabModeAsync, GrabModeAsync, drawwindow, None, CurrentTime); X_set_mouse_cursor(mouse_cursor_visible, mouse_x, mouse_y, w_x_res, w_y_res); mouse_enable_native_cursor(1); } else { config.mouse.use_absolute = 1; X_printf("X: mouse grab released\n"); - if (mainwindow != fullscreenwindow) { - XUngrabPointer(display, CurrentTime); - } + XUngrabPointer(display, CurrentTime); X_set_mouse_cursor(mouse_cursor_visible, mouse_x, mouse_y, w_x_res, w_y_res); mouse_sync_coords(mouse_x, mouse_y, w_x_res, w_y_res); mouse_enable_native_cursor(0); @@ -1338,7 +1392,7 @@ static void X_wait_unmapped(Window win) static void toggle_fullscreen_mode(int init) { - int resize_height, resize_width; + int reloc_x, reloc_y, resize_height, resize_width; if (!init) { XUnmapWindow(display, mainwindow); @@ -1351,15 +1405,12 @@ static void toggle_fullscreen_mode(int init) toggling_fullscreen = 2; saved_w_x_res = w_x_res; saved_w_y_res = w_y_res; - if (!grab_active) { - toggle_mouse_grab(); - force_grab = 1; - } - X_vidmode(x_res, y_res, &resize_width, &resize_height); + X_vidmode(x_res, y_res, &reloc_x, &reloc_y, &resize_width, &resize_height); mainwindow = fullscreenwindow; if (vga.mode_class == GRAPH || use_bitmap_font) { - XResizeWindow(display, mainwindow, resize_width+1, resize_height+1); - XResizeWindow(display, drawwindow, resize_width+1, resize_height+1); + X_printf("X: relocating window to %d,%d\n", reloc_x, reloc_y); + XMoveResizeWindow(display, mainwindow, reloc_x, reloc_y, resize_width+1, resize_height+1); + XMoveResizeWindow(display, drawwindow, reloc_x, reloc_y, resize_width+1, resize_height+1); } else { shift_x = (resize_width - w_x_res) / 2; shift_y = (resize_height - w_y_res) / 2; @@ -1368,30 +1419,35 @@ static void toggle_fullscreen_mode(int init) XMapWindow(display, mainwindow); XRaiseWindow(display, mainwindow); XReparentWindow(display, drawwindow, mainwindow, shift_x, shift_y); - XGrabPointer(display, drawwindow, True, - PointerMotionMask | ButtonPressMask | ButtonReleaseMask, - GrabModeAsync, GrabModeAsync, drawwindow, None, - CurrentTime); - XGrabKeyboard(display, drawwindow, True, GrabModeAsync, - GrabModeAsync, CurrentTime); + if (!grab_active) { + toggle_mouse_grab(); + force_grab = 1; + } + if (!kbd_grab_active) { + toggle_kbd_grab(); + force_kbd_grab = 1; + } } else { X_printf("X: entering windowed mode!\n"); w_x_res = saved_w_x_res; w_y_res = saved_w_y_res; - XUngrabKeyboard(display, CurrentTime); - XUngrabPointer(display, CurrentTime); mainwindow = normalwindow; - X_vidmode(-1, -1, &resize_width, &resize_height); + X_vidmode(-1, -1, &reloc_x, &reloc_y, &resize_width, &resize_height); if (vga.mode_class == GRAPH || use_bitmap_font) { - XResizeWindow(display, mainwindow, resize_width+1, resize_height+1); - XResizeWindow(display, drawwindow, resize_width+1, resize_height+1); + X_printf("X: relocating dosemu window to %d,%d\n", reloc_x, reloc_y); + XMoveResizeWindow(display, mainwindow, reloc_x, reloc_y, resize_width+1, resize_height+1); + XMoveResizeWindow(display, drawwindow, reloc_x, reloc_y, resize_width+1, resize_height+1); } XMapWindow(display, mainwindow); XReparentWindow(display, drawwindow, mainwindow, 0, 0); if (force_grab && grab_active) { toggle_mouse_grab(); + force_grab = 0; + } + if (force_kbd_grab && kbd_grab_active) { + toggle_kbd_grab(); + force_kbd_grab = 0; } - force_grab = 0; } if(vga.mode_class == TEXT && !use_bitmap_font) { X_resize_text_screen(); @@ -1550,6 +1606,7 @@ static void X_handle_events(void) toggle_mouse_grab(); break; } else if (keysym == XK_k) { + force_kbd_grab = 0; toggle_kbd_grab(); break; } else if (keysym == XK_f) { @@ -1652,6 +1709,12 @@ static void X_handle_events(void) } set_mouse_buttons(e.xcrossing.state); mouse_really_left_window = 0; + /* Grab keyboard if fullscreen (i.e., entering window from another + * CRTC in a multihead system. Otherwise we can't type.*/ + if (mainwindow == fullscreenwindow && !kbd_grab_active) { + toggle_kbd_grab(); + force_kbd_grab = 1; + } } break; @@ -1666,6 +1729,12 @@ static void X_handle_events(void) X_printf("X: bogus LeaveNotify event\n"); mouse_really_left_window = 0; } + /* Release keyboard if fullscreen, mouse leaves window, and the + * current grab had been forced by dosemu. */ + if (mainwindow == fullscreenwindow && kbd_grab_active && !force_kbd_grab) { + toggle_kbd_grab(); + force_kbd_grab = 0; + } break; case ConfigureNotify: @@ -2066,7 +2135,7 @@ static void lock_window_size(unsigned wx_res, unsigned wy_res) sh.width = sh.min_width = sh.max_width = wx_res; sh.height = sh.min_height = sh.max_height = wy_res; - sh.flags = PSize | PMinSize | PMaxSize; + sh.flags = PPosition | PSize | PMinSize | PMaxSize; if(config.X_fixed_aspect || config.X_aspect_43) sh.flags |= PAspect; if (use_bitmap_font) { sh.flags |= PResizeInc; @@ -2086,7 +2155,7 @@ static void lock_window_size(unsigned wx_res, unsigned wy_res) x_fill = w_x_res; y_fill = w_y_res; if (mainwindow == fullscreenwindow) - X_vidmode(x_res, y_res, &x_fill, &y_fill); + X_vidmode(x_res, y_res, NULL, NULL, &x_fill, &y_fill); XResizeWindow(display, mainwindow, x_fill+1, y_fill+1); @@ -2206,8 +2275,9 @@ int X_set_videomode(int mode_class, int text_width, int text_height) saved_w_x_res = w_x_res; saved_w_y_res = w_y_res; lock_window_size(w_x_res, w_y_res); + /* lock_window_size() already did this, but there must be a reason ... */ if(mainwindow == fullscreenwindow) { - X_vidmode(x_res, y_res, &w_x_res, &w_y_res); + X_vidmode(x_res, y_res, NULL, NULL, &w_x_res, &w_y_res); } if (!use_bitmap_font) { w_x_res = saved_w_x_res; @@ -2229,7 +2299,7 @@ int X_set_videomode(int mode_class, int text_width, int text_height) if(mainwindow == fullscreenwindow) { saved_w_x_res = w_x_res; saved_w_y_res = w_y_res; - X_vidmode(x_res, y_res, &w_x_res, &w_y_res); + X_vidmode(x_res, y_res, NULL, NULL, &w_x_res, &w_y_res); } create_ximage(); @@ -2319,13 +2389,128 @@ void X_resize_text_screen() /* * Change to requested video mode or the closest greater one. */ -static void X_vidmode(int w, int h, int *new_width, int *new_height) +static void X_vidmode(int w, int h, int *new_x, int *new_y, int *new_width, int *new_height) { int nw, nh, mx, my, shift_x, shift_y; nw = DisplayWidth(display, screen); nh = DisplayHeight(display, screen); + int dosemu_x = -1, dosemu_y = -1; + XWindowAttributes attr; + if (XGetWindowAttributes(display, rootwindow, &attr)) { + X_printf("X: X_vidmode: root window size (%d,%d)\n", + attr.width, attr.height); + + Window child; + if (XTranslateCoordinates(display, normalwindow, rootwindow, 0, 0, &dosemu_x, &dosemu_y, &child) + && XGetWindowAttributes(display, normalwindow, &attr)) { + X_printf("X: X_vidmode: dosemu window loc (%d,%d) size (%d,%d)\n", + dosemu_x, dosemu_y, attr.width, attr.height); + } + } + +#ifdef HAVE_XRANDR + if (xrandr_ok) { + if (w == -1 && h == -1) { + /* Return to window. */ + X_printf("X: RandR leaving fullscreen mode\n"); + X_randr_exit_fullscreen(); + dosemu_x = xrandr_win_xpos; + dosemu_y = xrandr_win_ypos; + } else if (mainwindow != fullscreenwindow) { + X_printf("X: RandR entering fullscreen mode\n"); + /* Find which CRTC 'window' dosemu's upper left corner is within. */ + XRRScreenResources *sr = XRRGetScreenResourcesCurrent(display, mainwindow); + RRCrtc crtc = None; + int i; + for (i = 0; i < sr->ncrtc; i++) { + XRRCrtcInfo *ci = XRRGetCrtcInfo(display, sr, sr->crtcs[i]); + if (ci->mode == None) { + /* crtc disabled, try another */ + XRRFreeCrtcInfo(ci); + continue; + } + X_printf("X: RandR considering CRTC (%d+%d,%d+%d)\n", ci->x, ci->width, ci->y, ci->height); + if (dosemu_x >= ci->x && dosemu_x < ci->x + ci->width && + dosemu_y >= ci->y && dosemu_y < ci->y + ci->height) + { + X_printf("X: RandR using this CRTC\n"); + crtc = sr->crtcs[i]; + XRRFreeCrtcInfo(ci); + break; + } + XRRFreeCrtcInfo(ci); + } + if (crtc == None) { + error("X: RandR found no suitable CRTC!\n"); + *new_width = w; + *new_height = h; + return; + } + XRRCrtcInfo *ci = XRRGetCrtcInfo(display, sr, crtc); + + /* XXX: Use the first CRTC output always. In what display configuration + * could this break? */ + int output_idx = 0; + XRROutputInfo *oi = XRRGetOutputInfo(display, sr, ci->outputs[output_idx]); + + /* Find the best mode supported by this CRTC by first finding the + * closest fit that is at least as large as the DOS screen, then the + * best refresh rate at that size. To do this we have to find every mode + * available in the output, then search the screen modes for its ID. */ + int mode_id = -1; + int best_rate = 0; + for (i = 0; i < oi->nmode; i++) { + RRMode output_mode = oi->modes[i]; + int j; + for (j = 0; j < sr->nmode; j++) { + const XRRModeInfo *mi = &sr->modes[j]; + if (mi->id != output_mode) + continue; + int width = mi->width; + int height = mi->height; + X_printf("X: RandR considering mode (%d,%d)\n", width, height); + if (width >= w && height >= h && width <= nw && height <= nh) { + if (width != nw || height != nh) + best_rate = 0; /* geometry changed, start over rate search */ + nw = width; + nh = height; + int rate = mi->dotClock / (mi->hTotal * mi->vTotal); + if (rate > best_rate) { + best_rate = rate; + mode_id = mi->id; + } + } + } + } + if (mode_id == -1) { + error("X: RandR found no suitable mode for CRTC output %d!\n", output_idx); + *new_width = w; + *new_height = h; + return; + } + XRRFreeOutputInfo(oi); + + /* Change to fullscreen; save window config first. */ + X_printf("X: RandR mode asking for (%d,%d); setting %dx%d@%d mode %d\n", w, h, nw, nh, best_rate, mode_id); + X_printf("X: RandR saving old mode %d crtc %d\n", (int)ci->mode, (int)crtc); + xrandr_win_mode = ci->mode; + xrandr_win_crtc = crtc; + xrandr_win_xpos = dosemu_x; + xrandr_win_ypos = dosemu_y; + /* Position window at (0,0) on *this* CRTC. */ + dosemu_x = ci->x; + dosemu_y = ci->y; + XRRSetCrtcConfig(display, sr, crtc, CurrentTime, ci->x, ci->y, mode_id, ci->rotation, ci->outputs, ci->noutput); + XRRFreeCrtcInfo(ci); + XRRFreeScreenResources(sr); + /* Callers always set this themselves, but anyway... */ + mainwindow = fullscreenwindow; + } + } else /* Only attempt VidMode or non-modechange fullscreen if RandR is disabled. */ +#endif /* HAVE_XRANDR */ + #ifdef HAVE_XVIDMODE if (xf86vm_ok) { static XF86VidModeModeLine vidmode_modeline; @@ -2402,6 +2587,10 @@ static void X_vidmode(int w, int h, int *new_width, int *new_height) XWarpPointer(display, None, drawwindow, 0, 0, 0, 0, mx, my); *new_width = nw; *new_height = nh; + if (new_x != NULL) + *new_x = dosemu_x; + if (new_y != NULL) + *new_y = dosemu_y; } /* -- 1.9.1 |