1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/widget/gtk/nsScreenManagerGtk.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,322 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "nsScreenManagerGtk.h" 1.10 +#include "nsScreenGtk.h" 1.11 +#include "nsIComponentManager.h" 1.12 +#include "nsRect.h" 1.13 +#include "nsAutoPtr.h" 1.14 + 1.15 +#define SCREEN_MANAGER_LIBRARY_LOAD_FAILED ((PRLibrary*)1) 1.16 + 1.17 +#ifdef MOZ_X11 1.18 +#include <gdk/gdkx.h> 1.19 +// prototypes from Xinerama.h 1.20 +typedef Bool (*_XnrmIsActive_fn)(Display *dpy); 1.21 +typedef XineramaScreenInfo* (*_XnrmQueryScreens_fn)(Display *dpy, int *number); 1.22 +#endif 1.23 + 1.24 +#include <gtk/gtk.h> 1.25 + 1.26 + 1.27 +static GdkFilterReturn 1.28 +root_window_event_filter(GdkXEvent *aGdkXEvent, GdkEvent *aGdkEvent, 1.29 + gpointer aClosure) 1.30 +{ 1.31 + nsScreenManagerGtk *manager = static_cast<nsScreenManagerGtk*>(aClosure); 1.32 +#ifdef MOZ_X11 1.33 + XEvent *xevent = static_cast<XEvent*>(aGdkXEvent); 1.34 + 1.35 + // See comments in nsScreenGtk::Init below. 1.36 + switch (xevent->type) { 1.37 + case ConfigureNotify: 1.38 + manager->Init(); 1.39 + break; 1.40 + case PropertyNotify: 1.41 + { 1.42 + XPropertyEvent *propertyEvent = &xevent->xproperty; 1.43 + if (propertyEvent->atom == manager->NetWorkareaAtom()) { 1.44 + manager->Init(); 1.45 + } 1.46 + } 1.47 + break; 1.48 + default: 1.49 + break; 1.50 + } 1.51 +#endif 1.52 + 1.53 + return GDK_FILTER_CONTINUE; 1.54 +} 1.55 + 1.56 +nsScreenManagerGtk :: nsScreenManagerGtk ( ) 1.57 + : mXineramalib(nullptr) 1.58 + , mRootWindow(nullptr) 1.59 +{ 1.60 + // nothing else to do. I guess we could cache a bunch of information 1.61 + // here, but we want to ask the device at runtime in case anything 1.62 + // has changed. 1.63 +} 1.64 + 1.65 + 1.66 +nsScreenManagerGtk :: ~nsScreenManagerGtk() 1.67 +{ 1.68 + if (mRootWindow) { 1.69 + gdk_window_remove_filter(mRootWindow, root_window_event_filter, this); 1.70 + g_object_unref(mRootWindow); 1.71 + mRootWindow = nullptr; 1.72 + } 1.73 + 1.74 + /* XineramaIsActive() registers a callback function close_display() 1.75 + * in X, which is to be called in XCloseDisplay(). This is the case 1.76 + * if Xinerama is active, even if only with one screen. 1.77 + * 1.78 + * We can't unload libXinerama.so.1 here because this will make 1.79 + * the address of close_display() registered in X to be invalid and 1.80 + * it will crash when XCloseDisplay() is called later. */ 1.81 +} 1.82 + 1.83 + 1.84 +// addref, release, QI 1.85 +NS_IMPL_ISUPPORTS(nsScreenManagerGtk, nsIScreenManager) 1.86 + 1.87 + 1.88 +// this function will make sure that everything has been initialized. 1.89 +nsresult 1.90 +nsScreenManagerGtk :: EnsureInit() 1.91 +{ 1.92 + if (mCachedScreenArray.Count() > 0) 1.93 + return NS_OK; 1.94 + 1.95 + mRootWindow = gdk_get_default_root_window(); 1.96 + g_object_ref(mRootWindow); 1.97 + 1.98 + // GDK_STRUCTURE_MASK ==> StructureNotifyMask, for ConfigureNotify 1.99 + // GDK_PROPERTY_CHANGE_MASK ==> PropertyChangeMask, for PropertyNotify 1.100 + gdk_window_set_events(mRootWindow, 1.101 + GdkEventMask(gdk_window_get_events(mRootWindow) | 1.102 + GDK_STRUCTURE_MASK | 1.103 + GDK_PROPERTY_CHANGE_MASK)); 1.104 + gdk_window_add_filter(mRootWindow, root_window_event_filter, this); 1.105 +#ifdef MOZ_X11 1.106 + mNetWorkareaAtom = 1.107 + XInternAtom(GDK_WINDOW_XDISPLAY(mRootWindow), "_NET_WORKAREA", False); 1.108 +#endif 1.109 + 1.110 + return Init(); 1.111 +} 1.112 + 1.113 +nsresult 1.114 +nsScreenManagerGtk :: Init() 1.115 +{ 1.116 +#ifdef MOZ_X11 1.117 + XineramaScreenInfo *screenInfo = nullptr; 1.118 + int numScreens; 1.119 + 1.120 + if (!mXineramalib) { 1.121 + mXineramalib = PR_LoadLibrary("libXinerama.so.1"); 1.122 + if (!mXineramalib) { 1.123 + mXineramalib = SCREEN_MANAGER_LIBRARY_LOAD_FAILED; 1.124 + } 1.125 + } 1.126 + if (mXineramalib && mXineramalib != SCREEN_MANAGER_LIBRARY_LOAD_FAILED) { 1.127 + _XnrmIsActive_fn _XnrmIsActive = (_XnrmIsActive_fn) 1.128 + PR_FindFunctionSymbol(mXineramalib, "XineramaIsActive"); 1.129 + 1.130 + _XnrmQueryScreens_fn _XnrmQueryScreens = (_XnrmQueryScreens_fn) 1.131 + PR_FindFunctionSymbol(mXineramalib, "XineramaQueryScreens"); 1.132 + 1.133 + // get the number of screens via xinerama 1.134 + Display *display = GDK_DISPLAY_XDISPLAY(gdk_display_get_default()); 1.135 + if (_XnrmIsActive && _XnrmQueryScreens && _XnrmIsActive(display)) { 1.136 + screenInfo = _XnrmQueryScreens(display, &numScreens); 1.137 + } 1.138 + } 1.139 + 1.140 + // screenInfo == nullptr if either Xinerama couldn't be loaded or 1.141 + // isn't running on the current display 1.142 + if (!screenInfo || numScreens == 1) { 1.143 + numScreens = 1; 1.144 +#endif 1.145 + nsRefPtr<nsScreenGtk> screen; 1.146 + 1.147 + if (mCachedScreenArray.Count() > 0) { 1.148 + screen = static_cast<nsScreenGtk*>(mCachedScreenArray[0]); 1.149 + } else { 1.150 + screen = new nsScreenGtk(); 1.151 + if (!screen || !mCachedScreenArray.AppendObject(screen)) { 1.152 + return NS_ERROR_OUT_OF_MEMORY; 1.153 + } 1.154 + } 1.155 + 1.156 + screen->Init(mRootWindow); 1.157 +#ifdef MOZ_X11 1.158 + } 1.159 + // If Xinerama is enabled and there's more than one screen, fill 1.160 + // in the info for all of the screens. If that's not the case 1.161 + // then nsScreenGTK() defaults to the screen width + height 1.162 + else { 1.163 +#ifdef DEBUG 1.164 + printf("Xinerama superpowers activated for %d screens!\n", numScreens); 1.165 +#endif 1.166 + for (int i = 0; i < numScreens; ++i) { 1.167 + nsRefPtr<nsScreenGtk> screen; 1.168 + if (mCachedScreenArray.Count() > i) { 1.169 + screen = static_cast<nsScreenGtk*>(mCachedScreenArray[i]); 1.170 + } else { 1.171 + screen = new nsScreenGtk(); 1.172 + if (!screen || !mCachedScreenArray.AppendObject(screen)) { 1.173 + return NS_ERROR_OUT_OF_MEMORY; 1.174 + } 1.175 + } 1.176 + 1.177 + // initialize this screen object 1.178 + screen->Init(&screenInfo[i]); 1.179 + } 1.180 + } 1.181 + // Remove any screens that are no longer present. 1.182 + while (mCachedScreenArray.Count() > numScreens) { 1.183 + mCachedScreenArray.RemoveObjectAt(mCachedScreenArray.Count() - 1); 1.184 + } 1.185 + 1.186 + if (screenInfo) { 1.187 + XFree(screenInfo); 1.188 + } 1.189 +#endif 1.190 + 1.191 + return NS_OK; 1.192 +} 1.193 + 1.194 + 1.195 +// 1.196 +// ScreenForRect 1.197 +// 1.198 +// Returns the screen that contains the rectangle. If the rect overlaps 1.199 +// multiple screens, it picks the screen with the greatest area of intersection. 1.200 +// 1.201 +// The coordinates are in pixels (not app units) and in screen coordinates. 1.202 +// 1.203 +NS_IMETHODIMP 1.204 +nsScreenManagerGtk :: ScreenForRect ( int32_t aX, int32_t aY, 1.205 + int32_t aWidth, int32_t aHeight, 1.206 + nsIScreen **aOutScreen ) 1.207 +{ 1.208 + nsresult rv; 1.209 + rv = EnsureInit(); 1.210 + if (NS_FAILED(rv)) { 1.211 + NS_ERROR("nsScreenManagerGtk::EnsureInit() failed from ScreenForRect"); 1.212 + return rv; 1.213 + } 1.214 + // which screen ( index from zero ) should we return? 1.215 + uint32_t which = 0; 1.216 + // Optimize for the common case. If the number of screens is only 1.217 + // one then this will fall through with which == 0 and will get the 1.218 + // primary screen. 1.219 + if (mCachedScreenArray.Count() > 1) { 1.220 + // walk the list of screens and find the one that has the most 1.221 + // surface area. 1.222 + uint32_t area = 0; 1.223 + nsIntRect windowRect(aX, aY, aWidth, aHeight); 1.224 + for (int32_t i = 0, i_end = mCachedScreenArray.Count(); i < i_end; ++i) { 1.225 + int32_t x, y, width, height; 1.226 + x = y = width = height = 0; 1.227 + mCachedScreenArray[i]->GetRect(&x, &y, &width, &height); 1.228 + // calculate the surface area 1.229 + nsIntRect screenRect(x, y, width, height); 1.230 + screenRect.IntersectRect(screenRect, windowRect); 1.231 + uint32_t tempArea = screenRect.width * screenRect.height; 1.232 + if (tempArea >= area) { 1.233 + which = i; 1.234 + area = tempArea; 1.235 + } 1.236 + } 1.237 + } 1.238 + *aOutScreen = mCachedScreenArray.SafeObjectAt(which); 1.239 + NS_IF_ADDREF(*aOutScreen); 1.240 + return NS_OK; 1.241 + 1.242 +} // ScreenForRect 1.243 + 1.244 + 1.245 +// 1.246 +// GetPrimaryScreen 1.247 +// 1.248 +// The screen with the menubar/taskbar. This shouldn't be needed very 1.249 +// often. 1.250 +// 1.251 +NS_IMETHODIMP 1.252 +nsScreenManagerGtk :: GetPrimaryScreen(nsIScreen * *aPrimaryScreen) 1.253 +{ 1.254 + nsresult rv; 1.255 + rv = EnsureInit(); 1.256 + if (NS_FAILED(rv)) { 1.257 + NS_ERROR("nsScreenManagerGtk::EnsureInit() failed from GetPrimaryScreen"); 1.258 + return rv; 1.259 + } 1.260 + *aPrimaryScreen = mCachedScreenArray.SafeObjectAt(0); 1.261 + NS_IF_ADDREF(*aPrimaryScreen); 1.262 + return NS_OK; 1.263 + 1.264 +} // GetPrimaryScreen 1.265 + 1.266 + 1.267 +// 1.268 +// GetNumberOfScreens 1.269 +// 1.270 +// Returns how many physical screens are available. 1.271 +// 1.272 +NS_IMETHODIMP 1.273 +nsScreenManagerGtk :: GetNumberOfScreens(uint32_t *aNumberOfScreens) 1.274 +{ 1.275 + nsresult rv; 1.276 + rv = EnsureInit(); 1.277 + if (NS_FAILED(rv)) { 1.278 + NS_ERROR("nsScreenManagerGtk::EnsureInit() failed from GetNumberOfScreens"); 1.279 + return rv; 1.280 + } 1.281 + *aNumberOfScreens = mCachedScreenArray.Count(); 1.282 + return NS_OK; 1.283 + 1.284 +} // GetNumberOfScreens 1.285 + 1.286 +NS_IMETHODIMP 1.287 +nsScreenManagerGtk::GetSystemDefaultScale(float *aDefaultScale) 1.288 +{ 1.289 + *aDefaultScale = 1.0f; 1.290 + return NS_OK; 1.291 +} 1.292 + 1.293 +NS_IMETHODIMP 1.294 +nsScreenManagerGtk :: ScreenForNativeWidget (void *aWidget, nsIScreen **outScreen) 1.295 +{ 1.296 + nsresult rv; 1.297 + rv = EnsureInit(); 1.298 + if (NS_FAILED(rv)) { 1.299 + NS_ERROR("nsScreenManagerGtk::EnsureInit() failed from ScreenForNativeWidget"); 1.300 + return rv; 1.301 + } 1.302 + 1.303 + if (mCachedScreenArray.Count() > 1) { 1.304 + // I don't know how to go from GtkWindow to nsIScreen, especially 1.305 + // given xinerama and stuff, so let's just do this 1.306 + gint x, y, width, height; 1.307 +#if (MOZ_WIDGET_GTK == 2) 1.308 + gint depth; 1.309 +#endif 1.310 + x = y = width = height = 0; 1.311 + 1.312 +#if (MOZ_WIDGET_GTK == 2) 1.313 + gdk_window_get_geometry(GDK_WINDOW(aWidget), &x, &y, &width, &height, 1.314 + &depth); 1.315 +#else 1.316 + gdk_window_get_geometry(GDK_WINDOW(aWidget), &x, &y, &width, &height); 1.317 +#endif 1.318 + gdk_window_get_origin(GDK_WINDOW(aWidget), &x, &y); 1.319 + rv = ScreenForRect(x, y, width, height, outScreen); 1.320 + } else { 1.321 + rv = GetPrimaryScreen(outScreen); 1.322 + } 1.323 + 1.324 + return rv; 1.325 +}