Skip to content

Commit

Permalink
ui/gtk3: Enable libdbusmenu in Indicator
Browse files Browse the repository at this point in the history
IBus panel now uses libdbusmenu to settle the panel menu coordiantes
in Plasma Wayland desktop session since the Wayland display is required
to use the Wayland input-method protocol.

Now g_bus_get (G_BUS_TYPE_SESSION) is moved to Indicator to follow
the AppIndicator implementation strictly and a new API
ibus_service_class_free_interfaces() is added in libibus.so in case
IBusService constructor does not need GDBusConnection.

Previously GtkMenu absolute cooridiantes are used with Xorg display
and AppIndicator "Activate" and "ContextMenu" D-Bus methods are used
but now the coordiates are transferred to libdbusmenu and "Menu" D-Bus
method is available and the activate menu only is shown by default.

The workaround is to use the middle mouse click to toggle the activate
and context menus at the moment.

BUG=#2408
  • Loading branch information
fujiwarat committed Jul 27, 2023
1 parent ed552e8 commit 8923456
Show file tree
Hide file tree
Showing 8 changed files with 217 additions and 87 deletions.
10 changes: 6 additions & 4 deletions configure.ac
Expand Up @@ -365,10 +365,6 @@ else
AC_SUBST(wayland_scanner_rules)
fi

if test x"$enable_appindicator" = x"yes"; then
enable_appindicator="yes (enabled, use --disable-appindicator to disable)"
fi

# GObject introspection
GOBJECT_INTROSPECTION_CHECK([0.6.8])

Expand Down Expand Up @@ -658,6 +654,12 @@ if test x"$enable_ui" = x"yes"; then
enable_ui="yes (enabled, use --disable-ui to disable)"
fi

if test x"$enable_appindicator" = x"yes"; then
PKG_CHECK_MODULES(DBUSMENU_GLIB, [dbusmenu-glib-0.4])
PKG_CHECK_MODULES(DBUSMENU_GTK3, [dbusmenu-gtk3-0.4])
enable_appindicator="yes (enabled, use --disable-appindicator to disable)"
fi

