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