michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsScreenManagerGtk.h" michael@0: #include "nsScreenGtk.h" michael@0: #include "nsIComponentManager.h" michael@0: #include "nsRect.h" michael@0: #include "nsAutoPtr.h" michael@0: michael@0: #define SCREEN_MANAGER_LIBRARY_LOAD_FAILED ((PRLibrary*)1) michael@0: michael@0: #ifdef MOZ_X11 michael@0: #include michael@0: // prototypes from Xinerama.h michael@0: typedef Bool (*_XnrmIsActive_fn)(Display *dpy); michael@0: typedef XineramaScreenInfo* (*_XnrmQueryScreens_fn)(Display *dpy, int *number); michael@0: #endif michael@0: michael@0: #include michael@0: michael@0: michael@0: static GdkFilterReturn michael@0: root_window_event_filter(GdkXEvent *aGdkXEvent, GdkEvent *aGdkEvent, michael@0: gpointer aClosure) michael@0: { michael@0: nsScreenManagerGtk *manager = static_cast(aClosure); michael@0: #ifdef MOZ_X11 michael@0: XEvent *xevent = static_cast(aGdkXEvent); michael@0: michael@0: // See comments in nsScreenGtk::Init below. michael@0: switch (xevent->type) { michael@0: case ConfigureNotify: michael@0: manager->Init(); michael@0: break; michael@0: case PropertyNotify: michael@0: { michael@0: XPropertyEvent *propertyEvent = &xevent->xproperty; michael@0: if (propertyEvent->atom == manager->NetWorkareaAtom()) { michael@0: manager->Init(); michael@0: } michael@0: } michael@0: break; michael@0: default: michael@0: break; michael@0: } michael@0: #endif michael@0: michael@0: return GDK_FILTER_CONTINUE; michael@0: } michael@0: michael@0: nsScreenManagerGtk :: nsScreenManagerGtk ( ) michael@0: : mXineramalib(nullptr) michael@0: , mRootWindow(nullptr) michael@0: { michael@0: // nothing else to do. I guess we could cache a bunch of information michael@0: // here, but we want to ask the device at runtime in case anything michael@0: // has changed. michael@0: } michael@0: michael@0: michael@0: nsScreenManagerGtk :: ~nsScreenManagerGtk() michael@0: { michael@0: if (mRootWindow) { michael@0: gdk_window_remove_filter(mRootWindow, root_window_event_filter, this); michael@0: g_object_unref(mRootWindow); michael@0: mRootWindow = nullptr; michael@0: } michael@0: michael@0: /* XineramaIsActive() registers a callback function close_display() michael@0: * in X, which is to be called in XCloseDisplay(). This is the case michael@0: * if Xinerama is active, even if only with one screen. michael@0: * michael@0: * We can't unload libXinerama.so.1 here because this will make michael@0: * the address of close_display() registered in X to be invalid and michael@0: * it will crash when XCloseDisplay() is called later. */ michael@0: } michael@0: michael@0: michael@0: // addref, release, QI michael@0: NS_IMPL_ISUPPORTS(nsScreenManagerGtk, nsIScreenManager) michael@0: michael@0: michael@0: // this function will make sure that everything has been initialized. michael@0: nsresult michael@0: nsScreenManagerGtk :: EnsureInit() michael@0: { michael@0: if (mCachedScreenArray.Count() > 0) michael@0: return NS_OK; michael@0: michael@0: mRootWindow = gdk_get_default_root_window(); michael@0: g_object_ref(mRootWindow); michael@0: michael@0: // GDK_STRUCTURE_MASK ==> StructureNotifyMask, for ConfigureNotify michael@0: // GDK_PROPERTY_CHANGE_MASK ==> PropertyChangeMask, for PropertyNotify michael@0: gdk_window_set_events(mRootWindow, michael@0: GdkEventMask(gdk_window_get_events(mRootWindow) | michael@0: GDK_STRUCTURE_MASK | michael@0: GDK_PROPERTY_CHANGE_MASK)); michael@0: gdk_window_add_filter(mRootWindow, root_window_event_filter, this); michael@0: #ifdef MOZ_X11 michael@0: mNetWorkareaAtom = michael@0: XInternAtom(GDK_WINDOW_XDISPLAY(mRootWindow), "_NET_WORKAREA", False); michael@0: #endif michael@0: michael@0: return Init(); michael@0: } michael@0: michael@0: nsresult michael@0: nsScreenManagerGtk :: Init() michael@0: { michael@0: #ifdef MOZ_X11 michael@0: XineramaScreenInfo *screenInfo = nullptr; michael@0: int numScreens; michael@0: michael@0: if (!mXineramalib) { michael@0: mXineramalib = PR_LoadLibrary("libXinerama.so.1"); michael@0: if (!mXineramalib) { michael@0: mXineramalib = SCREEN_MANAGER_LIBRARY_LOAD_FAILED; michael@0: } michael@0: } michael@0: if (mXineramalib && mXineramalib != SCREEN_MANAGER_LIBRARY_LOAD_FAILED) { michael@0: _XnrmIsActive_fn _XnrmIsActive = (_XnrmIsActive_fn) michael@0: PR_FindFunctionSymbol(mXineramalib, "XineramaIsActive"); michael@0: michael@0: _XnrmQueryScreens_fn _XnrmQueryScreens = (_XnrmQueryScreens_fn) michael@0: PR_FindFunctionSymbol(mXineramalib, "XineramaQueryScreens"); michael@0: michael@0: // get the number of screens via xinerama michael@0: Display *display = GDK_DISPLAY_XDISPLAY(gdk_display_get_default()); michael@0: if (_XnrmIsActive && _XnrmQueryScreens && _XnrmIsActive(display)) { michael@0: screenInfo = _XnrmQueryScreens(display, &numScreens); michael@0: } michael@0: } michael@0: michael@0: // screenInfo == nullptr if either Xinerama couldn't be loaded or michael@0: // isn't running on the current display michael@0: if (!screenInfo || numScreens == 1) { michael@0: numScreens = 1; michael@0: #endif michael@0: nsRefPtr screen; michael@0: michael@0: if (mCachedScreenArray.Count() > 0) { michael@0: screen = static_cast(mCachedScreenArray[0]); michael@0: } else { michael@0: screen = new nsScreenGtk(); michael@0: if (!screen || !mCachedScreenArray.AppendObject(screen)) { michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: } michael@0: michael@0: screen->Init(mRootWindow); michael@0: #ifdef MOZ_X11 michael@0: } michael@0: // If Xinerama is enabled and there's more than one screen, fill michael@0: // in the info for all of the screens. If that's not the case michael@0: // then nsScreenGTK() defaults to the screen width + height michael@0: else { michael@0: #ifdef DEBUG michael@0: printf("Xinerama superpowers activated for %d screens!\n", numScreens); michael@0: #endif michael@0: for (int i = 0; i < numScreens; ++i) { michael@0: nsRefPtr screen; michael@0: if (mCachedScreenArray.Count() > i) { michael@0: screen = static_cast(mCachedScreenArray[i]); michael@0: } else { michael@0: screen = new nsScreenGtk(); michael@0: if (!screen || !mCachedScreenArray.AppendObject(screen)) { michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: } michael@0: michael@0: // initialize this screen object michael@0: screen->Init(&screenInfo[i]); michael@0: } michael@0: } michael@0: // Remove any screens that are no longer present. michael@0: while (mCachedScreenArray.Count() > numScreens) { michael@0: mCachedScreenArray.RemoveObjectAt(mCachedScreenArray.Count() - 1); michael@0: } michael@0: michael@0: if (screenInfo) { michael@0: XFree(screenInfo); michael@0: } michael@0: #endif michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: // michael@0: // ScreenForRect michael@0: // michael@0: // Returns the screen that contains the rectangle. If the rect overlaps michael@0: // multiple screens, it picks the screen with the greatest area of intersection. michael@0: // michael@0: // The coordinates are in pixels (not app units) and in screen coordinates. michael@0: // michael@0: NS_IMETHODIMP michael@0: nsScreenManagerGtk :: ScreenForRect ( int32_t aX, int32_t aY, michael@0: int32_t aWidth, int32_t aHeight, michael@0: nsIScreen **aOutScreen ) michael@0: { michael@0: nsresult rv; michael@0: rv = EnsureInit(); michael@0: if (NS_FAILED(rv)) { michael@0: NS_ERROR("nsScreenManagerGtk::EnsureInit() failed from ScreenForRect"); michael@0: return rv; michael@0: } michael@0: // which screen ( index from zero ) should we return? michael@0: uint32_t which = 0; michael@0: // Optimize for the common case. If the number of screens is only michael@0: // one then this will fall through with which == 0 and will get the michael@0: // primary screen. michael@0: if (mCachedScreenArray.Count() > 1) { michael@0: // walk the list of screens and find the one that has the most michael@0: // surface area. michael@0: uint32_t area = 0; michael@0: nsIntRect windowRect(aX, aY, aWidth, aHeight); michael@0: for (int32_t i = 0, i_end = mCachedScreenArray.Count(); i < i_end; ++i) { michael@0: int32_t x, y, width, height; michael@0: x = y = width = height = 0; michael@0: mCachedScreenArray[i]->GetRect(&x, &y, &width, &height); michael@0: // calculate the surface area michael@0: nsIntRect screenRect(x, y, width, height); michael@0: screenRect.IntersectRect(screenRect, windowRect); michael@0: uint32_t tempArea = screenRect.width * screenRect.height; michael@0: if (tempArea >= area) { michael@0: which = i; michael@0: area = tempArea; michael@0: } michael@0: } michael@0: } michael@0: *aOutScreen = mCachedScreenArray.SafeObjectAt(which); michael@0: NS_IF_ADDREF(*aOutScreen); michael@0: return NS_OK; michael@0: michael@0: } // ScreenForRect michael@0: michael@0: michael@0: // michael@0: // GetPrimaryScreen michael@0: // michael@0: // The screen with the menubar/taskbar. This shouldn't be needed very michael@0: // often. michael@0: // michael@0: NS_IMETHODIMP michael@0: nsScreenManagerGtk :: GetPrimaryScreen(nsIScreen * *aPrimaryScreen) michael@0: { michael@0: nsresult rv; michael@0: rv = EnsureInit(); michael@0: if (NS_FAILED(rv)) { michael@0: NS_ERROR("nsScreenManagerGtk::EnsureInit() failed from GetPrimaryScreen"); michael@0: return rv; michael@0: } michael@0: *aPrimaryScreen = mCachedScreenArray.SafeObjectAt(0); michael@0: NS_IF_ADDREF(*aPrimaryScreen); michael@0: return NS_OK; michael@0: michael@0: } // GetPrimaryScreen michael@0: michael@0: michael@0: // michael@0: // GetNumberOfScreens michael@0: // michael@0: // Returns how many physical screens are available. michael@0: // michael@0: NS_IMETHODIMP michael@0: nsScreenManagerGtk :: GetNumberOfScreens(uint32_t *aNumberOfScreens) michael@0: { michael@0: nsresult rv; michael@0: rv = EnsureInit(); michael@0: if (NS_FAILED(rv)) { michael@0: NS_ERROR("nsScreenManagerGtk::EnsureInit() failed from GetNumberOfScreens"); michael@0: return rv; michael@0: } michael@0: *aNumberOfScreens = mCachedScreenArray.Count(); michael@0: return NS_OK; michael@0: michael@0: } // GetNumberOfScreens michael@0: michael@0: NS_IMETHODIMP michael@0: nsScreenManagerGtk::GetSystemDefaultScale(float *aDefaultScale) michael@0: { michael@0: *aDefaultScale = 1.0f; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsScreenManagerGtk :: ScreenForNativeWidget (void *aWidget, nsIScreen **outScreen) michael@0: { michael@0: nsresult rv; michael@0: rv = EnsureInit(); michael@0: if (NS_FAILED(rv)) { michael@0: NS_ERROR("nsScreenManagerGtk::EnsureInit() failed from ScreenForNativeWidget"); michael@0: return rv; michael@0: } michael@0: michael@0: if (mCachedScreenArray.Count() > 1) { michael@0: // I don't know how to go from GtkWindow to nsIScreen, especially michael@0: // given xinerama and stuff, so let's just do this michael@0: gint x, y, width, height; michael@0: #if (MOZ_WIDGET_GTK == 2) michael@0: gint depth; michael@0: #endif michael@0: x = y = width = height = 0; michael@0: michael@0: #if (MOZ_WIDGET_GTK == 2) michael@0: gdk_window_get_geometry(GDK_WINDOW(aWidget), &x, &y, &width, &height, michael@0: &depth); michael@0: #else michael@0: gdk_window_get_geometry(GDK_WINDOW(aWidget), &x, &y, &width, &height); michael@0: #endif michael@0: gdk_window_get_origin(GDK_WINDOW(aWidget), &x, &y); michael@0: rv = ScreenForRect(x, y, width, height, outScreen); michael@0: } else { michael@0: rv = GetPrimaryScreen(outScreen); michael@0: } michael@0: michael@0: return rv; michael@0: }