/src/mozilla-central/widget/gtk/nsWindow.cpp
Line | Count | Source (jump to first uncovered line) |
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 "nsWindow.h" |
9 | | |
10 | | #include "mozilla/ArrayUtils.h" |
11 | | #include "mozilla/EventForwards.h" |
12 | | #include "mozilla/MiscEvents.h" |
13 | | #include "mozilla/MouseEvents.h" |
14 | | #include "mozilla/RefPtr.h" |
15 | | #include "mozilla/TextEventDispatcher.h" |
16 | | #include "mozilla/TextEvents.h" |
17 | | #include "mozilla/TimeStamp.h" |
18 | | #include "mozilla/TouchEvents.h" |
19 | | #include "mozilla/UniquePtrExtensions.h" |
20 | | #include "mozilla/WidgetUtils.h" |
21 | | #include "mozilla/dom/WheelEventBinding.h" |
22 | | #include <algorithm> |
23 | | |
24 | | #include "GeckoProfiler.h" |
25 | | |
26 | | #include "prlink.h" |
27 | | #include "nsGTKToolkit.h" |
28 | | #include "nsIRollupListener.h" |
29 | | #include "nsINode.h" |
30 | | |
31 | | #include "nsWidgetsCID.h" |
32 | | #include "nsDragService.h" |
33 | | #include "nsIWidgetListener.h" |
34 | | #include "nsIScreenManager.h" |
35 | | #include "SystemTimeConverter.h" |
36 | | |
37 | | #include "nsGtkKeyUtils.h" |
38 | | #include "nsGtkCursors.h" |
39 | | #include "ScreenHelperGTK.h" |
40 | | |
41 | | #include <gtk/gtk.h> |
42 | | #include <gtk/gtkx.h> |
43 | | |
44 | | #ifdef MOZ_WAYLAND |
45 | | #include <gdk/gdkwayland.h> |
46 | | #endif /* MOZ_WAYLAND */ |
47 | | |
48 | | #ifdef MOZ_X11 |
49 | | #include <gdk/gdkx.h> |
50 | | #include <X11/Xatom.h> |
51 | | #include <X11/extensions/XShm.h> |
52 | | #include <X11/extensions/shape.h> |
53 | | #include <gdk/gdkkeysyms-compat.h> |
54 | | #endif /* MOZ_X11 */ |
55 | | |
56 | | #include <gdk/gdkkeysyms.h> |
57 | | |
58 | | #if defined(MOZ_WAYLAND) |
59 | | #include <gdk/gdkwayland.h> |
60 | | #endif |
61 | | |
62 | | #include "nsGkAtoms.h" |
63 | | |
64 | | #ifdef MOZ_ENABLE_STARTUP_NOTIFICATION |
65 | | #define SN_API_NOT_YET_FROZEN |
66 | | #include <startup-notification-1.0/libsn/sn.h> |
67 | | #endif |
68 | | |
69 | | #include "mozilla/Assertions.h" |
70 | | #include "mozilla/Likely.h" |
71 | | #include "mozilla/Preferences.h" |
72 | | #include "nsIPrefService.h" |
73 | | #include "nsIGConfService.h" |
74 | | #include "nsIServiceManager.h" |
75 | | #include "nsGfxCIID.h" |
76 | | #include "nsGtkUtils.h" |
77 | | #include "nsIObserverService.h" |
78 | | #include "mozilla/layers/LayersTypes.h" |
79 | | #include "nsIIdleServiceInternal.h" |
80 | | #include "nsIPropertyBag2.h" |
81 | | #include "GLContext.h" |
82 | | #include "gfx2DGlue.h" |
83 | | |
84 | | #ifdef ACCESSIBILITY |
85 | | #include "mozilla/a11y/Accessible.h" |
86 | | #include "mozilla/a11y/Platform.h" |
87 | | #include "nsAccessibilityService.h" |
88 | | |
89 | | using namespace mozilla; |
90 | | using namespace mozilla::widget; |
91 | | #endif |
92 | | |
93 | | /* For SetIcon */ |
94 | | #include "nsAppDirectoryServiceDefs.h" |
95 | | #include "nsString.h" |
96 | | #include "nsIFile.h" |
97 | | |
98 | | /* SetCursor(imgIContainer*) */ |
99 | | #include <gdk/gdk.h> |
100 | | #include <wchar.h> |
101 | | #include "imgIContainer.h" |
102 | | #include "nsGfxCIID.h" |
103 | | #include "nsImageToPixbuf.h" |
104 | | #include "nsIInterfaceRequestorUtils.h" |
105 | | #include "ClientLayerManager.h" |
106 | | |
107 | | #include "gfxPlatformGtk.h" |
108 | | #include "gfxContext.h" |
109 | | #include "gfxImageSurface.h" |
110 | | #include "gfxUtils.h" |
111 | | #include "Layers.h" |
112 | | #include "GLContextProvider.h" |
113 | | #include "mozilla/gfx/2D.h" |
114 | | #include "mozilla/gfx/HelpersCairo.h" |
115 | | #include "mozilla/gfx/GPUProcessManager.h" |
116 | | #include "mozilla/layers/CompositorBridgeParent.h" |
117 | | #include "mozilla/layers/CompositorThread.h" |
118 | | |
119 | | #ifdef MOZ_X11 |
120 | | #include "GLContextGLX.h" // for GLContextGLX::FindVisual() |
121 | | #include "GtkCompositorWidget.h" |
122 | | #include "gfxXlibSurface.h" |
123 | | #include "WindowSurfaceX11Image.h" |
124 | | #include "WindowSurfaceX11SHM.h" |
125 | | #include "WindowSurfaceXRender.h" |
126 | | #endif // MOZ_X11 |
127 | | #ifdef MOZ_WAYLAND |
128 | | #include "nsIClipboard.h" |
129 | | #endif |
130 | | |
131 | | #include "nsShmImage.h" |
132 | | #include "gtkdrawing.h" |
133 | | |
134 | | #include "NativeKeyBindings.h" |
135 | | |
136 | | #include <dlfcn.h> |
137 | | |
138 | | using namespace mozilla; |
139 | | using namespace mozilla::gfx; |
140 | | using namespace mozilla::widget; |
141 | | using namespace mozilla::layers; |
142 | | using mozilla::gl::GLContext; |
143 | | using mozilla::gl::GLContextGLX; |
144 | | |
145 | | // Don't put more than this many rects in the dirty region, just fluff |
146 | | // out to the bounding-box if there are more |
147 | | #define MAX_RECTS_IN_REGION 100 |
148 | | |
149 | | const gint kEvents = GDK_EXPOSURE_MASK | GDK_STRUCTURE_MASK | |
150 | | GDK_VISIBILITY_NOTIFY_MASK | |
151 | | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK | |
152 | | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | |
153 | | #if GTK_CHECK_VERSION(3,4,0) |
154 | | GDK_SMOOTH_SCROLL_MASK | |
155 | | GDK_TOUCH_MASK | |
156 | | #endif |
157 | | GDK_SCROLL_MASK | |
158 | | GDK_POINTER_MOTION_MASK | |
159 | | GDK_PROPERTY_CHANGE_MASK; |
160 | | |
161 | | /* utility functions */ |
162 | | static bool is_mouse_in_window(GdkWindow* aWindow, |
163 | | gdouble aMouseX, gdouble aMouseY); |
164 | | static nsWindow *get_window_for_gtk_widget(GtkWidget *widget); |
165 | | static nsWindow *get_window_for_gdk_window(GdkWindow *window); |
166 | | static GtkWidget *get_gtk_widget_for_gdk_window(GdkWindow *window); |
167 | | static GdkCursor *get_gtk_cursor(nsCursor aCursor); |
168 | | |
169 | | static GdkWindow *get_inner_gdk_window (GdkWindow *aWindow, |
170 | | gint x, gint y, |
171 | | gint *retx, gint *rety); |
172 | | |
173 | | static int is_parent_ungrab_enter(GdkEventCrossing *aEvent); |
174 | | static int is_parent_grab_leave(GdkEventCrossing *aEvent); |
175 | | |
176 | | /* callbacks from widgets */ |
177 | | static gboolean expose_event_cb (GtkWidget *widget, |
178 | | cairo_t *rect); |
179 | | static gboolean configure_event_cb (GtkWidget *widget, |
180 | | GdkEventConfigure *event); |
181 | | static void container_unrealize_cb (GtkWidget *widget); |
182 | | static void size_allocate_cb (GtkWidget *widget, |
183 | | GtkAllocation *allocation); |
184 | | static gboolean delete_event_cb (GtkWidget *widget, |
185 | | GdkEventAny *event); |
186 | | static gboolean enter_notify_event_cb (GtkWidget *widget, |
187 | | GdkEventCrossing *event); |
188 | | static gboolean leave_notify_event_cb (GtkWidget *widget, |
189 | | GdkEventCrossing *event); |
190 | | static gboolean motion_notify_event_cb (GtkWidget *widget, |
191 | | GdkEventMotion *event); |
192 | | static gboolean button_press_event_cb (GtkWidget *widget, |
193 | | GdkEventButton *event); |
194 | | static gboolean button_release_event_cb (GtkWidget *widget, |
195 | | GdkEventButton *event); |
196 | | static gboolean focus_in_event_cb (GtkWidget *widget, |
197 | | GdkEventFocus *event); |
198 | | static gboolean focus_out_event_cb (GtkWidget *widget, |
199 | | GdkEventFocus *event); |
200 | | static gboolean key_press_event_cb (GtkWidget *widget, |
201 | | GdkEventKey *event); |
202 | | static gboolean key_release_event_cb (GtkWidget *widget, |
203 | | GdkEventKey *event); |
204 | | static gboolean property_notify_event_cb (GtkWidget *widget, |
205 | | GdkEventProperty *event); |
206 | | static gboolean scroll_event_cb (GtkWidget *widget, |
207 | | GdkEventScroll *event); |
208 | | static gboolean visibility_notify_event_cb(GtkWidget *widget, |
209 | | GdkEventVisibility *event); |
210 | | static void hierarchy_changed_cb (GtkWidget *widget, |
211 | | GtkWidget *previous_toplevel); |
212 | | static gboolean window_state_event_cb (GtkWidget *widget, |
213 | | GdkEventWindowState *event); |
214 | | static void settings_changed_cb (GtkSettings *settings, |
215 | | GParamSpec *pspec, |
216 | | nsWindow *data); |
217 | | static void check_resize_cb (GtkContainer* container, |
218 | | gpointer user_data); |
219 | | static void screen_composited_changed_cb (GdkScreen* screen, |
220 | | gpointer user_data); |
221 | | static void widget_composited_changed_cb (GtkWidget* widget, |
222 | | gpointer user_data); |
223 | | |
224 | | static void scale_changed_cb (GtkWidget* widget, |
225 | | GParamSpec* aPSpec, |
226 | | gpointer aPointer); |
227 | | #if GTK_CHECK_VERSION(3,4,0) |
228 | | static gboolean touch_event_cb (GtkWidget* aWidget, |
229 | | GdkEventTouch* aEvent); |
230 | | #endif |
231 | | static nsWindow* GetFirstNSWindowForGDKWindow (GdkWindow *aGdkWindow); |
232 | | |
233 | | #ifdef __cplusplus |
234 | | extern "C" { |
235 | | #endif /* __cplusplus */ |
236 | | #ifdef MOZ_X11 |
237 | | static GdkFilterReturn popup_take_focus_filter (GdkXEvent *gdk_xevent, |
238 | | GdkEvent *event, |
239 | | gpointer data); |
240 | | #endif /* MOZ_X11 */ |
241 | | #ifdef __cplusplus |
242 | | } |
243 | | #endif /* __cplusplus */ |
244 | | |
245 | | static gboolean drag_motion_event_cb (GtkWidget *aWidget, |
246 | | GdkDragContext *aDragContext, |
247 | | gint aX, |
248 | | gint aY, |
249 | | guint aTime, |
250 | | gpointer aData); |
251 | | static void drag_leave_event_cb (GtkWidget *aWidget, |
252 | | GdkDragContext *aDragContext, |
253 | | guint aTime, |
254 | | gpointer aData); |
255 | | static gboolean drag_drop_event_cb (GtkWidget *aWidget, |
256 | | GdkDragContext *aDragContext, |
257 | | gint aX, |
258 | | gint aY, |
259 | | guint aTime, |
260 | | gpointer aData); |
261 | | static void drag_data_received_event_cb(GtkWidget *aWidget, |
262 | | GdkDragContext *aDragContext, |
263 | | gint aX, |
264 | | gint aY, |
265 | | GtkSelectionData *aSelectionData, |
266 | | guint aInfo, |
267 | | guint32 aTime, |
268 | | gpointer aData); |
269 | | |
270 | | /* initialization static functions */ |
271 | | static nsresult initialize_prefs (void); |
272 | | |
273 | | static guint32 sLastUserInputTime = GDK_CURRENT_TIME; |
274 | | static guint32 sRetryGrabTime; |
275 | | |
276 | | static SystemTimeConverter<guint32>& |
277 | 0 | TimeConverter() { |
278 | 0 | static SystemTimeConverter<guint32> sTimeConverterSingleton; |
279 | 0 | return sTimeConverterSingleton; |
280 | 0 | } |
281 | | |
282 | | nsWindow::CSDSupportLevel nsWindow::sCSDSupportLevel = CSD_SUPPORT_UNKNOWN; |
283 | | |
284 | | namespace mozilla { |
285 | | |
286 | | class CurrentX11TimeGetter |
287 | | { |
288 | | public: |
289 | | explicit CurrentX11TimeGetter(GdkWindow* aWindow) |
290 | | : mWindow(aWindow) |
291 | | , mAsyncUpdateStart() |
292 | 0 | { |
293 | 0 | } |
294 | | |
295 | | guint32 GetCurrentTime() const |
296 | 0 | { |
297 | 0 | return gdk_x11_get_server_time(mWindow); |
298 | 0 | } |
299 | | |
300 | | void GetTimeAsyncForPossibleBackwardsSkew(const TimeStamp& aNow) |
301 | 0 | { |
302 | 0 | // Check for in-flight request |
303 | 0 | if (!mAsyncUpdateStart.IsNull()) { |
304 | 0 | return; |
305 | 0 | } |
306 | 0 | mAsyncUpdateStart = aNow; |
307 | 0 |
|
308 | 0 | Display* xDisplay = GDK_WINDOW_XDISPLAY(mWindow); |
309 | 0 | Window xWindow = GDK_WINDOW_XID(mWindow); |
310 | 0 | unsigned char c = 'a'; |
311 | 0 | Atom timeStampPropAtom = TimeStampPropAtom(); |
312 | 0 | XChangeProperty(xDisplay, xWindow, timeStampPropAtom, |
313 | 0 | timeStampPropAtom, 8, PropModeReplace, &c, 1); |
314 | 0 | XFlush(xDisplay); |
315 | 0 | } |
316 | | |
317 | | gboolean PropertyNotifyHandler(GtkWidget* aWidget, |
318 | | GdkEventProperty* aEvent) |
319 | 0 | { |
320 | 0 | if (aEvent->atom != |
321 | 0 | gdk_x11_xatom_to_atom(TimeStampPropAtom())) { |
322 | 0 | return FALSE; |
323 | 0 | } |
324 | 0 | |
325 | 0 | guint32 eventTime = aEvent->time; |
326 | 0 | TimeStamp lowerBound = mAsyncUpdateStart; |
327 | 0 |
|
328 | 0 | TimeConverter().CompensateForBackwardsSkew(eventTime, lowerBound); |
329 | 0 | mAsyncUpdateStart = TimeStamp(); |
330 | 0 | return TRUE; |
331 | 0 | } |
332 | | |
333 | | private: |
334 | 0 | static Atom TimeStampPropAtom() { |
335 | 0 | return gdk_x11_get_xatom_by_name_for_display( |
336 | 0 | gdk_display_get_default(), "GDK_TIMESTAMP_PROP"); |
337 | 0 | } |
338 | | |
339 | | // This is safe because this class is stored as a member of mWindow and |
340 | | // won't outlive it. |
341 | | GdkWindow* mWindow; |
342 | | TimeStamp mAsyncUpdateStart; |
343 | | }; |
344 | | |
345 | | } // namespace mozilla |
346 | | |
347 | | static NS_DEFINE_IID(kCDragServiceCID, NS_DRAGSERVICE_CID); |
348 | | |
349 | | // The window from which the focus manager asks us to dispatch key events. |
350 | | static nsWindow *gFocusWindow = nullptr; |
351 | | static bool gBlockActivateEvent = false; |
352 | | static bool gGlobalsInitialized = false; |
353 | | static bool gRaiseWindows = true; |
354 | | |
355 | | #if GTK_CHECK_VERSION(3,4,0) |
356 | | static uint32_t gLastTouchID = 0; |
357 | | #endif |
358 | | |
359 | 0 | #define NS_WINDOW_TITLE_MAX_LENGTH 4095 |
360 | | |
361 | | // If after selecting profile window, the startup fail, please refer to |
362 | | // http://bugzilla.gnome.org/show_bug.cgi?id=88940 |
363 | | |
364 | | // needed for imgIContainer cursors |
365 | | // GdkDisplay* was added in 2.2 |
366 | | typedef struct _GdkDisplay GdkDisplay; |
367 | | |
368 | 0 | #define kWindowPositionSlop 20 |
369 | | |
370 | | // cursor cache |
371 | | static GdkCursor *gCursorCache[eCursorCount]; |
372 | | |
373 | | static GtkWidget *gInvisibleContainer = nullptr; |
374 | | |
375 | | // Sometimes this actually also includes the state of the modifier keys, but |
376 | | // only the button state bits are used. |
377 | | static guint gButtonState; |
378 | | |
379 | | static inline int32_t |
380 | | GetBitmapStride(int32_t width) |
381 | 0 | { |
382 | 0 | #if defined(MOZ_X11) |
383 | 0 | return (width+7)/8; |
384 | | #else |
385 | | return cairo_format_stride_for_width(CAIRO_FORMAT_A1, width); |
386 | | #endif |
387 | | } |
388 | | |
389 | | static inline bool TimestampIsNewerThan(guint32 a, guint32 b) |
390 | 0 | { |
391 | 0 | // Timestamps are just the least significant bits of a monotonically |
392 | 0 | // increasing function, and so the use of unsigned overflow arithmetic. |
393 | 0 | return a - b <= G_MAXUINT32/2; |
394 | 0 | } |
395 | | |
396 | | static void |
397 | | UpdateLastInputEventTime(void *aGdkEvent) |
398 | 0 | { |
399 | 0 | nsCOMPtr<nsIIdleServiceInternal> idleService = |
400 | 0 | do_GetService("@mozilla.org/widget/idleservice;1"); |
401 | 0 | if (idleService) { |
402 | 0 | idleService->ResetIdleTimeOut(0); |
403 | 0 | } |
404 | 0 |
|
405 | 0 | guint timestamp = gdk_event_get_time(static_cast<GdkEvent*>(aGdkEvent)); |
406 | 0 | if (timestamp == GDK_CURRENT_TIME) |
407 | 0 | return; |
408 | 0 | |
409 | 0 | sLastUserInputTime = timestamp; |
410 | 0 | } |
411 | | |
412 | | nsWindow::nsWindow() |
413 | 0 | { |
414 | 0 | mIsTopLevel = false; |
415 | 0 | mIsDestroyed = false; |
416 | 0 | mListenForResizes = false; |
417 | 0 | mNeedsDispatchResized = false; |
418 | 0 | mIsShown = false; |
419 | 0 | mNeedsShow = false; |
420 | 0 | mEnabled = true; |
421 | 0 | mCreated = false; |
422 | 0 | #if GTK_CHECK_VERSION(3,4,0) |
423 | 0 | mHandleTouchEvent = false; |
424 | 0 | #endif |
425 | 0 | mIsDragPopup = false; |
426 | 0 | mIsX11Display = GDK_IS_X11_DISPLAY(gdk_display_get_default()); |
427 | 0 |
|
428 | 0 | mContainer = nullptr; |
429 | 0 | mGdkWindow = nullptr; |
430 | 0 | mShell = nullptr; |
431 | 0 | mCompositorWidgetDelegate = nullptr; |
432 | 0 | mHasMappedToplevel = false; |
433 | 0 | mIsFullyObscured = false; |
434 | 0 | mRetryPointerGrab = false; |
435 | 0 | mWindowType = eWindowType_child; |
436 | 0 | mSizeState = nsSizeMode_Normal; |
437 | 0 | mLastSizeMode = nsSizeMode_Normal; |
438 | 0 | mSizeConstraints.mMaxSize = GetSafeWindowSize(mSizeConstraints.mMaxSize); |
439 | 0 |
|
440 | 0 | #ifdef MOZ_X11 |
441 | 0 | mOldFocusWindow = 0; |
442 | 0 |
|
443 | 0 | mXDisplay = nullptr; |
444 | 0 | mXWindow = X11None; |
445 | 0 | mXVisual = nullptr; |
446 | 0 | mXDepth = 0; |
447 | 0 | #endif /* MOZ_X11 */ |
448 | 0 |
|
449 | 0 | if (!gGlobalsInitialized) { |
450 | 0 | gGlobalsInitialized = true; |
451 | 0 |
|
452 | 0 | // It's OK if either of these fail, but it may not be one day. |
453 | 0 | initialize_prefs(); |
454 | 0 |
|
455 | | #ifdef MOZ_WAYLAND |
456 | | // Wayland provides clipboard data to application on focus-in event |
457 | | // so we need to init our clipboard hooks before we create window |
458 | | // and get focus. |
459 | | if (!mIsX11Display) { |
460 | | nsCOMPtr<nsIClipboard> clipboard = |
461 | | do_GetService("@mozilla.org/widget/clipboard;1"); |
462 | | NS_ASSERTION(clipboard, "Failed to init clipboard!"); |
463 | | } |
464 | | #endif |
465 | | } |
466 | 0 |
|
467 | 0 | mLastMotionPressure = 0; |
468 | 0 |
|
469 | 0 | #ifdef ACCESSIBILITY |
470 | 0 | mRootAccessible = nullptr; |
471 | 0 | #endif |
472 | 0 |
|
473 | 0 | mIsTransparent = false; |
474 | 0 | mTransparencyBitmap = nullptr; |
475 | 0 |
|
476 | 0 | mTransparencyBitmapWidth = 0; |
477 | 0 | mTransparencyBitmapHeight = 0; |
478 | 0 |
|
479 | 0 | #if GTK_CHECK_VERSION(3,4,0) |
480 | 0 | mLastScrollEventTime = GDK_CURRENT_TIME; |
481 | 0 | #endif |
482 | 0 | mPendingConfigures = 0; |
483 | 0 | mCSDSupportLevel = CSD_SUPPORT_NONE; |
484 | 0 | mDrawInTitlebar = false; |
485 | 0 |
|
486 | 0 | mHasAlphaVisual = false; |
487 | 0 | } |
488 | | |
489 | | nsWindow::~nsWindow() |
490 | 0 | { |
491 | 0 | LOG(("nsWindow::~nsWindow() [%p]\n", (void *)this)); |
492 | 0 |
|
493 | 0 | delete[] mTransparencyBitmap; |
494 | 0 | mTransparencyBitmap = nullptr; |
495 | 0 |
|
496 | 0 | Destroy(); |
497 | 0 | } |
498 | | |
499 | | /* static */ void |
500 | | nsWindow::ReleaseGlobals() |
501 | 0 | { |
502 | 0 | for (auto & cursor : gCursorCache) { |
503 | 0 | if (cursor) { |
504 | 0 | g_object_unref(cursor); |
505 | 0 | cursor = nullptr; |
506 | 0 | } |
507 | 0 | } |
508 | 0 | } |
509 | | |
510 | | void |
511 | | nsWindow::CommonCreate(nsIWidget *aParent, bool aListenForResizes) |
512 | 0 | { |
513 | 0 | mParent = aParent; |
514 | 0 | mListenForResizes = aListenForResizes; |
515 | 0 | mCreated = true; |
516 | 0 | } |
517 | | |
518 | | void |
519 | | nsWindow::DispatchActivateEvent(void) |
520 | 0 | { |
521 | 0 | NS_ASSERTION(mContainer || mIsDestroyed, |
522 | 0 | "DispatchActivateEvent only intended for container windows"); |
523 | 0 |
|
524 | 0 | #ifdef ACCESSIBILITY |
525 | 0 | DispatchActivateEventAccessible(); |
526 | 0 | #endif //ACCESSIBILITY |
527 | 0 |
|
528 | 0 | if (mWidgetListener) |
529 | 0 | mWidgetListener->WindowActivated(); |
530 | 0 | } |
531 | | |
532 | | void |
533 | | nsWindow::DispatchDeactivateEvent(void) |
534 | 0 | { |
535 | 0 | if (mWidgetListener) |
536 | 0 | mWidgetListener->WindowDeactivated(); |
537 | 0 |
|
538 | 0 | #ifdef ACCESSIBILITY |
539 | 0 | DispatchDeactivateEventAccessible(); |
540 | 0 | #endif //ACCESSIBILITY |
541 | 0 | } |
542 | | |
543 | | void |
544 | | nsWindow::DispatchResized() |
545 | 0 | { |
546 | 0 | mNeedsDispatchResized = false; |
547 | 0 | if (mWidgetListener) { |
548 | 0 | mWidgetListener->WindowResized(this, mBounds.width, mBounds.height); |
549 | 0 | } |
550 | 0 | if (mAttachedWidgetListener) { |
551 | 0 | mAttachedWidgetListener->WindowResized(this, |
552 | 0 | mBounds.width, mBounds.height); |
553 | 0 | } |
554 | 0 | } |
555 | | |
556 | | void |
557 | | nsWindow::MaybeDispatchResized() |
558 | 0 | { |
559 | 0 | if (mNeedsDispatchResized && !mIsDestroyed) { |
560 | 0 | DispatchResized(); |
561 | 0 | } |
562 | 0 | } |
563 | | |
564 | | nsIWidgetListener* |
565 | | nsWindow::GetListener() |
566 | 0 | { |
567 | 0 | return mAttachedWidgetListener ? mAttachedWidgetListener : mWidgetListener; |
568 | 0 | } |
569 | | |
570 | | nsresult |
571 | | nsWindow::DispatchEvent(WidgetGUIEvent* aEvent, nsEventStatus& aStatus) |
572 | 0 | { |
573 | | #ifdef DEBUG |
574 | | debug_DumpEvent(stdout, aEvent->mWidget, aEvent, |
575 | | "something", 0); |
576 | | #endif |
577 | | aStatus = nsEventStatus_eIgnore; |
578 | 0 | nsIWidgetListener* listener = GetListener(); |
579 | 0 | if (listener) { |
580 | 0 | aStatus = listener->HandleEvent(aEvent, mUseAttachedEvents); |
581 | 0 | } |
582 | 0 |
|
583 | 0 | return NS_OK; |
584 | 0 | } |
585 | | |
586 | | void |
587 | | nsWindow::OnDestroy(void) |
588 | 0 | { |
589 | 0 | if (mOnDestroyCalled) |
590 | 0 | return; |
591 | 0 | |
592 | 0 | mOnDestroyCalled = true; |
593 | 0 |
|
594 | 0 | // Prevent deletion. |
595 | 0 | nsCOMPtr<nsIWidget> kungFuDeathGrip = this; |
596 | 0 |
|
597 | 0 | // release references to children, device context, toolkit + app shell |
598 | 0 | nsBaseWidget::OnDestroy(); |
599 | 0 |
|
600 | 0 | // Remove association between this object and its parent and siblings. |
601 | 0 | nsBaseWidget::Destroy(); |
602 | 0 | mParent = nullptr; |
603 | 0 |
|
604 | 0 | NotifyWindowDestroyed(); |
605 | 0 | } |
606 | | |
607 | | bool |
608 | | nsWindow::AreBoundsSane(void) |
609 | 0 | { |
610 | 0 | if (mBounds.width > 0 && mBounds.height > 0) |
611 | 0 | return true; |
612 | 0 | |
613 | 0 | return false; |
614 | 0 | } |
615 | | |
616 | | static GtkWidget* |
617 | | EnsureInvisibleContainer() |
618 | 0 | { |
619 | 0 | if (!gInvisibleContainer) { |
620 | 0 | // GtkWidgets need to be anchored to a GtkWindow to be realized (to |
621 | 0 | // have a window). Using GTK_WINDOW_POPUP rather than |
622 | 0 | // GTK_WINDOW_TOPLEVEL in the hope that POPUP results in less |
623 | 0 | // initialization and window manager interaction. |
624 | 0 | GtkWidget* window = gtk_window_new(GTK_WINDOW_POPUP); |
625 | 0 | gInvisibleContainer = moz_container_new(); |
626 | 0 | gtk_container_add(GTK_CONTAINER(window), gInvisibleContainer); |
627 | 0 | gtk_widget_realize(gInvisibleContainer); |
628 | 0 |
|
629 | 0 | } |
630 | 0 | return gInvisibleContainer; |
631 | 0 | } |
632 | | |
633 | | static void |
634 | | CheckDestroyInvisibleContainer() |
635 | 0 | { |
636 | 0 | MOZ_ASSERT(gInvisibleContainer, "oh, no"); |
637 | 0 |
|
638 | 0 | if (!gdk_window_peek_children(gtk_widget_get_window(gInvisibleContainer))) { |
639 | 0 | // No children, so not in use. |
640 | 0 | // Make sure to destroy the GtkWindow also. |
641 | 0 | gtk_widget_destroy(gtk_widget_get_parent(gInvisibleContainer)); |
642 | 0 | gInvisibleContainer = nullptr; |
643 | 0 | } |
644 | 0 | } |
645 | | |
646 | | // Change the containing GtkWidget on a sub-hierarchy of GdkWindows belonging |
647 | | // to aOldWidget and rooted at aWindow, and reparent any child GtkWidgets of |
648 | | // the GdkWindow hierarchy to aNewWidget. |
649 | | static void |
650 | | SetWidgetForHierarchy(GdkWindow *aWindow, |
651 | | GtkWidget *aOldWidget, |
652 | | GtkWidget *aNewWidget) |
653 | 0 | { |
654 | 0 | gpointer data; |
655 | 0 | gdk_window_get_user_data(aWindow, &data); |
656 | 0 |
|
657 | 0 | if (data != aOldWidget) { |
658 | 0 | if (!GTK_IS_WIDGET(data)) |
659 | 0 | return; |
660 | 0 | |
661 | 0 | auto* widget = static_cast<GtkWidget*>(data); |
662 | 0 | if (gtk_widget_get_parent(widget) != aOldWidget) |
663 | 0 | return; |
664 | 0 | |
665 | 0 | // This window belongs to a child widget, which will no longer be a |
666 | 0 | // child of aOldWidget. |
667 | 0 | gtk_widget_reparent(widget, aNewWidget); |
668 | 0 |
|
669 | 0 | return; |
670 | 0 | } |
671 | 0 | |
672 | 0 | GList *children = gdk_window_get_children(aWindow); |
673 | 0 | for(GList *list = children; list; list = list->next) { |
674 | 0 | SetWidgetForHierarchy(GDK_WINDOW(list->data), aOldWidget, aNewWidget); |
675 | 0 | } |
676 | 0 | g_list_free(children); |
677 | 0 |
|
678 | 0 | gdk_window_set_user_data(aWindow, aNewWidget); |
679 | 0 | } |
680 | | |
681 | | // Walk the list of child windows and call destroy on them. |
682 | | void |
683 | | nsWindow::DestroyChildWindows() |
684 | 0 | { |
685 | 0 | if (!mGdkWindow) |
686 | 0 | return; |
687 | 0 | |
688 | 0 | while (GList *children = gdk_window_peek_children(mGdkWindow)) { |
689 | 0 | GdkWindow *child = GDK_WINDOW(children->data); |
690 | 0 | nsWindow *kid = get_window_for_gdk_window(child); |
691 | 0 | if (kid) { |
692 | 0 | kid->Destroy(); |
693 | 0 | } else { |
694 | 0 | // This child is not an nsWindow. |
695 | 0 | // Destroy the child GtkWidget. |
696 | 0 | gpointer data; |
697 | 0 | gdk_window_get_user_data(child, &data); |
698 | 0 | if (GTK_IS_WIDGET(data)) { |
699 | 0 | gtk_widget_destroy(static_cast<GtkWidget*>(data)); |
700 | 0 | } |
701 | 0 | } |
702 | 0 | } |
703 | 0 | } |
704 | | |
705 | | void |
706 | | nsWindow::Destroy() |
707 | 0 | { |
708 | 0 | if (mIsDestroyed || !mCreated) |
709 | 0 | return; |
710 | 0 | |
711 | 0 | LOG(("nsWindow::Destroy [%p]\n", (void *)this)); |
712 | 0 | mIsDestroyed = true; |
713 | 0 | mCreated = false; |
714 | 0 |
|
715 | 0 | /** Need to clean our LayerManager up while still alive */ |
716 | 0 | if (mLayerManager) { |
717 | 0 | mLayerManager->Destroy(); |
718 | 0 | } |
719 | 0 | mLayerManager = nullptr; |
720 | 0 |
|
721 | 0 | // It is safe to call DestroyeCompositor several times (here and |
722 | 0 | // in the parent class) since it will take effect only once. |
723 | 0 | // The reason we call it here is because on gtk platforms we need |
724 | 0 | // to destroy the compositor before we destroy the gdk window (which |
725 | 0 | // destroys the the gl context attached to it). |
726 | 0 | DestroyCompositor(); |
727 | 0 |
|
728 | 0 | #ifdef MOZ_X11 |
729 | 0 | // Ensure any resources assigned to the window get cleaned up first |
730 | 0 | // to avoid double-freeing. |
731 | 0 | mSurfaceProvider.CleanupResources(); |
732 | 0 | #endif |
733 | 0 |
|
734 | 0 | ClearCachedResources(); |
735 | 0 |
|
736 | 0 | g_signal_handlers_disconnect_by_func(gtk_settings_get_default(), |
737 | 0 | FuncToGpointer(settings_changed_cb), |
738 | 0 | this); |
739 | 0 |
|
740 | 0 | nsIRollupListener* rollupListener = nsBaseWidget::GetActiveRollupListener(); |
741 | 0 | if (rollupListener) { |
742 | 0 | nsCOMPtr<nsIWidget> rollupWidget = rollupListener->GetRollupWidget(); |
743 | 0 | if (static_cast<nsIWidget *>(this) == rollupWidget) { |
744 | 0 | rollupListener->Rollup(0, false, nullptr, nullptr); |
745 | 0 | } |
746 | 0 | } |
747 | 0 |
|
748 | 0 | // dragService will be null after shutdown of the service manager. |
749 | 0 | RefPtr<nsDragService> dragService = nsDragService::GetInstance(); |
750 | 0 | if (dragService && this == dragService->GetMostRecentDestWindow()) { |
751 | 0 | dragService->ScheduleLeaveEvent(); |
752 | 0 | } |
753 | 0 |
|
754 | 0 | NativeShow(false); |
755 | 0 |
|
756 | 0 | if (mIMContext) { |
757 | 0 | mIMContext->OnDestroyWindow(this); |
758 | 0 | } |
759 | 0 |
|
760 | 0 | // make sure that we remove ourself as the focus window |
761 | 0 | if (gFocusWindow == this) { |
762 | 0 | LOGFOCUS(("automatically losing focus...\n")); |
763 | 0 | gFocusWindow = nullptr; |
764 | 0 | } |
765 | 0 |
|
766 | 0 | GtkWidget *owningWidget = GetMozContainerWidget(); |
767 | 0 | if (mShell) { |
768 | 0 | gtk_widget_destroy(mShell); |
769 | 0 | mShell = nullptr; |
770 | 0 | mContainer = nullptr; |
771 | 0 | MOZ_ASSERT(!mGdkWindow, |
772 | 0 | "mGdkWindow should be NULL when mContainer is destroyed"); |
773 | 0 | } |
774 | 0 | else if (mContainer) { |
775 | 0 | gtk_widget_destroy(GTK_WIDGET(mContainer)); |
776 | 0 | mContainer = nullptr; |
777 | 0 | MOZ_ASSERT(!mGdkWindow, |
778 | 0 | "mGdkWindow should be NULL when mContainer is destroyed"); |
779 | 0 | } |
780 | 0 | else if (mGdkWindow) { |
781 | 0 | // Destroy child windows to ensure that their mThebesSurfaces are |
782 | 0 | // released and to remove references from GdkWindows back to their |
783 | 0 | // container widget. (OnContainerUnrealize() does this when the |
784 | 0 | // MozContainer widget is destroyed.) |
785 | 0 | DestroyChildWindows(); |
786 | 0 |
|
787 | 0 | gdk_window_set_user_data(mGdkWindow, nullptr); |
788 | 0 | g_object_set_data(G_OBJECT(mGdkWindow), "nsWindow", nullptr); |
789 | 0 | gdk_window_destroy(mGdkWindow); |
790 | 0 | mGdkWindow = nullptr; |
791 | 0 | } |
792 | 0 |
|
793 | 0 | if (gInvisibleContainer && owningWidget == gInvisibleContainer) { |
794 | 0 | CheckDestroyInvisibleContainer(); |
795 | 0 | } |
796 | 0 |
|
797 | 0 | #ifdef ACCESSIBILITY |
798 | 0 | if (mRootAccessible) { |
799 | 0 | mRootAccessible = nullptr; |
800 | 0 | } |
801 | 0 | #endif |
802 | 0 |
|
803 | 0 | // Save until last because OnDestroy() may cause us to be deleted. |
804 | 0 | OnDestroy(); |
805 | 0 | } |
806 | | |
807 | | nsIWidget * |
808 | | nsWindow::GetParent(void) |
809 | 0 | { |
810 | 0 | return mParent; |
811 | 0 | } |
812 | | |
813 | | float |
814 | | nsWindow::GetDPI() |
815 | 0 | { |
816 | 0 | float dpi = 96.0f; |
817 | 0 | nsCOMPtr<nsIScreen> screen = GetWidgetScreen(); |
818 | 0 | if (screen) { |
819 | 0 | screen->GetDpi(&dpi); |
820 | 0 | } |
821 | 0 | return dpi; |
822 | 0 | } |
823 | | |
824 | | double |
825 | | nsWindow::GetDefaultScaleInternal() |
826 | 0 | { |
827 | 0 | return GdkScaleFactor() * gfxPlatformGtk::GetFontScaleFactor(); |
828 | 0 | } |
829 | | |
830 | | DesktopToLayoutDeviceScale |
831 | | nsWindow::GetDesktopToDeviceScale() |
832 | 0 | { |
833 | | #ifdef MOZ_WAYLAND |
834 | | GdkDisplay* gdkDisplay = gdk_display_get_default(); |
835 | | if (GDK_IS_WAYLAND_DISPLAY(gdkDisplay)) { |
836 | | return DesktopToLayoutDeviceScale(GdkScaleFactor()); |
837 | | } |
838 | | #endif |
839 | |
|
840 | 0 | // In Gtk/X11, we manage windows using device pixels. |
841 | 0 | return DesktopToLayoutDeviceScale(1.0); |
842 | 0 | } |
843 | | |
844 | | void |
845 | | nsWindow::SetParent(nsIWidget *aNewParent) |
846 | 0 | { |
847 | 0 | if (!mGdkWindow) { |
848 | 0 | MOZ_ASSERT_UNREACHABLE("The native window has already been destroyed"); |
849 | 0 | return; |
850 | 0 | } |
851 | 0 |
|
852 | 0 | if (mContainer) { |
853 | 0 | // FIXME bug 1469183 |
854 | 0 | NS_ERROR("nsWindow should not have a container here"); |
855 | 0 | return; |
856 | 0 | } |
857 | 0 |
|
858 | 0 | nsCOMPtr<nsIWidget> kungFuDeathGrip = this; |
859 | 0 | if (mParent) { |
860 | 0 | mParent->RemoveChild(this); |
861 | 0 | } |
862 | 0 |
|
863 | 0 | mParent = aNewParent; |
864 | 0 |
|
865 | 0 | GtkWidget* oldContainer = GetMozContainerWidget(); |
866 | 0 | if (!oldContainer) { |
867 | 0 | // The GdkWindows have been destroyed so there is nothing else to |
868 | 0 | // reparent. |
869 | 0 | MOZ_ASSERT(gdk_window_is_destroyed(mGdkWindow), |
870 | 0 | "live GdkWindow with no widget"); |
871 | 0 | return; |
872 | 0 | } |
873 | 0 |
|
874 | 0 | if (aNewParent) { |
875 | 0 | aNewParent->AddChild(this); |
876 | 0 | ReparentNativeWidget(aNewParent); |
877 | 0 | } else { |
878 | 0 | // aNewParent is nullptr, but reparent to a hidden window to avoid |
879 | 0 | // destroying the GdkWindow and its descendants. |
880 | 0 | // An invisible container widget is needed to hold descendant |
881 | 0 | // GtkWidgets. |
882 | 0 | GtkWidget* newContainer = EnsureInvisibleContainer(); |
883 | 0 | GdkWindow* newParentWindow = gtk_widget_get_window(newContainer); |
884 | 0 | ReparentNativeWidgetInternal(aNewParent, newContainer, newParentWindow, |
885 | 0 | oldContainer); |
886 | 0 | } |
887 | 0 | } |
888 | | |
889 | | bool |
890 | | nsWindow::WidgetTypeSupportsAcceleration() |
891 | 0 | { |
892 | 0 | return !IsSmallPopup(); |
893 | 0 | } |
894 | | |
895 | | void |
896 | | nsWindow::ReparentNativeWidget(nsIWidget* aNewParent) |
897 | 0 | { |
898 | 0 | MOZ_ASSERT(aNewParent, "null widget"); |
899 | 0 | NS_ASSERTION(!mIsDestroyed, ""); |
900 | 0 | NS_ASSERTION(!static_cast<nsWindow*>(aNewParent)->mIsDestroyed, ""); |
901 | 0 |
|
902 | 0 | GtkWidget* oldContainer = GetMozContainerWidget(); |
903 | 0 | if (!oldContainer) { |
904 | 0 | // The GdkWindows have been destroyed so there is nothing else to |
905 | 0 | // reparent. |
906 | 0 | MOZ_ASSERT(gdk_window_is_destroyed(mGdkWindow), |
907 | 0 | "live GdkWindow with no widget"); |
908 | 0 | return; |
909 | 0 | } |
910 | 0 | MOZ_ASSERT(!gdk_window_is_destroyed(mGdkWindow), |
911 | 0 | "destroyed GdkWindow with widget"); |
912 | 0 |
|
913 | 0 | auto* newParent = static_cast<nsWindow*>(aNewParent); |
914 | 0 | GdkWindow* newParentWindow = newParent->mGdkWindow; |
915 | 0 | GtkWidget* newContainer = newParent->GetMozContainerWidget(); |
916 | 0 | GtkWindow* shell = GTK_WINDOW(mShell); |
917 | 0 |
|
918 | 0 | if (shell && gtk_window_get_transient_for(shell)) { |
919 | 0 | GtkWindow* topLevelParent = |
920 | 0 | GTK_WINDOW(gtk_widget_get_toplevel(newContainer)); |
921 | 0 | gtk_window_set_transient_for(shell, topLevelParent); |
922 | 0 | } |
923 | 0 |
|
924 | 0 | ReparentNativeWidgetInternal(aNewParent, newContainer, newParentWindow, |
925 | 0 | oldContainer); |
926 | 0 | } |
927 | | |
928 | | void |
929 | | nsWindow::ReparentNativeWidgetInternal(nsIWidget* aNewParent, |
930 | | GtkWidget* aNewContainer, |
931 | | GdkWindow* aNewParentWindow, |
932 | | GtkWidget* aOldContainer) |
933 | 0 | { |
934 | 0 | if (!aNewContainer) { |
935 | 0 | // The new parent GdkWindow has been destroyed. |
936 | 0 | MOZ_ASSERT(!aNewParentWindow || |
937 | 0 | gdk_window_is_destroyed(aNewParentWindow), |
938 | 0 | "live GdkWindow with no widget"); |
939 | 0 | Destroy(); |
940 | 0 | } else { |
941 | 0 | if (aNewContainer != aOldContainer) { |
942 | 0 | MOZ_ASSERT(!gdk_window_is_destroyed(aNewParentWindow), |
943 | 0 | "destroyed GdkWindow with widget"); |
944 | 0 | SetWidgetForHierarchy(mGdkWindow, aOldContainer, aNewContainer); |
945 | 0 |
|
946 | 0 | if (aOldContainer == gInvisibleContainer) { |
947 | 0 | CheckDestroyInvisibleContainer(); |
948 | 0 | } |
949 | 0 | } |
950 | 0 |
|
951 | 0 | if (!mIsTopLevel) { |
952 | 0 | gdk_window_reparent(mGdkWindow, aNewParentWindow, |
953 | 0 | DevicePixelsToGdkCoordRoundDown(mBounds.x), |
954 | 0 | DevicePixelsToGdkCoordRoundDown(mBounds.y)); |
955 | 0 | } |
956 | 0 | } |
957 | 0 |
|
958 | 0 | auto* newParent = static_cast<nsWindow*>(aNewParent); |
959 | 0 | bool parentHasMappedToplevel = |
960 | 0 | newParent && newParent->mHasMappedToplevel; |
961 | 0 | if (mHasMappedToplevel != parentHasMappedToplevel) { |
962 | 0 | SetHasMappedToplevel(parentHasMappedToplevel); |
963 | 0 | } |
964 | 0 | } |
965 | | |
966 | | void |
967 | | nsWindow::SetModal(bool aModal) |
968 | 0 | { |
969 | 0 | LOG(("nsWindow::SetModal [%p] %d\n", (void *)this, aModal)); |
970 | 0 | if (mIsDestroyed) |
971 | 0 | return; |
972 | 0 | if (!mIsTopLevel || !mShell) |
973 | 0 | return; |
974 | 0 | gtk_window_set_modal(GTK_WINDOW(mShell), aModal ? TRUE : FALSE); |
975 | 0 | } |
976 | | |
977 | | // nsIWidget method, which means IsShown. |
978 | | bool |
979 | | nsWindow::IsVisible() const |
980 | 0 | { |
981 | 0 | return mIsShown; |
982 | 0 | } |
983 | | |
984 | | void |
985 | | nsWindow::RegisterTouchWindow() |
986 | 0 | { |
987 | 0 | #if GTK_CHECK_VERSION(3,4,0) |
988 | 0 | mHandleTouchEvent = true; |
989 | 0 | mTouches.Clear(); |
990 | 0 | #endif |
991 | 0 | } |
992 | | |
993 | | void |
994 | | nsWindow::ConstrainPosition(bool aAllowSlop, int32_t *aX, int32_t *aY) |
995 | 0 | { |
996 | 0 | if (!mIsTopLevel || !mShell) |
997 | 0 | return; |
998 | 0 | |
999 | 0 | double dpiScale = GetDefaultScale().scale; |
1000 | 0 |
|
1001 | 0 | // we need to use the window size in logical screen pixels |
1002 | 0 | int32_t logWidth = std::max(NSToIntRound(mBounds.width / dpiScale), 1); |
1003 | 0 | int32_t logHeight = std::max(NSToIntRound(mBounds.height / dpiScale), 1); |
1004 | 0 |
|
1005 | 0 | /* get our playing field. use the current screen, or failing that |
1006 | 0 | for any reason, use device caps for the default screen. */ |
1007 | 0 | nsCOMPtr<nsIScreen> screen; |
1008 | 0 | nsCOMPtr<nsIScreenManager> screenmgr = do_GetService("@mozilla.org/gfx/screenmanager;1"); |
1009 | 0 | if (screenmgr) { |
1010 | 0 | screenmgr->ScreenForRect(*aX, *aY, logWidth, logHeight, |
1011 | 0 | getter_AddRefs(screen)); |
1012 | 0 | } |
1013 | 0 |
|
1014 | 0 | // We don't have any screen so leave the coordinates as is |
1015 | 0 | if (!screen) |
1016 | 0 | return; |
1017 | 0 | |
1018 | 0 | nsIntRect screenRect; |
1019 | 0 | if (mSizeMode != nsSizeMode_Fullscreen) { |
1020 | 0 | // For normalized windows, use the desktop work area. |
1021 | 0 | screen->GetAvailRectDisplayPix(&screenRect.x, &screenRect.y, |
1022 | 0 | &screenRect.width, &screenRect.height); |
1023 | 0 | } else { |
1024 | 0 | // For full screen windows, use the desktop. |
1025 | 0 | screen->GetRectDisplayPix(&screenRect.x, &screenRect.y, |
1026 | 0 | &screenRect.width, &screenRect.height); |
1027 | 0 | } |
1028 | 0 |
|
1029 | 0 | if (aAllowSlop) { |
1030 | 0 | if (*aX < screenRect.x - logWidth + kWindowPositionSlop) |
1031 | 0 | *aX = screenRect.x - logWidth + kWindowPositionSlop; |
1032 | 0 | else if (*aX >= screenRect.XMost() - kWindowPositionSlop) |
1033 | 0 | *aX = screenRect.XMost() - kWindowPositionSlop; |
1034 | 0 |
|
1035 | 0 | if (*aY < screenRect.y - logHeight + kWindowPositionSlop) |
1036 | 0 | *aY = screenRect.y - logHeight + kWindowPositionSlop; |
1037 | 0 | else if (*aY >= screenRect.YMost() - kWindowPositionSlop) |
1038 | 0 | *aY = screenRect.YMost() - kWindowPositionSlop; |
1039 | 0 | } else { |
1040 | 0 | if (*aX < screenRect.x) |
1041 | 0 | *aX = screenRect.x; |
1042 | 0 | else if (*aX >= screenRect.XMost() - logWidth) |
1043 | 0 | *aX = screenRect.XMost() - logWidth; |
1044 | 0 |
|
1045 | 0 | if (*aY < screenRect.y) |
1046 | 0 | *aY = screenRect.y; |
1047 | 0 | else if (*aY >= screenRect.YMost() - logHeight) |
1048 | 0 | *aY = screenRect.YMost() - logHeight; |
1049 | 0 | } |
1050 | 0 | } |
1051 | | |
1052 | | void nsWindow::SetSizeConstraints(const SizeConstraints& aConstraints) |
1053 | 0 | { |
1054 | 0 | mSizeConstraints.mMinSize = GetSafeWindowSize(aConstraints.mMinSize); |
1055 | 0 | mSizeConstraints.mMaxSize = GetSafeWindowSize(aConstraints.mMaxSize); |
1056 | 0 |
|
1057 | 0 | if (mShell) { |
1058 | 0 | GdkGeometry geometry; |
1059 | 0 | geometry.min_width = DevicePixelsToGdkCoordRoundUp( |
1060 | 0 | mSizeConstraints.mMinSize.width); |
1061 | 0 | geometry.min_height = DevicePixelsToGdkCoordRoundUp( |
1062 | 0 | mSizeConstraints.mMinSize.height); |
1063 | 0 | geometry.max_width = DevicePixelsToGdkCoordRoundDown( |
1064 | 0 | mSizeConstraints.mMaxSize.width); |
1065 | 0 | geometry.max_height = DevicePixelsToGdkCoordRoundDown( |
1066 | 0 | mSizeConstraints.mMaxSize.height); |
1067 | 0 |
|
1068 | 0 | uint32_t hints = 0; |
1069 | 0 | if (aConstraints.mMinSize != LayoutDeviceIntSize(0, 0)) { |
1070 | 0 | hints |= GDK_HINT_MIN_SIZE; |
1071 | 0 | } |
1072 | 0 | if (aConstraints.mMaxSize != |
1073 | 0 | LayoutDeviceIntSize(NS_MAXSIZE, NS_MAXSIZE)) { |
1074 | 0 | hints |= GDK_HINT_MAX_SIZE; |
1075 | 0 | } |
1076 | 0 | gtk_window_set_geometry_hints(GTK_WINDOW(mShell), nullptr, |
1077 | 0 | &geometry, GdkWindowHints(hints)); |
1078 | 0 | } |
1079 | 0 | } |
1080 | | |
1081 | | void |
1082 | | nsWindow::Show(bool aState) |
1083 | 0 | { |
1084 | 0 | if (aState == mIsShown) |
1085 | 0 | return; |
1086 | 0 | |
1087 | 0 | // Clear our cached resources when the window is hidden. |
1088 | 0 | if (mIsShown && !aState) { |
1089 | 0 | ClearCachedResources(); |
1090 | 0 | } |
1091 | 0 |
|
1092 | 0 | mIsShown = aState; |
1093 | 0 |
|
1094 | 0 | LOG(("nsWindow::Show [%p] state %d\n", (void *)this, aState)); |
1095 | 0 |
|
1096 | 0 | if (aState) { |
1097 | 0 | // Now that this window is shown, mHasMappedToplevel needs to be |
1098 | 0 | // tracked on viewable descendants. |
1099 | 0 | SetHasMappedToplevel(mHasMappedToplevel); |
1100 | 0 | } |
1101 | 0 |
|
1102 | 0 | // Ok, someone called show on a window that isn't sized to a sane |
1103 | 0 | // value. Mark this window as needing to have Show() called on it |
1104 | 0 | // and return. |
1105 | 0 | if ((aState && !AreBoundsSane()) || !mCreated) { |
1106 | 0 | LOG(("\tbounds are insane or window hasn't been created yet\n")); |
1107 | 0 | mNeedsShow = true; |
1108 | 0 | return; |
1109 | 0 | } |
1110 | 0 |
|
1111 | 0 | // If someone is hiding this widget, clear any needing show flag. |
1112 | 0 | if (!aState) |
1113 | 0 | mNeedsShow = false; |
1114 | 0 |
|
1115 | 0 | #ifdef ACCESSIBILITY |
1116 | 0 | if (aState && a11y::ShouldA11yBeEnabled()) |
1117 | 0 | CreateRootAccessible(); |
1118 | 0 | #endif |
1119 | 0 |
|
1120 | 0 | NativeShow(aState); |
1121 | 0 | } |
1122 | | |
1123 | | void |
1124 | | nsWindow::Resize(double aWidth, double aHeight, bool aRepaint) |
1125 | 0 | { |
1126 | 0 | double scale = BoundsUseDesktopPixels() ? GetDesktopToDeviceScale().scale : 1.0; |
1127 | 0 | int32_t width = NSToIntRound(scale * aWidth); |
1128 | 0 | int32_t height = NSToIntRound(scale * aHeight); |
1129 | 0 | ConstrainSize(&width, &height); |
1130 | 0 |
|
1131 | 0 | // For top-level windows, aWidth and aHeight should possibly be |
1132 | 0 | // interpreted as frame bounds, but NativeResize treats these as window |
1133 | 0 | // bounds (Bug 581866). |
1134 | 0 |
|
1135 | 0 | mBounds.SizeTo(width, height); |
1136 | 0 |
|
1137 | 0 | if (!mCreated) |
1138 | 0 | return; |
1139 | 0 | |
1140 | 0 | NativeResize(); |
1141 | 0 |
|
1142 | 0 | NotifyRollupGeometryChange(); |
1143 | 0 |
|
1144 | 0 | // send a resize notification if this is a toplevel |
1145 | 0 | if (mIsTopLevel || mListenForResizes) { |
1146 | 0 | DispatchResized(); |
1147 | 0 | } |
1148 | 0 | } |
1149 | | |
1150 | | void |
1151 | | nsWindow::Resize(double aX, double aY, double aWidth, double aHeight, |
1152 | | bool aRepaint) |
1153 | 0 | { |
1154 | 0 | double scale = BoundsUseDesktopPixels() ? GetDesktopToDeviceScale().scale : 1.0; |
1155 | 0 | int32_t width = NSToIntRound(scale * aWidth); |
1156 | 0 | int32_t height = NSToIntRound(scale * aHeight); |
1157 | 0 | ConstrainSize(&width, &height); |
1158 | 0 |
|
1159 | 0 | int32_t x = NSToIntRound(scale * aX); |
1160 | 0 | int32_t y = NSToIntRound(scale * aY); |
1161 | 0 | mBounds.x = x; |
1162 | 0 | mBounds.y = y; |
1163 | 0 | mBounds.SizeTo(width, height); |
1164 | 0 |
|
1165 | 0 | if (!mCreated) |
1166 | 0 | return; |
1167 | 0 | |
1168 | 0 | NativeMoveResize(); |
1169 | 0 |
|
1170 | 0 | NotifyRollupGeometryChange(); |
1171 | 0 |
|
1172 | 0 | if (mIsTopLevel || mListenForResizes) { |
1173 | 0 | DispatchResized(); |
1174 | 0 | } |
1175 | 0 | } |
1176 | | |
1177 | | void |
1178 | | nsWindow::Enable(bool aState) |
1179 | 0 | { |
1180 | 0 | mEnabled = aState; |
1181 | 0 | } |
1182 | | |
1183 | | bool |
1184 | | nsWindow::IsEnabled() const |
1185 | 0 | { |
1186 | 0 | return mEnabled; |
1187 | 0 | } |
1188 | | |
1189 | | void |
1190 | | nsWindow::Move(double aX, double aY) |
1191 | 0 | { |
1192 | 0 | LOG(("nsWindow::Move [%p] %f %f\n", (void *)this, |
1193 | 0 | aX, aY)); |
1194 | 0 |
|
1195 | 0 | double scale = BoundsUseDesktopPixels() ? GetDesktopToDeviceScale().scale : 1.0; |
1196 | 0 | int32_t x = NSToIntRound(aX * scale); |
1197 | 0 | int32_t y = NSToIntRound(aY * scale); |
1198 | 0 |
|
1199 | 0 | if (mWindowType == eWindowType_toplevel || |
1200 | 0 | mWindowType == eWindowType_dialog) { |
1201 | 0 | SetSizeMode(nsSizeMode_Normal); |
1202 | 0 | } |
1203 | 0 |
|
1204 | 0 | // Since a popup window's x/y coordinates are in relation to to |
1205 | 0 | // the parent, the parent might have moved so we always move a |
1206 | 0 | // popup window. |
1207 | 0 | if (x == mBounds.x && y == mBounds.y && |
1208 | 0 | mWindowType != eWindowType_popup) |
1209 | 0 | return; |
1210 | 0 | |
1211 | 0 | // XXX Should we do some AreBoundsSane check here? |
1212 | 0 | |
1213 | 0 | mBounds.x = x; |
1214 | 0 | mBounds.y = y; |
1215 | 0 |
|
1216 | 0 | if (!mCreated) |
1217 | 0 | return; |
1218 | 0 | |
1219 | 0 | NativeMove(); |
1220 | 0 |
|
1221 | 0 | NotifyRollupGeometryChange(); |
1222 | 0 | } |
1223 | | |
1224 | | |
1225 | | void |
1226 | | nsWindow::NativeMove() |
1227 | 0 | { |
1228 | 0 | GdkPoint point = DevicePixelsToGdkPointRoundDown(mBounds.TopLeft()); |
1229 | 0 |
|
1230 | 0 | if (mIsTopLevel) { |
1231 | 0 | gtk_window_move(GTK_WINDOW(mShell), point.x, point.y); |
1232 | 0 | } |
1233 | 0 | else if (mGdkWindow) { |
1234 | 0 | gdk_window_move(mGdkWindow, point.x, point.y); |
1235 | 0 | } |
1236 | 0 | } |
1237 | | |
1238 | | void |
1239 | | nsWindow::SetZIndex(int32_t aZIndex) |
1240 | 0 | { |
1241 | 0 | nsIWidget* oldPrev = GetPrevSibling(); |
1242 | 0 |
|
1243 | 0 | nsBaseWidget::SetZIndex(aZIndex); |
1244 | 0 |
|
1245 | 0 | if (GetPrevSibling() == oldPrev) { |
1246 | 0 | return; |
1247 | 0 | } |
1248 | 0 | |
1249 | 0 | NS_ASSERTION(!mContainer, "Expected Mozilla child widget"); |
1250 | 0 |
|
1251 | 0 | // We skip the nsWindows that don't have mGdkWindows. |
1252 | 0 | // These are probably in the process of being destroyed. |
1253 | 0 |
|
1254 | 0 | if (!GetNextSibling()) { |
1255 | 0 | // We're to be on top. |
1256 | 0 | if (mGdkWindow) |
1257 | 0 | gdk_window_raise(mGdkWindow); |
1258 | 0 | } else { |
1259 | 0 | // All the siblings before us need to be below our widget. |
1260 | 0 | for (nsWindow* w = this; w; |
1261 | 0 | w = static_cast<nsWindow*>(w->GetPrevSibling())) { |
1262 | 0 | if (w->mGdkWindow) |
1263 | 0 | gdk_window_lower(w->mGdkWindow); |
1264 | 0 | } |
1265 | 0 | } |
1266 | 0 | } |
1267 | | |
1268 | | void |
1269 | | nsWindow::SetSizeMode(nsSizeMode aMode) |
1270 | 0 | { |
1271 | 0 | LOG(("nsWindow::SetSizeMode [%p] %d\n", (void *)this, aMode)); |
1272 | 0 |
|
1273 | 0 | // Save the requested state. |
1274 | 0 | nsBaseWidget::SetSizeMode(aMode); |
1275 | 0 |
|
1276 | 0 | // return if there's no shell or our current state is the same as |
1277 | 0 | // the mode we were just set to. |
1278 | 0 | if (!mShell || mSizeState == mSizeMode) { |
1279 | 0 | return; |
1280 | 0 | } |
1281 | 0 | |
1282 | 0 | switch (aMode) { |
1283 | 0 | case nsSizeMode_Maximized: |
1284 | 0 | gtk_window_maximize(GTK_WINDOW(mShell)); |
1285 | 0 | break; |
1286 | 0 | case nsSizeMode_Minimized: |
1287 | 0 | gtk_window_iconify(GTK_WINDOW(mShell)); |
1288 | 0 | break; |
1289 | 0 | case nsSizeMode_Fullscreen: |
1290 | 0 | MakeFullScreen(true); |
1291 | 0 | break; |
1292 | 0 |
|
1293 | 0 | default: |
1294 | 0 | // nsSizeMode_Normal, really. |
1295 | 0 | if (mSizeState == nsSizeMode_Minimized) |
1296 | 0 | gtk_window_deiconify(GTK_WINDOW(mShell)); |
1297 | 0 | else if (mSizeState == nsSizeMode_Maximized) |
1298 | 0 | gtk_window_unmaximize(GTK_WINDOW(mShell)); |
1299 | 0 | break; |
1300 | 0 | } |
1301 | 0 |
|
1302 | 0 | mSizeState = mSizeMode; |
1303 | 0 | } |
1304 | | |
1305 | | typedef void (* SetUserTimeFunc)(GdkWindow* aWindow, guint32 aTimestamp); |
1306 | | |
1307 | | // This will become obsolete when new GTK APIs are widely supported, |
1308 | | // as described here: http://bugzilla.gnome.org/show_bug.cgi?id=347375 |
1309 | | static void |
1310 | | SetUserTimeAndStartupIDForActivatedWindow(GtkWidget* aWindow) |
1311 | 0 | { |
1312 | 0 | nsGTKToolkit* GTKToolkit = nsGTKToolkit::GetToolkit(); |
1313 | 0 | if (!GTKToolkit) |
1314 | 0 | return; |
1315 | 0 | |
1316 | 0 | nsAutoCString desktopStartupID; |
1317 | 0 | GTKToolkit->GetDesktopStartupID(&desktopStartupID); |
1318 | 0 | if (desktopStartupID.IsEmpty()) { |
1319 | 0 | // We don't have the data we need. Fall back to an |
1320 | 0 | // approximation ... using the timestamp of the remote command |
1321 | 0 | // being received as a guess for the timestamp of the user event |
1322 | 0 | // that triggered it. |
1323 | 0 | uint32_t timestamp = GTKToolkit->GetFocusTimestamp(); |
1324 | 0 | if (timestamp) { |
1325 | 0 | gdk_window_focus(gtk_widget_get_window(aWindow), timestamp); |
1326 | 0 | GTKToolkit->SetFocusTimestamp(0); |
1327 | 0 | } |
1328 | 0 | return; |
1329 | 0 | } |
1330 | 0 |
|
1331 | | #if defined(MOZ_ENABLE_STARTUP_NOTIFICATION) |
1332 | | // TODO - Implement for non-X11 Gtk backends (Bug 726479) |
1333 | | if (GDK_IS_X11_DISPLAY(gdk_display_get_default())) { |
1334 | | GdkWindow* gdkWindow = gtk_widget_get_window(aWindow); |
1335 | | |
1336 | | GdkScreen* screen = gdk_window_get_screen(gdkWindow); |
1337 | | SnDisplay* snd = |
1338 | | sn_display_new(gdk_x11_display_get_xdisplay(gdk_window_get_display(gdkWindow)), |
1339 | | nullptr, nullptr); |
1340 | | if (!snd) |
1341 | | return; |
1342 | | SnLauncheeContext* ctx = |
1343 | | sn_launchee_context_new(snd, gdk_screen_get_number(screen), |
1344 | | desktopStartupID.get()); |
1345 | | if (!ctx) { |
1346 | | sn_display_unref(snd); |
1347 | | return; |
1348 | | } |
1349 | | |
1350 | | if (sn_launchee_context_get_id_has_timestamp(ctx)) { |
1351 | | gdk_x11_window_set_user_time(gdkWindow, |
1352 | | sn_launchee_context_get_timestamp(ctx)); |
1353 | | } |
1354 | | |
1355 | | sn_launchee_context_setup_window(ctx, gdk_x11_window_get_xid(gdkWindow)); |
1356 | | sn_launchee_context_complete(ctx); |
1357 | | |
1358 | | sn_launchee_context_unref(ctx); |
1359 | | sn_display_unref(snd); |
1360 | | } |
1361 | | #endif |
1362 | | |
1363 | 0 | // If we used the startup ID, that already contains the focus timestamp; |
1364 | 0 | // we don't want to reuse the timestamp next time we raise the window |
1365 | 0 | GTKToolkit->SetFocusTimestamp(0); |
1366 | 0 | GTKToolkit->SetDesktopStartupID(EmptyCString()); |
1367 | 0 | } |
1368 | | |
1369 | | /* static */ guint32 |
1370 | | nsWindow::GetLastUserInputTime() |
1371 | 0 | { |
1372 | 0 | // gdk_x11_display_get_user_time/gtk_get_current_event_time tracks |
1373 | 0 | // button and key presses, DESKTOP_STARTUP_ID used to start the app, |
1374 | 0 | // drop events from external drags, |
1375 | 0 | // WM_DELETE_WINDOW delete events, but not usually mouse motion nor |
1376 | 0 | // button and key releases. Therefore use the most recent of |
1377 | 0 | // gdk_x11_display_get_user_time and the last time that we have seen. |
1378 | 0 | GdkDisplay* gdkDisplay = gdk_display_get_default(); |
1379 | 0 | guint32 timestamp = GDK_IS_X11_DISPLAY(gdkDisplay) ? |
1380 | 0 | gdk_x11_display_get_user_time(gdkDisplay) : |
1381 | 0 | gtk_get_current_event_time(); |
1382 | 0 |
|
1383 | 0 | if (sLastUserInputTime != GDK_CURRENT_TIME && |
1384 | 0 | TimestampIsNewerThan(sLastUserInputTime, timestamp)) { |
1385 | 0 | return sLastUserInputTime; |
1386 | 0 | } |
1387 | 0 | |
1388 | 0 | return timestamp; |
1389 | 0 | } |
1390 | | |
1391 | | nsresult |
1392 | | nsWindow::SetFocus(bool aRaise) |
1393 | 0 | { |
1394 | 0 | // Make sure that our owning widget has focus. If it doesn't try to |
1395 | 0 | // grab it. Note that we don't set our focus flag in this case. |
1396 | 0 |
|
1397 | 0 | LOGFOCUS((" SetFocus %d [%p]\n", aRaise, (void *)this)); |
1398 | 0 |
|
1399 | 0 | GtkWidget *owningWidget = GetMozContainerWidget(); |
1400 | 0 | if (!owningWidget) |
1401 | 0 | return NS_ERROR_FAILURE; |
1402 | 0 | |
1403 | 0 | // Raise the window if someone passed in true and the prefs are |
1404 | 0 | // set properly. |
1405 | 0 | GtkWidget *toplevelWidget = gtk_widget_get_toplevel(owningWidget); |
1406 | 0 |
|
1407 | 0 | if (gRaiseWindows && aRaise && toplevelWidget && |
1408 | 0 | !gtk_widget_has_focus(owningWidget) && |
1409 | 0 | !gtk_widget_has_focus(toplevelWidget)) { |
1410 | 0 | GtkWidget* top_window = GetToplevelWidget(); |
1411 | 0 | if (top_window && (gtk_widget_get_visible(top_window))) |
1412 | 0 | { |
1413 | 0 | gdk_window_show_unraised(gtk_widget_get_window(top_window)); |
1414 | 0 | // Unset the urgency hint if possible. |
1415 | 0 | SetUrgencyHint(top_window, false); |
1416 | 0 | } |
1417 | 0 | } |
1418 | 0 |
|
1419 | 0 | RefPtr<nsWindow> owningWindow = get_window_for_gtk_widget(owningWidget); |
1420 | 0 | if (!owningWindow) |
1421 | 0 | return NS_ERROR_FAILURE; |
1422 | 0 | |
1423 | 0 | if (aRaise) { |
1424 | 0 | // aRaise == true means request toplevel activation. |
1425 | 0 |
|
1426 | 0 | // This is asynchronous. |
1427 | 0 | // If and when the window manager accepts the request, then the focus |
1428 | 0 | // widget will get a focus-in-event signal. |
1429 | 0 | if (gRaiseWindows && owningWindow->mIsShown && owningWindow->mShell && |
1430 | 0 | !gtk_window_is_active(GTK_WINDOW(owningWindow->mShell))) { |
1431 | 0 |
|
1432 | 0 | uint32_t timestamp = GDK_CURRENT_TIME; |
1433 | 0 |
|
1434 | 0 | nsGTKToolkit* GTKToolkit = nsGTKToolkit::GetToolkit(); |
1435 | 0 | if (GTKToolkit) |
1436 | 0 | timestamp = GTKToolkit->GetFocusTimestamp(); |
1437 | 0 |
|
1438 | 0 | LOGFOCUS((" requesting toplevel activation [%p]\n", (void *)this)); |
1439 | 0 | NS_ASSERTION(owningWindow->mWindowType != eWindowType_popup |
1440 | 0 | || mParent, |
1441 | 0 | "Presenting an override-redirect window"); |
1442 | 0 | gtk_window_present_with_time(GTK_WINDOW(owningWindow->mShell), timestamp); |
1443 | 0 |
|
1444 | 0 | if (GTKToolkit) |
1445 | 0 | GTKToolkit->SetFocusTimestamp(0); |
1446 | 0 | } |
1447 | 0 |
|
1448 | 0 | return NS_OK; |
1449 | 0 | } |
1450 | 0 |
|
1451 | 0 | // aRaise == false means that keyboard events should be dispatched |
1452 | 0 | // from this widget. |
1453 | 0 |
|
1454 | 0 | // Ensure owningWidget is the focused GtkWidget within its toplevel window. |
1455 | 0 | // |
1456 | 0 | // For eWindowType_popup, this GtkWidget may not actually be the one that |
1457 | 0 | // receives the key events as it may be the parent window that is active. |
1458 | 0 | if (!gtk_widget_is_focus(owningWidget)) { |
1459 | 0 | // This is synchronous. It takes focus from a plugin or from a widget |
1460 | 0 | // in an embedder. The focus manager already knows that this window |
1461 | 0 | // is active so gBlockActivateEvent avoids another (unnecessary) |
1462 | 0 | // activate notification. |
1463 | 0 | gBlockActivateEvent = true; |
1464 | 0 | gtk_widget_grab_focus(owningWidget); |
1465 | 0 | gBlockActivateEvent = false; |
1466 | 0 | } |
1467 | 0 |
|
1468 | 0 | // If this is the widget that already has focus, return. |
1469 | 0 | if (gFocusWindow == this) { |
1470 | 0 | LOGFOCUS((" already have focus [%p]\n", (void *)this)); |
1471 | 0 | return NS_OK; |
1472 | 0 | } |
1473 | 0 |
|
1474 | 0 | // Set this window to be the focused child window |
1475 | 0 | gFocusWindow = this; |
1476 | 0 |
|
1477 | 0 | if (mIMContext) { |
1478 | 0 | mIMContext->OnFocusWindow(this); |
1479 | 0 | } |
1480 | 0 |
|
1481 | 0 | LOGFOCUS((" widget now has focus in SetFocus() [%p]\n", |
1482 | 0 | (void *)this)); |
1483 | 0 |
|
1484 | 0 | return NS_OK; |
1485 | 0 | } |
1486 | | |
1487 | | LayoutDeviceIntRect |
1488 | | nsWindow::GetScreenBounds() |
1489 | 0 | { |
1490 | 0 | LayoutDeviceIntRect rect; |
1491 | 0 | if (mIsTopLevel && mContainer) { |
1492 | 0 | // use the point including window decorations |
1493 | 0 | gint x, y; |
1494 | 0 | gdk_window_get_root_origin(gtk_widget_get_window(GTK_WIDGET(mContainer)), &x, &y); |
1495 | 0 | rect.MoveTo(GdkPointToDevicePixels({ x, y })); |
1496 | 0 | } else { |
1497 | 0 | rect.MoveTo(WidgetToScreenOffset()); |
1498 | 0 | } |
1499 | 0 | // mBounds.Size() is the window bounds, not the window-manager frame |
1500 | 0 | // bounds (bug 581863). gdk_window_get_frame_extents would give the |
1501 | 0 | // frame bounds, but mBounds.Size() is returned here for consistency |
1502 | 0 | // with Resize. |
1503 | 0 | rect.SizeTo(mBounds.Size()); |
1504 | 0 | LOG(("GetScreenBounds %d,%d | %dx%d\n", |
1505 | 0 | rect.x, rect.y, rect.width, rect.height)); |
1506 | 0 | return rect; |
1507 | 0 | } |
1508 | | |
1509 | | LayoutDeviceIntSize |
1510 | | nsWindow::GetClientSize() |
1511 | 0 | { |
1512 | 0 | return LayoutDeviceIntSize(mBounds.width, mBounds.height); |
1513 | 0 | } |
1514 | | |
1515 | | LayoutDeviceIntRect |
1516 | | nsWindow::GetClientBounds() |
1517 | 0 | { |
1518 | 0 | // GetBounds returns a rect whose top left represents the top left of the |
1519 | 0 | // outer bounds, but whose width/height represent the size of the inner |
1520 | 0 | // bounds (which is messed up). |
1521 | 0 | LayoutDeviceIntRect rect = GetBounds(); |
1522 | 0 | rect.MoveBy(GetClientOffset()); |
1523 | 0 | return rect; |
1524 | 0 | } |
1525 | | |
1526 | | void |
1527 | | nsWindow::UpdateClientOffset() |
1528 | 0 | { |
1529 | 0 | AUTO_PROFILER_LABEL("nsWindow::UpdateClientOffset", OTHER); |
1530 | 0 |
|
1531 | 0 | if (!mIsTopLevel || !mShell || !mIsX11Display || |
1532 | 0 | gtk_window_get_window_type(GTK_WINDOW(mShell)) == GTK_WINDOW_POPUP) { |
1533 | 0 | mClientOffset = nsIntPoint(0, 0); |
1534 | 0 | return; |
1535 | 0 | } |
1536 | 0 | |
1537 | 0 | GdkAtom cardinal_atom = gdk_x11_xatom_to_atom(XA_CARDINAL); |
1538 | 0 |
|
1539 | 0 | GdkAtom type_returned; |
1540 | 0 | int format_returned; |
1541 | 0 | int length_returned; |
1542 | 0 | long *frame_extents; |
1543 | 0 |
|
1544 | 0 | if (!gdk_property_get(gtk_widget_get_window(mShell), |
1545 | 0 | gdk_atom_intern ("_NET_FRAME_EXTENTS", FALSE), |
1546 | 0 | cardinal_atom, |
1547 | 0 | 0, // offset |
1548 | 0 | 4*4, // length |
1549 | 0 | FALSE, // delete |
1550 | 0 | &type_returned, |
1551 | 0 | &format_returned, |
1552 | 0 | &length_returned, |
1553 | 0 | (guchar **) &frame_extents) || |
1554 | 0 | length_returned/sizeof(glong) != 4) { |
1555 | 0 | mClientOffset = nsIntPoint(0, 0); |
1556 | 0 | return; |
1557 | 0 | } |
1558 | 0 | |
1559 | 0 | // data returned is in the order left, right, top, bottom |
1560 | 0 | auto left = int32_t(frame_extents[0]); |
1561 | 0 | auto top = int32_t(frame_extents[2]); |
1562 | 0 |
|
1563 | 0 | g_free(frame_extents); |
1564 | 0 |
|
1565 | 0 | mClientOffset = nsIntPoint(left, top); |
1566 | 0 | } |
1567 | | |
1568 | | LayoutDeviceIntPoint |
1569 | | nsWindow::GetClientOffset() |
1570 | 0 | { |
1571 | 0 | return LayoutDeviceIntPoint::FromUnknownPoint(mClientOffset); |
1572 | 0 | } |
1573 | | |
1574 | | gboolean |
1575 | | nsWindow::OnPropertyNotifyEvent(GtkWidget* aWidget, GdkEventProperty* aEvent) |
1576 | | |
1577 | 0 | { |
1578 | 0 | if (aEvent->atom == gdk_atom_intern("_NET_FRAME_EXTENTS", FALSE)) { |
1579 | 0 | UpdateClientOffset(); |
1580 | 0 |
|
1581 | 0 | // Send a WindowMoved notification. This ensures that TabParent |
1582 | 0 | // picks up the new client offset and sends it to the child process |
1583 | 0 | // if appropriate. |
1584 | 0 | NotifyWindowMoved(mBounds.x, mBounds.y); |
1585 | 0 | return FALSE; |
1586 | 0 | } |
1587 | 0 | |
1588 | 0 | if (GetCurrentTimeGetter()->PropertyNotifyHandler(aWidget, aEvent)) { |
1589 | 0 | return TRUE; |
1590 | 0 | } |
1591 | 0 | |
1592 | 0 | return FALSE; |
1593 | 0 | } |
1594 | | |
1595 | | void |
1596 | | nsWindow::SetCursor(nsCursor aCursor) |
1597 | 0 | { |
1598 | 0 | // if we're not the toplevel window pass up the cursor request to |
1599 | 0 | // the toplevel window to handle it. |
1600 | 0 | if (!mContainer && mGdkWindow) { |
1601 | 0 | nsWindow *window = GetContainerWindow(); |
1602 | 0 | if (!window) |
1603 | 0 | return; |
1604 | 0 | |
1605 | 0 | window->SetCursor(aCursor); |
1606 | 0 | return; |
1607 | 0 | } |
1608 | 0 | |
1609 | 0 | // Only change cursor if it's actually been changed |
1610 | 0 | if (aCursor != mCursor || mUpdateCursor) { |
1611 | 0 | GdkCursor *newCursor = nullptr; |
1612 | 0 | mUpdateCursor = false; |
1613 | 0 |
|
1614 | 0 | newCursor = get_gtk_cursor(aCursor); |
1615 | 0 |
|
1616 | 0 | if (nullptr != newCursor) { |
1617 | 0 | mCursor = aCursor; |
1618 | 0 |
|
1619 | 0 | if (!mContainer) |
1620 | 0 | return; |
1621 | 0 | |
1622 | 0 | gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(mContainer)), newCursor); |
1623 | 0 | } |
1624 | 0 | } |
1625 | 0 | } |
1626 | | |
1627 | | nsresult |
1628 | | nsWindow::SetCursor(imgIContainer* aCursor, |
1629 | | uint32_t aHotspotX, uint32_t aHotspotY) |
1630 | 0 | { |
1631 | 0 | // if we're not the toplevel window pass up the cursor request to |
1632 | 0 | // the toplevel window to handle it. |
1633 | 0 | if (!mContainer && mGdkWindow) { |
1634 | 0 | nsWindow *window = GetContainerWindow(); |
1635 | 0 | if (!window) |
1636 | 0 | return NS_ERROR_FAILURE; |
1637 | 0 | |
1638 | 0 | return window->SetCursor(aCursor, aHotspotX, aHotspotY); |
1639 | 0 | } |
1640 | 0 | |
1641 | 0 | mCursor = eCursorInvalid; |
1642 | 0 |
|
1643 | 0 | // Get the image's current frame |
1644 | 0 | GdkPixbuf* pixbuf = nsImageToPixbuf::ImageToPixbuf(aCursor); |
1645 | 0 | if (!pixbuf) |
1646 | 0 | return NS_ERROR_NOT_AVAILABLE; |
1647 | 0 | |
1648 | 0 | int width = gdk_pixbuf_get_width(pixbuf); |
1649 | 0 | int height = gdk_pixbuf_get_height(pixbuf); |
1650 | 0 | // Reject cursors greater than 128 pixels in some direction, to prevent |
1651 | 0 | // spoofing. |
1652 | 0 | // XXX ideally we should rescale. Also, we could modify the API to |
1653 | 0 | // allow trusted content to set larger cursors. |
1654 | 0 | if (width > 128 || height > 128) { |
1655 | 0 | g_object_unref(pixbuf); |
1656 | 0 | return NS_ERROR_NOT_AVAILABLE; |
1657 | 0 | } |
1658 | 0 | |
1659 | 0 | // Looks like all cursors need an alpha channel (tested on Gtk 2.4.4). This |
1660 | 0 | // is of course not documented anywhere... |
1661 | 0 | // So add one if there isn't one yet |
1662 | 0 | if (!gdk_pixbuf_get_has_alpha(pixbuf)) { |
1663 | 0 | GdkPixbuf* alphaBuf = gdk_pixbuf_add_alpha(pixbuf, FALSE, 0, 0, 0); |
1664 | 0 | g_object_unref(pixbuf); |
1665 | 0 | if (!alphaBuf) { |
1666 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
1667 | 0 | } |
1668 | 0 | pixbuf = alphaBuf; |
1669 | 0 | } |
1670 | 0 |
|
1671 | 0 | GdkCursor* cursor = gdk_cursor_new_from_pixbuf(gdk_display_get_default(), |
1672 | 0 | pixbuf, |
1673 | 0 | aHotspotX, aHotspotY); |
1674 | 0 | g_object_unref(pixbuf); |
1675 | 0 | nsresult rv = NS_ERROR_OUT_OF_MEMORY; |
1676 | 0 | if (cursor) { |
1677 | 0 | if (mContainer) { |
1678 | 0 | gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(mContainer)), cursor); |
1679 | 0 | rv = NS_OK; |
1680 | 0 | } |
1681 | 0 | g_object_unref(cursor); |
1682 | 0 | } |
1683 | 0 |
|
1684 | 0 | return rv; |
1685 | 0 | } |
1686 | | |
1687 | | void |
1688 | | nsWindow::Invalidate(const LayoutDeviceIntRect& aRect) |
1689 | 0 | { |
1690 | 0 | if (!mGdkWindow) |
1691 | 0 | return; |
1692 | 0 | |
1693 | 0 | GdkRectangle rect = DevicePixelsToGdkRectRoundOut(aRect); |
1694 | 0 | gdk_window_invalidate_rect(mGdkWindow, &rect, FALSE); |
1695 | 0 |
|
1696 | 0 | LOGDRAW(("Invalidate (rect) [%p]: %d %d %d %d\n", (void *)this, |
1697 | 0 | rect.x, rect.y, rect.width, rect.height)); |
1698 | 0 | } |
1699 | | |
1700 | | void* |
1701 | | nsWindow::GetNativeData(uint32_t aDataType) |
1702 | 0 | { |
1703 | 0 | switch (aDataType) { |
1704 | 0 | case NS_NATIVE_WINDOW: |
1705 | 0 | case NS_NATIVE_WIDGET: { |
1706 | 0 | if (!mGdkWindow) |
1707 | 0 | return nullptr; |
1708 | 0 | |
1709 | 0 | return mGdkWindow; |
1710 | 0 | } |
1711 | 0 |
|
1712 | 0 | case NS_NATIVE_DISPLAY: { |
1713 | 0 | #ifdef MOZ_X11 |
1714 | 0 | GdkDisplay* gdkDisplay = gdk_display_get_default(); |
1715 | 0 | if (GDK_IS_X11_DISPLAY(gdkDisplay)) { |
1716 | 0 | return GDK_DISPLAY_XDISPLAY(gdkDisplay); |
1717 | 0 | } |
1718 | 0 | #endif /* MOZ_X11 */ |
1719 | 0 | // Don't bother to return native display on Wayland as it's for |
1720 | 0 | // X11 only NPAPI plugins. |
1721 | 0 | return nullptr; |
1722 | 0 | } |
1723 | 0 | case NS_NATIVE_SHELLWIDGET: |
1724 | 0 | return GetToplevelWidget(); |
1725 | 0 |
|
1726 | 0 | case NS_NATIVE_SHAREABLE_WINDOW: |
1727 | 0 | if (mIsX11Display) { |
1728 | 0 | return (void *) GDK_WINDOW_XID(gdk_window_get_toplevel(mGdkWindow)); |
1729 | 0 | } |
1730 | 0 | NS_WARNING("nsWindow::GetNativeData(): NS_NATIVE_SHAREABLE_WINDOW is not handled on Wayland!"); |
1731 | 0 | return nullptr; |
1732 | 0 | case NS_RAW_NATIVE_IME_CONTEXT: { |
1733 | 0 | void* pseudoIMEContext = GetPseudoIMEContext(); |
1734 | 0 | if (pseudoIMEContext) { |
1735 | 0 | return pseudoIMEContext; |
1736 | 0 | } |
1737 | 0 | // If IME context isn't available on this widget, we should set |this| |
1738 | 0 | // instead of nullptr. |
1739 | 0 | if (!mIMContext) { |
1740 | 0 | return this; |
1741 | 0 | } |
1742 | 0 | return mIMContext.get(); |
1743 | 0 | } |
1744 | 0 | case NS_NATIVE_OPENGL_CONTEXT: |
1745 | 0 | return nullptr; |
1746 | 0 | #ifdef MOZ_X11 |
1747 | 0 | case NS_NATIVE_COMPOSITOR_DISPLAY: |
1748 | 0 | return gfxPlatformGtk::GetPlatform()->GetCompositorDisplay(); |
1749 | 0 | #endif // MOZ_X11 |
1750 | 0 | case NS_NATIVE_EGL_WINDOW: { |
1751 | 0 | if (mIsX11Display) |
1752 | 0 | return mGdkWindow ? (void*)GDK_WINDOW_XID(mGdkWindow) : nullptr; |
1753 | | #ifdef MOZ_WAYLAND |
1754 | | if (mContainer) |
1755 | | return moz_container_get_wl_egl_window(mContainer); |
1756 | | #endif |
1757 | 0 | return nullptr; |
1758 | 0 | } |
1759 | 0 | default: |
1760 | 0 | NS_WARNING("nsWindow::GetNativeData called with bad value"); |
1761 | 0 | return nullptr; |
1762 | 0 | } |
1763 | 0 | } |
1764 | | |
1765 | | nsresult |
1766 | | nsWindow::SetTitle(const nsAString& aTitle) |
1767 | 0 | { |
1768 | 0 | if (!mShell) |
1769 | 0 | return NS_OK; |
1770 | 0 | |
1771 | 0 | // convert the string into utf8 and set the title. |
1772 | 0 | #define UTF8_FOLLOWBYTE(ch) (((ch) & 0xC0) == 0x80) |
1773 | 0 | NS_ConvertUTF16toUTF8 titleUTF8(aTitle); |
1774 | 0 | if (titleUTF8.Length() > NS_WINDOW_TITLE_MAX_LENGTH) { |
1775 | 0 | // Truncate overlong titles (bug 167315). Make sure we chop after a |
1776 | 0 | // complete sequence by making sure the next char isn't a follow-byte. |
1777 | 0 | uint32_t len = NS_WINDOW_TITLE_MAX_LENGTH; |
1778 | 0 | while(UTF8_FOLLOWBYTE(titleUTF8[len])) |
1779 | 0 | --len; |
1780 | 0 | titleUTF8.Truncate(len); |
1781 | 0 | } |
1782 | 0 | gtk_window_set_title(GTK_WINDOW(mShell), (const char *)titleUTF8.get()); |
1783 | 0 |
|
1784 | 0 | return NS_OK; |
1785 | 0 | } |
1786 | | |
1787 | | void |
1788 | | nsWindow::SetIcon(const nsAString& aIconSpec) |
1789 | 0 | { |
1790 | 0 | if (!mShell) |
1791 | 0 | return; |
1792 | 0 | |
1793 | 0 | nsAutoCString iconName; |
1794 | 0 |
|
1795 | 0 | if (aIconSpec.EqualsLiteral("default")) { |
1796 | 0 | nsAutoString brandName; |
1797 | 0 | WidgetUtils::GetBrandShortName(brandName); |
1798 | 0 | if (brandName.IsEmpty()) { |
1799 | 0 | brandName.AssignLiteral(u"Mozilla"); |
1800 | 0 | } |
1801 | 0 | AppendUTF16toUTF8(brandName, iconName); |
1802 | 0 | ToLowerCase(iconName); |
1803 | 0 | } else { |
1804 | 0 | AppendUTF16toUTF8(aIconSpec, iconName); |
1805 | 0 | } |
1806 | 0 |
|
1807 | 0 | nsCOMPtr<nsIFile> iconFile; |
1808 | 0 | nsAutoCString path; |
1809 | 0 |
|
1810 | 0 | gint *iconSizes = |
1811 | 0 | gtk_icon_theme_get_icon_sizes(gtk_icon_theme_get_default(), |
1812 | 0 | iconName.get()); |
1813 | 0 | bool foundIcon = (iconSizes[0] != 0); |
1814 | 0 | g_free(iconSizes); |
1815 | 0 |
|
1816 | 0 | if (!foundIcon) { |
1817 | 0 | // Look for icons with the following suffixes appended to the base name |
1818 | 0 | // The last two entries (for the old XPM format) will be ignored unless |
1819 | 0 | // no icons are found using other suffixes. XPM icons are deprecated. |
1820 | 0 |
|
1821 | 0 | const char16_t extensions[9][8] = { u".png", u"16.png", u"32.png", |
1822 | 0 | u"48.png", u"64.png", u"128.png", |
1823 | 0 | u"256.png", |
1824 | 0 | u".xpm", u"16.xpm" }; |
1825 | 0 |
|
1826 | 0 | for (uint32_t i = 0; i < ArrayLength(extensions); i++) { |
1827 | 0 | // Don't bother looking for XPM versions if we found a PNG. |
1828 | 0 | if (i == ArrayLength(extensions) - 2 && foundIcon) |
1829 | 0 | break; |
1830 | 0 | |
1831 | 0 | ResolveIconName(aIconSpec, nsDependentString(extensions[i]), |
1832 | 0 | getter_AddRefs(iconFile)); |
1833 | 0 | if (iconFile) { |
1834 | 0 | iconFile->GetNativePath(path); |
1835 | 0 | GdkPixbuf *icon = gdk_pixbuf_new_from_file(path.get(), nullptr); |
1836 | 0 | if (icon) { |
1837 | 0 | gtk_icon_theme_add_builtin_icon(iconName.get(), |
1838 | 0 | gdk_pixbuf_get_height(icon), |
1839 | 0 | icon); |
1840 | 0 | g_object_unref(icon); |
1841 | 0 | foundIcon = true; |
1842 | 0 | } |
1843 | 0 | } |
1844 | 0 | } |
1845 | 0 | } |
1846 | 0 |
|
1847 | 0 | // leave the default icon intact if no matching icons were found |
1848 | 0 | if (foundIcon) { |
1849 | 0 | gtk_window_set_icon_name(GTK_WINDOW(mShell), iconName.get()); |
1850 | 0 | } |
1851 | 0 | } |
1852 | | |
1853 | | |
1854 | | LayoutDeviceIntPoint |
1855 | | nsWindow::WidgetToScreenOffset() |
1856 | 0 | { |
1857 | 0 | gint x = 0, y = 0; |
1858 | 0 |
|
1859 | 0 | if (mGdkWindow) { |
1860 | 0 | gdk_window_get_origin(mGdkWindow, &x, &y); |
1861 | 0 | } |
1862 | 0 |
|
1863 | 0 | return GdkPointToDevicePixels({ x, y }); |
1864 | 0 | } |
1865 | | |
1866 | | void |
1867 | | nsWindow::CaptureMouse(bool aCapture) |
1868 | 0 | { |
1869 | 0 | LOG(("CaptureMouse %p\n", (void *)this)); |
1870 | 0 |
|
1871 | 0 | if (!mGdkWindow) |
1872 | 0 | return; |
1873 | 0 | |
1874 | 0 | if (!mContainer) |
1875 | 0 | return; |
1876 | 0 | |
1877 | 0 | if (aCapture) { |
1878 | 0 | gtk_grab_add(GTK_WIDGET(mContainer)); |
1879 | 0 | GrabPointer(GetLastUserInputTime()); |
1880 | 0 | } |
1881 | 0 | else { |
1882 | 0 | ReleaseGrabs(); |
1883 | 0 | gtk_grab_remove(GTK_WIDGET(mContainer)); |
1884 | 0 | } |
1885 | 0 | } |
1886 | | |
1887 | | void |
1888 | | nsWindow::CaptureRollupEvents(nsIRollupListener *aListener, |
1889 | | bool aDoCapture) |
1890 | 0 | { |
1891 | 0 | if (!mGdkWindow) |
1892 | 0 | return; |
1893 | 0 | |
1894 | 0 | if (!mContainer) |
1895 | 0 | return; |
1896 | 0 | |
1897 | 0 | LOG(("CaptureRollupEvents %p %i\n", this, int(aDoCapture))); |
1898 | 0 |
|
1899 | 0 | if (aDoCapture) { |
1900 | 0 | gRollupListener = aListener; |
1901 | 0 | // Don't add a grab if a drag is in progress, or if the widget is a drag |
1902 | 0 | // feedback popup. (panels with type="drag"). |
1903 | 0 | if (!mIsDragPopup && !nsWindow::DragInProgress()) { |
1904 | 0 | gtk_grab_add(GTK_WIDGET(mContainer)); |
1905 | 0 | GrabPointer(GetLastUserInputTime()); |
1906 | 0 | } |
1907 | 0 | } |
1908 | 0 | else { |
1909 | 0 | if (!nsWindow::DragInProgress()) { |
1910 | 0 | ReleaseGrabs(); |
1911 | 0 | } |
1912 | 0 | // There may not have been a drag in process when aDoCapture was set, |
1913 | 0 | // so make sure to remove any added grab. This is a no-op if the grab |
1914 | 0 | // was not added to this widget. |
1915 | 0 | gtk_grab_remove(GTK_WIDGET(mContainer)); |
1916 | 0 | gRollupListener = nullptr; |
1917 | 0 | } |
1918 | 0 | } |
1919 | | |
1920 | | nsresult |
1921 | | nsWindow::GetAttention(int32_t aCycleCount) |
1922 | 0 | { |
1923 | 0 | LOG(("nsWindow::GetAttention [%p]\n", (void *)this)); |
1924 | 0 |
|
1925 | 0 | GtkWidget* top_window = GetToplevelWidget(); |
1926 | 0 | GtkWidget* top_focused_window = |
1927 | 0 | gFocusWindow ? gFocusWindow->GetToplevelWidget() : nullptr; |
1928 | 0 |
|
1929 | 0 | // Don't get attention if the window is focused anyway. |
1930 | 0 | if (top_window && (gtk_widget_get_visible(top_window)) && |
1931 | 0 | top_window != top_focused_window) { |
1932 | 0 | SetUrgencyHint(top_window, true); |
1933 | 0 | } |
1934 | 0 |
|
1935 | 0 | return NS_OK; |
1936 | 0 | } |
1937 | | |
1938 | | bool |
1939 | | nsWindow::HasPendingInputEvent() |
1940 | 0 | { |
1941 | 0 | // This sucks, but gtk/gdk has no way to answer the question we want while |
1942 | 0 | // excluding paint events, and there's no X API that will let us peek |
1943 | 0 | // without blocking or removing. To prevent event reordering, peek |
1944 | 0 | // anything except expose events. Reordering expose and others should be |
1945 | 0 | // ok, hopefully. |
1946 | 0 | bool haveEvent = false; |
1947 | 0 | #ifdef MOZ_X11 |
1948 | 0 | XEvent ev; |
1949 | 0 | if (mIsX11Display) { |
1950 | 0 | Display *display = GDK_DISPLAY_XDISPLAY(gdk_display_get_default()); |
1951 | 0 | haveEvent = |
1952 | 0 | XCheckMaskEvent(display, |
1953 | 0 | KeyPressMask | KeyReleaseMask | ButtonPressMask | |
1954 | 0 | ButtonReleaseMask | EnterWindowMask | LeaveWindowMask | |
1955 | 0 | PointerMotionMask | PointerMotionHintMask | |
1956 | 0 | Button1MotionMask | Button2MotionMask | |
1957 | 0 | Button3MotionMask | Button4MotionMask | |
1958 | 0 | Button5MotionMask | ButtonMotionMask | KeymapStateMask | |
1959 | 0 | VisibilityChangeMask | StructureNotifyMask | |
1960 | 0 | ResizeRedirectMask | SubstructureNotifyMask | |
1961 | 0 | SubstructureRedirectMask | FocusChangeMask | |
1962 | 0 | PropertyChangeMask | ColormapChangeMask | |
1963 | 0 | OwnerGrabButtonMask, &ev); |
1964 | 0 | if (haveEvent) { |
1965 | 0 | XPutBackEvent(display, &ev); |
1966 | 0 | } |
1967 | 0 | } |
1968 | 0 | #endif |
1969 | 0 | return haveEvent; |
1970 | 0 | } |
1971 | | |
1972 | | #if 0 |
1973 | | #ifdef DEBUG |
1974 | | // Paint flashing code (disabled for cairo - see below) |
1975 | | |
1976 | | #define CAPS_LOCK_IS_ON \ |
1977 | | (KeymapWrapper::AreModifiersCurrentlyActive(KeymapWrapper::CAPS_LOCK)) |
1978 | | |
1979 | | #define WANT_PAINT_FLASHING \ |
1980 | | (debug_WantPaintFlashing() && CAPS_LOCK_IS_ON) |
1981 | | |
1982 | | #ifdef MOZ_X11 |
1983 | | static void |
1984 | | gdk_window_flash(GdkWindow * aGdkWindow, |
1985 | | unsigned int aTimes, |
1986 | | unsigned int aInterval, // Milliseconds |
1987 | | GdkRegion * aRegion) |
1988 | | { |
1989 | | gint x; |
1990 | | gint y; |
1991 | | gint width; |
1992 | | gint height; |
1993 | | guint i; |
1994 | | GdkGC * gc = 0; |
1995 | | GdkColor white; |
1996 | | |
1997 | | gdk_window_get_geometry(aGdkWindow,nullptr,nullptr,&width,&height); |
1998 | | |
1999 | | gdk_window_get_origin (aGdkWindow, |
2000 | | &x, |
2001 | | &y); |
2002 | | |
2003 | | gc = gdk_gc_new(gdk_get_default_root_window()); |
2004 | | |
2005 | | white.pixel = WhitePixel(gdk_display,DefaultScreen(gdk_display)); |
2006 | | |
2007 | | gdk_gc_set_foreground(gc,&white); |
2008 | | gdk_gc_set_function(gc,GDK_XOR); |
2009 | | gdk_gc_set_subwindow(gc,GDK_INCLUDE_INFERIORS); |
2010 | | |
2011 | | gdk_region_offset(aRegion, x, y); |
2012 | | gdk_gc_set_clip_region(gc, aRegion); |
2013 | | |
2014 | | /* |
2015 | | * Need to do this twice so that the XOR effect can replace |
2016 | | * the original window contents. |
2017 | | */ |
2018 | | for (i = 0; i < aTimes * 2; i++) |
2019 | | { |
2020 | | gdk_draw_rectangle(gdk_get_default_root_window(), |
2021 | | gc, |
2022 | | TRUE, |
2023 | | x, |
2024 | | y, |
2025 | | width, |
2026 | | height); |
2027 | | |
2028 | | gdk_flush(); |
2029 | | |
2030 | | PR_Sleep(PR_MillisecondsToInterval(aInterval)); |
2031 | | } |
2032 | | |
2033 | | gdk_gc_destroy(gc); |
2034 | | |
2035 | | gdk_region_offset(aRegion, -x, -y); |
2036 | | } |
2037 | | #endif /* MOZ_X11 */ |
2038 | | #endif // DEBUG |
2039 | | #endif |
2040 | | |
2041 | | # ifdef cairo_copy_clip_rectangle_list |
2042 | | # error "Looks like we're including Mozilla's cairo instead of system cairo" |
2043 | | # endif |
2044 | | static bool |
2045 | | ExtractExposeRegion(LayoutDeviceIntRegion& aRegion, cairo_t* cr) |
2046 | 0 | { |
2047 | 0 | cairo_rectangle_list_t* rects = cairo_copy_clip_rectangle_list(cr); |
2048 | 0 | if (rects->status != CAIRO_STATUS_SUCCESS) { |
2049 | 0 | NS_WARNING("Failed to obtain cairo rectangle list."); |
2050 | 0 | return false; |
2051 | 0 | } |
2052 | 0 |
|
2053 | 0 | for (int i = 0; i < rects->num_rectangles; i++) { |
2054 | 0 | const cairo_rectangle_t& r = rects->rectangles[i]; |
2055 | 0 | aRegion.Or(aRegion, LayoutDeviceIntRect::Truncate(r.x, r.y, r.width, r.height)); |
2056 | 0 | LOGDRAW(("\t%f %f %f %f\n", r.x, r.y, r.width, r.height)); |
2057 | 0 | } |
2058 | 0 |
|
2059 | 0 | cairo_rectangle_list_destroy(rects); |
2060 | 0 | return true; |
2061 | 0 | } |
2062 | | |
2063 | | gboolean |
2064 | | nsWindow::OnExposeEvent(cairo_t *cr) |
2065 | 0 | { |
2066 | 0 | // Send any pending resize events so that layout can update. |
2067 | 0 | // May run event loop. |
2068 | 0 | MaybeDispatchResized(); |
2069 | 0 |
|
2070 | 0 | if (mIsDestroyed) { |
2071 | 0 | return FALSE; |
2072 | 0 | } |
2073 | 0 | |
2074 | 0 | // Windows that are not visible will be painted after they become visible. |
2075 | 0 | if (!mGdkWindow || mIsFullyObscured || !mHasMappedToplevel) |
2076 | 0 | return FALSE; |
2077 | 0 | |
2078 | | #ifdef MOZ_WAYLAND |
2079 | | // Window does not have visible wl_surface yet. |
2080 | | if (!mIsX11Display && !GetWaylandSurface()) |
2081 | | return FALSE; |
2082 | | #endif |
2083 | | |
2084 | 0 | nsIWidgetListener *listener = GetListener(); |
2085 | 0 | if (!listener) |
2086 | 0 | return FALSE; |
2087 | 0 | |
2088 | 0 | LayoutDeviceIntRegion exposeRegion; |
2089 | 0 | if (!ExtractExposeRegion(exposeRegion, cr)) { |
2090 | 0 | return FALSE; |
2091 | 0 | } |
2092 | 0 | |
2093 | 0 | gint scale = GdkScaleFactor(); |
2094 | 0 | LayoutDeviceIntRegion region = exposeRegion; |
2095 | 0 | region.ScaleRoundOut(scale, scale); |
2096 | 0 |
|
2097 | 0 | if (GetLayerManager()->AsKnowsCompositor() && mCompositorSession) { |
2098 | 0 | // We need to paint to the screen even if nothing changed, since if we |
2099 | 0 | // don't have a compositing window manager, our pixels could be stale. |
2100 | 0 | GetLayerManager()->SetNeedsComposite(true); |
2101 | 0 | GetLayerManager()->SendInvalidRegion(region.ToUnknownRegion()); |
2102 | 0 | } |
2103 | 0 |
|
2104 | 0 | RefPtr<nsWindow> strongThis(this); |
2105 | 0 |
|
2106 | 0 | // Dispatch WillPaintWindow notification to allow scripts etc. to run |
2107 | 0 | // before we paint |
2108 | 0 | { |
2109 | 0 | listener->WillPaintWindow(this); |
2110 | 0 |
|
2111 | 0 | // If the window has been destroyed during the will paint notification, |
2112 | 0 | // there is nothing left to do. |
2113 | 0 | if (!mGdkWindow) |
2114 | 0 | return TRUE; |
2115 | 0 | |
2116 | 0 | // Re-get the listener since the will paint notification might have |
2117 | 0 | // killed it. |
2118 | 0 | listener = GetListener(); |
2119 | 0 | if (!listener) |
2120 | 0 | return FALSE; |
2121 | 0 | } |
2122 | 0 | |
2123 | 0 | if (GetLayerManager()->AsKnowsCompositor() && GetLayerManager()->NeedsComposite()) { |
2124 | 0 | GetLayerManager()->ScheduleComposite(); |
2125 | 0 | GetLayerManager()->SetNeedsComposite(false); |
2126 | 0 | } |
2127 | 0 |
|
2128 | 0 | LOGDRAW(("sending expose event [%p] %p 0x%lx (rects follow):\n", |
2129 | 0 | (void *)this, (void *)mGdkWindow, |
2130 | 0 | mIsX11Display ? gdk_x11_window_get_xid(mGdkWindow) : 0)); |
2131 | 0 |
|
2132 | 0 | // Our bounds may have changed after calling WillPaintWindow. Clip |
2133 | 0 | // to the new bounds here. The region is relative to this |
2134 | 0 | // window. |
2135 | 0 | region.And(region, LayoutDeviceIntRect(0, 0, mBounds.width, mBounds.height)); |
2136 | 0 |
|
2137 | 0 | bool shaped = false; |
2138 | 0 | if (eTransparencyTransparent == GetTransparencyMode()) { |
2139 | 0 | if (mHasAlphaVisual) { |
2140 | 0 | // Remove possible shape mask from when window manger was not |
2141 | 0 | // previously compositing. |
2142 | 0 | static_cast<nsWindow*>(GetTopLevelWidget())-> |
2143 | 0 | ClearTransparencyBitmap(); |
2144 | 0 | } else { |
2145 | 0 | shaped = true; |
2146 | 0 | } |
2147 | 0 | } |
2148 | 0 |
|
2149 | 0 | if (!shaped) { |
2150 | 0 | GList *children = |
2151 | 0 | gdk_window_peek_children(mGdkWindow); |
2152 | 0 | while (children) { |
2153 | 0 | GdkWindow *gdkWin = GDK_WINDOW(children->data); |
2154 | 0 | nsWindow *kid = get_window_for_gdk_window(gdkWin); |
2155 | 0 | if (kid && gdk_window_is_visible(gdkWin)) { |
2156 | 0 | AutoTArray<LayoutDeviceIntRect,1> clipRects; |
2157 | 0 | kid->GetWindowClipRegion(&clipRects); |
2158 | 0 | LayoutDeviceIntRect bounds = kid->GetBounds(); |
2159 | 0 | for (uint32_t i = 0; i < clipRects.Length(); ++i) { |
2160 | 0 | LayoutDeviceIntRect r = clipRects[i] + bounds.TopLeft(); |
2161 | 0 | region.Sub(region, r); |
2162 | 0 | } |
2163 | 0 | } |
2164 | 0 | children = children->next; |
2165 | 0 | } |
2166 | 0 | } |
2167 | 0 |
|
2168 | 0 | if (region.IsEmpty()) { |
2169 | 0 | return TRUE; |
2170 | 0 | } |
2171 | 0 | |
2172 | 0 | // If this widget uses OMTC... |
2173 | 0 | if (GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_CLIENT || |
2174 | 0 | GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_WR) { |
2175 | 0 | listener->PaintWindow(this, region); |
2176 | 0 |
|
2177 | 0 | // Re-get the listener since the will paint notification might have |
2178 | 0 | // killed it. |
2179 | 0 | listener = GetListener(); |
2180 | 0 | if (!listener) |
2181 | 0 | return TRUE; |
2182 | 0 | |
2183 | 0 | listener->DidPaintWindow(); |
2184 | 0 | return TRUE; |
2185 | 0 | } |
2186 | 0 | |
2187 | 0 | BufferMode layerBuffering = BufferMode::BUFFERED; |
2188 | 0 | RefPtr<DrawTarget> dt = StartRemoteDrawingInRegion(region, &layerBuffering); |
2189 | 0 | if (!dt || !dt->IsValid()) { |
2190 | 0 | return FALSE; |
2191 | 0 | } |
2192 | 0 | RefPtr<gfxContext> ctx; |
2193 | 0 | IntRect boundsRect = region.GetBounds().ToUnknownRect(); |
2194 | 0 | IntPoint offset(0, 0); |
2195 | 0 | if (dt->GetSize() == boundsRect.Size()) { |
2196 | 0 | offset = boundsRect.TopLeft(); |
2197 | 0 | dt->SetTransform(Matrix::Translation(-offset)); |
2198 | 0 | } |
2199 | 0 |
|
2200 | 0 | #ifdef MOZ_X11 |
2201 | 0 | if (shaped) { |
2202 | 0 | // Collapse update area to the bounding box. This is so we only have to |
2203 | 0 | // call UpdateTranslucentWindowAlpha once. After we have dropped |
2204 | 0 | // support for non-Thebes graphics, UpdateTranslucentWindowAlpha will be |
2205 | 0 | // our private interface so we can rework things to avoid this. |
2206 | 0 | dt->PushClipRect(Rect(boundsRect)); |
2207 | 0 |
|
2208 | 0 | // The double buffering is done here to extract the shape mask. |
2209 | 0 | // (The shape mask won't be necessary when a visual with an alpha |
2210 | 0 | // channel is used on compositing window managers.) |
2211 | 0 | layerBuffering = BufferMode::BUFFER_NONE; |
2212 | 0 | RefPtr<DrawTarget> destDT = dt->CreateSimilarDrawTarget(boundsRect.Size(), SurfaceFormat::B8G8R8A8); |
2213 | 0 | if (!destDT || !destDT->IsValid()) { |
2214 | 0 | return FALSE; |
2215 | 0 | } |
2216 | 0 | destDT->SetTransform(Matrix::Translation(-boundsRect.TopLeft())); |
2217 | 0 | ctx = gfxContext::CreatePreservingTransformOrNull(destDT); |
2218 | 0 | } else { |
2219 | 0 | gfxUtils::ClipToRegion(dt, region.ToUnknownRegion()); |
2220 | 0 | ctx = gfxContext::CreatePreservingTransformOrNull(dt); |
2221 | 0 | } |
2222 | 0 | MOZ_ASSERT(ctx); // checked both dt and destDT valid draw target above |
2223 | 0 |
|
2224 | | #if 0 |
2225 | | // NOTE: Paint flashing region would be wrong for cairo, since |
2226 | | // cairo inflates the update region, etc. So don't paint flash |
2227 | | // for cairo. |
2228 | | #ifdef DEBUG |
2229 | | // XXX aEvent->region may refer to a newly-invalid area. FIXME |
2230 | | if (0 && WANT_PAINT_FLASHING && gtk_widget_get_window(aEvent)) |
2231 | | gdk_window_flash(mGdkWindow, 1, 100, aEvent->region); |
2232 | | #endif |
2233 | | #endif |
2234 | |
|
2235 | 0 | #endif // MOZ_X11 |
2236 | 0 |
|
2237 | 0 | bool painted = false; |
2238 | 0 | { |
2239 | 0 | if (GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_BASIC) { |
2240 | 0 | if (GetTransparencyMode() == eTransparencyTransparent && |
2241 | 0 | layerBuffering == BufferMode::BUFFER_NONE && |
2242 | 0 | mHasAlphaVisual) { |
2243 | 0 | // If our draw target is unbuffered and we use an alpha channel, |
2244 | 0 | // clear the image beforehand to ensure we don't get artifacts from a |
2245 | 0 | // reused SHM image. See bug 1258086. |
2246 | 0 | dt->ClearRect(Rect(boundsRect)); |
2247 | 0 | } |
2248 | 0 | AutoLayerManagerSetup setupLayerManager(this, ctx, layerBuffering); |
2249 | 0 | painted = listener->PaintWindow(this, region); |
2250 | 0 |
|
2251 | 0 | // Re-get the listener since the will paint notification might have |
2252 | 0 | // killed it. |
2253 | 0 | listener = GetListener(); |
2254 | 0 | if (!listener) |
2255 | 0 | return TRUE; |
2256 | 0 | |
2257 | 0 | } |
2258 | 0 | } |
2259 | 0 | |
2260 | 0 | #ifdef MOZ_X11 |
2261 | 0 | // PaintWindow can Destroy us (bug 378273), avoid doing any paint |
2262 | 0 | // operations below if that happened - it will lead to XError and exit(). |
2263 | 0 | if (shaped) { |
2264 | 0 | if (MOZ_LIKELY(!mIsDestroyed)) { |
2265 | 0 | if (painted) { |
2266 | 0 | RefPtr<SourceSurface> surf = ctx->GetDrawTarget()->Snapshot(); |
2267 | 0 |
|
2268 | 0 | UpdateAlpha(surf, boundsRect); |
2269 | 0 |
|
2270 | 0 | dt->DrawSurface(surf, Rect(boundsRect), Rect(0, 0, boundsRect.width, boundsRect.height), |
2271 | 0 | DrawSurfaceOptions(SamplingFilter::POINT), |
2272 | 0 | DrawOptions(1.0f, CompositionOp::OP_SOURCE)); |
2273 | 0 | } |
2274 | 0 | } |
2275 | 0 | } |
2276 | 0 |
|
2277 | 0 | ctx = nullptr; |
2278 | 0 | dt->PopClip(); |
2279 | 0 |
|
2280 | 0 | #endif // MOZ_X11 |
2281 | 0 |
|
2282 | 0 | EndRemoteDrawingInRegion(dt, region); |
2283 | 0 |
|
2284 | 0 | listener->DidPaintWindow(); |
2285 | 0 |
|
2286 | 0 | // Synchronously flush any new dirty areas |
2287 | 0 | cairo_region_t* dirtyArea = gdk_window_get_update_area(mGdkWindow); |
2288 | 0 |
|
2289 | 0 | if (dirtyArea) { |
2290 | 0 | gdk_window_invalidate_region(mGdkWindow, dirtyArea, false); |
2291 | 0 | cairo_region_destroy(dirtyArea); |
2292 | 0 | gdk_window_process_updates(mGdkWindow, false); |
2293 | 0 | } |
2294 | 0 |
|
2295 | 0 | // check the return value! |
2296 | 0 | return TRUE; |
2297 | 0 | } |
2298 | | |
2299 | | void |
2300 | | nsWindow::UpdateAlpha(SourceSurface* aSourceSurface, nsIntRect aBoundsRect) |
2301 | 0 | { |
2302 | 0 | // We need to create our own buffer to force the stride to match the |
2303 | 0 | // expected stride. |
2304 | 0 | int32_t stride = GetAlignedStride<4>(aBoundsRect.width, |
2305 | 0 | BytesPerPixel(SurfaceFormat::A8)); |
2306 | 0 | if (stride == 0) { |
2307 | 0 | return; |
2308 | 0 | } |
2309 | 0 | int32_t bufferSize = stride * aBoundsRect.height; |
2310 | 0 | auto imageBuffer = MakeUniqueFallible<uint8_t[]>(bufferSize); |
2311 | 0 | { |
2312 | 0 | RefPtr<DrawTarget> drawTarget = gfxPlatform::CreateDrawTargetForData( |
2313 | 0 | imageBuffer.get(), |
2314 | 0 | aBoundsRect.Size(), |
2315 | 0 | stride, SurfaceFormat::A8); |
2316 | 0 |
|
2317 | 0 | if (drawTarget) { |
2318 | 0 | drawTarget->DrawSurface(aSourceSurface, Rect(0, 0, aBoundsRect.width, aBoundsRect.height), |
2319 | 0 | Rect(0, 0, aSourceSurface->GetSize().width, aSourceSurface->GetSize().height), |
2320 | 0 | DrawSurfaceOptions(SamplingFilter::POINT), |
2321 | 0 | DrawOptions(1.0f, CompositionOp::OP_SOURCE)); |
2322 | 0 | } |
2323 | 0 | } |
2324 | 0 | UpdateTranslucentWindowAlphaInternal(aBoundsRect, imageBuffer.get(), stride); |
2325 | 0 | } |
2326 | | |
2327 | | gboolean |
2328 | | nsWindow::OnConfigureEvent(GtkWidget *aWidget, GdkEventConfigure *aEvent) |
2329 | 0 | { |
2330 | 0 | // These events are only received on toplevel windows. |
2331 | 0 | // |
2332 | 0 | // GDK ensures that the coordinates are the client window top-left wrt the |
2333 | 0 | // root window. |
2334 | 0 | // |
2335 | 0 | // GDK calculates the cordinates for real ConfigureNotify events on |
2336 | 0 | // managed windows (that would normally be relative to the parent |
2337 | 0 | // window). |
2338 | 0 | // |
2339 | 0 | // Synthetic ConfigureNotify events are from the window manager and |
2340 | 0 | // already relative to the root window. GDK creates all X windows with |
2341 | 0 | // border_width = 0, so synthetic events also indicate the top-left of |
2342 | 0 | // the client window. |
2343 | 0 | // |
2344 | 0 | // Override-redirect windows are children of the root window so parent |
2345 | 0 | // coordinates are root coordinates. |
2346 | 0 |
|
2347 | 0 | LOG(("configure event [%p] %d %d %d %d\n", (void *)this, |
2348 | 0 | aEvent->x, aEvent->y, aEvent->width, aEvent->height)); |
2349 | 0 |
|
2350 | 0 | if (mPendingConfigures > 0) { |
2351 | 0 | mPendingConfigures--; |
2352 | 0 | } |
2353 | 0 |
|
2354 | 0 | LayoutDeviceIntRect screenBounds = GetScreenBounds(); |
2355 | 0 |
|
2356 | 0 | if (mWindowType == eWindowType_toplevel || mWindowType == eWindowType_dialog) { |
2357 | 0 | // This check avoids unwanted rollup on spurious configure events from |
2358 | 0 | // Cygwin/X (bug 672103). |
2359 | 0 | if (mBounds.x != screenBounds.x || |
2360 | 0 | mBounds.y != screenBounds.y) { |
2361 | 0 | CheckForRollup(0, 0, false, true); |
2362 | 0 | } |
2363 | 0 | } |
2364 | 0 |
|
2365 | 0 | // This event indicates that the window position may have changed. |
2366 | 0 | // mBounds.Size() is updated in OnSizeAllocate(). |
2367 | 0 |
|
2368 | 0 | NS_ASSERTION(GTK_IS_WINDOW(aWidget), |
2369 | 0 | "Configure event on widget that is not a GtkWindow"); |
2370 | 0 | if (gtk_window_get_window_type(GTK_WINDOW(aWidget)) == GTK_WINDOW_POPUP) { |
2371 | 0 | // Override-redirect window |
2372 | 0 | // |
2373 | 0 | // These windows should not be moved by the window manager, and so any |
2374 | 0 | // change in position is a result of our direction. mBounds has |
2375 | 0 | // already been set in std::move() or Resize(), and that is more |
2376 | 0 | // up-to-date than the position in the ConfigureNotify event if the |
2377 | 0 | // event is from an earlier window move. |
2378 | 0 | // |
2379 | 0 | // Skipping the WindowMoved call saves context menus from an infinite |
2380 | 0 | // loop when nsXULPopupManager::PopupMoved moves the window to the new |
2381 | 0 | // position and nsMenuPopupFrame::SetPopupPosition adds |
2382 | 0 | // offsetForContextMenu on each iteration. |
2383 | 0 | return FALSE; |
2384 | 0 | } |
2385 | 0 | |
2386 | 0 | mBounds.MoveTo(screenBounds.TopLeft()); |
2387 | 0 |
|
2388 | 0 | // XXX mozilla will invalidate the entire window after this move |
2389 | 0 | // complete. wtf? |
2390 | 0 | NotifyWindowMoved(mBounds.x, mBounds.y); |
2391 | 0 |
|
2392 | 0 | return FALSE; |
2393 | 0 | } |
2394 | | |
2395 | | void |
2396 | | nsWindow::OnContainerUnrealize() |
2397 | 0 | { |
2398 | 0 | // The GdkWindows are about to be destroyed (but not deleted), so remove |
2399 | 0 | // their references back to their container widget while the GdkWindow |
2400 | 0 | // hierarchy is still available. |
2401 | 0 |
|
2402 | 0 | if (mGdkWindow) { |
2403 | 0 | DestroyChildWindows(); |
2404 | 0 |
|
2405 | 0 | g_object_set_data(G_OBJECT(mGdkWindow), "nsWindow", nullptr); |
2406 | 0 | mGdkWindow = nullptr; |
2407 | 0 | } |
2408 | 0 | } |
2409 | | |
2410 | | void |
2411 | | nsWindow::OnSizeAllocate(GtkAllocation *aAllocation) |
2412 | 0 | { |
2413 | 0 | LOG(("size_allocate [%p] %d %d %d %d\n", |
2414 | 0 | (void *)this, aAllocation->x, aAllocation->y, |
2415 | 0 | aAllocation->width, aAllocation->height)); |
2416 | 0 |
|
2417 | 0 | LayoutDeviceIntSize size = GdkRectToDevicePixels(*aAllocation).Size(); |
2418 | 0 |
|
2419 | 0 | if (mBounds.Size() == size) |
2420 | 0 | return; |
2421 | 0 | |
2422 | 0 | // Invalidate the new part of the window now for the pending paint to |
2423 | 0 | // minimize background flashes (GDK does not do this for external resizes |
2424 | 0 | // of toplevels.) |
2425 | 0 | if (mBounds.width < size.width) { |
2426 | 0 | GdkRectangle rect = DevicePixelsToGdkRectRoundOut( |
2427 | 0 | LayoutDeviceIntRect(mBounds.width, 0, |
2428 | 0 | size.width - mBounds.width, size.height)); |
2429 | 0 | gdk_window_invalidate_rect(mGdkWindow, &rect, FALSE); |
2430 | 0 | } |
2431 | 0 | if (mBounds.height < size.height) { |
2432 | 0 | GdkRectangle rect = DevicePixelsToGdkRectRoundOut( |
2433 | 0 | LayoutDeviceIntRect(0, mBounds.height, |
2434 | 0 | size.width, size.height - mBounds.height)); |
2435 | 0 | gdk_window_invalidate_rect(mGdkWindow, &rect, FALSE); |
2436 | 0 | } |
2437 | 0 |
|
2438 | 0 | mBounds.SizeTo(size); |
2439 | 0 |
|
2440 | 0 | #ifdef MOZ_X11 |
2441 | 0 | // Notify the GtkCompositorWidget of a ClientSizeChange |
2442 | 0 | if (mCompositorWidgetDelegate) { |
2443 | 0 | mCompositorWidgetDelegate->NotifyClientSizeChanged(GetClientSize()); |
2444 | 0 | } |
2445 | 0 | #endif |
2446 | 0 |
|
2447 | 0 | // Gecko permits running nested event loops during processing of events, |
2448 | 0 | // GtkWindow callers of gtk_widget_size_allocate expect the signal |
2449 | 0 | // handlers to return sometime in the near future. |
2450 | 0 | mNeedsDispatchResized = true; |
2451 | 0 | NS_DispatchToCurrentThread(NewRunnableMethod( |
2452 | 0 | "nsWindow::MaybeDispatchResized", this, &nsWindow::MaybeDispatchResized)); |
2453 | 0 | } |
2454 | | |
2455 | | void |
2456 | | nsWindow::OnDeleteEvent() |
2457 | 0 | { |
2458 | 0 | if (mWidgetListener) |
2459 | 0 | mWidgetListener->RequestWindowClose(this); |
2460 | 0 | } |
2461 | | |
2462 | | void |
2463 | | nsWindow::OnEnterNotifyEvent(GdkEventCrossing *aEvent) |
2464 | 0 | { |
2465 | 0 | // This skips NotifyVirtual and NotifyNonlinearVirtual enter notify events |
2466 | 0 | // when the pointer enters a child window. If the destination window is a |
2467 | 0 | // Gecko window then we'll catch the corresponding event on that window, |
2468 | 0 | // but we won't notice when the pointer directly enters a foreign (plugin) |
2469 | 0 | // child window without passing over a visible portion of a Gecko window. |
2470 | 0 | if (aEvent->subwindow != nullptr) |
2471 | 0 | return; |
2472 | 0 | |
2473 | 0 | // Check before is_parent_ungrab_enter() as the button state may have |
2474 | 0 | // changed while a non-Gecko ancestor window had a pointer grab. |
2475 | 0 | DispatchMissedButtonReleases(aEvent); |
2476 | 0 |
|
2477 | 0 | if (is_parent_ungrab_enter(aEvent)) |
2478 | 0 | return; |
2479 | 0 | |
2480 | 0 | WidgetMouseEvent event(true, eMouseEnterIntoWidget, this, |
2481 | 0 | WidgetMouseEvent::eReal); |
2482 | 0 |
|
2483 | 0 | event.mRefPoint = GdkEventCoordsToDevicePixels(aEvent->x, aEvent->y); |
2484 | 0 | event.AssignEventTime(GetWidgetEventTime(aEvent->time)); |
2485 | 0 |
|
2486 | 0 | LOG(("OnEnterNotify: %p\n", (void *)this)); |
2487 | 0 |
|
2488 | 0 | DispatchInputEvent(&event); |
2489 | 0 | } |
2490 | | |
2491 | | // XXX Is this the right test for embedding cases? |
2492 | | static bool |
2493 | | is_top_level_mouse_exit(GdkWindow* aWindow, GdkEventCrossing *aEvent) |
2494 | 0 | { |
2495 | 0 | auto x = gint(aEvent->x_root); |
2496 | 0 | auto y = gint(aEvent->y_root); |
2497 | 0 | GdkDisplay* display = gdk_window_get_display(aWindow); |
2498 | 0 | GdkWindow* winAtPt = gdk_display_get_window_at_pointer(display, &x, &y); |
2499 | 0 | if (!winAtPt) |
2500 | 0 | return true; |
2501 | 0 | GdkWindow* topLevelAtPt = gdk_window_get_toplevel(winAtPt); |
2502 | 0 | GdkWindow* topLevelWidget = gdk_window_get_toplevel(aWindow); |
2503 | 0 | return topLevelAtPt != topLevelWidget; |
2504 | 0 | } |
2505 | | |
2506 | | void |
2507 | | nsWindow::OnLeaveNotifyEvent(GdkEventCrossing *aEvent) |
2508 | 0 | { |
2509 | 0 | // This ignores NotifyVirtual and NotifyNonlinearVirtual leave notify |
2510 | 0 | // events when the pointer leaves a child window. If the destination |
2511 | 0 | // window is a Gecko window then we'll catch the corresponding event on |
2512 | 0 | // that window. |
2513 | 0 | // |
2514 | 0 | // XXXkt However, we will miss toplevel exits when the pointer directly |
2515 | 0 | // leaves a foreign (plugin) child window without passing over a visible |
2516 | 0 | // portion of a Gecko window. |
2517 | 0 | if (aEvent->subwindow != nullptr) |
2518 | 0 | return; |
2519 | 0 | |
2520 | 0 | WidgetMouseEvent event(true, eMouseExitFromWidget, this, |
2521 | 0 | WidgetMouseEvent::eReal); |
2522 | 0 |
|
2523 | 0 | event.mRefPoint = GdkEventCoordsToDevicePixels(aEvent->x, aEvent->y); |
2524 | 0 | event.AssignEventTime(GetWidgetEventTime(aEvent->time)); |
2525 | 0 |
|
2526 | 0 | event.mExitFrom = is_top_level_mouse_exit(mGdkWindow, aEvent) |
2527 | 0 | ? WidgetMouseEvent::eTopLevel : WidgetMouseEvent::eChild; |
2528 | 0 |
|
2529 | 0 | LOG(("OnLeaveNotify: %p\n", (void *)this)); |
2530 | 0 |
|
2531 | 0 | DispatchInputEvent(&event); |
2532 | 0 | } |
2533 | | |
2534 | | template <typename Event> static LayoutDeviceIntPoint |
2535 | | GetRefPoint(nsWindow* aWindow, Event* aEvent) |
2536 | 0 | { |
2537 | 0 | if (aEvent->window == aWindow->GetGdkWindow()) { |
2538 | 0 | // we are the window that the event happened on so no need for expensive WidgetToScreenOffset |
2539 | 0 | return aWindow->GdkEventCoordsToDevicePixels(aEvent->x, aEvent->y); |
2540 | 0 | } |
2541 | 0 | // XXX we're never quite sure which GdkWindow the event came from due to our custom bubbling |
2542 | 0 | // in scroll_event_cb(), so use ScreenToWidget to translate the screen root coordinates into |
2543 | 0 | // coordinates relative to this widget. |
2544 | 0 | return aWindow->GdkEventCoordsToDevicePixels( |
2545 | 0 | aEvent->x_root, aEvent->y_root) - aWindow->WidgetToScreenOffset(); |
2546 | 0 | } Unexecuted instantiation: nsWindow.cpp:mozilla::gfx::IntPointTyped<mozilla::LayoutDevicePixel> GetRefPoint<_GdkEventMotion>(nsWindow*, _GdkEventMotion*) Unexecuted instantiation: nsWindow.cpp:mozilla::gfx::IntPointTyped<mozilla::LayoutDevicePixel> GetRefPoint<_GdkEventButton>(nsWindow*, _GdkEventButton*) Unexecuted instantiation: nsWindow.cpp:mozilla::gfx::IntPointTyped<mozilla::LayoutDevicePixel> GetRefPoint<_GdkEventScroll>(nsWindow*, _GdkEventScroll*) Unexecuted instantiation: nsWindow.cpp:mozilla::gfx::IntPointTyped<mozilla::LayoutDevicePixel> GetRefPoint<_GdkEventTouch>(nsWindow*, _GdkEventTouch*) |
2547 | | |
2548 | | void |
2549 | | nsWindow::OnMotionNotifyEvent(GdkEventMotion *aEvent) |
2550 | 0 | { |
2551 | 0 | // see if we can compress this event |
2552 | 0 | // XXXldb Why skip every other motion event when we have multiple, |
2553 | 0 | // but not more than that? |
2554 | 0 | bool synthEvent = false; |
2555 | 0 | #ifdef MOZ_X11 |
2556 | 0 | XEvent xevent; |
2557 | 0 |
|
2558 | 0 | if (mIsX11Display) { |
2559 | 0 | while (XPending (GDK_WINDOW_XDISPLAY(aEvent->window))) { |
2560 | 0 | XEvent peeked; |
2561 | 0 | XPeekEvent (GDK_WINDOW_XDISPLAY(aEvent->window), &peeked); |
2562 | 0 | if (peeked.xany.window != gdk_x11_window_get_xid(aEvent->window) |
2563 | 0 | || peeked.type != MotionNotify) |
2564 | 0 | break; |
2565 | 0 | |
2566 | 0 | synthEvent = true; |
2567 | 0 | XNextEvent (GDK_WINDOW_XDISPLAY(aEvent->window), &xevent); |
2568 | 0 | } |
2569 | 0 | } |
2570 | 0 | #endif /* MOZ_X11 */ |
2571 | 0 |
|
2572 | 0 | WidgetMouseEvent event(true, eMouseMove, this, WidgetMouseEvent::eReal); |
2573 | 0 |
|
2574 | 0 | gdouble pressure = 0; |
2575 | 0 | gdk_event_get_axis ((GdkEvent*)aEvent, GDK_AXIS_PRESSURE, &pressure); |
2576 | 0 | // Sometime gdk generate 0 pressure value between normal values |
2577 | 0 | // We have to ignore that and use last valid value |
2578 | 0 | if (pressure) |
2579 | 0 | mLastMotionPressure = pressure; |
2580 | 0 | event.pressure = mLastMotionPressure; |
2581 | 0 |
|
2582 | 0 | guint modifierState; |
2583 | 0 | if (synthEvent) { |
2584 | 0 | #ifdef MOZ_X11 |
2585 | 0 | event.mRefPoint.x = nscoord(xevent.xmotion.x); |
2586 | 0 | event.mRefPoint.y = nscoord(xevent.xmotion.y); |
2587 | 0 |
|
2588 | 0 | modifierState = xevent.xmotion.state; |
2589 | 0 |
|
2590 | 0 | event.AssignEventTime(GetWidgetEventTime(xevent.xmotion.time)); |
2591 | | #else |
2592 | | event.mRefPoint = GdkEventCoordsToDevicePixels(aEvent->x, aEvent->y); |
2593 | | |
2594 | | modifierState = aEvent->state; |
2595 | | |
2596 | | event.AssignEventTime(GetWidgetEventTime(aEvent->time)); |
2597 | | #endif /* MOZ_X11 */ |
2598 | 0 | } else { |
2599 | 0 | event.mRefPoint = GetRefPoint(this, aEvent); |
2600 | 0 |
|
2601 | 0 | modifierState = aEvent->state; |
2602 | 0 |
|
2603 | 0 | event.AssignEventTime(GetWidgetEventTime(aEvent->time)); |
2604 | 0 | } |
2605 | 0 |
|
2606 | 0 | KeymapWrapper::InitInputEvent(event, modifierState); |
2607 | 0 |
|
2608 | 0 | DispatchInputEvent(&event); |
2609 | 0 | } |
2610 | | |
2611 | | // If the automatic pointer grab on ButtonPress has deactivated before |
2612 | | // ButtonRelease, and the mouse button is released while the pointer is not |
2613 | | // over any a Gecko window, then the ButtonRelease event will not be received. |
2614 | | // (A similar situation exists when the pointer is grabbed with owner_events |
2615 | | // True as the ButtonRelease may be received on a foreign [plugin] window). |
2616 | | // Use this method to check for released buttons when the pointer returns to a |
2617 | | // Gecko window. |
2618 | | void |
2619 | | nsWindow::DispatchMissedButtonReleases(GdkEventCrossing *aGdkEvent) |
2620 | 0 | { |
2621 | 0 | guint changed = aGdkEvent->state ^ gButtonState; |
2622 | 0 | // Only consider button releases. |
2623 | 0 | // (Ignore button presses that occurred outside Gecko.) |
2624 | 0 | guint released = changed & gButtonState; |
2625 | 0 | gButtonState = aGdkEvent->state; |
2626 | 0 |
|
2627 | 0 | // Loop over each button, excluding mouse wheel buttons 4 and 5 for which |
2628 | 0 | // GDK ignores releases. |
2629 | 0 | for (guint buttonMask = GDK_BUTTON1_MASK; |
2630 | 0 | buttonMask <= GDK_BUTTON3_MASK; |
2631 | 0 | buttonMask <<= 1) { |
2632 | 0 |
|
2633 | 0 | if (released & buttonMask) { |
2634 | 0 | int16_t buttonType; |
2635 | 0 | switch (buttonMask) { |
2636 | 0 | case GDK_BUTTON1_MASK: |
2637 | 0 | buttonType = WidgetMouseEvent::eLeftButton; |
2638 | 0 | break; |
2639 | 0 | case GDK_BUTTON2_MASK: |
2640 | 0 | buttonType = WidgetMouseEvent::eMiddleButton; |
2641 | 0 | break; |
2642 | 0 | default: |
2643 | 0 | NS_ASSERTION(buttonMask == GDK_BUTTON3_MASK, |
2644 | 0 | "Unexpected button mask"); |
2645 | 0 | buttonType = WidgetMouseEvent::eRightButton; |
2646 | 0 | } |
2647 | 0 |
|
2648 | 0 | LOG(("Synthesized button %u release on %p\n", |
2649 | 0 | guint(buttonType + 1), (void *)this)); |
2650 | 0 |
|
2651 | 0 | // Dispatch a synthesized button up event to tell Gecko about the |
2652 | 0 | // change in state. This event is marked as synthesized so that |
2653 | 0 | // it is not dispatched as a DOM event, because we don't know the |
2654 | 0 | // position, widget, modifiers, or time/order. |
2655 | 0 | WidgetMouseEvent synthEvent(true, eMouseUp, this, |
2656 | 0 | WidgetMouseEvent::eSynthesized); |
2657 | 0 | synthEvent.button = buttonType; |
2658 | 0 | DispatchInputEvent(&synthEvent); |
2659 | 0 | } |
2660 | 0 | } |
2661 | 0 | } |
2662 | | |
2663 | | void |
2664 | | nsWindow::InitButtonEvent(WidgetMouseEvent& aEvent, |
2665 | | GdkEventButton* aGdkEvent) |
2666 | 0 | { |
2667 | 0 | aEvent.mRefPoint = GetRefPoint(this, aGdkEvent); |
2668 | 0 |
|
2669 | 0 | guint modifierState = aGdkEvent->state; |
2670 | 0 | // aEvent's state includes the button state from immediately before this |
2671 | 0 | // event. If aEvent is a mousedown or mouseup event, we need to update |
2672 | 0 | // the button state. |
2673 | 0 | guint buttonMask = 0; |
2674 | 0 | switch (aGdkEvent->button) { |
2675 | 0 | case 1: |
2676 | 0 | buttonMask = GDK_BUTTON1_MASK; |
2677 | 0 | break; |
2678 | 0 | case 2: |
2679 | 0 | buttonMask = GDK_BUTTON2_MASK; |
2680 | 0 | break; |
2681 | 0 | case 3: |
2682 | 0 | buttonMask = GDK_BUTTON3_MASK; |
2683 | 0 | break; |
2684 | 0 | } |
2685 | 0 | if (aGdkEvent->type == GDK_BUTTON_RELEASE) { |
2686 | 0 | modifierState &= ~buttonMask; |
2687 | 0 | } else { |
2688 | 0 | modifierState |= buttonMask; |
2689 | 0 | } |
2690 | 0 |
|
2691 | 0 | KeymapWrapper::InitInputEvent(aEvent, modifierState); |
2692 | 0 |
|
2693 | 0 | aEvent.AssignEventTime(GetWidgetEventTime(aGdkEvent->time)); |
2694 | 0 |
|
2695 | 0 | switch (aGdkEvent->type) { |
2696 | 0 | case GDK_2BUTTON_PRESS: |
2697 | 0 | aEvent.mClickCount = 2; |
2698 | 0 | break; |
2699 | 0 | case GDK_3BUTTON_PRESS: |
2700 | 0 | aEvent.mClickCount = 3; |
2701 | 0 | break; |
2702 | 0 | // default is one click |
2703 | 0 | default: |
2704 | 0 | aEvent.mClickCount = 1; |
2705 | 0 | } |
2706 | 0 | } |
2707 | | |
2708 | | static guint ButtonMaskFromGDKButton(guint button) |
2709 | 0 | { |
2710 | 0 | return GDK_BUTTON1_MASK << (button - 1); |
2711 | 0 | } |
2712 | | |
2713 | | void |
2714 | | nsWindow::DispatchContextMenuEventFromMouseEvent(uint16_t domButton, |
2715 | | GdkEventButton *aEvent) |
2716 | 0 | { |
2717 | 0 | if (domButton == WidgetMouseEvent::eRightButton && MOZ_LIKELY(!mIsDestroyed)) { |
2718 | 0 | WidgetMouseEvent contextMenuEvent(true, eContextMenu, this, |
2719 | 0 | WidgetMouseEvent::eReal); |
2720 | 0 | InitButtonEvent(contextMenuEvent, aEvent); |
2721 | 0 | contextMenuEvent.pressure = mLastMotionPressure; |
2722 | 0 | DispatchInputEvent(&contextMenuEvent); |
2723 | 0 | } |
2724 | 0 | } |
2725 | | |
2726 | | void |
2727 | | nsWindow::OnButtonPressEvent(GdkEventButton *aEvent) |
2728 | 0 | { |
2729 | 0 | LOG(("Button %u press on %p\n", aEvent->button, (void *)this)); |
2730 | 0 |
|
2731 | 0 | // If you double click in GDK, it will actually generate a second |
2732 | 0 | // GDK_BUTTON_PRESS before sending the GDK_2BUTTON_PRESS, and this is |
2733 | 0 | // different than the DOM spec. GDK puts this in the queue |
2734 | 0 | // programatically, so it's safe to assume that if there's a |
2735 | 0 | // double click in the queue, it was generated so we can just drop |
2736 | 0 | // this click. |
2737 | 0 | GdkEvent *peekedEvent = gdk_event_peek(); |
2738 | 0 | if (peekedEvent) { |
2739 | 0 | GdkEventType type = peekedEvent->any.type; |
2740 | 0 | gdk_event_free(peekedEvent); |
2741 | 0 | if (type == GDK_2BUTTON_PRESS || type == GDK_3BUTTON_PRESS) |
2742 | 0 | return; |
2743 | 0 | } |
2744 | 0 | |
2745 | 0 | nsWindow *containerWindow = GetContainerWindow(); |
2746 | 0 | if (!gFocusWindow && containerWindow) { |
2747 | 0 | containerWindow->DispatchActivateEvent(); |
2748 | 0 | } |
2749 | 0 |
|
2750 | 0 | // check to see if we should rollup |
2751 | 0 | if (CheckForRollup(aEvent->x_root, aEvent->y_root, false, false)) |
2752 | 0 | return; |
2753 | 0 | |
2754 | 0 | gdouble pressure = 0; |
2755 | 0 | gdk_event_get_axis ((GdkEvent*)aEvent, GDK_AXIS_PRESSURE, &pressure); |
2756 | 0 | mLastMotionPressure = pressure; |
2757 | 0 |
|
2758 | 0 | uint16_t domButton; |
2759 | 0 | switch (aEvent->button) { |
2760 | 0 | case 1: |
2761 | 0 | domButton = WidgetMouseEvent::eLeftButton; |
2762 | 0 | break; |
2763 | 0 | case 2: |
2764 | 0 | domButton = WidgetMouseEvent::eMiddleButton; |
2765 | 0 | break; |
2766 | 0 | case 3: |
2767 | 0 | domButton = WidgetMouseEvent::eRightButton; |
2768 | 0 | break; |
2769 | 0 | // These are mapped to horizontal scroll |
2770 | 0 | case 6: |
2771 | 0 | case 7: |
2772 | 0 | NS_WARNING("We're not supporting legacy horizontal scroll event"); |
2773 | 0 | return; |
2774 | 0 | // Map buttons 8-9 to back/forward |
2775 | 0 | case 8: |
2776 | 0 | DispatchCommandEvent(nsGkAtoms::Back); |
2777 | 0 | return; |
2778 | 0 | case 9: |
2779 | 0 | DispatchCommandEvent(nsGkAtoms::Forward); |
2780 | 0 | return; |
2781 | 0 | default: |
2782 | 0 | return; |
2783 | 0 | } |
2784 | 0 | |
2785 | 0 | gButtonState |= ButtonMaskFromGDKButton(aEvent->button); |
2786 | 0 |
|
2787 | 0 | WidgetMouseEvent event(true, eMouseDown, this, WidgetMouseEvent::eReal); |
2788 | 0 | event.button = domButton; |
2789 | 0 | InitButtonEvent(event, aEvent); |
2790 | 0 | event.pressure = mLastMotionPressure; |
2791 | 0 |
|
2792 | 0 | DispatchInputEvent(&event); |
2793 | 0 |
|
2794 | 0 | // right menu click on linux should also pop up a context menu |
2795 | 0 | if (!nsBaseWidget::ShowContextMenuAfterMouseUp()) { |
2796 | 0 | DispatchContextMenuEventFromMouseEvent(domButton, aEvent); |
2797 | 0 | } |
2798 | 0 | } |
2799 | | |
2800 | | void |
2801 | | nsWindow::OnButtonReleaseEvent(GdkEventButton *aEvent) |
2802 | 0 | { |
2803 | 0 | LOG(("Button %u release on %p\n", aEvent->button, (void *)this)); |
2804 | 0 |
|
2805 | 0 | uint16_t domButton; |
2806 | 0 | switch (aEvent->button) { |
2807 | 0 | case 1: |
2808 | 0 | domButton = WidgetMouseEvent::eLeftButton; |
2809 | 0 | break; |
2810 | 0 | case 2: |
2811 | 0 | domButton = WidgetMouseEvent::eMiddleButton; |
2812 | 0 | break; |
2813 | 0 | case 3: |
2814 | 0 | domButton = WidgetMouseEvent::eRightButton; |
2815 | 0 | break; |
2816 | 0 | default: |
2817 | 0 | return; |
2818 | 0 | } |
2819 | 0 | |
2820 | 0 | gButtonState &= ~ButtonMaskFromGDKButton(aEvent->button); |
2821 | 0 |
|
2822 | 0 | WidgetMouseEvent event(true, eMouseUp, this, |
2823 | 0 | WidgetMouseEvent::eReal); |
2824 | 0 | event.button = domButton; |
2825 | 0 | InitButtonEvent(event, aEvent); |
2826 | 0 | gdouble pressure = 0; |
2827 | 0 | gdk_event_get_axis ((GdkEvent*)aEvent, GDK_AXIS_PRESSURE, &pressure); |
2828 | 0 | event.pressure = pressure ? pressure : mLastMotionPressure; |
2829 | 0 |
|
2830 | 0 | // The mRefPoint is manipulated in DispatchInputEvent, we're saving it |
2831 | 0 | // to use it for the doubleclick position check. |
2832 | 0 | LayoutDeviceIntPoint pos = event.mRefPoint; |
2833 | 0 |
|
2834 | 0 | nsEventStatus eventStatus = DispatchInputEvent(&event); |
2835 | 0 |
|
2836 | 0 | bool defaultPrevented = (eventStatus == nsEventStatus_eConsumeNoDefault); |
2837 | 0 | // Check if mouse position in titlebar and doubleclick happened to |
2838 | 0 | // trigger restore/maximize. |
2839 | 0 | if (!defaultPrevented |
2840 | 0 | && mDrawInTitlebar |
2841 | 0 | && event.button == WidgetMouseEvent::eLeftButton |
2842 | 0 | && event.mClickCount == 2 |
2843 | 0 | && mDraggableRegion.Contains(pos.x, pos.y)) { |
2844 | 0 |
|
2845 | 0 | if (mSizeState == nsSizeMode_Maximized) { |
2846 | 0 | SetSizeMode(nsSizeMode_Normal); |
2847 | 0 | } else { |
2848 | 0 | SetSizeMode(nsSizeMode_Maximized); |
2849 | 0 | } |
2850 | 0 | } |
2851 | 0 | mLastMotionPressure = pressure; |
2852 | 0 |
|
2853 | 0 | // right menu click on linux should also pop up a context menu |
2854 | 0 | if (nsBaseWidget::ShowContextMenuAfterMouseUp()) { |
2855 | 0 | DispatchContextMenuEventFromMouseEvent(domButton, aEvent); |
2856 | 0 | } |
2857 | 0 | } |
2858 | | |
2859 | | void |
2860 | | nsWindow::OnContainerFocusInEvent(GdkEventFocus *aEvent) |
2861 | 0 | { |
2862 | 0 | LOGFOCUS(("OnContainerFocusInEvent [%p]\n", (void *)this)); |
2863 | 0 |
|
2864 | 0 | // Unset the urgency hint, if possible |
2865 | 0 | GtkWidget* top_window = GetToplevelWidget(); |
2866 | 0 | if (top_window && (gtk_widget_get_visible(top_window))) |
2867 | 0 | SetUrgencyHint(top_window, false); |
2868 | 0 |
|
2869 | 0 | // Return if being called within SetFocus because the focus manager |
2870 | 0 | // already knows that the window is active. |
2871 | 0 | if (gBlockActivateEvent) { |
2872 | 0 | LOGFOCUS(("activated notification is blocked [%p]\n", (void *)this)); |
2873 | 0 | return; |
2874 | 0 | } |
2875 | 0 |
|
2876 | 0 | // If keyboard input will be accepted, the focus manager will call |
2877 | 0 | // SetFocus to set the correct window. |
2878 | 0 | gFocusWindow = nullptr; |
2879 | 0 |
|
2880 | 0 | DispatchActivateEvent(); |
2881 | 0 |
|
2882 | 0 | if (!gFocusWindow) { |
2883 | 0 | // We don't really have a window for dispatching key events, but |
2884 | 0 | // setting a non-nullptr value here prevents OnButtonPressEvent() from |
2885 | 0 | // dispatching an activation notification if the widget is already |
2886 | 0 | // active. |
2887 | 0 | gFocusWindow = this; |
2888 | 0 | } |
2889 | 0 |
|
2890 | 0 | LOGFOCUS(("Events sent from focus in event [%p]\n", (void *)this)); |
2891 | 0 | } |
2892 | | |
2893 | | void |
2894 | | nsWindow::OnContainerFocusOutEvent(GdkEventFocus *aEvent) |
2895 | 0 | { |
2896 | 0 | LOGFOCUS(("OnContainerFocusOutEvent [%p]\n", (void *)this)); |
2897 | 0 |
|
2898 | 0 | if (mWindowType == eWindowType_toplevel || mWindowType == eWindowType_dialog) { |
2899 | 0 | nsCOMPtr<nsIDragService> dragService = do_GetService(kCDragServiceCID); |
2900 | 0 | nsCOMPtr<nsIDragSession> dragSession; |
2901 | 0 | dragService->GetCurrentSession(getter_AddRefs(dragSession)); |
2902 | 0 |
|
2903 | 0 | // Rollup popups when a window is focused out unless a drag is occurring. |
2904 | 0 | // This check is because drags grab the keyboard and cause a focus out on |
2905 | 0 | // versions of GTK before 2.18. |
2906 | 0 | bool shouldRollup = !dragSession; |
2907 | 0 | if (!shouldRollup) { |
2908 | 0 | // we also roll up when a drag is from a different application |
2909 | 0 | nsCOMPtr<nsINode> sourceNode; |
2910 | 0 | dragSession->GetSourceNode(getter_AddRefs(sourceNode)); |
2911 | 0 | shouldRollup = (sourceNode == nullptr); |
2912 | 0 | } |
2913 | 0 |
|
2914 | 0 | if (shouldRollup) { |
2915 | 0 | CheckForRollup(0, 0, false, true); |
2916 | 0 | } |
2917 | 0 | } |
2918 | 0 |
|
2919 | 0 | if (gFocusWindow) { |
2920 | 0 | RefPtr<nsWindow> kungFuDeathGrip = gFocusWindow; |
2921 | 0 | if (gFocusWindow->mIMContext) { |
2922 | 0 | gFocusWindow->mIMContext->OnBlurWindow(gFocusWindow); |
2923 | 0 | } |
2924 | 0 | gFocusWindow = nullptr; |
2925 | 0 | } |
2926 | 0 |
|
2927 | 0 | DispatchDeactivateEvent(); |
2928 | 0 |
|
2929 | 0 | LOGFOCUS(("Done with container focus out [%p]\n", (void *)this)); |
2930 | 0 | } |
2931 | | |
2932 | | bool |
2933 | | nsWindow::DispatchCommandEvent(nsAtom* aCommand) |
2934 | 0 | { |
2935 | 0 | nsEventStatus status; |
2936 | 0 | WidgetCommandEvent appCommandEvent(true, aCommand, this); |
2937 | 0 | DispatchEvent(&appCommandEvent, status); |
2938 | 0 | return TRUE; |
2939 | 0 | } |
2940 | | |
2941 | | bool |
2942 | | nsWindow::DispatchContentCommandEvent(EventMessage aMsg) |
2943 | 0 | { |
2944 | 0 | nsEventStatus status; |
2945 | 0 | WidgetContentCommandEvent event(true, aMsg, this); |
2946 | 0 | DispatchEvent(&event, status); |
2947 | 0 | return TRUE; |
2948 | 0 | } |
2949 | | |
2950 | | static bool |
2951 | | IsCtrlAltTab(GdkEventKey *aEvent) |
2952 | 0 | { |
2953 | 0 | return aEvent->keyval == GDK_Tab && |
2954 | 0 | KeymapWrapper::AreModifiersActive( |
2955 | 0 | KeymapWrapper::CTRL | KeymapWrapper::ALT, aEvent->state); |
2956 | 0 | } |
2957 | | |
2958 | | bool |
2959 | | nsWindow::DispatchKeyDownOrKeyUpEvent(GdkEventKey* aEvent, |
2960 | | bool aIsProcessedByIME, |
2961 | | bool* aIsCancelled) |
2962 | 0 | { |
2963 | 0 | MOZ_ASSERT(aIsCancelled, "aIsCancelled must not be nullptr"); |
2964 | 0 |
|
2965 | 0 | *aIsCancelled = false; |
2966 | 0 |
|
2967 | 0 | if (aEvent->type == GDK_KEY_PRESS && IsCtrlAltTab(aEvent)) { |
2968 | 0 | return false; |
2969 | 0 | } |
2970 | 0 | |
2971 | 0 | EventMessage message = |
2972 | 0 | aEvent->type == GDK_KEY_PRESS ? eKeyDown : eKeyUp; |
2973 | 0 | WidgetKeyboardEvent keyEvent(true, message, this); |
2974 | 0 | KeymapWrapper::InitKeyEvent(keyEvent, aEvent, aIsProcessedByIME); |
2975 | 0 | return DispatchKeyDownOrKeyUpEvent(keyEvent, aIsCancelled); |
2976 | 0 | } |
2977 | | bool |
2978 | | nsWindow::DispatchKeyDownOrKeyUpEvent(WidgetKeyboardEvent& aKeyboardEvent, |
2979 | | bool* aIsCancelled) |
2980 | 0 | { |
2981 | 0 | MOZ_ASSERT(aIsCancelled, "aIsCancelled must not be nullptr"); |
2982 | 0 |
|
2983 | 0 | *aIsCancelled = false; |
2984 | 0 |
|
2985 | 0 | RefPtr<TextEventDispatcher> dispatcher = GetTextEventDispatcher(); |
2986 | 0 | nsresult rv = dispatcher->BeginNativeInputTransaction(); |
2987 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
2988 | 0 | return FALSE; |
2989 | 0 | } |
2990 | 0 | |
2991 | 0 | nsEventStatus status = nsEventStatus_eIgnore; |
2992 | 0 | bool dispatched = |
2993 | 0 | dispatcher->DispatchKeyboardEvent(aKeyboardEvent.mMessage, |
2994 | 0 | aKeyboardEvent, status, nullptr); |
2995 | 0 | *aIsCancelled = (status == nsEventStatus_eConsumeNoDefault); |
2996 | 0 | return dispatched; |
2997 | 0 | } |
2998 | | |
2999 | | WidgetEventTime |
3000 | | nsWindow::GetWidgetEventTime(guint32 aEventTime) |
3001 | 0 | { |
3002 | 0 | return WidgetEventTime(aEventTime, GetEventTimeStamp(aEventTime)); |
3003 | 0 | } |
3004 | | |
3005 | | TimeStamp |
3006 | | nsWindow::GetEventTimeStamp(guint32 aEventTime) |
3007 | 0 | { |
3008 | 0 | if (MOZ_UNLIKELY(!mGdkWindow)) { |
3009 | 0 | // nsWindow has been Destroy()ed. |
3010 | 0 | return TimeStamp::Now(); |
3011 | 0 | } |
3012 | 0 | if (aEventTime == 0) { |
3013 | 0 | // Some X11 and GDK events may be received with a time of 0 to indicate |
3014 | 0 | // that they are synthetic events. Some input method editors do this. |
3015 | 0 | // In this case too, just return the current timestamp. |
3016 | 0 | return TimeStamp::Now(); |
3017 | 0 | } |
3018 | 0 | |
3019 | 0 | TimeStamp eventTimeStamp; |
3020 | 0 |
|
3021 | 0 | if (!mIsX11Display) { |
3022 | 0 | // Wayland compositors use monotonic time to set timestamps. |
3023 | 0 | int64_t timestampTime = g_get_monotonic_time() / 1000; |
3024 | 0 | guint32 refTimeTruncated = guint32(timestampTime); |
3025 | 0 |
|
3026 | 0 | timestampTime -= refTimeTruncated - aEventTime; |
3027 | 0 | int64_t tick = |
3028 | 0 | BaseTimeDurationPlatformUtils::TicksFromMilliseconds(timestampTime); |
3029 | 0 | eventTimeStamp = TimeStamp::FromSystemTime(tick); |
3030 | 0 | } else { |
3031 | 0 | CurrentX11TimeGetter* getCurrentTime = GetCurrentTimeGetter(); |
3032 | 0 | MOZ_ASSERT(getCurrentTime, |
3033 | 0 | "Null current time getter despite having a window"); |
3034 | 0 | eventTimeStamp = TimeConverter().GetTimeStampFromSystemTime(aEventTime, |
3035 | 0 | *getCurrentTime); |
3036 | 0 | } |
3037 | 0 | return eventTimeStamp; |
3038 | 0 | } |
3039 | | |
3040 | | mozilla::CurrentX11TimeGetter* |
3041 | 0 | nsWindow::GetCurrentTimeGetter() { |
3042 | 0 | MOZ_ASSERT(mGdkWindow, "Expected mGdkWindow to be set"); |
3043 | 0 | if (MOZ_UNLIKELY(!mCurrentTimeGetter)) { |
3044 | 0 | mCurrentTimeGetter = MakeUnique<CurrentX11TimeGetter>(mGdkWindow); |
3045 | 0 | } |
3046 | 0 | return mCurrentTimeGetter.get(); |
3047 | 0 | } |
3048 | | |
3049 | | gboolean |
3050 | | nsWindow::OnKeyPressEvent(GdkEventKey *aEvent) |
3051 | 0 | { |
3052 | 0 | LOGFOCUS(("OnKeyPressEvent [%p]\n", (void *)this)); |
3053 | 0 |
|
3054 | 0 | // if we are in the middle of composing text, XIM gets to see it |
3055 | 0 | // before mozilla does. |
3056 | 0 | // FYI: Don't dispatch keydown event before notifying IME of the event |
3057 | 0 | // because IME may send a key event synchronously and consume the |
3058 | 0 | // original event. |
3059 | 0 | bool IMEWasEnabled = false; |
3060 | 0 | if (mIMContext) { |
3061 | 0 | IMEWasEnabled = mIMContext->IsEnabled(); |
3062 | 0 | if (mIMContext->OnKeyEvent(this, aEvent)) { |
3063 | 0 | return TRUE; |
3064 | 0 | } |
3065 | 0 | } |
3066 | 0 | |
3067 | 0 | // work around for annoying things. |
3068 | 0 | if (IsCtrlAltTab(aEvent)) { |
3069 | 0 | return TRUE; |
3070 | 0 | } |
3071 | 0 | |
3072 | 0 | nsCOMPtr<nsIWidget> kungFuDeathGrip = this; |
3073 | 0 |
|
3074 | 0 | // Dispatch keydown event always. At auto repeating, we should send |
3075 | 0 | // KEYDOWN -> KEYPRESS -> KEYDOWN -> KEYPRESS ... -> KEYUP |
3076 | 0 | // However, old distributions (e.g., Ubuntu 9.10) sent native key |
3077 | 0 | // release event, so, on such platform, the DOM events will be: |
3078 | 0 | // KEYDOWN -> KEYPRESS -> KEYUP -> KEYDOWN -> KEYPRESS -> KEYUP... |
3079 | 0 |
|
3080 | 0 | bool isKeyDownCancelled = false; |
3081 | 0 | if (DispatchKeyDownOrKeyUpEvent(aEvent, false, &isKeyDownCancelled) && |
3082 | 0 | (MOZ_UNLIKELY(mIsDestroyed) || isKeyDownCancelled)) { |
3083 | 0 | return TRUE; |
3084 | 0 | } |
3085 | 0 | |
3086 | 0 | // If a keydown event handler causes to enable IME, i.e., it moves |
3087 | 0 | // focus from IME unusable content to IME usable editor, we should |
3088 | 0 | // send the native key event to IME for the first input on the editor. |
3089 | 0 | if (!IMEWasEnabled && mIMContext && mIMContext->IsEnabled()) { |
3090 | 0 | // Notice our keydown event was already dispatched. This prevents |
3091 | 0 | // unnecessary DOM keydown event in the editor. |
3092 | 0 | if (mIMContext->OnKeyEvent(this, aEvent, true)) { |
3093 | 0 | return TRUE; |
3094 | 0 | } |
3095 | 0 | } |
3096 | 0 | |
3097 | 0 | // Look for specialized app-command keys |
3098 | 0 | switch (aEvent->keyval) { |
3099 | 0 | case GDK_Back: |
3100 | 0 | return DispatchCommandEvent(nsGkAtoms::Back); |
3101 | 0 | case GDK_Forward: |
3102 | 0 | return DispatchCommandEvent(nsGkAtoms::Forward); |
3103 | 0 | case GDK_Refresh: |
3104 | 0 | return DispatchCommandEvent(nsGkAtoms::Reload); |
3105 | 0 | case GDK_Stop: |
3106 | 0 | return DispatchCommandEvent(nsGkAtoms::Stop); |
3107 | 0 | case GDK_Search: |
3108 | 0 | return DispatchCommandEvent(nsGkAtoms::Search); |
3109 | 0 | case GDK_Favorites: |
3110 | 0 | return DispatchCommandEvent(nsGkAtoms::Bookmarks); |
3111 | 0 | case GDK_HomePage: |
3112 | 0 | return DispatchCommandEvent(nsGkAtoms::Home); |
3113 | 0 | case GDK_Copy: |
3114 | 0 | case GDK_F16: // F16, F20, F18, F14 are old keysyms for Copy Cut Paste Undo |
3115 | 0 | return DispatchContentCommandEvent(eContentCommandCopy); |
3116 | 0 | case GDK_Cut: |
3117 | 0 | case GDK_F20: |
3118 | 0 | return DispatchContentCommandEvent(eContentCommandCut); |
3119 | 0 | case GDK_Paste: |
3120 | 0 | case GDK_F18: |
3121 | 0 | return DispatchContentCommandEvent(eContentCommandPaste); |
3122 | 0 | case GDK_Redo: |
3123 | 0 | return DispatchContentCommandEvent(eContentCommandRedo); |
3124 | 0 | case GDK_Undo: |
3125 | 0 | case GDK_F14: |
3126 | 0 | return DispatchContentCommandEvent(eContentCommandUndo); |
3127 | 0 | } |
3128 | 0 |
|
3129 | 0 | WidgetKeyboardEvent keypressEvent(true, eKeyPress, this); |
3130 | 0 | KeymapWrapper::InitKeyEvent(keypressEvent, aEvent, false); |
3131 | 0 |
|
3132 | 0 | // before we dispatch a key, check if it's the context menu key. |
3133 | 0 | // If so, send a context menu key event instead. |
3134 | 0 | if (MaybeDispatchContextMenuEvent(aEvent)) { |
3135 | 0 | return TRUE; |
3136 | 0 | } |
3137 | 0 | |
3138 | 0 | RefPtr<TextEventDispatcher> dispatcher = GetTextEventDispatcher(); |
3139 | 0 | nsresult rv = dispatcher->BeginNativeInputTransaction(); |
3140 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
3141 | 0 | return TRUE; |
3142 | 0 | } |
3143 | 0 | |
3144 | 0 | // If the character code is in the BMP, send the key press event. |
3145 | 0 | // Otherwise, send a compositionchange event with the equivalent UTF-16 |
3146 | 0 | // string. |
3147 | 0 | // TODO: Investigate other browser's behavior in this case because |
3148 | 0 | // this hack is odd for UI Events. |
3149 | 0 | nsEventStatus status = nsEventStatus_eIgnore; |
3150 | 0 | if (keypressEvent.mKeyNameIndex != KEY_NAME_INDEX_USE_STRING || |
3151 | 0 | keypressEvent.mKeyValue.Length() == 1) { |
3152 | 0 | dispatcher->MaybeDispatchKeypressEvents(keypressEvent, |
3153 | 0 | status, aEvent); |
3154 | 0 | } else { |
3155 | 0 | WidgetEventTime eventTime = GetWidgetEventTime(aEvent->time); |
3156 | 0 | dispatcher->CommitComposition(status, &keypressEvent.mKeyValue, |
3157 | 0 | &eventTime); |
3158 | 0 | } |
3159 | 0 |
|
3160 | 0 | return TRUE; |
3161 | 0 | } |
3162 | | |
3163 | | bool |
3164 | | nsWindow::MaybeDispatchContextMenuEvent(const GdkEventKey* aEvent) |
3165 | 0 | { |
3166 | 0 | KeyNameIndex keyNameIndex = KeymapWrapper::ComputeDOMKeyNameIndex(aEvent); |
3167 | 0 |
|
3168 | 0 | // Shift+F10 and ContextMenu should cause eContextMenu event. |
3169 | 0 | if (keyNameIndex != KEY_NAME_INDEX_F10 && |
3170 | 0 | keyNameIndex != KEY_NAME_INDEX_ContextMenu) { |
3171 | 0 | return false; |
3172 | 0 | } |
3173 | 0 | |
3174 | 0 | WidgetMouseEvent contextMenuEvent(true, eContextMenu, this, |
3175 | 0 | WidgetMouseEvent::eReal, |
3176 | 0 | WidgetMouseEvent::eContextMenuKey); |
3177 | 0 |
|
3178 | 0 | contextMenuEvent.mRefPoint = LayoutDeviceIntPoint(0, 0); |
3179 | 0 | contextMenuEvent.AssignEventTime(GetWidgetEventTime(aEvent->time)); |
3180 | 0 | contextMenuEvent.mClickCount = 1; |
3181 | 0 | KeymapWrapper::InitInputEvent(contextMenuEvent, aEvent->state); |
3182 | 0 |
|
3183 | 0 | if (contextMenuEvent.IsControl() || contextMenuEvent.IsMeta() || |
3184 | 0 | contextMenuEvent.IsAlt()) { |
3185 | 0 | return false; |
3186 | 0 | } |
3187 | 0 | |
3188 | 0 | // If the key is ContextMenu, then an eContextMenu mouse event is |
3189 | 0 | // dispatched regardless of the state of the Shift modifier. When it is |
3190 | 0 | // pressed without the Shift modifier, a web page can prevent the default |
3191 | 0 | // context menu action. When pressed with the Shift modifier, the web page |
3192 | 0 | // cannot prevent the default context menu action. |
3193 | 0 | // (PresShell::HandleEventInternal() sets mOnlyChromeDispatch to true.) |
3194 | 0 | |
3195 | 0 | // If the key is F10, it needs Shift state because Shift+F10 is well-known |
3196 | 0 | // shortcut key on Linux. However, eContextMenu with Shift state is |
3197 | 0 | // special. It won't fire "contextmenu" event in the web content for |
3198 | 0 | // blocking web page to prevent its default. Therefore, this combination |
3199 | 0 | // should work same as ContextMenu key. |
3200 | 0 | // XXX Should we allow to block web page to prevent its default with |
3201 | 0 | // Ctrl+Shift+F10 or Alt+Shift+F10 instead? |
3202 | 0 | if (keyNameIndex == KEY_NAME_INDEX_F10) { |
3203 | 0 | if (!contextMenuEvent.IsShift()) { |
3204 | 0 | return false; |
3205 | 0 | } |
3206 | 0 | contextMenuEvent.mModifiers &= ~MODIFIER_SHIFT; |
3207 | 0 | } |
3208 | 0 |
|
3209 | 0 | DispatchInputEvent(&contextMenuEvent); |
3210 | 0 | return true; |
3211 | 0 | } |
3212 | | |
3213 | | gboolean |
3214 | | nsWindow::OnKeyReleaseEvent(GdkEventKey* aEvent) |
3215 | 0 | { |
3216 | 0 | LOGFOCUS(("OnKeyReleaseEvent [%p]\n", (void *)this)); |
3217 | 0 |
|
3218 | 0 | if (mIMContext && mIMContext->OnKeyEvent(this, aEvent)) { |
3219 | 0 | return TRUE; |
3220 | 0 | } |
3221 | 0 | |
3222 | 0 | bool isCancelled = false; |
3223 | 0 | if (NS_WARN_IF(!DispatchKeyDownOrKeyUpEvent(aEvent, false, &isCancelled))) { |
3224 | 0 | return FALSE; |
3225 | 0 | } |
3226 | 0 | |
3227 | 0 | return TRUE; |
3228 | 0 | } |
3229 | | |
3230 | | void |
3231 | | nsWindow::OnScrollEvent(GdkEventScroll *aEvent) |
3232 | 0 | { |
3233 | 0 | // check to see if we should rollup |
3234 | 0 | if (CheckForRollup(aEvent->x_root, aEvent->y_root, true, false)) |
3235 | 0 | return; |
3236 | 0 | #if GTK_CHECK_VERSION(3,4,0) |
3237 | 0 | // check for duplicate legacy scroll event, see GNOME bug 726878 |
3238 | 0 | if (aEvent->direction != GDK_SCROLL_SMOOTH && |
3239 | 0 | mLastScrollEventTime == aEvent->time) |
3240 | 0 | return; |
3241 | 0 | #endif |
3242 | 0 | WidgetWheelEvent wheelEvent(true, eWheel, this); |
3243 | 0 | wheelEvent.mDeltaMode = dom::WheelEvent_Binding::DOM_DELTA_LINE; |
3244 | 0 | switch (aEvent->direction) { |
3245 | 0 | #if GTK_CHECK_VERSION(3,4,0) |
3246 | 0 | case GDK_SCROLL_SMOOTH: |
3247 | 0 | { |
3248 | 0 | // As of GTK 3.4, all directional scroll events are provided by |
3249 | 0 | // the GDK_SCROLL_SMOOTH direction on XInput2 devices. |
3250 | 0 | mLastScrollEventTime = aEvent->time; |
3251 | 0 | // TODO - use a more appropriate scrolling unit than lines. |
3252 | 0 | // Multiply event deltas by 3 to emulate legacy behaviour. |
3253 | 0 | wheelEvent.mDeltaX = aEvent->delta_x * 3; |
3254 | 0 | wheelEvent.mDeltaY = aEvent->delta_y * 3; |
3255 | 0 | wheelEvent.mIsNoLineOrPageDelta = true; |
3256 | 0 | // This next step manually unsets smooth scrolling for touch devices |
3257 | 0 | // that trigger GDK_SCROLL_SMOOTH. We use the slave device, which |
3258 | 0 | // represents the actual input. |
3259 | 0 | GdkDevice *device = gdk_event_get_source_device((GdkEvent*)aEvent); |
3260 | 0 | GdkInputSource source = gdk_device_get_source(device); |
3261 | 0 | if (source == GDK_SOURCE_TOUCHSCREEN || |
3262 | 0 | source == GDK_SOURCE_TOUCHPAD) { |
3263 | 0 | wheelEvent.mScrollType = WidgetWheelEvent::SCROLL_ASYNCHRONOUSELY; |
3264 | 0 | } |
3265 | 0 | break; |
3266 | 0 | } |
3267 | 0 | #endif |
3268 | 0 | case GDK_SCROLL_UP: |
3269 | 0 | wheelEvent.mDeltaY = wheelEvent.mLineOrPageDeltaY = -3; |
3270 | 0 | break; |
3271 | 0 | case GDK_SCROLL_DOWN: |
3272 | 0 | wheelEvent.mDeltaY = wheelEvent.mLineOrPageDeltaY = 3; |
3273 | 0 | break; |
3274 | 0 | case GDK_SCROLL_LEFT: |
3275 | 0 | wheelEvent.mDeltaX = wheelEvent.mLineOrPageDeltaX = -1; |
3276 | 0 | break; |
3277 | 0 | case GDK_SCROLL_RIGHT: |
3278 | 0 | wheelEvent.mDeltaX = wheelEvent.mLineOrPageDeltaX = 1; |
3279 | 0 | break; |
3280 | 0 | } |
3281 | 0 | |
3282 | 0 | wheelEvent.mRefPoint = GetRefPoint(this, aEvent); |
3283 | 0 |
|
3284 | 0 | KeymapWrapper::InitInputEvent(wheelEvent, aEvent->state); |
3285 | 0 |
|
3286 | 0 | wheelEvent.AssignEventTime(GetWidgetEventTime(aEvent->time)); |
3287 | 0 |
|
3288 | 0 | DispatchInputEvent(&wheelEvent); |
3289 | 0 | } |
3290 | | |
3291 | | void |
3292 | | nsWindow::OnVisibilityNotifyEvent(GdkEventVisibility *aEvent) |
3293 | 0 | { |
3294 | 0 | LOGDRAW(("Visibility event %i on [%p] %p\n", |
3295 | 0 | aEvent->state, this, aEvent->window)); |
3296 | 0 |
|
3297 | 0 | if (!mGdkWindow) |
3298 | 0 | return; |
3299 | 0 | |
3300 | 0 | switch (aEvent->state) { |
3301 | 0 | case GDK_VISIBILITY_UNOBSCURED: |
3302 | 0 | case GDK_VISIBILITY_PARTIAL: |
3303 | 0 | if (mIsFullyObscured && mHasMappedToplevel) { |
3304 | 0 | // GDK_EXPOSE events have been ignored, so make sure GDK |
3305 | 0 | // doesn't think that the window has already been painted. |
3306 | 0 | gdk_window_invalidate_rect(mGdkWindow, nullptr, FALSE); |
3307 | 0 | } |
3308 | 0 |
|
3309 | 0 | mIsFullyObscured = false; |
3310 | 0 |
|
3311 | 0 | // if we have to retry the grab, retry it. |
3312 | 0 | EnsureGrabs(); |
3313 | 0 | break; |
3314 | 0 | default: // includes GDK_VISIBILITY_FULLY_OBSCURED |
3315 | 0 | mIsFullyObscured = true; |
3316 | 0 | break; |
3317 | 0 | } |
3318 | 0 | } |
3319 | | |
3320 | | void |
3321 | | nsWindow::OnWindowStateEvent(GtkWidget *aWidget, GdkEventWindowState *aEvent) |
3322 | 0 | { |
3323 | 0 | LOG(("nsWindow::OnWindowStateEvent [%p] changed %d new_window_state %d\n", |
3324 | 0 | (void *)this, aEvent->changed_mask, aEvent->new_window_state)); |
3325 | 0 |
|
3326 | 0 | if (IS_MOZ_CONTAINER(aWidget)) { |
3327 | 0 | // This event is notifying the container widget of changes to the |
3328 | 0 | // toplevel window. Just detect changes affecting whether windows are |
3329 | 0 | // viewable. |
3330 | 0 | // |
3331 | 0 | // (A visibility notify event is sent to each window that becomes |
3332 | 0 | // viewable when the toplevel is mapped, but we can't rely on that for |
3333 | 0 | // setting mHasMappedToplevel because these toplevel window state |
3334 | 0 | // events are asynchronous. The windows in the hierarchy now may not |
3335 | 0 | // be the same windows as when the toplevel was mapped, so they may |
3336 | 0 | // not get VisibilityNotify events.) |
3337 | 0 | bool mapped = |
3338 | 0 | !(aEvent->new_window_state & |
3339 | 0 | (GDK_WINDOW_STATE_ICONIFIED|GDK_WINDOW_STATE_WITHDRAWN)); |
3340 | 0 | if (mHasMappedToplevel != mapped) { |
3341 | 0 | SetHasMappedToplevel(mapped); |
3342 | 0 | } |
3343 | 0 | return; |
3344 | 0 | } |
3345 | 0 | // else the widget is a shell widget. |
3346 | 0 |
|
3347 | 0 | // The block below is a bit evil. |
3348 | 0 | // |
3349 | 0 | // When a window is resized before it is shown, gtk_window_resize() delays |
3350 | 0 | // resizes until the window is shown. If gtk_window_state_event() sees a |
3351 | 0 | // GDK_WINDOW_STATE_MAXIMIZED change [1] before the window is shown, then |
3352 | 0 | // gtk_window_compute_configure_request_size() ignores the values from the |
3353 | 0 | // resize [2]. See bug 1449166 for an example of how this could happen. |
3354 | 0 | // |
3355 | 0 | // [1] https://gitlab.gnome.org/GNOME/gtk/blob/3.22.30/gtk/gtkwindow.c#L7967 |
3356 | 0 | // [2] https://gitlab.gnome.org/GNOME/gtk/blob/3.22.30/gtk/gtkwindow.c#L9377 |
3357 | 0 | // |
3358 | 0 | // In order to provide a sensible size for the window when the user exits |
3359 | 0 | // maximized state, we hide the GDK_WINDOW_STATE_MAXIMIZED change from |
3360 | 0 | // gtk_window_state_event() so as to trick GTK into using the values from |
3361 | 0 | // gtk_window_resize() in its configure request. |
3362 | 0 | // |
3363 | 0 | // We instead notify gtk_window_state_event() of the maximized state change |
3364 | 0 | // once the window is shown. |
3365 | 0 | if (!mIsShown) { |
3366 | 0 | aEvent->changed_mask = static_cast<GdkWindowState> |
3367 | 0 | (aEvent->changed_mask & ~GDK_WINDOW_STATE_MAXIMIZED); |
3368 | 0 | } else if (aEvent->changed_mask & GDK_WINDOW_STATE_WITHDRAWN && |
3369 | 0 | aEvent->new_window_state & GDK_WINDOW_STATE_MAXIMIZED) { |
3370 | 0 | aEvent->changed_mask = static_cast<GdkWindowState> |
3371 | 0 | (aEvent->changed_mask | GDK_WINDOW_STATE_MAXIMIZED); |
3372 | 0 | } |
3373 | 0 |
|
3374 | 0 | // We don't care about anything but changes in the maximized/icon/fullscreen |
3375 | 0 | // states |
3376 | 0 | if ((aEvent->changed_mask |
3377 | 0 | & (GDK_WINDOW_STATE_ICONIFIED | |
3378 | 0 | GDK_WINDOW_STATE_MAXIMIZED | |
3379 | 0 | GDK_WINDOW_STATE_FULLSCREEN)) == 0) { |
3380 | 0 | return; |
3381 | 0 | } |
3382 | 0 | |
3383 | 0 | if (aEvent->new_window_state & GDK_WINDOW_STATE_ICONIFIED) { |
3384 | 0 | LOG(("\tIconified\n")); |
3385 | 0 | mSizeState = nsSizeMode_Minimized; |
3386 | 0 | #ifdef ACCESSIBILITY |
3387 | 0 | DispatchMinimizeEventAccessible(); |
3388 | 0 | #endif //ACCESSIBILITY |
3389 | 0 | } |
3390 | 0 | else if (aEvent->new_window_state & GDK_WINDOW_STATE_FULLSCREEN) { |
3391 | 0 | LOG(("\tFullscreen\n")); |
3392 | 0 | mSizeState = nsSizeMode_Fullscreen; |
3393 | 0 | } |
3394 | 0 | else if (aEvent->new_window_state & GDK_WINDOW_STATE_MAXIMIZED) { |
3395 | 0 | LOG(("\tMaximized\n")); |
3396 | 0 | mSizeState = nsSizeMode_Maximized; |
3397 | 0 | #ifdef ACCESSIBILITY |
3398 | 0 | DispatchMaximizeEventAccessible(); |
3399 | 0 | #endif //ACCESSIBILITY |
3400 | 0 | } |
3401 | 0 | else { |
3402 | 0 | LOG(("\tNormal\n")); |
3403 | 0 | mSizeState = nsSizeMode_Normal; |
3404 | 0 | #ifdef ACCESSIBILITY |
3405 | 0 | DispatchRestoreEventAccessible(); |
3406 | 0 | #endif //ACCESSIBILITY |
3407 | 0 | } |
3408 | 0 |
|
3409 | 0 | if (mWidgetListener) { |
3410 | 0 | mWidgetListener->SizeModeChanged(mSizeState); |
3411 | 0 | if (aEvent->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) { |
3412 | 0 | mWidgetListener->FullscreenChanged( |
3413 | 0 | aEvent->new_window_state & GDK_WINDOW_STATE_FULLSCREEN); |
3414 | 0 | } |
3415 | 0 | } |
3416 | 0 |
|
3417 | 0 | if (mDrawInTitlebar && mCSDSupportLevel == CSD_SUPPORT_CLIENT) { |
3418 | 0 | UpdateClientOffsetForCSDWindow(); |
3419 | 0 | } |
3420 | 0 | } |
3421 | | |
3422 | | void |
3423 | | nsWindow::ThemeChanged() |
3424 | 0 | { |
3425 | 0 | NotifyThemeChanged(); |
3426 | 0 |
|
3427 | 0 | if (!mGdkWindow || MOZ_UNLIKELY(mIsDestroyed)) |
3428 | 0 | return; |
3429 | 0 | |
3430 | 0 | // Dispatch theme change notification to all child windows |
3431 | 0 | GList *children = |
3432 | 0 | gdk_window_peek_children(mGdkWindow); |
3433 | 0 | while (children) { |
3434 | 0 | GdkWindow *gdkWin = GDK_WINDOW(children->data); |
3435 | 0 |
|
3436 | 0 | auto *win = (nsWindow*) g_object_get_data(G_OBJECT(gdkWin), |
3437 | 0 | "nsWindow"); |
3438 | 0 |
|
3439 | 0 | if (win && win != this) { // guard against infinite recursion |
3440 | 0 | RefPtr<nsWindow> kungFuDeathGrip = win; |
3441 | 0 | win->ThemeChanged(); |
3442 | 0 | } |
3443 | 0 |
|
3444 | 0 | children = children->next; |
3445 | 0 | } |
3446 | 0 |
|
3447 | 0 | IMContextWrapper::OnThemeChanged(); |
3448 | 0 | } |
3449 | | |
3450 | | void |
3451 | | nsWindow::OnDPIChanged() |
3452 | 0 | { |
3453 | 0 | if (mWidgetListener) { |
3454 | 0 | nsIPresShell* presShell = mWidgetListener->GetPresShell(); |
3455 | 0 | if (presShell) { |
3456 | 0 | presShell->BackingScaleFactorChanged(); |
3457 | 0 | // Update menu's font size etc |
3458 | 0 | presShell->ThemeChanged(); |
3459 | 0 | } |
3460 | 0 | mWidgetListener->UIResolutionChanged(); |
3461 | 0 | } |
3462 | 0 | } |
3463 | | |
3464 | | void |
3465 | | nsWindow::OnCheckResize() |
3466 | 0 | { |
3467 | 0 | mPendingConfigures++; |
3468 | 0 | } |
3469 | | |
3470 | | void |
3471 | | nsWindow::OnCompositedChanged() |
3472 | 0 | { |
3473 | 0 | if (mWidgetListener) { |
3474 | 0 | nsIPresShell* presShell = mWidgetListener->GetPresShell(); |
3475 | 0 | if (presShell) { |
3476 | 0 | // Update CSD after the change in alpha visibility |
3477 | 0 | presShell->ThemeChanged(); |
3478 | 0 | } |
3479 | 0 | } |
3480 | 0 | } |
3481 | | |
3482 | | void |
3483 | | nsWindow::DispatchDragEvent(EventMessage aMsg, const LayoutDeviceIntPoint& aRefPoint, |
3484 | | guint aTime) |
3485 | 0 | { |
3486 | 0 | WidgetDragEvent event(true, aMsg, this); |
3487 | 0 |
|
3488 | 0 | InitDragEvent(event); |
3489 | 0 |
|
3490 | 0 | event.mRefPoint = aRefPoint; |
3491 | 0 | event.AssignEventTime(GetWidgetEventTime(aTime)); |
3492 | 0 |
|
3493 | 0 | DispatchInputEvent(&event); |
3494 | 0 | } |
3495 | | |
3496 | | void |
3497 | | nsWindow::OnDragDataReceivedEvent(GtkWidget *aWidget, |
3498 | | GdkDragContext *aDragContext, |
3499 | | gint aX, |
3500 | | gint aY, |
3501 | | GtkSelectionData *aSelectionData, |
3502 | | guint aInfo, |
3503 | | guint aTime, |
3504 | | gpointer aData) |
3505 | 0 | { |
3506 | 0 | LOGDRAG(("nsWindow::OnDragDataReceived(%p)\n", (void*)this)); |
3507 | 0 |
|
3508 | 0 | RefPtr<nsDragService> dragService = nsDragService::GetInstance(); |
3509 | 0 | dragService-> |
3510 | 0 | TargetDataReceived(aWidget, aDragContext, aX, aY, |
3511 | 0 | aSelectionData, aInfo, aTime); |
3512 | 0 | } |
3513 | | |
3514 | | nsWindow* |
3515 | | nsWindow::GetTransientForWindowIfPopup() |
3516 | 0 | { |
3517 | 0 | if (mWindowType != eWindowType_popup) { |
3518 | 0 | return nullptr; |
3519 | 0 | } |
3520 | 0 | GtkWindow* toplevel = gtk_window_get_transient_for(GTK_WINDOW(mShell)); |
3521 | 0 | if (toplevel) { |
3522 | 0 | return get_window_for_gtk_widget(GTK_WIDGET(toplevel)); |
3523 | 0 | } |
3524 | 0 | return nullptr; |
3525 | 0 | } |
3526 | | |
3527 | | bool |
3528 | | nsWindow::IsHandlingTouchSequence(GdkEventSequence* aSequence) |
3529 | 0 | { |
3530 | 0 | return mHandleTouchEvent && mTouches.Contains(aSequence); |
3531 | 0 | } |
3532 | | |
3533 | | #if GTK_CHECK_VERSION(3,4,0) |
3534 | | gboolean |
3535 | | nsWindow::OnTouchEvent(GdkEventTouch* aEvent) |
3536 | 0 | { |
3537 | 0 | if (!mHandleTouchEvent) { |
3538 | 0 | // If a popup window was spawned (e.g. as the result of a long-press) |
3539 | 0 | // and touch events got diverted to that window within a touch sequence, |
3540 | 0 | // ensure the touch event gets sent to the original window instead. We |
3541 | 0 | // keep the checks here very conservative so that we only redirect |
3542 | 0 | // events in this specific scenario. |
3543 | 0 | nsWindow* targetWindow = GetTransientForWindowIfPopup(); |
3544 | 0 | if (targetWindow && |
3545 | 0 | targetWindow->IsHandlingTouchSequence(aEvent->sequence)) { |
3546 | 0 | return targetWindow->OnTouchEvent(aEvent); |
3547 | 0 | } |
3548 | 0 | |
3549 | 0 | return FALSE; |
3550 | 0 | } |
3551 | 0 | |
3552 | 0 | EventMessage msg; |
3553 | 0 | switch (aEvent->type) { |
3554 | 0 | case GDK_TOUCH_BEGIN: |
3555 | 0 | msg = eTouchStart; |
3556 | 0 | break; |
3557 | 0 | case GDK_TOUCH_UPDATE: |
3558 | 0 | msg = eTouchMove; |
3559 | 0 | break; |
3560 | 0 | case GDK_TOUCH_END: |
3561 | 0 | msg = eTouchEnd; |
3562 | 0 | break; |
3563 | 0 | case GDK_TOUCH_CANCEL: |
3564 | 0 | msg = eTouchCancel; |
3565 | 0 | break; |
3566 | 0 | default: |
3567 | 0 | return FALSE; |
3568 | 0 | } |
3569 | 0 | |
3570 | 0 | LayoutDeviceIntPoint touchPoint = GetRefPoint(this, aEvent); |
3571 | 0 |
|
3572 | 0 | int32_t id; |
3573 | 0 | RefPtr<dom::Touch> touch; |
3574 | 0 | if (mTouches.Remove(aEvent->sequence, getter_AddRefs(touch))) { |
3575 | 0 | id = touch->mIdentifier; |
3576 | 0 | } else { |
3577 | 0 | id = ++gLastTouchID & 0x7FFFFFFF; |
3578 | 0 | } |
3579 | 0 |
|
3580 | 0 | touch = new dom::Touch(id, touchPoint, LayoutDeviceIntPoint(1, 1), |
3581 | 0 | 0.0f, 0.0f); |
3582 | 0 |
|
3583 | 0 | WidgetTouchEvent event(true, msg, this); |
3584 | 0 | KeymapWrapper::InitInputEvent(event, aEvent->state); |
3585 | 0 | event.mTime = aEvent->time; |
3586 | 0 |
|
3587 | 0 | if (aEvent->type == GDK_TOUCH_BEGIN || aEvent->type == GDK_TOUCH_UPDATE) { |
3588 | 0 | mTouches.Put(aEvent->sequence, touch.forget()); |
3589 | 0 | // add all touch points to event object |
3590 | 0 | for (auto iter = mTouches.Iter(); !iter.Done(); iter.Next()) { |
3591 | 0 | event.mTouches.AppendElement(new dom::Touch(*iter.UserData())); |
3592 | 0 | } |
3593 | 0 | } else if (aEvent->type == GDK_TOUCH_END || |
3594 | 0 | aEvent->type == GDK_TOUCH_CANCEL) { |
3595 | 0 | *event.mTouches.AppendElement() = touch.forget(); |
3596 | 0 | } |
3597 | 0 |
|
3598 | 0 | DispatchInputEvent(&event); |
3599 | 0 | return TRUE; |
3600 | 0 | } |
3601 | | #endif |
3602 | | |
3603 | | static GdkWindow * |
3604 | | CreateGdkWindow(GdkWindow *parent, GtkWidget *widget) |
3605 | 0 | { |
3606 | 0 | GdkWindowAttr attributes; |
3607 | 0 | gint attributes_mask = GDK_WA_VISUAL; |
3608 | 0 |
|
3609 | 0 | attributes.event_mask = kEvents; |
3610 | 0 |
|
3611 | 0 | attributes.width = 1; |
3612 | 0 | attributes.height = 1; |
3613 | 0 | attributes.wclass = GDK_INPUT_OUTPUT; |
3614 | 0 | attributes.visual = gtk_widget_get_visual(widget); |
3615 | 0 | attributes.window_type = GDK_WINDOW_CHILD; |
3616 | 0 |
|
3617 | 0 | GdkWindow *window = gdk_window_new(parent, &attributes, attributes_mask); |
3618 | 0 | gdk_window_set_user_data(window, widget); |
3619 | 0 |
|
3620 | 0 | return window; |
3621 | 0 | } |
3622 | | |
3623 | | nsresult |
3624 | | nsWindow::Create(nsIWidget* aParent, |
3625 | | nsNativeWidget aNativeParent, |
3626 | | const LayoutDeviceIntRect& aRect, |
3627 | | nsWidgetInitData* aInitData) |
3628 | 0 | { |
3629 | 0 | // only set the base parent if we're going to be a dialog or a |
3630 | 0 | // toplevel |
3631 | 0 | nsIWidget *baseParent = aInitData && |
3632 | 0 | (aInitData->mWindowType == eWindowType_dialog || |
3633 | 0 | aInitData->mWindowType == eWindowType_toplevel || |
3634 | 0 | aInitData->mWindowType == eWindowType_invisible) ? |
3635 | 0 | nullptr : aParent; |
3636 | 0 |
|
3637 | 0 | #ifdef ACCESSIBILITY |
3638 | 0 | // Send a DBus message to check whether a11y is enabled |
3639 | 0 | a11y::PreInit(); |
3640 | 0 | #endif |
3641 | 0 |
|
3642 | 0 | // Ensure that the toolkit is created. |
3643 | 0 | nsGTKToolkit::GetToolkit(); |
3644 | 0 |
|
3645 | 0 | // initialize all the common bits of this class |
3646 | 0 | BaseCreate(baseParent, aInitData); |
3647 | 0 |
|
3648 | 0 | // Do we need to listen for resizes? |
3649 | 0 | bool listenForResizes = false;; |
3650 | 0 | if (aNativeParent || (aInitData && aInitData->mListenForResizes)) |
3651 | 0 | listenForResizes = true; |
3652 | 0 |
|
3653 | 0 | // and do our common creation |
3654 | 0 | CommonCreate(aParent, listenForResizes); |
3655 | 0 |
|
3656 | 0 | // save our bounds |
3657 | 0 | mBounds = aRect; |
3658 | 0 | ConstrainSize(&mBounds.width, &mBounds.height); |
3659 | 0 |
|
3660 | 0 | // figure out our parent window |
3661 | 0 | GtkWidget *parentMozContainer = nullptr; |
3662 | 0 | GtkContainer *parentGtkContainer = nullptr; |
3663 | 0 | GdkWindow *parentGdkWindow = nullptr; |
3664 | 0 | GtkWindow *topLevelParent = nullptr; |
3665 | 0 | nsWindow *parentnsWindow = nullptr; |
3666 | 0 | GtkWidget *eventWidget = nullptr; |
3667 | 0 | bool drawToContainer = false; |
3668 | 0 | bool needsAlphaVisual = (mWindowType == eWindowType_popup && |
3669 | 0 | aInitData->mSupportTranslucency); |
3670 | 0 |
|
3671 | 0 | // Some Gtk+ themes use non-rectangular toplevel windows. To fully support |
3672 | 0 | // such themes we need to make toplevel window transparent with ARGB visual. |
3673 | 0 | // It may cause performanance issue so make it configurable |
3674 | 0 | // and enable it by default for selected window managers. |
3675 | 0 | if (mWindowType == eWindowType_toplevel) { |
3676 | 0 | needsAlphaVisual = TopLevelWindowUseARGBVisual(); |
3677 | 0 | } |
3678 | 0 |
|
3679 | 0 | if (aParent) { |
3680 | 0 | parentnsWindow = static_cast<nsWindow*>(aParent); |
3681 | 0 | parentGdkWindow = parentnsWindow->mGdkWindow; |
3682 | 0 | } else if (aNativeParent && GDK_IS_WINDOW(aNativeParent)) { |
3683 | 0 | parentGdkWindow = GDK_WINDOW(aNativeParent); |
3684 | 0 | parentnsWindow = get_window_for_gdk_window(parentGdkWindow); |
3685 | 0 | if (!parentnsWindow) |
3686 | 0 | return NS_ERROR_FAILURE; |
3687 | 0 | |
3688 | 0 | } else if (aNativeParent && GTK_IS_CONTAINER(aNativeParent)) { |
3689 | 0 | parentGtkContainer = GTK_CONTAINER(aNativeParent); |
3690 | 0 | } |
3691 | 0 |
|
3692 | 0 | if (parentGdkWindow) { |
3693 | 0 | // get the widget for the window - it should be a moz container |
3694 | 0 | parentMozContainer = parentnsWindow->GetMozContainerWidget(); |
3695 | 0 | if (!parentMozContainer) |
3696 | 0 | return NS_ERROR_FAILURE; |
3697 | 0 | |
3698 | 0 | // get the toplevel window just in case someone needs to use it |
3699 | 0 | // for setting transients or whatever. |
3700 | 0 | topLevelParent = |
3701 | 0 | GTK_WINDOW(gtk_widget_get_toplevel(parentMozContainer)); |
3702 | 0 | } |
3703 | 0 |
|
3704 | 0 | // ok, create our windows |
3705 | 0 | switch (mWindowType) { |
3706 | 0 | case eWindowType_dialog: |
3707 | 0 | case eWindowType_popup: |
3708 | 0 | case eWindowType_toplevel: |
3709 | 0 | case eWindowType_invisible: { |
3710 | 0 | mIsTopLevel = true; |
3711 | 0 |
|
3712 | 0 | // Popups that are not noautohide are only temporary. The are used |
3713 | 0 | // for menus and the like and disappear when another window is used. |
3714 | 0 | // For most popups, use the standard GtkWindowType GTK_WINDOW_POPUP, |
3715 | 0 | // which will use a Window with the override-redirect attribute |
3716 | 0 | // (for temporary windows). |
3717 | 0 | // For long-lived windows, their stacking order is managed by the |
3718 | 0 | // window manager, as indicated by GTK_WINDOW_TOPLEVEL. |
3719 | 0 | // For Wayland we have to always use GTK_WINDOW_POPUP to control |
3720 | 0 | // popup window position. |
3721 | 0 | GtkWindowType type = GTK_WINDOW_TOPLEVEL; |
3722 | 0 | if (mWindowType == eWindowType_popup) { |
3723 | 0 | type = (mIsX11Display && aInitData->mNoAutoHide) ? |
3724 | 0 | GTK_WINDOW_TOPLEVEL : GTK_WINDOW_POPUP; |
3725 | 0 | } |
3726 | 0 | mShell = gtk_window_new(type); |
3727 | 0 |
|
3728 | 0 | #ifdef MOZ_X11 |
3729 | 0 | // Ensure gfxPlatform is initialized, since that is what initializes |
3730 | 0 | // gfxVars, used below. |
3731 | 0 | Unused << gfxPlatform::GetPlatform(); |
3732 | 0 |
|
3733 | 0 | bool useWebRender = gfx::gfxVars::UseWebRender() && |
3734 | 0 | AllowWebRenderForThisWindow(); |
3735 | 0 |
|
3736 | 0 | // If using WebRender on X11, we need to select a visual with a depth buffer, |
3737 | 0 | // as well as an alpha channel if transparency is requested. This must be done |
3738 | 0 | // before the widget is realized. |
3739 | 0 | if (mIsX11Display && useWebRender) { |
3740 | 0 | auto display = |
3741 | 0 | GDK_DISPLAY_XDISPLAY(gtk_widget_get_display(mShell)); |
3742 | 0 | auto screen = gtk_widget_get_screen(mShell); |
3743 | 0 | int screenNumber = GDK_SCREEN_XNUMBER(screen); |
3744 | 0 | int visualId = 0; |
3745 | 0 | if (GLContextGLX::FindVisual(display, screenNumber, |
3746 | 0 | useWebRender, needsAlphaVisual, |
3747 | 0 | &visualId)) { |
3748 | 0 | // If we're using CSD, rendering will go through mContainer, but |
3749 | 0 | // it will inherit this visual as it is a child of mShell. |
3750 | 0 | gtk_widget_set_visual(mShell, |
3751 | 0 | gdk_x11_screen_lookup_visual(screen, |
3752 | 0 | visualId)); |
3753 | 0 | mHasAlphaVisual = needsAlphaVisual; |
3754 | 0 | } else { |
3755 | 0 | NS_WARNING("We're missing X11 Visual for WebRender!"); |
3756 | 0 | } |
3757 | 0 | } else |
3758 | 0 | #endif // MOZ_X11 |
3759 | 0 | { |
3760 | 0 | if (needsAlphaVisual) { |
3761 | 0 | GdkScreen *screen = gtk_widget_get_screen(mShell); |
3762 | 0 | if (gdk_screen_is_composited(screen)) { |
3763 | 0 | GdkVisual *visual = gdk_screen_get_rgba_visual(screen); |
3764 | 0 | if (visual) { |
3765 | 0 | gtk_widget_set_visual(mShell, visual); |
3766 | 0 | mHasAlphaVisual = true; |
3767 | 0 | } |
3768 | 0 | } |
3769 | 0 | } |
3770 | 0 | } |
3771 | 0 |
|
3772 | 0 | // We have a toplevel window with transparency. Mark it as transparent |
3773 | 0 | // now as nsWindow::SetTransparencyMode() can't be called after |
3774 | 0 | // nsWindow is created (Bug 1344839). |
3775 | 0 | if (mWindowType == eWindowType_toplevel && mHasAlphaVisual) { |
3776 | 0 | mIsTransparent = true; |
3777 | 0 | } |
3778 | 0 |
|
3779 | 0 | // We only move a general managed toplevel window if someone has |
3780 | 0 | // actually placed the window somewhere. If no placement has taken |
3781 | 0 | // place, we just let the window manager Do The Right Thing. |
3782 | 0 | NativeResize(); |
3783 | 0 |
|
3784 | 0 | if (mWindowType == eWindowType_dialog) { |
3785 | 0 | SetDefaultIcon(); |
3786 | 0 | gtk_window_set_wmclass(GTK_WINDOW(mShell), "Dialog", |
3787 | 0 | gdk_get_program_class()); |
3788 | 0 | gtk_window_set_type_hint(GTK_WINDOW(mShell), |
3789 | 0 | GDK_WINDOW_TYPE_HINT_DIALOG); |
3790 | 0 | gtk_window_set_transient_for(GTK_WINDOW(mShell), |
3791 | 0 | topLevelParent); |
3792 | 0 | } |
3793 | 0 | else if (mWindowType == eWindowType_popup) { |
3794 | 0 | // With popup windows, we want to control their position, so don't |
3795 | 0 | // wait for the window manager to place them (which wouldn't |
3796 | 0 | // happen with override-redirect windows anyway). |
3797 | 0 | NativeMove(); |
3798 | 0 |
|
3799 | 0 | gtk_window_set_wmclass(GTK_WINDOW(mShell), "Popup", |
3800 | 0 | gdk_get_program_class()); |
3801 | 0 |
|
3802 | 0 | if (aInitData->mNoAutoHide) { |
3803 | 0 | // ... but the window manager does not decorate this window, |
3804 | 0 | // nor provide a separate taskbar icon. |
3805 | 0 | if (mBorderStyle == eBorderStyle_default) { |
3806 | 0 | gtk_window_set_decorated(GTK_WINDOW(mShell), FALSE); |
3807 | 0 | } |
3808 | 0 | else { |
3809 | 0 | bool decorate = mBorderStyle & eBorderStyle_title; |
3810 | 0 | gtk_window_set_decorated(GTK_WINDOW(mShell), decorate); |
3811 | 0 | if (decorate) { |
3812 | 0 | gtk_window_set_deletable(GTK_WINDOW(mShell), mBorderStyle & eBorderStyle_close); |
3813 | 0 | } |
3814 | 0 | } |
3815 | 0 | gtk_window_set_skip_taskbar_hint(GTK_WINDOW(mShell), TRUE); |
3816 | 0 | // Element focus is managed by the parent window so the |
3817 | 0 | // WM_HINTS input field is set to False to tell the window |
3818 | 0 | // manager not to set input focus to this window ... |
3819 | 0 | gtk_window_set_accept_focus(GTK_WINDOW(mShell), FALSE); |
3820 | 0 | #ifdef MOZ_X11 |
3821 | 0 | // ... but when the window manager offers focus through |
3822 | 0 | // WM_TAKE_FOCUS, focus is requested on the parent window. |
3823 | 0 | if (mIsX11Display) { |
3824 | 0 | gtk_widget_realize(mShell); |
3825 | 0 | gdk_window_add_filter(gtk_widget_get_window(mShell), |
3826 | 0 | popup_take_focus_filter, nullptr); |
3827 | 0 | } |
3828 | 0 | #endif |
3829 | 0 | } |
3830 | 0 |
|
3831 | 0 | GdkWindowTypeHint gtkTypeHint; |
3832 | 0 | if (aInitData->mIsDragPopup) { |
3833 | 0 | gtkTypeHint = GDK_WINDOW_TYPE_HINT_DND; |
3834 | 0 | mIsDragPopup = true; |
3835 | 0 | } |
3836 | 0 | else { |
3837 | 0 | switch (aInitData->mPopupHint) { |
3838 | 0 | case ePopupTypeMenu: |
3839 | 0 | // Use GDK_WINDOW_TYPE_HINT_UTILITY on Wayland which |
3840 | 0 | // guides Gtk to create the popup as subsurface |
3841 | 0 | // instead of xdg_shell popup (see Bug 1423598). |
3842 | 0 | gtkTypeHint = mIsX11Display ? GDK_WINDOW_TYPE_HINT_POPUP_MENU : |
3843 | 0 | GDK_WINDOW_TYPE_HINT_UTILITY; |
3844 | 0 | break; |
3845 | 0 | case ePopupTypeTooltip: |
3846 | 0 | gtkTypeHint = GDK_WINDOW_TYPE_HINT_TOOLTIP; |
3847 | 0 | break; |
3848 | 0 | default: |
3849 | 0 | gtkTypeHint = GDK_WINDOW_TYPE_HINT_UTILITY; |
3850 | 0 | break; |
3851 | 0 | } |
3852 | 0 | } |
3853 | 0 | gtk_window_set_type_hint(GTK_WINDOW(mShell), gtkTypeHint); |
3854 | 0 |
|
3855 | 0 | if (topLevelParent) { |
3856 | 0 | gtk_window_set_transient_for(GTK_WINDOW(mShell), |
3857 | 0 | topLevelParent); |
3858 | 0 | } |
3859 | 0 | } |
3860 | 0 | else { // must be eWindowType_toplevel |
3861 | 0 | SetDefaultIcon(); |
3862 | 0 | gtk_window_set_wmclass(GTK_WINDOW(mShell), "Toplevel", |
3863 | 0 | gdk_get_program_class()); |
3864 | 0 |
|
3865 | 0 | // each toplevel window gets its own window group |
3866 | 0 | GtkWindowGroup *group = gtk_window_group_new(); |
3867 | 0 | gtk_window_group_add_window(group, GTK_WINDOW(mShell)); |
3868 | 0 | g_object_unref(group); |
3869 | 0 |
|
3870 | 0 | // We enable titlebar rendering for toplevel windows only. |
3871 | 0 | mCSDSupportLevel = GetSystemCSDSupportLevel(); |
3872 | 0 | } |
3873 | 0 |
|
3874 | 0 | // Create a container to hold child windows and child GtkWidgets. |
3875 | 0 | GtkWidget *container = moz_container_new(); |
3876 | 0 | mContainer = MOZ_CONTAINER(container); |
3877 | 0 |
|
3878 | 0 | // "csd" style is set when widget is realized so we need to call |
3879 | 0 | // it explicitly now. |
3880 | 0 | gtk_widget_realize(mShell); |
3881 | 0 |
|
3882 | 0 | /* There are several cases here: |
3883 | 0 | * |
3884 | 0 | * 1) We're running on Gtk+ without client side decorations. |
3885 | 0 | * Content is rendered to mShell window and we listen |
3886 | 0 | * to the Gtk+ events on mShell |
3887 | 0 | * 2) We're running on Gtk+ and client side decorations |
3888 | 0 | * are drawn by Gtk+ to mShell. Content is rendered to mContainer |
3889 | 0 | * and we listen to the Gtk+ events on mContainer. |
3890 | 0 | * 3) We're running on Wayland. All gecko content is rendered |
3891 | 0 | * to mContainer and we listen to the Gtk+ events on mContainer. |
3892 | 0 | */ |
3893 | 0 | GtkStyleContext* style = gtk_widget_get_style_context(mShell); |
3894 | 0 | drawToContainer = |
3895 | 0 | !mIsX11Display || |
3896 | 0 | (mCSDSupportLevel == CSD_SUPPORT_CLIENT) || |
3897 | 0 | gtk_style_context_has_class(style, "csd"); |
3898 | 0 | eventWidget = (drawToContainer) ? container : mShell; |
3899 | 0 |
|
3900 | 0 | gtk_widget_add_events(eventWidget, kEvents); |
3901 | 0 | if (drawToContainer) |
3902 | 0 | gtk_widget_add_events(mShell, GDK_PROPERTY_CHANGE_MASK); |
3903 | 0 |
|
3904 | 0 | // Prevent GtkWindow from painting a background to avoid flickering. |
3905 | 0 | gtk_widget_set_app_paintable(eventWidget, TRUE); |
3906 | 0 |
|
3907 | 0 | // If we draw to mContainer window then configure it now because |
3908 | 0 | // gtk_container_add() realizes the child widget. |
3909 | 0 | gtk_widget_set_has_window(container, drawToContainer); |
3910 | 0 |
|
3911 | 0 | gtk_container_add(GTK_CONTAINER(mShell), container); |
3912 | 0 | gtk_widget_realize(container); |
3913 | 0 |
|
3914 | 0 | // make sure this is the focus widget in the container |
3915 | 0 | gtk_widget_show(container); |
3916 | 0 | gtk_widget_grab_focus(container); |
3917 | 0 |
|
3918 | 0 | // the drawing window |
3919 | 0 | mGdkWindow = gtk_widget_get_window(eventWidget); |
3920 | 0 |
|
3921 | 0 | if (mWindowType == eWindowType_popup) { |
3922 | 0 | // gdk does not automatically set the cursor for "temporary" |
3923 | 0 | // windows, which are what gtk uses for popups. |
3924 | 0 |
|
3925 | 0 | mCursor = eCursor_wait; // force SetCursor to actually set the |
3926 | 0 | // cursor, even though our internal state |
3927 | 0 | // indicates that we already have the |
3928 | 0 | // standard cursor. |
3929 | 0 | SetCursor(eCursor_standard); |
3930 | 0 |
|
3931 | 0 | if (aInitData->mNoAutoHide) { |
3932 | 0 | gint wmd = ConvertBorderStyles(mBorderStyle); |
3933 | 0 | if (wmd != -1) |
3934 | 0 | gdk_window_set_decorations(mGdkWindow, (GdkWMDecoration) wmd); |
3935 | 0 | } |
3936 | 0 |
|
3937 | 0 | if (!mIsX11Display) { |
3938 | 0 | gtk_widget_set_app_paintable(mShell, TRUE); |
3939 | 0 | } |
3940 | 0 |
|
3941 | 0 | // If the popup ignores mouse events, set an empty input shape. |
3942 | 0 | if (aInitData->mMouseTransparent) { |
3943 | 0 | cairo_rectangle_int_t rect = { 0, 0, 0, 0 }; |
3944 | 0 | cairo_region_t *region = cairo_region_create_rectangle(&rect); |
3945 | 0 |
|
3946 | 0 | gdk_window_input_shape_combine_region(mGdkWindow, region, 0, 0); |
3947 | 0 | cairo_region_destroy(region); |
3948 | 0 | } |
3949 | 0 | } |
3950 | 0 |
|
3951 | 0 | #ifdef MOZ_X11 |
3952 | 0 | // Set window manager hint to keep fullscreen windows composited. |
3953 | 0 | // |
3954 | 0 | // If the window were to get unredirected, there could be visible |
3955 | 0 | // tearing because Gecko does not align its framebuffer updates with |
3956 | 0 | // vblank. |
3957 | 0 | SetCompositorHint(GTK_WIDGET_COMPOSIDED_ENABLED); |
3958 | 0 | #endif |
3959 | 0 | } |
3960 | 0 | break; |
3961 | 0 |
|
3962 | 0 | case eWindowType_plugin: |
3963 | 0 | case eWindowType_plugin_ipc_chrome: |
3964 | 0 | case eWindowType_plugin_ipc_content: |
3965 | 0 | MOZ_ASSERT_UNREACHABLE("Unexpected eWindowType_plugin*"); |
3966 | 0 | return NS_ERROR_FAILURE; |
3967 | 0 |
|
3968 | 0 | case eWindowType_child: { |
3969 | 0 | if (parentMozContainer) { |
3970 | 0 | mGdkWindow = CreateGdkWindow(parentGdkWindow, parentMozContainer); |
3971 | 0 | mHasMappedToplevel = parentnsWindow->mHasMappedToplevel; |
3972 | 0 | } |
3973 | 0 | else if (parentGtkContainer) { |
3974 | 0 | // This MozContainer has its own window for drawing and receives |
3975 | 0 | // events because there is no mShell widget (corresponding to this |
3976 | 0 | // nsWindow). |
3977 | 0 | GtkWidget *container = moz_container_new(); |
3978 | 0 | mContainer = MOZ_CONTAINER(container); |
3979 | 0 | eventWidget = container; |
3980 | 0 | gtk_widget_add_events(eventWidget, kEvents); |
3981 | 0 | gtk_container_add(parentGtkContainer, container); |
3982 | 0 | gtk_widget_realize(container); |
3983 | 0 |
|
3984 | 0 | mGdkWindow = gtk_widget_get_window(container); |
3985 | 0 | } |
3986 | 0 | else { |
3987 | 0 | NS_WARNING("Warning: tried to create a new child widget with no parent!"); |
3988 | 0 | return NS_ERROR_FAILURE; |
3989 | 0 | } |
3990 | 0 | } |
3991 | 0 | break; |
3992 | 0 | default: |
3993 | 0 | break; |
3994 | 0 | } |
3995 | 0 | |
3996 | 0 | // label the drawing window with this object so we can find our way home |
3997 | 0 | g_object_set_data(G_OBJECT(mGdkWindow), "nsWindow", this); |
3998 | 0 | if (drawToContainer) { |
3999 | 0 | // Also label mShell toplevel window, |
4000 | 0 | // property_notify_event_cb callback also needs to find its way home |
4001 | 0 | g_object_set_data(G_OBJECT(gtk_widget_get_window(mShell)), |
4002 | 0 | "nsWindow", this); |
4003 | 0 | } |
4004 | 0 |
|
4005 | 0 | if (mContainer) |
4006 | 0 | g_object_set_data(G_OBJECT(mContainer), "nsWindow", this); |
4007 | 0 |
|
4008 | 0 | if (mShell) |
4009 | 0 | g_object_set_data(G_OBJECT(mShell), "nsWindow", this); |
4010 | 0 |
|
4011 | 0 | // attach listeners for events |
4012 | 0 | if (mShell) { |
4013 | 0 | g_signal_connect(mShell, "configure_event", |
4014 | 0 | G_CALLBACK(configure_event_cb), nullptr); |
4015 | 0 | g_signal_connect(mShell, "delete_event", |
4016 | 0 | G_CALLBACK(delete_event_cb), nullptr); |
4017 | 0 | g_signal_connect(mShell, "window_state_event", |
4018 | 0 | G_CALLBACK(window_state_event_cb), nullptr); |
4019 | 0 | g_signal_connect(mShell, "check-resize", |
4020 | 0 | G_CALLBACK(check_resize_cb), nullptr); |
4021 | 0 | g_signal_connect(mShell, "composited-changed", |
4022 | 0 | G_CALLBACK(widget_composited_changed_cb), nullptr); |
4023 | 0 | g_signal_connect(mShell, "property-notify-event", |
4024 | 0 | G_CALLBACK(property_notify_event_cb), nullptr); |
4025 | 0 |
|
4026 | 0 | GdkScreen *screen = gtk_widget_get_screen(mShell); |
4027 | 0 | if (!g_signal_handler_find(screen, G_SIGNAL_MATCH_FUNC, |
4028 | 0 | 0, 0, nullptr, |
4029 | 0 | FuncToGpointer(screen_composited_changed_cb), 0)) { |
4030 | 0 | g_signal_connect(screen, "composited-changed", |
4031 | 0 | G_CALLBACK(screen_composited_changed_cb), nullptr); |
4032 | 0 | } |
4033 | 0 |
|
4034 | 0 | GtkSettings* default_settings = gtk_settings_get_default(); |
4035 | 0 | g_signal_connect_after(default_settings, |
4036 | 0 | "notify::gtk-theme-name", |
4037 | 0 | G_CALLBACK(settings_changed_cb), this); |
4038 | 0 | g_signal_connect_after(default_settings, |
4039 | 0 | "notify::gtk-font-name", |
4040 | 0 | G_CALLBACK(settings_changed_cb), this); |
4041 | 0 | g_signal_connect_after(default_settings, |
4042 | 0 | "notify::gtk-enable-animations", |
4043 | 0 | G_CALLBACK(settings_changed_cb), this); |
4044 | 0 | } |
4045 | 0 |
|
4046 | 0 | if (mContainer) { |
4047 | 0 | // Widget signals |
4048 | 0 | g_signal_connect(mContainer, "unrealize", |
4049 | 0 | G_CALLBACK(container_unrealize_cb), nullptr); |
4050 | 0 | g_signal_connect_after(mContainer, "size_allocate", |
4051 | 0 | G_CALLBACK(size_allocate_cb), nullptr); |
4052 | 0 | g_signal_connect(mContainer, "hierarchy-changed", |
4053 | 0 | G_CALLBACK(hierarchy_changed_cb), nullptr); |
4054 | 0 | g_signal_connect(mContainer, "notify::scale-factor", |
4055 | 0 | G_CALLBACK(scale_changed_cb), nullptr); |
4056 | 0 | // Initialize mHasMappedToplevel. |
4057 | 0 | hierarchy_changed_cb(GTK_WIDGET(mContainer), nullptr); |
4058 | 0 | // Expose, focus, key, and drag events are sent even to GTK_NO_WINDOW |
4059 | 0 | // widgets. |
4060 | 0 | g_signal_connect(G_OBJECT(mContainer), "draw", |
4061 | 0 | G_CALLBACK(expose_event_cb), nullptr); |
4062 | 0 | g_signal_connect(mContainer, "focus_in_event", |
4063 | 0 | G_CALLBACK(focus_in_event_cb), nullptr); |
4064 | 0 | g_signal_connect(mContainer, "focus_out_event", |
4065 | 0 | G_CALLBACK(focus_out_event_cb), nullptr); |
4066 | 0 | g_signal_connect(mContainer, "key_press_event", |
4067 | 0 | G_CALLBACK(key_press_event_cb), nullptr); |
4068 | 0 | g_signal_connect(mContainer, "key_release_event", |
4069 | 0 | G_CALLBACK(key_release_event_cb), nullptr); |
4070 | 0 |
|
4071 | 0 | gtk_drag_dest_set((GtkWidget *)mContainer, |
4072 | 0 | (GtkDestDefaults)0, |
4073 | 0 | nullptr, |
4074 | 0 | 0, |
4075 | 0 | (GdkDragAction)0); |
4076 | 0 |
|
4077 | 0 | g_signal_connect(mContainer, "drag_motion", |
4078 | 0 | G_CALLBACK(drag_motion_event_cb), nullptr); |
4079 | 0 | g_signal_connect(mContainer, "drag_leave", |
4080 | 0 | G_CALLBACK(drag_leave_event_cb), nullptr); |
4081 | 0 | g_signal_connect(mContainer, "drag_drop", |
4082 | 0 | G_CALLBACK(drag_drop_event_cb), nullptr); |
4083 | 0 | g_signal_connect(mContainer, "drag_data_received", |
4084 | 0 | G_CALLBACK(drag_data_received_event_cb), nullptr); |
4085 | 0 |
|
4086 | 0 | GtkWidget *widgets[] = { GTK_WIDGET(mContainer), |
4087 | 0 | !drawToContainer ? mShell : nullptr }; |
4088 | 0 | for (size_t i = 0; i < ArrayLength(widgets) && widgets[i]; ++i) { |
4089 | 0 | // Visibility events are sent to the owning widget of the relevant |
4090 | 0 | // window but do not propagate to parent widgets so connect on |
4091 | 0 | // mShell (if it exists) as well as mContainer. |
4092 | 0 | g_signal_connect(widgets[i], "visibility-notify-event", |
4093 | 0 | G_CALLBACK(visibility_notify_event_cb), nullptr); |
4094 | 0 | // Similarly double buffering is controlled by the window's owning |
4095 | 0 | // widget. Disable double buffering for painting directly to the |
4096 | 0 | // X Window. |
4097 | 0 | gtk_widget_set_double_buffered(widgets[i], FALSE); |
4098 | 0 | } |
4099 | 0 |
|
4100 | 0 | // We create input contexts for all containers, except for |
4101 | 0 | // toplevel popup windows |
4102 | 0 | if (mWindowType != eWindowType_popup) { |
4103 | 0 | mIMContext = new IMContextWrapper(this); |
4104 | 0 | } |
4105 | 0 | } else if (!mIMContext) { |
4106 | 0 | nsWindow *container = GetContainerWindow(); |
4107 | 0 | if (container) { |
4108 | 0 | mIMContext = container->mIMContext; |
4109 | 0 | } |
4110 | 0 | } |
4111 | 0 |
|
4112 | 0 | if (eventWidget) { |
4113 | 0 |
|
4114 | 0 | // These events are sent to the owning widget of the relevant window |
4115 | 0 | // and propagate up to the first widget that handles the events, so we |
4116 | 0 | // need only connect on mShell, if it exists, to catch events on its |
4117 | 0 | // window and windows of mContainer. |
4118 | 0 | g_signal_connect(eventWidget, "enter-notify-event", |
4119 | 0 | G_CALLBACK(enter_notify_event_cb), nullptr); |
4120 | 0 | g_signal_connect(eventWidget, "leave-notify-event", |
4121 | 0 | G_CALLBACK(leave_notify_event_cb), nullptr); |
4122 | 0 | g_signal_connect(eventWidget, "motion-notify-event", |
4123 | 0 | G_CALLBACK(motion_notify_event_cb), nullptr); |
4124 | 0 | g_signal_connect(eventWidget, "button-press-event", |
4125 | 0 | G_CALLBACK(button_press_event_cb), nullptr); |
4126 | 0 | g_signal_connect(eventWidget, "button-release-event", |
4127 | 0 | G_CALLBACK(button_release_event_cb), nullptr); |
4128 | 0 | g_signal_connect(eventWidget, "scroll-event", |
4129 | 0 | G_CALLBACK(scroll_event_cb), nullptr); |
4130 | 0 | #if GTK_CHECK_VERSION(3,4,0) |
4131 | 0 | g_signal_connect(eventWidget, "touch-event", |
4132 | 0 | G_CALLBACK(touch_event_cb), nullptr); |
4133 | 0 | #endif |
4134 | 0 | } |
4135 | 0 |
|
4136 | 0 | LOG(("nsWindow [%p]\n", (void *)this)); |
4137 | 0 | if (mShell) { |
4138 | 0 | LOG(("\tmShell %p mContainer %p mGdkWindow %p 0x%lx\n", |
4139 | 0 | mShell, mContainer, mGdkWindow, |
4140 | 0 | mIsX11Display ? gdk_x11_window_get_xid(mGdkWindow) : 0)); |
4141 | 0 | } else if (mContainer) { |
4142 | 0 | LOG(("\tmContainer %p mGdkWindow %p\n", mContainer, mGdkWindow)); |
4143 | 0 | } |
4144 | 0 | else if (mGdkWindow) { |
4145 | 0 | LOG(("\tmGdkWindow %p parent %p\n", |
4146 | 0 | mGdkWindow, gdk_window_get_parent(mGdkWindow))); |
4147 | 0 | } |
4148 | 0 |
|
4149 | 0 | // resize so that everything is set to the right dimensions |
4150 | 0 | if (!mIsTopLevel) |
4151 | 0 | Resize(mBounds.x, mBounds.y, mBounds.width, mBounds.height, false); |
4152 | 0 |
|
4153 | 0 | #ifdef MOZ_X11 |
4154 | 0 | if (mIsX11Display && mGdkWindow) { |
4155 | 0 | mXDisplay = GDK_WINDOW_XDISPLAY(mGdkWindow); |
4156 | 0 | mXWindow = gdk_x11_window_get_xid(mGdkWindow); |
4157 | 0 |
|
4158 | 0 | GdkVisual* gdkVisual = gdk_window_get_visual(mGdkWindow); |
4159 | 0 | mXVisual = gdk_x11_visual_get_xvisual(gdkVisual); |
4160 | 0 | mXDepth = gdk_visual_get_depth(gdkVisual); |
4161 | 0 | bool shaped = needsAlphaVisual && !mHasAlphaVisual; |
4162 | 0 |
|
4163 | 0 | mSurfaceProvider.Initialize(mXDisplay, mXWindow, mXVisual, mXDepth, |
4164 | 0 | shaped); |
4165 | 0 | } |
4166 | | #ifdef MOZ_WAYLAND |
4167 | | else if (!mIsX11Display) { |
4168 | | mSurfaceProvider.Initialize(this); |
4169 | | } |
4170 | | #endif |
4171 | | #endif |
4172 | 0 | return NS_OK; |
4173 | 0 | } |
4174 | | |
4175 | | void |
4176 | | nsWindow::RefreshWindowClass(void) |
4177 | 0 | { |
4178 | 0 | if (mGtkWindowTypeName.IsEmpty() || mGtkWindowRoleName.IsEmpty()) |
4179 | 0 | return; |
4180 | 0 | |
4181 | 0 | GdkWindow* gdkWindow = gtk_widget_get_window(mShell); |
4182 | 0 | gdk_window_set_role(gdkWindow, mGtkWindowRoleName.get()); |
4183 | 0 |
|
4184 | 0 | #ifdef MOZ_X11 |
4185 | 0 | if (mIsX11Display) { |
4186 | 0 | XClassHint *class_hint = XAllocClassHint(); |
4187 | 0 | if (!class_hint) { |
4188 | 0 | return; |
4189 | 0 | } |
4190 | 0 | const char *res_class = gdk_get_program_class(); |
4191 | 0 | if (!res_class) |
4192 | 0 | return; |
4193 | 0 | |
4194 | 0 | class_hint->res_name = const_cast<char*>(mGtkWindowTypeName.get()); |
4195 | 0 | class_hint->res_class = const_cast<char*>(res_class); |
4196 | 0 |
|
4197 | 0 | // Can't use gtk_window_set_wmclass() for this; it prints |
4198 | 0 | // a warning & refuses to make the change. |
4199 | 0 | GdkDisplay *display = gdk_display_get_default(); |
4200 | 0 | XSetClassHint(GDK_DISPLAY_XDISPLAY(display), |
4201 | 0 | gdk_x11_window_get_xid(gdkWindow), |
4202 | 0 | class_hint); |
4203 | 0 | XFree(class_hint); |
4204 | 0 | } |
4205 | 0 | #endif /* MOZ_X11 */ |
4206 | 0 | } |
4207 | | |
4208 | | void |
4209 | | nsWindow::SetWindowClass(const nsAString &xulWinType) |
4210 | 0 | { |
4211 | 0 | if (!mShell) |
4212 | 0 | return; |
4213 | 0 | |
4214 | 0 | char *res_name = ToNewCString(xulWinType); |
4215 | 0 | if (!res_name) |
4216 | 0 | return; |
4217 | 0 | |
4218 | 0 | const char *role = nullptr; |
4219 | 0 |
|
4220 | 0 | // Parse res_name into a name and role. Characters other than |
4221 | 0 | // [A-Za-z0-9_-] are converted to '_'. Anything after the first |
4222 | 0 | // colon is assigned to role; if there's no colon, assign the |
4223 | 0 | // whole thing to both role and res_name. |
4224 | 0 | for (char *c = res_name; *c; c++) { |
4225 | 0 | if (':' == *c) { |
4226 | 0 | *c = 0; |
4227 | 0 | role = c + 1; |
4228 | 0 | } |
4229 | 0 | else if (!isascii(*c) || (!isalnum(*c) && ('_' != *c) && ('-' != *c))) |
4230 | 0 | *c = '_'; |
4231 | 0 | } |
4232 | 0 | res_name[0] = toupper(res_name[0]); |
4233 | 0 | if (!role) role = res_name; |
4234 | 0 |
|
4235 | 0 | mGtkWindowTypeName = res_name; |
4236 | 0 | mGtkWindowRoleName = role; |
4237 | 0 | free(res_name); |
4238 | 0 |
|
4239 | 0 | RefreshWindowClass(); |
4240 | 0 | } |
4241 | | |
4242 | | void |
4243 | | nsWindow::NativeResize() |
4244 | 0 | { |
4245 | 0 | if (!AreBoundsSane()) { |
4246 | 0 | // If someone has set this so that the needs show flag is false |
4247 | 0 | // and it needs to be hidden, update the flag and hide the |
4248 | 0 | // window. This flag will be cleared the next time someone |
4249 | 0 | // hides the window or shows it. It also prevents us from |
4250 | 0 | // calling NativeShow(false) excessively on the window which |
4251 | 0 | // causes unneeded X traffic. |
4252 | 0 | if (!mNeedsShow && mIsShown) { |
4253 | 0 | mNeedsShow = true; |
4254 | 0 | NativeShow(false); |
4255 | 0 | } |
4256 | 0 | return; |
4257 | 0 | } |
4258 | 0 |
|
4259 | 0 | GdkRectangle size = DevicePixelsToGdkSizeRoundUp(mBounds.Size()); |
4260 | 0 |
|
4261 | 0 | LOG(("nsWindow::NativeResize [%p] %d %d\n", (void *)this, |
4262 | 0 | size.width, size.height)); |
4263 | 0 |
|
4264 | 0 | if (mIsTopLevel) { |
4265 | 0 | MOZ_ASSERT(size.width > 0 && size.height > 0, |
4266 | 0 | "Can't resize window smaller than 1x1."); |
4267 | 0 | gtk_window_resize(GTK_WINDOW(mShell), size.width, size.height); |
4268 | 0 | } |
4269 | 0 | else if (mContainer) { |
4270 | 0 | GtkWidget *widget = GTK_WIDGET(mContainer); |
4271 | 0 | GtkAllocation allocation, prev_allocation; |
4272 | 0 | gtk_widget_get_allocation(widget, &prev_allocation); |
4273 | 0 | allocation.x = prev_allocation.x; |
4274 | 0 | allocation.y = prev_allocation.y; |
4275 | 0 | allocation.width = size.width; |
4276 | 0 | allocation.height = size.height; |
4277 | 0 | gtk_widget_size_allocate(widget, &allocation); |
4278 | 0 | } |
4279 | 0 | else if (mGdkWindow) { |
4280 | 0 | gdk_window_resize(mGdkWindow, size.width, size.height); |
4281 | 0 | } |
4282 | 0 |
|
4283 | 0 | #ifdef MOZ_X11 |
4284 | 0 | // Notify the GtkCompositorWidget of a ClientSizeChange |
4285 | 0 | // This is different than OnSizeAllocate to catch initial sizing |
4286 | 0 | if (mCompositorWidgetDelegate) { |
4287 | 0 | mCompositorWidgetDelegate->NotifyClientSizeChanged(GetClientSize()); |
4288 | 0 | } |
4289 | 0 | #endif |
4290 | 0 |
|
4291 | 0 | // Does it need to be shown because bounds were previously insane? |
4292 | 0 | if (mNeedsShow && mIsShown) { |
4293 | 0 | NativeShow(true); |
4294 | 0 | } |
4295 | 0 | } |
4296 | | |
4297 | | void |
4298 | | nsWindow::NativeMoveResize() |
4299 | 0 | { |
4300 | 0 | if (!AreBoundsSane()) { |
4301 | 0 | // If someone has set this so that the needs show flag is false |
4302 | 0 | // and it needs to be hidden, update the flag and hide the |
4303 | 0 | // window. This flag will be cleared the next time someone |
4304 | 0 | // hides the window or shows it. It also prevents us from |
4305 | 0 | // calling NativeShow(false) excessively on the window which |
4306 | 0 | // causes unneeded X traffic. |
4307 | 0 | if (!mNeedsShow && mIsShown) { |
4308 | 0 | mNeedsShow = true; |
4309 | 0 | NativeShow(false); |
4310 | 0 | } |
4311 | 0 | NativeMove(); |
4312 | 0 |
|
4313 | 0 | return; |
4314 | 0 | } |
4315 | 0 |
|
4316 | 0 | GdkRectangle size = DevicePixelsToGdkSizeRoundUp(mBounds.Size()); |
4317 | 0 | GdkPoint topLeft = DevicePixelsToGdkPointRoundDown(mBounds.TopLeft()); |
4318 | 0 |
|
4319 | 0 | LOG(("nsWindow::NativeMoveResize [%p] %d %d %d %d\n", (void *)this, |
4320 | 0 | topLeft.x, topLeft.y, size.width, size.height)); |
4321 | 0 |
|
4322 | 0 | if (mIsTopLevel) { |
4323 | 0 | // x and y give the position of the window manager frame top-left. |
4324 | 0 | gtk_window_move(GTK_WINDOW(mShell), topLeft.x, topLeft.y); |
4325 | 0 | // This sets the client window size. |
4326 | 0 | MOZ_ASSERT(size.width > 0 && size.height > 0, |
4327 | 0 | "Can't resize window smaller than 1x1."); |
4328 | 0 | gtk_window_resize(GTK_WINDOW(mShell), size.width, size.height); |
4329 | 0 | } |
4330 | 0 | else if (mContainer) { |
4331 | 0 | GtkAllocation allocation; |
4332 | 0 | allocation.x = topLeft.x; |
4333 | 0 | allocation.y = topLeft.y; |
4334 | 0 | allocation.width = size.width; |
4335 | 0 | allocation.height = size.height; |
4336 | 0 | gtk_widget_size_allocate(GTK_WIDGET(mContainer), &allocation); |
4337 | 0 | } |
4338 | 0 | else if (mGdkWindow) { |
4339 | 0 | gdk_window_move_resize(mGdkWindow, |
4340 | 0 | topLeft.x, topLeft.y, size.width, size.height); |
4341 | 0 | } |
4342 | 0 |
|
4343 | 0 | #ifdef MOZ_X11 |
4344 | 0 | // Notify the GtkCompositorWidget of a ClientSizeChange |
4345 | 0 | // This is different than OnSizeAllocate to catch initial sizing |
4346 | 0 | if (mCompositorWidgetDelegate) { |
4347 | 0 | mCompositorWidgetDelegate->NotifyClientSizeChanged(GetClientSize()); |
4348 | 0 | } |
4349 | 0 | #endif |
4350 | 0 |
|
4351 | 0 | // Does it need to be shown because bounds were previously insane? |
4352 | 0 | if (mNeedsShow && mIsShown) { |
4353 | 0 | NativeShow(true); |
4354 | 0 | } |
4355 | 0 | } |
4356 | | |
4357 | | void |
4358 | | nsWindow::NativeShow(bool aAction) |
4359 | 0 | { |
4360 | 0 | if (aAction) { |
4361 | 0 | // unset our flag now that our window has been shown |
4362 | 0 | mNeedsShow = false; |
4363 | 0 |
|
4364 | 0 | if (mIsTopLevel) { |
4365 | 0 | // Set up usertime/startupID metadata for the created window. |
4366 | 0 | if (mWindowType != eWindowType_invisible) { |
4367 | 0 | SetUserTimeAndStartupIDForActivatedWindow(mShell); |
4368 | 0 | } |
4369 | 0 |
|
4370 | 0 | gtk_widget_show(mShell); |
4371 | 0 | } |
4372 | 0 | else if (mContainer) { |
4373 | 0 | gtk_widget_show(GTK_WIDGET(mContainer)); |
4374 | 0 | } |
4375 | 0 | else if (mGdkWindow) { |
4376 | 0 | gdk_window_show_unraised(mGdkWindow); |
4377 | 0 | } |
4378 | 0 | } |
4379 | 0 | else { |
4380 | | #ifdef MOZ_WAYLAND |
4381 | | if (mContainer && moz_container_has_wl_egl_window(mContainer)) { |
4382 | | // Because wl_egl_window is destroyed on moz_container_unmap(), |
4383 | | // the current compositor cannot use it anymore. To avoid crash, |
4384 | | // destroy the compositor & recreate a new compositor on next |
4385 | | // expose event. |
4386 | | DestroyLayerManager(); |
4387 | | } |
4388 | | #endif |
4389 | |
|
4390 | 0 | if (mIsTopLevel) { |
4391 | 0 | // Workaround window freezes on GTK versions before 3.21.2 by |
4392 | 0 | // ensuring that configure events get dispatched to windows before |
4393 | 0 | // they are unmapped. See bug 1225044. |
4394 | 0 | if (gtk_check_version(3, 21, 2) != nullptr && mPendingConfigures > 0) { |
4395 | 0 | GtkAllocation allocation; |
4396 | 0 | gtk_widget_get_allocation(GTK_WIDGET(mShell), &allocation); |
4397 | 0 |
|
4398 | 0 | GdkEventConfigure event; |
4399 | 0 | PodZero(&event); |
4400 | 0 | event.type = GDK_CONFIGURE; |
4401 | 0 | event.window = mGdkWindow; |
4402 | 0 | event.send_event = TRUE; |
4403 | 0 | event.x = allocation.x; |
4404 | 0 | event.y = allocation.y; |
4405 | 0 | event.width = allocation.width; |
4406 | 0 | event.height = allocation.height; |
4407 | 0 |
|
4408 | 0 | auto shellClass = GTK_WIDGET_GET_CLASS(mShell); |
4409 | 0 | for (unsigned int i = 0; i < mPendingConfigures; i++) { |
4410 | 0 | Unused << shellClass->configure_event(mShell, &event); |
4411 | 0 | } |
4412 | 0 | mPendingConfigures = 0; |
4413 | 0 | } |
4414 | 0 |
|
4415 | 0 | gtk_widget_hide(mShell); |
4416 | 0 |
|
4417 | 0 | ClearTransparencyBitmap(); // Release some resources |
4418 | 0 | } |
4419 | 0 | else if (mContainer) { |
4420 | 0 | gtk_widget_hide(GTK_WIDGET(mContainer)); |
4421 | 0 | } |
4422 | 0 | else if (mGdkWindow) { |
4423 | 0 | gdk_window_hide(mGdkWindow); |
4424 | 0 | } |
4425 | 0 | } |
4426 | 0 | } |
4427 | | |
4428 | | void |
4429 | | nsWindow::SetHasMappedToplevel(bool aState) |
4430 | 0 | { |
4431 | 0 | // Even when aState == mHasMappedToplevel (as when this method is called |
4432 | 0 | // from Show()), child windows need to have their state checked, so don't |
4433 | 0 | // return early. |
4434 | 0 | bool oldState = mHasMappedToplevel; |
4435 | 0 | mHasMappedToplevel = aState; |
4436 | 0 |
|
4437 | 0 | // mHasMappedToplevel is not updated for children of windows that are |
4438 | 0 | // hidden; GDK knows not to send expose events for these windows. The |
4439 | 0 | // state is recorded on the hidden window itself, but, for child trees of |
4440 | 0 | // hidden windows, their state essentially becomes disconnected from their |
4441 | 0 | // hidden parent. When the hidden parent gets shown, the child trees are |
4442 | 0 | // reconnected, and the state of the window being shown can be easily |
4443 | 0 | // propagated. |
4444 | 0 | if (!mIsShown || !mGdkWindow) |
4445 | 0 | return; |
4446 | 0 | |
4447 | 0 | if (aState && !oldState && !mIsFullyObscured) { |
4448 | 0 | // GDK_EXPOSE events have been ignored but the window is now visible, |
4449 | 0 | // so make sure GDK doesn't think that the window has already been |
4450 | 0 | // painted. |
4451 | 0 | gdk_window_invalidate_rect(mGdkWindow, nullptr, FALSE); |
4452 | 0 |
|
4453 | 0 | // Check that a grab didn't fail due to the window not being |
4454 | 0 | // viewable. |
4455 | 0 | EnsureGrabs(); |
4456 | 0 | } |
4457 | 0 |
|
4458 | 0 | for (GList *children = gdk_window_peek_children(mGdkWindow); |
4459 | 0 | children; |
4460 | 0 | children = children->next) { |
4461 | 0 | GdkWindow *gdkWin = GDK_WINDOW(children->data); |
4462 | 0 | nsWindow *child = get_window_for_gdk_window(gdkWin); |
4463 | 0 |
|
4464 | 0 | if (child && child->mHasMappedToplevel != aState) { |
4465 | 0 | child->SetHasMappedToplevel(aState); |
4466 | 0 | } |
4467 | 0 | } |
4468 | 0 | } |
4469 | | |
4470 | | LayoutDeviceIntSize |
4471 | | nsWindow::GetSafeWindowSize(LayoutDeviceIntSize aSize) |
4472 | 0 | { |
4473 | 0 | // The X protocol uses CARD32 for window sizes, but the server (1.11.3) |
4474 | 0 | // reads it as CARD16. Sizes of pixmaps, used for drawing, are (unsigned) |
4475 | 0 | // CARD16 in the protocol, but the server's ProcCreatePixmap returns |
4476 | 0 | // BadAlloc if dimensions cannot be represented by signed shorts. |
4477 | 0 | LayoutDeviceIntSize result = aSize; |
4478 | 0 | const int32_t kInt16Max = 32767; |
4479 | 0 | if (result.width > kInt16Max) { |
4480 | 0 | result.width = kInt16Max; |
4481 | 0 | } |
4482 | 0 | if (result.height > kInt16Max) { |
4483 | 0 | result.height = kInt16Max; |
4484 | 0 | } |
4485 | 0 | return result; |
4486 | 0 | } |
4487 | | |
4488 | | void |
4489 | | nsWindow::EnsureGrabs(void) |
4490 | 0 | { |
4491 | 0 | if (mRetryPointerGrab) |
4492 | 0 | GrabPointer(sRetryGrabTime); |
4493 | 0 | } |
4494 | | |
4495 | | void |
4496 | 0 | nsWindow::CleanLayerManagerRecursive(void) { |
4497 | 0 | if (mLayerManager) { |
4498 | 0 | mLayerManager->Destroy(); |
4499 | 0 | mLayerManager = nullptr; |
4500 | 0 | } |
4501 | 0 |
|
4502 | 0 | DestroyCompositor(); |
4503 | 0 |
|
4504 | 0 | GList* children = gdk_window_peek_children(mGdkWindow); |
4505 | 0 | for (GList* list = children; list; list = list->next) { |
4506 | 0 | nsWindow* window = get_window_for_gdk_window(GDK_WINDOW(list->data)); |
4507 | 0 | if (window) { |
4508 | 0 | window->CleanLayerManagerRecursive(); |
4509 | 0 | } |
4510 | 0 | } |
4511 | 0 | } |
4512 | | |
4513 | | void |
4514 | | nsWindow::SetTransparencyMode(nsTransparencyMode aMode) |
4515 | 0 | { |
4516 | 0 | if (!mShell) { |
4517 | 0 | // Pass the request to the toplevel window |
4518 | 0 | GtkWidget *topWidget = GetToplevelWidget(); |
4519 | 0 | if (!topWidget) |
4520 | 0 | return; |
4521 | 0 | |
4522 | 0 | nsWindow *topWindow = get_window_for_gtk_widget(topWidget); |
4523 | 0 | if (!topWindow) |
4524 | 0 | return; |
4525 | 0 | |
4526 | 0 | topWindow->SetTransparencyMode(aMode); |
4527 | 0 | return; |
4528 | 0 | } |
4529 | 0 | |
4530 | 0 | bool isTransparent = aMode == eTransparencyTransparent; |
4531 | 0 |
|
4532 | 0 | if (mIsTransparent == isTransparent) { |
4533 | 0 | return; |
4534 | 0 | } else if (mWindowType != eWindowType_popup) { |
4535 | 0 | NS_WARNING("Cannot set transparency mode on non-popup windows."); |
4536 | 0 | return; |
4537 | 0 | } |
4538 | 0 |
|
4539 | 0 | if (!isTransparent) { |
4540 | 0 | ClearTransparencyBitmap(); |
4541 | 0 | } // else the new default alpha values are "all 1", so we don't |
4542 | 0 | // need to change anything yet |
4543 | 0 |
|
4544 | 0 | mIsTransparent = isTransparent; |
4545 | 0 |
|
4546 | 0 | // Need to clean our LayerManager up while still alive because |
4547 | 0 | // we don't want to use layers acceleration on shaped windows |
4548 | 0 | CleanLayerManagerRecursive(); |
4549 | 0 | } |
4550 | | |
4551 | | nsTransparencyMode |
4552 | | nsWindow::GetTransparencyMode() |
4553 | 0 | { |
4554 | 0 | if (!mShell) { |
4555 | 0 | // Pass the request to the toplevel window |
4556 | 0 | GtkWidget *topWidget = GetToplevelWidget(); |
4557 | 0 | if (!topWidget) { |
4558 | 0 | return eTransparencyOpaque; |
4559 | 0 | } |
4560 | 0 | |
4561 | 0 | nsWindow *topWindow = get_window_for_gtk_widget(topWidget); |
4562 | 0 | if (!topWindow) { |
4563 | 0 | return eTransparencyOpaque; |
4564 | 0 | } |
4565 | 0 | |
4566 | 0 | return topWindow->GetTransparencyMode(); |
4567 | 0 | } |
4568 | 0 | |
4569 | 0 | return mIsTransparent ? eTransparencyTransparent : eTransparencyOpaque; |
4570 | 0 | } |
4571 | | |
4572 | | // For setting the draggable titlebar region from CSS |
4573 | | // with -moz-window-dragging: drag. |
4574 | | void |
4575 | | nsWindow::UpdateWindowDraggingRegion(const LayoutDeviceIntRegion& aRegion) |
4576 | 0 | { |
4577 | 0 | if (mDraggableRegion != aRegion) { |
4578 | 0 | mDraggableRegion = aRegion; |
4579 | 0 | } |
4580 | 0 | } |
4581 | | |
4582 | | #if (MOZ_WIDGET_GTK >= 3) |
4583 | | void nsWindow::UpdateOpaqueRegion(const LayoutDeviceIntRegion& aOpaqueRegion) |
4584 | 0 | { |
4585 | 0 | // Available as of GTK 3.10+ |
4586 | 0 | static auto sGdkWindowSetOpaqueRegion = |
4587 | 0 | (void (*)(GdkWindow*, cairo_region_t*)) |
4588 | 0 | dlsym(RTLD_DEFAULT, "gdk_window_set_opaque_region"); |
4589 | 0 |
|
4590 | 0 | if (sGdkWindowSetOpaqueRegion && mGdkWindow && |
4591 | 0 | gdk_window_get_window_type(mGdkWindow) == GDK_WINDOW_TOPLEVEL) { |
4592 | 0 | if (aOpaqueRegion.IsEmpty()) { |
4593 | 0 | (*sGdkWindowSetOpaqueRegion)(mGdkWindow, nullptr); |
4594 | 0 | } else { |
4595 | 0 | cairo_region_t *region = cairo_region_create(); |
4596 | 0 | for (auto iter = aOpaqueRegion.RectIter(); !iter.Done(); |
4597 | 0 | iter.Next()) { |
4598 | 0 | const LayoutDeviceIntRect &r = iter.Get(); |
4599 | 0 | cairo_rectangle_int_t rect = { r.x, r.y, r.width, r.height }; |
4600 | 0 | cairo_region_union_rectangle(region, &rect); |
4601 | 0 | } |
4602 | 0 | (*sGdkWindowSetOpaqueRegion)(mGdkWindow, region); |
4603 | 0 | cairo_region_destroy(region); |
4604 | 0 | } |
4605 | 0 | } |
4606 | 0 | } |
4607 | | #endif |
4608 | | |
4609 | | nsresult |
4610 | | nsWindow::ConfigureChildren(const nsTArray<Configuration>& aConfigurations) |
4611 | 0 | { |
4612 | 0 | // If this is a remotely updated widget we receive clipping, position, and |
4613 | 0 | // size information from a source other than our owner. Don't let our parent |
4614 | 0 | // update this information. |
4615 | 0 | if (mWindowType == eWindowType_plugin_ipc_chrome) { |
4616 | 0 | return NS_OK; |
4617 | 0 | } |
4618 | 0 | |
4619 | 0 | for (uint32_t i = 0; i < aConfigurations.Length(); ++i) { |
4620 | 0 | const Configuration& configuration = aConfigurations[i]; |
4621 | 0 | auto* w = static_cast<nsWindow*>(configuration.mChild.get()); |
4622 | 0 | NS_ASSERTION(w->GetParent() == this, |
4623 | 0 | "Configured widget is not a child"); |
4624 | 0 | w->SetWindowClipRegion(configuration.mClipRegion, true); |
4625 | 0 | if (w->mBounds.Size() != configuration.mBounds.Size()) { |
4626 | 0 | w->Resize(configuration.mBounds.x, configuration.mBounds.y, |
4627 | 0 | configuration.mBounds.width, configuration.mBounds.height, |
4628 | 0 | true); |
4629 | 0 | } else if (w->mBounds.TopLeft() != configuration.mBounds.TopLeft()) { |
4630 | 0 | w->Move(configuration.mBounds.x, configuration.mBounds.y); |
4631 | 0 | } |
4632 | 0 | w->SetWindowClipRegion(configuration.mClipRegion, false); |
4633 | 0 | } |
4634 | 0 | return NS_OK; |
4635 | 0 | } |
4636 | | |
4637 | | nsresult |
4638 | | nsWindow::SetWindowClipRegion(const nsTArray<LayoutDeviceIntRect>& aRects, |
4639 | | bool aIntersectWithExisting) |
4640 | 0 | { |
4641 | 0 | const nsTArray<LayoutDeviceIntRect>* newRects = &aRects; |
4642 | 0 |
|
4643 | 0 | AutoTArray<LayoutDeviceIntRect,1> intersectRects; |
4644 | 0 | if (aIntersectWithExisting) { |
4645 | 0 | AutoTArray<LayoutDeviceIntRect,1> existingRects; |
4646 | 0 | GetWindowClipRegion(&existingRects); |
4647 | 0 |
|
4648 | 0 | LayoutDeviceIntRegion existingRegion = RegionFromArray(existingRects); |
4649 | 0 | LayoutDeviceIntRegion newRegion = RegionFromArray(aRects); |
4650 | 0 | LayoutDeviceIntRegion intersectRegion; |
4651 | 0 | intersectRegion.And(newRegion, existingRegion); |
4652 | 0 |
|
4653 | 0 | // If mClipRects is null we haven't set a clip rect yet, so we |
4654 | 0 | // need to set the clip even if it is equal. |
4655 | 0 | if (mClipRects && intersectRegion.IsEqual(existingRegion)) { |
4656 | 0 | return NS_OK; |
4657 | 0 | } |
4658 | 0 | |
4659 | 0 | if (!intersectRegion.IsEqual(newRegion)) { |
4660 | 0 | ArrayFromRegion(intersectRegion, intersectRects); |
4661 | 0 | newRects = &intersectRects; |
4662 | 0 | } |
4663 | 0 | } |
4664 | 0 |
|
4665 | 0 | if (IsWindowClipRegionEqual(*newRects)) |
4666 | 0 | return NS_OK; |
4667 | 0 | |
4668 | 0 | StoreWindowClipRegion(*newRects); |
4669 | 0 |
|
4670 | 0 | if (!mGdkWindow) |
4671 | 0 | return NS_OK; |
4672 | 0 | |
4673 | 0 | cairo_region_t *region = cairo_region_create(); |
4674 | 0 | for (uint32_t i = 0; i < newRects->Length(); ++i) { |
4675 | 0 | const LayoutDeviceIntRect& r = newRects->ElementAt(i); |
4676 | 0 | cairo_rectangle_int_t rect = { r.x, r.y, r.width, r.height }; |
4677 | 0 | cairo_region_union_rectangle(region, &rect); |
4678 | 0 | } |
4679 | 0 |
|
4680 | 0 | gdk_window_shape_combine_region(mGdkWindow, region, 0, 0); |
4681 | 0 | cairo_region_destroy(region); |
4682 | 0 |
|
4683 | 0 | return NS_OK; |
4684 | 0 | } |
4685 | | |
4686 | | void |
4687 | | nsWindow::ResizeTransparencyBitmap() |
4688 | 0 | { |
4689 | 0 | if (!mTransparencyBitmap) |
4690 | 0 | return; |
4691 | 0 | |
4692 | 0 | if (mBounds.width == mTransparencyBitmapWidth && |
4693 | 0 | mBounds.height == mTransparencyBitmapHeight) |
4694 | 0 | return; |
4695 | 0 | |
4696 | 0 | int32_t newRowBytes = GetBitmapStride(mBounds.width); |
4697 | 0 | int32_t newSize = newRowBytes * mBounds.height; |
4698 | 0 | auto* newBits = new gchar[newSize]; |
4699 | 0 | // fill new mask with "transparent", first |
4700 | 0 | memset(newBits, 0, newSize); |
4701 | 0 |
|
4702 | 0 | // Now copy the intersection of the old and new areas into the new mask |
4703 | 0 | int32_t copyWidth = std::min(mBounds.width, mTransparencyBitmapWidth); |
4704 | 0 | int32_t copyHeight = std::min(mBounds.height, mTransparencyBitmapHeight); |
4705 | 0 | int32_t oldRowBytes = GetBitmapStride(mTransparencyBitmapWidth); |
4706 | 0 | int32_t copyBytes = GetBitmapStride(copyWidth); |
4707 | 0 |
|
4708 | 0 | int32_t i; |
4709 | 0 | gchar* fromPtr = mTransparencyBitmap; |
4710 | 0 | gchar* toPtr = newBits; |
4711 | 0 | for (i = 0; i < copyHeight; i++) { |
4712 | 0 | memcpy(toPtr, fromPtr, copyBytes); |
4713 | 0 | fromPtr += oldRowBytes; |
4714 | 0 | toPtr += newRowBytes; |
4715 | 0 | } |
4716 | 0 |
|
4717 | 0 | delete[] mTransparencyBitmap; |
4718 | 0 | mTransparencyBitmap = newBits; |
4719 | 0 | mTransparencyBitmapWidth = mBounds.width; |
4720 | 0 | mTransparencyBitmapHeight = mBounds.height; |
4721 | 0 | } |
4722 | | |
4723 | | static bool |
4724 | | ChangedMaskBits(gchar* aMaskBits, int32_t aMaskWidth, int32_t aMaskHeight, |
4725 | | const nsIntRect& aRect, uint8_t* aAlphas, int32_t aStride) |
4726 | 0 | { |
4727 | 0 | int32_t x, y, xMax = aRect.XMost(), yMax = aRect.YMost(); |
4728 | 0 | int32_t maskBytesPerRow = GetBitmapStride(aMaskWidth); |
4729 | 0 | for (y = aRect.y; y < yMax; y++) { |
4730 | 0 | gchar* maskBytes = aMaskBits + y*maskBytesPerRow; |
4731 | 0 | uint8_t* alphas = aAlphas; |
4732 | 0 | for (x = aRect.x; x < xMax; x++) { |
4733 | 0 | bool newBit = *alphas > 0x7f; |
4734 | 0 | alphas++; |
4735 | 0 |
|
4736 | 0 | gchar maskByte = maskBytes[x >> 3]; |
4737 | 0 | bool maskBit = (maskByte & (1 << (x & 7))) != 0; |
4738 | 0 |
|
4739 | 0 | if (maskBit != newBit) { |
4740 | 0 | return true; |
4741 | 0 | } |
4742 | 0 | } |
4743 | 0 | aAlphas += aStride; |
4744 | 0 | } |
4745 | 0 |
|
4746 | 0 | return false; |
4747 | 0 | } |
4748 | | |
4749 | | static |
4750 | | void UpdateMaskBits(gchar* aMaskBits, int32_t aMaskWidth, int32_t aMaskHeight, |
4751 | | const nsIntRect& aRect, uint8_t* aAlphas, int32_t aStride) |
4752 | 0 | { |
4753 | 0 | int32_t x, y, xMax = aRect.XMost(), yMax = aRect.YMost(); |
4754 | 0 | int32_t maskBytesPerRow = GetBitmapStride(aMaskWidth); |
4755 | 0 | for (y = aRect.y; y < yMax; y++) { |
4756 | 0 | gchar* maskBytes = aMaskBits + y*maskBytesPerRow; |
4757 | 0 | uint8_t* alphas = aAlphas; |
4758 | 0 | for (x = aRect.x; x < xMax; x++) { |
4759 | 0 | bool newBit = *alphas > 0x7f; |
4760 | 0 | alphas++; |
4761 | 0 |
|
4762 | 0 | gchar mask = 1 << (x & 7); |
4763 | 0 | gchar maskByte = maskBytes[x >> 3]; |
4764 | 0 | // Note: '-newBit' turns 0 into 00...00 and 1 into 11...11 |
4765 | 0 | maskBytes[x >> 3] = (maskByte & ~mask) | (-newBit & mask); |
4766 | 0 | } |
4767 | 0 | aAlphas += aStride; |
4768 | 0 | } |
4769 | 0 | } |
4770 | | |
4771 | | void |
4772 | | nsWindow::ApplyTransparencyBitmap() |
4773 | 0 | { |
4774 | 0 | #ifdef MOZ_X11 |
4775 | 0 | // We use X11 calls where possible, because GDK handles expose events |
4776 | 0 | // for shaped windows in a way that's incompatible with us (Bug 635903). |
4777 | 0 | // It doesn't occur when the shapes are set through X. |
4778 | 0 | Display* xDisplay = GDK_WINDOW_XDISPLAY(mGdkWindow); |
4779 | 0 | Window xDrawable = GDK_WINDOW_XID(mGdkWindow); |
4780 | 0 | Pixmap maskPixmap = XCreateBitmapFromData(xDisplay, |
4781 | 0 | xDrawable, |
4782 | 0 | mTransparencyBitmap, |
4783 | 0 | mTransparencyBitmapWidth, |
4784 | 0 | mTransparencyBitmapHeight); |
4785 | 0 | XShapeCombineMask(xDisplay, xDrawable, |
4786 | 0 | ShapeBounding, 0, 0, |
4787 | 0 | maskPixmap, ShapeSet); |
4788 | 0 | XFreePixmap(xDisplay, maskPixmap); |
4789 | | #else |
4790 | | cairo_surface_t *maskBitmap; |
4791 | | maskBitmap = cairo_image_surface_create_for_data((unsigned char*)mTransparencyBitmap, |
4792 | | CAIRO_FORMAT_A1, |
4793 | | mTransparencyBitmapWidth, |
4794 | | mTransparencyBitmapHeight, |
4795 | | GetBitmapStride(mTransparencyBitmapWidth)); |
4796 | | if (!maskBitmap) |
4797 | | return; |
4798 | | |
4799 | | cairo_region_t * maskRegion = gdk_cairo_region_create_from_surface(maskBitmap); |
4800 | | gtk_widget_shape_combine_region(mShell, maskRegion); |
4801 | | cairo_region_destroy(maskRegion); |
4802 | | cairo_surface_destroy(maskBitmap); |
4803 | | #endif // MOZ_X11 |
4804 | | } |
4805 | | |
4806 | | void |
4807 | | nsWindow::ClearTransparencyBitmap() |
4808 | 0 | { |
4809 | 0 | if (!mTransparencyBitmap) |
4810 | 0 | return; |
4811 | 0 | |
4812 | 0 | delete[] mTransparencyBitmap; |
4813 | 0 | mTransparencyBitmap = nullptr; |
4814 | 0 | mTransparencyBitmapWidth = 0; |
4815 | 0 | mTransparencyBitmapHeight = 0; |
4816 | 0 |
|
4817 | 0 | if (!mShell) |
4818 | 0 | return; |
4819 | 0 | |
4820 | 0 | #ifdef MOZ_X11 |
4821 | 0 | if (!mGdkWindow) |
4822 | 0 | return; |
4823 | 0 | |
4824 | 0 | Display* xDisplay = GDK_WINDOW_XDISPLAY(mGdkWindow); |
4825 | 0 | Window xWindow = gdk_x11_window_get_xid(mGdkWindow); |
4826 | 0 |
|
4827 | 0 | XShapeCombineMask(xDisplay, xWindow, ShapeBounding, 0, 0, X11None, ShapeSet); |
4828 | 0 | #endif |
4829 | 0 | } |
4830 | | |
4831 | | nsresult |
4832 | | nsWindow::UpdateTranslucentWindowAlphaInternal(const nsIntRect& aRect, |
4833 | | uint8_t* aAlphas, int32_t aStride) |
4834 | 0 | { |
4835 | 0 | if (!mShell) { |
4836 | 0 | // Pass the request to the toplevel window |
4837 | 0 | GtkWidget *topWidget = GetToplevelWidget(); |
4838 | 0 | if (!topWidget) |
4839 | 0 | return NS_ERROR_FAILURE; |
4840 | 0 | |
4841 | 0 | nsWindow *topWindow = get_window_for_gtk_widget(topWidget); |
4842 | 0 | if (!topWindow) |
4843 | 0 | return NS_ERROR_FAILURE; |
4844 | 0 | |
4845 | 0 | return topWindow->UpdateTranslucentWindowAlphaInternal(aRect, aAlphas, aStride); |
4846 | 0 | } |
4847 | 0 | |
4848 | 0 | NS_ASSERTION(mIsTransparent, "Window is not transparent"); |
4849 | 0 |
|
4850 | 0 | if (mTransparencyBitmap == nullptr) { |
4851 | 0 | int32_t size = GetBitmapStride(mBounds.width)*mBounds.height; |
4852 | 0 | mTransparencyBitmap = new gchar[size]; |
4853 | 0 | memset(mTransparencyBitmap, 255, size); |
4854 | 0 | mTransparencyBitmapWidth = mBounds.width; |
4855 | 0 | mTransparencyBitmapHeight = mBounds.height; |
4856 | 0 | } else { |
4857 | 0 | ResizeTransparencyBitmap(); |
4858 | 0 | } |
4859 | 0 |
|
4860 | 0 | nsIntRect rect; |
4861 | 0 | rect.IntersectRect(aRect, nsIntRect(0, 0, mBounds.width, mBounds.height)); |
4862 | 0 |
|
4863 | 0 | if (!ChangedMaskBits(mTransparencyBitmap, mBounds.width, mBounds.height, |
4864 | 0 | rect, aAlphas, aStride)) |
4865 | 0 | // skip the expensive stuff if the mask bits haven't changed; hopefully |
4866 | 0 | // this is the common case |
4867 | 0 | return NS_OK; |
4868 | 0 | |
4869 | 0 | UpdateMaskBits(mTransparencyBitmap, mBounds.width, mBounds.height, |
4870 | 0 | rect, aAlphas, aStride); |
4871 | 0 |
|
4872 | 0 | if (!mNeedsShow) { |
4873 | 0 | ApplyTransparencyBitmap(); |
4874 | 0 | } |
4875 | 0 | return NS_OK; |
4876 | 0 | } |
4877 | | |
4878 | | void |
4879 | | nsWindow::GrabPointer(guint32 aTime) |
4880 | 0 | { |
4881 | 0 | LOG(("GrabPointer time=0x%08x retry=%d\n", |
4882 | 0 | (unsigned int)aTime, mRetryPointerGrab)); |
4883 | 0 |
|
4884 | 0 | mRetryPointerGrab = false; |
4885 | 0 | sRetryGrabTime = aTime; |
4886 | 0 |
|
4887 | 0 | // If the window isn't visible, just set the flag to retry the |
4888 | 0 | // grab. When this window becomes visible, the grab will be |
4889 | 0 | // retried. |
4890 | 0 | if (!mHasMappedToplevel || mIsFullyObscured) { |
4891 | 0 | LOG(("GrabPointer: window not visible\n")); |
4892 | 0 | mRetryPointerGrab = true; |
4893 | 0 | return; |
4894 | 0 | } |
4895 | 0 |
|
4896 | 0 | if (!mGdkWindow) |
4897 | 0 | return; |
4898 | 0 | |
4899 | 0 | if (!mIsX11Display) { |
4900 | 0 | // Don't to the grab on Wayland as it causes a regression |
4901 | 0 | // from Bug 1377084. |
4902 | 0 | return; |
4903 | 0 | } |
4904 | 0 | |
4905 | 0 | gint retval; |
4906 | 0 | // Note that we need GDK_TOUCH_MASK below to work around a GDK/X11 bug that |
4907 | 0 | // causes touch events that would normally be received by this client on |
4908 | 0 | // other windows to be discarded during the grab. |
4909 | 0 | retval = gdk_pointer_grab(mGdkWindow, TRUE, |
4910 | 0 | (GdkEventMask)(GDK_BUTTON_PRESS_MASK | |
4911 | 0 | GDK_BUTTON_RELEASE_MASK | |
4912 | 0 | GDK_ENTER_NOTIFY_MASK | |
4913 | 0 | GDK_LEAVE_NOTIFY_MASK | |
4914 | 0 | GDK_POINTER_MOTION_MASK | |
4915 | 0 | GDK_TOUCH_MASK), |
4916 | 0 | (GdkWindow *)nullptr, nullptr, aTime); |
4917 | 0 |
|
4918 | 0 | if (retval == GDK_GRAB_NOT_VIEWABLE) { |
4919 | 0 | LOG(("GrabPointer: window not viewable; will retry\n")); |
4920 | 0 | mRetryPointerGrab = true; |
4921 | 0 | } else if (retval != GDK_GRAB_SUCCESS) { |
4922 | 0 | LOG(("GrabPointer: pointer grab failed: %i\n", retval)); |
4923 | 0 | // A failed grab indicates that another app has grabbed the pointer. |
4924 | 0 | // Check for rollup now, because, without the grab, we likely won't |
4925 | 0 | // get subsequent button press events. Do this with an event so that |
4926 | 0 | // popups don't rollup while potentially adjusting the grab for |
4927 | 0 | // this popup. |
4928 | 0 | nsCOMPtr<nsIRunnable> event = |
4929 | 0 | NewRunnableMethod("nsWindow::CheckForRollupDuringGrab", |
4930 | 0 | this, |
4931 | 0 | &nsWindow::CheckForRollupDuringGrab); |
4932 | 0 | NS_DispatchToCurrentThread(event.forget()); |
4933 | 0 | } |
4934 | 0 | } |
4935 | | |
4936 | | void |
4937 | | nsWindow::ReleaseGrabs(void) |
4938 | 0 | { |
4939 | 0 | LOG(("ReleaseGrabs\n")); |
4940 | 0 |
|
4941 | 0 | mRetryPointerGrab = false; |
4942 | 0 |
|
4943 | 0 | if (!mIsX11Display) { |
4944 | 0 | // Don't to the ungrab on Wayland as it causes a regression |
4945 | 0 | // from Bug 1377084. |
4946 | 0 | return; |
4947 | 0 | } |
4948 | 0 | |
4949 | 0 | gdk_pointer_ungrab(GDK_CURRENT_TIME); |
4950 | 0 | } |
4951 | | |
4952 | | GtkWidget * |
4953 | | nsWindow::GetToplevelWidget() |
4954 | 0 | { |
4955 | 0 | if (mShell) { |
4956 | 0 | return mShell; |
4957 | 0 | } |
4958 | 0 | |
4959 | 0 | GtkWidget *widget = GetMozContainerWidget(); |
4960 | 0 | if (!widget) |
4961 | 0 | return nullptr; |
4962 | 0 | |
4963 | 0 | return gtk_widget_get_toplevel(widget); |
4964 | 0 | } |
4965 | | |
4966 | | GtkWidget * |
4967 | | nsWindow::GetMozContainerWidget() |
4968 | 0 | { |
4969 | 0 | if (!mGdkWindow) |
4970 | 0 | return nullptr; |
4971 | 0 | |
4972 | 0 | if (mContainer) |
4973 | 0 | return GTK_WIDGET(mContainer); |
4974 | 0 | |
4975 | 0 | GtkWidget *owningWidget = |
4976 | 0 | get_gtk_widget_for_gdk_window(mGdkWindow); |
4977 | 0 | return owningWidget; |
4978 | 0 | } |
4979 | | |
4980 | | nsWindow * |
4981 | | nsWindow::GetContainerWindow() |
4982 | 0 | { |
4983 | 0 | GtkWidget *owningWidget = GetMozContainerWidget(); |
4984 | 0 | if (!owningWidget) |
4985 | 0 | return nullptr; |
4986 | 0 | |
4987 | 0 | nsWindow *window = get_window_for_gtk_widget(owningWidget); |
4988 | 0 | NS_ASSERTION(window, "No nsWindow for container widget"); |
4989 | 0 | return window; |
4990 | 0 | } |
4991 | | |
4992 | | void |
4993 | | nsWindow::SetUrgencyHint(GtkWidget *top_window, bool state) |
4994 | 0 | { |
4995 | 0 | if (!top_window) |
4996 | 0 | return; |
4997 | 0 | |
4998 | 0 | gdk_window_set_urgency_hint(gtk_widget_get_window(top_window), state); |
4999 | 0 | } |
5000 | | |
5001 | | void |
5002 | | nsWindow::SetDefaultIcon(void) |
5003 | 0 | { |
5004 | 0 | SetIcon(NS_LITERAL_STRING("default")); |
5005 | 0 | } |
5006 | | |
5007 | | gint |
5008 | | nsWindow::ConvertBorderStyles(nsBorderStyle aStyle) |
5009 | 0 | { |
5010 | 0 | gint w = 0; |
5011 | 0 |
|
5012 | 0 | if (aStyle == eBorderStyle_default) |
5013 | 0 | return -1; |
5014 | 0 | |
5015 | 0 | // note that we don't handle eBorderStyle_close yet |
5016 | 0 | if (aStyle & eBorderStyle_all) |
5017 | 0 | w |= GDK_DECOR_ALL; |
5018 | 0 | if (aStyle & eBorderStyle_border) |
5019 | 0 | w |= GDK_DECOR_BORDER; |
5020 | 0 | if (aStyle & eBorderStyle_resizeh) |
5021 | 0 | w |= GDK_DECOR_RESIZEH; |
5022 | 0 | if (aStyle & eBorderStyle_title) |
5023 | 0 | w |= GDK_DECOR_TITLE; |
5024 | 0 | if (aStyle & eBorderStyle_menu) |
5025 | 0 | w |= GDK_DECOR_MENU; |
5026 | 0 | if (aStyle & eBorderStyle_minimize) |
5027 | 0 | w |= GDK_DECOR_MINIMIZE; |
5028 | 0 | if (aStyle & eBorderStyle_maximize) |
5029 | 0 | w |= GDK_DECOR_MAXIMIZE; |
5030 | 0 |
|
5031 | 0 | return w; |
5032 | 0 | } |
5033 | | |
5034 | | class FullscreenTransitionWindow final : public nsISupports |
5035 | | { |
5036 | | public: |
5037 | | NS_DECL_ISUPPORTS |
5038 | | |
5039 | | explicit FullscreenTransitionWindow(GtkWidget* aWidget); |
5040 | | |
5041 | | GtkWidget* mWindow; |
5042 | | |
5043 | | private: |
5044 | | ~FullscreenTransitionWindow(); |
5045 | | }; |
5046 | | |
5047 | | NS_IMPL_ISUPPORTS0(FullscreenTransitionWindow) |
5048 | | |
5049 | | FullscreenTransitionWindow::FullscreenTransitionWindow(GtkWidget* aWidget) |
5050 | 0 | { |
5051 | 0 | mWindow = gtk_window_new(GTK_WINDOW_POPUP); |
5052 | 0 | GtkWindow* gtkWin = GTK_WINDOW(mWindow); |
5053 | 0 |
|
5054 | 0 | gtk_window_set_type_hint(gtkWin, GDK_WINDOW_TYPE_HINT_SPLASHSCREEN); |
5055 | 0 | gtk_window_set_transient_for(gtkWin, GTK_WINDOW(aWidget)); |
5056 | 0 | gtk_window_set_decorated(gtkWin, false); |
5057 | 0 |
|
5058 | 0 | GdkWindow* gdkWin = gtk_widget_get_window(aWidget); |
5059 | 0 | GdkScreen* screen = gtk_widget_get_screen(aWidget); |
5060 | 0 | gint monitorNum = gdk_screen_get_monitor_at_window(screen, gdkWin); |
5061 | 0 | GdkRectangle monitorRect; |
5062 | 0 | gdk_screen_get_monitor_geometry(screen, monitorNum, &monitorRect); |
5063 | 0 | gtk_window_set_screen(gtkWin, screen); |
5064 | 0 | gtk_window_move(gtkWin, monitorRect.x, monitorRect.y); |
5065 | 0 | MOZ_ASSERT(monitorRect.width > 0 && monitorRect.height > 0, |
5066 | 0 | "Can't resize window smaller than 1x1."); |
5067 | 0 | gtk_window_resize(gtkWin, monitorRect.width, monitorRect.height); |
5068 | 0 |
|
5069 | 0 | GdkColor bgColor; |
5070 | 0 | bgColor.red = bgColor.green = bgColor.blue = 0; |
5071 | 0 | gtk_widget_modify_bg(mWindow, GTK_STATE_NORMAL, &bgColor); |
5072 | 0 |
|
5073 | 0 | gtk_window_set_opacity(gtkWin, 0.0); |
5074 | 0 | gtk_widget_show(mWindow); |
5075 | 0 | } |
5076 | | |
5077 | | FullscreenTransitionWindow::~FullscreenTransitionWindow() |
5078 | 0 | { |
5079 | 0 | gtk_widget_destroy(mWindow); |
5080 | 0 | } |
5081 | | |
5082 | | class FullscreenTransitionData |
5083 | | { |
5084 | | public: |
5085 | | FullscreenTransitionData(nsIWidget::FullscreenTransitionStage aStage, |
5086 | | uint16_t aDuration, nsIRunnable* aCallback, |
5087 | | FullscreenTransitionWindow* aWindow) |
5088 | | : mStage(aStage) |
5089 | | , mStartTime(TimeStamp::Now()) |
5090 | | , mDuration(TimeDuration::FromMilliseconds(aDuration)) |
5091 | | , mCallback(aCallback) |
5092 | 0 | , mWindow(aWindow) { } |
5093 | | |
5094 | | static const guint sInterval = 1000 / 30; // 30fps |
5095 | | static gboolean TimeoutCallback(gpointer aData); |
5096 | | |
5097 | | private: |
5098 | | nsIWidget::FullscreenTransitionStage mStage; |
5099 | | TimeStamp mStartTime; |
5100 | | TimeDuration mDuration; |
5101 | | nsCOMPtr<nsIRunnable> mCallback; |
5102 | | RefPtr<FullscreenTransitionWindow> mWindow; |
5103 | | }; |
5104 | | |
5105 | | /* static */ gboolean |
5106 | | FullscreenTransitionData::TimeoutCallback(gpointer aData) |
5107 | 0 | { |
5108 | 0 | bool finishing = false; |
5109 | 0 | auto data = static_cast<FullscreenTransitionData*>(aData); |
5110 | 0 | gdouble opacity = (TimeStamp::Now() - data->mStartTime) / data->mDuration; |
5111 | 0 | if (opacity >= 1.0) { |
5112 | 0 | opacity = 1.0; |
5113 | 0 | finishing = true; |
5114 | 0 | } |
5115 | 0 | if (data->mStage == nsIWidget::eAfterFullscreenToggle) { |
5116 | 0 | opacity = 1.0 - opacity; |
5117 | 0 | } |
5118 | 0 | gtk_window_set_opacity(GTK_WINDOW(data->mWindow->mWindow), opacity); |
5119 | 0 |
|
5120 | 0 | if (!finishing) { |
5121 | 0 | return TRUE; |
5122 | 0 | } |
5123 | 0 | NS_DispatchToMainThread(data->mCallback.forget()); |
5124 | 0 | delete data; |
5125 | 0 | return FALSE; |
5126 | 0 | } |
5127 | | |
5128 | | /* virtual */ bool |
5129 | | nsWindow::PrepareForFullscreenTransition(nsISupports** aData) |
5130 | 0 | { |
5131 | 0 | GdkScreen* screen = gtk_widget_get_screen(mShell); |
5132 | 0 | if (!gdk_screen_is_composited(screen)) { |
5133 | 0 | return false; |
5134 | 0 | } |
5135 | 0 | *aData = do_AddRef(new FullscreenTransitionWindow(mShell)).take(); |
5136 | 0 | return true; |
5137 | 0 | } |
5138 | | |
5139 | | /* virtual */ void |
5140 | | nsWindow::PerformFullscreenTransition(FullscreenTransitionStage aStage, |
5141 | | uint16_t aDuration, nsISupports* aData, |
5142 | | nsIRunnable* aCallback) |
5143 | 0 | { |
5144 | 0 | auto data = static_cast<FullscreenTransitionWindow*>(aData); |
5145 | 0 | // This will be released at the end of the last timeout callback for it. |
5146 | 0 | auto transitionData = new FullscreenTransitionData(aStage, aDuration, |
5147 | 0 | aCallback, data); |
5148 | 0 | g_timeout_add_full(G_PRIORITY_HIGH, |
5149 | 0 | FullscreenTransitionData::sInterval, |
5150 | 0 | FullscreenTransitionData::TimeoutCallback, |
5151 | 0 | transitionData, nullptr); |
5152 | 0 | } |
5153 | | |
5154 | | already_AddRefed<nsIScreen> |
5155 | | nsWindow::GetWidgetScreen() |
5156 | 0 | { |
5157 | 0 | nsCOMPtr<nsIScreenManager> screenManager; |
5158 | 0 | screenManager = do_GetService("@mozilla.org/gfx/screenmanager;1"); |
5159 | 0 | if (!screenManager) { |
5160 | 0 | return nullptr; |
5161 | 0 | } |
5162 | 0 | |
5163 | 0 | // GetScreenBounds() is slow for the GTK port so we override and use |
5164 | 0 | // mBounds directly. |
5165 | 0 | LayoutDeviceIntRect bounds = mBounds; |
5166 | 0 | if (!mIsTopLevel) { |
5167 | 0 | bounds.MoveTo(WidgetToScreenOffset()); |
5168 | 0 | } |
5169 | 0 |
|
5170 | 0 | DesktopIntRect deskBounds = RoundedToInt(bounds / GetDesktopToDeviceScale()); |
5171 | 0 | nsCOMPtr<nsIScreen> screen; |
5172 | 0 | screenManager->ScreenForRect(deskBounds.x, deskBounds.y, |
5173 | 0 | deskBounds.width, deskBounds.height, |
5174 | 0 | getter_AddRefs(screen)); |
5175 | 0 | return screen.forget(); |
5176 | 0 | } |
5177 | | |
5178 | | static bool |
5179 | | IsFullscreenSupported(GtkWidget* aShell) |
5180 | 0 | { |
5181 | 0 | #ifdef MOZ_X11 |
5182 | 0 | GdkScreen* screen = gtk_widget_get_screen(aShell); |
5183 | 0 | GdkAtom atom = gdk_atom_intern("_NET_WM_STATE_FULLSCREEN", FALSE); |
5184 | 0 | if (!gdk_x11_screen_supports_net_wm_hint(screen, atom)) { |
5185 | 0 | return false; |
5186 | 0 | } |
5187 | 0 | #endif |
5188 | 0 | return true; |
5189 | 0 | } |
5190 | | |
5191 | | nsresult |
5192 | | nsWindow::MakeFullScreen(bool aFullScreen, nsIScreen* aTargetScreen) |
5193 | 0 | { |
5194 | 0 | LOG(("nsWindow::MakeFullScreen [%p] aFullScreen %d\n", |
5195 | 0 | (void *)this, aFullScreen)); |
5196 | 0 |
|
5197 | 0 | if (mIsX11Display && !IsFullscreenSupported(mShell)) { |
5198 | 0 | return NS_ERROR_NOT_AVAILABLE; |
5199 | 0 | } |
5200 | 0 | |
5201 | 0 | if (aFullScreen) { |
5202 | 0 | if (mSizeMode != nsSizeMode_Fullscreen) |
5203 | 0 | mLastSizeMode = mSizeMode; |
5204 | 0 |
|
5205 | 0 | mSizeMode = nsSizeMode_Fullscreen; |
5206 | 0 | gtk_window_fullscreen(GTK_WINDOW(mShell)); |
5207 | 0 | } |
5208 | 0 | else { |
5209 | 0 | mSizeMode = mLastSizeMode; |
5210 | 0 | gtk_window_unfullscreen(GTK_WINDOW(mShell)); |
5211 | 0 | } |
5212 | 0 |
|
5213 | 0 | NS_ASSERTION(mLastSizeMode != nsSizeMode_Fullscreen, |
5214 | 0 | "mLastSizeMode should never be fullscreen"); |
5215 | 0 | return NS_OK; |
5216 | 0 | } |
5217 | | |
5218 | | void |
5219 | | nsWindow::SetWindowDecoration(nsBorderStyle aStyle) |
5220 | 0 | { |
5221 | 0 | if (!mShell) { |
5222 | 0 | // Pass the request to the toplevel window |
5223 | 0 | GtkWidget *topWidget = GetToplevelWidget(); |
5224 | 0 | if (!topWidget) |
5225 | 0 | return; |
5226 | 0 | |
5227 | 0 | nsWindow *topWindow = get_window_for_gtk_widget(topWidget); |
5228 | 0 | if (!topWindow) |
5229 | 0 | return; |
5230 | 0 | |
5231 | 0 | topWindow->SetWindowDecoration(aStyle); |
5232 | 0 | return; |
5233 | 0 | } |
5234 | 0 | |
5235 | 0 | // We can't use mGdkWindow directly here as it can be |
5236 | 0 | // derived from mContainer which is not a top-level GdkWindow. |
5237 | 0 | GdkWindow *window = gtk_widget_get_window(mShell); |
5238 | 0 |
|
5239 | 0 | // Sawfish, metacity, and presumably other window managers get |
5240 | 0 | // confused if we change the window decorations while the window |
5241 | 0 | // is visible. |
5242 | 0 | bool wasVisible = false; |
5243 | 0 | if (gdk_window_is_visible(window)) { |
5244 | 0 | gdk_window_hide(window); |
5245 | 0 | wasVisible = true; |
5246 | 0 | } |
5247 | 0 |
|
5248 | 0 | gint wmd = ConvertBorderStyles(aStyle); |
5249 | 0 | if (wmd != -1) |
5250 | 0 | gdk_window_set_decorations(window, (GdkWMDecoration) wmd); |
5251 | 0 |
|
5252 | 0 | if (wasVisible) |
5253 | 0 | gdk_window_show(window); |
5254 | 0 |
|
5255 | 0 | // For some window managers, adding or removing window decorations |
5256 | 0 | // requires unmapping and remapping our toplevel window. Go ahead |
5257 | 0 | // and flush the queue here so that we don't end up with a BadWindow |
5258 | 0 | // error later when this happens (when the persistence timer fires |
5259 | 0 | // and GetWindowPos is called) |
5260 | 0 | #ifdef MOZ_X11 |
5261 | 0 | if (mIsX11Display) { |
5262 | 0 | XSync(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()) , False); |
5263 | 0 | } else |
5264 | 0 | #endif /* MOZ_X11 */ |
5265 | 0 | { |
5266 | 0 | gdk_flush (); |
5267 | 0 | } |
5268 | 0 | } |
5269 | | |
5270 | | void |
5271 | | nsWindow::HideWindowChrome(bool aShouldHide) |
5272 | 0 | { |
5273 | 0 | SetWindowDecoration(aShouldHide ? eBorderStyle_none : mBorderStyle); |
5274 | 0 | } |
5275 | | |
5276 | | bool |
5277 | | nsWindow::CheckForRollup(gdouble aMouseX, gdouble aMouseY, |
5278 | | bool aIsWheel, bool aAlwaysRollup) |
5279 | 0 | { |
5280 | 0 | nsIRollupListener* rollupListener = GetActiveRollupListener(); |
5281 | 0 | nsCOMPtr<nsIWidget> rollupWidget; |
5282 | 0 | if (rollupListener) { |
5283 | 0 | rollupWidget = rollupListener->GetRollupWidget(); |
5284 | 0 | } |
5285 | 0 | if (!rollupWidget) { |
5286 | 0 | nsBaseWidget::gRollupListener = nullptr; |
5287 | 0 | return false; |
5288 | 0 | } |
5289 | 0 | |
5290 | 0 | bool retVal = false; |
5291 | 0 | auto *currentPopup = |
5292 | 0 | (GdkWindow *)rollupWidget->GetNativeData(NS_NATIVE_WINDOW); |
5293 | 0 | if (aAlwaysRollup || !is_mouse_in_window(currentPopup, aMouseX, aMouseY)) { |
5294 | 0 | bool rollup = true; |
5295 | 0 | if (aIsWheel) { |
5296 | 0 | rollup = rollupListener->ShouldRollupOnMouseWheelEvent(); |
5297 | 0 | retVal = rollupListener->ShouldConsumeOnMouseWheelEvent(); |
5298 | 0 | } |
5299 | 0 | // if we're dealing with menus, we probably have submenus and |
5300 | 0 | // we don't want to rollup if the click is in a parent menu of |
5301 | 0 | // the current submenu |
5302 | 0 | uint32_t popupsToRollup = UINT32_MAX; |
5303 | 0 | if (!aAlwaysRollup) { |
5304 | 0 | AutoTArray<nsIWidget*, 5> widgetChain; |
5305 | 0 | uint32_t sameTypeCount = rollupListener->GetSubmenuWidgetChain(&widgetChain); |
5306 | 0 | for (uint32_t i=0; i<widgetChain.Length(); ++i) { |
5307 | 0 | nsIWidget* widget = widgetChain[i]; |
5308 | 0 | auto* currWindow = |
5309 | 0 | (GdkWindow*) widget->GetNativeData(NS_NATIVE_WINDOW); |
5310 | 0 | if (is_mouse_in_window(currWindow, aMouseX, aMouseY)) { |
5311 | 0 | // don't roll up if the mouse event occurred within a |
5312 | 0 | // menu of the same type. If the mouse event occurred |
5313 | 0 | // in a menu higher than that, roll up, but pass the |
5314 | 0 | // number of popups to Rollup so that only those of the |
5315 | 0 | // same type close up. |
5316 | 0 | if (i < sameTypeCount) { |
5317 | 0 | rollup = false; |
5318 | 0 | } |
5319 | 0 | else { |
5320 | 0 | popupsToRollup = sameTypeCount; |
5321 | 0 | } |
5322 | 0 | break; |
5323 | 0 | } |
5324 | 0 | } // foreach parent menu widget |
5325 | 0 | } // if rollup listener knows about menus |
5326 | 0 |
|
5327 | 0 | // if we've determined that we should still rollup, do it. |
5328 | 0 | bool usePoint = !aIsWheel && !aAlwaysRollup; |
5329 | 0 | IntPoint point = IntPoint::Truncate(aMouseX, aMouseY); |
5330 | 0 | if (rollup && rollupListener->Rollup(popupsToRollup, true, usePoint ? &point : nullptr, nullptr)) { |
5331 | 0 | retVal = true; |
5332 | 0 | } |
5333 | 0 | } |
5334 | 0 | return retVal; |
5335 | 0 | } |
5336 | | |
5337 | | /* static */ |
5338 | | bool |
5339 | | nsWindow::DragInProgress(void) |
5340 | 0 | { |
5341 | 0 | nsCOMPtr<nsIDragService> dragService = do_GetService(kCDragServiceCID); |
5342 | 0 |
|
5343 | 0 | if (!dragService) |
5344 | 0 | return false; |
5345 | 0 | |
5346 | 0 | nsCOMPtr<nsIDragSession> currentDragSession; |
5347 | 0 | dragService->GetCurrentSession(getter_AddRefs(currentDragSession)); |
5348 | 0 |
|
5349 | 0 | return currentDragSession != nullptr; |
5350 | 0 | } |
5351 | | |
5352 | | static bool |
5353 | | is_mouse_in_window (GdkWindow* aWindow, gdouble aMouseX, gdouble aMouseY) |
5354 | 0 | { |
5355 | 0 | gint x = 0; |
5356 | 0 | gint y = 0; |
5357 | 0 | gint w, h; |
5358 | 0 |
|
5359 | 0 | gint offsetX = 0; |
5360 | 0 | gint offsetY = 0; |
5361 | 0 |
|
5362 | 0 | GdkWindow *window = aWindow; |
5363 | 0 |
|
5364 | 0 | while (window) { |
5365 | 0 | gint tmpX = 0; |
5366 | 0 | gint tmpY = 0; |
5367 | 0 |
|
5368 | 0 | gdk_window_get_position(window, &tmpX, &tmpY); |
5369 | 0 | GtkWidget *widget = get_gtk_widget_for_gdk_window(window); |
5370 | 0 |
|
5371 | 0 | // if this is a window, compute x and y given its origin and our |
5372 | 0 | // offset |
5373 | 0 | if (GTK_IS_WINDOW(widget)) { |
5374 | 0 | x = tmpX + offsetX; |
5375 | 0 | y = tmpY + offsetY; |
5376 | 0 | break; |
5377 | 0 | } |
5378 | 0 | |
5379 | 0 | offsetX += tmpX; |
5380 | 0 | offsetY += tmpY; |
5381 | 0 | window = gdk_window_get_parent(window); |
5382 | 0 | } |
5383 | 0 |
|
5384 | 0 | w = gdk_window_get_width(aWindow); |
5385 | 0 | h = gdk_window_get_height(aWindow); |
5386 | 0 |
|
5387 | 0 | if (aMouseX > x && aMouseX < x + w && |
5388 | 0 | aMouseY > y && aMouseY < y + h) |
5389 | 0 | return true; |
5390 | 0 | |
5391 | 0 | return false; |
5392 | 0 | } |
5393 | | |
5394 | | static nsWindow * |
5395 | | get_window_for_gtk_widget(GtkWidget *widget) |
5396 | 0 | { |
5397 | 0 | gpointer user_data = g_object_get_data(G_OBJECT(widget), "nsWindow"); |
5398 | 0 |
|
5399 | 0 | return static_cast<nsWindow *>(user_data); |
5400 | 0 | } |
5401 | | |
5402 | | static nsWindow * |
5403 | | get_window_for_gdk_window(GdkWindow *window) |
5404 | 0 | { |
5405 | 0 | gpointer user_data = g_object_get_data(G_OBJECT(window), "nsWindow"); |
5406 | 0 |
|
5407 | 0 | return static_cast<nsWindow *>(user_data); |
5408 | 0 | } |
5409 | | |
5410 | | static GtkWidget * |
5411 | | get_gtk_widget_for_gdk_window(GdkWindow *window) |
5412 | 0 | { |
5413 | 0 | gpointer user_data = nullptr; |
5414 | 0 | gdk_window_get_user_data(window, &user_data); |
5415 | 0 |
|
5416 | 0 | return GTK_WIDGET(user_data); |
5417 | 0 | } |
5418 | | |
5419 | | static GdkCursor * |
5420 | | get_gtk_cursor(nsCursor aCursor) |
5421 | 0 | { |
5422 | 0 | GdkCursor *gdkcursor = nullptr; |
5423 | 0 | uint8_t newType = 0xff; |
5424 | 0 |
|
5425 | 0 | if ((gdkcursor = gCursorCache[aCursor])) { |
5426 | 0 | return gdkcursor; |
5427 | 0 | } |
5428 | 0 | |
5429 | 0 | GdkDisplay *defaultDisplay = gdk_display_get_default(); |
5430 | 0 |
|
5431 | 0 | // The strategy here is to use standard GDK cursors, and, if not available, |
5432 | 0 | // load by standard name with gdk_cursor_new_from_name. |
5433 | 0 | // Spec is here: http://www.freedesktop.org/wiki/Specifications/cursor-spec/ |
5434 | 0 | switch (aCursor) { |
5435 | 0 | case eCursor_standard: |
5436 | 0 | gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_LEFT_PTR); |
5437 | 0 | break; |
5438 | 0 | case eCursor_wait: |
5439 | 0 | gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_WATCH); |
5440 | 0 | break; |
5441 | 0 | case eCursor_select: |
5442 | 0 | gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_XTERM); |
5443 | 0 | break; |
5444 | 0 | case eCursor_hyperlink: |
5445 | 0 | gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_HAND2); |
5446 | 0 | break; |
5447 | 0 | case eCursor_n_resize: |
5448 | 0 | gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_TOP_SIDE); |
5449 | 0 | break; |
5450 | 0 | case eCursor_s_resize: |
5451 | 0 | gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_BOTTOM_SIDE); |
5452 | 0 | break; |
5453 | 0 | case eCursor_w_resize: |
5454 | 0 | gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_LEFT_SIDE); |
5455 | 0 | break; |
5456 | 0 | case eCursor_e_resize: |
5457 | 0 | gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_RIGHT_SIDE); |
5458 | 0 | break; |
5459 | 0 | case eCursor_nw_resize: |
5460 | 0 | gdkcursor = gdk_cursor_new_for_display(defaultDisplay, |
5461 | 0 | GDK_TOP_LEFT_CORNER); |
5462 | 0 | break; |
5463 | 0 | case eCursor_se_resize: |
5464 | 0 | gdkcursor = gdk_cursor_new_for_display(defaultDisplay, |
5465 | 0 | GDK_BOTTOM_RIGHT_CORNER); |
5466 | 0 | break; |
5467 | 0 | case eCursor_ne_resize: |
5468 | 0 | gdkcursor = gdk_cursor_new_for_display(defaultDisplay, |
5469 | 0 | GDK_TOP_RIGHT_CORNER); |
5470 | 0 | break; |
5471 | 0 | case eCursor_sw_resize: |
5472 | 0 | gdkcursor = gdk_cursor_new_for_display(defaultDisplay, |
5473 | 0 | GDK_BOTTOM_LEFT_CORNER); |
5474 | 0 | break; |
5475 | 0 | case eCursor_crosshair: |
5476 | 0 | gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_CROSSHAIR); |
5477 | 0 | break; |
5478 | 0 | case eCursor_move: |
5479 | 0 | gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_FLEUR); |
5480 | 0 | break; |
5481 | 0 | case eCursor_help: |
5482 | 0 | gdkcursor = gdk_cursor_new_for_display(defaultDisplay, |
5483 | 0 | GDK_QUESTION_ARROW); |
5484 | 0 | break; |
5485 | 0 | case eCursor_copy: // CSS3 |
5486 | 0 | gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "copy"); |
5487 | 0 | if (!gdkcursor) |
5488 | 0 | newType = MOZ_CURSOR_COPY; |
5489 | 0 | break; |
5490 | 0 | case eCursor_alias: |
5491 | 0 | gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "alias"); |
5492 | 0 | if (!gdkcursor) |
5493 | 0 | newType = MOZ_CURSOR_ALIAS; |
5494 | 0 | break; |
5495 | 0 | case eCursor_context_menu: |
5496 | 0 | gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "context-menu"); |
5497 | 0 | if (!gdkcursor) |
5498 | 0 | newType = MOZ_CURSOR_CONTEXT_MENU; |
5499 | 0 | break; |
5500 | 0 | case eCursor_cell: |
5501 | 0 | gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_PLUS); |
5502 | 0 | break; |
5503 | 0 | // Those two aren’t standardized. Trying both KDE’s and GNOME’s names |
5504 | 0 | case eCursor_grab: |
5505 | 0 | gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "openhand"); |
5506 | 0 | if (!gdkcursor) |
5507 | 0 | newType = MOZ_CURSOR_HAND_GRAB; |
5508 | 0 | break; |
5509 | 0 | case eCursor_grabbing: |
5510 | 0 | gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "closedhand"); |
5511 | 0 | if (!gdkcursor) |
5512 | 0 | gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "grabbing"); |
5513 | 0 | if (!gdkcursor) |
5514 | 0 | newType = MOZ_CURSOR_HAND_GRABBING; |
5515 | 0 | break; |
5516 | 0 | case eCursor_spinning: |
5517 | 0 | gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "progress"); |
5518 | 0 | if (!gdkcursor) |
5519 | 0 | newType = MOZ_CURSOR_SPINNING; |
5520 | 0 | break; |
5521 | 0 | case eCursor_zoom_in: |
5522 | 0 | gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "zoom-in"); |
5523 | 0 | if (!gdkcursor) |
5524 | 0 | newType = MOZ_CURSOR_ZOOM_IN; |
5525 | 0 | break; |
5526 | 0 | case eCursor_zoom_out: |
5527 | 0 | gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "zoom-out"); |
5528 | 0 | if (!gdkcursor) |
5529 | 0 | newType = MOZ_CURSOR_ZOOM_OUT; |
5530 | 0 | break; |
5531 | 0 | case eCursor_not_allowed: |
5532 | 0 | gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "not-allowed"); |
5533 | 0 | if (!gdkcursor) // nonstandard, yet common |
5534 | 0 | gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "crossed_circle"); |
5535 | 0 | if (!gdkcursor) |
5536 | 0 | newType = MOZ_CURSOR_NOT_ALLOWED; |
5537 | 0 | break; |
5538 | 0 | case eCursor_no_drop: |
5539 | 0 | gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "no-drop"); |
5540 | 0 | if (!gdkcursor) // this nonstandard sequence makes it work on KDE and GNOME |
5541 | 0 | gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "forbidden"); |
5542 | 0 | if (!gdkcursor) |
5543 | 0 | gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "circle"); |
5544 | 0 | if (!gdkcursor) |
5545 | 0 | newType = MOZ_CURSOR_NOT_ALLOWED; |
5546 | 0 | break; |
5547 | 0 | case eCursor_vertical_text: |
5548 | 0 | newType = MOZ_CURSOR_VERTICAL_TEXT; |
5549 | 0 | break; |
5550 | 0 | case eCursor_all_scroll: |
5551 | 0 | gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_FLEUR); |
5552 | 0 | break; |
5553 | 0 | case eCursor_nesw_resize: |
5554 | 0 | gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "size_bdiag"); |
5555 | 0 | if (!gdkcursor) |
5556 | 0 | newType = MOZ_CURSOR_NESW_RESIZE; |
5557 | 0 | break; |
5558 | 0 | case eCursor_nwse_resize: |
5559 | 0 | gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "size_fdiag"); |
5560 | 0 | if (!gdkcursor) |
5561 | 0 | newType = MOZ_CURSOR_NWSE_RESIZE; |
5562 | 0 | break; |
5563 | 0 | case eCursor_ns_resize: |
5564 | 0 | gdkcursor = gdk_cursor_new_for_display(defaultDisplay, |
5565 | 0 | GDK_SB_V_DOUBLE_ARROW); |
5566 | 0 | break; |
5567 | 0 | case eCursor_ew_resize: |
5568 | 0 | gdkcursor = gdk_cursor_new_for_display(defaultDisplay, |
5569 | 0 | GDK_SB_H_DOUBLE_ARROW); |
5570 | 0 | break; |
5571 | 0 | // Here, two better fitting cursors exist in some cursor themes. Try those first |
5572 | 0 | case eCursor_row_resize: |
5573 | 0 | gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "split_v"); |
5574 | 0 | if (!gdkcursor) |
5575 | 0 | gdkcursor = gdk_cursor_new_for_display(defaultDisplay, |
5576 | 0 | GDK_SB_V_DOUBLE_ARROW); |
5577 | 0 | break; |
5578 | 0 | case eCursor_col_resize: |
5579 | 0 | gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "split_h"); |
5580 | 0 | if (!gdkcursor) |
5581 | 0 | gdkcursor = gdk_cursor_new_for_display(defaultDisplay, |
5582 | 0 | GDK_SB_H_DOUBLE_ARROW); |
5583 | 0 | break; |
5584 | 0 | case eCursor_none: |
5585 | 0 | newType = MOZ_CURSOR_NONE; |
5586 | 0 | break; |
5587 | 0 | default: |
5588 | 0 | NS_ASSERTION(aCursor, "Invalid cursor type"); |
5589 | 0 | gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_LEFT_PTR); |
5590 | 0 | break; |
5591 | 0 | } |
5592 | 0 |
|
5593 | 0 | // If by now we don't have a xcursor, this means we have to make a custom |
5594 | 0 | // one. First, we try creating a named cursor based on the hash of our |
5595 | 0 | // custom bitmap, as libXcursor has some magic to convert bitmapped cursors |
5596 | 0 | // to themed cursors |
5597 | 0 | if (newType != 0xFF && GtkCursors[newType].hash) { |
5598 | 0 | gdkcursor = gdk_cursor_new_from_name(defaultDisplay, GtkCursors[newType].hash); |
5599 | 0 | } |
5600 | 0 |
|
5601 | 0 | // If we still don't have a xcursor, we now really create a bitmap cursor |
5602 | 0 | if (newType != 0xff && !gdkcursor) { |
5603 | 0 | GdkPixbuf * cursor_pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, 32, 32); |
5604 | 0 | if (!cursor_pixbuf) |
5605 | 0 | return nullptr; |
5606 | 0 | |
5607 | 0 | guchar *data = gdk_pixbuf_get_pixels(cursor_pixbuf); |
5608 | 0 |
|
5609 | 0 | // Read data from GtkCursors and compose RGBA surface from 1bit bitmap and mask |
5610 | 0 | // GtkCursors bits and mask are 32x32 monochrome bitmaps (1 bit for each pixel) |
5611 | 0 | // so it's 128 byte array (4 bytes for are one bitmap row and there are 32 rows here). |
5612 | 0 | const unsigned char *bits = GtkCursors[newType].bits; |
5613 | 0 | const unsigned char *mask_bits = GtkCursors[newType].mask_bits; |
5614 | 0 |
|
5615 | 0 | for (int i = 0; i < 128; i++) { |
5616 | 0 | char bit = *bits++; |
5617 | 0 | char mask = *mask_bits++; |
5618 | 0 | for (int j = 0; j < 8; j++) { |
5619 | 0 | unsigned char pix = ~(((bit >> j) & 0x01) * 0xff); |
5620 | 0 | *data++ = pix; |
5621 | 0 | *data++ = pix; |
5622 | 0 | *data++ = pix; |
5623 | 0 | *data++ = (((mask >> j) & 0x01) * 0xff); |
5624 | 0 | } |
5625 | 0 | } |
5626 | 0 |
|
5627 | 0 | gdkcursor = gdk_cursor_new_from_pixbuf(gdk_display_get_default(), cursor_pixbuf, |
5628 | 0 | GtkCursors[newType].hot_x, |
5629 | 0 | GtkCursors[newType].hot_y); |
5630 | 0 |
|
5631 | 0 | g_object_unref(cursor_pixbuf); |
5632 | 0 | } |
5633 | 0 |
|
5634 | 0 | gCursorCache[aCursor] = gdkcursor; |
5635 | 0 |
|
5636 | 0 | return gdkcursor; |
5637 | 0 | } |
5638 | | |
5639 | | // gtk callbacks |
5640 | | |
5641 | | void |
5642 | | draw_window_of_widget(GtkWidget *widget, GdkWindow *aWindow, cairo_t *cr) |
5643 | 0 | { |
5644 | 0 | if (gtk_cairo_should_draw_window(cr, aWindow)) { |
5645 | 0 | RefPtr<nsWindow> window = get_window_for_gdk_window(aWindow); |
5646 | 0 | if (!window) { |
5647 | 0 | NS_WARNING("Cannot get nsWindow from GtkWidget"); |
5648 | 0 | } |
5649 | 0 | else { |
5650 | 0 | cairo_save(cr); |
5651 | 0 | gtk_cairo_transform_to_window(cr, widget, aWindow); |
5652 | 0 | // TODO - window->OnExposeEvent() can destroy this or other windows, |
5653 | 0 | // do we need to handle it somehow? |
5654 | 0 | window->OnExposeEvent(cr); |
5655 | 0 | cairo_restore(cr); |
5656 | 0 | } |
5657 | 0 | } |
5658 | 0 |
|
5659 | 0 | GList *children = gdk_window_get_children(aWindow); |
5660 | 0 | GList *child = children; |
5661 | 0 | while (child) { |
5662 | 0 | GdkWindow *window = GDK_WINDOW(child->data); |
5663 | 0 | gpointer windowWidget; |
5664 | 0 | gdk_window_get_user_data(window, &windowWidget); |
5665 | 0 | if (windowWidget == widget) { |
5666 | 0 | draw_window_of_widget(widget, window, cr); |
5667 | 0 | } |
5668 | 0 | child = g_list_next(child); |
5669 | 0 | } |
5670 | 0 | g_list_free(children); |
5671 | 0 | } |
5672 | | |
5673 | | /* static */ |
5674 | | gboolean |
5675 | | expose_event_cb(GtkWidget *widget, cairo_t *cr) |
5676 | 0 | { |
5677 | 0 | draw_window_of_widget(widget, gtk_widget_get_window(widget), cr); |
5678 | 0 |
|
5679 | 0 | // A strong reference is already held during "draw" signal emission, |
5680 | 0 | // but GTK+ 3.4 wants the object to live a little longer than that |
5681 | 0 | // (bug 1225970). |
5682 | 0 | g_object_ref(widget); |
5683 | 0 | g_idle_add( |
5684 | 0 | [](gpointer data) -> gboolean { |
5685 | 0 | g_object_unref(data); |
5686 | 0 | return G_SOURCE_REMOVE; |
5687 | 0 | }, |
5688 | 0 | widget); |
5689 | 0 |
|
5690 | 0 | return FALSE; |
5691 | 0 | } |
5692 | | |
5693 | | static gboolean |
5694 | | configure_event_cb(GtkWidget *widget, |
5695 | | GdkEventConfigure *event) |
5696 | 0 | { |
5697 | 0 | RefPtr<nsWindow> window = get_window_for_gtk_widget(widget); |
5698 | 0 | if (!window) |
5699 | 0 | return FALSE; |
5700 | 0 | |
5701 | 0 | return window->OnConfigureEvent(widget, event); |
5702 | 0 | } |
5703 | | |
5704 | | static void |
5705 | | container_unrealize_cb (GtkWidget *widget) |
5706 | 0 | { |
5707 | 0 | RefPtr<nsWindow> window = get_window_for_gtk_widget(widget); |
5708 | 0 | if (!window) |
5709 | 0 | return; |
5710 | 0 | |
5711 | 0 | window->OnContainerUnrealize(); |
5712 | 0 | } |
5713 | | |
5714 | | static void |
5715 | | size_allocate_cb (GtkWidget *widget, GtkAllocation *allocation) |
5716 | 0 | { |
5717 | 0 | RefPtr<nsWindow> window = get_window_for_gtk_widget(widget); |
5718 | 0 | if (!window) |
5719 | 0 | return; |
5720 | 0 | |
5721 | 0 | window->OnSizeAllocate(allocation); |
5722 | 0 | } |
5723 | | |
5724 | | static gboolean |
5725 | | delete_event_cb(GtkWidget *widget, GdkEventAny *event) |
5726 | 0 | { |
5727 | 0 | RefPtr<nsWindow> window = get_window_for_gtk_widget(widget); |
5728 | 0 | if (!window) |
5729 | 0 | return FALSE; |
5730 | 0 | |
5731 | 0 | window->OnDeleteEvent(); |
5732 | 0 |
|
5733 | 0 | return TRUE; |
5734 | 0 | } |
5735 | | |
5736 | | static gboolean |
5737 | | enter_notify_event_cb(GtkWidget *widget, |
5738 | | GdkEventCrossing *event) |
5739 | 0 | { |
5740 | 0 | RefPtr<nsWindow> window = get_window_for_gdk_window(event->window); |
5741 | 0 | if (!window) |
5742 | 0 | return TRUE; |
5743 | 0 | |
5744 | 0 | window->OnEnterNotifyEvent(event); |
5745 | 0 |
|
5746 | 0 | return TRUE; |
5747 | 0 | } |
5748 | | |
5749 | | static gboolean |
5750 | | leave_notify_event_cb(GtkWidget *widget, |
5751 | | GdkEventCrossing *event) |
5752 | 0 | { |
5753 | 0 | if (is_parent_grab_leave(event)) { |
5754 | 0 | return TRUE; |
5755 | 0 | } |
5756 | 0 | |
5757 | 0 | // bug 369599: Suppress LeaveNotify events caused by pointer grabs to |
5758 | 0 | // avoid generating spurious mouse exit events. |
5759 | 0 | auto x = gint(event->x_root); |
5760 | 0 | auto y = gint(event->y_root); |
5761 | 0 | GdkDisplay* display = gtk_widget_get_display(widget); |
5762 | 0 | GdkWindow* winAtPt = gdk_display_get_window_at_pointer(display, &x, &y); |
5763 | 0 | if (winAtPt == event->window) { |
5764 | 0 | return TRUE; |
5765 | 0 | } |
5766 | 0 | |
5767 | 0 | RefPtr<nsWindow> window = get_window_for_gdk_window(event->window); |
5768 | 0 | if (!window) |
5769 | 0 | return TRUE; |
5770 | 0 | |
5771 | 0 | window->OnLeaveNotifyEvent(event); |
5772 | 0 |
|
5773 | 0 | return TRUE; |
5774 | 0 | } |
5775 | | |
5776 | | static nsWindow* |
5777 | | GetFirstNSWindowForGDKWindow(GdkWindow *aGdkWindow) |
5778 | 0 | { |
5779 | 0 | nsWindow* window; |
5780 | 0 | while (!(window = get_window_for_gdk_window(aGdkWindow))) { |
5781 | 0 | // The event has bubbled to the moz_container widget as passed into each caller's *widget parameter, |
5782 | 0 | // but its corresponding nsWindow is an ancestor of the window that we need. Instead, look at |
5783 | 0 | // event->window and find the first ancestor nsWindow of it because event->window may be in a plugin. |
5784 | 0 | aGdkWindow = gdk_window_get_parent(aGdkWindow); |
5785 | 0 | if (!aGdkWindow) { |
5786 | 0 | window = nullptr; |
5787 | 0 | break; |
5788 | 0 | } |
5789 | 0 | } |
5790 | 0 | return window; |
5791 | 0 | } |
5792 | | |
5793 | | static gboolean |
5794 | | motion_notify_event_cb(GtkWidget *widget, GdkEventMotion *event) |
5795 | 0 | { |
5796 | 0 | UpdateLastInputEventTime(event); |
5797 | 0 |
|
5798 | 0 | nsWindow *window = GetFirstNSWindowForGDKWindow(event->window); |
5799 | 0 | if (!window) |
5800 | 0 | return FALSE; |
5801 | 0 | |
5802 | 0 | window->OnMotionNotifyEvent(event); |
5803 | 0 |
|
5804 | 0 | return TRUE; |
5805 | 0 | } |
5806 | | |
5807 | | static gboolean |
5808 | | button_press_event_cb(GtkWidget *widget, GdkEventButton *event) |
5809 | 0 | { |
5810 | 0 | UpdateLastInputEventTime(event); |
5811 | 0 |
|
5812 | 0 | nsWindow *window = GetFirstNSWindowForGDKWindow(event->window); |
5813 | 0 | if (!window) |
5814 | 0 | return FALSE; |
5815 | 0 | |
5816 | 0 | window->OnButtonPressEvent(event); |
5817 | 0 |
|
5818 | 0 | return TRUE; |
5819 | 0 | } |
5820 | | |
5821 | | static gboolean |
5822 | | button_release_event_cb(GtkWidget *widget, GdkEventButton *event) |
5823 | 0 | { |
5824 | 0 | UpdateLastInputEventTime(event); |
5825 | 0 |
|
5826 | 0 | nsWindow *window = GetFirstNSWindowForGDKWindow(event->window); |
5827 | 0 | if (!window) |
5828 | 0 | return FALSE; |
5829 | 0 | |
5830 | 0 | window->OnButtonReleaseEvent(event); |
5831 | 0 |
|
5832 | 0 | return TRUE; |
5833 | 0 | } |
5834 | | |
5835 | | static gboolean |
5836 | | focus_in_event_cb(GtkWidget *widget, GdkEventFocus *event) |
5837 | 0 | { |
5838 | 0 | RefPtr<nsWindow> window = get_window_for_gtk_widget(widget); |
5839 | 0 | if (!window) |
5840 | 0 | return FALSE; |
5841 | 0 | |
5842 | 0 | window->OnContainerFocusInEvent(event); |
5843 | 0 |
|
5844 | 0 | return FALSE; |
5845 | 0 | } |
5846 | | |
5847 | | static gboolean |
5848 | | focus_out_event_cb(GtkWidget *widget, GdkEventFocus *event) |
5849 | 0 | { |
5850 | 0 | RefPtr<nsWindow> window = get_window_for_gtk_widget(widget); |
5851 | 0 | if (!window) |
5852 | 0 | return FALSE; |
5853 | 0 | |
5854 | 0 | window->OnContainerFocusOutEvent(event); |
5855 | 0 |
|
5856 | 0 | return FALSE; |
5857 | 0 | } |
5858 | | |
5859 | | #ifdef MOZ_X11 |
5860 | | // For long-lived popup windows that don't really take focus themselves but |
5861 | | // may have elements that accept keyboard input when the parent window is |
5862 | | // active, focus is handled specially. These windows include noautohide |
5863 | | // panels. (This special handling is not necessary for temporary popups where |
5864 | | // the keyboard is grabbed.) |
5865 | | // |
5866 | | // Mousing over or clicking on these windows should not cause them to steal |
5867 | | // focus from their parent windows, so, the input field of WM_HINTS is set to |
5868 | | // False to request that the window manager not set the input focus to this |
5869 | | // window. http://tronche.com/gui/x/icccm/sec-4.html#s-4.1.7 |
5870 | | // |
5871 | | // However, these windows can still receive WM_TAKE_FOCUS messages from the |
5872 | | // window manager, so they can still detect when the user has indicated that |
5873 | | // they wish to direct keyboard input at these windows. When the window |
5874 | | // manager offers focus to these windows (after a mouse over or click, for |
5875 | | // example), a request to make the parent window active is issued. When the |
5876 | | // parent window becomes active, keyboard events will be received. |
5877 | | |
5878 | | static GdkFilterReturn |
5879 | | popup_take_focus_filter(GdkXEvent *gdk_xevent, |
5880 | | GdkEvent *event, |
5881 | | gpointer data) |
5882 | 0 | { |
5883 | 0 | auto* xevent = static_cast<XEvent*>(gdk_xevent); |
5884 | 0 | if (xevent->type != ClientMessage) |
5885 | 0 | return GDK_FILTER_CONTINUE; |
5886 | 0 | |
5887 | 0 | XClientMessageEvent& xclient = xevent->xclient; |
5888 | 0 | if (xclient.message_type != gdk_x11_get_xatom_by_name("WM_PROTOCOLS")) |
5889 | 0 | return GDK_FILTER_CONTINUE; |
5890 | 0 | |
5891 | 0 | Atom atom = xclient.data.l[0]; |
5892 | 0 | if (atom != gdk_x11_get_xatom_by_name("WM_TAKE_FOCUS")) |
5893 | 0 | return GDK_FILTER_CONTINUE; |
5894 | 0 | |
5895 | 0 | guint32 timestamp = xclient.data.l[1]; |
5896 | 0 |
|
5897 | 0 | GtkWidget* widget = get_gtk_widget_for_gdk_window(event->any.window); |
5898 | 0 | if (!widget) |
5899 | 0 | return GDK_FILTER_CONTINUE; |
5900 | 0 | |
5901 | 0 | GtkWindow* parent = gtk_window_get_transient_for(GTK_WINDOW(widget)); |
5902 | 0 | if (!parent) |
5903 | 0 | return GDK_FILTER_CONTINUE; |
5904 | 0 | |
5905 | 0 | if (gtk_window_is_active(parent)) |
5906 | 0 | return GDK_FILTER_REMOVE; // leave input focus on the parent |
5907 | 0 | |
5908 | 0 | GdkWindow* parent_window = gtk_widget_get_window(GTK_WIDGET(parent)); |
5909 | 0 | if (!parent_window) |
5910 | 0 | return GDK_FILTER_CONTINUE; |
5911 | 0 | |
5912 | 0 | // In case the parent has not been deconified. |
5913 | 0 | gdk_window_show_unraised(parent_window); |
5914 | 0 |
|
5915 | 0 | // Request focus on the parent window. |
5916 | 0 | // Use gdk_window_focus rather than gtk_window_present to avoid |
5917 | 0 | // raising the parent window. |
5918 | 0 | gdk_window_focus(parent_window, timestamp); |
5919 | 0 | return GDK_FILTER_REMOVE; |
5920 | 0 | } |
5921 | | #endif /* MOZ_X11 */ |
5922 | | |
5923 | | static gboolean |
5924 | | key_press_event_cb(GtkWidget *widget, GdkEventKey *event) |
5925 | 0 | { |
5926 | 0 | LOG(("key_press_event_cb\n")); |
5927 | 0 |
|
5928 | 0 | UpdateLastInputEventTime(event); |
5929 | 0 |
|
5930 | 0 | // find the window with focus and dispatch this event to that widget |
5931 | 0 | nsWindow *window = get_window_for_gtk_widget(widget); |
5932 | 0 | if (!window) |
5933 | 0 | return FALSE; |
5934 | 0 | |
5935 | 0 | RefPtr<nsWindow> focusWindow = gFocusWindow ? gFocusWindow : window; |
5936 | 0 |
|
5937 | 0 | #ifdef MOZ_X11 |
5938 | 0 | // Keyboard repeat can cause key press events to queue up when there are |
5939 | 0 | // slow event handlers (bug 301029). Throttle these events by removing |
5940 | 0 | // consecutive pending duplicate KeyPress events to the same window. |
5941 | 0 | // We use the event time of the last one. |
5942 | 0 | // Note: GDK calls XkbSetDetectableAutorepeat so that KeyRelease events |
5943 | 0 | // are generated only when the key is physically released. |
5944 | 0 | #define NS_GDKEVENT_MATCH_MASK 0x1FFF /* GDK_SHIFT_MASK .. GDK_BUTTON5_MASK */ |
5945 | 0 | GdkDisplay* gdkDisplay = gtk_widget_get_display(widget); |
5946 | 0 | if (GDK_IS_X11_DISPLAY(gdkDisplay)) { |
5947 | 0 | Display* dpy = GDK_DISPLAY_XDISPLAY(gdkDisplay); |
5948 | 0 | while (XPending(dpy)) { |
5949 | 0 | XEvent next_event; |
5950 | 0 | XPeekEvent(dpy, &next_event); |
5951 | 0 | GdkWindow* nextGdkWindow = |
5952 | 0 | gdk_x11_window_lookup_for_display(gdkDisplay, next_event.xany.window); |
5953 | 0 | if (nextGdkWindow != event->window || |
5954 | 0 | next_event.type != KeyPress || |
5955 | 0 | next_event.xkey.keycode != event->hardware_keycode || |
5956 | 0 | next_event.xkey.state != (event->state & NS_GDKEVENT_MATCH_MASK)) { |
5957 | 0 | break; |
5958 | 0 | } |
5959 | 0 | XNextEvent(dpy, &next_event); |
5960 | 0 | event->time = next_event.xkey.time; |
5961 | 0 | } |
5962 | 0 | } |
5963 | 0 | #endif |
5964 | 0 |
|
5965 | 0 | return focusWindow->OnKeyPressEvent(event); |
5966 | 0 | } |
5967 | | |
5968 | | static gboolean |
5969 | | key_release_event_cb(GtkWidget *widget, GdkEventKey *event) |
5970 | 0 | { |
5971 | 0 | LOG(("key_release_event_cb\n")); |
5972 | 0 |
|
5973 | 0 | UpdateLastInputEventTime(event); |
5974 | 0 |
|
5975 | 0 | // find the window with focus and dispatch this event to that widget |
5976 | 0 | nsWindow *window = get_window_for_gtk_widget(widget); |
5977 | 0 | if (!window) |
5978 | 0 | return FALSE; |
5979 | 0 | |
5980 | 0 | RefPtr<nsWindow> focusWindow = gFocusWindow ? gFocusWindow : window; |
5981 | 0 |
|
5982 | 0 | return focusWindow->OnKeyReleaseEvent(event); |
5983 | 0 | } |
5984 | | |
5985 | | static gboolean |
5986 | | property_notify_event_cb(GtkWidget* aWidget, GdkEventProperty* aEvent) |
5987 | 0 | { |
5988 | 0 | RefPtr<nsWindow> window = get_window_for_gdk_window(aEvent->window); |
5989 | 0 | if (!window) |
5990 | 0 | return FALSE; |
5991 | 0 | |
5992 | 0 | return window->OnPropertyNotifyEvent(aWidget, aEvent); |
5993 | 0 | } |
5994 | | |
5995 | | static gboolean |
5996 | | scroll_event_cb(GtkWidget *widget, GdkEventScroll *event) |
5997 | 0 | { |
5998 | 0 | nsWindow *window = GetFirstNSWindowForGDKWindow(event->window); |
5999 | 0 | if (!window) |
6000 | 0 | return FALSE; |
6001 | 0 | |
6002 | 0 | window->OnScrollEvent(event); |
6003 | 0 |
|
6004 | 0 | return TRUE; |
6005 | 0 | } |
6006 | | |
6007 | | static gboolean |
6008 | | visibility_notify_event_cb (GtkWidget *widget, GdkEventVisibility *event) |
6009 | 0 | { |
6010 | 0 | RefPtr<nsWindow> window = get_window_for_gdk_window(event->window); |
6011 | 0 | if (!window) |
6012 | 0 | return FALSE; |
6013 | 0 | |
6014 | 0 | window->OnVisibilityNotifyEvent(event); |
6015 | 0 |
|
6016 | 0 | return TRUE; |
6017 | 0 | } |
6018 | | |
6019 | | static void |
6020 | | hierarchy_changed_cb (GtkWidget *widget, |
6021 | | GtkWidget *previous_toplevel) |
6022 | 0 | { |
6023 | 0 | GtkWidget *toplevel = gtk_widget_get_toplevel(widget); |
6024 | 0 | GdkWindowState old_window_state = GDK_WINDOW_STATE_WITHDRAWN; |
6025 | 0 | GdkEventWindowState event; |
6026 | 0 |
|
6027 | 0 | event.new_window_state = GDK_WINDOW_STATE_WITHDRAWN; |
6028 | 0 |
|
6029 | 0 | if (GTK_IS_WINDOW(previous_toplevel)) { |
6030 | 0 | g_signal_handlers_disconnect_by_func(previous_toplevel, |
6031 | 0 | FuncToGpointer(window_state_event_cb), |
6032 | 0 | widget); |
6033 | 0 | GdkWindow *win = gtk_widget_get_window(previous_toplevel); |
6034 | 0 | if (win) { |
6035 | 0 | old_window_state = gdk_window_get_state(win); |
6036 | 0 | } |
6037 | 0 | } |
6038 | 0 |
|
6039 | 0 | if (GTK_IS_WINDOW(toplevel)) { |
6040 | 0 | g_signal_connect_swapped(toplevel, "window-state-event", |
6041 | 0 | G_CALLBACK(window_state_event_cb), widget); |
6042 | 0 | GdkWindow *win = gtk_widget_get_window(toplevel); |
6043 | 0 | if (win) { |
6044 | 0 | event.new_window_state = gdk_window_get_state(win); |
6045 | 0 | } |
6046 | 0 | } |
6047 | 0 |
|
6048 | 0 | event.changed_mask = static_cast<GdkWindowState> |
6049 | 0 | (old_window_state ^ event.new_window_state); |
6050 | 0 |
|
6051 | 0 | if (event.changed_mask) { |
6052 | 0 | event.type = GDK_WINDOW_STATE; |
6053 | 0 | event.window = nullptr; |
6054 | 0 | event.send_event = TRUE; |
6055 | 0 | window_state_event_cb(widget, &event); |
6056 | 0 | } |
6057 | 0 | } |
6058 | | |
6059 | | static gboolean |
6060 | | window_state_event_cb (GtkWidget *widget, GdkEventWindowState *event) |
6061 | 0 | { |
6062 | 0 | RefPtr<nsWindow> window = get_window_for_gtk_widget(widget); |
6063 | 0 | if (!window) |
6064 | 0 | return FALSE; |
6065 | 0 | |
6066 | 0 | window->OnWindowStateEvent(widget, event); |
6067 | 0 |
|
6068 | 0 | return FALSE; |
6069 | 0 | } |
6070 | | |
6071 | | static void |
6072 | | settings_changed_cb (GtkSettings *settings, GParamSpec *pspec, nsWindow *data) |
6073 | 0 | { |
6074 | 0 | RefPtr<nsWindow> window = data; |
6075 | 0 | window->ThemeChanged(); |
6076 | 0 | } |
6077 | | |
6078 | | static void |
6079 | | check_resize_cb (GtkContainer* container, gpointer user_data) |
6080 | 0 | { |
6081 | 0 | RefPtr<nsWindow> window = get_window_for_gtk_widget(GTK_WIDGET(container)); |
6082 | 0 | if (!window) { |
6083 | 0 | return; |
6084 | 0 | } |
6085 | 0 | window->OnCheckResize(); |
6086 | 0 | } |
6087 | | |
6088 | | static void |
6089 | | screen_composited_changed_cb (GdkScreen* screen, gpointer user_data) |
6090 | 0 | { |
6091 | 0 | // This callback can run before gfxPlatform::Init() in rare |
6092 | 0 | // cases involving the profile manager. When this happens, |
6093 | 0 | // we have no reason to reset any compositors as graphics |
6094 | 0 | // hasn't been initialized yet. |
6095 | 0 | if (GPUProcessManager::Get()) { |
6096 | 0 | GPUProcessManager::Get()->ResetCompositors(); |
6097 | 0 | } |
6098 | 0 | } |
6099 | | |
6100 | | static void |
6101 | | widget_composited_changed_cb (GtkWidget* widget, gpointer user_data) |
6102 | 0 | { |
6103 | 0 | RefPtr<nsWindow> window = get_window_for_gtk_widget(widget); |
6104 | 0 | if (!window) { |
6105 | 0 | return; |
6106 | 0 | } |
6107 | 0 | window->OnCompositedChanged(); |
6108 | 0 | } |
6109 | | |
6110 | | static void |
6111 | | scale_changed_cb (GtkWidget* widget, GParamSpec* aPSpec, gpointer aPointer) |
6112 | 0 | { |
6113 | 0 | RefPtr<nsWindow> window = get_window_for_gtk_widget(widget); |
6114 | 0 | if (!window) { |
6115 | 0 | return; |
6116 | 0 | } |
6117 | 0 | // This eventually propagate new scale to the PuppetWidgets |
6118 | 0 | window->OnDPIChanged(); |
6119 | 0 |
|
6120 | 0 | // configure_event is already fired before scale-factor signal, |
6121 | 0 | // but size-allocate isn't fired by changing scale |
6122 | 0 | GtkAllocation allocation; |
6123 | 0 | gtk_widget_get_allocation(widget, &allocation); |
6124 | 0 | window->OnSizeAllocate(&allocation); |
6125 | 0 | } |
6126 | | |
6127 | | #if GTK_CHECK_VERSION(3,4,0) |
6128 | | static gboolean |
6129 | | touch_event_cb(GtkWidget* aWidget, GdkEventTouch* aEvent) |
6130 | 0 | { |
6131 | 0 | UpdateLastInputEventTime(aEvent); |
6132 | 0 |
|
6133 | 0 | nsWindow* window = GetFirstNSWindowForGDKWindow(aEvent->window); |
6134 | 0 | if (!window) { |
6135 | 0 | return FALSE; |
6136 | 0 | } |
6137 | 0 | |
6138 | 0 | return window->OnTouchEvent(aEvent); |
6139 | 0 | } |
6140 | | #endif |
6141 | | |
6142 | | ////////////////////////////////////////////////////////////////////// |
6143 | | // These are all of our drag and drop operations |
6144 | | |
6145 | | void |
6146 | | nsWindow::InitDragEvent(WidgetDragEvent &aEvent) |
6147 | 0 | { |
6148 | 0 | // set the keyboard modifiers |
6149 | 0 | guint modifierState = KeymapWrapper::GetCurrentModifierState(); |
6150 | 0 | KeymapWrapper::InitInputEvent(aEvent, modifierState); |
6151 | 0 | } |
6152 | | |
6153 | | gboolean |
6154 | | WindowDragMotionHandler(GtkWidget *aWidget, |
6155 | | GdkDragContext *aDragContext, |
6156 | | nsWaylandDragContext *aWaylandDragContext, |
6157 | | gint aX, |
6158 | | gint aY, |
6159 | | guint aTime) |
6160 | 0 | { |
6161 | 0 | RefPtr<nsWindow> window = get_window_for_gtk_widget(aWidget); |
6162 | 0 | if (!window) |
6163 | 0 | return FALSE; |
6164 | 0 | |
6165 | 0 | // figure out which internal widget this drag motion actually happened on |
6166 | 0 | nscoord retx = 0; |
6167 | 0 | nscoord rety = 0; |
6168 | 0 |
|
6169 | 0 | GdkWindow *innerWindow = |
6170 | 0 | get_inner_gdk_window(gtk_widget_get_window(aWidget), aX, aY, |
6171 | 0 | &retx, &rety); |
6172 | 0 | RefPtr<nsWindow> innerMostWindow = get_window_for_gdk_window(innerWindow); |
6173 | 0 |
|
6174 | 0 | if (!innerMostWindow) { |
6175 | 0 | innerMostWindow = window; |
6176 | 0 | } |
6177 | 0 |
|
6178 | 0 | LOGDRAG(("nsWindow drag-motion signal for %p\n", (void*)innerMostWindow)); |
6179 | 0 |
|
6180 | 0 | LayoutDeviceIntPoint point = window->GdkPointToDevicePixels({ retx, rety }); |
6181 | 0 |
|
6182 | 0 | RefPtr<nsDragService> dragService = nsDragService::GetInstance(); |
6183 | 0 | return dragService-> |
6184 | 0 | ScheduleMotionEvent(innerMostWindow, aDragContext, aWaylandDragContext, |
6185 | 0 | point, aTime); |
6186 | 0 | } |
6187 | | |
6188 | | static gboolean |
6189 | | drag_motion_event_cb(GtkWidget *aWidget, |
6190 | | GdkDragContext *aDragContext, |
6191 | | gint aX, |
6192 | | gint aY, |
6193 | | guint aTime, |
6194 | | gpointer aData) |
6195 | 0 | { |
6196 | 0 | return WindowDragMotionHandler(aWidget, aDragContext, nullptr, |
6197 | 0 | aX, aY, aTime); |
6198 | 0 | } |
6199 | | |
6200 | | void |
6201 | | WindowDragLeaveHandler(GtkWidget *aWidget) |
6202 | 0 | { |
6203 | 0 | RefPtr<nsWindow> window = get_window_for_gtk_widget(aWidget); |
6204 | 0 | if (!window) |
6205 | 0 | return; |
6206 | 0 | |
6207 | 0 | RefPtr<nsDragService> dragService = nsDragService::GetInstance(); |
6208 | 0 |
|
6209 | 0 | nsWindow *mostRecentDragWindow = dragService->GetMostRecentDestWindow(); |
6210 | 0 | if (!mostRecentDragWindow) { |
6211 | 0 | // This can happen when the target will not accept a drop. A GTK drag |
6212 | 0 | // source sends the leave message to the destination before the |
6213 | 0 | // drag-failed signal on the source widget, but the leave message goes |
6214 | 0 | // via the X server, and so doesn't get processed at least until the |
6215 | 0 | // event loop runs again. |
6216 | 0 | return; |
6217 | 0 | } |
6218 | 0 | |
6219 | 0 | GtkWidget *mozContainer = mostRecentDragWindow->GetMozContainerWidget(); |
6220 | 0 | if (aWidget != mozContainer) |
6221 | 0 | { |
6222 | 0 | // When the drag moves between widgets, GTK can send leave signal for |
6223 | 0 | // the old widget after the motion or drop signal for the new widget. |
6224 | 0 | // We'll send the leave event when the motion or drop event is run. |
6225 | 0 | return; |
6226 | 0 | } |
6227 | 0 | |
6228 | 0 | LOGDRAG(("nsWindow drag-leave signal for %p\n", |
6229 | 0 | (void*)mostRecentDragWindow)); |
6230 | 0 |
|
6231 | 0 | dragService->ScheduleLeaveEvent(); |
6232 | 0 | } |
6233 | | |
6234 | | static void |
6235 | | drag_leave_event_cb(GtkWidget *aWidget, |
6236 | | GdkDragContext *aDragContext, |
6237 | | guint aTime, |
6238 | | gpointer aData) |
6239 | 0 | { |
6240 | 0 | WindowDragLeaveHandler(aWidget); |
6241 | 0 | } |
6242 | | |
6243 | | gboolean |
6244 | | WindowDragDropHandler(GtkWidget *aWidget, |
6245 | | GdkDragContext *aDragContext, |
6246 | | nsWaylandDragContext *aWaylandDragContext, |
6247 | | gint aX, |
6248 | | gint aY, |
6249 | | guint aTime) |
6250 | 0 | { |
6251 | 0 | RefPtr<nsWindow> window = get_window_for_gtk_widget(aWidget); |
6252 | 0 | if (!window) |
6253 | 0 | return FALSE; |
6254 | 0 | |
6255 | 0 | // figure out which internal widget this drag motion actually happened on |
6256 | 0 | nscoord retx = 0; |
6257 | 0 | nscoord rety = 0; |
6258 | 0 |
|
6259 | 0 | GdkWindow *innerWindow = |
6260 | 0 | get_inner_gdk_window(gtk_widget_get_window(aWidget), aX, aY, |
6261 | 0 | &retx, &rety); |
6262 | 0 | RefPtr<nsWindow> innerMostWindow = get_window_for_gdk_window(innerWindow); |
6263 | 0 |
|
6264 | 0 | if (!innerMostWindow) { |
6265 | 0 | innerMostWindow = window; |
6266 | 0 | } |
6267 | 0 |
|
6268 | 0 | LOGDRAG(("nsWindow drag-drop signal for %p\n", (void*)innerMostWindow)); |
6269 | 0 |
|
6270 | 0 | LayoutDeviceIntPoint point = window->GdkPointToDevicePixels({ retx, rety }); |
6271 | 0 |
|
6272 | 0 | RefPtr<nsDragService> dragService = nsDragService::GetInstance(); |
6273 | 0 | return dragService-> |
6274 | 0 | ScheduleDropEvent(innerMostWindow, aDragContext, aWaylandDragContext, |
6275 | 0 | point, aTime); |
6276 | 0 | } |
6277 | | |
6278 | | static gboolean |
6279 | | drag_drop_event_cb(GtkWidget *aWidget, |
6280 | | GdkDragContext *aDragContext, |
6281 | | gint aX, |
6282 | | gint aY, |
6283 | | guint aTime, |
6284 | | gpointer aData) |
6285 | 0 | { |
6286 | 0 | return WindowDragDropHandler(aWidget, aDragContext, nullptr, aX, aY, aTime); |
6287 | 0 | } |
6288 | | |
6289 | | static void |
6290 | | drag_data_received_event_cb(GtkWidget *aWidget, |
6291 | | GdkDragContext *aDragContext, |
6292 | | gint aX, |
6293 | | gint aY, |
6294 | | GtkSelectionData *aSelectionData, |
6295 | | guint aInfo, |
6296 | | guint aTime, |
6297 | | gpointer aData) |
6298 | 0 | { |
6299 | 0 | RefPtr<nsWindow> window = get_window_for_gtk_widget(aWidget); |
6300 | 0 | if (!window) |
6301 | 0 | return; |
6302 | 0 | |
6303 | 0 | window->OnDragDataReceivedEvent(aWidget, |
6304 | 0 | aDragContext, |
6305 | 0 | aX, aY, |
6306 | 0 | aSelectionData, |
6307 | 0 | aInfo, aTime, aData); |
6308 | 0 | } |
6309 | | |
6310 | | static nsresult |
6311 | | initialize_prefs(void) |
6312 | 0 | { |
6313 | 0 | gRaiseWindows = |
6314 | 0 | Preferences::GetBool("mozilla.widget.raise-on-setfocus", true); |
6315 | 0 |
|
6316 | 0 | return NS_OK; |
6317 | 0 | } |
6318 | | |
6319 | | static GdkWindow * |
6320 | | get_inner_gdk_window (GdkWindow *aWindow, |
6321 | | gint x, gint y, |
6322 | | gint *retx, gint *rety) |
6323 | 0 | { |
6324 | 0 | gint cx, cy, cw, ch; |
6325 | 0 | GList *children = gdk_window_peek_children(aWindow); |
6326 | 0 | for (GList *child = g_list_last(children); |
6327 | 0 | child; |
6328 | 0 | child = g_list_previous(child)) { |
6329 | 0 | auto *childWindow = (GdkWindow *) child->data; |
6330 | 0 | if (get_window_for_gdk_window(childWindow)) { |
6331 | 0 | gdk_window_get_geometry(childWindow, &cx, &cy, &cw, &ch); |
6332 | 0 | if ((cx < x) && (x < (cx + cw)) && |
6333 | 0 | (cy < y) && (y < (cy + ch)) && |
6334 | 0 | gdk_window_is_visible(childWindow)) { |
6335 | 0 | return get_inner_gdk_window(childWindow, |
6336 | 0 | x - cx, y - cy, |
6337 | 0 | retx, rety); |
6338 | 0 | } |
6339 | 0 | } |
6340 | 0 | } |
6341 | 0 | *retx = x; |
6342 | 0 | *rety = y; |
6343 | 0 | return aWindow; |
6344 | 0 | } |
6345 | | |
6346 | | static int |
6347 | | is_parent_ungrab_enter(GdkEventCrossing *aEvent) |
6348 | 0 | { |
6349 | 0 | return (GDK_CROSSING_UNGRAB == aEvent->mode) && |
6350 | 0 | ((GDK_NOTIFY_ANCESTOR == aEvent->detail) || |
6351 | 0 | (GDK_NOTIFY_VIRTUAL == aEvent->detail)); |
6352 | 0 |
|
6353 | 0 | } |
6354 | | |
6355 | | static int |
6356 | | is_parent_grab_leave(GdkEventCrossing *aEvent) |
6357 | 0 | { |
6358 | 0 | return (GDK_CROSSING_GRAB == aEvent->mode) && |
6359 | 0 | ((GDK_NOTIFY_ANCESTOR == aEvent->detail) || |
6360 | 0 | (GDK_NOTIFY_VIRTUAL == aEvent->detail)); |
6361 | 0 | } |
6362 | | |
6363 | | #ifdef ACCESSIBILITY |
6364 | | void |
6365 | | nsWindow::CreateRootAccessible() |
6366 | 0 | { |
6367 | 0 | if (mIsTopLevel && !mRootAccessible) { |
6368 | 0 | LOG(("nsWindow:: Create Toplevel Accessibility\n")); |
6369 | 0 | mRootAccessible = GetRootAccessible(); |
6370 | 0 | } |
6371 | 0 | } |
6372 | | |
6373 | | void |
6374 | | nsWindow::DispatchEventToRootAccessible(uint32_t aEventType) |
6375 | 0 | { |
6376 | 0 | if (!a11y::ShouldA11yBeEnabled()) { |
6377 | 0 | return; |
6378 | 0 | } |
6379 | 0 | |
6380 | 0 | nsAccessibilityService* accService = GetOrCreateAccService(); |
6381 | 0 | if (!accService) { |
6382 | 0 | return; |
6383 | 0 | } |
6384 | 0 | |
6385 | 0 | // Get the root document accessible and fire event to it. |
6386 | 0 | a11y::Accessible* acc = GetRootAccessible(); |
6387 | 0 | if (acc) { |
6388 | 0 | accService->FireAccessibleEvent(aEventType, acc); |
6389 | 0 | } |
6390 | 0 | } |
6391 | | |
6392 | | void |
6393 | | nsWindow::DispatchActivateEventAccessible(void) |
6394 | 0 | { |
6395 | 0 | DispatchEventToRootAccessible(nsIAccessibleEvent::EVENT_WINDOW_ACTIVATE); |
6396 | 0 | } |
6397 | | |
6398 | | void |
6399 | | nsWindow::DispatchDeactivateEventAccessible(void) |
6400 | 0 | { |
6401 | 0 | DispatchEventToRootAccessible(nsIAccessibleEvent::EVENT_WINDOW_DEACTIVATE); |
6402 | 0 | } |
6403 | | |
6404 | | void |
6405 | | nsWindow::DispatchMaximizeEventAccessible(void) |
6406 | 0 | { |
6407 | 0 | DispatchEventToRootAccessible(nsIAccessibleEvent::EVENT_WINDOW_MAXIMIZE); |
6408 | 0 | } |
6409 | | |
6410 | | void |
6411 | | nsWindow::DispatchMinimizeEventAccessible(void) |
6412 | 0 | { |
6413 | 0 | DispatchEventToRootAccessible(nsIAccessibleEvent::EVENT_WINDOW_MINIMIZE); |
6414 | 0 | } |
6415 | | |
6416 | | void |
6417 | | nsWindow::DispatchRestoreEventAccessible(void) |
6418 | 0 | { |
6419 | 0 | DispatchEventToRootAccessible(nsIAccessibleEvent::EVENT_WINDOW_RESTORE); |
6420 | 0 | } |
6421 | | |
6422 | | #endif /* #ifdef ACCESSIBILITY */ |
6423 | | |
6424 | | void |
6425 | | nsWindow::SetInputContext(const InputContext& aContext, |
6426 | | const InputContextAction& aAction) |
6427 | 0 | { |
6428 | 0 | if (!mIMContext) { |
6429 | 0 | return; |
6430 | 0 | } |
6431 | 0 | mIMContext->SetInputContext(this, &aContext, &aAction); |
6432 | 0 | } |
6433 | | |
6434 | | InputContext |
6435 | | nsWindow::GetInputContext() |
6436 | 0 | { |
6437 | 0 | InputContext context; |
6438 | 0 | if (!mIMContext) { |
6439 | 0 | context.mIMEState.mEnabled = IMEState::DISABLED; |
6440 | 0 | context.mIMEState.mOpen = IMEState::OPEN_STATE_NOT_SUPPORTED; |
6441 | 0 | } else { |
6442 | 0 | context = mIMContext->GetInputContext(); |
6443 | 0 | } |
6444 | 0 | return context; |
6445 | 0 | } |
6446 | | |
6447 | | TextEventDispatcherListener* |
6448 | | nsWindow::GetNativeTextEventDispatcherListener() |
6449 | 0 | { |
6450 | 0 | if (NS_WARN_IF(!mIMContext)) { |
6451 | 0 | return nullptr; |
6452 | 0 | } |
6453 | 0 | return mIMContext; |
6454 | 0 | } |
6455 | | |
6456 | | void |
6457 | | nsWindow::GetEditCommandsRemapped(NativeKeyBindingsType aType, |
6458 | | const WidgetKeyboardEvent& aEvent, |
6459 | | nsTArray<CommandInt>& aCommands, |
6460 | | uint32_t aGeckoKeyCode, |
6461 | | uint32_t aNativeKeyCode) |
6462 | 0 | { |
6463 | 0 | // If aEvent.mNativeKeyEvent is nullptr, the event was created by chrome |
6464 | 0 | // script. In such case, we shouldn't expose the OS settings to it. |
6465 | 0 | // So, just ignore such events here. |
6466 | 0 | if (!aEvent.mNativeKeyEvent) { |
6467 | 0 | return; |
6468 | 0 | } |
6469 | 0 | WidgetKeyboardEvent modifiedEvent(aEvent); |
6470 | 0 | modifiedEvent.mKeyCode = aGeckoKeyCode; |
6471 | 0 | static_cast<GdkEventKey*>(modifiedEvent.mNativeKeyEvent)->keyval = |
6472 | 0 | aNativeKeyCode; |
6473 | 0 |
|
6474 | 0 | NativeKeyBindings* keyBindings = NativeKeyBindings::GetInstance(aType); |
6475 | 0 | keyBindings->GetEditCommands(modifiedEvent, aCommands); |
6476 | 0 | } |
6477 | | |
6478 | | void |
6479 | | nsWindow::GetEditCommands(NativeKeyBindingsType aType, |
6480 | | const WidgetKeyboardEvent& aEvent, |
6481 | | nsTArray<CommandInt>& aCommands) |
6482 | 0 | { |
6483 | 0 | // Validate the arguments. |
6484 | 0 | nsIWidget::GetEditCommands(aType, aEvent, aCommands); |
6485 | 0 |
|
6486 | 0 | if (aEvent.mKeyCode >= NS_VK_LEFT && aEvent.mKeyCode <= NS_VK_DOWN) { |
6487 | 0 | // Check if we're targeting content with vertical writing mode, |
6488 | 0 | // and if so remap the arrow keys. |
6489 | 0 | // XXX This may be expensive. |
6490 | 0 | WidgetQueryContentEvent query(true, eQuerySelectedText, this); |
6491 | 0 | nsEventStatus status; |
6492 | 0 | DispatchEvent(&query, status); |
6493 | 0 |
|
6494 | 0 | if (query.mSucceeded && query.mReply.mWritingMode.IsVertical()) { |
6495 | 0 | uint32_t geckoCode = 0; |
6496 | 0 | uint32_t gdkCode = 0; |
6497 | 0 | switch (aEvent.mKeyCode) { |
6498 | 0 | case NS_VK_LEFT: |
6499 | 0 | if (query.mReply.mWritingMode.IsVerticalLR()) { |
6500 | 0 | geckoCode = NS_VK_UP; |
6501 | 0 | gdkCode = GDK_Up; |
6502 | 0 | } else { |
6503 | 0 | geckoCode = NS_VK_DOWN; |
6504 | 0 | gdkCode = GDK_Down; |
6505 | 0 | } |
6506 | 0 | break; |
6507 | 0 |
|
6508 | 0 | case NS_VK_RIGHT: |
6509 | 0 | if (query.mReply.mWritingMode.IsVerticalLR()) { |
6510 | 0 | geckoCode = NS_VK_DOWN; |
6511 | 0 | gdkCode = GDK_Down; |
6512 | 0 | } else { |
6513 | 0 | geckoCode = NS_VK_UP; |
6514 | 0 | gdkCode = GDK_Up; |
6515 | 0 | } |
6516 | 0 | break; |
6517 | 0 |
|
6518 | 0 | case NS_VK_UP: |
6519 | 0 | geckoCode = NS_VK_LEFT; |
6520 | 0 | gdkCode = GDK_Left; |
6521 | 0 | break; |
6522 | 0 |
|
6523 | 0 | case NS_VK_DOWN: |
6524 | 0 | geckoCode = NS_VK_RIGHT; |
6525 | 0 | gdkCode = GDK_Right; |
6526 | 0 | break; |
6527 | 0 | } |
6528 | 0 |
|
6529 | 0 | GetEditCommandsRemapped(aType, aEvent, aCommands, |
6530 | 0 | geckoCode, gdkCode); |
6531 | 0 | return; |
6532 | 0 | } |
6533 | 0 | } |
6534 | 0 |
|
6535 | 0 | NativeKeyBindings* keyBindings = NativeKeyBindings::GetInstance(aType); |
6536 | 0 | keyBindings->GetEditCommands(aEvent, aCommands); |
6537 | 0 | } |
6538 | | |
6539 | | already_AddRefed<DrawTarget> |
6540 | | nsWindow::StartRemoteDrawingInRegion(LayoutDeviceIntRegion& aInvalidRegion, BufferMode* aBufferMode) |
6541 | 0 | { |
6542 | 0 | return mSurfaceProvider.StartRemoteDrawingInRegion(aInvalidRegion, aBufferMode); |
6543 | 0 | } |
6544 | | |
6545 | | void |
6546 | | nsWindow::EndRemoteDrawingInRegion(DrawTarget* aDrawTarget, |
6547 | | LayoutDeviceIntRegion& aInvalidRegion) |
6548 | 0 | { |
6549 | 0 | mSurfaceProvider.EndRemoteDrawingInRegion(aDrawTarget, aInvalidRegion); |
6550 | 0 | } |
6551 | | |
6552 | | // Code shared begin BeginMoveDrag and BeginResizeDrag |
6553 | | bool |
6554 | | nsWindow::GetDragInfo(WidgetMouseEvent* aMouseEvent, |
6555 | | GdkWindow** aWindow, gint* aButton, |
6556 | | gint* aRootX, gint* aRootY) |
6557 | 0 | { |
6558 | 0 | if (aMouseEvent->button != WidgetMouseEvent::eLeftButton) { |
6559 | 0 | // we can only begin a move drag with the left mouse button |
6560 | 0 | return false; |
6561 | 0 | } |
6562 | 0 | *aButton = 1; |
6563 | 0 |
|
6564 | 0 | // get the gdk window for this widget |
6565 | 0 | GdkWindow* gdk_window = mGdkWindow; |
6566 | 0 | if (!gdk_window) { |
6567 | 0 | return false; |
6568 | 0 | } |
6569 | | #ifdef DEBUG |
6570 | | // GDK_IS_WINDOW(...) expands to a statement-expression, and |
6571 | | // statement-expressions are not allowed in template-argument lists. So we |
6572 | | // have to make the MOZ_ASSERT condition indirect. |
6573 | | if (!GDK_IS_WINDOW(gdk_window)) { |
6574 | | MOZ_ASSERT(false, "must really be window"); |
6575 | | } |
6576 | | #endif |
6577 | | |
6578 | 0 | // find the top-level window |
6579 | 0 | gdk_window = gdk_window_get_toplevel(gdk_window); |
6580 | 0 | MOZ_ASSERT(gdk_window, |
6581 | 0 | "gdk_window_get_toplevel should not return null"); |
6582 | 0 | *aWindow = gdk_window; |
6583 | 0 |
|
6584 | 0 | if (!aMouseEvent->mWidget) { |
6585 | 0 | return false; |
6586 | 0 | } |
6587 | 0 | |
6588 | 0 | if (mIsX11Display) { |
6589 | 0 | // Workaround for https://bugzilla.gnome.org/show_bug.cgi?id=789054 |
6590 | 0 | // To avoid crashes disable double-click on WM without _NET_WM_MOVERESIZE. |
6591 | 0 | // See _should_perform_ewmh_drag() at gdkwindow-x11.c |
6592 | 0 | GdkScreen* screen = gdk_window_get_screen(gdk_window); |
6593 | 0 | GdkAtom atom = gdk_atom_intern("_NET_WM_MOVERESIZE", FALSE); |
6594 | 0 | if (!gdk_x11_screen_supports_net_wm_hint(screen, atom)) { |
6595 | 0 | static unsigned int lastTimeStamp = 0; |
6596 | 0 | if (lastTimeStamp != aMouseEvent->mTime) { |
6597 | 0 | lastTimeStamp = aMouseEvent->mTime; |
6598 | 0 | } else { |
6599 | 0 | return false; |
6600 | 0 | } |
6601 | 0 | } |
6602 | 0 | } |
6603 | 0 | |
6604 | 0 | // FIXME: It would be nice to have the widget position at the time |
6605 | 0 | // of the event, but it's relatively unlikely that the widget has |
6606 | 0 | // moved since the mousedown. (On the other hand, it's quite likely |
6607 | 0 | // that the mouse has moved, which is why we use the mouse position |
6608 | 0 | // from the event.) |
6609 | 0 | LayoutDeviceIntPoint offset = aMouseEvent->mWidget->WidgetToScreenOffset(); |
6610 | 0 | *aRootX = aMouseEvent->mRefPoint.x + offset.x; |
6611 | 0 | *aRootY = aMouseEvent->mRefPoint.y + offset.y; |
6612 | 0 |
|
6613 | 0 | return true; |
6614 | 0 | } |
6615 | | |
6616 | | nsresult |
6617 | | nsWindow::BeginMoveDrag(WidgetMouseEvent* aEvent) |
6618 | 0 | { |
6619 | 0 | MOZ_ASSERT(aEvent, "must have event"); |
6620 | 0 | MOZ_ASSERT(aEvent->mClass == eMouseEventClass, |
6621 | 0 | "event must have correct struct type"); |
6622 | 0 |
|
6623 | 0 | GdkWindow *gdk_window; |
6624 | 0 | gint button, screenX, screenY; |
6625 | 0 | if (!GetDragInfo(aEvent, &gdk_window, &button, &screenX, &screenY)) { |
6626 | 0 | return NS_ERROR_FAILURE; |
6627 | 0 | } |
6628 | 0 | |
6629 | 0 | // tell the window manager to start the move |
6630 | 0 | screenX = DevicePixelsToGdkCoordRoundDown(screenX); |
6631 | 0 | screenY = DevicePixelsToGdkCoordRoundDown(screenY); |
6632 | 0 | gdk_window_begin_move_drag(gdk_window, button, screenX, screenY, |
6633 | 0 | aEvent->mTime); |
6634 | 0 |
|
6635 | 0 | return NS_OK; |
6636 | 0 | } |
6637 | | |
6638 | | nsresult |
6639 | | nsWindow::BeginResizeDrag(WidgetGUIEvent* aEvent, |
6640 | | int32_t aHorizontal, |
6641 | | int32_t aVertical) |
6642 | 0 | { |
6643 | 0 | NS_ENSURE_ARG_POINTER(aEvent); |
6644 | 0 |
|
6645 | 0 | if (aEvent->mClass != eMouseEventClass) { |
6646 | 0 | // you can only begin a resize drag with a mouse event |
6647 | 0 | return NS_ERROR_INVALID_ARG; |
6648 | 0 | } |
6649 | 0 | |
6650 | 0 | GdkWindow *gdk_window; |
6651 | 0 | gint button, screenX, screenY; |
6652 | 0 | if (!GetDragInfo(aEvent->AsMouseEvent(), &gdk_window, &button, |
6653 | 0 | &screenX, &screenY)) { |
6654 | 0 | return NS_ERROR_FAILURE; |
6655 | 0 | } |
6656 | 0 | |
6657 | 0 | // work out what GdkWindowEdge we're talking about |
6658 | 0 | GdkWindowEdge window_edge; |
6659 | 0 | if (aVertical < 0) { |
6660 | 0 | if (aHorizontal < 0) { |
6661 | 0 | window_edge = GDK_WINDOW_EDGE_NORTH_WEST; |
6662 | 0 | } else if (aHorizontal == 0) { |
6663 | 0 | window_edge = GDK_WINDOW_EDGE_NORTH; |
6664 | 0 | } else { |
6665 | 0 | window_edge = GDK_WINDOW_EDGE_NORTH_EAST; |
6666 | 0 | } |
6667 | 0 | } else if (aVertical == 0) { |
6668 | 0 | if (aHorizontal < 0) { |
6669 | 0 | window_edge = GDK_WINDOW_EDGE_WEST; |
6670 | 0 | } else if (aHorizontal == 0) { |
6671 | 0 | return NS_ERROR_INVALID_ARG; |
6672 | 0 | } else { |
6673 | 0 | window_edge = GDK_WINDOW_EDGE_EAST; |
6674 | 0 | } |
6675 | 0 | } else { |
6676 | 0 | if (aHorizontal < 0) { |
6677 | 0 | window_edge = GDK_WINDOW_EDGE_SOUTH_WEST; |
6678 | 0 | } else if (aHorizontal == 0) { |
6679 | 0 | window_edge = GDK_WINDOW_EDGE_SOUTH; |
6680 | 0 | } else { |
6681 | 0 | window_edge = GDK_WINDOW_EDGE_SOUTH_EAST; |
6682 | 0 | } |
6683 | 0 | } |
6684 | 0 |
|
6685 | 0 | // tell the window manager to start the resize |
6686 | 0 | gdk_window_begin_resize_drag(gdk_window, window_edge, button, |
6687 | 0 | screenX, screenY, aEvent->mTime); |
6688 | 0 |
|
6689 | 0 | return NS_OK; |
6690 | 0 | } |
6691 | | |
6692 | | nsIWidget::LayerManager* |
6693 | | nsWindow::GetLayerManager(PLayerTransactionChild* aShadowManager, |
6694 | | LayersBackend aBackendHint, |
6695 | | LayerManagerPersistence aPersistence) |
6696 | 0 | { |
6697 | 0 | if (mIsDestroyed) { |
6698 | 0 | // Prevent external code from triggering the re-creation of the LayerManager/Compositor |
6699 | 0 | // during shutdown. Just return what we currently have, which is most likely null. |
6700 | 0 | return mLayerManager; |
6701 | 0 | } |
6702 | 0 | |
6703 | 0 | return nsBaseWidget::GetLayerManager(aShadowManager, aBackendHint, aPersistence); |
6704 | 0 | } |
6705 | | |
6706 | | void |
6707 | | nsWindow::SetCompositorWidgetDelegate(CompositorWidgetDelegate* delegate) |
6708 | 0 | { |
6709 | 0 | if (delegate) { |
6710 | 0 | mCompositorWidgetDelegate = delegate->AsPlatformSpecificDelegate(); |
6711 | 0 | MOZ_ASSERT(mCompositorWidgetDelegate, |
6712 | 0 | "nsWindow::SetCompositorWidgetDelegate called with a non-PlatformCompositorWidgetDelegate"); |
6713 | 0 | } else { |
6714 | 0 | mCompositorWidgetDelegate = nullptr; |
6715 | 0 | } |
6716 | 0 | } |
6717 | | |
6718 | | void |
6719 | | nsWindow::ClearCachedResources() |
6720 | 0 | { |
6721 | 0 | if (mLayerManager && |
6722 | 0 | mLayerManager->GetBackendType() == mozilla::layers::LayersBackend::LAYERS_BASIC) { |
6723 | 0 | mLayerManager->ClearCachedResources(); |
6724 | 0 | } |
6725 | 0 |
|
6726 | 0 | GList* children = gdk_window_peek_children(mGdkWindow); |
6727 | 0 | for (GList* list = children; list; list = list->next) { |
6728 | 0 | nsWindow* window = get_window_for_gdk_window(GDK_WINDOW(list->data)); |
6729 | 0 | if (window) { |
6730 | 0 | window->ClearCachedResources(); |
6731 | 0 | } |
6732 | 0 | } |
6733 | 0 | } |
6734 | | |
6735 | | /* nsWindow::UpdateClientOffsetForCSDWindow() is designed to be called from |
6736 | | * paint code to update mClientOffset any time. It also propagates |
6737 | | * the mClientOffset to child tabs. |
6738 | | * |
6739 | | * It works only for CSD decorated GtkWindow. |
6740 | | */ |
6741 | | void |
6742 | | nsWindow::UpdateClientOffsetForCSDWindow() |
6743 | 0 | { |
6744 | 0 | // We update window offset on X11 as the window position is calculated |
6745 | 0 | // relatively to mShell. We don't do that on Wayland as our wl_subsurface |
6746 | 0 | // is attached to mContainer and mShell is ignored. |
6747 | 0 | if (!mIsX11Display) { |
6748 | 0 | return; |
6749 | 0 | } |
6750 | 0 | |
6751 | 0 | // _NET_FRAME_EXTENTS is not set on client decorated windows, |
6752 | 0 | // so we need to read offset between mContainer and toplevel mShell |
6753 | 0 | // window. |
6754 | 0 | if (mSizeState == nsSizeMode_Normal) { |
6755 | 0 | GtkBorder decorationSize; |
6756 | 0 | GetCSDDecorationSize(GTK_WINDOW(mShell), &decorationSize); |
6757 | 0 | mClientOffset = nsIntPoint(decorationSize.left, decorationSize.top); |
6758 | 0 | } else { |
6759 | 0 | mClientOffset = nsIntPoint(0, 0); |
6760 | 0 | } |
6761 | 0 |
|
6762 | 0 | // Send a WindowMoved notification. This ensures that TabParent |
6763 | 0 | // picks up the new client offset and sends it to the child process |
6764 | 0 | // if appropriate. |
6765 | 0 | NotifyWindowMoved(mBounds.x, mBounds.y); |
6766 | 0 | } |
6767 | | |
6768 | | nsresult |
6769 | | nsWindow::SetNonClientMargins(LayoutDeviceIntMargin &aMargins) |
6770 | 0 | { |
6771 | 0 | SetDrawsInTitlebar(aMargins.top == 0); |
6772 | 0 | return NS_OK; |
6773 | 0 | } |
6774 | | |
6775 | | void |
6776 | | nsWindow::SetDrawsInTitlebar(bool aState) |
6777 | 0 | { |
6778 | 0 | if (!mShell || |
6779 | 0 | mCSDSupportLevel == CSD_SUPPORT_NONE || |
6780 | 0 | aState == mDrawInTitlebar) { |
6781 | 0 | return; |
6782 | 0 | } |
6783 | 0 | |
6784 | 0 | if (mCSDSupportLevel == CSD_SUPPORT_SYSTEM) { |
6785 | 0 | SetWindowDecoration(aState ? eBorderStyle_border : mBorderStyle); |
6786 | 0 | } |
6787 | 0 | else if (mCSDSupportLevel == CSD_SUPPORT_CLIENT) { |
6788 | 0 | /* Window manager does not support GDK_DECOR_BORDER, |
6789 | 0 | * emulate it by CSD. |
6790 | 0 | * |
6791 | 0 | * gtk_window_set_titlebar() works on unrealized widgets only, |
6792 | 0 | * we need to handle mShell carefully here. |
6793 | 0 | * When CSD is enabled mGdkWindow is owned by mContainer which is good |
6794 | 0 | * as we can't delete our mGdkWindow. To make mShell unrealized while |
6795 | 0 | * mContainer is preserved we temporary reparent mContainer to an |
6796 | 0 | * invisible GtkWindow. |
6797 | 0 | */ |
6798 | 0 | NativeShow(false); |
6799 | 0 |
|
6800 | 0 | // Using GTK_WINDOW_POPUP rather than |
6801 | 0 | // GTK_WINDOW_TOPLEVEL in the hope that POPUP results in less |
6802 | 0 | // initialization and window manager interaction. |
6803 | 0 | GtkWidget* tmpWindow = gtk_window_new(GTK_WINDOW_POPUP); |
6804 | 0 | gtk_widget_realize(tmpWindow); |
6805 | 0 |
|
6806 | 0 | gtk_widget_reparent(GTK_WIDGET(mContainer), tmpWindow); |
6807 | 0 | gtk_widget_unrealize(GTK_WIDGET(mShell)); |
6808 | 0 |
|
6809 | 0 | // Available as of GTK 3.10+ |
6810 | 0 | static auto sGtkWindowSetTitlebar = (void (*)(GtkWindow*, GtkWidget*)) |
6811 | 0 | dlsym(RTLD_DEFAULT, "gtk_window_set_titlebar"); |
6812 | 0 | MOZ_ASSERT(sGtkWindowSetTitlebar, |
6813 | 0 | "Missing gtk_window_set_titlebar(), old Gtk+ library?"); |
6814 | 0 |
|
6815 | 0 | if (aState) { |
6816 | 0 | // Add a hidden titlebar widget to trigger CSD, but disable the default |
6817 | 0 | // titlebar. GtkFixed is a somewhat random choice for a simple unused |
6818 | 0 | // widget. gtk_window_set_titlebar() takes ownership of the titlebar |
6819 | 0 | // widget. |
6820 | 0 | sGtkWindowSetTitlebar(GTK_WINDOW(mShell), gtk_fixed_new()); |
6821 | 0 | } else { |
6822 | 0 | sGtkWindowSetTitlebar(GTK_WINDOW(mShell), nullptr); |
6823 | 0 | } |
6824 | 0 |
|
6825 | 0 | /* A workaround for https://bugzilla.gnome.org/show_bug.cgi?id=791081 |
6826 | 0 | * gtk_widget_realize() throws: |
6827 | 0 | * "In pixman_region32_init_rect: Invalid rectangle passed" |
6828 | 0 | * when mShell has default 1x1 size. |
6829 | 0 | */ |
6830 | 0 | GtkAllocation allocation = {0, 0, 0, 0}; |
6831 | 0 | gtk_widget_get_preferred_width(GTK_WIDGET(mShell), nullptr, |
6832 | 0 | &allocation.width); |
6833 | 0 | gtk_widget_get_preferred_height(GTK_WIDGET(mShell), nullptr, |
6834 | 0 | &allocation.height); |
6835 | 0 | gtk_widget_size_allocate(GTK_WIDGET(mShell), &allocation); |
6836 | 0 |
|
6837 | 0 | gtk_widget_realize(GTK_WIDGET(mShell)); |
6838 | 0 | gtk_widget_reparent(GTK_WIDGET(mContainer), GTK_WIDGET(mShell)); |
6839 | 0 | mNeedsShow = true; |
6840 | 0 | NativeResize(); |
6841 | 0 |
|
6842 | 0 | // Label mShell toplevel window so property_notify_event_cb callback |
6843 | 0 | // can find its way home. |
6844 | 0 | g_object_set_data(G_OBJECT(gtk_widget_get_window(mShell)), |
6845 | 0 | "nsWindow", this); |
6846 | 0 | #ifdef MOZ_X11 |
6847 | 0 | SetCompositorHint(GTK_WIDGET_COMPOSIDED_ENABLED); |
6848 | 0 | #endif |
6849 | 0 | RefreshWindowClass(); |
6850 | 0 |
|
6851 | 0 | // When we use system titlebar setup managed by Gtk+ we also get |
6852 | 0 | // _NET_FRAME_EXTENTS property for our toplevel window so we can't |
6853 | 0 | // update the client offset it here. |
6854 | 0 | if (aState) { |
6855 | 0 | UpdateClientOffsetForCSDWindow(); |
6856 | 0 | } |
6857 | 0 |
|
6858 | 0 | gtk_widget_destroy(tmpWindow); |
6859 | 0 | } |
6860 | 0 |
|
6861 | 0 | mDrawInTitlebar = aState; |
6862 | 0 | } |
6863 | | |
6864 | | gint |
6865 | | nsWindow::GdkScaleFactor() |
6866 | 0 | { |
6867 | 0 | #if (MOZ_WIDGET_GTK >= 3) |
6868 | 0 | // Available as of GTK 3.10+ |
6869 | 0 | static auto sGdkWindowGetScaleFactorPtr = (gint (*)(GdkWindow*)) |
6870 | 0 | dlsym(RTLD_DEFAULT, "gdk_window_get_scale_factor"); |
6871 | 0 | if (sGdkWindowGetScaleFactorPtr && mGdkWindow) |
6872 | 0 | return (*sGdkWindowGetScaleFactorPtr)(mGdkWindow); |
6873 | 0 | #endif |
6874 | 0 | return ScreenHelperGTK::GetGTKMonitorScaleFactor(); |
6875 | 0 | } |
6876 | | |
6877 | | |
6878 | | gint |
6879 | 0 | nsWindow::DevicePixelsToGdkCoordRoundUp(int pixels) { |
6880 | 0 | gint scale = GdkScaleFactor(); |
6881 | 0 | return (pixels + scale - 1) / scale; |
6882 | 0 | } |
6883 | | |
6884 | | gint |
6885 | 0 | nsWindow::DevicePixelsToGdkCoordRoundDown(int pixels) { |
6886 | 0 | gint scale = GdkScaleFactor(); |
6887 | 0 | return pixels / scale; |
6888 | 0 | } |
6889 | | |
6890 | | GdkPoint |
6891 | 0 | nsWindow::DevicePixelsToGdkPointRoundDown(LayoutDeviceIntPoint point) { |
6892 | 0 | gint scale = GdkScaleFactor(); |
6893 | 0 | return { point.x / scale, point.y / scale }; |
6894 | 0 | } |
6895 | | |
6896 | | GdkRectangle |
6897 | 0 | nsWindow::DevicePixelsToGdkRectRoundOut(LayoutDeviceIntRect rect) { |
6898 | 0 | gint scale = GdkScaleFactor(); |
6899 | 0 | int x = rect.x / scale; |
6900 | 0 | int y = rect.y / scale; |
6901 | 0 | int right = (rect.x + rect.width + scale - 1) / scale; |
6902 | 0 | int bottom = (rect.y + rect.height + scale - 1) / scale; |
6903 | 0 | return { x, y, right - x, bottom - y }; |
6904 | 0 | } |
6905 | | |
6906 | | GdkRectangle |
6907 | 0 | nsWindow::DevicePixelsToGdkSizeRoundUp(LayoutDeviceIntSize pixelSize) { |
6908 | 0 | gint scale = GdkScaleFactor(); |
6909 | 0 | gint width = (pixelSize.width + scale - 1) / scale; |
6910 | 0 | gint height = (pixelSize.height + scale - 1) / scale; |
6911 | 0 | return { 0, 0, width, height }; |
6912 | 0 | } |
6913 | | |
6914 | | int |
6915 | 0 | nsWindow::GdkCoordToDevicePixels(gint coord) { |
6916 | 0 | return coord * GdkScaleFactor(); |
6917 | 0 | } |
6918 | | |
6919 | | LayoutDeviceIntPoint |
6920 | | nsWindow::GdkEventCoordsToDevicePixels(gdouble x, gdouble y) |
6921 | 0 | { |
6922 | 0 | gint scale = GdkScaleFactor(); |
6923 | 0 | return LayoutDeviceIntPoint::Round(x * scale, y * scale); |
6924 | 0 | } |
6925 | | |
6926 | | LayoutDeviceIntPoint |
6927 | 0 | nsWindow::GdkPointToDevicePixels(GdkPoint point) { |
6928 | 0 | gint scale = GdkScaleFactor(); |
6929 | 0 | return LayoutDeviceIntPoint(point.x * scale, |
6930 | 0 | point.y * scale); |
6931 | 0 | } |
6932 | | |
6933 | | LayoutDeviceIntRect |
6934 | 0 | nsWindow::GdkRectToDevicePixels(GdkRectangle rect) { |
6935 | 0 | gint scale = GdkScaleFactor(); |
6936 | 0 | return LayoutDeviceIntRect(rect.x * scale, |
6937 | 0 | rect.y * scale, |
6938 | 0 | rect.width * scale, |
6939 | 0 | rect.height * scale); |
6940 | 0 | } |
6941 | | |
6942 | | nsresult |
6943 | | nsWindow::SynthesizeNativeMouseEvent(LayoutDeviceIntPoint aPoint, |
6944 | | uint32_t aNativeMessage, |
6945 | | uint32_t aModifierFlags, |
6946 | | nsIObserver* aObserver) |
6947 | 0 | { |
6948 | 0 | AutoObserverNotifier notifier(aObserver, "mouseevent"); |
6949 | 0 |
|
6950 | 0 | if (!mGdkWindow) { |
6951 | 0 | return NS_OK; |
6952 | 0 | } |
6953 | 0 | |
6954 | 0 | GdkDisplay* display = gdk_window_get_display(mGdkWindow); |
6955 | 0 |
|
6956 | 0 | // When a button-press/release event is requested, create it here and put it in the |
6957 | 0 | // event queue. This will not emit a motion event - this needs to be done |
6958 | 0 | // explicitly *before* requesting a button-press/release. You will also need to wait |
6959 | 0 | // for the motion event to be dispatched before requesting a button-press/release |
6960 | 0 | // event to maintain the desired event order. |
6961 | 0 | if (aNativeMessage == GDK_BUTTON_PRESS || aNativeMessage == GDK_BUTTON_RELEASE) { |
6962 | 0 | GdkEvent event; |
6963 | 0 | memset(&event, 0, sizeof(GdkEvent)); |
6964 | 0 | event.type = (GdkEventType)aNativeMessage; |
6965 | 0 | event.button.button = 1; |
6966 | 0 | event.button.window = mGdkWindow; |
6967 | 0 | event.button.time = GDK_CURRENT_TIME; |
6968 | 0 |
|
6969 | 0 | // Get device for event source |
6970 | 0 | GdkDeviceManager *device_manager = gdk_display_get_device_manager(display); |
6971 | 0 | event.button.device = gdk_device_manager_get_client_pointer(device_manager); |
6972 | 0 |
|
6973 | 0 | event.button.x_root = DevicePixelsToGdkCoordRoundDown(aPoint.x); |
6974 | 0 | event.button.y_root = DevicePixelsToGdkCoordRoundDown(aPoint.y); |
6975 | 0 |
|
6976 | 0 | LayoutDeviceIntPoint pointInWindow = aPoint - WidgetToScreenOffset(); |
6977 | 0 | event.button.x = DevicePixelsToGdkCoordRoundDown(pointInWindow.x); |
6978 | 0 | event.button.y = DevicePixelsToGdkCoordRoundDown(pointInWindow.y); |
6979 | 0 |
|
6980 | 0 | gdk_event_put(&event); |
6981 | 0 | } else { |
6982 | 0 | // We don't support specific events other than button-press/release. In all |
6983 | 0 | // other cases we'll synthesize a motion event that will be emitted by |
6984 | 0 | // gdk_display_warp_pointer(). |
6985 | 0 | GdkScreen* screen = gdk_window_get_screen(mGdkWindow); |
6986 | 0 | GdkPoint point = DevicePixelsToGdkPointRoundDown(aPoint); |
6987 | 0 | gdk_display_warp_pointer(display, screen, point.x, point.y); |
6988 | 0 | } |
6989 | 0 |
|
6990 | 0 | return NS_OK; |
6991 | 0 | } |
6992 | | |
6993 | | nsresult |
6994 | | nsWindow::SynthesizeNativeMouseScrollEvent(mozilla::LayoutDeviceIntPoint aPoint, |
6995 | | uint32_t aNativeMessage, |
6996 | | double aDeltaX, |
6997 | | double aDeltaY, |
6998 | | double aDeltaZ, |
6999 | | uint32_t aModifierFlags, |
7000 | | uint32_t aAdditionalFlags, |
7001 | | nsIObserver* aObserver) |
7002 | 0 | { |
7003 | 0 | AutoObserverNotifier notifier(aObserver, "mousescrollevent"); |
7004 | 0 |
|
7005 | 0 | if (!mGdkWindow) { |
7006 | 0 | return NS_OK; |
7007 | 0 | } |
7008 | 0 | |
7009 | 0 | GdkEvent event; |
7010 | 0 | memset(&event, 0, sizeof(GdkEvent)); |
7011 | 0 | event.type = GDK_SCROLL; |
7012 | 0 | event.scroll.window = mGdkWindow; |
7013 | 0 | event.scroll.time = GDK_CURRENT_TIME; |
7014 | 0 | // Get device for event source |
7015 | 0 | GdkDisplay* display = gdk_window_get_display(mGdkWindow); |
7016 | 0 | GdkDeviceManager *device_manager = gdk_display_get_device_manager(display); |
7017 | 0 | event.scroll.device = gdk_device_manager_get_client_pointer(device_manager); |
7018 | 0 | event.scroll.x_root = DevicePixelsToGdkCoordRoundDown(aPoint.x); |
7019 | 0 | event.scroll.y_root = DevicePixelsToGdkCoordRoundDown(aPoint.y); |
7020 | 0 |
|
7021 | 0 | LayoutDeviceIntPoint pointInWindow = aPoint - WidgetToScreenOffset(); |
7022 | 0 | event.scroll.x = DevicePixelsToGdkCoordRoundDown(pointInWindow.x); |
7023 | 0 | event.scroll.y = DevicePixelsToGdkCoordRoundDown(pointInWindow.y); |
7024 | 0 |
|
7025 | 0 | // The delta values are backwards on Linux compared to Windows and Cocoa, |
7026 | 0 | // hence the negation. |
7027 | 0 | #if GTK_CHECK_VERSION(3,4,0) |
7028 | 0 | // TODO: is this correct? I don't have GTK 3.4+ so I can't check |
7029 | 0 | event.scroll.direction = GDK_SCROLL_SMOOTH; |
7030 | 0 | event.scroll.delta_x = -aDeltaX; |
7031 | 0 | event.scroll.delta_y = -aDeltaY; |
7032 | | #else |
7033 | | if (aDeltaX < 0) { |
7034 | | event.scroll.direction = GDK_SCROLL_RIGHT; |
7035 | | } else if (aDeltaX > 0) { |
7036 | | event.scroll.direction = GDK_SCROLL_LEFT; |
7037 | | } else if (aDeltaY < 0) { |
7038 | | event.scroll.direction = GDK_SCROLL_DOWN; |
7039 | | } else if (aDeltaY > 0) { |
7040 | | event.scroll.direction = GDK_SCROLL_UP; |
7041 | | } else { |
7042 | | return NS_OK; |
7043 | | } |
7044 | | #endif |
7045 | |
|
7046 | 0 | gdk_event_put(&event); |
7047 | 0 |
|
7048 | 0 | return NS_OK; |
7049 | 0 | } |
7050 | | |
7051 | | #if GTK_CHECK_VERSION(3,4,0) |
7052 | | nsresult |
7053 | | nsWindow::SynthesizeNativeTouchPoint(uint32_t aPointerId, |
7054 | | TouchPointerState aPointerState, |
7055 | | LayoutDeviceIntPoint aPoint, |
7056 | | double aPointerPressure, |
7057 | | uint32_t aPointerOrientation, |
7058 | | nsIObserver* aObserver) |
7059 | 0 | { |
7060 | 0 | AutoObserverNotifier notifier(aObserver, "touchpoint"); |
7061 | 0 |
|
7062 | 0 | if (!mGdkWindow) { |
7063 | 0 | return NS_OK; |
7064 | 0 | } |
7065 | 0 | |
7066 | 0 | GdkEvent event; |
7067 | 0 | memset(&event, 0, sizeof(GdkEvent)); |
7068 | 0 |
|
7069 | 0 | static std::map<uint32_t, GdkEventSequence*> sKnownPointers; |
7070 | 0 |
|
7071 | 0 | auto result = sKnownPointers.find(aPointerId); |
7072 | 0 | switch (aPointerState) { |
7073 | 0 | case TOUCH_CONTACT: |
7074 | 0 | if (result == sKnownPointers.end()) { |
7075 | 0 | // GdkEventSequence isn't a thing we can instantiate, and never gets |
7076 | 0 | // dereferenced in the gtk code. It's an opaque pointer, the only |
7077 | 0 | // requirement is that it be distinct from other instances of |
7078 | 0 | // GdkEventSequence*. |
7079 | 0 | event.touch.sequence = (GdkEventSequence*)((uintptr_t)aPointerId); |
7080 | 0 | sKnownPointers[aPointerId] = event.touch.sequence; |
7081 | 0 | event.type = GDK_TOUCH_BEGIN; |
7082 | 0 | } else { |
7083 | 0 | event.touch.sequence = result->second; |
7084 | 0 | event.type = GDK_TOUCH_UPDATE; |
7085 | 0 | } |
7086 | 0 | break; |
7087 | 0 | case TOUCH_REMOVE: |
7088 | 0 | event.type = GDK_TOUCH_END; |
7089 | 0 | if (result == sKnownPointers.end()) { |
7090 | 0 | NS_WARNING("Tried to synthesize touch-end for unknown pointer!"); |
7091 | 0 | return NS_ERROR_UNEXPECTED; |
7092 | 0 | } |
7093 | 0 | event.touch.sequence = result->second; |
7094 | 0 | sKnownPointers.erase(result); |
7095 | 0 | break; |
7096 | 0 | case TOUCH_CANCEL: |
7097 | 0 | event.type = GDK_TOUCH_CANCEL; |
7098 | 0 | if (result == sKnownPointers.end()) { |
7099 | 0 | NS_WARNING("Tried to synthesize touch-cancel for unknown pointer!"); |
7100 | 0 | return NS_ERROR_UNEXPECTED; |
7101 | 0 | } |
7102 | 0 | event.touch.sequence = result->second; |
7103 | 0 | sKnownPointers.erase(result); |
7104 | 0 | break; |
7105 | 0 | case TOUCH_HOVER: |
7106 | 0 | default: |
7107 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
7108 | 0 | } |
7109 | 0 | |
7110 | 0 | event.touch.window = mGdkWindow; |
7111 | 0 | event.touch.time = GDK_CURRENT_TIME; |
7112 | 0 |
|
7113 | 0 | GdkDisplay* display = gdk_window_get_display(mGdkWindow); |
7114 | 0 | GdkDeviceManager* device_manager = gdk_display_get_device_manager(display); |
7115 | 0 | event.touch.device = gdk_device_manager_get_client_pointer(device_manager); |
7116 | 0 |
|
7117 | 0 | event.touch.x_root = DevicePixelsToGdkCoordRoundDown(aPoint.x); |
7118 | 0 | event.touch.y_root = DevicePixelsToGdkCoordRoundDown(aPoint.y); |
7119 | 0 |
|
7120 | 0 | LayoutDeviceIntPoint pointInWindow = aPoint - WidgetToScreenOffset(); |
7121 | 0 | event.touch.x = DevicePixelsToGdkCoordRoundDown(pointInWindow.x); |
7122 | 0 | event.touch.y = DevicePixelsToGdkCoordRoundDown(pointInWindow.y); |
7123 | 0 |
|
7124 | 0 | gdk_event_put(&event); |
7125 | 0 |
|
7126 | 0 | return NS_OK; |
7127 | 0 | } |
7128 | | #endif |
7129 | | |
7130 | | nsWindow::CSDSupportLevel |
7131 | 0 | nsWindow::GetSystemCSDSupportLevel() { |
7132 | 0 | if (sCSDSupportLevel != CSD_SUPPORT_UNKNOWN) { |
7133 | 0 | return sCSDSupportLevel; |
7134 | 0 | } |
7135 | 0 | |
7136 | 0 | // Require GTK 3.10 for GtkHeaderBar support and compatible window manager. |
7137 | 0 | if (gtk_check_version(3, 10, 0) != nullptr) { |
7138 | 0 | sCSDSupportLevel = CSD_SUPPORT_NONE; |
7139 | 0 | return sCSDSupportLevel; |
7140 | 0 | } |
7141 | 0 | |
7142 | 0 | const char* currentDesktop = getenv("XDG_CURRENT_DESKTOP"); |
7143 | 0 | if (currentDesktop) { |
7144 | 0 | // GNOME Flashback (fallback) |
7145 | 0 | if (strstr(currentDesktop, "GNOME-Flashback:GNOME") != nullptr) { |
7146 | 0 | sCSDSupportLevel = CSD_SUPPORT_CLIENT; |
7147 | 0 | // gnome-shell |
7148 | 0 | } else if (strstr(currentDesktop, "GNOME") != nullptr) { |
7149 | 0 | sCSDSupportLevel = CSD_SUPPORT_SYSTEM; |
7150 | 0 | } else if (strstr(currentDesktop, "XFCE") != nullptr) { |
7151 | 0 | sCSDSupportLevel = CSD_SUPPORT_CLIENT; |
7152 | 0 | } else if (strstr(currentDesktop, "X-Cinnamon") != nullptr) { |
7153 | 0 | sCSDSupportLevel = CSD_SUPPORT_SYSTEM; |
7154 | 0 | // KDE Plasma |
7155 | 0 | } else if (strstr(currentDesktop, "KDE") != nullptr) { |
7156 | 0 | sCSDSupportLevel = CSD_SUPPORT_CLIENT; |
7157 | 0 | } else if (strstr(currentDesktop, "Enlightenment") != nullptr) { |
7158 | 0 | sCSDSupportLevel = CSD_SUPPORT_CLIENT; |
7159 | 0 | } else if (strstr(currentDesktop, "LXDE") != nullptr) { |
7160 | 0 | sCSDSupportLevel = CSD_SUPPORT_CLIENT; |
7161 | 0 | } else if (strstr(currentDesktop, "openbox") != nullptr) { |
7162 | 0 | sCSDSupportLevel = CSD_SUPPORT_CLIENT; |
7163 | 0 | } else if (strstr(currentDesktop, "i3") != nullptr) { |
7164 | 0 | sCSDSupportLevel = CSD_SUPPORT_NONE; |
7165 | 0 | } else if (strstr(currentDesktop, "MATE") != nullptr) { |
7166 | 0 | sCSDSupportLevel = CSD_SUPPORT_CLIENT; |
7167 | 0 | // Ubuntu Unity |
7168 | 0 | } else if (strstr(currentDesktop, "Unity") != nullptr) { |
7169 | 0 | sCSDSupportLevel = CSD_SUPPORT_SYSTEM; |
7170 | 0 | // Elementary OS |
7171 | 0 | } else if (strstr(currentDesktop, "Pantheon") != nullptr) { |
7172 | 0 | sCSDSupportLevel = CSD_SUPPORT_SYSTEM; |
7173 | 0 | } else if (strstr(currentDesktop, "LXQt") != nullptr) { |
7174 | 0 | sCSDSupportLevel = CSD_SUPPORT_SYSTEM; |
7175 | 0 | } else if (strstr(currentDesktop, "Deepin") != nullptr) { |
7176 | 0 | sCSDSupportLevel = CSD_SUPPORT_SYSTEM; |
7177 | 0 | } else { |
7178 | 0 | // Release or beta builds are not supposed to be broken |
7179 | 0 | // so disable titlebar rendering on untested/unknown systems. |
7180 | | #if defined(RELEASE_OR_BETA) |
7181 | | sCSDSupportLevel = CSD_SUPPORT_NONE; |
7182 | | #else |
7183 | | sCSDSupportLevel = CSD_SUPPORT_CLIENT; |
7184 | 0 | #endif |
7185 | 0 | } |
7186 | 0 | } else { |
7187 | 0 | sCSDSupportLevel = CSD_SUPPORT_NONE; |
7188 | 0 | } |
7189 | 0 |
|
7190 | 0 | // We don't support CSD_SUPPORT_SYSTEM on Wayland |
7191 | 0 | if (!GDK_IS_X11_DISPLAY(gdk_display_get_default()) && |
7192 | 0 | sCSDSupportLevel == CSD_SUPPORT_SYSTEM) { |
7193 | 0 | sCSDSupportLevel = CSD_SUPPORT_CLIENT; |
7194 | 0 | } |
7195 | 0 |
|
7196 | 0 | // GTK_CSD forces CSD mode - use also CSD because window manager |
7197 | 0 | // decorations does not work with CSD. |
7198 | 0 | // We check GTK_CSD as well as gtk_window_should_use_csd() does. |
7199 | 0 | if (sCSDSupportLevel == CSD_SUPPORT_SYSTEM) { |
7200 | 0 | const char* csdOverride = getenv("GTK_CSD"); |
7201 | 0 | if (csdOverride && g_strcmp0(csdOverride, "1") == 0) { |
7202 | 0 | sCSDSupportLevel = CSD_SUPPORT_CLIENT; |
7203 | 0 | } |
7204 | 0 | } |
7205 | 0 |
|
7206 | 0 | // Allow MOZ_GTK_TITLEBAR_DECORATION to override our heuristics |
7207 | 0 | const char* decorationOverride = getenv("MOZ_GTK_TITLEBAR_DECORATION"); |
7208 | 0 | if (decorationOverride) { |
7209 | 0 | if (strcmp(decorationOverride, "none") == 0) { |
7210 | 0 | sCSDSupportLevel = CSD_SUPPORT_NONE; |
7211 | 0 | } else if (strcmp(decorationOverride, "client") == 0) { |
7212 | 0 | sCSDSupportLevel = CSD_SUPPORT_CLIENT; |
7213 | 0 | } else if (strcmp(decorationOverride, "system") == 0) { |
7214 | 0 | sCSDSupportLevel = CSD_SUPPORT_SYSTEM; |
7215 | 0 | } |
7216 | 0 | } |
7217 | 0 |
|
7218 | 0 | return sCSDSupportLevel; |
7219 | 0 | } |
7220 | | |
7221 | | bool |
7222 | | nsWindow::TopLevelWindowUseARGBVisual() |
7223 | 0 | { |
7224 | 0 | static int useARGBVisual = -1; |
7225 | 0 | if (useARGBVisual != -1) { |
7226 | 0 | return useARGBVisual; |
7227 | 0 | } |
7228 | 0 | |
7229 | 0 | if (Preferences::HasUserValue("mozilla.widget.use-argb-visuals")) { |
7230 | 0 | useARGBVisual = |
7231 | 0 | Preferences::GetBool("mozilla.widget.use-argb-visuals", false); |
7232 | 0 | } else { |
7233 | 0 | const char* currentDesktop = getenv("XDG_CURRENT_DESKTOP"); |
7234 | 0 | useARGBVisual = |
7235 | 0 | (currentDesktop && |
7236 | 0 | GetSystemCSDSupportLevel() != CSD_SUPPORT_NONE); |
7237 | 0 |
|
7238 | 0 | if (useARGBVisual) { |
7239 | 0 | useARGBVisual = |
7240 | 0 | (strstr(currentDesktop, "GNOME-Flashback:GNOME") != nullptr || |
7241 | 0 | strstr(currentDesktop, "GNOME") != nullptr); |
7242 | 0 | } |
7243 | 0 | } |
7244 | 0 |
|
7245 | 0 | return useARGBVisual; |
7246 | 0 | } |
7247 | | |
7248 | | int32_t |
7249 | | nsWindow::RoundsWidgetCoordinatesTo() |
7250 | 0 | { |
7251 | 0 | return GdkScaleFactor(); |
7252 | 0 | } |
7253 | | |
7254 | | void nsWindow::GetCompositorWidgetInitData(mozilla::widget::CompositorWidgetInitData* aInitData) |
7255 | 0 | { |
7256 | 0 | // Make sure the window XID is propagated to X server, we can fail otherwise |
7257 | 0 | // in GPU process (Bug 1401634). |
7258 | 0 | if (mXDisplay && mXWindow != X11None) { |
7259 | 0 | XFlush(mXDisplay); |
7260 | 0 | } |
7261 | 0 |
|
7262 | 0 | *aInitData = mozilla::widget::GtkCompositorWidgetInitData( |
7263 | 0 | (mXWindow != X11None) ? mXWindow : (uintptr_t)nullptr, |
7264 | 0 | mXDisplay ? nsCString(XDisplayString(mXDisplay)) : nsCString(), |
7265 | 0 | mIsTransparent && !mHasAlphaVisual, |
7266 | 0 | GetClientSize()); |
7267 | 0 | } |
7268 | | |
7269 | | #ifdef MOZ_WAYLAND |
7270 | | wl_display* |
7271 | | nsWindow::GetWaylandDisplay() |
7272 | | { |
7273 | | // Available as of GTK 3.8+ |
7274 | | static auto sGdkWaylandDisplayGetWlDisplay = |
7275 | | (wl_display *(*)(GdkDisplay *)) |
7276 | | dlsym(RTLD_DEFAULT, "gdk_wayland_display_get_wl_display"); |
7277 | | |
7278 | | GdkDisplay* gdkDisplay = gdk_display_get_default(); |
7279 | | return mIsX11Display ? nullptr : |
7280 | | sGdkWaylandDisplayGetWlDisplay(gdkDisplay); |
7281 | | } |
7282 | | |
7283 | | wl_surface* |
7284 | | nsWindow::GetWaylandSurface() |
7285 | | { |
7286 | | if (mContainer) |
7287 | | return moz_container_get_wl_surface(MOZ_CONTAINER(mContainer)); |
7288 | | |
7289 | | NS_WARNING("nsWindow::GetWaylandSurfaces(): We don't have any mContainer for drawing!"); |
7290 | | return nullptr; |
7291 | | } |
7292 | | #endif |
7293 | | |
7294 | | #ifdef MOZ_X11 |
7295 | | /* XApp progress support currently works by setting a property |
7296 | | * on a window with this Atom name. A supporting window manager |
7297 | | * will notice this and pass it along to whatever handling has |
7298 | | * been implemented on that end (e.g. passing it on to a taskbar |
7299 | | * widget.) There is no issue if WM support is lacking, this is |
7300 | | * simply ignored in that case. |
7301 | | * |
7302 | | * See https://github.com/linuxmint/xapps/blob/master/libxapp/xapp-gtk-window.c |
7303 | | * for further details. |
7304 | | */ |
7305 | | |
7306 | 0 | #define PROGRESS_HINT "_NET_WM_XAPP_PROGRESS" |
7307 | | |
7308 | | static void |
7309 | | set_window_hint_cardinal (Window xid, |
7310 | | const gchar *atom_name, |
7311 | | gulong cardinal) |
7312 | 0 | { |
7313 | 0 | GdkDisplay *display; |
7314 | 0 |
|
7315 | 0 | display = gdk_display_get_default (); |
7316 | 0 |
|
7317 | 0 | if (cardinal > 0) |
7318 | 0 | { |
7319 | 0 | XChangeProperty (GDK_DISPLAY_XDISPLAY (display), |
7320 | 0 | xid, |
7321 | 0 | gdk_x11_get_xatom_by_name_for_display (display, atom_name), |
7322 | 0 | XA_CARDINAL, 32, |
7323 | 0 | PropModeReplace, |
7324 | 0 | (guchar *) &cardinal, 1); |
7325 | 0 | } |
7326 | 0 | else |
7327 | 0 | { |
7328 | 0 | XDeleteProperty (GDK_DISPLAY_XDISPLAY (display), |
7329 | 0 | xid, |
7330 | 0 | gdk_x11_get_xatom_by_name_for_display (display, atom_name)); |
7331 | 0 | } |
7332 | 0 | } |
7333 | | #endif // MOZ_X11 |
7334 | | |
7335 | | void |
7336 | | nsWindow::SetProgress(unsigned long progressPercent) |
7337 | 0 | { |
7338 | 0 | #ifdef MOZ_X11 |
7339 | 0 |
|
7340 | 0 | if (!mIsX11Display) { |
7341 | 0 | return; |
7342 | 0 | } |
7343 | 0 | |
7344 | 0 | if (!mShell) { |
7345 | 0 | return; |
7346 | 0 | } |
7347 | 0 | |
7348 | 0 | progressPercent = MIN(progressPercent, 100); |
7349 | 0 |
|
7350 | 0 | set_window_hint_cardinal(GDK_WINDOW_XID(gtk_widget_get_window(mShell)), |
7351 | 0 | PROGRESS_HINT, |
7352 | 0 | progressPercent); |
7353 | 0 | #endif // MOZ_X11 |
7354 | 0 | } |
7355 | | |
7356 | | #ifdef MOZ_X11 |
7357 | | void |
7358 | | nsWindow::SetCompositorHint(WindowComposeRequest aState) |
7359 | 0 | { |
7360 | 0 | if (mIsX11Display) { |
7361 | 0 | gulong value = aState; |
7362 | 0 | GdkAtom cardinal_atom = gdk_x11_xatom_to_atom(XA_CARDINAL); |
7363 | 0 | gdk_property_change(gtk_widget_get_window(mShell), |
7364 | 0 | gdk_atom_intern("_NET_WM_BYPASS_COMPOSITOR", FALSE), |
7365 | 0 | cardinal_atom, |
7366 | 0 | 32, // format |
7367 | 0 | GDK_PROP_MODE_REPLACE, |
7368 | 0 | (guchar*)&value, |
7369 | 0 | 1); |
7370 | 0 | } |
7371 | 0 | } |
7372 | | #endif |
7373 | | |
7374 | | nsresult |
7375 | | nsWindow::SetSystemFont(const nsCString& aFontName) |
7376 | 0 | { |
7377 | 0 | GtkSettings* settings = gtk_settings_get_default(); |
7378 | 0 | g_object_set(settings, "gtk-font-name", aFontName.get(), nullptr); |
7379 | 0 | return NS_OK; |
7380 | 0 | } |
7381 | | |
7382 | | nsresult |
7383 | | nsWindow::GetSystemFont(nsCString& aFontName) |
7384 | 0 | { |
7385 | 0 | GtkSettings* settings = gtk_settings_get_default(); |
7386 | 0 | gchar* fontName = nullptr; |
7387 | 0 | g_object_get(settings, |
7388 | 0 | "gtk-font-name", &fontName, |
7389 | 0 | nullptr); |
7390 | 0 | if (fontName) { |
7391 | 0 | aFontName.Assign(fontName); |
7392 | 0 | g_free(fontName); |
7393 | 0 | } |
7394 | 0 | return NS_OK; |
7395 | 0 | } |
7396 | | |
7397 | | already_AddRefed<nsIWidget> |
7398 | | nsIWidget::CreateTopLevelWindow() |
7399 | 0 | { |
7400 | 0 | nsCOMPtr<nsIWidget> window = new nsWindow(); |
7401 | 0 | return window.forget(); |
7402 | 0 | } |
7403 | | |
7404 | | already_AddRefed<nsIWidget> |
7405 | | nsIWidget::CreateChildWindow() |
7406 | 0 | { |
7407 | 0 | nsCOMPtr<nsIWidget> window = new nsWindow(); |
7408 | 0 | return window.forget(); |
7409 | 0 | } |
7410 | | |
7411 | | bool |
7412 | | nsWindow::GetTopLevelWindowActiveState(nsIFrame *aFrame) |
7413 | 0 | { |
7414 | 0 | // Used by window frame and button box rendering. We can end up in here in |
7415 | 0 | // the content process when rendering one of these moz styles freely in a |
7416 | 0 | // page. Fail in this case, there is no applicable window focus state. |
7417 | 0 | if (!XRE_IsParentProcess()) { |
7418 | 0 | return false; |
7419 | 0 | } |
7420 | 0 | // All headless windows are considered active so they are painted. |
7421 | 0 | if (gfxPlatform::IsHeadless()) { |
7422 | 0 | return true; |
7423 | 0 | } |
7424 | 0 | // Get the widget. nsIFrame's GetNearestWidget walks up the view chain |
7425 | 0 | // until it finds a real window. |
7426 | 0 | nsWindow* window = static_cast<nsWindow*>(aFrame->GetNearestWidget()); |
7427 | 0 | if (!window) { |
7428 | 0 | return false; |
7429 | 0 | } |
7430 | 0 | |
7431 | 0 | // Get our toplevel nsWindow. |
7432 | 0 | if (!window->mIsTopLevel) { |
7433 | 0 | GtkWidget *widget = window->GetMozContainerWidget(); |
7434 | 0 | if (!widget) { |
7435 | 0 | return false; |
7436 | 0 | } |
7437 | 0 | |
7438 | 0 | GtkWidget *toplevelWidget = gtk_widget_get_toplevel(widget); |
7439 | 0 | window = get_window_for_gtk_widget(toplevelWidget); |
7440 | 0 | if (!window) { |
7441 | 0 | return false; |
7442 | 0 | } |
7443 | 0 | } |
7444 | 0 | |
7445 | 0 | return (gFocusWindow == window); |
7446 | 0 | } |