From: Ryan Underwood <neme...@icequake.net>

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 <neme...@icequake.net>
---
 configure               |  80 +++++++++++++-
 configure.ac            |  17 +++
 src/include/config.h.in |   3 +
 src/plugin/X/X.c        | 280 ++++++++++++++++++++++++++++++++++++++++--------
 4 files changed, 332 insertions(+), 48 deletions(-)

diff --git a/configure b/configure
index 1faf59f..bfa49e0 100755
--- a/configure
+++ b/configure
@@ -746,6 +746,7 @@ with_slangdir
 with_x
 enable_mitshm
 enable_vidmode
+enable_xrandr
 enable_experimental
 enable_debug
 enable_aspi
@@ -1382,6 +1383,7 @@ Optional Features:
   --disable-dlplugins     do NOT use dynamically loaded plugins
   --disable-mitshm        do NOT use the MITSHM X extension
   --enable-vidmode        use XF86 vidmode extension
+{  --disable-xrandr        do NOT use the XRandR extension]
   --enable-experimental   enable configuration of EXPERIMENTAL stuff
   --enable-debug          compile with debug info
   --disable-aspi          do NOT compile with ASPI support
@@ -3740,7 +3742,7 @@ else
     We can't simply define LARGE_OFF_T to be 9223372036854775807,
     since some C++ compilers masquerading as C compilers
     incorrectly reject 9223372036854775807.  */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
   int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
                       && LARGE_OFF_T % 2147483647 == 1)
                      ? 1 : -1];
@@ -3786,7 +3788,7 @@ else
     We can't simply define LARGE_OFF_T to be 9223372036854775807,
     since some C++ compilers masquerading as C compilers
     incorrectly reject 9223372036854775807.  */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
   int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
                       && LARGE_OFF_T % 2147483647 == 1)
                      ? 1 : -1];
@@ -3810,7 +3812,7 @@ rm -f core conftest.err conftest.$ac_objext 
conftest.$ac_ext
     We can't simply define LARGE_OFF_T to be 9223372036854775807,
     since some C++ compilers masquerading as C compilers
     incorrectly reject 9223372036854775807.  */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
   int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
                       && LARGE_OFF_T % 2147483647 == 1)
                      ? 1 : -1];
@@ -3855,7 +3857,7 @@ else
     We can't simply define LARGE_OFF_T to be 9223372036854775807,
     since some C++ compilers masquerading as C compilers
     incorrectly reject 9223372036854775807.  */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
   int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
                       && LARGE_OFF_T % 2147483647 == 1)
                      ? 1 : -1];
@@ -3879,7 +3881,7 @@ rm -f core conftest.err conftest.$ac_objext 
conftest.$ac_ext
     We can't simply define LARGE_OFF_T to be 9223372036854775807,
     since some C++ compilers masquerading as C compilers
     incorrectly reject 9223372036854775807.  */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
   int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
                       && LARGE_OFF_T % 2147483647 == 1)
                      ? 1 : -1];
@@ -5894,6 +5896,72 @@ fi
 
     fi
 
+    # Check whether --enable-xrandr was given.
+if test "${enable_xrandr+set}" = set; then :
+  enableval=$enable_xrandr;
+fi
+
+    if test "$enable_xrandr" != "no" ; then
+      { $as_echo "$as_me:${as_lineno-$LINENO}: checking for XRRQueryVersion in 
-lXrandr" >&5
+$as_echo_n "checking for XRRQueryVersion in -lXrandr... " >&6; }
+if ${ac_cv_lib_Xrandr_XRRQueryVersion+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lXrandr  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char XRRQueryVersion ();
+int
+main ()
+{
+return XRRQueryVersion ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_Xrandr_XRRQueryVersion=yes
+else
+  ac_cv_lib_Xrandr_XRRQueryVersion=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: 
$ac_cv_lib_Xrandr_XRRQueryVersion" >&5
+$as_echo "$ac_cv_lib_Xrandr_XRRQueryVersion" >&6; }
+if test "x$ac_cv_lib_Xrandr_XRRQueryVersion" = xyes; then :
+  enable_xrandr="yes"
+else
+  enable_xrandr="no"
+fi
+
+    fi
+    if test "$enable_xrandr" = "no" ; then
+               { $as_echo "$as_me:${as_lineno-$LINENO}: Compiling without the 
XRandR extension" >&5
+$as_echo "$as_me: Compiling without the XRandR extension" >&6;}
+    else
+         ac_fn_c_check_header_compile "$LINENO" "X11/extensions/Xrandr.h" 
"ac_cv_header_X11_extensions_Xrandr_h" "#include <X11/Xlib.h>
+"
+if test "x$ac_cv_header_X11_extensions_Xrandr_h" = xyes; then :
+  $as_echo "#define HAVE_XRANDR 1" >>confdefs.h
+ X_LIBS="-lXrandr $X_LIBS"
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: Compiling without the XRandR 
extension" >&5
+$as_echo "$as_me: Compiling without the XRandR extension" >&6;}
+fi
+
+
+    fi
+
     LIBS=$SLIBS
     unset SLIBS
 
@@ -6325,6 +6393,8 @@ ac_config_commands="$ac_config_commands Makefile"
 
 
 
+
+
 cat >confcache <<\_ACEOF
 # This file is a shell script that caches the results of configure
 # tests run on this system so they can be shared between configure
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..6cb5b4b 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, &param1, &param2) &&
+      XRRQueryVersion(display, &param1, &param2))
+  {
+    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,15 @@ 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);
+    /* In fullscreen, keyboard must always be grabbed if mouse is grabbed.
+     * If mouse is grabbed but keyboard is ungrabbed, there will be no way
+     * to refocus the fullscreen dosemu if it loses focus. */
+    if (mainwindow == fullscreenwindow && grab_active)
+      toggle_mouse_grab();
   }
   X_change_config(CHG_TITLE, NULL);
 }
@@ -1247,19 +1310,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 +1397,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 +1410,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 +1424,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 +1611,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 +1714,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 +1734,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 +2140,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 +2160,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 +2280,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 +2304,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 +2394,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 +2592,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


------------------------------------------------------------------------------
New Year. New Location. New Benefits. New Data Center in Ashburn, VA.
GigeNET is offering a free month of service with a new server in Ashburn.
Choose from 2 high performing configs, both with 100TB of bandwidth.
Higher redundancy.Lower latency.Increased capacity.Completely compliant.
http://p.sf.net/sfu/gigenet
_______________________________________________
Dosemu-devel mailing list
Dosemu-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/dosemu-devel

Reply via email to