# --disable-engine
AC_ARG_ENABLE(engine,
AS_HELP_STRING([--disable-engine],
Expand Down
43 changes: 42 additions & 1 deletion src/ibusservice.c
Expand Up @@ -265,7 +265,7 @@ ibus_service_class_init (IBusServiceClass *class)
"The connection of service object",
G_TYPE_DBUS_CONNECTION,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_CONSTRUCT |
G_PARAM_STATIC_NAME |
G_PARAM_STATIC_NICK |
G_PARAM_STATIC_BLURB)
Expand Down Expand Up @@ -304,6 +304,7 @@ ibus_service_set_property (IBusService *service,
service->priv->object_path = g_value_dup_string (value);
break;
case PROP_CONNECTION:
g_return_if_fail (!service->priv->connection);
service->priv->connection = g_value_dup_object (value);
break;
default:
Expand Down Expand Up @@ -661,3 +662,43 @@ ibus_service_class_add_interfaces (IBusServiceClass *class,
return TRUE;
}
}

int
ibus_service_class_free_interfaces (IBusServiceClass *class,
int depth)
{
GDBusInterfaceInfo **interfaces, **p;
int i, positive_depth, total = 0;

g_array_ref (class->interfaces);
p = interfaces = (GDBusInterfaceInfo **)class->interfaces->data;
while (*p != NULL) {
*p++;
total++;
}
if (!total)
return 0;
if (!depth)
return total;
p = interfaces;
positive_depth = (depth > 0) ? depth : -depth;
for (i = 0; i < positive_depth; i++) {
if (i == total) {
g_warning ("The length of GDBusInterfaceInfo is %d but your "
"depth is %d", total, depth);
positive_depth = total;
break;
}
if (depth > 0)
g_dbus_interface_info_unref (*(p + i));
else
g_dbus_interface_info_unref (*(p + total - 1 - i));
}
if (depth > 0) {
g_array_remove_range (class->interfaces, 0, positive_depth);
} else {
g_array_remove_range (class->interfaces,
total - positive_depth, positive_depth);
}
return i;
}
17 changes: 17 additions & 0 deletions src/ibusservice.h
Expand Up @@ -2,6 +2,7 @@
/* vim:set et sts=4: */
/* ibus - The Input Bus
* Copyright (C) 2008-2015 Peng Huang <shawn.p.huang@gmail.com>
* Copyright (C) 2011-2023 Takao Fujiwara <takao.fujiwara1@gmail.com>
* Copyright (C) 2008-2015 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
Expand Down Expand Up @@ -251,6 +252,22 @@ gboolean ibus_service_class_add_interfaces
(IBusServiceClass *klass,
const gchar *xml_data);

/**
* ibus_service_class_free_interfaces:
* @class: An IBusServiceClass.
* @depth: The number of D-Bus introspection interfaces.
*
* Free the first @depth interfaces if @depth is positive. Free the last
* -@depth interfaces if @depth is negative.
*
* Returns: The actual freed number of the introspection interfaces
* if @depth is not 0.
* If @depth is 0, the total number of the introspection interfaces is
* returned but any interfaces are not freed.
*/
int ibus_service_class_free_interfaces
(IBusServiceClass *class,
int depth);

G_END_DECLS
#endif
Expand Down
14 changes: 13 additions & 1 deletion ui/gtk3/Makefile.am
Expand Up @@ -105,7 +105,19 @@ AM_VALAFLAGS += \
endif

if ENABLE_APPINDICATOR
AM_VALAFLAGS += --define=INDICATOR
AM_VALAFLAGS += \
--define=INDICATOR \
--pkg=Dbusmenu-0.4 \
--pkg=DbusmenuGtk3-0.4 \
$(NULL)
AM_CFLAGS += \
@DBUSMENU_GLIB_CFLAGS@ \
@DBUSMENU_GTK3_CFLAGS@ \
$(NULL)
AM_LDADD += \
@DBUSMENU_GLIB_LIBS@ \
@DBUSMENU_GTK3_LIBS@ \
$(NULL)
endif

libexec_PROGRAMS = ibus-ui-gtk3
Expand Down
117 changes: 87 additions & 30 deletions ui/gtk3/indicator.vala
Expand Up @@ -92,44 +92,40 @@ class Indicator : IBus.Service

private GLib.DBusNodeInfo m_watcher_node_info;
private unowned GLib.DBusInterfaceInfo m_watcher_interface_info;
private bool m_registered;
private GLib.DBusProxy m_proxy;
private int m_context_menu_x;
private int m_context_menu_y;
private int m_activate_menu_x;
private int m_activate_menu_y;
private Gdk.Window m_indicator_window;
private Gtk.Menu m_menu;
private Dbusmenu.Server m_dbusmenu_server;


public Indicator(string id,
GLib.DBusConnection connection,
Category category = Category.OTHER) {
string path = DEFAULT_ITEM_PATH + "/" + id;
path = path.delimit("-", '_');

// AppIndicator.set_category() converts enum value to string internally.
// If connection is not assigned in the constructor, register() is
// not called.
GLib.Object(object_path: path,
id: id,
connection: connection,
category_s: category.to_nick());
this.status_s = Status.PASSIVE.to_nick();
this.icon_name = "";
this.icon_name = "ibus";
this.icon_desc = "";
this.title = "";
this.icon_theme_path = "";
this.attention_icon_name = "";
this.attention_icon_desc = "";
this.label_s = "";
this.label_guide_s = "";
unregister(connection);
var n = free_interfaces(0);
free_interfaces(-n);
add_interfaces(_notification_item);
try {
if (!register(connection))
return;
} catch (GLib.Error e) {
warning("Failed to register the application indicator xml: " +
e.message);
return;
}

try {
m_watcher_node_info =
Expand All @@ -141,26 +137,62 @@ class Indicator : IBus.Service
m_watcher_interface_info =
m_watcher_node_info.lookup_interface(
NOTIFICATION_WATCHER_DBUS_IFACE);
check_connect();
GLib.Bus.watch_name (GLib.BusType.SESSION,
NOTIFICATION_WATCHER_DBUS_ADDR,
GLib.BusNameWatcherFlags.NONE,
name_appeared_handler,
name_vanished_handler);
GLib.Bus.get.begin(GLib.BusType.SESSION, null, (obj, res) => {
try {
this.connection = GLib.Bus.get.end(res);
check_connect();
} catch (GLib.IOError e) {
warning("Failed to get the session bus: %s", e.message);
}
});
}


private void name_appeared_handler(GLib.DBusConnection connection,
string name,
string name_owner) {
GLib.DBusProxy.new.begin(
connection,
GLib.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES |
GLib.DBusProxyFlags.DO_NOT_CONNECT_SIGNALS,
m_watcher_interface_info,
NOTIFICATION_WATCHER_DBUS_ADDR,
NOTIFICATION_WATCHER_DBUS_OBJ,
NOTIFICATION_WATCHER_DBUS_IFACE,
null,
(obj, res) => {
bus_watcher_ready(obj, res);
});
}


private void name_vanished_handler(GLib.DBusConnection connection,
string name) {
m_proxy = null;
}


private void check_connect() {
if (m_proxy == null) {
GLib.DBusProxy.new.begin(
connection,
GLib.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES |
GLib.DBusProxyFlags.DO_NOT_CONNECT_SIGNALS,
m_watcher_interface_info,
NOTIFICATION_WATCHER_DBUS_ADDR,
NOTIFICATION_WATCHER_DBUS_OBJ,
NOTIFICATION_WATCHER_DBUS_IFACE,
null,
(obj, res) => {
bus_watcher_ready(obj, res);
});
} else {
bus_watcher_ready(null, null);
if (this.connection == null)
return;
if (m_menu == null)
return;
try {
if (!m_registered) {
if (!register(this.connection))
return;
else
m_registered = true;
}
} catch (GLib.Error e) {
warning("Failed to register the application indicator xml: " +
e.message);
return;
}
}

Expand Down Expand Up @@ -332,7 +364,13 @@ class Indicator : IBus.Service


private GLib.Variant? _get_menu(GLib.DBusConnection connection) {
return null;
if (m_dbusmenu_server != null) {
string o;
m_dbusmenu_server.get(Dbusmenu.SERVER_PROP_DBUS_OBJECT, out o);
var variant = new GLib.Variant("o", o);
return variant;
}
return new GLib.Variant("o", "/");;
}


Expand Down Expand Up @@ -373,6 +411,10 @@ class Indicator : IBus.Service
_context_menu_cb(connection, parameters, invocation);
return;
}
if (method_name == "SecondaryActivate") {
secondary_activate();
return;
}

warning("service_method_call() does not handle the method: " +
method_name);
Expand Down Expand Up @@ -483,7 +525,7 @@ class Indicator : IBus.Service
* this.connection emits the "NewIcon" signal or
* or m_proxy calls the "RegisterStatusNotifierItem" signal.
*/
if (this.connection == null)
if (this.connection == null || !m_registered)
return;
try {
this.connection.emit_signal(null,
Expand Down Expand Up @@ -541,6 +583,20 @@ class Indicator : IBus.Service
}


public void set_menu(Gtk.Menu menu) {
if (this.object_path == null)
return;
m_menu = menu;
if (m_dbusmenu_server == null) {
var path = this.object_path + "/Menu";
m_dbusmenu_server = new Dbusmenu.Server(path);
}
var root = DbusmenuGtk.gtk_parse_menu_structure(menu);
m_dbusmenu_server.set_root(root);
check_connect();
}


public void position_context_menu(Gtk.Menu menu,
out int x,
out int y,
Expand Down Expand Up @@ -585,5 +641,6 @@ class Indicator : IBus.Service
public signal void activate(int x,
int y,
Gdk.Window window);
public signal void secondary_activate();
public signal void registered_status_notifier_item();
}
11 changes: 2 additions & 9 deletions ui/gtk3/notification-item.xml
Expand Up @@ -37,15 +37,8 @@
<method name="XAyatanaSecondaryActivate">
<arg type="u" name="timestamp" direction="in" />
</method>
<!-- Activate is missed in AppIndicator -->
<method name="Activate">
<arg type="i" name="x" direction="in" />
<arg type="i" name="y" direction="in" />
</method>
<method name="ContextMenu">
<arg type="i" name="x" direction="in"/>
<arg type="i" name="y" direction="in"/>
</method>
<!-- Use "Menu" method instead of "Activate" and "ContextMenu"
methods -->

<!-- Signals -->
<signal name="NewIcon">
Expand Down

0 comments on commit 8923456

Please sign in to comment.