* Platform/Windows/WinDisplayDevice.cs:

* Platform/MacOS/QuartzDisplayDeviceDriver.cs: Updated to use the new
  DisplayDevice constructors.

* Platform/X11/X11XrandrDisplayDevice.cs: Query Xinerama for the exact
  bounds of each display device, if available.
Use global X lock to protect from multithreaded access.
This commit is contained in:
the_fiddler 2009-11-06 09:50:50 +00:00
parent 2f3481231b
commit 139b6af9fa
3 changed files with 174 additions and 90 deletions

View file

@ -92,7 +92,7 @@ namespace OpenTK.Platform.MacOS
} }
DisplayDevice opentk_dev = DisplayDevice opentk_dev =
new DisplayDevice(opentk_dev_current_res, primary, opentk_dev_available_res); new DisplayDevice(opentk_dev_current_res, primary, opentk_dev_available_res, Rectangle.Empty);
displayMap.Add(opentk_dev, currentDisplay); displayMap.Add(opentk_dev, currentDisplay);
} }

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. * This notice may not be removed.
@ -81,7 +81,8 @@ namespace OpenTK.Platform.Windows
opentk_dev = new DisplayDevice( opentk_dev = new DisplayDevice(
opentk_dev_current_res, opentk_dev_current_res,
opentk_dev_primary, opentk_dev_primary,
opentk_dev_available_res); opentk_dev_available_res,
opentk_dev_current_res.Bounds);
available_device_names.Add(opentk_dev, dev1.DeviceName); available_device_names.Add(opentk_dev, dev1.DeviceName);
} }

View file

