jjk-jacky / statusnotifier

Library to use KDE's StatusNotifierItem via GObject
Other
25 stars 8 forks source link

add dbusmenu support via libdbusmenu #8

Closed vovochka404 closed 9 years ago

vovochka404 commented 9 years ago

Adds two new functions:

status_notifier_set_context_menu status_notifier_get_context_menu

new ./configure options: --enable-dbusmenu --with-dbusmenu-gtk-version=[2,3]

the only problem, i don't know how to make this functions optional in documentation.

jjk-jacky commented 9 years ago

On 06/23/15 07:13, Vladimir wrote:

Adds two new functions:

status_notifier_set_context_menu status_notifier_get_context_menu

Look good, thanks. Although to be consistent the context menu should be exposed as a property (e.g. "menu") on the StatusNotifier GObject as well.

And I think it would be good in the example if, when dbusmenu support was enabled, instead of always using it this was based on an option. So running the example would still have it handle ContextMenu() and popup the menu itself, but when ran with e.g. --dbus-menu then it would simply set the menu property and let the Host display it when/as needed. That way it's easy for one to test/see differences between the two.

new ./configure options: --enable-dbusmenu --with-dbusmenu-gtk-version=[2,3]

the only problem, i don't know how to make this functions optional in documentation.

I don't think this is a problem, let's just have them always in there, and simply add a note in each one stating that they will only be available if StatusNotifier was compiled with dbusmenu support.

(Besides, I usually generate the html doc myself and include it inside the tarball, so it's probably best that way.)

Means the functions need to be added in docs/reference/statusnotifier-sections.txt though.

Some other (mostly minor) little things below.

---> configure.ac | 53 +++++++++++++++++++++++++++ example/sn-example.c | 28 +++++++++++--- src/interfaces.h | 3 ++ src/statusnotifier.c | 101 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/statusnotifier.h | 10 +++++ 5 files changed, 190 insertions(+), 5 deletions(-)

diff --git a/configure.ac b/configure.ac index fbcc75c..2afa4f4 100644 --- a/configure.ac +++ b/configure.ac @@ -45,6 +45,14 @@ AC_ARG_ENABLE([warning-flags], AS_HELP_STRING([--enable-warning-flags], [enable extra compiler warning flags]), [warningflags=$enableval], [warningflags=no])

+AC_ARG_ENABLE([dbusmenu],

  • AS_HELP_STRING([--enable-dbusmenu], [enable extra dbusmenu functionality via libdbusmenu]),
  • [dbusmenu=$enableval], [dbusmenu=no]) + +AC_ARG_WITH([dbusmenu-gtk-version],
  • AS_HELP_STRING([--with-dbusmenu-gtk-version], [Select libdbusmenu-gtk version to be built against if dbusmenu support enabled. Possible values: 2,3. Default: 3 ]),
  • [dbusmenugtkversion=$enableval], [dbusmenugtkversion=3]) +

    Checks for libraries.

    PKG_CHECK_MODULES(GOBJECT, [gobject-2.0], , AC_MSG_ERROR([GLib/GObject is required])) PKG_CHECK_MODULES(GIO, [gio-2.0], , AC_MSG_ERROR([GLib/GIO is required])) @@ -137,6 +145,49 @@ else AC_MSG_RESULT([no]) fi

+if test "x$dbusmenu" = "xyes"; then

  • if test "x$dbusmenugtkversion" = "x2"; then
  • AC_MSG_NOTICE([dbusmenu support will be built against dbusmenu-gtk (gtk2)])
  • elif test "x$dbusmenugtkversion" = "x3"; then
  • AC_MSG_NOTICE([dbusmenu support will be built against dbusmenu-gtk3 (gtk3)])
  • else
  • dbusmenugtkversion=3;
  • AC_MSG_NOTICE([dbusmenu support will be built against dbusmenu-gtk3 (gtk3)])
  • fi +
  • if test "x$dbusmenugtkversion" = "x2"; then
  • PKG_CHECK_MODULES(DBUSMENU, [dbusmenu-glib-0.4 dbusmenu-gtk-0.4], [
  • DEP_PACKAGES="${DEP_PACKAGES} dbusmenu-glib-0.4 dbusmenu-gtk-0.4"
  • ], [
  • AC_MSG_WARN([dbusmenu-glib and dbusmenu-gtk are required for dbusmenu support])
  • dbusmenu=no

Should be an error, not a warning. If a dependency if missing, as is the case here, configure should stop & fail.

  • ])
  • elif test "x$dbusmenugtkversion" = "x3"; then
  • PKG_CHECK_MODULES(DBUSMENU, [dbusmenu-glib-0.4 dbusmenu-gtk3-0.4], [
  • DEP_PACKAGES="${DEP_PACKAGES} dbusmenu-glib-0.4 dbusmenu-gtk3-0.4"
  • ], [
  • AC_MSG_WARN([dbusmenu-glib and dbusmenu-gtk3 are required for dbusmenu support])
  • dbusmenu=no

Same here.

  • ])
  • fi +fi + +AC_MSG_CHECKING([for dbusmenu support]) +if test "x$dbusmenu" = "xyes"; then
  • AC_MSG_RESULT([yes])
  • DEP_CFLAGS="$DEP_CFLAGS $DBUSMENU_CFLAGS"
  • DEP_LIBS="$DEP_LIBS $DBUSMENU_LIBS"
  • AC_SUBST(DEP_PACKAGES)
  • AC_SUBST(DEP_CFLAGS)
  • AC_SUBST(DEP_LIBS)
  • AC_DEFINE([USE_DBUSMENU], , [Use dbusmenu])
  • dbusmenu="will be built against GTK ${dbusmenugtkversion}" +else
  • AC_MSG_RESULT([no])
  • dbusmenu=no +fi +AM_CONDITIONAL(USE_DBUSMENU, test "x$dbusmenu" != "xno") + AC_CONFIG_FILES([Makefile statusnotifier.pc example/Makefile docs/reference/Makefile docs/reference/version.xml]) AC_OUTPUT @@ -150,9 +201,11 @@ echo "

    build html documentation : ${enable_gtk_doc} example : ${enable_example}

  • dbusmenu : ${dbusmenu}

    Install paths: binaries : $(eval echo $(eval echo ${bindir}))

  • libraries : $(eval echo $(eval echo ${libdir})) documentation : $(eval echo $(eval echo ${docdir})) man pages : $(eval echo $(eval echo ${mandir})) " diff --git a/example/sn-example.c b/example/sn-example.c index e0c0b97..69ff264 100644 --- a/example/sn-example.c +++ b/example/sn-example.c @@ -20,6 +20,8 @@

+#include "config.h" +

include <gtk/gtk.h>

include

include

@@ -56,14 +58,18 @@ set_status (GObject item, StatusNotifier sn) status_notifier_set_status (sn, GPOINTER_TO_UINT (g_object_get_data (item, "sn-status"))); }

-static gboolean -sn_menu (StatusNotifier sn, gint x, gint y, GMainLoop loop) +GtkMenu * get_menu(StatusNotifier sn, GMainLoop loop)

For coding style consistency, please no space between the * and the function name, but add one between the function name & parenthesis: GtkMenu get_menu (StatusNotifier sn, GMainLoop *loop)

