diff -up thunderbird-60.3.0/widget/gtk/GtkCompositorWidget.cpp.wayland thunderbird-60.3.0/widget/gtk/GtkCompositorWidget.cpp --- thunderbird-60.3.0/widget/gtk/GtkCompositorWidget.cpp.wayland 2018-10-30 12:45:35.000000000 +0100 +++ thunderbird-60.3.0/widget/gtk/GtkCompositorWidget.cpp 2018-11-21 13:42:00.757025666 +0100 @@ -40,7 +40,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; @@ -50,8 +52,7 @@ GtkCompositorWidget::GtkCompositorWidget mXDisplay, mXWindow, visual, - depth - ); + depth); } mClientSize = aInitData.InitialClientSize(); } diff -up thunderbird-60.3.0/widget/gtk/moz.build.wayland thunderbird-60.3.0/widget/gtk/moz.build --- thunderbird-60.3.0/widget/gtk/moz.build.wayland 2018-10-30 12:45:35.000000000 +0100 +++ thunderbird-60.3.0/widget/gtk/moz.build 2018-11-21 13:42:00.757025666 +0100 @@ -123,6 +123,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.3.0/widget/gtk/mozcontainer.cpp.wayland thunderbird-60.3.0/widget/gtk/mozcontainer.cpp --- thunderbird-60.3.0/widget/gtk/mozcontainer.cpp.wayland 2018-10-30 12:45:35.000000000 +0100 +++ thunderbird-60.3.0/widget/gtk/mozcontainer.cpp 2018-11-21 13:42:00.757025666 +0100 @@ -10,6 +10,7 @@ #ifdef MOZ_WAYLAND #include #include +#include #endif #include #include @@ -207,6 +208,12 @@ moz_container_init (MozContainer *contai #if defined(MOZ_WAYLAND) { + container->subcompositor = nullptr; + container->surface = nullptr; + container->subsurface = nullptr; + container->eglwindow = nullptr; + container->parent_surface_committed = false; + GdkDisplay *gdk_display = gtk_widget_get_display(GTK_WIDGET(container)); if (GDK_IS_WAYLAND_DISPLAY (gdk_display)) { // Available as of GTK 3.8+ @@ -225,12 +232,21 @@ moz_container_init (MozContainer *contai } #if defined(MOZ_WAYLAND) +static void +moz_container_commited_handler(GdkFrameClock *clock, MozContainer *container) +{ + container->parent_surface_committed = true; + g_signal_handler_disconnect(clock, + container->parent_surface_committed_handler); + container->parent_surface_committed_handler = 0; +} + /* 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. + * for reference. */ static gboolean moz_container_map_surface(MozContainer *container) @@ -242,6 +258,9 @@ moz_container_map_surface(MozContainer * static auto sGdkWaylandWindowGetWlSurface = (wl_surface *(*)(GdkWindow *)) dlsym(RTLD_DEFAULT, "gdk_wayland_window_get_wl_surface"); + static auto sGdkWindowGetFrameClock = + (GdkFrameClock *(*)(GdkWindow *)) + dlsym(RTLD_DEFAULT, "gdk_window_get_frame_clock"); GdkDisplay *display = gtk_widget_get_display(GTK_WIDGET(container)); if (GDK_IS_X11_DISPLAY(display)) @@ -250,6 +269,18 @@ moz_container_map_surface(MozContainer * if (container->subsurface && container->surface) return true; + if (!container->parent_surface_committed) { + if (!container->parent_surface_committed_handler) { + GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(container)); + GdkFrameClock *clock = sGdkWindowGetFrameClock(window); + container->parent_surface_committed_handler = + g_signal_connect_after(clock, "after-paint", + G_CALLBACK(moz_container_commited_handler), + container); + } + return false; + } + if (!container->surface) { struct wl_compositor *compositor; compositor = sGdkWaylandDisplayGetWlCompositor(display); @@ -289,8 +320,22 @@ moz_container_map_surface(MozContainer * static void moz_container_unmap_surface(MozContainer *container) { + //g_clear_pointer(&container->eglwindow, wl_egl_window_destroy); g_clear_pointer(&container->subsurface, wl_subsurface_destroy); g_clear_pointer(&container->surface, wl_surface_destroy); + + if (container->parent_surface_committed_handler) { + static auto sGdkWindowGetFrameClock = + (GdkFrameClock *(*)(GdkWindow *)) + dlsym(RTLD_DEFAULT, "gdk_window_get_frame_clock"); + GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(container)); + GdkFrameClock *clock = sGdkWindowGetFrameClock(window); + + g_signal_handler_disconnect(clock, + container->parent_surface_committed_handler); + container->parent_surface_committed_handler = 0; + } + container->parent_surface_committed = false; } #endif @@ -555,8 +605,40 @@ moz_container_get_wl_surface(MozContaine return nullptr; moz_container_map_surface(container); + // Set the scale factor for the buffer right after we create it. + if (container->surface) { + static auto sGdkWindowGetScaleFactorPtr = (gint (*)(GdkWindow*)) + dlsym(RTLD_DEFAULT, "gdk_window_get_scale_factor"); + if (sGdkWindowGetScaleFactorPtr && window) { + gint scaleFactor = (*sGdkWindowGetScaleFactorPtr)(window); + wl_surface_set_buffer_scale(container->surface, scaleFactor); + } + } } return container->surface; } + +struct wl_egl_window * +moz_container_get_wl_egl_window(MozContainer *container) +{ /* + if (!container->eglwindow) { + struct wl_surface *wlsurf = moz_container_get_wl_surface(container); + if (!wlsurf) + return nullptr; + + GdkWindow *window = gtk_widget_get_window(GTK_WIDGET(container)); + container->eglwindow + = wl_egl_window_create(wlsurf, + gdk_window_get_width(window), + gdk_window_get_height(window)); + } + return container->eglwindow;*/ return nullptr; +} + +gboolean +moz_container_has_wl_egl_window(MozContainer *container) +{ + return container->eglwindow ? true : false; +} #endif diff -up thunderbird-60.3.0/widget/gtk/mozcontainer.h.wayland thunderbird-60.3.0/widget/gtk/mozcontainer.h --- thunderbird-60.3.0/widget/gtk/mozcontainer.h.wayland 2018-10-30 12:45:34.000000000 +0100 +++ thunderbird-60.3.0/widget/gtk/mozcontainer.h 2018-11-21 13:42:00.757025666 +0100 @@ -72,6 +72,9 @@ struct _MozContainer struct wl_subcompositor *subcompositor; struct wl_surface *surface; struct wl_subsurface *subsurface; + struct wl_egl_window *eglwindow; + gboolean parent_surface_committed; + gulong parent_surface_committed_handler; #endif }; @@ -95,6 +98,8 @@ void moz_container_move ( #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); #endif #endif /* __MOZ_CONTAINER_H__ */ diff -up thunderbird-60.3.0/widget/gtk/mozgtk/mozgtk.c.wayland thunderbird-60.3.0/widget/gtk/mozgtk/mozgtk.c --- thunderbird-60.3.0/widget/gtk/mozgtk/mozgtk.c.wayland 2018-10-30 12:45:35.000000000 +0100 +++ thunderbird-60.3.0/widget/gtk/mozgtk/mozgtk.c 2018-11-21 13:42:00.757025666 +0100 @@ -135,6 +135,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) @@ -266,6 +268,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) diff -up thunderbird-60.3.0/widget/gtk/mozwayland/mozwayland.c.wayland thunderbird-60.3.0/widget/gtk/mozwayland/mozwayland.c --- thunderbird-60.3.0/widget/gtk/mozwayland/mozwayland.c.wayland 2018-10-30 12:45:35.000000000 +0100 +++ thunderbird-60.3.0/widget/gtk/mozwayland/mozwayland.c 2018-11-21 13:42:00.758025661 +0100 @@ -271,3 +271,21 @@ wl_log_set_handler_client(wl_log_func_t { } +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.3.0/widget/gtk/nsClipboard.cpp.wayland thunderbird-60.3.0/widget/gtk/nsClipboard.cpp --- thunderbird-60.3.0/widget/gtk/nsClipboard.cpp.wayland 2018-10-30 12:45:34.000000000 +0100 +++ thunderbird-60.3.0/widget/gtk/nsClipboard.cpp 2018-11-21 13:42:00.758025661 +0100 @@ -671,11 +671,9 @@ void ConvertHTMLtoUCS2(const char* data, *unicodeData = reinterpret_cast (moz_xmalloc((outUnicodeLen + sizeof('\0')) * sizeof(char16_t))); - if (*unicodeData) { - memcpy(*unicodeData, data + sizeof(char16_t), - outUnicodeLen * sizeof(char16_t)); - (*unicodeData)[outUnicodeLen] = '\0'; - } + memcpy(*unicodeData, data + sizeof(char16_t), + outUnicodeLen * sizeof(char16_t)); + (*unicodeData)[outUnicodeLen] = '\0'; } else if (charset.EqualsLiteral("UNKNOWN")) { outUnicodeLen = 0; return; @@ -701,27 +699,25 @@ void ConvertHTMLtoUCS2(const char* data, if (needed.value()) { *unicodeData = reinterpret_cast( moz_xmalloc((needed.value() + 1) * sizeof(char16_t))); - if (*unicodeData) { - uint32_t result; - size_t read; - size_t written; - bool hadErrors; - Tie(result, read, written, hadErrors) = - decoder->DecodeToUTF16(AsBytes(MakeSpan(data, dataLength)), - MakeSpan(*unicodeData, needed.value()), - true); - MOZ_ASSERT(result == kInputEmpty); - MOZ_ASSERT(read == size_t(dataLength)); - MOZ_ASSERT(written <= needed.value()); - Unused << hadErrors; + uint32_t result; + size_t read; + size_t written; + bool hadErrors; + Tie(result, read, written, hadErrors) = + decoder->DecodeToUTF16(AsBytes(MakeSpan(data, dataLength)), + MakeSpan(*unicodeData, needed.value()), + true); + MOZ_ASSERT(result == kInputEmpty); + MOZ_ASSERT(read == size_t(dataLength)); + MOZ_ASSERT(written <= needed.value()); + Unused << hadErrors; #ifdef DEBUG_CLIPBOARD - if (read != dataLength) - printf("didn't consume all the bytes\n"); + if (read != dataLength) + printf("didn't consume all the bytes\n"); #endif - outUnicodeLen = written; - // null terminate. - (*unicodeData)[outUnicodeLen] = '\0'; - } + outUnicodeLen = written; + // null terminate. + (*unicodeData)[outUnicodeLen] = '\0'; } // if valid length } } diff -up thunderbird-60.3.0/widget/gtk/nsClipboardWayland.cpp.wayland thunderbird-60.3.0/widget/gtk/nsClipboardWayland.cpp --- thunderbird-60.3.0/widget/gtk/nsClipboardWayland.cpp.wayland 2018-10-30 12:45:34.000000000 +0100 +++ thunderbird-60.3.0/widget/gtk/nsClipboardWayland.cpp 2018-11-21 13:42:00.758025661 +0100 @@ -23,6 +23,7 @@ #include "mozilla/Services.h" #include "mozilla/RefPtr.h" #include "mozilla/TimeStamp.h" +#include "nsDragService.h" #include "imgIContainer.h" @@ -46,6 +47,44 @@ nsRetrievalContextWayland::sTextMimeType "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) { @@ -114,7 +153,7 @@ DataOffer::GetData(wl_display* aDisplay, 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) { @@ -155,6 +194,61 @@ WaylandDataOffer::RequestDataTransfer(co 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, @@ -164,25 +258,39 @@ data_offer_offer (void * 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) { + 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) { + 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 = { data_offer_offer, @@ -246,12 +354,94 @@ PrimaryDataOffer::~PrimaryDataOffer(void } } +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 -nsRetrievalContextWayland::RegisterDataOffer(wl_data_offer *aWaylandDataOffer) +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); @@ -259,12 +449,15 @@ nsRetrievalContextWayland::RegisterDataO } void -nsRetrievalContextWayland::RegisterDataOffer( +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); @@ -274,6 +467,9 @@ nsRetrievalContextWayland::RegisterDataO void nsRetrievalContextWayland::SetClipboardDataOffer(wl_data_offer *aWaylandDataOffer) { + // Delete existing clipboard data offer + mClipboardOffer = nullptr; + DataOffer* dataOffer = static_cast(g_hash_table_lookup(mActiveOffers, aWaylandDataOffer)); @@ -288,10 +484,12 @@ 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)); @@ -304,12 +502,31 @@ nsRetrievalContextWayland::SetPrimaryDat } void -nsRetrievalContextWayland::ClearDataOffers(void) +nsRetrievalContextWayland::AddDragAndDropDataOffer(wl_data_offer *aDropDataOffer) { - if (mClipboardOffer) - mClipboardOffer = nullptr; - if (mPrimaryOffer) - mPrimaryOffer = nullptr; + // 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); + } +} + +nsWaylandDragContext* +nsRetrievalContextWayland::GetDragContext(void) +{ + return mDragContext; +} + +void +nsRetrievalContextWayland::ClearDragAndDropDataOffer(void) +{ + mDragContext = nullptr; } // We have a new fresh data content. @@ -321,7 +538,7 @@ data_device_data_offer (void { nsRetrievalContextWayland *context = static_cast(data); - context->RegisterDataOffer(offer); + context->RegisterNewDataOffer(offer); } // The new fresh data content is clipboard. @@ -341,31 +558,78 @@ data_device_enter (void struct wl_data_device *data_device, uint32_t time, struct wl_surface *surface, - int32_t x, - int32_t y, + 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); + + 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) + int32_t x_fixed, + int32_t y_fixed) { + nsRetrievalContextWayland *context = + static_cast(data); + + nsWaylandDragContext* dropContext = context->GetDragContext(); + + 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: @@ -405,7 +669,7 @@ primary_selection_data_offer (void // create and add listener nsRetrievalContextWayland *context = static_cast(data); - context->RegisterDataOffer(gtk_primary_offer); + context->RegisterNewDataOffer(gtk_primary_offer); } static void @@ -418,6 +682,18 @@ primary_selection_selection (void 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, @@ -430,81 +706,6 @@ nsRetrievalContextWayland::HasSelectionS 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(); -} - -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, @@ -530,7 +731,6 @@ nsRetrievalContextWayland::InitSeat(wl_r void *data) { mSeat = (wl_seat*)wl_registry_bind(registry, id, &wl_seat_interface, 1); - wl_seat_add_listener(mSeat, &seat_listener, data); } static void @@ -573,6 +773,7 @@ nsRetrievalContextWayland::nsRetrievalCo , mActiveOffers(g_hash_table_new(NULL, NULL)) , mClipboardOffer(nullptr) , mPrimaryOffer(nullptr) + , mDragContext(nullptr) , mClipboardRequestNumber(0) , mClipboardData(nullptr) , mClipboardDataLength(0) @@ -616,8 +817,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); } @@ -667,12 +881,14 @@ nsRetrievalContextWayland::TransferFastT 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!"); @@ -727,7 +943,7 @@ nsRetrievalContextWayland::GetClipboardT 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, diff -up thunderbird-60.3.0/widget/gtk/nsClipboardWayland.h.wayland thunderbird-60.3.0/widget/gtk/nsClipboardWayland.h --- thunderbird-60.3.0/widget/gtk/nsClipboardWayland.h.wayland 2018-10-30 12:45:34.000000000 +0100 +++ thunderbird-60.3.0/widget/gtk/nsClipboardWayland.h 2018-11-21 13:42:00.759025657 +0100 @@ -32,6 +32,7 @@ public: private: virtual bool RequestDataTransfer(const char* aMimeType, int fd) = 0; +protected: nsTArray mTargetMIMETypes; }; @@ -40,25 +41,66 @@ class WaylandDataOffer : public DataOffe public: WaylandDataOffer(wl_data_offer* aWaylandDataOffer); -private: + 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(); + 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); + 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: @@ -74,15 +116,16 @@ 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); @@ -103,6 +146,7 @@ private: GHashTable* mActiveOffers; nsAutoPtr mClipboardOffer; nsAutoPtr mPrimaryOffer; + RefPtr mDragContext; int mClipboardRequestNumber; char* mClipboardData; diff -up thunderbird-60.3.0/widget/gtk/nsDragService.cpp.wayland thunderbird-60.3.0/widget/gtk/nsDragService.cpp --- thunderbird-60.3.0/widget/gtk/nsDragService.cpp.wayland 2018-10-30 12:45:35.000000000 +0100 +++ thunderbird-60.3.0/widget/gtk/nsDragService.cpp 2018-11-21 13:42:00.759025657 +0100 @@ -34,7 +34,6 @@ #include "nsPresContext.h" #include "nsIContent.h" #include "nsIDocument.h" -#include "nsISelection.h" #include "nsViewManager.h" #include "nsIFrame.h" #include "nsGtkUtils.h" @@ -43,6 +42,9 @@ #include "gfxPlatform.h" #include "ScreenHelperGTK.h" #include "nsArrayUtils.h" +#ifdef MOZ_WAYLAND +#include "nsClipboardWayland.h" +#endif using namespace mozilla; using namespace mozilla::gfx; @@ -99,6 +101,10 @@ invisibleSourceDragDataGet(GtkWidget 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. @@ -516,6 +522,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); } @@ -636,6 +645,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); @@ -1027,9 +1044,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; @@ -1074,6 +1100,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; } @@ -1105,6 +1140,36 @@ nsDragService::ReplyToDragMotion(GdkDrag 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, @@ -1136,6 +1201,12 @@ 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 @@ -1174,17 +1245,28 @@ nsDragService::GetTargetDragData(GdkAtom 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")); } @@ -1435,7 +1517,7 @@ nsDragService::SourceEndDragSession(GdkD } // Schedule the appropriate drag end dom events. - Schedule(eDragTaskSourceEnd, nullptr, nullptr, LayoutDeviceIntPoint(), 0); + Schedule(eDragTaskSourceEnd, nullptr, nullptr, nullptr, LayoutDeviceIntPoint(), 0); } static void @@ -1785,9 +1867,10 @@ invisibleSourceDragEnd(GtkWidget gboolean nsDragService::ScheduleMotionEvent(nsWindow *aWindow, GdkDragContext *aDragContext, + nsWaylandDragContext *aWaylandDragContext, LayoutDeviceIntPoint aWindowPoint, guint aTime) { - if (mScheduledTask == eDragTaskMotion) { + 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 @@ -1798,7 +1881,7 @@ nsDragService::ScheduleMotionEvent(nsWin // Returning TRUE means we'll reply with a status message, unless we first // get a leave. - return Schedule(eDragTaskMotion, aWindow, aDragContext, + return Schedule(eDragTaskMotion, aWindow, aDragContext, aWaylandDragContext, aWindowPoint, aTime); } @@ -1808,7 +1891,8 @@ 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"); } } @@ -1816,10 +1900,11 @@ nsDragService::ScheduleLeaveEvent() gboolean nsDragService::ScheduleDropEvent(nsWindow *aWindow, GdkDragContext *aDragContext, + nsWaylandDragContext *aWaylandDragContext, LayoutDeviceIntPoint aWindowPoint, guint aTime) { if (!Schedule(eDragTaskDrop, aWindow, - aDragContext, aWindowPoint, aTime)) { + aDragContext, aWaylandDragContext, aWindowPoint, aTime)) { NS_WARNING("Additional drag drop ignored"); return FALSE; } @@ -1833,6 +1918,7 @@ nsDragService::ScheduleDropEvent(nsWindo 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 @@ -1851,6 +1937,9 @@ nsDragService::Schedule(DragTask aTask, mScheduledTask = aTask; mPendingWindow = aWindow; mPendingDragContext = aDragContext; +#ifdef MOZ_WAYLAND + mPendingWaylandDragContext = aWaylandDragContext; +#endif mPendingWindowPoint = aWindowPoint; mPendingTime = aTime; @@ -1927,6 +2016,9 @@ 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 @@ -1958,10 +2050,20 @@ 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 } } } @@ -1972,8 +2074,10 @@ 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. @@ -1986,6 +2090,9 @@ 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 @@ -2015,7 +2122,16 @@ 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) @@ -2044,6 +2160,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.3.0/widget/gtk/nsDragService.h.wayland thunderbird-60.3.0/widget/gtk/nsDragService.h --- thunderbird-60.3.0/widget/gtk/nsDragService.h.wayland 2018-10-30 12:45:37.000000000 +0100 +++ thunderbird-60.3.0/widget/gtk/nsDragService.h 2018-11-21 13:42:00.759025657 +0100 @@ -14,6 +14,7 @@ #include class nsWindow; +class nsWaylandDragContext; namespace mozilla { namespace gfx { @@ -98,11 +99,13 @@ public: 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); @@ -158,6 +161,9 @@ private: RefPtr mPendingWindow; mozilla::LayoutDeviceIntPoint mPendingWindowPoint; nsCountedRef mPendingDragContext; +#ifdef MOZ_WAYLAND + RefPtr mPendingWaylandDragContext; +#endif guint mPendingTime; // mTargetWindow and mTargetWindowPoint record the position of the last @@ -169,9 +175,15 @@ private: // 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? @@ -212,6 +224,7 @@ private: gboolean Schedule(DragTask aTask, nsWindow *aWindow, GdkDragContext *aDragContext, + nsWaylandDragContext* aPendingWaylandDragContext, mozilla::LayoutDeviceIntPoint aWindowPoint, guint aTime); // Callback for g_idle_add_full() to run mScheduledTask. @@ -220,9 +233,11 @@ private: void UpdateDragAction(); void DispatchMotionEvents(); void ReplyToDragMotion(GdkDragContext* aDragContext); +#ifdef MOZ_WAYLAND + void ReplyToDragMotion(nsWaylandDragContext* aDragContext); +#endif gboolean DispatchDropEvent(); static uint32_t GetCurrentModifiers(); }; #endif // nsDragService_h__ - diff -up thunderbird-60.3.0/widget/gtk/nsGtkKeyUtils.cpp.wayland thunderbird-60.3.0/widget/gtk/nsGtkKeyUtils.cpp --- thunderbird-60.3.0/widget/gtk/nsGtkKeyUtils.cpp.wayland 2018-10-30 12:45:34.000000000 +0100 +++ thunderbird-60.3.0/widget/gtk/nsGtkKeyUtils.cpp 2018-11-21 13:42:00.760025653 +0100 @@ -28,6 +28,10 @@ #include "mozilla/MouseEvents.h" #include "mozilla/TextEvents.h" +#ifdef MOZ_WAYLAND +#include +#endif + namespace mozilla { namespace widget { @@ -195,7 +199,11 @@ KeymapWrapper::Init() memset(mModifierMasks, 0, sizeof(mModifierMasks)); if (GDK_IS_X11_DISPLAY(gdk_display_get_default())) - InitBySystemSettings(); + InitBySystemSettingsX11(); +#ifdef MOZ_WAYLAND + else + InitBySystemSettingsWayland(); +#endif gdk_window_add_filter(nullptr, FilterEvents, this); @@ -275,10 +283,10 @@ KeymapWrapper::InitXKBExtension() } void -KeymapWrapper::InitBySystemSettings() +KeymapWrapper::InitBySystemSettingsX11() { MOZ_LOG(gKeymapWrapperLog, LogLevel::Info, - ("%p InitBySystemSettings, mGdkKeymap=%p", + ("%p InitBySystemSettingsX11, mGdkKeymap=%p", this, mGdkKeymap)); Display* display = @@ -439,6 +447,208 @@ 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 = wl_seat_get_keyboard(seat); + wl_keyboard_add_listener(keyboard, &keyboard_listener, nullptr); + } else if (keyboard && !(caps & WL_SEAT_CAPABILITY_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); + + // Call wl_display_roundtrip() twice to make sure all + // callbacks are processed. + wl_display_roundtrip(display); + wl_display_roundtrip(display); +} +#endif + KeymapWrapper::~KeymapWrapper() { gdk_window_remove_filter(nullptr, FilterEvents, this); @@ -1405,6 +1615,14 @@ 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.3.0/widget/gtk/nsGtkKeyUtils.h.wayland thunderbird-60.3.0/widget/gtk/nsGtkKeyUtils.h --- thunderbird-60.3.0/widget/gtk/nsGtkKeyUtils.h.wayland 2018-10-30 12:45:34.000000000 +0100 +++ thunderbird-60.3.0/widget/gtk/nsGtkKeyUtils.h 2018-11-21 13:42:00.760025653 +0100 @@ -13,6 +13,10 @@ #include #include +#ifdef MOZ_WAYLAND +#include +#include +#endif namespace mozilla { namespace widget { @@ -131,6 +135,7 @@ public: * @param aKeyEvent It's an WidgetKeyboardEvent which needs to be * initialized. * @param aGdkKeyEvent A native GDK key event. + * @param aIsProcessedByIME true if aGdkKeyEvent is handled by IME. */ static void InitKeyEvent(WidgetKeyboardEvent& aKeyEvent, GdkEventKey* aGdkKeyEvent); @@ -148,6 +153,14 @@ public: 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. */ @@ -172,7 +185,10 @@ protected: */ void Init(); void InitXKBExtension(); - void InitBySystemSettings(); + void InitBySystemSettingsX11(); +#ifdef MOZ_WAYLAND + void InitBySystemSettingsWayland(); +#endif /** * mModifierKeys stores each hardware key information. @@ -374,6 +390,15 @@ protected: */ 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.3.0/widget/gtk/nsLookAndFeel.cpp.wayland thunderbird-60.3.0/widget/gtk/nsLookAndFeel.cpp --- thunderbird-60.3.0/widget/gtk/nsLookAndFeel.cpp.wayland 2018-10-30 12:45:35.000000000 +0100 +++ thunderbird-60.3.0/widget/gtk/nsLookAndFeel.cpp 2018-11-21 13:42:00.760025653 +0100 @@ -31,7 +31,9 @@ #include #include "WidgetStyleCache.h" #include "prenv.h" +#include "nsCSSColorUtils.h" +using namespace mozilla; using mozilla::LookAndFeel; #define GDK_COLOR_TO_NS_RGB(c) \ @@ -182,7 +184,7 @@ GetBorderColors(GtkStyleContext* aContex // 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; } @@ -213,6 +215,58 @@ GetBorderColors(GtkStyleContext* aContex 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() { @@ -269,7 +323,6 @@ nsLookAndFeel::NativeGetColor(ColorID aI 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; @@ -279,10 +332,15 @@ nsLookAndFeel::NativeGetColor(ColorID aI 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; @@ -1009,6 +1067,9 @@ 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.3.0/widget/gtk/nsLookAndFeel.h.wayland thunderbird-60.3.0/widget/gtk/nsLookAndFeel.h --- thunderbird-60.3.0/widget/gtk/nsLookAndFeel.h.wayland 2018-10-30 12:45:35.000000000 +0100 +++ thunderbird-60.3.0/widget/gtk/nsLookAndFeel.h 2018-11-21 13:42:00.760025653 +0100 @@ -77,6 +77,8 @@ protected: nscolor mMozWindowActiveBorder; nscolor mMozWindowInactiveBorder; nscolor mMozWindowInactiveCaption; + nscolor mMozCellHighlightBackground; + nscolor mMozCellHighlightText; nscolor mTextSelectedText; nscolor mTextSelectedBackground; nscolor mMozScrollbar; @@ -91,6 +93,9 @@ protected: bool mInitialized; void EnsureInit(); + +private: + nsresult InitCellHighlightColors(); }; #endif diff -up thunderbird-60.3.0/widget/gtk/nsPrintDialogGTK.cpp.wayland thunderbird-60.3.0/widget/gtk/nsPrintDialogGTK.cpp --- thunderbird-60.3.0/widget/gtk/nsPrintDialogGTK.cpp.wayland 2018-10-30 12:45:35.000000000 +0100 +++ thunderbird-60.3.0/widget/gtk/nsPrintDialogGTK.cpp 2018-11-21 13:45:42.405091067 +0100 @@ -24,7 +24,20 @@ #include "nsIBaseWindow.h" #include "nsIDocShellTreeItem.h" #include "nsIDocShell.h" +#include "nsIGIOService.h" #include "WidgetUtils.h" +#include "nsIObserverService.h" + +// for gdk_x11_window_get_xid +#include +#include +#include +#include +#include + +// for dlsym +#include +#include "MainThreadUtils.h" using namespace mozilla; using namespace mozilla::widget; @@ -387,7 +400,7 @@ nsPrintDialogWidgetGTK::ExportHeaderFoot nsresult nsPrintDialogWidgetGTK::ImportSettings(nsIPrintSettings *aNSSettings) { - NS_PRECONDITION(aNSSettings, "aSettings must not be null"); + MOZ_ASSERT(aNSSettings, "aSettings must not be null"); NS_ENSURE_TRUE(aNSSettings, NS_ERROR_FAILURE); nsCOMPtr aNSSettingsGTK(do_QueryInterface(aNSSettings)); @@ -416,7 +429,7 @@ nsPrintDialogWidgetGTK::ImportSettings(n nsresult nsPrintDialogWidgetGTK::ExportSettings(nsIPrintSettings *aNSSettings) { - NS_PRECONDITION(aNSSettings, "aSettings must not be null"); + MOZ_ASSERT(aNSSettings, "aSettings must not be null"); NS_ENSURE_TRUE(aNSSettings, NS_ERROR_FAILURE); GtkPrintSettings* settings = gtk_print_unix_dialog_get_settings(GTK_PRINT_UNIX_DIALOG(dialog)); @@ -513,13 +526,521 @@ nsPrintDialogServiceGTK::Init() return NS_OK; } +// Used to obtain window handle. The portal use this handle +// to ensure that print dialog is modal. +typedef void (*WindowHandleExported) (GtkWindow *window, + const char *handle, + gpointer user_data); + +typedef void (*GtkWindowHandleExported) (GtkWindow *window, + const char *handle, + gpointer user_data); +#ifdef MOZ_WAYLAND +typedef struct { + GtkWindow *window; + WindowHandleExported callback; + gpointer user_data; +} WaylandWindowHandleExportedData; + +static void +wayland_window_handle_exported (GdkWindow *window, + const char *wayland_handle_str, + gpointer user_data) +{ + WaylandWindowHandleExportedData *data = + static_cast(user_data); + char *handle_str; + + handle_str = g_strdup_printf ("wayland:%s", wayland_handle_str); + data->callback (data->window, handle_str, data->user_data); + g_free (handle_str); +} +#endif + +// Get window handle for the portal, taken from gtk/gtkwindow.c +// (currently not exported) +static gboolean +window_export_handle(GtkWindow *window, + GtkWindowHandleExported callback, + gpointer user_data) +{ + if (GDK_IS_X11_DISPLAY(gtk_widget_get_display(GTK_WIDGET(window)))) + { + GdkWindow *gdk_window = gtk_widget_get_window(GTK_WIDGET(window)); + char *handle_str; + guint32 xid = (guint32) gdk_x11_window_get_xid(gdk_window); + + handle_str = g_strdup_printf("x11:%x", xid); + callback(window, handle_str, user_data); + g_free(handle_str); + return true; + } +#ifdef MOZ_WAYLAND + else + { + GdkWindow *gdk_window = gtk_widget_get_window(GTK_WIDGET(window)); + WaylandWindowHandleExportedData *data; + + data = g_new0(WaylandWindowHandleExportedData, 1); + data->window = window; + data->callback = callback; + data->user_data = user_data; + + static auto s_gdk_wayland_window_export_handle = + reinterpret_cast + (dlsym(RTLD_DEFAULT, "gdk_wayland_window_export_handle")); + if (!s_gdk_wayland_window_export_handle || + !s_gdk_wayland_window_export_handle(gdk_window, + wayland_window_handle_exported, + data, g_free)) { + g_free (data); + return false; + } else { + return true; + } + } +#endif + + g_warning("Couldn't export handle, unsupported windowing system"); + + return false; +} +/** + * Communication class with the GTK print portal handler + * + * To print document from flatpak we need to use print portal because + * printers are not directly accessible in the sandboxed environment. + * + * At first we request portal to show the print dialog to let user choose + * printer settings. We use DBUS interface for that (PreparePrint method). + * + * Next we force application to print to temporary file and after the writing + * to the file is finished we pass its file descriptor to the portal. + * Portal will pass duplicate of the file descriptor to the printer which + * user selected before (by DBUS Print method). + * + * Since DBUS communication is done async while nsPrintDialogServiceGTK::Show + * is expecting sync execution, we need to create a new GMainLoop during the + * print portal dialog is running. The loop is stopped after the dialog + * is closed. + */ +class nsFlatpakPrintPortal: public nsIObserver +{ + NS_DECL_ISUPPORTS + NS_DECL_NSIOBSERVER + public: + explicit nsFlatpakPrintPortal(nsPrintSettingsGTK* aPrintSettings); + nsresult PreparePrintRequest(GtkWindow* aWindow); + static void OnWindowExportHandleDone(GtkWindow *aWindow, + const char* aWindowHandleStr, + gpointer aUserData); + void PreparePrint(GtkWindow* aWindow, const char* aWindowHandleStr); + static void OnPreparePrintResponse(GDBusConnection *connection, + const char *sender_name, + const char *object_path, + const char *interface_name, + const char *signal_name, + GVariant *parameters, + gpointer data); + GtkPrintOperationResult GetResult(); + private: + virtual ~nsFlatpakPrintPortal(); + void FinishPrintDialog(GVariant* parameters); + nsCOMPtr mPrintAndPageSettings; + GDBusProxy* mProxy; + guint32 mToken; + GMainLoop* mLoop; + GtkPrintOperationResult mResult; + guint mResponseSignalId; + GtkWindow* mParentWindow; +}; + +NS_IMPL_ISUPPORTS(nsFlatpakPrintPortal, nsIObserver) + +nsFlatpakPrintPortal::nsFlatpakPrintPortal(nsPrintSettingsGTK* aPrintSettings): + mPrintAndPageSettings(aPrintSettings), + mProxy(nullptr), + mLoop(nullptr), + mResponseSignalId(0), + mParentWindow(nullptr) +{ +} + +/** + * Creates GDBusProxy, query for window handle and create a new GMainLoop. + * + * The GMainLoop is to be run from GetResult() and be quitted during + * FinishPrintDialog. + * + * @param aWindow toplevel application window which is used as parent of print + * dialog + */ +nsresult +nsFlatpakPrintPortal::PreparePrintRequest(GtkWindow* aWindow) +{ + MOZ_ASSERT(aWindow, "aWindow must not be null"); + MOZ_ASSERT(mPrintAndPageSettings, "mPrintAndPageSettings must not be null"); + + GError* error = nullptr; + mProxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION, + G_DBUS_PROXY_FLAGS_NONE, + nullptr, + "org.freedesktop.portal.Desktop", + "/org/freedesktop/portal/desktop", + "org.freedesktop.portal.Print", + nullptr, + &error); + if (mProxy == nullptr) { + NS_WARNING(nsPrintfCString("Unable to create dbus proxy: %s", error->message).get()); + g_error_free(error); + return NS_ERROR_FAILURE; + } + + // The window handler is returned async, we will continue by PreparePrint method + // when it is returned. + if (!window_export_handle(aWindow, + &nsFlatpakPrintPortal::OnWindowExportHandleDone, this)) { + NS_WARNING("Unable to get window handle for creating modal print dialog."); + return NS_ERROR_FAILURE; + } + + mLoop = g_main_loop_new (NULL, FALSE); + return NS_OK; +} + +void +nsFlatpakPrintPortal::OnWindowExportHandleDone(GtkWindow* aWindow, + const char* aWindowHandleStr, + gpointer aUserData) +{ + nsFlatpakPrintPortal* printPortal = static_cast(aUserData); + printPortal->PreparePrint(aWindow, aWindowHandleStr); +} + +/** + * Ask print portal to show the print dialog. + * + * Print and page settings and window handle are passed to the portal to prefill + * last used settings. + */ +void +nsFlatpakPrintPortal::PreparePrint(GtkWindow* aWindow, const char* aWindowHandleStr) +{ + GtkPrintSettings* gtkSettings = mPrintAndPageSettings->GetGtkPrintSettings(); + GtkPageSetup* pageSetup = mPrintAndPageSettings->GetGtkPageSetup(); + + // We need to remember GtkWindow to unexport window handle after it is + // no longer needed by the portal dialog (apply only on non-X11 sessions). + if (!GDK_IS_X11_DISPLAY(gdk_display_get_default())) { + mParentWindow = aWindow; + } + + GVariantBuilder opt_builder; + g_variant_builder_init(&opt_builder, G_VARIANT_TYPE_VARDICT); + char* token = g_strdup_printf("mozilla%d", g_random_int_range (0, G_MAXINT)); + g_variant_builder_add(&opt_builder, "{sv}", "handle_token", + g_variant_new_string(token)); + g_free(token); + GVariant* options = g_variant_builder_end(&opt_builder); + static auto s_gtk_print_settings_to_gvariant = + reinterpret_cast + (dlsym(RTLD_DEFAULT, "gtk_print_settings_to_gvariant")); + static auto s_gtk_page_setup_to_gvariant = + reinterpret_cast + (dlsym(RTLD_DEFAULT, "gtk_page_setup_to_gvariant")); + if (!s_gtk_print_settings_to_gvariant || !s_gtk_page_setup_to_gvariant) { + mResult = GTK_PRINT_OPERATION_RESULT_ERROR; + FinishPrintDialog(nullptr); + return; + } + + // Get translated window title + nsCOMPtr bundleSvc = + do_GetService(NS_STRINGBUNDLE_CONTRACTID); + nsCOMPtr printBundle; + bundleSvc->CreateBundle("chrome://global/locale/printdialog.properties", + getter_AddRefs(printBundle)); + nsAutoString intlPrintTitle; + printBundle->GetStringFromName("printTitleGTK", intlPrintTitle); + + GError* error = nullptr; + GVariant *ret = g_dbus_proxy_call_sync(mProxy, + "PreparePrint", + g_variant_new ("(ss@a{sv}@a{sv}@a{sv})", + aWindowHandleStr, + NS_ConvertUTF16toUTF8(intlPrintTitle).get(), // Title of the window + s_gtk_print_settings_to_gvariant(gtkSettings), + s_gtk_page_setup_to_gvariant(pageSetup), + options), + G_DBUS_CALL_FLAGS_NONE, + -1, + nullptr, + &error); + if (ret == nullptr) { + NS_WARNING(nsPrintfCString("Unable to call dbus proxy: %s", error->message).get()); + g_error_free (error); + mResult = GTK_PRINT_OPERATION_RESULT_ERROR; + FinishPrintDialog(nullptr); + return; + } + + const char* handle = nullptr; + g_variant_get (ret, "(&o)", &handle); + if (strcmp (aWindowHandleStr, handle) != 0) + { + aWindowHandleStr = g_strdup (handle); + if (mResponseSignalId) { + g_dbus_connection_signal_unsubscribe( + g_dbus_proxy_get_connection(G_DBUS_PROXY(mProxy)), mResponseSignalId); + } + } + mResponseSignalId = + g_dbus_connection_signal_subscribe( + g_dbus_proxy_get_connection(G_DBUS_PROXY(mProxy)), + "org.freedesktop.portal.Desktop", + "org.freedesktop.portal.Request", + "Response", + aWindowHandleStr, + NULL, + G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE, + &nsFlatpakPrintPortal::OnPreparePrintResponse, + this, NULL); + +} + +void +nsFlatpakPrintPortal::OnPreparePrintResponse(GDBusConnection *connection, + const char *sender_name, + const char *object_path, + const char *interface_name, + const char *signal_name, + GVariant *parameters, + gpointer data) +{ + nsFlatpakPrintPortal* printPortal = static_cast(data); + printPortal->FinishPrintDialog(parameters); +} + +/** + * When the dialog is accepted, read print and page settings and token. + * + * Token is later used for printing portal as print operation identifier. + * Print and page settings are modified in-place and stored to + * mPrintAndPageSettings. + */ +void +nsFlatpakPrintPortal::FinishPrintDialog(GVariant* parameters) +{ + // This ends GetResult() method + if (mLoop) { + g_main_loop_quit (mLoop); + mLoop = nullptr; + } + + if (!parameters) { + // mResult should be already defined + return; + } + + guint32 response; + GVariant *options; + + g_variant_get (parameters, "(u@a{sv})", &response, &options); + mResult = GTK_PRINT_OPERATION_RESULT_CANCEL; + if (response == 0) { + GVariant *v; + + char *filename; + char *uri; + v = g_variant_lookup_value (options, "settings", G_VARIANT_TYPE_VARDICT); + static auto s_gtk_print_settings_new_from_gvariant = + reinterpret_cast + (dlsym(RTLD_DEFAULT, "gtk_print_settings_new_from_gvariant")); + + GtkPrintSettings* printSettings = s_gtk_print_settings_new_from_gvariant(v); + g_variant_unref (v); + + v = g_variant_lookup_value (options, "page-setup", G_VARIANT_TYPE_VARDICT); + static auto s_gtk_page_setup_new_from_gvariant = + reinterpret_cast + (dlsym(RTLD_DEFAULT, "gtk_page_setup_new_from_gvariant")); + GtkPageSetup* pageSetup = s_gtk_page_setup_new_from_gvariant(v); + g_variant_unref (v); + + g_variant_lookup (options, "token", "u", &mToken); + + // Force printing to file because only filedescriptor of the file + // can be passed to portal + int fd = g_file_open_tmp("gtkprintXXXXXX", &filename, NULL); + uri = g_filename_to_uri(filename, NULL, NULL); + gtk_print_settings_set(printSettings, GTK_PRINT_SETTINGS_OUTPUT_URI, uri); + g_free (uri); + close (fd); + + // Save native settings in the session object + mPrintAndPageSettings->SetGtkPrintSettings(printSettings); + mPrintAndPageSettings->SetGtkPageSetup(pageSetup); + + // Portal consumes PDF file + mPrintAndPageSettings->SetOutputFormat(nsIPrintSettings::kOutputFormatPDF); + + // We need to set to print to file + mPrintAndPageSettings->SetPrintToFile(true); + + mResult = GTK_PRINT_OPERATION_RESULT_APPLY; + } +} + +/** + * Get result of the print dialog. + * + * This call blocks until FinishPrintDialog is called. + * + */ +GtkPrintOperationResult +nsFlatpakPrintPortal::GetResult() { + // If the mLoop has not been initialized we haven't go thru PreparePrint method + if (!NS_IsMainThread() || !mLoop) { + return GTK_PRINT_OPERATION_RESULT_ERROR; + } + // Calling g_main_loop_run stops current code until g_main_loop_quit is called + g_main_loop_run(mLoop); + + // Free resources we've allocated in order to show print dialog. +#ifdef MOZ_WAYLAND + if (mParentWindow) { + GdkWindow *gdk_window = gtk_widget_get_window(GTK_WIDGET(mParentWindow)); + static auto s_gdk_wayland_window_unexport_handle = + reinterpret_cast + (dlsym(RTLD_DEFAULT, "gdk_wayland_window_unexport_handle")); + if (s_gdk_wayland_window_unexport_handle) { + s_gdk_wayland_window_unexport_handle(gdk_window); + } + } +#endif + return mResult; +} + +/** + * Send file descriptor of the file which contains document to the portal to + * finish the print operation. + */ +NS_IMETHODIMP +nsFlatpakPrintPortal::Observe(nsISupports *aObject, const char * aTopic, + const char16_t * aData) +{ + // Check that written file match to the stored filename in case multiple + // print operations are in progress. + nsAutoString filenameStr; + mPrintAndPageSettings->GetToFileName(filenameStr); + if (!nsDependentString(aData).Equals(filenameStr)) { + // Different file is finished, not for this instance + return NS_OK; + } + int fd, idx; + fd = open(NS_ConvertUTF16toUTF8(filenameStr).get(), O_RDONLY|O_CLOEXEC); + static auto s_g_unix_fd_list_new = + reinterpret_cast + (dlsym(RTLD_DEFAULT, "g_unix_fd_list_new")); + NS_ASSERTION(s_g_unix_fd_list_new, "Cannot find g_unix_fd_list_new function."); + + GUnixFDList *fd_list = s_g_unix_fd_list_new(); + static auto s_g_unix_fd_list_append = + reinterpret_cast + (dlsym(RTLD_DEFAULT, "g_unix_fd_list_append")); + idx = s_g_unix_fd_list_append(fd_list, fd, NULL); + close(fd); + + GVariantBuilder opt_builder; + g_variant_builder_init(&opt_builder, G_VARIANT_TYPE_VARDICT); + g_variant_builder_add(&opt_builder, "{sv}", "token", + g_variant_new_uint32(mToken)); + g_dbus_proxy_call_with_unix_fd_list( + mProxy, + "Print", + g_variant_new("(ssh@a{sv})", + "", /* window */ + "Print", /* title */ + idx, + g_variant_builder_end(&opt_builder)), + G_DBUS_CALL_FLAGS_NONE, + -1, + fd_list, + NULL, + NULL, // TODO portal result cb function + nullptr); // data + g_object_unref(fd_list); + + nsCOMPtr os = mozilla::services::GetObserverService(); + // Let the nsFlatpakPrintPortal instance die + os->RemoveObserver(this, "print-to-file-finished"); + return NS_OK; +} + +nsFlatpakPrintPortal::~nsFlatpakPrintPortal() { + if (mProxy) { + if (mResponseSignalId) { + g_dbus_connection_signal_unsubscribe( + g_dbus_proxy_get_connection(G_DBUS_PROXY(mProxy)), mResponseSignalId); + } + g_object_unref(mProxy); + } + if (mLoop) + g_main_loop_quit(mLoop); +} + NS_IMETHODIMP nsPrintDialogServiceGTK::Show(nsPIDOMWindowOuter *aParent, nsIPrintSettings *aSettings, nsIWebBrowserPrint *aWebBrowserPrint) { - NS_PRECONDITION(aParent, "aParent must not be null"); - NS_PRECONDITION(aSettings, "aSettings must not be null"); + MOZ_ASSERT(aParent, "aParent must not be null"); + MOZ_ASSERT(aSettings, "aSettings must not be null"); + + // Check for the flatpak portal first + nsCOMPtr giovfs = + do_GetService(NS_GIOSERVICE_CONTRACTID); + bool shouldUsePortal = false; + if (shouldUsePortal && gtk_check_version(3, 22, 0) == nullptr) { + nsCOMPtr widget = WidgetUtils::DOMWindowToWidget(aParent); + NS_ASSERTION(widget, "Need a widget for dialog to be modal."); + GtkWindow* gtkParent = get_gtk_window_for_nsiwidget(widget); + NS_ASSERTION(gtkParent, "Need a GTK window for dialog to be modal."); + + + nsCOMPtr printSettingsGTK(do_QueryInterface(aSettings)); + RefPtr fpPrintPortal = + new nsFlatpakPrintPortal(printSettingsGTK); + + nsresult rv = fpPrintPortal->PreparePrintRequest(gtkParent); + NS_ENSURE_SUCCESS(rv, rv); + + // This blocks until nsFlatpakPrintPortal::FinishPrintDialog is called + GtkPrintOperationResult printDialogResult = fpPrintPortal->GetResult(); + + rv = NS_OK; + switch (printDialogResult) { + case GTK_PRINT_OPERATION_RESULT_APPLY: + { + nsCOMPtr observer = do_QueryInterface(fpPrintPortal); + nsCOMPtr os = mozilla::services::GetObserverService(); + NS_ENSURE_STATE(os); + // Observer waits until notified that the file with the content + // to print has been written. + rv = os->AddObserver(observer, "print-to-file-finished", false); + NS_ENSURE_SUCCESS(rv, rv); + break; + } + case GTK_PRINT_OPERATION_RESULT_CANCEL: + rv = NS_ERROR_ABORT; + break; + default: + NS_WARNING("Unexpected response"); + rv = NS_ERROR_ABORT; + } + return rv; + } nsPrintDialogWidgetGTK printDialog(aParent, aSettings); nsresult rv = printDialog.ImportSettings(aSettings); @@ -553,8 +1074,8 @@ NS_IMETHODIMP nsPrintDialogServiceGTK::ShowPageSetup(nsPIDOMWindowOuter *aParent, nsIPrintSettings *aNSSettings) { - NS_PRECONDITION(aParent, "aParent must not be null"); - NS_PRECONDITION(aNSSettings, "aSettings must not be null"); + MOZ_ASSERT(aParent, "aParent must not be null"); + MOZ_ASSERT(aNSSettings, "aSettings must not be null"); NS_ENSURE_TRUE(aNSSettings, NS_ERROR_FAILURE); nsCOMPtr widget = WidgetUtils::DOMWindowToWidget(aParent); diff -up thunderbird-60.3.0/widget/gtk/nsWindow.cpp.wayland thunderbird-60.3.0/widget/gtk/nsWindow.cpp --- thunderbird-60.3.0/widget/gtk/nsWindow.cpp.wayland 2018-10-30 12:45:35.000000000 +0100 +++ thunderbird-60.3.0/widget/gtk/nsWindow.cpp 2018-11-21 13:42:00.762025644 +0100 @@ -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,7 +26,7 @@ #include "prlink.h" #include "nsGTKToolkit.h" #include "nsIRollupListener.h" -#include "nsIDOMNode.h" +#include "nsINode.h" #include "nsWidgetsCID.h" #include "nsDragService.h" @@ -56,6 +57,7 @@ #if defined(MOZ_WAYLAND) #include +#include "nsView.h" #endif #include "nsGkAtoms.h" @@ -116,6 +118,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 +132,6 @@ using namespace mozilla::widget; #include "nsShmImage.h" #include "gtkdrawing.h" -#include "nsIDOMWheelEvent.h" - #include "NativeKeyBindings.h" #include @@ -140,6 +141,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 @@ -155,7 +157,8 @@ const gint kEvents = GDK_EXPOSURE_MASK | #endif GDK_SCROLL_MASK | GDK_POINTER_MOTION_MASK | - GDK_PROPERTY_CHANGE_MASK; + GDK_PROPERTY_CHANGE_MASK | + GDK_FOCUS_CHANGE_MASK; /* utility functions */ static bool is_mouse_in_window(GdkWindow* aWindow, @@ -210,7 +213,7 @@ static void hierarchy_changed_cb GtkWidget *previous_toplevel); static gboolean window_state_event_cb (GtkWidget *widget, GdkEventWindowState *event); -static void theme_changed_cb (GtkSettings *settings, +static void settings_changed_cb (GtkSettings *settings, GParamSpec *pspec, nsWindow *data); static void check_resize_cb (GtkContainer* container, @@ -481,6 +484,8 @@ nsWindow::nsWindow() mPendingConfigures = 0; mCSDSupportLevel = CSD_SUPPORT_NONE; mDrawInTitlebar = false; + + mHasAlphaVisual = false; } nsWindow::~nsWindow() @@ -630,7 +635,7 @@ EnsureInvisibleContainer() 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. @@ -731,7 +736,7 @@ nsWindow::Destroy() ClearCachedResources(); g_signal_handlers_disconnect_by_func(gtk_settings_get_default(), - FuncToGpointer(theme_changed_cb), + FuncToGpointer(settings_changed_cb), this); nsIRollupListener* rollupListener = nsBaseWidget::GetActiveRollupListener(); @@ -841,8 +846,14 @@ nsWindow::GetDesktopToDeviceScale() 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; } @@ -886,7 +897,7 @@ nsWindow::WidgetTypeSupportsAcceleration void nsWindow::ReparentNativeWidget(nsIWidget* aNewParent) { - NS_PRECONDITION(aNewParent, ""); + MOZ_ASSERT(aNewParent, "null widget"); NS_ASSERTION(!mIsDestroyed, ""); NS_ASSERTION(!static_cast(aNewParent)->mIsDestroyed, ""); @@ -1517,7 +1528,7 @@ nsWindow::GetClientBounds() 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) { @@ -2057,6 +2068,12 @@ nsWindow::OnExposeEvent(cairo_t *cr) if (!mGdkWindow || mIsFullyObscured || !mHasMappedToplevel) return FALSE; +#ifdef MOZ_WAYLAND + // Window does not have visible wl_surface yet. + if (!mIsX11Display && !GetWaylandSurface()) + return FALSE; +#endif + nsIWidgetListener *listener = GetListener(); if (!listener) return FALSE; @@ -3318,6 +3335,33 @@ nsWindow::OnWindowStateEvent(GtkWidget * } // 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 @@ -3404,6 +3448,7 @@ nsWindow::OnDPIChanged() // Update menu's font size etc presShell->ThemeChanged(); } + mWidgetListener->UIResolutionChanged(); } } @@ -3611,6 +3656,7 @@ nsWindow::Create(nsIWidget* aParent, nsWindow *parentnsWindow = nullptr; GtkWidget *eventWidget = nullptr; bool drawToContainer = false; + bool useAlphaVisual = false; if (aParent) { parentnsWindow = static_cast(aParent); @@ -3661,8 +3707,8 @@ nsWindow::Create(nsIWidget* aParent, } mShell = gtk_window_new(type); - bool useAlphaVisual = (mWindowType == eWindowType_popup && - aInitData->mSupportTranslucency); + useAlphaVisual = (mWindowType == eWindowType_popup && + aInitData->mSupportTranslucency); // mozilla.widget.use-argb-visuals is a hidden pref defaulting to false // to allow experimentation @@ -3784,7 +3830,7 @@ nsWindow::Create(nsIWidget* aParent, // it explicitly now. gtk_widget_realize(mShell); - /* There are two cases here: + /* There are several cases here: * * 1) We're running on Gtk+ without client side decorations. * Content is rendered to mShell window and we listen @@ -3859,17 +3905,7 @@ nsWindow::Create(nsIWidget* aParent, // If the window were to get unredirected, there could be visible // tearing because Gecko does not align its framebuffer updates with // vblank. - if (mIsX11Display) { - gulong value = 2; // Opt out of unredirection - 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); - } + SetCompositorHint(GTK_WIDGET_COMPOSIDED_ENABLED); #endif } break; @@ -3949,10 +3985,13 @@ nsWindow::Create(nsIWidget* aParent, GtkSettings* default_settings = gtk_settings_get_default(); g_signal_connect_after(default_settings, "notify::gtk-theme-name", - G_CALLBACK(theme_changed_cb), this); + G_CALLBACK(settings_changed_cb), this); g_signal_connect_after(default_settings, "notify::gtk-font-name", - G_CALLBACK(theme_changed_cb), this); + G_CALLBACK(settings_changed_cb), this); + g_signal_connect_after(default_settings, + "notify::gtk-enable-animations", + G_CALLBACK(settings_changed_cb), this); } if (mContainer) { @@ -4083,60 +4122,70 @@ nsWindow::Create(nsIWidget* aParent, } void -nsWindow::SetWindowClass(const nsAString &xulWinType) +nsWindow::RefreshWindowClass(void) { - if (!mShell) - return; + if (mGtkWindowTypeName.IsEmpty() || mGtkWindowRoleName.IsEmpty()) + return; - const char *res_class = gdk_get_program_class(); - if (!res_class) - return; + GdkWindow* gdkWindow = gtk_widget_get_window(mShell); + gdk_window_set_role(gdkWindow, mGtkWindowRoleName.get()); - char *res_name = ToNewCString(xulWinType); - if (!res_name) - return; +#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 */ +} - const char *role = nullptr; +void +nsWindow::SetWindowClass(const nsAString &xulWinType) +{ + if (!mShell) + return; - // Parse res_name into a name and role. Characters other than - // [A-Za-z0-9_-] are converted to '_'. Anything after the first - // colon is assigned to role; if there's no colon, assign the - // whole thing to both role and res_name. - for (char *c = res_name; *c; c++) { - if (':' == *c) { - *c = 0; - role = c + 1; - } - else if (!isascii(*c) || (!isalnum(*c) && ('_' != *c) && ('-' != *c))) - *c = '_'; - } - res_name[0] = toupper(res_name[0]); - if (!role) role = res_name; + char *res_name = ToNewCString(xulWinType); + if (!res_name) + return; - GdkWindow* gdkWindow = gtk_widget_get_window(mShell); - gdk_window_set_role(gdkWindow, role); + const char *role = nullptr; -#ifdef MOZ_X11 - if (mIsX11Display) { - XClassHint *class_hint = XAllocClassHint(); - if (!class_hint) { - free(res_name); - return; + // Parse res_name into a name and role. Characters other than + // [A-Za-z0-9_-] are converted to '_'. Anything after the first + // colon is assigned to role; if there's no colon, assign the + // whole thing to both role and res_name. + for (char *c = res_name; *c; c++) { + if (':' == *c) { + *c = 0; + role = c + 1; } - class_hint->res_name = res_name; - class_hint->res_class = const_cast(res_class); + else if (!isascii(*c) || (!isalnum(*c) && ('_' != *c) && ('-' != *c))) + *c = '_'; + } + res_name[0] = toupper(res_name[0]); + if (!role) role = res_name; - // 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); - free(res_name); + RefreshWindowClass(); } void @@ -4162,6 +4211,8 @@ nsWindow::NativeResize() size.width, size.height)); if (mIsTopLevel) { + 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) { @@ -4207,6 +4258,8 @@ nsWindow::NativeMoveResize() NativeShow(false); } NativeMove(); + + return; } GdkRectangle size = DevicePixelsToGdkSizeRoundUp(mBounds.Size()); @@ -4219,6 +4272,8 @@ 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) { @@ -4271,6 +4326,16 @@ nsWindow::NativeShow(bool aAction) } } 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 @@ -4946,6 +5011,8 @@ FullscreenTransitionWindow::FullscreenTr gdk_screen_get_monitor_geometry(screen, monitorNum, &monitorRect); gtk_window_set_screen(gtkWin, screen); gtk_window_move(gtkWin, monitorRect.x, monitorRect.y); + MOZ_ASSERT(monitorRect.width > 0 && monitorRect.height > 0, + "Can't resize window smaller than 1x1."); gtk_window_resize(gtkWin, monitorRect.width, monitorRect.height); GdkColor bgColor; @@ -5951,7 +6018,7 @@ window_state_event_cb (GtkWidget *widget } static void -theme_changed_cb (GtkSettings *settings, GParamSpec *pspec, nsWindow *data) +settings_changed_cb (GtkSettings *settings, GParamSpec *pspec, nsWindow *data) { RefPtr window = data; window->ThemeChanged(); @@ -6032,13 +6099,13 @@ nsWindow::InitDragEvent(WidgetDragEvent 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) @@ -6063,15 +6130,24 @@ drag_motion_event_cb(GtkWidget *aWidget, RefPtr dragService = nsDragService::GetInstance(); return dragService-> - ScheduleMotionEvent(innerMostWindow, aDragContext, + 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) @@ -6104,14 +6180,22 @@ drag_leave_event_cb(GtkWidget *aWidget, dragService->ScheduleLeaveEvent(); } +static void +drag_leave_event_cb(GtkWidget *aWidget, + GdkDragContext *aDragContext, + guint aTime, + gpointer aData) +{ + WindowDragLeaveHandler(aWidget); +} -static gboolean -drag_drop_event_cb(GtkWidget *aWidget, - GdkDragContext *aDragContext, - gint aX, - gint aY, - guint aTime, - gpointer aData) +gboolean +WindowDragDropHandler(GtkWidget *aWidget, + GdkDragContext *aDragContext, + nsWaylandDragContext *aWaylandDragContext, + gint aX, + gint aY, + guint aTime) { RefPtr window = get_window_for_gtk_widget(aWidget); if (!window) @@ -6136,10 +6220,21 @@ drag_drop_event_cb(GtkWidget *aWidget, RefPtr dragService = nsDragService::GetInstance(); return dragService-> - ScheduleDropEvent(innerMostWindow, aDragContext, + 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, GdkDragContext *aDragContext, @@ -6554,12 +6649,6 @@ nsWindow::GetLayerManager(PLayerTransact return mLayerManager; } - if (!mLayerManager && !IsComposited() && - eTransparencyTransparent == GetTransparencyMode()) - { - mLayerManager = CreateBasicLayerManager(); - } - return nsBaseWidget::GetLayerManager(aShadowManager, aBackendHint, aPersistence); } @@ -6601,6 +6690,13 @@ nsWindow::ClearCachedResources() 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. @@ -6692,6 +6788,15 @@ nsWindow::SetDrawsInTitlebar(bool aState 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. @@ -6998,6 +7103,8 @@ nsWindow::GetSystemCSDSupportLevel() { // 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) { @@ -7014,6 +7121,8 @@ nsWindow::GetSystemCSDSupportLevel() { 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. @@ -7066,26 +7175,18 @@ nsWindow::RoundsWidgetCoordinatesTo() 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() @@ -7110,3 +7211,85 @@ nsWindow::GetWaylandSurface() return nullptr; } #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) { + 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.3.0/widget/gtk/nsWindow.h.wayland thunderbird-60.3.0/widget/gtk/nsWindow.h --- thunderbird-60.3.0/widget/gtk/nsWindow.h.wayland 2018-10-30 12:45:35.000000000 +0100 +++ thunderbird-60.3.0/widget/gtk/nsWindow.h 2018-11-21 13:42:00.762025644 +0100 @@ -66,6 +66,21 @@ 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 { @@ -78,6 +93,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; nsWindow(); @@ -115,8 +131,7 @@ public: int32_t *aX, int32_t *aY) override; virtual void SetSizeConstraints(const SizeConstraints& aConstraints) override; - virtual void Move(double aX, - double aY) override; + virtual void Move(double aX, double aY) override; virtual void Show (bool aState) override; virtual void Resize (double aWidth, double aHeight, @@ -228,6 +243,8 @@ public: virtual void EndRemoteDrawingInRegion(mozilla::gfx::DrawTarget* aDrawTarget, LayoutDeviceIntRegion& aInvalidRegion) override; + void SetProgress(unsigned long progressPercent); + private: void UpdateAlpha(mozilla::gfx::SourceSurface* aSourceSurface, nsIntRect aBoundsRect); @@ -452,13 +469,24 @@ private: 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; @@ -558,6 +586,9 @@ private: // full translucency at this time; each pixel is either fully opaque // or fully transparent. gchar* mTransparencyBitmap; + // True when we're on compositing window manager and this + // window is using visual with alpha channel. + bool mHasAlphaVisual; // all of our DND stuff void InitDragEvent(mozilla::WidgetDragEvent& aEvent); diff -up thunderbird-60.3.0/widget/gtk/WindowSurfaceProvider.h.wayland thunderbird-60.3.0/widget/gtk/WindowSurfaceProvider.h --- thunderbird-60.3.0/widget/gtk/WindowSurfaceProvider.h.wayland 2018-10-30 12:45:34.000000000 +0100 +++ thunderbird-60.3.0/widget/gtk/WindowSurfaceProvider.h 2018-11-21 13:42:00.762025644 +0100 @@ -17,6 +17,7 @@ #include #endif #include // for Window, Display, Visual, etc. +#include "X11UndefineNone.h" class nsWindow; diff -up thunderbird-60.3.0/widget/gtk/WindowSurfaceWayland.cpp.wayland thunderbird-60.3.0/widget/gtk/WindowSurfaceWayland.cpp --- thunderbird-60.3.0/widget/gtk/WindowSurfaceWayland.cpp.wayland 2018-10-30 12:45:35.000000000 +0100 +++ thunderbird-60.3.0/widget/gtk/WindowSurfaceWayland.cpp 2018-11-21 13:42:00.763025640 +0100 @@ -151,8 +151,9 @@ static nsWaylandDisplay* WaylandDisplayG static void WaylandDisplayRelease(wl_display *aDisplay); static void WaylandDisplayLoop(wl_display *aDisplay); -// TODO: is the 60pfs loop correct? -#define EVENT_LOOP_DELAY (1000/60) +// TODO: Bug 1467125 - We need to integrate wl_display_dispatch_queue_pending() with +// compositor event loop. +#define EVENT_LOOP_DELAY (1000/240) // Get WaylandDisplay for given wl_display and actual calling thread. static nsWaylandDisplay* @@ -304,6 +305,7 @@ nsWaylandDisplay::nsWaylandDisplay(wl_di : mThreadId(PR_GetCurrentThread()) // gfx::SurfaceFormat::B8G8R8A8 is a basic Wayland format // and is always present. + // TODO: Provide also format without alpha (Bug 1470126). , mFormat(gfx::SurfaceFormat::B8G8R8A8) , mShm(nullptr) , mDisplay(aDisplay) @@ -522,7 +524,7 @@ WindowBackBuffer::Detach() } bool -WindowBackBuffer::SetImageDataFromBackBuffer( +WindowBackBuffer::SetImageDataFromBuffer( class WindowBackBuffer* aSourceBuffer) { if (!IsMatchingSize(aSourceBuffer)) { @@ -535,11 +537,9 @@ WindowBackBuffer::SetImageDataFromBackBu } already_AddRefed -WindowBackBuffer::Lock(const LayoutDeviceIntRegion& aRegion) +WindowBackBuffer::Lock() { - gfx::IntRect bounds = aRegion.GetBounds().ToUnknownRect(); - gfx::IntSize lockSize(bounds.XMost(), bounds.YMost()); - + gfx::IntSize lockSize(mWidth, mHeight); return gfxPlatform::CreateDrawTargetForData(static_cast(mShmPool.GetImageData()), lockSize, BUFFER_BPP * mWidth, @@ -560,26 +560,40 @@ static const struct wl_callback_listener WindowSurfaceWayland::WindowSurfaceWayland(nsWindow *aWindow) : mWindow(aWindow) , mWaylandDisplay(WaylandDisplayGet(aWindow->GetWaylandDisplay())) - , mFrontBuffer(nullptr) - , mBackBuffer(nullptr) + , mWaylandBuffer(nullptr) + , mBackupBuffer(nullptr) , mFrameCallback(nullptr) - , mFrameCallbackSurface(nullptr) + , mLastCommittedSurface(nullptr) , mDisplayThreadMessageLoop(MessageLoop::current()) - , mDelayedCommit(false) - , mFullScreenDamage(false) + , mDelayedCommitHandle(nullptr) + , mDrawToWaylandBufferDirectly(true) + , mPendingCommit(false) + , mWaylandBufferFullScreenDamage(false) , mIsMainThread(NS_IsMainThread()) + , mNeedScaleFactorUpdate(true) { } 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; + delete mBackupBuffer; + 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 @@ -593,162 +607,309 @@ WindowSurfaceWayland::~WindowSurfaceWayl } } -void -WindowSurfaceWayland::UpdateScaleFactor() -{ - wl_surface* waylandSurface = mWindow->GetWaylandSurface(); - if (waylandSurface) { - wl_surface_set_buffer_scale(waylandSurface, mWindow->GdkScaleFactor()); - } -} - WindowBackBuffer* -WindowSurfaceWayland::GetBufferToDraw(int aWidth, int aHeight) +WindowSurfaceWayland::GetWaylandBufferToDraw(int aWidth, int aHeight) { - if (!mFrontBuffer) { - mFrontBuffer = new WindowBackBuffer(mWaylandDisplay, aWidth, aHeight); - mBackBuffer = new WindowBackBuffer(mWaylandDisplay, aWidth, aHeight); - return mFrontBuffer; + if (!mWaylandBuffer) { + mWaylandBuffer = new WindowBackBuffer(mWaylandDisplay, aWidth, aHeight); + mBackupBuffer = 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 mFrontBuffer; + return mWaylandBuffer; } // Front buffer is used by compositor, draw to back buffer - if (mBackBuffer->IsAttached()) { + if (mBackupBuffer->IsAttached()) { NS_WARNING("No drawing buffer available"); return nullptr; } - MOZ_ASSERT(!mDelayedCommit, + MOZ_ASSERT(!mPendingCommit, "Uncommitted buffer switch, screen artifacts ahead."); - WindowBackBuffer *tmp = mFrontBuffer; - mFrontBuffer = mBackBuffer; - mBackBuffer = tmp; + WindowBackBuffer *tmp = mWaylandBuffer; + mWaylandBuffer = mBackupBuffer; + mBackupBuffer = tmp; - if (mBackBuffer->IsMatchingSize(aWidth, aHeight)) { + if (mBackupBuffer->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(mBackupBuffer); // 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 mWaylandBuffer; +} + +already_AddRefed +WindowSurfaceWayland::LockWaylandBuffer(int aWidth, int aHeight) +{ + WindowBackBuffer* buffer = GetWaylandBufferToDraw(aWidth, aHeight); + if (buffer) { + return buffer->Lock(); } - return mFrontBuffer; + NS_WARNING("WindowSurfaceWayland::LockWaylandBuffer(): No buffer available"); + return nullptr; } already_AddRefed +WindowSurfaceWayland::LockImageSurface(const gfx::IntSize& aLockSize) +{ + if (!mImageSurface || mImageSurface->CairoStatus() || + !(aLockSize <= mImageSurface->GetSize())) { + mImageSurface = new gfxImageSurface(aLockSize, + SurfaceFormatToImageFormat(mWaylandDisplay->GetSurfaceFormat())); + if (mImageSurface->CairoStatus()) { + return nullptr; + } + } + + return gfxPlatform::CreateDrawTargetForData(mImageSurface->Data(), + mImageSurface->GetSize(), + mImageSurface->Stride(), + mWaylandDisplay->GetSurfaceFormat()); +} + +/* + 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()); - // 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); - if (!buffer) { - NS_WARNING("No drawing buffer available"); - return nullptr; + 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); + 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); + RefPtr surf = + gfx::Factory::CreateSourceSurfaceForCairoSurface(mImageSurface->CairoSurface(), + mImageSurface->GetSize(), + mImageSurface->Format()); + if (!dt || !surf) { + return false; } - return buffer->Lock(aRegion); + 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::Commit(const LayoutDeviceIntRegion& aInvalidRegion) +WindowSurfaceWayland::CommitWaylandBuffer() { - MOZ_ASSERT(mIsMainThread == NS_IsMainThread()); + 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, 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()) { + gint scaleFactor = mWindow->GdkScaleFactor(); + for (auto iter = mWaylandBufferDamage.RectIter(); !iter.Done(); iter.Next()) { const mozilla::LayoutDeviceIntRect &r = iter.Get(); - wl_surface_damage(waylandSurface, r.x, r.y, r.width, r.height); + // 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.3.0/widget/gtk/WindowSurfaceWayland.h.wayland thunderbird-60.3.0/widget/gtk/WindowSurfaceWayland.h --- thunderbird-60.3.0/widget/gtk/WindowSurfaceWayland.h.wayland 2018-10-30 12:45:35.000000000 +0100 +++ thunderbird-60.3.0/widget/gtk/WindowSurfaceWayland.h 2018-11-21 13:42:00.763025640 +0100 @@ -8,6 +8,7 @@ #define _MOZILLA_WIDGET_GTK_WINDOW_SURFACE_WAYLAND_H #include +#include "mozilla/gfx/Types.h" namespace mozilla { namespace widget { @@ -66,14 +67,14 @@ public: 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; } bool Resize(int aWidth, int aHeight); - bool SetImageDataFromBackBuffer(class WindowBackBuffer* aSourceBuffer); + bool SetImageDataFromBuffer(class WindowBackBuffer* aSourceBuffer); bool IsMatchingSize(int aWidth, int aHeight) { @@ -110,22 +111,32 @@ public: 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); + 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; + 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