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