{

  • GtkMenu *menu;
  • static GtkMenu *menu = 0;

menu = NULL

 GtkMenu *submenu;
 GtkWidget *item;
 guint i;
 StatusNotifierStatus status;

+

  • if (menu)
  • {
  • return menu;
  • }

Drop the brackets.

 menu = (GtkMenu *) gtk_menu_new ();
 submenu = (GtkMenu *) gtk_menu_new ();

@@ -112,7 +118,14 @@ sn_menu (StatusNotifier sn, gint x, gint y, GMainLoop loop) ++i;

 g_object_ref_sink (menu);
  • g_signal_connect_swapped (menu, "hide", (GCallback) g_object_unref, menu);
  • return menu; +} + +static gboolean +sn_menu (StatusNotifier sn, gint x, gint y, GMainLoop loop) +{
  • GtkMenu *menu = get_menu(sn, loop);

Again, space before the parenthesis.

Also, it seems you've dropped the signal_connect() to "hide" but I don't think that was intended, it should be back here before gtk_menu_popup().

+ gtk_menu_popup (menu, NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time ()); return TRUE; } @@ -343,8 +356,13 @@ main (gint argc, gchar *argv[]) if (cfg.tooltip_body) status_notifier_set_tooltip_body (sn, cfg.tooltip_body);

  • g_signal_connect (sn, "registration-failed", (GCallback) sn_reg_failed, loop); +#ifdef USE_DBUSMENU
  • status_notifier_set_context_menu(sn, GTK_WIDGET(get_menu(sn, loop)));

As mentioned before, would be nice to make things based on a runtime option here. Also, spaces before parenthesis; And for consistency sake, a simple typecast instead: status_notifier_set_context_menu (sn, (GtkWidget *) get_menu (sn, loop));

Lastly, set_context_menu() does add its own ref, so unless I'm missing something we have a leak here. I wonder if it might not be better to make set_context_menu() call g_object_ref_sink() on the menu instead of g_object_ref(), and remove the g_object_ref_sink() call in get_menu().

That way here we would not need to do anything, which looks like a typical use case; and we'd have to add a g_object_ref_sink() in sn_menu()

+#else g_signal_connect (sn, "context-menu", (GCallback) sn_menu, loop); +#endif +

  • g_signal_connect (sn, "registration-failed", (GCallback) sn_reg_failed, loop); g_signal_connect_swapped (sn, "activate", (GCallback) sn_activate, loop); status_notifier_register (sn); g_main_loop_run (loop); diff --git a/src/interfaces.h b/src/interfaces.h index 507bfa7..d260dc5 100644 --- a/src/interfaces.h +++ b/src/interfaces.h @@ -62,6 +62,9 @@ static const gchar item_xml[] = " <property name='AttentionIconPixmap' type='(iiay)' access='read' />" " <property name='AttentionMovieName' type='s' access='read' />" " " +#ifdef USE_DBUSMENU
  • " " +#endif " " " " " " diff --git a/src/statusnotifier.c b/src/statusnotifier.c index e258ed1..0cff9c8 100644 --- a/src/statusnotifier.c +++ b/src/statusnotifier.c @@ -29,6 +29,12 @@

    include "interfaces.h"

    include "closures.h"

+#ifdef USE_DBUSMENU +#include <libdbusmenu-glib/menuitem.h> +#include <libdbusmenu-glib/server.h> +#include <libdbusmenu-gtk/parser.h> +#endif + /**

  • SECTION:statusnotifier
  • @Short_description: A StatusNotifierItem as per KDE's specifications @@ -146,6 +152,10 @@ struct _StatusNotifierPrivate guint dbus_owner_id; guint dbus_reg_id; GDBusProxy *dbus_proxy; +#ifdef USE_DBUSMENU

    • DbusmenuServer *menuservice;
    • GtkWidget menu; +#endif GDBusConnection dbus_conn; GError dbus_err; }; @@ -797,6 +807,17 @@ dbus_free (StatusNotifier sn) g_object_unref (priv->dbus_proxy); priv->dbus_proxy = NULL; } +#ifdef USE_DBUSMENU
    • if (priv->menu)
    • {
    • g_object_unref(priv->menu);
    • priv->menu = NULL;
    • }
    • if (priv->menuservice) {
    • g_object_unref (priv->menuservice);
    • priv->menuservice = NULL;
    • } +#endif if (priv->dbus_reg_id > 0) { g_dbus_connection_unregister_object (priv->dbus_conn, priv->dbus_reg_id); @@ -1621,6 +1642,21 @@ get_prop (GDBusConnection *conn,

      return variant; } +#ifdef USE_DBUSMENU

    • else if (!g_strcmp0 (property, "Menu"))
    • {
    • if (priv->menuservice != NULL) {
    • GValue strval = { 0 };
    • g_value_init(&strval, G_TYPE_STRING);
    • g_object_get_property (G_OBJECT (priv->menuservice), DBUSMENU_SERVER_PROP_DBUS_OBJECT, &strval);
    • GVariant * var = g_variant_new("o", g_value_get_string(&strval));

Please put all declarations on top of the block.

  • g_value_unset(&strval);
  • return var;
  • } else {
  • return g_variant_new("o", "/NO_DBUSMENU");
  • }
  • } +#endif

    g_return_val_if_reached (NULL); } @@ -1932,3 +1968,68 @@ status_notifier_get_state (StatusNotifier _sn) g_return_val_if_fail (IS_STATUS_NOTIFIER (sn), FALSE); return sn->priv->state; } + +#ifdef USEDBUSMENU +/*

  • * status_notifier_set_context_menu:
  • * @sn: A #StatusNotifier
  • * @menu: A #GtkWidget

A #GtkWidget of the menu to set as context menu

  • *
  • * Exports specified context menu via dbus.
  • * If menu is set, no #StatusNotifier::context_menu signals will be emited

typo: emitted

Also, there should probably be a mention of how things work re: references, as mentioned earlier.

  • *
  • / +void +status_notifier_set_context_menu (StatusNotifier sn,
  • GtkWidget *menu) +{
  • StatusNotifierPrivate *priv;
  • DbusmenuMenuitem *root = NULL; +
  • g_return_if_fail (IS_STATUS_NOTIFIER (sn));
  • g_return_if_fail (GTK_IS_MENU (menu));
  • priv = sn->priv; +
  • if (priv->menu)
  • {
  • g_object_unref(priv->menu);
  • }

Drop the brackets.

+

  • priv->menu = menu;
  • g_object_ref(menu);

Space before parenthesis. (And as mentioned, maybe do a ref_sink() instead?)

+

  • root = dbusmenu_gtk_parse_menu_structure(priv->menu); +
  • if (priv->menuservice == NULL)
  • {
  • priv->menuservice = dbusmenu_server_new ("/MenuBar");
  • }

Same here.

+

  • dbusmenu_server_set_root (priv->menuservice, root); +
  • /* Drop our local ref as set_root should get it's own. */
  • if (root != NULL)
  • {
  • g_object_unref(root);
  • }

Same here.

+} + +/**

  • * status_notifier_set_context_menu:
  • * @sn: A #StatusNotifier
  • *
  • * Returns #GtkWidget of currently setuped menu. If none was setuped
  • null will be returned.

Or simply: Returns the #GtkWidget set as context menu, or %NULL

  • *
  • * Returns: #GtkWidget or null (if menu was not setuped)

Same here, with the "(transfer none)" annotation.

  • / +GtkWidget +status_notifier_get_context_menu (StatusNotifier *sn) +{
  • StatusNotifierPrivate *priv; +
  • g_return_if_fail (IS_STATUS_NOTIFIER (sn));
  • priv = sn->priv; +
  • return priv->menu; +} +#endif diff --git a/src/statusnotifier.h b/src/statusnotifier.h index 3834064..e5ecdbb 100644 --- a/src/statusnotifier.h +++ b/src/statusnotifier.h @@ -27,6 +27,9 @@

    include

    include <gio/gio.h>

    include <gdk-pixbuf/gdk-pixbuf.h>

    +#ifdef USE_DBUSMENU +#include <gtk/gtk.h> +#endif

    G_BEGIN_DECLS

@@ -298,6 +301,13 @@ void status_notifier_register ( StatusNotifier sn); StatusNotifierState status_notifier_get_state ( StatusNotifier sn); +#ifdef USE_DBUSMENU +void status_notifier_set_context_menu (

  • StatusNotifier *sn,
  • GtkWidget menu); +GtkWidget \ status_notifier_get_context_menu (
  •                                      StatusNotifier          *sn);

    +#endif

    G_END_DECLS

vovochka404 commented 9 years ago

Looks like i fixed almost all your remarks :) Sorry for coding style :)

Some comments to your comments:

Also, it seems you've dropped the signal_connect() to "hide" but I don't think that was intended, it should be back here before gtk_menu_popup().

This was made intended due to with my refactoring menu isn't constructed on every call. Only on first one, so there is no reason to destroy it.

About all ref stuff: My code is based on one from libappindicator. I don't know what to do with all this ref stuff, so if you think that something should be changed - please, do it by yourself, cause i'm not quite understand your proposals.

When _setmenu gets pointer - it increases ref count, when pointer is change - decreases for old one. So why there is a leak - i don't understand.

Currently there is no way to remove menu, but i don't think it's a problem...

jjk-jacky commented 9 years ago

On 06/24/15 04:59, Vladimir wrote:

Looks like i fixed almost all your remarks :) Sorry for coding style :)

No worries; Thanks for the changes.

Some comments to your comments:

Also, it seems you've dropped the signal_connect() to "hide" but I don't think that was intended, it should be back here before gtk_menu_popup().

This was made intended due to with my refactoring menu isn't constructed on every call. Only on first one, so there is no reason to destroy it.

Oh right, my bad, missed that.

About all ref stuff: My code is based on one from libappindicator. I don't know what to do with all this ref stuff, so if you think that something should be changed - please, do it by yourself, cause i'm not quite understand your proposals.

Yes, I think it's best for set_context_menu() to do a ref_sink() as that's probably what would be expected for most use case.

Speaking of reference, I think there's some error in the property handling, when getting the property: g_value_take_object (value, status_notifier_get_context_menu (sn));

This is wrong, take_object() means the GValue takes ownership of the object, but it shouldn't. g_value_set_object() should be used here, so the GValue adds its own reference to the object, since get_context_menu() effectively does not add a reference.

Also noticed you forgot to mention on the help/description for {g,s}et_context_menu() that they'll only be available if dbusmenu was enabled at compile time.

When _setmenu gets pointer - it increases ref count, when pointer is change - decreases for old one. So why there is a leak - i don't understand.

Oh, the leak isn't in statusnotifier, but in the example, on the menu we create. get_menu() creates a menu and ref_sink()s it, then gives it to statusnotifier, which adds its own ref to it (as it should). But we (the example) never unref it, so effectively that's a leak. It might not really matter in the example as is, since it doesn't do much else, but ideally it would be handled better. I think the right way to go is change get_menu() into create_menu(), that only creates & returns the menu, w/out ref_sink()ing it. Then either the menu and passed directly via set_context_menu(), which will ref_sink() it, or it's in sn_menu() that we store the static pointer to the menu, and the first time around we call create_menu() and ref_sink() it.

Currently there is no way to remove menu, but i don't think it's a problem...

hmm... Well it should be easy enough to add it though, allowing NULL to be passed to set_context_menu().

I'll try to give this a try, but I don't have a VM with KDE around anymore to test, so it might take me a little while.

Cheers, -j

vovochka404 commented 9 years ago

Looks like i fixed all that was mentioned :)

Thanks for explaining stuff around take_object|set_object :)

jjk-jacky commented 9 years ago

On 06/25/15 03:22, Vladimir wrote:

Looks like i fixed all that was mentioned :)

Almost! :)

In the example, you forgot to drop the ref_sink() call in create_menu(). Also, in sn_menu() it should be a ref_sink() instead of a simple ref().

Few more things in the example:

ifdef USE_DBUSMENU

if (cfg.menu)
    status_notifier_set_context_menu (sn, (GtkWidget *)

create_menu(sn, loop)); else

endif

    g_signal_connect (sn, "context-menu", (GCallback) sn_menu, loop);

In statusnotifier now:

@@ -2005,7 +2007,10 @@ status_notifier_get_state (StatusNotifier *sn)

  • @menu: A #GtkWidget of the menu to set as context menu

This should have "(allow none)" annotation. Might even add ", or %NULL"

*

  • Exports specified context menu via dbus.
    • * If menu is set, no #StatusNotifier::context_menu signals will be emmited
    • * If menu is set, no #StatusNotifier::context_menu signals will be emmited.
    • * You can specify %NULL to remove currently setuped menu.

I'd rephrase that, to also mention the fact that it'll sunk the reference. E.g: " If @menu is set, g_object_ref_sink() will be used to take ownership. Also note that no #StatusNotifier::context_menu signals will be emitted when a context menu is set/shared via DBus. If @menu is %NULL any current menu will be unset (and

StatusNotifier::context_menu signals will be emitted as needed again).

"

  • *
    • Only available if dbusmenu support was enabled in compile-time. / void status_notifier_set_context_menu (StatusNotifier sn, @@ -2015,25 +2020,35 @@ status_notifier_set_context_menu (StatusNotifier sn, DbusmenuMenuitem root = NULL;

    g_return_if_fail (IS_STATUS_NOTIFIER (sn));

  • g_return_if_fail (GTK_IS_MENU (menu));
  • if (menu)
  • g_return_if_fail (GTK_IS_MENU (menu));

I don't think this is right, since those macros aren't intended to be used that way. It should be changed to: g_return_if_fail (!menu || GTK_IS_MENU (menu));

 priv = sn->priv;

 if (priv->menu)
  • g_object_unref(priv->menu);
  •  g_object_unref (priv->menu);

    priv->menu = menu;

  • g_object_ref (menu);
  • root = dbusmenu_gtk_parse_menu_structure(priv->menu);
  • if (menu)
  • {
  •  g_object_ref_sink (priv->menu);
  • if (priv->menuservice == NULL)
  • priv->menuservice = dbusmenu_server_new ("/MenuBar");
  •  root = dbusmenu_gtk_parse_menu_structure (priv->menu);
  • dbusmenu_server_set_root (priv->menuservice, root);
  • if (priv->menuservice == NULL)
  •      priv->menuservice = dbusmenu_server_new ("/MenuBar");
  • /* Drop our local ref as set_root should get it's own. */
  • if (root != NULL)
  • g_object_unref (root);
  • dbusmenu_server_set_root (priv->menuservice, root); +
  • /* Drop our local ref as set_root should get it's own. */
  • if (root != NULL)
  • g_object_unref (root);
  • }
  • else if (priv->menuservice)
  • {
  • g_object_unref (priv->menuservice);
  • priv->menuservice = NULL;
  • } }

    /* @@ -2042,6 +2057,8 @@ status_notifier_set_context_menu (StatusNotifier sn, *

    • Returns the #GtkWidget set as context menu, or %NULL
  • * Only available if dbusmenu support was enabled in compile-time.
  • *
    • Returns: #GtkWidget or %NULL (if menu was not setuped)

Returns: (transfer none): #GtkWidget of the context menu, or %NULL if none was set up

/ GtkWidget

Thanks! -j

vovochka404 commented 9 years ago

Next round... :-D It's really hard to contribute to your project :-D

But thanks for your patience :)

jjk-jacky commented 9 years ago

Thanks for all your work, very much appreciated! :)

I'm afraid I have a couple more remarks though...

Thanks, -j

vovochka404 commented 9 years ago

Here i'm back :)

jjk-jacky commented 9 years ago

Thanks, looks good!

I'll probably do a little bit of rebase (e.g to drop all references to the item-is-menu bit) and merge this soon-ish (With the heat over here I feel it's harder to find time to work on things...).

Thanks for all your work on this, much appreciated! -j

vovochka404 commented 9 years ago

If you want, i can prepare a new, clean pull request without all this fix-history :)

jjk-jacky commented 9 years ago

Sure, if you don't mind.

(Also, not that it matters here, but with the way I work/do things, such topic branches should be based on next instead of master, since I'll merge them into next, and when doing a new release simply fast-forward master up to next.)

vovochka404 commented 9 years ago

Closing due to #12