widget/gtk/nsScreenManagerGtk.cpp

branch
TOR_BUG_9701
changeset 15
b8a032363ba2
equal deleted inserted replaced
-1:000000000000 0:4fc6aaf1528b
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 #include "nsScreenManagerGtk.h"
7 #include "nsScreenGtk.h"
8 #include "nsIComponentManager.h"
9 #include "nsRect.h"
10 #include "nsAutoPtr.h"
11
12 #define SCREEN_MANAGER_LIBRARY_LOAD_FAILED ((PRLibrary*)1)
13
14 #ifdef MOZ_X11
15 #include <gdk/gdkx.h>
16 // prototypes from Xinerama.h
17 typedef Bool (*_XnrmIsActive_fn)(Display *dpy);
18 typedef XineramaScreenInfo* (*_XnrmQueryScreens_fn)(Display *dpy, int *number);
19 #endif
20
21 #include <gtk/gtk.h>
22
23
24 static GdkFilterReturn
25 root_window_event_filter(GdkXEvent *aGdkXEvent, GdkEvent *aGdkEvent,
26 gpointer aClosure)
27 {
28 nsScreenManagerGtk *manager = static_cast<nsScreenManagerGtk*>(aClosure);
29 #ifdef MOZ_X11
30 XEvent *xevent = static_cast<XEvent*>(aGdkXEvent);
31
32 // See comments in nsScreenGtk::Init below.
33 switch (xevent->type) {
34 case ConfigureNotify:
35 manager->Init();
36 break;
37 case PropertyNotify:
38 {
39 XPropertyEvent *propertyEvent = &xevent->xproperty;
40 if (propertyEvent->atom == manager->NetWorkareaAtom()) {
41 manager->Init();
42 }
43 }
44 break;
45 default:
46 break;
47 }
48 #endif
49
50 return GDK_FILTER_CONTINUE;
51 }
52
53 nsScreenManagerGtk :: nsScreenManagerGtk ( )
54 : mXineramalib(nullptr)
55 , mRootWindow(nullptr)
56 {
57 // nothing else to do. I guess we could cache a bunch of information
58 // here, but we want to ask the device at runtime in case anything
59 // has changed.
60 }
61
62
63 nsScreenManagerGtk :: ~nsScreenManagerGtk()
64 {
65 if (mRootWindow) {
66 gdk_window_remove_filter(mRootWindow, root_window_event_filter, this);
67 g_object_unref(mRootWindow);
68 mRootWindow = nullptr;
69 }
70
71 /* XineramaIsActive() registers a callback function close_display()
72 * in X, which is to be called in XCloseDisplay(). This is the case
73 * if Xinerama is active, even if only with one screen.
74 *
75 * We can't unload libXinerama.so.1 here because this will make
76 * the address of close_display() registered in X to be invalid and
77 * it will crash when XCloseDisplay() is called later. */
78 }
79
80
81 // addref, release, QI
82 NS_IMPL_ISUPPORTS(nsScreenManagerGtk, nsIScreenManager)
83
84
85 // this function will make sure that everything has been initialized.
86 nsresult
87 nsScreenManagerGtk :: EnsureInit()
88 {
89 if (mCachedScreenArray.Count() > 0)
90 return NS_OK;
91
92 mRootWindow = gdk_get_default_root_window();
93 g_object_ref(mRootWindow);
94
95 // GDK_STRUCTURE_MASK ==> StructureNotifyMask, for ConfigureNotify
96 // GDK_PROPERTY_CHANGE_MASK ==> PropertyChangeMask, for PropertyNotify
97 gdk_window_set_events(mRootWindow,
98 GdkEventMask(gdk_window_get_events(mRootWindow) |
99 GDK_STRUCTURE_MASK |
100 GDK_PROPERTY_CHANGE_MASK));
101 gdk_window_add_filter(mRootWindow, root_window_event_filter, this);
102 #ifdef MOZ_X11
103 mNetWorkareaAtom =
104 XInternAtom(GDK_WINDOW_XDISPLAY(mRootWindow), "_NET_WORKAREA", False);
105 #endif
106
107 return Init();
108 }
109
110 nsresult
111 nsScreenManagerGtk :: Init()
112 {
113 #ifdef MOZ_X11
114 XineramaScreenInfo *screenInfo = nullptr;
115 int numScreens;
116
117 if (!mXineramalib) {
118 mXineramalib = PR_LoadLibrary("libXinerama.so.1");
119 if (!mXineramalib) {
120 mXineramalib = SCREEN_MANAGER_LIBRARY_LOAD_FAILED;
121 }
122 }
123 if (mXineramalib && mXineramalib != SCREEN_MANAGER_LIBRARY_LOAD_FAILED) {
124 _XnrmIsActive_fn _XnrmIsActive = (_XnrmIsActive_fn)
125 PR_FindFunctionSymbol(mXineramalib, "XineramaIsActive");
126
127 _XnrmQueryScreens_fn _XnrmQueryScreens = (_XnrmQueryScreens_fn)
128 PR_FindFunctionSymbol(mXineramalib, "XineramaQueryScreens");
129
130 // get the number of screens via xinerama
131 Display *display = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
132 if (_XnrmIsActive && _XnrmQueryScreens && _XnrmIsActive(display)) {
133 screenInfo = _XnrmQueryScreens(display, &numScreens);
134 }
135 }
136
137 // screenInfo == nullptr if either Xinerama couldn't be loaded or
138 // isn't running on the current display
139 if (!screenInfo || numScreens == 1) {
140 numScreens = 1;
141 #endif
142 nsRefPtr<nsScreenGtk> screen;
143
144 if (mCachedScreenArray.Count() > 0) {
145 screen = static_cast<nsScreenGtk*>(mCachedScreenArray[0]);
146 } else {
147 screen = new nsScreenGtk();
148 if (!screen || !mCachedScreenArray.AppendObject(screen)) {
149 return NS_ERROR_OUT_OF_MEMORY;
150 }
151 }
152
153 screen->Init(mRootWindow);
154 #ifdef MOZ_X11
155 }
156 // If Xinerama is enabled and there's more than one screen, fill
157 // in the info for all of the screens. If that's not the case
158 // then nsScreenGTK() defaults to the screen width + height
159 else {
160 #ifdef DEBUG
161 printf("Xinerama superpowers activated for %d screens!\n", numScreens);
162 #endif
163 for (int i = 0; i < numScreens; ++i) {
164 nsRefPtr<nsScreenGtk> screen;
165 if (mCachedScreenArray.Count() > i) {
166 screen = static_cast<nsScreenGtk*>(mCachedScreenArray[i]);
167 } else {
168 screen = new nsScreenGtk();
169 if (!screen || !mCachedScreenArray.AppendObject(screen)) {
170 return NS_ERROR_OUT_OF_MEMORY;
171 }
172 }
173
174 // initialize this screen object
175 screen->Init(&screenInfo[i]);
176 }
177 }
178 // Remove any screens that are no longer present.
179 while (mCachedScreenArray.Count() > numScreens) {
180 mCachedScreenArray.RemoveObjectAt(mCachedScreenArray.Count() - 1);
181 }
182
183 if (screenInfo) {
184 XFree(screenInfo);
185 }
186 #endif
187
188 return NS_OK;
189 }
190
191
192 //
193 // ScreenForRect
194 //
195 // Returns the screen that contains the rectangle. If the rect overlaps
196 // multiple screens, it picks the screen with the greatest area of intersection.
197 //
198 // The coordinates are in pixels (not app units) and in screen coordinates.
199 //
200 NS_IMETHODIMP
201 nsScreenManagerGtk :: ScreenForRect ( int32_t aX, int32_t aY,
202 int32_t aWidth, int32_t aHeight,
203 nsIScreen **aOutScreen )
204 {
205 nsresult rv;
206 rv = EnsureInit();
207 if (NS_FAILED(rv)) {
208 NS_ERROR("nsScreenManagerGtk::EnsureInit() failed from ScreenForRect");
209 return rv;
210 }
211 // which screen ( index from zero ) should we return?
212 uint32_t which = 0;
213 // Optimize for the common case. If the number of screens is only
214 // one then this will fall through with which == 0 and will get the
215 // primary screen.
216 if (mCachedScreenArray.Count() > 1) {
217 // walk the list of screens and find the one that has the most
218 // surface area.
219 uint32_t area = 0;
220 nsIntRect windowRect(aX, aY, aWidth, aHeight);
221 for (int32_t i = 0, i_end = mCachedScreenArray.Count(); i < i_end; ++i) {
222 int32_t x, y, width, height;
223 x = y = width = height = 0;
224 mCachedScreenArray[i]->GetRect(&x, &y, &width, &height);
225 // calculate the surface area
226 nsIntRect screenRect(x, y, width, height);
227 screenRect.IntersectRect(screenRect, windowRect);
228 uint32_t tempArea = screenRect.width * screenRect.height;
229 if (tempArea >= area) {
230 which = i;
231 area = tempArea;
232 }
233 }
234 }
235 *aOutScreen = mCachedScreenArray.SafeObjectAt(which);
236 NS_IF_ADDREF(*aOutScreen);
237 return NS_OK;
238
239 } // ScreenForRect
240
241
242 //
243 // GetPrimaryScreen
244 //
245 // The screen with the menubar/taskbar. This shouldn't be needed very
246 // often.
247 //
248 NS_IMETHODIMP
249 nsScreenManagerGtk :: GetPrimaryScreen(nsIScreen * *aPrimaryScreen)
250 {
251 nsresult rv;
252 rv = EnsureInit();
253 if (NS_FAILED(rv)) {
254 NS_ERROR("nsScreenManagerGtk::EnsureInit() failed from GetPrimaryScreen");
255 return rv;
256 }
257 *aPrimaryScreen = mCachedScreenArray.SafeObjectAt(0);
258 NS_IF_ADDREF(*aPrimaryScreen);
259 return NS_OK;
260
261 } // GetPrimaryScreen
262
263
264 //
265 // GetNumberOfScreens
266 //
267 // Returns how many physical screens are available.
268 //
269 NS_IMETHODIMP
270 nsScreenManagerGtk :: GetNumberOfScreens(uint32_t *aNumberOfScreens)
271 {
272 nsresult rv;
273 rv = EnsureInit();
274 if (NS_FAILED(rv)) {
275 NS_ERROR("nsScreenManagerGtk::EnsureInit() failed from GetNumberOfScreens");
276 return rv;
277 }
278 *aNumberOfScreens = mCachedScreenArray.Count();
279 return NS_OK;
280
281 } // GetNumberOfScreens
282
283 NS_IMETHODIMP
284 nsScreenManagerGtk::GetSystemDefaultScale(float *aDefaultScale)
285 {
286 *aDefaultScale = 1.0f;
287 return NS_OK;
288 }
289
290 NS_IMETHODIMP
291 nsScreenManagerGtk :: ScreenForNativeWidget (void *aWidget, nsIScreen **outScreen)
292 {
293 nsresult rv;
294 rv = EnsureInit();
295 if (NS_FAILED(rv)) {
296 NS_ERROR("nsScreenManagerGtk::EnsureInit() failed from ScreenForNativeWidget");
297 return rv;
298 }
299
300 if (mCachedScreenArray.Count() > 1) {
301 // I don't know how to go from GtkWindow to nsIScreen, especially
302 // given xinerama and stuff, so let's just do this
303 gint x, y, width, height;
304 #if (MOZ_WIDGET_GTK == 2)
305 gint depth;
306 #endif
307 x = y = width = height = 0;
308
309 #if (MOZ_WIDGET_GTK == 2)
310 gdk_window_get_geometry(GDK_WINDOW(aWidget), &x, &y, &width, &height,
311 &depth);
312 #else
313 gdk_window_get_geometry(GDK_WINDOW(aWidget), &x, &y, &width, &height);
314 #endif
315 gdk_window_get_origin(GDK_WINDOW(aWidget), &x, &y);
316 rv = ScreenForRect(x, y, width, height, outScreen);
317 } else {
318 rv = GetPrimaryScreen(outScreen);
319 }
320
321 return rv;
322 }

mercurial