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