mirror of
https://github.com/libretro/RetroArch
synced 2025-01-31 15:32:59 +00:00
62e6439329
* Remove deps/switchres to update it right after * Squashed 'deps/switchres/' content from commit 4df022c68a git-subtree-dir: deps/switchres git-subtree-split: 4df022c68a43b6481e18d5aa8e0ea27481291d1a
1205 lines
40 KiB
C++
1205 lines
40 KiB
C++
/**************************************************************
|
|
|
|
custom_video_xrandr.cpp - Linux XRANDR video management layer
|
|
|
|
---------------------------------------------------------
|
|
|
|
Switchres Modeline generation engine for emulation
|
|
|
|
License GPL-2.0+
|
|
Copyright 2010-2021 Chris Kennedy, Antonio Giner,
|
|
Alexandre Wodarczyk, Gil Delescluse
|
|
|
|
**************************************************************/
|
|
|
|
#include <stdio.h>
|
|
#include <exception>
|
|
#include <dlfcn.h>
|
|
#include <string.h>
|
|
#include "custom_video_xrandr.h"
|
|
#include "log.h"
|
|
|
|
//============================================================
|
|
// library functions
|
|
//============================================================
|
|
|
|
#define XRRAddOutputMode p_XRRAddOutputMode
|
|
#define XRRConfigCurrentConfiguration p_XRRConfigCurrentConfiguration
|
|
#define XRRCreateMode p_XRRCreateMode
|
|
#define XRRDeleteOutputMode p_XRRDeleteOutputMode
|
|
#define XRRDestroyMode p_XRRDestroyMode
|
|
#define XRRFreeCrtcInfo p_XRRFreeCrtcInfo
|
|
#define XRRFreeOutputInfo p_XRRFreeOutputInfo
|
|
#define XRRFreeScreenConfigInfo p_XRRFreeScreenConfigInfo
|
|
#define XRRFreeScreenResources p_XRRFreeScreenResources
|
|
#define XRRGetCrtcInfo p_XRRGetCrtcInfo
|
|
#define XRRGetOutputInfo p_XRRGetOutputInfo
|
|
#define XRRGetScreenInfo p_XRRGetScreenInfo
|
|
#define XRRGetScreenResourcesCurrent p_XRRGetScreenResourcesCurrent
|
|
#define XRRQueryVersion p_XRRQueryVersion
|
|
#define XRRSetCrtcConfig p_XRRSetCrtcConfig
|
|
#define XRRSetScreenSize p_XRRSetScreenSize
|
|
#define XRRGetScreenSizeRange p_XRRGetScreenSizeRange
|
|
|
|
#define XCloseDisplay p_XCloseDisplay
|
|
#define XGrabServer p_XGrabServer
|
|
#define XOpenDisplay p_XOpenDisplay
|
|
#define XSync p_XSync
|
|
#define XUngrabServer p_XUngrabServer
|
|
#define XSetErrorHandler p_XSetErrorHandler
|
|
#define XClearWindow p_XClearWindow
|
|
#define XFillRectangle p_XFillRectangle
|
|
#define XCreateGC p_XCreateGC
|
|
|
|
//============================================================
|
|
// error_handler
|
|
// xorg error handler (static)
|
|
//============================================================
|
|
|
|
int xrandr_timing::ms_xerrors = 0;
|
|
int xrandr_timing::ms_xerrors_flag = 0;
|
|
static int (*old_error_handler)(Display *, XErrorEvent *);
|
|
|
|
static __typeof__(XGetErrorText) *p_XGetErrorText;
|
|
#define XGetErrorText p_XGetErrorText
|
|
|
|
static int error_handler(Display *dpy, XErrorEvent *err)
|
|
{
|
|
char buf[64];
|
|
XGetErrorText(dpy, err->error_code, buf, 64);
|
|
buf[0] = '\0';
|
|
xrandr_timing::ms_xerrors |= xrandr_timing::ms_xerrors_flag;
|
|
old_error_handler(dpy, err);
|
|
log_error("XRANDR: <-> (error_handler) [ERROR] %s error code %d flags %02x\n", buf, err->error_code, xrandr_timing::ms_xerrors);
|
|
return 0;
|
|
}
|
|
|
|
//============================================================
|
|
// id for class object (static)
|
|
//============================================================
|
|
|
|
static int s_id = 0;
|
|
|
|
//============================================================
|
|
// screen management exclusivity array (static)
|
|
//============================================================
|
|
|
|
static int s_total_managed_screen = 0;
|
|
static int *sp_shared_screen_manager = NULL;
|
|
|
|
//============================================================
|
|
// desktop screen positions (static)
|
|
//============================================================
|
|
|
|
static XRRCrtcInfo *sp_desktop_crtc = NULL;
|
|
|
|
//============================================================
|
|
// xrandr_timing::xrandr_timing
|
|
//============================================================
|
|
|
|
xrandr_timing::xrandr_timing(char *device_name, custom_video_settings *vs)
|
|
{
|
|
m_vs = *vs;
|
|
|
|
// Increment id for each new screen
|
|
m_id = ++s_id;
|
|
|
|
log_verbose("XRANDR: <%d> (xrandr_timing) creation (%s)\n", m_id, device_name);
|
|
// Copy screen device name and limit size
|
|
if ((strlen(device_name) + 1) > 32)
|
|
{
|
|
strncpy(m_device_name, device_name, 31);
|
|
log_error("XRANDR: <%d> (xrandr_timing) [ERROR] the device name is too long it has been trucated to %s\n", m_id, m_device_name);
|
|
}
|
|
else
|
|
strcpy(m_device_name, device_name);
|
|
|
|
if (m_vs.screen_reordering)
|
|
{
|
|
if (m_id == 1)
|
|
m_enable_screen_reordering = 1;
|
|
}
|
|
else if (m_vs.screen_compositing)
|
|
m_enable_screen_compositing = 1;
|
|
|
|
log_verbose("XRANDR: <%d> (xrandr_timing) checking X availability (early stub)\n", m_id);
|
|
|
|
m_x11_handle = dlopen("libX11.so", RTLD_NOW);
|
|
|
|
if (m_x11_handle)
|
|
{
|
|
p_XOpenDisplay = (__typeof__(XOpenDisplay)) dlsym(m_x11_handle, "XOpenDisplay");
|
|
if (p_XOpenDisplay == NULL)
|
|
{
|
|
log_error("XRANDR: <%d> (xrandr_timing) [ERROR] missing func %s in %s\n", m_id, "XOpenDisplay", "X11_LIBRARY");
|
|
throw std::exception();
|
|
}
|
|
else
|
|
{
|
|
if (!XOpenDisplay(NULL))
|
|
{
|
|
log_verbose("XRANDR: <%d> (xrandr_timing) X server not found\n", m_id);
|
|
throw std::exception();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
log_error("XRANDR: <%d> (xrandr_timing) [ERROR] missing %s library\n", m_id, "X11_LIBRARY");
|
|
throw std::exception();
|
|
}
|
|
|
|
s_total_managed_screen++;
|
|
}
|
|
|
|
//============================================================
|
|
// xrandr_timing::~xrandr_timing
|
|
//============================================================
|
|
|
|
xrandr_timing::~xrandr_timing()
|
|
{
|
|
s_total_managed_screen--;
|
|
if (s_total_managed_screen == 0)
|
|
{
|
|
s_id = 0;
|
|
|
|
if (sp_desktop_crtc)
|
|
delete[]sp_desktop_crtc;
|
|
|
|
if (sp_shared_screen_manager)
|
|
delete[]sp_shared_screen_manager;
|
|
|
|
// Restore default desktop background
|
|
XClearWindow(m_pdisplay, m_root);
|
|
}
|
|
|
|
// Free the display
|
|
if (m_pdisplay != NULL)
|
|
XCloseDisplay(m_pdisplay);
|
|
|
|
// close Xrandr library
|
|
if (m_xrandr_handle)
|
|
dlclose(m_xrandr_handle);
|
|
|
|
// close X11 library
|
|
if (m_x11_handle)
|
|
dlclose(m_x11_handle);
|
|
}
|
|
|
|
//============================================================
|
|
// xrandr_timing::init
|
|
//============================================================
|
|
|
|
bool xrandr_timing::init()
|
|
{
|
|
log_verbose("XRANDR: <%d> (init) loading Xrandr library\n", m_id);
|
|
if (!m_xrandr_handle)
|
|
m_xrandr_handle = dlopen("libXrandr.so", RTLD_NOW);
|
|
if (m_xrandr_handle)
|
|
{
|
|
p_XRRAddOutputMode = (__typeof__(XRRAddOutputMode)) dlsym(m_xrandr_handle, "XRRAddOutputMode");
|
|
if (p_XRRAddOutputMode == NULL)
|
|
{
|
|
log_error("XRANDR: <%d> (init) [ERROR] missing func %s in %s", m_id, "XRRAddOutputMode", "XRANDR_LIBRARY");
|
|
return false;
|
|
}
|
|
|
|
p_XRRConfigCurrentConfiguration = (__typeof__(XRRConfigCurrentConfiguration)) dlsym(m_xrandr_handle, "XRRConfigCurrentConfiguration");
|
|
if (p_XRRConfigCurrentConfiguration == NULL)
|
|
{
|
|
log_error("XRANDR: <%d> (init) [ERROR] missing func %s in %s", m_id, "XRRConfigCurrentConfiguration", "XRANDR_LIBRARY");
|
|
return false;
|
|
}
|
|
|
|
p_XRRCreateMode = (__typeof__(XRRCreateMode)) dlsym(m_xrandr_handle, "XRRCreateMode");
|
|
if (p_XRRCreateMode == NULL)
|
|
{
|
|
log_error("XRANDR: <%d> (init) [ERROR] missing func %s in %s", m_id, "XRRCreateMode", "XRANDR_LIBRARY");
|
|
return false;
|
|
}
|
|
|
|
p_XRRDeleteOutputMode = (__typeof__(XRRDeleteOutputMode)) dlsym(m_xrandr_handle, "XRRDeleteOutputMode");
|
|
if (p_XRRDeleteOutputMode == NULL)
|
|
{
|
|
log_error("XRANDR: <%d> (init) [ERROR] missing func %s in %s", m_id, "XRRDeleteOutputMode", "XRANDR_LIBRARY");
|
|
return false;
|
|
}
|
|
|
|
p_XRRDestroyMode = (__typeof__(XRRDestroyMode)) dlsym(m_xrandr_handle, "XRRDestroyMode");
|
|
if (p_XRRDestroyMode == NULL)
|
|
{
|
|
log_error("XRANDR: <%d> (init) [ERROR] missing func %s in %s", m_id, "XRRDestroyMode", "XRANDR_LIBRARY");
|
|
return false;
|
|
}
|
|
|
|
p_XRRFreeCrtcInfo = (__typeof__(XRRFreeCrtcInfo)) dlsym(m_xrandr_handle, "XRRFreeCrtcInfo");
|
|
if (p_XRRFreeCrtcInfo == NULL)
|
|
{
|
|
log_error("XRANDR: <%d> (init) [ERROR] missing func %s in %s", m_id, "XRRFreeCrtcInfo", "XRANDR_LIBRARY");
|
|
return false;
|
|
}
|
|
|
|
p_XRRFreeOutputInfo = (__typeof__(XRRFreeOutputInfo)) dlsym(m_xrandr_handle, "XRRFreeOutputInfo");
|
|
if (p_XRRFreeOutputInfo == NULL)
|
|
{
|
|
log_error("XRANDR: <%d> (init) [ERROR] missing func %s in %s", m_id, "XRRFreeOutputInfo", "XRANDR_LIBRARY");
|
|
return false;
|
|
}
|
|
|
|
p_XRRFreeScreenConfigInfo = (__typeof__(XRRFreeScreenConfigInfo)) dlsym(m_xrandr_handle, "XRRFreeScreenConfigInfo");
|
|
if (p_XRRFreeScreenConfigInfo == NULL)
|
|
{
|
|
log_error("XRANDR: <%d> (init) [ERROR] missing func %s in %s", m_id, "XRRFreeScreenConfigInfo", "XRANDR_LIBRARY");
|
|
return false;
|
|
}
|
|
|
|
p_XRRFreeScreenResources = (__typeof__(XRRFreeScreenResources)) dlsym(m_xrandr_handle, "XRRFreeScreenResources");
|
|
if (p_XRRFreeScreenResources == NULL)
|
|
{
|
|
log_error("XRANDR: <%d> (init) [ERROR] missing func %s in %s", m_id, "XRRFreeScreenResources", "XRANDR_LIBRARY");
|
|
return false;
|
|
}
|
|
|
|
p_XRRGetCrtcInfo = (__typeof__(XRRGetCrtcInfo)) dlsym(m_xrandr_handle, "XRRGetCrtcInfo");
|
|
if (p_XRRGetCrtcInfo == NULL)
|
|
{
|
|
log_error("XRANDR: <%d> (init) [ERROR] missing func %s in %s", m_id, "XRRGetCrtcInfo", "XRANDR_LIBRARY");
|
|
return false;
|
|
}
|
|
|
|
p_XRRGetOutputInfo = (__typeof__(XRRGetOutputInfo)) dlsym(m_xrandr_handle, "XRRGetOutputInfo");
|
|
if (p_XRRGetOutputInfo == NULL)
|
|
{
|
|
log_error("XRANDR: <%d> (init) [ERROR] missing func %s in %s", m_id, "XRRGetOutputInfo", "XRANDR_LIBRARY");
|
|
return false;
|
|
}
|
|
|
|
p_XRRGetScreenInfo = (__typeof__(XRRGetScreenInfo)) dlsym(m_xrandr_handle, "XRRGetScreenInfo");
|
|
if (p_XRRGetScreenInfo == NULL)
|
|
{
|
|
log_error("XRANDR: <%d> (init) [ERROR] missing func %s in %s", m_id, "XRRGetScreenInfo", "XRANDR_LIBRARY");
|
|
return false;
|
|
}
|
|
|
|
p_XRRGetScreenResourcesCurrent = (__typeof__(XRRGetScreenResourcesCurrent)) dlsym(m_xrandr_handle, "XRRGetScreenResourcesCurrent");
|
|
if (p_XRRGetScreenResourcesCurrent == NULL)
|
|
{
|
|
log_error("XRANDR: <%d> (init) [ERROR] missing func %s in %s", m_id, "XRRGetScreenResourcesCurrent", "XRANDR_LIBRARY");
|
|
return false;
|
|
}
|
|
|
|
p_XRRQueryVersion = (__typeof__(XRRQueryVersion)) dlsym(m_xrandr_handle, "XRRQueryVersion");
|
|
if (p_XRRQueryVersion == NULL)
|
|
{
|
|
log_error("XRANDR: <%d> (init) [ERROR] missing func %s in %s", m_id, "XRRQueryVersion", "XRANDR_LIBRARY");
|
|
return false;
|
|
}
|
|
|
|
p_XRRSetCrtcConfig = (__typeof__(XRRSetCrtcConfig)) dlsym(m_xrandr_handle, "XRRSetCrtcConfig");
|
|
if (p_XRRSetCrtcConfig == NULL)
|
|
{
|
|
log_error("XRANDR: <%d> (init) [ERROR] missing func %s in %s", m_id, "XRRSetCrtcConfig", "XRANDR_LIBRARY");
|
|
return false;
|
|
}
|
|
|
|
p_XRRSetScreenSize = (__typeof__(XRRSetScreenSize)) dlsym(m_xrandr_handle, "XRRSetScreenSize");
|
|
if (p_XRRSetScreenSize == NULL)
|
|
{
|
|
log_error("XRANDR: <%d> (init) [ERROR] missing func %s in %s", m_id, "XRRSetScreenSize", "XRANDR_LIBRARY");
|
|
return false;
|
|
}
|
|
|
|
p_XRRGetScreenSizeRange = (__typeof__(XRRGetScreenSizeRange)) dlsym(m_xrandr_handle, "XRRGetScreenSizeRange");
|
|
if (p_XRRGetScreenSizeRange == NULL)
|
|
{
|
|
log_error("XRANDR: <%d> (init) [ERROR] missing func %s in %s", m_id, "XRRSetScreenSize", "XRANDR_LIBRARY");
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
log_error("XRANDR: <%d> (init) [ERROR] missing %s library\n", m_id, "XRANDR_LIBRARY");
|
|
return false;
|
|
}
|
|
|
|
log_verbose("XRANDR: <%d> (init) loading X11 library\n", m_id);
|
|
if (!m_x11_handle)
|
|
m_x11_handle = dlopen("libX11.so", RTLD_NOW);
|
|
if (m_x11_handle)
|
|
{
|
|
p_XCloseDisplay = (__typeof__(XCloseDisplay)) dlsym(m_x11_handle, "XCloseDisplay");
|
|
if (p_XCloseDisplay == NULL)
|
|
{
|
|
log_error("XRANDR: <%d> (init) [ERROR] missing func %s in %s\n", m_id, "XCloseDisplay", "X11_LIBRARY");
|
|
return false;
|
|
}
|
|
|
|
p_XGrabServer = (__typeof__(XGrabServer)) dlsym(m_x11_handle, "XGrabServer");
|
|
if (p_XGrabServer == NULL)
|
|
{
|
|
log_error("XRANDR: <%d> (init) [ERROR] missing func %s in %s\n", m_id, "XGrabServer", "X11_LIBRARY");
|
|
return false;
|
|
}
|
|
|
|
p_XOpenDisplay = (__typeof__(XOpenDisplay)) dlsym(m_x11_handle, "XOpenDisplay");
|
|
if (p_XOpenDisplay == NULL)
|
|
{
|
|
log_error("XRANDR: <%d> (init) [ERROR] missing func %s in %s\n", m_id, "XOpenDisplay", "X11_LIBRARY");
|
|
return false;
|
|
}
|
|
|
|
p_XSync = (__typeof__(XSync)) dlsym(m_x11_handle, "XSync");
|
|
if (p_XSync == NULL)
|
|
{
|
|
log_error("XRANDR: <%d> (init) [ERROR] missing func %s in %s\n", m_id, "XSync", "X11_LIBRARY");
|
|
return false;
|
|
}
|
|
|
|
p_XUngrabServer = (__typeof__(XUngrabServer)) dlsym(m_x11_handle, "XUngrabServer");
|
|
if (p_XUngrabServer == NULL)
|
|
{
|
|
log_error("XRANDR: <%d> (init) [ERROR] missing func %s in %s\n", m_id, "XUngrabServer", "X11_LIBRARY");
|
|
return false;
|
|
}
|
|
|
|
p_XSetErrorHandler = (__typeof__(XSetErrorHandler)) dlsym(m_x11_handle, "XSetErrorHandler");
|
|
if (p_XSetErrorHandler == NULL)
|
|
{
|
|
log_error("XRANDR: <%d> (init) [ERROR] missing func %s in %s\n", m_id, "XSetErrorHandler", "X11_LIBRARY");
|
|
return false;
|
|
}
|
|
|
|
p_XGetErrorText = (__typeof__(XGetErrorText)) dlsym(m_x11_handle, "XGetErrorText");
|
|
if (p_XGetErrorText == NULL)
|
|
{
|
|
log_error("XRANDR: <%d> (init) [ERROR] missing func %s in %s\n", m_id, "XGetErrorText", "X11_LIBRARY");
|
|
return false;
|
|
}
|
|
|
|
p_XClearWindow = (__typeof__(XClearWindow)) dlsym(m_x11_handle, "XClearWindow");
|
|
if (p_XClearWindow == NULL)
|
|
{
|
|
log_error("XRANDR: <%d> (init) [ERROR] missing func %s in %s", m_id, "XClearWindow", "X11_LIBRARY");
|
|
return false;
|
|
}
|
|
|
|
p_XFillRectangle = (__typeof__(XFillRectangle)) dlsym(m_x11_handle, "XFillRectangle");
|
|
if (p_XFillRectangle == NULL)
|
|
{
|
|
log_error("XRANDR: <%d> (init) [ERROR] missing func %s in %s", m_id, "XFillRectangle", "X11_LIBRARY");
|
|
return false;
|
|
}
|
|
|
|
p_XCreateGC = (__typeof__(XCreateGC)) dlsym(m_x11_handle, "XCreateGC");
|
|
if (p_XCreateGC == NULL)
|
|
{
|
|
log_error("XRANDR: <%d> (init) [ERROR] missing func %s in %s", m_id, "XCreateGC", "X11_LIBRARY");
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
log_error("XRANDR: <%d> (init) [ERROR] missing %s library\n", m_id, "X11_LIBRARY");
|
|
return false;
|
|
}
|
|
|
|
// Select current display and root window
|
|
// m_pdisplay is global to reduce open/close calls, resource is freed when class is destroyed
|
|
if (!m_pdisplay)
|
|
m_pdisplay = XOpenDisplay(NULL);
|
|
|
|
if (!m_pdisplay)
|
|
{
|
|
log_error("XRANDR: <%d> (init) [ERROR] failed to connect to the X server\n", m_id);
|
|
return false;
|
|
}
|
|
|
|
// Display XRANDR version
|
|
int major_version, minor_version;
|
|
XRRQueryVersion(m_pdisplay, &major_version, &minor_version);
|
|
log_verbose("XRANDR: <%d> (init) version %d.%d\n", m_id, major_version, minor_version);
|
|
|
|
if (major_version < 1 || (major_version == 1 && minor_version < 2))
|
|
{
|
|
log_error("XRANDR: <%d> (init) [ERROR] Xrandr version 1.2 or above is required\n", m_id);
|
|
return false;
|
|
}
|
|
|
|
// screen_pos defines screen position, 0 is default first screen position and equivalent to 'auto'
|
|
int screen_pos = -1;
|
|
bool detected = false;
|
|
|
|
// Handle the screen name, "auto", "screen[0-9]" and XRANDR device name
|
|
if (strlen(m_device_name) == 7 && !strncmp(m_device_name, "screen", 6) && m_device_name[6] >= '0' && m_device_name[6] <= '9')
|
|
screen_pos = m_device_name[6] - '0';
|
|
else if (strlen(m_device_name) == 1 && m_device_name[0] >= '0' && m_device_name[0] <= '9')
|
|
screen_pos = m_device_name[0] - '0';
|
|
|
|
if (ScreenCount(m_pdisplay) > 1)
|
|
log_verbose("XRANDR: <%d> (init) [WARNING] screen count is %d, unpredictable behavior to be expected\n", m_id, ScreenCount(m_pdisplay));
|
|
|
|
for (int screen = 0; !detected && screen < ScreenCount(m_pdisplay); screen++)
|
|
{
|
|
log_verbose("XRANDR: <%d> (init) check screen number %d\n", m_id, screen);
|
|
m_screen = screen;
|
|
m_root = RootWindow(m_pdisplay, screen);
|
|
|
|
XRRScreenResources *resources = XRRGetScreenResourcesCurrent(m_pdisplay, m_root);
|
|
|
|
if (m_id == 1)
|
|
{
|
|
// Prepare the shared screen array
|
|
sp_shared_screen_manager = new int[resources->noutput];
|
|
for (int o = 0; o < resources->noutput; o++)
|
|
sp_shared_screen_manager[o] = 0;
|
|
|
|
// Save all active crtc positions
|
|
sp_desktop_crtc = new XRRCrtcInfo[resources->ncrtc];
|
|
for (int c = 0; c < resources->ncrtc; c++)
|
|
memcpy(&sp_desktop_crtc[c], XRRGetCrtcInfo(m_pdisplay, resources, resources->crtcs[c]), sizeof(XRRCrtcInfo));
|
|
}
|
|
|
|
// Get default screen rotation from screen configuration
|
|
XRRScreenConfiguration *sc = XRRGetScreenInfo(m_pdisplay, m_root);
|
|
XRRConfigCurrentConfiguration(sc, &m_desktop_rotation);
|
|
XRRFreeScreenConfigInfo(sc);
|
|
|
|
Rotation current_rotation = 0;
|
|
int output_position = 0;
|
|
for (int o = 0; o < resources->noutput; o++)
|
|
{
|
|
XRROutputInfo *output_info = XRRGetOutputInfo(m_pdisplay, resources, resources->outputs[o]);
|
|
if (!output_info)
|
|
{
|
|
log_error("XRANDR: <%d> (init) [ERROR] could not get output 0x%x information\n", m_id, (unsigned int)resources->outputs[o]);
|
|
continue;
|
|
}
|
|
// Check all connected output
|
|
if (m_desktop_output == -1 && output_info->connection == RR_Connected && output_info->crtc)
|
|
{
|
|
|
|
if (!strcmp(m_device_name, "auto") || !strcmp(m_device_name, output_info->name) || output_position == screen_pos)
|
|
{
|
|
// store the output connector
|
|
m_desktop_output = o;
|
|
|
|
// store screen minium and maximum resolutions
|
|
int min_width;
|
|
int max_width;
|
|
int min_height;
|
|
int max_height;
|
|
XRRGetScreenSizeRange (m_pdisplay, m_root, &min_width, &min_height, &max_width, &max_height);
|
|
m_min_width = min_width;
|
|
m_max_width = max_width;
|
|
m_min_height = min_height;
|
|
m_max_height = max_height;
|
|
|
|
if (sp_shared_screen_manager[m_desktop_output] == 0)
|
|
{
|
|
sp_shared_screen_manager[m_desktop_output] = m_id;
|
|
m_managed = 1;
|
|
}
|
|
|
|
// identify the current modeline and rotation
|
|
XRRCrtcInfo *crtc_info = XRRGetCrtcInfo(m_pdisplay, resources, output_info->crtc);
|
|
current_rotation = crtc_info->rotation;
|
|
for (int m = 0; m < resources->nmode && m_desktop_mode.id == 0; m++)
|
|
{
|
|
// Get screen mode
|
|
if (crtc_info->mode == resources->modes[m].id)
|
|
{
|
|
m_desktop_mode = resources->modes[m];
|
|
m_last_crtc = *crtc_info;
|
|
}
|
|
}
|
|
XRRFreeCrtcInfo(crtc_info);
|
|
|
|
// check screen rotation (left or right)
|
|
if (current_rotation & 0xe)
|
|
{
|
|
m_crtc_flags = MODE_ROTATED;
|
|
log_verbose("XRANDR: <%d> (init) desktop rotation is %s\n", m_id, (current_rotation & 0x2) ? "left" : ((current_rotation & 0x8) ? "right" : "inverted"));
|
|
}
|
|
}
|
|
output_position++;
|
|
}
|
|
log_verbose("XRANDR: <%d> (init) check output connector '%s' active %d crtc %d %s\n", m_id, output_info->name, output_info->connection == RR_Connected ? 1 : 0, output_info->crtc ? 1 : 0, m_desktop_output == o ? (m_managed ? "[SELECTED]" : "[UNMANAGED]") : "");
|
|
XRRFreeOutputInfo(output_info);
|
|
}
|
|
XRRFreeScreenResources(resources);
|
|
|
|
// Check if screen has been detected
|
|
detected = m_desktop_output != -1;
|
|
}
|
|
|
|
if (!detected)
|
|
log_error("XRANDR: <%d> (init) [ERROR] no screen detected\n", m_id);
|
|
|
|
else if (m_enable_screen_reordering)
|
|
{
|
|
// Global screen placement
|
|
modeline mode = {};
|
|
mode.type = MODE_DESKTOP;
|
|
set_timing(&mode, XRANDR_ENABLE_SCREEN_REORDERING);
|
|
}
|
|
|
|
return detected;
|
|
}
|
|
|
|
//============================================================
|
|
// xrandr_timing::update_mode
|
|
//============================================================
|
|
|
|
bool xrandr_timing::update_mode(modeline *mode)
|
|
{
|
|
if (!mode)
|
|
return false;
|
|
|
|
// Handle no screen detected case
|
|
if (m_desktop_output == -1)
|
|
{
|
|
log_error("XRANDR: <%d> (update_mode) [ERROR] no screen detected\n", m_id);
|
|
return false;
|
|
}
|
|
|
|
if (!delete_mode(mode))
|
|
{
|
|
log_error("XRANDR: <%d> (update_mode) [ERROR] delete operation not successful", m_id);
|
|
return false;
|
|
}
|
|
|
|
if (!add_mode(mode))
|
|
{
|
|
log_error("XRANDR: <%d> (update_mode) [ERROR] add operation not successful", m_id);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//============================================================
|
|
// xrandr_timing::add_mode
|
|
//============================================================
|
|
|
|
bool xrandr_timing::add_mode(modeline *mode)
|
|
{
|
|
if (!mode)
|
|
return false;
|
|
|
|
// Handle no screen detected case
|
|
if (m_desktop_output == -1)
|
|
{
|
|
log_error("XRANDR: <%d> (add_mode) [ERROR] no screen detected\n", m_id);
|
|
return false;
|
|
}
|
|
|
|
if (!m_managed)
|
|
{
|
|
log_error("XRANDR: <%d> (add_mode) [WARNING] this screen is managed by <%d>\n", m_id, sp_shared_screen_manager[m_desktop_output]);
|
|
return false;
|
|
}
|
|
|
|
// Check if mode is available from the plaftform_data mode id
|
|
XRRModeInfo *pxmode = find_mode(mode);
|
|
if (pxmode != NULL)
|
|
{
|
|
log_error("XRANDR: <%d> (add_mode) [WARNING] mode already exist\n", m_id);
|
|
return true;
|
|
}
|
|
|
|
// Create specific mode name
|
|
char name[48];
|
|
sprintf(name, "SR-%d_%dx%d@%.02f%s", m_id, mode->hactive, mode->vactive, mode->vfreq, mode->interlace ? "i" : "");
|
|
|
|
// Check if mode is available from the SR name (should not be the case, otherwise it means that we recevied twice the same mode request)
|
|
pxmode = find_mode_by_name(name);
|
|
if (pxmode != NULL)
|
|
{
|
|
log_error("XRANDR: <%d> (add_mode) [WARNING] mode already exist (duplicate request)\n", m_id);
|
|
mode->platform_data = pxmode->id;
|
|
return true;
|
|
}
|
|
|
|
log_verbose("XRANDR: <%d> (add_mode) create mode %s\n", m_id, name);
|
|
|
|
// Setup the xrandr mode structure
|
|
XRRModeInfo xmode = {};
|
|
|
|
xmode.name = name;
|
|
xmode.nameLength = strlen(name);
|
|
xmode.dotClock = mode->pclock;
|
|
xmode.width = mode->hactive;
|
|
xmode.hSyncStart = mode->hbegin;
|
|
xmode.hSyncEnd = mode->hend;
|
|
xmode.hTotal = mode->htotal;
|
|
xmode.height = mode->vactive;
|
|
xmode.vSyncStart = mode->vbegin;
|
|
xmode.vSyncEnd = mode->vend;
|
|
xmode.vTotal = mode->vtotal;
|
|
xmode.modeFlags = (mode->interlace ? RR_Interlace : 0) | (mode->doublescan ? RR_DoubleScan : 0) | (mode->hsync ? RR_HSyncPositive : RR_HSyncNegative) | (mode->vsync ? RR_VSyncPositive : RR_VSyncNegative);
|
|
xmode.hSkew = 0;
|
|
|
|
mode->type |= CUSTOM_VIDEO_TIMING_XRANDR;
|
|
|
|
// Create the modeline
|
|
XSync(m_pdisplay, False);
|
|
ms_xerrors = 0;
|
|
ms_xerrors_flag = 0x01;
|
|
old_error_handler = XSetErrorHandler(error_handler);
|
|
RRMode gmid = XRRCreateMode(m_pdisplay, m_root, &xmode);
|
|
XSync(m_pdisplay, False);
|
|
XSetErrorHandler(old_error_handler);
|
|
if (ms_xerrors & ms_xerrors_flag)
|
|
{
|
|
log_error("XRANDR: <%d> (add_mode) [ERROR] in %s\n", m_id, "XRRCreateMode");
|
|
return false;
|
|
}
|
|
|
|
mode->platform_data = gmid;
|
|
|
|
// Add new modeline to primary output
|
|
XRRScreenResources *resources = XRRGetScreenResourcesCurrent(m_pdisplay, m_root);
|
|
|
|
XSync(m_pdisplay, False);
|
|
ms_xerrors_flag = 0x02;
|
|
old_error_handler = XSetErrorHandler(error_handler);
|
|
XRRAddOutputMode(m_pdisplay, resources->outputs[m_desktop_output], mode->platform_data);
|
|
XSync(m_pdisplay, False);
|
|
XSetErrorHandler(old_error_handler);
|
|
|
|
XRRFreeScreenResources(resources);
|
|
|
|
if (ms_xerrors & ms_xerrors_flag)
|
|
{
|
|
log_error("XRANDR: <%d> (add_mode) [ERROR] in %s\n", m_id, "XRRAddOutputMode");
|
|
|
|
// remove unlinked modeline
|
|
if (mode->platform_data)
|
|
{
|
|
log_error("XRANDR: <%d> (add_mode) [ERROR] remove mode [%04lx]\n", m_id, mode->platform_data);
|
|
XRRDestroyMode(m_pdisplay, mode->platform_data);
|
|
mode->platform_data = 0;
|
|
}
|
|
}
|
|
else
|
|
log_verbose("XRANDR: <%d> (add_mode) mode %04lx %dx%d refresh %.6f added\n", m_id, mode->platform_data, mode->hactive, mode->vactive, mode->vfreq);
|
|
|
|
return ms_xerrors == 0;
|
|
}
|
|
|
|
//============================================================
|
|
// xrandr_timing::find_mode_by_name
|
|
//============================================================
|
|
|
|
XRRModeInfo *xrandr_timing::find_mode_by_name(char *name)
|
|
{
|
|
XRRModeInfo *pxmode = NULL;
|
|
XRRScreenResources *resources = XRRGetScreenResourcesCurrent(m_pdisplay, m_root);
|
|
|
|
// use SR name to return the mode
|
|
for (int m = 0; m < resources->nmode; m++)
|
|
{
|
|
if (strcmp(resources->modes[m].name, name) == 0)
|
|
{
|
|
pxmode = &resources->modes[m];
|
|
break;
|
|
}
|
|
}
|
|
|
|
XRRFreeScreenResources(resources);
|
|
|
|
return pxmode;
|
|
}
|
|
|
|
//============================================================
|
|
// xrandr_timing::find_mode
|
|
//============================================================
|
|
|
|
XRRModeInfo *xrandr_timing::find_mode(modeline *mode)
|
|
{
|
|
XRRModeInfo *pxmode = NULL;
|
|
XRRScreenResources *resources = XRRGetScreenResourcesCurrent(m_pdisplay, m_root);
|
|
|
|
// use platform_data (mode id) to return the mode
|
|
for (int m = 0; m < resources->nmode; m++)
|
|
{
|
|
if (mode->platform_data == resources->modes[m].id)
|
|
{
|
|
pxmode = &resources->modes[m];
|
|
break;
|
|
}
|
|
}
|
|
|
|
XRRFreeScreenResources(resources);
|
|
|
|
return pxmode;
|
|
}
|
|
|
|
//============================================================
|
|
// xrandr_timing::set_timing
|
|
//============================================================
|
|
|
|
bool xrandr_timing::set_timing(modeline *mode)
|
|
{
|
|
if (m_enable_screen_compositing)
|
|
return set_timing(mode, 0);
|
|
|
|
return set_timing(mode, XRANDR_DISABLE_CRTC_RELOCATION);
|
|
}
|
|
|
|
//============================================================
|
|
// xrandr_timing::set_timing
|
|
//============================================================
|
|
|
|
bool xrandr_timing::set_timing(modeline *mode, int flags)
|
|
{
|
|
// Handle no screen detected case
|
|
if (m_desktop_output == -1)
|
|
{
|
|
log_error("XRANDR: <%d> (set_timing) [ERROR] no screen detected\n", m_id);
|
|
return false;
|
|
}
|
|
|
|
if (!m_managed)
|
|
{
|
|
log_error("XRANDR: <%d> (set_timing) [WARNING] this screen is managed by <%d>\n", m_id, sp_shared_screen_manager[m_desktop_output]);
|
|
return false;
|
|
}
|
|
|
|
if (m_id != 1 && (flags & XRANDR_ENABLE_SCREEN_REORDERING))
|
|
flags = XRANDR_DISABLE_CRTC_RELOCATION; // only master can do global screen preparation
|
|
|
|
XRRModeInfo *pxmode = NULL;
|
|
|
|
if (mode->type & MODE_DESKTOP)
|
|
pxmode = &m_desktop_mode;
|
|
else
|
|
pxmode = find_mode(mode);
|
|
|
|
if (pxmode == NULL)
|
|
{
|
|
log_error("XRANDR: <%d> (set_timing) [ERROR] mode not found\n", m_id);
|
|
return false;
|
|
}
|
|
|
|
// Use xrandr to switch to new mode.
|
|
XRRScreenResources *resources = XRRGetScreenResourcesCurrent(m_pdisplay, m_root);
|
|
XRROutputInfo *output_info = XRRGetOutputInfo(m_pdisplay, resources, resources->outputs[m_desktop_output]);
|
|
XRRCrtcInfo *crtc_info = XRRGetCrtcInfo(m_pdisplay, resources, output_info->crtc);
|
|
|
|
if (flags & XRANDR_DISABLE_CRTC_RELOCATION)
|
|
log_verbose("XRANDR: <%d> (set_timing) DISABLE crtc relocation\n", m_id);
|
|
|
|
if (flags & XRANDR_ENABLE_SCREEN_REORDERING)
|
|
log_verbose("XRANDR: <%d> (set_timing) GLOBAL desktop screen preparation\n", m_id);
|
|
else if (m_last_crtc.mode == crtc_info->mode && m_last_crtc.x == crtc_info->x && m_last_crtc.y == crtc_info->y && pxmode->id == crtc_info->mode)
|
|
log_verbose("XRANDR: <%d> (set_timing) requested mode is already active [%04lx] %ux%u+%d+%d\n", m_id, crtc_info->mode, crtc_info->width, crtc_info->height, crtc_info->x, crtc_info->y);
|
|
else if (m_last_crtc.mode != crtc_info->mode)
|
|
{
|
|
log_verbose("XRANDR: <%d> (set_timing) [WARNING] unexpected active modeline detected (last:[%04lx] now:[%04lx] %ux%u+%d+%d want:[%04lx])\n", m_id, m_last_crtc.mode, crtc_info->mode, crtc_info->width, crtc_info->height, crtc_info->x, crtc_info->y, pxmode->id);
|
|
*crtc_info = m_last_crtc;
|
|
}
|
|
|
|
// Grab X server to prevent unwanted interaction from the window manager
|
|
XGrabServer(m_pdisplay);
|
|
|
|
unsigned int width = m_min_width;
|
|
unsigned int height = m_min_height;
|
|
|
|
unsigned int active_crtc = 0;
|
|
|
|
unsigned int reordering_last_y = 0;
|
|
|
|
ms_xerrors = 0;
|
|
|
|
XRRCrtcInfo *global_crtc = new XRRCrtcInfo[resources->ncrtc];
|
|
XRRCrtcInfo *original_crtc = new XRRCrtcInfo[resources->ncrtc];
|
|
|
|
// caculate necessary screen size and of crtc neighborhood if they have at least one side aligned with the mode changed crtc
|
|
for (int c = 0; c < resources->ncrtc; c++)
|
|
{
|
|
// Prepare crtc references
|
|
memcpy(&original_crtc[c], XRRGetCrtcInfo(m_pdisplay, resources, resources->crtcs[c]), sizeof(XRRCrtcInfo));
|
|
memcpy(&global_crtc[c], XRRGetCrtcInfo(m_pdisplay, resources, resources->crtcs[c]), sizeof(XRRCrtcInfo));
|
|
// Original state
|
|
XRRCrtcInfo *crtc_info0 = &original_crtc[c];
|
|
// Modified state
|
|
XRRCrtcInfo *crtc_info1 = &global_crtc[c];
|
|
// clear timestamp
|
|
crtc_info1->timestamp = 0;
|
|
|
|
// Skip unused crtc
|
|
if (output_info->crtc != 0 && crtc_info0->mode != 0)
|
|
{
|
|
if (flags & XRANDR_ENABLE_SCREEN_REORDERING)
|
|
{
|
|
// Relocate all crtcs
|
|
// Super resolution placement, vertical stacking, reserved XRANDR_REORDERING_MAXIMUM_HEIGHT pixels
|
|
crtc_info1->x = 0;
|
|
crtc_info1->y = reordering_last_y;
|
|
if (crtc_info1->height > XRANDR_REORDERING_MAXIMUM_HEIGHT)
|
|
reordering_last_y += crtc_info1->height;
|
|
else
|
|
reordering_last_y += XRANDR_REORDERING_MAXIMUM_HEIGHT;
|
|
crtc_info1->timestamp |= XRANDR_SETMODE_UPDATE_REORDERING;
|
|
active_crtc++;
|
|
}
|
|
// Switchres selected desktop output
|
|
else if (resources->crtcs[c] == output_info->crtc)
|
|
{
|
|
crtc_info1->timestamp |= XRANDR_SETMODE_IS_DESKTOP;
|
|
crtc_info1->mode = pxmode->id;
|
|
crtc_info1->width = pxmode->width;
|
|
crtc_info1->height = pxmode->height;
|
|
|
|
if (mode->type & MODE_DESKTOP)
|
|
{
|
|
if (!m_enable_screen_compositing && (crtc_info1->x != sp_desktop_crtc[c].x || crtc_info1->y != sp_desktop_crtc[c].y))
|
|
{
|
|
// Restore original desktop position
|
|
crtc_info1->x = sp_desktop_crtc[c].x;
|
|
crtc_info1->y = sp_desktop_crtc[c].y;
|
|
crtc_info1->timestamp |= XRANDR_SETMODE_RESTORE_DESKTOP;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Use curent position
|
|
crtc_info1->x = crtc_info->x;
|
|
crtc_info1->y = crtc_info->y;
|
|
}
|
|
|
|
if (crtc_info0->mode != crtc_info1->mode || crtc_info0->width != crtc_info1->width || crtc_info0->height != crtc_info1->height || crtc_info0->x != crtc_info1->x || crtc_info0->y != crtc_info1->y)
|
|
crtc_info1->timestamp |= XRANDR_SETMODE_UPDATE_DESKTOP_CRTC;
|
|
}
|
|
else if (mode->type & MODE_DESKTOP && m_enable_screen_reordering && (crtc_info1->x != sp_desktop_crtc[c].x || crtc_info1->y != sp_desktop_crtc[c].y))
|
|
{
|
|
crtc_info1->x = sp_desktop_crtc[c].x;
|
|
crtc_info1->y = sp_desktop_crtc[c].y;
|
|
crtc_info1->timestamp |= (XRANDR_SETMODE_RESTORE_DESKTOP | XRANDR_SETMODE_UPDATE_REORDERING);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int c = 0; c < resources->ncrtc; c++)
|
|
{
|
|
// Original state
|
|
XRRCrtcInfo *crtc_info0 = &original_crtc[c];
|
|
// Modified state
|
|
XRRCrtcInfo *crtc_info1 = &global_crtc[c];
|
|
|
|
// Skip unused crtc
|
|
if (output_info->crtc != 0 && crtc_info0->mode != 0)
|
|
{
|
|
if ((flags & XRANDR_DISABLE_CRTC_RELOCATION) == 0 && (crtc_info1->timestamp & XRANDR_SETMODE_IS_DESKTOP) == 0)
|
|
{
|
|
// relocate crtc impacted by new width
|
|
if (crtc_info1->x >= crtc_info->x + (int)crtc_info->width)
|
|
{
|
|
crtc_info1->x += pxmode->width - crtc_info->width;
|
|
crtc_info1->timestamp |= XRANDR_SETMODE_UPDATE_OTHER_CRTC;
|
|
}
|
|
|
|
// relocate crtc impacted by new height
|
|
if (crtc_info1->y >= crtc_info->y + (int)crtc_info->height)
|
|
{
|
|
crtc_info1->y += pxmode->height - crtc_info->height;
|
|
crtc_info1->timestamp |= XRANDR_SETMODE_UPDATE_OTHER_CRTC;
|
|
}
|
|
}
|
|
|
|
// Calculate overall screen size based on crtcs placement
|
|
if (crtc_info1->x + crtc_info1->width > width)
|
|
width = crtc_info1->x + crtc_info1->width;
|
|
|
|
if (crtc_info1->y + crtc_info1->height > height)
|
|
height = crtc_info1->y + crtc_info1->height;
|
|
|
|
if (width > m_max_width)
|
|
{
|
|
log_error("XRANDR: <%d> (set_timing) [ERROR] width is above allowed maximum (%d > %d)\n", m_id, width, m_max_width);
|
|
width = m_max_width;
|
|
}
|
|
|
|
if (height > m_max_height)
|
|
{
|
|
log_error("XRANDR: <%d> (set_timing) [ERROR] height is above allowed maximum (%d > %d)\n", m_id, height, m_max_height);
|
|
height = m_max_height;
|
|
}
|
|
|
|
if (crtc_info1->timestamp & XRANDR_SETMODE_UPDATE_MASK)
|
|
log_verbose("XRANDR: <%d> (set_timing) crtc %d%s [%04lx] %ux%u+%d+%d --> [%04lx] %ux%u+%d+%d flags [%02lx]\n", m_id, c, crtc_info1->timestamp & 1 ? "*" : " ", crtc_info0->mode, crtc_info0->width, crtc_info0->height, crtc_info0->x, crtc_info0->y, crtc_info1->mode, crtc_info1->width, crtc_info1->height, crtc_info1->x, crtc_info1->y, crtc_info1->timestamp);
|
|
else if (crtc_info1->timestamp & XRANDR_SETMODE_INFO_MASK)
|
|
log_verbose("XRANDR: <%d> (set_timing) crtc %d%s [%04lx] %ux%u+%d+%d flags [%02lx]\n", m_id, c, crtc_info1->timestamp & 1 ? "*" : " ", crtc_info1->mode, crtc_info1->width, crtc_info1->height, crtc_info1->x, crtc_info1->y, crtc_info1->timestamp);
|
|
else
|
|
log_verbose("XRANDR: <%d> (set_timing) crtc %d [%04lx] %ux%u+%d+%d\n", m_id, c, crtc_info1->mode, crtc_info1->width, crtc_info1->height, crtc_info1->x, crtc_info1->y);
|
|
}
|
|
}
|
|
|
|
// Disable crtc with pending modification
|
|
for (int c = 0; c < resources->ncrtc; c++)
|
|
{
|
|
// Modified state
|
|
if (global_crtc[c].timestamp & XRANDR_SETMODE_UPDATE_MASK)
|
|
{
|
|
if (XRRSetCrtcConfig(m_pdisplay, resources, resources->crtcs[c], CurrentTime, 0, 0, None, RR_Rotate_0, NULL, 0) != RRSetConfigSuccess)
|
|
{
|
|
log_error("XRANDR: <%d> (set_timing) [ERROR] when disabling crtc %d\n", m_id, c);
|
|
ms_xerrors_flag = 0x01;
|
|
ms_xerrors |= ms_xerrors_flag;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set the framebuffer screen size to enable all crtc
|
|
if (ms_xerrors == 0)
|
|
{
|
|
log_verbose("XRANDR: <%d> (set_timing) setting screen size to %d x %d\n", m_id, width, height);
|
|
XSync(m_pdisplay, False);
|
|
ms_xerrors_flag = 0x02;
|
|
old_error_handler = XSetErrorHandler(error_handler);
|
|
XRRSetScreenSize(m_pdisplay, m_root, width, height, (int) ((25.4 * width) / 96.0), (int) ((25.4 * height) / 96.0));
|
|
XSync(m_pdisplay, False);
|
|
XSetErrorHandler(old_error_handler);
|
|
if (ms_xerrors & ms_xerrors_flag)
|
|
log_error("XRANDR: <%d> (set_timing) [ERROR] in %s\n", m_id, "XRRSetScreenSize");
|
|
}
|
|
|
|
// Refresh all crtc, switch modeline and set new placement
|
|
for (int c = 0; c < resources->ncrtc; c++)
|
|
{
|
|
// Modified state
|
|
XRRCrtcInfo *crtc_info1 = &global_crtc[c];
|
|
if (crtc_info1->timestamp & XRANDR_SETMODE_UPDATE_MASK)
|
|
{
|
|
if (crtc_info1->timestamp & XRANDR_SETMODE_IS_DESKTOP)
|
|
XFillRectangle(m_pdisplay, m_root, XCreateGC(m_pdisplay, m_root, 0, 0), crtc_info1->x, crtc_info1->y, crtc_info1->width, crtc_info1->height);
|
|
// enable crtc with updated parameters
|
|
XSync(m_pdisplay, False);
|
|
ms_xerrors_flag = 0x14;
|
|
old_error_handler = XSetErrorHandler(error_handler);
|
|
XRRSetCrtcConfig(m_pdisplay, resources, resources->crtcs[c], CurrentTime, crtc_info1->x, crtc_info1->y, crtc_info1->mode, crtc_info1->rotation, crtc_info1->outputs, crtc_info1->noutput);
|
|
XSync(m_pdisplay, False);
|
|
XSetErrorHandler(old_error_handler);
|
|
if (ms_xerrors & 0x10)
|
|
{
|
|
log_error("XRANDR: <%d> (set_timing) [ERROR] in %s crtc %d set modeline %04lx\n", m_id, "XRRSetCrtcConfig", c, crtc_info1->mode);
|
|
ms_xerrors &= 0xEF;
|
|
}
|
|
}
|
|
}
|
|
delete[]original_crtc;
|
|
delete[]global_crtc;
|
|
|
|
// Release X server, events can be processed now
|
|
XUngrabServer(m_pdisplay);
|
|
|
|
if (ms_xerrors & ms_xerrors_flag)
|
|
log_error("XRANDR: <%d> (set_timing) [ERROR] in %s\n", m_id, "XRRSetCrtcConfig");
|
|
|
|
// Recall the impacted crtc to settle parameters
|
|
XRRFreeCrtcInfo(crtc_info);
|
|
crtc_info = XRRGetCrtcInfo(m_pdisplay, resources, output_info->crtc);
|
|
|
|
// crtc config modeline change fail
|
|
if (crtc_info->mode == 0)
|
|
log_error("XRANDR: <%d> (set_timing) [ERROR] switching resolution failed, no modeline is set\n", m_id);
|
|
else
|
|
// save last crtc
|
|
m_last_crtc = *crtc_info;
|
|
|
|
XRRFreeCrtcInfo(crtc_info);
|
|
XRRFreeOutputInfo(output_info);
|
|
XRRFreeScreenResources(resources);
|
|
|
|
return (ms_xerrors == 0 && crtc_info->mode != 0);
|
|
}
|
|
|
|
//============================================================
|
|
// xrandr_timing::delete_mode
|
|
//============================================================
|
|
|
|
bool xrandr_timing::delete_mode(modeline *mode)
|
|
{
|
|
// Handle no screen detected case
|
|
if (m_desktop_output == -1)
|
|
{
|
|
log_error("XRANDR: <%d> (delete_mode) [ERROR] no screen detected\n", m_id);
|
|
return false;
|
|
}
|
|
|
|
if (!m_managed)
|
|
{
|
|
log_error("XRANDR: <%d> (delete_mode) [WARNING] this screen is managed by <%d>\n", m_id, sp_shared_screen_manager[m_desktop_output]);
|
|
return false;
|
|
}
|
|
|
|
if (!mode)
|
|
return false;
|
|
|
|
XRRScreenResources *resources = XRRGetScreenResourcesCurrent(m_pdisplay, m_root);
|
|
|
|
int total_xerrors = 0;
|
|
// Delete modeline
|
|
for (int m = 0; m < resources->nmode && mode->platform_data != 0; m++)
|
|
{
|
|
if (mode->platform_data == resources->modes[m].id)
|
|
{
|
|
XRROutputInfo *output_info = XRRGetOutputInfo(m_pdisplay, resources, resources->outputs[m_desktop_output]);
|
|
XRRCrtcInfo *crtc_info = XRRGetCrtcInfo(m_pdisplay, resources, output_info->crtc);
|
|
if (resources->modes[m].id == crtc_info->mode)
|
|
{
|
|
log_verbose("XRANDR: <%d> (delete_mode) [WARNING] modeline [%04lx] is currently active, restoring desktop mode first\n", m_id, resources->modes[m].id);
|
|
modeline desktop_mode = {};
|
|
desktop_mode.type |= MODE_DESKTOP;
|
|
if (!set_timing(&desktop_mode, 0))
|
|
{
|
|
log_error("XRANDR: <%d> (delete_mode) [ERROR] Could not restore desktop mode\n", m_id);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
XRRFreeCrtcInfo(crtc_info);
|
|
XRRFreeOutputInfo(output_info);
|
|
|
|
log_verbose("XRANDR: <%d> (delete_mode) remove mode %s\n", m_id, resources->modes[m].name);
|
|
|
|
XSync(m_pdisplay, False);
|
|
ms_xerrors = 0;
|
|
ms_xerrors_flag = 0x01;
|
|
old_error_handler = XSetErrorHandler(error_handler);
|
|
XRRDeleteOutputMode(m_pdisplay, resources->outputs[m_desktop_output], resources->modes[m].id);
|
|
if (ms_xerrors & ms_xerrors_flag)
|
|
{
|
|
log_error("XRANDR: <%d> (delete_mode) [ERROR] in %s\n", m_id, "XRRDeleteOutputMode");
|
|
total_xerrors++;
|
|
}
|
|
|
|
ms_xerrors_flag = 0x02;
|
|
XRRDestroyMode(m_pdisplay, resources->modes[m].id);
|
|
XSync(m_pdisplay, False);
|
|
XSetErrorHandler(old_error_handler);
|
|
if (ms_xerrors & ms_xerrors_flag)
|
|
{
|
|
log_error("XRANDR: <%d> (delete_mode) [ERROR] in %s\n", m_id, "XRRDestroyMode");
|
|
total_xerrors++;
|
|
}
|
|
mode->platform_data = 0;
|
|
}
|
|
}
|
|
|
|
XRRFreeScreenResources(resources);
|
|
|
|
return total_xerrors == 0;
|
|
}
|
|
|
|
//============================================================
|
|
// xrandr_timing::get_timing
|
|
//============================================================
|
|
|
|
bool xrandr_timing::get_timing(modeline *mode)
|
|
{
|
|
// Handle no screen detected case
|
|
if (m_desktop_output == -1)
|
|
{
|
|
log_error("XRANDR: <%d> (get_timing) [ERROR] no screen detected\n", m_id);
|
|
return false;
|
|
}
|
|
|
|
XRRScreenResources *resources = XRRGetScreenResourcesCurrent(m_pdisplay, m_root);
|
|
XRROutputInfo *output_info = XRRGetOutputInfo(m_pdisplay, resources, resources->outputs[m_desktop_output]);
|
|
|
|
// Cycle through the modelines and report them back to the display manager
|
|
if (m_video_modes_position < output_info->nmode)
|
|
{
|
|
for (int m = 0; m < resources->nmode; m++)
|
|
{
|
|
XRRModeInfo *pxmode = &resources->modes[m];
|
|
|
|
if (pxmode->id == output_info->modes[m_video_modes_position])
|
|
{
|
|
mode->platform_data = pxmode->id;
|
|
|
|
mode->pclock = pxmode->dotClock;
|
|
mode->hactive = pxmode->width;
|
|
mode->hbegin = pxmode->hSyncStart;
|
|
mode->hend = pxmode->hSyncEnd;
|
|
mode->htotal = pxmode->hTotal;
|
|
mode->vactive = pxmode->height;
|
|
mode->vbegin = pxmode->vSyncStart;
|
|
mode->vend = pxmode->vSyncEnd;
|
|
mode->vtotal = pxmode->vTotal;
|
|
mode->interlace = (pxmode->modeFlags & RR_Interlace) ? 1 : 0;
|
|
mode->doublescan = (pxmode->modeFlags & RR_DoubleScan) ? 1 : 0;
|
|
mode->hsync = (pxmode->modeFlags & RR_HSyncPositive) ? 1 : 0;
|
|
mode->vsync = (pxmode->modeFlags & RR_VSyncPositive) ? 1 : 0;
|
|
|
|
mode->hfreq = mode->pclock / mode->htotal;
|
|
mode->vfreq = mode->hfreq / mode->vtotal * (mode->interlace ? 2 : 1);
|
|
mode->refresh = mode->vfreq;
|
|
|
|
mode->width = pxmode->width;
|
|
mode->height = pxmode->height;
|
|
|
|
// Add the rotation flag from the crtc
|
|
mode->type |= m_crtc_flags;
|
|
|
|
mode->type |= CUSTOM_VIDEO_TIMING_XRANDR;
|
|
|
|
if (strncmp(pxmode->name, "SR-", 3) == 0)
|
|
log_verbose("XRANDR: <%d> (get_timing) [WARNING] modeline %s detected\n", m_id, pxmode->name);
|
|
|
|
// Add the desktop flag to desktop modeline
|
|
if (m_desktop_mode.id == pxmode->id)
|
|
mode->type |= MODE_DESKTOP;
|
|
|
|
log_verbose("XRANDR: <%d> (get_timing) mode %04lx %dx%d refresh %.6f added\n", m_id, pxmode->id, pxmode->width, pxmode->height, mode->vfreq);
|
|
}
|
|
}
|
|
m_video_modes_position++;
|
|
}
|
|
else
|
|
{
|
|
// Inititalise the position for the modeline list
|
|
m_video_modes_position = 0;
|
|
}
|
|
|
|
XRRFreeOutputInfo(output_info);
|
|
XRRFreeScreenResources(resources);
|
|
|
|
return true;
|
|
}
|
|
|
|
//============================================================
|
|
// xrandr_timing::process_modelist
|
|
//============================================================
|
|
|
|
bool xrandr_timing::process_modelist(std::vector<modeline *> modelist)
|
|
{
|
|
bool error = false;
|
|
bool result = false;
|
|
|
|
for (auto &mode : modelist)
|
|
{
|
|
if (mode->type & MODE_DELETE)
|
|
result = delete_mode(mode);
|
|
|
|
else if (mode->type & MODE_ADD)
|
|
result = add_mode(mode);
|
|
|
|
else if (mode->type & MODE_UPDATE)
|
|
result = update_mode(mode);
|
|
|
|
if (!result)
|
|
{
|
|
mode->type |= MODE_ERROR;
|
|
error = true;
|
|
}
|
|
else
|
|
// succeed
|
|
mode->type &= ~MODE_ERROR;
|
|
}
|
|
|
|
return !error;
|
|
}
|