From 2bf84dbf3e638054d54791a67389a50b2b8a0a9f Mon Sep 17 00:00:00 2001
From: Kerrick Staley <mail@kerrickstaley.com>
Date: Wed, 15 Feb 2012 20:56:32 -0600
Subject: [PATCH] Fix issue #146

Check if XRandR is enabled, and if it is, listen for
XRRScreenChangeNotifyEvent; when such an event is received, requery the
screen size.

Notes:
- Credit goes to Norman Rasmussen for suggesting this fix.
- I also fixed another typo I found in the code: the third argument of
  XRRSizes() gets set to the length of the xrrs list, not the number of
  screens.
- On my system, XRRScreenChangeNotifyEvent is received 3 times when
  switching to a dual-head configuration, and 2 times when switching back
  to a mirrored configuration, so it's perhaps a little inefficient to
  requery every single time.
---
 src/lib/platform/CXWindowsScreen.cpp |   31 +++++++++++++++++++++++++++++--
 src/lib/platform/CXWindowsScreen.h   |    4 ++++
 2 files changed, 33 insertions(+), 2 deletions(-)

diff --git a/src/lib/platform/CXWindowsScreen.cpp b/src/lib/platform/CXWindowsScreen.cpp
index 81841c0..dc094e9 100644
--- a/src/lib/platform/CXWindowsScreen.cpp
+++ b/src/lib/platform/CXWindowsScreen.cpp
@@ -111,6 +111,7 @@ CXWindowsScreen::CXWindowsScreen(const char* displayName, bool isPrimary, bool d
 	m_preserveFocus(false),
 	m_xkb(false),
 	m_xi2detected(false),
+	m_xrandr(false),
 	m_eventQueue(eventQueue),
 	CPlatformScreen(eventQueue)
 {
@@ -941,6 +942,19 @@ CXWindowsScreen::openDisplay(const char* displayName)
 	}
 #endif
 
+#if HAVE_X11_EXTENSIONS_XRANDR_H
+	// query for XRandR extension
+	int dummyError;
+	m_xrandr = XRRQueryExtension(display, &m_xrandrEventBase, &dummyError);
+	if (m_xrandr) {
+		// enable XRRScreenChangeNotifyEvent
+		XRRSelectInput(display, DefaultRootWindow(display), RRScreenChangeNotifyMask);
+		// In addition to RRScreenChangeNotify, there's also RRCrtcChangeNotify, which
+		// has something to do with repositioning a screen within the larger virtual
+		// screen. I don't understand what it does, so I don't handle it here.
+	}
+#endif
+
 	return display;
 }
 
@@ -958,10 +972,10 @@ CXWindowsScreen::saveShape()
 
 #if HAVE_X11_EXTENSIONS_XRANDR_H
 	if (XRRQueryExtension(m_display, &eventBase, &errorBase)){
-	  int numScreens;
+	  int numSizes;
 	  XRRScreenSize* xrrs;
 	  Rotation rotation;
-	  xrrs = XRRSizes(m_display, DefaultScreen(m_display), &numScreens);
+	  xrrs = XRRSizes(m_display, DefaultScreen(m_display), &numSizes);
 	  XRRRotations(m_display, DefaultScreen(m_display), &rotation);
 	  if (xrrs != NULL) {
 	    if (rotation & (RR_Rotate_90|RR_Rotate_270) ){
@@ -1398,6 +1412,19 @@ CXWindowsScreen::handleSystemEvent(const CEvent& event, void*)
 			}
 		}
 #endif
+
+#if HAVE_X11_EXTENSIONS_XRANDR_H
+		if (m_xrandr && xevent->type == m_xrandrEventBase + RRScreenChangeNotify) {
+			LOG((CLOG_INFO "XRRScreenChangeNotifyEvent received"));
+
+			// we're required to call back into XLib so XLib can update its internal state
+			XRRUpdateConfiguration(xevent);
+
+			// requery/recalculate the screen shape
+			saveShape();
+		}
+#endif
+
 		break;
 	}
 }
diff --git a/src/lib/platform/CXWindowsScreen.h b/src/lib/platform/CXWindowsScreen.h
index f8bf77b..f17fdb1 100644
--- a/src/lib/platform/CXWindowsScreen.h
+++ b/src/lib/platform/CXWindowsScreen.h
@@ -240,6 +240,10 @@ private:
 
 	bool				m_xi2detected;
 
+	// XRandR extension stuff
+	bool                m_xrandr;
+	int                 m_xrandrEventBase;
+
 	IEventQueue&		m_eventQueue;
 	CKeyMap				m_keyMap;
 
-- 
1.7.9.1

