thunderbird/firefox-wayland.patch

3857 lines
137 KiB
Diff

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 <gdk/gdkx.h>
#include <gdk/gdkwayland.h>
+#include <wayland-egl.h>
#endif
#include <stdio.h>
#include <dlfcn.h>
@@ -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<char16_t*>
(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<char16_t*>(
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<GdkWindow*>(wl_surface_get_user_data(surface));
+
+ gpointer user_data = nullptr;
+ gdk_window_get_user_data(gdkParentWindow, &user_data);
+
+ return GTK_WIDGET(user_data);
+}
+
void
DataOffer::AddMIMEType(const char *aMimeType)
{
@@ -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<WaylandDataOffer*>(data);
+ offer->SetAvailableDragActions(source_actions);
}
+/* Advertise recently selected drag and drop action by compositor, based
+ * on source actions and user choice (key modifiers, etc.).
+ */
static void
data_offer_action(void *data,
struct wl_data_offer *wl_data_offer,
uint32_t dnd_action)
{
+ auto *offer = static_cast<WaylandDataOffer*>(data);
+ offer->SetSelectedDragAction(dnd_action);
}
/* wl_data_offer callback description:
*
* data_offer_offer - Is called for each MIME type available at wl_data_offer.
- * data_offer_source_actions - Exposes all available D&D actions.
- * data_offer_action - Expose one actually selected D&D action.
+ * data_offer_source_actions - This event indicates the actions offered by
+ * the data source.
+ * data_offer_action - This event indicates the action selected by
+ * the compositor after matching the source/destination
+ * side actions.
*/
static const struct wl_data_offer_listener data_offer_listener = {
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<DataOffer*>(g_hash_table_lookup(mActiveOffers,
aWaylandDataOffer));
+ MOZ_ASSERT(dataOffer == nullptr,
+ "Registered WaylandDataOffer already exists. Wayland protocol error?");
+
if (!dataOffer) {
dataOffer = new WaylandDataOffer(aWaylandDataOffer);
g_hash_table_insert(mActiveOffers, aWaylandDataOffer, dataOffer);
@@ -259,12 +449,15 @@ nsRetrievalContextWayland::RegisterDataO
}
void
-nsRetrievalContextWayland::RegisterDataOffer(
+nsRetrievalContextWayland::RegisterNewDataOffer(
gtk_primary_selection_offer *aPrimaryDataOffer)
{
DataOffer* dataOffer =
static_cast<DataOffer*>(g_hash_table_lookup(mActiveOffers,
aPrimaryDataOffer));
+ MOZ_ASSERT(dataOffer == nullptr,
+ "Registered PrimaryDataOffer already exists. Wayland protocol error?");
+
if (!dataOffer) {
dataOffer = new PrimaryDataOffer(aPrimaryDataOffer);
g_hash_table_insert(mActiveOffers, aPrimaryDataOffer, dataOffer);
@@ -274,6 +467,9 @@ nsRetrievalContextWayland::RegisterDataO
void
nsRetrievalContextWayland::SetClipboardDataOffer(wl_data_offer *aWaylandDataOffer)
{
+ // Delete existing clipboard data offer
+ mClipboardOffer = nullptr;
+
DataOffer* dataOffer =
static_cast<DataOffer*>(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<DataOffer*>(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<WaylandDataOffer*>(g_hash_table_lookup(mActiveOffers,
+ aDropDataOffer));
+ NS_ASSERTION(dataOffer, "We're missing drag and drop data offer!");
+ if (dataOffer) {
+ g_hash_table_remove(mActiveOffers, aDropDataOffer);
+ mDragContext = new nsWaylandDragContext(dataOffer, mDisplay);
+ }
+}
+
+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<nsRetrievalContextWayland*>(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<nsRetrievalContextWayland*>(data);
+ context->AddDragAndDropDataOffer(offer);
+
+ nsWaylandDragContext* dragContext = context->GetDragContext();
+
+ GtkWidget* gtkWidget = get_gtk_widget_for_wl_surface(surface);
+ if (!gtkWidget) {
+ NS_WARNING("DragAndDrop: Unable to get GtkWidget for wl_surface!");
+ return;
+ }
+
+ LOGDRAG(("nsWindow data_device_enter for GtkWidget %p\n",
+ (void*)gtkWidget));
+
+ dragContext->DropDataEnter(gtkWidget, time,
+ wl_fixed_to_int(x_fixed),
+ wl_fixed_to_int(y_fixed));
}
static void
data_device_leave (void *data,
struct wl_data_device *data_device)
{
+ nsRetrievalContextWayland *context =
+ static_cast<nsRetrievalContextWayland*>(data);
+
+ 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<nsRetrievalContextWayland*>(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<nsRetrievalContextWayland*>(data);
+
+ nsWaylandDragContext* dropContext = context->GetDragContext();
+
+ uint32_t time;
+ nscoord x, y;
+ dropContext->GetLastDropInfo(&time, &x, &y);
+
+ WindowDragDropHandler(dropContext->GetWidget(), nullptr, dropContext,
+ x, y, time);
}
/* wl_data_device callback description:
@@ -405,7 +669,7 @@ primary_selection_data_offer (void
// create and add listener
nsRetrievalContextWayland *context =
static_cast<nsRetrievalContextWayland*>(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<nsRetrievalContextWayland*>(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<nsRetrievalContextWayland*>(data);
- context->ConfigureKeyboard((wl_seat_capability)caps);
-}
-
-static const struct wl_seat_listener seat_listener = {
- seat_handle_capabilities,
-};
-
void
nsRetrievalContextWayland::InitDataDeviceManager(wl_registry *registry,
uint32_t id,
@@ -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<DataOffer*>(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<char*>(
- g_malloc(sizeof(char)*mClipboardDataLength));
+ g_malloc(sizeof(char)*(mClipboardDataLength+1)));
memcpy(mClipboardData, gtk_selection_data_get_data(aSelectionData),
sizeof(char)*mClipboardDataLength);
+ mClipboardData[mClipboardDataLength] = '\0';
}
} else {
NS_WARNING("Received obsoleted clipboard data!");
@@ -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<GdkAtom> 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<WaylandDataOffer> 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<DataOffer> mClipboardOffer;
nsAutoPtr<DataOffer> mPrimaryOffer;
+ RefPtr<nsWaylandDragContext> 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 <gtk/gtk.h>
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<nsWindow> mPendingWindow;
mozilla::LayoutDeviceIntPoint mPendingWindowPoint;
nsCountedRef<GdkDragContext> mPendingDragContext;
+#ifdef MOZ_WAYLAND
+ RefPtr<nsWaylandDragContext> mPendingWaylandDragContext;
+#endif
guint mPendingTime;
// mTargetWindow and mTargetWindowPoint record the position of the last
@@ -169,9 +175,15 @@ private:
// motion or drop events. mTime records the corresponding timestamp.
nsCountedRef<GtkWidget> mTargetWidget;
nsCountedRef<GdkDragContext> mTargetDragContext;
+#ifdef MOZ_WAYLAND
+ RefPtr<nsWaylandDragContext> mTargetWaylandDragContext;
+#endif
// mTargetDragContextForRemote is set while waiting for a reply from
// a child process.
nsCountedRef<GdkDragContext> mTargetDragContextForRemote;
+#ifdef MOZ_WAYLAND
+ RefPtr<nsWaylandDragContext> mTargetWaylandDragContextForRemote;
+#endif
guint mTargetTime;
// is it OK to drop on us?
@@ -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 <sys/mman.h>
+#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 <gdk/gdk.h>
#include <X11/XKBlib.h>
+#ifdef MOZ_WAYLAND
+#include <gdk/gdkwayland.h>
+#include <xkbcommon/xkbcommon.h>
+#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 <cairo-gobject.h>
#include "WidgetStyleCache.h"
#include "prenv.h"
+#include "nsCSSColorUtils.h"
+using namespace mozilla;
using mozilla::LookAndFeel;
#define GDK_COLOR_TO_NS_RGB(c) \
@@ -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 <gdk/gdkx.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <gio/gunixfdlist.h>
+
+// for dlsym
+#include <dlfcn.h>
+#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<nsPrintSettingsGTK> 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<WaylandWindowHandleExportedData*>(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<gboolean (*)(GdkWindow*, GdkWaylandWindowExported,
+ gpointer, GDestroyNotify)>
+ (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<nsPrintSettingsGTK> 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<nsFlatpakPrintPortal*>(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<GVariant* (*)(GtkPrintSettings*)>
+ (dlsym(RTLD_DEFAULT, "gtk_print_settings_to_gvariant"));
+ static auto s_gtk_page_setup_to_gvariant =
+ reinterpret_cast<GVariant* (*)(GtkPageSetup *)>
+ (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<nsIStringBundleService> bundleSvc =
+ do_GetService(NS_STRINGBUNDLE_CONTRACTID);
+ nsCOMPtr<nsIStringBundle> 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<nsFlatpakPrintPortal*>(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<GtkPrintSettings* (*)(GVariant*)>
+ (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<GtkPageSetup* (*)(GVariant*)>
+ (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<void (*)(GdkWindow*)>
+ (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<GUnixFDList* (*)(void)>
+ (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<gint (*)(GUnixFDList*, gint, GError**)>
+ (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<nsIObserverService> 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<nsIGIOService> giovfs =
+ do_GetService(NS_GIOSERVICE_CONTRACTID);
+ bool shouldUsePortal = false;
+ if (shouldUsePortal && gtk_check_version(3, 22, 0) == nullptr) {
+ nsCOMPtr<nsIWidget> 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<nsPrintSettingsGTK> printSettingsGTK(do_QueryInterface(aSettings));
+ RefPtr<nsFlatpakPrintPortal> 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<nsIObserver> observer = do_QueryInterface(fpPrintPortal);
+ nsCOMPtr<nsIObserverService> 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<nsIWidget> 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 <algorithm>
#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 <gdk/gdkwayland.h>
+#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 <dlfcn.h>
@@ -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<nsWindow*>(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<GdkWindowState>
+ (aEvent->changed_mask & ~GDK_WINDOW_STATE_MAXIMIZED);
+ } else if (aEvent->changed_mask & GDK_WINDOW_STATE_WITHDRAWN &&
+ aEvent->new_window_state & GDK_WINDOW_STATE_MAXIMIZED) {
+ aEvent->changed_mask = static_cast<GdkWindowState>
+ (aEvent->changed_mask | GDK_WINDOW_STATE_MAXIMIZED);
+ }
+
// We don't care about anything but changes in the maximized/icon/fullscreen
// states
if ((aEvent->changed_mask
@@ -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<nsWindow*>(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<char*>(mGtkWindowTypeName.get());
+ class_hint->res_class = const_cast<char*>(res_class);
+
+ // Can't use gtk_window_set_wmclass() for this; it prints
+ // a warning & refuses to make the change.
+ GdkDisplay *display = gdk_display_get_default();
+ XSetClassHint(GDK_DISPLAY_XDISPLAY(display),
+ gdk_x11_window_get_xid(gdkWindow),
+ class_hint);
+ XFree(class_hint);
+ }
+#endif /* MOZ_X11 */
+}
- 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<char*>(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<nsWindow> 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<nsWindow> window = get_window_for_gtk_widget(aWidget);
if (!window)
@@ -6063,15 +6130,24 @@ drag_motion_event_cb(GtkWidget *aWidget,
RefPtr<nsDragService> 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<nsWindow> 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<nsWindow> window = get_window_for_gtk_widget(aWidget);
if (!window)
@@ -6136,10 +6220,21 @@ drag_drop_event_cb(GtkWidget *aWidget,
RefPtr<nsDragService> 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 <gdk/gdkwayland.h>
#endif
#include <X11/Xlib.h> // 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<gfx::DrawTarget>
-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<unsigned char*>(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<gfx::DrawTarget>
+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<gfx::DrawTarget>
+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<gfx::DrawTarget>
WindowSurfaceWayland::Lock(const LayoutDeviceIntRegion& aRegion)
{
MOZ_ASSERT(mIsMainThread == NS_IsMainThread());
- // We allocate back buffer to widget size but return only
- // portion requested by aRegion.
- LayoutDeviceIntRect rect = mWindow->GetBounds();
- WindowBackBuffer* buffer = GetBufferToDraw(rect.width,
- rect.height);
- 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<gfx::DrawTarget> 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<gfx::DrawTarget> dt = LockWaylandBuffer(screenRect.width,
+ screenRect.height);
+ RefPtr<gfx::SourceSurface> 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<IntRect, 32> rects;
+ rects.SetCapacity(numRects);
+ for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) {
+ rects.AppendElement(iter.Get().ToUnknownRect());
+ }
+ dt->PushDeviceSpaceClipRects(rects.Elements(), rects.Length());
+ }
+
+ dt->DrawSurface(surf, rect, rect);
+
+ if (numRects != 1) {
+ dt->PopClip();
+ }
+
+ return true;
+}
+
+static void
+WaylandBufferDelayCommitHandler(WindowSurfaceWayland **aSurface)
+{
+ if (*aSurface) {
+ (*aSurface)->DelayedCommitHandler();
+ } else {
+ // Referenced WindowSurfaceWayland is already deleted.
+ // Do nothing but just release the mDelayedCommitHandle allocated at
+ // WindowSurfaceWayland::CommitWaylandBuffer().
+ free(aSurface);
+ }
}
void
-WindowSurfaceWayland::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<WindowSurfaceWayland**>(
+ moz_xmalloc(sizeof(*mDelayedCommitHandle)));
+ *mDelayedCommitHandle = this;
+
+ MessageLoop::current()->PostDelayedTask(
+ NewRunnableFunction("WaylandBackBufferCommit",
+ &WaylandBufferDelayCommitHandler,
+ mDelayedCommitHandle),
+ EVENT_LOOP_DELAY);
+ }
return;
}
wl_proxy_set_queue((struct wl_proxy *)waylandSurface,
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 <prthread.h>
+#include "mozilla/gfx/Types.h"
namespace mozilla {
namespace widget {
@@ -66,14 +67,14 @@ public:
WindowBackBuffer(nsWaylandDisplay* aDisplay, int aWidth, int aHeight);
~WindowBackBuffer();
- already_AddRefed<gfx::DrawTarget> Lock(const LayoutDeviceIntRegion& aRegion);
+ already_AddRefed<gfx::DrawTarget> Lock();
void Attach(wl_surface* aSurface);
void Detach();
bool IsAttached() { return mAttached; }
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<gfx::DrawTarget> Lock(const LayoutDeviceIntRegion& aRegion) override;
void Commit(const LayoutDeviceIntRegion& aInvalidRegion) final;
void FrameCallbackHandler();
+ void DelayedCommitHandler();
private:
- WindowBackBuffer* GetBufferToDraw(int aWidth, int aHeight);
- void UpdateScaleFactor();
+ WindowBackBuffer* GetWaylandBufferToDraw(int aWidth, int aHeight);
+
+ already_AddRefed<gfx::DrawTarget> LockWaylandBuffer(int aWidth, int aHeight);
+ already_AddRefed<gfx::DrawTarget> LockImageSurface(const gfx::IntSize& aLockSize);
+ bool CommitImageSurfaceToWaylandBuffer(const LayoutDeviceIntRegion& aRegion);
+ void CommitWaylandBuffer();
// TODO: Do we need to hold a reference to nsWindow object?
nsWindow* mWindow;
nsWaylandDisplay* mWaylandDisplay;
- WindowBackBuffer* mFrontBuffer;
- WindowBackBuffer* mBackBuffer;
+ WindowBackBuffer* mWaylandBuffer;
+ LayoutDeviceIntRegion mWaylandBufferDamage;
+ WindowBackBuffer* mBackupBuffer;
+ RefPtr<gfxImageSurface> mImageSurface;
wl_callback* mFrameCallback;
- wl_surface* mFrameCallbackSurface;
+ wl_surface* mLastCommittedSurface;
MessageLoop* mDisplayThreadMessageLoop;
- bool mDelayedCommit;
- bool mFullScreenDamage;
+ WindowSurfaceWayland** mDelayedCommitHandle;
+ bool mDrawToWaylandBufferDirectly;
+ bool mPendingCommit;
+ bool mWaylandBufferFullScreenDamage;
bool mIsMainThread;
+ bool mNeedScaleFactorUpdate;
};
} // namespace widget