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 -#ifdef MOZ_WAYLAND #include -#include +#ifdef MOZ_WAYLAND +#include "nsWaylandDisplay.h" +#include #endif #include #include @@ -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_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, ®istry_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 #include "mozilla/Types.h" #include +#include #include +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 +#include + +#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 #include #include -#include -#include #include -#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(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(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(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(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( 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( 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( - 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( + 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( 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( + 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(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(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(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(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(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(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(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(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, >k_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(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(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( - 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 @@ -32,31 +33,75 @@ class DataOffer { private: virtual bool RequestDataTransfer(const char* aMimeType, int fd) = 0; + protected: nsTArray 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 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 mClipboardOffer; nsAutoPtr mPrimaryOffer; + RefPtr 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 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 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 mPendingWindow; mozilla::LayoutDeviceIntPoint mPendingWindowPoint; nsCountedRef mPendingDragContext; +#ifdef MOZ_WAYLAND + RefPtr 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 mTargetWidget; nsCountedRef mTargetDragContext; +#ifdef MOZ_WAYLAND + RefPtr mTargetWaylandDragContext; +#endif // mTargetDragContextForRemote is set while waiting for a reply from // a child process. nsCountedRef mTargetDragContextForRemote; +#ifdef MOZ_WAYLAND + RefPtr 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 +#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 #include +#ifdef MOZ_WAYLAND +#include +#include +#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 #include "gfxPlatformGtk.h" +//#include "mozilla/FontPropertyTypes.h" #include "ScreenHelperGTK.h" #include "gtkdrawing.h" @@ -31,7 +32,9 @@ #include #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(data); + + if (strcmp(interface, "wl_shm") == 0) { + auto shm = static_cast( + 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_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_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(wl_registry_bind( + registry, id, >k_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_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, ®istry_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 #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 +#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 @@ -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 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(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( + 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( + 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(mGtkWindowTypeName.get()); + class_hint->res_class = const_cast(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(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 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 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 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 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 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 #include - #ifdef MOZ_X11 #include #include "X11UndefineNone.h" @@ -28,7 +17,16 @@ #ifdef MOZ_WAYLAND #include #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 #endif #include // 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 #include -#include #include #include +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 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(data); - auto shm = static_cast( - 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, ®istry_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(data); +static void buffer_release(void* data, wl_buffer* buffer) { + auto surface = reinterpret_cast(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 WindowBackBuffer::Lock( - const LayoutDeviceIntRegion &aRegion) { - gfx::IntRect bounds = aRegion.GetBounds().ToUnknownRect(); - gfx::IntSize lockSize(bounds.XMost(), bounds.YMost()); - +already_AddRefed WindowBackBuffer::Lock() { + gfx::IntSize lockSize(mWidth, mHeight); return gfxPlatform::CreateDrawTargetForData( - static_cast(mShmPool.GetImageData()), lockSize, - BUFFER_BPP * mWidth, mWaylandDisplay->GetSurfaceFormat()); + static_cast(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(data); + auto surface = reinterpret_cast(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 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 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 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 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 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 dt = LockWaylandBuffer( + screenRect.width, screenRect.height, mWindow->WaylandSurfaceNeedsClear()); + RefPtr surf = + gfx::Factory::CreateSourceSurfaceForCairoSurface( + mImageSurface->CairoSurface(), mImageSurface->GetSize(), + mImageSurface->Format()); + if (!dt || !surf) { + return false; + } + + uint32_t numRects = aRegion.GetNumRects(); + if (numRects != 1) { + AutoTArray 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( + 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 +#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 Lock(const LayoutDeviceIntRegion& aRegion); + already_AddRefed 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 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 LockWaylandBuffer(int aWidth, int aHeight, + bool aClearBuffer); + already_AddRefed 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 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