Use more aggressive xlib locking following the advice of the multithreaded X manpages (needs work).

This commit is contained in:
the_fiddler 2009-11-02 22:37:13 +00:00
parent 2a1924c62c
commit 974641086c
7 changed files with 158 additions and 99 deletions

View file

@ -1,4 +1,4 @@
#region --- License --- #region --- License ---
/* Copyright (c) 2006, 2007 Stefanos Apostolopoulos /* Copyright (c) 2006, 2007 Stefanos Apostolopoulos
* Contributions from Erik Ylvisaker * Contributions from Erik Ylvisaker
* See license.txt for license info * See license.txt for license info
@ -1589,6 +1589,23 @@ XF86VidModeGetGammaRampSize(
} }
} }
*/ */
struct XLock : IDisposable
{
readonly IntPtr Display;
public XLock(IntPtr display)
: this()
{
Functions.XLockDisplay(display);
Display = display;
}
public void Dispose()
{
Functions.XUnlockDisplay(Display);
}
}
} }
#pragma warning restore 3019 #pragma warning restore 3019

View file

@ -1,4 +1,4 @@
#region --- License --- #region --- License ---
/* Licensed under the MIT/X11 license. /* Licensed under the MIT/X11 license.
* Copyright (c) 2006-2008 the OpenTK Team. * Copyright (c) 2006-2008 the OpenTK Team.
* This notice may not be removed from any source distribution. * This notice may not be removed from any source distribution.
@ -77,10 +77,18 @@ namespace OpenTK.Platform.X11
internal static partial class Functions internal static partial class Functions
{ {
public static readonly object Lock = new object(); public static readonly object Lock = API.Lock;
[DllImport("libX11", EntryPoint = "XOpenDisplay")] [DllImport("libX11", EntryPoint = "XOpenDisplay")]
public extern static IntPtr XOpenDisplay(IntPtr display); extern static IntPtr sys_XOpenDisplay(IntPtr display);
public static IntPtr XOpenDisplay(IntPtr display)
{
lock (Lock)
{
return sys_XOpenDisplay(display);
}
}
[DllImport("libX11", EntryPoint = "XCloseDisplay")] [DllImport("libX11", EntryPoint = "XCloseDisplay")]
public extern static int XCloseDisplay(IntPtr display); public extern static int XCloseDisplay(IntPtr display);
[DllImport("libX11", EntryPoint = "XSynchronize")] [DllImport("libX11", EntryPoint = "XSynchronize")]

View file

@ -49,18 +49,18 @@ namespace OpenTK.Platform.X11
// Do not move this lower, as almost everything requires the Display // Do not move this lower, as almost everything requires the Display
// property to be correctly set. // property to be correctly set.
Display = ((X11WindowInfo)window).Display; Display = ((X11WindowInfo)window).Display;
currentWindow = (X11WindowInfo)window; currentWindow = (X11WindowInfo)window;
currentWindow.VisualInfo = SelectVisual(mode, currentWindow); currentWindow.VisualInfo = SelectVisual(mode, currentWindow);
ContextHandle shareHandle = shared != null ? ContextHandle shareHandle = shared != null ?
(shared as IGraphicsContextInternal).Context : (ContextHandle)IntPtr.Zero; (shared as IGraphicsContextInternal).Context : (ContextHandle)IntPtr.Zero;
Debug.Write("Creating X11GLContext context: "); Debug.Write("Creating X11GLContext context: ");
Debug.Write(direct ? "direct, " : "indirect, "); Debug.Write(direct ? "direct, " : "indirect, ");
Debug.WriteLine(shareHandle.Handle == IntPtr.Zero ? "not shared... " : Debug.WriteLine(shareHandle.Handle == IntPtr.Zero ? "not shared... " :
String.Format("shared with ({0})... ", shareHandle)); String.Format("shared with ({0})... ", shareHandle));
if (!glx_loaded) if (!glx_loaded)
{ {
Debug.WriteLine("Creating temporary context to load GLX extensions."); Debug.WriteLine("Creating temporary context to load GLX extensions.");
@ -130,7 +130,10 @@ namespace OpenTK.Platform.X11
else else
Debug.WriteLine("success!"); Debug.WriteLine("success!");
Functions.XFree((IntPtr)fbconfigs); using (new XLock(Display))
{
Functions.XFree((IntPtr)fbconfigs);
}
} }
} }
} }

View file

@ -122,7 +122,7 @@ namespace OpenTK.Platform.X11
Debug.Indent(); Debug.Indent();
lock (API.Lock) using (new XLock(window.Display))
{ {
if (!mode.Index.HasValue) if (!mode.Index.HasValue)
throw new GraphicsModeException("Invalid or unsupported GraphicsMode."); throw new GraphicsModeException("Invalid or unsupported GraphicsMode.");
@ -164,7 +164,7 @@ namespace OpenTK.Platform.X11
hints.base_width = width; hints.base_width = width;
hints.base_height = height; hints.base_height = height;
hints.flags = (IntPtr)(XSizeHintsFlags.PSize | XSizeHintsFlags.PPosition); hints.flags = (IntPtr)(XSizeHintsFlags.PSize | XSizeHintsFlags.PPosition);
lock (API.Lock) using (new XLock(window.Display))
{ {
Functions.XSetWMNormalHints(window.Display, window.WindowHandle, ref hints); Functions.XSetWMNormalHints(window.Display, window.WindowHandle, ref hints);
@ -194,21 +194,18 @@ namespace OpenTK.Platform.X11
Debug.Indent(); Debug.Indent();
// Open a display connection to the X server, and obtain the screen and root window. // Open a display connection to the X server, and obtain the screen and root window.
window.Display = API.DefaultDisplay; window.Display = Functions.XOpenDisplay(IntPtr.Zero);
//window.Display = API.DefaultDisplay;
if (window.Display == IntPtr.Zero) if (window.Display == IntPtr.Zero)
throw new Exception("Could not open connection to X"); throw new Exception("Could not open connection to X");
try using (new XLock(window.Display))
{ {
Functions.XLockDisplay(window.Display); Functions.XLockDisplay(window.Display);
window.Screen = Functions.XDefaultScreen(window.Display); //API.DefaultScreen; window.Screen = Functions.XDefaultScreen(window.Display); //API.DefaultScreen;
window.RootWindow = Functions.XRootWindow(window.Display, window.Screen); // API.RootWindow; window.RootWindow = Functions.XRootWindow(window.Display, window.Screen); // API.RootWindow;
} }
finally
{
Functions.XUnlockDisplay(window.Display);
}
Debug.Print("Display: {0}, Screen {1}, Root window: {2}", window.Display, window.Screen, Debug.Print("Display: {0}, Screen {1}, Root window: {2}", window.Display, window.Screen,
window.RootWindow); window.RootWindow);
@ -232,28 +229,30 @@ namespace OpenTK.Platform.X11
/// </summary> /// </summary>
private void RegisterAtoms(X11WindowInfo window) private void RegisterAtoms(X11WindowInfo window)
{ {
Debug.WriteLine("Registering atoms."); using (new XLock(window.Display))
_atom_wm_destroy = Functions.XInternAtom(window.Display, "WM_DELETE_WINDOW", true); {
Debug.WriteLine("Registering atoms.");
_atom_wm_destroy = Functions.XInternAtom(window.Display, "WM_DELETE_WINDOW", true);
_atom_net_wm_state = Functions.XInternAtom(window.Display, "_NET_WM_STATE", false); _atom_net_wm_state = Functions.XInternAtom(window.Display, "_NET_WM_STATE", false);
_atom_net_wm_state_minimized = Functions.XInternAtom(window.Display, "_NET_WM_STATE_MINIMIZED", false); _atom_net_wm_state_minimized = Functions.XInternAtom(window.Display, "_NET_WM_STATE_MINIMIZED", false);
_atom_net_wm_state_fullscreen = Functions.XInternAtom(window.Display, "_NET_WM_STATE_FULLSCREEN", false); _atom_net_wm_state_fullscreen = Functions.XInternAtom(window.Display, "_NET_WM_STATE_FULLSCREEN", false);
_atom_net_wm_state_maximized_horizontal = _atom_net_wm_state_maximized_horizontal =
Functions.XInternAtom(window.Display, "_NET_WM_STATE_MAXIMIZED_HORZ", false); Functions.XInternAtom(window.Display, "_NET_WM_STATE_MAXIMIZED_HORZ", false);
_atom_net_wm_state_maximized_vertical = _atom_net_wm_state_maximized_vertical =
Functions.XInternAtom(window.Display, "_NET_WM_STATE_MAXIMIZED_VERT", false); Functions.XInternAtom(window.Display, "_NET_WM_STATE_MAXIMIZED_VERT", false);
_atom_net_wm_allowed_actions = _atom_net_wm_allowed_actions =
Functions.XInternAtom(window.Display, "_NET_WM_ALLOWED_ACTIONS", false); Functions.XInternAtom(window.Display, "_NET_WM_ALLOWED_ACTIONS", false);
_atom_net_wm_action_resize = _atom_net_wm_action_resize =
Functions.XInternAtom(window.Display, "_NET_WM_ACTION_RESIZE", false); Functions.XInternAtom(window.Display, "_NET_WM_ACTION_RESIZE", false);
_atom_net_wm_action_maximize_horizontally = _atom_net_wm_action_maximize_horizontally =
Functions.XInternAtom(window.Display, "_NET_WM_ACTION_MAXIMIZE_HORZ", false); Functions.XInternAtom(window.Display, "_NET_WM_ACTION_MAXIMIZE_HORZ", false);
_atom_net_wm_action_maximize_vertically = _atom_net_wm_action_maximize_vertically =
Functions.XInternAtom(window.Display, "_NET_WM_ACTION_MAXIMIZE_VERT", false); Functions.XInternAtom(window.Display, "_NET_WM_ACTION_MAXIMIZE_VERT", false);
_atom_net_wm_icon = _atom_net_wm_icon =
Functions.XInternAtom(window.Display,"_NEW_WM_ICON", false); Functions.XInternAtom(window.Display, "_NEW_WM_ICON", false);
// string[] atom_names = new string[] // string[] atom_names = new string[]
// { // {
@ -266,6 +265,7 @@ namespace OpenTK.Platform.X11
// int offset = 0; // int offset = 0;
// //WMTitle = atoms[offset++]; // //WMTitle = atoms[offset++];
// //UTF8String = atoms[offset++]; // //UTF8String = atoms[offset++];
}
} }
#endregion #endregion

View file

@ -1,4 +1,4 @@
#region --- License --- #region --- License ---
/* Licensed under the MIT/X11 license. /* Licensed under the MIT/X11 license.
* Copyright (c) 2006-2008 the OpenTK Team. * Copyright (c) 2006-2008 the OpenTK Team.
* This notice may not be removed from any source distribution. * This notice may not be removed from any source distribution.
@ -35,22 +35,23 @@ namespace OpenTK.Platform.X11
public GraphicsMode SelectGraphicsMode(ColorFormat color, int depth, int stencil, int samples, ColorFormat accum, public GraphicsMode SelectGraphicsMode(ColorFormat color, int depth, int stencil, int samples, ColorFormat accum,
int buffers, bool stereo) int buffers, bool stereo)
{ {
GraphicsMode gfx; // The actual GraphicsMode that will be selected. GraphicsMode gfx;
// The actual GraphicsMode that will be selected.
IntPtr visual = IntPtr.Zero; IntPtr visual = IntPtr.Zero;
IntPtr display = API.DefaultDisplay; IntPtr display = API.DefaultDisplay;
// Try to select a visual using Glx.ChooseFBConfig and Glx.GetVisualFromFBConfig. // Try to select a visual using Glx.ChooseFBConfig and Glx.GetVisualFromFBConfig.
// This is only supported on GLX 1.3 - if it fails, fall back to Glx.ChooseVisual. // This is only supported on GLX 1.3 - if it fails, fall back to Glx.ChooseVisual.
visual = SelectVisualUsingFBConfig(color, depth, stencil, samples, accum, buffers, stereo); visual = SelectVisualUsingFBConfig(color, depth, stencil, samples, accum, buffers, stereo);
if (visual == IntPtr.Zero) if (visual == IntPtr.Zero)
visual = SelectVisualUsingChooseVisual(color, depth, stencil, samples, accum, buffers, stereo); visual = SelectVisualUsingChooseVisual(color, depth, stencil, samples, accum, buffers, stereo);
if (visual == IntPtr.Zero) if (visual == IntPtr.Zero)
throw new GraphicsContextException("Requested GraphicsMode not available."); throw new GraphicsContextException("Requested GraphicsMode not available.");
XVisualInfo info = (XVisualInfo)Marshal.PtrToStructure(visual, typeof(XVisualInfo)); XVisualInfo info = (XVisualInfo)Marshal.PtrToStructure(visual, typeof(XVisualInfo));
// See what we *really* got: // See what we *really* got:
int r, g, b, a; int r, g, b, a;
Glx.GetConfig(display, ref info, GLXAttribute.ALPHA_SIZE, out a); Glx.GetConfig(display, ref info, GLXAttribute.ALPHA_SIZE, out a);
@ -66,15 +67,19 @@ namespace OpenTK.Platform.X11
Glx.GetConfig(display, ref info, GLXAttribute.STENCIL_SIZE, out stencil); Glx.GetConfig(display, ref info, GLXAttribute.STENCIL_SIZE, out stencil);
Glx.GetConfig(display, ref info, GLXAttribute.SAMPLES, out samples); Glx.GetConfig(display, ref info, GLXAttribute.SAMPLES, out samples);
Glx.GetConfig(display, ref info, GLXAttribute.DOUBLEBUFFER, out buffers); Glx.GetConfig(display, ref info, GLXAttribute.DOUBLEBUFFER, out buffers);
++buffers; // the above lines returns 0 - false and 1 - true. ++buffers;
// the above lines returns 0 - false and 1 - true.
int st; int st;
Glx.GetConfig(display, ref info, GLXAttribute.STEREO, out st); Glx.GetConfig(display, ref info, GLXAttribute.STEREO, out st);
stereo = st != 0; stereo = st != 0;
gfx = new GraphicsMode(info.VisualID, new ColorFormat(r, g, b, a), depth, stencil, samples, gfx = new GraphicsMode(info.VisualID, new ColorFormat(r, g, b, a), depth, stencil, samples,
new ColorFormat(ar, ag, ab, aa), buffers, stereo); new ColorFormat(ar, ag, ab, aa), buffers, stereo);
Functions.XFree(visual); using (new XLock(display))
{
Functions.XFree(visual);
}
return gfx; return gfx;
} }

View file

@ -1,4 +1,4 @@
#region --- License --- #region --- License ---
/* Copyright (c) 2006, 2007 Stefanos Apostolopoulos /* Copyright (c) 2006, 2007 Stefanos Apostolopoulos
* See license.txt for license info * See license.txt for license info
*/ */
@ -53,38 +53,41 @@ namespace OpenTK.Platform.X11
//window = new X11WindowInfo(attach); //window = new X11WindowInfo(attach);
X11WindowInfo window = (X11WindowInfo)attach; X11WindowInfo window = (X11WindowInfo)attach;
// Init mouse // Init mouse
mouse.Description = "Default X11 mouse"; mouse.Description = "Default X11 mouse";
mouse.DeviceID = IntPtr.Zero; mouse.DeviceID = IntPtr.Zero;
mouse.NumberOfButtons = 5; mouse.NumberOfButtons = 5;
mouse.NumberOfWheels = 1; mouse.NumberOfWheels = 1;
dummy_mice_list.Add(mouse); dummy_mice_list.Add(mouse);
// Init keyboard using (new XLock(window.Display))
API.DisplayKeycodes(window.Display, ref firstKeyCode, ref lastKeyCode); {
Debug.Print("First keycode: {0}, last {1}", firstKeyCode, lastKeyCode); // Init keyboard
API.DisplayKeycodes(window.Display, ref firstKeyCode, ref lastKeyCode);
IntPtr keysym_ptr = API.GetKeyboardMapping(window.Display, (byte)firstKeyCode, Debug.Print("First keycode: {0}, last {1}", firstKeyCode, lastKeyCode);
lastKeyCode - firstKeyCode + 1, ref keysyms_per_keycode);
Debug.Print("{0} keysyms per keycode.", keysyms_per_keycode); IntPtr keysym_ptr = API.GetKeyboardMapping(window.Display, (byte)firstKeyCode,
lastKeyCode - firstKeyCode + 1, ref keysyms_per_keycode);
keysyms = new IntPtr[(lastKeyCode - firstKeyCode + 1) * keysyms_per_keycode]; Debug.Print("{0} keysyms per keycode.", keysyms_per_keycode);
Marshal.PtrToStructure(keysym_ptr, keysyms);
API.Free(keysym_ptr); keysyms = new IntPtr[(lastKeyCode - firstKeyCode + 1) * keysyms_per_keycode];
Marshal.PtrToStructure(keysym_ptr, keysyms);
KeyboardDevice kb = new KeyboardDevice(); API.Free(keysym_ptr);
keyboard.Description = "Default X11 keyboard";
keyboard.NumberOfKeys = lastKeyCode - firstKeyCode + 1; KeyboardDevice kb = new KeyboardDevice();
keyboard.DeviceID = IntPtr.Zero; keyboard.Description = "Default X11 keyboard";
dummy_keyboard_list.Add(keyboard); keyboard.NumberOfKeys = lastKeyCode - firstKeyCode + 1;
keyboard.DeviceID = IntPtr.Zero;
// Request that auto-repeat is only set on devices that support it physically. dummy_keyboard_list.Add(keyboard);
// This typically means that it's turned off for keyboards (which is what we want).
// We prefer this method over XAutoRepeatOff/On, because the latter needs to // Request that auto-repeat is only set on devices that support it physically.
// be reset before the program exits. // This typically means that it's turned off for keyboards (which is what we want).
bool supported; // We prefer this method over XAutoRepeatOff/On, because the latter needs to
Functions.XkbSetDetectableAutoRepeat(window.Display, true, out supported); // be reset before the program exits.
bool supported;
Functions.XkbSetDetectableAutoRepeat(window.Display, true, out supported);
}
Debug.Unindent(); Debug.Unindent();
} }

View file

@ -1,4 +1,4 @@
#region --- License --- #region --- License ---
/* Licensed under the MIT/X11 license. /* Licensed under the MIT/X11 license.
* Copyright (c) 2006-2008 the OpenTK Team. * Copyright (c) 2006-2008 the OpenTK Team.
* This notice may not be removed from any source distribution. * This notice may not be removed from any source distribution.
@ -42,16 +42,19 @@ namespace OpenTK.Platform.X11
for (int screen = 0; screen < API.ScreenCount; screen++) for (int screen = 0; screen < API.ScreenCount; screen++)
{ {
IntPtr timestamp_of_last_update; IntPtr timestamp_of_last_update;
Functions.XRRTimes(API.DefaultDisplay, screen, out timestamp_of_last_update); using (new XLock(API.DefaultDisplay))
{
Functions.XRRTimes(API.DefaultDisplay, screen, out timestamp_of_last_update);
}
lastConfigUpdate.Add(timestamp_of_last_update); lastConfigUpdate.Add(timestamp_of_last_update);
List<DisplayResolution> available_res = new List<DisplayResolution>(); List<DisplayResolution> available_res = new List<DisplayResolution>();
// Add info for a new screen. // Add info for a new screen.
screenResolutionToIndex.Add(new Dictionary<DisplayResolution, int>()); screenResolutionToIndex.Add(new Dictionary<DisplayResolution, int>());
int[] depths = FindAvailableDepths(screen); int[] depths = FindAvailableDepths(screen);
int resolution_count = 0; int resolution_count = 0;
foreach (XRRScreenSize size in FindAvailableResolutions(screen)) foreach (XRRScreenSize size in FindAvailableResolutions(screen))
{ {
@ -60,7 +63,11 @@ namespace OpenTK.Platform.X11
Debug.Print("[Warning] XRandR returned an invalid resolution ({0}) for display device {1}", size, screen); Debug.Print("[Warning] XRandR returned an invalid resolution ({0}) for display device {1}", size, screen);
continue; continue;
} }
short[] rates = Functions.XRRRates(API.DefaultDisplay, screen, resolution_count); short[] rates = null;
using (new XLock(API.DefaultDisplay))
{
rates = Functions.XRRRates(API.DefaultDisplay, screen, resolution_count);
}
// It seems that XRRRates returns 0 for modes that are larger than the screen // It seems that XRRRates returns 0 for modes that are larger than the screen
// can support, as well as for all supported modes. On Ubuntu 7.10 the tool // can support, as well as for all supported modes. On Ubuntu 7.10 the tool
@ -114,7 +121,10 @@ namespace OpenTK.Platform.X11
static int[] FindAvailableDepths(int screen) static int[] FindAvailableDepths(int screen)
{ {
return Functions.XListDepths(API.DefaultDisplay, screen); using (new XLock(API.DefaultDisplay))
{
return Functions.XListDepths(API.DefaultDisplay, screen);
}
} }
#endregion #endregion
@ -123,7 +133,11 @@ namespace OpenTK.Platform.X11
static XRRScreenSize[] FindAvailableResolutions(int screen) static XRRScreenSize[] FindAvailableResolutions(int screen)
{ {
XRRScreenSize[] resolutions = Functions.XRRSizes(API.DefaultDisplay, screen); XRRScreenSize[] resolutions = null;
using (new XLock(API.DefaultDisplay))
{
resolutions = Functions.XRRSizes(API.DefaultDisplay, screen);
}
if (resolutions == null) if (resolutions == null)
throw new NotSupportedException("XRandR extensions not available."); throw new NotSupportedException("XRandR extensions not available.");
return resolutions; return resolutions;
@ -135,12 +149,15 @@ namespace OpenTK.Platform.X11
static float FindCurrentRefreshRate(int screen) static float FindCurrentRefreshRate(int screen)
{ {
IntPtr screen_config = Functions.XRRGetScreenInfo(API.DefaultDisplay, Functions.XRootWindow(API.DefaultDisplay, screen)); short rate = 0;
ushort rotation = 0; using (new XLock(API.DefaultDisplay))
int size = Functions.XRRConfigCurrentConfiguration(screen_config, out rotation); {
short rate = Functions.XRRConfigCurrentRate(screen_config); IntPtr screen_config = Functions.XRRGetScreenInfo(API.DefaultDisplay, Functions.XRootWindow(API.DefaultDisplay, screen));
Functions.XRRFreeScreenConfigInfo(screen_config); ushort rotation = 0;
int size = Functions.XRRConfigCurrentConfiguration(screen_config, out rotation);
rate = Functions.XRRConfigCurrentRate(screen_config);
Functions.XRRFreeScreenConfigInfo(screen_config);
}
return (float)rate; return (float)rate;
} }
@ -150,7 +167,10 @@ namespace OpenTK.Platform.X11
private static int FindCurrentDepth(int screen) private static int FindCurrentDepth(int screen)
{ {
return (int)Functions.XDefaultDepth(API.DefaultDisplay, screen); using (new XLock(API.DefaultDisplay))
{
return (int)Functions.XDefaultDepth(API.DefaultDisplay, screen);
}
} }
#endregion #endregion
@ -163,24 +183,27 @@ namespace OpenTK.Platform.X11
{ {
// If resolution == null, restore to default resolution (new_resolution_index = 0). // If resolution == null, restore to default resolution (new_resolution_index = 0).
int screen = deviceToScreen[device]; using (new XLock(API.DefaultDisplay))
IntPtr root = Functions.XRootWindow(API.DefaultDisplay, screen); {
IntPtr screen_config = Functions.XRRGetScreenInfo(API.DefaultDisplay, root); int screen = deviceToScreen[device];
IntPtr root = Functions.XRootWindow(API.DefaultDisplay, screen);
ushort current_rotation; IntPtr screen_config = Functions.XRRGetScreenInfo(API.DefaultDisplay, root);
int current_resolution_index = Functions.XRRConfigCurrentConfiguration(screen_config, out current_rotation);
int new_resolution_index; ushort current_rotation;
if (resolution != null) int current_resolution_index = Functions.XRRConfigCurrentConfiguration(screen_config, out current_rotation);
new_resolution_index = screenResolutionToIndex[screen] int new_resolution_index;
[new DisplayResolution(0, 0, resolution.Width, resolution.Height, resolution.BitsPerPixel, 0)]; if (resolution != null)
else new_resolution_index = screenResolutionToIndex[screen]
new_resolution_index = deviceToDefaultResolution[device]; [new DisplayResolution(0, 0, resolution.Width, resolution.Height, resolution.BitsPerPixel, 0)];
else
Debug.Print("Changing size of screen {0} from {1} to {2}", new_resolution_index = deviceToDefaultResolution[device];
screen, current_resolution_index, new_resolution_index);
Debug.Print("Changing size of screen {0} from {1} to {2}",
return 0 == Functions.XRRSetScreenConfigAndRate(API.DefaultDisplay, screen_config, root, new_resolution_index, screen, current_resolution_index, new_resolution_index);
current_rotation, (short)(resolution != null ? resolution.RefreshRate : 0), lastConfigUpdate[screen]);
return 0 == Functions.XRRSetScreenConfigAndRate(API.DefaultDisplay, screen_config, root, new_resolution_index,
current_rotation, (short)(resolution != null ? resolution.RefreshRate : 0), lastConfigUpdate[screen]);
}
} }
public bool TryRestoreResolution(DisplayDevice device) public bool TryRestoreResolution(DisplayDevice device)