Merge branch 'blender2.7'
[blender.git] / intern / ghost / intern / GHOST_DisplayManagerX11.cpp
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
19  * All rights reserved.
20  *
21  * The Original Code is: all of this file.
22  *
23  * Video mode switching
24  * Copyright (C) 1997-2001 Id Software, Inc.
25  * Ported from Quake 2 by Alex Fraser <alex@phatcore.com>
26  *
27  * ***** END GPL LICENSE BLOCK *****
28  */
29
30 /** \file ghost/intern/GHOST_DisplayManagerX11.cpp
31  *  \ingroup GHOST
32  */
33
34 #ifdef WITH_X11_XF86VMODE
35 #  include <X11/Xlib.h>
36 #  include <X11/extensions/xf86vmode.h>
37 #endif
38
39 #include "GHOST_DisplayManagerX11.h"
40 #include "GHOST_SystemX11.h"
41
42
43 GHOST_DisplayManagerX11::
44 GHOST_DisplayManagerX11(
45         GHOST_SystemX11 *system)
46     : GHOST_DisplayManager(),
47       m_system(system)
48 {
49         /* nothing to do. */
50 }
51
52 GHOST_TSuccess
53 GHOST_DisplayManagerX11::
54 getNumDisplays(GHOST_TUns8& numDisplays) const
55 {
56         numDisplays =  m_system->getNumDisplays();
57         return GHOST_kSuccess;
58 }
59
60
61 GHOST_TSuccess
62 GHOST_DisplayManagerX11::
63 getNumDisplaySettings(
64                 GHOST_TUns8 display,
65                 GHOST_TInt32& numSettings) const
66 {
67 #ifdef WITH_X11_XF86VMODE
68         int majorVersion, minorVersion;
69         XF86VidModeModeInfo **vidmodes;
70         Display *dpy = m_system->getXDisplay();
71
72         GHOST_ASSERT(display < 1, "Only single display systems are currently supported.\n");
73
74         if (dpy == NULL)
75                 return GHOST_kFailure;
76
77         majorVersion = minorVersion = 0;
78         if (!XF86VidModeQueryVersion(dpy, &majorVersion, &minorVersion)) {
79                 fprintf(stderr, "Error: XF86VidMode extension missing!\n");
80                 return GHOST_kFailure;
81         }
82
83         if (XF86VidModeGetAllModeLines(dpy, DefaultScreen(dpy), &numSettings, &vidmodes)) {
84                 XFree(vidmodes);
85         }
86
87 #else
88         /* We only have one X11 setting at the moment. */
89         GHOST_ASSERT(display < 1, "Only single display systems are currently supported.\n");
90         numSettings = 1;
91 #endif
92
93         (void) display;
94         return GHOST_kSuccess;
95 }
96
97 /* from SDL2 */
98 #ifdef WITH_X11_XF86VMODE
99 static int
100 calculate_rate(XF86VidModeModeInfo *info)
101 {
102         return (info->htotal
103                 && info->vtotal) ? (1000 * info->dotclock / (info->htotal *
104                                                              info->vtotal)) : 0;
105 }
106 #endif
107
108 GHOST_TSuccess
109 GHOST_DisplayManagerX11::
110 getDisplaySetting(
111                 GHOST_TUns8 display,
112                 GHOST_TInt32 index,
113                 GHOST_DisplaySetting& setting) const
114 {
115         Display *dpy = m_system->getXDisplay();
116
117         if (dpy == NULL)
118                 return GHOST_kFailure;
119
120         (void)display;
121
122 #ifdef WITH_X11_XF86VMODE
123         int majorVersion, minorVersion;
124
125         GHOST_ASSERT(display < 1, "Only single display systems are currently supported.\n");
126
127         majorVersion = minorVersion = 0;
128         if (XF86VidModeQueryVersion(dpy, &majorVersion, &minorVersion)) {
129                 XF86VidModeModeInfo **vidmodes;
130                 int numSettings;
131
132                 if (XF86VidModeGetAllModeLines(dpy, DefaultScreen(dpy), &numSettings, &vidmodes)) {
133                         GHOST_ASSERT(index < numSettings, "Requested setting outside of valid range.\n");
134
135                         setting.xPixels = vidmodes[index]->hdisplay;
136                         setting.yPixels = vidmodes[index]->vdisplay;
137                         setting.bpp = DefaultDepth(dpy, DefaultScreen(dpy));
138                         setting.frequency = calculate_rate(vidmodes[index]);
139                         XFree(vidmodes);
140
141                         return GHOST_kSuccess;
142                 }
143         }
144         else {
145                 fprintf(stderr, "Warning: XF86VidMode extension missing!\n");
146                 /* fallback to non xf86vmode below */
147         }
148 #endif  /* WITH_X11_XF86VMODE */
149
150         GHOST_ASSERT(display < 1, "Only single display systems are currently supported.\n");
151         GHOST_ASSERT(index < 1, "Requested setting outside of valid range.\n");
152         (void)index;
153
154         setting.xPixels  = DisplayWidth(dpy, DefaultScreen(dpy));
155         setting.yPixels = DisplayHeight(dpy, DefaultScreen(dpy));
156         setting.bpp = DefaultDepth(dpy, DefaultScreen(dpy));
157         setting.frequency = 60.0f;
158
159         return GHOST_kSuccess;
160 }
161
162 GHOST_TSuccess
163 GHOST_DisplayManagerX11::
164 getCurrentDisplaySetting(
165                 GHOST_TUns8 display,
166                 GHOST_DisplaySetting& setting) const
167 {
168         /* According to the xf86vidmodegetallmodelines man page,
169          * "The first element of the array corresponds to the current video mode."
170          */
171         return getDisplaySetting(display, 0, setting);
172 }
173
174
175 GHOST_TSuccess
176 GHOST_DisplayManagerX11::
177 setCurrentDisplaySetting(
178                 GHOST_TUns8 /*display*/,
179                 const GHOST_DisplaySetting& setting)
180 {
181 #ifdef WITH_X11_XF86VMODE
182         /* Mode switching code ported from SDL:
183          * See: src/video/x11/SDL_x11modes.c:set_best_resolution
184          */
185         int majorVersion, minorVersion;
186         XF86VidModeModeInfo **vidmodes;
187         Display *dpy = m_system->getXDisplay();
188         int scrnum, num_vidmodes;
189
190         if (dpy == NULL)
191                 return GHOST_kFailure;
192
193         scrnum = DefaultScreen(dpy);
194
195         /* Get video mode list */
196         majorVersion = minorVersion = 0;
197         if (!XF86VidModeQueryVersion(dpy, &majorVersion, &minorVersion)) {
198                 fprintf(stderr, "Error: XF86VidMode extension missing!\n");
199                 return GHOST_kFailure;
200         }
201 #  ifdef DEBUG
202         printf("Using XFree86-VidModeExtension Version %d.%d\n",
203                majorVersion, minorVersion);
204 #  endif
205
206         if (XF86VidModeGetAllModeLines(dpy, scrnum, &num_vidmodes, &vidmodes)) {
207                 int best_fit = -1;
208
209                 for (int i = 0; i < num_vidmodes; i++) {
210                         if (vidmodes[i]->hdisplay < setting.xPixels ||
211                             vidmodes[i]->vdisplay < setting.yPixels)
212                         {
213                                 continue;
214                         }
215
216                         if (best_fit == -1 ||
217                             (vidmodes[i]->hdisplay < vidmodes[best_fit]->hdisplay) ||
218                             (vidmodes[i]->hdisplay == vidmodes[best_fit]->hdisplay &&
219                              vidmodes[i]->vdisplay < vidmodes[best_fit]->vdisplay))
220                         {
221                                 best_fit = i;
222                                 continue;
223                         }
224
225                         if ((vidmodes[i]->hdisplay == vidmodes[best_fit]->hdisplay) &&
226                             (vidmodes[i]->vdisplay == vidmodes[best_fit]->vdisplay))
227                         {
228                                 if (!setting.frequency) {
229                                         /* Higher is better, right? */
230                                         if (calculate_rate(vidmodes[i]) >
231                                             calculate_rate(vidmodes[best_fit]))
232                                         {
233                                                 best_fit = i;
234                                         }
235                                 }
236                                 else {
237                                         if (abs(calculate_rate(vidmodes[i]) - (int)setting.frequency) <
238                                             abs(calculate_rate(vidmodes[best_fit]) - (int)setting.frequency))
239                                         {
240                                                 best_fit = i;
241                                         }
242                                 }
243                         }
244                 }
245
246                 if (best_fit != -1) {
247 #  ifdef DEBUG
248                         printf("Switching to video mode %dx%d %dx%d %d\n",
249                                vidmodes[best_fit]->hdisplay, vidmodes[best_fit]->vdisplay,
250                                vidmodes[best_fit]->htotal, vidmodes[best_fit]->vtotal,
251                                calculate_rate(vidmodes[best_fit]));
252 #  endif
253
254                         /* change to the mode */
255                         XF86VidModeSwitchToMode(dpy, scrnum, vidmodes[best_fit]);
256
257                         /* Move the viewport to top left */
258                         XF86VidModeSetViewPort(dpy, scrnum, 0, 0);
259                 }
260
261                 XFree(vidmodes);
262         }
263         else {
264                 return GHOST_kFailure;
265         }
266
267         XFlush(dpy);
268         return GHOST_kSuccess;
269
270 #else
271         (void)setting;
272
273         /* Just pretend the request was successful. */
274         return GHOST_kSuccess;
275 #endif
276 }