widget/gtk/nsWindow.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial