00001 #region --- License ---
00002
00003
00004
00005
00006
00007 #endregion
00008
00009 using System;
00010 using System.Collections.Generic;
00011 using System.Diagnostics;
00012 using System.Drawing;
00013 using System.Runtime.InteropServices;
00014
00015 namespace OpenTK.Platform.X11
00016 {
00017 internal class X11XrandrDisplayDevice : IDisplayDeviceDriver
00018 {
00019 static object display_lock = new object();
00020
00021
00022
00023
00024 static List<Dictionary<DisplayResolution, int>> screenResolutionToIndex =
00025 new List<Dictionary<DisplayResolution, int>>();
00026
00027 static Dictionary<DisplayDevice, int> deviceToDefaultResolution = new Dictionary<DisplayDevice, int>();
00028
00029 static Dictionary<DisplayDevice, int> deviceToScreen = new Dictionary<DisplayDevice, int>();
00030
00031 static List<IntPtr> lastConfigUpdate = new List<IntPtr>();
00032
00033 #region --- Constructors ---
00034
00035 static X11XrandrDisplayDevice()
00036 {
00037 using (new XLock(API.DefaultDisplay))
00038 {
00039 List<DisplayDevice> devices = new List<DisplayDevice>();
00040 bool xinerama_supported = false;
00041 try
00042 {
00043
00044 int event_base, error_base;
00045 if (NativeMethods.XineramaQueryExtension(API.DefaultDisplay, out event_base, out error_base) &&
00046 NativeMethods.XineramaIsActive(API.DefaultDisplay))
00047 {
00048 IList<XineramaScreenInfo> screens = NativeMethods.XineramaQueryScreens(API.DefaultDisplay);
00049 bool first = true;
00050 foreach (XineramaScreenInfo screen in screens)
00051 {
00052 DisplayDevice dev = new DisplayDevice();
00053 dev.Bounds = new Rectangle(screen.X, screen.Y, screen.Width, screen.Height);
00054 if (first)
00055 {
00056
00057
00058 dev.IsPrimary = true;
00059 first = false;
00060 }
00061 devices.Add(dev);
00062
00063 deviceToScreen.Add(dev, 0 );
00064 xinerama_supported = true;
00065 }
00066 }
00067 }
00068 catch { Debug.Print("Xinerama query failed."); }
00069
00070 if (!xinerama_supported)
00071 {
00072
00073
00074 for (int i = 0; i < API.ScreenCount; i++)
00075 {
00076 DisplayDevice dev = new DisplayDevice();
00077 dev.IsPrimary = i == Functions.XDefaultScreen(API.DefaultDisplay);
00078 devices.Add(dev);
00079 deviceToScreen.Add(dev, i);
00080 }
00081 }
00082
00083
00084 foreach (DisplayDevice dev in devices)
00085 {
00086 int screen = deviceToScreen[dev];
00087
00088 IntPtr timestamp_of_last_update;
00089 Functions.XRRTimes(API.DefaultDisplay, screen, out timestamp_of_last_update);
00090 lastConfigUpdate.Add(timestamp_of_last_update);
00091
00092 List<DisplayResolution> available_res = new List<DisplayResolution>();
00093
00094
00095 screenResolutionToIndex.Add(new Dictionary<DisplayResolution, int>());
00096
00097 int[] depths = FindAvailableDepths(screen);
00098
00099 int resolution_count = 0;
00100 foreach (XRRScreenSize size in FindAvailableResolutions(screen))
00101 {
00102 if (size.Width == 0 || size.Height == 0)
00103 {
00104 Debug.Print("[Warning] XRandR returned an invalid resolution ({0}) for display device {1}", size, screen);
00105 continue;
00106 }
00107 short[] rates = null;
00108 rates = Functions.XRRRates(API.DefaultDisplay, screen, resolution_count);
00109
00110
00111
00112
00113 foreach (short rate in rates)
00114 {
00115
00116
00117 if (rate != 0 || rates.Length == 1)
00118 foreach (int depth in depths)
00119 available_res.Add(new DisplayResolution(0, 0, size.Width, size.Height, depth, (float)rate));
00120 }
00121
00122 foreach (int depth in depths)
00123 {
00124
00125
00126
00127
00128 DisplayResolution res = new DisplayResolution(0, 0, size.Width, size.Height, depth, 0);
00129 if (!screenResolutionToIndex[screen].ContainsKey(res))
00130 screenResolutionToIndex[screen].Add(res, resolution_count);
00131 }
00132
00133 ++resolution_count;
00134 }
00135
00136
00137
00138
00139
00140 float current_refresh_rate = FindCurrentRefreshRate(screen);
00141 int current_depth = FindCurrentDepth(screen);
00142 IntPtr screen_config = Functions.XRRGetScreenInfo(API.DefaultDisplay, Functions.XRootWindow(API.DefaultDisplay, screen));
00143 ushort current_rotation;
00144 int current_resolution_index = Functions.XRRConfigCurrentConfiguration(screen_config, out current_rotation);
00145
00146 if (dev.Bounds == Rectangle.Empty)
00147 dev.Bounds = new Rectangle(0, 0, available_res[current_resolution_index].Width, available_res[current_resolution_index].Height);
00148 dev.BitsPerPixel = current_depth;
00149 dev.RefreshRate = current_refresh_rate;
00150 dev.AvailableResolutions = available_res;
00151
00152 deviceToDefaultResolution.Add(dev, current_resolution_index);
00153 }
00154 }
00155 }
00156
00157 internal X11XrandrDisplayDevice() { }
00158
00159 #endregion
00160
00161 #region --- Private Methods ---
00162
00163 #region static int[] FindAvailableDepths(int screen)
00164
00165 static int[] FindAvailableDepths(int screen)
00166 {
00167 return Functions.XListDepths(API.DefaultDisplay, screen);
00168 }
00169
00170 #endregion
00171
00172 #region static XRRScreenSize[] FindAvailableResolutions(int screen)
00173
00174 static XRRScreenSize[] FindAvailableResolutions(int screen)
00175 {
00176 XRRScreenSize[] resolutions = null;
00177 resolutions = Functions.XRRSizes(API.DefaultDisplay, screen);
00178 if (resolutions == null)
00179 throw new NotSupportedException("XRandR extensions not available.");
00180 return resolutions;
00181 }
00182
00183 #endregion
00184
00185 #region static float FindCurrentRefreshRate(int screen)
00186
00187 static float FindCurrentRefreshRate(int screen)
00188 {
00189 short rate = 0;
00190 IntPtr screen_config = Functions.XRRGetScreenInfo(API.DefaultDisplay, Functions.XRootWindow(API.DefaultDisplay, screen));
00191 ushort rotation = 0;
00192 int size = Functions.XRRConfigCurrentConfiguration(screen_config, out rotation);
00193 rate = Functions.XRRConfigCurrentRate(screen_config);
00194 Functions.XRRFreeScreenConfigInfo(screen_config);
00195 return (float)rate;
00196 }
00197
00198 #endregion
00199
00200 #region private static int FindCurrentDepth(int screen)
00201
00202 private static int FindCurrentDepth(int screen)
00203 {
00204 return (int)Functions.XDefaultDepth(API.DefaultDisplay, screen);
00205 }
00206
00207 #endregion
00208
00209 #endregion
00210
00211 #region --- IDisplayDeviceDriver Members ---
00212
00213 public bool TryChangeResolution(DisplayDevice device, DisplayResolution resolution)
00214 {
00215
00216
00217 using (new XLock(API.DefaultDisplay))
00218 {
00219 int screen = deviceToScreen[device];
00220 IntPtr root = Functions.XRootWindow(API.DefaultDisplay, screen);
00221 IntPtr screen_config = Functions.XRRGetScreenInfo(API.DefaultDisplay, root);
00222
00223 ushort current_rotation;
00224 int current_resolution_index = Functions.XRRConfigCurrentConfiguration(screen_config, out current_rotation);
00225 int new_resolution_index;
00226 if (resolution != null)
00227 new_resolution_index = screenResolutionToIndex[screen]
00228 [new DisplayResolution(0, 0, resolution.Width, resolution.Height, resolution.BitsPerPixel, 0)];
00229 else
00230 new_resolution_index = deviceToDefaultResolution[device];
00231
00232 Debug.Print("Changing size of screen {0} from {1} to {2}",
00233 screen, current_resolution_index, new_resolution_index);
00234
00235 return 0 == Functions.XRRSetScreenConfigAndRate(API.DefaultDisplay, screen_config, root, new_resolution_index,
00236 current_rotation, (short)(resolution != null ? resolution.RefreshRate : 0), lastConfigUpdate[screen]);
00237 }
00238 }
00239
00240 public bool TryRestoreResolution(DisplayDevice device)
00241 {
00242 return TryChangeResolution(device, null);
00243
00244 }
00245
00246 #endregion
00247
00248 #region NativeMethods
00249
00250 static class NativeMethods
00251 {
00252 const string Xinerama = "libXinerama";
00253
00254 [DllImport(Xinerama)]
00255 public static extern bool XineramaQueryExtension(IntPtr dpy, out int event_basep, out int error_basep);
00256
00257 [DllImport(Xinerama)]
00258 public static extern int XineramaQueryVersion (IntPtr dpy, out int major_versionp, out int minor_versionp);
00259
00260 [DllImport(Xinerama)]
00261 public static extern bool XineramaIsActive(IntPtr dpy);
00262
00263 [DllImport(Xinerama)]
00264 static extern IntPtr XineramaQueryScreens(IntPtr dpy, out int number);
00265
00266 public static IList<XineramaScreenInfo> XineramaQueryScreens(IntPtr dpy)
00267 {
00268 int number;
00269 IntPtr screen_ptr = XineramaQueryScreens(dpy, out number);
00270 List<XineramaScreenInfo> screens = new List<XineramaScreenInfo>(number);
00271
00272 unsafe
00273 {
00274 XineramaScreenInfo* ptr = (XineramaScreenInfo*)screen_ptr;
00275 while (--number >= 0)
00276 {
00277 screens.Add(*ptr);
00278 ptr++;
00279 }
00280 }
00281
00282 return screens;
00283 }
00284 }
00285
00286 [StructLayout(LayoutKind.Sequential, Pack = 1)]
00287 struct XineramaScreenInfo
00288 {
00289 public int ScreenNumber;
00290 public short X;
00291 public short Y;
00292 public short Width;
00293 public short Height;
00294 }
00295
00296 #endregion
00297 }
00298 }