Bumping manifests a=b2g-bump
[gecko.git] / widget / gtk / nsWindow.cpp
blob31fc64bb527549172704525786655561102855b4
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* vim:expandtab:shiftwidth=4:tabstop=4:
3 */
4 /* This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 #include "mozilla/ArrayUtils.h"
9 #include "mozilla/MiscEvents.h"
10 #include "mozilla/MouseEvents.h"
11 #include "mozilla/TextEvents.h"
12 #include <algorithm>
14 #include "prlink.h"
15 #include "nsGTKToolkit.h"
16 #include "nsIRollupListener.h"
17 #include "nsIDOMNode.h"
19 #include "nsWidgetsCID.h"
20 #include "nsDragService.h"
21 #include "nsIWidgetListener.h"
23 #include "nsGtkKeyUtils.h"
24 #include "nsGtkCursors.h"
26 #include <gtk/gtk.h>
27 #if (MOZ_WIDGET_GTK == 3)
28 #include <gtk/gtkx.h>
29 #endif
30 #ifdef MOZ_X11
31 #include <gdk/gdkx.h>
32 #include <X11/Xatom.h>
33 #include <X11/extensions/XShm.h>
34 #include <X11/extensions/shape.h>
35 #if (MOZ_WIDGET_GTK == 3)
36 #include <gdk/gdkkeysyms-compat.h>
37 #endif
39 #ifdef AIX
40 #include <X11/keysym.h>
41 #else
42 #include <X11/XF86keysym.h>
43 #endif
45 #if (MOZ_WIDGET_GTK == 2)
46 #include "gtk2xtbin.h"
47 #endif
48 #endif /* MOZ_X11 */
49 #include <gdk/gdkkeysyms.h>
50 #if (MOZ_WIDGET_GTK == 2)
51 #include <gtk/gtkprivate.h>
52 #endif
54 #include "nsGkAtoms.h"
56 #ifdef MOZ_ENABLE_STARTUP_NOTIFICATION
57 #define SN_API_NOT_YET_FROZEN
58 #include <startup-notification-1.0/libsn/sn.h>
59 #endif
61 #include "mozilla/Likely.h"
62 #include "mozilla/Preferences.h"
63 #include "nsIPrefService.h"
64 #include "nsIGConfService.h"
65 #include "nsIServiceManager.h"
66 #include "nsIStringBundle.h"
67 #include "nsGfxCIID.h"
68 #include "nsGtkUtils.h"
69 #include "nsIObserverService.h"
70 #include "mozilla/layers/LayersTypes.h"
71 #include "nsIIdleServiceInternal.h"
72 #include "nsIPropertyBag2.h"
73 #include "GLContext.h"
74 #include "gfx2DGlue.h"
76 #ifdef ACCESSIBILITY
77 #include "mozilla/a11y/Accessible.h"
78 #include "mozilla/a11y/Platform.h"
79 #include "nsAccessibilityService.h"
81 using namespace mozilla;
82 using namespace mozilla::widget;
83 #endif
85 /* For SetIcon */
86 #include "nsAppDirectoryServiceDefs.h"
87 #include "nsXPIDLString.h"
88 #include "nsIFile.h"
90 /* SetCursor(imgIContainer*) */
91 #include <gdk/gdk.h>
92 #include <wchar.h>
93 #include "imgIContainer.h"
94 #include "nsGfxCIID.h"
95 #include "nsImageToPixbuf.h"
96 #include "nsIInterfaceRequestorUtils.h"
97 #include "nsAutoPtr.h"
98 #include "ClientLayerManager.h"
100 extern "C" {
101 #define PIXMAN_DONT_DEFINE_STDINT
102 #include "pixman.h"
104 #include "gfxPlatformGtk.h"
105 #include "gfxContext.h"
106 #include "gfxImageSurface.h"
107 #include "gfxUtils.h"
108 #include "Layers.h"
109 #include "GLContextProvider.h"
110 #include "mozilla/gfx/2D.h"
111 #include "mozilla/layers/CompositorParent.h"
113 #ifdef MOZ_X11
114 #include "gfxXlibSurface.h"
115 #include "cairo-xlib.h"
116 #endif
118 #include "nsShmImage.h"
120 #include "nsIDOMWheelEvent.h"
122 #include "NativeKeyBindings.h"
123 #include "nsWindow.h"
125 using namespace mozilla;
126 using namespace mozilla::gfx;
127 using namespace mozilla::widget;
128 using namespace mozilla::layers;
129 using mozilla::gl::GLContext;
131 // Don't put more than this many rects in the dirty region, just fluff
132 // out to the bounding-box if there are more
133 #define MAX_RECTS_IN_REGION 100
135 const gint kEvents = GDK_EXPOSURE_MASK | GDK_STRUCTURE_MASK |
136 GDK_VISIBILITY_NOTIFY_MASK |
137 GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
138 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
139 #if GTK_CHECK_VERSION(3,4,0)
140 GDK_SMOOTH_SCROLL_MASK |
141 #endif
142 GDK_SCROLL_MASK |
143 GDK_POINTER_MOTION_MASK;
145 /* utility functions */
146 static bool is_mouse_in_window(GdkWindow* aWindow,
147 gdouble aMouseX, gdouble aMouseY);
148 static nsWindow *get_window_for_gtk_widget(GtkWidget *widget);
149 static nsWindow *get_window_for_gdk_window(GdkWindow *window);
150 static GtkWidget *get_gtk_widget_for_gdk_window(GdkWindow *window);
151 static GdkCursor *get_gtk_cursor(nsCursor aCursor);
153 static GdkWindow *get_inner_gdk_window (GdkWindow *aWindow,
154 gint x, gint y,
155 gint *retx, gint *rety);
157 static inline bool is_context_menu_key(const WidgetKeyboardEvent& inKeyEvent);
159 static int is_parent_ungrab_enter(GdkEventCrossing *aEvent);
160 static int is_parent_grab_leave(GdkEventCrossing *aEvent);
162 static void GetBrandName(nsXPIDLString& brandName);
164 /* callbacks from widgets */
165 #if (MOZ_WIDGET_GTK == 2)
166 static gboolean expose_event_cb (GtkWidget *widget,
167 GdkEventExpose *event);
168 #else
169 static gboolean expose_event_cb (GtkWidget *widget,
170 cairo_t *rect);
171 #endif
172 static gboolean configure_event_cb (GtkWidget *widget,
173 GdkEventConfigure *event);
174 static void container_unrealize_cb (GtkWidget *widget);
175 static void size_allocate_cb (GtkWidget *widget,
176 GtkAllocation *allocation);
177 static gboolean delete_event_cb (GtkWidget *widget,
178 GdkEventAny *event);
179 static gboolean enter_notify_event_cb (GtkWidget *widget,
180 GdkEventCrossing *event);
181 static gboolean leave_notify_event_cb (GtkWidget *widget,
182 GdkEventCrossing *event);
183 static gboolean motion_notify_event_cb (GtkWidget *widget,
184 GdkEventMotion *event);
185 static gboolean button_press_event_cb (GtkWidget *widget,
186 GdkEventButton *event);
187 static gboolean button_release_event_cb (GtkWidget *widget,
188 GdkEventButton *event);
189 static gboolean focus_in_event_cb (GtkWidget *widget,
190 GdkEventFocus *event);
191 static gboolean focus_out_event_cb (GtkWidget *widget,
192 GdkEventFocus *event);
193 static gboolean key_press_event_cb (GtkWidget *widget,
194 GdkEventKey *event);
195 static gboolean key_release_event_cb (GtkWidget *widget,
196 GdkEventKey *event);
197 static gboolean scroll_event_cb (GtkWidget *widget,
198 GdkEventScroll *event);
199 static gboolean visibility_notify_event_cb(GtkWidget *widget,
200 GdkEventVisibility *event);
201 static void hierarchy_changed_cb (GtkWidget *widget,
202 GtkWidget *previous_toplevel);
203 static gboolean window_state_event_cb (GtkWidget *widget,
204 GdkEventWindowState *event);
205 static void theme_changed_cb (GtkSettings *settings,
206 GParamSpec *pspec,
207 nsWindow *data);
208 static nsWindow* GetFirstNSWindowForGDKWindow (GdkWindow *aGdkWindow);
210 #ifdef __cplusplus
211 extern "C" {
212 #endif /* __cplusplus */
213 #ifdef MOZ_X11
214 static GdkFilterReturn popup_take_focus_filter (GdkXEvent *gdk_xevent,
215 GdkEvent *event,
216 gpointer data);
217 static GdkFilterReturn plugin_window_filter_func (GdkXEvent *gdk_xevent,
218 GdkEvent *event,
219 gpointer data);
220 static GdkFilterReturn plugin_client_message_filter (GdkXEvent *xevent,
221 GdkEvent *event,
222 gpointer data);
223 #endif /* MOZ_X11 */
224 #ifdef __cplusplus
226 #endif /* __cplusplus */
228 static gboolean drag_motion_event_cb (GtkWidget *aWidget,
229 GdkDragContext *aDragContext,
230 gint aX,
231 gint aY,
232 guint aTime,
233 gpointer aData);
234 static void drag_leave_event_cb (GtkWidget *aWidget,
235 GdkDragContext *aDragContext,
236 guint aTime,
237 gpointer aData);
238 static gboolean drag_drop_event_cb (GtkWidget *aWidget,
239 GdkDragContext *aDragContext,
240 gint aX,
241 gint aY,
242 guint aTime,
243 gpointer aData);
244 static void drag_data_received_event_cb(GtkWidget *aWidget,
245 GdkDragContext *aDragContext,
246 gint aX,
247 gint aY,
248 GtkSelectionData *aSelectionData,
249 guint aInfo,
250 guint32 aTime,
251 gpointer aData);
253 /* initialization static functions */
254 static nsresult initialize_prefs (void);
256 static guint32 sLastUserInputTime = GDK_CURRENT_TIME;
257 static guint32 sRetryGrabTime;
259 static NS_DEFINE_IID(kCDragServiceCID, NS_DRAGSERVICE_CID);
261 // The window from which the focus manager asks us to dispatch key events.
262 static nsWindow *gFocusWindow = nullptr;
263 static bool gBlockActivateEvent = false;
264 static bool gGlobalsInitialized = false;
265 static bool gRaiseWindows = true;
266 static nsWindow *gPluginFocusWindow = nullptr;
269 #define NS_WINDOW_TITLE_MAX_LENGTH 4095
271 // If after selecting profile window, the startup fail, please refer to
272 // http://bugzilla.gnome.org/show_bug.cgi?id=88940
274 // needed for imgIContainer cursors
275 // GdkDisplay* was added in 2.2
276 typedef struct _GdkDisplay GdkDisplay;
278 #define kWindowPositionSlop 20
280 // cursor cache
281 static GdkCursor *gCursorCache[eCursorCount];
283 static GtkWidget *gInvisibleContainer = nullptr;
285 // Sometimes this actually also includes the state of the modifier keys, but
286 // only the button state bits are used.
287 static guint gButtonState;
289 // nsAutoRef<pixman_region32> uses nsSimpleRef<> to know how to automatically
290 // destroy regions.
291 template <>
292 class nsSimpleRef<pixman_region32> : public pixman_region32 {
293 protected:
294 typedef pixman_region32 RawRef;
296 nsSimpleRef() { data = nullptr; }
297 nsSimpleRef(const RawRef &aRawRef) : pixman_region32(aRawRef) { }
299 static void Release(pixman_region32& region) {
300 pixman_region32_fini(&region);
302 // Whether this needs to be released:
303 bool HaveResource() const { return data != nullptr; }
305 pixman_region32& get() { return *this; }
308 static inline int32_t
309 GetBitmapStride(int32_t width)
311 #if defined(MOZ_X11) || (MOZ_WIDGET_GTK == 2)
312 return (width+7)/8;
313 #else
314 return cairo_format_stride_for_width(CAIRO_FORMAT_A1, width);
315 #endif
318 static inline bool TimestampIsNewerThan(guint32 a, guint32 b)
320 // Timestamps are just the least significant bits of a monotonically
321 // increasing function, and so the use of unsigned overflow arithmetic.
322 return a - b <= G_MAXUINT32/2;
325 static void
326 UpdateLastInputEventTime(void *aGdkEvent)
328 nsCOMPtr<nsIIdleServiceInternal> idleService =
329 do_GetService("@mozilla.org/widget/idleservice;1");
330 if (idleService) {
331 idleService->ResetIdleTimeOut(0);
334 guint timestamp = gdk_event_get_time(static_cast<GdkEvent*>(aGdkEvent));
335 if (timestamp == GDK_CURRENT_TIME)
336 return;
338 sLastUserInputTime = timestamp;
341 nsWindow::nsWindow()
343 mIsTopLevel = false;
344 mIsDestroyed = false;
345 mNeedsResize = false;
346 mNeedsMove = false;
347 mListenForResizes = false;
348 mIsShown = false;
349 mNeedsShow = false;
350 mEnabled = true;
351 mCreated = false;
353 mContainer = nullptr;
354 mGdkWindow = nullptr;
355 mShell = nullptr;
356 mHasMappedToplevel = false;
357 mIsFullyObscured = false;
358 mRetryPointerGrab = false;
359 mWindowType = eWindowType_child;
360 mSizeState = nsSizeMode_Normal;
361 mLastSizeMode = nsSizeMode_Normal;
362 mSizeConstraints.mMaxSize = GetSafeWindowSize(mSizeConstraints.mMaxSize);
364 #ifdef MOZ_X11
365 mOldFocusWindow = 0;
366 #endif /* MOZ_X11 */
367 mPluginType = PluginType_NONE;
369 if (!gGlobalsInitialized) {
370 gGlobalsInitialized = true;
372 // It's OK if either of these fail, but it may not be one day.
373 initialize_prefs();
376 mLastMotionPressure = 0;
378 #ifdef ACCESSIBILITY
379 mRootAccessible = nullptr;
380 #endif
382 mIsTransparent = false;
383 mTransparencyBitmap = nullptr;
385 mTransparencyBitmapWidth = 0;
386 mTransparencyBitmapHeight = 0;
388 #if GTK_CHECK_VERSION(3,4,0)
389 mLastScrollEventTime = GDK_CURRENT_TIME;
390 #endif
393 nsWindow::~nsWindow()
395 LOG(("nsWindow::~nsWindow() [%p]\n", (void *)this));
397 delete[] mTransparencyBitmap;
398 mTransparencyBitmap = nullptr;
400 Destroy();
403 /* static */ void
404 nsWindow::ReleaseGlobals()
406 for (uint32_t i = 0; i < ArrayLength(gCursorCache); ++i) {
407 if (gCursorCache[i]) {
408 #if (MOZ_WIDGET_GTK == 3)
409 g_object_unref(gCursorCache[i]);
410 #else
411 gdk_cursor_unref(gCursorCache[i]);
412 #endif
413 gCursorCache[i] = nullptr;
418 NS_IMPL_ISUPPORTS_INHERITED(nsWindow, nsBaseWidget,
419 nsISupportsWeakReference)
421 void
422 nsWindow::CommonCreate(nsIWidget *aParent, bool aListenForResizes)
424 mParent = aParent;
425 mListenForResizes = aListenForResizes;
426 mCreated = true;
429 void
430 nsWindow::DispatchActivateEvent(void)
432 NS_ASSERTION(mContainer || mIsDestroyed,
433 "DispatchActivateEvent only intended for container windows");
435 #ifdef ACCESSIBILITY
436 DispatchActivateEventAccessible();
437 #endif //ACCESSIBILITY
439 if (mWidgetListener)
440 mWidgetListener->WindowActivated();
443 void
444 nsWindow::DispatchDeactivateEvent(void)
446 if (mWidgetListener)
447 mWidgetListener->WindowDeactivated();
449 #ifdef ACCESSIBILITY
450 DispatchDeactivateEventAccessible();
451 #endif //ACCESSIBILITY
454 void
455 nsWindow::DispatchResized(int32_t aWidth, int32_t aHeight)
457 nsIWidgetListener *listeners[] =
458 { mWidgetListener, mAttachedWidgetListener };
459 for (size_t i = 0; i < ArrayLength(listeners); ++i) {
460 if (listeners[i]) {
461 listeners[i]->WindowResized(this, aWidth, aHeight);
466 nsresult
467 nsWindow::DispatchEvent(WidgetGUIEvent* aEvent, nsEventStatus& aStatus)
469 #ifdef DEBUG
470 debug_DumpEvent(stdout, aEvent->widget, aEvent,
471 nsAutoCString("something"), 0);
472 #endif
474 aStatus = nsEventStatus_eIgnore;
475 nsIWidgetListener* listener =
476 mAttachedWidgetListener ? mAttachedWidgetListener : mWidgetListener;
477 if (listener) {
478 aStatus = listener->HandleEvent(aEvent, mUseAttachedEvents);
481 return NS_OK;
484 void
485 nsWindow::OnDestroy(void)
487 if (mOnDestroyCalled)
488 return;
490 mOnDestroyCalled = true;
492 // Prevent deletion.
493 nsCOMPtr<nsIWidget> kungFuDeathGrip = this;
495 // release references to children, device context, toolkit + app shell
496 nsBaseWidget::OnDestroy();
498 // Remove association between this object and its parent and siblings.
499 nsBaseWidget::Destroy();
500 mParent = nullptr;
502 NotifyWindowDestroyed();
505 bool
506 nsWindow::AreBoundsSane(void)
508 if (mBounds.width > 0 && mBounds.height > 0)
509 return true;
511 return false;
514 static GtkWidget*
515 EnsureInvisibleContainer()
517 if (!gInvisibleContainer) {
518 // GtkWidgets need to be anchored to a GtkWindow to be realized (to
519 // have a window). Using GTK_WINDOW_POPUP rather than
520 // GTK_WINDOW_TOPLEVEL in the hope that POPUP results in less
521 // initialization and window manager interaction.
522 GtkWidget* window = gtk_window_new(GTK_WINDOW_POPUP);
523 gInvisibleContainer = moz_container_new();
524 gtk_container_add(GTK_CONTAINER(window), gInvisibleContainer);
525 gtk_widget_realize(gInvisibleContainer);
528 return gInvisibleContainer;
531 static void
532 CheckDestroyInvisibleContainer()
534 NS_PRECONDITION(gInvisibleContainer, "oh, no");
536 if (!gdk_window_peek_children(gtk_widget_get_window(gInvisibleContainer))) {
537 // No children, so not in use.
538 // Make sure to destroy the GtkWindow also.
539 gtk_widget_destroy(gtk_widget_get_parent(gInvisibleContainer));
540 gInvisibleContainer = nullptr;
544 // Change the containing GtkWidget on a sub-hierarchy of GdkWindows belonging
545 // to aOldWidget and rooted at aWindow, and reparent any child GtkWidgets of
546 // the GdkWindow hierarchy to aNewWidget.
547 static void
548 SetWidgetForHierarchy(GdkWindow *aWindow,
549 GtkWidget *aOldWidget,
550 GtkWidget *aNewWidget)
552 gpointer data;
553 gdk_window_get_user_data(aWindow, &data);
555 if (data != aOldWidget) {
556 if (!GTK_IS_WIDGET(data))
557 return;
559 GtkWidget* widget = static_cast<GtkWidget*>(data);
560 if (gtk_widget_get_parent(widget) != aOldWidget)
561 return;
563 // This window belongs to a child widget, which will no longer be a
564 // child of aOldWidget.
565 gtk_widget_reparent(widget, aNewWidget);
567 return;
570 GList *children = gdk_window_get_children(aWindow);
571 for(GList *list = children; list; list = list->next) {
572 SetWidgetForHierarchy(GDK_WINDOW(list->data), aOldWidget, aNewWidget);
574 g_list_free(children);
576 gdk_window_set_user_data(aWindow, aNewWidget);
579 // Walk the list of child windows and call destroy on them.
580 void
581 nsWindow::DestroyChildWindows()
583 if (!mGdkWindow)
584 return;
586 while (GList *children = gdk_window_peek_children(mGdkWindow)) {
587 GdkWindow *child = GDK_WINDOW(children->data);
588 nsWindow *kid = get_window_for_gdk_window(child);
589 if (kid) {
590 kid->Destroy();
591 } else {
592 // This child is not an nsWindow.
593 // Destroy the child GtkWidget.
594 gpointer data;
595 gdk_window_get_user_data(child, &data);
596 if (GTK_IS_WIDGET(data)) {
597 gtk_widget_destroy(static_cast<GtkWidget*>(data));
603 NS_IMETHODIMP
604 nsWindow::Destroy(void)
606 if (mIsDestroyed || !mCreated)
607 return NS_OK;
609 LOG(("nsWindow::Destroy [%p]\n", (void *)this));
610 mIsDestroyed = true;
611 mCreated = false;
613 /** Need to clean our LayerManager up while still alive */
614 if (mLayerManager) {
615 mLayerManager->Destroy();
617 mLayerManager = nullptr;
619 // It is safe to call DestroyeCompositor several times (here and
620 // in the parent class) since it will take effect only once.
621 // The reason we call it here is because on gtk platforms we need
622 // to destroy the compositor before we destroy the gdk window (which
623 // destroys the the gl context attached to it).
624 DestroyCompositor();
626 ClearCachedResources();
628 g_signal_handlers_disconnect_by_func(gtk_settings_get_default(),
629 FuncToGpointer(theme_changed_cb),
630 this);
632 nsIRollupListener* rollupListener = nsBaseWidget::GetActiveRollupListener();
633 if (rollupListener) {
634 nsCOMPtr<nsIWidget> rollupWidget = rollupListener->GetRollupWidget();
635 if (static_cast<nsIWidget *>(this) == rollupWidget) {
636 rollupListener->Rollup(0, nullptr, nullptr);
640 // dragService will be null after shutdown of the service manager.
641 nsDragService *dragService = nsDragService::GetInstance();
642 if (dragService && this == dragService->GetMostRecentDestWindow()) {
643 dragService->ScheduleLeaveEvent();
646 NativeShow(false);
648 if (mIMModule) {
649 mIMModule->OnDestroyWindow(this);
652 // make sure that we remove ourself as the focus window
653 if (gFocusWindow == this) {
654 LOGFOCUS(("automatically losing focus...\n"));
655 gFocusWindow = nullptr;
658 #if (MOZ_WIDGET_GTK == 2) && defined(MOZ_X11)
659 // make sure that we remove ourself as the plugin focus window
660 if (gPluginFocusWindow == this) {
661 gPluginFocusWindow->LoseNonXEmbedPluginFocus();
663 #endif /* MOZ_X11 && MOZ_WIDGET_GTK2 */
665 // Destroy thebes surface now. Badness can happen if we destroy
666 // the surface after its X Window.
667 mThebesSurface = nullptr;
669 GtkWidget *owningWidget = GetMozContainerWidget();
670 if (mShell) {
671 gtk_widget_destroy(mShell);
672 mShell = nullptr;
673 mContainer = nullptr;
674 NS_ABORT_IF_FALSE(!mGdkWindow,
675 "mGdkWindow should be NULL when mContainer is destroyed");
677 else if (mContainer) {
678 gtk_widget_destroy(GTK_WIDGET(mContainer));
679 mContainer = nullptr;
680 NS_ABORT_IF_FALSE(!mGdkWindow,
681 "mGdkWindow should be NULL when mContainer is destroyed");
683 else if (mGdkWindow) {
684 // Destroy child windows to ensure that their mThebesSurfaces are
685 // released and to remove references from GdkWindows back to their
686 // container widget. (OnContainerUnrealize() does this when the
687 // MozContainer widget is destroyed.)
688 DestroyChildWindows();
690 gdk_window_set_user_data(mGdkWindow, nullptr);
691 g_object_set_data(G_OBJECT(mGdkWindow), "nsWindow", nullptr);
692 gdk_window_destroy(mGdkWindow);
693 mGdkWindow = nullptr;
696 if (gInvisibleContainer && owningWidget == gInvisibleContainer) {
697 CheckDestroyInvisibleContainer();
700 #ifdef ACCESSIBILITY
701 if (mRootAccessible) {
702 mRootAccessible = nullptr;
704 #endif
706 // Save until last because OnDestroy() may cause us to be deleted.
707 OnDestroy();
709 return NS_OK;
712 nsIWidget *
713 nsWindow::GetParent(void)
715 return mParent;
718 float
719 nsWindow::GetDPI()
721 Display *dpy = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
722 int defaultScreen = DefaultScreen(dpy);
723 double heightInches = DisplayHeightMM(dpy, defaultScreen)/MM_PER_INCH_FLOAT;
724 if (heightInches < 0.25) {
725 // Something's broken, but we'd better not crash.
726 return 96.0f;
728 return float(DisplayHeight(dpy, defaultScreen)/heightInches);
731 NS_IMETHODIMP
732 nsWindow::SetParent(nsIWidget *aNewParent)
734 if (mContainer || !mGdkWindow) {
735 NS_NOTREACHED("nsWindow::SetParent called illegally");
736 return NS_ERROR_NOT_IMPLEMENTED;
739 nsCOMPtr<nsIWidget> kungFuDeathGrip = this;
740 if (mParent) {
741 mParent->RemoveChild(this);
744 mParent = aNewParent;
746 GtkWidget* oldContainer = GetMozContainerWidget();
747 if (!oldContainer) {
748 // The GdkWindows have been destroyed so there is nothing else to
749 // reparent.
750 NS_ABORT_IF_FALSE(gdk_window_is_destroyed(mGdkWindow),
751 "live GdkWindow with no widget");
752 return NS_OK;
755 if (aNewParent) {
756 aNewParent->AddChild(this);
757 ReparentNativeWidget(aNewParent);
758 } else {
759 // aNewParent is nullptr, but reparent to a hidden window to avoid
760 // destroying the GdkWindow and its descendants.
761 // An invisible container widget is needed to hold descendant
762 // GtkWidgets.
763 GtkWidget* newContainer = EnsureInvisibleContainer();
764 GdkWindow* newParentWindow = gtk_widget_get_window(newContainer);
765 ReparentNativeWidgetInternal(aNewParent, newContainer, newParentWindow,
766 oldContainer);
768 return NS_OK;
771 NS_IMETHODIMP
772 nsWindow::ReparentNativeWidget(nsIWidget* aNewParent)
774 NS_PRECONDITION(aNewParent, "");
775 NS_ASSERTION(!mIsDestroyed, "");
776 NS_ASSERTION(!static_cast<nsWindow*>(aNewParent)->mIsDestroyed, "");
778 GtkWidget* oldContainer = GetMozContainerWidget();
779 if (!oldContainer) {
780 // The GdkWindows have been destroyed so there is nothing else to
781 // reparent.
782 NS_ABORT_IF_FALSE(gdk_window_is_destroyed(mGdkWindow),
783 "live GdkWindow with no widget");
784 return NS_OK;
786 NS_ABORT_IF_FALSE(!gdk_window_is_destroyed(mGdkWindow),
787 "destroyed GdkWindow with widget");
789 nsWindow* newParent = static_cast<nsWindow*>(aNewParent);
790 GdkWindow* newParentWindow = newParent->mGdkWindow;
791 GtkWidget* newContainer = newParent->GetMozContainerWidget();
792 GtkWindow* shell = GTK_WINDOW(mShell);
794 if (shell && gtk_window_get_transient_for(shell)) {
795 GtkWindow* topLevelParent =
796 GTK_WINDOW(gtk_widget_get_toplevel(newContainer));
797 gtk_window_set_transient_for(shell, topLevelParent);
800 ReparentNativeWidgetInternal(aNewParent, newContainer, newParentWindow,
801 oldContainer);
802 return NS_OK;
805 void
806 nsWindow::ReparentNativeWidgetInternal(nsIWidget* aNewParent,
807 GtkWidget* aNewContainer,
808 GdkWindow* aNewParentWindow,
809 GtkWidget* aOldContainer)
811 if (!aNewContainer) {
812 // The new parent GdkWindow has been destroyed.
813 NS_ABORT_IF_FALSE(!aNewParentWindow ||
814 gdk_window_is_destroyed(aNewParentWindow),
815 "live GdkWindow with no widget");
816 Destroy();
817 } else {
818 if (aNewContainer != aOldContainer) {
819 NS_ABORT_IF_FALSE(!gdk_window_is_destroyed(aNewParentWindow),
820 "destroyed GdkWindow with widget");
821 SetWidgetForHierarchy(mGdkWindow, aOldContainer, aNewContainer);
823 if (aOldContainer == gInvisibleContainer) {
824 CheckDestroyInvisibleContainer();
828 if (!mIsTopLevel) {
829 gdk_window_reparent(mGdkWindow, aNewParentWindow, mBounds.x,
830 mBounds.y);
834 nsWindow* newParent = static_cast<nsWindow*>(aNewParent);
835 bool parentHasMappedToplevel =
836 newParent && newParent->mHasMappedToplevel;
837 if (mHasMappedToplevel != parentHasMappedToplevel) {
838 SetHasMappedToplevel(parentHasMappedToplevel);
842 NS_IMETHODIMP
843 nsWindow::SetModal(bool aModal)
845 LOG(("nsWindow::SetModal [%p] %d\n", (void *)this, aModal));
846 if (mIsDestroyed)
847 return aModal ? NS_ERROR_NOT_AVAILABLE : NS_OK;
848 if (!mIsTopLevel || !mShell)
849 return NS_ERROR_FAILURE;
850 gtk_window_set_modal(GTK_WINDOW(mShell), aModal ? TRUE : FALSE);
851 return NS_OK;
854 // nsIWidget method, which means IsShown.
855 bool
856 nsWindow::IsVisible() const
858 return mIsShown;
861 NS_IMETHODIMP
862 nsWindow::ConstrainPosition(bool aAllowSlop, int32_t *aX, int32_t *aY)
864 if (mIsTopLevel && mShell) {
865 int32_t screenWidth = gdk_screen_width();
866 int32_t screenHeight = gdk_screen_height();
867 if (aAllowSlop) {
868 if (*aX < (kWindowPositionSlop - mBounds.width))
869 *aX = kWindowPositionSlop - mBounds.width;
870 if (*aX > (screenWidth - kWindowPositionSlop))
871 *aX = screenWidth - kWindowPositionSlop;
872 if (*aY < (kWindowPositionSlop - mBounds.height))
873 *aY = kWindowPositionSlop - mBounds.height;
874 if (*aY > (screenHeight - kWindowPositionSlop))
875 *aY = screenHeight - kWindowPositionSlop;
876 } else {
877 if (*aX < 0)
878 *aX = 0;
879 if (*aX > (screenWidth - mBounds.width))
880 *aX = screenWidth - mBounds.width;
881 if (*aY < 0)
882 *aY = 0;
883 if (*aY > (screenHeight - mBounds.height))
884 *aY = screenHeight - mBounds.height;
887 return NS_OK;
890 void nsWindow::SetSizeConstraints(const SizeConstraints& aConstraints)
892 mSizeConstraints.mMinSize = GetSafeWindowSize(aConstraints.mMinSize);
893 mSizeConstraints.mMaxSize = GetSafeWindowSize(aConstraints.mMaxSize);
895 if (mShell) {
896 GdkGeometry geometry;
897 geometry.min_width = mSizeConstraints.mMinSize.width;
898 geometry.min_height = mSizeConstraints.mMinSize.height;
899 geometry.max_width = mSizeConstraints.mMaxSize.width;
900 geometry.max_height = mSizeConstraints.mMaxSize.height;
902 uint32_t hints = GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE;
903 gtk_window_set_geometry_hints(GTK_WINDOW(mShell), nullptr,
904 &geometry, GdkWindowHints(hints));
908 NS_IMETHODIMP
909 nsWindow::Show(bool aState)
911 if (aState == mIsShown)
912 return NS_OK;
914 // Clear our cached resources when the window is hidden.
915 if (mIsShown && !aState) {
916 ClearCachedResources();
919 mIsShown = aState;
921 LOG(("nsWindow::Show [%p] state %d\n", (void *)this, aState));
923 if (aState) {
924 // Now that this window is shown, mHasMappedToplevel needs to be
925 // tracked on viewable descendants.
926 SetHasMappedToplevel(mHasMappedToplevel);
929 // Ok, someone called show on a window that isn't sized to a sane
930 // value. Mark this window as needing to have Show() called on it
931 // and return.
932 if ((aState && !AreBoundsSane()) || !mCreated) {
933 LOG(("\tbounds are insane or window hasn't been created yet\n"));
934 mNeedsShow = true;
935 return NS_OK;
938 // If someone is hiding this widget, clear any needing show flag.
939 if (!aState)
940 mNeedsShow = false;
942 // If someone is showing this window and it needs a resize then
943 // resize the widget.
944 if (aState) {
945 if (mNeedsMove) {
946 NativeResize(mBounds.x, mBounds.y, mBounds.width, mBounds.height,
947 false);
948 } else if (mNeedsResize) {
949 NativeResize(mBounds.width, mBounds.height, false);
953 #ifdef ACCESSIBILITY
954 if (aState && a11y::ShouldA11yBeEnabled())
955 CreateRootAccessible();
956 #endif
958 NativeShow(aState);
960 return NS_OK;
963 NS_IMETHODIMP
964 nsWindow::Resize(double aWidth, double aHeight, bool aRepaint)
966 CSSToLayoutDeviceScale scale = BoundsUseDisplayPixels() ? GetDefaultScale()
967 : CSSToLayoutDeviceScale(1.0);
968 int32_t width = NSToIntRound(scale.scale * aWidth);
969 int32_t height = NSToIntRound(scale.scale * aHeight);
970 ConstrainSize(&width, &height);
972 // For top-level windows, aWidth and aHeight should possibly be
973 // interpreted as frame bounds, but NativeResize treats these as window
974 // bounds (Bug 581866).
976 mBounds.SizeTo(width, height);
978 if (!mCreated)
979 return NS_OK;
981 // There are several cases here that we need to handle, based on a
982 // matrix of the visibility of the widget, the sanity of this resize
983 // and whether or not the widget was previously sane.
985 // Has this widget been set to visible?
986 if (mIsShown) {
987 // Are the bounds sane?
988 if (AreBoundsSane()) {
989 // Yep? Resize the window
990 //Maybe, the toplevel has moved
992 // Note that if the widget needs to be positioned because its
993 // size was previously insane in Resize(x,y,w,h), then we need
994 // to set the x and y here too, because the widget wasn't
995 // moved back then
996 if (mNeedsMove)
997 NativeResize(mBounds.x, mBounds.y,
998 mBounds.width, mBounds.height, aRepaint);
999 else
1000 NativeResize(mBounds.width, mBounds.height, aRepaint);
1002 // Does it need to be shown because it was previously insane?
1003 if (mNeedsShow)
1004 NativeShow(true);
1006 else {
1007 // If someone has set this so that the needs show flag is false
1008 // and it needs to be hidden, update the flag and hide the
1009 // window. This flag will be cleared the next time someone
1010 // hides the window or shows it. It also prevents us from
1011 // calling NativeShow(false) excessively on the window which
1012 // causes unneeded X traffic.
1013 if (!mNeedsShow) {
1014 mNeedsShow = true;
1015 NativeShow(false);
1019 // If the widget hasn't been shown, mark the widget as needing to be
1020 // resized before it is shown.
1021 else {
1022 if (AreBoundsSane() && mListenForResizes) {
1023 // For widgets that we listen for resizes for (widgets created
1024 // with native parents) we apparently _always_ have to resize. I
1025 // dunno why, but apparently we're lame like that.
1026 NativeResize(width, height, aRepaint);
1028 else {
1029 mNeedsResize = true;
1033 NotifyRollupGeometryChange();
1035 // send a resize notification if this is a toplevel
1036 if (mIsTopLevel || mListenForResizes) {
1037 DispatchResized(width, height);
1040 return NS_OK;
1043 NS_IMETHODIMP
1044 nsWindow::Resize(double aX, double aY, double aWidth, double aHeight,
1045 bool aRepaint)
1047 CSSToLayoutDeviceScale scale = BoundsUseDisplayPixels() ? GetDefaultScale()
1048 : CSSToLayoutDeviceScale(1.0);
1049 int32_t width = NSToIntRound(scale.scale * aWidth);
1050 int32_t height = NSToIntRound(scale.scale * aHeight);
1051 ConstrainSize(&width, &height);
1053 int32_t x = NSToIntRound(scale.scale * aX);
1054 int32_t y = NSToIntRound(scale.scale * aY);
1055 mBounds.x = x;
1056 mBounds.y = y;
1057 mBounds.SizeTo(width, height);
1059 mNeedsMove = true;
1061 if (!mCreated)
1062 return NS_OK;
1064 // There are several cases here that we need to handle, based on a
1065 // matrix of the visibility of the widget, the sanity of this resize
1066 // and whether or not the widget was previously sane.
1068 // Has this widget been set to visible?
1069 if (mIsShown) {
1070 // Are the bounds sane?
1071 if (AreBoundsSane()) {
1072 // Yep? Resize the window
1073 NativeResize(x, y, width, height, aRepaint);
1074 // Does it need to be shown because it was previously insane?
1075 if (mNeedsShow)
1076 NativeShow(true);
1078 else {
1079 // If someone has set this so that the needs show flag is false
1080 // and it needs to be hidden, update the flag and hide the
1081 // window. This flag will be cleared the next time someone
1082 // hides the window or shows it. It also prevents us from
1083 // calling NativeShow(false) excessively on the window which
1084 // causes unneeded X traffic.
1085 if (!mNeedsShow) {
1086 mNeedsShow = true;
1087 NativeShow(false);
1091 // If the widget hasn't been shown, mark the widget as needing to be
1092 // resized before it is shown
1093 else {
1094 if (AreBoundsSane() && mListenForResizes){
1095 // For widgets that we listen for resizes for (widgets created
1096 // with native parents) we apparently _always_ have to resize. I
1097 // dunno why, but apparently we're lame like that.
1098 NativeResize(x, y, width, height, aRepaint);
1100 else {
1101 mNeedsResize = true;
1105 NotifyRollupGeometryChange();
1107 if (mIsTopLevel || mListenForResizes) {
1108 DispatchResized(width, height);
1111 return NS_OK;
1114 NS_IMETHODIMP
1115 nsWindow::Enable(bool aState)
1117 mEnabled = aState;
1119 return NS_OK;
1122 bool
1123 nsWindow::IsEnabled() const
1125 return mEnabled;
1130 NS_IMETHODIMP
1131 nsWindow::Move(double aX, double aY)
1133 LOG(("nsWindow::Move [%p] %f %f\n", (void *)this,
1134 aX, aY));
1136 CSSToLayoutDeviceScale scale = BoundsUseDisplayPixels() ? GetDefaultScale()
1137 : CSSToLayoutDeviceScale(1.0);
1138 int32_t x = NSToIntRound(aX * scale.scale);
1139 int32_t y = NSToIntRound(aY * scale.scale);
1141 if (mWindowType == eWindowType_toplevel ||
1142 mWindowType == eWindowType_dialog) {
1143 SetSizeMode(nsSizeMode_Normal);
1146 // Since a popup window's x/y coordinates are in relation to to
1147 // the parent, the parent might have moved so we always move a
1148 // popup window.
1149 if (x == mBounds.x && y == mBounds.y &&
1150 mWindowType != eWindowType_popup)
1151 return NS_OK;
1153 // XXX Should we do some AreBoundsSane check here?
1155 mBounds.x = x;
1156 mBounds.y = y;
1158 if (!mCreated)
1159 return NS_OK;
1161 mNeedsMove = false;
1163 if (mIsTopLevel) {
1164 gtk_window_move(GTK_WINDOW(mShell), x, y);
1166 else if (mGdkWindow) {
1167 gdk_window_move(mGdkWindow, x, y);
1170 NotifyRollupGeometryChange();
1171 return NS_OK;
1174 NS_IMETHODIMP
1175 nsWindow::PlaceBehind(nsTopLevelWidgetZPlacement aPlacement,
1176 nsIWidget *aWidget,
1177 bool aActivate)
1179 return NS_ERROR_NOT_IMPLEMENTED;
1182 void
1183 nsWindow::SetZIndex(int32_t aZIndex)
1185 nsIWidget* oldPrev = GetPrevSibling();
1187 nsBaseWidget::SetZIndex(aZIndex);
1189 if (GetPrevSibling() == oldPrev) {
1190 return;
1193 NS_ASSERTION(!mContainer, "Expected Mozilla child widget");
1195 // We skip the nsWindows that don't have mGdkWindows.
1196 // These are probably in the process of being destroyed.
1198 if (!GetNextSibling()) {
1199 // We're to be on top.
1200 if (mGdkWindow)
1201 gdk_window_raise(mGdkWindow);
1202 } else {
1203 // All the siblings before us need to be below our widget.
1204 for (nsWindow* w = this; w;
1205 w = static_cast<nsWindow*>(w->GetPrevSibling())) {
1206 if (w->mGdkWindow)
1207 gdk_window_lower(w->mGdkWindow);
1212 NS_IMETHODIMP
1213 nsWindow::SetSizeMode(int32_t aMode)
1215 nsresult rv;
1217 LOG(("nsWindow::SetSizeMode [%p] %d\n", (void *)this, aMode));
1219 // Save the requested state.
1220 rv = nsBaseWidget::SetSizeMode(aMode);
1222 // return if there's no shell or our current state is the same as
1223 // the mode we were just set to.
1224 if (!mShell || mSizeState == mSizeMode) {
1225 return rv;
1228 switch (aMode) {
1229 case nsSizeMode_Maximized:
1230 gtk_window_maximize(GTK_WINDOW(mShell));
1231 break;
1232 case nsSizeMode_Minimized:
1233 gtk_window_iconify(GTK_WINDOW(mShell));
1234 break;
1235 case nsSizeMode_Fullscreen:
1236 MakeFullScreen(true);
1237 break;
1239 default:
1240 // nsSizeMode_Normal, really.
1241 if (mSizeState == nsSizeMode_Minimized)
1242 gtk_window_deiconify(GTK_WINDOW(mShell));
1243 else if (mSizeState == nsSizeMode_Maximized)
1244 gtk_window_unmaximize(GTK_WINDOW(mShell));
1245 break;
1248 mSizeState = mSizeMode;
1250 return rv;
1253 typedef void (* SetUserTimeFunc)(GdkWindow* aWindow, guint32 aTimestamp);
1255 // This will become obsolete when new GTK APIs are widely supported,
1256 // as described here: http://bugzilla.gnome.org/show_bug.cgi?id=347375
1257 static void
1258 SetUserTimeAndStartupIDForActivatedWindow(GtkWidget* aWindow)
1260 nsGTKToolkit* GTKToolkit = nsGTKToolkit::GetToolkit();
1261 if (!GTKToolkit)
1262 return;
1264 nsAutoCString desktopStartupID;
1265 GTKToolkit->GetDesktopStartupID(&desktopStartupID);
1266 if (desktopStartupID.IsEmpty()) {
1267 // We don't have the data we need. Fall back to an
1268 // approximation ... using the timestamp of the remote command
1269 // being received as a guess for the timestamp of the user event
1270 // that triggered it.
1271 uint32_t timestamp = GTKToolkit->GetFocusTimestamp();
1272 if (timestamp) {
1273 gdk_window_focus(gtk_widget_get_window(aWindow), timestamp);
1274 GTKToolkit->SetFocusTimestamp(0);
1276 return;
1279 #if defined(MOZ_ENABLE_STARTUP_NOTIFICATION)
1280 GdkWindow* gdkWindow = gtk_widget_get_window(aWindow);
1282 GdkScreen* screen = gdk_window_get_screen(gdkWindow);
1283 SnDisplay* snd =
1284 sn_display_new(gdk_x11_display_get_xdisplay(gdk_window_get_display(gdkWindow)),
1285 nullptr, nullptr);
1286 if (!snd)
1287 return;
1288 SnLauncheeContext* ctx =
1289 sn_launchee_context_new(snd, gdk_screen_get_number(screen),
1290 desktopStartupID.get());
1291 if (!ctx) {
1292 sn_display_unref(snd);
1293 return;
1296 if (sn_launchee_context_get_id_has_timestamp(ctx)) {
1297 PRLibrary* gtkLibrary;
1298 SetUserTimeFunc setUserTimeFunc = (SetUserTimeFunc)
1299 PR_FindFunctionSymbolAndLibrary("gdk_x11_window_set_user_time", &gtkLibrary);
1300 if (setUserTimeFunc) {
1301 setUserTimeFunc(gdkWindow, sn_launchee_context_get_timestamp(ctx));
1302 PR_UnloadLibrary(gtkLibrary);
1306 sn_launchee_context_setup_window(ctx, gdk_x11_window_get_xid(gdkWindow));
1307 sn_launchee_context_complete(ctx);
1309 sn_launchee_context_unref(ctx);
1310 sn_display_unref(snd);
1311 #endif
1313 // If we used the startup ID, that already contains the focus timestamp;
1314 // we don't want to reuse the timestamp next time we raise the window
1315 GTKToolkit->SetFocusTimestamp(0);
1316 GTKToolkit->SetDesktopStartupID(EmptyCString());
1319 /* static */ guint32
1320 nsWindow::GetLastUserInputTime()
1322 // gdk_x11_display_get_user_time tracks button and key presses,
1323 // DESKTOP_STARTUP_ID used to start the app, drop events from external
1324 // drags, WM_DELETE_WINDOW delete events, but not usually mouse motion nor
1325 // button and key releases. Therefore use the most recent of
1326 // gdk_x11_display_get_user_time and the last time that we have seen.
1327 guint32 timestamp =
1328 gdk_x11_display_get_user_time(gdk_display_get_default());
1329 if (sLastUserInputTime != GDK_CURRENT_TIME &&
1330 TimestampIsNewerThan(sLastUserInputTime, timestamp)) {
1331 return sLastUserInputTime;
1334 return timestamp;
1337 NS_IMETHODIMP
1338 nsWindow::SetFocus(bool aRaise)
1340 // Make sure that our owning widget has focus. If it doesn't try to
1341 // grab it. Note that we don't set our focus flag in this case.
1343 LOGFOCUS((" SetFocus %d [%p]\n", aRaise, (void *)this));
1345 GtkWidget *owningWidget = GetMozContainerWidget();
1346 if (!owningWidget)
1347 return NS_ERROR_FAILURE;
1349 // Raise the window if someone passed in true and the prefs are
1350 // set properly.
1351 GtkWidget *toplevelWidget = gtk_widget_get_toplevel(owningWidget);
1353 if (gRaiseWindows && aRaise && toplevelWidget &&
1354 !gtk_widget_has_focus(owningWidget) &&
1355 !gtk_widget_has_focus(toplevelWidget)) {
1356 GtkWidget* top_window = GetToplevelWidget();
1357 if (top_window && (gtk_widget_get_visible(top_window)))
1359 gdk_window_show_unraised(gtk_widget_get_window(top_window));
1360 // Unset the urgency hint if possible.
1361 SetUrgencyHint(top_window, false);
1365 nsRefPtr<nsWindow> owningWindow = get_window_for_gtk_widget(owningWidget);
1366 if (!owningWindow)
1367 return NS_ERROR_FAILURE;
1369 if (aRaise) {
1370 // aRaise == true means request toplevel activation.
1372 // This is asynchronous.
1373 // If and when the window manager accepts the request, then the focus
1374 // widget will get a focus-in-event signal.
1375 if (gRaiseWindows && owningWindow->mIsShown && owningWindow->mShell &&
1376 !gtk_window_is_active(GTK_WINDOW(owningWindow->mShell))) {
1378 uint32_t timestamp = GDK_CURRENT_TIME;
1380 nsGTKToolkit* GTKToolkit = nsGTKToolkit::GetToolkit();
1381 if (GTKToolkit)
1382 timestamp = GTKToolkit->GetFocusTimestamp();
1384 LOGFOCUS((" requesting toplevel activation [%p]\n", (void *)this));
1385 NS_ASSERTION(owningWindow->mWindowType != eWindowType_popup
1386 || mParent,
1387 "Presenting an override-redirect window");
1388 gtk_window_present_with_time(GTK_WINDOW(owningWindow->mShell), timestamp);
1390 if (GTKToolkit)
1391 GTKToolkit->SetFocusTimestamp(0);
1394 return NS_OK;
1397 // aRaise == false means that keyboard events should be dispatched
1398 // from this widget.
1400 // Ensure owningWidget is the focused GtkWidget within its toplevel window.
1402 // For eWindowType_popup, this GtkWidget may not actually be the one that
1403 // receives the key events as it may be the parent window that is active.
1404 if (!gtk_widget_is_focus(owningWidget)) {
1405 // This is synchronous. It takes focus from a plugin or from a widget
1406 // in an embedder. The focus manager already knows that this window
1407 // is active so gBlockActivateEvent avoids another (unnecessary)
1408 // activate notification.
1409 gBlockActivateEvent = true;
1410 gtk_widget_grab_focus(owningWidget);
1411 gBlockActivateEvent = false;
1414 // If this is the widget that already has focus, return.
1415 if (gFocusWindow == this) {
1416 LOGFOCUS((" already have focus [%p]\n", (void *)this));
1417 return NS_OK;
1420 // Set this window to be the focused child window
1421 gFocusWindow = this;
1423 if (mIMModule) {
1424 mIMModule->OnFocusWindow(this);
1427 LOGFOCUS((" widget now has focus in SetFocus() [%p]\n",
1428 (void *)this));
1430 return NS_OK;
1433 NS_IMETHODIMP
1434 nsWindow::GetScreenBounds(nsIntRect &aRect)
1436 if (mIsTopLevel && mContainer) {
1437 // use the point including window decorations
1438 gint x, y;
1439 gdk_window_get_root_origin(gtk_widget_get_window(GTK_WIDGET(mContainer)), &x, &y);
1440 aRect.MoveTo(x, y);
1442 else {
1443 aRect.MoveTo(WidgetToScreenOffset());
1445 // mBounds.Size() is the window bounds, not the window-manager frame
1446 // bounds (bug 581863). gdk_window_get_frame_extents would give the
1447 // frame bounds, but mBounds.Size() is returned here for consistency
1448 // with Resize.
1449 aRect.SizeTo(mBounds.Size());
1450 LOG(("GetScreenBounds %d,%d | %dx%d\n",
1451 aRect.x, aRect.y, aRect.width, aRect.height));
1452 return NS_OK;
1455 NS_IMETHODIMP
1456 nsWindow::GetClientBounds(nsIntRect &aRect)
1458 // GetBounds returns a rect whose top left represents the top left of the
1459 // outer bounds, but whose width/height represent the size of the inner
1460 // bounds (which is messed up).
1461 GetBounds(aRect);
1462 aRect.MoveBy(GetClientOffset());
1464 return NS_OK;
1467 nsIntPoint
1468 nsWindow::GetClientOffset()
1470 if (!mIsTopLevel) {
1471 return nsIntPoint(0, 0);
1474 GdkAtom cardinal_atom = gdk_x11_xatom_to_atom(XA_CARDINAL);
1476 GdkAtom type_returned;
1477 int format_returned;
1478 int length_returned;
1479 long *frame_extents;
1480 GdkWindow* window;
1482 if (!mShell || !(window = gtk_widget_get_window(mShell)) ||
1483 !gdk_property_get(window,
1484 gdk_atom_intern ("_NET_FRAME_EXTENTS", FALSE),
1485 cardinal_atom,
1486 0, // offset
1487 4*4, // length
1488 FALSE, // delete
1489 &type_returned,
1490 &format_returned,
1491 &length_returned,
1492 (guchar **) &frame_extents) ||
1493 length_returned/sizeof(glong) != 4) {
1495 return nsIntPoint(0, 0);
1498 // data returned is in the order left, right, top, bottom
1499 int32_t left = int32_t(frame_extents[0]);
1500 int32_t top = int32_t(frame_extents[2]);
1502 g_free(frame_extents);
1504 return nsIntPoint(left, top);
1507 NS_IMETHODIMP
1508 nsWindow::SetCursor(nsCursor aCursor)
1510 // if we're not the toplevel window pass up the cursor request to
1511 // the toplevel window to handle it.
1512 if (!mContainer && mGdkWindow) {
1513 nsWindow *window = GetContainerWindow();
1514 if (!window)
1515 return NS_ERROR_FAILURE;
1517 return window->SetCursor(aCursor);
1520 // Only change cursor if it's actually been changed
1521 if (aCursor != mCursor || mUpdateCursor) {
1522 GdkCursor *newCursor = nullptr;
1523 mUpdateCursor = false;
1525 newCursor = get_gtk_cursor(aCursor);
1527 if (nullptr != newCursor) {
1528 mCursor = aCursor;
1530 if (!mContainer)
1531 return NS_OK;
1533 gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(mContainer)), newCursor);
1537 return NS_OK;
1540 NS_IMETHODIMP
1541 nsWindow::SetCursor(imgIContainer* aCursor,
1542 uint32_t aHotspotX, uint32_t aHotspotY)
1544 // if we're not the toplevel window pass up the cursor request to
1545 // the toplevel window to handle it.
1546 if (!mContainer && mGdkWindow) {
1547 nsWindow *window = GetContainerWindow();
1548 if (!window)
1549 return NS_ERROR_FAILURE;
1551 return window->SetCursor(aCursor, aHotspotX, aHotspotY);
1554 mCursor = nsCursor(-1);
1556 // Get the image's current frame
1557 GdkPixbuf* pixbuf = nsImageToPixbuf::ImageToPixbuf(aCursor);
1558 if (!pixbuf)
1559 return NS_ERROR_NOT_AVAILABLE;
1561 int width = gdk_pixbuf_get_width(pixbuf);
1562 int height = gdk_pixbuf_get_height(pixbuf);
1563 // Reject cursors greater than 128 pixels in some direction, to prevent
1564 // spoofing.
1565 // XXX ideally we should rescale. Also, we could modify the API to
1566 // allow trusted content to set larger cursors.
1567 if (width > 128 || height > 128) {
1568 g_object_unref(pixbuf);
1569 return NS_ERROR_NOT_AVAILABLE;
1572 // Looks like all cursors need an alpha channel (tested on Gtk 2.4.4). This
1573 // is of course not documented anywhere...
1574 // So add one if there isn't one yet
1575 if (!gdk_pixbuf_get_has_alpha(pixbuf)) {
1576 GdkPixbuf* alphaBuf = gdk_pixbuf_add_alpha(pixbuf, FALSE, 0, 0, 0);
1577 g_object_unref(pixbuf);
1578 if (!alphaBuf) {
1579 return NS_ERROR_OUT_OF_MEMORY;
1581 pixbuf = alphaBuf;
1584 GdkCursor* cursor = gdk_cursor_new_from_pixbuf(gdk_display_get_default(),
1585 pixbuf,
1586 aHotspotX, aHotspotY);
1587 g_object_unref(pixbuf);
1588 nsresult rv = NS_ERROR_OUT_OF_MEMORY;
1589 if (cursor) {
1590 if (mContainer) {
1591 gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(mContainer)), cursor);
1592 rv = NS_OK;
1594 #if (MOZ_WIDGET_GTK == 3)
1595 g_object_unref(cursor);
1596 #else
1597 gdk_cursor_unref(cursor);
1598 #endif
1601 return rv;
1604 NS_IMETHODIMP
1605 nsWindow::Invalidate(const nsIntRect &aRect)
1607 if (!mGdkWindow)
1608 return NS_OK;
1610 GdkRectangle rect;
1611 rect.x = aRect.x;
1612 rect.y = aRect.y;
1613 rect.width = aRect.width;
1614 rect.height = aRect.height;
1616 LOGDRAW(("Invalidate (rect) [%p]: %d %d %d %d\n", (void *)this,
1617 rect.x, rect.y, rect.width, rect.height));
1619 gdk_window_invalidate_rect(mGdkWindow, &rect, FALSE);
1621 return NS_OK;
1624 void*
1625 nsWindow::GetNativeData(uint32_t aDataType)
1627 switch (aDataType) {
1628 case NS_NATIVE_WINDOW:
1629 case NS_NATIVE_WIDGET: {
1630 if (!mGdkWindow)
1631 return nullptr;
1633 return mGdkWindow;
1634 break;
1637 case NS_NATIVE_PLUGIN_PORT:
1638 return SetupPluginPort();
1639 break;
1641 case NS_NATIVE_DISPLAY:
1642 #ifdef MOZ_X11
1643 return GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
1644 #else
1645 return nullptr;
1646 #endif /* MOZ_X11 */
1647 break;
1649 case NS_NATIVE_SHELLWIDGET:
1650 return GetToplevelWidget();
1652 case NS_NATIVE_SHAREABLE_WINDOW:
1653 return (void *) GDK_WINDOW_XID(gdk_window_get_toplevel(mGdkWindow));
1655 default:
1656 NS_WARNING("nsWindow::GetNativeData called with bad value");
1657 return nullptr;
1661 NS_IMETHODIMP
1662 nsWindow::SetTitle(const nsAString& aTitle)
1664 if (!mShell)
1665 return NS_OK;
1667 // convert the string into utf8 and set the title.
1668 #define UTF8_FOLLOWBYTE(ch) (((ch) & 0xC0) == 0x80)
1669 NS_ConvertUTF16toUTF8 titleUTF8(aTitle);
1670 if (titleUTF8.Length() > NS_WINDOW_TITLE_MAX_LENGTH) {
1671 // Truncate overlong titles (bug 167315). Make sure we chop after a
1672 // complete sequence by making sure the next char isn't a follow-byte.
1673 uint32_t len = NS_WINDOW_TITLE_MAX_LENGTH;
1674 while(UTF8_FOLLOWBYTE(titleUTF8[len]))
1675 --len;
1676 titleUTF8.Truncate(len);
1678 gtk_window_set_title(GTK_WINDOW(mShell), (const char *)titleUTF8.get());
1680 return NS_OK;
1683 NS_IMETHODIMP
1684 nsWindow::SetIcon(const nsAString& aIconSpec)
1686 if (!mShell)
1687 return NS_OK;
1689 nsAutoCString iconName;
1691 if (aIconSpec.EqualsLiteral("default")) {
1692 nsXPIDLString brandName;
1693 GetBrandName(brandName);
1694 AppendUTF16toUTF8(brandName, iconName);
1695 ToLowerCase(iconName);
1696 } else {
1697 AppendUTF16toUTF8(aIconSpec, iconName);
1700 nsCOMPtr<nsIFile> iconFile;
1701 nsAutoCString path;
1703 gint *iconSizes =
1704 gtk_icon_theme_get_icon_sizes(gtk_icon_theme_get_default(),
1705 iconName.get());
1706 bool foundIcon = (iconSizes[0] != 0);
1707 g_free(iconSizes);
1709 if (!foundIcon) {
1710 // Look for icons with the following suffixes appended to the base name
1711 // The last two entries (for the old XPM format) will be ignored unless
1712 // no icons are found using other suffixes. XPM icons are deprecated.
1714 const char extensions[6][7] = { ".png", "16.png", "32.png", "48.png",
1715 ".xpm", "16.xpm" };
1717 for (uint32_t i = 0; i < ArrayLength(extensions); i++) {
1718 // Don't bother looking for XPM versions if we found a PNG.
1719 if (i == ArrayLength(extensions) - 2 && foundIcon)
1720 break;
1722 nsAutoString extension;
1723 extension.AppendASCII(extensions[i]);
1725 ResolveIconName(aIconSpec, extension, getter_AddRefs(iconFile));
1726 if (iconFile) {
1727 iconFile->GetNativePath(path);
1728 GdkPixbuf *icon = gdk_pixbuf_new_from_file(path.get(), nullptr);
1729 if (icon) {
1730 gtk_icon_theme_add_builtin_icon(iconName.get(),
1731 gdk_pixbuf_get_height(icon),
1732 icon);
1733 g_object_unref(icon);
1734 foundIcon = true;
1740 // leave the default icon intact if no matching icons were found
1741 if (foundIcon) {
1742 gtk_window_set_icon_name(GTK_WINDOW(mShell), iconName.get());
1745 return NS_OK;
1749 nsIntPoint
1750 nsWindow::WidgetToScreenOffset()
1752 gint x = 0, y = 0;
1754 if (mGdkWindow) {
1755 gdk_window_get_origin(mGdkWindow, &x, &y);
1758 return nsIntPoint(x, y);
1761 NS_IMETHODIMP
1762 nsWindow::EnableDragDrop(bool aEnable)
1764 return NS_OK;
1767 NS_IMETHODIMP
1768 nsWindow::CaptureMouse(bool aCapture)
1770 LOG(("CaptureMouse %p\n", (void *)this));
1772 if (!mGdkWindow)
1773 return NS_OK;
1775 if (!mShell)
1776 return NS_ERROR_FAILURE;
1778 if (aCapture) {
1779 gtk_grab_add(mShell);
1780 GrabPointer(GetLastUserInputTime());
1782 else {
1783 ReleaseGrabs();
1784 gtk_grab_remove(mShell);
1787 return NS_OK;
1790 NS_IMETHODIMP
1791 nsWindow::CaptureRollupEvents(nsIRollupListener *aListener,
1792 bool aDoCapture)
1794 if (!mGdkWindow)
1795 return NS_OK;
1797 if (!mShell)
1798 return NS_ERROR_FAILURE;
1800 LOG(("CaptureRollupEvents %p %i\n", this, int(aDoCapture)));
1802 if (aDoCapture) {
1803 gRollupListener = aListener;
1804 // real grab is only done when there is no dragging
1805 if (!nsWindow::DragInProgress()) {
1806 // This widget grab ensures that a Gecko GtkWidget receives mouse
1807 // events even when embedded in non-Gecko-owned GtkWidgets.
1808 // The grab is placed on the toplevel GtkWindow instead of the
1809 // MozContainer to avoid double dispatch of keyboard events
1810 // (bug 707623).
1811 gtk_grab_add(mShell);
1812 GrabPointer(GetLastUserInputTime());
1815 else {
1816 if (!nsWindow::DragInProgress()) {
1817 ReleaseGrabs();
1819 // There may not have been a drag in process when aDoCapture was set,
1820 // so make sure to remove any added grab. This is a no-op if the grab
1821 // was not added to this widget.
1822 gtk_grab_remove(mShell);
1823 gRollupListener = nullptr;
1826 return NS_OK;
1829 NS_IMETHODIMP
1830 nsWindow::GetAttention(int32_t aCycleCount)
1832 LOG(("nsWindow::GetAttention [%p]\n", (void *)this));
1834 GtkWidget* top_window = GetToplevelWidget();
1835 GtkWidget* top_focused_window =
1836 gFocusWindow ? gFocusWindow->GetToplevelWidget() : nullptr;
1838 // Don't get attention if the window is focused anyway.
1839 if (top_window && (gtk_widget_get_visible(top_window)) &&
1840 top_window != top_focused_window) {
1841 SetUrgencyHint(top_window, true);
1844 return NS_OK;
1847 bool
1848 nsWindow::HasPendingInputEvent()
1850 // This sucks, but gtk/gdk has no way to answer the question we want while
1851 // excluding paint events, and there's no X API that will let us peek
1852 // without blocking or removing. To prevent event reordering, peek
1853 // anything except expose events. Reordering expose and others should be
1854 // ok, hopefully.
1855 bool haveEvent;
1856 #ifdef MOZ_X11
1857 XEvent ev;
1858 Display *display = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
1859 haveEvent =
1860 XCheckMaskEvent(display,
1861 KeyPressMask | KeyReleaseMask | ButtonPressMask |
1862 ButtonReleaseMask | EnterWindowMask | LeaveWindowMask |
1863 PointerMotionMask | PointerMotionHintMask |
1864 Button1MotionMask | Button2MotionMask |
1865 Button3MotionMask | Button4MotionMask |
1866 Button5MotionMask | ButtonMotionMask | KeymapStateMask |
1867 VisibilityChangeMask | StructureNotifyMask |
1868 ResizeRedirectMask | SubstructureNotifyMask |
1869 SubstructureRedirectMask | FocusChangeMask |
1870 PropertyChangeMask | ColormapChangeMask |
1871 OwnerGrabButtonMask, &ev);
1872 if (haveEvent) {
1873 XPutBackEvent(display, &ev);
1875 #else
1876 haveEvent = false;
1877 #endif
1878 return haveEvent;
1881 #if 0
1882 #ifdef DEBUG
1883 // Paint flashing code (disabled for cairo - see below)
1885 #define CAPS_LOCK_IS_ON \
1886 (KeymapWrapper::AreModifiersCurrentlyActive(KeymapWrapper::CAPS_LOCK))
1888 #define WANT_PAINT_FLASHING \
1889 (debug_WantPaintFlashing() && CAPS_LOCK_IS_ON)
1891 #ifdef MOZ_X11
1892 static void
1893 gdk_window_flash(GdkWindow * aGdkWindow,
1894 unsigned int aTimes,
1895 unsigned int aInterval, // Milliseconds
1896 GdkRegion * aRegion)
1898 gint x;
1899 gint y;
1900 gint width;
1901 gint height;
1902 guint i;
1903 GdkGC * gc = 0;
1904 GdkColor white;
1906 #if (MOZ_WIDGET_GTK == 2)
1907 gdk_window_get_geometry(aGdkWindow,nullptr,nullptr,&width,&height,nullptr);
1908 #else
1909 gdk_window_get_geometry(aGdkWindow,nullptr,nullptr,&width,&height);
1910 #endif
1912 gdk_window_get_origin (aGdkWindow,
1914 &y);
1916 gc = gdk_gc_new(gdk_get_default_root_window());
1918 white.pixel = WhitePixel(gdk_display,DefaultScreen(gdk_display));
1920 gdk_gc_set_foreground(gc,&white);
1921 gdk_gc_set_function(gc,GDK_XOR);
1922 gdk_gc_set_subwindow(gc,GDK_INCLUDE_INFERIORS);
1924 gdk_region_offset(aRegion, x, y);
1925 gdk_gc_set_clip_region(gc, aRegion);
1928 * Need to do this twice so that the XOR effect can replace
1929 * the original window contents.
1931 for (i = 0; i < aTimes * 2; i++)
1933 gdk_draw_rectangle(gdk_get_default_root_window(),
1935 TRUE,
1938 width,
1939 height);
1941 gdk_flush();
1943 PR_Sleep(PR_MillisecondsToInterval(aInterval));
1946 gdk_gc_destroy(gc);
1948 gdk_region_offset(aRegion, -x, -y);
1950 #endif /* MOZ_X11 */
1951 #endif // DEBUG
1952 #endif
1954 struct ExposeRegion
1956 nsIntRegion mRegion;
1958 #if (MOZ_WIDGET_GTK == 2)
1959 GdkRectangle *mRects;
1960 GdkRectangle *mRectsEnd;
1962 ExposeRegion() : mRects(nullptr)
1965 ~ExposeRegion()
1967 g_free(mRects);
1969 bool Init(GdkEventExpose *aEvent)
1971 gint nrects;
1972 gdk_region_get_rectangles(aEvent->region, &mRects, &nrects);
1974 if (nrects > MAX_RECTS_IN_REGION) {
1975 // Just use the bounding box
1976 mRects[0] = aEvent->area;
1977 nrects = 1;
1980 mRectsEnd = mRects + nrects;
1982 for (GdkRectangle *r = mRects; r < mRectsEnd; r++) {
1983 mRegion.Or(mRegion, nsIntRect(r->x, r->y, r->width, r->height));
1984 LOGDRAW(("\t%d %d %d %d\n", r->x, r->y, r->width, r->height));
1986 return true;
1989 #else
1990 # ifdef cairo_copy_clip_rectangle_list
1991 # error "Looks like we're including Mozilla's cairo instead of system cairo"
1992 # endif
1993 cairo_rectangle_list_t *mRects;
1995 ExposeRegion() : mRects(nullptr)
1998 ~ExposeRegion()
2000 cairo_rectangle_list_destroy(mRects);
2002 bool Init(cairo_t* cr)
2004 mRects = cairo_copy_clip_rectangle_list(cr);
2005 if (mRects->status != CAIRO_STATUS_SUCCESS) {
2006 NS_WARNING("Failed to obtain cairo rectangle list.");
2007 return false;
2010 for (int i = 0; i < mRects->num_rectangles; i++) {
2011 const cairo_rectangle_t& r = mRects->rectangles[i];
2012 mRegion.Or(mRegion, nsIntRect(r.x, r.y, r.width, r.height));
2013 LOGDRAW(("\t%d %d %d %d\n", r.x, r.y, r.width, r.height));
2015 return true;
2017 #endif
2020 #if (MOZ_WIDGET_GTK == 2)
2021 gboolean
2022 nsWindow::OnExposeEvent(GdkEventExpose *aEvent)
2023 #else
2024 gboolean
2025 nsWindow::OnExposeEvent(cairo_t *cr)
2026 #endif
2028 if (mIsDestroyed) {
2029 return FALSE;
2032 // Windows that are not visible will be painted after they become visible.
2033 if (!mGdkWindow || mIsFullyObscured || !mHasMappedToplevel)
2034 return FALSE;
2036 nsIWidgetListener *listener =
2037 mAttachedWidgetListener ? mAttachedWidgetListener : mWidgetListener;
2038 if (!listener)
2039 return FALSE;
2041 ExposeRegion exposeRegion;
2042 #if (MOZ_WIDGET_GTK == 2)
2043 if (!exposeRegion.Init(aEvent)) {
2044 #else
2045 if (!exposeRegion.Init(cr)) {
2046 #endif
2047 return FALSE;
2050 nsIntRegion &region = exposeRegion.mRegion;
2052 ClientLayerManager *clientLayers =
2053 (GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_CLIENT)
2054 ? static_cast<ClientLayerManager*>(GetLayerManager())
2055 : nullptr;
2057 if (clientLayers && mCompositorParent) {
2058 // We need to paint to the screen even if nothing changed, since if we
2059 // don't have a compositing window manager, our pixels could be stale.
2060 clientLayers->SetNeedsComposite(true);
2061 clientLayers->SendInvalidRegion(region);
2064 // Dispatch WillPaintWindow notification to allow scripts etc. to run
2065 // before we paint
2067 listener->WillPaintWindow(this);
2069 // If the window has been destroyed during the will paint notification,
2070 // there is nothing left to do.
2071 if (!mGdkWindow)
2072 return TRUE;
2074 // Re-get the listener since the will paint notification might have
2075 // killed it.
2076 listener =
2077 mAttachedWidgetListener ? mAttachedWidgetListener : mWidgetListener;
2078 if (!listener)
2079 return FALSE;
2082 if (clientLayers && mCompositorParent && clientLayers->NeedsComposite()) {
2083 mCompositorParent->ScheduleRenderOnCompositorThread();
2084 clientLayers->SetNeedsComposite(false);
2087 LOGDRAW(("sending expose event [%p] %p 0x%lx (rects follow):\n",
2088 (void *)this, (void *)mGdkWindow,
2089 gdk_x11_window_get_xid(mGdkWindow)));
2091 // Our bounds may have changed after calling WillPaintWindow. Clip
2092 // to the new bounds here. The region is relative to this
2093 // window.
2094 region.And(region, nsIntRect(0, 0, mBounds.width, mBounds.height));
2096 bool shaped = false;
2097 if (eTransparencyTransparent == GetTransparencyMode()) {
2098 GdkScreen *screen = gdk_window_get_screen(mGdkWindow);
2099 if (gdk_screen_is_composited(screen) &&
2100 gdk_window_get_visual(mGdkWindow) ==
2101 gdk_screen_get_rgba_visual(screen)) {
2102 // Remove possible shape mask from when window manger was not
2103 // previously compositing.
2104 static_cast<nsWindow*>(GetTopLevelWidget())->
2105 ClearTransparencyBitmap();
2106 } else {
2107 shaped = true;
2111 if (!shaped) {
2112 GList *children =
2113 gdk_window_peek_children(mGdkWindow);
2114 while (children) {
2115 GdkWindow *gdkWin = GDK_WINDOW(children->data);
2116 nsWindow *kid = get_window_for_gdk_window(gdkWin);
2117 if (kid && gdk_window_is_visible(gdkWin)) {
2118 nsAutoTArray<nsIntRect,1> clipRects;
2119 kid->GetWindowClipRegion(&clipRects);
2120 nsIntRect bounds;
2121 kid->GetBounds(bounds);
2122 for (uint32_t i = 0; i < clipRects.Length(); ++i) {
2123 nsIntRect r = clipRects[i] + bounds.TopLeft();
2124 region.Sub(region, r);
2127 children = children->next;
2131 if (region.IsEmpty()) {
2132 return TRUE;
2135 // If this widget uses OMTC...
2136 if (GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_CLIENT) {
2137 listener->PaintWindow(this, region);
2138 listener->DidPaintWindow();
2139 return TRUE;
2142 gfxASurface* surf;
2143 #if (MOZ_WIDGET_GTK == 2)
2144 surf = GetThebesSurface();
2145 #else
2146 surf = GetThebesSurface(cr);
2147 #endif
2149 nsRefPtr<gfxContext> ctx;
2150 if (gfxPlatform::GetPlatform()->
2151 SupportsAzureContentForType(BackendType::CAIRO)) {
2152 IntSize intSize(surf->GetSize().width, surf->GetSize().height);
2153 RefPtr<DrawTarget> dt =
2154 gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(surf, intSize);
2155 ctx = new gfxContext(dt);
2156 } else if (gfxPlatform::GetPlatform()->
2157 SupportsAzureContentForType(BackendType::SKIA) &&
2158 surf->GetType() == gfxSurfaceType::Image) {
2159 gfxImageSurface* imgSurf = static_cast<gfxImageSurface*>(surf);
2160 SurfaceFormat format = ImageFormatToSurfaceFormat(imgSurf->Format());
2161 IntSize intSize(surf->GetSize().width, surf->GetSize().height);
2162 RefPtr<DrawTarget> dt =
2163 gfxPlatform::GetPlatform()->CreateDrawTargetForData(
2164 imgSurf->Data(), intSize, imgSurf->Stride(), format);
2165 ctx = new gfxContext(dt);
2166 } else {
2167 MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unexpected content type");
2170 #ifdef MOZ_X11
2171 nsIntRect boundsRect; // for shaped only
2173 ctx->NewPath();
2174 if (shaped) {
2175 // Collapse update area to the bounding box. This is so we only have to
2176 // call UpdateTranslucentWindowAlpha once. After we have dropped
2177 // support for non-Thebes graphics, UpdateTranslucentWindowAlpha will be
2178 // our private interface so we can rework things to avoid this.
2179 boundsRect = region.GetBounds();
2180 ctx->Rectangle(gfxRect(boundsRect.x, boundsRect.y,
2181 boundsRect.width, boundsRect.height));
2182 } else {
2183 gfxUtils::PathFromRegion(ctx, region);
2185 ctx->Clip();
2187 BufferMode layerBuffering;
2188 if (shaped) {
2189 // The double buffering is done here to extract the shape mask.
2190 // (The shape mask won't be necessary when a visual with an alpha
2191 // channel is used on compositing window managers.)
2192 layerBuffering = mozilla::layers::BufferMode::BUFFER_NONE;
2193 ctx->PushGroup(gfxContentType::COLOR_ALPHA);
2194 #ifdef MOZ_HAVE_SHMIMAGE
2195 } else if (nsShmImage::UseShm()) {
2196 // We're using an xshm mapping as a back buffer.
2197 layerBuffering = mozilla::layers::BufferMode::BUFFER_NONE;
2198 #endif // MOZ_HAVE_SHMIMAGE
2199 } else {
2200 // Get the layer manager to do double buffering (if necessary).
2201 layerBuffering = mozilla::layers::BufferMode::BUFFERED;
2204 #if 0
2205 // NOTE: Paint flashing region would be wrong for cairo, since
2206 // cairo inflates the update region, etc. So don't paint flash
2207 // for cairo.
2208 #ifdef DEBUG
2209 // XXX aEvent->region may refer to a newly-invalid area. FIXME
2210 if (0 && WANT_PAINT_FLASHING && gtk_widget_get_window(aEvent))
2211 gdk_window_flash(mGdkWindow, 1, 100, aEvent->region);
2212 #endif
2213 #endif
2215 #endif // MOZ_X11
2217 bool painted = false;
2219 if (GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_BASIC) {
2220 AutoLayerManagerSetup setupLayerManager(this, ctx, layerBuffering);
2221 painted = listener->PaintWindow(this, region);
2225 #ifdef MOZ_X11
2226 // PaintWindow can Destroy us (bug 378273), avoid doing any paint
2227 // operations below if that happened - it will lead to XError and exit().
2228 if (shaped) {
2229 if (MOZ_LIKELY(!mIsDestroyed)) {
2230 if (painted) {
2231 nsRefPtr<gfxPattern> pattern = ctx->PopGroup();
2233 UpdateAlpha(pattern, boundsRect);
2235 ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
2236 ctx->SetPattern(pattern);
2237 ctx->Paint();
2241 # ifdef MOZ_HAVE_SHMIMAGE
2242 if (mShmImage && MOZ_LIKELY(!mIsDestroyed)) {
2243 #if (MOZ_WIDGET_GTK == 2)
2244 mShmImage->Put(mGdkWindow, exposeRegion.mRects, exposeRegion.mRectsEnd);
2245 #else
2246 mShmImage->Put(mGdkWindow, exposeRegion.mRects);
2247 #endif
2249 # endif // MOZ_HAVE_SHMIMAGE
2250 #endif // MOZ_X11
2252 listener->DidPaintWindow();
2254 // Synchronously flush any new dirty areas
2255 #if (MOZ_WIDGET_GTK == 2)
2256 GdkRegion* dirtyArea = gdk_window_get_update_area(mGdkWindow);
2257 #else
2258 cairo_region_t* dirtyArea = gdk_window_get_update_area(mGdkWindow);
2259 #endif
2261 if (dirtyArea) {
2262 gdk_window_invalidate_region(mGdkWindow, dirtyArea, false);
2263 #if (MOZ_WIDGET_GTK == 2)
2264 gdk_region_destroy(dirtyArea);
2265 #else
2266 cairo_region_destroy(dirtyArea);
2267 #endif
2268 gdk_window_process_updates(mGdkWindow, false);
2271 // check the return value!
2272 return TRUE;
2275 void
2276 nsWindow::UpdateAlpha(gfxPattern* aPattern, nsIntRect aBoundsRect)
2278 // We need to create our own buffer to force the stride to match the
2279 // expected stride.
2280 int32_t stride = GetAlignedStride<4>(BytesPerPixel(SurfaceFormat::A8) *
2281 aBoundsRect.width);
2282 int32_t bufferSize = stride * aBoundsRect.height;
2283 nsAutoArrayPtr<uint8_t> imageBuffer(new (std::nothrow) uint8_t[bufferSize]);
2284 RefPtr<DrawTarget> drawTarget = gfxPlatform::GetPlatform()->
2285 CreateDrawTargetForData(imageBuffer, ToIntSize(aBoundsRect.Size()),
2286 stride, SurfaceFormat::A8);
2288 if (drawTarget) {
2289 Matrix transform = Matrix::Translation(-aBoundsRect.x, -aBoundsRect.y);
2290 drawTarget->SetTransform(transform);
2292 drawTarget->FillRect(Rect(aBoundsRect.x, aBoundsRect.y, aBoundsRect.width, aBoundsRect.height),
2293 *aPattern->GetPattern(drawTarget),
2294 DrawOptions(1.0, CompositionOp::OP_SOURCE));
2296 UpdateTranslucentWindowAlphaInternal(aBoundsRect, imageBuffer, stride);
2299 gboolean
2300 nsWindow::OnConfigureEvent(GtkWidget *aWidget, GdkEventConfigure *aEvent)
2302 // These events are only received on toplevel windows.
2304 // GDK ensures that the coordinates are the client window top-left wrt the
2305 // root window.
2307 // GDK calculates the cordinates for real ConfigureNotify events on
2308 // managed windows (that would normally be relative to the parent
2309 // window).
2311 // Synthetic ConfigureNotify events are from the window manager and
2312 // already relative to the root window. GDK creates all X windows with
2313 // border_width = 0, so synthetic events also indicate the top-left of
2314 // the client window.
2316 // Override-redirect windows are children of the root window so parent
2317 // coordinates are root coordinates.
2319 LOG(("configure event [%p] %d %d %d %d\n", (void *)this,
2320 aEvent->x, aEvent->y, aEvent->width, aEvent->height));
2322 nsIntRect screenBounds;
2323 GetScreenBounds(screenBounds);
2325 if (mWindowType == eWindowType_toplevel || mWindowType == eWindowType_dialog) {
2326 // This check avoids unwanted rollup on spurious configure events from
2327 // Cygwin/X (bug 672103).
2328 if (mBounds.x != screenBounds.x ||
2329 mBounds.y != screenBounds.y) {
2330 CheckForRollup(0, 0, false, true);
2334 // This event indicates that the window position may have changed.
2335 // mBounds.Size() is updated in OnSizeAllocate().
2337 // (The gtk_window_get_window_type() function is only available from
2338 // version 2.20.)
2339 NS_ASSERTION(GTK_IS_WINDOW(aWidget),
2340 "Configure event on widget that is not a GtkWindow");
2341 gint type;
2342 g_object_get(aWidget, "type", &type, nullptr);
2343 if (type == GTK_WINDOW_POPUP) {
2344 // Override-redirect window
2346 // These windows should not be moved by the window manager, and so any
2347 // change in position is a result of our direction. mBounds has
2348 // already been set in Move() or Resize(), and that is more
2349 // up-to-date than the position in the ConfigureNotify event if the
2350 // event is from an earlier window move.
2352 // Skipping the WindowMoved call saves context menus from an infinite
2353 // loop when nsXULPopupManager::PopupMoved moves the window to the new
2354 // position and nsMenuPopupFrame::SetPopupPosition adds
2355 // offsetForContextMenu on each iteration.
2356 return FALSE;
2359 mBounds.MoveTo(screenBounds.TopLeft());
2361 // XXX mozilla will invalidate the entire window after this move
2362 // complete. wtf?
2363 NotifyWindowMoved(mBounds.x, mBounds.y);
2365 return FALSE;
2368 void
2369 nsWindow::OnContainerUnrealize()
2371 // The GdkWindows are about to be destroyed (but not deleted), so remove
2372 // their references back to their container widget while the GdkWindow
2373 // hierarchy is still available.
2375 if (mGdkWindow) {
2376 DestroyChildWindows();
2378 g_object_set_data(G_OBJECT(mGdkWindow), "nsWindow", nullptr);
2379 mGdkWindow = nullptr;
2383 void
2384 nsWindow::OnSizeAllocate(GtkAllocation *aAllocation)
2386 LOG(("size_allocate [%p] %d %d %d %d\n",
2387 (void *)this, aAllocation->x, aAllocation->y,
2388 aAllocation->width, aAllocation->height));
2390 nsIntSize size(aAllocation->width, aAllocation->height);
2391 if (mBounds.Size() == size)
2392 return;
2394 // Invalidate the new part of the window now for the pending paint to
2395 // minimize background flashes (GDK does not do this for external resizes
2396 // of toplevels.)
2397 if (mBounds.width < size.width) {
2398 GdkRectangle rect =
2399 { mBounds.width, 0, size.width - mBounds.width, size.height };
2400 gdk_window_invalidate_rect(mGdkWindow, &rect, FALSE);
2402 if (mBounds.height < size.height) {
2403 GdkRectangle rect =
2404 { 0, mBounds.height, size.width, size.height - mBounds.height };
2405 gdk_window_invalidate_rect(mGdkWindow, &rect, FALSE);
2408 mBounds.SizeTo(size);
2410 if (!mGdkWindow)
2411 return;
2413 DispatchResized(size.width, size.height);
2416 void
2417 nsWindow::OnDeleteEvent()
2419 if (mWidgetListener)
2420 mWidgetListener->RequestWindowClose(this);
2423 void
2424 nsWindow::OnEnterNotifyEvent(GdkEventCrossing *aEvent)
2426 // This skips NotifyVirtual and NotifyNonlinearVirtual enter notify events
2427 // when the pointer enters a child window. If the destination window is a
2428 // Gecko window then we'll catch the corresponding event on that window,
2429 // but we won't notice when the pointer directly enters a foreign (plugin)
2430 // child window without passing over a visible portion of a Gecko window.
2431 if (aEvent->subwindow != nullptr)
2432 return;
2434 // Check before is_parent_ungrab_enter() as the button state may have
2435 // changed while a non-Gecko ancestor window had a pointer grab.
2436 DispatchMissedButtonReleases(aEvent);
2438 if (is_parent_ungrab_enter(aEvent))
2439 return;
2441 WidgetMouseEvent event(true, NS_MOUSE_ENTER, this, WidgetMouseEvent::eReal);
2443 event.refPoint.x = nscoord(aEvent->x);
2444 event.refPoint.y = nscoord(aEvent->y);
2446 event.time = aEvent->time;
2448 LOG(("OnEnterNotify: %p\n", (void *)this));
2450 nsEventStatus status;
2451 DispatchEvent(&event, status);
2454 // XXX Is this the right test for embedding cases?
2455 static bool
2456 is_top_level_mouse_exit(GdkWindow* aWindow, GdkEventCrossing *aEvent)
2458 gint x = gint(aEvent->x_root);
2459 gint y = gint(aEvent->y_root);
2460 GdkDisplay* display = gdk_window_get_display(aWindow);
2461 GdkWindow* winAtPt = gdk_display_get_window_at_pointer(display, &x, &y);
2462 if (!winAtPt)
2463 return true;
2464 GdkWindow* topLevelAtPt = gdk_window_get_toplevel(winAtPt);
2465 GdkWindow* topLevelWidget = gdk_window_get_toplevel(aWindow);
2466 return topLevelAtPt != topLevelWidget;
2469 void
2470 nsWindow::OnLeaveNotifyEvent(GdkEventCrossing *aEvent)
2472 // This ignores NotifyVirtual and NotifyNonlinearVirtual leave notify
2473 // events when the pointer leaves a child window. If the destination
2474 // window is a Gecko window then we'll catch the corresponding event on
2475 // that window.
2477 // XXXkt However, we will miss toplevel exits when the pointer directly
2478 // leaves a foreign (plugin) child window without passing over a visible
2479 // portion of a Gecko window.
2480 if (aEvent->subwindow != nullptr)
2481 return;
2483 WidgetMouseEvent event(true, NS_MOUSE_EXIT, this, WidgetMouseEvent::eReal);
2485 event.refPoint.x = nscoord(aEvent->x);
2486 event.refPoint.y = nscoord(aEvent->y);
2488 event.time = aEvent->time;
2490 event.exit = is_top_level_mouse_exit(mGdkWindow, aEvent)
2491 ? WidgetMouseEvent::eTopLevel : WidgetMouseEvent::eChild;
2493 LOG(("OnLeaveNotify: %p\n", (void *)this));
2495 nsEventStatus status;
2496 DispatchEvent(&event, status);
2499 void
2500 nsWindow::OnMotionNotifyEvent(GdkEventMotion *aEvent)
2502 // see if we can compress this event
2503 // XXXldb Why skip every other motion event when we have multiple,
2504 // but not more than that?
2505 bool synthEvent = false;
2506 #ifdef MOZ_X11
2507 XEvent xevent;
2509 while (XPending (GDK_WINDOW_XDISPLAY(aEvent->window))) {
2510 XEvent peeked;
2511 XPeekEvent (GDK_WINDOW_XDISPLAY(aEvent->window), &peeked);
2512 if (peeked.xany.window != gdk_x11_window_get_xid(aEvent->window)
2513 || peeked.type != MotionNotify)
2514 break;
2516 synthEvent = true;
2517 XNextEvent (GDK_WINDOW_XDISPLAY(aEvent->window), &xevent);
2519 #if (MOZ_WIDGET_GTK == 2)
2520 // if plugins still keeps the focus, get it back
2521 if (gPluginFocusWindow && gPluginFocusWindow != this) {
2522 nsRefPtr<nsWindow> kungFuDeathGrip = gPluginFocusWindow;
2523 gPluginFocusWindow->LoseNonXEmbedPluginFocus();
2525 #endif /* MOZ_WIDGET_GTK2 */
2526 #endif /* MOZ_X11 */
2528 WidgetMouseEvent event(true, NS_MOUSE_MOVE, this, WidgetMouseEvent::eReal);
2530 gdouble pressure = 0;
2531 gdk_event_get_axis ((GdkEvent*)aEvent, GDK_AXIS_PRESSURE, &pressure);
2532 // Sometime gdk generate 0 pressure value between normal values
2533 // We have to ignore that and use last valid value
2534 if (pressure)
2535 mLastMotionPressure = pressure;
2536 event.pressure = mLastMotionPressure;
2538 guint modifierState;
2539 if (synthEvent) {
2540 #ifdef MOZ_X11
2541 event.refPoint.x = nscoord(xevent.xmotion.x);
2542 event.refPoint.y = nscoord(xevent.xmotion.y);
2544 modifierState = xevent.xmotion.state;
2546 event.time = xevent.xmotion.time;
2547 #else
2548 event.refPoint.x = nscoord(aEvent->x);
2549 event.refPoint.y = nscoord(aEvent->y);
2551 modifierState = aEvent->state;
2553 event.time = aEvent->time;
2554 #endif /* MOZ_X11 */
2556 else {
2557 // XXX see OnScrollEvent()
2558 if (aEvent->window == mGdkWindow) {
2559 event.refPoint.x = nscoord(aEvent->x);
2560 event.refPoint.y = nscoord(aEvent->y);
2561 } else {
2562 LayoutDeviceIntPoint point(NSToIntFloor(aEvent->x_root),
2563 NSToIntFloor(aEvent->y_root));
2564 event.refPoint = point -
2565 LayoutDeviceIntPoint::FromUntyped(WidgetToScreenOffset());
2568 modifierState = aEvent->state;
2570 event.time = aEvent->time;
2573 KeymapWrapper::InitInputEvent(event, modifierState);
2575 nsEventStatus status;
2576 DispatchEvent(&event, status);
2579 // If the automatic pointer grab on ButtonPress has deactivated before
2580 // ButtonRelease, and the mouse button is released while the pointer is not
2581 // over any a Gecko window, then the ButtonRelease event will not be received.
2582 // (A similar situation exists when the pointer is grabbed with owner_events
2583 // True as the ButtonRelease may be received on a foreign [plugin] window).
2584 // Use this method to check for released buttons when the pointer returns to a
2585 // Gecko window.
2586 void
2587 nsWindow::DispatchMissedButtonReleases(GdkEventCrossing *aGdkEvent)
2589 guint changed = aGdkEvent->state ^ gButtonState;
2590 // Only consider button releases.
2591 // (Ignore button presses that occurred outside Gecko.)
2592 guint released = changed & gButtonState;
2593 gButtonState = aGdkEvent->state;
2595 // Loop over each button, excluding mouse wheel buttons 4 and 5 for which
2596 // GDK ignores releases.
2597 for (guint buttonMask = GDK_BUTTON1_MASK;
2598 buttonMask <= GDK_BUTTON3_MASK;
2599 buttonMask <<= 1) {
2601 if (released & buttonMask) {
2602 int16_t buttonType;
2603 switch (buttonMask) {
2604 case GDK_BUTTON1_MASK:
2605 buttonType = WidgetMouseEvent::eLeftButton;
2606 break;
2607 case GDK_BUTTON2_MASK:
2608 buttonType = WidgetMouseEvent::eMiddleButton;
2609 break;
2610 default:
2611 NS_ASSERTION(buttonMask == GDK_BUTTON3_MASK,
2612 "Unexpected button mask");
2613 buttonType = WidgetMouseEvent::eRightButton;
2616 LOG(("Synthesized button %u release on %p\n",
2617 guint(buttonType + 1), (void *)this));
2619 // Dispatch a synthesized button up event to tell Gecko about the
2620 // change in state. This event is marked as synthesized so that
2621 // it is not dispatched as a DOM event, because we don't know the
2622 // position, widget, modifiers, or time/order.
2623 WidgetMouseEvent synthEvent(true, NS_MOUSE_BUTTON_UP, this,
2624 WidgetMouseEvent::eSynthesized);
2625 synthEvent.button = buttonType;
2626 nsEventStatus status;
2627 DispatchEvent(&synthEvent, status);
2632 void
2633 nsWindow::InitButtonEvent(WidgetMouseEvent& aEvent,
2634 GdkEventButton* aGdkEvent)
2636 // XXX see OnScrollEvent()
2637 if (aGdkEvent->window == mGdkWindow) {
2638 aEvent.refPoint.x = nscoord(aGdkEvent->x);
2639 aEvent.refPoint.y = nscoord(aGdkEvent->y);
2640 } else {
2641 LayoutDeviceIntPoint point(NSToIntFloor(aGdkEvent->x_root),
2642 NSToIntFloor(aGdkEvent->y_root));
2643 aEvent.refPoint = point -
2644 LayoutDeviceIntPoint::FromUntyped(WidgetToScreenOffset());
2647 guint modifierState = aGdkEvent->state;
2648 // aEvent's state doesn't include this event's information. Therefore,
2649 // if aEvent is mouse button down event, we need to set it manually.
2650 // Note that we cannot do same thing for NS_MOUSE_BUTTON_UP because
2651 // system may have two or more mice and same button of another mouse
2652 // may be still pressed.
2653 if (aGdkEvent->type != GDK_BUTTON_RELEASE) {
2654 switch (aGdkEvent->button) {
2655 case 1:
2656 modifierState |= GDK_BUTTON1_MASK;
2657 break;
2658 case 2:
2659 modifierState |= GDK_BUTTON2_MASK;
2660 break;
2661 case 3:
2662 modifierState |= GDK_BUTTON3_MASK;
2663 break;
2667 KeymapWrapper::InitInputEvent(aEvent, modifierState);
2669 aEvent.time = aGdkEvent->time;
2671 switch (aGdkEvent->type) {
2672 case GDK_2BUTTON_PRESS:
2673 aEvent.clickCount = 2;
2674 break;
2675 case GDK_3BUTTON_PRESS:
2676 aEvent.clickCount = 3;
2677 break;
2678 // default is one click
2679 default:
2680 aEvent.clickCount = 1;
2684 static guint ButtonMaskFromGDKButton(guint button)
2686 return GDK_BUTTON1_MASK << (button - 1);
2689 void
2690 nsWindow::OnButtonPressEvent(GdkEventButton *aEvent)
2692 LOG(("Button %u press on %p\n", aEvent->button, (void *)this));
2694 nsEventStatus status;
2696 // If you double click in GDK, it will actually generate a second
2697 // GDK_BUTTON_PRESS before sending the GDK_2BUTTON_PRESS, and this is
2698 // different than the DOM spec. GDK puts this in the queue
2699 // programatically, so it's safe to assume that if there's a
2700 // double click in the queue, it was generated so we can just drop
2701 // this click.
2702 GdkEvent *peekedEvent = gdk_event_peek();
2703 if (peekedEvent) {
2704 GdkEventType type = peekedEvent->any.type;
2705 gdk_event_free(peekedEvent);
2706 if (type == GDK_2BUTTON_PRESS || type == GDK_3BUTTON_PRESS)
2707 return;
2710 nsWindow *containerWindow = GetContainerWindow();
2711 if (!gFocusWindow && containerWindow) {
2712 containerWindow->DispatchActivateEvent();
2715 // check to see if we should rollup
2716 if (CheckForRollup(aEvent->x_root, aEvent->y_root, false, false))
2717 return;
2719 gdouble pressure = 0;
2720 gdk_event_get_axis ((GdkEvent*)aEvent, GDK_AXIS_PRESSURE, &pressure);
2721 mLastMotionPressure = pressure;
2723 uint16_t domButton;
2724 switch (aEvent->button) {
2725 case 1:
2726 domButton = WidgetMouseEvent::eLeftButton;
2727 break;
2728 case 2:
2729 domButton = WidgetMouseEvent::eMiddleButton;
2730 break;
2731 case 3:
2732 domButton = WidgetMouseEvent::eRightButton;
2733 break;
2734 // These are mapped to horizontal scroll
2735 case 6:
2736 case 7:
2737 NS_WARNING("We're not supporting legacy horizontal scroll event");
2738 return;
2739 // Map buttons 8-9 to back/forward
2740 case 8:
2741 DispatchCommandEvent(nsGkAtoms::Back);
2742 return;
2743 case 9:
2744 DispatchCommandEvent(nsGkAtoms::Forward);
2745 return;
2746 default:
2747 return;
2750 gButtonState |= ButtonMaskFromGDKButton(aEvent->button);
2752 WidgetMouseEvent event(true, NS_MOUSE_BUTTON_DOWN, this,
2753 WidgetMouseEvent::eReal);
2754 event.button = domButton;
2755 InitButtonEvent(event, aEvent);
2756 event.pressure = mLastMotionPressure;
2758 DispatchEvent(&event, status);
2760 // right menu click on linux should also pop up a context menu
2761 if (domButton == WidgetMouseEvent::eRightButton &&
2762 MOZ_LIKELY(!mIsDestroyed)) {
2763 WidgetMouseEvent contextMenuEvent(true, NS_CONTEXTMENU, this,
2764 WidgetMouseEvent::eReal);
2765 InitButtonEvent(contextMenuEvent, aEvent);
2766 contextMenuEvent.pressure = mLastMotionPressure;
2767 DispatchEvent(&contextMenuEvent, status);
2771 void
2772 nsWindow::OnButtonReleaseEvent(GdkEventButton *aEvent)
2774 LOG(("Button %u release on %p\n", aEvent->button, (void *)this));
2776 uint16_t domButton;
2777 switch (aEvent->button) {
2778 case 1:
2779 domButton = WidgetMouseEvent::eLeftButton;
2780 break;
2781 case 2:
2782 domButton = WidgetMouseEvent::eMiddleButton;
2783 break;
2784 case 3:
2785 domButton = WidgetMouseEvent::eRightButton;
2786 break;
2787 default:
2788 return;
2791 gButtonState &= ~ButtonMaskFromGDKButton(aEvent->button);
2793 WidgetMouseEvent event(true, NS_MOUSE_BUTTON_UP, this,
2794 WidgetMouseEvent::eReal);
2795 event.button = domButton;
2796 InitButtonEvent(event, aEvent);
2797 gdouble pressure = 0;
2798 gdk_event_get_axis ((GdkEvent*)aEvent, GDK_AXIS_PRESSURE, &pressure);
2799 event.pressure = pressure ? pressure : mLastMotionPressure;
2801 nsEventStatus status;
2802 DispatchEvent(&event, status);
2803 mLastMotionPressure = pressure;
2806 void
2807 nsWindow::OnContainerFocusInEvent(GdkEventFocus *aEvent)
2809 LOGFOCUS(("OnContainerFocusInEvent [%p]\n", (void *)this));
2811 // Unset the urgency hint, if possible
2812 GtkWidget* top_window = GetToplevelWidget();
2813 if (top_window && (gtk_widget_get_visible(top_window)))
2814 SetUrgencyHint(top_window, false);
2816 // Return if being called within SetFocus because the focus manager
2817 // already knows that the window is active.
2818 if (gBlockActivateEvent) {
2819 LOGFOCUS(("activated notification is blocked [%p]\n", (void *)this));
2820 return;
2823 // If keyboard input will be accepted, the focus manager will call
2824 // SetFocus to set the correct window.
2825 gFocusWindow = nullptr;
2827 DispatchActivateEvent();
2829 if (!gFocusWindow) {
2830 // We don't really have a window for dispatching key events, but
2831 // setting a non-nullptr value here prevents OnButtonPressEvent() from
2832 // dispatching an activation notification if the widget is already
2833 // active.
2834 gFocusWindow = this;
2837 LOGFOCUS(("Events sent from focus in event [%p]\n", (void *)this));
2840 void
2841 nsWindow::OnContainerFocusOutEvent(GdkEventFocus *aEvent)
2843 LOGFOCUS(("OnContainerFocusOutEvent [%p]\n", (void *)this));
2845 if (mWindowType == eWindowType_toplevel || mWindowType == eWindowType_dialog) {
2846 nsCOMPtr<nsIDragService> dragService = do_GetService(kCDragServiceCID);
2847 nsCOMPtr<nsIDragSession> dragSession;
2848 dragService->GetCurrentSession(getter_AddRefs(dragSession));
2850 // Rollup popups when a window is focused out unless a drag is occurring.
2851 // This check is because drags grab the keyboard and cause a focus out on
2852 // versions of GTK before 2.18.
2853 bool shouldRollup = !dragSession;
2854 if (!shouldRollup) {
2855 // we also roll up when a drag is from a different application
2856 nsCOMPtr<nsIDOMNode> sourceNode;
2857 dragSession->GetSourceNode(getter_AddRefs(sourceNode));
2858 shouldRollup = (sourceNode == nullptr);
2861 if (shouldRollup) {
2862 CheckForRollup(0, 0, false, true);
2866 #if (MOZ_WIDGET_GTK == 2) && defined(MOZ_X11)
2867 // plugin lose focus
2868 if (gPluginFocusWindow) {
2869 nsRefPtr<nsWindow> kungFuDeathGrip = gPluginFocusWindow;
2870 gPluginFocusWindow->LoseNonXEmbedPluginFocus();
2872 #endif /* MOZ_X11 && MOZ_WIDGET_GTK2 */
2874 if (gFocusWindow) {
2875 nsRefPtr<nsWindow> kungFuDeathGrip = gFocusWindow;
2876 if (gFocusWindow->mIMModule) {
2877 gFocusWindow->mIMModule->OnBlurWindow(gFocusWindow);
2879 gFocusWindow = nullptr;
2882 DispatchDeactivateEvent();
2884 LOGFOCUS(("Done with container focus out [%p]\n", (void *)this));
2887 bool
2888 nsWindow::DispatchCommandEvent(nsIAtom* aCommand)
2890 nsEventStatus status;
2891 WidgetCommandEvent event(true, nsGkAtoms::onAppCommand, aCommand, this);
2892 DispatchEvent(&event, status);
2893 return TRUE;
2896 bool
2897 nsWindow::DispatchContentCommandEvent(int32_t aMsg)
2899 nsEventStatus status;
2900 WidgetContentCommandEvent event(true, aMsg, this);
2901 DispatchEvent(&event, status);
2902 return TRUE;
2905 static bool
2906 IsCtrlAltTab(GdkEventKey *aEvent)
2908 return aEvent->keyval == GDK_Tab &&
2909 KeymapWrapper::AreModifiersActive(
2910 KeymapWrapper::CTRL | KeymapWrapper::ALT, aEvent->state);
2913 bool
2914 nsWindow::DispatchKeyDownEvent(GdkEventKey *aEvent, bool *aCancelled)
2916 NS_PRECONDITION(aCancelled, "aCancelled must not be null");
2918 *aCancelled = false;
2920 if (IsCtrlAltTab(aEvent)) {
2921 return false;
2924 // send the key down event
2925 nsEventStatus status;
2926 WidgetKeyboardEvent downEvent(true, NS_KEY_DOWN, this);
2927 KeymapWrapper::InitKeyEvent(downEvent, aEvent);
2928 DispatchEvent(&downEvent, status);
2929 *aCancelled = (status == nsEventStatus_eConsumeNoDefault);
2930 return true;
2933 gboolean
2934 nsWindow::OnKeyPressEvent(GdkEventKey *aEvent)
2936 LOGFOCUS(("OnKeyPressEvent [%p]\n", (void *)this));
2938 // if we are in the middle of composing text, XIM gets to see it
2939 // before mozilla does.
2940 bool IMEWasEnabled = false;
2941 if (mIMModule) {
2942 IMEWasEnabled = mIMModule->IsEnabled();
2943 if (mIMModule->OnKeyEvent(this, aEvent)) {
2944 return TRUE;
2948 nsEventStatus status;
2950 // work around for annoying things.
2951 if (IsCtrlAltTab(aEvent)) {
2952 return TRUE;
2955 nsCOMPtr<nsIWidget> kungFuDeathGrip = this;
2957 // Dispatch keydown event always. At auto repeating, we should send
2958 // KEYDOWN -> KEYPRESS -> KEYDOWN -> KEYPRESS ... -> KEYUP
2959 // However, old distributions (e.g., Ubuntu 9.10) sent native key
2960 // release event, so, on such platform, the DOM events will be:
2961 // KEYDOWN -> KEYPRESS -> KEYUP -> KEYDOWN -> KEYPRESS -> KEYUP...
2963 bool isKeyDownCancelled = false;
2964 if (DispatchKeyDownEvent(aEvent, &isKeyDownCancelled) &&
2965 (MOZ_UNLIKELY(mIsDestroyed) || isKeyDownCancelled)) {
2966 return TRUE;
2969 // If a keydown event handler causes to enable IME, i.e., it moves
2970 // focus from IME unusable content to IME usable editor, we should
2971 // send the native key event to IME for the first input on the editor.
2972 if (!IMEWasEnabled && mIMModule && mIMModule->IsEnabled()) {
2973 // Notice our keydown event was already dispatched. This prevents
2974 // unnecessary DOM keydown event in the editor.
2975 if (mIMModule->OnKeyEvent(this, aEvent, true)) {
2976 return TRUE;
2980 // Don't pass modifiers as NS_KEY_PRESS events.
2981 // TODO: Instead of selectively excluding some keys from NS_KEY_PRESS events,
2982 // we should instead selectively include (as per MSDN spec; no official
2983 // spec covers KeyPress events).
2984 if (!KeymapWrapper::IsKeyPressEventNecessary(aEvent)) {
2985 return TRUE;
2988 #ifdef MOZ_X11
2989 #if ! defined AIX // no XFree86 on AIX 5L
2990 // Look for specialized app-command keys
2991 switch (aEvent->keyval) {
2992 case XF86XK_Back:
2993 return DispatchCommandEvent(nsGkAtoms::Back);
2994 case XF86XK_Forward:
2995 return DispatchCommandEvent(nsGkAtoms::Forward);
2996 case XF86XK_Refresh:
2997 return DispatchCommandEvent(nsGkAtoms::Reload);
2998 case XF86XK_Stop:
2999 return DispatchCommandEvent(nsGkAtoms::Stop);
3000 case XF86XK_Search:
3001 return DispatchCommandEvent(nsGkAtoms::Search);
3002 case XF86XK_Favorites:
3003 return DispatchCommandEvent(nsGkAtoms::Bookmarks);
3004 case XF86XK_HomePage:
3005 return DispatchCommandEvent(nsGkAtoms::Home);
3006 case XF86XK_Copy:
3007 case GDK_F16: // F16, F20, F18, F14 are old keysyms for Copy Cut Paste Undo
3008 return DispatchContentCommandEvent(NS_CONTENT_COMMAND_COPY);
3009 case XF86XK_Cut:
3010 case GDK_F20:
3011 return DispatchContentCommandEvent(NS_CONTENT_COMMAND_CUT);
3012 case XF86XK_Paste:
3013 case GDK_F18:
3014 return DispatchContentCommandEvent(NS_CONTENT_COMMAND_PASTE);
3015 case GDK_Redo:
3016 return DispatchContentCommandEvent(NS_CONTENT_COMMAND_REDO);
3017 case GDK_Undo:
3018 case GDK_F14:
3019 return DispatchContentCommandEvent(NS_CONTENT_COMMAND_UNDO);
3021 #endif /* ! AIX */
3022 #endif /* MOZ_X11 */
3024 WidgetKeyboardEvent event(true, NS_KEY_PRESS, this);
3025 KeymapWrapper::InitKeyEvent(event, aEvent);
3027 // before we dispatch a key, check if it's the context menu key.
3028 // If so, send a context menu key event instead.
3029 if (is_context_menu_key(event)) {
3030 WidgetMouseEvent contextMenuEvent(true, NS_CONTEXTMENU, this,
3031 WidgetMouseEvent::eReal,
3032 WidgetMouseEvent::eContextMenuKey);
3034 contextMenuEvent.refPoint = LayoutDeviceIntPoint(0, 0);
3035 contextMenuEvent.time = aEvent->time;
3036 contextMenuEvent.clickCount = 1;
3037 KeymapWrapper::InitInputEvent(contextMenuEvent, aEvent->state);
3038 DispatchEvent(&contextMenuEvent, status);
3040 else {
3041 // If the character code is in the BMP, send the key press event.
3042 // Otherwise, send a text event with the equivalent UTF-16 string.
3043 if (IS_IN_BMP(event.charCode)) {
3044 DispatchEvent(&event, status);
3046 else {
3047 WidgetTextEvent textEvent(true, NS_TEXT_TEXT, this);
3048 char16_t textString[3];
3049 textString[0] = H_SURROGATE(event.charCode);
3050 textString[1] = L_SURROGATE(event.charCode);
3051 textString[2] = 0;
3052 textEvent.theText = textString;
3053 textEvent.time = event.time;
3054 DispatchEvent(&textEvent, status);
3058 // If the event was consumed, return.
3059 if (status == nsEventStatus_eConsumeNoDefault) {
3060 return TRUE;
3063 return FALSE;
3066 gboolean
3067 nsWindow::OnKeyReleaseEvent(GdkEventKey *aEvent)
3069 LOGFOCUS(("OnKeyReleaseEvent [%p]\n", (void *)this));
3071 if (mIMModule && mIMModule->OnKeyEvent(this, aEvent)) {
3072 return TRUE;
3075 // send the key event as a key up event
3076 WidgetKeyboardEvent event(true, NS_KEY_UP, this);
3077 KeymapWrapper::InitKeyEvent(event, aEvent);
3079 nsEventStatus status;
3080 DispatchEvent(&event, status);
3082 // If the event was consumed, return.
3083 if (status == nsEventStatus_eConsumeNoDefault) {
3084 return TRUE;
3087 return FALSE;
3090 void
3091 nsWindow::OnScrollEvent(GdkEventScroll *aEvent)
3093 // check to see if we should rollup
3094 if (CheckForRollup(aEvent->x_root, aEvent->y_root, true, false))
3095 return;
3096 #if GTK_CHECK_VERSION(3,4,0)
3097 // check for duplicate legacy scroll event, see GNOME bug 726878
3098 if (mLastScrollEventTime == aEvent->time)
3099 return;
3100 #endif
3101 WidgetWheelEvent wheelEvent(true, NS_WHEEL_WHEEL, this);
3102 wheelEvent.deltaMode = nsIDOMWheelEvent::DOM_DELTA_LINE;
3103 switch (aEvent->direction) {
3104 #if GTK_CHECK_VERSION(3,4,0)
3105 case GDK_SCROLL_SMOOTH:
3107 // As of GTK 3.4, all directional scroll events are provided by
3108 // the GDK_SCROLL_SMOOTH direction on XInput2 devices.
3109 mLastScrollEventTime = aEvent->time;
3110 // TODO - use a more appropriate scrolling unit than lines.
3111 // Multiply event deltas by 3 to emulate legacy behaviour.
3112 wheelEvent.deltaX = aEvent->delta_x * 3;
3113 wheelEvent.deltaY = aEvent->delta_y * 3;
3114 wheelEvent.mIsNoLineOrPageDelta = true;
3115 // This next step manually unsets smooth scrolling for touch devices
3116 // that trigger GDK_SCROLL_SMOOTH. We use the slave device, which
3117 // represents the actual input.
3118 GdkDevice *device = gdk_event_get_source_device((GdkEvent*)aEvent);
3119 GdkInputSource source = gdk_device_get_source(device);
3120 if (source == GDK_SOURCE_TOUCHSCREEN ||
3121 source == GDK_SOURCE_TOUCHPAD) {
3122 wheelEvent.scrollType = WidgetWheelEvent::SCROLL_ASYNCHRONOUSELY;
3124 break;
3126 #endif
3127 case GDK_SCROLL_UP:
3128 wheelEvent.deltaY = wheelEvent.lineOrPageDeltaY = -3;
3129 break;
3130 case GDK_SCROLL_DOWN:
3131 wheelEvent.deltaY = wheelEvent.lineOrPageDeltaY = 3;
3132 break;
3133 case GDK_SCROLL_LEFT:
3134 wheelEvent.deltaX = wheelEvent.lineOrPageDeltaX = -1;
3135 break;
3136 case GDK_SCROLL_RIGHT:
3137 wheelEvent.deltaX = wheelEvent.lineOrPageDeltaX = 1;
3138 break;
3141 NS_ASSERTION(wheelEvent.deltaX || wheelEvent.deltaY,
3142 "deltaX or deltaY must be non-zero");
3144 if (aEvent->window == mGdkWindow) {
3145 // we are the window that the event happened on so no need for expensive WidgetToScreenOffset
3146 wheelEvent.refPoint.x = nscoord(aEvent->x);
3147 wheelEvent.refPoint.y = nscoord(aEvent->y);
3148 } else {
3149 // XXX we're never quite sure which GdkWindow the event came from due to our custom bubbling
3150 // in scroll_event_cb(), so use ScreenToWidget to translate the screen root coordinates into
3151 // coordinates relative to this widget.
3152 LayoutDeviceIntPoint point(NSToIntFloor(aEvent->x_root),
3153 NSToIntFloor(aEvent->y_root));
3154 wheelEvent.refPoint = point -
3155 LayoutDeviceIntPoint::FromUntyped(WidgetToScreenOffset());
3158 KeymapWrapper::InitInputEvent(wheelEvent, aEvent->state);
3160 wheelEvent.time = aEvent->time;
3162 nsEventStatus status;
3163 DispatchEvent(&wheelEvent, status);
3166 void
3167 nsWindow::OnVisibilityNotifyEvent(GdkEventVisibility *aEvent)
3169 LOGDRAW(("Visibility event %i on [%p] %p\n",
3170 aEvent->state, this, aEvent->window));
3172 if (!mGdkWindow)
3173 return;
3175 switch (aEvent->state) {
3176 case GDK_VISIBILITY_UNOBSCURED:
3177 case GDK_VISIBILITY_PARTIAL:
3178 if (mIsFullyObscured && mHasMappedToplevel) {
3179 // GDK_EXPOSE events have been ignored, so make sure GDK
3180 // doesn't think that the window has already been painted.
3181 gdk_window_invalidate_rect(mGdkWindow, nullptr, FALSE);
3184 mIsFullyObscured = false;
3186 if (!nsGtkIMModule::IsVirtualKeyboardOpened()) {
3187 // if we have to retry the grab, retry it.
3188 EnsureGrabs();
3190 break;
3191 default: // includes GDK_VISIBILITY_FULLY_OBSCURED
3192 mIsFullyObscured = true;
3193 break;
3197 void
3198 nsWindow::OnWindowStateEvent(GtkWidget *aWidget, GdkEventWindowState *aEvent)
3200 LOG(("nsWindow::OnWindowStateEvent [%p] changed %d new_window_state %d\n",
3201 (void *)this, aEvent->changed_mask, aEvent->new_window_state));
3203 if (IS_MOZ_CONTAINER(aWidget)) {
3204 // This event is notifying the container widget of changes to the
3205 // toplevel window. Just detect changes affecting whether windows are
3206 // viewable.
3208 // (A visibility notify event is sent to each window that becomes
3209 // viewable when the toplevel is mapped, but we can't rely on that for
3210 // setting mHasMappedToplevel because these toplevel window state
3211 // events are asynchronous. The windows in the hierarchy now may not
3212 // be the same windows as when the toplevel was mapped, so they may
3213 // not get VisibilityNotify events.)
3214 bool mapped =
3215 !(aEvent->new_window_state &
3216 (GDK_WINDOW_STATE_ICONIFIED|GDK_WINDOW_STATE_WITHDRAWN));
3217 if (mHasMappedToplevel != mapped) {
3218 SetHasMappedToplevel(mapped);
3220 return;
3222 // else the widget is a shell widget.
3224 // We don't care about anything but changes in the maximized/icon/fullscreen
3225 // states
3226 if ((aEvent->changed_mask
3227 & (GDK_WINDOW_STATE_ICONIFIED |
3228 GDK_WINDOW_STATE_MAXIMIZED |
3229 GDK_WINDOW_STATE_FULLSCREEN)) == 0) {
3230 return;
3233 if (aEvent->new_window_state & GDK_WINDOW_STATE_ICONIFIED) {
3234 LOG(("\tIconified\n"));
3235 mSizeState = nsSizeMode_Minimized;
3236 #ifdef ACCESSIBILITY
3237 DispatchMinimizeEventAccessible();
3238 #endif //ACCESSIBILITY
3240 else if (aEvent->new_window_state & GDK_WINDOW_STATE_FULLSCREEN) {
3241 LOG(("\tFullscreen\n"));
3242 mSizeState = nsSizeMode_Fullscreen;
3244 else if (aEvent->new_window_state & GDK_WINDOW_STATE_MAXIMIZED) {
3245 LOG(("\tMaximized\n"));
3246 mSizeState = nsSizeMode_Maximized;
3247 #ifdef ACCESSIBILITY
3248 DispatchMaximizeEventAccessible();
3249 #endif //ACCESSIBILITY
3251 else {
3252 LOG(("\tNormal\n"));
3253 mSizeState = nsSizeMode_Normal;
3254 #ifdef ACCESSIBILITY
3255 DispatchRestoreEventAccessible();
3256 #endif //ACCESSIBILITY
3259 if (mWidgetListener)
3260 mWidgetListener->SizeModeChanged(mSizeState);
3263 void
3264 nsWindow::ThemeChanged()
3266 NotifyThemeChanged();
3268 if (!mGdkWindow || MOZ_UNLIKELY(mIsDestroyed))
3269 return;
3271 // Dispatch theme change notification to all child windows
3272 GList *children =
3273 gdk_window_peek_children(mGdkWindow);
3274 while (children) {
3275 GdkWindow *gdkWin = GDK_WINDOW(children->data);
3277 nsWindow *win = (nsWindow*) g_object_get_data(G_OBJECT(gdkWin),
3278 "nsWindow");
3280 if (win && win != this) { // guard against infinite recursion
3281 nsRefPtr<nsWindow> kungFuDeathGrip = win;
3282 win->ThemeChanged();
3285 children = children->next;
3289 void
3290 nsWindow::DispatchDragEvent(uint32_t aMsg, const nsIntPoint& aRefPoint,
3291 guint aTime)
3293 WidgetDragEvent event(true, aMsg, this);
3295 if (aMsg == NS_DRAGDROP_OVER) {
3296 InitDragEvent(event);
3299 event.refPoint = LayoutDeviceIntPoint::FromUntyped(aRefPoint);
3300 event.time = aTime;
3302 nsEventStatus status;
3303 DispatchEvent(&event, status);
3306 void
3307 nsWindow::OnDragDataReceivedEvent(GtkWidget *aWidget,
3308 GdkDragContext *aDragContext,
3309 gint aX,
3310 gint aY,
3311 GtkSelectionData *aSelectionData,
3312 guint aInfo,
3313 guint aTime,
3314 gpointer aData)
3316 LOGDRAG(("nsWindow::OnDragDataReceived(%p)\n", (void*)this));
3318 nsDragService::GetInstance()->
3319 TargetDataReceived(aWidget, aDragContext, aX, aY,
3320 aSelectionData, aInfo, aTime);
3323 static void
3324 GetBrandName(nsXPIDLString& brandName)
3326 nsCOMPtr<nsIStringBundleService> bundleService =
3327 do_GetService(NS_STRINGBUNDLE_CONTRACTID);
3329 nsCOMPtr<nsIStringBundle> bundle;
3330 if (bundleService)
3331 bundleService->CreateBundle(
3332 "chrome://branding/locale/brand.properties",
3333 getter_AddRefs(bundle));
3335 if (bundle)
3336 bundle->GetStringFromName(
3337 MOZ_UTF16("brandShortName"),
3338 getter_Copies(brandName));
3340 if (brandName.IsEmpty())
3341 brandName.AssignLiteral(MOZ_UTF16("Mozilla"));
3344 static GdkWindow *
3345 CreateGdkWindow(GdkWindow *parent, GtkWidget *widget)
3347 GdkWindowAttr attributes;
3348 gint attributes_mask = GDK_WA_VISUAL;
3350 attributes.event_mask = kEvents;
3352 attributes.width = 1;
3353 attributes.height = 1;
3354 attributes.wclass = GDK_INPUT_OUTPUT;
3355 attributes.visual = gtk_widget_get_visual(widget);
3356 attributes.window_type = GDK_WINDOW_CHILD;
3358 #if (MOZ_WIDGET_GTK == 2)
3359 attributes_mask |= GDK_WA_COLORMAP;
3360 attributes.colormap = gtk_widget_get_colormap(widget);
3361 #endif
3363 GdkWindow *window = gdk_window_new(parent, &attributes, attributes_mask);
3364 gdk_window_set_user_data(window, widget);
3366 // GTK3 TODO?
3367 #if (MOZ_WIDGET_GTK == 2)
3368 /* set the default pixmap to None so that you don't end up with the
3369 gtk default which is BlackPixel. */
3370 gdk_window_set_back_pixmap(window, nullptr, FALSE);
3371 #endif
3373 return window;
3376 nsresult
3377 nsWindow::Create(nsIWidget *aParent,
3378 nsNativeWidget aNativeParent,
3379 const nsIntRect &aRect,
3380 nsDeviceContext *aContext,
3381 nsWidgetInitData *aInitData)
3383 // only set the base parent if we're going to be a dialog or a
3384 // toplevel
3385 nsIWidget *baseParent = aInitData &&
3386 (aInitData->mWindowType == eWindowType_dialog ||
3387 aInitData->mWindowType == eWindowType_toplevel ||
3388 aInitData->mWindowType == eWindowType_invisible) ?
3389 nullptr : aParent;
3391 #ifdef ACCESSIBILITY
3392 // Send a DBus message to check whether a11y is enabled
3393 a11y::PreInit();
3394 #endif
3396 // Ensure that the toolkit is created.
3397 nsGTKToolkit::GetToolkit();
3399 // initialize all the common bits of this class
3400 BaseCreate(baseParent, aRect, aContext, aInitData);
3402 // Do we need to listen for resizes?
3403 bool listenForResizes = false;;
3404 if (aNativeParent || (aInitData && aInitData->mListenForResizes))
3405 listenForResizes = true;
3407 // and do our common creation
3408 CommonCreate(aParent, listenForResizes);
3410 // save our bounds
3411 mBounds = aRect;
3412 ConstrainSize(&mBounds.width, &mBounds.height);
3414 // figure out our parent window
3415 GtkWidget *parentMozContainer = nullptr;
3416 GtkContainer *parentGtkContainer = nullptr;
3417 GdkWindow *parentGdkWindow = nullptr;
3418 GtkWindow *topLevelParent = nullptr;
3419 nsWindow *parentnsWindow = nullptr;
3420 GtkWidget *eventWidget = nullptr;
3422 if (aParent) {
3423 parentnsWindow = static_cast<nsWindow*>(aParent);
3424 parentGdkWindow = parentnsWindow->mGdkWindow;
3425 } else if (aNativeParent && GDK_IS_WINDOW(aNativeParent)) {
3426 parentGdkWindow = GDK_WINDOW(aNativeParent);
3427 parentnsWindow = get_window_for_gdk_window(parentGdkWindow);
3428 if (!parentnsWindow)
3429 return NS_ERROR_FAILURE;
3431 } else if (aNativeParent && GTK_IS_CONTAINER(aNativeParent)) {
3432 parentGtkContainer = GTK_CONTAINER(aNativeParent);
3435 if (parentGdkWindow) {
3436 // get the widget for the window - it should be a moz container
3437 parentMozContainer = parentnsWindow->GetMozContainerWidget();
3438 if (!parentMozContainer)
3439 return NS_ERROR_FAILURE;
3441 // get the toplevel window just in case someone needs to use it
3442 // for setting transients or whatever.
3443 topLevelParent =
3444 GTK_WINDOW(gtk_widget_get_toplevel(parentMozContainer));
3447 // ok, create our windows
3448 switch (mWindowType) {
3449 case eWindowType_dialog:
3450 case eWindowType_popup:
3451 case eWindowType_toplevel:
3452 case eWindowType_invisible: {
3453 mIsTopLevel = true;
3455 // We only move a general managed toplevel window if someone has
3456 // actually placed the window somewhere. If no placement has taken
3457 // place, we just let the window manager Do The Right Thing.
3459 // Indicate that if we're shown, we at least need to have our size set.
3460 // If we get explicitly moved, the position will also be set.
3461 mNeedsResize = true;
3463 if (mWindowType == eWindowType_dialog) {
3464 mShell = gtk_window_new(GTK_WINDOW_TOPLEVEL);
3465 SetDefaultIcon();
3466 gtk_window_set_wmclass(GTK_WINDOW(mShell), "Dialog",
3467 gdk_get_program_class());
3468 gtk_window_set_type_hint(GTK_WINDOW(mShell),
3469 GDK_WINDOW_TYPE_HINT_DIALOG);
3470 gtk_window_set_transient_for(GTK_WINDOW(mShell),
3471 topLevelParent);
3473 else if (mWindowType == eWindowType_popup) {
3474 // With popup windows, we want to control their position, so don't
3475 // wait for the window manager to place them (which wouldn't
3476 // happen with override-redirect windows anyway).
3477 mNeedsMove = true;
3479 // Popups that are not noautohide are only temporary. The are used
3480 // for menus and the like and disappear when another window is used.
3481 // For most popups, use the standard GtkWindowType GTK_WINDOW_POPUP,
3482 // which will use a Window with the override-redirect attribute
3483 // (for temporary windows).
3484 // For long-lived windows, their stacking order is managed by the
3485 // window manager, as indicated by GTK_WINDOW_TOPLEVEL ...
3486 GtkWindowType type = aInitData->mNoAutoHide ?
3487 GTK_WINDOW_TOPLEVEL : GTK_WINDOW_POPUP;
3488 mShell = gtk_window_new(type);
3489 gtk_window_set_wmclass(GTK_WINDOW(mShell), "Popup",
3490 gdk_get_program_class());
3492 if (aInitData->mSupportTranslucency) {
3493 // We need to select an ARGB visual here instead of in
3494 // SetTransparencyMode() because it has to be done before the
3495 // widget is realized. An ARGB visual is only useful if we
3496 // are on a compositing window manager.
3497 GdkScreen *screen = gtk_widget_get_screen(mShell);
3498 if (gdk_screen_is_composited(screen)) {
3499 #if (MOZ_WIDGET_GTK == 2)
3500 GdkColormap *colormap =
3501 gdk_screen_get_rgba_colormap(screen);
3502 gtk_widget_set_colormap(mShell, colormap);
3503 #else
3504 GdkVisual *visual = gdk_screen_get_rgba_visual(screen);
3505 gtk_widget_set_visual(mShell, visual);
3506 #endif
3509 if (aInitData->mNoAutoHide) {
3510 // ... but the window manager does not decorate this window,
3511 // nor provide a separate taskbar icon.
3512 if (mBorderStyle == eBorderStyle_default) {
3513 gtk_window_set_decorated(GTK_WINDOW(mShell), FALSE);
3515 else {
3516 bool decorate = mBorderStyle & eBorderStyle_title;
3517 gtk_window_set_decorated(GTK_WINDOW(mShell), decorate);
3518 if (decorate) {
3519 gtk_window_set_deletable(GTK_WINDOW(mShell), mBorderStyle & eBorderStyle_close);
3522 gtk_window_set_skip_taskbar_hint(GTK_WINDOW(mShell), TRUE);
3523 // Element focus is managed by the parent window so the
3524 // WM_HINTS input field is set to False to tell the window
3525 // manager not to set input focus to this window ...
3526 gtk_window_set_accept_focus(GTK_WINDOW(mShell), FALSE);
3527 #ifdef MOZ_X11
3528 // ... but when the window manager offers focus through
3529 // WM_TAKE_FOCUS, focus is requested on the parent window.
3530 gtk_widget_realize(mShell);
3531 gdk_window_add_filter(gtk_widget_get_window(mShell),
3532 popup_take_focus_filter, nullptr);
3533 #endif
3536 GdkWindowTypeHint gtkTypeHint;
3537 if (aInitData->mIsDragPopup) {
3538 gtkTypeHint = GDK_WINDOW_TYPE_HINT_DND;
3540 else {
3541 switch (aInitData->mPopupHint) {
3542 case ePopupTypeMenu:
3543 gtkTypeHint = GDK_WINDOW_TYPE_HINT_POPUP_MENU;
3544 break;
3545 case ePopupTypeTooltip:
3546 gtkTypeHint = GDK_WINDOW_TYPE_HINT_TOOLTIP;
3547 break;
3548 default:
3549 gtkTypeHint = GDK_WINDOW_TYPE_HINT_UTILITY;
3550 break;
3553 gtk_window_set_type_hint(GTK_WINDOW(mShell), gtkTypeHint);
3555 if (topLevelParent) {
3556 gtk_window_set_transient_for(GTK_WINDOW(mShell),
3557 topLevelParent);
3560 else { // must be eWindowType_toplevel
3561 mShell = gtk_window_new(GTK_WINDOW_TOPLEVEL);
3562 SetDefaultIcon();
3563 gtk_window_set_wmclass(GTK_WINDOW(mShell), "Toplevel",
3564 gdk_get_program_class());
3566 // each toplevel window gets its own window group
3567 GtkWindowGroup *group = gtk_window_group_new();
3568 gtk_window_group_add_window(group, GTK_WINDOW(mShell));
3569 g_object_unref(group);
3572 // Prevent GtkWindow from painting a background to flicker.
3573 gtk_widget_set_app_paintable(mShell, TRUE);
3575 // Create a container to hold child windows and child GtkWidgets.
3576 GtkWidget *container = moz_container_new();
3577 mContainer = MOZ_CONTAINER(container);
3578 // Use mShell's window for drawing and events.
3579 gtk_widget_set_has_window(container, FALSE);
3580 eventWidget = mShell;
3581 gtk_widget_add_events(eventWidget, kEvents);
3582 gtk_container_add(GTK_CONTAINER(mShell), container);
3583 gtk_widget_realize(container);
3585 // make sure this is the focus widget in the container
3586 gtk_widget_show(container);
3587 gtk_widget_grab_focus(container);
3589 // the drawing window
3590 mGdkWindow = gtk_widget_get_window(mShell);
3592 if (mWindowType == eWindowType_popup) {
3593 // gdk does not automatically set the cursor for "temporary"
3594 // windows, which are what gtk uses for popups.
3596 mCursor = eCursor_wait; // force SetCursor to actually set the
3597 // cursor, even though our internal state
3598 // indicates that we already have the
3599 // standard cursor.
3600 SetCursor(eCursor_standard);
3602 if (aInitData->mNoAutoHide) {
3603 gint wmd = ConvertBorderStyles(mBorderStyle);
3604 if (wmd != -1)
3605 gdk_window_set_decorations(gtk_widget_get_window(mShell), (GdkWMDecoration) wmd);
3608 // If the popup ignores mouse events, set an empty input shape.
3609 if (aInitData->mMouseTransparent) {
3610 #if (MOZ_WIDGET_GTK == 2)
3611 GdkRectangle rect = { 0, 0, 0, 0 };
3612 GdkRegion *region = gdk_region_rectangle(&rect);
3614 gdk_window_input_shape_combine_region(mGdkWindow, region, 0, 0);
3615 gdk_region_destroy(region);
3616 #else
3617 cairo_rectangle_int_t rect = { 0, 0, 0, 0 };
3618 cairo_region_t *region = cairo_region_create_rectangle(&rect);
3620 gdk_window_input_shape_combine_region(mGdkWindow, region, 0, 0);
3621 cairo_region_destroy(region);
3622 #endif
3626 break;
3627 case eWindowType_plugin:
3628 case eWindowType_child: {
3629 if (parentMozContainer) {
3630 mGdkWindow = CreateGdkWindow(parentGdkWindow, parentMozContainer);
3631 mHasMappedToplevel = parentnsWindow->mHasMappedToplevel;
3633 else if (parentGtkContainer) {
3634 // This MozContainer has its own window for drawing and receives
3635 // events because there is no mShell widget (corresponding to this
3636 // nsWindow).
3637 GtkWidget *container = moz_container_new();
3638 mContainer = MOZ_CONTAINER(container);
3639 eventWidget = container;
3640 gtk_widget_add_events(eventWidget, kEvents);
3641 gtk_container_add(parentGtkContainer, container);
3642 gtk_widget_realize(container);
3644 mGdkWindow = gtk_widget_get_window(container);
3646 else {
3647 NS_WARNING("Warning: tried to create a new child widget with no parent!");
3648 return NS_ERROR_FAILURE;
3651 break;
3652 default:
3653 break;
3656 // label the drawing window with this object so we can find our way home
3657 g_object_set_data(G_OBJECT(mGdkWindow), "nsWindow", this);
3659 if (mContainer)
3660 g_object_set_data(G_OBJECT(mContainer), "nsWindow", this);
3662 if (mShell)
3663 g_object_set_data(G_OBJECT(mShell), "nsWindow", this);
3665 // attach listeners for events
3666 if (mShell) {
3667 g_signal_connect(mShell, "configure_event",
3668 G_CALLBACK(configure_event_cb), nullptr);
3669 g_signal_connect(mShell, "delete_event",
3670 G_CALLBACK(delete_event_cb), nullptr);
3671 g_signal_connect(mShell, "window_state_event",
3672 G_CALLBACK(window_state_event_cb), nullptr);
3674 GtkSettings* default_settings = gtk_settings_get_default();
3675 g_signal_connect_after(default_settings,
3676 "notify::gtk-theme-name",
3677 G_CALLBACK(theme_changed_cb), this);
3678 g_signal_connect_after(default_settings,
3679 "notify::gtk-font-name",
3680 G_CALLBACK(theme_changed_cb), this);
3683 if (mContainer) {
3684 // Widget signals
3685 g_signal_connect(mContainer, "unrealize",
3686 G_CALLBACK(container_unrealize_cb), nullptr);
3687 g_signal_connect_after(mContainer, "size_allocate",
3688 G_CALLBACK(size_allocate_cb), nullptr);
3689 g_signal_connect(mContainer, "hierarchy-changed",
3690 G_CALLBACK(hierarchy_changed_cb), nullptr);
3691 // Initialize mHasMappedToplevel.
3692 hierarchy_changed_cb(GTK_WIDGET(mContainer), nullptr);
3693 // Expose, focus, key, and drag events are sent even to GTK_NO_WINDOW
3694 // widgets.
3695 #if (MOZ_WIDGET_GTK == 2)
3696 g_signal_connect(mContainer, "expose_event",
3697 G_CALLBACK(expose_event_cb), nullptr);
3698 #else
3699 g_signal_connect(G_OBJECT(mContainer), "draw",
3700 G_CALLBACK(expose_event_cb), nullptr);
3701 #endif
3702 g_signal_connect(mContainer, "focus_in_event",
3703 G_CALLBACK(focus_in_event_cb), nullptr);
3704 g_signal_connect(mContainer, "focus_out_event",
3705 G_CALLBACK(focus_out_event_cb), nullptr);
3706 g_signal_connect(mContainer, "key_press_event",
3707 G_CALLBACK(key_press_event_cb), nullptr);
3708 g_signal_connect(mContainer, "key_release_event",
3709 G_CALLBACK(key_release_event_cb), nullptr);
3711 gtk_drag_dest_set((GtkWidget *)mContainer,
3712 (GtkDestDefaults)0,
3713 nullptr,
3715 (GdkDragAction)0);
3717 g_signal_connect(mContainer, "drag_motion",
3718 G_CALLBACK(drag_motion_event_cb), nullptr);
3719 g_signal_connect(mContainer, "drag_leave",
3720 G_CALLBACK(drag_leave_event_cb), nullptr);
3721 g_signal_connect(mContainer, "drag_drop",
3722 G_CALLBACK(drag_drop_event_cb), nullptr);
3723 g_signal_connect(mContainer, "drag_data_received",
3724 G_CALLBACK(drag_data_received_event_cb), nullptr);
3726 GtkWidget *widgets[] = { GTK_WIDGET(mContainer), mShell };
3727 for (size_t i = 0; i < ArrayLength(widgets) && widgets[i]; ++i) {
3728 // Visibility events are sent to the owning widget of the relevant
3729 // window but do not propagate to parent widgets so connect on
3730 // mShell (if it exists) as well as mContainer.
3731 g_signal_connect(widgets[i], "visibility-notify-event",
3732 G_CALLBACK(visibility_notify_event_cb), nullptr);
3733 // Similarly double buffering is controlled by the window's owning
3734 // widget. Disable double buffering for painting directly to the
3735 // X Window.
3736 gtk_widget_set_double_buffered(widgets[i], FALSE);
3739 // We create input contexts for all containers, except for
3740 // toplevel popup windows
3741 if (mWindowType != eWindowType_popup) {
3742 mIMModule = new nsGtkIMModule(this);
3744 } else if (!mIMModule) {
3745 nsWindow *container = GetContainerWindow();
3746 if (container) {
3747 mIMModule = container->mIMModule;
3751 if (eventWidget) {
3752 #if (MOZ_WIDGET_GTK == 2)
3753 // Don't let GTK mess with the shapes of our GdkWindows
3754 GTK_PRIVATE_SET_FLAG(eventWidget, GTK_HAS_SHAPE_MASK);
3755 #endif
3757 // These events are sent to the owning widget of the relevant window
3758 // and propagate up to the first widget that handles the events, so we
3759 // need only connect on mShell, if it exists, to catch events on its
3760 // window and windows of mContainer.
3761 g_signal_connect(eventWidget, "enter-notify-event",
3762 G_CALLBACK(enter_notify_event_cb), nullptr);
3763 g_signal_connect(eventWidget, "leave-notify-event",
3764 G_CALLBACK(leave_notify_event_cb), nullptr);
3765 g_signal_connect(eventWidget, "motion-notify-event",
3766 G_CALLBACK(motion_notify_event_cb), nullptr);
3767 g_signal_connect(eventWidget, "button-press-event",
3768 G_CALLBACK(button_press_event_cb), nullptr);
3769 g_signal_connect(eventWidget, "button-release-event",
3770 G_CALLBACK(button_release_event_cb), nullptr);
3771 g_signal_connect(eventWidget, "scroll-event",
3772 G_CALLBACK(scroll_event_cb), nullptr);
3775 LOG(("nsWindow [%p]\n", (void *)this));
3776 if (mShell) {
3777 LOG(("\tmShell %p mContainer %p mGdkWindow %p 0x%lx\n",
3778 mShell, mContainer, mGdkWindow,
3779 gdk_x11_window_get_xid(mGdkWindow)));
3780 } else if (mContainer) {
3781 LOG(("\tmContainer %p mGdkWindow %p\n", mContainer, mGdkWindow));
3783 else if (mGdkWindow) {
3784 LOG(("\tmGdkWindow %p parent %p\n",
3785 mGdkWindow, gdk_window_get_parent(mGdkWindow)));
3788 // resize so that everything is set to the right dimensions
3789 if (!mIsTopLevel)
3790 Resize(mBounds.x, mBounds.y, mBounds.width, mBounds.height, false);
3792 return NS_OK;
3795 NS_IMETHODIMP
3796 nsWindow::SetWindowClass(const nsAString &xulWinType)
3798 if (!mShell)
3799 return NS_ERROR_FAILURE;
3801 const char *res_class = gdk_get_program_class();
3802 if (!res_class)
3803 return NS_ERROR_FAILURE;
3805 char *res_name = ToNewCString(xulWinType);
3806 if (!res_name)
3807 return NS_ERROR_OUT_OF_MEMORY;
3809 const char *role = nullptr;
3811 // Parse res_name into a name and role. Characters other than
3812 // [A-Za-z0-9_-] are converted to '_'. Anything after the first
3813 // colon is assigned to role; if there's no colon, assign the
3814 // whole thing to both role and res_name.
3815 for (char *c = res_name; *c; c++) {
3816 if (':' == *c) {
3817 *c = 0;
3818 role = c + 1;
3820 else if (!isascii(*c) || (!isalnum(*c) && ('_' != *c) && ('-' != *c)))
3821 *c = '_';
3823 res_name[0] = toupper(res_name[0]);
3824 if (!role) role = res_name;
3826 GdkWindow *shellWindow = gtk_widget_get_window(GTK_WIDGET(mShell));
3827 gdk_window_set_role(shellWindow, role);
3829 #ifdef MOZ_X11
3830 XClassHint *class_hint = XAllocClassHint();
3831 if (!class_hint) {
3832 nsMemory::Free(res_name);
3833 return NS_ERROR_OUT_OF_MEMORY;
3835 class_hint->res_name = res_name;
3836 class_hint->res_class = const_cast<char*>(res_class);
3838 // Can't use gtk_window_set_wmclass() for this; it prints
3839 // a warning & refuses to make the change.
3840 XSetClassHint(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()),
3841 gdk_x11_window_get_xid(shellWindow),
3842 class_hint);
3843 XFree(class_hint);
3844 #endif /* MOZ_X11 */
3846 nsMemory::Free(res_name);
3848 return NS_OK;
3851 void
3852 nsWindow::NativeResize(int32_t aWidth, int32_t aHeight, bool aRepaint)
3854 LOG(("nsWindow::NativeResize [%p] %d %d\n", (void *)this,
3855 aWidth, aHeight));
3857 // clear our resize flag
3858 mNeedsResize = false;
3860 if (mIsTopLevel) {
3861 gtk_window_resize(GTK_WINDOW(mShell), aWidth, aHeight);
3863 else if (mContainer) {
3864 GtkWidget *widget = GTK_WIDGET(mContainer);
3865 GtkAllocation allocation, prev_allocation;
3866 gtk_widget_get_allocation(widget, &prev_allocation);
3867 allocation.x = prev_allocation.x;
3868 allocation.y = prev_allocation.y;
3869 allocation.width = aWidth;
3870 allocation.height = aHeight;
3871 gtk_widget_size_allocate(widget, &allocation);
3873 else if (mGdkWindow) {
3874 gdk_window_resize(mGdkWindow, aWidth, aHeight);
3878 void
3879 nsWindow::NativeResize(int32_t aX, int32_t aY,
3880 int32_t aWidth, int32_t aHeight,
3881 bool aRepaint)
3883 mNeedsResize = false;
3884 mNeedsMove = false;
3886 LOG(("nsWindow::NativeResize [%p] %d %d %d %d\n", (void *)this,
3887 aX, aY, aWidth, aHeight));
3889 if (mIsTopLevel) {
3890 // aX and aY give the position of the window manager frame top-left.
3891 gtk_window_move(GTK_WINDOW(mShell), aX, aY);
3892 // This sets the client window size.
3893 gtk_window_resize(GTK_WINDOW(mShell), aWidth, aHeight);
3895 else if (mContainer) {
3896 GtkAllocation allocation;
3897 allocation.x = aX;
3898 allocation.y = aY;
3899 allocation.width = aWidth;
3900 allocation.height = aHeight;
3901 gtk_widget_size_allocate(GTK_WIDGET(mContainer), &allocation);
3903 else if (mGdkWindow) {
3904 gdk_window_move_resize(mGdkWindow, aX, aY, aWidth, aHeight);
3908 void
3909 nsWindow::NativeShow(bool aAction)
3911 if (aAction) {
3912 // unset our flag now that our window has been shown
3913 mNeedsShow = false;
3915 if (mIsTopLevel) {
3916 // Set up usertime/startupID metadata for the created window.
3917 if (mWindowType != eWindowType_invisible) {
3918 SetUserTimeAndStartupIDForActivatedWindow(mShell);
3921 gtk_widget_show(mShell);
3923 else if (mContainer) {
3924 gtk_widget_show(GTK_WIDGET(mContainer));
3926 else if (mGdkWindow) {
3927 gdk_window_show_unraised(mGdkWindow);
3930 else {
3931 if (mIsTopLevel) {
3932 gtk_widget_hide(GTK_WIDGET(mShell));
3934 ClearTransparencyBitmap(); // Release some resources
3936 else if (mContainer) {
3937 gtk_widget_hide(GTK_WIDGET(mContainer));
3939 else if (mGdkWindow) {
3940 gdk_window_hide(mGdkWindow);
3945 void
3946 nsWindow::SetHasMappedToplevel(bool aState)
3948 // Even when aState == mHasMappedToplevel (as when this method is called
3949 // from Show()), child windows need to have their state checked, so don't
3950 // return early.
3951 bool oldState = mHasMappedToplevel;
3952 mHasMappedToplevel = aState;
3954 // mHasMappedToplevel is not updated for children of windows that are
3955 // hidden; GDK knows not to send expose events for these windows. The
3956 // state is recorded on the hidden window itself, but, for child trees of
3957 // hidden windows, their state essentially becomes disconnected from their
3958 // hidden parent. When the hidden parent gets shown, the child trees are
3959 // reconnected, and the state of the window being shown can be easily
3960 // propagated.
3961 if (!mIsShown || !mGdkWindow)
3962 return;
3964 if (aState && !oldState && !mIsFullyObscured) {
3965 // GDK_EXPOSE events have been ignored but the window is now visible,
3966 // so make sure GDK doesn't think that the window has already been
3967 // painted.
3968 gdk_window_invalidate_rect(mGdkWindow, nullptr, FALSE);
3970 // Check that a grab didn't fail due to the window not being
3971 // viewable.
3972 EnsureGrabs();
3975 for (GList *children = gdk_window_peek_children(mGdkWindow);
3976 children;
3977 children = children->next) {
3978 GdkWindow *gdkWin = GDK_WINDOW(children->data);
3979 nsWindow *child = get_window_for_gdk_window(gdkWin);
3981 if (child && child->mHasMappedToplevel != aState) {
3982 child->SetHasMappedToplevel(aState);
3987 nsIntSize
3988 nsWindow::GetSafeWindowSize(nsIntSize aSize)
3990 // The X protocol uses CARD32 for window sizes, but the server (1.11.3)
3991 // reads it as CARD16. Sizes of pixmaps, used for drawing, are (unsigned)
3992 // CARD16 in the protocol, but the server's ProcCreatePixmap returns
3993 // BadAlloc if dimensions cannot be represented by signed shorts.
3994 nsIntSize result = aSize;
3995 const int32_t kInt16Max = 32767;
3996 if (result.width > kInt16Max) {
3997 result.width = kInt16Max;
3999 if (result.height > kInt16Max) {
4000 result.height = kInt16Max;
4002 return result;
4005 void
4006 nsWindow::EnsureGrabs(void)
4008 if (mRetryPointerGrab)
4009 GrabPointer(sRetryGrabTime);
4012 void
4013 nsWindow::CleanLayerManagerRecursive(void) {
4014 if (mLayerManager) {
4015 mLayerManager->Destroy();
4016 mLayerManager = nullptr;
4019 DestroyCompositor();
4021 GList* children = gdk_window_peek_children(mGdkWindow);
4022 for (GList* list = children; list; list = list->next) {
4023 nsWindow* window = get_window_for_gdk_window(GDK_WINDOW(list->data));
4024 if (window) {
4025 window->CleanLayerManagerRecursive();
4030 void
4031 nsWindow::SetTransparencyMode(nsTransparencyMode aMode)
4033 if (!mShell) {
4034 // Pass the request to the toplevel window
4035 GtkWidget *topWidget = GetToplevelWidget();
4036 if (!topWidget)
4037 return;
4039 nsWindow *topWindow = get_window_for_gtk_widget(topWidget);
4040 if (!topWindow)
4041 return;
4043 topWindow->SetTransparencyMode(aMode);
4044 return;
4046 bool isTransparent = aMode == eTransparencyTransparent;
4048 if (mIsTransparent == isTransparent)
4049 return;
4051 if (!isTransparent) {
4052 ClearTransparencyBitmap();
4053 } // else the new default alpha values are "all 1", so we don't
4054 // need to change anything yet
4056 mIsTransparent = isTransparent;
4058 // Need to clean our LayerManager up while still alive because
4059 // we don't want to use layers acceleration on shaped windows
4060 CleanLayerManagerRecursive();
4063 nsTransparencyMode
4064 nsWindow::GetTransparencyMode()
4066 if (!mShell) {
4067 // Pass the request to the toplevel window
4068 GtkWidget *topWidget = GetToplevelWidget();
4069 if (!topWidget) {
4070 return eTransparencyOpaque;
4073 nsWindow *topWindow = get_window_for_gtk_widget(topWidget);
4074 if (!topWindow) {
4075 return eTransparencyOpaque;
4078 return topWindow->GetTransparencyMode();
4081 return mIsTransparent ? eTransparencyTransparent : eTransparencyOpaque;
4084 nsresult
4085 nsWindow::ConfigureChildren(const nsTArray<Configuration>& aConfigurations)
4087 for (uint32_t i = 0; i < aConfigurations.Length(); ++i) {
4088 const Configuration& configuration = aConfigurations[i];
4089 nsWindow* w = static_cast<nsWindow*>(configuration.mChild);
4090 NS_ASSERTION(w->GetParent() == this,
4091 "Configured widget is not a child");
4092 w->SetWindowClipRegion(configuration.mClipRegion, true);
4093 if (w->mBounds.Size() != configuration.mBounds.Size()) {
4094 w->Resize(configuration.mBounds.x, configuration.mBounds.y,
4095 configuration.mBounds.width, configuration.mBounds.height,
4096 true);
4097 } else if (w->mBounds.TopLeft() != configuration.mBounds.TopLeft()) {
4098 w->Move(configuration.mBounds.x, configuration.mBounds.y);
4100 w->SetWindowClipRegion(configuration.mClipRegion, false);
4102 return NS_OK;
4105 static pixman_box32
4106 ToPixmanBox(const nsIntRect& aRect)
4108 pixman_box32_t result;
4109 result.x1 = aRect.x;
4110 result.y1 = aRect.y;
4111 result.x2 = aRect.XMost();
4112 result.y2 = aRect.YMost();
4113 return result;
4116 static nsIntRect
4117 ToIntRect(const pixman_box32& aBox)
4119 nsIntRect result;
4120 result.x = aBox.x1;
4121 result.y = aBox.y1;
4122 result.width = aBox.x2 - aBox.x1;
4123 result.height = aBox.y2 - aBox.y1;
4124 return result;
4127 static void
4128 InitRegion(pixman_region32* aRegion,
4129 const nsTArray<nsIntRect>& aRects)
4131 nsAutoTArray<pixman_box32,10> rects;
4132 rects.SetCapacity(aRects.Length());
4133 for (uint32_t i = 0; i < aRects.Length (); ++i) {
4134 if (!aRects[i].IsEmpty()) {
4135 rects.AppendElement(ToPixmanBox(aRects[i]));
4139 pixman_region32_init_rects(aRegion,
4140 rects.Elements(), rects.Length());
4143 static void
4144 GetIntRects(pixman_region32& aRegion, nsTArray<nsIntRect>* aRects)
4146 int nRects;
4147 pixman_box32* boxes = pixman_region32_rectangles(&aRegion, &nRects);
4148 aRects->SetCapacity(aRects->Length() + nRects);
4149 for (int i = 0; i < nRects; ++i) {
4150 aRects->AppendElement(ToIntRect(boxes[i]));
4154 void
4155 nsWindow::SetWindowClipRegion(const nsTArray<nsIntRect>& aRects,
4156 bool aIntersectWithExisting)
4158 const nsTArray<nsIntRect>* newRects = &aRects;
4160 nsAutoTArray<nsIntRect,1> intersectRects;
4161 if (aIntersectWithExisting) {
4162 nsAutoTArray<nsIntRect,1> existingRects;
4163 GetWindowClipRegion(&existingRects);
4165 nsAutoRef<pixman_region32> existingRegion;
4166 InitRegion(&existingRegion, existingRects);
4167 nsAutoRef<pixman_region32> newRegion;
4168 InitRegion(&newRegion, aRects);
4169 nsAutoRef<pixman_region32> intersectRegion;
4170 pixman_region32_init(&intersectRegion);
4171 pixman_region32_intersect(&intersectRegion,
4172 &newRegion, &existingRegion);
4174 // If mClipRects is null we haven't set a clip rect yet, so we
4175 // need to set the clip even if it is equal.
4176 if (mClipRects &&
4177 pixman_region32_equal(&intersectRegion, &existingRegion)) {
4178 return;
4181 if (!pixman_region32_equal(&intersectRegion, &newRegion)) {
4182 GetIntRects(intersectRegion, &intersectRects);
4183 newRects = &intersectRects;
4187 if (!StoreWindowClipRegion(*newRects))
4188 return;
4190 if (!mGdkWindow)
4191 return;
4193 #if (MOZ_WIDGET_GTK == 2)
4194 GdkRegion *region = gdk_region_new(); // aborts on OOM
4195 for (uint32_t i = 0; i < newRects->Length(); ++i) {
4196 const nsIntRect& r = newRects->ElementAt(i);
4197 GdkRectangle rect = { r.x, r.y, r.width, r.height };
4198 gdk_region_union_with_rect(region, &rect);
4201 gdk_window_shape_combine_region(mGdkWindow, region, 0, 0);
4202 gdk_region_destroy(region);
4203 #else
4204 cairo_region_t *region = cairo_region_create();
4205 for (uint32_t i = 0; i < newRects->Length(); ++i) {
4206 const nsIntRect& r = newRects->ElementAt(i);
4207 cairo_rectangle_int_t rect = { r.x, r.y, r.width, r.height };
4208 cairo_region_union_rectangle(region, &rect);
4211 gdk_window_shape_combine_region(mGdkWindow, region, 0, 0);
4212 cairo_region_destroy(region);
4213 #endif
4215 return;
4218 void
4219 nsWindow::ResizeTransparencyBitmap()
4221 if (!mTransparencyBitmap)
4222 return;
4224 if (mBounds.width == mTransparencyBitmapWidth &&
4225 mBounds.height == mTransparencyBitmapHeight)
4226 return;
4228 int32_t newRowBytes = GetBitmapStride(mBounds.width);
4229 int32_t newSize = newRowBytes * mBounds.height;
4230 gchar* newBits = new gchar[newSize];
4231 // fill new mask with "transparent", first
4232 memset(newBits, 0, newSize);
4234 // Now copy the intersection of the old and new areas into the new mask
4235 int32_t copyWidth = std::min(mBounds.width, mTransparencyBitmapWidth);
4236 int32_t copyHeight = std::min(mBounds.height, mTransparencyBitmapHeight);
4237 int32_t oldRowBytes = GetBitmapStride(mTransparencyBitmapWidth);
4238 int32_t copyBytes = GetBitmapStride(copyWidth);
4240 int32_t i;
4241 gchar* fromPtr = mTransparencyBitmap;
4242 gchar* toPtr = newBits;
4243 for (i = 0; i < copyHeight; i++) {
4244 memcpy(toPtr, fromPtr, copyBytes);
4245 fromPtr += oldRowBytes;
4246 toPtr += newRowBytes;
4249 delete[] mTransparencyBitmap;
4250 mTransparencyBitmap = newBits;
4251 mTransparencyBitmapWidth = mBounds.width;
4252 mTransparencyBitmapHeight = mBounds.height;
4255 static bool
4256 ChangedMaskBits(gchar* aMaskBits, int32_t aMaskWidth, int32_t aMaskHeight,
4257 const nsIntRect& aRect, uint8_t* aAlphas, int32_t aStride)
4259 int32_t x, y, xMax = aRect.XMost(), yMax = aRect.YMost();
4260 int32_t maskBytesPerRow = GetBitmapStride(aMaskWidth);
4261 for (y = aRect.y; y < yMax; y++) {
4262 gchar* maskBytes = aMaskBits + y*maskBytesPerRow;
4263 uint8_t* alphas = aAlphas;
4264 for (x = aRect.x; x < xMax; x++) {
4265 bool newBit = *alphas > 0x7f;
4266 alphas++;
4268 gchar maskByte = maskBytes[x >> 3];
4269 bool maskBit = (maskByte & (1 << (x & 7))) != 0;
4271 if (maskBit != newBit) {
4272 return true;
4275 aAlphas += aStride;
4278 return false;
4281 static
4282 void UpdateMaskBits(gchar* aMaskBits, int32_t aMaskWidth, int32_t aMaskHeight,
4283 const nsIntRect& aRect, uint8_t* aAlphas, int32_t aStride)
4285 int32_t x, y, xMax = aRect.XMost(), yMax = aRect.YMost();
4286 int32_t maskBytesPerRow = GetBitmapStride(aMaskWidth);
4287 for (y = aRect.y; y < yMax; y++) {
4288 gchar* maskBytes = aMaskBits + y*maskBytesPerRow;
4289 uint8_t* alphas = aAlphas;
4290 for (x = aRect.x; x < xMax; x++) {
4291 bool newBit = *alphas > 0x7f;
4292 alphas++;
4294 gchar mask = 1 << (x & 7);
4295 gchar maskByte = maskBytes[x >> 3];
4296 // Note: '-newBit' turns 0 into 00...00 and 1 into 11...11
4297 maskBytes[x >> 3] = (maskByte & ~mask) | (-newBit & mask);
4299 aAlphas += aStride;
4303 void
4304 nsWindow::ApplyTransparencyBitmap()
4306 #ifdef MOZ_X11
4307 // We use X11 calls where possible, because GDK handles expose events
4308 // for shaped windows in a way that's incompatible with us (Bug 635903).
4309 // It doesn't occur when the shapes are set through X.
4310 GdkWindow *shellWindow = gtk_widget_get_window(mShell);
4311 Display* xDisplay = GDK_WINDOW_XDISPLAY(shellWindow);
4312 Window xDrawable = GDK_WINDOW_XID(shellWindow);
4313 Pixmap maskPixmap = XCreateBitmapFromData(xDisplay,
4314 xDrawable,
4315 mTransparencyBitmap,
4316 mTransparencyBitmapWidth,
4317 mTransparencyBitmapHeight);
4318 XShapeCombineMask(xDisplay, xDrawable,
4319 ShapeBounding, 0, 0,
4320 maskPixmap, ShapeSet);
4321 XFreePixmap(xDisplay, maskPixmap);
4322 #else
4323 #if (MOZ_WIDGET_GTK == 2)
4324 gtk_widget_reset_shapes(mShell);
4325 GdkBitmap* maskBitmap = gdk_bitmap_create_from_data(gtk_widget_get_window(mShell),
4326 mTransparencyBitmap,
4327 mTransparencyBitmapWidth, mTransparencyBitmapHeight);
4328 if (!maskBitmap)
4329 return;
4331 gtk_widget_shape_combine_mask(mShell, maskBitmap, 0, 0);
4332 g_object_unref(maskBitmap);
4333 #else
4334 cairo_surface_t *maskBitmap;
4335 maskBitmap = cairo_image_surface_create_for_data((unsigned char*)mTransparencyBitmap,
4336 CAIRO_FORMAT_A1,
4337 mTransparencyBitmapWidth,
4338 mTransparencyBitmapHeight,
4339 GetBitmapStride(mTransparencyBitmapWidth));
4340 if (!maskBitmap)
4341 return;
4343 cairo_region_t * maskRegion = gdk_cairo_region_create_from_surface(maskBitmap);
4344 gtk_widget_shape_combine_region(mShell, maskRegion);
4345 cairo_region_destroy(maskRegion);
4346 cairo_surface_destroy(maskBitmap);
4347 #endif // MOZ_WIDGET_GTK2
4348 #endif // MOZ_X11
4351 void
4352 nsWindow::ClearTransparencyBitmap()
4354 if (!mTransparencyBitmap)
4355 return;
4357 delete[] mTransparencyBitmap;
4358 mTransparencyBitmap = nullptr;
4359 mTransparencyBitmapWidth = 0;
4360 mTransparencyBitmapHeight = 0;
4362 if (!mShell)
4363 return;
4365 #ifdef MOZ_X11
4366 GdkWindow *window = gtk_widget_get_window(mShell);
4367 if (!window)
4368 return;
4370 Display* xDisplay = GDK_WINDOW_XDISPLAY(window);
4371 Window xWindow = gdk_x11_window_get_xid(window);
4373 XShapeCombineMask(xDisplay, xWindow, ShapeBounding, 0, 0, None, ShapeSet);
4374 #endif
4377 nsresult
4378 nsWindow::UpdateTranslucentWindowAlphaInternal(const nsIntRect& aRect,
4379 uint8_t* aAlphas, int32_t aStride)
4381 if (!mShell) {
4382 // Pass the request to the toplevel window
4383 GtkWidget *topWidget = GetToplevelWidget();
4384 if (!topWidget)
4385 return NS_ERROR_FAILURE;
4387 nsWindow *topWindow = get_window_for_gtk_widget(topWidget);
4388 if (!topWindow)
4389 return NS_ERROR_FAILURE;
4391 return topWindow->UpdateTranslucentWindowAlphaInternal(aRect, aAlphas, aStride);
4394 NS_ASSERTION(mIsTransparent, "Window is not transparent");
4396 if (mTransparencyBitmap == nullptr) {
4397 int32_t size = GetBitmapStride(mBounds.width)*mBounds.height;
4398 mTransparencyBitmap = new gchar[size];
4399 memset(mTransparencyBitmap, 255, size);
4400 mTransparencyBitmapWidth = mBounds.width;
4401 mTransparencyBitmapHeight = mBounds.height;
4402 } else {
4403 ResizeTransparencyBitmap();
4406 nsIntRect rect;
4407 rect.IntersectRect(aRect, nsIntRect(0, 0, mBounds.width, mBounds.height));
4409 if (!ChangedMaskBits(mTransparencyBitmap, mBounds.width, mBounds.height,
4410 rect, aAlphas, aStride))
4411 // skip the expensive stuff if the mask bits haven't changed; hopefully
4412 // this is the common case
4413 return NS_OK;
4415 UpdateMaskBits(mTransparencyBitmap, mBounds.width, mBounds.height,
4416 rect, aAlphas, aStride);
4418 if (!mNeedsShow) {
4419 ApplyTransparencyBitmap();
4421 return NS_OK;
4424 void
4425 nsWindow::GrabPointer(guint32 aTime)
4427 LOG(("GrabPointer time=0x%08x retry=%d\n",
4428 (unsigned int)aTime, mRetryPointerGrab));
4430 mRetryPointerGrab = false;
4431 sRetryGrabTime = aTime;
4433 // If the window isn't visible, just set the flag to retry the
4434 // grab. When this window becomes visible, the grab will be
4435 // retried.
4436 if (!mHasMappedToplevel || mIsFullyObscured) {
4437 LOG(("GrabPointer: window not visible\n"));
4438 mRetryPointerGrab = true;
4439 return;
4442 if (!mGdkWindow)
4443 return;
4445 gint retval;
4446 retval = gdk_pointer_grab(mGdkWindow, TRUE,
4447 (GdkEventMask)(GDK_BUTTON_PRESS_MASK |
4448 GDK_BUTTON_RELEASE_MASK |
4449 GDK_ENTER_NOTIFY_MASK |
4450 GDK_LEAVE_NOTIFY_MASK |
4451 GDK_POINTER_MOTION_MASK),
4452 (GdkWindow *)nullptr, nullptr, aTime);
4454 if (retval == GDK_GRAB_NOT_VIEWABLE) {
4455 LOG(("GrabPointer: window not viewable; will retry\n"));
4456 mRetryPointerGrab = true;
4457 } else if (retval != GDK_GRAB_SUCCESS) {
4458 LOG(("GrabPointer: pointer grab failed: %i\n", retval));
4459 // A failed grab indicates that another app has grabbed the pointer.
4460 // Check for rollup now, because, without the grab, we likely won't
4461 // get subsequent button press events.
4462 CheckForRollup(0, 0, false, true);
4466 void
4467 nsWindow::ReleaseGrabs(void)
4469 LOG(("ReleaseGrabs\n"));
4471 mRetryPointerGrab = false;
4472 gdk_pointer_ungrab(GDK_CURRENT_TIME);
4475 GtkWidget *
4476 nsWindow::GetToplevelWidget()
4478 if (mShell) {
4479 return mShell;
4482 GtkWidget *widget = GetMozContainerWidget();
4483 if (!widget)
4484 return nullptr;
4486 return gtk_widget_get_toplevel(widget);
4489 GtkWidget *
4490 nsWindow::GetMozContainerWidget()
4492 if (!mGdkWindow)
4493 return nullptr;
4495 if (mContainer)
4496 return GTK_WIDGET(mContainer);
4498 GtkWidget *owningWidget =
4499 get_gtk_widget_for_gdk_window(mGdkWindow);
4500 return owningWidget;
4503 nsWindow *
4504 nsWindow::GetContainerWindow()
4506 GtkWidget *owningWidget = GetMozContainerWidget();
4507 if (!owningWidget)
4508 return nullptr;
4510 nsWindow *window = get_window_for_gtk_widget(owningWidget);
4511 NS_ASSERTION(window, "No nsWindow for container widget");
4512 return window;
4515 void
4516 nsWindow::SetUrgencyHint(GtkWidget *top_window, bool state)
4518 if (!top_window)
4519 return;
4521 gdk_window_set_urgency_hint(gtk_widget_get_window(top_window), state);
4524 void *
4525 nsWindow::SetupPluginPort(void)
4527 if (!mGdkWindow)
4528 return nullptr;
4530 if (gdk_window_is_destroyed(mGdkWindow) == TRUE)
4531 return nullptr;
4533 Window window = gdk_x11_window_get_xid(mGdkWindow);
4535 // we have to flush the X queue here so that any plugins that
4536 // might be running on separate X connections will be able to use
4537 // this window in case it was just created
4538 #ifdef MOZ_X11
4539 XWindowAttributes xattrs;
4540 Display *display = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
4541 XGetWindowAttributes(display, window, &xattrs);
4542 XSelectInput (display, window,
4543 xattrs.your_event_mask |
4544 SubstructureNotifyMask);
4546 gdk_window_add_filter(mGdkWindow, plugin_window_filter_func, this);
4548 XSync(display, False);
4549 #endif /* MOZ_X11 */
4551 return (void *)window;
4554 void
4555 nsWindow::SetDefaultIcon(void)
4557 SetIcon(NS_LITERAL_STRING("default"));
4560 void
4561 nsWindow::SetPluginType(PluginType aPluginType)
4563 mPluginType = aPluginType;
4566 #ifdef MOZ_X11
4567 void
4568 nsWindow::SetNonXEmbedPluginFocus()
4570 if (gPluginFocusWindow == this || mPluginType!=PluginType_NONXEMBED) {
4571 return;
4574 if (gPluginFocusWindow) {
4575 nsRefPtr<nsWindow> kungFuDeathGrip = gPluginFocusWindow;
4576 gPluginFocusWindow->LoseNonXEmbedPluginFocus();
4579 LOGFOCUS(("nsWindow::SetNonXEmbedPluginFocus\n"));
4581 Window curFocusWindow;
4582 int focusState;
4584 GdkDisplay *gdkDisplay = gdk_window_get_display(mGdkWindow);
4585 XGetInputFocus(gdk_x11_display_get_xdisplay(gdkDisplay),
4586 &curFocusWindow,
4587 &focusState);
4589 LOGFOCUS(("\t curFocusWindow=%p\n", curFocusWindow));
4591 GdkWindow* toplevel = gdk_window_get_toplevel(mGdkWindow);
4592 #if (MOZ_WIDGET_GTK == 2)
4593 GdkWindow *gdkfocuswin = gdk_window_lookup(curFocusWindow);
4594 #else
4595 GdkWindow *gdkfocuswin = gdk_x11_window_lookup_for_display(gdkDisplay,
4596 curFocusWindow);
4597 #endif
4599 // lookup with the focus proxy window is supposed to get the
4600 // same GdkWindow as toplevel. If the current focused window
4601 // is not the focus proxy, we return without any change.
4602 if (gdkfocuswin != toplevel) {
4603 return;
4606 // switch the focus from the focus proxy to the plugin window
4607 mOldFocusWindow = curFocusWindow;
4608 XRaiseWindow(GDK_WINDOW_XDISPLAY(mGdkWindow),
4609 gdk_x11_window_get_xid(mGdkWindow));
4610 gdk_error_trap_push();
4611 XSetInputFocus(GDK_WINDOW_XDISPLAY(mGdkWindow),
4612 gdk_x11_window_get_xid(mGdkWindow),
4613 RevertToNone,
4614 CurrentTime);
4615 gdk_flush();
4616 #if (MOZ_WIDGET_GTK == 3)
4617 gdk_error_trap_pop_ignored();
4618 #else
4619 gdk_error_trap_pop();
4620 #endif
4621 gPluginFocusWindow = this;
4622 gdk_window_add_filter(nullptr, plugin_client_message_filter, this);
4624 LOGFOCUS(("nsWindow::SetNonXEmbedPluginFocus oldfocus=%p new=%p\n",
4625 mOldFocusWindow, gdk_x11_window_get_xid(mGdkWindow)));
4628 void
4629 nsWindow::LoseNonXEmbedPluginFocus()
4631 LOGFOCUS(("nsWindow::LoseNonXEmbedPluginFocus\n"));
4633 // This method is only for the nsWindow which contains a
4634 // Non-XEmbed plugin, for example, JAVA plugin.
4635 if (gPluginFocusWindow != this || mPluginType!=PluginType_NONXEMBED) {
4636 return;
4639 Window curFocusWindow;
4640 int focusState;
4642 XGetInputFocus(GDK_WINDOW_XDISPLAY(mGdkWindow),
4643 &curFocusWindow,
4644 &focusState);
4646 // we only switch focus between plugin window and focus proxy. If the
4647 // current focused window is not the plugin window, just removing the
4648 // event filter that blocks the WM_TAKE_FOCUS is enough. WM and gtk2
4649 // will take care of the focus later.
4650 if (!curFocusWindow ||
4651 curFocusWindow == gdk_x11_window_get_xid(mGdkWindow)) {
4653 gdk_error_trap_push();
4654 XRaiseWindow(GDK_WINDOW_XDISPLAY(mGdkWindow),
4655 mOldFocusWindow);
4656 XSetInputFocus(GDK_WINDOW_XDISPLAY(mGdkWindow),
4657 mOldFocusWindow,
4658 RevertToParent,
4659 CurrentTime);
4660 gdk_flush();
4661 #if (MOZ_WIDGET_GTK == 3)
4662 gdk_error_trap_pop_ignored();
4663 #else
4664 gdk_error_trap_pop();
4665 #endif
4667 gPluginFocusWindow = nullptr;
4668 mOldFocusWindow = 0;
4669 gdk_window_remove_filter(nullptr, plugin_client_message_filter, this);
4671 LOGFOCUS(("nsWindow::LoseNonXEmbedPluginFocus end\n"));
4673 #endif /* MOZ_X11 */
4675 gint
4676 nsWindow::ConvertBorderStyles(nsBorderStyle aStyle)
4678 gint w = 0;
4680 if (aStyle == eBorderStyle_default)
4681 return -1;
4683 // note that we don't handle eBorderStyle_close yet
4684 if (aStyle & eBorderStyle_all)
4685 w |= GDK_DECOR_ALL;
4686 if (aStyle & eBorderStyle_border)
4687 w |= GDK_DECOR_BORDER;
4688 if (aStyle & eBorderStyle_resizeh)
4689 w |= GDK_DECOR_RESIZEH;
4690 if (aStyle & eBorderStyle_title)
4691 w |= GDK_DECOR_TITLE;
4692 if (aStyle & eBorderStyle_menu)
4693 w |= GDK_DECOR_MENU;
4694 if (aStyle & eBorderStyle_minimize)
4695 w |= GDK_DECOR_MINIMIZE;
4696 if (aStyle & eBorderStyle_maximize)
4697 w |= GDK_DECOR_MAXIMIZE;
4699 return w;
4702 NS_IMETHODIMP
4703 nsWindow::MakeFullScreen(bool aFullScreen)
4705 LOG(("nsWindow::MakeFullScreen [%p] aFullScreen %d\n",
4706 (void *)this, aFullScreen));
4708 if (aFullScreen) {
4709 if (mSizeMode != nsSizeMode_Fullscreen)
4710 mLastSizeMode = mSizeMode;
4712 mSizeMode = nsSizeMode_Fullscreen;
4713 gtk_window_fullscreen(GTK_WINDOW(mShell));
4715 else {
4716 mSizeMode = mLastSizeMode;
4717 gtk_window_unfullscreen(GTK_WINDOW(mShell));
4720 NS_ASSERTION(mLastSizeMode != nsSizeMode_Fullscreen,
4721 "mLastSizeMode should never be fullscreen");
4722 return NS_OK;
4725 NS_IMETHODIMP
4726 nsWindow::HideWindowChrome(bool aShouldHide)
4728 if (!mShell) {
4729 // Pass the request to the toplevel window
4730 GtkWidget *topWidget = GetToplevelWidget();
4731 if (!topWidget)
4732 return NS_ERROR_FAILURE;
4734 nsWindow *topWindow = get_window_for_gtk_widget(topWidget);
4735 if (!topWindow)
4736 return NS_ERROR_FAILURE;
4738 return topWindow->HideWindowChrome(aShouldHide);
4741 // Sawfish, metacity, and presumably other window managers get
4742 // confused if we change the window decorations while the window
4743 // is visible.
4744 bool wasVisible = false;
4745 GdkWindow *shellWindow = gtk_widget_get_window(mShell);
4746 if (gdk_window_is_visible(shellWindow)) {
4747 gdk_window_hide(shellWindow);
4748 wasVisible = true;
4751 gint wmd;
4752 if (aShouldHide)
4753 wmd = 0;
4754 else
4755 wmd = ConvertBorderStyles(mBorderStyle);
4757 if (wmd != -1)
4758 gdk_window_set_decorations(shellWindow, (GdkWMDecoration) wmd);
4760 if (wasVisible)
4761 gdk_window_show(shellWindow);
4763 // For some window managers, adding or removing window decorations
4764 // requires unmapping and remapping our toplevel window. Go ahead
4765 // and flush the queue here so that we don't end up with a BadWindow
4766 // error later when this happens (when the persistence timer fires
4767 // and GetWindowPos is called)
4768 #ifdef MOZ_X11
4769 XSync(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()) , False);
4770 #else
4771 gdk_flush ();
4772 #endif /* MOZ_X11 */
4774 return NS_OK;
4777 bool
4778 nsWindow::CheckForRollup(gdouble aMouseX, gdouble aMouseY,
4779 bool aIsWheel, bool aAlwaysRollup)
4781 nsIRollupListener* rollupListener = GetActiveRollupListener();
4782 nsCOMPtr<nsIWidget> rollupWidget;
4783 if (rollupListener) {
4784 rollupWidget = rollupListener->GetRollupWidget();
4786 if (!rollupWidget) {
4787 nsBaseWidget::gRollupListener = nullptr;
4788 return false;
4791 bool retVal = false;
4792 GdkWindow *currentPopup =
4793 (GdkWindow *)rollupWidget->GetNativeData(NS_NATIVE_WINDOW);
4794 if (aAlwaysRollup || !is_mouse_in_window(currentPopup, aMouseX, aMouseY)) {
4795 bool rollup = true;
4796 if (aIsWheel) {
4797 rollup = rollupListener->ShouldRollupOnMouseWheelEvent();
4798 retVal = rollupListener->ShouldConsumeOnMouseWheelEvent();
4800 // if we're dealing with menus, we probably have submenus and
4801 // we don't want to rollup if the click is in a parent menu of
4802 // the current submenu
4803 uint32_t popupsToRollup = UINT32_MAX;
4804 if (!aAlwaysRollup) {
4805 nsAutoTArray<nsIWidget*, 5> widgetChain;
4806 uint32_t sameTypeCount = rollupListener->GetSubmenuWidgetChain(&widgetChain);
4807 for (uint32_t i=0; i<widgetChain.Length(); ++i) {
4808 nsIWidget* widget = widgetChain[i];
4809 GdkWindow* currWindow =
4810 (GdkWindow*) widget->GetNativeData(NS_NATIVE_WINDOW);
4811 if (is_mouse_in_window(currWindow, aMouseX, aMouseY)) {
4812 // don't roll up if the mouse event occurred within a
4813 // menu of the same type. If the mouse event occurred
4814 // in a menu higher than that, roll up, but pass the
4815 // number of popups to Rollup so that only those of the
4816 // same type close up.
4817 if (i < sameTypeCount) {
4818 rollup = false;
4820 else {
4821 popupsToRollup = sameTypeCount;
4823 break;
4825 } // foreach parent menu widget
4826 } // if rollup listener knows about menus
4828 // if we've determined that we should still rollup, do it.
4829 bool usePoint = !aIsWheel && !aAlwaysRollup;
4830 nsIntPoint point(aMouseX, aMouseY);
4831 if (rollup && rollupListener->Rollup(popupsToRollup, usePoint ? &point : nullptr, nullptr)) {
4832 retVal = true;
4835 return retVal;
4838 /* static */
4839 bool
4840 nsWindow::DragInProgress(void)
4842 nsCOMPtr<nsIDragService> dragService = do_GetService(kCDragServiceCID);
4844 if (!dragService)
4845 return false;
4847 nsCOMPtr<nsIDragSession> currentDragSession;
4848 dragService->GetCurrentSession(getter_AddRefs(currentDragSession));
4850 return currentDragSession != nullptr;
4853 static bool
4854 is_mouse_in_window (GdkWindow* aWindow, gdouble aMouseX, gdouble aMouseY)
4856 gint x = 0;
4857 gint y = 0;
4858 gint w, h;
4860 gint offsetX = 0;
4861 gint offsetY = 0;
4863 GdkWindow *window = aWindow;
4865 while (window) {
4866 gint tmpX = 0;
4867 gint tmpY = 0;
4869 gdk_window_get_position(window, &tmpX, &tmpY);
4870 GtkWidget *widget = get_gtk_widget_for_gdk_window(window);
4872 // if this is a window, compute x and y given its origin and our
4873 // offset
4874 if (GTK_IS_WINDOW(widget)) {
4875 x = tmpX + offsetX;
4876 y = tmpY + offsetY;
4877 break;
4880 offsetX += tmpX;
4881 offsetY += tmpY;
4882 window = gdk_window_get_parent(window);
4885 #if (MOZ_WIDGET_GTK == 2)
4886 gdk_drawable_get_size(aWindow, &w, &h);
4887 #else
4888 w = gdk_window_get_width(aWindow);
4889 h = gdk_window_get_height(aWindow);
4890 #endif
4892 if (aMouseX > x && aMouseX < x + w &&
4893 aMouseY > y && aMouseY < y + h)
4894 return true;
4896 return false;
4899 static nsWindow *
4900 get_window_for_gtk_widget(GtkWidget *widget)
4902 gpointer user_data = g_object_get_data(G_OBJECT(widget), "nsWindow");
4904 return static_cast<nsWindow *>(user_data);
4907 static nsWindow *
4908 get_window_for_gdk_window(GdkWindow *window)
4910 gpointer user_data = g_object_get_data(G_OBJECT(window), "nsWindow");
4912 return static_cast<nsWindow *>(user_data);
4915 static GtkWidget *
4916 get_gtk_widget_for_gdk_window(GdkWindow *window)
4918 gpointer user_data = nullptr;
4919 gdk_window_get_user_data(window, &user_data);
4921 return GTK_WIDGET(user_data);
4924 static GdkCursor *
4925 get_gtk_cursor(nsCursor aCursor)
4927 GdkCursor *gdkcursor = nullptr;
4928 uint8_t newType = 0xff;
4930 if ((gdkcursor = gCursorCache[aCursor])) {
4931 return gdkcursor;
4934 GdkDisplay *defaultDisplay = gdk_display_get_default();
4936 // The strategy here is to use standard GDK cursors, and, if not available,
4937 // load by standard name with gdk_cursor_new_from_name.
4938 // Spec is here: http://www.freedesktop.org/wiki/Specifications/cursor-spec/
4939 switch (aCursor) {
4940 case eCursor_standard:
4941 gdkcursor = gdk_cursor_new(GDK_LEFT_PTR);
4942 break;
4943 case eCursor_wait:
4944 gdkcursor = gdk_cursor_new(GDK_WATCH);
4945 break;
4946 case eCursor_select:
4947 gdkcursor = gdk_cursor_new(GDK_XTERM);
4948 break;
4949 case eCursor_hyperlink:
4950 gdkcursor = gdk_cursor_new(GDK_HAND2);
4951 break;
4952 case eCursor_n_resize:
4953 gdkcursor = gdk_cursor_new(GDK_TOP_SIDE);
4954 break;
4955 case eCursor_s_resize:
4956 gdkcursor = gdk_cursor_new(GDK_BOTTOM_SIDE);
4957 break;
4958 case eCursor_w_resize:
4959 gdkcursor = gdk_cursor_new(GDK_LEFT_SIDE);
4960 break;
4961 case eCursor_e_resize:
4962 gdkcursor = gdk_cursor_new(GDK_RIGHT_SIDE);
4963 break;
4964 case eCursor_nw_resize:
4965 gdkcursor = gdk_cursor_new(GDK_TOP_LEFT_CORNER);
4966 break;
4967 case eCursor_se_resize:
4968 gdkcursor = gdk_cursor_new(GDK_BOTTOM_RIGHT_CORNER);
4969 break;
4970 case eCursor_ne_resize:
4971 gdkcursor = gdk_cursor_new(GDK_TOP_RIGHT_CORNER);
4972 break;
4973 case eCursor_sw_resize:
4974 gdkcursor = gdk_cursor_new(GDK_BOTTOM_LEFT_CORNER);
4975 break;
4976 case eCursor_crosshair:
4977 gdkcursor = gdk_cursor_new(GDK_CROSSHAIR);
4978 break;
4979 case eCursor_move:
4980 gdkcursor = gdk_cursor_new(GDK_FLEUR);
4981 break;
4982 case eCursor_help:
4983 gdkcursor = gdk_cursor_new(GDK_QUESTION_ARROW);
4984 break;
4985 case eCursor_copy: // CSS3
4986 gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "copy");
4987 if (!gdkcursor)
4988 newType = MOZ_CURSOR_COPY;
4989 break;
4990 case eCursor_alias:
4991 gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "alias");
4992 if (!gdkcursor)
4993 newType = MOZ_CURSOR_ALIAS;
4994 break;
4995 case eCursor_context_menu:
4996 gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "context-menu");
4997 if (!gdkcursor)
4998 newType = MOZ_CURSOR_CONTEXT_MENU;
4999 break;
5000 case eCursor_cell:
5001 gdkcursor = gdk_cursor_new(GDK_PLUS);
5002 break;
5003 // Those two aren’t standardized. Trying both KDE’s and GNOME’s names
5004 case eCursor_grab:
5005 gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "openhand");
5006 if (!gdkcursor)
5007 newType = MOZ_CURSOR_HAND_GRAB;
5008 break;
5009 case eCursor_grabbing:
5010 gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "closedhand");
5011 if (!gdkcursor)
5012 gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "grabbing");
5013 if (!gdkcursor)
5014 newType = MOZ_CURSOR_HAND_GRABBING;
5015 break;
5016 case eCursor_spinning:
5017 gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "progress");
5018 if (!gdkcursor)
5019 newType = MOZ_CURSOR_SPINNING;
5020 break;
5021 case eCursor_zoom_in:
5022 newType = MOZ_CURSOR_ZOOM_IN;
5023 break;
5024 case eCursor_zoom_out:
5025 newType = MOZ_CURSOR_ZOOM_OUT;
5026 break;
5027 case eCursor_not_allowed:
5028 gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "not-allowed");
5029 if (!gdkcursor) // nonstandard, yet common
5030 gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "crossed_circle");
5031 if (!gdkcursor)
5032 newType = MOZ_CURSOR_NOT_ALLOWED;
5033 break;
5034 case eCursor_no_drop:
5035 gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "no-drop");
5036 if (!gdkcursor) // this nonstandard sequence makes it work on KDE and GNOME
5037 gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "forbidden");
5038 if (!gdkcursor)
5039 gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "circle");
5040 if (!gdkcursor)
5041 newType = MOZ_CURSOR_NOT_ALLOWED;
5042 break;
5043 case eCursor_vertical_text:
5044 newType = MOZ_CURSOR_VERTICAL_TEXT;
5045 break;
5046 case eCursor_all_scroll:
5047 gdkcursor = gdk_cursor_new(GDK_FLEUR);
5048 break;
5049 case eCursor_nesw_resize:
5050 gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "size_bdiag");
5051 if (!gdkcursor)
5052 newType = MOZ_CURSOR_NESW_RESIZE;
5053 break;
5054 case eCursor_nwse_resize:
5055 gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "size_fdiag");
5056 if (!gdkcursor)
5057 newType = MOZ_CURSOR_NWSE_RESIZE;
5058 break;
5059 case eCursor_ns_resize:
5060 gdkcursor = gdk_cursor_new(GDK_SB_V_DOUBLE_ARROW);
5061 break;
5062 case eCursor_ew_resize:
5063 gdkcursor = gdk_cursor_new(GDK_SB_H_DOUBLE_ARROW);
5064 break;
5065 // Here, two better fitting cursors exist in some cursor themes. Try those first
5066 case eCursor_row_resize:
5067 gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "split_v");
5068 if (!gdkcursor)
5069 gdkcursor = gdk_cursor_new(GDK_SB_V_DOUBLE_ARROW);
5070 break;
5071 case eCursor_col_resize:
5072 gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "split_h");
5073 if (!gdkcursor)
5074 gdkcursor = gdk_cursor_new(GDK_SB_H_DOUBLE_ARROW);
5075 break;
5076 case eCursor_none:
5077 newType = MOZ_CURSOR_NONE;
5078 break;
5079 default:
5080 NS_ASSERTION(aCursor, "Invalid cursor type");
5081 gdkcursor = gdk_cursor_new(GDK_LEFT_PTR);
5082 break;
5085 // If by now we don't have a xcursor, this means we have to make a custom
5086 // one. First, we try creating a named cursor based on the hash of our
5087 // custom bitmap, as libXcursor has some magic to convert bitmapped cursors
5088 // to themed cursors
5089 if (newType != 0xFF && GtkCursors[newType].hash) {
5090 gdkcursor = gdk_cursor_new_from_name(defaultDisplay, GtkCursors[newType].hash);
5093 // If we still don't have a xcursor, we now really create a bitmap cursor
5094 if (newType != 0xff && !gdkcursor) {
5095 GdkPixbuf * cursor_pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, 32, 32);
5096 if (!cursor_pixbuf)
5097 return nullptr;
5099 guchar *data = gdk_pixbuf_get_pixels(cursor_pixbuf);
5101 // Read data from GtkCursors and compose RGBA surface from 1bit bitmap and mask
5102 // GtkCursors bits and mask are 32x32 monochrome bitmaps (1 bit for each pixel)
5103 // so it's 128 byte array (4 bytes for are one bitmap row and there are 32 rows here).
5104 const unsigned char *bits = GtkCursors[newType].bits;
5105 const unsigned char *mask_bits = GtkCursors[newType].mask_bits;
5107 for (int i = 0; i < 128; i++) {
5108 char bit = *bits++;
5109 char mask = *mask_bits++;
5110 for (int j = 0; j < 8; j++) {
5111 unsigned char pix = ~(((bit >> j) & 0x01) * 0xff);
5112 *data++ = pix;
5113 *data++ = pix;
5114 *data++ = pix;
5115 *data++ = (((mask >> j) & 0x01) * 0xff);
5119 gdkcursor = gdk_cursor_new_from_pixbuf(gdk_display_get_default(), cursor_pixbuf,
5120 GtkCursors[newType].hot_x,
5121 GtkCursors[newType].hot_y);
5123 g_object_unref(cursor_pixbuf);
5126 gCursorCache[aCursor] = gdkcursor;
5128 return gdkcursor;
5131 // gtk callbacks
5133 #if (MOZ_WIDGET_GTK == 2)
5134 static gboolean
5135 expose_event_cb(GtkWidget *widget, GdkEventExpose *event)
5137 nsRefPtr<nsWindow> window = get_window_for_gdk_window(event->window);
5138 if (!window)
5139 return FALSE;
5141 window->OnExposeEvent(event);
5142 return FALSE;
5144 #else
5145 void
5146 draw_window_of_widget(GtkWidget *widget, GdkWindow *aWindow, cairo_t *cr)
5148 if (gtk_cairo_should_draw_window(cr, aWindow)) {
5149 nsRefPtr<nsWindow> window = get_window_for_gdk_window(aWindow);
5150 if (!window) {
5151 NS_WARNING("Cannot get nsWindow from GtkWidget");
5153 else {
5154 cairo_save(cr);
5155 gtk_cairo_transform_to_window(cr, widget, aWindow);
5156 // TODO - window->OnExposeEvent() can destroy this or other windows,
5157 // do we need to handle it somehow?
5158 window->OnExposeEvent(cr);
5159 cairo_restore(cr);
5163 GList *children = gdk_window_get_children(aWindow);
5164 GList *child = children;
5165 while (child) {
5166 GdkWindow *window = GDK_WINDOW(child->data);
5167 gpointer windowWidget;
5168 gdk_window_get_user_data(window, &windowWidget);
5169 if (windowWidget == widget) {
5170 draw_window_of_widget(widget, window, cr);
5172 child = g_list_next(child);
5174 g_list_free(children);
5177 /* static */
5178 gboolean
5179 expose_event_cb(GtkWidget *widget, cairo_t *cr)
5181 draw_window_of_widget(widget, gtk_widget_get_window(widget), cr);
5182 return FALSE;
5184 #endif //MOZ_WIDGET_GTK2
5186 static gboolean
5187 configure_event_cb(GtkWidget *widget,
5188 GdkEventConfigure *event)
5190 nsRefPtr<nsWindow> window = get_window_for_gtk_widget(widget);
5191 if (!window)
5192 return FALSE;
5194 return window->OnConfigureEvent(widget, event);
5197 static void
5198 container_unrealize_cb (GtkWidget *widget)
5200 nsRefPtr<nsWindow> window = get_window_for_gtk_widget(widget);
5201 if (!window)
5202 return;
5204 window->OnContainerUnrealize();
5207 static void
5208 size_allocate_cb (GtkWidget *widget, GtkAllocation *allocation)
5210 nsRefPtr<nsWindow> window = get_window_for_gtk_widget(widget);
5211 if (!window)
5212 return;
5214 window->OnSizeAllocate(allocation);
5217 static gboolean
5218 delete_event_cb(GtkWidget *widget, GdkEventAny *event)
5220 nsRefPtr<nsWindow> window = get_window_for_gtk_widget(widget);
5221 if (!window)
5222 return FALSE;
5224 window->OnDeleteEvent();
5226 return TRUE;
5229 static gboolean
5230 enter_notify_event_cb(GtkWidget *widget,
5231 GdkEventCrossing *event)
5233 nsRefPtr<nsWindow> window = get_window_for_gdk_window(event->window);
5234 if (!window)
5235 return TRUE;
5237 window->OnEnterNotifyEvent(event);
5239 return TRUE;
5242 static gboolean
5243 leave_notify_event_cb(GtkWidget *widget,
5244 GdkEventCrossing *event)
5246 if (is_parent_grab_leave(event)) {
5247 return TRUE;
5250 // bug 369599: Suppress LeaveNotify events caused by pointer grabs to
5251 // avoid generating spurious mouse exit events.
5252 gint x = gint(event->x_root);
5253 gint y = gint(event->y_root);
5254 GdkDisplay* display = gtk_widget_get_display(widget);
5255 GdkWindow* winAtPt = gdk_display_get_window_at_pointer(display, &x, &y);
5256 if (winAtPt == event->window) {
5257 return TRUE;
5260 nsRefPtr<nsWindow> window = get_window_for_gdk_window(event->window);
5261 if (!window)
5262 return TRUE;
5264 window->OnLeaveNotifyEvent(event);
5266 return TRUE;
5269 static nsWindow*
5270 GetFirstNSWindowForGDKWindow(GdkWindow *aGdkWindow)
5272 nsWindow* window;
5273 while (!(window = get_window_for_gdk_window(aGdkWindow))) {
5274 // The event has bubbled to the moz_container widget as passed into each caller's *widget parameter,
5275 // but its corresponding nsWindow is an ancestor of the window that we need. Instead, look at
5276 // event->window and find the first ancestor nsWindow of it because event->window may be in a plugin.
5277 aGdkWindow = gdk_window_get_parent(aGdkWindow);
5278 if (!aGdkWindow) {
5279 window = nullptr;
5280 break;
5283 return window;
5286 static gboolean
5287 motion_notify_event_cb(GtkWidget *widget, GdkEventMotion *event)
5289 UpdateLastInputEventTime(event);
5291 nsWindow *window = GetFirstNSWindowForGDKWindow(event->window);
5292 if (!window)
5293 return FALSE;
5295 window->OnMotionNotifyEvent(event);
5297 return TRUE;
5300 static gboolean
5301 button_press_event_cb(GtkWidget *widget, GdkEventButton *event)
5303 UpdateLastInputEventTime(event);
5305 nsWindow *window = GetFirstNSWindowForGDKWindow(event->window);
5306 if (!window)
5307 return FALSE;
5309 window->OnButtonPressEvent(event);
5311 return TRUE;
5314 static gboolean
5315 button_release_event_cb(GtkWidget *widget, GdkEventButton *event)
5317 UpdateLastInputEventTime(event);
5319 nsWindow *window = GetFirstNSWindowForGDKWindow(event->window);
5320 if (!window)
5321 return FALSE;
5323 window->OnButtonReleaseEvent(event);
5325 return TRUE;
5328 static gboolean
5329 focus_in_event_cb(GtkWidget *widget, GdkEventFocus *event)
5331 nsRefPtr<nsWindow> window = get_window_for_gtk_widget(widget);
5332 if (!window)
5333 return FALSE;
5335 window->OnContainerFocusInEvent(event);
5337 return FALSE;
5340 static gboolean
5341 focus_out_event_cb(GtkWidget *widget, GdkEventFocus *event)
5343 nsRefPtr<nsWindow> window = get_window_for_gtk_widget(widget);
5344 if (!window)
5345 return FALSE;
5347 window->OnContainerFocusOutEvent(event);
5349 return FALSE;
5352 #ifdef MOZ_X11
5353 // For long-lived popup windows that don't really take focus themselves but
5354 // may have elements that accept keyboard input when the parent window is
5355 // active, focus is handled specially. These windows include noautohide
5356 // panels. (This special handling is not necessary for temporary popups where
5357 // the keyboard is grabbed.)
5359 // Mousing over or clicking on these windows should not cause them to steal
5360 // focus from their parent windows, so, the input field of WM_HINTS is set to
5361 // False to request that the window manager not set the input focus to this
5362 // window. http://tronche.com/gui/x/icccm/sec-4.html#s-4.1.7
5364 // However, these windows can still receive WM_TAKE_FOCUS messages from the
5365 // window manager, so they can still detect when the user has indicated that
5366 // they wish to direct keyboard input at these windows. When the window
5367 // manager offers focus to these windows (after a mouse over or click, for
5368 // example), a request to make the parent window active is issued. When the
5369 // parent window becomes active, keyboard events will be received.
5371 static GdkFilterReturn
5372 popup_take_focus_filter(GdkXEvent *gdk_xevent,
5373 GdkEvent *event,
5374 gpointer data)
5376 XEvent* xevent = static_cast<XEvent*>(gdk_xevent);
5377 if (xevent->type != ClientMessage)
5378 return GDK_FILTER_CONTINUE;
5380 XClientMessageEvent& xclient = xevent->xclient;
5381 if (xclient.message_type != gdk_x11_get_xatom_by_name("WM_PROTOCOLS"))
5382 return GDK_FILTER_CONTINUE;
5384 Atom atom = xclient.data.l[0];
5385 if (atom != gdk_x11_get_xatom_by_name("WM_TAKE_FOCUS"))
5386 return GDK_FILTER_CONTINUE;
5388 guint32 timestamp = xclient.data.l[1];
5390 GtkWidget* widget = get_gtk_widget_for_gdk_window(event->any.window);
5391 if (!widget)
5392 return GDK_FILTER_CONTINUE;
5394 GtkWindow* parent = gtk_window_get_transient_for(GTK_WINDOW(widget));
5395 if (!parent)
5396 return GDK_FILTER_CONTINUE;
5398 if (gtk_window_is_active(parent))
5399 return GDK_FILTER_REMOVE; // leave input focus on the parent
5401 GdkWindow* parent_window = gtk_widget_get_window(GTK_WIDGET(parent));
5402 if (!parent_window)
5403 return GDK_FILTER_CONTINUE;
5405 // In case the parent has not been deconified.
5406 gdk_window_show_unraised(parent_window);
5408 // Request focus on the parent window.
5409 // Use gdk_window_focus rather than gtk_window_present to avoid
5410 // raising the parent window.
5411 gdk_window_focus(parent_window, timestamp);
5412 return GDK_FILTER_REMOVE;
5415 static GdkFilterReturn
5416 plugin_window_filter_func(GdkXEvent *gdk_xevent, GdkEvent *event, gpointer data)
5418 GdkWindow *plugin_window;
5419 XEvent *xevent;
5420 Window xeventWindow;
5422 nsRefPtr<nsWindow> nswindow = (nsWindow*)data;
5423 GdkFilterReturn return_val;
5425 xevent = (XEvent *)gdk_xevent;
5426 return_val = GDK_FILTER_CONTINUE;
5428 switch (xevent->type)
5430 case CreateNotify:
5431 case ReparentNotify:
5432 if (xevent->type==CreateNotify) {
5433 xeventWindow = xevent->xcreatewindow.window;
5435 else {
5436 if (xevent->xreparent.event != xevent->xreparent.parent)
5437 break;
5438 xeventWindow = xevent->xreparent.window;
5440 #if (MOZ_WIDGET_GTK == 2)
5441 plugin_window = gdk_window_lookup(xeventWindow);
5442 #else
5443 plugin_window = gdk_x11_window_lookup_for_display(
5444 gdk_x11_lookup_xdisplay(xevent->xcreatewindow.display), xeventWindow);
5445 #endif
5446 if (plugin_window) {
5447 GtkWidget *widget =
5448 get_gtk_widget_for_gdk_window(plugin_window);
5450 // TODO GTK3
5451 #if (MOZ_WIDGET_GTK == 2)
5452 if (GTK_IS_XTBIN(widget)) {
5453 nswindow->SetPluginType(nsWindow::PluginType_NONXEMBED);
5454 break;
5456 else
5457 #endif
5458 if(GTK_IS_SOCKET(widget)) {
5459 if (!g_object_get_data(G_OBJECT(widget), "enable-xt-focus")) {
5460 nswindow->SetPluginType(nsWindow::PluginType_XEMBED);
5461 break;
5465 nswindow->SetPluginType(nsWindow::PluginType_NONXEMBED);
5466 return_val = GDK_FILTER_REMOVE;
5467 break;
5468 case EnterNotify:
5469 nswindow->SetNonXEmbedPluginFocus();
5470 break;
5471 case DestroyNotify:
5472 gdk_window_remove_filter
5473 ((GdkWindow*)(nswindow->GetNativeData(NS_NATIVE_WINDOW)),
5474 plugin_window_filter_func,
5475 nswindow);
5476 // Currently we consider all plugins are non-xembed and calls
5477 // LoseNonXEmbedPluginFocus without any checking.
5478 nswindow->LoseNonXEmbedPluginFocus();
5479 break;
5480 default:
5481 break;
5483 return return_val;
5486 static GdkFilterReturn
5487 plugin_client_message_filter(GdkXEvent *gdk_xevent,
5488 GdkEvent *event,
5489 gpointer data)
5491 XEvent *xevent;
5492 xevent = (XEvent *)gdk_xevent;
5494 GdkFilterReturn return_val;
5495 return_val = GDK_FILTER_CONTINUE;
5497 if (!gPluginFocusWindow || xevent->type!=ClientMessage) {
5498 return return_val;
5501 // When WM sends out WM_TAKE_FOCUS, gtk2 will use XSetInputFocus
5502 // to set the focus to the focus proxy. To prevent this happen
5503 // while the focus is on the plugin, we filter the WM_TAKE_FOCUS
5504 // out.
5505 if (gdk_x11_get_xatom_by_name("WM_PROTOCOLS")
5506 != xevent->xclient.message_type) {
5507 return return_val;
5510 if ((Atom) xevent->xclient.data.l[0] ==
5511 gdk_x11_get_xatom_by_name("WM_TAKE_FOCUS")) {
5512 // block it from gtk2.0 focus proxy
5513 return_val = GDK_FILTER_REMOVE;
5516 return return_val;
5518 #endif /* MOZ_X11 */
5520 static gboolean
5521 key_press_event_cb(GtkWidget *widget, GdkEventKey *event)
5523 LOG(("key_press_event_cb\n"));
5525 UpdateLastInputEventTime(event);
5527 // find the window with focus and dispatch this event to that widget
5528 nsWindow *window = get_window_for_gtk_widget(widget);
5529 if (!window)
5530 return FALSE;
5532 nsRefPtr<nsWindow> focusWindow = gFocusWindow ? gFocusWindow : window;
5534 #ifdef MOZ_X11
5535 // Keyboard repeat can cause key press events to queue up when there are
5536 // slow event handlers (bug 301029). Throttle these events by removing
5537 // consecutive pending duplicate KeyPress events to the same window.
5538 // We use the event time of the last one.
5539 // Note: GDK calls XkbSetDetectableAutorepeat so that KeyRelease events
5540 // are generated only when the key is physically released.
5541 #define NS_GDKEVENT_MATCH_MASK 0x1FFF /* GDK_SHIFT_MASK .. GDK_BUTTON5_MASK */
5542 GdkDisplay* gdkDisplay = gtk_widget_get_display(widget);
5543 Display* dpy = GDK_DISPLAY_XDISPLAY(gdkDisplay);
5544 while (XPending(dpy)) {
5545 XEvent next_event;
5546 XPeekEvent(dpy, &next_event);
5547 GdkWindow* nextGdkWindow =
5548 gdk_x11_window_lookup_for_display(gdkDisplay, next_event.xany.window);
5549 if (nextGdkWindow != event->window ||
5550 next_event.type != KeyPress ||
5551 next_event.xkey.keycode != event->hardware_keycode ||
5552 next_event.xkey.state != (event->state & NS_GDKEVENT_MATCH_MASK)) {
5553 break;
5555 XNextEvent(dpy, &next_event);
5556 event->time = next_event.xkey.time;
5558 #endif
5560 return focusWindow->OnKeyPressEvent(event);
5563 static gboolean
5564 key_release_event_cb(GtkWidget *widget, GdkEventKey *event)
5566 LOG(("key_release_event_cb\n"));
5568 UpdateLastInputEventTime(event);
5570 // find the window with focus and dispatch this event to that widget
5571 nsWindow *window = get_window_for_gtk_widget(widget);
5572 if (!window)
5573 return FALSE;
5575 nsRefPtr<nsWindow> focusWindow = gFocusWindow ? gFocusWindow : window;
5577 return focusWindow->OnKeyReleaseEvent(event);
5580 static gboolean
5581 scroll_event_cb(GtkWidget *widget, GdkEventScroll *event)
5583 nsWindow *window = GetFirstNSWindowForGDKWindow(event->window);
5584 if (!window)
5585 return FALSE;
5587 window->OnScrollEvent(event);
5589 return TRUE;
5592 static gboolean
5593 visibility_notify_event_cb (GtkWidget *widget, GdkEventVisibility *event)
5595 nsRefPtr<nsWindow> window = get_window_for_gdk_window(event->window);
5596 if (!window)
5597 return FALSE;
5599 window->OnVisibilityNotifyEvent(event);
5601 return TRUE;
5604 static void
5605 hierarchy_changed_cb (GtkWidget *widget,
5606 GtkWidget *previous_toplevel)
5608 GtkWidget *toplevel = gtk_widget_get_toplevel(widget);
5609 GdkWindowState old_window_state = GDK_WINDOW_STATE_WITHDRAWN;
5610 GdkEventWindowState event;
5612 event.new_window_state = GDK_WINDOW_STATE_WITHDRAWN;
5614 if (GTK_IS_WINDOW(previous_toplevel)) {
5615 g_signal_handlers_disconnect_by_func(previous_toplevel,
5616 FuncToGpointer(window_state_event_cb),
5617 widget);
5618 GdkWindow *win = gtk_widget_get_window(previous_toplevel);
5619 if (win) {
5620 old_window_state = gdk_window_get_state(win);
5624 if (GTK_IS_WINDOW(toplevel)) {
5625 g_signal_connect_swapped(toplevel, "window-state-event",
5626 G_CALLBACK(window_state_event_cb), widget);
5627 GdkWindow *win = gtk_widget_get_window(toplevel);
5628 if (win) {
5629 event.new_window_state = gdk_window_get_state(win);
5633 event.changed_mask = static_cast<GdkWindowState>
5634 (old_window_state ^ event.new_window_state);
5636 if (event.changed_mask) {
5637 event.type = GDK_WINDOW_STATE;
5638 event.window = nullptr;
5639 event.send_event = TRUE;
5640 window_state_event_cb(widget, &event);
5644 static gboolean
5645 window_state_event_cb (GtkWidget *widget, GdkEventWindowState *event)
5647 nsRefPtr<nsWindow> window = get_window_for_gtk_widget(widget);
5648 if (!window)
5649 return FALSE;
5651 window->OnWindowStateEvent(widget, event);
5653 return FALSE;
5656 static void
5657 theme_changed_cb (GtkSettings *settings, GParamSpec *pspec, nsWindow *data)
5659 nsRefPtr<nsWindow> window = data;
5660 window->ThemeChanged();
5663 //////////////////////////////////////////////////////////////////////
5664 // These are all of our drag and drop operations
5666 void
5667 nsWindow::InitDragEvent(WidgetDragEvent &aEvent)
5669 // set the keyboard modifiers
5670 guint modifierState = KeymapWrapper::GetCurrentModifierState();
5671 KeymapWrapper::InitInputEvent(aEvent, modifierState);
5674 static gboolean
5675 drag_motion_event_cb(GtkWidget *aWidget,
5676 GdkDragContext *aDragContext,
5677 gint aX,
5678 gint aY,
5679 guint aTime,
5680 gpointer aData)
5682 nsRefPtr<nsWindow> window = get_window_for_gtk_widget(aWidget);
5683 if (!window)
5684 return FALSE;
5686 // figure out which internal widget this drag motion actually happened on
5687 nscoord retx = 0;
5688 nscoord rety = 0;
5690 GdkWindow *innerWindow =
5691 get_inner_gdk_window(gtk_widget_get_window(aWidget), aX, aY,
5692 &retx, &rety);
5693 nsRefPtr<nsWindow> innerMostWindow = get_window_for_gdk_window(innerWindow);
5695 if (!innerMostWindow) {
5696 innerMostWindow = window;
5699 LOGDRAG(("nsWindow drag-motion signal for %p\n", (void*)innerMostWindow));
5701 return nsDragService::GetInstance()->
5702 ScheduleMotionEvent(innerMostWindow, aDragContext,
5703 nsIntPoint(retx, rety), aTime);
5706 static void
5707 drag_leave_event_cb(GtkWidget *aWidget,
5708 GdkDragContext *aDragContext,
5709 guint aTime,
5710 gpointer aData)
5712 nsRefPtr<nsWindow> window = get_window_for_gtk_widget(aWidget);
5713 if (!window)
5714 return;
5716 nsDragService *dragService = nsDragService::GetInstance();
5718 nsWindow *mostRecentDragWindow = dragService->GetMostRecentDestWindow();
5719 if (!mostRecentDragWindow) {
5720 // This can happen when the target will not accept a drop. A GTK drag
5721 // source sends the leave message to the destination before the
5722 // drag-failed signal on the source widget, but the leave message goes
5723 // via the X server, and so doesn't get processed at least until the
5724 // event loop runs again.
5725 return;
5728 GtkWidget *mozContainer = mostRecentDragWindow->GetMozContainerWidget();
5729 if (aWidget != mozContainer)
5731 // When the drag moves between widgets, GTK can send leave signal for
5732 // the old widget after the motion or drop signal for the new widget.
5733 // We'll send the leave event when the motion or drop event is run.
5734 return;
5737 LOGDRAG(("nsWindow drag-leave signal for %p\n",
5738 (void*)mostRecentDragWindow));
5740 dragService->ScheduleLeaveEvent();
5744 static gboolean
5745 drag_drop_event_cb(GtkWidget *aWidget,
5746 GdkDragContext *aDragContext,
5747 gint aX,
5748 gint aY,
5749 guint aTime,
5750 gpointer aData)
5752 nsRefPtr<nsWindow> window = get_window_for_gtk_widget(aWidget);
5753 if (!window)
5754 return FALSE;
5756 // figure out which internal widget this drag motion actually happened on
5757 nscoord retx = 0;
5758 nscoord rety = 0;
5760 GdkWindow *innerWindow =
5761 get_inner_gdk_window(gtk_widget_get_window(aWidget), aX, aY,
5762 &retx, &rety);
5763 nsRefPtr<nsWindow> innerMostWindow = get_window_for_gdk_window(innerWindow);
5765 if (!innerMostWindow) {
5766 innerMostWindow = window;
5769 LOGDRAG(("nsWindow drag-drop signal for %p\n", (void*)innerMostWindow));
5771 return nsDragService::GetInstance()->
5772 ScheduleDropEvent(innerMostWindow, aDragContext,
5773 nsIntPoint(retx, rety), aTime);
5776 static void
5777 drag_data_received_event_cb(GtkWidget *aWidget,
5778 GdkDragContext *aDragContext,
5779 gint aX,
5780 gint aY,
5781 GtkSelectionData *aSelectionData,
5782 guint aInfo,
5783 guint aTime,
5784 gpointer aData)
5786 nsRefPtr<nsWindow> window = get_window_for_gtk_widget(aWidget);
5787 if (!window)
5788 return;
5790 window->OnDragDataReceivedEvent(aWidget,
5791 aDragContext,
5792 aX, aY,
5793 aSelectionData,
5794 aInfo, aTime, aData);
5797 static nsresult
5798 initialize_prefs(void)
5800 gRaiseWindows =
5801 Preferences::GetBool("mozilla.widget.raise-on-setfocus", true);
5803 return NS_OK;
5806 static GdkWindow *
5807 get_inner_gdk_window (GdkWindow *aWindow,
5808 gint x, gint y,
5809 gint *retx, gint *rety)
5811 gint cx, cy, cw, ch;
5812 GList *children = gdk_window_peek_children(aWindow);
5813 for (GList *child = g_list_last(children);
5814 child;
5815 child = g_list_previous(child)) {
5816 GdkWindow *childWindow = (GdkWindow *) child->data;
5817 if (get_window_for_gdk_window(childWindow)) {
5818 #if (MOZ_WIDGET_GTK == 2)
5819 gdk_window_get_geometry(childWindow, &cx, &cy, &cw, &ch, nullptr);
5820 #else
5821 gdk_window_get_geometry(childWindow, &cx, &cy, &cw, &ch);
5822 #endif
5823 if ((cx < x) && (x < (cx + cw)) &&
5824 (cy < y) && (y < (cy + ch)) &&
5825 gdk_window_is_visible(childWindow)) {
5826 return get_inner_gdk_window(childWindow,
5827 x - cx, y - cy,
5828 retx, rety);
5832 *retx = x;
5833 *rety = y;
5834 return aWindow;
5837 static inline bool
5838 is_context_menu_key(const WidgetKeyboardEvent& aKeyEvent)
5840 return ((aKeyEvent.keyCode == NS_VK_F10 && aKeyEvent.IsShift() &&
5841 !aKeyEvent.IsControl() && !aKeyEvent.IsMeta() &&
5842 !aKeyEvent.IsAlt()) ||
5843 (aKeyEvent.keyCode == NS_VK_CONTEXT_MENU && !aKeyEvent.IsShift() &&
5844 !aKeyEvent.IsControl() && !aKeyEvent.IsMeta() &&
5845 !aKeyEvent.IsAlt()));
5848 static int
5849 is_parent_ungrab_enter(GdkEventCrossing *aEvent)
5851 return (GDK_CROSSING_UNGRAB == aEvent->mode) &&
5852 ((GDK_NOTIFY_ANCESTOR == aEvent->detail) ||
5853 (GDK_NOTIFY_VIRTUAL == aEvent->detail));
5857 static int
5858 is_parent_grab_leave(GdkEventCrossing *aEvent)
5860 return (GDK_CROSSING_GRAB == aEvent->mode) &&
5861 ((GDK_NOTIFY_ANCESTOR == aEvent->detail) ||
5862 (GDK_NOTIFY_VIRTUAL == aEvent->detail));
5865 #ifdef ACCESSIBILITY
5866 void
5867 nsWindow::CreateRootAccessible()
5869 if (mIsTopLevel && !mRootAccessible) {
5870 LOG(("nsWindow:: Create Toplevel Accessibility\n"));
5871 mRootAccessible = GetRootAccessible();
5875 void
5876 nsWindow::DispatchEventToRootAccessible(uint32_t aEventType)
5878 if (!a11y::ShouldA11yBeEnabled()) {
5879 return;
5882 nsCOMPtr<nsIAccessibilityService> accService =
5883 services::GetAccessibilityService();
5884 if (!accService) {
5885 return;
5888 // Get the root document accessible and fire event to it.
5889 a11y::Accessible* acc = GetRootAccessible();
5890 if (acc) {
5891 accService->FireAccessibleEvent(aEventType, acc);
5895 void
5896 nsWindow::DispatchActivateEventAccessible(void)
5898 DispatchEventToRootAccessible(nsIAccessibleEvent::EVENT_WINDOW_ACTIVATE);
5901 void
5902 nsWindow::DispatchDeactivateEventAccessible(void)
5904 DispatchEventToRootAccessible(nsIAccessibleEvent::EVENT_WINDOW_DEACTIVATE);
5907 void
5908 nsWindow::DispatchMaximizeEventAccessible(void)
5910 DispatchEventToRootAccessible(nsIAccessibleEvent::EVENT_WINDOW_MAXIMIZE);
5913 void
5914 nsWindow::DispatchMinimizeEventAccessible(void)
5916 DispatchEventToRootAccessible(nsIAccessibleEvent::EVENT_WINDOW_MINIMIZE);
5919 void
5920 nsWindow::DispatchRestoreEventAccessible(void)
5922 DispatchEventToRootAccessible(nsIAccessibleEvent::EVENT_WINDOW_RESTORE);
5925 #endif /* #ifdef ACCESSIBILITY */
5927 // nsChildWindow class
5929 nsChildWindow::nsChildWindow()
5933 nsChildWindow::~nsChildWindow()
5937 NS_IMETHODIMP
5938 nsWindow::NotifyIME(const IMENotification& aIMENotification)
5940 if (MOZ_UNLIKELY(!mIMModule)) {
5941 switch (aIMENotification.mMessage) {
5942 case NOTIFY_IME_OF_CURSOR_POS_CHANGED:
5943 case REQUEST_TO_COMMIT_COMPOSITION:
5944 case REQUEST_TO_CANCEL_COMPOSITION:
5945 case NOTIFY_IME_OF_FOCUS:
5946 case NOTIFY_IME_OF_BLUR:
5947 return NS_ERROR_NOT_AVAILABLE;
5948 default:
5949 break;
5952 switch (aIMENotification.mMessage) {
5953 // TODO: We should replace NOTIFY_IME_OF_CURSOR_POS_CHANGED with
5954 // NOTIFY_IME_OF_SELECTION_CHANGE. The required behavior is
5955 // really different from committing composition.
5956 case NOTIFY_IME_OF_CURSOR_POS_CHANGED:
5957 case REQUEST_TO_COMMIT_COMPOSITION:
5958 return mIMModule->CommitIMEComposition(this);
5959 case REQUEST_TO_CANCEL_COMPOSITION:
5960 return mIMModule->CancelIMEComposition(this);
5961 case NOTIFY_IME_OF_FOCUS:
5962 mIMModule->OnFocusChangeInGecko(true);
5963 return NS_OK;
5964 case NOTIFY_IME_OF_BLUR:
5965 mIMModule->OnFocusChangeInGecko(false);
5966 return NS_OK;
5967 case NOTIFY_IME_OF_COMPOSITION_UPDATE:
5968 mIMModule->OnUpdateComposition();
5969 return NS_OK;
5970 default:
5971 return NS_ERROR_NOT_IMPLEMENTED;
5975 NS_IMETHODIMP_(void)
5976 nsWindow::SetInputContext(const InputContext& aContext,
5977 const InputContextAction& aAction)
5979 if (!mIMModule) {
5980 return;
5982 mIMModule->SetInputContext(this, &aContext, &aAction);
5985 NS_IMETHODIMP_(InputContext)
5986 nsWindow::GetInputContext()
5988 InputContext context;
5989 if (!mIMModule) {
5990 context.mIMEState.mEnabled = IMEState::DISABLED;
5991 context.mIMEState.mOpen = IMEState::OPEN_STATE_NOT_SUPPORTED;
5992 // If IME context isn't available on this widget, we should set |this|
5993 // instead of nullptr since nullptr means that the platform has only one
5994 // context per process.
5995 context.mNativeIMEContext = this;
5996 } else {
5997 context = mIMModule->GetInputContext();
5998 context.mNativeIMEContext = mIMModule;
6000 return context;
6003 NS_IMETHODIMP_(bool)
6004 nsWindow::ExecuteNativeKeyBinding(NativeKeyBindingsType aType,
6005 const WidgetKeyboardEvent& aEvent,
6006 DoCommandCallback aCallback,
6007 void* aCallbackData)
6009 NativeKeyBindings* keyBindings = NativeKeyBindings::GetInstance(aType);
6010 return keyBindings->Execute(aEvent, aCallback, aCallbackData);
6013 NS_IMETHODIMP
6014 nsWindow::GetToggledKeyState(uint32_t aKeyCode, bool* aLEDState)
6016 NS_ENSURE_ARG_POINTER(aLEDState);
6018 KeymapWrapper::Modifiers modifier;
6019 switch (aKeyCode) {
6020 case NS_VK_CAPS_LOCK: modifier = KeymapWrapper::CAPS_LOCK; break;
6021 case NS_VK_NUM_LOCK: modifier = KeymapWrapper::NUM_LOCK; break;
6022 case NS_VK_SCROLL_LOCK: modifier = KeymapWrapper::SCROLL_LOCK; break;
6023 default: return NS_ERROR_INVALID_ARG;
6026 *aLEDState =
6027 KeymapWrapper::AreModifiersCurrentlyActive(modifier);
6028 return NS_OK;
6031 #if defined(MOZ_X11) && (MOZ_WIDGET_GTK == 2)
6032 /* static */ already_AddRefed<gfxASurface>
6033 nsWindow::GetSurfaceForGdkDrawable(GdkDrawable* aDrawable,
6034 const nsIntSize& aSize)
6036 GdkVisual* visual = gdk_drawable_get_visual(aDrawable);
6037 Screen* xScreen =
6038 gdk_x11_screen_get_xscreen(gdk_drawable_get_screen(aDrawable));
6039 Display* xDisplay = DisplayOfScreen(xScreen);
6040 Drawable xDrawable = gdk_x11_drawable_get_xid(aDrawable);
6042 nsRefPtr<gfxASurface> result;
6044 if (visual) {
6045 Visual* xVisual = gdk_x11_visual_get_xvisual(visual);
6047 result = new gfxXlibSurface(xDisplay, xDrawable, xVisual,
6048 gfxIntSize(aSize.width, aSize.height));
6049 } else {
6050 // no visual? we must be using an xrender format. Find a format
6051 // for this depth.
6052 XRenderPictFormat *pf = nullptr;
6053 switch (gdk_drawable_get_depth(aDrawable)) {
6054 case 32:
6055 pf = XRenderFindStandardFormat(xDisplay, PictStandardARGB32);
6056 break;
6057 case 24:
6058 pf = XRenderFindStandardFormat(xDisplay, PictStandardRGB24);
6059 break;
6060 default:
6061 NS_ERROR("Don't know how to handle the given depth!");
6062 break;
6065 result = new gfxXlibSurface(xScreen, xDrawable, pf,
6066 gfxIntSize(aSize.width, aSize.height));
6069 return result.forget();
6071 #endif
6073 TemporaryRef<DrawTarget>
6074 nsWindow::StartRemoteDrawing()
6076 gfxASurface *surf = GetThebesSurface();
6077 if (!surf) {
6078 return nullptr;
6081 IntSize size(surf->GetSize().width, surf->GetSize().height);
6082 if (size.width <= 0 || size.height <= 0) {
6083 return nullptr;
6086 return gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(surf, size);
6089 // return the gfxASurface for rendering to this widget
6090 gfxASurface*
6091 nsWindow::GetThebesSurface()
6092 #if (MOZ_WIDGET_GTK == 3)
6094 return GetThebesSurface(nullptr);
6096 gfxASurface*
6097 nsWindow::GetThebesSurface(cairo_t *cr)
6098 #endif
6100 if (!mGdkWindow)
6101 return nullptr;
6103 #ifdef MOZ_X11
6104 gint width, height;
6106 #if (MOZ_WIDGET_GTK == 2)
6107 gdk_drawable_get_size(GDK_DRAWABLE(mGdkWindow), &width, &height);
6108 #else
6109 width = gdk_window_get_width(mGdkWindow);
6110 height = gdk_window_get_height(mGdkWindow);
6111 #endif
6113 // Owen Taylor says this is the right thing to do!
6114 width = std::min(32767, width);
6115 height = std::min(32767, height);
6116 gfxIntSize size(width, height);
6118 GdkVisual *gdkVisual = gdk_window_get_visual(mGdkWindow);
6119 Visual* visual = gdk_x11_visual_get_xvisual(gdkVisual);
6121 # ifdef MOZ_HAVE_SHMIMAGE
6122 bool usingShm = false;
6123 if (nsShmImage::UseShm()) {
6124 // EnsureShmImage() is a dangerous interface, but we guarantee
6125 // that the thebes surface and the shmimage have the same
6126 // lifetime
6127 mThebesSurface =
6128 nsShmImage::EnsureShmImage(size,
6129 visual, gdk_visual_get_depth(gdkVisual),
6130 mShmImage);
6131 usingShm = mThebesSurface != nullptr;
6133 if (!usingShm)
6134 # endif // MOZ_HAVE_SHMIMAGE
6136 #if (MOZ_WIDGET_GTK == 3)
6137 #if MOZ_TREE_CAIRO
6138 #error "cairo-gtk3 target must be built with --enable-system-cairo"
6139 #else
6140 if (cr) {
6141 cairo_surface_t *surf = cairo_get_target(cr);
6142 if (cairo_surface_status(surf) != CAIRO_STATUS_SUCCESS) {
6143 NS_NOTREACHED("Missing cairo target?");
6144 return nullptr;
6146 mThebesSurface = gfxASurface::Wrap(surf);
6147 } else
6148 #endif
6149 #endif // (MOZ_WIDGET_GTK == 3)
6150 mThebesSurface = new gfxXlibSurface
6151 (GDK_WINDOW_XDISPLAY(mGdkWindow),
6152 gdk_x11_window_get_xid(mGdkWindow),
6153 visual,
6154 size);
6156 #endif // MOZ_X11
6158 // if the surface creation is reporting an error, then
6159 // we don't have a surface to give back
6160 if (mThebesSurface && mThebesSurface->CairoStatus() != 0) {
6161 mThebesSurface = nullptr;
6164 return mThebesSurface;
6167 // Code shared begin BeginMoveDrag and BeginResizeDrag
6168 bool
6169 nsWindow::GetDragInfo(WidgetMouseEvent* aMouseEvent,
6170 GdkWindow** aWindow, gint* aButton,
6171 gint* aRootX, gint* aRootY)
6173 if (aMouseEvent->button != WidgetMouseEvent::eLeftButton) {
6174 // we can only begin a move drag with the left mouse button
6175 return false;
6177 *aButton = 1;
6179 // get the gdk window for this widget
6180 GdkWindow* gdk_window = mGdkWindow;
6181 if (!gdk_window) {
6182 return false;
6184 NS_ABORT_IF_FALSE(GDK_IS_WINDOW(gdk_window), "must really be window");
6186 // find the top-level window
6187 gdk_window = gdk_window_get_toplevel(gdk_window);
6188 NS_ABORT_IF_FALSE(gdk_window,
6189 "gdk_window_get_toplevel should not return null");
6190 *aWindow = gdk_window;
6192 if (!aMouseEvent->widget) {
6193 return false;
6196 // FIXME: It would be nice to have the widget position at the time
6197 // of the event, but it's relatively unlikely that the widget has
6198 // moved since the mousedown. (On the other hand, it's quite likely
6199 // that the mouse has moved, which is why we use the mouse position
6200 // from the event.)
6201 nsIntPoint offset = aMouseEvent->widget->WidgetToScreenOffset();
6202 *aRootX = aMouseEvent->refPoint.x + offset.x;
6203 *aRootY = aMouseEvent->refPoint.y + offset.y;
6205 return true;
6208 NS_IMETHODIMP
6209 nsWindow::BeginMoveDrag(WidgetMouseEvent* aEvent)
6211 NS_ABORT_IF_FALSE(aEvent, "must have event");
6212 NS_ABORT_IF_FALSE(aEvent->mClass == eMouseEventClass,
6213 "event must have correct struct type");
6215 GdkWindow *gdk_window;
6216 gint button, screenX, screenY;
6217 if (!GetDragInfo(aEvent, &gdk_window, &button, &screenX, &screenY)) {
6218 return NS_ERROR_FAILURE;
6221 // tell the window manager to start the move
6222 gdk_window_begin_move_drag(gdk_window, button, screenX, screenY,
6223 aEvent->time);
6225 return NS_OK;
6228 NS_IMETHODIMP
6229 nsWindow::BeginResizeDrag(WidgetGUIEvent* aEvent,
6230 int32_t aHorizontal,
6231 int32_t aVertical)
6233 NS_ENSURE_ARG_POINTER(aEvent);
6235 if (aEvent->mClass != eMouseEventClass) {
6236 // you can only begin a resize drag with a mouse event
6237 return NS_ERROR_INVALID_ARG;
6240 GdkWindow *gdk_window;
6241 gint button, screenX, screenY;
6242 if (!GetDragInfo(aEvent->AsMouseEvent(), &gdk_window, &button,
6243 &screenX, &screenY)) {
6244 return NS_ERROR_FAILURE;
6247 // work out what GdkWindowEdge we're talking about
6248 GdkWindowEdge window_edge;
6249 if (aVertical < 0) {
6250 if (aHorizontal < 0) {
6251 window_edge = GDK_WINDOW_EDGE_NORTH_WEST;
6252 } else if (aHorizontal == 0) {
6253 window_edge = GDK_WINDOW_EDGE_NORTH;
6254 } else {
6255 window_edge = GDK_WINDOW_EDGE_NORTH_EAST;
6257 } else if (aVertical == 0) {
6258 if (aHorizontal < 0) {
6259 window_edge = GDK_WINDOW_EDGE_WEST;
6260 } else if (aHorizontal == 0) {
6261 return NS_ERROR_INVALID_ARG;
6262 } else {
6263 window_edge = GDK_WINDOW_EDGE_EAST;
6265 } else {
6266 if (aHorizontal < 0) {
6267 window_edge = GDK_WINDOW_EDGE_SOUTH_WEST;
6268 } else if (aHorizontal == 0) {
6269 window_edge = GDK_WINDOW_EDGE_SOUTH;
6270 } else {
6271 window_edge = GDK_WINDOW_EDGE_SOUTH_EAST;
6275 // tell the window manager to start the resize
6276 gdk_window_begin_resize_drag(gdk_window, window_edge, button,
6277 screenX, screenY, aEvent->time);
6279 return NS_OK;
6282 nsIWidget::LayerManager*
6283 nsWindow::GetLayerManager(PLayerTransactionChild* aShadowManager,
6284 LayersBackend aBackendHint,
6285 LayerManagerPersistence aPersistence,
6286 bool* aAllowRetaining)
6288 if (!mLayerManager && eTransparencyTransparent == GetTransparencyMode()) {
6289 mLayerManager = CreateBasicLayerManager();
6292 return nsBaseWidget::GetLayerManager(aShadowManager, aBackendHint,
6293 aPersistence, aAllowRetaining);
6296 void
6297 nsWindow::ClearCachedResources()
6299 if (mLayerManager &&
6300 mLayerManager->GetBackendType() == mozilla::layers::LayersBackend::LAYERS_BASIC) {
6301 mLayerManager->ClearCachedResources();
6304 GList* children = gdk_window_peek_children(mGdkWindow);
6305 for (GList* list = children; list; list = list->next) {
6306 nsWindow* window = get_window_for_gdk_window(GDK_WINDOW(list->data));
6307 if (window) {
6308 window->ClearCachedResources();
6313 nsresult
6314 nsWindow::SynthesizeNativeMouseEvent(nsIntPoint aPoint,
6315 uint32_t aNativeMessage,
6316 uint32_t aModifierFlags)
6318 if (!mGdkWindow) {
6319 return NS_OK;
6322 GdkDisplay* display = gdk_window_get_display(mGdkWindow);
6324 // When a button-release event is requested, create it here and put it in the
6325 // event queue. This will not emit a motion event - this needs to be done
6326 // explicitly *before* requesting a button-release. You will also need to wait
6327 // for the motion event to be dispatched before requesting a button-release
6328 // event to maintain the desired event order.
6329 if (aNativeMessage == GDK_BUTTON_RELEASE) {
6330 GdkEvent event;
6331 memset(&event, 0, sizeof(GdkEvent));
6332 event.type = (GdkEventType)aNativeMessage;
6333 event.button.button = 1;
6334 event.button.window = mGdkWindow;
6335 event.button.time = GDK_CURRENT_TIME;
6337 #if (MOZ_WIDGET_GTK == 3)
6338 // Get device for event source
6339 GdkDeviceManager *device_manager = gdk_display_get_device_manager(display);
6340 event.button.device = gdk_device_manager_get_client_pointer(device_manager);
6341 #endif
6343 gdk_event_put(&event);
6344 } else {
6345 // We don't support specific events other than button-release. In case
6346 // aNativeMessage != GDK_BUTTON_RELEASE we'll synthesize a motion event
6347 // that will be emitted by gdk_display_warp_pointer().
6348 GdkScreen* screen = gdk_window_get_screen(mGdkWindow);
6349 gdk_display_warp_pointer(display, screen, aPoint.x, aPoint.y);
6352 return NS_OK;