diff options
Diffstat (limited to 'widget/gtk')
34 files changed, 1774 insertions, 575 deletions
diff --git a/widget/gtk/DBusMenu.cpp b/widget/gtk/DBusMenu.cpp new file mode 100644 index 0000000000..8672de40ef --- /dev/null +++ b/widget/gtk/DBusMenu.cpp @@ -0,0 +1,67 @@ +/* -*- Mode: c++; tab-width: 2; indent-tabs-mode: nil; -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "DBusMenu.h" +#include "prlink.h" +#include "nsThreadUtils.h" +#include "mozilla/ArrayUtils.h" + +namespace mozilla::widget { + +#define FUNC(name, type, params) \ + DBusMenuFunctions::_##name##_fn DBusMenuFunctions::s_##name; +DBUSMENU_GLIB_FUNCTIONS +DBUSMENU_GTK_FUNCTIONS +#undef FUNC + +static PRLibrary* gDbusmenuGlib = nullptr; +static PRLibrary* gDbusmenuGtk = nullptr; + +using DBusMenuFunc = void (*)(); +struct DBusMenuDynamicFunction { + const char* functionName; + DBusMenuFunc* function; +}; + +static bool sInitialized; +static bool sLibPresent; + +/* static */ bool DBusMenuFunctions::Init() { + MOZ_ASSERT(NS_IsMainThread()); + if (sInitialized) { + return sLibPresent; + } + sInitialized = true; +#define FUNC(name, type, params) \ + {#name, (DBusMenuFunc*)&DBusMenuFunctions::s_##name}, + static const DBusMenuDynamicFunction kDbusmenuGlibSymbols[] = { + DBUSMENU_GLIB_FUNCTIONS}; + static const DBusMenuDynamicFunction kDbusmenuGtkSymbols[] = { + DBUSMENU_GTK_FUNCTIONS}; + +#define LOAD_LIBRARY(symbol, name) \ + if (!g##symbol) { \ + g##symbol = PR_LoadLibrary(name); \ + if (!g##symbol) { \ + return false; \ + } \ + } \ + for (uint32_t i = 0; i < mozilla::ArrayLength(k##symbol##Symbols); ++i) { \ + *k##symbol##Symbols[i].function = \ + PR_FindFunctionSymbol(g##symbol, k##symbol##Symbols[i].functionName); \ + if (!*k##symbol##Symbols[i].function) { \ + return false; \ + } \ + } + + LOAD_LIBRARY(DbusmenuGlib, "libdbusmenu-glib.so.4") + LOAD_LIBRARY(DbusmenuGtk, "libdbusmenu-gtk3.so.4") +#undef LOAD_LIBRARY + + sLibPresent = true; + return true; +} + +} // namespace mozilla::widget diff --git a/widget/gtk/DBusMenu.h b/widget/gtk/DBusMenu.h new file mode 100644 index 0000000000..d288d02322 --- /dev/null +++ b/widget/gtk/DBusMenu.h @@ -0,0 +1,137 @@ +/* -*- Mode: c++; tab-width: 2; indent-tabs-mode: nil; -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_widget_DBusMenu_h +#define mozilla_widget_DBusMenu_h + +#include <glib.h> +#include <gdk/gdk.h> + +namespace mozilla { + +namespace dom { +class Element; +} + +namespace widget { + +#define DBUSMENU_GLIB_FUNCTIONS \ + FUNC(dbusmenu_menuitem_child_add_position, gboolean, \ + (DbusmenuMenuitem * mi, DbusmenuMenuitem * child, guint position)) \ + FUNC(dbusmenu_menuitem_set_root, void, \ + (DbusmenuMenuitem * mi, gboolean root)) \ + FUNC(dbusmenu_menuitem_child_append, gboolean, \ + (DbusmenuMenuitem * mi, DbusmenuMenuitem * child)) \ + FUNC(dbusmenu_menuitem_child_delete, gboolean, \ + (DbusmenuMenuitem * mi, DbusmenuMenuitem * child)) \ + FUNC(dbusmenu_menuitem_get_children, GList*, (DbusmenuMenuitem * mi)) \ + FUNC(dbusmenu_menuitem_new, DbusmenuMenuitem*, (void)) \ + FUNC(dbusmenu_menuitem_property_get, const gchar*, \ + (DbusmenuMenuitem * mi, const gchar* property)) \ + FUNC(dbusmenu_menuitem_property_get_bool, gboolean, \ + (DbusmenuMenuitem * mi, const gchar* property)) \ + FUNC(dbusmenu_menuitem_property_remove, void, \ + (DbusmenuMenuitem * mi, const gchar* property)) \ + FUNC(dbusmenu_menuitem_property_set, gboolean, \ + (DbusmenuMenuitem * mi, const gchar* property, const gchar* value)) \ + FUNC(dbusmenu_menuitem_property_set_bool, gboolean, \ + (DbusmenuMenuitem * mi, const gchar* property, const gboolean value)) \ + FUNC(dbusmenu_menuitem_property_set_int, gboolean, \ + (DbusmenuMenuitem * mi, const gchar* property, const gint value)) \ + FUNC(dbusmenu_menuitem_show_to_user, void, \ + (DbusmenuMenuitem * mi, guint timestamp)) \ + FUNC(dbusmenu_menuitem_take_children, GList*, (DbusmenuMenuitem * mi)) \ + FUNC(dbusmenu_server_new, DbusmenuServer*, (const gchar* object)) \ + FUNC(dbusmenu_server_set_root, void, \ + (DbusmenuServer * server, DbusmenuMenuitem * root)) \ + FUNC(dbusmenu_server_set_status, void, \ + (DbusmenuServer * server, DbusmenuStatus status)) + +#define DBUSMENU_GTK_FUNCTIONS \ + FUNC(dbusmenu_menuitem_property_set_image, gboolean, \ + (DbusmenuMenuitem * menuitem, const gchar* property, \ + const GdkPixbuf* data)) \ + FUNC(dbusmenu_menuitem_property_set_shortcut, gboolean, \ + (DbusmenuMenuitem * menuitem, guint key, GdkModifierType modifier)) + +typedef struct _DbusmenuMenuitem DbusmenuMenuitem; +typedef struct _DbusmenuServer DbusmenuServer; + +enum DbusmenuStatus { DBUSMENU_STATUS_NORMAL, DBUSMENU_STATUS_NOTICE }; + +#define DBUSMENU_MENUITEM_CHILD_DISPLAY_SUBMENU "submenu" +#define DBUSMENU_MENUITEM_PROP_CHILD_DISPLAY "children-display" +#define DBUSMENU_MENUITEM_PROP_ENABLED "enabled" +#define DBUSMENU_MENUITEM_PROP_ICON_DATA "icon-data" +#define DBUSMENU_MENUITEM_PROP_LABEL "label" +#define DBUSMENU_MENUITEM_PROP_SHORTCUT "shortcut" +#define DBUSMENU_MENUITEM_PROP_TYPE "type" +#define DBUSMENU_MENUITEM_PROP_TOGGLE_STATE "toggle-state" +#define DBUSMENU_MENUITEM_PROP_TOGGLE_TYPE "toggle-type" +#define DBUSMENU_MENUITEM_PROP_VISIBLE "visible" +#define DBUSMENU_MENUITEM_SIGNAL_ABOUT_TO_SHOW "about-to-show" +#define DBUSMENU_MENUITEM_SIGNAL_EVENT "event" +#define DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED "item-activated" +#define DBUSMENU_MENUITEM_TOGGLE_CHECK "checkmark" +#define DBUSMENU_MENUITEM_TOGGLE_RADIO "radio" +#define DBUSMENU_MENUITEM_TOGGLE_STATE_CHECKED 1 +#define DBUSMENU_MENUITEM_TOGGLE_STATE_UNCHECKED 0 +#define DBUSMENU_SERVER_PROP_DBUS_OBJECT "dbus-object" + +class DBusMenuFunctions { + public: + DBusMenuFunctions() = delete; + + static bool Init(); + +#define FUNC(name, type, params) \ + typedef type(*_##name##_fn) params; \ + static _##name##_fn s_##name; + DBUSMENU_GLIB_FUNCTIONS + DBUSMENU_GTK_FUNCTIONS +#undef FUNC +}; + +#define dbusmenu_menuitem_set_root \ + DBusMenuFunctions::s_dbusmenu_menuitem_set_root +#define dbusmenu_menuitem_child_add_position \ + DBusMenuFunctions::s_dbusmenu_menuitem_child_add_position +#define dbusmenu_menuitem_child_append \ + DBusMenuFunctions::s_dbusmenu_menuitem_child_append +#define dbusmenu_menuitem_child_delete \ + DBusMenuFunctions::s_dbusmenu_menuitem_child_delete +#define dbusmenu_menuitem_get_children \ + DBusMenuFunctions::s_dbusmenu_menuitem_get_children +#define dbusmenu_menuitem_new DBusMenuFunctions::s_dbusmenu_menuitem_new +#define dbusmenu_menuitem_property_get \ + DBusMenuFunctions::s_dbusmenu_menuitem_property_get +#define dbusmenu_menuitem_property_get_bool \ + DBusMenuFunctions::s_dbusmenu_menuitem_property_get_bool +#define dbusmenu_menuitem_property_remove \ + DBusMenuFunctions::s_dbusmenu_menuitem_property_remove +#define dbusmenu_menuitem_property_set \ + DBusMenuFunctions::s_dbusmenu_menuitem_property_set +#define dbusmenu_menuitem_property_set_bool \ + DBusMenuFunctions::s_dbusmenu_menuitem_property_set_bool +#define dbusmenu_menuitem_property_set_int \ + DBusMenuFunctions::s_dbusmenu_menuitem_property_set_int +#define dbusmenu_menuitem_show_to_user \ + DBusMenuFunctions::s_dbusmenu_menuitem_show_to_user +#define dbusmenu_menuitem_take_children \ + DBusMenuFunctions::s_dbusmenu_menuitem_take_children +#define dbusmenu_server_new DBusMenuFunctions::s_dbusmenu_server_new +#define dbusmenu_server_set_root DBusMenuFunctions::s_dbusmenu_server_set_root +#define dbusmenu_server_set_status \ + DBusMenuFunctions::s_dbusmenu_server_set_status + +#define dbusmenu_menuitem_property_set_image \ + DBusMenuFunctions::s_dbusmenu_menuitem_property_set_image +#define dbusmenu_menuitem_property_set_shortcut \ + DBusMenuFunctions::s_dbusmenu_menuitem_property_set_shortcut + +} // namespace widget +} // namespace mozilla + +#endif diff --git a/widget/gtk/GRefPtr.h b/widget/gtk/GRefPtr.h index 1970008811..071164c06f 100644 --- a/widget/gtk/GRefPtr.h +++ b/widget/gtk/GRefPtr.h @@ -13,6 +13,9 @@ #include <gtk/gtk.h> #include "mozilla/RefPtr.h" +typedef struct _DbusmenuMenuitem DbusmenuMenuitem; +typedef struct _DbusmenuServer DbusmenuServer; + namespace mozilla { template <typename T> @@ -25,6 +28,8 @@ struct GObjectRefPtrTraits { template <> \ struct RefPtrTraits<type_> : public GObjectRefPtrTraits<type_> {}; +GOBJECT_TRAITS(DbusmenuMenuitem) +GOBJECT_TRAITS(DbusmenuServer) GOBJECT_TRAITS(GtkWidget) GOBJECT_TRAITS(GFile) GOBJECT_TRAITS(GFileMonitor) diff --git a/widget/gtk/GfxInfo.cpp b/widget/gtk/GfxInfo.cpp index 4dadbc81b5..457b2c72ce 100644 --- a/widget/gtk/GfxInfo.cpp +++ b/widget/gtk/GfxInfo.cpp @@ -75,17 +75,17 @@ nsresult GfxInfo::Init() { } void GfxInfo::AddCrashReportAnnotations() { - CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::AdapterVendorID, - mVendorId); - CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::AdapterDeviceID, - mDeviceId); - CrashReporter::AnnotateCrashReport( + CrashReporter::RecordAnnotationNSCString( + CrashReporter::Annotation::AdapterVendorID, mVendorId); + CrashReporter::RecordAnnotationNSCString( + CrashReporter::Annotation::AdapterDeviceID, mDeviceId); + CrashReporter::RecordAnnotationNSCString( CrashReporter::Annotation::AdapterDriverVendor, mDriverVendor); - CrashReporter::AnnotateCrashReport( + CrashReporter::RecordAnnotationNSCString( CrashReporter::Annotation::AdapterDriverVersion, mDriverVersion); - CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::IsWayland, - mIsWayland); - CrashReporter::AnnotateCrashReport( + CrashReporter::RecordAnnotationBool(CrashReporter::Annotation::IsWayland, + mIsWayland); + CrashReporter::RecordAnnotationNSCString( CrashReporter::Annotation::DesktopEnvironment, GetDesktopEnvironmentIdentifier()); diff --git a/widget/gtk/GtkCompositorWidget.cpp b/widget/gtk/GtkCompositorWidget.cpp index a3443b9828..36559cfd54 100644 --- a/widget/gtk/GtkCompositorWidget.cpp +++ b/widget/gtk/GtkCompositorWidget.cpp @@ -39,11 +39,11 @@ GtkCompositorWidget::GtkCompositorWidget( "GtkCompositorWidget::mClientSize") { #if defined(MOZ_X11) if (GdkIsX11Display()) { - mXWindow = (Window)aInitData.XWindow(); - ConfigureX11Backend(mXWindow, aInitData.Shaped()); + ConfigureX11Backend((Window)aInitData.XWindow(), aInitData.Shaped()); LOG("GtkCompositorWidget::GtkCompositorWidget() [%p] mXWindow %p " "mIsRenderingSuspended %d\n", - (void*)mWidget.get(), (void*)mXWindow, !!mIsRenderingSuspended); + (void*)mWidget.get(), (void*)aInitData.XWindow(), + !!mIsRenderingSuspended); } #endif #if defined(MOZ_WAYLAND) @@ -123,8 +123,8 @@ EGLNativeWindowType GtkCompositorWidget::GetEGLNativeWindow() { window = (EGLNativeWindowType)mWidget->GetNativeData(NS_NATIVE_EGL_WINDOW); } #if defined(MOZ_X11) - if (mXWindow) { - window = (EGLNativeWindowType)mXWindow; + else { + window = (EGLNativeWindowType)mProvider.GetXWindow(); } #endif LOG("GtkCompositorWidget::GetEGLNativeWindow [%p] window %p\n", mWidget.get(), @@ -173,9 +173,6 @@ void GtkCompositorWidget::DisableRendering() { LOG("GtkCompositorWidget::DisableRendering [%p]\n", (void*)mWidget.get()); mIsRenderingSuspended = true; mProvider.CleanupResources(); -#if defined(MOZ_X11) - mXWindow = {}; -#endif } #if defined(MOZ_WAYLAND) @@ -187,27 +184,13 @@ bool GtkCompositorWidget::ConfigureWaylandBackend() { #if defined(MOZ_X11) bool GtkCompositorWidget::ConfigureX11Backend(Window aXWindow, bool aShaped) { - mXWindow = aXWindow; - // We don't have X window yet. - if (!mXWindow) { + if (!aXWindow) { mIsRenderingSuspended = true; return false; } - - // Grab the window's visual and depth - XWindowAttributes windowAttrs; - if (!XGetWindowAttributes(DefaultXDisplay(), mXWindow, &windowAttrs)) { - NS_WARNING("GtkCompositorWidget(): XGetWindowAttributes() failed!"); - return false; - } - - Visual* visual = windowAttrs.visual; - int depth = windowAttrs.depth; - // Initialize the window surface provider - mProvider.Initialize(mXWindow, visual, depth, aShaped); - return true; + return mProvider.Initialize(aXWindow, aShaped); } #endif diff --git a/widget/gtk/GtkCompositorWidget.h b/widget/gtk/GtkCompositorWidget.h index 5bf89835d7..bde88bde6c 100644 --- a/widget/gtk/GtkCompositorWidget.h +++ b/widget/gtk/GtkCompositorWidget.h @@ -84,7 +84,7 @@ class GtkCompositorWidget : public CompositorWidget, bool SetEGLNativeWindowSize(const LayoutDeviceIntSize& aEGLWindowSize); #if defined(MOZ_X11) - Window XWindow() const { return mXWindow; } + Window XWindow() const { return mProvider.GetXWindow(); } #endif #if defined(MOZ_WAYLAND) RefPtr<mozilla::layers::NativeLayerRoot> GetNativeLayerRoot() override; @@ -123,11 +123,9 @@ class GtkCompositorWidget : public CompositorWidget, // of the two. DataMutex<LayoutDeviceIntSize> mClientSize; + // Holds rendering resources WindowSurfaceProvider mProvider; -#if defined(MOZ_X11) - Window mXWindow = {}; -#endif #ifdef MOZ_WAYLAND RefPtr<mozilla::layers::NativeLayerRootWayland> mNativeLayerRoot; #endif diff --git a/widget/gtk/IMContextWrapper.cpp b/widget/gtk/IMContextWrapper.cpp index fc87acbf86..2e438bbc5c 100644 --- a/widget/gtk/IMContextWrapper.cpp +++ b/widget/gtk/IMContextWrapper.cpp @@ -232,11 +232,18 @@ class SelectionStyleProvider final { sHasShutDown = true; } - // mContainer associated with an IM context. - void AttachTo(MozContainer* aContainer) { - gtk_style_context_add_provider( - gtk_widget_get_style_context(GTK_WIDGET(aContainer)), - GTK_STYLE_PROVIDER(mProvider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); + // aGDKWindow is a GTK window which will be associated with an IM context. + void AttachTo(GdkWindow* aGDKWindow) { + GtkWidget* widget = nullptr; + // gdk_window_get_user_data() typically returns pointer to widget that + // window belongs to. If it's widget, fcitx retrieves selection colors + // of them. So, we need to overwrite its style. + gdk_window_get_user_data(aGDKWindow, (gpointer*)&widget); + if (GTK_IS_WIDGET(widget)) { + gtk_style_context_add_provider(gtk_widget_get_style_context(widget), + GTK_STYLE_PROVIDER(mProvider), + GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); + } } void OnThemeChanged() { @@ -410,17 +417,21 @@ nsDependentCSubstring IMContextWrapper::GetIMName() const { } void IMContextWrapper::Init() { + MozContainer* container = mOwnerWindow->GetMozContainer(); + MOZ_ASSERT(container, "container is null"); + GdkWindow* gdkWindow = gtk_widget_get_window(GTK_WIDGET(container)); + // Overwrite selection colors of the window before associating the window // with IM context since IME may look up selection colors via IM context // to support any colored widgets. - SelectionStyleProvider::GetInstance()->AttachTo( - mOwnerWindow->GetMozContainer()); + SelectionStyleProvider::GetInstance()->AttachTo(gdkWindow); // NOTE: gtk_im_*_new() abort (kill) the whole process when it fails. // So, we don't need to check the result. // Normal context. mContext = gtk_im_multicontext_new(); + gtk_im_context_set_client_window(mContext, gdkWindow); g_signal_connect(mContext, "preedit_changed", G_CALLBACK(IMContextWrapper::OnChangeCompositionCallback), this); @@ -492,6 +503,7 @@ void IMContextWrapper::Init() { // Simple context if (sUseSimpleContext) { mSimpleContext = gtk_im_context_simple_new(); + gtk_im_context_set_client_window(mSimpleContext, gdkWindow); g_signal_connect(mSimpleContext, "preedit_changed", G_CALLBACK(&IMContextWrapper::OnChangeCompositionCallback), this); @@ -514,6 +526,7 @@ void IMContextWrapper::Init() { // Dummy context mDummyContext = gtk_im_multicontext_new(); + gtk_im_context_set_client_window(mDummyContext, gdkWindow); MOZ_LOG(gIMELog, LogLevel::Info, ("0x%p Init(), mOwnerWindow=%p, mContext=%p (im=\"%s\"), " @@ -540,17 +553,6 @@ IMContextWrapper::~IMContextWrapper() { MOZ_LOG(gIMELog, LogLevel::Info, ("0x%p ~IMContextWrapper()", this)); } -void IMContextWrapper::SetGdkWindow(GdkWindow* aGdkWindow) { - MOZ_LOG(gIMELog, LogLevel::Info, - ("0x%p GdkWindowChanged(%p)", this, aGdkWindow)); - MOZ_ASSERT(!aGdkWindow || mOwnerWindow->GetGdkWindow() == aGdkWindow); - gtk_im_context_set_client_window(mContext, aGdkWindow); - if (mSimpleContext) { - gtk_im_context_set_client_window(mSimpleContext, aGdkWindow); - } - gtk_im_context_set_client_window(mDummyContext, aGdkWindow); -} - NS_IMETHODIMP IMContextWrapper::NotifyIME(TextEventDispatcher* aTextEventDispatcher, const IMENotification& aNotification) { diff --git a/widget/gtk/IMContextWrapper.h b/widget/gtk/IMContextWrapper.h index 213c5ce8d3..cf1d2638e0 100644 --- a/widget/gtk/IMContextWrapper.h +++ b/widget/gtk/IMContextWrapper.h @@ -117,10 +117,6 @@ class IMContextWrapper final : public TextEventDispatcherListener { void OnUpdateComposition(); void OnLayoutChange(); - // Set GdkWindow associated with IM context. - // It can be null which disables context operations. - void SetGdkWindow(GdkWindow* aGdkWindow); - TextEventDispatcher* GetTextEventDispatcher(); // TODO: Typically, new IM comes every several years. And now, our code diff --git a/widget/gtk/MozContainer.cpp b/widget/gtk/MozContainer.cpp index 95d32b57b4..775ae0488f 100644 --- a/widget/gtk/MozContainer.cpp +++ b/widget/gtk/MozContainer.cpp @@ -35,27 +35,6 @@ static void moz_container_size_allocate(GtkWidget* widget, static void moz_container_realize(GtkWidget* widget); static void moz_container_unrealize(GtkWidget* widget); -/* container class methods */ -static void moz_container_remove(GtkContainer* container, - GtkWidget* child_widget); -static void moz_container_forall(GtkContainer* container, - gboolean include_internals, - GtkCallback callback, gpointer callback_data); -static void moz_container_add(GtkContainer* container, GtkWidget* widget); - -typedef struct _MozContainerChild MozContainerChild; - -struct _MozContainerChild { - GtkWidget* widget; - gint x; - gint y; -}; - -static void moz_container_allocate_child(MozContainer* container, - MozContainerChild* child); -static MozContainerChild* moz_container_get_child(MozContainer* container, - GtkWidget* child); - /* public methods */ GType moz_container_get_type(void) { @@ -92,26 +71,6 @@ GtkWidget* moz_container_new(void) { return GTK_WIDGET(container); } -void moz_container_put(MozContainer* container, GtkWidget* child_widget, gint x, - gint y) { - MozContainerChild* child; - - child = g_new(MozContainerChild, 1); - - child->widget = child_widget; - child->x = x; - child->y = y; - - /* printf("moz_container_put %p %p %d %d\n", (void *)container, - (void *)child_widget, x, y); */ - - container->data.children = g_list_append(container->data.children, child); - - /* we assume that the caller of this function will have already set - the parent GdkWindow because we can have many anonymous children. */ - gtk_widget_set_parent(child_widget, GTK_WIDGET(container)); -} - static void moz_container_destroy(GtkWidget* widget) { auto* container = MOZ_CONTAINER(widget); if (container->destroyed) { @@ -126,7 +85,6 @@ static void moz_container_destroy(GtkWidget* widget) { void moz_container_class_init(MozContainerClass* klass) { /*GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GtkObjectClass *object_class = GTK_OBJECT_CLASS (klass); */ - GtkContainerClass* container_class = GTK_CONTAINER_CLASS(klass); GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(klass); widget_class->realize = moz_container_realize; @@ -147,10 +105,6 @@ void moz_container_class_init(MozContainerClass* klass) { #ifdef MOZ_WAYLAND } #endif - - container_class->remove = moz_container_remove; - container_class->forall = moz_container_forall; - container_class->add = moz_container_add; } void moz_container_init(MozContainer* container) { @@ -164,8 +118,6 @@ void moz_container_init(MozContainer* container) { void moz_container_map(GtkWidget* widget) { MozContainer* container; - GList* tmp_list; - GtkWidget* tmp_child; g_return_if_fail(IS_MOZ_CONTAINER(widget)); container = MOZ_CONTAINER(widget); @@ -175,16 +127,6 @@ void moz_container_map(GtkWidget* widget) { gtk_widget_set_mapped(widget, TRUE); - tmp_list = container->data.children; - while (tmp_list) { - tmp_child = ((MozContainerChild*)tmp_list->data)->widget; - - if (gtk_widget_get_visible(tmp_child)) { - if (!gtk_widget_get_mapped(tmp_child)) gtk_widget_map(tmp_child); - } - tmp_list = tmp_list->next; - } - if (gtk_widget_get_has_window(widget)) { gdk_window_show(gtk_widget_get_window(widget)); } @@ -257,8 +199,6 @@ void moz_container_unrealize(GtkWidget* widget) { } void moz_container_size_allocate(GtkWidget* widget, GtkAllocation* allocation) { - MozContainer* container; - GList* tmp_list; GtkAllocation tmp_allocation; g_return_if_fail(IS_MOZ_CONTAINER(widget)); @@ -269,10 +209,8 @@ void moz_container_size_allocate(GtkWidget* widget, GtkAllocation* allocation) { allocation->height)); /* short circuit if you can */ - container = MOZ_CONTAINER(widget); gtk_widget_get_allocation(widget, &tmp_allocation); - if (!container->data.children && tmp_allocation.x == allocation->x && - tmp_allocation.y == allocation->y && + if (tmp_allocation.x == allocation->x && tmp_allocation.y == allocation->y && tmp_allocation.width == allocation->width && tmp_allocation.height == allocation->height) { return; @@ -280,16 +218,6 @@ void moz_container_size_allocate(GtkWidget* widget, GtkAllocation* allocation) { gtk_widget_set_allocation(widget, allocation); - tmp_list = container->data.children; - - while (tmp_list) { - MozContainerChild* child = static_cast<MozContainerChild*>(tmp_list->data); - - moz_container_allocate_child(container, child); - - tmp_list = tmp_list->next; - } - if (gtk_widget_get_has_window(widget) && gtk_widget_get_realized(widget)) { gdk_window_move_resize(gtk_widget_get_window(widget), allocation->x, allocation->y, allocation->width, @@ -297,99 +225,6 @@ void moz_container_size_allocate(GtkWidget* widget, GtkAllocation* allocation) { } } -void moz_container_remove(GtkContainer* container, GtkWidget* child_widget) { - MozContainerChild* child; - MozContainer* moz_container; - GdkWindow* parent_window; - - g_return_if_fail(IS_MOZ_CONTAINER(container)); - g_return_if_fail(GTK_IS_WIDGET(child_widget)); - - moz_container = MOZ_CONTAINER(container); - - child = moz_container_get_child(moz_container, child_widget); - g_return_if_fail(child); - - /* gtk_widget_unparent will remove the parent window (as well as the - * parent widget), but, in Mozilla's window hierarchy, the parent window - * may need to be kept because it may be part of a GdkWindow sub-hierarchy - * that is being moved to another MozContainer. - * - * (In a conventional GtkWidget hierarchy, GdkWindows being reparented - * would have their own GtkWidget and that widget would be the one being - * reparented. In Mozilla's hierarchy, the parent_window needs to be - * retained so that the GdkWindow sub-hierarchy is maintained.) - */ - parent_window = gtk_widget_get_parent_window(child_widget); - if (parent_window) g_object_ref(parent_window); - - gtk_widget_unparent(child_widget); - - if (parent_window) { - /* The child_widget will always still exist because g_signal_emit, - * which invokes this function, holds a reference. - * - * If parent_window is the container's root window then it will not be - * the parent_window if the child_widget is placed in another - * container. - */ - if (parent_window != gtk_widget_get_window(GTK_WIDGET(container))) { - gtk_widget_set_parent_window(child_widget, parent_window); - } - - g_object_unref(parent_window); - } - - moz_container->data.children = - g_list_remove(moz_container->data.children, child); - g_free(child); -} - -void moz_container_forall(GtkContainer* container, gboolean include_internals, - GtkCallback callback, gpointer callback_data) { - g_return_if_fail(IS_MOZ_CONTAINER(container)); - g_return_if_fail(callback); - - MozContainer* moz_container = MOZ_CONTAINER(container); - - GList* tmp_list = moz_container->data.children; - while (tmp_list) { - MozContainerChild* child; - child = static_cast<MozContainerChild*>(tmp_list->data); - tmp_list = tmp_list->next; - (*callback)(child->widget, callback_data); - } -} - -static void moz_container_allocate_child(MozContainer* container, - MozContainerChild* child) { - GtkAllocation allocation; - - gtk_widget_get_allocation(child->widget, &allocation); - allocation.x = child->x; - allocation.y = child->y; - - gtk_widget_size_allocate(child->widget, &allocation); -} - -MozContainerChild* moz_container_get_child(MozContainer* container, - GtkWidget* child_widget) { - GList* tmp_list = container->data.children; - while (tmp_list) { - MozContainerChild* child; - - child = static_cast<MozContainerChild*>(tmp_list->data); - tmp_list = tmp_list->next; - - if (child->widget == child_widget) return child; - } - return nullptr; -} - -static void moz_container_add(GtkContainer* container, GtkWidget* widget) { - moz_container_put(MOZ_CONTAINER(container), widget, 0, 0); -} - void moz_container_force_default_visual(MozContainer* container) { container->data.force_default_visual = true; } diff --git a/widget/gtk/MozContainer.h b/widget/gtk/MozContainer.h index 27fa2a701f..e6f9b4e992 100644 --- a/widget/gtk/MozContainer.h +++ b/widget/gtk/MozContainer.h @@ -18,35 +18,12 @@ /* * MozContainer * - * This class serves three purposes in the nsIWidget implementation. + * This class serves two purposes in the nsIWidget implementation. * * - It provides objects to receive signals from GTK for events on native * windows. * - * - It provides GdkWindow to draw content on Wayland or when Gtk+ renders - * client side decorations to mShell. - * - * - It provides a container parent for GtkWidgets. The only GtkWidgets - * that need this in Mozilla are the GtkSockets for windowed plugins (Xt - * and XEmbed). - * - * Note that the window hierarchy in Mozilla differs from conventional - * GtkWidget hierarchies. - * - * Mozilla's hierarchy exists through the GdkWindow hierarchy, and all child - * GdkWindows (within a child nsIWidget hierarchy) belong to one MozContainer - * GtkWidget. If the MozContainer is unrealized or its GdkWindows are - * destroyed for some other reason, then the hierarchy no longer exists. (In - * conventional GTK clients, the hierarchy is recorded by the GtkWidgets, and - * so can be re-established after destruction of the GdkWindows.) - * - * One consequence of this is that the MozContainer does not know which of its - * GdkWindows should parent child GtkWidgets. (Conventional GtkContainers - * determine which GdkWindow to assign child GtkWidgets.) - * - * Therefore, when adding a child GtkWidget to a MozContainer, - * gtk_widget_set_parent_window should be called on the child GtkWidget before - * it is realized. + * - It provides GdkWindow to draw content. */ #define MOZ_CONTAINER_TYPE (moz_container_get_type()) @@ -71,7 +48,6 @@ struct _MozContainer { GtkContainer container; gboolean destroyed; struct Data { - GList* children = nullptr; gboolean force_default_visual = false; #ifdef MOZ_WAYLAND MozContainerWayland wl_container; diff --git a/widget/gtk/MozContainerWayland.cpp b/widget/gtk/MozContainerWayland.cpp index 0e50a3f27c..8490f25599 100644 --- a/widget/gtk/MozContainerWayland.cpp +++ b/widget/gtk/MozContainerWayland.cpp @@ -436,7 +436,6 @@ void moz_container_wayland_map(GtkWidget* widget) { void moz_container_wayland_size_allocate(GtkWidget* widget, GtkAllocation* allocation) { - MozContainer* container; GtkAllocation tmp_allocation; g_return_if_fail(IS_MOZ_CONTAINER(widget)); @@ -447,10 +446,8 @@ void moz_container_wayland_size_allocate(GtkWidget* widget, allocation->height); /* short circuit if you can */ - container = MOZ_CONTAINER(widget); gtk_widget_get_allocation(widget, &tmp_allocation); - if (!container->data.children && tmp_allocation.x == allocation->x && - tmp_allocation.y == allocation->y && + if (tmp_allocation.x == allocation->x && tmp_allocation.y == allocation->y && tmp_allocation.width == allocation->width && tmp_allocation.height == allocation->height) { return; @@ -464,6 +461,7 @@ void moz_container_wayland_size_allocate(GtkWidget* widget, // We need to position our subsurface according to GdkWindow // when offset changes (GdkWindow is maximized for instance). // see gtk-clutter-embed.c for reference. + MozContainer* container = MOZ_CONTAINER(widget); MutexAutoLock lock(container->data.wl_container.container_lock); if (!container->data.wl_container.surface) { if (!moz_container_wayland_surface_create_locked(lock, container)) { diff --git a/widget/gtk/NativeMenuGtk.cpp b/widget/gtk/NativeMenuGtk.cpp index 9d413d475e..3f8aeb1940 100644 --- a/widget/gtk/NativeMenuGtk.cpp +++ b/widget/gtk/NativeMenuGtk.cpp @@ -4,6 +4,9 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "NativeMenuGtk.h" +#include "AsyncDBus.h" +#include "gdk/gdkkeysyms-compat.h" +#include "mozilla/BasicEvents.h" #include "mozilla/dom/Document.h" #include "mozilla/dom/DocumentInlines.h" #include "mozilla/dom/XULCommandEvent.h" @@ -15,6 +18,10 @@ #include "nsStubMutationObserver.h" #include "mozilla/dom/Element.h" #include "mozilla/StaticPrefs_widget.h" +#include "DBusMenu.h" +#include "nsLayoutUtils.h" +#include "nsGtkUtils.h" +#include "nsGtkKeyUtils.h" #include <dlfcn.h> #include <gtk/gtk.h> @@ -35,7 +42,8 @@ static bool IsDisabled(const dom::Element& aElement) { } static bool NodeIsRelevant(const nsINode& aNode) { return aNode.IsAnyOfXULElements(nsGkAtoms::menu, nsGkAtoms::menuseparator, - nsGkAtoms::menuitem, nsGkAtoms::menugroup); + nsGkAtoms::menuitem, nsGkAtoms::menugroup, + nsGkAtoms::menubar); } // If this is a radio / checkbox menuitem, get the current value. @@ -155,7 +163,7 @@ void Actions::Clear() { mNextActionIndex = 0; } -class MenuModel final : public nsStubMutationObserver { +class MenuModel : public nsStubMutationObserver { NS_DECL_ISUPPORTS NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED @@ -166,41 +174,60 @@ class MenuModel final : public nsStubMutationObserver { public: explicit MenuModel(dom::Element* aElement) : mElement(aElement) { mElement->AddMutationObserver(this); - mGMenu = dont_AddRef(g_menu_new()); - mActions.mGroup = dont_AddRef(g_simple_action_group_new()); - } - - GMenuModel* GetModel() { return G_MENU_MODEL(mGMenu.get()); } - GActionGroup* GetActionGroup() { - return G_ACTION_GROUP(mActions.mGroup.get()); } dom::Element* Element() { return mElement; } - void RecomputeModelIfNeeded(); + void RecomputeModelIfNeeded() { + if (!mDirty) { + return; + } + RecomputeModel(); + mDirty = false; + } - bool IsShowing() { return mPoppedUp; } + bool IsShowing() { return mShowing; } void WillShow() { - mPoppedUp = true; + mShowing = true; RecomputeModelIfNeeded(); } - void DidHide() { mPoppedUp = false; } + void DidHide() { mShowing = false; } - private: + protected: + virtual void RecomputeModel() = 0; virtual ~MenuModel() { mElement->RemoveMutationObserver(this); } void DirtyModel() { mDirty = true; - if (mPoppedUp) { + if (mShowing) { RecomputeModelIfNeeded(); } } RefPtr<dom::Element> mElement; + bool mDirty = true; + bool mShowing = false; +}; + +class MenuModelGMenu final : public MenuModel { + public: + explicit MenuModelGMenu(dom::Element* aElement) : MenuModel(aElement) { + mGMenu = dont_AddRef(g_menu_new()); + mActions.mGroup = dont_AddRef(g_simple_action_group_new()); + } + + GMenuModel* GetModel() { return G_MENU_MODEL(mGMenu.get()); } + GActionGroup* GetActionGroup() { + return G_ACTION_GROUP(mActions.mGroup.get()); + } + + protected: + void RecomputeModel() override; + static void RecomputeModelFor(GMenu* aMenu, Actions& aActions, + const dom::Element& aElement); + RefPtr<GMenu> mGMenu; Actions mActions; - bool mDirty = true; - bool mPoppedUp = false; }; NS_IMPL_ISUPPORTS(MenuModel, nsIMutationObserver) @@ -243,8 +270,8 @@ static const dom::Element* GetMenuPopupChild(const dom::Element& aElement) { return nullptr; } -static void RecomputeModelFor(GMenu* aMenu, Actions& aActions, - const dom::Element& aElement) { +void MenuModelGMenu::RecomputeModelFor(GMenu* aMenu, Actions& aActions, + const dom::Element& aElement) { RefPtr<GMenu> sectionMenu; auto FlushSectionMenu = [&] { if (sectionMenu) { @@ -305,10 +332,7 @@ static void RecomputeModelFor(GMenu* aMenu, Actions& aActions, FlushSectionMenu(); } -void MenuModel::RecomputeModelIfNeeded() { - if (!mDirty) { - return; - } +void MenuModelGMenu::RecomputeModel() { mActions.Clear(); g_menu_remove_all(mGMenu.get()); RecomputeModelFor(mGMenu.get(), mActions, *mElement); @@ -341,7 +365,7 @@ METHOD_SIGNAL(Unmap); #undef METHOD_SIGNAL NativeMenuGtk::NativeMenuGtk(dom::Element* aElement) - : mMenuModel(MakeRefPtr<MenuModel>(aElement)) { + : mMenuModel(MakeRefPtr<MenuModelGMenu>(aElement)) { // Floating, so no need to dont_AddRef. mNativeMenu = gtk_menu_new_from_model(mMenuModel->GetModel()); gtk_widget_insert_action_group(mNativeMenu.get(), "menu", @@ -421,4 +445,383 @@ void NativeMenuGtk::CloseSubmenu(dom::Element*) { // TODO: For testing mostly. } +#ifdef MOZ_ENABLE_DBUS + +class MenubarModelDBus final : public MenuModel { + public: + explicit MenubarModelDBus(dom::Element* aElement) : MenuModel(aElement) { + mRoot = dont_AddRef(dbusmenu_menuitem_new()); + dbusmenu_menuitem_set_root(mRoot.get(), true); + mShowing = true; + } + + DbusmenuMenuitem* Root() const { return mRoot.get(); } + + protected: + void RecomputeModel() override; + static void AppendMenuItem(DbusmenuMenuitem* aParent, + const dom::Element* aElement); + static void AppendSeparator(DbusmenuMenuitem* aParent); + static void AppendSubmenu(DbusmenuMenuitem* aParent, + const dom::Element* aMenu, + const dom::Element* aPopup); + static uint RecomputeModelFor(DbusmenuMenuitem* aParent, + const dom::Element& aElement); + + RefPtr<DbusmenuMenuitem> mRoot; +}; + +void MenubarModelDBus::RecomputeModel() { + while (GList* children = dbusmenu_menuitem_get_children(mRoot.get())) { + auto* first = static_cast<DbusmenuMenuitem*>(children->data); + if (!first) { + break; + } + dbusmenu_menuitem_child_delete(mRoot.get(), first); + } + RecomputeModelFor(mRoot, *Element()); +} + +static const dom::Element* RelevantElementForKeys( + const dom::Element* aElement) { + nsAutoString key; + aElement->GetAttr(nsGkAtoms::key, key); + if (!key.IsEmpty()) { + dom::Document* document = aElement->OwnerDoc(); + dom::Element* element = document->GetElementById(key); + if (element) { + return element; + } + } + return aElement; +} + +static uint32_t ParseKey(const nsAString& aKey, const nsAString& aKeyCode) { + guint key = 0; + if (!aKey.IsEmpty()) { + key = gdk_unicode_to_keyval(*aKey.BeginReading()); + } + + if (key == 0 && !aKeyCode.IsEmpty()) { + key = KeymapWrapper::ConvertGeckoKeyCodeToGDKKeyval(aKeyCode); + } + + return key; +} + +static uint32_t KeyFrom(const dom::Element* aElement) { + const auto* element = RelevantElementForKeys(aElement); + + nsAutoString key; + nsAutoString keycode; + element->GetAttr(nsGkAtoms::key, key); + element->GetAttr(nsGkAtoms::keycode, keycode); + + return ParseKey(key, keycode); +} + +// TODO(emilio): Unify with nsMenuUtilsX::GeckoModifiersForNodeAttribute (or +// at least switch to strtok_r). +static uint32_t ParseModifiers(const nsAString& aModifiers) { + if (aModifiers.IsEmpty()) { + return 0; + } + + uint32_t modifier = 0; + char* str = ToNewUTF8String(aModifiers); + char* token = strtok(str, ", \t"); + while (token) { + if (nsCRT::strcmp(token, "shift") == 0) { + modifier |= GDK_SHIFT_MASK; + } else if (nsCRT::strcmp(token, "alt") == 0) { + modifier |= GDK_MOD1_MASK; + } else if (nsCRT::strcmp(token, "meta") == 0) { + modifier |= GDK_META_MASK; + } else if (nsCRT::strcmp(token, "control") == 0) { + modifier |= GDK_CONTROL_MASK; + } else if (nsCRT::strcmp(token, "accel") == 0) { + auto accel = WidgetInputEvent::AccelModifier(); + if (accel == MODIFIER_META) { + modifier |= GDK_META_MASK; + } else if (accel == MODIFIER_ALT) { + modifier |= GDK_MOD1_MASK; + } else if (accel == MODIFIER_CONTROL) { + modifier |= GDK_CONTROL_MASK; + } + } + + token = strtok(nullptr, ", \t"); + } + + free(str); + + return modifier; +} + +static uint32_t ModifiersFrom(const dom::Element* aContent) { + const auto* element = RelevantElementForKeys(aContent); + + nsAutoString modifiers; + element->GetAttr(nsGkAtoms::modifiers, modifiers); + + return ParseModifiers(modifiers); +} + +static void UpdateAccel(DbusmenuMenuitem* aItem, const nsIContent* aContent) { + uint32_t key = KeyFrom(aContent->AsElement()); + if (key != 0) { + dbusmenu_menuitem_property_set_shortcut( + aItem, key, + static_cast<GdkModifierType>(ModifiersFrom(aContent->AsElement()))); + } +} + +static void UpdateRadioOrCheck(DbusmenuMenuitem* aItem, + const dom::Element* aContent) { + static mozilla::dom::Element::AttrValuesArray attrs[] = { + nsGkAtoms::checkbox, nsGkAtoms::radio, nullptr}; + int32_t type = aContent->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::type, + attrs, eCaseMatters); + + if (type < 0 || type >= 2) { + return; + } + + if (type == 0) { + dbusmenu_menuitem_property_set(aItem, DBUSMENU_MENUITEM_PROP_TOGGLE_TYPE, + DBUSMENU_MENUITEM_TOGGLE_CHECK); + } else { + dbusmenu_menuitem_property_set(aItem, DBUSMENU_MENUITEM_PROP_TOGGLE_TYPE, + DBUSMENU_MENUITEM_TOGGLE_RADIO); + } + + bool isChecked = aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::checked, + nsGkAtoms::_true, eCaseMatters); + dbusmenu_menuitem_property_set_int( + aItem, DBUSMENU_MENUITEM_PROP_TOGGLE_STATE, + isChecked ? DBUSMENU_MENUITEM_TOGGLE_STATE_CHECKED + : DBUSMENU_MENUITEM_TOGGLE_STATE_UNCHECKED); +} + +static void UpdateEnabled(DbusmenuMenuitem* aItem, const nsIContent* aContent) { + bool disabled = aContent->AsElement()->AttrValueIs( + kNameSpaceID_None, nsGkAtoms::disabled, nsGkAtoms::_true, eCaseMatters); + + dbusmenu_menuitem_property_set_bool(aItem, DBUSMENU_MENUITEM_PROP_ENABLED, + !disabled); +} + +// we rebuild the dbus model when elements are removed from the DOM, +// so this isn't going to trigger for asynchronous +static MOZ_CAN_RUN_SCRIPT void DBusActivationCallback( + DbusmenuMenuitem* aMenuitem, guint aTimestamp, gpointer aUserData) { + RefPtr element = static_cast<dom::Element*>(aUserData); + ActivateItem(*element); +} + +static void ConnectActivated(DbusmenuMenuitem* aItem, + const dom::Element* aContent) { + g_signal_connect(G_OBJECT(aItem), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, + G_CALLBACK(DBusActivationCallback), + const_cast<dom::Element*>(aContent)); +} + +static MOZ_CAN_RUN_SCRIPT void DBusAboutToShowCallback( + DbusmenuMenuitem* aMenuitem, gpointer aUserData) { + RefPtr element = static_cast<dom::Element*>(aUserData); + FireEvent(element, eXULPopupShowing); + FireEvent(element, eXULPopupShown); +} + +static void ConnectAboutToShow(DbusmenuMenuitem* aItem, + const dom::Element* aContent) { + g_signal_connect(G_OBJECT(aItem), DBUSMENU_MENUITEM_SIGNAL_ABOUT_TO_SHOW, + G_CALLBACK(DBusAboutToShowCallback), + const_cast<dom::Element*>(aContent)); +} + +void MenubarModelDBus::AppendMenuItem(DbusmenuMenuitem* aParent, + const dom::Element* aChild) { + nsAutoString label; + aChild->GetAttr(nsGkAtoms::label, label); + if (label.IsEmpty()) { + aChild->GetAttr(nsGkAtoms::aria_label, label); + } + RefPtr<DbusmenuMenuitem> child = dont_AddRef(dbusmenu_menuitem_new()); + dbusmenu_menuitem_property_set(child, DBUSMENU_MENUITEM_PROP_LABEL, + NS_ConvertUTF16toUTF8(label).get()); + dbusmenu_menuitem_child_append(aParent, child); + UpdateAccel(child, aChild); + UpdateRadioOrCheck(child, aChild); + UpdateEnabled(child, aChild); + ConnectActivated(child, aChild); + // TODO: icons +} + +void MenubarModelDBus::AppendSeparator(DbusmenuMenuitem* aParent) { + RefPtr<DbusmenuMenuitem> child = dont_AddRef(dbusmenu_menuitem_new()); + dbusmenu_menuitem_property_set(child, DBUSMENU_MENUITEM_PROP_TYPE, + "separator"); + dbusmenu_menuitem_child_append(aParent, child); +} + +void MenubarModelDBus::AppendSubmenu(DbusmenuMenuitem* aParent, + const dom::Element* aMenu, + const dom::Element* aPopup) { + RefPtr<DbusmenuMenuitem> submenu = dont_AddRef(dbusmenu_menuitem_new()); + if (RecomputeModelFor(submenu, *aPopup) == 0) { + RefPtr<DbusmenuMenuitem> placeholder = dont_AddRef(dbusmenu_menuitem_new()); + dbusmenu_menuitem_child_append(submenu, placeholder); + } + nsAutoString label; + aMenu->GetAttr(nsGkAtoms::label, label); + ConnectAboutToShow(submenu, aPopup); + dbusmenu_menuitem_property_set(submenu, DBUSMENU_MENUITEM_PROP_LABEL, + NS_ConvertUTF16toUTF8(label).get()); + dbusmenu_menuitem_child_append(aParent, submenu); +} + +uint MenubarModelDBus::RecomputeModelFor(DbusmenuMenuitem* aParent, + const dom::Element& aElement) { + uint childCount = 0; + for (const nsIContent* child = aElement.GetFirstChild(); child; + child = child->GetNextSibling()) { + if (child->IsXULElement(nsGkAtoms::menuitem) && + !IsDisabled(*child->AsElement())) { + AppendMenuItem(aParent, child->AsElement()); + childCount++; + continue; + } + if (child->IsXULElement(nsGkAtoms::menuseparator)) { + AppendSeparator(aParent); + childCount++; + continue; + } + if (child->IsXULElement(nsGkAtoms::menu) && + !IsDisabled(*child->AsElement())) { + if (const auto* popup = GetMenuPopupChild(*child->AsElement())) { + childCount++; + AppendSubmenu(aParent, child->AsElement(), popup); + } + } + } + return childCount; +} + +void DBusMenuBar::NameOwnerChangedCallback(GObject*, GParamSpec*, + gpointer user_data) { + static_cast<DBusMenuBar*>(user_data)->OnNameOwnerChanged(); +} + +void DBusMenuBar::OnNameOwnerChanged() { + GUniquePtr<gchar> nameOwner(g_dbus_proxy_get_name_owner(mProxy)); + if (!nameOwner) { + return; + } + + RefPtr win = mMenuModel->Element()->OwnerDoc()->GetInnerWindow(); + if (NS_WARN_IF(!win)) { + return; + } + nsIWidget* widget = nsGlobalWindowInner::Cast(win.get())->GetNearestWidget(); + if (NS_WARN_IF(!widget)) { + return; + } + auto* gdkWin = + static_cast<GdkWindow*>(widget->GetNativeData(NS_NATIVE_WINDOW)); + if (NS_WARN_IF(!gdkWin)) { + return; + } + +# ifdef MOZ_WAYLAND + if (auto* display = widget::WaylandDisplayGet()) { + if (!StaticPrefs::widget_gtk_global_menu_wayland_enabled()) { + return; + } + xdg_dbus_annotation_manager_v1* annotationManager = + display->GetXdgDbusAnnotationManager(); + if (NS_WARN_IF(!annotationManager)) { + return; + } + + wl_surface* surface = gdk_wayland_window_get_wl_surface(gdkWin); + if (NS_WARN_IF(!surface)) { + return; + } + + GDBusConnection* connection = g_dbus_proxy_get_connection(mProxy); + const char* myServiceName = g_dbus_connection_get_unique_name(connection); + if (NS_WARN_IF(!myServiceName)) { + return; + } + + // FIXME(emilio, bug 1883209): Nothing deletes this as of right now. + mAnnotation = xdg_dbus_annotation_manager_v1_create_surface( + annotationManager, "com.canonical.dbusmenu", surface); + + xdg_dbus_annotation_v1_set_address(mAnnotation, myServiceName, + mObjectPath.get()); + return; + } +# endif +# ifdef MOZ_X11 + // legacy path + auto xid = GDK_WINDOW_XID(gdkWin); + widget::DBusProxyCall(mProxy, "RegisterWindow", + g_variant_new("(uo)", xid, mObjectPath.get()), + G_DBUS_CALL_FLAGS_NONE) + ->Then( + GetCurrentSerialEventTarget(), __func__, + [self = RefPtr{this}](RefPtr<GVariant>&& aResult) { + self->mMenuModel->Element()->SetBoolAttr(nsGkAtoms::hidden, true); + }, + [self = RefPtr{this}](GUniquePtr<GError>&& aError) { + g_printerr("Failed to register window menubar: %s\n", + aError->message); + self->mMenuModel->Element()->SetBoolAttr(nsGkAtoms::hidden, false); + }); +# endif +} + +static unsigned sID = 0; + +DBusMenuBar::DBusMenuBar(dom::Element* aElement) + : mObjectPath(nsPrintfCString("/com/canonical/menu/%u", sID++)), + mMenuModel(MakeRefPtr<MenubarModelDBus>(aElement)), + mServer(dont_AddRef(dbusmenu_server_new(mObjectPath.get()))) { + mMenuModel->RecomputeModelIfNeeded(); + dbusmenu_server_set_root(mServer.get(), mMenuModel->Root()); +} + +RefPtr<DBusMenuBar> DBusMenuBar::Create(dom::Element* aElement) { + RefPtr<DBusMenuBar> self = new DBusMenuBar(aElement); + widget::CreateDBusProxyForBus( + G_BUS_TYPE_SESSION, + GDBusProxyFlags(G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | + G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS | + G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START), + nullptr, "com.canonical.AppMenu.Registrar", + "/com/canonical/AppMenu/Registrar", "com.canonical.AppMenu.Registrar") + ->Then( + GetCurrentSerialEventTarget(), __func__, + [self](RefPtr<GDBusProxy>&& aProxy) { + self->mProxy = std::move(aProxy); + g_signal_connect(self->mProxy, "notify::g-name-owner", + G_CALLBACK(NameOwnerChangedCallback), self.get()); + self->OnNameOwnerChanged(); + }, + [](GUniquePtr<GError>&& aError) { + g_printerr("Failed to create DBUS proxy for menubar: %s\n", + aError->message); + }); + return self; +} + +DBusMenuBar::~DBusMenuBar() { +# ifdef MOZ_WAYLAND + MozClearPointer(mAnnotation, xdg_dbus_annotation_v1_destroy); +# endif +} +#endif + } // namespace mozilla::widget diff --git a/widget/gtk/NativeMenuGtk.h b/widget/gtk/NativeMenuGtk.h index 3f1f3213c1..ca0f64c8ff 100644 --- a/widget/gtk/NativeMenuGtk.h +++ b/widget/gtk/NativeMenuGtk.h @@ -1,4 +1,3 @@ - /* -*- Mode: c++; tab-width: 2; indent-tabs-mode: nil; -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -7,10 +6,13 @@ #ifndef mozilla_widget_NativeMenuGtk_h #define mozilla_widget_NativeMenuGtk_h +#include "mozilla/RefCounted.h" #include "mozilla/widget/NativeMenu.h" #include "mozilla/EventForwards.h" #include "GRefPtr.h" +struct xdg_dbus_annotation_v1; + namespace mozilla { namespace dom { @@ -19,7 +21,8 @@ class Element; namespace widget { -class MenuModel; +class MenuModelGMenu; +class MenubarModelDBus; class NativeMenuGtk : public NativeMenu { public: @@ -54,10 +57,35 @@ class NativeMenuGtk : public NativeMenu { bool mPoppedUp = false; RefPtr<GtkWidget> mNativeMenu; - RefPtr<MenuModel> mMenuModel; + RefPtr<MenuModelGMenu> mMenuModel; nsTArray<NativeMenu::Observer*> mObservers; }; +#ifdef MOZ_ENABLE_DBUS + +class DBusMenuBar final : public RefCounted<DBusMenuBar> { + public: + MOZ_DECLARE_REFCOUNTED_TYPENAME(DBusMenuBar) + static RefPtr<DBusMenuBar> Create(dom::Element*); + ~DBusMenuBar(); + + protected: + explicit DBusMenuBar(dom::Element* aElement); + + static void NameOwnerChangedCallback(GObject*, GParamSpec*, gpointer); + void OnNameOwnerChanged(); + + nsCString mObjectPath; + RefPtr<MenubarModelDBus> mMenuModel; + RefPtr<DbusmenuServer> mServer; + RefPtr<GDBusProxy> mProxy; +# ifdef MOZ_WAYLAND + xdg_dbus_annotation_v1* mAnnotation = nullptr; +# endif +}; + +#endif + } // namespace widget } // namespace mozilla diff --git a/widget/gtk/NativeMenuSupport.cpp b/widget/gtk/NativeMenuSupport.cpp index 4360867fff..60517cd526 100644 --- a/widget/gtk/NativeMenuSupport.cpp +++ b/widget/gtk/NativeMenuSupport.cpp @@ -5,8 +5,11 @@ #include "mozilla/widget/NativeMenuSupport.h" +#include "mozilla/StaticPrefs_widget.h" #include "MainThreadUtils.h" #include "NativeMenuGtk.h" +#include "DBusMenu.h" +#include "nsWindow.h" namespace mozilla::widget { @@ -14,7 +17,14 @@ void NativeMenuSupport::CreateNativeMenuBar(nsIWidget* aParent, dom::Element* aMenuBarElement) { MOZ_RELEASE_ASSERT(NS_IsMainThread(), "Attempting to create native menu bar on wrong thread!"); - // TODO + +#ifdef MOZ_ENABLE_DBUS + if (aMenuBarElement && StaticPrefs::widget_gtk_global_menu_enabled() && + DBusMenuFunctions::Init()) { + static_cast<nsWindow*>(aParent)->SetDBusMenuBar( + DBusMenuBar::Create(aMenuBarElement)); + } +#endif } already_AddRefed<NativeMenu> NativeMenuSupport::CreateNativeContextMenu( diff --git a/widget/gtk/WakeLockListener.cpp b/widget/gtk/WakeLockListener.cpp index b2c43cb485..33bb374cef 100644 --- a/widget/gtk/WakeLockListener.cpp +++ b/widget/gtk/WakeLockListener.cpp @@ -137,6 +137,7 @@ class WakeLockTopic { bool UninhibitWaylandIdle(); #endif + bool IsNativeWakeLock(int aWakeLockType); bool IsWakeLockTypeAvailable(int aWakeLockType); bool SwitchToNextWakeLockType(); @@ -779,8 +780,14 @@ nsresult WakeLockTopic::InhibitScreensaver() { mShouldInhibit = true; // Iterate through wake lock types in case of failure. - while (!SendInhibit() && SwitchToNextWakeLockType()) { - ; + while (!SendInhibit()) { + // We don't switch away from native locks. Just try again. + if (IsNativeWakeLock(sWakeLockType)) { + return NS_ERROR_FAILURE; + } + if (!SwitchToNextWakeLockType()) { + return NS_ERROR_FAILURE; + } } return (sWakeLockType != Unsupported) ? NS_OK : NS_ERROR_FAILURE; @@ -850,6 +857,21 @@ bool WakeLockTopic::IsWakeLockTypeAvailable(int aWakeLockType) { } } +bool WakeLockTopic::IsNativeWakeLock(int aWakeLockType) { + switch (aWakeLockType) { +#if defined(MOZ_X11) + case XScreenSaver: + return true; +#endif +#if defined(MOZ_WAYLAND) + case WaylandIdleInhibit: + return true; +#endif + default: + return false; + } +} + bool WakeLockTopic::SwitchToNextWakeLockType() { WAKE_LOCK_LOG("WakeLockTopic::SwitchToNextWakeLockType() WakeLockType %s", WakeLockTypeNames[sWakeLockType]); diff --git a/widget/gtk/WidgetStyleCache.cpp b/widget/gtk/WidgetStyleCache.cpp index b7c1cf8a9f..13b194a64e 100644 --- a/widget/gtk/WidgetStyleCache.cpp +++ b/widget/gtk/WidgetStyleCache.cpp @@ -644,10 +644,7 @@ static void CreateHeaderBarButtons() { GtkWidget* headerBar = sWidgetStorage[MOZ_GTK_HEADER_BAR]; MOZ_ASSERT(headerBar, "We're missing header bar widget!"); - gint buttonSpacing = 6; - g_object_get(headerBar, "spacing", &buttonSpacing, nullptr); - - GtkWidget* buttonBox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, buttonSpacing); + GtkWidget* buttonBox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); gtk_container_add(GTK_CONTAINER(headerBar), buttonBox); // We support only LTR headerbar layout for now. gtk_style_context_add_class(gtk_widget_get_style_context(buttonBox), diff --git a/widget/gtk/WindowSurfaceProvider.cpp b/widget/gtk/WindowSurfaceProvider.cpp index b346f0783d..82f9029315 100644 --- a/widget/gtk/WindowSurfaceProvider.cpp +++ b/widget/gtk/WindowSurfaceProvider.cpp @@ -63,24 +63,34 @@ WindowSurfaceProvider::~WindowSurfaceProvider() { } #ifdef MOZ_WAYLAND -void WindowSurfaceProvider::Initialize(RefPtr<nsWindow> aWidget) { +bool WindowSurfaceProvider::Initialize(RefPtr<nsWindow> aWidget) { mWindowSurfaceValid = false; mWidget = std::move(aWidget); + return true; } -void WindowSurfaceProvider::Initialize(GtkCompositorWidget* aCompositorWidget) { +bool WindowSurfaceProvider::Initialize(GtkCompositorWidget* aCompositorWidget) { mWindowSurfaceValid = false; mCompositorWidget = aCompositorWidget; mWidget = static_cast<nsWindow*>(aCompositorWidget->RealWidget()); + return true; } #endif #ifdef MOZ_X11 -void WindowSurfaceProvider::Initialize(Window aWindow, Visual* aVisual, - int aDepth, bool aIsShaped) { +bool WindowSurfaceProvider::Initialize(Window aWindow, bool aIsShaped) { mWindowSurfaceValid = false; + + // Grab the window's visual and depth + XWindowAttributes windowAttrs; + if (!XGetWindowAttributes(DefaultXDisplay(), aWindow, &windowAttrs)) { + NS_WARNING("GtkCompositorWidget(): XGetWindowAttributes() failed!"); + return false; + } + mXWindow = aWindow; - mXVisual = aVisual; - mXDepth = aDepth; + mXVisual = windowAttrs.visual; + mXDepth = windowAttrs.depth; mIsShaped = aIsShaped; + return true; } #endif diff --git a/widget/gtk/WindowSurfaceProvider.h b/widget/gtk/WindowSurfaceProvider.h index a040bbe395..6aaf50e2da 100644 --- a/widget/gtk/WindowSurfaceProvider.h +++ b/widget/gtk/WindowSurfaceProvider.h @@ -45,11 +45,12 @@ class WindowSurfaceProvider final { * while WindowSurfaceProvider is used. */ #ifdef MOZ_WAYLAND - void Initialize(RefPtr<nsWindow> aWidget); - void Initialize(GtkCompositorWidget* aCompositorWidget); + bool Initialize(RefPtr<nsWindow> aWidget); + bool Initialize(GtkCompositorWidget* aCompositorWidget); #endif #ifdef MOZ_X11 - void Initialize(Window aWindow, Visual* aVisual, int aDepth, bool aIsShaped); + bool Initialize(Window aWindow, bool aIsShaped); + Window GetXWindow() const { return mXWindow; } #endif /** diff --git a/widget/gtk/gtk3drawing.cpp b/widget/gtk/gtk3drawing.cpp index 1fa8b95606..122b43d688 100644 --- a/widget/gtk/gtk3drawing.cpp +++ b/widget/gtk/gtk3drawing.cpp @@ -1,4 +1,4 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ @@ -264,7 +264,6 @@ static void CalculateToolbarButtonMetrics(WidgetNodeType aAppearance, gint iconWidth, iconHeight; if (!gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &iconWidth, &iconHeight)) { NS_WARNING("Failed to get Gtk+ icon size for titlebar button!"); - // Use some reasonable fallback size iconWidth = 16; iconHeight = 16; @@ -292,37 +291,7 @@ static void CalculateToolbarButtonMetrics(WidgetNodeType aAppearance, // Place icon at button center. aMetrics->iconXPosition = (width - iconWidth) / 2; aMetrics->iconYPosition = (height - iconHeight) / 2; - - aMetrics->minSizeWithBorderMargin.width = width; - aMetrics->minSizeWithBorderMargin.height = height; -} - -// We support LTR layout only here for now. -static void CalculateToolbarButtonSpacing(WidgetNodeType aAppearance, - ToolbarButtonGTKMetrics* aMetrics) { - GtkStyleContext* style = GetStyleContext(aAppearance); - gtk_style_context_get_margin(style, gtk_style_context_get_state(style), - &aMetrics->buttonMargin); - - // Get titlebar spacing, a default one is 6 pixels (gtk/gtkheaderbar.c) - gint buttonSpacing = 6; - g_object_get(GetWidget(MOZ_GTK_HEADER_BAR), "spacing", &buttonSpacing, - nullptr); - - // We apply spacing as a margin equally to both adjacent buttons. - buttonSpacing /= 2; - - if (!aMetrics->firstButton) { - aMetrics->buttonMargin.left += buttonSpacing; - } - if (!aMetrics->lastButton) { - aMetrics->buttonMargin.right += buttonSpacing; - } - - aMetrics->minSizeWithBorderMargin.width += - aMetrics->buttonMargin.right + aMetrics->buttonMargin.left; - aMetrics->minSizeWithBorderMargin.height += - aMetrics->buttonMargin.top + aMetrics->buttonMargin.bottom; + aMetrics->minSizeWithBorder = {width, height}; } size_t GetGtkHeaderBarButtonLayout(Span<ButtonLayout> aButtonLayout, @@ -388,26 +357,14 @@ static void EnsureToolbarMetrics() { memset(&sToolbarMetrics, 0, sizeof(sToolbarMetrics)); // Calculate titlebar button visibility and positions. - ButtonLayout aButtonLayout[TOOLBAR_BUTTONS]; + ButtonLayout buttonLayout[TOOLBAR_BUTTONS]; size_t activeButtonNums = - GetGtkHeaderBarButtonLayout(Span(aButtonLayout), nullptr); - - for (size_t i = 0; i < activeButtonNums; i++) { - int buttonIndex = - (aButtonLayout[i].mType - MOZ_GTK_HEADER_BAR_BUTTON_CLOSE); - ToolbarButtonGTKMetrics* metrics = sToolbarMetrics.button + buttonIndex; - metrics->visible = true; - // Mark first button - if (!i) { - metrics->firstButton = true; - } - // Mark last button. - if (i == (activeButtonNums - 1)) { - metrics->lastButton = true; - } + GetGtkHeaderBarButtonLayout(Span(buttonLayout), nullptr); - CalculateToolbarButtonMetrics(aButtonLayout[i].mType, metrics); - CalculateToolbarButtonSpacing(aButtonLayout[i].mType, metrics); + for (const auto& layout : Span(buttonLayout, activeButtonNums)) { + int buttonIndex = layout.mType - MOZ_GTK_HEADER_BAR_BUTTON_CLOSE; + ToolbarButtonGTKMetrics* metrics = &sToolbarMetrics.button[buttonIndex]; + CalculateToolbarButtonMetrics(layout.mType, metrics); } sToolbarMetrics.initialized = true; @@ -506,26 +463,27 @@ static gint moz_gtk_button_paint(cairo_t* cr, const GdkRectangle* rect, return MOZ_GTK_SUCCESS; } -static gint moz_gtk_header_bar_button_paint(cairo_t* cr, - const GdkRectangle* aRect, +static gint moz_gtk_header_bar_button_paint(cairo_t* cr, GdkRectangle* aRect, GtkWidgetState* state, GtkReliefStyle relief, WidgetNodeType aIconWidgetType, GtkTextDirection direction) { - GdkRectangle rect = *aRect; - // We need to inset our calculated margin because it also - // contains titlebar button spacing. + GtkWidget* buttonWidget = GetWidget(aIconWidgetType); + if (!buttonWidget) { + return MOZ_GTK_UNKNOWN_WIDGET; + } + const ToolbarButtonGTKMetrics* metrics = GetToolbarButtonMetrics( aIconWidgetType == MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE_RESTORE ? MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE : aIconWidgetType); - Inset(&rect, metrics->buttonMargin); - - GtkWidget* buttonWidget = GetWidget(aIconWidgetType); - if (!buttonWidget) { - return MOZ_GTK_UNKNOWN_WIDGET; + // Vertically center and clamp the rect to the desired size. + if (aRect->height > metrics->minSizeWithBorder.height) { + gint diff = aRect->height - metrics->minSizeWithBorder.height; + aRect->y += diff / 2; + aRect->height = metrics->minSizeWithBorder.height; } - moz_gtk_button_paint(cr, &rect, state, relief, buttonWidget, direction); + moz_gtk_button_paint(cr, aRect, state, relief, buttonWidget, direction); GtkWidget* iconWidget = gtk_bin_get_child(GTK_BIN(GetWidget(aIconWidgetType))); @@ -544,8 +502,9 @@ static gint moz_gtk_header_bar_button_paint(cairo_t* cr, gtk_style_context_set_state(style, state_flags); /* This is available since Gtk+ 3.10 as well as GtkHeaderBar */ - gtk_render_icon_surface(style, cr, surface, rect.x + metrics->iconXPosition, - rect.y + metrics->iconYPosition); + gtk_render_icon_surface(style, cr, surface, + aRect->x + metrics->iconXPosition, + aRect->y + metrics->iconYPosition); gtk_style_context_restore(style); } @@ -1700,22 +1659,6 @@ gint moz_gtk_get_widget_border(WidgetNodeType widget, gint* left, gint* top, return MOZ_GTK_SUCCESS; } - case MOZ_GTK_HEADER_BAR_BUTTON_BOX: { - style = GetStyleContext(MOZ_GTK_HEADER_BAR); - moz_gtk_add_border_padding(style, left, top, right, bottom); - *top = *bottom = 0; - bool leftButtonsPlacement = false; - GetGtkHeaderBarButtonLayout({}, &leftButtonsPlacement); - if (direction == GTK_TEXT_DIR_RTL) { - leftButtonsPlacement = !leftButtonsPlacement; - } - if (leftButtonsPlacement) { - *right = 0; - } else { - *left = 0; - } - return MOZ_GTK_SUCCESS; - } /* These widgets have no borders, since they are not containers. */ case MOZ_GTK_SPLITTER_HORIZONTAL: case MOZ_GTK_SPLITTER_VERTICAL: diff --git a/widget/gtk/gtkdrawing.h b/widget/gtk/gtkdrawing.h index e751dc38c9..4ca226d9c7 100644 --- a/widget/gtk/gtkdrawing.h +++ b/widget/gtk/gtkdrawing.h @@ -76,13 +76,9 @@ typedef struct { } ToggleGTKMetrics; typedef struct { - MozGtkSize minSizeWithBorderMargin; - GtkBorder buttonMargin; + MozGtkSize minSizeWithBorder; gint iconXPosition; gint iconYPosition; - bool visible; - bool firstButton; - bool lastButton; } ToolbarButtonGTKMetrics; #define TOOLBAR_BUTTONS 3 @@ -269,8 +265,6 @@ enum WidgetNodeType : int { MOZ_GTK_HEADER_BAR, /* Paints a GtkHeaderBar in maximized state */ MOZ_GTK_HEADER_BAR_MAXIMIZED, - /* Container for GtkHeaderBar buttons */ - MOZ_GTK_HEADER_BAR_BUTTON_BOX, /* Paints GtkHeaderBar title buttons. * Keep the order here as MOZ_GTK_HEADER_BAR_BUTTON_* are processed * as an array from MOZ_GTK_HEADER_BAR_BUTTON_CLOSE to the last one. diff --git a/widget/gtk/moz.build b/widget/gtk/moz.build index 0d3916853c..87866b1a9b 100644 --- a/widget/gtk/moz.build +++ b/widget/gtk/moz.build @@ -173,6 +173,7 @@ if CONFIG["MOZ_ENABLE_DBUS"]: ] UNIFIED_SOURCES += [ "AsyncDBus.cpp", + "DBusMenu.cpp", ] CXXFLAGS += CONFIG["MOZ_DBUS_CFLAGS"] diff --git a/widget/gtk/nsGtkKeyUtils.cpp b/widget/gtk/nsGtkKeyUtils.cpp index 7157a09664..e49f64a64b 100644 --- a/widget/gtk/nsGtkKeyUtils.cpp +++ b/widget/gtk/nsGtkKeyUtils.cpp @@ -2125,6 +2125,212 @@ guint KeymapWrapper::GetGDKKeyvalWithoutModifier( return keyval; } +struct KeyCodeData { + const char* str; + size_t strlength; + uint32_t keycode; +}; + +static struct KeyCodeData gKeyCodes[] = { +#define NS_DEFINE_VK(aDOMKeyName, aDOMKeyCode) \ + {#aDOMKeyName, sizeof(#aDOMKeyName) - 1, aDOMKeyCode}, +#include "mozilla/VirtualKeyCodeList.h" +#undef NS_DEFINE_VK + {nullptr, 0, 0}}; + +struct KeyPair { + uint32_t DOMKeyCode; + guint GDKKeyval; +}; + +// +// Netscape keycodes are defined in widget/public/nsGUIEvent.h +// GTK keycodes are defined in <gdk/gdkkeysyms.h> +// +static const KeyPair gKeyPairs[] = { + {NS_VK_CANCEL, GDK_Cancel}, + {NS_VK_BACK, GDK_BackSpace}, + {NS_VK_TAB, GDK_Tab}, + {NS_VK_CLEAR, GDK_Clear}, + {NS_VK_RETURN, GDK_Return}, + {NS_VK_SHIFT, GDK_Shift_L}, + {NS_VK_CONTROL, GDK_Control_L}, + {NS_VK_ALT, GDK_Alt_L}, + {NS_VK_META, GDK_Meta_L}, + + // Assume that Super or Hyper is always mapped to physical Win key. + {NS_VK_WIN, GDK_Super_L}, + + // GTK's AltGraph key is similar to Mac's Option (Alt) key. However, + // unfortunately, browsers on Mac are using NS_VK_ALT for it even though + // it's really different from Alt key on Windows. + // On the other hand, GTK's AltGrapsh keys are really different from + // Alt key. However, there is no AltGrapsh key on Windows. On Windows, + // both Ctrl and Alt keys are pressed internally when AltGr key is pressed. + // For some languages' users, AltGraph key is important, so, web + // applications on such locale may want to know AltGraph key press. + // Therefore, we should map AltGr keycode for them only on GTK. + {NS_VK_ALTGR, GDK_ISO_Level3_Shift}, + + {NS_VK_PAUSE, GDK_Pause}, + {NS_VK_CAPS_LOCK, GDK_Caps_Lock}, + {NS_VK_ESCAPE, GDK_Escape}, + // { NS_VK_ACCEPT, GDK_XXX }, + // { NS_VK_MODECHANGE, GDK_XXX }, + {NS_VK_SPACE, GDK_space}, + {NS_VK_PAGE_UP, GDK_Page_Up}, + {NS_VK_PAGE_DOWN, GDK_Page_Down}, + {NS_VK_END, GDK_End}, + {NS_VK_HOME, GDK_Home}, + {NS_VK_LEFT, GDK_Left}, + {NS_VK_UP, GDK_Up}, + {NS_VK_RIGHT, GDK_Right}, + {NS_VK_DOWN, GDK_Down}, + {NS_VK_SELECT, GDK_Select}, + {NS_VK_PRINT, GDK_Print}, + {NS_VK_EXECUTE, GDK_Execute}, + {NS_VK_PRINTSCREEN, GDK_Print}, + {NS_VK_INSERT, GDK_Insert}, + {NS_VK_DELETE, GDK_Delete}, + {NS_VK_HELP, GDK_Help}, + + {NS_VK_NUM_LOCK, GDK_Num_Lock}, + {NS_VK_SCROLL_LOCK, GDK_Scroll_Lock}, + + // Function keys + {NS_VK_F1, GDK_F1}, + {NS_VK_F2, GDK_F2}, + {NS_VK_F3, GDK_F3}, + {NS_VK_F4, GDK_F4}, + {NS_VK_F5, GDK_F5}, + {NS_VK_F6, GDK_F6}, + {NS_VK_F7, GDK_F7}, + {NS_VK_F8, GDK_F8}, + {NS_VK_F9, GDK_F9}, + {NS_VK_F10, GDK_F10}, + {NS_VK_F11, GDK_F11}, + {NS_VK_F12, GDK_F12}, + {NS_VK_F13, GDK_F13}, + {NS_VK_F14, GDK_F14}, + {NS_VK_F15, GDK_F15}, + {NS_VK_F16, GDK_F16}, + {NS_VK_F17, GDK_F17}, + {NS_VK_F18, GDK_F18}, + {NS_VK_F19, GDK_F19}, + {NS_VK_F20, GDK_F20}, + {NS_VK_F21, GDK_F21}, + {NS_VK_F22, GDK_F22}, + {NS_VK_F23, GDK_F23}, + {NS_VK_F24, GDK_F24}, + + // context menu key, keysym 0xff67, typically keycode 117 on 105-key + // (Microsoft) x86 keyboards, located between right 'Windows' key and right + // Ctrl key + {NS_VK_CONTEXT_MENU, GDK_Menu}, + {NS_VK_SLEEP, GDK_Sleep}, + + {NS_VK_ATTN, GDK_3270_Attn}, + {NS_VK_CRSEL, GDK_3270_CursorSelect}, + {NS_VK_EXSEL, GDK_3270_ExSelect}, + {NS_VK_EREOF, GDK_3270_EraseEOF}, + {NS_VK_PLAY, GDK_3270_Play}, + //{ NS_VK_ZOOM, GDK_XXX }, + {NS_VK_PA1, GDK_3270_PA1}, + + {NS_VK_MULTIPLY, GDK_KP_Multiply}, + {NS_VK_ADD, GDK_KP_Add}, + {NS_VK_SEPARATOR, GDK_KP_Separator}, + {NS_VK_SUBTRACT, GDK_KP_Subtract}, + {NS_VK_DECIMAL, GDK_KP_Decimal}, + {NS_VK_DIVIDE, GDK_KP_Divide}, + {NS_VK_NUMPAD0, GDK_KP_0}, + {NS_VK_NUMPAD1, GDK_KP_1}, + {NS_VK_NUMPAD2, GDK_KP_2}, + {NS_VK_NUMPAD3, GDK_KP_3}, + {NS_VK_NUMPAD4, GDK_KP_4}, + {NS_VK_NUMPAD5, GDK_KP_5}, + {NS_VK_NUMPAD6, GDK_KP_6}, + {NS_VK_NUMPAD7, GDK_KP_7}, + {NS_VK_NUMPAD8, GDK_KP_8}, + {NS_VK_NUMPAD9, GDK_KP_9}, + {NS_VK_SPACE, GDK_space}, + {NS_VK_COLON, GDK_colon}, + {NS_VK_SEMICOLON, GDK_semicolon}, + {NS_VK_LESS_THAN, GDK_less}, + {NS_VK_EQUALS, GDK_equal}, + {NS_VK_GREATER_THAN, GDK_greater}, + {NS_VK_QUESTION_MARK, GDK_question}, + {NS_VK_AT, GDK_at}, + {NS_VK_CIRCUMFLEX, GDK_asciicircum}, + {NS_VK_EXCLAMATION, GDK_exclam}, + {NS_VK_DOUBLE_QUOTE, GDK_quotedbl}, + {NS_VK_HASH, GDK_numbersign}, + {NS_VK_DOLLAR, GDK_dollar}, + {NS_VK_PERCENT, GDK_percent}, + {NS_VK_AMPERSAND, GDK_ampersand}, + {NS_VK_UNDERSCORE, GDK_underscore}, + {NS_VK_OPEN_PAREN, GDK_parenleft}, + {NS_VK_CLOSE_PAREN, GDK_parenright}, + {NS_VK_ASTERISK, GDK_asterisk}, + {NS_VK_PLUS, GDK_plus}, + {NS_VK_PIPE, GDK_bar}, + {NS_VK_HYPHEN_MINUS, GDK_minus}, + {NS_VK_OPEN_CURLY_BRACKET, GDK_braceleft}, + {NS_VK_CLOSE_CURLY_BRACKET, GDK_braceright}, + {NS_VK_TILDE, GDK_asciitilde}, + {NS_VK_COMMA, GDK_comma}, + {NS_VK_PERIOD, GDK_period}, + {NS_VK_SLASH, GDK_slash}, + {NS_VK_BACK_QUOTE, GDK_grave}, + {NS_VK_OPEN_BRACKET, GDK_bracketleft}, + {NS_VK_BACK_SLASH, GDK_backslash}, + {NS_VK_CLOSE_BRACKET, GDK_bracketright}, + {NS_VK_QUOTE, GDK_apostrophe}, +}; + +/* static */ +guint KeymapWrapper::ConvertGeckoKeyCodeToGDKKeyval(const nsAString& aKeyCode) { + NS_ConvertUTF16toUTF8 keyName(aKeyCode); + ToUpperCase(keyName); // We want case-insensitive comparison with data + // stored as uppercase. + + uint32_t keyCode = 0; + + uint32_t keyNameLength = keyName.Length(); + const char* keyNameStr = keyName.get(); + for (const auto& code : gKeyCodes) { + if (keyNameLength == code.strlength && + !nsCRT::strcmp(code.str, keyNameStr)) { + keyCode = code.keycode; + break; + } + } + + // First, try to handle alphanumeric input, not listed in nsKeycodes: + // most likely, more letters will be getting typed in than things in + // the key list, so we will look through these first. + + if (keyCode >= NS_VK_A && keyCode <= NS_VK_Z) { + // gdk and DOM both use the ASCII codes for these keys. + return keyCode; + } + + // numbers + if (keyCode >= NS_VK_0 && keyCode <= NS_VK_9) { + // gdk and DOM both use the ASCII codes for these keys. + return keyCode - NS_VK_0 + GDK_0; + } + + // misc other things + for (const auto& pair : gKeyPairs) { + if (pair.DOMKeyCode == keyCode) { + return pair.GDKKeyval; + } + } + + return 0; +} + /* static */ uint32_t KeymapWrapper::GetDOMKeyCodeFromKeyPairs(guint aGdkKeyval) { switch (aGdkKeyval) { diff --git a/widget/gtk/nsGtkKeyUtils.h b/widget/gtk/nsGtkKeyUtils.h index aac9d446f3..b1f1fae138 100644 --- a/widget/gtk/nsGtkKeyUtils.h +++ b/widget/gtk/nsGtkKeyUtils.h @@ -55,6 +55,8 @@ class KeymapWrapper { */ static CodeNameIndex ComputeDOMCodeNameIndex(const GdkEventKey* aGdkKeyEvent); + static guint ConvertGeckoKeyCodeToGDKKeyval(const nsAString& aKeyCode); + /** * We need to translate modifiers masks from Gdk to Gecko. * MappedModifier is a table of mapped modifiers, we ignore other diff --git a/widget/gtk/nsLookAndFeel.cpp b/widget/gtk/nsLookAndFeel.cpp index c4b430d2eb..040d942cdf 100644 --- a/widget/gtk/nsLookAndFeel.cpp +++ b/widget/gtk/nsLookAndFeel.cpp @@ -905,10 +905,6 @@ nsresult nsLookAndFeel::NativeGetInt(IntID aID, int32_t& aResult) { aResult = (int32_t)delay; break; } - case IntID::TooltipDelay: { - aResult = 500; - break; - } case IntID::MenusCanOverlapOSBar: // we want XUL popups to be able to overlap the task bar. aResult = 1; @@ -1035,6 +1031,11 @@ nsresult nsLookAndFeel::NativeGetInt(IntID aID, int32_t& aResult) { aResult = EffectiveTheme().mTitlebarRadius; break; } + case IntID::TitlebarButtonSpacing: { + EnsureInit(); + aResult = EffectiveTheme().mTitlebarButtonSpacing; + break; + } case IntID::AllowOverlayScrollbarsOverlap: { aResult = 1; break; @@ -1976,6 +1977,10 @@ void nsLookAndFeel::PerThemeData::Init() { mTitlebar = GetColorPair(style, GTK_STATE_FLAG_NORMAL); mTitlebarInactive = GetColorPair(style, GTK_STATE_FLAG_BACKDROP); mTitlebarRadius = IsSolidCSDStyleUsed() ? 0 : GetBorderRadius(style); + // Get titlebar spacing, a default one is 6 pixels (gtk/gtkheaderbar.c) + mTitlebarButtonSpacing = 6; + g_object_get(GetWidget(MOZ_GTK_HEADER_BAR), "spacing", + &mTitlebarButtonSpacing, nullptr); } // We special-case the header bar color in Adwaita, Yaru and Breeze to be the diff --git a/widget/gtk/nsLookAndFeel.h b/widget/gtk/nsLookAndFeel.h index 56608d331f..1ef28afe21 100644 --- a/widget/gtk/nsLookAndFeel.h +++ b/widget/gtk/nsLookAndFeel.h @@ -137,6 +137,7 @@ class nsLookAndFeel final : public nsXPLookAndFeel { float mCaretRatio = 0.0f; int32_t mTitlebarRadius = 0; + int32_t mTitlebarButtonSpacing = 0; char16_t mInvisibleCharacter = 0; bool mMenuSupportsDrag = false; diff --git a/widget/gtk/nsNativeThemeGTK.cpp b/widget/gtk/nsNativeThemeGTK.cpp index 06d9b48007..16945349bb 100644 --- a/widget/gtk/nsNativeThemeGTK.cpp +++ b/widget/gtk/nsNativeThemeGTK.cpp @@ -191,6 +191,10 @@ bool nsNativeThemeGTK::GetGtkWidgetAndState(StyleAppearance aAppearance, aAppearance == StyleAppearance::Toolbarbutton || aAppearance == StyleAppearance::Dualbutton || aAppearance == StyleAppearance::ToolbarbuttonDropdown || + aAppearance == StyleAppearance::MozWindowButtonMinimize || + aAppearance == StyleAppearance::MozWindowButtonRestore || + aAppearance == StyleAppearance::MozWindowButtonMaximize || + aAppearance == StyleAppearance::MozWindowButtonClose || aAppearance == StyleAppearance::Menulist || aAppearance == StyleAppearance::MenulistButton) { aState->active &= aState->inHover; @@ -392,9 +396,6 @@ bool nsNativeThemeGTK::GetGtkWidgetAndState(StyleAppearance aAppearance, case StyleAppearance::MozWindowTitlebarMaximized: aGtkWidgetType = MOZ_GTK_HEADER_BAR_MAXIMIZED; break; - case StyleAppearance::MozWindowButtonBox: - aGtkWidgetType = MOZ_GTK_HEADER_BAR_BUTTON_BOX; - break; case StyleAppearance::MozWindowButtonClose: aGtkWidgetType = MOZ_GTK_HEADER_BAR_BUTTON_CLOSE; break; @@ -676,16 +677,6 @@ CSSIntMargin nsNativeThemeGTK::GetExtraSizeForWidget( return extra; } -bool nsNativeThemeGTK::IsWidgetVisible(StyleAppearance aAppearance) { - switch (aAppearance) { - case StyleAppearance::MozWindowButtonBox: - return false; - default: - break; - } - return true; -} - NS_IMETHODIMP nsNativeThemeGTK::DrawWidgetBackground(gfxContext* aContext, nsIFrame* aFrame, StyleAppearance aAppearance, @@ -702,8 +693,7 @@ nsNativeThemeGTK::DrawWidgetBackground(gfxContext* aContext, nsIFrame* aFrame, GtkTextDirection direction = GetTextDirection(aFrame); gint flags; - if (!IsWidgetVisible(aAppearance) || - !GetGtkWidgetAndState(aAppearance, aFrame, gtkWidgetType, &state, + if (!GetGtkWidgetAndState(aAppearance, aFrame, gtkWidgetType, &state, &flags)) { return NS_OK; } @@ -937,7 +927,6 @@ bool nsNativeThemeGTK::GetWidgetPadding(nsDeviceContext* aContext, switch (aAppearance) { case StyleAppearance::Toolbarbutton: case StyleAppearance::Tooltip: - case StyleAppearance::MozWindowButtonBox: case StyleAppearance::MozWindowButtonClose: case StyleAppearance::MozWindowButtonMinimize: case StyleAppearance::MozWindowButtonMaximize: @@ -1072,23 +1061,23 @@ LayoutDeviceIntSize nsNativeThemeGTK::GetMinimumWidgetSize( case StyleAppearance::MozWindowButtonClose: { const ToolbarButtonGTKMetrics* metrics = GetToolbarButtonMetrics(MOZ_GTK_HEADER_BAR_BUTTON_CLOSE); - result.width = metrics->minSizeWithBorderMargin.width; - result.height = metrics->minSizeWithBorderMargin.height; + result.width = metrics->minSizeWithBorder.width; + result.height = metrics->minSizeWithBorder.height; break; } case StyleAppearance::MozWindowButtonMinimize: { const ToolbarButtonGTKMetrics* metrics = GetToolbarButtonMetrics(MOZ_GTK_HEADER_BAR_BUTTON_MINIMIZE); - result.width = metrics->minSizeWithBorderMargin.width; - result.height = metrics->minSizeWithBorderMargin.height; + result.width = metrics->minSizeWithBorder.width; + result.height = metrics->minSizeWithBorder.height; break; } case StyleAppearance::MozWindowButtonMaximize: case StyleAppearance::MozWindowButtonRestore: { const ToolbarButtonGTKMetrics* metrics = GetToolbarButtonMetrics(MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE); - result.width = metrics->minSizeWithBorderMargin.width; - result.height = metrics->minSizeWithBorderMargin.height; + result.width = metrics->minSizeWithBorder.width; + result.height = metrics->minSizeWithBorder.height; break; } case StyleAppearance::Button: @@ -1288,7 +1277,6 @@ nsNativeThemeGTK::ThemeSupportsWidget(nsPresContext* aPresContext, case StyleAppearance::Range: case StyleAppearance::RangeThumb: case StyleAppearance::Splitter: - case StyleAppearance::MozWindowButtonBox: case StyleAppearance::MozWindowButtonClose: case StyleAppearance::MozWindowButtonMinimize: case StyleAppearance::MozWindowButtonMaximize: diff --git a/widget/gtk/nsNativeThemeGTK.h b/widget/gtk/nsNativeThemeGTK.h index 2d0878290e..62a046c959 100644 --- a/widget/gtk/nsNativeThemeGTK.h +++ b/widget/gtk/nsNativeThemeGTK.h @@ -93,7 +93,6 @@ class nsNativeThemeGTK final : public mozilla::widget::Theme { WidgetNodeType& aGtkWidgetType, GtkWidgetState* aState, gint* aWidgetFlags); mozilla::CSSIntMargin GetExtraSizeForWidget(nsIFrame*, StyleAppearance); - bool IsWidgetVisible(StyleAppearance aAppearance); void RefreshWidgetWindow(nsIFrame* aFrame); WidgetNodeType NativeThemeToGtkTheme(StyleAppearance aAppearance, diff --git a/widget/gtk/nsWaylandDisplay.cpp b/widget/gtk/nsWaylandDisplay.cpp index 2a1021457a..1ea87b3bc5 100644 --- a/widget/gtk/nsWaylandDisplay.cpp +++ b/widget/gtk/nsWaylandDisplay.cpp @@ -93,6 +93,11 @@ void nsWaylandDisplay::SetXdgActivation(xdg_activation_v1* aXdgActivation) { mXdgActivation = aXdgActivation; } +void nsWaylandDisplay::SetXdgDbusAnnotationManager( + xdg_dbus_annotation_manager_v1* aXdgDbusAnnotationManager) { + mXdgDbusAnnotationManager = aXdgDbusAnnotationManager; +} + static void global_registry_handler(void* data, wl_registry* registry, uint32_t id, const char* interface, uint32_t version) { @@ -140,6 +145,11 @@ static void global_registry_handler(void* data, wl_registry* registry, auto* activation = WaylandRegistryBind<xdg_activation_v1>( registry, id, &xdg_activation_v1_interface, 1); display->SetXdgActivation(activation); + } else if (iface.EqualsLiteral("xdg_dbus_annotation_manager_v1")) { + auto* annotationManager = + WaylandRegistryBind<xdg_dbus_annotation_manager_v1>( + registry, id, &xdg_dbus_annotation_manager_v1_interface, 1); + display->SetXdgDbusAnnotationManager(annotationManager); } else if (iface.EqualsLiteral("wl_seat")) { // Install keyboard handlers for main thread only auto* seat = diff --git a/widget/gtk/nsWaylandDisplay.h b/widget/gtk/nsWaylandDisplay.h index cd8124d97f..40250c2bf2 100644 --- a/widget/gtk/nsWaylandDisplay.h +++ b/widget/gtk/nsWaylandDisplay.h @@ -19,6 +19,7 @@ #include "mozilla/widget/linux-dmabuf-unstable-v1-client-protocol.h" #include "mozilla/widget/viewporter-client-protocol.h" #include "mozilla/widget/xdg-activation-v1-client-protocol.h" +#include "mozilla/widget/xdg-dbus-annotation-v1-client-protocol.h" #include "mozilla/widget/xdg-output-unstable-v1-client-protocol.h" namespace mozilla::widget { @@ -48,6 +49,9 @@ class nsWaylandDisplay { } zwp_linux_dmabuf_v1* GetDmabuf() { return mDmabuf; }; xdg_activation_v1* GetXdgActivation() { return mXdgActivation; }; + xdg_dbus_annotation_manager_v1* GetXdgDbusAnnotationManager() { + return mXdgDbusAnnotationManager; + } wp_fractional_scale_manager_v1* GetFractionalScaleManager() { return mFractionalScaleManager; } @@ -64,6 +68,8 @@ class nsWaylandDisplay { void SetPointerConstraints(zwp_pointer_constraints_v1* aPointerConstraints); void SetDmabuf(zwp_linux_dmabuf_v1* aDmabuf); void SetXdgActivation(xdg_activation_v1* aXdgActivation); + void SetXdgDbusAnnotationManager( + xdg_dbus_annotation_manager_v1* aXdgDbusAnnotationManager); void SetFractionalScaleManager(wp_fractional_scale_manager_v1* aManager) { mFractionalScaleManager = aManager; } @@ -84,6 +90,7 @@ class nsWaylandDisplay { wp_viewporter* mViewporter = nullptr; zwp_linux_dmabuf_v1* mDmabuf = nullptr; xdg_activation_v1* mXdgActivation = nullptr; + xdg_dbus_annotation_manager_v1* mXdgDbusAnnotationManager = nullptr; wp_fractional_scale_manager_v1* mFractionalScaleManager = nullptr; bool mExplicitSync = false; bool mIsPrimarySelectionEnabled = false; diff --git a/widget/gtk/nsWindow.cpp b/widget/gtk/nsWindow.cpp index 8185c7bda9..e84044990c 100644 --- a/widget/gtk/nsWindow.cpp +++ b/widget/gtk/nsWindow.cpp @@ -100,6 +100,7 @@ #include "ScreenHelperGTK.h" #include "SystemTimeConverter.h" #include "WidgetUtilsGtk.h" +#include "NativeMenuGtk.h" #ifdef ACCESSIBILITY # include "mozilla/a11y/LocalAccessible.h" @@ -182,8 +183,6 @@ static nsWindow* get_window_for_gtk_widget(GtkWidget* widget); static nsWindow* get_window_for_gdk_window(GdkWindow* window); static GtkWidget* get_gtk_widget_for_gdk_window(GdkWindow* window); static GdkCursor* get_gtk_cursor(nsCursor aCursor); -static GdkWindow* get_inner_gdk_window(GdkWindow* aWindow, gint x, gint y, - gint* retx, gint* rety); /* callbacks from widgets */ static gboolean expose_event_cb(GtkWidget* widget, cairo_t* cr); @@ -404,6 +403,7 @@ nsWindow::nsWindow() mIsDragPopup(false), mCompositedScreen(gdk_screen_is_composited(gdk_screen_get_default())), mIsAccelerated(false), + mIsAlert(false), mWindowShouldStartDragging(false), mHasMappedToplevel(false), mRetryPointerGrab(false), @@ -572,21 +572,6 @@ bool nsWindow::AreBoundsSane() { return !mLastSizeRequest.IsEmpty(); } -// Walk the list of child windows and call destroy on them. -void nsWindow::DestroyChildWindows() { - LOG("nsWindow::DestroyChildWindows()"); - if (!mGdkWindow) { - return; - } - while (GList* children = gdk_window_peek_children(mGdkWindow)) { - GdkWindow* child = GDK_WINDOW(children->data); - nsWindow* kid = get_window_for_gdk_window(child); - if (kid) { - kid->Destroy(); - } - } -} - void nsWindow::Destroy() { MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread()); @@ -4141,6 +4126,16 @@ void nsWindow::OnUnmap() { mSourceDragContext = nullptr; } } + + // We don't have valid XWindow any more, + // so clear stored ones at GtkCompositorWidget() for OMTC rendering + // and mSurfaceProvider for legacy rendering. + if (GdkIsX11Display()) { + mSurfaceProvider.CleanupResources(); + if (mCompositorWidgetDelegate) { + mCompositorWidgetDelegate->DisableRendering(); + } + } } void nsWindow::OnSizeAllocate(GtkAllocation* aAllocation) { @@ -5394,6 +5389,7 @@ void nsWindow::OnDPIChanged() { } mWidgetListener->UIResolutionChanged(); } + NotifyAPZOfDPIChange(); } void nsWindow::OnCheckResize() { mPendingConfigures++; } @@ -5427,6 +5423,8 @@ void nsWindow::OnScaleChanged(bool aNotify) { return; } + NotifyAPZOfDPIChange(); + LOG("OnScaleChanged %d, %f -> %d, %f\n", int(mCeiledScaleFactor), mFractionalScaleFactor, newCeiled, newFractional); @@ -5816,9 +5814,6 @@ void nsWindow::EnsureGdkWindow() { if (!mGdkWindow) { mGdkWindow = gtk_widget_get_window(GTK_WIDGET(mContainer)); g_object_set_data(G_OBJECT(mGdkWindow), "nsWindow", this); - if (mIMContext) { - mIMContext->SetGdkWindow(mGdkWindow); - } } } @@ -5871,13 +5866,13 @@ void nsWindow::ConfigureGdkWindow() { EnsureGdkWindow(); OnScaleChanged(/* aNotify = */ false); + if (mIsAlert) { + gdk_window_set_override_redirect(mGdkWindow, TRUE); + } + #ifdef MOZ_X11 if (GdkIsX11Display()) { - GdkVisual* gdkVisual = gdk_window_get_visual(mGdkWindow); - Visual* visual = gdk_x11_visual_get_xvisual(gdkVisual); - int depth = gdk_visual_get_depth(gdkVisual); - mSurfaceProvider.Initialize(GetX11Window(), visual, depth, - GetShapedState()); + mSurfaceProvider.Initialize(GetX11Window(), GetShapedState()); // Set window manager hint to keep fullscreen windows composited. // @@ -6019,6 +6014,7 @@ nsresult nsWindow::Create(nsIWidget* aParent, nsNativeWidget aNativeParent, // and can be changed so we use WaylandPopupIsPermanent() to get // recent popup config (Bug 1728952). mNoAutoHide = aInitData && aInitData->mNoAutoHide; + mIsAlert = aInitData && aInitData->mIsAlert; // Popups that are not noautohide are only temporary. The are used // for menus and the like and disappear when another window is used. @@ -6108,10 +6104,11 @@ nsresult nsWindow::Create(nsIWidget* aParent, nsNativeWidget aNativeParent, if (mIsPIPWindow) { LOG(" Is PIP window\n"); gtk_window_set_type_hint(GTK_WINDOW(mShell), GDK_WINDOW_TYPE_HINT_UTILITY); - } else if (aInitData && aInitData->mIsAlert) { + } else if (mIsAlert) { LOG(" Is alert window\n"); gtk_window_set_type_hint(GTK_WINDOW(mShell), GDK_WINDOW_TYPE_HINT_NOTIFICATION); + gtk_window_set_skip_taskbar_hint(GTK_WINDOW(mShell), TRUE); } else if (mWindowType == WindowType::Dialog) { mGtkWindowRoleName = "Dialog"; @@ -6706,10 +6703,6 @@ void nsWindow::ResumeCompositorImpl() { LOG("nsWindow::ResumeCompositorImpl()\n"); MOZ_DIAGNOSTIC_ASSERT(mCompositorWidgetDelegate); - - // DisableRendering() clears stored X11Window so we're sure EnableRendering() - // really updates it. - mCompositorWidgetDelegate->DisableRendering(); mCompositorWidgetDelegate->EnableRendering(GetX11Window(), GetShapedState()); // As WaylandStartVsync needs mCompositorWidgetDelegate this is the right @@ -6988,6 +6981,13 @@ void nsWindow::UpdateWindowDraggingRegion( } } +#ifdef MOZ_ENABLE_DBUS +void nsWindow::SetDBusMenuBar( + RefPtr<mozilla::widget::DBusMenuBar> aDbusMenuBar) { + mDBusMenuBar = std::move(aDbusMenuBar); +} +#endif + LayoutDeviceIntCoord nsWindow::GetTitlebarRadius() { MOZ_RELEASE_ASSERT(NS_IsMainThread()); int32_t cssCoord = LookAndFeel::GetInt(LookAndFeel::IntID::TitlebarRadius); @@ -7839,14 +7839,246 @@ static GtkWidget* get_gtk_widget_for_gdk_window(GdkWindow* window) { return GTK_WIDGET(user_data); } -static GdkCursor* get_gtk_cursor(nsCursor aCursor) { +static GdkCursor* get_gtk_cursor_from_type(uint8_t aCursorType) { + GdkDisplay* defaultDisplay = gdk_display_get_default(); GdkCursor* gdkcursor = nullptr; - uint8_t newType = 0xff; - if ((gdkcursor = gCursorCache[aCursor])) { - return gdkcursor; + // GtkCursors are defined at nsGtkCursors.h + if (aCursorType > MOZ_CURSOR_NONE) { + return nullptr; } + // If by now we don't have a xcursor, this means we have to make a custom + // one. First, we try creating a named cursor based on the hash of our + // custom bitmap, as libXcursor has some magic to convert bitmapped cursors + // to themed cursors + if (GtkCursors[aCursorType].hash) { + gdkcursor = + gdk_cursor_new_from_name(defaultDisplay, GtkCursors[aCursorType].hash); + if (gdkcursor) { + return gdkcursor; + } + } + + LOGW("get_gtk_cursor_from_type(): Failed to get cursor type %d, try bitmap", + aCursorType); + + // If we still don't have a xcursor, we now really create a bitmap cursor + GdkPixbuf* cursor_pixbuf = + gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, 32, 32); + if (!cursor_pixbuf) { + return nullptr; + } + + guchar* data = gdk_pixbuf_get_pixels(cursor_pixbuf); + + // Read data from GtkCursors and compose RGBA surface from 1bit bitmap and + // mask GtkCursors bits and mask are 32x32 monochrome bitmaps (1 bit for + // each pixel) so it's 128 byte array (4 bytes for are one bitmap row and + // there are 32 rows here). + const unsigned char* bits = GtkCursors[aCursorType].bits; + const unsigned char* mask_bits = GtkCursors[aCursorType].mask_bits; + + for (int i = 0; i < 128; i++) { + char bit = (char)*bits++; + char mask = (char)*mask_bits++; + for (int j = 0; j < 8; j++) { + unsigned char pix = ~(((bit >> j) & 0x01) * 0xff); + *data++ = pix; + *data++ = pix; + *data++ = pix; + *data++ = (((mask >> j) & 0x01) * 0xff); + } + } + + gdkcursor = gdk_cursor_new_from_pixbuf( + gdk_display_get_default(), cursor_pixbuf, GtkCursors[aCursorType].hot_x, + GtkCursors[aCursorType].hot_y); + + g_object_unref(cursor_pixbuf); + return gdkcursor; +} + +static GdkCursor* get_gtk_cursor_legacy(nsCursor aCursor) { + GdkCursor* gdkcursor = nullptr; + Maybe<uint8_t> fallbackType; + + GdkDisplay* defaultDisplay = gdk_display_get_default(); + + // The strategy here is to use standard GDK cursors, and, if not available, + // load by standard name with gdk_cursor_new_from_name. + // Spec is here: http://www.freedesktop.org/wiki/Specifications/cursor-spec/ + switch (aCursor) { + case eCursor_standard: + gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_LEFT_PTR); + break; + case eCursor_wait: + gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_WATCH); + break; + case eCursor_select: + gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_XTERM); + break; + case eCursor_hyperlink: + gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_HAND2); + break; + case eCursor_n_resize: + gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_TOP_SIDE); + break; + case eCursor_s_resize: + gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_BOTTOM_SIDE); + break; + case eCursor_w_resize: + gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_LEFT_SIDE); + break; + case eCursor_e_resize: + gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_RIGHT_SIDE); + break; + case eCursor_nw_resize: + gdkcursor = + gdk_cursor_new_for_display(defaultDisplay, GDK_TOP_LEFT_CORNER); + break; + case eCursor_se_resize: + gdkcursor = + gdk_cursor_new_for_display(defaultDisplay, GDK_BOTTOM_RIGHT_CORNER); + break; + case eCursor_ne_resize: + gdkcursor = + gdk_cursor_new_for_display(defaultDisplay, GDK_TOP_RIGHT_CORNER); + break; + case eCursor_sw_resize: + gdkcursor = + gdk_cursor_new_for_display(defaultDisplay, GDK_BOTTOM_LEFT_CORNER); + break; + case eCursor_crosshair: + gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_CROSSHAIR); + break; + case eCursor_move: + gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_FLEUR); + break; + case eCursor_help: + gdkcursor = + gdk_cursor_new_for_display(defaultDisplay, GDK_QUESTION_ARROW); + break; + case eCursor_copy: // CSS3 + gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "copy"); + if (!gdkcursor) fallbackType.emplace(MOZ_CURSOR_COPY); + break; + case eCursor_alias: + gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "alias"); + if (!gdkcursor) fallbackType.emplace(MOZ_CURSOR_ALIAS); + break; + case eCursor_context_menu: + gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "context-menu"); + if (!gdkcursor) fallbackType.emplace(MOZ_CURSOR_CONTEXT_MENU); + break; + case eCursor_cell: + gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_PLUS); + break; + // Those two aren’t standardized. Trying both KDE’s and GNOME’s names + case eCursor_grab: + gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "openhand"); + if (!gdkcursor) fallbackType.emplace(MOZ_CURSOR_HAND_GRAB); + break; + case eCursor_grabbing: + gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "closedhand"); + if (!gdkcursor) { + gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "grabbing"); + } + if (!gdkcursor) fallbackType.emplace(MOZ_CURSOR_HAND_GRABBING); + break; + case eCursor_spinning: + gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "progress"); + if (!gdkcursor) fallbackType.emplace(MOZ_CURSOR_SPINNING); + break; + case eCursor_zoom_in: + gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "zoom-in"); + if (!gdkcursor) fallbackType.emplace(MOZ_CURSOR_ZOOM_IN); + break; + case eCursor_zoom_out: + gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "zoom-out"); + if (!gdkcursor) fallbackType.emplace(MOZ_CURSOR_ZOOM_OUT); + break; + case eCursor_not_allowed: + gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "not-allowed"); + if (!gdkcursor) { // nonstandard, yet common + gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "crossed_circle"); + } + if (!gdkcursor) fallbackType.emplace(MOZ_CURSOR_NOT_ALLOWED); + break; + case eCursor_no_drop: + gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "no-drop"); + if (!gdkcursor) { // this nonstandard sequence makes it work on KDE and + // GNOME + gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "forbidden"); + } + if (!gdkcursor) { + gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "circle"); + } + if (!gdkcursor) fallbackType.emplace(MOZ_CURSOR_NOT_ALLOWED); + break; + case eCursor_vertical_text: + gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "vertical-text"); + if (!gdkcursor) { + fallbackType.emplace(MOZ_CURSOR_VERTICAL_TEXT); + } + break; + case eCursor_all_scroll: + gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_FLEUR); + break; + case eCursor_nesw_resize: + gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "size_bdiag"); + if (!gdkcursor) fallbackType.emplace(MOZ_CURSOR_NESW_RESIZE); + break; + case eCursor_nwse_resize: + gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "size_fdiag"); + if (!gdkcursor) fallbackType.emplace(MOZ_CURSOR_NWSE_RESIZE); + break; + case eCursor_ns_resize: + gdkcursor = + gdk_cursor_new_for_display(defaultDisplay, GDK_SB_V_DOUBLE_ARROW); + break; + case eCursor_ew_resize: + gdkcursor = + gdk_cursor_new_for_display(defaultDisplay, GDK_SB_H_DOUBLE_ARROW); + break; + // Here, two better fitting cursors exist in some cursor themes. Try those + // first + case eCursor_row_resize: + gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "split_v"); + if (!gdkcursor) { + gdkcursor = + gdk_cursor_new_for_display(defaultDisplay, GDK_SB_V_DOUBLE_ARROW); + } + break; + case eCursor_col_resize: + gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "split_h"); + if (!gdkcursor) { + gdkcursor = + gdk_cursor_new_for_display(defaultDisplay, GDK_SB_H_DOUBLE_ARROW); + } + break; + case eCursor_none: + fallbackType.emplace(MOZ_CURSOR_NONE); + break; + default: + NS_ASSERTION(aCursor, "Invalid cursor type"); + gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_LEFT_PTR); + break; + } + + if (!gdkcursor && fallbackType.isSome()) { + LOGW("get_gtk_cursor_legacy(): Failed to get cursor %d, try fallback", + aCursor); + gdkcursor = get_gtk_cursor_from_type(*fallbackType); + } + + return gdkcursor; +} + +static GdkCursor* get_gtk_cursor_from_name(nsCursor aCursor) { + GdkCursor* gdkcursor = nullptr; + Maybe<uint8_t> fallbackType; + GdkDisplay* defaultDisplay = gdk_display_get_default(); switch (aCursor) { @@ -7897,42 +8129,42 @@ static GdkCursor* get_gtk_cursor(nsCursor aCursor) { break; case eCursor_copy: gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "copy"); - if (!gdkcursor) newType = MOZ_CURSOR_COPY; + if (!gdkcursor) fallbackType.emplace(MOZ_CURSOR_COPY); break; case eCursor_alias: gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "alias"); - if (!gdkcursor) newType = MOZ_CURSOR_ALIAS; + if (!gdkcursor) fallbackType.emplace(MOZ_CURSOR_ALIAS); break; case eCursor_context_menu: gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "context-menu"); - if (!gdkcursor) newType = MOZ_CURSOR_CONTEXT_MENU; + if (!gdkcursor) fallbackType.emplace(MOZ_CURSOR_CONTEXT_MENU); break; case eCursor_cell: gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "cell"); break; case eCursor_grab: gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "grab"); - if (!gdkcursor) newType = MOZ_CURSOR_HAND_GRAB; + if (!gdkcursor) fallbackType.emplace(MOZ_CURSOR_HAND_GRAB); break; case eCursor_grabbing: gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "grabbing"); - if (!gdkcursor) newType = MOZ_CURSOR_HAND_GRABBING; + if (!gdkcursor) fallbackType.emplace(MOZ_CURSOR_HAND_GRABBING); break; case eCursor_spinning: gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "progress"); - if (!gdkcursor) newType = MOZ_CURSOR_SPINNING; + if (!gdkcursor) fallbackType.emplace(MOZ_CURSOR_SPINNING); break; case eCursor_zoom_in: gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "zoom-in"); - if (!gdkcursor) newType = MOZ_CURSOR_ZOOM_IN; + if (!gdkcursor) fallbackType.emplace(MOZ_CURSOR_ZOOM_IN); break; case eCursor_zoom_out: gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "zoom-out"); - if (!gdkcursor) newType = MOZ_CURSOR_ZOOM_OUT; + if (!gdkcursor) fallbackType.emplace(MOZ_CURSOR_ZOOM_OUT); break; case eCursor_not_allowed: gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "not-allowed"); - if (!gdkcursor) newType = MOZ_CURSOR_NOT_ALLOWED; + if (!gdkcursor) fallbackType.emplace(MOZ_CURSOR_NOT_ALLOWED); break; case eCursor_no_drop: gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "no-drop"); @@ -7943,12 +8175,12 @@ static GdkCursor* get_gtk_cursor(nsCursor aCursor) { if (!gdkcursor) { gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "circle"); } - if (!gdkcursor) newType = MOZ_CURSOR_NOT_ALLOWED; + if (!gdkcursor) fallbackType.emplace(MOZ_CURSOR_NOT_ALLOWED); break; case eCursor_vertical_text: gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "vertical-text"); if (!gdkcursor) { - newType = MOZ_CURSOR_VERTICAL_TEXT; + fallbackType.emplace(MOZ_CURSOR_VERTICAL_TEXT); } break; case eCursor_all_scroll: @@ -7956,11 +8188,11 @@ static GdkCursor* get_gtk_cursor(nsCursor aCursor) { break; case eCursor_nesw_resize: gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "nesw-resize"); - if (!gdkcursor) newType = MOZ_CURSOR_NESW_RESIZE; + if (!gdkcursor) fallbackType.emplace(MOZ_CURSOR_NESW_RESIZE); break; case eCursor_nwse_resize: gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "nwse-resize"); - if (!gdkcursor) newType = MOZ_CURSOR_NWSE_RESIZE; + if (!gdkcursor) fallbackType.emplace(MOZ_CURSOR_NWSE_RESIZE); break; case eCursor_ns_resize: gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "ns-resize"); @@ -7976,7 +8208,7 @@ static GdkCursor* get_gtk_cursor(nsCursor aCursor) { break; case eCursor_none: gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "none"); - if (!gdkcursor) newType = MOZ_CURSOR_NONE; + if (!gdkcursor) fallbackType.emplace(MOZ_CURSOR_NONE); break; default: NS_ASSERTION(aCursor, "Invalid cursor type"); @@ -7984,51 +8216,26 @@ static GdkCursor* get_gtk_cursor(nsCursor aCursor) { break; } - // If by now we don't have a xcursor, this means we have to make a custom - // one. First, we try creating a named cursor based on the hash of our - // custom bitmap, as libXcursor has some magic to convert bitmapped cursors - // to themed cursors - if (newType != 0xFF && GtkCursors[newType].hash) { - gdkcursor = - gdk_cursor_new_from_name(defaultDisplay, GtkCursors[newType].hash); + if (!gdkcursor && fallbackType.isSome()) { + LOGW("get_gtk_cursor_from_name(): Failed to get cursor %d, try fallback", + aCursor); + gdkcursor = get_gtk_cursor_from_type(*fallbackType); } - // If we still don't have a xcursor, we now really create a bitmap cursor - if (newType != 0xff && !gdkcursor) { - GdkPixbuf* cursor_pixbuf = - gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, 32, 32); - if (!cursor_pixbuf) { - return nullptr; - } - - guchar* data = gdk_pixbuf_get_pixels(cursor_pixbuf); - - // Read data from GtkCursors and compose RGBA surface from 1bit bitmap and - // mask GtkCursors bits and mask are 32x32 monochrome bitmaps (1 bit for - // each pixel) so it's 128 byte array (4 bytes for are one bitmap row and - // there are 32 rows here). - const unsigned char* bits = GtkCursors[newType].bits; - const unsigned char* mask_bits = GtkCursors[newType].mask_bits; - - for (int i = 0; i < 128; i++) { - char bit = (char)*bits++; - char mask = (char)*mask_bits++; - for (int j = 0; j < 8; j++) { - unsigned char pix = ~(((bit >> j) & 0x01) * 0xff); - *data++ = pix; - *data++ = pix; - *data++ = pix; - *data++ = (((mask >> j) & 0x01) * 0xff); - } - } + return gdkcursor; +} - gdkcursor = gdk_cursor_new_from_pixbuf( - gdk_display_get_default(), cursor_pixbuf, GtkCursors[newType].hot_x, - GtkCursors[newType].hot_y); +static GdkCursor* get_gtk_cursor(nsCursor aCursor) { + GdkCursor* gdkcursor = nullptr; - g_object_unref(cursor_pixbuf); + if ((gdkcursor = gCursorCache[aCursor])) { + return gdkcursor; } + gdkcursor = StaticPrefs::widget_gtk_legacy_cursors_enabled() + ? get_gtk_cursor_legacy(aCursor) + : get_gtk_cursor_from_name(aCursor); + gCursorCache[aCursor] = gdkcursor; return gdkcursor; @@ -8214,6 +8421,10 @@ static gboolean button_press_event_cb(GtkWidget* widget, GdkEventButton* event) { UpdateLastInputEventTime(event); + if (event->button == 2 && !StaticPrefs::widget_gtk_middle_click_enabled()) { + return FALSE; + } + RefPtr<nsWindow> window = GetFirstNSWindowForGDKWindow(event->window); if (!window) { return FALSE; @@ -8232,6 +8443,10 @@ static gboolean button_release_event_cb(GtkWidget* widget, GdkEventButton* event) { UpdateLastInputEventTime(event); + if (event->button == 2 && !StaticPrefs::widget_gtk_middle_click_enabled()) { + return FALSE; + } + RefPtr<nsWindow> window = GetFirstNSWindowForGDKWindow(event->window); if (!window) { return FALSE; @@ -8579,28 +8794,26 @@ gboolean WindowDragMotionHandler(GtkWidget* aWidget, GdkDragContext* aDragContext, gint aX, gint aY, guint aTime) { RefPtr<nsWindow> window = get_window_for_gtk_widget(aWidget); - if (!window) { + if (!window || !window->GetGdkWindow()) { return FALSE; } - // figure out which internal widget this drag motion actually happened on - nscoord retx = 0; - nscoord rety = 0; - - GdkWindow* innerWindow = get_inner_gdk_window(gtk_widget_get_window(aWidget), - aX, aY, &retx, &rety); - RefPtr<nsWindow> innerMostWindow = get_window_for_gdk_window(innerWindow); - if (!innerMostWindow) { - innerMostWindow = window; + // We're getting aX,aY in mShell coordinates space. + // mContainer is shifted by CSD decorations so translate the coords + // to mContainer space where our content lives. + if (aWidget == window->GetGtkWidget()) { + int x, y; + gdk_window_get_geometry(window->GetGdkWindow(), &x, &y, nullptr, nullptr); + aX -= x; + aY -= y; } - LOGDRAG("WindowDragMotionHandler target nsWindow [%p]", - innerMostWindow.get()); + + LOGDRAG("WindowDragMotionHandler target nsWindow [%p]", window.get()); RefPtr<nsDragService> dragService = nsDragService::GetInstance(); nsDragService::AutoEventLoop loop(dragService); if (!dragService->ScheduleMotionEvent( - innerMostWindow, aDragContext, - GetWindowDropPosition(innerMostWindow, retx, rety), aTime)) { + window, aDragContext, GetWindowDropPosition(window, aX, aY), aTime)) { return FALSE; } return TRUE; @@ -8656,28 +8869,25 @@ static void drag_leave_event_cb(GtkWidget* aWidget, gboolean WindowDragDropHandler(GtkWidget* aWidget, GdkDragContext* aDragContext, gint aX, gint aY, guint aTime) { RefPtr<nsWindow> window = get_window_for_gtk_widget(aWidget); - if (!window) { + if (!window || !window->GetGdkWindow()) { return FALSE; } - // figure out which internal widget this drag motion actually happened on - nscoord retx = 0; - nscoord rety = 0; - - GdkWindow* innerWindow = get_inner_gdk_window(gtk_widget_get_window(aWidget), - aX, aY, &retx, &rety); - RefPtr<nsWindow> innerMostWindow = get_window_for_gdk_window(innerWindow); - - if (!innerMostWindow) { - innerMostWindow = window; + // We're getting aX,aY in mShell coordinates space. + // mContainer is shifted by CSD decorations so translate the coords + // to mContainer space where our content lives. + if (aWidget == window->GetGtkWidget()) { + int x, y; + gdk_window_get_geometry(window->GetGdkWindow(), &x, &y, nullptr, nullptr); + aX -= x; + aY -= y; } - LOGDRAG("WindowDragDropHandler nsWindow [%p]", innerMostWindow.get()); + LOGDRAG("WindowDragDropHandler nsWindow [%p]", window.get()); RefPtr<nsDragService> dragService = nsDragService::GetInstance(); nsDragService::AutoEventLoop loop(dragService); return dragService->ScheduleDropEvent( - innerMostWindow, aDragContext, - GetWindowDropPosition(innerMostWindow, retx, rety), aTime); + window, aDragContext, GetWindowDropPosition(window, aX, aY), aTime); } static gboolean drag_drop_event_cb(GtkWidget* aWidget, @@ -8710,27 +8920,6 @@ static nsresult initialize_prefs(void) { return NS_OK; } -// TODO: Can we simplify it for mShell/mContainer only scenario? -static GdkWindow* get_inner_gdk_window(GdkWindow* aWindow, gint x, gint y, - gint* retx, gint* rety) { - gint cx, cy, cw, ch; - GList* children = gdk_window_peek_children(aWindow); - for (GList* child = g_list_last(children); child; - child = g_list_previous(child)) { - auto* childWindow = (GdkWindow*)child->data; - if (get_window_for_gdk_window(childWindow)) { - gdk_window_get_geometry(childWindow, &cx, &cy, &cw, &ch); - if ((cx < x) && (x < (cx + cw)) && (cy < y) && (y < (cy + ch)) && - gdk_window_is_visible(childWindow)) { - return get_inner_gdk_window(childWindow, x - cx, y - cy, retx, rety); - } - } - } - *retx = x; - *rety = y; - return aWindow; -} - #ifdef ACCESSIBILITY void nsWindow::CreateRootAccessible() { if (!mRootAccessible) { @@ -9548,8 +9737,19 @@ void nsWindow::GetCompositorWidgetInitData( LOG("nsWindow::GetCompositorWidgetInitData"); + Window window = GetX11Window(); +#ifdef MOZ_X11 + // We're bit hackish here. Old GLX backend needs XWindow when GLContext + // is created so get XWindow now before map signal. + // We may see crashes/errors when nsWindow is unmapped (XWindow is + // invalidated) but we can't do anything about it. + if (!window && !gfxVars::UseEGL()) { + window = + gdk_x11_window_get_xid(gtk_widget_get_window(GTK_WIDGET(mContainer))); + } +#endif *aInitData = mozilla::widget::GtkCompositorWidgetInitData( - GetX11Window(), displayName, GetShapedState(), GdkIsX11Display(), + window, displayName, GetShapedState(), GdkIsX11Display(), GetClientSize()); #ifdef MOZ_X11 @@ -9927,38 +10127,42 @@ void nsWindow::DisableRendering() { LOG("nsWindow::DisableRendering()"); if (mGdkWindow) { - if (mIMContext) { - mIMContext->SetGdkWindow(nullptr); - } g_object_set_data(G_OBJECT(mGdkWindow), "nsWindow", nullptr); mGdkWindow = nullptr; } + // Until Bug 1654938 is fixed we delete layer manager for hidden popups, + // otherwise it can easily hold 1GB+ memory for long time. + if (mWindowType == WindowType::Popup) { + DestroyLayerManager(); + mSurfaceProvider.CleanupResources(); + } else { #ifdef MOZ_WAYLAND - // Widget is backed by OpenGL EGLSurface created over wl_surface - // owned by mContainer. - // RenderCompositorEGL::Resume() deletes recent EGLSurface based on - // wl_surface owned by mContainer and creates a new fallback EGLSurface. - // Then we can delete wl_surface in moz_container_wayland_unmap(). - // We don't want to pause compositor as it may lead to whole - // browser freeze (Bug 1777664). - /// - // We don't need to do such operation for SW backend as - // WindowSurfaceWaylandMB::Commit() gets wl_surface from - // MozContainer every commit. - if (moz_container_wayland_has_egl_window(mContainer) && - mCompositorWidgetDelegate) { - if (CompositorBridgeChild* remoteRenderer = GetRemoteRenderer()) { - // Call DisableRendering() to make GtkCompositorWidget hidden. - // Then SendResume() will create fallback EGLSurface, see - // GLContextEGL::CreateEGLSurfaceForCompositorWidget(). - mCompositorWidgetDelegate->DisableRendering(); - remoteRenderer->SendResume(); - mCompositorWidgetDelegate->EnableRendering(GetX11Window(), - GetShapedState()); + // Widget is backed by OpenGL EGLSurface created over wl_surface + // owned by mContainer. + // RenderCompositorEGL::Resume() deletes recent EGLSurface based on + // wl_surface owned by mContainer and creates a new fallback EGLSurface. + // Then we can delete wl_surface in moz_container_wayland_unmap(). + // We don't want to pause compositor as it may lead to whole + // browser freeze (Bug 1777664). + /// + // We don't need to do such operation for SW backend as + // WindowSurfaceWaylandMB::Commit() gets wl_surface from + // MozContainer every commit. + if (moz_container_wayland_has_egl_window(mContainer) && + mCompositorWidgetDelegate) { + if (CompositorBridgeChild* remoteRenderer = GetRemoteRenderer()) { + // Call DisableRendering() to make GtkCompositorWidget hidden. + // Then SendResume() will create fallback EGLSurface, see + // GLContextEGL::CreateEGLSurfaceForCompositorWidget(). + mCompositorWidgetDelegate->DisableRendering(); + remoteRenderer->SendResume(); + mCompositorWidgetDelegate->EnableRendering(GetX11Window(), + GetShapedState()); + } } - } #endif + } } // Apply workaround for Mutter compositor bug (mzbz#1777269). diff --git a/widget/gtk/nsWindow.h b/widget/gtk/nsWindow.h index e235d12c08..f8fe344f09 100644 --- a/widget/gtk/nsWindow.h +++ b/widget/gtk/nsWindow.h @@ -113,6 +113,7 @@ class CurrentX11TimeGetter; #endif namespace widget { +class DBusMenuBar; class Screen; } // namespace widget } // namespace mozilla @@ -373,6 +374,10 @@ class nsWindow final : public nsBaseWidget { void UpdateWindowDraggingRegion( const LayoutDeviceIntRegion& aRegion) override; +#ifdef MOZ_ENABLE_DBUS + void SetDBusMenuBar(RefPtr<mozilla::widget::DBusMenuBar> aDbusMenuBar); +#endif + // HiDPI scale conversion gint GdkCeiledScaleFactor(); double FractionalScaleFactor(); @@ -654,6 +659,7 @@ class nsWindow final : public nsBaseWidget { bool mIsDragPopup : 1; bool mCompositedScreen : 1; bool mIsAccelerated : 1; + bool mIsAlert : 1; bool mWindowShouldStartDragging : 1; bool mHasMappedToplevel : 1; bool mRetryPointerGrab : 1; @@ -904,6 +910,10 @@ class nsWindow final : public nsBaseWidget { RefPtr<nsWindow> mWaylandPopupNext; RefPtr<nsWindow> mWaylandPopupPrev; +#ifdef MOZ_ENABLE_DBUS + RefPtr<mozilla::widget::DBusMenuBar> mDBusMenuBar; +#endif + // When popup is resized by Gtk by move-to-rect callback, // we store final popup size here. Then we use mMoveToRectPopupSize size // in following popup operations unless mLayoutPopupSizeCleared is set. diff --git a/widget/gtk/wayland/moz.build b/widget/gtk/wayland/moz.build index e033187a3b..b9ea6a4f70 100644 --- a/widget/gtk/wayland/moz.build +++ b/widget/gtk/wayland/moz.build @@ -15,6 +15,7 @@ SOURCES += [ "relative-pointer-unstable-v1-protocol.c", "viewporter-protocol.c", "xdg-activation-v1-protocol.c", + "xdg-dbus-annotation-v1-protocol.c", "xdg-output-unstable-v1-protocol.c", ] @@ -26,6 +27,7 @@ EXPORTS.mozilla.widget += [ "relative-pointer-unstable-v1-client-protocol.h", "viewporter-client-protocol.h", "xdg-activation-v1-client-protocol.h", + "xdg-dbus-annotation-v1-client-protocol.h", "xdg-output-unstable-v1-client-protocol.h", ] diff --git a/widget/gtk/wayland/xdg-dbus-annotation-v1-client-protocol.h b/widget/gtk/wayland/xdg-dbus-annotation-v1-client-protocol.h new file mode 100644 index 0000000000..fe5567cf8e --- /dev/null +++ b/widget/gtk/wayland/xdg-dbus-annotation-v1-client-protocol.h @@ -0,0 +1,284 @@ +/* Generated by wayland-scanner 1.19.0 */ + +#ifndef XDG_DBUS_ANNOTATION_V1_CLIENT_PROTOCOL_H +#define XDG_DBUS_ANNOTATION_V1_CLIENT_PROTOCOL_H + +#include <stdint.h> +#include <stddef.h> +#include "wayland-client.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @page page_xdg_dbus_annotation_v1 The xdg_dbus_annotation_v1 protocol + * Wayland protocol for associating DBus objects with toplevels + * + * @section page_desc_xdg_dbus_annotation_v1 Description + * + * This description provides a high-level overview of the interplay between + * the interfaces defined in this protocol. For details, see the protocol + * specification. + * + * The dbus_annotation_manager allows a client to request the creation of an + * annotation object associated with an wl_surface or itself. The annotation + * object allows a client to notify the compositor of a DBus object associated + * with itself. + * + * Clients should request the creation of an dbus_annotation object when they + * create a DBus object associated with an wl_surface or themselves, and should + * release the object when they destroy a DBus object associated with their + * wl_surface or themselves. + * + * Clients should only own at most one dbus_annotation object with a given name + * for each of their wl_surface objects or themselves. A protocol error will be + * raised if a client requests more than one dbus_annotation object for an + * wl_surface or themselves with a given name. + * + * @section page_ifaces_xdg_dbus_annotation_v1 Interfaces + * - @subpage page_iface_xdg_dbus_annotation_manager_v1 - controller object for + * registering dbus objects associated with wl_surfaces or clients + * - @subpage page_iface_xdg_dbus_annotation_v1 - controller object for + * associating dbus objects with an wl_surface + * @section page_copyright_xdg_dbus_annotation_v1 Copyright + * <pre> + * + * Copyright © 2017 David Edmundson + * Copyrihgt © 2023 Janet Blackquill + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * </pre> + */ +struct wl_surface; +struct xdg_dbus_annotation_manager_v1; +struct xdg_dbus_annotation_v1; + +#ifndef XDG_DBUS_ANNOTATION_MANAGER_V1_INTERFACE +# define XDG_DBUS_ANNOTATION_MANAGER_V1_INTERFACE +/** + * @page page_iface_xdg_dbus_annotation_manager_v1 + * xdg_dbus_annotation_manager_v1 + * @section page_iface_xdg_dbus_annotation_manager_v1_desc Description + * + * An object that provides access to the creation of dbus_annotation objects. + * @section page_iface_xdg_dbus_annotation_manager_v1_api API + * See @ref iface_xdg_dbus_annotation_manager_v1. + */ +/** + * @defgroup iface_xdg_dbus_annotation_manager_v1 The + * xdg_dbus_annotation_manager_v1 interface + * + * An object that provides access to the creation of dbus_annotation objects. + */ +extern const struct wl_interface xdg_dbus_annotation_manager_v1_interface; +#endif +#ifndef XDG_DBUS_ANNOTATION_V1_INTERFACE +# define XDG_DBUS_ANNOTATION_V1_INTERFACE +/** + * @page page_iface_xdg_dbus_annotation_v1 xdg_dbus_annotation_v1 + * @section page_iface_xdg_dbus_annotation_v1_desc Description + * + * An object that provides access to clients to notify the compositor of + * associated DBus objects for an wl_surface. + * + * If not applicable, clients should remove this object. + * @section page_iface_xdg_dbus_annotation_v1_api API + * See @ref iface_xdg_dbus_annotation_v1. + */ +/** + * @defgroup iface_xdg_dbus_annotation_v1 The xdg_dbus_annotation_v1 interface + * + * An object that provides access to clients to notify the compositor of + * associated DBus objects for an wl_surface. + * + * If not applicable, clients should remove this object. + */ +extern const struct wl_interface xdg_dbus_annotation_v1_interface; +#endif + +#ifndef XDG_DBUS_ANNOTATION_MANAGER_V1_ERROR_ENUM +# define XDG_DBUS_ANNOTATION_MANAGER_V1_ERROR_ENUM +enum xdg_dbus_annotation_manager_v1_error { + /** + * given wl_surface or client already has a dbus_annotation with the same + * interface + */ + XDG_DBUS_ANNOTATION_MANAGER_V1_ERROR_ALREADY_ANNOTATED = 0, + /** + * given wl_surface is invalid + */ + XDG_DBUS_ANNOTATION_MANAGER_V1_ERROR_BAD_TARGET = 1, +}; +#endif /* XDG_DBUS_ANNOTATION_MANAGER_V1_ERROR_ENUM */ + +#define XDG_DBUS_ANNOTATION_MANAGER_V1_DESTROY 0 +#define XDG_DBUS_ANNOTATION_MANAGER_V1_CREATE_CLIENT 1 +#define XDG_DBUS_ANNOTATION_MANAGER_V1_CREATE_SURFACE 2 + +/** + * @ingroup iface_xdg_dbus_annotation_manager_v1 + */ +#define XDG_DBUS_ANNOTATION_MANAGER_V1_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_dbus_annotation_manager_v1 + */ +#define XDG_DBUS_ANNOTATION_MANAGER_V1_CREATE_CLIENT_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_dbus_annotation_manager_v1 + */ +#define XDG_DBUS_ANNOTATION_MANAGER_V1_CREATE_SURFACE_SINCE_VERSION 1 + +/** @ingroup iface_xdg_dbus_annotation_manager_v1 */ +static inline void xdg_dbus_annotation_manager_v1_set_user_data( + struct xdg_dbus_annotation_manager_v1* xdg_dbus_annotation_manager_v1, + void* user_data) { + wl_proxy_set_user_data((struct wl_proxy*)xdg_dbus_annotation_manager_v1, + user_data); +} + +/** @ingroup iface_xdg_dbus_annotation_manager_v1 */ +static inline void* xdg_dbus_annotation_manager_v1_get_user_data( + struct xdg_dbus_annotation_manager_v1* xdg_dbus_annotation_manager_v1) { + return wl_proxy_get_user_data( + (struct wl_proxy*)xdg_dbus_annotation_manager_v1); +} + +static inline uint32_t xdg_dbus_annotation_manager_v1_get_version( + struct xdg_dbus_annotation_manager_v1* xdg_dbus_annotation_manager_v1) { + return wl_proxy_get_version((struct wl_proxy*)xdg_dbus_annotation_manager_v1); +} + +/** + * @ingroup iface_xdg_dbus_annotation_manager_v1 + * + * Destroy the xdg_dbus_annotation_manager object. xdg_dbus_annotation objects + * created from this object remain valid and should be destroyed separately. + */ +static inline void xdg_dbus_annotation_manager_v1_destroy( + struct xdg_dbus_annotation_manager_v1* xdg_dbus_annotation_manager_v1) { + wl_proxy_marshal((struct wl_proxy*)xdg_dbus_annotation_manager_v1, + XDG_DBUS_ANNOTATION_MANAGER_V1_DESTROY); + + wl_proxy_destroy((struct wl_proxy*)xdg_dbus_annotation_manager_v1); +} + +/** + * @ingroup iface_xdg_dbus_annotation_manager_v1 + * + * The interface other DBus clients can expect the object specified by the + * annotation to implement. + */ +static inline struct xdg_dbus_annotation_v1* +xdg_dbus_annotation_manager_v1_create_client( + struct xdg_dbus_annotation_manager_v1* xdg_dbus_annotation_manager_v1, + const char* interface) { + struct wl_proxy* id; + + id = wl_proxy_marshal_constructor( + (struct wl_proxy*)xdg_dbus_annotation_manager_v1, + XDG_DBUS_ANNOTATION_MANAGER_V1_CREATE_CLIENT, + &xdg_dbus_annotation_v1_interface, interface, NULL); + + return (struct xdg_dbus_annotation_v1*)id; +} + +/** + * @ingroup iface_xdg_dbus_annotation_manager_v1 + * + * The surface to associate the annotation with + */ +static inline struct xdg_dbus_annotation_v1* +xdg_dbus_annotation_manager_v1_create_surface( + struct xdg_dbus_annotation_manager_v1* xdg_dbus_annotation_manager_v1, + const char* interface, struct wl_surface* toplevel) { + struct wl_proxy* id; + + id = wl_proxy_marshal_constructor( + (struct wl_proxy*)xdg_dbus_annotation_manager_v1, + XDG_DBUS_ANNOTATION_MANAGER_V1_CREATE_SURFACE, + &xdg_dbus_annotation_v1_interface, interface, NULL, toplevel); + + return (struct xdg_dbus_annotation_v1*)id; +} + +#define XDG_DBUS_ANNOTATION_V1_DESTROY 0 +#define XDG_DBUS_ANNOTATION_V1_SET_ADDRESS 1 + +/** + * @ingroup iface_xdg_dbus_annotation_v1 + */ +#define XDG_DBUS_ANNOTATION_V1_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_dbus_annotation_v1 + */ +#define XDG_DBUS_ANNOTATION_V1_SET_ADDRESS_SINCE_VERSION 1 + +/** @ingroup iface_xdg_dbus_annotation_v1 */ +static inline void xdg_dbus_annotation_v1_set_user_data( + struct xdg_dbus_annotation_v1* xdg_dbus_annotation_v1, void* user_data) { + wl_proxy_set_user_data((struct wl_proxy*)xdg_dbus_annotation_v1, user_data); +} + +/** @ingroup iface_xdg_dbus_annotation_v1 */ +static inline void* xdg_dbus_annotation_v1_get_user_data( + struct xdg_dbus_annotation_v1* xdg_dbus_annotation_v1) { + return wl_proxy_get_user_data((struct wl_proxy*)xdg_dbus_annotation_v1); +} + +static inline uint32_t xdg_dbus_annotation_v1_get_version( + struct xdg_dbus_annotation_v1* xdg_dbus_annotation_v1) { + return wl_proxy_get_version((struct wl_proxy*)xdg_dbus_annotation_v1); +} + +/** + * @ingroup iface_xdg_dbus_annotation_v1 + */ +static inline void xdg_dbus_annotation_v1_destroy( + struct xdg_dbus_annotation_v1* xdg_dbus_annotation_v1) { + wl_proxy_marshal((struct wl_proxy*)xdg_dbus_annotation_v1, + XDG_DBUS_ANNOTATION_V1_DESTROY); + + wl_proxy_destroy((struct wl_proxy*)xdg_dbus_annotation_v1); +} + +/** + * @ingroup iface_xdg_dbus_annotation_v1 + * + * Set or update the service name and object path corresponding to the + * DBus object. The DBus object should be registered on the session bus + * before sending this request. + * + * Strings should be formatted in Latin-1 matching the relevant DBus + * specifications. + */ +static inline void xdg_dbus_annotation_v1_set_address( + struct xdg_dbus_annotation_v1* xdg_dbus_annotation_v1, + const char* service_name, const char* object_path) { + wl_proxy_marshal((struct wl_proxy*)xdg_dbus_annotation_v1, + XDG_DBUS_ANNOTATION_V1_SET_ADDRESS, service_name, + object_path); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/widget/gtk/wayland/xdg-dbus-annotation-v1-protocol.c b/widget/gtk/wayland/xdg-dbus-annotation-v1-protocol.c new file mode 100644 index 0000000000..af51b3b0e8 --- /dev/null +++ b/widget/gtk/wayland/xdg-dbus-annotation-v1-protocol.c @@ -0,0 +1,75 @@ +/* Generated by wayland-scanner 1.19.0 */ + +/* + * Copyright © 2017 David Edmundson + * Copyrihgt © 2023 Janet Blackquill + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include <stdlib.h> +#include <stdint.h> +#include "wayland-util.h" + +#ifndef __has_attribute +# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */ +#endif + +#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4) +# define WL_PRIVATE __attribute__((visibility("hidden"))) +#else +# define WL_PRIVATE +#endif + +#pragma GCC visibility push(default) +extern const struct wl_interface wl_surface_interface; +#pragma GCC visibility pop +extern const struct wl_interface xdg_dbus_annotation_v1_interface; + +static const struct wl_interface* xdg_dbus_annotation_v1_types[] = { + NULL, + NULL, + NULL, + &xdg_dbus_annotation_v1_interface, + NULL, + &xdg_dbus_annotation_v1_interface, + &wl_surface_interface, +}; + +static const struct wl_message xdg_dbus_annotation_manager_v1_requests[] = { + {"destroy", "", xdg_dbus_annotation_v1_types + 0}, + {"create_client", "sn", xdg_dbus_annotation_v1_types + 2}, + {"create_surface", "sno", xdg_dbus_annotation_v1_types + 4}, +}; + +WL_PRIVATE const struct wl_interface xdg_dbus_annotation_manager_v1_interface = + { + "xdg_dbus_annotation_manager_v1", 1, 3, + xdg_dbus_annotation_manager_v1_requests, 0, NULL, +}; + +static const struct wl_message xdg_dbus_annotation_v1_requests[] = { + {"destroy", "", xdg_dbus_annotation_v1_types + 0}, + {"set_address", "ss", xdg_dbus_annotation_v1_types + 0}, +}; + +WL_PRIVATE const struct wl_interface xdg_dbus_annotation_v1_interface = { + "xdg_dbus_annotation_v1", 1, 2, xdg_dbus_annotation_v1_requests, 0, NULL, +}; |