diff --git a/src/modules/ctypes/ctypes-utils.jsm b/src/modules/ctypes/ctypes-utils.jsm index 4142aaf..68b929c 100644 --- a/src/modules/ctypes/ctypes-utils.jsm +++ b/src/modules/ctypes/ctypes-utils.jsm @@ -128,7 +128,7 @@ function ctypes_library(aName, aABIs, aDefines, aGlobal) { log.debug("Successfully loaded " + soname); break; } catch(e) { - log.error(soname+" unfound."); + log.warn(soname+" unfound."); } } diff --git a/src/modules/ctypes/linux/appindicator.jsm b/src/modules/ctypes/linux/appindicator.jsm new file mode 100644 index 0000000..0a39ce4 --- /dev/null +++ b/src/modules/ctypes/linux/appindicator.jsm @@ -0,0 +1,51 @@ +/* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ + +var EXPORTED_SYMBOLS = [ "appind3" ]; + +const APPINDICATOR_LIBNAME = "appindicator3"; +const APPINDICATOR_ABIS = [ 1 ]; + +const Cu = Components.utils; +const Cc = Components.classes; +const Ci = Components.interfaces; + +Cu.import("resource://gre/modules/ctypes.jsm"); +Cu.import("resource://firetray/ctypes/ctypes-utils.jsm"); +Cu.import("resource://firetray/ctypes/linux/gobject.jsm"); +Cu.import("resource://firetray/ctypes/linux/gtk.jsm"); + +function appindicator_defines(lib) { + this.AppIndicator = ctypes.StructType("AppIndicator"); + + this.INDICATOR_APPLICATION_DBUS_ADDR = "com.canonical.indicator.application"; + this.INDICATOR_APPLICATION_DBUS_OBJ = "/com/canonical/indicator/application/service"; + this.INDICATOR_APPLICATION_DBUS_IFACE = "com.canonical.indicator.application.service"; + this.NOTIFICATION_WATCHER_DBUS_ADDR = "org.kde.StatusNotifierWatcher"; + this.NOTIFICATION_WATCHER_DBUS_OBJ = "/StatusNotifierWatcher"; + this.NOTIFICATION_WATCHER_DBUS_IFACE = "org.kde.StatusNotifierWatcher"; + this.NOTIFICATION_ITEM_DBUS_IFACE = "org.kde.StatusNotifierItem"; + this.NOTIFICATION_ITEM_DEFAULT_OBJ = "/StatusNotifierItem"; + this.NOTIFICATION_APPROVER_DBUS_IFACE = "org.ayatana.StatusNotifierApprover"; + + this.AppIndicatorCategory = ctypes.int; // enum + this.APP_INDICATOR_CATEGORY_APPLICATION_STATUS = 0; /*< nick=ApplicationStatus >*/ + this.APP_INDICATOR_CATEGORY_COMMUNICATIONS = 1; /*< nick=Communications >*/ + this.APP_INDICATOR_CATEGORY_SYSTEM_SERVICES = 2; /*< nick=SystemServices >*/ + this.APP_INDICATOR_CATEGORY_HARDWARE = 3; /*< nick=Hardware >*/ + this.APP_INDICATOR_CATEGORY_OTHER = 4; /*< nick=Other >*/ + + this.AppIndicatorStatus = ctypes.int; // enum + this.APP_INDICATOR_STATUS_PASSIVE = 0; /*< nick=Passive >*/ + this.APP_INDICATOR_STATUS_ACTIVE = 1; /*< nick=Active >*/ + this.APP_INDICATOR_STATUS_ATTENTION = 2; /*< nick=NeedsAttention >*/ + + lib.lazy_bind("app_indicator_new", this.AppIndicator.ptr, gobject.gchar.ptr, gobject.gchar.ptr, this.AppIndicatorCategory); + lib.lazy_bind("app_indicator_set_status", ctypes.void_t, this.AppIndicator.ptr, this.AppIndicatorStatus); + lib.lazy_bind("app_indicator_get_status", this.AppIndicatorStatus, this.AppIndicator.ptr); + lib.lazy_bind("app_indicator_set_menu", ctypes.void_t, this.AppIndicator.ptr, gtk.GtkMenu.ptr); + + this.AppIndicatorConnectionChangedCb_t = ctypes.FunctionType( + ctypes.default_abi, ctypes.void_t, [this.AppIndicator.ptr, gobject.gboolean, gobject.gpointer]).ptr; +}; + +appind3 = new ctypes_library(APPINDICATOR_LIBNAME, APPINDICATOR_ABIS, appindicator_defines, this); diff --git a/src/modules/ctypes/linux/gio.jsm b/src/modules/ctypes/linux/gio.jsm index b6547bd..ccd7e2d 100644 --- a/src/modules/ctypes/linux/gio.jsm +++ b/src/modules/ctypes/linux/gio.jsm @@ -11,6 +11,7 @@ const Ci = Components.interfaces; Cu.import("resource://gre/modules/ctypes.jsm"); Cu.import("resource://firetray/ctypes/ctypes-utils.jsm"); +Cu.import("resource://firetray/ctypes/linux/glib.jsm"); Cu.import("resource://firetray/ctypes/linux/gobject.jsm"); function gio_defines(lib) { @@ -21,6 +22,25 @@ function gio_defines(lib) { lib.lazy_bind("g_themed_icon_new_from_names", this.GIcon.ptr, ctypes.char.ptr.ptr, ctypes.int); lib.lazy_bind("g_themed_icon_get_names", gobject.gchar.ptr.ptr, this.GThemedIcon.ptr); + this.GBusType = ctypes.int; // enum + this.G_BUS_TYPE_STARTER = -1; + this.G_BUS_TYPE_NONE = 0; + this.G_BUS_TYPE_SYSTEM = 1; + this.G_BUS_TYPE_SESSION = 2; + this.GDBusProxyFlags = ctypes.int; // enum + this.G_DBUS_PROXY_FLAGS_NONE = 0; + this.G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES = (1<<0); + this.G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS = (1<<1); + this.G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START = (1<<2); + + this.GDBusConnection = ctypes.StructType("GDBusConnection"); + this.GCancellable = ctypes.StructType("GCancellable"); + this.GDBusProxy = ctypes.StructType("GDBusProxy"); + this.GDBusInterfaceInfo = ctypes.StructType("GDBusInterfaceInfo"); + + lib.lazy_bind("g_bus_get_sync", this.GDBusConnection.ptr, this.GBusType, this.GCancellable.ptr, glib.GError.ptr.ptr); + lib.lazy_bind("g_dbus_proxy_new_for_bus_sync", this.GDBusProxy.ptr, this.GBusType, this.GDBusProxyFlags, this.GDBusInterfaceInfo.ptr, gobject.gchar.ptr, gobject.gchar.ptr, gobject.gchar.ptr, this.GCancellable.ptr, glib.GError.ptr.ptr); + lib.lazy_bind("g_dbus_proxy_get_name_owner", gobject.gchar.ptr, this.GDBusProxy.ptr); } new ctypes_library(GIO_LIBNAME, GIO_ABIS, gio_defines, this); diff --git a/src/modules/ctypes/linux/glib.jsm b/src/modules/ctypes/linux/glib.jsm index e1d5d71..963bd5b 100644 --- a/src/modules/ctypes/linux/glib.jsm +++ b/src/modules/ctypes/linux/glib.jsm @@ -15,8 +15,12 @@ Cu.import("resource://firetray/ctypes/ctypes-utils.jsm"); function glib_defines(lib) { /* mutual inclusion not possible */ this.GQuark = ctypes.uint32_t; // this.GQuark = gobject.guint32; - this.GError = ctypes.StructType("GError"); - + this.GError = ctypes.StructType("GError", [ + { domain: this.GQuark }, + { code: ctypes.int }, // gint + { message: ctypes.char.ptr } // gchar.ptr + ]); + lib.lazy_bind("g_error_free", ctypes.void_t, this.GError.ptr); lib.lazy_bind("g_strfreev", ctypes.void_t, ctypes.char.ptr.ptr); }; diff --git a/src/modules/ctypes/linux/gobject.jsm b/src/modules/ctypes/linux/gobject.jsm index fb50f5e..cf95946 100644 --- a/src/modules/ctypes/linux/gobject.jsm +++ b/src/modules/ctypes/linux/gobject.jsm @@ -70,6 +70,7 @@ function gobject_defines(lib) { this.gchar = ctypes.char; this.guchar = ctypes.unsigned_char; this.gboolean = this.gint; + this.FALSE = this.gboolean(0); this.gfloat = ctypes.float; this.gdouble = ctypes.double; this.gsize = ctypes.unsigned_long; @@ -131,6 +132,9 @@ function gobject_defines(lib) { /* NOTE: we can't easily work with g_object_get_property() because it uses GValue, which is an opaque struct, and thus can't be initialized by ctypes */ + this.GValue = ctypes.StructType("GValue"); + lib.lazy_bind("g_object_get_property", ctypes.void_t, this.GObject.ptr, this.gchar.ptr, this.GValue.ptr); + lib.lazy_bind("g_object_get", ctypes.void_t, this.gpointer, this.gchar.ptr, "..."); } new ctypes_library(GOBJECT_LIBNAME, GOBJECT_ABIS, gobject_defines, this); diff --git a/src/modules/linux/FiretrayStatusIcon.jsm b/src/modules/linux/FiretrayStatusIcon.jsm index 9882572..6a9bde9 100644 --- a/src/modules/linux/FiretrayStatusIcon.jsm +++ b/src/modules/linux/FiretrayStatusIcon.jsm @@ -13,6 +13,7 @@ Cu.import("resource://firetray/ctypes/linux/cairo.jsm"); Cu.import("resource://firetray/ctypes/linux/gobject.jsm"); Cu.import("resource://firetray/ctypes/linux/gdk.jsm"); Cu.import("resource://firetray/ctypes/linux/gio.jsm"); +Cu.import("resource://firetray/ctypes/linux/glib.jsm"); Cu.import("resource://firetray/ctypes/linux/gtk.jsm"); Cu.import("resource://firetray/ctypes/linux/pango.jsm"); Cu.import("resource://firetray/ctypes/linux/pangocairo.jsm"); @@ -58,6 +59,33 @@ firetray.StatusIcon = { this.addCallbacks(); + Cu.import("resource://firetray/ctypes/linux/appindicator.jsm"); + if (appind3.available() && this.isNotificationWatcherReady()) { + this.indicator = appind3.app_indicator_new( + 'FOUDIL', + 'firefox', + appind3.APP_INDICATOR_CATEGORY_APPLICATION_STATUS + ); + appind3.app_indicator_set_status(this.indicator, appind3.APP_INDICATOR_STATUS_ACTIVE); + appind3.app_indicator_set_menu(this.indicator, firetray.PopupMenu.menu); // mandatory + log.warn("indicator="+this.indicator); + /* + let gval = new gobject.gboolean; + gobject.g_object_get( + ctypes.cast(this.indicator, gobject.gpointer), + "connected", + gval.address(), + ctypes.voidptr_t(null) + ); + log.warn("gval="+gval+" true? "+!firetray.js.strEquals(gval, gobject.FALSE)); + */ + this.callbacks.indicator = appind3.AppIndicatorConnectionChangedCb_t( + firetray.StatusIcon.onAppIndicatorConnectionChanged); // void return, no sentinel + gobject.g_signal_connect(this.indicator, "connection-changed", + firetray.StatusIcon.callbacks.indicator, null); + log.warn("status="+appind3.app_indicator_get_status(this.indicator)); + } + this.initialized = true; return true; }, @@ -201,6 +229,48 @@ firetray.StatusIcon = { log.error("Icon missing"); log.debug(gicon); gtk.gtk_status_icon_set_from_gicon(firetray.StatusIcon.trayIcon, gicon); + }, + + onAppIndicatorConnectionChanged: function(indicator, connected, data) { + log.warn("AppIndicator connection-changed: "+connected); + }, + + isNotificationWatcherReady: function() { + let watcherReady = false; + + let conn = new gio.GDBusConnection.ptr; + let error = new glib.GError.ptr(null); + conn = gio.g_bus_get_sync(gio.G_BUS_TYPE_SESSION, null, error.address()); + firetray.js.assert(error.isNull()); + if (!conn.isNull()) { + let flags = gio.G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START | + gio.G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | + gio.G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS; + + let proxy = gio.g_dbus_proxy_new_for_bus_sync( + gio.G_BUS_TYPE_SESSION, + flags, + null, /* GDBusInterfaceInfo */ + appind3.NOTIFICATION_WATCHER_DBUS_ADDR, + appind3.NOTIFICATION_WATCHER_DBUS_OBJ, + appind3.NOTIFICATION_WATCHER_DBUS_IFACE, + null, /* GCancellable */ + error.address()); + firetray.js.assert(error.isNull()); + + if (!proxy.isNull()) { + let owner = gio.g_dbus_proxy_get_name_owner(proxy); + if (!owner.isNull()) { + watcherReady = true; + } + gobject.g_object_unref(proxy); + } + + gobject.g_object_unref(conn); + } + glib.g_error_free(error); + + return watcherReady; } }; // firetray.StatusIcon