|
|
|
@@ -5,31 +5,54 @@
|
|
|
|
|
#include "archutils/Unix/X11Helper.h"
|
|
|
|
|
#include "PrefsManager.h" // XXX
|
|
|
|
|
#include "RageDisplay.h" // VideoModeParams
|
|
|
|
|
#include "DisplayResolutions.h"
|
|
|
|
|
#include "DisplaySpec.h"
|
|
|
|
|
#include "LocalizedString.h"
|
|
|
|
|
|
|
|
|
|
#include "RageDisplay_OGL_Helpers.h"
|
|
|
|
|
using namespace RageDisplay_Legacy_Helpers;
|
|
|
|
|
using namespace X11Helper;
|
|
|
|
|
|
|
|
|
|
#include <stack>
|
|
|
|
|
#include <set>
|
|
|
|
|
#include <math.h> // ceil()
|
|
|
|
|
#include <GL/glxew.h>
|
|
|
|
|
#define GLX_GLXEXT_PROTOTYPES
|
|
|
|
|
#include <GL/glx.h> // All sorts of stuff...
|
|
|
|
|
#include <X11/Xlib.h>
|
|
|
|
|
#include <X11/Xatom.h>
|
|
|
|
|
#include <X11/extensions/Xrandr.h>
|
|
|
|
|
#if defined(HAVE_XINERAMA)
|
|
|
|
|
#include <X11/extensions/Xinerama.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#if defined(HAVE_LIBXTST)
|
|
|
|
|
#include <X11/extensions/XTest.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
// Display ID for treating the entire X screen as the display
|
|
|
|
|
const std::string ID_XSCREEN = "XSCREEN_RANDR";
|
|
|
|
|
|
|
|
|
|
static GLXContext g_pContext = NULL;
|
|
|
|
|
static GLXContext g_pBackgroundContext = NULL;
|
|
|
|
|
static Window g_AltWindow = None;
|
|
|
|
|
static Rotation g_OldRotation;
|
|
|
|
|
static int g_iOldSize;
|
|
|
|
|
XRRScreenConfiguration *g_pScreenConfig = NULL;
|
|
|
|
|
static bool g_bChangedScreenSize = false;
|
|
|
|
|
static SizeID g_iOldSize = None;
|
|
|
|
|
static Rotation g_OldRotation = RR_Rotate_0;
|
|
|
|
|
static XRRScreenConfiguration *g_pScreenConfig = nullptr;
|
|
|
|
|
static RRMode g_originalRandRMode = None;
|
|
|
|
|
static RROutput g_usedCrtc = None;
|
|
|
|
|
static int g_iRandRVerMinor = 0;
|
|
|
|
|
static int g_iRandRVerMajor = 0;
|
|
|
|
|
static bool g_bUseXRandR12 = false;
|
|
|
|
|
static bool g_bUseXinerama = false;
|
|
|
|
|
|
|
|
|
|
inline float calcRandRRefresh( unsigned long iPixelClock, int iHTotal, int iVTotal )
|
|
|
|
|
{
|
|
|
|
|
// Pixel Clock divided by total pixels in mode,
|
|
|
|
|
// not just those onscreen!
|
|
|
|
|
return ( iPixelClock ) / ( iHTotal * iVTotal );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool NetWMSupported(Display *Dpy, Atom feature);
|
|
|
|
|
|
|
|
|
|
static LocalizedString FAILED_CONNECTION_XSERVER( "LowLevelWindow_X11", "Failed to establish a connection with the X server" );
|
|
|
|
|
LowLevelWindow_X11::LowLevelWindow_X11()
|
|
|
|
@@ -37,6 +60,18 @@ LowLevelWindow_X11::LowLevelWindow_X11()
|
|
|
|
|
if( !OpenXConnection() )
|
|
|
|
|
RageException::Throw( "%s", FAILED_CONNECTION_XSERVER.GetValue().c_str() );
|
|
|
|
|
|
|
|
|
|
if( XRRQueryVersion( Dpy, &g_iRandRVerMajor, &g_iRandRVerMinor ) && g_iRandRVerMajor >= 1 && g_iRandRVerMinor >= 2) g_bUseXRandR12 = true;
|
|
|
|
|
#ifdef HAVE_XINERAMA
|
|
|
|
|
int xinerama_event_base = 0;
|
|
|
|
|
int xinerama_error_base = 0;
|
|
|
|
|
Atom fullscreen_monitors = XInternAtom( Dpy, "_NET_WM_FULLSCREEN_MONITORS", False );
|
|
|
|
|
if (XineramaQueryExtension( Dpy, &xinerama_event_base, &xinerama_error_base ) &&
|
|
|
|
|
NetWMSupported( Dpy, fullscreen_monitors ))
|
|
|
|
|
{
|
|
|
|
|
g_bUseXinerama = true;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
const int iScreen = DefaultScreen( Dpy );
|
|
|
|
|
int iXServerVersion = XVendorRelease( Dpy ); /* eg. 40201001 */
|
|
|
|
|
int iMajor = iXServerVersion / 10000000; iXServerVersion %= 10000000;
|
|
|
|
@@ -50,7 +85,6 @@ LowLevelWindow_X11::LowLevelWindow_X11()
|
|
|
|
|
LOG->Info( "Client GLX vendor: %s [%s]", glXGetClientString( Dpy, GLX_VENDOR ), glXGetClientString( Dpy, GLX_VERSION ) );
|
|
|
|
|
m_bWasWindowed = true;
|
|
|
|
|
g_pScreenConfig = XRRGetScreenInfo( Dpy, RootWindow(Dpy, DefaultScreen(Dpy)) );
|
|
|
|
|
g_iOldSize = XRRConfigCurrentConfiguration( g_pScreenConfig, &g_OldRotation );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LowLevelWindow_X11::~LowLevelWindow_X11()
|
|
|
|
@@ -58,8 +92,7 @@ LowLevelWindow_X11::~LowLevelWindow_X11()
|
|
|
|
|
// Reset the display
|
|
|
|
|
if( !m_bWasWindowed )
|
|
|
|
|
{
|
|
|
|
|
XRRSetScreenConfig( Dpy, g_pScreenConfig, RootWindow(Dpy, DefaultScreen(Dpy)), g_iOldSize, g_OldRotation, CurrentTime );
|
|
|
|
|
|
|
|
|
|
RestoreOutputConfig();
|
|
|
|
|
XUngrabKeyboard( Dpy, CurrentTime );
|
|
|
|
|
}
|
|
|
|
|
if( g_pContext )
|
|
|
|
@@ -72,9 +105,6 @@ LowLevelWindow_X11::~LowLevelWindow_X11()
|
|
|
|
|
glXDestroyContext( Dpy, g_pBackgroundContext );
|
|
|
|
|
g_pBackgroundContext = NULL;
|
|
|
|
|
}
|
|
|
|
|
XRRFreeScreenConfigInfo( g_pScreenConfig );
|
|
|
|
|
g_pScreenConfig = NULL;
|
|
|
|
|
|
|
|
|
|
XDestroyWindow( Dpy, Win );
|
|
|
|
|
Win = None;
|
|
|
|
|
XDestroyWindow( Dpy, g_AltWindow );
|
|
|
|
@@ -82,6 +112,29 @@ LowLevelWindow_X11::~LowLevelWindow_X11()
|
|
|
|
|
CloseXConnection();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Restore saved X screen/CRTC configuration
|
|
|
|
|
*/
|
|
|
|
|
void LowLevelWindow_X11::RestoreOutputConfig() {
|
|
|
|
|
if (g_bChangedScreenSize) {
|
|
|
|
|
XRRSetScreenConfig(Dpy, g_pScreenConfig, RootWindow(Dpy, DefaultScreen(Dpy)), g_iOldSize, g_OldRotation,
|
|
|
|
|
CurrentTime);
|
|
|
|
|
}
|
|
|
|
|
if (g_usedCrtc != None) {
|
|
|
|
|
ASSERT(g_bUseXRandR12);
|
|
|
|
|
XRRScreenResources *res = XRRGetScreenResources(Dpy, Win);
|
|
|
|
|
XRRCrtcInfo *conf = XRRGetCrtcInfo(Dpy, res, g_usedCrtc);
|
|
|
|
|
XRRSetCrtcConfig(Dpy, res, g_usedCrtc, conf->timestamp, conf->x, conf->y, g_originalRandRMode, conf->rotation,
|
|
|
|
|
conf->outputs, conf->noutput);
|
|
|
|
|
XRRFreeScreenResources(res);
|
|
|
|
|
XRRFreeCrtcInfo(conf);
|
|
|
|
|
}
|
|
|
|
|
g_iOldSize = None;
|
|
|
|
|
g_bChangedScreenSize = false;
|
|
|
|
|
g_usedCrtc = None;
|
|
|
|
|
g_OldRotation = RR_Rotate_0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void *LowLevelWindow_X11::GetProcAddress( RString s )
|
|
|
|
|
{
|
|
|
|
|
// XXX: We should check whether glXGetProcAddress or
|
|
|
|
@@ -92,19 +145,20 @@ void *LowLevelWindow_X11::GetProcAddress( RString s )
|
|
|
|
|
|
|
|
|
|
RString LowLevelWindow_X11::TryVideoMode( const VideoModeParams &p, bool &bNewDeviceOut )
|
|
|
|
|
{
|
|
|
|
|
#if defined(UNIX)
|
|
|
|
|
/* nVidia cards:
|
|
|
|
|
* This only works the first time we set up a window; after that, the
|
|
|
|
|
* drivers appear to cache the value, so you have to actually restart
|
|
|
|
|
* the program to change it again. */
|
|
|
|
|
static char buf[128];
|
|
|
|
|
strcpy( buf, "__GL_SYNC_TO_VBLANK=" );
|
|
|
|
|
strcat( buf, p.vsync?"1":"0" );
|
|
|
|
|
putenv( buf );
|
|
|
|
|
#endif
|
|
|
|
|
// We're going to be interested in MapNotify/ConfigureNotify events in this routine,
|
|
|
|
|
// so ensure our event mask includes these, restore it on exit
|
|
|
|
|
XWindowAttributes winAttrib;
|
|
|
|
|
auto restore = [&](XWindowAttributes *attr) { XSelectInput( Dpy, Win, attr->your_event_mask );};
|
|
|
|
|
auto restoreAttrib = std::unique_ptr<XWindowAttributes, decltype(restore)>(&winAttrib, restore);
|
|
|
|
|
|
|
|
|
|
// These might change if we're rendering at different resolution than window
|
|
|
|
|
int windowWidth = p.width;
|
|
|
|
|
int windowHeight = p.height;
|
|
|
|
|
bool renderOffscreen = false;
|
|
|
|
|
|
|
|
|
|
if( g_pContext == NULL || p.bpp != CurrentParams.bpp || m_bWasWindowed != p.windowed )
|
|
|
|
|
{
|
|
|
|
|
bool bFirstRun = g_pContext == NULL;
|
|
|
|
|
// Different depth, or we didn't make a window before. New context.
|
|
|
|
|
bNewDeviceOut = true;
|
|
|
|
|
|
|
|
|
@@ -156,128 +210,377 @@ RString LowLevelWindow_X11::TryVideoMode( const VideoModeParams &p, bool &bNewDe
|
|
|
|
|
|
|
|
|
|
glXMakeCurrent( Dpy, Win, g_pContext );
|
|
|
|
|
|
|
|
|
|
// Map the window, ensuring we get the MapNotify event
|
|
|
|
|
XWindowAttributes winAttrib;
|
|
|
|
|
XGetWindowAttributes( Dpy, Win, &winAttrib );
|
|
|
|
|
XSelectInput( Dpy, Win, winAttrib.your_event_mask | StructureNotifyMask );
|
|
|
|
|
XSelectInput( Dpy, Win, winAttrib.your_event_mask | StructureNotifyMask | PropertyChangeMask );
|
|
|
|
|
|
|
|
|
|
XMapWindow( Dpy, Win );
|
|
|
|
|
|
|
|
|
|
// Wait until we actually have a mapped window before trying to
|
|
|
|
|
// use it!
|
|
|
|
|
XEvent event;
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
XNextEvent( Dpy, &event );
|
|
|
|
|
} while (event.type != MapNotify);
|
|
|
|
|
XEvent ev;
|
|
|
|
|
do {XWindowEvent( Dpy, Win, StructureNotifyMask, &ev );}
|
|
|
|
|
while ( ev.type != MapNotify);
|
|
|
|
|
|
|
|
|
|
// Set the event mask back to what it was
|
|
|
|
|
XSelectInput( Dpy, Win, winAttrib.your_event_mask );
|
|
|
|
|
// I can't find official docs saying what happens if you re-init GLEW.
|
|
|
|
|
// I'll just assume the behavior is undefined.
|
|
|
|
|
if(bFirstRun)
|
|
|
|
|
{
|
|
|
|
|
GLenum err = glewInit();
|
|
|
|
|
ASSERT( err == GLEW_OK );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// We're remodeling the existing window, and not touching the context.
|
|
|
|
|
bNewDeviceOut = false;
|
|
|
|
|
|
|
|
|
|
XGetWindowAttributes( Dpy, Win, &winAttrib );
|
|
|
|
|
XSelectInput( Dpy, Win, winAttrib.your_event_mask | StructureNotifyMask | PropertyChangeMask );
|
|
|
|
|
|
|
|
|
|
if( !p.windowed )
|
|
|
|
|
{
|
|
|
|
|
// X11 is an asynchronous beast. If we're resizing an existing
|
|
|
|
|
// window directly (i.e. override-redirect as opposed to asking the
|
|
|
|
|
// WM to do it) and don't wait for the window to actually be
|
|
|
|
|
// resized, we'll get unexpected results from glViewport() etc. I
|
|
|
|
|
// don't know why, or why it *doesn't* break in the slower process
|
|
|
|
|
// of waiting for the WM to resize the window.
|
|
|
|
|
|
|
|
|
|
// So, set the event mask so we're notified when the window is resized...
|
|
|
|
|
// Send the resize command...
|
|
|
|
|
XResizeWindow( Dpy, Win, static_cast<unsigned int> (p.width), static_cast<unsigned int> (p.height) );
|
|
|
|
|
|
|
|
|
|
// We'll wait for the notification once we've done everything else,
|
|
|
|
|
// to save time.
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float rate = 60; // Will be unchanged if windowed. Not sure I care.
|
|
|
|
|
|
|
|
|
|
if( !p.windowed )
|
|
|
|
|
{
|
|
|
|
|
if( m_bWasWindowed )
|
|
|
|
|
{
|
|
|
|
|
RestoreOutputConfig();
|
|
|
|
|
|
|
|
|
|
if (p.sDisplayId == ID_XSCREEN || p.sDisplayId.empty()) {
|
|
|
|
|
// If the user changed the resolution while StepMania was windowed we overwrite the resolution to restore with it at exit.
|
|
|
|
|
g_iOldSize = XRRConfigCurrentConfiguration( g_pScreenConfig, &g_OldRotation );
|
|
|
|
|
m_bWasWindowed = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Find a matching mode.
|
|
|
|
|
int iSizesXct;
|
|
|
|
|
XRRScreenSize *pSizesX = XRRSizes( Dpy, DefaultScreen(Dpy), &iSizesXct );
|
|
|
|
|
ASSERT_M( iSizesXct != 0, "Couldn't get resolution list from X server" );
|
|
|
|
|
// Find a matching mode.
|
|
|
|
|
int iSizesXct;
|
|
|
|
|
XRRScreenSize *pSizesX = XRRSizes( Dpy, DefaultScreen(Dpy), &iSizesXct );
|
|
|
|
|
ASSERT_M( iSizesXct != 0, "Couldn't get resolution list from X server" );
|
|
|
|
|
|
|
|
|
|
int iSizeMatch = -1;
|
|
|
|
|
int iSizeMatch = -1;
|
|
|
|
|
|
|
|
|
|
for( int i = 0; i < iSizesXct; ++i )
|
|
|
|
|
{
|
|
|
|
|
if( pSizesX[i].width == p.width && pSizesX[i].height == p.height )
|
|
|
|
|
{
|
|
|
|
|
iSizeMatch = i;
|
|
|
|
|
break;
|
|
|
|
|
for (int i = 0; i < iSizesXct; ++i) {
|
|
|
|
|
if (pSizesX[i].width == p.width && pSizesX[i].height == p.height) {
|
|
|
|
|
iSizeMatch = i;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (iSizeMatch != g_iOldSize) {
|
|
|
|
|
g_bChangedScreenSize = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Set this mode.
|
|
|
|
|
// XXX: This doesn't handle if the config has changed since we queried it (see man Xrandr)
|
|
|
|
|
XRRSetScreenConfig( Dpy, g_pScreenConfig, RootWindow(Dpy, DefaultScreen(Dpy)), iSizeMatch, 1, CurrentTime );
|
|
|
|
|
// Set this mode.
|
|
|
|
|
// XXX: This doesn't handle if the config has changed since we queried it (see man Xrandr)
|
|
|
|
|
Status s = XRRSetScreenConfig( Dpy, g_pScreenConfig, RootWindow(Dpy, DefaultScreen(Dpy)), iSizeMatch, 1, CurrentTime );
|
|
|
|
|
if (s)
|
|
|
|
|
{
|
|
|
|
|
return "Failed to set screen config";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
XMoveWindow( Dpy, Win, 0, 0 );
|
|
|
|
|
|
|
|
|
|
XRaiseWindow( Dpy, Win );
|
|
|
|
|
|
|
|
|
|
// We want to prevent the WM from catching anything that comes from the keyboard.
|
|
|
|
|
// We should do this every time on fullscreen and not only we entering from windowed mode because we could lose focus at resolution change and that will leave the user input locked.
|
|
|
|
|
while (XGrabKeyboard( Dpy, Win, True, GrabModeAsync, GrabModeAsync, CurrentTime ));
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
ASSERT(g_bUseXRandR12);
|
|
|
|
|
/* === Configuring a specific CRTC === */
|
|
|
|
|
// Arcane and undocumented but PROPER XRandR 1.2 method.
|
|
|
|
|
// What we do is directly reconfigure the CRTC of the primary display,
|
|
|
|
|
// Which prevents the (RandR) screen itself from resizing, and therefore
|
|
|
|
|
// leaving user's desktop unmolested.
|
|
|
|
|
LOG->Info("LowLevelWindow_X11: Using XRandR");
|
|
|
|
|
|
|
|
|
|
XRRScreenResources *scrRes = XRRGetScreenResources(Dpy, Win);
|
|
|
|
|
ASSERT(scrRes != NULL);
|
|
|
|
|
ASSERT(scrRes->ncrtc > 0);
|
|
|
|
|
ASSERT(scrRes->noutput > 0);
|
|
|
|
|
ASSERT(scrRes->nmode > 0);
|
|
|
|
|
|
|
|
|
|
// If an output name has been specified, search for it
|
|
|
|
|
RROutput targetOut = None;
|
|
|
|
|
if (p.sDisplayId.length() > 0) {
|
|
|
|
|
for (unsigned int i = 0; i < scrRes->noutput && targetOut == None; ++i) {
|
|
|
|
|
XRROutputInfo *outInfo = XRRGetOutputInfo(Dpy, scrRes, scrRes->outputs[i]);
|
|
|
|
|
std::string outName = std::string(outInfo->name, static_cast<unsigned int> (outInfo->nameLen));
|
|
|
|
|
if (p.sDisplayId == outName) {
|
|
|
|
|
targetOut = scrRes->outputs[i];
|
|
|
|
|
}
|
|
|
|
|
XRRFreeOutputInfo(outInfo);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (targetOut == None) {
|
|
|
|
|
LOG->Info("Did not find display output %s, trying another", p.sDisplayId.c_str());
|
|
|
|
|
// didn't find named output, pick primary/or at least one that works
|
|
|
|
|
if (g_iRandRVerMajor >= 1 && g_iRandRVerMinor >= 3) {
|
|
|
|
|
// RandR 1.3 can tell us what the primary display is.
|
|
|
|
|
targetOut = XRRGetOutputPrimary(Dpy, Win);
|
|
|
|
|
} else {
|
|
|
|
|
// Only RandR 1.2. We'll look for a "Connected" output, or if we can't find that,
|
|
|
|
|
// (it is possible the connection state could be unknown), we'll at least
|
|
|
|
|
// look for an output with a CRTC driving it
|
|
|
|
|
RROutput connected = None, hasCrtc = None;
|
|
|
|
|
for (unsigned int i = 0; i < scrRes->noutput; ++i) {
|
|
|
|
|
XRROutputInfo *outInfo = XRRGetOutputInfo(Dpy, scrRes, scrRes->outputs[i]);
|
|
|
|
|
if (outInfo->connection == RR_Connected) { // Check for CONNECTED state: Connected == 0
|
|
|
|
|
connected = scrRes->outputs[i];
|
|
|
|
|
}
|
|
|
|
|
if (outInfo->crtc != None) {
|
|
|
|
|
hasCrtc = outInfo->crtc;
|
|
|
|
|
}
|
|
|
|
|
XRRFreeOutputInfo(outInfo);
|
|
|
|
|
}
|
|
|
|
|
targetOut = connected != None ? connected : hasCrtc;
|
|
|
|
|
ASSERT(targetOut != None);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// if the target output is not currently being driven by a crtc,
|
|
|
|
|
// find an unused crtc that can be connected to it
|
|
|
|
|
XRROutputInfo *tgtOutInfo = XRRGetOutputInfo( Dpy, scrRes, targetOut );
|
|
|
|
|
if (tgtOutInfo == NULL)
|
|
|
|
|
{
|
|
|
|
|
XRRFreeScreenResources(scrRes);
|
|
|
|
|
return "Failed to find XRROutput";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RRCrtc tgtOutCrtc = tgtOutInfo->crtc;
|
|
|
|
|
if (tgtOutCrtc == None)
|
|
|
|
|
{
|
|
|
|
|
for (unsigned int i = 0; i < tgtOutInfo->ncrtc; ++i)
|
|
|
|
|
{
|
|
|
|
|
XRRCrtcInfo *crtcInfo = XRRGetCrtcInfo( Dpy, scrRes, tgtOutInfo->crtcs[i] );
|
|
|
|
|
if (crtcInfo->mode == None)
|
|
|
|
|
{
|
|
|
|
|
tgtOutCrtc = tgtOutInfo->crtcs[i];
|
|
|
|
|
}
|
|
|
|
|
XRRFreeCrtcInfo( crtcInfo );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ASSERT(tgtOutCrtc != None);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
XRRCrtcInfo *oldConf = XRRGetCrtcInfo( Dpy, scrRes, tgtOutCrtc );
|
|
|
|
|
|
|
|
|
|
float fRefreshDiff = 99999;
|
|
|
|
|
float fRefreshRate = 0;
|
|
|
|
|
RRMode mode = None;
|
|
|
|
|
// A quirk of XRandR is that the width and height are as the display
|
|
|
|
|
// controller ("CRTC") sees it, which means height and width are
|
|
|
|
|
// flipped if there's rotation going on.
|
|
|
|
|
const bool bPortrait = (oldConf->rotation & (RR_Rotate_90 | RR_Rotate_270)) != 0;
|
|
|
|
|
// Find a mode that matches our exact wanted resolution,
|
|
|
|
|
// with as close to our desired refresh rate as possible.
|
|
|
|
|
for (int i = 0; i < scrRes->nmode; i++) {
|
|
|
|
|
const XRRModeInfo &thisMI = scrRes->modes[i];
|
|
|
|
|
const unsigned int modeWidth = bPortrait ? thisMI.height : thisMI.width;
|
|
|
|
|
const unsigned int modeHeight = bPortrait ? thisMI.width : thisMI.height;
|
|
|
|
|
if (modeWidth == p.width && modeHeight == p.height) {
|
|
|
|
|
float fTempRefresh = calcRandRRefresh(thisMI.dotClock, thisMI.hTotal, thisMI.vTotal);
|
|
|
|
|
float fTempDiff = std::abs(p.rate - fTempRefresh);
|
|
|
|
|
if ((p.rate != REFRESH_DEFAULT && fTempDiff < fRefreshDiff) ||
|
|
|
|
|
(p.rate == REFRESH_DEFAULT && fTempRefresh > fRefreshRate)) {
|
|
|
|
|
int j;
|
|
|
|
|
// Ensure that the output supports the mode
|
|
|
|
|
for (j = 0; j < tgtOutInfo->nmode; j++)
|
|
|
|
|
if (tgtOutInfo->modes[j] == scrRes->modes[i].id) {
|
|
|
|
|
mode = tgtOutInfo->modes[j];
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (j < tgtOutInfo->nmode) {
|
|
|
|
|
fRefreshRate = fTempRefresh;
|
|
|
|
|
fRefreshDiff = fTempDiff;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
rate = roundf(fRefreshRate);
|
|
|
|
|
|
|
|
|
|
g_usedCrtc = tgtOutCrtc;
|
|
|
|
|
g_originalRandRMode = oldConf->mode;
|
|
|
|
|
|
|
|
|
|
const std::string tgtOutName = std::string(tgtOutInfo->name, static_cast<unsigned int> (tgtOutInfo->nameLen));
|
|
|
|
|
LOG->Info("XRandR output config using CRTC %lu in mode %lu, driving output %s",
|
|
|
|
|
g_usedCrtc, mode, tgtOutName.c_str());
|
|
|
|
|
// and FIRE!
|
|
|
|
|
Status s = XRRSetCrtcConfig(Dpy, scrRes, g_usedCrtc, oldConf->timestamp, oldConf->x, oldConf->y, mode,
|
|
|
|
|
oldConf->rotation, oldConf->outputs, oldConf->noutput);
|
|
|
|
|
if (s) {
|
|
|
|
|
XRRFreeCrtcInfo(oldConf);
|
|
|
|
|
XRRFreeOutputInfo(tgtOutInfo);
|
|
|
|
|
XRRFreeScreenResources(scrRes);
|
|
|
|
|
return "Failed to set CRTC config";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We don't move to absolute 0,0 because that may be in the area of a different output.
|
|
|
|
|
// Instead we preserved the corner of our CRTC; go to that.
|
|
|
|
|
XMoveWindow(Dpy, Win, oldConf->x, oldConf->y);
|
|
|
|
|
|
|
|
|
|
// Final cleanup
|
|
|
|
|
XRRFreeCrtcInfo(oldConf);
|
|
|
|
|
XRRFreeOutputInfo(tgtOutInfo);
|
|
|
|
|
XRRFreeScreenResources(scrRes);
|
|
|
|
|
}
|
|
|
|
|
m_bWasWindowed = false;
|
|
|
|
|
|
|
|
|
|
XRaiseWindow( Dpy, Win );
|
|
|
|
|
|
|
|
|
|
// We want to prevent the WM from catching anything that comes from the keyboard.
|
|
|
|
|
// We should do this every time on fullscreen and not only we entering from windowed mode because we could lose focus at resolution change and that will leave the user input locked.
|
|
|
|
|
XGrabKeyboard( Dpy, Win, True, GrabModeAsync, GrabModeAsync, CurrentTime );
|
|
|
|
|
while (XGrabKeyboard( Dpy, Win, True, GrabModeAsync, GrabModeAsync, CurrentTime ));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
else // if(p.windowed)
|
|
|
|
|
{
|
|
|
|
|
if( !m_bWasWindowed )
|
|
|
|
|
{
|
|
|
|
|
XRRSetScreenConfig( Dpy, g_pScreenConfig, RootWindow(Dpy, DefaultScreen(Dpy)), g_iOldSize, g_OldRotation, CurrentTime );
|
|
|
|
|
// In windowed mode, we actually want the WM to function normally.
|
|
|
|
|
// Release any previous grab.
|
|
|
|
|
// Return the display to the mode it was in before we fullscreened.
|
|
|
|
|
RestoreOutputConfig();
|
|
|
|
|
XUngrabKeyboard( Dpy, CurrentTime );
|
|
|
|
|
m_bWasWindowed = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Atom net_wm_state = XInternAtom( Dpy, "_NET_WM_STATE", False );
|
|
|
|
|
Atom fullscreen_state = XInternAtom( Dpy, "_NET_WM_STATE_FULLSCREEN", False );
|
|
|
|
|
Atom maximized_vert = XInternAtom( Dpy, "_NET_WM_STATE_MAXIMIZED_VERT", False );
|
|
|
|
|
Atom maximized_horz = XInternAtom( Dpy, "_NET_WM_STATE_MAXIMIZED_HORZ", False );
|
|
|
|
|
// if FSBW, find matching monitor, move window to its origin,
|
|
|
|
|
// then set fullscreen hint, and set the CurrentParams.outWidth, CurrentParams.outHeight to the values of that display
|
|
|
|
|
// otherwise set the size hints and disable MAXIMIZED_*
|
|
|
|
|
if (p.bWindowIsFullscreenBorderless)
|
|
|
|
|
{
|
|
|
|
|
auto specs = DisplaySpecs{};
|
|
|
|
|
GetDisplaySpecs( specs );
|
|
|
|
|
auto target = std::find_if( specs.begin(), specs.end(), [&]( const DisplaySpec &spec ) {
|
|
|
|
|
return p.sDisplayId == spec.id() && spec.currentMode() != nullptr;
|
|
|
|
|
} );
|
|
|
|
|
// If we didn't find a matching DisplaySpec for the requested ID, pick the first one with a current mode
|
|
|
|
|
if (target == specs.end())
|
|
|
|
|
{
|
|
|
|
|
target = std::find_if( specs.begin(), specs.end(), [&]( const DisplaySpec &spec ) {
|
|
|
|
|
return spec.currentMode() != nullptr;
|
|
|
|
|
} );
|
|
|
|
|
}
|
|
|
|
|
// If we _still_ haven't found anything (unlikely), then just give up
|
|
|
|
|
if (target == specs.end())
|
|
|
|
|
{
|
|
|
|
|
return "Unable to find destination monitor for fullscreen borderless";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
windowWidth = target->currentMode()->width;
|
|
|
|
|
windowHeight = target->currentMode()->height;
|
|
|
|
|
|
|
|
|
|
if (windowWidth != p.width || windowHeight != p.height)
|
|
|
|
|
{
|
|
|
|
|
renderOffscreen = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Reset anything that might've been set previously:
|
|
|
|
|
// (1) Undo Min/Max size bounds
|
|
|
|
|
// (2) Remove FULLSCREEN/MAXIMIZED_{HORIZ,VERT} hints
|
|
|
|
|
// Without doing this, WM may not let us move/resize window to new display
|
|
|
|
|
// Give Window manager the chance to react to changes (otherwise, Mutter had problems
|
|
|
|
|
// properly reacting to moving a _NET_WM_STATE_FULLSCREEN window to a different output
|
|
|
|
|
// and fullscreen resetting FULLSCREEN hint.
|
|
|
|
|
XSizeHints hints;
|
|
|
|
|
hints.flags = 0;
|
|
|
|
|
XSetWMNormalHints( Dpy, Win, &hints );
|
|
|
|
|
#if defined(HAVE_XINERAMA)
|
|
|
|
|
if (!g_bUseXinerama || !SetWMFullscreenMonitors( *target ))
|
|
|
|
|
#endif
|
|
|
|
|
{
|
|
|
|
|
SetWMState( winAttrib.root, Win, 0, maximized_horz );
|
|
|
|
|
SetWMState( winAttrib.root, Win, 0, maximized_vert );
|
|
|
|
|
SetWMState( winAttrib.root, Win, 0, fullscreen_state );
|
|
|
|
|
|
|
|
|
|
XFlush( Dpy );
|
|
|
|
|
XResizeWindow( Dpy, Win, static_cast<unsigned int> (windowWidth), static_cast<unsigned int> (windowHeight) );
|
|
|
|
|
XMoveWindow( Dpy, Win, target->currentBounds().left, target->currentBounds().top );
|
|
|
|
|
XRaiseWindow( Dpy, Win );
|
|
|
|
|
|
|
|
|
|
SetWMState( winAttrib.root, Win, 1, fullscreen_state );
|
|
|
|
|
SetWMState( winAttrib.root, Win, 1, maximized_horz );
|
|
|
|
|
SetWMState( winAttrib.root, Win, 1, maximized_vert );
|
|
|
|
|
}
|
|
|
|
|
} else
|
|
|
|
|
{
|
|
|
|
|
windowWidth = p.width;
|
|
|
|
|
windowHeight = p.height;
|
|
|
|
|
|
|
|
|
|
SetWMState( winAttrib.root, Win, 0, fullscreen_state );
|
|
|
|
|
// Make a window fixed size, don't let resize it or maximize it.
|
|
|
|
|
// Do this before resizing the window so that pane-style WMs (Ion,
|
|
|
|
|
// ratpoison) don't resize us back inappropriately.
|
|
|
|
|
{
|
|
|
|
|
XSizeHints hints;
|
|
|
|
|
|
|
|
|
|
hints.flags = PMinSize|PMaxSize|PWinGravity;
|
|
|
|
|
hints.min_width = hints.max_width = windowWidth;
|
|
|
|
|
hints.min_height = hints.max_height = windowHeight;
|
|
|
|
|
hints.win_gravity = CenterGravity;
|
|
|
|
|
|
|
|
|
|
XSetWMNormalHints( Dpy, Win, &hints );
|
|
|
|
|
}
|
|
|
|
|
/* Workaround for metacity and compiz: if the window have the same
|
|
|
|
|
* resolution or higher than the screen, it gets automaximized even
|
|
|
|
|
* when the window is set to not let it happen. This happens when
|
|
|
|
|
* changing from fullscreen to window mode and our screen resolution
|
|
|
|
|
* is bigger. */
|
|
|
|
|
{
|
|
|
|
|
SetWMState( winAttrib.root, Win, 1, maximized_vert );
|
|
|
|
|
SetWMState( winAttrib.root, Win, 1, maximized_horz );
|
|
|
|
|
|
|
|
|
|
// This one is needed for compiz, if the window reaches out of bounds of the screen it becames destroyed, only the window, the program is left running.
|
|
|
|
|
// Commented out per the patch at http://ssc.ajworld.net/sm-ssc/bugtracker/view.php?id=398
|
|
|
|
|
//XMoveWindow( Dpy, Win, 0, 0 );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
// NOTE: nVidia's implementation of this is broken by default.
|
|
|
|
|
// The only ways around this are mucking with xorg.conf or querying
|
|
|
|
|
// nvidia-settings with "$ nvidia-settings -t -q RefreshRate".
|
|
|
|
|
int rate = XRRConfigCurrentRate( g_pScreenConfig );
|
|
|
|
|
|
|
|
|
|
// Make a window fixed size, don't let resize it or maximize it.
|
|
|
|
|
// Do this before resizing the window so that pane-style WMs (Ion,
|
|
|
|
|
// ratpoison) don't resize us back inappropriately.
|
|
|
|
|
{
|
|
|
|
|
XSizeHints hints;
|
|
|
|
|
|
|
|
|
|
hints.flags = PMinSize|PMaxSize|PWinGravity;
|
|
|
|
|
hints.min_width = hints.max_width = p.width;
|
|
|
|
|
hints.min_height = hints.max_height = p.height;
|
|
|
|
|
hints.win_gravity = CenterGravity;
|
|
|
|
|
|
|
|
|
|
XSetWMNormalHints( Dpy, Win, &hints );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Workaround for metacity and compiz: if the window have the same
|
|
|
|
|
* resolution or higher than the screen, it gets automaximized even
|
|
|
|
|
* when the window is set to not let it happen. This happens when
|
|
|
|
|
* changing from fullscreen to window mode and our screen resolution
|
|
|
|
|
* is bigger. */
|
|
|
|
|
{
|
|
|
|
|
XEvent xev;
|
|
|
|
|
Atom wm_state = XInternAtom(Dpy, "_NET_WM_STATE", False);
|
|
|
|
|
Atom maximized_vert = XInternAtom(Dpy, "_NET_WM_STATE_MAXIMIZED_VERT", False);
|
|
|
|
|
Atom maximized_horz = XInternAtom(Dpy, "_NET_WM_STATE_MAXIMIZED_HORZ", False);
|
|
|
|
|
|
|
|
|
|
memset(&xev, 0, sizeof(xev));
|
|
|
|
|
xev.type = ClientMessage;
|
|
|
|
|
xev.xclient.window = Win;
|
|
|
|
|
xev.xclient.message_type = wm_state;
|
|
|
|
|
xev.xclient.format = 32;
|
|
|
|
|
xev.xclient.data.l[0] = 1;
|
|
|
|
|
xev.xclient.data.l[1] = maximized_vert;
|
|
|
|
|
xev.xclient.data.l[2] = 0;
|
|
|
|
|
|
|
|
|
|
XSendEvent(Dpy, DefaultRootWindow(Dpy), False, SubstructureNotifyMask, &xev);
|
|
|
|
|
xev.xclient.data.l[1] = maximized_horz;
|
|
|
|
|
XSendEvent(Dpy, DefaultRootWindow(Dpy), False, SubstructureNotifyMask, &xev);
|
|
|
|
|
|
|
|
|
|
// This one is needed for compiz, if the window reaches out of bounds of the screen it becames destroyed, only the window, the program is left running.
|
|
|
|
|
// Commented out per the patch at http://ssc.ajworld.net/sm-ssc/bugtracker/view.php?id=398
|
|
|
|
|
//XMoveWindow( Dpy, Win, 0, 0 );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Resize the window.
|
|
|
|
|
XResizeWindow( Dpy, Win, p.width, p.height );
|
|
|
|
|
|
|
|
|
|
CurrentParams = p;
|
|
|
|
|
CurrentParams.rate = rate;
|
|
|
|
|
CurrentParams.windowWidth = windowWidth;
|
|
|
|
|
CurrentParams.windowHeight = windowHeight;
|
|
|
|
|
CurrentParams.renderOffscreen = renderOffscreen;
|
|
|
|
|
ASSERT( rate > 0 );
|
|
|
|
|
CurrentParams.rate = static_cast<int> (roundf(rate));
|
|
|
|
|
|
|
|
|
|
if (!p.windowed)
|
|
|
|
|
{
|
|
|
|
|
// Set our V-sync hint.
|
|
|
|
|
if (GLXEW_EXT_swap_control) // I haven't seen this actually implemented yet, but why not.
|
|
|
|
|
glXSwapIntervalEXT( Dpy, Win, CurrentParams.vsync ? 1 : 0 );
|
|
|
|
|
// XXX: These two might be server-global. I should look into whether
|
|
|
|
|
// to try to preserve the original value on exit.
|
|
|
|
|
#ifdef GLXEW_MESA_swap_control // Added in 1.7. 1.6 is still common out there apparently.
|
|
|
|
|
else if(GLXEW_MESA_swap_control) // Haven't seen this NOT implemented yet
|
|
|
|
|
glXSwapIntervalMESA( CurrentParams.vsync ? 1 : 0 );
|
|
|
|
|
#endif
|
|
|
|
|
else if (GLXEW_SGI_swap_control) // But old GLEW.
|
|
|
|
|
glXSwapIntervalSGI( CurrentParams.vsync ? 1 : 0 );
|
|
|
|
|
else
|
|
|
|
|
CurrentParams.vsync = false; // Assuming it's not on
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return ""; // Success
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -332,16 +635,114 @@ void LowLevelWindow_X11::SwapBuffers()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void LowLevelWindow_X11::GetDisplayResolutions( DisplayResolutions &out ) const
|
|
|
|
|
{
|
|
|
|
|
int iSizesXct;
|
|
|
|
|
XRRScreenSize *pSizesX = XRRSizes( Dpy, DefaultScreen( Dpy ), &iSizesXct );
|
|
|
|
|
ASSERT_M( iSizesXct != 0, "Couldn't get resolution list from X server" );
|
|
|
|
|
void LowLevelWindow_X11::GetDisplaySpecs(DisplaySpecs &out) const {
|
|
|
|
|
int screenNum = DefaultScreen(Dpy);
|
|
|
|
|
Screen *screen = ScreenOfDisplay(Dpy, screenNum);
|
|
|
|
|
|
|
|
|
|
for( int i = 0; i < iSizesXct; ++i )
|
|
|
|
|
{
|
|
|
|
|
DisplayResolution res = { pSizesX[i].width, pSizesX[i].height, true };
|
|
|
|
|
out.insert( res );
|
|
|
|
|
XWindowAttributes winAttr = XWindowAttributes();
|
|
|
|
|
if (XGetWindowAttributes(Dpy, Win, &winAttr)) {
|
|
|
|
|
screen = winAttr.screen;
|
|
|
|
|
screenNum = XScreenNumberOfScreen(screen);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create a display spec for the entire X screen itself
|
|
|
|
|
// First get current config
|
|
|
|
|
Rotation curRotation;
|
|
|
|
|
XRRScreenConfiguration *screenConf = XRRGetScreenInfo(Dpy, Win);
|
|
|
|
|
const short curRate = XRRConfigCurrentRate(screenConf);
|
|
|
|
|
SizeID curSizeId = XRRConfigCurrentConfiguration(screenConf, &curRotation);
|
|
|
|
|
// curRotation does not factor into how we report supported XScreen sizes:
|
|
|
|
|
// XRR reports the supported *screen* sizes with height/width swapped appropriately
|
|
|
|
|
// for currently configured rotation. Supported sizes for *output* modes (below)
|
|
|
|
|
// DO NOT account for screen rotation
|
|
|
|
|
|
|
|
|
|
std::set<DisplayMode> screenModes;
|
|
|
|
|
int nsizes = 0;
|
|
|
|
|
XRRScreenSize *screenSizes = XRRSizes( Dpy, screenNum, &nsizes);
|
|
|
|
|
DisplayMode screenCurMode = {0};
|
|
|
|
|
for (unsigned int szIdx = 0, mode_idx = 0; szIdx < nsizes; ++szIdx) {
|
|
|
|
|
XRRScreenSize &size = screenSizes[szIdx];
|
|
|
|
|
int nrates = 0;
|
|
|
|
|
short *rates = XRRRates(Dpy, screenNum, szIdx, &nrates);
|
|
|
|
|
for (unsigned int rIdx = 0; rIdx < nrates; ++rIdx, ++mode_idx) {
|
|
|
|
|
DisplayMode m = {static_cast<unsigned int> (size.width), static_cast<unsigned int> (size.height), static_cast<double> (rates[rIdx])};
|
|
|
|
|
screenModes.insert(m);
|
|
|
|
|
if (rates[rIdx] == curRate && szIdx == curSizeId) {
|
|
|
|
|
screenCurMode = m;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
const RectI screenBounds( 0, 0, screenSizes[curSizeId].width, screenSizes[curSizeId].height);
|
|
|
|
|
const DisplaySpec screenSpec( ID_XSCREEN, "X Screen", screenModes, screenCurMode, screenBounds, true);
|
|
|
|
|
out.insert(screenSpec);
|
|
|
|
|
// XRRScreenSize array from XRRSizes does *not* have to be returned (valgrind said XFree was an invalid
|
|
|
|
|
// free in a small test program, there is no XRRFreeScreenSize, etc)
|
|
|
|
|
XRRFreeScreenConfigInfo(screenConf);
|
|
|
|
|
|
|
|
|
|
if (g_bUseXRandR12) {
|
|
|
|
|
// Build per-output DisplaySpecs
|
|
|
|
|
|
|
|
|
|
// First, get the list of resolutions that'll be referenced (by RRMode) in each
|
|
|
|
|
// OutputInfo
|
|
|
|
|
XRRScreenResources *scrRes = XRRGetScreenResources(Dpy, Win);
|
|
|
|
|
std::map<RRMode, DisplayMode> outputModes;
|
|
|
|
|
for (unsigned int i = 0; i < scrRes->nmode; ++i) {
|
|
|
|
|
const XRRModeInfo &mode = scrRes->modes[i];
|
|
|
|
|
DisplayMode m = {mode.width, mode.height,
|
|
|
|
|
calcRandRRefresh(mode.dotClock, mode.hTotal, mode.vTotal)};
|
|
|
|
|
outputModes[mode.id] = m;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Now, for each output, build a corresponding DisplaySpec
|
|
|
|
|
for (unsigned int outIdx = 0; outIdx < scrRes->noutput; ++outIdx)
|
|
|
|
|
{
|
|
|
|
|
XRROutputInfo *outInfo = XRRGetOutputInfo( Dpy, scrRes, scrRes->outputs[outIdx] );
|
|
|
|
|
if (outInfo->nmode > 0)
|
|
|
|
|
{
|
|
|
|
|
// Get the current configuration of the Output, if it's being driven by
|
|
|
|
|
// a crtc
|
|
|
|
|
RRMode curRRMode = None;
|
|
|
|
|
bool bPortrait = false;
|
|
|
|
|
int crtcX = 0, crtcY = 0;
|
|
|
|
|
if (outInfo->crtc != None)
|
|
|
|
|
{
|
|
|
|
|
XRRCrtcInfo *conf = XRRGetCrtcInfo( Dpy, scrRes, outInfo->crtc );
|
|
|
|
|
curRRMode = conf->mode;
|
|
|
|
|
bPortrait = (conf->rotation & (RR_Rotate_90 | RR_Rotate_270)) != 0;
|
|
|
|
|
crtcX = conf->x;
|
|
|
|
|
crtcY = conf->y;
|
|
|
|
|
XRRFreeCrtcInfo( conf );
|
|
|
|
|
}
|
|
|
|
|
// Get all supported modes, noting which one, if any, is currently active
|
|
|
|
|
std::set<DisplayMode> outputSupported;
|
|
|
|
|
DisplayMode outputCurMode = {0};
|
|
|
|
|
RectI outBounds;
|
|
|
|
|
for (unsigned int modeIdx = 0; modeIdx < outInfo->nmode; ++modeIdx)
|
|
|
|
|
{
|
|
|
|
|
DisplayMode mode = outputModes[outInfo->modes[modeIdx]];
|
|
|
|
|
unsigned int modeWidth = bPortrait ? mode.height : mode.width;
|
|
|
|
|
unsigned int modeHeight = bPortrait ? mode.width : mode.height;
|
|
|
|
|
DisplayMode m = {modeWidth, modeHeight, mode.refreshRate};
|
|
|
|
|
outputSupported.insert( m );
|
|
|
|
|
if (curRRMode != None && outInfo->modes[modeIdx] == curRRMode)
|
|
|
|
|
{
|
|
|
|
|
outputCurMode = m;
|
|
|
|
|
outBounds = RectI( crtcX, crtcY, crtcX + modeWidth, crtcY + modeHeight);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
const std::string outId( outInfo->name, static_cast<unsigned int> (outInfo->nameLen) );
|
|
|
|
|
const std::string outName( outId );
|
|
|
|
|
if (curRRMode != None)
|
|
|
|
|
{
|
|
|
|
|
out.insert( DisplaySpec( outId, outName, outputSupported, outputCurMode, outBounds ));
|
|
|
|
|
} else
|
|
|
|
|
{
|
|
|
|
|
out.insert( DisplaySpec( outId, outName, outputSupported ));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
XRRFreeOutputInfo( outInfo );
|
|
|
|
|
}
|
|
|
|
|
XRRFreeScreenResources( scrRes );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -512,6 +913,34 @@ bool LowLevelWindow_X11::SupportsRenderToTexture() const
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool NetWMSupported(Display *Dpy, Atom feature)
|
|
|
|
|
{
|
|
|
|
|
Atom net_supported = XInternAtom( Dpy, "_NET_SUPPORTED", False );
|
|
|
|
|
Atom actual_type_return = BadAtom;
|
|
|
|
|
int actual_format_return = 0;
|
|
|
|
|
unsigned long nitems_return = 0;
|
|
|
|
|
unsigned long bytes_after_return = 0;
|
|
|
|
|
Atom *prop_return;
|
|
|
|
|
Status status = XGetWindowProperty( Dpy, RootWindow( Dpy, DefaultScreen( Dpy )), net_supported, 0, 8192, False,
|
|
|
|
|
XA_ATOM, &actual_type_return,
|
|
|
|
|
&actual_format_return, &nitems_return, &bytes_after_return,
|
|
|
|
|
reinterpret_cast<unsigned char **> (&prop_return));
|
|
|
|
|
if (status != Success)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto supported = std::find( prop_return, prop_return + nitems_return, feature ) != prop_return + nitems_return;
|
|
|
|
|
XFree( prop_return );
|
|
|
|
|
return supported;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool LowLevelWindow_X11::SupportsFullscreenBorderlessWindow() const
|
|
|
|
|
{
|
|
|
|
|
Atom fullscreen = XInternAtom( Dpy, "_NET_WM_STATE_FULLSCREEN", False );
|
|
|
|
|
return NetWMSupported( Dpy, fullscreen );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RenderTarget *LowLevelWindow_X11::CreateRenderTarget()
|
|
|
|
|
{
|
|
|
|
|
return new RenderTarget_X11( this );
|
|
|
|
|