thunderbird/firefox-wayland.patch
2019-02-05 15:49:40 +01:00

4429 lines
164 KiB
Diff

diff -up thunderbird-60.5.0/widget/gtk/GtkCompositorWidget.cpp.wayland thunderbird-60.5.0/widget/gtk/GtkCompositorWidget.cpp
--- thunderbird-60.5.0/widget/gtk/GtkCompositorWidget.cpp.wayland 2019-01-22 20:44:04.000000000 +0100
+++ thunderbird-60.5.0/widget/gtk/GtkCompositorWidget.cpp 2019-02-05 14:26:16.973316654 +0100
@@ -38,7 +38,9 @@ GtkCompositorWidget::GtkCompositorWidget
// Grab the window's visual and depth
XWindowAttributes windowAttrs;
- XGetWindowAttributes(mXDisplay, mXWindow, &windowAttrs);
+ if (!XGetWindowAttributes(mXDisplay, mXWindow, &windowAttrs)) {
+ NS_WARNING("GtkCompositorWidget(): XGetWindowAttributes() failed!");
+ }
Visual* visual = windowAttrs.visual;
int depth = windowAttrs.depth;
diff -up thunderbird-60.5.0/widget/gtk/moz.build.wayland thunderbird-60.5.0/widget/gtk/moz.build
--- thunderbird-60.5.0/widget/gtk/moz.build.wayland 2019-01-22 20:44:04.000000000 +0100
+++ thunderbird-60.5.0/widget/gtk/moz.build 2019-02-05 14:26:16.974316651 +0100
@@ -99,6 +99,7 @@ if CONFIG['MOZ_X11']:
if CONFIG['MOZ_WAYLAND']:
UNIFIED_SOURCES += [
'nsClipboardWayland.cpp',
+ 'nsWaylandDisplay.cpp',
'WindowSurfaceWayland.cpp',
]
@@ -123,6 +124,7 @@ include('/ipc/chromium/chromium-config.m
FINAL_LIBRARY = 'xul'
LOCAL_INCLUDES += [
+ '/layout/base',
'/layout/generic',
'/layout/xul',
'/other-licenses/atk-1.0',
diff -up thunderbird-60.5.0/widget/gtk/mozcontainer.cpp.wayland thunderbird-60.5.0/widget/gtk/mozcontainer.cpp
--- thunderbird-60.5.0/widget/gtk/mozcontainer.cpp.wayland 2019-01-22 20:44:04.000000000 +0100
+++ thunderbird-60.5.0/widget/gtk/mozcontainer.cpp 2019-02-05 15:09:24.116970135 +0100
@@ -1,4 +1,4 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:expandtab:shiftwidth=4:tabstop=4:
*/
/* This Source Code Form is subject to the terms of the Mozilla Public
@@ -7,9 +7,10 @@
#include "mozcontainer.h"
#include <gtk/gtk.h>
-#ifdef MOZ_WAYLAND
#include <gdk/gdkx.h>
-#include <gdk/gdkwayland.h>
+#ifdef MOZ_WAYLAND
+#include "nsWaylandDisplay.h"
+#include <wayland-egl.h>
#endif
#include <stdio.h>
#include <dlfcn.h>
@@ -19,12 +20,20 @@
#include "maiRedundantObjectFactory.h"
#endif
+#ifdef MOZ_WAYLAND
+using namespace mozilla;
+using namespace mozilla::widget;
+#endif
+
/* init methods */
static void moz_container_class_init(MozContainerClass *klass);
static void moz_container_init(MozContainer *container);
/* widget class methods */
static void moz_container_map(GtkWidget *widget);
+#if defined(MOZ_WAYLAND)
+static gboolean moz_container_map_wayland(GtkWidget *widget, GdkEventAny *event);
+#endif
static void moz_container_unmap(GtkWidget *widget);
static void moz_container_realize(GtkWidget *widget);
static void moz_container_size_allocate(GtkWidget *widget,
@@ -114,29 +123,6 @@ void moz_container_put(MozContainer *con
gtk_widget_set_parent(child_widget, GTK_WIDGET(container));
}
-void moz_container_move(MozContainer *container, GtkWidget *child_widget,
- gint x, gint y, gint width, gint height) {
- MozContainerChild *child;
- GtkAllocation new_allocation;
-
- child = moz_container_get_child(container, child_widget);
-
- child->x = x;
- child->y = y;
-
- new_allocation.x = x;
- new_allocation.y = y;
- new_allocation.width = width;
- new_allocation.height = height;
-
- /* printf("moz_container_move %p %p will allocate to %d %d %d %d\n",
- (void *)container, (void *)child_widget,
- new_allocation.x, new_allocation.y,
- new_allocation.width, new_allocation.height); */
-
- gtk_widget_size_allocate(child_widget, &new_allocation);
-}
-
/* static methods */
void moz_container_class_init(MozContainerClass *klass) {
@@ -146,6 +132,11 @@ void moz_container_class_init(MozContain
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
widget_class->map = moz_container_map;
+#if defined(MOZ_WAYLAND)
+ if (!GDK_IS_X11_DISPLAY(gdk_display_get_default())) {
+ widget_class->map_event = moz_container_map_wayland;
+ }
+#endif
widget_class->unmap = moz_container_unmap;
widget_class->realize = moz_container_realize;
widget_class->size_allocate = moz_container_size_allocate;
@@ -155,109 +146,81 @@ void moz_container_class_init(MozContain
container_class->add = moz_container_add;
}
-#if defined(MOZ_WAYLAND)
-static void registry_handle_global(void *data, struct wl_registry *registry,
- uint32_t name, const char *interface,
- uint32_t version) {
- MozContainer *container = MOZ_CONTAINER(data);
- if (strcmp(interface, "wl_subcompositor") == 0) {
- container->subcompositor = static_cast<wl_subcompositor *>(
- wl_registry_bind(registry, name, &wl_subcompositor_interface, 1));
- }
-}
-
-static void registry_handle_global_remove(void *data,
- struct wl_registry *registry,
- uint32_t name) {}
-
-static const struct wl_registry_listener registry_listener = {
- registry_handle_global, registry_handle_global_remove};
-#endif
-
void moz_container_init(MozContainer *container) {
gtk_widget_set_can_focus(GTK_WIDGET(container), TRUE);
gtk_container_set_resize_mode(GTK_CONTAINER(container), GTK_RESIZE_IMMEDIATE);
gtk_widget_set_redraw_on_allocate(GTK_WIDGET(container), FALSE);
#if defined(MOZ_WAYLAND)
- {
- GdkDisplay *gdk_display = gtk_widget_get_display(GTK_WIDGET(container));
- if (GDK_IS_WAYLAND_DISPLAY(gdk_display)) {
- // Available as of GTK 3.8+
- static auto sGdkWaylandDisplayGetWlDisplay =
- (wl_display * (*)(GdkDisplay *))
- dlsym(RTLD_DEFAULT, "gdk_wayland_display_get_wl_display");
-
- wl_display *display = sGdkWaylandDisplayGetWlDisplay(gdk_display);
- wl_registry *registry = wl_display_get_registry(display);
- wl_registry_add_listener(registry, &registry_listener, container);
- wl_display_dispatch(display);
- wl_display_roundtrip(display);
- }
- }
+ container->surface = nullptr;
+ container->subsurface = nullptr;
+ container->eglwindow = nullptr;
+ container->frame_callback_handler = nullptr;
+ // We can draw to x11 window any time.
+ container->ready_to_draw = GDK_IS_X11_DISPLAY(gdk_display_get_default());
+ container->surface_needs_clear = true;
#endif
}
#if defined(MOZ_WAYLAND)
-/* We want to draw to GdkWindow owned by mContainer from Compositor thread but
- * Gtk+ can be used in main thread only. So we create wayland wl_surface
- * and attach it as an overlay to GdkWindow.
- *
- * see gtk_clutter_embed_ensure_subsurface() at gtk-clutter-embed.c
- * for reference.
- */
-static gboolean moz_container_map_surface(MozContainer *container) {
- // Available as of GTK 3.8+
- static auto sGdkWaylandDisplayGetWlCompositor =
- (wl_compositor * (*)(GdkDisplay *))
- dlsym(RTLD_DEFAULT, "gdk_wayland_display_get_wl_compositor");
+static wl_surface *moz_container_get_gtk_container_surface(
+ MozContainer *container) {
static auto sGdkWaylandWindowGetWlSurface = (wl_surface * (*)(GdkWindow *))
dlsym(RTLD_DEFAULT, "gdk_wayland_window_get_wl_surface");
- GdkDisplay *display = gtk_widget_get_display(GTK_WIDGET(container));
- if (GDK_IS_X11_DISPLAY(display)) return false;
+ GdkWindow *window = gtk_widget_get_window(GTK_WIDGET(container));
+ return sGdkWaylandWindowGetWlSurface(window);
+}
- if (container->subsurface && container->surface) return true;
+static void frame_callback_handler(void *data, struct wl_callback *callback,
+ uint32_t time) {
+ MozContainer *container = MOZ_CONTAINER(data);
+ g_clear_pointer(&container->frame_callback_handler, wl_callback_destroy);
+ container->ready_to_draw = true;
+}
- if (!container->surface) {
- struct wl_compositor *compositor;
- compositor = sGdkWaylandDisplayGetWlCompositor(display);
- container->surface = wl_compositor_create_surface(compositor);
- }
+static const struct wl_callback_listener frame_listener = {
+ frame_callback_handler};
- if (!container->subsurface) {
- GdkWindow *window = gtk_widget_get_window(GTK_WIDGET(container));
- wl_surface *gtk_surface = sGdkWaylandWindowGetWlSurface(window);
- if (!gtk_surface) {
- // We requested the underlying wl_surface too early when container
- // is not realized yet. We'll try again before first rendering
- // to mContainer.
- return false;
- }
+static gboolean moz_container_map_wayland(GtkWidget *widget, GdkEventAny *event) {
+ MozContainer* container = MOZ_CONTAINER(widget);
- container->subsurface = wl_subcompositor_get_subsurface(
- container->subcompositor, container->surface, gtk_surface);
- gint x, y;
- gdk_window_get_position(window, &x, &y);
- wl_subsurface_set_position(container->subsurface, x, y);
- wl_subsurface_set_desync(container->subsurface);
+ if (container->ready_to_draw || container->frame_callback_handler) {
+ return FALSE;
+ }
- // Route input to parent wl_surface owned by Gtk+ so we get input
- // events from Gtk+.
- GdkDisplay *display = gtk_widget_get_display(GTK_WIDGET(container));
- wl_compositor *compositor = sGdkWaylandDisplayGetWlCompositor(display);
- wl_region *region = wl_compositor_create_region(compositor);
- wl_surface_set_input_region(container->surface, region);
- wl_region_destroy(region);
+ wl_surface *gtk_container_surface =
+ moz_container_get_gtk_container_surface(container);
+
+ if (gtk_container_surface) {
+ container->frame_callback_handler = wl_surface_frame(gtk_container_surface);
+ wl_callback_add_listener(container->frame_callback_handler, &frame_listener,
+ container);
}
- return true;
+
+ return FALSE;
}
-static void moz_container_unmap_surface(MozContainer *container) {
+static void moz_container_unmap_wayland(MozContainer *container) {
g_clear_pointer(&container->subsurface, wl_subsurface_destroy);
g_clear_pointer(&container->surface, wl_surface_destroy);
+ g_clear_pointer(&container->frame_callback_handler, wl_callback_destroy);
+
+ container->surface_needs_clear = true;
+ container->ready_to_draw = false;
}
+static gint moz_container_get_scale(MozContainer *container) {
+ static auto sGdkWindowGetScaleFactorPtr = (gint(*)(GdkWindow *))dlsym(
+ RTLD_DEFAULT, "gdk_window_get_scale_factor");
+
+ if (sGdkWindowGetScaleFactorPtr) {
+ GdkWindow *window = gtk_widget_get_window(GTK_WIDGET(container));
+ return (*sGdkWindowGetScaleFactorPtr)(window);
+ }
+
+ return 1;
+}
#endif
void moz_container_map(GtkWidget *widget) {
@@ -283,7 +246,9 @@ void moz_container_map(GtkWidget *widget
if (gtk_widget_get_has_window(widget)) {
gdk_window_show(gtk_widget_get_window(widget));
#if defined(MOZ_WAYLAND)
- moz_container_map_surface(MOZ_CONTAINER(widget));
+ if (!GDK_IS_X11_DISPLAY(gdk_display_get_default())) {
+ moz_container_map_wayland(widget, nullptr);
+ }
#endif
}
}
@@ -296,7 +261,9 @@ void moz_container_unmap(GtkWidget *widg
if (gtk_widget_get_has_window(widget)) {
gdk_window_hide(gtk_widget_get_window(widget));
#if defined(MOZ_WAYLAND)
- moz_container_unmap_surface(MOZ_CONTAINER(widget));
+ if (!GDK_IS_X11_DISPLAY(gdk_display_get_default())) {
+ moz_container_unmap_wayland(MOZ_CONTAINER(widget));
+ }
#endif
}
}
@@ -485,13 +452,62 @@ static void moz_container_add(GtkContain
#ifdef MOZ_WAYLAND
struct wl_surface *moz_container_get_wl_surface(MozContainer *container) {
- if (!container->subsurface || !container->surface) {
+ if (!container->surface) {
+ if (!container->ready_to_draw) {
+ return nullptr;
+ }
+ GdkDisplay *display = gtk_widget_get_display(GTK_WIDGET(container));
+
+ // Available as of GTK 3.8+
+ static auto sGdkWaylandDisplayGetWlCompositor =
+ (wl_compositor * (*)(GdkDisplay *))
+ dlsym(RTLD_DEFAULT, "gdk_wayland_display_get_wl_compositor");
+ struct wl_compositor *compositor =
+ sGdkWaylandDisplayGetWlCompositor(display);
+ container->surface = wl_compositor_create_surface(compositor);
+
+ nsWaylandDisplay *waylandDisplay = WaylandDisplayGet(display);
+ container->subsurface = wl_subcompositor_get_subsurface(
+ waylandDisplay->GetSubcompositor(), container->surface,
+ moz_container_get_gtk_container_surface(container));
+ WaylandDisplayRelease(waylandDisplay);
+
GdkWindow *window = gtk_widget_get_window(GTK_WIDGET(container));
- if (!gdk_window_is_visible(window)) return nullptr;
+ gint x, y;
+ gdk_window_get_position(window, &x, &y);
+ wl_subsurface_set_position(container->subsurface, x, y);
+ wl_subsurface_set_desync(container->subsurface);
- moz_container_map_surface(container);
+ // Route input to parent wl_surface owned by Gtk+ so we get input
+ // events from Gtk+.
+ wl_region *region = wl_compositor_create_region(compositor);
+ wl_surface_set_input_region(container->surface, region);
+ wl_region_destroy(region);
+
+ wl_surface_set_buffer_scale(container->surface,
+ moz_container_get_scale(container));
}
return container->surface;
}
+
+struct wl_egl_window *moz_container_get_wl_egl_window(MozContainer *container) {
+ if (!container->eglwindow) {
+ wl_surface *surface = moz_container_get_wl_surface(container);
+ if (!surface) {
+ return nullptr;
+ }
+ }
+ return container->eglwindow;
+}
+
+gboolean moz_container_has_wl_egl_window(MozContainer *container) {
+ return container->eglwindow ? true : false;
+}
+
+gboolean moz_container_surface_needs_clear(MozContainer *container) {
+ gboolean state = container->surface_needs_clear;
+ container->surface_needs_clear = false;
+ return state;
+}
#endif
diff -up thunderbird-60.5.0/widget/gtk/mozcontainer.h.wayland thunderbird-60.5.0/widget/gtk/mozcontainer.h
--- thunderbird-60.5.0/widget/gtk/mozcontainer.h.wayland 2019-01-22 20:44:03.000000000 +0100
+++ thunderbird-60.5.0/widget/gtk/mozcontainer.h 2019-02-05 14:26:16.974316651 +0100
@@ -1,4 +1,4 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:expandtab:shiftwidth=4:tabstop=4:
*/
/* This Source Code Form is subject to the terms of the Mozilla Public
@@ -63,7 +63,6 @@ typedef struct _MozContainerClass MozCon
* present in wayland-devel < 1.12
*/
#ifdef MOZ_WAYLAND
-struct wl_subcompositor;
struct wl_surface;
struct wl_subsurface;
#endif
@@ -73,9 +72,12 @@ struct _MozContainer {
GList *children;
#ifdef MOZ_WAYLAND
- struct wl_subcompositor *subcompositor;
struct wl_surface *surface;
struct wl_subsurface *subsurface;
+ struct wl_egl_window *eglwindow;
+ struct wl_callback *frame_callback_handler;
+ gboolean surface_needs_clear;
+ gboolean ready_to_draw;
#endif
};
@@ -87,11 +89,13 @@ GType moz_container_get_type(void);
GtkWidget *moz_container_new(void);
void moz_container_put(MozContainer *container, GtkWidget *child_widget, gint x,
gint y);
-void moz_container_move(MozContainer *container, GtkWidget *child_widget,
- gint x, gint y, gint width, gint height);
#ifdef MOZ_WAYLAND
struct wl_surface *moz_container_get_wl_surface(MozContainer *container);
+struct wl_egl_window *moz_container_get_wl_egl_window(MozContainer *container);
+
+gboolean moz_container_has_wl_egl_window(MozContainer *container);
+gboolean moz_container_surface_needs_clear(MozContainer *container);
#endif
#endif /* __MOZ_CONTAINER_H__ */
diff -up thunderbird-60.5.0/widget/gtk/mozgtk/mozgtk.c.wayland thunderbird-60.5.0/widget/gtk/mozgtk/mozgtk.c
--- thunderbird-60.5.0/widget/gtk/mozgtk/mozgtk.c.wayland 2019-01-22 20:44:04.000000000 +0100
+++ thunderbird-60.5.0/widget/gtk/mozgtk/mozgtk.c 2019-02-05 14:26:16.974316651 +0100
@@ -26,6 +26,7 @@ STUB(gdk_display_sync)
STUB(gdk_display_warp_pointer)
STUB(gdk_drag_context_get_actions)
STUB(gdk_drag_context_get_dest_window)
+STUB(gdk_drag_context_get_source_window)
STUB(gdk_drag_context_list_targets)
STUB(gdk_drag_status)
STUB(gdk_error_trap_pop)
@@ -55,6 +56,7 @@ STUB(gdk_pointer_grab)
STUB(gdk_pointer_ungrab)
STUB(gdk_property_change)
STUB(gdk_property_get)
+STUB(gdk_property_delete)
STUB(gdk_screen_get_default)
STUB(gdk_screen_get_display)
STUB(gdk_screen_get_font_options)
@@ -136,6 +138,8 @@ STUB(gdk_x11_get_xatom_by_name)
STUB(gdk_x11_get_xatom_by_name_for_display)
STUB(gdk_x11_lookup_xdisplay)
STUB(gdk_x11_screen_get_xscreen)
+STUB(gdk_x11_screen_get_screen_number)
+STUB(gdk_x11_screen_lookup_visual)
STUB(gdk_x11_screen_supports_net_wm_hint)
STUB(gdk_x11_visual_get_xvisual)
STUB(gdk_x11_window_foreign_new_for_display)
@@ -267,6 +271,7 @@ STUB(gtk_im_context_set_client_window)
STUB(gtk_im_context_set_cursor_location)
STUB(gtk_im_context_set_surrounding)
STUB(gtk_im_context_simple_new)
+STUB(gtk_im_multicontext_get_context_id)
STUB(gtk_im_multicontext_get_type)
STUB(gtk_im_multicontext_new)
STUB(gtk_info_bar_get_type)
@@ -411,6 +416,7 @@ STUB(gtk_table_get_type)
STUB(gtk_table_new)
STUB(gtk_target_list_add)
STUB(gtk_target_list_add_image_targets)
+STUB(gtk_target_list_add_text_targets)
STUB(gtk_target_list_new)
STUB(gtk_target_list_unref)
STUB(gtk_targets_include_image)
@@ -491,6 +497,7 @@ STUB(gtk_widget_unrealize)
STUB(gtk_window_deiconify)
STUB(gtk_window_fullscreen)
STUB(gtk_window_get_group)
+STUB(gtk_window_get_modal)
STUB(gtk_window_get_transient_for)
STUB(gtk_window_get_type)
STUB(gtk_window_get_type_hint)
diff -up thunderbird-60.5.0/widget/gtk/mozwayland/mozwayland.c.wayland thunderbird-60.5.0/widget/gtk/mozwayland/mozwayland.c
--- thunderbird-60.5.0/widget/gtk/mozwayland/mozwayland.c.wayland 2019-01-22 20:44:02.000000000 +0100
+++ thunderbird-60.5.0/widget/gtk/mozwayland/mozwayland.c 2019-02-05 14:26:16.974316651 +0100
@@ -1,14 +1,23 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:expandtab:shiftwidth=4:tabstop=4:
*/
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include <stdlib.h>
#include "mozilla/Types.h"
#include <gtk/gtk.h>
+#include <gtk/gtkx.h>
#include <gdk/gdkwayland.h>
+union wl_argument;
+
+/* Those strucures are just placeholders and will be replaced by
+ * real symbols from libwayland during run-time linking. We need to make
+ * them explicitly visible.
+ */
+#pragma GCC visibility push(default)
const struct wl_interface wl_buffer_interface;
const struct wl_interface wl_callback_interface;
const struct wl_interface wl_data_device_interface;
@@ -22,6 +31,7 @@ const struct wl_interface wl_seat_interf
const struct wl_interface wl_surface_interface;
const struct wl_interface wl_subsurface_interface;
const struct wl_interface wl_subcompositor_interface;
+#pragma GCC visibility pop
MOZ_EXPORT void wl_event_queue_destroy(struct wl_event_queue *queue) {}
@@ -75,6 +85,10 @@ MOZ_EXPORT const void *wl_proxy_get_list
return NULL;
}
+typedef int (*wl_dispatcher_func_t)(const void *, void *, uint32_t,
+ const struct wl_message *,
+ union wl_argument *);
+
MOZ_EXPORT int wl_proxy_add_dispatcher(struct wl_proxy *proxy,
wl_dispatcher_func_t dispatcher_func,
const void *dispatcher_data,
@@ -160,3 +174,13 @@ MOZ_EXPORT void wl_display_cancel_read(s
MOZ_EXPORT int wl_display_read_events(struct wl_display *display) { return -1; }
MOZ_EXPORT void wl_log_set_handler_client(wl_log_func_t handler) {}
+
+MOZ_EXPORT struct wl_egl_window *wl_egl_window_create(
+ struct wl_surface *surface, int width, int height) {
+ return NULL;
+}
+
+MOZ_EXPORT void wl_egl_window_destroy(struct wl_egl_window *egl_window) {}
+
+MOZ_EXPORT void wl_egl_window_resize(struct wl_egl_window *egl_window,
+ int width, int height, int dx, int dy) {}
diff -up thunderbird-60.5.0/widget/gtk/mozwayland/mozwayland.h.wayland thunderbird-60.5.0/widget/gtk/mozwayland/mozwayland.h
--- thunderbird-60.5.0/widget/gtk/mozwayland/mozwayland.h.wayland 2019-02-05 14:26:16.975316648 +0100
+++ thunderbird-60.5.0/widget/gtk/mozwayland/mozwayland.h 2019-02-05 14:26:16.975316648 +0100
@@ -0,0 +1,115 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:expandtab:shiftwidth=4:tabstop=4:
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* Wayland compatibility header, it makes Firefox build with
+ wayland-1.2 and Gtk+ 3.10.
+*/
+
+#ifndef __MozWayland_h_
+#define __MozWayland_h_
+
+#include "mozilla/Types.h"
+#include <gtk/gtk.h>
+#include <gdk/gdkwayland.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+MOZ_EXPORT int wl_display_roundtrip_queue(struct wl_display *display,
+ struct wl_event_queue *queue);
+MOZ_EXPORT uint32_t wl_proxy_get_version(struct wl_proxy *proxy);
+MOZ_EXPORT struct wl_proxy *wl_proxy_marshal_constructor(
+ struct wl_proxy *proxy, uint32_t opcode,
+ const struct wl_interface *interface, ...);
+
+/* We need implement some missing functions from wayland-client-protocol.h
+ */
+#ifndef WL_DATA_DEVICE_MANAGER_DND_ACTION_ENUM
+enum wl_data_device_manager_dnd_action {
+ WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE = 0,
+ WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY = 1,
+ WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE = 2,
+ WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK = 4
+};
+#endif
+
+#ifndef WL_DATA_OFFER_SET_ACTIONS
+#define WL_DATA_OFFER_SET_ACTIONS 4
+
+struct moz_wl_data_offer_listener {
+ void (*offer)(void *data, struct wl_data_offer *wl_data_offer,
+ const char *mime_type);
+ void (*source_actions)(void *data, struct wl_data_offer *wl_data_offer,
+ uint32_t source_actions);
+ void (*action)(void *data, struct wl_data_offer *wl_data_offer,
+ uint32_t dnd_action);
+};
+
+static inline void wl_data_offer_set_actions(
+ struct wl_data_offer *wl_data_offer, uint32_t dnd_actions,
+ uint32_t preferred_action) {
+ wl_proxy_marshal((struct wl_proxy *)wl_data_offer, WL_DATA_OFFER_SET_ACTIONS,
+ dnd_actions, preferred_action);
+}
+#else
+typedef struct wl_data_offer_listener moz_wl_data_offer_listener;
+#endif
+
+#ifndef WL_SUBCOMPOSITOR_GET_SUBSURFACE
+#define WL_SUBCOMPOSITOR_GET_SUBSURFACE 1
+struct wl_subcompositor;
+
+// Emulate what mozilla header wrapper does - make the
+// wl_subcompositor_interface always visible.
+#pragma GCC visibility push(default)
+extern const struct wl_interface wl_subsurface_interface;
+extern const struct wl_interface wl_subcompositor_interface;
+#pragma GCC visibility pop
+
+#define WL_SUBSURFACE_DESTROY 0
+#define WL_SUBSURFACE_SET_POSITION 1
+#define WL_SUBSURFACE_PLACE_ABOVE 2
+#define WL_SUBSURFACE_PLACE_BELOW 3
+#define WL_SUBSURFACE_SET_SYNC 4
+#define WL_SUBSURFACE_SET_DESYNC 5
+
+static inline struct wl_subsurface *wl_subcompositor_get_subsurface(
+ struct wl_subcompositor *wl_subcompositor, struct wl_surface *surface,
+ struct wl_surface *parent) {
+ struct wl_proxy *id;
+
+ id = wl_proxy_marshal_constructor(
+ (struct wl_proxy *)wl_subcompositor, WL_SUBCOMPOSITOR_GET_SUBSURFACE,
+ &wl_subsurface_interface, NULL, surface, parent);
+
+ return (struct wl_subsurface *)id;
+}
+
+static inline void wl_subsurface_set_position(
+ struct wl_subsurface *wl_subsurface, int32_t x, int32_t y) {
+ wl_proxy_marshal((struct wl_proxy *)wl_subsurface, WL_SUBSURFACE_SET_POSITION,
+ x, y);
+}
+
+static inline void wl_subsurface_set_desync(
+ struct wl_subsurface *wl_subsurface) {
+ wl_proxy_marshal((struct wl_proxy *)wl_subsurface, WL_SUBSURFACE_SET_DESYNC);
+}
+
+static inline void wl_subsurface_destroy(struct wl_subsurface *wl_subsurface) {
+ wl_proxy_marshal((struct wl_proxy *)wl_subsurface, WL_SUBSURFACE_DESTROY);
+
+ wl_proxy_destroy((struct wl_proxy *)wl_subsurface);
+}
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __MozWayland_h_ */
diff -up thunderbird-60.5.0/widget/gtk/nsClipboard.cpp.wayland thunderbird-60.5.0/widget/gtk/nsClipboard.cpp
--- thunderbird-60.5.0/widget/gtk/nsClipboard.cpp.wayland 2019-01-22 20:44:03.000000000 +0100
+++ thunderbird-60.5.0/widget/gtk/nsClipboard.cpp 2019-02-05 14:26:16.975316648 +0100
@@ -72,7 +72,7 @@ nsClipboard::~nsClipboard() {
}
}
-NS_IMPL_ISUPPORTS(nsClipboard, nsIClipboard)
+NS_IMPL_ISUPPORTS(nsClipboard, nsIClipboard, nsIObserver)
nsresult nsClipboard::Init(void) {
GdkDisplay *display = gdk_display_get_default();
diff -up thunderbird-60.5.0/widget/gtk/nsClipboardWayland.cpp.wayland thunderbird-60.5.0/widget/gtk/nsClipboardWayland.cpp
--- thunderbird-60.5.0/widget/gtk/nsClipboardWayland.cpp.wayland 2019-01-22 20:44:04.000000000 +0100
+++ thunderbird-60.5.0/widget/gtk/nsClipboardWayland.cpp 2019-02-05 14:26:16.975316648 +0100
@@ -1,4 +1,4 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:expandtab:shiftwidth=4:tabstop=4:
*/
/* This Source Code Form is subject to the terms of the Mozilla Public
@@ -20,9 +20,11 @@
#include "nsImageToPixbuf.h"
#include "nsStringStream.h"
#include "nsIObserverService.h"
-#include "mozilla/Services.h"
#include "mozilla/RefPtr.h"
#include "mozilla/TimeStamp.h"
+#include "nsDragService.h"
+#include "mozwayland/mozwayland.h"
+#include "nsWaylandDisplay.h"
#include "imgIContainer.h"
@@ -31,15 +33,43 @@
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
-#include <gtk/gtk.h>
-#include <gdk/gdkwayland.h>
#include <errno.h>
-#include "wayland/gtk-primary-selection-client-protocol.h"
-
const char *nsRetrievalContextWayland::sTextMimeTypes[TEXT_MIME_TYPES_NUM] = {
"text/plain;charset=utf-8", "UTF8_STRING", "COMPOUND_TEXT"};
+static inline GdkDragAction wl_to_gdk_actions(uint32_t dnd_actions) {
+ GdkDragAction actions = GdkDragAction(0);
+
+ if (dnd_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY)
+ actions = GdkDragAction(actions | GDK_ACTION_COPY);
+ if (dnd_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE)
+ actions = GdkDragAction(actions | GDK_ACTION_MOVE);
+
+ return actions;
+}
+
+static inline uint32_t gdk_to_wl_actions(GdkDragAction action) {
+ uint32_t dnd_actions = 0;
+
+ if (action & (GDK_ACTION_COPY | GDK_ACTION_LINK | GDK_ACTION_PRIVATE))
+ dnd_actions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY;
+ if (action & GDK_ACTION_MOVE)
+ dnd_actions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE;
+
+ return dnd_actions;
+}
+
+static GtkWidget *get_gtk_widget_for_wl_surface(struct wl_surface *surface) {
+ GdkWindow *gdkParentWindow =
+ static_cast<GdkWindow *>(wl_surface_get_user_data(surface));
+
+ gpointer user_data = nullptr;
+ gdk_window_get_user_data(gdkParentWindow, &user_data);
+
+ return GTK_WIDGET(user_data);
+}
+
void DataOffer::AddMIMEType(const char *aMimeType) {
GdkAtom atom = gdk_atom_intern(aMimeType, FALSE);
mTargetMIMETypes.AppendElement(atom);
@@ -98,7 +128,7 @@ char *DataOffer::GetData(wl_display *aDi
GIOChannel *channel = g_io_channel_unix_new(pipe_fd[0]);
GError *error = nullptr;
- char *clipboardData;
+ char *clipboardData = nullptr;
g_io_channel_set_encoding(channel, nullptr, &error);
if (!error) {
@@ -138,31 +168,92 @@ bool WaylandDataOffer::RequestDataTransf
return false;
}
+void WaylandDataOffer::DragOfferAccept(const char *aMimeType, uint32_t aTime) {
+ wl_data_offer_accept(mWaylandDataOffer, aTime, aMimeType);
+}
+
+/* We follow logic of gdk_wayland_drag_context_commit_status()/gdkdnd-wayland.c
+ * here.
+ */
+void WaylandDataOffer::SetDragStatus(GdkDragAction aAction, uint32_t aTime) {
+ uint32_t dnd_actions = gdk_to_wl_actions(aAction);
+ uint32_t all_actions = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY |
+ WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE;
+
+ wl_data_offer_set_actions(mWaylandDataOffer, all_actions, dnd_actions);
+
+ /* Workaround Wayland D&D architecture here. To get the data_device_drop()
+ signal (which routes to nsDragService::GetData() call) we need to
+ accept at least one mime type before data_device_leave().
+
+ Real wl_data_offer_accept() for actualy requested data mime type is
+ called from nsDragService::GetData().
+ */
+ if (mTargetMIMETypes[0]) {
+ wl_data_offer_accept(mWaylandDataOffer, aTime,
+ gdk_atom_name(mTargetMIMETypes[0]));
+ }
+}
+
+void WaylandDataOffer::SetSelectedDragAction(uint32_t aWaylandAction) {
+ mSelectedDragAction = aWaylandAction;
+}
+
+GdkDragAction WaylandDataOffer::GetSelectedDragAction() {
+ return wl_to_gdk_actions(mSelectedDragAction);
+}
+
+void WaylandDataOffer::SetAvailableDragActions(uint32_t aWaylandActions) {
+ mAvailableDragAction = aWaylandActions;
+}
+
+GdkDragAction WaylandDataOffer::GetAvailableDragActions() {
+ return wl_to_gdk_actions(mAvailableDragAction);
+}
+
static void data_offer_offer(void *data, struct wl_data_offer *wl_data_offer,
const char *type) {
auto *offer = static_cast<DataOffer *>(data);
offer->AddMIMEType(type);
}
+/* Advertise all available drag and drop actions from source.
+ * We don't use that but follow gdk_wayland_drag_context_commit_status()
+ * from gdkdnd-wayland.c here.
+ */
static void data_offer_source_actions(void *data,
struct wl_data_offer *wl_data_offer,
- uint32_t source_actions) {}
+ uint32_t source_actions) {
+ auto *offer = static_cast<WaylandDataOffer *>(data);
+ offer->SetAvailableDragActions(source_actions);
+}
+/* Advertise recently selected drag and drop action by compositor, based
+ * on source actions and user choice (key modifiers, etc.).
+ */
static void data_offer_action(void *data, struct wl_data_offer *wl_data_offer,
- uint32_t dnd_action) {}
+ uint32_t dnd_action) {
+ auto *offer = static_cast<WaylandDataOffer *>(data);
+ offer->SetSelectedDragAction(dnd_action);
+}
/* wl_data_offer callback description:
*
* data_offer_offer - Is called for each MIME type available at wl_data_offer.
- * data_offer_source_actions - Exposes all available D&D actions.
- * data_offer_action - Expose one actually selected D&D action.
+ * data_offer_source_actions - This event indicates the actions offered by
+ * the data source.
+ * data_offer_action - This event indicates the action selected by
+ * the compositor after matching the source/destination
+ * side actions.
*/
-static const struct wl_data_offer_listener data_offer_listener = {
+static const moz_wl_data_offer_listener data_offer_listener = {
data_offer_offer, data_offer_source_actions, data_offer_action};
WaylandDataOffer::WaylandDataOffer(wl_data_offer *aWaylandDataOffer)
: mWaylandDataOffer(aWaylandDataOffer) {
- wl_data_offer_add_listener(mWaylandDataOffer, &data_offer_listener, this);
+ wl_data_offer_add_listener(
+ mWaylandDataOffer, (struct wl_data_offer_listener *)&data_offer_listener,
+ this);
}
WaylandDataOffer::~WaylandDataOffer(void) {
@@ -207,20 +298,93 @@ PrimaryDataOffer::~PrimaryDataOffer(void
}
}
-void nsRetrievalContextWayland::RegisterDataOffer(
+NS_IMPL_ISUPPORTS(nsWaylandDragContext, nsISupports);
+
+nsWaylandDragContext::nsWaylandDragContext(WaylandDataOffer *aDataOffer,
+ wl_display *aDisplay)
+ : mDataOffer(aDataOffer),
+ mDisplay(aDisplay),
+ mTime(0),
+ mGtkWidget(nullptr),
+ mX(0),
+ mY(0) {}
+
+void nsWaylandDragContext::DropDataEnter(GtkWidget *aGtkWidget, uint32_t aTime,
+ nscoord aX, nscoord aY) {
+ mTime = aTime;
+ mGtkWidget = aGtkWidget;
+ mX = aX;
+ mY = aY;
+}
+
+void nsWaylandDragContext::DropMotion(uint32_t aTime, nscoord aX, nscoord aY) {
+ mTime = aTime;
+ mX = aX;
+ mY = aY;
+}
+
+void nsWaylandDragContext::GetLastDropInfo(uint32_t *aTime, nscoord *aX,
+ nscoord *aY) {
+ *aTime = mTime;
+ *aX = mX;
+ *aY = mY;
+}
+
+void nsWaylandDragContext::SetDragStatus(GdkDragAction aAction) {
+ mDataOffer->SetDragStatus(aAction, mTime);
+}
+
+GdkDragAction nsWaylandDragContext::GetSelectedDragAction() {
+ GdkDragAction gdkAction = mDataOffer->GetSelectedDragAction();
+
+ // We emulate gdk_drag_context_get_actions() here.
+ if (!gdkAction) {
+ gdkAction = mDataOffer->GetAvailableDragActions();
+ }
+
+ return gdkAction;
+}
+
+GList *nsWaylandDragContext::GetTargets() {
+ int targetNums;
+ GdkAtom *atoms = mDataOffer->GetTargets(&targetNums);
+
+ GList *targetList = nullptr;
+ for (int i = 0; i < targetNums; i++) {
+ targetList = g_list_append(targetList, GDK_ATOM_TO_POINTER(atoms[i]));
+ }
+
+ return targetList;
+}
+
+char *nsWaylandDragContext::GetData(const char *aMimeType,
+ uint32_t *aContentLength) {
+ mDataOffer->DragOfferAccept(aMimeType, mTime);
+ return mDataOffer->GetData(mDisplay, aMimeType, aContentLength);
+}
+
+void nsRetrievalContextWayland::RegisterNewDataOffer(
wl_data_offer *aWaylandDataOffer) {
DataOffer *dataOffer = static_cast<DataOffer *>(
g_hash_table_lookup(mActiveOffers, aWaylandDataOffer));
+ MOZ_ASSERT(
+ dataOffer == nullptr,
+ "Registered WaylandDataOffer already exists. Wayland protocol error?");
+
if (!dataOffer) {
dataOffer = new WaylandDataOffer(aWaylandDataOffer);
g_hash_table_insert(mActiveOffers, aWaylandDataOffer, dataOffer);
}
}
-void nsRetrievalContextWayland::RegisterDataOffer(
+void nsRetrievalContextWayland::RegisterNewDataOffer(
gtk_primary_selection_offer *aPrimaryDataOffer) {
DataOffer *dataOffer = static_cast<DataOffer *>(
g_hash_table_lookup(mActiveOffers, aPrimaryDataOffer));
+ MOZ_ASSERT(
+ dataOffer == nullptr,
+ "Registered PrimaryDataOffer already exists. Wayland protocol error?");
+
if (!dataOffer) {
dataOffer = new PrimaryDataOffer(aPrimaryDataOffer);
g_hash_table_insert(mActiveOffers, aPrimaryDataOffer, dataOffer);
@@ -229,21 +393,30 @@ void nsRetrievalContextWayland::Register
void nsRetrievalContextWayland::SetClipboardDataOffer(
wl_data_offer *aWaylandDataOffer) {
- DataOffer *dataOffer = static_cast<DataOffer *>(
- g_hash_table_lookup(mActiveOffers, aWaylandDataOffer));
- NS_ASSERTION(dataOffer, "We're missing clipboard data offer!");
- if (dataOffer) {
- g_hash_table_remove(mActiveOffers, aWaylandDataOffer);
- mClipboardOffer = dataOffer;
+ // Delete existing clipboard data offer
+ mClipboardOffer = nullptr;
+
+ // null aWaylandDataOffer indicates that our clipboard content
+ // is no longer valid and should be release.
+ if (aWaylandDataOffer != nullptr) {
+ DataOffer *dataOffer = static_cast<DataOffer *>(
+ g_hash_table_lookup(mActiveOffers, aWaylandDataOffer));
+ NS_ASSERTION(dataOffer, "We're missing stored clipboard data offer!");
+ if (dataOffer) {
+ g_hash_table_remove(mActiveOffers, aWaylandDataOffer);
+ mClipboardOffer = dataOffer;
+ }
}
}
void nsRetrievalContextWayland::SetPrimaryDataOffer(
gtk_primary_selection_offer *aPrimaryDataOffer) {
- if (aPrimaryDataOffer == nullptr) {
- // Release any primary offer we have.
- mPrimaryOffer = nullptr;
- } else {
+ // Release any primary offer we have.
+ mPrimaryOffer = nullptr;
+
+ // aPrimaryDataOffer can be null which means we lost
+ // the mouse selection.
+ if (aPrimaryDataOffer) {
DataOffer *dataOffer = static_cast<DataOffer *>(
g_hash_table_lookup(mActiveOffers, aPrimaryDataOffer));
NS_ASSERTION(dataOffer, "We're missing primary data offer!");
@@ -254,9 +427,26 @@ void nsRetrievalContextWayland::SetPrima
}
}
-void nsRetrievalContextWayland::ClearDataOffers(void) {
- if (mClipboardOffer) mClipboardOffer = nullptr;
- if (mPrimaryOffer) mPrimaryOffer = nullptr;
+void nsRetrievalContextWayland::AddDragAndDropDataOffer(
+ wl_data_offer *aDropDataOffer) {
+ // Remove any existing D&D contexts.
+ mDragContext = nullptr;
+
+ WaylandDataOffer *dataOffer = static_cast<WaylandDataOffer *>(
+ g_hash_table_lookup(mActiveOffers, aDropDataOffer));
+ NS_ASSERTION(dataOffer, "We're missing drag and drop data offer!");
+ if (dataOffer) {
+ g_hash_table_remove(mActiveOffers, aDropDataOffer);
+ mDragContext = new nsWaylandDragContext(dataOffer, mDisplay->GetDisplay());
+ }
+}
+
+nsWaylandDragContext *nsRetrievalContextWayland::GetDragContext(void) {
+ return mDragContext;
+}
+
+void nsRetrievalContextWayland::ClearDragAndDropDataOffer(void) {
+ mDragContext = nullptr;
}
// We have a new fresh data content.
@@ -266,7 +456,7 @@ static void data_device_data_offer(void
struct wl_data_offer *offer) {
nsRetrievalContextWayland *context =
static_cast<nsRetrievalContextWayland *>(data);
- context->RegisterDataOffer(offer);
+ context->RegisterNewDataOffer(offer);
}
// The new fresh data content is clipboard.
@@ -281,15 +471,65 @@ static void data_device_selection(void *
// The new fresh wayland data content is drag and drop.
static void data_device_enter(void *data, struct wl_data_device *data_device,
uint32_t time, struct wl_surface *surface,
- int32_t x, int32_t y,
- struct wl_data_offer *offer) {}
+ int32_t x_fixed, int32_t y_fixed,
+ struct wl_data_offer *offer) {
+ nsRetrievalContextWayland *context =
+ static_cast<nsRetrievalContextWayland *>(data);
+ context->AddDragAndDropDataOffer(offer);
+
+ nsWaylandDragContext *dragContext = context->GetDragContext();
+
+ GtkWidget *gtkWidget = get_gtk_widget_for_wl_surface(surface);
+ if (!gtkWidget) {
+ NS_WARNING("DragAndDrop: Unable to get GtkWidget for wl_surface!");
+ return;
+ }
+
+ LOGDRAG(("nsWindow data_device_enter for GtkWidget %p\n", (void *)gtkWidget));
+
+ dragContext->DropDataEnter(gtkWidget, time, wl_fixed_to_int(x_fixed),
+ wl_fixed_to_int(y_fixed));
+}
+
+static void data_device_leave(void *data, struct wl_data_device *data_device) {
+ nsRetrievalContextWayland *context =
+ static_cast<nsRetrievalContextWayland *>(data);
-static void data_device_leave(void *data, struct wl_data_device *data_device) {}
+ nsWaylandDragContext *dropContext = context->GetDragContext();
+ WindowDragLeaveHandler(dropContext->GetWidget());
+
+ context->ClearDragAndDropDataOffer();
+}
static void data_device_motion(void *data, struct wl_data_device *data_device,
- uint32_t time, int32_t x, int32_t y) {}
+ uint32_t time, int32_t x_fixed,
+ int32_t y_fixed) {
+ nsRetrievalContextWayland *context =
+ static_cast<nsRetrievalContextWayland *>(data);
+
+ nsWaylandDragContext *dropContext = context->GetDragContext();
-static void data_device_drop(void *data, struct wl_data_device *data_device) {}
+ nscoord x = wl_fixed_to_int(x_fixed);
+ nscoord y = wl_fixed_to_int(y_fixed);
+ dropContext->DropMotion(time, x, y);
+
+ WindowDragMotionHandler(dropContext->GetWidget(), nullptr, dropContext, x, y,
+ time);
+}
+
+static void data_device_drop(void *data, struct wl_data_device *data_device) {
+ nsRetrievalContextWayland *context =
+ static_cast<nsRetrievalContextWayland *>(data);
+
+ nsWaylandDragContext *dropContext = context->GetDragContext();
+
+ uint32_t time;
+ nscoord x, y;
+ dropContext->GetLastDropInfo(&time, &x, &y);
+
+ WindowDragDropHandler(dropContext->GetWidget(), nullptr, dropContext, x, y,
+ time);
+}
/* wl_data_device callback description:
*
@@ -323,7 +563,7 @@ static void primary_selection_data_offer
// create and add listener
nsRetrievalContextWayland *context =
static_cast<nsRetrievalContextWayland *>(data);
- context->RegisterDataOffer(gtk_primary_offer);
+ context->RegisterNewDataOffer(gtk_primary_offer);
}
static void primary_selection_selection(
@@ -335,6 +575,19 @@ static void primary_selection_selection(
context->SetPrimaryDataOffer(gtk_primary_offer);
}
+/* gtk_primary_selection_device callback description:
+ *
+ * primary_selection_data_offer - It's called when there's a new
+ * gtk_primary_selection_offer available. We need to
+ * attach gtk_primary_selection_offer_listener to it
+ * to get available MIME types.
+ *
+ * primary_selection_selection - It's called when the new
+ * gtk_primary_selection_offer is a primary selection
+ * content. It can be also called with
+ * gtk_primary_selection_offer = null which means
+ * there's no primary selection.
+ */
static const struct gtk_primary_selection_device_listener
primary_selection_device_listener = {
primary_selection_data_offer,
@@ -342,149 +595,29 @@ static const struct gtk_primary_selectio
};
bool nsRetrievalContextWayland::HasSelectionSupport(void) {
- return mPrimarySelectionDataDeviceManager != nullptr;
-}
-
-static void keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard,
- uint32_t format, int fd, uint32_t size) {}
-
-static void keyboard_handle_enter(void *data, struct wl_keyboard *keyboard,
- uint32_t serial, struct wl_surface *surface,
- struct wl_array *keys) {}
-
-static void keyboard_handle_leave(void *data, struct wl_keyboard *keyboard,
- uint32_t serial, struct wl_surface *surface) {
- // We lost focus so our clipboard data are outdated
- nsRetrievalContextWayland *context =
- static_cast<nsRetrievalContextWayland *>(data);
-
- context->ClearDataOffers();
+ return mDisplay->GetPrimarySelectionDeviceManager() != nullptr;
}
-static void keyboard_handle_key(void *data, struct wl_keyboard *keyboard,
- uint32_t serial, uint32_t time, uint32_t key,
- uint32_t state) {}
-
-static void keyboard_handle_modifiers(void *data, struct wl_keyboard *keyboard,
- uint32_t serial, uint32_t mods_depressed,
- uint32_t mods_latched,
- uint32_t mods_locked, uint32_t group) {}
-
-static const struct wl_keyboard_listener keyboard_listener = {
- keyboard_handle_keymap, keyboard_handle_enter, keyboard_handle_leave,
- keyboard_handle_key, keyboard_handle_modifiers,
-};
-
-void nsRetrievalContextWayland::ConfigureKeyboard(wl_seat_capability caps) {
- // ConfigureKeyboard() is called when wl_seat configuration is changed.
- // We look for the keyboard only, get it when is't available and release it
- // when it's lost (we don't have focus for instance).
- if (caps & WL_SEAT_CAPABILITY_KEYBOARD) {
- mKeyboard = wl_seat_get_keyboard(mSeat);
- wl_keyboard_add_listener(mKeyboard, &keyboard_listener, this);
- } else if (mKeyboard && !(caps & WL_SEAT_CAPABILITY_KEYBOARD)) {
- wl_keyboard_destroy(mKeyboard);
- mKeyboard = nullptr;
- }
-}
-
-static void seat_handle_capabilities(void *data, struct wl_seat *seat,
- unsigned int caps) {
- nsRetrievalContextWayland *context =
- static_cast<nsRetrievalContextWayland *>(data);
- context->ConfigureKeyboard((wl_seat_capability)caps);
-}
-
-static const struct wl_seat_listener seat_listener = {
- seat_handle_capabilities,
-};
-
-void nsRetrievalContextWayland::InitDataDeviceManager(wl_registry *registry,
- uint32_t id,
- uint32_t version) {
- int data_device_manager_version = MIN(version, 3);
- mDataDeviceManager = (wl_data_device_manager *)wl_registry_bind(
- registry, id, &wl_data_device_manager_interface,
- data_device_manager_version);
-}
-
-void nsRetrievalContextWayland::InitPrimarySelectionDataDeviceManager(
- wl_registry *registry, uint32_t id) {
- mPrimarySelectionDataDeviceManager =
- (gtk_primary_selection_device_manager *)wl_registry_bind(
- registry, id, &gtk_primary_selection_device_manager_interface, 1);
-}
-
-void nsRetrievalContextWayland::InitSeat(wl_registry *registry, uint32_t id,
- uint32_t version, void *data) {
- mSeat = (wl_seat *)wl_registry_bind(registry, id, &wl_seat_interface, 1);
- wl_seat_add_listener(mSeat, &seat_listener, data);
-}
-
-static void gdk_registry_handle_global(void *data, struct wl_registry *registry,
- uint32_t id, const char *interface,
- uint32_t version) {
- nsRetrievalContextWayland *context =
- static_cast<nsRetrievalContextWayland *>(data);
-
- if (strcmp(interface, "wl_data_device_manager") == 0) {
- context->InitDataDeviceManager(registry, id, version);
- } else if (strcmp(interface, "wl_seat") == 0) {
- context->InitSeat(registry, id, version, data);
- } else if (strcmp(interface, "gtk_primary_selection_device_manager") == 0) {
- context->InitPrimarySelectionDataDeviceManager(registry, id);
- }
-}
-
-static void gdk_registry_handle_global_remove(void *data,
- struct wl_registry *registry,
- uint32_t id) {}
-
-static const struct wl_registry_listener clipboard_registry_listener = {
- gdk_registry_handle_global, gdk_registry_handle_global_remove};
-
nsRetrievalContextWayland::nsRetrievalContextWayland(void)
: mInitialized(false),
- mSeat(nullptr),
- mDataDeviceManager(nullptr),
- mPrimarySelectionDataDeviceManager(nullptr),
- mKeyboard(nullptr),
+ mDisplay(WaylandDisplayGet()),
mActiveOffers(g_hash_table_new(NULL, NULL)),
mClipboardOffer(nullptr),
mPrimaryOffer(nullptr),
+ mDragContext(nullptr),
mClipboardRequestNumber(0),
mClipboardData(nullptr),
mClipboardDataLength(0) {
- // Available as of GTK 3.8+
- static auto sGdkWaylandDisplayGetWlDisplay = (wl_display * (*)(GdkDisplay *))
- dlsym(RTLD_DEFAULT, "gdk_wayland_display_get_wl_display");
-
- mDisplay = sGdkWaylandDisplayGetWlDisplay(gdk_display_get_default());
- wl_registry_add_listener(wl_display_get_registry(mDisplay),
- &clipboard_registry_listener, this);
- // Call wl_display_roundtrip() twice to make sure all
- // callbacks are processed.
- wl_display_roundtrip(mDisplay);
- wl_display_roundtrip(mDisplay);
-
- // mSeat/mDataDeviceManager should be set now by
- // gdk_registry_handle_global() as a response to
- // wl_registry_add_listener() call.
- if (!mDataDeviceManager || !mSeat) return;
-
- wl_data_device *dataDevice =
- wl_data_device_manager_get_data_device(mDataDeviceManager, mSeat);
+ wl_data_device *dataDevice = wl_data_device_manager_get_data_device(
+ mDisplay->GetDataDeviceManager(), mDisplay->GetSeat());
wl_data_device_add_listener(dataDevice, &data_device_listener, this);
- // We have to call wl_display_roundtrip() twice otherwise data_offer_listener
- // may not be processed because it's called from data_device_data_offer
- // callback.
- wl_display_roundtrip(mDisplay);
- wl_display_roundtrip(mDisplay);
- if (mPrimarySelectionDataDeviceManager) {
+ gtk_primary_selection_device_manager *manager =
+ mDisplay->GetPrimarySelectionDeviceManager();
+ if (manager) {
gtk_primary_selection_device *primaryDataDevice =
- gtk_primary_selection_device_manager_get_device(
- mPrimarySelectionDataDeviceManager, mSeat);
+ gtk_primary_selection_device_manager_get_device(manager,
+ mDisplay->GetSeat());
gtk_primary_selection_device_add_listener(
primaryDataDevice, &primary_selection_device_listener, this);
}
@@ -492,8 +625,21 @@ nsRetrievalContextWayland::nsRetrievalCo
mInitialized = true;
}
+static gboolean offer_hash_remove(gpointer wl_offer, gpointer aDataOffer,
+ gpointer user_data) {
+#ifdef DEBUG
+ nsPrintfCString msg("nsRetrievalContextWayland(): leaked nsDataOffer %p\n",
+ aDataOffer);
+ NS_WARNING(msg.get());
+#endif
+ delete static_cast<DataOffer *>(aDataOffer);
+ return true;
+}
+
nsRetrievalContextWayland::~nsRetrievalContextWayland(void) {
+ g_hash_table_foreach_remove(mActiveOffers, offer_hash_remove, nullptr);
g_hash_table_destroy(mActiveOffers);
+ WaylandDisplayRelease(mDisplay);
}
GdkAtom *nsRetrievalContextWayland::GetTargets(int32_t aWhichClipboard,
@@ -533,12 +679,14 @@ static void wayland_clipboard_contents_r
void nsRetrievalContextWayland::TransferFastTrackClipboard(
int aClipboardRequestNumber, GtkSelectionData *aSelectionData) {
if (mClipboardRequestNumber == aClipboardRequestNumber) {
- mClipboardDataLength = gtk_selection_data_get_length(aSelectionData);
- if (mClipboardDataLength > 0) {
+ int dataLength = gtk_selection_data_get_length(aSelectionData);
+ if (dataLength > 0) {
+ mClipboardDataLength = dataLength;
mClipboardData = reinterpret_cast<char *>(
- g_malloc(sizeof(char) * mClipboardDataLength));
+ g_malloc(sizeof(char) * (mClipboardDataLength + 1)));
memcpy(mClipboardData, gtk_selection_data_get_data(aSelectionData),
sizeof(char) * mClipboardDataLength);
+ mClipboardData[mClipboardDataLength] = '\0';
}
} else {
NS_WARNING("Received obsoleted clipboard data!");
@@ -572,8 +720,8 @@ const char *nsRetrievalContextWayland::G
mClipboardData = nullptr;
mClipboardDataLength = 0;
} else {
- mClipboardData =
- dataOffer->GetData(mDisplay, aMimeType, &mClipboardDataLength);
+ mClipboardData = dataOffer->GetData(mDisplay->GetDisplay(), aMimeType,
+ &mClipboardDataLength);
}
}
@@ -588,7 +736,7 @@ const char *nsRetrievalContextWayland::G
(selection == GDK_SELECTION_PRIMARY) ? mPrimaryOffer : mClipboardOffer;
if (!dataOffer) return nullptr;
- for (unsigned int i = 0; i < sizeof(sTextMimeTypes); i++) {
+ for (unsigned int i = 0; i < TEXT_MIME_TYPES_NUM; i++) {
if (dataOffer->HasTarget(sTextMimeTypes[i])) {
uint32_t unused;
return GetClipboardData(sTextMimeTypes[i], aWhichClipboard, &unused);
diff -up thunderbird-60.5.0/widget/gtk/nsClipboardWayland.h.wayland thunderbird-60.5.0/widget/gtk/nsClipboardWayland.h
--- thunderbird-60.5.0/widget/gtk/nsClipboardWayland.h.wayland 2019-01-22 20:44:04.000000000 +0100
+++ thunderbird-60.5.0/widget/gtk/nsClipboardWayland.h 2019-02-05 14:26:16.975316648 +0100
@@ -1,4 +1,4 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:expandtab:shiftwidth=4:tabstop=4:
*/
/* This Source Code Form is subject to the terms of the Mozilla Public
@@ -9,6 +9,7 @@
#define __nsClipboardWayland_h_
#include "nsIClipboard.h"
+#include "mozwayland/mozwayland.h"
#include "wayland/gtk-primary-selection-client-protocol.h"
#include <gtk/gtk.h>
@@ -32,31 +33,75 @@ class DataOffer {
private:
virtual bool RequestDataTransfer(const char* aMimeType, int fd) = 0;
+ protected:
nsTArray<GdkAtom> mTargetMIMETypes;
};
class WaylandDataOffer : public DataOffer {
public:
- WaylandDataOffer(wl_data_offer* aWaylandDataOffer);
+ explicit WaylandDataOffer(wl_data_offer* aWaylandDataOffer);
+
+ void DragOfferAccept(const char* aMimeType, uint32_t aTime);
+ void SetDragStatus(GdkDragAction aAction, uint32_t aTime);
+
+ GdkDragAction GetSelectedDragAction();
+ void SetSelectedDragAction(uint32_t aWaylandAction);
+
+ void SetAvailableDragActions(uint32_t aWaylandActions);
+ GdkDragAction GetAvailableDragActions();
- private:
virtual ~WaylandDataOffer();
+
+ private:
bool RequestDataTransfer(const char* aMimeType, int fd) override;
wl_data_offer* mWaylandDataOffer;
+ uint32_t mSelectedDragAction;
+ uint32_t mAvailableDragAction;
};
class PrimaryDataOffer : public DataOffer {
public:
- PrimaryDataOffer(gtk_primary_selection_offer* aPrimaryDataOffer);
+ explicit PrimaryDataOffer(gtk_primary_selection_offer* aPrimaryDataOffer);
+ void SetAvailableDragActions(uint32_t aWaylandActions){};
- private:
virtual ~PrimaryDataOffer();
+
+ private:
bool RequestDataTransfer(const char* aMimeType, int fd) override;
gtk_primary_selection_offer* mPrimaryDataOffer;
};
+class nsWaylandDragContext : public nsISupports {
+ NS_DECL_ISUPPORTS
+
+ public:
+ nsWaylandDragContext(WaylandDataOffer* aWaylandDataOffer,
+ wl_display* aDisplay);
+
+ void DropDataEnter(GtkWidget* aGtkWidget, uint32_t aTime, nscoord aX,
+ nscoord aY);
+ void DropMotion(uint32_t aTime, nscoord aX, nscoord aY);
+ void GetLastDropInfo(uint32_t* aTime, nscoord* aX, nscoord* aY);
+
+ void SetDragStatus(GdkDragAction action);
+ GdkDragAction GetSelectedDragAction();
+
+ GtkWidget* GetWidget() { return mGtkWidget; }
+ GList* GetTargets();
+ char* GetData(const char* aMimeType, uint32_t* aContentLength);
+
+ private:
+ virtual ~nsWaylandDragContext(){};
+
+ nsAutoPtr<WaylandDataOffer> mDataOffer;
+ wl_display* mDisplay;
+ uint32_t mTime;
+ GtkWidget* mGtkWidget;
+ nscoord mX, mY;
+};
+
class nsRetrievalContextWayland : public nsRetrievalContext {
public:
nsRetrievalContextWayland();
@@ -71,38 +116,30 @@ class nsRetrievalContextWayland : public
int* aTargetNum) override;
virtual bool HasSelectionSupport(void) override;
- void RegisterDataOffer(wl_data_offer* aWaylandDataOffer);
- void RegisterDataOffer(gtk_primary_selection_offer* aPrimaryDataOffer);
+ void RegisterNewDataOffer(wl_data_offer* aWaylandDataOffer);
+ void RegisterNewDataOffer(gtk_primary_selection_offer* aPrimaryDataOffer);
void SetClipboardDataOffer(wl_data_offer* aWaylandDataOffer);
void SetPrimaryDataOffer(gtk_primary_selection_offer* aPrimaryDataOffer);
+ void AddDragAndDropDataOffer(wl_data_offer* aWaylandDataOffer);
+ nsWaylandDragContext* GetDragContext();
- void ClearDataOffers();
+ void ClearDragAndDropDataOffer();
- void ConfigureKeyboard(wl_seat_capability caps);
void TransferFastTrackClipboard(int aClipboardRequestNumber,
GtkSelectionData* aSelectionData);
- void InitDataDeviceManager(wl_registry* registry, uint32_t id,
- uint32_t version);
- void InitPrimarySelectionDataDeviceManager(wl_registry* registry,
- uint32_t id);
- void InitSeat(wl_registry* registry, uint32_t id, uint32_t version,
- void* data);
virtual ~nsRetrievalContextWayland() override;
private:
bool mInitialized;
- wl_display* mDisplay;
- wl_seat* mSeat;
- wl_data_device_manager* mDataDeviceManager;
- gtk_primary_selection_device_manager* mPrimarySelectionDataDeviceManager;
- wl_keyboard* mKeyboard;
+ nsWaylandDisplay* mDisplay;
// Data offers provided by Wayland data device
GHashTable* mActiveOffers;
nsAutoPtr<DataOffer> mClipboardOffer;
nsAutoPtr<DataOffer> mPrimaryOffer;
+ RefPtr<nsWaylandDragContext> mDragContext;
int mClipboardRequestNumber;
char* mClipboardData;
diff -up thunderbird-60.5.0/widget/gtk/nsDragService.cpp.wayland thunderbird-60.5.0/widget/gtk/nsDragService.cpp
--- thunderbird-60.5.0/widget/gtk/nsDragService.cpp.wayland 2019-01-22 20:44:04.000000000 +0100
+++ thunderbird-60.5.0/widget/gtk/nsDragService.cpp 2019-02-05 14:26:16.976316645 +0100
@@ -1,5 +1,5 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/* vim: set ts=4 et sw=4 tw=80: */
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=4 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
@@ -9,6 +9,7 @@
#include "nsIObserverService.h"
#include "nsWidgetsCID.h"
#include "nsWindow.h"
+#include "nsSystemInfo.h"
#include "nsIServiceManager.h"
#include "nsXPCOM.h"
#include "nsISupportsPrimitives.h"
@@ -34,7 +35,6 @@
#include "nsPresContext.h"
#include "nsIContent.h"
#include "nsIDocument.h"
-#include "nsISelection.h"
#include "nsViewManager.h"
#include "nsIFrame.h"
#include "nsGtkUtils.h"
@@ -43,10 +43,15 @@
#include "gfxPlatform.h"
#include "ScreenHelperGTK.h"
#include "nsArrayUtils.h"
+#ifdef MOZ_WAYLAND
+#include "nsClipboardWayland.h"
+#endif
using namespace mozilla;
using namespace mozilla::gfx;
+#define NS_SYSTEMINFO_CONTRACTID "@mozilla.org/system-info;1"
+
// This sets how opaque the drag image is
#define DRAG_IMAGE_ALPHA_LEVEL 0.5
@@ -68,6 +73,7 @@ static const char gMimeListType[] = "app
static const char gMozUrlType[] = "_NETSCAPE_URL";
static const char gTextUriListType[] = "text/uri-list";
static const char gTextPlainUTF8Type[] = "text/plain;charset=utf-8";
+static const char gXdndDirectSaveType[] = "XdndDirectSave0";
static void invisibleSourceDragBegin(GtkWidget *aWidget,
GdkDragContext *aContext, gpointer aData);
@@ -85,7 +91,15 @@ static void invisibleSourceDragDataGet(G
guint aInfo, guint32 aTime,
gpointer aData);
-nsDragService::nsDragService() : mScheduledTask(eDragTaskNone), mTaskSource(0) {
+nsDragService::nsDragService()
+ : mScheduledTask(eDragTaskNone),
+ mTaskSource(0)
+#ifdef MOZ_WAYLAND
+ ,
+ mPendingWaylandDragContext(nullptr),
+ mTargetWaylandDragContext(nullptr)
+#endif
+{
// We have to destroy the hidden widget before the event loop stops
// running.
nsCOMPtr<nsIObserverService> obsServ =
@@ -159,7 +173,7 @@ nsDragService::Observe(nsISupports *aSub
}
TargetResetData();
} else {
- NS_NOTREACHED("unexpected topic");
+ MOZ_ASSERT_UNREACHABLE("unexpected topic");
return NS_ERROR_UNEXPECTED;
}
@@ -457,6 +471,9 @@ nsDragService::EndDragSession(bool aDone
// We're done with the drag context.
mTargetDragContextForRemote = nullptr;
+#ifdef MOZ_WAYLAND
+ mTargetWaylandDragContextForRemote = nullptr;
+#endif
return nsBaseDragService::EndDragSession(aDoneDrag, aKeyModifiers);
}
@@ -550,6 +567,14 @@ nsDragService::GetNumDropItems(uint32_t
return NS_OK;
}
+#ifdef MOZ_WAYLAND
+ // TODO: Wayland implementation of text/uri-list.
+ if (!mTargetDragContext) {
+ *aNumItems = 1;
+ return NS_OK;
+ }
+#endif
+
bool isList = IsTargetContextList();
if (isList)
mSourceDataItems->GetLength(aNumItems);
@@ -907,9 +932,18 @@ nsDragService::IsDataFlavorSupported(con
}
// check the target context vs. this flavor, one at a time
- GList *tmp;
- for (tmp = gdk_drag_context_list_targets(mTargetDragContext); tmp;
- tmp = tmp->next) {
+ GList *tmp = nullptr;
+ if (mTargetDragContext) {
+ tmp = gdk_drag_context_list_targets(mTargetDragContext);
+ }
+#ifdef MOZ_WAYLAND
+ else if (mTargetWaylandDragContext) {
+ tmp = mTargetWaylandDragContext->GetTargets();
+ }
+ GList *tmp_head = tmp;
+#endif
+
+ for (; tmp; tmp = tmp->next) {
/* Bug 331198 */
GdkAtom atom = GDK_POINTER_TO_ATOM(tmp->data);
gchar *name = nullptr;
@@ -946,6 +980,15 @@ nsDragService::IsDataFlavorSupported(con
}
g_free(name);
}
+
+#ifdef MOZ_WAYLAND
+ // mTargetWaylandDragContext->GetTargets allocates the list
+ // so we need to free it here.
+ if (!mTargetDragContext && tmp_head) {
+ g_list_free(tmp_head);
+ }
+#endif
+
return NS_OK;
}
@@ -975,6 +1018,34 @@ void nsDragService::ReplyToDragMotion(Gd
gdk_drag_status(aDragContext, action, mTargetTime);
}
+#ifdef MOZ_WAYLAND
+void nsDragService::ReplyToDragMotion(nsWaylandDragContext *aDragContext) {
+ MOZ_LOG(sDragLm, LogLevel::Debug,
+ ("nsDragService::ReplyToDragMotion %d", mCanDrop));
+
+ GdkDragAction action = (GdkDragAction)0;
+ if (mCanDrop) {
+ // notify the dragger if we can drop
+ switch (mDragAction) {
+ case DRAGDROP_ACTION_COPY:
+ action = GDK_ACTION_COPY;
+ break;
+ case DRAGDROP_ACTION_LINK:
+ action = GDK_ACTION_LINK;
+ break;
+ case DRAGDROP_ACTION_NONE:
+ action = (GdkDragAction)0;
+ break;
+ default:
+ action = GDK_ACTION_MOVE;
+ break;
+ }
+ }
+
+ aDragContext->SetDragStatus(action);
+}
+#endif
+
void nsDragService::TargetDataReceived(GtkWidget *aWidget,
GdkDragContext *aContext, gint aX,
gint aY,
@@ -999,6 +1070,11 @@ void nsDragService::TargetDataReceived(G
bool nsDragService::IsTargetContextList(void) {
bool retval = false;
+#ifdef MOZ_WAYLAND
+ // TODO: We need a wayland implementation here.
+ if (!mTargetDragContext) return retval;
+#endif
+
// gMimeListType drags only work for drags within a single process. The
// gtk_drag_get_source_widget() function will return nullptr if the source
// of the drag is another app, so we use it to check if a gMimeListType
@@ -1032,17 +1108,27 @@ void nsDragService::GetTargetDragData(Gd
mTargetDragContext.get()));
// reset our target data areas
TargetResetData();
- gtk_drag_get_data(mTargetWidget, mTargetDragContext, aFlavor, mTargetTime);
- MOZ_LOG(sDragLm, LogLevel::Debug, ("about to start inner iteration."));
- PRTime entryTime = PR_Now();
- while (!mTargetDragDataReceived && mDoingDrag) {
- // check the number of iterations
- MOZ_LOG(sDragLm, LogLevel::Debug, ("doing iteration...\n"));
- PR_Sleep(20 * PR_TicksPerSecond() / 1000); /* sleep for 20 ms/iteration */
- if (PR_Now() - entryTime > NS_DND_TIMEOUT) break;
- gtk_main_iteration();
+ if (mTargetDragContext) {
+ gtk_drag_get_data(mTargetWidget, mTargetDragContext, aFlavor, mTargetTime);
+
+ MOZ_LOG(sDragLm, LogLevel::Debug, ("about to start inner iteration."));
+ PRTime entryTime = PR_Now();
+ while (!mTargetDragDataReceived && mDoingDrag) {
+ // check the number of iterations
+ MOZ_LOG(sDragLm, LogLevel::Debug, ("doing iteration...\n"));
+ PR_Sleep(20 * PR_TicksPerSecond() / 1000); /* sleep for 20 ms/iteration */
+ if (PR_Now() - entryTime > NS_DND_TIMEOUT) break;
+ gtk_main_iteration();
+ }
}
+#ifdef MOZ_WAYLAND
+ else {
+ mTargetDragData = mTargetWaylandDragContext->GetData(gdk_atom_name(aFlavor),
+ &mTargetDragDataLen);
+ mTargetDragDataReceived = true;
+ }
+#endif
MOZ_LOG(sDragLm, LogLevel::Debug, ("finished inner iteration\n"));
}
@@ -1218,6 +1304,10 @@ void nsDragService::SourceEndDragSession
// this just releases the list of data items that we provide
mSourceDataItems = nullptr;
+ // Remove this property, if it exists, to satisfy the Direct Save Protocol.
+ GdkAtom property = gdk_atom_intern(gXdndDirectSaveType, FALSE);
+ gdk_property_delete(gdk_drag_context_get_source_window(aContext), property);
+
if (!mDoingDrag || mScheduledTask == eDragTaskSourceEnd)
// EndDragSession() was already called on drop
// or SourceEndDragSession on drag-failed
@@ -1276,7 +1366,7 @@ void nsDragService::SourceEndDragSession
}
// Schedule the appropriate drag end dom events.
- Schedule(eDragTaskSourceEnd, nullptr, nullptr, LayoutDeviceIntPoint(), 0);
+ Schedule(eDragTaskSourceEnd, nullptr, nullptr, nullptr, LayoutDeviceIntPoint(), 0);
}
static void CreateUriList(nsIArray *items, gchar **text, gint *length) {
@@ -1585,11 +1675,11 @@ static void invisibleSourceDragEnd(GtkWi
// Gecko drag events are in flight. This helps event handlers that may not
// expect nested events, while accessing an event's dataTransfer for example.
-gboolean nsDragService::ScheduleMotionEvent(nsWindow *aWindow,
- GdkDragContext *aDragContext,
- LayoutDeviceIntPoint aWindowPoint,
- guint aTime) {
- if (mScheduledTask == eDragTaskMotion) {
+gboolean nsDragService::ScheduleMotionEvent(
+ nsWindow *aWindow, GdkDragContext *aDragContext,
+ nsWaylandDragContext *aWaylandDragContext,
+ LayoutDeviceIntPoint aWindowPoint, guint aTime) {
+ if (aDragContext && mScheduledTask == eDragTaskMotion) {
// The drag source has sent another motion message before we've
// replied to the previous. That shouldn't happen with Xdnd. The
// spec for Motif drags is less clear, but we'll just update the
@@ -1600,23 +1690,26 @@ gboolean nsDragService::ScheduleMotionEv
// Returning TRUE means we'll reply with a status message, unless we first
// get a leave.
- return Schedule(eDragTaskMotion, aWindow, aDragContext, aWindowPoint, aTime);
+ return Schedule(eDragTaskMotion, aWindow, aDragContext, aWaylandDragContext,
+ aWindowPoint, aTime);
}
void nsDragService::ScheduleLeaveEvent() {
// We don't know at this stage whether a drop signal will immediately
// follow. If the drop signal gets sent it will happen before we return
// to the main loop and the scheduled leave task will be replaced.
- if (!Schedule(eDragTaskLeave, nullptr, nullptr, LayoutDeviceIntPoint(), 0)) {
+ if (!Schedule(eDragTaskLeave, nullptr, nullptr, nullptr,
+ LayoutDeviceIntPoint(), 0)) {
NS_WARNING("Drag leave after drop");
}
}
-gboolean nsDragService::ScheduleDropEvent(nsWindow *aWindow,
- GdkDragContext *aDragContext,
- LayoutDeviceIntPoint aWindowPoint,
- guint aTime) {
- if (!Schedule(eDragTaskDrop, aWindow, aDragContext, aWindowPoint, aTime)) {
+gboolean nsDragService::ScheduleDropEvent(
+ nsWindow *aWindow, GdkDragContext *aDragContext,
+ nsWaylandDragContext *aWaylandDragContext,
+ LayoutDeviceIntPoint aWindowPoint, guint aTime) {
+ if (!Schedule(eDragTaskDrop, aWindow, aDragContext, aWaylandDragContext,
+ aWindowPoint, aTime)) {
NS_WARNING("Additional drag drop ignored");
return FALSE;
}
@@ -1629,6 +1722,7 @@ gboolean nsDragService::ScheduleDropEven
gboolean nsDragService::Schedule(DragTask aTask, nsWindow *aWindow,
GdkDragContext *aDragContext,
+ nsWaylandDragContext *aWaylandDragContext,
LayoutDeviceIntPoint aWindowPoint,
guint aTime) {
// If there is an existing leave or motion task scheduled, then that
@@ -1647,6 +1741,9 @@ gboolean nsDragService::Schedule(DragTas
mScheduledTask = aTask;
mPendingWindow = aWindow;
mPendingDragContext = aDragContext;
+#ifdef MOZ_WAYLAND
+ mPendingWaylandDragContext = aWaylandDragContext;
+#endif
mPendingWindowPoint = aWindowPoint;
mPendingTime = aTime;
@@ -1717,6 +1814,9 @@ gboolean nsDragService::RunScheduledTask
// succeeed.
mTargetWidget = mTargetWindow->GetMozContainerWidget();
mTargetDragContext.steal(mPendingDragContext);
+#ifdef MOZ_WAYLAND
+ mTargetWaylandDragContext = mPendingWaylandDragContext.forget();
+#endif
mTargetTime = mPendingTime;
// http://www.whatwg.org/specs/web-apps/current-work/multipage/dnd.html#drag-and-drop-processing-model
@@ -1748,10 +1848,20 @@ gboolean nsDragService::RunScheduledTask
if (task == eDragTaskMotion) {
if (TakeDragEventDispatchedToChildProcess()) {
mTargetDragContextForRemote = mTargetDragContext;
+#ifdef MOZ_WAYLAND
+ mTargetWaylandDragContextForRemote = mTargetWaylandDragContext;
+#endif
} else {
// Reply to tell the source whether we can drop and what
// action would be taken.
- ReplyToDragMotion(mTargetDragContext);
+ if (mTargetDragContext) {
+ ReplyToDragMotion(mTargetDragContext);
+ }
+#ifdef MOZ_WAYLAND
+ else if (mTargetWaylandDragContext) {
+ ReplyToDragMotion(mTargetWaylandDragContext);
+ }
+#endif
}
}
}
@@ -1762,8 +1872,10 @@ gboolean nsDragService::RunScheduledTask
// Perhaps we should set the del parameter to TRUE when the drag
// action is move, but we don't know whether the data was successfully
// transferred.
- gtk_drag_finish(mTargetDragContext, success,
- /* del = */ FALSE, mTargetTime);
+ if (mTargetDragContext) {
+ gtk_drag_finish(mTargetDragContext, success,
+ /* del = */ FALSE, mTargetTime);
+ }
// This drag is over, so clear out our reference to the previous
// window.
@@ -1776,6 +1888,9 @@ gboolean nsDragService::RunScheduledTask
// We're done with the drag context.
mTargetWidget = nullptr;
mTargetDragContext = nullptr;
+#ifdef MOZ_WAYLAND
+ mTargetWaylandDragContext = nullptr;
+#endif
// If we got another drag signal while running the sheduled task, that
// must have happened while running a nested event loop. Leave the task
@@ -1802,7 +1917,16 @@ void nsDragService::UpdateDragAction() {
// default is to do nothing
int action = nsIDragService::DRAGDROP_ACTION_NONE;
- GdkDragAction gdkAction = gdk_drag_context_get_actions(mTargetDragContext);
+ GdkDragAction gdkAction = GDK_ACTION_DEFAULT;
+ if (mTargetDragContext) {
+ gdkAction = gdk_drag_context_get_actions(mTargetDragContext);
+ }
+#ifdef MOZ_WAYLAND
+ else if (mTargetWaylandDragContext) {
+ // We got the selected D&D action from compositor on Wayland.
+ gdkAction = mTargetWaylandDragContext->GetSelectedDragAction();
+ }
+#endif
// set the default just in case nothing matches below
if (gdkAction & GDK_ACTION_DEFAULT)
@@ -1830,6 +1954,12 @@ nsDragService::UpdateDragEffect() {
ReplyToDragMotion(mTargetDragContextForRemote);
mTargetDragContextForRemote = nullptr;
}
+#ifdef MOZ_WAYLAND
+ else if (mTargetWaylandDragContextForRemote) {
+ ReplyToDragMotion(mTargetWaylandDragContextForRemote);
+ mTargetWaylandDragContextForRemote = nullptr;
+ }
+#endif
return NS_OK;
}
diff -up thunderbird-60.5.0/widget/gtk/nsDragService.h.wayland thunderbird-60.5.0/widget/gtk/nsDragService.h
--- thunderbird-60.5.0/widget/gtk/nsDragService.h.wayland 2019-01-22 20:44:03.000000000 +0100
+++ thunderbird-60.5.0/widget/gtk/nsDragService.h 2019-02-05 14:26:16.976316645 +0100
@@ -1,5 +1,5 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/* vim: set ts=4 et sw=4 tw=80: */
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=4 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
@@ -14,6 +14,7 @@
#include <gtk/gtk.h>
class nsWindow;
+class nsWaylandDragContext;
namespace mozilla {
namespace gfx {
@@ -91,10 +92,12 @@ class nsDragService final : public nsBas
guint aInfo, guint32 aTime);
gboolean ScheduleMotionEvent(nsWindow *aWindow, GdkDragContext *aDragContext,
+ nsWaylandDragContext *aPendingWaylandDragContext,
mozilla::LayoutDeviceIntPoint aWindowPoint,
guint aTime);
void ScheduleLeaveEvent();
gboolean ScheduleDropEvent(nsWindow *aWindow, GdkDragContext *aDragContext,
+ nsWaylandDragContext *aPendingWaylandDragContext,
mozilla::LayoutDeviceIntPoint aWindowPoint,
guint aTime);
@@ -111,6 +114,8 @@ class nsDragService final : public nsBas
void SourceDataGet(GtkWidget *widget, GdkDragContext *context,
GtkSelectionData *selection_data, guint32 aTime);
+ void SourceBeginDrag(GdkDragContext *aContext);
+
// set the drag icon during drag-begin
void SetDragIcon(GdkDragContext *aContext);
@@ -144,6 +149,9 @@ class nsDragService final : public nsBas
RefPtr<nsWindow> mPendingWindow;
mozilla::LayoutDeviceIntPoint mPendingWindowPoint;
nsCountedRef<GdkDragContext> mPendingDragContext;
+#ifdef MOZ_WAYLAND
+ RefPtr<nsWaylandDragContext> mPendingWaylandDragContext;
+#endif
guint mPendingTime;
// mTargetWindow and mTargetWindowPoint record the position of the last
@@ -155,9 +163,15 @@ class nsDragService final : public nsBas
// motion or drop events. mTime records the corresponding timestamp.
nsCountedRef<GtkWidget> mTargetWidget;
nsCountedRef<GdkDragContext> mTargetDragContext;
+#ifdef MOZ_WAYLAND
+ RefPtr<nsWaylandDragContext> mTargetWaylandDragContext;
+#endif
// mTargetDragContextForRemote is set while waiting for a reply from
// a child process.
nsCountedRef<GdkDragContext> mTargetDragContextForRemote;
+#ifdef MOZ_WAYLAND
+ RefPtr<nsWaylandDragContext> mTargetWaylandDragContextForRemote;
+#endif
guint mTargetTime;
// is it OK to drop on us?
@@ -196,6 +210,7 @@ class nsDragService final : public nsBas
gboolean Schedule(DragTask aTask, nsWindow *aWindow,
GdkDragContext *aDragContext,
+ nsWaylandDragContext *aPendingWaylandDragContext,
mozilla::LayoutDeviceIntPoint aWindowPoint, guint aTime);
// Callback for g_idle_add_full() to run mScheduledTask.
@@ -204,6 +219,9 @@ class nsDragService final : public nsBas
void UpdateDragAction();
void DispatchMotionEvents();
void ReplyToDragMotion(GdkDragContext *aDragContext);
+#ifdef MOZ_WAYLAND
+ void ReplyToDragMotion(nsWaylandDragContext *aDragContext);
+#endif
gboolean DispatchDropEvent();
static uint32_t GetCurrentModifiers();
};
diff -up thunderbird-60.5.0/widget/gtk/nsGtkKeyUtils.cpp.wayland thunderbird-60.5.0/widget/gtk/nsGtkKeyUtils.cpp
--- thunderbird-60.5.0/widget/gtk/nsGtkKeyUtils.cpp.wayland 2019-01-22 20:44:03.000000000 +0100
+++ thunderbird-60.5.0/widget/gtk/nsGtkKeyUtils.cpp 2019-02-05 14:26:16.976316645 +0100
@@ -1,4 +1,4 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:expandtab:shiftwidth=4:tabstop=4:
*/
/* This Source Code Form is subject to the terms of the Mozilla Public
@@ -28,6 +28,10 @@
#include "mozilla/MouseEvents.h"
#include "mozilla/TextEvents.h"
+#ifdef MOZ_WAYLAND
+#include <sys/mman.h>
+#endif
+
namespace mozilla {
namespace widget {
@@ -200,7 +204,11 @@ void KeymapWrapper::Init() {
mModifierKeys.Clear();
memset(mModifierMasks, 0, sizeof(mModifierMasks));
- if (GDK_IS_X11_DISPLAY(gdk_display_get_default())) InitBySystemSettings();
+ if (GDK_IS_X11_DISPLAY(gdk_display_get_default())) InitBySystemSettingsX11();
+#ifdef MOZ_WAYLAND
+ else
+ InitBySystemSettingsWayland();
+#endif
gdk_window_add_filter(nullptr, FilterEvents, this);
@@ -276,9 +284,9 @@ void KeymapWrapper::InitXKBExtension() {
("%p InitXKBExtension, Succeeded", this));
}
-void KeymapWrapper::InitBySystemSettings() {
+void KeymapWrapper::InitBySystemSettingsX11() {
MOZ_LOG(gKeymapWrapperLog, LogLevel::Info,
- ("%p InitBySystemSettings, mGdkKeymap=%p", this, mGdkKeymap));
+ ("%p InitBySystemSettingsX11, mGdkKeymap=%p", this, mGdkKeymap));
Display* display = gdk_x11_display_get_xdisplay(gdk_display_get_default());
@@ -439,6 +447,163 @@ void KeymapWrapper::InitBySystemSettings
XFree(xkeymap);
}
+#ifdef MOZ_WAYLAND
+void KeymapWrapper::SetModifierMask(xkb_keymap* aKeymap,
+ ModifierIndex aModifierIndex,
+ const char* aModifierName) {
+ static auto sXkbKeymapModGetIndex =
+ (xkb_mod_index_t(*)(struct xkb_keymap*, const char*))dlsym(
+ RTLD_DEFAULT, "xkb_keymap_mod_get_index");
+
+ xkb_mod_index_t index = sXkbKeymapModGetIndex(aKeymap, aModifierName);
+ if (index != XKB_MOD_INVALID) {
+ mModifierMasks[aModifierIndex] = (1 << index);
+ }
+}
+
+void KeymapWrapper::SetModifierMasks(xkb_keymap* aKeymap) {
+ KeymapWrapper* keymapWrapper = GetInstance();
+
+ // This mapping is derived from get_xkb_modifiers() at gdkkeys-wayland.c
+ keymapWrapper->SetModifierMask(aKeymap, INDEX_NUM_LOCK, XKB_MOD_NAME_NUM);
+ keymapWrapper->SetModifierMask(aKeymap, INDEX_ALT, XKB_MOD_NAME_ALT);
+ keymapWrapper->SetModifierMask(aKeymap, INDEX_META, "Meta");
+ keymapWrapper->SetModifierMask(aKeymap, INDEX_SUPER, "Super");
+ keymapWrapper->SetModifierMask(aKeymap, INDEX_HYPER, "Hyper");
+
+ keymapWrapper->SetModifierMask(aKeymap, INDEX_SCROLL_LOCK, "ScrollLock");
+ keymapWrapper->SetModifierMask(aKeymap, INDEX_LEVEL3, "Level3");
+ keymapWrapper->SetModifierMask(aKeymap, INDEX_LEVEL5, "Level5");
+
+ MOZ_LOG(gKeymapWrapperLog, LogLevel::Info,
+ ("%p KeymapWrapper::SetModifierMasks, CapsLock=0x%X, NumLock=0x%X, "
+ "ScrollLock=0x%X, Level3=0x%X, Level5=0x%X, "
+ "Shift=0x%X, Ctrl=0x%X, Alt=0x%X, Meta=0x%X, Super=0x%X, Hyper=0x%X",
+ keymapWrapper, keymapWrapper->GetModifierMask(CAPS_LOCK),
+ keymapWrapper->GetModifierMask(NUM_LOCK),
+ keymapWrapper->GetModifierMask(SCROLL_LOCK),
+ keymapWrapper->GetModifierMask(LEVEL3),
+ keymapWrapper->GetModifierMask(LEVEL5),
+ keymapWrapper->GetModifierMask(SHIFT),
+ keymapWrapper->GetModifierMask(CTRL),
+ keymapWrapper->GetModifierMask(ALT),
+ keymapWrapper->GetModifierMask(META),
+ keymapWrapper->GetModifierMask(SUPER),
+ keymapWrapper->GetModifierMask(HYPER)));
+}
+
+/* This keymap routine is derived from weston-2.0.0/clients/simple-im.c
+ */
+static void keyboard_handle_keymap(void* data, struct wl_keyboard* wl_keyboard,
+ uint32_t format, int fd, uint32_t size) {
+ if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
+ close(fd);
+ return;
+ }
+
+ char* mapString = (char*)mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
+ if (mapString == MAP_FAILED) {
+ close(fd);
+ return;
+ }
+
+ static auto sXkbContextNew =
+ (struct xkb_context * (*)(enum xkb_context_flags))
+ dlsym(RTLD_DEFAULT, "xkb_context_new");
+ static auto sXkbKeymapNewFromString =
+ (struct xkb_keymap * (*)(struct xkb_context*, const char*,
+ enum xkb_keymap_format,
+ enum xkb_keymap_compile_flags))
+ dlsym(RTLD_DEFAULT, "xkb_keymap_new_from_string");
+
+ struct xkb_context* xkb_context = sXkbContextNew(XKB_CONTEXT_NO_FLAGS);
+ struct xkb_keymap* keymap =
+ sXkbKeymapNewFromString(xkb_context, mapString, XKB_KEYMAP_FORMAT_TEXT_V1,
+ XKB_KEYMAP_COMPILE_NO_FLAGS);
+
+ munmap(mapString, size);
+ close(fd);
+
+ if (!keymap) {
+ NS_WARNING("keyboard_handle_keymap(): Failed to compile keymap!\n");
+ return;
+ }
+
+ KeymapWrapper::SetModifierMasks(keymap);
+
+ static auto sXkbKeymapUnRef =
+ (void (*)(struct xkb_keymap*))dlsym(RTLD_DEFAULT, "xkb_keymap_unref");
+ sXkbKeymapUnRef(keymap);
+
+ static auto sXkbContextUnref =
+ (void (*)(struct xkb_context*))dlsym(RTLD_DEFAULT, "xkb_context_unref");
+ sXkbContextUnref(xkb_context);
+}
+
+static void keyboard_handle_enter(void* data, struct wl_keyboard* keyboard,
+ uint32_t serial, struct wl_surface* surface,
+ struct wl_array* keys) {}
+static void keyboard_handle_leave(void* data, struct wl_keyboard* keyboard,
+ uint32_t serial, struct wl_surface* surface) {
+}
+static void keyboard_handle_key(void* data, struct wl_keyboard* keyboard,
+ uint32_t serial, uint32_t time, uint32_t key,
+ uint32_t state) {}
+static void keyboard_handle_modifiers(void* data, struct wl_keyboard* keyboard,
+ uint32_t serial, uint32_t mods_depressed,
+ uint32_t mods_latched,
+ uint32_t mods_locked, uint32_t group) {}
+
+static const struct wl_keyboard_listener keyboard_listener = {
+ keyboard_handle_keymap, keyboard_handle_enter, keyboard_handle_leave,
+ keyboard_handle_key, keyboard_handle_modifiers,
+};
+
+static void seat_handle_capabilities(void* data, struct wl_seat* seat,
+ unsigned int caps) {
+ static wl_keyboard* keyboard = nullptr;
+
+ if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !keyboard) {
+ keyboard = wl_seat_get_keyboard(seat);
+ wl_keyboard_add_listener(keyboard, &keyboard_listener, nullptr);
+ } else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && keyboard) {
+ wl_keyboard_destroy(keyboard);
+ keyboard = nullptr;
+ }
+}
+
+static const struct wl_seat_listener seat_listener = {
+ seat_handle_capabilities,
+};
+
+static void gdk_registry_handle_global(void* data, struct wl_registry* registry,
+ uint32_t id, const char* interface,
+ uint32_t version) {
+ if (strcmp(interface, "wl_seat") == 0) {
+ wl_seat* seat =
+ (wl_seat*)wl_registry_bind(registry, id, &wl_seat_interface, 1);
+ wl_seat_add_listener(seat, &seat_listener, data);
+ }
+}
+
+static void gdk_registry_handle_global_remove(void* data,
+ struct wl_registry* registry,
+ uint32_t id) {}
+
+static const struct wl_registry_listener keyboard_registry_listener = {
+ gdk_registry_handle_global, gdk_registry_handle_global_remove};
+
+void KeymapWrapper::InitBySystemSettingsWayland() {
+ // Available as of GTK 3.8+
+ static auto sGdkWaylandDisplayGetWlDisplay = (wl_display * (*)(GdkDisplay*))
+ dlsym(RTLD_DEFAULT, "gdk_wayland_display_get_wl_display");
+ wl_display* display =
+ sGdkWaylandDisplayGetWlDisplay(gdk_display_get_default());
+ wl_registry_add_listener(wl_display_get_registry(display),
+ &keyboard_registry_listener, this);
+}
+#endif
+
KeymapWrapper::~KeymapWrapper() {
gdk_window_remove_filter(nullptr, FilterEvents, this);
g_signal_handlers_disconnect_by_func(mGdkKeymap,
@@ -1473,6 +1638,14 @@ void KeymapWrapper::WillDispatchKeyboard
void KeymapWrapper::WillDispatchKeyboardEventInternal(
WidgetKeyboardEvent& aKeyEvent, GdkEventKey* aGdkKeyEvent) {
+ if (!aGdkKeyEvent) {
+ // If aGdkKeyEvent is nullptr, we're trying to dispatch a fake keyboard
+ // event in such case, we don't need to set alternative char codes.
+ // So, we don't need to do nothing here. This case is typically we're
+ // dispatching eKeyDown or eKeyUp event during composition.
+ return;
+ }
+
uint32_t charCode = GetCharCodeFor(aGdkKeyEvent);
if (!charCode) {
MOZ_LOG(gKeymapWrapperLog, LogLevel::Info,
diff -up thunderbird-60.5.0/widget/gtk/nsGtkKeyUtils.h.wayland thunderbird-60.5.0/widget/gtk/nsGtkKeyUtils.h
--- thunderbird-60.5.0/widget/gtk/nsGtkKeyUtils.h.wayland 2019-01-22 20:44:04.000000000 +0100
+++ thunderbird-60.5.0/widget/gtk/nsGtkKeyUtils.h 2019-02-05 14:26:16.976316645 +0100
@@ -1,4 +1,4 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:expandtab:shiftwidth=4:tabstop=4:
*/
/* This Source Code Form is subject to the terms of the Mozilla Public
@@ -13,6 +13,10 @@
#include <gdk/gdk.h>
#include <X11/XKBlib.h>
+#ifdef MOZ_WAYLAND
+#include <gdk/gdkwayland.h>
+#include <xkbcommon/xkbcommon.h>
+#endif
namespace mozilla {
namespace widget {
@@ -145,6 +149,14 @@ class KeymapWrapper {
static void WillDispatchKeyboardEvent(WidgetKeyboardEvent& aKeyEvent,
GdkEventKey* aGdkKeyEvent);
+#ifdef MOZ_WAYLAND
+ /**
+ * Utility function to set all supported modifier masks
+ * from xkb_keymap. We call that from Wayland backend routines.
+ */
+ static void SetModifierMasks(xkb_keymap* aKeymap);
+#endif
+
/**
* Destroys the singleton KeymapWrapper instance, if it exists.
*/
@@ -168,7 +180,10 @@ class KeymapWrapper {
*/
void Init();
void InitXKBExtension();
- void InitBySystemSettings();
+ void InitBySystemSettingsX11();
+#ifdef MOZ_WAYLAND
+ void InitBySystemSettingsWayland();
+#endif
/**
* mModifierKeys stores each hardware key information.
@@ -360,6 +375,14 @@ class KeymapWrapper {
*/
void WillDispatchKeyboardEventInternal(WidgetKeyboardEvent& aKeyEvent,
GdkEventKey* aGdkKeyEvent);
+
+#ifdef MOZ_WAYLAND
+ /**
+ * Utility function to set Xkb modifier key mask.
+ */
+ void SetModifierMask(xkb_keymap* aKeymap, ModifierIndex aModifierIndex,
+ const char* aModifierName);
+#endif
};
} // namespace widget
diff -up thunderbird-60.5.0/widget/gtk/nsLookAndFeel.cpp.wayland thunderbird-60.5.0/widget/gtk/nsLookAndFeel.cpp
--- thunderbird-60.5.0/widget/gtk/nsLookAndFeel.cpp.wayland 2019-01-22 20:44:03.000000000 +0100
+++ thunderbird-60.5.0/widget/gtk/nsLookAndFeel.cpp 2019-02-05 14:26:16.977316642 +0100
@@ -1,4 +1,4 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:expandtab:shiftwidth=4:tabstop=4:
*/
/* This Source Code Form is subject to the terms of the Mozilla Public
@@ -18,6 +18,7 @@
#include <fontconfig/fontconfig.h>
#include "gfxPlatformGtk.h"
+//#include "mozilla/FontPropertyTypes.h"
#include "ScreenHelperGTK.h"
#include "gtkdrawing.h"
@@ -31,7 +32,9 @@
#include <cairo-gobject.h>
#include "WidgetStyleCache.h"
#include "prenv.h"
+#include "nsCSSColorUtils.h"
+using namespace mozilla;
using mozilla::LookAndFeel;
#define GDK_COLOR_TO_NS_RGB(c) \
@@ -170,7 +173,7 @@ static bool GetBorderColors(GtkStyleCont
// GTK has an initial value of zero for border-widths, and so themes
// need to explicitly set border-widths to make borders visible.
GtkBorder border;
- gtk_style_context_get_border(aContext, GTK_STATE_FLAG_NORMAL, &border);
+ gtk_style_context_get_border(aContext, state, &border);
visible = border.top != 0 || border.right != 0 || border.bottom != 0 ||
border.left != 0;
}
@@ -199,6 +202,57 @@ static bool GetBorderColors(GtkStyleCont
return ret;
}
+// Finds ideal cell highlight colors used for unfocused+selected cells distinct
+// from both Highlight, used as focused+selected background, and the listbox
+// background which is assumed to be similar to -moz-field
+nsresult nsLookAndFeel::InitCellHighlightColors() {
+ // NS_SUFFICIENT_LUMINOSITY_DIFFERENCE is the a11y standard for text
+ // on a background. Use 20% of that standard since we have a background
+ // on top of another background
+ int32_t minLuminosityDifference = NS_SUFFICIENT_LUMINOSITY_DIFFERENCE / 5;
+ int32_t backLuminosityDifference =
+ NS_LUMINOSITY_DIFFERENCE(mMozWindowBackground, mMozFieldBackground);
+ if (backLuminosityDifference >= minLuminosityDifference) {
+ mMozCellHighlightBackground = mMozWindowBackground;
+ mMozCellHighlightText = mMozWindowText;
+ return NS_OK;
+ }
+
+ uint16_t hue, sat, luminance;
+ uint8_t alpha;
+ mMozCellHighlightBackground = mMozFieldBackground;
+ mMozCellHighlightText = mMozFieldText;
+
+ NS_RGB2HSV(mMozCellHighlightBackground, hue, sat, luminance, alpha);
+
+ uint16_t step = 30;
+ // Lighten the color if the color is very dark
+ if (luminance <= step) {
+ luminance += step;
+ }
+ // Darken it if it is very light
+ else if (luminance >= 255 - step) {
+ luminance -= step;
+ }
+ // Otherwise, compute what works best depending on the text luminance.
+ else {
+ uint16_t textHue, textSat, textLuminance;
+ uint8_t textAlpha;
+ NS_RGB2HSV(mMozCellHighlightText, textHue, textSat, textLuminance,
+ textAlpha);
+ // Text is darker than background, use a lighter shade
+ if (textLuminance < luminance) {
+ luminance += step;
+ }
+ // Otherwise, use a darker shade
+ else {
+ luminance -= step;
+ }
+ }
+ NS_HSV2RGB(mMozCellHighlightBackground, hue, sat, luminance, alpha);
+ return NS_OK;
+}
+
void nsLookAndFeel::NativeInit() { EnsureInit(); }
void nsLookAndFeel::RefreshImpl() {
@@ -248,7 +302,6 @@ nsresult nsLookAndFeel::NativeGetColor(C
case eColorID_IMESelectedRawTextBackground:
case eColorID_IMESelectedConvertedTextBackground:
case eColorID__moz_dragtargetzone:
- case eColorID__moz_cellhighlight:
case eColorID__moz_html_cellhighlight:
case eColorID_highlight: // preference selected item,
aColor = mTextSelectedBackground;
@@ -258,10 +311,15 @@ nsresult nsLookAndFeel::NativeGetColor(C
case eColorID_IMESelectedRawTextForeground:
case eColorID_IMESelectedConvertedTextForeground:
case eColorID_highlighttext:
- case eColorID__moz_cellhighlighttext:
case eColorID__moz_html_cellhighlighttext:
aColor = mTextSelectedText;
break;
+ case eColorID__moz_cellhighlight:
+ aColor = mMozCellHighlightBackground;
+ break;
+ case eColorID__moz_cellhighlighttext:
+ aColor = mMozCellHighlightText;
+ break;
case eColorID_Widget3DHighlight:
aColor = NS_RGB(0xa0, 0xa0, 0xa0);
break;
@@ -961,6 +1019,9 @@ void nsLookAndFeel::EnsureInit() {
mOddCellBackground = GDK_RGBA_TO_NS_RGBA(color);
gtk_style_context_restore(style);
+ // Compute cell highlight colors
+ InitCellHighlightColors();
+
// GtkFrame has a "border" subnode on which Adwaita draws the border.
// Some themes do not draw on this node but draw a border on the widget
// root node, so check the root node if no border is found on the border
diff -up thunderbird-60.5.0/widget/gtk/nsLookAndFeel.h.wayland thunderbird-60.5.0/widget/gtk/nsLookAndFeel.h
--- thunderbird-60.5.0/widget/gtk/nsLookAndFeel.h.wayland 2019-01-22 20:44:03.000000000 +0100
+++ thunderbird-60.5.0/widget/gtk/nsLookAndFeel.h 2019-02-05 14:26:16.977316642 +0100
@@ -1,4 +1,4 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:expandtab:shiftwidth=4:tabstop=4:
*/
/* This Source Code Form is subject to the terms of the Mozilla Public
@@ -8,6 +8,7 @@
#ifndef __nsLookAndFeel
#define __nsLookAndFeel
+#include "X11UndefineNone.h"
#include "nsXPLookAndFeel.h"
#include "nsCOMPtr.h"
#include "gfxFont.h"
@@ -75,6 +76,8 @@ class nsLookAndFeel final : public nsXPL
nscolor mMozWindowActiveBorder;
nscolor mMozWindowInactiveBorder;
nscolor mMozWindowInactiveCaption;
+ nscolor mMozCellHighlightBackground;
+ nscolor mMozCellHighlightText;
nscolor mTextSelectedText;
nscolor mTextSelectedBackground;
nscolor mMozScrollbar;
@@ -89,6 +92,9 @@ class nsLookAndFeel final : public nsXPL
bool mInitialized;
void EnsureInit();
+
+ private:
+ nsresult InitCellHighlightColors();
};
#endif
diff -up thunderbird-60.5.0/widget/gtk/nsWaylandDisplay.cpp.wayland thunderbird-60.5.0/widget/gtk/nsWaylandDisplay.cpp
--- thunderbird-60.5.0/widget/gtk/nsWaylandDisplay.cpp.wayland 2019-02-05 14:26:16.977316642 +0100
+++ thunderbird-60.5.0/widget/gtk/nsWaylandDisplay.cpp 2019-02-05 14:26:16.977316642 +0100
@@ -0,0 +1,222 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:expandtab:shiftwidth=4:tabstop=4:
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsWaylandDisplay.h"
+
+#include "base/message_loop.h" // for MessageLoop
+#include "base/task.h" // for NewRunnableMethod, etc
+#include "mozilla/StaticMutex.h"
+
+namespace mozilla {
+namespace widget {
+
+#define MAX_DISPLAY_CONNECTIONS 2
+
+static nsWaylandDisplay *gWaylandDisplays[MAX_DISPLAY_CONNECTIONS];
+static StaticMutex gWaylandDisplaysMutex;
+
+// Each thread which is using wayland connection (wl_display) has to operate
+// its own wl_event_queue. Main Firefox thread wl_event_queue is handled
+// by Gtk main loop, other threads/wl_event_queue has to be handled by us.
+//
+// nsWaylandDisplay is our interface to wayland compositor. It provides wayland
+// global objects as we need (wl_display, wl_shm) and operates wl_event_queue on
+// compositor (not the main) thread.
+static void WaylandDisplayLoop(wl_display *aDisplay);
+
+// Get WaylandDisplay for given wl_display and actual calling thread.
+static nsWaylandDisplay *WaylandDisplayGetLocked(wl_display *aDisplay,
+ const StaticMutexAutoLock &) {
+ for (auto &display : gWaylandDisplays) {
+ if (display && display->Matches(aDisplay)) {
+ NS_ADDREF(display);
+ return display;
+ }
+ }
+
+ for (auto &display : gWaylandDisplays) {
+ if (display == nullptr) {
+ display = new nsWaylandDisplay(aDisplay);
+ NS_ADDREF(display);
+ return display;
+ }
+ }
+
+ MOZ_CRASH("There's too many wayland display conections!");
+ return nullptr;
+}
+
+nsWaylandDisplay *WaylandDisplayGet(GdkDisplay *aGdkDisplay) {
+ if (!aGdkDisplay) {
+ aGdkDisplay = gdk_display_get_default();
+ }
+
+ // Available as of GTK 3.8+
+ static auto sGdkWaylandDisplayGetWlDisplay = (wl_display * (*)(GdkDisplay *))
+ dlsym(RTLD_DEFAULT, "gdk_wayland_display_get_wl_display");
+
+ wl_display *display = sGdkWaylandDisplayGetWlDisplay(aGdkDisplay);
+
+ StaticMutexAutoLock lock(gWaylandDisplaysMutex);
+ return WaylandDisplayGetLocked(display, lock);
+}
+
+static bool WaylandDisplayReleaseLocked(nsWaylandDisplay *aDisplay,
+ const StaticMutexAutoLock &) {
+ for (auto &display : gWaylandDisplays) {
+ if (display == aDisplay) {
+ int rc = display->Release();
+ if (rc == 0) {
+ display = nullptr;
+ }
+ return true;
+ }
+ }
+ MOZ_ASSERT(false, "Missing nsWaylandDisplay for this thread!");
+ return false;
+}
+
+void WaylandDisplayRelease(nsWaylandDisplay *aDisplay) {
+ StaticMutexAutoLock lock(gWaylandDisplaysMutex);
+ WaylandDisplayReleaseLocked(aDisplay, lock);
+}
+
+static void WaylandDisplayLoopLocked(wl_display *aDisplay,
+ const StaticMutexAutoLock &) {
+ for (auto &display : gWaylandDisplays) {
+ if (display && display->Matches(aDisplay)) {
+ if (display->DisplayLoop()) {
+ MessageLoop::current()->PostDelayedTask(
+ NewRunnableFunction("WaylandDisplayLoop", &WaylandDisplayLoop,
+ aDisplay),
+ EVENT_LOOP_DELAY);
+ }
+ break;
+ }
+ }
+}
+
+static void WaylandDisplayLoop(wl_display *aDisplay) {
+ MOZ_ASSERT(!NS_IsMainThread());
+ StaticMutexAutoLock lock(gWaylandDisplaysMutex);
+ WaylandDisplayLoopLocked(aDisplay, lock);
+}
+
+void nsWaylandDisplay::SetShm(wl_shm *aShm) { mShm = aShm; }
+
+void nsWaylandDisplay::SetSubcompositor(wl_subcompositor *aSubcompositor) {
+ mSubcompositor = aSubcompositor;
+}
+
+void nsWaylandDisplay::SetDataDeviceManager(
+ wl_data_device_manager *aDataDeviceManager) {
+ mDataDeviceManager = aDataDeviceManager;
+}
+
+void nsWaylandDisplay::SetSeat(wl_seat *aSeat) { mSeat = aSeat; }
+
+void nsWaylandDisplay::SetPrimarySelectionDeviceManager(
+ gtk_primary_selection_device_manager *aPrimarySelectionDeviceManager) {
+ mPrimarySelectionDeviceManager = aPrimarySelectionDeviceManager;
+}
+
+static void global_registry_handler(void *data, wl_registry *registry,
+ uint32_t id, const char *interface,
+ uint32_t version) {
+ auto display = reinterpret_cast<nsWaylandDisplay *>(data);
+
+ if (strcmp(interface, "wl_shm") == 0) {
+ auto shm = static_cast<wl_shm *>(
+ wl_registry_bind(registry, id, &wl_shm_interface, 1));
+ wl_proxy_set_queue((struct wl_proxy *)shm, display->GetEventQueue());
+ display->SetShm(shm);
+ } else if (strcmp(interface, "wl_data_device_manager") == 0) {
+ int data_device_manager_version = MIN(version, 3);
+ auto data_device_manager = static_cast<wl_data_device_manager *>(
+ wl_registry_bind(registry, id, &wl_data_device_manager_interface,
+ data_device_manager_version));
+ wl_proxy_set_queue((struct wl_proxy *)data_device_manager,
+ display->GetEventQueue());
+ display->SetDataDeviceManager(data_device_manager);
+ } else if (strcmp(interface, "wl_seat") == 0) {
+ auto seat = static_cast<wl_seat *>(
+ wl_registry_bind(registry, id, &wl_seat_interface, 1));
+ wl_proxy_set_queue((struct wl_proxy *)seat, display->GetEventQueue());
+ display->SetSeat(seat);
+ } else if (strcmp(interface, "gtk_primary_selection_device_manager") == 0) {
+ auto primary_selection_device_manager =
+ static_cast<gtk_primary_selection_device_manager *>(wl_registry_bind(
+ registry, id, &gtk_primary_selection_device_manager_interface, 1));
+ wl_proxy_set_queue((struct wl_proxy *)primary_selection_device_manager,
+ display->GetEventQueue());
+ display->SetPrimarySelectionDeviceManager(primary_selection_device_manager);
+ } else if (strcmp(interface, "wl_subcompositor") == 0) {
+ auto subcompositor = static_cast<wl_subcompositor *>(
+ wl_registry_bind(registry, id, &wl_subcompositor_interface, 1));
+ wl_proxy_set_queue((struct wl_proxy *)subcompositor,
+ display->GetEventQueue());
+ display->SetSubcompositor(subcompositor);
+ }
+}
+
+static void global_registry_remover(void *data, wl_registry *registry,
+ uint32_t id) {}
+
+static const struct wl_registry_listener registry_listener = {
+ global_registry_handler, global_registry_remover};
+
+bool nsWaylandDisplay::DisplayLoop() {
+ wl_display_dispatch_queue_pending(mDisplay, mEventQueue);
+ return true;
+}
+
+bool nsWaylandDisplay::Matches(wl_display *aDisplay) {
+ return mThreadId == PR_GetCurrentThread() && aDisplay == mDisplay;
+}
+
+NS_IMPL_ISUPPORTS(nsWaylandDisplay, nsISupports);
+
+nsWaylandDisplay::nsWaylandDisplay(wl_display *aDisplay)
+ : mThreadId(PR_GetCurrentThread()),
+ mDisplay(aDisplay),
+ mEventQueue(nullptr),
+ mDataDeviceManager(nullptr),
+ mSubcompositor(nullptr),
+ mSeat(nullptr),
+ mShm(nullptr),
+ mPrimarySelectionDeviceManager(nullptr) {
+ wl_registry *registry = wl_display_get_registry(mDisplay);
+ wl_registry_add_listener(registry, &registry_listener, this);
+
+ if (NS_IsMainThread()) {
+ // Use default event queue in main thread operated by Gtk+.
+ mEventQueue = nullptr;
+ wl_display_roundtrip(mDisplay);
+ wl_display_roundtrip(mDisplay);
+ } else {
+ mEventQueue = wl_display_create_queue(mDisplay);
+ MessageLoop::current()->PostTask(NewRunnableFunction(
+ "WaylandDisplayLoop", &WaylandDisplayLoop, mDisplay));
+ wl_proxy_set_queue((struct wl_proxy *)registry, mEventQueue);
+ wl_display_roundtrip_queue(mDisplay, mEventQueue);
+ wl_display_roundtrip_queue(mDisplay, mEventQueue);
+ }
+}
+
+nsWaylandDisplay::~nsWaylandDisplay() {
+ MOZ_ASSERT(mThreadId == PR_GetCurrentThread());
+ // Owned by Gtk+, we don't need to release
+ mDisplay = nullptr;
+
+ if (mEventQueue) {
+ wl_event_queue_destroy(mEventQueue);
+ mEventQueue = nullptr;
+ }
+}
+
+} // namespace widget
+} // namespace mozilla
diff -up thunderbird-60.5.0/widget/gtk/nsWaylandDisplay.h.wayland thunderbird-60.5.0/widget/gtk/nsWaylandDisplay.h
--- thunderbird-60.5.0/widget/gtk/nsWaylandDisplay.h.wayland 2019-02-05 14:26:16.977316642 +0100
+++ thunderbird-60.5.0/widget/gtk/nsWaylandDisplay.h 2019-02-05 14:26:16.977316642 +0100
@@ -0,0 +1,72 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:expandtab:shiftwidth=4:tabstop=4:
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __MOZ_WAYLAND_REGISTRY_H__
+#define __MOZ_WAYLAND_REGISTRY_H__
+
+#include "nsISupports.h"
+#include "mozwayland/mozwayland.h"
+#include "wayland/gtk-primary-selection-client-protocol.h"
+
+namespace mozilla {
+namespace widget {
+
+// TODO: Bug 1467125 - We need to integrate wl_display_dispatch_queue_pending()
+// with compositor event loop.
+#define EVENT_LOOP_DELAY (1000 / 240)
+
+// Our general connection to Wayland display server,
+// holds our display connection and runs event loop.
+class nsWaylandDisplay : public nsISupports {
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ public:
+ explicit nsWaylandDisplay(wl_display* aDisplay);
+
+ bool DisplayLoop();
+ bool Matches(wl_display* aDisplay);
+
+ wl_display* GetDisplay() { return mDisplay; };
+ wl_event_queue* GetEventQueue() { return mEventQueue; };
+ wl_subcompositor* GetSubcompositor(void) { return mSubcompositor; };
+ wl_data_device_manager* GetDataDeviceManager(void) {
+ return mDataDeviceManager;
+ };
+ wl_seat* GetSeat(void) { return mSeat; };
+ wl_shm* GetShm(void) { return mShm; };
+ gtk_primary_selection_device_manager* GetPrimarySelectionDeviceManager(void) {
+ return mPrimarySelectionDeviceManager;
+ };
+
+ public:
+ void SetShm(wl_shm* aShm);
+ void SetSubcompositor(wl_subcompositor* aSubcompositor);
+ void SetDataDeviceManager(wl_data_device_manager* aDataDeviceManager);
+ void SetSeat(wl_seat* aSeat);
+ void SetPrimarySelectionDeviceManager(
+ gtk_primary_selection_device_manager* aPrimarySelectionDeviceManager);
+
+ private:
+ virtual ~nsWaylandDisplay();
+
+ PRThread* mThreadId;
+ wl_display* mDisplay;
+ wl_event_queue* mEventQueue;
+ wl_data_device_manager* mDataDeviceManager;
+ wl_subcompositor* mSubcompositor;
+ wl_seat* mSeat;
+ wl_shm* mShm;
+ gtk_primary_selection_device_manager* mPrimarySelectionDeviceManager;
+};
+
+nsWaylandDisplay* WaylandDisplayGet(GdkDisplay* aGdkDisplay = nullptr);
+void WaylandDisplayRelease(nsWaylandDisplay* aDisplay);
+
+} // namespace widget
+} // namespace mozilla
+
+#endif // __MOZ_WAYLAND_REGISTRY_H__
diff -up thunderbird-60.5.0/widget/gtk/nsWindow.cpp.wayland thunderbird-60.5.0/widget/gtk/nsWindow.cpp
--- thunderbird-60.5.0/widget/gtk/nsWindow.cpp.wayland 2019-01-22 20:44:03.000000000 +0100
+++ thunderbird-60.5.0/widget/gtk/nsWindow.cpp 2019-02-05 14:26:16.978316639 +0100
@@ -1,4 +1,4 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:expandtab:shiftwidth=4:tabstop=4:
*/
/* This Source Code Form is subject to the terms of the Mozilla Public
@@ -18,6 +18,7 @@
#include "mozilla/TouchEvents.h"
#include "mozilla/UniquePtrExtensions.h"
#include "mozilla/WidgetUtils.h"
+#include "mozilla/dom/WheelEventBinding.h"
#include <algorithm>
#include "GeckoProfiler.h"
@@ -25,13 +26,15 @@
#include "prlink.h"
#include "nsGTKToolkit.h"
#include "nsIRollupListener.h"
-#include "nsIDOMNode.h"
+#include "nsINode.h"
#include "nsWidgetsCID.h"
#include "nsDragService.h"
#include "nsIWidgetListener.h"
#include "nsIScreenManager.h"
#include "SystemTimeConverter.h"
+#include "nsIPresShell.h"
+#include "nsViewManager.h"
#include "nsGtkKeyUtils.h"
#include "nsGtkCursors.h"
@@ -56,6 +59,7 @@
#if defined(MOZ_WAYLAND)
#include <gdk/gdkwayland.h>
+#include "nsView.h"
#endif
#include "nsGkAtoms.h"
@@ -116,6 +120,7 @@ using namespace mozilla::widget;
#include "mozilla/layers/CompositorThread.h"
#ifdef MOZ_X11
+#include "GLContextGLX.h" // for GLContextGLX::FindVisual()
#include "GtkCompositorWidget.h"
#include "gfxXlibSurface.h"
#include "WindowSurfaceX11Image.h"
@@ -129,8 +134,6 @@ using namespace mozilla::widget;
#include "nsShmImage.h"
#include "gtkdrawing.h"
-#include "nsIDOMWheelEvent.h"
-
#include "NativeKeyBindings.h"
#include <dlfcn.h>
@@ -140,6 +143,7 @@ using namespace mozilla::gfx;
using namespace mozilla::widget;
using namespace mozilla::layers;
using mozilla::gl::GLContext;
+using mozilla::gl::GLContextGLX;
// Don't put more than this many rects in the dirty region, just fluff
// out to the bounding-box if there are more
@@ -152,9 +156,12 @@ const gint kEvents =
#if GTK_CHECK_VERSION(3, 4, 0)
GDK_SMOOTH_SCROLL_MASK | GDK_TOUCH_MASK |
#endif
- GDK_SCROLL_MASK | GDK_POINTER_MOTION_MASK | GDK_PROPERTY_CHANGE_MASK;
+ GDK_SCROLL_MASK | GDK_POINTER_MOTION_MASK | GDK_PROPERTY_CHANGE_MASK |
+ GDK_FOCUS_CHANGE_MASK;
/* utility functions */
+static void theme_changed_cb(GtkSettings *settings, GParamSpec *pspec,
+ nsWindow *data);
static bool is_mouse_in_window(GdkWindow *aWindow, gdouble aMouseX,
gdouble aMouseY);
static nsWindow *get_window_for_gtk_widget(GtkWidget *widget);
@@ -196,8 +203,6 @@ static void hierarchy_changed_cb(GtkWidg
GtkWidget *previous_toplevel);
static gboolean window_state_event_cb(GtkWidget *widget,
GdkEventWindowState *event);
-static void theme_changed_cb(GtkSettings *settings, GParamSpec *pspec,
- nsWindow *data);
static void check_resize_cb(GtkContainer *container, gpointer user_data);
static void screen_composited_changed_cb(GdkScreen *screen, gpointer user_data);
static void widget_composited_changed_cb(GtkWidget *widget, gpointer user_data);
@@ -550,7 +555,7 @@ static GtkWidget *EnsureInvisibleContain
}
static void CheckDestroyInvisibleContainer() {
- NS_PRECONDITION(gInvisibleContainer, "oh, no");
+ MOZ_ASSERT(gInvisibleContainer, "oh, no");
if (!gdk_window_peek_children(gtk_widget_get_window(gInvisibleContainer))) {
// No children, so not in use.
@@ -639,9 +644,6 @@ void nsWindow::Destroy() {
ClearCachedResources();
- g_signal_handlers_disconnect_by_func(gtk_settings_get_default(),
- FuncToGpointer(theme_changed_cb), this);
-
nsIRollupListener *rollupListener = nsBaseWidget::GetActiveRollupListener();
if (rollupListener) {
nsCOMPtr<nsIWidget> rollupWidget = rollupListener->GetRollupWidget();
@@ -725,7 +727,7 @@ double nsWindow::GetDefaultScaleInternal
DesktopToLayoutDeviceScale nsWindow::GetDesktopToDeviceScale() {
#ifdef MOZ_WAYLAND
GdkDisplay *gdkDisplay = gdk_display_get_default();
- if (GDK_IS_WAYLAND_DISPLAY(gdkDisplay)) {
+ if (!GDK_IS_X11_DISPLAY(gdkDisplay)) {
return DesktopToLayoutDeviceScale(GdkScaleFactor());
}
#endif
@@ -735,8 +737,14 @@ DesktopToLayoutDeviceScale nsWindow::Get
}
void nsWindow::SetParent(nsIWidget *aNewParent) {
- if (mContainer || !mGdkWindow) {
- NS_NOTREACHED("nsWindow::SetParent called illegally");
+ if (!mGdkWindow) {
+ MOZ_ASSERT_UNREACHABLE("The native window has already been destroyed");
+ return;
+ }
+
+ if (mContainer) {
+ // FIXME bug 1469183
+ NS_ERROR("nsWindow should not have a container here");
return;
}
@@ -774,7 +782,7 @@ void nsWindow::SetParent(nsIWidget *aNew
bool nsWindow::WidgetTypeSupportsAcceleration() { return !IsSmallPopup(); }
void nsWindow::ReparentNativeWidget(nsIWidget *aNewParent) {
- NS_PRECONDITION(aNewParent, "");
+ MOZ_ASSERT(aNewParent, "null widget");
NS_ASSERTION(!mIsDestroyed, "");
NS_ASSERTION(!static_cast<nsWindow *>(aNewParent)->mIsDestroyed, "");
@@ -1331,7 +1339,7 @@ LayoutDeviceIntRect nsWindow::GetClientB
}
void nsWindow::UpdateClientOffset() {
- AUTO_PROFILER_LABEL("nsWindow::UpdateClientOffset", GRAPHICS);
+ AUTO_PROFILER_LABEL("nsWindow::UpdateClientOffset", OTHER);
if (!mIsTopLevel || !mShell || !mIsX11Display ||
gtk_window_get_window_type(GTK_WINDOW(mShell)) == GTK_WINDOW_POPUP) {
@@ -1373,9 +1381,7 @@ LayoutDeviceIntPoint nsWindow::GetClient
}
gboolean nsWindow::OnPropertyNotifyEvent(GtkWidget *aWidget,
- GdkEventProperty *aEvent)
-
-{
+ GdkEventProperty *aEvent) {
if (aEvent->atom == gdk_atom_intern("_NET_FRAME_EXTENTS", FALSE)) {
UpdateClientOffset();
@@ -1820,6 +1826,9 @@ gboolean nsWindow::OnExposeEvent(cairo_t
// Windows that are not visible will be painted after they become visible.
if (!mGdkWindow || mIsFullyObscured || !mHasMappedToplevel) return FALSE;
+#ifdef MOZ_WAYLAND
+ if (mContainer && !mContainer->ready_to_draw) return FALSE;
+#endif
nsIWidgetListener *listener = GetListener();
if (!listener) return FALSE;
@@ -3000,6 +3009,33 @@ void nsWindow::OnWindowStateEvent(GtkWid
}
// else the widget is a shell widget.
+ // The block below is a bit evil.
+ //
+ // When a window is resized before it is shown, gtk_window_resize() delays
+ // resizes until the window is shown. If gtk_window_state_event() sees a
+ // GDK_WINDOW_STATE_MAXIMIZED change [1] before the window is shown, then
+ // gtk_window_compute_configure_request_size() ignores the values from the
+ // resize [2]. See bug 1449166 for an example of how this could happen.
+ //
+ // [1] https://gitlab.gnome.org/GNOME/gtk/blob/3.22.30/gtk/gtkwindow.c#L7967
+ // [2] https://gitlab.gnome.org/GNOME/gtk/blob/3.22.30/gtk/gtkwindow.c#L9377
+ //
+ // In order to provide a sensible size for the window when the user exits
+ // maximized state, we hide the GDK_WINDOW_STATE_MAXIMIZED change from
+ // gtk_window_state_event() so as to trick GTK into using the values from
+ // gtk_window_resize() in its configure request.
+ //
+ // We instead notify gtk_window_state_event() of the maximized state change
+ // once the window is shown.
+ if (!mIsShown) {
+ aEvent->changed_mask = static_cast<GdkWindowState>(
+ aEvent->changed_mask & ~GDK_WINDOW_STATE_MAXIMIZED);
+ } else if (aEvent->changed_mask & GDK_WINDOW_STATE_WITHDRAWN &&
+ aEvent->new_window_state & GDK_WINDOW_STATE_MAXIMIZED) {
+ aEvent->changed_mask = static_cast<GdkWindowState>(
+ aEvent->changed_mask | GDK_WINDOW_STATE_MAXIMIZED);
+ }
+
// We don't care about anything but changes in the maximized/icon/fullscreen
// states
if ((aEvent->changed_mask &
@@ -3075,6 +3111,7 @@ void nsWindow::OnDPIChanged() {
// Update menu's font size etc
presShell->ThemeChanged();
}
+ mWidgetListener->UIResolutionChanged();
}
}
@@ -3443,13 +3480,15 @@ nsresult nsWindow::Create(nsIWidget *aPa
gtk_style_context_has_class(style, "csd");
eventWidget = (drawToContainer) ? container : mShell;
- gtk_widget_add_events(eventWidget, kEvents);
- if (drawToContainer)
- gtk_widget_add_events(mShell, GDK_PROPERTY_CHANGE_MASK);
-
// Prevent GtkWindow from painting a background to avoid flickering.
gtk_widget_set_app_paintable(eventWidget, TRUE);
+ gtk_widget_add_events(eventWidget, kEvents);
+ if (drawToContainer) {
+ gtk_widget_add_events(mShell, GDK_PROPERTY_CHANGE_MASK);
+ gtk_widget_set_app_paintable(mShell, TRUE);
+ }
+
// If we draw to mContainer window then configure it now because
// gtk_container_add() realizes the child widget.
gtk_widget_set_has_window(container, drawToContainer);
@@ -3698,6 +3737,15 @@ nsresult nsWindow::Create(nsIWidget *aPa
mXDepth = gdk_visual_get_depth(gdkVisual);
mSurfaceProvider.Initialize(mXDisplay, mXWindow, mXVisual, mXDepth);
+
+ if (mIsTopLevel) {
+ // Set window manager hint to keep fullscreen windows composited.
+ //
+ // If the window were to get unredirected, there could be visible
+ // tearing because Gecko does not align its framebuffer updates with
+ // vblank.
+ SetCompositorHint(GTK_WIDGET_COMPOSIDED_ENABLED);
+ }
}
#ifdef MOZ_WAYLAND
else if (!mIsX11Display) {
@@ -3708,12 +3756,37 @@ nsresult nsWindow::Create(nsIWidget *aPa
return NS_OK;
}
+void nsWindow::RefreshWindowClass(void) {
+ if (mGtkWindowTypeName.IsEmpty() || mGtkWindowRoleName.IsEmpty()) return;
+
+ GdkWindow *gdkWindow = gtk_widget_get_window(mShell);
+ gdk_window_set_role(gdkWindow, mGtkWindowRoleName.get());
+
+#ifdef MOZ_X11
+ if (mIsX11Display) {
+ XClassHint *class_hint = XAllocClassHint();
+ if (!class_hint) {
+ return;
+ }
+ const char *res_class = gdk_get_program_class();
+ if (!res_class) return;
+
+ class_hint->res_name = const_cast<char *>(mGtkWindowTypeName.get());
+ class_hint->res_class = const_cast<char *>(res_class);
+
+ // Can't use gtk_window_set_wmclass() for this; it prints
+ // a warning & refuses to make the change.
+ GdkDisplay *display = gdk_display_get_default();
+ XSetClassHint(GDK_DISPLAY_XDISPLAY(display),
+ gdk_x11_window_get_xid(gdkWindow), class_hint);
+ XFree(class_hint);
+ }
+#endif /* MOZ_X11 */
+}
+
void nsWindow::SetWindowClass(const nsAString &xulWinType) {
if (!mShell) return;
- const char *res_class = gdk_get_program_class();
- if (!res_class) return;
-
char *res_name = ToNewCString(xulWinType);
if (!res_name) return;
@@ -3733,29 +3806,11 @@ void nsWindow::SetWindowClass(const nsAS
res_name[0] = toupper(res_name[0]);
if (!role) role = res_name;
- GdkWindow *gdkWindow = gtk_widget_get_window(mShell);
- gdk_window_set_role(gdkWindow, role);
-
-#ifdef MOZ_X11
- if (mIsX11Display) {
- XClassHint *class_hint = XAllocClassHint();
- if (!class_hint) {
- free(res_name);
- return;
- }
- class_hint->res_name = res_name;
- class_hint->res_class = const_cast<char *>(res_class);
-
- // Can't use gtk_window_set_wmclass() for this; it prints
- // a warning & refuses to make the change.
- GdkDisplay *display = gdk_display_get_default();
- XSetClassHint(GDK_DISPLAY_XDISPLAY(display),
- gdk_x11_window_get_xid(gdkWindow), class_hint);
- XFree(class_hint);
- }
-#endif /* MOZ_X11 */
-
+ mGtkWindowTypeName = res_name;
+ mGtkWindowRoleName = role;
free(res_name);
+
+ RefreshWindowClass();
}
void nsWindow::NativeResize() {
@@ -3820,6 +3875,8 @@ void nsWindow::NativeMoveResize() {
NativeShow(false);
}
NativeMove();
+
+ return;
}
GdkRectangle size = DevicePixelsToGdkSizeRoundUp(mBounds.Size());
@@ -3832,6 +3889,8 @@ void nsWindow::NativeMoveResize() {
// x and y give the position of the window manager frame top-left.
gtk_window_move(GTK_WINDOW(mShell), topLeft.x, topLeft.y);
// This sets the client window size.
+ MOZ_ASSERT(size.width > 0 && size.height > 0,
+ "Can't resize window smaller than 1x1.");
gtk_window_resize(GTK_WINDOW(mShell), size.width, size.height);
} else if (mContainer) {
GtkAllocation allocation;
@@ -3877,6 +3936,16 @@ void nsWindow::NativeShow(bool aAction)
gdk_window_show_unraised(mGdkWindow);
}
} else {
+#ifdef MOZ_WAYLAND
+ if (mContainer && moz_container_has_wl_egl_window(mContainer)) {
+ // Because wl_egl_window is destroyed on moz_container_unmap(),
+ // the current compositor cannot use it anymore. To avoid crash,
+ // destroy the compositor & recreate a new compositor on next
+ // expose event.
+ DestroyLayerManager();
+ }
+#endif
+
if (mIsTopLevel) {
// Workaround window freezes on GTK versions before 3.21.2 by
// ensuring that configure events get dispatched to windows before
@@ -5436,9 +5505,10 @@ void nsWindow::InitDragEvent(WidgetDragE
KeymapWrapper::InitInputEvent(aEvent, modifierState);
}
-static gboolean drag_motion_event_cb(GtkWidget *aWidget,
- GdkDragContext *aDragContext, gint aX,
- gint aY, guint aTime, gpointer aData) {
+gboolean WindowDragMotionHandler(GtkWidget *aWidget,
+ GdkDragContext *aDragContext,
+ nsWaylandDragContext *aWaylandDragContext,
+ gint aX, gint aY, guint aTime) {
RefPtr<nsWindow> window = get_window_for_gtk_widget(aWidget);
if (!window) return FALSE;
@@ -5459,13 +5529,17 @@ static gboolean drag_motion_event_cb(Gtk
LayoutDeviceIntPoint point = window->GdkPointToDevicePixels({retx, rety});
RefPtr<nsDragService> dragService = nsDragService::GetInstance();
- return dragService->ScheduleMotionEvent(innerMostWindow, aDragContext, point,
- aTime);
+ return dragService->ScheduleMotionEvent(innerMostWindow, aDragContext,
+ aWaylandDragContext, point, aTime);
}
-static void drag_leave_event_cb(GtkWidget *aWidget,
- GdkDragContext *aDragContext, guint aTime,
- gpointer aData) {
+static gboolean drag_motion_event_cb(GtkWidget *aWidget,
+ GdkDragContext *aDragContext, gint aX,
+ gint aY, guint aTime, gpointer aData) {
+ return WindowDragMotionHandler(aWidget, aDragContext, nullptr, aX, aY, aTime);
+}
+
+void WindowDragLeaveHandler(GtkWidget *aWidget) {
RefPtr<nsWindow> window = get_window_for_gtk_widget(aWidget);
if (!window) return;
@@ -5495,9 +5569,15 @@ static void drag_leave_event_cb(GtkWidge
dragService->ScheduleLeaveEvent();
}
-static gboolean drag_drop_event_cb(GtkWidget *aWidget,
- GdkDragContext *aDragContext, gint aX,
- gint aY, guint aTime, gpointer aData) {
+static void drag_leave_event_cb(GtkWidget *aWidget,
+ GdkDragContext *aDragContext, guint aTime,
+ gpointer aData) {
+ WindowDragLeaveHandler(aWidget);
+}
+
+gboolean WindowDragDropHandler(GtkWidget *aWidget, GdkDragContext *aDragContext,
+ nsWaylandDragContext *aWaylandDragContext,
+ gint aX, gint aY, guint aTime) {
RefPtr<nsWindow> window = get_window_for_gtk_widget(aWidget);
if (!window) return FALSE;
@@ -5518,8 +5598,14 @@ static gboolean drag_drop_event_cb(GtkWi
LayoutDeviceIntPoint point = window->GdkPointToDevicePixels({retx, rety});
RefPtr<nsDragService> dragService = nsDragService::GetInstance();
- return dragService->ScheduleDropEvent(innerMostWindow, aDragContext, point,
- aTime);
+ return dragService->ScheduleDropEvent(innerMostWindow, aDragContext,
+ aWaylandDragContext, point, aTime);
+}
+
+static gboolean drag_drop_event_cb(GtkWidget *aWidget,
+ GdkDragContext *aDragContext, gint aX,
+ gint aY, guint aTime, gpointer aData) {
+ return WindowDragDropHandler(aWidget, aDragContext, nullptr, aX, aY, aTime);
}
static void drag_data_received_event_cb(GtkWidget *aWidget,
@@ -5877,11 +5963,6 @@ nsIWidget::LayerManager *nsWindow::GetLa
return mLayerManager;
}
- if (!mLayerManager && !IsComposited() &&
- eTransparencyTransparent == GetTransparencyMode()) {
- mLayerManager = CreateBasicLayerManager();
- }
-
return nsBaseWidget::GetLayerManager(aShadowManager, aBackendHint,
aPersistence);
}
@@ -5919,6 +6000,13 @@ void nsWindow::ClearCachedResources() {
* It works only for CSD decorated GtkWindow.
*/
void nsWindow::UpdateClientOffsetForCSDWindow() {
+ // We update window offset on X11 as the window position is calculated
+ // relatively to mShell. We don't do that on Wayland as our wl_subsurface
+ // is attached to mContainer and mShell is ignored.
+ if (!mIsX11Display) {
+ return;
+ }
+
// _NET_FRAME_EXTENTS is not set on client decorated windows,
// so we need to read offset between mContainer and toplevel mShell
// window.
@@ -6005,6 +6093,15 @@ void nsWindow::SetDrawsInTitlebar(bool a
mNeedsShow = true;
NativeResize();
+ // Label mShell toplevel window so property_notify_event_cb callback
+ // can find its way home.
+ g_object_set_data(G_OBJECT(gtk_widget_get_window(mShell)), "nsWindow",
+ this);
+#ifdef MOZ_X11
+ SetCompositorHint(GTK_WIDGET_COMPOSIDED_ENABLED);
+#endif
+ RefreshWindowClass();
+
// When we use system titlebar setup managed by Gtk+ we also get
// _NET_FRAME_EXTENTS property for our toplevel window so we can't
// update the client offset it here.
@@ -6019,13 +6116,11 @@ void nsWindow::SetDrawsInTitlebar(bool a
}
gint nsWindow::GdkScaleFactor() {
-#if (MOZ_WIDGET_GTK >= 3)
// Available as of GTK 3.10+
static auto sGdkWindowGetScaleFactorPtr =
(gint(*)(GdkWindow *))dlsym(RTLD_DEFAULT, "gdk_window_get_scale_factor");
if (sGdkWindowGetScaleFactorPtr && mGdkWindow)
return (*sGdkWindowGetScaleFactorPtr)(mGdkWindow);
-#endif
return ScreenHelperGTK::GetGTKMonitorScaleFactor();
}
@@ -6287,6 +6382,8 @@ nsWindow::CSDSupportLevel nsWindow::GetS
// KDE Plasma
} else if (strstr(currentDesktop, "KDE") != nullptr) {
sCSDSupportLevel = CSD_SUPPORT_CLIENT;
+ } else if (strstr(currentDesktop, "Enlightenment") != nullptr) {
+ sCSDSupportLevel = CSD_SUPPORT_CLIENT;
} else if (strstr(currentDesktop, "LXDE") != nullptr) {
sCSDSupportLevel = CSD_SUPPORT_CLIENT;
} else if (strstr(currentDesktop, "openbox") != nullptr) {
@@ -6303,6 +6400,8 @@ nsWindow::CSDSupportLevel nsWindow::GetS
sCSDSupportLevel = CSD_SUPPORT_SYSTEM;
} else if (strstr(currentDesktop, "LXQt") != nullptr) {
sCSDSupportLevel = CSD_SUPPORT_SYSTEM;
+ } else if (strstr(currentDesktop, "Deepin") != nullptr) {
+ sCSDSupportLevel = CSD_SUPPORT_SYSTEM;
} else {
// Release or beta builds are not supposed to be broken
// so disable titlebar rendering on untested/unknown systems.
@@ -6351,34 +6450,19 @@ int32_t nsWindow::RoundsWidgetCoordinate
void nsWindow::GetCompositorWidgetInitData(
mozilla::widget::CompositorWidgetInitData *aInitData) {
+ // Make sure the window XID is propagated to X server, we can fail otherwise
+ // in GPU process (Bug 1401634).
+ if (mXDisplay && mXWindow != X11None) {
+ XFlush(mXDisplay);
+ }
+
*aInitData = mozilla::widget::GtkCompositorWidgetInitData(
(mXWindow != X11None) ? mXWindow : (uintptr_t) nullptr,
mXDisplay ? nsCString(XDisplayString(mXDisplay)) : nsCString(),
GetClientSize());
}
-bool nsWindow::IsComposited() const {
- if (!mGdkWindow) {
- NS_WARNING("nsWindow::HasARGBVisual called before realization!");
- return false;
- }
-
- GdkScreen *gdkScreen = gdk_screen_get_default();
- return gdk_screen_is_composited(gdkScreen) &&
- (gdk_window_get_visual(mGdkWindow) ==
- gdk_screen_get_rgba_visual(gdkScreen));
-}
-
#ifdef MOZ_WAYLAND
-wl_display *nsWindow::GetWaylandDisplay() {
- // Available as of GTK 3.8+
- static auto sGdkWaylandDisplayGetWlDisplay = (wl_display * (*)(GdkDisplay *))
- dlsym(RTLD_DEFAULT, "gdk_wayland_display_get_wl_display");
-
- GdkDisplay *gdkDisplay = gdk_display_get_default();
- return mIsX11Display ? nullptr : sGdkWaylandDisplayGetWlDisplay(gdkDisplay);
-}
-
wl_surface *nsWindow::GetWaylandSurface() {
if (mContainer)
return moz_container_get_wl_surface(MOZ_CONTAINER(mContainer));
@@ -6388,4 +6472,80 @@ wl_surface *nsWindow::GetWaylandSurface(
"drawing!");
return nullptr;
}
+
+bool nsWindow::WaylandSurfaceNeedsClear() {
+ if (mContainer) {
+ return moz_container_surface_needs_clear(MOZ_CONTAINER(mContainer));
+ }
+
+ NS_WARNING(
+ "nsWindow::WaylandSurfaceNeedsClear(): We don't have any mContainer!");
+ return false;
+}
#endif
+
+#ifdef MOZ_X11
+/* XApp progress support currently works by setting a property
+ * on a window with this Atom name. A supporting window manager
+ * will notice this and pass it along to whatever handling has
+ * been implemented on that end (e.g. passing it on to a taskbar
+ * widget.) There is no issue if WM support is lacking, this is
+ * simply ignored in that case.
+ *
+ * See https://github.com/linuxmint/xapps/blob/master/libxapp/xapp-gtk-window.c
+ * for further details.
+ */
+
+#define PROGRESS_HINT "_NET_WM_XAPP_PROGRESS"
+
+static void set_window_hint_cardinal(Window xid, const gchar *atom_name,
+ gulong cardinal) {
+ GdkDisplay *display;
+
+ display = gdk_display_get_default();
+
+ if (cardinal > 0) {
+ XChangeProperty(GDK_DISPLAY_XDISPLAY(display), xid,
+ gdk_x11_get_xatom_by_name_for_display(display, atom_name),
+ XA_CARDINAL, 32, PropModeReplace, (guchar *)&cardinal, 1);
+ } else {
+ XDeleteProperty(GDK_DISPLAY_XDISPLAY(display), xid,
+ gdk_x11_get_xatom_by_name_for_display(display, atom_name));
+ }
+}
+#endif // MOZ_X11
+
+void nsWindow::SetProgress(unsigned long progressPercent) {
+#ifdef MOZ_X11
+
+ if (!mIsX11Display) {
+ return;
+ }
+
+ if (!mShell) {
+ return;
+ }
+
+ progressPercent = MIN(progressPercent, 100);
+
+ set_window_hint_cardinal(GDK_WINDOW_XID(gtk_widget_get_window(mShell)),
+ PROGRESS_HINT, progressPercent);
+#endif // MOZ_X11
+}
+
+#ifdef MOZ_X11
+void nsWindow::SetCompositorHint(WindowComposeRequest aState) {
+ if (mIsX11Display &&
+ (!GetLayerManager() ||
+ GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_BASIC)) {
+ gulong value = aState;
+ GdkAtom cardinal_atom = gdk_x11_xatom_to_atom(XA_CARDINAL);
+ gdk_property_change(gtk_widget_get_window(mShell),
+ gdk_atom_intern("_NET_WM_BYPASS_COMPOSITOR", FALSE),
+ cardinal_atom,
+ 32, // format
+ GDK_PROP_MODE_REPLACE, (guchar *)&value, 1);
+ }
+}
+#endif
+
diff -up thunderbird-60.5.0/widget/gtk/nsWindow.h.wayland thunderbird-60.5.0/widget/gtk/nsWindow.h
--- thunderbird-60.5.0/widget/gtk/nsWindow.h.wayland 2019-01-22 20:44:03.000000000 +0100
+++ thunderbird-60.5.0/widget/gtk/nsWindow.h 2019-02-05 14:26:16.978316639 +0100
@@ -1,4 +1,4 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:expandtab:shiftwidth=4:tabstop=4:
*/
/* This Source Code Form is subject to the terms of the Mozilla Public
@@ -8,19 +8,8 @@
#ifndef __nsWindow_h__
#define __nsWindow_h__
-#include "mozcontainer.h"
-#include "mozilla/RefPtr.h"
-#include "mozilla/UniquePtr.h"
-#include "nsIDragService.h"
-#include "nsITimer.h"
-#include "nsGkAtoms.h"
-#include "nsRefPtrHashtable.h"
-
-#include "nsBaseWidget.h"
-#include "CompositorWidget.h"
#include <gdk/gdk.h>
#include <gtk/gtk.h>
-
#ifdef MOZ_X11
#include <gdk/gdkx.h>
#include "X11UndefineNone.h"
@@ -28,7 +17,16 @@
#ifdef MOZ_WAYLAND
#include <gdk/gdkwayland.h>
#endif
-
+#include "mozcontainer.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/UniquePtr.h"
+#include "nsIDragService.h"
+#include "nsITimer.h"
+#include "nsGkAtoms.h"
+#include "nsRefPtrHashtable.h"
+#include "nsIFrame.h"
+#include "nsBaseWidget.h"
+#include "CompositorWidget.h"
#include "mozilla/widget/WindowSurface.h"
#include "mozilla/widget/WindowSurfaceProvider.h"
@@ -66,6 +64,19 @@ extern mozilla::LazyLogModule gWidgetDra
#endif /* MOZ_LOGGING */
+#ifdef MOZ_WAYLAND
+class nsWaylandDragContext;
+
+gboolean WindowDragMotionHandler(GtkWidget* aWidget,
+ GdkDragContext* aDragContext,
+ nsWaylandDragContext* aWaylandDragContext,
+ gint aX, gint aY, guint aTime);
+gboolean WindowDragDropHandler(GtkWidget* aWidget, GdkDragContext* aDragContext,
+ nsWaylandDragContext* aWaylandDragContext,
+ gint aX, gint aY, guint aTime);
+void WindowDragLeaveHandler(GtkWidget* aWidget);
+#endif
+
class gfxPattern;
namespace mozilla {
@@ -77,6 +88,7 @@ class nsWindow final : public nsBaseWidg
public:
typedef mozilla::gfx::DrawTarget DrawTarget;
typedef mozilla::WidgetEventTime WidgetEventTime;
+ typedef mozilla::WidgetKeyboardEvent WidgetKeyboardEvent;
typedef mozilla::widget::PlatformCompositorWidgetDelegate
PlatformCompositorWidgetDelegate;
@@ -216,6 +228,8 @@ class nsWindow final : public nsBaseWidg
mozilla::gfx::DrawTarget* aDrawTarget,
LayoutDeviceIntRegion& aInvalidRegion) override;
+ void SetProgress(unsigned long progressPercent);
+
private:
void UpdateAlpha(mozilla::gfx::SourceSurface* aSourceSurface,
nsIntRect aBoundsRect);
@@ -335,6 +349,7 @@ class nsWindow final : public nsBaseWidg
#ifdef MOZ_WAYLAND
wl_display* GetWaylandDisplay();
wl_surface* GetWaylandSurface();
+ bool WaylandSurfaceNeedsClear();
#endif
virtual void GetCompositorWidgetInitData(
mozilla::widget::CompositorWidgetInitData* aInitData) override;
@@ -436,13 +451,23 @@ class nsWindow final : public nsBaseWidg
gint* aButton, gint* aRootX, gint* aRootY);
void ClearCachedResources();
nsIWidgetListener* GetListener();
- bool IsComposited() const;
void UpdateClientOffsetForCSDWindow();
nsWindow* GetTransientForWindowIfPopup();
bool IsHandlingTouchSequence(GdkEventSequence* aSequence);
+#ifdef MOZ_X11
+ typedef enum {GTK_WIDGET_COMPOSIDED_DEFAULT = 0,
+ GTK_WIDGET_COMPOSIDED_DISABLED = 1,
+ GTK_WIDGET_COMPOSIDED_ENABLED = 2} WindowComposeRequest;
+
+ void SetCompositorHint(WindowComposeRequest aState);
+#endif
+ nsCString mGtkWindowTypeName;
+ nsCString mGtkWindowRoleName;
+ void RefreshWindowClass();
+
GtkWidget* mShell;
MozContainer* mContainer;
GdkWindow* mGdkWindow;
diff -up thunderbird-60.5.0/widget/gtk/WindowSurfaceProvider.h.wayland thunderbird-60.5.0/widget/gtk/WindowSurfaceProvider.h
--- thunderbird-60.5.0/widget/gtk/WindowSurfaceProvider.h.wayland 2019-01-22 20:44:04.000000000 +0100
+++ thunderbird-60.5.0/widget/gtk/WindowSurfaceProvider.h 2019-02-05 14:26:16.978316639 +0100
@@ -1,4 +1,4 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -17,6 +17,7 @@
#include <gdk/gdkwayland.h>
#endif
#include <X11/Xlib.h> // for Window, Display, Visual, etc.
+#include "X11UndefineNone.h"
class nsWindow;
@@ -70,6 +71,7 @@ class WindowSurfaceProvider final {
#ifdef MOZ_WAYLAND
nsWindow* mWidget;
#endif
+ bool mIsShaped;
};
} // namespace widget
diff -up thunderbird-60.5.0/widget/gtk/WindowSurfaceWayland.cpp.wayland thunderbird-60.5.0/widget/gtk/WindowSurfaceWayland.cpp
--- thunderbird-60.5.0/widget/gtk/WindowSurfaceWayland.cpp.wayland 2019-01-22 20:44:04.000000000 +0100
+++ thunderbird-60.5.0/widget/gtk/WindowSurfaceWayland.cpp 2019-02-05 14:26:16.979316635 +0100
@@ -1,27 +1,28 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "nsWaylandDisplay.h"
#include "WindowSurfaceWayland.h"
-#include "base/message_loop.h" // for MessageLoop
-#include "base/task.h" // for NewRunnableMethod, etc
#include "nsPrintfCString.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/Tools.h"
#include "gfxPlatform.h"
#include "mozcontainer.h"
-#include "nsCOMArray.h"
-#include "mozilla/StaticMutex.h"
+#include "nsTArray.h"
+#include "base/message_loop.h" // for MessageLoop
+#include "base/task.h" // for NewRunnableMethod, etc
-#include <gdk/gdkwayland.h>
#include <sys/mman.h>
-#include <assert.h>
#include <fcntl.h>
#include <errno.h>
+namespace mozilla {
+namespace widget {
+
/*
Wayland multi-thread rendering scheme
@@ -131,188 +132,16 @@ handle to wayland compositor by WindowBa
(wl_buffer/wl_surface).
*/
-namespace mozilla {
-namespace widget {
-
#define BUFFER_BPP 4
-
-// TODO: How many rendering threads do we actualy handle?
-static nsCOMArray<nsWaylandDisplay> gWaylandDisplays;
-static StaticMutex gWaylandDisplaysMutex;
-
-// Each thread which is using wayland connection (wl_display) has to operate
-// its own wl_event_queue. Main Firefox thread wl_event_queue is handled
-// by Gtk main loop, other threads/wl_event_queue has to be handled by us.
-//
-// nsWaylandDisplay is our interface to wayland compositor. It provides wayland
-// global objects as we need (wl_display, wl_shm) and operates wl_event_queue on
-// compositor (not the main) thread.
-static nsWaylandDisplay *WaylandDisplayGet(wl_display *aDisplay);
-static void WaylandDisplayRelease(wl_display *aDisplay);
-static void WaylandDisplayLoop(wl_display *aDisplay);
-
-// TODO: is the 60pfs loop correct?
-#define EVENT_LOOP_DELAY (1000 / 60)
-
-// Get WaylandDisplay for given wl_display and actual calling thread.
-static nsWaylandDisplay *WaylandDisplayGetLocked(wl_display *aDisplay,
- const StaticMutexAutoLock &) {
- nsWaylandDisplay *waylandDisplay = nullptr;
-
- int len = gWaylandDisplays.Count();
- for (int i = 0; i < len; i++) {
- if (gWaylandDisplays[i]->Matches(aDisplay)) {
- waylandDisplay = gWaylandDisplays[i];
- break;
- }
- }
-
- if (!waylandDisplay) {
- waylandDisplay = new nsWaylandDisplay(aDisplay);
- gWaylandDisplays.AppendObject(waylandDisplay);
- }
-
- NS_ADDREF(waylandDisplay);
- return waylandDisplay;
-}
-
-static nsWaylandDisplay *WaylandDisplayGet(wl_display *aDisplay) {
- StaticMutexAutoLock lock(gWaylandDisplaysMutex);
- return WaylandDisplayGetLocked(aDisplay, lock);
-}
-
-static bool WaylandDisplayReleaseLocked(wl_display *aDisplay,
- const StaticMutexAutoLock &) {
- int len = gWaylandDisplays.Count();
- for (int i = 0; i < len; i++) {
- if (gWaylandDisplays[i]->Matches(aDisplay)) {
- int rc = gWaylandDisplays[i]->Release();
- // nsCOMArray::AppendObject()/RemoveObjectAt() also call
- // AddRef()/Release() so remove WaylandDisplay when ref count is 1.
- if (rc == 1) {
- gWaylandDisplays.RemoveObjectAt(i);
- }
- return true;
- }
- }
- MOZ_ASSERT(false, "Missing nsWaylandDisplay for this thread!");
- return false;
-}
-
-static void WaylandDisplayRelease(wl_display *aDisplay) {
- StaticMutexAutoLock lock(gWaylandDisplaysMutex);
- WaylandDisplayReleaseLocked(aDisplay, lock);
-}
-
-static void WaylandDisplayLoopLocked(wl_display *aDisplay,
- const StaticMutexAutoLock &) {
- int len = gWaylandDisplays.Count();
- for (int i = 0; i < len; i++) {
- if (gWaylandDisplays[i]->Matches(aDisplay)) {
- if (gWaylandDisplays[i]->DisplayLoop()) {
- MessageLoop::current()->PostDelayedTask(
- NewRunnableFunction("WaylandDisplayLoop", &WaylandDisplayLoop,
- aDisplay),
- EVENT_LOOP_DELAY);
- }
- break;
- }
- }
-}
-
-static void WaylandDisplayLoop(wl_display *aDisplay) {
- MOZ_ASSERT(!NS_IsMainThread());
- StaticMutexAutoLock lock(gWaylandDisplaysMutex);
- WaylandDisplayLoopLocked(aDisplay, lock);
-}
-
-static void global_registry_handler(void *data, wl_registry *registry,
- uint32_t id, const char *interface,
- uint32_t version) {
- if (strcmp(interface, "wl_shm") == 0) {
- auto interface = reinterpret_cast<nsWaylandDisplay *>(data);
- auto shm = static_cast<wl_shm *>(
- wl_registry_bind(registry, id, &wl_shm_interface, 1));
- wl_proxy_set_queue((struct wl_proxy *)shm, interface->GetEventQueue());
- interface->SetShm(shm);
- }
-}
-
-static void global_registry_remover(void *data, wl_registry *registry,
- uint32_t id) {}
-
-static const struct wl_registry_listener registry_listener = {
- global_registry_handler, global_registry_remover};
-
-wl_shm *nsWaylandDisplay::GetShm() {
- MOZ_ASSERT(mThreadId == PR_GetCurrentThread());
-
- if (!mShm) {
- // wl_shm is not provided by Gtk so we need to query wayland directly
- // See weston/simple-shm.c and create_display() for reference.
- wl_registry *registry = wl_display_get_registry(mDisplay);
- wl_registry_add_listener(registry, &registry_listener, this);
-
- wl_proxy_set_queue((struct wl_proxy *)registry, mEventQueue);
- if (mEventQueue) {
- wl_display_roundtrip_queue(mDisplay, mEventQueue);
- } else {
- wl_display_roundtrip(mDisplay);
- }
-
- MOZ_RELEASE_ASSERT(mShm, "Wayland registry query failed!");
- }
-
- return (mShm);
-}
-
-bool nsWaylandDisplay::DisplayLoop() {
- wl_display_dispatch_queue_pending(mDisplay, mEventQueue);
- return true;
-}
-
-bool nsWaylandDisplay::Matches(wl_display *aDisplay) {
- return mThreadId == PR_GetCurrentThread() && aDisplay == mDisplay;
-}
-
-NS_IMPL_ISUPPORTS(nsWaylandDisplay, nsISupports);
-
-nsWaylandDisplay::nsWaylandDisplay(wl_display *aDisplay)
- : mThreadId(PR_GetCurrentThread())
- // gfx::SurfaceFormat::B8G8R8A8 is a basic Wayland format
- // and is always present.
- ,
- mFormat(gfx::SurfaceFormat::B8G8R8A8),
- mShm(nullptr),
- mDisplay(aDisplay) {
- if (NS_IsMainThread()) {
- // Use default event queue in main thread operated by Gtk+.
- mEventQueue = nullptr;
- } else {
- mEventQueue = wl_display_create_queue(mDisplay);
- MessageLoop::current()->PostTask(NewRunnableFunction(
- "WaylandDisplayLoop", &WaylandDisplayLoop, mDisplay));
- }
-}
-
-nsWaylandDisplay::~nsWaylandDisplay() {
- MOZ_ASSERT(mThreadId == PR_GetCurrentThread());
- // Owned by Gtk+, we don't need to release
- mDisplay = nullptr;
-
- if (mEventQueue) {
- wl_event_queue_destroy(mEventQueue);
- mEventQueue = nullptr;
- }
-}
+gfx::SurfaceFormat WindowBackBuffer::mFormat = gfx::SurfaceFormat::B8G8R8A8;
int WaylandShmPool::CreateTemporaryFile(int aSize) {
- const char *tmppath = getenv("XDG_RUNTIME_DIR");
+ const char* tmppath = getenv("XDG_RUNTIME_DIR");
MOZ_RELEASE_ASSERT(tmppath, "Missing XDG_RUNTIME_DIR env variable.");
nsPrintfCString tmpname("%s/mozilla-shared-XXXXXX", tmppath);
- char *filename;
+ char* filename;
int fd = -1;
int ret = 0;
@@ -353,7 +182,7 @@ int WaylandShmPool::CreateTemporaryFile(
return fd;
}
-WaylandShmPool::WaylandShmPool(nsWaylandDisplay *aWaylandDisplay, int aSize)
+WaylandShmPool::WaylandShmPool(nsWaylandDisplay* aWaylandDisplay, int aSize)
: mAllocatedSize(aSize) {
mShmPoolFd = CreateTemporaryFile(mAllocatedSize);
mImageData = mmap(nullptr, mAllocatedSize, PROT_READ | PROT_WRITE, MAP_SHARED,
@@ -365,7 +194,7 @@ WaylandShmPool::WaylandShmPool(nsWayland
wl_shm_create_pool(aWaylandDisplay->GetShm(), mShmPoolFd, mAllocatedSize);
// We set our queue to get mShmPool events at compositor thread.
- wl_proxy_set_queue((struct wl_proxy *)mShmPool,
+ wl_proxy_set_queue((struct wl_proxy*)mShmPool,
aWaylandDisplay->GetEventQueue());
}
@@ -394,7 +223,7 @@ bool WaylandShmPool::Resize(int aSize) {
return true;
}
-void WaylandShmPool::SetImageDataFromPool(class WaylandShmPool *aSourcePool,
+void WaylandShmPool::SetImageDataFromPool(class WaylandShmPool* aSourcePool,
int aImageDataSize) {
MOZ_ASSERT(mAllocatedSize >= aImageDataSize, "WaylandShmPool overflows!");
memcpy(mImageData, aSourcePool->GetImageData(), aImageDataSize);
@@ -406,8 +235,8 @@ WaylandShmPool::~WaylandShmPool() {
close(mShmPoolFd);
}
-static void buffer_release(void *data, wl_buffer *buffer) {
- auto surface = reinterpret_cast<WindowBackBuffer *>(data);
+static void buffer_release(void* data, wl_buffer* buffer) {
+ auto surface = reinterpret_cast<WindowBackBuffer*>(data);
surface->Detach();
}
@@ -422,7 +251,7 @@ void WindowBackBuffer::Create(int aWidth
mWaylandBuffer =
wl_shm_pool_create_buffer(mShmPool.GetShmPool(), 0, aWidth, aHeight,
aWidth * BUFFER_BPP, WL_SHM_FORMAT_ARGB8888);
- wl_proxy_set_queue((struct wl_proxy *)mWaylandBuffer,
+ wl_proxy_set_queue((struct wl_proxy*)mWaylandBuffer,
mWaylandDisplay->GetEventQueue());
wl_buffer_add_listener(mWaylandBuffer, &buffer_listener, this);
@@ -435,7 +264,11 @@ void WindowBackBuffer::Release() {
mWidth = mHeight = 0;
}
-WindowBackBuffer::WindowBackBuffer(nsWaylandDisplay *aWaylandDisplay,
+void WindowBackBuffer::Clear() {
+ memset(mShmPool.GetImageData(), 0, mHeight * mWidth * BUFFER_BPP);
+}
+
+WindowBackBuffer::WindowBackBuffer(nsWaylandDisplay* aWaylandDisplay,
int aWidth, int aHeight)
: mShmPool(aWaylandDisplay, aWidth * aHeight * BUFFER_BPP),
mWaylandBuffer(nullptr),
@@ -457,7 +290,7 @@ bool WindowBackBuffer::Resize(int aWidth
return (mWaylandBuffer != nullptr);
}
-void WindowBackBuffer::Attach(wl_surface *aSurface) {
+void WindowBackBuffer::Attach(wl_surface* aSurface) {
wl_surface_attach(aSurface, mWaylandBuffer, 0, 0);
wl_surface_commit(aSurface);
wl_display_flush(mWaylandDisplay->GetDisplay());
@@ -466,8 +299,8 @@ void WindowBackBuffer::Attach(wl_surface
void WindowBackBuffer::Detach() { mAttached = false; }
-bool WindowBackBuffer::SetImageDataFromBackBuffer(
- class WindowBackBuffer *aSourceBuffer) {
+bool WindowBackBuffer::SetImageDataFromBuffer(
+ class WindowBackBuffer* aSourceBuffer) {
if (!IsMatchingSize(aSourceBuffer)) {
Resize(aSourceBuffer->mWidth, aSourceBuffer->mHeight);
}
@@ -478,204 +311,381 @@ bool WindowBackBuffer::SetImageDataFromB
return true;
}
-already_AddRefed<gfx::DrawTarget> WindowBackBuffer::Lock(
- const LayoutDeviceIntRegion &aRegion) {
- gfx::IntRect bounds = aRegion.GetBounds().ToUnknownRect();
- gfx::IntSize lockSize(bounds.XMost(), bounds.YMost());
-
+already_AddRefed<gfx::DrawTarget> WindowBackBuffer::Lock() {
+ gfx::IntSize lockSize(mWidth, mHeight);
return gfxPlatform::CreateDrawTargetForData(
- static_cast<unsigned char *>(mShmPool.GetImageData()), lockSize,
- BUFFER_BPP * mWidth, mWaylandDisplay->GetSurfaceFormat());
+ static_cast<unsigned char*>(mShmPool.GetImageData()), lockSize,
+ BUFFER_BPP * mWidth, mFormat);
}
-static void frame_callback_handler(void *data, struct wl_callback *callback,
+static void frame_callback_handler(void* data, struct wl_callback* callback,
uint32_t time) {
- auto surface = reinterpret_cast<WindowSurfaceWayland *>(data);
+ auto surface = reinterpret_cast<WindowSurfaceWayland*>(data);
surface->FrameCallbackHandler();
}
static const struct wl_callback_listener frame_listener = {
frame_callback_handler};
-WindowSurfaceWayland::WindowSurfaceWayland(nsWindow *aWindow)
+WindowSurfaceWayland::WindowSurfaceWayland(nsWindow* aWindow)
: mWindow(aWindow),
- mWaylandDisplay(WaylandDisplayGet(aWindow->GetWaylandDisplay())),
- mFrontBuffer(nullptr),
- mBackBuffer(nullptr),
+ mWaylandDisplay(WaylandDisplayGet()),
+ mWaylandBuffer(nullptr),
mFrameCallback(nullptr),
- mFrameCallbackSurface(nullptr),
+ mLastCommittedSurface(nullptr),
mDisplayThreadMessageLoop(MessageLoop::current()),
- mDelayedCommit(false),
- mFullScreenDamage(false),
- mIsMainThread(NS_IsMainThread()) {}
+ mDelayedCommitHandle(nullptr),
+ mDrawToWaylandBufferDirectly(true),
+ mPendingCommit(false),
+ mWaylandBufferFullScreenDamage(false),
+ mIsMainThread(NS_IsMainThread()),
+ mNeedScaleFactorUpdate(true) {
+ for (int i = 0; i < BACK_BUFFER_NUM; i++) mBackupBuffer[i] = nullptr;
+}
WindowSurfaceWayland::~WindowSurfaceWayland() {
- delete mFrontBuffer;
- delete mBackBuffer;
+ if (mPendingCommit) {
+ NS_WARNING("Deleted WindowSurfaceWayland with a pending commit!");
+ }
+
+ if (mDelayedCommitHandle) {
+ // Delete reference to this to prevent WaylandBufferDelayCommitHandler()
+ // operate on released this. mDelayedCommitHandle itself will
+ // be released at WaylandBufferDelayCommitHandler().
+ *mDelayedCommitHandle = nullptr;
+ }
if (mFrameCallback) {
wl_callback_destroy(mFrameCallback);
}
+ delete mWaylandBuffer;
+
+ for (int i = 0; i < BACK_BUFFER_NUM; i++) {
+ if (mBackupBuffer[i]) {
+ delete mBackupBuffer[i];
+ }
+ }
+
if (!mIsMainThread) {
// We can be destroyed from main thread even though we was created/used
// in compositor thread. We have to unref/delete WaylandDisplay in
// compositor thread then and we can't use MessageLoop::current() here.
- mDisplayThreadMessageLoop->PostTask(
- NewRunnableFunction("WaylandDisplayRelease", &WaylandDisplayRelease,
- mWaylandDisplay->GetDisplay()));
+ mDisplayThreadMessageLoop->PostTask(NewRunnableFunction(
+ "WaylandDisplayRelease", &WaylandDisplayRelease, mWaylandDisplay));
} else {
- WaylandDisplayRelease(mWaylandDisplay->GetDisplay());
- }
-}
-
-void WindowSurfaceWayland::UpdateScaleFactor() {
- wl_surface *waylandSurface = mWindow->GetWaylandSurface();
- if (waylandSurface) {
- wl_surface_set_buffer_scale(waylandSurface, mWindow->GdkScaleFactor());
+ WaylandDisplayRelease(mWaylandDisplay);
}
}
-WindowBackBuffer *WindowSurfaceWayland::GetBufferToDraw(int aWidth,
- int aHeight) {
- if (!mFrontBuffer) {
- mFrontBuffer = new WindowBackBuffer(mWaylandDisplay, aWidth, aHeight);
- mBackBuffer = new WindowBackBuffer(mWaylandDisplay, aWidth, aHeight);
- return mFrontBuffer;
+WindowBackBuffer* WindowSurfaceWayland::GetWaylandBufferToDraw(int aWidth,
+ int aHeight) {
+ if (!mWaylandBuffer) {
+ mWaylandBuffer = new WindowBackBuffer(mWaylandDisplay, aWidth, aHeight);
+ return mWaylandBuffer;
}
- if (!mFrontBuffer->IsAttached()) {
- if (!mFrontBuffer->IsMatchingSize(aWidth, aHeight)) {
- mFrontBuffer->Resize(aWidth, aHeight);
+ if (!mWaylandBuffer->IsAttached()) {
+ if (!mWaylandBuffer->IsMatchingSize(aWidth, aHeight)) {
+ mWaylandBuffer->Resize(aWidth, aHeight);
// There's a chance that scale factor has been changed
// when buffer size changed
- UpdateScaleFactor();
+ mNeedScaleFactorUpdate = true;
+ }
+ return mWaylandBuffer;
+ }
+
+ MOZ_ASSERT(!mPendingCommit,
+ "Uncommitted buffer switch, screen artifacts ahead.");
+
+ // Front buffer is used by compositor, select a back buffer
+ int availableBuffer;
+ for (availableBuffer = 0; availableBuffer < BACK_BUFFER_NUM;
+ availableBuffer++) {
+ if (!mBackupBuffer[availableBuffer]) {
+ mBackupBuffer[availableBuffer] =
+ new WindowBackBuffer(mWaylandDisplay, aWidth, aHeight);
+ break;
+ }
+
+ if (!mBackupBuffer[availableBuffer]->IsAttached()) {
+ break;
}
- return mFrontBuffer;
}
- // Front buffer is used by compositor, draw to back buffer
- if (mBackBuffer->IsAttached()) {
+ if (MOZ_UNLIKELY(availableBuffer == BACK_BUFFER_NUM)) {
NS_WARNING("No drawing buffer available");
return nullptr;
}
- MOZ_ASSERT(!mDelayedCommit,
- "Uncommitted buffer switch, screen artifacts ahead.");
-
- WindowBackBuffer *tmp = mFrontBuffer;
- mFrontBuffer = mBackBuffer;
- mBackBuffer = tmp;
+ WindowBackBuffer* lastWaylandBuffer = mWaylandBuffer;
+ mWaylandBuffer = mBackupBuffer[availableBuffer];
+ mBackupBuffer[availableBuffer] = lastWaylandBuffer;
- if (mBackBuffer->IsMatchingSize(aWidth, aHeight)) {
+ if (lastWaylandBuffer->IsMatchingSize(aWidth, aHeight)) {
// Former front buffer has the same size as a requested one.
// Gecko may expect a content already drawn on screen so copy
// existing data to the new buffer.
- mFrontBuffer->SetImageDataFromBackBuffer(mBackBuffer);
+ mWaylandBuffer->SetImageDataFromBuffer(lastWaylandBuffer);
// When buffer switches we need to damage whole screen
// (https://bugzilla.redhat.com/show_bug.cgi?id=1418260)
- mFullScreenDamage = true;
+ mWaylandBufferFullScreenDamage = true;
} else {
// Former buffer has different size from the new request. Only resize
// the new buffer and leave gecko to render new whole content.
- mFrontBuffer->Resize(aWidth, aHeight);
+ mWaylandBuffer->Resize(aWidth, aHeight);
}
- return mFrontBuffer;
+ return mWaylandBuffer;
}
-already_AddRefed<gfx::DrawTarget> WindowSurfaceWayland::Lock(
- const LayoutDeviceIntRegion &aRegion) {
- MOZ_ASSERT(mIsMainThread == NS_IsMainThread());
-
- // We allocate back buffer to widget size but return only
- // portion requested by aRegion.
- LayoutDeviceIntRect rect = mWindow->GetBounds();
- WindowBackBuffer *buffer = GetBufferToDraw(rect.width, rect.height);
+already_AddRefed<gfx::DrawTarget> WindowSurfaceWayland::LockWaylandBuffer(
+ int aWidth, int aHeight, bool aClearBuffer) {
+ WindowBackBuffer* buffer = GetWaylandBufferToDraw(aWidth, aHeight);
if (!buffer) {
- NS_WARNING("No drawing buffer available");
+ NS_WARNING(
+ "WindowSurfaceWayland::LockWaylandBuffer(): No buffer available");
return nullptr;
}
- return buffer->Lock(aRegion);
+ if (aClearBuffer) {
+ buffer->Clear();
+ }
+
+ return buffer->Lock();
+}
+
+already_AddRefed<gfx::DrawTarget> WindowSurfaceWayland::LockImageSurface(
+ const gfx::IntSize& aLockSize) {
+ if (!mImageSurface || mImageSurface->CairoStatus() ||
+ !(aLockSize <= mImageSurface->GetSize())) {
+ mImageSurface = new gfxImageSurface(
+ aLockSize,
+ SurfaceFormatToImageFormat(WindowBackBuffer::GetSurfaceFormat()));
+ if (mImageSurface->CairoStatus()) {
+ return nullptr;
+ }
+ }
+
+ return gfxPlatform::CreateDrawTargetForData(
+ mImageSurface->Data(), mImageSurface->GetSize(), mImageSurface->Stride(),
+ WindowBackBuffer::GetSurfaceFormat());
}
-void WindowSurfaceWayland::Commit(const LayoutDeviceIntRegion &aInvalidRegion) {
+/*
+ There are some situations which can happen here:
+
+ A) Lock() is called to whole surface. In that case we don't need
+ to clip/buffer the drawing and we can return wl_buffer directly
+ for drawing.
+ - mWaylandBuffer is available - that's an ideal situation.
+ - mWaylandBuffer is locked by compositor - flip buffers and draw.
+ - if we can't flip buffers - go B)
+
+ B) Lock() is requested for part(s) of screen. We need to provide temporary
+ surface to draw into and copy result (clipped) to target wl_surface.
+ */
+already_AddRefed<gfx::DrawTarget> WindowSurfaceWayland::Lock(
+ const LayoutDeviceIntRegion& aRegion) {
MOZ_ASSERT(mIsMainThread == NS_IsMainThread());
- wl_surface *waylandSurface = mWindow->GetWaylandSurface();
+ LayoutDeviceIntRect screenRect = mWindow->GetBounds();
+ gfx::IntRect bounds = aRegion.GetBounds().ToUnknownRect();
+ gfx::IntSize lockSize(bounds.XMost(), bounds.YMost());
+
+ // Are we asked for entire nsWindow to draw?
+ mDrawToWaylandBufferDirectly =
+ (aRegion.GetNumRects() == 1 && bounds.x == 0 && bounds.y == 0 &&
+ lockSize.width == screenRect.width &&
+ lockSize.height == screenRect.height);
+
+ if (mDrawToWaylandBufferDirectly) {
+ RefPtr<gfx::DrawTarget> dt =
+ LockWaylandBuffer(screenRect.width, screenRect.height,
+ mWindow->WaylandSurfaceNeedsClear());
+ if (dt) {
+ return dt.forget();
+ }
+
+ // We don't have any front buffer available. Try indirect drawing
+ // to mImageSurface which is mirrored to front buffer at commit.
+ mDrawToWaylandBufferDirectly = false;
+ }
+
+ return LockImageSurface(lockSize);
+}
+
+bool WindowSurfaceWayland::CommitImageSurfaceToWaylandBuffer(
+ const LayoutDeviceIntRegion& aRegion) {
+ MOZ_ASSERT(!mDrawToWaylandBufferDirectly);
+
+ LayoutDeviceIntRect screenRect = mWindow->GetBounds();
+ gfx::IntRect bounds = aRegion.GetBounds().ToUnknownRect();
+
+ gfx::Rect rect(bounds);
+ if (rect.IsEmpty()) {
+ return false;
+ }
+
+ RefPtr<gfx::DrawTarget> dt = LockWaylandBuffer(
+ screenRect.width, screenRect.height, mWindow->WaylandSurfaceNeedsClear());
+ RefPtr<gfx::SourceSurface> surf =
+ gfx::Factory::CreateSourceSurfaceForCairoSurface(
+ mImageSurface->CairoSurface(), mImageSurface->GetSize(),
+ mImageSurface->Format());
+ if (!dt || !surf) {
+ return false;
+ }
+
+ uint32_t numRects = aRegion.GetNumRects();
+ if (numRects != 1) {
+ AutoTArray<IntRect, 32> rects;
+ rects.SetCapacity(numRects);
+ for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) {
+ rects.AppendElement(iter.Get().ToUnknownRect());
+ }
+ dt->PushDeviceSpaceClipRects(rects.Elements(), rects.Length());
+ }
+
+ dt->DrawSurface(surf, rect, rect);
+
+ if (numRects != 1) {
+ dt->PopClip();
+ }
+
+ return true;
+}
+
+static void WaylandBufferDelayCommitHandler(WindowSurfaceWayland** aSurface) {
+ if (*aSurface) {
+ (*aSurface)->DelayedCommitHandler();
+ } else {
+ // Referenced WindowSurfaceWayland is already deleted.
+ // Do nothing but just release the mDelayedCommitHandle allocated at
+ // WindowSurfaceWayland::CommitWaylandBuffer().
+ free(aSurface);
+ }
+}
+
+void WindowSurfaceWayland::CommitWaylandBuffer() {
+ MOZ_ASSERT(mPendingCommit, "Committing empty surface!");
+
+ wl_surface* waylandSurface = mWindow->GetWaylandSurface();
if (!waylandSurface) {
- // Target window is already destroyed - don't bother to render there.
+ // Target window is not created yet - delay the commit. This can happen only
+ // when the window is newly created and there's no active
+ // frame callback pending.
+ MOZ_ASSERT(!mFrameCallback || waylandSurface != mLastCommittedSurface,
+ "Missing wayland surface at frame callback!");
+
+ // Do nothing if there's already mDelayedCommitHandle pending.
+ if (!mDelayedCommitHandle) {
+ mDelayedCommitHandle = static_cast<WindowSurfaceWayland**>(
+ moz_xmalloc(sizeof(*mDelayedCommitHandle)));
+ *mDelayedCommitHandle = this;
+
+ MessageLoop::current()->PostDelayedTask(
+ NewRunnableFunction("WaylandBackBufferCommit",
+ &WaylandBufferDelayCommitHandler,
+ mDelayedCommitHandle),
+ EVENT_LOOP_DELAY);
+ }
return;
}
- wl_proxy_set_queue((struct wl_proxy *)waylandSurface,
+ wl_proxy_set_queue((struct wl_proxy*)waylandSurface,
mWaylandDisplay->GetEventQueue());
- if (mFullScreenDamage) {
+ // We have an active frame callback request so handle it.
+ if (mFrameCallback) {
+ if (waylandSurface == mLastCommittedSurface) {
+ // We have an active frame callback pending from our recent surface.
+ // It means we should defer the commit to FrameCallbackHandler().
+ return;
+ }
+ // If our stored wl_surface does not match the actual one it means the frame
+ // callback is no longer active and we should release it.
+ wl_callback_destroy(mFrameCallback);
+ mFrameCallback = nullptr;
+ mLastCommittedSurface = nullptr;
+ }
+
+ if (mWaylandBufferFullScreenDamage) {
LayoutDeviceIntRect rect = mWindow->GetBounds();
wl_surface_damage(waylandSurface, 0, 0, rect.width, rect.height);
- mFullScreenDamage = false;
+ mWaylandBufferFullScreenDamage = false;
} else {
- for (auto iter = aInvalidRegion.RectIter(); !iter.Done(); iter.Next()) {
- const mozilla::LayoutDeviceIntRect &r = iter.Get();
- wl_surface_damage(waylandSurface, r.x, r.y, r.width, r.height);
+ gint scaleFactor = mWindow->GdkScaleFactor();
+ for (auto iter = mWaylandBufferDamage.RectIter(); !iter.Done();
+ iter.Next()) {
+ const mozilla::LayoutDeviceIntRect& r = iter.Get();
+ // We need to remove the scale factor because the wl_surface_damage
+ // also multiplies by current scale factor.
+ wl_surface_damage(waylandSurface, r.x / scaleFactor, r.y / scaleFactor,
+ r.width / scaleFactor, r.height / scaleFactor);
}
}
- // Frame callback is always connected to actual wl_surface. When the surface
- // is unmapped/deleted the frame callback is never called. Unfortunatelly
- // we don't know if the frame callback is not going to be called.
- // But our mozcontainer code deletes wl_surface when the GdkWindow is hidden
- // creates a new one when is visible.
- if (mFrameCallback && mFrameCallbackSurface == waylandSurface) {
- // Do nothing here - we have a valid wl_surface and the buffer will be
- // commited to compositor in next frame callback event.
- mDelayedCommit = true;
- return;
- } else {
- if (mFrameCallback) {
- // Delete frame callback connected to obsoleted wl_surface.
- wl_callback_destroy(mFrameCallback);
- }
+ // Clear all back buffer damage as we're committing
+ // all requested regions.
+ mWaylandBufferDamage.SetEmpty();
- mFrameCallback = wl_surface_frame(waylandSurface);
- wl_callback_add_listener(mFrameCallback, &frame_listener, this);
- mFrameCallbackSurface = waylandSurface;
-
- // There's no pending frame callback so we can draw immediately
- // and create frame callback for possible subsequent drawing.
- mFrontBuffer->Attach(waylandSurface);
- mDelayedCommit = false;
+ mFrameCallback = wl_surface_frame(waylandSurface);
+ wl_callback_add_listener(mFrameCallback, &frame_listener, this);
+
+ if (mNeedScaleFactorUpdate || mLastCommittedSurface != waylandSurface) {
+ wl_surface_set_buffer_scale(waylandSurface, mWindow->GdkScaleFactor());
+ mNeedScaleFactorUpdate = false;
}
+
+ mWaylandBuffer->Attach(waylandSurface);
+ mLastCommittedSurface = waylandSurface;
+
+ // There's no pending commit, all changes are sent to compositor.
+ mPendingCommit = false;
+}
+
+void WindowSurfaceWayland::Commit(const LayoutDeviceIntRegion& aInvalidRegion) {
+ MOZ_ASSERT(mIsMainThread == NS_IsMainThread());
+
+ // We have new content at mImageSurface - copy data to mWaylandBuffer first.
+ if (!mDrawToWaylandBufferDirectly) {
+ CommitImageSurfaceToWaylandBuffer(aInvalidRegion);
+ }
+
+ // If we're not at fullscreen damage add drawing area from aInvalidRegion
+ if (!mWaylandBufferFullScreenDamage) {
+ mWaylandBufferDamage.OrWith(aInvalidRegion);
+ }
+
+ // We're ready to commit.
+ mPendingCommit = true;
+ CommitWaylandBuffer();
}
void WindowSurfaceWayland::FrameCallbackHandler() {
MOZ_ASSERT(mIsMainThread == NS_IsMainThread());
+ MOZ_ASSERT(mFrameCallback != nullptr,
+ "FrameCallbackHandler() called without valid frame callback!");
+ MOZ_ASSERT(mLastCommittedSurface != nullptr,
+ "FrameCallbackHandler() called without valid wl_surface!");
- if (mFrameCallback) {
- wl_callback_destroy(mFrameCallback);
- mFrameCallback = nullptr;
- mFrameCallbackSurface = nullptr;
+ wl_callback_destroy(mFrameCallback);
+ mFrameCallback = nullptr;
+
+ if (mPendingCommit) {
+ CommitWaylandBuffer();
}
+}
- if (mDelayedCommit) {
- wl_surface *waylandSurface = mWindow->GetWaylandSurface();
- if (!waylandSurface) {
- // Target window is already destroyed - don't bother to render there.
- NS_WARNING("No drawing buffer available");
- return;
- }
- wl_proxy_set_queue((struct wl_proxy *)waylandSurface,
- mWaylandDisplay->GetEventQueue());
+void WindowSurfaceWayland::DelayedCommitHandler() {
+ MOZ_ASSERT(mDelayedCommitHandle != nullptr, "Missing mDelayedCommitHandle!");
- // Send pending surface to compositor and register frame callback
- // for possible subsequent drawing.
- mFrameCallback = wl_surface_frame(waylandSurface);
- wl_callback_add_listener(mFrameCallback, &frame_listener, this);
- mFrameCallbackSurface = waylandSurface;
+ *mDelayedCommitHandle = nullptr;
+ free(mDelayedCommitHandle);
+ mDelayedCommitHandle = nullptr;
- mFrontBuffer->Attach(waylandSurface);
- mDelayedCommit = false;
+ if (mPendingCommit) {
+ CommitWaylandBuffer();
}
}
diff -up thunderbird-60.5.0/widget/gtk/WindowSurfaceWayland.h.wayland thunderbird-60.5.0/widget/gtk/WindowSurfaceWayland.h
--- thunderbird-60.5.0/widget/gtk/WindowSurfaceWayland.h.wayland 2019-01-22 20:44:03.000000000 +0100
+++ thunderbird-60.5.0/widget/gtk/WindowSurfaceWayland.h 2019-02-05 14:26:16.979316635 +0100
@@ -1,4 +1,4 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -8,37 +8,14 @@
#define _MOZILLA_WIDGET_GTK_WINDOW_SURFACE_WAYLAND_H
#include <prthread.h>
+#include "mozilla/gfx/Types.h"
+#include "nsWaylandDisplay.h"
+
+#define BACK_BUFFER_NUM 2
namespace mozilla {
namespace widget {
-// Our general connection to Wayland display server,
-// holds our display connection and runs event loop.
-class nsWaylandDisplay : public nsISupports {
- NS_DECL_THREADSAFE_ISUPPORTS
-
- public:
- nsWaylandDisplay(wl_display* aDisplay);
-
- wl_shm* GetShm();
- void SetShm(wl_shm* aShm) { mShm = aShm; };
-
- wl_display* GetDisplay() { return mDisplay; };
- wl_event_queue* GetEventQueue() { return mEventQueue; };
- gfx::SurfaceFormat GetSurfaceFormat() { return mFormat; };
- bool DisplayLoop();
- bool Matches(wl_display* aDisplay);
-
- private:
- virtual ~nsWaylandDisplay();
-
- PRThread* mThreadId;
- gfx::SurfaceFormat mFormat;
- wl_shm* mShm;
- wl_event_queue* mEventQueue;
- wl_display* mDisplay;
-};
-
// Allocates and owns shared memory for Wayland drawing surface
class WaylandShmPool {
public:
@@ -66,14 +43,15 @@ class WindowBackBuffer {
WindowBackBuffer(nsWaylandDisplay* aDisplay, int aWidth, int aHeight);
~WindowBackBuffer();
- already_AddRefed<gfx::DrawTarget> Lock(const LayoutDeviceIntRegion& aRegion);
+ already_AddRefed<gfx::DrawTarget> Lock();
void Attach(wl_surface* aSurface);
void Detach();
bool IsAttached() { return mAttached; }
+ void Clear();
bool Resize(int aWidth, int aHeight);
- bool SetImageDataFromBackBuffer(class WindowBackBuffer* aSourceBuffer);
+ bool SetImageDataFromBuffer(class WindowBackBuffer* aSourceBuffer);
bool IsMatchingSize(int aWidth, int aHeight) {
return aWidth == mWidth && aHeight == mHeight;
@@ -82,6 +60,8 @@ class WindowBackBuffer {
return aBuffer->mWidth == mWidth && aBuffer->mHeight == mHeight;
}
+ static gfx::SurfaceFormat GetSurfaceFormat() { return mFormat; }
+
private:
void Create(int aWidth, int aHeight);
void Release();
@@ -96,35 +76,48 @@ class WindowBackBuffer {
int mHeight;
bool mAttached;
nsWaylandDisplay* mWaylandDisplay;
+ static gfx::SurfaceFormat mFormat;
};
// WindowSurfaceWayland is an abstraction for wl_surface
// and related management
class WindowSurfaceWayland : public WindowSurface {
public:
- WindowSurfaceWayland(nsWindow* aWindow);
+ explicit WindowSurfaceWayland(nsWindow* aWindow);
~WindowSurfaceWayland();
already_AddRefed<gfx::DrawTarget> Lock(
const LayoutDeviceIntRegion& aRegion) override;
void Commit(const LayoutDeviceIntRegion& aInvalidRegion) final;
void FrameCallbackHandler();
+ void DelayedCommitHandler();
private:
- WindowBackBuffer* GetBufferToDraw(int aWidth, int aHeight);
- void UpdateScaleFactor();
+ WindowBackBuffer* GetWaylandBufferToDraw(int aWidth, int aHeight);
+
+ already_AddRefed<gfx::DrawTarget> LockWaylandBuffer(int aWidth, int aHeight,
+ bool aClearBuffer);
+ already_AddRefed<gfx::DrawTarget> LockImageSurface(
+ const gfx::IntSize& aLockSize);
+ bool CommitImageSurfaceToWaylandBuffer(const LayoutDeviceIntRegion& aRegion);
+ void CommitWaylandBuffer();
// TODO: Do we need to hold a reference to nsWindow object?
nsWindow* mWindow;
nsWaylandDisplay* mWaylandDisplay;
- WindowBackBuffer* mFrontBuffer;
- WindowBackBuffer* mBackBuffer;
+ WindowBackBuffer* mWaylandBuffer;
+ LayoutDeviceIntRegion mWaylandBufferDamage;
+ WindowBackBuffer* mBackupBuffer[BACK_BUFFER_NUM];
+ RefPtr<gfxImageSurface> mImageSurface;
wl_callback* mFrameCallback;
- wl_surface* mFrameCallbackSurface;
+ wl_surface* mLastCommittedSurface;
MessageLoop* mDisplayThreadMessageLoop;
- bool mDelayedCommit;
- bool mFullScreenDamage;
+ WindowSurfaceWayland** mDelayedCommitHandle;
+ bool mDrawToWaylandBufferDirectly;
+ bool mPendingCommit;
+ bool mWaylandBufferFullScreenDamage;
bool mIsMainThread;
+ bool mNeedScaleFactorUpdate;
};
} // namespace widget