From 81694ae8f93ba1759d38a67e1fe5298918e82668 Mon Sep 17 00:00:00 2001 From: "Franc[e]sco" Date: Wed, 10 May 2017 21:27:27 +0200 Subject: [PATCH] add xinerama multi-monitor support and position window hints --- unix/configure | 26 +++++++++++ unix/configure.ac | 17 +++++++ unix/x11.cpp | 110 +++++++++++++++++++++++++++++++++++++--------- 3 files changed, 133 insertions(+), 20 deletions(-) diff --git a/unix/configure b/unix/configure index 14be08d5..7cff1766 100755 --- a/unix/configure +++ b/unix/configure @@ -719,6 +719,7 @@ enable_jma enable_screenshot with_x enable_xvideo +enable_xinerama enable_sound ' ac_precious_vars='build_alias @@ -1370,6 +1371,7 @@ Optional Features: --enable-screenshot enable screenshot support through libpng (default: yes) --enable-xvideo enable Xvideo if available (default: yes) + --enable-xinerama enable Xinerama if available (default: yes) --enable-sound enable sound if available (default: yes) Optional Packages: @@ -6265,6 +6267,29 @@ if test "x$ac_cv_header_X11_extensions_Xv_h" = xyes; then : fi +fi + +# Check if we can build with Xinerama multi-monitor support +# Check whether --enable-xinerama was given. +if test "${enable_xinerama+set}" = set; then : + enableval=$enable_xinerama; +else + enable_xinerama="yes" +fi + + +if test "x$enable_xinerama" = "xyes"; then + enable_xinerama="no" + ac_fn_cxx_check_header_mongrel "$LINENO" "X11/extensions/Xinerama.h" "ac_cv_header_X11_extensions_Xinerama_h" "$ac_includes_default" +if test "x$ac_cv_header_X11_extensions_Xinerama_h" = xyes; then : + + enable_xinerama="yes" + S9XLIBS="$S9XLIBS -lXinerama" + S9XDEFS="$S9XDEFS -DUSE_XINERAMA" + +fi + + fi # Check if we have sound code for this platform. @@ -6343,6 +6368,7 @@ libs................. $S9XLIBS features: Xvideo support....... $enable_xvideo +Xinerama support..... $enable_xinerama sound support........ $enable_sound screenshot support... $enable_screenshot netplay support...... $enable_netplay diff --git a/unix/configure.ac b/unix/configure.ac index daa68ec2..3ff749de 100644 --- a/unix/configure.ac +++ b/unix/configure.ac @@ -418,6 +418,22 @@ if test "x$enable_xvideo" = "xyes"; then ]) fi +# Check if we can build with Xinerama multi-monitor support +AC_ARG_ENABLE([xinerama], + [AS_HELP_STRING([--enable-xinerama], + [enable Xinerama if available (default: yes)])], + [], [enable_xinerama="yes"]) + +if test "x$enable_xinerama" = "xyes"; then + enable_xinerama="no" + AC_CHECK_HEADER([X11/extensions/Xinerama.h], + [ + enable_xinerama="yes" + S9XLIBS="$S9XLIBS -lXinerama" + S9XDEFS="$S9XDEFS -DUSE_XINERAMA" + ]) +fi + # Check if we have sound code for this platform. AC_ARG_ENABLE([sound], @@ -483,6 +499,7 @@ libs................. $S9XLIBS features: Xvideo support....... $enable_xvideo +Xinerama support..... $enable_xinerama sound support........ $enable_sound screenshot support... $enable_screenshot netplay support...... $enable_netplay diff --git a/unix/x11.cpp b/unix/x11.cpp index f046fcd8..5f082db4 100644 --- a/unix/x11.cpp +++ b/unix/x11.cpp @@ -212,6 +212,10 @@ #define FOURCC_YUY2 0x32595559 #endif +#ifdef USE_XINERAMA +#include +#endif + #ifdef MITSHM #include #include @@ -297,6 +301,9 @@ struct GUIData unsigned char u_table[1 << 15]; unsigned char v_table[1 << 15]; #endif +#ifdef USE_XINERAMA + uint32 xinerama_head; +#endif #ifdef MITSHM XShmSegmentInfo sm_info; bool8 use_shared_memory; @@ -361,6 +368,9 @@ void S9xExtraDisplayUsage (void) #ifdef USE_XVIDEO S9xMessage(S9X_INFO, S9X_USAGE, "-xvideo Hardware accelerated scaling"); S9xMessage(S9X_INFO, S9X_USAGE, "-maxaspect Try to fill the display, in fullscreen"); +#endif +#ifdef USE_XINERAMA + S9xMessage(S9X_INFO, S9X_USAGE, "-xineramahead Xinerama head number for multi-monitor setups"); #endif S9xMessage(S9X_INFO, S9X_USAGE, ""); S9xMessage(S9X_INFO, S9X_USAGE, "-v1 Video mode: Blocky (default)"); @@ -389,6 +399,16 @@ void S9xParseDisplayArg (char **argv, int &i, int argc) if (!strcasecmp(argv[i], "-maxaspect")) GUI.maxaspect = TRUE; else +#endif +#ifdef USE_XINERAMA + if (!strcasecmp(argv[i], "-xineramahead")) + { + if (i + 1 < argc) + GUI.xinerama_head = atoi(argv[++i]); + else + S9xUsage(); + } + else #endif if (!strncasecmp(argv[i], "-v", 2)) { @@ -553,6 +573,9 @@ const char * S9xParseDisplayConfig (ConfigFile &conf, int pass) GUI.use_xvideo = conf.GetBool("Unix/X11::Xvideo", FALSE); GUI.maxaspect = conf.GetBool("Unix/X11::MaxAspect", FALSE); #endif +#ifdef USE_XINERAMA + GUI.xinerama_head = conf.GetUInt("Unix/X11::XineramaHead", 0); +#endif if (conf.Exists("Unix/X11::VideoMode")) { @@ -853,33 +876,80 @@ void S9xInitDisplay (int argc, char **argv) attrib.background_pixel = BlackPixelOfScreen(GUI.screen); attrib.colormap = XCreateColormap(GUI.display, RootWindowOfScreen(GUI.screen), GUI.visual, AllocNone); + int screen_left = 0, screen_top = 0; + int screen_w = WidthOfScreen(GUI.screen), screen_h = HeightOfScreen(GUI.screen); + +#ifdef USE_XINERAMA + int heads = 0; + XineramaScreenInfo* si = 0; + + int useless1, useless2; + if (!XineramaQueryExtension(GUI.display, &useless1, &useless2)) { + puts("Xinerama is not available"); + goto xinerama_end; + } + + if (!XineramaIsActive(GUI.display)) { + puts("Xinerama is not active"); + goto xinerama_end; + } + + si = XineramaQueryScreens(GUI.display, &heads); + if (!si) { + puts("XineramaQueryScreens failed"); + goto xinerama_end; + } + + if (GUI.xinerama_head >= heads) { + printf("Invalid xinerama head id (expected 0-%d, got %u)\n", heads - 1, GUI.xinerama_head); + goto xinerama_end; + } + + si = &si[GUI.xinerama_head]; + screen_left = si->x_org; + screen_top = si->y_org; + screen_w = si->width; + screen_h = si->height; + + printf("Selected xinerama head %u (%d,%d %dx%d)\n", GUI.xinerama_head, screen_left, screen_top, screen_w, screen_h); + +xinerama_end: +#endif + + XSizeHints Hints; + memset((void *) &Hints, 0, sizeof(XSizeHints)); + /* Try to switch to Fullscreen. */ if (GUI.fullscreen == TRUE) { + Hints.flags = PPosition; + Hints.x = screen_left; + Hints.y = screen_top; + /* Create the window with maximum screen width,height positioned at 0,0. */ GUI.window = XCreateWindow(GUI.display, RootWindowOfScreen(GUI.screen), - 0, 0, - WidthOfScreen(GUI.screen), HeightOfScreen(GUI.screen), 0, + Hints.x, Hints.y, + screen_w, screen_h, 0, GUI.depth, InputOutput, GUI.visual, CWBackPixel | CWColormap, &attrib); #ifdef USE_XVIDEO if (GUI.use_xvideo) { // Set some defaults - GUI.scale_w = WidthOfScreen(GUI.screen); - GUI.scale_h = HeightOfScreen(GUI.screen); + GUI.scale_w = screen_w; + GUI.scale_h = screen_h; GUI.imageHeight = SNES_HEIGHT_EXTENDED * 2; if (! GUI.maxaspect) { // Compute the maximum screen size for scaling xvideo window. - double screenAspect = (double)WidthOfScreen(GUI.screen) / HeightOfScreen(GUI.screen); + double screenAspect = (double)screen_w / screen_h; double snesAspect = (double)SNES_WIDTH / SNES_HEIGHT_EXTENDED; double ratio = screenAspect / snesAspect; printf("\tScreen (%dx%d) aspect %f vs SNES (%dx%d) aspect %f (ratio: %f)\n", - WidthOfScreen(GUI.screen),HeightOfScreen(GUI.screen),screenAspect, + screen_w,screen_h,screenAspect, SNES_WIDTH,SNES_HEIGHT_EXTENDED,snesAspect, ratio); @@ -889,12 +959,12 @@ void S9xInitDisplay (int argc, char **argv) // widescreen monitor, 4:3 snes // match height, scale width GUI.scale_w /= ratio; - GUI.x_offset = (WidthOfScreen(GUI.screen) - GUI.scale_w) / 2; + GUI.x_offset = (screen_w - GUI.scale_w) / 2; } else { // narrow monitor, 4:3 snes // match width, scale height GUI.scale_h *= ratio; - GUI.y_offset = (HeightOfScreen(GUI.screen) - GUI.scale_h) / 2; + GUI.y_offset = (screen_h - GUI.scale_h) / 2; } } @@ -904,23 +974,21 @@ void S9xInitDisplay (int argc, char **argv) #endif { /* Last: position the output window in the center of the screen. */ - GUI.x_offset = (WidthOfScreen(GUI.screen) - SNES_WIDTH * 2) / 2; - GUI.y_offset = (HeightOfScreen(GUI.screen) - SNES_HEIGHT_EXTENDED * 2) / 2; + GUI.x_offset = (screen_w - SNES_WIDTH * 2) / 2; + GUI.y_offset = (screen_h - SNES_HEIGHT_EXTENDED * 2) / 2; } } else { - /* Create the window. */ - GUI.window = XCreateWindow(GUI.display, RootWindowOfScreen(GUI.screen), - (WidthOfScreen(GUI.screen) - SNES_WIDTH * 2) / 2, (HeightOfScreen(GUI.screen) - SNES_HEIGHT_EXTENDED * 2) / 2, - SNES_WIDTH * 2, SNES_HEIGHT_EXTENDED * 2, 0, GUI.depth, InputOutput, GUI.visual, CWBackPixel | CWColormap, &attrib); - /* Tell the Window Manager that we do not wish to be resizable */ - XSizeHints Hints; - memset((void *) &Hints, 0, sizeof(XSizeHints)); - - Hints.flags = PSize | PMinSize | PMaxSize; + Hints.flags = PSize | PMinSize | PMaxSize | PPosition; + Hints.x = screen_left + (screen_w - SNES_WIDTH * 2) / 2; + Hints.y = screen_top + (screen_h - SNES_HEIGHT_EXTENDED * 2) / 2; Hints.min_width = Hints.max_width = Hints.base_width = SNES_WIDTH * 2; Hints.min_height = Hints.max_height = Hints.base_height = SNES_HEIGHT_EXTENDED * 2; - XSetWMNormalHints(GUI.display, GUI.window, &Hints); + + /* Create the window. */ + GUI.window = XCreateWindow(GUI.display, RootWindowOfScreen(GUI.screen), + Hints.x, Hints.y, + SNES_WIDTH * 2, SNES_HEIGHT_EXTENDED * 2, 0, GUI.depth, InputOutput, GUI.visual, CWBackPixel | CWColormap, &attrib); /* Last: Windowed SNES is not drawn with any offsets. */ GUI.x_offset = GUI.y_offset = 0; @@ -930,6 +998,8 @@ void S9xInitDisplay (int argc, char **argv) #endif } + XSetWMNormalHints(GUI.display, GUI.window, &Hints); + /* Load UI cursors */ static XColor bg, fg; static char data[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };