From 974641086c57dfcfe29405c9a48c5d3589ef2a55 Mon Sep 17 00:00:00 2001 From: the_fiddler Date: Mon, 2 Nov 2009 22:37:13 +0000 Subject: [PATCH] Use more aggressive xlib locking following the advice of the multithreaded X manpages (needs work). --- Source/OpenTK/Platform/X11/API.cs | 19 +++- Source/OpenTK/Platform/X11/Functions.cs | 14 ++- Source/OpenTK/Platform/X11/X11GLContext.cs | 13 ++- Source/OpenTK/Platform/X11/X11GLNative.cs | 38 ++++---- Source/OpenTK/Platform/X11/X11GraphicsMode.cs | 25 +++-- Source/OpenTK/Platform/X11/X11Input.cs | 57 ++++++------ .../Platform/X11/X11XrandrDisplayDevice.cs | 91 ++++++++++++------- 7 files changed, 158 insertions(+), 99 deletions(-) diff --git a/Source/OpenTK/Platform/X11/API.cs b/Source/OpenTK/Platform/X11/API.cs index 59831011..8be467f9 100644 --- a/Source/OpenTK/Platform/X11/API.cs +++ b/Source/OpenTK/Platform/X11/API.cs @@ -1,4 +1,4 @@ -#region --- License --- +#region --- License --- /* Copyright (c) 2006, 2007 Stefanos Apostolopoulos * Contributions from Erik Ylvisaker * 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 diff --git a/Source/OpenTK/Platform/X11/Functions.cs b/Source/OpenTK/Platform/X11/Functions.cs index 58938955..6083b371 100644 --- a/Source/OpenTK/Platform/X11/Functions.cs +++ b/Source/OpenTK/Platform/X11/Functions.cs @@ -1,4 +1,4 @@ -#region --- License --- +#region --- License --- /* Licensed under the MIT/X11 license. * Copyright (c) 2006-2008 the OpenTK Team. * This notice may not be removed from any source distribution. @@ -77,10 +77,18 @@ namespace OpenTK.Platform.X11 internal static partial class Functions { - public static readonly object Lock = new object(); + public static readonly object Lock = API.Lock; [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")] public extern static int XCloseDisplay(IntPtr display); [DllImport("libX11", EntryPoint = "XSynchronize")] diff --git a/Source/OpenTK/Platform/X11/X11GLContext.cs b/Source/OpenTK/Platform/X11/X11GLContext.cs index 4f247fa8..aa0e0591 100644 --- a/Source/OpenTK/Platform/X11/X11GLContext.cs +++ b/Source/OpenTK/Platform/X11/X11GLContext.cs @@ -49,18 +49,18 @@ namespace OpenTK.Platform.X11 // Do not move this lower, as almost everything requires the Display // property to be correctly set. Display = ((X11WindowInfo)window).Display; - + currentWindow = (X11WindowInfo)window; currentWindow.VisualInfo = SelectVisual(mode, currentWindow); - + ContextHandle shareHandle = shared != null ? (shared as IGraphicsContextInternal).Context : (ContextHandle)IntPtr.Zero; - + Debug.Write("Creating X11GLContext context: "); Debug.Write(direct ? "direct, " : "indirect, "); Debug.WriteLine(shareHandle.Handle == IntPtr.Zero ? "not shared... " : String.Format("shared with ({0})... ", shareHandle)); - + if (!glx_loaded) { Debug.WriteLine("Creating temporary context to load GLX extensions."); @@ -130,7 +130,10 @@ namespace OpenTK.Platform.X11 else Debug.WriteLine("success!"); - Functions.XFree((IntPtr)fbconfigs); + using (new XLock(Display)) + { + Functions.XFree((IntPtr)fbconfigs); + } } } } diff --git a/Source/OpenTK/Platform/X11/X11GLNative.cs b/Source/OpenTK/Platform/X11/X11GLNative.cs index 6a7d469b..30e5979c 100644 --- a/Source/OpenTK/Platform/X11/X11GLNative.cs +++ b/Source/OpenTK/Platform/X11/X11GLNative.cs @@ -122,7 +122,7 @@ namespace OpenTK.Platform.X11 Debug.Indent(); - lock (API.Lock) + using (new XLock(window.Display)) { if (!mode.Index.HasValue) throw new GraphicsModeException("Invalid or unsupported GraphicsMode."); @@ -164,7 +164,7 @@ namespace OpenTK.Platform.X11 hints.base_width = width; hints.base_height = height; hints.flags = (IntPtr)(XSizeHintsFlags.PSize | XSizeHintsFlags.PPosition); - lock (API.Lock) + using (new XLock(window.Display)) { Functions.XSetWMNormalHints(window.Display, window.WindowHandle, ref hints); @@ -194,21 +194,18 @@ namespace OpenTK.Platform.X11 Debug.Indent(); // 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) throw new Exception("Could not open connection to X"); - try + using (new XLock(window.Display)) { Functions.XLockDisplay(window.Display); window.Screen = Functions.XDefaultScreen(window.Display); //API.DefaultScreen; 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, window.RootWindow); @@ -232,28 +229,30 @@ namespace OpenTK.Platform.X11 /// private void RegisterAtoms(X11WindowInfo window) { - Debug.WriteLine("Registering atoms."); - _atom_wm_destroy = Functions.XInternAtom(window.Display, "WM_DELETE_WINDOW", true); + using (new XLock(window.Display)) + { + 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_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_maximized_horizontal = + _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_maximized_horizontal = 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); _atom_net_wm_allowed_actions = 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); - _atom_net_wm_action_maximize_horizontally = + _atom_net_wm_action_maximize_horizontally = 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); _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[] // { @@ -266,6 +265,7 @@ namespace OpenTK.Platform.X11 // int offset = 0; // //WMTitle = atoms[offset++]; // //UTF8String = atoms[offset++]; + } } #endregion diff --git a/Source/OpenTK/Platform/X11/X11GraphicsMode.cs b/Source/OpenTK/Platform/X11/X11GraphicsMode.cs index 118284ad..e5c75532 100644 --- a/Source/OpenTK/Platform/X11/X11GraphicsMode.cs +++ b/Source/OpenTK/Platform/X11/X11GraphicsMode.cs @@ -1,4 +1,4 @@ -#region --- License --- +#region --- License --- /* Licensed under the MIT/X11 license. * Copyright (c) 2006-2008 the OpenTK Team. * 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, 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 display = API.DefaultDisplay; - + // 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. visual = SelectVisualUsingFBConfig(color, depth, stencil, samples, accum, buffers, stereo); if (visual == IntPtr.Zero) visual = SelectVisualUsingChooseVisual(color, depth, stencil, samples, accum, buffers, stereo); - + if (visual == IntPtr.Zero) throw new GraphicsContextException("Requested GraphicsMode not available."); - + XVisualInfo info = (XVisualInfo)Marshal.PtrToStructure(visual, typeof(XVisualInfo)); - + // See what we *really* got: int r, g, b, 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.SAMPLES, out samples); 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; Glx.GetConfig(display, ref info, GLXAttribute.STEREO, out st); stereo = st != 0; gfx = new GraphicsMode(info.VisualID, new ColorFormat(r, g, b, a), depth, stencil, samples, - new ColorFormat(ar, ag, ab, aa), buffers, stereo); - - Functions.XFree(visual); + new ColorFormat(ar, ag, ab, aa), buffers, stereo); + + using (new XLock(display)) + { + Functions.XFree(visual); + } return gfx; } diff --git a/Source/OpenTK/Platform/X11/X11Input.cs b/Source/OpenTK/Platform/X11/X11Input.cs index 7df010aa..a0d597b2 100644 --- a/Source/OpenTK/Platform/X11/X11Input.cs +++ b/Source/OpenTK/Platform/X11/X11Input.cs @@ -1,4 +1,4 @@ -#region --- License --- +#region --- License --- /* Copyright (c) 2006, 2007 Stefanos Apostolopoulos * See license.txt for license info */ @@ -53,38 +53,41 @@ namespace OpenTK.Platform.X11 //window = new X11WindowInfo(attach); X11WindowInfo window = (X11WindowInfo)attach; - + // Init mouse mouse.Description = "Default X11 mouse"; mouse.DeviceID = IntPtr.Zero; mouse.NumberOfButtons = 5; mouse.NumberOfWheels = 1; dummy_mice_list.Add(mouse); - - // Init keyboard - API.DisplayKeycodes(window.Display, ref firstKeyCode, ref lastKeyCode); - Debug.Print("First keycode: {0}, last {1}", firstKeyCode, lastKeyCode); - - IntPtr keysym_ptr = API.GetKeyboardMapping(window.Display, (byte)firstKeyCode, - lastKeyCode - firstKeyCode + 1, ref keysyms_per_keycode); - Debug.Print("{0} keysyms per keycode.", keysyms_per_keycode); - - keysyms = new IntPtr[(lastKeyCode - firstKeyCode + 1) * keysyms_per_keycode]; - Marshal.PtrToStructure(keysym_ptr, keysyms); - API.Free(keysym_ptr); - - KeyboardDevice kb = new KeyboardDevice(); - keyboard.Description = "Default X11 keyboard"; - keyboard.NumberOfKeys = lastKeyCode - firstKeyCode + 1; - keyboard.DeviceID = IntPtr.Zero; - dummy_keyboard_list.Add(keyboard); - - // Request that auto-repeat is only set on devices that support it physically. - // 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 - // be reset before the program exits. - bool supported; - Functions.XkbSetDetectableAutoRepeat(window.Display, true, out supported); + + using (new XLock(window.Display)) + { + // Init keyboard + API.DisplayKeycodes(window.Display, ref firstKeyCode, ref lastKeyCode); + Debug.Print("First keycode: {0}, last {1}", firstKeyCode, lastKeyCode); + + IntPtr keysym_ptr = API.GetKeyboardMapping(window.Display, (byte)firstKeyCode, + lastKeyCode - firstKeyCode + 1, ref keysyms_per_keycode); + Debug.Print("{0} keysyms per keycode.", keysyms_per_keycode); + + keysyms = new IntPtr[(lastKeyCode - firstKeyCode + 1) * keysyms_per_keycode]; + Marshal.PtrToStructure(keysym_ptr, keysyms); + API.Free(keysym_ptr); + + KeyboardDevice kb = new KeyboardDevice(); + keyboard.Description = "Default X11 keyboard"; + keyboard.NumberOfKeys = lastKeyCode - firstKeyCode + 1; + keyboard.DeviceID = IntPtr.Zero; + dummy_keyboard_list.Add(keyboard); + + // Request that auto-repeat is only set on devices that support it physically. + // 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 + // be reset before the program exits. + bool supported; + Functions.XkbSetDetectableAutoRepeat(window.Display, true, out supported); + } Debug.Unindent(); } diff --git a/Source/OpenTK/Platform/X11/X11XrandrDisplayDevice.cs b/Source/OpenTK/Platform/X11/X11XrandrDisplayDevice.cs index 01e30020..c0b7f85f 100644 --- a/Source/OpenTK/Platform/X11/X11XrandrDisplayDevice.cs +++ b/Source/OpenTK/Platform/X11/X11XrandrDisplayDevice.cs @@ -1,4 +1,4 @@ -#region --- License --- +#region --- License --- /* Licensed under the MIT/X11 license. * Copyright (c) 2006-2008 the OpenTK Team. * 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++) { 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); - + List available_res = new List(); - + // Add info for a new screen. screenResolutionToIndex.Add(new Dictionary()); - + int[] depths = FindAvailableDepths(screen); - + int resolution_count = 0; 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); 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 // 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) { - return Functions.XListDepths(API.DefaultDisplay, screen); + using (new XLock(API.DefaultDisplay)) + { + return Functions.XListDepths(API.DefaultDisplay, screen); + } } #endregion @@ -123,7 +133,11 @@ namespace OpenTK.Platform.X11 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) throw new NotSupportedException("XRandR extensions not available."); return resolutions; @@ -135,12 +149,15 @@ namespace OpenTK.Platform.X11 static float FindCurrentRefreshRate(int screen) { - IntPtr screen_config = Functions.XRRGetScreenInfo(API.DefaultDisplay, Functions.XRootWindow(API.DefaultDisplay, screen)); - ushort rotation = 0; - int size = Functions.XRRConfigCurrentConfiguration(screen_config, out rotation); - short rate = Functions.XRRConfigCurrentRate(screen_config); - Functions.XRRFreeScreenConfigInfo(screen_config); - + short rate = 0; + using (new XLock(API.DefaultDisplay)) + { + IntPtr screen_config = Functions.XRRGetScreenInfo(API.DefaultDisplay, Functions.XRootWindow(API.DefaultDisplay, screen)); + ushort rotation = 0; + int size = Functions.XRRConfigCurrentConfiguration(screen_config, out rotation); + rate = Functions.XRRConfigCurrentRate(screen_config); + Functions.XRRFreeScreenConfigInfo(screen_config); + } return (float)rate; } @@ -150,7 +167,10 @@ namespace OpenTK.Platform.X11 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 @@ -163,24 +183,27 @@ namespace OpenTK.Platform.X11 { // If resolution == null, restore to default resolution (new_resolution_index = 0). - int screen = deviceToScreen[device]; - IntPtr root = Functions.XRootWindow(API.DefaultDisplay, screen); - IntPtr screen_config = Functions.XRRGetScreenInfo(API.DefaultDisplay, root); - - ushort current_rotation; - int current_resolution_index = Functions.XRRConfigCurrentConfiguration(screen_config, out current_rotation); - int new_resolution_index; - if (resolution != null) - new_resolution_index = screenResolutionToIndex[screen] - [new DisplayResolution(0, 0, resolution.Width, resolution.Height, resolution.BitsPerPixel, 0)]; - else - new_resolution_index = deviceToDefaultResolution[device]; - - Debug.Print("Changing size of screen {0} from {1} to {2}", - screen, current_resolution_index, new_resolution_index); - - return 0 == Functions.XRRSetScreenConfigAndRate(API.DefaultDisplay, screen_config, root, new_resolution_index, - current_rotation, (short)(resolution != null ? resolution.RefreshRate : 0), lastConfigUpdate[screen]); + using (new XLock(API.DefaultDisplay)) + { + int screen = deviceToScreen[device]; + IntPtr root = Functions.XRootWindow(API.DefaultDisplay, screen); + IntPtr screen_config = Functions.XRRGetScreenInfo(API.DefaultDisplay, root); + + ushort current_rotation; + int current_resolution_index = Functions.XRRConfigCurrentConfiguration(screen_config, out current_rotation); + int new_resolution_index; + if (resolution != null) + new_resolution_index = screenResolutionToIndex[screen] + [new DisplayResolution(0, 0, resolution.Width, resolution.Height, resolution.BitsPerPixel, 0)]; + else + new_resolution_index = deviceToDefaultResolution[device]; + + Debug.Print("Changing size of screen {0} from {1} to {2}", + screen, current_resolution_index, new_resolution_index); + + 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)