@ -8,6 +8,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text; using System.Text;
using OpenTK.Graphics; using OpenTK.Graphics;
@ -35,17 +36,59 @@ namespace OpenTK.Platform.X11
static X11XrandrDisplayDevice() static X11XrandrDisplayDevice()
{ {
// Get available resolutions. Then, for each resolution get all
// available rates.
// TODO: Global X11 lock.
// TODO: Use xinerama to get the bounds of each monitor.
for (int screen = 0; screen < API.ScreenCount; screen++)
{
IntPtr timestamp_of_last_update;
using (new XLock(API.DefaultDisplay)) using (new XLock(API.DefaultDisplay))
{ {
Functions.XRRTimes(API.DefaultDisplay, screen, out timestamp_of_last_update); List<DisplayDevice> devices = new List<DisplayDevice>();
bool xinerama_supported = false;
try
{
// Try to use Xinerama to obtain the geometry of all output devices.
int event_base, error_base;
if (NativeMethods.XineramaQueryExtension(API.DefaultDisplay, out event_base, out error_base) &&
NativeMethods.XineramaIsActive(API.DefaultDisplay))
{
IList<XineramaScreenInfo> screens = NativeMethods.XineramaQueryScreens(API.DefaultDisplay);
bool first = true;
foreach (XineramaScreenInfo screen in screens)
{
DisplayDevice dev = new DisplayDevice();
dev.Bounds = new Rectangle(screen.X, screen.Y, screen.Width, screen.Height);
if (first)
{
// We consider the first device returned by Xinerama as the primary one.
// Makes sense conceptually, but is there a way to verify this?
dev.IsPrimary = true;
first = false;
} }
devices.Add(dev);
// It seems that all X screens are equal to 0 is Xinerama is enabled, at least on Nvidia (verify?)
deviceToScreen.Add(dev, 0 /*screen.ScreenNumber*/);
xinerama_supported = true;
}
}
}
catch { Debug.Print("Xinerama query failed."); }
if (!xinerama_supported)
{
// We assume that devices are equivalent to the number of available screens.
// Note: this won't work correctly in the case of distinct X servers.
for (int i = 0; i < API.ScreenCount; i++)
{
DisplayDevice dev = new DisplayDevice();
dev.IsPrimary = i == Functions.XDefaultScreen(API.DefaultDisplay);
devices.Add(dev);
deviceToScreen.Add(dev, i);
}
}
// Get available resolutions. Then, for each resolution get all available rates.
foreach (DisplayDevice dev in devices)
{
int screen = deviceToScreen[dev];
IntPtr timestamp_of_last_update;
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>();
@ -64,10 +107,7 @@ namespace OpenTK.Platform.X11
continue; continue;
} }
short[] rates = null; short[] rates = null;
using (new XLock(API.DefaultDisplay))
{
rates = Functions.XRRRates(API.DefaultDisplay, screen, resolution_count); 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
@ -82,8 +122,15 @@ namespace OpenTK.Platform.X11
} }
// Keep the index of this resolution - we will need it for resolution changes later. // Keep the index of this resolution - we will need it for resolution changes later.
foreach (int depth in depths) foreach (int depth in depths)
screenResolutionToIndex[screen].Add(new DisplayResolution(0, 0, size.Width, size.Height, depth, 0), {
resolution_count); // Note that Xinerama may return multiple devices for a single screen. XRandR will
// not distinguish between the two as far as resolutions are supported (since XRandR
// operates on X screens, not display devices) - we need to be careful not to add the
// same resolution twice!
DisplayResolution res = new DisplayResolution(0, 0, size.Width, size.Height, depth, 0);
if (!screenResolutionToIndex[screen].ContainsKey(res))
screenResolutionToIndex[screen].Add(res, resolution_count);
}
++resolution_count; ++resolution_count;
} }
@ -98,16 +145,14 @@ namespace OpenTK.Platform.X11
ushort current_rotation; // Not needed. ushort current_rotation; // Not needed.
int current_resolution_index = Functions.XRRConfigCurrentConfiguration(screen_config, out current_rotation); int current_resolution_index = Functions.XRRConfigCurrentConfiguration(screen_config, out current_rotation);
DisplayDevice current_device = new DisplayDevice( if (dev.Bounds == Rectangle.Empty)
new DisplayResolution( dev.Bounds = new Rectangle(0, 0, available_res[current_resolution_index].Width, available_res[current_resolution_index].Height);
0, 0, dev.BitsPerPixel = current_depth;
available_res[current_resolution_index].Width, available_res[current_resolution_index].Height, dev.RefreshRate = current_refresh_rate;
current_depth, current_refresh_rate), dev.AvailableResolutions = available_res;
screen == Functions.XDefaultScreen(API.DefaultDisplay),
available_res);
deviceToScreen.Add(current_device, screen); deviceToDefaultResolution.Add(dev, current_resolution_index);
deviceToDefaultResolution.Add(current_device, current_resolution_index); }
} }
} }
@ -120,12 +165,9 @@ namespace OpenTK.Platform.X11
#region static int[] FindAvailableDepths(int screen) #region static int[] FindAvailableDepths(int screen)
static int[] FindAvailableDepths(int screen) static int[] FindAvailableDepths(int screen)
{
using (new XLock(API.DefaultDisplay))
{ {
return Functions.XListDepths(API.DefaultDisplay, screen); return Functions.XListDepths(API.DefaultDisplay, screen);
} }
}
#endregion #endregion
@ -134,10 +176,7 @@ namespace OpenTK.Platform.X11
static XRRScreenSize[] FindAvailableResolutions(int screen) static XRRScreenSize[] FindAvailableResolutions(int screen)
{ {
XRRScreenSize[] resolutions = null; XRRScreenSize[] resolutions = null;
using (new XLock(API.DefaultDisplay))
{
resolutions = Functions.XRRSizes(API.DefaultDisplay, screen); 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;
@ -150,14 +189,11 @@ namespace OpenTK.Platform.X11
static float FindCurrentRefreshRate(int screen) static float FindCurrentRefreshRate(int screen)
{ {
short rate = 0; short rate = 0;
using (new XLock(API.DefaultDisplay))
{
IntPtr screen_config = Functions.XRRGetScreenInfo(API.DefaultDisplay, Functions.XRootWindow(API.DefaultDisplay, screen)); IntPtr screen_config = Functions.XRRGetScreenInfo(API.DefaultDisplay, Functions.XRootWindow(API.DefaultDisplay, screen));
ushort rotation = 0; ushort rotation = 0;
int size = Functions.XRRConfigCurrentConfiguration(screen_config, out rotation); int size = Functions.XRRConfigCurrentConfiguration(screen_config, out rotation);
rate = Functions.XRRConfigCurrentRate(screen_config); rate = Functions.XRRConfigCurrentRate(screen_config);
Functions.XRRFreeScreenConfigInfo(screen_config); Functions.XRRFreeScreenConfigInfo(screen_config);
}
return (float)rate; return (float)rate;
} }
@ -166,12 +202,9 @@ namespace OpenTK.Platform.X11
#region private static int FindCurrentDepth(int screen) #region private static int FindCurrentDepth(int screen)
private static int FindCurrentDepth(int screen) private static int FindCurrentDepth(int screen)
{
using (new XLock(API.DefaultDisplay))
{ {
return (int)Functions.XDefaultDepth(API.DefaultDisplay, screen); return (int)Functions.XDefaultDepth(API.DefaultDisplay, screen);
} }
}
#endregion #endregion
@ -181,7 +214,7 @@ namespace OpenTK.Platform.X11
public bool TryChangeResolution(DisplayDevice device, DisplayResolution resolution) public bool TryChangeResolution(DisplayDevice device, DisplayResolution resolution)
{ {
// If resolution == null, restore to default resolution (new_resolution_index = 0). // If resolution is null, restore the default resolution (new_resolution_index = 0).
using (new XLock(API.DefaultDisplay)) using (new XLock(API.DefaultDisplay))
{ {
@ -213,5 +246,55 @@ namespace OpenTK.Platform.X11
} }
#endregion #endregion
#region NativeMethods
static class NativeMethods
{
const string Xinerama = "libXinerama";
[DllImport(Xinerama)]
public static extern bool XineramaQueryExtension(IntPtr dpy, out int event_basep, out int error_basep);
[DllImport(Xinerama)]
public static extern int XineramaQueryVersion (IntPtr dpy, out int major_versionp, out int minor_versionp);
[DllImport(Xinerama)]
public static extern bool XineramaIsActive(IntPtr dpy);
[DllImport(Xinerama)]
static extern IntPtr XineramaQueryScreens(IntPtr dpy, out int number);
public static IList<XineramaScreenInfo> XineramaQueryScreens(IntPtr dpy)
{
int number;
IntPtr screen_ptr = XineramaQueryScreens(dpy, out number);
List<XineramaScreenInfo> screens = new List<XineramaScreenInfo>(number);
unsafe
{
XineramaScreenInfo* ptr = (XineramaScreenInfo*)screen_ptr;
while (--number >= 0)
{
screens.Add(*ptr);
ptr++;
}
}
return screens;
}
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct XineramaScreenInfo
{
public int ScreenNumber;
public short X;
public short Y;
public short Width;
public short Height;
}
#endregion
} }
} }