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
24  * \ingroup GHOST
25  */
26
27 #ifdef WITH_X11_XF86VMODE
28 #  include <X11/Xlib.h>
29 #  include <X11/extensions/xf86vmode.h>
30 #endif
31
32 #include "GHOST_DisplayManagerX11.h"
33 #include "GHOST_SystemX11.h"
34
35
36 GHOST_DisplayManagerX11::
37 GHOST_DisplayManagerX11(
38         GHOST_SystemX11 *system)
39     : GHOST_DisplayManager(),
40       m_system(system)
41 {
42         /* nothing to do. */
43 }
44
45 GHOST_TSuccess
46 GHOST_DisplayManagerX11::
47 getNumDisplays(GHOST_TUns8& numDisplays) const
48 {
49         numDisplays =  m_system->getNumDisplays();
50         return GHOST_kSuccess;
51 }
52
53
54 GHOST_TSuccess
55 GHOST_DisplayManagerX11::
56 getNumDisplaySettings(
57                 GHOST_TUns8 display,
58                 GHOST_TInt32& numSettings) const
59 {
60 #ifdef WITH_X11_XF86VMODE
61         int majorVersion, minorVersion;
62         XF86VidModeModeInfo **vidmodes;
63         Display *dpy = m_system->getXDisplay();
64
65         GHOST_ASSERT(display < 1, "Only single display systems are currently supported.\n");
66
67         if (dpy == NULL)
68                 return GHOST_kFailure;
69
70         majorVersion = minorVersion = 0;
71         if (!XF86VidModeQueryVersion(dpy, &majorVersion, &minorVersion)) {
72                 fprintf(stderr, "Error: XF86VidMode extension missing!\n");
73                 return GHOST_kFailure;
74         }
75
76         if (XF86VidModeGetAllModeLines(dpy, DefaultScreen(dpy), &numSettings, &vidmodes)) {
77                 XFree(vidmodes);
78         }
79
80 #else
81         /* We only have one X11 setting at the moment. */
82         GHOST_ASSERT(display < 1, "Only single display systems are currently supported.\n");
83         numSettings = 1;
84 #endif
85
86         (void) display;
87         return GHOST_kSuccess;
88 }
89
90 /* from SDL2 */
91 #ifdef WITH_X11_XF86VMODE
92 static int
93 calculate_rate(XF86VidModeModeInfo *info)
94 {
95         return (info->htotal
96                 && info->vtotal) ? (1000 * info->dotclock / (info->htotal *
97                                                              info->vtotal)) : 0;
98 }
99 #endif
100
101 GHOST_TSuccess
102 GHOST_DisplayManagerX11::
103 getDisplaySetting(
104                 GHOST_TUns8 display,
105                 GHOST_TInt32 index,
106                 GHOST_DisplaySetting& setting) const
107 {
108         Display *dpy = m_system->getXDisplay();
109
110         if (dpy == NULL)
111                 return GHOST_kFailure;
112
113         (void)display;
114
115 #ifdef WITH_X11_XF86VMODE
116         int majorVersion, minorVersion;
117
118         GHOST_ASSERT(display < 1, "Only single display systems are currently supported.\n");
119
120         majorVersion = minorVersion = 0;
121         if (XF86VidModeQueryVersion(dpy, &majorVersion, &minorVersion)) {
122                 XF86VidModeModeInfo **vidmodes;
123                 int numSettings;
124
125                 if (XF86VidModeGetAllModeLines(dpy, DefaultScreen(dpy), &numSettings, &vidmodes)) {
126                         GHOST_ASSERT(index < numSettings, "Requested setting outside of valid range.\n");
127
128                         setting.xPixels = vidmodes[index]->hdisplay;
129                         setting.yPixels = vidmodes[index]->vdisplay;
130                         setting.bpp = DefaultDepth(dpy, DefaultScreen(dpy));
131                         setting.frequency = calculate_rate(vidmodes[index]);
132                         XFree(vidmodes);
133
134                         return GHOST_kSuccess;
135                 }
136         }
137         else {
138                 fprintf(stderr, "Warning: XF86VidMode extension missing!\n");
139                 /* fallback to non xf86vmode below */
140         }
141 #endif  /* WITH_X11_XF86VMODE */
142
143         GHOST_ASSERT(display < 1, "Only single display systems are currently supported.\n");
144         GHOST_ASSERT(index < 1, "Requested setting outside of valid range.\n");
145         (void)index;
146
147         setting.xPixels  = DisplayWidth(dpy, DefaultScreen(dpy));
148         setting.yPixels = DisplayHeight(dpy, DefaultScreen(dpy));
149         setting.bpp = DefaultDepth(dpy, DefaultScreen(dpy));
150         setting.frequency = 60.0f;
151
152         return GHOST_kSuccess;
153 }
154
155 GHOST_TSuccess
156 GHOST_DisplayManagerX11::
157 getCurrentDisplaySetting(
158                 GHOST_TUns8 display,
159                 GHOST_DisplaySetting& setting) const
160 {
161         /* According to the xf86vidmodegetallmodelines man page,
162          * "The first element of the array corresponds to the current video mode."
163          */
164         return getDisplaySetting(display, 0, setting);
165 }
166
167
168 GHOST_TSuccess
169 GHOST_DisplayManagerX11::
170 setCurrentDisplaySetting(
171                 GHOST_TUns8 /*display*/,
172                 const GHOST_DisplaySetting& setting)
173 {
174 #ifdef WITH_X11_XF86VMODE
175         /* Mode switching code ported from SDL:
176          * See: src/video/x11/SDL_x11modes.c:set_best_resolution
177          */
178         int majorVersion, minorVersion;
179         XF86VidModeModeInfo **vidmodes;
180         Display *dpy = m_system->getXDisplay();
181         int scrnum, num_vidmodes;
182
183         if (dpy == NULL)
184                 return GHOST_kFailure;
185
186         scrnum = DefaultScreen(dpy);
187
188         /* Get video mode list */
189         majorVersion = minorVersion = 0;
190         if (!XF86VidModeQueryVersion(dpy, &majorVersion, &minorVersion)) {
191                 fprintf(stderr, "Error: XF86VidMode extension missing!\n");
192                 return GHOST_kFailure;
193         }
194 #  ifdef DEBUG
195         printf("Using XFree86-VidModeExtension Version %d.%d\n",
196                majorVersion, minorVersion);
197 #  endif
198
199         if (XF86VidModeGetAllModeLines(dpy, scrnum, &num_vidmodes, &vidmodes)) {
200                 int best_fit = -1;
201
202                 for (int i = 0; i < num_vidmodes; i++) {
203                         if (vidmodes[i]->hdisplay < setting.xPixels ||
204                             vidmodes[i]->vdisplay < setting.yPixels)
205                         {
206                                 continue;
207                         }
208
209                         if (best_fit == -1 ||
210                             (vidmodes[i]->hdisplay < vidmodes[best_fit]->hdisplay) ||
211                             (vidmodes[i]->hdisplay == vidmodes[best_fit]->hdisplay &&
212                              vidmodes[i]->vdisplay < vidmodes[best_fit]->vdisplay))
213                         {
214                                 best_fit = i;
215                                 continue;
216                         }
217
218                         if ((vidmodes[i]->hdisplay == vidmodes[best_fit]->hdisplay) &&
219                             (vidmodes[i]->vdisplay == vidmodes[best_fit]->vdisplay))
220                         {
221                                 if (!setting.frequency) {
222                                         /* Higher is better, right? */
223                                         if (calculate_rate(vidmodes[i]) >
224                                             calculate_rate(vidmodes[best_fit]))
225                                         {
226                                                 best_fit = i;
227                                         }
228                                 }
229                                 else {
230                                         if (abs(calculate_rate(vidmodes[i]) - (int)setting.frequency) <
231                                             abs(calculate_rate(vidmodes[best_fit]) - (int)setting.frequency))
232                                         {
233                                                 best_fit = i;
234                                         }
235                                 }
236                         }
237                 }
238
239                 if (best_fit != -1) {
240 #  ifdef DEBUG
241                         printf("Switching to video mode %dx%d %dx%d %d\n",
242                                vidmodes[best_fit]->hdisplay, vidmodes[best_fit]->vdisplay,
243                                vidmodes[best_fit]->htotal, vidmodes[best_fit]->vtotal,
244                                calculate_rate(vidmodes[best_fit]));
245 #  endif
246
247                         /* change to the mode */
248                         XF86VidModeSwitchToMode(dpy, scrnum, vidmodes[best_fit]);
249
250                         /* Move the viewport to top left */
251                         XF86VidModeSetViewPort(dpy, scrnum, 0, 0);
252                 }
253
254                 XFree(vidmodes);
255         }
256         else {
257                 return GHOST_kFailure;
258         }
259
260         XFlush(dpy);
261         return GHOST_kSuccess;
262
263 #else
264         (void)setting;
265
266         /* Just pretend the request was successful. */
267         return GHOST_kSuccess;
268 #endif
269 }