Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
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"
103 }
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 GDK_SCROLL_MASK |
140 GDK_POINTER_MOTION_MASK;
142 /* utility functions */
143 static bool is_mouse_in_window(GdkWindow* aWindow,
144 gdouble aMouseX, gdouble aMouseY);
145 static nsWindow *get_window_for_gtk_widget(GtkWidget *widget);
146 static nsWindow *get_window_for_gdk_window(GdkWindow *window);
147 static GtkWidget *get_gtk_widget_for_gdk_window(GdkWindow *window);
148 static GdkCursor *get_gtk_cursor(nsCursor aCursor);
150 static GdkWindow *get_inner_gdk_window (GdkWindow *aWindow,
151 gint x, gint y,
152 gint *retx, gint *rety);
154 static inline bool is_context_menu_key(const WidgetKeyboardEvent& inKeyEvent);
156 static int is_parent_ungrab_enter(GdkEventCrossing *aEvent);
157 static int is_parent_grab_leave(GdkEventCrossing *aEvent);
159 static void GetBrandName(nsXPIDLString& brandName);
161 /* callbacks from widgets */
162 #if (MOZ_WIDGET_GTK == 2)
163 static gboolean expose_event_cb (GtkWidget *widget,
164 GdkEventExpose *event);
165 #else
166 static gboolean expose_event_cb (GtkWidget *widget,
167 cairo_t *rect);
168 #endif
169 static gboolean configure_event_cb (GtkWidget *widget,
170 GdkEventConfigure *event);
171 static void container_unrealize_cb (GtkWidget *widget);
172 static void size_allocate_cb (GtkWidget *widget,
173 GtkAllocation *allocation);
174 static gboolean delete_event_cb (GtkWidget *widget,
175 GdkEventAny *event);
176 static gboolean enter_notify_event_cb (GtkWidget *widget,
177 GdkEventCrossing *event);
178 static gboolean leave_notify_event_cb (GtkWidget *widget,
179 GdkEventCrossing *event);
180 static gboolean motion_notify_event_cb (GtkWidget *widget,
181 GdkEventMotion *event);
182 static gboolean button_press_event_cb (GtkWidget *widget,
183 GdkEventButton *event);
184 static gboolean button_release_event_cb (GtkWidget *widget,
185 GdkEventButton *event);
186 static gboolean focus_in_event_cb (GtkWidget *widget,
187 GdkEventFocus *event);
188 static gboolean focus_out_event_cb (GtkWidget *widget,
189 GdkEventFocus *event);
190 static gboolean key_press_event_cb (GtkWidget *widget,
191 GdkEventKey *event);
192 static gboolean key_release_event_cb (GtkWidget *widget,
193 GdkEventKey *event);
194 static gboolean scroll_event_cb (GtkWidget *widget,
195 GdkEventScroll *event);
196 static gboolean visibility_notify_event_cb(GtkWidget *widget,
197 GdkEventVisibility *event);
198 static void hierarchy_changed_cb (GtkWidget *widget,
199 GtkWidget *previous_toplevel);
200 static gboolean window_state_event_cb (GtkWidget *widget,
201 GdkEventWindowState *event);
202 static void theme_changed_cb (GtkSettings *settings,
203 GParamSpec *pspec,
204 nsWindow *data);
205 static nsWindow* GetFirstNSWindowForGDKWindow (GdkWindow *aGdkWindow);
207 #ifdef __cplusplus
208 extern "C" {
209 #endif /* __cplusplus */
210 #ifdef MOZ_X11
211 static GdkFilterReturn popup_take_focus_filter (GdkXEvent *gdk_xevent,
212 GdkEvent *event,
213 gpointer data);
214 static GdkFilterReturn plugin_window_filter_func (GdkXEvent *gdk_xevent,
215 GdkEvent *event,
216 gpointer data);
217 static GdkFilterReturn plugin_client_message_filter (GdkXEvent *xevent,
218 GdkEvent *event,
219 gpointer data);
220 #endif /* MOZ_X11 */
221 #ifdef __cplusplus
222 }
223 #endif /* __cplusplus */
225 static gboolean drag_motion_event_cb (GtkWidget *aWidget,
226 GdkDragContext *aDragContext,
227 gint aX,
228 gint aY,
229 guint aTime,
230 gpointer aData);
231 static void drag_leave_event_cb (GtkWidget *aWidget,
232 GdkDragContext *aDragContext,
233 guint aTime,
234 gpointer aData);
235 static gboolean drag_drop_event_cb (GtkWidget *aWidget,
236 GdkDragContext *aDragContext,
237 gint aX,
238 gint aY,
239 guint aTime,
240 gpointer aData);
241 static void drag_data_received_event_cb(GtkWidget *aWidget,
242 GdkDragContext *aDragContext,
243 gint aX,
244 gint aY,
245 GtkSelectionData *aSelectionData,
246 guint aInfo,
247 guint32 aTime,
248 gpointer aData);
250 /* initialization static functions */
251 static nsresult initialize_prefs (void);
253 static guint32 sLastUserInputTime = GDK_CURRENT_TIME;
254 static guint32 sRetryGrabTime;
256 static NS_DEFINE_IID(kCDragServiceCID, NS_DRAGSERVICE_CID);
258 // The window from which the focus manager asks us to dispatch key events.
259 static nsWindow *gFocusWindow = nullptr;
260 static bool gBlockActivateEvent = false;
261 static bool gGlobalsInitialized = false;
262 static bool gRaiseWindows = true;
263 static nsWindow *gPluginFocusWindow = nullptr;
266 #define NS_WINDOW_TITLE_MAX_LENGTH 4095
268 // If after selecting profile window, the startup fail, please refer to
269 // http://bugzilla.gnome.org/show_bug.cgi?id=88940
271 // needed for imgIContainer cursors
272 // GdkDisplay* was added in 2.2
273 typedef struct _GdkDisplay GdkDisplay;
275 #define kWindowPositionSlop 20
277 // cursor cache
278 static GdkCursor *gCursorCache[eCursorCount];
280 static GtkWidget *gInvisibleContainer = nullptr;
282 // Sometimes this actually also includes the state of the modifier keys, but
283 // only the button state bits are used.
284 static guint gButtonState;
286 // nsAutoRef<pixman_region32> uses nsSimpleRef<> to know how to automatically
287 // destroy regions.
288 template <>
289 class nsSimpleRef<pixman_region32> : public pixman_region32 {
290 protected:
291 typedef pixman_region32 RawRef;
293 nsSimpleRef() { data = nullptr; }
294 nsSimpleRef(const RawRef &aRawRef) : pixman_region32(aRawRef) { }
296 static void Release(pixman_region32& region) {
297 pixman_region32_fini(®ion);
298 }
299 // Whether this needs to be released:
300 bool HaveResource() const { return data != nullptr; }
302 pixman_region32& get() { return *this; }
303 };
305 static inline int32_t
306 GetBitmapStride(int32_t width)
307 {
308 #if defined(MOZ_X11) || (MOZ_WIDGET_GTK == 2)
309 return (width+7)/8;
310 #else
311 return cairo_format_stride_for_width(CAIRO_FORMAT_A1, width);
312 #endif
313 }
315 static inline bool TimestampIsNewerThan(guint32 a, guint32 b)
316 {
317 // Timestamps are just the least significant bits of a monotonically
318 // increasing function, and so the use of unsigned overflow arithmetic.
319 return a - b <= G_MAXUINT32/2;
320 }
322 static void
323 UpdateLastInputEventTime(void *aGdkEvent)
324 {
325 nsCOMPtr<nsIIdleServiceInternal> idleService =
326 do_GetService("@mozilla.org/widget/idleservice;1");
327 if (idleService) {
328 idleService->ResetIdleTimeOut(0);
329 }
331 guint timestamp = gdk_event_get_time(static_cast<GdkEvent*>(aGdkEvent));
332 if (timestamp == GDK_CURRENT_TIME)
333 return;
335 sLastUserInputTime = timestamp;
336 }
338 nsWindow::nsWindow()
339 {
340 mIsTopLevel = false;
341 mIsDestroyed = false;
342 mNeedsResize = false;
343 mNeedsMove = false;
344 mListenForResizes = false;
345 mIsShown = false;
346 mNeedsShow = false;
347 mEnabled = true;
348 mCreated = false;
350 mContainer = nullptr;
351 mGdkWindow = nullptr;
352 mShell = nullptr;
353 mHasMappedToplevel = false;
354 mIsFullyObscured = false;
355 mRetryPointerGrab = false;
356 mWindowType = eWindowType_child;
357 mSizeState = nsSizeMode_Normal;
358 mLastSizeMode = nsSizeMode_Normal;
359 mSizeConstraints.mMaxSize = GetSafeWindowSize(mSizeConstraints.mMaxSize);
361 #ifdef MOZ_X11
362 mOldFocusWindow = 0;
363 #endif /* MOZ_X11 */
364 mPluginType = PluginType_NONE;
366 if (!gGlobalsInitialized) {
367 gGlobalsInitialized = true;
369 // It's OK if either of these fail, but it may not be one day.
370 initialize_prefs();
371 }
373 mLastMotionPressure = 0;
375 #ifdef ACCESSIBILITY
376 mRootAccessible = nullptr;
377 #endif
379 mIsTransparent = false;
380 mTransparencyBitmap = nullptr;
382 mTransparencyBitmapWidth = 0;
383 mTransparencyBitmapHeight = 0;
384 }
386 nsWindow::~nsWindow()
387 {
388 LOG(("nsWindow::~nsWindow() [%p]\n", (void *)this));
390 delete[] mTransparencyBitmap;
391 mTransparencyBitmap = nullptr;
393 Destroy();
394 }
396 /* static */ void
397 nsWindow::ReleaseGlobals()
398 {
399 for (uint32_t i = 0; i < ArrayLength(gCursorCache); ++i) {
400 if (gCursorCache[i]) {
401 gdk_cursor_unref(gCursorCache[i]);
402 gCursorCache[i] = nullptr;
403 }
404 }
405 }
407 NS_IMPL_ISUPPORTS_INHERITED(nsWindow, nsBaseWidget,
408 nsISupportsWeakReference)
410 void
411 nsWindow::CommonCreate(nsIWidget *aParent, bool aListenForResizes)
412 {
413 mParent = aParent;
414 mListenForResizes = aListenForResizes;
415 mCreated = true;
416 }
418 void
419 nsWindow::DispatchActivateEvent(void)
420 {
421 NS_ASSERTION(mContainer || mIsDestroyed,
422 "DispatchActivateEvent only intended for container windows");
424 #ifdef ACCESSIBILITY
425 DispatchActivateEventAccessible();
426 #endif //ACCESSIBILITY
428 if (mWidgetListener)
429 mWidgetListener->WindowActivated();
430 }
432 void
433 nsWindow::DispatchDeactivateEvent(void)
434 {
435 if (mWidgetListener)
436 mWidgetListener->WindowDeactivated();
438 #ifdef ACCESSIBILITY
439 DispatchDeactivateEventAccessible();
440 #endif //ACCESSIBILITY
441 }
443 void
444 nsWindow::DispatchResized(int32_t aWidth, int32_t aHeight)
445 {
446 nsIWidgetListener *listeners[] =
447 { mWidgetListener, mAttachedWidgetListener };
448 for (size_t i = 0; i < ArrayLength(listeners); ++i) {
449 if (listeners[i]) {
450 listeners[i]->WindowResized(this, aWidth, aHeight);
451 }
452 }
453 }
455 nsresult
456 nsWindow::DispatchEvent(WidgetGUIEvent* aEvent, nsEventStatus& aStatus)
457 {
458 #ifdef DEBUG
459 debug_DumpEvent(stdout, aEvent->widget, aEvent,
460 nsAutoCString("something"), 0);
461 #endif
463 aStatus = nsEventStatus_eIgnore;
464 nsIWidgetListener* listener =
465 mAttachedWidgetListener ? mAttachedWidgetListener : mWidgetListener;
466 if (listener) {
467 aStatus = listener->HandleEvent(aEvent, mUseAttachedEvents);
468 }
470 return NS_OK;
471 }
473 void
474 nsWindow::OnDestroy(void)
475 {
476 if (mOnDestroyCalled)
477 return;
479 mOnDestroyCalled = true;
481 // Prevent deletion.
482 nsCOMPtr<nsIWidget> kungFuDeathGrip = this;
484 // release references to children, device context, toolkit + app shell
485 nsBaseWidget::OnDestroy();
487 // Remove association between this object and its parent and siblings.
488 nsBaseWidget::Destroy();
489 mParent = nullptr;
491 NotifyWindowDestroyed();
492 }
494 bool
495 nsWindow::AreBoundsSane(void)
496 {
497 if (mBounds.width > 0 && mBounds.height > 0)
498 return true;
500 return false;
501 }
503 static GtkWidget*
504 EnsureInvisibleContainer()
505 {
506 if (!gInvisibleContainer) {
507 // GtkWidgets need to be anchored to a GtkWindow to be realized (to
508 // have a window). Using GTK_WINDOW_POPUP rather than
509 // GTK_WINDOW_TOPLEVEL in the hope that POPUP results in less
510 // initialization and window manager interaction.
511 GtkWidget* window = gtk_window_new(GTK_WINDOW_POPUP);
512 gInvisibleContainer = moz_container_new();
513 gtk_container_add(GTK_CONTAINER(window), gInvisibleContainer);
514 gtk_widget_realize(gInvisibleContainer);
516 }
517 return gInvisibleContainer;
518 }
520 static void
521 CheckDestroyInvisibleContainer()
522 {
523 NS_PRECONDITION(gInvisibleContainer, "oh, no");
525 if (!gdk_window_peek_children(gtk_widget_get_window(gInvisibleContainer))) {
526 // No children, so not in use.
527 // Make sure to destroy the GtkWindow also.
528 gtk_widget_destroy(gtk_widget_get_parent(gInvisibleContainer));
529 gInvisibleContainer = nullptr;
530 }
531 }
533 // Change the containing GtkWidget on a sub-hierarchy of GdkWindows belonging
534 // to aOldWidget and rooted at aWindow, and reparent any child GtkWidgets of
535 // the GdkWindow hierarchy to aNewWidget.
536 static void
537 SetWidgetForHierarchy(GdkWindow *aWindow,
538 GtkWidget *aOldWidget,
539 GtkWidget *aNewWidget)
540 {
541 gpointer data;
542 gdk_window_get_user_data(aWindow, &data);
544 if (data != aOldWidget) {
545 if (!GTK_IS_WIDGET(data))
546 return;
548 GtkWidget* widget = static_cast<GtkWidget*>(data);
549 if (gtk_widget_get_parent(widget) != aOldWidget)
550 return;
552 // This window belongs to a child widget, which will no longer be a
553 // child of aOldWidget.
554 gtk_widget_reparent(widget, aNewWidget);
556 return;
557 }
559 GList *children = gdk_window_get_children(aWindow);
560 for(GList *list = children; list; list = list->next) {
561 SetWidgetForHierarchy(GDK_WINDOW(list->data), aOldWidget, aNewWidget);
562 }
563 g_list_free(children);
565 gdk_window_set_user_data(aWindow, aNewWidget);
566 }
568 // Walk the list of child windows and call destroy on them.
569 void
570 nsWindow::DestroyChildWindows()
571 {
572 if (!mGdkWindow)
573 return;
575 while (GList *children = gdk_window_peek_children(mGdkWindow)) {
576 GdkWindow *child = GDK_WINDOW(children->data);
577 nsWindow *kid = get_window_for_gdk_window(child);
578 if (kid) {
579 kid->Destroy();
580 } else {
581 // This child is not an nsWindow.
582 // Destroy the child GtkWidget.
583 gpointer data;
584 gdk_window_get_user_data(child, &data);
585 if (GTK_IS_WIDGET(data)) {
586 gtk_widget_destroy(static_cast<GtkWidget*>(data));
587 }
588 }
589 }
590 }
592 NS_IMETHODIMP
593 nsWindow::Destroy(void)
594 {
595 if (mIsDestroyed || !mCreated)
596 return NS_OK;
598 LOG(("nsWindow::Destroy [%p]\n", (void *)this));
599 mIsDestroyed = true;
600 mCreated = false;
602 /** Need to clean our LayerManager up while still alive */
603 if (mLayerManager) {
604 mLayerManager->Destroy();
605 }
606 mLayerManager = nullptr;
608 // It is safe to call DestroyeCompositor several times (here and
609 // in the parent class) since it will take effect only once.
610 // The reason we call it here is because on gtk platforms we need
611 // to destroy the compositor before we destroy the gdk window (which
612 // destroys the the gl context attached to it).
613 DestroyCompositor();
615 ClearCachedResources();
617 g_signal_handlers_disconnect_by_func(gtk_settings_get_default(),
618 FuncToGpointer(theme_changed_cb),
619 this);
621 nsIRollupListener* rollupListener = nsBaseWidget::GetActiveRollupListener();
622 if (rollupListener) {
623 nsCOMPtr<nsIWidget> rollupWidget = rollupListener->GetRollupWidget();
624 if (static_cast<nsIWidget *>(this) == rollupWidget) {
625 rollupListener->Rollup(0, nullptr, nullptr);
626 }
627 }
629 // dragService will be null after shutdown of the service manager.
630 nsDragService *dragService = nsDragService::GetInstance();
631 if (dragService && this == dragService->GetMostRecentDestWindow()) {
632 dragService->ScheduleLeaveEvent();
633 }
635 NativeShow(false);
637 if (mIMModule) {
638 mIMModule->OnDestroyWindow(this);
639 }
641 // make sure that we remove ourself as the focus window
642 if (gFocusWindow == this) {
643 LOGFOCUS(("automatically losing focus...\n"));
644 gFocusWindow = nullptr;
645 }
647 #if (MOZ_WIDGET_GTK == 2) && defined(MOZ_X11)
648 // make sure that we remove ourself as the plugin focus window
649 if (gPluginFocusWindow == this) {
650 gPluginFocusWindow->LoseNonXEmbedPluginFocus();
651 }
652 #endif /* MOZ_X11 && MOZ_WIDGET_GTK2 */
654 // Destroy thebes surface now. Badness can happen if we destroy
655 // the surface after its X Window.
656 mThebesSurface = nullptr;
658 GtkWidget *owningWidget = GetMozContainerWidget();
659 if (mShell) {
660 gtk_widget_destroy(mShell);
661 mShell = nullptr;
662 mContainer = nullptr;
663 NS_ABORT_IF_FALSE(!mGdkWindow,
664 "mGdkWindow should be NULL when mContainer is destroyed");
665 }
666 else if (mContainer) {
667 gtk_widget_destroy(GTK_WIDGET(mContainer));
668 mContainer = nullptr;
669 NS_ABORT_IF_FALSE(!mGdkWindow,
670 "mGdkWindow should be NULL when mContainer is destroyed");
671 }
672 else if (mGdkWindow) {
673 // Destroy child windows to ensure that their mThebesSurfaces are
674 // released and to remove references from GdkWindows back to their
675 // container widget. (OnContainerUnrealize() does this when the
676 // MozContainer widget is destroyed.)
677 DestroyChildWindows();
679 gdk_window_set_user_data(mGdkWindow, nullptr);
680 g_object_set_data(G_OBJECT(mGdkWindow), "nsWindow", nullptr);
681 gdk_window_destroy(mGdkWindow);
682 mGdkWindow = nullptr;
683 }
685 if (gInvisibleContainer && owningWidget == gInvisibleContainer) {
686 CheckDestroyInvisibleContainer();
687 }
689 #ifdef ACCESSIBILITY
690 if (mRootAccessible) {
691 mRootAccessible = nullptr;
692 }
693 #endif
695 // Save until last because OnDestroy() may cause us to be deleted.
696 OnDestroy();
698 return NS_OK;
699 }
701 nsIWidget *
702 nsWindow::GetParent(void)
703 {
704 return mParent;
705 }
707 float
708 nsWindow::GetDPI()
709 {
710 Display *dpy = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
711 int defaultScreen = DefaultScreen(dpy);
712 double heightInches = DisplayHeightMM(dpy, defaultScreen)/MM_PER_INCH_FLOAT;
713 if (heightInches < 0.25) {
714 // Something's broken, but we'd better not crash.
715 return 96.0f;
716 }
717 return float(DisplayHeight(dpy, defaultScreen)/heightInches);
718 }
720 NS_IMETHODIMP
721 nsWindow::SetParent(nsIWidget *aNewParent)
722 {
723 if (mContainer || !mGdkWindow) {
724 NS_NOTREACHED("nsWindow::SetParent called illegally");
725 return NS_ERROR_NOT_IMPLEMENTED;
726 }
728 nsCOMPtr<nsIWidget> kungFuDeathGrip = this;
729 if (mParent) {
730 mParent->RemoveChild(this);
731 }
733 mParent = aNewParent;
735 GtkWidget* oldContainer = GetMozContainerWidget();
736 if (!oldContainer) {
737 // The GdkWindows have been destroyed so there is nothing else to
738 // reparent.
739 NS_ABORT_IF_FALSE(gdk_window_is_destroyed(mGdkWindow),
740 "live GdkWindow with no widget");
741 return NS_OK;
742 }
744 if (aNewParent) {
745 aNewParent->AddChild(this);
746 ReparentNativeWidget(aNewParent);
747 } else {
748 // aNewParent is nullptr, but reparent to a hidden window to avoid
749 // destroying the GdkWindow and its descendants.
750 // An invisible container widget is needed to hold descendant
751 // GtkWidgets.
752 GtkWidget* newContainer = EnsureInvisibleContainer();
753 GdkWindow* newParentWindow = gtk_widget_get_window(newContainer);
754 ReparentNativeWidgetInternal(aNewParent, newContainer, newParentWindow,
755 oldContainer);
756 }
757 return NS_OK;
758 }
760 NS_IMETHODIMP
761 nsWindow::ReparentNativeWidget(nsIWidget* aNewParent)
762 {
763 NS_PRECONDITION(aNewParent, "");
764 NS_ASSERTION(!mIsDestroyed, "");
765 NS_ASSERTION(!static_cast<nsWindow*>(aNewParent)->mIsDestroyed, "");
767 GtkWidget* oldContainer = GetMozContainerWidget();
768 if (!oldContainer) {
769 // The GdkWindows have been destroyed so there is nothing else to
770 // reparent.
771 NS_ABORT_IF_FALSE(gdk_window_is_destroyed(mGdkWindow),
772 "live GdkWindow with no widget");
773 return NS_OK;
774 }
775 NS_ABORT_IF_FALSE(!gdk_window_is_destroyed(mGdkWindow),
776 "destroyed GdkWindow with widget");
778 nsWindow* newParent = static_cast<nsWindow*>(aNewParent);
779 GdkWindow* newParentWindow = newParent->mGdkWindow;
780 GtkWidget* newContainer = newParent->GetMozContainerWidget();
781 GtkWindow* shell = GTK_WINDOW(mShell);
783 if (shell && gtk_window_get_transient_for(shell)) {
784 GtkWindow* topLevelParent =
785 GTK_WINDOW(gtk_widget_get_toplevel(newContainer));
786 gtk_window_set_transient_for(shell, topLevelParent);
787 }
789 ReparentNativeWidgetInternal(aNewParent, newContainer, newParentWindow,
790 oldContainer);
791 return NS_OK;
792 }
794 void
795 nsWindow::ReparentNativeWidgetInternal(nsIWidget* aNewParent,
796 GtkWidget* aNewContainer,
797 GdkWindow* aNewParentWindow,
798 GtkWidget* aOldContainer)
799 {
800 if (!aNewContainer) {
801 // The new parent GdkWindow has been destroyed.
802 NS_ABORT_IF_FALSE(!aNewParentWindow ||
803 gdk_window_is_destroyed(aNewParentWindow),
804 "live GdkWindow with no widget");
805 Destroy();
806 } else {
807 if (aNewContainer != aOldContainer) {
808 NS_ABORT_IF_FALSE(!gdk_window_is_destroyed(aNewParentWindow),
809 "destroyed GdkWindow with widget");
810 SetWidgetForHierarchy(mGdkWindow, aOldContainer, aNewContainer);
812 if (aOldContainer == gInvisibleContainer) {
813 CheckDestroyInvisibleContainer();
814 }
815 }
817 if (!mIsTopLevel) {
818 gdk_window_reparent(mGdkWindow, aNewParentWindow, mBounds.x,
819 mBounds.y);
820 }
821 }
823 nsWindow* newParent = static_cast<nsWindow*>(aNewParent);
824 bool parentHasMappedToplevel =
825 newParent && newParent->mHasMappedToplevel;
826 if (mHasMappedToplevel != parentHasMappedToplevel) {
827 SetHasMappedToplevel(parentHasMappedToplevel);
828 }
829 }
831 NS_IMETHODIMP
832 nsWindow::SetModal(bool aModal)
833 {
834 LOG(("nsWindow::SetModal [%p] %d\n", (void *)this, aModal));
835 if (mIsDestroyed)
836 return aModal ? NS_ERROR_NOT_AVAILABLE : NS_OK;
837 if (!mIsTopLevel || !mShell)
838 return NS_ERROR_FAILURE;
839 gtk_window_set_modal(GTK_WINDOW(mShell), aModal ? TRUE : FALSE);
840 return NS_OK;
841 }
843 // nsIWidget method, which means IsShown.
844 bool
845 nsWindow::IsVisible() const
846 {
847 return mIsShown;
848 }
850 NS_IMETHODIMP
851 nsWindow::ConstrainPosition(bool aAllowSlop, int32_t *aX, int32_t *aY)
852 {
853 if (mIsTopLevel && mShell) {
854 int32_t screenWidth = gdk_screen_width();
855 int32_t screenHeight = gdk_screen_height();
856 if (aAllowSlop) {
857 if (*aX < (kWindowPositionSlop - mBounds.width))
858 *aX = kWindowPositionSlop - mBounds.width;
859 if (*aX > (screenWidth - kWindowPositionSlop))
860 *aX = screenWidth - kWindowPositionSlop;
861 if (*aY < (kWindowPositionSlop - mBounds.height))
862 *aY = kWindowPositionSlop - mBounds.height;
863 if (*aY > (screenHeight - kWindowPositionSlop))
864 *aY = screenHeight - kWindowPositionSlop;
865 } else {
866 if (*aX < 0)
867 *aX = 0;
868 if (*aX > (screenWidth - mBounds.width))
869 *aX = screenWidth - mBounds.width;
870 if (*aY < 0)
871 *aY = 0;
872 if (*aY > (screenHeight - mBounds.height))
873 *aY = screenHeight - mBounds.height;
874 }
875 }
876 return NS_OK;
877 }
879 void nsWindow::SetSizeConstraints(const SizeConstraints& aConstraints)
880 {
881 mSizeConstraints.mMinSize = GetSafeWindowSize(aConstraints.mMinSize);
882 mSizeConstraints.mMaxSize = GetSafeWindowSize(aConstraints.mMaxSize);
884 if (mShell) {
885 GdkGeometry geometry;
886 geometry.min_width = mSizeConstraints.mMinSize.width;
887 geometry.min_height = mSizeConstraints.mMinSize.height;
888 geometry.max_width = mSizeConstraints.mMaxSize.width;
889 geometry.max_height = mSizeConstraints.mMaxSize.height;
891 uint32_t hints = GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE;
892 gtk_window_set_geometry_hints(GTK_WINDOW(mShell), nullptr,
893 &geometry, GdkWindowHints(hints));
894 }
895 }
897 NS_IMETHODIMP
898 nsWindow::Show(bool aState)
899 {
900 if (aState == mIsShown)
901 return NS_OK;
903 // Clear our cached resources when the window is hidden.
904 if (mIsShown && !aState) {
905 ClearCachedResources();
906 }
908 mIsShown = aState;
910 LOG(("nsWindow::Show [%p] state %d\n", (void *)this, aState));
912 if (aState) {
913 // Now that this window is shown, mHasMappedToplevel needs to be
914 // tracked on viewable descendants.
915 SetHasMappedToplevel(mHasMappedToplevel);
916 }
918 // Ok, someone called show on a window that isn't sized to a sane
919 // value. Mark this window as needing to have Show() called on it
920 // and return.
921 if ((aState && !AreBoundsSane()) || !mCreated) {
922 LOG(("\tbounds are insane or window hasn't been created yet\n"));
923 mNeedsShow = true;
924 return NS_OK;
925 }
927 // If someone is hiding this widget, clear any needing show flag.
928 if (!aState)
929 mNeedsShow = false;
931 // If someone is showing this window and it needs a resize then
932 // resize the widget.
933 if (aState) {
934 if (mNeedsMove) {
935 NativeResize(mBounds.x, mBounds.y, mBounds.width, mBounds.height,
936 false);
937 } else if (mNeedsResize) {
938 NativeResize(mBounds.width, mBounds.height, false);
939 }
940 }
942 #ifdef ACCESSIBILITY
943 if (aState && a11y::ShouldA11yBeEnabled())
944 CreateRootAccessible();
945 #endif
947 NativeShow(aState);
949 return NS_OK;
950 }
952 NS_IMETHODIMP
953 nsWindow::Resize(double aWidth, double aHeight, bool aRepaint)
954 {
955 CSSToLayoutDeviceScale scale = BoundsUseDisplayPixels() ? GetDefaultScale()
956 : CSSToLayoutDeviceScale(1.0);
957 int32_t width = NSToIntRound(scale.scale * aWidth);
958 int32_t height = NSToIntRound(scale.scale * aHeight);
959 ConstrainSize(&width, &height);
961 // For top-level windows, aWidth and aHeight should possibly be
962 // interpreted as frame bounds, but NativeResize treats these as window
963 // bounds (Bug 581866).
965 mBounds.SizeTo(width, height);
967 if (!mCreated)
968 return NS_OK;
970 // There are several cases here that we need to handle, based on a
971 // matrix of the visibility of the widget, the sanity of this resize
972 // and whether or not the widget was previously sane.
974 // Has this widget been set to visible?
975 if (mIsShown) {
976 // Are the bounds sane?
977 if (AreBoundsSane()) {
978 // Yep? Resize the window
979 //Maybe, the toplevel has moved
981 // Note that if the widget needs to be positioned because its
982 // size was previously insane in Resize(x,y,w,h), then we need
983 // to set the x and y here too, because the widget wasn't
984 // moved back then
985 if (mNeedsMove)
986 NativeResize(mBounds.x, mBounds.y,
987 mBounds.width, mBounds.height, aRepaint);
988 else
989 NativeResize(mBounds.width, mBounds.height, aRepaint);
991 // Does it need to be shown because it was previously insane?
992 if (mNeedsShow)
993 NativeShow(true);
994 }
995 else {
996 // If someone has set this so that the needs show flag is false
997 // and it needs to be hidden, update the flag and hide the
998 // window. This flag will be cleared the next time someone
999 // hides the window or shows it. It also prevents us from
1000 // calling NativeShow(false) excessively on the window which
1001 // causes unneeded X traffic.
1002 if (!mNeedsShow) {
1003 mNeedsShow = true;
1004 NativeShow(false);
1005 }
1006 }
1007 }
1008 // If the widget hasn't been shown, mark the widget as needing to be
1009 // resized before it is shown.
1010 else {
1011 if (AreBoundsSane() && mListenForResizes) {
1012 // For widgets that we listen for resizes for (widgets created
1013 // with native parents) we apparently _always_ have to resize. I
1014 // dunno why, but apparently we're lame like that.
1015 NativeResize(width, height, aRepaint);
1016 }
1017 else {
1018 mNeedsResize = true;
1019 }
1020 }
1022 NotifyRollupGeometryChange();
1024 // send a resize notification if this is a toplevel
1025 if (mIsTopLevel || mListenForResizes) {
1026 DispatchResized(width, height);
1027 }
1029 return NS_OK;
1030 }
1032 NS_IMETHODIMP
1033 nsWindow::Resize(double aX, double aY, double aWidth, double aHeight,
1034 bool aRepaint)
1035 {
1036 CSSToLayoutDeviceScale scale = BoundsUseDisplayPixels() ? GetDefaultScale()
1037 : CSSToLayoutDeviceScale(1.0);
1038 int32_t width = NSToIntRound(scale.scale * aWidth);
1039 int32_t height = NSToIntRound(scale.scale * aHeight);
1040 ConstrainSize(&width, &height);
1042 int32_t x = NSToIntRound(scale.scale * aX);
1043 int32_t y = NSToIntRound(scale.scale * aY);
1044 mBounds.x = x;
1045 mBounds.y = y;
1046 mBounds.SizeTo(width, height);
1048 mNeedsMove = true;
1050 if (!mCreated)
1051 return NS_OK;
1053 // There are several cases here that we need to handle, based on a
1054 // matrix of the visibility of the widget, the sanity of this resize
1055 // and whether or not the widget was previously sane.
1057 // Has this widget been set to visible?
1058 if (mIsShown) {
1059 // Are the bounds sane?
1060 if (AreBoundsSane()) {
1061 // Yep? Resize the window
1062 NativeResize(x, y, width, height, aRepaint);
1063 // Does it need to be shown because it was previously insane?
1064 if (mNeedsShow)
1065 NativeShow(true);
1066 }
1067 else {
1068 // If someone has set this so that the needs show flag is false
1069 // and it needs to be hidden, update the flag and hide the
1070 // window. This flag will be cleared the next time someone
1071 // hides the window or shows it. It also prevents us from
1072 // calling NativeShow(false) excessively on the window which
1073 // causes unneeded X traffic.
1074 if (!mNeedsShow) {
1075 mNeedsShow = true;
1076 NativeShow(false);
1077 }
1078 }
1079 }
1080 // If the widget hasn't been shown, mark the widget as needing to be
1081 // resized before it is shown
1082 else {
1083 if (AreBoundsSane() && mListenForResizes){
1084 // For widgets that we listen for resizes for (widgets created
1085 // with native parents) we apparently _always_ have to resize. I
1086 // dunno why, but apparently we're lame like that.
1087 NativeResize(x, y, width, height, aRepaint);
1088 }
1089 else {
1090 mNeedsResize = true;
1091 }
1092 }
1094 NotifyRollupGeometryChange();
1096 if (mIsTopLevel || mListenForResizes) {
1097 DispatchResized(width, height);
1098 }
1100 return NS_OK;
1101 }
1103 NS_IMETHODIMP
1104 nsWindow::Enable(bool aState)
1105 {
1106 mEnabled = aState;
1108 return NS_OK;
1109 }
1111 bool
1112 nsWindow::IsEnabled() const
1113 {
1114 return mEnabled;
1115 }
1119 NS_IMETHODIMP
1120 nsWindow::Move(double aX, double aY)
1121 {
1122 LOG(("nsWindow::Move [%p] %f %f\n", (void *)this,
1123 aX, aY));
1125 CSSToLayoutDeviceScale scale = BoundsUseDisplayPixels() ? GetDefaultScale()
1126 : CSSToLayoutDeviceScale(1.0);
1127 int32_t x = NSToIntRound(aX * scale.scale);
1128 int32_t y = NSToIntRound(aY * scale.scale);
1130 if (mWindowType == eWindowType_toplevel ||
1131 mWindowType == eWindowType_dialog) {
1132 SetSizeMode(nsSizeMode_Normal);
1133 }
1135 // Since a popup window's x/y coordinates are in relation to to
1136 // the parent, the parent might have moved so we always move a
1137 // popup window.
1138 if (x == mBounds.x && y == mBounds.y &&
1139 mWindowType != eWindowType_popup)
1140 return NS_OK;
1142 // XXX Should we do some AreBoundsSane check here?
1144 mBounds.x = x;
1145 mBounds.y = y;
1147 if (!mCreated)
1148 return NS_OK;
1150 mNeedsMove = false;
1152 if (mIsTopLevel) {
1153 gtk_window_move(GTK_WINDOW(mShell), x, y);
1154 }
1155 else if (mGdkWindow) {
1156 gdk_window_move(mGdkWindow, x, y);
1157 }
1159 NotifyRollupGeometryChange();
1160 return NS_OK;
1161 }
1163 NS_IMETHODIMP
1164 nsWindow::PlaceBehind(nsTopLevelWidgetZPlacement aPlacement,
1165 nsIWidget *aWidget,
1166 bool aActivate)
1167 {
1168 return NS_ERROR_NOT_IMPLEMENTED;
1169 }
1171 void
1172 nsWindow::SetZIndex(int32_t aZIndex)
1173 {
1174 nsIWidget* oldPrev = GetPrevSibling();
1176 nsBaseWidget::SetZIndex(aZIndex);
1178 if (GetPrevSibling() == oldPrev) {
1179 return;
1180 }
1182 NS_ASSERTION(!mContainer, "Expected Mozilla child widget");
1184 // We skip the nsWindows that don't have mGdkWindows.
1185 // These are probably in the process of being destroyed.
1187 if (!GetNextSibling()) {
1188 // We're to be on top.
1189 if (mGdkWindow)
1190 gdk_window_raise(mGdkWindow);
1191 } else {
1192 // All the siblings before us need to be below our widget.
1193 for (nsWindow* w = this; w;
1194 w = static_cast<nsWindow*>(w->GetPrevSibling())) {
1195 if (w->mGdkWindow)
1196 gdk_window_lower(w->mGdkWindow);
1197 }
1198 }
1199 }
1201 NS_IMETHODIMP
1202 nsWindow::SetSizeMode(int32_t aMode)
1203 {
1204 nsresult rv;
1206 LOG(("nsWindow::SetSizeMode [%p] %d\n", (void *)this, aMode));
1208 // Save the requested state.
1209 rv = nsBaseWidget::SetSizeMode(aMode);
1211 // return if there's no shell or our current state is the same as
1212 // the mode we were just set to.
1213 if (!mShell || mSizeState == mSizeMode) {
1214 return rv;
1215 }
1217 switch (aMode) {
1218 case nsSizeMode_Maximized:
1219 gtk_window_maximize(GTK_WINDOW(mShell));
1220 break;
1221 case nsSizeMode_Minimized:
1222 gtk_window_iconify(GTK_WINDOW(mShell));
1223 break;
1224 case nsSizeMode_Fullscreen:
1225 MakeFullScreen(true);
1226 break;
1228 default:
1229 // nsSizeMode_Normal, really.
1230 if (mSizeState == nsSizeMode_Minimized)
1231 gtk_window_deiconify(GTK_WINDOW(mShell));
1232 else if (mSizeState == nsSizeMode_Maximized)
1233 gtk_window_unmaximize(GTK_WINDOW(mShell));
1234 break;
1235 }
1237 mSizeState = mSizeMode;
1239 return rv;
1240 }
1242 typedef void (* SetUserTimeFunc)(GdkWindow* aWindow, guint32 aTimestamp);
1244 // This will become obsolete when new GTK APIs are widely supported,
1245 // as described here: http://bugzilla.gnome.org/show_bug.cgi?id=347375
1246 static void
1247 SetUserTimeAndStartupIDForActivatedWindow(GtkWidget* aWindow)
1248 {
1249 nsGTKToolkit* GTKToolkit = nsGTKToolkit::GetToolkit();
1250 if (!GTKToolkit)
1251 return;
1253 nsAutoCString desktopStartupID;
1254 GTKToolkit->GetDesktopStartupID(&desktopStartupID);
1255 if (desktopStartupID.IsEmpty()) {
1256 // We don't have the data we need. Fall back to an
1257 // approximation ... using the timestamp of the remote command
1258 // being received as a guess for the timestamp of the user event
1259 // that triggered it.
1260 uint32_t timestamp = GTKToolkit->GetFocusTimestamp();
1261 if (timestamp) {
1262 gdk_window_focus(gtk_widget_get_window(aWindow), timestamp);
1263 GTKToolkit->SetFocusTimestamp(0);
1264 }
1265 return;
1266 }
1268 #if defined(MOZ_ENABLE_STARTUP_NOTIFICATION)
1269 GdkWindow* gdkWindow = gtk_widget_get_window(aWindow);
1271 GdkScreen* screen = gdk_window_get_screen(gdkWindow);
1272 SnDisplay* snd =
1273 sn_display_new(gdk_x11_display_get_xdisplay(gdk_window_get_display(gdkWindow)),
1274 nullptr, nullptr);
1275 if (!snd)
1276 return;
1277 SnLauncheeContext* ctx =
1278 sn_launchee_context_new(snd, gdk_screen_get_number(screen),
1279 desktopStartupID.get());
1280 if (!ctx) {
1281 sn_display_unref(snd);
1282 return;
1283 }
1285 if (sn_launchee_context_get_id_has_timestamp(ctx)) {
1286 PRLibrary* gtkLibrary;
1287 SetUserTimeFunc setUserTimeFunc = (SetUserTimeFunc)
1288 PR_FindFunctionSymbolAndLibrary("gdk_x11_window_set_user_time", >kLibrary);
1289 if (setUserTimeFunc) {
1290 setUserTimeFunc(gdkWindow, sn_launchee_context_get_timestamp(ctx));
1291 PR_UnloadLibrary(gtkLibrary);
1292 }
1293 }
1295 sn_launchee_context_setup_window(ctx, gdk_x11_window_get_xid(gdkWindow));
1296 sn_launchee_context_complete(ctx);
1298 sn_launchee_context_unref(ctx);
1299 sn_display_unref(snd);
1300 #endif
1302 // If we used the startup ID, that already contains the focus timestamp;
1303 // we don't want to reuse the timestamp next time we raise the window
1304 GTKToolkit->SetFocusTimestamp(0);
1305 GTKToolkit->SetDesktopStartupID(EmptyCString());
1306 }
1308 /* static */ guint32
1309 nsWindow::GetLastUserInputTime()
1310 {
1311 // gdk_x11_display_get_user_time tracks button and key presses,
1312 // DESKTOP_STARTUP_ID used to start the app, drop events from external
1313 // drags, WM_DELETE_WINDOW delete events, but not usually mouse motion nor
1314 // button and key releases. Therefore use the most recent of
1315 // gdk_x11_display_get_user_time and the last time that we have seen.
1316 guint32 timestamp =
1317 gdk_x11_display_get_user_time(gdk_display_get_default());
1318 if (sLastUserInputTime != GDK_CURRENT_TIME &&
1319 TimestampIsNewerThan(sLastUserInputTime, timestamp)) {
1320 return sLastUserInputTime;
1321 }
1323 return timestamp;
1324 }
1326 NS_IMETHODIMP
1327 nsWindow::SetFocus(bool aRaise)
1328 {
1329 // Make sure that our owning widget has focus. If it doesn't try to
1330 // grab it. Note that we don't set our focus flag in this case.
1332 LOGFOCUS((" SetFocus %d [%p]\n", aRaise, (void *)this));
1334 GtkWidget *owningWidget = GetMozContainerWidget();
1335 if (!owningWidget)
1336 return NS_ERROR_FAILURE;
1338 // Raise the window if someone passed in true and the prefs are
1339 // set properly.
1340 GtkWidget *toplevelWidget = gtk_widget_get_toplevel(owningWidget);
1342 if (gRaiseWindows && aRaise && toplevelWidget &&
1343 !gtk_widget_has_focus(owningWidget) &&
1344 !gtk_widget_has_focus(toplevelWidget)) {
1345 GtkWidget* top_window = GetToplevelWidget();
1346 if (top_window && (gtk_widget_get_visible(top_window)))
1347 {
1348 gdk_window_show_unraised(gtk_widget_get_window(top_window));
1349 // Unset the urgency hint if possible.
1350 SetUrgencyHint(top_window, false);
1351 }
1352 }
1354 nsRefPtr<nsWindow> owningWindow = get_window_for_gtk_widget(owningWidget);
1355 if (!owningWindow)
1356 return NS_ERROR_FAILURE;
1358 if (aRaise) {
1359 // aRaise == true means request toplevel activation.
1361 // This is asynchronous.
1362 // If and when the window manager accepts the request, then the focus
1363 // widget will get a focus-in-event signal.
1364 if (gRaiseWindows && owningWindow->mIsShown && owningWindow->mShell &&
1365 !gtk_window_is_active(GTK_WINDOW(owningWindow->mShell))) {
1367 uint32_t timestamp = GDK_CURRENT_TIME;
1369 nsGTKToolkit* GTKToolkit = nsGTKToolkit::GetToolkit();
1370 if (GTKToolkit)
1371 timestamp = GTKToolkit->GetFocusTimestamp();
1373 LOGFOCUS((" requesting toplevel activation [%p]\n", (void *)this));
1374 NS_ASSERTION(owningWindow->mWindowType != eWindowType_popup
1375 || mParent,
1376 "Presenting an override-redirect window");
1377 gtk_window_present_with_time(GTK_WINDOW(owningWindow->mShell), timestamp);
1379 if (GTKToolkit)
1380 GTKToolkit->SetFocusTimestamp(0);
1381 }
1383 return NS_OK;
1384 }
1386 // aRaise == false means that keyboard events should be dispatched
1387 // from this widget.
1389 // Ensure owningWidget is the focused GtkWidget within its toplevel window.
1390 //
1391 // For eWindowType_popup, this GtkWidget may not actually be the one that
1392 // receives the key events as it may be the parent window that is active.
1393 if (!gtk_widget_is_focus(owningWidget)) {
1394 // This is synchronous. It takes focus from a plugin or from a widget
1395 // in an embedder. The focus manager already knows that this window
1396 // is active so gBlockActivateEvent avoids another (unnecessary)
1397 // activate notification.
1398 gBlockActivateEvent = true;
1399 gtk_widget_grab_focus(owningWidget);
1400 gBlockActivateEvent = false;
1401 }
1403 // If this is the widget that already has focus, return.
1404 if (gFocusWindow == this) {
1405 LOGFOCUS((" already have focus [%p]\n", (void *)this));
1406 return NS_OK;
1407 }
1409 // Set this window to be the focused child window
1410 gFocusWindow = this;
1412 if (mIMModule) {
1413 mIMModule->OnFocusWindow(this);
1414 }
1416 LOGFOCUS((" widget now has focus in SetFocus() [%p]\n",
1417 (void *)this));
1419 return NS_OK;
1420 }
1422 NS_IMETHODIMP
1423 nsWindow::GetScreenBounds(nsIntRect &aRect)
1424 {
1425 if (mIsTopLevel && mContainer) {
1426 // use the point including window decorations
1427 gint x, y;
1428 gdk_window_get_root_origin(gtk_widget_get_window(GTK_WIDGET(mContainer)), &x, &y);
1429 aRect.MoveTo(x, y);
1430 }
1431 else {
1432 aRect.MoveTo(WidgetToScreenOffset());
1433 }
1434 // mBounds.Size() is the window bounds, not the window-manager frame
1435 // bounds (bug 581863). gdk_window_get_frame_extents would give the
1436 // frame bounds, but mBounds.Size() is returned here for consistency
1437 // with Resize.
1438 aRect.SizeTo(mBounds.Size());
1439 LOG(("GetScreenBounds %d,%d | %dx%d\n",
1440 aRect.x, aRect.y, aRect.width, aRect.height));
1441 return NS_OK;
1442 }
1444 NS_IMETHODIMP
1445 nsWindow::GetClientBounds(nsIntRect &aRect)
1446 {
1447 // GetBounds returns a rect whose top left represents the top left of the
1448 // outer bounds, but whose width/height represent the size of the inner
1449 // bounds (which is messed up).
1450 GetBounds(aRect);
1451 aRect.MoveBy(GetClientOffset());
1453 return NS_OK;
1454 }
1456 nsIntPoint
1457 nsWindow::GetClientOffset()
1458 {
1459 if (!mIsTopLevel) {
1460 return nsIntPoint(0, 0);
1461 }
1463 GdkAtom cardinal_atom = gdk_x11_xatom_to_atom(XA_CARDINAL);
1465 GdkAtom type_returned;
1466 int format_returned;
1467 int length_returned;
1468 long *frame_extents;
1469 GdkWindow* window;
1471 if (!mShell || !(window = gtk_widget_get_window(mShell)) ||
1472 !gdk_property_get(window,
1473 gdk_atom_intern ("_NET_FRAME_EXTENTS", FALSE),
1474 cardinal_atom,
1475 0, // offset
1476 4*4, // length
1477 FALSE, // delete
1478 &type_returned,
1479 &format_returned,
1480 &length_returned,
1481 (guchar **) &frame_extents) ||
1482 length_returned/sizeof(glong) != 4) {
1484 return nsIntPoint(0, 0);
1485 }
1487 // data returned is in the order left, right, top, bottom
1488 int32_t left = int32_t(frame_extents[0]);
1489 int32_t top = int32_t(frame_extents[2]);
1491 g_free(frame_extents);
1493 return nsIntPoint(left, top);
1494 }
1496 NS_IMETHODIMP
1497 nsWindow::SetCursor(nsCursor aCursor)
1498 {
1499 // if we're not the toplevel window pass up the cursor request to
1500 // the toplevel window to handle it.
1501 if (!mContainer && mGdkWindow) {
1502 nsWindow *window = GetContainerWindow();
1503 if (!window)
1504 return NS_ERROR_FAILURE;
1506 return window->SetCursor(aCursor);
1507 }
1509 // Only change cursor if it's actually been changed
1510 if (aCursor != mCursor) {
1511 GdkCursor *newCursor = nullptr;
1513 newCursor = get_gtk_cursor(aCursor);
1515 if (nullptr != newCursor) {
1516 mCursor = aCursor;
1518 if (!mContainer)
1519 return NS_OK;
1521 gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(mContainer)), newCursor);
1522 }
1523 }
1525 return NS_OK;
1526 }
1528 NS_IMETHODIMP
1529 nsWindow::SetCursor(imgIContainer* aCursor,
1530 uint32_t aHotspotX, uint32_t aHotspotY)
1531 {
1532 // if we're not the toplevel window pass up the cursor request to
1533 // the toplevel window to handle it.
1534 if (!mContainer && mGdkWindow) {
1535 nsWindow *window = GetContainerWindow();
1536 if (!window)
1537 return NS_ERROR_FAILURE;
1539 return window->SetCursor(aCursor, aHotspotX, aHotspotY);
1540 }
1542 mCursor = nsCursor(-1);
1544 // Get the image's current frame
1545 GdkPixbuf* pixbuf = nsImageToPixbuf::ImageToPixbuf(aCursor);
1546 if (!pixbuf)
1547 return NS_ERROR_NOT_AVAILABLE;
1549 int width = gdk_pixbuf_get_width(pixbuf);
1550 int height = gdk_pixbuf_get_height(pixbuf);
1551 // Reject cursors greater than 128 pixels in some direction, to prevent
1552 // spoofing.
1553 // XXX ideally we should rescale. Also, we could modify the API to
1554 // allow trusted content to set larger cursors.
1555 if (width > 128 || height > 128) {
1556 g_object_unref(pixbuf);
1557 return NS_ERROR_NOT_AVAILABLE;
1558 }
1560 // Looks like all cursors need an alpha channel (tested on Gtk 2.4.4). This
1561 // is of course not documented anywhere...
1562 // So add one if there isn't one yet
1563 if (!gdk_pixbuf_get_has_alpha(pixbuf)) {
1564 GdkPixbuf* alphaBuf = gdk_pixbuf_add_alpha(pixbuf, FALSE, 0, 0, 0);
1565 g_object_unref(pixbuf);
1566 if (!alphaBuf) {
1567 return NS_ERROR_OUT_OF_MEMORY;
1568 }
1569 pixbuf = alphaBuf;
1570 }
1572 GdkCursor* cursor = gdk_cursor_new_from_pixbuf(gdk_display_get_default(),
1573 pixbuf,
1574 aHotspotX, aHotspotY);
1575 g_object_unref(pixbuf);
1576 nsresult rv = NS_ERROR_OUT_OF_MEMORY;
1577 if (cursor) {
1578 if (mContainer) {
1579 gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(mContainer)), cursor);
1580 rv = NS_OK;
1581 }
1582 gdk_cursor_unref(cursor);
1583 }
1585 return rv;
1586 }
1588 NS_IMETHODIMP
1589 nsWindow::Invalidate(const nsIntRect &aRect)
1590 {
1591 if (!mGdkWindow)
1592 return NS_OK;
1594 GdkRectangle rect;
1595 rect.x = aRect.x;
1596 rect.y = aRect.y;
1597 rect.width = aRect.width;
1598 rect.height = aRect.height;
1600 LOGDRAW(("Invalidate (rect) [%p]: %d %d %d %d\n", (void *)this,
1601 rect.x, rect.y, rect.width, rect.height));
1603 gdk_window_invalidate_rect(mGdkWindow, &rect, FALSE);
1605 return NS_OK;
1606 }
1608 void*
1609 nsWindow::GetNativeData(uint32_t aDataType)
1610 {
1611 switch (aDataType) {
1612 case NS_NATIVE_WINDOW:
1613 case NS_NATIVE_WIDGET: {
1614 if (!mGdkWindow)
1615 return nullptr;
1617 return mGdkWindow;
1618 break;
1619 }
1621 case NS_NATIVE_PLUGIN_PORT:
1622 return SetupPluginPort();
1623 break;
1625 case NS_NATIVE_DISPLAY:
1626 #ifdef MOZ_X11
1627 return GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
1628 #else
1629 return nullptr;
1630 #endif /* MOZ_X11 */
1631 break;
1633 case NS_NATIVE_SHELLWIDGET:
1634 return GetToplevelWidget();
1636 case NS_NATIVE_SHAREABLE_WINDOW:
1637 return (void *) GDK_WINDOW_XID(gdk_window_get_toplevel(mGdkWindow));
1639 default:
1640 NS_WARNING("nsWindow::GetNativeData called with bad value");
1641 return nullptr;
1642 }
1643 }
1645 NS_IMETHODIMP
1646 nsWindow::SetTitle(const nsAString& aTitle)
1647 {
1648 if (!mShell)
1649 return NS_OK;
1651 // convert the string into utf8 and set the title.
1652 #define UTF8_FOLLOWBYTE(ch) (((ch) & 0xC0) == 0x80)
1653 NS_ConvertUTF16toUTF8 titleUTF8(aTitle);
1654 if (titleUTF8.Length() > NS_WINDOW_TITLE_MAX_LENGTH) {
1655 // Truncate overlong titles (bug 167315). Make sure we chop after a
1656 // complete sequence by making sure the next char isn't a follow-byte.
1657 uint32_t len = NS_WINDOW_TITLE_MAX_LENGTH;
1658 while(UTF8_FOLLOWBYTE(titleUTF8[len]))
1659 --len;
1660 titleUTF8.Truncate(len);
1661 }
1662 gtk_window_set_title(GTK_WINDOW(mShell), (const char *)titleUTF8.get());
1664 return NS_OK;
1665 }
1667 NS_IMETHODIMP
1668 nsWindow::SetIcon(const nsAString& aIconSpec)
1669 {
1670 if (!mShell)
1671 return NS_OK;
1673 nsAutoCString iconName;
1675 if (aIconSpec.EqualsLiteral("default")) {
1676 nsXPIDLString brandName;
1677 GetBrandName(brandName);
1678 AppendUTF16toUTF8(brandName, iconName);
1679 ToLowerCase(iconName);
1680 } else {
1681 AppendUTF16toUTF8(aIconSpec, iconName);
1682 }
1684 nsCOMPtr<nsIFile> iconFile;
1685 nsAutoCString path;
1687 gint *iconSizes =
1688 gtk_icon_theme_get_icon_sizes(gtk_icon_theme_get_default(),
1689 iconName.get());
1690 bool foundIcon = (iconSizes[0] != 0);
1691 g_free(iconSizes);
1693 if (!foundIcon) {
1694 // Look for icons with the following suffixes appended to the base name
1695 // The last two entries (for the old XPM format) will be ignored unless
1696 // no icons are found using other suffixes. XPM icons are deprecated.
1698 const char extensions[6][7] = { ".png", "16.png", "32.png", "48.png",
1699 ".xpm", "16.xpm" };
1701 for (uint32_t i = 0; i < ArrayLength(extensions); i++) {
1702 // Don't bother looking for XPM versions if we found a PNG.
1703 if (i == ArrayLength(extensions) - 2 && foundIcon)
1704 break;
1706 nsAutoString extension;
1707 extension.AppendASCII(extensions[i]);
1709 ResolveIconName(aIconSpec, extension, getter_AddRefs(iconFile));
1710 if (iconFile) {
1711 iconFile->GetNativePath(path);
1712 GdkPixbuf *icon = gdk_pixbuf_new_from_file(path.get(), nullptr);
1713 if (icon) {
1714 gtk_icon_theme_add_builtin_icon(iconName.get(),
1715 gdk_pixbuf_get_height(icon),
1716 icon);
1717 g_object_unref(icon);
1718 foundIcon = true;
1719 }
1720 }
1721 }
1722 }
1724 // leave the default icon intact if no matching icons were found
1725 if (foundIcon) {
1726 gtk_window_set_icon_name(GTK_WINDOW(mShell), iconName.get());
1727 }
1729 return NS_OK;
1730 }
1733 nsIntPoint
1734 nsWindow::WidgetToScreenOffset()
1735 {
1736 gint x = 0, y = 0;
1738 if (mGdkWindow) {
1739 gdk_window_get_origin(mGdkWindow, &x, &y);
1740 }
1742 return nsIntPoint(x, y);
1743 }
1745 NS_IMETHODIMP
1746 nsWindow::EnableDragDrop(bool aEnable)
1747 {
1748 return NS_OK;
1749 }
1751 NS_IMETHODIMP
1752 nsWindow::CaptureMouse(bool aCapture)
1753 {
1754 LOG(("CaptureMouse %p\n", (void *)this));
1756 if (!mGdkWindow)
1757 return NS_OK;
1759 if (!mShell)
1760 return NS_ERROR_FAILURE;
1762 if (aCapture) {
1763 gtk_grab_add(mShell);
1764 GrabPointer(GetLastUserInputTime());
1765 }
1766 else {
1767 ReleaseGrabs();
1768 gtk_grab_remove(mShell);
1769 }
1771 return NS_OK;
1772 }
1774 NS_IMETHODIMP
1775 nsWindow::CaptureRollupEvents(nsIRollupListener *aListener,
1776 bool aDoCapture)
1777 {
1778 if (!mGdkWindow)
1779 return NS_OK;
1781 if (!mShell)
1782 return NS_ERROR_FAILURE;
1784 LOG(("CaptureRollupEvents %p %i\n", this, int(aDoCapture)));
1786 if (aDoCapture) {
1787 gRollupListener = aListener;
1788 // real grab is only done when there is no dragging
1789 if (!nsWindow::DragInProgress()) {
1790 // This widget grab ensures that a Gecko GtkWidget receives mouse
1791 // events even when embedded in non-Gecko-owned GtkWidgets.
1792 // The grab is placed on the toplevel GtkWindow instead of the
1793 // MozContainer to avoid double dispatch of keyboard events
1794 // (bug 707623).
1795 gtk_grab_add(mShell);
1796 GrabPointer(GetLastUserInputTime());
1797 }
1798 }
1799 else {
1800 if (!nsWindow::DragInProgress()) {
1801 ReleaseGrabs();
1802 }
1803 // There may not have been a drag in process when aDoCapture was set,
1804 // so make sure to remove any added grab. This is a no-op if the grab
1805 // was not added to this widget.
1806 gtk_grab_remove(mShell);
1807 gRollupListener = nullptr;
1808 }
1810 return NS_OK;
1811 }
1813 NS_IMETHODIMP
1814 nsWindow::GetAttention(int32_t aCycleCount)
1815 {
1816 LOG(("nsWindow::GetAttention [%p]\n", (void *)this));
1818 GtkWidget* top_window = GetToplevelWidget();
1819 GtkWidget* top_focused_window =
1820 gFocusWindow ? gFocusWindow->GetToplevelWidget() : nullptr;
1822 // Don't get attention if the window is focused anyway.
1823 if (top_window && (gtk_widget_get_visible(top_window)) &&
1824 top_window != top_focused_window) {
1825 SetUrgencyHint(top_window, true);
1826 }
1828 return NS_OK;
1829 }
1831 bool
1832 nsWindow::HasPendingInputEvent()
1833 {
1834 // This sucks, but gtk/gdk has no way to answer the question we want while
1835 // excluding paint events, and there's no X API that will let us peek
1836 // without blocking or removing. To prevent event reordering, peek
1837 // anything except expose events. Reordering expose and others should be
1838 // ok, hopefully.
1839 bool haveEvent;
1840 #ifdef MOZ_X11
1841 XEvent ev;
1842 Display *display = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
1843 haveEvent =
1844 XCheckMaskEvent(display,
1845 KeyPressMask | KeyReleaseMask | ButtonPressMask |
1846 ButtonReleaseMask | EnterWindowMask | LeaveWindowMask |
1847 PointerMotionMask | PointerMotionHintMask |
1848 Button1MotionMask | Button2MotionMask |
1849 Button3MotionMask | Button4MotionMask |
1850 Button5MotionMask | ButtonMotionMask | KeymapStateMask |
1851 VisibilityChangeMask | StructureNotifyMask |
1852 ResizeRedirectMask | SubstructureNotifyMask |
1853 SubstructureRedirectMask | FocusChangeMask |
1854 PropertyChangeMask | ColormapChangeMask |
1855 OwnerGrabButtonMask, &ev);
1856 if (haveEvent) {
1857 XPutBackEvent(display, &ev);
1858 }
1859 #else
1860 haveEvent = false;
1861 #endif
1862 return haveEvent;
1863 }
1865 #if 0
1866 #ifdef DEBUG
1867 // Paint flashing code (disabled for cairo - see below)
1869 #define CAPS_LOCK_IS_ON \
1870 (KeymapWrapper::AreModifiersCurrentlyActive(KeymapWrapper::CAPS_LOCK))
1872 #define WANT_PAINT_FLASHING \
1873 (debug_WantPaintFlashing() && CAPS_LOCK_IS_ON)
1875 #ifdef MOZ_X11
1876 static void
1877 gdk_window_flash(GdkWindow * aGdkWindow,
1878 unsigned int aTimes,
1879 unsigned int aInterval, // Milliseconds
1880 GdkRegion * aRegion)
1881 {
1882 gint x;
1883 gint y;
1884 gint width;
1885 gint height;
1886 guint i;
1887 GdkGC * gc = 0;
1888 GdkColor white;
1890 #if (MOZ_WIDGET_GTK == 2)
1891 gdk_window_get_geometry(aGdkWindow,nullptr,nullptr,&width,&height,nullptr);
1892 #else
1893 gdk_window_get_geometry(aGdkWindow,nullptr,nullptr,&width,&height);
1894 #endif
1896 gdk_window_get_origin (aGdkWindow,
1897 &x,
1898 &y);
1900 gc = gdk_gc_new(gdk_get_default_root_window());
1902 white.pixel = WhitePixel(gdk_display,DefaultScreen(gdk_display));
1904 gdk_gc_set_foreground(gc,&white);
1905 gdk_gc_set_function(gc,GDK_XOR);
1906 gdk_gc_set_subwindow(gc,GDK_INCLUDE_INFERIORS);
1908 gdk_region_offset(aRegion, x, y);
1909 gdk_gc_set_clip_region(gc, aRegion);
1911 /*
1912 * Need to do this twice so that the XOR effect can replace
1913 * the original window contents.
1914 */
1915 for (i = 0; i < aTimes * 2; i++)
1916 {
1917 gdk_draw_rectangle(gdk_get_default_root_window(),
1918 gc,
1919 TRUE,
1920 x,
1921 y,
1922 width,
1923 height);
1925 gdk_flush();
1927 PR_Sleep(PR_MillisecondsToInterval(aInterval));
1928 }
1930 gdk_gc_destroy(gc);
1932 gdk_region_offset(aRegion, -x, -y);
1933 }
1934 #endif /* MOZ_X11 */
1935 #endif // DEBUG
1936 #endif
1938 struct ExposeRegion
1939 {
1940 nsIntRegion mRegion;
1942 #if (MOZ_WIDGET_GTK == 2)
1943 GdkRectangle *mRects;
1944 GdkRectangle *mRectsEnd;
1946 ExposeRegion() : mRects(nullptr)
1947 {
1948 }
1949 ~ExposeRegion()
1950 {
1951 g_free(mRects);
1952 }
1953 bool Init(GdkEventExpose *aEvent)
1954 {
1955 gint nrects;
1956 gdk_region_get_rectangles(aEvent->region, &mRects, &nrects);
1958 if (nrects > MAX_RECTS_IN_REGION) {
1959 // Just use the bounding box
1960 mRects[0] = aEvent->area;
1961 nrects = 1;
1962 }
1964 mRectsEnd = mRects + nrects;
1966 for (GdkRectangle *r = mRects; r < mRectsEnd; r++) {
1967 mRegion.Or(mRegion, nsIntRect(r->x, r->y, r->width, r->height));
1968 LOGDRAW(("\t%d %d %d %d\n", r->x, r->y, r->width, r->height));
1969 }
1970 return true;
1971 }
1973 #else
1974 # ifdef cairo_copy_clip_rectangle_list
1975 # error "Looks like we're including Mozilla's cairo instead of system cairo"
1976 # endif
1977 cairo_rectangle_list_t *mRects;
1979 ExposeRegion() : mRects(nullptr)
1980 {
1981 }
1982 ~ExposeRegion()
1983 {
1984 cairo_rectangle_list_destroy(mRects);
1985 }
1986 bool Init(cairo_t* cr)
1987 {
1988 mRects = cairo_copy_clip_rectangle_list(cr);
1989 if (mRects->status != CAIRO_STATUS_SUCCESS) {
1990 NS_WARNING("Failed to obtain cairo rectangle list.");
1991 return false;
1992 }
1994 for (int i = 0; i < mRects->num_rectangles; i++) {
1995 const cairo_rectangle_t& r = mRects->rectangles[i];
1996 mRegion.Or(mRegion, nsIntRect(r.x, r.y, r.width, r.height));
1997 LOGDRAW(("\t%d %d %d %d\n", r.x, r.y, r.width, r.height));
1998 }
1999 return true;
2000 }
2001 #endif
2002 };
2004 #if (MOZ_WIDGET_GTK == 2)
2005 gboolean
2006 nsWindow::OnExposeEvent(GdkEventExpose *aEvent)
2007 #else
2008 gboolean
2009 nsWindow::OnExposeEvent(cairo_t *cr)
2010 #endif
2011 {
2012 if (mIsDestroyed) {
2013 return FALSE;
2014 }
2016 // Windows that are not visible will be painted after they become visible.
2017 if (!mGdkWindow || mIsFullyObscured || !mHasMappedToplevel)
2018 return FALSE;
2020 nsIWidgetListener *listener =
2021 mAttachedWidgetListener ? mAttachedWidgetListener : mWidgetListener;
2022 if (!listener)
2023 return FALSE;
2025 ExposeRegion exposeRegion;
2026 #if (MOZ_WIDGET_GTK == 2)
2027 if (!exposeRegion.Init(aEvent)) {
2028 #else
2029 if (!exposeRegion.Init(cr)) {
2030 #endif
2031 return FALSE;
2032 }
2034 nsIntRegion ®ion = exposeRegion.mRegion;
2036 ClientLayerManager *clientLayers =
2037 (GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_CLIENT)
2038 ? static_cast<ClientLayerManager*>(GetLayerManager())
2039 : nullptr;
2041 if (clientLayers && mCompositorParent) {
2042 // We need to paint to the screen even if nothing changed, since if we
2043 // don't have a compositing window manager, our pixels could be stale.
2044 clientLayers->SetNeedsComposite(true);
2045 clientLayers->SendInvalidRegion(region);
2046 }
2048 // Dispatch WillPaintWindow notification to allow scripts etc. to run
2049 // before we paint
2050 {
2051 listener->WillPaintWindow(this);
2053 // If the window has been destroyed during the will paint notification,
2054 // there is nothing left to do.
2055 if (!mGdkWindow)
2056 return TRUE;
2058 // Re-get the listener since the will paint notification might have
2059 // killed it.
2060 listener =
2061 mAttachedWidgetListener ? mAttachedWidgetListener : mWidgetListener;
2062 if (!listener)
2063 return FALSE;
2064 }
2066 if (clientLayers && mCompositorParent && clientLayers->NeedsComposite()) {
2067 mCompositorParent->ScheduleRenderOnCompositorThread();
2068 clientLayers->SetNeedsComposite(false);
2069 }
2071 LOGDRAW(("sending expose event [%p] %p 0x%lx (rects follow):\n",
2072 (void *)this, (void *)mGdkWindow,
2073 gdk_x11_window_get_xid(mGdkWindow)));
2075 // Our bounds may have changed after calling WillPaintWindow. Clip
2076 // to the new bounds here. The region is relative to this
2077 // window.
2078 region.And(region, nsIntRect(0, 0, mBounds.width, mBounds.height));
2080 bool shaped = false;
2081 if (eTransparencyTransparent == GetTransparencyMode()) {
2082 GdkScreen *screen = gdk_window_get_screen(mGdkWindow);
2083 if (gdk_screen_is_composited(screen) &&
2084 gdk_window_get_visual(mGdkWindow) ==
2085 gdk_screen_get_rgba_visual(screen)) {
2086 // Remove possible shape mask from when window manger was not
2087 // previously compositing.
2088 static_cast<nsWindow*>(GetTopLevelWidget())->
2089 ClearTransparencyBitmap();
2090 } else {
2091 shaped = true;
2092 }
2093 }
2095 if (!shaped) {
2096 GList *children =
2097 gdk_window_peek_children(mGdkWindow);
2098 while (children) {
2099 GdkWindow *gdkWin = GDK_WINDOW(children->data);
2100 nsWindow *kid = get_window_for_gdk_window(gdkWin);
2101 if (kid && gdk_window_is_visible(gdkWin)) {
2102 nsAutoTArray<nsIntRect,1> clipRects;
2103 kid->GetWindowClipRegion(&clipRects);
2104 nsIntRect bounds;
2105 kid->GetBounds(bounds);
2106 for (uint32_t i = 0; i < clipRects.Length(); ++i) {
2107 nsIntRect r = clipRects[i] + bounds.TopLeft();
2108 region.Sub(region, r);
2109 }
2110 }
2111 children = children->next;
2112 }
2113 }
2115 if (region.IsEmpty()) {
2116 return TRUE;
2117 }
2119 // If this widget uses OMTC...
2120 if (GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_CLIENT) {
2121 listener->PaintWindow(this, region);
2122 listener->DidPaintWindow();
2123 return TRUE;
2124 }
2126 gfxASurface* surf;
2127 #if (MOZ_WIDGET_GTK == 2)
2128 surf = GetThebesSurface();
2129 #else
2130 surf = GetThebesSurface(cr);
2131 #endif
2133 nsRefPtr<gfxContext> ctx;
2134 if (gfxPlatform::GetPlatform()->
2135 SupportsAzureContentForType(BackendType::CAIRO)) {
2136 IntSize intSize(surf->GetSize().width, surf->GetSize().height);
2137 ctx = new gfxContext(gfxPlatform::GetPlatform()->
2138 CreateDrawTargetForSurface(surf, intSize));
2139 } else if (gfxPlatform::GetPlatform()->
2140 SupportsAzureContentForType(BackendType::SKIA) &&
2141 surf->GetType() == gfxSurfaceType::Image) {
2142 gfxImageSurface* imgSurf = static_cast<gfxImageSurface*>(surf);
2143 SurfaceFormat format = ImageFormatToSurfaceFormat(imgSurf->Format());
2144 IntSize intSize(surf->GetSize().width, surf->GetSize().height);
2145 ctx = new gfxContext(gfxPlatform::GetPlatform()->CreateDrawTargetForData(
2146 imgSurf->Data(), intSize, imgSurf->Stride(), format));
2147 } else {
2148 ctx = new gfxContext(surf);
2149 }
2151 #ifdef MOZ_X11
2152 nsIntRect boundsRect; // for shaped only
2154 ctx->NewPath();
2155 if (shaped) {
2156 // Collapse update area to the bounding box. This is so we only have to
2157 // call UpdateTranslucentWindowAlpha once. After we have dropped
2158 // support for non-Thebes graphics, UpdateTranslucentWindowAlpha will be
2159 // our private interface so we can rework things to avoid this.
2160 boundsRect = region.GetBounds();
2161 ctx->Rectangle(gfxRect(boundsRect.x, boundsRect.y,
2162 boundsRect.width, boundsRect.height));
2163 } else {
2164 gfxUtils::PathFromRegion(ctx, region);
2165 }
2166 ctx->Clip();
2168 BufferMode layerBuffering;
2169 if (shaped) {
2170 // The double buffering is done here to extract the shape mask.
2171 // (The shape mask won't be necessary when a visual with an alpha
2172 // channel is used on compositing window managers.)
2173 layerBuffering = mozilla::layers::BufferMode::BUFFER_NONE;
2174 ctx->PushGroup(gfxContentType::COLOR_ALPHA);
2175 #ifdef MOZ_HAVE_SHMIMAGE
2176 } else if (nsShmImage::UseShm()) {
2177 // We're using an xshm mapping as a back buffer.
2178 layerBuffering = mozilla::layers::BufferMode::BUFFER_NONE;
2179 #endif // MOZ_HAVE_SHMIMAGE
2180 } else {
2181 // Get the layer manager to do double buffering (if necessary).
2182 layerBuffering = mozilla::layers::BufferMode::BUFFERED;
2183 }
2185 #if 0
2186 // NOTE: Paint flashing region would be wrong for cairo, since
2187 // cairo inflates the update region, etc. So don't paint flash
2188 // for cairo.
2189 #ifdef DEBUG
2190 // XXX aEvent->region may refer to a newly-invalid area. FIXME
2191 if (0 && WANT_PAINT_FLASHING && gtk_widget_get_window(aEvent))
2192 gdk_window_flash(mGdkWindow, 1, 100, aEvent->region);
2193 #endif
2194 #endif
2196 #endif // MOZ_X11
2198 bool painted = false;
2199 {
2200 if (GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_BASIC) {
2201 AutoLayerManagerSetup setupLayerManager(this, ctx, layerBuffering);
2202 painted = listener->PaintWindow(this, region);
2203 }
2204 }
2206 #ifdef MOZ_X11
2207 // PaintWindow can Destroy us (bug 378273), avoid doing any paint
2208 // operations below if that happened - it will lead to XError and exit().
2209 if (shaped) {
2210 if (MOZ_LIKELY(!mIsDestroyed)) {
2211 if (painted) {
2212 nsRefPtr<gfxPattern> pattern = ctx->PopGroup();
2214 UpdateAlpha(pattern, boundsRect);
2216 ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
2217 ctx->SetPattern(pattern);
2218 ctx->Paint();
2219 }
2220 }
2221 }
2222 # ifdef MOZ_HAVE_SHMIMAGE
2223 if (mShmImage && MOZ_LIKELY(!mIsDestroyed)) {
2224 #if (MOZ_WIDGET_GTK == 2)
2225 mShmImage->Put(mGdkWindow, exposeRegion.mRects, exposeRegion.mRectsEnd);
2226 #else
2227 mShmImage->Put(mGdkWindow, exposeRegion.mRects);
2228 #endif
2229 }
2230 # endif // MOZ_HAVE_SHMIMAGE
2231 #endif // MOZ_X11
2233 listener->DidPaintWindow();
2235 // Synchronously flush any new dirty areas
2236 #if (MOZ_WIDGET_GTK == 2)
2237 GdkRegion* dirtyArea = gdk_window_get_update_area(mGdkWindow);
2238 #else
2239 cairo_region_t* dirtyArea = gdk_window_get_update_area(mGdkWindow);
2240 #endif
2242 if (dirtyArea) {
2243 gdk_window_invalidate_region(mGdkWindow, dirtyArea, false);
2244 #if (MOZ_WIDGET_GTK == 2)
2245 gdk_region_destroy(dirtyArea);
2246 #else
2247 cairo_region_destroy(dirtyArea);
2248 #endif
2249 gdk_window_process_updates(mGdkWindow, false);
2250 }
2252 // check the return value!
2253 return TRUE;
2254 }
2256 void
2257 nsWindow::UpdateAlpha(gfxPattern* aPattern, nsIntRect aBoundsRect)
2258 {
2259 // We need to create our own buffer to force the stride to match the
2260 // expected stride.
2261 int32_t stride = GetAlignedStride<4>(BytesPerPixel(SurfaceFormat::A8) *
2262 aBoundsRect.width);
2263 int32_t bufferSize = stride * aBoundsRect.height;
2264 nsAutoArrayPtr<uint8_t> imageBuffer(new (std::nothrow) uint8_t[bufferSize]);
2265 RefPtr<DrawTarget> drawTarget = gfxPlatform::GetPlatform()->
2266 CreateDrawTargetForData(imageBuffer, ToIntSize(aBoundsRect.Size()),
2267 stride, SurfaceFormat::A8);
2269 if (drawTarget) {
2270 drawTarget->FillRect(Rect(0, 0, aBoundsRect.width, aBoundsRect.height),
2271 *aPattern->GetPattern(drawTarget),
2272 DrawOptions(1.0, CompositionOp::OP_SOURCE));
2273 }
2274 UpdateTranslucentWindowAlphaInternal(aBoundsRect, imageBuffer, stride);
2275 }
2277 gboolean
2278 nsWindow::OnConfigureEvent(GtkWidget *aWidget, GdkEventConfigure *aEvent)
2279 {
2280 // These events are only received on toplevel windows.
2281 //
2282 // GDK ensures that the coordinates are the client window top-left wrt the
2283 // root window.
2284 //
2285 // GDK calculates the cordinates for real ConfigureNotify events on
2286 // managed windows (that would normally be relative to the parent
2287 // window).
2288 //
2289 // Synthetic ConfigureNotify events are from the window manager and
2290 // already relative to the root window. GDK creates all X windows with
2291 // border_width = 0, so synthetic events also indicate the top-left of
2292 // the client window.
2293 //
2294 // Override-redirect windows are children of the root window so parent
2295 // coordinates are root coordinates.
2297 LOG(("configure event [%p] %d %d %d %d\n", (void *)this,
2298 aEvent->x, aEvent->y, aEvent->width, aEvent->height));
2300 nsIntRect screenBounds;
2301 GetScreenBounds(screenBounds);
2303 if (mWindowType == eWindowType_toplevel || mWindowType == eWindowType_dialog) {
2304 // This check avoids unwanted rollup on spurious configure events from
2305 // Cygwin/X (bug 672103).
2306 if (mBounds.x != screenBounds.x ||
2307 mBounds.y != screenBounds.y) {
2308 CheckForRollup(0, 0, false, true);
2309 }
2310 }
2312 // This event indicates that the window position may have changed.
2313 // mBounds.Size() is updated in OnSizeAllocate().
2315 // (The gtk_window_get_window_type() function is only available from
2316 // version 2.20.)
2317 NS_ASSERTION(GTK_IS_WINDOW(aWidget),
2318 "Configure event on widget that is not a GtkWindow");
2319 gint type;
2320 g_object_get(aWidget, "type", &type, nullptr);
2321 if (type == GTK_WINDOW_POPUP) {
2322 // Override-redirect window
2323 //
2324 // These windows should not be moved by the window manager, and so any
2325 // change in position is a result of our direction. mBounds has
2326 // already been set in Move() or Resize(), and that is more
2327 // up-to-date than the position in the ConfigureNotify event if the
2328 // event is from an earlier window move.
2329 //
2330 // Skipping the WindowMoved call saves context menus from an infinite
2331 // loop when nsXULPopupManager::PopupMoved moves the window to the new
2332 // position and nsMenuPopupFrame::SetPopupPosition adds
2333 // offsetForContextMenu on each iteration.
2334 return FALSE;
2335 }
2337 mBounds.MoveTo(screenBounds.TopLeft());
2339 // XXX mozilla will invalidate the entire window after this move
2340 // complete. wtf?
2341 NotifyWindowMoved(mBounds.x, mBounds.y);
2343 return FALSE;
2344 }
2346 void
2347 nsWindow::OnContainerUnrealize()
2348 {
2349 // The GdkWindows are about to be destroyed (but not deleted), so remove
2350 // their references back to their container widget while the GdkWindow
2351 // hierarchy is still available.
2353 if (mGdkWindow) {
2354 DestroyChildWindows();
2356 g_object_set_data(G_OBJECT(mGdkWindow), "nsWindow", nullptr);
2357 mGdkWindow = nullptr;
2358 }
2359 }
2361 void
2362 nsWindow::OnSizeAllocate(GtkAllocation *aAllocation)
2363 {
2364 LOG(("size_allocate [%p] %d %d %d %d\n",
2365 (void *)this, aAllocation->x, aAllocation->y,
2366 aAllocation->width, aAllocation->height));
2368 nsIntSize size(aAllocation->width, aAllocation->height);
2369 if (mBounds.Size() == size)
2370 return;
2372 // Invalidate the new part of the window now for the pending paint to
2373 // minimize background flashes (GDK does not do this for external resizes
2374 // of toplevels.)
2375 if (mBounds.width < size.width) {
2376 GdkRectangle rect =
2377 { mBounds.width, 0, size.width - mBounds.width, size.height };
2378 gdk_window_invalidate_rect(mGdkWindow, &rect, FALSE);
2379 }
2380 if (mBounds.height < size.height) {
2381 GdkRectangle rect =
2382 { 0, mBounds.height, size.width, size.height - mBounds.height };
2383 gdk_window_invalidate_rect(mGdkWindow, &rect, FALSE);
2384 }
2386 mBounds.SizeTo(size);
2388 if (!mGdkWindow)
2389 return;
2391 DispatchResized(size.width, size.height);
2392 }
2394 void
2395 nsWindow::OnDeleteEvent()
2396 {
2397 if (mWidgetListener)
2398 mWidgetListener->RequestWindowClose(this);
2399 }
2401 void
2402 nsWindow::OnEnterNotifyEvent(GdkEventCrossing *aEvent)
2403 {
2404 // This skips NotifyVirtual and NotifyNonlinearVirtual enter notify events
2405 // when the pointer enters a child window. If the destination window is a
2406 // Gecko window then we'll catch the corresponding event on that window,
2407 // but we won't notice when the pointer directly enters a foreign (plugin)
2408 // child window without passing over a visible portion of a Gecko window.
2409 if (aEvent->subwindow != nullptr)
2410 return;
2412 // Check before is_parent_ungrab_enter() as the button state may have
2413 // changed while a non-Gecko ancestor window had a pointer grab.
2414 DispatchMissedButtonReleases(aEvent);
2416 if (is_parent_ungrab_enter(aEvent))
2417 return;
2419 WidgetMouseEvent event(true, NS_MOUSE_ENTER, this, WidgetMouseEvent::eReal);
2421 event.refPoint.x = nscoord(aEvent->x);
2422 event.refPoint.y = nscoord(aEvent->y);
2424 event.time = aEvent->time;
2426 LOG(("OnEnterNotify: %p\n", (void *)this));
2428 nsEventStatus status;
2429 DispatchEvent(&event, status);
2430 }
2432 // XXX Is this the right test for embedding cases?
2433 static bool
2434 is_top_level_mouse_exit(GdkWindow* aWindow, GdkEventCrossing *aEvent)
2435 {
2436 gint x = gint(aEvent->x_root);
2437 gint y = gint(aEvent->y_root);
2438 GdkDisplay* display = gdk_window_get_display(aWindow);
2439 GdkWindow* winAtPt = gdk_display_get_window_at_pointer(display, &x, &y);
2440 if (!winAtPt)
2441 return true;
2442 GdkWindow* topLevelAtPt = gdk_window_get_toplevel(winAtPt);
2443 GdkWindow* topLevelWidget = gdk_window_get_toplevel(aWindow);
2444 return topLevelAtPt != topLevelWidget;
2445 }
2447 void
2448 nsWindow::OnLeaveNotifyEvent(GdkEventCrossing *aEvent)
2449 {
2450 // This ignores NotifyVirtual and NotifyNonlinearVirtual leave notify
2451 // events when the pointer leaves a child window. If the destination
2452 // window is a Gecko window then we'll catch the corresponding event on
2453 // that window.
2454 //
2455 // XXXkt However, we will miss toplevel exits when the pointer directly
2456 // leaves a foreign (plugin) child window without passing over a visible
2457 // portion of a Gecko window.
2458 if (aEvent->subwindow != nullptr)
2459 return;
2461 WidgetMouseEvent event(true, NS_MOUSE_EXIT, this, WidgetMouseEvent::eReal);
2463 event.refPoint.x = nscoord(aEvent->x);
2464 event.refPoint.y = nscoord(aEvent->y);
2466 event.time = aEvent->time;
2468 event.exit = is_top_level_mouse_exit(mGdkWindow, aEvent)
2469 ? WidgetMouseEvent::eTopLevel : WidgetMouseEvent::eChild;
2471 LOG(("OnLeaveNotify: %p\n", (void *)this));
2473 nsEventStatus status;
2474 DispatchEvent(&event, status);
2475 }
2477 void
2478 nsWindow::OnMotionNotifyEvent(GdkEventMotion *aEvent)
2479 {
2480 // see if we can compress this event
2481 // XXXldb Why skip every other motion event when we have multiple,
2482 // but not more than that?
2483 bool synthEvent = false;
2484 #ifdef MOZ_X11
2485 XEvent xevent;
2487 while (XPending (GDK_WINDOW_XDISPLAY(aEvent->window))) {
2488 XEvent peeked;
2489 XPeekEvent (GDK_WINDOW_XDISPLAY(aEvent->window), &peeked);
2490 if (peeked.xany.window != gdk_x11_window_get_xid(aEvent->window)
2491 || peeked.type != MotionNotify)
2492 break;
2494 synthEvent = true;
2495 XNextEvent (GDK_WINDOW_XDISPLAY(aEvent->window), &xevent);
2496 }
2497 #if (MOZ_WIDGET_GTK == 2)
2498 // if plugins still keeps the focus, get it back
2499 if (gPluginFocusWindow && gPluginFocusWindow != this) {
2500 nsRefPtr<nsWindow> kungFuDeathGrip = gPluginFocusWindow;
2501 gPluginFocusWindow->LoseNonXEmbedPluginFocus();
2502 }
2503 #endif /* MOZ_WIDGET_GTK2 */
2504 #endif /* MOZ_X11 */
2506 WidgetMouseEvent event(true, NS_MOUSE_MOVE, this, WidgetMouseEvent::eReal);
2508 gdouble pressure = 0;
2509 gdk_event_get_axis ((GdkEvent*)aEvent, GDK_AXIS_PRESSURE, &pressure);
2510 // Sometime gdk generate 0 pressure value between normal values
2511 // We have to ignore that and use last valid value
2512 if (pressure)
2513 mLastMotionPressure = pressure;
2514 event.pressure = mLastMotionPressure;
2516 guint modifierState;
2517 if (synthEvent) {
2518 #ifdef MOZ_X11
2519 event.refPoint.x = nscoord(xevent.xmotion.x);
2520 event.refPoint.y = nscoord(xevent.xmotion.y);
2522 modifierState = xevent.xmotion.state;
2524 event.time = xevent.xmotion.time;
2525 #else
2526 event.refPoint.x = nscoord(aEvent->x);
2527 event.refPoint.y = nscoord(aEvent->y);
2529 modifierState = aEvent->state;
2531 event.time = aEvent->time;
2532 #endif /* MOZ_X11 */
2533 }
2534 else {
2535 // XXX see OnScrollEvent()
2536 if (aEvent->window == mGdkWindow) {
2537 event.refPoint.x = nscoord(aEvent->x);
2538 event.refPoint.y = nscoord(aEvent->y);
2539 } else {
2540 LayoutDeviceIntPoint point(NSToIntFloor(aEvent->x_root),
2541 NSToIntFloor(aEvent->y_root));
2542 event.refPoint = point -
2543 LayoutDeviceIntPoint::FromUntyped(WidgetToScreenOffset());
2544 }
2546 modifierState = aEvent->state;
2548 event.time = aEvent->time;
2549 }
2551 KeymapWrapper::InitInputEvent(event, modifierState);
2553 nsEventStatus status;
2554 DispatchEvent(&event, status);
2555 }
2557 // If the automatic pointer grab on ButtonPress has deactivated before
2558 // ButtonRelease, and the mouse button is released while the pointer is not
2559 // over any a Gecko window, then the ButtonRelease event will not be received.
2560 // (A similar situation exists when the pointer is grabbed with owner_events
2561 // True as the ButtonRelease may be received on a foreign [plugin] window).
2562 // Use this method to check for released buttons when the pointer returns to a
2563 // Gecko window.
2564 void
2565 nsWindow::DispatchMissedButtonReleases(GdkEventCrossing *aGdkEvent)
2566 {
2567 guint changed = aGdkEvent->state ^ gButtonState;
2568 // Only consider button releases.
2569 // (Ignore button presses that occurred outside Gecko.)
2570 guint released = changed & gButtonState;
2571 gButtonState = aGdkEvent->state;
2573 // Loop over each button, excluding mouse wheel buttons 4 and 5 for which
2574 // GDK ignores releases.
2575 for (guint buttonMask = GDK_BUTTON1_MASK;
2576 buttonMask <= GDK_BUTTON3_MASK;
2577 buttonMask <<= 1) {
2579 if (released & buttonMask) {
2580 int16_t buttonType;
2581 switch (buttonMask) {
2582 case GDK_BUTTON1_MASK:
2583 buttonType = WidgetMouseEvent::eLeftButton;
2584 break;
2585 case GDK_BUTTON2_MASK:
2586 buttonType = WidgetMouseEvent::eMiddleButton;
2587 break;
2588 default:
2589 NS_ASSERTION(buttonMask == GDK_BUTTON3_MASK,
2590 "Unexpected button mask");
2591 buttonType = WidgetMouseEvent::eRightButton;
2592 }
2594 LOG(("Synthesized button %u release on %p\n",
2595 guint(buttonType + 1), (void *)this));
2597 // Dispatch a synthesized button up event to tell Gecko about the
2598 // change in state. This event is marked as synthesized so that
2599 // it is not dispatched as a DOM event, because we don't know the
2600 // position, widget, modifiers, or time/order.
2601 WidgetMouseEvent synthEvent(true, NS_MOUSE_BUTTON_UP, this,
2602 WidgetMouseEvent::eSynthesized);
2603 synthEvent.button = buttonType;
2604 nsEventStatus status;
2605 DispatchEvent(&synthEvent, status);
2606 }
2607 }
2608 }
2610 void
2611 nsWindow::InitButtonEvent(WidgetMouseEvent& aEvent,
2612 GdkEventButton* aGdkEvent)
2613 {
2614 // XXX see OnScrollEvent()
2615 if (aGdkEvent->window == mGdkWindow) {
2616 aEvent.refPoint.x = nscoord(aGdkEvent->x);
2617 aEvent.refPoint.y = nscoord(aGdkEvent->y);
2618 } else {
2619 LayoutDeviceIntPoint point(NSToIntFloor(aGdkEvent->x_root),
2620 NSToIntFloor(aGdkEvent->y_root));
2621 aEvent.refPoint = point -
2622 LayoutDeviceIntPoint::FromUntyped(WidgetToScreenOffset());
2623 }
2625 guint modifierState = aGdkEvent->state;
2626 // aEvent's state doesn't include this event's information. Therefore,
2627 // if aEvent is mouse button down event, we need to set it manually.
2628 // Note that we cannot do same thing for NS_MOUSE_BUTTON_UP because
2629 // system may have two or more mice and same button of another mouse
2630 // may be still pressed.
2631 if (aGdkEvent->type != GDK_BUTTON_RELEASE) {
2632 switch (aGdkEvent->button) {
2633 case 1:
2634 modifierState |= GDK_BUTTON1_MASK;
2635 break;
2636 case 2:
2637 modifierState |= GDK_BUTTON2_MASK;
2638 break;
2639 case 3:
2640 modifierState |= GDK_BUTTON3_MASK;
2641 break;
2642 }
2643 }
2645 KeymapWrapper::InitInputEvent(aEvent, modifierState);
2647 aEvent.time = aGdkEvent->time;
2649 switch (aGdkEvent->type) {
2650 case GDK_2BUTTON_PRESS:
2651 aEvent.clickCount = 2;
2652 break;
2653 case GDK_3BUTTON_PRESS:
2654 aEvent.clickCount = 3;
2655 break;
2656 // default is one click
2657 default:
2658 aEvent.clickCount = 1;
2659 }
2660 }
2662 static guint ButtonMaskFromGDKButton(guint button)
2663 {
2664 return GDK_BUTTON1_MASK << (button - 1);
2665 }
2667 void
2668 nsWindow::OnButtonPressEvent(GdkEventButton *aEvent)
2669 {
2670 LOG(("Button %u press on %p\n", aEvent->button, (void *)this));
2672 nsEventStatus status;
2674 // If you double click in GDK, it will actually generate a second
2675 // GDK_BUTTON_PRESS before sending the GDK_2BUTTON_PRESS, and this is
2676 // different than the DOM spec. GDK puts this in the queue
2677 // programatically, so it's safe to assume that if there's a
2678 // double click in the queue, it was generated so we can just drop
2679 // this click.
2680 GdkEvent *peekedEvent = gdk_event_peek();
2681 if (peekedEvent) {
2682 GdkEventType type = peekedEvent->any.type;
2683 gdk_event_free(peekedEvent);
2684 if (type == GDK_2BUTTON_PRESS || type == GDK_3BUTTON_PRESS)
2685 return;
2686 }
2688 nsWindow *containerWindow = GetContainerWindow();
2689 if (!gFocusWindow && containerWindow) {
2690 containerWindow->DispatchActivateEvent();
2691 }
2693 // check to see if we should rollup
2694 if (CheckForRollup(aEvent->x_root, aEvent->y_root, false, false))
2695 return;
2697 gdouble pressure = 0;
2698 gdk_event_get_axis ((GdkEvent*)aEvent, GDK_AXIS_PRESSURE, &pressure);
2699 mLastMotionPressure = pressure;
2701 uint16_t domButton;
2702 switch (aEvent->button) {
2703 case 1:
2704 domButton = WidgetMouseEvent::eLeftButton;
2705 break;
2706 case 2:
2707 domButton = WidgetMouseEvent::eMiddleButton;
2708 break;
2709 case 3:
2710 domButton = WidgetMouseEvent::eRightButton;
2711 break;
2712 // These are mapped to horizontal scroll
2713 case 6:
2714 case 7:
2715 NS_WARNING("We're not supporting legacy horizontal scroll event");
2716 return;
2717 // Map buttons 8-9 to back/forward
2718 case 8:
2719 DispatchCommandEvent(nsGkAtoms::Back);
2720 return;
2721 case 9:
2722 DispatchCommandEvent(nsGkAtoms::Forward);
2723 return;
2724 default:
2725 return;
2726 }
2728 gButtonState |= ButtonMaskFromGDKButton(aEvent->button);
2730 WidgetMouseEvent event(true, NS_MOUSE_BUTTON_DOWN, this,
2731 WidgetMouseEvent::eReal);
2732 event.button = domButton;
2733 InitButtonEvent(event, aEvent);
2734 event.pressure = mLastMotionPressure;
2736 DispatchEvent(&event, status);
2738 // right menu click on linux should also pop up a context menu
2739 if (domButton == WidgetMouseEvent::eRightButton &&
2740 MOZ_LIKELY(!mIsDestroyed)) {
2741 WidgetMouseEvent contextMenuEvent(true, NS_CONTEXTMENU, this,
2742 WidgetMouseEvent::eReal);
2743 InitButtonEvent(contextMenuEvent, aEvent);
2744 contextMenuEvent.pressure = mLastMotionPressure;
2745 DispatchEvent(&contextMenuEvent, status);
2746 }
2747 }
2749 void
2750 nsWindow::OnButtonReleaseEvent(GdkEventButton *aEvent)
2751 {
2752 LOG(("Button %u release on %p\n", aEvent->button, (void *)this));
2754 uint16_t domButton;
2755 switch (aEvent->button) {
2756 case 1:
2757 domButton = WidgetMouseEvent::eLeftButton;
2758 break;
2759 case 2:
2760 domButton = WidgetMouseEvent::eMiddleButton;
2761 break;
2762 case 3:
2763 domButton = WidgetMouseEvent::eRightButton;
2764 break;
2765 default:
2766 return;
2767 }
2769 gButtonState &= ~ButtonMaskFromGDKButton(aEvent->button);
2771 WidgetMouseEvent event(true, NS_MOUSE_BUTTON_UP, this,
2772 WidgetMouseEvent::eReal);
2773 event.button = domButton;
2774 InitButtonEvent(event, aEvent);
2775 gdouble pressure = 0;
2776 gdk_event_get_axis ((GdkEvent*)aEvent, GDK_AXIS_PRESSURE, &pressure);
2777 event.pressure = pressure ? pressure : mLastMotionPressure;
2779 nsEventStatus status;
2780 DispatchEvent(&event, status);
2781 mLastMotionPressure = pressure;
2782 }
2784 void
2785 nsWindow::OnContainerFocusInEvent(GdkEventFocus *aEvent)
2786 {
2787 LOGFOCUS(("OnContainerFocusInEvent [%p]\n", (void *)this));
2789 // Unset the urgency hint, if possible
2790 GtkWidget* top_window = GetToplevelWidget();
2791 if (top_window && (gtk_widget_get_visible(top_window)))
2792 SetUrgencyHint(top_window, false);
2794 // Return if being called within SetFocus because the focus manager
2795 // already knows that the window is active.
2796 if (gBlockActivateEvent) {
2797 LOGFOCUS(("activated notification is blocked [%p]\n", (void *)this));
2798 return;
2799 }
2801 // If keyboard input will be accepted, the focus manager will call
2802 // SetFocus to set the correct window.
2803 gFocusWindow = nullptr;
2805 DispatchActivateEvent();
2807 if (!gFocusWindow) {
2808 // We don't really have a window for dispatching key events, but
2809 // setting a non-nullptr value here prevents OnButtonPressEvent() from
2810 // dispatching an activation notification if the widget is already
2811 // active.
2812 gFocusWindow = this;
2813 }
2815 LOGFOCUS(("Events sent from focus in event [%p]\n", (void *)this));
2816 }
2818 void
2819 nsWindow::OnContainerFocusOutEvent(GdkEventFocus *aEvent)
2820 {
2821 LOGFOCUS(("OnContainerFocusOutEvent [%p]\n", (void *)this));
2823 if (mWindowType == eWindowType_toplevel || mWindowType == eWindowType_dialog) {
2824 nsCOMPtr<nsIDragService> dragService = do_GetService(kCDragServiceCID);
2825 nsCOMPtr<nsIDragSession> dragSession;
2826 dragService->GetCurrentSession(getter_AddRefs(dragSession));
2828 // Rollup popups when a window is focused out unless a drag is occurring.
2829 // This check is because drags grab the keyboard and cause a focus out on
2830 // versions of GTK before 2.18.
2831 bool shouldRollup = !dragSession;
2832 if (!shouldRollup) {
2833 // we also roll up when a drag is from a different application
2834 nsCOMPtr<nsIDOMNode> sourceNode;
2835 dragSession->GetSourceNode(getter_AddRefs(sourceNode));
2836 shouldRollup = (sourceNode == nullptr);
2837 }
2839 if (shouldRollup) {
2840 CheckForRollup(0, 0, false, true);
2841 }
2842 }
2844 #if (MOZ_WIDGET_GTK == 2) && defined(MOZ_X11)
2845 // plugin lose focus
2846 if (gPluginFocusWindow) {
2847 nsRefPtr<nsWindow> kungFuDeathGrip = gPluginFocusWindow;
2848 gPluginFocusWindow->LoseNonXEmbedPluginFocus();
2849 }
2850 #endif /* MOZ_X11 && MOZ_WIDGET_GTK2 */
2852 if (gFocusWindow) {
2853 nsRefPtr<nsWindow> kungFuDeathGrip = gFocusWindow;
2854 if (gFocusWindow->mIMModule) {
2855 gFocusWindow->mIMModule->OnBlurWindow(gFocusWindow);
2856 }
2857 gFocusWindow = nullptr;
2858 }
2860 DispatchDeactivateEvent();
2862 LOGFOCUS(("Done with container focus out [%p]\n", (void *)this));
2863 }
2865 bool
2866 nsWindow::DispatchCommandEvent(nsIAtom* aCommand)
2867 {
2868 nsEventStatus status;
2869 WidgetCommandEvent event(true, nsGkAtoms::onAppCommand, aCommand, this);
2870 DispatchEvent(&event, status);
2871 return TRUE;
2872 }
2874 bool
2875 nsWindow::DispatchContentCommandEvent(int32_t aMsg)
2876 {
2877 nsEventStatus status;
2878 WidgetContentCommandEvent event(true, aMsg, this);
2879 DispatchEvent(&event, status);
2880 return TRUE;
2881 }
2883 static bool
2884 IsCtrlAltTab(GdkEventKey *aEvent)
2885 {
2886 return aEvent->keyval == GDK_Tab &&
2887 KeymapWrapper::AreModifiersActive(
2888 KeymapWrapper::CTRL | KeymapWrapper::ALT, aEvent->state);
2889 }
2891 bool
2892 nsWindow::DispatchKeyDownEvent(GdkEventKey *aEvent, bool *aCancelled)
2893 {
2894 NS_PRECONDITION(aCancelled, "aCancelled must not be null");
2896 *aCancelled = false;
2898 if (IsCtrlAltTab(aEvent)) {
2899 return false;
2900 }
2902 // send the key down event
2903 nsEventStatus status;
2904 WidgetKeyboardEvent downEvent(true, NS_KEY_DOWN, this);
2905 KeymapWrapper::InitKeyEvent(downEvent, aEvent);
2906 DispatchEvent(&downEvent, status);
2907 *aCancelled = (status == nsEventStatus_eConsumeNoDefault);
2908 return true;
2909 }
2911 gboolean
2912 nsWindow::OnKeyPressEvent(GdkEventKey *aEvent)
2913 {
2914 LOGFOCUS(("OnKeyPressEvent [%p]\n", (void *)this));
2916 // if we are in the middle of composing text, XIM gets to see it
2917 // before mozilla does.
2918 bool IMEWasEnabled = false;
2919 if (mIMModule) {
2920 IMEWasEnabled = mIMModule->IsEnabled();
2921 if (mIMModule->OnKeyEvent(this, aEvent)) {
2922 return TRUE;
2923 }
2924 }
2926 nsEventStatus status;
2928 // work around for annoying things.
2929 if (IsCtrlAltTab(aEvent)) {
2930 return TRUE;
2931 }
2933 nsCOMPtr<nsIWidget> kungFuDeathGrip = this;
2935 // Dispatch keydown event always. At auto repeating, we should send
2936 // KEYDOWN -> KEYPRESS -> KEYDOWN -> KEYPRESS ... -> KEYUP
2937 // However, old distributions (e.g., Ubuntu 9.10) sent native key
2938 // release event, so, on such platform, the DOM events will be:
2939 // KEYDOWN -> KEYPRESS -> KEYUP -> KEYDOWN -> KEYPRESS -> KEYUP...
2941 bool isKeyDownCancelled = false;
2942 if (DispatchKeyDownEvent(aEvent, &isKeyDownCancelled) &&
2943 (MOZ_UNLIKELY(mIsDestroyed) || isKeyDownCancelled)) {
2944 return TRUE;
2945 }
2947 // If a keydown event handler causes to enable IME, i.e., it moves
2948 // focus from IME unusable content to IME usable editor, we should
2949 // send the native key event to IME for the first input on the editor.
2950 if (!IMEWasEnabled && mIMModule && mIMModule->IsEnabled()) {
2951 // Notice our keydown event was already dispatched. This prevents
2952 // unnecessary DOM keydown event in the editor.
2953 if (mIMModule->OnKeyEvent(this, aEvent, true)) {
2954 return TRUE;
2955 }
2956 }
2958 // Don't pass modifiers as NS_KEY_PRESS events.
2959 // TODO: Instead of selectively excluding some keys from NS_KEY_PRESS events,
2960 // we should instead selectively include (as per MSDN spec; no official
2961 // spec covers KeyPress events).
2962 if (!KeymapWrapper::IsKeyPressEventNecessary(aEvent)) {
2963 return TRUE;
2964 }
2966 #ifdef MOZ_X11
2967 #if ! defined AIX // no XFree86 on AIX 5L
2968 // Look for specialized app-command keys
2969 switch (aEvent->keyval) {
2970 case XF86XK_Back:
2971 return DispatchCommandEvent(nsGkAtoms::Back);
2972 case XF86XK_Forward:
2973 return DispatchCommandEvent(nsGkAtoms::Forward);
2974 case XF86XK_Refresh:
2975 return DispatchCommandEvent(nsGkAtoms::Reload);
2976 case XF86XK_Stop:
2977 return DispatchCommandEvent(nsGkAtoms::Stop);
2978 case XF86XK_Search:
2979 return DispatchCommandEvent(nsGkAtoms::Search);
2980 case XF86XK_Favorites:
2981 return DispatchCommandEvent(nsGkAtoms::Bookmarks);
2982 case XF86XK_HomePage:
2983 return DispatchCommandEvent(nsGkAtoms::Home);
2984 case XF86XK_Copy:
2985 case GDK_F16: // F16, F20, F18, F14 are old keysyms for Copy Cut Paste Undo
2986 return DispatchContentCommandEvent(NS_CONTENT_COMMAND_COPY);
2987 case XF86XK_Cut:
2988 case GDK_F20:
2989 return DispatchContentCommandEvent(NS_CONTENT_COMMAND_CUT);
2990 case XF86XK_Paste:
2991 case GDK_F18:
2992 return DispatchContentCommandEvent(NS_CONTENT_COMMAND_PASTE);
2993 case GDK_Redo:
2994 return DispatchContentCommandEvent(NS_CONTENT_COMMAND_REDO);
2995 case GDK_Undo:
2996 case GDK_F14:
2997 return DispatchContentCommandEvent(NS_CONTENT_COMMAND_UNDO);
2998 }
2999 #endif /* ! AIX */
3000 #endif /* MOZ_X11 */
3002 WidgetKeyboardEvent event(true, NS_KEY_PRESS, this);
3003 KeymapWrapper::InitKeyEvent(event, aEvent);
3005 // before we dispatch a key, check if it's the context menu key.
3006 // If so, send a context menu key event instead.
3007 if (is_context_menu_key(event)) {
3008 WidgetMouseEvent contextMenuEvent(true, NS_CONTEXTMENU, this,
3009 WidgetMouseEvent::eReal,
3010 WidgetMouseEvent::eContextMenuKey);
3012 contextMenuEvent.refPoint = LayoutDeviceIntPoint(0, 0);
3013 contextMenuEvent.time = aEvent->time;
3014 contextMenuEvent.clickCount = 1;
3015 KeymapWrapper::InitInputEvent(contextMenuEvent, aEvent->state);
3016 DispatchEvent(&contextMenuEvent, status);
3017 }
3018 else {
3019 // If the character code is in the BMP, send the key press event.
3020 // Otherwise, send a text event with the equivalent UTF-16 string.
3021 if (IS_IN_BMP(event.charCode)) {
3022 DispatchEvent(&event, status);
3023 }
3024 else {
3025 WidgetTextEvent textEvent(true, NS_TEXT_TEXT, this);
3026 char16_t textString[3];
3027 textString[0] = H_SURROGATE(event.charCode);
3028 textString[1] = L_SURROGATE(event.charCode);
3029 textString[2] = 0;
3030 textEvent.theText = textString;
3031 textEvent.time = event.time;
3032 DispatchEvent(&textEvent, status);
3033 }
3034 }
3036 // If the event was consumed, return.
3037 if (status == nsEventStatus_eConsumeNoDefault) {
3038 return TRUE;
3039 }
3041 return FALSE;
3042 }
3044 gboolean
3045 nsWindow::OnKeyReleaseEvent(GdkEventKey *aEvent)
3046 {
3047 LOGFOCUS(("OnKeyReleaseEvent [%p]\n", (void *)this));
3049 if (mIMModule && mIMModule->OnKeyEvent(this, aEvent)) {
3050 return TRUE;
3051 }
3053 // send the key event as a key up event
3054 WidgetKeyboardEvent event(true, NS_KEY_UP, this);
3055 KeymapWrapper::InitKeyEvent(event, aEvent);
3057 nsEventStatus status;
3058 DispatchEvent(&event, status);
3060 // If the event was consumed, return.
3061 if (status == nsEventStatus_eConsumeNoDefault) {
3062 return TRUE;
3063 }
3065 return FALSE;
3066 }
3068 void
3069 nsWindow::OnScrollEvent(GdkEventScroll *aEvent)
3070 {
3071 // check to see if we should rollup
3072 if (CheckForRollup(aEvent->x_root, aEvent->y_root, true, false))
3073 return;
3075 WidgetWheelEvent wheelEvent(true, NS_WHEEL_WHEEL, this);
3076 wheelEvent.deltaMode = nsIDOMWheelEvent::DOM_DELTA_LINE;
3077 switch (aEvent->direction) {
3078 case GDK_SCROLL_UP:
3079 wheelEvent.deltaY = wheelEvent.lineOrPageDeltaY = -3;
3080 break;
3081 case GDK_SCROLL_DOWN:
3082 wheelEvent.deltaY = wheelEvent.lineOrPageDeltaY = 3;
3083 break;
3084 case GDK_SCROLL_LEFT:
3085 wheelEvent.deltaX = wheelEvent.lineOrPageDeltaX = -1;
3086 break;
3087 case GDK_SCROLL_RIGHT:
3088 wheelEvent.deltaX = wheelEvent.lineOrPageDeltaX = 1;
3089 break;
3090 }
3092 NS_ASSERTION(wheelEvent.deltaX || wheelEvent.deltaY,
3093 "deltaX or deltaY must be non-zero");
3095 if (aEvent->window == mGdkWindow) {
3096 // we are the window that the event happened on so no need for expensive WidgetToScreenOffset
3097 wheelEvent.refPoint.x = nscoord(aEvent->x);
3098 wheelEvent.refPoint.y = nscoord(aEvent->y);
3099 } else {
3100 // XXX we're never quite sure which GdkWindow the event came from due to our custom bubbling
3101 // in scroll_event_cb(), so use ScreenToWidget to translate the screen root coordinates into
3102 // coordinates relative to this widget.
3103 LayoutDeviceIntPoint point(NSToIntFloor(aEvent->x_root),
3104 NSToIntFloor(aEvent->y_root));
3105 wheelEvent.refPoint = point -
3106 LayoutDeviceIntPoint::FromUntyped(WidgetToScreenOffset());
3107 }
3109 KeymapWrapper::InitInputEvent(wheelEvent, aEvent->state);
3111 wheelEvent.time = aEvent->time;
3113 nsEventStatus status;
3114 DispatchEvent(&wheelEvent, status);
3115 }
3117 void
3118 nsWindow::OnVisibilityNotifyEvent(GdkEventVisibility *aEvent)
3119 {
3120 LOGDRAW(("Visibility event %i on [%p] %p\n",
3121 aEvent->state, this, aEvent->window));
3123 if (!mGdkWindow)
3124 return;
3126 switch (aEvent->state) {
3127 case GDK_VISIBILITY_UNOBSCURED:
3128 case GDK_VISIBILITY_PARTIAL:
3129 if (mIsFullyObscured && mHasMappedToplevel) {
3130 // GDK_EXPOSE events have been ignored, so make sure GDK
3131 // doesn't think that the window has already been painted.
3132 gdk_window_invalidate_rect(mGdkWindow, nullptr, FALSE);
3133 }
3135 mIsFullyObscured = false;
3137 if (!nsGtkIMModule::IsVirtualKeyboardOpened()) {
3138 // if we have to retry the grab, retry it.
3139 EnsureGrabs();
3140 }
3141 break;
3142 default: // includes GDK_VISIBILITY_FULLY_OBSCURED
3143 mIsFullyObscured = true;
3144 break;
3145 }
3146 }
3148 void
3149 nsWindow::OnWindowStateEvent(GtkWidget *aWidget, GdkEventWindowState *aEvent)
3150 {
3151 LOG(("nsWindow::OnWindowStateEvent [%p] changed %d new_window_state %d\n",
3152 (void *)this, aEvent->changed_mask, aEvent->new_window_state));
3154 if (IS_MOZ_CONTAINER(aWidget)) {
3155 // This event is notifying the container widget of changes to the
3156 // toplevel window. Just detect changes affecting whether windows are
3157 // viewable.
3158 //
3159 // (A visibility notify event is sent to each window that becomes
3160 // viewable when the toplevel is mapped, but we can't rely on that for
3161 // setting mHasMappedToplevel because these toplevel window state
3162 // events are asynchronous. The windows in the hierarchy now may not
3163 // be the same windows as when the toplevel was mapped, so they may
3164 // not get VisibilityNotify events.)
3165 bool mapped =
3166 !(aEvent->new_window_state &
3167 (GDK_WINDOW_STATE_ICONIFIED|GDK_WINDOW_STATE_WITHDRAWN));
3168 if (mHasMappedToplevel != mapped) {
3169 SetHasMappedToplevel(mapped);
3170 }
3171 return;
3172 }
3173 // else the widget is a shell widget.
3175 // We don't care about anything but changes in the maximized/icon/fullscreen
3176 // states
3177 if ((aEvent->changed_mask
3178 & (GDK_WINDOW_STATE_ICONIFIED |
3179 GDK_WINDOW_STATE_MAXIMIZED |
3180 GDK_WINDOW_STATE_FULLSCREEN)) == 0) {
3181 return;
3182 }
3184 if (aEvent->new_window_state & GDK_WINDOW_STATE_ICONIFIED) {
3185 LOG(("\tIconified\n"));
3186 mSizeState = nsSizeMode_Minimized;
3187 #ifdef ACCESSIBILITY
3188 DispatchMinimizeEventAccessible();
3189 #endif //ACCESSIBILITY
3190 }
3191 else if (aEvent->new_window_state & GDK_WINDOW_STATE_FULLSCREEN) {
3192 LOG(("\tFullscreen\n"));
3193 mSizeState = nsSizeMode_Fullscreen;
3194 }
3195 else if (aEvent->new_window_state & GDK_WINDOW_STATE_MAXIMIZED) {
3196 LOG(("\tMaximized\n"));
3197 mSizeState = nsSizeMode_Maximized;
3198 #ifdef ACCESSIBILITY
3199 DispatchMaximizeEventAccessible();
3200 #endif //ACCESSIBILITY
3201 }
3202 else {
3203 LOG(("\tNormal\n"));
3204 mSizeState = nsSizeMode_Normal;
3205 #ifdef ACCESSIBILITY
3206 DispatchRestoreEventAccessible();
3207 #endif //ACCESSIBILITY
3208 }
3210 if (mWidgetListener)
3211 mWidgetListener->SizeModeChanged(mSizeState);
3212 }
3214 void
3215 nsWindow::ThemeChanged()
3216 {
3217 NotifyThemeChanged();
3219 if (!mGdkWindow || MOZ_UNLIKELY(mIsDestroyed))
3220 return;
3222 // Dispatch theme change notification to all child windows
3223 GList *children =
3224 gdk_window_peek_children(mGdkWindow);
3225 while (children) {
3226 GdkWindow *gdkWin = GDK_WINDOW(children->data);
3228 nsWindow *win = (nsWindow*) g_object_get_data(G_OBJECT(gdkWin),
3229 "nsWindow");
3231 if (win && win != this) { // guard against infinite recursion
3232 nsRefPtr<nsWindow> kungFuDeathGrip = win;
3233 win->ThemeChanged();
3234 }
3236 children = children->next;
3237 }
3238 }
3240 void
3241 nsWindow::DispatchDragEvent(uint32_t aMsg, const nsIntPoint& aRefPoint,
3242 guint aTime)
3243 {
3244 WidgetDragEvent event(true, aMsg, this);
3246 if (aMsg == NS_DRAGDROP_OVER) {
3247 InitDragEvent(event);
3248 }
3250 event.refPoint = LayoutDeviceIntPoint::FromUntyped(aRefPoint);
3251 event.time = aTime;
3253 nsEventStatus status;
3254 DispatchEvent(&event, status);
3255 }
3257 void
3258 nsWindow::OnDragDataReceivedEvent(GtkWidget *aWidget,
3259 GdkDragContext *aDragContext,
3260 gint aX,
3261 gint aY,
3262 GtkSelectionData *aSelectionData,
3263 guint aInfo,
3264 guint aTime,
3265 gpointer aData)
3266 {
3267 LOGDRAG(("nsWindow::OnDragDataReceived(%p)\n", (void*)this));
3269 nsDragService::GetInstance()->
3270 TargetDataReceived(aWidget, aDragContext, aX, aY,
3271 aSelectionData, aInfo, aTime);
3272 }
3274 static void
3275 GetBrandName(nsXPIDLString& brandName)
3276 {
3277 nsCOMPtr<nsIStringBundleService> bundleService =
3278 do_GetService(NS_STRINGBUNDLE_CONTRACTID);
3280 nsCOMPtr<nsIStringBundle> bundle;
3281 if (bundleService)
3282 bundleService->CreateBundle(
3283 "chrome://branding/locale/brand.properties",
3284 getter_AddRefs(bundle));
3286 if (bundle)
3287 bundle->GetStringFromName(
3288 MOZ_UTF16("brandShortName"),
3289 getter_Copies(brandName));
3291 if (brandName.IsEmpty())
3292 brandName.Assign(NS_LITERAL_STRING("Mozilla"));
3293 }
3295 static GdkWindow *
3296 CreateGdkWindow(GdkWindow *parent, GtkWidget *widget)
3297 {
3298 GdkWindowAttr attributes;
3299 gint attributes_mask = GDK_WA_VISUAL;
3301 attributes.event_mask = kEvents;
3303 attributes.width = 1;
3304 attributes.height = 1;
3305 attributes.wclass = GDK_INPUT_OUTPUT;
3306 attributes.visual = gtk_widget_get_visual(widget);
3307 attributes.window_type = GDK_WINDOW_CHILD;
3309 #if (MOZ_WIDGET_GTK == 2)
3310 attributes_mask |= GDK_WA_COLORMAP;
3311 attributes.colormap = gtk_widget_get_colormap(widget);
3312 #endif
3314 GdkWindow *window = gdk_window_new(parent, &attributes, attributes_mask);
3315 gdk_window_set_user_data(window, widget);
3317 // GTK3 TODO?
3318 #if (MOZ_WIDGET_GTK == 2)
3319 /* set the default pixmap to None so that you don't end up with the
3320 gtk default which is BlackPixel. */
3321 gdk_window_set_back_pixmap(window, nullptr, FALSE);
3322 #endif
3324 return window;
3325 }
3327 nsresult
3328 nsWindow::Create(nsIWidget *aParent,
3329 nsNativeWidget aNativeParent,
3330 const nsIntRect &aRect,
3331 nsDeviceContext *aContext,
3332 nsWidgetInitData *aInitData)
3333 {
3334 // only set the base parent if we're going to be a dialog or a
3335 // toplevel
3336 nsIWidget *baseParent = aInitData &&
3337 (aInitData->mWindowType == eWindowType_dialog ||
3338 aInitData->mWindowType == eWindowType_toplevel ||
3339 aInitData->mWindowType == eWindowType_invisible) ?
3340 nullptr : aParent;
3342 #ifdef ACCESSIBILITY
3343 // Send a DBus message to check whether a11y is enabled
3344 a11y::PreInit();
3345 #endif
3347 // Ensure that the toolkit is created.
3348 nsGTKToolkit::GetToolkit();
3350 // initialize all the common bits of this class
3351 BaseCreate(baseParent, aRect, aContext, aInitData);
3353 // Do we need to listen for resizes?
3354 bool listenForResizes = false;;
3355 if (aNativeParent || (aInitData && aInitData->mListenForResizes))
3356 listenForResizes = true;
3358 // and do our common creation
3359 CommonCreate(aParent, listenForResizes);
3361 // save our bounds
3362 mBounds = aRect;
3363 ConstrainSize(&mBounds.width, &mBounds.height);
3365 // figure out our parent window
3366 GtkWidget *parentMozContainer = nullptr;
3367 GtkContainer *parentGtkContainer = nullptr;
3368 GdkWindow *parentGdkWindow = nullptr;
3369 GtkWindow *topLevelParent = nullptr;
3370 nsWindow *parentnsWindow = nullptr;
3371 GtkWidget *eventWidget = nullptr;
3373 if (aParent) {
3374 parentnsWindow = static_cast<nsWindow*>(aParent);
3375 parentGdkWindow = parentnsWindow->mGdkWindow;
3376 } else if (aNativeParent && GDK_IS_WINDOW(aNativeParent)) {
3377 parentGdkWindow = GDK_WINDOW(aNativeParent);
3378 parentnsWindow = get_window_for_gdk_window(parentGdkWindow);
3379 if (!parentnsWindow)
3380 return NS_ERROR_FAILURE;
3382 } else if (aNativeParent && GTK_IS_CONTAINER(aNativeParent)) {
3383 parentGtkContainer = GTK_CONTAINER(aNativeParent);
3384 }
3386 if (parentGdkWindow) {
3387 // get the widget for the window - it should be a moz container
3388 parentMozContainer = parentnsWindow->GetMozContainerWidget();
3389 if (!parentMozContainer)
3390 return NS_ERROR_FAILURE;
3392 // get the toplevel window just in case someone needs to use it
3393 // for setting transients or whatever.
3394 topLevelParent =
3395 GTK_WINDOW(gtk_widget_get_toplevel(parentMozContainer));
3396 }
3398 // ok, create our windows
3399 switch (mWindowType) {
3400 case eWindowType_dialog:
3401 case eWindowType_popup:
3402 case eWindowType_toplevel:
3403 case eWindowType_invisible: {
3404 mIsTopLevel = true;
3406 // We only move a general managed toplevel window if someone has
3407 // actually placed the window somewhere. If no placement has taken
3408 // place, we just let the window manager Do The Right Thing.
3409 //
3410 // Indicate that if we're shown, we at least need to have our size set.
3411 // If we get explicitly moved, the position will also be set.
3412 mNeedsResize = true;
3414 if (mWindowType == eWindowType_dialog) {
3415 mShell = gtk_window_new(GTK_WINDOW_TOPLEVEL);
3416 SetDefaultIcon();
3417 gtk_window_set_wmclass(GTK_WINDOW(mShell), "Dialog",
3418 gdk_get_program_class());
3419 gtk_window_set_type_hint(GTK_WINDOW(mShell),
3420 GDK_WINDOW_TYPE_HINT_DIALOG);
3421 gtk_window_set_transient_for(GTK_WINDOW(mShell),
3422 topLevelParent);
3423 }
3424 else if (mWindowType == eWindowType_popup) {
3425 // With popup windows, we want to control their position, so don't
3426 // wait for the window manager to place them (which wouldn't
3427 // happen with override-redirect windows anyway).
3428 mNeedsMove = true;
3430 // Popups that are not noautohide are only temporary. The are used
3431 // for menus and the like and disappear when another window is used.
3432 // For most popups, use the standard GtkWindowType GTK_WINDOW_POPUP,
3433 // which will use a Window with the override-redirect attribute
3434 // (for temporary windows).
3435 // For long-lived windows, their stacking order is managed by the
3436 // window manager, as indicated by GTK_WINDOW_TOPLEVEL ...
3437 GtkWindowType type = aInitData->mNoAutoHide ?
3438 GTK_WINDOW_TOPLEVEL : GTK_WINDOW_POPUP;
3439 mShell = gtk_window_new(type);
3440 gtk_window_set_wmclass(GTK_WINDOW(mShell), "Popup",
3441 gdk_get_program_class());
3443 if (aInitData->mSupportTranslucency) {
3444 // We need to select an ARGB visual here instead of in
3445 // SetTransparencyMode() because it has to be done before the
3446 // widget is realized. An ARGB visual is only useful if we
3447 // are on a compositing window manager.
3448 GdkScreen *screen = gtk_widget_get_screen(mShell);
3449 if (gdk_screen_is_composited(screen)) {
3450 #if (MOZ_WIDGET_GTK == 2)
3451 GdkColormap *colormap =
3452 gdk_screen_get_rgba_colormap(screen);
3453 gtk_widget_set_colormap(mShell, colormap);
3454 #else
3455 GdkVisual *visual = gdk_screen_get_rgba_visual(screen);
3456 gtk_widget_set_visual(mShell, visual);
3457 #endif
3458 }
3459 }
3460 if (aInitData->mNoAutoHide) {
3461 // ... but the window manager does not decorate this window,
3462 // nor provide a separate taskbar icon.
3463 if (mBorderStyle == eBorderStyle_default) {
3464 gtk_window_set_decorated(GTK_WINDOW(mShell), FALSE);
3465 }
3466 else {
3467 bool decorate = mBorderStyle & eBorderStyle_title;
3468 gtk_window_set_decorated(GTK_WINDOW(mShell), decorate);
3469 if (decorate) {
3470 gtk_window_set_deletable(GTK_WINDOW(mShell), mBorderStyle & eBorderStyle_close);
3471 }
3472 }
3473 gtk_window_set_skip_taskbar_hint(GTK_WINDOW(mShell), TRUE);
3474 // Element focus is managed by the parent window so the
3475 // WM_HINTS input field is set to False to tell the window
3476 // manager not to set input focus to this window ...
3477 gtk_window_set_accept_focus(GTK_WINDOW(mShell), FALSE);
3478 #ifdef MOZ_X11
3479 // ... but when the window manager offers focus through
3480 // WM_TAKE_FOCUS, focus is requested on the parent window.
3481 gtk_widget_realize(mShell);
3482 gdk_window_add_filter(gtk_widget_get_window(mShell),
3483 popup_take_focus_filter, nullptr);
3484 #endif
3485 }
3487 GdkWindowTypeHint gtkTypeHint;
3488 if (aInitData->mIsDragPopup) {
3489 gtkTypeHint = GDK_WINDOW_TYPE_HINT_DND;
3490 }
3491 else {
3492 switch (aInitData->mPopupHint) {
3493 case ePopupTypeMenu:
3494 gtkTypeHint = GDK_WINDOW_TYPE_HINT_POPUP_MENU;
3495 break;
3496 case ePopupTypeTooltip:
3497 gtkTypeHint = GDK_WINDOW_TYPE_HINT_TOOLTIP;
3498 break;
3499 default:
3500 gtkTypeHint = GDK_WINDOW_TYPE_HINT_UTILITY;
3501 break;
3502 }
3503 }
3504 gtk_window_set_type_hint(GTK_WINDOW(mShell), gtkTypeHint);
3506 if (topLevelParent) {
3507 gtk_window_set_transient_for(GTK_WINDOW(mShell),
3508 topLevelParent);
3509 }
3510 }
3511 else { // must be eWindowType_toplevel
3512 mShell = gtk_window_new(GTK_WINDOW_TOPLEVEL);
3513 SetDefaultIcon();
3514 gtk_window_set_wmclass(GTK_WINDOW(mShell), "Toplevel",
3515 gdk_get_program_class());
3517 // each toplevel window gets its own window group
3518 GtkWindowGroup *group = gtk_window_group_new();
3519 gtk_window_group_add_window(group, GTK_WINDOW(mShell));
3520 g_object_unref(group);
3521 }
3523 // Prevent GtkWindow from painting a background to flicker.
3524 gtk_widget_set_app_paintable(mShell, TRUE);
3526 // Create a container to hold child windows and child GtkWidgets.
3527 GtkWidget *container = moz_container_new();
3528 mContainer = MOZ_CONTAINER(container);
3529 // Use mShell's window for drawing and events.
3530 gtk_widget_set_has_window(container, FALSE);
3531 eventWidget = mShell;
3532 gtk_widget_add_events(eventWidget, kEvents);
3533 gtk_container_add(GTK_CONTAINER(mShell), container);
3534 gtk_widget_realize(container);
3536 // make sure this is the focus widget in the container
3537 gtk_widget_show(container);
3538 gtk_widget_grab_focus(container);
3540 // the drawing window
3541 mGdkWindow = gtk_widget_get_window(mShell);
3543 if (mWindowType == eWindowType_popup) {
3544 // gdk does not automatically set the cursor for "temporary"
3545 // windows, which are what gtk uses for popups.
3547 mCursor = eCursor_wait; // force SetCursor to actually set the
3548 // cursor, even though our internal state
3549 // indicates that we already have the
3550 // standard cursor.
3551 SetCursor(eCursor_standard);
3553 if (aInitData->mNoAutoHide) {
3554 gint wmd = ConvertBorderStyles(mBorderStyle);
3555 if (wmd != -1)
3556 gdk_window_set_decorations(gtk_widget_get_window(mShell), (GdkWMDecoration) wmd);
3557 }
3559 // If the popup ignores mouse events, set an empty input shape.
3560 if (aInitData->mMouseTransparent) {
3561 #if (MOZ_WIDGET_GTK == 2)
3562 GdkRectangle rect = { 0, 0, 0, 0 };
3563 GdkRegion *region = gdk_region_rectangle(&rect);
3565 gdk_window_input_shape_combine_region(mGdkWindow, region, 0, 0);
3566 gdk_region_destroy(region);
3567 #else
3568 cairo_rectangle_int_t rect = { 0, 0, 0, 0 };
3569 cairo_region_t *region = cairo_region_create_rectangle(&rect);
3571 gdk_window_input_shape_combine_region(mGdkWindow, region, 0, 0);
3572 cairo_region_destroy(region);
3573 #endif
3574 }
3575 }
3576 }
3577 break;
3578 case eWindowType_plugin:
3579 case eWindowType_child: {
3580 if (parentMozContainer) {
3581 mGdkWindow = CreateGdkWindow(parentGdkWindow, parentMozContainer);
3582 mHasMappedToplevel = parentnsWindow->mHasMappedToplevel;
3583 }
3584 else if (parentGtkContainer) {
3585 // This MozContainer has its own window for drawing and receives
3586 // events because there is no mShell widget (corresponding to this
3587 // nsWindow).
3588 GtkWidget *container = moz_container_new();
3589 mContainer = MOZ_CONTAINER(container);
3590 eventWidget = container;
3591 gtk_widget_add_events(eventWidget, kEvents);
3592 gtk_container_add(parentGtkContainer, container);
3593 gtk_widget_realize(container);
3595 mGdkWindow = gtk_widget_get_window(container);
3596 }
3597 else {
3598 NS_WARNING("Warning: tried to create a new child widget with no parent!");
3599 return NS_ERROR_FAILURE;
3600 }
3601 }
3602 break;
3603 default:
3604 break;
3605 }
3607 // label the drawing window with this object so we can find our way home
3608 g_object_set_data(G_OBJECT(mGdkWindow), "nsWindow", this);
3610 if (mContainer)
3611 g_object_set_data(G_OBJECT(mContainer), "nsWindow", this);
3613 if (mShell)
3614 g_object_set_data(G_OBJECT(mShell), "nsWindow", this);
3616 // attach listeners for events
3617 if (mShell) {
3618 g_signal_connect(mShell, "configure_event",
3619 G_CALLBACK(configure_event_cb), nullptr);
3620 g_signal_connect(mShell, "delete_event",
3621 G_CALLBACK(delete_event_cb), nullptr);
3622 g_signal_connect(mShell, "window_state_event",
3623 G_CALLBACK(window_state_event_cb), nullptr);
3625 GtkSettings* default_settings = gtk_settings_get_default();
3626 g_signal_connect_after(default_settings,
3627 "notify::gtk-theme-name",
3628 G_CALLBACK(theme_changed_cb), this);
3629 g_signal_connect_after(default_settings,
3630 "notify::gtk-font-name",
3631 G_CALLBACK(theme_changed_cb), this);
3632 }
3634 if (mContainer) {
3635 // Widget signals
3636 g_signal_connect(mContainer, "unrealize",
3637 G_CALLBACK(container_unrealize_cb), nullptr);
3638 g_signal_connect_after(mContainer, "size_allocate",
3639 G_CALLBACK(size_allocate_cb), nullptr);
3640 g_signal_connect(mContainer, "hierarchy-changed",
3641 G_CALLBACK(hierarchy_changed_cb), nullptr);
3642 // Initialize mHasMappedToplevel.
3643 hierarchy_changed_cb(GTK_WIDGET(mContainer), nullptr);
3644 // Expose, focus, key, and drag events are sent even to GTK_NO_WINDOW
3645 // widgets.
3646 #if (MOZ_WIDGET_GTK == 2)
3647 g_signal_connect(mContainer, "expose_event",
3648 G_CALLBACK(expose_event_cb), nullptr);
3649 #else
3650 g_signal_connect(G_OBJECT(mContainer), "draw",
3651 G_CALLBACK(expose_event_cb), nullptr);
3652 #endif
3653 g_signal_connect(mContainer, "focus_in_event",
3654 G_CALLBACK(focus_in_event_cb), nullptr);
3655 g_signal_connect(mContainer, "focus_out_event",
3656 G_CALLBACK(focus_out_event_cb), nullptr);
3657 g_signal_connect(mContainer, "key_press_event",
3658 G_CALLBACK(key_press_event_cb), nullptr);
3659 g_signal_connect(mContainer, "key_release_event",
3660 G_CALLBACK(key_release_event_cb), nullptr);
3662 gtk_drag_dest_set((GtkWidget *)mContainer,
3663 (GtkDestDefaults)0,
3664 nullptr,
3665 0,
3666 (GdkDragAction)0);
3668 g_signal_connect(mContainer, "drag_motion",
3669 G_CALLBACK(drag_motion_event_cb), nullptr);
3670 g_signal_connect(mContainer, "drag_leave",
3671 G_CALLBACK(drag_leave_event_cb), nullptr);
3672 g_signal_connect(mContainer, "drag_drop",
3673 G_CALLBACK(drag_drop_event_cb), nullptr);
3674 g_signal_connect(mContainer, "drag_data_received",
3675 G_CALLBACK(drag_data_received_event_cb), nullptr);
3677 GtkWidget *widgets[] = { GTK_WIDGET(mContainer), mShell };
3678 for (size_t i = 0; i < ArrayLength(widgets) && widgets[i]; ++i) {
3679 // Visibility events are sent to the owning widget of the relevant
3680 // window but do not propagate to parent widgets so connect on
3681 // mShell (if it exists) as well as mContainer.
3682 g_signal_connect(widgets[i], "visibility-notify-event",
3683 G_CALLBACK(visibility_notify_event_cb), nullptr);
3684 // Similarly double buffering is controlled by the window's owning
3685 // widget. Disable double buffering for painting directly to the
3686 // X Window.
3687 gtk_widget_set_double_buffered(widgets[i], FALSE);
3688 }
3690 // We create input contexts for all containers, except for
3691 // toplevel popup windows
3692 if (mWindowType != eWindowType_popup) {
3693 mIMModule = new nsGtkIMModule(this);
3694 }
3695 } else if (!mIMModule) {
3696 nsWindow *container = GetContainerWindow();
3697 if (container) {
3698 mIMModule = container->mIMModule;
3699 }
3700 }
3702 if (eventWidget) {
3703 #if (MOZ_WIDGET_GTK == 2)
3704 // Don't let GTK mess with the shapes of our GdkWindows
3705 GTK_PRIVATE_SET_FLAG(eventWidget, GTK_HAS_SHAPE_MASK);
3706 #endif
3708 // These events are sent to the owning widget of the relevant window
3709 // and propagate up to the first widget that handles the events, so we
3710 // need only connect on mShell, if it exists, to catch events on its
3711 // window and windows of mContainer.
3712 g_signal_connect(eventWidget, "enter-notify-event",
3713 G_CALLBACK(enter_notify_event_cb), nullptr);
3714 g_signal_connect(eventWidget, "leave-notify-event",
3715 G_CALLBACK(leave_notify_event_cb), nullptr);
3716 g_signal_connect(eventWidget, "motion-notify-event",
3717 G_CALLBACK(motion_notify_event_cb), nullptr);
3718 g_signal_connect(eventWidget, "button-press-event",
3719 G_CALLBACK(button_press_event_cb), nullptr);
3720 g_signal_connect(eventWidget, "button-release-event",
3721 G_CALLBACK(button_release_event_cb), nullptr);
3722 g_signal_connect(eventWidget, "scroll-event",
3723 G_CALLBACK(scroll_event_cb), nullptr);
3724 }
3726 LOG(("nsWindow [%p]\n", (void *)this));
3727 if (mShell) {
3728 LOG(("\tmShell %p mContainer %p mGdkWindow %p 0x%lx\n",
3729 mShell, mContainer, mGdkWindow,
3730 gdk_x11_window_get_xid(mGdkWindow)));
3731 } else if (mContainer) {
3732 LOG(("\tmContainer %p mGdkWindow %p\n", mContainer, mGdkWindow));
3733 }
3734 else if (mGdkWindow) {
3735 LOG(("\tmGdkWindow %p parent %p\n",
3736 mGdkWindow, gdk_window_get_parent(mGdkWindow)));
3737 }
3739 // resize so that everything is set to the right dimensions
3740 if (!mIsTopLevel)
3741 Resize(mBounds.x, mBounds.y, mBounds.width, mBounds.height, false);
3743 return NS_OK;
3744 }
3746 NS_IMETHODIMP
3747 nsWindow::SetWindowClass(const nsAString &xulWinType)
3748 {
3749 if (!mShell)
3750 return NS_ERROR_FAILURE;
3752 const char *res_class = gdk_get_program_class();
3753 if (!res_class)
3754 return NS_ERROR_FAILURE;
3756 char *res_name = ToNewCString(xulWinType);
3757 if (!res_name)
3758 return NS_ERROR_OUT_OF_MEMORY;
3760 const char *role = nullptr;
3762 // Parse res_name into a name and role. Characters other than
3763 // [A-Za-z0-9_-] are converted to '_'. Anything after the first
3764 // colon is assigned to role; if there's no colon, assign the
3765 // whole thing to both role and res_name.
3766 for (char *c = res_name; *c; c++) {
3767 if (':' == *c) {
3768 *c = 0;
3769 role = c + 1;
3770 }
3771 else if (!isascii(*c) || (!isalnum(*c) && ('_' != *c) && ('-' != *c)))
3772 *c = '_';
3773 }
3774 res_name[0] = toupper(res_name[0]);
3775 if (!role) role = res_name;
3777 GdkWindow *shellWindow = gtk_widget_get_window(GTK_WIDGET(mShell));
3778 gdk_window_set_role(shellWindow, role);
3780 #ifdef MOZ_X11
3781 XClassHint *class_hint = XAllocClassHint();
3782 if (!class_hint) {
3783 nsMemory::Free(res_name);
3784 return NS_ERROR_OUT_OF_MEMORY;
3785 }
3786 class_hint->res_name = res_name;
3787 class_hint->res_class = const_cast<char*>(res_class);
3789 // Can't use gtk_window_set_wmclass() for this; it prints
3790 // a warning & refuses to make the change.
3791 XSetClassHint(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()),
3792 gdk_x11_window_get_xid(shellWindow),
3793 class_hint);
3794 XFree(class_hint);
3795 #endif /* MOZ_X11 */
3797 nsMemory::Free(res_name);
3799 return NS_OK;
3800 }
3802 void
3803 nsWindow::NativeResize(int32_t aWidth, int32_t aHeight, bool aRepaint)
3804 {
3805 LOG(("nsWindow::NativeResize [%p] %d %d\n", (void *)this,
3806 aWidth, aHeight));
3808 // clear our resize flag
3809 mNeedsResize = false;
3811 if (mIsTopLevel) {
3812 gtk_window_resize(GTK_WINDOW(mShell), aWidth, aHeight);
3813 }
3814 else if (mContainer) {
3815 GtkWidget *widget = GTK_WIDGET(mContainer);
3816 GtkAllocation allocation, prev_allocation;
3817 gtk_widget_get_allocation(widget, &prev_allocation);
3818 allocation.x = prev_allocation.x;
3819 allocation.y = prev_allocation.y;
3820 allocation.width = aWidth;
3821 allocation.height = aHeight;
3822 gtk_widget_size_allocate(widget, &allocation);
3823 }
3824 else if (mGdkWindow) {
3825 gdk_window_resize(mGdkWindow, aWidth, aHeight);
3826 }
3827 }
3829 void
3830 nsWindow::NativeResize(int32_t aX, int32_t aY,
3831 int32_t aWidth, int32_t aHeight,
3832 bool aRepaint)
3833 {
3834 mNeedsResize = false;
3835 mNeedsMove = false;
3837 LOG(("nsWindow::NativeResize [%p] %d %d %d %d\n", (void *)this,
3838 aX, aY, aWidth, aHeight));
3840 if (mIsTopLevel) {
3841 // aX and aY give the position of the window manager frame top-left.
3842 gtk_window_move(GTK_WINDOW(mShell), aX, aY);
3843 // This sets the client window size.
3844 gtk_window_resize(GTK_WINDOW(mShell), aWidth, aHeight);
3845 }
3846 else if (mContainer) {
3847 GtkAllocation allocation;
3848 allocation.x = aX;
3849 allocation.y = aY;
3850 allocation.width = aWidth;
3851 allocation.height = aHeight;
3852 gtk_widget_size_allocate(GTK_WIDGET(mContainer), &allocation);
3853 }
3854 else if (mGdkWindow) {
3855 gdk_window_move_resize(mGdkWindow, aX, aY, aWidth, aHeight);
3856 }
3857 }
3859 void
3860 nsWindow::NativeShow(bool aAction)
3861 {
3862 if (aAction) {
3863 // unset our flag now that our window has been shown
3864 mNeedsShow = false;
3866 if (mIsTopLevel) {
3867 // Set up usertime/startupID metadata for the created window.
3868 if (mWindowType != eWindowType_invisible) {
3869 SetUserTimeAndStartupIDForActivatedWindow(mShell);
3870 }
3872 gtk_widget_show(mShell);
3873 }
3874 else if (mContainer) {
3875 gtk_widget_show(GTK_WIDGET(mContainer));
3876 }
3877 else if (mGdkWindow) {
3878 gdk_window_show_unraised(mGdkWindow);
3879 }
3880 }
3881 else {
3882 if (mIsTopLevel) {
3883 gtk_widget_hide(GTK_WIDGET(mShell));
3885 ClearTransparencyBitmap(); // Release some resources
3886 }
3887 else if (mContainer) {
3888 gtk_widget_hide(GTK_WIDGET(mContainer));
3889 }
3890 else if (mGdkWindow) {
3891 gdk_window_hide(mGdkWindow);
3892 }
3893 }
3894 }
3896 void
3897 nsWindow::SetHasMappedToplevel(bool aState)
3898 {
3899 // Even when aState == mHasMappedToplevel (as when this method is called
3900 // from Show()), child windows need to have their state checked, so don't
3901 // return early.
3902 bool oldState = mHasMappedToplevel;
3903 mHasMappedToplevel = aState;
3905 // mHasMappedToplevel is not updated for children of windows that are
3906 // hidden; GDK knows not to send expose events for these windows. The
3907 // state is recorded on the hidden window itself, but, for child trees of
3908 // hidden windows, their state essentially becomes disconnected from their
3909 // hidden parent. When the hidden parent gets shown, the child trees are
3910 // reconnected, and the state of the window being shown can be easily
3911 // propagated.
3912 if (!mIsShown || !mGdkWindow)
3913 return;
3915 if (aState && !oldState && !mIsFullyObscured) {
3916 // GDK_EXPOSE events have been ignored but the window is now visible,
3917 // so make sure GDK doesn't think that the window has already been
3918 // painted.
3919 gdk_window_invalidate_rect(mGdkWindow, nullptr, FALSE);
3921 // Check that a grab didn't fail due to the window not being
3922 // viewable.
3923 EnsureGrabs();
3924 }
3926 for (GList *children = gdk_window_peek_children(mGdkWindow);
3927 children;
3928 children = children->next) {
3929 GdkWindow *gdkWin = GDK_WINDOW(children->data);
3930 nsWindow *child = get_window_for_gdk_window(gdkWin);
3932 if (child && child->mHasMappedToplevel != aState) {
3933 child->SetHasMappedToplevel(aState);
3934 }
3935 }
3936 }
3938 nsIntSize
3939 nsWindow::GetSafeWindowSize(nsIntSize aSize)
3940 {
3941 // The X protocol uses CARD32 for window sizes, but the server (1.11.3)
3942 // reads it as CARD16. Sizes of pixmaps, used for drawing, are (unsigned)
3943 // CARD16 in the protocol, but the server's ProcCreatePixmap returns
3944 // BadAlloc if dimensions cannot be represented by signed shorts.
3945 nsIntSize result = aSize;
3946 const int32_t kInt16Max = 32767;
3947 if (result.width > kInt16Max) {
3948 result.width = kInt16Max;
3949 }
3950 if (result.height > kInt16Max) {
3951 result.height = kInt16Max;
3952 }
3953 return result;
3954 }
3956 void
3957 nsWindow::EnsureGrabs(void)
3958 {
3959 if (mRetryPointerGrab)
3960 GrabPointer(sRetryGrabTime);
3961 }
3963 void
3964 nsWindow::CleanLayerManagerRecursive(void) {
3965 if (mLayerManager) {
3966 mLayerManager->Destroy();
3967 mLayerManager = nullptr;
3968 }
3970 DestroyCompositor();
3972 GList* children = gdk_window_peek_children(mGdkWindow);
3973 for (GList* list = children; list; list = list->next) {
3974 nsWindow* window = get_window_for_gdk_window(GDK_WINDOW(list->data));
3975 if (window) {
3976 window->CleanLayerManagerRecursive();
3977 }
3978 }
3979 }
3981 void
3982 nsWindow::SetTransparencyMode(nsTransparencyMode aMode)
3983 {
3984 if (!mShell) {
3985 // Pass the request to the toplevel window
3986 GtkWidget *topWidget = GetToplevelWidget();
3987 if (!topWidget)
3988 return;
3990 nsWindow *topWindow = get_window_for_gtk_widget(topWidget);
3991 if (!topWindow)
3992 return;
3994 topWindow->SetTransparencyMode(aMode);
3995 return;
3996 }
3997 bool isTransparent = aMode == eTransparencyTransparent;
3999 if (mIsTransparent == isTransparent)
4000 return;
4002 if (!isTransparent) {
4003 ClearTransparencyBitmap();
4004 } // else the new default alpha values are "all 1", so we don't
4005 // need to change anything yet
4007 mIsTransparent = isTransparent;
4009 // Need to clean our LayerManager up while still alive because
4010 // we don't want to use layers acceleration on shaped windows
4011 CleanLayerManagerRecursive();
4012 }
4014 nsTransparencyMode
4015 nsWindow::GetTransparencyMode()
4016 {
4017 if (!mShell) {
4018 // Pass the request to the toplevel window
4019 GtkWidget *topWidget = GetToplevelWidget();
4020 if (!topWidget) {
4021 return eTransparencyOpaque;
4022 }
4024 nsWindow *topWindow = get_window_for_gtk_widget(topWidget);
4025 if (!topWindow) {
4026 return eTransparencyOpaque;
4027 }
4029 return topWindow->GetTransparencyMode();
4030 }
4032 return mIsTransparent ? eTransparencyTransparent : eTransparencyOpaque;
4033 }
4035 nsresult
4036 nsWindow::ConfigureChildren(const nsTArray<Configuration>& aConfigurations)
4037 {
4038 for (uint32_t i = 0; i < aConfigurations.Length(); ++i) {
4039 const Configuration& configuration = aConfigurations[i];
4040 nsWindow* w = static_cast<nsWindow*>(configuration.mChild);
4041 NS_ASSERTION(w->GetParent() == this,
4042 "Configured widget is not a child");
4043 w->SetWindowClipRegion(configuration.mClipRegion, true);
4044 if (w->mBounds.Size() != configuration.mBounds.Size()) {
4045 w->Resize(configuration.mBounds.x, configuration.mBounds.y,
4046 configuration.mBounds.width, configuration.mBounds.height,
4047 true);
4048 } else if (w->mBounds.TopLeft() != configuration.mBounds.TopLeft()) {
4049 w->Move(configuration.mBounds.x, configuration.mBounds.y);
4050 }
4051 w->SetWindowClipRegion(configuration.mClipRegion, false);
4052 }
4053 return NS_OK;
4054 }
4056 static pixman_box32
4057 ToPixmanBox(const nsIntRect& aRect)
4058 {
4059 pixman_box32_t result;
4060 result.x1 = aRect.x;
4061 result.y1 = aRect.y;
4062 result.x2 = aRect.XMost();
4063 result.y2 = aRect.YMost();
4064 return result;
4065 }
4067 static nsIntRect
4068 ToIntRect(const pixman_box32& aBox)
4069 {
4070 nsIntRect result;
4071 result.x = aBox.x1;
4072 result.y = aBox.y1;
4073 result.width = aBox.x2 - aBox.x1;
4074 result.height = aBox.y2 - aBox.y1;
4075 return result;
4076 }
4078 static void
4079 InitRegion(pixman_region32* aRegion,
4080 const nsTArray<nsIntRect>& aRects)
4081 {
4082 nsAutoTArray<pixman_box32,10> rects;
4083 rects.SetCapacity(aRects.Length());
4084 for (uint32_t i = 0; i < aRects.Length (); ++i) {
4085 if (!aRects[i].IsEmpty()) {
4086 rects.AppendElement(ToPixmanBox(aRects[i]));
4087 }
4088 }
4090 pixman_region32_init_rects(aRegion,
4091 rects.Elements(), rects.Length());
4092 }
4094 static void
4095 GetIntRects(pixman_region32& aRegion, nsTArray<nsIntRect>* aRects)
4096 {
4097 int nRects;
4098 pixman_box32* boxes = pixman_region32_rectangles(&aRegion, &nRects);
4099 aRects->SetCapacity(aRects->Length() + nRects);
4100 for (int i = 0; i < nRects; ++i) {
4101 aRects->AppendElement(ToIntRect(boxes[i]));
4102 }
4103 }
4105 void
4106 nsWindow::SetWindowClipRegion(const nsTArray<nsIntRect>& aRects,
4107 bool aIntersectWithExisting)
4108 {
4109 const nsTArray<nsIntRect>* newRects = &aRects;
4111 nsAutoTArray<nsIntRect,1> intersectRects;
4112 if (aIntersectWithExisting) {
4113 nsAutoTArray<nsIntRect,1> existingRects;
4114 GetWindowClipRegion(&existingRects);
4116 nsAutoRef<pixman_region32> existingRegion;
4117 InitRegion(&existingRegion, existingRects);
4118 nsAutoRef<pixman_region32> newRegion;
4119 InitRegion(&newRegion, aRects);
4120 nsAutoRef<pixman_region32> intersectRegion;
4121 pixman_region32_init(&intersectRegion);
4122 pixman_region32_intersect(&intersectRegion,
4123 &newRegion, &existingRegion);
4125 // If mClipRects is null we haven't set a clip rect yet, so we
4126 // need to set the clip even if it is equal.
4127 if (mClipRects &&
4128 pixman_region32_equal(&intersectRegion, &existingRegion)) {
4129 return;
4130 }
4132 if (!pixman_region32_equal(&intersectRegion, &newRegion)) {
4133 GetIntRects(intersectRegion, &intersectRects);
4134 newRects = &intersectRects;
4135 }
4136 }
4138 if (!StoreWindowClipRegion(*newRects))
4139 return;
4141 if (!mGdkWindow)
4142 return;
4144 #if (MOZ_WIDGET_GTK == 2)
4145 GdkRegion *region = gdk_region_new(); // aborts on OOM
4146 for (uint32_t i = 0; i < newRects->Length(); ++i) {
4147 const nsIntRect& r = newRects->ElementAt(i);
4148 GdkRectangle rect = { r.x, r.y, r.width, r.height };
4149 gdk_region_union_with_rect(region, &rect);
4150 }
4152 gdk_window_shape_combine_region(mGdkWindow, region, 0, 0);
4153 gdk_region_destroy(region);
4154 #else
4155 cairo_region_t *region = cairo_region_create();
4156 for (uint32_t i = 0; i < newRects->Length(); ++i) {
4157 const nsIntRect& r = newRects->ElementAt(i);
4158 cairo_rectangle_int_t rect = { r.x, r.y, r.width, r.height };
4159 cairo_region_union_rectangle(region, &rect);
4160 }
4162 gdk_window_shape_combine_region(mGdkWindow, region, 0, 0);
4163 cairo_region_destroy(region);
4164 #endif
4166 return;
4167 }
4169 void
4170 nsWindow::ResizeTransparencyBitmap()
4171 {
4172 if (!mTransparencyBitmap)
4173 return;
4175 if (mBounds.width == mTransparencyBitmapWidth &&
4176 mBounds.height == mTransparencyBitmapHeight)
4177 return;
4179 int32_t newRowBytes = GetBitmapStride(mBounds.width);
4180 int32_t newSize = newRowBytes * mBounds.height;
4181 gchar* newBits = new gchar[newSize];
4182 // fill new mask with "transparent", first
4183 memset(newBits, 0, newSize);
4185 // Now copy the intersection of the old and new areas into the new mask
4186 int32_t copyWidth = std::min(mBounds.width, mTransparencyBitmapWidth);
4187 int32_t copyHeight = std::min(mBounds.height, mTransparencyBitmapHeight);
4188 int32_t oldRowBytes = GetBitmapStride(mTransparencyBitmapWidth);
4189 int32_t copyBytes = GetBitmapStride(copyWidth);
4191 int32_t i;
4192 gchar* fromPtr = mTransparencyBitmap;
4193 gchar* toPtr = newBits;
4194 for (i = 0; i < copyHeight; i++) {
4195 memcpy(toPtr, fromPtr, copyBytes);
4196 fromPtr += oldRowBytes;
4197 toPtr += newRowBytes;
4198 }
4200 delete[] mTransparencyBitmap;
4201 mTransparencyBitmap = newBits;
4202 mTransparencyBitmapWidth = mBounds.width;
4203 mTransparencyBitmapHeight = mBounds.height;
4204 }
4206 static bool
4207 ChangedMaskBits(gchar* aMaskBits, int32_t aMaskWidth, int32_t aMaskHeight,
4208 const nsIntRect& aRect, uint8_t* aAlphas, int32_t aStride)
4209 {
4210 int32_t x, y, xMax = aRect.XMost(), yMax = aRect.YMost();
4211 int32_t maskBytesPerRow = GetBitmapStride(aMaskWidth);
4212 for (y = aRect.y; y < yMax; y++) {
4213 gchar* maskBytes = aMaskBits + y*maskBytesPerRow;
4214 uint8_t* alphas = aAlphas;
4215 for (x = aRect.x; x < xMax; x++) {
4216 bool newBit = *alphas > 0x7f;
4217 alphas++;
4219 gchar maskByte = maskBytes[x >> 3];
4220 bool maskBit = (maskByte & (1 << (x & 7))) != 0;
4222 if (maskBit != newBit) {
4223 return true;
4224 }
4225 }
4226 aAlphas += aStride;
4227 }
4229 return false;
4230 }
4232 static
4233 void UpdateMaskBits(gchar* aMaskBits, int32_t aMaskWidth, int32_t aMaskHeight,
4234 const nsIntRect& aRect, uint8_t* aAlphas, int32_t aStride)
4235 {
4236 int32_t x, y, xMax = aRect.XMost(), yMax = aRect.YMost();
4237 int32_t maskBytesPerRow = GetBitmapStride(aMaskWidth);
4238 for (y = aRect.y; y < yMax; y++) {
4239 gchar* maskBytes = aMaskBits + y*maskBytesPerRow;
4240 uint8_t* alphas = aAlphas;
4241 for (x = aRect.x; x < xMax; x++) {
4242 bool newBit = *alphas > 0x7f;
4243 alphas++;
4245 gchar mask = 1 << (x & 7);
4246 gchar maskByte = maskBytes[x >> 3];
4247 // Note: '-newBit' turns 0 into 00...00 and 1 into 11...11
4248 maskBytes[x >> 3] = (maskByte & ~mask) | (-newBit & mask);
4249 }
4250 aAlphas += aStride;
4251 }
4252 }
4254 void
4255 nsWindow::ApplyTransparencyBitmap()
4256 {
4257 #ifdef MOZ_X11
4258 // We use X11 calls where possible, because GDK handles expose events
4259 // for shaped windows in a way that's incompatible with us (Bug 635903).
4260 // It doesn't occur when the shapes are set through X.
4261 GdkWindow *shellWindow = gtk_widget_get_window(mShell);
4262 Display* xDisplay = GDK_WINDOW_XDISPLAY(shellWindow);
4263 Window xDrawable = GDK_WINDOW_XID(shellWindow);
4264 Pixmap maskPixmap = XCreateBitmapFromData(xDisplay,
4265 xDrawable,
4266 mTransparencyBitmap,
4267 mTransparencyBitmapWidth,
4268 mTransparencyBitmapHeight);
4269 XShapeCombineMask(xDisplay, xDrawable,
4270 ShapeBounding, 0, 0,
4271 maskPixmap, ShapeSet);
4272 XFreePixmap(xDisplay, maskPixmap);
4273 #else
4274 #if (MOZ_WIDGET_GTK == 2)
4275 gtk_widget_reset_shapes(mShell);
4276 GdkBitmap* maskBitmap = gdk_bitmap_create_from_data(gtk_widget_get_window(mShell),
4277 mTransparencyBitmap,
4278 mTransparencyBitmapWidth, mTransparencyBitmapHeight);
4279 if (!maskBitmap)
4280 return;
4282 gtk_widget_shape_combine_mask(mShell, maskBitmap, 0, 0);
4283 g_object_unref(maskBitmap);
4284 #else
4285 cairo_surface_t *maskBitmap;
4286 maskBitmap = cairo_image_surface_create_for_data((unsigned char*)mTransparencyBitmap,
4287 CAIRO_FORMAT_A1,
4288 mTransparencyBitmapWidth,
4289 mTransparencyBitmapHeight,
4290 GetBitmapStride(mTransparencyBitmapWidth));
4291 if (!maskBitmap)
4292 return;
4294 cairo_region_t * maskRegion = gdk_cairo_region_create_from_surface(maskBitmap);
4295 gtk_widget_shape_combine_region(mShell, maskRegion);
4296 cairo_region_destroy(maskRegion);
4297 cairo_surface_destroy(maskBitmap);
4298 #endif // MOZ_WIDGET_GTK2
4299 #endif // MOZ_X11
4300 }
4302 void
4303 nsWindow::ClearTransparencyBitmap()
4304 {
4305 if (!mTransparencyBitmap)
4306 return;
4308 delete[] mTransparencyBitmap;
4309 mTransparencyBitmap = nullptr;
4310 mTransparencyBitmapWidth = 0;
4311 mTransparencyBitmapHeight = 0;
4313 if (!mShell)
4314 return;
4316 #ifdef MOZ_X11
4317 GdkWindow *window = gtk_widget_get_window(mShell);
4318 if (!window)
4319 return;
4321 Display* xDisplay = GDK_WINDOW_XDISPLAY(window);
4322 Window xWindow = gdk_x11_window_get_xid(window);
4324 XShapeCombineMask(xDisplay, xWindow, ShapeBounding, 0, 0, None, ShapeSet);
4325 #endif
4326 }
4328 nsresult
4329 nsWindow::UpdateTranslucentWindowAlphaInternal(const nsIntRect& aRect,
4330 uint8_t* aAlphas, int32_t aStride)
4331 {
4332 if (!mShell) {
4333 // Pass the request to the toplevel window
4334 GtkWidget *topWidget = GetToplevelWidget();
4335 if (!topWidget)
4336 return NS_ERROR_FAILURE;
4338 nsWindow *topWindow = get_window_for_gtk_widget(topWidget);
4339 if (!topWindow)
4340 return NS_ERROR_FAILURE;
4342 return topWindow->UpdateTranslucentWindowAlphaInternal(aRect, aAlphas, aStride);
4343 }
4345 NS_ASSERTION(mIsTransparent, "Window is not transparent");
4347 if (mTransparencyBitmap == nullptr) {
4348 int32_t size = GetBitmapStride(mBounds.width)*mBounds.height;
4349 mTransparencyBitmap = new gchar[size];
4350 memset(mTransparencyBitmap, 255, size);
4351 mTransparencyBitmapWidth = mBounds.width;
4352 mTransparencyBitmapHeight = mBounds.height;
4353 } else {
4354 ResizeTransparencyBitmap();
4355 }
4357 nsIntRect rect;
4358 rect.IntersectRect(aRect, nsIntRect(0, 0, mBounds.width, mBounds.height));
4360 if (!ChangedMaskBits(mTransparencyBitmap, mBounds.width, mBounds.height,
4361 rect, aAlphas, aStride))
4362 // skip the expensive stuff if the mask bits haven't changed; hopefully
4363 // this is the common case
4364 return NS_OK;
4366 UpdateMaskBits(mTransparencyBitmap, mBounds.width, mBounds.height,
4367 rect, aAlphas, aStride);
4369 if (!mNeedsShow) {
4370 ApplyTransparencyBitmap();
4371 }
4372 return NS_OK;
4373 }
4375 void
4376 nsWindow::GrabPointer(guint32 aTime)
4377 {
4378 LOG(("GrabPointer time=0x%08x retry=%d\n",
4379 (unsigned int)aTime, mRetryPointerGrab));
4381 mRetryPointerGrab = false;
4382 sRetryGrabTime = aTime;
4384 // If the window isn't visible, just set the flag to retry the
4385 // grab. When this window becomes visible, the grab will be
4386 // retried.
4387 if (!mHasMappedToplevel || mIsFullyObscured) {
4388 LOG(("GrabPointer: window not visible\n"));
4389 mRetryPointerGrab = true;
4390 return;
4391 }
4393 if (!mGdkWindow)
4394 return;
4396 gint retval;
4397 retval = gdk_pointer_grab(mGdkWindow, TRUE,
4398 (GdkEventMask)(GDK_BUTTON_PRESS_MASK |
4399 GDK_BUTTON_RELEASE_MASK |
4400 GDK_ENTER_NOTIFY_MASK |
4401 GDK_LEAVE_NOTIFY_MASK |
4402 GDK_POINTER_MOTION_MASK),
4403 (GdkWindow *)nullptr, nullptr, aTime);
4405 if (retval == GDK_GRAB_NOT_VIEWABLE) {
4406 LOG(("GrabPointer: window not viewable; will retry\n"));
4407 mRetryPointerGrab = true;
4408 } else if (retval != GDK_GRAB_SUCCESS) {
4409 LOG(("GrabPointer: pointer grab failed: %i\n", retval));
4410 // A failed grab indicates that another app has grabbed the pointer.
4411 // Check for rollup now, because, without the grab, we likely won't
4412 // get subsequent button press events.
4413 CheckForRollup(0, 0, false, true);
4414 }
4415 }
4417 void
4418 nsWindow::ReleaseGrabs(void)
4419 {
4420 LOG(("ReleaseGrabs\n"));
4422 mRetryPointerGrab = false;
4423 gdk_pointer_ungrab(GDK_CURRENT_TIME);
4424 }
4426 GtkWidget *
4427 nsWindow::GetToplevelWidget()
4428 {
4429 if (mShell) {
4430 return mShell;
4431 }
4433 GtkWidget *widget = GetMozContainerWidget();
4434 if (!widget)
4435 return nullptr;
4437 return gtk_widget_get_toplevel(widget);
4438 }
4440 GtkWidget *
4441 nsWindow::GetMozContainerWidget()
4442 {
4443 if (!mGdkWindow)
4444 return nullptr;
4446 if (mContainer)
4447 return GTK_WIDGET(mContainer);
4449 GtkWidget *owningWidget =
4450 get_gtk_widget_for_gdk_window(mGdkWindow);
4451 return owningWidget;
4452 }
4454 nsWindow *
4455 nsWindow::GetContainerWindow()
4456 {
4457 GtkWidget *owningWidget = GetMozContainerWidget();
4458 if (!owningWidget)
4459 return nullptr;
4461 nsWindow *window = get_window_for_gtk_widget(owningWidget);
4462 NS_ASSERTION(window, "No nsWindow for container widget");
4463 return window;
4464 }
4466 void
4467 nsWindow::SetUrgencyHint(GtkWidget *top_window, bool state)
4468 {
4469 if (!top_window)
4470 return;
4472 gdk_window_set_urgency_hint(gtk_widget_get_window(top_window), state);
4473 }
4475 void *
4476 nsWindow::SetupPluginPort(void)
4477 {
4478 if (!mGdkWindow)
4479 return nullptr;
4481 if (gdk_window_is_destroyed(mGdkWindow) == TRUE)
4482 return nullptr;
4484 Window window = gdk_x11_window_get_xid(mGdkWindow);
4486 // we have to flush the X queue here so that any plugins that
4487 // might be running on separate X connections will be able to use
4488 // this window in case it was just created
4489 #ifdef MOZ_X11
4490 XWindowAttributes xattrs;
4491 Display *display = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
4492 XGetWindowAttributes(display, window, &xattrs);
4493 XSelectInput (display, window,
4494 xattrs.your_event_mask |
4495 SubstructureNotifyMask);
4497 gdk_window_add_filter(mGdkWindow, plugin_window_filter_func, this);
4499 XSync(display, False);
4500 #endif /* MOZ_X11 */
4502 return (void *)window;
4503 }
4505 void
4506 nsWindow::SetDefaultIcon(void)
4507 {
4508 SetIcon(NS_LITERAL_STRING("default"));
4509 }
4511 void
4512 nsWindow::SetPluginType(PluginType aPluginType)
4513 {
4514 mPluginType = aPluginType;
4515 }
4517 #ifdef MOZ_X11
4518 void
4519 nsWindow::SetNonXEmbedPluginFocus()
4520 {
4521 if (gPluginFocusWindow == this || mPluginType!=PluginType_NONXEMBED) {
4522 return;
4523 }
4525 if (gPluginFocusWindow) {
4526 nsRefPtr<nsWindow> kungFuDeathGrip = gPluginFocusWindow;
4527 gPluginFocusWindow->LoseNonXEmbedPluginFocus();
4528 }
4530 LOGFOCUS(("nsWindow::SetNonXEmbedPluginFocus\n"));
4532 Window curFocusWindow;
4533 int focusState;
4535 GdkDisplay *gdkDisplay = gdk_window_get_display(mGdkWindow);
4536 XGetInputFocus(gdk_x11_display_get_xdisplay(gdkDisplay),
4537 &curFocusWindow,
4538 &focusState);
4540 LOGFOCUS(("\t curFocusWindow=%p\n", curFocusWindow));
4542 GdkWindow* toplevel = gdk_window_get_toplevel(mGdkWindow);
4543 #if (MOZ_WIDGET_GTK == 2)
4544 GdkWindow *gdkfocuswin = gdk_window_lookup(curFocusWindow);
4545 #else
4546 GdkWindow *gdkfocuswin = gdk_x11_window_lookup_for_display(gdkDisplay,
4547 curFocusWindow);
4548 #endif
4550 // lookup with the focus proxy window is supposed to get the
4551 // same GdkWindow as toplevel. If the current focused window
4552 // is not the focus proxy, we return without any change.
4553 if (gdkfocuswin != toplevel) {
4554 return;
4555 }
4557 // switch the focus from the focus proxy to the plugin window
4558 mOldFocusWindow = curFocusWindow;
4559 XRaiseWindow(GDK_WINDOW_XDISPLAY(mGdkWindow),
4560 gdk_x11_window_get_xid(mGdkWindow));
4561 gdk_error_trap_push();
4562 XSetInputFocus(GDK_WINDOW_XDISPLAY(mGdkWindow),
4563 gdk_x11_window_get_xid(mGdkWindow),
4564 RevertToNone,
4565 CurrentTime);
4566 gdk_flush();
4567 gdk_error_trap_pop();
4568 gPluginFocusWindow = this;
4569 gdk_window_add_filter(nullptr, plugin_client_message_filter, this);
4571 LOGFOCUS(("nsWindow::SetNonXEmbedPluginFocus oldfocus=%p new=%p\n",
4572 mOldFocusWindow, gdk_x11_window_get_xid(mGdkWindow)));
4573 }
4575 void
4576 nsWindow::LoseNonXEmbedPluginFocus()
4577 {
4578 LOGFOCUS(("nsWindow::LoseNonXEmbedPluginFocus\n"));
4580 // This method is only for the nsWindow which contains a
4581 // Non-XEmbed plugin, for example, JAVA plugin.
4582 if (gPluginFocusWindow != this || mPluginType!=PluginType_NONXEMBED) {
4583 return;
4584 }
4586 Window curFocusWindow;
4587 int focusState;
4589 XGetInputFocus(GDK_WINDOW_XDISPLAY(mGdkWindow),
4590 &curFocusWindow,
4591 &focusState);
4593 // we only switch focus between plugin window and focus proxy. If the
4594 // current focused window is not the plugin window, just removing the
4595 // event filter that blocks the WM_TAKE_FOCUS is enough. WM and gtk2
4596 // will take care of the focus later.
4597 if (!curFocusWindow ||
4598 curFocusWindow == gdk_x11_window_get_xid(mGdkWindow)) {
4600 gdk_error_trap_push();
4601 XRaiseWindow(GDK_WINDOW_XDISPLAY(mGdkWindow),
4602 mOldFocusWindow);
4603 XSetInputFocus(GDK_WINDOW_XDISPLAY(mGdkWindow),
4604 mOldFocusWindow,
4605 RevertToParent,
4606 CurrentTime);
4607 gdk_flush();
4608 gdk_error_trap_pop();
4609 }
4610 gPluginFocusWindow = nullptr;
4611 mOldFocusWindow = 0;
4612 gdk_window_remove_filter(nullptr, plugin_client_message_filter, this);
4614 LOGFOCUS(("nsWindow::LoseNonXEmbedPluginFocus end\n"));
4615 }
4616 #endif /* MOZ_X11 */
4618 gint
4619 nsWindow::ConvertBorderStyles(nsBorderStyle aStyle)
4620 {
4621 gint w = 0;
4623 if (aStyle == eBorderStyle_default)
4624 return -1;
4626 // note that we don't handle eBorderStyle_close yet
4627 if (aStyle & eBorderStyle_all)
4628 w |= GDK_DECOR_ALL;
4629 if (aStyle & eBorderStyle_border)
4630 w |= GDK_DECOR_BORDER;
4631 if (aStyle & eBorderStyle_resizeh)
4632 w |= GDK_DECOR_RESIZEH;
4633 if (aStyle & eBorderStyle_title)
4634 w |= GDK_DECOR_TITLE;
4635 if (aStyle & eBorderStyle_menu)
4636 w |= GDK_DECOR_MENU;
4637 if (aStyle & eBorderStyle_minimize)
4638 w |= GDK_DECOR_MINIMIZE;
4639 if (aStyle & eBorderStyle_maximize)
4640 w |= GDK_DECOR_MAXIMIZE;
4642 return w;
4643 }
4645 NS_IMETHODIMP
4646 nsWindow::MakeFullScreen(bool aFullScreen)
4647 {
4648 LOG(("nsWindow::MakeFullScreen [%p] aFullScreen %d\n",
4649 (void *)this, aFullScreen));
4651 if (aFullScreen) {
4652 if (mSizeMode != nsSizeMode_Fullscreen)
4653 mLastSizeMode = mSizeMode;
4655 mSizeMode = nsSizeMode_Fullscreen;
4656 gtk_window_fullscreen(GTK_WINDOW(mShell));
4657 }
4658 else {
4659 mSizeMode = mLastSizeMode;
4660 gtk_window_unfullscreen(GTK_WINDOW(mShell));
4661 }
4663 NS_ASSERTION(mLastSizeMode != nsSizeMode_Fullscreen,
4664 "mLastSizeMode should never be fullscreen");
4665 return NS_OK;
4666 }
4668 NS_IMETHODIMP
4669 nsWindow::HideWindowChrome(bool aShouldHide)
4670 {
4671 if (!mShell) {
4672 // Pass the request to the toplevel window
4673 GtkWidget *topWidget = GetToplevelWidget();
4674 if (!topWidget)
4675 return NS_ERROR_FAILURE;
4677 nsWindow *topWindow = get_window_for_gtk_widget(topWidget);
4678 if (!topWindow)
4679 return NS_ERROR_FAILURE;
4681 return topWindow->HideWindowChrome(aShouldHide);
4682 }
4684 // Sawfish, metacity, and presumably other window managers get
4685 // confused if we change the window decorations while the window
4686 // is visible.
4687 bool wasVisible = false;
4688 GdkWindow *shellWindow = gtk_widget_get_window(mShell);
4689 if (gdk_window_is_visible(shellWindow)) {
4690 gdk_window_hide(shellWindow);
4691 wasVisible = true;
4692 }
4694 gint wmd;
4695 if (aShouldHide)
4696 wmd = 0;
4697 else
4698 wmd = ConvertBorderStyles(mBorderStyle);
4700 if (wmd != -1)
4701 gdk_window_set_decorations(shellWindow, (GdkWMDecoration) wmd);
4703 if (wasVisible)
4704 gdk_window_show(shellWindow);
4706 // For some window managers, adding or removing window decorations
4707 // requires unmapping and remapping our toplevel window. Go ahead
4708 // and flush the queue here so that we don't end up with a BadWindow
4709 // error later when this happens (when the persistence timer fires
4710 // and GetWindowPos is called)
4711 #ifdef MOZ_X11
4712 XSync(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()) , False);
4713 #else
4714 gdk_flush ();
4715 #endif /* MOZ_X11 */
4717 return NS_OK;
4718 }
4720 bool
4721 nsWindow::CheckForRollup(gdouble aMouseX, gdouble aMouseY,
4722 bool aIsWheel, bool aAlwaysRollup)
4723 {
4724 nsIRollupListener* rollupListener = GetActiveRollupListener();
4725 nsCOMPtr<nsIWidget> rollupWidget;
4726 if (rollupListener) {
4727 rollupWidget = rollupListener->GetRollupWidget();
4728 }
4729 if (!rollupWidget) {
4730 nsBaseWidget::gRollupListener = nullptr;
4731 return false;
4732 }
4734 bool retVal = false;
4735 GdkWindow *currentPopup =
4736 (GdkWindow *)rollupWidget->GetNativeData(NS_NATIVE_WINDOW);
4737 if (aAlwaysRollup || !is_mouse_in_window(currentPopup, aMouseX, aMouseY)) {
4738 bool rollup = true;
4739 if (aIsWheel) {
4740 rollup = rollupListener->ShouldRollupOnMouseWheelEvent();
4741 retVal = rollupListener->ShouldConsumeOnMouseWheelEvent();
4742 }
4743 // if we're dealing with menus, we probably have submenus and
4744 // we don't want to rollup if the click is in a parent menu of
4745 // the current submenu
4746 uint32_t popupsToRollup = UINT32_MAX;
4747 if (!aAlwaysRollup) {
4748 nsAutoTArray<nsIWidget*, 5> widgetChain;
4749 uint32_t sameTypeCount = rollupListener->GetSubmenuWidgetChain(&widgetChain);
4750 for (uint32_t i=0; i<widgetChain.Length(); ++i) {
4751 nsIWidget* widget = widgetChain[i];
4752 GdkWindow* currWindow =
4753 (GdkWindow*) widget->GetNativeData(NS_NATIVE_WINDOW);
4754 if (is_mouse_in_window(currWindow, aMouseX, aMouseY)) {
4755 // don't roll up if the mouse event occurred within a
4756 // menu of the same type. If the mouse event occurred
4757 // in a menu higher than that, roll up, but pass the
4758 // number of popups to Rollup so that only those of the
4759 // same type close up.
4760 if (i < sameTypeCount) {
4761 rollup = false;
4762 }
4763 else {
4764 popupsToRollup = sameTypeCount;
4765 }
4766 break;
4767 }
4768 } // foreach parent menu widget
4769 } // if rollup listener knows about menus
4771 // if we've determined that we should still rollup, do it.
4772 bool usePoint = !aIsWheel && !aAlwaysRollup;
4773 nsIntPoint point(aMouseX, aMouseY);
4774 if (rollup && rollupListener->Rollup(popupsToRollup, usePoint ? &point : nullptr, nullptr)) {
4775 retVal = true;
4776 }
4777 }
4778 return retVal;
4779 }
4781 /* static */
4782 bool
4783 nsWindow::DragInProgress(void)
4784 {
4785 nsCOMPtr<nsIDragService> dragService = do_GetService(kCDragServiceCID);
4787 if (!dragService)
4788 return false;
4790 nsCOMPtr<nsIDragSession> currentDragSession;
4791 dragService->GetCurrentSession(getter_AddRefs(currentDragSession));
4793 return currentDragSession != nullptr;
4794 }
4796 static bool
4797 is_mouse_in_window (GdkWindow* aWindow, gdouble aMouseX, gdouble aMouseY)
4798 {
4799 gint x = 0;
4800 gint y = 0;
4801 gint w, h;
4803 gint offsetX = 0;
4804 gint offsetY = 0;
4806 GdkWindow *window = aWindow;
4808 while (window) {
4809 gint tmpX = 0;
4810 gint tmpY = 0;
4812 gdk_window_get_position(window, &tmpX, &tmpY);
4813 GtkWidget *widget = get_gtk_widget_for_gdk_window(window);
4815 // if this is a window, compute x and y given its origin and our
4816 // offset
4817 if (GTK_IS_WINDOW(widget)) {
4818 x = tmpX + offsetX;
4819 y = tmpY + offsetY;
4820 break;
4821 }
4823 offsetX += tmpX;
4824 offsetY += tmpY;
4825 window = gdk_window_get_parent(window);
4826 }
4828 #if (MOZ_WIDGET_GTK == 2)
4829 gdk_drawable_get_size(aWindow, &w, &h);
4830 #else
4831 w = gdk_window_get_width(aWindow);
4832 h = gdk_window_get_height(aWindow);
4833 #endif
4835 if (aMouseX > x && aMouseX < x + w &&
4836 aMouseY > y && aMouseY < y + h)
4837 return true;
4839 return false;
4840 }
4842 static nsWindow *
4843 get_window_for_gtk_widget(GtkWidget *widget)
4844 {
4845 gpointer user_data = g_object_get_data(G_OBJECT(widget), "nsWindow");
4847 return static_cast<nsWindow *>(user_data);
4848 }
4850 static nsWindow *
4851 get_window_for_gdk_window(GdkWindow *window)
4852 {
4853 gpointer user_data = g_object_get_data(G_OBJECT(window), "nsWindow");
4855 return static_cast<nsWindow *>(user_data);
4856 }
4858 static GtkWidget *
4859 get_gtk_widget_for_gdk_window(GdkWindow *window)
4860 {
4861 gpointer user_data = nullptr;
4862 gdk_window_get_user_data(window, &user_data);
4864 return GTK_WIDGET(user_data);
4865 }
4867 static GdkCursor *
4868 get_gtk_cursor(nsCursor aCursor)
4869 {
4870 GdkCursor *gdkcursor = nullptr;
4871 uint8_t newType = 0xff;
4873 if ((gdkcursor = gCursorCache[aCursor])) {
4874 return gdkcursor;
4875 }
4877 GdkDisplay *defaultDisplay = gdk_display_get_default();
4879 // The strategy here is to use standard GDK cursors, and, if not available,
4880 // load by standard name with gdk_cursor_new_from_name.
4881 // Spec is here: http://www.freedesktop.org/wiki/Specifications/cursor-spec/
4882 switch (aCursor) {
4883 case eCursor_standard:
4884 gdkcursor = gdk_cursor_new(GDK_LEFT_PTR);
4885 break;
4886 case eCursor_wait:
4887 gdkcursor = gdk_cursor_new(GDK_WATCH);
4888 break;
4889 case eCursor_select:
4890 gdkcursor = gdk_cursor_new(GDK_XTERM);
4891 break;
4892 case eCursor_hyperlink:
4893 gdkcursor = gdk_cursor_new(GDK_HAND2);
4894 break;
4895 case eCursor_n_resize:
4896 gdkcursor = gdk_cursor_new(GDK_TOP_SIDE);
4897 break;
4898 case eCursor_s_resize:
4899 gdkcursor = gdk_cursor_new(GDK_BOTTOM_SIDE);
4900 break;
4901 case eCursor_w_resize:
4902 gdkcursor = gdk_cursor_new(GDK_LEFT_SIDE);
4903 break;
4904 case eCursor_e_resize:
4905 gdkcursor = gdk_cursor_new(GDK_RIGHT_SIDE);
4906 break;
4907 case eCursor_nw_resize:
4908 gdkcursor = gdk_cursor_new(GDK_TOP_LEFT_CORNER);
4909 break;
4910 case eCursor_se_resize:
4911 gdkcursor = gdk_cursor_new(GDK_BOTTOM_RIGHT_CORNER);
4912 break;
4913 case eCursor_ne_resize:
4914 gdkcursor = gdk_cursor_new(GDK_TOP_RIGHT_CORNER);
4915 break;
4916 case eCursor_sw_resize:
4917 gdkcursor = gdk_cursor_new(GDK_BOTTOM_LEFT_CORNER);
4918 break;
4919 case eCursor_crosshair:
4920 gdkcursor = gdk_cursor_new(GDK_CROSSHAIR);
4921 break;
4922 case eCursor_move:
4923 gdkcursor = gdk_cursor_new(GDK_FLEUR);
4924 break;
4925 case eCursor_help:
4926 gdkcursor = gdk_cursor_new(GDK_QUESTION_ARROW);
4927 break;
4928 case eCursor_copy: // CSS3
4929 gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "copy");
4930 if (!gdkcursor)
4931 newType = MOZ_CURSOR_COPY;
4932 break;
4933 case eCursor_alias:
4934 gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "alias");
4935 if (!gdkcursor)
4936 newType = MOZ_CURSOR_ALIAS;
4937 break;
4938 case eCursor_context_menu:
4939 gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "context-menu");
4940 if (!gdkcursor)
4941 newType = MOZ_CURSOR_CONTEXT_MENU;
4942 break;
4943 case eCursor_cell:
4944 gdkcursor = gdk_cursor_new(GDK_PLUS);
4945 break;
4946 // Those two aren’t standardized. Trying both KDE’s and GNOME’s names
4947 case eCursor_grab:
4948 gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "openhand");
4949 if (!gdkcursor)
4950 gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "hand1");
4951 if (!gdkcursor)
4952 newType = MOZ_CURSOR_HAND_GRAB;
4953 break;
4954 case eCursor_grabbing:
4955 gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "closedhand");
4956 if (!gdkcursor)
4957 gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "grabbing");
4958 if (!gdkcursor)
4959 newType = MOZ_CURSOR_HAND_GRABBING;
4960 break;
4961 case eCursor_spinning:
4962 gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "progress");
4963 if (!gdkcursor)
4964 newType = MOZ_CURSOR_SPINNING;
4965 break;
4966 case eCursor_zoom_in:
4967 newType = MOZ_CURSOR_ZOOM_IN;
4968 break;
4969 case eCursor_zoom_out:
4970 newType = MOZ_CURSOR_ZOOM_OUT;
4971 break;
4972 case eCursor_not_allowed:
4973 gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "not-allowed");
4974 if (!gdkcursor) // nonstandard, yet common
4975 gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "crossed_circle");
4976 if (!gdkcursor)
4977 newType = MOZ_CURSOR_NOT_ALLOWED;
4978 break;
4979 case eCursor_no_drop:
4980 gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "no-drop");
4981 if (!gdkcursor) // this nonstandard sequence makes it work on KDE and GNOME
4982 gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "forbidden");
4983 if (!gdkcursor)
4984 gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "circle");
4985 if (!gdkcursor)
4986 newType = MOZ_CURSOR_NOT_ALLOWED;
4987 break;
4988 case eCursor_vertical_text:
4989 newType = MOZ_CURSOR_VERTICAL_TEXT;
4990 break;
4991 case eCursor_all_scroll:
4992 gdkcursor = gdk_cursor_new(GDK_FLEUR);
4993 break;
4994 case eCursor_nesw_resize:
4995 gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "size_bdiag");
4996 if (!gdkcursor)
4997 newType = MOZ_CURSOR_NESW_RESIZE;
4998 break;
4999 case eCursor_nwse_resize:
5000 gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "size_fdiag");
5001 if (!gdkcursor)
5002 newType = MOZ_CURSOR_NWSE_RESIZE;
5003 break;
5004 case eCursor_ns_resize:
5005 gdkcursor = gdk_cursor_new(GDK_SB_V_DOUBLE_ARROW);
5006 break;
5007 case eCursor_ew_resize:
5008 gdkcursor = gdk_cursor_new(GDK_SB_H_DOUBLE_ARROW);
5009 break;
5010 // Here, two better fitting cursors exist in some cursor themes. Try those first
5011 case eCursor_row_resize:
5012 gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "split_v");
5013 if (!gdkcursor)
5014 gdkcursor = gdk_cursor_new(GDK_SB_V_DOUBLE_ARROW);
5015 break;
5016 case eCursor_col_resize:
5017 gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "split_h");
5018 if (!gdkcursor)
5019 gdkcursor = gdk_cursor_new(GDK_SB_H_DOUBLE_ARROW);
5020 break;
5021 case eCursor_none:
5022 newType = MOZ_CURSOR_NONE;
5023 break;
5024 default:
5025 NS_ASSERTION(aCursor, "Invalid cursor type");
5026 gdkcursor = gdk_cursor_new(GDK_LEFT_PTR);
5027 break;
5028 }
5030 // If by now we don't have a xcursor, this means we have to make a custom
5031 // one. First, we try creating a named cursor based on the hash of our
5032 // custom bitmap, as libXcursor has some magic to convert bitmapped cursors
5033 // to themed cursors
5034 if (newType != 0xFF && GtkCursors[newType].hash) {
5035 gdkcursor = gdk_cursor_new_from_name(defaultDisplay, GtkCursors[newType].hash);
5036 }
5038 // If we still don't have a xcursor, we now really create a bitmap cursor
5039 if (newType != 0xff && !gdkcursor) {
5040 GdkPixbuf * cursor_pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, 32, 32);
5041 if (!cursor_pixbuf)
5042 return nullptr;
5044 guchar *data = gdk_pixbuf_get_pixels(cursor_pixbuf);
5046 // Read data from GtkCursors and compose RGBA surface from 1bit bitmap and mask
5047 // GtkCursors bits and mask are 32x32 monochrome bitmaps (1 bit for each pixel)
5048 // so it's 128 byte array (4 bytes for are one bitmap row and there are 32 rows here).
5049 const unsigned char *bits = GtkCursors[newType].bits;
5050 const unsigned char *mask_bits = GtkCursors[newType].mask_bits;
5052 for (int i = 0; i < 128; i++) {
5053 char bit = *bits++;
5054 char mask = *mask_bits++;
5055 for (int j = 0; j < 8; j++) {
5056 unsigned char pix = ~(((bit >> j) & 0x01) * 0xff);
5057 *data++ = pix;
5058 *data++ = pix;
5059 *data++ = pix;
5060 *data++ = (((mask >> j) & 0x01) * 0xff);
5061 }
5062 }
5064 gdkcursor = gdk_cursor_new_from_pixbuf(gdk_display_get_default(), cursor_pixbuf,
5065 GtkCursors[newType].hot_x,
5066 GtkCursors[newType].hot_y);
5068 g_object_unref(cursor_pixbuf);
5069 }
5071 gCursorCache[aCursor] = gdkcursor;
5073 return gdkcursor;
5074 }
5076 // gtk callbacks
5078 #if (MOZ_WIDGET_GTK == 2)
5079 static gboolean
5080 expose_event_cb(GtkWidget *widget, GdkEventExpose *event)
5081 {
5082 nsRefPtr<nsWindow> window = get_window_for_gdk_window(event->window);
5083 if (!window)
5084 return FALSE;
5086 window->OnExposeEvent(event);
5087 return FALSE;
5088 }
5089 #else
5090 void
5091 draw_window_of_widget(GtkWidget *widget, GdkWindow *aWindow, cairo_t *cr)
5092 {
5093 if (gtk_cairo_should_draw_window(cr, aWindow)) {
5094 nsRefPtr<nsWindow> window = get_window_for_gdk_window(aWindow);
5095 if (!window) {
5096 NS_WARNING("Cannot get nsWindow from GtkWidget");
5097 }
5098 else {
5099 cairo_save(cr);
5100 gtk_cairo_transform_to_window(cr, widget, aWindow);
5101 // TODO - window->OnExposeEvent() can destroy this or other windows,
5102 // do we need to handle it somehow?
5103 window->OnExposeEvent(cr);
5104 cairo_restore(cr);
5105 }
5106 }
5108 GList *children = gdk_window_get_children(aWindow);
5109 GList *child = children;
5110 while (child) {
5111 GdkWindow *window = GDK_WINDOW(child->data);
5112 gpointer windowWidget;
5113 gdk_window_get_user_data(window, &windowWidget);
5114 if (windowWidget == widget) {
5115 draw_window_of_widget(widget, window, cr);
5116 }
5117 child = g_list_next(child);
5118 }
5119 g_list_free(children);
5120 }
5122 /* static */
5123 gboolean
5124 expose_event_cb(GtkWidget *widget, cairo_t *cr)
5125 {
5126 draw_window_of_widget(widget, gtk_widget_get_window(widget), cr);
5127 return FALSE;
5128 }
5129 #endif //MOZ_WIDGET_GTK2
5131 static gboolean
5132 configure_event_cb(GtkWidget *widget,
5133 GdkEventConfigure *event)
5134 {
5135 nsRefPtr<nsWindow> window = get_window_for_gtk_widget(widget);
5136 if (!window)
5137 return FALSE;
5139 return window->OnConfigureEvent(widget, event);
5140 }
5142 static void
5143 container_unrealize_cb (GtkWidget *widget)
5144 {
5145 nsRefPtr<nsWindow> window = get_window_for_gtk_widget(widget);
5146 if (!window)
5147 return;
5149 window->OnContainerUnrealize();
5150 }
5152 static void
5153 size_allocate_cb (GtkWidget *widget, GtkAllocation *allocation)
5154 {
5155 nsRefPtr<nsWindow> window = get_window_for_gtk_widget(widget);
5156 if (!window)
5157 return;
5159 window->OnSizeAllocate(allocation);
5160 }
5162 static gboolean
5163 delete_event_cb(GtkWidget *widget, GdkEventAny *event)
5164 {
5165 nsRefPtr<nsWindow> window = get_window_for_gtk_widget(widget);
5166 if (!window)
5167 return FALSE;
5169 window->OnDeleteEvent();
5171 return TRUE;
5172 }
5174 static gboolean
5175 enter_notify_event_cb(GtkWidget *widget,
5176 GdkEventCrossing *event)
5177 {
5178 nsRefPtr<nsWindow> window = get_window_for_gdk_window(event->window);
5179 if (!window)
5180 return TRUE;
5182 window->OnEnterNotifyEvent(event);
5184 return TRUE;
5185 }
5187 static gboolean
5188 leave_notify_event_cb(GtkWidget *widget,
5189 GdkEventCrossing *event)
5190 {
5191 if (is_parent_grab_leave(event)) {
5192 return TRUE;
5193 }
5195 // bug 369599: Suppress LeaveNotify events caused by pointer grabs to
5196 // avoid generating spurious mouse exit events.
5197 gint x = gint(event->x_root);
5198 gint y = gint(event->y_root);
5199 GdkDisplay* display = gtk_widget_get_display(widget);
5200 GdkWindow* winAtPt = gdk_display_get_window_at_pointer(display, &x, &y);
5201 if (winAtPt == event->window) {
5202 return TRUE;
5203 }
5205 nsRefPtr<nsWindow> window = get_window_for_gdk_window(event->window);
5206 if (!window)
5207 return TRUE;
5209 window->OnLeaveNotifyEvent(event);
5211 return TRUE;
5212 }
5214 static nsWindow*
5215 GetFirstNSWindowForGDKWindow(GdkWindow *aGdkWindow)
5216 {
5217 nsWindow* window;
5218 while (!(window = get_window_for_gdk_window(aGdkWindow))) {
5219 // The event has bubbled to the moz_container widget as passed into each caller's *widget parameter,
5220 // but its corresponding nsWindow is an ancestor of the window that we need. Instead, look at
5221 // event->window and find the first ancestor nsWindow of it because event->window may be in a plugin.
5222 aGdkWindow = gdk_window_get_parent(aGdkWindow);
5223 if (!aGdkWindow) {
5224 window = nullptr;
5225 break;
5226 }
5227 }
5228 return window;
5229 }
5231 static gboolean
5232 motion_notify_event_cb(GtkWidget *widget, GdkEventMotion *event)
5233 {
5234 UpdateLastInputEventTime(event);
5236 nsWindow *window = GetFirstNSWindowForGDKWindow(event->window);
5237 if (!window)
5238 return FALSE;
5240 window->OnMotionNotifyEvent(event);
5242 return TRUE;
5243 }
5245 static gboolean
5246 button_press_event_cb(GtkWidget *widget, GdkEventButton *event)
5247 {
5248 UpdateLastInputEventTime(event);
5250 nsWindow *window = GetFirstNSWindowForGDKWindow(event->window);
5251 if (!window)
5252 return FALSE;
5254 window->OnButtonPressEvent(event);
5256 return TRUE;
5257 }
5259 static gboolean
5260 button_release_event_cb(GtkWidget *widget, GdkEventButton *event)
5261 {
5262 UpdateLastInputEventTime(event);
5264 nsWindow *window = GetFirstNSWindowForGDKWindow(event->window);
5265 if (!window)
5266 return FALSE;
5268 window->OnButtonReleaseEvent(event);
5270 return TRUE;
5271 }
5273 static gboolean
5274 focus_in_event_cb(GtkWidget *widget, GdkEventFocus *event)
5275 {
5276 nsRefPtr<nsWindow> window = get_window_for_gtk_widget(widget);
5277 if (!window)
5278 return FALSE;
5280 window->OnContainerFocusInEvent(event);
5282 return FALSE;
5283 }
5285 static gboolean
5286 focus_out_event_cb(GtkWidget *widget, GdkEventFocus *event)
5287 {
5288 nsRefPtr<nsWindow> window = get_window_for_gtk_widget(widget);
5289 if (!window)
5290 return FALSE;
5292 window->OnContainerFocusOutEvent(event);
5294 return FALSE;
5295 }
5297 #ifdef MOZ_X11
5298 // For long-lived popup windows that don't really take focus themselves but
5299 // may have elements that accept keyboard input when the parent window is
5300 // active, focus is handled specially. These windows include noautohide
5301 // panels. (This special handling is not necessary for temporary popups where
5302 // the keyboard is grabbed.)
5303 //
5304 // Mousing over or clicking on these windows should not cause them to steal
5305 // focus from their parent windows, so, the input field of WM_HINTS is set to
5306 // False to request that the window manager not set the input focus to this
5307 // window. http://tronche.com/gui/x/icccm/sec-4.html#s-4.1.7
5308 //
5309 // However, these windows can still receive WM_TAKE_FOCUS messages from the
5310 // window manager, so they can still detect when the user has indicated that
5311 // they wish to direct keyboard input at these windows. When the window
5312 // manager offers focus to these windows (after a mouse over or click, for
5313 // example), a request to make the parent window active is issued. When the
5314 // parent window becomes active, keyboard events will be received.
5316 static GdkFilterReturn
5317 popup_take_focus_filter(GdkXEvent *gdk_xevent,
5318 GdkEvent *event,
5319 gpointer data)
5320 {
5321 XEvent* xevent = static_cast<XEvent*>(gdk_xevent);
5322 if (xevent->type != ClientMessage)
5323 return GDK_FILTER_CONTINUE;
5325 XClientMessageEvent& xclient = xevent->xclient;
5326 if (xclient.message_type != gdk_x11_get_xatom_by_name("WM_PROTOCOLS"))
5327 return GDK_FILTER_CONTINUE;
5329 Atom atom = xclient.data.l[0];
5330 if (atom != gdk_x11_get_xatom_by_name("WM_TAKE_FOCUS"))
5331 return GDK_FILTER_CONTINUE;
5333 guint32 timestamp = xclient.data.l[1];
5335 GtkWidget* widget = get_gtk_widget_for_gdk_window(event->any.window);
5336 if (!widget)
5337 return GDK_FILTER_CONTINUE;
5339 GtkWindow* parent = gtk_window_get_transient_for(GTK_WINDOW(widget));
5340 if (!parent)
5341 return GDK_FILTER_CONTINUE;
5343 if (gtk_window_is_active(parent))
5344 return GDK_FILTER_REMOVE; // leave input focus on the parent
5346 GdkWindow* parent_window = gtk_widget_get_window(GTK_WIDGET(parent));
5347 if (!parent_window)
5348 return GDK_FILTER_CONTINUE;
5350 // In case the parent has not been deconified.
5351 gdk_window_show_unraised(parent_window);
5353 // Request focus on the parent window.
5354 // Use gdk_window_focus rather than gtk_window_present to avoid
5355 // raising the parent window.
5356 gdk_window_focus(parent_window, timestamp);
5357 return GDK_FILTER_REMOVE;
5358 }
5360 static GdkFilterReturn
5361 plugin_window_filter_func(GdkXEvent *gdk_xevent, GdkEvent *event, gpointer data)
5362 {
5363 GdkWindow *plugin_window;
5364 XEvent *xevent;
5365 Window xeventWindow;
5367 nsRefPtr<nsWindow> nswindow = (nsWindow*)data;
5368 GdkFilterReturn return_val;
5370 xevent = (XEvent *)gdk_xevent;
5371 return_val = GDK_FILTER_CONTINUE;
5373 switch (xevent->type)
5374 {
5375 case CreateNotify:
5376 case ReparentNotify:
5377 if (xevent->type==CreateNotify) {
5378 xeventWindow = xevent->xcreatewindow.window;
5379 }
5380 else {
5381 if (xevent->xreparent.event != xevent->xreparent.parent)
5382 break;
5383 xeventWindow = xevent->xreparent.window;
5384 }
5385 #if (MOZ_WIDGET_GTK == 2)
5386 plugin_window = gdk_window_lookup(xeventWindow);
5387 #else
5388 plugin_window = gdk_x11_window_lookup_for_display(
5389 gdk_x11_lookup_xdisplay(xevent->xcreatewindow.display), xeventWindow);
5390 #endif
5391 if (plugin_window) {
5392 GtkWidget *widget =
5393 get_gtk_widget_for_gdk_window(plugin_window);
5395 // TODO GTK3
5396 #if (MOZ_WIDGET_GTK == 2)
5397 if (GTK_IS_XTBIN(widget)) {
5398 nswindow->SetPluginType(nsWindow::PluginType_NONXEMBED);
5399 break;
5400 }
5401 else
5402 #endif
5403 if(GTK_IS_SOCKET(widget)) {
5404 if (!g_object_get_data(G_OBJECT(widget), "enable-xt-focus")) {
5405 nswindow->SetPluginType(nsWindow::PluginType_XEMBED);
5406 break;
5407 }
5408 }
5409 }
5410 nswindow->SetPluginType(nsWindow::PluginType_NONXEMBED);
5411 return_val = GDK_FILTER_REMOVE;
5412 break;
5413 case EnterNotify:
5414 nswindow->SetNonXEmbedPluginFocus();
5415 break;
5416 case DestroyNotify:
5417 gdk_window_remove_filter
5418 ((GdkWindow*)(nswindow->GetNativeData(NS_NATIVE_WINDOW)),
5419 plugin_window_filter_func,
5420 nswindow);
5421 // Currently we consider all plugins are non-xembed and calls
5422 // LoseNonXEmbedPluginFocus without any checking.
5423 nswindow->LoseNonXEmbedPluginFocus();
5424 break;
5425 default:
5426 break;
5427 }
5428 return return_val;
5429 }
5431 static GdkFilterReturn
5432 plugin_client_message_filter(GdkXEvent *gdk_xevent,
5433 GdkEvent *event,
5434 gpointer data)
5435 {
5436 XEvent *xevent;
5437 xevent = (XEvent *)gdk_xevent;
5439 GdkFilterReturn return_val;
5440 return_val = GDK_FILTER_CONTINUE;
5442 if (!gPluginFocusWindow || xevent->type!=ClientMessage) {
5443 return return_val;
5444 }
5446 // When WM sends out WM_TAKE_FOCUS, gtk2 will use XSetInputFocus
5447 // to set the focus to the focus proxy. To prevent this happen
5448 // while the focus is on the plugin, we filter the WM_TAKE_FOCUS
5449 // out.
5450 if (gdk_x11_get_xatom_by_name("WM_PROTOCOLS")
5451 != xevent->xclient.message_type) {
5452 return return_val;
5453 }
5455 if ((Atom) xevent->xclient.data.l[0] ==
5456 gdk_x11_get_xatom_by_name("WM_TAKE_FOCUS")) {
5457 // block it from gtk2.0 focus proxy
5458 return_val = GDK_FILTER_REMOVE;
5459 }
5461 return return_val;
5462 }
5463 #endif /* MOZ_X11 */
5465 static gboolean
5466 key_press_event_cb(GtkWidget *widget, GdkEventKey *event)
5467 {
5468 LOG(("key_press_event_cb\n"));
5470 UpdateLastInputEventTime(event);
5472 // find the window with focus and dispatch this event to that widget
5473 nsWindow *window = get_window_for_gtk_widget(widget);
5474 if (!window)
5475 return FALSE;
5477 nsRefPtr<nsWindow> focusWindow = gFocusWindow ? gFocusWindow : window;
5479 #ifdef MOZ_X11
5480 // Keyboard repeat can cause key press events to queue up when there are
5481 // slow event handlers (bug 301029). Throttle these events by removing
5482 // consecutive pending duplicate KeyPress events to the same window.
5483 // We use the event time of the last one.
5484 // Note: GDK calls XkbSetDetectableAutorepeat so that KeyRelease events
5485 // are generated only when the key is physically released.
5486 #define NS_GDKEVENT_MATCH_MASK 0x1FFF /* GDK_SHIFT_MASK .. GDK_BUTTON5_MASK */
5487 GdkDisplay* gdkDisplay = gtk_widget_get_display(widget);
5488 Display* dpy = GDK_DISPLAY_XDISPLAY(gdkDisplay);
5489 while (XPending(dpy)) {
5490 XEvent next_event;
5491 XPeekEvent(dpy, &next_event);
5492 GdkWindow* nextGdkWindow =
5493 gdk_x11_window_lookup_for_display(gdkDisplay, next_event.xany.window);
5494 if (nextGdkWindow != event->window ||
5495 next_event.type != KeyPress ||
5496 next_event.xkey.keycode != event->hardware_keycode ||
5497 next_event.xkey.state != (event->state & NS_GDKEVENT_MATCH_MASK)) {
5498 break;
5499 }
5500 XNextEvent(dpy, &next_event);
5501 event->time = next_event.xkey.time;
5502 }
5503 #endif
5505 return focusWindow->OnKeyPressEvent(event);
5506 }
5508 static gboolean
5509 key_release_event_cb(GtkWidget *widget, GdkEventKey *event)
5510 {
5511 LOG(("key_release_event_cb\n"));
5513 UpdateLastInputEventTime(event);
5515 // find the window with focus and dispatch this event to that widget
5516 nsWindow *window = get_window_for_gtk_widget(widget);
5517 if (!window)
5518 return FALSE;
5520 nsRefPtr<nsWindow> focusWindow = gFocusWindow ? gFocusWindow : window;
5522 return focusWindow->OnKeyReleaseEvent(event);
5523 }
5525 static gboolean
5526 scroll_event_cb(GtkWidget *widget, GdkEventScroll *event)
5527 {
5528 nsWindow *window = GetFirstNSWindowForGDKWindow(event->window);
5529 if (!window)
5530 return FALSE;
5532 window->OnScrollEvent(event);
5534 return TRUE;
5535 }
5537 static gboolean
5538 visibility_notify_event_cb (GtkWidget *widget, GdkEventVisibility *event)
5539 {
5540 nsRefPtr<nsWindow> window = get_window_for_gdk_window(event->window);
5541 if (!window)
5542 return FALSE;
5544 window->OnVisibilityNotifyEvent(event);
5546 return TRUE;
5547 }
5549 static void
5550 hierarchy_changed_cb (GtkWidget *widget,
5551 GtkWidget *previous_toplevel)
5552 {
5553 GtkWidget *toplevel = gtk_widget_get_toplevel(widget);
5554 GdkWindowState old_window_state = GDK_WINDOW_STATE_WITHDRAWN;
5555 GdkEventWindowState event;
5557 event.new_window_state = GDK_WINDOW_STATE_WITHDRAWN;
5559 if (GTK_IS_WINDOW(previous_toplevel)) {
5560 g_signal_handlers_disconnect_by_func(previous_toplevel,
5561 FuncToGpointer(window_state_event_cb),
5562 widget);
5563 GdkWindow *win = gtk_widget_get_window(previous_toplevel);
5564 if (win) {
5565 old_window_state = gdk_window_get_state(win);
5566 }
5567 }
5569 if (GTK_IS_WINDOW(toplevel)) {
5570 g_signal_connect_swapped(toplevel, "window-state-event",
5571 G_CALLBACK(window_state_event_cb), widget);
5572 GdkWindow *win = gtk_widget_get_window(toplevel);
5573 if (win) {
5574 event.new_window_state = gdk_window_get_state(win);
5575 }
5576 }
5578 event.changed_mask = static_cast<GdkWindowState>
5579 (old_window_state ^ event.new_window_state);
5581 if (event.changed_mask) {
5582 event.type = GDK_WINDOW_STATE;
5583 event.window = nullptr;
5584 event.send_event = TRUE;
5585 window_state_event_cb(widget, &event);
5586 }
5587 }
5589 static gboolean
5590 window_state_event_cb (GtkWidget *widget, GdkEventWindowState *event)
5591 {
5592 nsRefPtr<nsWindow> window = get_window_for_gtk_widget(widget);
5593 if (!window)
5594 return FALSE;
5596 window->OnWindowStateEvent(widget, event);
5598 return FALSE;
5599 }
5601 static void
5602 theme_changed_cb (GtkSettings *settings, GParamSpec *pspec, nsWindow *data)
5603 {
5604 nsRefPtr<nsWindow> window = data;
5605 window->ThemeChanged();
5606 }
5608 //////////////////////////////////////////////////////////////////////
5609 // These are all of our drag and drop operations
5611 void
5612 nsWindow::InitDragEvent(WidgetDragEvent &aEvent)
5613 {
5614 // set the keyboard modifiers
5615 guint modifierState = KeymapWrapper::GetCurrentModifierState();
5616 KeymapWrapper::InitInputEvent(aEvent, modifierState);
5617 }
5619 static gboolean
5620 drag_motion_event_cb(GtkWidget *aWidget,
5621 GdkDragContext *aDragContext,
5622 gint aX,
5623 gint aY,
5624 guint aTime,
5625 gpointer aData)
5626 {
5627 nsRefPtr<nsWindow> window = get_window_for_gtk_widget(aWidget);
5628 if (!window)
5629 return FALSE;
5631 // figure out which internal widget this drag motion actually happened on
5632 nscoord retx = 0;
5633 nscoord rety = 0;
5635 GdkWindow *innerWindow =
5636 get_inner_gdk_window(gtk_widget_get_window(aWidget), aX, aY,
5637 &retx, &rety);
5638 nsRefPtr<nsWindow> innerMostWindow = get_window_for_gdk_window(innerWindow);
5640 if (!innerMostWindow) {
5641 innerMostWindow = window;
5642 }
5644 LOGDRAG(("nsWindow drag-motion signal for %p\n", (void*)innerMostWindow));
5646 return nsDragService::GetInstance()->
5647 ScheduleMotionEvent(innerMostWindow, aDragContext,
5648 nsIntPoint(retx, rety), aTime);
5649 }
5651 static void
5652 drag_leave_event_cb(GtkWidget *aWidget,
5653 GdkDragContext *aDragContext,
5654 guint aTime,
5655 gpointer aData)
5656 {
5657 nsRefPtr<nsWindow> window = get_window_for_gtk_widget(aWidget);
5658 if (!window)
5659 return;
5661 nsDragService *dragService = nsDragService::GetInstance();
5663 nsWindow *mostRecentDragWindow = dragService->GetMostRecentDestWindow();
5664 if (!mostRecentDragWindow) {
5665 // This can happen when the target will not accept a drop. A GTK drag
5666 // source sends the leave message to the destination before the
5667 // drag-failed signal on the source widget, but the leave message goes
5668 // via the X server, and so doesn't get processed at least until the
5669 // event loop runs again.
5670 return;
5671 }
5673 GtkWidget *mozContainer = mostRecentDragWindow->GetMozContainerWidget();
5674 if (aWidget != mozContainer)
5675 {
5676 // When the drag moves between widgets, GTK can send leave signal for
5677 // the old widget after the motion or drop signal for the new widget.
5678 // We'll send the leave event when the motion or drop event is run.
5679 return;
5680 }
5682 LOGDRAG(("nsWindow drag-leave signal for %p\n",
5683 (void*)mostRecentDragWindow));
5685 dragService->ScheduleLeaveEvent();
5686 }
5689 static gboolean
5690 drag_drop_event_cb(GtkWidget *aWidget,
5691 GdkDragContext *aDragContext,
5692 gint aX,
5693 gint aY,
5694 guint aTime,
5695 gpointer aData)
5696 {
5697 nsRefPtr<nsWindow> window = get_window_for_gtk_widget(aWidget);
5698 if (!window)
5699 return FALSE;
5701 // figure out which internal widget this drag motion actually happened on
5702 nscoord retx = 0;
5703 nscoord rety = 0;
5705 GdkWindow *innerWindow =
5706 get_inner_gdk_window(gtk_widget_get_window(aWidget), aX, aY,
5707 &retx, &rety);
5708 nsRefPtr<nsWindow> innerMostWindow = get_window_for_gdk_window(innerWindow);
5710 if (!innerMostWindow) {
5711 innerMostWindow = window;
5712 }
5714 LOGDRAG(("nsWindow drag-drop signal for %p\n", (void*)innerMostWindow));
5716 return nsDragService::GetInstance()->
5717 ScheduleDropEvent(innerMostWindow, aDragContext,
5718 nsIntPoint(retx, rety), aTime);
5719 }
5721 static void
5722 drag_data_received_event_cb(GtkWidget *aWidget,
5723 GdkDragContext *aDragContext,
5724 gint aX,
5725 gint aY,
5726 GtkSelectionData *aSelectionData,
5727 guint aInfo,
5728 guint aTime,
5729 gpointer aData)
5730 {
5731 nsRefPtr<nsWindow> window = get_window_for_gtk_widget(aWidget);
5732 if (!window)
5733 return;
5735 window->OnDragDataReceivedEvent(aWidget,
5736 aDragContext,
5737 aX, aY,
5738 aSelectionData,
5739 aInfo, aTime, aData);
5740 }
5742 static nsresult
5743 initialize_prefs(void)
5744 {
5745 gRaiseWindows =
5746 Preferences::GetBool("mozilla.widget.raise-on-setfocus", true);
5748 return NS_OK;
5749 }
5751 static GdkWindow *
5752 get_inner_gdk_window (GdkWindow *aWindow,
5753 gint x, gint y,
5754 gint *retx, gint *rety)
5755 {
5756 gint cx, cy, cw, ch;
5757 GList *children = gdk_window_peek_children(aWindow);
5758 for (GList *child = g_list_last(children);
5759 child;
5760 child = g_list_previous(child)) {
5761 GdkWindow *childWindow = (GdkWindow *) child->data;
5762 if (get_window_for_gdk_window(childWindow)) {
5763 #if (MOZ_WIDGET_GTK == 2)
5764 gdk_window_get_geometry(childWindow, &cx, &cy, &cw, &ch, nullptr);
5765 #else
5766 gdk_window_get_geometry(childWindow, &cx, &cy, &cw, &ch);
5767 #endif
5768 if ((cx < x) && (x < (cx + cw)) &&
5769 (cy < y) && (y < (cy + ch)) &&
5770 gdk_window_is_visible(childWindow)) {
5771 return get_inner_gdk_window(childWindow,
5772 x - cx, y - cy,
5773 retx, rety);
5774 }
5775 }
5776 }
5777 *retx = x;
5778 *rety = y;
5779 return aWindow;
5780 }
5782 static inline bool
5783 is_context_menu_key(const WidgetKeyboardEvent& aKeyEvent)
5784 {
5785 return ((aKeyEvent.keyCode == NS_VK_F10 && aKeyEvent.IsShift() &&
5786 !aKeyEvent.IsControl() && !aKeyEvent.IsMeta() &&
5787 !aKeyEvent.IsAlt()) ||
5788 (aKeyEvent.keyCode == NS_VK_CONTEXT_MENU && !aKeyEvent.IsShift() &&
5789 !aKeyEvent.IsControl() && !aKeyEvent.IsMeta() &&
5790 !aKeyEvent.IsAlt()));
5791 }
5793 static int
5794 is_parent_ungrab_enter(GdkEventCrossing *aEvent)
5795 {
5796 return (GDK_CROSSING_UNGRAB == aEvent->mode) &&
5797 ((GDK_NOTIFY_ANCESTOR == aEvent->detail) ||
5798 (GDK_NOTIFY_VIRTUAL == aEvent->detail));
5800 }
5802 static int
5803 is_parent_grab_leave(GdkEventCrossing *aEvent)
5804 {
5805 return (GDK_CROSSING_GRAB == aEvent->mode) &&
5806 ((GDK_NOTIFY_ANCESTOR == aEvent->detail) ||
5807 (GDK_NOTIFY_VIRTUAL == aEvent->detail));
5808 }
5810 #ifdef ACCESSIBILITY
5811 void
5812 nsWindow::CreateRootAccessible()
5813 {
5814 if (mIsTopLevel && !mRootAccessible) {
5815 LOG(("nsWindow:: Create Toplevel Accessibility\n"));
5816 mRootAccessible = GetRootAccessible();
5817 }
5818 }
5820 void
5821 nsWindow::DispatchEventToRootAccessible(uint32_t aEventType)
5822 {
5823 if (!a11y::ShouldA11yBeEnabled()) {
5824 return;
5825 }
5827 nsCOMPtr<nsIAccessibilityService> accService =
5828 do_GetService("@mozilla.org/accessibilityService;1");
5829 if (!accService) {
5830 return;
5831 }
5833 // Get the root document accessible and fire event to it.
5834 a11y::Accessible* acc = GetRootAccessible();
5835 if (acc) {
5836 accService->FireAccessibleEvent(aEventType, acc);
5837 }
5838 }
5840 void
5841 nsWindow::DispatchActivateEventAccessible(void)
5842 {
5843 DispatchEventToRootAccessible(nsIAccessibleEvent::EVENT_WINDOW_ACTIVATE);
5844 }
5846 void
5847 nsWindow::DispatchDeactivateEventAccessible(void)
5848 {
5849 DispatchEventToRootAccessible(nsIAccessibleEvent::EVENT_WINDOW_DEACTIVATE);
5850 }
5852 void
5853 nsWindow::DispatchMaximizeEventAccessible(void)
5854 {
5855 DispatchEventToRootAccessible(nsIAccessibleEvent::EVENT_WINDOW_MAXIMIZE);
5856 }
5858 void
5859 nsWindow::DispatchMinimizeEventAccessible(void)
5860 {
5861 DispatchEventToRootAccessible(nsIAccessibleEvent::EVENT_WINDOW_MINIMIZE);
5862 }
5864 void
5865 nsWindow::DispatchRestoreEventAccessible(void)
5866 {
5867 DispatchEventToRootAccessible(nsIAccessibleEvent::EVENT_WINDOW_RESTORE);
5868 }
5870 #endif /* #ifdef ACCESSIBILITY */
5872 // nsChildWindow class
5874 nsChildWindow::nsChildWindow()
5875 {
5876 }
5878 nsChildWindow::~nsChildWindow()
5879 {
5880 }
5882 NS_IMETHODIMP
5883 nsWindow::NotifyIME(const IMENotification& aIMENotification)
5884 {
5885 if (MOZ_UNLIKELY(!mIMModule)) {
5886 switch (aIMENotification.mMessage) {
5887 case NOTIFY_IME_OF_CURSOR_POS_CHANGED:
5888 case REQUEST_TO_COMMIT_COMPOSITION:
5889 case REQUEST_TO_CANCEL_COMPOSITION:
5890 case NOTIFY_IME_OF_FOCUS:
5891 case NOTIFY_IME_OF_BLUR:
5892 return NS_ERROR_NOT_AVAILABLE;
5893 default:
5894 break;
5895 }
5896 }
5897 switch (aIMENotification.mMessage) {
5898 // TODO: We should replace NOTIFY_IME_OF_CURSOR_POS_CHANGED with
5899 // NOTIFY_IME_OF_SELECTION_CHANGE. The required behavior is
5900 // really different from committing composition.
5901 case NOTIFY_IME_OF_CURSOR_POS_CHANGED:
5902 case REQUEST_TO_COMMIT_COMPOSITION:
5903 return mIMModule->CommitIMEComposition(this);
5904 case REQUEST_TO_CANCEL_COMPOSITION:
5905 return mIMModule->CancelIMEComposition(this);
5906 case NOTIFY_IME_OF_FOCUS:
5907 mIMModule->OnFocusChangeInGecko(true);
5908 return NS_OK;
5909 case NOTIFY_IME_OF_BLUR:
5910 mIMModule->OnFocusChangeInGecko(false);
5911 return NS_OK;
5912 case NOTIFY_IME_OF_COMPOSITION_UPDATE:
5913 mIMModule->OnUpdateComposition();
5914 return NS_OK;
5915 default:
5916 return NS_ERROR_NOT_IMPLEMENTED;
5917 }
5918 }
5920 NS_IMETHODIMP_(void)
5921 nsWindow::SetInputContext(const InputContext& aContext,
5922 const InputContextAction& aAction)
5923 {
5924 if (!mIMModule) {
5925 return;
5926 }
5927 mIMModule->SetInputContext(this, &aContext, &aAction);
5928 }
5930 NS_IMETHODIMP_(InputContext)
5931 nsWindow::GetInputContext()
5932 {
5933 InputContext context;
5934 if (!mIMModule) {
5935 context.mIMEState.mEnabled = IMEState::DISABLED;
5936 context.mIMEState.mOpen = IMEState::OPEN_STATE_NOT_SUPPORTED;
5937 // If IME context isn't available on this widget, we should set |this|
5938 // instead of nullptr since nullptr means that the platform has only one
5939 // context per process.
5940 context.mNativeIMEContext = this;
5941 } else {
5942 context = mIMModule->GetInputContext();
5943 context.mNativeIMEContext = mIMModule;
5944 }
5945 return context;
5946 }
5948 NS_IMETHODIMP_(bool)
5949 nsWindow::ExecuteNativeKeyBinding(NativeKeyBindingsType aType,
5950 const WidgetKeyboardEvent& aEvent,
5951 DoCommandCallback aCallback,
5952 void* aCallbackData)
5953 {
5954 NativeKeyBindings* keyBindings = NativeKeyBindings::GetInstance(aType);
5955 return keyBindings->Execute(aEvent, aCallback, aCallbackData);
5956 }
5958 NS_IMETHODIMP
5959 nsWindow::GetToggledKeyState(uint32_t aKeyCode, bool* aLEDState)
5960 {
5961 NS_ENSURE_ARG_POINTER(aLEDState);
5963 KeymapWrapper::Modifiers modifier;
5964 switch (aKeyCode) {
5965 case NS_VK_CAPS_LOCK: modifier = KeymapWrapper::CAPS_LOCK; break;
5966 case NS_VK_NUM_LOCK: modifier = KeymapWrapper::NUM_LOCK; break;
5967 case NS_VK_SCROLL_LOCK: modifier = KeymapWrapper::SCROLL_LOCK; break;
5968 default: return NS_ERROR_INVALID_ARG;
5969 }
5971 *aLEDState =
5972 KeymapWrapper::AreModifiersCurrentlyActive(modifier);
5973 return NS_OK;
5974 }
5976 #if defined(MOZ_X11) && (MOZ_WIDGET_GTK == 2)
5977 /* static */ already_AddRefed<gfxASurface>
5978 nsWindow::GetSurfaceForGdkDrawable(GdkDrawable* aDrawable,
5979 const nsIntSize& aSize)
5980 {
5981 GdkVisual* visual = gdk_drawable_get_visual(aDrawable);
5982 Screen* xScreen =
5983 gdk_x11_screen_get_xscreen(gdk_drawable_get_screen(aDrawable));
5984 Display* xDisplay = DisplayOfScreen(xScreen);
5985 Drawable xDrawable = gdk_x11_drawable_get_xid(aDrawable);
5987 nsRefPtr<gfxASurface> result;
5989 if (visual) {
5990 Visual* xVisual = gdk_x11_visual_get_xvisual(visual);
5992 result = new gfxXlibSurface(xDisplay, xDrawable, xVisual,
5993 gfxIntSize(aSize.width, aSize.height));
5994 } else {
5995 // no visual? we must be using an xrender format. Find a format
5996 // for this depth.
5997 XRenderPictFormat *pf = nullptr;
5998 switch (gdk_drawable_get_depth(aDrawable)) {
5999 case 32:
6000 pf = XRenderFindStandardFormat(xDisplay, PictStandardARGB32);
6001 break;
6002 case 24:
6003 pf = XRenderFindStandardFormat(xDisplay, PictStandardRGB24);
6004 break;
6005 default:
6006 NS_ERROR("Don't know how to handle the given depth!");
6007 break;
6008 }
6010 result = new gfxXlibSurface(xScreen, xDrawable, pf,
6011 gfxIntSize(aSize.width, aSize.height));
6012 }
6014 return result.forget();
6015 }
6016 #endif
6018 TemporaryRef<DrawTarget>
6019 nsWindow::StartRemoteDrawing()
6020 {
6021 #if (MOZ_WIDGET_GTK == 2)
6022 gfxASurface *surf = GetThebesSurface();
6023 #else
6024 // TODO GTK3
6025 gfxASurface *surf = nullptr;
6026 #endif
6027 if (!surf) {
6028 return nullptr;
6029 }
6031 IntSize size(surf->GetSize().width, surf->GetSize().height);
6032 if (size.width <= 0 || size.height <= 0) {
6033 return nullptr;
6034 }
6036 return gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(surf, size);
6037 }
6039 // return the gfxASurface for rendering to this widget
6040 gfxASurface*
6041 #if (MOZ_WIDGET_GTK == 2)
6042 nsWindow::GetThebesSurface()
6043 #else
6044 nsWindow::GetThebesSurface(cairo_t *cr)
6045 #endif
6046 {
6047 if (!mGdkWindow)
6048 return nullptr;
6050 #if (MOZ_WIDGET_GTK != 2)
6051 cairo_surface_t *surf = cairo_get_target(cr);
6052 if (cairo_surface_status(surf) != CAIRO_STATUS_SUCCESS) {
6053 NS_NOTREACHED("Missing cairo target?");
6054 return nullptr;
6055 }
6056 #endif // MOZ_WIDGET_GTK2
6058 #ifdef MOZ_X11
6059 gint width, height;
6061 #if (MOZ_WIDGET_GTK == 2)
6062 gdk_drawable_get_size(GDK_DRAWABLE(mGdkWindow), &width, &height);
6063 #else
6064 width = gdk_window_get_width(mGdkWindow);
6065 height = gdk_window_get_height(mGdkWindow);
6066 #endif
6068 // Owen Taylor says this is the right thing to do!
6069 width = std::min(32767, width);
6070 height = std::min(32767, height);
6071 gfxIntSize size(width, height);
6073 GdkVisual *gdkVisual = gdk_window_get_visual(mGdkWindow);
6074 Visual* visual = gdk_x11_visual_get_xvisual(gdkVisual);
6076 # ifdef MOZ_HAVE_SHMIMAGE
6077 bool usingShm = false;
6078 if (nsShmImage::UseShm()) {
6079 // EnsureShmImage() is a dangerous interface, but we guarantee
6080 // that the thebes surface and the shmimage have the same
6081 // lifetime
6082 mThebesSurface =
6083 nsShmImage::EnsureShmImage(size,
6084 visual, gdk_visual_get_depth(gdkVisual),
6085 mShmImage);
6086 usingShm = mThebesSurface != nullptr;
6087 }
6088 if (!usingShm)
6089 # endif // MOZ_HAVE_SHMIMAGE
6091 #if (MOZ_WIDGET_GTK == 2)
6092 mThebesSurface = new gfxXlibSurface
6093 (GDK_WINDOW_XDISPLAY(mGdkWindow),
6094 gdk_x11_window_get_xid(mGdkWindow),
6095 visual,
6096 size);
6097 #else
6098 #if MOZ_TREE_CAIRO
6099 #error "cairo-gtk3 target must be built with --enable-system-cairo"
6100 #else
6101 mThebesSurface = gfxASurface::Wrap(surf);
6102 #endif
6103 #endif
6105 #endif
6107 // if the surface creation is reporting an error, then
6108 // we don't have a surface to give back
6109 if (mThebesSurface && mThebesSurface->CairoStatus() != 0) {
6110 mThebesSurface = nullptr;
6111 }
6113 return mThebesSurface;
6114 }
6116 // Code shared begin BeginMoveDrag and BeginResizeDrag
6117 bool
6118 nsWindow::GetDragInfo(WidgetMouseEvent* aMouseEvent,
6119 GdkWindow** aWindow, gint* aButton,
6120 gint* aRootX, gint* aRootY)
6121 {
6122 if (aMouseEvent->button != WidgetMouseEvent::eLeftButton) {
6123 // we can only begin a move drag with the left mouse button
6124 return false;
6125 }
6126 *aButton = 1;
6128 // get the gdk window for this widget
6129 GdkWindow* gdk_window = mGdkWindow;
6130 if (!gdk_window) {
6131 return false;
6132 }
6133 NS_ABORT_IF_FALSE(GDK_IS_WINDOW(gdk_window), "must really be window");
6135 // find the top-level window
6136 gdk_window = gdk_window_get_toplevel(gdk_window);
6137 NS_ABORT_IF_FALSE(gdk_window,
6138 "gdk_window_get_toplevel should not return null");
6139 *aWindow = gdk_window;
6141 if (!aMouseEvent->widget) {
6142 return false;
6143 }
6145 // FIXME: It would be nice to have the widget position at the time
6146 // of the event, but it's relatively unlikely that the widget has
6147 // moved since the mousedown. (On the other hand, it's quite likely
6148 // that the mouse has moved, which is why we use the mouse position
6149 // from the event.)
6150 nsIntPoint offset = aMouseEvent->widget->WidgetToScreenOffset();
6151 *aRootX = aMouseEvent->refPoint.x + offset.x;
6152 *aRootY = aMouseEvent->refPoint.y + offset.y;
6154 return true;
6155 }
6157 NS_IMETHODIMP
6158 nsWindow::BeginMoveDrag(WidgetMouseEvent* aEvent)
6159 {
6160 NS_ABORT_IF_FALSE(aEvent, "must have event");
6161 NS_ABORT_IF_FALSE(aEvent->eventStructType == NS_MOUSE_EVENT,
6162 "event must have correct struct type");
6164 GdkWindow *gdk_window;
6165 gint button, screenX, screenY;
6166 if (!GetDragInfo(aEvent, &gdk_window, &button, &screenX, &screenY)) {
6167 return NS_ERROR_FAILURE;
6168 }
6170 // tell the window manager to start the move
6171 gdk_window_begin_move_drag(gdk_window, button, screenX, screenY,
6172 aEvent->time);
6174 return NS_OK;
6175 }
6177 NS_IMETHODIMP
6178 nsWindow::BeginResizeDrag(WidgetGUIEvent* aEvent,
6179 int32_t aHorizontal,
6180 int32_t aVertical)
6181 {
6182 NS_ENSURE_ARG_POINTER(aEvent);
6184 if (aEvent->eventStructType != NS_MOUSE_EVENT) {
6185 // you can only begin a resize drag with a mouse event
6186 return NS_ERROR_INVALID_ARG;
6187 }
6189 GdkWindow *gdk_window;
6190 gint button, screenX, screenY;
6191 if (!GetDragInfo(aEvent->AsMouseEvent(), &gdk_window, &button,
6192 &screenX, &screenY)) {
6193 return NS_ERROR_FAILURE;
6194 }
6196 // work out what GdkWindowEdge we're talking about
6197 GdkWindowEdge window_edge;
6198 if (aVertical < 0) {
6199 if (aHorizontal < 0) {
6200 window_edge = GDK_WINDOW_EDGE_NORTH_WEST;
6201 } else if (aHorizontal == 0) {
6202 window_edge = GDK_WINDOW_EDGE_NORTH;
6203 } else {
6204 window_edge = GDK_WINDOW_EDGE_NORTH_EAST;
6205 }
6206 } else if (aVertical == 0) {
6207 if (aHorizontal < 0) {
6208 window_edge = GDK_WINDOW_EDGE_WEST;
6209 } else if (aHorizontal == 0) {
6210 return NS_ERROR_INVALID_ARG;
6211 } else {
6212 window_edge = GDK_WINDOW_EDGE_EAST;
6213 }
6214 } else {
6215 if (aHorizontal < 0) {
6216 window_edge = GDK_WINDOW_EDGE_SOUTH_WEST;
6217 } else if (aHorizontal == 0) {
6218 window_edge = GDK_WINDOW_EDGE_SOUTH;
6219 } else {
6220 window_edge = GDK_WINDOW_EDGE_SOUTH_EAST;
6221 }
6222 }
6224 // tell the window manager to start the resize
6225 gdk_window_begin_resize_drag(gdk_window, window_edge, button,
6226 screenX, screenY, aEvent->time);
6228 return NS_OK;
6229 }
6231 nsIWidget::LayerManager*
6232 nsWindow::GetLayerManager(PLayerTransactionChild* aShadowManager,
6233 LayersBackend aBackendHint,
6234 LayerManagerPersistence aPersistence,
6235 bool* aAllowRetaining)
6236 {
6237 if (!mLayerManager && eTransparencyTransparent == GetTransparencyMode()) {
6238 mLayerManager = CreateBasicLayerManager();
6239 }
6241 return nsBaseWidget::GetLayerManager(aShadowManager, aBackendHint,
6242 aPersistence, aAllowRetaining);
6243 }
6245 void
6246 nsWindow::ClearCachedResources()
6247 {
6248 if (mLayerManager &&
6249 mLayerManager->GetBackendType() == mozilla::layers::LayersBackend::LAYERS_BASIC) {
6250 mLayerManager->ClearCachedResources();
6251 }
6253 GList* children = gdk_window_peek_children(mGdkWindow);
6254 for (GList* list = children; list; list = list->next) {
6255 nsWindow* window = get_window_for_gdk_window(GDK_WINDOW(list->data));
6256 if (window) {
6257 window->ClearCachedResources();
6258 }
6259 }
6260 }
6262 nsresult
6263 nsWindow::SynthesizeNativeMouseEvent(nsIntPoint aPoint,
6264 uint32_t aNativeMessage,
6265 uint32_t aModifierFlags)
6266 {
6267 if (!mGdkWindow) {
6268 return NS_OK;
6269 }
6271 GdkDisplay* display = gdk_window_get_display(mGdkWindow);
6273 // When a button-release event is requested, create it here and put it in the
6274 // event queue. This will not emit a motion event - this needs to be done
6275 // explicitly *before* requesting a button-release. You will also need to wait
6276 // for the motion event to be dispatched before requesting a button-release
6277 // event to maintain the desired event order.
6278 if (aNativeMessage == GDK_BUTTON_RELEASE) {
6279 GdkEvent event;
6280 memset(&event, 0, sizeof(GdkEvent));
6281 event.type = (GdkEventType)aNativeMessage;
6282 event.button.button = 1;
6283 event.button.window = mGdkWindow;
6284 event.button.time = GDK_CURRENT_TIME;
6286 #if (MOZ_WIDGET_GTK == 3)
6287 // Get device for event source
6288 GdkDeviceManager *device_manager = gdk_display_get_device_manager(display);
6289 event.button.device = gdk_device_manager_get_client_pointer(device_manager);
6290 #endif
6292 gdk_event_put(&event);
6293 } else {
6294 // We don't support specific events other than button-release. In case
6295 // aNativeMessage != GDK_BUTTON_RELEASE we'll synthesize a motion event
6296 // that will be emitted by gdk_display_warp_pointer().
6297 GdkScreen* screen = gdk_window_get_screen(mGdkWindow);
6298 gdk_display_warp_pointer(display, screen, aPoint.x, aPoint.y);
6299 }
6301 return NS_OK;
6302 }