widget/gtk/nsScreenManagerGtk.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial