michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* vim:expandtab:shiftwidth=4:tabstop=4: michael@0: */ 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 "mozilla/ArrayUtils.h" michael@0: #include "mozilla/MiscEvents.h" michael@0: #include "mozilla/MouseEvents.h" michael@0: #include "mozilla/TextEvents.h" michael@0: #include michael@0: michael@0: #include "prlink.h" michael@0: #include "nsGTKToolkit.h" michael@0: #include "nsIRollupListener.h" michael@0: #include "nsIDOMNode.h" michael@0: michael@0: #include "nsWidgetsCID.h" michael@0: #include "nsDragService.h" michael@0: #include "nsIWidgetListener.h" michael@0: michael@0: #include "nsGtkKeyUtils.h" michael@0: #include "nsGtkCursors.h" michael@0: michael@0: #include michael@0: #if (MOZ_WIDGET_GTK == 3) michael@0: #include michael@0: #endif michael@0: #ifdef MOZ_X11 michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #if (MOZ_WIDGET_GTK == 3) michael@0: #include michael@0: #endif michael@0: michael@0: #ifdef AIX michael@0: #include michael@0: #else michael@0: #include michael@0: #endif michael@0: michael@0: #if (MOZ_WIDGET_GTK == 2) michael@0: #include "gtk2xtbin.h" michael@0: #endif michael@0: #endif /* MOZ_X11 */ michael@0: #include michael@0: #if (MOZ_WIDGET_GTK == 2) michael@0: #include michael@0: #endif michael@0: michael@0: #include "nsGkAtoms.h" michael@0: michael@0: #ifdef MOZ_ENABLE_STARTUP_NOTIFICATION michael@0: #define SN_API_NOT_YET_FROZEN michael@0: #include michael@0: #endif michael@0: michael@0: #include "mozilla/Likely.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "nsIPrefService.h" michael@0: #include "nsIGConfService.h" michael@0: #include "nsIServiceManager.h" michael@0: #include "nsIStringBundle.h" michael@0: #include "nsGfxCIID.h" michael@0: #include "nsGtkUtils.h" michael@0: #include "nsIObserverService.h" michael@0: #include "mozilla/layers/LayersTypes.h" michael@0: #include "nsIIdleServiceInternal.h" michael@0: #include "nsIPropertyBag2.h" michael@0: #include "GLContext.h" michael@0: #include "gfx2DGlue.h" michael@0: michael@0: #ifdef ACCESSIBILITY michael@0: #include "mozilla/a11y/Accessible.h" michael@0: #include "mozilla/a11y/Platform.h" michael@0: #include "nsAccessibilityService.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::widget; michael@0: #endif michael@0: michael@0: /* For SetIcon */ michael@0: #include "nsAppDirectoryServiceDefs.h" michael@0: #include "nsXPIDLString.h" michael@0: #include "nsIFile.h" michael@0: michael@0: /* SetCursor(imgIContainer*) */ michael@0: #include michael@0: #include michael@0: #include "imgIContainer.h" michael@0: #include "nsGfxCIID.h" michael@0: #include "nsImageToPixbuf.h" michael@0: #include "nsIInterfaceRequestorUtils.h" michael@0: #include "nsAutoPtr.h" michael@0: #include "ClientLayerManager.h" michael@0: michael@0: extern "C" { michael@0: #define PIXMAN_DONT_DEFINE_STDINT michael@0: #include "pixman.h" michael@0: } michael@0: #include "gfxPlatformGtk.h" michael@0: #include "gfxContext.h" michael@0: #include "gfxImageSurface.h" michael@0: #include "gfxUtils.h" michael@0: #include "Layers.h" michael@0: #include "GLContextProvider.h" michael@0: #include "mozilla/gfx/2D.h" michael@0: #include "mozilla/layers/CompositorParent.h" michael@0: michael@0: #ifdef MOZ_X11 michael@0: #include "gfxXlibSurface.h" michael@0: #include "cairo-xlib.h" michael@0: #endif michael@0: michael@0: #include "nsShmImage.h" michael@0: michael@0: #include "nsIDOMWheelEvent.h" michael@0: michael@0: #include "NativeKeyBindings.h" michael@0: #include "nsWindow.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::gfx; michael@0: using namespace mozilla::widget; michael@0: using namespace mozilla::layers; michael@0: using mozilla::gl::GLContext; michael@0: michael@0: // Don't put more than this many rects in the dirty region, just fluff michael@0: // out to the bounding-box if there are more michael@0: #define MAX_RECTS_IN_REGION 100 michael@0: michael@0: const gint kEvents = GDK_EXPOSURE_MASK | GDK_STRUCTURE_MASK | michael@0: GDK_VISIBILITY_NOTIFY_MASK | michael@0: GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK | michael@0: GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | michael@0: GDK_SCROLL_MASK | michael@0: GDK_POINTER_MOTION_MASK; michael@0: michael@0: /* utility functions */ michael@0: static bool is_mouse_in_window(GdkWindow* aWindow, michael@0: gdouble aMouseX, gdouble aMouseY); michael@0: static nsWindow *get_window_for_gtk_widget(GtkWidget *widget); michael@0: static nsWindow *get_window_for_gdk_window(GdkWindow *window); michael@0: static GtkWidget *get_gtk_widget_for_gdk_window(GdkWindow *window); michael@0: static GdkCursor *get_gtk_cursor(nsCursor aCursor); michael@0: michael@0: static GdkWindow *get_inner_gdk_window (GdkWindow *aWindow, michael@0: gint x, gint y, michael@0: gint *retx, gint *rety); michael@0: michael@0: static inline bool is_context_menu_key(const WidgetKeyboardEvent& inKeyEvent); michael@0: michael@0: static int is_parent_ungrab_enter(GdkEventCrossing *aEvent); michael@0: static int is_parent_grab_leave(GdkEventCrossing *aEvent); michael@0: michael@0: static void GetBrandName(nsXPIDLString& brandName); michael@0: michael@0: /* callbacks from widgets */ michael@0: #if (MOZ_WIDGET_GTK == 2) michael@0: static gboolean expose_event_cb (GtkWidget *widget, michael@0: GdkEventExpose *event); michael@0: #else michael@0: static gboolean expose_event_cb (GtkWidget *widget, michael@0: cairo_t *rect); michael@0: #endif michael@0: static gboolean configure_event_cb (GtkWidget *widget, michael@0: GdkEventConfigure *event); michael@0: static void container_unrealize_cb (GtkWidget *widget); michael@0: static void size_allocate_cb (GtkWidget *widget, michael@0: GtkAllocation *allocation); michael@0: static gboolean delete_event_cb (GtkWidget *widget, michael@0: GdkEventAny *event); michael@0: static gboolean enter_notify_event_cb (GtkWidget *widget, michael@0: GdkEventCrossing *event); michael@0: static gboolean leave_notify_event_cb (GtkWidget *widget, michael@0: GdkEventCrossing *event); michael@0: static gboolean motion_notify_event_cb (GtkWidget *widget, michael@0: GdkEventMotion *event); michael@0: static gboolean button_press_event_cb (GtkWidget *widget, michael@0: GdkEventButton *event); michael@0: static gboolean button_release_event_cb (GtkWidget *widget, michael@0: GdkEventButton *event); michael@0: static gboolean focus_in_event_cb (GtkWidget *widget, michael@0: GdkEventFocus *event); michael@0: static gboolean focus_out_event_cb (GtkWidget *widget, michael@0: GdkEventFocus *event); michael@0: static gboolean key_press_event_cb (GtkWidget *widget, michael@0: GdkEventKey *event); michael@0: static gboolean key_release_event_cb (GtkWidget *widget, michael@0: GdkEventKey *event); michael@0: static gboolean scroll_event_cb (GtkWidget *widget, michael@0: GdkEventScroll *event); michael@0: static gboolean visibility_notify_event_cb(GtkWidget *widget, michael@0: GdkEventVisibility *event); michael@0: static void hierarchy_changed_cb (GtkWidget *widget, michael@0: GtkWidget *previous_toplevel); michael@0: static gboolean window_state_event_cb (GtkWidget *widget, michael@0: GdkEventWindowState *event); michael@0: static void theme_changed_cb (GtkSettings *settings, michael@0: GParamSpec *pspec, michael@0: nsWindow *data); michael@0: static nsWindow* GetFirstNSWindowForGDKWindow (GdkWindow *aGdkWindow); michael@0: michael@0: #ifdef __cplusplus michael@0: extern "C" { michael@0: #endif /* __cplusplus */ michael@0: #ifdef MOZ_X11 michael@0: static GdkFilterReturn popup_take_focus_filter (GdkXEvent *gdk_xevent, michael@0: GdkEvent *event, michael@0: gpointer data); michael@0: static GdkFilterReturn plugin_window_filter_func (GdkXEvent *gdk_xevent, michael@0: GdkEvent *event, michael@0: gpointer data); michael@0: static GdkFilterReturn plugin_client_message_filter (GdkXEvent *xevent, michael@0: GdkEvent *event, michael@0: gpointer data); michael@0: #endif /* MOZ_X11 */ michael@0: #ifdef __cplusplus michael@0: } michael@0: #endif /* __cplusplus */ michael@0: michael@0: static gboolean drag_motion_event_cb (GtkWidget *aWidget, michael@0: GdkDragContext *aDragContext, michael@0: gint aX, michael@0: gint aY, michael@0: guint aTime, michael@0: gpointer aData); michael@0: static void drag_leave_event_cb (GtkWidget *aWidget, michael@0: GdkDragContext *aDragContext, michael@0: guint aTime, michael@0: gpointer aData); michael@0: static gboolean drag_drop_event_cb (GtkWidget *aWidget, michael@0: GdkDragContext *aDragContext, michael@0: gint aX, michael@0: gint aY, michael@0: guint aTime, michael@0: gpointer aData); michael@0: static void drag_data_received_event_cb(GtkWidget *aWidget, michael@0: GdkDragContext *aDragContext, michael@0: gint aX, michael@0: gint aY, michael@0: GtkSelectionData *aSelectionData, michael@0: guint aInfo, michael@0: guint32 aTime, michael@0: gpointer aData); michael@0: michael@0: /* initialization static functions */ michael@0: static nsresult initialize_prefs (void); michael@0: michael@0: static guint32 sLastUserInputTime = GDK_CURRENT_TIME; michael@0: static guint32 sRetryGrabTime; michael@0: michael@0: static NS_DEFINE_IID(kCDragServiceCID, NS_DRAGSERVICE_CID); michael@0: michael@0: // The window from which the focus manager asks us to dispatch key events. michael@0: static nsWindow *gFocusWindow = nullptr; michael@0: static bool gBlockActivateEvent = false; michael@0: static bool gGlobalsInitialized = false; michael@0: static bool gRaiseWindows = true; michael@0: static nsWindow *gPluginFocusWindow = nullptr; michael@0: michael@0: michael@0: #define NS_WINDOW_TITLE_MAX_LENGTH 4095 michael@0: michael@0: // If after selecting profile window, the startup fail, please refer to michael@0: // http://bugzilla.gnome.org/show_bug.cgi?id=88940 michael@0: michael@0: // needed for imgIContainer cursors michael@0: // GdkDisplay* was added in 2.2 michael@0: typedef struct _GdkDisplay GdkDisplay; michael@0: michael@0: #define kWindowPositionSlop 20 michael@0: michael@0: // cursor cache michael@0: static GdkCursor *gCursorCache[eCursorCount]; michael@0: michael@0: static GtkWidget *gInvisibleContainer = nullptr; michael@0: michael@0: // Sometimes this actually also includes the state of the modifier keys, but michael@0: // only the button state bits are used. michael@0: static guint gButtonState; michael@0: michael@0: // nsAutoRef uses nsSimpleRef<> to know how to automatically michael@0: // destroy regions. michael@0: template <> michael@0: class nsSimpleRef : public pixman_region32 { michael@0: protected: michael@0: typedef pixman_region32 RawRef; michael@0: michael@0: nsSimpleRef() { data = nullptr; } michael@0: nsSimpleRef(const RawRef &aRawRef) : pixman_region32(aRawRef) { } michael@0: michael@0: static void Release(pixman_region32& region) { michael@0: pixman_region32_fini(®ion); michael@0: } michael@0: // Whether this needs to be released: michael@0: bool HaveResource() const { return data != nullptr; } michael@0: michael@0: pixman_region32& get() { return *this; } michael@0: }; michael@0: michael@0: static inline int32_t michael@0: GetBitmapStride(int32_t width) michael@0: { michael@0: #if defined(MOZ_X11) || (MOZ_WIDGET_GTK == 2) michael@0: return (width+7)/8; michael@0: #else michael@0: return cairo_format_stride_for_width(CAIRO_FORMAT_A1, width); michael@0: #endif michael@0: } michael@0: michael@0: static inline bool TimestampIsNewerThan(guint32 a, guint32 b) michael@0: { michael@0: // Timestamps are just the least significant bits of a monotonically michael@0: // increasing function, and so the use of unsigned overflow arithmetic. michael@0: return a - b <= G_MAXUINT32/2; michael@0: } michael@0: michael@0: static void michael@0: UpdateLastInputEventTime(void *aGdkEvent) michael@0: { michael@0: nsCOMPtr idleService = michael@0: do_GetService("@mozilla.org/widget/idleservice;1"); michael@0: if (idleService) { michael@0: idleService->ResetIdleTimeOut(0); michael@0: } michael@0: michael@0: guint timestamp = gdk_event_get_time(static_cast(aGdkEvent)); michael@0: if (timestamp == GDK_CURRENT_TIME) michael@0: return; michael@0: michael@0: sLastUserInputTime = timestamp; michael@0: } michael@0: michael@0: nsWindow::nsWindow() michael@0: { michael@0: mIsTopLevel = false; michael@0: mIsDestroyed = false; michael@0: mNeedsResize = false; michael@0: mNeedsMove = false; michael@0: mListenForResizes = false; michael@0: mIsShown = false; michael@0: mNeedsShow = false; michael@0: mEnabled = true; michael@0: mCreated = false; michael@0: michael@0: mContainer = nullptr; michael@0: mGdkWindow = nullptr; michael@0: mShell = nullptr; michael@0: mHasMappedToplevel = false; michael@0: mIsFullyObscured = false; michael@0: mRetryPointerGrab = false; michael@0: mWindowType = eWindowType_child; michael@0: mSizeState = nsSizeMode_Normal; michael@0: mLastSizeMode = nsSizeMode_Normal; michael@0: mSizeConstraints.mMaxSize = GetSafeWindowSize(mSizeConstraints.mMaxSize); michael@0: michael@0: #ifdef MOZ_X11 michael@0: mOldFocusWindow = 0; michael@0: #endif /* MOZ_X11 */ michael@0: mPluginType = PluginType_NONE; michael@0: michael@0: if (!gGlobalsInitialized) { michael@0: gGlobalsInitialized = true; michael@0: michael@0: // It's OK if either of these fail, but it may not be one day. michael@0: initialize_prefs(); michael@0: } michael@0: michael@0: mLastMotionPressure = 0; michael@0: michael@0: #ifdef ACCESSIBILITY michael@0: mRootAccessible = nullptr; michael@0: #endif michael@0: michael@0: mIsTransparent = false; michael@0: mTransparencyBitmap = nullptr; michael@0: michael@0: mTransparencyBitmapWidth = 0; michael@0: mTransparencyBitmapHeight = 0; michael@0: } michael@0: michael@0: nsWindow::~nsWindow() michael@0: { michael@0: LOG(("nsWindow::~nsWindow() [%p]\n", (void *)this)); michael@0: michael@0: delete[] mTransparencyBitmap; michael@0: mTransparencyBitmap = nullptr; michael@0: michael@0: Destroy(); michael@0: } michael@0: michael@0: /* static */ void michael@0: nsWindow::ReleaseGlobals() michael@0: { michael@0: for (uint32_t i = 0; i < ArrayLength(gCursorCache); ++i) { michael@0: if (gCursorCache[i]) { michael@0: gdk_cursor_unref(gCursorCache[i]); michael@0: gCursorCache[i] = nullptr; michael@0: } michael@0: } michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS_INHERITED(nsWindow, nsBaseWidget, michael@0: nsISupportsWeakReference) michael@0: michael@0: void michael@0: nsWindow::CommonCreate(nsIWidget *aParent, bool aListenForResizes) michael@0: { michael@0: mParent = aParent; michael@0: mListenForResizes = aListenForResizes; michael@0: mCreated = true; michael@0: } michael@0: michael@0: void michael@0: nsWindow::DispatchActivateEvent(void) michael@0: { michael@0: NS_ASSERTION(mContainer || mIsDestroyed, michael@0: "DispatchActivateEvent only intended for container windows"); michael@0: michael@0: #ifdef ACCESSIBILITY michael@0: DispatchActivateEventAccessible(); michael@0: #endif //ACCESSIBILITY michael@0: michael@0: if (mWidgetListener) michael@0: mWidgetListener->WindowActivated(); michael@0: } michael@0: michael@0: void michael@0: nsWindow::DispatchDeactivateEvent(void) michael@0: { michael@0: if (mWidgetListener) michael@0: mWidgetListener->WindowDeactivated(); michael@0: michael@0: #ifdef ACCESSIBILITY michael@0: DispatchDeactivateEventAccessible(); michael@0: #endif //ACCESSIBILITY michael@0: } michael@0: michael@0: void michael@0: nsWindow::DispatchResized(int32_t aWidth, int32_t aHeight) michael@0: { michael@0: nsIWidgetListener *listeners[] = michael@0: { mWidgetListener, mAttachedWidgetListener }; michael@0: for (size_t i = 0; i < ArrayLength(listeners); ++i) { michael@0: if (listeners[i]) { michael@0: listeners[i]->WindowResized(this, aWidth, aHeight); michael@0: } michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: nsWindow::DispatchEvent(WidgetGUIEvent* aEvent, nsEventStatus& aStatus) michael@0: { michael@0: #ifdef DEBUG michael@0: debug_DumpEvent(stdout, aEvent->widget, aEvent, michael@0: nsAutoCString("something"), 0); michael@0: #endif michael@0: michael@0: aStatus = nsEventStatus_eIgnore; michael@0: nsIWidgetListener* listener = michael@0: mAttachedWidgetListener ? mAttachedWidgetListener : mWidgetListener; michael@0: if (listener) { michael@0: aStatus = listener->HandleEvent(aEvent, mUseAttachedEvents); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsWindow::OnDestroy(void) michael@0: { michael@0: if (mOnDestroyCalled) michael@0: return; michael@0: michael@0: mOnDestroyCalled = true; michael@0: michael@0: // Prevent deletion. michael@0: nsCOMPtr kungFuDeathGrip = this; michael@0: michael@0: // release references to children, device context, toolkit + app shell michael@0: nsBaseWidget::OnDestroy(); michael@0: michael@0: // Remove association between this object and its parent and siblings. michael@0: nsBaseWidget::Destroy(); michael@0: mParent = nullptr; michael@0: michael@0: NotifyWindowDestroyed(); michael@0: } michael@0: michael@0: bool michael@0: nsWindow::AreBoundsSane(void) michael@0: { michael@0: if (mBounds.width > 0 && mBounds.height > 0) michael@0: return true; michael@0: michael@0: return false; michael@0: } michael@0: michael@0: static GtkWidget* michael@0: EnsureInvisibleContainer() michael@0: { michael@0: if (!gInvisibleContainer) { michael@0: // GtkWidgets need to be anchored to a GtkWindow to be realized (to michael@0: // have a window). Using GTK_WINDOW_POPUP rather than michael@0: // GTK_WINDOW_TOPLEVEL in the hope that POPUP results in less michael@0: // initialization and window manager interaction. michael@0: GtkWidget* window = gtk_window_new(GTK_WINDOW_POPUP); michael@0: gInvisibleContainer = moz_container_new(); michael@0: gtk_container_add(GTK_CONTAINER(window), gInvisibleContainer); michael@0: gtk_widget_realize(gInvisibleContainer); michael@0: michael@0: } michael@0: return gInvisibleContainer; michael@0: } michael@0: michael@0: static void michael@0: CheckDestroyInvisibleContainer() michael@0: { michael@0: NS_PRECONDITION(gInvisibleContainer, "oh, no"); michael@0: michael@0: if (!gdk_window_peek_children(gtk_widget_get_window(gInvisibleContainer))) { michael@0: // No children, so not in use. michael@0: // Make sure to destroy the GtkWindow also. michael@0: gtk_widget_destroy(gtk_widget_get_parent(gInvisibleContainer)); michael@0: gInvisibleContainer = nullptr; michael@0: } michael@0: } michael@0: michael@0: // Change the containing GtkWidget on a sub-hierarchy of GdkWindows belonging michael@0: // to aOldWidget and rooted at aWindow, and reparent any child GtkWidgets of michael@0: // the GdkWindow hierarchy to aNewWidget. michael@0: static void michael@0: SetWidgetForHierarchy(GdkWindow *aWindow, michael@0: GtkWidget *aOldWidget, michael@0: GtkWidget *aNewWidget) michael@0: { michael@0: gpointer data; michael@0: gdk_window_get_user_data(aWindow, &data); michael@0: michael@0: if (data != aOldWidget) { michael@0: if (!GTK_IS_WIDGET(data)) michael@0: return; michael@0: michael@0: GtkWidget* widget = static_cast(data); michael@0: if (gtk_widget_get_parent(widget) != aOldWidget) michael@0: return; michael@0: michael@0: // This window belongs to a child widget, which will no longer be a michael@0: // child of aOldWidget. michael@0: gtk_widget_reparent(widget, aNewWidget); michael@0: michael@0: return; michael@0: } michael@0: michael@0: GList *children = gdk_window_get_children(aWindow); michael@0: for(GList *list = children; list; list = list->next) { michael@0: SetWidgetForHierarchy(GDK_WINDOW(list->data), aOldWidget, aNewWidget); michael@0: } michael@0: g_list_free(children); michael@0: michael@0: gdk_window_set_user_data(aWindow, aNewWidget); michael@0: } michael@0: michael@0: // Walk the list of child windows and call destroy on them. michael@0: void michael@0: nsWindow::DestroyChildWindows() michael@0: { michael@0: if (!mGdkWindow) michael@0: return; michael@0: michael@0: while (GList *children = gdk_window_peek_children(mGdkWindow)) { michael@0: GdkWindow *child = GDK_WINDOW(children->data); michael@0: nsWindow *kid = get_window_for_gdk_window(child); michael@0: if (kid) { michael@0: kid->Destroy(); michael@0: } else { michael@0: // This child is not an nsWindow. michael@0: // Destroy the child GtkWidget. michael@0: gpointer data; michael@0: gdk_window_get_user_data(child, &data); michael@0: if (GTK_IS_WIDGET(data)) { michael@0: gtk_widget_destroy(static_cast(data)); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindow::Destroy(void) michael@0: { michael@0: if (mIsDestroyed || !mCreated) michael@0: return NS_OK; michael@0: michael@0: LOG(("nsWindow::Destroy [%p]\n", (void *)this)); michael@0: mIsDestroyed = true; michael@0: mCreated = false; michael@0: michael@0: /** Need to clean our LayerManager up while still alive */ michael@0: if (mLayerManager) { michael@0: mLayerManager->Destroy(); michael@0: } michael@0: mLayerManager = nullptr; michael@0: michael@0: // It is safe to call DestroyeCompositor several times (here and michael@0: // in the parent class) since it will take effect only once. michael@0: // The reason we call it here is because on gtk platforms we need michael@0: // to destroy the compositor before we destroy the gdk window (which michael@0: // destroys the the gl context attached to it). michael@0: DestroyCompositor(); michael@0: michael@0: ClearCachedResources(); michael@0: michael@0: g_signal_handlers_disconnect_by_func(gtk_settings_get_default(), michael@0: FuncToGpointer(theme_changed_cb), michael@0: this); michael@0: michael@0: nsIRollupListener* rollupListener = nsBaseWidget::GetActiveRollupListener(); michael@0: if (rollupListener) { michael@0: nsCOMPtr rollupWidget = rollupListener->GetRollupWidget(); michael@0: if (static_cast(this) == rollupWidget) { michael@0: rollupListener->Rollup(0, nullptr, nullptr); michael@0: } michael@0: } michael@0: michael@0: // dragService will be null after shutdown of the service manager. michael@0: nsDragService *dragService = nsDragService::GetInstance(); michael@0: if (dragService && this == dragService->GetMostRecentDestWindow()) { michael@0: dragService->ScheduleLeaveEvent(); michael@0: } michael@0: michael@0: NativeShow(false); michael@0: michael@0: if (mIMModule) { michael@0: mIMModule->OnDestroyWindow(this); michael@0: } michael@0: michael@0: // make sure that we remove ourself as the focus window michael@0: if (gFocusWindow == this) { michael@0: LOGFOCUS(("automatically losing focus...\n")); michael@0: gFocusWindow = nullptr; michael@0: } michael@0: michael@0: #if (MOZ_WIDGET_GTK == 2) && defined(MOZ_X11) michael@0: // make sure that we remove ourself as the plugin focus window michael@0: if (gPluginFocusWindow == this) { michael@0: gPluginFocusWindow->LoseNonXEmbedPluginFocus(); michael@0: } michael@0: #endif /* MOZ_X11 && MOZ_WIDGET_GTK2 */ michael@0: michael@0: // Destroy thebes surface now. Badness can happen if we destroy michael@0: // the surface after its X Window. michael@0: mThebesSurface = nullptr; michael@0: michael@0: GtkWidget *owningWidget = GetMozContainerWidget(); michael@0: if (mShell) { michael@0: gtk_widget_destroy(mShell); michael@0: mShell = nullptr; michael@0: mContainer = nullptr; michael@0: NS_ABORT_IF_FALSE(!mGdkWindow, michael@0: "mGdkWindow should be NULL when mContainer is destroyed"); michael@0: } michael@0: else if (mContainer) { michael@0: gtk_widget_destroy(GTK_WIDGET(mContainer)); michael@0: mContainer = nullptr; michael@0: NS_ABORT_IF_FALSE(!mGdkWindow, michael@0: "mGdkWindow should be NULL when mContainer is destroyed"); michael@0: } michael@0: else if (mGdkWindow) { michael@0: // Destroy child windows to ensure that their mThebesSurfaces are michael@0: // released and to remove references from GdkWindows back to their michael@0: // container widget. (OnContainerUnrealize() does this when the michael@0: // MozContainer widget is destroyed.) michael@0: DestroyChildWindows(); michael@0: michael@0: gdk_window_set_user_data(mGdkWindow, nullptr); michael@0: g_object_set_data(G_OBJECT(mGdkWindow), "nsWindow", nullptr); michael@0: gdk_window_destroy(mGdkWindow); michael@0: mGdkWindow = nullptr; michael@0: } michael@0: michael@0: if (gInvisibleContainer && owningWidget == gInvisibleContainer) { michael@0: CheckDestroyInvisibleContainer(); michael@0: } michael@0: michael@0: #ifdef ACCESSIBILITY michael@0: if (mRootAccessible) { michael@0: mRootAccessible = nullptr; michael@0: } michael@0: #endif michael@0: michael@0: // Save until last because OnDestroy() may cause us to be deleted. michael@0: OnDestroy(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsIWidget * michael@0: nsWindow::GetParent(void) michael@0: { michael@0: return mParent; michael@0: } michael@0: michael@0: float michael@0: nsWindow::GetDPI() michael@0: { michael@0: Display *dpy = GDK_DISPLAY_XDISPLAY(gdk_display_get_default()); michael@0: int defaultScreen = DefaultScreen(dpy); michael@0: double heightInches = DisplayHeightMM(dpy, defaultScreen)/MM_PER_INCH_FLOAT; michael@0: if (heightInches < 0.25) { michael@0: // Something's broken, but we'd better not crash. michael@0: return 96.0f; michael@0: } michael@0: return float(DisplayHeight(dpy, defaultScreen)/heightInches); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindow::SetParent(nsIWidget *aNewParent) michael@0: { michael@0: if (mContainer || !mGdkWindow) { michael@0: NS_NOTREACHED("nsWindow::SetParent called illegally"); michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: nsCOMPtr kungFuDeathGrip = this; michael@0: if (mParent) { michael@0: mParent->RemoveChild(this); michael@0: } michael@0: michael@0: mParent = aNewParent; michael@0: michael@0: GtkWidget* oldContainer = GetMozContainerWidget(); michael@0: if (!oldContainer) { michael@0: // The GdkWindows have been destroyed so there is nothing else to michael@0: // reparent. michael@0: NS_ABORT_IF_FALSE(gdk_window_is_destroyed(mGdkWindow), michael@0: "live GdkWindow with no widget"); michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (aNewParent) { michael@0: aNewParent->AddChild(this); michael@0: ReparentNativeWidget(aNewParent); michael@0: } else { michael@0: // aNewParent is nullptr, but reparent to a hidden window to avoid michael@0: // destroying the GdkWindow and its descendants. michael@0: // An invisible container widget is needed to hold descendant michael@0: // GtkWidgets. michael@0: GtkWidget* newContainer = EnsureInvisibleContainer(); michael@0: GdkWindow* newParentWindow = gtk_widget_get_window(newContainer); michael@0: ReparentNativeWidgetInternal(aNewParent, newContainer, newParentWindow, michael@0: oldContainer); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindow::ReparentNativeWidget(nsIWidget* aNewParent) michael@0: { michael@0: NS_PRECONDITION(aNewParent, ""); michael@0: NS_ASSERTION(!mIsDestroyed, ""); michael@0: NS_ASSERTION(!static_cast(aNewParent)->mIsDestroyed, ""); michael@0: michael@0: GtkWidget* oldContainer = GetMozContainerWidget(); michael@0: if (!oldContainer) { michael@0: // The GdkWindows have been destroyed so there is nothing else to michael@0: // reparent. michael@0: NS_ABORT_IF_FALSE(gdk_window_is_destroyed(mGdkWindow), michael@0: "live GdkWindow with no widget"); michael@0: return NS_OK; michael@0: } michael@0: NS_ABORT_IF_FALSE(!gdk_window_is_destroyed(mGdkWindow), michael@0: "destroyed GdkWindow with widget"); michael@0: michael@0: nsWindow* newParent = static_cast(aNewParent); michael@0: GdkWindow* newParentWindow = newParent->mGdkWindow; michael@0: GtkWidget* newContainer = newParent->GetMozContainerWidget(); michael@0: GtkWindow* shell = GTK_WINDOW(mShell); michael@0: michael@0: if (shell && gtk_window_get_transient_for(shell)) { michael@0: GtkWindow* topLevelParent = michael@0: GTK_WINDOW(gtk_widget_get_toplevel(newContainer)); michael@0: gtk_window_set_transient_for(shell, topLevelParent); michael@0: } michael@0: michael@0: ReparentNativeWidgetInternal(aNewParent, newContainer, newParentWindow, michael@0: oldContainer); michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsWindow::ReparentNativeWidgetInternal(nsIWidget* aNewParent, michael@0: GtkWidget* aNewContainer, michael@0: GdkWindow* aNewParentWindow, michael@0: GtkWidget* aOldContainer) michael@0: { michael@0: if (!aNewContainer) { michael@0: // The new parent GdkWindow has been destroyed. michael@0: NS_ABORT_IF_FALSE(!aNewParentWindow || michael@0: gdk_window_is_destroyed(aNewParentWindow), michael@0: "live GdkWindow with no widget"); michael@0: Destroy(); michael@0: } else { michael@0: if (aNewContainer != aOldContainer) { michael@0: NS_ABORT_IF_FALSE(!gdk_window_is_destroyed(aNewParentWindow), michael@0: "destroyed GdkWindow with widget"); michael@0: SetWidgetForHierarchy(mGdkWindow, aOldContainer, aNewContainer); michael@0: michael@0: if (aOldContainer == gInvisibleContainer) { michael@0: CheckDestroyInvisibleContainer(); michael@0: } michael@0: } michael@0: michael@0: if (!mIsTopLevel) { michael@0: gdk_window_reparent(mGdkWindow, aNewParentWindow, mBounds.x, michael@0: mBounds.y); michael@0: } michael@0: } michael@0: michael@0: nsWindow* newParent = static_cast(aNewParent); michael@0: bool parentHasMappedToplevel = michael@0: newParent && newParent->mHasMappedToplevel; michael@0: if (mHasMappedToplevel != parentHasMappedToplevel) { michael@0: SetHasMappedToplevel(parentHasMappedToplevel); michael@0: } michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindow::SetModal(bool aModal) michael@0: { michael@0: LOG(("nsWindow::SetModal [%p] %d\n", (void *)this, aModal)); michael@0: if (mIsDestroyed) michael@0: return aModal ? NS_ERROR_NOT_AVAILABLE : NS_OK; michael@0: if (!mIsTopLevel || !mShell) michael@0: return NS_ERROR_FAILURE; michael@0: gtk_window_set_modal(GTK_WINDOW(mShell), aModal ? TRUE : FALSE); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // nsIWidget method, which means IsShown. michael@0: bool michael@0: nsWindow::IsVisible() const michael@0: { michael@0: return mIsShown; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindow::ConstrainPosition(bool aAllowSlop, int32_t *aX, int32_t *aY) michael@0: { michael@0: if (mIsTopLevel && mShell) { michael@0: int32_t screenWidth = gdk_screen_width(); michael@0: int32_t screenHeight = gdk_screen_height(); michael@0: if (aAllowSlop) { michael@0: if (*aX < (kWindowPositionSlop - mBounds.width)) michael@0: *aX = kWindowPositionSlop - mBounds.width; michael@0: if (*aX > (screenWidth - kWindowPositionSlop)) michael@0: *aX = screenWidth - kWindowPositionSlop; michael@0: if (*aY < (kWindowPositionSlop - mBounds.height)) michael@0: *aY = kWindowPositionSlop - mBounds.height; michael@0: if (*aY > (screenHeight - kWindowPositionSlop)) michael@0: *aY = screenHeight - kWindowPositionSlop; michael@0: } else { michael@0: if (*aX < 0) michael@0: *aX = 0; michael@0: if (*aX > (screenWidth - mBounds.width)) michael@0: *aX = screenWidth - mBounds.width; michael@0: if (*aY < 0) michael@0: *aY = 0; michael@0: if (*aY > (screenHeight - mBounds.height)) michael@0: *aY = screenHeight - mBounds.height; michael@0: } michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: void nsWindow::SetSizeConstraints(const SizeConstraints& aConstraints) michael@0: { michael@0: mSizeConstraints.mMinSize = GetSafeWindowSize(aConstraints.mMinSize); michael@0: mSizeConstraints.mMaxSize = GetSafeWindowSize(aConstraints.mMaxSize); michael@0: michael@0: if (mShell) { michael@0: GdkGeometry geometry; michael@0: geometry.min_width = mSizeConstraints.mMinSize.width; michael@0: geometry.min_height = mSizeConstraints.mMinSize.height; michael@0: geometry.max_width = mSizeConstraints.mMaxSize.width; michael@0: geometry.max_height = mSizeConstraints.mMaxSize.height; michael@0: michael@0: uint32_t hints = GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE; michael@0: gtk_window_set_geometry_hints(GTK_WINDOW(mShell), nullptr, michael@0: &geometry, GdkWindowHints(hints)); michael@0: } michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindow::Show(bool aState) michael@0: { michael@0: if (aState == mIsShown) michael@0: return NS_OK; michael@0: michael@0: // Clear our cached resources when the window is hidden. michael@0: if (mIsShown && !aState) { michael@0: ClearCachedResources(); michael@0: } michael@0: michael@0: mIsShown = aState; michael@0: michael@0: LOG(("nsWindow::Show [%p] state %d\n", (void *)this, aState)); michael@0: michael@0: if (aState) { michael@0: // Now that this window is shown, mHasMappedToplevel needs to be michael@0: // tracked on viewable descendants. michael@0: SetHasMappedToplevel(mHasMappedToplevel); michael@0: } michael@0: michael@0: // Ok, someone called show on a window that isn't sized to a sane michael@0: // value. Mark this window as needing to have Show() called on it michael@0: // and return. michael@0: if ((aState && !AreBoundsSane()) || !mCreated) { michael@0: LOG(("\tbounds are insane or window hasn't been created yet\n")); michael@0: mNeedsShow = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: // If someone is hiding this widget, clear any needing show flag. michael@0: if (!aState) michael@0: mNeedsShow = false; michael@0: michael@0: // If someone is showing this window and it needs a resize then michael@0: // resize the widget. michael@0: if (aState) { michael@0: if (mNeedsMove) { michael@0: NativeResize(mBounds.x, mBounds.y, mBounds.width, mBounds.height, michael@0: false); michael@0: } else if (mNeedsResize) { michael@0: NativeResize(mBounds.width, mBounds.height, false); michael@0: } michael@0: } michael@0: michael@0: #ifdef ACCESSIBILITY michael@0: if (aState && a11y::ShouldA11yBeEnabled()) michael@0: CreateRootAccessible(); michael@0: #endif michael@0: michael@0: NativeShow(aState); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindow::Resize(double aWidth, double aHeight, bool aRepaint) michael@0: { michael@0: CSSToLayoutDeviceScale scale = BoundsUseDisplayPixels() ? GetDefaultScale() michael@0: : CSSToLayoutDeviceScale(1.0); michael@0: int32_t width = NSToIntRound(scale.scale * aWidth); michael@0: int32_t height = NSToIntRound(scale.scale * aHeight); michael@0: ConstrainSize(&width, &height); michael@0: michael@0: // For top-level windows, aWidth and aHeight should possibly be michael@0: // interpreted as frame bounds, but NativeResize treats these as window michael@0: // bounds (Bug 581866). michael@0: michael@0: mBounds.SizeTo(width, height); michael@0: michael@0: if (!mCreated) michael@0: return NS_OK; michael@0: michael@0: // There are several cases here that we need to handle, based on a michael@0: // matrix of the visibility of the widget, the sanity of this resize michael@0: // and whether or not the widget was previously sane. michael@0: michael@0: // Has this widget been set to visible? michael@0: if (mIsShown) { michael@0: // Are the bounds sane? michael@0: if (AreBoundsSane()) { michael@0: // Yep? Resize the window michael@0: //Maybe, the toplevel has moved michael@0: michael@0: // Note that if the widget needs to be positioned because its michael@0: // size was previously insane in Resize(x,y,w,h), then we need michael@0: // to set the x and y here too, because the widget wasn't michael@0: // moved back then michael@0: if (mNeedsMove) michael@0: NativeResize(mBounds.x, mBounds.y, michael@0: mBounds.width, mBounds.height, aRepaint); michael@0: else michael@0: NativeResize(mBounds.width, mBounds.height, aRepaint); michael@0: michael@0: // Does it need to be shown because it was previously insane? michael@0: if (mNeedsShow) michael@0: NativeShow(true); michael@0: } michael@0: else { michael@0: // If someone has set this so that the needs show flag is false michael@0: // and it needs to be hidden, update the flag and hide the michael@0: // window. This flag will be cleared the next time someone michael@0: // hides the window or shows it. It also prevents us from michael@0: // calling NativeShow(false) excessively on the window which michael@0: // causes unneeded X traffic. michael@0: if (!mNeedsShow) { michael@0: mNeedsShow = true; michael@0: NativeShow(false); michael@0: } michael@0: } michael@0: } michael@0: // If the widget hasn't been shown, mark the widget as needing to be michael@0: // resized before it is shown. michael@0: else { michael@0: if (AreBoundsSane() && mListenForResizes) { michael@0: // For widgets that we listen for resizes for (widgets created michael@0: // with native parents) we apparently _always_ have to resize. I michael@0: // dunno why, but apparently we're lame like that. michael@0: NativeResize(width, height, aRepaint); michael@0: } michael@0: else { michael@0: mNeedsResize = true; michael@0: } michael@0: } michael@0: michael@0: NotifyRollupGeometryChange(); michael@0: michael@0: // send a resize notification if this is a toplevel michael@0: if (mIsTopLevel || mListenForResizes) { michael@0: DispatchResized(width, height); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindow::Resize(double aX, double aY, double aWidth, double aHeight, michael@0: bool aRepaint) michael@0: { michael@0: CSSToLayoutDeviceScale scale = BoundsUseDisplayPixels() ? GetDefaultScale() michael@0: : CSSToLayoutDeviceScale(1.0); michael@0: int32_t width = NSToIntRound(scale.scale * aWidth); michael@0: int32_t height = NSToIntRound(scale.scale * aHeight); michael@0: ConstrainSize(&width, &height); michael@0: michael@0: int32_t x = NSToIntRound(scale.scale * aX); michael@0: int32_t y = NSToIntRound(scale.scale * aY); michael@0: mBounds.x = x; michael@0: mBounds.y = y; michael@0: mBounds.SizeTo(width, height); michael@0: michael@0: mNeedsMove = true; michael@0: michael@0: if (!mCreated) michael@0: return NS_OK; michael@0: michael@0: // There are several cases here that we need to handle, based on a michael@0: // matrix of the visibility of the widget, the sanity of this resize michael@0: // and whether or not the widget was previously sane. michael@0: michael@0: // Has this widget been set to visible? michael@0: if (mIsShown) { michael@0: // Are the bounds sane? michael@0: if (AreBoundsSane()) { michael@0: // Yep? Resize the window michael@0: NativeResize(x, y, width, height, aRepaint); michael@0: // Does it need to be shown because it was previously insane? michael@0: if (mNeedsShow) michael@0: NativeShow(true); michael@0: } michael@0: else { michael@0: // If someone has set this so that the needs show flag is false michael@0: // and it needs to be hidden, update the flag and hide the michael@0: // window. This flag will be cleared the next time someone michael@0: // hides the window or shows it. It also prevents us from michael@0: // calling NativeShow(false) excessively on the window which michael@0: // causes unneeded X traffic. michael@0: if (!mNeedsShow) { michael@0: mNeedsShow = true; michael@0: NativeShow(false); michael@0: } michael@0: } michael@0: } michael@0: // If the widget hasn't been shown, mark the widget as needing to be michael@0: // resized before it is shown michael@0: else { michael@0: if (AreBoundsSane() && mListenForResizes){ michael@0: // For widgets that we listen for resizes for (widgets created michael@0: // with native parents) we apparently _always_ have to resize. I michael@0: // dunno why, but apparently we're lame like that. michael@0: NativeResize(x, y, width, height, aRepaint); michael@0: } michael@0: else { michael@0: mNeedsResize = true; michael@0: } michael@0: } michael@0: michael@0: NotifyRollupGeometryChange(); michael@0: michael@0: if (mIsTopLevel || mListenForResizes) { michael@0: DispatchResized(width, height); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindow::Enable(bool aState) michael@0: { michael@0: mEnabled = aState; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: bool michael@0: nsWindow::IsEnabled() const michael@0: { michael@0: return mEnabled; michael@0: } michael@0: michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindow::Move(double aX, double aY) michael@0: { michael@0: LOG(("nsWindow::Move [%p] %f %f\n", (void *)this, michael@0: aX, aY)); michael@0: michael@0: CSSToLayoutDeviceScale scale = BoundsUseDisplayPixels() ? GetDefaultScale() michael@0: : CSSToLayoutDeviceScale(1.0); michael@0: int32_t x = NSToIntRound(aX * scale.scale); michael@0: int32_t y = NSToIntRound(aY * scale.scale); michael@0: michael@0: if (mWindowType == eWindowType_toplevel || michael@0: mWindowType == eWindowType_dialog) { michael@0: SetSizeMode(nsSizeMode_Normal); michael@0: } michael@0: michael@0: // Since a popup window's x/y coordinates are in relation to to michael@0: // the parent, the parent might have moved so we always move a michael@0: // popup window. michael@0: if (x == mBounds.x && y == mBounds.y && michael@0: mWindowType != eWindowType_popup) michael@0: return NS_OK; michael@0: michael@0: // XXX Should we do some AreBoundsSane check here? michael@0: michael@0: mBounds.x = x; michael@0: mBounds.y = y; michael@0: michael@0: if (!mCreated) michael@0: return NS_OK; michael@0: michael@0: mNeedsMove = false; michael@0: michael@0: if (mIsTopLevel) { michael@0: gtk_window_move(GTK_WINDOW(mShell), x, y); michael@0: } michael@0: else if (mGdkWindow) { michael@0: gdk_window_move(mGdkWindow, x, y); michael@0: } michael@0: michael@0: NotifyRollupGeometryChange(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindow::PlaceBehind(nsTopLevelWidgetZPlacement aPlacement, michael@0: nsIWidget *aWidget, michael@0: bool aActivate) michael@0: { michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: void michael@0: nsWindow::SetZIndex(int32_t aZIndex) michael@0: { michael@0: nsIWidget* oldPrev = GetPrevSibling(); michael@0: michael@0: nsBaseWidget::SetZIndex(aZIndex); michael@0: michael@0: if (GetPrevSibling() == oldPrev) { michael@0: return; michael@0: } michael@0: michael@0: NS_ASSERTION(!mContainer, "Expected Mozilla child widget"); michael@0: michael@0: // We skip the nsWindows that don't have mGdkWindows. michael@0: // These are probably in the process of being destroyed. michael@0: michael@0: if (!GetNextSibling()) { michael@0: // We're to be on top. michael@0: if (mGdkWindow) michael@0: gdk_window_raise(mGdkWindow); michael@0: } else { michael@0: // All the siblings before us need to be below our widget. michael@0: for (nsWindow* w = this; w; michael@0: w = static_cast(w->GetPrevSibling())) { michael@0: if (w->mGdkWindow) michael@0: gdk_window_lower(w->mGdkWindow); michael@0: } michael@0: } michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindow::SetSizeMode(int32_t aMode) michael@0: { michael@0: nsresult rv; michael@0: michael@0: LOG(("nsWindow::SetSizeMode [%p] %d\n", (void *)this, aMode)); michael@0: michael@0: // Save the requested state. michael@0: rv = nsBaseWidget::SetSizeMode(aMode); michael@0: michael@0: // return if there's no shell or our current state is the same as michael@0: // the mode we were just set to. michael@0: if (!mShell || mSizeState == mSizeMode) { michael@0: return rv; michael@0: } michael@0: michael@0: switch (aMode) { michael@0: case nsSizeMode_Maximized: michael@0: gtk_window_maximize(GTK_WINDOW(mShell)); michael@0: break; michael@0: case nsSizeMode_Minimized: michael@0: gtk_window_iconify(GTK_WINDOW(mShell)); michael@0: break; michael@0: case nsSizeMode_Fullscreen: michael@0: MakeFullScreen(true); michael@0: break; michael@0: michael@0: default: michael@0: // nsSizeMode_Normal, really. michael@0: if (mSizeState == nsSizeMode_Minimized) michael@0: gtk_window_deiconify(GTK_WINDOW(mShell)); michael@0: else if (mSizeState == nsSizeMode_Maximized) michael@0: gtk_window_unmaximize(GTK_WINDOW(mShell)); michael@0: break; michael@0: } michael@0: michael@0: mSizeState = mSizeMode; michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: typedef void (* SetUserTimeFunc)(GdkWindow* aWindow, guint32 aTimestamp); michael@0: michael@0: // This will become obsolete when new GTK APIs are widely supported, michael@0: // as described here: http://bugzilla.gnome.org/show_bug.cgi?id=347375 michael@0: static void michael@0: SetUserTimeAndStartupIDForActivatedWindow(GtkWidget* aWindow) michael@0: { michael@0: nsGTKToolkit* GTKToolkit = nsGTKToolkit::GetToolkit(); michael@0: if (!GTKToolkit) michael@0: return; michael@0: michael@0: nsAutoCString desktopStartupID; michael@0: GTKToolkit->GetDesktopStartupID(&desktopStartupID); michael@0: if (desktopStartupID.IsEmpty()) { michael@0: // We don't have the data we need. Fall back to an michael@0: // approximation ... using the timestamp of the remote command michael@0: // being received as a guess for the timestamp of the user event michael@0: // that triggered it. michael@0: uint32_t timestamp = GTKToolkit->GetFocusTimestamp(); michael@0: if (timestamp) { michael@0: gdk_window_focus(gtk_widget_get_window(aWindow), timestamp); michael@0: GTKToolkit->SetFocusTimestamp(0); michael@0: } michael@0: return; michael@0: } michael@0: michael@0: #if defined(MOZ_ENABLE_STARTUP_NOTIFICATION) michael@0: GdkWindow* gdkWindow = gtk_widget_get_window(aWindow); michael@0: michael@0: GdkScreen* screen = gdk_window_get_screen(gdkWindow); michael@0: SnDisplay* snd = michael@0: sn_display_new(gdk_x11_display_get_xdisplay(gdk_window_get_display(gdkWindow)), michael@0: nullptr, nullptr); michael@0: if (!snd) michael@0: return; michael@0: SnLauncheeContext* ctx = michael@0: sn_launchee_context_new(snd, gdk_screen_get_number(screen), michael@0: desktopStartupID.get()); michael@0: if (!ctx) { michael@0: sn_display_unref(snd); michael@0: return; michael@0: } michael@0: michael@0: if (sn_launchee_context_get_id_has_timestamp(ctx)) { michael@0: PRLibrary* gtkLibrary; michael@0: SetUserTimeFunc setUserTimeFunc = (SetUserTimeFunc) michael@0: PR_FindFunctionSymbolAndLibrary("gdk_x11_window_set_user_time", >kLibrary); michael@0: if (setUserTimeFunc) { michael@0: setUserTimeFunc(gdkWindow, sn_launchee_context_get_timestamp(ctx)); michael@0: PR_UnloadLibrary(gtkLibrary); michael@0: } michael@0: } michael@0: michael@0: sn_launchee_context_setup_window(ctx, gdk_x11_window_get_xid(gdkWindow)); michael@0: sn_launchee_context_complete(ctx); michael@0: michael@0: sn_launchee_context_unref(ctx); michael@0: sn_display_unref(snd); michael@0: #endif michael@0: michael@0: // If we used the startup ID, that already contains the focus timestamp; michael@0: // we don't want to reuse the timestamp next time we raise the window michael@0: GTKToolkit->SetFocusTimestamp(0); michael@0: GTKToolkit->SetDesktopStartupID(EmptyCString()); michael@0: } michael@0: michael@0: /* static */ guint32 michael@0: nsWindow::GetLastUserInputTime() michael@0: { michael@0: // gdk_x11_display_get_user_time tracks button and key presses, michael@0: // DESKTOP_STARTUP_ID used to start the app, drop events from external michael@0: // drags, WM_DELETE_WINDOW delete events, but not usually mouse motion nor michael@0: // button and key releases. Therefore use the most recent of michael@0: // gdk_x11_display_get_user_time and the last time that we have seen. michael@0: guint32 timestamp = michael@0: gdk_x11_display_get_user_time(gdk_display_get_default()); michael@0: if (sLastUserInputTime != GDK_CURRENT_TIME && michael@0: TimestampIsNewerThan(sLastUserInputTime, timestamp)) { michael@0: return sLastUserInputTime; michael@0: } michael@0: michael@0: return timestamp; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindow::SetFocus(bool aRaise) michael@0: { michael@0: // Make sure that our owning widget has focus. If it doesn't try to michael@0: // grab it. Note that we don't set our focus flag in this case. michael@0: michael@0: LOGFOCUS((" SetFocus %d [%p]\n", aRaise, (void *)this)); michael@0: michael@0: GtkWidget *owningWidget = GetMozContainerWidget(); michael@0: if (!owningWidget) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: // Raise the window if someone passed in true and the prefs are michael@0: // set properly. michael@0: GtkWidget *toplevelWidget = gtk_widget_get_toplevel(owningWidget); michael@0: michael@0: if (gRaiseWindows && aRaise && toplevelWidget && michael@0: !gtk_widget_has_focus(owningWidget) && michael@0: !gtk_widget_has_focus(toplevelWidget)) { michael@0: GtkWidget* top_window = GetToplevelWidget(); michael@0: if (top_window && (gtk_widget_get_visible(top_window))) michael@0: { michael@0: gdk_window_show_unraised(gtk_widget_get_window(top_window)); michael@0: // Unset the urgency hint if possible. michael@0: SetUrgencyHint(top_window, false); michael@0: } michael@0: } michael@0: michael@0: nsRefPtr owningWindow = get_window_for_gtk_widget(owningWidget); michael@0: if (!owningWindow) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: if (aRaise) { michael@0: // aRaise == true means request toplevel activation. michael@0: michael@0: // This is asynchronous. michael@0: // If and when the window manager accepts the request, then the focus michael@0: // widget will get a focus-in-event signal. michael@0: if (gRaiseWindows && owningWindow->mIsShown && owningWindow->mShell && michael@0: !gtk_window_is_active(GTK_WINDOW(owningWindow->mShell))) { michael@0: michael@0: uint32_t timestamp = GDK_CURRENT_TIME; michael@0: michael@0: nsGTKToolkit* GTKToolkit = nsGTKToolkit::GetToolkit(); michael@0: if (GTKToolkit) michael@0: timestamp = GTKToolkit->GetFocusTimestamp(); michael@0: michael@0: LOGFOCUS((" requesting toplevel activation [%p]\n", (void *)this)); michael@0: NS_ASSERTION(owningWindow->mWindowType != eWindowType_popup michael@0: || mParent, michael@0: "Presenting an override-redirect window"); michael@0: gtk_window_present_with_time(GTK_WINDOW(owningWindow->mShell), timestamp); michael@0: michael@0: if (GTKToolkit) michael@0: GTKToolkit->SetFocusTimestamp(0); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // aRaise == false means that keyboard events should be dispatched michael@0: // from this widget. michael@0: michael@0: // Ensure owningWidget is the focused GtkWidget within its toplevel window. michael@0: // michael@0: // For eWindowType_popup, this GtkWidget may not actually be the one that michael@0: // receives the key events as it may be the parent window that is active. michael@0: if (!gtk_widget_is_focus(owningWidget)) { michael@0: // This is synchronous. It takes focus from a plugin or from a widget michael@0: // in an embedder. The focus manager already knows that this window michael@0: // is active so gBlockActivateEvent avoids another (unnecessary) michael@0: // activate notification. michael@0: gBlockActivateEvent = true; michael@0: gtk_widget_grab_focus(owningWidget); michael@0: gBlockActivateEvent = false; michael@0: } michael@0: michael@0: // If this is the widget that already has focus, return. michael@0: if (gFocusWindow == this) { michael@0: LOGFOCUS((" already have focus [%p]\n", (void *)this)); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Set this window to be the focused child window michael@0: gFocusWindow = this; michael@0: michael@0: if (mIMModule) { michael@0: mIMModule->OnFocusWindow(this); michael@0: } michael@0: michael@0: LOGFOCUS((" widget now has focus in SetFocus() [%p]\n", michael@0: (void *)this)); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindow::GetScreenBounds(nsIntRect &aRect) michael@0: { michael@0: if (mIsTopLevel && mContainer) { michael@0: // use the point including window decorations michael@0: gint x, y; michael@0: gdk_window_get_root_origin(gtk_widget_get_window(GTK_WIDGET(mContainer)), &x, &y); michael@0: aRect.MoveTo(x, y); michael@0: } michael@0: else { michael@0: aRect.MoveTo(WidgetToScreenOffset()); michael@0: } michael@0: // mBounds.Size() is the window bounds, not the window-manager frame michael@0: // bounds (bug 581863). gdk_window_get_frame_extents would give the michael@0: // frame bounds, but mBounds.Size() is returned here for consistency michael@0: // with Resize. michael@0: aRect.SizeTo(mBounds.Size()); michael@0: LOG(("GetScreenBounds %d,%d | %dx%d\n", michael@0: aRect.x, aRect.y, aRect.width, aRect.height)); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindow::GetClientBounds(nsIntRect &aRect) michael@0: { michael@0: // GetBounds returns a rect whose top left represents the top left of the michael@0: // outer bounds, but whose width/height represent the size of the inner michael@0: // bounds (which is messed up). michael@0: GetBounds(aRect); michael@0: aRect.MoveBy(GetClientOffset()); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsIntPoint michael@0: nsWindow::GetClientOffset() michael@0: { michael@0: if (!mIsTopLevel) { michael@0: return nsIntPoint(0, 0); michael@0: } michael@0: michael@0: GdkAtom cardinal_atom = gdk_x11_xatom_to_atom(XA_CARDINAL); michael@0: michael@0: GdkAtom type_returned; michael@0: int format_returned; michael@0: int length_returned; michael@0: long *frame_extents; michael@0: GdkWindow* window; michael@0: michael@0: if (!mShell || !(window = gtk_widget_get_window(mShell)) || michael@0: !gdk_property_get(window, michael@0: gdk_atom_intern ("_NET_FRAME_EXTENTS", FALSE), michael@0: cardinal_atom, michael@0: 0, // offset michael@0: 4*4, // length michael@0: FALSE, // delete michael@0: &type_returned, michael@0: &format_returned, michael@0: &length_returned, michael@0: (guchar **) &frame_extents) || michael@0: length_returned/sizeof(glong) != 4) { michael@0: michael@0: return nsIntPoint(0, 0); michael@0: } michael@0: michael@0: // data returned is in the order left, right, top, bottom michael@0: int32_t left = int32_t(frame_extents[0]); michael@0: int32_t top = int32_t(frame_extents[2]); michael@0: michael@0: g_free(frame_extents); michael@0: michael@0: return nsIntPoint(left, top); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindow::SetCursor(nsCursor aCursor) michael@0: { michael@0: // if we're not the toplevel window pass up the cursor request to michael@0: // the toplevel window to handle it. michael@0: if (!mContainer && mGdkWindow) { michael@0: nsWindow *window = GetContainerWindow(); michael@0: if (!window) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: return window->SetCursor(aCursor); michael@0: } michael@0: michael@0: // Only change cursor if it's actually been changed michael@0: if (aCursor != mCursor) { michael@0: GdkCursor *newCursor = nullptr; michael@0: michael@0: newCursor = get_gtk_cursor(aCursor); michael@0: michael@0: if (nullptr != newCursor) { michael@0: mCursor = aCursor; michael@0: michael@0: if (!mContainer) michael@0: return NS_OK; michael@0: michael@0: gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(mContainer)), newCursor); michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindow::SetCursor(imgIContainer* aCursor, michael@0: uint32_t aHotspotX, uint32_t aHotspotY) michael@0: { michael@0: // if we're not the toplevel window pass up the cursor request to michael@0: // the toplevel window to handle it. michael@0: if (!mContainer && mGdkWindow) { michael@0: nsWindow *window = GetContainerWindow(); michael@0: if (!window) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: return window->SetCursor(aCursor, aHotspotX, aHotspotY); michael@0: } michael@0: michael@0: mCursor = nsCursor(-1); michael@0: michael@0: // Get the image's current frame michael@0: GdkPixbuf* pixbuf = nsImageToPixbuf::ImageToPixbuf(aCursor); michael@0: if (!pixbuf) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: int width = gdk_pixbuf_get_width(pixbuf); michael@0: int height = gdk_pixbuf_get_height(pixbuf); michael@0: // Reject cursors greater than 128 pixels in some direction, to prevent michael@0: // spoofing. michael@0: // XXX ideally we should rescale. Also, we could modify the API to michael@0: // allow trusted content to set larger cursors. michael@0: if (width > 128 || height > 128) { michael@0: g_object_unref(pixbuf); michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: // Looks like all cursors need an alpha channel (tested on Gtk 2.4.4). This michael@0: // is of course not documented anywhere... michael@0: // So add one if there isn't one yet michael@0: if (!gdk_pixbuf_get_has_alpha(pixbuf)) { michael@0: GdkPixbuf* alphaBuf = gdk_pixbuf_add_alpha(pixbuf, FALSE, 0, 0, 0); michael@0: g_object_unref(pixbuf); michael@0: if (!alphaBuf) { michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: pixbuf = alphaBuf; michael@0: } michael@0: michael@0: GdkCursor* cursor = gdk_cursor_new_from_pixbuf(gdk_display_get_default(), michael@0: pixbuf, michael@0: aHotspotX, aHotspotY); michael@0: g_object_unref(pixbuf); michael@0: nsresult rv = NS_ERROR_OUT_OF_MEMORY; michael@0: if (cursor) { michael@0: if (mContainer) { michael@0: gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(mContainer)), cursor); michael@0: rv = NS_OK; michael@0: } michael@0: gdk_cursor_unref(cursor); michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindow::Invalidate(const nsIntRect &aRect) michael@0: { michael@0: if (!mGdkWindow) michael@0: return NS_OK; michael@0: michael@0: GdkRectangle rect; michael@0: rect.x = aRect.x; michael@0: rect.y = aRect.y; michael@0: rect.width = aRect.width; michael@0: rect.height = aRect.height; michael@0: michael@0: LOGDRAW(("Invalidate (rect) [%p]: %d %d %d %d\n", (void *)this, michael@0: rect.x, rect.y, rect.width, rect.height)); michael@0: michael@0: gdk_window_invalidate_rect(mGdkWindow, &rect, FALSE); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void* michael@0: nsWindow::GetNativeData(uint32_t aDataType) michael@0: { michael@0: switch (aDataType) { michael@0: case NS_NATIVE_WINDOW: michael@0: case NS_NATIVE_WIDGET: { michael@0: if (!mGdkWindow) michael@0: return nullptr; michael@0: michael@0: return mGdkWindow; michael@0: break; michael@0: } michael@0: michael@0: case NS_NATIVE_PLUGIN_PORT: michael@0: return SetupPluginPort(); michael@0: break; michael@0: michael@0: case NS_NATIVE_DISPLAY: michael@0: #ifdef MOZ_X11 michael@0: return GDK_DISPLAY_XDISPLAY(gdk_display_get_default()); michael@0: #else michael@0: return nullptr; michael@0: #endif /* MOZ_X11 */ michael@0: break; michael@0: michael@0: case NS_NATIVE_SHELLWIDGET: michael@0: return GetToplevelWidget(); michael@0: michael@0: case NS_NATIVE_SHAREABLE_WINDOW: michael@0: return (void *) GDK_WINDOW_XID(gdk_window_get_toplevel(mGdkWindow)); michael@0: michael@0: default: michael@0: NS_WARNING("nsWindow::GetNativeData called with bad value"); michael@0: return nullptr; michael@0: } michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindow::SetTitle(const nsAString& aTitle) michael@0: { michael@0: if (!mShell) michael@0: return NS_OK; michael@0: michael@0: // convert the string into utf8 and set the title. michael@0: #define UTF8_FOLLOWBYTE(ch) (((ch) & 0xC0) == 0x80) michael@0: NS_ConvertUTF16toUTF8 titleUTF8(aTitle); michael@0: if (titleUTF8.Length() > NS_WINDOW_TITLE_MAX_LENGTH) { michael@0: // Truncate overlong titles (bug 167315). Make sure we chop after a michael@0: // complete sequence by making sure the next char isn't a follow-byte. michael@0: uint32_t len = NS_WINDOW_TITLE_MAX_LENGTH; michael@0: while(UTF8_FOLLOWBYTE(titleUTF8[len])) michael@0: --len; michael@0: titleUTF8.Truncate(len); michael@0: } michael@0: gtk_window_set_title(GTK_WINDOW(mShell), (const char *)titleUTF8.get()); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindow::SetIcon(const nsAString& aIconSpec) michael@0: { michael@0: if (!mShell) michael@0: return NS_OK; michael@0: michael@0: nsAutoCString iconName; michael@0: michael@0: if (aIconSpec.EqualsLiteral("default")) { michael@0: nsXPIDLString brandName; michael@0: GetBrandName(brandName); michael@0: AppendUTF16toUTF8(brandName, iconName); michael@0: ToLowerCase(iconName); michael@0: } else { michael@0: AppendUTF16toUTF8(aIconSpec, iconName); michael@0: } michael@0: michael@0: nsCOMPtr iconFile; michael@0: nsAutoCString path; michael@0: michael@0: gint *iconSizes = michael@0: gtk_icon_theme_get_icon_sizes(gtk_icon_theme_get_default(), michael@0: iconName.get()); michael@0: bool foundIcon = (iconSizes[0] != 0); michael@0: g_free(iconSizes); michael@0: michael@0: if (!foundIcon) { michael@0: // Look for icons with the following suffixes appended to the base name michael@0: // The last two entries (for the old XPM format) will be ignored unless michael@0: // no icons are found using other suffixes. XPM icons are deprecated. michael@0: michael@0: const char extensions[6][7] = { ".png", "16.png", "32.png", "48.png", michael@0: ".xpm", "16.xpm" }; michael@0: michael@0: for (uint32_t i = 0; i < ArrayLength(extensions); i++) { michael@0: // Don't bother looking for XPM versions if we found a PNG. michael@0: if (i == ArrayLength(extensions) - 2 && foundIcon) michael@0: break; michael@0: michael@0: nsAutoString extension; michael@0: extension.AppendASCII(extensions[i]); michael@0: michael@0: ResolveIconName(aIconSpec, extension, getter_AddRefs(iconFile)); michael@0: if (iconFile) { michael@0: iconFile->GetNativePath(path); michael@0: GdkPixbuf *icon = gdk_pixbuf_new_from_file(path.get(), nullptr); michael@0: if (icon) { michael@0: gtk_icon_theme_add_builtin_icon(iconName.get(), michael@0: gdk_pixbuf_get_height(icon), michael@0: icon); michael@0: g_object_unref(icon); michael@0: foundIcon = true; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: // leave the default icon intact if no matching icons were found michael@0: if (foundIcon) { michael@0: gtk_window_set_icon_name(GTK_WINDOW(mShell), iconName.get()); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: nsIntPoint michael@0: nsWindow::WidgetToScreenOffset() michael@0: { michael@0: gint x = 0, y = 0; michael@0: michael@0: if (mGdkWindow) { michael@0: gdk_window_get_origin(mGdkWindow, &x, &y); michael@0: } michael@0: michael@0: return nsIntPoint(x, y); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindow::EnableDragDrop(bool aEnable) michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindow::CaptureMouse(bool aCapture) michael@0: { michael@0: LOG(("CaptureMouse %p\n", (void *)this)); michael@0: michael@0: if (!mGdkWindow) michael@0: return NS_OK; michael@0: michael@0: if (!mShell) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: if (aCapture) { michael@0: gtk_grab_add(mShell); michael@0: GrabPointer(GetLastUserInputTime()); michael@0: } michael@0: else { michael@0: ReleaseGrabs(); michael@0: gtk_grab_remove(mShell); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindow::CaptureRollupEvents(nsIRollupListener *aListener, michael@0: bool aDoCapture) michael@0: { michael@0: if (!mGdkWindow) michael@0: return NS_OK; michael@0: michael@0: if (!mShell) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: LOG(("CaptureRollupEvents %p %i\n", this, int(aDoCapture))); michael@0: michael@0: if (aDoCapture) { michael@0: gRollupListener = aListener; michael@0: // real grab is only done when there is no dragging michael@0: if (!nsWindow::DragInProgress()) { michael@0: // This widget grab ensures that a Gecko GtkWidget receives mouse michael@0: // events even when embedded in non-Gecko-owned GtkWidgets. michael@0: // The grab is placed on the toplevel GtkWindow instead of the michael@0: // MozContainer to avoid double dispatch of keyboard events michael@0: // (bug 707623). michael@0: gtk_grab_add(mShell); michael@0: GrabPointer(GetLastUserInputTime()); michael@0: } michael@0: } michael@0: else { michael@0: if (!nsWindow::DragInProgress()) { michael@0: ReleaseGrabs(); michael@0: } michael@0: // There may not have been a drag in process when aDoCapture was set, michael@0: // so make sure to remove any added grab. This is a no-op if the grab michael@0: // was not added to this widget. michael@0: gtk_grab_remove(mShell); michael@0: gRollupListener = nullptr; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindow::GetAttention(int32_t aCycleCount) michael@0: { michael@0: LOG(("nsWindow::GetAttention [%p]\n", (void *)this)); michael@0: michael@0: GtkWidget* top_window = GetToplevelWidget(); michael@0: GtkWidget* top_focused_window = michael@0: gFocusWindow ? gFocusWindow->GetToplevelWidget() : nullptr; michael@0: michael@0: // Don't get attention if the window is focused anyway. michael@0: if (top_window && (gtk_widget_get_visible(top_window)) && michael@0: top_window != top_focused_window) { michael@0: SetUrgencyHint(top_window, true); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: bool michael@0: nsWindow::HasPendingInputEvent() michael@0: { michael@0: // This sucks, but gtk/gdk has no way to answer the question we want while michael@0: // excluding paint events, and there's no X API that will let us peek michael@0: // without blocking or removing. To prevent event reordering, peek michael@0: // anything except expose events. Reordering expose and others should be michael@0: // ok, hopefully. michael@0: bool haveEvent; michael@0: #ifdef MOZ_X11 michael@0: XEvent ev; michael@0: Display *display = GDK_DISPLAY_XDISPLAY(gdk_display_get_default()); michael@0: haveEvent = michael@0: XCheckMaskEvent(display, michael@0: KeyPressMask | KeyReleaseMask | ButtonPressMask | michael@0: ButtonReleaseMask | EnterWindowMask | LeaveWindowMask | michael@0: PointerMotionMask | PointerMotionHintMask | michael@0: Button1MotionMask | Button2MotionMask | michael@0: Button3MotionMask | Button4MotionMask | michael@0: Button5MotionMask | ButtonMotionMask | KeymapStateMask | michael@0: VisibilityChangeMask | StructureNotifyMask | michael@0: ResizeRedirectMask | SubstructureNotifyMask | michael@0: SubstructureRedirectMask | FocusChangeMask | michael@0: PropertyChangeMask | ColormapChangeMask | michael@0: OwnerGrabButtonMask, &ev); michael@0: if (haveEvent) { michael@0: XPutBackEvent(display, &ev); michael@0: } michael@0: #else michael@0: haveEvent = false; michael@0: #endif michael@0: return haveEvent; michael@0: } michael@0: michael@0: #if 0 michael@0: #ifdef DEBUG michael@0: // Paint flashing code (disabled for cairo - see below) michael@0: michael@0: #define CAPS_LOCK_IS_ON \ michael@0: (KeymapWrapper::AreModifiersCurrentlyActive(KeymapWrapper::CAPS_LOCK)) michael@0: michael@0: #define WANT_PAINT_FLASHING \ michael@0: (debug_WantPaintFlashing() && CAPS_LOCK_IS_ON) michael@0: michael@0: #ifdef MOZ_X11 michael@0: static void michael@0: gdk_window_flash(GdkWindow * aGdkWindow, michael@0: unsigned int aTimes, michael@0: unsigned int aInterval, // Milliseconds michael@0: GdkRegion * aRegion) michael@0: { michael@0: gint x; michael@0: gint y; michael@0: gint width; michael@0: gint height; michael@0: guint i; michael@0: GdkGC * gc = 0; michael@0: GdkColor white; michael@0: michael@0: #if (MOZ_WIDGET_GTK == 2) michael@0: gdk_window_get_geometry(aGdkWindow,nullptr,nullptr,&width,&height,nullptr); michael@0: #else michael@0: gdk_window_get_geometry(aGdkWindow,nullptr,nullptr,&width,&height); michael@0: #endif michael@0: michael@0: gdk_window_get_origin (aGdkWindow, michael@0: &x, michael@0: &y); michael@0: michael@0: gc = gdk_gc_new(gdk_get_default_root_window()); michael@0: michael@0: white.pixel = WhitePixel(gdk_display,DefaultScreen(gdk_display)); michael@0: michael@0: gdk_gc_set_foreground(gc,&white); michael@0: gdk_gc_set_function(gc,GDK_XOR); michael@0: gdk_gc_set_subwindow(gc,GDK_INCLUDE_INFERIORS); michael@0: michael@0: gdk_region_offset(aRegion, x, y); michael@0: gdk_gc_set_clip_region(gc, aRegion); michael@0: michael@0: /* michael@0: * Need to do this twice so that the XOR effect can replace michael@0: * the original window contents. michael@0: */ michael@0: for (i = 0; i < aTimes * 2; i++) michael@0: { michael@0: gdk_draw_rectangle(gdk_get_default_root_window(), michael@0: gc, michael@0: TRUE, michael@0: x, michael@0: y, michael@0: width, michael@0: height); michael@0: michael@0: gdk_flush(); michael@0: michael@0: PR_Sleep(PR_MillisecondsToInterval(aInterval)); michael@0: } michael@0: michael@0: gdk_gc_destroy(gc); michael@0: michael@0: gdk_region_offset(aRegion, -x, -y); michael@0: } michael@0: #endif /* MOZ_X11 */ michael@0: #endif // DEBUG michael@0: #endif michael@0: michael@0: struct ExposeRegion michael@0: { michael@0: nsIntRegion mRegion; michael@0: michael@0: #if (MOZ_WIDGET_GTK == 2) michael@0: GdkRectangle *mRects; michael@0: GdkRectangle *mRectsEnd; michael@0: michael@0: ExposeRegion() : mRects(nullptr) michael@0: { michael@0: } michael@0: ~ExposeRegion() michael@0: { michael@0: g_free(mRects); michael@0: } michael@0: bool Init(GdkEventExpose *aEvent) michael@0: { michael@0: gint nrects; michael@0: gdk_region_get_rectangles(aEvent->region, &mRects, &nrects); michael@0: michael@0: if (nrects > MAX_RECTS_IN_REGION) { michael@0: // Just use the bounding box michael@0: mRects[0] = aEvent->area; michael@0: nrects = 1; michael@0: } michael@0: michael@0: mRectsEnd = mRects + nrects; michael@0: michael@0: for (GdkRectangle *r = mRects; r < mRectsEnd; r++) { michael@0: mRegion.Or(mRegion, nsIntRect(r->x, r->y, r->width, r->height)); michael@0: LOGDRAW(("\t%d %d %d %d\n", r->x, r->y, r->width, r->height)); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: #else michael@0: # ifdef cairo_copy_clip_rectangle_list michael@0: # error "Looks like we're including Mozilla's cairo instead of system cairo" michael@0: # endif michael@0: cairo_rectangle_list_t *mRects; michael@0: michael@0: ExposeRegion() : mRects(nullptr) michael@0: { michael@0: } michael@0: ~ExposeRegion() michael@0: { michael@0: cairo_rectangle_list_destroy(mRects); michael@0: } michael@0: bool Init(cairo_t* cr) michael@0: { michael@0: mRects = cairo_copy_clip_rectangle_list(cr); michael@0: if (mRects->status != CAIRO_STATUS_SUCCESS) { michael@0: NS_WARNING("Failed to obtain cairo rectangle list."); michael@0: return false; michael@0: } michael@0: michael@0: for (int i = 0; i < mRects->num_rectangles; i++) { michael@0: const cairo_rectangle_t& r = mRects->rectangles[i]; michael@0: mRegion.Or(mRegion, nsIntRect(r.x, r.y, r.width, r.height)); michael@0: LOGDRAW(("\t%d %d %d %d\n", r.x, r.y, r.width, r.height)); michael@0: } michael@0: return true; michael@0: } michael@0: #endif michael@0: }; michael@0: michael@0: #if (MOZ_WIDGET_GTK == 2) michael@0: gboolean michael@0: nsWindow::OnExposeEvent(GdkEventExpose *aEvent) michael@0: #else michael@0: gboolean michael@0: nsWindow::OnExposeEvent(cairo_t *cr) michael@0: #endif michael@0: { michael@0: if (mIsDestroyed) { michael@0: return FALSE; michael@0: } michael@0: michael@0: // Windows that are not visible will be painted after they become visible. michael@0: if (!mGdkWindow || mIsFullyObscured || !mHasMappedToplevel) michael@0: return FALSE; michael@0: michael@0: nsIWidgetListener *listener = michael@0: mAttachedWidgetListener ? mAttachedWidgetListener : mWidgetListener; michael@0: if (!listener) michael@0: return FALSE; michael@0: michael@0: ExposeRegion exposeRegion; michael@0: #if (MOZ_WIDGET_GTK == 2) michael@0: if (!exposeRegion.Init(aEvent)) { michael@0: #else michael@0: if (!exposeRegion.Init(cr)) { michael@0: #endif michael@0: return FALSE; michael@0: } michael@0: michael@0: nsIntRegion ®ion = exposeRegion.mRegion; michael@0: michael@0: ClientLayerManager *clientLayers = michael@0: (GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_CLIENT) michael@0: ? static_cast(GetLayerManager()) michael@0: : nullptr; michael@0: michael@0: if (clientLayers && mCompositorParent) { michael@0: // We need to paint to the screen even if nothing changed, since if we michael@0: // don't have a compositing window manager, our pixels could be stale. michael@0: clientLayers->SetNeedsComposite(true); michael@0: clientLayers->SendInvalidRegion(region); michael@0: } michael@0: michael@0: // Dispatch WillPaintWindow notification to allow scripts etc. to run michael@0: // before we paint michael@0: { michael@0: listener->WillPaintWindow(this); michael@0: michael@0: // If the window has been destroyed during the will paint notification, michael@0: // there is nothing left to do. michael@0: if (!mGdkWindow) michael@0: return TRUE; michael@0: michael@0: // Re-get the listener since the will paint notification might have michael@0: // killed it. michael@0: listener = michael@0: mAttachedWidgetListener ? mAttachedWidgetListener : mWidgetListener; michael@0: if (!listener) michael@0: return FALSE; michael@0: } michael@0: michael@0: if (clientLayers && mCompositorParent && clientLayers->NeedsComposite()) { michael@0: mCompositorParent->ScheduleRenderOnCompositorThread(); michael@0: clientLayers->SetNeedsComposite(false); michael@0: } michael@0: michael@0: LOGDRAW(("sending expose event [%p] %p 0x%lx (rects follow):\n", michael@0: (void *)this, (void *)mGdkWindow, michael@0: gdk_x11_window_get_xid(mGdkWindow))); michael@0: michael@0: // Our bounds may have changed after calling WillPaintWindow. Clip michael@0: // to the new bounds here. The region is relative to this michael@0: // window. michael@0: region.And(region, nsIntRect(0, 0, mBounds.width, mBounds.height)); michael@0: michael@0: bool shaped = false; michael@0: if (eTransparencyTransparent == GetTransparencyMode()) { michael@0: GdkScreen *screen = gdk_window_get_screen(mGdkWindow); michael@0: if (gdk_screen_is_composited(screen) && michael@0: gdk_window_get_visual(mGdkWindow) == michael@0: gdk_screen_get_rgba_visual(screen)) { michael@0: // Remove possible shape mask from when window manger was not michael@0: // previously compositing. michael@0: static_cast(GetTopLevelWidget())-> michael@0: ClearTransparencyBitmap(); michael@0: } else { michael@0: shaped = true; michael@0: } michael@0: } michael@0: michael@0: if (!shaped) { michael@0: GList *children = michael@0: gdk_window_peek_children(mGdkWindow); michael@0: while (children) { michael@0: GdkWindow *gdkWin = GDK_WINDOW(children->data); michael@0: nsWindow *kid = get_window_for_gdk_window(gdkWin); michael@0: if (kid && gdk_window_is_visible(gdkWin)) { michael@0: nsAutoTArray clipRects; michael@0: kid->GetWindowClipRegion(&clipRects); michael@0: nsIntRect bounds; michael@0: kid->GetBounds(bounds); michael@0: for (uint32_t i = 0; i < clipRects.Length(); ++i) { michael@0: nsIntRect r = clipRects[i] + bounds.TopLeft(); michael@0: region.Sub(region, r); michael@0: } michael@0: } michael@0: children = children->next; michael@0: } michael@0: } michael@0: michael@0: if (region.IsEmpty()) { michael@0: return TRUE; michael@0: } michael@0: michael@0: // If this widget uses OMTC... michael@0: if (GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_CLIENT) { michael@0: listener->PaintWindow(this, region); michael@0: listener->DidPaintWindow(); michael@0: return TRUE; michael@0: } michael@0: michael@0: gfxASurface* surf; michael@0: #if (MOZ_WIDGET_GTK == 2) michael@0: surf = GetThebesSurface(); michael@0: #else michael@0: surf = GetThebesSurface(cr); michael@0: #endif michael@0: michael@0: nsRefPtr ctx; michael@0: if (gfxPlatform::GetPlatform()-> michael@0: SupportsAzureContentForType(BackendType::CAIRO)) { michael@0: IntSize intSize(surf->GetSize().width, surf->GetSize().height); michael@0: ctx = new gfxContext(gfxPlatform::GetPlatform()-> michael@0: CreateDrawTargetForSurface(surf, intSize)); michael@0: } else if (gfxPlatform::GetPlatform()-> michael@0: SupportsAzureContentForType(BackendType::SKIA) && michael@0: surf->GetType() == gfxSurfaceType::Image) { michael@0: gfxImageSurface* imgSurf = static_cast(surf); michael@0: SurfaceFormat format = ImageFormatToSurfaceFormat(imgSurf->Format()); michael@0: IntSize intSize(surf->GetSize().width, surf->GetSize().height); michael@0: ctx = new gfxContext(gfxPlatform::GetPlatform()->CreateDrawTargetForData( michael@0: imgSurf->Data(), intSize, imgSurf->Stride(), format)); michael@0: } else { michael@0: ctx = new gfxContext(surf); michael@0: } michael@0: michael@0: #ifdef MOZ_X11 michael@0: nsIntRect boundsRect; // for shaped only michael@0: michael@0: ctx->NewPath(); michael@0: if (shaped) { michael@0: // Collapse update area to the bounding box. This is so we only have to michael@0: // call UpdateTranslucentWindowAlpha once. After we have dropped michael@0: // support for non-Thebes graphics, UpdateTranslucentWindowAlpha will be michael@0: // our private interface so we can rework things to avoid this. michael@0: boundsRect = region.GetBounds(); michael@0: ctx->Rectangle(gfxRect(boundsRect.x, boundsRect.y, michael@0: boundsRect.width, boundsRect.height)); michael@0: } else { michael@0: gfxUtils::PathFromRegion(ctx, region); michael@0: } michael@0: ctx->Clip(); michael@0: michael@0: BufferMode layerBuffering; michael@0: if (shaped) { michael@0: // The double buffering is done here to extract the shape mask. michael@0: // (The shape mask won't be necessary when a visual with an alpha michael@0: // channel is used on compositing window managers.) michael@0: layerBuffering = mozilla::layers::BufferMode::BUFFER_NONE; michael@0: ctx->PushGroup(gfxContentType::COLOR_ALPHA); michael@0: #ifdef MOZ_HAVE_SHMIMAGE michael@0: } else if (nsShmImage::UseShm()) { michael@0: // We're using an xshm mapping as a back buffer. michael@0: layerBuffering = mozilla::layers::BufferMode::BUFFER_NONE; michael@0: #endif // MOZ_HAVE_SHMIMAGE michael@0: } else { michael@0: // Get the layer manager to do double buffering (if necessary). michael@0: layerBuffering = mozilla::layers::BufferMode::BUFFERED; michael@0: } michael@0: michael@0: #if 0 michael@0: // NOTE: Paint flashing region would be wrong for cairo, since michael@0: // cairo inflates the update region, etc. So don't paint flash michael@0: // for cairo. michael@0: #ifdef DEBUG michael@0: // XXX aEvent->region may refer to a newly-invalid area. FIXME michael@0: if (0 && WANT_PAINT_FLASHING && gtk_widget_get_window(aEvent)) michael@0: gdk_window_flash(mGdkWindow, 1, 100, aEvent->region); michael@0: #endif michael@0: #endif michael@0: michael@0: #endif // MOZ_X11 michael@0: michael@0: bool painted = false; michael@0: { michael@0: if (GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_BASIC) { michael@0: AutoLayerManagerSetup setupLayerManager(this, ctx, layerBuffering); michael@0: painted = listener->PaintWindow(this, region); michael@0: } michael@0: } michael@0: michael@0: #ifdef MOZ_X11 michael@0: // PaintWindow can Destroy us (bug 378273), avoid doing any paint michael@0: // operations below if that happened - it will lead to XError and exit(). michael@0: if (shaped) { michael@0: if (MOZ_LIKELY(!mIsDestroyed)) { michael@0: if (painted) { michael@0: nsRefPtr pattern = ctx->PopGroup(); michael@0: michael@0: UpdateAlpha(pattern, boundsRect); michael@0: michael@0: ctx->SetOperator(gfxContext::OPERATOR_SOURCE); michael@0: ctx->SetPattern(pattern); michael@0: ctx->Paint(); michael@0: } michael@0: } michael@0: } michael@0: # ifdef MOZ_HAVE_SHMIMAGE michael@0: if (mShmImage && MOZ_LIKELY(!mIsDestroyed)) { michael@0: #if (MOZ_WIDGET_GTK == 2) michael@0: mShmImage->Put(mGdkWindow, exposeRegion.mRects, exposeRegion.mRectsEnd); michael@0: #else michael@0: mShmImage->Put(mGdkWindow, exposeRegion.mRects); michael@0: #endif michael@0: } michael@0: # endif // MOZ_HAVE_SHMIMAGE michael@0: #endif // MOZ_X11 michael@0: michael@0: listener->DidPaintWindow(); michael@0: michael@0: // Synchronously flush any new dirty areas michael@0: #if (MOZ_WIDGET_GTK == 2) michael@0: GdkRegion* dirtyArea = gdk_window_get_update_area(mGdkWindow); michael@0: #else michael@0: cairo_region_t* dirtyArea = gdk_window_get_update_area(mGdkWindow); michael@0: #endif michael@0: michael@0: if (dirtyArea) { michael@0: gdk_window_invalidate_region(mGdkWindow, dirtyArea, false); michael@0: #if (MOZ_WIDGET_GTK == 2) michael@0: gdk_region_destroy(dirtyArea); michael@0: #else michael@0: cairo_region_destroy(dirtyArea); michael@0: #endif michael@0: gdk_window_process_updates(mGdkWindow, false); michael@0: } michael@0: michael@0: // check the return value! michael@0: return TRUE; michael@0: } michael@0: michael@0: void michael@0: nsWindow::UpdateAlpha(gfxPattern* aPattern, nsIntRect aBoundsRect) michael@0: { michael@0: // We need to create our own buffer to force the stride to match the michael@0: // expected stride. michael@0: int32_t stride = GetAlignedStride<4>(BytesPerPixel(SurfaceFormat::A8) * michael@0: aBoundsRect.width); michael@0: int32_t bufferSize = stride * aBoundsRect.height; michael@0: nsAutoArrayPtr imageBuffer(new (std::nothrow) uint8_t[bufferSize]); michael@0: RefPtr drawTarget = gfxPlatform::GetPlatform()-> michael@0: CreateDrawTargetForData(imageBuffer, ToIntSize(aBoundsRect.Size()), michael@0: stride, SurfaceFormat::A8); michael@0: michael@0: if (drawTarget) { michael@0: drawTarget->FillRect(Rect(0, 0, aBoundsRect.width, aBoundsRect.height), michael@0: *aPattern->GetPattern(drawTarget), michael@0: DrawOptions(1.0, CompositionOp::OP_SOURCE)); michael@0: } michael@0: UpdateTranslucentWindowAlphaInternal(aBoundsRect, imageBuffer, stride); michael@0: } michael@0: michael@0: gboolean michael@0: nsWindow::OnConfigureEvent(GtkWidget *aWidget, GdkEventConfigure *aEvent) michael@0: { michael@0: // These events are only received on toplevel windows. michael@0: // michael@0: // GDK ensures that the coordinates are the client window top-left wrt the michael@0: // root window. michael@0: // michael@0: // GDK calculates the cordinates for real ConfigureNotify events on michael@0: // managed windows (that would normally be relative to the parent michael@0: // window). michael@0: // michael@0: // Synthetic ConfigureNotify events are from the window manager and michael@0: // already relative to the root window. GDK creates all X windows with michael@0: // border_width = 0, so synthetic events also indicate the top-left of michael@0: // the client window. michael@0: // michael@0: // Override-redirect windows are children of the root window so parent michael@0: // coordinates are root coordinates. michael@0: michael@0: LOG(("configure event [%p] %d %d %d %d\n", (void *)this, michael@0: aEvent->x, aEvent->y, aEvent->width, aEvent->height)); michael@0: michael@0: nsIntRect screenBounds; michael@0: GetScreenBounds(screenBounds); michael@0: michael@0: if (mWindowType == eWindowType_toplevel || mWindowType == eWindowType_dialog) { michael@0: // This check avoids unwanted rollup on spurious configure events from michael@0: // Cygwin/X (bug 672103). michael@0: if (mBounds.x != screenBounds.x || michael@0: mBounds.y != screenBounds.y) { michael@0: CheckForRollup(0, 0, false, true); michael@0: } michael@0: } michael@0: michael@0: // This event indicates that the window position may have changed. michael@0: // mBounds.Size() is updated in OnSizeAllocate(). michael@0: michael@0: // (The gtk_window_get_window_type() function is only available from michael@0: // version 2.20.) michael@0: NS_ASSERTION(GTK_IS_WINDOW(aWidget), michael@0: "Configure event on widget that is not a GtkWindow"); michael@0: gint type; michael@0: g_object_get(aWidget, "type", &type, nullptr); michael@0: if (type == GTK_WINDOW_POPUP) { michael@0: // Override-redirect window michael@0: // michael@0: // These windows should not be moved by the window manager, and so any michael@0: // change in position is a result of our direction. mBounds has michael@0: // already been set in Move() or Resize(), and that is more michael@0: // up-to-date than the position in the ConfigureNotify event if the michael@0: // event is from an earlier window move. michael@0: // michael@0: // Skipping the WindowMoved call saves context menus from an infinite michael@0: // loop when nsXULPopupManager::PopupMoved moves the window to the new michael@0: // position and nsMenuPopupFrame::SetPopupPosition adds michael@0: // offsetForContextMenu on each iteration. michael@0: return FALSE; michael@0: } michael@0: michael@0: mBounds.MoveTo(screenBounds.TopLeft()); michael@0: michael@0: // XXX mozilla will invalidate the entire window after this move michael@0: // complete. wtf? michael@0: NotifyWindowMoved(mBounds.x, mBounds.y); michael@0: michael@0: return FALSE; michael@0: } michael@0: michael@0: void michael@0: nsWindow::OnContainerUnrealize() michael@0: { michael@0: // The GdkWindows are about to be destroyed (but not deleted), so remove michael@0: // their references back to their container widget while the GdkWindow michael@0: // hierarchy is still available. michael@0: michael@0: if (mGdkWindow) { michael@0: DestroyChildWindows(); michael@0: michael@0: g_object_set_data(G_OBJECT(mGdkWindow), "nsWindow", nullptr); michael@0: mGdkWindow = nullptr; michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsWindow::OnSizeAllocate(GtkAllocation *aAllocation) michael@0: { michael@0: LOG(("size_allocate [%p] %d %d %d %d\n", michael@0: (void *)this, aAllocation->x, aAllocation->y, michael@0: aAllocation->width, aAllocation->height)); michael@0: michael@0: nsIntSize size(aAllocation->width, aAllocation->height); michael@0: if (mBounds.Size() == size) michael@0: return; michael@0: michael@0: // Invalidate the new part of the window now for the pending paint to michael@0: // minimize background flashes (GDK does not do this for external resizes michael@0: // of toplevels.) michael@0: if (mBounds.width < size.width) { michael@0: GdkRectangle rect = michael@0: { mBounds.width, 0, size.width - mBounds.width, size.height }; michael@0: gdk_window_invalidate_rect(mGdkWindow, &rect, FALSE); michael@0: } michael@0: if (mBounds.height < size.height) { michael@0: GdkRectangle rect = michael@0: { 0, mBounds.height, size.width, size.height - mBounds.height }; michael@0: gdk_window_invalidate_rect(mGdkWindow, &rect, FALSE); michael@0: } michael@0: michael@0: mBounds.SizeTo(size); michael@0: michael@0: if (!mGdkWindow) michael@0: return; michael@0: michael@0: DispatchResized(size.width, size.height); michael@0: } michael@0: michael@0: void michael@0: nsWindow::OnDeleteEvent() michael@0: { michael@0: if (mWidgetListener) michael@0: mWidgetListener->RequestWindowClose(this); michael@0: } michael@0: michael@0: void michael@0: nsWindow::OnEnterNotifyEvent(GdkEventCrossing *aEvent) michael@0: { michael@0: // This skips NotifyVirtual and NotifyNonlinearVirtual enter notify events michael@0: // when the pointer enters a child window. If the destination window is a michael@0: // Gecko window then we'll catch the corresponding event on that window, michael@0: // but we won't notice when the pointer directly enters a foreign (plugin) michael@0: // child window without passing over a visible portion of a Gecko window. michael@0: if (aEvent->subwindow != nullptr) michael@0: return; michael@0: michael@0: // Check before is_parent_ungrab_enter() as the button state may have michael@0: // changed while a non-Gecko ancestor window had a pointer grab. michael@0: DispatchMissedButtonReleases(aEvent); michael@0: michael@0: if (is_parent_ungrab_enter(aEvent)) michael@0: return; michael@0: michael@0: WidgetMouseEvent event(true, NS_MOUSE_ENTER, this, WidgetMouseEvent::eReal); michael@0: michael@0: event.refPoint.x = nscoord(aEvent->x); michael@0: event.refPoint.y = nscoord(aEvent->y); michael@0: michael@0: event.time = aEvent->time; michael@0: michael@0: LOG(("OnEnterNotify: %p\n", (void *)this)); michael@0: michael@0: nsEventStatus status; michael@0: DispatchEvent(&event, status); michael@0: } michael@0: michael@0: // XXX Is this the right test for embedding cases? michael@0: static bool michael@0: is_top_level_mouse_exit(GdkWindow* aWindow, GdkEventCrossing *aEvent) michael@0: { michael@0: gint x = gint(aEvent->x_root); michael@0: gint y = gint(aEvent->y_root); michael@0: GdkDisplay* display = gdk_window_get_display(aWindow); michael@0: GdkWindow* winAtPt = gdk_display_get_window_at_pointer(display, &x, &y); michael@0: if (!winAtPt) michael@0: return true; michael@0: GdkWindow* topLevelAtPt = gdk_window_get_toplevel(winAtPt); michael@0: GdkWindow* topLevelWidget = gdk_window_get_toplevel(aWindow); michael@0: return topLevelAtPt != topLevelWidget; michael@0: } michael@0: michael@0: void michael@0: nsWindow::OnLeaveNotifyEvent(GdkEventCrossing *aEvent) michael@0: { michael@0: // This ignores NotifyVirtual and NotifyNonlinearVirtual leave notify michael@0: // events when the pointer leaves a child window. If the destination michael@0: // window is a Gecko window then we'll catch the corresponding event on michael@0: // that window. michael@0: // michael@0: // XXXkt However, we will miss toplevel exits when the pointer directly michael@0: // leaves a foreign (plugin) child window without passing over a visible michael@0: // portion of a Gecko window. michael@0: if (aEvent->subwindow != nullptr) michael@0: return; michael@0: michael@0: WidgetMouseEvent event(true, NS_MOUSE_EXIT, this, WidgetMouseEvent::eReal); michael@0: michael@0: event.refPoint.x = nscoord(aEvent->x); michael@0: event.refPoint.y = nscoord(aEvent->y); michael@0: michael@0: event.time = aEvent->time; michael@0: michael@0: event.exit = is_top_level_mouse_exit(mGdkWindow, aEvent) michael@0: ? WidgetMouseEvent::eTopLevel : WidgetMouseEvent::eChild; michael@0: michael@0: LOG(("OnLeaveNotify: %p\n", (void *)this)); michael@0: michael@0: nsEventStatus status; michael@0: DispatchEvent(&event, status); michael@0: } michael@0: michael@0: void michael@0: nsWindow::OnMotionNotifyEvent(GdkEventMotion *aEvent) michael@0: { michael@0: // see if we can compress this event michael@0: // XXXldb Why skip every other motion event when we have multiple, michael@0: // but not more than that? michael@0: bool synthEvent = false; michael@0: #ifdef MOZ_X11 michael@0: XEvent xevent; michael@0: michael@0: while (XPending (GDK_WINDOW_XDISPLAY(aEvent->window))) { michael@0: XEvent peeked; michael@0: XPeekEvent (GDK_WINDOW_XDISPLAY(aEvent->window), &peeked); michael@0: if (peeked.xany.window != gdk_x11_window_get_xid(aEvent->window) michael@0: || peeked.type != MotionNotify) michael@0: break; michael@0: michael@0: synthEvent = true; michael@0: XNextEvent (GDK_WINDOW_XDISPLAY(aEvent->window), &xevent); michael@0: } michael@0: #if (MOZ_WIDGET_GTK == 2) michael@0: // if plugins still keeps the focus, get it back michael@0: if (gPluginFocusWindow && gPluginFocusWindow != this) { michael@0: nsRefPtr kungFuDeathGrip = gPluginFocusWindow; michael@0: gPluginFocusWindow->LoseNonXEmbedPluginFocus(); michael@0: } michael@0: #endif /* MOZ_WIDGET_GTK2 */ michael@0: #endif /* MOZ_X11 */ michael@0: michael@0: WidgetMouseEvent event(true, NS_MOUSE_MOVE, this, WidgetMouseEvent::eReal); michael@0: michael@0: gdouble pressure = 0; michael@0: gdk_event_get_axis ((GdkEvent*)aEvent, GDK_AXIS_PRESSURE, &pressure); michael@0: // Sometime gdk generate 0 pressure value between normal values michael@0: // We have to ignore that and use last valid value michael@0: if (pressure) michael@0: mLastMotionPressure = pressure; michael@0: event.pressure = mLastMotionPressure; michael@0: michael@0: guint modifierState; michael@0: if (synthEvent) { michael@0: #ifdef MOZ_X11 michael@0: event.refPoint.x = nscoord(xevent.xmotion.x); michael@0: event.refPoint.y = nscoord(xevent.xmotion.y); michael@0: michael@0: modifierState = xevent.xmotion.state; michael@0: michael@0: event.time = xevent.xmotion.time; michael@0: #else michael@0: event.refPoint.x = nscoord(aEvent->x); michael@0: event.refPoint.y = nscoord(aEvent->y); michael@0: michael@0: modifierState = aEvent->state; michael@0: michael@0: event.time = aEvent->time; michael@0: #endif /* MOZ_X11 */ michael@0: } michael@0: else { michael@0: // XXX see OnScrollEvent() michael@0: if (aEvent->window == mGdkWindow) { michael@0: event.refPoint.x = nscoord(aEvent->x); michael@0: event.refPoint.y = nscoord(aEvent->y); michael@0: } else { michael@0: LayoutDeviceIntPoint point(NSToIntFloor(aEvent->x_root), michael@0: NSToIntFloor(aEvent->y_root)); michael@0: event.refPoint = point - michael@0: LayoutDeviceIntPoint::FromUntyped(WidgetToScreenOffset()); michael@0: } michael@0: michael@0: modifierState = aEvent->state; michael@0: michael@0: event.time = aEvent->time; michael@0: } michael@0: michael@0: KeymapWrapper::InitInputEvent(event, modifierState); michael@0: michael@0: nsEventStatus status; michael@0: DispatchEvent(&event, status); michael@0: } michael@0: michael@0: // If the automatic pointer grab on ButtonPress has deactivated before michael@0: // ButtonRelease, and the mouse button is released while the pointer is not michael@0: // over any a Gecko window, then the ButtonRelease event will not be received. michael@0: // (A similar situation exists when the pointer is grabbed with owner_events michael@0: // True as the ButtonRelease may be received on a foreign [plugin] window). michael@0: // Use this method to check for released buttons when the pointer returns to a michael@0: // Gecko window. michael@0: void michael@0: nsWindow::DispatchMissedButtonReleases(GdkEventCrossing *aGdkEvent) michael@0: { michael@0: guint changed = aGdkEvent->state ^ gButtonState; michael@0: // Only consider button releases. michael@0: // (Ignore button presses that occurred outside Gecko.) michael@0: guint released = changed & gButtonState; michael@0: gButtonState = aGdkEvent->state; michael@0: michael@0: // Loop over each button, excluding mouse wheel buttons 4 and 5 for which michael@0: // GDK ignores releases. michael@0: for (guint buttonMask = GDK_BUTTON1_MASK; michael@0: buttonMask <= GDK_BUTTON3_MASK; michael@0: buttonMask <<= 1) { michael@0: michael@0: if (released & buttonMask) { michael@0: int16_t buttonType; michael@0: switch (buttonMask) { michael@0: case GDK_BUTTON1_MASK: michael@0: buttonType = WidgetMouseEvent::eLeftButton; michael@0: break; michael@0: case GDK_BUTTON2_MASK: michael@0: buttonType = WidgetMouseEvent::eMiddleButton; michael@0: break; michael@0: default: michael@0: NS_ASSERTION(buttonMask == GDK_BUTTON3_MASK, michael@0: "Unexpected button mask"); michael@0: buttonType = WidgetMouseEvent::eRightButton; michael@0: } michael@0: michael@0: LOG(("Synthesized button %u release on %p\n", michael@0: guint(buttonType + 1), (void *)this)); michael@0: michael@0: // Dispatch a synthesized button up event to tell Gecko about the michael@0: // change in state. This event is marked as synthesized so that michael@0: // it is not dispatched as a DOM event, because we don't know the michael@0: // position, widget, modifiers, or time/order. michael@0: WidgetMouseEvent synthEvent(true, NS_MOUSE_BUTTON_UP, this, michael@0: WidgetMouseEvent::eSynthesized); michael@0: synthEvent.button = buttonType; michael@0: nsEventStatus status; michael@0: DispatchEvent(&synthEvent, status); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsWindow::InitButtonEvent(WidgetMouseEvent& aEvent, michael@0: GdkEventButton* aGdkEvent) michael@0: { michael@0: // XXX see OnScrollEvent() michael@0: if (aGdkEvent->window == mGdkWindow) { michael@0: aEvent.refPoint.x = nscoord(aGdkEvent->x); michael@0: aEvent.refPoint.y = nscoord(aGdkEvent->y); michael@0: } else { michael@0: LayoutDeviceIntPoint point(NSToIntFloor(aGdkEvent->x_root), michael@0: NSToIntFloor(aGdkEvent->y_root)); michael@0: aEvent.refPoint = point - michael@0: LayoutDeviceIntPoint::FromUntyped(WidgetToScreenOffset()); michael@0: } michael@0: michael@0: guint modifierState = aGdkEvent->state; michael@0: // aEvent's state doesn't include this event's information. Therefore, michael@0: // if aEvent is mouse button down event, we need to set it manually. michael@0: // Note that we cannot do same thing for NS_MOUSE_BUTTON_UP because michael@0: // system may have two or more mice and same button of another mouse michael@0: // may be still pressed. michael@0: if (aGdkEvent->type != GDK_BUTTON_RELEASE) { michael@0: switch (aGdkEvent->button) { michael@0: case 1: michael@0: modifierState |= GDK_BUTTON1_MASK; michael@0: break; michael@0: case 2: michael@0: modifierState |= GDK_BUTTON2_MASK; michael@0: break; michael@0: case 3: michael@0: modifierState |= GDK_BUTTON3_MASK; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: KeymapWrapper::InitInputEvent(aEvent, modifierState); michael@0: michael@0: aEvent.time = aGdkEvent->time; michael@0: michael@0: switch (aGdkEvent->type) { michael@0: case GDK_2BUTTON_PRESS: michael@0: aEvent.clickCount = 2; michael@0: break; michael@0: case GDK_3BUTTON_PRESS: michael@0: aEvent.clickCount = 3; michael@0: break; michael@0: // default is one click michael@0: default: michael@0: aEvent.clickCount = 1; michael@0: } michael@0: } michael@0: michael@0: static guint ButtonMaskFromGDKButton(guint button) michael@0: { michael@0: return GDK_BUTTON1_MASK << (button - 1); michael@0: } michael@0: michael@0: void michael@0: nsWindow::OnButtonPressEvent(GdkEventButton *aEvent) michael@0: { michael@0: LOG(("Button %u press on %p\n", aEvent->button, (void *)this)); michael@0: michael@0: nsEventStatus status; michael@0: michael@0: // If you double click in GDK, it will actually generate a second michael@0: // GDK_BUTTON_PRESS before sending the GDK_2BUTTON_PRESS, and this is michael@0: // different than the DOM spec. GDK puts this in the queue michael@0: // programatically, so it's safe to assume that if there's a michael@0: // double click in the queue, it was generated so we can just drop michael@0: // this click. michael@0: GdkEvent *peekedEvent = gdk_event_peek(); michael@0: if (peekedEvent) { michael@0: GdkEventType type = peekedEvent->any.type; michael@0: gdk_event_free(peekedEvent); michael@0: if (type == GDK_2BUTTON_PRESS || type == GDK_3BUTTON_PRESS) michael@0: return; michael@0: } michael@0: michael@0: nsWindow *containerWindow = GetContainerWindow(); michael@0: if (!gFocusWindow && containerWindow) { michael@0: containerWindow->DispatchActivateEvent(); michael@0: } michael@0: michael@0: // check to see if we should rollup michael@0: if (CheckForRollup(aEvent->x_root, aEvent->y_root, false, false)) michael@0: return; michael@0: michael@0: gdouble pressure = 0; michael@0: gdk_event_get_axis ((GdkEvent*)aEvent, GDK_AXIS_PRESSURE, &pressure); michael@0: mLastMotionPressure = pressure; michael@0: michael@0: uint16_t domButton; michael@0: switch (aEvent->button) { michael@0: case 1: michael@0: domButton = WidgetMouseEvent::eLeftButton; michael@0: break; michael@0: case 2: michael@0: domButton = WidgetMouseEvent::eMiddleButton; michael@0: break; michael@0: case 3: michael@0: domButton = WidgetMouseEvent::eRightButton; michael@0: break; michael@0: // These are mapped to horizontal scroll michael@0: case 6: michael@0: case 7: michael@0: NS_WARNING("We're not supporting legacy horizontal scroll event"); michael@0: return; michael@0: // Map buttons 8-9 to back/forward michael@0: case 8: michael@0: DispatchCommandEvent(nsGkAtoms::Back); michael@0: return; michael@0: case 9: michael@0: DispatchCommandEvent(nsGkAtoms::Forward); michael@0: return; michael@0: default: michael@0: return; michael@0: } michael@0: michael@0: gButtonState |= ButtonMaskFromGDKButton(aEvent->button); michael@0: michael@0: WidgetMouseEvent event(true, NS_MOUSE_BUTTON_DOWN, this, michael@0: WidgetMouseEvent::eReal); michael@0: event.button = domButton; michael@0: InitButtonEvent(event, aEvent); michael@0: event.pressure = mLastMotionPressure; michael@0: michael@0: DispatchEvent(&event, status); michael@0: michael@0: // right menu click on linux should also pop up a context menu michael@0: if (domButton == WidgetMouseEvent::eRightButton && michael@0: MOZ_LIKELY(!mIsDestroyed)) { michael@0: WidgetMouseEvent contextMenuEvent(true, NS_CONTEXTMENU, this, michael@0: WidgetMouseEvent::eReal); michael@0: InitButtonEvent(contextMenuEvent, aEvent); michael@0: contextMenuEvent.pressure = mLastMotionPressure; michael@0: DispatchEvent(&contextMenuEvent, status); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsWindow::OnButtonReleaseEvent(GdkEventButton *aEvent) michael@0: { michael@0: LOG(("Button %u release on %p\n", aEvent->button, (void *)this)); michael@0: michael@0: uint16_t domButton; michael@0: switch (aEvent->button) { michael@0: case 1: michael@0: domButton = WidgetMouseEvent::eLeftButton; michael@0: break; michael@0: case 2: michael@0: domButton = WidgetMouseEvent::eMiddleButton; michael@0: break; michael@0: case 3: michael@0: domButton = WidgetMouseEvent::eRightButton; michael@0: break; michael@0: default: michael@0: return; michael@0: } michael@0: michael@0: gButtonState &= ~ButtonMaskFromGDKButton(aEvent->button); michael@0: michael@0: WidgetMouseEvent event(true, NS_MOUSE_BUTTON_UP, this, michael@0: WidgetMouseEvent::eReal); michael@0: event.button = domButton; michael@0: InitButtonEvent(event, aEvent); michael@0: gdouble pressure = 0; michael@0: gdk_event_get_axis ((GdkEvent*)aEvent, GDK_AXIS_PRESSURE, &pressure); michael@0: event.pressure = pressure ? pressure : mLastMotionPressure; michael@0: michael@0: nsEventStatus status; michael@0: DispatchEvent(&event, status); michael@0: mLastMotionPressure = pressure; michael@0: } michael@0: michael@0: void michael@0: nsWindow::OnContainerFocusInEvent(GdkEventFocus *aEvent) michael@0: { michael@0: LOGFOCUS(("OnContainerFocusInEvent [%p]\n", (void *)this)); michael@0: michael@0: // Unset the urgency hint, if possible michael@0: GtkWidget* top_window = GetToplevelWidget(); michael@0: if (top_window && (gtk_widget_get_visible(top_window))) michael@0: SetUrgencyHint(top_window, false); michael@0: michael@0: // Return if being called within SetFocus because the focus manager michael@0: // already knows that the window is active. michael@0: if (gBlockActivateEvent) { michael@0: LOGFOCUS(("activated notification is blocked [%p]\n", (void *)this)); michael@0: return; michael@0: } michael@0: michael@0: // If keyboard input will be accepted, the focus manager will call michael@0: // SetFocus to set the correct window. michael@0: gFocusWindow = nullptr; michael@0: michael@0: DispatchActivateEvent(); michael@0: michael@0: if (!gFocusWindow) { michael@0: // We don't really have a window for dispatching key events, but michael@0: // setting a non-nullptr value here prevents OnButtonPressEvent() from michael@0: // dispatching an activation notification if the widget is already michael@0: // active. michael@0: gFocusWindow = this; michael@0: } michael@0: michael@0: LOGFOCUS(("Events sent from focus in event [%p]\n", (void *)this)); michael@0: } michael@0: michael@0: void michael@0: nsWindow::OnContainerFocusOutEvent(GdkEventFocus *aEvent) michael@0: { michael@0: LOGFOCUS(("OnContainerFocusOutEvent [%p]\n", (void *)this)); michael@0: michael@0: if (mWindowType == eWindowType_toplevel || mWindowType == eWindowType_dialog) { michael@0: nsCOMPtr dragService = do_GetService(kCDragServiceCID); michael@0: nsCOMPtr dragSession; michael@0: dragService->GetCurrentSession(getter_AddRefs(dragSession)); michael@0: michael@0: // Rollup popups when a window is focused out unless a drag is occurring. michael@0: // This check is because drags grab the keyboard and cause a focus out on michael@0: // versions of GTK before 2.18. michael@0: bool shouldRollup = !dragSession; michael@0: if (!shouldRollup) { michael@0: // we also roll up when a drag is from a different application michael@0: nsCOMPtr sourceNode; michael@0: dragSession->GetSourceNode(getter_AddRefs(sourceNode)); michael@0: shouldRollup = (sourceNode == nullptr); michael@0: } michael@0: michael@0: if (shouldRollup) { michael@0: CheckForRollup(0, 0, false, true); michael@0: } michael@0: } michael@0: michael@0: #if (MOZ_WIDGET_GTK == 2) && defined(MOZ_X11) michael@0: // plugin lose focus michael@0: if (gPluginFocusWindow) { michael@0: nsRefPtr kungFuDeathGrip = gPluginFocusWindow; michael@0: gPluginFocusWindow->LoseNonXEmbedPluginFocus(); michael@0: } michael@0: #endif /* MOZ_X11 && MOZ_WIDGET_GTK2 */ michael@0: michael@0: if (gFocusWindow) { michael@0: nsRefPtr kungFuDeathGrip = gFocusWindow; michael@0: if (gFocusWindow->mIMModule) { michael@0: gFocusWindow->mIMModule->OnBlurWindow(gFocusWindow); michael@0: } michael@0: gFocusWindow = nullptr; michael@0: } michael@0: michael@0: DispatchDeactivateEvent(); michael@0: michael@0: LOGFOCUS(("Done with container focus out [%p]\n", (void *)this)); michael@0: } michael@0: michael@0: bool michael@0: nsWindow::DispatchCommandEvent(nsIAtom* aCommand) michael@0: { michael@0: nsEventStatus status; michael@0: WidgetCommandEvent event(true, nsGkAtoms::onAppCommand, aCommand, this); michael@0: DispatchEvent(&event, status); michael@0: return TRUE; michael@0: } michael@0: michael@0: bool michael@0: nsWindow::DispatchContentCommandEvent(int32_t aMsg) michael@0: { michael@0: nsEventStatus status; michael@0: WidgetContentCommandEvent event(true, aMsg, this); michael@0: DispatchEvent(&event, status); michael@0: return TRUE; michael@0: } michael@0: michael@0: static bool michael@0: IsCtrlAltTab(GdkEventKey *aEvent) michael@0: { michael@0: return aEvent->keyval == GDK_Tab && michael@0: KeymapWrapper::AreModifiersActive( michael@0: KeymapWrapper::CTRL | KeymapWrapper::ALT, aEvent->state); michael@0: } michael@0: michael@0: bool michael@0: nsWindow::DispatchKeyDownEvent(GdkEventKey *aEvent, bool *aCancelled) michael@0: { michael@0: NS_PRECONDITION(aCancelled, "aCancelled must not be null"); michael@0: michael@0: *aCancelled = false; michael@0: michael@0: if (IsCtrlAltTab(aEvent)) { michael@0: return false; michael@0: } michael@0: michael@0: // send the key down event michael@0: nsEventStatus status; michael@0: WidgetKeyboardEvent downEvent(true, NS_KEY_DOWN, this); michael@0: KeymapWrapper::InitKeyEvent(downEvent, aEvent); michael@0: DispatchEvent(&downEvent, status); michael@0: *aCancelled = (status == nsEventStatus_eConsumeNoDefault); michael@0: return true; michael@0: } michael@0: michael@0: gboolean michael@0: nsWindow::OnKeyPressEvent(GdkEventKey *aEvent) michael@0: { michael@0: LOGFOCUS(("OnKeyPressEvent [%p]\n", (void *)this)); michael@0: michael@0: // if we are in the middle of composing text, XIM gets to see it michael@0: // before mozilla does. michael@0: bool IMEWasEnabled = false; michael@0: if (mIMModule) { michael@0: IMEWasEnabled = mIMModule->IsEnabled(); michael@0: if (mIMModule->OnKeyEvent(this, aEvent)) { michael@0: return TRUE; michael@0: } michael@0: } michael@0: michael@0: nsEventStatus status; michael@0: michael@0: // work around for annoying things. michael@0: if (IsCtrlAltTab(aEvent)) { michael@0: return TRUE; michael@0: } michael@0: michael@0: nsCOMPtr kungFuDeathGrip = this; michael@0: michael@0: // Dispatch keydown event always. At auto repeating, we should send michael@0: // KEYDOWN -> KEYPRESS -> KEYDOWN -> KEYPRESS ... -> KEYUP michael@0: // However, old distributions (e.g., Ubuntu 9.10) sent native key michael@0: // release event, so, on such platform, the DOM events will be: michael@0: // KEYDOWN -> KEYPRESS -> KEYUP -> KEYDOWN -> KEYPRESS -> KEYUP... michael@0: michael@0: bool isKeyDownCancelled = false; michael@0: if (DispatchKeyDownEvent(aEvent, &isKeyDownCancelled) && michael@0: (MOZ_UNLIKELY(mIsDestroyed) || isKeyDownCancelled)) { michael@0: return TRUE; michael@0: } michael@0: michael@0: // If a keydown event handler causes to enable IME, i.e., it moves michael@0: // focus from IME unusable content to IME usable editor, we should michael@0: // send the native key event to IME for the first input on the editor. michael@0: if (!IMEWasEnabled && mIMModule && mIMModule->IsEnabled()) { michael@0: // Notice our keydown event was already dispatched. This prevents michael@0: // unnecessary DOM keydown event in the editor. michael@0: if (mIMModule->OnKeyEvent(this, aEvent, true)) { michael@0: return TRUE; michael@0: } michael@0: } michael@0: michael@0: // Don't pass modifiers as NS_KEY_PRESS events. michael@0: // TODO: Instead of selectively excluding some keys from NS_KEY_PRESS events, michael@0: // we should instead selectively include (as per MSDN spec; no official michael@0: // spec covers KeyPress events). michael@0: if (!KeymapWrapper::IsKeyPressEventNecessary(aEvent)) { michael@0: return TRUE; michael@0: } michael@0: michael@0: #ifdef MOZ_X11 michael@0: #if ! defined AIX // no XFree86 on AIX 5L michael@0: // Look for specialized app-command keys michael@0: switch (aEvent->keyval) { michael@0: case XF86XK_Back: michael@0: return DispatchCommandEvent(nsGkAtoms::Back); michael@0: case XF86XK_Forward: michael@0: return DispatchCommandEvent(nsGkAtoms::Forward); michael@0: case XF86XK_Refresh: michael@0: return DispatchCommandEvent(nsGkAtoms::Reload); michael@0: case XF86XK_Stop: michael@0: return DispatchCommandEvent(nsGkAtoms::Stop); michael@0: case XF86XK_Search: michael@0: return DispatchCommandEvent(nsGkAtoms::Search); michael@0: case XF86XK_Favorites: michael@0: return DispatchCommandEvent(nsGkAtoms::Bookmarks); michael@0: case XF86XK_HomePage: michael@0: return DispatchCommandEvent(nsGkAtoms::Home); michael@0: case XF86XK_Copy: michael@0: case GDK_F16: // F16, F20, F18, F14 are old keysyms for Copy Cut Paste Undo michael@0: return DispatchContentCommandEvent(NS_CONTENT_COMMAND_COPY); michael@0: case XF86XK_Cut: michael@0: case GDK_F20: michael@0: return DispatchContentCommandEvent(NS_CONTENT_COMMAND_CUT); michael@0: case XF86XK_Paste: michael@0: case GDK_F18: michael@0: return DispatchContentCommandEvent(NS_CONTENT_COMMAND_PASTE); michael@0: case GDK_Redo: michael@0: return DispatchContentCommandEvent(NS_CONTENT_COMMAND_REDO); michael@0: case GDK_Undo: michael@0: case GDK_F14: michael@0: return DispatchContentCommandEvent(NS_CONTENT_COMMAND_UNDO); michael@0: } michael@0: #endif /* ! AIX */ michael@0: #endif /* MOZ_X11 */ michael@0: michael@0: WidgetKeyboardEvent event(true, NS_KEY_PRESS, this); michael@0: KeymapWrapper::InitKeyEvent(event, aEvent); michael@0: michael@0: // before we dispatch a key, check if it's the context menu key. michael@0: // If so, send a context menu key event instead. michael@0: if (is_context_menu_key(event)) { michael@0: WidgetMouseEvent contextMenuEvent(true, NS_CONTEXTMENU, this, michael@0: WidgetMouseEvent::eReal, michael@0: WidgetMouseEvent::eContextMenuKey); michael@0: michael@0: contextMenuEvent.refPoint = LayoutDeviceIntPoint(0, 0); michael@0: contextMenuEvent.time = aEvent->time; michael@0: contextMenuEvent.clickCount = 1; michael@0: KeymapWrapper::InitInputEvent(contextMenuEvent, aEvent->state); michael@0: DispatchEvent(&contextMenuEvent, status); michael@0: } michael@0: else { michael@0: // If the character code is in the BMP, send the key press event. michael@0: // Otherwise, send a text event with the equivalent UTF-16 string. michael@0: if (IS_IN_BMP(event.charCode)) { michael@0: DispatchEvent(&event, status); michael@0: } michael@0: else { michael@0: WidgetTextEvent textEvent(true, NS_TEXT_TEXT, this); michael@0: char16_t textString[3]; michael@0: textString[0] = H_SURROGATE(event.charCode); michael@0: textString[1] = L_SURROGATE(event.charCode); michael@0: textString[2] = 0; michael@0: textEvent.theText = textString; michael@0: textEvent.time = event.time; michael@0: DispatchEvent(&textEvent, status); michael@0: } michael@0: } michael@0: michael@0: // If the event was consumed, return. michael@0: if (status == nsEventStatus_eConsumeNoDefault) { michael@0: return TRUE; michael@0: } michael@0: michael@0: return FALSE; michael@0: } michael@0: michael@0: gboolean michael@0: nsWindow::OnKeyReleaseEvent(GdkEventKey *aEvent) michael@0: { michael@0: LOGFOCUS(("OnKeyReleaseEvent [%p]\n", (void *)this)); michael@0: michael@0: if (mIMModule && mIMModule->OnKeyEvent(this, aEvent)) { michael@0: return TRUE; michael@0: } michael@0: michael@0: // send the key event as a key up event michael@0: WidgetKeyboardEvent event(true, NS_KEY_UP, this); michael@0: KeymapWrapper::InitKeyEvent(event, aEvent); michael@0: michael@0: nsEventStatus status; michael@0: DispatchEvent(&event, status); michael@0: michael@0: // If the event was consumed, return. michael@0: if (status == nsEventStatus_eConsumeNoDefault) { michael@0: return TRUE; michael@0: } michael@0: michael@0: return FALSE; michael@0: } michael@0: michael@0: void michael@0: nsWindow::OnScrollEvent(GdkEventScroll *aEvent) michael@0: { michael@0: // check to see if we should rollup michael@0: if (CheckForRollup(aEvent->x_root, aEvent->y_root, true, false)) michael@0: return; michael@0: michael@0: WidgetWheelEvent wheelEvent(true, NS_WHEEL_WHEEL, this); michael@0: wheelEvent.deltaMode = nsIDOMWheelEvent::DOM_DELTA_LINE; michael@0: switch (aEvent->direction) { michael@0: case GDK_SCROLL_UP: michael@0: wheelEvent.deltaY = wheelEvent.lineOrPageDeltaY = -3; michael@0: break; michael@0: case GDK_SCROLL_DOWN: michael@0: wheelEvent.deltaY = wheelEvent.lineOrPageDeltaY = 3; michael@0: break; michael@0: case GDK_SCROLL_LEFT: michael@0: wheelEvent.deltaX = wheelEvent.lineOrPageDeltaX = -1; michael@0: break; michael@0: case GDK_SCROLL_RIGHT: michael@0: wheelEvent.deltaX = wheelEvent.lineOrPageDeltaX = 1; michael@0: break; michael@0: } michael@0: michael@0: NS_ASSERTION(wheelEvent.deltaX || wheelEvent.deltaY, michael@0: "deltaX or deltaY must be non-zero"); michael@0: michael@0: if (aEvent->window == mGdkWindow) { michael@0: // we are the window that the event happened on so no need for expensive WidgetToScreenOffset michael@0: wheelEvent.refPoint.x = nscoord(aEvent->x); michael@0: wheelEvent.refPoint.y = nscoord(aEvent->y); michael@0: } else { michael@0: // XXX we're never quite sure which GdkWindow the event came from due to our custom bubbling michael@0: // in scroll_event_cb(), so use ScreenToWidget to translate the screen root coordinates into michael@0: // coordinates relative to this widget. michael@0: LayoutDeviceIntPoint point(NSToIntFloor(aEvent->x_root), michael@0: NSToIntFloor(aEvent->y_root)); michael@0: wheelEvent.refPoint = point - michael@0: LayoutDeviceIntPoint::FromUntyped(WidgetToScreenOffset()); michael@0: } michael@0: michael@0: KeymapWrapper::InitInputEvent(wheelEvent, aEvent->state); michael@0: michael@0: wheelEvent.time = aEvent->time; michael@0: michael@0: nsEventStatus status; michael@0: DispatchEvent(&wheelEvent, status); michael@0: } michael@0: michael@0: void michael@0: nsWindow::OnVisibilityNotifyEvent(GdkEventVisibility *aEvent) michael@0: { michael@0: LOGDRAW(("Visibility event %i on [%p] %p\n", michael@0: aEvent->state, this, aEvent->window)); michael@0: michael@0: if (!mGdkWindow) michael@0: return; michael@0: michael@0: switch (aEvent->state) { michael@0: case GDK_VISIBILITY_UNOBSCURED: michael@0: case GDK_VISIBILITY_PARTIAL: michael@0: if (mIsFullyObscured && mHasMappedToplevel) { michael@0: // GDK_EXPOSE events have been ignored, so make sure GDK michael@0: // doesn't think that the window has already been painted. michael@0: gdk_window_invalidate_rect(mGdkWindow, nullptr, FALSE); michael@0: } michael@0: michael@0: mIsFullyObscured = false; michael@0: michael@0: if (!nsGtkIMModule::IsVirtualKeyboardOpened()) { michael@0: // if we have to retry the grab, retry it. michael@0: EnsureGrabs(); michael@0: } michael@0: break; michael@0: default: // includes GDK_VISIBILITY_FULLY_OBSCURED michael@0: mIsFullyObscured = true; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsWindow::OnWindowStateEvent(GtkWidget *aWidget, GdkEventWindowState *aEvent) michael@0: { michael@0: LOG(("nsWindow::OnWindowStateEvent [%p] changed %d new_window_state %d\n", michael@0: (void *)this, aEvent->changed_mask, aEvent->new_window_state)); michael@0: michael@0: if (IS_MOZ_CONTAINER(aWidget)) { michael@0: // This event is notifying the container widget of changes to the michael@0: // toplevel window. Just detect changes affecting whether windows are michael@0: // viewable. michael@0: // michael@0: // (A visibility notify event is sent to each window that becomes michael@0: // viewable when the toplevel is mapped, but we can't rely on that for michael@0: // setting mHasMappedToplevel because these toplevel window state michael@0: // events are asynchronous. The windows in the hierarchy now may not michael@0: // be the same windows as when the toplevel was mapped, so they may michael@0: // not get VisibilityNotify events.) michael@0: bool mapped = michael@0: !(aEvent->new_window_state & michael@0: (GDK_WINDOW_STATE_ICONIFIED|GDK_WINDOW_STATE_WITHDRAWN)); michael@0: if (mHasMappedToplevel != mapped) { michael@0: SetHasMappedToplevel(mapped); michael@0: } michael@0: return; michael@0: } michael@0: // else the widget is a shell widget. michael@0: michael@0: // We don't care about anything but changes in the maximized/icon/fullscreen michael@0: // states michael@0: if ((aEvent->changed_mask michael@0: & (GDK_WINDOW_STATE_ICONIFIED | michael@0: GDK_WINDOW_STATE_MAXIMIZED | michael@0: GDK_WINDOW_STATE_FULLSCREEN)) == 0) { michael@0: return; michael@0: } michael@0: michael@0: if (aEvent->new_window_state & GDK_WINDOW_STATE_ICONIFIED) { michael@0: LOG(("\tIconified\n")); michael@0: mSizeState = nsSizeMode_Minimized; michael@0: #ifdef ACCESSIBILITY michael@0: DispatchMinimizeEventAccessible(); michael@0: #endif //ACCESSIBILITY michael@0: } michael@0: else if (aEvent->new_window_state & GDK_WINDOW_STATE_FULLSCREEN) { michael@0: LOG(("\tFullscreen\n")); michael@0: mSizeState = nsSizeMode_Fullscreen; michael@0: } michael@0: else if (aEvent->new_window_state & GDK_WINDOW_STATE_MAXIMIZED) { michael@0: LOG(("\tMaximized\n")); michael@0: mSizeState = nsSizeMode_Maximized; michael@0: #ifdef ACCESSIBILITY michael@0: DispatchMaximizeEventAccessible(); michael@0: #endif //ACCESSIBILITY michael@0: } michael@0: else { michael@0: LOG(("\tNormal\n")); michael@0: mSizeState = nsSizeMode_Normal; michael@0: #ifdef ACCESSIBILITY michael@0: DispatchRestoreEventAccessible(); michael@0: #endif //ACCESSIBILITY michael@0: } michael@0: michael@0: if (mWidgetListener) michael@0: mWidgetListener->SizeModeChanged(mSizeState); michael@0: } michael@0: michael@0: void michael@0: nsWindow::ThemeChanged() michael@0: { michael@0: NotifyThemeChanged(); michael@0: michael@0: if (!mGdkWindow || MOZ_UNLIKELY(mIsDestroyed)) michael@0: return; michael@0: michael@0: // Dispatch theme change notification to all child windows michael@0: GList *children = michael@0: gdk_window_peek_children(mGdkWindow); michael@0: while (children) { michael@0: GdkWindow *gdkWin = GDK_WINDOW(children->data); michael@0: michael@0: nsWindow *win = (nsWindow*) g_object_get_data(G_OBJECT(gdkWin), michael@0: "nsWindow"); michael@0: michael@0: if (win && win != this) { // guard against infinite recursion michael@0: nsRefPtr kungFuDeathGrip = win; michael@0: win->ThemeChanged(); michael@0: } michael@0: michael@0: children = children->next; michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsWindow::DispatchDragEvent(uint32_t aMsg, const nsIntPoint& aRefPoint, michael@0: guint aTime) michael@0: { michael@0: WidgetDragEvent event(true, aMsg, this); michael@0: michael@0: if (aMsg == NS_DRAGDROP_OVER) { michael@0: InitDragEvent(event); michael@0: } michael@0: michael@0: event.refPoint = LayoutDeviceIntPoint::FromUntyped(aRefPoint); michael@0: event.time = aTime; michael@0: michael@0: nsEventStatus status; michael@0: DispatchEvent(&event, status); michael@0: } michael@0: michael@0: void michael@0: nsWindow::OnDragDataReceivedEvent(GtkWidget *aWidget, michael@0: GdkDragContext *aDragContext, michael@0: gint aX, michael@0: gint aY, michael@0: GtkSelectionData *aSelectionData, michael@0: guint aInfo, michael@0: guint aTime, michael@0: gpointer aData) michael@0: { michael@0: LOGDRAG(("nsWindow::OnDragDataReceived(%p)\n", (void*)this)); michael@0: michael@0: nsDragService::GetInstance()-> michael@0: TargetDataReceived(aWidget, aDragContext, aX, aY, michael@0: aSelectionData, aInfo, aTime); michael@0: } michael@0: michael@0: static void michael@0: GetBrandName(nsXPIDLString& brandName) michael@0: { michael@0: nsCOMPtr bundleService = michael@0: do_GetService(NS_STRINGBUNDLE_CONTRACTID); michael@0: michael@0: nsCOMPtr bundle; michael@0: if (bundleService) michael@0: bundleService->CreateBundle( michael@0: "chrome://branding/locale/brand.properties", michael@0: getter_AddRefs(bundle)); michael@0: michael@0: if (bundle) michael@0: bundle->GetStringFromName( michael@0: MOZ_UTF16("brandShortName"), michael@0: getter_Copies(brandName)); michael@0: michael@0: if (brandName.IsEmpty()) michael@0: brandName.Assign(NS_LITERAL_STRING("Mozilla")); michael@0: } michael@0: michael@0: static GdkWindow * michael@0: CreateGdkWindow(GdkWindow *parent, GtkWidget *widget) michael@0: { michael@0: GdkWindowAttr attributes; michael@0: gint attributes_mask = GDK_WA_VISUAL; michael@0: michael@0: attributes.event_mask = kEvents; michael@0: michael@0: attributes.width = 1; michael@0: attributes.height = 1; michael@0: attributes.wclass = GDK_INPUT_OUTPUT; michael@0: attributes.visual = gtk_widget_get_visual(widget); michael@0: attributes.window_type = GDK_WINDOW_CHILD; michael@0: michael@0: #if (MOZ_WIDGET_GTK == 2) michael@0: attributes_mask |= GDK_WA_COLORMAP; michael@0: attributes.colormap = gtk_widget_get_colormap(widget); michael@0: #endif michael@0: michael@0: GdkWindow *window = gdk_window_new(parent, &attributes, attributes_mask); michael@0: gdk_window_set_user_data(window, widget); michael@0: michael@0: // GTK3 TODO? michael@0: #if (MOZ_WIDGET_GTK == 2) michael@0: /* set the default pixmap to None so that you don't end up with the michael@0: gtk default which is BlackPixel. */ michael@0: gdk_window_set_back_pixmap(window, nullptr, FALSE); michael@0: #endif michael@0: michael@0: return window; michael@0: } michael@0: michael@0: nsresult michael@0: nsWindow::Create(nsIWidget *aParent, michael@0: nsNativeWidget aNativeParent, michael@0: const nsIntRect &aRect, michael@0: nsDeviceContext *aContext, michael@0: nsWidgetInitData *aInitData) michael@0: { michael@0: // only set the base parent if we're going to be a dialog or a michael@0: // toplevel michael@0: nsIWidget *baseParent = aInitData && michael@0: (aInitData->mWindowType == eWindowType_dialog || michael@0: aInitData->mWindowType == eWindowType_toplevel || michael@0: aInitData->mWindowType == eWindowType_invisible) ? michael@0: nullptr : aParent; michael@0: michael@0: #ifdef ACCESSIBILITY michael@0: // Send a DBus message to check whether a11y is enabled michael@0: a11y::PreInit(); michael@0: #endif michael@0: michael@0: // Ensure that the toolkit is created. michael@0: nsGTKToolkit::GetToolkit(); michael@0: michael@0: // initialize all the common bits of this class michael@0: BaseCreate(baseParent, aRect, aContext, aInitData); michael@0: michael@0: // Do we need to listen for resizes? michael@0: bool listenForResizes = false;; michael@0: if (aNativeParent || (aInitData && aInitData->mListenForResizes)) michael@0: listenForResizes = true; michael@0: michael@0: // and do our common creation michael@0: CommonCreate(aParent, listenForResizes); michael@0: michael@0: // save our bounds michael@0: mBounds = aRect; michael@0: ConstrainSize(&mBounds.width, &mBounds.height); michael@0: michael@0: // figure out our parent window michael@0: GtkWidget *parentMozContainer = nullptr; michael@0: GtkContainer *parentGtkContainer = nullptr; michael@0: GdkWindow *parentGdkWindow = nullptr; michael@0: GtkWindow *topLevelParent = nullptr; michael@0: nsWindow *parentnsWindow = nullptr; michael@0: GtkWidget *eventWidget = nullptr; michael@0: michael@0: if (aParent) { michael@0: parentnsWindow = static_cast(aParent); michael@0: parentGdkWindow = parentnsWindow->mGdkWindow; michael@0: } else if (aNativeParent && GDK_IS_WINDOW(aNativeParent)) { michael@0: parentGdkWindow = GDK_WINDOW(aNativeParent); michael@0: parentnsWindow = get_window_for_gdk_window(parentGdkWindow); michael@0: if (!parentnsWindow) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: } else if (aNativeParent && GTK_IS_CONTAINER(aNativeParent)) { michael@0: parentGtkContainer = GTK_CONTAINER(aNativeParent); michael@0: } michael@0: michael@0: if (parentGdkWindow) { michael@0: // get the widget for the window - it should be a moz container michael@0: parentMozContainer = parentnsWindow->GetMozContainerWidget(); michael@0: if (!parentMozContainer) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: // get the toplevel window just in case someone needs to use it michael@0: // for setting transients or whatever. michael@0: topLevelParent = michael@0: GTK_WINDOW(gtk_widget_get_toplevel(parentMozContainer)); michael@0: } michael@0: michael@0: // ok, create our windows michael@0: switch (mWindowType) { michael@0: case eWindowType_dialog: michael@0: case eWindowType_popup: michael@0: case eWindowType_toplevel: michael@0: case eWindowType_invisible: { michael@0: mIsTopLevel = true; michael@0: michael@0: // We only move a general managed toplevel window if someone has michael@0: // actually placed the window somewhere. If no placement has taken michael@0: // place, we just let the window manager Do The Right Thing. michael@0: // michael@0: // Indicate that if we're shown, we at least need to have our size set. michael@0: // If we get explicitly moved, the position will also be set. michael@0: mNeedsResize = true; michael@0: michael@0: if (mWindowType == eWindowType_dialog) { michael@0: mShell = gtk_window_new(GTK_WINDOW_TOPLEVEL); michael@0: SetDefaultIcon(); michael@0: gtk_window_set_wmclass(GTK_WINDOW(mShell), "Dialog", michael@0: gdk_get_program_class()); michael@0: gtk_window_set_type_hint(GTK_WINDOW(mShell), michael@0: GDK_WINDOW_TYPE_HINT_DIALOG); michael@0: gtk_window_set_transient_for(GTK_WINDOW(mShell), michael@0: topLevelParent); michael@0: } michael@0: else if (mWindowType == eWindowType_popup) { michael@0: // With popup windows, we want to control their position, so don't michael@0: // wait for the window manager to place them (which wouldn't michael@0: // happen with override-redirect windows anyway). michael@0: mNeedsMove = true; michael@0: michael@0: // Popups that are not noautohide are only temporary. The are used michael@0: // for menus and the like and disappear when another window is used. michael@0: // For most popups, use the standard GtkWindowType GTK_WINDOW_POPUP, michael@0: // which will use a Window with the override-redirect attribute michael@0: // (for temporary windows). michael@0: // For long-lived windows, their stacking order is managed by the michael@0: // window manager, as indicated by GTK_WINDOW_TOPLEVEL ... michael@0: GtkWindowType type = aInitData->mNoAutoHide ? michael@0: GTK_WINDOW_TOPLEVEL : GTK_WINDOW_POPUP; michael@0: mShell = gtk_window_new(type); michael@0: gtk_window_set_wmclass(GTK_WINDOW(mShell), "Popup", michael@0: gdk_get_program_class()); michael@0: michael@0: if (aInitData->mSupportTranslucency) { michael@0: // We need to select an ARGB visual here instead of in michael@0: // SetTransparencyMode() because it has to be done before the michael@0: // widget is realized. An ARGB visual is only useful if we michael@0: // are on a compositing window manager. michael@0: GdkScreen *screen = gtk_widget_get_screen(mShell); michael@0: if (gdk_screen_is_composited(screen)) { michael@0: #if (MOZ_WIDGET_GTK == 2) michael@0: GdkColormap *colormap = michael@0: gdk_screen_get_rgba_colormap(screen); michael@0: gtk_widget_set_colormap(mShell, colormap); michael@0: #else michael@0: GdkVisual *visual = gdk_screen_get_rgba_visual(screen); michael@0: gtk_widget_set_visual(mShell, visual); michael@0: #endif michael@0: } michael@0: } michael@0: if (aInitData->mNoAutoHide) { michael@0: // ... but the window manager does not decorate this window, michael@0: // nor provide a separate taskbar icon. michael@0: if (mBorderStyle == eBorderStyle_default) { michael@0: gtk_window_set_decorated(GTK_WINDOW(mShell), FALSE); michael@0: } michael@0: else { michael@0: bool decorate = mBorderStyle & eBorderStyle_title; michael@0: gtk_window_set_decorated(GTK_WINDOW(mShell), decorate); michael@0: if (decorate) { michael@0: gtk_window_set_deletable(GTK_WINDOW(mShell), mBorderStyle & eBorderStyle_close); michael@0: } michael@0: } michael@0: gtk_window_set_skip_taskbar_hint(GTK_WINDOW(mShell), TRUE); michael@0: // Element focus is managed by the parent window so the michael@0: // WM_HINTS input field is set to False to tell the window michael@0: // manager not to set input focus to this window ... michael@0: gtk_window_set_accept_focus(GTK_WINDOW(mShell), FALSE); michael@0: #ifdef MOZ_X11 michael@0: // ... but when the window manager offers focus through michael@0: // WM_TAKE_FOCUS, focus is requested on the parent window. michael@0: gtk_widget_realize(mShell); michael@0: gdk_window_add_filter(gtk_widget_get_window(mShell), michael@0: popup_take_focus_filter, nullptr); michael@0: #endif michael@0: } michael@0: michael@0: GdkWindowTypeHint gtkTypeHint; michael@0: if (aInitData->mIsDragPopup) { michael@0: gtkTypeHint = GDK_WINDOW_TYPE_HINT_DND; michael@0: } michael@0: else { michael@0: switch (aInitData->mPopupHint) { michael@0: case ePopupTypeMenu: michael@0: gtkTypeHint = GDK_WINDOW_TYPE_HINT_POPUP_MENU; michael@0: break; michael@0: case ePopupTypeTooltip: michael@0: gtkTypeHint = GDK_WINDOW_TYPE_HINT_TOOLTIP; michael@0: break; michael@0: default: michael@0: gtkTypeHint = GDK_WINDOW_TYPE_HINT_UTILITY; michael@0: break; michael@0: } michael@0: } michael@0: gtk_window_set_type_hint(GTK_WINDOW(mShell), gtkTypeHint); michael@0: michael@0: if (topLevelParent) { michael@0: gtk_window_set_transient_for(GTK_WINDOW(mShell), michael@0: topLevelParent); michael@0: } michael@0: } michael@0: else { // must be eWindowType_toplevel michael@0: mShell = gtk_window_new(GTK_WINDOW_TOPLEVEL); michael@0: SetDefaultIcon(); michael@0: gtk_window_set_wmclass(GTK_WINDOW(mShell), "Toplevel", michael@0: gdk_get_program_class()); michael@0: michael@0: // each toplevel window gets its own window group michael@0: GtkWindowGroup *group = gtk_window_group_new(); michael@0: gtk_window_group_add_window(group, GTK_WINDOW(mShell)); michael@0: g_object_unref(group); michael@0: } michael@0: michael@0: // Prevent GtkWindow from painting a background to flicker. michael@0: gtk_widget_set_app_paintable(mShell, TRUE); michael@0: michael@0: // Create a container to hold child windows and child GtkWidgets. michael@0: GtkWidget *container = moz_container_new(); michael@0: mContainer = MOZ_CONTAINER(container); michael@0: // Use mShell's window for drawing and events. michael@0: gtk_widget_set_has_window(container, FALSE); michael@0: eventWidget = mShell; michael@0: gtk_widget_add_events(eventWidget, kEvents); michael@0: gtk_container_add(GTK_CONTAINER(mShell), container); michael@0: gtk_widget_realize(container); michael@0: michael@0: // make sure this is the focus widget in the container michael@0: gtk_widget_show(container); michael@0: gtk_widget_grab_focus(container); michael@0: michael@0: // the drawing window michael@0: mGdkWindow = gtk_widget_get_window(mShell); michael@0: michael@0: if (mWindowType == eWindowType_popup) { michael@0: // gdk does not automatically set the cursor for "temporary" michael@0: // windows, which are what gtk uses for popups. michael@0: michael@0: mCursor = eCursor_wait; // force SetCursor to actually set the michael@0: // cursor, even though our internal state michael@0: // indicates that we already have the michael@0: // standard cursor. michael@0: SetCursor(eCursor_standard); michael@0: michael@0: if (aInitData->mNoAutoHide) { michael@0: gint wmd = ConvertBorderStyles(mBorderStyle); michael@0: if (wmd != -1) michael@0: gdk_window_set_decorations(gtk_widget_get_window(mShell), (GdkWMDecoration) wmd); michael@0: } michael@0: michael@0: // If the popup ignores mouse events, set an empty input shape. michael@0: if (aInitData->mMouseTransparent) { michael@0: #if (MOZ_WIDGET_GTK == 2) michael@0: GdkRectangle rect = { 0, 0, 0, 0 }; michael@0: GdkRegion *region = gdk_region_rectangle(&rect); michael@0: michael@0: gdk_window_input_shape_combine_region(mGdkWindow, region, 0, 0); michael@0: gdk_region_destroy(region); michael@0: #else michael@0: cairo_rectangle_int_t rect = { 0, 0, 0, 0 }; michael@0: cairo_region_t *region = cairo_region_create_rectangle(&rect); michael@0: michael@0: gdk_window_input_shape_combine_region(mGdkWindow, region, 0, 0); michael@0: cairo_region_destroy(region); michael@0: #endif michael@0: } michael@0: } michael@0: } michael@0: break; michael@0: case eWindowType_plugin: michael@0: case eWindowType_child: { michael@0: if (parentMozContainer) { michael@0: mGdkWindow = CreateGdkWindow(parentGdkWindow, parentMozContainer); michael@0: mHasMappedToplevel = parentnsWindow->mHasMappedToplevel; michael@0: } michael@0: else if (parentGtkContainer) { michael@0: // This MozContainer has its own window for drawing and receives michael@0: // events because there is no mShell widget (corresponding to this michael@0: // nsWindow). michael@0: GtkWidget *container = moz_container_new(); michael@0: mContainer = MOZ_CONTAINER(container); michael@0: eventWidget = container; michael@0: gtk_widget_add_events(eventWidget, kEvents); michael@0: gtk_container_add(parentGtkContainer, container); michael@0: gtk_widget_realize(container); michael@0: michael@0: mGdkWindow = gtk_widget_get_window(container); michael@0: } michael@0: else { michael@0: NS_WARNING("Warning: tried to create a new child widget with no parent!"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: } michael@0: break; michael@0: default: michael@0: break; michael@0: } michael@0: michael@0: // label the drawing window with this object so we can find our way home michael@0: g_object_set_data(G_OBJECT(mGdkWindow), "nsWindow", this); michael@0: michael@0: if (mContainer) michael@0: g_object_set_data(G_OBJECT(mContainer), "nsWindow", this); michael@0: michael@0: if (mShell) michael@0: g_object_set_data(G_OBJECT(mShell), "nsWindow", this); michael@0: michael@0: // attach listeners for events michael@0: if (mShell) { michael@0: g_signal_connect(mShell, "configure_event", michael@0: G_CALLBACK(configure_event_cb), nullptr); michael@0: g_signal_connect(mShell, "delete_event", michael@0: G_CALLBACK(delete_event_cb), nullptr); michael@0: g_signal_connect(mShell, "window_state_event", michael@0: G_CALLBACK(window_state_event_cb), nullptr); michael@0: michael@0: GtkSettings* default_settings = gtk_settings_get_default(); michael@0: g_signal_connect_after(default_settings, michael@0: "notify::gtk-theme-name", michael@0: G_CALLBACK(theme_changed_cb), this); michael@0: g_signal_connect_after(default_settings, michael@0: "notify::gtk-font-name", michael@0: G_CALLBACK(theme_changed_cb), this); michael@0: } michael@0: michael@0: if (mContainer) { michael@0: // Widget signals michael@0: g_signal_connect(mContainer, "unrealize", michael@0: G_CALLBACK(container_unrealize_cb), nullptr); michael@0: g_signal_connect_after(mContainer, "size_allocate", michael@0: G_CALLBACK(size_allocate_cb), nullptr); michael@0: g_signal_connect(mContainer, "hierarchy-changed", michael@0: G_CALLBACK(hierarchy_changed_cb), nullptr); michael@0: // Initialize mHasMappedToplevel. michael@0: hierarchy_changed_cb(GTK_WIDGET(mContainer), nullptr); michael@0: // Expose, focus, key, and drag events are sent even to GTK_NO_WINDOW michael@0: // widgets. michael@0: #if (MOZ_WIDGET_GTK == 2) michael@0: g_signal_connect(mContainer, "expose_event", michael@0: G_CALLBACK(expose_event_cb), nullptr); michael@0: #else michael@0: g_signal_connect(G_OBJECT(mContainer), "draw", michael@0: G_CALLBACK(expose_event_cb), nullptr); michael@0: #endif michael@0: g_signal_connect(mContainer, "focus_in_event", michael@0: G_CALLBACK(focus_in_event_cb), nullptr); michael@0: g_signal_connect(mContainer, "focus_out_event", michael@0: G_CALLBACK(focus_out_event_cb), nullptr); michael@0: g_signal_connect(mContainer, "key_press_event", michael@0: G_CALLBACK(key_press_event_cb), nullptr); michael@0: g_signal_connect(mContainer, "key_release_event", michael@0: G_CALLBACK(key_release_event_cb), nullptr); michael@0: michael@0: gtk_drag_dest_set((GtkWidget *)mContainer, michael@0: (GtkDestDefaults)0, michael@0: nullptr, michael@0: 0, michael@0: (GdkDragAction)0); michael@0: michael@0: g_signal_connect(mContainer, "drag_motion", michael@0: G_CALLBACK(drag_motion_event_cb), nullptr); michael@0: g_signal_connect(mContainer, "drag_leave", michael@0: G_CALLBACK(drag_leave_event_cb), nullptr); michael@0: g_signal_connect(mContainer, "drag_drop", michael@0: G_CALLBACK(drag_drop_event_cb), nullptr); michael@0: g_signal_connect(mContainer, "drag_data_received", michael@0: G_CALLBACK(drag_data_received_event_cb), nullptr); michael@0: michael@0: GtkWidget *widgets[] = { GTK_WIDGET(mContainer), mShell }; michael@0: for (size_t i = 0; i < ArrayLength(widgets) && widgets[i]; ++i) { michael@0: // Visibility events are sent to the owning widget of the relevant michael@0: // window but do not propagate to parent widgets so connect on michael@0: // mShell (if it exists) as well as mContainer. michael@0: g_signal_connect(widgets[i], "visibility-notify-event", michael@0: G_CALLBACK(visibility_notify_event_cb), nullptr); michael@0: // Similarly double buffering is controlled by the window's owning michael@0: // widget. Disable double buffering for painting directly to the michael@0: // X Window. michael@0: gtk_widget_set_double_buffered(widgets[i], FALSE); michael@0: } michael@0: michael@0: // We create input contexts for all containers, except for michael@0: // toplevel popup windows michael@0: if (mWindowType != eWindowType_popup) { michael@0: mIMModule = new nsGtkIMModule(this); michael@0: } michael@0: } else if (!mIMModule) { michael@0: nsWindow *container = GetContainerWindow(); michael@0: if (container) { michael@0: mIMModule = container->mIMModule; michael@0: } michael@0: } michael@0: michael@0: if (eventWidget) { michael@0: #if (MOZ_WIDGET_GTK == 2) michael@0: // Don't let GTK mess with the shapes of our GdkWindows michael@0: GTK_PRIVATE_SET_FLAG(eventWidget, GTK_HAS_SHAPE_MASK); michael@0: #endif michael@0: michael@0: // These events are sent to the owning widget of the relevant window michael@0: // and propagate up to the first widget that handles the events, so we michael@0: // need only connect on mShell, if it exists, to catch events on its michael@0: // window and windows of mContainer. michael@0: g_signal_connect(eventWidget, "enter-notify-event", michael@0: G_CALLBACK(enter_notify_event_cb), nullptr); michael@0: g_signal_connect(eventWidget, "leave-notify-event", michael@0: G_CALLBACK(leave_notify_event_cb), nullptr); michael@0: g_signal_connect(eventWidget, "motion-notify-event", michael@0: G_CALLBACK(motion_notify_event_cb), nullptr); michael@0: g_signal_connect(eventWidget, "button-press-event", michael@0: G_CALLBACK(button_press_event_cb), nullptr); michael@0: g_signal_connect(eventWidget, "button-release-event", michael@0: G_CALLBACK(button_release_event_cb), nullptr); michael@0: g_signal_connect(eventWidget, "scroll-event", michael@0: G_CALLBACK(scroll_event_cb), nullptr); michael@0: } michael@0: michael@0: LOG(("nsWindow [%p]\n", (void *)this)); michael@0: if (mShell) { michael@0: LOG(("\tmShell %p mContainer %p mGdkWindow %p 0x%lx\n", michael@0: mShell, mContainer, mGdkWindow, michael@0: gdk_x11_window_get_xid(mGdkWindow))); michael@0: } else if (mContainer) { michael@0: LOG(("\tmContainer %p mGdkWindow %p\n", mContainer, mGdkWindow)); michael@0: } michael@0: else if (mGdkWindow) { michael@0: LOG(("\tmGdkWindow %p parent %p\n", michael@0: mGdkWindow, gdk_window_get_parent(mGdkWindow))); michael@0: } michael@0: michael@0: // resize so that everything is set to the right dimensions michael@0: if (!mIsTopLevel) michael@0: Resize(mBounds.x, mBounds.y, mBounds.width, mBounds.height, false); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindow::SetWindowClass(const nsAString &xulWinType) michael@0: { michael@0: if (!mShell) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: const char *res_class = gdk_get_program_class(); michael@0: if (!res_class) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: char *res_name = ToNewCString(xulWinType); michael@0: if (!res_name) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: const char *role = nullptr; michael@0: michael@0: // Parse res_name into a name and role. Characters other than michael@0: // [A-Za-z0-9_-] are converted to '_'. Anything after the first michael@0: // colon is assigned to role; if there's no colon, assign the michael@0: // whole thing to both role and res_name. michael@0: for (char *c = res_name; *c; c++) { michael@0: if (':' == *c) { michael@0: *c = 0; michael@0: role = c + 1; michael@0: } michael@0: else if (!isascii(*c) || (!isalnum(*c) && ('_' != *c) && ('-' != *c))) michael@0: *c = '_'; michael@0: } michael@0: res_name[0] = toupper(res_name[0]); michael@0: if (!role) role = res_name; michael@0: michael@0: GdkWindow *shellWindow = gtk_widget_get_window(GTK_WIDGET(mShell)); michael@0: gdk_window_set_role(shellWindow, role); michael@0: michael@0: #ifdef MOZ_X11 michael@0: XClassHint *class_hint = XAllocClassHint(); michael@0: if (!class_hint) { michael@0: nsMemory::Free(res_name); michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: class_hint->res_name = res_name; michael@0: class_hint->res_class = const_cast(res_class); michael@0: michael@0: // Can't use gtk_window_set_wmclass() for this; it prints michael@0: // a warning & refuses to make the change. michael@0: XSetClassHint(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), michael@0: gdk_x11_window_get_xid(shellWindow), michael@0: class_hint); michael@0: XFree(class_hint); michael@0: #endif /* MOZ_X11 */ michael@0: michael@0: nsMemory::Free(res_name); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsWindow::NativeResize(int32_t aWidth, int32_t aHeight, bool aRepaint) michael@0: { michael@0: LOG(("nsWindow::NativeResize [%p] %d %d\n", (void *)this, michael@0: aWidth, aHeight)); michael@0: michael@0: // clear our resize flag michael@0: mNeedsResize = false; michael@0: michael@0: if (mIsTopLevel) { michael@0: gtk_window_resize(GTK_WINDOW(mShell), aWidth, aHeight); michael@0: } michael@0: else if (mContainer) { michael@0: GtkWidget *widget = GTK_WIDGET(mContainer); michael@0: GtkAllocation allocation, prev_allocation; michael@0: gtk_widget_get_allocation(widget, &prev_allocation); michael@0: allocation.x = prev_allocation.x; michael@0: allocation.y = prev_allocation.y; michael@0: allocation.width = aWidth; michael@0: allocation.height = aHeight; michael@0: gtk_widget_size_allocate(widget, &allocation); michael@0: } michael@0: else if (mGdkWindow) { michael@0: gdk_window_resize(mGdkWindow, aWidth, aHeight); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsWindow::NativeResize(int32_t aX, int32_t aY, michael@0: int32_t aWidth, int32_t aHeight, michael@0: bool aRepaint) michael@0: { michael@0: mNeedsResize = false; michael@0: mNeedsMove = false; michael@0: michael@0: LOG(("nsWindow::NativeResize [%p] %d %d %d %d\n", (void *)this, michael@0: aX, aY, aWidth, aHeight)); michael@0: michael@0: if (mIsTopLevel) { michael@0: // aX and aY give the position of the window manager frame top-left. michael@0: gtk_window_move(GTK_WINDOW(mShell), aX, aY); michael@0: // This sets the client window size. michael@0: gtk_window_resize(GTK_WINDOW(mShell), aWidth, aHeight); michael@0: } michael@0: else if (mContainer) { michael@0: GtkAllocation allocation; michael@0: allocation.x = aX; michael@0: allocation.y = aY; michael@0: allocation.width = aWidth; michael@0: allocation.height = aHeight; michael@0: gtk_widget_size_allocate(GTK_WIDGET(mContainer), &allocation); michael@0: } michael@0: else if (mGdkWindow) { michael@0: gdk_window_move_resize(mGdkWindow, aX, aY, aWidth, aHeight); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsWindow::NativeShow(bool aAction) michael@0: { michael@0: if (aAction) { michael@0: // unset our flag now that our window has been shown michael@0: mNeedsShow = false; michael@0: michael@0: if (mIsTopLevel) { michael@0: // Set up usertime/startupID metadata for the created window. michael@0: if (mWindowType != eWindowType_invisible) { michael@0: SetUserTimeAndStartupIDForActivatedWindow(mShell); michael@0: } michael@0: michael@0: gtk_widget_show(mShell); michael@0: } michael@0: else if (mContainer) { michael@0: gtk_widget_show(GTK_WIDGET(mContainer)); michael@0: } michael@0: else if (mGdkWindow) { michael@0: gdk_window_show_unraised(mGdkWindow); michael@0: } michael@0: } michael@0: else { michael@0: if (mIsTopLevel) { michael@0: gtk_widget_hide(GTK_WIDGET(mShell)); michael@0: michael@0: ClearTransparencyBitmap(); // Release some resources michael@0: } michael@0: else if (mContainer) { michael@0: gtk_widget_hide(GTK_WIDGET(mContainer)); michael@0: } michael@0: else if (mGdkWindow) { michael@0: gdk_window_hide(mGdkWindow); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsWindow::SetHasMappedToplevel(bool aState) michael@0: { michael@0: // Even when aState == mHasMappedToplevel (as when this method is called michael@0: // from Show()), child windows need to have their state checked, so don't michael@0: // return early. michael@0: bool oldState = mHasMappedToplevel; michael@0: mHasMappedToplevel = aState; michael@0: michael@0: // mHasMappedToplevel is not updated for children of windows that are michael@0: // hidden; GDK knows not to send expose events for these windows. The michael@0: // state is recorded on the hidden window itself, but, for child trees of michael@0: // hidden windows, their state essentially becomes disconnected from their michael@0: // hidden parent. When the hidden parent gets shown, the child trees are michael@0: // reconnected, and the state of the window being shown can be easily michael@0: // propagated. michael@0: if (!mIsShown || !mGdkWindow) michael@0: return; michael@0: michael@0: if (aState && !oldState && !mIsFullyObscured) { michael@0: // GDK_EXPOSE events have been ignored but the window is now visible, michael@0: // so make sure GDK doesn't think that the window has already been michael@0: // painted. michael@0: gdk_window_invalidate_rect(mGdkWindow, nullptr, FALSE); michael@0: michael@0: // Check that a grab didn't fail due to the window not being michael@0: // viewable. michael@0: EnsureGrabs(); michael@0: } michael@0: michael@0: for (GList *children = gdk_window_peek_children(mGdkWindow); michael@0: children; michael@0: children = children->next) { michael@0: GdkWindow *gdkWin = GDK_WINDOW(children->data); michael@0: nsWindow *child = get_window_for_gdk_window(gdkWin); michael@0: michael@0: if (child && child->mHasMappedToplevel != aState) { michael@0: child->SetHasMappedToplevel(aState); michael@0: } michael@0: } michael@0: } michael@0: michael@0: nsIntSize michael@0: nsWindow::GetSafeWindowSize(nsIntSize aSize) michael@0: { michael@0: // The X protocol uses CARD32 for window sizes, but the server (1.11.3) michael@0: // reads it as CARD16. Sizes of pixmaps, used for drawing, are (unsigned) michael@0: // CARD16 in the protocol, but the server's ProcCreatePixmap returns michael@0: // BadAlloc if dimensions cannot be represented by signed shorts. michael@0: nsIntSize result = aSize; michael@0: const int32_t kInt16Max = 32767; michael@0: if (result.width > kInt16Max) { michael@0: result.width = kInt16Max; michael@0: } michael@0: if (result.height > kInt16Max) { michael@0: result.height = kInt16Max; michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: void michael@0: nsWindow::EnsureGrabs(void) michael@0: { michael@0: if (mRetryPointerGrab) michael@0: GrabPointer(sRetryGrabTime); michael@0: } michael@0: michael@0: void michael@0: nsWindow::CleanLayerManagerRecursive(void) { michael@0: if (mLayerManager) { michael@0: mLayerManager->Destroy(); michael@0: mLayerManager = nullptr; michael@0: } michael@0: michael@0: DestroyCompositor(); michael@0: michael@0: GList* children = gdk_window_peek_children(mGdkWindow); michael@0: for (GList* list = children; list; list = list->next) { michael@0: nsWindow* window = get_window_for_gdk_window(GDK_WINDOW(list->data)); michael@0: if (window) { michael@0: window->CleanLayerManagerRecursive(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsWindow::SetTransparencyMode(nsTransparencyMode aMode) michael@0: { michael@0: if (!mShell) { michael@0: // Pass the request to the toplevel window michael@0: GtkWidget *topWidget = GetToplevelWidget(); michael@0: if (!topWidget) michael@0: return; michael@0: michael@0: nsWindow *topWindow = get_window_for_gtk_widget(topWidget); michael@0: if (!topWindow) michael@0: return; michael@0: michael@0: topWindow->SetTransparencyMode(aMode); michael@0: return; michael@0: } michael@0: bool isTransparent = aMode == eTransparencyTransparent; michael@0: michael@0: if (mIsTransparent == isTransparent) michael@0: return; michael@0: michael@0: if (!isTransparent) { michael@0: ClearTransparencyBitmap(); michael@0: } // else the new default alpha values are "all 1", so we don't michael@0: // need to change anything yet michael@0: michael@0: mIsTransparent = isTransparent; michael@0: michael@0: // Need to clean our LayerManager up while still alive because michael@0: // we don't want to use layers acceleration on shaped windows michael@0: CleanLayerManagerRecursive(); michael@0: } michael@0: michael@0: nsTransparencyMode michael@0: nsWindow::GetTransparencyMode() michael@0: { michael@0: if (!mShell) { michael@0: // Pass the request to the toplevel window michael@0: GtkWidget *topWidget = GetToplevelWidget(); michael@0: if (!topWidget) { michael@0: return eTransparencyOpaque; michael@0: } michael@0: michael@0: nsWindow *topWindow = get_window_for_gtk_widget(topWidget); michael@0: if (!topWindow) { michael@0: return eTransparencyOpaque; michael@0: } michael@0: michael@0: return topWindow->GetTransparencyMode(); michael@0: } michael@0: michael@0: return mIsTransparent ? eTransparencyTransparent : eTransparencyOpaque; michael@0: } michael@0: michael@0: nsresult michael@0: nsWindow::ConfigureChildren(const nsTArray& aConfigurations) michael@0: { michael@0: for (uint32_t i = 0; i < aConfigurations.Length(); ++i) { michael@0: const Configuration& configuration = aConfigurations[i]; michael@0: nsWindow* w = static_cast(configuration.mChild); michael@0: NS_ASSERTION(w->GetParent() == this, michael@0: "Configured widget is not a child"); michael@0: w->SetWindowClipRegion(configuration.mClipRegion, true); michael@0: if (w->mBounds.Size() != configuration.mBounds.Size()) { michael@0: w->Resize(configuration.mBounds.x, configuration.mBounds.y, michael@0: configuration.mBounds.width, configuration.mBounds.height, michael@0: true); michael@0: } else if (w->mBounds.TopLeft() != configuration.mBounds.TopLeft()) { michael@0: w->Move(configuration.mBounds.x, configuration.mBounds.y); michael@0: } michael@0: w->SetWindowClipRegion(configuration.mClipRegion, false); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: static pixman_box32 michael@0: ToPixmanBox(const nsIntRect& aRect) michael@0: { michael@0: pixman_box32_t result; michael@0: result.x1 = aRect.x; michael@0: result.y1 = aRect.y; michael@0: result.x2 = aRect.XMost(); michael@0: result.y2 = aRect.YMost(); michael@0: return result; michael@0: } michael@0: michael@0: static nsIntRect michael@0: ToIntRect(const pixman_box32& aBox) michael@0: { michael@0: nsIntRect result; michael@0: result.x = aBox.x1; michael@0: result.y = aBox.y1; michael@0: result.width = aBox.x2 - aBox.x1; michael@0: result.height = aBox.y2 - aBox.y1; michael@0: return result; michael@0: } michael@0: michael@0: static void michael@0: InitRegion(pixman_region32* aRegion, michael@0: const nsTArray& aRects) michael@0: { michael@0: nsAutoTArray rects; michael@0: rects.SetCapacity(aRects.Length()); michael@0: for (uint32_t i = 0; i < aRects.Length (); ++i) { michael@0: if (!aRects[i].IsEmpty()) { michael@0: rects.AppendElement(ToPixmanBox(aRects[i])); michael@0: } michael@0: } michael@0: michael@0: pixman_region32_init_rects(aRegion, michael@0: rects.Elements(), rects.Length()); michael@0: } michael@0: michael@0: static void michael@0: GetIntRects(pixman_region32& aRegion, nsTArray* aRects) michael@0: { michael@0: int nRects; michael@0: pixman_box32* boxes = pixman_region32_rectangles(&aRegion, &nRects); michael@0: aRects->SetCapacity(aRects->Length() + nRects); michael@0: for (int i = 0; i < nRects; ++i) { michael@0: aRects->AppendElement(ToIntRect(boxes[i])); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsWindow::SetWindowClipRegion(const nsTArray& aRects, michael@0: bool aIntersectWithExisting) michael@0: { michael@0: const nsTArray* newRects = &aRects; michael@0: michael@0: nsAutoTArray intersectRects; michael@0: if (aIntersectWithExisting) { michael@0: nsAutoTArray existingRects; michael@0: GetWindowClipRegion(&existingRects); michael@0: michael@0: nsAutoRef existingRegion; michael@0: InitRegion(&existingRegion, existingRects); michael@0: nsAutoRef newRegion; michael@0: InitRegion(&newRegion, aRects); michael@0: nsAutoRef intersectRegion; michael@0: pixman_region32_init(&intersectRegion); michael@0: pixman_region32_intersect(&intersectRegion, michael@0: &newRegion, &existingRegion); michael@0: michael@0: // If mClipRects is null we haven't set a clip rect yet, so we michael@0: // need to set the clip even if it is equal. michael@0: if (mClipRects && michael@0: pixman_region32_equal(&intersectRegion, &existingRegion)) { michael@0: return; michael@0: } michael@0: michael@0: if (!pixman_region32_equal(&intersectRegion, &newRegion)) { michael@0: GetIntRects(intersectRegion, &intersectRects); michael@0: newRects = &intersectRects; michael@0: } michael@0: } michael@0: michael@0: if (!StoreWindowClipRegion(*newRects)) michael@0: return; michael@0: michael@0: if (!mGdkWindow) michael@0: return; michael@0: michael@0: #if (MOZ_WIDGET_GTK == 2) michael@0: GdkRegion *region = gdk_region_new(); // aborts on OOM michael@0: for (uint32_t i = 0; i < newRects->Length(); ++i) { michael@0: const nsIntRect& r = newRects->ElementAt(i); michael@0: GdkRectangle rect = { r.x, r.y, r.width, r.height }; michael@0: gdk_region_union_with_rect(region, &rect); michael@0: } michael@0: michael@0: gdk_window_shape_combine_region(mGdkWindow, region, 0, 0); michael@0: gdk_region_destroy(region); michael@0: #else michael@0: cairo_region_t *region = cairo_region_create(); michael@0: for (uint32_t i = 0; i < newRects->Length(); ++i) { michael@0: const nsIntRect& r = newRects->ElementAt(i); michael@0: cairo_rectangle_int_t rect = { r.x, r.y, r.width, r.height }; michael@0: cairo_region_union_rectangle(region, &rect); michael@0: } michael@0: michael@0: gdk_window_shape_combine_region(mGdkWindow, region, 0, 0); michael@0: cairo_region_destroy(region); michael@0: #endif michael@0: michael@0: return; michael@0: } michael@0: michael@0: void michael@0: nsWindow::ResizeTransparencyBitmap() michael@0: { michael@0: if (!mTransparencyBitmap) michael@0: return; michael@0: michael@0: if (mBounds.width == mTransparencyBitmapWidth && michael@0: mBounds.height == mTransparencyBitmapHeight) michael@0: return; michael@0: michael@0: int32_t newRowBytes = GetBitmapStride(mBounds.width); michael@0: int32_t newSize = newRowBytes * mBounds.height; michael@0: gchar* newBits = new gchar[newSize]; michael@0: // fill new mask with "transparent", first michael@0: memset(newBits, 0, newSize); michael@0: michael@0: // Now copy the intersection of the old and new areas into the new mask michael@0: int32_t copyWidth = std::min(mBounds.width, mTransparencyBitmapWidth); michael@0: int32_t copyHeight = std::min(mBounds.height, mTransparencyBitmapHeight); michael@0: int32_t oldRowBytes = GetBitmapStride(mTransparencyBitmapWidth); michael@0: int32_t copyBytes = GetBitmapStride(copyWidth); michael@0: michael@0: int32_t i; michael@0: gchar* fromPtr = mTransparencyBitmap; michael@0: gchar* toPtr = newBits; michael@0: for (i = 0; i < copyHeight; i++) { michael@0: memcpy(toPtr, fromPtr, copyBytes); michael@0: fromPtr += oldRowBytes; michael@0: toPtr += newRowBytes; michael@0: } michael@0: michael@0: delete[] mTransparencyBitmap; michael@0: mTransparencyBitmap = newBits; michael@0: mTransparencyBitmapWidth = mBounds.width; michael@0: mTransparencyBitmapHeight = mBounds.height; michael@0: } michael@0: michael@0: static bool michael@0: ChangedMaskBits(gchar* aMaskBits, int32_t aMaskWidth, int32_t aMaskHeight, michael@0: const nsIntRect& aRect, uint8_t* aAlphas, int32_t aStride) michael@0: { michael@0: int32_t x, y, xMax = aRect.XMost(), yMax = aRect.YMost(); michael@0: int32_t maskBytesPerRow = GetBitmapStride(aMaskWidth); michael@0: for (y = aRect.y; y < yMax; y++) { michael@0: gchar* maskBytes = aMaskBits + y*maskBytesPerRow; michael@0: uint8_t* alphas = aAlphas; michael@0: for (x = aRect.x; x < xMax; x++) { michael@0: bool newBit = *alphas > 0x7f; michael@0: alphas++; michael@0: michael@0: gchar maskByte = maskBytes[x >> 3]; michael@0: bool maskBit = (maskByte & (1 << (x & 7))) != 0; michael@0: michael@0: if (maskBit != newBit) { michael@0: return true; michael@0: } michael@0: } michael@0: aAlphas += aStride; michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: static michael@0: void UpdateMaskBits(gchar* aMaskBits, int32_t aMaskWidth, int32_t aMaskHeight, michael@0: const nsIntRect& aRect, uint8_t* aAlphas, int32_t aStride) michael@0: { michael@0: int32_t x, y, xMax = aRect.XMost(), yMax = aRect.YMost(); michael@0: int32_t maskBytesPerRow = GetBitmapStride(aMaskWidth); michael@0: for (y = aRect.y; y < yMax; y++) { michael@0: gchar* maskBytes = aMaskBits + y*maskBytesPerRow; michael@0: uint8_t* alphas = aAlphas; michael@0: for (x = aRect.x; x < xMax; x++) { michael@0: bool newBit = *alphas > 0x7f; michael@0: alphas++; michael@0: michael@0: gchar mask = 1 << (x & 7); michael@0: gchar maskByte = maskBytes[x >> 3]; michael@0: // Note: '-newBit' turns 0 into 00...00 and 1 into 11...11 michael@0: maskBytes[x >> 3] = (maskByte & ~mask) | (-newBit & mask); michael@0: } michael@0: aAlphas += aStride; michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsWindow::ApplyTransparencyBitmap() michael@0: { michael@0: #ifdef MOZ_X11 michael@0: // We use X11 calls where possible, because GDK handles expose events michael@0: // for shaped windows in a way that's incompatible with us (Bug 635903). michael@0: // It doesn't occur when the shapes are set through X. michael@0: GdkWindow *shellWindow = gtk_widget_get_window(mShell); michael@0: Display* xDisplay = GDK_WINDOW_XDISPLAY(shellWindow); michael@0: Window xDrawable = GDK_WINDOW_XID(shellWindow); michael@0: Pixmap maskPixmap = XCreateBitmapFromData(xDisplay, michael@0: xDrawable, michael@0: mTransparencyBitmap, michael@0: mTransparencyBitmapWidth, michael@0: mTransparencyBitmapHeight); michael@0: XShapeCombineMask(xDisplay, xDrawable, michael@0: ShapeBounding, 0, 0, michael@0: maskPixmap, ShapeSet); michael@0: XFreePixmap(xDisplay, maskPixmap); michael@0: #else michael@0: #if (MOZ_WIDGET_GTK == 2) michael@0: gtk_widget_reset_shapes(mShell); michael@0: GdkBitmap* maskBitmap = gdk_bitmap_create_from_data(gtk_widget_get_window(mShell), michael@0: mTransparencyBitmap, michael@0: mTransparencyBitmapWidth, mTransparencyBitmapHeight); michael@0: if (!maskBitmap) michael@0: return; michael@0: michael@0: gtk_widget_shape_combine_mask(mShell, maskBitmap, 0, 0); michael@0: g_object_unref(maskBitmap); michael@0: #else michael@0: cairo_surface_t *maskBitmap; michael@0: maskBitmap = cairo_image_surface_create_for_data((unsigned char*)mTransparencyBitmap, michael@0: CAIRO_FORMAT_A1, michael@0: mTransparencyBitmapWidth, michael@0: mTransparencyBitmapHeight, michael@0: GetBitmapStride(mTransparencyBitmapWidth)); michael@0: if (!maskBitmap) michael@0: return; michael@0: michael@0: cairo_region_t * maskRegion = gdk_cairo_region_create_from_surface(maskBitmap); michael@0: gtk_widget_shape_combine_region(mShell, maskRegion); michael@0: cairo_region_destroy(maskRegion); michael@0: cairo_surface_destroy(maskBitmap); michael@0: #endif // MOZ_WIDGET_GTK2 michael@0: #endif // MOZ_X11 michael@0: } michael@0: michael@0: void michael@0: nsWindow::ClearTransparencyBitmap() michael@0: { michael@0: if (!mTransparencyBitmap) michael@0: return; michael@0: michael@0: delete[] mTransparencyBitmap; michael@0: mTransparencyBitmap = nullptr; michael@0: mTransparencyBitmapWidth = 0; michael@0: mTransparencyBitmapHeight = 0; michael@0: michael@0: if (!mShell) michael@0: return; michael@0: michael@0: #ifdef MOZ_X11 michael@0: GdkWindow *window = gtk_widget_get_window(mShell); michael@0: if (!window) michael@0: return; michael@0: michael@0: Display* xDisplay = GDK_WINDOW_XDISPLAY(window); michael@0: Window xWindow = gdk_x11_window_get_xid(window); michael@0: michael@0: XShapeCombineMask(xDisplay, xWindow, ShapeBounding, 0, 0, None, ShapeSet); michael@0: #endif michael@0: } michael@0: michael@0: nsresult michael@0: nsWindow::UpdateTranslucentWindowAlphaInternal(const nsIntRect& aRect, michael@0: uint8_t* aAlphas, int32_t aStride) michael@0: { michael@0: if (!mShell) { michael@0: // Pass the request to the toplevel window michael@0: GtkWidget *topWidget = GetToplevelWidget(); michael@0: if (!topWidget) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nsWindow *topWindow = get_window_for_gtk_widget(topWidget); michael@0: if (!topWindow) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: return topWindow->UpdateTranslucentWindowAlphaInternal(aRect, aAlphas, aStride); michael@0: } michael@0: michael@0: NS_ASSERTION(mIsTransparent, "Window is not transparent"); michael@0: michael@0: if (mTransparencyBitmap == nullptr) { michael@0: int32_t size = GetBitmapStride(mBounds.width)*mBounds.height; michael@0: mTransparencyBitmap = new gchar[size]; michael@0: memset(mTransparencyBitmap, 255, size); michael@0: mTransparencyBitmapWidth = mBounds.width; michael@0: mTransparencyBitmapHeight = mBounds.height; michael@0: } else { michael@0: ResizeTransparencyBitmap(); michael@0: } michael@0: michael@0: nsIntRect rect; michael@0: rect.IntersectRect(aRect, nsIntRect(0, 0, mBounds.width, mBounds.height)); michael@0: michael@0: if (!ChangedMaskBits(mTransparencyBitmap, mBounds.width, mBounds.height, michael@0: rect, aAlphas, aStride)) michael@0: // skip the expensive stuff if the mask bits haven't changed; hopefully michael@0: // this is the common case michael@0: return NS_OK; michael@0: michael@0: UpdateMaskBits(mTransparencyBitmap, mBounds.width, mBounds.height, michael@0: rect, aAlphas, aStride); michael@0: michael@0: if (!mNeedsShow) { michael@0: ApplyTransparencyBitmap(); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsWindow::GrabPointer(guint32 aTime) michael@0: { michael@0: LOG(("GrabPointer time=0x%08x retry=%d\n", michael@0: (unsigned int)aTime, mRetryPointerGrab)); michael@0: michael@0: mRetryPointerGrab = false; michael@0: sRetryGrabTime = aTime; michael@0: michael@0: // If the window isn't visible, just set the flag to retry the michael@0: // grab. When this window becomes visible, the grab will be michael@0: // retried. michael@0: if (!mHasMappedToplevel || mIsFullyObscured) { michael@0: LOG(("GrabPointer: window not visible\n")); michael@0: mRetryPointerGrab = true; michael@0: return; michael@0: } michael@0: michael@0: if (!mGdkWindow) michael@0: return; michael@0: michael@0: gint retval; michael@0: retval = gdk_pointer_grab(mGdkWindow, TRUE, michael@0: (GdkEventMask)(GDK_BUTTON_PRESS_MASK | michael@0: GDK_BUTTON_RELEASE_MASK | michael@0: GDK_ENTER_NOTIFY_MASK | michael@0: GDK_LEAVE_NOTIFY_MASK | michael@0: GDK_POINTER_MOTION_MASK), michael@0: (GdkWindow *)nullptr, nullptr, aTime); michael@0: michael@0: if (retval == GDK_GRAB_NOT_VIEWABLE) { michael@0: LOG(("GrabPointer: window not viewable; will retry\n")); michael@0: mRetryPointerGrab = true; michael@0: } else if (retval != GDK_GRAB_SUCCESS) { michael@0: LOG(("GrabPointer: pointer grab failed: %i\n", retval)); michael@0: // A failed grab indicates that another app has grabbed the pointer. michael@0: // Check for rollup now, because, without the grab, we likely won't michael@0: // get subsequent button press events. michael@0: CheckForRollup(0, 0, false, true); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsWindow::ReleaseGrabs(void) michael@0: { michael@0: LOG(("ReleaseGrabs\n")); michael@0: michael@0: mRetryPointerGrab = false; michael@0: gdk_pointer_ungrab(GDK_CURRENT_TIME); michael@0: } michael@0: michael@0: GtkWidget * michael@0: nsWindow::GetToplevelWidget() michael@0: { michael@0: if (mShell) { michael@0: return mShell; michael@0: } michael@0: michael@0: GtkWidget *widget = GetMozContainerWidget(); michael@0: if (!widget) michael@0: return nullptr; michael@0: michael@0: return gtk_widget_get_toplevel(widget); michael@0: } michael@0: michael@0: GtkWidget * michael@0: nsWindow::GetMozContainerWidget() michael@0: { michael@0: if (!mGdkWindow) michael@0: return nullptr; michael@0: michael@0: if (mContainer) michael@0: return GTK_WIDGET(mContainer); michael@0: michael@0: GtkWidget *owningWidget = michael@0: get_gtk_widget_for_gdk_window(mGdkWindow); michael@0: return owningWidget; michael@0: } michael@0: michael@0: nsWindow * michael@0: nsWindow::GetContainerWindow() michael@0: { michael@0: GtkWidget *owningWidget = GetMozContainerWidget(); michael@0: if (!owningWidget) michael@0: return nullptr; michael@0: michael@0: nsWindow *window = get_window_for_gtk_widget(owningWidget); michael@0: NS_ASSERTION(window, "No nsWindow for container widget"); michael@0: return window; michael@0: } michael@0: michael@0: void michael@0: nsWindow::SetUrgencyHint(GtkWidget *top_window, bool state) michael@0: { michael@0: if (!top_window) michael@0: return; michael@0: michael@0: gdk_window_set_urgency_hint(gtk_widget_get_window(top_window), state); michael@0: } michael@0: michael@0: void * michael@0: nsWindow::SetupPluginPort(void) michael@0: { michael@0: if (!mGdkWindow) michael@0: return nullptr; michael@0: michael@0: if (gdk_window_is_destroyed(mGdkWindow) == TRUE) michael@0: return nullptr; michael@0: michael@0: Window window = gdk_x11_window_get_xid(mGdkWindow); michael@0: michael@0: // we have to flush the X queue here so that any plugins that michael@0: // might be running on separate X connections will be able to use michael@0: // this window in case it was just created michael@0: #ifdef MOZ_X11 michael@0: XWindowAttributes xattrs; michael@0: Display *display = GDK_DISPLAY_XDISPLAY(gdk_display_get_default()); michael@0: XGetWindowAttributes(display, window, &xattrs); michael@0: XSelectInput (display, window, michael@0: xattrs.your_event_mask | michael@0: SubstructureNotifyMask); michael@0: michael@0: gdk_window_add_filter(mGdkWindow, plugin_window_filter_func, this); michael@0: michael@0: XSync(display, False); michael@0: #endif /* MOZ_X11 */ michael@0: michael@0: return (void *)window; michael@0: } michael@0: michael@0: void michael@0: nsWindow::SetDefaultIcon(void) michael@0: { michael@0: SetIcon(NS_LITERAL_STRING("default")); michael@0: } michael@0: michael@0: void michael@0: nsWindow::SetPluginType(PluginType aPluginType) michael@0: { michael@0: mPluginType = aPluginType; michael@0: } michael@0: michael@0: #ifdef MOZ_X11 michael@0: void michael@0: nsWindow::SetNonXEmbedPluginFocus() michael@0: { michael@0: if (gPluginFocusWindow == this || mPluginType!=PluginType_NONXEMBED) { michael@0: return; michael@0: } michael@0: michael@0: if (gPluginFocusWindow) { michael@0: nsRefPtr kungFuDeathGrip = gPluginFocusWindow; michael@0: gPluginFocusWindow->LoseNonXEmbedPluginFocus(); michael@0: } michael@0: michael@0: LOGFOCUS(("nsWindow::SetNonXEmbedPluginFocus\n")); michael@0: michael@0: Window curFocusWindow; michael@0: int focusState; michael@0: michael@0: GdkDisplay *gdkDisplay = gdk_window_get_display(mGdkWindow); michael@0: XGetInputFocus(gdk_x11_display_get_xdisplay(gdkDisplay), michael@0: &curFocusWindow, michael@0: &focusState); michael@0: michael@0: LOGFOCUS(("\t curFocusWindow=%p\n", curFocusWindow)); michael@0: michael@0: GdkWindow* toplevel = gdk_window_get_toplevel(mGdkWindow); michael@0: #if (MOZ_WIDGET_GTK == 2) michael@0: GdkWindow *gdkfocuswin = gdk_window_lookup(curFocusWindow); michael@0: #else michael@0: GdkWindow *gdkfocuswin = gdk_x11_window_lookup_for_display(gdkDisplay, michael@0: curFocusWindow); michael@0: #endif michael@0: michael@0: // lookup with the focus proxy window is supposed to get the michael@0: // same GdkWindow as toplevel. If the current focused window michael@0: // is not the focus proxy, we return without any change. michael@0: if (gdkfocuswin != toplevel) { michael@0: return; michael@0: } michael@0: michael@0: // switch the focus from the focus proxy to the plugin window michael@0: mOldFocusWindow = curFocusWindow; michael@0: XRaiseWindow(GDK_WINDOW_XDISPLAY(mGdkWindow), michael@0: gdk_x11_window_get_xid(mGdkWindow)); michael@0: gdk_error_trap_push(); michael@0: XSetInputFocus(GDK_WINDOW_XDISPLAY(mGdkWindow), michael@0: gdk_x11_window_get_xid(mGdkWindow), michael@0: RevertToNone, michael@0: CurrentTime); michael@0: gdk_flush(); michael@0: gdk_error_trap_pop(); michael@0: gPluginFocusWindow = this; michael@0: gdk_window_add_filter(nullptr, plugin_client_message_filter, this); michael@0: michael@0: LOGFOCUS(("nsWindow::SetNonXEmbedPluginFocus oldfocus=%p new=%p\n", michael@0: mOldFocusWindow, gdk_x11_window_get_xid(mGdkWindow))); michael@0: } michael@0: michael@0: void michael@0: nsWindow::LoseNonXEmbedPluginFocus() michael@0: { michael@0: LOGFOCUS(("nsWindow::LoseNonXEmbedPluginFocus\n")); michael@0: michael@0: // This method is only for the nsWindow which contains a michael@0: // Non-XEmbed plugin, for example, JAVA plugin. michael@0: if (gPluginFocusWindow != this || mPluginType!=PluginType_NONXEMBED) { michael@0: return; michael@0: } michael@0: michael@0: Window curFocusWindow; michael@0: int focusState; michael@0: michael@0: XGetInputFocus(GDK_WINDOW_XDISPLAY(mGdkWindow), michael@0: &curFocusWindow, michael@0: &focusState); michael@0: michael@0: // we only switch focus between plugin window and focus proxy. If the michael@0: // current focused window is not the plugin window, just removing the michael@0: // event filter that blocks the WM_TAKE_FOCUS is enough. WM and gtk2 michael@0: // will take care of the focus later. michael@0: if (!curFocusWindow || michael@0: curFocusWindow == gdk_x11_window_get_xid(mGdkWindow)) { michael@0: michael@0: gdk_error_trap_push(); michael@0: XRaiseWindow(GDK_WINDOW_XDISPLAY(mGdkWindow), michael@0: mOldFocusWindow); michael@0: XSetInputFocus(GDK_WINDOW_XDISPLAY(mGdkWindow), michael@0: mOldFocusWindow, michael@0: RevertToParent, michael@0: CurrentTime); michael@0: gdk_flush(); michael@0: gdk_error_trap_pop(); michael@0: } michael@0: gPluginFocusWindow = nullptr; michael@0: mOldFocusWindow = 0; michael@0: gdk_window_remove_filter(nullptr, plugin_client_message_filter, this); michael@0: michael@0: LOGFOCUS(("nsWindow::LoseNonXEmbedPluginFocus end\n")); michael@0: } michael@0: #endif /* MOZ_X11 */ michael@0: michael@0: gint michael@0: nsWindow::ConvertBorderStyles(nsBorderStyle aStyle) michael@0: { michael@0: gint w = 0; michael@0: michael@0: if (aStyle == eBorderStyle_default) michael@0: return -1; michael@0: michael@0: // note that we don't handle eBorderStyle_close yet michael@0: if (aStyle & eBorderStyle_all) michael@0: w |= GDK_DECOR_ALL; michael@0: if (aStyle & eBorderStyle_border) michael@0: w |= GDK_DECOR_BORDER; michael@0: if (aStyle & eBorderStyle_resizeh) michael@0: w |= GDK_DECOR_RESIZEH; michael@0: if (aStyle & eBorderStyle_title) michael@0: w |= GDK_DECOR_TITLE; michael@0: if (aStyle & eBorderStyle_menu) michael@0: w |= GDK_DECOR_MENU; michael@0: if (aStyle & eBorderStyle_minimize) michael@0: w |= GDK_DECOR_MINIMIZE; michael@0: if (aStyle & eBorderStyle_maximize) michael@0: w |= GDK_DECOR_MAXIMIZE; michael@0: michael@0: return w; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindow::MakeFullScreen(bool aFullScreen) michael@0: { michael@0: LOG(("nsWindow::MakeFullScreen [%p] aFullScreen %d\n", michael@0: (void *)this, aFullScreen)); michael@0: michael@0: if (aFullScreen) { michael@0: if (mSizeMode != nsSizeMode_Fullscreen) michael@0: mLastSizeMode = mSizeMode; michael@0: michael@0: mSizeMode = nsSizeMode_Fullscreen; michael@0: gtk_window_fullscreen(GTK_WINDOW(mShell)); michael@0: } michael@0: else { michael@0: mSizeMode = mLastSizeMode; michael@0: gtk_window_unfullscreen(GTK_WINDOW(mShell)); michael@0: } michael@0: michael@0: NS_ASSERTION(mLastSizeMode != nsSizeMode_Fullscreen, michael@0: "mLastSizeMode should never be fullscreen"); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindow::HideWindowChrome(bool aShouldHide) michael@0: { michael@0: if (!mShell) { michael@0: // Pass the request to the toplevel window michael@0: GtkWidget *topWidget = GetToplevelWidget(); michael@0: if (!topWidget) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nsWindow *topWindow = get_window_for_gtk_widget(topWidget); michael@0: if (!topWindow) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: return topWindow->HideWindowChrome(aShouldHide); michael@0: } michael@0: michael@0: // Sawfish, metacity, and presumably other window managers get michael@0: // confused if we change the window decorations while the window michael@0: // is visible. michael@0: bool wasVisible = false; michael@0: GdkWindow *shellWindow = gtk_widget_get_window(mShell); michael@0: if (gdk_window_is_visible(shellWindow)) { michael@0: gdk_window_hide(shellWindow); michael@0: wasVisible = true; michael@0: } michael@0: michael@0: gint wmd; michael@0: if (aShouldHide) michael@0: wmd = 0; michael@0: else michael@0: wmd = ConvertBorderStyles(mBorderStyle); michael@0: michael@0: if (wmd != -1) michael@0: gdk_window_set_decorations(shellWindow, (GdkWMDecoration) wmd); michael@0: michael@0: if (wasVisible) michael@0: gdk_window_show(shellWindow); michael@0: michael@0: // For some window managers, adding or removing window decorations michael@0: // requires unmapping and remapping our toplevel window. Go ahead michael@0: // and flush the queue here so that we don't end up with a BadWindow michael@0: // error later when this happens (when the persistence timer fires michael@0: // and GetWindowPos is called) michael@0: #ifdef MOZ_X11 michael@0: XSync(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()) , False); michael@0: #else michael@0: gdk_flush (); michael@0: #endif /* MOZ_X11 */ michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: bool michael@0: nsWindow::CheckForRollup(gdouble aMouseX, gdouble aMouseY, michael@0: bool aIsWheel, bool aAlwaysRollup) michael@0: { michael@0: nsIRollupListener* rollupListener = GetActiveRollupListener(); michael@0: nsCOMPtr rollupWidget; michael@0: if (rollupListener) { michael@0: rollupWidget = rollupListener->GetRollupWidget(); michael@0: } michael@0: if (!rollupWidget) { michael@0: nsBaseWidget::gRollupListener = nullptr; michael@0: return false; michael@0: } michael@0: michael@0: bool retVal = false; michael@0: GdkWindow *currentPopup = michael@0: (GdkWindow *)rollupWidget->GetNativeData(NS_NATIVE_WINDOW); michael@0: if (aAlwaysRollup || !is_mouse_in_window(currentPopup, aMouseX, aMouseY)) { michael@0: bool rollup = true; michael@0: if (aIsWheel) { michael@0: rollup = rollupListener->ShouldRollupOnMouseWheelEvent(); michael@0: retVal = rollupListener->ShouldConsumeOnMouseWheelEvent(); michael@0: } michael@0: // if we're dealing with menus, we probably have submenus and michael@0: // we don't want to rollup if the click is in a parent menu of michael@0: // the current submenu michael@0: uint32_t popupsToRollup = UINT32_MAX; michael@0: if (!aAlwaysRollup) { michael@0: nsAutoTArray widgetChain; michael@0: uint32_t sameTypeCount = rollupListener->GetSubmenuWidgetChain(&widgetChain); michael@0: for (uint32_t i=0; iGetNativeData(NS_NATIVE_WINDOW); michael@0: if (is_mouse_in_window(currWindow, aMouseX, aMouseY)) { michael@0: // don't roll up if the mouse event occurred within a michael@0: // menu of the same type. If the mouse event occurred michael@0: // in a menu higher than that, roll up, but pass the michael@0: // number of popups to Rollup so that only those of the michael@0: // same type close up. michael@0: if (i < sameTypeCount) { michael@0: rollup = false; michael@0: } michael@0: else { michael@0: popupsToRollup = sameTypeCount; michael@0: } michael@0: break; michael@0: } michael@0: } // foreach parent menu widget michael@0: } // if rollup listener knows about menus michael@0: michael@0: // if we've determined that we should still rollup, do it. michael@0: bool usePoint = !aIsWheel && !aAlwaysRollup; michael@0: nsIntPoint point(aMouseX, aMouseY); michael@0: if (rollup && rollupListener->Rollup(popupsToRollup, usePoint ? &point : nullptr, nullptr)) { michael@0: retVal = true; michael@0: } michael@0: } michael@0: return retVal; michael@0: } michael@0: michael@0: /* static */ michael@0: bool michael@0: nsWindow::DragInProgress(void) michael@0: { michael@0: nsCOMPtr dragService = do_GetService(kCDragServiceCID); michael@0: michael@0: if (!dragService) michael@0: return false; michael@0: michael@0: nsCOMPtr currentDragSession; michael@0: dragService->GetCurrentSession(getter_AddRefs(currentDragSession)); michael@0: michael@0: return currentDragSession != nullptr; michael@0: } michael@0: michael@0: static bool michael@0: is_mouse_in_window (GdkWindow* aWindow, gdouble aMouseX, gdouble aMouseY) michael@0: { michael@0: gint x = 0; michael@0: gint y = 0; michael@0: gint w, h; michael@0: michael@0: gint offsetX = 0; michael@0: gint offsetY = 0; michael@0: michael@0: GdkWindow *window = aWindow; michael@0: michael@0: while (window) { michael@0: gint tmpX = 0; michael@0: gint tmpY = 0; michael@0: michael@0: gdk_window_get_position(window, &tmpX, &tmpY); michael@0: GtkWidget *widget = get_gtk_widget_for_gdk_window(window); michael@0: michael@0: // if this is a window, compute x and y given its origin and our michael@0: // offset michael@0: if (GTK_IS_WINDOW(widget)) { michael@0: x = tmpX + offsetX; michael@0: y = tmpY + offsetY; michael@0: break; michael@0: } michael@0: michael@0: offsetX += tmpX; michael@0: offsetY += tmpY; michael@0: window = gdk_window_get_parent(window); michael@0: } michael@0: michael@0: #if (MOZ_WIDGET_GTK == 2) michael@0: gdk_drawable_get_size(aWindow, &w, &h); michael@0: #else michael@0: w = gdk_window_get_width(aWindow); michael@0: h = gdk_window_get_height(aWindow); michael@0: #endif michael@0: michael@0: if (aMouseX > x && aMouseX < x + w && michael@0: aMouseY > y && aMouseY < y + h) michael@0: return true; michael@0: michael@0: return false; michael@0: } michael@0: michael@0: static nsWindow * michael@0: get_window_for_gtk_widget(GtkWidget *widget) michael@0: { michael@0: gpointer user_data = g_object_get_data(G_OBJECT(widget), "nsWindow"); michael@0: michael@0: return static_cast(user_data); michael@0: } michael@0: michael@0: static nsWindow * michael@0: get_window_for_gdk_window(GdkWindow *window) michael@0: { michael@0: gpointer user_data = g_object_get_data(G_OBJECT(window), "nsWindow"); michael@0: michael@0: return static_cast(user_data); michael@0: } michael@0: michael@0: static GtkWidget * michael@0: get_gtk_widget_for_gdk_window(GdkWindow *window) michael@0: { michael@0: gpointer user_data = nullptr; michael@0: gdk_window_get_user_data(window, &user_data); michael@0: michael@0: return GTK_WIDGET(user_data); michael@0: } michael@0: michael@0: static GdkCursor * michael@0: get_gtk_cursor(nsCursor aCursor) michael@0: { michael@0: GdkCursor *gdkcursor = nullptr; michael@0: uint8_t newType = 0xff; michael@0: michael@0: if ((gdkcursor = gCursorCache[aCursor])) { michael@0: return gdkcursor; michael@0: } michael@0: michael@0: GdkDisplay *defaultDisplay = gdk_display_get_default(); michael@0: michael@0: // The strategy here is to use standard GDK cursors, and, if not available, michael@0: // load by standard name with gdk_cursor_new_from_name. michael@0: // Spec is here: http://www.freedesktop.org/wiki/Specifications/cursor-spec/ michael@0: switch (aCursor) { michael@0: case eCursor_standard: michael@0: gdkcursor = gdk_cursor_new(GDK_LEFT_PTR); michael@0: break; michael@0: case eCursor_wait: michael@0: gdkcursor = gdk_cursor_new(GDK_WATCH); michael@0: break; michael@0: case eCursor_select: michael@0: gdkcursor = gdk_cursor_new(GDK_XTERM); michael@0: break; michael@0: case eCursor_hyperlink: michael@0: gdkcursor = gdk_cursor_new(GDK_HAND2); michael@0: break; michael@0: case eCursor_n_resize: michael@0: gdkcursor = gdk_cursor_new(GDK_TOP_SIDE); michael@0: break; michael@0: case eCursor_s_resize: michael@0: gdkcursor = gdk_cursor_new(GDK_BOTTOM_SIDE); michael@0: break; michael@0: case eCursor_w_resize: michael@0: gdkcursor = gdk_cursor_new(GDK_LEFT_SIDE); michael@0: break; michael@0: case eCursor_e_resize: michael@0: gdkcursor = gdk_cursor_new(GDK_RIGHT_SIDE); michael@0: break; michael@0: case eCursor_nw_resize: michael@0: gdkcursor = gdk_cursor_new(GDK_TOP_LEFT_CORNER); michael@0: break; michael@0: case eCursor_se_resize: michael@0: gdkcursor = gdk_cursor_new(GDK_BOTTOM_RIGHT_CORNER); michael@0: break; michael@0: case eCursor_ne_resize: michael@0: gdkcursor = gdk_cursor_new(GDK_TOP_RIGHT_CORNER); michael@0: break; michael@0: case eCursor_sw_resize: michael@0: gdkcursor = gdk_cursor_new(GDK_BOTTOM_LEFT_CORNER); michael@0: break; michael@0: case eCursor_crosshair: michael@0: gdkcursor = gdk_cursor_new(GDK_CROSSHAIR); michael@0: break; michael@0: case eCursor_move: michael@0: gdkcursor = gdk_cursor_new(GDK_FLEUR); michael@0: break; michael@0: case eCursor_help: michael@0: gdkcursor = gdk_cursor_new(GDK_QUESTION_ARROW); michael@0: break; michael@0: case eCursor_copy: // CSS3 michael@0: gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "copy"); michael@0: if (!gdkcursor) michael@0: newType = MOZ_CURSOR_COPY; michael@0: break; michael@0: case eCursor_alias: michael@0: gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "alias"); michael@0: if (!gdkcursor) michael@0: newType = MOZ_CURSOR_ALIAS; michael@0: break; michael@0: case eCursor_context_menu: michael@0: gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "context-menu"); michael@0: if (!gdkcursor) michael@0: newType = MOZ_CURSOR_CONTEXT_MENU; michael@0: break; michael@0: case eCursor_cell: michael@0: gdkcursor = gdk_cursor_new(GDK_PLUS); michael@0: break; michael@0: // Those two aren’t standardized. Trying both KDE’s and GNOME’s names michael@0: case eCursor_grab: michael@0: gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "openhand"); michael@0: if (!gdkcursor) michael@0: gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "hand1"); michael@0: if (!gdkcursor) michael@0: newType = MOZ_CURSOR_HAND_GRAB; michael@0: break; michael@0: case eCursor_grabbing: michael@0: gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "closedhand"); michael@0: if (!gdkcursor) michael@0: gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "grabbing"); michael@0: if (!gdkcursor) michael@0: newType = MOZ_CURSOR_HAND_GRABBING; michael@0: break; michael@0: case eCursor_spinning: michael@0: gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "progress"); michael@0: if (!gdkcursor) michael@0: newType = MOZ_CURSOR_SPINNING; michael@0: break; michael@0: case eCursor_zoom_in: michael@0: newType = MOZ_CURSOR_ZOOM_IN; michael@0: break; michael@0: case eCursor_zoom_out: michael@0: newType = MOZ_CURSOR_ZOOM_OUT; michael@0: break; michael@0: case eCursor_not_allowed: michael@0: gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "not-allowed"); michael@0: if (!gdkcursor) // nonstandard, yet common michael@0: gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "crossed_circle"); michael@0: if (!gdkcursor) michael@0: newType = MOZ_CURSOR_NOT_ALLOWED; michael@0: break; michael@0: case eCursor_no_drop: michael@0: gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "no-drop"); michael@0: if (!gdkcursor) // this nonstandard sequence makes it work on KDE and GNOME michael@0: gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "forbidden"); michael@0: if (!gdkcursor) michael@0: gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "circle"); michael@0: if (!gdkcursor) michael@0: newType = MOZ_CURSOR_NOT_ALLOWED; michael@0: break; michael@0: case eCursor_vertical_text: michael@0: newType = MOZ_CURSOR_VERTICAL_TEXT; michael@0: break; michael@0: case eCursor_all_scroll: michael@0: gdkcursor = gdk_cursor_new(GDK_FLEUR); michael@0: break; michael@0: case eCursor_nesw_resize: michael@0: gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "size_bdiag"); michael@0: if (!gdkcursor) michael@0: newType = MOZ_CURSOR_NESW_RESIZE; michael@0: break; michael@0: case eCursor_nwse_resize: michael@0: gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "size_fdiag"); michael@0: if (!gdkcursor) michael@0: newType = MOZ_CURSOR_NWSE_RESIZE; michael@0: break; michael@0: case eCursor_ns_resize: michael@0: gdkcursor = gdk_cursor_new(GDK_SB_V_DOUBLE_ARROW); michael@0: break; michael@0: case eCursor_ew_resize: michael@0: gdkcursor = gdk_cursor_new(GDK_SB_H_DOUBLE_ARROW); michael@0: break; michael@0: // Here, two better fitting cursors exist in some cursor themes. Try those first michael@0: case eCursor_row_resize: michael@0: gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "split_v"); michael@0: if (!gdkcursor) michael@0: gdkcursor = gdk_cursor_new(GDK_SB_V_DOUBLE_ARROW); michael@0: break; michael@0: case eCursor_col_resize: michael@0: gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "split_h"); michael@0: if (!gdkcursor) michael@0: gdkcursor = gdk_cursor_new(GDK_SB_H_DOUBLE_ARROW); michael@0: break; michael@0: case eCursor_none: michael@0: newType = MOZ_CURSOR_NONE; michael@0: break; michael@0: default: michael@0: NS_ASSERTION(aCursor, "Invalid cursor type"); michael@0: gdkcursor = gdk_cursor_new(GDK_LEFT_PTR); michael@0: break; michael@0: } michael@0: michael@0: // If by now we don't have a xcursor, this means we have to make a custom michael@0: // one. First, we try creating a named cursor based on the hash of our michael@0: // custom bitmap, as libXcursor has some magic to convert bitmapped cursors michael@0: // to themed cursors michael@0: if (newType != 0xFF && GtkCursors[newType].hash) { michael@0: gdkcursor = gdk_cursor_new_from_name(defaultDisplay, GtkCursors[newType].hash); michael@0: } michael@0: michael@0: // If we still don't have a xcursor, we now really create a bitmap cursor michael@0: if (newType != 0xff && !gdkcursor) { michael@0: GdkPixbuf * cursor_pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, 32, 32); michael@0: if (!cursor_pixbuf) michael@0: return nullptr; michael@0: michael@0: guchar *data = gdk_pixbuf_get_pixels(cursor_pixbuf); michael@0: michael@0: // Read data from GtkCursors and compose RGBA surface from 1bit bitmap and mask michael@0: // GtkCursors bits and mask are 32x32 monochrome bitmaps (1 bit for each pixel) michael@0: // so it's 128 byte array (4 bytes for are one bitmap row and there are 32 rows here). michael@0: const unsigned char *bits = GtkCursors[newType].bits; michael@0: const unsigned char *mask_bits = GtkCursors[newType].mask_bits; michael@0: michael@0: for (int i = 0; i < 128; i++) { michael@0: char bit = *bits++; michael@0: char mask = *mask_bits++; michael@0: for (int j = 0; j < 8; j++) { michael@0: unsigned char pix = ~(((bit >> j) & 0x01) * 0xff); michael@0: *data++ = pix; michael@0: *data++ = pix; michael@0: *data++ = pix; michael@0: *data++ = (((mask >> j) & 0x01) * 0xff); michael@0: } michael@0: } michael@0: michael@0: gdkcursor = gdk_cursor_new_from_pixbuf(gdk_display_get_default(), cursor_pixbuf, michael@0: GtkCursors[newType].hot_x, michael@0: GtkCursors[newType].hot_y); michael@0: michael@0: g_object_unref(cursor_pixbuf); michael@0: } michael@0: michael@0: gCursorCache[aCursor] = gdkcursor; michael@0: michael@0: return gdkcursor; michael@0: } michael@0: michael@0: // gtk callbacks michael@0: michael@0: #if (MOZ_WIDGET_GTK == 2) michael@0: static gboolean michael@0: expose_event_cb(GtkWidget *widget, GdkEventExpose *event) michael@0: { michael@0: nsRefPtr window = get_window_for_gdk_window(event->window); michael@0: if (!window) michael@0: return FALSE; michael@0: michael@0: window->OnExposeEvent(event); michael@0: return FALSE; michael@0: } michael@0: #else michael@0: void michael@0: draw_window_of_widget(GtkWidget *widget, GdkWindow *aWindow, cairo_t *cr) michael@0: { michael@0: if (gtk_cairo_should_draw_window(cr, aWindow)) { michael@0: nsRefPtr window = get_window_for_gdk_window(aWindow); michael@0: if (!window) { michael@0: NS_WARNING("Cannot get nsWindow from GtkWidget"); michael@0: } michael@0: else { michael@0: cairo_save(cr); michael@0: gtk_cairo_transform_to_window(cr, widget, aWindow); michael@0: // TODO - window->OnExposeEvent() can destroy this or other windows, michael@0: // do we need to handle it somehow? michael@0: window->OnExposeEvent(cr); michael@0: cairo_restore(cr); michael@0: } michael@0: } michael@0: michael@0: GList *children = gdk_window_get_children(aWindow); michael@0: GList *child = children; michael@0: while (child) { michael@0: GdkWindow *window = GDK_WINDOW(child->data); michael@0: gpointer windowWidget; michael@0: gdk_window_get_user_data(window, &windowWidget); michael@0: if (windowWidget == widget) { michael@0: draw_window_of_widget(widget, window, cr); michael@0: } michael@0: child = g_list_next(child); michael@0: } michael@0: g_list_free(children); michael@0: } michael@0: michael@0: /* static */ michael@0: gboolean michael@0: expose_event_cb(GtkWidget *widget, cairo_t *cr) michael@0: { michael@0: draw_window_of_widget(widget, gtk_widget_get_window(widget), cr); michael@0: return FALSE; michael@0: } michael@0: #endif //MOZ_WIDGET_GTK2 michael@0: michael@0: static gboolean michael@0: configure_event_cb(GtkWidget *widget, michael@0: GdkEventConfigure *event) michael@0: { michael@0: nsRefPtr window = get_window_for_gtk_widget(widget); michael@0: if (!window) michael@0: return FALSE; michael@0: michael@0: return window->OnConfigureEvent(widget, event); michael@0: } michael@0: michael@0: static void michael@0: container_unrealize_cb (GtkWidget *widget) michael@0: { michael@0: nsRefPtr window = get_window_for_gtk_widget(widget); michael@0: if (!window) michael@0: return; michael@0: michael@0: window->OnContainerUnrealize(); michael@0: } michael@0: michael@0: static void michael@0: size_allocate_cb (GtkWidget *widget, GtkAllocation *allocation) michael@0: { michael@0: nsRefPtr window = get_window_for_gtk_widget(widget); michael@0: if (!window) michael@0: return; michael@0: michael@0: window->OnSizeAllocate(allocation); michael@0: } michael@0: michael@0: static gboolean michael@0: delete_event_cb(GtkWidget *widget, GdkEventAny *event) michael@0: { michael@0: nsRefPtr window = get_window_for_gtk_widget(widget); michael@0: if (!window) michael@0: return FALSE; michael@0: michael@0: window->OnDeleteEvent(); michael@0: michael@0: return TRUE; michael@0: } michael@0: michael@0: static gboolean michael@0: enter_notify_event_cb(GtkWidget *widget, michael@0: GdkEventCrossing *event) michael@0: { michael@0: nsRefPtr window = get_window_for_gdk_window(event->window); michael@0: if (!window) michael@0: return TRUE; michael@0: michael@0: window->OnEnterNotifyEvent(event); michael@0: michael@0: return TRUE; michael@0: } michael@0: michael@0: static gboolean michael@0: leave_notify_event_cb(GtkWidget *widget, michael@0: GdkEventCrossing *event) michael@0: { michael@0: if (is_parent_grab_leave(event)) { michael@0: return TRUE; michael@0: } michael@0: michael@0: // bug 369599: Suppress LeaveNotify events caused by pointer grabs to michael@0: // avoid generating spurious mouse exit events. michael@0: gint x = gint(event->x_root); michael@0: gint y = gint(event->y_root); michael@0: GdkDisplay* display = gtk_widget_get_display(widget); michael@0: GdkWindow* winAtPt = gdk_display_get_window_at_pointer(display, &x, &y); michael@0: if (winAtPt == event->window) { michael@0: return TRUE; michael@0: } michael@0: michael@0: nsRefPtr window = get_window_for_gdk_window(event->window); michael@0: if (!window) michael@0: return TRUE; michael@0: michael@0: window->OnLeaveNotifyEvent(event); michael@0: michael@0: return TRUE; michael@0: } michael@0: michael@0: static nsWindow* michael@0: GetFirstNSWindowForGDKWindow(GdkWindow *aGdkWindow) michael@0: { michael@0: nsWindow* window; michael@0: while (!(window = get_window_for_gdk_window(aGdkWindow))) { michael@0: // The event has bubbled to the moz_container widget as passed into each caller's *widget parameter, michael@0: // but its corresponding nsWindow is an ancestor of the window that we need. Instead, look at michael@0: // event->window and find the first ancestor nsWindow of it because event->window may be in a plugin. michael@0: aGdkWindow = gdk_window_get_parent(aGdkWindow); michael@0: if (!aGdkWindow) { michael@0: window = nullptr; michael@0: break; michael@0: } michael@0: } michael@0: return window; michael@0: } michael@0: michael@0: static gboolean michael@0: motion_notify_event_cb(GtkWidget *widget, GdkEventMotion *event) michael@0: { michael@0: UpdateLastInputEventTime(event); michael@0: michael@0: nsWindow *window = GetFirstNSWindowForGDKWindow(event->window); michael@0: if (!window) michael@0: return FALSE; michael@0: michael@0: window->OnMotionNotifyEvent(event); michael@0: michael@0: return TRUE; michael@0: } michael@0: michael@0: static gboolean michael@0: button_press_event_cb(GtkWidget *widget, GdkEventButton *event) michael@0: { michael@0: UpdateLastInputEventTime(event); michael@0: michael@0: nsWindow *window = GetFirstNSWindowForGDKWindow(event->window); michael@0: if (!window) michael@0: return FALSE; michael@0: michael@0: window->OnButtonPressEvent(event); michael@0: michael@0: return TRUE; michael@0: } michael@0: michael@0: static gboolean michael@0: button_release_event_cb(GtkWidget *widget, GdkEventButton *event) michael@0: { michael@0: UpdateLastInputEventTime(event); michael@0: michael@0: nsWindow *window = GetFirstNSWindowForGDKWindow(event->window); michael@0: if (!window) michael@0: return FALSE; michael@0: michael@0: window->OnButtonReleaseEvent(event); michael@0: michael@0: return TRUE; michael@0: } michael@0: michael@0: static gboolean michael@0: focus_in_event_cb(GtkWidget *widget, GdkEventFocus *event) michael@0: { michael@0: nsRefPtr window = get_window_for_gtk_widget(widget); michael@0: if (!window) michael@0: return FALSE; michael@0: michael@0: window->OnContainerFocusInEvent(event); michael@0: michael@0: return FALSE; michael@0: } michael@0: michael@0: static gboolean michael@0: focus_out_event_cb(GtkWidget *widget, GdkEventFocus *event) michael@0: { michael@0: nsRefPtr window = get_window_for_gtk_widget(widget); michael@0: if (!window) michael@0: return FALSE; michael@0: michael@0: window->OnContainerFocusOutEvent(event); michael@0: michael@0: return FALSE; michael@0: } michael@0: michael@0: #ifdef MOZ_X11 michael@0: // For long-lived popup windows that don't really take focus themselves but michael@0: // may have elements that accept keyboard input when the parent window is michael@0: // active, focus is handled specially. These windows include noautohide michael@0: // panels. (This special handling is not necessary for temporary popups where michael@0: // the keyboard is grabbed.) michael@0: // michael@0: // Mousing over or clicking on these windows should not cause them to steal michael@0: // focus from their parent windows, so, the input field of WM_HINTS is set to michael@0: // False to request that the window manager not set the input focus to this michael@0: // window. http://tronche.com/gui/x/icccm/sec-4.html#s-4.1.7 michael@0: // michael@0: // However, these windows can still receive WM_TAKE_FOCUS messages from the michael@0: // window manager, so they can still detect when the user has indicated that michael@0: // they wish to direct keyboard input at these windows. When the window michael@0: // manager offers focus to these windows (after a mouse over or click, for michael@0: // example), a request to make the parent window active is issued. When the michael@0: // parent window becomes active, keyboard events will be received. michael@0: michael@0: static GdkFilterReturn michael@0: popup_take_focus_filter(GdkXEvent *gdk_xevent, michael@0: GdkEvent *event, michael@0: gpointer data) michael@0: { michael@0: XEvent* xevent = static_cast(gdk_xevent); michael@0: if (xevent->type != ClientMessage) michael@0: return GDK_FILTER_CONTINUE; michael@0: michael@0: XClientMessageEvent& xclient = xevent->xclient; michael@0: if (xclient.message_type != gdk_x11_get_xatom_by_name("WM_PROTOCOLS")) michael@0: return GDK_FILTER_CONTINUE; michael@0: michael@0: Atom atom = xclient.data.l[0]; michael@0: if (atom != gdk_x11_get_xatom_by_name("WM_TAKE_FOCUS")) michael@0: return GDK_FILTER_CONTINUE; michael@0: michael@0: guint32 timestamp = xclient.data.l[1]; michael@0: michael@0: GtkWidget* widget = get_gtk_widget_for_gdk_window(event->any.window); michael@0: if (!widget) michael@0: return GDK_FILTER_CONTINUE; michael@0: michael@0: GtkWindow* parent = gtk_window_get_transient_for(GTK_WINDOW(widget)); michael@0: if (!parent) michael@0: return GDK_FILTER_CONTINUE; michael@0: michael@0: if (gtk_window_is_active(parent)) michael@0: return GDK_FILTER_REMOVE; // leave input focus on the parent michael@0: michael@0: GdkWindow* parent_window = gtk_widget_get_window(GTK_WIDGET(parent)); michael@0: if (!parent_window) michael@0: return GDK_FILTER_CONTINUE; michael@0: michael@0: // In case the parent has not been deconified. michael@0: gdk_window_show_unraised(parent_window); michael@0: michael@0: // Request focus on the parent window. michael@0: // Use gdk_window_focus rather than gtk_window_present to avoid michael@0: // raising the parent window. michael@0: gdk_window_focus(parent_window, timestamp); michael@0: return GDK_FILTER_REMOVE; michael@0: } michael@0: michael@0: static GdkFilterReturn michael@0: plugin_window_filter_func(GdkXEvent *gdk_xevent, GdkEvent *event, gpointer data) michael@0: { michael@0: GdkWindow *plugin_window; michael@0: XEvent *xevent; michael@0: Window xeventWindow; michael@0: michael@0: nsRefPtr nswindow = (nsWindow*)data; michael@0: GdkFilterReturn return_val; michael@0: michael@0: xevent = (XEvent *)gdk_xevent; michael@0: return_val = GDK_FILTER_CONTINUE; michael@0: michael@0: switch (xevent->type) michael@0: { michael@0: case CreateNotify: michael@0: case ReparentNotify: michael@0: if (xevent->type==CreateNotify) { michael@0: xeventWindow = xevent->xcreatewindow.window; michael@0: } michael@0: else { michael@0: if (xevent->xreparent.event != xevent->xreparent.parent) michael@0: break; michael@0: xeventWindow = xevent->xreparent.window; michael@0: } michael@0: #if (MOZ_WIDGET_GTK == 2) michael@0: plugin_window = gdk_window_lookup(xeventWindow); michael@0: #else michael@0: plugin_window = gdk_x11_window_lookup_for_display( michael@0: gdk_x11_lookup_xdisplay(xevent->xcreatewindow.display), xeventWindow); michael@0: #endif michael@0: if (plugin_window) { michael@0: GtkWidget *widget = michael@0: get_gtk_widget_for_gdk_window(plugin_window); michael@0: michael@0: // TODO GTK3 michael@0: #if (MOZ_WIDGET_GTK == 2) michael@0: if (GTK_IS_XTBIN(widget)) { michael@0: nswindow->SetPluginType(nsWindow::PluginType_NONXEMBED); michael@0: break; michael@0: } michael@0: else michael@0: #endif michael@0: if(GTK_IS_SOCKET(widget)) { michael@0: if (!g_object_get_data(G_OBJECT(widget), "enable-xt-focus")) { michael@0: nswindow->SetPluginType(nsWindow::PluginType_XEMBED); michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: nswindow->SetPluginType(nsWindow::PluginType_NONXEMBED); michael@0: return_val = GDK_FILTER_REMOVE; michael@0: break; michael@0: case EnterNotify: michael@0: nswindow->SetNonXEmbedPluginFocus(); michael@0: break; michael@0: case DestroyNotify: michael@0: gdk_window_remove_filter michael@0: ((GdkWindow*)(nswindow->GetNativeData(NS_NATIVE_WINDOW)), michael@0: plugin_window_filter_func, michael@0: nswindow); michael@0: // Currently we consider all plugins are non-xembed and calls michael@0: // LoseNonXEmbedPluginFocus without any checking. michael@0: nswindow->LoseNonXEmbedPluginFocus(); michael@0: break; michael@0: default: michael@0: break; michael@0: } michael@0: return return_val; michael@0: } michael@0: michael@0: static GdkFilterReturn michael@0: plugin_client_message_filter(GdkXEvent *gdk_xevent, michael@0: GdkEvent *event, michael@0: gpointer data) michael@0: { michael@0: XEvent *xevent; michael@0: xevent = (XEvent *)gdk_xevent; michael@0: michael@0: GdkFilterReturn return_val; michael@0: return_val = GDK_FILTER_CONTINUE; michael@0: michael@0: if (!gPluginFocusWindow || xevent->type!=ClientMessage) { michael@0: return return_val; michael@0: } michael@0: michael@0: // When WM sends out WM_TAKE_FOCUS, gtk2 will use XSetInputFocus michael@0: // to set the focus to the focus proxy. To prevent this happen michael@0: // while the focus is on the plugin, we filter the WM_TAKE_FOCUS michael@0: // out. michael@0: if (gdk_x11_get_xatom_by_name("WM_PROTOCOLS") michael@0: != xevent->xclient.message_type) { michael@0: return return_val; michael@0: } michael@0: michael@0: if ((Atom) xevent->xclient.data.l[0] == michael@0: gdk_x11_get_xatom_by_name("WM_TAKE_FOCUS")) { michael@0: // block it from gtk2.0 focus proxy michael@0: return_val = GDK_FILTER_REMOVE; michael@0: } michael@0: michael@0: return return_val; michael@0: } michael@0: #endif /* MOZ_X11 */ michael@0: michael@0: static gboolean michael@0: key_press_event_cb(GtkWidget *widget, GdkEventKey *event) michael@0: { michael@0: LOG(("key_press_event_cb\n")); michael@0: michael@0: UpdateLastInputEventTime(event); michael@0: michael@0: // find the window with focus and dispatch this event to that widget michael@0: nsWindow *window = get_window_for_gtk_widget(widget); michael@0: if (!window) michael@0: return FALSE; michael@0: michael@0: nsRefPtr focusWindow = gFocusWindow ? gFocusWindow : window; michael@0: michael@0: #ifdef MOZ_X11 michael@0: // Keyboard repeat can cause key press events to queue up when there are michael@0: // slow event handlers (bug 301029). Throttle these events by removing michael@0: // consecutive pending duplicate KeyPress events to the same window. michael@0: // We use the event time of the last one. michael@0: // Note: GDK calls XkbSetDetectableAutorepeat so that KeyRelease events michael@0: // are generated only when the key is physically released. michael@0: #define NS_GDKEVENT_MATCH_MASK 0x1FFF /* GDK_SHIFT_MASK .. GDK_BUTTON5_MASK */ michael@0: GdkDisplay* gdkDisplay = gtk_widget_get_display(widget); michael@0: Display* dpy = GDK_DISPLAY_XDISPLAY(gdkDisplay); michael@0: while (XPending(dpy)) { michael@0: XEvent next_event; michael@0: XPeekEvent(dpy, &next_event); michael@0: GdkWindow* nextGdkWindow = michael@0: gdk_x11_window_lookup_for_display(gdkDisplay, next_event.xany.window); michael@0: if (nextGdkWindow != event->window || michael@0: next_event.type != KeyPress || michael@0: next_event.xkey.keycode != event->hardware_keycode || michael@0: next_event.xkey.state != (event->state & NS_GDKEVENT_MATCH_MASK)) { michael@0: break; michael@0: } michael@0: XNextEvent(dpy, &next_event); michael@0: event->time = next_event.xkey.time; michael@0: } michael@0: #endif michael@0: michael@0: return focusWindow->OnKeyPressEvent(event); michael@0: } michael@0: michael@0: static gboolean michael@0: key_release_event_cb(GtkWidget *widget, GdkEventKey *event) michael@0: { michael@0: LOG(("key_release_event_cb\n")); michael@0: michael@0: UpdateLastInputEventTime(event); michael@0: michael@0: // find the window with focus and dispatch this event to that widget michael@0: nsWindow *window = get_window_for_gtk_widget(widget); michael@0: if (!window) michael@0: return FALSE; michael@0: michael@0: nsRefPtr focusWindow = gFocusWindow ? gFocusWindow : window; michael@0: michael@0: return focusWindow->OnKeyReleaseEvent(event); michael@0: } michael@0: michael@0: static gboolean michael@0: scroll_event_cb(GtkWidget *widget, GdkEventScroll *event) michael@0: { michael@0: nsWindow *window = GetFirstNSWindowForGDKWindow(event->window); michael@0: if (!window) michael@0: return FALSE; michael@0: michael@0: window->OnScrollEvent(event); michael@0: michael@0: return TRUE; michael@0: } michael@0: michael@0: static gboolean michael@0: visibility_notify_event_cb (GtkWidget *widget, GdkEventVisibility *event) michael@0: { michael@0: nsRefPtr window = get_window_for_gdk_window(event->window); michael@0: if (!window) michael@0: return FALSE; michael@0: michael@0: window->OnVisibilityNotifyEvent(event); michael@0: michael@0: return TRUE; michael@0: } michael@0: michael@0: static void michael@0: hierarchy_changed_cb (GtkWidget *widget, michael@0: GtkWidget *previous_toplevel) michael@0: { michael@0: GtkWidget *toplevel = gtk_widget_get_toplevel(widget); michael@0: GdkWindowState old_window_state = GDK_WINDOW_STATE_WITHDRAWN; michael@0: GdkEventWindowState event; michael@0: michael@0: event.new_window_state = GDK_WINDOW_STATE_WITHDRAWN; michael@0: michael@0: if (GTK_IS_WINDOW(previous_toplevel)) { michael@0: g_signal_handlers_disconnect_by_func(previous_toplevel, michael@0: FuncToGpointer(window_state_event_cb), michael@0: widget); michael@0: GdkWindow *win = gtk_widget_get_window(previous_toplevel); michael@0: if (win) { michael@0: old_window_state = gdk_window_get_state(win); michael@0: } michael@0: } michael@0: michael@0: if (GTK_IS_WINDOW(toplevel)) { michael@0: g_signal_connect_swapped(toplevel, "window-state-event", michael@0: G_CALLBACK(window_state_event_cb), widget); michael@0: GdkWindow *win = gtk_widget_get_window(toplevel); michael@0: if (win) { michael@0: event.new_window_state = gdk_window_get_state(win); michael@0: } michael@0: } michael@0: michael@0: event.changed_mask = static_cast michael@0: (old_window_state ^ event.new_window_state); michael@0: michael@0: if (event.changed_mask) { michael@0: event.type = GDK_WINDOW_STATE; michael@0: event.window = nullptr; michael@0: event.send_event = TRUE; michael@0: window_state_event_cb(widget, &event); michael@0: } michael@0: } michael@0: michael@0: static gboolean michael@0: window_state_event_cb (GtkWidget *widget, GdkEventWindowState *event) michael@0: { michael@0: nsRefPtr window = get_window_for_gtk_widget(widget); michael@0: if (!window) michael@0: return FALSE; michael@0: michael@0: window->OnWindowStateEvent(widget, event); michael@0: michael@0: return FALSE; michael@0: } michael@0: michael@0: static void michael@0: theme_changed_cb (GtkSettings *settings, GParamSpec *pspec, nsWindow *data) michael@0: { michael@0: nsRefPtr window = data; michael@0: window->ThemeChanged(); michael@0: } michael@0: michael@0: ////////////////////////////////////////////////////////////////////// michael@0: // These are all of our drag and drop operations michael@0: michael@0: void michael@0: nsWindow::InitDragEvent(WidgetDragEvent &aEvent) michael@0: { michael@0: // set the keyboard modifiers michael@0: guint modifierState = KeymapWrapper::GetCurrentModifierState(); michael@0: KeymapWrapper::InitInputEvent(aEvent, modifierState); michael@0: } michael@0: michael@0: static gboolean michael@0: drag_motion_event_cb(GtkWidget *aWidget, michael@0: GdkDragContext *aDragContext, michael@0: gint aX, michael@0: gint aY, michael@0: guint aTime, michael@0: gpointer aData) michael@0: { michael@0: nsRefPtr window = get_window_for_gtk_widget(aWidget); michael@0: if (!window) michael@0: return FALSE; michael@0: michael@0: // figure out which internal widget this drag motion actually happened on michael@0: nscoord retx = 0; michael@0: nscoord rety = 0; michael@0: michael@0: GdkWindow *innerWindow = michael@0: get_inner_gdk_window(gtk_widget_get_window(aWidget), aX, aY, michael@0: &retx, &rety); michael@0: nsRefPtr innerMostWindow = get_window_for_gdk_window(innerWindow); michael@0: michael@0: if (!innerMostWindow) { michael@0: innerMostWindow = window; michael@0: } michael@0: michael@0: LOGDRAG(("nsWindow drag-motion signal for %p\n", (void*)innerMostWindow)); michael@0: michael@0: return nsDragService::GetInstance()-> michael@0: ScheduleMotionEvent(innerMostWindow, aDragContext, michael@0: nsIntPoint(retx, rety), aTime); michael@0: } michael@0: michael@0: static void michael@0: drag_leave_event_cb(GtkWidget *aWidget, michael@0: GdkDragContext *aDragContext, michael@0: guint aTime, michael@0: gpointer aData) michael@0: { michael@0: nsRefPtr window = get_window_for_gtk_widget(aWidget); michael@0: if (!window) michael@0: return; michael@0: michael@0: nsDragService *dragService = nsDragService::GetInstance(); michael@0: michael@0: nsWindow *mostRecentDragWindow = dragService->GetMostRecentDestWindow(); michael@0: if (!mostRecentDragWindow) { michael@0: // This can happen when the target will not accept a drop. A GTK drag michael@0: // source sends the leave message to the destination before the michael@0: // drag-failed signal on the source widget, but the leave message goes michael@0: // via the X server, and so doesn't get processed at least until the michael@0: // event loop runs again. michael@0: return; michael@0: } michael@0: michael@0: GtkWidget *mozContainer = mostRecentDragWindow->GetMozContainerWidget(); michael@0: if (aWidget != mozContainer) michael@0: { michael@0: // When the drag moves between widgets, GTK can send leave signal for michael@0: // the old widget after the motion or drop signal for the new widget. michael@0: // We'll send the leave event when the motion or drop event is run. michael@0: return; michael@0: } michael@0: michael@0: LOGDRAG(("nsWindow drag-leave signal for %p\n", michael@0: (void*)mostRecentDragWindow)); michael@0: michael@0: dragService->ScheduleLeaveEvent(); michael@0: } michael@0: michael@0: michael@0: static gboolean michael@0: drag_drop_event_cb(GtkWidget *aWidget, michael@0: GdkDragContext *aDragContext, michael@0: gint aX, michael@0: gint aY, michael@0: guint aTime, michael@0: gpointer aData) michael@0: { michael@0: nsRefPtr window = get_window_for_gtk_widget(aWidget); michael@0: if (!window) michael@0: return FALSE; michael@0: michael@0: // figure out which internal widget this drag motion actually happened on michael@0: nscoord retx = 0; michael@0: nscoord rety = 0; michael@0: michael@0: GdkWindow *innerWindow = michael@0: get_inner_gdk_window(gtk_widget_get_window(aWidget), aX, aY, michael@0: &retx, &rety); michael@0: nsRefPtr innerMostWindow = get_window_for_gdk_window(innerWindow); michael@0: michael@0: if (!innerMostWindow) { michael@0: innerMostWindow = window; michael@0: } michael@0: michael@0: LOGDRAG(("nsWindow drag-drop signal for %p\n", (void*)innerMostWindow)); michael@0: michael@0: return nsDragService::GetInstance()-> michael@0: ScheduleDropEvent(innerMostWindow, aDragContext, michael@0: nsIntPoint(retx, rety), aTime); michael@0: } michael@0: michael@0: static void michael@0: drag_data_received_event_cb(GtkWidget *aWidget, michael@0: GdkDragContext *aDragContext, michael@0: gint aX, michael@0: gint aY, michael@0: GtkSelectionData *aSelectionData, michael@0: guint aInfo, michael@0: guint aTime, michael@0: gpointer aData) michael@0: { michael@0: nsRefPtr window = get_window_for_gtk_widget(aWidget); michael@0: if (!window) michael@0: return; michael@0: michael@0: window->OnDragDataReceivedEvent(aWidget, michael@0: aDragContext, michael@0: aX, aY, michael@0: aSelectionData, michael@0: aInfo, aTime, aData); michael@0: } michael@0: michael@0: static nsresult michael@0: initialize_prefs(void) michael@0: { michael@0: gRaiseWindows = michael@0: Preferences::GetBool("mozilla.widget.raise-on-setfocus", true); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: static GdkWindow * michael@0: get_inner_gdk_window (GdkWindow *aWindow, michael@0: gint x, gint y, michael@0: gint *retx, gint *rety) michael@0: { michael@0: gint cx, cy, cw, ch; michael@0: GList *children = gdk_window_peek_children(aWindow); michael@0: for (GList *child = g_list_last(children); michael@0: child; michael@0: child = g_list_previous(child)) { michael@0: GdkWindow *childWindow = (GdkWindow *) child->data; michael@0: if (get_window_for_gdk_window(childWindow)) { michael@0: #if (MOZ_WIDGET_GTK == 2) michael@0: gdk_window_get_geometry(childWindow, &cx, &cy, &cw, &ch, nullptr); michael@0: #else michael@0: gdk_window_get_geometry(childWindow, &cx, &cy, &cw, &ch); michael@0: #endif michael@0: if ((cx < x) && (x < (cx + cw)) && michael@0: (cy < y) && (y < (cy + ch)) && michael@0: gdk_window_is_visible(childWindow)) { michael@0: return get_inner_gdk_window(childWindow, michael@0: x - cx, y - cy, michael@0: retx, rety); michael@0: } michael@0: } michael@0: } michael@0: *retx = x; michael@0: *rety = y; michael@0: return aWindow; michael@0: } michael@0: michael@0: static inline bool michael@0: is_context_menu_key(const WidgetKeyboardEvent& aKeyEvent) michael@0: { michael@0: return ((aKeyEvent.keyCode == NS_VK_F10 && aKeyEvent.IsShift() && michael@0: !aKeyEvent.IsControl() && !aKeyEvent.IsMeta() && michael@0: !aKeyEvent.IsAlt()) || michael@0: (aKeyEvent.keyCode == NS_VK_CONTEXT_MENU && !aKeyEvent.IsShift() && michael@0: !aKeyEvent.IsControl() && !aKeyEvent.IsMeta() && michael@0: !aKeyEvent.IsAlt())); michael@0: } michael@0: michael@0: static int michael@0: is_parent_ungrab_enter(GdkEventCrossing *aEvent) michael@0: { michael@0: return (GDK_CROSSING_UNGRAB == aEvent->mode) && michael@0: ((GDK_NOTIFY_ANCESTOR == aEvent->detail) || michael@0: (GDK_NOTIFY_VIRTUAL == aEvent->detail)); michael@0: michael@0: } michael@0: michael@0: static int michael@0: is_parent_grab_leave(GdkEventCrossing *aEvent) michael@0: { michael@0: return (GDK_CROSSING_GRAB == aEvent->mode) && michael@0: ((GDK_NOTIFY_ANCESTOR == aEvent->detail) || michael@0: (GDK_NOTIFY_VIRTUAL == aEvent->detail)); michael@0: } michael@0: michael@0: #ifdef ACCESSIBILITY michael@0: void michael@0: nsWindow::CreateRootAccessible() michael@0: { michael@0: if (mIsTopLevel && !mRootAccessible) { michael@0: LOG(("nsWindow:: Create Toplevel Accessibility\n")); michael@0: mRootAccessible = GetRootAccessible(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsWindow::DispatchEventToRootAccessible(uint32_t aEventType) michael@0: { michael@0: if (!a11y::ShouldA11yBeEnabled()) { michael@0: return; michael@0: } michael@0: michael@0: nsCOMPtr accService = michael@0: do_GetService("@mozilla.org/accessibilityService;1"); michael@0: if (!accService) { michael@0: return; michael@0: } michael@0: michael@0: // Get the root document accessible and fire event to it. michael@0: a11y::Accessible* acc = GetRootAccessible(); michael@0: if (acc) { michael@0: accService->FireAccessibleEvent(aEventType, acc); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsWindow::DispatchActivateEventAccessible(void) michael@0: { michael@0: DispatchEventToRootAccessible(nsIAccessibleEvent::EVENT_WINDOW_ACTIVATE); michael@0: } michael@0: michael@0: void michael@0: nsWindow::DispatchDeactivateEventAccessible(void) michael@0: { michael@0: DispatchEventToRootAccessible(nsIAccessibleEvent::EVENT_WINDOW_DEACTIVATE); michael@0: } michael@0: michael@0: void michael@0: nsWindow::DispatchMaximizeEventAccessible(void) michael@0: { michael@0: DispatchEventToRootAccessible(nsIAccessibleEvent::EVENT_WINDOW_MAXIMIZE); michael@0: } michael@0: michael@0: void michael@0: nsWindow::DispatchMinimizeEventAccessible(void) michael@0: { michael@0: DispatchEventToRootAccessible(nsIAccessibleEvent::EVENT_WINDOW_MINIMIZE); michael@0: } michael@0: michael@0: void michael@0: nsWindow::DispatchRestoreEventAccessible(void) michael@0: { michael@0: DispatchEventToRootAccessible(nsIAccessibleEvent::EVENT_WINDOW_RESTORE); michael@0: } michael@0: michael@0: #endif /* #ifdef ACCESSIBILITY */ michael@0: michael@0: // nsChildWindow class michael@0: michael@0: nsChildWindow::nsChildWindow() michael@0: { michael@0: } michael@0: michael@0: nsChildWindow::~nsChildWindow() michael@0: { michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindow::NotifyIME(const IMENotification& aIMENotification) michael@0: { michael@0: if (MOZ_UNLIKELY(!mIMModule)) { michael@0: switch (aIMENotification.mMessage) { michael@0: case NOTIFY_IME_OF_CURSOR_POS_CHANGED: michael@0: case REQUEST_TO_COMMIT_COMPOSITION: michael@0: case REQUEST_TO_CANCEL_COMPOSITION: michael@0: case NOTIFY_IME_OF_FOCUS: michael@0: case NOTIFY_IME_OF_BLUR: michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: default: michael@0: break; michael@0: } michael@0: } michael@0: switch (aIMENotification.mMessage) { michael@0: // TODO: We should replace NOTIFY_IME_OF_CURSOR_POS_CHANGED with michael@0: // NOTIFY_IME_OF_SELECTION_CHANGE. The required behavior is michael@0: // really different from committing composition. michael@0: case NOTIFY_IME_OF_CURSOR_POS_CHANGED: michael@0: case REQUEST_TO_COMMIT_COMPOSITION: michael@0: return mIMModule->CommitIMEComposition(this); michael@0: case REQUEST_TO_CANCEL_COMPOSITION: michael@0: return mIMModule->CancelIMEComposition(this); michael@0: case NOTIFY_IME_OF_FOCUS: michael@0: mIMModule->OnFocusChangeInGecko(true); michael@0: return NS_OK; michael@0: case NOTIFY_IME_OF_BLUR: michael@0: mIMModule->OnFocusChangeInGecko(false); michael@0: return NS_OK; michael@0: case NOTIFY_IME_OF_COMPOSITION_UPDATE: michael@0: mIMModule->OnUpdateComposition(); michael@0: return NS_OK; michael@0: default: michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: } michael@0: michael@0: NS_IMETHODIMP_(void) michael@0: nsWindow::SetInputContext(const InputContext& aContext, michael@0: const InputContextAction& aAction) michael@0: { michael@0: if (!mIMModule) { michael@0: return; michael@0: } michael@0: mIMModule->SetInputContext(this, &aContext, &aAction); michael@0: } michael@0: michael@0: NS_IMETHODIMP_(InputContext) michael@0: nsWindow::GetInputContext() michael@0: { michael@0: InputContext context; michael@0: if (!mIMModule) { michael@0: context.mIMEState.mEnabled = IMEState::DISABLED; michael@0: context.mIMEState.mOpen = IMEState::OPEN_STATE_NOT_SUPPORTED; michael@0: // If IME context isn't available on this widget, we should set |this| michael@0: // instead of nullptr since nullptr means that the platform has only one michael@0: // context per process. michael@0: context.mNativeIMEContext = this; michael@0: } else { michael@0: context = mIMModule->GetInputContext(); michael@0: context.mNativeIMEContext = mIMModule; michael@0: } michael@0: return context; michael@0: } michael@0: michael@0: NS_IMETHODIMP_(bool) michael@0: nsWindow::ExecuteNativeKeyBinding(NativeKeyBindingsType aType, michael@0: const WidgetKeyboardEvent& aEvent, michael@0: DoCommandCallback aCallback, michael@0: void* aCallbackData) michael@0: { michael@0: NativeKeyBindings* keyBindings = NativeKeyBindings::GetInstance(aType); michael@0: return keyBindings->Execute(aEvent, aCallback, aCallbackData); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindow::GetToggledKeyState(uint32_t aKeyCode, bool* aLEDState) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aLEDState); michael@0: michael@0: KeymapWrapper::Modifiers modifier; michael@0: switch (aKeyCode) { michael@0: case NS_VK_CAPS_LOCK: modifier = KeymapWrapper::CAPS_LOCK; break; michael@0: case NS_VK_NUM_LOCK: modifier = KeymapWrapper::NUM_LOCK; break; michael@0: case NS_VK_SCROLL_LOCK: modifier = KeymapWrapper::SCROLL_LOCK; break; michael@0: default: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: *aLEDState = michael@0: KeymapWrapper::AreModifiersCurrentlyActive(modifier); michael@0: return NS_OK; michael@0: } michael@0: michael@0: #if defined(MOZ_X11) && (MOZ_WIDGET_GTK == 2) michael@0: /* static */ already_AddRefed michael@0: nsWindow::GetSurfaceForGdkDrawable(GdkDrawable* aDrawable, michael@0: const nsIntSize& aSize) michael@0: { michael@0: GdkVisual* visual = gdk_drawable_get_visual(aDrawable); michael@0: Screen* xScreen = michael@0: gdk_x11_screen_get_xscreen(gdk_drawable_get_screen(aDrawable)); michael@0: Display* xDisplay = DisplayOfScreen(xScreen); michael@0: Drawable xDrawable = gdk_x11_drawable_get_xid(aDrawable); michael@0: michael@0: nsRefPtr result; michael@0: michael@0: if (visual) { michael@0: Visual* xVisual = gdk_x11_visual_get_xvisual(visual); michael@0: michael@0: result = new gfxXlibSurface(xDisplay, xDrawable, xVisual, michael@0: gfxIntSize(aSize.width, aSize.height)); michael@0: } else { michael@0: // no visual? we must be using an xrender format. Find a format michael@0: // for this depth. michael@0: XRenderPictFormat *pf = nullptr; michael@0: switch (gdk_drawable_get_depth(aDrawable)) { michael@0: case 32: michael@0: pf = XRenderFindStandardFormat(xDisplay, PictStandardARGB32); michael@0: break; michael@0: case 24: michael@0: pf = XRenderFindStandardFormat(xDisplay, PictStandardRGB24); michael@0: break; michael@0: default: michael@0: NS_ERROR("Don't know how to handle the given depth!"); michael@0: break; michael@0: } michael@0: michael@0: result = new gfxXlibSurface(xScreen, xDrawable, pf, michael@0: gfxIntSize(aSize.width, aSize.height)); michael@0: } michael@0: michael@0: return result.forget(); michael@0: } michael@0: #endif michael@0: michael@0: TemporaryRef michael@0: nsWindow::StartRemoteDrawing() michael@0: { michael@0: #if (MOZ_WIDGET_GTK == 2) michael@0: gfxASurface *surf = GetThebesSurface(); michael@0: #else michael@0: // TODO GTK3 michael@0: gfxASurface *surf = nullptr; michael@0: #endif michael@0: if (!surf) { michael@0: return nullptr; michael@0: } michael@0: michael@0: IntSize size(surf->GetSize().width, surf->GetSize().height); michael@0: if (size.width <= 0 || size.height <= 0) { michael@0: return nullptr; michael@0: } michael@0: michael@0: return gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(surf, size); michael@0: } michael@0: michael@0: // return the gfxASurface for rendering to this widget michael@0: gfxASurface* michael@0: #if (MOZ_WIDGET_GTK == 2) michael@0: nsWindow::GetThebesSurface() michael@0: #else michael@0: nsWindow::GetThebesSurface(cairo_t *cr) michael@0: #endif michael@0: { michael@0: if (!mGdkWindow) michael@0: return nullptr; michael@0: michael@0: #if (MOZ_WIDGET_GTK != 2) michael@0: cairo_surface_t *surf = cairo_get_target(cr); michael@0: if (cairo_surface_status(surf) != CAIRO_STATUS_SUCCESS) { michael@0: NS_NOTREACHED("Missing cairo target?"); michael@0: return nullptr; michael@0: } michael@0: #endif // MOZ_WIDGET_GTK2 michael@0: michael@0: #ifdef MOZ_X11 michael@0: gint width, height; michael@0: michael@0: #if (MOZ_WIDGET_GTK == 2) michael@0: gdk_drawable_get_size(GDK_DRAWABLE(mGdkWindow), &width, &height); michael@0: #else michael@0: width = gdk_window_get_width(mGdkWindow); michael@0: height = gdk_window_get_height(mGdkWindow); michael@0: #endif michael@0: michael@0: // Owen Taylor says this is the right thing to do! michael@0: width = std::min(32767, width); michael@0: height = std::min(32767, height); michael@0: gfxIntSize size(width, height); michael@0: michael@0: GdkVisual *gdkVisual = gdk_window_get_visual(mGdkWindow); michael@0: Visual* visual = gdk_x11_visual_get_xvisual(gdkVisual); michael@0: michael@0: # ifdef MOZ_HAVE_SHMIMAGE michael@0: bool usingShm = false; michael@0: if (nsShmImage::UseShm()) { michael@0: // EnsureShmImage() is a dangerous interface, but we guarantee michael@0: // that the thebes surface and the shmimage have the same michael@0: // lifetime michael@0: mThebesSurface = michael@0: nsShmImage::EnsureShmImage(size, michael@0: visual, gdk_visual_get_depth(gdkVisual), michael@0: mShmImage); michael@0: usingShm = mThebesSurface != nullptr; michael@0: } michael@0: if (!usingShm) michael@0: # endif // MOZ_HAVE_SHMIMAGE michael@0: michael@0: #if (MOZ_WIDGET_GTK == 2) michael@0: mThebesSurface = new gfxXlibSurface michael@0: (GDK_WINDOW_XDISPLAY(mGdkWindow), michael@0: gdk_x11_window_get_xid(mGdkWindow), michael@0: visual, michael@0: size); michael@0: #else michael@0: #if MOZ_TREE_CAIRO michael@0: #error "cairo-gtk3 target must be built with --enable-system-cairo" michael@0: #else michael@0: mThebesSurface = gfxASurface::Wrap(surf); michael@0: #endif michael@0: #endif michael@0: michael@0: #endif michael@0: michael@0: // if the surface creation is reporting an error, then michael@0: // we don't have a surface to give back michael@0: if (mThebesSurface && mThebesSurface->CairoStatus() != 0) { michael@0: mThebesSurface = nullptr; michael@0: } michael@0: michael@0: return mThebesSurface; michael@0: } michael@0: michael@0: // Code shared begin BeginMoveDrag and BeginResizeDrag michael@0: bool michael@0: nsWindow::GetDragInfo(WidgetMouseEvent* aMouseEvent, michael@0: GdkWindow** aWindow, gint* aButton, michael@0: gint* aRootX, gint* aRootY) michael@0: { michael@0: if (aMouseEvent->button != WidgetMouseEvent::eLeftButton) { michael@0: // we can only begin a move drag with the left mouse button michael@0: return false; michael@0: } michael@0: *aButton = 1; michael@0: michael@0: // get the gdk window for this widget michael@0: GdkWindow* gdk_window = mGdkWindow; michael@0: if (!gdk_window) { michael@0: return false; michael@0: } michael@0: NS_ABORT_IF_FALSE(GDK_IS_WINDOW(gdk_window), "must really be window"); michael@0: michael@0: // find the top-level window michael@0: gdk_window = gdk_window_get_toplevel(gdk_window); michael@0: NS_ABORT_IF_FALSE(gdk_window, michael@0: "gdk_window_get_toplevel should not return null"); michael@0: *aWindow = gdk_window; michael@0: michael@0: if (!aMouseEvent->widget) { michael@0: return false; michael@0: } michael@0: michael@0: // FIXME: It would be nice to have the widget position at the time michael@0: // of the event, but it's relatively unlikely that the widget has michael@0: // moved since the mousedown. (On the other hand, it's quite likely michael@0: // that the mouse has moved, which is why we use the mouse position michael@0: // from the event.) michael@0: nsIntPoint offset = aMouseEvent->widget->WidgetToScreenOffset(); michael@0: *aRootX = aMouseEvent->refPoint.x + offset.x; michael@0: *aRootY = aMouseEvent->refPoint.y + offset.y; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindow::BeginMoveDrag(WidgetMouseEvent* aEvent) michael@0: { michael@0: NS_ABORT_IF_FALSE(aEvent, "must have event"); michael@0: NS_ABORT_IF_FALSE(aEvent->eventStructType == NS_MOUSE_EVENT, michael@0: "event must have correct struct type"); michael@0: michael@0: GdkWindow *gdk_window; michael@0: gint button, screenX, screenY; michael@0: if (!GetDragInfo(aEvent, &gdk_window, &button, &screenX, &screenY)) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: // tell the window manager to start the move michael@0: gdk_window_begin_move_drag(gdk_window, button, screenX, screenY, michael@0: aEvent->time); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindow::BeginResizeDrag(WidgetGUIEvent* aEvent, michael@0: int32_t aHorizontal, michael@0: int32_t aVertical) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aEvent); michael@0: michael@0: if (aEvent->eventStructType != NS_MOUSE_EVENT) { michael@0: // you can only begin a resize drag with a mouse event michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: GdkWindow *gdk_window; michael@0: gint button, screenX, screenY; michael@0: if (!GetDragInfo(aEvent->AsMouseEvent(), &gdk_window, &button, michael@0: &screenX, &screenY)) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: // work out what GdkWindowEdge we're talking about michael@0: GdkWindowEdge window_edge; michael@0: if (aVertical < 0) { michael@0: if (aHorizontal < 0) { michael@0: window_edge = GDK_WINDOW_EDGE_NORTH_WEST; michael@0: } else if (aHorizontal == 0) { michael@0: window_edge = GDK_WINDOW_EDGE_NORTH; michael@0: } else { michael@0: window_edge = GDK_WINDOW_EDGE_NORTH_EAST; michael@0: } michael@0: } else if (aVertical == 0) { michael@0: if (aHorizontal < 0) { michael@0: window_edge = GDK_WINDOW_EDGE_WEST; michael@0: } else if (aHorizontal == 0) { michael@0: return NS_ERROR_INVALID_ARG; michael@0: } else { michael@0: window_edge = GDK_WINDOW_EDGE_EAST; michael@0: } michael@0: } else { michael@0: if (aHorizontal < 0) { michael@0: window_edge = GDK_WINDOW_EDGE_SOUTH_WEST; michael@0: } else if (aHorizontal == 0) { michael@0: window_edge = GDK_WINDOW_EDGE_SOUTH; michael@0: } else { michael@0: window_edge = GDK_WINDOW_EDGE_SOUTH_EAST; michael@0: } michael@0: } michael@0: michael@0: // tell the window manager to start the resize michael@0: gdk_window_begin_resize_drag(gdk_window, window_edge, button, michael@0: screenX, screenY, aEvent->time); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsIWidget::LayerManager* michael@0: nsWindow::GetLayerManager(PLayerTransactionChild* aShadowManager, michael@0: LayersBackend aBackendHint, michael@0: LayerManagerPersistence aPersistence, michael@0: bool* aAllowRetaining) michael@0: { michael@0: if (!mLayerManager && eTransparencyTransparent == GetTransparencyMode()) { michael@0: mLayerManager = CreateBasicLayerManager(); michael@0: } michael@0: michael@0: return nsBaseWidget::GetLayerManager(aShadowManager, aBackendHint, michael@0: aPersistence, aAllowRetaining); michael@0: } michael@0: michael@0: void michael@0: nsWindow::ClearCachedResources() michael@0: { michael@0: if (mLayerManager && michael@0: mLayerManager->GetBackendType() == mozilla::layers::LayersBackend::LAYERS_BASIC) { michael@0: mLayerManager->ClearCachedResources(); michael@0: } michael@0: michael@0: GList* children = gdk_window_peek_children(mGdkWindow); michael@0: for (GList* list = children; list; list = list->next) { michael@0: nsWindow* window = get_window_for_gdk_window(GDK_WINDOW(list->data)); michael@0: if (window) { michael@0: window->ClearCachedResources(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: nsWindow::SynthesizeNativeMouseEvent(nsIntPoint aPoint, michael@0: uint32_t aNativeMessage, michael@0: uint32_t aModifierFlags) michael@0: { michael@0: if (!mGdkWindow) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: GdkDisplay* display = gdk_window_get_display(mGdkWindow); michael@0: michael@0: // When a button-release event is requested, create it here and put it in the michael@0: // event queue. This will not emit a motion event - this needs to be done michael@0: // explicitly *before* requesting a button-release. You will also need to wait michael@0: // for the motion event to be dispatched before requesting a button-release michael@0: // event to maintain the desired event order. michael@0: if (aNativeMessage == GDK_BUTTON_RELEASE) { michael@0: GdkEvent event; michael@0: memset(&event, 0, sizeof(GdkEvent)); michael@0: event.type = (GdkEventType)aNativeMessage; michael@0: event.button.button = 1; michael@0: event.button.window = mGdkWindow; michael@0: event.button.time = GDK_CURRENT_TIME; michael@0: michael@0: #if (MOZ_WIDGET_GTK == 3) michael@0: // Get device for event source michael@0: GdkDeviceManager *device_manager = gdk_display_get_device_manager(display); michael@0: event.button.device = gdk_device_manager_get_client_pointer(device_manager); michael@0: #endif michael@0: michael@0: gdk_event_put(&event); michael@0: } else { michael@0: // We don't support specific events other than button-release. In case michael@0: // aNativeMessage != GDK_BUTTON_RELEASE we'll synthesize a motion event michael@0: // that will be emitted by gdk_display_warp_pointer(). michael@0: GdkScreen* screen = gdk_window_get_screen(mGdkWindow); michael@0: gdk_display_warp_pointer(display, screen, aPoint.x, aPoint.y); michael@0: } michael@0: michael@0: return NS_OK; michael@0: }